albertlast /
SMF2.1
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file has all the main functions in it that relate to the database. |
||
| 5 | * |
||
| 6 | * Simple Machines Forum (SMF) |
||
| 7 | * |
||
| 8 | * @package SMF |
||
| 9 | * @author Simple Machines http://www.simplemachines.org |
||
| 10 | * @copyright 2016 Simple Machines and individual contributors |
||
| 11 | * @license http://www.simplemachines.org/about/smf/license.php BSD |
||
| 12 | * |
||
| 13 | * @version 2.1 Beta 3 |
||
| 14 | */ |
||
| 15 | |||
| 16 | if (!defined('SMF')) |
||
| 17 | die('No direct access...'); |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Maps the implementations in this file (smf_db_function_name) |
||
| 21 | * to the $smcFunc['db_function_name'] variable. |
||
| 22 | * |
||
| 23 | * @param string $db_server The database server |
||
| 24 | * @param string $db_name The name of the database |
||
| 25 | * @param string $db_user The database username |
||
| 26 | * @param string $db_passwd The database password |
||
| 27 | * @param string $db_prefix The table prefix |
||
| 28 | * @param array $db_options An array of database options |
||
| 29 | * @return null|resource Returns null on failure if $db_options['non_fatal'] is true or a MySQL connection resource handle if the connection was successful. |
||
| 30 | */ |
||
| 31 | function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array()) |
||
| 32 | { |
||
| 33 | global $smcFunc, $mysql_set_mode; |
||
| 34 | |||
| 35 | // Map some database specific functions, only do this once. |
||
| 36 | View Code Duplication | if (!isset($smcFunc['db_fetch_assoc']) || $smcFunc['db_fetch_assoc'] != 'mysql_fetch_assoc') |
|
| 37 | $smcFunc += array( |
||
| 38 | 'db_query' => 'smf_db_query', |
||
| 39 | 'db_quote' => 'smf_db_quote', |
||
| 40 | 'db_fetch_assoc' => 'mysql_fetch_assoc', |
||
| 41 | 'db_fetch_row' => 'mysql_fetch_row', |
||
| 42 | 'db_free_result' => 'mysql_free_result', |
||
| 43 | 'db_insert' => 'smf_db_insert', |
||
| 44 | 'db_insert_id' => 'smf_db_insert_id', |
||
| 45 | 'db_num_rows' => 'mysql_num_rows', |
||
| 46 | 'db_data_seek' => 'mysql_data_seek', |
||
| 47 | 'db_num_fields' => 'mysql_num_fields', |
||
| 48 | 'db_escape_string' => 'addslashes', |
||
| 49 | 'db_unescape_string' => 'stripslashes', |
||
| 50 | 'db_server_info' => 'mysql_get_server_info', |
||
| 51 | 'db_affected_rows' => 'smf_db_affected_rows', |
||
| 52 | 'db_transaction' => 'smf_db_transaction', |
||
| 53 | 'db_error' => 'mysql_error', |
||
| 54 | 'db_select_db' => 'mysql_select_db', |
||
| 55 | 'db_title' => 'MySQL', |
||
| 56 | 'db_sybase' => false, |
||
| 57 | 'db_case_sensitive' => false, |
||
| 58 | 'db_escape_wildcard_string' => 'smf_db_escape_wildcard_string', |
||
| 59 | 'db_is_resource' => 'is_resource', |
||
| 60 | ); |
||
| 61 | |||
| 62 | if (!empty($db_options['port'])) |
||
| 63 | $db_server .= ':' . $db_options['port']; |
||
| 64 | |||
| 65 | $flags = 2; //#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ |
||
| 66 | |||
| 67 | if (!empty($db_options['persist'])) |
||
| 68 | $connection = @mysql_pconnect($db_server, $db_user, $db_passwd, $flags); |
||
| 69 | else |
||
| 70 | $connection = @mysql_connect($db_server, $db_user, $db_passwd, false, $flags); |
||
| 71 | |||
| 72 | // Something's wrong, show an error if its fatal (which we assume it is) |
||
| 73 | if (!$connection) |
||
| 74 | { |
||
| 75 | if (!empty($db_options['non_fatal'])) |
||
| 76 | return null; |
||
| 77 | else |
||
| 78 | display_db_error(); |
||
| 79 | } |
||
| 80 | |||
| 81 | // Select the database, unless told not to |
||
| 82 | View Code Duplication | if (empty($db_options['dont_select_db']) && !@mysql_select_db($db_name, $connection) && empty($db_options['non_fatal'])) |
|
| 83 | display_db_error(); |
||
| 84 | |||
| 85 | // This makes it possible to have SMF automatically change the sql_mode and autocommit if needed. |
||
| 86 | View Code Duplication | if (isset($mysql_set_mode) && $mysql_set_mode === true) |
|
| 87 | $smcFunc['db_query']('', 'SET sql_mode = \'\', AUTOCOMMIT = 1', |
||
| 88 | array(), |
||
| 89 | false |
||
| 90 | ); |
||
| 91 | |||
| 92 | return $connection; |
||
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * Extend the database functionality. It calls the respective file's init |
||
| 97 | * to add the implementations in that file to $smcFunc array. |
||
| 98 | * |
||
| 99 | * @param string $type Indicates which additional file to load. ('extra', 'packages') |
||
| 100 | */ |
||
| 101 | View Code Duplication | function db_extend($type = 'extra') |
|
| 102 | { |
||
| 103 | global $sourcedir, $db_type; |
||
| 104 | |||
| 105 | require_once($sourcedir . '/Db' . strtoupper($type[0]) . substr($type, 1) . '-' . $db_type . '.php'); |
||
| 106 | $initFunc = 'db_' . $type . '_init'; |
||
| 107 | $initFunc(); |
||
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Fix up the prefix so it doesn't require the database to be selected. |
||
| 112 | * |
||
| 113 | * @param string &$db_prefix The table prefix |
||
| 114 | * @param string $db_name The database name |
||
| 115 | */ |
||
| 116 | function db_fix_prefix(&$db_prefix, $db_name) |
||
| 117 | { |
||
| 118 | $db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix; |
||
| 119 | } |
||
| 120 | |||
| 121 | /** |
||
| 122 | * Callback for preg_replace_callback on the query. |
||
| 123 | * It allows to replace on the fly a few pre-defined strings, for convenience ('query_see_board', 'query_wanna_see_board'), with |
||
| 124 | * their current values from $user_info. |
||
| 125 | * In addition, it performs checks and sanitization on the values sent to the database. |
||
| 126 | * |
||
| 127 | * @param array $matches The matches from preg_replace_callback |
||
| 128 | * @return string The appropriate string depending on $matches[1] |
||
| 129 | */ |
||
| 130 | View Code Duplication | function smf_db_replacement__callback($matches) |
|
|
0 ignored issues
–
show
|
|||
| 131 | { |
||
| 132 | global $db_callback, $user_info, $db_prefix, $smcFunc; |
||
| 133 | |||
| 134 | list ($values, $connection) = $db_callback; |
||
| 135 | |||
| 136 | // Connection gone??? This should *never* happen at this point, yet it does :'( |
||
| 137 | if (!is_resource($connection)) |
||
| 138 | display_db_error(); |
||
| 139 | |||
| 140 | if ($matches[1] === 'db_prefix') |
||
| 141 | return $db_prefix; |
||
| 142 | |||
| 143 | if ($matches[1] === 'query_see_board') |
||
| 144 | return $user_info['query_see_board']; |
||
| 145 | |||
| 146 | if ($matches[1] === 'query_wanna_see_board') |
||
| 147 | return $user_info['query_wanna_see_board']; |
||
| 148 | |||
| 149 | if ($matches[1] === 'empty') |
||
| 150 | return '\'\''; |
||
| 151 | |||
| 152 | if (!isset($matches[2])) |
||
| 153 | smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 154 | |||
| 155 | if ($matches[1] === 'literal') |
||
| 156 | return '\'' . mysql_real_escape_string($matches[2], $connection) . '\''; |
||
| 157 | |||
| 158 | if (!isset($values[$matches[2]])) |
||
| 159 | smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . (isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($matches[2]) : htmlspecialchars($matches[2])), '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 160 | |||
| 161 | $replacement = $values[$matches[2]]; |
||
| 162 | |||
| 163 | switch ($matches[1]) |
||
| 164 | { |
||
| 165 | case 'int': |
||
| 166 | if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) |
||
| 167 | smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 168 | return (string) (int) $replacement; |
||
| 169 | break; |
||
| 170 | |||
| 171 | case 'string': |
||
| 172 | case 'text': |
||
| 173 | return sprintf('\'%1$s\'', mysql_real_escape_string($replacement, $connection)); |
||
| 174 | break; |
||
| 175 | |||
| 176 | case 'array_int': |
||
| 177 | if (is_array($replacement)) |
||
| 178 | { |
||
| 179 | if (empty($replacement)) |
||
| 180 | smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 181 | |||
| 182 | foreach ($replacement as $key => $value) |
||
| 183 | { |
||
| 184 | if (!is_numeric($value) || (string) $value !== (string) (int) $value) |
||
| 185 | smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 186 | |||
| 187 | $replacement[$key] = (string) (int) $value; |
||
| 188 | } |
||
| 189 | |||
| 190 | return implode(', ', $replacement); |
||
| 191 | } |
||
| 192 | else |
||
| 193 | smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 194 | |||
| 195 | break; |
||
| 196 | |||
| 197 | case 'array_string': |
||
| 198 | if (is_array($replacement)) |
||
| 199 | { |
||
| 200 | if (empty($replacement)) |
||
| 201 | smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 202 | |||
| 203 | foreach ($replacement as $key => $value) |
||
| 204 | $replacement[$key] = sprintf('\'%1$s\'', mysql_real_escape_string($value, $connection)); |
||
| 205 | |||
| 206 | return implode(', ', $replacement); |
||
| 207 | } |
||
| 208 | else |
||
| 209 | smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 210 | break; |
||
| 211 | |||
| 212 | case 'date': |
||
| 213 | if (preg_match('~^(\d{4})-([0-1]?\d)-([0-3]?\d)$~', $replacement, $date_matches) === 1) |
||
| 214 | return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]); |
||
| 215 | else |
||
| 216 | smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 217 | break; |
||
| 218 | |||
| 219 | case 'time': |
||
| 220 | if (preg_match('~^([0-1]?\d|2[0-3]):([0-5]\d):([0-5]\d)$~', $replacement, $time_matches) === 1) |
||
| 221 | return sprintf('\'%02d:%02d:%02d\'', $time_matches[1], $time_matches[2], $time_matches[3]); |
||
| 222 | else |
||
| 223 | smf_db_error_backtrace('Wrong value type sent to the database. Time expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 224 | break; |
||
| 225 | |||
| 226 | case 'float': |
||
| 227 | if (!is_numeric($replacement)) |
||
| 228 | smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 229 | return (string) (float) $replacement; |
||
| 230 | break; |
||
|
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. Loading history...
|
|||
| 231 | |||
| 232 | case 'identifier': |
||
| 233 | // Backticks inside identifiers are supported as of MySQL 4.1. We don't need them for SMF. |
||
| 234 | return '`' . strtr($replacement, array('`' => '', '.' => '')) . '`'; |
||
| 235 | break; |
||
|
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. Loading history...
|
|||
| 236 | |||
| 237 | case 'raw': |
||
| 238 | return $replacement; |
||
| 239 | break; |
||
|
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. Loading history...
|
|||
| 240 | |||
| 241 | case 'inet': |
||
| 242 | if ($replacement == 'null' || $replacement == '') |
||
| 243 | return 'null'; |
||
| 244 | if (!isValidIP($replacement)) |
||
| 245 | smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 246 | //we don't use the native support of mysql > 5.6.2 |
||
| 247 | return sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($replacement))); |
||
| 248 | |||
| 249 | case 'array_inet': |
||
| 250 | if (is_array($replacement)) |
||
| 251 | { |
||
| 252 | if (empty($replacement)) |
||
| 253 | smf_db_error_backtrace('Database error, given array of IPv4 or IPv6 values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 254 | |||
| 255 | foreach ($replacement as $key => $value) |
||
| 256 | { |
||
| 257 | if ($replacement == 'null' || $replacement == '') |
||
| 258 | $replacement[$key] = 'null'; |
||
| 259 | if (!isValidIP($value)) |
||
| 260 | smf_db_error_backtrace('Wrong value type sent to the database. IPv4 or IPv6 expected.(' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 261 | $replacement[$key] = sprintf('unhex(\'%1$s\')', bin2hex(inet_pton($value))); |
||
| 262 | } |
||
| 263 | |||
| 264 | return implode(', ', $replacement); |
||
| 265 | } |
||
| 266 | else |
||
| 267 | smf_db_error_backtrace('Wrong value type sent to the database. Array of IPv4 or IPv6 expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
||
| 268 | break; |
||
| 269 | |||
| 270 | default: |
||
| 271 | smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__); |
||
| 272 | break; |
||
| 273 | } |
||
| 274 | } |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Just like the db_query, escape and quote a string, but not executing the query. |
||
| 278 | * |
||
| 279 | * @param string $db_string The database string |
||
| 280 | * @param array $db_values An array of values to be injected into the string |
||
| 281 | * @param resource $connection = null The connection to use (null to use $db_connection) |
||
| 282 | * @return string The string with the values inserted |
||
| 283 | */ |
||
| 284 | View Code Duplication | function smf_db_quote($db_string, $db_values, $connection = null) |
|
| 285 | { |
||
| 286 | global $db_callback, $db_connection; |
||
| 287 | |||
| 288 | // Only bother if there's something to replace. |
||
| 289 | if (strpos($db_string, '{') !== false) |
||
| 290 | { |
||
| 291 | // This is needed by the callback function. |
||
| 292 | $db_callback = array($db_values, $connection === null ? $db_connection : $connection); |
||
| 293 | |||
| 294 | // Do the quoting and escaping |
||
| 295 | $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); |
||
| 296 | |||
| 297 | // Clear this global variable. |
||
| 298 | $db_callback = array(); |
||
| 299 | } |
||
| 300 | |||
| 301 | return $db_string; |
||
| 302 | } |
||
| 303 | |||
| 304 | /** |
||
| 305 | * Do a query. Takes care of errors too. |
||
| 306 | * |
||
| 307 | * @param string $identifier An identifier. Only used in Postgres when we need to do things differently... |
||
| 308 | * @param string $db_string The database string |
||
| 309 | * @param array $db_values = array() The values to be inserted into the string |
||
| 310 | * @param resource $connection = null The connection to use (null to use $db_connection) |
||
| 311 | * @return resource|bool Returns a MySQL result resource (for SELECT queries), true (for UPDATE queries) or false if the query failed |
||
| 312 | */ |
||
| 313 | function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null) |
||
| 314 | { |
||
| 315 | global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start; |
||
| 316 | global $db_unbuffered, $db_callback, $modSettings; |
||
| 317 | |||
| 318 | // Comments that are allowed in a query are preg_removed. |
||
| 319 | static $allowed_comments_from = array( |
||
| 320 | '~\s+~s', |
||
| 321 | '~/\*!40001 SQL_NO_CACHE \*/~', |
||
| 322 | '~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~', |
||
| 323 | '~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~', |
||
| 324 | ); |
||
| 325 | static $allowed_comments_to = array( |
||
| 326 | ' ', |
||
| 327 | '', |
||
| 328 | '', |
||
| 329 | '', |
||
| 330 | ); |
||
| 331 | |||
| 332 | // Decide which connection to use. |
||
| 333 | $connection = $connection === null ? $db_connection : $connection; |
||
| 334 | |||
| 335 | // One more query.... |
||
| 336 | $db_count = !isset($db_count) ? 1 : $db_count + 1; |
||
| 337 | |||
| 338 | View Code Duplication | if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override'])) |
|
| 339 | smf_db_error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__); |
||
| 340 | |||
| 341 | // Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By |
||
| 342 | View Code Duplication | if (strpos($db_string, 'GROUP BY') !== false && strpos($db_string, 'ORDER BY') === false && preg_match('~^\s+SELECT~i', $db_string)) |
|
| 343 | { |
||
| 344 | // Add before LIMIT |
||
| 345 | if ($pos = strpos($db_string, 'LIMIT ')) |
||
| 346 | $db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string)); |
||
| 347 | else |
||
| 348 | // Append it. |
||
| 349 | $db_string .= "\n\t\t\tORDER BY null"; |
||
| 350 | } |
||
| 351 | |||
| 352 | View Code Duplication | if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false)) |
|
| 353 | { |
||
| 354 | // Pass some values to the global space for use in the callback function. |
||
| 355 | $db_callback = array($db_values, $connection); |
||
| 356 | |||
| 357 | // Inject the values passed to this function. |
||
| 358 | $db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', 'smf_db_replacement__callback', $db_string); |
||
| 359 | |||
| 360 | // This shouldn't be residing in global space any longer. |
||
| 361 | $db_callback = array(); |
||
| 362 | } |
||
| 363 | |||
| 364 | // Debugging. |
||
| 365 | View Code Duplication | if (isset($db_show_debug) && $db_show_debug === true) |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 366 | { |
||
| 367 | // Get the file and line number this function was called. |
||
| 368 | list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); |
||
| 369 | |||
| 370 | // Initialize $db_cache if not already initialized. |
||
| 371 | if (!isset($db_cache)) |
||
| 372 | $db_cache = array(); |
||
| 373 | |||
| 374 | if (!empty($_SESSION['debug_redirect'])) |
||
| 375 | { |
||
| 376 | $db_cache = array_merge($_SESSION['debug_redirect'], $db_cache); |
||
| 377 | $db_count = count($db_cache) + 1; |
||
| 378 | $_SESSION['debug_redirect'] = array(); |
||
| 379 | } |
||
| 380 | |||
| 381 | // Don't overload it. |
||
| 382 | $st = microtime(); |
||
| 383 | $db_cache[$db_count]['q'] = $db_count < 50 ? $db_string : '...'; |
||
| 384 | $db_cache[$db_count]['f'] = $file; |
||
| 385 | $db_cache[$db_count]['l'] = $line; |
||
| 386 | $db_cache[$db_count]['s'] = array_sum(explode(' ', $st)) - array_sum(explode(' ', $time_start)); |
||
| 387 | } |
||
| 388 | |||
| 389 | // First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over. |
||
| 390 | View Code Duplication | if (empty($modSettings['disableQueryCheck'])) |
|
| 391 | { |
||
| 392 | $clean = ''; |
||
| 393 | $old_pos = 0; |
||
| 394 | $pos = -1; |
||
| 395 | while (true) |
||
| 396 | { |
||
| 397 | $pos = strpos($db_string, '\'', $pos + 1); |
||
| 398 | if ($pos === false) |
||
| 399 | break; |
||
| 400 | $clean .= substr($db_string, $old_pos, $pos - $old_pos); |
||
| 401 | |||
| 402 | while (true) |
||
| 403 | { |
||
| 404 | $pos1 = strpos($db_string, '\'', $pos + 1); |
||
| 405 | $pos2 = strpos($db_string, '\\', $pos + 1); |
||
| 406 | if ($pos1 === false) |
||
| 407 | break; |
||
| 408 | elseif ($pos2 === false || $pos2 > $pos1) |
||
| 409 | { |
||
| 410 | $pos = $pos1; |
||
| 411 | break; |
||
| 412 | } |
||
| 413 | |||
| 414 | $pos = $pos2 + 1; |
||
| 415 | } |
||
| 416 | $clean .= ' %s '; |
||
| 417 | |||
| 418 | $old_pos = $pos + 1; |
||
| 419 | } |
||
| 420 | $clean .= substr($db_string, $old_pos); |
||
| 421 | $clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean))); |
||
| 422 | |||
| 423 | // Comments? We don't use comments in our queries, we leave 'em outside! |
||
| 424 | if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false) |
||
| 425 | $fail = true; |
||
| 426 | // Trying to change passwords, slow us down, or something? |
||
| 427 | elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0) |
||
| 428 | $fail = true; |
||
| 429 | elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0) |
||
| 430 | $fail = true; |
||
| 431 | |||
| 432 | if (!empty($fail) && function_exists('log_error')) |
||
| 433 | smf_db_error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__); |
||
| 434 | } |
||
| 435 | |||
| 436 | if (empty($db_unbuffered)) |
||
| 437 | $ret = @mysql_query($db_string, $connection); |
||
| 438 | else |
||
| 439 | $ret = @mysql_unbuffered_query($db_string, $connection); |
||
| 440 | |||
| 441 | View Code Duplication | if ($ret === false && empty($db_values['db_error_skip'])) |
|
| 442 | $ret = smf_db_error($db_string, $connection); |
||
| 443 | |||
| 444 | // Debugging. |
||
| 445 | View Code Duplication | if (isset($db_show_debug) && $db_show_debug === true) |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 446 | $db_cache[$db_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st)); |
||
| 447 | |||
| 448 | return $ret; |
||
| 449 | } |
||
| 450 | |||
| 451 | /** |
||
| 452 | * affected_rows |
||
| 453 | * @param resource $connection A connection to use (if null, $db_connection is used) |
||
| 454 | * @return int The number of rows affected by the last query |
||
| 455 | */ |
||
| 456 | function smf_db_affected_rows($connection = null) |
||
| 457 | { |
||
| 458 | global $db_connection; |
||
| 459 | |||
| 460 | return mysql_affected_rows($connection === null ? $db_connection : $connection); |
||
| 461 | } |
||
| 462 | |||
| 463 | /** |
||
| 464 | * Gets the ID of the most recently inserted row. |
||
| 465 | * |
||
| 466 | * @param string $table The table (only used for Postgres) |
||
| 467 | * @param string $field = null The specific field (not used here) |
||
| 468 | * @param resource $connection = null The connection (if null, $db_connection is used) |
||
| 469 | * @return int The ID of the most recently inserted row |
||
| 470 | */ |
||
| 471 | View Code Duplication | function smf_db_insert_id($table, $field = null, $connection = null) |
|
| 472 | { |
||
| 473 | global $db_connection, $db_prefix; |
||
| 474 | |||
| 475 | $table = str_replace('{db_prefix}', $db_prefix, $table); |
||
| 476 | |||
| 477 | // MySQL doesn't need the table or field information. |
||
| 478 | return mysql_insert_id($connection === null ? $db_connection : $connection); |
||
| 479 | } |
||
| 480 | |||
| 481 | /** |
||
| 482 | * Do a transaction. |
||
| 483 | * |
||
| 484 | * @param string $type The step to perform (i.e. 'begin', 'commit', 'rollback') |
||
| 485 | * @param resource $connection The connection to use (if null, $db_connection is used) |
||
| 486 | * @return bool True if successful, false otherwise |
||
| 487 | */ |
||
| 488 | View Code Duplication | function smf_db_transaction($type = 'commit', $connection = null) |
|
| 489 | { |
||
| 490 | global $db_connection; |
||
| 491 | |||
| 492 | // Decide which connection to use |
||
| 493 | $connection = $connection === null ? $db_connection : $connection; |
||
| 494 | |||
| 495 | if ($type == 'begin') |
||
| 496 | return @mysql_query('BEGIN', $connection); |
||
| 497 | elseif ($type == 'rollback') |
||
| 498 | return @mysql_query('ROLLBACK', $connection); |
||
| 499 | elseif ($type == 'commit') |
||
| 500 | return @mysql_query('COMMIT', $connection); |
||
| 501 | |||
| 502 | return false; |
||
| 503 | } |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Database error! |
||
| 507 | * Backtrace, log, try to fix. |
||
| 508 | * |
||
| 509 | * @param string $db_string The DB string |
||
| 510 | * @param resource $connection The connection to use (if null, $db_connection is used) |
||
| 511 | */ |
||
| 512 | View Code Duplication | function smf_db_error($db_string, $connection = null) |
|
| 513 | { |
||
| 514 | global $txt, $context, $sourcedir, $webmaster_email, $modSettings; |
||
| 515 | global $db_connection, $db_last_error, $db_persist; |
||
| 516 | global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; |
||
| 517 | global $smcFunc; |
||
| 518 | |||
| 519 | // Get the file and line numbers. |
||
| 520 | list ($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); |
||
| 521 | |||
| 522 | // Decide which connection to use |
||
| 523 | $connection = $connection === null ? $db_connection : $connection; |
||
| 524 | |||
| 525 | // This is the error message... |
||
| 526 | $query_error = mysql_error($connection); |
||
| 527 | $query_errno = mysql_errno($connection); |
||
| 528 | |||
| 529 | // Error numbers: |
||
| 530 | // 1016: Can't open file '....MYI' |
||
| 531 | // 1030: Got error ??? from table handler. |
||
| 532 | // 1034: Incorrect key file for table. |
||
| 533 | // 1035: Old key file for table. |
||
| 534 | // 1205: Lock wait timeout exceeded. |
||
| 535 | // 1213: Deadlock found. |
||
| 536 | // 2006: Server has gone away. |
||
| 537 | // 2013: Lost connection to server during query. |
||
| 538 | |||
| 539 | // Log the error. |
||
| 540 | if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error')) |
||
| 541 | log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line); |
||
| 542 | |||
| 543 | // Database error auto fixing ;). |
||
| 544 | if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1')) |
||
| 545 | { |
||
| 546 | // Force caching on, just for the error checking. |
||
| 547 | $old_cache = @$modSettings['cache_enable']; |
||
| 548 | $modSettings['cache_enable'] = '1'; |
||
| 549 | |||
| 550 | if (($temp = cache_get_data('db_last_error', 600)) !== null) |
||
| 551 | $db_last_error = max(@$db_last_error, $temp); |
||
| 552 | |||
| 553 | if (@$db_last_error < time() - 3600 * 24 * 3) |
||
| 554 | { |
||
| 555 | // We know there's a problem... but what? Try to auto detect. |
||
| 556 | if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false) |
||
| 557 | { |
||
| 558 | preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches); |
||
| 559 | |||
| 560 | $fix_tables = array(); |
||
| 561 | foreach ($matches[1] as $tables) |
||
| 562 | { |
||
| 563 | $tables = array_unique(explode(',', $tables)); |
||
| 564 | foreach ($tables as $table) |
||
| 565 | { |
||
| 566 | // Now, it's still theoretically possible this could be an injection. So backtick it! |
||
| 567 | if (trim($table) != '') |
||
| 568 | $fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`'; |
||
| 569 | } |
||
| 570 | } |
||
| 571 | |||
| 572 | $fix_tables = array_unique($fix_tables); |
||
| 573 | } |
||
| 574 | // Table crashed. Let's try to fix it. |
||
| 575 | elseif ($query_errno == 1016) |
||
| 576 | { |
||
| 577 | if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0) |
||
| 578 | $fix_tables = array('`' . $match[1] . '`'); |
||
| 579 | } |
||
| 580 | // Indexes crashed. Should be easy to fix! |
||
| 581 | elseif ($query_errno == 1034 || $query_errno == 1035) |
||
| 582 | { |
||
| 583 | preg_match('~\'([^\']+?)\'~', $query_error, $match); |
||
| 584 | $fix_tables = array('`' . $match[1] . '`'); |
||
| 585 | } |
||
| 586 | } |
||
| 587 | |||
| 588 | // Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...) |
||
| 589 | if (!empty($fix_tables)) |
||
| 590 | { |
||
| 591 | // Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail(). |
||
| 592 | require_once($sourcedir . '/Subs-Admin.php'); |
||
| 593 | require_once($sourcedir . '/Subs-Post.php'); |
||
| 594 | |||
| 595 | // Make a note of the REPAIR... |
||
| 596 | cache_put_data('db_last_error', time(), 600); |
||
| 597 | if (($temp = cache_get_data('db_last_error', 600)) === null) |
||
| 598 | updateSettingsFile(array('db_last_error' => time())); |
||
| 599 | |||
| 600 | // Attempt to find and repair the broken table. |
||
| 601 | foreach ($fix_tables as $table) |
||
| 602 | $smcFunc['db_query']('', " |
||
| 603 | REPAIR TABLE $table", false, false); |
||
| 604 | |||
| 605 | // And send off an email! |
||
| 606 | sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair'], null, 'dberror'); |
||
| 607 | |||
| 608 | $modSettings['cache_enable'] = $old_cache; |
||
| 609 | |||
| 610 | // Try the query again...? |
||
| 611 | $ret = $smcFunc['db_query']('', $db_string, false, false); |
||
| 612 | if ($ret !== false) |
||
| 613 | return $ret; |
||
| 614 | } |
||
| 615 | else |
||
| 616 | $modSettings['cache_enable'] = $old_cache; |
||
| 617 | |||
| 618 | // Check for the "lost connection" or "deadlock found" errors - and try it just one more time. |
||
| 619 | if (in_array($query_errno, array(1205, 1213, 2006, 2013))) |
||
| 620 | { |
||
| 621 | if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection) |
||
| 622 | { |
||
| 623 | // Are we in SSI mode? If so try that username and password first |
||
| 624 | if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd)) |
||
| 625 | { |
||
| 626 | if (empty($db_persist)) |
||
| 627 | $db_connection = @mysql_connect($db_server, $ssi_db_user, $ssi_db_passwd); |
||
| 628 | else |
||
| 629 | $db_connection = @mysql_pconnect($db_server, $ssi_db_user, $ssi_db_passwd); |
||
| 630 | } |
||
| 631 | // Fall back to the regular username and password if need be |
||
| 632 | if (!$db_connection) |
||
| 633 | { |
||
| 634 | if (empty($db_persist)) |
||
| 635 | $db_connection = @mysql_connect($db_server, $db_user, $db_passwd); |
||
| 636 | else |
||
| 637 | $db_connection = @mysql_pconnect($db_server, $db_user, $db_passwd); |
||
| 638 | } |
||
| 639 | |||
| 640 | if (!$db_connection || !@mysql_select_db($db_name, $db_connection)) |
||
| 641 | $db_connection = false; |
||
| 642 | } |
||
| 643 | |||
| 644 | if ($db_connection) |
||
| 645 | { |
||
| 646 | // Try a deadlock more than once more. |
||
| 647 | for ($n = 0; $n < 4; $n++) |
||
| 648 | { |
||
| 649 | $ret = $smcFunc['db_query']('', $db_string, false, false); |
||
| 650 | |||
| 651 | $new_errno = mysql_errno($db_connection); |
||
| 652 | if ($ret !== false || in_array($new_errno, array(1205, 1213))) |
||
| 653 | break; |
||
| 654 | } |
||
| 655 | |||
| 656 | // If it failed again, shucks to be you... we're not trying it over and over. |
||
| 657 | if ($ret !== false) |
||
| 658 | return $ret; |
||
| 659 | } |
||
| 660 | } |
||
| 661 | // Are they out of space, perhaps? |
||
| 662 | elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false)) |
||
| 663 | { |
||
| 664 | if (!isset($txt)) |
||
| 665 | $query_error .= ' - check database storage space.'; |
||
| 666 | else |
||
| 667 | { |
||
| 668 | if (!isset($txt['mysql_error_space'])) |
||
| 669 | loadLanguage('Errors'); |
||
| 670 | |||
| 671 | $query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space']; |
||
| 672 | } |
||
| 673 | } |
||
| 674 | } |
||
| 675 | |||
| 676 | // Nothing's defined yet... just die with it. |
||
| 677 | if (empty($context) || empty($txt)) |
||
| 678 | die($query_error); |
||
| 679 | |||
| 680 | // Show an error message, if possible. |
||
| 681 | $context['error_title'] = $txt['database_error']; |
||
| 682 | if (allowedTo('admin_forum')) |
||
| 683 | $context['error_message'] = nl2br($query_error) . '<br>' . $txt['file'] . ': ' . $file . '<br>' . $txt['line'] . ': ' . $line; |
||
| 684 | else |
||
| 685 | $context['error_message'] = $txt['try_again']; |
||
| 686 | |||
| 687 | if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) |
||
| 688 | { |
||
| 689 | $context['error_message'] .= '<br><br>' . nl2br($db_string); |
||
| 690 | } |
||
| 691 | |||
| 692 | // It's already been logged... don't log it again. |
||
| 693 | fatal_error($context['error_message'], false); |
||
| 694 | } |
||
| 695 | |||
| 696 | /** |
||
| 697 | * Inserts data into a table |
||
| 698 | * |
||
| 699 | * @param string $method The insert method - can be 'replace', 'ignore' or 'insert' |
||
| 700 | * @param string $table The table we're inserting the data into |
||
| 701 | * @param array $columns An array of the columns we're inserting the data into. Should contain 'column' => 'datatype' pairs |
||
| 702 | * @param array $data The data to insert |
||
| 703 | * @param array $keys The keys for the table |
||
| 704 | * @param bool $disable_trans Whether to disable transactions |
||
| 705 | * @param resource $connection The connection to use (if null, $db_connection is used) |
||
| 706 | */ |
||
| 707 | View Code Duplication | function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null) |
|
| 708 | { |
||
| 709 | global $smcFunc, $db_connection, $db_prefix; |
||
| 710 | |||
| 711 | $connection = $connection === null ? $db_connection : $connection; |
||
| 712 | |||
| 713 | // With nothing to insert, simply return. |
||
| 714 | if (empty($data)) |
||
| 715 | return; |
||
| 716 | |||
| 717 | // Replace the prefix holder with the actual prefix. |
||
| 718 | $table = str_replace('{db_prefix}', $db_prefix, $table); |
||
| 719 | |||
| 720 | // Inserting data as a single row can be done as a single array. |
||
| 721 | if (!is_array($data[array_rand($data)])) |
||
| 722 | $data = array($data); |
||
| 723 | |||
| 724 | // Create the mold for a single row insert. |
||
| 725 | $insertData = '('; |
||
| 726 | foreach ($columns as $columnName => $type) |
||
| 727 | { |
||
| 728 | // Are we restricting the length? |
||
| 729 | if (strpos($type, 'string-') !== false) |
||
| 730 | $insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName); |
||
| 731 | else |
||
| 732 | $insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName); |
||
| 733 | } |
||
| 734 | $insertData = substr($insertData, 0, -2) . ')'; |
||
| 735 | |||
| 736 | // Create an array consisting of only the columns. |
||
| 737 | $indexed_columns = array_keys($columns); |
||
| 738 | |||
| 739 | // Here's where the variables are injected to the query. |
||
| 740 | $insertRows = array(); |
||
| 741 | foreach ($data as $dataRow) |
||
| 742 | $insertRows[] = smf_db_quote($insertData, array_combine($indexed_columns, $dataRow), $connection); |
||
| 743 | |||
| 744 | // Determine the method of insertion. |
||
| 745 | $queryTitle = $method == 'replace' ? 'REPLACE' : ($method == 'ignore' ? 'INSERT IGNORE' : 'INSERT'); |
||
| 746 | |||
| 747 | // Do the insert. |
||
| 748 | $smcFunc['db_query']('', ' |
||
| 749 | ' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`) |
||
| 750 | VALUES |
||
| 751 | ' . implode(', |
||
| 752 | ', $insertRows), |
||
| 753 | array( |
||
| 754 | 'security_override' => true, |
||
| 755 | 'db_error_skip' => $table === $db_prefix . 'log_errors', |
||
| 756 | ), |
||
| 757 | $connection |
||
| 758 | ); |
||
| 759 | } |
||
| 760 | |||
| 761 | /** |
||
| 762 | * This function tries to work out additional error information from a back trace. |
||
| 763 | * |
||
| 764 | * @param string $error_message The error message |
||
| 765 | * @param string $log_message The message to log |
||
| 766 | * @param string|bool $error_type What type of error this is |
||
| 767 | * @param string $file The file the error occurred in |
||
| 768 | * @param int $line What line of $file the code which generated the error is on |
||
| 769 | * @return void|array Returns an array with the file and line if $error_type is 'return' |
||
| 770 | */ |
||
| 771 | View Code Duplication | function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null) |
|
| 772 | { |
||
| 773 | if (empty($log_message)) |
||
| 774 | $log_message = $error_message; |
||
| 775 | |||
| 776 | foreach (debug_backtrace() as $step) |
||
| 777 | { |
||
| 778 | // Found it? |
||
| 779 | if (strpos($step['function'], 'query') === false && !in_array(substr($step['function'], 0, 7), array('smf_db_', 'preg_re', 'db_erro', 'call_us')) && strpos($step['function'], '__') !== 0) |
||
| 780 | { |
||
| 781 | $log_message .= '<br>Function: ' . $step['function']; |
||
| 782 | break; |
||
| 783 | } |
||
| 784 | |||
| 785 | if (isset($step['line'])) |
||
| 786 | { |
||
| 787 | $file = $step['file']; |
||
| 788 | $line = $step['line']; |
||
| 789 | } |
||
| 790 | } |
||
| 791 | |||
| 792 | // A special case - we want the file and line numbers for debugging. |
||
| 793 | if ($error_type == 'return') |
||
| 794 | return array($file, $line); |
||
| 795 | |||
| 796 | // Is always a critical error. |
||
| 797 | if (function_exists('log_error')) |
||
| 798 | log_error($log_message, 'critical', $file, $line); |
||
| 799 | |||
| 800 | if (function_exists('fatal_error')) |
||
| 801 | { |
||
| 802 | fatal_error($error_message, false); |
||
| 803 | |||
| 804 | // Cannot continue... |
||
| 805 | exit; |
||
| 806 | } |
||
| 807 | elseif ($error_type) |
||
| 808 | trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''), $error_type); |
||
| 809 | else |
||
| 810 | trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : '')); |
||
| 811 | } |
||
| 812 | |||
| 813 | /** |
||
| 814 | * Escape the LIKE wildcards so that they match the character and not the wildcard. |
||
| 815 | * |
||
| 816 | * @param string $string The string to escape |
||
| 817 | * @param bool $translate_human_wildcards If true, turns human readable wildcards into SQL wildcards. |
||
| 818 | * @return string The escaped string |
||
| 819 | */ |
||
| 820 | View Code Duplication | function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false) |
|
| 821 | { |
||
| 822 | $replacements = array( |
||
| 823 | '%' => '\%', |
||
| 824 | '_' => '\_', |
||
| 825 | '\\' => '\\\\', |
||
| 826 | ); |
||
| 827 | |||
| 828 | if ($translate_human_wildcards) |
||
| 829 | $replacements += array( |
||
| 830 | '*' => '%', |
||
| 831 | ); |
||
| 832 | |||
| 833 | return strtr($string, $replacements); |
||
| 834 | } |
||
| 835 | |||
| 836 | ?> |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.