/** * Convert special chars (like german umlauts) to ASCII characters. * * @todo dh> IMHO this function should not be included in a file that gets used often/always. * @param string * @return string */ function replace_special_chars($str) { global $evo_charset; if (can_convert_charsets('UTF-8', $evo_charset) && can_convert_charsets('UTF-8', 'ISO-8859-1')) { $str = convert_charset($str, 'UTF-8', $evo_charset); // TODO: add more...?! $search = array('Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', 'ß', 'à', 'ç', 'è', 'é', 'ì', 'ò', 'ô', 'ù'); // iso-8859-1 $replace = array('Ae', 'ae', 'Oe', 'oe', 'Ue', 'ue', 'ss', 'a', 'c', 'e', 'e', 'i', 'o', 'o', 'u'); foreach ($search as $k => $v) { // convert $search to UTF-8 $search[$k] = convert_charset($v, 'UTF-8', 'ISO-8859-1'); } $str = str_replace($search, $replace, $str); // Replace HTML entities $str = htmlentities($str, ENT_NOQUOTES, 'UTF-8'); } else { // Replace HTML entities only $str = htmlentities($str, ENT_NOQUOTES, $evo_charset); } // Keep only one char in entities! $str = preg_replace('/&(.).+?;/', '$1', $str); // Replace non acceptable chars $str = preg_replace('/[^A-Za-z0-9_]+/', '-', $str); // Remove '-' at start and end: $str = preg_replace('/^-+/', '', $str); $str = preg_replace('/-+$/', '', $str); return $str; }
function set_euckr($str) { if (is_utf8($str)) { $str = convert_charset('utf-8', 'cp949', $str); } $str = trim($str); return $str; }
/** * Ping the b2evonet RPC service. */ function ItemSendPing(&$params) { global $evonetsrv_host, $evonetsrv_port, $evonetsrv_uri; global $debug, $baseurl, $instance_name, $evo_charset; /** * @var Blog */ $item_Blog = $params['Item']->get_Blog(); $client = new xmlrpc_client($evonetsrv_uri, $evonetsrv_host, $evonetsrv_port); $client->debug = $debug == 2; $message = new xmlrpcmsg('b2evo.ping', array(new xmlrpcval($item_Blog->ID), new xmlrpcval($baseurl), new xmlrpcval($instance_name), new xmlrpcval(convert_charset($item_Blog->get('name'), 'utf-8', $evo_charset)), new xmlrpcval(convert_charset($item_Blog->get('url'), 'utf-8', $evo_charset)), new xmlrpcval($item_Blog->locale), new xmlrpcval(convert_charset($params['Item']->get('title'), 'utf-8', $evo_charset)))); $result = $client->send($message); $params['xmlrpcresp'] = $result; return true; }
/** * Sets a parameter with values from the request or to provided default, * except if param is already set! * * Also removes magic quotes if they are set automatically by PHP. * Also forces type. * Priority order: POST, GET, COOKIE, DEFAULT. * * @param string Variable to set * @param string Force value type to one of: * - integer * - float, double * - string (strips (HTML-)Tags, trims whitespace) * - array (TODO: array/integer , array/array/string ) * - html (does nothing) * - '' (does nothing) * - '/^...$/' check regexp pattern match (string) * - boolean (will force type to boolean, but you can't use 'true' as a default since it has special meaning. There is no real reason to pass booleans on a URL though. Passing 0 and 1 as integers seems to be best practice). * Value type will be forced only if resulting value (probably from default then) is !== NULL * @param mixed Default value or TRUE if user input required * @param boolean Do we need to memorize this to regenerate the URL for this page? * @param boolean Override if variable already set * @param boolean Force setting of variable to default if no param is sent and var wasn't set before * @param mixed true will refuse illegal values, * false will try to convert illegal to legal values, * 'allow_empty' will refuse illegal values but will always accept empty values (This helps blocking dirty spambots or borked index bots. Saves a lot of processor time by killing invalid requests) * @return mixed Final value of Variable, or false if we don't force setting and did not set */ function param($var, $type = '', $default = '', $memorize = false, $override = false, $use_default = true, $strict_typing = 'allow_empty') { global $Debuglog, $debug, $evo_charset, $io_charset; // NOTE: we use $GLOBALS[$var] instead of $$var, because otherwise it would conflict with param names which are used as function params ("var", "type", "default", ..)! /* * STEP 1 : Set the variable * * Check if already set * WARNING: when PHP register globals is ON, COOKIES get priority over GET and POST with this!!! * dh> I never understood that comment.. does it refer to "variables_order" php.ini setting? * fp> I guess */ if (!isset($GLOBALS[$var]) || $override) { if (isset($_POST[$var])) { $GLOBALS[$var] = remove_magic_quotes($_POST[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by POST', 'params' ); } elseif (isset($_GET[$var])) { $GLOBALS[$var] = remove_magic_quotes($_GET[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by GET', 'params' ); } elseif (isset($_COOKIE[$var])) { $GLOBALS[$var] = remove_magic_quotes($_COOKIE[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by COOKIE', 'params' ); } elseif ($default === true) { bad_request_die(sprintf(T_('Parameter «%s» is required!'), $var)); } elseif ($use_default) { // We haven't set any value yet and we really want one: use default: $GLOBALS[$var] = $default; // echo '<br>param(-): '.$var.'='.$GLOBALS[$var].' set by default'; // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by default', 'params' ); } else { // param not found! don't set the variable. // Won't be memorized nor type-forced! return false; } } else { // Variable was already set but we need to remove the auto quotes $GLOBALS[$var] = remove_magic_quotes($GLOBALS[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.' already set to ['.var_export($GLOBALS[$var], true).']!', 'params' ); } if (isset($io_charset) && !empty($evo_charset)) { $GLOBALS[$var] = convert_charset($GLOBALS[$var], $evo_charset, $io_charset); } /* * STEP 2: make sure the data fits the expected type * * type will be forced even if it was set before and not overriden */ if (!empty($type) && $GLOBALS[$var] !== NULL) { // Force the type // echo "forcing type!"; switch ($type) { case 'html': // do nothing if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> as RAW Unsecure HTML', 'params'); } break; case 'string': // strip out any html: // echo $var, '=', $GLOBALS[$var], '<br />'; if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string // TODO: dh> debug_die() instead? $GLOBALS[$var] = ''; $Debuglog->add('param(-): <strong>' . $var . '</strong> is not scalar!', 'params'); } else { $GLOBALS[$var] = trim(strip_tags($GLOBALS[$var])); // Make sure the string is a single line $GLOBALS[$var] = preg_replace('¤\\r|\\n¤', '', $GLOBALS[$var]); } $Debuglog->add('param(-): <strong>' . $var . '</strong> as string', 'params'); break; default: if (substr($type, 0, 1) == '/') { // We want to match against a REGEXP: if (preg_match($type, $GLOBALS[$var])) { // Okay, match if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> matched against ' . $type, 'params'); } } elseif ($strict_typing == 'allow_empty' && empty($GLOBALS[$var])) { // No match but we accept empty value: if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> is empty: ok', 'params'); } } elseif ($strict_typing) { // We cannot accept this MISMATCH: bad_request_die(sprintf(T_('Illegal value received for parameter «%s»!'), $var)); } else { // Fall back to default: $GLOBALS[$var] = $default; if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> DID NOT match ' . $type . ' set to default value=' . $GLOBALS[$var], 'params'); } } // From now on, consider this as a string: (we need this when memorizing) $type = 'string'; } elseif ($GLOBALS[$var] === '') { // Special handling of empty values. if ($strict_typing === false && $use_default) { // ADDED BY FP 2006-07-06 // We want to consider empty values as invalid and fall back to the default value: $GLOBALS[$var] = $default; } else { // We memorize the empty value as NULL: // fplanque> note: there might be side effects to this, but we need // this to distinguish between 0 and 'no input' // Note: we do this after regexps because we may or may not want to allow empty strings in regexps $GLOBALS[$var] = NULL; if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> set to NULL', 'params'); } } } elseif ($GLOBALS[$var] === array()) { if ($strict_typing === false && $use_default) { // ADDED BY FP 2006-09-07 // We want to consider empty values as invalid and fall back to the default value: $GLOBALS[$var] = $default; } } else { if ($strict_typing) { // We want to make sure the value is valid: $regexp = ''; switch ($type) { case 'boolean': $regexp = '/^(0|1|false|true)$/i'; break; case 'integer': $regexp = '/^(\\+|-)?[0-9]+$/'; break; case 'float': case 'double': $regexp = '/^(\\+|-)?[0-9]+(.[0-9]+)?$/'; break; // Note: other types are not tested here. } if ($strict_typing == 'allow_empty' && empty($GLOBALS[$var])) { // We have an empty value and we accept it // ok.. } elseif (!empty($regexp) && (!is_scalar($GLOBALS[$var]) || !preg_match($regexp, $GLOBALS[$var]))) { // Value does not match! bad_request_die(sprintf(T_('Illegal value received for parameter «%s»!'), $var)); } } // Change the variable type: settype($GLOBALS[$var], $type); if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> typed to ' . $type . ', new value=' . $GLOBALS[$var], 'params'); } } } } /* * STEP 3: memorize the value for later url regeneration */ if ($memorize) { // Memorize this parameter memorize_param($var, $type, $default); } // echo $var, '(', gettype($GLOBALS[$var]), ')=', $GLOBALS[$var], '<br />'; return $GLOBALS[$var]; }
/** * IDNA-Encode URL to Punycode. * @param string URL * @return string Encoded URL (ASCII) */ function idna_encode($url) { global $evo_charset; $url_utf8 = convert_charset($url, 'utf-8', $evo_charset); if (version_compare(PHP_VERSION, '5', '>=')) { load_class('_ext/idna/_idna_convert.class.php', 'idna_convert'); $IDNA = new idna_convert(); } else { load_class('_ext/idna/_idna_convert.class.php4', 'Net_IDNA_php4'); $IDNA = new Net_IDNA_php4(); } //echo '['.$url_utf8.'] '; $url = $IDNA->encode($url_utf8); /* if( $idna_error = $IDNA->get_last_error() ) { echo $idna_error; } */ // echo '['.$url.']<br>'; return $url; }
/** * @ignore */ function T_($string, $req_locale = '') { /** * The translations keyed by locale. They get loaded through include() of _global.php * @var array * @static */ static $trans = array(); global $current_locale, $locales, $Debuglog, $locales_path, $evo_charset; if (empty($req_locale)) { // By default we use the current locale if (empty($current_locale)) { // don't translate if we have no locale return $string; } $req_locale = $current_locale; } if (!isset($locales[$req_locale]['messages'])) { $Debuglog->add('No messages file path for locale. $locales["' . $req_locale . '"] is ' . var_export(@$locales[$req_locale], true), 'locale'); $locales[$req_locale]['messages'] = false; } $messages = $locales[$req_locale]['messages']; // replace special characters to msgid-equivalents $search = str_replace(array("\n", "\r", "\t"), array('\\n', '', '\\t'), $string); // echo "Translating ", $search, " to $messages<br />"; if (!isset($trans[$messages])) { // Translations for current locale have not yet been loaded: // echo 'LOADING', dirname(__FILE__).'/../locales/'. $messages. '/_global.php'; if (file_exists($locales_path . $messages . '/_global.php')) { include_once $locales_path . $messages . '/_global.php'; } if (!isset($trans[$messages])) { // Still not loaded... file doesn't exist, memorize that no translations are available // echo 'file not found!'; $trans[$messages] = array(); /* May be an english locale without translation. TODO: when refactoring locales, assign a key for 'original english'. $Debuglog->add( 'No messages found for locale ['.$req_locale.'], message file [/locales/'.$messages.'/_global.php]', 'locale' );*/ } } if (isset($trans[$messages][$search])) { // If the string has been translated: $r = $trans[$messages][$search]; $messages_charset = $locales[$req_locale]['charset']; } else { // echo "Not found!"; // Return the English string: $r = $string; $messages_charset = 'iso-8859-1'; // our .php file encoding } if (!empty($evo_charset)) { $r = convert_charset($r, $evo_charset, $messages_charset); } return $r; }
/** * A helper function to conditionally convert a string from current charset to UTF-8 * * @param mixed * @return mixed */ function current_charset_to_utf8($a) { global $current_charset; if (is_string($a) && $current_charset != '' && $current_charset != 'utf-8') { return convert_charset($a, 'utf-8', $current_charset); } return $a; }
/** * Get languages defined in the language directory */ function get_fs_languages($target_charset = null) { if (empty($target_charset)) { $target_charset = get_pwg_charset(); } $target_charset = strtolower($target_charset); $dir = opendir(PHPWG_ROOT_PATH . 'language'); while ($file = readdir($dir)) { if ($file != '.' and $file != '..') { $path = PHPWG_ROOT_PATH . 'language/' . $file; if (is_dir($path) and !is_link($path) and preg_match('/^[a-zA-Z0-9-_]+$/', $file) and file_exists($path . '/common.lang.php')) { $language = array('name' => $file, 'code' => $file, 'version' => '0', 'uri' => '', 'author' => ''); $plg_data = implode('', file($path . '/common.lang.php')); if (preg_match("|Language Name:\\s*(.+)|", $plg_data, $val)) { $language['name'] = trim($val[1]); $language['name'] = convert_charset($language['name'], 'utf-8', $target_charset); } if (preg_match("|Version:\\s*([\\w.-]+)|", $plg_data, $val)) { $language['version'] = trim($val[1]); } if (preg_match("|Language URI:\\s*(https?:\\/\\/.+)|", $plg_data, $val)) { $language['uri'] = trim($val[1]); } if (preg_match("|Author:\\s*(.+)|", $plg_data, $val)) { $language['author'] = trim($val[1]); } if (preg_match("|Author URI:\\s*(https?:\\/\\/.+)|", $plg_data, $val)) { $language['author uri'] = trim($val[1]); } if (!empty($language['uri']) and strpos($language['uri'], 'extension_view.php?eid=')) { list(, $extension) = explode('extension_view.php?eid=', $language['uri']); if (is_numeric($extension)) { $language['extension'] = $extension; } } // IMPORTANT SECURITY ! $language = array_map('htmlspecialchars', $language); $this->fs_languages[$file] = $language; } } } closedir($dir); @uasort($this->fs_languages, 'name_compare'); }
없으면 mb_convert_encoding 함수를 사용한다. 둘다 없으면 사용할 수 없다. */ function convert_charset($from_charset, $to_charset, $str) { if (function_exists('iconv')) { return iconv($from_charset, $to_charset, $str); } elseif (function_exists('mb_convert_encoding')) { return mb_convert_encoding($str, $to_charset, $from_charset); } else { die("Not found 'iconv' or 'mbstring' library in server."); } } } if (strtolower($g4[charset]) == 'euc-kr') { $reg_mb_nick = convert_charset('UTF-8', 'CP949', $reg_mb_nick); } // 별명은 한글, 영문, 숫자만 가능 if (!check_string($reg_mb_nick, _G4_HANGUL_ + _G4_ALPHABETIC_ + _G4_NUMERIC_)) { echo "110"; // 별명은 공백없이 한글, 영문, 숫자만 입력 가능합니다. } else { if (strlen($reg_mb_nick) < 4) { echo "120"; // 4글자 이상 입력 } else { $row = sql_fetch(" select count(*) as cnt from {$g4['member_table']} where mb_nick = '{$reg_mb_nick}' "); if ($row[cnt]) { echo "130"; // 이미 존재하는 별명 } else {
/** * Returns the number of the words in a string, sans HTML * * @todo dh> Test if http://de3.php.net/manual/en/function.str-word-count.php#85579 works better/faster * (only one preg_* call and no loop). * * @param string The string. * @return integer Number of words. * * @internal PHP's str_word_count() is not accurate. Inaccuracy reported * by sam2kb: http://forums.b2evolution.net/viewtopic.php?t=16596 */ function bpost_count_words($str) { global $evo_charset; $str = trim(strip_tags($str)); // Note: The \p escape sequence is available since PHP 4.4.0 and 5.1.0. if (@preg_match('|\\pL|u', 'foo') === false) { return str_word_count($str); } $count = 0; foreach (preg_split('#\\s+#', convert_charset($str, 'UTF-8', $evo_charset), -1, PREG_SPLIT_NO_EMPTY) as $word) { if (preg_match('#\\pL#u', $word)) { ++$count; } } return $count; }
/** * Sends a mail, wrapping PHP's mail() function. * * {@link $current_locale} will be used to set the charset. * * Note: we use a single \n as line ending, though it does not comply to * {@link http://www.faqs.org/rfcs/rfc2822 RFC2822}, but seems to be safer, * because some mail transfer agents replace \n by \r\n automatically. * * @todo Unit testing with "nice addresses" This gets broken over and over again. * * @param string Recipient, either email only or in "Name <*****@*****.**>" format (RFC2822). * Can be multiple comma-separated addresses. * @param string Subject of the mail * @param string The message text * @param string From address, being added to headers (we'll prevent injections); * see {@link http://securephp.damonkohler.com/index.php/Email_Injection}. * Might be just an email address or of the same form as {@link $to}. * {@link $notify_from} gets used as default (if NULL). * @param array Additional headers ( headername => value ). Take care of injection! * @return boolean True if mail could be sent (not necessarily delivered!), false if not - (return value of {@link mail()}) */ function send_mail($to, $subject, $message, $from = NULL, $headers = array()) { global $debug, $app_name, $app_version, $current_locale, $current_charset, $evo_charset, $locales, $Debuglog, $notify_from; $NL = "\n"; if (is_windows()) { // fplanque: Windows XP, Apache 1.3, PHP 4.4, MS SMTP : will not accept "nice" addresses. $to = preg_replace('/^.*?<(.+?)>$/', '$1', $to); $from = preg_replace('/^.*?<(.+?)>$/', '$1', $from); } // echo 'sending email to: ['.htmlspecialchars($to).'] from ['.htmlspecialchars($from).']'; if (!is_array($headers)) { // Make sure $headers is an array $headers = array($headers); } // Specify charset and content-type of email $headers['Content-Type'] = 'text/plain; charset=' . $current_charset; $headers['X-Mailer'] = $app_name . ' ' . $app_version . ' - PHP/' . phpversion(); $headers['X-Remote-Addr'] = implode(',', get_ip_list()); // -- Build headers ---- if (empty($from)) { $from = $notify_from; } else { $from = trim($from); } if (!empty($from)) { // From has to go into headers $from_save = preg_replace('~(\\r|\\n).*$~s', '', $from); // Prevent injection! (remove everything after (and including) \n or \r) if ($from != $from_save) { if (strpos($from_save, '<') !== false && !strpos($from_save, '>')) { // We have probably stripped the '>' at the end! $from_save .= '>'; } // Add X-b2evo notification mail header about this $headers['X-b2evo'] = 'Fixed email header injection (From)'; $Debuglog->add('Detected email injection! Fixed «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($from_save) . '».', 'security'); $from = $from_save; } $headers['From'] = $from; } $headerstring = ''; reset($headers); while (list($lKey, $lValue) = each($headers)) { // Add additional headers $headerstring .= $lKey . ': ' . $lValue . $NL; } if (function_exists('mb_encode_mimeheader')) { // encode subject $subject = mb_encode_mimeheader($subject, mb_internal_encoding(), 'B', $NL); } $message = str_replace(array("\r\n", "\r"), $NL, $message); // Convert encoding of message (from internal encoding to the one of the message): $message = convert_charset($message, $current_charset, $evo_charset); if ($debug > 1) { // We agree to die for debugging... if (!mail($to, $subject, $message, $headerstring)) { debug_die('Sending mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '» FAILED.'); } } else { // Soft debugging only.... if (!@mail($to, $subject, $message, $headerstring)) { $Debuglog->add('Sending mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '» FAILED.', 'error'); return false; } } $Debuglog->add('Sent mail from «' . htmlspecialchars($from) . '» to «' . htmlspecialchars($to) . '», Subject «' . htmlspecialchars($subject) . '».'); return true; }
/** * Check the validity of a given URL * * Checks allowed URI schemes and URL ban list. * URL can be empty. * * Note: We have a problem when trying to "antispam" a keyword which is already blacklisted * If that keyword appears in the URL... then the next page has a bad referer! :/ * * {@internal This function gets tested in misc.funcs.simpletest.php.}} * * @param string Url to validate * @param string * @param boolean also do an antispam check on the url * @return mixed false (which means OK) or error message */ function validate_url($url, $context = 'posting', $antispam_check = true) { global $Debuglog, $debug; if (empty($url)) { // Empty URL, no problem return false; } $verbose = $debug || $context != 'commenting'; $allowed_uri_schemes = get_allowed_uri_schemes($context); // Validate URL structure if ($url[0] == '$') { // This is a 'special replace code' URL (used in footers) if (!preg_match('¤\\$([a-z_]+)\\$¤', $url)) { return T_('Invalid URL $code$ format'); } } elseif (preg_match('~^\\w+:~', $url)) { // there's a scheme and therefor an absolute URL: if (substr($url, 0, 7) == 'mailto:') { // mailto:link if (!in_array('mailto', $allowed_uri_schemes)) { // Scheme not allowed $scheme = 'mailto:'; $Debuglog->add('URI scheme «' . $scheme . '» not allowed!', 'error'); return $verbose ? sprintf(T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme)) : T_('URI scheme not allowed.'); } preg_match('~^(mailto):(.*?)(\\?.*)?$~', $url, $match); if (!$match) { return $verbose ? sprintf(T_('Invalid email link: %s.'), htmlspecialchars($url)) : T_('Invalid email link.'); } elseif (!is_email($match[2])) { return $verbose ? sprintf(T_('Supplied email address (%s) is invalid.'), htmlspecialchars($match[2])) : T_('Invalid email address.'); } } elseif (substr($url, 0, 6) == 'clsid:') { // clsid:link if (!in_array('clsid', $allowed_uri_schemes)) { // Scheme not allowed $scheme = 'clsid:'; $Debuglog->add('URI scheme «' . $scheme . '» not allowed!', 'error'); return $verbose ? sprintf(T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme)) : T_('URI scheme not allowed.'); } if (!preg_match('¤^(clsid):([a-fA-F0-9\\-]+)$¤', $url, $match)) { return T_('Invalid class ID format'); } } elseif (substr($url, 0, 11) == 'javascript:') { // javascript: // Basically there could be anything here if (!in_array('javascript', $allowed_uri_schemes)) { // Scheme not allowed $scheme = 'javascript:'; $Debuglog->add('URI scheme «' . $scheme . '» not allowed!', 'error'); return $verbose ? sprintf(T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme)) : T_('URI scheme not allowed.'); } preg_match('¤^(javascript):¤', $url, $match); } else { // convert URL to IDN: load_funcs('_ext/idna/_idna_convert.class.php'); $IDNA = new Net_IDNA_php4(); global $evo_charset; $url = $IDNA->encode(convert_charset($url, 'utf-8', $evo_charset)); if (!preg_match('~^ # start ([a-z][a-z0-9+.\\-]*) # scheme :// # authorize absolute URLs only ( // not present in clsid: -- problem? ; mailto: handled above) (\\w+(:\\w+)?@)? # username or username and password (optional) ( localhost | [a-z0-9]([a-z0-9\\-])* # Don t allow anything too funky like entities \\. # require at least 1 dot [a-z0-9]([a-z0-9.\\-])+ # Don t allow anything too funky like entities ) (:[0-9]+)? # optional port specification [^ ]* # allow no space $~ix', $url, $match)) { // Cannot validate URL structure $Debuglog->add('URL «' . $url . '» does not match url pattern!', 'error'); return $verbose ? sprintf(T_('Invalid URL format (%s).'), htmlspecialchars($url)) : T_('Invalid URL format.'); } $scheme = strtolower($match[1]); if (!in_array($scheme, $allowed_uri_schemes)) { // Scheme not allowed $Debuglog->add('URI scheme «' . $scheme . '» not allowed!', 'error'); return $verbose ? sprintf(T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme)) : T_('URI scheme not allowed.'); } } } else { // URL is relative.. if ($context == 'commenting') { // We do not allow relative URLs in comments return $verbose ? sprintf(T_('URL "%s" must be absolute.'), htmlspecialchars($url)) : T_('URL must be absolute.'); } $char = substr($url, 0, 1); if ($char != '/' && $char != '#') { // must start with a slash or hash (for HTML anchors to the same page) return $verbose ? sprintf(T_('URL "%s" must be a full path starting with "/" or an anchor starting with "#".'), htmlspecialchars($url)) : T_('URL must be a full path starting with "/" or an anchor starting with "#".'); } } if ($antispam_check) { // Search for blocked keywords: if ($block = antispam_check($url)) { return $verbose ? sprintf(T_('URL "%s" not allowed: blacklisted word "%s".'), htmlspecialchars($url), $block) : T_('URL not allowed'); } } return false; // OK }
/** * Sets a parameter with values from the request or to provided default, * except if param is already set! * * Also removes magic quotes if they are set automatically by PHP. * Also forces type. * Priority order: POST, GET, COOKIE, DEFAULT. * * @todo when bad_request_die() gets called, the GLOBAL should not be left set to the invalid value! * fp> Why? if the process dies anyway * * @param string Variable to set * @param string Force value type to one of: * - integer * - float, double * - string (strips (HTML-)Tags, trims whitespace) * - text like string but allows multiple lines * - array (it may contains arbitrary array elements) NOTE: If there is one way to avoid and use some other array type then it should not be used * - array:integer (elements of array must be integer) * - array:string (strips (HTML-)Tags, trims whitespace of array's elements) * - array:/regexp/ (elements of array must match to the given regular expression) e.g. 'array:/^[a-z]*$/' * - array:array:integer (two dimensional array and the elements must be integers) * - array:array:string (strips (HTML-)Tags, trims whitespace of the two dimensional array's elements) * - html (does nothing, for now) * - raw (does nothing) * - '' (does nothing) -- DEPRECATED, use "raw" instead * - '/^...$/' check regexp pattern match (string) * - boolean (will force type to boolean, but you can't use 'true' as a default since it has special meaning. There is no real reason to pass booleans on a URL though. Passing 0 and 1 as integers seems to be best practice). * - url (like string but dies on illegal urls) * Value type will be forced only if resulting value (probably from default then) is !== NULL * @param mixed Default value or TRUE if user input required * @param boolean Do we need to memorize this to regenerate the URL for this page? * @param boolean Override if variable already set * @param boolean Force setting of variable to default if no param is sent and var wasn't set before * @param mixed true will refuse illegal values, * false will try to convert illegal to legal values, * 'allow_empty' will refuse illegal values but will always accept empty values (This helps blocking dirty spambots or borked index bots. Saves a lot of processor time by killing invalid requests) * @return mixed Final value of Variable, or false if we don't force setting and did not set */ function param($var, $type = 'raw', $default = '', $memorize = false, $override = false, $use_default = true, $strict_typing = 'allow_empty') { global $Debuglog, $debug, $evo_charset, $io_charset; // NOTE: we use $GLOBALS[$var] instead of $$var, because otherwise it would conflict with param names which are used as function params ("var", "type", "default", ..)! /* * STEP 1 : Set the variable * * Check if already set * WARNING: when PHP register globals is ON, COOKIES get priority over GET and POST with this!!! * dh> I never understood that comment.. does it refer to "variables_order" php.ini setting? * fp> I guess */ if (!isset($GLOBALS[$var]) || $override) { if (isset($_POST[$var])) { $GLOBALS[$var] = remove_magic_quotes($_POST[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by POST', 'params' ); } elseif (isset($_GET[$var])) { $GLOBALS[$var] = remove_magic_quotes($_GET[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by GET', 'params' ); } elseif (isset($_COOKIE[$var])) { $GLOBALS[$var] = remove_magic_quotes($_COOKIE[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by COOKIE', 'params' ); } elseif ($default === true) { bad_request_die(sprintf(T_('Parameter «%s» is required!'), $var)); } elseif ($use_default) { // We haven't set any value yet and we really want one: use default: if (in_array($type, array('array', 'array:integer', 'array:string', 'array:array:integer', 'array:array:string')) && $default === '') { // Change default '' into array() (otherwise there would be a notice with settype() below) $default = array(); } $GLOBALS[$var] = $default; // echo '<br>param(-): '.$var.'='.$GLOBALS[$var].' set by default'; // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by default', 'params' ); } else { // param not found! don't set the variable. // Won't be memorized nor type-forced! return false; } } else { // Variable was already set but we need to remove the auto quotes $GLOBALS[$var] = remove_magic_quotes($GLOBALS[$var]); // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.' already set to ['.var_export($GLOBALS[$var], true).']!', 'params' ); } if (isset($io_charset) && !empty($evo_charset)) { $GLOBALS[$var] = convert_charset($GLOBALS[$var], $evo_charset, $io_charset); } // Check if the type is the special array or regexp if (substr($type, 0, 7) == 'array:/') { // It is an array type param which may contains elements mathcing to the given regular expression $elements_regexp = substr($type, 6); $elements_type = 'string'; $type = 'array:regexp'; } /* * STEP 2: make sure the data fits the expected type * * type will be forced even if it was set before and not overriden */ if (!empty($type) && $GLOBALS[$var] !== NULL) { // Force the type // echo "forcing type!"; switch ($type) { case 'html': // Technically does the same as "raw", but may do more in the future. // Technically does the same as "raw", but may do more in the future. case 'raw': if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string debug_die('param(-): <strong>' . $var . '</strong> is not scalar!'); } // Clean utf8: $GLOBALS[$var] = utf8_clean($GLOBALS[$var]); // do nothing if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> as RAW Unsecure HTML', 'params'); } break; case 'htmlspecialchars': if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string debug_die('param(-): <strong>' . $var . '</strong> is not scalar!'); } // convert all html to special characters: $GLOBALS[$var] = utf8_trim(htmlspecialchars($GLOBALS[$var], ENT_COMPAT, $evo_charset)); // cross-platform newlines: $GLOBALS[$var] = preg_replace("~(\r\n|\r)~", "\n", $GLOBALS[$var]); $Debuglog->add('param(-): <strong>' . $var . '</strong> as text with html special chars', 'params'); break; case 'text': if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string debug_die('param(-): <strong>' . $var . '</strong> is not scalar!'); } // strip out any html: $GLOBALS[$var] = utf8_trim(utf8_strip_tags($GLOBALS[$var])); // cross-platform newlines: $GLOBALS[$var] = preg_replace("~(\r\n|\r)~", "\n", $GLOBALS[$var]); $Debuglog->add('param(-): <strong>' . $var . '</strong> as text', 'params'); break; case 'string': if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string debug_die('param(-): <strong>' . $var . '</strong> is not scalar!'); } // echo $var, '=', $GLOBALS[$var], '<br />'; // Make sure the string is a single line $GLOBALS[$var] = preg_replace('~\\r|\\n~', '', $GLOBALS[$var]); // strip out any html: $GLOBALS[$var] = utf8_strip_tags($GLOBALS[$var]); // echo "param $var=".$GLOBALS[$var]."<br />\n"; $GLOBALS[$var] = utf8_trim($GLOBALS[$var]); // echo "param $var=".$GLOBALS[$var]."<br />\n"; $Debuglog->add('param(-): <strong>' . $var . '</strong> as string', 'params'); break; case 'url': if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string debug_die('param(-): <strong>' . $var . '</strong> is not scalar!'); } // Decode url: $GLOBALS[$var] = urldecode($GLOBALS[$var]); // strip out any html: $GLOBALS[$var] = utf8_trim(utf8_strip_tags($GLOBALS[$var])); // Remove new line chars and double quote from url $GLOBALS[$var] = preg_replace('~\\r|\\n|"~', '', $GLOBALS[$var]); if (!empty($GLOBALS[$var]) && !preg_match('#^(/|\\?|https?://)#i', $GLOBALS[$var])) { // We cannot accept this MISMATCH: bad_request_die(sprintf(T_('Illegal value received for parameter «%s»!'), $var)); } $Debuglog->add('param(-): <strong>' . $var . '</strong> as url', 'params'); break; case 'array:integer': case 'array:array:integer': // Set elements type to integer, and set the corresponding regular expression $elements_type = 'integer'; $elements_regexp = '/^(\\+|-)?[0-9]+$/'; case 'array': case 'array:string': case 'array:regexp': case 'array:array:string': if (!is_array($GLOBALS[$var])) { // This param must be array debug_die('param(-): <strong>' . $var . '</strong> is not array!'); } // Store current array in temp var for checking and preparing $globals_var = $GLOBALS[$var]; // Check if the given array type is one dimensional array $one_dimensional = $type == 'array' || $type == 'array:integer' || $type == 'array:string' || $type == 'array:regexp'; // Check if the given array type should contains string elements $contains_strings = $type == 'array:string' || $type == 'array:array:string'; if ($one_dimensional) { // Convert to a two dimensional array to handle one and two dimensional arrays the same way $globals_var = array($globals_var); } foreach ($globals_var as $i => $var_array) { if (!is_array($var_array)) { // This param must be array // Note: In case of one dimensional array params this will never happen debug_die('param(-): <strong>' . $var . '[' . $i . ']</strong> is not array!'); } if ($type == 'array') { // This param may contain any kind of elements we need to check and validate it recursively $globals_var[$i] = param_check_general_array($var_array); break; } foreach ($var_array as $j => $var_value) { if (!is_scalar($var_value)) { // This happens if someone uses "foo[][]=x" where "foo[]" is expected as string debug_die('param(-): element of array <strong>' . $var . '</strong> is not scalar!'); } if ($contains_strings) { // Prepare string elements of array // Make sure the string is a single line $var_value = preg_replace('~\\r|\\n~', '', $var_value); // strip out any html: $globals_var[$i][$j] = utf8_trim(utf8_strip_tags($var_value)); continue; } if (isset($elements_regexp)) { // Array contains elements which must match to the given regular expression if (preg_match($elements_regexp, $var_value)) { // OK match, set the corresponding type settype($globals_var[$i][$j], $elements_type); } else { // No match, cannot accept this MISMATCH // Note: In case of array:integer or array:regexp we always use strict typing for the array elements bad_request_die(sprintf(T_('Illegal value received for parameter «%s»!'), $var)); } } } } if ($one_dimensional) { // Extract real array from temp array $globals_var = $globals_var[0]; } // Restore current array with prepared data $GLOBALS[$var] = $globals_var; $Debuglog->add('param(-): <strong>' . $var . '</strong> as ' . $type, 'params'); if ($GLOBALS[$var] === array() && $strict_typing === false && $use_default) { // We want to consider empty values as invalid and fall back to the default value: $GLOBALS[$var] = $default; } break; default: if (utf8_substr($type, 0, 1) == '/') { // We want to match against a REGEXP: if (!is_scalar($GLOBALS[$var])) { // This happens if someone uses "foo[]=x" where "foo" is expected as string debug_die('param(-): <strong>' . $var . '</strong> is not scalar!'); } elseif (preg_match($type, $GLOBALS[$var])) { // Okay, match if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> matched against ' . $type, 'params'); } } elseif ($strict_typing == 'allow_empty' && empty($GLOBALS[$var])) { // No match but we accept empty value: if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> is empty: ok', 'params'); } } elseif ($strict_typing) { // We cannot accept this MISMATCH: bad_request_die(sprintf(T_('Illegal value received for parameter «%s»!'), $var)); } else { // Fall back to default: $GLOBALS[$var] = $default; if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> DID NOT match ' . $type . ' set to default value=' . $GLOBALS[$var], 'params'); } } // From now on, consider this as a string: (we need this when memorizing) $type = 'string'; } elseif ($GLOBALS[$var] === '') { // Special handling of empty values. if ($strict_typing === false && $use_default) { // ADDED BY FP 2006-07-06 // We want to consider empty values as invalid and fall back to the default value: $GLOBALS[$var] = $default; } else { // We memorize the empty value as NULL: // fplanque> note: there might be side effects to this, but we need // this to distinguish between 0 and 'no input' // Note: we do this after regexps because we may or may not want to allow empty strings in regexps $GLOBALS[$var] = NULL; if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . $var . '</strong> set to NULL', 'params'); } } } else { if ($strict_typing) { // We want to make sure the value is valid: $regexp = ''; switch ($type) { case 'boolean': $regexp = '/^(0|1|false|true)$/i'; break; case 'integer': $regexp = '/^(\\+|-)?[0-9]+$/'; break; case 'float': case 'double': $regexp = '/^(\\+|-)?[0-9]+(.[0-9]+)?$/'; break; default: // Note: other types are not tested and they are not allowed without testing. debug_die('Invalid parameter type!'); } if ($strict_typing == 'allow_empty' && empty($GLOBALS[$var])) { // We have an empty value and we accept it // ok.. } elseif (!empty($regexp)) { if ($type == 'boolean' && strtolower($GLOBALS[$var]) == 'false') { // 'false' string must be interpreted as boolean false value $GLOBALS[$var] = false; } elseif (!is_scalar($GLOBALS[$var]) || !preg_match($regexp, $GLOBALS[$var])) { // Value of scalar var does not match! bad_request_die(sprintf(T_('Illegal value received for parameter «%s»!'), $var)); } } } // Change the variable type: settype($GLOBALS[$var], $type); if (isset($Debuglog)) { $Debuglog->add('param(-): <strong>' . var_export($var, true) . '</strong> typed to ' . $type . ', new value=' . var_export($GLOBALS[$var], true), 'params'); } } } } /* * STEP 3: memorize the value for later url regeneration */ if ($memorize) { // Memorize this parameter memorize_param($var, $type, $default); } // echo $var, '(', gettype($GLOBALS[$var]), ')=', $GLOBALS[$var], '<br />'; return $GLOBALS[$var]; }
/** * A helper function to conditionally convert a string from current charset to UTF-8 * * @param string * @return string */ function current_charset_to_utf8(&$a) { global $current_charset; if (is_string($a) && $current_charset != '' && $current_charset != 'utf-8') { // Convert string to utf-8 if it has another charset $a = convert_charset($a, 'utf-8', $current_charset); } return $a; }
for ($i=0; $i<count($filter); $i++) { $str = $filter[$i]; // 제목 필터링 (찾으면 중지) $subj = ""; $pos = strpos($subject, $str); if ($pos !== false) { if (strtolower($g4[charset]) == 'euc-kr') $subj = convert_charset('utf-8', 'cp949', $str);//cp949 로 변환해서 반환 else $subj = $str; break; } // 내용 필터링 (찾으면 중지) $cont = ""; $pos = strpos($content, $str); if ($pos !== false) { if (strtolower($g4[charset]) == 'euc-kr') $cont = convert_charset('utf-8', 'cp949', $str);//cp949 로 변환해서 반환 else $cont = $str; break; } } die("{\"subject\":\"$subj\",\"content\":\"$cont\"}"); ?>
/** * Convert special chars (like german umlauts) to ASCII characters. * * @param string Input string to operate on * @param NULL|string The post locale or NULL if there is no specific locale. * Gets passed to evo_iconv_transliterate(). * @return string The input string with replaced chars. */ function replace_special_chars($str, $post_locale = NULL) { global $evo_charset, $default_locale, $current_locale, $locales; // Decode entities to be able to transliterate the associated chars: // Tblue> TODO: Check if this could have side effects. $str = html_entity_decode($str, ENT_NOQUOTES, $evo_charset); $our_locale = $post_locale; if ($our_locale === NULL) { // post locale is not set, try to guess current locale if (!empty($default_locale)) { $our_locale = $default_locale; } if (!empty($current_locale)) { // Override with current locale if available $our_locale = $current_locale; } } if ($our_locale !== NULL && isset($locales[$our_locale]) && !empty($locales[$our_locale]['transliteration_map'])) { // Use locale 'transliteration_map' if present if (!array_key_exists('', $locales[$our_locale]['transliteration_map'])) { // Make sure there's no empty string key, otherwise strtr() returns false if ($tmp_str = strtr($str, $locales[$our_locale]['transliteration_map'])) { } // Use newly transliterated string $str = $tmp_str; } } if (($newstr = evo_iconv_transliterate($str, $post_locale)) !== false) { // iconv allows us to get nice URL titles by transliterating non-ASCII chars. // Tblue> htmlentities() does not know anything about ASCII?! ISO-8859-1 will work too, though. $newstr_charset = 'ISO-8859-1'; } else { if (can_convert_charsets('UTF-8', $evo_charset) && can_convert_charsets('UTF-8', 'ISO-8859-1')) { // Fallback to the limited old method: Transliterate only a few known chars. $newstr = convert_charset($str, 'UTF-8', $evo_charset); $newstr_charset = 'UTF-8'; $search = array('Ä', 'ä', 'Ö', 'ö', 'Ü', 'ü', 'ß', 'à', 'ç', 'è', 'é', 'ì', 'ò', 'ô', 'ù'); // iso-8859-1 $replace = array('Ae', 'ae', 'Oe', 'oe', 'Ue', 'ue', 'ss', 'a', 'c', 'e', 'e', 'i', 'o', 'o', 'u'); foreach ($search as $k => $v) { // convert $search to UTF-8 $search[$k] = convert_charset($v, 'UTF-8', 'ISO-8859-1'); } $newstr = str_replace($search, $replace, $newstr); } else { // Replace HTML entities only. $newstr = $str; $newstr_charset = $evo_charset; } } // Replace HTML entities $newstr = htmlentities($newstr, ENT_NOQUOTES, $newstr_charset); // Handle special entities (e.g., use "-" instead of "a" for "&"): $newstr = str_replace(array('&', '«', '»'), '-', $newstr); // Keep only one char in entities! $newstr = preg_replace('/&(.).+?;/', '$1', $newstr); // Replace non acceptable chars $newstr = preg_replace('/[^A-Za-z0-9_]+/', '-', $newstr); // Remove '-' at start and end: $newstr = preg_replace('/^-+/', '', $newstr); $newstr = preg_replace('/-+$/', '', $newstr); //pre_dump( $str, $newstr ); return $newstr; }
/** * Event handler: called when a user attemps to login. * * This function will check if the user exists in the LDAP directory and create it locally if it does not. * * @param array 'login', 'pass' and 'pass_md5' */ function LoginAttempt(&$params) { global $localtimenow; global $Settings, $Hit, $evo_charset; // Check if LDAP is available: if (!function_exists('ldap_connect')) { $this->debug_log('This PHP installation does not support LDAP functions.'); return false; // Login failed! } // Get ready to go through ALL LDAP Servers configured in the plugin: $search_sets = $this->Settings->get('search_sets'); if (empty($search_sets)) { $this->debug_log('No LDAP servers have been configured in the LDAP plugin settings.'); return false; // Login failed! } // Detect if we already have a local user with the same login: $UserCache =& get_Cache('UserCache'); if ($local_User =& $UserCache->get_by_login($params['login'])) { $this->debug_log('User <b>' . $params['login'] . '</b> already exists locally. We will UPDATE it with the latest LDAP attibutes.'); $update_mode = true; // Try to find a number of a search set which was used on successful logging previous time by current user: $user_search_set_num = intval($this->UserSettings->get('search_set_num', $local_User->ID)); if ($user_search_set_num > 0 && isset($search_sets[$user_search_set_num])) { // We have found this, Reorder the array to use the successful set firstly: $success_search_set = $search_sets[$user_search_set_num]; unset($search_sets[$user_search_set_num]); $search_sets = array($user_search_set_num => $success_search_set) + $search_sets; } } else { $update_mode = false; } $this->debug_log(sprintf('LDAP plugin will attempt to login with login=<b>%s</b> / pass=<b>%s</b> / MD5 pass=<b>%s</b>', $params['login'], $params['pass'], $params['pass_md5'])); // ------ Loop through list of configured LDAP Servers: ------ foreach ($search_sets as $l_id => $l_set) { $this->debug_log('Step 1 : STARTING LDAP AUTH WITH SERVER #' . $l_id); // --- CONNECT TO SERVER --- $server_port = explode(':', $l_set['server']); $server = $server_port[0]; $port = isset($server_port[1]) ? $server_port[1] : 389; if (!empty($l_set['disabled'])) { $this->debug_log('Skipping disabled LDAP server «' . $server . ':' . $port . '»!'); continue; } if (!($ldap_conn = @ldap_connect($server, $port))) { $this->debug_log('Could not connect to LDAP server «' . $server . ':' . $port . '»!'); continue; } $this->debug_log('Connected to server «' . $server . ':' . $port . '»..'); $ldap_rdn = str_replace('%s', $params['login'], $l_set['rdn']); $this->debug_log('Using RDN «' . $ldap_rdn . '» for binding...'); // --- SET PROTOCOL VERSION --- // Get protocol version to use: if (!ldap_get_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version)) { $this->debug_log('Failed to get LDAP_OPT_PROTOCOL_VERSION.'); $initial_protocol_version = null; } $protocol_version = isset($l_set['protocol_version']) ? $l_set['protocol_version'] : 'auto'; // new setting in 2.01 if ($protocol_version[0] == 'v') { // transform "vX" => "X" $try_versions = array(substr($protocol_version, 1)); } else { // "auto" $try_versions = array(3, 2); if (isset($initial_protocol_version)) { array_unshift($try_versions, $initial_protocol_version); } $try_versions = array_unique($try_versions); } $this->debug_log('We will try protocol versions: ' . implode(', ', $try_versions)); // --- VERIFY USER CREDENTIALS BY BINDING TO SERVER --- // you might use this for testing with Apache DS: if( !@ldap_bind($ldap_conn, 'uid=admin,ou=system', 'secret') ) // Bind: $bound = false; $bind_errors = array(); foreach ($try_versions as $try_version) { $this->debug_log(sprintf('Trying to connect with protocol version: %s / RDN: %s / pass: %s', $try_version, $ldap_rdn, $params['pass'])); ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $try_version); if (@ldap_bind($ldap_conn, $ldap_rdn, $params['pass'])) { // Success $this->debug_log('Binding worked.'); $bound = true; break; } else { $this->debug_log('Binding failed. Errno: ' . ldap_errno($ldap_conn) . ' Error: ' . ldap_error($ldap_conn)); } } if (!$bound) { if (isset($initial_protocol_version)) { // Reset this for the next search set: ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version); } continue; } $this->debug_log('User successfully bound to server.'); // --- STEP 2 : TRY TO OBTAIN MORE INFO ABOUT USER --- // Search user info $filter = str_replace('%s', $params['login'], $l_set['search_filter']); $this->debug_log(sprintf('Step 2 : Now querying for additional user info. base_dn: <b>%s</b>, filter: <b>%s</b>', $l_set['base_dn'], $filter)); $search_result = @ldap_search($ldap_conn, $l_set['base_dn'], $filter); if (!$search_result) { // this may happen with an empty base_dn $this->debug_log('Invalid ldap_search result. Skipping to next search set. Errno: ' . ldap_errno($ldap_conn) . ' Error: ' . ldap_error($ldap_conn)); continue; } $search_info = ldap_get_entries($ldap_conn, $search_result); //$this->debug_log( 'Results returned by LDAP Server: <pre>'.var_export( $search_info, true ).'</pre>' ); if ($search_info['count'] != 1) { // We have found 0 or more than 1 users, which is a problem... $this->debug_log('# of entries found with search: ' . $search_info['count'] . ' - Skipping...'); /* for ($i=0; $i<$search_info["count"]; $i++) { echo "dn: ". $search_info[$i]["dn"] ."<br>"; echo "first cn entry: ". $search_info[$i]["cn"][0] ."<br>"; echo "first email entry: ". $search_info[$i]["mail"][0] ."<p>"; } */ continue; } $this->debug_log('User info has been found.'); // --- CREATE OR UPDATE USER ACCOUNT IN B2EVO --- if ($update_mode == false) { $this->debug_log('Step 3 : Creating a local user in b2evolution...'); $local_User = new User(); $local_User->set('login', $params['login']); $local_User->set('locale', locale_from_httpaccept()); // use the browser's locale $local_User->set_datecreated($localtimenow); // $local_User->set( 'level', 1 ); } else { // User exists already exists $this->debug_log('Step 3 : Updating the existing local user.'); } $this->debug_log('Randomize password in b2evolution DB and autoactivate user.'); // Generate a random password (we never want LDAP users to be able to login without a prior LDAP check) (also on update, just in case... $local_User->set_password(generate_random_passwd(32)); // $params['pass'] ); $local_User->set('status', 'autoactivated'); // Activate the user automatically (no email activation necessary) // Convert each input string to current server encoding: $exclude_encoding_fields = array('uid', 'mail', 'jpegphoto'); if (isset($search_info[0]) && is_array($search_info[0])) { foreach ($search_info[0] as $search_info_key => $search_info_data) { if (isset($search_info_data[0]) && is_string($search_info_data[0]) && !in_array($search_info_key, $exclude_encoding_fields)) { // Convert string from LDAP server encoding to current server encoding: $search_info[0][$search_info_key][0] = convert_charset($search_info_data[0], $l_set['encoding'], $evo_charset); } } } // Make some updates: // mail -> email: if (isset($search_info[0]['mail'][0])) { $local_User->set_email($search_info[0]['mail'][0]); } // uid -> nickname if (isset($search_info[0]['uid'][0])) { $this->debug_log('UID: <b>' . $search_info[0]['uid'][0] . '</b>'); $local_User->set('nickname', $search_info[0]['uid'][0]); } else { // if not found, use login. $local_User->set('nickname', $params['login']); } // givenname -> Firstname: if (isset($search_info[0]['givenname'][0])) { $this->debug_log('First name (givenname): <b>' . $search_info[0]['givenname'][0] . '</b>'); $local_User->set('firstname', $search_info[0]['givenname'][0]); } // sn -> Lastname: if (isset($search_info[0]['sn'][0])) { $this->debug_log('Last name (sn): <b>' . $search_info[0]['sn'][0] . '</b>'); $local_User->set('lastname', $search_info[0]['sn'][0]); } // roomnumber -> user field "roomnumber" (if not found, autocreate it in group "Address") if (isset($search_info[0]['roomnumber'][0])) { $this->debug_log('Room number: <b>' . $search_info[0]['roomnumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'roomnumber', $search_info[0]['roomnumber'][0], 'Address', 'Room Number', 'word'); } // businesscategory -> user field "businesscategory" (if not found, autocreate it in group "About me") if (isset($search_info[0]['businesscategory'][0])) { $this->debug_log('Business Category: <b>' . $search_info[0]['businesscategory'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'businesscategory', $search_info[0]['businesscategory'][0], 'About me', 'Business Category', 'text'); } // telephonenumber -> user field "officephone" (if not found, autocreate it in group "Phone") if (isset($search_info[0]['telephonenumber'][0])) { $this->debug_log('Office phone: <b>' . $search_info[0]['telephonenumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'officephone', $search_info[0]['telephonenumber'][0], 'Phone', 'Office phone', 'phone'); } // mobile -> user field "cellphone" (if not found, autocreate it in group "Phone") if (isset($search_info[0]['mobile'][0])) { $this->debug_log('Cell phone: <b>' . $search_info[0]['mobile'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'cellphone', $search_info[0]['mobile'][0], 'Phone', 'Cell phone', 'phone'); } // employeenumber -> user field "employeenumber" (if not found, autocreate it in group "About me") if (isset($search_info[0]['employeenumber'][0])) { $this->debug_log('Employee number: <b>' . $search_info[0]['employeenumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'employeenumber', $search_info[0]['employeenumber'][0], 'About me', 'Employee number', 'word'); } // title -> user field "title" (if not found, autocreate it in group "About me") if (isset($search_info[0]['title'][0])) { $this->debug_log('Title: <b>' . $search_info[0]['title'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'title', $search_info[0]['title'][0], 'About me', 'Title', 'word'); $userfield_title = $search_info[0]['title'][0]; // Use this as role for all organizations below } else { $userfield_title = ''; } // departmentnumber -> join Organization with the same name (create if doesn't exist) if (isset($search_info[0]['departmentnumber'][0])) { $this->debug_log('Department Number: <b>' . $search_info[0]['departmentnumber'][0] . '</b>'); $this->userorg_update_by_name($local_User, $search_info[0]['departmentnumber'][0], $userfield_title); } // o -> join Organization with the same name (create if doesn't exist) if (isset($search_info[0]['o'][0])) { $this->debug_log('Organization: <b>' . $search_info[0]['o'][0] . '</b>'); $this->userorg_update_by_name($local_User, $search_info[0]['o'][0], $userfield_title); } // telexnumber -> user field "officefax" (if not found, autocreate it in group "Phone") if (isset($search_info[0]['telexnumber'][0])) { $this->debug_log('Office FAX: <b>' . $search_info[0]['telexnumber'][0] . '</b>'); $this->userfield_update_by_code($local_User, 'officefax', $search_info[0]['telexnumber'][0], 'Phone', 'Office FAX', 'phone'); } // ---- GROUP STUFF ---- if ($update_mode == true) { // Updating existing user $this->debug_log('Updating existing user: we do NOT touch the primary group.'); $local_User->dbupdate(); $this->debug_log('OK -- User has been updated.'); } else { // Try to assign prilary group from the search results: $assigned_group = false; if (!empty($l_set['assign_user_to_group_by'])) { $this->debug_log('Plugin is configured to assign the Primary Group by the ' . $l_set['assign_user_to_group_by'] . ' key...'); if (isset($search_info[0][$l_set['assign_user_to_group_by']]) && isset($search_info[0][$l_set['assign_user_to_group_by']][0])) { // There is info we want to assign by $assign_by_value = $search_info[0][$l_set['assign_user_to_group_by']][0]; $this->debug_log('User info says has ' . $l_set['assign_user_to_group_by'] . ' = "<b>' . $assign_by_value . '</b>"'); $GroupCache =& get_Cache('GroupCache'); if ($users_Group =& $GroupCache->get_by_name($assign_by_value, false)) { // A group with the users value returned exists. $local_User->set_Group($users_Group); $assigned_group = true; $this->debug_log('Assigning User to existing Primary Group.'); } else { $this->debug_log('Group with that name does not exist...'); if ($new_Group =& $this->usergroup_create($l_set['tpl_new_grp_ID'], $assign_by_value)) { // Link the user to new created group: $local_User->set_Group($new_Group); $assigned_group = true; $this->debug_log('Assigned User to new Primary Group.'); } } } } if (!$assigned_group) { // Default group: $this->debug_log('Falling back to default primary group...'); $users_Group = NULL; $fallback_grp_ID = $this->Settings->get('fallback_grp_ID'); if (empty($fallback_grp_ID)) { $this->debug_log('No default/fallback primary group configured.'); $this->debug_log('User NOT created, try next LDAP server...'); //Continue to next LDAP server: continue; } else { $GroupCache =& get_Cache('GroupCache'); $users_Group =& $GroupCache->get_by_ID($fallback_grp_ID); if ($users_Group) { // either $this->default_group_name is not given or wrong $local_User->set_Group($users_Group); $assigned_group = true; $this->debug_log('Using default/fallback primary group: <b>' . $users_Group->get('name') . '</b>'); } else { $this->debug_log('Default/fallback primary group does not exist (' . $fallback_grp_ID . ').'); $this->debug_log('User NOT created, try next LDAP server...'); //Continue to next LDAP server: continue; } } } $local_User->dbinsert(); $UserCache->add($local_User); $this->debug_log('OK -- User has been created.'); } // Remember this settings number in order use this first in next logging time by current user: $this->UserSettings->set('search_set_num', $l_id, $local_User->ID); $this->UserSettings->dbupdate(); // Assign user to organizations: $this->userorg_assign_to_user($local_User); // jpegphoto -> Save as profile pictue "ldap.jpeg" and associate with user if (isset($search_info[0]['jpegphoto'][0])) { $this->debug_log('Photo: <img src="data:image/jpeg;base64,' . base64_encode($search_info[0]['jpegphoto'][0]) . '" />'); // Save to disk and attach to user: $this->userimg_attach_photo($local_User, $search_info[0]['jpegphoto'][0], !empty($l_set['expand_pics'])); } // --- EXTRA GROUPS --- if (!empty($l_set['secondary_grp_search_filter'])) { global $app_version; if (evo_version_compare($app_version, '6.7.0-alpha') < 0) { // The plugin is used on b2evo 6.6 $this->debug_log('Secondary groups not handled. This feature requires b2evolution v6.7.0-alpha or newer.'); } elseif (empty($l_set['secondary_grp_name_attribute'])) { $this->debug_log('Missing name attribute for secondary groups'); } else { $filter = str_replace('%s', $params['login'], $l_set['secondary_grp_search_filter']); $grp_name_attribute = $l_set['secondary_grp_name_attribute']; $this->debug_log(sprintf('Step 4 : Now querying for secondary groups. base_dn: <b>%s</b>, filter: <b>%s</b>, name attribue=<b>%s</b>', $l_set['secondary_grp_base_dn'], $filter, $grp_name_attribute)); $search_result = @ldap_search($ldap_conn, $l_set['secondary_grp_base_dn'], $filter, array($grp_name_attribute)); if (!$search_result) { // this may happen with an empty base_dn $this->debug_log('Invalid ldap_search result. No secondary groups will be assigned. Errno: ' . ldap_errno($ldap_conn) . ' Error: ' . ldap_error($ldap_conn)); } else { $search_info = ldap_get_entries($ldap_conn, $search_result); // $this->debug_log( 'Results returned by LDAP Server: <pre>'.var_export( $search_info, true ).'</pre>' ); $secondary_groups = array(); // $this->debug_log( 'Secondary groups name prefix: <pre>'.var_export( $l_set['secondary_grp_name_prefix'], true ).'</pre>' ); // Walk through results: foreach ($search_info as $group_candidate) { if (is_array($group_candidate) && isset($group_candidate[$grp_name_attribute][0])) { $group_candidate_cn = $group_candidate[$grp_name_attribute][0]; if (empty($l_set['secondary_grp_name_prefix']) || strpos($group_candidate_cn, $l_set['secondary_grp_name_prefix']) === 0) { // prefix is ok $this->debug_log('Accepted Secondary Group: ' . $group_candidate_cn); $secondary_groups[] = $group_candidate_cn; } else { // prefix is NOT ok $this->debug_log('REJECTED Secondary Group: ' . $group_candidate_cn); } } } // Hardcode two secondary groups: // $secondary_groups = array( 'Blog B members', 'Blog D Members' ); $this->debug_log('Secondary groups to be assigned: <pre>' . var_export($secondary_groups, true) . '</pre>'); // Update secondary groups for the User: $this->usersecgroup_update($local_User, $secondary_groups, $l_set['tpl_new_secondary_grp_ID']); } } } if (isset($initial_protocol_version)) { ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version); } // --- CONSIDER THE LOGIN ATTEMPT TO BE SUCCESSFUL AND WE ACCEPT IT --- // Update this value which has been passed by REFERENCE: $params['pass_ok'] = true; return true; // Login was a success (but return "true" does not trigger anything special in b2evolution) } if (isset($initial_protocol_version)) { ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, $initial_protocol_version); } return false; // Login failed! }
$config['cf_filter'] = convert_charset('cp949', 'utf-8', $config['cf_filter']); } //$filter = explode(",", strtolower(trim($config['cf_filter']))); // strtolower 에 의한 한글 변형으로 아래 코드로 대체 (곱슬최씨님이 알려 주셨습니다.) $filter = explode(",", trim($config['cf_filter'])); for ($i = 0; $i < count($filter); $i++) { $str = $filter[$i]; // 제목 필터링 (찾으면 중지) $subj = ""; $pos = strpos($subject, $str); if ($pos !== false) { if (strtolower($g4[charset]) == 'euc-kr') { $subj = convert_charset('utf-8', 'cp949', $str); } else { $subj = $str; } break; } // 내용 필터링 (찾으면 중지) $cont = ""; $pos = strpos($content, $str); if ($pos !== false) { if (strtolower($g4[charset]) == 'euc-kr') { $cont = convert_charset('utf-8', 'cp949', $str); } else { $cont = $str; } break; } } die("{\"subject\":\"{$subj}\",\"content\":\"{$cont}\"}");
/** * Translate a given string, in the Plugin's context. * * This means, that the translation is obtained from the Plugin's "locales" folder. * @link http://manual.b2evolution.net/Localization#Plugins * * It uses the global/regular {@link T_()} function as fallback. * * {@internal This is mainly a copy of {@link T_()}, for the $use_l10n==2 case.}} * * @param string The string (english), that should be translated * @param string Requested locale ({@link $current_locale} gets used by default) * @return string */ function T_($string, $req_locale = '') { global $current_locale, $locales, $Debuglog, $plugins_path, $evo_charset; $trans =& $this->_trans; if (empty($req_locale)) { // By default we use the current locale if (empty($current_locale)) { // don't translate if we have no locale return $string; } $req_locale = $current_locale; } if (!isset($locales[$req_locale]['messages'])) { $this->debug_log('No messages file dirname for locale. $locales["' . $req_locale . '"] is ' . var_export(@$locales[$req_locale], true), 'locale'); $locales[$req_locale]['messages'] = false; } $messages = $locales[$req_locale]['messages']; // replace special characters to msgid-equivalents $search = str_replace(array("\n", "\r", "\t"), array('\\n', '', '\\t'), $string); // echo "Translating ", $search, " to $messages<br />"; if (!isset($trans[$messages])) { // Translations for current locale have not yet been loaded: if (!isset($this->classfile_path)) { // ->T_() called through the plugin's constructor, which is deprecated! $this->debug_log('T_() method called through plugin constructor!'); return $string; } $locales_dir = dirname($this->classfile_path) . '/'; if ($locales_dir == $plugins_path) { $locales_dir .= $this->classname . '/'; } $locales_dir .= 'locales/'; // First load the global messages file, if existing: if (!$this->_trans_loaded_global) { $this->_trans_loaded_global = true; $file_path = $locales_dir . '_global.php'; if (file_exists($file_path)) { if (is_readable($file_path)) { // echo 'LOADING GLOBAL '.$file_path; include $file_path; } else { $this->debug_log('Global messages file ' . $file_path . ' is not readable!', 'locale'); } } } // Then load locale specific files: $file_path = $locales_dir . $messages . '/_global.php'; if (file_exists($file_path)) { if (is_readable($file_path)) { // echo 'LOADING '.$file_path; include $file_path; } else { $this->debug_log('Messages file ' . $file_path . ' for locale ' . $req_locale . ' is not readable!', 'locale'); } } if (!isset($trans[$messages])) { // Still not loaded... file doesn't exist, memorize that no translations are available // echo 'file not found!'; $trans[$messages] = array(); /* May be an english locale without translation. TODO: when refactoring locales, assign a key for 'original english'. $Debuglog->add( 'No messages found for locale ['.$req_locale.'], message file [/locales/'.$messages.'/_global.php]', 'locale' );*/ } // Remember the charset: if (isset($trans[$messages][''])) { if (($pos = strpos($trans[$messages][''], 'Content-Type:')) !== false) { if (preg_match('~^Content-Type:.*charset=([\\w\\d-]+)~', substr($trans[$messages][''], $pos), $match)) { $this->_trans_charsets[$messages] = $match[1]; $this->debug_log('Charset of messages for ' . $messages . ' is ' . $match[1]); } } } if (!isset($this->_trans_charsets[$messages])) { // not provided, use the one of the main locale files: $this->_trans_charsets[$messages] = $locales[$req_locale]['charset']; } } if (isset($trans[$messages][$search])) { // If the string has been translated: $r = $trans[$messages][$search]; } else { // Fallback to global T_() function: return T_($string, $req_locale); } if (!empty($evo_charset)) { $r = convert_charset($r, $evo_charset, $this->_trans_charsets[$messages]); } return $r; }
/** * TRANSLATE! * * Translate a text to the desired locale or to the current locale. * * @param string String to translate, '' to get language file info (as in gettext spec) * @param string locale to translate to, '' to use current locale * @param array Array containing the following keys (all optional): * - 'ext_transarray': A reference to an alternate array * to use for the caching of the * translated strings or NULL to use * the internal array. * - 'alt_basedir': Alternate base directory to search * for translations, e. g. a plugin or * skin directory. * - 'for_helper': (boolean) Is the translation for the b2evoHelper object? * @return string The translated string or the original string on error. * * @internal The last parameter/its 'alt_basedir' key is used by * Plugin::T_() and Skin::T_(). */ function T_($string, $req_locale = '', $params = array()) { /** * The translations keyed by locale. * * This array is only used if $params['ext_transarray'] === NULL. * * @var array * @static */ static $_trans = array(); global $current_locale, $locales, $locales_path, $plugins_path; global $evo_charset, $Debuglog; $params = array_merge(array('ext_transarray' => NULL, 'alt_basedir' => '', 'for_helper' => false), $params); if (empty($req_locale)) { // By default we use the current locale if (empty($current_locale)) { // don't translate if we have no locale return $string; } $req_locale = $current_locale; } if (!isset($locales[$req_locale]['messages'])) { $Debuglog->add('No messages file path for locale. $locales["' . $req_locale . '"] is ' . var_export(@$locales[$req_locale], true), 'locale'); if (!empty($evo_charset)) { $string = convert_charset($string, $evo_charset, 'iso-8859-1'); } return $string; } $messages = $locales[$req_locale]['messages']; if (is_null($params['ext_transarray'])) { // use our array //$Debuglog->add( 'Using internal array', 'locale' ); $trans =& $_trans; } else { // use external array: //$Debuglog->add( 'Using external array', 'locale' ); $trans =& $params['ext_transarray']; } if (!isset($trans[$messages])) { // Translations for current locale have not yet been loaded: $Debuglog->add('We need to load a new translation file to translate: "' . $string . '"', 'locale'); if ($params['alt_basedir'] != '') { // Load the translation file from the alternative base dir: //$Debuglog->add( 'Using alternative basedir ['.$params['alt_basedir'].']', 'locale' ); $path = $params['alt_basedir'] . '/locales/' . $messages . '/_global.php'; } else { // Load our global translation file. $path = $locales_path . $messages . '/_global.php'; } if (file_exists($path) && is_readable($path)) { $Debuglog->add('T_: Loading file: ' . $path, 'locale'); include_once $path; } else { $Debuglog->add('T_: Messages file does not exist or is not readable: ' . $path, 'locale'); } if (!isset($trans[$messages])) { // Still not loaded... file doesn't exist, memorize that no translations are available // echo 'file not found!'; $trans[$messages] = array(); } else { if (!isset($trans[$messages]['__meta__'])) { // Unknown/old messages format (< version 1): $Debuglog->add('Found deprecated messages format (no __meta__ info).', 'locale'); // Translate keys (e.g. 'foo\nbar') to real strings ("foo\nbar") // Doing this here for all strings, is actually faster than doing it on key lookup (like it has been done before always) foreach ($trans[$messages] as $k => $v) { if (($pos = strpos($k, '\\')) === false) { // fast-path-skip continue; } // Replace string as done in the good old days: $new_k = str_replace(array('\\n', '\\r', '\\t'), array("\n", "\r", "\t"), $k); if ($new_k != $k) { $trans[$messages][$new_k] = $v; unset($trans[$messages][$k]); } } } } } // sam2kb> b2evolution creates _global.php files with "\n" line breaks, and we must normalize newlines // in supplied string before trying to translate it. Otherwise strings won't match. // fp> TODO: this is not really satisfying in the long term. We need our own // parser that will extract T_() TS_() NT_() etc string and create a normalized potfile. // Actually it sgould create several potfiles. One for general use, one for admin, one for install, etc. // That way translators can concentrate on the most essential stuff first. $search_string = str_replace(array("\r\n", "\r"), "\n", $string); if (isset($trans[$messages][$search_string])) { // If the string has been translated: //$Debuglog->add( 'String ['.$string.'] found', 'locale' ); $r = $trans[$messages][$search_string]; if (isset($trans[$messages]['__meta__']['charset'])) { // new format: charset in meta data: $messages_charset = $trans[$messages]['__meta__']['charset']; } else { // old format.. extract charset from content type or fall back to setting from global locale definition: $meta = $trans[$messages]['']; if (preg_match('~^Content-Type: text/plain; charset=(.*);?$~m', $meta, $match)) { $messages_charset = $match[1]; } else { $messages_charset = $locales[$req_locale]['charset']; } // Set it accordingly to new format. $trans[$messages]['__meta__']['charset'] = $messages_charset; } } else { //$Debuglog->add( 'String ['.$string.'] not found', 'locale' ); // Return the English string: $r = $string; // $messages_charset = 'iso-8859-1'; // our .php file encoding // fp> I am changing the above for the User custom field group labels (in theroy the php files are plain ASCII anyways!!) $messages_charset = $evo_charset; } if (!empty($evo_charset)) { $r = convert_charset($r, $evo_charset, $messages_charset); } else { $Debuglog->add(sprintf('Warning: evo_charset not set to translate "%s"', htmlspecialchars($string)), 'locale'); } if ($params['for_helper']) { // translation is for the b2evoHelper object add_js_translation($string, $r); } //$Debuglog->add( 'Result: ['.$r.']', 'locale' ); return $r; }
/** * Tests {@link test_convert_charset()}. */ function test_convert_charset() { $this->assertEqual(convert_charset('йкил', 'utf-8', 'latin1'), 'éêèë'); $this->assertEqual(convert_charset('éêèë', 'latin1', 'utf-8'), 'йкил'); $this->assertEqual(convert_charset('éêèë', 'Latin1', 'UTF-8'), 'йкил'); $this->assertEqual(convert_charset('éêèë', 'Latin1', 'Utf8'), 'йкил'); // THIS ONE will produce NO conversion because 'latin-1' is not a valid charset name for this func $this->assertEqual(convert_charset('йкил', 'utf-8', 'latin-1'), 'йкил'); }
/** * return a cleaned IPTC value. * * @param string $value * @return string */ function clean_iptc_value($value) { // strip leading zeros (weird Kodak Scanner software) while (isset($value[0]) and $value[0] == chr(0)) { $value = substr($value, 1); } // remove binary nulls $value = str_replace(chr(0x0), ' ', $value); if (preg_match('/[\\x80-\\xff]/', $value)) { // apparently mac uses some MacRoman crap encoding. I don't know // how to detect it so a plugin should do the trick. $value = trigger_change('clean_iptc_value', $value); if (($qual = qualify_utf8($value)) != 0) { // has non ascii chars if ($qual > 0) { $input_encoding = 'utf-8'; } else { $input_encoding = 'iso-8859-1'; if (function_exists('iconv') or function_exists('mb_convert_encoding')) { // using windows-1252 because it supports additional characters // such as "oe" in a single character (ligature). About the // difference between Windows-1252 and ISO-8859-1: the characters // 0x80-0x9F will not convert correctly. But these are control // characters which are almost never used. $input_encoding = 'windows-1252'; } } $value = convert_charset($value, $input_encoding, get_pwg_charset()); } } return $value; }
/** * Check the content of a given URL (referer), if the requested URI (with different hostname variations) * is present. * * @todo Use DB cache to avoid checking the same page again and again! (Plugin DB table) * * @param string * @param string URI to append to matching pattern for hostnames * @return boolean */ function is_referer_linking_us($referer, $uri) { global $misc_inc_path, $lib_subdir, $ReqHost; if (empty($referer)) { return false; } // Load page content (max. 500kb), using fsockopen: $url_parsed = @parse_url($referer); if (!$url_parsed) { return false; } if (empty($url_parsed['scheme'])) { $url_parsed = parse_url('http://' . $referer); } $host = $url_parsed['host']; $port = empty($url_parsed['port']) ? 80 : $url_parsed['port']; $path = empty($url_parsed['path']) ? '/' : $url_parsed['path']; if (!empty($url_parsed['query'])) { $path .= '?' . $url_parsed['query']; } $fp = @fsockopen($host, $port, $errno, $errstr, 30); if (!$fp) { // could not access referring page $this->debug_log('is_referer_linking_us(): could not access «' . $referer . '» (host: ' . $host . '): ' . $errstr . ' (#' . $errno . ')'); return false; } // Set timeout for data: if (function_exists('stream_set_timeout')) { stream_set_timeout($fp, 20); } else { socket_set_timeout($fp, 20); } // PHP 4 // Send request: $out = "GET {$path} HTTP/1.0\r\n"; $out .= "Host: {$host}:{$port}\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); // Skip headers: $i = 0; $source_charset = 'iso-8859-1'; // default while (($s = fgets($fp, 4096)) !== false) { $i++; if ($s == "\r\n" || $i > 100) { break; } if (preg_match('~^Content-Type:.*?charset=([\\w-]+)~i', $s, $match)) { $source_charset = $match[1]; } } // Get the refering page's content $content_ref_page = ''; $bytes_read = 0; while (($s = fgets($fp, 4096)) !== false) { $content_ref_page .= $s; $bytes_read += strlen($s); if ($bytes_read > 512000) { // do not pull more than 500kb of data! break; } } fclose($fp); if (!strlen($content_ref_page)) { $this->debug_log('is_referer_linking_us(): empty $content_ref_page (' . bytesreadable($bytes_read) . ' read)'); return false; } $have_idn_name = false; // Build the search pattern: // We match for basically for 'href="[SERVER][URI]', where [SERVER] is a list of possible hosts (especially IDNA) $search_pattern = '~\\shref=["\']?https?://('; $possible_hosts = array($_SERVER['HTTP_HOST']); if ($_SERVER['SERVER_NAME'] != $_SERVER['HTTP_HOST']) { $possible_hosts[] = $_SERVER['SERVER_NAME']; } $search_pattern_hosts = array(); foreach ($possible_hosts as $l_host) { if (preg_match('~^([^.]+\\.)(.*?)([^.]+\\.[^.]+)$~', $l_host, $match)) { // we have subdomains in this hostname if (stristr($match[1], 'www')) { // search also for hostname without 'www.' $search_pattern_hosts[] = $match[2] . $match[3]; } } $search_pattern_hosts[] = $l_host; } $search_pattern_hosts = array_unique($search_pattern_hosts); foreach ($search_pattern_hosts as $l_host) { // add IDN, because this could be linked: $l_idn_host = idna_decode($l_host); // the decoded puny-code ("xn--..") name (utf8) if ($l_idn_host != $l_host) { $have_idn_name = true; $search_pattern_hosts[] = $l_idn_host; } } // add hosts to pattern, preg_quoted for ($i = 0, $n = count($search_pattern_hosts); $i < $n; $i++) { $search_pattern_hosts[$i] = preg_quote($search_pattern_hosts[$i], '~'); } $search_pattern .= implode('|', $search_pattern_hosts) . ')'; if (empty($uri)) { // host(s) should end with "/", "'", '"', "?" or whitespace $search_pattern .= '[/"\'\\s?]'; } else { $search_pattern .= preg_quote($uri, '~'); // URI should end with "'", '"' or whitespace $search_pattern .= '["\'\\s]'; } $search_pattern .= '~i'; if ($have_idn_name) { // Convert charset to UTF-8, because the decoded domain name is UTF-8, too: if (can_convert_charsets('utf-8', $source_charset)) { $content_ref_page = convert_charset($content_ref_page, 'utf-8', $source_charset); } else { $this->debug_log('is_referer_linking_us(): warning: cannot convert charset of referring page'); } } if (preg_match($search_pattern, $content_ref_page)) { $this->debug_log('is_referer_linking_us(): found current URL in page (' . bytesreadable($bytes_read) . ' read)'); return true; } else { if (strpos($referer, $ReqHost) === 0 && !empty($uri)) { // Referer is the same host.. just search for $uri if (strpos($content_ref_page, $uri) !== false) { $this->debug_log('is_referer_linking_us(): found current URI in page (' . bytesreadable($bytes_read) . ' read)'); return true; } } $this->debug_log('is_referer_linking_us(): ' . sprintf('did not find «%s» in «%s» (%s bytes read).', $search_pattern, $referer, bytesreadable($bytes_read))); return false; } }
/** * Extracts a keyword from a raw not encoded URL. * Will only extract keyword if a known search engine has been detected. * Returns the keyword: * - in UTF8: automatically converted from other charsets when applicable * - strtolowered: "QUErY test!" will return "query test!" * - trimmed: extra spaces before and after are removed * * A list of supported search engines can be found in /inc/sessions/model/_search_engines.php * The function returns false when a keyword couldn't be found. * eg. if the url is "http://www.google.com/partners.html" this will return false, * as the google keyword parameter couldn't be found. * * @param string URL referer * @return array|false false if a keyword couldn't be extracted, * or array( * 'engine_name' => 'Google', * 'keywords' => 'my searched keywords', * 'serprank' => 4) */ function extract_params_from_referer($ref) { global $Debuglog, $search_engine_params, $evo_charset, $current_charset; // Make sure we don't try params extraction twice $this->_search_params_tried = true; @(list($ref_host, $ref_path, $query, $fragment) = $this->is_search_referer($ref, true)); if (empty($ref_host)) { // Not a search referer return false; } $search_engine_name = $search_engine_params[$ref_host][0]; $keyword_param = NULL; if (!empty($search_engine_params[$ref_host][1])) { $keyword_param = $search_engine_params[$ref_host][1]; } if (is_null($keyword_param)) { // Get settings from first item in group $search_engine_names = $this->get_search_engine_names(); $url = $search_engine_names[$search_engine_name]; $keyword_param = $search_engine_params[$url][1]; } if (!is_array($keyword_param)) { $keyword_param = array($keyword_param); } if ($search_engine_name == 'Google Images' || $search_engine_name == 'Google' && strpos($ref, '/imgres') !== false) { // Google image search $search_engine_name = 'Google Images'; $query = urldecode(trim($this->get_param_from_string($query, 'prev'))); $query = str_replace('&', '&', strstr($query, '?')); } elseif ($search_engine_name == 'Google' && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0)) { // Google with "as_" param $keys = array(); if ($key = $this->get_param_from_string($query, 'as_q')) { array_push($keys, $key); } if ($key = $this->get_param_from_string($query, 'as_oq')) { array_push($keys, str_replace('+', ' OR ', $key)); } if ($key = $this->get_param_from_string($query, 'as_epq')) { array_push($keys, "\"{$key}\""); } if ($key = $this->get_param_from_string($query, 'as_eq')) { array_push($keys, "-{$key}"); } $key = trim(urldecode(implode(' ', $keys))); } if (empty($key)) { // we haven't extracted a search key with the special cases above... foreach ($keyword_param as $param) { if ($param[0] == '/') { // regular expression match if (@preg_match($param, $ref, $matches)) { $key = trim(urldecode($matches[1])); break; } } else { // search for keywords now &vname=keyword if ($key = $this->get_param_from_string($query, $param)) { $key = trim(urldecode($key)); if (!empty($key)) { break; } } } } } $key_param_in_query = false; if (empty($key) && !empty($keyword_param)) { // Check if empty key param exists in query, e.g. "/search?q=&other_param=text" foreach ($keyword_param as $k_param) { if (strpos($query, '&' . $k_param . '=') !== false || strpos($query, $k_param . '=') === 0) { // Key param with empty value exists in query, We can decide this referer url as from search engine $key_param_in_query = true; } } } if (empty($key) && !$key_param_in_query) { // Not a search referer if ($this->referer_type == 'search') { // If the referer was detected as 'search' we need to change it to 'special' // to keep search stats clean. $this->referer_type = 'special'; $Debuglog->add('Hit: extract_params_from_referer() overrides referer type set by detect_referer(): "search" -> "special"', 'request'); } return false; } // Convert encoding if (!empty($search_engine_params[$ref_host][3])) { $ie = $search_engine_params[$ref_host][3]; } elseif (isset($url) && !empty($search_engine_params[$url][3])) { $ie = $search_engine_params[$url][3]; } else { // Fallback to default encoding $ie = array('utf-8', 'iso-8859-15'); } if (is_array($ie)) { if (can_check_encoding()) { foreach ($ie as $test_encoding) { if (check_encoding($key, $test_encoding)) { $ie = $test_encoding; break; } } } else { $ie = $ie[0]; } } $key = convert_charset($key, $evo_charset, $ie); // convert to lower string but keep in evo_charset $saved_charset = $current_charset; $current_charset = $evo_charset; $key = utf8_strtolower($key); $current_charset = $saved_charset; // Extract the "serp rank" // Typically http://google.com?s=keyphraz&start=18 returns 18 if (!empty($search_engine_params[$ref_host][4])) { $serp_param = $search_engine_params[$ref_host][4]; } elseif (isset($url) && !empty($search_engine_params[$url][4])) { $serp_param = $search_engine_params[$url][4]; } else { // Fallback to default params $serp_param = array('offset', 'page', 'start'); } if (!is_array($serp_param)) { $serp_param = array($serp_param); } if (strpos($search_engine_name, 'Google') !== false) { // Append fragment which Google uses in instant search $query .= '&' . $fragment; } foreach ($serp_param as $param) { if ($var = $this->get_param_from_string($query, $param)) { if (ctype_digit($var)) { $serprank = $var; break; } } } $this->_search_engine = $search_engine_name; $this->_keyphrase = $key; $this->_serprank = isset($serprank) ? $serprank : NULL; return array('engine_name' => $this->_search_engine, 'keyphrase' => $this->_keyphrase, 'serprank' => $this->_serprank); }
/** * Test {@link bpost_count_words()}. */ function test_bpost_count_words() { global $evo_charset; if (!can_convert_charsets('ISO-8859-1', 'UTF-8')) { echo 'Skipping tests (cannot convert charsets)...<br />', "\n"; return; } $old_evo_charset = $evo_charset; $evo_charset = 'ISO-8859-1'; $this->assertEqual(bpost_count_words(convert_charset('eine gleichung wie 1 + 2 = 9 /', 'ISO-8859-1', 'UTF-8')), 3); $this->assertEqual(bpost_count_words(convert_charset('mixed with the 3 ümläuts: äää ööö üüü ÄÄÄ ÖÖÖ ÜÜÜ', 'ISO-8859-1', 'UTF-8')), 10); $evo_charset = 'UTF-8'; $this->assertEqual(bpost_count_words('möre (again 3) ümläüts... öö üü ää ÄÄ ÖÖ ÜÜ'), 9); $this->assertEqual(bpost_count_words('russian: Расширенные возможности - это удобный'), 5); $this->assertEqual(bpost_count_words('A versão foi apelidade de Tilqi, porque era aniversário dele. numbers: 42'), 11); $this->assertEqual(bpost_count_words('HTML tags -> <a href="http://b2evolution.net" target="_blank">visit b2evo!</a>. Some other chars: "\' \' " <<< < >>> > ``` -- versão удобный überladen'), 10); $evo_charset = $old_evo_charset; }