From 9daa11eb108ff8e82e63816d371939d0415c1247 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 18 Dec 2023 21:27:36 -0500 Subject: [PATCH 1/2] [woodpecker] Remove PHP 7.3 PHPUnit instance - Friendica now supports at least PHP 7.4 --- .woodpecker/.phpunit.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.woodpecker/.phpunit.yml b/.woodpecker/.phpunit.yml index f198ba2a..a6c5db09 100644 --- a/.woodpecker/.phpunit.yml +++ b/.woodpecker/.phpunit.yml @@ -1,7 +1,5 @@ matrix: include: - - PHP_MAJOR_VERSION: 7.3 - PHP_VERSION: 7.3.33 - PHP_MAJOR_VERSION: 7.4 PHP_VERSION: 7.4.33 - PHP_MAJOR_VERSION: 8.0 From 3e74af9775ac9863c559422275e6322345a2b73a Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 18 Dec 2023 21:28:16 -0500 Subject: [PATCH 2/2] [s3_storage] Bump version of akeeba/s3 to version 2.3.1 - Address https://github.com/friendica/friendica/issues/12011#issuecomment-1854681792 --- s3_storage/composer.lock | 19 +- s3_storage/vendor/akeeba/s3/.gitignore | 3 +- s3_storage/vendor/akeeba/s3/README.md | 87 +++-- s3_storage/vendor/akeeba/s3/TODO.md | 13 - s3_storage/vendor/akeeba/s3/composer.json | 17 +- s3_storage/vendor/akeeba/s3/composer.lock | 37 +- s3_storage/vendor/akeeba/s3/minitest/NOTES.md | 62 +++ .../akeeba/s3/minitest/Test/AbstractTest.php | 16 +- .../akeeba/s3/minitest/Test/BigFiles.php | 46 +-- .../s3/minitest/Test/BucketLocation.php | 6 +- .../akeeba/s3/minitest/Test/BucketsList.php | 22 +- .../akeeba/s3/minitest/Test/HeadObject.php | 67 ++++ .../akeeba/s3/minitest/Test/ListFiles.php | 214 +++++----- .../s3/minitest/Test/ListThousandsOfFiles.php | 111 ++++++ .../akeeba/s3/minitest/Test/Multipart.php | 28 +- .../akeeba/s3/minitest/Test/SignedURLs.php | 16 +- .../s3/minitest/Test/SingleSmallFile.php | 43 ++ .../akeeba/s3/minitest/Test/SmallFiles.php | 28 +- .../s3/minitest/Test/SmallFilesNoDelete.php | 6 +- .../s3/minitest/Test/SmallFilesOnlyUpload.php | 8 +- .../s3/minitest/Test/SmallInlineFiles.php | 15 +- .../Test/SmallInlineFilesNoDelete.php | 6 +- .../Test/SmallInlineFilesOnlyUpload.php | 8 +- .../s3/minitest/Test/SmallInlineXMLFiles.php | 131 +++++++ .../s3/minitest/Test/StorageClasses.php | 22 +- .../vendor/akeeba/s3/minitest/config.dist.php | 8 +- .../vendor/akeeba/s3/minitest/minitest.php | 22 +- s3_storage/vendor/akeeba/s3/src/Acl.php | 18 +- .../vendor/akeeba/s3/src/Configuration.php | 14 +- s3_storage/vendor/akeeba/s3/src/Connector.php | 367 ++++++++++-------- .../s3/src/Exception/CannotDeleteFile.php | 6 +- .../s3/src/Exception/CannotGetBucket.php | 6 +- .../akeeba/s3/src/Exception/CannotGetFile.php | 6 +- .../s3/src/Exception/CannotListBuckets.php | 6 +- .../src/Exception/CannotOpenFileForRead.php | 6 +- .../src/Exception/CannotOpenFileForWrite.php | 6 +- .../akeeba/s3/src/Exception/CannotPutFile.php | 6 +- .../s3/src/Exception/ConfigurationError.php | 6 +- .../s3/src/Exception/InvalidAccessKey.php | 6 +- .../akeeba/s3/src/Exception/InvalidBody.php | 6 +- .../s3/src/Exception/InvalidEndpoint.php | 6 +- .../s3/src/Exception/InvalidFilePointer.php | 6 +- .../akeeba/s3/src/Exception/InvalidRegion.php | 6 +- .../s3/src/Exception/InvalidSecretKey.php | 6 +- .../src/Exception/InvalidSignatureMethod.php | 6 +- .../s3/src/Exception/PropertyNotFound.php | 6 +- s3_storage/vendor/akeeba/s3/src/Input.php | 52 ++- s3_storage/vendor/akeeba/s3/src/Request.php | 124 ++++-- s3_storage/vendor/akeeba/s3/src/Response.php | 30 +- .../vendor/akeeba/s3/src/Response/Error.php | 6 +- s3_storage/vendor/akeeba/s3/src/Signature.php | 8 +- .../vendor/akeeba/s3/src/Signature/V2.php | 14 +- .../vendor/akeeba/s3/src/Signature/V4.php | 170 ++++++-- .../vendor/akeeba/s3/src/StorageClass.php | 18 +- s3_storage/vendor/akeeba/s3/src/aliasing.php | 35 ++ .../vendor/composer/autoload_classmap.php | 54 +-- s3_storage/vendor/composer/autoload_files.php | 10 + s3_storage/vendor/composer/autoload_psr4.php | 2 +- s3_storage/vendor/composer/autoload_real.php | 18 + .../vendor/composer/autoload_static.php | 62 +-- s3_storage/vendor/composer/installed.json | 21 +- 61 files changed, 1472 insertions(+), 708 deletions(-) delete mode 100644 s3_storage/vendor/akeeba/s3/TODO.md create mode 100644 s3_storage/vendor/akeeba/s3/minitest/NOTES.md create mode 100644 s3_storage/vendor/akeeba/s3/minitest/Test/HeadObject.php create mode 100644 s3_storage/vendor/akeeba/s3/minitest/Test/ListThousandsOfFiles.php create mode 100644 s3_storage/vendor/akeeba/s3/minitest/Test/SingleSmallFile.php create mode 100644 s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineXMLFiles.php create mode 100644 s3_storage/vendor/akeeba/s3/src/aliasing.php create mode 100644 s3_storage/vendor/composer/autoload_files.php diff --git a/s3_storage/composer.lock b/s3_storage/composer.lock index 78b10dd9..992e5a9e 100644 --- a/s3_storage/composer.lock +++ b/s3_storage/composer.lock @@ -8,32 +8,35 @@ "packages": [ { "name": "akeeba/s3", - "version": "2.0.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/akeeba/s3.git", - "reference": "01520dae1f736555e08efda0ddc1044701bd340a" + "reference": "7f5b3e929c93eb02ba24472560c0cbbef735aed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/akeeba/s3/zipball/01520dae1f736555e08efda0ddc1044701bd340a", - "reference": "01520dae1f736555e08efda0ddc1044701bd340a", + "url": "https://api.github.com/repos/akeeba/s3/zipball/7f5b3e929c93eb02ba24472560c0cbbef735aed9", + "reference": "7f5b3e929c93eb02ba24472560c0cbbef735aed9", "shasum": "" }, "require": { "ext-curl": "*", "ext-simplexml": "*", - "php": ">=7.1.0 <8.1" + "php": ">=7.1.0 <8.4" }, "type": "library", "autoload": { + "files": [ + "src/aliasing.php" + ], "psr-4": { - "Akeeba\\Engine\\Postproc\\Connector\\S3v4\\": "src" + "Akeeba\\S3\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "GPL-3.0+" + "GPL-3.0-or-later" ], "authors": [ { @@ -48,7 +51,7 @@ "keywords": [ "s3" ], - "time": "2020-11-30T14:03:55+00:00" + "time": "2023-09-26T11:40:10+00:00" } ], "packages-dev": [], diff --git a/s3_storage/vendor/akeeba/s3/.gitignore b/s3_storage/vendor/akeeba/s3/.gitignore index 54f46f28..09e75c29 100644 --- a/s3_storage/vendor/akeeba/s3/.gitignore +++ b/s3_storage/vendor/akeeba/s3/.gitignore @@ -1,5 +1,6 @@ /.idea/ /000/ -/minitest/config.php +/minitest/config.* +!/minitest/config.dist.php /minitest/tmp /vendor/ diff --git a/s3_storage/vendor/akeeba/s3/README.md b/s3_storage/vendor/akeeba/s3/README.md index d082720c..eb3c5b53 100644 --- a/s3_storage/vendor/akeeba/s3/README.md +++ b/s3_storage/vendor/akeeba/s3/README.md @@ -4,29 +4,53 @@ A compact, dependency-less Amazon S3 API client implementing the most commonly u ## Why reinvent the wheel -After having a lot of impossible to debug problems with Amazon's Guzzle-based AWS SDK we decided to roll our own connector for Amazon S3. This is by no means a complete implementation, just a small subset of S3's features which are required by our software. The design goals are simplicity, no external dependencies and low memory footprint. +After having a lot of impossible to debug problems with Amazon's Guzzle-based AWS SDK we decided to roll our own connector for Amazon S3. This is by no means a complete implementation, just a small subset of S3's features which are required by our software. The design goals are simplicity, no external dependencies and a low memory footprint. This code was originally based on [S3.php written by Donovan Schonknecht](http://undesigned.org.za/2007/10/22/amazon-s3-php-class) which is available under a BSD-like license. This repository no longer reflects the original author's work and should not be confused with it. -This software is distributed under the GNU General Public License version 3 or, at your option, any later version published by the Free Software Foundation (FSF). In short, it's "GPLv3+". +This software is distributed under the GNU General Public License version 3 or, at your option, any later version published by the Free Software Foundation (FSF). In short, it's GPL-3.0-or-later, as noted in composer.json. -## Important note about version 2 +## Important notes about version 2 -Akeeba Amazon S3 Connector version 2 has dropped support for PPH 5.3 to 7.0 inclusive. It is only compatible with PHP 7.1 or later, up to and including PHP 8.0. +### PHP version support since 2.0 -The most significant change in this version is that all methods use scalar type hints for parameters and return values. This _may_ break existing consumers which relied on implicit type conversion e.g. passing strings containing integer values instead of _actual_ integer values. +Akeeba Amazon S3 Connector version 2 has dropped support for PHP 5.3 to 7.0 inclusive. + +The most significant change in this version is that all methods use scalar type hints for parameters and return values. This _may_ break existing consumers which relied on implicit type conversion. + +### Namespace change since 2.3 + +Up to and including version 2.2 of the library, the namespace was `\Akeeba\Engine\Postproc\Connector\S3v4`. From version 2.3 of the library the namespace has changed to `\Akeeba\S3`. + +The library automatically registers aliases of the old classes to the new ones, thus ensuring updating the library will not introduce backwards incompatible changes. This is why it's not a major version update. Aliases will remain in place until at least version 3.0 of the library. ## Using the connector +You need to define a constant before using or referencing any class in the library: + +```php +defined('AKEEBAENGINE') or define('AKEEBAENGINE', 1); +``` + +All library files have a line similar to + +```php +defined('AKEEBAENGINE') or die(); +``` + +to prevent direct access to the libraries files. This is intentional. The primary use case for this library is mass-distributed software which gets installed in a publicly accessible subdirectory of the web root. This line prevents any accidental path disclosure from PHP error messages if someone were to access these files directly on misconfigured servers. + +If you are writing a Joomla extension, especially a plugin or module, please _always_ check if the constant has already been defined before defining it yourself. Thank you! + ### Get a connector object ```php -$configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( +$configuration = new \Akeeba\S3\Configuration( 'YourAmazonAccessKey', 'YourAmazonSecretKey' ); -$connector = new \Akeeba\Engine\Postproc\Connector\S3v4\Connector($configuration); +$connector = new \Akeeba\S3\Connector($configuration); ``` If you are running inside an Amazon EC2 instance you can fetch temporary credentials from the instance's metadata @@ -37,7 +61,7 @@ IP hosting the instance's metadata cache service): $role = file_get_contents('http://169.254.169.254/latest/meta-data/iam/security-credentials/'); $jsonCredentials = file_get_contents('http://169.254.169.254/latest/meta-data/iam/security-credentials/' . $role); $credentials = json_decode($jsonCredentials, true); -$configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( +$configuration = new \Akeeba\S3\Configuration( $credentials['AccessKeyId'], $credentials['SecretAccessKey'], 'v4', @@ -45,14 +69,14 @@ $configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( ); $configuration->setToken($credentials['Token']); -$connector = new \Akeeba\Engine\Postproc\Connector\S3v4\Connector($configuration); +$connector = new \Akeeba\S3\Connector($configuration); ``` where `$yourRegion` is the AWS region of your bucket, e.g. `us-east-1`. Please note that we are passing the security token (`$credentials['Token']`) to the Configuration object. This is REQUIRED. The temporary credentials returned by the metadata service won't work without it. -Also worth noting is that the temporary credentials don't last forever. Check the `$credentials['Expiration']` to see +Another point worth noting is that the temporary credentials don't last forever. Check the `$credentials['Expiration']` to see when they are about to expire. Amazon recommends that you retry fetching new credentials from the metadata service 10 minutes before your cached credentials are set to expire. The metadata service is guaranteed to provision fresh temporary credentials by that time. @@ -120,21 +144,21 @@ The last parameter (common prefixes) controls the listing of "subdirectories" From a file: ```php -$input = \Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sourceFile); +$input = \Akeeba\S3\Input::createFromFile($sourceFile); $connector->putObject($input, 'mybucket', 'path/to/myfile.txt'); ``` From a string: ```php -$input = \Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromData($sourceString); +$input = \Akeeba\S3\Input::createFromData($sourceString); $connector->putObject($input, 'mybucket', 'path/to/myfile.txt'); ``` From a stream resource: ```php -$input = \Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromResource($streamHandle, false); +$input = \Akeeba\S3\Input::createFromResource($streamHandle, false); $connector->putObject($input, 'mybucket', 'path/to/myfile.txt'); ``` @@ -145,7 +169,7 @@ In all cases the entirety of the file has to be loaded in memory. Files are uploaded in 5Mb chunks. ```php -$input = \Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sourceFile); +$input = \Akeeba\S3\Input::createFromFile($sourceFile); $uploadId = $connector->startMultipart($input, 'mybucket', 'mypath/movie.mov'); $eTags = array(); @@ -155,7 +179,7 @@ $partNumber = 0; do { // IMPORTANT: You MUST create the input afresh before each uploadMultipart call - $input = \Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sourceFile); + $input = \Akeeba\S3\Input::createFromFile($sourceFile); $input->setUploadID($uploadId); $input->setPartNumber(++$partNumber); @@ -169,7 +193,7 @@ do while (!is_null($eTag)); // IMPORTANT: You MUST create the input afresh before finalising the multipart upload -$input = \Akeeba\Engine\Postproc\Connector\S3v4\Input::createFromFile($sourceFile); +$input = \Akeeba\S3\Input::createFromFile($sourceFile); $input->setUploadID($uploadId); $input->setEtags($eTags); @@ -209,6 +233,23 @@ $content = $connector->getObject('mybucket', 'path/to/file.jpg', false); $connector->deleteObject('mybucket', 'path/to/file.jpg'); ``` +### Test if an object exists + +```php +try +{ + $headers = $connector->headObject('mybucket', 'path/to/file.jpg'); + $exists = true; +} +catch (\Akeeba\S3\Exception\CannotGetFile $e) +{ + $headers = []; + $exists = false; +} +``` + +The `$headers` variable contains an array with the S3 headers returned by the [HeadObject(https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html) API call. The header keys are always in lowercase. Please note that _not all_ of the headers Amazon describes in their documentation are returned in every request. + ## Configuration options The Configuration option has optional methods which can be used to enable some useful features in the connector. @@ -216,7 +257,7 @@ The Configuration option has optional methods which can be used to enable some u You need to execute these methods against the Configuration object before passing it to the Connector's constructor. For example: ```php -$configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( +$configuration = new \Akeeba\S3\Configuration( 'YourAmazonAccessKey', 'YourAmazonSecretKey' ); @@ -225,7 +266,7 @@ $configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( $configuration->setSignatureMethod('v4'); $configuration->setUseDualstackUrl(true); -$connector = new \Akeeba\Engine\Postproc\Connector\S3v4\Connector($configuration); +$connector = new \Akeeba\S3\Connector($configuration); ``` ### HTTPS vs plain HTTP @@ -245,7 +286,7 @@ Please note that if the S3-compatible APi uses v4 signatures you need to enter t ```php // DigitalOcean Spaces using v4 signatures // The access credentials are those used in the example at https://developers.digitalocean.com/documentation/spaces/ -$configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( +$configuration = new \Akeeba\S3\Configuration( '532SZONTQ6ALKBCU94OU', 'zCkY83KVDXD8u83RouEYPKEm/dhPSPB45XsfnWj8fxQ', 'v4', @@ -253,7 +294,7 @@ $configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( ); $configuration->setEndpoint('nyc3.digitaloceanspaces.com'); -$connector = new \Akeeba\Engine\Postproc\Connector\S3v4\Connector($configuration); +$connector = new \Akeeba\S3\Connector($configuration); ``` If your S3-compatible API uses v2 signatures you do not need to specify a region. @@ -261,14 +302,14 @@ If your S3-compatible API uses v2 signatures you do not need to specify a region ```php // DigitalOcean Spaces using v2 signatures // The access credentials are those used in the example at https://developers.digitalocean.com/documentation/spaces/ -$configuration = new \Akeeba\Engine\Postproc\Connector\S3v4\Configuration( +$configuration = new \Akeeba\S3\Configuration( '532SZONTQ6ALKBCU94OU', 'zCkY83KVDXD8u83RouEYPKEm/dhPSPB45XsfnWj8fxQ', 'v2' ); $configuration->setEndpoint('nyc3.digitaloceanspaces.com'); -$connector = new \Akeeba\Engine\Postproc\Connector\S3v4\Connector($configuration); +$connector = new \Akeeba\S3\Connector($configuration); ``` ### Legacy path-style access @@ -282,7 +323,7 @@ You need to do: $configuration->setUseLegacyPathStyle(true); ``` -Caveat: this will not work with v2 signatures if you are using Amazon AWS S3 proper. It will work with the v2 signatures if you are using a custom endpoint, though. In fact, most S3-compatible APIs implementing V2 signatures _expect_ you to use path-style access. +Caveat: this will not work with v2 signatures if you are using Amazon AWS S3 proper. It will very likely work with the v2 signatures if you are using a custom endpoint, though. ### Dualstack (IPv4 and IPv6) support diff --git a/s3_storage/vendor/akeeba/s3/TODO.md b/s3_storage/vendor/akeeba/s3/TODO.md deleted file mode 100644 index e19fb64c..00000000 --- a/s3_storage/vendor/akeeba/s3/TODO.md +++ /dev/null @@ -1,13 +0,0 @@ -Need to check: - -endpoint in [amazon, custom] -signature in [v2, v4] -path style in [true, false] - upload - download - presigned URL generation - presigned URL access - - -USING VIRTUAL HOSTING, v4 SIGNATURES - presigned URL must use s3.amazonaws.com i.e. path-style hosting (because who needs logic?) \ No newline at end of file diff --git a/s3_storage/vendor/akeeba/s3/composer.json b/s3_storage/vendor/akeeba/s3/composer.json index bfb1943c..2d3ef59f 100644 --- a/s3_storage/vendor/akeeba/s3/composer.json +++ b/s3_storage/vendor/akeeba/s3/composer.json @@ -3,7 +3,7 @@ "type": "library", "description": "A compact, dependency-less Amazon S3 API client implementing the most commonly used features", "require": { - "php": ">=7.1.0 <8.1", + "php": ">=7.1.0 <8.4", "ext-curl": "*", "ext-simplexml": "*" }, @@ -11,7 +11,7 @@ "s3" ], "homepage": "https://github.com/akeeba/s3", - "license": "GPL-3.0+", + "license": "GPL-3.0-or-later", "authors": [ { "name": "Nicholas K. Dionysopoulos", @@ -22,7 +22,16 @@ ], "autoload": { "psr-4": { - "Akeeba\\Engine\\Postproc\\Connector\\S3v4\\": "src" - } + "Akeeba\\S3\\": "src" + }, + "files": [ + "src/aliasing.php" + ] + }, + "archive": { + "exclude": [ + "minitest", + "TODO.md" + ] } } diff --git a/s3_storage/vendor/akeeba/s3/composer.lock b/s3_storage/vendor/akeeba/s3/composer.lock index e7cbd746..a7e203f9 100644 --- a/s3_storage/vendor/akeeba/s3/composer.lock +++ b/s3_storage/vendor/akeeba/s3/composer.lock @@ -1,19 +1,22 @@ { - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "content-hash": "1070071b351d45a80934e854f0725d64", - "packages": [], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.3.4" - }, - "platform-dev": [] + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "27f387a657b2784510b177f73c436346", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1.0 <8.4", + "ext-curl": "*", + "ext-simplexml": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" } diff --git a/s3_storage/vendor/akeeba/s3/minitest/NOTES.md b/s3_storage/vendor/akeeba/s3/minitest/NOTES.md new file mode 100644 index 00000000..b31f4f0a --- /dev/null +++ b/s3_storage/vendor/akeeba/s3/minitest/NOTES.md @@ -0,0 +1,62 @@ +# Testing notes + +## Against Amazon S3 proper + +This is the _canonical_ method for testing this library since Amazon S3 proper is the canonical provider of the S3 API (and not all of its quirks are fully documented, we might add). + +Copy `config.dist.php` to `config.php` and enter the connection information to your Amazon S3 or compatible service. + +## Against [LocalStack](https://localstack.cloud) + +This method is very useful for development. + +Install LocalStack [as per their documentation](https://docs.localstack.cloud/getting-started/installation/). + +You will also need to install [`awslocal`](https://github.com/localstack/awscli-local) like so: +```php +pip install awscli +pip install awscli-local +``` + +Start LocalStack e.g. `localstack start -d` + +Create a new bucket called `test` i.e. `awslocal s3 mk s3://test` + +Copy `config.dist.php` to `config.php` and make the following changes: +```php + define('DEFAULT_ENDPOINT', 'localhost.localstack.cloud:4566'); + define('DEFAULT_ACCESS_KEY', 'ANYRANDOMSTRINGWILLDO'); + define('DEFAULT_SECRET_KEY', 'ThisIsAlwaysIgnoredByLocalStack'); + define('DEFAULT_REGION', 'us-east-1'); + define('DEFAULT_BUCKET', 'test'); + define('DEFAULT_SIGNATURE', 'v4'); + define('DEFAULT_PATH_ACCESS', true); +``` + +Note that single- and dualstack tests result in the same URLs for all S3-compatible services, including LocalStack. These tests are essentially duplicates in this use case. + +## Against Wasabi + +Wasabi nominally supports v4 signatures, but their implementation is actually _non-canonical_, as they only read the date from the optional `x-amz-date` header, without falling back to the standard HTTP `Date` header. We have added a workaround for this behaviour which necessitates testing with it. + +Just like with Amazon S3 proper, copy `config.dist.php` to `config.php` and enter the connection information to your Wasabi storage. You will also need to set up the custom endpoint like so: +```php +define('DEFAULT_ENDPOINT', 's3.eu-central-2.wasabisys.com'); +``` + +**IMPORTANT!** The above endpoint will be different, depending on which region you've created your bucket in. The example above assumes the `eu-central-2` region. If you use the wrong region the tests _will_ fail! + +## Against Synology C2 + +Synology C2 is an S3-“compatible” storage service. It is not very “compatible” though, since they implemented Amazon's documentation of the v4 signatures instead of how the v4 signatures work in the real world (yeah, there's a very big difference). While Amazon S3 _in reality_ expects all dates to be formatted as per RFC1123, they document that they expect them to be formatted as per “ISO 8601” and they give their _completely wrong_ interpretation of what the “ISO 8601” format is. Synology did not catch that discrepancy, and they only expect the wrongly formatted dates which is totally NOT what S3 itself expects. Luckily, most third party implementations expect either format because they've caught the discrepancy between documentation and reality, therefore making it possible for us to come up with a viable workaround. + +And that's why we need to test with C2 as well, folks. + +Copy `config.dist.php` to `config.php` and enter the connection information to your Synology S3 service. + +It is very important to note two things: +```php +define('DEFAULT_ENDPOINT', 'eu-002.s3.synologyc2.net'); +define('DEFAULT_REGION', 'eu-002'); +``` +The endpoint URL is given in the Synology C2 Object Manager, next to each bucket. Note the part before `.s3.`. This is the **region** you need to use with v4 signatures. They do not document this anywhere. \ No newline at end of file diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/AbstractTest.php b/s3_storage/vendor/akeeba/s3/minitest/Test/AbstractTest.php index 8470e260..895259dd 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/AbstractTest.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/AbstractTest.php @@ -3,13 +3,13 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; use RuntimeException; abstract class AbstractTest @@ -58,24 +58,24 @@ abstract class AbstractTest */ protected static function createFile(int $size = AbstractTest::SIX_HUNDRED_KB, int $blockSize = self::BLOCK_SIZE, bool $reuseBlock = true) { - $tempFilePath = tempnam(self::getTempFolder(), 'as3'); + $tempFilePath = tempnam(static::getTempFolder(), 'as3'); if ($tempFilePath === false) { throw new RuntimeException("Cannot create a temporary file."); } - $fp = @fopen($tempFilePath, 'wb', false); + $fp = @fopen($tempFilePath, 'w', false); if ($fp === false) { throw new RuntimeException("Cannot write to the temporary file."); } - $blockSize = self::BLOCK_SIZE; + $blockSize = static::BLOCK_SIZE; $lastBlockSize = $size % $blockSize; $wholeBlocks = (int) (($size - $lastBlockSize) / $blockSize); - $blockData = self::getRandomData(); + $blockData = static::getRandomData(); for ($i = 0; $i < $wholeBlocks; $i++) { @@ -83,7 +83,7 @@ abstract class AbstractTest if (!$reuseBlock) { - $blockData = self::getRandomData($blockSize); + $blockData = static::getRandomData($blockSize); } } @@ -155,7 +155,7 @@ abstract class AbstractTest return false; } - return hash_file(self::FILE_HASHING_ALGORITHM, $referenceFilePath) === hash_file(self::FILE_HASHING_ALGORITHM, $unknownFilePath); + return hash_file(static::FILE_HASHING_ALGORITHM, $referenceFilePath) === hash_file(static::FILE_HASHING_ALGORITHM, $unknownFilePath); } /** diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/BigFiles.php b/s3_storage/vendor/akeeba/s3/minitest/Test/BigFiles.php index b7273bad..437d73ed 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/BigFiles.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/BigFiles.php @@ -3,15 +3,15 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; -use Akeeba\Engine\Postproc\Connector\S3v4\Input; +use Akeeba\S3\Connector; +use Akeeba\S3\Input; /** * Upload, download and delete big files (over 1MB), without multipart uploads. Uses string or file sources. @@ -51,7 +51,7 @@ class BigFiles extends AbstractTest /** * Number of uploaded chunks. * - * This is set by self::upload(). Zero for single part uploads, non-zero for multipart uploads. + * This is set by static::upload(). Zero for single part uploads, non-zero for multipart uploads. * * @var int */ @@ -59,42 +59,42 @@ class BigFiles extends AbstractTest public static function upload5MBString(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::FIVE_MB, 'bigtest_5mb.dat'); + return static::upload($s3, $options, static::FIVE_MB, 'bigtest_5mb.dat'); } public static function upload6MBString(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::SIX_MB, 'bigtest_6mb.dat'); + return static::upload($s3, $options, static::SIX_MB, 'bigtest_6mb.dat'); } public static function upload10MBString(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::TEN_MB, 'bigtest_10mb.dat'); + return static::upload($s3, $options, static::TEN_MB, 'bigtest_10mb.dat'); } public static function upload11MBString(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::ELEVEN_MB, 'bigtest_11mb.dat'); + return static::upload($s3, $options, static::ELEVEN_MB, 'bigtest_11mb.dat'); } public static function upload5MBFile(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::FIVE_MB, 'bigtest_5mb.dat', false); + return static::upload($s3, $options, static::FIVE_MB, 'bigtest_5mb.dat', false); } public static function upload6MBFile(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::SIX_MB, 'bigtest_6mb.dat', false); + return static::upload($s3, $options, static::SIX_MB, 'bigtest_6mb.dat', false); } public static function upload10MBFile(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::TEN_MB, 'bigtest_10mb.dat', false); + return static::upload($s3, $options, static::TEN_MB, 'bigtest_10mb.dat', false); } public static function upload11MBFile(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::ELEVEN_MB, 'bigtest_11mb.dat', false); + return static::upload($s3, $options, static::ELEVEN_MB, 'bigtest_11mb.dat', false); } protected static function upload(Connector $s3, array $options, int $size, string $uri, bool $useString = true): bool @@ -103,24 +103,24 @@ class BigFiles extends AbstractTest $dotPos = strrpos($uri, '.'); $uri = substr($uri, 0, $dotPos) . '.' . md5(microtime(false)) . substr($uri, $dotPos); - self::$numberOfChunks = 0; + static::$numberOfChunks = 0; if ($useString) { - $sourceData = self::getRandomData($size); + $sourceData = static::getRandomData($size); $input = Input::createFromData($sourceData); } else { // Create a file with random data - $sourceFile = self::createFile($size); + $sourceFile = static::createFile($size); $input = Input::createFromFile($sourceFile); } // Upload the file. Throws exception if it fails. $bucket = $options['bucket']; - if (!self::$multipart) + if (!static::$multipart) { $s3->putObject($input, $bucket, $uri); } @@ -149,7 +149,7 @@ class BigFiles extends AbstractTest $input->setEtags($eTags); $input->setPartNumber($partNumber); - $etag = $s3->uploadMultipart($input, $bucket, $uri, [], self::$uploadChunkSize); + $etag = $s3->uploadMultipart($input, $bucket, $uri, [], static::$uploadChunkSize); // If the result was null we have no more file parts to process. if (is_null($etag)) @@ -166,7 +166,7 @@ class BigFiles extends AbstractTest $partNumber++; } - self::$numberOfChunks = count($eTags); + static::$numberOfChunks = count($eTags); // Finalize the multipart upload. Tells Amazon to construct the file from the uploaded parts. $s3->finalizeMultipart($input, $bucket, $uri); @@ -176,7 +176,7 @@ class BigFiles extends AbstractTest $result = true; // Should I download the file and compare its contents? - if (self::$downloadAfter) + if (static::$downloadAfter) { if ($useString) { @@ -184,16 +184,16 @@ class BigFiles extends AbstractTest $downloadedData = $s3->getObject($bucket, $uri); // Compare the file contents. - $result = self::areStringsEqual($sourceData, $downloadedData); + $result = static::areStringsEqual($sourceData, $downloadedData); } else { // Download the data. Throws exception if it fails. - $downloadedFile = tempnam(self::getTempFolder(), 'as3'); + $downloadedFile = tempnam(static::getTempFolder(), 'as3'); $s3->getObject($bucket, $uri, $downloadedFile); // Compare the file contents. - $result = self::areFilesEqual($sourceFile, $downloadedFile); + $result = static::areFilesEqual($sourceFile, $downloadedFile); @unlink($downloadedFile); } @@ -206,7 +206,7 @@ class BigFiles extends AbstractTest } // Should I delete the remotely stored file? - if (self::$deleteRemote) + if (static::$deleteRemote) { // Delete the remote file. Throws exception if it fails. $s3->deleteObject($bucket, $uri); diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/BucketLocation.php b/s3_storage/vendor/akeeba/s3/minitest/Test/BucketLocation.php index 6ba388b4..ff04f58a 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/BucketLocation.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/BucketLocation.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; class BucketLocation extends AbstractTest { @@ -18,7 +18,7 @@ class BucketLocation extends AbstractTest { $location = $s3->getBucketLocation($options['bucket']); - self::assert($location === $options['region'], "Bucket ‘{$options['bucket']}′ reports being in region ‘{$location}′ instead of expected ‘{$options['region']}′"); + static::assert($location === $options['region'], "Bucket ‘{$options['bucket']}′ reports being in region ‘{$location}′ instead of expected ‘{$options['region']}′"); return true; } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/BucketsList.php b/s3_storage/vendor/akeeba/s3/minitest/Test/BucketsList.php index 33925cd2..4dab491d 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/BucketsList.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/BucketsList.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; use RuntimeException; class BucketsList extends AbstractTest @@ -19,16 +19,16 @@ class BucketsList extends AbstractTest { $buckets = $s3->listBuckets(true); - self::assert(is_array($buckets), "Detailed buckets list is not an array"); - self::assert(isset($buckets['owner']), "Detailed buckets list does not list an owner"); - self::assert(isset($buckets['owner']['id']), "Detailed buckets list does not list an owner's id"); - self::assert(isset($buckets['owner']['name']), "Detailed buckets list does not list an owner's name"); - self::assert(isset($buckets['buckets']), "Detailed buckets list does not list any buckets"); + static::assert(is_array($buckets), "Detailed buckets list is not an array"); + static::assert(isset($buckets['owner']), "Detailed buckets list does not list an owner"); + static::assert(isset($buckets['owner']['id']), "Detailed buckets list does not list an owner's id"); + static::assert(isset($buckets['owner']['name']), "Detailed buckets list does not list an owner's name"); + static::assert(isset($buckets['buckets']), "Detailed buckets list does not list any buckets"); foreach ($buckets['buckets'] as $bucketInfo) { - self::assert(isset($bucketInfo['name']), "Bucket information does not list a name"); - self::assert(isset($bucketInfo['time']), "Bucket information does not list a created times"); + static::assert(isset($bucketInfo['name']), "Bucket information does not list a name"); + static::assert(isset($bucketInfo['time']), "Bucket information does not list a created times"); if ($bucketInfo['name'] === $options['bucket']) { @@ -43,8 +43,8 @@ class BucketsList extends AbstractTest { $buckets = $s3->listBuckets(false); - self::assert(is_array($buckets), "Simple buckets list is not an array"); - self::assert(in_array($options['bucket'], $buckets), "Simple buckets list does not include configured bucket ‘{$options['bucket']}′"); + static::assert(is_array($buckets), "Simple buckets list is not an array"); + static::assert(in_array($options['bucket'], $buckets), "Simple buckets list does not include configured bucket ‘{$options['bucket']}′"); return true; } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/HeadObject.php b/s3_storage/vendor/akeeba/s3/minitest/Test/HeadObject.php new file mode 100644 index 00000000..535afdbd --- /dev/null +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/HeadObject.php @@ -0,0 +1,67 @@ +putObject($input, $bucket, $uri); + + $headers = $s3->headObject($bucket, $uri); + + static::assert(isset($headers['size']), 'The returned headers do not contain the object size'); + static::assert($headers['size'] == AbstractTest::TEN_KB, 'The returned size does not match'); + + // Remove the local files + @unlink($sourceFile); + + // Delete the remote file. Throws exception if it fails. + $s3->deleteObject($bucket, $uri); + + return true; + } + + public static function testMissingFile(Connector $s3, array $options): bool + { + $bucket = $options['bucket']; + + try + { + $headers = $s3->headObject($bucket, md5(microtime(false)) . '_does_not_exist'); + } + catch (CannotGetFile $e) + { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/ListFiles.php b/s3_storage/vendor/akeeba/s3/minitest/Test/ListFiles.php index 53893922..00b5fdbf 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/ListFiles.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/ListFiles.php @@ -3,16 +3,16 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotPutFile; -use Akeeba\Engine\Postproc\Connector\S3v4\Input; +use Akeeba\S3\Connector; +use Akeeba\S3\Exception\CannotPutFile; +use Akeeba\S3\Input; class ListFiles extends AbstractTest { @@ -34,9 +34,9 @@ class ListFiles extends AbstractTest public static function setup(Connector $s3, array $options): void { - $data = self::getRandomData(self::TEN_KB); + $data = static::getRandomData(static::TEN_KB); - foreach (self::$paths as $uri) + foreach (static::$paths as $uri) { $input = Input::createFromData($data); try @@ -52,7 +52,7 @@ class ListFiles extends AbstractTest public static function teardown(Connector $s3, array $options): void { - foreach (self::$paths as $uri) + foreach (static::$paths as $uri) { try { @@ -69,29 +69,29 @@ class ListFiles extends AbstractTest { $listing = $s3->getBucket($options['bucket'], 'listtest_'); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 3, "I am expecting to see 3 files"); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 3, "I am expecting to see 3 files"); // Make sure I have the expected files - self::assert(array_key_exists('listtest_one.dat', $listing), "File listtest_one.dat not in listing"); - self::assert(array_key_exists('listtest_two.dat', $listing), "File listtest_two.dat not in listing"); - self::assert(array_key_exists('listtest_three.dat', $listing), "File listtest_three.dat not in listing"); + static::assert(array_key_exists('listtest_one.dat', $listing), "File listtest_one.dat not in listing"); + static::assert(array_key_exists('listtest_two.dat', $listing), "File listtest_two.dat not in listing"); + static::assert(array_key_exists('listtest_three.dat', $listing), "File listtest_three.dat not in listing"); // I must not see the files in subdirectories - self::assert(!array_key_exists('listtest_four.dat', $listing), "File listtest_four.dat in listing"); - self::assert(!array_key_exists('listtest_five.dat', $listing), "File listtest_five.dat in listing"); - self::assert(!array_key_exists('listtest_six.dat', $listing), "File listtest_six.dat in listing"); + static::assert(!array_key_exists('listtest_four.dat', $listing), "File listtest_four.dat in listing"); + static::assert(!array_key_exists('listtest_five.dat', $listing), "File listtest_five.dat in listing"); + static::assert(!array_key_exists('listtest_six.dat', $listing), "File listtest_six.dat in listing"); // I must not see the files not matching the prefix I gave - self::assert(!array_key_exists('spam.dat', $listing), "File spam.dat in listing"); - self::assert(!array_key_exists('ham.dat', $listing), "File ham.dat in listing"); + static::assert(!array_key_exists('spam.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('ham.dat', $listing), "File ham.dat in listing"); foreach ($listing as $fileName => $info) { - self::assert(isset($info['name']), "File entries must have a name"); - self::assert(isset($info['time']), "File entries must have a time"); - self::assert(isset($info['size']), "File entries must have a size"); - self::assert(isset($info['hash']), "File entries must have a hash"); + static::assert(isset($info['name']), "File entries must have a name"); + static::assert(isset($info['time']), "File entries must have a time"); + static::assert(isset($info['size']), "File entries must have a size"); + static::assert(isset($info['hash']), "File entries must have a hash"); } return true; @@ -101,37 +101,37 @@ class ListFiles extends AbstractTest { $listing = $s3->getBucket($options['bucket'], 'listtest_', null, 1); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 1, sprintf("I am expecting to see 1 file, %s seen", count($listing))); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 1, sprintf("I am expecting to see 1 file, %s seen", count($listing))); $files = array_keys($listing); $continued = $s3->getBucket($options['bucket'], 'listtest_', array_shift($files)); - self::assert(is_array($continued), "The continued files listing must be an array"); - self::assert(count($continued) == 2, sprintf("I am expecting to see 2 files, %s seen", count($continued))); + static::assert(is_array($continued), "The continued files listing must be an array"); + static::assert(count($continued) == 2, sprintf("I am expecting to see 2 files, %s seen", count($continued))); $listing = array_merge($listing, $continued); // Make sure I have the expected files - self::assert(array_key_exists('listtest_one.dat', $listing), "File listtest_one.dat not in listing"); - self::assert(array_key_exists('listtest_two.dat', $listing), "File listtest_two.dat not in listing"); - self::assert(array_key_exists('listtest_three.dat', $listing), "File listtest_three.dat not in listing"); + static::assert(array_key_exists('listtest_one.dat', $listing), "File listtest_one.dat not in listing"); + static::assert(array_key_exists('listtest_two.dat', $listing), "File listtest_two.dat not in listing"); + static::assert(array_key_exists('listtest_three.dat', $listing), "File listtest_three.dat not in listing"); // I must not see the files in subdirectories - self::assert(!array_key_exists('listtest_four.dat', $listing), "File listtest_four.dat in listing"); - self::assert(!array_key_exists('listtest_five.dat', $listing), "File listtest_five.dat in listing"); - self::assert(!array_key_exists('listtest_six.dat', $listing), "File listtest_six.dat in listing"); + static::assert(!array_key_exists('listtest_four.dat', $listing), "File listtest_four.dat in listing"); + static::assert(!array_key_exists('listtest_five.dat', $listing), "File listtest_five.dat in listing"); + static::assert(!array_key_exists('listtest_six.dat', $listing), "File listtest_six.dat in listing"); // I must not see the files not matching the prefix I gave - self::assert(!array_key_exists('spam.dat', $listing), "File spam.dat in listing"); - self::assert(!array_key_exists('ham.dat', $listing), "File ham.dat in listing"); + static::assert(!array_key_exists('spam.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('ham.dat', $listing), "File ham.dat in listing"); foreach ($listing as $fileName => $info) { - self::assert(isset($info['name']), "File entries must have a name"); - self::assert(isset($info['time']), "File entries must have a time"); - self::assert(isset($info['size']), "File entries must have a size"); - self::assert(isset($info['hash']), "File entries must have a hash"); + static::assert(isset($info['name']), "File entries must have a name"); + static::assert(isset($info['time']), "File entries must have a time"); + static::assert(isset($info['size']), "File entries must have a size"); + static::assert(isset($info['hash']), "File entries must have a hash"); } return true; @@ -141,30 +141,30 @@ class ListFiles extends AbstractTest { $listing = $s3->getBucket($options['bucket'], 'list_deeper/test_'); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 3, "I am expecting to see 3 files"); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 3, "I am expecting to see 3 files"); // Make sure I have the expected files - self::assert(array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat not in listing"); - self::assert(array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat not in listing"); - self::assert(array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat not in listing"); + static::assert(array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat not in listing"); + static::assert(array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat not in listing"); + static::assert(array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat not in listing"); // I must not see the files with different prefix - self::assert(!array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat in listing"); - self::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat in listing"); + static::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); // I must not see the files in subdirectories - self::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File spam.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File spam.dat in listing"); foreach ($listing as $fileName => $info) { - self::assert(isset($info['name']), "File entries must have a name"); - self::assert(isset($info['time']), "File entries must have a time"); - self::assert(isset($info['size']), "File entries must have a size"); - self::assert(isset($info['hash']), "File entries must have a hash"); + static::assert(isset($info['name']), "File entries must have a name"); + static::assert(isset($info['time']), "File entries must have a time"); + static::assert(isset($info['size']), "File entries must have a size"); + static::assert(isset($info['hash']), "File entries must have a hash"); } return true; @@ -174,41 +174,41 @@ class ListFiles extends AbstractTest { $listing = $s3->getBucket($options['bucket'], 'list_deeper/test_', null, 1); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 1, sprintf("I am expecting to see 1 file, %s seen", count($listing))); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 1, sprintf("I am expecting to see 1 file, %s seen", count($listing))); $files = array_keys($listing); $continued = $s3->getBucket($options['bucket'], 'list_deeper/test_', array_shift($files)); - self::assert(is_array($continued), "The continued files listing must be an array"); - self::assert(count($continued) == 2, sprintf("I am expecting to see 2 files, %s seen", count($continued))); + static::assert(is_array($continued), "The continued files listing must be an array"); + static::assert(count($continued) == 2, sprintf("I am expecting to see 2 files, %s seen", count($continued))); $listing = array_merge($listing, $continued); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 3, "I am expecting to see 3 files"); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 3, "I am expecting to see 3 files"); // Make sure I have the expected files - self::assert(array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat not in listing"); - self::assert(array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat not in listing"); - self::assert(array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat not in listing"); + static::assert(array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat not in listing"); + static::assert(array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat not in listing"); + static::assert(array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat not in listing"); // I must not see the files with different prefix - self::assert(!array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat in listing"); - self::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat in listing"); + static::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); // I must not see the files in subdirectories - self::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File spam.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File spam.dat in listing"); foreach ($listing as $fileName => $info) { - self::assert(isset($info['name']), "File entries must have a name"); - self::assert(isset($info['time']), "File entries must have a time"); - self::assert(isset($info['size']), "File entries must have a size"); - self::assert(isset($info['hash']), "File entries must have a hash"); + static::assert(isset($info['name']), "File entries must have a name"); + static::assert(isset($info['time']), "File entries must have a time"); + static::assert(isset($info['size']), "File entries must have a size"); + static::assert(isset($info['hash']), "File entries must have a hash"); } return true; @@ -224,42 +224,42 @@ class ListFiles extends AbstractTest */ $listing = $s3->getBucket($options['bucket'], 'list_deeper/listtest_', null, 1); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 1, sprintf("I am expecting to see 1 files, %s seen", count($listing))); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 1, sprintf("I am expecting to see 1 files, %s seen", count($listing))); $files = array_keys($listing); $continued = $s3->getBucket($options['bucket'], 'list_deeper/listtest_', array_shift($files)); - self::assert(is_array($continued), "The continued files listing must be an array"); - self::assert(count($continued) == 2, sprintf("I am expecting to see 2 files, %s seen", count($continued))); + static::assert(is_array($continued), "The continued files listing must be an array"); + static::assert(count($continued) == 2, sprintf("I am expecting to see 2 files, %s seen", count($continued))); $listing = array_merge($listing, $continued); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 3, "I am expecting to see 3 files"); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 3, "I am expecting to see 3 files"); // Make sure I have the expected files - self::assert(array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat not in listing"); - self::assert(array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat not in listing"); - self::assert(array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat not in listing"); + static::assert(array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat not in listing"); + static::assert(array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat not in listing"); + static::assert(array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat not in listing"); // I must not see the files with different prefix - self::assert(!array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat in listing"); - self::assert(!array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat in listing"); - self::assert(!array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat in listing"); - self::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat in listing"); + static::assert(!array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat in listing"); + static::assert(!array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat in listing"); + static::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); // I must not see the files in subdirectories - self::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File spam.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File spam.dat in listing"); foreach ($listing as $fileName => $info) { - self::assert(isset($info['name']), "File entries must have a name"); - self::assert(isset($info['time']), "File entries must have a time"); - self::assert(isset($info['size']), "File entries must have a size"); - self::assert(isset($info['hash']), "File entries must have a hash"); + static::assert(isset($info['name']), "File entries must have a name"); + static::assert(isset($info['time']), "File entries must have a time"); + static::assert(isset($info['size']), "File entries must have a size"); + static::assert(isset($info['hash']), "File entries must have a hash"); } return true; @@ -269,37 +269,37 @@ class ListFiles extends AbstractTest { $listing = $s3->getBucket($options['bucket'], 'list_deeper/listtest_', null, null, '/', true); - self::assert(is_array($listing), "The files listing must be an array"); - self::assert(count($listing) == 4, sprintf("I am expecting to see 4 entries, %s entries seen.", count($listing))); + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) == 4, sprintf("I am expecting to see 4 entries, %s entries seen.", count($listing))); // Make sure I have the expected files - self::assert(array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat not in listing"); - self::assert(array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat not in listing"); - self::assert(array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat not in listing"); - self::assert(array_key_exists('list_deeper/listtest_deeper/', $listing), "Folder listtest_deeper not in listing"); + static::assert(array_key_exists('list_deeper/listtest_four.dat', $listing), "File listtest_four.dat not in listing"); + static::assert(array_key_exists('list_deeper/listtest_five.dat', $listing), "File listtest_five.dat not in listing"); + static::assert(array_key_exists('list_deeper/listtest_six.dat', $listing), "File listtest_six.dat not in listing"); + static::assert(array_key_exists('list_deeper/listtest_deeper/', $listing), "Folder listtest_deeper not in listing"); // I must not see the files in subdirectories - self::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File seven.dat in listing"); - self::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File eight.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/seven.dat', $listing), "File seven.dat in listing"); + static::assert(!array_key_exists('list_deeper/listtest_deeper/eight.dat', $listing), "File eight.dat in listing"); // I must not see the files with different prefix - self::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); - self::assert(!array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat not in listing"); - self::assert(!array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat not in listing"); - self::assert(!array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat not in listing"); + static::assert(!array_key_exists('list_deeper/spam.dat', $listing), "File spam.dat in listing"); + static::assert(!array_key_exists('list_deeper/test_one.dat', $listing), "File test_one.dat not in listing"); + static::assert(!array_key_exists('list_deeper/test_two.dat', $listing), "File test_two.dat not in listing"); + static::assert(!array_key_exists('list_deeper/test_three.dat', $listing), "File test_three.dat not in listing"); foreach ($listing as $fileName => $info) { if (substr($fileName, -1) !== '/') { - self::assert(isset($info['name']), "File entries must have a name"); - self::assert(isset($info['time']), "File entries must have a time"); - self::assert(isset($info['size']), "File entries must have a size"); - self::assert(isset($info['hash']), "File entries must have a hash"); + static::assert(isset($info['name']), "File entries must have a name"); + static::assert(isset($info['time']), "File entries must have a time"); + static::assert(isset($info['size']), "File entries must have a size"); + static::assert(isset($info['hash']), "File entries must have a hash"); } else { - self::assert(isset($info['prefix']), "Folder entries must return a prefix"); + static::assert(isset($info['prefix']), "Folder entries must return a prefix"); } } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/ListThousandsOfFiles.php b/s3_storage/vendor/akeeba/s3/minitest/Test/ListThousandsOfFiles.php new file mode 100644 index 00000000..8146d4cd --- /dev/null +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/ListThousandsOfFiles.php @@ -0,0 +1,111 @@ +putObject($input, $options['bucket'], $uri); + } + } + + public static function testGetAll(Connector $s3, array $options): bool + { + $listing = $s3->getBucket($options['bucket'], static::PATH_PREFIX); + + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) === 2100, "I am expecting to see 2100 files"); + + for ($i = 1; $i <= 2100; $i++) + { + $key = sprintf('%stest_%04u.dat', static::PATH_PREFIX, $i); + + static::assert(array_key_exists($key, $listing), sprintf('Results should list object %s', $key)); + } + + return true; + } + + public static function testGetHundred(Connector $s3, array $options): bool + { + $listing = $s3->getBucket($options['bucket'], static::PATH_PREFIX, null, 100); + + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) === 100, "I am expecting to see 100 files"); + + for ($i = 1; $i <= 100; $i++) + { + $key = sprintf('%stest_%04u.dat', static::PATH_PREFIX, $i); + + static::assert(array_key_exists($key, $listing), sprintf('Results should list object %s', $key)); + } + + return true; + } + + public static function testGetElevenHundred(Connector $s3, array $options): bool + { + $listing = $s3->getBucket($options['bucket'], static::PATH_PREFIX, null, 1100); + + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) === 1100, "I am expecting to see 1100 files"); + + for ($i = 1; $i <= 1100; $i++) + { + $key = sprintf('%stest_%04u.dat', static::PATH_PREFIX, $i); + + static::assert(array_key_exists($key, $listing), sprintf('Results should list object %s', $key)); + } + + return true; + } + + public static function testGetLastHundred(Connector $s3, array $options): bool + { + $listing = $s3->getBucket($options['bucket'], static::PATH_PREFIX . 'test_20', null); + + static::assert(is_array($listing), "The files listing must be an array"); + static::assert(count($listing) === 100, "I am expecting to see 100 files"); + + for ($i = 2000; $i <= 2099; $i++) + { + $key = sprintf('%stest_%04u.dat', static::PATH_PREFIX, $i); + + static::assert(array_key_exists($key, $listing), sprintf('Results should list object %s', $key)); + } + + return true; + } + +} \ No newline at end of file diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/Multipart.php b/s3_storage/vendor/akeeba/s3/minitest/Test/Multipart.php index e4c7c284..df097e96 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/Multipart.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/Multipart.php @@ -1,16 +1,22 @@ putObject($input, $bucket, $uri); + + $downloadedData = $s3->getObject($bucket, $uri); + $result = static::areStringsEqual($sourceData, $downloadedData); + + $s3->deleteObject($bucket, $uri); + + return $result ?? true; + } +} \ No newline at end of file diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFiles.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFiles.php index 52f49e58..82b2027f 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFiles.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFiles.php @@ -3,15 +3,15 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; -use Akeeba\Engine\Postproc\Connector\S3v4\Input; +use Akeeba\S3\Connector; +use Akeeba\S3\Input; /** * Upload, download and delete small files (under 1MB) using a file source @@ -36,32 +36,32 @@ class SmallFiles extends AbstractTest public static function upload10KbRoot(Connector $s3, array $options): bool { - return self::upload($s3, $options, AbstractTest::TEN_KB, 'root_10kb.dat'); + return static::upload($s3, $options, AbstractTest::TEN_KB, 'root_10kb.dat'); } public static function upload10KbRootGreek(Connector $s3, array $options): bool { - return self::upload($s3, $options, AbstractTest::TEN_KB, 'δοκιμή_10kb.dat'); + return static::upload($s3, $options, AbstractTest::TEN_KB, 'δοκιμή_10kb.dat'); } public static function upload10KbFolderGreek(Connector $s3, array $options): bool { - return self::upload($s3, $options, AbstractTest::TEN_KB, 'ο_φάκελός_μου/δοκιμή_10kb.dat'); + return static::upload($s3, $options, AbstractTest::TEN_KB, 'ο_φάκελός_μου/δοκιμή_10kb.dat'); } public static function upload600KbRoot(Connector $s3, array $options): bool { - return self::upload($s3, $options, AbstractTest::SIX_HUNDRED_KB, 'root_600kb.dat'); + return static::upload($s3, $options, AbstractTest::SIX_HUNDRED_KB, 'root_600kb.dat'); } public static function upload10KbFolder(Connector $s3, array $options): bool { - return self::upload($s3, $options, AbstractTest::TEN_KB, 'my_folder/10kb.dat'); + return static::upload($s3, $options, AbstractTest::TEN_KB, 'my_folder/10kb.dat'); } public static function upload600KbFolder(Connector $s3, array $options): bool { - return self::upload($s3, $options, AbstractTest::SIX_HUNDRED_KB, 'my_folder/600kb.dat'); + return static::upload($s3, $options, AbstractTest::SIX_HUNDRED_KB, 'my_folder/600kb.dat'); } protected static function upload(Connector $s3, array $options, int $size, string $uri): bool @@ -71,7 +71,7 @@ class SmallFiles extends AbstractTest $uri = substr($uri, 0, $dotPos) . '.' . md5(microtime(false)) . substr($uri, $dotPos); // Create a file with random data - $sourceFile = self::createFile($size); + $sourceFile = static::createFile($size); // Upload the file. Throws exception if it fails. $bucket = $options['bucket']; @@ -83,14 +83,14 @@ class SmallFiles extends AbstractTest $result = true; // Should I download the file and compare its contents? - if (self::$downloadAfter) + if (static::$downloadAfter) { // Donwload the data. Throws exception if it fails. - $downloadedFile = tempnam(self::getTempFolder(), 'as3'); + $downloadedFile = tempnam(static::getTempFolder(), 'as3'); $s3->getObject($bucket, $uri, $downloadedFile); // Compare the file contents. - $result = self::areFilesEqual($sourceFile, $downloadedFile); + $result = static::areFilesEqual($sourceFile, $downloadedFile); } // Remove the local files @@ -98,7 +98,7 @@ class SmallFiles extends AbstractTest @unlink($downloadedFile); // Should I delete the remotely stored file? - if (self::$deleteRemote) + if (static::$deleteRemote) { // Delete the remote file. Throws exception if it fails. $s3->deleteObject($bucket, $uri); diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesNoDelete.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesNoDelete.php index 7ef9cf5b..bdd3193b 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesNoDelete.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesNoDelete.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; /** * Upload and download small files (under 1MB) using a file source @@ -21,7 +21,7 @@ class SmallFilesNoDelete extends SmallFiles { public static function setup(Connector $s3, array $options): void { - self::$deleteRemote = false; + static::$deleteRemote = false; parent::setup($s3, $options); } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesOnlyUpload.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesOnlyUpload.php index 1b6cdca7..e1aacafd 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesOnlyUpload.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallFilesOnlyUpload.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; /** * Upload small files (under 1MB) using a file source @@ -21,8 +21,8 @@ class SmallFilesOnlyUpload extends SmallFiles { public static function setup(Connector $s3, array $options): void { - self::$deleteRemote = false; - self::$downloadAfter = false; + static::$deleteRemote = false; + static::$downloadAfter = false; parent::setup($s3, $options); } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFiles.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFiles.php index 5cb942e7..d648d106 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFiles.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFiles.php @@ -3,15 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; - -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; -use Akeeba\Engine\Postproc\Connector\S3v4\Input; +use Akeeba\S3\Connector; +use Akeeba\S3\Input; /** * Upload, download and delete small files (under 1MB) using a string source @@ -27,7 +26,7 @@ class SmallInlineFiles extends SmallFiles $uri = substr($uri, 0, $dotPos) . '.' . md5(microtime(false)) . substr($uri, $dotPos); // Create some random data to upload - $sourceData = self::getRandomData($size); + $sourceData = static::getRandomData($size); // Upload the data. Throws exception if it fails. $bucket = $options['bucket']; @@ -39,15 +38,15 @@ class SmallInlineFiles extends SmallFiles $result = true; // Should I download the file and compare its contents with my random data? - if (self::$downloadAfter) + if (static::$downloadAfter) { $downloadedData = $s3->getObject($bucket, $uri); - $result = self::areStringsEqual($sourceData, $downloadedData); + $result = static::areStringsEqual($sourceData, $downloadedData); } // Should I delete the remotely stored file? - if (self::$deleteRemote) + if (static::$deleteRemote) { // Delete the remote file. Throws exception if it fails. $s3->deleteObject($bucket, $uri); diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesNoDelete.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesNoDelete.php index 8ba99355..d7e0d73e 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesNoDelete.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesNoDelete.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; /** * Upload and download small files (under 1MB) using a string source @@ -21,7 +21,7 @@ class SmallInlineFilesNoDelete extends SmallInlineFiles { public static function setup(Connector $s3, array $options): void { - self:: $deleteRemote = false; + static:: $deleteRemote = false; parent::setup($s3, $options); } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesOnlyUpload.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesOnlyUpload.php index e41bf5ec..890ecccd 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesOnlyUpload.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineFilesOnlyUpload.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; +use Akeeba\S3\Connector; /** * Upload small files (under 1MB) using a string source @@ -21,8 +21,8 @@ class SmallInlineFilesOnlyUpload extends SmallInlineFiles { public static function setup(Connector $s3, array $options): void { - self::$deleteRemote = false; - self::$downloadAfter = false; + static::$deleteRemote = false; + static::$downloadAfter = false; parent::setup($s3, $options); } diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineXMLFiles.php b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineXMLFiles.php new file mode 100644 index 00000000..5ab54893 --- /dev/null +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/SmallInlineXMLFiles.php @@ -0,0 +1,131 @@ +putObject($input, $bucket, $uri); + + // Tentatively accept that this method succeeded. + $result = true; + + // Should I download the file and compare its contents with my random data? + if (static::$downloadAfter) + { + $downloadedData = $s3->getObject($bucket, $uri); + + $result = static::areStringsEqual($sourceData, $downloadedData); + } + + // Should I delete the remotely stored file? + if (static::$deleteRemote) + { + // Delete the remote file. Throws exception if it fails. + $s3->deleteObject($bucket, $uri); + } + + return $result; + } + + private static function createXMLFile(int $size): string + { + $out = <<< XML + + +XML; + + $chunks = floor(($size - 55) / 1024); + + for ($i = 1; $i <= $chunks; $i++) + { + $randomBlock = static::genRandomData(1024 - 63); + $out .= <<< XML + + $i + + +XML; + + } + + + $out .= <<< XML + +XML; + + return $out; + } + + private static function genRandomData(int $length): string + { + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; + $maxLength = strlen($chars) - 1; + $salt = ''; + + for ($i = 0; $i < $length; $i++) + { + $salt .= substr($chars, random_int(0, $maxLength), 1); + } + + return $salt; + } +} \ No newline at end of file diff --git a/s3_storage/vendor/akeeba/s3/minitest/Test/StorageClasses.php b/s3_storage/vendor/akeeba/s3/minitest/Test/StorageClasses.php index 9e57ba7a..42adce42 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/Test/StorageClasses.php +++ b/s3_storage/vendor/akeeba/s3/minitest/Test/StorageClasses.php @@ -3,17 +3,17 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\MiniTest\Test; -use Akeeba\Engine\Postproc\Connector\S3v4\Acl; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; -use Akeeba\Engine\Postproc\Connector\S3v4\Input; -use Akeeba\Engine\Postproc\Connector\S3v4\StorageClass; +use Akeeba\S3\Acl; +use Akeeba\S3\Connector; +use Akeeba\S3\Input; +use Akeeba\S3\StorageClass; class StorageClasses extends AbstractTest { @@ -23,12 +23,12 @@ class StorageClasses extends AbstractTest public static function uploadRRS(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::TEN_KB, 'rrs_test_10kb.dat', StorageClass::REDUCED_REDUNDANCY); + return static::upload($s3, $options, static::TEN_KB, 'rrs_test_10kb.dat', StorageClass::REDUCED_REDUNDANCY); } public static function uploadIntelligentTiering(Connector $s3, array $options): bool { - return self::upload($s3, $options, self::TEN_KB, 'rrs_test_10kb.dat', StorageClass::INTELLIGENT_TIERING); + return static::upload($s3, $options, static::TEN_KB, 'rrs_test_10kb.dat', StorageClass::INTELLIGENT_TIERING); } protected static function upload(Connector $s3, array $options, int $size, string $uri, string $storageClass = null) @@ -38,7 +38,7 @@ class StorageClasses extends AbstractTest $uri = substr($uri, 0, $dotPos) . '.' . md5(microtime(false)) . substr($uri, $dotPos); // Create some random data to upload - $sourceData = self::getRandomData($size); + $sourceData = static::getRandomData($size); // Upload the data. Throws exception if it fails. $bucket = $options['bucket']; @@ -54,15 +54,15 @@ class StorageClasses extends AbstractTest $result = true; // Should I download the file and compare its contents with my random data? - if (self::$downloadAfter) + if (static::$downloadAfter) { $downloadedData = $s3->getObject($bucket, $uri); - $result = self::areStringsEqual($sourceData, $downloadedData); + $result = static::areStringsEqual($sourceData, $downloadedData); } // Should I delete the remotely stored file? - if (self::$deleteRemote) + if (static::$deleteRemote) { // Delete the remote file. Throws exception if it fails. $s3->deleteObject($bucket, $uri); diff --git a/s3_storage/vendor/akeeba/s3/minitest/config.dist.php b/s3_storage/vendor/akeeba/s3/minitest/config.dist.php index 063934fe..b9124065 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/config.dist.php +++ b/s3_storage/vendor/akeeba/s3/minitest/config.dist.php @@ -3,10 +3,12 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ +// Custom Endpoint. The example below is for using LocalStack, see https://localstack.cloud/ +// define('DEFAULT_ENDPOINT', 'localhost.localstack.cloud:4566'); // Default Amazon S3 Access Key define('DEFAULT_ACCESS_KEY', 'your s3 access key'); // Default Amazon S3 Secret Key @@ -23,6 +25,8 @@ define('DEFAULT_DUALSTACK', false); define('DEFAULT_PATH_ACCESS', false); // Should I use SSL by default? define('DEFAULT_SSL', true); +// Create the 2100 test files in the bucket? +define('CREATE_2100_FILES', true); /** * Tests for standard key pairs allowing us to read, write and delete @@ -33,7 +37,9 @@ $standardTests = [ 'BucketsList', 'BucketLocation', 'SmallFiles', + 'HeadObject', 'SmallInlineFiles', + 'SmallInlineXMLFiles', 'SignedURLs', 'StorageClasses', 'ListFiles', diff --git a/s3_storage/vendor/akeeba/s3/minitest/minitest.php b/s3_storage/vendor/akeeba/s3/minitest/minitest.php index e9490321..21e9dce1 100644 --- a/s3_storage/vendor/akeeba/s3/minitest/minitest.php +++ b/s3_storage/vendor/akeeba/s3/minitest/minitest.php @@ -3,13 +3,13 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -use Akeeba\Engine\Postproc\Connector\S3v4\Configuration; -use Akeeba\Engine\Postproc\Connector\S3v4\Connector; -use Akeeba\Engine\Postproc\Connector\S3v4\Input; +use Akeeba\S3\Configuration; +use Akeeba\S3\Connector; +use Akeeba\S3\Input; // Necessary for including the library define('AKEEBAENGINE', 1); @@ -167,7 +167,7 @@ foreach ($testConfigurations as $description => $setup) 'dualstack' => DEFAULT_DUALSTACK, 'path_access' => DEFAULT_PATH_ACCESS, 'ssl' => DEFAULT_SSL, - 'endpoint' => null, + 'endpoint' => defined('DEFAULT_ENDPOINT') ? constant('DEFAULT_ENDPOINT') : null, ], $setup['configuration']); // Extract the test classes/methods to run @@ -185,15 +185,21 @@ foreach ($testConfigurations as $description => $setup) // Create the S3 configuration object $s3Configuration = new Configuration($configOptions['access'], $configOptions['secret'], $configOptions['signature'], $configOptions['region']); - $s3Configuration->setUseDualstackUrl($configOptions['dualstack']); - $s3Configuration->setUseLegacyPathStyle($configOptions['path_access']); - $s3Configuration->setSSL($configOptions['ssl']); + $s3Configuration->setRegion($configOptions['region']); + $s3Configuration->setSignatureMethod($configOptions['signature']); if (!is_null($configOptions['endpoint'])) { $s3Configuration->setEndpoint($configOptions['endpoint']); + // We need to redo this because setting the endpoint may reset these options + $s3Configuration->setRegion($configOptions['region']); + $s3Configuration->setSignatureMethod($configOptions['signature']); } + $s3Configuration->setUseDualstackUrl($configOptions['dualstack']); + $s3Configuration->setUseLegacyPathStyle($configOptions['path_access']); + $s3Configuration->setSSL($configOptions['ssl']); + // Create the connector object $s3Connector = new Connector($s3Configuration); diff --git a/s3_storage/vendor/akeeba/s3/src/Acl.php b/s3_storage/vendor/akeeba/s3/src/Acl.php index 36952cba..3a58f5e8 100644 --- a/s3_storage/vendor/akeeba/s3/src/Acl.php +++ b/s3_storage/vendor/akeeba/s3/src/Acl.php @@ -3,29 +3,29 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); /** * Shortcuts to often used access control privileges */ class Acl { - const ACL_PRIVATE = 'private'; + public const ACL_PRIVATE = 'private'; - const ACL_PUBLIC_READ = 'public-read'; + public const ACL_PUBLIC_READ = 'public-read'; - const ACL_PUBLIC_READ_WRITE = 'public-read-write'; + public const ACL_PUBLIC_READ_WRITE = 'public-read-write'; - const ACL_AUTHENTICATED_READ = 'authenticated-read'; + public const ACL_AUTHENTICATED_READ = 'authenticated-read'; - const ACL_BUCKET_OWNER_READ = 'bucket-owner-read'; + public const ACL_BUCKET_OWNER_READ = 'bucket-owner-read'; - const ACL_BUCKET_OWNER_FULL_CONTROL = 'bucket-owner-full-control'; + public const ACL_BUCKET_OWNER_FULL_CONTROL = 'bucket-owner-full-control'; } diff --git a/s3_storage/vendor/akeeba/s3/src/Configuration.php b/s3_storage/vendor/akeeba/s3/src/Configuration.php index 13540dd0..ca5fa19e 100644 --- a/s3_storage/vendor/akeeba/s3/src/Configuration.php +++ b/s3_storage/vendor/akeeba/s3/src/Configuration.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); /** * Holds the Amazon S3 confiugration credentials @@ -199,6 +199,8 @@ class Configuration throw new Exception\InvalidSignatureMethod; } + $this->signatureMethod = $signatureMethod; + // If you switch to v2 signatures we unset the region. if ($signatureMethod == 'v2') { @@ -214,13 +216,7 @@ class Configuration $this->setUseLegacyPathStyle(false); } - } else { - if (empty($this->getRegion())) { - $this->setRegion('us-east-1'); - } } - - $this->signatureMethod = $signatureMethod; } /** diff --git a/s3_storage/vendor/akeeba/s3/src/Connector.php b/s3_storage/vendor/akeeba/s3/src/Connector.php index 4bf52caa..54fdc3cc 100644 --- a/s3_storage/vendor/akeeba/s3/src/Connector.php +++ b/s3_storage/vendor/akeeba/s3/src/Connector.php @@ -3,22 +3,22 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; // Protection against direct access -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotDeleteFile; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotGetBucket; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotGetFile; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotListBuckets; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotOpenFileForWrite; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\CannotPutFile; -use Akeeba\Engine\Postproc\Connector\S3v4\Response\Error; +use Akeeba\S3\Exception\CannotDeleteFile; +use Akeeba\S3\Exception\CannotGetBucket; +use Akeeba\S3\Exception\CannotGetFile; +use Akeeba\S3\Exception\CannotListBuckets; +use Akeeba\S3\Exception\CannotOpenFileForWrite; +use Akeeba\S3\Exception\CannotPutFile; +use Akeeba\S3\Response\Error; -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); class Connector { @@ -81,7 +81,10 @@ class Connector if (($input->getSize() <= 0) || (($input->getInputType() == Input::INPUT_DATA) && (!strlen($input->getDataReference())))) { - throw new CannotPutFile('Missing input parameters', 0); + if (substr($uri, -1) !== '/') + { + throw new CannotPutFile('Missing input parameters', 0); + } } // We need to post with Content-Length and Content-Type, MD5 is optional @@ -169,7 +172,7 @@ class Connector if (!is_resource($saveTo) && is_string($saveTo)) { - $fp = @fopen($saveTo, 'wb'); + $fp = @fopen($saveTo, 'w'); if ($fp === false) { @@ -193,6 +196,53 @@ class Connector $request->setHeader('Range', "bytes=$from-$to"); } + $response = $request->getResponse(true); + + if (!$response->error->isError() && (($response->code !== 200) && ($response->code !== 206))) + { + $response->error = new Error( + $response->code, + "Unexpected HTTP status {$response->code}" + ); + } + + if ($response->error->isError()) + { + throw new CannotGetFile( + sprintf( + __METHOD__ . "({%s}, {%s}): [%s] %s\n\nDebug info:\n%s", + $bucket, + $uri, + $response->error->getCode(), + $response->error->getMessage(), + print_r($response->body, true) + ) + ); + } + + if (!is_resource($fp)) + { + return $response->body; + } + + return null; + } + + /** + * Get information about an object. + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * + * @return array The headers returned by Amazon S3 + * + * @throws CannotGetFile If the file does not exist + * @see https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html + */ + public function headObject(string $bucket, string $uri): array + { + $request = new Request('HEAD', $bucket, $uri, $this->configuration); + $response = $request->getResponse(); if (!$response->error->isError() && (($response->code !== 200) && ($response->code !== 206))) @@ -206,20 +256,21 @@ class Connector if ($response->error->isError()) { throw new CannotGetFile( - sprintf(__METHOD__ . "({$bucket}, {$uri}): [%s] %s\n\nDebug info:\n%s", - $response->error->getCode(), $response->error->getMessage(), print_r($response->body, true)), - $response->error->getCode() + sprintf( + __METHOD__ . "({%s}, {%s}): [%s] %s\n\nDebug info:\n%s", + $bucket, + $uri, + $response->error->getCode(), + $response->error->getMessage(), + print_r($response->body, true) + ) ); } - if (!is_resource($fp)) - { - return $response->body; - } - - return null; + return $response->getHeaders(); } + /** * Delete an object * @@ -244,9 +295,13 @@ class Connector if ($response->error->isError()) { throw new CannotDeleteFile( - sprintf(__METHOD__ . "({$bucket}, {$uri}): [%s] %s", - $response->error->getCode(), $response->error->getMessage()), - $response->error->getCode() + sprintf( + __METHOD__ . "({%s}, {%s}): [%s] %s", + $bucket, + $uri, + $response->error->getCode(), + $response->error->getMessage() + ) ); } } @@ -358,8 +413,7 @@ class Connector if ($response->error->isError()) { throw new CannotGetBucket( - sprintf(__METHOD__ . "(): [%s] %s", $response->error->getCode(), $response->error->getMessage()), - $response->error->getCode() + sprintf(__METHOD__ . "(): [%s] %s", $response->error->getCode(), $response->error->getMessage()) ); } @@ -403,168 +457,47 @@ class Connector */ public function getBucket(string $bucket, ?string $prefix = null, ?string $marker = null, ?int $maxKeys = null, string $delimiter = '/', bool $returnCommonPrefixes = false): array { - $request = new Request('GET', $bucket, '', $this->configuration); + $internalResult = $this->internalGetBucket($bucket, $prefix, $marker, $maxKeys, $delimiter, $returnCommonPrefixes); - if (!empty($prefix)) - { - $request->setParameter('prefix', $prefix); - } + /** + * @var array $objects + * @var ?string $nextMarker + */ + extract($internalResult); + unset($internalResult); - if (!empty($marker)) - { - $request->setParameter('marker', $marker); - } - - if (!empty($maxKeys)) - { - $request->setParameter('max-keys', $maxKeys); - } - - if (!empty($delimiter)) - { - $request->setParameter('delimiter', $delimiter); - } - - $response = $request->getResponse(); - - if (!$response->error->isError() && $response->code !== 200) - { - $response->error = new Error( - $response->code, - "Unexpected HTTP status {$response->code}" - ); - } - - if ($response->error->isError()) - { - throw new CannotGetBucket( - sprintf(__METHOD__ . "(): [%s] %s", $response->error->getCode(), $response->error->getMessage()), - $response->error->getCode() - ); - } - - $results = []; - - $nextMarker = null; - - if ($response->hasBody() && isset($response->body->Contents)) - { - foreach ($response->body->Contents as $c) - { - $results[(string) $c->Key] = [ - 'name' => (string) $c->Key, - 'time' => strtotime((string) $c->LastModified), - 'size' => (int) $c->Size, - 'hash' => substr((string) $c->ETag, 1, -1), - ]; - - $nextMarker = (string) $c->Key; - } - } - - if ($returnCommonPrefixes && $response->hasBody() && isset($response->body->CommonPrefixes)) - { - foreach ($response->body->CommonPrefixes as $c) - { - $results[(string) $c->Prefix] = ['prefix' => (string) $c->Prefix]; - } - } - - if ($response->hasBody() && isset($response->body->IsTruncated) && - ((string) $response->body->IsTruncated == 'false') - ) - { - return $results; - } - - if ($response->hasBody() && isset($response->body->NextMarker)) - { - $nextMarker = (string) $response->body->NextMarker; - } - - // Is it a truncated result? - $isTruncated = ($nextMarker !== null) && ((string) $response->body->IsTruncated == 'true'); - // Is this a truncated result and no maxKeys specified? - $isTruncatedAndNoMaxKeys = ($maxKeys == null) && $isTruncated; - // Is this a truncated result with less keys than the specified maxKeys; and common prefixes found but not returned to the caller? - $isTruncatedAndNeedsContinue = ($maxKeys != null) && $isTruncated && (count($results) < $maxKeys); - - // Loop through truncated results if maxKeys isn't specified - if ($isTruncatedAndNoMaxKeys || $isTruncatedAndNeedsContinue) + // Loop through truncated results if maxKeys isn't specified or we don't have enough object records yet. + if ($nextMarker !== null && ($maxKeys === null || count($objects) < $maxKeys)) { do { - $request = new Request('GET', $bucket, '', $this->configuration); + $internalResult = $this->internalGetBucket($bucket, $prefix, $nextMarker, $maxKeys, $delimiter, $returnCommonPrefixes); - if (!empty($prefix)) - { - $request->setParameter('prefix', $prefix); - } + $nextMarker = $internalResult['nextMarker']; + $objects = array_merge($objects, $internalResult['objects']); - $request->setParameter('marker', $nextMarker); + unset($internalResult); - if (!empty($delimiter)) - { - $request->setParameter('delimiter', $delimiter); - } - - try - { - $response = $request->getResponse(); - } - catch (\Exception $e) + // If the last call did not return a nextMarker I am done iterating. + if ($nextMarker === null) { break; } - if ($response->hasBody() && isset($response->body->Contents)) + // If we have maxKeys AND the number of objects is at least this many I am done iterating. + if ($maxKeys !== null && count($objects) >= $maxKeys) { - foreach ($response->body->Contents as $c) - { - $results[(string) $c->Key] = [ - 'name' => (string) $c->Key, - 'time' => strtotime((string) $c->LastModified), - 'size' => (int) $c->Size, - 'hash' => substr((string) $c->ETag, 1, -1), - ]; - - $nextMarker = (string) $c->Key; - } + break; } - - if ($returnCommonPrefixes && $response->hasBody() && isset($response->body->CommonPrefixes)) - { - foreach ($response->body->CommonPrefixes as $c) - { - $results[(string) $c->Prefix] = ['prefix' => (string) $c->Prefix]; - } - } - - if ($response->hasBody() && isset($response->body->NextMarker)) - { - $nextMarker = (string) $response->body->NextMarker; - } - - $continueCondition = false; - - if ($isTruncatedAndNoMaxKeys) - { - $continueCondition = !$response->error->isError() && $isTruncated; - } - - if ($isTruncatedAndNeedsContinue) - { - $continueCondition = !$response->error->isError() && $isTruncated && (count($results) < $maxKeys); - } - } while ($continueCondition); + } while (true); } - if (!is_null($maxKeys)) + if ($maxKeys !== null) { - $results = array_splice($results, 0, $maxKeys); + return array_splice($objects, 0, $maxKeys); } - return $results; + return $objects; } /** @@ -594,8 +527,7 @@ class Connector if ($response->error->isError()) { throw new CannotListBuckets( - sprintf(__METHOD__ . "(): [%s] %s", $response->error->getCode(), $response->error->getMessage()), - $response->error->getCode() + sprintf(__METHOD__ . "(): [%s] %s", $response->error->getCode(), $response->error->getMessage()) ); } @@ -691,7 +623,12 @@ class Connector if ($response->error->isError()) { throw new CannotPutFile( - sprintf(__METHOD__ . "(): [%s] %s\n\nDebug info:\n%s", $response->error->getCode(), $response->error->getMessage(), print_r($response->body, true)) + sprintf( + __METHOD__ . "(): [%s] %s\n\nDebug info:\n%s", + $response->error->getCode(), + $response->error->getMessage(), + print_r($response->body, true) + ) ); } @@ -958,4 +895,90 @@ class Connector { return $this->configuration; } + + private function internalGetBucket(string $bucket, ?string $prefix = null, ?string $marker = null, ?int $maxKeys = null, string $delimiter = '/', bool $returnCommonPrefixes = false): array + { + $request = new Request('GET', $bucket, '', $this->configuration); + + if (!empty($prefix)) + { + $request->setParameter('prefix', $prefix); + } + + if (!empty($marker)) + { + $request->setParameter('marker', $marker); + } + + if (!empty($maxKeys)) + { + $request->setParameter('max-keys', $maxKeys); + } + + if (!empty($delimiter)) + { + $request->setParameter('delimiter', $delimiter); + } + + $response = $request->getResponse(); + + if (!$response->error->isError() && $response->code !== 200) + { + $response->error = new Error( + $response->code, + "Unexpected HTTP status {$response->code}" + ); + } + + if ($response->error->isError()) + { + throw new CannotGetBucket( + sprintf(__METHOD__ . "(): [%s] %s", $response->error->getCode(), $response->error->getMessage()) + ); + } + + $results = [ + 'objects' => [], + 'nextMarker' => null, + ]; + + if ($response->hasBody() && isset($response->body->Contents)) + { + foreach ($response->body->Contents as $c) + { + $results['objects'][(string) $c->Key] = [ + 'name' => (string) $c->Key, + 'time' => strtotime((string) $c->LastModified), + 'size' => (int) $c->Size, + 'hash' => substr((string) $c->ETag, 1, -1), + ]; + + $results['nextMarker'] = (string) $c->Key; + } + } + + if ($returnCommonPrefixes && $response->hasBody() && isset($response->body->CommonPrefixes)) + { + foreach ($response->body->CommonPrefixes as $c) + { + $results['objects'][(string) $c->Prefix] = ['prefix' => (string) $c->Prefix]; + } + } + + if ($response->hasBody() && isset($response->body->IsTruncated) && + ((string) $response->body->IsTruncated == 'false') + ) + { + $results['nextMarker'] = null; + + return $results; + } + + if ($response->hasBody() && isset($response->body->NextMarker)) + { + $results['nextMarker'] = (string) $response->body->NextMarker; + } + + return $results; + } } diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotDeleteFile.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotDeleteFile.php index b98c566e..05e3fa50 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotDeleteFile.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotDeleteFile.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetBucket.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetBucket.php index 6047a4b4..e87c0994 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetBucket.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetBucket.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetFile.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetFile.php index b9719353..5a9d8e82 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetFile.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotGetFile.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotListBuckets.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotListBuckets.php index de29bcbd..7a365fba 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotListBuckets.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotListBuckets.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForRead.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForRead.php index 6a206ffe..78aa5cd2 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForRead.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForRead.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForWrite.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForWrite.php index ecec4e70..24f8a724 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForWrite.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotOpenFileForWrite.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/CannotPutFile.php b/s3_storage/vendor/akeeba/s3/src/Exception/CannotPutFile.php index 9f53c4f5..a7a2a478 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/CannotPutFile.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/CannotPutFile.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/ConfigurationError.php b/s3_storage/vendor/akeeba/s3/src/Exception/ConfigurationError.php index 60d64a6c..2699b5e7 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/ConfigurationError.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/ConfigurationError.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidAccessKey.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidAccessKey.php index dd561686..3db444b4 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidAccessKey.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidAccessKey.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidBody.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidBody.php index 6e1e1690..57b628fd 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidBody.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidBody.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; use RuntimeException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidEndpoint.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidEndpoint.php index b35e56d0..3c58c035 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidEndpoint.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidEndpoint.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidFilePointer.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidFilePointer.php index 084b3cd0..15c546cd 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidFilePointer.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidFilePointer.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; use InvalidArgumentException; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidRegion.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidRegion.php index fe6abc08..e21c1f67 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidRegion.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidRegion.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSecretKey.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSecretKey.php index 2348c289..af58e5d9 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSecretKey.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSecretKey.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSignatureMethod.php b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSignatureMethod.php index 6145ed1c..bd4fdedc 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSignatureMethod.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/InvalidSignatureMethod.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use Exception; diff --git a/s3_storage/vendor/akeeba/s3/src/Exception/PropertyNotFound.php b/s3_storage/vendor/akeeba/s3/src/Exception/PropertyNotFound.php index c397fee8..3f521532 100644 --- a/s3_storage/vendor/akeeba/s3/src/Exception/PropertyNotFound.php +++ b/s3_storage/vendor/akeeba/s3/src/Exception/PropertyNotFound.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Exception; +namespace Akeeba\S3\Exception; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); use LogicException; diff --git a/s3_storage/vendor/akeeba/s3/src/Input.php b/s3_storage/vendor/akeeba/s3/src/Input.php index 4420bdba..5dd5743d 100644 --- a/s3_storage/vendor/akeeba/s3/src/Input.php +++ b/s3_storage/vendor/akeeba/s3/src/Input.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); /** * Defines an input source for PUT/POST requests to Amazon S3 @@ -20,17 +20,17 @@ class Input /** * Input type: resource */ - const INPUT_RESOURCE = 1; + public const INPUT_RESOURCE = 1; /** * Input type: file */ - const INPUT_FILE = 2; + public const INPUT_FILE = 2; /** * Input type: raw data */ - const INPUT_DATA = 3; + public const INPUT_DATA = 3; /** * File pointer, in case we have a resource @@ -177,7 +177,13 @@ class Input { if (is_resource($this->fp)) { - @fclose($this->fp); + try + { + @fclose($this->fp); + } + catch (\Throwable $e) + { + } } } @@ -258,10 +264,16 @@ class Input if (is_resource($this->fp)) { - @fclose($this->fp); + try + { + @fclose($this->fp); + } + catch (\Throwable $e) + { + } } - $this->fp = @fopen($file, 'rb'); + $this->fp = @fopen($file, 'r'); if ($this->fp === false) { @@ -295,7 +307,13 @@ class Input if (is_resource($this->fp)) { - @fclose($this->fp); + try + { + @fclose($this->fp); + } + catch (\Throwable $e) + { + } } $this->file = null; @@ -329,7 +347,13 @@ class Input if (is_resource($this->fp)) { - @fclose($this->fp); + try + { + @fclose($this->fp); + } + catch (\Throwable $e) + { + } } $this->file = null; @@ -450,7 +474,7 @@ class Input */ public function setSha256(?string $sha256): void { - $this->sha256 = strtolower($sha256); + $this->sha256 = is_null($sha256) ? null : strtolower($sha256); } /** @@ -532,7 +556,7 @@ class Input switch ($this->getInputType()) { case self::INPUT_DATA: - return function_exists('mb_strlen') ? mb_strlen($this->data, '8bit') : strlen($this->data); + return function_exists('mb_strlen') ? mb_strlen($this->data ?? '', '8bit') : strlen($this->data ?? ''); break; case self::INPUT_FILE: @@ -635,7 +659,7 @@ class Input $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); - return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; + return $exts[$ext] ?? 'application/octet-stream'; } /** diff --git a/s3_storage/vendor/akeeba/s3/src/Request.php b/s3_storage/vendor/akeeba/s3/src/Request.php index 172113fc..4237b862 100644 --- a/s3_storage/vendor/akeeba/s3/src/Request.php +++ b/s3_storage/vendor/akeeba/s3/src/Request.php @@ -3,16 +3,16 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; -use Akeeba\Engine\Postproc\Connector\S3v4\Response\Error; +use Akeeba\S3\Response\Error; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); class Request @@ -142,6 +142,12 @@ class Request // The date must always be added as a header $this->headers['Date'] = gmdate('D, d M Y H:i:s O'); + // S3-"compatible" services use a different date format. Because why not? + if (strpos($this->headers['Host'], '.amazonaws.com') === false) + { + $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); + } + // If there is a security token we need to set up the X-Amz-Security-Token header $token = $this->configuration->getToken(); @@ -367,7 +373,7 @@ class Request * * @return Response */ - public function getResponse(): Response + public function getResponse(bool $rawResponse = false): Response { $this->processParametersIntoResource(); @@ -417,8 +423,10 @@ class Request * Caveat: if your bucket contains dots in the name we have to turn off host verification due to the way the * S3 SSL certificates are set up. */ - $isAmazonS3 = (substr($this->headers['Host'], -14) == '.amazonaws.com') || - substr($this->headers['Host'], -16) == 'amazonaws.com.cn'; + $isAmazonS3 = (substr($this->headers['Host'], -14) == '.amazonaws.com') + || substr( + $this->headers['Host'], -16 + ) == 'amazonaws.com.cn'; $tooManyDots = substr_count($this->headers['Host'], '.') > 4; $verifyHost = ($isAmazonS3 && $tooManyDots) ? 0 : 2; @@ -429,6 +437,27 @@ class Request curl_setopt($curl, CURLOPT_URL, $url); + /** + * Set the optional x-amz-date header for third party services. + * + * Amazon S3 proper expects to get the date from the Date header. Third party services typically implement the + * (wrongly) documented behaviour of using the x-amz-date header but, if it's missing, fall back to the Date + * header. Wasabi does not fall back; it only uses the x-amz-date header which is why we have to set it here if + * the request iss not made to Amazon S3 proper. + */ + $this->headers['x-amz-date'] = strpos($this->headers['Host'], '.amazonaws.com') !== false + ? '' + : (new \DateTime($this->headers['Date']))->format('Ymd\THis\Z'); + + /** + * Remove empty headers. + * + * While Amazon S3 proper and most third party implementations have no problem with that, there a few of them + * (such as Synology C2) which choke on empty headers. + */ + $this->headers = array_filter($this->headers); + + // Get the request signature $signer = Signature::getSignatureObject($this, $this->configuration->getSignatureMethod()); $signer->preProcessHeaders($this->headers, $this->amzHeaders); @@ -482,7 +511,7 @@ class Request $data = $this->input->getDataReference(); - if (strlen($data)) + if (strlen($data ?? '')) { curl_setopt($curl, CURLOPT_POSTFIELDS, $data); } @@ -538,12 +567,18 @@ class Request @curl_close($curl); // Set the body data - $this->response->finaliseBody(); + $this->response->finaliseBody($rawResponse); // Clean up file resources if (!is_null($this->fp) && is_resource($this->fp)) { - fclose($this->fp); + try + { + @fclose($this->fp); + } + catch (\Throwable $e) + { + } } return $this->response; @@ -560,7 +595,7 @@ class Request */ protected function __responseWriteCallback($curl, string $data): int { - if (in_array($this->response->code, [200, 206]) && !is_null($this->fp) && is_resource($this->fp)) + if (in_array($this->response->code, [0, 200, 206]) && !is_null($this->fp) && is_resource($this->fp)) { return fwrite($this->fp, $data); } @@ -573,7 +608,7 @@ class Request /** * cURL header callback * - * @param resource $curl cURL resource + * @param resource $curl cURL resource * @param string &$data Data * * @return int Length in bytes @@ -592,7 +627,15 @@ class Request return $strlen; } - [$header, $value] = explode(': ', trim($data), 2); + // Ignore malformed headers without a value. + if (strpos($data, ':') === false) + { + return $strlen; + } + + [$header, $value] = explode(':', trim($data), 2); + $header = trim($header ?? ''); + $value = trim($value ?? ''); switch (strtolower($header)) { @@ -609,10 +652,12 @@ class Request break; case 'etag': - $this->response->setHeader('hash', $value[0] == '"' ? substr($value, 1, -1) : $value); + $this->response->setHeader('hash', trim($value, '"')); break; default: + $this->response->setHeader(strtolower($header), is_numeric($value) ? (int) $value : $value); + if (preg_match('/^x-amz-meta-.*$/', $header)) { $this->setHeader($header, is_numeric($value) ? (int) $value : $value); @@ -652,13 +697,12 @@ class Request $query = substr($query, 0, -1); $this->uri .= $query; - if (array_key_exists('acl', $this->parameters) || - array_key_exists('location', $this->parameters) || - array_key_exists('torrent', $this->parameters) || - array_key_exists('logging', $this->parameters) || - array_key_exists('uploads', $this->parameters) || - array_key_exists('uploadId', $this->parameters) || - array_key_exists('partNumber', $this->parameters) + if (array_key_exists('acl', $this->parameters) || array_key_exists('location', $this->parameters) + || array_key_exists('torrent', $this->parameters) + || array_key_exists('logging', $this->parameters) + || array_key_exists('uploads', $this->parameters) + || array_key_exists('uploadId', $this->parameters) + || array_key_exists('partNumber', $this->parameters) ) { $this->resource .= $query; @@ -720,6 +764,8 @@ class Request } /** + * Only applies to Amazon S3 proper. + * * When using the Amazon S3 with the v4 signature API we have to use a different hostname per region. The * mapping can be found in https://docs.aws.amazon.com/general/latest/gr/s3.html#s3_region * @@ -728,25 +774,27 @@ class Request * * v4 signing does NOT support non-Amazon endpoints. */ - - // Most endpoints: s3-REGION.amazonaws.com - $regionalEndpoint = $region . '.amazonaws.com'; - - // Exception: China - if (substr($region, 0, 3) == 'cn-') + if (in_array($endpoint, ['s3.amazonaws.com', 'amazonaws.com.cn'])) { - // Chinese endpoint, e.g.: s3.cn-north-1.amazonaws.com.cn - $regionalEndpoint = $regionalEndpoint . '.cn'; - } + // Most endpoints: s3-REGION.amazonaws.com + $regionalEndpoint = $region . '.amazonaws.com'; - // If dual-stack URLs are enabled then prepend the endpoint - if ($configuration->getDualstackUrl()) - { - $endpoint = 's3.dualstack.' . $regionalEndpoint; - } - else - { - $endpoint = 's3.' . $regionalEndpoint; + // Exception: China + if (substr($region, 0, 3) == 'cn-') + { + // Chinese endpoint, e.g.: s3.cn-north-1.amazonaws.com.cn + $regionalEndpoint = $regionalEndpoint . '.cn'; + } + + // If dual-stack URLs are enabled then prepend the endpoint + if ($configuration->getDualstackUrl()) + { + $endpoint = 's3.dualstack.' . $regionalEndpoint; + } + else + { + $endpoint = 's3.' . $regionalEndpoint; + } } // Legacy path style access: return just the endpoint diff --git a/s3_storage/vendor/akeeba/s3/src/Response.php b/s3_storage/vendor/akeeba/s3/src/Response.php index a8078b29..01810941 100644 --- a/s3_storage/vendor/akeeba/s3/src/Response.php +++ b/s3_storage/vendor/akeeba/s3/src/Response.php @@ -3,18 +3,18 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; -use Akeeba\Engine\Postproc\Connector\S3v4\Exception\PropertyNotFound; -use Akeeba\Engine\Postproc\Connector\S3v4\Response\Error; +use Akeeba\S3\Exception\PropertyNotFound; +use Akeeba\S3\Response\Error; use SimpleXMLElement; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); /** * Amazon S3 API response object @@ -124,7 +124,7 @@ class Response * * @param string|SimpleXMLElement|null $body */ - public function setBody($body): void + public function setBody($body, bool $rawResponse = false): void { $this->body = null; @@ -135,7 +135,7 @@ class Response $this->body = $body; - $this->finaliseBody(); + $this->finaliseBody($rawResponse); } public function resetBody(): void @@ -153,7 +153,7 @@ class Response $this->body .= $data; } - public function finaliseBody(): void + public function finaliseBody(bool $rawResponse = false): void { if (!$this->hasBody()) { @@ -165,8 +165,14 @@ class Response $this->headers['type'] = 'text/plain'; } - if (is_string($this->body) && - (($this->headers['type'] == 'application/xml') || (substr($this->body, 0, 5) == 'body) + && + ( + ($this->headers['type'] == 'application/xml') + || (substr($this->body, 0, 5) == 'body = simplexml_load_string($this->body); @@ -332,8 +338,8 @@ class Response ) { $this->error = new Error( - $this->code, - (string) $this->body->Message + 500, + (string) $this->body->Code . ':' . (string) $this->body->Message ); if (isset($this->body->Resource)) diff --git a/s3_storage/vendor/akeeba/s3/src/Response/Error.php b/s3_storage/vendor/akeeba/s3/src/Response/Error.php index ca40985d..69710bdf 100644 --- a/s3_storage/vendor/akeeba/s3/src/Response/Error.php +++ b/s3_storage/vendor/akeeba/s3/src/Response/Error.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Response; +namespace Akeeba\S3\Response; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); /** * S3 response error object diff --git a/s3_storage/vendor/akeeba/s3/src/Signature.php b/s3_storage/vendor/akeeba/s3/src/Signature.php index c841ebe1..93d8f0f6 100644 --- a/s3_storage/vendor/akeeba/s3/src/Signature.php +++ b/s3_storage/vendor/akeeba/s3/src/Signature.php @@ -3,14 +3,14 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); /** * Base class for request signing objects. @@ -44,7 +44,7 @@ abstract class Signature */ public static function getSignatureObject(Request $request, string $method = 'v2'): self { - $className = '\\Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature\\' . ucfirst($method); + $className = __NAMESPACE__ . '\\Signature\\' . ucfirst($method); return new $className($request); } diff --git a/s3_storage/vendor/akeeba/s3/src/Signature/V2.php b/s3_storage/vendor/akeeba/s3/src/Signature/V2.php index 86d9be75..6864846f 100644 --- a/s3_storage/vendor/akeeba/s3/src/Signature/V2.php +++ b/s3_storage/vendor/akeeba/s3/src/Signature/V2.php @@ -3,16 +3,16 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Signature; +namespace Akeeba\S3\Signature; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); -use Akeeba\Engine\Postproc\Connector\S3v4\Signature; +use Akeeba\S3\Signature; /** * Implements the Amazon AWS v2 signatures @@ -123,7 +123,7 @@ class V2 extends Signature } // AMZ headers must be sorted and sent as separate lines - if (sizeof($amz) > 0) + if (count($amz) > 0) { sort($amz); $amzString = "\n" . implode("\n", $amz); @@ -150,8 +150,8 @@ class V2 extends Signature } $stringToSign = $verb . "\n" . - (isset($headers['Content-MD5']) ? $headers['Content-MD5'] : '') . "\n" . - (isset($headers['Content-Type']) ? $headers['Content-Type'] : '') . "\n" . + ($headers['Content-MD5'] ?? '') . "\n" . + ($headers['Content-Type'] ?? '') . "\n" . $headers['Date'] . $amzString . "\n" . $resourcePath; diff --git a/s3_storage/vendor/akeeba/s3/src/Signature/V4.php b/s3_storage/vendor/akeeba/s3/src/Signature/V4.php index e2bb908e..b39f81f4 100644 --- a/s3_storage/vendor/akeeba/s3/src/Signature/V4.php +++ b/s3_storage/vendor/akeeba/s3/src/Signature/V4.php @@ -3,16 +3,16 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4\Signature; +namespace Akeeba\S3\Signature; // Protection against direct access -defined('AKEEBAENGINE') or die(); +defined('AKEEBAENGINE') || die(); -use Akeeba\Engine\Postproc\Connector\S3v4\Signature; +use Akeeba\S3\Signature; use DateTime; /** @@ -77,14 +77,20 @@ class V4 extends Signature * http://s3-eu-west-1.amazonaws.com/example instead of http://example.amazonaws.com/ for all authenticated URLs */ $region = $this->request->getConfiguration()->getRegion(); + $bucket = $this->request->getBucket(); $hostname = $this->getPresignedHostnameForRegion($region); + + if ($this->isValidBucketName($bucket)) + { + $hostname = $bucket . '.' . $hostname; + } + $this->request->setHeader('Host', $hostname); // Set the expiration time in seconds $this->request->setHeader('Expires', (int) $lifetime); // Get the query parameters, including the calculated signature - $bucket = $this->request->getBucket(); $uri = $this->request->getResource(); $headers = $this->request->getHeaders(); $protocol = $https ? 'https' : 'http'; @@ -93,6 +99,11 @@ class V4 extends Signature // The query parameters are returned serialized; unserialize them, then build and return the URL. $queryParameters = unserialize($serialisedParams); + if ($this->isValidBucketName($bucket) && strpos($uri, '/' . $bucket) === 0) + { + $uri = substr($uri, strlen($bucket) + 1); + } + $query = http_build_query($queryParameters); $url = $protocol . '://' . $headers['Host'] . $uri; @@ -129,8 +140,8 @@ class V4 extends Signature $signatureDate = new DateTime($headers['Date']); $credentialScope = $signatureDate->format('Ymd') . '/' . - $this->request->getConfiguration()->getRegion() . '/' . - 's3/aws4_request'; + $this->request->getConfiguration()->getRegion() . '/' . + 's3/aws4_request'; /** * If the Expires header is set up we're pre-signing a download URL. The string to sign is a bit @@ -208,12 +219,14 @@ class V4 extends Signature // The canonical URI is the resource path $canonicalURI = $resourcePath; $bucketResource = '/' . $bucket; - $regionalHostname = ($headers['Host'] != 's3.amazonaws.com') && ($headers['Host'] != $bucket . '.s3.amazonaws.com'); + $regionalHostname = ($headers['Host'] != 's3.amazonaws.com') + && ($headers['Host'] != $bucket . '.s3.amazonaws.com'); // Special case: if the canonical URI ends in /?location the bucket name DOES count as part of the canonical URL // even though the Host is s3.amazonaws.com (in which case it normally shouldn't count). Yeah, I know, it makes // no sense!!! - if (!$regionalHostname && ($headers['Host'] == 's3.amazonaws.com') && (substr($canonicalURI, -10) == '/?location')) + if (!$regionalHostname && ($headers['Host'] == 's3.amazonaws.com') + && (substr($canonicalURI, -10) == '/?location')) { $regionalHostname = true; } @@ -274,11 +287,11 @@ class V4 extends Signature // Calculate the canonical request $canonicalRequest = $verb . "\n" . - $canonicalURI . "\n" . - $canonicalQueryString . "\n" . - $canonicalHeaders . "\n" . - $signedHeaders . "\n" . - $requestPayloadHash; + $canonicalURI . "\n" . + $canonicalQueryString . "\n" . + $canonicalHeaders . "\n" . + $signedHeaders . "\n" . + $requestPayloadHash; $hashedCanonicalRequest = hash('sha256', $canonicalRequest); @@ -290,17 +303,40 @@ class V4 extends Signature $headers['Date'] = ''; } + /** + * The Date in the String-to-Sign is a messy situation. + * + * Amazon's documentation says it must be in ISO 8601 format: `Ymd\THis\Z`. Unfortunately, Amazon's + * documentation is actually wrong :troll_face: The actual Amazon S3 service expects the date to be formatted as + * per RFC1123. + * + * Most third party implementations have caught up to the fact that Amazon has documented the v4 signatures + * wrongly (naughty AWS!) and accept either format. + * + * Some other third party implementations, which never bothered to validate their implementations against Amazon + * S3 proper, only expect what Amazon has documented as "ISO 8601". Therefore, we detect third party services + * and switch to the as-documented date format. + * + * Some other third party services, like Wasabi, are broken in yet a different way. They will only use the date + * from the x-amz-date header, WITHOUT falling back to the Date header if the former is not present. This is + * the opposite of Amazon S3 proper which does expect the Date header. That's why the Request class sets both + * headers if the request is made to a service _other_ than Amazon S3 proper. + */ + $dateToSignFor = strpos($headers['Host'], '.amazonaws.com') !== false + ? $headers['Date'] + : $signatureDate->format('Ymd\THis\Z'); + $stringToSign = "AWS4-HMAC-SHA256\n" . - $headers['Date'] . "\n" . - $credentialScope . "\n" . - $hashedCanonicalRequest; + $dateToSignFor . "\n" . + $credentialScope . "\n" . + $hashedCanonicalRequest; if ($isPresignedURL) { $stringToSign = "AWS4-HMAC-SHA256\n" . - $parameters['X-Amz-Date'] . "\n" . - $credentialScope . "\n" . - $hashedCanonicalRequest; + $parameters['X-Amz-Date'] . "\n" . + $credentialScope . "\n" . + $hashedCanonicalRequest; } // ========== Step 3: Calculate the signature ========== @@ -313,9 +349,9 @@ class V4 extends Signature // See http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html $authorization = 'AWS4-HMAC-SHA256 Credential=' . - $this->request->getConfiguration()->getAccess() . '/' . $credentialScope . ', ' . - 'SignedHeaders=' . $signedHeaders . ', ' . - 'Signature=' . $signature; + $this->request->getConfiguration()->getAccess() . '/' . $credentialScope . ', ' . + 'SignedHeaders=' . $signedHeaders . ', ' . + 'Signature=' . $signature; // For presigned URLs we only return the Base64-encoded signature without the AWS format specifier and the // public access key. @@ -366,7 +402,14 @@ class V4 extends Signature */ private function getPresignedHostnameForRegion(string $region): string { - $endpoint = 's3.' . $region . '.amazonaws.com'; + $config = $this->request->getConfiguration(); + $endpoint = $config->getEndpoint(); + + if (empty($endpoint)) + { + $endpoint = 's3.' . $region . '.amazonaws.com'; + } + $dualstackEnabled = $this->request->getConfiguration()->getDualstackUrl(); // If dual-stack URLs are enabled then prepend the endpoint @@ -382,4 +425,83 @@ class V4 extends Signature return $endpoint; } + + /** + * Is this a valid bucket name? + * + * @param string $bucketName The bucket name to check + * @param bool $asSubdomain Should I put additional restrictions for use as a subdomain? + * + * @return bool + * @since 2.3.1 + * + * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html + */ + private function isValidBucketName(string $bucketName, bool $asSubdomain = true): bool + { + /** + * If there are dots in the bucket name I can't use it as a subdomain. + * + * "If you include dots in a bucket's name, you can't use virtual-host-style addressing over HTTPS, unless you + * perform your own certificate validation. This is because the security certificates used for virtual hosting + * of buckets don't work for buckets with dots in their names." + */ + if ($asSubdomain && strpos($bucketName, '.') !== false) + { + return false; + } + + /** + * - Bucket names must be between 3 (min) and 63 (max) characters long. + * - Bucket names can consist only of lowercase letters, numbers, dots (.), and hyphens (-). + */ + if (!preg_match('/^[a-z0-9\-.]{3,63}$/', $bucketName)) + { + return false; + } + + // Bucket names must begin and end with a letter or number. + if (!preg_match('/^[a-z0-9].*[a-z0-9]$/', $bucketName)) + { + return false; + } + + // Bucket names must not contain two adjacent periods. + if (preg_match('/\.\./', $bucketName)) + { + return false; + } + + // Bucket names must not be formatted as an IP address (for example, 192.168.5.4). + if (preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $bucketName)) + { + return false; + } + + // Bucket names must not start with the prefix xn--. + if (strpos($bucketName, 'xn--') === 0) + { + return false; + } + + // Bucket names must not start with the prefix sthree- and the prefix sthree-configurator. + if (strpos($bucketName, 'sthree-') === 0) + { + return false; + } + + // Bucket names must not end with the suffix -s3alias. + if (substr($bucketName, -8) === '-s3alias') + { + return false; + } + + // Bucket names must not end with the suffix --ol-s3. + if (substr($bucketName, -7) === '--ol-s3') + { + return false; + } + + return true; + } } diff --git a/s3_storage/vendor/akeeba/s3/src/StorageClass.php b/s3_storage/vendor/akeeba/s3/src/StorageClass.php index 53ea310f..0c213af2 100644 --- a/s3_storage/vendor/akeeba/s3/src/StorageClass.php +++ b/s3_storage/vendor/akeeba/s3/src/StorageClass.php @@ -3,11 +3,11 @@ * Akeeba Engine * * @package akeebaengine - * @copyright Copyright (c)2006-2020 Nicholas K. Dionysopoulos / Akeeba Ltd + * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ -namespace Akeeba\Engine\Postproc\Connector\S3v4; +namespace Akeeba\S3; /** * Amazon S3 Storage Classes @@ -28,39 +28,39 @@ class StorageClass /** * Amazon S3 Standard (S3 Standard) */ - const STANDARD = 'STANDARD'; + public const STANDARD = 'STANDARD'; /** * Reduced redundancy storage * * Not recommended anymore. Use INTELLIGENT_TIERING instead. */ - const REDUCED_REDUNDANCY = 'REDUCED_REDUNDANCY'; + public const REDUCED_REDUNDANCY = 'REDUCED_REDUNDANCY'; /** * Amazon S3 Intelligent-Tiering (S3 Intelligent-Tiering) */ - const INTELLIGENT_TIERING = 'INTELLIGENT_TIERING'; + public const INTELLIGENT_TIERING = 'INTELLIGENT_TIERING'; /** * Amazon S3 Standard-Infrequent Access (S3 Standard-IA) */ - const STANDARD_IA = 'STANDARD_IA'; + public const STANDARD_IA = 'STANDARD_IA'; /** * Amazon S3 One Zone-Infrequent Access (S3 One Zone-IA) */ - const ONEZONE_IA = 'ONEZONE_IA'; + public const ONEZONE_IA = 'ONEZONE_IA'; /** * Amazon S3 Glacier (S3 Glacier) */ - const GLACIER = 'GLACIER'; + public const GLACIER = 'GLACIER'; /** * Amazon S3 Glacier Deep Archive (S3 Glacier Deep Archive) */ - const DEEP_ARCHIVE = 'DEEP_ARCHIVE'; + public const DEEP_ARCHIVE = 'DEEP_ARCHIVE'; /** * Manipulate the $headers array, setting the X-Amz-Storage-Class header for the requested storage class. diff --git a/s3_storage/vendor/akeeba/s3/src/aliasing.php b/s3_storage/vendor/akeeba/s3/src/aliasing.php new file mode 100644 index 00000000..4b408ac3 --- /dev/null +++ b/s3_storage/vendor/akeeba/s3/src/aliasing.php @@ -0,0 +1,35 @@ + $vendorDir . '/akeeba/s3/src/Acl.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Configuration' => $vendorDir . '/akeeba/s3/src/Configuration.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Connector' => $vendorDir . '/akeeba/s3/src/Connector.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotDeleteFile' => $vendorDir . '/akeeba/s3/src/Exception/CannotDeleteFile.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotGetBucket' => $vendorDir . '/akeeba/s3/src/Exception/CannotGetBucket.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotGetFile' => $vendorDir . '/akeeba/s3/src/Exception/CannotGetFile.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotListBuckets' => $vendorDir . '/akeeba/s3/src/Exception/CannotListBuckets.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotOpenFileForRead' => $vendorDir . '/akeeba/s3/src/Exception/CannotOpenFileForRead.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotOpenFileForWrite' => $vendorDir . '/akeeba/s3/src/Exception/CannotOpenFileForWrite.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotPutFile' => $vendorDir . '/akeeba/s3/src/Exception/CannotPutFile.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\ConfigurationError' => $vendorDir . '/akeeba/s3/src/Exception/ConfigurationError.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidAccessKey' => $vendorDir . '/akeeba/s3/src/Exception/InvalidAccessKey.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidBody' => $vendorDir . '/akeeba/s3/src/Exception/InvalidBody.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidEndpoint' => $vendorDir . '/akeeba/s3/src/Exception/InvalidEndpoint.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidFilePointer' => $vendorDir . '/akeeba/s3/src/Exception/InvalidFilePointer.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidRegion' => $vendorDir . '/akeeba/s3/src/Exception/InvalidRegion.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidSecretKey' => $vendorDir . '/akeeba/s3/src/Exception/InvalidSecretKey.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidSignatureMethod' => $vendorDir . '/akeeba/s3/src/Exception/InvalidSignatureMethod.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\PropertyNotFound' => $vendorDir . '/akeeba/s3/src/Exception/PropertyNotFound.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Input' => $vendorDir . '/akeeba/s3/src/Input.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Request' => $vendorDir . '/akeeba/s3/src/Request.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Response' => $vendorDir . '/akeeba/s3/src/Response.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Response\\Error' => $vendorDir . '/akeeba/s3/src/Response/Error.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature' => $vendorDir . '/akeeba/s3/src/Signature.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature\\V2' => $vendorDir . '/akeeba/s3/src/Signature/V2.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature\\V4' => $vendorDir . '/akeeba/s3/src/Signature/V4.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\StorageClass' => $vendorDir . '/akeeba/s3/src/StorageClass.php', + 'Akeeba\\S3\\Acl' => $vendorDir . '/akeeba/s3/src/Acl.php', + 'Akeeba\\S3\\Configuration' => $vendorDir . '/akeeba/s3/src/Configuration.php', + 'Akeeba\\S3\\Connector' => $vendorDir . '/akeeba/s3/src/Connector.php', + 'Akeeba\\S3\\Exception\\CannotDeleteFile' => $vendorDir . '/akeeba/s3/src/Exception/CannotDeleteFile.php', + 'Akeeba\\S3\\Exception\\CannotGetBucket' => $vendorDir . '/akeeba/s3/src/Exception/CannotGetBucket.php', + 'Akeeba\\S3\\Exception\\CannotGetFile' => $vendorDir . '/akeeba/s3/src/Exception/CannotGetFile.php', + 'Akeeba\\S3\\Exception\\CannotListBuckets' => $vendorDir . '/akeeba/s3/src/Exception/CannotListBuckets.php', + 'Akeeba\\S3\\Exception\\CannotOpenFileForRead' => $vendorDir . '/akeeba/s3/src/Exception/CannotOpenFileForRead.php', + 'Akeeba\\S3\\Exception\\CannotOpenFileForWrite' => $vendorDir . '/akeeba/s3/src/Exception/CannotOpenFileForWrite.php', + 'Akeeba\\S3\\Exception\\CannotPutFile' => $vendorDir . '/akeeba/s3/src/Exception/CannotPutFile.php', + 'Akeeba\\S3\\Exception\\ConfigurationError' => $vendorDir . '/akeeba/s3/src/Exception/ConfigurationError.php', + 'Akeeba\\S3\\Exception\\InvalidAccessKey' => $vendorDir . '/akeeba/s3/src/Exception/InvalidAccessKey.php', + 'Akeeba\\S3\\Exception\\InvalidBody' => $vendorDir . '/akeeba/s3/src/Exception/InvalidBody.php', + 'Akeeba\\S3\\Exception\\InvalidEndpoint' => $vendorDir . '/akeeba/s3/src/Exception/InvalidEndpoint.php', + 'Akeeba\\S3\\Exception\\InvalidFilePointer' => $vendorDir . '/akeeba/s3/src/Exception/InvalidFilePointer.php', + 'Akeeba\\S3\\Exception\\InvalidRegion' => $vendorDir . '/akeeba/s3/src/Exception/InvalidRegion.php', + 'Akeeba\\S3\\Exception\\InvalidSecretKey' => $vendorDir . '/akeeba/s3/src/Exception/InvalidSecretKey.php', + 'Akeeba\\S3\\Exception\\InvalidSignatureMethod' => $vendorDir . '/akeeba/s3/src/Exception/InvalidSignatureMethod.php', + 'Akeeba\\S3\\Exception\\PropertyNotFound' => $vendorDir . '/akeeba/s3/src/Exception/PropertyNotFound.php', + 'Akeeba\\S3\\Input' => $vendorDir . '/akeeba/s3/src/Input.php', + 'Akeeba\\S3\\Request' => $vendorDir . '/akeeba/s3/src/Request.php', + 'Akeeba\\S3\\Response' => $vendorDir . '/akeeba/s3/src/Response.php', + 'Akeeba\\S3\\Response\\Error' => $vendorDir . '/akeeba/s3/src/Response/Error.php', + 'Akeeba\\S3\\Signature' => $vendorDir . '/akeeba/s3/src/Signature.php', + 'Akeeba\\S3\\Signature\\V2' => $vendorDir . '/akeeba/s3/src/Signature/V2.php', + 'Akeeba\\S3\\Signature\\V4' => $vendorDir . '/akeeba/s3/src/Signature/V4.php', + 'Akeeba\\S3\\StorageClass' => $vendorDir . '/akeeba/s3/src/StorageClass.php', ); diff --git a/s3_storage/vendor/composer/autoload_files.php b/s3_storage/vendor/composer/autoload_files.php new file mode 100644 index 00000000..cc01550f --- /dev/null +++ b/s3_storage/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/akeeba/s3/src/aliasing.php', +); diff --git a/s3_storage/vendor/composer/autoload_psr4.php b/s3_storage/vendor/composer/autoload_psr4.php index b1215e7f..400320bf 100644 --- a/s3_storage/vendor/composer/autoload_psr4.php +++ b/s3_storage/vendor/composer/autoload_psr4.php @@ -6,5 +6,5 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\' => array($vendorDir . '/akeeba/s3/src'), + 'Akeeba\\S3\\' => array($vendorDir . '/akeeba/s3/src'), ); diff --git a/s3_storage/vendor/composer/autoload_real.php b/s3_storage/vendor/composer/autoload_real.php index a5d4ee9a..9df2057b 100644 --- a/s3_storage/vendor/composer/autoload_real.php +++ b/s3_storage/vendor/composer/autoload_real.php @@ -50,6 +50,24 @@ class ComposerAutoloaderInitS3StorageAddon $loader->register(true); + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInitS3StorageAddon::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequireS3StorageAddon($fileIdentifier, $file); + } + return $loader; } } + +function composerRequireS3StorageAddon($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/s3_storage/vendor/composer/autoload_static.php b/s3_storage/vendor/composer/autoload_static.php index 09179f94..929f8194 100644 --- a/s3_storage/vendor/composer/autoload_static.php +++ b/s3_storage/vendor/composer/autoload_static.php @@ -6,48 +6,52 @@ namespace Composer\Autoload; class ComposerStaticInitS3StorageAddon { + public static $files = array ( + '714ccd4b330431237faf946f71c4c9a4' => __DIR__ . '/..' . '/akeeba/s3/src/aliasing.php', + ); + public static $prefixLengthsPsr4 = array ( 'A' => array ( - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\' => 38, + 'Akeeba\\S3\\' => 10, ), ); public static $prefixDirsPsr4 = array ( - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\' => + 'Akeeba\\S3\\' => array ( 0 => __DIR__ . '/..' . '/akeeba/s3/src', ), ); public static $classMap = array ( - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Acl' => __DIR__ . '/..' . '/akeeba/s3/src/Acl.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Configuration' => __DIR__ . '/..' . '/akeeba/s3/src/Configuration.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Connector' => __DIR__ . '/..' . '/akeeba/s3/src/Connector.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotDeleteFile' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotDeleteFile.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotGetBucket' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotGetBucket.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotGetFile' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotGetFile.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotListBuckets' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotListBuckets.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotOpenFileForRead' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotOpenFileForRead.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotOpenFileForWrite' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotOpenFileForWrite.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\CannotPutFile' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotPutFile.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\ConfigurationError' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/ConfigurationError.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidAccessKey' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidAccessKey.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidBody' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidBody.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidEndpoint' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidEndpoint.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidFilePointer' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidFilePointer.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidRegion' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidRegion.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidSecretKey' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidSecretKey.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\InvalidSignatureMethod' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidSignatureMethod.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Exception\\PropertyNotFound' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/PropertyNotFound.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Input' => __DIR__ . '/..' . '/akeeba/s3/src/Input.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Request' => __DIR__ . '/..' . '/akeeba/s3/src/Request.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Response' => __DIR__ . '/..' . '/akeeba/s3/src/Response.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Response\\Error' => __DIR__ . '/..' . '/akeeba/s3/src/Response/Error.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature' => __DIR__ . '/..' . '/akeeba/s3/src/Signature.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature\\V2' => __DIR__ . '/..' . '/akeeba/s3/src/Signature/V2.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\Signature\\V4' => __DIR__ . '/..' . '/akeeba/s3/src/Signature/V4.php', - 'Akeeba\\Engine\\Postproc\\Connector\\S3v4\\StorageClass' => __DIR__ . '/..' . '/akeeba/s3/src/StorageClass.php', + 'Akeeba\\S3\\Acl' => __DIR__ . '/..' . '/akeeba/s3/src/Acl.php', + 'Akeeba\\S3\\Configuration' => __DIR__ . '/..' . '/akeeba/s3/src/Configuration.php', + 'Akeeba\\S3\\Connector' => __DIR__ . '/..' . '/akeeba/s3/src/Connector.php', + 'Akeeba\\S3\\Exception\\CannotDeleteFile' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotDeleteFile.php', + 'Akeeba\\S3\\Exception\\CannotGetBucket' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotGetBucket.php', + 'Akeeba\\S3\\Exception\\CannotGetFile' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotGetFile.php', + 'Akeeba\\S3\\Exception\\CannotListBuckets' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotListBuckets.php', + 'Akeeba\\S3\\Exception\\CannotOpenFileForRead' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotOpenFileForRead.php', + 'Akeeba\\S3\\Exception\\CannotOpenFileForWrite' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotOpenFileForWrite.php', + 'Akeeba\\S3\\Exception\\CannotPutFile' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/CannotPutFile.php', + 'Akeeba\\S3\\Exception\\ConfigurationError' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/ConfigurationError.php', + 'Akeeba\\S3\\Exception\\InvalidAccessKey' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidAccessKey.php', + 'Akeeba\\S3\\Exception\\InvalidBody' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidBody.php', + 'Akeeba\\S3\\Exception\\InvalidEndpoint' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidEndpoint.php', + 'Akeeba\\S3\\Exception\\InvalidFilePointer' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidFilePointer.php', + 'Akeeba\\S3\\Exception\\InvalidRegion' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidRegion.php', + 'Akeeba\\S3\\Exception\\InvalidSecretKey' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidSecretKey.php', + 'Akeeba\\S3\\Exception\\InvalidSignatureMethod' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/InvalidSignatureMethod.php', + 'Akeeba\\S3\\Exception\\PropertyNotFound' => __DIR__ . '/..' . '/akeeba/s3/src/Exception/PropertyNotFound.php', + 'Akeeba\\S3\\Input' => __DIR__ . '/..' . '/akeeba/s3/src/Input.php', + 'Akeeba\\S3\\Request' => __DIR__ . '/..' . '/akeeba/s3/src/Request.php', + 'Akeeba\\S3\\Response' => __DIR__ . '/..' . '/akeeba/s3/src/Response.php', + 'Akeeba\\S3\\Response\\Error' => __DIR__ . '/..' . '/akeeba/s3/src/Response/Error.php', + 'Akeeba\\S3\\Signature' => __DIR__ . '/..' . '/akeeba/s3/src/Signature.php', + 'Akeeba\\S3\\Signature\\V2' => __DIR__ . '/..' . '/akeeba/s3/src/Signature/V2.php', + 'Akeeba\\S3\\Signature\\V4' => __DIR__ . '/..' . '/akeeba/s3/src/Signature/V4.php', + 'Akeeba\\S3\\StorageClass' => __DIR__ . '/..' . '/akeeba/s3/src/StorageClass.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/s3_storage/vendor/composer/installed.json b/s3_storage/vendor/composer/installed.json index f122e1ba..70c17e11 100644 --- a/s3_storage/vendor/composer/installed.json +++ b/s3_storage/vendor/composer/installed.json @@ -1,35 +1,38 @@ [ { "name": "akeeba/s3", - "version": "2.0.0", - "version_normalized": "2.0.0.0", + "version": "2.3.1", + "version_normalized": "2.3.1.0", "source": { "type": "git", "url": "https://github.com/akeeba/s3.git", - "reference": "01520dae1f736555e08efda0ddc1044701bd340a" + "reference": "7f5b3e929c93eb02ba24472560c0cbbef735aed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/akeeba/s3/zipball/01520dae1f736555e08efda0ddc1044701bd340a", - "reference": "01520dae1f736555e08efda0ddc1044701bd340a", + "url": "https://api.github.com/repos/akeeba/s3/zipball/7f5b3e929c93eb02ba24472560c0cbbef735aed9", + "reference": "7f5b3e929c93eb02ba24472560c0cbbef735aed9", "shasum": "" }, "require": { "ext-curl": "*", "ext-simplexml": "*", - "php": ">=7.1.0 <8.1" + "php": ">=7.1.0 <8.4" }, - "time": "2020-11-30T14:03:55+00:00", + "time": "2023-09-26T11:40:10+00:00", "type": "library", "installation-source": "dist", "autoload": { + "files": [ + "src/aliasing.php" + ], "psr-4": { - "Akeeba\\Engine\\Postproc\\Connector\\S3v4\\": "src" + "Akeeba\\S3\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "GPL-3.0+" + "GPL-3.0-or-later" ], "authors": [ {