README
------
- StatusNet 0.9.3 ("Half a World Away")
- 29 June 2010
+ StatusNet 0.9.4beta2
+ 11 August 2010
This is the README file for StatusNet, the Open Source microblogging
platform. It includes installation instructions, descriptions of
you can move back and forth between a hosted version or a version
installed on your own servers.
+ A commercial software subscription is available from StatusNet Inc. It
+ includes 24-hour technical support and developer support. More
+ information at http://status.net/contact or email sales@status.net.
+
License
=======
New this version
================
- This is a minor bug and feature release since version 0.9.2 released on
- 4 May 2010.
+ This is a security, bug and feature release since version 0.9.3 released on
+ 29 June 2010.
For best compatibility with client software and site federation, and a lot of
bug fixes, it is highly recommended that all public sites upgrade to the new
version.
+ Changes from 0.9.4beta1:
+ - fix for daemon config switching on multi-site setup
+
Notable changes this version:
- - Enhanced API output to aid StatusNet-specific clients
+ - OpenID and OAuth libraries patched for potential timing attack
+ - OStatus feed i/o updated for Activity Streams
+ - Correctness fixes on XRD, other discovery bits
+ - Support for contacting SNI-based SSL virtual hosts when SSL
+ certificate verification is enabled (requires PHP 5.3.2+ or
+ enabling CURL backend with $config['http']['curl'] = true)
+ - Experimental SubMirror plugin
+ - Multi-site status_network table mode has been tweaked to support
+ multiple tags better
- Many updates to user interface translation from TranslateWiki
- - OStatus now works subscribing to SSL-protected sites by default
- - OpenID now works on PHP 5.3, supports closer site integration.
- - Numerous API and FOAF output fixes.
- - Fixes to Facebook integration for FB API behavior changes
- - PostgreSQL support updates
- - Initial version of a custom theme uploader (disabled by default)
- - LDAP auth plugins cleanup
- Many other bugfixes
- A full changelog is available at http://status.net/wiki/StatusNet_0.9.3.
+ A full changelog is available at http://status.net/wiki/StatusNet_0.9.4.
Prerequisites
=============
- MySQL. For accessing the database.
- GD. For scaling down avatar images.
- mbstring. For handling Unicode (UTF-8) encoded strings.
- - gettext. For multiple languages. Default on many PHP installs.
For some functionality, you will also need the following extensions:
Sphinx server to serve the search queries.
- bcmath or gmp. For Salmon signatures (part of OStatus). Needed
if you have OStatus configured.
+ - gettext. For multiple languages. Default on many PHP installs;
+ will be emulated if not present.
You will almost definitely get 2-3 times better performance from your
site if you install a PHP bytecode cache/accelerator. Some well-known
1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work:
- tar zxf statusnet-0.9.2.tar.gz
+ tar zxf statusnet-0.9.4.tar.gz
...which will make a statusnet-0.9.2 subdirectory in your current
directory. (If you don't have shell access on your Web server, you
2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work:
- mv statusnet-0.9.2 /var/www/statusnet
+ mv statusnet-0.9.4 /var/www/statusnet
This will make your StatusNet instance available in the statusnet path of
your server, like "http://example.net/statusnet". "microblog" or
parameters correctly so that both the SSL server and the
"normal" server can access the session cookie and
preferably other cookies as well.
-shorturllength: Length of URL at which URLs in a message exceeding 140
- characters will be sent to the user's chosen
- shortening service.
+shorturllength: ignored. See 'url' section below.
dupelimit: minimum time allowed for one person to say the same thing
twice. Default 60s. Anything lower is considered a user
or UI error.
'search', 'message', 'settings', 'admin'. Ignored when site
is private, in which case the entire site ('/') is disallowed.
+url
+---
+
+Everybody loves URL shorteners. These are some options for fine-tuning
+how and when the server shortens URLs.
+
+shortener: URL shortening service to use by default. Users can override
+ individually. 'ur1.ca' by default.
+maxlength: If an URL is strictly longer than this limit, it will be
+ shortened. Note that the URL shortener service may return an
+ URL longer than this limit. Defaults to 25. Users can
+ override. If set to 0, all URLs will be shortened.
+maxnoticelength: If a notice is strictly longer than this limit, all
+ URLs in the notice will be shortened. Users can override.
+ -1 means the text limit for notices.
+
Plugins
=======
* @param string $long_url
* @return string
*/
- function makeShort($long_url) {
+ function makeShort($long_url)
+ {
$canon = File_redirection::_canonUrl($long_url);
$short_url = File_redirection::_userMakeShort($canon);
// Did we get one? Is it shorter?
- if (!empty($short_url) && mb_strlen($short_url) < mb_strlen($long_url)) {
+
+ if (!empty($short_url)) {
+ return $short_url;
+ } else {
+ return $long_url;
+ }
+ }
+
+ /**
+ * Shorten a URL with the current user's configured shortening
+ * options, if applicable.
+ *
+ * If it cannot be shortened or the "short" URL is longer than the
+ * original, the original is returned.
+ *
+ * If the referenced item has not been seen before, embedding data
+ * may be saved.
+ *
+ * @param string $long_url
+ * @return string
+ */
+
+ function forceShort($long_url)
+ {
+ $canon = File_redirection::_canonUrl($long_url);
+
+ $short_url = File_redirection::_userMakeShort($canon, true);
+
+ // Did we get one? Is it shorter?
+ if (!empty($short_url)) {
return $short_url;
} else {
return $long_url;
}
}
- function _userMakeShort($long_url) {
- $short_url = common_shorten_url($long_url);
+ function _userMakeShort($long_url, $force = false) {
+ $short_url = common_shorten_url($long_url, $force);
if (!empty($short_url) && $short_url != $long_url) {
$short_url = (string)$short_url;
// store it
} else if (is_string($redir_data)) {
// The file is a known redirect target.
$file = File::staticGet('url', $redir_data);
+ if (empty($file)) {
+ // @fixme should we save a new one?
+ // this case was triggering sometimes for redirects
+ // with unresolvable targets; found while fixing
+ // "can't linkify" bugs with shortened links to
+ // SSL sites with cert issues.
+ return null;
+ }
$file_id = $file->id;
}
} else {
//exit with 200 response, if this is checking fancy from the installer
if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; }
- define('STATUSNET_VERSION', '0.9.3');
+ define('STATUSNET_VERSION', '0.9.4beta2');
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
- define('STATUSNET_CODENAME', 'Half a World Away');
+ define('STATUSNET_CODENAME', 'Orange Crush');
define('AVATAR_PROFILE_SIZE', 96);
define('AVATAR_STREAM_SIZE', 48);
# global configuration object
require_once('PEAR.php');
+require_once('PEAR/Exception.php');
require_once('DB/DataObject.php');
require_once('DB/DataObject/Cast.php'); # for dates
require_once INSTALLDIR.'/lib/clientexception.php';
require_once INSTALLDIR.'/lib/serverexception.php';
+
+//set PEAR error handling to use regular PHP exceptions
+function PEAR_ErrorToPEAR_Exception($err)
+{
+ //DB_DataObject throws error when an empty set would be returned
+ //That behavior is weird, and not how the rest of StatusNet works.
+ //So just ignore those errors.
+ if ($err->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
+ return;
+ }
+ if ($err->getCode()) {
+ throw new PEAR_Exception($err->getMessage(), $err->getCode());
+ }
+ throw new PEAR_Exception($err->getMessage());
+}
+PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'PEAR_ErrorToPEAR_Exception');
+
try {
StatusNet::init(@$server, @$path, @$conffile);
} catch (NoConfigException $e) {
'OStatus' => null,
'WikiHashtags' => null,
'RSSCloud' => null,
+ 'ClientSideShorten' => null,
'OpenID' => null),
),
+ 'pluginlist' => array(),
'admin' =>
- array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice')),
+ array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'plugins')),
'singleuser' =>
array('enabled' => false,
'nickname' => null),
array('subscribers' => true,
'members' => true,
'peopletag' => true),
+ 'url' =>
+ array('shortener' => 'ur1.ca',
+ 'maxlength' => 25,
+ 'maxnoticelength' => -1),
'http' => // HTTP client settings when contacting other sites
- array('ssl_cafile' => false // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt')
+ array('ssl_cafile' => false, // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt')
+ 'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.)
),
);
return true;
}
- $sn = Status_network::staticGet($nickname);
+ $sn = Status_network::staticGet('nickname', $nickname);
if (empty($sn)) {
return false;
throw new Exception("No such site nickname '$nickname'");
{
// Load default plugins
foreach (common_config('plugins', 'default') as $name => $params) {
+ $key = 'disable-' . $name;
+ if (common_config('plugins', $key)) {
+ continue;
+ }
+
if (is_null($params)) {
addPlugin($name);
} else if (is_array($params)) {
}
// Backwards compatibility
-
if (array_key_exists('memcached', $config)) {
if ($config['memcached']['enabled']) {
addPlugin('Memcache', array('servers' => $config['memcached']['server']));
$config['cache']['base'] = $config['memcached']['base'];
}
}
+ if (array_key_exists('xmpp', $config)) {
+ if ($config['xmpp']['enabled']) {
+ addPlugin('xmpp', array(
+ 'server' => $config['xmpp']['server'],
+ 'port' => $config['xmpp']['port'],
+ 'user' => $config['xmpp']['user'],
+ 'resource' => $config['xmpp']['resource'],
+ 'encryption' => $config['xmpp']['encryption'],
+ 'password' => $config['xmpp']['password'],
+ 'host' => $config['xmpp']['host'],
+ 'debug' => $config['xmpp']['debug'],
+ 'public' => $config['xmpp']['public']
+ ));
+ }
+ }
}
}
return common_config('site', 'timezone');
}
+function common_valid_language($lang)
+{
+ if ($lang) {
+ // Validate -- we don't want to end up with a bogus code
+ // left over from some old junk.
+ foreach (common_config('site', 'languages') as $code => $info) {
+ if ($info['lang'] == $lang) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
function common_language()
{
+ // Allow ?uselang=xx override, very useful for debugging
+ // and helping translators check usage and context.
+ if (isset($_GET['uselang'])) {
+ $uselang = strval($_GET['uselang']);
+ if (common_valid_language($uselang)) {
+ return $uselang;
+ }
+ }
// If there is a user logged in and they've set a language preference
// then return that one...
if (_have_config() && common_logged_in()) {
$user = common_current_user();
- $user_language = $user->language;
-
- if ($user->language) {
- // Validate -- we don't want to end up with a bogus code
- // left over from some old junk.
- foreach (common_config('site', 'languages') as $code => $info) {
- if ($info['lang'] == $user_language) {
- return $user_language;
- }
- }
+
+ if (common_valid_language($user->language)) {
+ return $user->language;
}
}
} elseif (is_string($longurl_data)) {
$longurl = $longurl_data;
} else {
- throw new ServerException("Can't linkify url '$url'");
+ // Unable to reach the server to verify contents, etc
+ // Just pass the link on through for now.
+ common_log(LOG_ERR, "Can't linkify url '$url'");
+ $longurl = $url;
}
}
$attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external');
function common_shorten_links($text, $always = false)
{
- $maxLength = Notice::maxContent();
- if (!$always && ($maxLength == 0 || mb_strlen($text) <= $maxLength)) return $text;
- return common_replace_urls_callback($text, array('File_redirection', 'makeShort'));
+ common_debug("common_shorten_links() called");
+
+ $user = common_current_user();
+
+ $maxLength = User_urlshortener_prefs::maxNoticeLength($user);
+
+ common_debug("maxLength = $maxLength");
+
+ if ($always || mb_strlen($text) > $maxLength) {
+ common_debug("Forcing shortening");
+ return common_replace_urls_callback($text, array('File_redirection', 'forceShort'));
+ } else {
+ common_debug("Not forcing shortening");
+ return common_replace_urls_callback($text, array('File_redirection', 'makeShort'));
+ }
}
function common_xml_safe_str($str)
$transports[] = 'plugin';
}
- $xmpp = common_config('xmpp', 'enabled');
-
- if ($xmpp) {
- $transports[] = 'jabber';
- }
-
// We can skip these for gatewayed notices.
if ($notice->isLocal()) {
$transports = array_merge($transports, $localTransports);
- if ($xmpp) {
- $transports[] = 'public';
- }
}
if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) {
* Determine if given domain or address literal is valid
* eg for use in JIDs and URLs. Does not check if the domain
* exists!
- *
+ *
* @param string $domain
* @return boolean valid or not
*/
/**
* Shorten a URL with the current user's configured shortening service,
* or ur1.ca if configured, or not at all if no shortening is set up.
- * Length is not considered.
*
- * @param string $long_url
+ * @param string $long_url original URL
+ * @param boolean $force Force shortening (used when notice is too long)
+ *
* @return string may return the original URL if shortening failed
*
* @fixme provide a way to specify a particular shortener
* @fixme provide a way to specify to use a given user's shortening preferences
*/
-function common_shorten_url($long_url)
+
+function common_shorten_url($long_url, $force = false)
{
+ common_debug("Shortening URL '$long_url' (force = $force)");
+
$long_url = trim($long_url);
+
$user = common_current_user();
- if (empty($user)) {
- // common current user does not find a user when called from the XMPP daemon
- // therefore we'll set one here fix, so that XMPP given URLs may be shortened
- $shortenerName = 'ur1.ca';
- } else {
- $shortenerName = $user->urlshorteningservice;
+
+ $maxUrlLength = User_urlshortener_prefs::maxUrlLength($user);
+ common_debug("maxUrlLength = $maxUrlLength");
+
+ // $force forces shortening even if it's not strictly needed
+
+ if (mb_strlen($long_url) < $maxUrlLength && !$force) {
+ common_debug("Skipped shortening URL.");
+ return $long_url;
}
- if(Event::handle('StartShortenUrl', array($long_url,$shortenerName,&$shortenedUrl))){
+ $shortenerName = User_urlshortener_prefs::urlShorteningService($user);
+
+ common_debug("Shortener name = '$shortenerName'");
+
+ if (Event::handle('StartShortenUrl', array($long_url, $shortenerName, &$shortenedUrl))) {
//URL wasn't shortened, so return the long url
return $long_url;
- }else{
+ } else {
//URL was shortened, so return the result
return trim($shortenedUrl);
}