X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Futil.php;h=22962325778f3f09ab28575007f637f995d91b01;hb=ebc8e2a0e7dde135aef73be7b3d96dff155fe091;hp=bda78afe13f9de41a21287b317a1b87393cb8795;hpb=2a4dc77a633cc78907934fd93200ac16d55be78e;p=quix0rs-gnu-social.git diff --git a/lib/util.php b/lib/util.php index bda78afe13..2296232577 100644 --- a/lib/util.php +++ b/lib/util.php @@ -210,14 +210,18 @@ function common_language() /** * Salted, hashed passwords are stored in the DB. */ -function common_munge_password($password, $id) +function common_munge_password($password, $id, Profile $profile=null) { - if (is_object($id) || is_object($password)) { - $e = new Exception(); - common_log(LOG_ERR, __METHOD__ . ' object in param to common_munge_password ' . - str_replace("\n", " ", $e->getTraceAsString())); + $hashed = null; + + if (Event::handle('StartHashPassword', array(&$hashed, $password, $profile))) { + Event::handle('EndHashPassword', array(&$hashed, $password, $profile)); + } + if (empty($hashed)) { + throw new PasswordHashException(); } - return md5($password . $id); + + return $hashed; } /** @@ -237,20 +241,18 @@ function common_check_user($nickname, $password) if (common_is_email($nickname)) { $user = User::getKV('email', common_canonical_email($nickname)); } else { - $user = User::getKV('nickname', common_canonical_nickname($nickname)); + $user = User::getKV('nickname', Nickname::normalize($nickname)); } - if (!empty($user)) { - if (!empty($password)) { // never allow login with blank password - if (0 == strcmp(common_munge_password($password, $user->id), - $user->password)) { - //internal checking passed - $authenticatedUser = $user; - } + if ($user instanceof User && !empty($password)) { + if (0 == strcmp(common_munge_password($password, $user->id), + $user->password)) { + //internal checking passed + $authenticatedUser = $user; } } - Event::handle('EndCheckPassword', array($nickname, $password, $authenticatedUser)); } + Event::handle('EndCheckPassword', array($nickname, $password, $authenticatedUser)); return $authenticatedUser; } @@ -316,11 +318,11 @@ function common_set_user($user) } else if (is_string($user)) { $nickname = $user; $user = User::getKV('nickname', $nickname); - } else if (!($user instanceof User)) { + } else if (!$user instanceof User) { return false; } - if ($user) { + if ($user instanceof User) { if (Event::handle('StartSetUser', array(&$user))) { if (!empty($user)) { if (!$user->hasRight(Right::WEBLOGIN)) { @@ -370,7 +372,7 @@ function common_rememberme($user=null) $rm = new Remember_me(); - $rm->code = common_good_rand(16); + $rm->code = common_random_hexstr(16); $rm->user_id = $user->id; // Wrap the insert in some good ol' fashioned transaction code @@ -381,6 +383,7 @@ function common_rememberme($user=null) if (!$result) { common_log_db_error($rm, 'INSERT', __FILE__); + $rm->query('ROLLBACK'); return false; } @@ -429,7 +432,7 @@ function common_remembered_user() $user = User::getKV('id', $rm->user_id); - if (!$user) { + if (!$user instanceof User) { common_log(LOG_WARNING, 'No such user for rememberme: ' . $rm->user_id); common_forgetme(); return null; @@ -484,8 +487,8 @@ function common_current_user() common_ensure_session(); $id = isset($_SESSION['userid']) ? $_SESSION['userid'] : false; if ($id) { - $user = User::getKV($id); - if ($user) { + $user = User::getKV('id', $id); + if ($user instanceof User) { $_cur = $user; return $_cur; } @@ -536,7 +539,7 @@ function common_user_cache_hash($user=false) if ($user === false) { $user = common_current_user(); } - if ($user) { + if ($user instanceof User) { return crc32($user->id . ':' . $user->nickname); } else { return '0'; @@ -580,13 +583,10 @@ function common_canonical_email($email) * @param Notice $notice in whose context we're working * @return string partially rendered HTML */ -function common_render_content($text, $notice) +function common_render_content($text, Notice $notice) { $r = common_render_text($text); - $id = $notice->profile_id; $r = common_linkify_mentions($r, $notice); - $r = preg_replace('/(^|[\s\.\,\:\;]+)!(' . Nickname::DISPLAY_FMT . ')/e', - "'\\1!'.common_group_link($id, '\\2')", $r); return $r; } @@ -636,17 +636,13 @@ function common_linkify_mention($mention) $xs = new XMLStringer(false); $attrs = array('href' => $mention['url'], - 'class' => 'url'); + 'class' => 'h-card '.$mention['type']); if (!empty($mention['title'])) { $attrs['title'] = $mention['title']; } - $xs->elementStart('span', 'vcard'); - $xs->elementStart('a', $attrs); - $xs->element('span', 'fn nickname mention', $mention['text']); - $xs->elementEnd('a'); - $xs->elementEnd('span'); + $xs->element('a', $attrs, $mention['text']); $output = $xs->getString(); @@ -671,37 +667,43 @@ function common_linkify_mention($mention) * * @access private */ -function common_find_mentions($text, $notice) +function common_find_mentions($text, Notice $notice) { - $mentions = array(); - - $sender = Profile::getKV('id', $notice->profile_id); - - if (empty($sender)) { - return $mentions; + try { + $sender = Profile::getKV('id', $notice->profile_id); + } catch (NoProfileException $e) { + return array(); } + $mentions = array(); + if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) { // Get the context of the original notice, if any - $originalAuthor = null; - $originalNotice = null; - $originalMentions = array(); + $origAuthor = null; + $origNotice = null; + $origMentions = array(); // Is it a reply? - if (!empty($notice) && !empty($notice->reply_to)) { - $originalNotice = Notice::getKV('id', $notice->reply_to); - if (!empty($originalNotice)) { - $originalAuthor = Profile::getKV('id', $originalNotice->profile_id); + if ($notice instanceof Notice) { + try { + $origNotice = $notice->getParent(); + $origAuthor = $origNotice->getProfile(); - $ids = $originalNotice->getReplies(); + $ids = $origNotice->getReplies(); foreach ($ids as $id) { $repliedTo = Profile::getKV('id', $id); - if (!empty($repliedTo)) { - $originalMentions[$repliedTo->nickname] = $repliedTo; + if ($repliedTo instanceof Profile) { + $origMentions[$repliedTo->nickname] = $repliedTo; } } + } catch (NoProfileException $e) { + common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id)); + } catch (ServerException $e) { + // Probably just no parent. Should get a specific NoParentException + } catch (Exception $e) { + common_log(LOG_WARNING, __METHOD__ . ' got exception ' . get_class($e) . ' : ' . $e->getMessage()); } } @@ -719,25 +721,26 @@ function common_find_mentions($text, $notice) // Start with conversation context, then go to // sender context. - if (!empty($originalAuthor) && $originalAuthor->nickname == $nickname) { - $mentioned = $originalAuthor; - } else if (!empty($originalMentions) && - array_key_exists($nickname, $originalMentions)) { - $mentioned = $originalMentions[$nickname]; + if ($origAuthor instanceof Profile && $origAuthor->nickname == $nickname) { + $mentioned = $origAuthor; + } else if (!empty($origMentions) && + array_key_exists($nickname, $origMentions)) { + $mentioned = $origMentions[$nickname]; } else { $mentioned = common_relative_profile($sender, $nickname); } - if (!empty($mentioned)) { + if ($mentioned instanceof Profile) { $user = User::getKV('id', $mentioned->id); - if ($user) { + if ($user instanceof User) { $url = common_local_url('userbyid', array('id' => $user->id)); } else { $url = $mentioned->profileurl; } $mention = array('mentioned' => array($mentioned), + 'type' => 'mention', 'text' => $match[0], 'position' => $match[1], 'url' => $url); @@ -753,26 +756,44 @@ function common_find_mentions($text, $notice) // @#tag => mention of all subscriptions tagged 'tag' preg_match_all('/(?:^|[\s\.\,\:\;]+)@#([\pL\pN_\-\.]{1,64})/', - $text, - $hmatches, - PREG_OFFSET_CAPTURE); - + $text, $hmatches, PREG_OFFSET_CAPTURE); foreach ($hmatches[1] as $hmatch) { - $tag = common_canonical_tag($hmatch[0]); $plist = Profile_list::getByTaggerAndTag($sender->id, $tag); - if (!empty($plist) && !$plist->private) { - $tagged = $sender->getTaggedSubscribers($tag); + if (!$plist instanceof Profile_list || $plist->private) { + continue; + } + $tagged = $sender->getTaggedSubscribers($tag); + + $url = common_local_url('showprofiletag', + array('tagger' => $sender->nickname, + 'tag' => $tag)); + + $mentions[] = array('mentioned' => $tagged, + 'type' => 'list', + 'text' => $hmatch[0], + 'position' => $hmatch[1], + 'url' => $url); + } - $url = common_local_url('showprofiletag', - array('tagger' => $sender->nickname, - 'tag' => $tag)); + preg_match_all('/(?:^|[\s\.\,\:\;]+)!(' . Nickname::DISPLAY_FMT . ')/', + $text, $hmatches, PREG_OFFSET_CAPTURE); + foreach ($hmatches[1] as $hmatch) { + $nickname = Nickname::normalize($hmatch[0]); + $group = User_group::getForNickname($nickname, $sender); - $mentions[] = array('mentioned' => $tagged, - 'text' => $hmatch[0], - 'position' => $hmatch[1], - 'url' => $url); + if (!$group instanceof User_group || !$sender->isMember($group)) { + continue; } + + $profile = $group->getProfile(); + + $mentions[] = array('mentioned' => array($profile), + 'type' => 'group', + 'text' => $hmatch[0], + 'position' => $hmatch[1], + 'url' => $group->permalink(), + 'title' => $group->getFancyName()); } Event::handle('EndFindMentions', array($sender, $text, &$mentions)); @@ -808,11 +829,12 @@ function common_find_mentions_raw($text) function common_render_text($text) { - $r = htmlspecialchars($text); + $r = nl2br(htmlspecialchars($text)); $r = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $r); $r = common_replace_urls_callback($r, 'common_linkify'); - $r = preg_replace('/(^|\"\;|\'|\(|\[|\{|\s+)#([\pL\pN_\-\.]{1,64})/ue', "'\\1#'.common_tag_link('\\2')", $r); + $r = preg_replace_callback('/(^|\"\;|\'|\(|\[|\{|\s+)#([\pL\pN_\-\.]{1,64})/u', + function ($m) { return "{$m[1]}#".common_tag_link($m[2]); }, $r); // XXX: machine tags return $r; } @@ -852,7 +874,7 @@ function common_replace_urls_callback($text, $callback, $arg = null) { '(?:[\pN\pL\-\_\+\%\~]+(?:\:[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@ '[\pN\pL\-\_]+(?:\.[\pN\pL\-\_]+)*\.'. //tld list from http://data.iana.org/TLD/tlds-alpha-by-domain.txt, also added local, loc, and onion - '(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZW|local|loc|onion)'. + '(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZONE|ZW|local|loc|onion)'. ')(?![\pN\pL\-\_])'. ')'. '(?:'. @@ -926,24 +948,7 @@ function callback_helper($matches, $callback, $arg=null) { return substr($matches[0],0,$left) . $result . substr($matches[0],$right); } -if (version_compare(PHP_VERSION, '5.3.0', 'ge')) { - // lambda implementation in a separate file; PHP 5.2 won't parse it. - require_once INSTALLDIR . "/lib/curry.php"; -} else { - function curry($fn) { - $args = func_get_args(); - array_shift($args); - $id = uniqid('_partial'); - $GLOBALS[$id] = array($fn, $args); - return create_function('', - '$args = func_get_args(); '. - 'return call_user_func_array('. - '$GLOBALS["'.$id.'"][0],'. - 'array_merge('. - '$args,'. - '$GLOBALS["'.$id.'"][1]));'); - } -} +require_once INSTALLDIR . "/lib/curry.php"; function common_linkify($url) { // It comes in special'd, so we unspecial it before passing to the stringifying @@ -981,22 +986,27 @@ function common_linkify($url) { $f = File::getKV('url', $longurl); - if (empty($f)) { + if (!$f instanceof File) { if (common_config('attachments', 'process_links')) { // XXX: this writes to the database. :< - $f = File::processNew($longurl); + try { + $f = File::processNew($longurl); + } catch (ServerException $e) { + $f = null; + } } } - if (!empty($f)) { - if ($f->getEnclosure()) { + if ($f instanceof File) { + try { + $enclosure = $f->getEnclosure(); $is_attachment = true; $attachment_id = $f->id; $thumb = File_thumbnail::getKV('file_id', $f->id); - if (!empty($thumb)) { - $has_thumb = true; - } + $has_thumb = ($thumb instanceof File_thumbnail); + } catch (ServerException $e) { + // There was not enough metadata available } } @@ -1046,7 +1056,7 @@ function common_shorten_links($text, $always = false, User $user=null) $maxLength = User_urlshortener_prefs::maxNoticeLength($user); - if ($always || mb_strlen($text) > $maxLength) { + if ($always || ($maxLength != -1 && mb_strlen($text) > $maxLength)) { return common_replace_urls_callback($text, array('File_redirection', 'forceShort'), $user); } else { return common_replace_urls_callback($text, array('File_redirection', 'makeShort'), $user); @@ -1141,35 +1151,6 @@ 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::getKV($sender_id); - $group = User_group::getForNickname($nickname, $sender); - if ($sender && $group && $sender->isMember($group)) { - $attrs = array('href' => $group->permalink(), - 'class' => 'url'); - if (!empty($group->fullname)) { - $attrs['title'] = $group->getFancyName(); - } - $xs = new XMLStringer(); - $xs->elementStart('span', 'vcard'); - $xs->elementStart('a', $attrs); - $xs->element('span', 'fn nickname group', $nickname); - $xs->elementEnd('a'); - $xs->elementEnd('span'); - return $xs->getString(); - } else { - return $nickname; - } -} - /** * Resolve an ambiguous profile nickname reference, checking in following order: * - profiles that $sender subscribes to @@ -1217,10 +1198,10 @@ function common_relative_profile($sender, $nickname, $dt=null) return $recipient; } // If this is a local user, try to find a local user with that nickname. - $sender = User::getKV($sender->id); - if ($sender) { + $sender = User::getKV('id', $sender->id); + if ($sender instanceof User) { $recipient_user = User::getKV('nickname', $nickname); - if ($recipient_user) { + if ($recipient_user instanceof User) { return $recipient_user->getProfile(); } } @@ -1236,7 +1217,9 @@ function common_local_url($action, $args=null, $params=null, $fragment=null, $ad $r = Router::get(); $path = $r->build($action, $args, $params, $fragment); - $ssl = common_is_sensitive($action); + $ssl = common_config('site', 'ssl') === 'always' + || StatusNet::isHTTPS() + || common_is_sensitive($action); if (common_config('site','fancy')) { $url = common_path($path, $ssl, $addSession); @@ -1259,10 +1242,10 @@ function common_is_sensitive($action) 'register', 'passwordsettings', 'api', - 'ApiOauthRequestToken', - 'ApiOauthAccessToken', - 'ApiOauthAuthorize', - 'ApiOauthPin', + 'ApiOAuthRequestToken', + 'ApiOAuthAccessToken', + 'ApiOAuthAuthorize', + 'ApiOAuthPin', 'showapplication' ); $ssl = null; @@ -1279,6 +1262,7 @@ function common_path($relative, $ssl=false, $addSession=true) $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : ''; if (($ssl && (common_config('site', 'ssl') === 'sometimes')) + || StatusNet::isHTTPS() || common_config('site', 'ssl') === 'always') { $proto = 'https'; if (is_string(common_config('site', 'sslserver')) && @@ -1307,26 +1291,26 @@ function common_path($relative, $ssl=false, $addSession=true) function common_inject_session($url, $serverpart = null) { - if (common_have_session()) { + if (!common_have_session()) { + return $url; + } - if (empty($serverpart)) { - $serverpart = parse_url($url, PHP_URL_HOST); - } + if (empty($serverpart)) { + $serverpart = parse_url($url, PHP_URL_HOST); + } - $currentServer = (array_key_exists('HTTP_HOST', $_SERVER)) ? $_SERVER['HTTP_HOST'] : null; + $currentServer = (array_key_exists('HTTP_HOST', $_SERVER)) ? $_SERVER['HTTP_HOST'] : null; - // Are we pointing to another server (like an SSL server?) + // Are we pointing to another server (like an SSL server?) - if (!empty($currentServer) && - 0 != strcasecmp($currentServer, $serverpart)) { - // Pass the session ID as a GET parameter - $sesspart = session_name() . '=' . session_id(); - $i = strpos($url, '?'); - if ($i === false) { // no GET params, just append - $url .= '?' . $sesspart; - } else { - $url = substr($url, 0, $i + 1).$sesspart.'&'.substr($url, $i + 1); - } + if (!empty($currentServer) && 0 != strcasecmp($currentServer, $serverpart)) { + // Pass the session ID as a GET parameter + $sesspart = session_name() . '=' . session_id(); + $i = strpos($url, '?'); + if ($i === false) { // no GET params, just append + $url .= '?' . $sesspart; + } else { + $url = substr($url, 0, $i + 1).$sesspart.'&'.substr($url, $i + 1); } } @@ -1395,7 +1379,8 @@ function common_exact_date($dt) $dateStr = date('d F Y H:i:s', strtotime($dt)); $d = new DateTime($dateStr, $_utc); $d->setTimezone($_siteTz); - return $d->format(DATE_RFC850); + // TRANS: Human-readable full date-time specification (formatting on http://php.net/date) + return $d->format(_('l, d-M-Y H:i:s T')); } function common_date_w3dtf($dt) @@ -1506,21 +1491,6 @@ function common_enqueue_notice($notice) return true; } -/** - * Legacy function to broadcast profile updates to OMB remote subscribers. - * - * XXX: This probably needs killing, but there are several bits of code - * that broadcast profile changes that need to be dealt with. AFAIK - * this function is only used for OMB. -z - * - * Since this may be slow with a lot of subscribers or bad remote sites, - * this is run through the background queues if possible. - */ -function common_broadcast_profile(Profile $profile) -{ - Event::handle('BroadcastProfile', array($profile)); -} - function common_profile_url($nickname) { return common_local_url('showstream', array('nickname' => $nickname), @@ -1542,16 +1512,18 @@ function common_root_url($ssl=false) /** * returns $bytes bytes of random data as a hexadecimal string - * "good" here is a goal and not a guarantee */ -function common_good_rand($bytes) +function common_random_hexstr($bytes) { - // XXX: use random.org...? - if (@file_exists('/dev/urandom')) { - return common_urandom($bytes); - } else { // FIXME: this is probably not good enough - return common_mtrand($bytes); + $str = @file_exists('/dev/urandom') + ? common_urandom($bytes) + : common_mtrand($bytes); + + $hexstr = ''; + for ($i = 0; $i < $bytes; $i++) { + $hexstr .= sprintf("%02x", ord($str[$i])); } + return $hexstr; } function common_urandom($bytes) @@ -1560,20 +1532,16 @@ function common_urandom($bytes) // should not block $src = fread($h, $bytes); fclose($h); - $enc = ''; - for ($i = 0; $i < $bytes; $i++) { - $enc .= sprintf("%02x", (ord($src[$i]))); - } - return $enc; + return $src; } function common_mtrand($bytes) { - $enc = ''; + $str = ''; for ($i = 0; $i < $bytes; $i++) { - $enc .= sprintf("%02x", mt_rand(0, 255)); + $str .= chr(mt_rand(0, 255)); } - return $enc; + return $str; } /** @@ -1688,8 +1656,10 @@ function common_debug($msg, $filename=null) function common_log_db_error(&$object, $verb, $filename=null) { + global $_PEAR; + $objstr = common_log_objstring($object); - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + $last_error = &$_PEAR->getStaticProperty('DB_DataObject','lastError'); if (is_object($last_error)) { $msg = $last_error->message; } else { @@ -1719,9 +1689,13 @@ function common_log_objstring(&$object) return $objstring; } -function common_valid_http_url($url) +function common_valid_http_url($url, $secure=false) { - return Validate::uri($url, array('allowed_schemes' => array('http', 'https'))); + // If $secure is true, only allow https URLs to pass + // (if false, we use '?' in 'https?' to say the 's' is optional) + $regex = $secure ? '/^https$/' : '/^https?$/'; + return filter_var($url, FILTER_VALIDATE_URL) + && preg_match($regex, parse_url($url, PHP_URL_SCHEME)); } function common_valid_tag($tag) @@ -1810,6 +1784,53 @@ function common_accept_to_prefs($accept, $def = '*/*') return $prefs; } +// Match by our supported file extensions +function common_supported_ext_to_mime($fileext) +{ + // Accept a filename and take out the extension + if (strpos($fileext, '.') !== false) { + $fileext = substr(strrchr($fileext, '.'), 1); + } + + $supported = common_config('attachments', 'supported'); + foreach($supported as $type => $ext) { + if ($ext === $fileext) { + return $type; + } + } + + throw new ServerException('Unsupported file extension'); +} + +// Match by our supported mime types +function common_supported_mime_to_ext($mimetype) +{ + $supported = common_config('attachments', 'supported'); + foreach($supported as $type => $ext) { + if ($mimetype === $type) { + return $ext; + } + } + + throw new ServerException('Unsupported MIME type'); +} + +// The MIME "media" is the part before the slash (video in video/webm) +function common_get_mime_media($type) +{ + $tmp = explode('/', $type); + return strtolower($tmp[0]); +} + +function common_bare_mime($mimetype) +{ + $mimetype = mb_strtolower($mimetype); + if ($semicolon = mb_strpos($mimetype, ';')) { + $mimetype = mb_substr($mimetype, 0, $semicolon); + } + return $mimetype; +} + function common_mime_type_match($type, $avail) { if(array_key_exists($type, $avail)) { @@ -1941,13 +1962,6 @@ function common_user_uri(&$user) null, null, false); } -function common_notice_uri(&$notice) -{ - return common_local_url('shownotice', - array('notice' => $notice->id), - null, null, false); -} - // 36 alphanums - lookalikes (0, O, 1, I) = 32 chars = 5 bits function common_confirmation_code($bits) @@ -1958,7 +1972,7 @@ function common_confirmation_code($bits) $code = ''; for ($i = 0; $i < $chars; $i++) { // XXX: convert to string and back - $num = hexdec(common_good_rand(1)); + $num = hexdec(common_random_hexstr(1)); // XXX: randomness is too precious to throw away almost // 40% of the bits we get! $code .= $codechars[$num%32]; @@ -1967,9 +1981,12 @@ function common_confirmation_code($bits) } // convert markup to HTML - function common_markup_to_html($c, $args=null) { + if ($c === null) { + return ''; + } + if (is_null($args)) { $args = array(); } @@ -1980,11 +1997,12 @@ function common_markup_to_html($c, $args=null) $c = preg_replace('/%%arg.'.$name.'%%/', $value, $c); } - $c = preg_replace('/%%user.(\w+)%%/e', "common_user_property('\\1')", $c); - $c = preg_replace('/%%action.(\w+)%%/e', "common_local_url('\\1')", $c); - $c = preg_replace('/%%doc.(\w+)%%/e', "common_local_url('doc', array('title'=>'\\1'))", $c); - $c = preg_replace('/%%(\w+).(\w+)%%/e', 'common_config(\'\\1\', \'\\2\')', $c); - return Markdown($c); + $c = preg_replace_callback('/%%user.(\w+)%%/', function ($m) { return common_user_property($m[1]); }, $c); + $c = preg_replace_callback('/%%action.(\w+)%%/', function ($m) { return common_local_url($m[1]); }, $c); + $c = preg_replace_callback('/%%doc.(\w+)%%/', function ($m) { return common_local_url('doc', array('title'=>$m[1])); }, $c); + $c = preg_replace_callback('/%%(\w+).(\w+)%%/', function ($m) { return common_config($m[1], $m[2]); }, $c); + + return \Michelf\Markdown::defaultTransform($c); } function common_user_property($property) @@ -2004,7 +2022,11 @@ function common_user_property($property) return $profile->$property; break; case 'avatar': - return $profile->getAvatar(AVATAR_STREAM_SIZE); + try { + return $profile->getAvatar(AVATAR_STREAM_SIZE); + } catch (Exception $e) { + return null; + } break; case 'bestname': return $profile->getBestName(); @@ -2020,9 +2042,9 @@ function common_profile_uri($profile) if (!empty($profile)) { if (Event::handle('StartCommonProfileURI', array($profile, &$uri))) { - $user = User::getKV($profile->id); - if (!empty($user)) { - $uri = $user->uri; + $user = User::getKV('id', $profile->id); + if ($user instanceof User) { + $uri = $user->getUri(); } Event::handle('EndCommonProfileURI', array($profile, &$uri)); } @@ -2086,7 +2108,7 @@ function common_session_token() { common_ensure_session(); if (!array_key_exists('token', $_SESSION)) { - $_SESSION['token'] = common_good_rand(64); + $_SESSION['token'] = common_random_hexstr(64); } return $_SESSION['token']; } @@ -2150,7 +2172,7 @@ function common_shorten_url($long_url, User $user=null, $force = false) // $force forces shortening even if it's not strictly needed // I doubt URL shortening is ever 'strictly' needed. - ESP - if (mb_strlen($long_url) < $maxUrlLength && !$force) { + if (($maxUrlLength == -1 || mb_strlen($long_url) < $maxUrlLength) && !$force) { return $long_url; } @@ -2159,17 +2181,16 @@ function common_shorten_url($long_url, User $user=null, $force = false) if (Event::handle('StartShortenUrl', array($long_url, $shortenerName, &$shortenedUrl))) { if ($shortenerName == 'internal') { - $f = File::processNew($long_url); - if (empty($f)) { - return $long_url; - } else { - $shortenedUrl = common_local_url('redirecturl', - array('id' => $f->id)); + try { + $f = File::processNew($long_url); + $shortenedUrl = common_local_url('redirecturl', array('id' => $f->id)); if ((mb_strlen($shortenedUrl) < mb_strlen($long_url)) || $force) { return $shortenedUrl; } else { return $long_url; } + } catch (ServerException $e) { + return $long_url; } } else { return $long_url; @@ -2274,8 +2295,11 @@ function common_url_to_nickname($url) function common_nicknamize($str) { - $str = preg_replace('/\W/', '', $str); - return strtolower($str); + try { + return Nickname::normalize($str); + } catch (NicknameException $e) { + return null; + } } function common_perf_counter($key, $val=null) @@ -2289,7 +2313,7 @@ function common_perf_counter($key, $val=null) $_perfCounters[$key] = array($val); } if (common_config('site', 'logperf_detail')) { - common_log(LOG_DEBUG, "PERF COUNTER HIT: $key $val"); + common_debug("PERF COUNTER HIT: $key $val"); } } } @@ -2303,14 +2327,14 @@ function common_log_perf_counters() if (isset($_startTime)) { $endTime = microtime(true); $diff = round(($endTime - $_startTime) * 1000); - common_log(LOG_DEBUG, "PERF runtime: ${diff}ms"); + common_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)"); + common_debug("PERF COUNTER: $key $count ($unique unique)"); } } } @@ -2347,3 +2371,12 @@ function common_log_delta($comment=null) common_debug(sprintf("%s: %d %d", $comment, $mtotal, round($ttotal * 1000000))); } + +function common_strip_html($html, $trim=true, $save_whitespace=false) +{ + if (!$save_whitespace) { + $html = preg_replace('/\s+/', ' ', $html); + } + $text = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8'); + return $trim ? trim($text) : $text; +}