'&')); $start = strpos($sbody, $regs[0]) + strlen($regs[0]); $sbody = substr($sbody, $start); } /* Replace each email address with a compose URL */ foreach ($addresses as $text => $email) { $comp_uri = makeComposeLink('src/compose.php?send_to='.urlencode($email), $text); $body = str_replace($text, $comp_uri, $body); } /* Return number of unique addresses found */ return count($addresses); } /* We don't want to re-initialize this stuff for every line. Save work * and just do it once here. */ global $url_parser_url_tokens; $url_parser_url_tokens = array( 'http://', 'https://', 'ftp://', 'telnet:', // Special case -- doesn't need the slashes 'mailto:', // Special case -- doesn't use the slashes 'gopher://', 'news://'); global $url_parser_poss_ends; $url_parser_poss_ends = array(' ', "\n", "\r", '<', '>', ".\r", ".\n", '. ', ' ', ')', '(', '"', '<', '>', '.<', ']', '[', '{', '}', "\240", ', ', '. ', ",\n", ",\r"); /** * rfc 2368 (mailto URL) preg_match() regexp * @link http://www.ietf.org/rfc/rfc2368.txt * @global string MailTo_PReg_Match the encapsulated regexp for preg_match() */ global $MailTo_PReg_Match; $Mailto_Email_RegExp = '[0-9a-z%]([-_.+%]?[0-9a-z])*(%' . $Host_RegExp_Match . ')?@' . $Host_RegExp_Match; $MailTo_PReg_Match = '/((?:' . $Mailto_Email_RegExp . ')*)((?:\?(?:to|cc|bcc|subject|body)=[^\s\?&=,()]+)?(?:&(?:to|cc|bcc|subject|body)=[^\s\?&=,()]+)*)/i'; /** * Parses a body and converts all found URLs to clickable links. * * @param string body the body to process, by ref * @return void */ function parseUrl (&$body) { global $url_parser_poss_ends, $url_parser_url_tokens; $start = 0; $blength = strlen($body); while ($start < $blength) { $target_token = ''; $target_pos = $blength; /* Find the first token to replace */ foreach ($url_parser_url_tokens as $the_token) { $pos = strpos(strtolower($body), $the_token, $start); if (is_int($pos) && $pos < $target_pos) { $target_pos = $pos; $target_token = $the_token; } } /* Look for email addresses between $start and $target_pos */ $check_str = substr($body, $start, $target_pos-$start); if (parseEmail($check_str)) { replaceBlock($body, $check_str, $start, $target_pos); $blength = strlen($body); $target_pos = strlen($check_str) + $start; } // rfc 2368 (mailto URL) if ($target_token == 'mailto:') { $target_pos += 7; //skip mailto: $end = $blength; $mailto = substr($body, $target_pos, $end-$target_pos); global $MailTo_PReg_Match; if ((preg_match($MailTo_PReg_Match, $mailto, $regs)) && ($regs[0] != '')) { //sm_print_r($regs); $mailto_before = $target_token . $regs[0]; $mailto_params = $regs[10]; if ($regs[1]) { //if there is an email addr before '?', we need to merge it with the params $to = 'to=' . $regs[1]; if (strpos($mailto_params, 'to=') > -1) //already a 'to=' $mailto_params = str_replace('to=', $to . '%2C%20', $mailto_params); else { if ($mailto_params) //already some params, append to them $mailto_params .= '&' . $to; else $mailto_params .= '?' . $to; } } $url_str = preg_replace(array('/to=/i', '/(?$url"; replaceBlock($body,$url_str,$target_pos,$end); $target_pos += strlen($url_str); } else { // Not quite a valid link, skip ahead to next chance $target_pos += strlen($target_token); } } /* Move forward */ $start = $target_pos; $blength = strlen($body); } } /** * Parses a string and returns the first e-mail address found. * * @param string string the string to process * @return string the first e-mail address found */ function getEmail($string) { global $Email_RegExp_Match; $addresses = array(); /* Find all the email addresses in the body */ while (eregi($Email_RegExp_Match, $string, $regs)) { $addresses[$regs[0]] = strtr($regs[0], array('&' => '&')); $start = strpos($string, $regs[0]) + strlen($regs[0]); $string = substr($string, $start); } /* Return the first address, or an empty string if no address was found */ $addresses = array_values($addresses); return (array_key_exists(0, $addresses) ? $addresses[0] : ''); } /** * Finds first occurrence of 8bit data in the string * * Function finds first 8bit symbol or html entity that represents 8bit character. * Search start is defined by $offset argument. Search ends at $maxlength position. * If $maxlength is not defined or bigger than provided string, search ends when * string ends. * * Check returned data type in order to avoid confusion between bool(false) * (not found) and int(0) (first char in the string). * @param string $haystack * @param integer $offset * @param integer $maxlength * @return mixed integer with first 8bit character position or boolean false * @since 1.5.2 */ function sq_strpos_8bit($haystack,$offset=0,$maxlength=false) { $ret = false; if ($maxlength===false || strlen($haystack) < $maxlength) { $maxlength=strlen($haystack); } for($i=$offset;$i<$maxlength;$i++) { /* rh7-8 compatibility. don't use full 8bit range in regexp */ if (preg_match('/[\200-\237]|\240|[\241-\377]/',$haystack[$i])) { /* we have 8bit char. stop here and return position */ $ret = $i; break; } elseif ($haystack[$i]=='&') { $substring = substr($haystack,$i); /** * 1. look for "&#(decimal number);" where decimal_number is bigger than 127 * 2. look for "&x(hexadecimal number);", where hex number is bigger than x7f * 3. look for any html character entity that is not 7bit html special char. Use * own sq_get_html_translation_table() function with 'utf-8' character set in * order to get all html entities. */ if ((preg_match('/^&#(\d+);/',$substring,$match) && $match[1]>127) || (preg_match('/^&x([0-9a-f]+);/i',$substring,$match) && $match[1]>"\x7f") || (preg_match('/^&([a-z]+);/i',$substring,$match) && !in_array($match[0],get_html_translation_table(HTML_SPECIALCHARS)) && in_array($match[0],sq_get_html_translation_table(HTML_ENTITIES,ENT_COMPAT,'utf-8')))) { $ret = $i; break; } } } return $ret; }