/** * Outputs Bad request Error message. When in debug mode it also prints a backtrace. * * This should be used when a bad user input is detected. * * @param string Message to output (HTML) */ function bad_request_die($additional_info = '') { global $debug, $baseurl; // Attempt to output an error header (will not work if the output buffer has already flushed once): // This should help preventing indexing robots from indexing the error :P if (!headers_sent()) { load_funcs('_core/_template.funcs.php'); headers_content_mightcache('text/html', 0); // Do NOT cache error messages! (Users would not see they fixed them) header_http_response('400 Bad Request'); } echo '<div style="background-color: #fdd; padding: 1ex; margin-bottom: 1ex;">'; echo '<h3 style="color:#f00;">' . T_('Bad Request!') . '</h3>'; echo '<p>' . T_('The parameters of your request are invalid.') . '</p>'; echo '<p>' . T_('If you have obtained this error by clicking on a link INSIDE of this site, please report the bad link to the administrator.') . '</p>'; echo '<p><a href="' . $baseurl . '">' . T_('Go back to home page') . '</a></p>'; echo '</div>'; if (!empty($additional_info)) { echo '<div style="background-color: #ddd; padding: 1ex; margin-bottom: 1ex;">'; if ($debug) { // Display additional info only in debug mode because it can reveal system info to hackers and greatly facilitate exploits echo '<h3>' . T_('Additional information about this error:') . '</h3>'; echo $additional_info; } else { echo '<p><i>Enable debugging to get additional information about this error.</i></p>' . get_manual_link('debugging', 'How to enable debug mode?'); } echo '</div>'; // Append the error text to AJAX log if it is AJAX request global $Ajaxlog; if (!empty($Ajaxlog)) { $Ajaxlog->add($additional_info, 'error'); $Ajaxlog->display(NULL, NULL, true, 'all', array('error' => array('class' => 'jslog_error', 'divClass' => false), 'note' => array('class' => 'jslog_note', 'divClass' => false)), 'ul', 'jslog'); } } if ($debug) { echo debug_get_backtrace(); } // Attempt to keep the html valid (but it doesn't really matter anyway) echo '</body></html>'; die(2); // Error code 2. Note: this will still call the shutdown function. }
/** * Basic Query * * @param string SQL query * @param string title for debugging * @return mixed # of rows affected or false if error */ function query($query, $title = '') { global $Timer; // initialise return $return_val = 0; // Flush cached values.. $this->flush(); // Replace aliases: if (!empty($this->dbaliases)) { // TODO: this should only replace the table name part(s), not the whole query! // blueyed> I've changed it to replace in table name parts for UPDATE, INSERT and REPLACE, because // it corrupted serialized data.. // IMHO, a cleaner solution would be to use {T_xxx} in the queries and replace it here. In object properties (e.g. DataObject::$dbtablename), only "T_xxx" would get used and surrounded by "{..}" in the queries it creates. if (preg_match('~^\\s*(UPDATE\\s+)(.*?)(\\sSET\\s.*)$~is', $query, $match)) { // replace only between UPDATE and SET, but check subqueries: if (preg_match('~^(.*SELECT.*FROM\\s+)(.*?)(\\s.*)$~is', $match[3], $subquery_match)) { // replace in subquery $match[3] = $subquery_match[1] . preg_replace($this->dbaliases, $this->dbreplaces, $subquery_match[2]) . $subquery_match[3]; } if (preg_match('~^(.*SELECT.*JOIN\\s+)(.*?)(\\s.*)$~is', $match[3], $subquery_match)) { // replace in whole subquery, there can be any number of JOIN: $match[3] = preg_replace($this->dbaliases, $this->dbreplaces, $match[3]); } $query = $match[1] . preg_replace($this->dbaliases, $this->dbreplaces, $match[2]) . $match[3]; } elseif (preg_match('~^\\s*(INSERT|REPLACE\\s+)(.*?)(\\s(VALUES|SET)\\s.*)$~is', $query, $match)) { // replace only between INSERT|REPLACE and VALUES|SET: $query = $match[1] . preg_replace($this->dbaliases, $this->dbreplaces, $match[2]) . $match[3]; } else { // replace in whole query: $query = preg_replace($this->dbaliases, $this->dbreplaces, $query); if (!empty($this->table_options) && preg_match('#^ \\s* create \\s* table \\s #ix', $query)) { // Query is a table creation, we add table options: $query = preg_replace('~;\\s*$~', '', $query); // remove any ";" at the end $query .= ' ' . $this->table_options; } } } elseif (!empty($this->table_options)) { // No aliases, but table_options: if (preg_match('#^ \\s* create \\s* table \\s #ix', $query)) { // Query is a table creation, we add table options: $query = preg_replace('~;\\s*$~', '', $query); // remove any ";" at the end $query .= $this->table_options; } } // echo '<p>'.$query.'</p>'; // Keep track of the last query for debug.. $this->last_query = $query; // Perform the query via std mysql_query function.. $this->num_queries++; if ($this->log_queries) { // We want to log queries: $this->queries[$this->num_queries - 1] = array('title' => $title, 'sql' => $query, 'rows' => -1, 'time' => 'unknown', 'results' => 'unknown'); } if (is_object($Timer)) { // Resume global query timer $Timer->resume('SQL QUERIES', false); // Start a timer for this particular query: $Timer->start('sql_query', false); // Run query: $this->result = @mysql_query($query, $this->dbhandle); if ($this->log_queries) { // We want to log queries: // Get duration for last query: $this->queries[$this->num_queries - 1]['time'] = $Timer->get_duration('sql_query', 10); } // Pause global query timer: $Timer->pause('SQL QUERIES', false); } else { // Run query: $this->result = @mysql_query($query, $this->dbhandle); } // If there is an error then take note of it.. if (is_resource($this->dbhandle) && mysql_error($this->dbhandle)) { if (is_resource($this->result)) { mysql_free_result($this->result); } $last_errno = mysql_errno($this->dbhandle); if ($this->use_transactions && $this->transaction_isolation_level == 'SERIALIZABLE' && 1213 == $last_errno) { // deadlock exception occured, transaction must be rolled back $this->rollback_nested_transaction = true; return false; } $this->print_error('', '', $title); return false; } if (preg_match('#^\\s*(INSERT|DELETE|UPDATE|REPLACE)\\s#i', $query, $match)) { // Query was an insert, delete, update, replace: $this->rows_affected = mysql_affected_rows($this->dbhandle); if ($this->log_queries) { // We want to log queries: $this->queries[$this->num_queries - 1]['rows'] = $this->rows_affected; } // Take note of the insert_id, for INSERT and REPLACE: $match[1] = strtoupper($match[1]); if ($match[1] == 'INSERT' || $match[1] == 'REPLACE') { $this->insert_id = mysql_insert_id($this->dbhandle); } // Return number of rows affected $return_val = $this->rows_affected; } else { // Query was a select, alter, etc...: if (is_resource($this->result)) { // It's not a resource for CREATE or DROP for example and can even trigger a fatal error (see http://forums.b2evolution.net//viewtopic.php?t=9529) $this->num_rows = mysql_num_rows($this->result); } if ($this->log_queries) { // We want to log queries: $this->queries[$this->num_queries - 1]['rows'] = $this->num_rows; } // Return number of rows selected $return_val = $this->num_rows; } if ($this->log_queries) { // We want to log queries: if ($this->debug_dump_function_trace_for_queries) { $this->queries[$this->num_queries - 1]['function_trace'] = debug_get_backtrace($this->debug_dump_function_trace_for_queries, array(array('class' => 'DB')), 1); // including first stack entry from class DB } if ($this->debug_dump_rows && $this->num_rows) { $this->queries[$this->num_queries - 1]['results'] = $this->debug_get_rows_table($this->debug_dump_rows); } // Profile queries if ($this->debug_profile_queries) { // save values: $saved_last_result = $this->result; $saved_num_rows = $this->num_rows; $this->num_rows = 0; $this->result = @mysql_query('SHOW PROFILE', $this->dbhandle); $this->num_rows = mysql_num_rows($this->result); if ($this->num_rows) { $this->queries[$this->num_queries - 1]['profile'] = $this->debug_get_rows_table(100, true); // Get time information from PROFILING table (which corresponds to "SHOW PROFILE") $this->result = mysql_query('SELECT FORMAT(SUM(DURATION), 6) AS DURATION FROM INFORMATION_SCHEMA.PROFILING GROUP BY QUERY_ID ORDER BY QUERY_ID DESC LIMIT 1', $this->dbhandle); $this->queries[$this->num_queries - 1]['time_profile'] = array_shift(mysql_fetch_row($this->result)); } // Free "PROFILE" result resource: mysql_free_result($this->result); // Restore: $this->result = $saved_last_result; $this->num_rows = $saved_num_rows; } } return $return_val; }
/** * Append a backtrace to any query errors, if errors get * displayed. */ function print_error($title = '', $html_str = '', $query_title = '') { $args = func_get_args(); call_user_func_array(array($this, 'parent::print_error'), $args); if ($this->show_errors) { echo debug_get_backtrace(NULL, array('function' => 'print_error')); } }
/** * Outputs Bad request Error message. When in debug mode it also prints a backtrace. * * This should be used when a bad user input is detected? * * @param string Message to output */ function bad_request_die($additional_info = '') { global $debug, $baseurl; // Attempt to output an error header (will not work if the output buffer has already flushed once): // This should help preventing indexing robots from indexing the error :P if (!headers_sent()) { global $io_charset; header('Content-type: text/html; charset=' . $io_charset); // it's ok, if a previous header would be replaced; header('HTTP/1.0 400 Bad Request'); } echo '<div style="background-color: #fdd; padding: 1ex; margin-bottom: 1ex;">'; echo '<h3 style="color:#f00;">' . T_('Bad Request!') . '</h3>'; echo '<p>' . T_('The parameters of your request are invalid.') . '</p>'; echo '<p>' . T_('If you have obtained this error by clicking on a link INSIDE of this site, please report the bad link to the administrator.') . '</p>'; echo '<p><a href="' . $baseurl . '">' . T_('Go back to home page') . '</a></p>'; echo '</div>'; if (!empty($additional_info)) { echo '<div style="background-color: #ddd; padding: 1ex; margin-bottom: 1ex;">'; echo '<h3>' . T_('Additional information about this error:') . '</h3>'; echo $additional_info; echo '</div>'; } if ($debug) { echo debug_get_backtrace(); debug_info(); } // Attempt to keep the html valid (but it doesn't really matter anyway) die('</body></html>'); }
/** * Basic Query * * @param string SQL query * @param string title for debugging * @return mixed # of rows affected or false if error */ function query($query, $title = '') { global $Timer; // initialise return $return_val = 0; // Flush cached values.. $this->flush(); // Log how the function was called $this->func_call = '$db->query("' . $query . '")'; // echo $this->func_call, '<br />'; // Replace aliases: if (!empty($this->dbaliases)) { // TODO: this should only replace the table name part(s), not the whole query! // blueyed> I've changed it to replace in table name parts for UPDATE, INSERT and REPLACE, because // it corrupted serialized data.. // IMHO, a cleaner solution would be to use {T_xxx} in the queries and replace it here. In object properties (e.g. DataObject::$dbtablename), only "T_xxx" would get used and surrounded by "{..}" in the queries it creates. if (preg_match('~^\\s*(UPDATE\\s+)(.*?)(\\sSET\\s.*)$~is', $query, $match)) { // replace only between UPDATE and SET: $query = $match[1] . preg_replace($this->dbaliases, $this->dbreplaces, $match[2]) . $match[3]; } elseif (preg_match('~^\\s*(INSERT|REPLACE\\s+)(.*?)(\\s(VALUES|SET)\\s.*)$~is', $query, $match)) { // replace only between INSERT|REPLACE and VALUES|SET: $query = $match[1] . preg_replace($this->dbaliases, $this->dbreplaces, $match[2]) . $match[3]; } else { // replace in whole query: $query = preg_replace($this->dbaliases, $this->dbreplaces, $query); if (!empty($this->table_options) && preg_match('#^ \\s* create \\s* table \\s #ix', $query)) { // Query is a table creation, we add table options: $query = preg_replace('~;\\s*$~', '', $query); // remove any ";" at the end $query .= ' ' . $this->table_options; } } } elseif (!empty($this->table_options)) { // No aliases, but table_options: if (preg_match('#^ \\s* create \\s* table \\s #ix', $query)) { // Query is a table creation, we add table options: $query = preg_replace('~;\\s*$~', '', $query); // remove any ";" at the end $query .= $this->table_options; } } // echo '<p>'.$query.'</p>'; // Keep track of the last query for debug.. $this->last_query = $query; // Perform the query via std mysql_query function.. $this->num_queries++; if ($this->log_queries) { // We want to log queries: $this->queries[$this->num_queries - 1] = array('title' => $title, 'sql' => $query, 'rows' => -1, 'time' => 'unknown', 'results' => 'unknown'); } if (is_object($Timer)) { // Resume global query timer $Timer->resume('sql_queries'); // Start a timer for this particular query: $Timer->start('sql_query', false); // Run query: $this->result = @mysql_query($query, $this->dbhandle); if ($this->log_queries) { // We want to log queries: // Get duration for last query: $this->queries[$this->num_queries - 1]['time'] = $Timer->get_duration('sql_query', 10); } // Pause global query timer: $Timer->pause('sql_queries'); } else { // Run query: $this->result = @mysql_query($query, $this->dbhandle); } // If there is an error then take note of it.. if (mysql_error($this->dbhandle)) { @mysql_free_result($this->result); $this->print_error('', '', $title); return false; } if (preg_match('#^\\s*(INSERT|DELETE|UPDATE|REPLACE)\\s#i', $query, $match)) { // Query was an insert, delete, update, replace: $this->rows_affected = mysql_affected_rows($this->dbhandle); if ($this->log_queries) { // We want to log queries: $this->queries[$this->num_queries - 1]['rows'] = $this->rows_affected; } // Take note of the insert_id, for INSERT and REPLACE: $match[1] = strtoupper($match[1]); if ($match[1] == 'INSERT' || $match[1] == 'REPLACE') { $this->insert_id = mysql_insert_id($this->dbhandle); } // Return number of rows affected $return_val = $this->rows_affected; } else { // Query was a select, alter, etc...: $this->num_rows = 0; if (is_resource($this->result)) { // It's not a resource for CREATE or DROP for example and can even trigger a fatal error (see http://forums.b2evolution.net//viewtopic.php?t=9529) // Store Query Results while ($row = mysql_fetch_object($this->result)) { // Store relults as an objects within main array $this->last_result[$this->num_rows] = $row; $this->num_rows++; } } if ($this->log_queries) { // We want to log queries: $this->queries[$this->num_queries - 1]['rows'] = $this->num_rows; } // Return number of rows selected $return_val = $this->num_rows; } if ($this->log_queries) { // We want to log queries: if ($this->debug_dump_function_trace_for_queries) { $this->queries[$this->num_queries - 1]['function_trace'] = debug_get_backtrace($this->debug_dump_function_trace_for_queries, array(array('class' => 'DB')), 1); // including first stack entry from class DB } if ($this->debug_dump_rows) { $this->queries[$this->num_queries - 1]['results'] = $this->debug_get_rows_table($this->debug_dump_rows); } } // Free original query's result: @mysql_free_result($this->result); // EXPLAIN JOINS ?? if ($this->log_queries && $this->debug_explain_joins && preg_match('#^ \\s* SELECT \\s #ix', $query)) { // Query was a select, let's try to explain joins... // save values: $saved_last_result = $this->last_result; $saved_num_rows = $this->num_rows; $this->last_result = NULL; $this->num_rows = 0; $this->result = @mysql_query('EXPLAIN ' . $query, $this->dbhandle); // Store Query Results $this->num_rows = 0; while ($row = @mysql_fetch_object($this->result)) { // Store results as an objects within main array $this->last_result[$this->num_rows] = $row; $this->num_rows++; } $this->queries[$this->num_queries - 1]['explain'] = $this->debug_get_rows_table(100, true); // Free "EXPLAIN" result resource: @mysql_free_result($this->result); // Restore: $this->last_result = $saved_last_result; $this->num_rows = $saved_num_rows; } return $return_val; }