Michel Käser
2015-05-16 82e9b9e7c7ecf1664a7b0d4e57a5c4893739559d
commit | author | age
b5a2f8 1 <?php
T 2 /*
f5b0ca 3    Copyright (c) 2005, Till Brehm, projektfarm Gmbh
N 4    All rights reserved.
b5a2f8 5
f5b0ca 6    Redistribution and use in source and binary forms, with or without modification,
N 7    are permitted provided that the following conditions are met:
b5a2f8 8
f5b0ca 9  * Redistributions of source code must retain the above copyright notice,
N 10  this list of conditions and the following disclaimer.
11  * Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following disclaimer in the documentation
13  and/or other materials provided with the distribution.
14  * Neither the name of ISPConfig nor the names of its contributors
15  may be used to endorse or promote products derived from this software without
16  specific prior written permission.
b5a2f8 17
f5b0ca 18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
N 19  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
b5a2f8 29
f5b0ca 30 class db extends mysqli
N 31 {
cd8bb8 32     /**#@+
MC 33      * @access private
34      */
35     private $_iQueryId;
36     private $_iConnId;
37
7fe908 38     private $dbHost = '';  // hostname of the MySQL server
82e9b9 39     private $dbPort = '';  // port of the MySQL server
7fe908 40     private $dbName = '';  // logical database name on that server
MC 41     private $dbUser = '';  // database authorized user
42     private $dbPass = '';  // user's password
43     private $dbCharset = 'utf8';// Database charset
44     private $dbNewLink = false; // Return a new linkID when connect is called again
45     private $dbClientFlags = 0; // MySQL Client falgs
cd8bb8 46     /**#@-*/
MC 47
48     public $show_error_messages = false; // false in server, true in interface
49
50
51     /* old things - unused now ////
7fe908 52     private $linkId = 0;  // last result of mysqli_connect()
MC 53     private $queryId = 0;  // last result of mysqli_query()
54     private $record = array(); // last record fetched
55     private $autoCommit = 1;    // Autocommit Transactions
56     private $currentRow;  // current row number
57     private $errorNumber = 0; // last error number
58     public $errorMessage = ''; // last error message
59     private $errorLocation = '';// last error location
60     private $isConnected = false; // needed to know if we have a valid mysqli object from the constructor
cd8bb8 61     ////
MC 62     */
b5a2f8 63
7fe908 64     // constructor
MC 65     public function __construct($prefix = '') {
66         global $conf;
67         if($prefix != '') $prefix .= '_';
68         $this->dbHost = $conf[$prefix.'db_host'];
82e9b9 69         $this->dbPort = $conf[$prefix.'db_port'];
7fe908 70         $this->dbName = $conf[$prefix.'db_database'];
MC 71         $this->dbUser = $conf[$prefix.'db_user'];
72         $this->dbPass = $conf[$prefix.'db_password'];
73         $this->dbCharset = $conf[$prefix.'db_charset'];
74         $this->dbNewLink = $conf[$prefix.'db_new_link'];
75         $this->dbClientFlags = $conf[$prefix.'db_client_flags'];
cd8bb8 76
82e9b9 77         $this->_iConnId = mysqli_connect($this->dbHost, $this->dbUser, $this->dbPass, (int)$this->dbPort);
7fe908 78         $try = 0;
cd8bb8 79         while((!is_object($this->_iConnId) || mysqli_connect_error()) && $try < 5) {
7fe908 80             if($try > 0) sleep(1);
f5b0ca 81
7fe908 82             $try++;
82e9b9 83             $this->_iConnId = mysqli_connect($this->dbHost, $this->dbUser, $this->dbPass, (int)$this->dbPort);
7fe908 84         }
f5b0ca 85
cd8bb8 86         if(!is_object($this->_iConnId) || mysqli_connect_error()) {
MC 87             $this->_iConnId = null;
88             $this->_sqlerror('Zugriff auf Datenbankserver fehlgeschlagen! / Database server not accessible!');
89             return false;
90         }
ccfc84 91         if(!((bool)mysqli_query( $this->_iConnId, 'USE `' . $this->dbName . '`'))) {
cd8bb8 92             $this->close();
MC 93             $this->_sqlerror('Datenbank nicht gefunden / Database not found');
94             return false;
95         }
7fe908 96
cd8bb8 97         $this->_setCharset();
7fe908 98     }
MC 99
100     public function __destruct() {
cd8bb8 101         if($this->_iConnId) mysqli_close($this->_iConnId);
7fe908 102     }
MC 103
cd8bb8 104     public function close() {
MC 105         if($this->_iConnId) mysqli_close($this->_iConnId);
106         $this->_iConnId = null;
107     }
7fe908 108
cd8bb8 109     public function _build_query_string($sQuery = '') {
MC 110         $iArgs = func_num_args();
111         if($iArgs > 1) {
112             $aArgs = func_get_args();
cc6568 113
cd8bb8 114             if($iArgs == 3 && $aArgs[1] === true && is_array($aArgs[2])) {
MC 115                 $aArgs = $aArgs[2];
116                 $iArgs = count($aArgs);
117             } else {
118                 array_shift($aArgs); // delete the query string that is the first arg!
119             }
f5b0ca 120
cd8bb8 121             $iPos = 0;
MC 122             $iPos2 = 0;
123             foreach($aArgs as $sKey => $sValue) {
124                 $iPos2 = strpos($sQuery, '??', $iPos2);
125                 $iPos = strpos($sQuery, '?', $iPos);
126
127                 if($iPos === false && $iPos2 === false) break;
128
129                 if($iPos2 !== false && ($iPos === false || $iPos2 <= $iPos)) {
130                     $sTxt = $this->escape($sValue);
0e41de 131                     
MC 132                     $sTxt = str_replace('`', '', $sTxt);
d7fe4d 133                     if(strpos($sTxt, '.') !== false) {
MC 134                         $sTxt = preg_replace('/^(.+)\.(.+)$/', '`$1`.`$2`', $sTxt);
135                         $sTxt = str_replace('.`*`', '.*', $sTxt);
136                     } else $sTxt = '`' . $sTxt . '`';
cd8bb8 137
MC 138                     $sQuery = substr_replace($sQuery, $sTxt, $iPos2, 2);
139                     $iPos2 += strlen($sTxt);
140                     $iPos = $iPos2;
141                 } else {
142                     if(is_int($sValue) || is_float($sValue)) {
143                         $sTxt = $sValue;
3a11d2 144                     } elseif(is_null($sValue) || (is_string($sValue) && (strcmp($sValue, '#NULL#') == 0))) {
cd8bb8 145                         $sTxt = 'NULL';
MC 146                     } elseif(is_array($sValue)) {
3a11d2 147                         if(isset($sValue['SQL'])) {
MC 148                             $sTxt = $sValue['SQL'];
149                         } else {
150                             $sTxt = '';
151                             foreach($sValue as $sVal) $sTxt .= ',\'' . $this->escape($sVal) . '\'';
152                             $sTxt = '(' . substr($sTxt, 1) . ')';
153                             if($sTxt == '()') $sTxt = '(0)';
154                         }
cd8bb8 155                     } else {
MC 156                         $sTxt = '\'' . $this->escape($sValue) . '\'';
157                     }
158
159                     $sQuery = substr_replace($sQuery, $sTxt, $iPos, 1);
160                     $iPos += strlen($sTxt);
161                     $iPos2 = $iPos;
7fe908 162                 }
cd8bb8 163             }
7fe908 164         }
cd8bb8 165
MC 166         return $sQuery;
f5b0ca 167     }
7fe908 168
cd8bb8 169     /**#@-*/
MC 170
171
172     /**#@+
173      * @access private
174      */
175     private function _setCharset() {
176         mysqli_query($this->_iConnId, 'SET NAMES '.$this->dbCharset);
177         mysqli_query($this->_iConnId, "SET character_set_results = '".$this->dbCharset."', character_set_client = '".$this->dbCharset."', character_set_connection = '".$this->dbCharset."', character_set_database = '".$this->dbCharset."', character_set_server = '".$this->dbCharset."'");
f5b0ca 178     }
cb1221 179     
TB 180     private function securityScan($string) {
181         global $app, $conf;
182         
183         // get security config
184         if(isset($app)) {
185             $app->uses('getconf');
186             $ids_config = $app->getconf->get_security_config('ids');
187             
188             if($ids_config['sql_scan_enabled'] == 'yes') {
189                 
0e34ba 190                 // Remove whitespace
TB 191                 $string = trim($string);
192                 if(substr($string,-1) == ';') $string = substr($string,0,-1);
193                 
194                 // Save original string
cb1221 195                 $string_orig = $string;
TB 196                 
197                 //echo $string;
68b146 198                 $chars = array(';', '#', '/*', '*/', '--', '\\\'', '\\"');
cb1221 199         
TB 200                 $string = str_replace('\\\\', '', $string);
f986a3 201                 $string = preg_replace('/(^|[^\\\])([\'"])\\2/is', '$1', $string);
MC 202                 $string = preg_replace('/(^|[^\\\])([\'"])(.*?[^\\\])\\2/is', '$1', $string);
cb1221 203                 $ok = true;
TB 204
205                 if(substr_count($string, "`") % 2 != 0 || substr_count($string, "'") % 2 != 0 || substr_count($string, '"') % 2 != 0) {
206                     $app->log("SQL injection warning (" . $string_orig . ")",2);
207                     $ok = false;
208                 } else {
209                     foreach($chars as $char) {
210                         if(strpos($string, $char) !== false) {
211                             $ok = false;
212                             $app->log("SQL injection warning (" . $string_orig . ")",2);
213                             break;
214                         }
215                     }
216                 }
217                 if($ok == true) {
218                     return true;
219                 } else {
220                     if($ids_config['sql_scan_action'] == 'warn') {
221                         // we return false in warning level.
222                         return false;
223                     } else {
224                         // if sql action = 'block' or anything else then stop here.
225                         $app->error('Possible SQL injection. All actions have been logged.');
226                     }
227                 }
228             }
229         }
230     }
f5b0ca 231
cd8bb8 232     private function _query($sQuery = '') {
MC 233         global $app;
234
235         if ($sQuery == '') {
236             $this->_sqlerror('Keine Anfrage angegeben / No query given');
237             return false;
238         }
239
7fe908 240         $try = 0;
MC 241         do {
242             $try++;
cd8bb8 243             $ok = mysqli_ping($this->_iConnId);
7fe908 244             if(!$ok) {
82e9b9 245                 if(!mysqli_connect($this->dbHost, $this->dbUser, $this->dbPass, $this->dbName, (int)$this->dbPort)) {
7fe908 246                     if($try > 4) {
cd8bb8 247                         $this->_sqlerror('DB::query -> reconnect');
7fe908 248                         return false;
MC 249                     } else {
250                         sleep(1);
251                     }
252                 } else {
cd8bb8 253                     $this->_setCharset();
7fe908 254                     $ok = true;
MC 255                 }
256             }
257         } while($ok == false);
cd8bb8 258
MC 259         $aArgs = func_get_args();
260         $sQuery = call_user_func_array(array(&$this, '_build_query_string'), $aArgs);
797215 261         $this->securityScan($sQuery);
cd8bb8 262
8173c6 263         $this->_iQueryId = @mysqli_query($this->_iConnId, $sQuery);
cd8bb8 264         if (!$this->_iQueryId) {
MC 265             $this->_sqlerror('Falsche Anfrage / Wrong Query', false, 'SQL-Query = ' . $sQuery);
7fe908 266             return false;
MC 267         }
cd8bb8 268
MC 269         return is_bool($this->_iQueryId) ? $this->_iQueryId : new db_result($this->_iQueryId, $this->_iConnId);
7fe908 270     }
f5b0ca 271
cd8bb8 272     /**#@-*/
MC 273
274
275
276
277
278     /**
279      * Executes a query
280      *
281      * Executes a given query string, has a variable amount of parameters:
282      * - 1 parameter
283      *   executes the given query
284      * - 2 parameters
285      *   executes the given query, replaces the first ? in the query with the second parameter
286      * - 3 parameters
287      *   if the 2nd parameter is a boolean true, the 3rd parameter has to be an array containing all the replacements for every occuring ? in the query, otherwise the second parameter replaces the first ?, the third parameter replaces the second ? in the query
288      * - 4 or more parameters
289      *   all ? in the query are replaced from left to right by the parameters 2 to x
290      *
291      * @access public
292      * @param string  $sQuery query string
293      * @param mixed   ... one or more parameters
294      * @return db_result the result object of the query
295      */
296
297
298     public function query($sQuery = '') {
299         $aArgs = func_get_args();
300         return call_user_func_array(array(&$this, '_query'), $aArgs);
7fe908 301     }
f5b0ca 302
cd8bb8 303     /**
MC 304      * Execute a query and get first result array
305      *
306      * Executes a query and returns the first result row as an array
307      * This is like calling $result = $db->query(),  $result->get(), $result->free()
308      * Use of this function @see query
309      *
310      * @access public
311      * @param string  $sQuery query to execute
312      * @param ...     further params (see query())
313      * @return array result row or NULL if none found
314      */
315     public function queryOneRecord($sQuery = '') {
316         if(!preg_match('/limit \d+\s*,\s*\d+$/i', $sQuery)) $sQuery .= ' LIMIT 0,1';
317
318         $aArgs = func_get_args();
319         $oResult = call_user_func_array(array(&$this, 'query'), $aArgs);
320         if(!$oResult) return null;
321
322         $aReturn = $oResult->get();
323         $oResult->free();
324
325         return $aReturn;
7fe908 326     }
f5b0ca 327
cd8bb8 328     public function queryOne($sQuery = '') {
7ae5b0 329         return call_user_func_array(array(&$this, 'queryOneRecord'), func_get_args());
7fe908 330     }
MC 331
cd8bb8 332     public function query_one($sQuery = '') {
7ae5b0 333         return call_user_func_array(array(&$this, 'queryOneRecord'), func_get_args());
cd8bb8 334     }
MC 335
336     /**
337      * Execute a query and return all rows
338      *
339      * Executes a query and returns all result rows in an array
340      * <strong>Use this with extreme care!!!</strong> Uses lots of memory on big result sets.
341      *
342      * @access public
343      * @param string  $sQuery query to execute
344      * @param ...     further params (see query())
345      * @return array all the rows in the result set
346      */
347     public function queryAllRecords($sQuery = '') {
348         $aArgs = func_get_args();
349         $oResult = call_user_func_array(array(&$this, 'query'), $aArgs);
350         if(!$oResult) return array();
351
352         $aResults = array();
353         while($aRow = $oResult->get()) {
354             $aResults[] = $aRow;
355         }
356         $oResult->free();
357
358         return $aResults;
359     }
360
361     public function queryAll($sQuery = '') {
7ae5b0 362         return call_user_func_array(array(&$this, 'queryAllRecords'), func_get_args());
cd8bb8 363     }
MC 364
365     public function query_all($sQuery = '') {
7ae5b0 366         return call_user_func_array(array(&$this, 'queryAllRecords'), func_get_args());
cd8bb8 367     }
MC 368
369     /**
370      * Execute a query and return all rows as simple array
371      *
372      * Executes a query and returns all result rows in an array with elements
373      * <strong>Only first column is returned</strong> Uses lots of memory on big result sets.
374      *
375      * @access public
376      * @param string  $sQuery query to execute
377      * @param ...     further params (see query())
378      * @return array all the rows in the result set
379      */
380     public function queryAllArray($sQuery = '') {
381         $aArgs = func_get_args();
382         $oResult = call_user_func_array(array(&$this, 'query'), $aArgs);
383         if(!$oResult) return array();
384
385         $aResults = array();
386         while($aRow = $oResult->get()) {
387             $aResults[] = reset($aRow);
388         }
389         $oResult->free();
390
391         return $aResults;
392     }
393
394     public function query_all_array($sQuery = '') {
395         return $this->queryAllArray($sQuery);
396     }
397
398
399
400     /**
401      * Get id of last inserted row
402      *
403      * Gives you the id of the last inserted row in a table with an auto-increment primary key
404      *
405      * @access public
406      * @return int id of last inserted row or 0 if none
407      */
408     public function insert_id() {
409         $iRes = mysqli_query($this->_iConnId, 'SELECT LAST_INSERT_ID() as `newid`');
410         if(!is_object($iRes)) return false;
411
412         $aReturn = mysqli_fetch_assoc($iRes);
413         mysqli_free_result($iRes);
414
415         return $aReturn['newid'];
416     }
417
418
419
420     /**
421      * get affected row count
422      *
423      * Gets the amount of rows affected by the previous query
424      *
425      * @access public
426      * @return int affected rows
427      */
428     public function affected() {
429         if(!is_object($this->_iConnId)) return 0;
430         $iRows = mysqli_affected_rows($this->_iConnId);
431         if(!$iRows) $iRows = 0;
432         return $iRows;
433     }
434
435
385f54 436     /**
MC 437      * check if a utf8 string is valid
438      *
439      * @access public
440      * @param string  $string the string to check
441      * @return bool true if it is valid utf8, false otherwise
442      */
443     private function check_utf8($str) {
444         $len = strlen($str);
445         for($i = 0; $i < $len; $i++){
446             $c = ord($str[$i]);
447             if ($c > 128) {
448                 if (($c > 247)) return false;
449                 elseif ($c > 239) $bytes = 4;
450                 elseif ($c > 223) $bytes = 3;
451                 elseif ($c > 191) $bytes = 2;
452                 else return false;
453                 if (($i + $bytes) > $len) return false;
454                 while ($bytes > 1) {
455                     $i++;
456                     $b = ord($str[$i]);
457                     if ($b < 128 || $b > 191) return false;
458                     $bytes--;
459                 }
460             }
461         }
462         return true;
463     } // end of check_utf8
cd8bb8 464
MC 465     /**
466      * Escape a string for usage in a query
467      *
468      * @access public
469      * @param string  $sString query string to escape
470      * @return string escaped string
471      */
472     public function escape($sString) {
473         global $app;
474         if(!is_string($sString) && !is_numeric($sString)) {
475             $app->log('NON-String given in escape function! (' . gettype($sString) . ')', LOGLEVEL_INFO);
563649 476             //$sAddMsg = getDebugBacktrace();
cd8bb8 477             $app->log($sAddMsg, LOGLEVEL_DEBUG);
MC 478             $sString = '';
479         }
480
385f54 481         $cur_encoding = mb_detect_encoding($sString);
cd8bb8 482         if($cur_encoding != "UTF-8") {
MC 483             if($cur_encoding != 'ASCII') {
385f54 484                 $app->log('String ' . substr($sString, 0, 25) . '... is ' . $cur_encoding . '.', LOGLEVEL_INFO);
cd8bb8 485                 if($cur_encoding) $sString = mb_convert_encoding($sString, 'UTF-8', $cur_encoding);
MC 486                 else $sString = mb_convert_encoding($sString, 'UTF-8');
487             }
385f54 488         } elseif(!$this->check_utf8($sString)) {
cd8bb8 489             $sString = utf8_encode($sString);
385f54 490         }
cd8bb8 491
MC 492         if($this->_iConnId) return mysqli_real_escape_string($this->_iConnId, $sString);
493         else return addslashes($sString);
494     }
495
496     /**
497      *
498      *
499      * @access private
500      */
501     private function _sqlerror($sErrormsg = 'Unbekannter Fehler', $sAddMsg = '') {
502         global $app, $conf;
503
504         $mysql_error = (is_object($this->_iConnId) ? mysqli_error($this->_iConnId) : mysqli_connect_error());
505         $mysql_errno = (is_object($this->_iConnId) ? mysqli_errno($this->_iConnId) : mysqli_connect_errno());
506
563649 507         //$sAddMsg .= getDebugBacktrace();
cd8bb8 508
MC 509         if($this->show_error_messages && $conf['demo_mode'] === false) {
510             echo $sErrormsg . $sAddMsg;
511         } else if(is_object($app) && method_exists($app, 'log')) {
a1918e 512                 $app->log($sErrormsg . $sAddMsg . ' -> ' . $mysql_errno . ' (' . $mysql_error . ')', LOGLEVEL_WARN);
cd8bb8 513             }
7fe908 514     }
MC 515
516     public function affectedRows() {
cd8bb8 517         return $this->affected();
7fe908 518     }
MC 519
520     // returns mySQL insert id
521     public function insertID() {
cd8bb8 522         return $this->insert_id();
7fe908 523     }
MC 524
525
526     //* Function to quote strings
527     public function quote($formfield) {
cd8bb8 528         return $this->escape($formfield);
7fe908 529     }
MC 530
531     //* Function to unquotae strings
532     public function unquote($formfield) {
533         return stripslashes($formfield);
534     }
535
536     public function toLower($record) {
537         if(is_array($record)) {
538             foreach($record as $key => $val) {
539                 $key = strtolower($key);
540                 $out[$key] = $val;
541             }
542         }
543         return $out;
544     }
a6a094 545     
MC 546     public function insertFromArray($tablename, $data) {
547         if(!is_array($data)) return false;
548         
549         $k_query = '';
550         $v_query = '';
551         
552         $params = array($tablename);
553         $v_params = array();
554         
555         foreach($data as $key => $value) {
556             $k_query .= ($k_query != '' ? ', ' : '') . '??';
557             $v_query .= ($v_query != '' ? ', ' : '') . '?';
558             $params[] = $key;
559             $v_params[] = $value;
560         }
561         
562         $query = 'INSERT INTO ?? (' . $k_query . ') VALUES (' . $v_query . ')';
563         return $this->query($query, true, $params + $v_params);
564     }
565     
7fe908 566     public function diffrec($record_old, $record_new) {
MC 567         $diffrec_full = array();
568         $diff_num = 0;
569
570         if(is_array($record_old) && count($record_old) > 0) {
571             foreach($record_old as $key => $val) {
572                 // if(!isset($record_new[$key]) || $record_new[$key] != $val) {
573                 if(@$record_new[$key] != $val) {
574                     // Record has changed
575                     $diffrec_full['old'][$key] = $val;
576                     $diffrec_full['new'][$key] = @$record_new[$key];
577                     $diff_num++;
578                 } else {
579                     $diffrec_full['old'][$key] = $val;
580                     $diffrec_full['new'][$key] = $val;
581                 }
582             }
583         } elseif(is_array($record_new)) {
584             foreach($record_new as $key => $val) {
585                 if(isset($record_new[$key]) && @$record_old[$key] != $val) {
586                     // Record has changed
587                     $diffrec_full['new'][$key] = $val;
588                     $diffrec_full['old'][$key] = @$record_old[$key];
589                     $diff_num++;
590                 } else {
591                     $diffrec_full['new'][$key] = $val;
592                     $diffrec_full['old'][$key] = $val;
593                 }
594             }
595         }
596
597         return array('diff_num' => $diff_num, 'diff_rec' => $diffrec_full);
598
599     }
600
601     //** Function to fill the datalog with a full differential record.
602     public function datalogSave($db_table, $action, $primary_field, $primary_id, $record_old, $record_new, $force_update = false) {
603         global $app, $conf;
604
94b44c 605         // Check fields
7272e4 606         if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$db_table)) $app->error('Invalid table name '.$db_table);
cd48c7 607         if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$primary_field)) $app->error('Invalid primary field '.$primary_field.' in table '.$db_table);
94b44c 608         
TB 609         $primary_id = intval($primary_id);
f5b0ca 610
b0eb45 611         if($force_update == true) {
T 612             //* We force a update even if no record has changed
7fe908 613             $diffrec_full = array('new' => $record_new, 'old' => $record_old);
b0eb45 614             $diff_num = count($record_new);
T 615         } else {
616             //* get the difference record between old and new record
617             $tmp = $this->diffrec($record_old, $record_new);
618             $diffrec_full = $tmp['diff_rec'];
619             $diff_num = $tmp['diff_num'];
620             unset($tmp);
621         }
f5b0ca 622
7fe908 623         // Insert the server_id, if the record has a server_id
MC 624         $server_id = (isset($record_old['server_id']) && $record_old['server_id'] > 0)?$record_old['server_id']:0;
625         if(isset($record_new['server_id'])) $server_id = $record_new['server_id'];
cd8bb8 626
f5b0ca 627
7fe908 628         if($diff_num > 0) {
MC 629             //print_r($diff_num);
630             //print_r($diffrec_full);
cd8bb8 631             $diffstr = serialize($diffrec_full);
MC 632             $username = $_SESSION['s']['user']['username'];
7fe908 633             $dbidx = $primary_field.':'.$primary_id;
f5b0ca 634
7fe908 635             if($action == 'INSERT') $action = 'i';
MC 636             if($action == 'UPDATE') $action = 'u';
637             if($action == 'DELETE') $action = 'd';
cd8bb8 638             $sql = "INSERT INTO sys_datalog (dbtable,dbidx,server_id,action,tstamp,user,data) VALUES (?, ?, ?, ?, ?, ?, ?)";
MC 639             $app->db->query($sql, $db_table, $dbidx, $server_id, $action, time(), $username, $diffstr);
7fe908 640         }
f5b0ca 641
7fe908 642         return true;
MC 643     }
f5b0ca 644
7fe908 645     //** Inserts a record and saves the changes into the datalog
MC 646     public function datalogInsert($tablename, $insert_data, $index_field) {
647         global $app;
e9a57d 648         
94b44c 649         // Check fields
7272e4 650         if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$tablename)) $app->error('Invalid table name '.$tablename);
cd48c7 651         if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$index_field)) $app->error('Invalid index field '.$index_field.' in table '.$tablename);
94b44c 652         
7fe908 653         if(is_array($insert_data)) {
ee260d 654             $key_str = '';
T 655             $val_str = '';
2af58c 656             $params = array($tablename);
MC 657             $v_params = array();
ee260d 658             foreach($insert_data as $key => $val) {
a6e3ae 659                 $key_str .= '??,';
2af58c 660                 $params[] = $key;
MC 661                 
662                 $val_str .= '?,';
663                 $v_params[] = $val;
ee260d 664             }
7fe908 665             $key_str = substr($key_str, 0, -1);
MC 666             $val_str = substr($val_str, 0, -1);
ee260d 667             $insert_data_str = '('.$key_str.') VALUES ('.$val_str.')';
2af58c 668             $this->query("INSERT INTO ?? $insert_data_str", true, $params + $v_params);
ee260d 669         } else {
2af58c 670             /* TODO: deprecate this method! */
ee260d 671             $insert_data_str = $insert_data;
2af58c 672             $this->query("INSERT INTO ?? $insert_data_str", $tablename);
3a11d2 673             $app->log("deprecated use of passing values to datalogInsert() - table " . $tablename, 1);
ee260d 674         }
2af58c 675         
7fe908 676         $old_rec = array();
MC 677         $index_value = $this->insertID();
cd8bb8 678         $new_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ? = ?", $tablename, $index_field, $index_value);
7fe908 679         $this->datalogSave($tablename, 'INSERT', $index_field, $index_value, $old_rec, $new_rec);
f5b0ca 680
7fe908 681         return $index_value;
MC 682     }
f5b0ca 683
7fe908 684     //** Updates a record and saves the changes into the datalog
MC 685     public function datalogUpdate($tablename, $update_data, $index_field, $index_value, $force_update = false) {
ee260d 686         global $app;
7fe908 687
94b44c 688         // Check fields
7272e4 689         if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$tablename)) $app->error('Invalid table name '.$tablename);
cd48c7 690         if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$index_field)) $app->error('Invalid index field '.$index_field.' in table '.$tablename);
94b44c 691         
cd8bb8 692         $old_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
7fe908 693
MC 694         if(is_array($update_data)) {
2af58c 695             $params = array($tablename);
ee260d 696             $update_data_str = '';
T 697             foreach($update_data as $key => $val) {
2af58c 698                 $update_data_str .= '?? = ?,';
MC 699                 $params[] = $key;
700                 $params[] = $val;
ee260d 701             }
2af58c 702             $params[] = $index_field;
MC 703             $params[] = $index_value;
7fe908 704             $update_data_str = substr($update_data_str, 0, -1);
2af58c 705             $this->query("UPDATE ?? SET $update_data_str WHERE ?? = ?", true, $params);
ee260d 706         } else {
2af58c 707             /* TODO: deprecate this method! */
ee260d 708             $update_data_str = $update_data;
2af58c 709             $this->query("UPDATE ?? SET $update_data_str WHERE ?? = ?", $tablename, $index_field, $index_value);
3a11d2 710             $app->log("deprecated use of passing values to datalogUpdate() - table " . $tablename, 1);
ee260d 711         }
f5b0ca 712
cd8bb8 713         $new_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
7fe908 714         $this->datalogSave($tablename, 'UPDATE', $index_field, $index_value, $old_rec, $new_rec, $force_update);
f5b0ca 715
7fe908 716         return true;
MC 717     }
f5b0ca 718
7fe908 719     //** Deletes a record and saves the changes into the datalog
MC 720     public function datalogDelete($tablename, $index_field, $index_value) {
721         global $app;
f5b0ca 722
94b44c 723         // Check fields
7272e4 724         if(!preg_match('/^[a-zA-Z0-9\-\_\.]{1,64}$/',$tablename)) $app->error('Invalid table name '.$tablename);
cd48c7 725         if(!preg_match('/^[a-zA-Z0-9\-\_]{1,64}$/',$index_field)) $app->error('Invalid index field '.$index_field.' in table '.$tablename);
94b44c 726         
cd8bb8 727         $old_rec = $this->queryOneRecord("SELECT * FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
MC 728         $this->query("DELETE FROM ?? WHERE ?? = ?", $tablename, $index_field, $index_value);
7fe908 729         $new_rec = array();
MC 730         $this->datalogSave($tablename, 'DELETE', $index_field, $index_value, $old_rec, $new_rec);
731
732         return true;
733     }
734
735     //* get the current datalog status for the specified login (or currently logged in user)
736     public function datalogStatus($login = '') {
737         global $app;
738
739         $return = array('count' => 0, 'entries' => array());
740         if($_SESSION['s']['user']['typ'] == 'admin') return $return; // these information should not be displayed to admin users
741
742         if($login == '' && isset($_SESSION['s']['user'])) {
743             $login = $_SESSION['s']['user']['username'];
744         }
745
cd8bb8 746         $result = $this->queryAllRecords("SELECT COUNT( * ) AS cnt, sys_datalog.action, sys_datalog.dbtable FROM sys_datalog, server WHERE server.server_id = sys_datalog.server_id AND sys_datalog.user = ? AND sys_datalog.datalog_id > server.updated GROUP BY sys_datalog.dbtable, sys_datalog.action", $login);
7fe908 747         foreach($result as $row) {
MC 748             if(!$row['dbtable'] || in_array($row['dbtable'], array('aps_instances', 'aps_instances_settings', 'mail_access', 'mail_content_filter'))) continue; // ignore some entries, maybe more to come
749             $return['entries'][] = array('table' => $row['dbtable'], 'action' => $row['action'], 'count' => $row['cnt'], 'text' => $app->lng('datalog_status_' . $row['action'] . '_' . $row['dbtable']));
750             $return['count'] += $row['cnt'];
751         }
752         unset($result);
753
754         return $return;
755     }
f5b0ca 756
7fe908 757     /*
f5b0ca 758        $columns = array(action =>   add | alter | drop
N 759        name =>     Spaltenname
760        name_new => neuer Spaltenname, nur bei 'alter' belegt
761        type =>     42go-Meta-Type: int16, int32, int64, double, char, varchar, text, blob
762        typeValue => Wert z.B. bei Varchar
763        defaultValue =>  Default Wert
764        notNull =>   true | false
765        autoInc =>   true | false
766        option =>   unique | primary | index)
767
768
769      */
770
7fe908 771     public function createTable($table_name, $columns) {
MC 772         $index = '';
cd8bb8 773         $sql = "CREATE TABLE ?? (";
7fe908 774         foreach($columns as $col){
MC 775             $sql .= $col['name'].' '.$this->mapType($col['type'], $col['typeValue']).' ';
f5b0ca 776
7fe908 777             if($col['defaultValue'] != '') $sql .= "DEFAULT '".$col['defaultValue']."' ";
MC 778             if($col['notNull'] == true) {
779                 $sql .= 'NOT NULL ';
780             } else {
781                 $sql .= 'NULL ';
782             }
783             if($col['autoInc'] == true) $sql .= 'auto_increment ';
784             $sql.= ',';
785             // key Definitionen
786             if($col['option'] == 'primary') $index .= 'PRIMARY KEY ('.$col['name'].'),';
787             if($col['option'] == 'index') $index .= 'INDEX ('.$col['name'].'),';
788             if($col['option'] == 'unique') $index .= 'UNIQUE ('.$col['name'].'),';
789         }
790         $sql .= $index;
791         $sql = substr($sql, 0, -1);
792         $sql .= ')';
cd8bb8 793         /* TODO: secure parameters */
MC 794         $this->query($sql, $table_name);
7fe908 795         return true;
f5b0ca 796     }
N 797
7fe908 798     /*
f5b0ca 799        $columns = array(action =>   add | alter | drop
N 800        name =>     Spaltenname
801        name_new => neuer Spaltenname, nur bei 'alter' belegt
802        type =>     42go-Meta-Type: int16, int32, int64, double, char, varchar, text, blob
803        typeValue => Wert z.B. bei Varchar
804        defaultValue =>  Default Wert
805        notNull =>   true | false
806        autoInc =>   true | false
807        option =>   unique | primary | index)
808
809
810      */
7fe908 811     public function alterTable($table_name, $columns) {
MC 812         $index = '';
cd8bb8 813         $sql = "ALTER TABLE ?? ";
7fe908 814         foreach($columns as $col){
MC 815             if($col['action'] == 'add') {
816                 $sql .= 'ADD '.$col['name'].' '.$this->mapType($col['type'], $col['typeValue']).' ';
817             } elseif ($col['action'] == 'alter') {
818                 $sql .= 'CHANGE '.$col['name'].' '.$col['name_new'].' '.$this->mapType($col['type'], $col['typeValue']).' ';
819             } elseif ($col['action'] == 'drop') {
820                 $sql .= 'DROP '.$col['name'].' ';
821             }
822             if($col['action'] != 'drop') {
823                 if($col['defaultValue'] != '') $sql .= "DEFAULT '".$col['defaultValue']."' ";
824                 if($col['notNull'] == true) {
825                     $sql .= 'NOT NULL ';
826                 } else {
827                     $sql .= 'NULL ';
828                 }
829                 if($col['autoInc'] == true) $sql .= 'auto_increment ';
830                 $sql.= ',';
831                 // Index definitions
832                 if($col['option'] == 'primary') $index .= 'PRIMARY KEY ('.$col['name'].'),';
833                 if($col['option'] == 'index') $index .= 'INDEX ('.$col['name'].'),';
834                 if($col['option'] == 'unique') $index .= 'UNIQUE ('.$col['name'].'),';
835             }
836         }
837         $sql .= $index;
838         $sql = substr($sql, 0, -1);
cd8bb8 839         /* TODO: secure parameters */
7fe908 840         //die($sql);
cd8bb8 841         $this->query($sql, $table_name);
7fe908 842         return true;
f5b0ca 843     }
7fe908 844
MC 845     public function dropTable($table_name) {
846         $this->check($table_name);
cd8bb8 847         $sql = "DROP TABLE ??";
MC 848         return $this->query($sql, $table_name);
f5b0ca 849     }
N 850
7fe908 851     // gibt Array mit Tabellennamen zur�ck
MC 852     public function getTables($database_name = '') {
cd8bb8 853         if(!is_object($this->_iConnId)) return false;
7fe908 854         if($database_name == '') $database_name = $this->dbName;
cd8bb8 855         $tb_names = $this->queryAllArray("SHOW TABLES FROM ??", $database_name);
7fe908 856         return $tb_names;
MC 857     }
f5b0ca 858
7fe908 859     // gibt Feldinformationen zur Tabelle zur�ck
MC 860     /*
f5b0ca 861        $columns = array(action =>   add | alter | drop
N 862        name =>     Spaltenname
863        name_new => neuer Spaltenname, nur bei 'alter' belegt
864        type =>     42go-Meta-Type: int16, int32, int64, double, char, varchar, text, blob
865        typeValue => Wert z.B. bei Varchar
866        defaultValue =>  Default Wert
867        notNull =>   true | false
868        autoInc =>   true | false
869        option =>   unique | primary | index)
870
871
872      */
873
7fe908 874     function tableInfo($table_name) {
f5b0ca 875
7fe908 876         global $go_api, $go_info, $app;
MC 877         // Tabellenfelder einlesen
f5b0ca 878
cd8bb8 879         if($rows = $app->db->queryAllRecords('SHOW FIELDS FROM ??', $table_name)){
7fe908 880             foreach($rows as $row) {
MC 881                 /*
f5b0ca 882       $name = $row[0];
N 883       $default = $row[4];
884       $key = $row[3];
885       $extra = $row[5];
886       $isnull = $row[2];
887       $type = $row[1];
bfcdef 888       */
7fe908 889
MC 890                 $name = $row['Field'];
891                 $default = $row['Default'];
892                 $key = $row['Key'];
893                 $extra = $row['Extra'];
894                 $isnull = $row['Null'];
895                 $type = $row['Type'];
f5b0ca 896
N 897
7fe908 898                 $column = array();
f5b0ca 899
7fe908 900                 $column['name'] = $name;
MC 901                 //$column['type'] = $type;
902                 $column['defaultValue'] = $default;
903                 if(stristr($key, 'PRI')) $column['option'] = 'primary';
904                 if(stristr($isnull, 'YES')) {
905                     $column['notNull'] = false;
906                 } else {
907                     $column['notNull'] = true;
908                 }
909                 if($extra == 'auto_increment') $column['autoInc'] = true;
f5b0ca 910
N 911
7fe908 912                 // Type in Metatype umsetzen
f5b0ca 913
7fe908 914                 if(stristr($type, 'int(')) $metaType = 'int32';
MC 915                 if(stristr($type, 'bigint')) $metaType = 'int64';
916                 if(stristr($type, 'char')) {
917                     $metaType = 'char';
918                     $tmp_typeValue = explode('(', $type);
919                     $column['typeValue'] = substr($tmp_typeValue[1], 0, -1);
920                 }
921                 if(stristr($type, 'varchar')) {
922                     $metaType = 'varchar';
923                     $tmp_typeValue = explode('(', $type);
924                     $column['typeValue'] = substr($tmp_typeValue[1], 0, -1);
925                 }
926                 if(stristr($type, 'text')) $metaType = 'text';
927                 if(stristr($type, 'double')) $metaType = 'double';
928                 if(stristr($type, 'blob')) $metaType = 'blob';
f5b0ca 929
N 930
7fe908 931                 $column['type'] = $metaType;
f5b0ca 932
7fe908 933                 $columns[] = $column;
MC 934             }
935             return $columns;
936         } else {
937             return false;
938         }
f5b0ca 939
N 940
7fe908 941         //$this->createTable('tester',$columns);
f5b0ca 942
7fe908 943         /*
f5b0ca 944      $result = mysql_list_fields($go_info["server"]["db_name"],$table_name);
N 945      $fields = mysql_num_fields ($result);
946      $i = 0;
947      $table = mysql_field_table ($result, $i);
948      while ($i < $fields) {
949      $name  = mysql_field_name  ($result, $i);
950      $type  = mysql_field_type  ($result, $i);
951      $len   = mysql_field_len   ($result, $i);
952      $flags = mysql_field_flags ($result, $i);
953      print_r($flags);
954
955      $columns = array(name => $name,
956      type =>     "",
957      defaultValue =>  "",
958      isnull =>   1,
959      option =>   "");
960      $returnvar[] = $columns;
961
962      $i++;
963      }
964        */
965
966
967
7fe908 968     }
f5b0ca 969
7fe908 970     public function mapType($metaType, $typeValue) {
MC 971         global $go_api;
972         $metaType = strtolower($metaType);
973         switch ($metaType) {
974         case 'int16':
975             return 'smallint';
976             break;
977         case 'int32':
978             return 'int';
979             break;
980         case 'int64':
981             return 'bigint';
982             break;
983         case 'double':
984             return 'double';
985             break;
986         case 'char':
987             return 'char';
988             break;
989         case 'varchar':
990             if($typeValue < 1) die('Database failure: Lenght required for these data types.');
991             return 'varchar('.$typeValue.')';
992             break;
993         case 'text':
994             return 'text';
995             break;
996         case 'blob':
997             return 'blob';
998             break;
8748b3 999         case 'date':
TB 1000             return 'date';
1001             break;
7fe908 1002         }
MC 1003     }
f5b0ca 1004
7fe908 1005 }
f5b0ca 1006
cd8bb8 1007 /**
MC 1008  * database query result class
1009  *
1010  * @package pxFramework
1011  *
1012  */
1013 class db_result {
1014
1015     /**
1016      *
1017      *
1018      * @access private
1019      */
1020     private $_iResId = null;
1021     private $_iConnection = null;
1022
1023
1024
1025     /**
1026      *
1027      *
1028      * @access private
1029      */
1030     public function db_result($iResId, $iConnection) {
1031         $this->_iResId = $iResId;
1032         $this->_iConnection = $iConnection;
1033     }
1034
1035
1036
1037     /**
1038      * get count of result rows
1039      *
1040      * Returns the amount of rows in the result set
1041      *
1042      * @access public
1043      * @return int amount of rows
1044      */
1045     public function rows() {
1046         if(!is_object($this->_iResId)) return 0;
1047         $iRows = mysqli_num_rows($this->_iResId);
1048         if(!$iRows) $iRows = 0;
1049         return $iRows;
1050     }
1051
1052
1053
1054     /**
1055      * Get number of affected rows
1056      *
1057      * Returns the amount of rows affected by the previous query
1058      *
1059      * @access public
1060      * @return int amount of affected rows
1061      */
1062     public function affected() {
1063         if(!is_object($this->_iConnection)) return 0;
1064         $iRows = mysqli_affected_rows($this->_iConnection);
1065         if(!$iRows) $iRows = 0;
1066         return $iRows;
1067     }
1068
1069
1070
1071     /**
1072      * Frees the result set
1073      *
1074      * @access public
1075      */
1076     public function free() {
1077         if(!is_object($this->_iResId)) return;
1078
1079         mysqli_free_result($this->_iResId);
1080         return;
1081     }
1082
1083
1084
1085     /**
1086      * Get a result row (associative)
1087      *
1088      * Returns the next row in the result set. To be used in a while loop like while($currow = $result->get()) { do something ... }
1089      *
1090      * @access public
1091      * @return array result row
1092      */
1093     public function get() {
1094         $aItem = null;
1095
1096         if(is_object($this->_iResId)) {
1097             $aItem = mysqli_fetch_assoc($this->_iResId);
1098             if(!$aItem) $aItem = null;
1099         }
1100         return $aItem;
1101     }
1102
1103
1104
1105     /**
1106      * Get a result row (array with numeric index)
1107      *
1108      * @access public
1109      * @return array result row
1110      */
1111     public function getAsRow() {
1112         $aItem = null;
1113
1114         if(is_object($this->_iResId)) {
1115             $aItem = mysqli_fetch_row($this->_iResId);
1116             if(!$aItem) $aItem = null;
1117         }
1118         return $aItem;
1119     }
1120
1121 }
1122
1123 /**
1124  * database query result class
1125  *
1126  * emulates a db result set out of an array so you can use array results and db results the same way
1127  *
1128  * @package pxFramework
1129  * @see db_result
1130  *
1131  *
1132  */
1133 class fakedb_result {
1134
1135     /**
1136      *
1137      *
1138      * @access private
1139      */
1140     private $aResultData = array();
1141
1142     /**
1143      *
1144      *
1145      * @access private
1146      */
1147     private $aLimitedData = array();
1148
1149
1150
1151     /**
1152      *
1153      *
1154      * @access private
1155      */
1156     public function fakedb_result($aData) {
1157         $this->aResultData = $aData;
1158         $this->aLimitedData = $aData;
1159         reset($this->aLimitedData);
1160     }
1161
1162
1163
1164     /**
1165      * get count of result rows
1166      *
1167      * Returns the amount of rows in the result set
1168      *
1169      * @access public
1170      * @return int amount of rows
1171      */
1172     // Gibt die Anzahl Zeilen zurück
1173     public function rows() {
1174         return count($this->aLimitedData);
1175     }
1176
1177
1178
1179     /**
1180      * Frees the result set
1181      *
1182      * @access public
1183      */
1184     // Gibt ein Ergebnisset frei
1185     public function free() {
1186         $this->aResultData = array();
1187         $this->aLimitedData = array();
1188         return;
1189     }
1190
1191
1192
1193     /**
1194      * Get a result row (associative)
1195      *
1196      * Returns the next row in the result set. To be used in a while loop like while($currow = $result->get()) { do something ... }
1197      *
1198      * @access public
1199      * @return array result row
1200      */
1201     // Gibt eine Ergebniszeile zurück
1202     public function get() {
1203         $aItem = null;
1204
1205         if(!is_array($this->aLimitedData)) return $aItem;
1206
1207         if(list($vKey, $aItem) = each($this->aLimitedData)) {
1208             if(!$aItem) $aItem = null;
1209         }
1210         return $aItem;
1211     }
1212
1213
1214
1215     /**
1216      * Get a result row (array with numeric index)
1217      *
1218      * @access public
1219      * @return array result row
1220      */
1221     public function getAsRow() {
1222         return $this->get();
1223     }
1224
1225
1226
1227     /**
1228      * Limit the result (like a LIMIT x,y in a SQL query)
1229      *
1230      * @access public
1231      * @param int     $iStart offset to start read
1232      * @param int     iLength amount of datasets to read
1233      */
1234     public function limit_result($iStart, $iLength) {
1235         $this->aLimitedData = array_slice($this->aResultData, $iStart, $iLength, true);
1236     }
1237
1238 }
1239
1240
7fe908 1241 ?>