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