diff options
-rw-r--r-- | it.class | 51 | ||||
-rw-r--r-- | it_auto_prepend.php | 2 | ||||
-rw-r--r-- | it_dbi.class | 18 | ||||
-rw-r--r-- | it_debug.class | 6 | ||||
-rw-r--r-- | it_mail.class | 28 | ||||
-rw-r--r-- | it_url.class | 97 | ||||
-rw-r--r-- | it_xml.class | 2 | ||||
-rw-r--r-- | itjs.class | 15 | ||||
-rw-r--r-- | itjs.php | 14 | ||||
-rwxr-xr-x | tests/exec.t | 2 | ||||
-rwxr-xr-x | tests/it.t | 13 | ||||
-rwxr-xr-x | tests/it_dbi.t | 8 | ||||
-rwxr-xr-x | tests/it_html.t | 24 | ||||
-rwxr-xr-x | tests/it_mail.t | 55 | ||||
-rwxr-xr-x | tests/it_pipe.t | 19 | ||||
-rwxr-xr-x | tests/it_url.t | 8 | ||||
-rwxr-xr-x | tests/it_xml.t | 2 | ||||
-rwxr-xr-x | tests/itjs.t | 2 |
18 files changed, 199 insertions, 167 deletions
@@ -75,9 +75,9 @@ static function &cloneobj(&$object) static function log($name /* ... */) { $args = func_get_args(); - $line = it::date("Y-m-d H:i:s") . "\t" . implode("\t", array_slice($args, 1)) . "\n"; + $line = date("Y-m-d H:i:s") . "\t" . implode("\t", array_slice($args, 1)) . "\n"; $basefn = substr($name, 0, 1) == "/" ? $name : $GLOBALS['ULTRAHOME'] . "/log/$name"; - $fullfn = $basefn . "-" . it::date('Ymd'); + $fullfn = $basefn . "-" . date('Ymd'); if (substr($fullfn, 0, 1) == "/") { @@ -114,7 +114,7 @@ static function timerlog($label = '') * Send verbose error report to browser or (if display_errors is off) by email to .diffnotice gods, file owner or SERVER_ADMIN * @param $p either error title or assoc array of params, see below * @param $p['title'] error title, one line - * @param $p['body'] error body, multiline + * @param $p['body'] error body, multiline string or any data type (will be dumped) * @param $p['to'] comma separated recipient list * @param $p['id'] identifier of error. if given, only subsequent errors of same id will trigger message * @param $p['graceperiod'] number of seconds within which additional errors are ignored if id is set [60] @@ -244,13 +244,13 @@ static function error($p = array()) else if ($_SERVER['REMOTE_ADDR']) # toscreen mode: web echo "<pre style='z-index:10000; position:relative; background:white'>" . htmlspecialchars($p['title'] . "\n" . rtrim($body), ENT_COMPAT, "iso-8859-1") . "</pre>"; # works with iso-8859-1 or utf-8, UTF8SAFE else # toscreen mode: shell (outputs to stderr) - error_log(substr($p['title'], 0, 100000) . " in " . ($trace ? $trace : "{$p['file']}:{$p['line']} Url: $url") . " " . (EDC('verboseerrors') ? D($p['locals']) : "")); + error_log(substr($p['title'], 0, 100000) . " in " . ($trace ? $trace : " {$p['file']}:{$p['line']}") . " Url: $url " . (EDC('verboseerrors') ? D($p['locals']) : "")); } if ($_SERVER['REMOTE_ADDR']) # additional entry in log/error_log error_log("it::error: " . $p['title'] . " Url: $url"); - file_put_contents("/tmp/alertdata/alert.log", it::date() . " " . $p['title'] . " in " . ($trace ? $trace : "{$p['file']}:{$p['line']} Url: $url") . "\n", FILE_APPEND); + file_put_contents("/tmp/alertdata/alert.log", it::date() . " " . $p['title'] . " in " . ($trace ? $trace : "{$p['file']}:{$p['line']}") . " Url: $url\n", FILE_APPEND); @chmod("/tmp/alertdata/alert.log", 0777); return null; @@ -315,6 +315,31 @@ static function toascii($text) /** + * Check whether an IP adress lies within a given range. Supports IPv4 and IPv6 + * @param $ip IP address (192.168.42.123) + * @param $cidrs IP range in CIDR notation (192.168.42.64/26) or array of ranges + * @return true if $ip is within $cidr + */ +function cidr_match($ip, $cidrs) +{ + foreach ((array)$cidrs as $cidr) + { + list($subnet, $mask) = explode('/', $cidr); + $ip_bin = inet_pton($ip); + $subnet_bin = inet_pton($subnet); + $valid_bytes = $mask ? $mask >> 3 : 42; + $bitmask = 256 - (1 << (8 - ($mask & 7))); + $lastbyte_matched = $bitmask ? (ord($ip_bin{$valid_bytes}) & $bitmask) == (ord($subnet_bin{$valid_bytes}) & $bitmask) : true; + + if (substr($ip_bin, 0, $valid_bytes) == substr($subnet_bin, 0, $valid_bytes) && $lastbyte_matched) + return true; + } + + return false; +} + + +/** * Convert regex for preg (adds and escapes delimiter, adds modifiers) * @param $pattern Regex to convert * @param $p['casesensitive'] Regex is case sensitive (omit modifier i) @@ -509,7 +534,7 @@ static function filter_keys($array, $keys, $p = array()) * Construct shell command using it::shell_command, log it, execute it and return output as string. * {keyword} quotes and inserts value from assoc array like ET() * {0} .. {n} quotes and inserts positional arguments - * {-opts} takes an array and inserts options a la it_html attributes (value, true, false or null) + * {-opts} array of opts => {value,true,false,null}: it::exec('ls {-opts}', ['-opts' => ["-l" => true]]); * @param $cmd Format string with {keywords} a la ET() * @param $values (zero, one or more arrays can be passed) * @return output of command. shell errors not detectable, see error_log in /www/server/logs @@ -529,12 +554,8 @@ static function exec(/* $cmd, $values1 = array(), ... */) /** * Construct shell command using it::shell_command, log it, execute it and return exit code. - * stdout/stderr is forwarded to stdout/stderror of calling script - * {keyword} quotes and inserts value from assoc array like ET() - * {0} .. {n} quotes and inserts positional arguments - * {-opts} takes an array and inserts options a la it_html attributes (value, true, false or null) - * @param $cmd Format string with {keywords} a la ET() - * @param $values (zero, one or more arrays can be passed) + * stdout/stderr is forwarded to stdout/stderror of calling script. + * See ::exec above for usage * @return exit code of command. */ static function system(/* $cmd, $values1 = array(), ... */) @@ -555,8 +576,8 @@ static function system(/* $cmd, $values1 = array(), ... */) /** * Construct shell command - * Keywords {keyword} are replace a la ET(), {-opts} takes an array and - * inserts options a la it_html attributes (value, true, false or null) + * Keywords {keyword} are replace a la ET(), + * {-opts} array of opts => {value,true,false,null}: it::exec('ls {-opts}', ['-opts' => ["-l" => true]]); * @param $cmd Format string with {keywords} replace a la ET() * @param $values (zero, one or more arrays can be passed) * @return output of command. shell errors not detectable, see error_log in /www/server/logs @@ -745,7 +766,7 @@ static function getopt($usage, $p = array()) list($shortoptname, $shortoptarg) = $matches; } - if ($default = it::match('\[(.*)\]\s*$', $usageline)) + if (($default = it::match('\[(.*)\]\s*$', $usageline)) !== null) { if ($longoptarg || $shortoptarg) $defaults[$longoptname ? $longoptname : $shortoptname] = it::replace(array('^default:?\s*' => ""), trim($default)); diff --git a/it_auto_prepend.php b/it_auto_prepend.php index 10bee36..cea5a0d 100644 --- a/it_auto_prepend.php +++ b/it_auto_prepend.php @@ -70,7 +70,7 @@ function it_initialize() # IT_HOME is recommended variable name for applications $GLOBALS['IT_HOME'] = $GLOBALS['ULTRAHOME'] = it_untaint($GLOBALS['ULTRAHOME'], TC_ALL); - $needsconvert = $GLOBALS['IT_SYNTAXCONVERTER_DIR'] !== false && !@eval("return is_array(42=>69,);"); # Check if PHP is patched to support our syntax, see http://cschneid.com/php/ + try { $needsconvert = $GLOBALS['IT_SYNTAXCONVERTER_DIR'] !== false && !@eval("return is_array(42=>69,);"); } catch (Error $needsconvert) {} # Check if PHP is patched to support our syntax, see http://cschneid.com/php/ if ($needsconvert && !$GLOBALS['IT_SYNTAXCONVERTER_DIR']) { diff --git a/it_dbi.class b/it_dbi.class index 1b749eb..d65d1a2 100644 --- a/it_dbi.class +++ b/it_dbi.class @@ -115,10 +115,8 @@ function createclasses($p = array()) if (!$tables = $state['tables']) { - for ($tables = array(), $res = $dbi->query('SHOW TABLES', $p); $row = mysqli_fetch_row($res);) - $tables[] = $row[0]; - - $state = it_dbi::_state_get($dbid); # State could have been modified by query above + $tables = $dbi->tables($p); + $state = it_dbi::_state_get($dbid); # State could have been modified by $db->tables() call $state['tables'] = $tables; it_dbi::_state_put($dbid, $state); } @@ -422,6 +420,18 @@ function _write_pre_process(&$tags, $command) # NOPHPLINT /** + * Return an array of all tables of this database + */ +function tables($p = array()) +{ + for ($qr = $this->query('SHOW TABLES', $p); $row = mysqli_fetch_row($qr);) + $result[] = $row[0]; + + return (array)$result; +} + + +/** * Clear record */ function clear($pp = true) diff --git a/it_debug.class b/it_debug.class index 89d5259..5501353 100644 --- a/it_debug.class +++ b/it_debug.class @@ -130,7 +130,7 @@ static function dump($args) $item = preg_replace_callback('/\b(1[234]\d\d\d\d\d\d\d\d)\b(.*)/', function($m) { return $m[1] . $m[2] . " # " . date('Y-m-d H:i:s', $m[1]); }, $item); $item = preg_replace("#(=>?)\s*\n\s*(array|class)#", '$1 $2', $item); # array( and class on same line as key - $item = preg_replace('#array \(\s+([^({,;]+),\s+\)#', 'array( $1 )', $item); # 1-element arrays on 1 line + $item = preg_replace_callback('#array \(\s+([^(){};]{1,100}),\s+\)#', function($m) { return "array (" . it::replace(array("\n\s*" => " "), $m[1]) . ")"; }, $item); # short arrays on 1 line $item = preg_replace('#class (\S+) \{\s+([^({,;]+;)?\s+\}#', 'class $1 { $2 }', $item); # 1-element objects on 1 line #$item = preg_replace('#\{\s*var \$attr#', '{ var $attr', $item); # move $attr on same line $item = preg_replace("#\\(\s*\n\s*\\)#", "()", $item); # empty arrays on 1 line @@ -138,7 +138,7 @@ static function dump($args) $item = "$red$item$nored"; if ($htmlok) - $item = preg_replace("#http://[^\s']*#", "<a href='\$0'>\$0</a>", htmlspecialchars($item)); + $item = preg_replace("#(https?:)?//[^\s']*#", "<a href='\$0'>\$0</a>", htmlspecialchars($item)); if (preg_match('/^[\'"]/', $var)) $r .= "$item "; @@ -156,7 +156,7 @@ static function dump($args) $title = htmlspecialchars(it_debug::backtrace(array('skiplevels' => 1)), ENT_COMPAT, "ISO-8859-1"); if ($htmlok) - return "<pre title='$title' style='color:#c00; text-align:left; background:white; margin:0 0 0.8em; border-bottom:1px solid #ddd; z-index:9999; position:relative'>$r</pre>\n"; + return "<pre title='$title' style='color:#c00; text-align:left; background:white; border-bottom:1px solid #ddd; z-index:9999; position:relative'>$r</pre>\n"; else return $ansiok ? "$r\n" : "\xe2\x96\x88\xe2\x96\x8c" . it::replace(array("\n" => "\n\xe2\x96\x88 "), "$r") . "\n"; } diff --git a/it_mail.class b/it_mail.class index 5746056..503da08 100644 --- a/it_mail.class +++ b/it_mail.class @@ -305,7 +305,14 @@ function send($p = array()) function header_escape($string) { return preg_match('/[\x00-\x1f\x7f-\xff]/', $string) - ? ("=?{$this->charset}?Q?" . str_replace(" ", "_", preg_replace_callback('/[\x00-\x1f=\x7f-\xff]/', function($m) { return sprintf('=%02X', ord($m[0])); }, trim($string, '"'))) . "?=") + ? ltrim( + ($encoded = @iconv_mime_encode('', $string, array('scheme' => 'Q', 'input-charset' => $this->charset, 'output-charset' => $this->charset))) !== false + ? $encoded + : ($encoded = @iconv_mime_encode('', $string, array('scheme' => 'B', 'input-charset' => $this->charset, 'output-charset' => $this->charset))) !== false + ? $encoded + : iconv_mime_encode('', $string, array('scheme', 'B', 'input-charset' => 'ISO-8859-1', 'output-charset' => $this->charset)), + ' :' + ) : $string; } @@ -318,15 +325,26 @@ function addrlist_escape($string) { # Exclude e-mail addresses from being encoded as # e.g. GMail or Exchange have problems with that - foreach (explode(',', $string) as $mailbox) + foreach (str_split($string) as $char) + { + if ($char == '"') + $quoted = !$quoted; + + if ($char == ',' && !$quoted) + $n++; + else + $mailboxes[$n] .= $char; + } + + foreach ((array)$mailboxes as $mailbox) { if (preg_match('/^(.*)(\s+?<[^>]+@[^>]+>\s*)$/', $mailbox, $matches)) - $result[] = $this->header_escape($matches[1]) . $matches[2]; + $result[] = $this->header_escape(trim($matches[1])) . $matches[2]; else - $result[] = $mailbox; + $result[] = trim($mailbox); } - return implode(',', $result); + return implode(', ', $result); } diff --git a/it_url.class b/it_url.class index ab1f90f..1a8d171 100644 --- a/it_url.class +++ b/it_url.class @@ -1,6 +1,6 @@ <?php /* -** Copyright (C) 1995-2007 by the ITools Authors. +** Copyright (C) 1995-2016 by the ITools Authors. ** This file is part of ITools - the Internet Tools Library ** ** ITools is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ class it_url var $hostname; /* E.g. relog.ch */ var $realhostname; /* E.g. www.relog.ch */ var $port; /* E.g. 80 */ - var $explicitport; /* E.g. 80, explicitly set in rawurl */ + var $explicitport; /* E.g. :80, explicitly set in rawurl */ var $path; /* E.g. / */ var $rawurl; /* E.g. HTTP://falcon:joshua@www.Relog.CH.:80/default.asp */ var $user; /* E.g. falcon */ @@ -42,78 +42,22 @@ class it_url /** * Constructor: canonicalize an URL * @param $url URL this object represents - * @param $options['encoding'] encoding of hostname ('utf-8', 'iso-8859-1' etc.) */ -function it_url($url = null, $options = array()) +function it_url($url = null) { $this->rawurl = $url; - - if (preg_match('#^([a-z]+):/+(?:([^:]*):([^@]*)@)?(.*)$#is', $url, $regs)) - { - $this->protocol = strtolower($regs[1]); - $this->user = $regs[2]; - $this->pass = $regs[3]; - $url = $regs[4]; - } - else if (preg_match('/^[a-z]:/', $url) || preg_match('#^/#', $url)) - { - $this->protocol = 'file'; - } - else - $this->protocol = 'http'; - - /* Default port */ - if ($this->protocol == 'http') - $protoport = 80; - else if ($this->protocol == 'https') - $protoport = 443; - - $this->port = intval($protoport); - - if (class_exists('Net_IDNA', false)) - $idn = Net_IDNA::getInstance(); - - if ($idn) - $pattern = '^([^/]+)/*(.*)$'; - else - $pattern = '^([a-z0-9_:\.-]+)/*(.*)$'; - - $this->explicitport = ''; - if (preg_match("#$pattern#is", $url, $regs)) - { - list($hostname, $port) = explode(':', $regs[1]); - - $this->realhostname = strtolower($hostname); - - if ($port) { - $this->port = intval($port); - $this->explicitport = ":" . $port; - } - - $url = $regs[2]; - } - + $comp = parse_url($url); + $this->protocol = strtolower($comp['scheme']) ?: "http"; + $protoport = $this->protocol == 'https' ? 443 : 80; # port according to protocol + $this->port = intval($comp['port'] ?: $protoport); # this is set even in default case + $this->explicitport = $comp['port'] ? ':' . $comp['port'] : ''; # only set if explicitly specified in url, contains leading : + $this->user = $comp['user']; + $this->pass = $comp['pass']; + $this->realhostname = strtolower($comp['host']); $this->hostname = preg_replace('/^www\./', '', $this->realhostname); - - $this->path = preg_replace('#^/$#', '', $url); - - if ($this->port != $protoport) - $this->url = "$this->protocol://$this->realhostname:$this->port/$this->path"; - else - $this->url = "$this->protocol://$this->realhostname/$this->path"; - - if ($idn) - { - $realhostname = $this->realhostname; - - if (!preg_match('/^utf-?8$/i', $options['encoding'])) - $realhostname = utf8_encode($realhostname); - - $encoded = $idn->encode($realhostname); - - if ($encoded != $realhostname) - $this->realhostname = $encoded; - } + $this->path = ltrim($comp['path'] . ($comp['query'] ? '?' . $comp['query'] : ''), '/'); # $this->path is named poorly, it includes path and query + $this->url = "$this->protocol://$this->realhostname" . ($this->port != $protoport ? $this->explicitport : '') . "/$this->path"; + $this->realhostname = idn_to_ascii($this->realhostname) ?: $this->realhostname; # punycode or original } @@ -349,10 +293,8 @@ static function curl_opts($p=array()) CURLOPT_FOLLOWLOCATION => false, CURLOPT_HTTPHEADER => $headers, - CURLOPT_SSL_VERIFYPEER => true, - CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_CAPATH => '/etc/ssl/certs/', - CURLOPT_SSL_VERIFYPEER => 1, + CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLINFO_HEADER_OUT => 1, @@ -445,7 +387,7 @@ function get_multi($p=null) foreach ($urls as $key => $url) { $handle = curl_init(); - curl_setopt($handle, CURLOPT_URL, $url['url']); + curl_setopt($handle, CURLOPT_URL, it::replace([ '^//' => "http://" ], $url['url'])); curl_setopt_array($handle, $opts); curl_multi_add_handle($mh, $handle); $keys[$handle] = $key; @@ -541,6 +483,7 @@ function get_cache_filename($p) * @param $p['keepfailed'] keep old versions of files if download fails (sending alerts conservatively) * @param $p['returnheaders'] Return array($path, $headers) instead of simply $path * @param $p['it_error'] parameters for it::error() + * @return Cache filename or false if fetch failed */ function get_cache($p = array()) { @@ -645,11 +588,11 @@ function get_cache($p = array()) } # cache cleanup at night - if ((date('H')*3600 + date('i')*60 < $p['cleanbefore']) && (time()-@filemtime($p['cachedir'] . "/cleaned") > 80000)) + if (date('H') >= 1 && (date('H')*3600 + date('i')*60 < $p['cleanbefore']) && (time()-@filemtime($p['cachedir'] . "/cleaned") > 80000)) { touch($p['cachedir'] . "/cleaned"); $maxagemin = intval($p['maxage']/60); - exec("nohup bash -c 'cd {$p['cachedir']} && sleep 10 && find ?? -mmin +$maxagemin -print0 | xargs -0 -r rm' </dev/null >/dev/null 2>&1 &"); + exec("nohup bash -c 'cd {$p['cachedir']} && for i in ??; do sleep 20; ionice -c 3 find \$i -mmin +$maxagemin -delete; done' </dev/null >/dev/null 2>&1 &"); } if (EDC('getcachelog')) @@ -665,7 +608,7 @@ function get_cache($p = array()) */ function get_cache_contents($p) { - return ($fn = self::get_cache($p)) ? file_get_contents($fn) : ($p['safety'] === 0 ? null : it::error(array('title' => "failed getting " . $p['url'], 'body' => var_export($p, true)))); + return ($fn = self::get_cache($p)) ? file_get_contents($fn) : ($p['safety'] === 0 ? null : it::error(array('title' => "failed getting " . it_url::absolute($p['url']), 'body' => var_export($p, true)))); } /** diff --git a/it_xml.class b/it_xml.class index 7584c1e..644ef9e 100644 --- a/it_xml.class +++ b/it_xml.class @@ -120,7 +120,7 @@ function _sanitize($xmldata, $isutf8 = null) if (!preg_match('/^(<\?xml|\xEF\xBB\xBF|\xFE\xFF|\xFF\xFE|\x00\x00\xFE\xFF|\x00\x00\xFF\xFE)/', $xmldata)) $xmldata = '<?xml version="1.0" encoding="' . $this->_p['encoding'] . '"?>' . $xmldata; - $isutf8 = (!preg_match('/^<\?xml[^>]* encoding=/i', $xmldata) || preg_match('/^<\?xml[^>]* encoding=.utf-8/i', $xmldata)); + $isutf8 = (!preg_match('/^<\?xml[^>]* encoding=/i', $xmldata) || preg_match('/^<\?xml[^>]* encoding=.utf-?8/i', $xmldata)); } # Decode illegal entities but protect semantically important ones @@ -146,7 +146,7 @@ static function filenames($filelist) /** * Return (php-interpreted by default) files that will be sent to client. Files must exist. */ -static function filecontents($filenames, $execphp = true) +static function filecontents($filenames) { foreach ($filenames as $filename) { @@ -155,9 +155,9 @@ static function filecontents($filenames, $execphp = true) { $origget = $_GET; list($filename, $paramstr) = explode("?", $filename); - if ($paramstr && $execphp) + if ($paramstr) parse_str($paramstr, $_GET); - $result .= it::replace(array('^1$' => ""), $execphp ? include($filename) : file_get_contents($filename), array('utf8' => false)); + $result .= it::replace(array('^1$' => ""), it::match('\.(js|css)$', $filename) ? include_once($filename) : it_url::get($filename), array('utf8' => false)); $_GET = $origget; } $result .= ob_get_clean(); @@ -198,15 +198,12 @@ static function checksum($fnlist, $p = array()) foreach (is_array($fnlist) ? $fnlist : itjs::filenames($fnlist) as $filename) $filenames[] = !file_exists($filename) && file_exists($t = it::replace(array('^/www/[^/]*' => "/www/lib.search.ch"), $filename)) ? $t : $filename; - if (preg_grep('/jquery(build)\.js/', $filenames)) # jquery files may be included invisibly - $filenames = array_merge($filenames, array("/www/lib.search.ch/var/jquery-ui/dist/minified/core.min.js", "/www/lib.search.ch/var/jquery/dist/jquery.min.js")); - $key = "itjs_" . md5(join("", it::map('"$v" . @filemtime("$v")', $filenames))); if ($filenames && $p['short_expire'] && (time() - max(@array_map('filemtime', $filenames)) < 60)) return "-"; # trigger short expire, our file may not yet be up to date on other slaves else if ($filenames) - return it_cache::get($key) ?: it_cache::put($key, substr(md5(self::filecontents($filenames, false)), 0, 10), array('ttl' => 60)); + return it_cache::get($key) ?: it_cache::put($key, substr(md5("X" . self::filecontents($filenames)), 0, 10), array('ttl' => 60)); } /** @@ -214,8 +211,8 @@ static function checksum($fnlist, $p = array()) */ function crcurl($url, $p = array()) { - if (it::match('^http', $url)) # remote url, must fetch to crc - list($fn, $short_expire) = array(it_url::get_cache(array('url' => $url, 'maxage' => 3600, 'id' => "itjs_crcurl") + $p), false); + if (it::match('^(http|//)', $url)) # remote url, must fetch to crc + list($fn, $short_expire) = array(it_url::get_cache(array('url' => $url, 'maxage' => 3600, 'safety' => 1, 'id' => "itjs_crcurl") + $p), false); else list($fn, $short_expire) = array(($m = it::match("^//(\w+)(/.*)", $url)) ? "/www/$m[0].search.ch" . $m[1] : $GLOBALS['ULTRAHOME'] . $url, true);; @@ -31,13 +31,13 @@ itjs::far_future_headers(); # may exit $files = itjs::filenames($_GET['files'] ?: it::match('/itjs/([-a-z0-9_,.]*)', $_SERVER['PHP_SELF'])); $data = itjs::filecontents($files); -$file = end($files); +$lastfile = end($files) ?: it::match('[^,]+$', $_GET['files']); # set correct mime type even if files not found -if (it::match('\.gif$', $file)) +if (it::match('\.gif$', $lastfile)) { header("Content-Type: image/gif"); } -else if (it::match('\.css', $file)) +else if (it::match('\.css', $lastfile)) { header("Content-Type: text/css"); $data .= "\n#it_boot_dom { display:none }\n"; # Append magic style for it_boot @@ -56,6 +56,8 @@ else if (it::match('\.css', $file)) '([^/])\*(\w)' => '$1$2', 'svg\..*' => "", # ignore svg styles '\battr\(.*?\)' => "'x'", # ignore content:attr(placeholder) + '\bcalc\(.*?\)' => "0", # ignore calc() + '\bbackground-position-[xy]' => "background-position", ), $data ); @@ -65,14 +67,14 @@ else if (it::match('\.css', $file)) $data ); } -else if (it::match('\.htc$', $file)) +else if (it::match('\.htc$', $lastfile)) { header("Content-Type: text/x-component"); } -else if (!it::match('\.html$', $file)) +else if (!it::match('\.html$', $lastfile)) { $data = "window.trace+='i';\n$data\nwindow.trace+='I';\n"; - $data .= "\nwindow.it_gotjs = (window.it_gotjs ? window.it_gotjs : '') + '." . it::match('\w+', basename($file)) . "';\n"; + $data .= "\nwindow.it_gotjs = (window.it_gotjs ? window.it_gotjs : '') + '." . it::match('\w+', basename($lastfile)) . "';\n"; $charset = ini_get('default_charset') ?: 'iso-8859-1'; header("Content-Type: application/x-javascript; charset=$charset"); } diff --git a/tests/exec.t b/tests/exec.t index da5113d..c155c88 100755 --- a/tests/exec.t +++ b/tests/exec.t @@ -84,7 +84,7 @@ it::system('touch /tmp/it_system_test'); ok(file_exists('/tmp/it_system_test'), 'shell command gets executed'); @unlink('/tmp/it_system_test'); -it::system('touch {path}', 'path' => '/tmp/it_system_test'); +it::system('touch {path}', array('path' => '/tmp/it_system_test')); ok(file_exists('/tmp/it_system_test'), 'shell command with argument'); @unlink('/tmp/it_system_test'); @@ -315,6 +315,19 @@ is(it::grep('match', array('foo' => 'match', 'bar' => 'gna')), array('foo' => 'm setlocale(LC_CTYPE, $oldlocale); ini_set('default_charset', $oldcharset); # end of tests that must run with specific charset +# it::cidr_match tests +is(it::cidr_match('192.168.2.3', '192.168.2.5'), false, "cidr_match full IP no match no mask"); +is(it::cidr_match('192.168.2.3', '192.168.2.3'), true, "cidr_match full IP match no mask"); +is(it::cidr_match('192.168.2.3', '192.168.2.5/32'), false, "cidr_match full IP no match"); +is(it::cidr_match('192.168.2.5', '192.168.2.5/32'), true, "cidr_match full IP match"); +is(it::cidr_match('192.168.1.1', '192.168.42.0/24'), false, "cidr_match no match"); +is(it::cidr_match('192.168.42.1', '192.168.42.0/24'), true, "cidr_match basic match"); +is(it::cidr_match('192.168.42.42', '192.168.0.0/16'), true, "cidr_match class b"); +is(it::cidr_match('192.168.42.42', '192.168.42.64/26'), false, "cidr_match offset no match"); +is(it::cidr_match('192.168.42.42', '192.168.42.32/27'), true, "cidr_match offset"); +is(it::cidr_match('2001:918:ff83:101:798e:77c0:b722:fe56', '2001:918:ff83:101::/64'), true, "cidr_match ipv6"); +is(it::cidr_match('2001:918:ff83:102:798e:77c0:b722:fe56', '2001:918:ff83:101::/64'), false, "cidr_match ipv6 no match" ); +is(it::cidr_match('10.11.12.13', array('10.0.0.0/8', '192.168.0.0./16')), true, "cidr_match array"); # it::filter_keys tests diff --git a/tests/it_dbi.t b/tests/it_dbi.t index c9e7d9d..ca39f1f 100755 --- a/tests/it_dbi.t +++ b/tests/it_dbi.t @@ -140,13 +140,13 @@ is( is( $record->update(array('x' => 18), array('x' => 17)), 1, - "return affected rows", + "return affected rows" ); is( $record->update(array('x' => 18), array('x' => 17)), 0, - "return zero affected rows", + "return zero affected rows" ); $record->update(array('-x' => 'POW(2,2) * 10')); @@ -166,14 +166,14 @@ is ( $record->update(array('foo' => NULL)); is ( - $record->_set('foo' => ""), + $record->_set(array('foo' => "")), "SET `foo`=''", 'update: _set optimization with NULL => ""' ); $record->update(array('foo' => "bar")); $record->update(array('foo' => "")); is ( - $record->_set('foo' => NULL), + $record->_set(array('foo' => NULL)), "SET `foo`=NULL", 'update: _set optimization with "" => NULL' ); diff --git a/tests/it_html.t b/tests/it_html.t index 4c06d9d..4f4da24 100755 --- a/tests/it_html.t +++ b/tests/it_html.t @@ -52,40 +52,40 @@ is( ); is( - div('arg' => "val: \x03, \x0e, \x0f, \x0c, \xc2\x80, \xc2\x9f, \x09, \n", "\x02, \x0e, \x0f, \x0c, \xc2\x80, \xc2\x9f, \x09, \n"), + div(array('arg' => "val: \x03, \x0e, \x0f, \x0c, \xc2\x80, \xc2\x9f, \x09, \n", "\x02, \x0e, \x0f, \x0c, \xc2\x80, \xc2\x9f, \x09, \n")), "<div arg=\"val: , , , , , , \x09, \">\x02, \x0e, \x0f, \x0c, \xc2\x80, \xc2\x9f, \x09, \n</div>\n", "blank unprintable characters and illegal utf8 in attributes but not in normal text" ); is( - div("arg\x03\x0e\x0f\xc2\x80\xc2\x9fendarg" => "value", "content"), + div(array("arg\x03\x0e\x0f\xc2\x80\xc2\x9fendarg" => "value", "content")), "<div arg\x03\x0e\x0f\xc2\x80\xc2\x9fendarg=\"value\">content</div>\n", "don't blank unprintable characters and illegal utf8 in attribute names" ); is( - div('arg' => "abc äüö éá© œàè îôÇ xyz", "abc äüö éá© œàè îôÇ xyz"), + div(array('arg' => "abc äüö éá© œàè îôÇ xyz", "abc äüö éá© œàè îôÇ xyz")), "<div arg=\"abc äüö éá© œàè îôÇ xyz\">abc äüö éá© œàè îôÇ xyz</div>\n", - "leave legal utf8 intact", + "leave legal utf8 intact" ); unset($GLOBALS['debug_utf8check']); is( - div('arg' => "value \xc2", "content"), + div(array('arg' => "value \xc2", "content")), "<div arg=\"value \xc2\">content</div>\n", - "handle single \\xc2 at end of attribute value", + "handle single \\xc2 at end of attribute value" ); is( - div("arg\x00end" => "value \x00 end", "content \x00 content end"), + div(array("arg\x00end" => "value \x00 end", "content \x00 content end")), "<div arg\x00end=\"value end\">content \x00 content end</div>\n", - "handle 0-bytes", + "handle 0-bytes" ); is( - div('arg' => "& \" < > \n '", "& \" < > \n '"), + div(array('arg' => "& \" < > \n '", "& \" < > \n '")), "<div arg=\"& " < > '\">& \" < > \n '</div>\n", - "use html entities in attributes but not in normal text", + "use html entities in attributes but not in normal text" ); # Test different html types @@ -93,7 +93,7 @@ foreach (array('html5' => "<br flag>", 'html' => "<br flag>", 'xhtml' => "<br fl { unset($GLOBALS['it_html']); new it_html(array('htmltype' => $type)); - is (trim(br('flag' => true)), $value, "Check empty tag and attribute for $type"); + is (trim(br(array('flag' => true))), $value, "Check empty tag and attribute for $type"); } # XML generation @@ -180,7 +180,7 @@ is( is( it_html::sanitize('<div></div>'), '', - 'empty tags removal', + 'empty tags removal' ); is( diff --git a/tests/it_mail.t b/tests/it_mail.t index 1762788..486dee8 100755 --- a/tests/it_mail.t +++ b/tests/it_mail.t @@ -9,19 +9,25 @@ $mail = new it_mail(); is( $mail->addrlist_escape('éxample@example.com', true), 'éxample@example.com', - "Don't escape plain email addresses in email headers", + "Don't escape plain email addresses in email headers" ); is( $mail->header_escape('éxample@example.com'), '=?utf-8?Q?=C3=A9xample@example.com?=', - "Escape plain email in non-email headers", + "Escape plain email in non-email headers" +); + +is( + $mail->header_escape('search.ch e-mail code d\'accès'), + '=?utf-8?B?c2VhcmNoLmNoIGUtbWFpbCBjb2RlIGQnYWNjw6hz?=', + "Use base64 encoding when php iconv fails with quoted-printable (workaround for php bug #53891)" ); is( $mail->addrlist_escape('Èxample User <èxample@example.com>', true), - '=?utf-8?Q?=C3=88xample_User?= <èxample@example.com>', - "Escape name but not email in email headers", + '=?utf-8?Q?=C3=88xample=20User?= <èxample@example.com>', + "Escape name but not email in email headers" ); is( @@ -32,51 +38,56 @@ is( is( $mail->addrlist_escape('example@example.com, éxample@example.com, Sömeone Ëlse <sömeone@example.com>', true), - 'example@example.com, éxample@example.com,=?utf-8?Q?_S=C3=B6meone_=C3=8Blse?= <sömeone@example.com>', - "Don't escape email addresses but escape realnames", + 'example@example.com, éxample@example.com, =?utf-8?Q?S=C3=B6meone=20=C3=8Blse?= <sömeone@example.com>', + "Don't escape email addresses but escape realnames" ); is( $mail->addrlist_escape('"Alfred E. Neuman" <neuman@example.com>', true), '"Alfred E. Neuman" <neuman@example.com>', - "Don't remove quoting characters from realname", + "Don't remove quoting characters from realname" ); +is( + $mail->addrlist_escape('"Schmitt, Sören" <schmitt@example.com>', true), + '=?utf-8?Q?"Schmitt,=20S=C3=B6ren"?= <schmitt@example.com>', + "Don't remove quoting characters from realname when it contains a quotable character" +); -$mail = new it_mail( +$mail = new it_mail(array( 'From' => 'Someone Ïmportant <ïmportant@search.ch>', - 'To' => 'éxample@example.com, example@example.com, Sömeone Ëlse <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>', - 'Cc' => 'éxample@example.com, example@example.com, Sömeone Ëlse <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>', - 'Bcc' => 'éxample@example.com, example@example.com, Sömeone Ëlse <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>', - 'Subject' => "§önÐë®z€ı¢ħèṇ", -); + 'To' => 'éxample@example.com, example@example.com, Sömeone Ëlse <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>, "Schmitt, Sören" <schmitt@example.com>', + 'Cc' => 'éxample@example.com, example@example.com, Sömeone Ëlse <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>, "Schmitt, Sören" <schmitt@example.com>', + 'Bcc' => 'éxample@example.com, example@example.com, Sömeone Ëlse <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>, "Schmitt, Sören" <schmitt@example.com>', + 'Subject' => "§önÐë®z€ı¢ħèṇ" +)); is( $mail->to[0], - 'éxample@example.com, example@example.com,=?utf-8?Q?_S=C3=B6meone_=C3=8Blse?= <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>', - 'Escape To: field as addrlist', + 'éxample@example.com, example@example.com, =?utf-8?Q?S=C3=B6meone=20=C3=8Blse?= <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>, =?utf-8?Q?"Schmitt,=20S=C3=B6ren"?= <schmitt@example.com>', + 'Escape To: field as addrlist' ); is( $mail->cc[0], - 'éxample@example.com, example@example.com,=?utf-8?Q?_S=C3=B6meone_=C3=8Blse?= <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>', - 'Escape Cc: field as addrlist', + 'éxample@example.com, example@example.com, =?utf-8?Q?S=C3=B6meone=20=C3=8Blse?= <sömeone@example.com>, "Alfred E. Neuman" <neuman@example.com>, =?utf-8?Q?"Schmitt,=20S=C3=B6ren"?= <schmitt@example.com>', + 'Escape Cc: field as addrlist' ); is( $mail->bcc[0], |