Created
          December 9, 2010 06:46 
        
      - 
      
- 
        Save sprice/734417 to your computer and use it in GitHub Desktop. 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | ? sites/all/modules | |
| ? sites/default/files | |
| ? sites/default/settings.php | |
| Index: install.php | |
| =================================================================== | |
| RCS file: /cvs/drupal/drupal/install.php,v | |
| retrieving revision 1.113.2.12 | |
| diff -u -p -r1.113.2.12 install.php | |
| --- install.php 9 May 2010 14:13:31 -0000 1.113.2.12 | |
| +++ install.php 7 Oct 2010 21:55:51 -0000 | |
| @@ -20,6 +20,14 @@ function install_main() { | |
| require_once './includes/bootstrap.inc'; | |
| drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); | |
| + // The user agent header is used to pass a database prefix in the request when | |
| + // running tests. However, for security reasons, it is imperative that no | |
| + // installation be permitted using such a prefix. | |
| + if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) { | |
| + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); | |
| + exit; | |
| + } | |
| + | |
| // This must go after drupal_bootstrap(), which unsets globals! | |
| global $profile, $install_locale, $conf; | |
| Index: includes/bootstrap.inc | |
| =================================================================== | |
| RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v | |
| retrieving revision 1.206.2.29 | |
| diff -u -p -r1.206.2.29 bootstrap.inc | |
| --- includes/bootstrap.inc 6 Aug 2010 11:50:24 -0000 1.206.2.29 | |
| +++ includes/bootstrap.inc 7 Oct 2010 21:55:51 -0000 | |
| @@ -1112,7 +1112,7 @@ function drupal_bootstrap($phase) { | |
| } | |
| function _drupal_bootstrap($phase) { | |
| - global $conf; | |
| + global $conf, $db_prefix; | |
| switch ($phase) { | |
| @@ -1138,6 +1138,19 @@ function _drupal_bootstrap($phase) { | |
| break; | |
| case DRUPAL_BOOTSTRAP_DATABASE: | |
| + // The user agent header is used to pass a database prefix in the request when | |
| + // running tests. However, for security reasons, it is imperative that we | |
| + // validate we ourselves made the request. | |
| + $GLOBALS['simpletest_installed'] = TRUE; | |
| + if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);/", $_SERVER['HTTP_USER_AGENT'], $matches)) { | |
| + if (!drupal_valid_test_ua($_SERVER['HTTP_USER_AGENT'])) { | |
| + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); | |
| + exit; | |
| + } | |
| + $db_prefix_string = is_array($db_prefix) ? $db_prefix['default'] : $db_prefix; | |
| + $db_prefix = $db_prefix_string . $matches[1]; | |
| + } | |
| + | |
| // Initialize the default database. | |
| require_once './includes/database.inc'; | |
| db_set_active(); | |
| @@ -1331,3 +1344,46 @@ function ip_address() { | |
| return $ip_address; | |
| } | |
| + | |
| +/** | |
| + * Validate the HMAC and timestamp of a user agent header from simpletest. | |
| + */ | |
| +function drupal_valid_test_ua($user_agent) { | |
| +// global $dbatabases; | |
| + global $db_url; | |
| + | |
| + list($prefix, $time, $salt, $hmac) = explode(';', $user_agent); | |
| + $check_string = $prefix . ';' . $time . ';' . $salt; | |
| + // We use the database credentials from settings.php to make the HMAC key, since | |
| + // the database is not yet initialized and we can't access any Drupal variables. | |
| + // The file properties add more entropy not easily accessible to others. | |
| +// $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc'; | |
| + $filepath = './includes/bootstrap.inc'; | |
| +// $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE); | |
| + $key = sha1(serialize($db_url) . filectime($filepath) . fileinode($filepath), TRUE); | |
| + // The HMAC must match. | |
| + return $hmac == base64_encode(hash_hmac('sha1', $check_string, $key, TRUE)); | |
| +} | |
| + | |
| +/** | |
| + * Generate a user agent string with a HMAC and timestamp for simpletest. | |
| + */ | |
| +function drupal_generate_test_ua($prefix) { | |
| +// global $dbatabases; | |
| + global $db_url; | |
| + static $key; | |
| + | |
| + if (!isset($key)) { | |
| + // We use the database credentials to make the HMAC key, since we | |
| + // check the HMAC before the database is initialized. filectime() | |
| + // and fileinode() are not easily determined from remote. | |
| +// $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc'; | |
| + $filepath = './includes/bootstrap.inc'; | |
| +// $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE); | |
| + $key = sha1(serialize($db_url) . filectime($filepath) . fileinode($filepath), TRUE); | |
| + } | |
| + // Generate a moderately secure HMAC based on the database credentials. | |
| + $salt = uniqid('', TRUE); | |
| + $check_string = $prefix . ';' . time() . ';' . $salt; | |
| + return $check_string . ';' . base64_encode(hash_hmac('sha1', $check_string, $key, TRUE)); | |
| +} | |
| Index: includes/common.inc | |
| =================================================================== | |
| RCS file: /cvs/drupal/drupal/includes/common.inc,v | |
| retrieving revision 1.756.2.98 | |
| diff -u -p -r1.756.2.98 common.inc | |
| --- includes/common.inc 6 Sep 2010 11:13:27 -0000 1.756.2.98 | |
| +++ includes/common.inc 7 Oct 2010 21:55:51 -0000 | |
| @@ -531,7 +531,7 @@ function drupal_http_request($url, $head | |
| // same time won't interfere with each other as they would if the database | |
| // prefix were stored statically in a file or database variable. | |
| if (is_string($db_prefix) && preg_match("/^simpletest\d+$/", $db_prefix, $matches)) { | |
| - $defaults['User-Agent'] = 'User-Agent: ' . $matches[0]; | |
| + $defaults['User-Agent'] = 'User-Agent: ' . drupal_generate_test_ua($matches[0]); | |
| } | |
| foreach ($headers as $header => $value) { | |
| @@ -2661,14 +2661,24 @@ function _drupal_bootstrap_full() { | |
| require_once './includes/mail.inc'; | |
| require_once './includes/actions.inc'; | |
| // Set the Drupal custom error handler. | |
| - set_error_handler('drupal_error_handler'); | |
| + set_error_handler('_drupal_error_handler'); | |
| + set_exception_handler('_drupal_exception_handler'); | |
| // Emit the correct charset HTTP header. | |
| drupal_set_header('Content-Type: text/html; charset=utf-8'); | |
| // Detect string handling method | |
| unicode_check(); | |
| // Undo magic quotes | |
| fix_gpc_magic(); | |
| - // Load all enabled modules | |
| + | |
| + if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'simpletest') !== FALSE) { | |
| + // Valid SimpleTest user-agent, log fatal errors to test specific file | |
| + // directory. The user-agent is validated in DRUPAL_BOOTSTRAP_DATABASE | |
| + // phase so as long as it is a SimpleTest user-agent it is valid. | |
| + ini_set('log_errors', 1); | |
| + ini_set('error_log', file_directory_path() . '/error.log'); | |
| + } | |
| + | |
| +// Load all enabled modules | |
| module_load_all(); | |
| // Let all modules take action before menu system handles the request | |
| // We do not want this while running update.php. | |
| @@ -3789,3 +3799,264 @@ function _drupal_flush_css_js() { | |
| } | |
| variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19)); | |
| } | |
| + | |
| +/** | |
| + * Error reporting level: display no errors. | |
| + */ | |
| +define('ERROR_REPORTING_HIDE', 0); | |
| + | |
| +/** | |
| + * Error reporting level: display errors and warnings. | |
| + */ | |
| +define('ERROR_REPORTING_DISPLAY_SOME', 1); | |
| + | |
| +/** | |
| + * Error reporting level: display all messages. | |
| + */ | |
| +define('ERROR_REPORTING_DISPLAY_ALL', 2); | |
| + | |
| +/** | |
| + * Custom PHP error handler. | |
| + * | |
| + * @param $error_level | |
| + * The level of the error raised. | |
| + * @param $message | |
| + * The error message. | |
| + * @param $filename | |
| + * The filename that the error was raised in. | |
| + * @param $line | |
| + * The line number the error was raised at. | |
| + * @param $context | |
| + * An array that points to the active symbol table at the point the error occurred. | |
| + */ | |
| +function _drupal_error_handler($error_level, $message, $filename, $line, $context) { | |
| + if ($error_level & error_reporting()) { | |
| + // All these constants are documented at http://php.net/manual/en/errorfunc.constants.php | |
| + $types = array( | |
| + E_ERROR => 'Error', | |
| + E_WARNING => 'Warning', | |
| + E_PARSE => 'Parse error', | |
| + E_NOTICE => 'Notice', | |
| + E_CORE_ERROR => 'Core error', | |
| + E_CORE_WARNING => 'Core warning', | |
| + E_COMPILE_ERROR => 'Compile error', | |
| + E_COMPILE_WARNING => 'Compile warning', | |
| + E_USER_ERROR => 'User error', | |
| + E_USER_WARNING => 'User warning', | |
| + E_USER_NOTICE => 'User notice', | |
| + E_STRICT => 'Strict warning', | |
| + E_RECOVERABLE_ERROR => 'Recoverable fatal error' | |
| + ); | |
| + $caller = _drupal_get_last_caller(debug_backtrace()); | |
| + | |
| + // We treat recoverable errors as fatal. | |
| + _drupal_log_error(array( | |
| + '%type' => isset($types[$error_level]) ? $types[$error_level] : 'Unknown error', | |
| + '%message' => $message, | |
| + '%function' => $caller['function'], | |
| + '%file' => $caller['file'], | |
| + '%line' => $caller['line'], | |
| + ), $error_level == E_RECOVERABLE_ERROR); | |
| + } | |
| +} | |
| + | |
| +/** | |
| + * Custom PHP exception handler. | |
| + * | |
| + * Uncaught exceptions are those not enclosed in a try/catch block. They are | |
| + * always fatal: the execution of the script will stop as soon as the exception | |
| + * handler exits. | |
| + * | |
| + * @param $exception | |
| + * The exception object that was thrown. | |
| + */ | |
| +function _drupal_exception_handler($exception) { | |
| + // Log the message to the watchdog and return an error page to the user. | |
| + _drupal_log_error(_drupal_decode_exception($exception), TRUE); | |
| +} | |
| + | |
| +/** | |
| + * Decode an exception, especially to retrive the correct caller. | |
| + * | |
| + * @param $exception | |
| + * The exception object that was thrown. | |
| + * @return An error in the format expected by _drupal_log_error(). | |
| + */ | |
| +function _drupal_decode_exception($exception) { | |
| + $message = $exception->getMessage(); | |
| + | |
| + $backtrace = $exception->getTrace(); | |
| + // Add the line throwing the exception to the backtrace. | |
| + array_unshift($backtrace, array('line' => $exception->getLine(), 'file' => $exception->getFile())); | |
| + | |
| + // For PDOException errors, we try to return the initial caller, | |
| + // skipping internal functions of the database layer. | |
| + if (is_a($exception, 'PDOException')) { | |
| + // The first element in the stack is the call, the second element gives us the caller. | |
| + // We skip calls that occurred in one of the classes of the database layer | |
| + // or in one of its global functions. | |
| +// $db_functions = array('db_query', 'pager_query', 'db_query_range', 'db_query_temporary', 'update_sql'); | |
| + $db_functions = array('db_query', '_db_query', 'pager_query', 'db_query_range', 'db_query_temporary', 'update_sql'); | |
| + while (!empty($backtrace[1]) && ($caller = $backtrace[1]) && | |
| + ((isset($caller['class']) && (strpos($caller['class'], 'Query') !== FALSE || strpos($caller['class'], 'Database') !== FALSE || strpos($caller['class'], 'PDO') !== FALSE)) || | |
| + in_array($caller['function'], $db_functions))) { | |
| + // We remove that call. | |
| + array_shift($backtrace); | |
| + } | |
| + if (isset($exception->query_string, $exception->args)) { | |
| + $message .= ": " . $exception->query_string . "; " . print_r($exception->args, TRUE); | |
| + } | |
| + } | |
| + $caller = _drupal_get_last_caller($backtrace); | |
| + | |
| + return array( | |
| + '%type' => get_class($exception), | |
| + '%message' => $message, | |
| + '%function' => $caller['function'], | |
| + '%file' => $caller['file'], | |
| + '%line' => $caller['line'], | |
| + ); | |
| +} | |
| + | |
| +/** | |
| + * Log a PHP error or exception, display an error page in fatal cases. | |
| + * | |
| + * @param $error | |
| + * An array with the following keys: %type, %message, %function, %file, %line. | |
| + * @param $fatal | |
| + * TRUE if the error is fatal. | |
| + */ | |
| +function _drupal_log_error($error, $fatal = FALSE) { | |
| + // Initialize a maintenance theme if the boostrap was not complete. | |
| + // Do it early because drupal_set_message() triggers a drupal_theme_initialize(). | |
| +// if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) { | |
| + if ($fatal) { // Assumed full bootstrap since common.inc loaded. | |
| + unset($GLOBALS['theme']); | |
| + if (!defined('MAINTENANCE_MODE')) { | |
| + define('MAINTENANCE_MODE', 'error'); | |
| + } | |
| + drupal_maintenance_theme(); | |
| + } | |
| + | |
| + // When running inside the testing framework, we relay the errors | |
| + // to the tested site by the way of HTTP headers. | |
| + if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^simpletest\d+;/", $_SERVER['HTTP_USER_AGENT']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) { | |
| + // $number does not use drupal_static as it should not be reset | |
| + // as it uniquely identifies each PHP error. | |
| + static $number = 0; | |
| + $assertion = array( | |
| + $error['%message'], | |
| + $error['%type'], | |
| + array( | |
| + 'function' => $error['%function'], | |
| + 'file' => $error['%file'], | |
| + 'line' => $error['%line'], | |
| + ), | |
| + ); | |
| + header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion))); | |
| + $number++; | |
| + } | |
| + | |
| + try { | |
| + watchdog('php', '%type: %message in %function (line %line of %file).', $error, WATCHDOG_ERROR); | |
| + } | |
| + catch (Exception $e) { | |
| + // Ignore any additional watchdog exception, as that probably means | |
| + // that the database was not initialized correctly. | |
| + } | |
| + | |
| + if ($fatal) { | |
| + drupal_set_header('500 Service unavailable (with message)'); | |
| + } | |
| + | |
| + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { | |
| + if ($fatal) { | |
| + // When called from JavaScript, simply output the error message. | |
| + print t('%type: %message in %function (line %line of %file).', $error); | |
| + exit; | |
| + } | |
| + } | |
| + else { | |
| + // Display the message if the current error reporting level allows this type | |
| + // of message to be displayed, and unconditionnaly in update.php. | |
| + $error_level = variable_get('error_level', ERROR_REPORTING_DISPLAY_ALL); | |
| + $display_error = $error_level == ERROR_REPORTING_DISPLAY_ALL || ($error_level == ERROR_REPORTING_DISPLAY_SOME && $error['%type'] != 'Notice'); | |
| + if ($display_error || (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update')) { | |
| + $class = 'error'; | |
| + | |
| + // If error type is 'User notice' then treat it as debug information | |
| + // instead of an error message, see dd(). | |
| + if ($error['%type'] == 'User notice') { | |
| + $error['%type'] = 'Debug'; | |
| + $class = 'status'; | |
| + } | |
| + | |
| + drupal_set_message(t('%type: %message in %function (line %line of %file).', $error), $class); | |
| + } | |
| + | |
| + if ($fatal) { | |
| + drupal_set_title(t('Error')); | |
| + // We fallback to a maintenance page at this point, because the page generation | |
| + // itself can generate errors. | |
| + print theme('maintenance_page', t('The website encountered an unexpected error. Please try again later.')); | |
| + exit; | |
| + } | |
| + } | |
| +} | |
| + | |
| +/** | |
| + * Gets the last caller from a backtrace. | |
| + * | |
| + * @param $backtrace | |
| + * A standard PHP backtrace. | |
| + * @return | |
| + * An associative array with keys 'file', 'line' and 'function'. | |
| + */ | |
| +function _drupal_get_last_caller($backtrace) { | |
| + // Errors that occur inside PHP internal functions do not generate | |
| + // information about file and line. Ignore black listed functions. | |
| + $blacklist = array('debug'); | |
| + while (($backtrace && !isset($backtrace[0]['line'])) || | |
| + (isset($backtrace[1]['function']) && in_array($backtrace[1]['function'], $blacklist))) { | |
| + array_shift($backtrace); | |
| + } | |
| + | |
| + // The first trace is the call itself. | |
| + // It gives us the line and the file of the last call. | |
| + $call = $backtrace[0]; | |
| + | |
| + // The second call give us the function where the call originated. | |
| + if (isset($backtrace[1])) { | |
| + if (isset($backtrace[1]['class'])) { | |
| + $call['function'] = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()'; | |
| + } | |
| + else { | |
| + $call['function'] = $backtrace[1]['function'] . '()'; | |
| + } | |
| + } | |
| + else { | |
| + $call['function'] = 'main()'; | |
| + } | |
| + return $call; | |
| +} | |
| + | |
| +/** | |
| + * Debug function used for outputting debug information. | |
| + * | |
| + * The debug information is passed on to trigger_error() after being converted | |
| + * to a string using _drupal_debug_message(). | |
| + * | |
| + * @param $data | |
| + * Data to be output. | |
| + * @param $label | |
| + * Label to prefix the data. | |
| + * @param $print_r | |
| + * Flag to switch between print_r() and var_export() for data conversion to | |
| + * string. Set $print_r to TRUE when dealing with a recursive data structure | |
| + * as var_export() will generate an error. | |
| + */ | |
| +function debug($data, $label = NULL, $print_r = FALSE) { | |
| + // Print $data contents to string. | |
| + $string = $print_r ? print_r($data, TRUE) : var_export($data, TRUE); | |
| + trigger_error(trim($label ? "$label: $string" : $string)); | |
| +} | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment