Browse Source

updated core to 7.67

Bachir Soussi Chiadmi 4 years ago
parent
commit
cc3b64a193
100 changed files with 2176 additions and 211 deletions
  1. 26 0
      CHANGELOG.txt
  2. 1 1
      includes/bootstrap.inc
  3. 5 0
      includes/common.inc
  4. 48 3
      includes/file.inc
  5. 14 0
      includes/file.phar.inc
  6. 30 5
      includes/registry.inc
  7. 4 0
      misc/brumann/polyfill-unserialize/.gitignore
  8. 20 0
      misc/brumann/polyfill-unserialize/.travis.yml
  9. 21 0
      misc/brumann/polyfill-unserialize/LICENSE
  10. 61 0
      misc/brumann/polyfill-unserialize/README.md
  11. 26 0
      misc/brumann/polyfill-unserialize/composer.json
  12. 25 0
      misc/brumann/polyfill-unserialize/phpunit.xml.dist
  13. 58 0
      misc/brumann/polyfill-unserialize/src/Unserialize.php
  14. 112 0
      misc/jquery-extend-3.4.0.js
  15. 3 0
      misc/typo3/phar-stream-wrapper/.gitignore
  16. 66 3
      misc/typo3/phar-stream-wrapper/README.md
  17. 5 1
      misc/typo3/phar-stream-wrapper/composer.json
  18. 37 0
      misc/typo3/phar-stream-wrapper/src/Collectable.php
  19. 18 2
      misc/typo3/phar-stream-wrapper/src/Helper.php
  20. 88 0
      misc/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php
  21. 4 4
      misc/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php
  22. 73 0
      misc/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php
  23. 56 6
      misc/typo3/phar-stream-wrapper/src/Manager.php
  24. 59 0
      misc/typo3/phar-stream-wrapper/src/Phar/Container.php
  25. 18 0
      misc/typo3/phar-stream-wrapper/src/Phar/DeserializationException.php
  26. 176 0
      misc/typo3/phar-stream-wrapper/src/Phar/Manifest.php
  27. 220 0
      misc/typo3/phar-stream-wrapper/src/Phar/Reader.php
  28. 18 0
      misc/typo3/phar-stream-wrapper/src/Phar/ReaderException.php
  29. 65 0
      misc/typo3/phar-stream-wrapper/src/Phar/Stub.php
  30. 36 2
      misc/typo3/phar-stream-wrapper/src/PharStreamWrapper.php
  31. 24 0
      misc/typo3/phar-stream-wrapper/src/Resolvable.php
  32. 125 0
      misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.php
  33. 156 0
      misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.php
  34. 241 0
      misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php
  35. 3 3
      modules/aggregator/aggregator.info
  36. 3 3
      modules/aggregator/tests/aggregator_test.info
  37. 3 3
      modules/block/block.info
  38. 3 3
      modules/block/tests/block_test.info
  39. 3 3
      modules/block/tests/themes/block_test_theme/block_test_theme.info
  40. 3 3
      modules/blog/blog.info
  41. 3 3
      modules/book/book.info
  42. 3 3
      modules/color/color.info
  43. 3 3
      modules/comment/comment.info
  44. 3 3
      modules/contact/contact.info
  45. 3 3
      modules/contextual/contextual.info
  46. 3 3
      modules/dashboard/dashboard.info
  47. 3 3
      modules/dblog/dblog.info
  48. 3 3
      modules/field/field.info
  49. 3 3
      modules/field/modules/field_sql_storage/field_sql_storage.info
  50. 3 3
      modules/field/modules/list/list.info
  51. 3 3
      modules/field/modules/list/tests/list_test.info
  52. 3 3
      modules/field/modules/number/number.info
  53. 1 1
      modules/field/modules/number/number.test
  54. 3 3
      modules/field/modules/options/options.info
  55. 3 3
      modules/field/modules/text/text.info
  56. 3 3
      modules/field/tests/field_test.info
  57. 3 3
      modules/field_ui/field_ui.info
  58. 1 1
      modules/file/file.field.inc
  59. 3 3
      modules/file/file.info
  60. 57 0
      modules/file/tests/file.test
  61. 3 3
      modules/file/tests/file_module_test.info
  62. 0 0
      modules/file/tests/fixtures/file_scan_ignore/a.txt
  63. 0 0
      modules/file/tests/fixtures/file_scan_ignore/frontend_framework/b.txt
  64. 0 0
      modules/file/tests/fixtures/file_scan_ignore/frontend_framework/c.txt
  65. 3 3
      modules/filter/filter.info
  66. 3 3
      modules/forum/forum.info
  67. 3 3
      modules/help/help.info
  68. 3 3
      modules/image/image.info
  69. 3 3
      modules/image/tests/image_module_test.info
  70. 3 3
      modules/locale/locale.info
  71. 3 3
      modules/locale/tests/locale_test.info
  72. 3 3
      modules/menu/menu.info
  73. 3 3
      modules/node/node.info
  74. 3 3
      modules/node/tests/node_access_test.info
  75. 3 3
      modules/node/tests/node_test.info
  76. 3 3
      modules/node/tests/node_test_exception.info
  77. 3 3
      modules/openid/openid.info
  78. 3 3
      modules/openid/tests/openid_test.info
  79. 3 3
      modules/overlay/overlay.info
  80. 3 3
      modules/path/path.info
  81. 3 3
      modules/php/php.info
  82. 3 3
      modules/poll/poll.info
  83. 3 3
      modules/profile/profile.info
  84. 3 3
      modules/rdf/rdf.info
  85. 3 3
      modules/rdf/tests/rdf_test.info
  86. 3 3
      modules/search/search.info
  87. 3 3
      modules/search/tests/search_embedded_form.info
  88. 3 3
      modules/search/tests/search_extra_type.info
  89. 3 3
      modules/search/tests/search_node_tags.info
  90. 3 3
      modules/shortcut/shortcut.info
  91. 2 2
      modules/simpletest/drupal_web_test_case.php
  92. 3 3
      modules/simpletest/simpletest.info
  93. 3 3
      modules/simpletest/tests/actions_loop_test.info
  94. 3 3
      modules/simpletest/tests/ajax_forms_test.info
  95. 3 3
      modules/simpletest/tests/ajax_test.info
  96. 3 3
      modules/simpletest/tests/batch_test.info
  97. 3 3
      modules/simpletest/tests/boot_test_1.info
  98. 3 3
      modules/simpletest/tests/boot_test_2.info
  99. 2 6
      modules/simpletest/tests/bootstrap.test
  100. 3 3
      modules/simpletest/tests/common_test.info

+ 26 - 0
CHANGELOG.txt

@@ -1,3 +1,29 @@
+Drupal 7.xx, xxxx-xx-xx (development version)
+-----------------------
+
+Drupal 7.67, 2019-05-08
+-----------------------
+- Fixed security issues:
+   - SA-CORE-2019-007
+
+Drupal 7.66, 2019-04-17
+-----------------------
+- Fixed security issues:
+   - SA-CORE-2019-006
+
+Drupal 7.65, 2019-03-20
+-----------------------
+- Fixed security issues:
+   - SA-CORE-2019-004
+
+Drupal 7.64, 2019-02-06
+-----------------------
+- [regression] Unset the 'host' header in drupal_http_request() during redirect
+- Fixed: 7.x does not have Phar protection and Phar tests are failing on Drupal 7
+- Fixed: Notice: Undefined index: display_field in file_field_widget_value() (line 582 of /module/file/file.field.inc)
+- Performance improvement: Registry rebuild should not parse the same file twice in the same request
+- Fixed _registry_update() to clear caches after transaction is committed
+
 Drupal 7.63, 2019-01-16
 Drupal 7.63, 2019-01-16
 -----------------------
 -----------------------
 - Fixed a fatal error for some Drush users introduced by SA-CORE-2019-002.
 - Fixed a fatal error for some Drush users introduced by SA-CORE-2019-002.

+ 1 - 1
includes/bootstrap.inc

@@ -8,7 +8,7 @@
 /**
 /**
  * The current system version.
  * The current system version.
  */
  */
-define('VERSION', '7.63');
+define('VERSION', '7.67');
 
 
 /**
 /**
  * Core API compatibility.
  * Core API compatibility.

+ 5 - 0
includes/common.inc

@@ -1094,6 +1094,11 @@ function drupal_http_request($url, array $options = array()) {
       elseif ($options['max_redirects']) {
       elseif ($options['max_redirects']) {
         // Redirect to the new location.
         // Redirect to the new location.
         $options['max_redirects']--;
         $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 = drupal_http_request($location, $options);
         $result->redirect_code = $code;
         $result->redirect_code = $code;
       }
       }

+ 48 - 3
includes/file.inc

@@ -993,8 +993,15 @@ function file_build_uri($path) {
  * @return
  * @return
  *   The destination filepath, or FALSE if the file already exists
  *   The destination filepath, or FALSE if the file already exists
  *   and FILE_EXISTS_ERROR is specified.
  *   and FILE_EXISTS_ERROR is specified.
+ *
+ * @throws RuntimeException
+ *   Thrown if the filename contains invalid UTF-8.
  */
  */
 function file_destination($destination, $replace) {
 function file_destination($destination, $replace) {
+  $basename = drupal_basename($destination);
+  if (!drupal_validate_utf8($basename)) {
+    throw new RuntimeException(sprintf("Invalid filename '%s'", $basename));
+  }
   if (file_exists($destination)) {
   if (file_exists($destination)) {
     switch ($replace) {
     switch ($replace) {
       case FILE_EXISTS_REPLACE:
       case FILE_EXISTS_REPLACE:
@@ -1002,7 +1009,6 @@ function file_destination($destination, $replace) {
         break;
         break;
 
 
       case FILE_EXISTS_RENAME:
       case FILE_EXISTS_RENAME:
-        $basename = drupal_basename($destination);
         $directory = drupal_dirname($destination);
         $directory = drupal_dirname($destination);
         $destination = file_create_filename($basename, $directory);
         $destination = file_create_filename($basename, $directory);
         break;
         break;
@@ -1218,11 +1224,20 @@ function file_unmunge_filename($filename) {
  * @return
  * @return
  *   File path consisting of $directory and a unique filename based off
  *   File path consisting of $directory and a unique filename based off
  *   of $basename.
  *   of $basename.
+ *
+ * @throws RuntimeException
+ *   Thrown if the $basename is not valid UTF-8 or another error occurs
+ *   stripping control characters.
  */
  */
 function file_create_filename($basename, $directory) {
 function file_create_filename($basename, $directory) {
+  $original = $basename;
   // Strip control characters (ASCII value < 32). Though these are allowed in
   // Strip control characters (ASCII value < 32). Though these are allowed in
   // some filesystems, not many applications handle them well.
   // some filesystems, not many applications handle them well.
   $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename);
   $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename);
+  if (preg_last_error() !== PREG_NO_ERROR) {
+    throw new RuntimeException(sprintf("Invalid filename '%s'", $original));
+  }
+
   if (substr(PHP_OS, 0, 3) == 'WIN') {
   if (substr(PHP_OS, 0, 3) == 'WIN') {
     // These characters are not allowed in Windows filenames
     // These characters are not allowed in Windows filenames
     $basename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $basename);
     $basename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $basename);
@@ -1563,7 +1578,13 @@ function file_save_upload($form_field_name, $validators = array(), $destination
   if (substr($destination, -1) != '/') {
   if (substr($destination, -1) != '/') {
     $destination .= '/';
     $destination .= '/';
   }
   }
-  $file->destination = file_destination($destination . $file->filename, $replace);
+  try {
+    $file->destination = file_destination($destination . $file->filename, $replace);
+  }
+  catch (RuntimeException $e) {
+    drupal_set_message(t('The file %source could not be uploaded because the name is invalid.', array('%source' => $form_field_name)), 'error');
+    return FALSE;
+  }
   // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
   // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
   // there's an existing file so we need to bail.
   // there's an existing file so we need to bail.
   if ($file->destination === FALSE) {
   if ($file->destination === FALSE) {
@@ -2130,9 +2151,33 @@ function file_download_access($uri) {
  *   'filename', and 'name' members corresponding to the matching files.
  *   'filename', and 'name' members corresponding to the matching files.
  */
  */
 function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
 function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
+  // Default nomask option.
+  $nomask = '/(\.\.?|CVS)$/';
+
+  // Overrides the $nomask variable accordingly if $options['nomask'] is set.
+  //
+  // Allow directories specified in settings.php to be ignored. You can use this
+  // to not check for files in common special-purpose directories. For example,
+  // node_modules and bower_components. Ignoring irrelevant directories is a
+  // performance boost.
+  if (!isset($options['nomask'])) {
+    $ignore_directories = variable_get(
+      'file_scan_ignore_directories',
+      array()
+    );
+
+    foreach ($ignore_directories as $index => $ignore_directory) {
+      $ignore_directories[$index] = preg_quote($ignore_directory, '/');
+    }
+
+    if (!empty($ignore_directories)) {
+      $nomask = '/^(\.\.?)|CVS|' . implode('|', $ignore_directories) . '$/';
+    }
+  }
+
   // Merge in defaults.
   // Merge in defaults.
   $options += array(
   $options += array(
-    'nomask' => '/(\.\.?|CVS)$/',
+    'nomask' => $nomask,
     'callback' => 0,
     'callback' => 0,
     'recurse' => TRUE,
     'recurse' => TRUE,
     'key' => 'uri',
     'key' => 'uri',

+ 14 - 0
includes/file.phar.inc

@@ -18,7 +18,21 @@ function file_register_phar_wrapper() {
   include_once $directory . '/Helper.php';
   include_once $directory . '/Helper.php';
   include_once $directory . '/Manager.php';
   include_once $directory . '/Manager.php';
   include_once $directory . '/PharStreamWrapper.php';
   include_once $directory . '/PharStreamWrapper.php';
+  include_once $directory . '/Collectable.php';
+  include_once $directory . '/Interceptor/ConjunctionInterceptor.php';
+  include_once $directory . '/Interceptor/PharMetaDataInterceptor.php';
+  include_once $directory . '/Phar/Container.php';
+  include_once $directory . '/Phar/DeserializationException.php';
+  include_once $directory . '/Phar/Manifest.php';
+  include_once $directory . '/Phar/Reader.php';
+  include_once $directory . '/Phar/ReaderException.php';
+  include_once $directory . '/Phar/Stub.php';
+  include_once $directory . '/Resolvable.php';
+  include_once $directory . '/Resolver/PharInvocation.php';
+  include_once $directory . '/Resolver/PharInvocationCollection.php';
+  include_once $directory . '/Resolver/PharInvocationResolver.php';
   include_once DRUPAL_ROOT . '/misc/typo3/drupal-security/PharExtensionInterceptor.php';
   include_once DRUPAL_ROOT . '/misc/typo3/drupal-security/PharExtensionInterceptor.php';
+  include_once DRUPAL_ROOT . '/misc/brumann/polyfill-unserialize/src/Unserialize.php';
 
 
   // Set up a stream wrapper to handle insecurities due to PHP's built-in
   // Set up a stream wrapper to handle insecurities due to PHP's built-in
   // phar stream wrapper.
   // phar stream wrapper.

+ 30 - 5
includes/registry.inc

@@ -19,7 +19,6 @@
  * Does the work for registry_update().
  * Does the work for registry_update().
  */
  */
 function _registry_update() {
 function _registry_update() {
-
   // The registry serves as a central autoloader for all classes, including
   // The registry serves as a central autoloader for all classes, including
   // the database query builders. However, the registry rebuild process
   // the database query builders. However, the registry rebuild process
   // requires write ability to the database, which means having access to the
   // requires write ability to the database, which means having access to the
@@ -33,6 +32,11 @@ function _registry_update() {
   require_once DRUPAL_ROOT . '/includes/database/select.inc';
   require_once DRUPAL_ROOT . '/includes/database/select.inc';
   require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
   require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
 
 
+  // During the first registry rebuild in a request, we check all the files.
+  // During subsequent rebuilds, we only add new files. It makes the rebuilding
+  // process faster during installation of modules.
+  static $check_existing_files = TRUE;
+
   // Get current list of modules and their files.
   // Get current list of modules and their files.
   $modules = db_query("SELECT * FROM {system} WHERE type = 'module'")->fetchAll();
   $modules = db_query("SELECT * FROM {system} WHERE type = 'module'")->fetchAll();
   // Get the list of files we are going to parse.
   // Get the list of files we are going to parse.
@@ -55,6 +59,9 @@ function _registry_update() {
     $files["$filename"] = array('module' => '', 'weight' => 0);
     $files["$filename"] = array('module' => '', 'weight' => 0);
   }
   }
 
 
+  // Initialize an empty array for the unchanged files.
+  $unchanged_files = array();
+
   $transaction = db_transaction();
   $transaction = db_transaction();
   try {
   try {
     // Allow modules to manually modify the list of files before the registry
     // Allow modules to manually modify the list of files before the registry
@@ -63,10 +70,19 @@ function _registry_update() {
     // list can then be added to the list of files that the registry will parse,
     // list can then be added to the list of files that the registry will parse,
     // or modify attributes of a file.
     // or modify attributes of a file.
     drupal_alter('registry_files', $files, $modules);
     drupal_alter('registry_files', $files, $modules);
+
     foreach (registry_get_parsed_files() as $filename => $file) {
     foreach (registry_get_parsed_files() as $filename => $file) {
       // Add the hash for those files we have already parsed.
       // Add the hash for those files we have already parsed.
       if (isset($files[$filename])) {
       if (isset($files[$filename])) {
-        $files[$filename]['hash'] = $file['hash'];
+        if ($check_existing_files === TRUE) {
+          $files[$filename]['hash'] = $file['hash'];
+        }
+        else {
+          // Ignore that file for this request, it has been parsed previously
+          // and it is unlikely it has changed.
+          unset($files[$filename]);
+          $unchanged_files[$filename] = $file;
+        }
       }
       }
       else {
       else {
         // Flush the registry of resources in files that are no longer on disc
         // Flush the registry of resources in files that are no longer on disc
@@ -79,8 +95,12 @@ function _registry_update() {
           ->execute();
           ->execute();
       }
       }
     }
     }
+
     $parsed_files = _registry_parse_files($files);
     $parsed_files = _registry_parse_files($files);
 
 
+    // Add unchanged files to the files.
+    $files += $unchanged_files;
+
     $unchanged_resources = array();
     $unchanged_resources = array();
     $lookup_cache = array();
     $lookup_cache = array();
     if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) {
     if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) {
@@ -89,12 +109,10 @@ function _registry_update() {
     foreach ($lookup_cache as $key => $file) {
     foreach ($lookup_cache as $key => $file) {
       // If the file for this cached resource is carried over unchanged from
       // If the file for this cached resource is carried over unchanged from
       // the last registry build, then we can safely re-cache it.
       // the last registry build, then we can safely re-cache it.
-      if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
+      if ($file && isset($files[$file]) && !in_array($file, $parsed_files, TRUE)) {
         $unchanged_resources[$key] = $file;
         $unchanged_resources[$key] = $file;
       }
       }
     }
     }
-    module_implements('', FALSE, TRUE);
-    _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
   }
   }
   catch (Exception $e) {
   catch (Exception $e) {
     $transaction->rollback();
     $transaction->rollback();
@@ -102,6 +120,13 @@ function _registry_update() {
     throw $e;
     throw $e;
   }
   }
 
 
+  module_implements('', FALSE, TRUE);
+  _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
+
+  // During the next run in this request, don't bother re-checking existing
+  // files.
+  $check_existing_files = FALSE;
+
   // We have some unchanged resources, warm up the cache - no need to pay
   // We have some unchanged resources, warm up the cache - no need to pay
   // for looking them up again.
   // for looking them up again.
   if (count($unchanged_resources) > 0) {
   if (count($unchanged_resources) > 0) {

+ 4 - 0
misc/brumann/polyfill-unserialize/.gitignore

@@ -0,0 +1,4 @@
+/vendor/
+/phpunit.xml
+/.composer.lock
+

+ 20 - 0
misc/brumann/polyfill-unserialize/.travis.yml

@@ -0,0 +1,20 @@
+language: php
+
+sudo: false
+
+php:
+  - '5.3'
+  - '5.4'
+  - '5.5'
+  - '5.6'
+  - '7.0'
+  - '7.1'
+
+before_install:
+  - phpenv config-rm xdebug.ini
+  - composer self-update
+
+install:
+  - composer install
+
+script: phpunit

+ 21 - 0
misc/brumann/polyfill-unserialize/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Denis Brumann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 61 - 0
misc/brumann/polyfill-unserialize/README.md

@@ -0,0 +1,61 @@
+Polyfill unserialize [![Build Status](https://travis-ci.org/dbrumann/polyfill-unserialize.svg?branch=master)](https://travis-ci.org/dbrumann/polyfill-unserialize)
+===
+
+Backports unserialize options introduced in PHP 7.0 to older PHP versions.
+This was originally designed as a Proof of Concept for Symfony Issue [#21090](https://github.com/symfony/symfony/pull/21090).
+
+You can use this package in projects that rely on PHP versions older than PHP 7.0.
+In case you are using PHP 7.0+ the original `unserialize()` will be used instead.
+
+From the [documentation](https://secure.php.net/manual/en/function.unserialize.php):
+
+> Warning: Do not pass untrusted user input to unserialize(). Unserialization can
+> result in code being loaded and executed due to object instantiation
+> and autoloading, and a malicious user may be able to exploit this.
+
+This warning holds true even when `allowed_classes` is used.
+
+Requirements
+------------
+
+ - PHP 5.3+
+
+Installation
+------------
+
+You can install this package via composer:
+
+```
+composer require brumann/polyfill-unserialize "^1.0"
+```
+
+Known Issues
+------------
+
+There is a mismatch in behavior when `allowed_classes` in `$options` is not
+of the correct type (array or boolean). PHP 7.1 will issue a warning, whereas
+PHP 7.0 will not. I opted to copy the behavior of the former.
+
+Tests
+-----
+
+You can run the test suite using PHPUnit. It is intentionally not bundled as
+dev dependency to make sure this package has the lowest restrictions on the
+implementing system as possible.
+
+Please read the [PHPUnit Manual](https://phpunit.de/manual/current/en/installation.html)
+for information how to install it on your system.
+
+You can run the test suite as follows:
+
+```
+phpunit -c phpunit.xml.dist tests/
+```
+
+Contributing
+------------
+
+This package is considered feature complete. As such I will likely not update it
+unless there are security issues.
+
+Should you find any bugs or have questions, feel free to submit an Issue or a Pull Request.

+ 26 - 0
misc/brumann/polyfill-unserialize/composer.json

@@ -0,0 +1,26 @@
+{
+    "name": "brumann/polyfill-unserialize",
+    "description": "Backports unserialize options introduced in PHP 7.0 to older PHP versions.",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Denis Brumann",
+            "email": "denis.brumann@sensiolabs.de"
+        }
+    ],
+    "autoload": {
+        "psr-4": {
+            "Brumann\\Polyfill\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\Brumann\\Polyfill\\": "tests/"
+        }
+    },
+    "minimum-stability": "stable",
+    "require": {
+        "php": "^5.3|^7.0"
+    }
+}

+ 25 - 0
misc/brumann/polyfill-unserialize/phpunit.xml.dist

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+    backupGlobals="false"
+    colors="true"
+    bootstrap="vendor/autoload.php"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Brumann\Polyfill Test Suite">
+            <directory>./tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./src/</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 58 - 0
misc/brumann/polyfill-unserialize/src/Unserialize.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace Brumann\Polyfill;
+
+final class Unserialize
+{
+    /**
+     * @see https://secure.php.net/manual/en/function.unserialize.php
+     *
+     * @param string $serialized Serialized data
+     * @param array $options Associative array containing options
+     *
+     * @return mixed
+     */
+    public static function unserialize($serialized, array $options = array())
+    {
+        if (PHP_VERSION_ID >= 70000) {
+            return \unserialize($serialized, $options);
+        }
+        if (!array_key_exists('allowed_classes', $options)) {
+            $options['allowed_classes'] = true;
+        }
+        $allowedClasses = $options['allowed_classes'];
+        if (true === $allowedClasses) {
+            return \unserialize($serialized);
+        }
+        if (false === $allowedClasses) {
+            $allowedClasses = array();
+        }
+        if (!is_array($allowedClasses)) {
+            trigger_error(
+                'unserialize(): allowed_classes option should be array or boolean',
+                E_USER_WARNING
+            );
+            $allowedClasses = array();
+        }
+
+        $sanitizedSerialized = preg_replace_callback(
+            '/(^|;)O:\d+:"([^"]*)":(\d+):{/',
+            function ($match) use ($allowedClasses) {
+                list($completeMatch, $leftBorder, $className, $objectSize) = $match;
+                if (in_array($className, $allowedClasses)) {
+                    return $completeMatch;
+                } else {
+                    return sprintf(
+                        '%sO:22:"__PHP_Incomplete_Class":%d:{s:27:"__PHP_Incomplete_Class_Name";%s',
+                        $leftBorder,
+                        $objectSize + 1, // size of object + 1 for added string
+                        \serialize($className)
+                    );
+                }
+            },
+            $serialized
+        );
+
+        return \unserialize($sanitizedSerialized);
+    }
+}

+ 112 - 0
misc/jquery-extend-3.4.0.js

@@ -0,0 +1,112 @@
+/**
+ * For jQuery versions less than 3.4.0, this replaces the jQuery.extend
+ * function with the one from jQuery 3.4.0, slightly modified (documented
+ * below) to be compatible with older jQuery versions and browsers.
+ *
+ * This provides the Object.prototype pollution vulnerability fix to Drupal
+ * installations running older jQuery versions, including the versions shipped
+ * with Drupal core and https://www.drupal.org/project/jquery_update.
+ *
+ * @see https://github.com/jquery/jquery/pull/4333
+ */
+
+(function (jQuery) {
+
+// Do not override jQuery.extend() if the jQuery version is already >=3.4.0.
+var versionParts = jQuery.fn.jquery.split('.');
+var majorVersion = parseInt(versionParts[0]);
+var minorVersion = parseInt(versionParts[1]);
+var patchVersion = parseInt(versionParts[2]);
+var isPreReleaseVersion = (patchVersion.toString() !== versionParts[2]);
+if (
+  (majorVersion > 3) ||
+  (majorVersion === 3 && minorVersion > 4) ||
+  (majorVersion === 3 && minorVersion === 4 && patchVersion > 0) ||
+  (majorVersion === 3 && minorVersion === 4 && patchVersion === 0 && !isPreReleaseVersion)
+) {
+  return;
+}
+
+/**
+ * This is almost verbatim copied from jQuery 3.4.0.
+ *
+ * Only two minor changes have been made:
+ * - The call to isFunction() is changed to jQuery.isFunction().
+ * - The two calls to Array.isArray() is changed to jQuery.isArray().
+ *
+ * The above two changes ensure compatibility with all older jQuery versions
+ * (1.4.4 - 3.3.1) and older browser versions (e.g., IE8).
+ */
+jQuery.extend = jQuery.fn.extend = function() {
+  var options, name, src, copy, copyIsArray, clone,
+    target = arguments[ 0 ] || {},
+    i = 1,
+    length = arguments.length,
+    deep = false;
+
+  // Handle a deep copy situation
+  if ( typeof target === "boolean" ) {
+    deep = target;
+
+    // Skip the boolean and the target
+    target = arguments[ i ] || {};
+    i++;
+  }
+
+  // Handle case when target is a string or something (possible in deep copy)
+  if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
+    target = {};
+  }
+
+  // Extend jQuery itself if only one argument is passed
+  if ( i === length ) {
+    target = this;
+    i--;
+  }
+
+  for ( ; i < length; i++ ) {
+
+    // Only deal with non-null/undefined values
+    if ( ( options = arguments[ i ] ) != null ) {
+
+      // Extend the base object
+      for ( name in options ) {
+        copy = options[ name ];
+
+        // Prevent Object.prototype pollution
+        // Prevent never-ending loop
+        if ( name === "__proto__" || target === copy ) {
+          continue;
+        }
+
+        // Recurse if we're merging plain objects or arrays
+        if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
+          ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
+          src = target[ name ];
+
+          // Ensure proper type for the source value
+          if ( copyIsArray && !jQuery.isArray( src ) ) {
+            clone = [];
+          } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
+            clone = {};
+          } else {
+            clone = src;
+          }
+          copyIsArray = false;
+
+          // Never move original objects, clone them
+          target[ name ] = jQuery.extend( deep, clone, copy );
+
+          // Don't bring in undefined values
+        } else if ( copy !== undefined ) {
+          target[ name ] = copy;
+        }
+      }
+    }
+  }
+
+  // Return the modified object
+  return target;
+};
+
+})(jQuery);

+ 3 - 0
misc/typo3/phar-stream-wrapper/.gitignore

@@ -0,0 +1,3 @@
+.idea
+vendor/
+composer.lock

+ 66 - 3
misc/typo3/phar-stream-wrapper/README.md

@@ -63,7 +63,7 @@ adjusted to according requirements.
 
 
 ```
 ```
 $behavior = new \TYPO3\PharStreamWrapper\Behavior();
 $behavior = new \TYPO3\PharStreamWrapper\Behavior();
-Manager::initialize(
+\TYPO3\PharStreamWrapper\Manager::initialize(
     $behavior->withAssertion(new PharExtensionInterceptor())
     $behavior->withAssertion(new PharExtensionInterceptor())
 );
 );
 
 
@@ -90,7 +90,7 @@ if (in_array('phar', stream_get_wrappers())) {
   + `COMMAND_UNLINK`
   + `COMMAND_UNLINK`
   + `COMMAND_URL_STAT`
   + `COMMAND_URL_STAT`
 
 
-## Interceptor
+## Interceptors
 
 
 The following interceptor is shipped with the package and ready to use in order
 The following interceptor is shipped with the package and ready to use in order
 to block any Phar invocation of files not having a `.phar` suffix. Besides that
 to block any Phar invocation of files not having a `.phar` suffix. Besides that
@@ -137,9 +137,72 @@ class PharExtensionInterceptor implements Assertable
 }
 }
 ```
 ```
 
 
+### ConjunctionInterceptor
+
+This interceptor combines multiple interceptors implementing `Assertable`.
+It succeeds when all nested interceptors succeed as well (logical `AND`).
+
+```
+$behavior = new \TYPO3\PharStreamWrapper\Behavior();
+\TYPO3\PharStreamWrapper\Manager::initialize(
+    $behavior->withAssertion(new ConjunctionInterceptor(array(
+        new PharExtensionInterceptor(),
+        new PharMetaDataInterceptor()
+    )))
+);
+```
+
+### PharExtensionInterceptor
+
+This (basic) interceptor just checks whether the invoked Phar archive has
+an according `.phar` file extension. Resolving symbolic links as well as
+Phar internal alias resolving are considered as well.
+
+```
+$behavior = new \TYPO3\PharStreamWrapper\Behavior();
+\TYPO3\PharStreamWrapper\Manager::initialize(
+    $behavior->withAssertion(new PharExtensionInterceptor())
+);
+```
+
+### PharMetaDataInterceptor
+
+This interceptor is actually checking serialized Phar meta-data against
+PHP objects and would consider a Phar archive malicious in case not only
+scalar values are found. A custom low-level `Phar\Reader` is used in order to
+avoid using PHP's `Phar` object which would trigger the initial vulnerability.
+
+```
+$behavior = new \TYPO3\PharStreamWrapper\Behavior();
+\TYPO3\PharStreamWrapper\Manager::initialize(
+    $behavior->withAssertion(new PharMetaDataInterceptor())
+);
+```
+
+## Reader
+
+* `Phar\Reader::__construct(string $fileName)`: Creates low-level reader for Phar archive
+* `Phar\Reader::resolveContainer(): Phar\Container`: Resolves model representing Phar archive
+* `Phar\Container::getStub(): Phar\Stub`: Resolves (plain PHP) stub section of Phar archive
+* `Phar\Container::getManifest(): Phar\Manifest`: Resolves parsed Phar archive manifest as
+  documented at http://php.net/manual/en/phar.fileformat.manifestfile.php
+* `Phar\Stub::getMappedAlias(): string`: Resolves internal Phar archive alias defined in stub
+  using `Phar::mapPhar('alias.phar')` - actually the plain PHP source is analyzed here
+* `Phar\Manifest::getAlias(): string` - Resolves internal Phar archive alias defined in manifest
+  using `Phar::setAlias('alias.phar')`
+* `Phar\Manifest::getMetaData(): string`: Resolves serialized Phar archive meta-data
+* `Phar\Manifest::deserializeMetaData(): mixed`: Resolves deserialized Phar archive meta-data
+  containing only scalar values - in case an object is determined, an according
+  `Phar\DeserializationException` will be thrown
+
+```
+$reader = new Phar\Reader('example.phar');
+var_dump($reader->resolveContainer()->getManifest()->deserializeMetaData());
+```
+
 ## Helper
 ## Helper
 
 
-* `Helper::determineBaseFile(string $path)`: Determines base file that can be
+* `Helper::determineBaseFile(string $path): string`: Determines base file that can be
   accessed using the regular file system. For instance the following path
   accessed using the regular file system. For instance the following path
   `phar:///home/user/bundle.phar/content.txt` would be resolved to
   `phar:///home/user/bundle.phar/content.txt` would be resolved to
   `/home/user/bundle.phar`.
   `/home/user/bundle.phar`.

+ 5 - 1
misc/typo3/phar-stream-wrapper/composer.json

@@ -6,9 +6,13 @@
     "homepage": "https://typo3.org/",
     "homepage": "https://typo3.org/",
     "keywords": ["php", "phar", "stream-wrapper", "security"],
     "keywords": ["php", "phar", "stream-wrapper", "security"],
     "require": {
     "require": {
-        "php": "^5.3.3|^7.0"
+        "php": "^5.3.3|^7.0",
+        "ext-fileinfo": "*",
+        "ext-json": "*",
+        "brumann/polyfill-unserialize": "^1.0"
     },
     },
     "require-dev": {
     "require-dev": {
+        "ext-xdebug": "*",
         "phpunit/phpunit": "^4.8.36"
         "phpunit/phpunit": "^4.8.36"
     },
     },
     "autoload": {
     "autoload": {

+ 37 - 0
misc/typo3/phar-stream-wrapper/src/Collectable.php

@@ -0,0 +1,37 @@
+<?php
+namespace TYPO3\PharStreamWrapper;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+
+interface Collectable
+{
+    /**
+     * @param PharInvocation $invocation
+     * @return bool
+     */
+    public function has(PharInvocation $invocation);
+
+    /**
+     * @param PharInvocation $invocation
+     * @param null $flags
+     * @return bool
+     */
+    public function collect(PharInvocation $invocation, $flags = null);
+
+    /**
+     * @param callable $callback
+     * @param bool $reverse
+     * @return null|PharInvocation
+     */
+    public function findByCallback($callback, $reverse = false);
+}

+ 18 - 2
misc/typo3/phar-stream-wrapper/src/Helper.php

@@ -11,6 +11,13 @@ namespace TYPO3\PharStreamWrapper;
  * The TYPO3 project - inspiring people to share!
  * The TYPO3 project - inspiring people to share!
  */
  */
 
 
+/**
+ * Helper provides low-level tools on file name resolving. However it does not
+ * (and should not) maintain any runtime state information. In order to resolve
+ * Phar archive paths according resolvers have to be used.
+ *
+ * @see \TYPO3\PharStreamWrapper\Resolvable::resolve()
+ */
 class Helper
 class Helper
 {
 {
     /*
     /*
@@ -54,6 +61,15 @@ class Helper
         return null;
         return null;
     }
     }
 
 
+    /**
+     * @param string $path
+     * @return bool
+     */
+    public static function hasPharPrefix($path)
+    {
+        return stripos($path, 'phar://') === 0;
+    }
+
     /**
     /**
      * @param string $path
      * @param string $path
      * @return string
      * @return string
@@ -61,7 +77,7 @@ class Helper
     public static function removePharPrefix($path)
     public static function removePharPrefix($path)
     {
     {
         $path = trim($path);
         $path = trim($path);
-        if (stripos($path, 'phar://') !== 0) {
+        if (!static::hasPharPrefix($path)) {
             return $path;
             return $path;
         }
         }
         return substr($path, 7);
         return substr($path, 7);
@@ -77,7 +93,7 @@ class Helper
     public static function normalizePath($path)
     public static function normalizePath($path)
     {
     {
         return rtrim(
         return rtrim(
-            static::getCanonicalPath(
+            static::normalizeWindowsPath(
                 static::removePharPrefix($path)
                 static::removePharPrefix($path)
             ),
             ),
             '/'
             '/'

+ 88 - 0
misc/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php

@@ -0,0 +1,88 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Interceptor;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Assertable;
+use TYPO3\PharStreamWrapper\Exception;
+
+class ConjunctionInterceptor implements Assertable
+{
+    /**
+     * @var Assertable[]
+     */
+    private $assertions;
+
+    public function __construct(array $assertions)
+    {
+        $this->assertAssertions($assertions);
+        $this->assertions = $assertions;
+    }
+
+    /**
+     * Executes assertions based on all contained assertions.
+     *
+     * @param string $path
+     * @param string $command
+     * @return bool
+     * @throws Exception
+     */
+    public function assert($path, $command)
+    {
+        if ($this->invokeAssertions($path, $command)) {
+            return true;
+        }
+        throw new Exception(
+            sprintf(
+                'Assertion failed in "%s"',
+                $path
+            ),
+            1539625084
+        );
+    }
+
+    /**
+     * @param Assertable[] $assertions
+     */
+    private function assertAssertions(array $assertions)
+    {
+        foreach ($assertions as $assertion) {
+            if (!$assertion instanceof Assertable) {
+                throw new \InvalidArgumentException(
+                    sprintf(
+                        'Instance %s must implement Assertable',
+                        get_class($assertion)
+                    ),
+                    1539624719
+                );
+            }
+        }
+    }
+
+    /**
+     * @param string $path
+     * @param string $command
+     * @return bool
+     */
+    private function invokeAssertions($path, $command)
+    {
+        try {
+            foreach ($this->assertions as $assertion) {
+                if (!$assertion->assert($path, $command)) {
+                    return false;
+                }
+            }
+        } catch (Exception $exception) {
+            return false;
+        }
+        return true;
+    }
+}

+ 4 - 4
misc/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php

@@ -12,8 +12,8 @@ namespace TYPO3\PharStreamWrapper\Interceptor;
  */
  */
 
 
 use TYPO3\PharStreamWrapper\Assertable;
 use TYPO3\PharStreamWrapper\Assertable;
-use TYPO3\PharStreamWrapper\Helper;
 use TYPO3\PharStreamWrapper\Exception;
 use TYPO3\PharStreamWrapper\Exception;
+use TYPO3\PharStreamWrapper\Manager;
 
 
 class PharExtensionInterceptor implements Assertable
 class PharExtensionInterceptor implements Assertable
 {
 {
@@ -45,11 +45,11 @@ class PharExtensionInterceptor implements Assertable
      */
      */
     private function baseFileContainsPharExtension($path)
     private function baseFileContainsPharExtension($path)
     {
     {
-        $baseFile = Helper::determineBaseFile($path);
-        if ($baseFile === null) {
+        $invocation = Manager::instance()->resolve($path);
+        if ($invocation === null) {
             return false;
             return false;
         }
         }
-        $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION);
+        $fileExtension = pathinfo($invocation->getBaseName(), PATHINFO_EXTENSION);
         return strtolower($fileExtension) === 'phar';
         return strtolower($fileExtension) === 'phar';
     }
     }
 }
 }

+ 73 - 0
misc/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php

@@ -0,0 +1,73 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Interceptor;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Assertable;
+use TYPO3\PharStreamWrapper\Exception;
+use TYPO3\PharStreamWrapper\Manager;
+use TYPO3\PharStreamWrapper\Phar\DeserializationException;
+use TYPO3\PharStreamWrapper\Phar\Reader;
+
+/**
+ * @internal Experimental implementation of checking against serialized objects in Phar meta-data
+ * @internal This functionality has not been 100% pentested...
+ */
+class PharMetaDataInterceptor implements Assertable
+{
+    /**
+     * Determines whether the according Phar archive contains
+     * (potential insecure) serialized objects.
+     *
+     * @param string $path
+     * @param string $command
+     * @return bool
+     * @throws Exception
+     */
+    public function assert($path, $command)
+    {
+        if ($this->baseFileDoesNotHaveMetaDataIssues($path)) {
+            return true;
+        }
+        throw new Exception(
+            sprintf(
+                'Problematic meta-data in "%s"',
+                $path
+            ),
+            1539632368
+        );
+    }
+
+    /**
+     * @param string $path
+     * @return bool
+     */
+    private function baseFileDoesNotHaveMetaDataIssues($path)
+    {
+        $invocation = Manager::instance()->resolve($path);
+        if ($invocation === null) {
+            return false;
+        }
+        // directly return in case invocation was checked before
+        if ($invocation->getVariable(__CLASS__) === true) {
+            return true;
+        }
+        // otherwise analyze meta-data
+        try {
+            $reader = new Reader($invocation->getBaseName());
+            $reader->resolveContainer()->getManifest()->deserializeMetaData();
+            $invocation->setVariable(__CLASS__, true);
+        } catch (DeserializationException $exception) {
+            return false;
+        }
+        return true;
+    }
+}

+ 56 - 6
misc/typo3/phar-stream-wrapper/src/Manager.php

@@ -11,7 +11,11 @@ namespace TYPO3\PharStreamWrapper;
  * The TYPO3 project - inspiring people to share!
  * The TYPO3 project - inspiring people to share!
  */
  */
 
 
-class Manager implements Assertable
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+use TYPO3\PharStreamWrapper\Resolver\PharInvocationCollection;
+use TYPO3\PharStreamWrapper\Resolver\PharInvocationResolver;
+
+class Manager
 {
 {
     /**
     /**
      * @var self
      * @var self
@@ -23,14 +27,29 @@ class Manager implements Assertable
      */
      */
     private $behavior;
     private $behavior;
 
 
+    /**
+     * @var Resolvable
+     */
+    private $resolver;
+
+    /**
+     * @var Collectable
+     */
+    private $collection;
+
     /**
     /**
      * @param Behavior $behaviour
      * @param Behavior $behaviour
+     * @param Resolvable $resolver
+     * @param Collectable $collection
      * @return self
      * @return self
      */
      */
-    public static function initialize(Behavior $behaviour)
-    {
+    public static function initialize(
+        Behavior $behaviour,
+        Resolvable $resolver = null,
+        Collectable $collection = null
+    ) {
         if (self::$instance === null) {
         if (self::$instance === null) {
-            self::$instance = new self($behaviour);
+            self::$instance = new self($behaviour, $resolver, $collection);
             return self::$instance;
             return self::$instance;
         }
         }
         throw new \LogicException(
         throw new \LogicException(
@@ -67,9 +86,22 @@ class Manager implements Assertable
 
 
     /**
     /**
      * @param Behavior $behaviour
      * @param Behavior $behaviour
+     * @param Resolvable $resolver
+     * @param Collectable $collection
      */
      */
-    private function __construct(Behavior $behaviour)
-    {
+    private function __construct(
+        Behavior $behaviour,
+        Resolvable $resolver = null,
+        Collectable $collection = null
+    ) {
+        if ($collection === null) {
+            $collection = new PharInvocationCollection();
+        }
+        if ($resolver === null) {
+            $resolver = new PharInvocationResolver();
+        }
+        $this->collection = $collection;
+        $this->resolver = $resolver;
         $this->behavior = $behaviour;
         $this->behavior = $behaviour;
     }
     }
 
 
@@ -82,4 +114,22 @@ class Manager implements Assertable
     {
     {
         return $this->behavior->assert($path, $command);
         return $this->behavior->assert($path, $command);
     }
     }
+
+    /**
+     * @param string $path
+     * @param null|int $flags
+     * @return null|PharInvocation
+     */
+    public function resolve($path, $flags = null)
+    {
+        return $this->resolver->resolve($path, $flags);
+    }
+
+    /**
+     * @return Collectable
+     */
+    public function getCollection()
+    {
+        return $this->collection;
+    }
 }
 }

+ 59 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Container.php

@@ -0,0 +1,59 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+class Container
+{
+    /**
+     * @var Stub
+     */
+    private $stub;
+
+    /**
+     * @var Manifest
+     */
+    private $manifest;
+
+    /**
+     * @param Stub $stub
+     * @param Manifest $manifest
+     */
+    public function __construct(Stub $stub, Manifest $manifest)
+    {
+        $this->stub = $stub;
+        $this->manifest = $manifest;
+    }
+
+    /**
+     * @return Stub
+     */
+    public function getStub()
+    {
+        return $this->stub;
+    }
+
+    /**
+     * @return Manifest
+     */
+    public function getManifest()
+    {
+        return $this->manifest;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAlias()
+    {
+        return $this->manifest->getAlias() ?: $this->stub->getMappedAlias();
+    }
+}

+ 18 - 0
misc/typo3/phar-stream-wrapper/src/Phar/DeserializationException.php

@@ -0,0 +1,18 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Exception;
+
+class DeserializationException extends Exception
+{
+}

+ 176 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Manifest.php

@@ -0,0 +1,176 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Brumann\Polyfill\Unserialize;
+
+class Manifest
+{
+    /**
+     * @param string $content
+     * @return self
+     * @see http://php.net/manual/en/phar.fileformat.phar.php
+     */
+    public static function fromContent($content)
+    {
+        $target = new static();
+        $target->manifestLength = Reader::resolveFourByteLittleEndian($content, 0);
+        $target->amountOfFiles = Reader::resolveFourByteLittleEndian($content, 4);
+        $target->flags = Reader::resolveFourByteLittleEndian($content, 10);
+        $target->aliasLength = Reader::resolveFourByteLittleEndian($content, 14);
+        $target->alias = substr($content, 18, $target->aliasLength);
+        $target->metaDataLength = Reader::resolveFourByteLittleEndian($content, 18 + $target->aliasLength);
+        $target->metaData = substr($content, 22 + $target->aliasLength, $target->metaDataLength);
+
+        $apiVersionNibbles = Reader::resolveTwoByteBigEndian($content, 8);
+        $target->apiVersion = implode('.', array(
+            ($apiVersionNibbles & 0xf000) >> 12,
+            ($apiVersionNibbles & 0x0f00) >> 8,
+            ($apiVersionNibbles & 0x00f0) >> 4,
+        ));
+
+        return $target;
+    }
+
+    /**
+     * @var int
+     */
+    private $manifestLength;
+
+    /**
+     * @var int
+     */
+    private $amountOfFiles;
+
+    /**
+     * @var string
+     */
+    private $apiVersion;
+
+    /**
+     * @var int
+     */
+    private $flags;
+
+    /**
+     * @var int
+     */
+    private $aliasLength;
+
+    /**
+     * @var string
+     */
+    private $alias;
+
+    /**
+     * @var int
+     */
+    private $metaDataLength;
+
+    /**
+     * @var string
+     */
+    private $metaData;
+
+    /**
+     * Avoid direct instantiation.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * @return int
+     */
+    public function getManifestLength()
+    {
+        return $this->manifestLength;
+    }
+
+    /**
+     * @return int
+     */
+    public function getAmountOfFiles()
+    {
+        return $this->amountOfFiles;
+    }
+
+    /**
+     * @return string
+     */
+    public function getApiVersion()
+    {
+        return $this->apiVersion;
+    }
+
+    /**
+     * @return int
+     */
+    public function getFlags()
+    {
+        return $this->flags;
+    }
+
+    /**
+     * @return int
+     */
+    public function getAliasLength()
+    {
+        return $this->aliasLength;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAlias()
+    {
+        return $this->alias;
+    }
+
+    /**
+     * @return int
+     */
+    public function getMetaDataLength()
+    {
+        return $this->metaDataLength;
+    }
+
+    /**
+     * @return string
+     */
+    public function getMetaData()
+    {
+        return $this->metaData;
+    }
+
+    /**
+     * @return mixed|null
+     */
+    public function deserializeMetaData()
+    {
+        if (empty($this->metaData)) {
+            return null;
+        }
+
+        $result = Unserialize::unserialize($this->metaData, array('allowed_classes' => false));
+
+        $serialized = json_encode($result);
+        if (strpos($serialized, '__PHP_Incomplete_Class_Name') !== false) {
+            throw new DeserializationException(
+                'Meta-data contains serialized object',
+                1539623382
+            );
+        }
+
+        return $result;
+    }
+}

+ 220 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Reader.php

@@ -0,0 +1,220 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+class Reader
+{
+    /**
+     * @var string
+     */
+    private $fileName;
+
+    /**
+     * @var string
+     */
+    private $fileType;
+
+    /**
+     * @param string $fileName
+     */
+    public function __construct($fileName)
+    {
+        if (strpos($fileName, '://') !== false) {
+            throw new ReaderException(
+                'File name must not contain stream prefix',
+                1539623708
+            );
+        }
+
+        $this->fileName = $fileName;
+        $this->fileType = $this->determineFileType();
+    }
+
+    /**
+     * @return Container
+     */
+    public function resolveContainer()
+    {
+        $data = $this->extractData($this->resolveStream() . $this->fileName);
+
+        if ($data['stubContent'] === null) {
+            throw new ReaderException(
+                'Cannot resolve stub',
+                1547807881
+            );
+        }
+        if ($data['manifestContent'] === null || $data['manifestLength'] === null) {
+            throw new ReaderException(
+                'Cannot resolve manifest',
+                1547807882
+            );
+        }
+        if (strlen($data['manifestContent']) < $data['manifestLength']) {
+            throw new ReaderException(
+                sprintf(
+                    'Exected manifest length %d, got %d',
+                    strlen($data['manifestContent']),
+                    $data['manifestLength']
+                ),
+                1547807883
+            );
+        }
+
+        return new Container(
+            Stub::fromContent($data['stubContent']),
+            Manifest::fromContent($data['manifestContent'])
+        );
+    }
+
+    /**
+     * @param string $fileName e.g. '/path/file.phar' or 'compress.zlib:///path/file.phar'
+     * @return array
+     */
+    private function extractData($fileName)
+    {
+        $stubContent = null;
+        $manifestContent = null;
+        $manifestLength = null;
+
+        $resource = fopen($fileName, 'r');
+        if (!is_resource($resource)) {
+            throw new ReaderException(
+                sprintf('Resource %s could not be opened', $fileName),
+                1547902055
+            );
+        }
+
+        while (!feof($resource)) {
+            $line = fgets($resource);
+            // stop reading file when manifest can be extracted
+            if ($manifestLength !== null && $manifestContent !== null && strlen($manifestContent) >= $manifestLength) {
+                break;
+            }
+
+            $manifestPosition = strpos($line, '__HALT_COMPILER();');
+
+            // first line contains start of manifest
+            if ($stubContent === null && $manifestContent === null && $manifestPosition !== false) {
+                $stubContent = substr($line, 0, $manifestPosition - 1);
+                $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
+                $manifestLength = $this->resolveManifestLength($manifestContent);
+            // line contains start of stub
+            } elseif ($stubContent === null) {
+                $stubContent = $line;
+            // line contains start of manifest
+            } elseif ($manifestContent === null && $manifestPosition !== false) {
+                $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
+                $manifestLength = $this->resolveManifestLength($manifestContent);
+            // manifest has been started (thus is cannot be stub anymore), add content
+            } elseif ($manifestContent !== null) {
+                $manifestContent .= $line;
+                $manifestLength = $this->resolveManifestLength($manifestContent);
+            // stub has been started (thus cannot be manifest here, yet), add content
+            } elseif ($stubContent !== null) {
+                $stubContent .= $line;
+            }
+        }
+        fclose($resource);
+
+        return array(
+            'stubContent' => $stubContent,
+            'manifestContent' => $manifestContent,
+            'manifestLength' => $manifestLength,
+        );
+    }
+
+    /**
+     * Resolves stream in order to handle compressed Phar archives.
+     *
+     * @return string
+     */
+    private function resolveStream()
+    {
+        if ($this->fileType === 'application/x-gzip') {
+            return 'compress.zlib://';
+        } elseif ($this->fileType === 'application/x-bzip2') {
+            return 'compress.bzip2://';
+        }
+        return '';
+    }
+
+    /**
+     * @return string
+     */
+    private function determineFileType()
+    {
+        $fileInfo = new \finfo();
+        return $fileInfo->file($this->fileName, FILEINFO_MIME_TYPE);
+    }
+
+    /**
+     * @param string $content
+     * @return int|null
+     */
+    private function resolveManifestLength($content)
+    {
+        if (strlen($content) < 4) {
+            return null;
+        }
+        return static::resolveFourByteLittleEndian($content, 0);
+    }
+
+    /**
+     * @param string $content
+     * @param int $start
+     * @return int
+     */
+    public static function resolveFourByteLittleEndian($content, $start)
+    {
+        $payload = substr($content, $start, 4);
+        if (!is_string($payload)) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614260
+            );
+        }
+
+        $value = unpack('V', $payload);
+        if (!isset($value[1])) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614261
+            );
+        }
+        return $value[1];
+    }
+
+    /**
+     * @param string $content
+     * @param int $start
+     * @return int
+     */
+    public static function resolveTwoByteBigEndian($content, $start)
+    {
+        $payload = substr($content, $start, 2);
+        if (!is_string($payload)) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614263
+            );
+        }
+
+        $value = unpack('n', $payload);
+        if (!isset($value[1])) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614264
+            );
+        }
+        return $value[1];
+    }
+}

+ 18 - 0
misc/typo3/phar-stream-wrapper/src/Phar/ReaderException.php

@@ -0,0 +1,18 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Exception;
+
+class ReaderException extends Exception
+{
+}

+ 65 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Stub.php

@@ -0,0 +1,65 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * @internal Experimental implementation of Phar archive internals
+ */
+class Stub
+{
+    /**
+     * @param string $content
+     * @return self
+     */
+    public static function fromContent($content)
+    {
+        $target = new static();
+        $target->content = $content;
+
+        if (
+            stripos($content, 'Phar::mapPhar(') !== false
+            && preg_match('#Phar\:\:mapPhar\(([^)]+)\)#', $content, $matches)
+        ) {
+            // remove spaces, single & double quotes
+            // @todo `'my' . 'alias' . '.phar'` is not evaluated here
+            $target->mappedAlias = trim($matches[1], ' \'"');
+        }
+
+        return $target;
+    }
+
+    /**
+     * @var string
+     */
+    private $content;
+
+    /**
+     * @var string
+     */
+    private $mappedAlias = '';
+
+    /**
+     * @return string
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * @return string
+     */
+    public function getMappedAlias()
+    {
+        return $this->mappedAlias;
+    }
+}

+ 36 - 2
misc/typo3/phar-stream-wrapper/src/PharStreamWrapper.php

@@ -11,6 +11,8 @@ namespace TYPO3\PharStreamWrapper;
  * The TYPO3 project - inspiring people to share!
  * The TYPO3 project - inspiring people to share!
  */
  */
 
 
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+
 class PharStreamWrapper
 class PharStreamWrapper
 {
 {
     /**
     /**
@@ -29,6 +31,11 @@ class PharStreamWrapper
      */
      */
     protected $internalResource;
     protected $internalResource;
 
 
+    /**
+     * @var PharInvocation
+     */
+    protected $invocation;
+
     /**
     /**
      * @return bool
      * @return bool
      */
      */
@@ -409,7 +416,8 @@ class PharStreamWrapper
      */
      */
     protected function assert($path, $command)
     protected function assert($path, $command)
     {
     {
-        if ($this->resolveAssertable()->assert($path, $command) === true) {
+        if (Manager::instance()->assert($path, $command) === true) {
+            $this->collectInvocation($path);
             return;
             return;
         }
         }
 
 
@@ -424,7 +432,33 @@ class PharStreamWrapper
     }
     }
 
 
     /**
     /**
-     * @return Assertable
+     * @param string $path
+     */
+    protected function collectInvocation($path)
+    {
+        if (isset($this->invocation)) {
+            return;
+        }
+
+        $manager = Manager::instance();
+        $this->invocation = $manager->resolve($path);
+        if ($this->invocation === null) {
+            throw new Exception(
+                'Expected invocation could not be resolved',
+                1556389591
+            );
+        }
+        // confirm, previous interceptor(s) validated invocation
+        $this->invocation->confirm();
+        $collection = $manager->getCollection();
+        if (!$collection->has($this->invocation)) {
+            $collection->collect($this->invocation);
+        }
+    }
+
+    /**
+     * @return Manager|Assertable
+     * @deprecated Use Manager::instance() directly
      */
      */
     protected function resolveAssertable()
     protected function resolveAssertable()
     {
     {

+ 24 - 0
misc/typo3/phar-stream-wrapper/src/Resolvable.php

@@ -0,0 +1,24 @@
+<?php
+namespace TYPO3\PharStreamWrapper;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+
+interface Resolvable
+{
+    /**
+     * @param string $path
+     * @param null|int $flags
+     * @return null|PharInvocation
+     */
+    public function resolve($path, $flags = null);
+}

+ 125 - 0
misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.php

@@ -0,0 +1,125 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Resolver;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Exception;
+
+class PharInvocation
+{
+    /**
+     * @var string
+     */
+    private $baseName;
+
+    /**
+     * @var string
+     */
+    private $alias;
+
+    /**
+     * @var bool
+     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
+     */
+    private $confirmed = false;
+
+    /**
+     * Arbitrary variables to be used by interceptors as registry
+     * (e.g. in order to avoid duplicate processing and assertions)
+     *
+     * @var array
+     */
+    private $variables;
+
+    /**
+     * @param string $baseName
+     * @param string $alias
+     */
+    public function __construct($baseName, $alias = '')
+    {
+        if ($baseName === '') {
+            throw new Exception(
+                'Base-name cannot be empty',
+                1551283689
+            );
+        }
+        $this->baseName = $baseName;
+        $this->alias = $alias;
+    }
+
+    /**
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->baseName;
+    }
+
+    /**
+     * @return string
+     */
+    public function getBaseName()
+    {
+        return $this->baseName;
+    }
+
+    /**
+     * @return null|string
+     */
+    public function getAlias()
+    {
+        return $this->alias;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isConfirmed()
+    {
+        return $this->confirmed;
+    }
+
+    public function confirm()
+    {
+        $this->confirmed = true;
+    }
+
+    /**
+     * @param string $name
+     * @return mixed|null
+     */
+    public function getVariable($name)
+    {
+        if (!isset($this->variables[$name])) {
+            return null;
+        }
+        return $this->variables[$name];
+    }
+
+    /**
+     * @param string $name
+     * @param mixed $value
+     */
+    public function setVariable($name, $value)
+    {
+        $this->variables[$name] = $value;
+    }
+
+    /**
+     * @param PharInvocation $other
+     * @return bool
+     */
+    public function equals(PharInvocation $other)
+    {
+        return $other->baseName === $this->baseName
+            && $other->alias === $this->alias;
+    }
+}

+ 156 - 0
misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.php

@@ -0,0 +1,156 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Resolver;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Collectable;
+
+class PharInvocationCollection implements Collectable
+{
+    const UNIQUE_INVOCATION = 1;
+    const UNIQUE_BASE_NAME = 2;
+    const DUPLICATE_ALIAS_WARNING = 32;
+
+    /**
+     * @var PharInvocation[]
+     */
+    private $invocations = array();
+
+    /**
+     * @param PharInvocation $invocation
+     * @return bool
+     */
+    public function has(PharInvocation $invocation)
+    {
+        return in_array($invocation, $this->invocations, true);
+    }
+
+    /**
+     * @param PharInvocation $invocation
+     * @param null|int $flags
+     * @return bool
+     */
+    public function collect(PharInvocation $invocation, $flags = null)
+    {
+        if ($flags === null) {
+            $flags = static::UNIQUE_INVOCATION | static::DUPLICATE_ALIAS_WARNING;
+        }
+        if ($invocation->getBaseName() === ''
+            || $invocation->getAlias() === ''
+            || !$this->assertUniqueBaseName($invocation, $flags)
+            || !$this->assertUniqueInvocation($invocation, $flags)
+        ) {
+            return false;
+        }
+        if ($flags & static::DUPLICATE_ALIAS_WARNING) {
+            $this->triggerDuplicateAliasWarning($invocation);
+        }
+
+        $this->invocations[] = $invocation;
+        return true;
+    }
+
+    /**
+     * @param callable $callback
+     * @param bool $reverse
+     * @return null|PharInvocation
+     */
+    public function findByCallback($callback, $reverse = false)
+    {
+        foreach ($this->getInvocations($reverse) as $invocation) {
+            if (call_user_func($callback, $invocation) === true) {
+                return $invocation;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Asserts that base-name is unique. This disallows having multiple invocations for
+     * same base-name but having different alias names.
+     *
+     * @param PharInvocation $invocation
+     * @param int $flags
+     * @return bool
+     */
+    private function assertUniqueBaseName(PharInvocation $invocation, $flags)
+    {
+        if (!($flags & static::UNIQUE_BASE_NAME)) {
+            return true;
+        }
+        return $this->findByCallback(
+                function (PharInvocation $candidate) use ($invocation) {
+                    return $candidate->getBaseName() === $invocation->getBaseName();
+                }
+            ) === null;
+    }
+
+    /**
+     * Asserts that combination of base-name and alias is unique. This allows having multiple
+     * invocations for same base-name but having different alias names (for whatever reason).
+     *
+     * @param PharInvocation $invocation
+     * @param int $flags
+     * @return bool
+     */
+    private function assertUniqueInvocation(PharInvocation $invocation, $flags)
+    {
+        if (!($flags & static::UNIQUE_INVOCATION)) {
+            return true;
+        }
+        return $this->findByCallback(
+                function (PharInvocation $candidate) use ($invocation) {
+                    return $candidate->equals($invocation);
+                }
+            ) === null;
+    }
+
+    /**
+     * Triggers warning for invocations with same alias and same confirmation state.
+     *
+     * @param PharInvocation $invocation
+     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
+     */
+    private function triggerDuplicateAliasWarning(PharInvocation $invocation)
+    {
+        $sameAliasInvocation = $this->findByCallback(
+            function (PharInvocation $candidate) use ($invocation) {
+                return $candidate->isConfirmed() === $invocation->isConfirmed()
+                    && $candidate->getAlias() === $invocation->getAlias();
+            },
+            true
+        );
+        if ($sameAliasInvocation === null) {
+            return;
+        }
+        trigger_error(
+            sprintf(
+                'Alias %s cannot be used by %s, already used by %s',
+                $invocation->getAlias(),
+                $invocation->getBaseName(),
+                $sameAliasInvocation->getBaseName()
+            ),
+            E_USER_WARNING
+        );
+    }
+
+    /**
+     * @param bool $reverse
+     * @return PharInvocation[]
+     */
+    private function getInvocations($reverse = false)
+    {
+        if ($reverse) {
+            return array_reverse($this->invocations);
+        }
+        return $this->invocations;
+    }
+}

+ 241 - 0
misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php

@@ -0,0 +1,241 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Resolver;
+
+/*
+ * This file is part of the TYPO3 project.
+ *
+ * It is free software; you can redistribute it and/or modify it under the terms
+ * of the MIT License (MIT). For the full copyright and license information,
+ * please read the LICENSE file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\PharStreamWrapper\Helper;
+use TYPO3\PharStreamWrapper\Manager;
+use TYPO3\PharStreamWrapper\Phar\Reader;
+use TYPO3\PharStreamWrapper\Resolvable;
+
+class PharInvocationResolver implements Resolvable
+{
+    const RESOLVE_REALPATH = 1;
+    const RESOLVE_ALIAS = 2;
+    const ASSERT_INTERNAL_INVOCATION = 32;
+
+    /**
+     * @var string[]
+     */
+    private $invocationFunctionNames = array(
+        'include',
+        'include_once',
+        'require',
+        'require_once'
+    );
+
+    /**
+     * Contains resolved base names in order to reduce file IO.
+     *
+     * @var string[]
+     */
+    private $baseNames = array();
+
+    /**
+     * Resolves PharInvocation value object (baseName and optional alias).
+     *
+     * Phar aliases are intended to be used only inside Phar archives, however
+     * PharStreamWrapper needs this information exposed outside of Phar as well
+     * It is possible that same alias is used for different $baseName values.
+     * That's why PharInvocationCollection behaves like a stack when resolving
+     * base-name for a given alias. On the other hand it is not possible that
+     * one $baseName is referring to multiple aliases.
+     * @see https://secure.php.net/manual/en/phar.setalias.php
+     * @see https://secure.php.net/manual/en/phar.mapphar.php
+     *
+     * @param string $path
+     * @param int|null $flags
+     * @return null|PharInvocation
+     */
+    public function resolve($path, $flags = null)
+    {
+        $hasPharPrefix = Helper::hasPharPrefix($path);
+        if ($flags === null) {
+            $flags = static::RESOLVE_REALPATH | static::RESOLVE_ALIAS | static::ASSERT_INTERNAL_INVOCATION;
+        }
+
+        if ($hasPharPrefix && $flags & static::RESOLVE_ALIAS) {
+            $invocation = $this->findByAlias($path);
+            if ($invocation !== null) {
+                return $invocation;
+            }
+        }
+
+        $baseName = $this->resolveBaseName($path, $flags);
+        if ($baseName === null) {
+            return null;
+        }
+
+        if ($flags & static::RESOLVE_REALPATH) {
+            $baseName = $this->baseNames[$baseName];
+        }
+
+        return $this->retrieveInvocation($baseName, $flags);
+    }
+
+    /**
+     * Retrieves PharInvocation, either existing in collection or created on demand
+     * with resolving a potential alias name used in the according Phar archive.
+     *
+     * @param string $baseName
+     * @param int $flags
+     * @return PharInvocation
+     */
+    private function retrieveInvocation($baseName, $flags)
+    {
+        $invocation = $this->findByBaseName($baseName);
+        if ($invocation !== null) {
+            return $invocation;
+        }
+
+        if ($flags & static::RESOLVE_ALIAS) {
+            $reader = new Reader($baseName);
+            $alias = $reader->resolveContainer()->getAlias();
+        } else {
+            $alias = '';
+        }
+        // add unconfirmed(!) new invocation to collection
+        $invocation = new PharInvocation($baseName, $alias);
+        Manager::instance()->getCollection()->collect($invocation);
+        return $invocation;
+    }
+
+    /**
+     * @param string $path
+     * @param int $flags
+     * @return null|string
+     */
+    private function resolveBaseName($path, $flags)
+    {
+        $baseName = $this->findInBaseNames($path);
+        if ($baseName !== null) {
+            return $baseName;
+        }
+
+        $baseName = Helper::determineBaseFile($path);
+        if ($baseName !== null) {
+            $this->addBaseName($baseName);
+            return $baseName;
+        }
+
+        $possibleAlias = $this->resolvePossibleAlias($path);
+        if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias === null) {
+            return null;
+        }
+
+        $trace = debug_backtrace();
+        foreach ($trace as $item) {
+            if (!isset($item['function']) || !isset($item['args'][0])
+                || !in_array($item['function'], $this->invocationFunctionNames, true)) {
+                continue;
+            }
+            $currentPath = $item['args'][0];
+            if (Helper::hasPharPrefix($currentPath)) {
+                continue;
+            }
+            $currentBaseName = Helper::determineBaseFile($currentPath);
+            if ($currentBaseName === null) {
+                continue;
+            }
+            // ensure the possible alias name (how we have been called initially) matches
+            // the resolved alias name that was retrieved by the current possible base name
+            $reader = new Reader($currentBaseName);
+            $currentAlias = $reader->resolveContainer()->getAlias();
+            if ($currentAlias !== $possibleAlias) {
+                continue;
+            }
+            $this->addBaseName($currentBaseName);
+            return $currentBaseName;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $path
+     * @return null|string
+     */
+    private function resolvePossibleAlias($path)
+    {
+        $normalizedPath = Helper::normalizePath($path);
+        return strstr($normalizedPath, '/', true) ?: null;
+    }
+
+    /**
+     * @param string $baseName
+     * @return null|PharInvocation
+     */
+    private function findByBaseName($baseName)
+    {
+        return Manager::instance()->getCollection()->findByCallback(
+            function (PharInvocation $candidate) use ($baseName) {
+                return $candidate->getBaseName() === $baseName;
+            },
+            true
+        );
+    }
+
+    /**
+     * @param string $path
+     * @return null|string
+     */
+    private function findInBaseNames($path)
+    {
+        // return directly if the resolved base name was submitted
+        if (in_array($path, $this->baseNames, true)) {
+            return $path;
+        }
+
+        $parts = explode('/', Helper::normalizePath($path));
+
+        while (count($parts)) {
+            $currentPath = implode('/', $parts);
+            if (isset($this->baseNames[$currentPath])) {
+                return $currentPath;
+            }
+            array_pop($parts);
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $baseName
+     */
+    private function addBaseName($baseName)
+    {
+        if (isset($this->baseNames[$baseName])) {
+            return;
+        }
+        $this->baseNames[$baseName] = realpath($baseName);
+    }
+
+    /**
+     * Finds confirmed(!) invocations by alias.
+     *
+     * @param string $path
+     * @return null|PharInvocation
+     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
+     */
+    private function findByAlias($path)
+    {
+        $possibleAlias = $this->resolvePossibleAlias($path);
+        if ($possibleAlias === null) {
+            return null;
+        }
+        return Manager::instance()->getCollection()->findByCallback(
+            function (PharInvocation $candidate) use ($possibleAlias) {
+                return $candidate->isConfirmed() && $candidate->getAlias() === $possibleAlias;
+            },
+            true
+        );
+    }
+}

+ 3 - 3
modules/aggregator/aggregator.info

@@ -7,7 +7,7 @@ files[] = aggregator.test
 configure = admin/config/services/aggregator/settings
 configure = admin/config/services/aggregator/settings
 stylesheets[all][] = aggregator.css
 stylesheets[all][] = aggregator.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/aggregator/tests/aggregator_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/block/block.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = block.test
 files[] = block.test
 configure = admin/structure/block
 configure = admin/structure/block
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/block/tests/block_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/block/tests/themes/block_test_theme/block_test_theme.info

@@ -13,7 +13,7 @@ regions[footer] = Footer
 regions[highlighted] = Highlighted
 regions[highlighted] = Highlighted
 regions[help] = Help
 regions[help] = Help
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/blog/blog.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = blog.test
 files[] = blog.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/book/book.info

@@ -7,7 +7,7 @@ files[] = book.test
 configure = admin/content/book/settings
 configure = admin/content/book/settings
 stylesheets[all][] = book.css
 stylesheets[all][] = book.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/color/color.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = color.test
 files[] = color.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/comment/comment.info

@@ -9,7 +9,7 @@ files[] = comment.test
 configure = admin/content/comment
 configure = admin/content/comment
 stylesheets[all][] = comment.css
 stylesheets[all][] = comment.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/contact/contact.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = contact.test
 files[] = contact.test
 configure = admin/structure/contact
 configure = admin/structure/contact
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/contextual/contextual.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = contextual.test
 files[] = contextual.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/dashboard/dashboard.info

@@ -7,7 +7,7 @@ files[] = dashboard.test
 dependencies[] = block
 dependencies[] = block
 configure = admin/dashboard/customize
 configure = admin/dashboard/customize
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/dblog/dblog.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = dblog.test
 files[] = dblog.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/field.info

@@ -11,7 +11,7 @@ dependencies[] = field_sql_storage
 required = TRUE
 required = TRUE
 stylesheets[all][] = theme/field.css
 stylesheets[all][] = theme/field.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/field_sql_storage/field_sql_storage.info

@@ -7,7 +7,7 @@ dependencies[] = field
 files[] = field_sql_storage.test
 files[] = field_sql_storage.test
 required = TRUE
 required = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/list/list.info

@@ -7,7 +7,7 @@ dependencies[] = field
 dependencies[] = options
 dependencies[] = options
 files[] = tests/list.test
 files[] = tests/list.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/list/tests/list_test.info

@@ -5,7 +5,7 @@ package = Testing
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/number/number.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = number.test
 files[] = number.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 1 - 1
modules/field/modules/number/number.test

@@ -69,7 +69,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
     preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
     preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
     $id = $match[1];
     $id = $match[1];
     $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
     $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
-    $this->assertRaw(round($value, 2), 'Value is displayed.');
+    $this->assertRaw($value, 'Value is displayed.');
 
 
     // Try to create entries with more than one decimal separator; assert fail.
     // Try to create entries with more than one decimal separator; assert fail.
     $wrong_entries = array(
     $wrong_entries = array(

+ 3 - 3
modules/field/modules/options/options.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = options.test
 files[] = options.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/text/text.info

@@ -7,7 +7,7 @@ dependencies[] = field
 files[] = text.test
 files[] = text.test
 required = TRUE
 required = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/tests/field_test.info

@@ -6,7 +6,7 @@ files[] = field_test.entity.inc
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field_ui/field_ui.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = field_ui.test
 files[] = field_ui.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 1 - 1
modules/file/file.field.inc

@@ -599,7 +599,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) {
     // If the display field is present make sure its unchecked value is saved.
     // If the display field is present make sure its unchecked value is saved.
     $field = field_widget_field($element, $form_state);
     $field = field_widget_field($element, $form_state);
     if (empty($input['display'])) {
     if (empty($input['display'])) {
-      $input['display'] = $field['settings']['display_field'] ? 0 : 1;
+      $input['display'] = !empty($field['settings']['display_field']) ? 0 : 1;
     }
     }
   }
   }
 
 

+ 3 - 3
modules/file/file.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 dependencies[] = field
 files[] = tests/file.test
 files[] = tests/file.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 57 - 0
modules/file/tests/file.test

@@ -1875,3 +1875,60 @@ class FileFieldAnonymousSubmission extends FileFieldTestCase {
   }
   }
 
 
 }
 }
+
+/**
+ * Tests the file_scan_directory() function.
+ */
+class FileScanDirectory extends FileFieldTestCase {
+
+  /**
+   * @var string
+   */
+  protected $path;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'File ScanDirectory',
+      'description' => 'Tests the file_scan_directory() function.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->path = 'modules/file/tests/fixtures/file_scan_ignore';
+  }
+
+  /**
+   * Tests file_scan_directory() obeys 'file_scan_ignore_directories' setting.
+   * If nomask is not passed as argument, it should use the default settings.
+   * If nomask is passed as argument, it should obey this rule.
+   */
+  public function testNoMask() {
+    $files = file_scan_directory($this->path, '/\.txt$/');
+    $this->assertEqual(3, count($files), '3 text files found when not ignoring directories.');
+
+    global $conf;
+    $conf['file_scan_ignore_directories'] = array('frontend_framework');
+
+    $files = file_scan_directory($this->path, '/\.txt$/');
+    $this->assertEqual(1, count($files), '1 text files found when ignoring directories called "frontend_framework".');
+
+    // Make that directories specified by default still work when a new nomask is provided.
+    $files = file_scan_directory($this->path, '/\.txt$/', array('nomask' => '/^c.txt/'));
+    $this->assertEqual(2, count($files), '2 text files found when an "nomask" option is passed in.');
+
+    // Ensure that the directories in file_scan_ignore_directories are escaped using preg_quote.
+    $conf['file_scan_ignore_directories'] = array('frontend.*');
+    $files = file_scan_directory($this->path, '/\.txt$/');
+    $this->assertEqual(3, count($files), '2 text files found when ignoring a directory that is not there.');
+  }
+
+}

+ 3 - 3
modules/file/tests/file_module_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 0 - 0
modules/file/tests/fixtures/file_scan_ignore/a.txt


+ 0 - 0
modules/file/tests/fixtures/file_scan_ignore/frontend_framework/b.txt


+ 0 - 0
modules/file/tests/fixtures/file_scan_ignore/frontend_framework/c.txt


+ 3 - 3
modules/filter/filter.info

@@ -7,7 +7,7 @@ files[] = filter.test
 required = TRUE
 required = TRUE
 configure = admin/config/content/formats
 configure = admin/config/content/formats
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/forum/forum.info

@@ -9,7 +9,7 @@ files[] = forum.test
 configure = admin/structure/forum
 configure = admin/structure/forum
 stylesheets[all][] = forum.css
 stylesheets[all][] = forum.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/help/help.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = help.test
 files[] = help.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/image/image.info

@@ -7,7 +7,7 @@ dependencies[] = file
 files[] = image.test
 files[] = image.test
 configure = admin/config/media/image-styles
 configure = admin/config/media/image-styles
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/image/tests/image_module_test.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = image_module_test.module
 files[] = image_module_test.module
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/locale/locale.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = locale.test
 files[] = locale.test
 configure = admin/config/regional/language
 configure = admin/config/regional/language
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/locale/tests/locale_test.info

@@ -5,7 +5,7 @@ package = Testing
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/menu/menu.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = menu.test
 files[] = menu.test
 configure = admin/structure/menu
 configure = admin/structure/menu
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/node/node.info

@@ -9,7 +9,7 @@ required = TRUE
 configure = admin/structure/types
 configure = admin/structure/types
 stylesheets[all][] = node.css
 stylesheets[all][] = node.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/node/tests/node_access_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/node/tests/node_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/node/tests/node_test_exception.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/openid/openid.info

@@ -5,7 +5,7 @@ package = Core
 core = 7.x
 core = 7.x
 files[] = openid.test
 files[] = openid.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/openid/tests/openid_test.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = openid
 dependencies[] = openid
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/overlay/overlay.info

@@ -4,7 +4,7 @@ package = Core
 version = VERSION
 version = VERSION
 core = 7.x
 core = 7.x
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/path/path.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = path.test
 files[] = path.test
 configure = admin/config/search/path
 configure = admin/config/search/path
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/php/php.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = php.test
 files[] = php.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/poll/poll.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = poll.test
 files[] = poll.test
 stylesheets[all][] = poll.css
 stylesheets[all][] = poll.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/profile/profile.info

@@ -11,7 +11,7 @@ configure = admin/config/people/profile
 ; See user_system_info_alter().
 ; See user_system_info_alter().
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/rdf/rdf.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 files[] = rdf.test
 files[] = rdf.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/rdf/tests/rdf_test.info

@@ -6,7 +6,7 @@ core = 7.x
 hidden = TRUE
 hidden = TRUE
 dependencies[] = blog
 dependencies[] = blog
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/search/search.info

@@ -8,7 +8,7 @@ files[] = search.test
 configure = admin/config/search/settings
 configure = admin/config/search/settings
 stylesheets[all][] = search.css
 stylesheets[all][] = search.css
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/search/tests/search_embedded_form.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/search/tests/search_extra_type.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/search/tests/search_node_tags.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/shortcut/shortcut.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = shortcut.test
 files[] = shortcut.test
 configure = admin/config/user-interface/shortcut
 configure = admin/config/user-interface/shortcut
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 2 - 2
modules/simpletest/drupal_web_test_case.php

@@ -3012,7 +3012,7 @@ class DrupalWebTestCase extends DrupalTestCase {
     if (!$message) {
     if (!$message) {
       $message = t('Raw "@raw" found', array('@raw' => $raw));
       $message = t('Raw "@raw" found', array('@raw' => $raw));
     }
     }
-    return $this->assert(strpos($this->drupalGetContent(), $raw) !== FALSE, $message, $group);
+    return $this->assert(strpos($this->drupalGetContent(), (string) $raw) !== FALSE, $message, $group);
   }
   }
 
 
   /**
   /**
@@ -3032,7 +3032,7 @@ class DrupalWebTestCase extends DrupalTestCase {
     if (!$message) {
     if (!$message) {
       $message = t('Raw "@raw" not found', array('@raw' => $raw));
       $message = t('Raw "@raw" not found', array('@raw' => $raw));
     }
     }
-    return $this->assert(strpos($this->drupalGetContent(), $raw) === FALSE, $message, $group);
+    return $this->assert(strpos($this->drupalGetContent(), (string) $raw) === FALSE, $message, $group);
   }
   }
 
 
   /**
   /**

+ 3 - 3
modules/simpletest/simpletest.info

@@ -57,7 +57,7 @@ files[] = tests/upgrade/update.trigger.test
 files[] = tests/upgrade/update.field.test
 files[] = tests/upgrade/update.field.test
 files[] = tests/upgrade/update.user.test
 files[] = tests/upgrade/update.user.test
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/simpletest/tests/actions_loop_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/simpletest/tests/ajax_forms_test.info

@@ -5,7 +5,7 @@ package = Testing
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/simpletest/tests/ajax_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/simpletest/tests/batch_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 core = 7.x
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/simpletest/tests/boot_test_1.info

@@ -5,7 +5,7 @@ package = Testing
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/simpletest/tests/boot_test_2.info

@@ -5,7 +5,7 @@ package = Testing
 version = VERSION
 version = VERSION
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 2 - 6
modules/simpletest/tests/bootstrap.test

@@ -729,16 +729,12 @@ class BootstrapMiscTestCase extends DrupalUnitTestCase {
    * Tests that the drupal_check_memory_limit() function works as expected.
    * Tests that the drupal_check_memory_limit() function works as expected.
    */
    */
   function testCheckMemoryLimit() {
   function testCheckMemoryLimit() {
-    $memory_limit = ini_get('memory_limit');
     // Test that a very reasonable amount of memory is available.
     // Test that a very reasonable amount of memory is available.
     $this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
     $this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
 
 
-    // Get the available memory and multiply it by two to make it unreasonably
-    // high.
-    $twice_avail_memory = ($memory_limit * 2) . 'MB';
-
+    // Test an unlimited memory limit.
     // The function should always return true if the memory limit is set to -1.
     // The function should always return true if the memory limit is set to -1.
-    $this->assertTrue(drupal_check_memory_limit($twice_avail_memory, -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
+    $this->assertTrue(drupal_check_memory_limit('9999999999YB', -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
 
 
     // Test that even though we have 30MB of memory available - the function
     // Test that even though we have 30MB of memory available - the function
     // returns FALSE when given an upper limit for how much memory can be used.
     // returns FALSE when given an upper limit for how much memory can be used.

+ 3 - 3
modules/simpletest/tests/common_test.info

@@ -7,7 +7,7 @@ stylesheets[all][] = common_test.css
 stylesheets[print][] = common_test.print.css
 stylesheets[print][] = common_test.print.css
 hidden = TRUE
 hidden = TRUE
 
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

Some files were not shown because too many files changed in this diff