X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Futil.php;h=85f49e4c59f40c57ae2b74c54fffd76a48dcf064;hb=f7b431d60bd2d38d5fbf12e1a37678f3d25a7cc6;hp=35fcfdb090a7f862b446fc6f77e09e61e82d3d12;hpb=ebfa8bce27d341a3cd10c53d5673b75b60c212bb;p=quix0rs-gnu-social.git diff --git a/lib/util.php b/lib/util.php index 35fcfdb090..85f49e4c59 100644 --- a/lib/util.php +++ b/lib/util.php @@ -145,7 +145,6 @@ function common_switch_locale($language=null) textdomain("statusnet"); } - function common_timezone() { if (common_logged_in()) { @@ -327,7 +326,8 @@ function common_set_cookie($key, $value, $expiration=0) $value, $expiration, $cookiepath, - $server); + $server, + common_config('site', 'ssl')=='always'); } define('REMEMBERME', 'rememberme'); @@ -517,14 +517,29 @@ function common_user_cache_hash($user=false) } } -// get canonical version of nickname for comparison +/** + * get canonical version of nickname for comparison + * + * @param string $nickname + * @return string + * + * @throws NicknameException on invalid input + * @deprecated call Nickname::normalize() directly. + */ function common_canonical_nickname($nickname) { - // XXX: UTF-8 canonicalization (like combining chars) - return strtolower($nickname); + return Nickname::normalize($nickname); } -// get canonical version of email for comparison +/** + * get canonical version of email for comparison + * + * @fixme actually normalize + * @fixme reject invalid input + * + * @param string $email + * @return string + */ function common_canonical_email($email) { // XXX: canonicalize UTF-8 @@ -532,15 +547,33 @@ function common_canonical_email($email) return $email; } +/** + * Partial notice markup rendering step: build links to !group references. + * + * @param string $text partially rendered HTML + * @param Notice $notice in whose context we're working + * @return string partially rendered HTML + */ function common_render_content($text, $notice) { $r = common_render_text($text); $id = $notice->profile_id; $r = common_linkify_mentions($r, $notice); - $r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r); + $r = preg_replace('/(^|[\s\.\,\:\;]+)!(' . Nickname::DISPLAY_FMT . ')/e', + "'\\1!'.common_group_link($id, '\\2')", $r); return $r; } +/** + * Finds @-mentions within the partially-rendered text section and + * turns them into live links. + * + * Should generally not be called except from common_render_content(). + * + * @param string $text partially-rendered HTML + * @param Notice $notice in-progress or complete Notice object for context + * @return string partially-rendered HTML + */ function common_linkify_mentions($text, $notice) { $mentions = common_find_mentions($text, $notice); @@ -597,6 +630,21 @@ function common_linkify_mention($mention) return $output; } +/** + * Find @-mentions in the given text, using the given notice object as context. + * References will be resolved with common_relative_profile() against the user + * who posted the notice. + * + * Note the return data format is internal, to be used for building links and + * such. Should not be used directly; rather, call common_linkify_mentions(). + * + * @param string $text + * @param Notice $notice notice in whose context we're building links + * + * @return array + * + * @access private + */ function common_find_mentions($text, $notice) { $mentions = array(); @@ -631,20 +679,15 @@ function common_find_mentions($text, $notice) } } - preg_match_all('/^T ([A-Z0-9]{1,64}) /', - $text, - $tmatches, - PREG_OFFSET_CAPTURE); - - preg_match_all('/(?:^|\s+)@(['.NICKNAME_FMT.']{1,64})/', - $text, - $atmatches, - PREG_OFFSET_CAPTURE); - - $matches = array_merge($tmatches[1], $atmatches[1]); + $matches = common_find_mentions_raw($text); foreach ($matches as $match) { - $nickname = common_canonical_nickname($match[0]); + try { + $nickname = Nickname::normalize($match[0]); + } catch (NicknameException $e) { + // Bogus match? Drop it. + continue; + } // Try to get a profile for this nickname. // Start with conversation context, then go to @@ -710,6 +753,31 @@ function common_find_mentions($text, $notice) return $mentions; } +/** + * Does the actual regex pulls to find @-mentions in text. + * Should generally not be called directly; for use in common_find_mentions. + * + * @param string $text + * @return array of PCRE match arrays + */ +function common_find_mentions_raw($text) +{ + $tmatches = array(); + preg_match_all('/^T (' . Nickname::DISPLAY_FMT . ') /', + $text, + $tmatches, + PREG_OFFSET_CAPTURE); + + $atmatches = array(); + preg_match_all('/(?:^|\s+)@(' . Nickname::DISPLAY_FMT . ')\b/', + $text, + $atmatches, + PREG_OFFSET_CAPTURE); + + $matches = array_merge($tmatches[1], $atmatches[1]); + return $matches; +} + function common_render_text($text) { $r = htmlspecialchars($text); @@ -721,7 +789,14 @@ function common_render_text($text) return $r; } -function common_replace_urls_callback($text, $callback, $notice_id = null) { +/** + * Find links in the given text and pass them to the given callback function. + * + * @param string $text + * @param function($text, $arg) $callback: return replacement text + * @param mixed $arg: optional argument will be passed on to the callback + */ +function common_replace_urls_callback($text, $callback, $arg = null) { // Start off with a regex $regex = '#'. '(?:^|[\s\<\>\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'. @@ -762,10 +837,21 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { '#ixu'; //preg_match_all($regex,$text,$matches); //print_r($matches); - return preg_replace_callback($regex, curry('callback_helper',$callback,$notice_id) ,$text); + return preg_replace_callback($regex, curry('callback_helper',$callback,$arg) ,$text); } -function callback_helper($matches, $callback, $notice_id) { +/** + * Intermediate callback for common_replace_links(), helps resolve some + * ambiguous link forms before passing on to the final callback. + * + * @param array $matches + * @param callable $callback + * @param mixed $arg optional argument to pass on as second param to callback + * @return string + * + * @access private + */ +function callback_helper($matches, $callback, $arg=null) { $url=$matches[1]; $left = strpos($matches[0],$url); $right = $left+strlen($url); @@ -808,11 +894,7 @@ function callback_helper($matches, $callback, $notice_id) { } }while($original_url!=$url); - if(empty($notice_id)){ - $result = call_user_func_array($callback, array($url)); - }else{ - $result = call_user_func_array($callback, array(array($url,$notice_id)) ); - } + $result = call_user_func_array($callback, array($url, $arg)); return substr($matches[0],0,$left) . $result . substr($matches[0],$right); } @@ -848,7 +930,7 @@ function common_linkify($url) { $canon = File_redirection::_canonUrl($url); - $longurl_data = File_redirection::where($canon); + $longurl_data = File_redirection::where($canon, common_config('attachments', 'process_links')); if (is_array($longurl_data)) { $longurl = $longurl_data['url']; } elseif (is_string($longurl_data)) { @@ -860,7 +942,8 @@ function common_linkify($url) { $longurl = $url; } } - $attrs = array('href' => $canon, 'title' => $longurl, 'rel' => 'external'); + + $attrs = array('href' => $canon, 'title' => $longurl); $is_attachment = false; $attachment_id = null; @@ -871,12 +954,14 @@ function common_linkify($url) { $f = File::staticGet('url', $longurl); if (empty($f)) { - // XXX: this writes to the database. :< - $f = File::processNew($longurl); + if (common_config('attachments', 'process_links')) { + // XXX: this writes to the database. :< + $f = File::processNew($longurl); + } } if (!empty($f)) { - if ($f->getEnclosure() || File_oembed::staticGet('file_id',$f->id)) { + if ($f->getEnclosure()) { $is_attachment = true; $attachment_id = $f->id; @@ -896,14 +981,40 @@ function common_linkify($url) { $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) +/** + * Find and shorten links in a given chunk of text if it's longer than the + * configured notice content limit (or unconditionally). + * + * Side effects: may save file and file_redirection records for referenced URLs. + * + * Pass the $user option or call $user->shortenLinks($text) to ensure the proper + * user's options are used; otherwise the current web session user's setitngs + * will be used or ur1.ca if there is no active web login. + * + * @param string $text + * @param boolean $always (optional) + * @param User $user (optional) + * + * @return string + */ +function common_shorten_links($text, $always = false, User $user=null) { $maxLength = Notice::maxContent(); if (!$always && ($maxLength == 0 || mb_strlen($text) <= $maxLength)) return $text; - return common_replace_urls_callback($text, array('File_redirection', 'makeShort')); + return common_replace_urls_callback($text, array('File_redirection', 'makeShort'), $user); } /** @@ -919,7 +1030,12 @@ function common_shorten_links($text, $always = false) function common_validate_utf8($str) { // preg_replace will return NULL on invalid UTF-8 input. - return preg_replace('//u', '', $str); + // + // 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); } /** @@ -959,8 +1075,9 @@ function common_tag_link($tag) $canonical = common_canonical_tag($tag); if (common_config('singleuser', 'enabled')) { // regular TagAction isn't set up in 1user mode + $nickname = User::singleUserNickname(); $url = common_local_url('showstream', - array('nickname' => common_config('singleuser', 'nickname'), + array('nickname' => $nickname, 'tag' => $canonical)); } else { $url = common_local_url('tag', array('tag' => $canonical)); @@ -985,6 +1102,13 @@ function common_valid_profile_tag($str) return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str); } +/** + * + * @param $sender_id + * @param $nickname + * @return + * @access private + */ function common_group_link($sender_id, $nickname) { $sender = Profile::staticGet($sender_id); @@ -993,7 +1117,7 @@ function common_group_link($sender_id, $nickname) $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'); @@ -1007,13 +1131,37 @@ function common_group_link($sender_id, $nickname) } } +/** + * Resolve an ambiguous profile nickname reference, checking in following order: + * - profiles that $sender subscribes to + * - profiles that subscribe to $sender + * - local user profiles + * + * WARNING: does not validate or normalize $nickname -- MUST BE PRE-VALIDATED + * OR THERE MAY BE A RISK OF SQL INJECTION ATTACKS. THIS FUNCTION DOES NOT + * ESCAPE SQL. + * + * @fixme validate input + * @fixme escape SQL + * @fixme fix or remove mystery third parameter + * @fixme is $sender a User or Profile? + * + * @param $sender the user or profile in whose context we're looking + * @param string $nickname validated nickname of + * @param $dt unused mystery parameter; in Notice reply-to handling a timestamp is passed. + * + * @return Profile or null + */ function common_relative_profile($sender, $nickname, $dt=null) { + // Will throw exception on invalid input. + $nickname = Nickname::normalize($nickname); + // Try to find profiles this profile is subscribed to that have this nickname $recipient = new Profile(); // XXX: use a join instead of a subquery - $recipient->whereAdd('EXISTS (SELECT subscribed from subscription where subscriber = '.$sender->id.' and subscribed = id)', 'AND'); - $recipient->whereAdd("nickname = '" . trim($nickname) . "'", 'AND'); + $recipient->whereAdd('EXISTS (SELECT subscribed from subscription where subscriber = '.intval($sender->id).' and subscribed = id)', 'AND'); + $recipient->whereAdd("nickname = '" . $recipient->escape($nickname) . "'", 'AND'); if ($recipient->find(true)) { // XXX: should probably differentiate between profiles with // the same name by date of most recent update @@ -1022,8 +1170,8 @@ function common_relative_profile($sender, $nickname, $dt=null) // Try to find profiles that listen to this profile and that have this nickname $recipient = new Profile(); // XXX: use a join instead of a subquery - $recipient->whereAdd('EXISTS (SELECT subscriber from subscription where subscribed = '.$sender->id.' and subscriber = id)', 'AND'); - $recipient->whereAdd("nickname = '" . trim($nickname) . "'", 'AND'); + $recipient->whereAdd('EXISTS (SELECT subscriber from subscription where subscribed = '.intval($sender->id).' and subscriber = id)', 'AND'); + $recipient->whereAdd("nickname = '" . $recipient->escape($nickname) . "'", 'AND'); if ($recipient->find(true)) { // XXX: should probably differentiate between profiles with // the same name by date of most recent update @@ -1064,7 +1212,17 @@ function common_local_url($action, $args=null, $params=null, $fragment=null, $ad function common_is_sensitive($action) { - static $sensitive = array('login', 'register', 'passwordsettings', 'api'); + static $sensitive = array( + 'login', + 'register', + 'passwordsettings', + 'api', + 'ApiOauthRequestToken', + 'ApiOauthAccessToken', + 'ApiOauthAuthorize', + 'ApiOauthPin', + 'showapplication' + ); $ssl = null; if (Event::handle('SensitiveAction', array($action, &$ssl))) { @@ -1472,6 +1630,7 @@ function common_request_id() function common_log($priority, $msg, $filename=null) { if(Event::handle('StartLog', array(&$priority, &$msg, &$filename))){ + $msg = (empty($filename)) ? $msg : basename($filename) . ' - ' . $msg; $msg = '[' . common_request_id() . '] ' . $msg; $logfile = common_config('site', 'logfile'); if ($logfile) { @@ -1899,15 +2058,18 @@ function common_database_tablename($tablename) * Length is not considered. * * @param string $long_url + * @param User $user to specify a particular user's options * @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, User $user=null) { $long_url = trim($long_url); - $user = common_current_user(); + if (empty($user)) { + // Current web session + $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 @@ -2022,3 +2184,40 @@ function common_nicknamize($str) $str = preg_replace('/\W/', '', $str); return strtolower($str); } + +function common_perf_counter($key, $val=null) +{ + global $_perfCounters; + if (isset($_perfCounters)) { + if (common_config('site', 'logperf')) { + if (array_key_exists($key, $_perfCounters)) { + $_perfCounters[$key][] = $val; + } else { + $_perfCounters[$key] = array($val); + } + if (common_config('site', 'logperf_detail')) { + common_log(LOG_DEBUG, "PERF COUNTER HIT: $key $val"); + } + } + } +} + +function common_log_perf_counters() +{ + if (common_config('site', 'logperf')) { + global $_startTime, $_perfCounters; + + if (isset($_startTime)) { + $endTime = microtime(true); + $diff = round(($endTime - $_startTime) * 1000); + common_log(LOG_DEBUG, "PERF runtime: ${diff}ms"); + } + $counters = $_perfCounters; + ksort($counters); + foreach ($counters as $key => $values) { + $count = count($values); + $unique = count(array_unique($values)); + common_log(LOG_DEBUG, "PERF COUNTER: $key $count ($unique unique)"); + } + } +}