includes/dbal.php
changeset 326 ab66d6d1f1f4
parent 313 854eecfada20
parent 322 5f1cd51bf1be
child 331 03850e101d7f
equal deleted inserted replaced
314:474f8be55943 326:ab66d6d1f1f4
   118                       Most recent SQL query:</p>
   118                       Most recent SQL query:</p>
   119                       <pre>'.$bt.'</pre>';
   119                       <pre>'.$bt.'</pre>';
   120     return $internal_text;
   120     return $internal_text;
   121   }
   121   }
   122   
   122   
   123   function connect()
   123   function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false)
   124   {
   124   {
   125     $this->enable_errorhandler();
   125     $this->enable_errorhandler();
   126     
   126     
   127     if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
   127     define('ENANO_DBLAYER', 'MYSQL');
   128     {
   128     define('ENANO_SQLFUNC_LOWERCASE', 'lcase');
   129       @include(ENANO_ROOT.'/config.new.php');
   129     define('ENANO_SQL_MULTISTRING_PRFIX', '');
   130     }
   130     define('ENANO_SQL_BOOLEAN_TRUE', 'true');
   131     else
   131     define('ENANO_SQL_BOOLEAN_FALSE', 'false');
   132     {
   132     
   133       @include(ENANO_ROOT.'/config.php');
   133     if ( !$manual_credentials )
   134     }
   134     {
       
   135       if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
       
   136       {
       
   137         @include(ENANO_ROOT.'/config.new.php');
       
   138       }
       
   139       else
       
   140       {
       
   141         @include(ENANO_ROOT.'/config.php');
       
   142       }
       
   143         
       
   144       if ( isset($crypto_key) )
       
   145         unset($crypto_key); // Get this sucker out of memory fast
   135       
   146       
   136     if ( isset($crypto_key) )
   147       if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
   137       unset($crypto_key); // Get this sucker out of memory fast
   148       {
   138     
   149         // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
   139     if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
   150         if ( !defined('scriptPath') )
   140     {
       
   141       // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
       
   142       if ( !defined('scriptPath') )
       
   143       {
       
   144         if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
       
   145         {
   151         {
   146           $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
   152           if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
       
   153           {
       
   154             $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
       
   155           }
       
   156           if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
       
   157           {
       
   158             // user requested http://foo/enano as opposed to http://foo/enano/index.php
       
   159             $_SERVER['REQUEST_URI'] .= '/index.php';
       
   160           }
       
   161           $sp = dirname($_SERVER['REQUEST_URI']);
       
   162           if($sp == '/' || $sp == '\\') $sp = '';
       
   163           define('scriptPath', $sp);
       
   164           define('contentPath', "$sp/index.php?title=");
   147         }
   165         }
   148         if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
   166         $loc = scriptPath . '/install.php';
   149         {
   167         // header("Location: $loc");
   150           // user requested http://foo/enano as opposed to http://foo/enano/index.php
   168         redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
   151           $_SERVER['REQUEST_URI'] .= '/index.php';
   169         exit;
   152         }
   170       }
   153         $sp = dirname($_SERVER['REQUEST_URI']);
   171     }
   154         if($sp == '/' || $sp == '\\') $sp = '';
   172     
   155         define('scriptPath', $sp);
       
   156         define('contentPath', "$sp/index.php?title=");
       
   157       }
       
   158       $loc = scriptPath . '/install.php';
       
   159       // header("Location: $loc");
       
   160       redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
       
   161       exit;
       
   162     }
       
   163     $this->_conn = @mysql_connect($dbhost, $dbuser, $dbpasswd);
   173     $this->_conn = @mysql_connect($dbhost, $dbuser, $dbpasswd);
   164     unset($dbuser);
   174     unset($dbuser);
   165     unset($dbpasswd); // Security
   175     unset($dbpasswd); // Security
   166     
   176     
   167     if ( !$this->_conn )
   177     if ( !$this->_conn )
   776           </div>';
   786           </div>';
   777     $template->footer();
   787     $template->footer();
   778   }
   788   }
   779 }
   789 }
   780 
   790 
       
   791 class postgresql {
       
   792   var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
       
   793   var $row = array();
       
   794 	var $rowset = array();
       
   795   var $errhandler;
       
   796   
       
   797   function enable_errorhandler()
       
   798   {
       
   799     // echo "DBAL: enabling error handler<br />";
       
   800     if ( function_exists('debug_backtrace') )
       
   801     {
       
   802       $this->errhandler = set_error_handler('db_error_handler');
       
   803     }
       
   804   }
       
   805   
       
   806   function disable_errorhandler()
       
   807   {
       
   808     // echo "DBAL: disabling error handler<br />";
       
   809     if ( $this->errhandler )
       
   810     {
       
   811       set_error_handler($this->errhandler);
       
   812     }
       
   813     else
       
   814     {
       
   815       restore_error_handler();
       
   816     }
       
   817   }
       
   818   
       
   819   function sql_backtrace()
       
   820   {
       
   821     return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
       
   822   }
       
   823   
       
   824   function ensure_connection()
       
   825   {
       
   826     if(!$this->_conn)
       
   827     {
       
   828       $this->connect();
       
   829     }
       
   830   }
       
   831   
       
   832   function _die($t = '') {
       
   833     if(defined('ENANO_HEADERS_SENT')) {
       
   834       ob_clean();
       
   835     }
       
   836     header('HTTP/1.1 500 Internal Server Error');
       
   837     $bt = $this->latest_query; // $this->sql_backtrace();
       
   838     $e = htmlspecialchars(pg_last_error());
       
   839     if($e=='') $e='&lt;none&gt;';
       
   840     $t = ( !empty($t) ) ? $t : '&lt;No error description provided&gt;';
       
   841     global $email;
       
   842     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
       
   843     $internal_text = '<h3>The site was unable to finish serving your request.</h3>
       
   844                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
       
   845                       <p>Description or location of error: '.$t.'<br />
       
   846                       Error returned by PostgreSQL extension: ' . $e . '<br />
       
   847                       Most recent SQL query:</p>
       
   848                       <pre>'.$bt.'</pre>';
       
   849     if(defined('ENANO_CONFIG_FETCHED')) die_semicritical('Database error', $internal_text);
       
   850     else                                   grinding_halt('Database error', $internal_text);
       
   851     exit;
       
   852   }
       
   853   
       
   854   function die_json()
       
   855   {
       
   856     $e = addslashes(htmlspecialchars(pg_last_error()));
       
   857     $q = addslashes($this->latest_query);
       
   858     $t = "{'mode':'error','error':'An error occurred during database query.\nQuery was:\n  $q\n\nError returned by PostgreSQL: $e'}";
       
   859     die($t);
       
   860   }
       
   861   
       
   862   function get_error($t = '') {
       
   863     header('HTTP/1.1 500 Internal Server Error');
       
   864     $bt = $this->sql_backtrace();
       
   865     $e = htmlspecialchars(pg_last_error());
       
   866     if($e=='') $e='&lt;none&gt;';
       
   867     global $email;
       
   868     $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) ) ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;' : '';
       
   869     $internal_text = '<h3>The site was unable to finish serving your request.</h3>
       
   870                       <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site' . $email_info . '.</p>
       
   871                       <p>Description or location of error: '.$t.'<br />
       
   872                       Error returned by MySQL extension: ' . $e . '<br />
       
   873                       Most recent SQL query:</p>
       
   874                       <pre>'.$bt.'</pre>';
       
   875     return $internal_text;
       
   876   }
       
   877   
       
   878   function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false)
       
   879   {
       
   880     $this->enable_errorhandler();
       
   881     
       
   882     define('ENANO_DBLAYER', 'PGSQL');
       
   883     define('ENANO_SQLFUNC_LOWERCASE', 'lower');
       
   884     define('ENANO_SQL_MULTISTRING_PRFIX', 'E');
       
   885     define('ENANO_SQL_BOOLEAN_TRUE', '1');
       
   886     define('ENANO_SQL_BOOLEAN_FALSE', '0');
       
   887     
       
   888     if ( !$manual_credentials )
       
   889     {
       
   890       if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
       
   891       {
       
   892         @include(ENANO_ROOT.'/config.new.php');
       
   893       }
       
   894       else
       
   895       {
       
   896         @include(ENANO_ROOT.'/config.php');
       
   897       }
       
   898         
       
   899       if ( isset($crypto_key) )
       
   900         unset($crypto_key); // Get this sucker out of memory fast
       
   901       
       
   902       if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
       
   903       {
       
   904         // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
       
   905         if ( !defined('scriptPath') )
       
   906         {
       
   907           if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
       
   908           {
       
   909             $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
       
   910           }
       
   911           if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
       
   912           {
       
   913             // user requested http://foo/enano as opposed to http://foo/enano/index.php
       
   914             $_SERVER['REQUEST_URI'] .= '/index.php';
       
   915           }
       
   916           $sp = dirname($_SERVER['REQUEST_URI']);
       
   917           if($sp == '/' || $sp == '\\') $sp = '';
       
   918           define('scriptPath', $sp);
       
   919           define('contentPath', "$sp/index.php?title=");
       
   920         }
       
   921         $loc = scriptPath . '/install.php';
       
   922         // header("Location: $loc");
       
   923         redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
       
   924         exit;
       
   925       }
       
   926     }
       
   927     $this->_conn = @pg_connect("host=$dbhost port=5432 dbname=$dbname user=$dbuser password=$dbpasswd");
       
   928     unset($dbuser);
       
   929     unset($dbpasswd); // Security
       
   930     
       
   931     if ( !$this->_conn )
       
   932     {
       
   933       grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to PostgreSQL.<br />'.pg_last_error().'</p>');
       
   934     }
       
   935     
       
   936     // Reset some variables
       
   937     $this->query_backtrace = array();
       
   938     $this->query_times = array();
       
   939     $this->query_sources = array();
       
   940     $this->num_queries = 0;
       
   941     
       
   942     $this->debug = ( defined('ENANO_DEBUG') );
       
   943     
       
   944     // We're in!
       
   945     $this->disable_errorhandler();
       
   946     return true;
       
   947   }
       
   948   
       
   949   function sql_query($q)
       
   950   {
       
   951     $this->enable_errorhandler();
       
   952     
       
   953     if ( $this->debug && function_exists('debug_backtrace') )
       
   954     {
       
   955       $backtrace = @debug_backtrace();
       
   956       if ( is_array($backtrace) )
       
   957       {
       
   958         $bt = $backtrace[0];
       
   959         if ( isset($backtrace[1]['class']) )
       
   960         {
       
   961           if ( $backtrace[1]['class'] == 'sessionManager' )
       
   962           {
       
   963             $bt = $backtrace[1];
       
   964           }
       
   965         }
       
   966         $this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line'];
       
   967       }
       
   968       unset($backtrace);
       
   969     }
       
   970     
       
   971     $this->num_queries++;
       
   972     $this->query_backtrace[] = $q;
       
   973     $this->latest_query = $q;
       
   974     // First make sure we have a connection
       
   975     if ( !$this->_conn )
       
   976     {
       
   977       $this->_die('A database connection has not yet been established.');
       
   978     }
       
   979     // Does this query look malicious?
       
   980     if ( !$this->check_query($q) )
       
   981     {
       
   982       $this->report_query($q);
       
   983       grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
       
   984     }
       
   985     
       
   986     $time_start = microtime_float();
       
   987     $r = pg_query($q);
       
   988     $this->query_times[$q] = microtime_float() - $time_start;
       
   989     $this->latest_result = $r;
       
   990     $this->disable_errorhandler();
       
   991     return $r;
       
   992   }
       
   993   
       
   994   function sql_unbuffered_query($q)
       
   995   {
       
   996     $this->enable_errorhandler();
       
   997     
       
   998     $this->num_queries++;
       
   999     $this->query_backtrace[] = '(UNBUFFERED) ' . $q;
       
  1000     $this->latest_query = $q;
       
  1001     // First make sure we have a connection
       
  1002     if ( !$this->_conn )
       
  1003     {
       
  1004       $this->_die('A database connection has not yet been established.');
       
  1005     }
       
  1006     // Does this query look malicious?
       
  1007     if ( !$this->check_query($q) )
       
  1008     {
       
  1009       $this->report_query($q);
       
  1010       grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
       
  1011     }
       
  1012     
       
  1013     $time_start = microtime_float();
       
  1014     $r = pg_query($q);
       
  1015     $this->query_times[$q] = microtime_float() - $time_start;
       
  1016     $this->latest_result = $r;
       
  1017     $this->disable_errorhandler();
       
  1018     return $r;
       
  1019   }
       
  1020   
       
  1021   /**
       
  1022    * Checks a SQL query for possible signs of injection attempts
       
  1023    * @param string $q the query to check
       
  1024    * @return bool true if query passed check, otherwise false
       
  1025    */
       
  1026   
       
  1027   function check_query($q, $debug = false)
       
  1028   {
       
  1029     if($debug) echo "\$db-&gt;check_query(): checking query: ".htmlspecialchars($q).'<br />'."\n";
       
  1030     $sz = strlen($q);
       
  1031     $quotechar = false;
       
  1032     $quotepos  = 0;
       
  1033     $prev_is_quote = false;
       
  1034     $just_started = false;
       
  1035     for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
       
  1036     {
       
  1037       $next = substr($q, $i+1, 1);
       
  1038       $next2 = substr($q, $i+2, 1);
       
  1039       $prev = substr($q, $i-1, 1);
       
  1040       $prev2 = substr($q, $i-2, 1);
       
  1041       if(isset($c) && in_array($c, Array('"', "'", '`')))
       
  1042       {
       
  1043         if($quotechar)
       
  1044         {
       
  1045           if (
       
  1046               ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
       
  1047               ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
       
  1048             )
       
  1049           {
       
  1050             $quotechar = false;
       
  1051             if($debug) echo('$db-&gt;check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '<br />');
       
  1052             $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
       
  1053             if($debug) echo('$db-&gt;check_query(): Filtered query: '.$q.'<br />');
       
  1054             $i = $quotepos;
       
  1055           }
       
  1056         }
       
  1057         else
       
  1058         {
       
  1059           $quotechar = $c;
       
  1060           $quotepos  = $i;
       
  1061           $just_started = true;
       
  1062         }
       
  1063         if($debug) echo '$db-&gt;check_query(): found quote char as pos: '.$i.'<br />';
       
  1064         continue;
       
  1065       }
       
  1066       $just_started = false;
       
  1067     }
       
  1068     if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
       
  1069     for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1))
       
  1070     {
       
  1071       if ( 
       
  1072            ( ( $c == ';' && $i != $sz-1 ) || $c . substr($q, $i+1, 1) == '--' )
       
  1073         || ( in_array($c, Array('"', "'", '`')) )
       
  1074          ) // Don't permit semicolons in mid-query, and never allow comments
       
  1075       {
       
  1076         // Injection attempt!
       
  1077         if($debug)
       
  1078         {
       
  1079           $e = '';
       
  1080           for($j=$i-5;$j<$i+5;$j++)
       
  1081           {
       
  1082             if($j == $i) $e .= '<span style="color: red; text-decoration: underline;">' . $c . '</span>';
       
  1083             else $e .= $c;
       
  1084           }
       
  1085           echo 'Injection attempt caught at pos: '.$i.'<br />';
       
  1086         }
       
  1087         return false;
       
  1088       }
       
  1089     }
       
  1090     if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
       
  1091     {
       
  1092       if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
       
  1093       return false;
       
  1094     }
       
  1095     return true;
       
  1096   }
       
  1097   
       
  1098   /**
       
  1099    * Set the internal result pointer to X
       
  1100    * @param int $pos The number of the row
       
  1101    * @param resource $result The MySQL result resource - if not given, the latest cached query is assumed
       
  1102    * @return true on success, false on failure
       
  1103    */
       
  1104    
       
  1105   function sql_data_seek($pos, $result = false)
       
  1106   {
       
  1107     $this->enable_errorhandler();
       
  1108     if(!$result)
       
  1109       $result = $this->latest_result;
       
  1110     if(!$result)
       
  1111     {
       
  1112       $this->disable_errorhandler();
       
  1113       return false;
       
  1114     }
       
  1115     if(pg_result_seek($result, $pos))
       
  1116     {
       
  1117       $this->disable_errorhandler();
       
  1118       return true;
       
  1119     }
       
  1120     else
       
  1121     {
       
  1122       $this->disable_errorhandler();
       
  1123       return false;
       
  1124     }
       
  1125   }
       
  1126   
       
  1127   /**
       
  1128    * Reports a bad query to the admin
       
  1129    * @param string $query the naughty query
       
  1130    * @access private
       
  1131    */
       
  1132    
       
  1133   function report_query($query)
       
  1134   {
       
  1135     global $session;
       
  1136     if(is_object($session) && defined('ENANO_MAINSTREAM'))
       
  1137       $username = $session->username;
       
  1138     else
       
  1139       $username = 'Unavailable';
       
  1140     $query = $this->escape($query);
       
  1141     $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            edit_summary)
       
  1142                                                      VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', \''.$_SERVER['REMOTE_ADDR'].'\');');
       
  1143   }
       
  1144   
       
  1145   /**
       
  1146    * Returns the ID of the row last inserted.
       
  1147    * @return int
       
  1148    */
       
  1149   
       
  1150   function insert_id()
       
  1151   {
       
  1152     return @pg_last_oid();
       
  1153   }
       
  1154   
       
  1155   function fetchrow($r = false) {
       
  1156     $this->enable_errorhandler();
       
  1157     if(!$this->_conn) return false;
       
  1158     if(!$r) $r = $this->latest_result;
       
  1159     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
  1160     $row = pg_fetch_assoc($r);
       
  1161     $this->disable_errorhandler();
       
  1162     return $row;
       
  1163   }
       
  1164   
       
  1165   function fetchrow_num($r = false) {
       
  1166     $this->enable_errorhandler();
       
  1167     if(!$r) $r = $this->latest_result;
       
  1168     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
  1169     $row = pg_fetch_row($r);
       
  1170     $this->disable_errorhandler();
       
  1171     return $row;
       
  1172   }
       
  1173   
       
  1174   function numrows($r = false) {
       
  1175     $this->enable_errorhandler();
       
  1176     if(!$r) $r = $this->latest_result;
       
  1177     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
  1178     $n = pg_num_rows($r);
       
  1179     $this->disable_errorhandler();
       
  1180     return $n;
       
  1181   }
       
  1182   
       
  1183   function escape($str)
       
  1184   {
       
  1185     $this->enable_errorhandler();
       
  1186     $str = pg_escape_string($str);
       
  1187     $this->disable_errorhandler();
       
  1188     return $str;
       
  1189   }
       
  1190   
       
  1191   function free_result($result = false)
       
  1192   {
       
  1193     $this->enable_errorhandler();
       
  1194     if(!$result)
       
  1195       $result = $this->latest_result;
       
  1196     if(!$result)
       
  1197     {
       
  1198       $this->disable_errorhandler();
       
  1199       return null;
       
  1200     }
       
  1201     pg_free_result($result);
       
  1202     $this->disable_errorhandler();
       
  1203     return null;
       
  1204   }
       
  1205   
       
  1206   function close() {
       
  1207     pg_close($this->_conn);
       
  1208     unset($this->_conn);
       
  1209   }
       
  1210   
       
  1211   // phpBB DBAL compatibility
       
  1212   function sql_fetchrow($r = false)
       
  1213   {
       
  1214     return $this->fetchrow($r);
       
  1215   }
       
  1216   function sql_freeresult($r = false)
       
  1217   {
       
  1218     if(!$this->_conn) return false;
       
  1219     if(!$r) $r = $this->latest_result;
       
  1220     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
  1221     $this->free_result($r);
       
  1222   }
       
  1223   function sql_numrows($r = false)
       
  1224   {
       
  1225     return $this->numrows();
       
  1226   }
       
  1227   function sql_affectedrows($r = false, $f, $n)
       
  1228   {
       
  1229     if(!$this->_conn) return false;
       
  1230     if(!$r) $r = $this->latest_result;
       
  1231     if(!$r) $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
       
  1232     return pg_affected_rows();
       
  1233   }
       
  1234   
       
  1235   function sql_type_cast(&$value)
       
  1236 	{
       
  1237 		if ( is_float($value) )
       
  1238 		{
       
  1239 			return doubleval($value);
       
  1240 		}
       
  1241 		if ( is_integer($value) || is_bool($value) )
       
  1242 		{
       
  1243 			return intval($value);
       
  1244 		}
       
  1245 		if ( is_string($value) || empty($value) )
       
  1246 		{
       
  1247 			return '\'' . $this->sql_escape_string($value) . '\'';
       
  1248 		}
       
  1249 		// uncastable var : let's do a basic protection on it to prevent sql injection attempt
       
  1250 		return '\'' . $this->sql_escape_string(htmlspecialchars($value)) . '\'';
       
  1251 	}
       
  1252 
       
  1253 	function sql_statement(&$fields, $fields_inc='')
       
  1254 	{
       
  1255 		// init result
       
  1256 		$this->sql_fields = $this->sql_values = $this->sql_update = '';
       
  1257 		if ( empty($fields) && empty($fields_inc) )
       
  1258 		{
       
  1259 			return;
       
  1260 		}
       
  1261 
       
  1262 		// process
       
  1263 		if ( !empty($fields) )
       
  1264 		{
       
  1265 			$first = true;
       
  1266 			foreach ( $fields as $field => $value )
       
  1267 			{
       
  1268 				// field must contain a field name
       
  1269 				if ( !empty($field) && is_string($field) )
       
  1270 				{
       
  1271 					$value = $this->sql_type_cast($value);
       
  1272 					$this->sql_fields .= ( $first ? '' : ', ' ) . $field;
       
  1273 					$this->sql_values .= ( $first ? '' : ', ' ) . $value;
       
  1274 					$this->sql_update .= ( $first ? '' : ', ' ) . $field . ' = ' . $value;
       
  1275 					$first = false;
       
  1276 				}
       
  1277 			}
       
  1278 		}
       
  1279 		if ( !empty($fields_inc) )
       
  1280 		{
       
  1281 			foreach ( $fields_inc as $field => $indent )
       
  1282 			{
       
  1283 				if ( $indent != 0 )
       
  1284 				{
       
  1285 					$this->sql_update .= (empty($this->sql_update) ? '' : ', ') . $field . ' = ' . $field . ($indent < 0 ? ' - ' : ' + ') . abs($indent);
       
  1286 				}
       
  1287 			}
       
  1288 		}
       
  1289 	}
       
  1290 
       
  1291 	function sql_stack_reset($id='')
       
  1292 	{
       
  1293 		if ( empty($id) )
       
  1294 		{
       
  1295 			$this->sql_stack_fields = array();
       
  1296 			$this->sql_stack_values = array();
       
  1297 		}
       
  1298 		else
       
  1299 		{
       
  1300 			$this->sql_stack_fields[$id] = array();
       
  1301 			$this->sql_stack_values[$id] = array();
       
  1302 		}
       
  1303 	}
       
  1304 
       
  1305 	function sql_stack_statement(&$fields, $id='')
       
  1306 	{
       
  1307 		$this->sql_statement($fields);
       
  1308 		if ( empty($id) )
       
  1309 		{
       
  1310 			$this->sql_stack_fields = $this->sql_fields;
       
  1311 			$this->sql_stack_values[] = '(' . $this->sql_values . ')';
       
  1312 		}
       
  1313 		else
       
  1314 		{
       
  1315 			$this->sql_stack_fields[$id] = $this->sql_fields;
       
  1316 			$this->sql_stack_values[$id][] = '(' . $this->sql_values . ')';
       
  1317 		}
       
  1318 	}
       
  1319 
       
  1320 	function sql_stack_insert($table, $transaction=false, $line='', $file='', $break_on_error=true, $id='')
       
  1321 	{
       
  1322 		if ( (empty($id) && empty($this->sql_stack_values)) || (!empty($id) && empty($this->sql_stack_values[$id])) )
       
  1323 		{
       
  1324 			return false;
       
  1325 		}
       
  1326 		switch( SQL_LAYER )
       
  1327 		{
       
  1328 			case 'mysql':
       
  1329 			case 'mysql4':
       
  1330 				if ( empty($id) )
       
  1331 				{
       
  1332 					$sql = 'INSERT INTO ' . $table . '
       
  1333 								(' . $this->sql_stack_fields . ') VALUES ' . implode(",\n", $this->sql_stack_values);
       
  1334 				}
       
  1335 				else
       
  1336 				{
       
  1337 					$sql = 'INSERT INTO ' . $table . '
       
  1338 								(' . $this->sql_stack_fields[$id] . ') VALUES ' . implode(",\n", $this->sql_stack_values[$id]);
       
  1339 				}
       
  1340 				$this->sql_stack_reset($id);
       
  1341 				return $this->sql_query($sql, $transaction, $line, $file, $break_on_error);
       
  1342 				break;
       
  1343 			default:
       
  1344 				$count_sql_stack_values = empty($id) ? count($this->sql_stack_values) : count($this->sql_stack_values[$id]);
       
  1345 				$result = !empty($count_sql_stack_values);
       
  1346 				for ( $i = 0; $i < $count_sql_stack_values; $i++ )
       
  1347 				{
       
  1348 					if ( empty($id) )
       
  1349 					{
       
  1350 						$sql = 'INSERT INTO ' . $table . '
       
  1351 									(' . $this->sql_stack_fields . ') VALUES ' . $this->sql_stack_values[$i];
       
  1352 					}
       
  1353 					else
       
  1354 					{
       
  1355 						$sql = 'INSERT INTO ' . $table . '
       
  1356 									(' . $this->sql_stack_fields[$id] . ') VALUES ' . $this->sql_stack_values[$id][$i];
       
  1357 					}
       
  1358 					$result &= $this->sql_query($sql, $transaction, $line, $file, $break_on_error);
       
  1359 				}
       
  1360 				$this->sql_stack_reset($id);
       
  1361 				return $result;
       
  1362 				break;
       
  1363 		}
       
  1364 	}
       
  1365 
       
  1366 	function sql_subquery($field, $sql, $line='', $file='', $break_on_error=true, $type=TYPE_INT)
       
  1367 	{
       
  1368 		// sub-queries doable
       
  1369 		$this->sql_get_version();
       
  1370 		if ( !in_array(SQL_LAYER, array('mysql', 'mysql4')) || (($this->sql_version[0] + ($this->sql_version[1] / 100)) >= 4.01) )
       
  1371 		{
       
  1372 			return $sql;
       
  1373 		}
       
  1374 
       
  1375 		// no sub-queries
       
  1376 		$ids = array();
       
  1377 		$result = $this->sql_query(trim($sql), false, $line, $file, $break_on_error);
       
  1378 		while ( $row = $this->sql_fetchrow($result) )
       
  1379 		{
       
  1380 			$ids[] = $type == TYPE_INT ? intval($row[$field]) : '\'' . $this->sql_escape_string($row[$field]) . '\'';
       
  1381 		}
       
  1382 		$this->sql_freeresult($result);
       
  1383 		return empty($ids) ? 'NULL' : implode(', ', $ids);
       
  1384 	}
       
  1385 
       
  1386 	function sql_col_id($expr, $alias)
       
  1387 	{
       
  1388 		$this->sql_get_version();
       
  1389 		return in_array(SQL_LAYER, array('mysql', 'mysql4')) && (($this->sql_version[0] + ($this->sql_version[1] / 100)) <= 4.01) ? $alias : $expr;
       
  1390 	}
       
  1391 
       
  1392 	function sql_get_version()
       
  1393 	{
       
  1394 		if ( empty($this->sql_version) )
       
  1395 		{
       
  1396 			$this->sql_version = array(0, 0, 0);
       
  1397 			switch ( SQL_LAYER )
       
  1398 			{
       
  1399 				case 'mysql':
       
  1400 				case 'mysql4':
       
  1401 					if ( function_exists('mysql_get_server_info') )
       
  1402 					{
       
  1403 						$lo_version = explode('-', mysql_get_server_info());
       
  1404 						$this->sql_version = explode('.', $lo_version[0]);
       
  1405 						$this->sql_version = array(intval($this->sql_version[0]), intval($this->sql_version[1]), intval($this->sql_version[2]), $lo_version[1]);
       
  1406 					}
       
  1407 					break;
       
  1408 
       
  1409 				case 'postgresql':
       
  1410 				case 'mssql':
       
  1411 				case 'mssql-odbc':
       
  1412 				default:
       
  1413 					break;
       
  1414 			}
       
  1415 		}
       
  1416 		return $this->sql_version;
       
  1417 	}
       
  1418 
       
  1419 	function sql_error()
       
  1420 	{
       
  1421 		if ( $this->_conn )
       
  1422 		{
       
  1423 			return mysql_error();
       
  1424 		}
       
  1425 		else
       
  1426 		{
       
  1427 			return array();
       
  1428 		}
       
  1429 	}
       
  1430   function sql_escape_string($t) 
       
  1431   {
       
  1432     return mysql_real_escape_string($t);
       
  1433   }
       
  1434   function sql_close()
       
  1435   {
       
  1436     $this->close();
       
  1437   }
       
  1438   function sql_fetchrowset($query_id = 0)
       
  1439 	{
       
  1440 		if( !$query_id )
       
  1441 		{
       
  1442 			$query_id = $this->query_result;
       
  1443 		}
       
  1444 
       
  1445 		if( $query_id )
       
  1446 		{
       
  1447 			unset($this->rowset[$query_id]);
       
  1448 			unset($this->row[$query_id]);
       
  1449 
       
  1450 			while($this->rowset[$query_id] = mysql_fetch_array($query_id, MYSQL_ASSOC))
       
  1451 			{
       
  1452 				$result[] = $this->rowset[$query_id];
       
  1453 			}
       
  1454 
       
  1455 			return $result;
       
  1456 		}
       
  1457 		else
       
  1458 		{
       
  1459 			return false;
       
  1460 		}
       
  1461 	}
       
  1462   /**
       
  1463    * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
       
  1464    */
       
  1465   
       
  1466   function sql_report()
       
  1467   {
       
  1468     global $db, $session, $paths, $template, $plugins; // Common objects
       
  1469     if ( !$session->get_permissions('mod_misc') )
       
  1470     {
       
  1471       die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>');
       
  1472     }
       
  1473     // Create copies of variables that may be changed after header is called
       
  1474     $backtrace = $this->query_backtrace;
       
  1475     $times = $this->query_times;
       
  1476     $template->header();
       
  1477     echo '<h3>SQL query log and timetable</h3>';
       
  1478     echo '<div class="tblholder">
       
  1479             <table border="0" cellspacing="1" cellpadding="4">';
       
  1480     $i = 0;
       
  1481     foreach ( $backtrace as $query )
       
  1482     {
       
  1483       $i++;
       
  1484       $unbuffered = false;
       
  1485       if ( substr($query, 0, 13) == '(UNBUFFERED) ' )
       
  1486       {
       
  1487         $query = substr($query, 13);
       
  1488         $unbuffered = true;
       
  1489       }
       
  1490       if ( $i == 1 )
       
  1491       {
       
  1492         echo '<tr>
       
  1493                 <th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th>
       
  1494               </tr>';
       
  1495       }
       
  1496       else
       
  1497       {
       
  1498         echo '<tr>
       
  1499                 <th class="subhead" colspan="2">&nbsp;</th>
       
  1500               </tr>';
       
  1501       }
       
  1502       echo '<tr>
       
  1503               <td class="row2">Query:</td>
       
  1504               <td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td>
       
  1505             </tr>
       
  1506             <tr>
       
  1507               <td class="row2">Time:</td>
       
  1508               <td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td>
       
  1509             </tr>
       
  1510             <tr>
       
  1511               <td class="row2">Unbuffered:</td>
       
  1512               <td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td>
       
  1513             </tr>';
       
  1514       if ( isset($this->query_sources[$query]) )
       
  1515       {
       
  1516         echo '<tr>
       
  1517                 <td class="row2">Called from:</td>
       
  1518                 <td class="row1">' . $this->query_sources[$query] . '</td>
       
  1519               </tr>';
       
  1520       }
       
  1521     }
       
  1522     if ( function_exists('array_sum') )
       
  1523     {
       
  1524       $query_time_total = array_sum($this->query_times);
       
  1525       echo '<tr>
       
  1526               <th class="subhead" colspan="2">
       
  1527                 Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds
       
  1528               </th>
       
  1529             </tr>';
       
  1530     }
       
  1531     echo '  </table>
       
  1532           </div>';
       
  1533     $template->footer();
       
  1534   }
       
  1535 }
       
  1536 
   781 ?>
  1537 ?>