diff -Naur drupal-7.13/.editorconfig drupal-7.66/.editorconfig
--- drupal-7.13/.editorconfig 1970-01-01 01:00:00.000000000 +0100
+++ drupal-7.66/.editorconfig 2019-04-17 22:20:46.000000000 +0200
@@ -0,0 +1,14 @@
+# Drupal editor configuration normalization
+# @see http://editorconfig.org/
+
+# This is the top-most .editorconfig file; do not search in parent directories.
+root = true
+
+# All files.
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff -Naur drupal-7.13/.htaccess drupal-7.66/.htaccess
--- drupal-7.13/.htaccess 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/.htaccess 2019-04-17 22:20:46.000000000 +0200
@@ -3,8 +3,13 @@
#
# Protect files and directories from prying eyes.
-
, and
+ similar tags in a larger font size, so it is easier to read.
+- Fixed a bug in the Search module that caused exceptions to be thrown during
+ searches if the server was not configured to represent decimal points as a
+ period.
+- Fixed a regression in the Image module that made image_style_url() not work
+ when a relative path (rather than a complete file URI) was passed to it.
+- Added an optional feature to the Statistics module to allow node views to be
+ tracked by Ajax requests rather than during the server-side generation of the
+ page. This allows the node counter to work on sites that use external page
+ caches (string change and new administrative option:
+ https://drupal.org/node/2164069).
+- Added a link to the drupal.org documentation page for cron to the Cron
+ settings page (string change).
+- Added a 'drupal_anonymous_user_object' variable to allow the anonymous user
+ object returned by drupal_anonymous_user() to be overridden with a classed
+ object (API addition).
+- Changed the database API to allow inserts based on a SELECT * query to work
+ correctly.
+- Changed the database schema of the {file_managed} table to allow Drupal to
+ manage files larger than 4 GB.
+- Changed the File module's hook_field_load() implementation to prevent file
+ entity properties which have the same name as file or image field properties
+ from overwriting the field properties (minor API change).
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.24, 2013-11-20
+-----------------------
+- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
+
+Drupal 7.23, 2013-08-07
+-----------------------
+- Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module
+ from Drupal 6 to Drupal 7.
+- Fixed the default ordering of CSS files for sites using right-to-left
+ languages, to consistently place the right-to-left override file immediately
+ after the CSS it is overriding (API change: https://drupal.org/node/2058463).
+- Added a drupal_check_memory_limit() API function to allow the memory limit to
+ be checked consistently (API addition).
+- Changed the default web.config file for IIS servers to allow favicon.ico
+ files which are present in the filesystem to be accessed.
+- Fixed inconsistent support for the 'tel' protocol in Drupal's URL filtering
+ functions.
+- Performance improvement: Allowed all hooks to be included in the
+ module_implements() cache, even those that are only invoked on HTTP POST
+ requests.
+- Made the database system replace truncate queries with delete queries when
+ inside a transaction, to fix issues with PostgreSQL and other databases.
+- Fixed a bug which caused nested contextual links to display improperly.
+- Fixed a bug which prevented cached image derivatives from being flushed for
+ private files and other non-default file schemes.
+- Fixed drupal_render() to always return an empty string when there is no
+ output, rather than sometimes returning NULL (minor API change).
+- Added protection to cache_clear_all() to ensure that non-cache tables cannot
+ be truncated (API addition: a new isValidBin() method has been added to the
+ default database cache implementation).
+- Changed the default .htaccess file to support HTTP authorization in CGI
+ environments.
+- Changed the password reset form to pre-fill the username when requested via a
+ URL query parameter, and used this in the error message that appears after a
+ failed login attempt (minor data structure and behavior change).
+- Fixed broken support for foreign keys in the field API.
+- Fixed "No active batch" error when a user cancels their own account.
+- Added a description to the "access content overview" permission on the
+ permissions page (string change).
+- Added a drupal_array_diff_assoc_recursive() function to allow associative
+ arrays to be compared recursively (API addition).
+- Added human-readable labels to image styles, in addition to the existing
+ machine-readable name (API change: https://drupal.org/node/2058503).
+- Moved the drupal_get_hash_salt() function to bootstrap.inc and used it in
+ additional places in the code, for added security in the case where there is
+ no hash salt in settings.php.
+- Fixed a regression in Drupal 7.22 that caused internal server errors for
+ sites running on very old Apache 1.x web servers.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.22, 2013-04-03
+-----------------------
+- Allowed the drupal_http_request() function to be overridden so that
+ additional HTTP request capabilities can be added by contributed modules.
+- Changed the Simpletest module to allow PSR-0 test classes to be used in
+ Drupal 7.
+- Removed an unnecessary "Content-Disposition" header from private file
+ downloads; it prevented many private files from being viewed inline in a web
+ browser.
+- Changed various field API functions to allow them to optionally act on a
+ single field within an entity (API addition: http://drupal.org/node/1825844).
+- Fixed a bug which prevented Drupal's file transfer functionality from working
+ on some PHP 5.4 systems.
+- Fixed incorrect log message when theme() is called for a theme hook that does
+ not exist (minor string change).
+- Fixed Drupal's token-replacement system to allow spaces in the token value.
+- Changed the default behavior after a user creates a node they do not have
+ access to view. The user will now be redirected to the front page rather than
+ an access denied page.
+- Fixed a bug which prevented empty HTTP headers (such as "0") from being set.
+ (Minor behavior change: Callers of drupal_add_http_header() must now set
+ FALSE explicitly to prevent a header from being sent at all; this was already
+ indicated in the function's documentation.)
+- Fixed OpenID errors when more than one module implements hook_openid(). The
+ behavior is now changed so that if more than one module tries to set the same
+ parameter, the last module's change takes effect.
+- Fixed a serious documentation bug: The $name variable in the
+ taxonomy-term.tpl.php theme template was incorrectly documented as being
+ sanitized when in fact it is not.
+- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
+ duplicate permission names in the User module's database tables.
+- Added an empty "datatype" attribute to taxonomy term and username links to
+ make the RDFa markup upward compatible with RDFa 1.1 (minor markup addition).
+- Fixed a bug which caused the denial-of-service protection added in Drupal
+ 7.20 to break certain valid image URLs that had an extra slash in them.
+- Fixed a bug with update queries in the SQLite database driver that prevented
+ Drupal from being installed with SQLite on PHP 5.4.
+- Fixed enforced dependencies errors updating to recent versions of Drupal 7 on
+ certain non-MySQL databases.
+- Refactored the Field module's caching behavior to obtain large improvements
+ in memory usage for sites with many fields and instances (API addition:
+ http://drupal.org/node/1915646).
+- Fixed entity argument not being passed to implementations of
+ hook_file_download_access_alter(). The fix adds an additional context
+ parameter that can be passed when calling drupal_alter() for any hook (API
+ change: http://drupal.org/node/1882722).
+- Fixed broken support for translatable comment fields (API change:
+ http://drupal.org/node/1874724).
+- Added an assertThemeOutput() method to Simpletest to allow tests to check
+ that themed output matches an expected HTML string (API addition).
+- Added a link to "Install another module" after a module has been successfully
+ downloaded via the Update Manager (UI change).
+- Added an optional "exclusive" flag to installation profile .info files which
+ allows Drupal distributions to force a profile to be selected during
+ installation (API addition: http://drupal.org/node/1961012).
+- Fixed a bug which caused the database API to not properly close database
+ connections.
+- Added a link to the URL for running cron from outside the site to the Cron
+ settings page (UI change).
+- Fixed a bug which prevented image styles from being reverted on PHP 5.4.
+- Made the default .htaccess rules protocol sensitive to improve security for
+ sites which use HTTPS and redirect between "www" and non-"www" versions of
+ the page.
+- Numerous small bug fixes.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.21, 2013-03-06
+-----------------------
+- Allowed sites using the 'image_allow_insecure_derivatives' variable to still
+ have partial protection from the security issues fixed in Drupal 7.20.
+
+Drupal 7.20, 2013-02-20
+-----------------------
+- Fixed security issues (denial of service). See SA-CORE-2013-002.
+
+Drupal 7.19, 2013-01-16
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2013-001.
+
+Drupal 7.18, 2012-12-19
+-----------------------
+- Fixed security issues (multiple vulnerabilities). See SA-CORE-2012-004.
+
+Drupal 7.17, 2012-11-07
+-----------------------
+- Changed the default value of the '404_fast_html' variable to have a DOCTYPE
+ declaration.
+- Made it possible to use associative arrays for the 'items' variable in
+ theme_item_list().
+- Fixed a bug which prevented required form elements without a title from being
+ given an "error" class when the form fails validation.
+- Prevented duplicate HTML IDs from appearing when two forms are displayed on
+ the same page and one of them is submitted with invalid data (minor markup
+ change).
+- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
+ stale data in the Upload module's database tables.
+- Fixed a bug in the States API which prevented certain types of form elements
+ from being disabled when requested.
+- Allowed aggregator feed items with author names longer than 255 characters to
+ have a truncated version saved to the database (rather than causing a fatal
+ error).
+- Allowed aggregator feed items to have URLs longer than 255 characters
+ (schema change which results in several columns in the Aggregator module's
+ database tables changing from VARCHAR to TEXT fields).
+- Added hook_taxonomy_term_view() and standardized the process for rendering
+ taxonomy terms to invoke hook_entity_view() and otherwise make it consistent
+ with other entities (API change: http://drupal.org/node/1808870).
+- Added hook_entity_view_mode_alter() to allow modules to change entity view
+ modes on display (API addition: http://drupal.org/node/1833086).
+- Fixed a bug which made database queries running a "LIKE" query on blob fields
+ fail on PostgreSQL databases. This caused errors during the Drupal 6 to
+ Drupal 7 upgrade.
+- Changed the hook_menu() entry for Drupal's rss.xml page to prevent extra path
+ components from being accidentally passed to the page callback function (data
+ structure change).
+- Removed a non-standard "name" attribute from Drupal's default Content-Type
+ header for file downloads.
+- Fixed the theme settings form to properly clean up submitted values in
+ $form_state['values'] when the form is submitted (data structure change).
+- Fixed an inconsistency by removing the colon from the end of the label on
+ multi-valued form fields (minor string change).
+- Added support for 'weight' in hook_field_widget_info() to allow modules to
+ control the order in which widgets are displayed in the Field UI.
+- Updated various tables in the OpenID and Book modules to use the default
+ "empty table" text pattern (string change).
+- Added proxy server support to drupal_http_request().
+- Added "lang" attributes to language links, to better support screen readers.
+- Fixed double occurrence of a "ul" HTML tag on secondary local tasks in the
+ Seven theme (markup change).
+- Fixed bugs which caused taxonomy vocabulary and shortcut set titles to be
+ double-escaped. The fix replaces the taxonomy vocabulary overview page and
+ "Edit shortcuts" menu items' title callback entries in hook_menu() with new
+ functions that do not escape HTML characters (data structure change).
+- Modified the Update manager module to allow drupal.org to collect usage
+ statistics for individual modules and themes, rather than only for entire
+ projects.
+- Modified the node listing database query on Drupal's default front page to
+ add table aliases for better query altering (this is a data structure change
+ affecting code which implements hook_query_alter() on this query).
+- Improved the translatability of the "Field type(s) in use" message on the
+ modules page (admin-facing string change).
+- Fixed a regression which caused a "call to undefined function
+ drupal_find_base_themes()" fatal error under rare circumstances.
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.16, 2012-10-17
+-----------------------
+- Fixed security issues (Arbitrary PHP code execution and information
+ disclosure). See SA-CORE-2012-003.
+
+Drupal 7.15, 2012-08-01
+-----------------------
+- Introduced a 'user_password_reset_timeout' variable to allow the 24-hour
+ expiration for user password reset links to be adjusted (API addition).
+- Fixed database errors due to ambiguous column names that occurred when
+ EntityFieldQuery was used in certain situations.
+- Changed the drupal_array_get_nested_value() function to return a reference
+ (API addition).
+- Changed the System module's hook_block_info() implementation to assign the
+ "Main page content" and "System help" blocks to appropriate regions by
+ default and prevent error messages on the block administration page (data
+ structure change).
+- Fixed regression: Non-node entities couldn't be accessed with
+ EntityFieldQuery.
+- Fixed regression: Optional radio buttons with an empty, non-NULL default
+ value led to an illegal choice error when none were selected.
+- Reorganized the testing framework to split setUp() into specific sub-methods
+ and fix several regressions in the process.
+- Fixed bug which made it impossible to search for strings that have not been
+ translated into a particular language.
+- Renamed the "Field" column on the Manage Fields screen to "Field type", since
+ the former was confusing and inaccurate (UI change).
+- Performance improvement: Removed needless call to system_rebuild_module_data()
+ in field_sync_field_status(), greatly speeding up bulk module enable/disable.
+- Fixed bug which prevented notifications from being sent when core, module, and
+ theme updates are available.
+- Fixed bug which prevented sub-themes from inheriting the default values of
+ theme settings defined by the base theme.
+- Fixed bug which prevented the jQuery UI Datepicker from being localized.
+- Made Ajax alert dialogs respect error reporting settings.
+- Fixed bug which prevented image styles from being deleted on PHP 5.4.
+- Fixed bug: Language detection by domain only worked on port 80.
+- Fixed regression: The first plural index on a page was not calculated
+ correctly.
+- Introduced generic entity language support. Entities may now declare their
+ language property in hook_entity_info(), and modules working with entities
+ may access the language using entity_language() (API change:
+ http://drupal.org/node/1626346).
+- Added EntityFieldQuery support for taxonomy bundles.
+- Fixed issue where field form structure was incomplete if field_access()
+ returned FALSE. Instead of being incomplete, the form structure now has
+ #access set to FALSE and field form validation is skipped (data structure
+ change: http://drupal.org/node/1663020).
+- Fixed data loss issue due to field_has_data() returning inconsistent results.
+ The fix adds an optional DANGEROUS_ACCESS_CHECK_OPT_OUT tag to entity field
+ queries which field storage engines can respond to (API addition:
+ http://drupal.org/node/1597378).
+- Fixed notice: Undefined index: default_image in image_field_prepare_view()
+- Numerous API documentation improvements.
+- Additional automated test coverage.
+
+Drupal 7.14, 2012-05-02
+-----------------------
+- Fixed "integrity constraint" fatal errors when rebuilding registry.
+- Fixed custom logo and favicon functionality referencing incorrect paths.
+- Fixed DB Case Sensitivity: Allow BINARY attribute in MySQL.
+- Split field_bundle_settings out per bundle.
+- Improve UX for machine names for fields (UI change).
+- Fixed User pictures are not removed properly.
+- Fixed HTTPS sessions not working in all cases.
+- Fixed Regression: Required radios throw illegal choice error when none
+ selected.
+- Fixed allow autocompletion requests to include slashes.
+- Eliminate $user->cache and {session}.cache in favor of
+ $_SESSION['cache_expiration'][$bin] (Performance).
+- Fixed focus jumps to tab when pressing enter on a form element within tab.
+- Fixed race condition in locale() - duplicates in {locales_source}.
+- Fixed Missing "Default image" per field instance.
+- Quit clobbering people's work when they click the filter tips link
+- Form API #states: Fix conditionals to allow OR and XOR constructions.
+- Fixed Focus jumps to tab when pressing enter on a form element within tab.
+ (Accessibility)
+- Improved performance of node_access queries.
+- Fixed Fieldsets inside vertical tabs have no title and can't be collapsed.
+- Reduce size of cache_menu table (Performance).
+- Fixed unnecessary aggregation of CSS/JS (Performance).
+- Fixed taxonomy_autocomplete() produces SQL error for nonexistent field.
+- Fixed HTML filter is not run first by default, despite default weight.
+- Fixed Overlay does not work with prefixed URL paths.
+- Better debug info for field errors (string change).
+- Fixed Data corruption in comment IDs (results in broken threading on
+ PostgreSQL).
+- Fixed machine name not editable if every character is replaced.
+- Fixed user picture not appearing in comment preview (Markup change).
+- Added optional vid argument for taxonomy_get_term_by_name().
+- Fixed Invalid Unicode code range in PREG_CLASS_UNICODE_WORD_BOUNDARY fails
+ with PCRE 8.30.
+- Fixed {trigger_assignments()}.hook has only 32 characters, is too short.
+- Numerous fixes to run-tests.sh.
+- Fixed Tests in profiles/[name]/modules cannot be run and cannot use a
+ different profile for running tests.
+- Numerous JavaScript performance fixes.
+- Numerous documentation fixes.
+- Fixed All pager links have an 'active' CSS class.
+- Numerous upgrade path fixes; notably:
+ - system_update_7061() fails on inserting files with same name but different
+ case.
+ - system_update_7061() converts filepaths too aggressively.
+ - Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25.
+
+Drupal 7.13, 2012-05-02
+-----------------------
- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002.
Drupal 7.12, 2012-02-01
-----------------------
+-----------------------
- Fixed bug preventing custom menus from receiving an active trail.
- Fixed hook_field_delete() no longer invoked during field_purge_data().
- Fixed bug causing entity info cache to not be cleared with the rest of caches.
@@ -38,11 +942,11 @@
cache.
Drupal 7.11, 2012-02-01
-----------------------
+-----------------------
- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
Drupal 7.10, 2011-12-05
-----------------------
+-----------------------
- Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
- Reduce memory usage of theme registry (performance).
- Fixed PECL upload progress bar for FileField
@@ -210,8 +1114,8 @@
order can now be customized using the Views module.
* Removed the 'related terms' feature from taxonomy module since this can
now be achieved with Field API.
- * Added additional features to the default install profile, and implemented
- a "slimmed down" install profile designed for developers.
+ * Added additional features to the default installation profile, and
+ implemented a "slimmed down" profile designed for developers.
* Added a built-in, automated cron run feature, which is triggered by site
visitors.
* Added an administrator role which is assigned all permissions for
@@ -395,7 +1299,7 @@
requests.
Drupal 6.23-dev, xxxx-xx-xx (development release)
------------------------
+---------------------------
Drupal 6.22, 2011-05-25
-----------------------
@@ -405,25 +1309,25 @@
- Fixed a variety of other bugs.
Drupal 6.21, 2011-05-25
-----------------------
+-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
Drupal 6.20, 2010-12-15
-----------------------
+-----------------------
- Fixed a variety of small bugs, improved code documentation.
Drupal 6.19, 2010-08-11
-----------------------
+-----------------------
- Fixed a variety of small bugs, improved code documentation.
Drupal 6.18, 2010-08-11
-----------------------
+-----------------------
- Fixed security issues (OpenID authentication bypass, File download access
bypass, Comment unpublishing bypass, Actions cross site scripting),
see SA-CORE-2010-002.
Drupal 6.17, 2010-06-02
-----------------------
+-----------------------
- Improved PostgreSQL compatibility
- Better PHP 5.3 and PHP 4 compatibility
- Better browser compatibility of CSS and JS aggregation
@@ -432,24 +1336,24 @@
- Fixed a variety of other bugs.
Drupal 6.16, 2010-03-03
-----------------------
+-----------------------
- Fixed security issues (Installation cross site scripting, Open redirection,
Locale module cross site scripting, Blocked user session regeneration),
see SA-CORE-2010-001.
- Better support for updated jQuery versions.
- Reduced resource usage of update.module.
-- Fixed several issues relating to support of install profiles and
+- Fixed several issues relating to support of installation profiles and
distributions.
- Added a locking framework to avoid data corruption on long operations.
- Fixed a variety of other bugs.
Drupal 6.15, 2009-12-16
-----------------------
+-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
- Fixed a variety of other bugs.
Drupal 6.14, 2009-09-16
-----------------------
+-----------------------
- Fixed security issues (OpenID association cross site request forgeries,
OpenID impersonation and File upload), see SA-CORE-2009-008.
- Changed the system modules page to not run all cache rebuilds; use the
@@ -458,18 +1362,18 @@
- Fixed a variety of small bugs.
Drupal 6.13, 2009-07-01
-----------------------
+-----------------------
- Fixed security issues (Cross site scripting, Input format access bypass and
Password leakage in URL), see SA-CORE-2009-007.
- Fixed a variety of small bugs.
Drupal 6.12, 2009-05-13
-----------------------
+-----------------------
- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
- Fixed a variety of small bugs.
Drupal 6.11, 2009-04-29
-----------------------
+-----------------------
- Fixed security issues (Cross site scripting and limited information
disclosure), see SA-CORE-2009-005
- Fixed performance issues with the menu router cache, the update
@@ -477,7 +1381,7 @@
- Fixed a variety of small bugs.
Drupal 6.10, 2009-02-25
-----------------------
+-----------------------
- Fixed a security issue, (Local file inclusion on Windows),
see SA-CORE-2009-003
- Fixed node_feed() so custom fields can show up in RSS feeds.
@@ -556,7 +1460,7 @@
* Expands the severity levels from 3 (Error, Warning, Notice) to the 8
levels defined in RFC 3164.
* The watchdog module is now called dblog, and is optional, but enabled by
- default in the default install profile.
+ default in the default installation profile.
* Extended the database log module so log messages can be filtered.
* Added syslog module: useful for monitoring large Drupal installations.
- Added optional e-mail notifications when users are approved, blocked, or
@@ -611,7 +1515,7 @@
* Themed the installer with the Garland theme.
* Added form to provide initial site information during installation.
* Added ability to provide extra installation steps programmatically.
- * Made it possible to import interface translations at install time.
+ * Made it possible to import interface translations during installation.
- Added the HTML corrector filter:
* Fixes faulty and chopped off HTML in postings.
* Tags are now automatically closed at the end of the teaser.
@@ -790,7 +1694,7 @@
- Added web-based installer which can:
* Check installation and run-time requirements
* Automatically generate the database configuration file
- * Install pre-made 'install profiles' or distributions
+ * Install pre-made installation profiles or distributions
* Import the database structure with automatic table prefixing
* Be localized
- Added new default Garland theme
@@ -850,7 +1754,7 @@
- Removed the archive module.
- Upgrade system:
* Created space for update branches.
-- Forms API:
+- Form API:
* Made it possible to programmatically submit forms.
* Improved api for multistep forms.
- Theme system:
@@ -873,7 +1777,7 @@
- fixed a security issue (SQL injection), see SA-2007-031
Drupal 4.7.8, 2007-10-17
-----------------------
+------------------------
- fixed a security issue (HTTP response splitting), see SA-2007-024
- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
- fixed a security issue (API handling of unpublished comment), see SA-2007-030
@@ -986,7 +1890,7 @@
- Fixed security issue (DoS), see SA-2007-002
Drupal 4.6.10, 2006-10-18
-------------------------
+-------------------------
- Fixed security issue (XSS), see SA-2006-024
- Fixed security issue (CSRF), see SA-2006-025
- Fixed security issue (Form action attribute injection), see SA-2006-026
diff -Naur drupal-7.13/COPYRIGHT.txt drupal-7.66/COPYRIGHT.txt
--- drupal-7.13/COPYRIGHT.txt 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/COPYRIGHT.txt 2019-04-17 22:20:46.000000000 +0200
@@ -1,5 +1,4 @@
-
-All Drupal code is Copyright 2001 - 2010 by the original authors.
+All Drupal code is Copyright 2001 - 2013 by the original authors.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,5 +20,25 @@
according to the terms of the GNU General Public License or a compatible
license, including:
- jQuery - Copyright (c) 2008 - 2009 John Resig
+Javascript
+
+ Farbtastic - Copyright (c) 2010 Matt Farina
+
+ jQuery - Copyright (c) 2010 John Resig
+
+ jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman
+
+ jQuery Cookie - Copyright (c) 2006 Klaus Hartl
+
+ jQuery Form - Copyright (c) 2010 Mike Alsup
+
+ jQuery Once - Copyright (c) 2009 Konstantin K�fer
+
+ jQuery UI - Copyright (c) 2010 by the original authors
+ (http://jqueryui.com/about)
+
+ Sizzle.js - Copyright (c) 2010 The Dojo Foundation (http://sizzlejs.com/)
+
+PHP
+ ArchiveTar - Copyright (c) 1997 - 2008 Vincent Blavet
diff -Naur drupal-7.13/INSTALL.mysql.txt drupal-7.66/INSTALL.mysql.txt
--- drupal-7.13/INSTALL.mysql.txt 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/INSTALL.mysql.txt 2019-04-17 22:20:46.000000000 +0200
@@ -20,18 +20,21 @@
Again, you will be asked for the 'username' database password. At the MySQL
prompt, enter the following command:
- GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
- ON databasename.*
+ GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
+ CREATE TEMPORARY TABLES ON databasename.*
TO 'username'@'localhost' IDENTIFIED BY 'password';
-where
+where:
'databasename' is the name of your database
- 'username@localhost' is the username of your MySQL account
+ 'username' is the username of your MySQL account
+ 'localhost' is the web server host where Drupal is installed
'password' is the password required for that username
-Note: Unless your database user has all of the privileges listed above, you will
-not be able to run Drupal.
+Note: Unless the database user/host combination for your Drupal installation
+has all of the privileges listed above (except possibly CREATE TEMPORARY TABLES,
+which is currently only used by Drupal core automated tests and some
+contributed modules), you will not be able to install or run Drupal.
If successful, MySQL will reply with:
diff -Naur drupal-7.13/INSTALL.txt drupal-7.66/INSTALL.txt
--- drupal-7.13/INSTALL.txt 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/INSTALL.txt 2019-04-17 22:20:46.000000000 +0200
@@ -20,8 +20,10 @@
- MySQL 5.0.15 (or greater) (http://www.mysql.com/).
- MariaDB 5.1.44 (or greater) (http://mariadb.org/). MariaDB is a fully
compatible drop-in replacement for MySQL.
+ - Percona Server 5.1.70 (or greater) (http://www.percona.com/). Percona
+ Server is a backwards-compatible replacement for MySQL.
- PostgreSQL 8.3 (or greater) (http://www.postgresql.org/).
- - SQLite 3.4.2 (or greater) (http://www.sqlite.org/).
+ - SQLite 3.3.7 (or greater) (http://www.sqlite.org/).
For more detailed information about Drupal requirements, including a list of
PHP extensions and configurations that are required, see "System requirements"
@@ -89,8 +91,8 @@
- Download a translation file for the correct Drupal version and language
from the translation server: http://localize.drupal.org/translate/downloads
- - Place the file into your installation profile's translations
- directory. For instance, if you are using the Standard install profile,
+ - Place the file into your installation profile's translations directory.
+ For instance, if you are using the Standard installation profile,
move the .po file into the directory:
profiles/standard/translations/
diff -Naur drupal-7.13/MAINTAINERS.txt drupal-7.66/MAINTAINERS.txt
--- drupal-7.13/MAINTAINERS.txt 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/MAINTAINERS.txt 2019-04-17 22:20:46.000000000 +0200
@@ -1,7 +1,8 @@
Drupal core is built and maintained by the Drupal project community. Everyone is
encouraged to submit issues and changes (patches) to improve Drupal, and to
-contribute in other ways -- see http://drupal.org/contribute to find out how.
+contribute in other ways -- see https://www.drupal.org/contribute to find out
+how.
Branch maintainers
------------------
@@ -9,141 +10,154 @@
The Drupal Core branch maintainers oversee the development of Drupal as a whole.
The branch maintainers for Drupal 7 are:
-- Dries Buytaert 'dries'
-- Angela Byron 'webchick'
+- Dries Buytaert 'dries' https://www.drupal.org/u/dries
+- Angela Byron 'webchick' https://www.drupal.org/u/webchick
+- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
+- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
+- Stefan Ruijsenaars 'stefan.r' https://www.drupal.org/u/stefanr-0
+- (provisional) Pol Dellaiera 'Pol' https://www.drupal.org/u/pol
Component maintainers
---------------------
The Drupal Core component maintainers oversee the development of Drupal
-subsystems. See http://drupal.org/contribute/core-maintainers for more
+subsystems. See https://www.drupal.org/contribute/core-maintainers for more
information on their responsibilities, and to find out how to become a component
maintainer. Current component maintainers for Drupal 7:
Ajax system
-- Alex Bronstein 'effulgentsia'
-- Randy Fay 'rfay'
-- Earl Miles 'merlinofchaos'
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
Base system
-- Károly Négyesi 'chx'
-- Damien Tournoud 'DamZ'
-- Moshe Weitzman 'moshe weitzman'
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
Batch system
-- Yves Chedemois 'yched'
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
Cache system
-- Damien Tournoud 'DamZ'
-- Nathaniel Catchpole 'catch'
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Cron system
-- Károly Négyesi 'chx'
-- Derek Wright 'dww'
+- Derek Wright 'dww' https://www.drupal.org/u/dww
Database system
-- Larry Garfield 'Crell'
+- ?
- MySQL driver
- - Larry Garfield 'Crell'
- - David Strauss 'David Strauss'
+ - David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
- PostgreSQL driver
- - Damien Tournoud 'DamZ'
- - Josh Waihi 'fiasco'
+ - Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
+ - Josh Waihi 'fiasco' https://www.drupal.org/u/josh-waihi
- Sqlite driver
- - Damien Tournoud 'DamZ'
- - Károly Négyesi 'chx'
+ - Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Database update system
-- Károly Négyesi 'chx'
+- Ashok Modi 'BTMash' https://www.drupal.org/u/btmash
Entity system
-- Nathaniel Catchpole 'catch'
-- Franz Heinzmann 'Frando'
+- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
File system
-- Andrew Morton 'drewish'
-- Aaron Winborn 'aaron'
+- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
+- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Form system
-- Károly Négyesi 'chx'
-- Alex Bronstein 'effulgentsia'
-- Wolfgang Ziegler 'fago'
-- Daniel F. Kudwien 'sun'
-- Franz Heinzmann 'Frando'
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
+- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
Image system
-- Andrew Morton 'drewish'
-- Nathan Haug 'quicksketch'
+- Andrew Morton 'drewish' https://www.drupal.org/u/drewish
+- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
Install system
-- David Rothstein 'David_Rothstein'
+- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
JavaScript
-- ?
+- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
+- Steve De Jonghe 'seutje' https://www.drupal.org/u/seutje
Language system
-- Francesco Placella 'plach'
-- Daniel F. Kudwien 'sun'
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Lock system
-- Damien Tournoud 'DamZ'
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Mail system
- ?
Markup
-- Jacine Luisi 'Jacine'
-- Daniel F. Kudwien 'sun'
+- Jacine Luisi 'Jacine' https://www.drupal.org/u/jacine
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Menu system
-- Peter Wolanin 'pwolanin'
-- Károly Négyesi 'chx'
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Path system
-- Dave Reid 'davereid'
-- Nathaniel Catchpole 'catch'
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Render system
-- Moshe Weitzman 'moshe weitzman'
-- Alex Bronstein 'effulgentsia'
-- Franz Heinzmann 'Frando'
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Franz Heinzmann 'Frando' https://www.drupal.org/u/frando
Theme system
-- Earl Miles 'merlinofchaos'
-- Alex Bronstein 'effulgentsia'
-- Joon Park 'dvessel'
-- John Albin Wilkins 'JohnAlbin'
+- Earl Miles 'merlinofchaos' https://www.drupal.org/u/merlinofchaos
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Joon Park 'dvessel' https://www.drupal.org/u/dvessel
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Token system
-- Dave Reid 'davereid'
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
XML-RPC system
-- Frederic G. Marand 'fgm'
+- Frederic G. Marand 'fgm' https://www.drupal.org/u/fgm
Topic coordinators
------------------
Accessibility
-- Everett Zufelt 'Everett Zufelt'
-- Brandon Bowersox 'brandonojc'
+- Everett Zufelt 'Everett Zufelt' https://www.drupal.org/u/everett-zufelt
+- Brandon Bowersox-Johnson 'bowersox' https://www.drupal.org/u/bowersox
Documentation
-- Jennifer Hodgdon 'jhodgdon'
-
-Security
-- Heine Deelstra 'Heine'
+- Jennifer Hodgdon 'jhodgdon' https://www.drupal.org/u/jhodgdon
Translations
-- Gerhard Killesreiter 'killes'
+- Gerhard Killesreiter 'killes' https://www.drupal.org/u/gerhard-killesreiter
User experience and usability
-- Roy Scholten 'yoroy'
-- Bojhan Somers 'Bojhan'
+- Roy Scholten 'yoroy' https://www.drupal.org/u/yoroy
+- Bojhan Somers 'Bojhan' https://www.drupal.org/u/bojhan
+
+Node Access
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
+
+
+Security team
+-----------------
+
+To report a security issue, see: https://www.drupal.org/security-team/report-issue
+
+The Drupal security team provides Security Advisories for vulnerabilities,
+assists developers in resolving security issues, and provides security
+documentation. See https://www.drupal.org/security-team for more information.
+The security team lead is:
+
+- Michael Hess 'mlhess' https://www.drupal.org/u/mlhess
Module maintainers
@@ -153,145 +167,141 @@
- ?
Block module
-- John Albin Wilkins 'JohnAlbin'
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Blog module
- ?
Book module
-- Peter Wolanin 'pwolanin'
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
Color module
- ?
Comment module
-- Nathaniel Catchpole 'catch'
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
Contact module
-- Dave Reid 'davereid'
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
Contextual module
-- Daniel F. Kudwien 'sun'
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Dashboard module
- ?
Database logging module
-- Khalid Baheyeldin 'kbahey'
+- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
Field module
-- Yves Chedemois 'yched'
-- Barry Jaspan 'bjaspan'
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
+- Barry Jaspan 'bjaspan' https://www.drupal.org/u/bjaspan
Field UI module
-- Yves Chedemois 'yched'
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
File module
-- Aaron Winborn 'aaron'
+- Aaron Winborn 'aaron' https://www.drupal.org/u/aaron
Filter module
-- Daniel F. Kudwien 'sun'
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
Forum module
-- Lee Rowlands 'larowlan'
+- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
Help module
- ?
Image module
-- Nathan Haug 'quicksketch'
+- Nathan Haug 'quicksketch' https://www.drupal.org/u/quicksketch
Locale module
-- Gábor Hojtsy 'Gábor Hojtsy'
+- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
Menu module
- ?
Node module
-- Moshe Weitzman 'moshe weitzman'
-- David Strauss 'David Strauss'
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
OpenID module
-- Vojtech Kusy 'wojtha'
-- Heine Deelstra 'Heine'
-- Christian Schmidt 'c960657'
-- Damien Tournoud 'DamZ'
+- Vojtech Kusy 'wojtha' https://www.drupal.org/u/wojtha
+- Christian Schmidt 'c960657' https://www.drupal.org/u/c960657
+- Damien Tournoud 'DamZ' https://www.drupal.org/u/damien-tournoud
Overlay module
-- Katherine Senzee 'ksenzee'
+- Katherine Senzee 'ksenzee' https://www.drupal.org/u/ksenzee
Path module
-- Dave Reid 'davereid'
+- Dave Reid 'davereid' https://www.drupal.org/u/dave-reid
PHP module
- ?
Poll module
-- ?
+- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
Profile module
- ?
RDF module
-- Stéphane Corlosquet 'scor'
+- Stéphane Corlosquet 'scor' https://www.drupal.org/u/scor
Search module
-- Doug Green 'douggreen'
+- Doug Green 'douggreen' https://www.drupal.org/u/douggreen
Shortcut module
-- David Rothstein 'David_Rothstein'
-- Kristof De Jaeger 'swentel'
+- David Rothstein 'David_Rothstein' https://www.drupal.org/u/david_rothstein
Simpletest module
-- Jimmy Berry 'boombatower'
-- Károly Négyesi 'chx'
+- Jimmy Berry 'boombatower' https://www.drupal.org/u/boombatower
Statistics module
-- Dave Reid 'davereid'
+- Tim Millwood 'timmillwood' https://www.drupal.org/u/timmillwood
Syslog module
-- Khalid Baheyeldin 'kbahey'
+- Khalid Baheyeldin 'kbahey' https://www.drupal.org/u/kbahey
System module
- ?
Taxonomy module
-- Jess Myrbo 'xjm'
-- Nathaniel Catchpole 'catch'
-- Benjamin Doherty 'bangpound'
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+- Benjamin Doherty 'bangpound' https://www.drupal.org/u/bangpound
Toolbar module
- ?
Tracker module
-- David Strauss 'David Strauss'
+- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Translation module
-- Francesco Placella 'plach'
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
Trigger module
- ?
Update module
-- Derek Wright 'dww'
+- Derek Wright 'dww' https://www.drupal.org/u/dww
User module
-- Moshe Weitzman 'moshe weitzman'
-- David Strauss 'David Strauss'
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
Theme maintainers
-----------------
Bartik theme
-- Jen Simmons 'jensimmons'
-- Jeff Burns 'Jeff Burnz'
+- Jen Simmons 'jensimmons' https://www.drupal.org/u/jensimmons
+- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Garland theme
-- John Albin Wilkins 'JohnAlbin'
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
Seven theme
-- Jeff Burns 'Jeff Burnz'
+- Jeff Burns 'Jeff Burnz' https://www.drupal.org/u/jeff-burnz
Stark theme
-- John Albin Wilkins 'JohnAlbin'
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
diff -Naur drupal-7.13/README.txt drupal-7.66/README.txt
--- drupal-7.13/README.txt 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/README.txt 2019-04-17 22:20:46.000000000 +0200
@@ -4,6 +4,7 @@
* About Drupal
* Configuration and features
+ * Installation profiles
* Appearance
* Developing for Drupal
@@ -43,6 +44,40 @@
http://drupal.org/project/modules
* See also: "Developing for Drupal" for writing your own modules, below.
+INSTALLATION PROFILES
+---------------------
+
+Installation profiles define additional steps (such as enabling modules,
+defining content types, etc.) that run after the base installation provided
+by core when Drupal is first installed. There are two basic installation
+profiles provided with Drupal core.
+
+Installation profiles from the Drupal community modify the installation process
+to provide a website for a specific use case, such as a CMS for media
+publishers, a web-based project tracking tool, or a full-fledged CRM for
+non-profit organizations raising money and accepting donations. They can be
+distributed as bare installation profiles or as "distributions". Distributions
+include Drupal core, the installation profile, and all other required
+extensions, such as contributed and custom modules, themes, and third-party
+libraries. Bare installation profiles require you to download Drupal Core and
+the required extensions separately; place the downloaded profile in the
+/profiles directory before you start the installation process. Note that the
+contents of this directory may be overwritten during updates of Drupal core;
+it is advised to keep code backups or use a version control system.
+
+Additionally, modules and themes may be placed inside subdirectories in a
+specific installation profile such as profiles/your_site_profile/modules and
+profiles/your_site_profile/themes respectively to restrict their usage to only
+sites that were installed with that specific profile.
+
+More about installation profiles and distributions:
+ * Read about the difference between installation profiles and distributions:
+ http://drupal.org/node/1089736
+ * Download contributed installation profiles and distributions:
+ http://drupal.org/project/distributions
+ * Develop your own installation profile or distribution:
+ http://drupal.org/developing/distributions
+
APPEARANCE
----------
diff -Naur drupal-7.13/UPGRADE.txt drupal-7.66/UPGRADE.txt
--- drupal-7.13/UPGRADE.txt 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/UPGRADE.txt 2019-04-17 22:20:46.000000000 +0200
@@ -64,6 +64,9 @@
Sometimes an update includes changes to default.settings.php (this will be
noted in the release notes). If that's the case, follow these steps:
+ - Locate your settings.php file in the /sites/* directory. (Typically
+ sites/default.)
+
- Make a backup copy of your settings.php file, with a different file name.
- Make a copy of the new default.settings.php file, and name the copy
@@ -74,6 +77,13 @@
database information, and you will also want to copy in any other
customizations you have added.
+ You can find the release notes for your version at
+ https://www.drupal.org/project/drupal. At bottom of the project page under
+ "Downloads" use the link for your version of Drupal to view the release
+ notes. If your version is not listed, use the 'View all releases' link. From
+ this page you can scroll down or use the filter to find your version and its
+ release notes.
+
4. Download the latest Drupal 7.x release from http://drupal.org to a
directory outside of your web root. Extract the archive and copy the files
into your Drupal directory.
@@ -141,7 +151,7 @@
download Drupal 6.x and follow the instructions in its UPGRADE.txt. This
document only applies for upgrades from 6.x to 7.x.
-3. In addition to updating to the latest available version of Drupal 7.x core,
+3. In addition to updating to the latest available version of Drupal 6.x core,
you must also upgrade all of your contributed modules for Drupal to their
latest Drupal 6.x versions.
diff -Naur drupal-7.13/authorize.php drupal-7.66/authorize.php
--- drupal-7.13/authorize.php 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/authorize.php 2019-04-17 22:20:46.000000000 +0200
@@ -4,16 +4,16 @@
* @file
* Administrative script for running authorized file operations.
*
- * Using this script, the site owner (the user actually owning the files on
- * the webserver) can authorize certain file-related operations to proceed
- * with elevated privileges, for example to deploy and upgrade modules or
- * themes. Users should not visit this page directly, but instead use an
- * administrative user interface which knows how to redirect the user to this
- * script as part of a multistep process. This script actually performs the
- * selected operations without loading all of Drupal, to be able to more
- * gracefully recover from errors. Access to the script is controlled by a
- * global killswitch in settings.php ('allow_authorize_operations') and via
- * the 'administer software updates' permission.
+ * Using this script, the site owner (the user actually owning the files on the
+ * webserver) can authorize certain file-related operations to proceed with
+ * elevated privileges, for example to deploy and upgrade modules or themes.
+ * Users should not visit this page directly, but instead use an administrative
+ * user interface which knows how to redirect the user to this script as part of
+ * a multistep process. This script actually performs the selected operations
+ * without loading all of Drupal, to be able to more gracefully recover from
+ * errors. Access to the script is controlled by a global killswitch in
+ * settings.php ('allow_authorize_operations') and via the 'administer software
+ * updates' permission.
*
* There are helper functions for setting up an operation to run via this
* system in modules/system/system.module. For more information, see:
@@ -21,16 +21,17 @@
*/
/**
- * Root directory of Drupal installation.
+ * Defines the root directory of the Drupal installation.
*/
define('DRUPAL_ROOT', getcwd());
/**
- * Global flag to identify update.php and authorize.php runs, and so
- * avoid various unwanted operations, such as hook_init() and
- * hook_exit() invokes, css/js preprocessing and translation, and
- * solve some theming issues. This flag is checked on several places
- * in Drupal code (not just authorize.php).
+ * Global flag to identify update.php and authorize.php runs.
+ *
+ * Identifies update.php and authorize.php runs, avoiding unwanted operations
+ * such as hook_init() and hook_exit() invokes, css/js preprocessing and
+ * translation, and solves some theming issues. The flag is checked in other
+ * places in Drupal code (not just authorize.php).
*/
define('MAINTENANCE_MODE', 'update');
@@ -51,7 +52,7 @@
* have access to the 'administer software updates' permission.
*
* @return
- * TRUE if the current user can run authorize.php, otherwise FALSE.
+ * TRUE if the current user can run authorize.php, and FALSE if not.
*/
function authorize_access_allowed() {
return variable_get('allow_authorize_operations', TRUE) && user_access('administer software updates');
@@ -60,7 +61,6 @@
// *** Real work of the script begins here. ***
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
-require_once DRUPAL_ROOT . '/includes/session.inc';
require_once DRUPAL_ROOT . '/includes/common.inc';
require_once DRUPAL_ROOT . '/includes/file.inc';
require_once DRUPAL_ROOT . '/includes/module.inc';
diff -Naur drupal-7.13/includes/ajax.inc drupal-7.66/includes/ajax.inc
--- drupal-7.13/includes/ajax.inc 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/includes/ajax.inc 2019-04-17 22:20:46.000000000 +0200
@@ -168,7 +168,7 @@
* displayed while awaiting a response from the callback, and add an optional
* message. Possible keys: 'type', 'message', 'url', 'interval'.
* More information is available in the
- * @link http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7 Form API Reference @endlink
+ * @link forms_api_reference.html Form API Reference @endlink
*
* In addition to using Form API for doing in-form modification, Ajax may be
* enabled by adding classes to buttons and links. By adding the 'use-ajax'
@@ -211,7 +211,7 @@
*
* When returning an Ajax command array, it is often useful to have
* status messages rendered along with other tasks in the command array.
- * In that case the the Ajax commands array may be constructed like this:
+ * In that case the Ajax commands array may be constructed like this:
* @code
* $commands = array();
* $commands[] = ajax_command_replace(NULL, $output);
@@ -230,6 +230,10 @@
* functions.
*/
function ajax_render($commands = array()) {
+ // Although ajax_deliver() does this, some contributed and custom modules
+ // render Ajax responses without using that delivery callback.
+ ajax_set_verification_header();
+
// Ajax responses aren't rendered with html.tpl.php, so we have to call
// drupal_get_css() and drupal_get_js() here, in order to have new files added
// during this request to be loaded by the page. We only want to send back
@@ -251,8 +255,8 @@
// reliably diffed with array_diff_key(), since the number can change
// due to factors unrelated to the inline content, so for now, we strip
// the inline items from Ajax responses, and can add support for them
- // when drupal_add_css() and drupal_add_js() are changed to using md5()
- // or some other hash of the inline content.
+ // when drupal_add_css() and drupal_add_js() are changed to use a hash
+ // of the inline content as the array key.
foreach ($items[$type] as $key => $item) {
if (is_numeric($key)) {
unset($items[$type][$key]);
@@ -276,7 +280,7 @@
$extra_commands = array();
if (!empty($styles)) {
- $extra_commands[] = ajax_command_prepend('head', $styles);
+ $extra_commands[] = ajax_command_add_css($styles);
}
if (!empty($scripts_header)) {
$extra_commands[] = ajax_command_prepend('head', $scripts_header);
@@ -292,7 +296,7 @@
$scripts = drupal_add_js();
if (!empty($scripts['settings'])) {
$settings = $scripts['settings'];
- array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
+ array_unshift($commands, ajax_command_settings(drupal_array_merge_deep_array($settings['data']), TRUE));
}
// Allow modules to alter any Ajax response.
@@ -308,10 +312,11 @@
* pulls the form info from $_POST.
*
* @return
- * An array containing the $form and $form_state. Use the list() function
- * to break these apart:
+ * An array containing the $form, $form_state, $form_id, $form_build_id and an
+ * initial list of Ajax $commands. Use the list() function to break these
+ * apart:
* @code
- * list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
+ * list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
* @endcode
*/
function ajax_get_form() {
@@ -331,6 +336,17 @@
drupal_exit();
}
+ // When a page level cache is enabled, the form-build id might have been
+ // replaced from within form_get_cache. If this is the case, it is also
+ // necessary to update it in the browser by issuing an appropriate Ajax
+ // command.
+ $commands = array();
+ if (isset($form['#build_id_old']) && $form['#build_id_old'] != $form['#build_id']) {
+ // If the form build ID has changed, issue an Ajax command to update it.
+ $commands[] = ajax_command_update_build_id($form);
+ $form_build_id = $form['#build_id'];
+ }
+
// Since some of the submit handlers are run, redirects need to be disabled.
$form_state['no_redirect'] = TRUE;
@@ -345,7 +361,7 @@
$form_state['input'] = $_POST;
$form_id = $form['#form_id'];
- return array($form, $form_state, $form_id, $form_build_id);
+ return array($form, $form_state, $form_id, $form_build_id, $commands);
}
/**
@@ -366,7 +382,7 @@
* @see system_menu()
*/
function ajax_form_callback() {
- list($form, $form_state) = ajax_get_form();
+ list($form, $form_state, $form_id, $form_build_id, $commands) = ajax_get_form();
drupal_process_form($form['#form_id'], $form, $form_state);
// We need to return the part of the form (or some other content) that needs
@@ -378,8 +394,20 @@
if (!empty($form_state['triggering_element'])) {
$callback = $form_state['triggering_element']['#ajax']['callback'];
}
- if (!empty($callback) && function_exists($callback)) {
- return $callback($form, $form_state);
+ if (!empty($callback) && is_callable($callback)) {
+ $result = $callback($form, $form_state);
+
+ if (!(is_array($result) && isset($result['#type']) && $result['#type'] == 'ajax')) {
+ // Turn the response into a #type=ajax array if it isn't one already.
+ $result = array(
+ '#type' => 'ajax',
+ '#commands' => ajax_prepare_response($result),
+ );
+ }
+
+ $result['#commands'] = array_merge($commands, $result['#commands']);
+
+ return $result;
}
}
@@ -463,6 +491,9 @@
}
}
+ // Let ajax.js know that this response is safe to process.
+ ajax_set_verification_header();
+
// Print the response.
$commands = ajax_prepare_response($page_callback_result);
$json = ajax_render($commands);
@@ -553,6 +584,29 @@
}
/**
+ * Sets a response header for ajax.js to trust the response body.
+ *
+ * It is not safe to invoke Ajax commands within user-uploaded files, so this
+ * header protects against those being invoked.
+ *
+ * @see Drupal.ajax.options.success()
+ */
+function ajax_set_verification_header() {
+ $added = &drupal_static(__FUNCTION__);
+
+ // User-uploaded files cannot set any response headers, so a custom header is
+ // used to indicate to ajax.js that this response is safe. Note that most
+ // Ajax requests bound using the Form API will be protected by having the URL
+ // flagged as trusted in Drupal.settings, so this header is used only for
+ // things like custom markup that gets Ajax behaviors attached.
+ if (empty($added)) {
+ drupal_add_http_header('X-Drupal-Ajax-Token', '1');
+ // Avoid sending the header twice.
+ $added = TRUE;
+ }
+}
+
+/**
* Performs end-of-Ajax-request tasks.
*
* This function is the equivalent of drupal_page_footer(), but for Ajax
@@ -740,7 +794,12 @@
$element['#attached']['js'][] = array(
'type' => 'setting',
- 'data' => array('ajax' => array($element['#id'] => $settings)),
+ 'data' => array(
+ 'ajax' => array($element['#id'] => $settings),
+ 'urlIsAjaxTrusted' => array(
+ $settings['url'] => TRUE,
+ ),
+ ),
);
// Indicate that Ajax processing was successful.
@@ -836,7 +895,8 @@
* @return
* An array suitable for use with the ajax_render() function.
*
- * See @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
+ * See
+ * @link http://docs.jquery.com/Manipulation/replaceWith#content jQuery replaceWith command @endlink
*/
function ajax_command_replace($selector, $html, $settings = NULL) {
return array(
@@ -1209,3 +1269,49 @@
'selector' => $selector,
);
}
+
+/**
+ * Creates a Drupal Ajax 'update_build_id' command.
+ *
+ * This command updates the value of a hidden form_build_id input element on a
+ * form. It requires the form passed in to have keys for both the old build ID
+ * in #build_id_old and the new build ID in #build_id.
+ *
+ * The primary use case for this Ajax command is to serve a new build ID to a
+ * form served from the cache to an anonymous user, preventing one anonymous
+ * user from accessing the form state of another anonymous users on Ajax enabled
+ * forms.
+ *
+ * @param $form
+ * The form array representing the form whose build ID should be updated.
+ */
+function ajax_command_update_build_id($form) {
+ return array(
+ 'command' => 'updateBuildId',
+ 'old' => $form['#build_id_old'],
+ 'new' => $form['#build_id'],
+ );
+}
+
+/**
+ * Creates a Drupal Ajax 'add_css' command.
+ *
+ * This method will add css via ajax in a cross-browser compatible way.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.add_css()
+ * defined in misc/ajax.js.
+ *
+ * @param $styles
+ * A string that contains the styles to be added.
+ *
+ * @return
+ * An array suitable for use with the ajax_render() function.
+ *
+ * @see misc/ajax.js
+ */
+function ajax_command_add_css($styles) {
+ return array(
+ 'command' => 'add_css',
+ 'data' => $styles,
+ );
+}
diff -Naur drupal-7.13/includes/authorize.inc drupal-7.66/includes/authorize.inc
--- drupal-7.13/includes/authorize.inc 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/includes/authorize.inc 2019-04-17 22:20:46.000000000 +0200
@@ -18,7 +18,7 @@
global $base_url, $is_https;
$form = array();
- // If possible, we want to post this form securely via https.
+ // If possible, we want to post this form securely via HTTPS.
$form['#https'] = TRUE;
// CSS we depend on lives in modules/system/maintenance.css, which is loaded
diff -Naur drupal-7.13/includes/batch.inc drupal-7.66/includes/batch.inc
--- drupal-7.13/includes/batch.inc 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/includes/batch.inc 2019-04-17 22:20:46.000000000 +0200
@@ -460,10 +460,10 @@
if (isset($batch_set['file']) && is_file($batch_set['file'])) {
include_once DRUPAL_ROOT . '/' . $batch_set['file'];
}
- if (function_exists($batch_set['finished'])) {
+ if (is_callable($batch_set['finished'])) {
$queue = _batch_queue($batch_set);
$operations = $queue->getAllItems();
- $batch_set['finished']($batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
+ call_user_func($batch_set['finished'], $batch_set['success'], $batch_set['results'], $operations, format_interval($batch_set['elapsed'] / 1000));
}
}
}
diff -Naur drupal-7.13/includes/bootstrap.inc drupal-7.66/includes/bootstrap.inc
--- drupal-7.13/includes/bootstrap.inc 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/includes/bootstrap.inc 2019-04-17 22:20:46.000000000 +0200
@@ -8,7 +8,7 @@
/**
* The current system version.
*/
-define('VERSION', '7.13');
+define('VERSION', '7.66');
/**
* Core API compatibility.
@@ -26,6 +26,21 @@
define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M');
/**
+ * 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);
+
+/**
* Indicates that the item should never be removed unless explicitly selected.
*
* The item may be removed using cache_clear_all() with a cache ID.
@@ -68,32 +83,32 @@
define('WATCHDOG_ALERT', 1);
/**
- * Log message severity -- Critical: critical conditions.
+ * Log message severity -- Critical conditions.
*/
define('WATCHDOG_CRITICAL', 2);
/**
- * Log message severity -- Error: error conditions.
+ * Log message severity -- Error conditions.
*/
define('WATCHDOG_ERROR', 3);
/**
- * Log message severity -- Warning: warning conditions.
+ * Log message severity -- Warning conditions.
*/
define('WATCHDOG_WARNING', 4);
/**
- * Log message severity -- Notice: normal but significant condition.
+ * Log message severity -- Normal but significant conditions.
*/
define('WATCHDOG_NOTICE', 5);
/**
- * Log message severity -- Informational: informational messages.
+ * Log message severity -- Informational messages.
*/
define('WATCHDOG_INFO', 6);
/**
- * Log message severity -- Debug: debug-level messages.
+ * Log message severity -- Debug-level messages.
*/
define('WATCHDOG_DEBUG', 7);
@@ -203,12 +218,16 @@
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
/**
- * Flag for drupal_set_title(); text is not sanitized, so run check_plain().
+ * Flag used to indicate that text is not sanitized, so run check_plain().
+ *
+ * @see drupal_set_title()
*/
define('CHECK_PLAIN', 0);
/**
- * Flag for drupal_set_title(); text has already been sanitized.
+ * Flag used to indicate that text has already been sanitized.
+ *
+ * @see drupal_set_title()
*/
define('PASS_THROUGH', -1);
@@ -225,11 +244,25 @@
/**
* Regular expression to match PHP function names.
*
- * @see http://php.net/manual/en/language.functions.php
+ * @see http://php.net/manual/language.functions.php
*/
define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
/**
+ * A RFC7231 Compliant date.
+ *
+ * http://tools.ietf.org/html/rfc7231#section-7.1.1.1
+ *
+ * Example: Sun, 06 Nov 1994 08:49:37 GMT
+ *
+ * This constant was introduced in PHP 7.0.19 and PHP 7.1.5 but needs to be
+ * defined by Drupal for earlier PHP versions.
+ */
+if (!defined('DATE_RFC7231')) {
+ define('DATE_RFC7231', 'D, d M Y H:i:s \G\M\T');
+}
+
+/**
* Provides a caching wrapper to be used in place of large array structures.
*
* This class should be extended by systems that need to cache large amounts
@@ -259,7 +292,7 @@
* error, and $var will be populated with the contents of $object['foo'], but
* that data will be passed by value, not reference. For more information on
* the PHP limitation, see the note in the official PHP documentation at·
- * http://php.net/manual/en/arrayaccess.offsetget.php on
+ * http://php.net/manual/arrayaccess.offsetget.php on
* ArrayAccess::offsetGet().
*
* By default, the class accounts for caches where calling functions might
@@ -279,7 +312,7 @@
* means that assigning an offset via arrayAccess will only apply while the
* object is in scope and will not be written back to the persistent cache.
* This follows a similar pattern to static vs. persistent caching in
- * procedural code. Extending classes may wish to alter this behaviour, for
+ * procedural code. Extending classes may wish to alter this behavior, for
* example by overriding offsetSet() and adding an automatic call to persist().
*
* @see SchemaCache
@@ -365,11 +398,11 @@
* without necessarily writing back to the persistent cache at the end.
*
* @param $offset
- * The array offset that was request.
+ * The array offset that was requested.
* @param $persist
* Optional boolean to specify whether the offset should be persisted or
* not, defaults to TRUE. When called with $persist = FALSE the offset will
- * be unflagged so that it will not written at the end of the request.
+ * be unflagged so that it will not be written at the end of the request.
*/
protected function persist($offset, $persist = TRUE) {
$this->keysToPersist[$offset] = $persist;
@@ -498,56 +531,11 @@
}
/**
- * Finds the appropriate configuration directory.
+ * Returns the appropriate configuration directory.
*
- * Finds a matching configuration directory by stripping the website's
- * hostname from left to right and pathname from right to left. The first
- * configuration file found will be used and the remaining ones will be ignored.
- * If no configuration file is found, return a default value '$confdir/default'.
- *
- * With a site located at http://www.example.com:8080/mysite/test/, the file,
- * settings.php, is searched for in the following directories:
- *
- * - $confdir/8080.www.example.com.mysite.test
- * - $confdir/www.example.com.mysite.test
- * - $confdir/example.com.mysite.test
- * - $confdir/com.mysite.test
- *
- * - $confdir/8080.www.example.com.mysite
- * - $confdir/www.example.com.mysite
- * - $confdir/example.com.mysite
- * - $confdir/com.mysite
- *
- * - $confdir/8080.www.example.com
- * - $confdir/www.example.com
- * - $confdir/example.com
- * - $confdir/com
- *
- * - $confdir/default
- *
- * If a file named sites.php is present in the $confdir, it will be loaded
- * prior to scanning for directories. It should define an associative array
- * named $sites, which maps domains to directories. It should be in the form
- * of:
- * @code
- * $sites = array(
- * 'The url to alias' => 'A directory within the sites directory'
- * );
- * @endcode
- * For example:
- * @code
- * $sites = array(
- * 'devexample.com' => 'example.com',
- * 'localhost.example' => 'example.com',
- * );
- * @endcode
- * The above array will cause Drupal to look for a directory named
- * "example.com" in the sites directory whenever a request comes from
- * "example.com", "devexample.com", or "localhost/example". That is useful
- * on development servers, where the domain name may not be the same as the
- * domain of the live server. Since Drupal stores file paths into the database
- * (files, system table, etc.) this will ensure the paths are correct while
- * accessed on development servers.
+ * Returns the configuration path based on the site's hostname, port, and
+ * pathname. See default.settings.php for examples on how the URL is converted
+ * to a directory.
*
* @param bool $require_settings
* Only configuration directories with an existing settings.php file
@@ -560,6 +548,8 @@
*
* @return
* The path of the matching directory.
+ *
+ * @see default.settings.php
*/
function conf_path($require_settings = TRUE, $reset = FALSE) {
$conf = &drupal_static(__FUNCTION__, '');
@@ -706,13 +696,27 @@
ini_set('session.use_only_cookies', '1');
ini_set('session.use_trans_sid', '0');
// Don't send HTTP headers using PHP's session handler.
- ini_set('session.cache_limiter', 'none');
+ // An empty string is used here to disable the cache limiter.
+ ini_set('session.cache_limiter', '');
// Use httponly session cookies.
ini_set('session.cookie_httponly', '1');
// Set sane locale settings, to ensure consistent string, dates, times and
// numbers handling.
setlocale(LC_ALL, 'C');
+
+ // PHP's built-in phar:// stream wrapper is not sufficiently secure. Override
+ // it with a more secure one, which requires PHP 5.3.3. For lower versions,
+ // unregister the built-in one without replacing it. Sites needing phar
+ // support for lower PHP versions must implement hook_stream_wrappers() to
+ // register their desired implementation.
+ if (in_array('phar', stream_get_wrappers(), TRUE)) {
+ stream_wrapper_unregister('phar');
+ if (version_compare(PHP_VERSION, '5.3.3', '>=')) {
+ include_once DRUPAL_ROOT . '/includes/file.phar.inc';
+ file_register_phar_wrapper();
+ }
+ }
}
/**
@@ -722,7 +726,24 @@
* TRUE if only containing valid characters, or FALSE otherwise.
*/
function drupal_valid_http_host($host) {
- return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
+ // Limit the length of the host name to 1000 bytes to prevent DoS attacks with
+ // long host names.
+ return strlen($host) <= 1000
+ // Limit the number of subdomains and port separators to prevent DoS attacks
+ // in conf_path().
+ && substr_count($host, '.') <= 100
+ && substr_count($host, ':') <= 100
+ && preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
+}
+
+/**
+ * Checks whether an HTTPS request is being served.
+ *
+ * @return bool
+ * TRUE if the request is HTTPS, FALSE otherwise.
+ */
+function drupal_is_https() {
+ return isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
}
/**
@@ -731,19 +752,18 @@
function drupal_settings_initialize() {
global $base_url, $base_path, $base_root;
- // Export the following settings.php variables to the global namespace
+ // Export these settings.php variables to the global namespace.
global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
$conf = array();
if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
}
- $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
+ $is_https = drupal_is_https();
if (isset($base_url)) {
// Parse fixed base URL from settings.php.
$parts = parse_url($base_url);
- $http_protocol = $parts['scheme'];
if (!isset($parts['path'])) {
$parts['path'] = '';
}
@@ -752,7 +772,7 @@
$base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
}
else {
- // Create base URL
+ // Create base URL.
$http_protocol = $is_https ? 'https' : 'http';
$base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
@@ -778,7 +798,7 @@
}
else {
// Otherwise use $base_url as session name, without the protocol
- // to use the same session identifiers across http and https.
+ // to use the same session identifiers across HTTP and HTTPS.
list( , $session_name) = explode('://', $base_url, 2);
// HTTP_HOST can be modified by a visitor, but we already sanitized it
// in drupal_settings_initialize().
@@ -819,7 +839,7 @@
*
* This function plays a key role in allowing Drupal's resources (modules
* and themes) to be located in different places depending on a site's
- * configuration. For example, a module 'foo' may legally be be located
+ * configuration. For example, a module 'foo' may legally be located
* in any of these three places:
*
* modules/foo/foo.module
@@ -830,20 +850,27 @@
* the above, depending on where the module is located.
*
* @param $type
- * The type of the item (i.e. theme, theme_engine, module, profile).
+ * The type of the item (theme, theme_engine, module, profile).
* @param $name
* The name of the item for which the filename is requested.
* @param $filename
* The filename of the item if it is to be set explicitly rather
* than by consulting the database.
+ * @param bool $trigger_error
+ * Whether to trigger an error when a file is missing or has unexpectedly
+ * moved. This defaults to TRUE, but can be set to FALSE by calling code that
+ * merely wants to check whether an item exists in the filesystem.
*
* @return
- * The filename of the requested item.
- */
-function drupal_get_filename($type, $name, $filename = NULL) {
+ * The filename of the requested item or NULL if the item is not found.
+ */
+function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TRUE) {
+ // The $files static variable will hold the locations of all requested files.
+ // We can be sure that any file listed in this static variable actually
+ // exists as all additions have gone through a file_exists() check.
// The location of files will not change during the request, so do not use
// drupal_static().
- static $files = array(), $dirs = array();
+ static $files = array();
// Profiles are a special case: they have a fixed location and naming.
if ($type == 'profile') {
@@ -855,64 +882,296 @@
}
if (!empty($filename) && file_exists($filename)) {
+ // Prime the static cache with the provided filename.
$files[$type][$name] = $filename;
}
elseif (isset($files[$type][$name])) {
- // nothing
+ // This item had already been found earlier in the request, either through
+ // priming of the static cache (for example, in system_list()), through a
+ // lookup in the {system} table, or through a file scan (cached or not). Do
+ // nothing.
}
- // Verify that we have an active database connection, before querying
- // the database. This is required because this function is called both
- // before we have a database connection (i.e. during installation) and
- // when a database connection fails.
else {
+ // Look for the filename listed in the {system} table. Verify that we have
+ // an active database connection before doing so, since this function is
+ // called both before we have a database connection (i.e. during
+ // installation) and when a database connection fails.
+ $database_unavailable = TRUE;
try {
if (function_exists('db_query')) {
$file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
- if (file_exists(DRUPAL_ROOT . '/' . $file)) {
+ if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
$files[$type][$name] = $file;
}
+ $database_unavailable = FALSE;
}
}
catch (Exception $e) {
// The database table may not exist because Drupal is not yet installed,
- // or the database might be down. We have a fallback for this case so we
- // hide the error completely.
+ // the database might be down, or we may have done a non-database cache
+ // flush while $conf['page_cache_without_database'] = TRUE and
+ // $conf['page_cache_invoke_hooks'] = TRUE. We have a fallback for these
+ // cases so we hide the error completely.
}
- // Fallback to searching the filesystem if the database could not find the
- // file or the file returned by the database is not found.
+ // Fall back to searching the filesystem if the database could not find the
+ // file or the file does not exist at the path returned by the database.
if (!isset($files[$type][$name])) {
- // We have a consistent directory naming: modules, themes...
- $dir = $type . 's';
- if ($type == 'theme_engine') {
- $dir = 'themes/engines';
- $extension = 'engine';
- }
- elseif ($type == 'theme') {
- $extension = 'info';
- }
- else {
- $extension = $type;
- }
+ $files[$type][$name] = _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable);
+ }
+ }
- if (!isset($dirs[$dir][$extension])) {
- $dirs[$dir][$extension] = TRUE;
- if (!function_exists('drupal_system_listing')) {
- require_once DRUPAL_ROOT . '/includes/common.inc';
- }
- // Scan the appropriate directories for all files with the requested
- // extension, not just the file we are currently looking for. This
- // prevents unnecessary scans from being repeated when this function is
- // called more than once in the same page request.
- $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
- foreach ($matches as $matched_name => $file) {
- $files[$type][$matched_name] = $file->uri;
+ if (isset($files[$type][$name])) {
+ return $files[$type][$name];
+ }
+}
+
+/**
+ * Performs a cached file system scan as a fallback when searching for a file.
+ *
+ * This function looks for the requested file by triggering a file scan,
+ * caching the new location if the file has moved and caching the miss
+ * if the file is missing. If a file had been marked as missing in a previous
+ * file scan, or if it has been marked as moved and is still in the last known
+ * location, no new file scan will be performed.
+ *
+ * @param string $type
+ * The type of the item (theme, theme_engine, module, profile).
+ * @param string $name
+ * The name of the item for which the filename is requested.
+ * @param bool $trigger_error
+ * Whether to trigger an error when a file is missing or has unexpectedly
+ * moved.
+ * @param bool $database_unavailable
+ * Whether this function is being called because the Drupal database could
+ * not be queried for the file's location.
+ *
+ * @return
+ * The filename of the requested item or NULL if the item is not found.
+ *
+ * @see drupal_get_filename()
+ */
+function _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable) {
+ $file_scans = &_drupal_file_scan_cache();
+ $filename = NULL;
+
+ // If the cache indicates that the item is missing, or we can verify that the
+ // item exists in the location the cache says it exists in, use that.
+ if (isset($file_scans[$type][$name]) && ($file_scans[$type][$name] === FALSE || file_exists($file_scans[$type][$name]))) {
+ $filename = $file_scans[$type][$name];
+ }
+ // Otherwise, perform a new file scan to find the item.
+ else {
+ $filename = _drupal_get_filename_perform_file_scan($type, $name);
+ // Update the static cache, and mark the persistent cache for updating at
+ // the end of the page request. See drupal_file_scan_write_cache().
+ $file_scans[$type][$name] = $filename;
+ $file_scans['#write_cache'] = TRUE;
+ }
+
+ // If requested, trigger a user-level warning about the missing or
+ // unexpectedly moved file. If the database was unavailable, do not trigger a
+ // warning in the latter case, though, since if the {system} table could not
+ // be queried there is no way to know if the location found here was
+ // "unexpected" or not.
+ if ($trigger_error) {
+ $error_type = $filename === FALSE ? 'missing' : 'moved';
+ if ($error_type == 'missing' || !$database_unavailable) {
+ _drupal_get_filename_fallback_trigger_error($type, $name, $error_type);
+ }
+ }
+
+ // The cache stores FALSE for files that aren't found (to be able to
+ // distinguish them from files that have not yet been searched for), but
+ // drupal_get_filename() expects NULL for these instead, so convert to NULL
+ // before returning.
+ if ($filename === FALSE) {
+ $filename = NULL;
+ }
+ return $filename;
+}
+
+/**
+ * Returns the current list of cached file system scan results.
+ *
+ * @return
+ * An associative array tracking the most recent file scan results for all
+ * files that have had scans performed. The keys are the type and name of the
+ * item that was searched for, and the values can be either:
+ * - Boolean FALSE if the item was not found in the file system.
+ * - A string pointing to the location where the item was found.
+ */
+function &_drupal_file_scan_cache() {
+ $file_scans = &drupal_static(__FUNCTION__, array());
+
+ // The file scan results are stored in a persistent cache (in addition to the
+ // static cache) but because this function can be called before the
+ // persistent cache is available, we must merge any items that were found
+ // earlier in the page request into the results from the persistent cache.
+ if (!isset($file_scans['#cache_merge_done'])) {
+ try {
+ if (function_exists('cache_get')) {
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ if (!empty($cache->data)) {
+ // File scan results from the current request should take precedence
+ // over the results from the persistent cache, since they are newer.
+ $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
}
+ // Set a flag to indicate that the persistent cache does not need to be
+ // merged again.
+ $file_scans['#cache_merge_done'] = TRUE;
}
}
+ catch (Exception $e) {
+ // Hide the error.
+ }
}
- if (isset($files[$type][$name])) {
- return $files[$type][$name];
+ return $file_scans;
+}
+
+/**
+ * Performs a file system scan to search for a system resource.
+ *
+ * @param $type
+ * The type of the item (theme, theme_engine, module, profile).
+ * @param $name
+ * The name of the item for which the filename is requested.
+ *
+ * @return
+ * The filename of the requested item or FALSE if the item is not found.
+ *
+ * @see drupal_get_filename()
+ * @see _drupal_get_filename_fallback()
+ */
+function _drupal_get_filename_perform_file_scan($type, $name) {
+ // The location of files will not change during the request, so do not use
+ // drupal_static().
+ static $dirs = array(), $files = array();
+
+ // We have a consistent directory naming: modules, themes...
+ $dir = $type . 's';
+ if ($type == 'theme_engine') {
+ $dir = 'themes/engines';
+ $extension = 'engine';
+ }
+ elseif ($type == 'theme') {
+ $extension = 'info';
+ }
+ else {
+ $extension = $type;
+ }
+
+ // Check if we had already scanned this directory/extension combination.
+ if (!isset($dirs[$dir][$extension])) {
+ // Log that we have now scanned this directory/extension combination
+ // into a static variable so as to prevent unnecessary file scans.
+ $dirs[$dir][$extension] = TRUE;
+ if (!function_exists('drupal_system_listing')) {
+ require_once DRUPAL_ROOT . '/includes/common.inc';
+ }
+ // Scan the appropriate directories for all files with the requested
+ // extension, not just the file we are currently looking for. This
+ // prevents unnecessary scans from being repeated when this function is
+ // called more than once in the same page request.
+ $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
+ foreach ($matches as $matched_name => $file) {
+ // Log the locations found in the file scan into a static variable.
+ $files[$type][$matched_name] = $file->uri;
+ }
+ }
+
+ // Return the results of the file system scan, or FALSE to indicate the file
+ // was not found.
+ return isset($files[$type][$name]) ? $files[$type][$name] : FALSE;
+}
+
+/**
+ * Triggers a user-level warning for missing or unexpectedly moved files.
+ *
+ * @param $type
+ * The type of the item (theme, theme_engine, module, profile).
+ * @param $name
+ * The name of the item for which the filename is requested.
+ * @param $error_type
+ * The type of the error ('missing' or 'moved').
+ *
+ * @see drupal_get_filename()
+ * @see _drupal_get_filename_fallback()
+ */
+function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type) {
+ // Hide messages due to known bugs that will appear on a lot of sites.
+ // @todo Remove this in https://www.drupal.org/node/2383823
+ if (empty($name)) {
+ return;
+ }
+
+ // Make sure we only show any missing or moved file errors only once per
+ // request.
+ static $errors_triggered = array();
+ if (empty($errors_triggered[$type][$name][$error_type])) {
+ // Use _drupal_trigger_error_with_delayed_logging() here since these are
+ // triggered during low-level operations that cannot necessarily be
+ // interrupted by a watchdog() call.
+ if ($error_type == 'missing') {
+ _drupal_trigger_error_with_delayed_logging(format_string('The following @type is missing from the file system: %name. For information about how to fix this, see the documentation page.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+ }
+ elseif ($error_type == 'moved') {
+ _drupal_trigger_error_with_delayed_logging(format_string('The following @type has moved within the file system: %name. In order to fix this, clear caches or put the @type back in its original location. For more information, see the documentation page.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+ }
+ $errors_triggered[$type][$name][$error_type] = TRUE;
+ }
+}
+
+/**
+ * Invokes trigger_error() with logging delayed until the end of the request.
+ *
+ * This is an alternative to PHP's trigger_error() function which can be used
+ * during low-level Drupal core operations that need to avoid being interrupted
+ * by a watchdog() call.
+ *
+ * Normally, Drupal's error handler calls watchdog() in response to a
+ * trigger_error() call. However, this invokes hook_watchdog() which can run
+ * arbitrary code. If the trigger_error() happens in the middle of an
+ * operation such as a rebuild operation which should not be interrupted by
+ * arbitrary code, that could potentially break or trigger the rebuild again.
+ * This function protects against that by delaying the watchdog() call until
+ * the end of the current page request.
+ *
+ * This is an internal function which should only be called by low-level Drupal
+ * core functions. It may be removed in a future Drupal 7 release.
+ *
+ * @param string $error_msg
+ * The error message to trigger. As with trigger_error() itself, this is
+ * limited to 1024 bytes; additional characters beyond that will be removed.
+ * @param int $error_type
+ * (optional) The type of error. This should be one of the E_USER family of
+ * constants. As with trigger_error() itself, this defaults to E_USER_NOTICE
+ * if not provided.
+ *
+ * @see _drupal_log_error()
+ */
+function _drupal_trigger_error_with_delayed_logging($error_msg, $error_type = E_USER_NOTICE) {
+ $delay_logging = &drupal_static(__FUNCTION__, FALSE);
+ $delay_logging = TRUE;
+ trigger_error($error_msg, $error_type);
+ $delay_logging = FALSE;
+}
+
+/**
+ * Writes the file scan cache to the persistent cache.
+ *
+ * This cache stores all files marked as missing or moved after a file scan
+ * to prevent unnecessary file scans in subsequent requests. This cache is
+ * cleared in system_list_reset() (i.e. after a module/theme rebuild).
+ */
+function drupal_file_scan_write_cache() {
+ // Only write to the persistent cache if requested, and if we know that any
+ // data previously in the cache was successfully loaded and merged in by
+ // _drupal_file_scan_cache().
+ $file_scans = &_drupal_file_scan_cache();
+ if (isset($file_scans['#write_cache']) && isset($file_scans['#cache_merge_done'])) {
+ unset($file_scans['#write_cache']);
+ cache_set('_drupal_file_scan_cache', $file_scans, 'cache_bootstrap');
}
}
@@ -966,7 +1225,7 @@
* The default value to use if this variable has never been set.
*
* @return
- * The value of the variable.
+ * The value of the variable. Unserialization is taken care of as necessary.
*
* @see variable_del()
* @see variable_set()
@@ -1063,7 +1322,7 @@
* Determines the cacheability of the current page.
*
* @param $allow_caching
- * Set to FALSE if you want to prevent this page to get cached.
+ * Set to FALSE if you want to prevent this page from being cached.
*
* @return
* TRUE if the current page can be cached, FALSE otherwise.
@@ -1213,10 +1472,11 @@
* Headers are set in drupal_add_http_header(). Default headers are not set
* if they have been replaced or unset using drupal_add_http_header().
*
- * @param $default_headers
- * An array of headers as name/value pairs.
- * @param $single
- * If TRUE and headers have already be sent, send only the specified header.
+ * @param array $default_headers
+ * (optional) An array of headers as name/value pairs.
+ * @param bool $only_default
+ * (optional) If TRUE and headers have already been sent, send only the
+ * specified headers.
*/
function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
$headers_sent = &drupal_static(__FUNCTION__, FALSE);
@@ -1239,7 +1499,7 @@
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
}
// Skip headers that have been unset.
- elseif ($value) {
+ elseif ($value !== FALSE) {
header($header_names[$name_lower] . ': ' . $value);
}
}
@@ -1252,23 +1512,10 @@
* fresh page on every request. This prevents authenticated users from seeing
* locally cached pages.
*
- * Also give each page a unique ETag. This will force clients to include both
- * an If-Modified-Since header and an If-None-Match header when doing
- * conditional requests for the page (required by RFC 2616, section 13.3.4),
- * making the validation more robust. This is a workaround for a bug in Mozilla
- * Firefox that is triggered when Drupal's caching is enabled and the user
- * accesses Drupal via an HTTP proxy (see
- * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
- * user requests a page, and then logs out and requests the same page again,
- * Firefox may send a conditional request based on the page that was cached
- * locally when the user was logged in. If this page did not have an ETag
- * header, the request only contains an If-Modified-Since header. The date will
- * be recent, because with authenticated users the Last-Modified header always
- * refers to the time of the request. If the user accesses Drupal via a proxy
- * server, and the proxy already has a cached copy of the anonymous page with an
- * older Last-Modified date, the proxy may respond with 304 Not Modified, making
- * the client think that the anonymous and authenticated pageviews are
- * identical.
+ * ETag and Last-Modified headers are not set per default for authenticated
+ * users so that browsers do not send If-Modified-Since headers from
+ * authenticated user pages. drupal_serve_page_from_cache() will set appropriate
+ * ETag and Last-Modified headers for cached pages.
*
* @see drupal_page_set_cache()
*/
@@ -1281,9 +1528,11 @@
$default_headers = array(
'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
- 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
- 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
- 'ETag' => '"' . REQUEST_TIME . '"',
+ 'Cache-Control' => 'no-cache, must-revalidate',
+ // Prevent browsers from sniffing a response and picking a MIME type
+ // different from the declared content-type, since that can lead to
+ // XSS and other vulnerabilities.
+ 'X-Content-Type-Options' => 'nosniff',
);
drupal_send_headers($default_headers);
}
@@ -1301,7 +1550,7 @@
*/
function drupal_serve_page_from_cache(stdClass $cache) {
// Negotiate whether to use compression.
- $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+ $page_compression = !empty($cache->data['page_compressed']);
$return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
// Get headers set in hook_boot(). Keys are lower-case.
@@ -1351,7 +1600,7 @@
drupal_add_http_header($name, $value);
}
- $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
+ $default_headers['Last-Modified'] = gmdate(DATE_RFC7231, $cache->created);
// HTTP/1.0 proxies does not support the Vary header, so prevent any caching
// by sending an Expires date in the past. HTTP/1.1 clients ignores the
@@ -1432,6 +1681,7 @@
* more information, including recommendations on how to break up or not
* break up strings for translation.
*
+ * @section sec_translating_vars Translating Variables
* You should never use t() to translate variables, such as calling
* @code t($text); @endcode, unless the text that the variable holds has been
* passed through t() elsewhere (e.g., $text is one of several translated
@@ -1447,13 +1697,32 @@
* Basically, you can put variables like @name into your string, and t() will
* substitute their sanitized values at translation time. (See the
* Localization API pages referenced above and the documentation of
- * format_string() for details.) Translators can then rearrange the string as
- * necessary for the language (e.g., in Spanish, it might be "blog de @name").
+ * format_string() for details about how to define variables in your string.)
+ * Translators can then rearrange the string as necessary for the language
+ * (e.g., in Spanish, it might be "blog de @name").
*
+ * @section sec_alt_funcs_install Use During Installation Phase
* During the Drupal installation phase, some resources used by t() wil not be
* available to code that needs localization. See st() and get_t() for
* alternatives.
*
+ * @section sec_context String context
+ * Matching source strings are normally only translated once, and the same
+ * translation is used everywhere that has a matching string. However, in some
+ * cases, a certain English source string needs to have multiple translations.
+ * One example of this is the string "May", which could be used as either a
+ * full month name or a 3-letter abbreviated month. In other languages where
+ * the month name for May has more than 3 letters, you would need to provide
+ * two different translations (one for the full name and one abbreviated), and
+ * the correct form would need to be chosen, depending on how "May" is being
+ * used. To facilitate this, the "May" string should be provided with two
+ * different contexts in the $options parameter when calling t(). For example:
+ * @code
+ * t('May', array(), array('context' => 'Long month name')
+ * t('May', array(), array('context' => 'Abbreviated month name')
+ * @endcode
+ * See https://localize.drupal.org/node/2109 for more information.
+ *
* @param $string
* A string containing the English string to translate.
* @param $args
@@ -1464,8 +1733,9 @@
* An associative array of additional options, with the following elements:
* - 'langcode' (defaults to the current language): The language code to
* translate to a language other than what is used to display the page.
- * - 'context' (defaults to the empty context): The context the source string
- * belongs to.
+ * - 'context' (defaults to the empty context): A string giving the context
+ * that the source string belongs to. See @ref sec_context above for more
+ * information.
*
* @return
* The translated string.
@@ -1511,21 +1781,34 @@
}
/**
- * Replaces placeholders with sanitized values in a string.
+ * Formats a string for HTML display by replacing variable placeholders.
+ *
+ * This function replaces variable placeholders in a string with the requested
+ * values and escapes the values so they can be safely displayed as HTML. It
+ * should be used on any unknown text that is intended to be printed to an HTML
+ * page (especially text that may have come from untrusted users, since in that
+ * case it prevents cross-site scripting and other security problems).
+ *
+ * In most cases, you should use t() rather than calling this function
+ * directly, since it will translate the text (on non-English-only sites) in
+ * addition to formatting it.
*
* @param $string
* A string containing placeholders.
* @param $args
* An associative array of replacements to make. Occurrences in $string of
- * any key in $args are replaced with the corresponding value, after
- * sanitization. The sanitization function depends on the first character of
- * the key:
- * - !variable: Inserted as is. Use this for text that has already been
- * sanitized.
- * - @variable: Escaped to HTML using check_plain(). Use this for anything
- * displayed on a page on the site.
- * - %variable: Escaped as a placeholder for user-submitted content using
- * drupal_placeholder(), which shows up as emphasized text.
+ * any key in $args are replaced with the corresponding value, after optional
+ * sanitization and formatting. The type of sanitization and formatting
+ * depends on the first character of the key:
+ * - @variable: Escaped to HTML using check_plain(). Use this as the default
+ * choice for anything displayed on a page on the site.
+ * - %variable: Escaped to HTML and formatted using drupal_placeholder(),
+ * which makes it display as emphasized text.
+ * - !variable: Inserted as is, with no sanitization or formatting. Only use
+ * this for text that has already been prepared for HTML display (for
+ * example, user-supplied text that has already been run through
+ * check_plain() previously, or is expected to contain some limited HTML
+ * tags and has already been run through filter_xss() previously).
*
* @see t()
* @ingroup sanitization
@@ -1558,12 +1841,13 @@
* Also validates strings as UTF-8 to prevent cross site scripting attacks on
* Internet Explorer 6.
*
- * @param $text
+ * @param string $text
* The text to be checked or processed.
*
- * @return
- * An HTML safe version of $text, or an empty string if $text is not
- * valid UTF-8.
+ * @return string
+ * An HTML safe version of $text. If $text is not valid UTF-8, an empty string
+ * is returned and, on PHP < 5.4, a warning may be issued depending on server
+ * configuration (see @link https://bugs.php.net/bug.php?id=47494 @endlink).
*
* @see drupal_validate_utf8()
* @ingroup sanitization
@@ -1648,14 +1932,14 @@
* information about the passed-in exception is used.
* @param $variables
* Array of variables to replace in the message on display. Defaults to the
- * return value of drupal_decode_exception().
+ * return value of _drupal_decode_exception().
* @param $severity
* The severity of the message, as per RFC 3164.
* @param $link
* A link to associate with the message.
*
* @see watchdog()
- * @see drupal_decode_exception()
+ * @see _drupal_decode_exception()
*/
function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {
@@ -1691,8 +1975,16 @@
* NULL if message is already translated or not possible to
* translate.
* @param $severity
- * The severity of the message, as per RFC 3164. Possible values are
- * WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
+ * The severity of the message; one of the following values as defined in
+ * @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
+ * - WATCHDOG_EMERGENCY: Emergency, system is unusable.
+ * - WATCHDOG_ALERT: Alert, action must be taken immediately.
+ * - WATCHDOG_CRITICAL: Critical conditions.
+ * - WATCHDOG_ERROR: Error conditions.
+ * - WATCHDOG_WARNING: Warning conditions.
+ * - WATCHDOG_NOTICE: (default) Normal but significant conditions.
+ * - WATCHDOG_INFO: Informational messages.
+ * - WATCHDOG_DEBUG: Debug-level messages.
* @param $link
* A link to associate with the message.
*
@@ -1709,6 +2001,9 @@
if (!$in_error_state && function_exists('module_implements')) {
$in_error_state = TRUE;
+ // The user object may not exist in all conditions, so 0 is substituted if needed.
+ $user_uid = isset($user->uid) ? $user->uid : 0;
+
// Prepare the fields to be logged
$log_entry = array(
'type' => $type,
@@ -1717,10 +2012,12 @@
'severity' => $severity,
'link' => $link,
'user' => $user,
+ 'uid' => $user_uid,
'request_uri' => $base_root . request_uri(),
'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
'ip' => ip_address(),
- 'timestamp' => REQUEST_TIME,
+ // Request time isn't accurate for long processes, use time() instead.
+ 'timestamp' => time(),
);
// Call the logging hooks to log/process the message
@@ -1735,25 +2032,40 @@
}
/**
- * Sets a message which reflects the status of the performed operation.
+ * Sets a message to display to the user.
*
- * If the function is called with no arguments, this function returns all set
- * messages without clearing them.
+ * Messages are stored in a session variable and displayed in page.tpl.php via
+ * the $messages theme variable.
*
- * @param $message
- * The message to be displayed to the user. For consistency with other
- * messages, it should begin with a capital letter and end with a period.
- * @param $type
- * The type of the message. One of the following values are possible:
+ * Example usage:
+ * @code
+ * drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
+ * @endcode
+ *
+ * @param string $message
+ * (optional) The translated message to be displayed to the user. For
+ * consistency with other messages, it should begin with a capital letter and
+ * end with a period.
+ * @param string $type
+ * (optional) The message's type. Defaults to 'status'. These values are
+ * supported:
* - 'status'
* - 'warning'
* - 'error'
- * @param $repeat
- * If this is FALSE and the message is already set, then the message won't
- * be repeated.
+ * @param bool $repeat
+ * (optional) If this is FALSE and the message is already set, then the
+ * message won't be repeated. Defaults to TRUE.
+ *
+ * @return array|null
+ * A multidimensional array with keys corresponding to the set message types.
+ * The indexed array values of each contain the set messages for that type.
+ * Or, if there are no messages set, the function returns NULL.
+ *
+ * @see drupal_get_messages()
+ * @see theme_status_messages()
*/
function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
- if ($message) {
+ if ($message || $message === '0' || $message === 0) {
if (!isset($_SESSION['messages'][$type])) {
$_SESSION['messages'][$type] = array();
}
@@ -1771,18 +2083,29 @@
}
/**
- * Returns all messages that have been set.
+ * Returns all messages that have been set with drupal_set_message().
*
- * @param $type
- * (optional) Only return messages of this type.
- * @param $clear_queue
- * (optional) Set to FALSE if you do not want to clear the messages queue
+ * @param string $type
+ * (optional) Limit the messages returned by type. Defaults to NULL, meaning
+ * all types. These values are supported:
+ * - NULL
+ * - 'status'
+ * - 'warning'
+ * - 'error'
+ * @param bool $clear_queue
+ * (optional) If this is TRUE, the queue will be cleared of messages of the
+ * type specified in the $type parameter. Otherwise the queue will be left
+ * intact. Defaults to TRUE.
+ *
+ * @return array
+ * A multidimensional array with keys corresponding to the set message types.
+ * The indexed array values of each contain the set messages for that type.
+ * The messages returned are limited to the type specified in the $type
+ * parameter. If there are no messages of the specified type, an empty array
+ * is returned.
*
- * @return
- * An associative array, the key is the message type, the value an array
- * of messages. If the $type parameter is passed, you get only that type,
- * or an empty array if there are no such messages. If $type is not passed,
- * all message types are returned, or an empty array if none exist.
+ * @see drupal_set_message()
+ * @see theme_status_messages()
*/
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
if ($messages = drupal_set_message()) {
@@ -1901,6 +2224,33 @@
}
/**
+ * Returns a URL-safe, base64 encoded string of highly randomized bytes (over the full 8-bit range).
+ *
+ * @param $byte_count
+ * The number of random bytes to fetch and base64 encode.
+ *
+ * @return string
+ * The base64 encoded result will have a length of up to 4 * $byte_count.
+ */
+function drupal_random_key($byte_count = 32) {
+ return drupal_base64_encode(drupal_random_bytes($byte_count));
+}
+
+/**
+ * Returns a URL-safe, base64 encoded version of the supplied string.
+ *
+ * @param $string
+ * The string to convert to base64.
+ *
+ * @return string
+ */
+function drupal_base64_encode($string) {
+ $data = base64_encode($string);
+ // Modify the output so it's safe to use in URLs.
+ return strtr($data, array('+' => '-', '/' => '_', '=' => ''));
+}
+
+/**
* Returns a string of highly randomized bytes (over the full 8-bit range).
*
* This function is better than simply calling mt_rand() or any other built-in
@@ -1913,28 +2263,34 @@
*/
function drupal_random_bytes($count) {
// $random_state does not use drupal_static as it stores random bytes.
- static $random_state, $bytes;
- // Initialize on the first call. The contents of $_SERVER includes a mix of
- // user-specific and system information that varies a little with each page.
- if (!isset($random_state)) {
- $random_state = print_r($_SERVER, TRUE);
- if (function_exists('getmypid')) {
- // Further initialize with the somewhat random PHP process ID.
- $random_state .= getmypid();
- }
- $bytes = '';
- }
- if (strlen($bytes) < $count) {
- // /dev/urandom is available on many *nix systems and is considered the
- // best commonly available pseudo-random source.
- if ($fh = @fopen('/dev/urandom', 'rb')) {
+ static $random_state, $bytes, $has_openssl;
+
+ $missing_bytes = $count - strlen($bytes);
+
+ if ($missing_bytes > 0) {
+ // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
+ // locking on Windows and rendered it unusable.
+ if (!isset($has_openssl)) {
+ $has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes');
+ }
+
+ // openssl_random_pseudo_bytes() will find entropy in a system-dependent
+ // way.
+ if ($has_openssl) {
+ $bytes .= openssl_random_pseudo_bytes($missing_bytes);
+ }
+
+ // Else, read directly from /dev/urandom, which is available on many *nix
+ // systems and is considered cryptographically secure.
+ elseif ($fh = @fopen('/dev/urandom', 'rb')) {
// PHP only performs buffered reads, so in reality it will always read
// at least 4096 bytes. Thus, it costs nothing extra to read and store
// that much so as to speed any additional invocations.
- $bytes .= fread($fh, max(4096, $count));
+ $bytes .= fread($fh, max(4096, $missing_bytes));
fclose($fh);
}
- // If /dev/urandom is not available or returns no bytes, this loop will
+
+ // If we couldn't get enough entropy, this simple hash-based PRNG will
// generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed
// through hash() prior to being rolled into $output, that the two hash()
@@ -1942,9 +2298,23 @@
// the microtime() - is prepended rather than appended. This is to avoid
// directly leaking $random_state via the $output stream, which could
// allow for trivial prediction of further "random" numbers.
- while (strlen($bytes) < $count) {
- $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
- $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+ if (strlen($bytes) < $count) {
+ // Initialize on the first call. The contents of $_SERVER includes a mix of
+ // user-specific and system information that varies a little with each page.
+ if (!isset($random_state)) {
+ $random_state = print_r($_SERVER, TRUE);
+ if (function_exists('getmypid')) {
+ // Further initialize with the somewhat random PHP process ID.
+ $random_state .= getmypid();
+ }
+ $bytes = '';
+ }
+
+ do {
+ $random_state = hash('sha256', microtime() . mt_rand() . $random_state);
+ $bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
+ }
+ while (strlen($bytes) < $count);
}
}
$output = substr($bytes, 0, $count);
@@ -1955,17 +2325,21 @@
/**
* Calculates a base-64 encoded, URL-safe sha-256 hmac.
*
- * @param $data
+ * @param string $data
* String to be validated with the hmac.
- * @param $key
+ * @param string $key
* A secret string key.
*
- * @return
+ * @return string
* A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and
* any = padding characters removed.
*/
function drupal_hmac_base64($data, $key) {
- $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
+ // Casting $data and $key to strings here is necessary to avoid empty string
+ // results of the hash function if they are not scalar values. As this
+ // function is used in security-critical contexts like token validation it is
+ // important that it never returns an empty string.
+ $hmac = base64_encode(hash_hmac('sha256', (string) $data, (string) $key, TRUE));
// Modify the hmac so it's safe to use in URLs.
return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
}
@@ -2066,7 +2440,7 @@
* @return Object - the user object.
*/
function drupal_anonymous_user() {
- $user = new stdClass();
+ $user = variable_get('drupal_anonymous_user_object', new stdClass);
$user->uid = 0;
$user->hostname = ip_address();
$user->roles = array();
@@ -2078,19 +2452,31 @@
/**
* Ensures Drupal is bootstrapped to the specified phase.
*
- * The bootstrap phase is an integer constant identifying a phase of Drupal
- * to load. Each phase adds to the previous one, so invoking a later phase
- * automatically runs the earlier phases as well. To access the Drupal
- * database from a script without loading anything else, include bootstrap.inc
- * and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
- *
- * @param $phase
- * A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
- * @param $new_phase
+ * In order to bootstrap Drupal from another PHP script, you can use this code:
+ * @code
+ * define('DRUPAL_ROOT', '/path/to/drupal');
+ * require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
+ * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+ * @endcode
+ *
+ * @param int $phase
+ * A constant telling which phase to bootstrap to. When you bootstrap to a
+ * particular phase, all earlier phases are run automatically. Possible
+ * values:
+ * - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration.
+ * - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page.
+ * - DRUPAL_BOOTSTRAP_DATABASE: Initializes the database layer.
+ * - DRUPAL_BOOTSTRAP_VARIABLES: Initializes the variable system.
+ * - DRUPAL_BOOTSTRAP_SESSION: Initializes session handling.
+ * - DRUPAL_BOOTSTRAP_PAGE_HEADER: Sets up the page header.
+ * - DRUPAL_BOOTSTRAP_LANGUAGE: Finds out the language of the page.
+ * - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input
+ * data.
+ * @param boolean $new_phase
* A boolean, set to FALSE if calling drupal_bootstrap from inside a
* function called from drupal_bootstrap (recursion).
*
- * @return
+ * @return int
* The most recently completed phase.
*/
function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
@@ -2112,12 +2498,13 @@
// bootstrap state.
static $stored_phase = -1;
- // When not recursing, store the phase name so it's not forgotten while
- // recursing.
- if ($new_phase) {
- $final_phase = $phase;
- }
if (isset($phase)) {
+ // When not recursing, store the phase name so it's not forgotten while
+ // recursing but take care of not going backwards.
+ if ($new_phase && $phase >= $stored_phase) {
+ $final_phase = $phase;
+ }
+
// Call a phase if it has not been called before and is below the requested
// phase.
while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) {
@@ -2185,6 +2572,19 @@
}
/**
+ * Gets a salt useful for hardening against SQL injection.
+ *
+ * @return
+ * A salt based on information in settings.php, not in the database.
+ */
+function drupal_get_hash_salt() {
+ global $drupal_hash_salt, $databases;
+ // If the $drupal_hash_salt variable is empty, a hash of the serialized
+ // database credentials is used as a fallback salt.
+ return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
+}
+
+/**
* Provides custom PHP error handling.
*
* @param $error_level
@@ -2245,6 +2645,10 @@
timer_start('page');
// Initialize the configuration, including variables from settings.php.
drupal_settings_initialize();
+
+ // Sanitize unsafe keys from the request.
+ require_once DRUPAL_ROOT . '/includes/request-sanitizer.inc';
+ DrupalRequestSanitizer::sanitize();
}
/**
@@ -2353,6 +2757,9 @@
// the install or upgrade process.
spl_autoload_register('drupal_autoload_class');
spl_autoload_register('drupal_autoload_interface');
+ if (version_compare(PHP_VERSION, '5.4') >= 0) {
+ spl_autoload_register('drupal_autoload_trait');
+ }
}
/**
@@ -2370,6 +2777,31 @@
// Load bootstrap modules.
require_once DRUPAL_ROOT . '/includes/module.inc';
module_load_all(TRUE);
+
+ // Sanitize the destination parameter (which is often used for redirects) to
+ // prevent open redirect attacks leading to other domains. Sanitize both
+ // $_GET['destination'] and $_REQUEST['destination'] to protect code that
+ // relies on either, but do not sanitize $_POST to avoid interfering with
+ // unrelated form submissions. The sanitization happens here because
+ // url_is_external() requires the variable system to be available.
+ if (isset($_GET['destination']) || isset($_REQUEST['destination'])) {
+ require_once DRUPAL_ROOT . '/includes/common.inc';
+ // If the destination is an external URL, remove it.
+ if (isset($_GET['destination']) && url_is_external($_GET['destination'])) {
+ unset($_GET['destination']);
+ unset($_REQUEST['destination']);
+ }
+ // Use the DrupalRequestSanitizer to ensure that the destination's query
+ // parameters are not dangerous.
+ if (isset($_GET['destination'])) {
+ DrupalRequestSanitizer::cleanDestination();
+ }
+ // 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']) && url_is_external($_REQUEST['destination'])) {
+ unset($_REQUEST['destination']);
+ }
+ }
}
/**
@@ -2392,7 +2824,7 @@
* @see drupal_bootstrap()
*/
function drupal_get_bootstrap_phase() {
- return drupal_bootstrap();
+ return drupal_bootstrap(NULL, FALSE);
}
/**
@@ -2404,7 +2836,6 @@
* HMAC and timestamp.
*/
function drupal_valid_test_ua() {
- global $drupal_hash_salt;
// No reason to reset this.
static $test_prefix;
@@ -2418,7 +2849,7 @@
// We use the salt 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.
- $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
$time_diff = REQUEST_TIME - $time;
// Since we are making a local request a 5 second time window is allowed,
// and the HMAC must match.
@@ -2428,21 +2859,21 @@
}
}
- return FALSE;
+ $test_prefix = FALSE;
+ return $test_prefix;
}
/**
* Generates a user agent string with a HMAC and timestamp for simpletest.
*/
function drupal_generate_test_ua($prefix) {
- global $drupal_hash_salt;
static $key;
if (!isset($key)) {
// We use the salt 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.
- $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
}
// Generate a moderately secure HMAC based on the database credentials.
$salt = uniqid('', TRUE);
@@ -2483,7 +2914,7 @@
$fast_paths = variable_get('404_fast_paths', FALSE);
if ($fast_paths && preg_match($fast_paths, $_GET['q'])) {
drupal_add_http_header('Status', '404 Not Found');
- $fast_404_html = variable_get('404_fast_html', '404 Not Found Not Found
The requested URL "@path" was not found on this server.
');
+ $fast_404_html = variable_get('404_fast_html', '404 Not Found Not Found
The requested URL "@path" was not found on this server.
');
// Replace @path in the variable with the page path.
print strtr($fast_404_html, array('@path' => check_plain(request_uri())));
exit;
@@ -2507,7 +2938,7 @@
*
* This would include implementations of hook_install(), which could run
* during the Drupal installation phase, and might also be run during
- * non-installation time, such as while installing the module from the the
+ * non-installation time, such as while installing the module from the
* module administration page.
*
* Example usage:
@@ -2576,6 +3007,9 @@
/**
* Returns TRUE if there is more than one language enabled.
+ *
+ * @return
+ * TRUE if more than one language is enabled.
*/
function drupal_multilingual() {
// The "language_count" variable stores the number of enabled languages to
@@ -2586,6 +3020,10 @@
/**
* Returns an array of the available language types.
+ *
+ * @return
+ * An array of all language types where the keys of each are the language type
+ * name and its value is its configurability (TRUE/FALSE).
*/
function language_types() {
return array_keys(variable_get('language_types', drupal_language_types()));
@@ -2642,10 +3080,14 @@
}
/**
- * Returns the default language used on the site
+ * Returns the default language, as an object, or one of its properties.
*
* @param $property
- * Optional property of the language object to return
+ * (optional) The property of the language object to return.
+ *
+ * @return
+ * Either the language object for the default language used on the site,
+ * or the property of that object named in the $property parameter.
*/
function language_default($property = NULL) {
$language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
@@ -2676,7 +3118,7 @@
return $path;
}
- if (isset($_GET['q'])) {
+ if (isset($_GET['q']) && is_string($_GET['q'])) {
// This is a request with a ?q=foo/bar query string. $_GET['q'] is
// overwritten in drupal_path_initialize(), but request_path() is called
// very early in the bootstrap process, so the original value is saved in
@@ -2797,8 +3239,15 @@
// Eliminate all trusted IPs.
$untrusted = array_diff($forwarded, $reverse_proxy_addresses);
- // The right-most IP is the most specific we can trust.
- $ip_address = array_pop($untrusted);
+ if (!empty($untrusted)) {
+ // The right-most IP is the most specific we can trust.
+ $ip_address = array_pop($untrusted);
+ }
+ else {
+ // All IP addresses in the forwarded array are configured proxy IPs
+ // (and thus trusted). We take the leftmost IP.
+ $ip_address = array_shift($forwarded);
+ }
}
}
}
@@ -2807,7 +3256,7 @@
}
/**
- * @ingroup schemaapi
+ * @addtogroup schemaapi
* @{
*/
@@ -2815,7 +3264,9 @@
* Gets the schema definition of a table, or the whole database schema.
*
* The returned schema will include any modifications made by any
- * module that implements hook_schema_alter().
+ * module that implements hook_schema_alter(). To get the schema without
+ * modifications, use drupal_get_schema_unprocessed().
+ *
*
* @param $table
* The name of the table. If not given, the schema of all tables is returned.
@@ -2929,12 +3380,12 @@
}
/**
- * @} End of "ingroup schemaapi".
+ * @} End of "addtogroup schemaapi".
*/
/**
- * @ingroup registry
+ * @addtogroup registry
* @{
*/
@@ -2971,6 +3422,22 @@
}
/**
+ * Confirms that a trait is available.
+ *
+ * This function is rarely called directly. Instead, it is registered as an
+ * spl_autoload() handler, and PHP calls it for us when necessary.
+ *
+ * @param string $trait
+ * The name of the trait to check or load.
+ *
+ * @return bool
+ * TRUE if the trait is currently available, FALSE otherwise.
+ */
+function drupal_autoload_trait($trait) {
+ return _registry_check_code('trait', $trait);
+}
+
+/**
* Checks for a resource in the registry.
*
* @param $type
@@ -2988,7 +3455,7 @@
function _registry_check_code($type, $name = NULL) {
static $lookup_cache, $cache_update_needed;
- if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name)) {
+ if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name) || $type == 'trait' && trait_exists($name)) {
return TRUE;
}
@@ -3021,7 +3488,7 @@
$cache_key = $type[0] . $name;
if (isset($lookup_cache[$cache_key])) {
if ($lookup_cache[$cache_key]) {
- require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
+ include_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
}
return (bool) $lookup_cache[$cache_key];
}
@@ -3029,10 +3496,13 @@
// This function may get called when the default database is not active, but
// there is no reason we'd ever want to not use the default database for
// this query.
- $file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array(
- ':name' => $name,
- ':type' => $type,
- ))
+ $file = Database::getConnection('default', 'default')
+ ->select('registry', 'r', array('target' => 'default'))
+ ->fields('r', array('filename'))
+ // Use LIKE here to make the query case-insensitive.
+ ->condition('r.name', db_like($name), 'LIKE')
+ ->condition('r.type', $type)
+ ->execute()
->fetchField();
// Flag that we've run a lookup query and need to update the cache.
@@ -3043,7 +3513,7 @@
$lookup_cache[$cache_key] = $file;
if ($file) {
- require_once DRUPAL_ROOT . '/' . $file;
+ include_once DRUPAL_ROOT . '/' . $file;
return TRUE;
}
else {
@@ -3069,15 +3539,34 @@
* to be called, because it is already known that the list of files in the
* {system} table matches those in the file system.
*
+ * @return
+ * TRUE if the registry was rebuilt, FALSE if another thread was rebuilding
+ * in parallel and the current thread just waited for completion.
+ *
* @see registry_rebuild()
*/
function registry_update() {
+ // install_system_module() calls module_enable() which calls into this
+ // function during initial system installation, so the lock system is neither
+ // loaded nor does its storage exist yet.
+ $in_installer = drupal_installation_attempted();
+ if (!$in_installer && !lock_acquire(__FUNCTION__)) {
+ // Another request got the lock, wait for it to finish.
+ lock_wait(__FUNCTION__);
+ return FALSE;
+ }
+
require_once DRUPAL_ROOT . '/includes/registry.inc';
_registry_update();
+
+ if (!$in_installer) {
+ lock_release(__FUNCTION__);
+ }
+ return TRUE;
}
/**
- * @} End of "ingroup registry".
+ * @} End of "addtogroup registry".
*/
/**
@@ -3161,8 +3650,8 @@
* However, the above line of code does not work, because PHP only allows static
* variables to be initializied by literal values, and does not allow static
* variables to be assigned to references.
- * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static
- * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references
+ * - http://php.net/manual/language.variables.scope.php#language.variables.scope.static
+ * - http://php.net/manual/language.variables.scope.php#language.variables.scope.references
* The example below shows the syntax needed to work around both limitations.
* For benchmarks and more information, see http://drupal.org/node/619666.
*
@@ -3187,11 +3676,9 @@
* @param $default_value
* Optional default value.
* @param $reset
- * TRUE to reset a specific named variable, or all variables if $name is NULL.
- * Resetting every variable should only be used, for example, for running
- * unit tests with a clean environment. Should be used only though via
- * function drupal_static_reset() and the return value should not be used in
- * this case.
+ * TRUE to reset one or all variables(s). This parameter is only used
+ * internally and should not be passed in; use drupal_static_reset() instead.
+ * (This function's return value should not be used when TRUE is passed in.)
*
* @return
* Returns a variable by reference.
@@ -3236,6 +3723,8 @@
*
* @param $name
* Name of the static variable to reset. Omit to reset all variables.
+ * Resetting all variables should only be used, for example, for running unit
+ * tests with a clean environment.
*/
function drupal_static_reset($name = NULL) {
drupal_static($name, NULL, TRUE);
@@ -3309,8 +3798,12 @@
chdir(DRUPAL_ROOT);
try {
- while (list($key, $callback) = each($callbacks)) {
+ // Manually iterate over the array instead of using a foreach loop.
+ // A foreach operates on a copy of the array, so any shutdown functions that
+ // were added from other shutdown functions would never be called.
+ while ($callback = current($callbacks)) {
call_user_func_array($callback['callback'], $callback['arguments']);
+ next($callbacks);
}
}
catch (Exception $exception) {
@@ -3322,3 +3815,63 @@
}
}
}
+
+/**
+ * Compares the memory required for an operation to the available memory.
+ *
+ * @param $required
+ * The memory required for the operation, expressed as a number of bytes with
+ * optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8bytes,
+ * 9mbytes).
+ * @param $memory_limit
+ * (optional) The memory limit for the operation, expressed as a number of
+ * bytes with optional SI or IEC binary unit prefix (e.g. 2, 3K, 5MB, 10G,
+ * 6GiB, 8bytes, 9mbytes). If no value is passed, the current PHP
+ * memory_limit will be used. Defaults to NULL.
+ *
+ * @return
+ * TRUE if there is sufficient memory to allow the operation, or FALSE
+ * otherwise.
+ */
+function drupal_check_memory_limit($required, $memory_limit = NULL) {
+ if (!isset($memory_limit)) {
+ $memory_limit = ini_get('memory_limit');
+ }
+
+ // There is sufficient memory if:
+ // - No memory limit is set.
+ // - The memory limit is set to unlimited (-1).
+ // - The memory limit is greater than the memory required for the operation.
+ return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
+}
+
+/**
+ * Invalidates a PHP file from any active opcode caches.
+ *
+ * If the opcode cache does not support the invalidation of individual files,
+ * the entire cache will be flushed.
+ *
+ * @param string $filepath
+ * The absolute path of the PHP file to invalidate.
+ */
+function drupal_clear_opcode_cache($filepath) {
+ if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
+ // Below PHP 5.3, clearstatcache does not accept any function parameters.
+ clearstatcache();
+ }
+ else {
+ clearstatcache(TRUE, $filepath);
+ }
+
+ // Zend OPcache.
+ if (function_exists('opcache_invalidate')) {
+ opcache_invalidate($filepath, TRUE);
+ }
+ // APC.
+ if (function_exists('apc_delete_file')) {
+ // apc_delete_file() throws a PHP warning in case the specified file was
+ // not compiled yet.
+ // @see http://php.net/apc-delete-file
+ @apc_delete_file($filepath);
+ }
+}
diff -Naur drupal-7.13/includes/cache.inc drupal-7.66/includes/cache.inc
--- drupal-7.13/includes/cache.inc 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/includes/cache.inc 2019-04-17 22:20:46.000000000 +0200
@@ -14,6 +14,7 @@
*
* @param $bin
* The cache bin for which the cache object should be returned.
+ *
* @return DrupalCacheInterface
* The cache object associated with the specified bin.
*
@@ -80,43 +81,15 @@
* same name. Other implementations might want to store several bins in data
* structures that get flushed together. While it is not a problem for most
* cache bins if the entries in them are flushed before their expire time, some
- * might break functionality or are extremely expensive to recalculate. These
- * will be marked with a (*). The other bins expired automatically by core.
- * Contributed modules can add additional bins and get them expired
- * automatically by implementing hook_flush_caches().
- *
- * - cache: Generic cache storage bin (used for variables, theme registry,
- * locale date, list of simpletest tests etc).
- *
- * - cache_block: Stores the content of various blocks.
- *
- * - cache field: Stores the field data belonging to a given object.
- *
- * - cache_filter: Stores filtered pieces of content.
- *
- * - cache_form(*): Stores multistep forms. Flushing this bin means that some
- * forms displayed to users lose their state and the data already submitted
- * to them.
- *
- * - cache_menu: Stores the structure of visible navigation menus per page.
- *
- * - cache_page: Stores generated pages for anonymous users. It is flushed
- * very often, whenever a page changes, at least for every ode and comment
- * submission. This is the only bin affected by the page cache setting on
- * the administrator panel.
- *
- * - cache path: Stores the system paths that have an alias.
- *
- * - cache update(*): Stores available releases. The update server (for
- * example, drupal.org) needs to produce the relevant XML for every project
- * installed on the current site. As this is different for (almost) every
- * site, it's very expensive to recalculate for the update server.
+ * might break functionality or are extremely expensive to recalculate. The
+ * other bins are expired automatically by core. Contributed modules can add
+ * additional bins and get them expired automatically by implementing
+ * hook_flush_caches().
*
* The reasons for having several bins are as follows:
- *
- * - smaller bins mean smaller database tables and allow for faster selects and
- * inserts
- * - we try to put fast changing cache items and rather static ones into
+ * - Smaller bins mean smaller database tables and allow for faster selects and
+ * inserts.
+ * - We try to put fast changing cache items and rather static ones into
* different bins. The effect is that only the fast changing bins will need a
* lot of writes to disk. The more static bins will also be better cacheable
* with MySQL's query cache.
@@ -125,15 +98,36 @@
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
- * serialized before insertion.
- * Strings will be stored as plain text and not serialized.
+ * serialized before insertion. Strings will be stored as plain text and are
+ * not serialized. Some storage engines only allow objects up to a maximum of
+ * 1MB in size to be stored by default. When caching large arrays or similar,
+ * take care to ensure $data does not exceed this size.
* @param $bin
- * The cache bin to store the data in. Valid core values are 'cache_block',
- * 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
- * 'cache_menu', 'cache_page', 'cache_update' or 'cache' for the default
- * cache.
+ * (optional) The cache bin to store the data in. Valid core values are:
+ * - cache: (default) Generic cache storage bin (used for theme registry,
+ * locale date, list of simpletest tests, etc.).
+ * - cache_block: Stores the content of various blocks.
+ * - cache_bootstrap: Stores the class registry, the system list of modules,
+ * the list of which modules implement which hooks, and the Drupal variable
+ * list.
+ * - cache_field: Stores the field data belonging to a given object.
+ * - cache_filter: Stores filtered pieces of content.
+ * - cache_form: Stores multistep forms. Flushing this bin means that some
+ * forms displayed to users lose their state and the data already submitted
+ * to them. This bin should not be flushed before its expired time.
+ * - cache_menu: Stores the structure of visible navigation menus per page.
+ * - cache_page: Stores generated pages for anonymous users. It is flushed
+ * very often, whenever a page changes, at least for every node and comment
+ * submission. This is the only bin affected by the page cache setting on
+ * the administrator panel.
+ * - cache_path: Stores the system paths that have an alias.
* @param $expire
- * One of the following values:
+ * (optional) Controls the maximum lifetime of this cache entry. Note that
+ * caches might be subject to clearing at any time, so this setting does not
+ * guarantee a minimum lifetime. With this in mind, the cache should not be
+ * used for data that must be kept during a cache clear, like sessions.
+ *
+ * Use one of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
* explicitly told to using cache_clear_all() with a cache ID.
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
@@ -141,6 +135,7 @@
* - A Unix timestamp: Indicates that the item should be kept at least until
* the given time, after which it behaves like CACHE_TEMPORARY.
*
+ * @see _update_cache_set()
* @see cache_get()
*/
function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
@@ -150,18 +145,20 @@
/**
* Expires data from the cache.
*
- * If called without arguments, expirable entries will be cleared from the
- * cache_page and cache_block bins.
+ * If called with the arguments $cid and $bin set to NULL or omitted, then
+ * expirable entries will be cleared from the cache_page and cache_block bins,
+ * and the $wildcard argument is ignored.
*
* @param $cid
- * If set, the cache ID to delete. Otherwise, all cache entries that can
- * expire are deleted.
+ * If set, the cache ID or an array of cache IDs. Otherwise, all cache entries
+ * that can expire are deleted. The $wildcard argument will be ignored if set
+ * to NULL.
* @param $bin
* If set, the cache bin to delete from. Mandatory argument if $cid is set.
* @param $wildcard
- * If TRUE, cache IDs starting with $cid are deleted in addition to the
- * exact cache ID specified by $cid. If $wildcard is TRUE and $cid is '*',
- * the entire cache bin is emptied.
+ * If TRUE, the $cid argument must contain a string value and cache IDs
+ * starting with $cid are deleted in addition to the exact cache ID specified
+ * by $cid. If $wildcard is TRUE and $cid is '*', the entire cache is emptied.
*/
function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
if (!isset($cid) && !isset($bin)) {
@@ -230,13 +227,6 @@
* @see DrupalDatabaseCache
*/
interface DrupalCacheInterface {
- /**
- * Constructs a new cache interface.
- *
- * @param $bin
- * The cache bin for which the object is created.
- */
- function __construct($bin);
/**
* Returns data from the persistent cache.
@@ -272,10 +262,17 @@
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
- * serialized before insertion.
- * Strings will be stored as plain text and not serialized.
+ * serialized before insertion. Strings will be stored as plain text and not
+ * serialized. Some storage engines only allow objects up to a maximum of
+ * 1MB in size to be stored by default. When caching large arrays or
+ * similar, take care to ensure $data does not exceed this size.
* @param $expire
- * One of the following values:
+ * (optional) Controls the maximum lifetime of this cache entry. Note that
+ * caches might be subject to clearing at any time, so this setting does not
+ * guarantee a minimum lifetime. With this in mind, the cache should not be
+ * used for data that must be kept during a cache clear, like sessions.
+ *
+ * Use one of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
* explicitly told to using cache_clear_all() with a cache ID.
* - CACHE_TEMPORARY: Indicates that the item should be removed at the next
@@ -293,12 +290,14 @@
* cache_page and cache_block bins.
*
* @param $cid
- * If set, the cache ID to delete. Otherwise, all cache entries that can
- * expire are deleted.
+ * If set, the cache ID or an array of cache IDs. Otherwise, all cache
+ * entries that can expire are deleted. The $wildcard argument will be
+ * ignored if set to NULL.
* @param $wildcard
- * If set to TRUE, the $cid is treated as a substring
- * to match rather than a complete ID. The match is a right hand
- * match. If '*' is given as $cid, the bin $bin will be emptied.
+ * If TRUE, the $cid argument must contain a string value and cache IDs
+ * starting with $cid are deleted in addition to the exact cache ID
+ * specified by $cid. If $wildcard is TRUE and $cid is '*', the entire
+ * cache is emptied.
*/
function clear($cid = NULL, $wildcard = FALSE);
@@ -324,7 +323,10 @@
protected $bin;
/**
- * Constructs a new DrupalDatabaseCache object.
+ * Constructs a DrupalDatabaseCache object.
+ *
+ * @param $bin
+ * The cache bin for which the object is created.
*/
function __construct($bin) {
$this->bin = $bin;
@@ -379,11 +381,31 @@
* The bin being requested.
*/
protected function garbageCollection() {
- global $user;
+ $cache_lifetime = variable_get('cache_lifetime', 0);
+
+ // Clean-up the per-user cache expiration session data, so that the session
+ // handler can properly clean-up the session data for anonymous users.
+ if (isset($_SESSION['cache_expiration'])) {
+ $expire = REQUEST_TIME - $cache_lifetime;
+ foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
+ if ($timestamp < $expire) {
+ unset($_SESSION['cache_expiration'][$bin]);
+ }
+ }
+ if (!$_SESSION['cache_expiration']) {
+ unset($_SESSION['cache_expiration']);
+ }
+ }
- // Garbage collection necessary when enforcing a minimum cache lifetime.
+ // Garbage collection of temporary items is only necessary when enforcing
+ // a minimum cache lifetime.
+ if (!$cache_lifetime) {
+ return;
+ }
+ // When cache lifetime is in force, avoid running garbage collection too
+ // often since this will remove temporary cache items indiscriminately.
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
- if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
+ if ($cache_flush && ($cache_flush + $cache_lifetime <= REQUEST_TIME)) {
// Reset the variable immediately to prevent a meltdown in heavy load situations.
variable_set('cache_flush_' . $this->bin, 0);
// Time to flush old cache data
@@ -413,17 +435,16 @@
if (!isset($cache->data)) {
return FALSE;
}
- // If enforcing a minimum cache lifetime, validate that the data is
- // currently valid for this user before we return it by making sure the cache
- // entry was created before the timestamp in the current session's cache
- // timer. The cache variable is loaded into the $user object by _drupal_session_read()
- // in session.inc. If the data is permanent or we're not enforcing a minimum
- // cache lifetime always return the cached data.
- if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
- // This cache data is too old and thus not valid for us, ignore it.
+ // If the cached data is temporary and subject to a per-user minimum
+ // lifetime, compare the cache entry timestamp with the user session
+ // cache_expiration timestamp. If the cache entry is too old, ignore it.
+ if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) {
+ // Ignore cache data that is too old and thus not valid for this user.
return FALSE;
}
+ // If the data is permanent or not subject to a minimum cache lifetime,
+ // unserialize and return the cached data.
if ($cache->serialized) {
$cache->data = unserialize($cache->data);
}
@@ -468,11 +489,10 @@
if (empty($cid)) {
if (variable_get('cache_lifetime', 0)) {
- // We store the time in the current user's $user->cache variable which
- // will be saved into the sessions bin by _drupal_session_write(). We then
- // simulate that the cache was flushed for this user by not returning
- // cached data that was cached before the timestamp.
- $user->cache = REQUEST_TIME;
+ // We store the time in the current user's session. We then simulate
+ // that the cache was flushed for this user by not returning cached
+ // data that was cached before the timestamp.
+ $_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush == 0) {
@@ -500,7 +520,16 @@
else {
if ($wildcard) {
if ($cid == '*') {
- db_truncate($this->bin)->execute();
+ // Check if $this->bin is a cache table before truncating. Other
+ // cache_clear_all() operations throw a PDO error in this situation,
+ // so we don't need to verify them first. This ensures that non-cache
+ // tables cannot be truncated accidentally.
+ if ($this->isValidBin()) {
+ db_truncate($this->bin)->execute();
+ }
+ else {
+ throw new Exception(t('Invalid or missing cache bin specified: %bin', array('%bin' => $this->bin)));
+ }
}
else {
db_delete($this->bin)
@@ -537,4 +566,25 @@
->fetchField();
return empty($result);
}
+
+ /**
+ * Checks if $this->bin represents a valid cache table.
+ *
+ * This check is required to ensure that non-cache tables are not truncated
+ * accidentally when calling cache_clear_all().
+ *
+ * @return boolean
+ */
+ function isValidBin() {
+ if ($this->bin == 'cache' || substr($this->bin, 0, 6) == 'cache_') {
+ // Skip schema check for bins with standard table names.
+ return TRUE;
+ }
+ // These fields are required for any cache table.
+ $fields = array('cid', 'data', 'expire', 'created', 'serialized');
+ // Load the table schema.
+ $schema = drupal_get_schema($this->bin);
+ // Confirm that all fields are present.
+ return isset($schema['fields']) && !array_diff($fields, array_keys($schema['fields']));
+ }
}
diff -Naur drupal-7.13/includes/common.inc drupal-7.66/includes/common.inc
--- drupal-7.13/includes/common.inc 2012-05-03 00:01:31.000000000 +0200
+++ drupal-7.66/includes/common.inc 2019-04-17 22:20:46.000000000 +0200
@@ -92,14 +92,20 @@
define('HTTP_REQUEST_TIMEOUT', -1);
/**
- * Constants defining cache granularity for blocks and renderable arrays.
+ * @defgroup block_caching Block Caching
+ * @{
+ * Constants that define each block's caching state.
*
- * Modules specify the caching patterns for their blocks using binary
- * combinations of these constants in their hook_block_info():
- * $block[delta]['cache'] = DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE;
- * DRUPAL_CACHE_PER_ROLE is used as a default when no caching pattern is
- * specified. Use DRUPAL_CACHE_CUSTOM to disable standard block cache and
- * implement
+ * Modules specify how their blocks can be cached in their hook_block_info()
+ * implementations. Caching can be turned off (DRUPAL_NO_CACHE), managed by the
+ * module declaring the block (DRUPAL_CACHE_CUSTOM), or managed by the core
+ * Block module. If the Block module is managing the cache, you can specify that
+ * the block is the same for every page and user (DRUPAL_CACHE_GLOBAL), or that
+ * it can change depending on the page (DRUPAL_CACHE_PER_PAGE) or by user
+ * (DRUPAL_CACHE_PER_ROLE or DRUPAL_CACHE_PER_USER). Page and user settings can
+ * be combined with a bitwise-binary or operator; for example,
+ * DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE means that the block can change
+ * depending on the user role or page it is on.
*
* The block cache is cleared in cache_clear_all(), and uses the same clearing
* policy than page cache (node, comment, user, taxonomy added or updated...).
@@ -123,9 +129,8 @@
/**
* The block is handling its own caching in its hook_block_view().
*
- * From the perspective of the block cache system, this is equivalent to
- * DRUPAL_NO_CACHE. Useful when time based expiration is needed or a site uses
- * a node access which invalidates standard block cache.
+ * This setting is useful when time based expiration is needed or a site uses a
+ * node access which invalidates standard block cache.
*/
define('DRUPAL_CACHE_CUSTOM', -2);
@@ -156,6 +161,10 @@
define('DRUPAL_CACHE_GLOBAL', 0x0008);
/**
+ * @} End of "defgroup block_caching".
+ */
+
+/**
* Adds content to a specified region.
*
* @param $region
@@ -199,7 +208,7 @@
}
/**
- * Gets the name of the currently active install profile.
+ * Gets the name of the currently active installation profile.
*
* When this function is called during Drupal's initial installation process,
* the name of the profile that's about to be installed is stored in the global
@@ -208,7 +217,7 @@
* variable_get() to determine what one is active.
*
* @return $profile
- * The name of the install profile.
+ * The name of the installation profile.
*/
function drupal_get_profile() {
global $install_state;
@@ -272,7 +281,7 @@
/**
* Adds output to the HEAD tag of the HTML page.
*
- * This function can be called as long the headers aren't sent. Pass no
+ * This function can be called as long as the headers aren't sent. Pass no
* arguments (or NULL for both) to retrieve the currently stored elements.
*
* @param $data
@@ -443,13 +452,13 @@
* The query string to split.
*
* @return
- * An array of url decoded couples $param_name => $value.
+ * An array of URL decoded couples $param_name => $value.
*/
function drupal_get_query_array($query) {
$result = array();
if (!empty($query)) {
foreach (explode('&', $query) as $param) {
- $param = explode('=', $param);
+ $param = explode('=', $param, 2);
$result[$param[0]] = isset($param[1]) ? rawurldecode($param[1]) : '';
}
}
@@ -478,7 +487,7 @@
$params = array();
foreach ($query as $key => $value) {
- $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
+ $key = $parent ? $parent . rawurlencode('[' . $key . ']') : rawurlencode($key);
// Recurse into children.
if (is_array($value)) {
@@ -505,6 +514,12 @@
* previous request, that destination is returned. As such, a destination can
* persist across multiple pages.
*
+ * @return
+ * An associative array containing the key:
+ * - destination: The path provided via the destination query string or, if
+ * not available, the current path.
+ *
+ * @see current_path()
* @see drupal_goto()
*/
function drupal_get_destination() {
@@ -529,37 +544,32 @@
}
/**
- * Parses a system URL string into an associative array suitable for url().
+ * Parses a URL string into its path, query, and fragment components.
*
- * This function should only be used for URLs that have been generated by the
- * system, resp. url(). It should not be used for URLs that come from external
- * sources, or URLs that link to external resources.
+ * This function splits both internal paths like @code node?b=c#d @endcode and
+ * external URLs like @code https://example.com/a?b=c#d @endcode into their
+ * component parts. See
+ * @link http://tools.ietf.org/html/rfc3986#section-3 RFC 3986 @endlink for an
+ * explanation of what the component parts are.
*
- * The returned array contains a 'path' that may be passed separately to url().
- * For example:
- * @code
- * $options = drupal_parse_url($_GET['destination']);
- * $my_url = url($options['path'], $options);
- * $my_link = l('Example link', $options['path'], $options);
- * @endcode
+ * Note that, unlike the RFC, when passed an external URL, this function
+ * groups the scheme, authority, and path together into the path component.
*
- * This is required, because url() does not support relative URLs containing a
- * query string or fragment in its $path argument. Instead, any query string
- * needs to be parsed into an associative query parameter array in
- * $options['query'] and the fragment into $options['fragment'].
+ * @param string $url
+ * The internal path or external URL string to parse.
*
- * @param $url
- * The URL string to parse, f.e. $_GET['destination'].
- *
- * @return
- * An associative array containing the keys:
- * - 'path': The path of the URL. If the given $url is external, this includes
- * the scheme and host.
- * - 'query': An array of query parameters of $url, if existent.
- * - 'fragment': The fragment of $url, if existent.
+ * @return array
+ * An associative array containing:
+ * - path: The path component of $url. If $url is an external URL, this
+ * includes the scheme, authority, and path.
+ * - query: An array of query parameters from $url, if they exist.
+ * - fragment: The fragment component from $url, if it exists.
*
- * @see url()
* @see drupal_goto()
+ * @see l()
+ * @see url()
+ * @see http://tools.ietf.org/html/rfc3986
+ *
* @ingroup php_wrappers
*/
function drupal_parse_url($url) {
@@ -601,8 +611,9 @@
}
// 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.
- if (isset($options['query']['q'])) {
+ // 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']);
}
@@ -626,7 +637,7 @@
}
/**
- * Sends the user to a different Drupal page.
+ * Sends the user to a different page.
*
* This issues an on-site HTTP redirect. The function makes sure the redirected
* URL is formatted correctly.
@@ -647,20 +658,23 @@
* callback.
*
* @param $path
- * A Drupal path or a full URL.
+ * (optional) A Drupal path or a full URL, which will be passed to url() to
+ * compute the redirect for the URL.
* @param $options
- * An associative array of additional URL options to pass to url().
+ * (optional) An associative array of additional URL options to pass to url().
* @param $http_response_code
- * Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
- * - 301 Moved Permanently (the recommended value for most redirects)
- * - 302 Found (default in Drupal and PHP, sometimes used for spamming search
- * engines)
- * - 303 See Other
- * - 304 Not Modified
- * - 305 Use Proxy
- * - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
- * Note: Other values are defined by RFC 2616, but are rarely used and poorly
- * supported.
+ * (optional) The HTTP status code to use for the redirection, defaults to
+ * 302. The valid values for 3xx redirection status codes are defined in
+ * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3 RFC 2616 @endlink
+ * and the
+ * @link http://tools.ietf.org/html/draft-reschke-http-status-308-07 draft for the new HTTP status codes: @endlink
+ * - 301: Moved Permanently (the recommended value for most redirects).
+ * - 302: Found (default in Drupal and PHP, sometimes used for spamming search
+ * engines).
+ * - 303: See Other.
+ * - 304: Not Modified.
+ * - 305: Use Proxy.
+ * - 307: Temporary Redirect.
*
* @see drupal_get_destination()
* @see url()
@@ -675,6 +689,13 @@
$options['fragment'] = $destination['fragment'];
}
+ // In some cases modules call drupal_goto(current_path()). We need to ensure
+ // that such a redirect is not to an external URL.
+ if ($path === current_path() && empty($options['external']) && url_is_external($path)) {
+ // Force url() to generate a non-external URL.
+ $options['external'] = FALSE;
+ }
+
drupal_alter('drupal_goto', $path, $options, $http_response_code);
// The 'Location' HTTP header must be absolute.
@@ -740,7 +761,8 @@
* - headers: An array containing request headers to send as name/value pairs.
* - method: A string containing the request method. Defaults to 'GET'.
* - data: A string containing the request body, formatted as
- * 'param=value¶m=value&...'. Defaults to NULL.
+ * 'param=value¶m=value&...'; to generate this, use http_build_query().
+ * Defaults to NULL.
* - max_redirects: An integer representing how many times a redirect
* may be followed. Defaults to 3.
* - timeout: A float representing the maximum number of seconds the function
@@ -765,8 +787,17 @@
* HTTP header names are case-insensitive (RFC 2616, section 4.2), so for
* easy access the array keys are returned in lower case.
* - data: A string containing the response body that was received.
+ *
+ * @see http_build_query()
*/
function drupal_http_request($url, array $options = array()) {
+ // Allow an alternate HTTP client library to replace Drupal's default
+ // implementation.
+ $override_function = variable_get('drupal_http_request_function', FALSE);
+ if (!empty($override_function) && function_exists($override_function)) {
+ return $override_function($url, $options);
+ }
+
$result = new stdClass();
// Parse the URL and make sure we can handle the schema.
@@ -795,10 +826,53 @@
'timeout' => 30.0,
'context' => NULL,
);
+
+ // Merge the default headers.
+ $options['headers'] += array(
+ 'User-Agent' => 'Drupal (+http://drupal.org/)',
+ );
+
// stream_socket_client() requires timeout to be a float.
$options['timeout'] = (float) $options['timeout'];
+ // Use a proxy if one is defined and the host is not on the excluded list.
+ $proxy_server = variable_get('proxy_server', '');
+ if ($proxy_server && _drupal_http_use_proxy($uri['host'])) {
+ // Set the scheme so we open a socket to the proxy server.
+ $uri['scheme'] = 'proxy';
+ // Set the path to be the full URL.
+ $uri['path'] = $url;
+ // Since the URL is passed as the path, we won't use the parsed query.
+ unset($uri['query']);
+
+ // Add in username and password to Proxy-Authorization header if needed.
+ if ($proxy_username = variable_get('proxy_username', '')) {
+ $proxy_password = variable_get('proxy_password', '');
+ $options['headers']['Proxy-Authorization'] = 'Basic ' . base64_encode($proxy_username . (!empty($proxy_password) ? ":" . $proxy_password : ''));
+ }
+ // Some proxies reject requests with any User-Agent headers, while others
+ // require a specific one.
+ $proxy_user_agent = variable_get('proxy_user_agent', '');
+ // The default value matches neither condition.
+ if ($proxy_user_agent === NULL) {
+ unset($options['headers']['User-Agent']);
+ }
+ elseif ($proxy_user_agent) {
+ $options['headers']['User-Agent'] = $proxy_user_agent;
+ }
+ }
+
switch ($uri['scheme']) {
+ case 'proxy':
+ // Make the socket connection to a proxy server.
+ $socket = 'tcp://' . $proxy_server . ':' . variable_get('proxy_port', 8080);
+ // The Host header still needs to match the real request.
+ if (!isset($options['headers']['Host'])) {
+ $options['headers']['Host'] = $uri['host'];
+ $options['headers']['Host'] .= isset($uri['port']) && $uri['port'] != 80 ? ':' . $uri['port'] : '';
+ }
+ break;
+
case 'http':
case 'feed':
$port = isset($uri['port']) ? $uri['port'] : 80;
@@ -806,14 +880,20 @@
// RFC 2616: "non-standard ports MUST, default ports MAY be included".
// We don't add the standard port to prevent from breaking rewrite rules
// checking the host that do not take into account the port number.
- $options['headers']['Host'] = $uri['host'] . ($port != 80 ? ':' . $port : '');
+ if (!isset($options['headers']['Host'])) {
+ $options['headers']['Host'] = $uri['host'] . ($port != 80 ? ':' . $port : '');
+ }
break;
+
case 'https':
// Note: Only works when PHP is compiled with OpenSSL support.
$port = isset($uri['port']) ? $uri['port'] : 443;
$socket = 'ssl://' . $uri['host'] . ':' . $port;
- $options['headers']['Host'] = $uri['host'] . ($port != 443 ? ':' . $port : '');
+ if (!isset($options['headers']['Host'])) {
+ $options['headers']['Host'] = $uri['host'] . ($port != 443 ? ':' . $port : '');
+ }
break;
+
default:
$result->error = 'invalid schema ' . $uri['scheme'];
$result->code = -1003;
@@ -850,11 +930,6 @@
$path .= '?' . $uri['query'];
}
- // Merge the default headers.
- $options['headers'] += array(
- 'User-Agent' => 'Drupal (+http://drupal.org/)',
- );
-
// Only add Content-Length if we actually have any content or if it is a POST
// or PUT request. Some non-standard servers get confused by Content-Length in
// at least HEAD/GET requests, and Squid always requires Content-Length in
@@ -866,7 +941,7 @@
// If the server URL has a user then attempt to use basic authentication.
if (isset($uri['user'])) {
- $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
+ $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ':'));
}
// If the database prefix is being used by SimpleTest to run the tests in a copied
@@ -927,9 +1002,10 @@
$response = preg_split("/\r\n|\n|\r/", $response);
// Parse the response status line.
- list($protocol, $code, $status_message) = explode(' ', trim(array_shift($response)), 3);
- $result->protocol = $protocol;
- $result->status_message = $status_message;
+ $response_status_array = _drupal_parse_response_status(trim(array_shift($response)));
+ $result->protocol = $response_status_array['http_version'];
+ $result->status_message = $response_status_array['reason_phrase'];
+ $code = $response_status_array['response_code'];
$result->headers = array();
@@ -998,6 +1074,12 @@
switch ($code) {
case 200: // OK
+ case 201: // Created
+ case 202: // Accepted
+ case 203: // Non-Authoritative Information
+ case 204: // No Content
+ case 205: // Reset Content
+ case 206: // Partial Content
case 304: // Not modified
break;
case 301: // Moved permanently
@@ -1012,6 +1094,11 @@
elseif ($options['max_redirects']) {
// Redirect to the new location.
$options['max_redirects']--;
+
+ // We need to unset the 'Host' header
+ // as we are redirecting to a new location.
+ unset($options['headers']['Host']);
+
$result = drupal_http_request($location, $options);
$result->redirect_code = $code;
}
@@ -1020,11 +1107,54 @@
}
break;
default:
- $result->error = $status_message;
+ $result->error = $result->status_message;
}
return $result;
}
+
+/**
+ * Splits an HTTP response status line into components.
+ *
+ * See the @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html status line definition @endlink
+ * in RFC 2616.
+ *
+ * @param string $respone
+ * The response status line, for example 'HTTP/1.1 500 Internal Server Error'.
+ *
+ * @return array
+ * Keyed array containing the component parts. If the response is malformed,
+ * all possible parts will be extracted. 'reason_phrase' could be empty.
+ * Possible keys:
+ * - 'http_version'
+ * - 'response_code'
+ * - 'reason_phrase'
+ */
+function _drupal_parse_response_status($response) {
+ $response_array = explode(' ', trim($response), 3);
+ // Set up empty values.
+ $result = array(
+ 'reason_phrase' => '',
+ );
+ $result['http_version'] = $response_array[0];
+ $result['response_code'] = $response_array[1];
+ if (isset($response_array[2])) {
+ $result['reason_phrase'] = $response_array[2];
+ }
+ return $result;
+}
+
+/**
+ * Helper function for determining hosts excluded from needing a proxy.
+ *
+ * @return
+ * TRUE if a proxy should be used for this host.
+ */
+function _drupal_http_use_proxy($host) {
+ $proxy_exceptions = variable_get('proxy_exceptions', array('localhost', '127.0.0.1'));
+ return !in_array(strtolower($host), $proxy_exceptions, TRUE);
+}
+
/**
* @} End of "HTTP handling".
*/
@@ -1059,7 +1189,7 @@
* @param $key
* The key for the item within $_FILES.
*
- * @see http://php.net/manual/en/features.file-upload.php#42280
+ * @see http://php.net/manual/features.file-upload.php#42280
*/
function _fix_gpc_magic_files(&$item, $key) {
if ($key != 'tmp_name') {
@@ -1099,7 +1229,8 @@
/**
* Verifies the syntax of the given e-mail address.
*
- * Empty e-mail addresses are allowed. See RFC 2822 for details.
+ * This uses the
+ * @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
*
* @param $mail
* A string containing an e-mail address.
@@ -1350,7 +1481,6 @@
* valid UTF-8.
*
* @see drupal_validate_utf8()
- * @ingroup sanitization
*/
function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite', 'blockquote', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd')) {
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
@@ -1420,7 +1550,7 @@
return '<';
}
- if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|()$%', $string, $matches)) {
+ if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9\-]+)([^>]*)>?|()$%', $string, $matches)) {
// Seriously malformed.
return '';
}
@@ -1652,9 +1782,15 @@
* - 'key': element name
* - 'value': element contents
* - 'attributes': associative array of element attributes
+ * - 'encoded': TRUE if 'value' is already encoded
*
* In both cases, 'value' can be a simple string, or it can be another array
* with the same format as $array itself for nesting.
+ *
+ * If 'encoded' is TRUE it is up to the caller to ensure that 'value' is either
+ * entity-encoded or CDATA-escaped. Using this option is not recommended when
+ * working with untrusted user input, since failing to escape the data
+ * correctly has security implications.
*/
function format_xml_elements($array) {
$output = '';
@@ -1667,7 +1803,7 @@
}
if (isset($value['value']) && $value['value'] != '') {
- $output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) . '' . $value['key'] . ">\n";
+ $output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : (!empty($value['encoded']) ? $value['value'] : check_plain($value['value']))) . '' . $value['key'] . ">\n";
}
else {
$output .= " />\n";
@@ -1698,7 +1834,7 @@
* $output = format_plural($update_count,
* 'Changed the content type of 1 post from %old-type to %new-type.',
* 'Changed the content type of @count posts from %old-type to %new-type.',
- * array('%old-type' => $info->old_type, '%new-type' => $info->new_type)));
+ * array('%old-type' => $info->old_type, '%new-type' => $info->new_type));
* @endcode
*
* @param $count
@@ -1734,7 +1870,8 @@
// Get the plural index through the gettext formula.
$index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
- // Backwards compatibility.
+ // If the index cannot be computed, use the plural as a fallback (which
+ // allows for most flexiblity with the replaceable @count value).
if ($index < 0) {
return t($plural, $args, $options);
}
@@ -1873,7 +2010,7 @@
* get interpreted as date format characters.
* @param $timezone
* (optional) Time zone identifier, as described at
- * http://php.net/manual/en/timezones.php Defaults to the time zone used to
+ * http://php.net/manual/timezones.php Defaults to the time zone used to
* display the page.
* @param $langcode
* (optional) Language code to translate to. Defaults to the language used to
@@ -2009,6 +2146,9 @@
/**
* Format a username.
*
+ * This is also the label callback implementation of
+ * callback_entity_info_label() for user_entity_info().
+ *
* By default, the passed-in object's 'name' property is used if it exists, or
* else, the site-defined value for the 'anonymous' variable. However, a module
* may override this by implementing hook_username_alter(&$name, $account).
@@ -2040,8 +2180,9 @@
* alternative than url().
*
* @param $path
- * The internal path or external URL being linked to, such as "node/34" or
- * "http://example.com/foo". A few notes:
+ * (optional) The internal path or external URL being linked to, such as
+ * "node/34" or "http://example.com/foo". The default value is equivalent to
+ * passing in ''. A few notes:
* - If you provide a full URL, it will be considered an external URL.
* - If you provide only the path (e.g. "node/34"), it will be
* considered an internal link. In this case, it should be a system URL,
@@ -2057,7 +2198,8 @@
* include them in $path, or use $options['query'] to let this function
* URL encode them.
* @param $options
- * An associative array of additional options, with the following elements:
+ * (optional) An associative array of additional options, with the following
+ * elements:
* - 'query': An array of query key/value-pairs (without any URL-encoding) to
* append to the URL.
* - 'fragment': A fragment identifier (named anchor) to append to the URL.
@@ -2073,7 +2215,7 @@
* for the URL. If $options['language'] is omitted, the global $language_url
* will be used.
* - 'https': Whether this URL should point to a secure location. If not
- * defined, the current scheme is used, so the user stays on http or https
+ * defined, the current scheme is used, so the user stays on HTTP or HTTPS
* respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
* only be enforced when the variable 'https' is set to TRUE.
* - 'base_url': Only used internally, to modify the base URL when a language
@@ -2106,14 +2248,11 @@
'prefix' => ''
);
+ // Determine whether this is an external link, but ensure that the current
+ // path is always treated as internal by default (to prevent external link
+ // injection vulnerabilities).
if (!isset($options['external'])) {
- // Return an external link if $path contains an allowed absolute URL. Only
- // call the slow drupal_strip_dangerous_protocols() if $path contains a ':'
- // before any / ? or #. Note: we could use url_is_external($path) here, but
- // that would require another function call, and performance inside url() is
- // critical.
- $colonpos = strpos($path, ':');
- $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path);
+ $options['external'] = $path === $_GET['q'] ? FALSE : url_is_external($path);
}
// Preserve the original path before altering or aliasing.
@@ -2151,6 +2290,11 @@
return $path . $options['fragment'];
}
+ // 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, '/');
+
global $base_url, $base_secure_url, $base_insecure_url;
// The base_url might be rewritten from the language rewrite in domain mode.
@@ -2178,7 +2322,10 @@
$language = isset($options['language']) && isset($options['language']->language) ? $options['language']->language : '';
$alias = drupal_get_path_alias($original_path, $language);
if ($alias != $original_path) {
- $path = $alias;
+ // Strip leading slashes from internal path aliases to prevent them
+ // becoming external URLs without protocol. /example.com should not be
+ // turned into //example.com.
+ $path = ltrim($alias, '/');
}
}
@@ -2228,10 +2375,21 @@
*/
function url_is_external($path) {
$colonpos = strpos($path, ':');
- // Avoid calling drupal_strip_dangerous_protocols() if there is any
- // slash (/), hash (#) or question_mark (?) before the colon (:)
- // occurrence - if any - as this would clearly mean it is not a URL.
- return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path;
+ // Some browsers treat \ as / so normalize to forward slashes.
+ $path = str_replace('\\', '/', $path);
+ // If the path starts with 2 slashes then it is always considered an external
+ // URL without an explicit protocol part.
+ return (strpos($path, '//') === 0)
+ // Leading control characters may be ignored or mishandled by browsers, so
+ // assume such a path may lead to an external location. The \p{C} character
+ // class matches all UTF-8 control, unassigned, and private characters.
+ || (preg_match('/^\p{C}/u', $path) !== 0)
+ // Avoid calling drupal_strip_dangerous_protocols() if there is any slash
+ // (/), hash (#) or question_mark (?) before the colon (:) occurrence - if
+ // any - as this would clearly mean it is not a URL.
+ || ($colonpos !== FALSE
+ && !preg_match('![/?#]!', substr($path, 0, $colonpos))
+ && drupal_strip_dangerous_protocols($path) == $path);
}
/**
@@ -2303,21 +2461,30 @@
/**
* Formats an internal or external URL link as an HTML anchor tag.
*
- * This function correctly handles aliased paths, and adds an 'active' class
+ * This function correctly handles aliased paths and adds an 'active' class
* attribute to links that point to the current page (for theming), so all
* internal links output by modules should be generated by this function if
* possible.
*
- * @param $text
- * The link text for the anchor tag.
- * @param $path
+ * However, for links enclosed in translatable text you should use t() and
+ * embed the HTML anchor tag directly in the translated string. For example:
+ * @code
+ * t('Visit the settings page', array('@url' => url('admin')));
+ * @endcode
+ * This keeps the context of the link title ('settings' in the example) for
+ * translators.
+ *
+ * @param string $text
+ * The translated link text for the anchor tag.
+ * @param string $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo". After the url() function is called to construct
* the URL from $path and $options, the resulting URL is passed through
* check_plain() before it is inserted into the HTML anchor tag, to ensure
* well-formed HTML. See url() for more information and notes.
* @param array $options
- * An associative array of additional options, with the following elements:
+ * An associative array of additional options. Defaults to an empty array. It
+ * may contain the following elements.
* - 'attributes': An associative array of HTML attributes to apply to the
* anchor tag. If element 'class' is included, it must be an array; 'title'
* must be a string; other elements are more flexible, as they just need
@@ -2333,8 +2500,10 @@
* well as the path must match). This element is also used by url().
* - Additional $options elements used by the url() function.
*
- * @return
+ * @return string
* An HTML string containing a link to the given path.
+ *
+ * @see url()
*/
function l($text, $path, array $options = array()) {
global $language_url;
@@ -2502,6 +2671,15 @@
global $language;
drupal_add_http_header('Content-Language', $language->language);
+ // By default, do not allow the site to be rendered in an iframe on another
+ // domain, but provide a variable to override this. If the code running for
+ // this page request already set the X-Frame-Options header earlier, don't
+ // overwrite it here.
+ $frame_options = variable_get('x_frame_options', 'SAMEORIGIN');
+ if ($frame_options && is_null(drupal_get_http_header('X-Frame-Options'))) {
+ drupal_add_http_header('X-Frame-Options', $frame_options);
+ }
+
// Menu status constants are integers; page content is a string or array.
if (is_int($page_callback_result)) {
// @todo: Break these up into separate functions?
@@ -2517,7 +2695,10 @@
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
- $_GET['destination'] = $_GET['q'];
+ // Make sure that the current path is not interpreted as external URL.
+ if (!url_is_external($_GET['q'])) {
+ $_GET['destination'] = $_GET['q'];
+ }
}
$path = drupal_get_normal_path(variable_get('site_404', ''));
@@ -2546,7 +2727,10 @@
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
- $_GET['destination'] = $_GET['q'];
+ // Make sure that the current path is not interpreted as external URL.
+ if (!url_is_external($_GET['q'])) {
+ $_GET['destination'] = $_GET['q'];
+ }
}
$path = drupal_get_normal_path(variable_get('site_403', ''));
@@ -2610,6 +2794,7 @@
_registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
drupal_cache_system_paths();
module_implements_write_cache();
+ drupal_file_scan_write_cache();
system_run_automated_cron();
}
@@ -2671,11 +2856,11 @@
* into script execution a call such as set_time_limit(20) is made, the
* script will run for a total of 45 seconds before timing out.
*
- * It also means that it is possible to decrease the total time limit if
- * the sum of the new time limit and the current time spent running the
- * script is inferior to the original time limit. It is inherent to the way
- * set_time_limit() works, it should rather be called with an appropriate
- * value every time you need to allocate a certain amount of time
+ * If the current time limit is not unlimited it is possible to decrease the
+ * total time limit if the sum of the new time limit and the current time spent
+ * running the script is inferior to the original time limit. It is inherent to
+ * the way set_time_limit() works, it should rather be called with an
+ * appropriate value every time you need to allocate a certain amount of time
* to execute a task than only once at the beginning of the script.
*
* Before calling set_time_limit(), we check if this function is available
@@ -2692,7 +2877,11 @@
*/
function drupal_set_time_limit($time_limit) {
if (function_exists('set_time_limit')) {
- @set_time_limit($time_limit);
+ $current = ini_get('max_execution_time');
+ // Do not set time limit if it is currently unlimited.
+ if ($current != 0) {
+ @set_time_limit($time_limit);
+ }
}
}
@@ -2705,7 +2894,7 @@
* The name of the item for which the path is requested.
*
* @return
- * The path to the requested item.
+ * The path to the requested item or an empty string if the item is not found.
*/
function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
@@ -2821,7 +3010,7 @@
* - 'group': A number identifying the group in which to add the stylesheet.
* Available constants are:
* - CSS_SYSTEM: Any system-layer CSS.
- * - CSS_DEFAULT: Any module-layer CSS.
+ * - CSS_DEFAULT: (default) Any module-layer CSS.
* - CSS_THEME: Any theme-layer CSS.
* The group number serves as a weight: the markup for loading a stylesheet
* within a lower weight group is output to the page before the markup for
@@ -2873,6 +3062,13 @@
*/
function drupal_add_css($data = NULL, $options = NULL) {
$css = &drupal_static(__FUNCTION__, array());
+ $count = &drupal_static(__FUNCTION__ . '_count', 0);
+
+ // If the $css variable has been reset with drupal_static_reset(), there is
+ // no longer any CSS being tracked, so set the counter back to 0 also.
+ if (count($css) === 0) {
+ $count = 0;
+ }
// Construct the options, taking the defaults into consideration.
if (isset($options)) {
@@ -2908,7 +3104,8 @@
}
// Always add a tiny value to the weight, to conserve the insertion order.
- $options['weight'] += count($css) / 1000;
+ $options['weight'] += $count / 1000;
+ $count++;
// Add the data to the CSS array depending on the type.
switch ($options['type']) {
@@ -2969,6 +3166,18 @@
// Sort CSS items, so that they appear in the correct order.
uasort($css, 'drupal_sort_css_js');
+ // Provide the page with information about the individual CSS files used,
+ // information not otherwise available when CSS aggregation is enabled. The
+ // setting is attached later in this function, but is set here, so that CSS
+ // files removed below are still considered "used" and prevented from being
+ // added in a later AJAX request.
+ // Skip if no files were added to the page or jQuery.extend() will overwrite
+ // the Drupal.settings.ajaxPageState.css object with an empty array.
+ if (!empty($css)) {
+ // Cast the array to an object to be on the safe side even if not empty.
+ $setting['ajaxPageState']['css'] = (object) array_fill_keys(array_keys($css), 1);
+ }
+
// Remove the overridden CSS files. Later CSS files override former ones.
$previous_item = array();
foreach ($css as $key => $item) {
@@ -2989,10 +3198,9 @@
'#items' => $css,
);
- // Provide the page with information about the individual CSS files used,
- // information not otherwise available when CSS aggregation is enabled.
- $setting['ajaxPageState']['css'] = array_fill_keys(array_keys($css), 1);
- $styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
+ if (!empty($setting)) {
+ $styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
+ }
return drupal_render($styles);
}
@@ -3344,7 +3552,11 @@
$import_batch = array_slice($import, 0, 31);
$import = array_slice($import, 31);
$element = $style_element_defaults;
- $element['#value'] = implode("\n", $import_batch);
+ // This simplifies the JavaScript regex, allowing each line
+ // (separated by \n) to be treated as a completely different string.
+ // This means that we can use ^ and $ on one line at a time, and not
+ // worry about style tags since they'll never match the regex.
+ $element['#value'] = "\n" . implode("\n", $import_batch) . "\n";
$element['#attributes']['media'] = $group['media'];
$element['#browsers'] = $group['browsers'];
$elements[] = $element;
@@ -3443,7 +3655,13 @@
$data = '';
$uri = '';
$map = variable_get('drupal_css_cache_files', array());
- $key = hash('sha256', serialize($css));
+ // Create a new array so that only the file names are used to create the hash.
+ // This prevents new aggregates from being created unnecessarily.
+ $css_data = array();
+ foreach ($css as $css_file) {
+ $css_data[] = $css_file['data'];
+ }
+ $key = hash('sha256', serialize($css_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
@@ -3563,17 +3781,23 @@
if ($basepath && !file_uri_scheme($file)) {
$file = $basepath . '/' . $file;
}
+ // Store the parent base path to restore it later.
+ $parent_base_path = $basepath;
+ // Set the current base path to process possible child imports.
$basepath = dirname($file);
// Load the CSS stylesheet. We suppress errors because themes may specify
// stylesheets in their .info file that don't exist in the theme's path,
// but are merely there to disable certain module CSS files.
+ $content = '';
if ($contents = @file_get_contents($file)) {
// Return the processed stylesheet.
- return drupal_load_stylesheet_content($contents, $_optimize);
+ $content = drupal_load_stylesheet_content($contents, $_optimize);
}
- return '';
+ // Restore the parent base path as the file and its childen are processed.
+ $basepath = $parent_base_path;
+ return $content;
}
/**
@@ -3590,7 +3814,7 @@
*/
function drupal_load_stylesheet_content($contents, $optimize = FALSE) {
// Remove multiple charset declarations for standards compliance (and fixing Safari problems).
- $contents = preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', $contents);
+ $contents = preg_replace('/^@charset\s+[\'"](\S*?)\b[\'"];/i', '', $contents);
if ($optimize) {
// Perform some safe CSS optimizations.
@@ -3609,7 +3833,7 @@
// Remove certain whitespace.
// There are different conditions for removing leading and trailing
// whitespace.
- // @see http://php.net/manual/en/regexp.reference.subpatterns.php
+ // @see http://php.net/manual/regexp.reference.subpatterns.php
$contents = preg_replace('<
# Strip leading and trailing whitespace.
\s*([@{};,])\s*
@@ -3634,7 +3858,7 @@
// Replaces @import commands with the actual stylesheet content.
// This happens recursively but omits external files.
- $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
+ $contents = preg_replace_callback('/@import\s*(?:url\(\s*)?[\'"]?(?![a-z]+:)(?!\/\/)([^\'"\()]+)[\'"]?\s*\)?\s*;/', '_drupal_load_stylesheet', $contents);
return $contents;
}
@@ -3658,7 +3882,7 @@
// Alter all internal url() paths. Leave external paths alone. We don't need
// to normalize absolute paths here (i.e. remove folder/... segments) because
// that will be done later.
- return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)/i', 'url(\1'. $directory, $file);
+ return preg_replace('/url\(\s*([\'"]?)(?![a-z]+:|\/+)([^\'")]+)([\'"]?)\s*\)/i', 'url(\1' . $directory . '\2\3)', $file);
}
/**
@@ -3694,6 +3918,21 @@
* The cleaned identifier.
*/
function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '')) {
+ // Use the advanced drupal_static() pattern, since this is called very often.
+ static $drupal_static_fast;
+ if (!isset($drupal_static_fast)) {
+ $drupal_static_fast['allow_css_double_underscores'] = &drupal_static(__FUNCTION__ . ':allow_css_double_underscores');
+ }
+ $allow_css_double_underscores = &$drupal_static_fast['allow_css_double_underscores'];
+ if (!isset($allow_css_double_underscores)) {
+ $allow_css_double_underscores = variable_get('allow_css_double_underscores', FALSE);
+ }
+
+ // Preserve BEM-style double-underscores depending on custom setting.
+ if ($allow_css_double_underscores) {
+ $filter['__'] = '__';
+ }
+
// By default, we filter using Drupal's coding standards.
$identifier = strtr($identifier, $filter);
@@ -3723,7 +3962,14 @@
* The cleaned class name.
*/
function drupal_html_class($class) {
- return drupal_clean_css_identifier(drupal_strtolower($class));
+ // The output of this function will never change, so this uses a normal
+ // static instead of drupal_static().
+ static $classes = array();
+
+ if (!isset($classes[$class])) {
+ $classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
+ }
+ return $classes[$class];
}
/**
@@ -3758,7 +4004,11 @@
// be merged with content already on the base page. The HTML IDs must be
// unique for the fully merged content. Therefore, initialize $seen_ids to
// take into account IDs that are already in use on the base page.
- $seen_ids_init = &drupal_static(__FUNCTION__ . ':init');
+ static $drupal_static_fast;
+ if (!isset($drupal_static_fast['seen_ids_init'])) {
+ $drupal_static_fast['seen_ids_init'] = &drupal_static(__FUNCTION__ . ':init');
+ }
+ $seen_ids_init = &$drupal_static_fast['seen_ids_init'];
if (!isset($seen_ids_init)) {
// Ideally, Drupal would provide an API to persist state information about
// prior page requests in the database, and we'd be able to add this
@@ -3778,7 +4028,16 @@
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
// returned by this function, potentially with the appended counter, so
// we parse that to reconstruct the $seen_ids array.
- foreach ($_POST['ajax_html_ids'] as $seen_id) {
+ if (isset($_POST['ajax_html_ids'][0]) && strpos($_POST['ajax_html_ids'][0], ',') === FALSE) {
+ $ajax_html_ids = $_POST['ajax_html_ids'];
+ }
+ else {
+ // jquery.form.js may send the server a comma-separated string as the
+ // first element of an array (see http://drupal.org/node/1575060), so
+ // we need to convert it to an array in that case.
+ $ajax_html_ids = explode(',', $_POST['ajax_html_ids'][0]);
+ }
+ foreach ($ajax_html_ids as $seen_id) {
// We rely on '--' being used solely for separating a base id from the
// counter, which this function ensures when returning an id.
$parts = explode('--', $seen_id, 2);
@@ -3794,7 +4053,10 @@
}
}
}
- $seen_ids = &drupal_static(__FUNCTION__, $seen_ids_init);
+ if (!isset($drupal_static_fast['seen_ids'])) {
+ $drupal_static_fast['seen_ids'] = &drupal_static(__FUNCTION__, $seen_ids_init);
+ }
+ $seen_ids = &$drupal_static_fast['seen_ids'];
$id = strtr(drupal_strtolower($id), array(' ' => '-', '_' => '-', '[' => '-', ']' => ''));
@@ -3904,7 +4166,8 @@
* actually needed.
*
* @param $data
- * (optional) If given, the value depends on the $options parameter:
+ * (optional) If given, the value depends on the $options parameter, or
+ * $options['type'] if $options is passed as an associative array:
* - 'file': Path to the file relative to base_path().
* - 'inline': The JavaScript code that should be placed in the given scope.
* - 'external': The absolute path to an external JavaScript file that is not
@@ -3977,7 +4240,14 @@
* else being the same, JavaScript added by a call to drupal_add_js() that
* happened later in the page request gets added to the page after one for
* which drupal_add_js() happened earlier in the page request.
- * - defer: If set to TRUE, the defer attribute is set on the <script>
+ * - requires_jquery: Set this to FALSE if the JavaScript you are adding does
+ * not have a dependency on jQuery. Defaults to TRUE, except for JavaScript
+ * settings where it defaults to FALSE. This is used on sites that have the
+ * 'javascript_always_use_jquery' variable set to FALSE; on those sites, if
+ * all the JavaScript added to the page by drupal_add_js() does not have a
+ * dependency on jQuery, then for improved front-end performance Drupal
+ * will not add jQuery and related libraries and settings to the page.
+ * - defer: If set to TRUE, the defer attribute is set on the