From 1d1fe8fee4ec868d9ce5f90973b722c9c2515428 Mon Sep 17 00:00:00 2001 From: ikkez Date: Sun, 31 Dec 2017 15:47:25 +0100 Subject: [PATCH] 3.6.3-Release --- lib/CHANGELOG.md | 32 ++++++++ lib/auth.php | 15 +++- lib/base.php | 167 ++++++++++++++++++++++++++++------------- lib/db/sql.php | 19 ++--- lib/db/sql/mapper.php | 55 ++++++++------ lib/db/sql/session.php | 10 ++- lib/session.php | 2 +- lib/smtp.php | 12 +-- lib/template.php | 4 +- lib/web.php | 2 + 10 files changed, 212 insertions(+), 106 deletions(-) diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index ad5f82042..d7c078836 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,37 @@ CHANGELOG +3.6.3 (31 December 2017) +* PHP7 fix: remove deprecated (unset) cast +* Web->request: restricted follow_location to 3XX responses only +* CLI mode: refactored arguments parsing +* CLI mode: fixed query string encoding +* SMTP: Refactor parsing of attachments +* SMTP: clean-up mail headers for multipart messages, [#1065](https://github.com/bcosca/fatfree/issues/1065) +* config: fixed performance issues on parsing config files +* config: cast command parameters in config entries to php type & constant, [#1030](https://github.com/bcosca/fatfree/issues/1030) +* config: reduced registry calls +* config: skip hive escaping when resolving dynamic config vars, [#1030](https://github.com/bcosca/fatfree/issues/1030) +* Bug fix: Incorrect cookie lifetime computation, [#1070](https://github.com/bcosca/fatfree/issues/1070), [#1016](https://github.com/bcosca/fatfree/issues/1016) +* DB\SQL\Mapper: use RETURNING option instead of a sequence query to get lastInsertId in PostgreSQL, [#1069](https://github.com/bcosca/fatfree/issues/1069), [#230](https://github.com/bcosca/fatfree-core/issues/230) +* DB\SQL\Session: check if _agent is too long for SQL based sessions [#236](https://github.com/bcosca/fatfree-core/issues/236) +* DB\SQL\Session: fix Session handler table creation issue on SQL Server, [#899](https://github.com/bcosca/fatfree/issues/899) +* DB\SQL: fix oracle db issue with empty error variable, [#1072](https://github.com/bcosca/fatfree/issues/1072) +* DB\SQL\Mapper: fix sorting issues on SQL Server, [#1052](https://github.com/bcosca/fatfree/issues/1052) [#225](https://github.com/bcosca/fatfree-core/issues/225) +* Prevent directory traversal attacks on filesystem based cache [#1073](https://github.com/bcosca/fatfree/issues/1073) +* Bug fix, Template: PHP constants used in include with attribute, [#983](https://github.com/bcosca/fatfree/issues/983) +* Bug fix, Template: Numeric value in expression alters PHP_EOL context +* Template: use existing linefeed instead of PHP_EOL, [#1048](https://github.com/bcosca/fatfree/issues/1048) +* Template: make newline interpolation handling configurable [#223](https://github.com/bcosca/fatfree-core/issues/223) +* Template: add beforerender to Preview +* fix custom FORMATS without modifiers +* Cache: Refactor Cache->reset for XCache +* Cache: loosen reset cache key pattern, [#1041](https://github.com/bcosca/fatfree/issues/1041) +* XCache: suffix reset only works if xcache.admin.enable_auth is disabled +* Added HTTP 103 as recently approved by the IETF +* LDAP changes to for AD flexibility [#227](https://github.com/bcosca/fatfree-core/issues/227) +* Hide debug trace from ajax errors when DEBUG=0 [#1071](https://github.com/bcosca/fatfree/issues/1071) +* fix View->render using potentially wrong cache entry + 3.6.2 (26 June 2017) * Return a status code > 0 when dying on error [#220](https://github.com/bcosca/fatfree-core/issues/220) * fix SMTP line width [#215](https://github.com/bcosca/fatfree-core/issues/215) diff --git a/lib/auth.php b/lib/auth.php index a5fe6d26a..c1e8a570b 100644 --- a/lib/auth.php +++ b/lib/auth.php @@ -115,18 +115,27 @@ protected function _sql($id,$pw,$realm) { * @param $pw string **/ protected function _ldap($id,$pw) { - $dc=@ldap_connect($this->args['dc']); + $port=(int)($this->args['port']?:389); + $filter=$this->args['filter']=$this->args['filter']?:"uid=".$id; + $this->args['attr']=$this->args['attr']?:["uid"]; + array_walk($this->args['attr'], + function($attr)use(&$filter,$id) { + $filter=str_ireplace($attr."=*",$attr."=".$id,$filter);}); + $dc=@ldap_connect($this->args['dc'],$port); if ($dc && ldap_set_option($dc,LDAP_OPT_PROTOCOL_VERSION,3) && ldap_set_option($dc,LDAP_OPT_REFERRALS,0) && ldap_bind($dc,$this->args['rdn'],$this->args['pw']) && ($result=ldap_search($dc,$this->args['base_dn'], - $this->args['uid'].'='.$id)) && + $filter,$this->args['attr'])) && ldap_count_entries($dc,$result) && ($info=ldap_get_entries($dc,$result)) && + $info['count']==1 && @ldap_bind($dc,$info[0]['dn'],$pw) && @ldap_close($dc)) { - return $info[0][$this->args['uid']][0]==$id; + return in_array($id,(array_map(function($value){return $value[0];}, + array_intersect_key($info[0], + array_flip($this->args['attr'])))),TRUE); } user_error(self::E_LDAP,E_USER_ERROR); } diff --git a/lib/base.php b/lib/base.php index ecde6f46f..62fa14fb2 100644 --- a/lib/base.php +++ b/lib/base.php @@ -45,13 +45,14 @@ final class Base extends Prefab implements ArrayAccess { //@{ Framework details const PACKAGE='Fat-Free Framework', - VERSION='3.6.2-Release'; + VERSION='3.6.3-Release'; //@} //@{ HTTP status codes (RFC 2616) const HTTP_100='Continue', HTTP_101='Switching Protocols', + HTTP_103='Early Hints', HTTP_200='OK', HTTP_201='Created', HTTP_202='Accepted', @@ -205,6 +206,20 @@ function parse($str) { return $out; } + /** + * cast string variable to php type or constant + * @param $val + * @return mixed + */ + function cast($val) { + if (is_numeric($val)) + return $val+0; + $val=trim($val); + if (preg_match('/^\w+$/i',$val) && defined($val)) + return constant($val); + return $val; + } + /** * Convert JS-style token to PHP expression * @return string @@ -331,14 +346,14 @@ function devoid($key,&$val=NULL) { * @param $ttl int **/ function set($key,$val,$ttl=0) { - $time=time(); + $time=$this->hive['TIME']; if (preg_match('/^(GET|POST|COOKIE)\b(.+)/',$key,$expr)) { $this->set('REQUEST'.$expr[2],$val); if ($expr[1]=='COOKIE') { $parts=$this->cut($key); $jar=$this->unserialize($this->serialize($this->hive['JAR'])); if (isset($_COOKIE[$parts[1]])) { - $jar['expire']=strtotime('-1 year'); + $jar['expire']=0; call_user_func_array('setcookie', array_merge([$parts[1],NULL],$jar)); } @@ -427,7 +442,7 @@ function clear($key) { if ($expr[1]=='COOKIE') { $parts=$this->cut($key); $jar=$this->hive['JAR']; - $jar['expire']=strtotime('-1 year'); + $jar['expire']=0; call_user_func_array('setcookie', array_merge([$parts[1],NULL],$jar)); unset($_COOKIE[$parts[1]]); @@ -868,7 +883,7 @@ function($expr) use($args,$conv) { if (isset($type)) { if (isset($this->hive['FORMATS'][$type])) return $this->call($this->hive['FORMATS'][$type], - [$args[$pos],$mod,isset($prop)?$prop:null]); + [$args[$pos],isset($mod)?$mod:null,isset($prop)?$prop:null]); switch ($type) { case 'plural': preg_match_all('/(?\w+)'. @@ -1240,7 +1255,7 @@ function error($code,$text='',array $trace=NULL,$level=0) { 'beforeroute,afterroute')===FALSE) && !$prior && !$this->hive['CLI'] && !$this->hive['QUIET']) echo $this->hive['AJAX']? - json_encode($this->hive['ERROR']): + json_encode(array_diff_key($this->hive['ERROR'],$this->hive['DEBUG']?[]:['trace'=>1])): (''.$eol. ''.$eol. ''. @@ -1836,6 +1851,8 @@ function relay($funcs,$args=NULL) { function config($source,$allow=FALSE) { if (is_string($source)) $source=$this->split($source); + if ($allow) + $preview=Preview::instance(); foreach ($source as $file) { preg_match_all( '/(?<=^|\n)(?:'. @@ -1860,12 +1877,10 @@ function config($source,$allow=FALSE) { '^((?:\.?\w)+)\s*\>\s*(.*)/i',$sec,$cmd); continue; } - if ($allow) { - $match['lval']=Preview::instance()-> - resolve($match['lval']); - $match['rval']=Preview::instance()-> - resolve($match['rval']); - } + if ($allow) + foreach (['lval','rval'] as $ndx) + $match[$ndx]=$preview-> + resolve($match[$ndx],NULL,0,FALSE,FALSE); if (!empty($cmd)) { isset($cmd[3])? $this->call($cmd[3], @@ -1873,7 +1888,7 @@ function config($source,$allow=FALSE) { call_user_func_array( [$this,$cmd[1]], array_merge([$match['lval']], - str_getcsv($match['rval'])) + str_getcsv($this->cast($match['rval']))) ); } else { @@ -1886,13 +1901,10 @@ function config($source,$allow=FALSE) { } $args=array_map( function($val) { - if (is_numeric($val)) - return $val+0; - $val=trim($val); - if (preg_match('/^\w+$/i',$val) && - defined($val)) - return constant($val); - return preg_replace('/\\\\"/','"',$val); + $val=$this->cast($val); + return is_string($val) + ? preg_replace('/\\\\"/','"',$val) + : $val; }, // Mark quoted strings with 0x00 whitespace str_getcsv(preg_replace( @@ -2187,23 +2199,28 @@ function($level,$text,$file,$line) { $_SERVER['argc']++; $_SERVER['argv'][1]='/'; } - if (substr($_SERVER['argv'][1],0,1)=='/') - $_SERVER['REQUEST_URI']=$_SERVER['argv'][1]; - else { - $req=$opts=''; + $req=$query=''; + if (substr($_SERVER['argv'][1],0,1)=='/') { + $req=$_SERVER['argv'][1]; + $query=parse_url($req,PHP_URL_QUERY); + } else { foreach($_SERVER['argv'] as $i=>$arg) { if (!$i) continue; if (preg_match('/^\-(\-)?(\w+)(?:\=(.*))?$/',$arg,$m)) { foreach($m[1]?[$m[2]]:str_split($m[2]) as $k) - $opts.=($opts?'&':'').$k.'='; + $query.=($query?'&':'').urlencode($k).'='; if (isset($m[3])) - $opts.=$m[3]; + $query.=urlencode($m[3]); } else $req.='/'.$arg; } - $_SERVER['REQUEST_URI']=($req?:'/').'?'.urlencode($opts); - parse_str($opts,$GLOBALS['_GET']); + if (!$req) + $req='/'; + if ($query) + $req.='?'.$query; } + $_SERVER['REQUEST_URI']=$req; + parse_str($query,$GLOBALS['_GET']); } $headers=[]; if (!$cli) { @@ -2255,17 +2272,16 @@ function($level,$text,$file,$line) { (isset($uri['fragment'])?'#'.$uri['fragment']:''); $path=preg_replace('/^'.preg_quote($base,'/').'/','',$uri['path']); session_cache_limiter(''); - call_user_func_array('session_set_cookie_params', - $jar=[ - 'expire'=>0, - 'path'=>$base?:'/', - 'domain'=>is_int(strpos($_SERVER['SERVER_NAME'],'.')) && - !filter_var($_SERVER['SERVER_NAME'],FILTER_VALIDATE_IP)? - $_SERVER['SERVER_NAME']:'', - 'secure'=>($scheme=='https'), - 'httponly'=>TRUE - ] - ); + $jar=[ + 'expire'=>0, + 'path'=>$base?:'/', + 'domain'=>is_int(strpos($_SERVER['SERVER_NAME'],'.')) && + !filter_var($_SERVER['SERVER_NAME'],FILTER_VALIDATE_IP)? + $_SERVER['SERVER_NAME']:'', + 'secure'=>($scheme=='https'), + 'httponly'=>TRUE + ]; + call_user_func_array('session_set_cookie_params',$jar); $port=80; if (isset($headers['X-Forwarded-Port'])) $port=$headers['X-Forwarded-Port']; @@ -2463,7 +2479,7 @@ function set($key,$val,$ttl=0) { case 'xcache': return xcache_set($ndx,$data,$ttl); case 'folder': - return $fw->write($parts[1].$ndx,$data); + return $fw->write($parts[1].str_replace(['/','\\'],'',$ndx),$data); } return FALSE; } @@ -2515,7 +2531,7 @@ function clear($key) { function reset($suffix=NULL) { if (!$this->dsn) return TRUE; - $regex='/'.preg_quote($this->prefix.'.','/').'.+'. + $regex='/'.preg_quote($this->prefix.'.','/').'.*'. preg_quote($suffix,'/').'/'; $parts=explode('=',$this->dsn,2); switch ($parts[0]) { @@ -2560,7 +2576,16 @@ function reset($suffix=NULL) { wincache_ucache_delete($item['key_name']); return TRUE; case 'xcache': - xcache_unset_by_prefix($this->prefix.'.'); + if ($suffix && !ini_get('xcache.admin.enable_auth')) { + $cnt=xcache_count(XC_TYPE_VAR); + for ($i=0;$i<$cnt;$i++) { + $list=xcache_list(XC_TYPE_VAR,$i); + foreach ($list['cache_list'] as $item) + if (preg_match($regex,$item['name'])) + xcache_unset($item['name']); + } + } else + xcache_unset_by_prefix($this->prefix.'.'); return TRUE; case 'folder': if ($glob=@glob($parts[1].'*')) @@ -2721,18 +2746,18 @@ protected function sandbox(array $hive=NULL,$mime=NULL) { function render($file,$mime='text/html',array $hive=NULL,$ttl=0) { $fw=Base::instance(); $cache=Cache::instance(); - if ($cache->exists($hash=$fw->hash($file),$data)) - return $data; foreach ($fw->split($fw->UI) as $dir) + if ($cache->exists($hash=$fw->hash($dir.$file),$data)) + return $data; if (is_file($this->file=$fw->fixslashes($dir.$file))) { if (isset($_COOKIE[session_name()]) && !headers_sent() && session_status()!=PHP_SESSION_ACTIVE) session_start(); $fw->sync('SESSION'); $data=$this->sandbox($hive,$mime); - if(isset($this->trigger['afterrender'])) + if (isset($this->trigger['afterrender'])) foreach($this->trigger['afterrender'] as $func) - $data=$fw->call($func,$data); + $data=$fw->call($func,[$data, $dir.$file]); if ($ttl) $cache->set($hash,$data,$ttl); return $data; @@ -2763,6 +2788,19 @@ class Preview extends View { 'format'=>'Base::instance()->format' ]; + protected + //! newline interpolation + $interpolation=true; + + /** + * enable/disable markup parsing interpolation + * mainly used for adding appropriate newlines + * @param $bool bool + */ + function interpolation($bool) { + $this->interpolation=$bool; + } + /** * Return C-locale equivalent of number * @return string @@ -2783,12 +2821,13 @@ function c($val) { * @param $str string **/ function token($str) { + $fw = Base::instance(); $str=trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'), - Base::instance()->compile($str))); + $fw->compile($str))); if (preg_match('/^(.+)(?split($parts[2]) as $func) + foreach ($fw->split($parts[2]) as $func) $str=is_string($cmd=$this->filter($func))? $cmd.'('.$str.')': 'Base::instance()->'. @@ -2820,7 +2859,7 @@ function filter($key=NULL,$func=NULL) { protected function build($node) { return preg_replace_callback( '/\{~(.+?)~\}|\{\*(.+?)\*\}|\{\-(.+?)\-\}|'. - '\{\{(.+?)\}\}((?:\r?\n)*)/s', + '\{\{(.+?)\}\}((\r?\n)*)/s', function($expr) { if ($expr[1]) $str='token($expr[1]).' ?>'; @@ -2829,8 +2868,9 @@ function($expr) { elseif ($expr[3]) $str=$expr[3]; else { - $str='token($expr[4])). - (!empty($expr[5])?'.PHP_EOL':'').' ?>'; + $str='token($expr[4])).')'. + ($this->interpolation? + (!empty($expr[6])?'."'.$expr[6].'"':''):'').' ?>'; if (isset($expr[5])) $str.=$expr[5]; } @@ -2847,10 +2887,15 @@ function($expr) { * @param $hive array * @param $ttl int * @param $persist bool + * @param $escape bool **/ - function resolve($node,array $hive=NULL,$ttl=0,$persist=FALSE) { + function resolve($node,array $hive=NULL,$ttl=0,$persist=FALSE,$escape=NULL) { $fw=Base::instance(); $cache=Cache::instance(); + if ($escape!==NULL) { + $esc=$fw->ESCAPE; + $fw->ESCAPE=$escape; + } if ($ttl || $persist) $hash=$fw->hash($fw->serialize($node)); if ($ttl && $cache->exists($hash,$data)) @@ -2880,6 +2925,8 @@ function resolve($node,array $hive=NULL,$ttl=0,$persist=FALSE) { } if ($ttl) $cache->set($hash,$data,$ttl); + if ($escape!==NULL) + $fw->ESCAPE=$esc; return $data; } @@ -2915,7 +2962,11 @@ function render($file,$mime='text/html',array $hive=NULL,$ttl=0) { if (!is_file($this->file=($tmp. $fw->SEED.'.'.$fw->hash($view).'.php')) || filemtime($this->file)parse($fw->read($view)); + $contents=$fw->read($view); + if (isset($this->trigger['beforerender'])) + foreach ($this->trigger['beforerender'] as $func) + $contents=$fw->call($func, [$contents, $view]); + $text=$this->parse($contents); $fw->write($this->file,$this->build($text)); } if (isset($_COOKIE[session_name()]) && @@ -2925,7 +2976,7 @@ function render($file,$mime='text/html',array $hive=NULL,$ttl=0) { $data=$this->sandbox($hive,$mime); if(isset($this->trigger['afterrender'])) foreach ($this->trigger['afterrender'] as $func) - $data=$fw->call($func, $data); + $data=$fw->call($func, [$data, $view]); if ($ttl) $cache->set($hash,$data,$ttl); return $data; @@ -2934,6 +2985,14 @@ function render($file,$mime='text/html',array $hive=NULL,$ttl=0) { user_error(sprintf(Base::E_Open,$file),E_USER_ERROR); } + /** + * post rendering handler + * @param $func callback + */ + function beforerender($func) { + $this->trigger['beforerender'][]=$func; + } + } //! ISO language/country codes diff --git a/lib/db/sql.php b/lib/db/sql.php index 586f05ece..39e77f0cf 100644 --- a/lib/db/sql.php +++ b/lib/db/sql.php @@ -124,7 +124,7 @@ function value($type,$val) { $val=str_replace(',','.',$val); return $val; case \PDO::PARAM_NULL: - return (unset)$val; + return NULL; case \PDO::PARAM_INT: return (int)$val; case \PDO::PARAM_BOOL: @@ -228,8 +228,7 @@ function exec($cmds,$args=NULL,$ttl=0,$log=TRUE,$stamp=FALSE) { $this->log=str_replace('(-0ms)', '('.sprintf('%.1f',1e3*(microtime(TRUE)-$now)).'ms)', $this->log); - $error=$query->errorinfo(); - if ($error[0]!=\PDO::ERR_NONE) { + if (($error=$query->errorinfo()) && $error[0]!=\PDO::ERR_NONE) { // Statement-level error occurred if ($this->trans) $this->rollback(); @@ -258,15 +257,13 @@ function exec($cmds,$args=NULL,$ttl=0,$log=TRUE,$stamp=FALSE) { $query->closecursor(); unset($query); } - else { - $error=$this->errorinfo(); - if ($error[0]!=\PDO::ERR_NONE) { - // PDO-level error occurred - if ($this->trans) - $this->rollback(); - user_error('PDO: '.$error[2],E_USER_ERROR); - } + elseif (($error=$this->errorinfo()) && $error[0]!=\PDO::ERR_NONE) { + // PDO-level error occurred + if ($this->trans) + $this->rollback(); + user_error('PDO: '.$error[2],E_USER_ERROR); } + } if ($this->trans && $auto) $this->commit(); diff --git a/lib/db/sql/mapper.php b/lib/db/sql/mapper.php index 09a3b897e..f78fc36c6 100644 --- a/lib/db/sql/mapper.php +++ b/lib/db/sql/mapper.php @@ -236,7 +236,7 @@ function($parts) use($db) { explode(',',$options['group']))); } if ($options['order']) { - $sql.=' ORDER BY '.implode(',',array_map( + $order=' ORDER BY '.implode(',',array_map( function($str) use($db) { return preg_match('/^\h*(\w+[._\-\w]*)(?:\h+((?:ASC|DESC)[\w\h]*))?\h*$/i', $str,$parts)? @@ -245,33 +245,36 @@ function($str) use($db) { }, explode(',',$options['order']))); } + // SQL Server fixes if (preg_match('/mssql|sqlsrv|odbc/', $this->engine) && ($options['limit'] || $options['offset'])) { - $pkeys=[]; - foreach ($this->fields as $key=>$field) - if ($field['pkey']) - $pkeys[]=$key; + // order by pkey when no ordering option was given + if (!$options['order']) + foreach ($this->fields as $key=>$field) + if ($field['pkey']) { + $order=' ORDER BY '.$db->quotekey($key); + break; + } $ofs=$options['offset']?(int)$options['offset']:0; $lmt=$options['limit']?(int)$options['limit']:0; if (strncmp($db->version(),'11',2)>=0) { - // SQL Server 2012 - if (!$options['order']) - $sql.=' ORDER BY '.$db->quotekey($pkeys[0]); - $sql.=' OFFSET '.$ofs.' ROWS'; + // SQL Server >= 2012 + $sql.=$order.' OFFSET '.$ofs.' ROWS'; if ($lmt) $sql.=' FETCH NEXT '.$lmt.' ROWS ONLY'; } else { // SQL Server 2008 - $sql=str_replace('SELECT', + $sql=preg_replace('/SELECT/', 'SELECT '. ($lmt>0?'TOP '.($ofs+$lmt):'').' ROW_NUMBER() '. - 'OVER (ORDER BY '. - $db->quotekey($pkeys[0]).') AS rnum,',$sql); + 'OVER ('.$order.') AS rnum,',$sql.$order,1); $sql='SELECT * FROM ('.$sql.') x WHERE rnum > '.($ofs); } } else { + if (isset($order)) + $sql.=$order; if ($options['limit']) $sql.=' LIMIT '.(int)$options['limit']; if ($options['offset']) @@ -344,7 +347,10 @@ function count($filter=NULL,array $options=NULL,$ttl=0) { $adhoc=''; foreach ($this->adhoc as $key=>$field) $adhoc.=','.$field['expr'].' AS '.$this->db->quotekey($key); - list($sql,$args)=$this->stringify('*'.$adhoc,$filter,$options); + $fields='*'.$adhoc; + if (preg_match('/mssql|dblib|sqlsrv/',$this->engine)) + $fields='TOP 100 PERCENT '.$fields; + list($sql,$args)=$this->stringify($fields,$filter,$options); $sql='SELECT COUNT(*) AS '.$this->db->quotekey('_rows').' '. 'FROM ('.$sql.') AS '.$this->db->quotekey('_temp'); $result=$this->db->exec($sql,$args,$ttl); @@ -418,22 +424,23 @@ function insert() { } } if ($fields) { - $this->db->exec( + $add=''; + if ($this->engine=='pgsql') { + $names=array_keys($pkeys); + $aik=end($names); + $add=' RETURNING '.$this->db->quotekey($aik); + } + $lID=$this->db->exec( (preg_match('/mssql|dblib|sqlsrv/',$this->engine) && array_intersect(array_keys($pkeys),$ckeys)? 'SET IDENTITY_INSERT '.$this->table.' ON;':''). 'INSERT INTO '.$this->table.' ('.$fields.') '. - 'VALUES ('.$values.')',$args + 'VALUES ('.$values.')'.$add,$args ); - $seq=NULL; - if ($this->engine=='pgsql') { - $names=array_keys($pkeys); - $aik=end($names); - if ($this->fields[$aik]['pdo_type']==\PDO::PARAM_INT) - $seq=$this->source.'_'.$aik.'_seq'; - } - if ($this->engine!='oci' && !($this->engine=='pgsql' && !$seq)) - $this->_id=$this->db->lastinsertid($seq); + if ($this->engine=='pgsql' && $lID) + $this->_id=$lID[0][$aik]; + elseif ($this->engine!='oci') + $this->_id=$this->db->lastinsertid(); // Reload to obtain default and auto-increment field values if ($reload=($inc || $filter)) $this->load($inc? diff --git a/lib/db/sql/session.php b/lib/db/sql/session.php index fdd3f388f..799cd0ffb 100644 --- a/lib/db/sql/session.php +++ b/lib/db/sql/session.php @@ -170,8 +170,9 @@ function __construct(\DB\SQL $db,$table='sessions',$force=TRUE,$onsuspect=NULL,$ if ($force) { $eol="\n"; $tab="\t"; + $sqlsrv=preg_match('/mssql|sqlsrv|sybase/',$db->driver()); $db->exec( - (preg_match('/mssql|sqlsrv|sybase/',$db->driver())? + ($sqlsrv? ('IF NOT EXISTS (SELECT * FROM sysobjects WHERE '. 'name='.$db->quote($table).' AND xtype=\'U\') '. 'CREATE TABLE dbo.'): @@ -179,12 +180,14 @@ function __construct(\DB\SQL $db,$table='sessions',$force=TRUE,$onsuspect=NULL,$ ((($name=$db->name())&&$db->driver()!='pgsql')? ($db->quotekey($name,FALSE).'.'):''))). $db->quotekey($table,FALSE).' ('.$eol. + ($sqlsrv?$tab.$db->quotekey('id').' INT IDENTITY,'.$eol:''). $tab.$db->quotekey('session_id').' VARCHAR(255),'.$eol. $tab.$db->quotekey('data').' TEXT,'.$eol. $tab.$db->quotekey('ip').' VARCHAR(45),'.$eol. $tab.$db->quotekey('agent').' VARCHAR(300),'.$eol. $tab.$db->quotekey('stamp').' INTEGER,'.$eol. - $tab.'PRIMARY KEY ('.$db->quotekey('session_id').')'.$eol. + $tab.'PRIMARY KEY ('.$db->quotekey($sqlsrv?'id':'session_id').')'.$eol. + ($sqlsrv?',CONSTRAINT [UK_session_id] UNIQUE(session_id)':''). ');' ); } @@ -205,6 +208,9 @@ function __construct(\DB\SQL $db,$table='sessions',$force=TRUE,$onsuspect=NULL,$ if ($key) $fw->$key=$this->_csrf; $this->_agent=isset($headers['User-Agent'])?$headers['User-Agent']:''; + if (strlen($this->_agent) > 300) { + $this->_agent = substr($this->_agent, 0, 300); + } $this->_ip=$fw->IP; } diff --git a/lib/session.php b/lib/session.php index 02f52a06a..3434f8d21 100644 --- a/lib/session.php +++ b/lib/session.php @@ -95,7 +95,7 @@ function write($id,$data) { 'agent'=>$this->_agent, 'stamp'=>time() ], - $jar['expire']?($jar['expire']-time()):0 + $jar['expire'] ); return TRUE; } diff --git a/lib/smtp.php b/lib/smtp.php index 52156b104..6a8e53338 100644 --- a/lib/smtp.php +++ b/lib/smtp.php @@ -171,7 +171,7 @@ function attach($file,$alias=NULL,$cid=NULL) { if (!is_file($file)) user_error(sprintf(self::E_Attach,$file),E_USER_ERROR); if ($alias) - $file=[$alias=>$file]; + $file=[$alias,$file]; $this->attachments[]=['filename'=>$file,'cid'=>$cid]; } @@ -244,14 +244,8 @@ function send($message,$log=TRUE,$mock=FALSE) { if (empty($headers[$id])) user_error(sprintf(self::E_Header,$id),E_USER_ERROR); $eol="\r\n"; - $str=''; // Stringify headers foreach ($headers as $key=>&$val) { - if (!in_array($key,$reqd) && - (!$this->attachments || - $key!='Content-Type' && - $key!='Content-Transfer-Encoding')) - $str.=$key.': '.$val.$eol; if (in_array($key,['From','To','Cc','Bcc'])) { $email=''; preg_match_all('/(?:".+?" )?(?:<.+?>|[^ ,]+)/', @@ -291,11 +285,11 @@ function send($message,$log=TRUE,$mock=FALSE) { $out.='--'.$hash.$eol; $out.='Content-Type: '.$type.$eol; $out.='Content-Transfer-Encoding: '.$enc.$eol; - $out.=$str.$eol; + $out.=$eol; $out.=$message.$eol; foreach ($this->attachments as $attachment) { if (is_array($attachment['filename'])) - list($alias,$file)=each($attachment['filename']); + list($alias,$file)=$attachment['filename']; else $alias=basename($file=$attachment['filename']); $out.='--'.$hash.$eol; diff --git a/lib/template.php b/lib/template.php index 769c1873b..5d4c74b03 100644 --- a/lib/template.php +++ b/lib/template.php @@ -65,8 +65,8 @@ protected function _include(array $node) { return '\''.$pair[1].'\'=>'. (preg_match('/^\'.*\'$/',$pair[2]) || preg_match('/\$/',$pair[2])? - $pair[2]: - \Base::instance()->stringify($pair[2])); + $pair[2]:Base::instance()->stringify( + Base::instance()->cast($pair[2]))); },$pairs)).']+get_defined_vars()'): 'get_defined_vars()'; $ttl=isset($attrib['ttl'])?(int)$attrib['ttl']:0; diff --git a/lib/web.php b/lib/web.php index 9ca16628c..dd817ce24 100644 --- a/lib/web.php +++ b/lib/web.php @@ -305,6 +305,7 @@ function($curl,$line) use(&$headers) { $body=ob_get_clean(); if (!$err && $options['follow_location'] && + preg_grep('/HTTP\/1\.\d 3\d{2}/',$headers) && preg_match('/^Location: (.+)$/m',implode(PHP_EOL,$headers),$loc)) { $options['max_redirects']--; if($loc[1][0] == '/') { @@ -424,6 +425,7 @@ protected function _socket($url,$options) { break; } if ($options['follow_location'] && + preg_grep('/HTTP\/1\.\d 3\d{2}/',$headers) && preg_match('/Location: (.+?)'.preg_quote($eol).'/', $html[0],$loc)) { $options['max_redirects']--;