/**
* Salted, hashed passwords are stored in the DB.
*/
-function common_munge_password($password, $id)
+function common_munge_password($password, 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;
}
/**
if (Event::handle('StartCheckPassword', array($nickname, $password, &$authenticatedUser))) {
if (common_is_email($nickname)) {
- $user = User::staticGet('email', common_canonical_email($nickname));
+ $user = User::getKV('email', common_canonical_email($nickname));
} else {
- $user = User::staticGet('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->getProfile()), $user->password)) {
+ //internal checking passed
+ $authenticatedUser = $user;
}
}
- Event::handle('EndCheckPassword', array($nickname, $password, $authenticatedUser));
}
+ Event::handle('EndCheckPassword', array($nickname, $password, $authenticatedUser));
return $authenticatedUser;
}
return true;
} else if (is_string($user)) {
$nickname = $user;
- $user = User::staticGet('nickname', $nickname);
- } else if (!($user instanceof User)) {
+ $user = User::getKV('nickname', $nickname);
+ } 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)) {
$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
if (!$result) {
common_log_db_error($rm, 'INSERT', __FILE__);
+ $rm->query('ROLLBACK');
return false;
}
return null;
}
- $rm = Remember_me::staticGet('code', $code);
+ $rm = Remember_me::getKV('code', $code);
if (!$rm) {
common_log(LOG_WARNING, 'No such remember code: ' . $code);
return null;
}
- $user = User::staticGet('id', $rm->user_id);
+ $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;
common_ensure_session();
$id = isset($_SESSION['userid']) ? $_SESSION['userid'] : false;
if ($id) {
- $user = User::staticGet($id);
- if ($user) {
+ $user = User::getKV('id', $id);
+ if ($user instanceof User) {
$_cur = $user;
return $_cur;
}
if ($user === false) {
$user = common_current_user();
}
- if ($user) {
+ if ($user instanceof User) {
return crc32($user->id . ':' . $user->nickname);
} else {
return '0';
return $email;
}
+function common_purify($html)
+{
+ require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
+
+ $config = array('safe' => 1, // means that elements=* means elements=*-applet-embed-iframe-object-script or so
+ 'elements' => '*',
+ 'deny_attribute' => 'id,style,on*');
+
+ // Remove more elements than what the 'safe' filter gives (elements must be '*' before this)
+ // http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm#s3.6
+ foreach (common_config('htmlfilter') as $tag=>$filter) {
+ if ($filter === true) {
+ $config['elements'] .= "-{$tag}";
+ }
+ }
+
+ $html = common_remove_unicode_formatting($html);
+
+ return htmLawed($html, $config);
+}
+
+function common_remove_unicode_formatting($text)
+{
+ // Strip Unicode text formatting/direction codes
+ // this is pretty dangerous for visualisation of text and can be used for mischief
+ return preg_replace('/[\\x{200b}-\\x{200f}\\x{202a}-\\x{202e}]/u', '', $text);
+}
+
/**
* 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
+ * @param string $text partially rendered HTML
+ * @param Profile $author the Profile that is composing the current notice
+ * @param Notice $parent the Notice this is sent in reply to, if any
* @return string partially rendered HTML
*/
-function common_render_content($text, $notice)
+function common_render_content($text, Profile $author, Notice $parent=null)
{
- $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;
+ $text = common_render_text($text);
+ $text = common_linkify_mentions($text, $author, $parent);
+ return $text;
}
/**
*
* 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
+ * @param string $text partially-rendered HTML
+ * @param Profile $author the Profile that is composing the current notice
+ * @param Notice $parent the Notice this is sent in reply to, if any
* @return string partially-rendered HTML
*/
-function common_linkify_mentions($text, $notice)
+function common_linkify_mentions($text, Profile $author, Notice $parent=null)
{
- $mentions = common_find_mentions($text, $notice);
+ $mentions = common_find_mentions($text, $author, $parent);
// We need to go through in reverse order by position,
// so our positions stay valid despite our fudging with the
$linkText = common_linkify_mention($mention);
- $text = substr_replace($text, $linkText, $position, mb_strlen($mention['text']));
+ $text = substr_replace($text, $linkText, $position, $mention['length']);
}
return $text;
}
-function common_linkify_mention($mention)
+function common_linkify_mention(array $mention)
{
$output = null;
$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();
return $output;
}
+function common_get_attentions($text, Profile $sender, Notice $parent=null)
+{
+ $mentions = common_find_mentions($text, $sender, $parent);
+ $atts = array();
+ foreach ($mentions as $mention) {
+ foreach ($mention['mentioned'] as $mentioned) {
+ $atts[$mentioned->getUri()] = $mentioned->getObjectType();
+ }
+ }
+ if ($parent instanceof Notice) {
+ $parentAuthor = $parent->getProfile();
+ // afaik groups can't be authors
+ $atts[$parentAuthor->getUri()] = ActivityObject::PERSON;
+ }
+ return $atts;
+}
+
/**
* Find @-mentions in the given text, using the given notice object as context.
* References will be resolved with common_relative_profile() against the user
* 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
+ * @param string $text
+ * @param Profile $sender the Profile that is sending the current text
+ * @param Notice $parent the Notice this text is in reply to, if any
*
* @return array
*
* @access private
*/
-function common_find_mentions($text, $notice)
+function common_find_mentions($text, Profile $sender, Notice $parent=null)
{
$mentions = array();
- $sender = Profile::staticGet('id', $notice->profile_id);
-
- if (empty($sender)) {
- return $mentions;
- }
-
if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
// Get the context of the original notice, if any
- $originalAuthor = null;
- $originalNotice = null;
- $originalMentions = array();
-
- // Is it a reply?
-
- if (!empty($notice) && !empty($notice->reply_to)) {
- $originalNotice = Notice::staticGet('id', $notice->reply_to);
- if (!empty($originalNotice)) {
- $originalAuthor = Profile::staticGet('id', $originalNotice->profile_id);
-
- $ids = $originalNotice->getReplies();
-
- foreach ($ids as $id) {
- $repliedTo = Profile::staticGet('id', $id);
- if (!empty($repliedTo)) {
- $originalMentions[$repliedTo->nickname] = $repliedTo;
- }
+ $origMentions = array();
+
+ // Does it have a parent notice for context?
+ if ($parent instanceof Notice) {
+ $ids = $parent->getReplies(); // replied-to _profile ids_
+
+ foreach ($ids as $id) {
+ try {
+ $repliedTo = Profile::getByID($id);
+ $origMentions[$repliedTo->getNickname()] = $repliedTo;
+ } catch (NoResultException $e) {
+ // continue foreach
}
}
}
// 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 ($parent instanceof Notice && $parent->getProfile()->getNickname() === $nickname) {
+ $mentioned = $parent->getProfile();
+ } else if (!empty($origMentions) &&
+ array_key_exists($nickname, $origMentions)) {
+ $mentioned = $origMentions[$nickname];
} else {
+ // sets to null if no match
$mentioned = common_relative_profile($sender, $nickname);
}
- if (!empty($mentioned)) {
- $user = User::staticGet('id', $mentioned->id);
+ if ($mentioned instanceof Profile) {
+ $user = User::getKV('id', $mentioned->id);
- if ($user) {
- $url = common_local_url('userbyid', array('id' => $user->id));
- } else {
- $url = $mentioned->profileurl;
+ try {
+ $url = $mentioned->getUrl();
+ } catch (InvalidUrlException $e) {
+ $url = common_local_url('userbyid', array('id' => $mentioned->getID()));
}
$mention = array('mentioned' => array($mentioned),
+ 'type' => 'mention',
'text' => $match[0],
'position' => $match[1],
+ 'length' => mb_strlen($match[0]),
+ 'title' => $mentioned->getFullname(),
'url' => $url);
- if (!empty($mentioned->fullname)) {
- $mention['title'] = $mentioned->fullname;
- }
-
$mentions[] = $mention;
}
}
// @#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->getID(), $tag);
+ if (!$plist instanceof Profile_list || $plist->private) {
+ continue;
+ }
+ $tagged = $sender->getTaggedSubscribers($tag);
+
+ $url = common_local_url('showprofiletag',
+ array('nickname' => $sender->getNickname(),
+ 'tag' => $tag));
+
+ $mentions[] = array('mentioned' => $tagged,
+ 'type' => 'list',
+ 'text' => $hmatch[0],
+ 'position' => $hmatch[1],
+ 'length' => mb_strlen($hmatch[0]),
+ 'url' => $url);
+ }
+ 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);
- $tag = common_canonical_tag($hmatch[0]);
- $plist = Profile_list::getByTaggerAndTag($sender->id, $tag);
- if (!empty($plist) && !$plist->private) {
- $tagged = $sender->getTaggedSubscribers($tag);
-
- $url = common_local_url('showprofiletag',
- array('tagger' => $sender->nickname,
- 'tag' => $tag));
-
- $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],
+ 'length' => mb_strlen($hmatch[0]),
+ 'url' => $group->permalink(),
+ 'title' => $group->getFancyName());
}
Event::handle('EndFindMentions', array($sender, $text, &$mentions));
function common_render_text($text)
{
- $r = htmlspecialchars($text);
+ $text = common_remove_unicode_formatting($text);
+ $text = 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);
+ $text = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $text);
+ $text = common_replace_urls_callback($text, 'common_linkify');
+ $text = preg_replace_callback('/(^|\"\;|\'|\(|\[|\{|\s+)#([\pL\pN_\-\.]{1,64})/u',
+ function ($m) { return "{$m[1]}#".common_tag_link($m[2]); }, $text);
// XXX: machine tags
- return $r;
+ return $text;
}
/**
'(?:'.
'(?:'. //Known protocols
'(?:'.
- '(?:(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|irc)://)'.
+ '(?:(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|ircs?)://)'.
'|'.
'(?:(?:mailto|aim|tel|xmpp):)'.
')'.
')'.
')'.
')'.
+ '|(?:(?:magnet):)'. // URLs without domain name
'|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'. //IPv4
'|(?:'. //IPv6
'\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?(?<!:)'.
- ')|(?:'. //DNS
- '(?:[\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)'.
- ')(?![\pN\pL\-\_])'.
+ ')'.
+ (common_config('linkify', 'bare_domains')
+ ? '|(?:'. //DNS
+ '(?:[\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|ZONE|ZW|local|loc|onion)'.
+ ')(?![\pN\pL\-\_])'
+ : '') . // if common_config('linkify', 'bare_domains') is false, don't add anything here
')'.
'(?:'.
'(?:\:\d+)?'. //:port
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
$canon = "mailto:$url";
$longurl = "mailto:$url";
} else {
-
$canon = File_redirection::_canonUrl($url);
-
$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)) {
- $longurl = $longurl_data;
+
+ if(isset($longurl_data->redir_url)) {
+ $longurl = $longurl_data->redir_url;
} else {
- // 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;
+ // e.g. local files
+ $longurl = $longurl_data->url;
}
}
-
- $attrs = array('href' => $canon, 'title' => $longurl);
+
+ $attrs = array('href' => $longurl, 'title' => $longurl);
$is_attachment = false;
$attachment_id = null;
// Check to see whether this is a known "attachment" URL.
- $f = File::staticGet('url', $longurl);
-
- if (empty($f)) {
+ try {
+ $f = File::getByUrl($longurl);
+ } catch (NoResultException $e) {
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::staticGet('file_id', $f->id);
- if (!empty($thumb)) {
- $has_thumb = true;
- }
+ $thumb = File_thumbnail::getKV('file_id', $f->id);
+ $has_thumb = ($thumb instanceof File_thumbnail);
+ } catch (ServerException $e) {
+ // There was not enough metadata available
}
}
*/
function common_shorten_links($text, $always = false, User $user=null)
{
- $user = common_current_user();
+ if ($user === null) {
+ $user = common_current_user();
+ }
$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);
return preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str);
}
+function common_slugify($str)
+{
+ // php5-intl is highly recommended...
+ if (!function_exists('transliterator_transliterate')) {
+ $str = preg_replace('/[^\pL\pN]/u', '', $str);
+ $str = mb_convert_case($str, MB_CASE_LOWER, 'UTF-8');
+ $str = substr($str, 0, 64);
+ return $str;
+ }
+ $str = transliterator_transliterate(
+ 'Any-Latin;' . // any charset to latin compatible
+ 'NFD;' . // decompose
+ '[:Nonspacing Mark:] Remove;' . // remove nonspacing marks (accents etc.)
+ 'NFC;' . // composite again
+ '[:Punctuation:] Remove;' . // remove punctuation (.,¿? etc.)
+ 'Lower();' . // turn into lowercase
+ 'Latin-ASCII;', // get ASCII equivalents (ð to d for example)
+ $str);
+ return preg_replace('/[^\pL\pN]/', '', $str);
+}
+
function common_tag_link($tag)
{
$canonical = common_canonical_tag($tag);
function common_canonical_tag($tag)
{
- // only alphanum
- $tag = preg_replace('/[^\pL\pN]/u', '', $tag);
- $tag = mb_convert_case($tag, MB_CASE_LOWER, "UTF-8");
- $tag = substr($tag, 0, 64);
- return $tag;
+ $tag = common_slugify($tag);
+ $tag = substr($tag, 0, 64);
+ return $tag;
}
function common_valid_profile_tag($str)
return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str);
}
-/**
- *
- * @param <type> $sender_id
- * @param <type> $nickname
- * @return <type>
- * @access private
- */
-function common_group_link($sender_id, $nickname)
-{
- $sender = Profile::staticGet($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
return $recipient;
}
// If this is a local user, try to find a local user with that nickname.
- $sender = User::staticGet($sender->id);
- if ($sender) {
- $recipient_user = User::staticGet('nickname', $nickname);
- if ($recipient_user) {
+ $sender = User::getKV('id', $sender->id);
+ if ($sender instanceof User) {
+ $recipient_user = User::getKV('nickname', $nickname);
+ if ($recipient_user instanceof User) {
return $recipient_user->getProfile();
}
}
$r = Router::get();
$path = $r->build($action, $args, $params, $fragment);
- $ssl = common_is_sensitive($action);
+ $ssl = common_config('site', 'ssl') === 'always'
+ || GNUsocial::isHTTPS()
+ || common_is_sensitive($action);
if (common_config('site','fancy')) {
$url = common_path($path, $ssl, $addSession);
'register',
'passwordsettings',
'api',
- 'ApiOauthRequestToken',
- 'ApiOauthAccessToken',
- 'ApiOauthAuthorize',
- 'ApiOauthPin',
+ 'ApiOAuthRequestToken',
+ 'ApiOAuthAccessToken',
+ 'ApiOAuthAuthorize',
+ 'ApiOAuthPin',
'showapplication'
);
$ssl = null;
$pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
if (($ssl && (common_config('site', 'ssl') === 'sometimes'))
+ || GNUsocial::isHTTPS()
|| common_config('site', 'ssl') === 'always') {
$proto = 'https';
if (is_string(common_config('site', 'sslserver')) &&
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 = $_SERVER['HTTP_HOST'];
+ $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);
}
}
$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)
header('HTTP/1.1 '.$code.' '.$status[$code]);
header("Location: $url");
+ header("Connection: close");
$xo = new XMLOutputter();
$xo->startXML('a',
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),
return $url;
}
+/**
+ * returns $bytes bytes of raw random data
+ */
+function common_random_rawstr($bytes)
+{
+ $rawstr = @file_exists('/dev/urandom')
+ ? common_urandom($bytes)
+ : common_mtrand($bytes);
+
+ return $rawstr;
+}
+
/**
* 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 = common_random_rawstr($bytes);
+
+ $hexstr = '';
+ for ($i = 0; $i < $bytes; $i++) {
+ $hexstr .= sprintf("%02x", ord($str[$i]));
}
+ return $hexstr;
}
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;
}
/**
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 {
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)
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]);
+}
+
+// Get only the mimetype and not additional info (separated from bare mime with semi-colon)
+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)) {
return $besttype;
}
-function common_config($main, $sub)
+function common_config($main, $sub=null)
{
global $config;
+ if (is_null($sub)) {
+ // Return the config category array
+ return array_key_exists($main, $config) ? $config[$main] : array();
+ }
+ // Return the config value
return (array_key_exists($main, $config) &&
array_key_exists($sub, $config[$main])) ? $config[$main][$sub] : false;
}
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)
$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];
}
// convert markup to HTML
-
function common_markup_to_html($c, $args=null)
{
+ if ($c === null) {
+ return '';
+ }
+
if (is_null($args)) {
$args = array();
}
$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)
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();
if (!empty($profile)) {
if (Event::handle('StartCommonProfileURI', array($profile, &$uri))) {
- $user = User::staticGet($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));
}
{
common_ensure_session();
if (!array_key_exists('token', $_SESSION)) {
- $_SESSION['token'] = common_good_rand(64);
+ $_SESSION['token'] = common_random_hexstr(64);
}
return $_SESSION['token'];
}
// $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;
}
if (Event::handle('StartShortenUrl',
array($long_url, $shortenerName, &$shortenedUrl))) {
if ($shortenerName == 'internal') {
- $f = File::processNew($long_url);
- if (empty($f)) {
+ 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 {
- $shortenedUrl = common_local_url('redirecturl',
- array('id' => $f->id));
- return $shortenedUrl;
}
} else {
return $long_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)
$_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");
}
}
}
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)");
}
}
}
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;
+}
+
+function html_sprintf()
+{
+ $args = func_get_args();
+ for ($i=1; $i<count($args); $i++) {
+ $args[$i] = htmlspecialchars($args[$i]);
+ }
+ return call_user_func_array('sprintf', $args);
+}