/* XXX: break up into separate modules (HTTP, user, files) */
-// Show a server error
-
+/**
+ * Show a server error.
+ */
function common_server_error($msg, $code=500)
{
$err = new ServerErrorAction($msg, $code);
$err->showPage();
}
-// Show a user error
+/**
+ * Show a user error.
+ */
function common_user_error($msg, $code=400)
{
$err = new ClientErrorAction($msg, $code);
/**
* This should only be used at setup; processes switching languages
* to send text to other users should use common_switch_locale().
- *
+ *
* @param string $language Locale language code (optional; empty uses
* current user's preference or site default)
* @return mixed success
/**
* Initialize locale and charset settings and gettext with our message catalog,
* using the current user's language preference or the site default.
- *
+ *
* This should generally only be run at framework initialization; code switching
* languages at runtime should call common_switch_language().
- *
+ *
* @access private
*/
function common_init_language()
textdomain("statusnet");
}
-
function common_timezone()
{
if (common_logged_in()) {
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;
}
}
// Finally, if none of the above worked, use the site's default...
return common_config('site', 'language');
}
-// salted, hashed passwords are stored in the DB
+/**
+ * Salted, hashed passwords are stored in the DB.
+ */
function common_munge_password($password, $id)
{
if (is_object($id) || is_object($password)) {
return md5($password . $id);
}
-// check if a username exists and has matching password
-
+/**
+ * Check if a username exists and has matching password.
+ */
function common_check_user($nickname, $password)
{
// empty nickname always unacceptable
return $authenticatedUser;
}
-// is the current user logged in?
+/**
+ * Is the current user logged in?
+ */
function common_logged_in()
{
return (!is_null(common_current_user()));
// 3) null to clear
// Initialize to false; set to null if none found
-
$_cur = false;
function common_set_user($user)
{
-
global $_cur;
if (is_null($user) && common_have_session()) {
$value,
$expiration,
$cookiepath,
- $server);
+ $server,
+ common_config('site', 'ssl')=='always');
}
define('REMEMBERME', 'rememberme');
function common_remembered_user()
{
-
$user = null;
$packed = isset($_COOKIE[REMEMBERME]) ? $_COOKIE[REMEMBERME] : null;
return $user;
}
-// must be called with a valid user!
-
+/**
+ * must be called with a valid user!
+ */
function common_forgetme()
{
common_set_cookie(REMEMBERME, '', 0);
}
-// who is the current user?
+/**
+ * Who is the current user?
+ */
function common_current_user()
{
global $_cur;
return $_cur;
}
-// Logins that are 'remembered' aren't 'real' -- they're subject to
-// cookie-stealing. So, we don't let them do certain things. New reg,
-// OpenID, and password logins _are_ real.
-
+/**
+ * Logins that are 'remembered' aren't 'real' -- they're subject to
+ * cookie-stealing. So, we don't let them do certain things. New reg,
+ * OpenID, and password logins _are_ real.
+ */
function common_real_login($real=true)
{
common_ensure_session();
return common_logged_in() && $_SESSION['real_login'];
}
+/**
+ * Get a hash portion for HTTP caching Etags and such including
+ * info on the current user's session. If login/logout state changes,
+ * or we've changed accounts, or we've renamed the current user,
+ * we'll get a new hash value.
+ *
+ * This should not be considered secure information.
+ *
+ * @param User $user (optional; uses common_current_user() if left out)
+ * @return string
+ */
+function common_user_cache_hash($user=false)
+{
+ if ($user === false) {
+ $user = common_current_user();
+ }
+ if ($user) {
+ return crc32($user->id . ':' . $user->nickname);
+ } else {
+ return '0';
+ }
+}
+
// get canonical version of nickname for comparison
function common_canonical_nickname($nickname)
{
}
if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
-
// Get the context of the original notice, if any
-
$originalAuthor = null;
$originalNotice = null;
$originalMentions = array();
$matches = array_merge($tmatches[1], $atmatches[1]);
foreach ($matches as $match) {
-
$nickname = common_canonical_nickname($match[0]);
// Try to get a profile for this nickname.
// sender context.
if (!empty($originalAuthor) && $originalAuthor->nickname == $nickname) {
-
$mentioned = $originalAuthor;
-
} else if (!empty($originalMentions) &&
array_key_exists($nickname, $originalMentions)) {
-
$mentioned = $originalMentions[$nickname];
} else {
$mentioned = common_relative_profile($sender, $nickname);
}
if (!empty($mentioned)) {
-
$user = User::staticGet('id', $mentioned->id);
if ($user) {
} 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');
+
+ $attrs = array('href' => $canon, 'title' => $longurl);
$is_attachment = false;
$attachment_id = null;
$attrs['id'] = "attachment-{$attachment_id}";
}
+ // Whether to nofollow
+
+ $nf = common_config('nofollow', 'external');
+
+ if ($nf == 'never') {
+ $attrs['rel'] = 'external';
+ } else {
+ $attrs['rel'] = 'nofollow external';
+ }
+
return XMLStringer::estring('a', $attrs, $url);
}
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'));
+ }
}
+/**
+ * Very basic stripping of invalid UTF-8 input text.
+ *
+ * @param string $str
+ * @return mixed string or null if invalid input
+ *
+ * @todo ideally we should drop bad chars, and maybe do some of the checks
+ * from common_xml_safe_str. But we can't strip newlines, etc.
+ * @todo Unicode normalization might also be useful, but not needed now.
+ */
+function common_validate_utf8($str)
+{
+ // preg_replace will return NULL on invalid UTF-8 input.
+ //
+ // Note: empty regex //u also caused NULL return on some
+ // production machines, but none of our test machines.
+ //
+ // This should be replaced with a more reliable check.
+ return preg_replace('/\x00/u', '', $str);
+}
+
+/**
+ * Make sure an arbitrary string is safe for output in XML as a single line.
+ *
+ * @param string $str
+ * @return string
+ */
function common_xml_safe_str($str)
{
// Replace common eol and extra whitespace input chars
$canonical = common_canonical_tag($tag);
if (common_config('singleuser', 'enabled')) {
// regular TagAction isn't set up in 1user mode
+ $user = User::singleUser();
$url = common_local_url('showstream',
- array('nickname' => common_config('singleuser', 'nickname'),
+ array('nickname' => $user->nickname,
'tag' => $canonical));
} else {
$url = common_local_url('tag', array('tag' => $canonical));
$attrs = array('href' => $group->permalink(),
'class' => 'url');
if (!empty($group->fullname)) {
- $attrs['title'] = $group->fullname . ' (' . $group->nickname . ')';
+ $attrs['title'] = $group->getFancyName();
}
$xs = new XMLStringer();
$xs->elementStart('span', 'vcard');
function common_is_sensitive($action)
{
- static $sensitive = array('login', 'register', 'passwordsettings',
- 'twittersettings', 'api');
+ static $sensitive = array(
+ 'login',
+ 'register',
+ 'passwordsettings',
+ 'api',
+ 'ApiOauthRequestToken',
+ 'ApiOauthAccessToken',
+ 'ApiOauthAuthorize',
+ 'ApiOauthPin',
+ 'showapplication'
+ );
$ssl = null;
if (Event::handle('SensitiveAction', array($action, &$ssl))) {
// TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a minute ago');
} else if ($diff < 3300) {
- // XXX: should support plural.
+ $minutes = round($diff/60);
// TRANS: Used in notices to indicate when the notice was made compared to now.
- return sprintf(_('about %d minutes ago'), round($diff/60));
+ return sprintf( ngettext('about one minute ago', 'about %d minutes ago', $minutes), $minutes);
} else if ($diff < 5400) {
// TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about an hour ago');
} else if ($diff < 22 * 3600) {
- // XXX: should support plural.
+ $hours = round($diff/3600);
// TRANS: Used in notices to indicate when the notice was made compared to now.
- return sprintf(_('about %d hours ago'), round($diff/3600));
+ return sprintf( ngettext('about one hour ago', 'about %d hours ago', $hours), $hours);
} else if ($diff < 37 * 3600) {
// TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a day ago');
} else if ($diff < 24 * 24 * 3600) {
- // XXX: should support plural.
+ $days = round($diff/(24*3600));
// TRANS: Used in notices to indicate when the notice was made compared to now.
- return sprintf(_('about %d days ago'), round($diff/(24*3600)));
+ return sprintf( ngettext('about one day ago', 'about %d days ago', $days), $days);
} else if ($diff < 46 * 24 * 3600) {
// TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a month ago');
} else if ($diff < 330 * 24 * 3600) {
- // XXX: should support plural.
+ $months = round($diff/(30*24*3600));
// TRANS: Used in notices to indicate when the notice was made compared to now.
- return sprintf(_('about %d months ago'), round($diff/(30*24*3600)));
+ return sprintf( ngettext('about one month ago', 'about %d months ago',$months), $months);
} else if ($diff < 480 * 24 * 3600) {
// TRANS: Used in notices to indicate when the notice was made compared to now.
return _('about a year ago');
exit;
}
-function common_broadcast_notice($notice, $remote=false)
-{
- // DO NOTHING!
-}
-
// Stick the notice on the queue
function common_enqueue_notice($notice)
$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))) {
null, null, false);
}
-// Should make up a reasonable root URL
-
+/**
+ * Should make up a reasonable root URL
+ */
function common_root_url($ssl=false)
{
$url = common_path('', $ssl, false);
return $url;
}
-// returns $bytes bytes of random data as a hexadecimal string
-// "good" here is a goal and not a guarantee
-
+/**
+ * returns $bytes bytes of random data as a hexadecimal string
+ * "good" here is a goal and not a guarantee
+ */
function common_good_rand($bytes)
{
// XXX: use random.org...?
/**
* Record the given URL as the return destination for a future
* form submission, to be read by common_get_returnto().
- *
+ *
* @param string $url
- *
+ *
* @fixme as a session-global setting, this can allow multiple forms
* to conflict and overwrite each others' returnto destinations if
* the user has multiple tabs or windows open.
- *
+ *
* Should refactor to index with a token or otherwise only pass the
* data along its intended path.
*/
/**
* Fetch a return-destination URL previously recorded by
* common_set_returnto().
- *
+ *
* @return mixed URL string or null
- *
+ *
* @fixme as a session-global setting, this can allow multiple forms
* to conflict and overwrite each others' returnto destinations if
* the user has multiple tabs or windows open.
- *
+ *
* Should refactor to index with a token or otherwise only pass the
* data along its intended path.
*/
{
$objstr = common_log_objstring($object);
$last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR, $last_error->message . '(' . $verb . ' on ' . $objstr . ')', $filename);
+ if (is_object($last_error)) {
+ $msg = $last_error->message;
+ } else {
+ $msg = 'Unknown error (' . var_export($last_error, true) . ')';
+ }
+ common_log(LOG_ERR, $msg . '(' . $verb . ' on ' . $objstr . ')', $filename);
}
function common_log_objstring(&$object)
* 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
*/
array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
}
+/**
+ * Pull arguments from a GET/POST/REQUEST array with first-level input checks:
+ * strips "magic quotes" slashes if necessary, and kills invalid UTF-8 strings.
+ *
+ * @param array $from
+ * @return array
+ */
function common_copy_args($from)
{
$to = array();
$strip = get_magic_quotes_gpc();
foreach ($from as $k => $v) {
- if($strip) {
- if(is_array($v)) {
- $to[$k] = common_copy_args($v);
- } else {
- $to[$k] = stripslashes($v);
- }
+ if(is_array($v)) {
+ $to[$k] = common_copy_args($v);
} else {
- $to[$k] = $v;
+ if ($strip) {
+ $v = stripslashes($v);
+ }
+ $to[$k] = strval(common_validate_utf8($v));
}
}
return $to;
return $_SESSION['token'];
}
-function common_cache_key($extra)
-{
- return Cache::key($extra);
-}
-
-function common_keyize($str)
-{
- return Cache::keyize($str);
-}
-
-function common_memcache()
-{
- return Cache::instance();
-}
-
function common_license_terms($uri)
{
if(preg_match('/creativecommons.org\/licenses\/([^\/]+)/', $uri, $matches)) {
*/
function common_database_tablename($tablename)
{
-
if(common_config('db','quote_identifiers')) {
$tablename = '"'. $tablename .'"';
}
/**
* 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);
}