comparison forum/Sources/Errors.php @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
comparison
equal deleted inserted replaced
75:72f59aa7e503 76:e3e11437ecea
1 <?php
2
3 /**
4 * Simple Machines Forum (SMF)
5 *
6 * @package SMF
7 * @author Simple Machines http://www.simplemachines.org
8 * @copyright 2011 Simple Machines
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
10 *
11 * @version 2.0.4
12 */
13
14 if (!defined('SMF'))
15 die('Hacking attempt...');
16
17 /* The purpose of this file is... errors. (hard to guess, huh?) It takes
18 care of logging, error messages, error handling, database errors, and
19 error log administration. It does this with:
20
21 bool db_fatal_error(bool loadavg = false)
22 - calls show_db_error().
23 - this is used for database connection error handling.
24 - loadavg means this is a load average problem, not a database error.
25
26 string log_error(string error_message, string error_type = general,
27 string filename = none, int line = none)
28 - logs an error, if error logging is enabled.
29 - depends on the enableErrorLogging setting.
30 - filename and line should be __FILE__ and __LINE__, respectively.
31 - returns the error message. (ie. die(log_error($msg));)
32
33 void fatal_error(string error_message, mixed (bool or string) log = general)
34 - stops execution and displays an error message.
35 - logs the error message if log is missing or true.
36
37 void fatal_lang_error(string error_message_key, mixed (bool or string) log = general,
38 array sprintf = array())
39 - stops execution and displays an error message by key.
40 - uses the string with the error_message_key key.
41 - loads the Errors language file.
42 - applies the sprintf information if specified.
43 - the information is logged if log is true or missing.
44 - logs the error in the forum's default language while displaying the error
45 message in the user's language
46
47 void error_handler(int error_level, string error_string, string filename,
48 int line)
49 - this is a standard PHP error handler replacement.
50 - dies with fatal_error() if the error_level matches with
51 error_reporting.
52
53 void setup_fatal_error_context(string error_message)
54 - uses the fatal_error sub template of the Errors template - or the
55 error sub template in the Wireless template.
56 - used by fatal_error() and fatal_lang_error()
57
58 void show_db_error(bool loadavg = false)
59 - called by db_fatal_error() function
60 - shows a complete page independent of language files or themes.
61 - used only if there's no way to connect to the database or the
62 load averages are too high to do so.
63 - loadavg means this is a load average problem, not a database error.
64 - stops further execution of the script.
65 */
66
67 // Handle fatal errors - like connection errors or load average problems
68 function db_fatal_error($loadavg = false)
69 {
70 global $sourcedir;
71
72 show_db_error($loadavg);
73
74 // Since we use "or db_fatal_error();" this is needed...
75 return false;
76 }
77
78 // Log an error, if the option is on.
79 function log_error($error_message, $error_type = 'general', $file = null, $line = null)
80 {
81 global $txt, $modSettings, $sc, $user_info, $smcFunc, $scripturl, $last_error;
82
83 // Check if error logging is actually on.
84 if (empty($modSettings['enableErrorLogging']))
85 return $error_message;
86
87 // Basically, htmlspecialchars it minus &. (for entities!)
88 $error_message = strtr($error_message, array('<' => '&lt;', '>' => '&gt;', '"' => '&quot;'));
89 $error_message = strtr($error_message, array('&lt;br /&gt;' => '<br />', '&lt;b&gt;' => '<strong>', '&lt;/b&gt;' => '</strong>', "\n" => '<br />'));
90
91 // Add a file and line to the error message?
92 // Don't use the actual txt entries for file and line but instead use %1$s for file and %2$s for line
93 if ($file == null)
94 $file = '';
95 else
96 // Window style slashes don't play well, lets convert them to the unix style.
97 $file = str_replace('\\', '/', $file);
98
99 if ($line == null)
100 $line = 0;
101 else
102 $line = (int) $line;
103
104 // Just in case there's no id_member or IP set yet.
105 if (empty($user_info['id']))
106 $user_info['id'] = 0;
107 if (empty($user_info['ip']))
108 $user_info['ip'] = '';
109
110 // Find the best query string we can...
111 $query_string = empty($_SERVER['QUERY_STRING']) ? (empty($_SERVER['REQUEST_URL']) ? '' : str_replace($scripturl, '', $_SERVER['REQUEST_URL'])) : $_SERVER['QUERY_STRING'];
112
113 // Don't log the session hash in the url twice, it's a waste.
114 $query_string = htmlspecialchars((SMF == 'SSI' ? '' : '?') . preg_replace(array('~;sesc=[^&;]+~', '~' . session_name() . '=' . session_id() . '[&;]~'), array(';sesc', ''), $query_string));
115
116 // Just so we know what board error messages are from.
117 if (isset($_POST['board']) && !isset($_GET['board']))
118 $query_string .= ($query_string == '' ? 'board=' : ';board=') . $_POST['board'];
119
120 // What types of categories do we have?
121 $known_error_types = array(
122 'general',
123 'critical',
124 'database',
125 'undefined_vars',
126 'user',
127 'template',
128 'debug',
129 );
130
131 // Make sure the category that was specified is a valid one
132 $error_type = in_array($error_type, $known_error_types) && $error_type !== true ? $error_type : 'general';
133
134 // Don't log the same error countless times, as we can get in a cycle of depression...
135 $error_info = array($user_info['id'], time(), $user_info['ip'], $query_string, $error_message, (string) $sc, $error_type, $file, $line);
136 if (empty($last_error) || $last_error != $error_info)
137 {
138 // Insert the error into the database.
139 $smcFunc['db_insert']('',
140 '{db_prefix}log_errors',
141 array('id_member' => 'int', 'log_time' => 'int', 'ip' => 'string-16', 'url' => 'string-65534', 'message' => 'string-65534', 'session' => 'string', 'error_type' => 'string', 'file' => 'string-255', 'line' => 'int'),
142 $error_info,
143 array('id_error')
144 );
145 $last_error = $error_info;
146 }
147
148 // Return the message to make things simpler.
149 return $error_message;
150 }
151
152 // An irrecoverable error.
153 function fatal_error($error, $log = 'general')
154 {
155 global $txt, $context, $modSettings;
156
157 // We don't have $txt yet, but that's okay...
158 if (empty($txt))
159 die($error);
160
161 setup_fatal_error_context($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2) ? log_error($error, $log) : $error);
162 }
163
164 // A fatal error with a message stored in the language file.
165 function fatal_lang_error($error, $log = 'general', $sprintf = array())
166 {
167 global $txt, $language, $modSettings, $user_info, $context;
168 static $fatal_error_called = false;
169
170 // Try to load a theme if we don't have one.
171 if (empty($context['theme_loaded']) && empty($fatal_error_called))
172 {
173 $fatal_error_called = true;
174 loadTheme();
175 }
176
177 // If we have no theme stuff we can't have the language file...
178 if (empty($context['theme_loaded']))
179 die($error);
180
181 $reload_lang_file = true;
182 // Log the error in the forum's language, but don't waste the time if we aren't logging
183 if ($log || (!empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2))
184 {
185 loadLanguage('Errors', $language);
186 $reload_lang_file = $language != $user_info['language'];
187 $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
188 log_error($error_message, $log);
189 }
190
191 // Load the language file, only if it needs to be reloaded
192 if ($reload_lang_file)
193 {
194 loadLanguage('Errors');
195 $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
196 }
197
198 setup_fatal_error_context($error_message);
199 }
200
201 // Handler for standard error messages.
202 function error_handler($error_level, $error_string, $file, $line)
203 {
204 global $settings, $modSettings, $db_show_debug;
205
206 // Ignore errors if we're ignoring them or they are strict notices from PHP 5 (which cannot be solved without breaking PHP 4.)
207 if (error_reporting() == 0 || (defined('E_STRICT') && $error_level == E_STRICT && (empty($modSettings['enableErrorLogging']) || $modSettings['enableErrorLogging'] != 2)))
208 return;
209
210 if (strpos($file, 'eval()') !== false && !empty($settings['current_include_filename']))
211 {
212 if (function_exists('debug_backtrace'))
213 {
214 $array = debug_backtrace();
215 for ($i = 0; $i < count($array); $i++)
216 {
217 if ($array[$i]['function'] != 'loadSubTemplate')
218 continue;
219
220 // This is a bug in PHP, with eval, it seems!
221 if (empty($array[$i]['args']))
222 $i++;
223 break;
224 }
225
226 if (isset($array[$i]) && !empty($array[$i]['args']))
227 $file = realpath($settings['current_include_filename']) . ' (' . $array[$i]['args'][0] . ' sub template - eval?)';
228 else
229 $file = realpath($settings['current_include_filename']) . ' (eval?)';
230 }
231 else
232 $file = realpath($settings['current_include_filename']) . ' (eval?)';
233 }
234
235 if (isset($db_show_debug) && $db_show_debug === true)
236 {
237 // Commonly, undefined indexes will occur inside attributes; try to show them anyway!
238 if ($error_level % 255 != E_ERROR)
239 {
240 $temporary = ob_get_contents();
241 if (substr($temporary, -2) == '="')
242 echo '"';
243 }
244
245 // Debugging! This should look like a PHP error message.
246 echo '<br />
247 <strong>', $error_level % 255 == E_ERROR ? 'Error' : ($error_level % 255 == E_WARNING ? 'Warning' : 'Notice'), '</strong>: ', $error_string, ' in <strong>', $file, '</strong> on line <strong>', $line, '</strong><br />';
248 }
249
250 $error_type = strpos(strtolower($error_string), 'undefined') !== false ? 'undefined_vars' : 'general';
251
252 $message = log_error($error_level . ': ' . $error_string, $error_type, $file, $line);
253
254 // Let's give integrations a chance to ouput a bit differently
255 call_integration_hook('integrate_output_error', array($message, $error_type, $error_level, $file, $line));
256
257 // Dying on these errors only causes MORE problems (blank pages!)
258 if ($file == 'Unknown')
259 return;
260
261 // If this is an E_ERROR or E_USER_ERROR.... die. Violently so.
262 if ($error_level % 255 == E_ERROR)
263 obExit(false);
264 else
265 return;
266
267 // If this is an E_ERROR, E_USER_ERROR, E_WARNING, or E_USER_WARNING.... die. Violently so.
268 if ($error_level % 255 == E_ERROR || $error_level % 255 == E_WARNING)
269 fatal_error(allowedTo('admin_forum') ? $message : $error_string, false);
270
271 // We should NEVER get to this point. Any fatal error MUST quit, or very bad things can happen.
272 if ($error_level % 255 == E_ERROR)
273 die('Hacking attempt...');
274 }
275
276 function setup_fatal_error_context($error_message)
277 {
278 global $context, $txt, $ssi_on_error_method;
279 static $level = 0;
280
281 // Attempt to prevent a recursive loop.
282 ++$level;
283 if ($level > 1)
284 return false;
285
286 // Maybe they came from dlattach or similar?
287 if (SMF != 'SSI' && empty($context['theme_loaded']))
288 loadTheme();
289
290 // Don't bother indexing errors mate...
291 $context['robot_no_index'] = true;
292
293 if (!isset($context['error_title']))
294 $context['error_title'] = $txt['error_occured'];
295 $context['error_message'] = isset($context['error_message']) ? $context['error_message'] : $error_message;
296
297 if (empty($context['page_title']))
298 $context['page_title'] = $context['error_title'];
299
300 // Display the error message - wireless?
301 if (defined('WIRELESS') && WIRELESS)
302 $context['sub_template'] = WIRELESS_PROTOCOL . '_error';
303 // Load the template and set the sub template.
304 else
305 {
306 loadTemplate('Errors');
307 $context['sub_template'] = 'fatal_error';
308 }
309
310 // If this is SSI, what do they want us to do?
311 if (SMF == 'SSI')
312 {
313 if (!empty($ssi_on_error_method) && $ssi_on_error_method !== true && is_callable($ssi_on_error_method))
314 $ssi_on_error_method();
315 elseif (empty($ssi_on_error_method) || $ssi_on_error_method !== true)
316 loadSubTemplate('fatal_error');
317
318 // No layers?
319 if (empty($ssi_on_error_method) || $ssi_on_error_method !== true)
320 exit;
321 }
322
323 // We want whatever for the header, and a footer. (footer includes sub template!)
324 obExit(null, true, false, true);
325
326 /* DO NOT IGNORE:
327 If you are creating a bridge to SMF or modifying this function, you MUST
328 make ABSOLUTELY SURE that this function quits and DOES NOT RETURN TO NORMAL
329 PROGRAM FLOW. Otherwise, security error messages will not be shown, and
330 your forum will be in a very easily hackable state.
331 */
332 trigger_error('Hacking attempt...', E_USER_ERROR);
333 }
334
335 // Show an error message for the connection problems.
336 function show_db_error($loadavg = false)
337 {
338 global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings;
339 global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc;
340
341 // Just check we're not in any buffers, just in case.
342 for ($i = ob_get_level(); $i > 0; $i--)
343 @ob_end_clean();
344
345 // Don't cache this page!
346 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
347 header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
348 header('Cache-Control: no-cache');
349
350 // Send the right error codes.
351 header('HTTP/1.1 503 Service Temporarily Unavailable');
352 header('Status: 503 Service Temporarily Unavailable');
353 header('Retry-After: 3600');
354
355 if ($loadavg == false)
356 {
357 // For our purposes, we're gonna want this on if at all possible.
358 $modSettings['cache_enable'] = '1';
359
360 if (($temp = cache_get_data('db_last_error', 600)) !== null)
361 $db_last_error = max($db_last_error, $temp);
362
363 if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
364 {
365 require_once($sourcedir . '/Subs-Admin.php');
366
367 // Avoid writing to the Settings.php file if at all possible; use shared memory instead.
368 cache_put_data('db_last_error', time(), 600);
369 if (($temp = cache_get_data('db_last_error', 600)) == null)
370 updateLastDatabaseError();
371
372 // Language files aren't loaded yet :(.
373 $db_error = @$smcFunc['db_error']($db_connection);
374 @mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . $smcFunc['db_title'] . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.');
375 }
376 }
377
378 if (!empty($maintenance))
379 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
380 <html xmlns="http://www.w3.org/1999/xhtml">
381 <head>
382 <meta name="robots" content="noindex" />
383 <title>', $mtitle, '</title>
384 </head>
385 <body>
386 <h3>', $mtitle, '</h3>
387 ', $mmessage, '
388 </body>
389 </html>';
390 // If this is a load average problem, display an appropriate message (but we still don't have language files!)
391 elseif ($loadavg)
392 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
393 <html xmlns="http://www.w3.org/1999/xhtml">
394 <head>
395 <meta name="robots" content="noindex" />
396 <title>Temporarily Unavailable</title>
397 </head>
398 <body>
399 <h3>Temporarily Unavailable</h3>
400 Due to high stress on the server the forum is temporarily unavailable. Please try again later.
401 </body>
402 </html>';
403 // What to do? Language files haven't and can't be loaded yet...
404 else
405 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
406 <html xmlns="http://www.w3.org/1999/xhtml">
407 <head>
408 <meta name="robots" content="noindex" />
409 <title>Connection Problems</title>
410 </head>
411 <body>
412 <h3>Connection Problems</h3>
413 Sorry, SMF was unable to connect to the database. This may be caused by the server being busy. Please try again later.
414 </body>
415 </html>';
416
417 die;
418 }
419
420 ?>