diff --git a/lib/adodb/adodb-cryptsession.php b/lib/adodb/adodb-cryptsession.php
new file mode 100644
index 00000000000..9aec4a26a16
--- /dev/null
+++ b/lib/adodb/adodb-cryptsession.php
@@ -0,0 +1,245 @@
+
+ Set tabs to 4 for best viewing.
+
+ Latest version of ADODB is available at http://php.weblogs.com/adodb
+ ======================================================================
+
+ This file provides PHP4 session management using the ADODB database
+wrapper library.
+
+ Example
+ =======
+
+ GLOBAL $HTTP_SESSION_VARS;
+ include('adodb.inc.php');
+ include('adodb-session.php');
+ session_start();
+ session_register('AVAR');
+ $HTTP_SESSION_VARS['AVAR'] += 1;
+ print "
";
+}
+
+?>
diff --git a/lib/adodb/adodb.inc.php b/lib/adodb/adodb.inc.php
new file mode 100644
index 00000000000..a33969a5fc8
--- /dev/null
+++ b/lib/adodb/adodb.inc.php
@@ -0,0 +1,2369 @@
+Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;');
+
+ define('ADODB_FETCH_DEFAULT',0);
+ define('ADODB_FETCH_NUM',1);
+ define('ADODB_FETCH_ASSOC',2);
+ define('ADODB_FETCH_BOTH',3);
+
+ // allow [ ] @ and . in table names
+ define('ADODB_TABLE_REGEX','([]0-9a-z_\.\@\[-]*)');
+ if (!defined('MAX_BLOB_SIZE')) define('MAX_BLOB_SIZE',999999); // 900K
+
+ if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
+
+ /**
+ * Set ADODB_DIR to the directory where this file resides...
+ * This constant was formerly called $ADODB_RootPath
+ */
+ if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
+
+ //==============================================================================================
+ // GLOBAL VARIABLES
+ //==============================================================================================
+
+ GLOBAL
+ $ADODB_vers, // database version
+ $ADODB_Database, // last database driver used
+ $ADODB_COUNTRECS, // count number of records returned - slows down query
+ $ADODB_CACHE_DIR, // directory to cache recordsets
+ $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
+
+ //==============================================================================================
+ // GLOBAL SETUP
+ //==============================================================================================
+
+ $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
+
+ if (!isset($ADODB_CACHE_DIR)) {
+ $ADODB_CACHE_DIR = '/tmp';
+ } else {
+ // do not accept url based paths, eg. http:/ or ftp:/
+ if (strpos($ADODB_CACHE_DIR,'://') !== false)
+ die("Illegal path http:// or ftp://");
+ }
+
+ //==============================================================================================
+ // CHANGE NOTHING BELOW UNLESS YOU ARE CODING
+ //==============================================================================================
+
+
+ // Initialize random number generator for randomizing cache flushes
+ srand(((double)microtime())*1000000);
+
+ /**
+ * Name of last database driver loaded into memory. Set by ADOLoadCode().
+ */
+ $ADODB_Database = '';
+
+ /**
+ * ADODB version as a string.
+ */
+ $ADODB_vers = 'V2.00 13 May 2002 (c) 2000-2002 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.';
+
+ /**
+ * Determines whether recordset->RecordCount() is used.
+ * Set to false for highest performance -- RecordCount() will always return -1 then
+ * for databases that provide "virtual" recordcounts...
+ */
+ $ADODB_COUNTRECS = true;
+
+ //==============================================================================================
+ // CLASS ADOFieldObject
+ //==============================================================================================
+
+ /**
+ * Helper class for FetchFields -- holds info on a column
+ */
+ class ADOFieldObject {
+ var $name = '';
+ var $max_length=0;
+ var $type="";
+
+ // additional fields by dannym... (danny_milo@yahoo.com)
+ var $not_null = false;
+ // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
+ // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
+
+ var $has_default = false; // this one I have done only in mysql and postgres for now ...
+ // others to come (dannym)
+ var $default_value; // default, if any, and supported. Check has_default first.
+ }
+
+
+ //==============================================================================================
+ // CLASS ADOConnection
+ //==============================================================================================
+
+ /**
+ * Connection object. For connecting to databases, and executing queries.
+ */
+ class ADOConnection {
+ /*
+ * PUBLIC VARS
+ */
+ var $dataProvider = 'native';
+ var $databaseType = ''; // RDBMS currently in use, eg. odbc, mysql, mssql
+ var $database = ''; // Name of database to be used.
+ var $host = ''; // The hostname of the database server
+ var $user = ''; // The username which is used to connect to the database server.
+ var $password = ''; // Password for the username
+ var $debug = false; // if set to true will output sql statements
+ var $maxblobsize = 8000; // maximum size of blobs or large text fields -- some databases die otherwise like foxpro
+ var $concat_operator = '+'; // default concat operator -- change to || for Oracle/Interbase
+ var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database
+ var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; // used by DBTimeStamp as the default timestamp fmt.
+ var $true = '1'; // string that represents TRUE for a database
+ var $false = '0'; // string that represents FALSE for a database
+ var $replaceQuote = "\\'"; // string to use to replace quotes
+ var $hasInsertID = false; // supports autoincrement ID?
+ var $hasAffectedRows = false; // supports affected rows for update/delete?
+ var $charSet=false; // character set to use - only for interbase
+ var $metaTablesSQL = '';
+ var $hasTop = false; // support mssql/access SELECT TOP 10 * FROM TABLE
+ var $hasLimit = false; // support pgsql/mysql SELECT * FROM TABLE LIMIT 10
+ var $readOnly = false; // this is a readonly database - used by phpLens
+ var $hasMoveFirst = false; // has ability to run MoveFirst(), scrolling backwards
+ var $hasGenID = false; // can generate sequences using GenID();
+ var $genID = 0; // sequence id used by GenID();
+ var $raiseErrorFn = false; // error function to call
+ var $upperCase = false; // uppercase function to call for searching/where
+ var $isoDates = false; // accepts dates in ISO format
+ var $cacheSecs = 3600; // cache for 1 hour
+ var $sysDate = false; // name of function that returns the current date
+ var $sysTimeStamp = false; // name of function that returns the current timestamp
+ var $arrayClass = 'ADORecordSet_array';
+ // oracle specific stuff
+ var $noNullStrings = false;
+
+ /*
+ * PRIVATE VARS
+ */
+ var $_connectionID = false; // The returned link identifier whenever a successful database connection is made. */
+
+ var $_errorMsg = ''; // A variable which was used to keep the returned last error message. The value will
+ //then returned by the errorMsg() function
+
+ var $_queryID = false; // This variable keeps the last created result link identifier. */
+
+ var $_isPersistentConnection = false; // A boolean variable to state whether its a persistent connection or normal connection. */
+
+ var $_bindInputArray = false; // set to true if ADOConnection.Execute() permits binding of array parameters.
+
+ var $autoCommit = true; // do not modify this yourself - actually private
+
+ /**
+ * Constructor
+ */
+ function ADOConnection()
+ {
+ die('Virtual Class -- cannot instantiate');
+ }
+
+
+ /**
+ * Connect to database
+ *
+ * @param [argHostname] Host to connect to
+ * @param [argUsername] Userid to login
+ * @param [argPassword] Associated password
+ * @param [argDatabaseName] database
+ *
+ * @return true or false
+ */
+ function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
+ {
+ if ($argHostname != "") $this->host = $argHostname;
+ if ($argUsername != "") $this->user = $argUsername;
+ if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
+ if ($argDatabaseName != "") $this->database = $argDatabaseName;
+
+ $this->_isPersistentConnection = false;
+ if ($fn = $this->raiseErrorFn) {
+ if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
+ $err = $this->ErrorMsg();
+ if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
+ $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database);
+ } else
+ if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
+
+ if ($this->debug) print $this->host.': '.$this->ErrorMsg()." \n";
+
+ return false;
+ }
+
+
+ /**
+ * Establish persistent connect to database
+ *
+ * @param [argHostname] Host to connect to
+ * @param [argUsername] Userid to login
+ * @param [argPassword] Associated password
+ * @param [argDatabaseName] database
+ *
+ * @return return true or false
+ */
+ function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
+ {
+ if ($argHostname != "") $this->host = $argHostname;
+ if ($argUsername != "") $this->user = $argUsername;
+ if ($argPassword != "") $this->password = $argPassword;
+ if ($argDatabaseName != "") $this->database = $argDatabaseName;
+
+ $this->_isPersistentConnection = true;
+
+ if ($fn = $this->raiseErrorFn) {
+ if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
+ $err = $this->ErrorMsg();
+ if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
+ $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database);
+ } else
+ if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
+
+ if ($this->debug) print $this->host.': '.$this->ErrorMsg()." \n";
+
+ return false;
+ }
+
+ function UnixDate($d)
+ {
+ return ADORecordSet::UnixDate($d);
+ }
+
+ /**
+ * Should prepare the sql statement and return the stmt resource.
+ * For databases that do not support this, we return the $sql. To ensure
+ * compatibility with databases that do not support prepare:
+ *
+ * $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
+ * $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
+ * $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
+ *
+ * @param sql SQL to send to database
+ *
+ * @return return FALSE, or the prepared statement, or the original sql if
+ * if the database does not support prepare.
+ *
+ */
+ function Prepare($sql)
+ {
+ return $sql;
+ }
+
+ /**
+ * Some databases, eg. mssql require a different function for preparing
+ * stored procedures. So we cannot use Prepare().
+ *
+ * Should prepare the stored procedure and return the stmt resource.
+ * For databases that do not support this, we return the $sql. To ensure
+ * compatibility with databases that do not support prepare:
+ *
+ * @param sql SQL to send to database
+ *
+ * @return return FALSE, or the prepared statement, or the original sql if
+ * if the database does not support prepare.
+ *
+ */
+ function PrepareSP($sql)
+ {
+ return $this->Prepare($sql);
+ }
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function Quote($s)
+ {
+ return $this->qstr($s);
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function ErrorNative()
+ {
+ return $this->ErrorNo();
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function nextId($seq_name)
+ {
+ return $this->GenID($seq_name);
+ }
+
+ /**
+ * Lock a row, will escalate and lock the table if row locking not supported
+ * will normally free the lock at the end of the transaction
+ *
+ * @param $table name of table to lock
+ * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
+ */
+ function RowLock($table,$where)
+ {
+ return false;
+ }
+
+ function CommitLock($table)
+ {
+ return $this->CommitTrans();
+ }
+
+ function RollbackLock($table)
+ {
+ return $this->RollbackTrans();
+ }
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ *
+ * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
+ * for easy porting :-)
+ */
+ function SetFetchMode($mode)
+ {
+ global $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = $mode;
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally.
+ */
+ function &Query($sql, $inputarr=false)
+ {
+ $rs = &$this->Execute($sql, $inputarr);
+ if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
+ return $rs;
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally
+ */
+ function &LimitQuery($sql, $offset, $count)
+ {
+ $rs = &$this->SelectLimit($sql, $count, $offset); // swap
+ if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
+ return $rs;
+ }
+
+
+ /**
+ * PEAR DB Compat - do not use internally
+ */
+ function Disconnect()
+ {
+ return $this->Close();
+ }
+
+ /*
+ Usage in oracle
+ $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
+ $db->Parameter($stmt,$id,'myid');
+ $db->Parameter($stmt,$group,'group',64);
+ $db->Execute();
+
+ @param $stmt Statement returned by Prepare() or PrepareSP().
+ @param $var PHP variable to bind to
+ @param $name Name of stored procedure variable name to bind to.
+ @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
+ @param [$maxLen] Holds an maximum length of the variable.
+ @param [$type] The data type of $var. Legal values depend on driver.
+
+ */
+ function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
+ {
+ return false;
+ }
+
+ /**
+ * Execute SQL
+ *
+ * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
+ * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
+ * @param [arg3] reserved for john lim for future use
+ * @return RecordSet or false
+ */
+ function &Execute($sql,$inputarr=false,$arg3=false)
+ {
+ if (!$this->_bindInputArray && $inputarr) {
+ $sqlarr = explode('?',$sql);
+ $sql = '';
+ $i = 0;
+ foreach($inputarr as $v) {
+
+ $sql .= $sqlarr[$i];
+ // from Ron Baldwin
+ // Only quote string types
+ if (gettype($v) == 'string')
+ $sql .= $this->qstr($v);
+ else if ($v === null)
+ $sql .= 'NULL';
+ else
+ $sql .= $v;
+ $i += 1;
+
+ }
+ $sql .= $sqlarr[$i];
+ if ($i+1 != sizeof($sqlarr))
+ print "Input Array does not match ?: ".htmlspecialchars($sql);
+ $inputarr = false;
+ }
+
+ // debug version of query
+ if ($this->debug) {
+ global $HTTP_SERVER_VARS;
+
+ $ss = '';
+ if ($inputarr) {
+ foreach ($inputarr as $kk => $vv) {
+ if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
+ $ss .= "($kk=>'$vv') ";
+ }
+ $ss = "[ $ss ]";
+ }
+ if (is_array($sql)) $sqlTxt = $sql[0];
+ else $sqlTxt = $sql;
+
+ // check if running from browser or command-line
+ $inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']);
+
+ if ($inBrowser)
+ print "\n($this->databaseType): ".htmlspecialchars($sqlTxt)." $ss\n\n";
+ else
+ print "=----\n($this->databaseType): ".($sqlTxt)." \n-----\n";
+ flush();
+
+ $this->_queryID = $this->_query($sql,$inputarr,$arg3);
+
+ if ($this->databaseType == 'mssql') {
+ // ErrorNo is a slow function call in mssql, and not reliable
+ // in PHP 4.0.6
+ if($this->ErrorMsg()) {
+ $err = $this->ErrorNo();
+ if ($err) {
+ print $err.': '.$this->ErrorMsg().(($inBrowser) ? " \n" : "\n");
+ flush();
+ }
+ }
+ } else
+ if (!$this->_queryID) {
+ print $this->ErrorNo().': '.$this->ErrorMsg().(($inBrowser) ? " \n" : "\n");
+ flush();
+ }
+ } else
+ // non-debug version of query
+ $this->_queryID =@$this->_query($sql,$inputarr,$arg3);
+
+ // error handling if query fails
+ if ($this->_queryID === false) {
+ if ($fn = $this->raiseErrorFn) {
+ $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr);
+ }
+ return false;
+ } else if ($this->_queryID === true){
+ // return simplified empty recordset for inserts/updates/deletes with lower overhead
+ $rs = new ADORecordSet_empty();
+ return $rs;
+ }
+
+ // return real recordset from select statement
+ $rsclass = "ADORecordSet_".$this->databaseType;
+ $rs = new $rsclass($this->_queryID); // &new not supported by older PHP versions
+ $rs->connection = &$this; // Pablo suggestion
+ $rs->Init();
+
+ if (is_array($sql)) $rs->sql = $sql[0];
+ else $rs->sql = $sql;
+
+ global $ADODB_COUNTRECS;
+ if ($rs->_numOfRows <= 0 && !$rs->EOF && $ADODB_COUNTRECS) {
+ $rs = &$this->_rs2rs($rs);
+ $rs->_queryID = $this->_queryID;
+ }
+ return $rs;
+ }
+
+
+ /**
+ * Generates a sequence id and stores it in $this->genID;
+ * GenID is only available if $this->hasGenID = true;
+ *
+ * @seqname name of sequence to use
+ * @startID if sequence does not exist, start at this ID
+ * @return 0 if not supported, otherwise a sequence id
+ */
+
+ function GenID($seqname='adodbseq',$startID=1)
+ {
+ if (!$this->hasGenID) {
+ return 0; // formerly returns false pre 1.60
+ }
+
+ $getnext = sprintf($this->_genIDSQL,$seqname);
+ $rs = @$this->Execute($getnext);
+ if (!$rs) {
+ $u = strtoupper($seqname);
+ $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
+ $rs = $this->Execute($getnext);
+ }
+ if ($rs && !$rs->EOF) $this->genID = (integer) reset($rs->fields);
+ else $this->genID = 0; // false
+
+ if ($rs) $rs->Close();
+
+ return $this->genID;
+ }
+
+ /**
+ * @return the last inserted ID. Not all databases support this.
+ */
+ function Insert_ID()
+ {
+ if ($this->hasInsertID) return $this->_insertid();
+ if ($this->debug) print '
Insert_ID error
';
+ return false;
+ }
+
+
+ /**
+ * Portable Insert ID. Pablo Roca
+ *
+ * @return the last inserted ID. All databases support this. But aware possible
+ * problems in multiuser environments. Heavy test this before deploying.
+ */
+ function PO_Insert_ID($table="", $id="")
+ {
+ if ($this->hasInsertID){
+ return $this->Insert_ID();
+ } else {
+ return $this->GetOne("SELECT MAX($id) FROM $table");
+ }
+ }
+
+
+ /**
+ * @return # rows affected by UPDATE/DELETE
+ */
+ function Affected_Rows()
+ {
+ if ($this->hasAffectedRows) {
+ $val = $this->_affectedrows();
+ return ($val < 0) ? false : $val;
+ }
+
+ if ($this->debug) print '
Affected_Rows error
';
+ return false;
+ }
+
+
+ /**
+ * @return the last error message
+ */
+ function ErrorMsg()
+ {
+ return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
+ }
+
+
+ /**
+ * @return the last error number. Normally 0 means no error.
+ */
+ function ErrorNo()
+ {
+ return ($this->_errorMsg) ? -1 : 0;
+ }
+
+
+ /**
+ * @returns an array with the primary key columns in it.
+ */
+ function MetaPrimaryKeys($table)
+ {
+ return false;
+ }
+
+
+ /**
+ * Choose a database to connect to. Many databases do not support this.
+ *
+ * @param dbName is the name of the database to select
+ * @return true or false
+ */
+ function SelectDB($dbName)
+ {return false;}
+
+
+ /**
+ * Will select, getting rows from $offset (1-based), for $nrows.
+ * This simulates the MySQL "select * from table limit $offset,$nrows" , and
+ * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
+ * MySQL and PostgreSQL parameter ordering is the opposite of the other.
+ * eg.
+ * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
+ * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
+ *
+ * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
+ * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
+ *
+ * @param sql
+ * @param [offset] is the row to start calculations from (1-based)
+ * @param [rows] is the number of rows to get
+ * @param [inputarr] array of bind variables
+ * @param [arg3] is a private parameter only used by jlim
+ * @param [secs2cache] is a private parameter only used by jlim
+ * @return the recordset ($rs->databaseType == 'array')
+ */
+ function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0)
+ {
+ if ($this->hasTop && $nrows > 0) {
+ // suggested by Reinhard Balling. Access requires top after distinct
+
+ if ($offset <= 0) {
+ $sql = preg_replace(
+ '/(^[\\t\\n ]*select[\\t\\n ]*(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql);
+
+ if ($secs2cache>0) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3);
+ else return $this->Execute($sql,$inputarr,$arg3);
+ } else {
+ $nrows += $offset;
+ $sql = preg_replace(
+ '/(^[\\t\\n ]*select[\\t\\n ]*(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql);
+ $nrows = -1;
+ }
+
+ }
+
+ // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows
+ // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
+ global $ADODB_COUNTRECS;
+
+ $savec = $ADODB_COUNTRECS;
+ $ADODB_COUNTRECS = false;
+
+ if ($offset>0){
+ if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3);
+ else $rs = &$this->Execute($sql,$inputarr,$arg3);
+ } else {
+ if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3);
+ else $rs = &$this->Execute($sql,$inputarr,$arg3);
+ }
+ $ADODB_COUNTRECS = $savec;
+
+ if ($rs && !$rs->EOF) {
+ return $this->_rs2rs($rs,$nrows,$offset);
+ }
+ //print_r($rs);
+ return $rs;
+ }
+
+
+ /**
+ * Convert recordset to an array recordset
+ * input recordset's cursor should be at beginning, and
+ * old $rs will be closed.
+ *
+ * @param rs the recordset to copy
+ * @param [nrows] number of rows to retrieve (optional)
+ * @param [offset] offset by number of rows (optional)
+ * @return the new recordset
+ */
+ function &_rs2rs(&$rs,$nrows=-1,$offset=-1)
+ {
+ $arr = &$rs->GetArrayLimit($nrows,$offset);
+ $flds = array();
+ for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
+ $flds[] = &$rs->FetchField($i);
+ $rs->Close();
+
+ $arrayClass = $this->arrayClass;
+
+ $rs2 = new $arrayClass();
+ $rs2->connection = &$this;
+ $rs2->sql = $rs->sql;
+ $rs2->InitArrayFields($arr,$flds);
+ return $rs2;
+ }
+
+
+ /**
+ * Return first element of first row of sql statement. Recordset is disposed
+ * for you.
+ *
+ * @param sql SQL statement
+ * @param [inputarr] input bind array
+ */
+ function GetOne($sql,$inputarr=false)
+ {
+ $ret = false;
+ $rs = &$this->Execute($sql,$inputarr);
+ if ($rs) {
+ if (!$rs->EOF) $ret = reset($rs->fields);
+ $rs->Close();
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Return all rows. Compat with PEAR DB
+ *
+ * @param sql SQL statement
+ * @param [inputarr] input bind array
+ */
+ function &GetAll($sql,$inputarr=false)
+ {
+ $rs = $this->Execute($sql,$inputarr);
+ if (!$rs)
+ if (defined('ADODB_PEAR')) return ADODB_PEAR_Error();
+ else return false;
+ return $rs->GetArray();
+ }
+
+
+ /**
+ * Return one row of sql statement. Recordset is disposed for you.
+ *
+ * @param sql SQL statement
+ * @param [inputarr] input bind array
+ */
+ function GetRow($sql,$inputarr=false)
+ {
+ $rs = $this->Execute($sql,$inputarr);
+ if ($rs) {
+ $arr = false;
+ if (!$rs->EOF) $arr = $rs->fields;
+ $rs->Close();
+ return $arr;
+ }
+ return false;
+ }
+
+
+
+ /**
+ * Will select, getting rows from $offset (1-based), for $nrows.
+ * This simulates the MySQL "select * from table limit $offset,$nrows" , and
+ * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
+ * MySQL and PostgreSQL parameter ordering is the opposite of the other.
+ * eg.
+ * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
+ * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
+ *
+ * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
+ *
+ * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional
+ * @param sql
+ * @param [offset] is the row to start calculations from (1-based)
+ * @param [nrows] is the number of rows to get
+ * @param [inputarr] array of bind variables
+ * @param [arg3] is a private parameter only used by jlim
+ * @return the recordset ($rs->databaseType == 'array')
+ */
+ function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false)
+ {
+ if (!is_numeric($secs2cache)) {
+ if ($sql === false) $sql = -1;
+ if ($offset == -1) $offset = false;
+ // sql, nrows, offset,inputarr,arg3
+ return $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$inputarr,$this->cacheSecs);
+ }
+ if ($sql === false) echo "Warning: \$sql missing from CacheSelectLimit() \n";
+ return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache);
+ }
+
+
+ function CacheFlush($sql)
+ {
+ $f = $this->_gencachename($sql,false);
+ adodb_write_file($f,''); // is adodb_write_file needed?
+ @unlink($f);
+ }
+
+
+ function _gencachename($sql,$createdir)
+ {
+ global $ADODB_CACHE_DIR;
+
+ $m = md5($sql.$this->databaseType.$this->database.$this->user);
+ $dir = $ADODB_CACHE_DIR.'/'.substr($m,0,2);
+ if ($createdir)
+ if(!file_exists($dir) && !mkdir($dir,0771))
+ if ($this->debug) print "Unable to mkdir $dir for $sql \n";
+ return $dir.'/adodb_'.$m.'.cache';
+ }
+
+
+ /**
+ * Execute SQL, caching recordsets.
+ *
+ * @param [secs2cache] seconds to cache data, set to 0 to force query.
+ * This is an optional parameter.
+ * @param sql SQL statement to execute
+ * @param [inputarr] holds the input data to bind to
+ * @param [arg3] reserved for john lim for future use
+ * @return RecordSet or false
+ */
+ function &CacheExecute($secs2cache,$sql=false,$inputarr=false,$arg3=false)
+ {
+ if (!is_numeric($secs2cache)) {
+ $arg3 = $inputarr;
+ $inputarr = $sql;
+ $sql = $secs2cache;
+ $secs2cache = $this->cacheSecs;
+ }
+ include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
+ // cannot cache if $inputarr set
+ if ($inputarr) return $this->Execute($sql, $inputarr, $arg3);
+
+ $md5file = $this->_gencachename($sql,true);
+ $err = '';
+
+ if ($secs2cache > 0)$rs = &csv2rs($md5file,$err,$secs2cache);
+ else {
+ $err='Timeout 1';
+ $rs = false;
+ }
+
+ if (!$rs) {
+ // no cached rs found
+ if ($this->debug) print " $md5file cache failure: $err \n";
+ $rs = &$this->Execute($sql,$inputarr,$arg3);
+ if ($rs) {
+ $eof = $rs->EOF;
+ $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
+ $txt = &rs2csv($rs,false,$sql); // serialize
+
+ if (!adodb_write_file($md5file,$txt,$this->debug)) {
+ if ($fn = $this->raiseErrorFn) {
+ $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql);
+ }
+ if ($this->debug) print " Cache write error \n";
+ }
+ if ($rs->EOF && !$eof) {
+ $rs = &csv2rs($md5file,$err);
+ $rs->connection = &$this; // Pablo suggestion
+ }
+
+ } else
+ @unlink($md5file);
+ } else {
+ // ok, set cached object found
+ $rs->connection = &$this; // Pablo suggestion
+ if ($this->debug){
+ $ttl = $rs->timeCreated + $secs2cache - time();
+ print " $md5file reloaded, ttl=$ttl \n";
+ }
+ }
+ return $rs;
+ }
+
+
+ /**
+ * Generates an Update Query based on an existing recordset.
+ * $arrFields is an associative array of fields with the value
+ * that should be assigned.
+ *
+ * Note: This function should only be used on a recordset
+ * that is run against a single table and sql should only
+ * be a simple select stmt with no groupby/orderby/limit
+ *
+ * "Jonathan Younger"
+ */
+ function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false)
+ {
+ include_once(ADODB_DIR.'/adodb-lib.inc.php');
+ return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq);
+ }
+
+
+ /**
+ * Generates an Insert Query based on an existing recordset.
+ * $arrFields is an associative array of fields with the value
+ * that should be assigned.
+ *
+ * Note: This function should only be used on a recordset
+ * that is run against a single table.
+ */
+ function GetInsertSQL(&$rs, $arrFields,$magicq=false)
+ {
+ include_once(ADODB_DIR.'/adodb-lib.inc.php');
+ return _adodb_getinsertsql($this,$rs,$arrFields,$magicq);
+ }
+
+
+ /**
+ * Usage:
+ * UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'BLOB');
+ *
+ * $blobtype supports 'BLOB' and 'CLOB'
+ *
+ * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
+ * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
+ */
+
+ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+ {
+ return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
+ }
+
+ /**
+ * Usage:
+ * UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1', 'BLOB');
+ *
+ * $blobtype supports 'BLOB' and 'CLOB'
+ *
+ * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
+ * $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
+ */
+ function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
+ {
+ $fd = fopen($path,'rb');
+ if ($fd === false) return false;
+ $val = fread($fd,filesize($path));
+ fclose($fd);
+ return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
+ }
+
+ /**
+ * Usage:
+ * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
+ *
+ * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
+ * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
+ */
+ function UpdateClob($table,$column,$val,$where)
+ {
+ return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
+ }
+
+
+ /**
+ * @meta contains the desired type, which could be...
+ * C for character. You will have to define the precision yourself.
+ * X for teXt. For unlimited character lengths.
+ * B for Binary
+ * F for floating point, with no need to define scale and precision
+ * N for decimal numbers, you will have to define the (scale, precision) yourself
+ * D for date
+ * T for timestamp
+ * L for logical/Boolean
+ * I for integer
+ * R for autoincrement counter/integer
+ * and if you want to use double-byte, add a 2 to the end, like C2 or X2.
+ *
+ *
+ * @return the actual type of the data or false if no such type available
+ */
+ function ActualType($meta)
+ {
+ switch($meta) {
+ case 'C':
+ case 'X':
+ return 'VARCHAR';
+ case 'B':
+
+ case 'D':
+ case 'T':
+ case 'L':
+
+ case 'R':
+
+ case 'I':
+ case 'N':
+ return false;
+ }
+ }
+
+
+ /*
+ * Maximum size of C field
+ */
+ function CharMax()
+ {
+ return 255; // make it conservative if not defined
+ }
+
+
+ /*
+ * Maximum size of X field
+ */
+ function TextMax()
+ {
+ return 4000; // make it conservative if not defined
+ }
+
+
+ /**
+ * Close Connection
+ */
+ function Close()
+ {
+ return $this->_close();
+
+ // "Simon Lee" reports that persistent connections need
+ // to be closed too!
+ //if ($this->_isPersistentConnection != true) return $this->_close();
+ //else return true;
+ }
+
+
+ /**
+ * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
+ *
+ * @return true if succeeded or false if database does not support transactions
+ */
+ function BeginTrans() {return false;}
+
+
+ /**
+ * If database does not support transactions, always return true as data always commited
+ *
+ * @param $ok set to false to rollback transaction, true to commit
+ *
+ * @return true/false.
+ */
+ function CommitTrans($ok=true)
+ { return true;}
+
+
+ /**
+ * If database does not support transactions, rollbacks always fail, so return false
+ *
+ * @return true/false.
+ */
+ function RollbackTrans()
+ { return false;}
+
+
+ /**
+ * return the databases that the driver can connect to.
+ * Some databases will return an empty array.
+ *
+ * @return an array of database names.
+ */
+ function MetaDatabases()
+ {return false;}
+
+ /**
+ * @return array of tables for current database.
+ */
+ function MetaTables()
+ {
+ global $ADODB_FETCH_MODE;
+
+ if ($this->metaTablesSQL) {
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ $rs = $this->Execute($this->metaTablesSQL);
+ $ADODB_FETCH_MODE = $save;
+
+ if ($rs === false) return false;
+ $arr = $rs->GetArray();
+ $arr2 = array();
+ for ($i=0; $i < sizeof($arr); $i++) {
+ $arr2[] = $arr[$i][0];
+ }
+ $rs->Close();
+ return $arr2;
+ }
+ return false;
+ }
+
+
+ /**
+ * List columns in a database as an array of ADOFieldObjects.
+ * See top of file for definition of object.
+ *
+ * @params table table name to query
+ * @params upper uppercase table name (required by some databases)
+ *
+ * @return array of ADOFieldObjects for current table.
+ */
+ function MetaColumns($table,$upper=true)
+ {
+ global $ADODB_FETCH_MODE;
+
+ if (!empty($this->metaColumnsSQL)) {
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ $rs = $this->Execute(sprintf($this->metaColumnsSQL,($upper)?strtoupper($table):$table));
+ $ADODB_FETCH_MODE = $save;
+ if ($rs === false) return false;
+
+ $retarr = array();
+ while (!$rs->EOF) { //print_r($rs->fields);
+ $fld = new ADOFieldObject();
+ $fld->name = $rs->fields[0];
+ $fld->type = $rs->fields[1];
+ $fld->max_length = $rs->fields[2];
+ $retarr[strtoupper($fld->name)] = $fld;
+
+ $rs->MoveNext();
+ }
+ $rs->Close();
+ return $retarr;
+ }
+ return false;
+ }
+
+ /**
+ * List columns names in a table as an array.
+ * @params table table name to query
+ *
+ * @return array of column names for current table.
+ */
+ function MetaColumnNames($table)
+ {
+ $objarr = $this->MetaColumns($table);
+ if (!is_array($objarr)) return false;
+
+ $arr = array();
+ foreach($objarr as $v) {
+ $arr[] = $v->name;
+ }
+ return $arr;
+ }
+
+ /**
+ * Different SQL databases used different methods to combine strings together.
+ * This function provides a wrapper.
+ *
+ * @param s variable number of string parameters
+ *
+ * Usage: $db->Concat($str1,$str2);
+ *
+ * @return concatenated string
+ */
+ function Concat()
+ {
+ $arr = func_get_args();
+ return implode($this->concat_operator, $arr);
+ }
+
+
+ /**
+ * Converts a date "d" to a string that the database can understand.
+ *
+ * @param d a date in Unix date time format.
+ *
+ * @return date string in database date format
+ */
+ function DBDate($d)
+ {
+
+ // note that we are limited to 1970 to 2038
+ if (empty($d) && $d !== 0) return 'null';
+
+ if (is_string($d) && !is_numeric($d))
+ if ($this->isoDates) return "'$d'";
+ else $d = ADOConnection::UnixDate($d);
+
+ return date($this->fmtDate,$d);
+ }
+
+
+ /**
+ * Converts a timestamp "ts" to a string that the database can understand.
+ *
+ * @param ts a timestamp in Unix date time format.
+ *
+ * @return timestamp string in database timestamp format
+ */
+ function DBTimeStamp($ts)
+ {
+ if (empty($ts) && $ts !== 0) return 'null';
+
+ if (is_string($ts) && !is_numeric($ts))
+ if ($this->isoDates) return "'$ts'";
+ else $ts = ADOConnection::UnixTimeStamp($ts);
+ return date($this->fmtTimeStamp,$ts);
+ }
+
+ /**
+ * Also in ADORecordSet.
+ * @param $v is a date string in YYYY-MM-DD format
+ *
+ * @return date in unix timestamp format, or 0 if before 1970, or false if invalid date format
+ */
+ function UnixDate($v)
+ {
+ if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})$|",
+ ($v), $rr)) return false;
+
+ if ($rr[1] <= 1970) return 0;
+ // h-m-s-MM-DD-YY
+ return mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
+ }
+
+
+ /**
+ * Also in ADORecordSet.
+ * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
+ *
+ * @return date in unix timestamp format, or 0 if before 1970, or false if invalid date format
+ */
+ function UnixTimeStamp($v)
+ {
+ if (!preg_match(
+ "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9]{1,2}))?$|",
+ ($v), $rr)) return false;
+ if ($rr[1] <= 1970 && $rr[2]<= 1) return 0;
+
+ // h-m-s-MM-DD-YY
+ return @mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
+ }
+
+ /**
+ * Converts a timestamp "ts" to a string that the database can understand.
+ * An example is $db->qstr("Don't bother",magic_quotes_runtime());
+ *
+ * @param s the string to quote
+ * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
+ * This undoes the stupidity of magic quotes for GPC.
+ *
+ * @return quoted string to be sent back to database
+ */
+ function qstr($s,$magic_quotes=false)
+ {
+ $nofixquotes=false;
+ if (!$magic_quotes) {
+
+ if ($this->replaceQuote[0] == '\\'){
+ $s = str_replace('\\','\\\\',$s);
+ }
+ return "'".str_replace("'",$this->replaceQuote,$s)."'";
+ }
+
+ // undo magic quotes for "
+ $s = str_replace('\\"','"',$s);
+
+ if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
+ return "'$s'";
+ else {// change \' to '' for sybase/mssql
+ $s = str_replace('\\\\','\\',$s);
+ return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
+ }
+ }
+
+
+ /**
+ * Will select the supplied $page number from a recordset, given that it is paginated in pages of
+ * $nrows rows per page. It also saves two boolean values saying if the given page is the first
+ * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
+ *
+ * See readme.htm#ex8 for an example of usage.
+ *
+ * @param sql
+ * @param nrows is the number of rows per page to get
+ * @param page is the page number to get (1-based)
+ * @param [inputarr] array of bind variables
+ * @param [arg3] is a private parameter only used by jlim
+ * @param [secs2cache] is a private parameter only used by jlim
+ * @return the recordset ($rs->databaseType == 'array')
+ *
+ * NOTE: phpLens uses a different algorithm and does not use PageExecute().
+ *
+ */
+ function &PageExecute($sql, $nrows, $page, $inputarr=false, $arg3=false, $secs2cache=0)
+ {
+ include_once(ADODB_DIR.'/adodb-lib.inc.php');
+ return _adodb_pageexecute($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache);
+
+ }
+
+
+ /**
+ * Will select the supplied $page number from a recordset, given that it is paginated in pages of
+ * $nrows rows per page. It also saves two boolean values saying if the given page is the first
+ * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
+ *
+ * @param secs2cache seconds to cache data, set to 0 to force query
+ * @param sql
+ * @param nrows is the number of rows per page to get
+ * @param page is the page number to get (1-based)
+ * @param [inputarr] array of bind variables
+ * @param [arg3] is a private parameter only used by jlim
+ * @return the recordset ($rs->databaseType == 'array')
+ */
+ function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false, $arg3=false) {
+ include_once(ADODB_DIR.'/adodb-lib.inc.php');
+ return _adodb_pageexecute($this, $sql, $nrows, $page, $inputarr, $arg3,$secs2cache);
+ }
+
+} // end class ADOConnection
+
+
+
+ //==============================================================================================
+ // CLASS ADOFetchObj
+ //==============================================================================================
+
+ /**
+ * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
+ */
+ class ADOFetchObj {
+ };
+
+ //==============================================================================================
+ // CLASS ADORecordSet_empty
+ //==============================================================================================
+
+ /**
+ * Lightweight recordset when there are no records to be returned
+ */
+ class ADORecordSet_empty
+ {
+ var $dataProvider = 'empty';
+ var $EOF = true;
+ var $_numOfRows = 0;
+ var $fields = false;
+ var $connection = false;
+ function RowCount() {return 0;}
+ function RecordCount() {return 0;}
+ function PO_RecordCount(){return 0;}
+ function Close(){return true;}
+ function FetchRow() {return false;}
+ function FieldCount(){ return 0;}
+ }
+
+ //==============================================================================================
+ // CLASS ADORecordSet
+ //==============================================================================================
+
+ /**
+ * RecordSet class that represents the dataset returned by the database.
+ * To keep memory overhead low, this class holds only the current row in memory.
+ * No prefetching of data is done, so the RecordCount() can return -1 ( which
+ * means recordcount not known).
+ */
+ class ADORecordSet {
+ /*
+ * public variables
+ */
+ var $dataProvider = "native";
+ var $fields = false; // holds the current row data
+ var $blobSize = 64; // any varchar/char field this size or greater is treated as a blob
+ // in other words, we use a text area for editting.
+ var $canSeek = false; // indicates that seek is supported
+ var $sql; // sql text
+ var $EOF = false; /* Indicates that the current record position is after the last record in a Recordset object. */
+
+ var $emptyTimeStamp = ' '; // what to display when $time==0
+ var $emptyDate = ' '; // what to display when $time==0
+ var $debug = false;
+ var $timeCreated=0; // datetime in Unix format rs created -- for cached recordsets
+
+ var $bind = false; // used by Fields() to hold array - should be private?
+ var $fetchMode; // default fetch mode
+ var $connection = false; // the parent connection
+ /*
+ * private variables
+ */
+ var $_numOfRows = -1; /* number of rows, or -1 */
+ var $_numOfFields = -1; /* number of fields in recordset */
+ var $_queryID = -1; /* This variable keeps the result link identifier. */
+ var $_currentRow = -1; /* This variable keeps the current row in the Recordset. */
+ var $_closed = false; /* has recordset been closed */
+ var $_inited = false; /* Init() should only be called once */
+ var $_obj; /* Used by FetchObj */
+ var $_names; /* Used by FetchObj */
+
+ var $_currentPage = -1; /* Added by Iván Oliva to implement recordset pagination */
+ var $_atFirstPage = false; /* Added by Iván Oliva to implement recordset pagination */
+ var $_atLastPage = false; /* Added by Iván Oliva to implement recordset pagination */
+
+
+ /**
+ * Constructor
+ *
+ * @param queryID this is the queryID returned by ADOConnection->_query()
+ *
+ */
+ function ADORecordSet($queryID)
+ {
+ $this->_queryID = $queryID;
+ }
+
+
+ function Init()
+ {
+ if ($this->_inited) return;
+ $this->_inited = true;
+
+ if ($this->_queryID) @$this->_initrs();
+ else {
+ $this->_numOfRows = 0;
+ $this->_numOfFields = 0;
+ }
+ if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
+ $this->_currentRow = 0;
+ $this->EOF = ($this->_fetch() === false);
+ } else
+ $this->EOF = true;
+ }
+
+
+ /**
+ * Generate a