From fc43e72dc72834ee1a1a08479b68daadaf69ef03 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Fri, 23 Feb 2018 16:09:30 -0600 Subject: [PATCH 1/5] Backport of fixes for SA-CORE-2018-001 (#112) * Backport of fixes for SA-CORE-2018-001 * Update Drupal version to 6.39 --- includes/common.inc | 2 +- misc/drupal.js | 104 ++++++++++++++++++++++++++++++++++- modules/system/system.module | 2 +- 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index 0e4f48490..29e0df0a6 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1481,7 +1481,7 @@ function url($path = NULL, $options = array()) { ); if (!isset($options['external'])) { - $options['external'] = menu_path_is_external($path); + $options['external'] = $_GET['q'] === $path ? FALSE : menu_path_is_external($path); } // May need language dependent rewriting if language.inc is present. diff --git a/misc/drupal.js b/misc/drupal.js index 975556bf4..6eb2d4450 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -20,6 +20,42 @@ return jquery_init.call(this, selector, context, rootjQuery); }; jQuery.fn.init.prototype = jquery_init.prototype; + + /** + * Pre-filter Ajax requests to guard against XSS attacks. + * + * See https://github.com/jquery/jquery/issues/2432 + */ + if ($.ajaxPrefilter) { + // For newer versions of jQuery, use an Ajax prefilter to prevent + // auto-executing script tags from untrusted domains. This is similar to the + // fix that is built in to jQuery 3.0 and higher. + $.ajaxPrefilter(function (s) { + if (s.crossDomain) { + s.contents.script = false; + } + }); + } + else if ($.httpData) { + // For the version of jQuery that ships with Drupal core, override + // jQuery.httpData to prevent auto-detecting "script" data types from + // untrusted domains. + var jquery_httpData = $.httpData; + $.httpData = function (xhr, type, s) { + // @todo Consider backporting code from newer jQuery versions to check for + // a cross-domain request here, rather than using Drupal.urlIsLocal() to + // block scripts from all URLs that are not on the same site. + if (!type && (!s || !Drupal.urlIsLocal(s.url))) { + var content_type = xhr.getResponseHeader('content-type') || ''; + if (content_type.indexOf('javascript') >= 0) { + // Default to a safe data type. + type = 'text'; + } + } + return jquery_httpData.call(this, xhr, type, s); + }; + $.httpData.prototype = jquery_httpData.prototype; + } })(); var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {} }; @@ -69,7 +105,7 @@ Drupal.attachBehaviors = function(context) { */ Drupal.checkPlain = function(str) { str = String(str); - var replace = { '&': '&', '"': '"', '<': '<', '>': '>' }; + var replace = { '&': '&', "'": ''', '"': '"', '<': '<', '>': '>' }; for (var character in replace) { var regex = new RegExp(character, 'g'); str = str.replace(regex, replace[character]); @@ -174,6 +210,72 @@ Drupal.formatPlural = function(count, singular, plural, args) { } }; +/** + * Returns the passed in URL as an absolute URL. + * + * @param url + * The URL string to be normalized to an absolute URL. + * + * @return + * The normalized, absolute URL. + * + * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js + * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript + * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53 + */ +Drupal.absoluteUrl = function (url) { + var urlParsingNode = document.createElement('a'); + + // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8 + // strings may throw an exception. + try { + url = decodeURIComponent(url); + } catch (e) {} + + urlParsingNode.setAttribute('href', url); + + // IE <= 7 normalizes the URL when assigned to the anchor node similar to + // the other browsers. + return urlParsingNode.cloneNode(false).href; +}; + +/** + * Returns true if the URL is within Drupal's base path. + * + * @param url + * The URL string to be tested. + * + * @return + * Boolean true if local. + * + * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58 + */ +Drupal.urlIsLocal = function (url) { + // Always use browser-derived absolute URLs in the comparison, to avoid + // attempts to break out of the base path using directory traversal. + var absoluteUrl = Drupal.absoluteUrl(url); + var protocol = location.protocol; + + // Consider URLs that match this site's base URL but use HTTPS instead of HTTP + // as local as well. + if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) { + protocol = 'https:'; + } + var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1); + + // Decoding non-UTF-8 strings may throw an exception. + try { + absoluteUrl = decodeURIComponent(absoluteUrl); + } catch (e) {} + try { + baseUrl = decodeURIComponent(baseUrl); + } catch (e) {} + + // The given URL matches the site's base URL, or has a path under the site's + // base URL. + return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0; +}; + /** * Generate the themed representation of a Drupal object. * diff --git a/modules/system/system.module b/modules/system/system.module index dc421d2e5..93962ac5b 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.38'); +define('VERSION', '6.39'); /** * Core API compatibility. From 2d234e178e17f3db557a0919eec303917ece09b0 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Wed, 28 Mar 2018 14:53:39 -0500 Subject: [PATCH 2/5] Backport of fixes from SA-CORE-2018-002 (#116) --- includes/bootstrap.inc | 55 ++++++++++++++++++++++++++++++++++++ modules/system/system.module | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index acc055e74..afed7408e 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1483,6 +1483,7 @@ function _drupal_bootstrap($phase) { timer_start('page'); // Initialize the configuration conf_init(); + _drupal_bootstrap_sanitize_request(); break; case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: @@ -2207,3 +2208,57 @@ function filter_xss_bad_protocol($string, $decode = TRUE) { } while ($before != $string); return check_plain($string); } + +/** + * Sanitizes unsafe keys from the request. + */ +function _drupal_bootstrap_sanitize_request() { + global $conf; + static $sanitized; + + if (!$sanitized) { + // Ensure the whitelist array exists. + if (!isset($conf['sanitize_input_whitelist']) || !is_array($conf['sanitize_input_whitelist'])) { + $conf['sanitize_input_whitelist'] = array(); + } + + $sanitized_keys = _drupal_bootstrap_sanitize_input($_GET, $conf['sanitize_input_whitelist']); + $sanitized_keys = array_merge($sanitized_keys, _drupal_bootstrap_sanitize_input($_POST, $conf['sanitize_input_whitelist'])); + $sanitized_keys = array_merge($sanitized_keys, _drupal_bootstrap_sanitize_input($_REQUEST, $conf['sanitize_input_whitelist'])); + $sanitized_keys = array_merge($sanitized_keys, _drupal_bootstrap_sanitize_input($_COOKIE, $conf['sanitize_input_whitelist'])); + $sanitized_keys = array_unique($sanitized_keys); + + if (count($sanitized_keys) && !empty($conf['sanitize_input_logging'])) { + trigger_error(check_plain(sprintf('Potentially unsafe keys removed from request parameters: %s', implode(', ', $sanitized_keys)), E_USER_WARNING)); + } + + $sanitized = TRUE; + } +} + +/** + * Sanitizes unsafe keys from user input. + * + * @param mixed $input + * Input to sanitize. + * @param array $whitelist + * Whitelist of values. + * @return array + */ +function _drupal_bootstrap_sanitize_input(&$input, $whitelist = array()) { + $sanitized_keys = array(); + + if (is_array($input)) { + foreach ($input as $key => $value) { + if ($key !== '' && $key[0] === '#' && !in_array($key, $whitelist, TRUE)) { + unset($input[$key]); + $sanitized_keys[] = $key; + } + elseif (is_array($input[$key])) { + $sanitized_keys = array_merge($sanitized_keys, _drupal_bootstrap_sanitize_input($input[$key], $whitelist)); + } + } + } + + return $sanitized_keys; +} diff --git a/modules/system/system.module b/modules/system/system.module index 93962ac5b..ed372b805 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.39'); +define('VERSION', '6.40'); /** * Core API compatibility. From b00a0d48b3cfb4e034057a57e15eb4608c94c058 Mon Sep 17 00:00:00 2001 From: pwolanin Date: Thu, 29 Mar 2018 17:26:40 -0400 Subject: [PATCH 3/5] Fixes bug from SA-CORE-2018-002 changes, updates CHANGELOG (#117) * Fixes bug from SA-CORE-2018-002 changes, updates CHANGELOG * Fix version in system.module, align version with d6lts/drupal --- CHANGELOG.txt | 16 ++++++++++++++++ includes/bootstrap.inc | 2 ++ modules/system/system.module | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index f0c0aabca..fbf4921b8 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,19 @@ +Drupal 6.43 LTS, 2018-03-29 +----------------------- +- Fixes bug from SA-CORE-2018-002 changes, update version. + +Drupal 6.41, Drupal 6.42 +----------------------- +Skipped to bring version number in line with +https://github.com/d6lts/drupal + +Drupal 6.40 Pressflow, 2018-03-28 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2018-002. + +Drupal 6.39 Pressflow, 2018-02-21 +----------------------- +- Fixed security issues (multiple vulnerabilities). See SA-CORE-2018-001. Drupal 6.38, 2016-02-24 - Final release --------------------------------------- diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index afed7408e..7cf8c5aab 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -2258,6 +2258,8 @@ function _drupal_bootstrap_sanitize_input(&$input, $whitelist = array()) { $sanitized_keys = array_merge($sanitized_keys, _drupal_bootstrap_sanitize_input($input[$key], $whitelist)); } } + // PHP 5.x will leave the array pointer at the end without this. + reset($input); } return $sanitized_keys; diff --git a/modules/system/system.module b/modules/system/system.module index ed372b805..f818b9d90 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.40'); +define('VERSION', '6.43'); /** * Core API compatibility. From afc7591aa3ad53ccfb94b99e3eed0be5c65f3167 Mon Sep 17 00:00:00 2001 From: Lisa Walley Date: Wed, 25 Apr 2018 16:24:28 -0400 Subject: [PATCH 4/5] Backport of fixes from SA-CORE-2018-004. (#120) * Backport of fixes from SA-CORE-2018-004. * Update version to 6.44 and add changelog --- CHANGELOG.txt | 4 ++ includes/bootstrap.inc | 91 ++++++++++++++++++++++++++++++++++++ modules/system/system.module | 2 +- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fbf4921b8..8b1d074ff 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,7 @@ +Drupal 6.44 LTS, 2018-04-25 +--------------------------------------- +- Fixed security issues (remote code execution), backport. See SA-CORE-2018-004. + Drupal 6.43 LTS, 2018-03-29 ----------------------- - Fixes bug from SA-CORE-2018-002 changes, update version. diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 7cf8c5aab..8231c0f99 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1558,6 +1558,10 @@ function _drupal_bootstrap($phase) { unset($_GET['destination']); unset($_REQUEST['destination']); } + // Ensure that the destination's query parameters are not dangerous. + if (isset($_GET['destination'])) { + _drupal_bootstrap_clean_destination(); + } // If there's still something in $_REQUEST['destination'] that didn't // come from $_GET, check it too. if (isset($_REQUEST['destination']) && (!isset($_GET['destination']) || $_REQUEST['destination'] != $_GET['destination']) && menu_path_is_external($_REQUEST['destination'])) { @@ -2264,3 +2268,90 @@ function _drupal_bootstrap_sanitize_input(&$input, $whitelist = array()) { return $sanitized_keys; } + +/** + * Removes the destination if it is dangerous. + * + * Note this can only be called after common.inc has been included. + * + * @return bool + * TRUE if the destination has been removed from $_GET, FALSE if not. + */ +function _drupal_bootstrap_clean_destination() { + $dangerous_keys = array(); + + $parts = _drupal_parse_url($_GET['destination']); + if (!empty($parts['query'])) { + $whitelist = variable_get('sanitize_input_whitelist', array()); + $log_sanitized_keys = variable_get('sanitize_input_logging', FALSE); + + $dangerous_keys = _drupal_bootstrap_sanitize_input($parts['query'], $whitelist); + if (!empty($dangerous_keys)) { + // The destination is removed rather than sanitized to mirror the + // handling of external destinations. + unset($_GET['destination']); + unset($_REQUEST['destination']); + if ($log_sanitized_keys) { + trigger_error(sprintf('Potentially unsafe destination removed from query string parameters (GET) because it contained the following keys: %s', implode(', ', $dangerous_keys))); + } + return TRUE; + } + } + return FALSE; +} + +/** + * Backport of drupal_parse_url() from Drupal 7. + */ +function _drupal_parse_url($url) { + $options = array( + 'path' => NULL, + 'query' => array(), + 'fragment' => '', + ); + + // External URLs: not using parse_url() here, so we do not have to rebuild + // the scheme, host, and path without having any use for it. + if (strpos($url, '://') !== FALSE) { + + // Split off everything before the query string into 'path'. + $parts = explode('?', $url); + $options['path'] = $parts[0]; + + // If there is a query string, transform it into keyed query parameters. + if (isset($parts[1])) { + $query_parts = explode('#', $parts[1]); + parse_str($query_parts[0], $options['query']); + + // Take over the fragment, if there is any. + if (isset($query_parts[1])) { + $options['fragment'] = $query_parts[1]; + } + } + } + else { + + // parse_url() does not support relative URLs, so make it absolute. E.g. the + // relative URL "foo/bar:1" isn't properly parsed. + $parts = parse_url('http://example.com/' . $url); + + // Strip the leading slash that was just added. + $options['path'] = substr($parts['path'], 1); + if (isset($parts['query'])) { + parse_str($parts['query'], $options['query']); + } + if (isset($parts['fragment'])) { + $options['fragment'] = $parts['fragment']; + } + } + + // The 'q' parameter contains the path of the current page if clean URLs are + // disabled. It overrides the 'path' of the URL when present, even if clean + // URLs are enabled, due to how Apache rewriting rules work. The path + // parameter must be a string. + if (isset($options['query']['q']) && is_string($options['query']['q'])) { + $options['path'] = $options['query']['q']; + unset($options['query']['q']); + } + return $options; +} diff --git a/modules/system/system.module b/modules/system/system.module index f818b9d90..ad64ff347 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.43'); +define('VERSION', '6.44'); /** * Core API compatibility. From 586e6c81f192c2157141c58e5b2a1899c534f3ab Mon Sep 17 00:00:00 2001 From: Greg Anderson Date: Mon, 26 Nov 2018 15:33:41 -0800 Subject: [PATCH 5/5] Update to Drupal 6.46. For more information, see https://github.com/pressflow/6/releases/tag/pressflow-6.46.126 --- .htaccess | 10 +++++ CHANGELOG.txt | 8 ++++ includes/bootstrap.inc | 34 ++++++++++++++- includes/common.inc | 4 ++ includes/file.inc | 3 ++ includes/form.inc | 8 +++- includes/install.inc | 5 +++ includes/install.mysql.inc | 1 + includes/install.mysqli.inc | 1 + includes/unicode.inc | 28 +++++++++++-- install.php | 2 +- modules/openid/openid.install | 62 ++++++++++++++++++++++++++++ modules/statistics/statistics.module | 2 +- modules/system/system.module | 2 +- modules/taxonomy/taxonomy.pages.inc | 1 + modules/upload/upload.module | 2 +- sites/default/default.settings.php | 1 - 17 files changed, 163 insertions(+), 11 deletions(-) diff --git a/.htaccess b/.htaccess index 4733ffa4a..ff6c2f017 100644 --- a/.htaccess +++ b/.htaccess @@ -58,6 +58,16 @@ DirectoryIndex index.php php_value mbstring.encoding_translation 0 +# PHP 7, Apache 1 and 2. + + php_value magic_quotes_gpc 0 + php_value register_globals 0 + php_value session.auto_start 0 + php_value mbstring.http_input pass + php_value mbstring.http_output pass + php_value mbstring.encoding_translation 0 + + # Requires mod_expires to be enabled. # Enable expirations. diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 8b1d074ff..caa00daab 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,11 @@ +Drupal 6.46 LTS, 2018-10-17 +--------------------------------------- +- Fixed security issues (open redirect), backport. See SA-CORE-2018-006. + +Drupal 6.45 LTS, 2018-10-04 +--------------------------------------- +- Initial support for PHP 7.2. + Drupal 6.44 LTS, 2018-04-25 --------------------------------------- - Fixed security issues (remote code execution), backport. See SA-CORE-2018-004. diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 6d695b173..9ad12f386 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -790,7 +790,7 @@ function drupal_page_is_cacheable($force = NULL) { $result = $forced_cache && !drupal_session_started() && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') - && !count(drupal_get_messages(NULL, FALSE)) + && !drupal_get_messages(NULL, FALSE) && !drupal_is_cli(); return $result; @@ -1921,7 +1921,12 @@ function drupal_session_regenerate() { if (drupal_session_started()) { $old_session_id = session_id(); + // On PHP7, sess_read($key) is being called with the new session key but not + // on PHP5. This failsafe stores the initial user object, which on PHP5 should + // be just the same, and restore it later on. + $account = $user; session_regenerate_id(); + $user = $account; } else { // Start the session when it doesn't exist yet. @@ -2404,3 +2409,30 @@ function _drupal_parse_url($url) { } return $options; } + +// Shim for ereg() family of functions for PHP 7+ where they don't exist. +if (!function_exists('ereg')) { + function ereg($pattern, $subject, &$matches = array()) { + return preg_match('/' . $pattern . '/', $subject, $matches); + } + + function eregi($pattern, $subject, &$matches = array()) { + return preg_match('/' . $pattern . '/i', $subject, $matches); + } + + function ereg_replace($pattern, $replacement, $string) { + return preg_replace('/' . $pattern . '/', $replacement, $string); + } + + function eregi_replace($pattern, $replacement, $string) { + return preg_replace('/' . $pattern . '/i', $replacement, $string); + } + + function split($pattern, $subject, $limit = -1) { + return preg_split('/' . $pattern . '/', $subject, $limit); + } + + function spliti($pattern, $subject, $limit = -1) { + return preg_split('/' . $pattern . '/i', $subject, $limit); + } +} diff --git a/includes/common.inc b/includes/common.inc index b3e0949fe..d37016360 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -1540,6 +1540,10 @@ function url($path = NULL, $options = array()) { } elseif (!empty($path) && !$options['alias']) { $path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : ''); + // Strip leading slashes from internal paths to prevent them becoming external + // URLs without protocol. /example.com should not be turned into + // //example.com. + $path = ltrim($path, '/'); } if (function_exists('custom_url_rewrite_outbound')) { diff --git a/includes/file.inc b/includes/file.inc index 96a140d91..bfa358389 100644 --- a/includes/file.inc +++ b/includes/file.inc @@ -236,6 +236,9 @@ SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 php_flag engine off + + php_flag engine off + # PHP 4, Apache 1. php_flag engine off diff --git a/includes/form.inc b/includes/form.inc index b9e066d6b..be58955e2 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -737,7 +737,7 @@ function _form_validate($elements, &$form_state, $form_id = NULL) { // A simple call to empty() will not cut it here as some fields, like // checkboxes, can return a valid value of '0'. Instead, check the // length if it's a string, and the item count if it's an array. - if ($elements['#required'] && (!count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) { + if ($elements['#required'] && (is_array($elements['#value']) && !count($elements['#value']) || (is_string($elements['#value']) && strlen(trim($elements['#value'])) == 0))) { form_error($elements, $t('!name field is required.', array('!name' => $elements['#title']))); } @@ -1426,6 +1426,12 @@ function form_set_value($form_item, $value, &$form_state) { function _form_set_value(&$form_values, $form_item, $parents, $value) { $parent = array_shift($parents); if (empty($parents)) { + // This makes PHP 7 have the same behavior as PHP 5 when the value is an + // empty string, rather than an array. This is depended on surprisingly + // often in Drupal 6 contrib. + if ($form_values === '') { + $form_values = array(); + } $form_values[$parent] = $value; } else { diff --git a/includes/install.inc b/includes/install.inc index 171ba736c..cb149ceec 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -236,6 +236,11 @@ function drupal_rewrite_settings($settings = array(), $prefix = '') { if ($fp && fwrite($fp, $buffer) === FALSE) { drupal_set_message(st('Failed to modify %settings, please verify the file permissions.', array('%settings' => $settings_file)), 'error'); } + fclose($fp); + // Invalidate the cache of the settings file so next request read the newly modified one + if (function_exists('opcache_invalidate')) { + opcache_invalidate($settings_file, TRUE); + } } else { drupal_set_message(st('Failed to open %settings, please verify the file permissions.', array('%settings' => $default_settings)), 'error'); diff --git a/includes/install.mysql.inc b/includes/install.mysql.inc index 73aa365b0..d08e72ba6 100644 --- a/includes/install.mysql.inc +++ b/includes/install.mysql.inc @@ -31,6 +31,7 @@ function drupal_test_mysql($url, &$success) { $url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : ''; $url['host'] = urldecode($url['host']); $url['path'] = urldecode($url['path']); + $url['port'] = isset($url['port']) ? $url['port'] : NULL; // Allow for non-standard MySQL port. if (isset($url['port'])) { diff --git a/includes/install.mysqli.inc b/includes/install.mysqli.inc index 008080416..ad49ab28f 100644 --- a/includes/install.mysqli.inc +++ b/includes/install.mysqli.inc @@ -31,6 +31,7 @@ function drupal_test_mysqli($url, &$success) { $url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : ''; $url['host'] = urldecode($url['host']); $url['path'] = urldecode($url['path']); + $url['port'] = isset($url['port']) ? $url['port'] : NULL; $connection = mysqli_init(); @mysqli_real_connect($connection, $url['host'], $url['user'], $url['pass'], substr($url['path'], 1), $url['port'], NULL, MYSQLI_CLIENT_FOUND_ROWS); diff --git a/includes/unicode.inc b/includes/unicode.inc index b1ce75760..bf0ae52c9 100644 --- a/includes/unicode.inc +++ b/includes/unicode.inc @@ -317,6 +317,25 @@ function _mime_header_decode($matches) { return $data; } +/** + * Simple class to use instead of closure in decode_entities(). + * + * @see decode_entities() + */ +class DrupalDecodeEntitiesCallback { + public $html_entities; + public $exclude; + + public function __construct($html_entities, $exclude) { + $this->html_entities = $html_entities; + $this->exclude = $exclude; + } + + public function callback($matches) { + return _decode_entities($matches[1], $matches[2], $matches[0], $this->html_entities, $this->exclude); + } +} + /** * Decodes all HTML entities (including numerical ones) to regular UTF-8 bytes. * @@ -342,11 +361,12 @@ function decode_entities($text, $exclude = array()) { // Flip the exclude list so that we can do quick lookups later. $exclude = array_flip($exclude); + // Use object instead of closure to retain PHP 5.2 compatibility. + $callback = new DrupalDecodeEntitiesCallback($html_entities, $exclude); + // Use a regexp to select all entities in one pass, to avoid decoding - // double-escaped entities twice. The PREG_REPLACE_EVAL modifier 'e' is - // being used to allow for a callback (see - // http://php.net/manual/en/reference.pcre.pattern.modifiers). - return preg_replace('/&(#x?)?([A-Za-z0-9]+);/e', '_decode_entities("$1", "$2", "$0", $html_entities, $exclude)', $text); + // double-escaped entities twice. + return preg_replace_callback('/&(#x?)?([A-Za-z0-9]+);/', array($callback, 'callback'), $text); } /** diff --git a/install.php b/install.php index 8aea0eacc..9a7821658 100644 --- a/install.php +++ b/install.php @@ -655,7 +655,7 @@ function install_tasks($profile, $task) { global $base_url, $install_locale; // Bootstrap newly installed Drupal, while preserving existing messages. - $messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : ''; + $messages = isset($_SESSION['messages']) ? $_SESSION['messages'] : array(); drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); $_SESSION['messages'] = $messages; diff --git a/modules/openid/openid.install b/modules/openid/openid.install index 8f1013621..0b0c95d29 100644 --- a/modules/openid/openid.install +++ b/modules/openid/openid.install @@ -142,6 +142,68 @@ function openid_update_6000() { return $ret; } +/** + * Bind associations to their providers. + */ +function openid_update_6001() { + $ret = array(); + + db_drop_table($ret, 'openid_association'); + + $schema['openid_association'] = array( + 'description' => 'Stores temporary shared key association information for OpenID authentication.', + 'fields' => array( + 'idp_endpoint_uri' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Primary Key: URI of the OpenID Provider endpoint.', + ), + 'assoc_handle' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'description' => 'Used to refer to this association in subsequent messages.', + ), + 'assoc_type' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'The signature algorithm used: one of HMAC-SHA1 or HMAC-SHA256.', + ), + 'session_type' => array( + 'type' => 'varchar', + 'length' => 32, + 'description' => 'Valid association session types: "no-encryption", "DH-SHA1", and "DH-SHA256".', + ), + 'mac_key' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'The MAC key (shared secret) for this association.', + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'UNIX timestamp for when the association was created.', + ), + 'expires_in' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The lifetime, in seconds, of this association.', + ), + ), + 'primary key' => array('idp_endpoint_uri'), + 'unique keys' => array( + 'assoc_handle' => array('assoc_handle'), + ), + ); + + db_create_table($ret, 'openid_association', $schema['openid_association']); + + return $ret; +} + /** * @} End of "addtogroup updates-6.x-extra". * The next series of updates should start at 7000. diff --git a/modules/statistics/statistics.module b/modules/statistics/statistics.module index ba964ef15..b15380e2c 100644 --- a/modules/statistics/statistics.module +++ b/modules/statistics/statistics.module @@ -176,7 +176,7 @@ function statistics_user($op, &$edit, &$user) { * Implementation of hook_cron(). */ function statistics_cron() { - $statistics_timestamp = variable_get('statistics_day_timestamp', ''); + $statistics_timestamp = variable_get('statistics_day_timestamp', 0); if ((time() - $statistics_timestamp) >= 86400) { // Reset day counts. diff --git a/modules/system/system.module b/modules/system/system.module index e7326e0fe..7826a21f1 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '6.44'); +define('VERSION', '6.46'); /** * Core API compatibility. diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index 99757b169..56460c308 100644 --- a/modules/taxonomy/taxonomy.pages.inc +++ b/modules/taxonomy/taxonomy.pages.inc @@ -29,6 +29,7 @@ function taxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') { switch ($op) { case 'page': + $current = new stdClass(); // Build breadcrumb based on first hierarchy of first term: $current->tid = $tids[0]; $breadcrumb = array(); diff --git a/modules/upload/upload.module b/modules/upload/upload.module index ee5b127dd..cb48c9648 100644 --- a/modules/upload/upload.module +++ b/modules/upload/upload.module @@ -278,7 +278,7 @@ function upload_nodeapi(&$node, $op, $teaser = NULL) { case 'load': $output = ''; if (variable_get("upload_$node->type", 1) == 1) { - $output['files'] = upload_load($node); + $output = array('files' => upload_load($node)); return $output; } break; diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 497bf1112..fc905a8fa 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -160,7 +160,6 @@ ini_set('session.cache_limiter', 'none'); ini_set('session.cookie_lifetime', 2000000); ini_set('session.gc_maxlifetime', 200000); -ini_set('session.save_handler', 'user'); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); ini_set('session.use_trans_sid', 0);