Orderadmin/css/boldgrid-backup-premium-admin-historical.css 0000644 00000000603 14760036270 0017074 0 ustar 00 .dashicons.dashicons-archive { color: #c4963d; } .dashicons.dashicons-media-default { color: #0073aa; } .dashicons.dashicons-admin-page { color: #0073aa; } /* Fix the padding for the "#" heading. */ table.widefat thead td.check-column { padding: 8px 10px; } /* Add a border between each of the version #'s found. */ tr.top:not(:first-of-type) td { border-top: 1px solid #ddd; } admin/js/boldgrid-backup-premium-admin-zip-browser.js 0000644 00000002471 14760036270 0016673 0 ustar 00 /** * Browser. * * @summary JS for all admin backup pages. * * @since 1.5.3 */ /* global ajaxurl,jQuery,boldgrid_backup_premium_zip_browser */ var BoldGrid = BoldGrid || {}; BoldGrid.PremiumZipBrowser = function( $ ) { var self = this; self.onClickRestore = function() { var $a = $( this ), $tr = $a.closest( 'tr' ), $fileTr = $tr.prev(), data = { action: 'boldgrid_backup_restore_single_file', filename: $( '#filename' ).val(), // Total Upkeep provides a general nonce for usage on the Archive Details page. security: $( '#bgbkup_archive_details_nonce' ).val(), file: $fileTr.attr( 'data-dir' ) }, $restoring = $( ' ' + boldgrid_backup_premium_zip_browser.restoring + '...' ); $a.after( $restoring ).remove(); $.post( ajaxurl, data, function( response ) { if ( response.data !== undefined ) { $restoring.html( response.data ); } else { $restoring.html( boldgrid_backup_premium_zip_browser.unknownError ); } } ).error( function() { $restoring.html( boldgrid_backup_premium_zip_browser.unknownError ); } ); }; /** * Init. */ $( function() { $( 'body' ).on( 'click', '.file-actions a.restore', self.onClickRestore ); } ); }; BoldGrid.PremiumZipBrowser( jQuery ); admin/js/boldgrid-backup-premium-admin-timely-updates.js 0000644 00000003175 14760036270 0017360 0 ustar 00 /** * File: boldgrid-backup-premium-admin-timely-updates.js * * @summary JS for the admin themes page. * * @since 1.4.0 */ /* global ajaxurl,jQuery,settingsData*/ var BoldGrid = BoldGrid || {}; /** * Class: TimelyUpdates. * * @since 1.4.0 */ class TimelyUpdates { /** * Constructor * * @since 1.4.0 */ constructor() { $ = jQuery; this.themes = window.BgbckTheme || {}; $(document).on('ready', this._onReady()); } /** * _onReady * * @since 1.4.0 */ _onReady() { var themeSlugs = []; for (var themeSlug in this.themes) { themeSlugs.push(themeSlug); } themeSlugs.forEach(function(themeSlug, index) { var updateMessage = this.themes[themeSlug].message; var contents = $(updateMessage); this.prependDiv(themeSlug, contents); }, this); } /** * Prepend Div with provided contents. * * @since 1.4.0 * * @param {string} themeSlug Theme Slug. * @param {string} contents Contents of update message. */ async prependDiv(themeSlug, contents) { var totalCheckTime = 0, checkExist = setInterval(function() { /* * Every 250ms we will check for the themes to have been rendered. For performance * reasons, we will stop checking after 5 seconds. */ totalCheckTime += 250; if (totalCheckTime > 5000) { clearInterval(checkExist); } // When the themes have been rendered, add our upgrade notice. if ($('.theme-browser.rendered').length) { $(".theme[aria-describedby*='" + themeSlug + "'] .update-message p").after(contents); clearInterval(checkExist); } }, 250); // check every 250ms } } BoldGrid.TimelyUpdates = new TimelyUpdates(); admin/js/boldgrid-backup-premium-admin-settings.js 0000644 00000006757 14760036270 0016263 0 ustar 00 /** * File: boldgrid-backup-premium-admin-settings.js * * @summary JS for the admin settings page. * * @since 1.3.0 */ /* global ajaxurl,jQuery,settingsData */ var BoldGrid = BoldGrid || {}; BoldGrid.BGBPSettings = function( $ ) { var self = this; /** * Add settings markup to the Backup Security section. * * @since 1.3.0 */ self.addSettingsMarkup = function() { var markup = '
' + settingsData.lang.addTokenText + '
' + boldgrid_backup_premium_admin_plugin_editor.error_saving + '
' + closeButton + '' + boldgrid_backup_premium_admin_plugin_editor.success_saving + '
' + closeButton + '' . __( 'There are no files last modified within the given time frame. Please try again.', 'boldgrid-backup' ) . '
'; } printf( '%2$s
', __( 'Recently Modified Files', 'boldgrid-backup' ), // phpcs:ignore __( 'Use this tool to find a list of all files modified recently. Enter a number of minutes below and we will find all files modified within that time frame.', 'boldgrid-backup' ) // phpcs:ignore ); printf( ' ', /* 1 */ __( 'Search', 'boldgrid-backup' ), /* 2 */ ! empty( $minutes ) ? esc_attr( $minutes ) : '', /* 3 */ '60', /* 4 */ __( 'Minutes', 'boldgrid-backup' ) // phpcs:ignore ); echo $table; // phpcs:ignore $output = ob_get_contents(); ob_end_clean(); return $output; admin/partials/history.php 0000644 00000003536 14760036270 0011701 0 ustar 00 */ // phpcs:disable Squiz.PHP.NonExecutableCode if ( ! defined( 'WPINC' ) ) { die; } ob_start(); printf( '' . sprintf( // translators: 1: Plugin title encapsulated with HTML strong tags. esc_html__( '%1$s keeps a running history of changes to your site (such as plugin updates, backups created, etc). This page shows your history log.', 'boldgrid-backup' ), '' . esc_html( BOLDGRID_BACKUP_PREMIUM_TITLE ) . '' ) . '
'; ?>Date | User | Action |
---|---|---|
%1$s %2$s ago |
%3$s | %4$s |
' . sprintf( // translators: 1: HTML opening strong tag, 2: HTML closing strong tag, 3: Plugin title. __( 'The %1$s%3$s%2$s plugin offers two additional tools below, %1$sSave a copy before updating%2$s and %1$sFind a version to restore%2$s. If you want to make a backup of this file before saving any changes, click the %1$sSave a copy%2$s button. If you want to find or restore any copies previously saved or included in a backup, click %1$sFind a version%2$s.', 'boldgrid-backup' ), '', '', BOLDGRID_BACKUP_PREMIUM_TITLE ) . '
'; admin/remote/s3_uploader.php 0000644 00000007466 14760036270 0012102 0 ustar 00 */ use Aws\S3\Model\MultipartUpload\UploadBuilder; /** * S3 Uploader class. * * @since 1.2.0 */ class Boldgrid_Backup_Premium_Admin_Remote_S3_Uploader { /** * An array of error messages. * * @since 1.2.0 * @var array $errors * @access private */ private $errors = array(); /** * Get errors. * * @since 1.2.0 * * @return array */ public function get_errors() { return $this->errors; } /** * Whether or not we have errors. * * @since 1.2.0 * * @return bool */ public function has_error() { return ! empty( $this->errors ); } /** * Upload a backup file. * * @since 1.2.0 * * @param Boldgrid_Backup_Premium_Admin_Remote_S3_Bucket $bucket Our bucket. * @param string $filepath File path. * @return bool */ public function upload( Boldgrid_Backup_Premium_Admin_Remote_S3_Bucket $bucket, $filepath ) { $success = false; $core = apply_filters( 'boldgrid_backup_get_core', null ); $client = $bucket->get_client(); if ( ! $core->wp_filesystem->exists( $filepath ) ) { // Translators: 1: File path. $this->errors[] = sprintf( __( 'Failed to upload, filepath does not exist: %1$s', 'boldgrid-backup' ), $filepath ); return $success; } /* * When files are uploaded to an S3 host S3, the LastModified is the time the file was uploaded, * not the time the file was last modified. When we enforce retention, we'll need to know when * the backup archive was created, not when it was uploaded. */ $archive_data = $core->archive_log->get_by_zip( $filepath ); $last_modified = ! empty( $archive_data['lastmodunix'] ) ? $archive_data['lastmodunix'] : $core->wp_filesystem->mtime( $filepath ); try { $uploader = new \Aws\S3\MultipartUploader( $client->get_client(), fopen( $filepath, 'rb' ), // phpcs:ignore WordPress.WP.AlternativeFunctions array( 'bucket' => $bucket->get_id(), 'key' => basename( $filepath ), /* * Before Initiate. * * Set our custom metadata. * * Originally, our Amazon S3 class used is_boldgrid_backup and last_modified. These * don't work with DreamObjects. Appears only lowercase letters are allowed. * * @link https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#createmultipartupload * @link https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-multipart-upload.html * * @var $command Aws\Command A CreateMultipartUpload operation. */ 'before_initiate' => function( $command ) use ( $last_modified ) { $command['Metadata'] = array( 'isboldgridbackup' => 'true', 'lastmodified' => $last_modified, ); }, ) ); } catch ( Exception $e ) { $this->errors[] = sprintf( // Translators: 1, the status code (such as 403), 2 the error code (such as "SignatureDoesNotMatch"). __( '%1$s error: %2$s.', 'boldgrid-backup' ), $e->getStatusCode(), $e->getAwsErrorCode() ); return $success; } try { $uploader->upload(); // We've uploaded a new file. Delete this bucket's objects transient. $bucket->get_client()->get_provider()->get_transient()->delete_objects( $bucket->get_id() ); // Upload was a success if our bucket has our backup file. $bucket->set_objects( true ); $success = $bucket->has_object_key( basename( $filepath ) ); } catch ( MultipartUploadException $e ) { $uploader->abort(); $this->errors[] = __( 'Failed to upload.', 'boldgrid-inspirations' ); return $success; } return $success; } } admin/remote/s3_provider.php 0000644 00000011466 14760036270 0012114 0 ustar 00 */ /** * Generic S3 class. * * @since 1.2.0 */ class Boldgrid_Backup_Premium_Admin_Remote_S3_Provider extends Boldgrid_Backup_Premium_Admin_Remote_Provider { /** * An instance of Boldgrid_Backup_Premium_Admin_Remote_S3_Client. * * @since 1.2.0 * @var Boldgrid_Backup_Premium_Admin_Remote_S3_Client * @access private */ private $client; /** * Our transient class. * * @since 1.2.0 * @var Boldgrid_Backup_Premium_Admin_Remote_S3_Transient * @access private */ private $transient; /** * Our uploader. * * @since 1.2.0 * @var Boldgrid_Backup_Premium_Admin_Remote_S3_Uploader */ private $uploader; /** * An instance of Boldgrid_Backup_Premium_Admin_Remote_S3_Bucket * * @since 1.2.0 * @var Boldgrid_Backup_Premium_Admin_Remote_S3_Client * @access protected */ protected $bucket; /** * Constructor. * * @since 1.2.0 */ public function __construct() { // phpcs:ignore parent::__construct(); } /** * Enforce retention. * * @since 1.2.0 */ public function enforce_retention() { $retention_count = $this->get_setting( 'retention_count' ); $bucket = $this->get_bucket(); $bucket->enforce_retention( $retention_count, $this->title ); } /** * Get our bucket. * * @since 1.2.0 * * @return Boldgrid_Backup_Premium_Admin_Remote_S3_Bucket */ public function get_bucket() { if ( is_null( $this->bucket ) ) { $client = $this->get_client(); $bucket_id = $this->get_setting( 'bucket_id' ); if ( ! empty( $client ) && ! empty( $bucket_id ) ) { $this->bucket = new Boldgrid_Backup_Premium_Admin_Remote_S3_Bucket( $client, $bucket_id ); } } return $this->bucket; } /** * Get our client. * * @since 1.2.0 * * @return Boldgrid_Backup_Premium_Admin_Remote_S3_Client */ public function get_client() { if ( is_null( $this->client ) ) { $settings = $this->remote_settings->get_settings(); // Only try to initialize the client if we have the needed settings. if ( $this->remote_settings->has_setting_keys( array( 'key', 'secret', 'host' ) ) ) { $this->client = new Boldgrid_Backup_Premium_Admin_Remote_S3_Client( array( 'key' => $settings['key'], 'secret' => $settings['secret'], 'endpoint' => $settings['host'], ) ); $this->client->set_provider( $this ); } } return $this->client; } /** * Get details * * @since 1.2.0 * * @param bool $try_cache Whether or not to use last_login to validate the Dreamobjects * account. Please see param definition in $this->is_setup(). * @return array */ public function get_details( $try_cache = false ) { $client = $this->get_client(); $is_setup = ! empty( $client ) && $client->is_valid(); $enabled = $this->get_setting( 'enabled' ); $details = array( 'title' => $this->title, 'key' => $this->key, 'configure' => 'admin.php?page=boldgrid-backup-' . $this->key, 'is_setup' => $is_setup, 'enabled' => $enabled && $is_setup, ); return $details; } /** * Get our transient class. * * @since 1.2.0 * * @return Boldgrid_Backup_Premium_Admin_Remote_S3_Transient */ public function get_transient() { if ( is_null( $this->transient ) ) { $this->transient = new Boldgrid_Backup_Premium_Admin_Remote_S3_Transient( $this ); } return $this->transient; } /** * Get our uploader. * * @since 1.2.0 * * @return Boldgrid_Backup_Premium_Admin_Remote_S3_Uploader */ public function get_uploader() { return $this->uploader; } /** * Determine whether or not this provider has a bucket. * * @since 1.2.1 * * @return bool */ public function has_bucket() { $bucket = $this->get_bucket(); return ! empty( $bucket ); } /** * Determine whether or not this provider has a client. * * @since 1.2.1 * * @return bool */ public function has_client() { $client = $this->get_client(); return ! empty( $client ); } /** * Upload a file. * * @since 1.2.0 * * @param string $filepath Path to file to upload. * @return bool True on success. */ public function upload( $filepath ) { $bucket = $this->get_bucket(); $this->uploader = new Boldgrid_Backup_Premium_Admin_Remote_S3_Uploader(); $success = $this->uploader->upload( $bucket, $filepath ); if ( $success ) { $this->enforce_retention(); /** * File uploaded to remote storage location. * * @since 1.2.0 * * @param string DreamObjects * @param string $filepath */ do_action( 'boldgrid_backup_remote_uploaded', $this->title, $filepath ); } return $success; } } admin/remote/s3_page.php 0000644 00000015266 14760036270 0011200 0 ustar 00 */ /** * S3_Page class. * * @since 1.2.0 */ class Boldgrid_Backup_Premium_Admin_Remote_S3_Page { /** * Errors. * * @since 1.2.0 * @var array * @access private */ private $errors = array(); /** * The provider this page is for. * * @since 1.2.0 * @var Boldgrid_Backup_Premium_Admin_Remote_S3_Provider * @access private */ private $provider; /** * Constructor. * * @since 1.2.0 * * @param Boldgrid_Backup_Premium_Admin_Remote_S3_Provider $provider */ public function __construct( Boldgrid_Backup_Premium_Admin_Remote_S3_Provider $provider ) { $this->provider = $provider; } /** * Add menu items. * * @since 1.2.0 */ public function add_submenu_page() { $capability = 'administrator'; $title = sprintf( // Translators: 1 the name of the s3 provider, such as DreamObjects. __( '%1$s Settings', 'boldgrid-backup' ), $this->provider->get_title() ); add_submenu_page( 'boldgrid-backup-settings', $title, $title, $capability, 'boldgrid-backup-' . $this->provider->get_key(), array( $this, 'submenu_page', ) ); } /** * Ensure the host has a protocol. * * @since 1.2.0 * * @return string */ public function sanitize_host( $host ) { if ( false === strpos( $host, '://' ) ) { $host = 'https://' . $host; } return stripslashes( $host ); } /** * Whether or not the page was submitted with a valid nonce. * * @since 1.2.0 * * @return bool */ public function is_valid_nonce() { $nonce = ! empty( $_POST['_wpnonce'] ) ? $_POST['_wpnonce'] : null; // phpcs:ignore return wp_verify_nonce( $nonce, 'save-provider-settings_' . $this->provider->get_key() ); } /** * Generate the submenu page for our Provider's Settings page. * * @since 1.2.0 */ public function submenu_page() { wp_enqueue_style( 'boldgrid-backup-admin-hide-all' ); $this->submenu_page_save(); if ( ! empty( $this->errors ) ) { do_action( 'boldgrid_backup_notice', implode( '' . wp_kses( sprintf( // Translators: 1 an opening strong tag, 2 its closing strong tag, 3 the filename of the backup just downloaded, 4 the name of our provider (such as DreamObjects). __( 'Backup file %1$s%3$s%2$s successfully downloaded from %4$s.', 'boldgrid-backup' ), '', '', $filename, $this->provider->get_title() ), array( 'strong' => array() ) ) . '
'; $core->notice->add_user_notice( $notice, 'notice notice-success' ); wp_send_json_success(); } else { $bucket_errors = $bucket->has_errors() ? implode( 'Backup file %1$s successfully downloaded from Google Drive.
', 'boldgrid-backup' ), $filename, BOLDGRID_BACKUP_PREMIUM_TITLE ), $allowed_html ), 'notice notice-success' ); wp_send_json_success(); } else { $this->core->notice->add_user_notice( sprintf( $error, $archive->last_error ), 'notice notice-error' ); wp_send_json_error(); } } } admin/remote/google_drive_folder.php 0000644 00000034721 14760036270 0013654 0 ustar 00 */ /** * Class: Boldgrid_Backup_Premium_Admin_Remote_Google_Drive_Folder * * @since 1.1.0 */ class Boldgrid_Backup_Premium_Admin_Remote_Google_Drive_Folder { /** * Our last error message. * * @since 1.1.0 * @var string */ public $last_error; /** * The core class object. * * @since 1.1.0 * @access private * @var Boldgrid_Backup_Admin_Core */ private $core; /** * Our parent folder name. * * All backups will be stored off the root in a folder named after the parent plugin, such as: * /BOLDGRID_BACKUP_TITLE/DomainA.com/backup1.zip * /BOLDGRID_BACKUP_TITLE/DomainB.com/backup1.zip * * @since 1.1.0 * @access private * @var string */ private $parent_folder_name = 'BoldGrid Backup'; /** * An instance of Boldgrid_Backup_Premium_Admin_Core. * * @since 1.1.0 * @access private * @var Boldgrid_Backup_Premium_Admin_Core */ private $premium_core; /** * Constructor. * * @since 1.1.0 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. * @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object. */ public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core ) { $this->core = $core; $this->premium_core = $premium_core; } /** * Create a folder. * * @since 1.1.0 * * @link https://github.com/google/google-api-php-client/issues/860 * * @param string $name The name of the folder to create. * @param string $parent_id Optional, a parent folder id. * @return mixed Google_Service_Drive_DriveFile on Success, false on failure. */ public function create( $name, $parent_id = '' ) { $service = $this->premium_core->google_drive->client->get_service(); $args = array( 'name' => $name, 'mimeType' => 'application/vnd.google-apps.folder', ); // By default, folders will be created in the root directory, unless otherwise stated. if ( ! empty( $parent_id ) ) { $args['parents'] = array( $parent_id ); } $folder = new Google_Service_Drive_DriveFile( $args ); // Catch any possible exceptions thrown by Google Drive classes. try { $req = $service->files->create( $folder, array( 'fields' => 'id', ) ); return $req; } catch ( Google_Exception $e ) { $this->last_error = sprintf( // translators: 1 The name of a folder we're trying to create, 2 that folder's parent id, 3 the error code, 4 the error message. __( 'Unable to create folder "%1$s" (with a parent id of "%2$s"). Error %3$s: %4$s.', 'boldgrid-backup' ), $name, $parent_id, $e->getCode(), $this->get_exception_message( $e ) ); return false; } } /** * Create our parent folder. * * @see self::parent_folder_name * * @return mixed Google_Service_Drive_DriveFile on Success, false on failure. */ private function create_parent() { return $this->create( $this->parent_folder_name ); } /** * Enforce retention. * * @since 1.1.0 * * @param string $folder_id The id of the folder to enforce retention on. */ public function enforce_retention( $folder_id = '' ) { // If we have an invalid retention count, abort. $retention_count = $this->premium_core->google_drive->settings->get_setting( 'retention_count', $this->premium_core->google_drive->page->get_default_retention_count() ); if ( empty( $retention_count ) ) { return; } $files = $this->get_files_asc( $folder_id ); if ( count( $files ) <= $retention_count ) { return; } $service = $this->premium_core->google_drive->client->get_service(); $count_to_delete = count( $files ) - $retention_count; foreach ( $files as $file ) { if ( 0 === $count_to_delete ) { break; } try { $service->files->delete( $file['id'], array( 'supportsAllDrives' => true, 'supportsTeamDrives' => true, ) ); } catch ( Exception $e ) { $this->last_error = __( 'An error occurred deleting a backup during retention:', 'boldgrid-backup' ) . ' ' . $e->getMessage(); } $count_to_delete--; } } /** * Get the id of our folder. * * @since 1.1.0 * * @param string $name The name of the folder to create. * @param string $parent_id Optional, a parent folder id. * @return string Our folder id, or false on failure. */ public function get_id( $name, $parent_id = '' ) { if ( empty( $name ) ) { return false; } /* * Try to get our folder. * * If we get false, self::last_error has been set and we can abort. */ $folder = $this->get_folder( $name, $parent_id ); if ( false === $folder ) { return false; } if ( empty( $folder ) ) { $folder = $this->create( $name, $parent_id ); } return empty( $folder->id ) ? false : $folder->id; } /** * Get a folder based on key / value search. * * Typical method is to get folder by name. * * @since 1.1.0 * * @link https://developers.google.com/drive/api/v3/search-parameters * * @param string $name The name of the folder to create. * @param string $parent_id Optional, a parent folder id. * @return mixed Google_Service_Drive_DriveFile Object on success, false on failure. */ public function get_folder( $name, $parent_id = '' ) { $service = $this->premium_core->google_drive->client->get_service(); $this->premium_core->google_drive->client->set_defer( false ); $query = array( 'name = "' . $name . '"', 'mimeType = "application/vnd.google-apps.folder"', 'trashed = false', ); // By default, we do not specify a parent folder id. if ( ! empty( $parent_id ) ) { $query[] = '"' . $parent_id . '" in parents'; } try { /* * Build our query to search for a folder. * * It is assumed that we will be using this method to search by folder name. If we later * accept other parameters besides $key = 'name', we will need to build the additional * logic below. * * Example $files: https://pastebin.com/54rRb6ue. */ $files = $service->files->listFiles( array( 'q' => implode( ' and ', $query ), 'pageSize' => 1, ) ); return empty( $files->files ) ? array() : $files->files[0]; } catch ( Google_Exception $e ) { $this->last_error = sprintf( // translators: 1 The name of a folder we're trying to get, 2 that folder's parent id, 3 the error code, 4 the error message. __( 'Unable to get backup folder "%1$s" (with a parent id of "%2$s"). Error %3$s: %4$s.', 'boldgrid-backup' ), $name, $parent_id, $e->getCode(), $this->get_exception_message( $e ) ); return false; } } /** * Get the id of our backup folder. * * Do not confuse this with self::get_parent_id(). * * /parent folder/backup folder/backup.zip * * @since 1.1.0 * * @see self::get_parent_id() * @see self::parent_folder_name * * @return string Our folder id, or false on failure. */ public function get_backup_id() { $backup_folder_id = false; /* * Prior to 1.4.0, backups on Google Drive were stored in a parent / child folder, such as * PARENT/BACKUP/backup.zip. The user was only able to configure the "BACKUP" folder. * * As of 1.4.0, the user can now specify a Google Drive ID as the folder. So, if the user wanted * to upload a file here: https://drive.google.com/drive/u/0/folders/abc123 - the user only * needs to specify "abc123" * * Our Google Drive settings now have two settings: "folder_type" and "folder_name". Examples: * * Original method, specifying a folder name to upload backups to: * # folder_type: name * # folder_name: domain.com * * New method, specifying a folder id to upload backups to: * # folder_type: id * # folder_name: abc123 */ switch ( $this->premium_core->google_drive->settings->get_setting( 'folder_type', 'name' ) ) { case 'name': $folder_name = $this->get_folder_name(); if ( empty( $folder_name ) ) { $this->last_error = __( 'Cannot get id of backup folder: Empty folder name. Please try to resave your Google Drive settings.', 'boldgrid-backup' ); } else { $backup_folder_id = $this->get_id( $folder_name, $this->get_parent_id() ); } break; case 'id': $backup_folder_id = $this->premium_core->google_drive->settings->get_setting( 'folder_name' ); break; } return $backup_folder_id; } /** * Get our error message from an exception. * * Sometimes we're dealing with a string, and others an array. * * @since 1.3.2 * * @param Google_Exception $e * @return string */ private function get_exception_message( $e ) { $message = $e->getMessage(); $unknown = __( 'Unknown error', 'boldgrid-backup' ); $is_json = '{' === substr( trim( $message ), 0, 1 ); if ( $is_json ) { $message = json_decode( $message ); $message = ! empty( $message->error->errors[0]->message ) ? $message->error->errors[0]->message : $unknown; } else { $message = is_string( $message ) && ! empty( $message ) ? $message : $unknown; } return $message; } /** * Get a specific file. * * @since 1.1.0 * * @param string $filename The name of our file. * @param string $folder_id The id of the file's folder. * @return array The first file found. */ public function get_file( $filename, $folder_id = '' ) { $service = $this->premium_core->google_drive->client->get_service(); $this->premium_core->google_drive->client->set_defer( false ); if ( empty( $folder_id ) ) { $folder_id = $this->get_backup_id(); if ( false === $folder_id ) { $this->last_error = $this->last_error; return false; } } try { $query = array( '"' . $folder_id . '" in parents', 'name = "' . $filename . '"', 'mimeType != "application/vnd.google-apps.folder"', 'trashed = false', ); $files = $service->files->listFiles( array( 'q' => implode( ' AND ', $query ), 'pageSize' => 1, 'fields' => 'files(id,size)', // Shared drive support. 'includeItemsFromAllDrives' => true, 'supportsAllDrives' => true, ) ); // Return the first file in the set of results. return empty( $files->files ) ? array() : $files->files[0]; } catch ( Google_Exception $e ) { // Translators: 1: Error message. $this->last_error = sprintf( __( 'Unable to determine if archive exists in Google Drive. %1$s.', 'boldgrid-backup' ), $e->getMessage() ); return false; } } /** * Get a list of files in our backup folder. * * @since 1.1.0 * * @param string $folder_id The folder id to get the contents of. * @return mixed False on failure, Google_Service_Drive_FileList on success. * Example: https://pastebin.com/Ui0BSrwz */ public function get_files( $folder_id = '' ) { $folder_id = empty( $folder_id ) ? $this->get_backup_id() : $folder_id; if ( empty( $folder_id ) ) { $this->last_error = __( 'Unable to get Google Drive folder id.', 'boldgrid-backup' ); return false; } $service = $this->premium_core->google_drive->client->get_service(); $this->premium_core->google_drive->client->set_defer( false ); $q = array( '"' . $folder_id . '" in parents', 'mimeType != "application/vnd.google-apps.folder"', 'trashed = false', ); try { $files = $service->files->listFiles( array( 'q' => implode( ' and ', $q ), 'pageSize' => 100, 'fields' => 'files(id,size,name,createdTime,properties)', // Shared drive support. 'includeItemsFromAllDrives' => true, 'supportsAllDrives' => true, ) ); $site_backup_id = get_site_option( 'boldgrid_backup_id' ); $file_count = count( $files->files ); $filtered_files = array(); for ( $i = 0; $i < $file_count; $i++ ) { if ( false !== strpos( $files[ $i ]['name'], $site_backup_id ) ) { $filtered_files[] = $files[ $i ]; } } $files->files = $filtered_files; return $files; } catch ( Google_Exception $e ) { $message = __( 'Unable to retrieve file listing.', 'boldgrid-backup' ); if ( 401 === $e->getCode() ) { $message .= ' ' . __( 'Invalid Credentials.', 'boldgrid-backup' ); } $this->last_error = $message; $this->premium_core->google_drive->logs->get_connect_log()->add( $message ); return false; } } /** * Get a list of files in asc order (based on date created). * * Used in the retention process. * * @param string $folder_id The folder id to get the contents of. * @return array */ public function get_files_asc( $folder_id = '' ) { $folder_id = empty( $folder_id ) ? $this->get_backup_id() : $folder_id; $files = $this->get_files( $folder_id ); $files = $files instanceof Google_Service_Drive_FileList && ! empty( $files['files'] ) ? $files['files'] : array(); // Sort by created time. usort( $files, function( $a, $b ) { $a_time = ! empty( $a['properties']['createdTime'] ) ? $a['properties']['createdTime'] : strtotime( $a['createdTime'] ); // phpcs:ignore $b_time = ! empty( $b['properties']['createdTime'] ) ? $b['properties']['createdTime'] : strtotime( $b['createdTime'] ); // phpcs:ignore // Sorts low (oldest) to high (newest). return $a_time > $b_time ? 1 : -1; } ); return $files; } /** * Get the name of our folder on Google Drive where we are storing backups. * * @since 1.1.0 * * @return string */ public function get_folder_name() { return $this->premium_core->google_drive->settings->get_setting( 'folder_name', $this->premium_core->google_drive->page->get_default_folder_name() ); } /** * Get our parent folder. * * @since 1.1.0 * * @see self::parent_folder_name * * @return mixed Google_Service_Drive_DriveFile Object on success, false on failure. */ private function get_parent_folder() { return $this->get_folder( $this->parent_folder_name ); } /** * Get our parent folder id. * * Do not confuse this with self::get_backup_id(). * * /parent folder/backup folder/backup.zip * * @since 1.1.0 * * @see self::get_backup_id() * @see self::parent_folder_name * * @return string Our folder id, or false on failure. */ public function get_parent_id() { return $this->get_id( $this->parent_folder_name ); } } admin/remote/google_drive_client.php 0000644 00000024013 14760036270 0013650 0 ustar 00 */ /** * Google Drive Client class. * * @since 1.1.0 */ class Boldgrid_Backup_Premium_Admin_Remote_Google_Drive_Client { /** * Our Google Drive client. * * @since 1.1.0 * @var Google_Client */ public $client; /** * The last error message, if any, received. * * @since 1.1.0 * @var string */ public $last_error; /** * Access token key. * * @since 1.1.0 * @access private * @var string */ private $access_token_key = 'access_token'; /** * Code key. * * @since 1.1.0 * @access private * @var string */ private $code_key = 'code'; /** * The core class object. * * @since 1.1.0 * @access private * @var Boldgrid_Backup_Admin_Core */ private $core; /** * An instance of Boldgrid_Backup_Premium_Admin_Core. * * @since 1.1.0 * @access private * @var Boldgrid_Backup_Premium_Admin_Core */ private $premium_core; /** * An instance of Google_Service_Drive. * * @since 1.1.0 * @access private * @var Google_Service_Drive */ private $service; /** * Constructor. * * @since 1.1.0 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. * @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object. */ public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core ) { $this->core = $core; $this->premium_core = $premium_core; } /** * Init our client. * * @since 1.1.0 * * @link https://github.com/googleapis/google-api-php-client/blob/master/examples/large-file-upload.php * * @return mixed Google_Client on success, false on failure. */ public function init() { if ( ! empty( $this->client ) ) { return $this->client; } $configs = $this->premium_core->get_configs(); $this->client = new Google_Client(); $this->client->setAuthConfig( $configs['google_drive_config'] ); /* * Refreshing an access token (offline access) * * @link https://developers.google.com/identity/protocols/OAuth2WebServer#offline * * Access tokens periodically expire. You can refresh an access token without prompting the * user for permission (including when the user is not present) if you requested offline * access to the scopes associated with the token. */ $this->client->setAccessType( 'offline' ); $this->client->setIncludeGrantedScopes( true ); // View and manage Google Drive files and folders that you have opened or created with this app. $this->client->addScope( Google_Service_Drive::DRIVE_FILE ); /* * Required to get a refresh token. * * @link https://stackoverflow.com/questions/8942340/get-refresh-token-google-api */ $this->client->setApprovalPrompt( 'force' ); if ( empty( $_GET['code'] ) ) { // phpcs:ignore $code = $this->get_code(); $access_token = $this->get_access_token(); } else { $code = $_GET['code']; // phpcs:ignore $this->update_code( $code ); // Exchange an authorization code for an access token. $access_token = $this->client->authenticate( $code ); $this->update_access_token( $access_token ); if ( ! empty( $access_token['error'] ) ) { $message = __( 'Unable to authorize Google Drive:', 'boldgrid-backup' ) . ' ' . esc_html( $access_token['error'] ) . ' - ' . esc_html( $access_token['error_description'] ); $this->core->notice->add_user_notice( $message, $this->core->notice->lang['dis_error'] ); } else { $message = __( 'Google Drive successfully authorized!', 'boldgrid-backup' ); $this->core->notice->add_user_notice( $message, $this->core->notice->lang['dis_success'] ); } $this->premium_core->google_drive->logs->get_connect_log()->add( $message ); wp_safe_redirect( admin_url( 'admin.php?page=boldgrid-backup-settings§ion=section_storage' ) ); // phpcs:ignore WordPress.VIP exit; } // Catch any possible exceptions thrown by the Google Drive classes. try { $this->client->setAccessToken( $access_token ); $this->maybe_refresh_token(); if ( $this->client->isAccessTokenExpired() ) { $this->last_error = __( 'Unable to connect to Google Drive. Access token expired.', 'boldgrid-backup' ); $this->premium_core->google_drive->logs->get_connect_log()->add( $this->last_error ); return false; } } catch ( InvalidArgumentException $e ) { // Translators: 1: Error message. $this->last_error = sprintf( __( 'Unable to connect to Google Drive. %1$s.', 'boldgrid-backup' ), $e->getMessage() ); $this->premium_core->google_drive->logs->get_connect_log()->add( $this->last_error ); return false; } return $this->client; } /** * Maybe refresh our access token. * * Since 1.1.0 * * @return bool Whether or not the access token was updated. */ public function maybe_refresh_token() { $refreshed = false; if ( $this->client->isAccessTokenExpired() ) { $this->premium_core->google_drive->logs->get_connect_log()->add( __METHOD__ . ' Access token is expired.' ); $refresh_token = $this->get_refresh_token(); if ( ! empty( $refresh_token ) ) { $this->premium_core->google_drive->logs->get_connect_log()->add( __METHOD__ . ' Fetching access token with refresh token...' ); $access_token = $this->client->fetchAccessTokenWithRefreshToken( $refresh_token ); $refreshed = $this->update_access_token( $access_token ); } else { $this->premium_core->google_drive->logs->get_connect_log()->add( __METHOD__ . ' Unable to fetch access token. Missing refresh token.' ); } } return $refreshed; } /** * Set our client's defer status. * * Declare whether making API calls should make the call immediately, or return a request which * can be called with ->execute(); * * This is a wrapper method, made primarily to give the developer the following comments: * * On a case by case basis, you may need to set the defer to false so that calls are executed * right away. This was discovered because calls to "get files" were returning different results * when called in the same manner. For example, sometimes we would get a * Google_Service_Drive_FileList object in return, and other times we would get a * GuzzleHttp\Psr7\Request object instead. Most likely this was caused by the calls being made * immediately before the calls to "get files", which probably changed the defer type. So, if * you experience an issue as described above, try setting the defer type to false. * * @since 1.1.0 * * @link https://github.com/googleapis/google-api-php-client/blob/v2.2.2/src/Google/Service/Resource.php#L222-L230 * @link https://github.com/googleapis/google-api-php-client/blob/v2.2.2/src/Google/Client.php#L906-L915 * * @param bool $defer True if calls should not be executed right away. */ public function set_defer( $defer ) { $this->client->setDefer( $defer ); } /** * Get our access token. * * @since 1.1.0 * * @return array Example: https://pastebin.com/ur1Jh9YM */ public function get_access_token() { $access_token = $this->premium_core->google_drive->settings->get_setting( $this->access_token_key, array() ); if ( empty( $access_token ) ) { $this->premium_core->google_drive->logs->get_connect_log()->add( __METHOD__ . ' No access token.' ); } return $access_token; } /** * Get our authentication code. * * @since 1.1.0 * * @return string */ public function get_code() { return $this->premium_core->google_drive->settings->get_setting( $this->code_key ); } /** * Get our refresh token. * * Access tokens have limited lifetimes. If your application needs access to a Google API beyond * the lifetime of a single access token, it can obtain a refresh token. A refresh token allows * your application to obtain new access tokens. Save refresh tokens in secure long-term storage * and continue to use them as long as they remain valid. Limits apply to the number of refresh * tokens that are issued per client-user combination, and per user across all clients, and these * limits are different. If your application requests enough refresh tokens to go over one of the * limits, older refresh tokens stop working. * * @since 1.1.0 * * @return string */ public function get_refresh_token() { $refresh_token = ''; $option_value = $this->get_access_token(); if ( ! empty( $option_value['refresh_token'] ) ) { $refresh_token = $option_value['refresh_token']; } else { $this->premium_core->google_drive->logs->get_connection_log()->add( 'No refresh token found.' ); } return $refresh_token; } /** * Determine whether or not we have an access token. * * @since SINCEVERSION * * @return bool */ public function has_access_token() { $access_token = $this->get_access_token(); return ! empty( $access_token ); } /** * Get our service object. * * @since 1.1.0 * * @return Google_Service_Drive object. */ public function get_service() { if ( ! is_null( $this->service ) ) { return $this->service; } $this->init(); $this->service = new Google_Service_Drive( $this->client ); return $this->service; } /** * Save our access token. * * @since 1.1.0 * * @param array $access_token An array of access token info. * @return bool */ public function update_access_token( array $access_token ) { if ( empty( $access_token['access_token'] ) || empty( $access_token['refresh_token'] ) ) { return false; } if ( isset( $access_token['error'] ) ) { return false; } return $this->premium_core->google_drive->settings->save_setting( $this->access_token_key, $access_token ); } /** * Update our authentication code. * * @since 1.1.0 * * @param string $code Authentication code. * @return bool */ public function update_code( $code ) { return $this->premium_core->google_drive->settings->save_setting( $this->code_key, $code ); } } admin/remote/google_drive_archive.php 0000644 00000026105 14760036270 0014017 0 ustar 00 */ /** * Google Drive Archive class. * * @since 1.1.0 */ class Boldgrid_Backup_Premium_Admin_Remote_Google_Drive_Archive { /** * The last error message received, if any. * * @since 1.1.0 * @var string */ public $last_error; /** * The core class object. * * @since 1.1.0 * @access private * @var Boldgrid_Backup_Admin_Core */ private $core; /** * An archive filename. * * @since 1.1.0 * @access private * @var string */ private $filename; /** * An instance of Boldgrid_Backup_Premium_Admin_Core. * * @since 1.1.0 * @access private * @var Boldgrid_Backup_Premium_Admin_Core */ private $premium_core; /** * Constructor. * * @since 1.1.0 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. * @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object. * @param string $filename An archive filename. */ public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core, $filename ) { $this->core = $core; $this->premium_core = $premium_core; $this->filename = $filename; $this->last_error = __( 'Unknown Error.', 'boldgrid-backup' ); } /** * Download a backup file. * * Had many issues following example code in the 1st @link. This method modifies that example * code based on official documentation in the 2nd @link. For example, we're manually adding the * Authorization header rather than having the google-api-php-client handle it. * * @since 1.1.0 * * @link https://github.com/googleapis/google-api-php-client/blob/master/examples/large-file-download.php * @link https://developers.google.com/drive/api/v3/manage-downloads * * @return bool True if file was downloaded successfully. */ public function download() { $log = $this->premium_core->google_drive->logs->get_download_log(); $log->add_separator(); $log->add( 'Downloading ' . $this->filename ); $client = $this->premium_core->google_drive->client->init(); if ( false === $client ) { $this->last_error = $this->premium_core->google_drive->client->last_error; $log->add( $this->last_error ); return false; } $file = $this->premium_core->google_drive->folder->get_file( $this->filename ); if ( ! $file instanceof Google_Service_Drive_DriveFile || empty( $file->id ) || empty( $file->size ) ) { $this->last_error = __( 'Unable to find backup file on Google Drive.', 'boldgrid-backup' ); $log->add( $this->last_error ); return false; } $file_size = intval( $file->size ); $local_filepath = $this->core->backup_dir->get_path_to( $this->filename ); $http = $client->authorize(); $fp = fopen( $local_filepath, 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions $chunk_size_bytes = 1 * 1024 * 1024; // Download in 1 MB chunks $chunk_start = 0; $time_start = microtime( true ); while ( $chunk_start < $file_size ) { $chunk_end = $chunk_start + $chunk_size_bytes; try { $response = $http->get( sprintf( 'https://www.googleapis.com/drive/v3/files/%s', $file->id ), array( 'query' => array( 'alt' => 'media', ), 'headers' => array( 'Range' => sprintf( 'bytes=%s-%s', $chunk_start, $chunk_end ), ), ) ); } catch ( Exception $e ) { $this->core->archive->delete( $local_filepath ); $this->last_error = esc_html( $e->getMessage() ); $log->add( $e->getMessage() ); return false; } $chunk_start = $chunk_end + 1; /* * Example api code used getBody()->getContents() instead of getBody. The difference * between the two approaches is that getContents returns the remaining contents, so * that a second call returns nothing unless you seek the position of the stream with * rewind or seek . * * @link https://stackoverflow.com/questions/30549226/guzzlehttp-how-get-the-body-of-a-response-from-guzzle-6 */ fwrite( $fp, $response->getBody( true ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions } $time_end = microtime( true ); $duration = $time_end - $time_start; $success = fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions if ( $success ) { $log->add( 'Download completed successfully.' ); $size = $this->core->wp_filesystem->size( $local_filepath ); $rate = $size / $duration; $log->add( size_format( $size, 2 ) . ' downloaded in ' . human_time_diff( $time_start, $time_end ) . '(' . size_format( $rate, 2 ) . '/s)' ); $this->core->remote->post_download( $local_filepath ); } else { $log->add( 'Download failed.' ); } return $success; } /** * Determine whether or not a backup is uploaded. * * @since 1.1.0 * * @return bool */ public function is_uploaded() { $file = $this->premium_core->google_drive->folder->get_file( $this->filename ); if ( empty( $file ) ) { $this->last_error = $this->premium_core->google_drive->folder->last_error; return false; } else { return true; } } /** * Upload an archive. * * @since 1.1.0 */ public function upload() { // Add info to the upload log. $log = $this->premium_core->google_drive->logs->get_upload_log(); $log->add_separator(); $log->add( 'Filename: ' . $this->filename ); // Add info to the connect log. $this->premium_core->google_drive->logs->get_connect_log()->add_separator(); $this->premium_core->google_drive->logs->get_connect_log()->add( __METHOD__ ); $backup_folder_id = $this->premium_core->google_drive->folder->get_backup_id(); if ( false === $backup_folder_id ) { $this->last_error = $this->premium_core->google_drive->folder->last_error; $log->add( $this->last_error ); return false; } // Setup our client and service, needed to upload. $client = $this->premium_core->google_drive->client; $client->init(); $service = $client->get_service(); // Init our archive so we can get the timestamp and filepath later below. $this->core->archive->init_by_filename( $this->filename ); $archive_size = $this->core->wp_filesystem->size( $this->core->archive->filepath ); $log->add( $this->core->archive->filepath . ' / ' . $archive_size . ' (' . size_format( $archive_size, 2 ) . ')' ); // Make sure our backup file exists. if ( ! $this->core->wp_filesystem->exists( $this->core->archive->filepath ) ) { $this->last_error = sprintf( // translators: 1 The filepath to a backup file. __( 'Archive does not exist: $1$s', 'boldgrid-backup' ), $this->core->archive->filepath ); $log->add( $this->last_error ); return false; } /* * Insert file into folder. * * @link https://developers.google.com/drive/api/v3/folder */ $file = new Google_Service_Drive_DriveFile( array( 'name' => $this->core->archive->filename, 'parents' => array( $backup_folder_id ), 'createdTime' => gmdate( 'c', $this->core->archive->timestamp ), 'properties' => array( 'createdTime' => $this->core->archive->timestamp, ), ) ); $chunk_size_bytes = 1 * 1024 * 1024; // Call the API with the media upload, defer so it doesn't immediately return. $client->client->setDefer( true ); /* * The supportsAllDrives parameter will be valid until June 1, 2020. After June 1, 2020, all * applications will be assumed to support shared drives. * * @link https://developers.google.com/drive/api/v3/enable-shareddrives */ $request = $service->files->create( $file, array( 'supportsAllDrives' => true ) ); // Create a media file upload to represent our upload process. $media = new Google_Http_MediaFileUpload( $client->client, $request, 'application/zip', null, true, $chunk_size_bytes ); $media->setFileSize( $archive_size ); // Upload the various chunks. $status will be false until the process is complete. $status = false; // Make sure we can open our backup file before we try to upload it. $handle = fopen( $this->core->archive->filepath, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions if ( false === $handle ) { $this->last_error = sprintf( // translators: 1 The filepath to a backup file. __( 'Unable to open archive: $1$s', 'boldgrid-backup' ), $this->core->archive->filepath ); $log->add( $this->last_error ); return false; } $log->add( 'Beginning to upload file...' ); $start_time = microtime( true ); while ( ! $status && ! feof( $handle ) ) { /* * Read until you get $chunk_size_bytes from TESTFILE. fread will never return more than * 8192 bytes if the stream is read buffered and it does not represent a plain file. An * example of a read buffered file is when reading from a URL */ $chunk = $this->read_big_chunk( $handle, $chunk_size_bytes ); try { $status = $media->nextChunk( $chunk ); } catch ( Google_Service_Exception $e ) { $this->last_error = __( 'Unable to upload file to Google Drive', 'boldgrid-backup' ) . ': ' . $e->getCode(); $log->add( $this->last_error ); } } fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions // Calculate how long the upload took and the bytes per second average. $end_time = microtime( true ); $upload_speed = $archive_size / ( $end_time - $start_time ); $log->add( 'File upload complete! Uploaded ' . size_format( $archive_size, 2 ) . ' in ' . human_time_diff( $start_time, $end_time ) . ' (' . size_format( $upload_speed, 2 ) . '/s)' ); /* * The final value of $status will be the data from the API for the object that's been uploaded. * * Example $status on success: https://pastebin.com/SZxwHwNC */ $result = false; if ( false !== $status ) { $result = $status; $log->add( 'Enforcing retention...' ); $this->premium_core->google_drive->folder->enforce_retention( $backup_folder_id ); $log->add( 'Retention enforcement complete.' ); } $success = false !== $result; $log->add( __METHOD__ . ' Method completed. Success? ' . ( $success ? 'Yes' : 'No' ) ); return $success; } /** * Read a big chunk. * * @since 1.1.0 * @access private * * @link https://github.com/googleapis/google-api-php-client/blob/f88a98dbaac0207e177419a15214ab4fcf30c47a/examples/large-file-upload.php#L133 * * @param resource $handle File handle. * @param int $chunk_size Chunk size. * @return string */ private function read_big_chunk( $handle, $chunk_size ) { $byte_count = 0; $giant_chunk = ''; while ( ! feof( $handle ) ) { /* * fread will never return more than 8192 bytes if the stream is read buffered and it * does not represent a plain file */ $chunk = fread( $handle, 8192 ); // phpcs:ignore WordPress.WP.AlternativeFunctions $byte_count += strlen( $chunk ); $giant_chunk .= $chunk; if ( $byte_count >= $chunk_size ) { return $giant_chunk; } } return $giant_chunk; } } admin/remote/amazon_s3_bucket.php 0000644 00000014067 14760036270 0013104 0 ustar 00 */ /** * Amazon S3 Bucket class. * * @since 1.0.0 */ class Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3_Bucket { /** * Errors. * * @since 1.0.0 * @var array */ public $errors = array(); /** * The core class object. * * @since 1.0.0 * @access private * @var Boldgrid_Backup_Admin_Core */ private $core; /** * An instance of Boldgrid_Backup_Premium_Admin_Core. * * @since 1.0.0 * @access private * @var Boldgrid_Backup_Premium_Admin_Core */ private $premium_core; /** * Constructor. * * @since 1.0.0 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. * @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object. */ public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core ) { $this->core = $core; $this->premium_core = $premium_core; } /** * Create a bucket. * * If the bucket already exists and this user owns it, we'll return true. * * @since 1.0.0 * * @param string $bucket_id Bucket id. * @return bool */ public function create( $bucket_id ) { // Validate bucket name. $valid_name = $this->premium_core->amazon_s3->client->isBucketDnsCompatible( $bucket_id ); if ( ! $valid_name ) { $this->premium_core->amazon_s3->errors[] = __( 'Invalid Bucket ID. Bucket name must be between 3 and 63 characters long, must not end with a dash or period, and must not use any special characters.', 'boldgrid-backup' ); return false; } try { $this->premium_core->amazon_s3->client->createBucket( array( 'Bucket' => $bucket_id, ) ); } catch ( Aws\S3\Exception\BucketAlreadyOwnedByYouException $e ) { return true; } catch ( Aws\S3\Exception\BucketAlreadyExistsException $e ) { $this->premium_core->amazon_s3->errors[] = sprintf( // Translators: 1: Bucket id. __( 'Bucket ID %1$s already exist. Please try another Bucket ID.', 'boldgrid-backup' ), '' . esc_html( $bucket_id ) . '' ); return false; } catch ( Exception $e ) { $this->premium_core->amazon_s3->errors[] = __( 'Unknown error when attempting to create bucket:', 'boldgrid-backup' ) . ' - ' . $e->getMessage(); return false; } return true; } /** * Delete bucket transients. * * @since 1.5.4 */ public function delete_transients() { $bucket_id = $this->premium_core->amazon_s3->bucket_id; $transient = sprintf( 'boldgrid_backup_s3_bucket_w_headers_%1$s', $bucket_id ); delete_transient( $transient ); $transient = sprintf( 'boldgrid_backup_s3_bucket_%1$s', $bucket_id ); delete_transient( $transient ); } /** * Get the contents of a bucket. * * @since 1.0.0 * * @param string $bucket_id Bucket id. * @param bool $include_headers Include headers. * @param bool $use_transient Whether or not to first try to get our * bucket from the transient. In some situations * we need fresh data, and we can pass true * to get fresh data from Amazon. * @return array https://pastebin.com/uVkx8t5A */ public function get( $bucket_id = null, $include_headers = false, $use_transient = true ) { // Shorten for readability. $s3 = $this->premium_core->amazon_s3; $s3->set_client(); if ( empty( $s3->client ) ) { return array(); } if ( ! empty( $bucket_id ) ) { $s3->set_bucket_id( $bucket_id ); } if ( ! $s3->client->isBucketDnsCompatible( $s3->bucket_id ) ) { return array(); } $transient_name = sprintf( 'boldgrid_backup_s3_bucket_%1$s%2$s', $include_headers ? 'w_headers_' : '', $s3->bucket_id ); // Save resources and try to get bucket contents from transient. if ( $use_transient ) { $bucket_contents = get_transient( $transient_name ); if ( false !== $bucket_contents ) { return $bucket_contents; } } $bucket_contents = array(); // If the bucket does not exist, return an empty bucket. try { $iterator = $s3->client->getIterator( 'ListObjects', array( 'Bucket' => $s3->bucket_id, ) ); foreach ( $iterator as $object ) { if ( ! $this->core->archive->is_site_archive( $object['Key'] ) ) { continue; } if ( $include_headers ) { $object['Headers'] = $s3->get_headers( $object['Key'] ); } $bucket_contents[] = $object; } } catch ( Aws\S3\Exception\NoSuchBucketException $e ) { return array(); } set_transient( $transient_name, $bucket_contents, 5 * MINUTE_IN_SECONDS ); return $bucket_contents; } /** * Get an item from the bucket. * * @since 1.5.4 * * @param string $key Key. * @return mixed Array on success, false on failure. */ public function get_item( $key ) { $bucket_contents = $this->get(); foreach ( $bucket_contents as $item ) { if ( $item['Key'] === $key ) { return $item; } } return false; } /** * Validate a local backup matches the remove backup. * * @since 1.5.4 * * @param string $key Key. */ public function validate_backup( $key ) { $item = $this->get_item( $key ); $local_path = $this->core->backup_dir->get_path_to( $key ); $local = $this->core->wp_filesystem->dirlist( $local_path ); $remote_size = ! empty( $item['Size'] ) ? intval( $item['Size'] ) : null; $local_size = ! empty( $local[ $key ]['size'] ) ? intval( $local[ $key ]['size'] ) : null; if ( empty( $remote_size ) || empty( $local_size ) ) { return false; } $same_size = $remote_size === $local_size; if ( ! $same_size ) { $this->errors[] = sprintf( // Translators: 1: Local file size, 2: Remote file size. __( 'Downloaded filesize (%1$s) does not match remote filesize (%2$s).', 'boldgrid-backup' ), $local_size, $remote_size ); } return $same_size; } } admin/remote/amazon_s3_backups_page.php 0000644 00000007771 14760036270 0014257 0 ustar 00 */ /** * Amazon S3 Backups Page class. * * @since 1.0.0 */ class Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3_Backups_Page { /** * An instance of Boldgrid_Backup_Admin_Core. * * @since 1.0.0 * @access private * @var Boldgrid_Backup_Premium_Admin_Core */ private $core; /** * An instance of Boldgrid_Backup_Premium_Admin_Core. * * @since 1.0.0 * @access private * @var Boldgrid_Backup_Premium_Admin_Core */ private $premium_core; /** * Constructor. * * @since 1.0.0 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. * @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object. */ public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core ) { $this->core = $core; $this->premium_core = $premium_core; } /** * Enqueue scripts. * * @since 1.5.4 * * @param string $hook Hook name. */ public function admin_enqueue_scripts( $hook ) { if ( 'total-upkeep_page_boldgrid-backup' !== $hook ) { return; } $handle = 'boldgrid-backup-premium-admin-amazon-s3'; wp_register_script( $handle, plugin_dir_url( dirname( __FILE__ ) ) . 'js/boldgrid-backup-premium-admin-amazon-s3.js', array( 'jquery' ), BOLDGRID_BACKUP_PREMIUM_VERSION, true ); $translation = array( 'downloading' => __( 'Downloading', 'boldgrid-backup' ), ); wp_localize_script( $handle, 'boldgrid_backup_premium_admin_amazon_s3', $translation ); wp_enqueue_script( $handle ); } /** * If a local backup is on Amazon S3 too, update verbiage to reflect. * * @since 1.5.4 * * @param array $locations Locations. * @param string $filepath File path. * @return array */ public function backup_locations( array $locations, $filepath ) { if ( $this->premium_core->amazon_s3->in_bucket( null, $filepath ) ) { $locations[] = __( 'Amazon S3', 'boldgrid-backup' ); } return $locations; } /** * Handle the ajax request to download an Amazon S3 backup locally. * * @since 1.5.4 */ public function wp_ajax_download() { $error = __( 'Unable to download backup from Amazon S3', 'bolgrid-bakcup' ); // Validation, user role. if ( ! current_user_can( 'update_plugins' ) ) { $this->core->notice->add_user_notice( sprintf( $error . ': ' . __( 'Permission denied.', 'boldgrid-backup' ) ), 'notice notice-error' ); wp_send_json_error(); } // Validation, nonce. if ( ! $this->core->archive_details->validate_nonce() ) { $this->core->notice->add_user_notice( sprintf( $error . ': ' . __( 'Invalid nonce.', 'boldgrid-backup' ) ), 'notice notice-error' ); wp_send_json_error(); } // Validation, $_POST data. $key = ! empty( $_POST['filename'] ) ? $_POST['filename'] : false; // phpcs:ignore if ( empty( $key ) ) { $this->core->notice->add_user_notice( sprintf( $error . ': ' . __( 'Invalid key.', 'boldgrid-backup' ) ), 'notice notice-error' ); wp_send_json_error(); } $result = $this->premium_core->amazon_s3->download( $key ); if ( $result ) { $this->core->notice->add_user_notice( sprintf( // Translators: 1: Key, 2: Title. __( 'Backup file %1$s successfully downloaded from Amazon S3.
', 'boldgrid-backup' ), /* 1 */ $key, /* 2 */ BOLDGRID_BACKUP_PREMIUM_TITLE . ' - ' . __( 'Amazon S3 Download', 'boldgrid-backup' ) ), 'notice notice-success' ); wp_send_json_success(); } if ( ! empty( $this->premium_core->amazon_s3->errors ) ) { $this->core->notice->add_user_notice( implode( '' . sprintf( // Translators: 1: File count. __( 'We found %1$s different version(s) of this file you can restore.', 'boldgrid-backup' ), count( $version_count ) ) . '
'; $versions_table .= sprintf( '# | %1$s | %2$s | %3$s |
---|---|---|---|
%1$s %2$s
', $this->core->lang['icon_warning'], __( 'No versions of this file could be found.', 'boldgrid-backup' ) ); } wp_send_json_success( $versions_table ); } /** * Restore a historical version. * * @since 1.5.3 */ public function wp_ajax_restore_historical() { $error = __( 'An error occurred while attempting to restore this file:', 'boldgrid-backup' ); if ( ! current_user_can( 'update_plugins' ) ) { wp_send_json_error( $error . ' ' . __( 'Permission denied.', 'boldgrid-backup' ) ); } if ( ! check_ajax_referer( 'bgbkup_historical_version_page', 'security', false ) ) { wp_send_json_error( $error . ' ' . __( 'Invalid nonce.', 'boldgrid-backup' ) ); } $file_version = ! empty( $_POST['file_version'] ) ? $_POST['file_version'] : false; // phpcs:ignore $file = ! empty( $_POST['file'] ) ? $_POST['file'] : false; // phpcs:ignore if ( empty( $file_version ) || empty( $file ) ) { wp_send_json_error( $error . ' ' . __( 'Invalid file / version.', 'boldgrid-backup' ) ); } $restored = $this->restore( $file, $file_version ); if ( $restored ) { wp_send_json_success( __( '✓ Restored', 'boldgrid-backup' ) ); } $error_message = ! empty( $this->errors ) ? implode( ' ', $this->errors ) : __( 'Unknown error.', 'boldgrid-backup' ); wp_send_json_error( $error . ' ' . $error_message ); } } admin/class-boldgrid-backup-premium-admin-core.php 0000644 00000013744 14760036270 0016207 0 ustar 00 */ /** * Class: Boldgrid_Backup_Premium_Admin_Core * * @since 1.0.0 */ class Boldgrid_Backup_Premium_Admin_Core { /** * Amazon S3 class. * * @since 1.0.0 * @var Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3 */ public $amazon_s3; /** * An instance of the Boldgrid_Backup_Premium_Admin_Archive_Browser class. * * @since 1.5.3 * @var Boldgrid_Backup_Premium_Admin_Archive_Browser */ public $archive_browser; /** * Our DreamObjects class. * * @since 1.2.0 * @var Boldgrid_Backup_Premium_Admin_Remote_Dreamobjects */ public $dreamobjects; /** * An instance of Boldgrid_Backup_Premium_Admin_Core * * @since 1.1.0 * @var Boldgrid_Backup_Premium_Admin_Core */ public $google_drive; /** * An instance of Boldgrid_Backup_Premium_Admin_Historical. * * @since 1.5.3 * @var Boldgrid_Backup_Premium_Admin_Historical */ public $historical; /** * An instance of Boldgrid_Backup_Premium_Admin_History. * * @since 1.5.3 * @var Boldgrid_Backup_Premium_Admin_History */ public $history; /** * An instance of Boldgrid_Backup_Premium_Admin_Plugin_Editor. * * @since 1.5.3 * @var Boldgrid_Backup_Premium_Admin_Plugin_Editor */ public $plugin_editor; /** * An instance of Boldgrid_Backup_Premium_Admin_Recent. * * @since 1.5.4 * @var Boldgrid_Backup_Premium_Admin_Recent */ public $recent; /** * An instance of Boldgrid_Backup_Premium_Admin_Support. * * @since 1.1.0 * @var Boldgrid_Backup_Premium_Admin_Support */ public $support; /** * An instance of Boldgrid\Backup\Premium\Admin\Crypt. * * @since 1.3.0 * @var Boldgrid\Backup\Premium\Admin\Crypt */ public $crypt; /** * An instance of Boldgrid\Backup\Premium\Admin\Settings. * * @since 1.3.0 * @var Boldgrid\Backup\Premium\Admin\Settings */ public $settings; /** * Configuration array. * * @since 1.0.0 * @access private * @var array * @staticvar */ private static $configs; /** * The core class object. * * @since 1.0.0 * @access private * @var Boldgrid_Backup_Admin_Core */ private $core; /** * Constructor. * * @since 1.0.0 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. */ public function __construct( Boldgrid_Backup_Admin_Core $core ) { $this->core = $core; $this->archive_browser = new Boldgrid_Backup_Premium_Admin_Archive_Browser( $this->core, $this ); $this->amazon_s3 = new Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3( $this->core, $this ); $this->historical = new Boldgrid_Backup_Premium_Admin_Historical( $this->core, $this ); $this->plugin_editor = new Boldgrid_Backup_Premium_Admin_Plugin_Editor( $this->core, $this ); $this->history = new Boldgrid_Backup_Premium_Admin_History( $this->core, $this ); $this->recent = new Boldgrid_Backup_Premium_Admin_Recent( $this->core, $this ); $this->support = new Boldgrid_Backup_Premium_Admin_Support( $this->core, $this ); $this->crypt = new \Boldgrid\Backup\Premium\Admin\Crypt( $this->core, $this ); $this->settings = new \Boldgrid\Backup\Premium\Admin\Settings( $this->core, $this ); $this->google_drive = new Boldgrid_Backup_Premium_Admin_Remote_Google_Drive( $this->core, $this ); $this->dreamobjects = new Boldgrid_Backup_Premium_Admin_Remote_Dreamobjects(); $this->prepare_plugin_update(); } /** * Prepare the plugin update class. * * @since 1.0.0 * * @see self::get_configs() */ public function prepare_plugin_update() { $is_cron = ( defined( 'DOING_CRON' ) && DOING_CRON ); $is_wpcli = ( defined( 'WP_CLI' ) && WP_CLI ); if ( $is_cron || $is_wpcli || is_admin() ) { require_once BOLDGRID_BACKUP_PREMIUM_PATH . '/admin/class-boldgrid-backup-premium-admin-update.php'; $plugin_update = new Boldgrid_Backup_Premium_Admin_Update( self::get_configs() ); add_action( 'init', array( $plugin_update, 'add_hooks', ) ); } } /** * Get configuration settings. * * @since 1.0.0 * * @static * * @return array An array of configuration settings. */ public static function get_configs() { // If the configuration array was already created, then return it. if ( ! empty( self::$configs ) ) { return self::$configs; } // Set the config directory. $config_dir = BOLDGRID_BACKUP_PREMIUM_PATH . '/includes/config'; // Set the config file paths. $global_config_path = $config_dir . '/config.plugin.php'; $local_config_path = $config_dir . '/config.local.php'; // Initialize $global_configs array. $global_configs = array(); // If a global config file exists, read the global configuration settings. if ( file_exists( $global_config_path ) ) { $global_configs = require $global_config_path; } // Initialize $local_configs array. $local_configs = array(); // If a local configuration file exists, then read the settings. if ( file_exists( $local_config_path ) ) { $local_configs = require $local_config_path; } // If an api key hash stored in the database, then set it as the global api_key. $api_key_from_database = get_option( 'boldgrid_api_key' ); if ( ! empty( $api_key_from_database ) ) { $global_configs['api_key'] = $api_key_from_database; } // Get the WordPress site url and set it in the global configs array. $global_configs['site_url'] = get_site_url(); // Merge global and local configuration settings. if ( ! empty( $local_configs ) ) { $configs = array_replace_recursive( $global_configs, $local_configs ); } else { $configs = $global_configs; } // Set the configuration array in the class property. self::$configs = $configs; // Return the configuration array. return $configs; } } admin/class-boldgrid-backup-premium-admin-archive-browser.php 0000644 00000011335 14760036270 0020353 0 ustar 00 */ /** * Amazon S3 class. * * @since 1.0.0 */ class Boldgrid_Backup_Premium_Admin_Archive_Browser { /** * The core class object. * * @since 1.5.3 * @var Boldgrid_Backup_Admin_Core */ private $core; /** * An instance of Boldgrid_Backup_Premium_Admin_Core. * * @since 1.5.3 * @var Boldgrid_Backup_Premium_Admin_Core */ private $premium_core; /** * Constructor. * * @since 1.5.3 * * @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object. * @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object. */ public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core ) { $this->core = $core; $this->premium_core = $premium_core; } /** * Enqueue scripts on the archive details page. * * @since 1.5.3 */ public function enqueue_archive_details() { wp_register_script( 'boldgrid-backup-premium-admin-zip-browser', plugin_dir_url( __FILE__ ) . 'js/boldgrid-backup-premium-admin-zip-browser.js', array( 'jquery' ), BOLDGRID_BACKUP_PREMIUM_VERSION, true ); $translations = array( 'restoring' => __( 'Restoring', 'boldgrid-backup' ), 'unknownError' => __( 'An unknown error occurred when attempting to restore this file.', 'boldgrid-backup' ), ); wp_localize_script( 'boldgrid-backup-premium-admin-zip-browser', 'boldgrid_backup_premium_zip_browser', $translations ); wp_enqueue_script( 'boldgrid-backup-premium-admin-zip-browser' ); } /** * Provide a list of premium features for one file in an archive. * * For example, allow the user to restore one file from an archive. * * @since 1.5.3 * * @param string $response Response. * @param string $file File. * @return string */ public function wp_ajax_file_actions( $response, $file ) { $response = sprintf( ' %1$s | %2$s%4$s
', __( 'Restore this version', 'boldgrid-backup' ), __( 'Find other versions to restore', 'boldgrid-backup' ), $file, __( 'When you choose to restore a single file from backup, a copy of the file is made just before it is overwritten. You can restore this file on the Find other versions to restore page.', 'boldgrid-backup' ) ); return $response; } /** * Restore one file (via an ajax call). * * @since 1.5.3 */ public function wp_ajax_restore_file() { $error = __( 'An error occurred while attempting to restore this file:', 'boldgrid-backup' ); if ( ! current_user_can( 'update_plugins' ) ) { wp_send_json_error( $error . ' ' . __( 'Permission denied.', 'boldgrid-backup' ) ); } /* * Nonce validation. * * Single files can be restored from either the: * 1. Arhchive Browser (bgbkup_archive_details_page). Total Upkeep provides a general nonce * for usage on the Archive Details page. * 2. Historical Versions page (bgbkup_historical_version_page). */ $valid_nonce = check_ajax_referer( 'bgbkup_archive_details_page', 'security', false ) || check_ajax_referer( 'bgbkup_historical_version_page', 'security', false ); if ( ! $valid_nonce ) { wp_send_json_error( $error . ' ' . __( 'Invalid nonce.', 'boldgrid-backup' ) ); } $filename = ! empty( $_POST['filename'] ) ? $_POST['filename'] : false; // phpcs:ignore $filepath = $this->core->backup_dir->get_path_to( $filename ); $file = ! empty( $_POST['file'] ) ? $_POST['file'] : false; // phpcs:ignore if ( ! $this->core->archive->is_archive( $filepath ) || empty( $file ) ) { wp_send_json_error( $error . ' ' . __( 'Invalid file / filepath.', 'boldgrid-backup' ) ); } $this->premium_core->historical->save( $file ); $zip = new Boldgrid_Backup_Admin_Compressor_Pcl_Zip( $this->core ); $status = $zip->extract_one( $filepath, $file ); if ( ! $status ) { $error_message = ! empty( $zip->test_errors ) ? implode( '' . esc_html__( 'When To Perform Updates', 'boldgrid-backup' ) . ' | |||
---|---|---|---|
' . esc_html__( 'Select the number of days you wish to wait after an update is released before updating plugins or themes.', 'boldgrid-backup' ) . ' |
|||
'; $when_updates_markup .= ' ' . esc_html__( 'Days since update was released' ) . ' |
Timely Auto Updates are enabled, but you have not selected any themes or plugins to Auto Update.
* // First try an INI file at this location.
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
* // Then try an INI file at this location.
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
* // Then try loading from environment variables.
* $c = ConfigurationProvider::env();
* // Combine the three providers together.
* $composed = ConfigurationProvider::chain($a, $b, $c);
* // Returns a promise that is fulfilled with a configuration or throws.
* $promise = $composed();
* // Wait on the configuration to resolve.
* $config = $promise->wait();
*
*/
class ConfigurationProvider extends AbstractConfigurationProvider
implements ConfigurationProviderInterface
{
const DEFAULT_CLIENT_ID = '';
const DEFAULT_ENABLED = false;
const DEFAULT_HOST = '127.0.0.1';
const DEFAULT_PORT = 31000;
const ENV_CLIENT_ID = 'AWS_CSM_CLIENT_ID';
const ENV_ENABLED = 'AWS_CSM_ENABLED';
const ENV_HOST = 'AWS_CSM_HOST';
const ENV_PORT = 'AWS_CSM_PORT';
const ENV_PROFILE = 'AWS_PROFILE';
public static $cacheKey = 'aws_cached_csm_config';
protected static $interfaceClass = ConfigurationInterface::class;
protected static $exceptionClass = ConfigurationException::class;
/**
* Create a default config provider that first checks for environment
* variables, then checks for a specified profile in the environment-defined
* config file location (env variable is 'AWS_CONFIG_FILE', file location
* defaults to ~/.aws/config), then checks for the "default" profile in the
* environment-defined config file location, and failing those uses a default
* fallback set of configuration options.
*
* This provider is automatically wrapped in a memoize function that caches
* previously provided config options.
*
* @param array $config
*
* @return callable
*/
public static function defaultProvider(array $config = [])
{
$configProviders = [self::env()];
if (
!isset($config['use_aws_shared_config_files'])
|| $config['use_aws_shared_config_files'] != false
) {
$configProviders[] = self::ini();
}
$configProviders[] = self::fallback();
$memo = self::memoize(
call_user_func_array('self::chain', $configProviders)
);
if (isset($config['csm']) && $config['csm'] instanceof CacheInterface) {
return self::cache($memo, $config['csm'], self::$cacheKey);
}
return $memo;
}
/**
* Provider that creates CSM config from environment variables.
*
* @return callable
*/
public static function env()
{
return function () {
// Use credentials from environment variables, if available
$enabled = getenv(self::ENV_ENABLED);
if ($enabled !== false) {
return Promise\promise_for(
new Configuration(
$enabled,
getenv(self::ENV_HOST) ?: self::DEFAULT_HOST,
getenv(self::ENV_PORT) ?: self::DEFAULT_PORT,
getenv(self:: ENV_CLIENT_ID) ?: self::DEFAULT_CLIENT_ID
)
);
}
return self::reject('Could not find environment variable CSM config'
. ' in ' . self::ENV_ENABLED. '/' . self::ENV_HOST . '/'
. self::ENV_PORT . '/' . self::ENV_CLIENT_ID);
};
}
/**
* Fallback config options when other sources are not set.
*
* @return callable
*/
public static function fallback()
{
return function() {
return Promise\promise_for(
new Configuration(
self::DEFAULT_ENABLED,
self::DEFAULT_HOST,
self::DEFAULT_PORT,
self::DEFAULT_CLIENT_ID
)
);
};
}
/**
* Config provider that creates config using a config file whose location
* is specified by an environment variable 'AWS_CONFIG_FILE', defaulting to
* ~/.aws/config if not specified
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile.
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the default directory.
*
* @return callable
*/
public static function ini($profile = null, $filename = null)
{
$filename = $filename ?: (self::getDefaultConfigFilename());
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'aws_csm');
return function () use ($profile, $filename) {
if (!is_readable($filename)) {
return self::reject("Cannot read CSM config from $filename");
}
$data = \Aws\parse_ini_file($filename, true);
if ($data === false) {
return self::reject("Invalid config file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in config file");
}
if (!isset($data[$profile]['csm_enabled'])) {
return self::reject("Required CSM config values not present in
INI profile '{$profile}' ({$filename})");
}
// host is optional
if (empty($data[$profile]['csm_host'])) {
$data[$profile]['csm_host'] = self::DEFAULT_HOST;
}
// port is optional
if (empty($data[$profile]['csm_port'])) {
$data[$profile]['csm_port'] = self::DEFAULT_PORT;
}
// client_id is optional
if (empty($data[$profile]['csm_client_id'])) {
$data[$profile]['csm_client_id'] = self::DEFAULT_CLIENT_ID;
}
return Promise\promise_for(
new Configuration(
$data[$profile]['csm_enabled'],
$data[$profile]['csm_host'],
$data[$profile]['csm_port'],
$data[$profile]['csm_client_id']
)
);
};
}
/**
* Unwraps a configuration object in whatever valid form it is in,
* always returning a ConfigurationInterface object.
*
* @param mixed $config
* @return ConfigurationInterface
* @throws \InvalidArgumentException
*/
public static function unwrap($config)
{
if (is_callable($config)) {
$config = $config();
}
if ($config instanceof PromiseInterface) {
$config = $config->wait();
}
if ($config instanceof ConfigurationInterface) {
return $config;
} elseif (is_array($config) && isset($config['enabled'])) {
$client_id = isset($config['client_id']) ? $config['client_id']
: self::DEFAULT_CLIENT_ID;
$host = isset($config['host']) ? $config['host']
: self::DEFAULT_HOST;
$port = isset($config['port']) ? $config['port']
: self::DEFAULT_PORT;
return new Configuration($config['enabled'], $host, $port, $client_id);
}
throw new \InvalidArgumentException('Not a valid CSM configuration '
. 'argument.');
}
} vendor/aws/aws-sdk-php/src/ClientSideMonitoring/ConfigurationInterface.php 0000644 00000001503 14760036270 0023016 0 ustar 00 host = $host;
$this->port = filter_var($port, FILTER_VALIDATE_INT);
if ($this->port === false) {
throw new \InvalidArgumentException(
"CSM 'port' value must be an integer!");
}
// Unparsable $enabled flag errors on the side of disabling CSM
$this->enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
$this->clientId = trim($clientId);
}
/**
* {@inheritdoc}
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* {@inheritdoc}
*/
public function getClientId()
{
return $this->clientId;
}
/**
* /{@inheritdoc}
*/
public function getHost()
{
return $this->host;
}
/**
* {@inheritdoc}
*/
public function getPort()
{
return $this->port;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return [
'client_id' => $this->getClientId(),
'enabled' => $this->isEnabled(),
'host' => $this->getHost(),
'port' => $this->getPort()
];
}
} vendor/aws/aws-sdk-php/src/ClientSideMonitoring/ApiCallMonitoringMiddleware.php 0000644 00000011271 14760036270 0023742 0 ustar 00 'AwsException',
'FinalAwsExceptionMessage' => 'AwsExceptionMessage',
'FinalSdkException' => 'SdkException',
'FinalSdkExceptionMessage' => 'SdkExceptionMessage',
'FinalHttpStatusCode' => 'HttpStatusCode',
];
/**
* Standard middleware wrapper function with CSM options passed in.
*
* @param callable $credentialProvider
* @param mixed $options
* @param string $region
* @param string $service
* @return callable
*/
public static function wrap(
callable $credentialProvider,
$options,
$region,
$service
) {
return function (callable $handler) use (
$credentialProvider,
$options,
$region,
$service
) {
return new static(
$handler,
$credentialProvider,
$options,
$region,
$service
);
};
}
/**
* {@inheritdoc}
*/
public static function getRequestData(RequestInterface $request)
{
return [];
}
/**
* {@inheritdoc}
*/
public static function getResponseData($klass)
{
if ($klass instanceof ResultInterface) {
$data = [
'AttemptCount' => self::getResultAttemptCount($klass),
'MaxRetriesExceeded' => 0,
];
} elseif ($klass instanceof \Exception) {
$data = [
'AttemptCount' => self::getExceptionAttemptCount($klass),
'MaxRetriesExceeded' => self::getMaxRetriesExceeded($klass),
];
} else {
throw new \InvalidArgumentException('Parameter must be an instance of ResultInterface or Exception.');
}
return $data + self::getFinalAttemptData($klass);
}
private static function getResultAttemptCount(ResultInterface $result) {
if (isset($result['@metadata']['transferStats']['http'])) {
return count($result['@metadata']['transferStats']['http']);
}
return 1;
}
private static function getExceptionAttemptCount(\Exception $e) {
$attemptCount = 0;
if ($e instanceof MonitoringEventsInterface) {
foreach ($e->getMonitoringEvents() as $event) {
if (isset($event['Type']) &&
$event['Type'] === 'ApiCallAttempt') {
$attemptCount++;
}
}
}
return $attemptCount;
}
private static function getFinalAttemptData($klass)
{
$data = [];
if ($klass instanceof MonitoringEventsInterface) {
$finalAttempt = self::getFinalAttempt($klass->getMonitoringEvents());
if (!empty($finalAttempt)) {
foreach (self::$eventKeys as $callKey => $attemptKey) {
if (isset($finalAttempt[$attemptKey])) {
$data[$callKey] = $finalAttempt[$attemptKey];
}
}
}
}
return $data;
}
private static function getFinalAttempt(array $events)
{
for (end($events); key($events) !== null; prev($events)) {
$current = current($events);
if (isset($current['Type'])
&& $current['Type'] === 'ApiCallAttempt'
) {
return $current;
}
}
return null;
}
private static function getMaxRetriesExceeded($klass)
{
if ($klass instanceof AwsException && $klass->isMaxRetriesExceeded()) {
return 1;
}
return 0;
}
/**
* {@inheritdoc}
*/
protected function populateRequestEventData(
CommandInterface $cmd,
RequestInterface $request,
array $event
) {
$event = parent::populateRequestEventData($cmd, $request, $event);
$event['Type'] = 'ApiCall';
return $event;
}
/**
* {@inheritdoc}
*/
protected function populateResultEventData(
$result,
array $event
) {
$event = parent::populateResultEventData($result, $event);
$event['Latency'] = (int) (floor(microtime(true) * 1000) - $event['Timestamp']);
return $event;
}
}
vendor/aws/aws-sdk-php/src/ClientSideMonitoring/ApiCallAttemptMonitoringMiddleware.php 0000644 00000020327 14760036270 0025303 0 ustar 00 $request->getUri()->getHost(),
];
}
/**
* {@inheritdoc}
*/
public static function getResponseData($klass)
{
if ($klass instanceof ResultInterface) {
return [
'AttemptLatency' => self::getResultAttemptLatency($klass),
'DestinationIp' => self::getResultDestinationIp($klass),
'DnsLatency' => self::getResultDnsLatency($klass),
'HttpStatusCode' => self::getResultHttpStatusCode($klass),
'XAmzId2' => self::getResultHeader($klass, 'x-amz-id-2'),
'XAmzRequestId' => self::getResultHeader($klass, 'x-amz-request-id'),
'XAmznRequestId' => self::getResultHeader($klass, 'x-amzn-RequestId'),
];
}
if ($klass instanceof AwsException) {
return [
'AttemptLatency' => self::getAwsExceptionAttemptLatency($klass),
'AwsException' => substr(
self::getAwsExceptionErrorCode($klass),
0,
128
),
'AwsExceptionMessage' => substr(
self::getAwsExceptionMessage($klass),
0,
512
),
'DestinationIp' => self::getAwsExceptionDestinationIp($klass),
'DnsLatency' => self::getAwsExceptionDnsLatency($klass),
'HttpStatusCode' => self::getAwsExceptionHttpStatusCode($klass),
'XAmzId2' => self::getAwsExceptionHeader($klass, 'x-amz-id-2'),
'XAmzRequestId' => self::getAwsExceptionHeader(
$klass,
'x-amz-request-id'
),
'XAmznRequestId' => self::getAwsExceptionHeader(
$klass,
'x-amzn-RequestId'
),
];
}
if ($klass instanceof \Exception) {
return [
'HttpStatusCode' => self::getExceptionHttpStatusCode($klass),
'SdkException' => substr(
self::getExceptionCode($klass),
0,
128
),
'SdkExceptionMessage' => substr(
self::getExceptionMessage($klass),
0,
512
),
'XAmzId2' => self::getExceptionHeader($klass, 'x-amz-id-2'),
'XAmzRequestId' => self::getExceptionHeader($klass, 'x-amz-request-id'),
'XAmznRequestId' => self::getExceptionHeader($klass, 'x-amzn-RequestId'),
];
}
throw new \InvalidArgumentException('Parameter must be an instance of ResultInterface, AwsException or Exception.');
}
private static function getResultAttemptLatency(ResultInterface $result)
{
if (isset($result['@metadata']['transferStats']['http'])) {
$attempt = end($result['@metadata']['transferStats']['http']);
if (isset($attempt['total_time'])) {
return (int) floor($attempt['total_time'] * 1000);
}
}
return null;
}
private static function getResultDestinationIp(ResultInterface $result)
{
if (isset($result['@metadata']['transferStats']['http'])) {
$attempt = end($result['@metadata']['transferStats']['http']);
if (isset($attempt['primary_ip'])) {
return $attempt['primary_ip'];
}
}
return null;
}
private static function getResultDnsLatency(ResultInterface $result)
{
if (isset($result['@metadata']['transferStats']['http'])) {
$attempt = end($result['@metadata']['transferStats']['http']);
if (isset($attempt['namelookup_time'])) {
return (int) floor($attempt['namelookup_time'] * 1000);
}
}
return null;
}
private static function getResultHttpStatusCode(ResultInterface $result)
{
return $result['@metadata']['statusCode'];
}
private static function getAwsExceptionAttemptLatency(AwsException $e) {
$attempt = $e->getTransferInfo();
if (isset($attempt['total_time'])) {
return (int) floor($attempt['total_time'] * 1000);
}
return null;
}
private static function getAwsExceptionErrorCode(AwsException $e) {
return $e->getAwsErrorCode();
}
private static function getAwsExceptionMessage(AwsException $e) {
return $e->getAwsErrorMessage();
}
private static function getAwsExceptionDestinationIp(AwsException $e) {
$attempt = $e->getTransferInfo();
if (isset($attempt['primary_ip'])) {
return $attempt['primary_ip'];
}
return null;
}
private static function getAwsExceptionDnsLatency(AwsException $e) {
$attempt = $e->getTransferInfo();
if (isset($attempt['namelookup_time'])) {
return (int) floor($attempt['namelookup_time'] * 1000);
}
return null;
}
private static function getAwsExceptionHttpStatusCode(AwsException $e) {
$response = $e->getResponse();
if ($response !== null) {
return $response->getStatusCode();
}
return null;
}
private static function getExceptionHttpStatusCode(\Exception $e) {
if ($e instanceof ResponseContainerInterface) {
$response = $e->getResponse();
if ($response instanceof ResponseInterface) {
return $response->getStatusCode();
}
}
return null;
}
private static function getExceptionCode(\Exception $e) {
if (!($e instanceof AwsException)) {
return get_class($e);
}
return null;
}
private static function getExceptionMessage(\Exception $e) {
if (!($e instanceof AwsException)) {
return $e->getMessage();
}
return null;
}
/**
* {@inheritdoc}
*/
protected function populateRequestEventData(
CommandInterface $cmd,
RequestInterface $request,
array $event
) {
$event = parent::populateRequestEventData($cmd, $request, $event);
$event['Type'] = 'ApiCallAttempt';
return $event;
}
/**
* {@inheritdoc}
*/
protected function populateResultEventData(
$result,
array $event
) {
$event = parent::populateResultEventData($result, $event);
$provider = $this->credentialProvider;
/** @var CredentialsInterface $credentials */
$credentials = $provider()->wait();
$event['AccessKey'] = $credentials->getAccessKeyId();
$sessionToken = $credentials->getSecurityToken();
if ($sessionToken !== null) {
$event['SessionToken'] = $sessionToken;
}
if (empty($event['AttemptLatency'])) {
$event['AttemptLatency'] = (int) (floor(microtime(true) * 1000) - $event['Timestamp']);
}
return $event;
}
}
vendor/aws/aws-sdk-php/src/ClientSideMonitoring/AbstractMonitoringMiddleware.php 0000644 00000017674 14760036270 0024215 0 ustar 00 getResponse();
if ($response !== null) {
$header = $response->getHeader($headerName);
if (!empty($header[0])) {
return $header[0];
}
}
return null;
}
protected static function getResultHeader(ResultInterface $result, $headerName)
{
if (isset($result['@metadata']['headers'][$headerName])) {
return $result['@metadata']['headers'][$headerName];
}
return null;
}
protected static function getExceptionHeader(\Exception $e, $headerName)
{
if ($e instanceof ResponseContainerInterface) {
$response = $e->getResponse();
if ($response instanceof ResponseInterface) {
$header = $response->getHeader($headerName);
if (!empty($header[0])) {
return $header[0];
}
}
}
return null;
}
/**
* Constructor stores the passed in handler and options.
*
* @param callable $handler
* @param callable $credentialProvider
* @param $options
* @param $region
* @param $service
*/
public function __construct(
callable $handler,
callable $credentialProvider,
$options,
$region,
$service
) {
$this->nextHandler = $handler;
$this->credentialProvider = $credentialProvider;
$this->options = $options;
$this->region = $region;
$this->service = $service;
}
/**
* Standard invoke pattern for middleware execution to be implemented by
* child classes.
*
* @param CommandInterface $cmd
* @param RequestInterface $request
* @return Promise\PromiseInterface
*/
public function __invoke(CommandInterface $cmd, RequestInterface $request)
{
$handler = $this->nextHandler;
$eventData = null;
$enabled = $this->isEnabled();
if ($enabled) {
$cmd['@http']['collect_stats'] = true;
$eventData = $this->populateRequestEventData(
$cmd,
$request,
$this->getNewEvent($cmd, $request)
);
}
$g = function ($value) use ($eventData, $enabled) {
if ($enabled) {
$eventData = $this->populateResultEventData(
$value,
$eventData
);
$this->sendEventData($eventData);
if ($value instanceof MonitoringEventsInterface) {
$value->appendMonitoringEvent($eventData);
}
}
if ($value instanceof \Exception || $value instanceof \Throwable) {
return Promise\rejection_for($value);
}
return $value;
};
return Promise\promise_for($handler($cmd, $request))->then($g, $g);
}
private function getClientId()
{
return $this->unwrappedOptions()->getClientId();
}
private function getNewEvent(
CommandInterface $cmd,
RequestInterface $request
) {
$event = [
'Api' => $cmd->getName(),
'ClientId' => $this->getClientId(),
'Region' => $this->getRegion(),
'Service' => $this->getService(),
'Timestamp' => (int) floor(microtime(true) * 1000),
'UserAgent' => substr(
$request->getHeaderLine('User-Agent') . ' ' . \Aws\default_user_agent(),
0,
256
),
'Version' => 1
];
return $event;
}
private function getHost()
{
return $this->unwrappedOptions()->getHost();
}
private function getPort()
{
return $this->unwrappedOptions()->getPort();
}
private function getRegion()
{
return $this->region;
}
private function getService()
{
return $this->service;
}
/**
* Returns enabled flag from options, unwrapping options if necessary.
*
* @return bool
*/
private function isEnabled()
{
return $this->unwrappedOptions()->isEnabled();
}
/**
* Returns $eventData array with information from the request and command.
*
* @param CommandInterface $cmd
* @param RequestInterface $request
* @param array $event
* @return array
*/
protected function populateRequestEventData(
CommandInterface $cmd,
RequestInterface $request,
array $event
) {
$dataFormat = static::getRequestData($request);
foreach ($dataFormat as $eventKey => $value) {
if ($value !== null) {
$event[$eventKey] = $value;
}
}
return $event;
}
/**
* Returns $eventData array with information from the response, including
* the calculation for attempt latency.
*
* @param ResultInterface|\Exception $result
* @param array $event
* @return array
*/
protected function populateResultEventData(
$result,
array $event
) {
$dataFormat = static::getResponseData($result);
foreach ($dataFormat as $eventKey => $value) {
if ($value !== null) {
$event[$eventKey] = $value;
}
}
return $event;
}
/**
* Creates a UDP socket resource and stores it with the class, or retrieves
* it if already instantiated and connected. Handles error-checking and
* re-connecting if necessary. If $forceNewConnection is set to true, a new
* socket will be created.
*
* @param bool $forceNewConnection
* @return Resource
*/
private function prepareSocket($forceNewConnection = false)
{
if (!is_resource(self::$socket)
|| $forceNewConnection
|| socket_last_error(self::$socket)
) {
self::$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_clear_error(self::$socket);
socket_connect(self::$socket, $this->getHost(), $this->getPort());
}
return self::$socket;
}
/**
* Sends formatted monitoring event data via the UDP socket connection to
* the CSM agent endpoint.
*
* @param array $eventData
* @return int
*/
private function sendEventData(array $eventData)
{
$socket = $this->prepareSocket();
$datagram = json_encode($eventData);
$result = socket_write($socket, $datagram, strlen($datagram));
if ($result === false) {
$this->prepareSocket(true);
}
return $result;
}
/**
* Unwraps options, if needed, and returns them.
*
* @return ConfigurationInterface
*/
private function unwrappedOptions()
{
if (!($this->options instanceof ConfigurationInterface)) {
try {
$this->options = ConfigurationProvider::unwrap($this->options);
} catch (\Exception $e) {
// Errors unwrapping CSM config defaults to disabling it
$this->options = new Configuration(
false,
ConfigurationProvider::DEFAULT_HOST,
ConfigurationProvider::DEFAULT_PORT
);
}
}
return $this->options;
}
} vendor/aws/aws-sdk-php/src/Credentials/InstanceProfileProvider.php 0000644 00000022255 14760036270 0021341 0 ustar 00 timeout = (float) getenv(self::ENV_TIMEOUT) ?: (isset($config['timeout']) ? $config['timeout'] : 1.0);
$this->profile = isset($config['profile']) ? $config['profile'] : null;
$this->retries = (int) getenv(self::ENV_RETRIES) ?: (isset($config['retries']) ? $config['retries'] : 3);
$this->attempts = 0;
$this->client = isset($config['client'])
? $config['client'] // internal use only
: \Aws\default_http_handler();
}
/**
* Loads instance profile credentials.
*
* @return PromiseInterface
*/
public function __invoke()
{
return Promise\coroutine(function () {
// Retrieve token or switch out of secure mode
$token = null;
while ($this->secureMode && is_null($token)) {
try {
$token = (yield $this->request(
self::TOKEN_PATH,
'PUT',
[
'x-aws-ec2-metadata-token-ttl-seconds' => 21600
]
));
} catch (TransferException $e) {
if (!method_exists($e, 'getResponse')
|| empty($e->getResponse())
|| !in_array(
$e->getResponse()->getStatusCode(),
[400, 500, 502, 503, 504]
)
) {
$this->secureMode = false;
} else {
$this->handleRetryableException(
$e,
[],
$this->createErrorMessage(
'Error retrieving metadata token'
)
);
}
}
$this->attempts++;
}
// Set token header only for secure mode
$headers = [];
if ($this->secureMode) {
$headers = [
'x-aws-ec2-metadata-token' => $token
];
}
// Retrieve profile
while (!$this->profile) {
try {
$this->profile = (yield $this->request(
self::CRED_PATH,
'GET',
$headers
));
} catch (TransferException $e) {
// 401 indicates insecure flow not supported, switch to
// attempting secure mode for subsequent calls
if (!empty($this->getExceptionStatusCode($e))
&& $this->getExceptionStatusCode($e) === 401
) {
$this->secureMode = true;
}
$this->handleRetryableException(
$e,
[ 'blacklist' => [401, 403] ],
$this->createErrorMessage($e->getMessage())
);
}
$this->attempts++;
}
// Retrieve credentials
$result = null;
while ($result == null) {
try {
$json = (yield $this->request(
self::CRED_PATH . $this->profile,
'GET',
$headers
));
$result = $this->decodeResult($json);
} catch (InvalidJsonException $e) {
$this->handleRetryableException(
$e,
[ 'blacklist' => [401, 403] ],
$this->createErrorMessage(
'Invalid JSON response, retries exhausted'
)
);
} catch (TransferException $e) {
// 401 indicates insecure flow not supported, switch to
// attempting secure mode for subsequent calls
if (!empty($this->getExceptionStatusCode($e))
&& $this->getExceptionStatusCode($e) === 401
) {
$this->secureMode = true;
}
$this->handleRetryableException(
$e,
[ 'blacklist' => [401, 403] ],
$this->createErrorMessage($e->getMessage())
);
}
$this->attempts++;
}
yield new Credentials(
$result['AccessKeyId'],
$result['SecretAccessKey'],
$result['Token'],
strtotime($result['Expiration'])
);
});
}
/**
* @param string $url
* @param string $method
* @param array $headers
* @return PromiseInterface Returns a promise that is fulfilled with the
* body of the response as a string.
*/
private function request($url, $method = 'GET', $headers = [])
{
$disabled = getenv(self::ENV_DISABLE) ?: false;
if (strcasecmp($disabled, 'true') === 0) {
throw new CredentialsException(
$this->createErrorMessage('EC2 metadata service access disabled')
);
}
$fn = $this->client;
$request = new Request($method, self::SERVER_URI . $url);
$userAgent = 'aws-sdk-php/' . Sdk::VERSION;
if (defined('HHVM_VERSION')) {
$userAgent .= ' HHVM/' . HHVM_VERSION;
}
$userAgent .= ' ' . \Aws\default_user_agent();
$request = $request->withHeader('User-Agent', $userAgent);
foreach ($headers as $key => $value) {
$request = $request->withHeader($key, $value);
}
return $fn($request, ['timeout' => $this->timeout])
->then(function (ResponseInterface $response) {
return (string) $response->getBody();
})->otherwise(function (array $reason) {
$reason = $reason['exception'];
if ($reason instanceof TransferException) {
throw $reason;
}
$msg = $reason->getMessage();
throw new CredentialsException(
$this->createErrorMessage($msg)
);
});
}
private function handleRetryableException(
\Exception $e,
$retryOptions,
$message
) {
$isRetryable = true;
if (!empty($status = $this->getExceptionStatusCode($e))
&& isset($retryOptions['blacklist'])
&& in_array($status, $retryOptions['blacklist'])
) {
$isRetryable = false;
}
if ($isRetryable && $this->attempts < $this->retries) {
sleep(pow(1.2, $this->attempts));
} else {
throw new CredentialsException($message);
}
}
private function getExceptionStatusCode(\Exception $e)
{
if (method_exists($e, 'getResponse')
&& !empty($e->getResponse())
) {
return $e->getResponse()->getStatusCode();
}
return null;
}
private function createErrorMessage($previous)
{
return "Error retrieving credentials from the instance profile "
. "metadata service. ({$previous})";
}
private function decodeResult($response)
{
$result = json_decode($response, true);
if (json_last_error() > 0) {
throw new InvalidJsonException();
}
if ($result['Code'] !== 'Success') {
throw new CredentialsException('Unexpected instance profile '
. 'response code: ' . $result['Code']);
}
return $result;
}
}
vendor/aws/aws-sdk-php/src/Credentials/EcsCredentialProvider.php 0000644 00000005730 14760036270 0020760 0 ustar 00 timeout = (float) $timeout;
$this->client = isset($config['client'])
? $config['client']
: \Aws\default_http_handler();
}
/**
* Load ECS credentials
*
* @return PromiseInterface
*/
public function __invoke()
{
$client = $this->client;
$request = new Request('GET', self::getEcsUri());
return $client(
$request,
[
'timeout' => $this->timeout,
'proxy' => '',
]
)->then(function (ResponseInterface $response) {
$result = $this->decodeResult((string) $response->getBody());
return new Credentials(
$result['AccessKeyId'],
$result['SecretAccessKey'],
$result['Token'],
strtotime($result['Expiration'])
);
})->otherwise(function ($reason) {
$reason = is_array($reason) ? $reason['exception'] : $reason;
$msg = $reason->getMessage();
throw new CredentialsException(
"Error retrieving credential from ECS ($msg)"
);
});
}
/**
* Fetch credential URI from ECS environment variable
*
* @return string Returns ECS URI
*/
private function getEcsUri()
{
$credsUri = getenv(self::ENV_URI);
if ($credsUri === false) {
$credsUri = isset($_SERVER[self::ENV_URI]) ? $_SERVER[self::ENV_URI] : '';
}
return self::SERVER_URI . $credsUri;
}
private function decodeResult($response)
{
$result = json_decode($response, true);
if (!isset($result['AccessKeyId'])) {
throw new CredentialsException('Unexpected ECS credential value');
}
return $result;
}
}
vendor/aws/aws-sdk-php/src/Credentials/CredentialsInterface.php 0000644 00000002162 14760036270 0020612 0 ustar 00 key = trim($key);
$this->secret = trim($secret);
$this->token = $token;
$this->expires = $expires;
}
public static function __set_state(array $state)
{
return new self(
$state['key'],
$state['secret'],
$state['token'],
$state['expires']
);
}
public function getAccessKeyId()
{
return $this->key;
}
public function getSecretKey()
{
return $this->secret;
}
public function getSecurityToken()
{
return $this->token;
}
public function getExpiration()
{
return $this->expires;
}
public function isExpired()
{
return $this->expires !== null && time() >= $this->expires;
}
public function toArray()
{
return [
'key' => $this->key,
'secret' => $this->secret,
'token' => $this->token,
'expires' => $this->expires
];
}
public function serialize()
{
return json_encode($this->toArray());
}
public function unserialize($serialized)
{
$data = json_decode($serialized, true);
$this->key = $data['key'];
$this->secret = $data['secret'];
$this->token = $data['token'];
$this->expires = $data['expires'];
}
}
vendor/aws/aws-sdk-php/src/Credentials/CredentialProvider.php 0000644 00000101241 14760036270 0020317 0 ustar 00
* use Aws\Credentials\CredentialProvider;
* $provider = CredentialProvider::defaultProvider();
* // Returns a CredentialsInterface or throws.
* $creds = $provider()->wait();
*
*
* Credential providers can be composed to create credentials using conditional
* logic that can create different credentials in different environments. You
* can compose multiple providers into a single provider using
* {@see Aws\Credentials\CredentialProvider::chain}. This function accepts
* providers as variadic arguments and returns a new function that will invoke
* each provider until a successful set of credentials is returned.
*
*
* // First try an INI file at this location.
* $a = CredentialProvider::ini(null, '/path/to/file.ini');
* // Then try an INI file at this location.
* $b = CredentialProvider::ini(null, '/path/to/other-file.ini');
* // Then try loading from environment variables.
* $c = CredentialProvider::env();
* // Combine the three providers together.
* $composed = CredentialProvider::chain($a, $b, $c);
* // Returns a promise that is fulfilled with credentials or throws.
* $promise = $composed();
* // Wait on the credentials to resolve.
* $creds = $promise->wait();
*
*/
class CredentialProvider
{
const ENV_ARN = 'AWS_ROLE_ARN';
const ENV_KEY = 'AWS_ACCESS_KEY_ID';
const ENV_PROFILE = 'AWS_PROFILE';
const ENV_ROLE_SESSION_NAME = 'AWS_ROLE_SESSION_NAME';
const ENV_SECRET = 'AWS_SECRET_ACCESS_KEY';
const ENV_SESSION = 'AWS_SESSION_TOKEN';
const ENV_TOKEN_FILE = 'AWS_WEB_IDENTITY_TOKEN_FILE';
const ENV_SHARED_CREDENTIALS_FILE = 'AWS_SHARED_CREDENTIALS_FILE';
/**
* Create a default credential provider that
* first checks for environment variables,
* then checks for assumed role via web identity,
* then checks for cached SSO credentials from the CLI,
* then check for credential_process in the "default" profile in ~/.aws/credentials,
* then checks for the "default" profile in ~/.aws/credentials,
* then for credential_process in the "default profile" profile in ~/.aws/config,
* then checks for "profile default" profile in ~/.aws/config (which is
* the default profile of AWS CLI),
* then tries to make a GET Request to fetch credentials if ECS environment variable is presented,
* finally checks for EC2 instance profile credentials.
*
* This provider is automatically wrapped in a memoize function that caches
* previously provided credentials.
*
* @param array $config Optional array of ecs/instance profile credentials
* provider options.
*
* @return callable
*/
public static function defaultProvider(array $config = [])
{
$cacheable = [
'web_identity',
'sso',
'process_credentials',
'process_config',
'ecs',
'instance'
];
$defaultChain = [
'env' => self::env(),
'web_identity' => self::assumeRoleWithWebIdentityCredentialProvider($config),
];
if (
!isset($config['use_aws_shared_config_files'])
|| $config['use_aws_shared_config_files'] !== false
) {
$defaultChain['sso'] = self::sso(
'profile default',
self::getHomeDir() . '/.aws/config',
$config
);
$defaultChain['process_credentials'] = self::process();
$defaultChain['ini'] = self::ini();
$defaultChain['process_config'] = self::process(
'profile default',
self::getHomeDir() . '/.aws/config'
);
$defaultChain['ini_config'] = self::ini(
'profile default',
self::getHomeDir() . '/.aws/config'
);
}
$shouldUseEcsCredentialsProvider = getenv(EcsCredentialProvider::ENV_URI);
// getenv() is not thread safe - fall back to $_SERVER
if ($shouldUseEcsCredentialsProvider === false) {
$shouldUseEcsCredentialsProvider = isset($_SERVER[EcsCredentialProvider::ENV_URI])
? $_SERVER[EcsCredentialProvider::ENV_URI]
: false;
}
if (!empty($shouldUseEcsCredentialsProvider)) {
$defaultChain['ecs'] = self::ecsCredentials($config);
} else {
$defaultChain['instance'] = self::instanceProfile($config);
}
if (isset($config['credentials'])
&& $config['credentials'] instanceof CacheInterface
) {
foreach ($cacheable as $provider) {
if (isset($defaultChain[$provider])) {
$defaultChain[$provider] = self::cache(
$defaultChain[$provider],
$config['credentials'],
'aws_cached_' . $provider . '_credentials'
);
}
}
}
return self::memoize(
call_user_func_array(
'self::chain',
array_values($defaultChain)
)
);
}
/**
* Create a credential provider function from a set of static credentials.
*
* @param CredentialsInterface $creds
*
* @return callable
*/
public static function fromCredentials(CredentialsInterface $creds)
{
$promise = Promise\promise_for($creds);
return function () use ($promise) {
return $promise;
};
}
/**
* Creates an aggregate credentials provider that invokes the provided
* variadic providers one after the other until a provider returns
* credentials.
*
* @return callable
*/
public static function chain()
{
$links = func_get_args();
if (empty($links)) {
throw new \InvalidArgumentException('No providers in chain');
}
return function () use ($links) {
/** @var callable $parent */
$parent = array_shift($links);
$promise = $parent();
while ($next = array_shift($links)) {
$promise = $promise->otherwise($next);
}
return $promise;
};
}
/**
* Wraps a credential provider and caches previously provided credentials.
*
* Ensures that cached credentials are refreshed when they expire.
*
* @param callable $provider Credentials provider function to wrap.
*
* @return callable
*/
public static function memoize(callable $provider)
{
return function () use ($provider) {
static $result;
static $isConstant;
// Constant credentials will be returned constantly.
if ($isConstant) {
return $result;
}
// Create the initial promise that will be used as the cached value
// until it expires.
if (null === $result) {
$result = $provider();
}
// Return credentials that could expire and refresh when needed.
return $result
->then(function (CredentialsInterface $creds) use ($provider, &$isConstant, &$result) {
// Determine if these are constant credentials.
if (!$creds->getExpiration()) {
$isConstant = true;
return $creds;
}
// Refresh expired credentials.
if (!$creds->isExpired()) {
return $creds;
}
// Refresh the result and forward the promise.
return $result = $provider();
})
->otherwise(function($reason) use (&$result) {
// Cleanup rejected promise.
$result = null;
return new Promise\RejectedPromise($reason);
});
};
}
/**
* Wraps a credential provider and saves provided credentials in an
* instance of Aws\CacheInterface. Forwards calls when no credentials found
* in cache and updates cache with the results.
*
* @param callable $provider Credentials provider function to wrap
* @param CacheInterface $cache Cache to store credentials
* @param string|null $cacheKey (optional) Cache key to use
*
* @return callable
*/
public static function cache(
callable $provider,
CacheInterface $cache,
$cacheKey = null
) {
$cacheKey = $cacheKey ?: 'aws_cached_credentials';
return function () use ($provider, $cache, $cacheKey) {
$found = $cache->get($cacheKey);
if ($found instanceof CredentialsInterface && !$found->isExpired()) {
return Promise\promise_for($found);
}
return $provider()
->then(function (CredentialsInterface $creds) use (
$cache,
$cacheKey
) {
$cache->set(
$cacheKey,
$creds,
null === $creds->getExpiration() ?
0 : $creds->getExpiration() - time()
);
return $creds;
});
};
}
/**
* Provider that creates credentials from environment variables
* AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN.
*
* @return callable
*/
public static function env()
{
return function () {
// Use credentials from environment variables, if available
$key = getenv(self::ENV_KEY);
$secret = getenv(self::ENV_SECRET);
if ($key && $secret) {
return Promise\promise_for(
new Credentials($key, $secret, getenv(self::ENV_SESSION) ?: NULL)
);
}
return self::reject('Could not find environment variable '
. 'credentials in ' . self::ENV_KEY . '/' . self::ENV_SECRET);
};
}
/**
* Credential provider that creates credentials using instance profile
* credentials.
*
* @param array $config Array of configuration data.
*
* @return InstanceProfileProvider
* @see Aws\Credentials\InstanceProfileProvider for $config details.
*/
public static function instanceProfile(array $config = [])
{
return new InstanceProfileProvider($config);
}
/**
* Credential provider that retrieves cached SSO credentials from the CLI
*
* @return callable
*/
public static function sso($ssoProfileName, $filename = null, $config = [])
{
$filename = $filename ?: (self::getHomeDir() . '/.aws/config');
return function () use ($ssoProfileName, $filename, $config) {
if (!is_readable($filename)) {
return self::reject("Cannot read credentials from $filename");
}
$profiles = self::loadProfiles($filename);
if (!isset($profiles[$ssoProfileName])) {
return self::reject("Profile {$ssoProfileName} does not exist in {$filename}.");
}
$ssoProfile = $profiles[$ssoProfileName];
if (empty($ssoProfile['sso_start_url'])
|| empty($ssoProfile['sso_region'])
|| empty($ssoProfile['sso_account_id'])
|| empty($ssoProfile['sso_role_name'])
) {
return self::reject(
"Profile {$ssoProfileName} in {$filename} must contain the following keys: "
. "sso_start_url, sso_region, sso_account_id, and sso_role_name."
);
}
$tokenLocation = self::getHomeDir()
. '/.aws/sso/cache/'
. utf8_encode(sha1($ssoProfile['sso_start_url']))
. ".json";
if (!is_readable($tokenLocation)) {
return self::reject("Unable to read token file at $tokenLocation");
}
$tokenData = json_decode(file_get_contents($tokenLocation), true);
if (empty($tokenData['accessToken']) || empty($tokenData['expiresAt'])) {
return self::reject(
"Token file at {$tokenLocation} must contain an access token and an expiration"
);
}
try {
$expiration = (new DateTimeResult($tokenData['expiresAt']))->getTimestamp();
} catch (\Exception $e) {
return self::reject("Cached SSO credentials returned an invalid expiration");
}
$now = time();
if ($expiration < $now) {
return self::reject("Cached SSO credentials returned expired credentials");
}
$ssoClient = null;
if (empty($config['ssoClient'])) {
$ssoClient = new Aws\SSO\SSOClient([
'region' => $ssoProfile['sso_region'],
'version' => '2019-06-10',
'credentials' => false
]);
} else {
$ssoClient = $config['ssoClient'];
}
$ssoResponse = $ssoClient->getRoleCredentials([
'accessToken' => $tokenData['accessToken'],
'accountId' => $ssoProfile['sso_account_id'],
'roleName' => $ssoProfile['sso_role_name']
]);
$ssoCredentials = $ssoResponse['roleCredentials'];
return Promise\promise_for(
new Credentials(
$ssoCredentials['accessKeyId'],
$ssoCredentials['secretAccessKey'],
$ssoCredentials['sessionToken'],
$expiration
)
);
};
}
/**
* Credential provider that creates credentials using
* ecs credentials by a GET request, whose uri is specified
* by environment variable
*
* @param array $config Array of configuration data.
*
* @return EcsCredentialProvider
* @see Aws\Credentials\EcsCredentialProvider for $config details.
*/
public static function ecsCredentials(array $config = [])
{
return new EcsCredentialProvider($config);
}
/**
* Credential provider that creates credentials using assume role
*
* @param array $config Array of configuration data
* @return callable
* @see Aws\Credentials\AssumeRoleCredentialProvider for $config details.
*/
public static function assumeRole(array $config=[])
{
return new AssumeRoleCredentialProvider($config);
}
/**
* Credential provider that creates credentials by assuming role from a
* Web Identity Token
*
* @param array $config Array of configuration data
* @return callable
* @see Aws\Credentials\AssumeRoleWithWebIdentityCredentialProvider for
* $config details.
*/
public static function assumeRoleWithWebIdentityCredentialProvider(array $config = [])
{
return function () use ($config) {
$arnFromEnv = getenv(self::ENV_ARN);
$tokenFromEnv = getenv(self::ENV_TOKEN_FILE);
$stsClient = isset($config['stsClient'])
? $config['stsClient']
: null;
$region = isset($config['region'])
? $config['region']
: null;
if ($tokenFromEnv && $arnFromEnv) {
$sessionName = getenv(self::ENV_ROLE_SESSION_NAME)
? getenv(self::ENV_ROLE_SESSION_NAME)
: null;
$provider = new AssumeRoleWithWebIdentityCredentialProvider([
'RoleArn' => $arnFromEnv,
'WebIdentityTokenFile' => $tokenFromEnv,
'SessionName' => $sessionName,
'client' => $stsClient,
'region' => $region
]);
return $provider();
}
$profileName = getenv(self::ENV_PROFILE) ?: 'default';
if (isset($config['filename'])) {
$profiles = self::loadProfiles($config['filename']);
} else {
$profiles = self::loadDefaultProfiles();
}
if (isset($profiles[$profileName])) {
$profile = $profiles[$profileName];
if (isset($profile['region'])) {
$region = $profile['region'];
}
if (isset($profile['web_identity_token_file'])
&& isset($profile['role_arn'])
) {
$sessionName = isset($profile['role_session_name'])
? $profile['role_session_name']
: null;
$provider = new AssumeRoleWithWebIdentityCredentialProvider([
'RoleArn' => $profile['role_arn'],
'WebIdentityTokenFile' => $profile['web_identity_token_file'],
'SessionName' => $sessionName,
'client' => $stsClient,
'region' => $region
]);
return $provider();
}
} else {
return self::reject("Unknown profile: $profileName");
}
return self::reject("No RoleArn or WebIdentityTokenFile specified");
};
}
/**
* Credentials provider that creates credentials using an ini file stored
* in the current user's home directory. A source can be provided
* in this file for assuming a role using the credential_source config option.
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile in "~/.aws/credentials".
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the home directory.
* @param array|null $config If provided, may contain the following:
* preferStaticCredentials: If true, prefer static
* credentials to role_arn if both are present
* disableAssumeRole: If true, disable support for
* roles that assume an IAM role. If true and role profile
* is selected, an error is raised.
* stsClient: StsClient used to assume role specified in profile
*
* @return callable
*/
public static function ini($profile = null, $filename = null, array $config = [])
{
$filename = self::getFileName($filename);
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
return function () use ($profile, $filename, $config) {
$preferStaticCredentials = isset($config['preferStaticCredentials'])
? $config['preferStaticCredentials']
: false;
$disableAssumeRole = isset($config['disableAssumeRole'])
? $config['disableAssumeRole']
: false;
$stsClient = isset($config['stsClient']) ? $config['stsClient'] : null;
if (!is_readable($filename)) {
return self::reject("Cannot read credentials from $filename");
}
$data = self::loadProfiles($filename);
if ($data === false) {
return self::reject("Invalid credentials file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in credentials file");
}
/*
In the CLI, the presence of both a role_arn and static credentials have
different meanings depending on how many profiles have been visited. For
the first profile processed, role_arn takes precedence over any static
credentials, but for all subsequent profiles, static credentials are
used if present, and only in their absence will the profile's
source_profile and role_arn keys be used to load another set of
credentials. This bool is intended to yield compatible behaviour in this
sdk.
*/
$preferStaticCredentialsToRoleArn = ($preferStaticCredentials
&& isset($data[$profile]['aws_access_key_id'])
&& isset($data[$profile]['aws_secret_access_key']));
if (isset($data[$profile]['role_arn'])
&& !$preferStaticCredentialsToRoleArn
) {
if ($disableAssumeRole) {
return self::reject(
"Role assumption profiles are disabled. "
. "Failed to load profile " . $profile);
}
return self::loadRoleProfile(
$data,
$profile,
$filename,
$stsClient,
$config
);
}
if (!isset($data[$profile]['aws_access_key_id'])
|| !isset($data[$profile]['aws_secret_access_key'])
) {
return self::reject("No credentials present in INI profile "
. "'$profile' ($filename)");
}
if (empty($data[$profile]['aws_session_token'])) {
$data[$profile]['aws_session_token']
= isset($data[$profile]['aws_security_token'])
? $data[$profile]['aws_security_token']
: null;
}
return Promise\promise_for(
new Credentials(
$data[$profile]['aws_access_key_id'],
$data[$profile]['aws_secret_access_key'],
$data[$profile]['aws_session_token']
)
);
};
}
/**
* Credentials provider that creates credentials using a process configured in
* ini file stored in the current user's home directory.
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile in "~/.aws/credentials".
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the home directory.
*
* @return callable
*/
public static function process($profile = null, $filename = null)
{
$filename = self::getFileName($filename);
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
return function () use ($profile, $filename) {
if (!is_readable($filename)) {
return self::reject("Cannot read process credentials from $filename");
}
$data = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW);
if ($data === false) {
return self::reject("Invalid credentials file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in credentials file");
}
if (!isset($data[$profile]['credential_process'])) {
return self::reject("No credential_process present in INI profile "
. "'$profile' ($filename)");
}
$credentialProcess = $data[$profile]['credential_process'];
$json = shell_exec($credentialProcess);
$processData = json_decode($json, true);
// Only support version 1
if (isset($processData['Version'])) {
if ($processData['Version'] !== 1) {
return self::reject("credential_process does not return Version == 1");
}
}
if (!isset($processData['AccessKeyId'])
|| !isset($processData['SecretAccessKey']))
{
return self::reject("credential_process does not return valid credentials");
}
if (isset($processData['Expiration'])) {
try {
$expiration = new DateTimeResult($processData['Expiration']);
} catch (\Exception $e) {
return self::reject("credential_process returned invalid expiration");
}
$now = new DateTimeResult();
if ($expiration < $now) {
return self::reject("credential_process returned expired credentials");
}
$expires = $expiration->getTimestamp();
} else {
$expires = null;
}
if (empty($processData['SessionToken'])) {
$processData['SessionToken'] = null;
}
return Promise\promise_for(
new Credentials(
$processData['AccessKeyId'],
$processData['SecretAccessKey'],
$processData['SessionToken'],
$expires
)
);
};
}
/**
* Assumes role for profile that includes role_arn
*
* @return callable
*/
private static function loadRoleProfile(
$profiles,
$profileName,
$filename,
$stsClient,
$config = []
) {
$roleProfile = $profiles[$profileName];
$roleArn = isset($roleProfile['role_arn']) ? $roleProfile['role_arn'] : '';
$roleSessionName = isset($roleProfile['role_session_name'])
? $roleProfile['role_session_name']
: 'aws-sdk-php-' . round(microtime(true) * 1000);
if (
empty($roleProfile['source_profile'])
== empty($roleProfile['credential_source'])
) {
return self::reject("Either source_profile or credential_source must be set " .
"using profile " . $profileName . ", but not both."
);
}
$sourceProfileName = "";
if (!empty($roleProfile['source_profile'])) {
$sourceProfileName = $roleProfile['source_profile'];
if (!isset($profiles[$sourceProfileName])) {
return self::reject("source_profile " . $sourceProfileName
. " using profile " . $profileName . " does not exist"
);
}
if (isset($config['visited_profiles']) &&
in_array($roleProfile['source_profile'], $config['visited_profiles'])
) {
return self::reject("Circular source_profile reference found.");
}
$config['visited_profiles'] [] = $roleProfile['source_profile'];
} else {
if (empty($roleArn)) {
return self::reject(
"A role_arn must be provided with credential_source in " .
"file {$filename} under profile {$profileName} "
);
}
}
if (empty($stsClient)) {
$sourceRegion = isset($profiles[$sourceProfileName]['region'])
? $profiles[$sourceProfileName]['region']
: 'us-east-1';
$config['preferStaticCredentials'] = true;
$sourceCredentials = null;
if (!empty($roleProfile['source_profile'])){
$sourceCredentials = call_user_func(
CredentialProvider::ini($sourceProfileName, $filename, $config)
)->wait();
} else {
$sourceCredentials = self::getCredentialsFromSource(
$profileName,
$filename
);
}
$stsClient = new StsClient([
'credentials' => $sourceCredentials,
'region' => $sourceRegion,
'version' => '2011-06-15',
]);
}
$result = $stsClient->assumeRole([
'RoleArn' => $roleArn,
'RoleSessionName' => $roleSessionName
]);
$credentials = $stsClient->createCredentials($result);
return Promise\promise_for($credentials);
}
/**
* Gets the environment's HOME directory if available.
*
* @return null|string
*/
private static function getHomeDir()
{
// On Linux/Unix-like systems, use the HOME environment variable
if ($homeDir = getenv('HOME')) {
return $homeDir;
}
// Get the HOMEDRIVE and HOMEPATH values for Windows hosts
$homeDrive = getenv('HOMEDRIVE');
$homePath = getenv('HOMEPATH');
return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
}
/**
* Gets profiles from specified $filename, or default ini files.
*/
private static function loadProfiles($filename)
{
$profileData = \Aws\parse_ini_file($filename, true, INI_SCANNER_RAW);
// If loading .aws/credentials, also load .aws/config when AWS_SDK_LOAD_NONDEFAULT_CONFIG is set
if ($filename === self::getHomeDir() . '/.aws/credentials'
&& getenv('AWS_SDK_LOAD_NONDEFAULT_CONFIG')
) {
$configFilename = self::getHomeDir() . '/.aws/config';
$configProfileData = \Aws\parse_ini_file($configFilename, true, INI_SCANNER_RAW);
foreach ($configProfileData as $name => $profile) {
// standardize config profile names
$name = str_replace('profile ', '', $name);
if (!isset($profileData[$name])) {
$profileData[$name] = $profile;
}
}
}
return $profileData;
}
/**
* Gets profiles from ~/.aws/credentials and ~/.aws/config ini files
*/
private static function loadDefaultProfiles() {
$profiles = [];
$credFile = self::getHomeDir() . '/.aws/credentials';
$configFile = self::getHomeDir() . '/.aws/config';
if (file_exists($credFile)) {
$profiles = \Aws\parse_ini_file($credFile, true, INI_SCANNER_RAW);
}
if (file_exists($configFile)) {
$configProfileData = \Aws\parse_ini_file($configFile, true, INI_SCANNER_RAW);
foreach ($configProfileData as $name => $profile) {
// standardize config profile names
$name = str_replace('profile ', '', $name);
if (!isset($profiles[$name])) {
$profiles[$name] = $profile;
}
}
}
return $profiles;
}
public static function getCredentialsFromSource(
$profileName = '',
$filename = '',
$config = []
) {
$data = self::loadProfiles($filename);
$credentialSource = !empty($data[$profileName]['credential_source'])
? $data[$profileName]['credential_source']
: null;
$credentialsPromise = null;
switch ($credentialSource) {
case 'Environment':
$credentialsPromise = self::env();
break;
case 'Ec2InstanceMetadata':
$credentialsPromise = self::instanceProfile($config);
break;
case 'EcsContainer':
$credentialsPromise = self::ecsCredentials($config);
break;
default:
throw new CredentialsException(
"Invalid credential_source found in config file: {$credentialSource}. Valid inputs "
. "include Environment, Ec2InstanceMetadata, and EcsContainer."
);
}
$credentialsResult = null;
try {
$credentialsResult = $credentialsPromise()->wait();
} catch (\Exception $reason) {
return self::reject(
"Unable to successfully retrieve credentials from the source specified in the"
. " credentials file: {$credentialSource}; failure message was: "
. $reason->getMessage()
);
}
return function () use ($credentialsResult) {
return Promise\promise_for($credentialsResult);
};
}
private static function reject($msg)
{
return new Promise\RejectedPromise(new CredentialsException($msg));
}
/**
* @param $filename
* @return string
*/
private static function getFileName($filename)
{
if (!isset($filename)) {
$filename = getenv(self::ENV_SHARED_CREDENTIALS_FILE) ?:
(self::getHomeDir() . '/.aws/credentials');
}
return $filename;
}
}
vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleWithWebIdentityCredentialProvider.php 0000644 00000014050 14760036270 0025324 0 ustar 00 arn = $config['RoleArn'];
if (!isset($config['WebIdentityTokenFile'])) {
throw new \InvalidArgumentException(self::ERROR_MSG . "'WebIdentityTokenFile'.");
}
$this->tokenFile = $config['WebIdentityTokenFile'];
if (!preg_match("/^\w\:|^\/|^\\\/", $this->tokenFile)) {
throw new \InvalidArgumentException("'WebIdentityTokenFile' must be an absolute path.");
}
$this->retries = (int) getenv(self::ENV_RETRIES) ?: (isset($config['retries']) ? $config['retries'] : 3);
$this->authenticationAttempts = 0;
$this->tokenFileReadAttempts = 0;
$this->session = isset($config['SessionName'])
? $config['SessionName']
: 'aws-sdk-php-' . round(microtime(true) * 1000);
$region = isset($config['region'])
? $config['region']
: 'us-east-1';
if (isset($config['client'])) {
$this->client = $config['client'];
} else {
$this->client = new StsClient([
'credentials' => false,
'region' => $region,
'version' => 'latest'
]);
}
}
/**
* Loads assume role with web identity credentials.
*
* @return Promise\PromiseInterface
*/
public function __invoke()
{
return Promise\coroutine(function () {
$client = $this->client;
$result = null;
while ($result == null) {
try {
$token = is_readable($this->tokenFile)
? file_get_contents($this->tokenFile)
: false;
if (false === $token) {
clearstatcache(true, dirname($this->tokenFile) . "/" . readlink($this->tokenFile));
clearstatcache(true, dirname($this->tokenFile) . "/" . dirname(readlink($this->tokenFile)));
clearstatcache(true, $this->tokenFile);
if (!is_readable($this->tokenFile)) {
throw new CredentialsException(
"Unreadable tokenfile at location {$this->tokenFile}"
);
}
$token = file_get_contents($this->tokenFile);
}
if (empty($token)) {
if ($this->tokenFileReadAttempts < $this->retries) {
sleep(pow(1.2, $this->tokenFileReadAttempts));
$this->tokenFileReadAttempts++;
continue;
}
throw new CredentialsException("InvalidIdentityToken from file: {$this->tokenFile}");
}
} catch (\Exception $exception) {
throw new CredentialsException(
"Error reading WebIdentityTokenFile from " . $this->tokenFile,
0,
$exception
);
}
$assumeParams = [
'RoleArn' => $this->arn,
'RoleSessionName' => $this->session,
'WebIdentityToken' => $token
];
try {
$result = $client->assumeRoleWithWebIdentity($assumeParams);
} catch (AwsException $e) {
if ($e->getAwsErrorCode() == 'InvalidIdentityToken') {
if ($this->authenticationAttempts < $this->retries) {
sleep(pow(1.2, $this->authenticationAttempts));
} else {
throw new CredentialsException(
"InvalidIdentityToken, retries exhausted"
);
}
} else {
throw new CredentialsException(
"Error assuming role from web identity credentials",
0,
$e
);
}
} catch (\Exception $e) {
throw new CredentialsException(
"Error retrieving web identity credentials: " . $e->getMessage()
. " (" . $e->getCode() . ")"
);
}
$this->authenticationAttempts++;
}
yield $this->client->createCredentials($result);
});
}
}
vendor/aws/aws-sdk-php/src/Credentials/AssumeRoleCredentialProvider.php 0000644 00000003620 14760036270 0022321 0 ustar 00 client = $config['client'];
$this->assumeRoleParams = $config['assume_role_params'];
}
/**
* Loads assume role credentials.
*
* @return PromiseInterface
*/
public function __invoke()
{
$client = $this->client;
return $client->assumeRoleAsync($this->assumeRoleParams)
->then(function (Result $result) {
return $this->client->createCredentials($result);
})->otherwise(function (\RuntimeException $exception) {
throw new CredentialsException(
"Error in retrieving assume role credentials.",
0,
$exception
);
});
}
}
vendor/aws/aws-sdk-php/src/Endpoint/PatternEndpointProvider.php 0000644 00000002511 14760036270 0020706 0 ustar 00 patterns = $patterns;
}
public function __invoke(array $args = [])
{
$service = isset($args['service']) ? $args['service'] : '';
$region = isset($args['region']) ? $args['region'] : '';
$keys = ["{$region}/{$service}", "{$region}/*", "*/{$service}", "*/*"];
foreach ($keys as $key) {
if (isset($this->patterns[$key])) {
return $this->expand(
$this->patterns[$key],
isset($args['scheme']) ? $args['scheme'] : 'https',
$service,
$region
);
}
}
return null;
}
private function expand(array $config, $scheme, $service, $region)
{
$config['endpoint'] = $scheme . '://'
. strtr($config['endpoint'], [
'{service}' => $service,
'{region}' => $region
]);
return $config;
}
}
vendor/aws/aws-sdk-php/src/Endpoint/PartitionInterface.php 0000644 00000003152 14760036270 0017651 0 ustar 00 partitions = array_map(function (array $definition) {
return new Partition($definition);
}, array_values($partitions));
$this->defaultPartition = $defaultPartition;
$this->options = $options;
}
public function __invoke(array $args = [])
{
$partition = $this->getPartition(
isset($args['region']) ? $args['region'] : '',
isset($args['service']) ? $args['service'] : ''
);
$args['options'] = $this->options;
return $partition($args);
}
/**
* Returns the partition containing the provided region or the default
* partition if no match is found.
*
* @param string $region
* @param string $service
*
* @return Partition
*/
public function getPartition($region, $service)
{
foreach ($this->partitions as $partition) {
if ($partition->isRegionMatch($region, $service)) {
return $partition;
}
}
return $this->getPartitionByName($this->defaultPartition);
}
/**
* Returns the partition with the provided name or null if no partition with
* the provided name can be found.
*
* @param string $name
*
* @return Partition|null
*/
public function getPartitionByName($name)
{
foreach ($this->partitions as $partition) {
if ($name === $partition->getName()) {
return $partition;
}
}
}
/**
* Creates and returns the default SDK partition provider.
*
* @param array $options
* @return PartitionEndpointProvider
*/
public static function defaultProvider($options = [])
{
$data = \Aws\load_compiled_json(__DIR__ . '/../data/endpoints.json');
$prefixData = \Aws\load_compiled_json(__DIR__ . '/../data/endpoints_prefix_history.json');
$mergedData = self::mergePrefixData($data, $prefixData);
return new self($mergedData['partitions'], 'aws', $options);
}
/**
* Copy endpoint data for other prefixes used by a given service
*
* @param $data
* @param $prefixData
* @return array
*/
public static function mergePrefixData($data, $prefixData)
{
$prefixGroups = $prefixData['prefix-groups'];
foreach ($data["partitions"] as $index => $partition) {
foreach ($prefixGroups as $current => $old) {
$serviceData = Env::search("services.\"{$current}\"", $partition);
if (!empty($serviceData)) {
foreach ($old as $prefix) {
if (empty(Env::search("services.\"{$prefix}\"", $partition))) {
$data["partitions"][$index]["services"][$prefix] = $serviceData;
}
}
}
}
}
return $data;
}
}
vendor/aws/aws-sdk-php/src/Endpoint/Partition.php 0000644 00000020514 14760036270 0016031 0 ustar 00 data = $definition;
}
public function getName()
{
return $this->data['partition'];
}
/**
* @internal
* @return mixed
*/
public function getDnsSuffix()
{
return $this->data['dnsSuffix'];
}
public function isRegionMatch($region, $service)
{
if (isset($this->data['regions'][$region])
|| isset($this->data['services'][$service]['endpoints'][$region])
) {
return true;
}
if (isset($this->data['regionRegex'])) {
return (bool) preg_match(
"@{$this->data['regionRegex']}@",
$region
);
}
return false;
}
public function getAvailableEndpoints(
$service,
$allowNonRegionalEndpoints = false
) {
if ($this->isServicePartitionGlobal($service)) {
return [$this->getPartitionEndpoint($service)];
}
if (isset($this->data['services'][$service]['endpoints'])) {
$serviceRegions = array_keys(
$this->data['services'][$service]['endpoints']
);
return $allowNonRegionalEndpoints
? $serviceRegions
: array_intersect($serviceRegions, array_keys(
$this->data['regions']
));
}
return [];
}
public function __invoke(array $args = [])
{
$service = isset($args['service']) ? $args['service'] : '';
$region = isset($args['region']) ? $args['region'] : '';
$scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
$options = isset($args['options']) ? $args['options'] : [];
$data = $this->getEndpointData($service, $region, $options);
return [
'endpoint' => "{$scheme}://" . $this->formatEndpoint(
isset($data['hostname']) ? $data['hostname'] : '',
$service,
$region
),
'signatureVersion' => $this->getSignatureVersion($data),
'signingRegion' => isset($data['credentialScope']['region'])
? $data['credentialScope']['region']
: $region,
'signingName' => isset($data['credentialScope']['service'])
? $data['credentialScope']['service']
: $service,
];
}
private function getEndpointData($service, $region, $options)
{
$defaultRegion = $this->resolveRegion($service, $region, $options);
$data = isset($this->data['services'][$service]['endpoints'][$defaultRegion])
? $this->data['services'][$service]['endpoints'][$defaultRegion]
: [];
$data += isset($this->data['services'][$service]['defaults'])
? $this->data['services'][$service]['defaults']
: [];
$data += isset($this->data['defaults'])
? $this->data['defaults']
: [];
return $data;
}
private function getSignatureVersion(array $data)
{
static $supportedBySdk = [
's3v4',
'v4',
'anonymous',
];
$possibilities = array_intersect(
$supportedBySdk,
isset($data['signatureVersions'])
? $data['signatureVersions']
: ['v4']
);
return array_shift($possibilities);
}
private function resolveRegion($service, $region, $options)
{
if (isset($this->data['services'][$service]['endpoints'][$region])
&& $this->isFipsEndpointUsed($region)
) {
return $region;
}
if ($this->isServicePartitionGlobal($service)
|| $this->isStsLegacyEndpointUsed($service, $region, $options)
|| $this->isS3LegacyEndpointUsed($service, $region, $options)
) {
return $this->getPartitionEndpoint($service);
}
return $region;
}
private function isServicePartitionGlobal($service)
{
return isset($this->data['services'][$service]['isRegionalized'])
&& false === $this->data['services'][$service]['isRegionalized']
&& isset($this->data['services'][$service]['partitionEndpoint']);
}
/**
* STS legacy endpoints used for valid regions unless option is explicitly
* set to 'regional'
*
* @param string $service
* @param string $region
* @param array $options
* @return bool
*/
private function isStsLegacyEndpointUsed($service, $region, $options)
{
return $service === 'sts'
&& in_array($region, $this->stsLegacyGlobalRegions)
&& (empty($options['sts_regional_endpoints'])
|| ConfigurationProvider::unwrap(
$options['sts_regional_endpoints']
)->getEndpointsType() !== 'regional'
);
}
/**
* S3 legacy us-east-1 endpoint used for valid regions unless option is explicitly
* set to 'regional'
*
* @param string $service
* @param string $region
* @param array $options
* @return bool
*/
private function isS3LegacyEndpointUsed($service, $region, $options)
{
return $service === 's3'
&& $region === 'us-east-1'
&& (empty($options['s3_us_east_1_regional_endpoint'])
|| S3ConfigurationProvider::unwrap(
$options['s3_us_east_1_regional_endpoint']
)->getEndpointsType() !== 'regional'
);
}
private function getPartitionEndpoint($service)
{
return $this->data['services'][$service]['partitionEndpoint'];
}
private function formatEndpoint($template, $service, $region)
{
return strtr($template, [
'{service}' => $service,
'{region}' => $region,
'{dnsSuffix}' => $this->data['dnsSuffix'],
]);
}
/**
* @param $region
* @return bool
*/
private function isFipsEndpointUsed($region)
{
return strpos($region, "fips") !== false;
}
}
vendor/aws/aws-sdk-php/src/Endpoint/EndpointProvider.php 0000644 00000006361 14760036270 0017357 0 ustar 00 'ec2', 'region' => 'us-west-2']);
* // Returns an endpoint array or throws.
* $endpoint = EndpointProvider::resolve($provider, [
* 'service' => 'ec2',
* 'region' => 'us-west-2'
* ]);
*
* You can compose multiple providers into a single provider using
* {@see Aws\or_chain}. This function accepts providers as arguments and
* returns a new function that will invoke each provider until a non-null value
* is returned.
*
* $a = function (array $args) {
* if ($args['region'] === 'my-test-region') {
* return ['endpoint' => 'http://localhost:123/api'];
* }
* };
* $b = EndpointProvider::defaultProvider();
* $c = \Aws\or_chain($a, $b);
* $config = ['service' => 'ec2', 'region' => 'my-test-region'];
* $res = $c($config); // $a handles this.
* $config['region'] = 'us-west-2';
* $res = $c($config); // $b handles this.
*/
class EndpointProvider
{
/**
* Resolves and endpoint provider and ensures a non-null return value.
*
* @param callable $provider Provider function to invoke.
* @param array $args Endpoint arguments to pass to the provider.
*
* @return array
* @throws UnresolvedEndpointException
*/
public static function resolve(callable $provider, array $args = [])
{
$result = $provider($args);
if (is_array($result)) {
return $result;
}
throw new UnresolvedEndpointException(
'Unable to resolve an endpoint using the provider arguments: '
. json_encode($args) . '. Note: you can provide an "endpoint" '
. 'option to a client constructor to bypass invoking an endpoint '
. 'provider.');
}
/**
* Creates and returns the default SDK endpoint provider.
*
* @deprecated Use an instance of \Aws\Endpoint\Partition instead.
*
* @return callable
*/
public static function defaultProvider()
{
return PartitionEndpointProvider::defaultProvider();
}
/**
* Creates and returns an endpoint provider that uses patterns from an
* array.
*
* @param array $patterns Endpoint patterns
*
* @return callable
*/
public static function patterns(array $patterns)
{
return new PatternEndpointProvider($patterns);
}
}
vendor/aws/aws-sdk-php/src/EndpointDiscovery/Exception/ConfigurationException.php 0000644 00000000523 14760036270 0024372 0 ustar 00 active = $endpoints;
reset($this->active);
}
/**
* Gets an active (unexpired) endpoint. Returns null if none found.
*
* @return null|string
*/
public function getActive()
{
if (count($this->active) < 1) {
return null;
}
while (time() > current($this->active)) {
$key = key($this->active);
$this->expired[$key] = current($this->active);
$this->increment($this->active);
unset($this->active[$key]);
if (count($this->active) < 1) {
return null;
}
}
$active = key($this->active);
$this->increment($this->active);
return $active;
}
/**
* Gets an active endpoint if possible, then an expired endpoint if possible.
* Returns null if no endpoints found.
*
* @return null|string
*/
public function getEndpoint()
{
if (!empty($active = $this->getActive())) {
return $active;
}
return $this->getExpired();
}
/**
* Removes an endpoint from both lists.
*
* @param string $key
*/
public function remove($key)
{
unset($this->active[$key]);
unset($this->expired[$key]);
}
/**
* Get an expired endpoint. Returns null if none found.
*
* @return null|string
*/
private function getExpired()
{
if (count($this->expired) < 1) {
return null;
}
$expired = key($this->expired);
$this->increment($this->expired);
return $expired;
}
private function increment(&$array)
{
if (next($array) === false) {
reset($array);
}
}
}
vendor/aws/aws-sdk-php/src/EndpointDiscovery/EndpointDiscoveryMiddleware.php 0000644 00000032250 14760036270 0023416 0 ustar 00 nextHandler = $handler;
$this->client = $client;
$this->args = $args;
$this->service = $client->getApi();
$this->config = $config;
}
public function __invoke(CommandInterface $cmd, RequestInterface $request)
{
$nextHandler = $this->nextHandler;
$op = $this->service->getOperation($cmd->getName())->toArray();
// Continue only if endpointdiscovery trait is set
if (isset($op['endpointdiscovery'])) {
$config = ConfigurationProvider::unwrap($this->config);
$isRequired = !empty($op['endpointdiscovery']['required']);
if ($isRequired && !($config->isEnabled())) {
throw new UnresolvedEndpointException('This operation '
. 'requires the use of endpoint discovery, but this has '
. 'been disabled in the configuration. Enable endpoint '
. 'discovery or use a different operation.');
}
// Continue only if enabled by config
if ($config->isEnabled()) {
if (isset($op['endpointoperation'])) {
throw new UnresolvedEndpointException('This operation is '
. 'contradictorily marked both as using endpoint discovery '
. 'and being the endpoint discovery operation. Please '
. 'verify the accuracy of your model files.');
}
// Original endpoint may be used if discovery optional
$originalUri = $request->getUri();
$identifiers = $this->getIdentifiers($op);
$cacheKey = $this->getCacheKey(
$this->client->getCredentials()->wait(),
$cmd,
$identifiers
);
// Check/create cache
if (!isset(self::$cache)) {
self::$cache = new LruArrayCache($config->getCacheLimit());
}
if (empty($endpointList = self::$cache->get($cacheKey))) {
$endpointList = new EndpointList([]);
}
$endpoint = $endpointList->getActive();
// Retrieve endpoints if there is no active endpoint
if (empty($endpoint)) {
try {
$endpoint = $this->discoverEndpoint(
$cacheKey,
$cmd,
$identifiers
);
} catch (\Exception $e) {
// Use cached endpoint, expired or active, if any remain
$endpoint = $endpointList->getEndpoint();
if (empty($endpoint)) {
return $this->handleDiscoveryException(
$isRequired,
$originalUri,
$e,
$cmd,
$request
);
}
}
}
$request = $this->modifyRequest($request, $endpoint);
$g = function ($value) use (
$cacheKey,
$cmd,
$identifiers,
$isRequired,
$originalUri,
$request,
&$endpoint,
&$g
) {
if ($value instanceof AwsException
&& (
$value->getAwsErrorCode() == 'InvalidEndpointException'
|| $value->getStatusCode() == 421
)
) {
return $this->handleInvalidEndpoint(
$cacheKey,
$cmd,
$identifiers,
$isRequired,
$originalUri,
$request,
$value,
$endpoint,
$g
);
}
return $value;
};
return $nextHandler($cmd, $request)->otherwise($g);
}
}
return $nextHandler($cmd, $request);
}
private function discoverEndpoint(
$cacheKey,
CommandInterface $cmd,
array $identifiers
) {
$discCmd = $this->getDiscoveryCommand($cmd, $identifiers);
$this->discoveryTimes[$cacheKey] = time();
$result = $this->client->execute($discCmd);
if (isset($result['Endpoints'])) {
$endpointData = [];
foreach ($result['Endpoints'] as $datum) {
$endpointData[$datum['Address']] = time()
+ ($datum['CachePeriodInMinutes'] * 60);
}
$endpointList = new EndpointList($endpointData);
self::$cache->set($cacheKey, $endpointList);
return $endpointList->getEndpoint();
}
throw new UnresolvedEndpointException('The endpoint discovery operation '
. 'yielded a response that did not contain properly formatted '
. 'endpoint data.');
}
private function getCacheKey(
CredentialsInterface $creds,
CommandInterface $cmd,
array $identifiers
) {
$key = $this->service->getServiceName() . '_' . $creds->getAccessKeyId();
if (!empty($identifiers)) {
$key .= '_' . $cmd->getName();
foreach ($identifiers as $identifier) {
$key .= "_{$cmd[$identifier]}";
}
}
return $key;
}
private function getDiscoveryCommand(
CommandInterface $cmd,
array $identifiers
) {
foreach ($this->service->getOperations() as $op) {
if (isset($op['endpointoperation'])) {
$endpointOperation = $op->toArray()['name'];
break;
}
}
if (!isset($endpointOperation)) {
throw new UnresolvedEndpointException('This command is set to use '
. 'endpoint discovery, but no endpoint discovery operation was '
. 'found. Please verify the accuracy of your model files.');
}
$params = [];
if (!empty($identifiers)) {
$params['Operation'] = $cmd->getName();
$params['Identifiers'] = [];
foreach ($identifiers as $identifier) {
$params['Identifiers'][$identifier] = $cmd[$identifier];
}
}
$command = $this->client->getCommand($endpointOperation, $params);
$command->getHandlerList()->appendBuild(
Middleware::mapRequest(function (RequestInterface $r) {
return $r->withHeader(
'x-amz-api-version',
$this->service->getApiVersion()
);
}),
'x-amz-api-version-header'
);
return $command;
}
private function getIdentifiers(array $operation)
{
$inputShape = $this->service->getShapeMap()
->resolve($operation['input'])
->toArray();
$identifiers = [];
foreach ($inputShape['members'] as $key => $member) {
if (!empty($member['endpointdiscoveryid'])) {
$identifiers[] = $key;
}
}
return $identifiers;
}
private function handleDiscoveryException(
$isRequired,
$originalUri,
\Exception $e,
CommandInterface $cmd,
RequestInterface $request
) {
// If no cached endpoints and discovery required,
// throw exception
if ($isRequired) {
$message = 'The endpoint required for this service is currently '
. 'unable to be retrieved, and your request can not be fulfilled '
. 'unless you manually specify an endpoint.';
throw new AwsException(
$message,
$cmd,
[
'code' => 'EndpointDiscoveryException',
'message' => $message
],
$e
);
}
// If discovery isn't required, use original endpoint
return $this->useOriginalUri(
$originalUri,
$cmd,
$request
);
}
private function handleInvalidEndpoint(
$cacheKey,
$cmd,
$identifiers,
$isRequired,
$originalUri,
$request,
$value,
&$endpoint,
&$g
) {
$nextHandler = $this->nextHandler;
$endpointList = self::$cache->get($cacheKey);
if ($endpointList instanceof EndpointList) {
// Remove invalid endpoint from cached list
$endpointList->remove($endpoint);
// If possible, get another cached endpoint
$newEndpoint = $endpointList->getEndpoint();
}
if (empty($newEndpoint)) {
// If no more cached endpoints, make discovery call
// if none made within cooldown for given key
if (time() - $this->discoveryTimes[$cacheKey]
< self::$discoveryCooldown
) {
// If no more cached endpoints and it's required,
// fail with original exception
if ($isRequired) {
return $value;
}
// Use original endpoint if not required
return $this->useOriginalUri(
$originalUri,
$cmd,
$request
);
}
$newEndpoint = $this->discoverEndpoint(
$cacheKey,
$cmd,
$identifiers
);
}
$endpoint = $newEndpoint;
$request = $this->modifyRequest($request, $endpoint);
return $nextHandler($cmd, $request)->otherwise($g);
}
private function modifyRequest(RequestInterface $request, $endpoint)
{
$parsed = $this->parseEndpoint($endpoint);
if (!empty($request->getHeader('User-Agent'))) {
$userAgent = $request->getHeader('User-Agent')[0];
if (strpos($userAgent, 'endpoint-discovery') === false) {
$userAgent = $userAgent . ' endpoint-discovery';
}
} else {
$userAgent = 'endpoint-discovery';
}
return $request
->withUri(
$request->getUri()
->withHost($parsed['host'])
->withPath($parsed['path'])
)
->withHeader('User-Agent', $userAgent);
}
/**
* Parses an endpoint returned from the discovery API into an array with
* 'host' and 'path' keys.
*
* @param $endpoint
* @return array
*/
private function parseEndpoint($endpoint)
{
$parsed = parse_url($endpoint);
// parse_url() will correctly parse full URIs with schemes
if (isset($parsed['host'])) {
return $parsed;
}
// parse_url() will put host & path in 'path' if scheme is not provided
if (isset($parsed['path'])) {
$split = explode('/', $parsed['path'], 2);
$parsed['host'] = $split[0];
if (isset($split[1])) {
$parsed['path'] = $split[1];
} else {
$parsed['path'] = '';
}
return $parsed;
}
throw new UnresolvedEndpointException("The supplied endpoint '"
. "{$endpoint}' is invalid.");
}
private function useOriginalUri(
UriInterface $uri,
CommandInterface $cmd,
RequestInterface $request
) {
$nextHandler = $this->nextHandler;
$endpoint = $uri->getHost() . $uri->getPath();
$request = $this->modifyRequest(
$request,
$endpoint
);
return $nextHandler($cmd, $request);
}
}
vendor/aws/aws-sdk-php/src/EndpointDiscovery/ConfigurationProvider.php 0000644 00000021163 14760036270 0022273 0 ustar 00
* use Aws\EndpointDiscovery\ConfigurationProvider;
* $provider = ConfigurationProvider::defaultProvider();
* // Returns a ConfigurationInterface or throws.
* $config = $provider()->wait();
*
*
* Configuration providers can be composed to create configuration using
* conditional logic that can create different configurations in different
* environments. You can compose multiple providers into a single provider using
* {@see Aws\EndpointDiscovery\ConfigurationProvider::chain}. This function
* accepts providers as variadic arguments and returns a new function that will
* invoke each provider until a successful configuration is returned.
*
*
* // First try an INI file at this location.
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
* // Then try an INI file at this location.
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
* // Then try loading from environment variables.
* $c = ConfigurationProvider::env();
* // Combine the three providers together.
* $composed = ConfigurationProvider::chain($a, $b, $c);
* // Returns a promise that is fulfilled with a configuration or throws.
* $promise = $composed();
* // Wait on the configuration to resolve.
* $config = $promise->wait();
*
*/
class ConfigurationProvider extends AbstractConfigurationProvider
implements ConfigurationProviderInterface
{
const DEFAULT_ENABLED = false;
const DEFAULT_CACHE_LIMIT = 1000;
const ENV_ENABLED = 'AWS_ENDPOINT_DISCOVERY_ENABLED';
const ENV_ENABLED_ALT = 'AWS_ENABLE_ENDPOINT_DISCOVERY';
const ENV_PROFILE = 'AWS_PROFILE';
public static $cacheKey = 'aws_cached_endpoint_discovery_config';
protected static $interfaceClass = ConfigurationInterface::class;
protected static $exceptionClass = ConfigurationException::class;
/**
* Create a default config provider that first checks for environment
* variables, then checks for a specified profile in the environment-defined
* config file location (env variable is 'AWS_CONFIG_FILE', file location
* defaults to ~/.aws/config), then checks for the "default" profile in the
* environment-defined config file location, and failing those uses a default
* fallback set of configuration options.
*
* This provider is automatically wrapped in a memoize function that caches
* previously provided config options.
*
* @param array $config
*
* @return callable
*/
public static function defaultProvider(array $config = [])
{
$configProviders = [self::env()];
if (
!isset($config['use_aws_shared_config_files'])
|| $config['use_aws_shared_config_files'] != false
) {
$configProviders[] = self::ini();
}
$configProviders[] = self::fallback($config);
$memo = self::memoize(
call_user_func_array('self::chain', $configProviders)
);
if (isset($config['endpoint_discovery'])
&& $config['endpoint_discovery'] instanceof CacheInterface
) {
return self::cache($memo, $config['endpoint_discovery'], self::$cacheKey);
}
return $memo;
}
/**
* Provider that creates config from environment variables.
*
* @param $cacheLimit
* @return callable
*/
public static function env($cacheLimit = self::DEFAULT_CACHE_LIMIT)
{
return function () use ($cacheLimit) {
// Use config from environment variables, if available
$enabled = getenv(self::ENV_ENABLED);
if ($enabled === false || $enabled === '') {
$enabled = getenv(self::ENV_ENABLED_ALT);
}
if ($enabled !== false && $enabled !== '') {
return Promise\promise_for(
new Configuration($enabled, $cacheLimit)
);
}
return self::reject('Could not find environment variable config'
. ' in ' . self::ENV_ENABLED);
};
}
/**
* Fallback config options when other sources are not set. Will check the
* service model for any endpoint discovery required operations, and enable
* endpoint discovery in that case. If no required operations found, will use
* the class default values.
*
* @param array $config
* @return callable
*/
public static function fallback($config = [])
{
$enabled = self::DEFAULT_ENABLED;
if (!empty($config['api_provider'])
&& !empty($config['service'])
&& !empty($config['version'])
) {
$provider = $config['api_provider'];
$apiData = $provider('api', $config['service'], $config['version']);
if (!empty($apiData['operations'])) {
foreach ($apiData['operations'] as $operation) {
if (!empty($operation['endpointdiscovery']['required'])) {
$enabled = true;
}
}
}
}
return function () use ($enabled) {
return Promise\promise_for(
new Configuration(
$enabled,
self::DEFAULT_CACHE_LIMIT
)
);
};
}
/**
* Config provider that creates config using a config file whose location
* is specified by an environment variable 'AWS_CONFIG_FILE', defaulting to
* ~/.aws/config if not specified
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile.
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the default directory.
* @param int $cacheLimit
*
* @return callable
*/
public static function ini(
$profile = null,
$filename = null,
$cacheLimit = self::DEFAULT_CACHE_LIMIT
) {
$filename = $filename ?: (self::getDefaultConfigFilename());
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
return function () use ($profile, $filename, $cacheLimit) {
if (!is_readable($filename)) {
return self::reject("Cannot read configuration from $filename");
}
$data = \Aws\parse_ini_file($filename, true);
if ($data === false) {
return self::reject("Invalid config file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in config file");
}
if (!isset($data[$profile]['endpoint_discovery_enabled'])) {
return self::reject("Required endpoint discovery config values
not present in INI profile '{$profile}' ({$filename})");
}
return Promise\promise_for(
new Configuration(
$data[$profile]['endpoint_discovery_enabled'],
$cacheLimit
)
);
};
}
/**
* Unwraps a configuration object in whatever valid form it is in,
* always returning a ConfigurationInterface object.
*
* @param mixed $config
* @return ConfigurationInterface
* @throws \InvalidArgumentException
*/
public static function unwrap($config)
{
if (is_callable($config)) {
$config = $config();
}
if ($config instanceof PromiseInterface) {
$config = $config->wait();
}
if ($config instanceof ConfigurationInterface) {
return $config;
} elseif (is_array($config) && isset($config['enabled'])) {
if (isset($config['cache_limit'])) {
return new Configuration(
$config['enabled'],
$config['cache_limit']
);
}
return new Configuration(
$config['enabled'],
self::DEFAULT_CACHE_LIMIT
);
}
throw new \InvalidArgumentException('Not a valid endpoint_discovery '
. 'configuration argument.');
}
}
vendor/aws/aws-sdk-php/src/EndpointDiscovery/ConfigurationInterface.php 0000644 00000001104 14760036270 0022372 0 ustar 00 cacheLimit = filter_var($cacheLimit, FILTER_VALIDATE_INT);
if ($this->cacheLimit == false || $this->cacheLimit < 1) {
throw new \InvalidArgumentException(
"'cache_limit' value must be a positive integer."
);
}
// Unparsable $enabled flag errs on the side of disabling endpoint discovery
$this->enabled = filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
}
/**
* {@inheritdoc}
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* {@inheritdoc}
*/
public function getCacheLimit()
{
return $this->cacheLimit;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return [
'enabled' => $this->isEnabled(),
'cache_limit' => $this->getCacheLimit()
];
}
}
vendor/aws/aws-sdk-php/src/Exception/UnresolvedSignatureException.php 0000644 00000000362 14760036270 0022124 0 ustar 00 'uploading parts to']);
$msg .= ". The following parts had errors:\n";
/** @var $error AwsException */
foreach ($prev as $part => $error) {
$msg .= "- Part {$part}: " . $error->getMessage(). "\n";
}
} elseif ($prev instanceof AwsException) {
switch ($prev->getCommand()->getName()) {
case 'CreateMultipartUpload':
case 'InitiateMultipartUpload':
$action = 'initiating';
break;
case 'CompleteMultipartUpload':
$action = 'completing';
break;
}
if (isset($action)) {
$msg = strtr($msg, ['performing' => $action]);
}
$msg .= ": {$prev->getMessage()}";
}
if (!$prev instanceof \Exception) {
$prev = null;
}
parent::__construct($msg, 0, $prev);
$this->state = $state;
}
/**
* Get the state of the transfer
*
* @return UploadState
*/
public function getState()
{
return $this->state;
}
}
vendor/aws/aws-sdk-php/src/Exception/InvalidRegionException.php 0000644 00000000354 14760036270 0020647 0 ustar 00 errorCode = $code;
$this->errorMessage = $message;
parent::__construct($message);
}
/**
* Get the AWS error code.
*
* @return string|null Returns null if no response was received
*/
public function getAwsErrorCode()
{
return $this->errorCode;
}
/**
* Get the concise error message if any.
*
* @return string|null
*/
public function getAwsErrorMessage()
{
return $this->errorMessage;
}
}
vendor/aws/aws-sdk-php/src/Exception/CryptoPolyfillException.php 0000644 00000000240 14760036270 0021102 0 ustar 00 data = isset($context['body']) ? $context['body'] : [];
$this->command = $command;
$this->response = isset($context['response']) ? $context['response'] : null;
$this->request = isset($context['request']) ? $context['request'] : null;
$this->requestId = isset($context['request_id'])
? $context['request_id']
: null;
$this->errorType = isset($context['type']) ? $context['type'] : null;
$this->errorCode = isset($context['code']) ? $context['code'] : null;
$this->errorShape = isset($context['error_shape']) ? $context['error_shape'] : null;
$this->connectionError = !empty($context['connection_error']);
$this->result = isset($context['result']) ? $context['result'] : null;
$this->transferInfo = isset($context['transfer_stats'])
? $context['transfer_stats']
: [];
$this->errorMessage = isset($context['message'])
? $context['message']
: null;
$this->monitoringEvents = [];
$this->maxRetriesExceeded = false;
parent::__construct($message, 0, $previous);
}
public function __toString()
{
if (!$this->getPrevious()) {
return parent::__toString();
}
// PHP strangely shows the innermost exception first before the outer
// exception message. It also has a default character limit for
// exception message strings such that the "next" exception (this one)
// might not even get shown, causing developers to attempt to catch
// the inner exception instead of the actual exception because they
// can't see the outer exception's __toString output.
return sprintf(
"exception '%s' with message '%s'\n\n%s",
get_class($this),
$this->getMessage(),
parent::__toString()
);
}
/**
* Get the command that was executed.
*
* @return CommandInterface
*/
public function getCommand()
{
return $this->command;
}
/**
* Get the concise error message if any.
*
* @return string|null
*/
public function getAwsErrorMessage()
{
return $this->errorMessage;
}
/**
* Get the sent HTTP request if any.
*
* @return RequestInterface|null
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the received HTTP response if any.
*
* @return ResponseInterface|null
*/
public function getResponse()
{
return $this->response;
}
/**
* Get the result of the exception if available
*
* @return ResultInterface|null
*/
public function getResult()
{
return $this->result;
}
/**
* Returns true if this is a connection error.
*
* @return bool
*/
public function isConnectionError()
{
return $this->connectionError;
}
/**
* If available, gets the HTTP status code of the corresponding response
*
* @return int|null
*/
public function getStatusCode()
{
return $this->response ? $this->response->getStatusCode() : null;
}
/**
* Get the request ID of the error. This value is only present if a
* response was received and is not present in the event of a networking
* error.
*
* @return string|null Returns null if no response was received
*/
public function getAwsRequestId()
{
return $this->requestId;
}
/**
* Get the AWS error type.
*
* @return string|null Returns null if no response was received
*/
public function getAwsErrorType()
{
return $this->errorType;
}
/**
* Get the AWS error code.
*
* @return string|null Returns null if no response was received
*/
public function getAwsErrorCode()
{
return $this->errorCode;
}
/**
* Get the AWS error shape.
*
* @return Shape|null Returns null if no response was received
*/
public function getAwsErrorShape()
{
return $this->errorShape;
}
/**
* Get all transfer information as an associative array if no $name
* argument is supplied, or gets a specific transfer statistic if
* a $name attribute is supplied (e.g., 'retries_attempted').
*
* @param string $name Name of the transfer stat to retrieve
*
* @return mixed|null|array
*/
public function getTransferInfo($name = null)
{
if (!$name) {
return $this->transferInfo;
}
return isset($this->transferInfo[$name])
? $this->transferInfo[$name]
: null;
}
/**
* Replace the transfer information associated with an exception.
*
* @param array $info
*/
public function setTransferInfo(array $info)
{
$this->transferInfo = $info;
}
/**
* Returns whether the max number of retries is exceeded.
*
* @return bool
*/
public function isMaxRetriesExceeded()
{
return $this->maxRetriesExceeded;
}
/**
* Sets the flag for max number of retries exceeded.
*/
public function setMaxRetriesExceeded()
{
$this->maxRetriesExceeded = true;
}
public function hasKey($name)
{
return isset($this->data[$name]);
}
public function get($key)
{
return $this[$key];
}
public function search($expression)
{
return JmesPath::search($expression, $this->toArray());
}
}
vendor/aws/aws-sdk-php/src/Handler/GuzzleV5/PsrStream.php 0000644 00000001302 14760036270 0017262 0 ustar 00 stream = $stream;
}
public function rewind()
{
$this->stream->seek(0);
}
public function getContents()
{
return $this->stream->getContents();
}
}
vendor/aws/aws-sdk-php/src/Handler/GuzzleV5/GuzzleStream.php 0000644 00000001035 14760036270 0020001 0 ustar 00 stream = $stream;
}
}
vendor/aws/aws-sdk-php/src/Handler/GuzzleV5/GuzzleHandler.php 0000644 00000015202 14760036270 0020124 0 ustar 00 true,
'expect' => true,
'cert' => true,
'verify' => true,
'timeout' => true,
'debug' => true,
'connect_timeout' => true,
'stream' => true,
'delay' => true,
'sink' => true,
];
/** @var ClientInterface */
private $client;
/**
* @param ClientInterface $client
*/
public function __construct(ClientInterface $client = null)
{
$this->client = $client ?: new Client();
}
/**
* @param Psr7Request $request
* @param array $options
* @return Promise\Promise|Promise\PromiseInterface
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function __invoke(Psr7Request $request, array $options = [])
{
// Create and send a Guzzle 5 request
$guzzlePromise = $this->client->send(
$this->createGuzzleRequest($request, $options)
);
$promise = new Promise\Promise(
function () use ($guzzlePromise) {
try {
$guzzlePromise->wait();
} catch (\Exception $e) {
// The promise is already delivered when the exception is
// thrown, so don't rethrow it.
}
},
[$guzzlePromise, 'cancel']
);
$guzzlePromise->then([$promise, 'resolve'], [$promise, 'reject']);
return $promise->then(
function (GuzzleResponse $response) {
// Adapt the Guzzle 5 Future to a Guzzle 6 ResponsePromise.
return $this->createPsr7Response($response);
},
function (Exception $exception) use ($options) {
// If we got a 'sink' that's a path, set the response body to
// the contents of the file. This will build the resulting
// exception with more information.
if ($exception instanceof RequestException) {
if (isset($options['sink'])) {
if (!($options['sink'] instanceof Psr7StreamInterface)) {
$exception->getResponse()->setBody(
Stream::factory(
file_get_contents($options['sink'])
)
);
}
}
}
// Reject with information about the error.
return new Promise\RejectedPromise($this->prepareErrorData($exception));
}
);
}
private function createGuzzleRequest(Psr7Request $psrRequest, array $options)
{
$ringConfig = [];
$statsCallback = isset($options['http_stats_receiver'])
? $options['http_stats_receiver']
: null;
unset($options['http_stats_receiver']);
// Remove unsupported options.
foreach (array_keys($options) as $key) {
if (!isset(self::$validOptions[$key])) {
unset($options[$key]);
}
}
// Handle delay option.
if (isset($options['delay'])) {
$ringConfig['delay'] = $options['delay'];
unset($options['delay']);
}
// Prepare sink option.
if (isset($options['sink'])) {
$ringConfig['save_to'] = ($options['sink'] instanceof Psr7StreamInterface)
? new GuzzleStream($options['sink'])
: $options['sink'];
unset($options['sink']);
}
// Ensure that all requests are async and lazy like Guzzle 6.
$options['future'] = 'lazy';
// Create the Guzzle 5 request from the provided PSR7 request.
$request = $this->client->createRequest(
$psrRequest->getMethod(),
$psrRequest->getUri(),
$options
);
if (is_callable($statsCallback)) {
$request->getEmitter()->on(
'end',
function (EndEvent $event) use ($statsCallback) {
$statsCallback($event->getTransferInfo());
}
);
}
// For the request body, adapt the PSR stream to a Guzzle stream.
$body = $psrRequest->getBody();
if ($body->getSize() === 0) {
$request->setBody(null);
} else {
$request->setBody(new GuzzleStream($body));
}
$request->setHeaders($psrRequest->getHeaders());
$request->setHeader(
'User-Agent',
$request->getHeader('User-Agent')
. ' ' . Client::getDefaultUserAgent()
);
// Make sure the delay is configured, if provided.
if ($ringConfig) {
foreach ($ringConfig as $k => $v) {
$request->getConfig()->set($k, $v);
}
}
return $request;
}
private function createPsr7Response(GuzzleResponse $response)
{
if ($body = $response->getBody()) {
$body = new PsrStream($body);
}
return new Psr7Response(
$response->getStatusCode(),
$response->getHeaders(),
$body,
$response->getReasonPhrase()
);
}
private function prepareErrorData(Exception $e)
{
$error = [
'exception' => $e,
'connection_error' => false,
'response' => null,
];
if ($e instanceof ConnectException) {
$error['connection_error'] = true;
}
if ($e instanceof RequestException && $e->getResponse()) {
$error['response'] = $this->createPsr7Response($e->getResponse());
}
return $error;
}
}
vendor/aws/aws-sdk-php/src/Handler/GuzzleV6/GuzzleHandler.php 0000644 00000005346 14760036270 0020135 0 ustar 00 client = $client ?: new Client();
}
/**
* @param Psr7Request $request
* @param array $options
*
* @return Promise\Promise
*/
public function __invoke(Psr7Request $request, array $options = [])
{
$request = $request->withHeader(
'User-Agent',
$request->getHeaderLine('User-Agent')
. ' ' . \GuzzleHttp\default_user_agent()
);
return $this->client->sendAsync($request, $this->parseOptions($options))
->otherwise(
static function ($e) {
$error = [
'exception' => $e,
'connection_error' => $e instanceof ConnectException,
'response' => null,
];
if (
($e instanceof RequestException)
&& $e->getResponse()
) {
$error['response'] = $e->getResponse();
} else if (
class_exists('Error')
&& $e instanceof \Error
&& $e ->getResponse()
) {
$error['response'] = $e->getResponse();
}
return new Promise\RejectedPromise($error);
}
);
}
private function parseOptions(array $options)
{
if (isset($options['http_stats_receiver'])) {
$fn = $options['http_stats_receiver'];
unset($options['http_stats_receiver']);
$prev = isset($options['on_stats'])
? $options['on_stats']
: null;
$options['on_stats'] = static function (
TransferStats $stats
) use ($fn, $prev) {
if (is_callable($prev)) {
$prev($stats);
}
$transferStats = ['total_time' => $stats->getTransferTime()];
$transferStats += $stats->getHandlerStats();
$fn($transferStats);
};
}
return $options;
}
}
vendor/aws/aws-sdk-php/src/Multipart/UploadState.php 0000644 00000006407 14760036270 0016513 0 ustar 00 id = $id;
}
/**
* Get the upload's ID, which is a tuple of parameters that can uniquely
* identify the upload.
*
* @return array
*/
public function getId()
{
return $this->id;
}
/**
* Set's the "upload_id", or 3rd part of the upload's ID. This typically
* only needs to be done after initiating an upload.
*
* @param string $key The param key of the upload_id.
* @param string $value The param value of the upload_id.
*/
public function setUploadId($key, $value)
{
$this->id[$key] = $value;
}
/**
* Get the part size.
*
* @return int
*/
public function getPartSize()
{
return $this->partSize;
}
/**
* Set the part size.
*
* @param $partSize int Size of upload parts.
*/
public function setPartSize($partSize)
{
$this->partSize = $partSize;
}
/**
* Marks a part as being uploaded.
*
* @param int $partNumber The part number.
* @param array $partData Data from the upload operation that needs to be
* recalled during the complete operation.
*/
public function markPartAsUploaded($partNumber, array $partData = [])
{
$this->uploadedParts[$partNumber] = $partData;
}
/**
* Returns whether a part has been uploaded.
*
* @param int $partNumber The part number.
*
* @return bool
*/
public function hasPartBeenUploaded($partNumber)
{
return isset($this->uploadedParts[$partNumber]);
}
/**
* Returns a sorted list of all the uploaded parts.
*
* @return array
*/
public function getUploadedParts()
{
ksort($this->uploadedParts);
return $this->uploadedParts;
}
/**
* Set the status of the upload.
*
* @param int $status Status is an integer code defined by the constants
* CREATED, INITIATED, and COMPLETED on this class.
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* Determines whether the upload state is in the INITIATED status.
*
* @return bool
*/
public function isInitiated()
{
return $this->status === self::INITIATED;
}
/**
* Determines whether the upload state is in the COMPLETED status.
*
* @return bool
*/
public function isCompleted()
{
return $this->status === self::COMPLETED;
}
}
vendor/aws/aws-sdk-php/src/Multipart/AbstractUploader.php 0000644 00000011201 14760036270 0017511 0 ustar 00 source = $this->determineSource($source);
parent::__construct($client, $config);
}
/**
* Create a stream for a part that starts at the current position and
* has a length of the upload part size (or less with the final part).
*
* @param Stream $stream
*
* @return Psr7\LimitStream
*/
protected function limitPartStream(Stream $stream)
{
// Limit what is read from the stream to the part size.
return new Psr7\LimitStream(
$stream,
$this->state->getPartSize(),
$this->source->tell()
);
}
protected function getUploadCommands(callable $resultHandler)
{
// Determine if the source can be seeked.
$seekable = $this->source->isSeekable()
&& $this->source->getMetadata('wrapper_type') === 'plainfile';
for ($partNumber = 1; $this->isEof($seekable); $partNumber++) {
// If we haven't already uploaded this part, yield a new part.
if (!$this->state->hasPartBeenUploaded($partNumber)) {
$partStartPos = $this->source->tell();
if (!($data = $this->createPart($seekable, $partNumber))) {
break;
}
$command = $this->client->getCommand(
$this->info['command']['upload'],
$data + $this->state->getId()
);
$command->getHandlerList()->appendSign($resultHandler, 'mup');
$numberOfParts = $this->getNumberOfParts($this->state->getPartSize());
if (isset($numberOfParts) && $partNumber > $numberOfParts) {
throw new $this->config['exception_class'](
$this->state,
new AwsException(
"Maximum part number for this job exceeded, file has likely been corrupted." .
" Please restart this upload.",
$command
)
);
}
yield $command;
if ($this->source->tell() > $partStartPos) {
continue;
}
}
// Advance the source's offset if not already advanced.
if ($seekable) {
$this->source->seek(min(
$this->source->tell() + $this->state->getPartSize(),
$this->source->getSize()
));
} else {
$this->source->read($this->state->getPartSize());
}
}
}
/**
* Generates the parameters for an upload part by analyzing a range of the
* source starting from the current offset up to the part size.
*
* @param bool $seekable
* @param int $number
*
* @return array|null
*/
abstract protected function createPart($seekable, $number);
/**
* Checks if the source is at EOF.
*
* @param bool $seekable
*
* @return bool
*/
private function isEof($seekable)
{
return $seekable
? $this->source->tell() < $this->source->getSize()
: !$this->source->eof();
}
/**
* Turns the provided source into a stream and stores it.
*
* If a string is provided, it is assumed to be a filename, otherwise, it
* passes the value directly to `Psr7\Utils::streamFor()`.
*
* @param mixed $source
*
* @return Stream
*/
private function determineSource($source)
{
// Use the contents of a file as the data source.
if (is_string($source)) {
$source = Psr7\Utils::tryFopen($source, 'r');
}
// Create a source stream.
$stream = Psr7\Utils::streamFor($source);
if (!$stream->isReadable()) {
throw new IAE('Source stream must be readable.');
}
return $stream;
}
protected function getNumberOfParts($partSize)
{
if ($sourceSize = $this->source->getSize()) {
return ceil($sourceSize/$partSize);
}
return null;
}
}
vendor/aws/aws-sdk-php/src/Multipart/AbstractUploadManager.php 0000644 00000024403 14760036270 0020465 0 ustar 00 null,
'state' => null,
'concurrency' => self::DEFAULT_CONCURRENCY,
'prepare_data_source' => null,
'before_initiate' => null,
'before_upload' => null,
'before_complete' => null,
'exception_class' => 'Aws\Exception\MultipartUploadException',
];
/** @var Client Client used for the upload. */
protected $client;
/** @var array Configuration used to perform the upload. */
protected $config;
/** @var array Service-specific information about the upload workflow. */
protected $info;
/** @var PromiseInterface Promise that represents the multipart upload. */
protected $promise;
/** @var UploadState State used to manage the upload. */
protected $state;
/**
* @param Client $client
* @param array $config
*/
public function __construct(Client $client, array $config = [])
{
$this->client = $client;
$this->info = $this->loadUploadWorkflowInfo();
$this->config = $config + self::$defaultConfig;
$this->state = $this->determineState();
}
/**
* Returns the current state of the upload
*
* @return UploadState
*/
public function getState()
{
return $this->state;
}
/**
* Upload the source using multipart upload operations.
*
* @return Result The result of the CompleteMultipartUpload operation.
* @throws \LogicException if the upload is already complete or aborted.
* @throws MultipartUploadException if an upload operation fails.
*/
public function upload()
{
return $this->promise()->wait();
}
/**
* Upload the source asynchronously using multipart upload operations.
*
* @return PromiseInterface
*/
public function promise()
{
if ($this->promise) {
return $this->promise;
}
return $this->promise = Promise\coroutine(function () {
// Initiate the upload.
if ($this->state->isCompleted()) {
throw new \LogicException('This multipart upload has already '
. 'been completed or aborted.'
);
}
if (!$this->state->isInitiated()) {
// Execute the prepare callback.
if (is_callable($this->config["prepare_data_source"])) {
$this->config["prepare_data_source"]();
}
$result = (yield $this->execCommand('initiate', $this->getInitiateParams()));
$this->state->setUploadId(
$this->info['id']['upload_id'],
$result[$this->info['id']['upload_id']]
);
$this->state->setStatus(UploadState::INITIATED);
}
// Create a command pool from a generator that yields UploadPart
// commands for each upload part.
$resultHandler = $this->getResultHandler($errors);
$commands = new CommandPool(
$this->client,
$this->getUploadCommands($resultHandler),
[
'concurrency' => $this->config['concurrency'],
'before' => $this->config['before_upload'],
]
);
// Execute the pool of commands concurrently, and process errors.
yield $commands->promise();
if ($errors) {
throw new $this->config['exception_class']($this->state, $errors);
}
// Complete the multipart upload.
yield $this->execCommand('complete', $this->getCompleteParams());
$this->state->setStatus(UploadState::COMPLETED);
})->otherwise($this->buildFailureCatch());
}
private function transformException($e)
{
// Throw errors from the operations as a specific Multipart error.
if ($e instanceof AwsException) {
$e = new $this->config['exception_class']($this->state, $e);
}
throw $e;
}
private function buildFailureCatch()
{
if (interface_exists("Throwable")) {
return function (\Throwable $e) {
return $this->transformException($e);
};
} else {
return function (\Exception $e) {
return $this->transformException($e);
};
}
}
protected function getConfig()
{
return $this->config;
}
/**
* Provides service-specific information about the multipart upload
* workflow.
*
* This array of data should include the keys: 'command', 'id', and 'part_num'.
*
* @return array
*/
abstract protected function loadUploadWorkflowInfo();
/**
* Determines the part size to use for upload parts.
*
* Examines the provided partSize value and the source to determine the
* best possible part size.
*
* @throws \InvalidArgumentException if the part size is invalid.
*
* @return int
*/
abstract protected function determinePartSize();
/**
* Uses information from the Command and Result to determine which part was
* uploaded and mark it as uploaded in the upload's state.
*
* @param CommandInterface $command
* @param ResultInterface $result
*/
abstract protected function handleResult(
CommandInterface $command,
ResultInterface $result
);
/**
* Gets the service-specific parameters used to initiate the upload.
*
* @return array
*/
abstract protected function getInitiateParams();
/**
* Gets the service-specific parameters used to complete the upload.
*
* @return array
*/
abstract protected function getCompleteParams();
/**
* Based on the config and service-specific workflow info, creates a
* `Promise` for an `UploadState` object.
*
* @return PromiseInterface A `Promise` that resolves to an `UploadState`.
*/
private function determineState()
{
// If the state was provided via config, then just use it.
if ($this->config['state'] instanceof UploadState) {
return $this->config['state'];
}
// Otherwise, construct a new state from the provided identifiers.
$required = $this->info['id'];
$id = [$required['upload_id'] => null];
unset($required['upload_id']);
foreach ($required as $key => $param) {
if (!$this->config[$key]) {
throw new IAE('You must provide a value for "' . $key . '" in '
. 'your config for the MultipartUploader for '
. $this->client->getApi()->getServiceFullName() . '.');
}
$id[$param] = $this->config[$key];
}
$state = new UploadState($id);
$state->setPartSize($this->determinePartSize());
return $state;
}
/**
* Executes a MUP command with all of the parameters for the operation.
*
* @param string $operation Name of the operation.
* @param array $params Service-specific params for the operation.
*
* @return PromiseInterface
*/
protected function execCommand($operation, array $params)
{
// Create the command.
$command = $this->client->getCommand(
$this->info['command'][$operation],
$params + $this->state->getId()
);
// Execute the before callback.
if (is_callable($this->config["before_{$operation}"])) {
$this->config["before_{$operation}"]($command);
}
// Execute the command asynchronously and return the promise.
return $this->client->executeAsync($command);
}
/**
* Returns a middleware for processing responses of part upload operations.
*
* - Adds an onFulfilled callback that calls the service-specific
* handleResult method on the Result of the operation.
* - Adds an onRejected callback that adds the error to an array of errors.
* - Has a passedByRef $errors arg that the exceptions get added to. The
* caller should use that &$errors array to do error handling.
*
* @param array $errors Errors from upload operations are added to this.
*
* @return callable
*/
protected function getResultHandler(&$errors = [])
{
return function (callable $handler) use (&$errors) {
return function (
CommandInterface $command,
RequestInterface $request = null
) use ($handler, &$errors) {
return $handler($command, $request)->then(
function (ResultInterface $result) use ($command) {
$this->handleResult($command, $result);
return $result;
},
function (AwsException $e) use (&$errors) {
$errors[$e->getCommand()[$this->info['part_num']]] = $e;
return new Result();
}
);
};
};
}
/**
* Creates a generator that yields part data for the upload's source.
*
* Yields associative arrays of parameters that are ultimately merged in
* with others to form the complete parameters of a command. This can
* include the Body parameter, which is a limited stream (i.e., a Stream
* object, decorated with a LimitStream).
*
* @param callable $resultHandler
*
* @return \Generator
*/
abstract protected function getUploadCommands(callable $resultHandler);
}
vendor/aws/aws-sdk-php/src/Retry/Exception/ConfigurationException.php 0000644 00000000466 14760036270 0022035 0 ustar 00 withHeader('aws-sdk-retry', "{$retries}/{$delayBy}");
}
private function updateStats($retries, $delay, array &$stats)
{
if (!isset($stats['total_retry_delay'])) {
$stats['total_retry_delay'] = 0;
}
$stats['total_retry_delay'] += $delay;
$stats['retries_attempted'] = $retries;
}
private function updateHttpStats($value, array &$stats)
{
if (empty($stats['http'])) {
$stats['http'] = [];
}
if ($value instanceof AwsException) {
$resultStats = $value->getTransferInfo();
$stats['http'] []= $resultStats;
} elseif ($value instanceof ResultInterface) {
$resultStats = isset($value['@metadata']['transferStats']['http'][0])
? $value['@metadata']['transferStats']['http'][0]
: [];
$stats['http'] []= $resultStats;
}
}
private function bindStatsToReturn($return, array $stats)
{
if ($return instanceof ResultInterface) {
if (!isset($return['@metadata'])) {
$return['@metadata'] = [];
}
$return['@metadata']['transferStats'] = $stats;
} elseif ($return instanceof AwsException) {
$return->setTransferInfo($stats);
}
return $return;
}
}
vendor/aws/aws-sdk-php/src/Retry/RateLimiter.php 0000644 00000012021 14760036270 0015620 0 ustar 00 beta = isset($options['beta'])
? $options['beta']
: 0.7;
$this->minCapacity = isset($options['min_capacity'])
? $options['min_capacity']
: 1;
$this->minFillRate = isset($options['min_fill_rate'])
? $options['min_fill_rate']
: 0.5;
$this->scaleConstant = isset($options['scale_constant'])
? $options['scale_constant']
: 0.4;
$this->smooth = isset($options['smooth'])
? $options['smooth']
: 0.8;
$this->timeProvider = isset($options['time_provider'])
? $options['time_provider']
: null;
$this->lastTxRateBucket = floor($this->time());
$this->lastThrottleTime = $this->time();
}
public function isEnabled()
{
return $this->enabled;
}
public function getSendToken()
{
$this->acquireToken(1);
}
public function updateSendingRate($isThrottled)
{
$this->updateMeasuredRate();
if ($isThrottled) {
if (!$this->isEnabled()) {
$rateToUse = $this->measuredTxRate;
} else {
$rateToUse = min($this->measuredTxRate, $this->fillRate);
}
$this->lastMaxRate = $rateToUse;
$this->calculateTimeWindow();
$this->lastThrottleTime = $this->time();
$calculatedRate = $this->cubicThrottle($rateToUse);
$this->enableTokenBucket();
} else {
$this->calculateTimeWindow();
$calculatedRate = $this->cubicSuccess($this->time());
}
$newRate = min($calculatedRate, 2 * $this->measuredTxRate);
$this->updateTokenBucketRate($newRate);
return $newRate;
}
private function acquireToken($amount)
{
if (!$this->enabled) {
return true;
}
$this->refillTokenBucket();
if ($amount > $this->currentCapacity) {
usleep(1000000 * ($amount - $this->currentCapacity) / $this->fillRate);
}
$this->currentCapacity -= $amount;
return true;
}
private function calculateTimeWindow()
{
$this->timeWindow = pow(($this->lastMaxRate * (1 - $this->beta) / $this->scaleConstant), 0.333);
}
private function cubicSuccess($timestamp)
{
$dt = $timestamp - $this->lastThrottleTime;
return $this->scaleConstant * pow($dt - $this->timeWindow, 3) + $this->lastMaxRate;
}
private function cubicThrottle($rateToUse)
{
return $rateToUse * $this->beta;
}
private function enableTokenBucket()
{
$this->enabled = true;
}
private function refillTokenBucket()
{
$timestamp = $this->time();
if (!isset($this->lastTimestamp)) {
$this->lastTimestamp = $timestamp;
return;
}
$fillAmount = ($timestamp - $this->lastTimestamp) * $this->fillRate;
$this->currentCapacity = $this->currentCapacity + $fillAmount;
if (!is_null($this->maxCapacity)) {
$this->currentCapacity = min(
$this->maxCapacity,
$this->currentCapacity
);
}
$this->lastTimestamp = $timestamp;
}
private function time()
{
if (is_callable($this->timeProvider)) {
$provider = $this->timeProvider;
$time = $provider();
return $time;
}
return microtime(true);
}
private function updateMeasuredRate()
{
$timestamp = $this->time();
$timeBucket = floor(round($timestamp, 3) * 2) / 2;
$this->requestCount++;
if ($timeBucket > $this->lastTxRateBucket) {
$currentRate = $this->requestCount / ($timeBucket - $this->lastTxRateBucket);
$this->measuredTxRate = ($currentRate * $this->smooth)
+ ($this->measuredTxRate * (1 - $this->smooth));
$this->requestCount = 0;
$this->lastTxRateBucket = $timeBucket;
}
}
private function updateTokenBucketRate($newRps)
{
$this->refillTokenBucket();
$this->fillRate = max($newRps, $this->minFillRate);
$this->maxCapacity = max($newRps, $this->minCapacity);
$this->currentCapacity = min($this->currentCapacity, $this->maxCapacity);
}
}
vendor/aws/aws-sdk-php/src/Retry/QuotaManager.php 0000644 00000005012 14760036270 0015765 0 ustar 00 initialRetryTokens = isset($config['initial_retry_tokens'])
? $config['initial_retry_tokens']
: 500;
$this->noRetryIncrement = isset($config['no_retry_increment'])
? $config['no_retry_increment']
: 1;
$this->retryCost = isset($config['retry_cost'])
? $config['retry_cost']
: 5;
$this->timeoutRetryCost = isset($config['timeout_retry_cost'])
? $config['timeout_retry_cost']
: 10;
$this->maxCapacity = $this->initialRetryTokens;
$this->availableCapacity = $this->initialRetryTokens;
}
public function hasRetryQuota($result)
{
if ($result instanceof AwsException && $result->isConnectionError()) {
$this->capacityAmount = $this->timeoutRetryCost;
} else {
$this->capacityAmount = $this->retryCost;
}
if ($this->capacityAmount > $this->availableCapacity) {
return false;
}
$this->availableCapacity -= $this->capacityAmount;
return true;
}
public function releaseToQuota($result)
{
if ($result instanceof AwsException) {
$statusCode = (int) $result->getStatusCode();
} elseif ($result instanceof ResultInterface) {
$statusCode = isset($result['@metadata']['statusCode'])
? (int) $result['@metadata']['statusCode']
: null;
}
if (!empty($statusCode) && $statusCode >= 200 && $statusCode < 300) {
if (isset($this->capacityAmount)) {
$amount = $this->capacityAmount;
$this->availableCapacity += $amount;
unset($this->capacityAmount);
} else {
$amount = $this->noRetryIncrement;
$this->availableCapacity += $amount;
}
$this->availableCapacity = min(
$this->availableCapacity,
$this->maxCapacity
);
}
return (isset($amount) ? $amount : 0);
}
public function getAvailableCapacity()
{
return $this->availableCapacity;
}
}
vendor/aws/aws-sdk-php/src/Retry/ConfigurationProvider.php 0000644 00000017325 14760036270 0017735 0 ustar 00
* use Aws\Sts\RegionalEndpoints\ConfigurationProvider;
* $provider = ConfigurationProvider::defaultProvider();
* // Returns a ConfigurationInterface or throws.
* $config = $provider()->wait();
*
*
* Configuration providers can be composed to create configuration using
* conditional logic that can create different configurations in different
* environments. You can compose multiple providers into a single provider using
* {@see \Aws\Retry\ConfigurationProvider::chain}. This function
* accepts providers as variadic arguments and returns a new function that will
* invoke each provider until a successful configuration is returned.
*
*
* // First try an INI file at this location.
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
* // Then try an INI file at this location.
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
* // Then try loading from environment variables.
* $c = ConfigurationProvider::env();
* // Combine the three providers together.
* $composed = ConfigurationProvider::chain($a, $b, $c);
* // Returns a promise that is fulfilled with a configuration or throws.
* $promise = $composed();
* // Wait on the configuration to resolve.
* $config = $promise->wait();
*
*/
class ConfigurationProvider extends AbstractConfigurationProvider
implements ConfigurationProviderInterface
{
const DEFAULT_MAX_ATTEMPTS = 3;
const DEFAULT_MODE = 'legacy';
const ENV_MAX_ATTEMPTS = 'AWS_MAX_ATTEMPTS';
const ENV_MODE = 'AWS_RETRY_MODE';
const ENV_PROFILE = 'AWS_PROFILE';
const INI_MAX_ATTEMPTS = 'max_attempts';
const INI_MODE = 'retry_mode';
public static $cacheKey = 'aws_retries_config';
protected static $interfaceClass = ConfigurationInterface::class;
protected static $exceptionClass = ConfigurationException::class;
/**
* Create a default config provider that first checks for environment
* variables, then checks for a specified profile in the environment-defined
* config file location (env variable is 'AWS_CONFIG_FILE', file location
* defaults to ~/.aws/config), then checks for the "default" profile in the
* environment-defined config file location, and failing those uses a default
* fallback set of configuration options.
*
* This provider is automatically wrapped in a memoize function that caches
* previously provided config options.
*
* @param array $config
*
* @return callable
*/
public static function defaultProvider(array $config = [])
{
$configProviders = [self::env()];
if (
!isset($config['use_aws_shared_config_files'])
|| $config['use_aws_shared_config_files'] != false
) {
$configProviders[] = self::ini();
}
$configProviders[] = self::fallback();
$memo = self::memoize(
call_user_func_array('self::chain', $configProviders)
);
if (isset($config['retries'])
&& $config['retries'] instanceof CacheInterface
) {
return self::cache($memo, $config['retries'], self::$cacheKey);
}
return $memo;
}
/**
* Provider that creates config from environment variables.
*
* @return callable
*/
public static function env()
{
return function () {
// Use config from environment variables, if available
$mode = getenv(self::ENV_MODE);
$maxAttempts = getenv(self::ENV_MAX_ATTEMPTS)
? getenv(self::ENV_MAX_ATTEMPTS)
: self::DEFAULT_MAX_ATTEMPTS;
if (!empty($mode)) {
return Promise\promise_for(
new Configuration($mode, $maxAttempts)
);
}
return self::reject('Could not find environment variable config'
. ' in ' . self::ENV_MODE);
};
}
/**
* Fallback config options when other sources are not set.
*
* @return callable
*/
public static function fallback()
{
return function () {
return Promise\promise_for(
new Configuration(self::DEFAULT_MODE, self::DEFAULT_MAX_ATTEMPTS)
);
};
}
/**
* Config provider that creates config using a config file whose location
* is specified by an environment variable 'AWS_CONFIG_FILE', defaulting to
* ~/.aws/config if not specified
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile.
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the default directory.
*
* @return callable
*/
public static function ini(
$profile = null,
$filename = null
) {
$filename = $filename ?: (self::getDefaultConfigFilename());
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
return function () use ($profile, $filename) {
if (!is_readable($filename)) {
return self::reject("Cannot read configuration from $filename");
}
$data = \Aws\parse_ini_file($filename, true);
if ($data === false) {
return self::reject("Invalid config file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in config file");
}
if (!isset($data[$profile][self::INI_MODE])) {
return self::reject("Required retry config values
not present in INI profile '{$profile}' ({$filename})");
}
$maxAttempts = isset($data[$profile][self::INI_MAX_ATTEMPTS])
? $data[$profile][self::INI_MAX_ATTEMPTS]
: self::DEFAULT_MAX_ATTEMPTS;
return Promise\promise_for(
new Configuration(
$data[$profile][self::INI_MODE],
$maxAttempts
)
);
};
}
/**
* Unwraps a configuration object in whatever valid form it is in,
* always returning a ConfigurationInterface object.
*
* @param mixed $config
* @return ConfigurationInterface
* @throws \InvalidArgumentException
*/
public static function unwrap($config)
{
if (is_callable($config)) {
$config = $config();
}
if ($config instanceof PromiseInterface) {
$config = $config->wait();
}
if ($config instanceof ConfigurationInterface) {
return $config;
}
// An integer value for this config indicates the legacy 'retries'
// config option, which is incremented to translate to max attempts
if (is_int($config)) {
return new Configuration('legacy', $config + 1);
}
if (is_array($config) && isset($config['mode'])) {
$maxAttempts = isset($config['max_attempts'])
? $config['max_attempts']
: self::DEFAULT_MAX_ATTEMPTS;
return new Configuration($config['mode'], $maxAttempts);
}
throw new \InvalidArgumentException('Not a valid retry configuration'
. ' argument.');
}
}
vendor/aws/aws-sdk-php/src/Retry/ConfigurationInterface.php 0000644 00000001113 14760036270 0020027 0 ustar 00 validModes)) {
throw new ConfigurationException("'{$mode}' is not a valid mode."
. " The mode has to be 'legacy', 'standard', or 'adaptive'.");
}
if (!is_numeric($maxAttempts)
|| intval($maxAttempts) != $maxAttempts
|| $maxAttempts < 1
) {
throw new ConfigurationException("The 'maxAttempts' parameter has"
. " to be an integer >= 1.");
}
$this->mode = $mode;
$this->maxAttempts = intval($maxAttempts);
}
/**
* {@inheritdoc}
*/
public function getMode()
{
return $this->mode;
}
/**
* {@inheritdoc}
*/
public function getMaxAttempts()
{
return $this->maxAttempts;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return [
'mode' => $this->getMode(),
'max_attempts' => $this->getMaxAttempts(),
];
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/UserAgentTrait.php 0000644 00000001710 14760036270 0016743 0 ustar 00 getHandlerList();
$list->appendBuild(Middleware::mapRequest(
function(RequestInterface $req) use ($agentString) {
if (!empty($req->getHeader('User-Agent'))
&& !empty($req->getHeader('User-Agent')[0])
) {
$userAgent = $req->getHeader('User-Agent')[0];
if (strpos($userAgent, $agentString) === false) {
$userAgent .= " {$agentString}";
};
} else {
$userAgent = $agentString;
}
$req = $req->withHeader('User-Agent', $userAgent);
return $req;
}
));
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploaderV2.php 0000644 00000016400 14760036270 0022212 0 ustar 00 appendUserAgent($client, 'S3CryptoV' . self::CRYPTO_VERSION);
$this->client = $client;
$config['params'] = [];
if (!empty($config['bucket'])) {
$config['params']['Bucket'] = $config['bucket'];
}
if (!empty($config['key'])) {
$config['params']['Key'] = $config['key'];
}
$this->provider = $this->getMaterialsProvider($config);
unset($config['@MaterialsProvider']);
$this->instructionFileSuffix = $this->getInstructionFileSuffix($config);
unset($config['@InstructionFileSuffix']);
$this->strategy = $this->getMetadataStrategy(
$config,
$this->instructionFileSuffix
);
if ($this->strategy === null) {
$this->strategy = self::getDefaultStrategy();
}
unset($config['@MetadataStrategy']);
$config['prepare_data_source'] = $this->getEncryptingDataPreparer();
parent::__construct($client, $source, $config);
}
private static function getDefaultStrategy()
{
return new HeadersMetadataStrategy();
}
private function getEncryptingDataPreparer()
{
return function() {
// Defer encryption work until promise is executed
$envelope = new MetadataEnvelope();
list($this->source, $params) = Promise\promise_for($this->encrypt(
$this->source,
$this->config ?: [],
$this->provider,
$envelope
))->then(
function ($bodyStream) use ($envelope) {
$params = $this->strategy->save(
$envelope,
$this->config['params']
);
return [$bodyStream, $params];
}
)->wait();
$this->source->rewind();
$this->config['params'] = $params;
};
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploader.php 0000644 00000015212 14760036270 0022002 0 ustar 00 appendUserAgent($client, 'S3CryptoV' . self::CRYPTO_VERSION);
$this->client = $client;
$config['params'] = [];
if (!empty($config['bucket'])) {
$config['params']['Bucket'] = $config['bucket'];
}
if (!empty($config['key'])) {
$config['params']['Key'] = $config['key'];
}
$this->provider = $this->getMaterialsProvider($config);
unset($config['@MaterialsProvider']);
$this->instructionFileSuffix = $this->getInstructionFileSuffix($config);
unset($config['@InstructionFileSuffix']);
$this->strategy = $this->getMetadataStrategy(
$config,
$this->instructionFileSuffix
);
if ($this->strategy === null) {
$this->strategy = self::getDefaultStrategy();
}
unset($config['@MetadataStrategy']);
$config['prepare_data_source'] = $this->getEncryptingDataPreparer();
parent::__construct($client, $source, $config);
}
private static function getDefaultStrategy()
{
return new HeadersMetadataStrategy();
}
private function getEncryptingDataPreparer()
{
return function() {
// Defer encryption work until promise is executed
$envelope = new MetadataEnvelope();
list($this->source, $params) = Promise\promise_for($this->encrypt(
$this->source,
$this->config['@cipheroptions'] ?: [],
$this->provider,
$envelope
))->then(
function ($bodyStream) use ($envelope) {
$params = $this->strategy->save(
$envelope,
$this->config['params']
);
return [$bodyStream, $params];
}
)->wait();
$this->source->rewind();
$this->config['params'] = $params;
};
}
} vendor/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClientV2.php 0000644 00000043647 14760036270 0017770 0 ustar 00
* use Aws\Crypto\KmsMaterialsProviderV2;
* use Aws\S3\Crypto\S3EncryptionClientV2;
* use Aws\S3\S3Client;
*
* $encryptionClient = new S3EncryptionClientV2(
* new S3Client([
* 'region' => 'us-west-2',
* 'version' => 'latest'
* ])
* );
* $materialsProvider = new KmsMaterialsProviderV2(
* new KmsClient([
* 'profile' => 'default',
* 'region' => 'us-east-1',
* 'version' => 'latest',
* ],
* 'your-kms-key-id'
* );
*
* $encryptionClient->putObject([
* '@MaterialsProvider' => $materialsProvider,
* '@CipherOptions' => [
* 'Cipher' => 'gcm',
* 'KeySize' => 256,
* ],
* '@KmsEncryptionContext' => ['foo' => 'bar'],
* 'Bucket' => 'your-bucket',
* 'Key' => 'your-key',
* 'Body' => 'your-encrypted-data',
* ]);
*
*
* Example read call (using objects from previous example):
*
*
* $encryptionClient->getObject([
* '@MaterialsProvider' => $materialsProvider,
* '@CipherOptions' => [
* 'Cipher' => 'gcm',
* 'KeySize' => 256,
* ],
* 'Bucket' => 'your-bucket',
* 'Key' => 'your-key',
* ]);
*
*/
class S3EncryptionClientV2 extends AbstractCryptoClientV2
{
use CipherBuilderTrait;
use CryptoParamsTraitV2;
use DecryptionTraitV2;
use EncryptionTraitV2;
use UserAgentTrait;
const CRYPTO_VERSION = '2.1';
private $client;
private $instructionFileSuffix;
private $legacyWarningCount;
/**
* @param S3Client $client The S3Client to be used for true uploading and
* retrieving objects from S3 when using the
* encryption client.
* @param string|null $instructionFileSuffix Suffix for a client wide
* default when using instruction
* files for metadata storage.
*/
public function __construct(
S3Client $client,
$instructionFileSuffix = null
) {
$this->appendUserAgent($client, 'S3CryptoV' . self::CRYPTO_VERSION);
$this->client = $client;
$this->instructionFileSuffix = $instructionFileSuffix;
$this->legacyWarningCount = 0;
}
private static function getDefaultStrategy()
{
return new HeadersMetadataStrategy();
}
/**
* Encrypts the data in the 'Body' field of $args and promises to upload it
* to the specified location on S3.
*
* Note that for PHP versions of < 7.1, this operation uses an AES-GCM
* polyfill for encryption since there is no native PHP support. The
* performance for large inputs will be a lot slower than for PHP 7.1+, so
* upgrading older PHP version environments may be necessary to use this
* effectively.
*
* @param array $args Arguments for encrypting an object and uploading it
* to S3 via PutObject.
*
* The required configuration arguments are as follows:
*
* - @MaterialsProvider: (MaterialsProviderV2) Provides Cek, Iv, and Cek
* encrypting/decrypting for encryption metadata.
* - @CipherOptions: (array) Cipher options for encrypting data. Only the
* Cipher option is required. Accepts the following:
* - Cipher: (string) gcm
* See also: AbstractCryptoClientV2::$supportedCiphers
* - KeySize: (int) 128|256
* See also: MaterialsProvider::$supportedKeySizes
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. Note if you pass in
* Aad, the PHP SDK will be able to decrypt the resulting object,
* but other AWS SDKs may not be able to do so.
* - @KmsEncryptionContext: (array) Only required if using
* KmsMaterialsProviderV2. An associative array of key-value
* pairs to be added to the encryption context for KMS key encryption. An
* empty array may be passed if no additional context is desired.
*
* The optional configuration arguments are as follows:
*
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
* MetadataEnvelope information. Defaults to using a
* HeadersMetadataStrategy. Can either be a class implementing
* MetadataStrategy, a class name of a predefined strategy, or empty/null
* to default.
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
* instruction file if using an InstructionFileMetadataHandler.
*
* @return PromiseInterface
*
* @throws \InvalidArgumentException Thrown when arguments above are not
* passed or are passed incorrectly.
*/
public function putObjectAsync(array $args)
{
$provider = $this->getMaterialsProvider($args);
unset($args['@MaterialsProvider']);
$instructionFileSuffix = $this->getInstructionFileSuffix($args);
unset($args['@InstructionFileSuffix']);
$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
unset($args['@MetadataStrategy']);
$envelope = new MetadataEnvelope();
return Promise\promise_for($this->encrypt(
Psr7\Utils::streamFor($args['Body']),
$args,
$provider,
$envelope
))->then(
function ($encryptedBodyStream) use ($args) {
$hash = new PhpHash('sha256');
$hashingEncryptedBodyStream = new HashingStream(
$encryptedBodyStream,
$hash,
self::getContentShaDecorator($args)
);
return [$hashingEncryptedBodyStream, $args];
}
)->then(
function ($putObjectContents) use ($strategy, $envelope) {
list($bodyStream, $args) = $putObjectContents;
if ($strategy === null) {
$strategy = self::getDefaultStrategy();
}
$updatedArgs = $strategy->save($envelope, $args);
$updatedArgs['Body'] = $bodyStream;
return $updatedArgs;
}
)->then(
function ($args) {
unset($args['@CipherOptions']);
return $this->client->putObjectAsync($args);
}
);
}
private static function getContentShaDecorator(&$args)
{
return function ($hash) use (&$args) {
$args['ContentSHA256'] = bin2hex($hash);
};
}
/**
* Encrypts the data in the 'Body' field of $args and uploads it to the
* specified location on S3.
*
* Note that for PHP versions of < 7.1, this operation uses an AES-GCM
* polyfill for encryption since there is no native PHP support. The
* performance for large inputs will be a lot slower than for PHP 7.1+, so
* upgrading older PHP version environments may be necessary to use this
* effectively.
*
* @param array $args Arguments for encrypting an object and uploading it
* to S3 via PutObject.
*
* The required configuration arguments are as follows:
*
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
* encrypting/decrypting for encryption metadata.
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
* is required. Accepts the following options:
* - Cipher: (string) gcm
* See also: AbstractCryptoClientV2::$supportedCiphers
* - KeySize: (int) 128|256
* See also: MaterialsProvider::$supportedKeySizes
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. Note if you pass in
* Aad, the PHP SDK will be able to decrypt the resulting object,
* but other AWS SDKs may not be able to do so.
* - @KmsEncryptionContext: (array) Only required if using
* KmsMaterialsProviderV2. An associative array of key-value
* pairs to be added to the encryption context for KMS key encryption. An
* empty array may be passed if no additional context is desired.
*
* The optional configuration arguments are as follows:
*
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
* MetadataEnvelope information. Defaults to using a
* HeadersMetadataStrategy. Can either be a class implementing
* MetadataStrategy, a class name of a predefined strategy, or empty/null
* to default.
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
* instruction file if an using an InstructionFileMetadataHandler was
* determined.
*
* @return \Aws\Result PutObject call result with the details of uploading
* the encrypted file.
*
* @throws \InvalidArgumentException Thrown when arguments above are not
* passed or are passed incorrectly.
*/
public function putObject(array $args)
{
return $this->putObjectAsync($args)->wait();
}
/**
* Promises to retrieve an object from S3 and decrypt the data in the
* 'Body' field.
*
* @param array $args Arguments for retrieving an object from S3 via
* GetObject and decrypting it.
*
* The required configuration argument is as follows:
*
* - @MaterialsProvider: (MaterialsProviderInterface) Provides Cek, Iv, and Cek
* encrypting/decrypting for decryption metadata. May have data loaded
* from the MetadataEnvelope upon decryption.
* - @SecurityProfile: (string) Must be set to 'V2' or 'V2_AND_LEGACY'.
* - 'V2' indicates that only objects encrypted with S3EncryptionClientV2
* content encryption and key wrap schemas are able to be decrypted.
* - 'V2_AND_LEGACY' indicates that objects encrypted with both
* S3EncryptionClientV2 and older legacy encryption clients are able
* to be decrypted.
*
* The optional configuration arguments are as follows:
*
* - SaveAs: (string) The path to a file on disk to save the decrypted
* object data. This will be handled by file_put_contents instead of the
* Guzzle sink.
*
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for reading
* MetadataEnvelope information. Defaults to determining based on object
* response headers. Can either be a class implementing MetadataStrategy,
* a class name of a predefined strategy, or empty/null to default.
* - @InstructionFileSuffix: (string) Suffix used when looking for an
* instruction file if an InstructionFileMetadataHandler is being used.
* - @CipherOptions: (array) Cipher options for decrypting data. A Cipher
* is required. Accepts the following options:
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. It is ignored when
* using cbc.
* - @KmsAllowDecryptWithAnyCmk: (bool) This allows decryption with
* KMS materials for any KMS key ID, instead of needing the KMS key ID to
* be specified and provided to the decrypt operation. Ignored for non-KMS
* materials providers. Defaults to false.
*
* @return PromiseInterface
*
* @throws \InvalidArgumentException Thrown when required arguments are not
* passed or are passed incorrectly.
*/
public function getObjectAsync(array $args)
{
$provider = $this->getMaterialsProvider($args);
unset($args['@MaterialsProvider']);
$instructionFileSuffix = $this->getInstructionFileSuffix($args);
unset($args['@InstructionFileSuffix']);
$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
unset($args['@MetadataStrategy']);
if (!isset($args['@SecurityProfile'])
|| !in_array($args['@SecurityProfile'], self::$supportedSecurityProfiles)
) {
throw new CryptoException("@SecurityProfile is required and must be"
. " set to 'V2' or 'V2_AND_LEGACY'");
}
// Only throw this legacy warning once per client
if (in_array($args['@SecurityProfile'], self::$legacySecurityProfiles)
&& $this->legacyWarningCount < 1
) {
$this->legacyWarningCount++;
trigger_error(
"This S3 Encryption Client operation is configured to"
. " read encrypted data with legacy encryption modes. If you"
. " don't have objects encrypted with these legacy modes,"
. " you should disable support for them to enhance security. ",
E_USER_WARNING
);
}
$saveAs = null;
if (!empty($args['SaveAs'])) {
$saveAs = $args['SaveAs'];
}
$promise = $this->client->getObjectAsync($args)
->then(
function ($result) use (
$provider,
$instructionFileSuffix,
$strategy,
$args
) {
if ($strategy === null) {
$strategy = $this->determineGetObjectStrategy(
$result,
$instructionFileSuffix
);
}
$envelope = $strategy->load($args + [
'Metadata' => $result['Metadata']
]);
$result['Body'] = $this->decrypt(
$result['Body'],
$provider,
$envelope,
$args
);
return $result;
}
)->then(
function ($result) use ($saveAs) {
if (!empty($saveAs)) {
file_put_contents(
$saveAs,
(string)$result['Body'],
LOCK_EX
);
}
return $result;
}
);
return $promise;
}
/**
* Retrieves an object from S3 and decrypts the data in the 'Body' field.
*
* @param array $args Arguments for retrieving an object from S3 via
* GetObject and decrypting it.
*
* The required configuration argument is as follows:
*
* - @MaterialsProvider: (MaterialsProviderInterface) Provides Cek, Iv, and Cek
* encrypting/decrypting for decryption metadata. May have data loaded
* from the MetadataEnvelope upon decryption.
* - @SecurityProfile: (string) Must be set to 'V2' or 'V2_AND_LEGACY'.
* - 'V2' indicates that only objects encrypted with S3EncryptionClientV2
* content encryption and key wrap schemas are able to be decrypted.
* - 'V2_AND_LEGACY' indicates that objects encrypted with both
* S3EncryptionClientV2 and older legacy encryption clients are able
* to be decrypted.
*
* The optional configuration arguments are as follows:
*
* - SaveAs: (string) The path to a file on disk to save the decrypted
* object data. This will be handled by file_put_contents instead of the
* Guzzle sink.
* - @InstructionFileSuffix: (string|null) Suffix used when looking for an
* instruction file if an InstructionFileMetadataHandler was detected.
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
* is required. Accepts the following options:
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. It is ignored when
* using cbc.
* - @KmsAllowDecryptWithAnyCmk: (bool) This allows decryption with
* KMS materials for any KMS key ID, instead of needing the KMS key ID to
* be specified and provided to the decrypt operation. Ignored for non-KMS
* materials providers. Defaults to false.
*
* @return \Aws\Result GetObject call result with the 'Body' field
* wrapped in a decryption stream with its metadata
* information.
*
* @throws \InvalidArgumentException Thrown when arguments above are not
* passed or are passed incorrectly.
*/
public function getObject(array $args)
{
return $this->getObjectAsync($args)->wait();
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClient.php 0000644 00000032634 14760036270 0017552 0 ustar 00 appendUserAgent($client, 'S3CryptoV' . self::CRYPTO_VERSION);
$this->client = $client;
$this->instructionFileSuffix = $instructionFileSuffix;
}
private static function getDefaultStrategy()
{
return new HeadersMetadataStrategy();
}
/**
* Encrypts the data in the 'Body' field of $args and promises to upload it
* to the specified location on S3.
*
* @param array $args Arguments for encrypting an object and uploading it
* to S3 via PutObject.
*
* The required configuration arguments are as follows:
*
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
* encrypting/decrypting for encryption metadata.
* - @CipherOptions: (array) Cipher options for encrypting data. Only the
* Cipher option is required. Accepts the following:
* - Cipher: (string) cbc|gcm
* See also: AbstractCryptoClient::$supportedCiphers. Note that
* cbc is deprecated and gcm should be used when possible.
* - KeySize: (int) 128|192|256
* See also: MaterialsProvider::$supportedKeySizes
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. It is ignored when
* using cbc. Note if you pass in Aad for gcm encryption, the
* PHP SDK will be able to decrypt the resulting object, but other
* AWS SDKs may not be able to do so.
*
* The optional configuration arguments are as follows:
*
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
* MetadataEnvelope information. Defaults to using a
* HeadersMetadataStrategy. Can either be a class implementing
* MetadataStrategy, a class name of a predefined strategy, or empty/null
* to default.
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
* instruction file if using an InstructionFileMetadataHandler.
*
* @return PromiseInterface
*
* @throws \InvalidArgumentException Thrown when arguments above are not
* passed or are passed incorrectly.
*/
public function putObjectAsync(array $args)
{
$provider = $this->getMaterialsProvider($args);
unset($args['@MaterialsProvider']);
$instructionFileSuffix = $this->getInstructionFileSuffix($args);
unset($args['@InstructionFileSuffix']);
$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
unset($args['@MetadataStrategy']);
$envelope = new MetadataEnvelope();
return Promise\promise_for($this->encrypt(
Psr7\Utils::streamFor($args['Body']),
$args['@CipherOptions'] ?: [],
$provider,
$envelope
))->then(
function ($encryptedBodyStream) use ($args) {
$hash = new PhpHash('sha256');
$hashingEncryptedBodyStream = new HashingStream(
$encryptedBodyStream,
$hash,
self::getContentShaDecorator($args)
);
return [$hashingEncryptedBodyStream, $args];
}
)->then(
function ($putObjectContents) use ($strategy, $envelope) {
list($bodyStream, $args) = $putObjectContents;
if ($strategy === null) {
$strategy = self::getDefaultStrategy();
}
$updatedArgs = $strategy->save($envelope, $args);
$updatedArgs['Body'] = $bodyStream;
return $updatedArgs;
}
)->then(
function ($args) {
unset($args['@CipherOptions']);
return $this->client->putObjectAsync($args);
}
);
}
private static function getContentShaDecorator(&$args)
{
return function ($hash) use (&$args) {
$args['ContentSHA256'] = bin2hex($hash);
};
}
/**
* Encrypts the data in the 'Body' field of $args and uploads it to the
* specified location on S3.
*
* @param array $args Arguments for encrypting an object and uploading it
* to S3 via PutObject.
*
* The required configuration arguments are as follows:
*
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
* encrypting/decrypting for encryption metadata.
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
* is required. Accepts the following options:
* - Cipher: (string) cbc|gcm
* See also: AbstractCryptoClient::$supportedCiphers. Note that
* cbc is deprecated and gcm should be used when possible.
* - KeySize: (int) 128|192|256
* See also: MaterialsProvider::$supportedKeySizes
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. It is ignored when
* using cbc. Note if you pass in Aad for gcm encryption, the
* PHP SDK will be able to decrypt the resulting object, but other
* AWS SDKs may not be able to do so.
*
* The optional configuration arguments are as follows:
*
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for storing
* MetadataEnvelope information. Defaults to using a
* HeadersMetadataStrategy. Can either be a class implementing
* MetadataStrategy, a class name of a predefined strategy, or empty/null
* to default.
* - @InstructionFileSuffix: (string|null) Suffix used when writing to an
* instruction file if an using an InstructionFileMetadataHandler was
* determined.
*
* @return \Aws\Result PutObject call result with the details of uploading
* the encrypted file.
*
* @throws \InvalidArgumentException Thrown when arguments above are not
* passed or are passed incorrectly.
*/
public function putObject(array $args)
{
return $this->putObjectAsync($args)->wait();
}
/**
* Promises to retrieve an object from S3 and decrypt the data in the
* 'Body' field.
*
* @param array $args Arguments for retrieving an object from S3 via
* GetObject and decrypting it.
*
* The required configuration argument is as follows:
*
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
* encrypting/decrypting for decryption metadata. May have data loaded
* from the MetadataEnvelope upon decryption.
*
* The optional configuration arguments are as follows:
*
* - SaveAs: (string) The path to a file on disk to save the decrypted
* object data. This will be handled by file_put_contents instead of the
* Guzzle sink.
*
* - @MetadataStrategy: (MetadataStrategy|string|null) Strategy for reading
* MetadataEnvelope information. Defaults to determining based on object
* response headers. Can either be a class implementing MetadataStrategy,
* a class name of a predefined strategy, or empty/null to default.
* - @InstructionFileSuffix: (string) Suffix used when looking for an
* instruction file if an InstructionFileMetadataHandler is being used.
* - @CipherOptions: (array) Cipher options for decrypting data. A Cipher
* is required. Accepts the following options:
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. It is ignored when
* using cbc.
*
* @return PromiseInterface
*
* @throws \InvalidArgumentException Thrown when required arguments are not
* passed or are passed incorrectly.
*/
public function getObjectAsync(array $args)
{
$provider = $this->getMaterialsProvider($args);
unset($args['@MaterialsProvider']);
$instructionFileSuffix = $this->getInstructionFileSuffix($args);
unset($args['@InstructionFileSuffix']);
$strategy = $this->getMetadataStrategy($args, $instructionFileSuffix);
unset($args['@MetadataStrategy']);
$saveAs = null;
if (!empty($args['SaveAs'])) {
$saveAs = $args['SaveAs'];
}
$promise = $this->client->getObjectAsync($args)
->then(
function ($result) use (
$provider,
$instructionFileSuffix,
$strategy,
$args
) {
if ($strategy === null) {
$strategy = $this->determineGetObjectStrategy(
$result,
$instructionFileSuffix
);
}
$envelope = $strategy->load($args + [
'Metadata' => $result['Metadata']
]);
$provider = $provider->fromDecryptionEnvelope($envelope);
$result['Body'] = $this->decrypt(
$result['Body'],
$provider,
$envelope,
isset($args['@CipherOptions'])
? $args['@CipherOptions']
: []
);
return $result;
}
)->then(
function ($result) use ($saveAs) {
if (!empty($saveAs)) {
file_put_contents(
$saveAs,
(string)$result['Body'],
LOCK_EX
);
}
return $result;
}
);
return $promise;
}
/**
* Retrieves an object from S3 and decrypts the data in the 'Body' field.
*
* @param array $args Arguments for retrieving an object from S3 via
* GetObject and decrypting it.
*
* The required configuration argument is as follows:
*
* - @MaterialsProvider: (MaterialsProvider) Provides Cek, Iv, and Cek
* encrypting/decrypting for decryption metadata. May have data loaded
* from the MetadataEnvelope upon decryption.
*
* The optional configuration arguments are as follows:
*
* - SaveAs: (string) The path to a file on disk to save the decrypted
* object data. This will be handled by file_put_contents instead of the
* Guzzle sink.
* - @InstructionFileSuffix: (string|null) Suffix used when looking for an
* instruction file if an InstructionFileMetadataHandler was detected.
* - @CipherOptions: (array) Cipher options for encrypting data. A Cipher
* is required. Accepts the following options:
* - Aad: (string) Additional authentication data. This option is
* passed directly to OpenSSL when using gcm. It is ignored when
* using cbc.
*
* @return \Aws\Result GetObject call result with the 'Body' field
* wrapped in a decryption stream with its metadata
* information.
*
* @throws \InvalidArgumentException Thrown when arguments above are not
* passed or are passed incorrectly.
*/
public function getObject(array $args)
{
return $this->getObjectAsync($args)->wait();
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/InstructionFileMetadataStrategy.php 0000644 00000005625 14760036270 0022360 0 ustar 00 suffix = empty($suffix)
? self::DEFAULT_FILE_SUFFIX
: $suffix;
$this->client = $client;
}
/**
* Places the information in the MetadataEnvelope to a location on S3.
*
* @param MetadataEnvelope $envelope Encryption data to save according to
* the strategy.
* @param array $args Starting arguments for PutObject, used for saving
* extra the instruction file.
*
* @return array Updated arguments for PutObject.
*/
public function save(MetadataEnvelope $envelope, array $args)
{
$this->client->putObject([
'Bucket' => $args['Bucket'],
'Key' => $args['Key'] . $this->suffix,
'Body' => json_encode($envelope)
]);
return $args;
}
/**
* Uses the strategy's client to retrieve the instruction file from S3 and generates
* a MetadataEnvelope from its contents.
*
* @param array $args Arguments from Command and Result that contains
* S3 Object information, relevant headers, and command
* configuration.
*
* @return MetadataEnvelope
*/
public function load(array $args)
{
$result = $this->client->getObject([
'Bucket' => $args['Bucket'],
'Key' => $args['Key'] . $this->suffix
]);
$metadataHeaders = json_decode($result['Body'], true);
$envelope = new MetadataEnvelope();
$constantValues = MetadataEnvelope::getConstantValues();
foreach ($constantValues as $constant) {
if (!empty($metadataHeaders[$constant])) {
$envelope[$constant] = $metadataHeaders[$constant];
}
}
return $envelope;
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/HeadersMetadataStrategy.php 0000644 00000003127 14760036270 0020605 0 ustar 00 $value) {
$args['Metadata'][$header] = $value;
}
return $args;
}
/**
* Generates a MetadataEnvelope according to the metadata headers from the
* GetObject result.
*
* @param array $args Arguments from Command and Result that contains
* S3 Object information, relevant headers, and command
* configuration.
*
* @return MetadataEnvelope
*/
public function load(array $args)
{
$envelope = new MetadataEnvelope();
$constantValues = MetadataEnvelope::getConstantValues();
foreach ($constantValues as $constant) {
if (!empty($args['Metadata'][$constant])) {
$envelope[$constant] = $args['Metadata'][$constant];
}
}
return $envelope;
}
}
vendor/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTraitV2.php 0000644 00000000771 14760036270 0017710 0 ustar 00 instructionFileSuffix;
}
protected function determineGetObjectStrategy(
$result,
$instructionFileSuffix
) {
if (isset($result['Metadata'][MetadataEnvelope::CONTENT_KEY_V2_HEADER])) {
return new HeadersMetadataStrategy();
}
return new InstructionFileMetadataStrategy(
$this->client,
$instructionFileSuffix
);
}
protected function getMetadataStrategy(array $args, $instructionFileSuffix)
{
if (!empty($args['@MetadataStrategy'])) {
if ($args['@MetadataStrategy'] instanceof MetadataStrategyInterface) {
return $args['@MetadataStrategy'];
}
if (is_string($args['@MetadataStrategy'])) {
switch ($args['@MetadataStrategy']) {
case HeadersMetadataStrategy::class:
return new HeadersMetadataStrategy();
case InstructionFileMetadataStrategy::class:
return new InstructionFileMetadataStrategy(
$this->client,
$instructionFileSuffix
);
default:
throw new \InvalidArgumentException('Could not match the'
. ' specified string in "MetadataStrategy" to a'
. ' predefined strategy.');
}
} else {
throw new \InvalidArgumentException('The metadata strategy that'
. ' was passed to "MetadataStrategy" was unrecognized.');
}
} elseif ($instructionFileSuffix) {
return new InstructionFileMetadataStrategy(
$this->client,
$instructionFileSuffix
);
}
return null;
}
}
vendor/aws/aws-sdk-php/src/S3/Exception/S3MultipartUploadException.php 0000644 00000005150 14760036270 0021735 0 ustar 00 collectPathInfo($error->getCommand());
} elseif ($prev instanceof AwsException) {
$this->collectPathInfo($prev->getCommand());
}
parent::__construct($state, $prev);
}
/**
* Get the Bucket information of the transfer object
*
* @return string|null Returns null when 'Bucket' information
* is unavailable.
*/
public function getBucket()
{
return $this->bucket;
}
/**
* Get the Key information of the transfer object
*
* @return string|null Returns null when 'Key' information
* is unavailable.
*/
public function getKey()
{
return $this->key;
}
/**
* Get the source file name of the transfer object
*
* @return string|null Returns null when metadata of the stream
* wrapped in 'Body' parameter is unavailable.
*/
public function getSourceFileName()
{
return $this->filename;
}
/**
* Collect file path information when accessible. (Bucket, Key)
*
* @param CommandInterface $cmd
*/
private function collectPathInfo(CommandInterface $cmd)
{
if (empty($this->bucket) && isset($cmd['Bucket'])) {
$this->bucket = $cmd['Bucket'];
}
if (empty($this->key) && isset($cmd['Key'])) {
$this->key = $cmd['Key'];
}
if (empty($this->filename) && isset($cmd['Body'])) {
$this->filename = $cmd['Body']->getMetadata('uri');
}
}
}
vendor/aws/aws-sdk-php/src/S3/Exception/S3Exception.php 0000644 00000000301 14760036270 0016657 0 ustar 00 deleted = array_values($deleted);
$this->errors = array_values($errors);
parent::__construct('Unable to delete certain keys when executing a'
. ' DeleteMultipleObjects request: '
. self::createMessageFromErrors($errors));
}
/**
* Create a single error message from multiple errors.
*
* @param array $errors Errors encountered
*
* @return string
*/
public static function createMessageFromErrors(array $errors)
{
return "\n- " . implode("\n- ", array_map(function ($key) {
return json_encode($key);
}, $errors));
}
/**
* Get the errored objects
*
* @return array Returns an array of associative arrays, each containing
* a 'Code', 'Message', and 'Key' key.
*/
public function getErrors()
{
return $this->errors;
}
/**
* Get the successfully deleted objects
*
* @return array Returns an array of associative arrays, each containing
* a 'Key' and optionally 'DeleteMarker' and
* 'DeleterMarkerVersionId'
*/
public function getDeleted()
{
return $this->deleted;
}
}
vendor/aws/aws-sdk-php/src/S3/RegionalEndpoint/Exception/ConfigurationException.php 0000644 00000000531 14760036271 0024450 0 ustar 00
* use Aws\S3\RegionalEndpoint\ConfigurationProvider;
* $provider = ConfigurationProvider::defaultProvider();
* // Returns a ConfigurationInterface or throws.
* $config = $provider()->wait();
*
*
* Configuration providers can be composed to create configuration using
* conditional logic that can create different configurations in different
* environments. You can compose multiple providers into a single provider using
* {@see \Aws\S3\RegionalEndpoint\ConfigurationProvider::chain}. This function
* accepts providers as variadic arguments and returns a new function that will
* invoke each provider until a successful configuration is returned.
*
*
* // First try an INI file at this location.
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
* // Then try an INI file at this location.
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
* // Then try loading from environment variables.
* $c = ConfigurationProvider::env();
* // Combine the three providers together.
* $composed = ConfigurationProvider::chain($a, $b, $c);
* // Returns a promise that is fulfilled with a configuration or throws.
* $promise = $composed();
* // Wait on the configuration to resolve.
* $config = $promise->wait();
*
*/
class ConfigurationProvider extends AbstractConfigurationProvider
implements ConfigurationProviderInterface
{
const ENV_ENDPOINTS_TYPE = 'AWS_S3_US_EAST_1_REGIONAL_ENDPOINT';
const INI_ENDPOINTS_TYPE = 's3_us_east_1_regional_endpoint';
const DEFAULT_ENDPOINTS_TYPE = 'legacy';
public static $cacheKey = 'aws_s3_us_east_1_regional_endpoint_config';
protected static $interfaceClass = ConfigurationInterface::class;
protected static $exceptionClass = ConfigurationException::class;
/**
* Create a default config provider that first checks for environment
* variables, then checks for a specified profile in the environment-defined
* config file location (env variable is 'AWS_CONFIG_FILE', file location
* defaults to ~/.aws/config), then checks for the "default" profile in the
* environment-defined config file location, and failing those uses a default
* fallback set of configuration options.
*
* This provider is automatically wrapped in a memoize function that caches
* previously provided config options.
*
* @param array $config
*
* @return callable
*/
public static function defaultProvider(array $config = [])
{
$configProviders = [self::env()];
if (
!isset($config['use_aws_shared_config_files'])
|| $config['use_aws_shared_config_files'] != false
) {
$configProviders[] = self::ini();
}
$configProviders[] = self::fallback();
$memo = self::memoize(
call_user_func_array('self::chain', $configProviders)
);
if (isset($config['s3_us_east_1_regional_endpoint'])
&& $config['s3_us_east_1_regional_endpoint'] instanceof CacheInterface
) {
return self::cache($memo, $config['s3_us_east_1_regional_endpoint'], self::$cacheKey);
}
return $memo;
}
public static function env()
{
return function () {
// Use config from environment variables, if available
$endpointsType = getenv(self::ENV_ENDPOINTS_TYPE);
if (!empty($endpointsType)) {
return Promise\promise_for(
new Configuration($endpointsType)
);
}
return self::reject('Could not find environment variable config'
. ' in ' . self::ENV_ENDPOINTS_TYPE);
};
}
/**
* Config provider that creates config using a config file whose location
* is specified by an environment variable 'AWS_CONFIG_FILE', defaulting to
* ~/.aws/config if not specified
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile.
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the default directory.
*
* @return callable
*/
public static function ini(
$profile = null,
$filename = null
) {
$filename = $filename ?: (self::getDefaultConfigFilename());
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
return function () use ($profile, $filename) {
if (!is_readable($filename)) {
return self::reject("Cannot read configuration from $filename");
}
$data = \Aws\parse_ini_file($filename, true);
if ($data === false) {
return self::reject("Invalid config file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in config file");
}
if (!isset($data[$profile][self::INI_ENDPOINTS_TYPE])) {
return self::reject("Required S3 regional endpoint config values
not present in INI profile '{$profile}' ({$filename})");
}
return Promise\promise_for(
new Configuration($data[$profile][self::INI_ENDPOINTS_TYPE])
);
};
}
/**
* Fallback config options when other sources are not set.
*
* @return callable
*/
public static function fallback()
{
return function () {
return Promise\promise_for(
new Configuration(self::DEFAULT_ENDPOINTS_TYPE)
);
};
}
/**
* Unwraps a configuration object in whatever valid form it is in,
* always returning a ConfigurationInterface object.
*
* @param mixed $config
* @return ConfigurationInterface
* @throws \InvalidArgumentException
*/
public static function unwrap($config)
{
if (is_callable($config)) {
$config = $config();
}
if ($config instanceof Promise\PromiseInterface) {
$config = $config->wait();
}
if ($config instanceof ConfigurationInterface) {
return $config;
}
if (is_string($config)) {
return new Configuration($config);
}
if (is_array($config) && isset($config['endpoints_type'])) {
return new Configuration($config['endpoints_type']);
}
throw new \InvalidArgumentException('Not a valid S3 regional endpoint '
. 'configuration argument.');
}
} vendor/aws/aws-sdk-php/src/S3/RegionalEndpoint/ConfigurationInterface.php 0000644 00000000645 14760036271 0022462 0 ustar 00 endpointsType = strtolower($endpointsType);
if (!in_array($this->endpointsType, ['legacy', 'regional'])) {
throw new \InvalidArgumentException(
"Configuration parameter must either be 'legacy' or 'regional'."
);
}
}
/**
* {@inheritdoc}
*/
public function getEndpointsType()
{
return $this->endpointsType;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return [
'endpoints_type' => $this->getEndpointsType()
];
}
}
vendor/aws/aws-sdk-php/src/S3/UseArnRegion/Exception/ConfigurationException.php 0000644 00000000520 14760036271 0023546 0 ustar 00
* use Aws\S3\UseArnRegion\ConfigurationProvider;
* $provider = ConfigurationProvider::defaultProvider();
* // Returns a ConfigurationInterface or throws.
* $config = $provider()->wait();
*
*
* Configuration providers can be composed to create configuration using
* conditional logic that can create different configurations in different
* environments. You can compose multiple providers into a single provider using
* {@see Aws\S3\UseArnRegion\ConfigurationProvider::chain}. This function
* accepts providers as variadic arguments and returns a new function that will
* invoke each provider until a successful configuration is returned.
*
*
* // First try an INI file at this location.
* $a = ConfigurationProvider::ini(null, '/path/to/file.ini');
* // Then try an INI file at this location.
* $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini');
* // Then try loading from environment variables.
* $c = ConfigurationProvider::env();
* // Combine the three providers together.
* $composed = ConfigurationProvider::chain($a, $b, $c);
* // Returns a promise that is fulfilled with a configuration or throws.
* $promise = $composed();
* // Wait on the configuration to resolve.
* $config = $promise->wait();
*
*/
class ConfigurationProvider extends AbstractConfigurationProvider
implements ConfigurationProviderInterface
{
const ENV_USE_ARN_REGION = 'AWS_S3_USE_ARN_REGION';
const INI_USE_ARN_REGION = 's3_use_arn_region';
const DEFAULT_USE_ARN_REGION = false;
public static $cacheKey = 'aws_s3_use_arn_region_config';
protected static $interfaceClass = ConfigurationInterface::class;
protected static $exceptionClass = ConfigurationException::class;
/**
* Create a default config provider that first checks for environment
* variables, then checks for a specified profile in the environment-defined
* config file location (env variable is 'AWS_CONFIG_FILE', file location
* defaults to ~/.aws/config), then checks for the "default" profile in the
* environment-defined config file location, and failing those uses a default
* fallback set of configuration options.
*
* This provider is automatically wrapped in a memoize function that caches
* previously provided config options.
*
* @param array $config
*
* @return callable
*/
public static function defaultProvider(array $config = [])
{
$configProviders = [self::env()];
if (
!isset($config['use_aws_shared_config_files'])
|| $config['use_aws_shared_config_files'] != false
) {
$configProviders[] = self::ini();
}
$configProviders[] = self::fallback();
$memo = self::memoize(
call_user_func_array('self::chain', $configProviders)
);
if (isset($config['use_arn_region'])
&& $config['use_arn_region'] instanceof CacheInterface
) {
return self::cache($memo, $config['use_arn_region'], self::$cacheKey);
}
return $memo;
}
/**
* Provider that creates config from environment variables.
*
* @return callable
*/
public static function env()
{
return function () {
// Use config from environment variables, if available
$useArnRegion = getenv(self::ENV_USE_ARN_REGION);
if (!empty($useArnRegion)) {
return Promise\promise_for(
new Configuration($useArnRegion)
);
}
return self::reject('Could not find environment variable config'
. ' in ' . self::ENV_USE_ARN_REGION);
};
}
/**
* Config provider that creates config using a config file whose location
* is specified by an environment variable 'AWS_CONFIG_FILE', defaulting to
* ~/.aws/config if not specified
*
* @param string|null $profile Profile to use. If not specified will use
* the "default" profile.
* @param string|null $filename If provided, uses a custom filename rather
* than looking in the default directory.
*
* @return callable
*/
public static function ini($profile = null, $filename = null)
{
$filename = $filename ?: (self::getDefaultConfigFilename());
$profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default');
return function () use ($profile, $filename) {
if (!is_readable($filename)) {
return self::reject("Cannot read configuration from $filename");
}
// Use INI_SCANNER_NORMAL instead of INI_SCANNER_TYPED for PHP 5.5 compatibility
$data = \Aws\parse_ini_file($filename, true, INI_SCANNER_NORMAL);
if ($data === false) {
return self::reject("Invalid config file: $filename");
}
if (!isset($data[$profile])) {
return self::reject("'$profile' not found in config file");
}
if (!isset($data[$profile][self::INI_USE_ARN_REGION])) {
return self::reject("Required S3 Use Arn Region config values
not present in INI profile '{$profile}' ({$filename})");
}
// INI_SCANNER_NORMAL parses false-y values as an empty string
if ($data[$profile][self::INI_USE_ARN_REGION] === "") {
$data[$profile][self::INI_USE_ARN_REGION] = false;
}
return Promise\promise_for(
new Configuration($data[$profile][self::INI_USE_ARN_REGION])
);
};
}
/**
* Fallback config options when other sources are not set.
*
* @return callable
*/
public static function fallback()
{
return function () {
return Promise\promise_for(
new Configuration(self::DEFAULT_USE_ARN_REGION)
);
};
}
} vendor/aws/aws-sdk-php/src/S3/UseArnRegion/ConfigurationInterface.php 0000644 00000000557 14760036271 0021564 0 ustar 00 useArnRegion = Aws\boolean_value($useArnRegion);
if (is_null($this->useArnRegion)) {
throw new ConfigurationException("'use_arn_region' config option"
. " must be a boolean value.");
}
}
/**
* {@inheritdoc}
*/
public function isUseArnRegion()
{
return $this->useArnRegion;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return [
'use_arn_region' => $this->isUseArnRegion(),
];
}
}
vendor/aws/aws-sdk-php/src/S3/Transfer.php 0000644 00000035472 14760036271 0014363 0 ustar 00 client = $client;
// Prepare the destination.
$this->destination = $this->prepareTarget($dest);
if ($this->destination['scheme'] === 's3') {
$this->s3Args = $this->getS3Args($this->destination['path']);
}
// Prepare the source.
if (is_string($source)) {
$this->sourceMetadata = $this->prepareTarget($source);
$this->source = $source;
} elseif ($source instanceof Iterator) {
if (empty($options['base_dir'])) {
throw new \InvalidArgumentException('You must provide the source'
. ' argument as a string or provide the "base_dir" option.');
}
$this->sourceMetadata = $this->prepareTarget($options['base_dir']);
$this->source = $source;
} else {
throw new \InvalidArgumentException('source must be the path to a '
. 'directory or an iterator that yields file names.');
}
// Validate schemes.
if ($this->sourceMetadata['scheme'] === $this->destination['scheme']) {
throw new \InvalidArgumentException("You cannot copy from"
. " {$this->sourceMetadata['scheme']} to"
. " {$this->destination['scheme']}."
);
}
// Handle multipart-related options.
$this->concurrency = isset($options['concurrency'])
? $options['concurrency']
: MultipartUploader::DEFAULT_CONCURRENCY;
$this->mupThreshold = isset($options['mup_threshold'])
? $options['mup_threshold']
: 16777216;
if ($this->mupThreshold < MultipartUploader::PART_MIN_SIZE) {
throw new \InvalidArgumentException('mup_threshold must be >= 5MB');
}
// Handle "before" callback option.
if (isset($options['before'])) {
$this->before = $options['before'];
if (!is_callable($this->before)) {
throw new \InvalidArgumentException('before must be a callable.');
}
}
// Handle "debug" option.
if (isset($options['debug'])) {
if ($options['debug'] === true) {
$options['debug'] = fopen('php://output', 'w');
}
if (is_resource($options['debug'])) {
$this->addDebugToBefore($options['debug']);
}
}
}
/**
* Transfers the files.
*/
public function promise()
{
// If the promise has been created, just return it.
if (!$this->promise) {
// Create an upload/download promise for the transfer.
$this->promise = $this->sourceMetadata['scheme'] === 'file'
? $this->createUploadPromise()
: $this->createDownloadPromise();
}
return $this->promise;
}
/**
* Transfers the files synchronously.
*/
public function transfer()
{
$this->promise()->wait();
}
private function prepareTarget($targetPath)
{
$target = [
'path' => $this->normalizePath($targetPath),
'scheme' => $this->determineScheme($targetPath),
];
if ($target['scheme'] !== 's3' && $target['scheme'] !== 'file') {
throw new \InvalidArgumentException('Scheme must be "s3" or "file".');
}
return $target;
}
/**
* Creates an array that contains Bucket and Key by parsing the filename.
*
* @param string $path Path to parse.
*
* @return array
*/
private function getS3Args($path)
{
$parts = explode('/', str_replace('s3://', '', $path), 2);
$args = ['Bucket' => $parts[0]];
if (isset($parts[1])) {
$args['Key'] = $parts[1];
}
return $args;
}
/**
* Parses the scheme from a filename.
*
* @param string $path Path to parse.
*
* @return string
*/
private function determineScheme($path)
{
return !strpos($path, '://') ? 'file' : explode('://', $path)[0];
}
/**
* Normalize a path so that it has UNIX-style directory separators and no trailing /
*
* @param string $path
*
* @return string
*/
private function normalizePath($path)
{
return rtrim(str_replace('\\', '/', $path), '/');
}
private function resolveUri($uri)
{
$resolved = [];
$sections = explode('/', $uri);
foreach ($sections as $section) {
if ($section === '.' || $section === '') {
continue;
}
if ($section === '..') {
array_pop($resolved);
} else {
$resolved []= $section;
}
}
return ($uri[0] === '/' ? '/' : '')
. implode('/', $resolved);
}
private function createDownloadPromise()
{
$parts = $this->getS3Args($this->sourceMetadata['path']);
$prefix = "s3://{$parts['Bucket']}/"
. (isset($parts['Key']) ? $parts['Key'] . '/' : '');
$commands = [];
foreach ($this->getDownloadsIterator() as $object) {
// Prepare the sink.
$objectKey = preg_replace('/^' . preg_quote($prefix, '/') . '/', '', $object);
$resolveSink = $this->destination['path'] . '/';
if (isset($parts['Key']) && strpos($objectKey, $parts['Key']) !== 0) {
$resolveSink .= $parts['Key'] . '/';
}
$resolveSink .= $objectKey;
$sink = $this->destination['path'] . '/' . $objectKey;
$command = $this->client->getCommand(
'GetObject',
$this->getS3Args($object) + ['@http' => ['sink' => $sink]]
);
if (strpos(
$this->resolveUri($resolveSink),
$this->destination['path']
) !== 0
) {
throw new AwsException(
'Cannot download key ' . $objectKey
. ', its relative path resolves outside the'
. ' parent directory', $command);
}
// Create the directory if needed.
$dir = dirname($sink);
if (!is_dir($dir) && !mkdir($dir, 0777, true)) {
throw new \RuntimeException("Could not create dir: {$dir}");
}
// Create the command.
$commands []= $command;
}
// Create a GetObject command pool and return the promise.
return (new Aws\CommandPool($this->client, $commands, [
'concurrency' => $this->concurrency,
'before' => $this->before,
'rejected' => function ($reason, $idx, Promise\PromiseInterface $p) {
$p->reject($reason);
}
]))->promise();
}
private function createUploadPromise()
{
// Map each file into a promise that performs the actual transfer.
$files = \Aws\map($this->getUploadsIterator(), function ($file) {
return (filesize($file) >= $this->mupThreshold)
? $this->uploadMultipart($file)
: $this->upload($file);
});
// Create an EachPromise, that will concurrently handle the upload
// operations' yielded promises from the iterator.
return Promise\each_limit_all($files, $this->concurrency);
}
/** @return Iterator */
private function getUploadsIterator()
{
if (is_string($this->source)) {
return Aws\filter(
Aws\recursive_dir_iterator($this->sourceMetadata['path']),
function ($file) { return !is_dir($file); }
);
}
return $this->source;
}
/** @return Iterator */
private function getDownloadsIterator()
{
if (is_string($this->source)) {
$listArgs = $this->getS3Args($this->sourceMetadata['path']);
if (isset($listArgs['Key'])) {
$listArgs['Prefix'] = $listArgs['Key'] . '/';
unset($listArgs['Key']);
}
$files = $this->client
->getPaginator('ListObjects', $listArgs)
->search('Contents[].Key');
$files = Aws\map($files, function ($key) use ($listArgs) {
return "s3://{$listArgs['Bucket']}/$key";
});
return Aws\filter($files, function ($key) {
return substr($key, -1, 1) !== '/';
});
}
return $this->source;
}
private function upload($filename)
{
$args = $this->s3Args;
$args['SourceFile'] = $filename;
$args['Key'] = $this->createS3Key($filename);
$command = $this->client->getCommand('PutObject', $args);
$this->before and call_user_func($this->before, $command);
return $this->client->executeAsync($command);
}
private function uploadMultipart($filename)
{
$args = $this->s3Args;
$args['Key'] = $this->createS3Key($filename);
$filename = $filename instanceof \SplFileInfo ? $filename->getPathname() : $filename;
return (new MultipartUploader($this->client, $filename, [
'bucket' => $args['Bucket'],
'key' => $args['Key'],
'before_initiate' => $this->before,
'before_upload' => $this->before,
'before_complete' => $this->before,
'concurrency' => $this->concurrency,
]))->promise();
}
private function createS3Key($filename)
{
$filename = $this->normalizePath($filename);
$relative_file_path = ltrim(
preg_replace('#^' . preg_quote($this->sourceMetadata['path']) . '#', '', $filename),
'/\\'
);
if (isset($this->s3Args['Key'])) {
return rtrim($this->s3Args['Key'], '/').'/'.$relative_file_path;
}
return $relative_file_path;
}
private function addDebugToBefore($debug)
{
$before = $this->before;
$sourcePath = $this->sourceMetadata['path'];
$s3Args = $this->s3Args;
$this->before = static function (
CommandInterface $command
) use ($before, $debug, $sourcePath, $s3Args) {
// Call the composed before function.
$before and $before($command);
// Determine the source and dest values based on operation.
switch ($operation = $command->getName()) {
case 'GetObject':
$source = "s3://{$command['Bucket']}/{$command['Key']}";
$dest = $command['@http']['sink'];
break;
case 'PutObject':
$source = $command['SourceFile'];
$dest = "s3://{$command['Bucket']}/{$command['Key']}";
break;
case 'UploadPart':
$part = $command['PartNumber'];
case 'CreateMultipartUpload':
case 'CompleteMultipartUpload':
$sourceKey = $command['Key'];
if (isset($s3Args['Key']) && strpos($sourceKey, $s3Args['Key']) === 0) {
$sourceKey = substr($sourceKey, strlen($s3Args['Key']) + 1);
}
$source = "{$sourcePath}/{$sourceKey}";
$dest = "s3://{$command['Bucket']}/{$command['Key']}";
break;
default:
throw new \UnexpectedValueException(
"Transfer encountered an unexpected operation: {$operation}."
);
}
// Print the debugging message.
$context = sprintf('%s -> %s (%s)', $source, $dest, $operation);
if (isset($part)) {
$context .= " : Part={$part}";
}
fwrite($debug, "Transferring {$context}\n");
};
}
}
vendor/aws/aws-sdk-php/src/S3/StreamWrapper.php 0000644 00000073606 14760036271 0015374 0 ustar 00 /Additional info on response behavior: if there is"
. " an internal error in S3 after the request was successfully recieved,"
. " a 200 response will be returned with an S3Exception
embedded"
. " in it; this will still be caught and retried by"
. " RetryMiddleware.
Additional info on behavior of the stream"
. " parameters: Psr7 takes ownership of streams and will automatically close"
. " streams when this method is called with a stream as the Body
"
. " parameter. To prevent this, set the Body
using"
. " GuzzleHttp\Psr7\stream_for
method with a is an instance of"
. " Psr\Http\Message\StreamInterface
, and it will be returned"
. " unmodified. This will allow you to keep the stream in scope.