X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Futil.php;h=df339d4b1e39e6058c84ea3d31becf2e1bb05210;hb=99194e03fa50b61f99164674afc949b4bbefd44a;hp=049001abaf982b299ddfc89fa7bf6696e06b0f93;hpb=5f4c6ec626d3d641f0712b276deb32b218b7a330;p=quix0rs-gnu-social.git diff --git a/lib/util.php b/lib/util.php index 049001abaf..df339d4b1e 100644 --- a/lib/util.php +++ b/lib/util.php @@ -19,15 +19,18 @@ /* 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); @@ -37,7 +40,7 @@ function common_user_error($msg, $code=400) /** * 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 @@ -61,10 +64,10 @@ function common_init_locale($language=null) /** * 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() @@ -88,8 +91,8 @@ function common_init_language() // don't do the job. en_US.UTF-8 should be there most of the // time, but not guaranteed. $ok = common_init_locale("en_US"); - if (!$ok) { - // Try to find a complete, working locale... + if (!$ok && strtolower(substr(PHP_OS, 0, 3)) != 'win') { + // Try to find a complete, working locale on Unix/Linux... // @fixme shelling out feels awfully inefficient // but I don't think there's a more standard way. $all = `locale -a`; @@ -101,9 +104,9 @@ function common_init_language() } } } - if (!$ok) { - common_log(LOG_ERR, "Unable to find a UTF-8 locale on this system; UI translations may not work."); - } + } + if (!$ok) { + common_log(LOG_ERR, "Unable to find a UTF-8 locale on this system; UI translations may not work."); } $locale_set = common_init_locale($language); } @@ -142,7 +145,6 @@ function common_switch_locale($language=null) textdomain("statusnet"); } - function common_timezone() { if (common_logged_in()) { @@ -155,23 +157,38 @@ function common_timezone() 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; } } @@ -189,8 +206,10 @@ function common_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)) { @@ -201,8 +220,9 @@ function common_munge_password($password, $id) 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 @@ -229,7 +249,9 @@ function common_check_user($nickname, $password) return $authenticatedUser; } -// is the current user logged in? +/** + * Is the current user logged in? + */ function common_logged_in() { return (!is_null(common_current_user())); @@ -275,12 +297,10 @@ function common_ensure_session() // 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()) { @@ -366,7 +386,6 @@ function common_rememberme($user=null) function common_remembered_user() { - $user = null; $packed = isset($_COOKIE[REMEMBERME]) ? $_COOKIE[REMEMBERME] : null; @@ -428,14 +447,17 @@ function common_remembered_user() 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; @@ -471,10 +493,11 @@ function common_current_user() 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(); @@ -486,6 +509,29 @@ function common_is_real_login() 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) { @@ -577,9 +623,7 @@ function common_find_mentions($text, $notice) } if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) { - // Get the context of the original notice, if any - $originalAuthor = null; $originalNotice = null; $originalMentions = array(); @@ -615,7 +659,6 @@ function common_find_mentions($text, $notice) $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. @@ -623,19 +666,15 @@ function common_find_mentions($text, $notice) // 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) { @@ -830,7 +869,10 @@ function common_linkify($url) { } 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'); @@ -849,7 +891,7 @@ function common_linkify($url) { } if (!empty($f)) { - if ($f->getEnclosure()) { + if ($f->getEnclosure() || File_oembed::staticGet('file_id',$f->id)) { $is_attachment = true; $attachment_id = $f->id; @@ -872,13 +914,47 @@ function common_linkify($url) { return XMLStringer::estring('a', $attrs, $url); } -function common_shorten_links($text) +function common_shorten_links($text, $always = false) +{ + 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) { - $maxLength = Notice::maxContent(); - if ($maxLength == 0 || mb_strlen($text) <= $maxLength) return $text; - return common_replace_urls_callback($text, array('File_redirection', 'makeShort')); + // preg_replace will return NULL on invalid UTF-8 input. + return preg_replace('//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 @@ -1015,8 +1091,7 @@ function common_local_url($action, $args=null, $params=null, $fragment=null, $ad function common_is_sensitive($action) { - static $sensitive = array('login', 'register', 'passwordsettings', - 'twittersettings', 'api'); + static $sensitive = array('login', 'register', 'passwordsettings', 'api'); $ssl = null; if (Event::handle('SensitiveAction', array($action, &$ssl))) { @@ -1096,24 +1171,38 @@ function common_date_string($dt) if ($now < $t) { // that shouldn't happen! return common_exact_date($dt); } else if ($diff < 60) { + // TRANS: Used in notices to indicate when the notice was made compared to now. return _('a few seconds ago'); } else if ($diff < 92) { + // TRANS: Used in notices to indicate when the notice was made compared to now. return _('about a minute ago'); } else if ($diff < 3300) { - return sprintf(_('about %d minutes ago'), round($diff/60)); + $minutes = round($diff/60); + // TRANS: Used in notices to indicate when the notice was made compared to now. + 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) { - return sprintf(_('about %d hours ago'), round($diff/3600)); + $hours = round($diff/3600); + // TRANS: Used in notices to indicate when the notice was made compared to now. + 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) { - return sprintf(_('about %d days ago'), round($diff/(24*3600))); + $days = round($diff/(24*3600)); + // TRANS: Used in notices to indicate when the notice was made compared to now. + 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) { - return sprintf(_('about %d months ago'), round($diff/(30*24*3600))); + $months = round($diff/(30*24*3600)); + // TRANS: Used in notices to indicate when the notice was made compared to now. + 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'); } else { return common_exact_date($dt); @@ -1209,11 +1298,6 @@ function common_redirect($url, $code=307) exit; } -function common_broadcast_notice($notice, $remote=false) -{ - // DO NOTHING! -} - // Stick the notice on the queue function common_enqueue_notice($notice) @@ -1229,18 +1313,9 @@ 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))) { @@ -1277,8 +1352,9 @@ function common_profile_url($nickname) 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); @@ -1289,9 +1365,10 @@ function common_root_url($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...? @@ -1327,13 +1404,13 @@ function common_mtrand($bytes) /** * 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. */ @@ -1346,13 +1423,13 @@ function common_set_returnto($url) /** * 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. */ @@ -1437,7 +1514,12 @@ function common_log_db_error(&$object, $verb, $filename=null) { $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) @@ -1479,7 +1561,7 @@ function common_valid_tag($tag) * 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 */ @@ -1615,19 +1697,25 @@ function common_config($main, $sub) 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; @@ -1765,21 +1853,6 @@ function common_session_token() 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)) { @@ -1810,7 +1883,6 @@ function common_compatible_license($from, $to) */ function common_database_tablename($tablename) { - if(common_config('db','quote_identifiers')) { $tablename = '"'. $tablename .'"'; } @@ -1821,30 +1893,42 @@ function common_database_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); }