This commit is contained in:
2018-10-17 11:14:36 +03:00
parent 75a35947e5
commit 04d60d7e2c
2716 changed files with 431449 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
<IfModule mod_php4.c>
php_flag engine Off
</IfModule>
<IfModule mod_php5.c>
php_flag engine Off
</IfModule>
<IfModule mod_php6.c>
php_flag engine Off
</IfModule>
<IfModule mod_cgi.c>
Options -ExecCGI
</IfModule>
RemoveHandler .cgi .pl .py .pyc .pyo .phtml .php .php3 .php4 .php5 .php6 .pcgi .pcgi3 .pcgi4 .pcgi5 .pchi6 .inc
RemoveType .cgi .pl .py .pyc .pyo .phtml .php .php3 .php4 .php5 .php6 .pcgi .pcgi3 .pcgi4 .pcgi5 .pchi6 .inc
SetHandler None
SetHandler default-handler
# Remove both lines below if you want to render HTML files from the upload folder
AddType text/plain .html
AddType text/plain .htm
+85
View File
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CKFinder Changelog</title>
</head>
<body>
<h1 id="ckfinder-changelog">CKFinder Changelog</h1>
<p><a href="http://cksource.com/ckfinder">CKFinder</a></p>
<p>Copyright (c) 2007-2016, CKSource - Frederico Knabben. All rights reserved.</p>
<h2 id="version-3-4-1">Version 3.4.1</h2>
<h3 id="new-features">New Features</h3>
<ul>
<li class="net">Added support for FTP storage.</li></ul>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li class="net">Improved Thumbnail command performance.</li></ul>
<h3 id="localization-updates">Localization Updates</h3>
<ul>
<li>Updated: Greek and Turkish.</li></ul>
<h2 id="version-3-4">Version 3.4</h2>
<h3 id="new-features">New Features</h3>
<ul>
<li>The <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-settings_change_GROUP_NAME"><code>settings:change:GROUP:NAME</code></a> event has a new parameter: <code>previousValue</code>.</li><li>Added the <a href="https://cksource.com/ckfinder/demo#compact-ui">Compact View</a> for displaying files in columns (not supported in IE9).</li><li>The <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Config-cfg-defaultViewType"><code>defaultViewType</code></a> configuration option accepts a new value: <code>compact</code>.</li><li>Added the <code>width</code> parameter for columns defined for List View in the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-listView_columns"><code>listView:columns</code></a> event.</li><li>Added the &quot;OK&quot; button to information dialogs (those created using the <code>dialog:info</code> request).</li><li class="net">Improved configuration validation.</li><li class="net">Added progress tracking for time-consuming operations.</li><li class="net">Added support for IIS virtual directories in the local storage adapter.</li></ul>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li>Added the missing <code>command:before:FileUpload</code>, <code>command:ok:FileUpload</code> and <code>command:error:FileUpload</code> events for uploading files.</li><li>Fixed focus in the settings panel when changing the view type.</li><li>Fixed double <code>toolbar:reset:Main:folder</code> event in compact mode when the user selects a folder in the files pane.</li><li>Fixed keyboard navigation for disabled context menu items.</li><li>Removed the possibility to invoke the Delete File command by keyboard inside a folder with <code>ACL FILE_DELETE</code> set to <code>false</code>.</li><li>Removed the possibility to invoke the Rename File command by keyboard inside a folder with <code>ACL FILE_RENAME</code> set to <code>false</code>.</li><li>Fixed drag&amp;drop uploads in List View.</li><li>Updated Javascript code prettifier for samples.</li><li class="php">Fixed file permission issue occurring during file upload on some IIS server configurations.</li><li class="net">Fixed HTML file extension matching.</li><li class="net">Fixed URL generation for resized images in backends that use the Proxy command.</li><li class="net">Added missing file size field to the SaveImage command response.</li><li class="net">Improved thumbnail caching.</li></ul>
<h3 id="localization-updates">Localization Updates</h3>
<ul>
<li>Added: Swiss German (thanks to <a href="https://twitter.com/mirogrenda">Miro Grenda</a>!) and Ukrainian (thanks to Holovin Yevhen Nikolayevich!).</li><li>Updated: Chinese, Czech, Esperanto, French, German, Kurdish, Latvian, Polish, Russian, Slovakian, Spanish and Turkish.</li></ul>
<h2 id="version-3-3-1">Version 3.3.1</h2>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li class="net">Fixed performance issue in folders with more than 1000 files.</li></ul>
<h2 id="version-3-3">Version 3.3</h2>
<h3 id="new-features">New Features</h3>
<ul>
<li class="php">Added support for Microsoft Azure Storage.</li><li class="net">Added a stable version of the ASP.NET connector.</li><li class="net">Added support for Microsoft Azure Storage.</li><li class="net">Added support for Amazon S3 Storage.</li></ul>
<h3 id="backward-incompatible-changes">Backward Incompatible Changes</h3>
<ul>
<li>Language files have undergone a major reorganization. Obsolete keys were removed.</li><li>Events related to rendering columns in the files pane were changed: <code>listView:file:column:NAME</code>, <code>listView:folder:column:NAME</code>.</li></ul>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li>Performance improvements for rendering the files pane with thousands of items.</li><li>Thumbnail slider enabled in list view when loading CKFinder.</li><li>Choosing files with double click does not fetch the file URL for some remote backends.</li><li>Invalid time when parsing time in the 12-hour clock system.</li><li class="php">Image Edit: Saving an image that exceeds <code>maxWidth</code>/<code>maxHeight</code> throws an error.</li></ul>
<h3 id="localization-updates">Localization Updates</h3>
<ul>
<li>Added: Bosnian.</li><li>Updated: Brazilian Portuguese, Czech, Esperanto, French, German, Italian, Kurdish, Latvian, Persian, Polish, Russian, Spanish and Swedish.</li></ul>
<h2 id="version-3-2-1">Version 3.2.1</h2>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li>In widget mode (in Internet Explorer/Edge only) the CSRF protection was too strict and did not allow for actions that should be allowed.</li></ul>
<h2 id="version-3-2">Version 3.2</h2>
<h3 id="new-features">New Features</h3>
<ul>
<li>Added new view for files pane: a list view with file details.</li><li>Added sorting of files in files pane by name, date and size.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Config-cfg-listViewIconSize"><code>listViewIconSize</code></a> configuration option.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Config-cfg-defaultSortBy"><code>defaultSortBy</code></a> configuration option.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Config-cfg-defaultSortByOrder"><code>defaultSortByOrder</code></a> configuration option.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Config-cfg-defaultViewType"><code>defaultViewType</code></a> configuration option.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-listView_columns"><code>listView:columns</code></a> event.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-listView_file_column_NAME"><code>listView:file:column:NAME</code></a> event.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-listView_folder_column_NAME"><code>listView:folder:column:NAME</code></a> event.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-resources_show_before"><code>resources:show:before</code></a> event.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-dialog_close_NAME"><code>dialog:close:NAME</code></a> event.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-request-file_getActive"><code>file:getActive</code></a> request.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-request-csrf_getToken"><code>csrf:getToken</code></a> request.</li><li>Added the <code>sendPostAsJson</code> parameter to the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-request-command_send"><code>command:send</code></a> request</li><li>The <code>view</code> parameter for the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-request-page_create"><code>page:create</code></a> request is now optional.</li></ul>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li>Individual settings views have a proper name in the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-view_NAME"><code>view:NAME</code></a> event instead of a single <code>Setting</code>.</li><li>Compact mode: Keyboard navigation in breadcrumbs is inverted for RTL languages.</li><li>Fixed an issue that prevented to move or copy over 250 files on a default PHP installation.</li><li>Fixed wrong error message for an empty file name.</li><li>Fixed detection of swipe direction when opening panels on touch screen devices.</li><li>Compact mode: Focus on filter box is lost when typing.</li><li>Thumbnails are not refreshed after editing an image.</li><li>Files filter is not refreshed after clicking a folder.</li><li>Focus is lost after moving files.</li><li>The toolbar is unnecessarily rendered when lazy loading of a folder finishes and a file was selected.</li></ul>
<h3 id="localization-updates">Localization Updates</h3>
<ul>
<li>Added: Esperanto.</li></ul>
<h2 id="version-3-1">Version 3.1</h2>
<h3 id="security-updates">Security Updates</h3>
<p>As a result of security testing and hacking that we did on CKFinder 3 we discovered some potential security concerns in the server-side part of the application.
These issues affected actions that only authenticated users could perform solely in locations specified in your CKFinder backends configuration, but since
in some cases it was possible to skip ACL checks or file extension checks, <strong>an upgrade is highly recommended</strong>.</p>
<h3 id="new-features">New Features</h3>
<ul>
<li>Improved accessibility. Added compatibility with screen readers.</li><li>Reworked keyboard navigation in the entire application. Implemented custom <kbd>Tab</kbd> key support to resolve inconsistency between browsers.</li><li>Added the Keyboard Shortcuts dialog window &mdash; press <kbd>?</kbd> to open it.</li><li>Greatly improved application performance when loading files from remote locations (e.g. Amazon S3).</li><li>Improved performance by reducing the number of situations when the entire files pane is reloaded.</li><li>Improved performance by caching files on subsequent clicks of the folder.</li><li>Added Microsoft Edge compatibility.</li><li>Added preview of PDF files in the gallery.</li><li>Added drag and drop support for files onto folders and breadcrumbs in compact mode.</li><li>Added busy dialog and progress tracking for time-consuming operations.</li><li>Added Proxy command support. It is now possible to view files stored outside the document root or in remote backends in the gallery.</li><li class="php">Added the <a href="http://docs.cksource.com/ckfinder3-php/commands.html#command_proxy"><code>Proxy</code></a> command and the corresponding <a href="http://docs.cksource.com/ckfinder3-php/configuration.html#backend_option_useProxyCommand"><code>useProxyCommand</code></a> backend configuration option.</li><li>Reworked the Choose Scaled dialog window.</li><li>Edit Image feature now warns against closing without saving changes.</li><li>Added the <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-request-folder_getIcon"><code>folder:getIcon</code></a> request.</li><li>Removed Maximize/Minimize buttons in popup mode as the browser provides native controls for it.</li><li class="php">Improved performance by caching file previews.</li><li class="php">Added the <a href="http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_cache"><code>cache</code></a> option that configures cache lifetime for various CKFinder components.</li><li class="php">Added the <a href="http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_tempDirectory"><code>tempDirectory</code></a> option that configures the path to the temporary files folder used by CKFinder.</li><li class="php">Added the <a href="http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_sessionWriteClose"><code>sessionWriteClose</code></a> option that configures whether the connector should close write access to the session to avoid performance issues.</li><li class="php">Added the <a href="http://docs.cksource.com/ckfinder3-php/commands.html#command_operation"><code>Operation</code></a> command that tracks the progress of operation in time-consuming connector commands.</li></ul>
<h3 id="backward-incompatible-changes">Backward Incompatible Changes</h3>
<ul>
<li>Context menu API has undergone major changes. See the updated <a href="http://docs.cksource.com/ckfinder3/#!/guide/dev_contextmenu-section-ckfinder-3.1%2B">context menu documentation</a>.</li><li>Toolbar API events <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-toolbar_reset_NAME"><code>toolbar:reset:NAME</code></a> and <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-event-toolbar_reset_NAME_EVENT"><code>toolbar:reset:NAME:EVENT</code></a> - <code>data.toolbar</code> is now a <code>Backbone.Collection</code>, not an array.</li><li>The <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Application-request-file_getIcon"><code>file:getIcon</code></a> request no longer has the <code>extension</code> parameter. Pass the <code>file</code> parameter instead.</li></ul>
<h3 id="fixed-issues">Fixed Issues</h3>
<ul>
<li>Compact mode: Breadcrumb was hidden when entering a folder with many files.</li><li>Compact mode: There was no way to enter a folder on Android.</li><li>Compact mode: Broken border around CKFinder when viewing the top level folder with resource types.</li><li>Edit Image: Loading an image was not working when the domain name contained a dash character.</li><li>Edit Image: Context menu item was enabled when the user had no permissions to edit the file.</li><li>Edit Image: Resize option was not available in compact mode.</li><li>Edit Image: Reset button should have been disabled if there was nothing to reset.</li><li>Edit Image: Fixed validation of provided values in the edit image controls.</li><li>Edit Image: There was no information about failing to save a file.</li><li>Edit Image: It was possible to crop an area bigger than the edited image.</li><li>Edit Image: Fixed progress bar behavior.</li><li>Filter input: Clearing filter input did not result in showing all files in IE9.</li><li>Filter input: Filter should remember its state if it is still active.</li><li>Focusing elements: Focus in context menu and toolbar did not cycle.</li><li>Focusing elements: Focus was lost after resizing an image.</li><li>Focusing elements: Focus was not remembered when returning to the files pane.</li><li>Focusing elements: Pressing <kbd>Tab</kbd> should focus the first item in a component (file, folder, toolbar button), not the container.</li><li>Focusing elements: Lock focus chain within the settings panel.</li><li>Scrolling files: Scrolling on mobile devices was troublesome and sometimes did not work at all.</li><li>Scrolling files: Files were unnecessarily selected on scroll on mobile devices.</li><li>Thumbnails: Problem with thumbnails in widgets/popups when <a href="http://docs.cksource.com/ckfinder3/#!/api/CKFinder.Config-cfg-connectorPath"><code>connectorPath</code></a> did not include the domain.</li><li>Thumbnails: Thumbnails loaded with a significant delay for a larger number of files.</li><li>Thumbnails: Thumbnails were not refreshed in certain scenarios.</li><li>iOS/Safari: Thumbnails were not shown.</li><li>iOS/Safari: Popup sample did not work.</li><li>iOS/Safari: The configured height of the widget was ignored, instead CKFinder height depended on the number of files inside.</li><li>iOS/Safari: Downloading files did not work.</li><li>Setting global configuration did not work for widgets and popups.</li><li>There was no Close button available after file upload.</li><li>It should not be possible to move dialog windows.</li><li>Fixed various UI glitches in the RTL interface.</li><li>Video or image sometimes overlapped file preview controls in file preview.</li><li>Delete files confirmation dialog did not appear when the files pane was scrolled.</li><li>Changed the confusing empty folder message in the read-only mode.</li><li>Removed the notification about correctly uploaded file in IE9.</li><li>Resolved an issue with validating license names that started with <code>www[0-9]</code> or that contained upper case letters.</li><li>In certain scenarios not all available toolbar buttons were shown in CKFinder.</li><li>Fixed the look of the &quot;More&quot; drop-down in the toolbar.</li><li class="php">Empty <code>directory</code> key in the backend definition resulted in a double slash in the file URL.</li></ul>
<h3 id="localization-updates">Localization Updates</h3>
<ul>
<li>Added: Basque, Kurdish.</li><li>Updated: Brazilian Portuguese, Chinese, Croatian, Czech, Danish, Esperanto, Estonian, French, German, Greek, Hungarian, Italian, Korean, Norwegian, Persian, Polish and Russian.</li></ul>
<h2 id="version-3-0">Version 3.0</h2>
<p>A brand new version of CKFinder, currently available only for PHP. For an overview of new features, see the <a href="http://cksource.com/blog/CKFinder-3.0-for-PHP-Released">announcement about CKFinder 3.0 for PHP</a>.</p>
<ul>
<li>New architecture based on jQuery, jQuery Mobile, Backbone, Marionette, and RequireJS.</li><li>Built-in image editor.</li><li>Customizable skins compatible with jQuery UI Themeroller.</li><li>Full responsiveness, great mobile support.</li><li>Cloud storage support (Amazon S3, Dropbox) and FTP connector.</li></ul>
</body>
</html>
+295
View File
@@ -0,0 +1,295 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CKFinder License</title>
</head>
<body>
<h2>CKFinder License Agreement, Version 3.1</h2>
<p>This document (&ldquo;Agreement&rdquo;) is a legal agreement between You, either an individual or a Legal Entity, and
CKSource sp. z o.o. sp.k., with its registered office in Warsaw, at ul. Zygmunta Słomińskiego 15 lok. 508, 00-195
Warsaw, Poland, NIP number 5252621537, REGON: 361766395 (&ldquo;CKSource&rdquo;), covering Your permissions to
reproduce and distribute the Software under the License terms defined hereby.</p>
<h3>1. Definitions</h3>
<p>&ldquo;Agreement Name&rdquo; shall mean the name used to reference this Agreement in any context, which
is &ldquo;CKFinder License Agreement 3.1&rdquo; or &ldquo;CKFinder License 3.1&rdquo;.</p>
<p>&ldquo;Software&rdquo; or &ldquo;CKFinder&rdquo; shall mean the copyrighted material owned by CKSource,
subject to the terms of this License. The Software is publicly, uniquely, and in its entirety recognizable
by the &ldquo;CKFinder&rdquo; name (&ldquo;Software Name&rdquo;).</p>
<p>&ldquo;Software Release&rdquo; or &ldquo;Release&rdquo; shall mean a set of files distributed by CKSource, or
anyone authorized to distribute it, that represents the Software. A Release is uniquely identified by the
Software Name and a code. Such code is generally referenced as the Software version or revision number, or a
combination of both.</p>
<p>&ldquo;Release Date&rdquo; shall mean the day that CKSource started distributing a Release.</p>
<p>&ldquo;Product&rdquo; shall mean a single computer program or one or more websites (&ldquo;Program&rdquo;)
(i) owned by You, or (ii) to which the owner grants You the permission to act in behalf of the owner for the
purposes of this Agreement. A Program family or a group of Programs does not constitute a Product for the
scope of this Agreement. A Program that goes in competition with the Software in the marketplace does not
constitute a valid Product for the scope of this Agreement.</p>
<p>&ldquo;Development Server&rdquo; shall mean a computer with one or more computer central processing units
(CPU&rsquo;s) that operates for the exclusive purpose of software development or software testing.</p>
<p>&ldquo;Development Activity&rdquo; shall mean the act of interacting with the Software or one of its
Releases, in any number of Products owned or produced by You, with the intent of installation,
customization, configuration, testing, documentation, or any other software development activity related to
the Software.</p>
<p>&ldquo;Developer&rdquo; shall mean an authorized person designated by You to perform Development
Activities.</p>
<p>&ldquo;Production Website&rdquo; shall mean a Product with the Software installed, which already had
Development Activities performed, and that has been delivered to end-users for production usage. Maintenance
activities performed on Production Websites, excluding software development activities related to the
Software, are not considered Development Activities.</p>
<p>&ldquo;Hostname&rdquo; shall mean a unique name by which a website is reachable in a network. This includes,
but is not limited to, a website IP address. (For example, if a website is reachable by the Internet
address &ldquo;<a href="http://www.example.com/">http://www.example.com/</a>&rdquo;, the Hostname
is &ldquo;<a href="http://www.example.com/">www.example.com</a>&rdquo;.)</p>
<p>&ldquo;Legal Entity&rdquo; shall mean the union of the acting entity and all other entities that control, are
controlled by, or are under common control of that entity. For the purposes of this definition, &quot;control&quot;
means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii)
beneficial ownership of such entity.</p>
<p>&ldquo;Effective Date&rdquo; means the date on which the Legal Evidence is made effective.</p>
<p>&ldquo;You&rdquo; (or &ldquo;Your&rdquo;) shall mean an individual or a Legal Entity exercising permissions
granted by the License and accepting this Agreement.</p>
<h3>2. Grant of License</h3>
<p>Subject to the terms of this Agreement, CKSource hereby grants You, in one of the License Models described in
Section 4, a non-exclusive, perpetual, irrevocable, royalty free, worldwide license (&ldquo;License&rdquo;)
to use, reproduce, modify, and distribute a Software Release in a collective work assembled with the
Product.</p>
<p>You should carefully read the following terms and conditions before using, installing, copying, or
distributing the Software. Unless otherwise agreed in writing by CKSource, your use, installation, copying,
or distribution of the Software indicates your acceptance of this License.</p>
<h3>3. Scope of License</h3>
<p>All rights of any kind to the Software, which are not expressly granted in this Agreement, are entirely and
exclusively reserved to and by CKSource. The Software is protected by applicable national and international
laws and treaties.</p>
<p>You may use, install, copy, and distribute the Software solely as provided in this Agreement. You may not
rent, lease, loan, sublicense, reverse engineer, decompile, disassemble, or create derivative works based on
the Software, in whole or in part, nor permit anyone else to do so.</p>
<p>It is agreed that in exchange for the license set forth herein, you will pay a license fee (&ldquo;License
Fee&rdquo;). The fee value will be specified by CKSource at the moment of the purchase.</p>
<h3>4. License Models</h3>
<p>The Software may be assembled with the Product and redistributed in respect of one, and only one, of the
following models (&ldquo;License Models&rdquo;) of Your choice:</p>
<ul>
<li>a)&nbsp;(&ldquo;Basic&rdquo;) Assembling the Software into a Product distributed as a single website (&ldquo;Site&rdquo;).
The Site URL (&ldquo;URL&rdquo;) must be specified at the moment of purchase. The license will not be
valid for sub-domains of the specified URL. This license includes 2 Developer licenses that cover
Development Activities exclusively related to the Site.
</li>
<li>b)&nbsp;(&ldquo;Professional&rdquo;) Assembling the Software with a maximum of 3 websites (&ldquo;Professional
Sites&rdquo;) owned or produced by You. Sites produced by third-parties with Software produced by You
are not included in this License Model. This license includes 5 Developer licenses that cover
Development Activities exclusively related to Professional Sites.
</li>
<li>c)&nbsp;(&ldquo;Developer&rdquo;) License granting Development Activities to a single Developer
Person.
</li>
</ul>
<p>A valid Developer license is required for every single Developer Person. Developer licenses cannot be shared
among different Developer Persons. Non-Development Activities on Production Websites do not require
Developer licenses.</p>
<p>License Fee values may differ depending on the license model.</p>
<h3>5. Unlicensed Copies</h3>
<p>If You did not pay License Fee, You may use unlicensed copies of CKFinder for the exclusive purpose of
demonstration. In this case You will be using CKFinder in &quot;demo mode&quot;. Without derogating from the
forgoing, You may not use CKFinder in &quot;demo mode&quot; for commercial purposes. CKFinder shall only be
used for evaluation purposes and may not be used or disclosed for any other purposes, including, without
limitation, for external distribution. You may not remove the demo notices from the interface nor disable
the ability to display such notices or otherwise modify CKFinder. Product support, if any, is not offered
for CKFinder in &quot;demo mode&quot;.</p>
<h3>6. Agreement Acceptance</h3>
<p>This Agreement is automatically accepted by both parties as long as You are in possession of legal evidence (&ldquo;Legal
Evidence&rdquo;) that the acceptance has taken place. The Legal Evidence can be represented by (i) a copy of
this Agreement signed by You and CKSource or (ii) a valid Certificate of License Ownership, provided by
CKSource and addressed to You. The Legal Evidence must precisely indicate this Agreement Name, the Software
Name, the License Model You have chosen, and the following restrictive information, if applicable:</p>
<ul>
<li>a)&nbsp;If You have chosen the Basic License Model, the Site Hostname must be indicated in the Legal
Evidence. This Agreement will be valid for the Software assembled with the Site publicized under the
Site Hostname only. Other Sites are excluded from this Agreement as long as a Legal Evidence is not
produced for each of these Sites.
</li>
</ul>
<p>Legal Evidences for different combinations of License Models, Sites, and Products will not restrict each
other and will not interfere in the rights granted to You by each of them.</p>
<p>Legal Evidences are not transferable to different Sites and Products.</p>
<h3>7. Limitation on Releases</h3>
<p>This agreement is valid for all Releases of the Software with Release Dates within or before the 365 days
that follow the Effective Date (&ldquo;Upgrade Period&rdquo;). CKSource has no obligation to provide you any
Release that is not released for general distribution to other CKSource licensees. Nothing in this Agreement
shall be construed to obligate CKSource to provide additional Releases to You under any circumstances.</p>
<h3>8. Support</h3>
<p>CKSource shall provide support for Developers covered by valid Developer licenses for the period of 365 days
following the Effective Date (&ldquo;Support Period&rdquo;). Support shall be limited to electronic
messaging access. CKSource shall keep You informed, either per CKSource readiness or by following your
request, including changes to it, about the rules and procedures that You must perform to enjoy support
under the terms of this Agreement. Support topics shall be limited to the following (each a &ldquo;Support
Request&rdquo;):</p>
<ul>
<li>a)&nbsp;problem solving,</li>
<li>b)&nbsp;bug reporting,</li>
<li>c)&nbsp;and documentation clarification.</li>
</ul>
<p>The number of Support Requests that CKSource is entitled to accept from You is limited to 2 per month if You
have chosen the Basic license model, or 5 per month if You have chosen the Professional license model.
CKSource may refuse Support Requests that exceed these limits.</p>
<p>CKSource is not in any way obliged to perform bug fixing or custom development activities as a result of a
Support Request.</p>
<h3>9. License Key</h3>
<p>Following a valid License purchase, a unique license key (the &quot;License Key&quot;) may be provided to
You, which allows Software activation. The License Key is subject to the restrictions set forth in this
License and may not be disclosed or distributed in any way. The disclosure or distribution of the License
Key shall constitute a breach of this License, the effect of which shall be the automatic termination and
revocation of any and all rights granted herein.</p>
<h3>10. Source Code</h3>
<p>The original source code (&quot;Source Code&quot;) of the Software may be distributed by CKSource alongside
its executable version, or as an integral part of it. You may modify and compile the Source Code. The Source
Code or its modified version can be copied and distributed exclusively within the scope of this license, as
defined in the &quot;License Model&quot; terms of this license, as long as a valid license has been
purchased for the distribution target. CKSource retains all rights over the Source Code and all its modified
versions. Redistributions of the Source Code and modified versions of it must contain the original headers
and copyright notices. Modifications to the Source Code must be explicitly and entirely identified in the
Source Code files. This section of the license supersedes all modification restrictions imposed by other
sections. You are not allowed to remove copyright notices nor make changes to the license validation code
present in the Source Code.</p>
<h3>11. License Fee</h3>
<p>In consideration for the License granted in this Agreement during the term of this Agreement, You agreed to
pay to CKSource a one time fee (&ldquo;License Fee&rdquo;).</p>
<p>In consideration for the Software upgrades access and support services provided during the Upgrade Period and
Support Period, You shall pay a yearly fee (&ldquo;Annual Support and Upgrade Fee&rdquo;).</p>
<p>The License Fee and the Annual Support and Upgrade Fee amount shall be specified by CKSource at the moment of
the purchase as a single unified price.</p>
<p>The fees listed in this Agreement do not include taxes. If CKSource is required to pay any sales, use,
property, excise, value added, gross receipts, withholding or other taxes levied on the Software or support
under this Agreement or on Your use thereof, then such taxes shall be billed to and paid by You. This
Section does not apply to taxes based on CKSource net income, franchise taxes or CKSource&rsquo;s employer
contributions and taxes.</p>
<p>You understand that CKSource uses third-party paying agents to process selected payments. You understand that
until payments are not received and verified by the paying agent, this Agreement is not valid.</p>
<p>All payments made hereunder are nonrefundable. You may not withhold or set off any amounts due under this
Agreement. Failure to pay any fee when due shall constitute a material breach of this Agreement. In
addition, if You fail to make any payments when due for support, upon written notice to You, CKSource shall
cease providing support.</p>
<h3>12. Automatic Renewal</h3>
<p>You may opt to automatically extend the expiration date for both the Upgrade Period and Support Period by
successive cycles of 365 days (each one a &ldquo;Renewal&rdquo;). In such case, you agree to pay a fee for
each Renewal at the beginning of each cycle. The annual Renewal fee shall be informed by CKSource at the
moment of the purchase and optionally agreed by you.</p>
<p>If you opted to have automatic Renewals, CKSource shall send you a payment request for the Renewal fee at any
moment following the first day of each Renewal period. You shall perform the full payment in 30 calendar
days following the payment request.</p>
<p>Both parties can discontinue the automatic Renewals by written notification to the other party before the
beginning of extension cycles.</p>
<h3>13. Reservation of Rights and Ownership</h3>
<p>CKSource reserves all rights not expressly granted to You in this Agreement. The Software is protected by
copyright and other intellectual property laws and treaties. CKSource owns the title, copyright, and other
intellectual property rights in the Software. The Software is licensed, not sold. This Agreement does not
grant you any rights to the Software&#39;s trademarks or services.</p>
<h3>14. Termination</h3>
<p>Without prejudice to any other rights, this Agreement automatically terminates if You fail to comply with the
terms and conditions of this Agreement. You may terminate this Agreement at any time without cause. In case
of termination, in any circumstance, payments issued by You will not be reimbursed.</p>
<p>You shall immediately discontinue distribution of Product, assembled with the Software, upon expiration or
termination of this Agreement.</p>
<h3>15. Warranty</h3>
<p>CKSource warrants that it has full title and ownership to the Software and has the authority to grant the
license hereunder. To the best of CKSource&#39;s knowledge the Software does not infringe upon the
intellectual property rights of any third party and that CKSource did not receive any notice regarding any
alleged infringement thereof.</p>
<h3>16. Disclaimer of Warranties</h3>
<p>With the exclusion of warranties explicitly mentioned in Section 15, the Software and its related material
are provided &ldquo;AS IS&rdquo; and without warranty of any kind. CKSource expressly disclaims all other
warranties, expressed or implied, including, but not limited to, the implied warranties of merchantability
and fitness for a particular purpose.</p>
<h3>17. Exclusion of Incidental, Consequential and Certain Other Damages</h3>
<p>TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL CKSOURCE BE LIABLE FOR ANY SPECIAL,
INCIDENTAL, PUNITIVE, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, BUT NOT LIMITED TO, DAMAGES
FOR LOSS OF PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION, FOR PERSONAL INJURY,
FOR LOSS OF PRIVACY, FOR FAILURE TO MEET ANY DUTY INCLUDING OF GOOD FAITH OR OF REASONABLE CARE, FOR
NEGLIGENCE, AND FOR ANY OTHER PECUNIARY OR OTHER LOSS WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO
THE USE OF OR INABILITY TO USE THE SOFTWARE, THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER
SERVICES, INFORMATON, SOFTWARE, AND RELATED CONTENT THROUGH THE SOFTWARE OR OTHERWISE ARISING OUT OF THE USE
OF THE SOFTWARE, OR OTHERWISE UNDER OR IN CONNECTION WITH ANY PROVISION OF THIS AGREEMENT, EVEN IN THE EVENT
OF THE FAULT, TORT (INCLUDING NEGLIGENCE), MISREPRESENTATION, STRICT LIABILITY, BREACH OF CONTRACT, AND EVEN
IF THE OWNER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
<h3>18. Limitation of Liability</h3>
<p>In no event shall CKSource&rsquo;s liability exceed the license fee paid, if any.</p>
<h3>19. Governing Law and Venue</h3>
<p>This Agreement shall be construed and controlled by the laws of Poland, and You and CKSource further consent
to exclusive jurisdiction by the courts of Poland.</p>
<p>END OF AGREEMENT TERMS</p>
</body>
</html>
+42
View File
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CKFinder Readme</title>
</head>
<body>
<h1 id="ckfinder-3-for-php">CKFinder 3 for PHP</h1>
<p>Thank you for choosing CKFinder.</p>
<h2 id="feedback">Feedback</h2>
<p>Use <a href="https://github.com/ckfinder/ckfinder/issues">https://github.com/ckfinder/ckfinder/issues</a> to report issues in CKFinder 3 (and its documentation) or submit
feature requests.</p>
<p>If you are unsure what information to provide when reporting a bug, check <a href="http://docs.cksource.com/ckfinder3/#!/guide/dev_issues_readme">http://docs.cksource.com/ckfinder3/#!/guide/dev_issues_readme</a></p>
<h2 id="translations">Translations</h2>
<p>In order to submit translations for CKFinder please visit <a href="https://github.com/ckfinder/ckfinder-translations">https://github.com/ckfinder/ckfinder-translations</a></p>
<h2 id="documentation">Documentation</h2>
<p>CKFinder is made from two parts: the client side part and the server side connector(s).</p>
<p>The client side part is common across all distributions (PHP and ASP.NET, Java in the future), while
the server side parts are different for each language, that&#39;s why there are multiple documentation websites available.</p>
<h3 id="ckfinder-3-documentation-http-docs-cksource-com-ckfinder3-">CKFinder 3 Documentation - <a href="http://docs.cksource.com/ckfinder3/">http://docs.cksource.com/ckfinder3/</a></h3>
<p>This website contains documentation about the client side part of CKFinder, common for all versions of CKFinder 3
and includes information about:</p>
<ul>
<li>Integrating CKFinder with your website or with CKEditor.</li>
<li>Client side configuration options.</li>
<li>API documentation.</li>
<li>Tutorials about creating JavaScript plugins.</li>
<li>Tutorials about creating skins.</li>
</ul>
<h3 id="ckfinder-3-for-php-documentation-http-docs-cksource-com-ckfinder3-php-">CKFinder 3 for PHP Documentation - <a href="http://docs.cksource.com/ckfinder3-php/">http://docs.cksource.com/ckfinder3-php/</a></h3>
<p>This website contains documentation about the PHP connector for CKFinder 3 and includes information about:</p>
<ul>
<li>Enabling the PHP connector.</li>
<li>Configuring the PHP connector.</li>
<li>Tutorials about creating PHP plugins.</li>
</ul>
<h2 id="license">License</h2>
<p>Copyright (c) 2007-2016, CKSource - Frederico Knabben. All rights reserved.</p>
<p>To purchase a license for CKFinder visit <a href="http://cksource.com/ckfinder">http://cksource.com/ckfinder</a></p>
</body>
</html>
+21
View File
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<!--
Copyright (c) 2007-2016, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://cksource.com/ckfinder/license
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<title>CKFinder 3 - File Browser</title>
</head>
<body>
<script src="ckfinder.js"></script>
<script>
CKFinder.start();
</script>
</body>
</html>
File diff suppressed because one or more lines are too long
+14
View File
@@ -0,0 +1,14 @@
/*
Copyright (c) 2007-2016, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://cksource.com/ckfinder/license
*/
var config = {};
// Set your configuration options below.
// Examples:
// config.language = 'pl';
// config.skin = 'jquery-mobile';
CKFinder.define( config );
+175
View File
@@ -0,0 +1,175 @@
<?php
/*
* CKFinder Configuration File
*
* For the official documentation visit http://docs.cksource.com/ckfinder3-php/
*/
/*============================ PHP Error Reporting ====================================*/
// http://docs.cksource.com/ckfinder3-php/debugging.html
// Production
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
ini_set('display_errors', 0);
// Development
// error_reporting(E_ALL);
// ini_set('display_errors', 1);
/*============================ General Settings =======================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html
$config = array();
/*============================ Enable PHP Connector HERE ==============================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_authentication
$config['authentication'] = function () {
return false;
};
/*============================ License Key ============================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_licenseKey
$config['licenseName'] = '';
$config['licenseKey'] = '';
/*============================ CKFinder Internal Directory ============================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_privateDir
$config['privateDir'] = array(
'backend' => 'default',
'tags' => '.ckfinder/tags',
'logs' => '.ckfinder/logs',
'cache' => '.ckfinder/cache',
'thumbs' => '.ckfinder/cache/thumbs',
);
/*============================ Images and Thumbnails ==================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_images
$config['images'] = array(
'maxWidth' => 1600,
'maxHeight' => 1200,
'quality' => 80,
'sizes' => array(
'small' => array('width' => 480, 'height' => 320, 'quality' => 80),
'medium' => array('width' => 600, 'height' => 480, 'quality' => 80),
'large' => array('width' => 800, 'height' => 600, 'quality' => 80)
)
);
/*=================================== Backends ========================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_backends
$config['backends'][] = array(
'name' => 'default',
'adapter' => 'local',
'baseUrl' => '/ckfinder/userfiles/',
// 'root' => '', // Can be used to explicitly set the CKFinder user files directory.
'chmodFiles' => 0777,
'chmodFolders' => 0755,
'filesystemEncoding' => 'UTF-8',
);
/*================================ Resource Types =====================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_resourceTypes
$config['defaultResourceTypes'] = '';
$config['resourceTypes'][] = array(
'name' => 'Files', // Single quotes not allowed.
'directory' => 'files',
'maxSize' => 0,
'allowedExtensions' => '7z,aiff,asf,avi,bmp,csv,doc,docx,fla,flv,gif,gz,gzip,jpeg,jpg,mid,mov,mp3,mp4,mpc,mpeg,mpg,ods,odt,pdf,png,ppt,pptx,pxd,qt,ram,rar,rm,rmi,rmvb,rtf,sdc,sitd,swf,sxc,sxw,tar,tgz,tif,tiff,txt,vsd,wav,wma,wmv,xls,xlsx,zip',
'deniedExtensions' => '',
'backend' => 'default'
);
$config['resourceTypes'][] = array(
'name' => 'Images',
'directory' => 'images',
'maxSize' => 0,
'allowedExtensions' => 'bmp,gif,jpeg,jpg,png',
'deniedExtensions' => '',
'backend' => 'default'
);
/*================================ Access Control =====================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_roleSessionVar
$config['roleSessionVar'] = 'CKFinder_UserRole';
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_accessControl
$config['accessControl'][] = array(
'role' => '*',
'resourceType' => '*',
'folder' => '/',
'FOLDER_VIEW' => true,
'FOLDER_CREATE' => true,
'FOLDER_RENAME' => true,
'FOLDER_DELETE' => true,
'FILE_VIEW' => true,
'FILE_CREATE' => true,
'FILE_RENAME' => true,
'FILE_DELETE' => true,
'IMAGE_RESIZE' => true,
'IMAGE_RESIZE_CUSTOM' => true
);
/*================================ Other Settings =====================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html
$config['overwriteOnUpload'] = false;
$config['checkDoubleExtension'] = true;
$config['disallowUnsafeCharacters'] = false;
$config['secureImageUploads'] = true;
$config['checkSizeAfterScaling'] = true;
$config['htmlExtensions'] = array('html', 'htm', 'xml', 'js');
$config['hideFolders'] = array('.*', 'CVS', '__thumbs');
$config['hideFiles'] = array('.*');
$config['forceAscii'] = false;
$config['xSendfile'] = false;
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_debug
$config['debug'] = false;
/*==================================== Plugins ========================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_plugins
$config['pluginsDirectory'] = __DIR__ . '/plugins';
$config['plugins'] = array();
/*================================ Cache settings =====================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_cache
$config['cache'] = array(
'imagePreview' => 24 * 3600,
'thumbnails' => 24 * 3600 * 365,
'proxyCommand' => 0
);
/*============================ Temp Directory settings ================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_tempDirectory
$config['tempDirectory'] = sys_get_temp_dir();
/*============================ Session Cause Performance Issues =======================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_sessionWriteClose
$config['sessionWriteClose'] = true;
/*================================= CSRF protection ===================================*/
// http://docs.cksource.com/ckfinder3-php/configuration.html#configuration_options_csrfProtection
$config['csrfProtection'] = true;
/*============================== End of Configuration =================================*/
// Config must be returned - do not change it.
return $config;
@@ -0,0 +1,20 @@
<?php
/*
* CKFinder
* ========
* http://cksource.com/ckfinder
* Copyright (c) 2007-2016, CKSource - Frederico Knabben. All rights reserved.
*
* The software, this file and its contents are subject to the CKFinder
* License. Please read the license.txt file before using, installing, copying,
* modifying or distribute this file or part of its contents. The contents of
* this file is part of the Source Code of CKFinder.
*/
require_once __DIR__ . '/vendor/autoload.php';
use CKSource\CKFinder\CKFinder;
$ckfinder = new CKFinder(__DIR__ . '/../../../config.php');
$ckfinder->run();
+7
View File
@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInitc8ccb46a47efb4379a7eebb3f851e19d::getLoader();
@@ -0,0 +1,106 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common;
use Aws\Common\Facade\Facade;
use Guzzle\Service\Builder\ServiceBuilder;
use Guzzle\Service\Builder\ServiceBuilderLoader;
/**
* Base class for interacting with web service clients
*/
class Aws extends ServiceBuilder
{
/**
* @var string Current version of the SDK
*/
const VERSION = '2.8.31';
/**
* Create a new service locator for the AWS SDK
*
* You can configure the service locator is four different ways:
*
* 1. Use the default configuration file shipped with the SDK that wires class names with service short names and
* specify global parameters to add to every definition (e.g. key, secret, credentials, etc)
*
* 2. Use a custom configuration file that extends the default config and supplies credentials for each service.
*
* 3. Use a custom config file that wires services to custom short names for services.
*
* 4. If you are on Amazon EC2, you can use the default configuration file and not provide any credentials so that
* you are using InstanceProfile credentials.
*
* @param array|string $config The full path to a .php or .js|.json file, or an associative array of data
* to use as global parameters to pass to each service.
* @param array $globalParameters Global parameters to pass to every service as it is instantiated.
*
* @return Aws
*/
public static function factory($config = null, array $globalParameters = array())
{
if (!$config) {
// If nothing is passed in, then use the default configuration file with credentials from the environment
$config = self::getDefaultServiceDefinition();
} elseif (is_array($config)) {
// If an array was passed, then use the default configuration file with parameter overrides
$globalParameters = $config;
$config = self::getDefaultServiceDefinition();
}
$loader = new ServiceBuilderLoader();
$loader->addAlias('_aws', self::getDefaultServiceDefinition())
->addAlias('_sdk1', __DIR__ . '/Resources/sdk1-config.php');
return $loader->load($config, $globalParameters);
}
/**
* Get the full path to the default service builder definition file
*
* @return string
*/
public static function getDefaultServiceDefinition()
{
return __DIR__ . '/Resources/aws-config.php';
}
/**
* Returns the configuration for the service builder
*
* @return array
*/
public function getConfig()
{
return $this->builderConfig;
}
/**
* Enables the facades for the clients defined in the service builder
*
* @param string|null $namespace The namespace that the facades should be mounted to. Defaults to global namespace
*
* @return Aws
* @deprecated "Facades" are being removed in version 3.0 of the SDK.
*/
public function enableFacades($namespace = null)
{
Facade::mountFacades($this, $namespace);
return $this;
}
}
@@ -0,0 +1,283 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Aws;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\TransferException;
use Aws\Common\RulesEndpointProvider;
use Aws\Common\Signature\EndpointSignatureInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Signature\SignatureListener;
use Aws\Common\Waiter\WaiterClassFactory;
use Aws\Common\Waiter\CompositeWaiterFactory;
use Aws\Common\Waiter\WaiterFactoryInterface;
use Aws\Common\Waiter\WaiterConfigFactory;
use Guzzle\Common\Collection;
use Guzzle\Http\Exception\CurlException;
use Guzzle\Http\QueryAggregator\DuplicateAggregator;
use Guzzle\Service\Client;
use Guzzle\Service\Description\ServiceDescriptionInterface;
/**
* Abstract AWS client
*/
abstract class AbstractClient extends Client implements AwsClientInterface
{
/** @var CredentialsInterface AWS credentials */
protected $credentials;
/** @var SignatureInterface Signature implementation of the service */
protected $signature;
/** @var WaiterFactoryInterface Factory used to create waiter classes */
protected $waiterFactory;
/** @var DuplicateAggregator Cached query aggregator*/
protected $aggregator;
/**
* {@inheritdoc}
*/
public static function getAllEvents()
{
return array_merge(Client::getAllEvents(), array(
'client.region_changed',
'client.credentials_changed',
));
}
/**
* @param CredentialsInterface $credentials AWS credentials
* @param SignatureInterface $signature Signature implementation
* @param Collection $config Configuration options
*
* @throws InvalidArgumentException if an endpoint provider isn't provided
*/
public function __construct(CredentialsInterface $credentials, SignatureInterface $signature, Collection $config)
{
// Bootstrap with Guzzle
parent::__construct($config->get(Options::BASE_URL), $config);
$this->credentials = $credentials;
$this->signature = $signature;
$this->aggregator = new DuplicateAggregator();
// Make sure the user agent is prefixed by the SDK version
$this->setUserAgent('aws-sdk-php2/' . Aws::VERSION, true);
// Add the event listener so that requests are signed before they are sent
$dispatcher = $this->getEventDispatcher();
$dispatcher->addSubscriber(new SignatureListener($credentials, $signature));
if ($backoff = $config->get(Options::BACKOFF)) {
$dispatcher->addSubscriber($backoff, -255);
}
}
public function __call($method, $args)
{
if (substr($method, 0, 3) === 'get' && substr($method, -8) === 'Iterator') {
// Allow magic method calls for iterators (e.g. $client->get<CommandName>Iterator($params))
$commandOptions = isset($args[0]) ? $args[0] : null;
$iteratorOptions = isset($args[1]) ? $args[1] : array();
return $this->getIterator(substr($method, 3, -8), $commandOptions, $iteratorOptions);
} elseif (substr($method, 0, 9) == 'waitUntil') {
// Allow magic method calls for waiters (e.g. $client->waitUntil<WaiterName>($params))
return $this->waitUntil(substr($method, 9), isset($args[0]) ? $args[0]: array());
} else {
return parent::__call(ucfirst($method), $args);
}
}
/**
* Get an endpoint for a specific region from a service description
* @deprecated This function will no longer be updated to work with new regions.
*/
public static function getEndpoint(ServiceDescriptionInterface $description, $region, $scheme)
{
try {
$service = $description->getData('endpointPrefix');
$provider = RulesEndpointProvider::fromDefaults();
$result = $provider(array(
'service' => $service,
'region' => $region,
'scheme' => $scheme
));
return $result['endpoint'];
} catch (\InvalidArgumentException $e) {
throw new InvalidArgumentException($e->getMessage(), 0, $e);
}
}
public function getCredentials()
{
return $this->credentials;
}
public function setCredentials(CredentialsInterface $credentials)
{
$formerCredentials = $this->credentials;
$this->credentials = $credentials;
// Dispatch an event that the credentials have been changed
$this->dispatch('client.credentials_changed', array(
'credentials' => $credentials,
'former_credentials' => $formerCredentials,
));
return $this;
}
public function getSignature()
{
return $this->signature;
}
public function getRegions()
{
return $this->serviceDescription->getData('regions');
}
public function getRegion()
{
return $this->getConfig(Options::REGION);
}
public function setRegion($region)
{
$config = $this->getConfig();
$formerRegion = $config->get(Options::REGION);
$global = $this->serviceDescription->getData('globalEndpoint');
$provider = $config->get('endpoint_provider');
if (!$provider) {
throw new \RuntimeException('No endpoint provider configured');
}
// Only change the region if the service does not have a global endpoint
if (!$global || $this->serviceDescription->getData('namespace') === 'S3') {
$endpoint = call_user_func(
$provider,
array(
'scheme' => $config->get(Options::SCHEME),
'region' => $region,
'service' => $config->get(Options::SERVICE)
)
);
$this->setBaseUrl($endpoint['endpoint']);
$config->set(Options::BASE_URL, $endpoint['endpoint']);
$config->set(Options::REGION, $region);
// Update the signature if necessary
$signature = $this->getSignature();
if ($signature instanceof EndpointSignatureInterface) {
/** @var EndpointSignatureInterface $signature */
$signature->setRegionName($region);
}
// Dispatch an event that the region has been changed
$this->dispatch('client.region_changed', array(
'region' => $region,
'former_region' => $formerRegion,
));
}
return $this;
}
public function waitUntil($waiter, array $input = array())
{
$this->getWaiter($waiter, $input)->wait();
return $this;
}
public function getWaiter($waiter, array $input = array())
{
return $this->getWaiterFactory()->build($waiter)
->setClient($this)
->setConfig($input);
}
public function setWaiterFactory(WaiterFactoryInterface $waiterFactory)
{
$this->waiterFactory = $waiterFactory;
return $this;
}
public function getWaiterFactory()
{
if (!$this->waiterFactory) {
$clientClass = get_class($this);
// Use a composite factory that checks for classes first, then config waiters
$this->waiterFactory = new CompositeWaiterFactory(array(
new WaiterClassFactory(substr($clientClass, 0, strrpos($clientClass, '\\')) . '\\Waiter')
));
if ($this->getDescription()) {
$waiterConfig = $this->getDescription()->getData('waiters') ?: array();
$this->waiterFactory->addFactory(new WaiterConfigFactory($waiterConfig));
}
}
return $this->waiterFactory;
}
public function getApiVersion()
{
return $this->serviceDescription->getApiVersion();
}
/**
* {@inheritdoc}
* @throws \Aws\Common\Exception\TransferException
*/
public function send($requests)
{
try {
return parent::send($requests);
} catch (CurlException $e) {
$wrapped = new TransferException($e->getMessage(), null, $e);
$wrapped->setCurlHandle($e->getCurlHandle())
->setCurlInfo($e->getCurlInfo())
->setError($e->getError(), $e->getErrorNo())
->setRequest($e->getRequest());
throw $wrapped;
}
}
/**
* Ensures that the duplicate query string aggregator is used so that
* query string values are sent over the wire as foo=bar&foo=baz.
* {@inheritdoc}
*/
public function createRequest(
$method = 'GET',
$uri = null,
$headers = null,
$body = null,
array $options = array()
) {
$request = parent::createRequest($method, $uri, $headers, $body, $options);
$request->getQuery()->setAggregator($this->aggregator);
return $request;
}
}
@@ -0,0 +1,118 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Waiter\WaiterFactoryInterface;
use Aws\Common\Waiter\WaiterInterface;
use Guzzle\Service\ClientInterface;
/**
* Interface that all AWS clients implement
*/
interface AwsClientInterface extends ClientInterface
{
/**
* Returns the AWS credentials associated with the client
*
* @return CredentialsInterface
*/
public function getCredentials();
/**
* Sets the credentials object associated with the client
*
* @param CredentialsInterface $credentials Credentials object to use
*
* @return self
*/
public function setCredentials(CredentialsInterface $credentials);
/**
* Returns the signature implementation used with the client
*
* @return SignatureInterface
*/
public function getSignature();
/**
* Get a list of available regions and region data
*
* @return array
*/
public function getRegions();
/**
* Get the name of the region to which the client is configured to send requests
*
* @return string
*/
public function getRegion();
/**
* Change the region to which the client is configured to send requests
*
* @param string $region Name of the region
*
* @return self
*/
public function setRegion($region);
/**
* Get the waiter factory being used by the client
*
* @return WaiterFactoryInterface
*/
public function getWaiterFactory();
/**
* Set the waiter factory to use with the client
*
* @param WaiterFactoryInterface $waiterFactory Factory used to create waiters
*
* @return self
*/
public function setWaiterFactory(WaiterFactoryInterface $waiterFactory);
/**
* Wait until a resource is available or an associated waiter returns true
*
* @param string $waiter Name of the waiter
* @param array $input Values used as input for the underlying operation and to control the waiter
*
* @return self
*/
public function waitUntil($waiter, array $input = array());
/**
* Get a named waiter object
*
* @param string $waiter Name of the waiter
* @param array $input Values used as input for the underlying operation and to control the waiter
*
* @return WaiterInterface
*/
public function getWaiter($waiter, array $input = array());
/**
* Get the API version of the client (e.g. 2006-03-01)
*
* @return string
*/
public function getApiVersion();
}
@@ -0,0 +1,527 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Credentials\Credentials;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Credentials\NullCredentials;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\ExceptionListener;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\NamespaceExceptionFactory;
use Aws\Common\Exception\Parser\DefaultXmlExceptionParser;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Aws\Common\Iterator\AwsResourceIteratorFactory;
use Aws\Common\RulesEndpointProvider;
use Aws\Common\Signature\EndpointSignatureInterface;
use Aws\Common\Signature\SignatureInterface;
use Aws\Common\Signature\SignatureV2;
use Aws\Common\Signature\SignatureV3Https;
use Aws\Common\Signature\SignatureV4;
use Guzzle\Common\Collection;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy;
use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
use Guzzle\Service\Description\ServiceDescription;
use Guzzle\Service\Resource\ResourceIteratorClassFactory;
use Guzzle\Log\LogAdapterInterface;
use Guzzle\Log\ClosureLogAdapter;
use Guzzle\Plugin\Backoff\BackoffLogger;
/**
* Builder for creating AWS service clients
*/
class ClientBuilder
{
/**
* @var array Default client config
*/
protected static $commonConfigDefaults = array('scheme' => 'https');
/**
* @var array Default client requirements
*/
protected static $commonConfigRequirements = array(Options::SERVICE_DESCRIPTION);
/**
* @var string The namespace of the client
*/
protected $clientNamespace;
/**
* @var array The config options
*/
protected $config = array();
/**
* @var array The config defaults
*/
protected $configDefaults = array();
/**
* @var array The config requirements
*/
protected $configRequirements = array();
/**
* @var ExceptionParserInterface The Parser interface for the client
*/
protected $exceptionParser;
/**
* @var array Array of configuration data for iterators available for the client
*/
protected $iteratorsConfig = array();
/** @var string */
private $clientClass;
/** @var string */
private $serviceName;
/**
* Factory method for creating the client builder
*
* @param string $namespace The namespace of the client
*
* @return ClientBuilder
*/
public static function factory($namespace = null)
{
return new static($namespace);
}
/**
* Constructs a client builder
*
* @param string $namespace The namespace of the client
*/
public function __construct($namespace = null)
{
$this->clientNamespace = $namespace;
// Determine service and class name
$this->clientClass = 'Aws\Common\Client\DefaultClient';
if ($this->clientNamespace) {
$this->serviceName = substr($this->clientNamespace, strrpos($this->clientNamespace, '\\') + 1);
$this->clientClass = $this->clientNamespace . '\\' . $this->serviceName . 'Client';
}
}
/**
* Sets the config options
*
* @param array|Collection $config The config options
*
* @return ClientBuilder
*/
public function setConfig($config)
{
$this->config = $this->processArray($config);
return $this;
}
/**
* Sets the config options' defaults
*
* @param array|Collection $defaults The default values
*
* @return ClientBuilder
*/
public function setConfigDefaults($defaults)
{
$this->configDefaults = $this->processArray($defaults);
return $this;
}
/**
* Sets the required config options
*
* @param array|Collection $required The required config options
*
* @return ClientBuilder
*/
public function setConfigRequirements($required)
{
$this->configRequirements = $this->processArray($required);
return $this;
}
/**
* Sets the exception parser. If one is not provided the builder will use
* the default XML exception parser.
*
* @param ExceptionParserInterface $parser The exception parser
*
* @return ClientBuilder
*/
public function setExceptionParser(ExceptionParserInterface $parser)
{
$this->exceptionParser = $parser;
return $this;
}
/**
* Set the configuration for the client's iterators
*
* @param array $config Configuration data for client's iterators
*
* @return ClientBuilder
*/
public function setIteratorsConfig(array $config)
{
$this->iteratorsConfig = $config;
return $this;
}
/**
* Performs the building logic using all of the parameters that have been
* set and falling back to default values. Returns an instantiate service
* client with credentials prepared and plugins attached.
*
* @return AwsClientInterface
* @throws InvalidArgumentException
*/
public function build()
{
// Resolve configuration
$config = Collection::fromConfig(
$this->config,
array_merge(self::$commonConfigDefaults, $this->configDefaults),
(self::$commonConfigRequirements + $this->configRequirements)
);
if ($config[Options::VERSION] === 'latest') {
$config[Options::VERSION] = constant("{$this->clientClass}::LATEST_API_VERSION");
}
if (!isset($config['endpoint_provider'])) {
$config['endpoint_provider'] = RulesEndpointProvider::fromDefaults();
}
// Resolve the endpoint, signature, and credentials
$description = $this->updateConfigFromDescription($config);
$signature = $this->getSignature($description, $config);
$credentials = $this->getCredentials($config);
$this->extractHttpConfig($config);
// Resolve exception parser
if (!$this->exceptionParser) {
$this->exceptionParser = new DefaultXmlExceptionParser();
}
// Resolve backoff strategy
$backoff = $config->get(Options::BACKOFF);
if ($backoff === null) {
$retries = isset($config[Options::BACKOFF_RETRIES]) ? $config[Options::BACKOFF_RETRIES] : 3;
$backoff = $this->createDefaultBackoff($retries);
$config->set(Options::BACKOFF, $backoff);
}
if ($backoff) {
$this->addBackoffLogger($backoff, $config);
}
/** @var AwsClientInterface $client */
$client = new $this->clientClass($credentials, $signature, $config);
$client->setDescription($description);
// Add exception marshaling so that more descriptive exception are thrown
if ($this->clientNamespace) {
$exceptionFactory = new NamespaceExceptionFactory(
$this->exceptionParser,
"{$this->clientNamespace}\\Exception",
"{$this->clientNamespace}\\Exception\\{$this->serviceName}Exception"
);
$client->addSubscriber(new ExceptionListener($exceptionFactory));
}
// Add the UserAgentPlugin to append to the User-Agent header of requests
$client->addSubscriber(new UserAgentListener());
// Filters used for the cache plugin
$client->getConfig()->set(
'params.cache.key_filter',
'header=date,x-amz-date,x-amz-security-token,x-amzn-authorization'
);
// Set the iterator resource factory based on the provided iterators config
$client->setResourceIteratorFactory(new AwsResourceIteratorFactory(
$this->iteratorsConfig,
new ResourceIteratorClassFactory($this->clientNamespace . '\\Iterator')
));
// Disable parameter validation if needed
if ($config->get(Options::VALIDATION) === false) {
$params = $config->get('command.params') ?: array();
$params['command.disable_validation'] = true;
$config->set('command.params', $params);
}
return $client;
}
/**
* Add backoff logging to the backoff plugin if needed
*
* @param BackoffPlugin $plugin Backoff plugin
* @param Collection $config Configuration settings
*
* @throws InvalidArgumentException
*/
protected function addBackoffLogger(BackoffPlugin $plugin, Collection $config)
{
// The log option can be set to `debug` or an instance of a LogAdapterInterface
if ($logger = $config->get(Options::BACKOFF_LOGGER)) {
$format = $config->get(Options::BACKOFF_LOGGER_TEMPLATE);
if ($logger === 'debug') {
$logger = new ClosureLogAdapter(function ($message) {
trigger_error($message . "\n");
});
} elseif (!($logger instanceof LogAdapterInterface)) {
throw new InvalidArgumentException(
Options::BACKOFF_LOGGER . ' must be set to `debug` or an instance of '
. 'Guzzle\\Common\\Log\\LogAdapterInterface'
);
}
// Create the plugin responsible for logging exponential backoff retries
$logPlugin = new BackoffLogger($logger);
// You can specify a custom format or use the default
if ($format) {
$logPlugin->setTemplate($format);
}
$plugin->addSubscriber($logPlugin);
}
}
/**
* Ensures that an array (e.g. for config data) is actually in array form
*
* @param array|Collection $array The array data
*
* @return array
* @throws InvalidArgumentException if the arg is not an array or Collection
*/
protected function processArray($array)
{
if ($array instanceof Collection) {
$array = $array->getAll();
}
if (!is_array($array)) {
throw new InvalidArgumentException('The config must be provided as an array or Collection.');
}
return $array;
}
/**
* Update a configuration object from a service description
*
* @param Collection $config Config to update
*
* @return ServiceDescription
* @throws InvalidArgumentException
*/
protected function updateConfigFromDescription(Collection $config)
{
$description = $config->get(Options::SERVICE_DESCRIPTION);
if (!($description instanceof ServiceDescription)) {
// Inject the version into the sprintf template if it is a string
if (is_string($description)) {
$description = sprintf($description, $config->get(Options::VERSION));
}
$description = ServiceDescription::factory($description);
$config->set(Options::SERVICE_DESCRIPTION, $description);
}
if (!$config->get(Options::SERVICE)) {
$config->set(Options::SERVICE, $description->getData('endpointPrefix'));
}
if ($iterators = $description->getData('iterators')) {
$this->setIteratorsConfig($iterators);
}
$this->handleRegion($config);
$this->handleEndpoint($config);
return $description;
}
/**
* Return an appropriate signature object for a a client based on the
* "signature" configuration setting, or the default signature specified in
* a service description. The signature can be set to a valid signature
* version identifier string or an instance of Aws\Common\Signature\SignatureInterface.
*
* @param ServiceDescription $description Description that holds a signature option
* @param Collection $config Configuration options
*
* @return SignatureInterface
* @throws InvalidArgumentException
*/
protected function getSignature(ServiceDescription $description, Collection $config)
{
// If a custom signature has not been provided, then use the default
// signature setting specified in the service description.
$signature = $config->get(Options::SIGNATURE) ?: $description->getData('signatureVersion');
if (is_string($signature)) {
if ($signature == 'v4') {
$signature = new SignatureV4();
} elseif ($signature == 'v2') {
$signature = new SignatureV2();
} elseif ($signature == 'v3https') {
$signature = new SignatureV3Https();
} else {
throw new InvalidArgumentException("Invalid signature type: {$signature}");
}
} elseif (!($signature instanceof SignatureInterface)) {
throw new InvalidArgumentException('The provided signature is not '
. 'a signature version string or an instance of '
. 'Aws\\Common\\Signature\\SignatureInterface');
}
// Allow a custom service name or region value to be provided
if ($signature instanceof EndpointSignatureInterface) {
// Determine the service name to use when signing
$signature->setServiceName($config->get(Options::SIGNATURE_SERVICE)
?: $description->getData('signingName')
?: $description->getData('endpointPrefix'));
// Determine the region to use when signing requests
$signature->setRegionName($config->get(Options::SIGNATURE_REGION) ?: $config->get(Options::REGION));
}
return $signature;
}
protected function getCredentials(Collection $config)
{
$credentials = $config->get(Options::CREDENTIALS);
if (is_array($credentials)) {
$credentials = Credentials::factory($credentials);
} elseif ($credentials === false) {
$credentials = new NullCredentials();
} elseif (!$credentials instanceof CredentialsInterface) {
$credentials = Credentials::factory($config);
}
return $credentials;
}
private function handleRegion(Collection $config)
{
// Make sure a valid region is set
$region = $config[Options::REGION];
$description = $config[Options::SERVICE_DESCRIPTION];
$global = $description->getData('globalEndpoint');
if (!$global && !$region) {
throw new InvalidArgumentException(
'A region is required when using ' . $description->getData('serviceFullName')
);
} elseif ($global && !$region) {
$config[Options::REGION] = 'us-east-1';
}
}
private function handleEndpoint(Collection $config)
{
// Alias "endpoint" with "base_url" for forwards compatibility.
if ($config['endpoint']) {
$config[Options::BASE_URL] = $config['endpoint'];
return;
}
if ($config[Options::BASE_URL]) {
return;
}
$endpoint = call_user_func(
$config['endpoint_provider'],
array(
'scheme' => $config[Options::SCHEME],
'region' => $config[Options::REGION],
'service' => $config[Options::SERVICE]
)
);
$config[Options::BASE_URL] = $endpoint['endpoint'];
// Set a signature if one was not explicitly provided.
if (!$config->hasKey(Options::SIGNATURE)
&& isset($endpoint['signatureVersion'])
) {
$config->set(Options::SIGNATURE, $endpoint['signatureVersion']);
}
// The the signing region if endpoint rule specifies one.
if (isset($endpoint['credentialScope'])) {
$scope = $endpoint['credentialScope'];
if (isset($scope['region'])) {
$config->set(Options::SIGNATURE_REGION, $scope['region']);
}
}
}
private function createDefaultBackoff($retries = 3)
{
return new BackoffPlugin(
// Retry failed requests up to 3 times if it is determined that the request can be retried
new TruncatedBackoffStrategy($retries,
// Retry failed requests with 400-level responses due to throttling
new ThrottlingErrorChecker($this->exceptionParser,
// Retry failed requests due to transient network or cURL problems
new CurlBackoffStrategy(null,
// Retry failed requests with 500-level responses
new HttpBackoffStrategy(array(500, 503, 509),
// Retry requests that failed due to expired credentials
new ExpiredCredentialsChecker($this->exceptionParser,
new ExponentialBackoffStrategy()
)
)
)
)
)
);
}
private function extractHttpConfig(Collection $config)
{
$http = $config['http'];
if (!is_array($http)) {
return;
}
if (isset($http['verify'])) {
$config[Options::SSL_CERT] = $http['verify'];
}
}
}
@@ -0,0 +1,67 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Enum\ClientOptions as Options;
use Guzzle\Common\Collection;
/**
* Generic client for interacting with an AWS service
*/
class DefaultClient extends AbstractClient
{
/**
* Factory method to create a default client using an array of configuration options.
*
* The following array keys and values are available options:
*
* Credential options ((`key`, `secret`, and optional `token`) OR `credentials` is required):
*
* - key: AWS Access Key ID
* - secret: AWS secret access key
* - credentials: You can optionally provide a custom `Aws\Common\Credentials\CredentialsInterface` object
* - token: Custom AWS security token to use with request authentication. Please note that not all services accept temporary credentials. See http://docs.aws.amazon.com/STS/latest/UsingSTS/UsingTokens.html
* - token.ttd: UNIX timestamp for when the custom credentials expire
* - credentials.cache.key: Optional custom cache key to use with the credentials
* - credentials.client: Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your credentials require a HTTP request (e.g. RefreshableInstanceProfileCredentials)
*
* Region and endpoint options (Some services do not require a region while others do. Check the service specific user guide documentation for details):
*
* - region: Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
* - scheme: URI Scheme of the base URL (e.g. 'https', 'http') used when endpoint is not supplied
* - endpoint: Allows you to specify a custom endpoint instead of building one from the region and scheme
*
* Generic client options:
*
* - signature: Overrides the signature used by the client. Clients will always choose an appropriate default signature. However, it can be useful to override this with a custom setting. This can be set to "v4", "v3https", "v2" or an instance of Aws\Common\Signature\SignatureInterface.
* - ssl.certificate_authority: Set to true to use the bundled CA cert or pass the full path to an SSL certificate bundle
* - curl.options: Associative of CURLOPT_* cURL options to add to each request
* - client.backoff.logger: `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use 'debug' to emit PHP warnings when a retry is issued.
* - client.backoff.logger.template: Optional template to use for exponential backoff log messages. See `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
*
* @param array|Collection $config Client configuration data
*
* @return self
*/
public static function factory($config = array())
{
return ClientBuilder::factory()
->setConfig($config)
->setConfigDefaults(array(Options::SCHEME => 'https'))
->build();
}
}
@@ -0,0 +1,80 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Credentials\AbstractRefreshableCredentials;
use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffStrategyInterface;
use Guzzle\Plugin\Backoff\AbstractBackoffStrategy;
/**
* Backoff logic that handles retrying requests when credentials expire
*/
class ExpiredCredentialsChecker extends AbstractBackoffStrategy
{
/**
* @var array Array of known retrying exception codes
*/
protected $retryable = array(
'RequestExpired' => true,
'ExpiredTokenException' => true,
'ExpiredToken' => true
);
/**
* @var ExceptionParserInterface Exception parser used to parse exception responses
*/
protected $exceptionParser;
public function __construct(ExceptionParserInterface $exceptionParser, BackoffStrategyInterface $next = null) {
$this->exceptionParser = $exceptionParser;
$this->next = $next;
}
public function makesDecision()
{
return true;
}
protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null)
{
if ($response && $response->isClientError()) {
$parts = $this->exceptionParser->parse($request, $response);
if (!isset($this->retryable[$parts['code']]) || !$request->getClient()) {
return null;
}
/** @var AwsClientInterface $client */
$client = $request->getClient();
// Only retry if the credentials can be refreshed
if (!($client->getCredentials() instanceof AbstractRefreshableCredentials)) {
return null;
}
// Resign the request using new credentials
$client->getSignature()->signRequest($request, $client->getCredentials()->setExpiration(-1));
// Retry immediately with no delay
return 0;
}
}
}
@@ -0,0 +1,75 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Guzzle\Http\Exception\HttpException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffStrategyInterface;
use Guzzle\Plugin\Backoff\AbstractBackoffStrategy;
/**
* Backoff logic that handles throttling exceptions from services
*/
class ThrottlingErrorChecker extends AbstractBackoffStrategy
{
/** @var array Whitelist of exception codes (as indexes) that indicate throttling */
protected static $throttlingExceptions = array(
'RequestLimitExceeded' => true,
'Throttling' => true,
'ThrottlingException' => true,
'ProvisionedThroughputExceededException' => true,
'RequestThrottled' => true,
);
/**
* @var ExceptionParserInterface Exception parser used to parse exception responses
*/
protected $exceptionParser;
public function __construct(ExceptionParserInterface $exceptionParser, BackoffStrategyInterface $next = null)
{
$this->exceptionParser = $exceptionParser;
if ($next) {
$this->setNext($next);
}
}
/**
* {@inheritdoc}
*/
public function makesDecision()
{
return true;
}
/**
* {@inheritdoc}
*/
protected function getDelay(
$retries,
RequestInterface $request,
Response $response = null,
HttpException $e = null
) {
if ($response && $response->isClientError()) {
$parts = $this->exceptionParser->parse($request, $response);
return isset(self::$throttlingExceptions[$parts['code']]) ? true : null;
}
}
}
@@ -0,0 +1,95 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Event;
use Guzzle\Http\EntityBody;
use Guzzle\Service\Command\AbstractCommand as Command;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Prepares the body parameter of a command such that the parameter is more flexible (e.g. accepts file handles) with
* the value it accepts but converts it to the correct format for the command. Also looks for a "Filename" parameter.
*/
class UploadBodyListener implements EventSubscriberInterface
{
/**
* @var array The names of the commands of which to modify the body parameter
*/
protected $commands;
/**
* @var string The key for the upload body parameter
*/
protected $bodyParameter;
/**
* @var string The key for the source file parameter
*/
protected $sourceParameter;
/**
* @param array $commands The commands to modify
* @param string $bodyParameter The key for the body parameter
* @param string $sourceParameter The key for the source file parameter
*/
public function __construct(array $commands, $bodyParameter = 'Body', $sourceParameter = 'SourceFile')
{
$this->commands = $commands;
$this->bodyParameter = (string) $bodyParameter;
$this->sourceParameter = (string) $sourceParameter;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array('command.before_prepare' => array('onCommandBeforePrepare'));
}
/**
* Converts filenames and file handles into EntityBody objects before the command is validated
*
* @param Event $event Event emitted
* @throws InvalidArgumentException
*/
public function onCommandBeforePrepare(Event $event)
{
/** @var Command $command */
$command = $event['command'];
if (in_array($command->getName(), $this->commands)) {
// Get the interesting parameters
$source = $command->get($this->sourceParameter);
$body = $command->get($this->bodyParameter);
// If a file path is passed in then get the file handle
if (is_string($source) && file_exists($source)) {
$body = fopen($source, 'r');
}
// Prepare the body parameter and remove the source file parameter
if (null !== $body) {
$command->remove($this->sourceParameter);
$command->set($this->bodyParameter, EntityBody::factory($body));
} else {
throw new InvalidArgumentException("You must specify a non-null value for the {$this->bodyParameter} or {$this->sourceParameter} parameters.");
}
}
}
}
@@ -0,0 +1,61 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Client;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listener used to append strings to the User-Agent header of a request based
* on the `ua.append` option. `ua.append` can contain a string or array of values.
*/
class UserAgentListener implements EventSubscriberInterface
{
/**
* @var string Option used to store User-Agent modifiers
*/
const OPTION = 'ua.append';
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array('command.before_send' => 'onBeforeSend');
}
/**
* Adds strings to the User-Agent header using the `ua.append` parameter of a command
*
* @param Event $event Event emitted
*/
public function onBeforeSend(Event $event)
{
$command = $event['command'];
if ($userAgentAppends = $command->get(self::OPTION)) {
$request = $command->getRequest();
$userAgent = (string) $request->getHeader('User-Agent');
foreach ((array) $userAgentAppends as $append) {
$append = ' ' . $append;
if (strpos($userAgent, $append) === false) {
$userAgent .= $append;
}
}
$request->setHeader('User-Agent', $userAgent);
}
}
}
@@ -0,0 +1,120 @@
<?php
namespace Aws\Common\Command;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor;
/**
* Location visitor used to serialize AWS query parameters (e.g. EC2, SES, SNS, SQS, etc) as POST fields
*/
class AwsQueryVisitor extends AbstractRequestVisitor
{
private $fqname;
public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value)
{
$this->fqname = $command->getName();
$query = array();
$this->customResolver($value, $param, $query, $param->getWireName());
$request->addPostFields($query);
}
/**
* Map nested parameters into the location_key based parameters
*
* @param array $value Value to map
* @param Parameter $param Parameter that holds information about the current key
* @param array $query Built up query string values
* @param string $prefix String to prepend to sub query values
*/
protected function customResolver($value, Parameter $param, array &$query, $prefix = '')
{
switch ($param->getType()) {
case 'object':
$this->resolveObject($param, $value, $prefix, $query);
break;
case 'array':
$this->resolveArray($param, $value, $prefix, $query);
break;
default:
$query[$prefix] = $param->filter($value);
}
}
/**
* Custom handling for objects
*
* @param Parameter $param Parameter for the object
* @param array $value Value that is set for this parameter
* @param string $prefix Prefix for the resulting key
* @param array $query Query string array passed by reference
*/
protected function resolveObject(Parameter $param, array $value, $prefix, array &$query)
{
// Maps are implemented using additional properties
$hasAdditionalProperties = ($param->getAdditionalProperties() instanceof Parameter);
$additionalPropertyCount = 0;
foreach ($value as $name => $v) {
if ($subParam = $param->getProperty($name)) {
// if the parameter was found by name as a regular property
$key = $prefix . '.' . $subParam->getWireName();
$this->customResolver($v, $subParam, $query, $key);
} elseif ($hasAdditionalProperties) {
// Handle map cases like &Attribute.1.Name=<name>&Attribute.1.Value=<value>
$additionalPropertyCount++;
$data = $param->getData();
$keyName = isset($data['keyName']) ? $data['keyName'] : 'key';
$valueName = isset($data['valueName']) ? $data['valueName'] : 'value';
$query["{$prefix}.{$additionalPropertyCount}.{$keyName}"] = $name;
$newPrefix = "{$prefix}.{$additionalPropertyCount}.{$valueName}";
if (is_array($v)) {
$this->customResolver($v, $param->getAdditionalProperties(), $query, $newPrefix);
} else {
$query[$newPrefix] = $param->filter($v);
}
}
}
}
/**
* Custom handling for arrays
*
* @param Parameter $param Parameter for the object
* @param array $value Value that is set for this parameter
* @param string $prefix Prefix for the resulting key
* @param array $query Query string array passed by reference
*/
protected function resolveArray(Parameter $param, array $value, $prefix, array &$query)
{
static $serializeEmpty = array(
'SetLoadBalancerPoliciesForBackendServer' => 1,
'SetLoadBalancerPoliciesOfListener' => 1,
'UpdateStack' => 1
);
// For BC, serialize empty lists for specific operations
if (!$value) {
if (isset($serializeEmpty[$this->fqname])) {
if (substr($prefix, -7) === '.member') {
$prefix = substr($prefix, 0, -7);
}
$query[$prefix] = '';
}
return;
}
$offset = $param->getData('offset') ?: 1;
foreach ($value as $index => $v) {
$index += $offset;
if (is_array($v) && $items = $param->getItems()) {
$this->customResolver($v, $items, $query, $prefix . '.' . $index);
} else {
$query[$prefix . '.' . $index] = $param->filter($v);
}
}
}
}
@@ -0,0 +1,47 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Command;
use Guzzle\Service\Command\OperationCommand;
use Guzzle\Http\Curl\CurlHandle;
/**
* Adds AWS JSON body functionality to dynamically generated HTTP requests
*/
class JsonCommand extends OperationCommand
{
/**
* {@inheritdoc}
*/
protected function build()
{
parent::build();
// Ensure that the body of the request ALWAYS includes some JSON. By default, this is an empty object.
if (!$this->request->getBody()) {
$this->request->setBody('{}');
}
// Never send the Expect header when interacting with a JSON query service
$this->request->removeHeader('Expect');
// Always send JSON requests as a raw string rather than using streams to avoid issues with
// cURL error code 65: "necessary data rewind wasn't possible".
// This could be removed after PHP addresses https://bugs.php.net/bug.php?id=47204
$this->request->getCurlOptions()->set(CurlHandle::BODY_AS_STRING, true);
}
}
@@ -0,0 +1,53 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Command;
use Guzzle\Service\Command\OperationCommand;
/**
* Adds AWS Query service serialization
*/
class QueryCommand extends OperationCommand
{
/**
* @var AwsQueryVisitor
*/
protected static $queryVisitor;
/**
* @var XmlResponseLocationVisitor
*/
protected static $xmlVisitor;
/**
* Register the aws.query visitor
*/
protected function init()
{
// @codeCoverageIgnoreStart
if (!self::$queryVisitor) {
self::$queryVisitor = new AwsQueryVisitor();
}
if (!self::$xmlVisitor) {
self::$xmlVisitor = new XmlResponseLocationVisitor();
}
// @codeCoverageIgnoreEnd
$this->getRequestSerializer()->addVisitor('aws.query', self::$queryVisitor);
$this->getResponseParser()->addVisitor('xml', self::$xmlVisitor);
}
}
@@ -0,0 +1,74 @@
<?php
namespace Aws\Common\Command;
use Guzzle\Service\Description\Operation;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Http\Message\Response;
use Guzzle\Service\Description\Parameter;
use Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor;
/**
* Class used for custom AWS XML response parsing of query services
*/
class XmlResponseLocationVisitor extends XmlVisitor
{
/**
* {@inheritdoc}
*/
public function before(CommandInterface $command, array &$result)
{
parent::before($command, $result);
// Unwrapped wrapped responses
$operation = $command->getOperation();
if ($operation->getServiceDescription()->getData('resultWrapped')) {
$wrappingNode = $operation->getName() . 'Result';
if (isset($result[$wrappingNode])) {
$result = $result[$wrappingNode] + $result;
unset($result[$wrappingNode]);
}
}
}
/**
* Accounts for wrapper nodes
* {@inheritdoc}
*/
public function visit(
CommandInterface $command,
Response $response,
Parameter $param,
&$value,
$context = null
) {
parent::visit($command, $response, $param, $value, $context);
// Account for wrapper nodes (e.g. RDS, ElastiCache, etc)
if ($param->getData('wrapper')) {
$wireName = $param->getWireName();
$value += $value[$wireName];
unset($value[$wireName]);
}
}
/**
* Filter used when converting XML maps into associative arrays in service descriptions
*
* @param array $value Value to filter
* @param string $entryName Name of each entry
* @param string $keyName Name of each key
* @param string $valueName Name of each value
*
* @return array Returns the map of the XML data
*/
public static function xmlMap($value, $entryName, $keyName, $valueName)
{
$result = array();
foreach ($value as $entry) {
$result[$entry[$keyName]] = $entry[$valueName];
}
return $result;
}
}
@@ -0,0 +1,136 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Credentials;
/**
* Abstract credentials decorator
*/
class AbstractCredentialsDecorator implements CredentialsInterface
{
/**
* @var CredentialsInterface Wrapped credentials object
*/
protected $credentials;
/**
* Constructs a new BasicAWSCredentials object, with the specified AWS
* access key and AWS secret key
*
* @param CredentialsInterface $credentials
*/
public function __construct(CredentialsInterface $credentials)
{
$this->credentials = $credentials;
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return $this->credentials->serialize();
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$this->credentials = new Credentials('', '');
$this->credentials->unserialize($serialized);
}
/**
* {@inheritdoc}
*/
public function getAccessKeyId()
{
return $this->credentials->getAccessKeyId();
}
/**
* {@inheritdoc}
*/
public function getSecretKey()
{
return $this->credentials->getSecretKey();
}
/**
* {@inheritdoc}
*/
public function getSecurityToken()
{
return $this->credentials->getSecurityToken();
}
/**
* {@inheritdoc}
*/
public function getExpiration()
{
return $this->credentials->getExpiration();
}
/**
* {@inheritdoc}
*/
public function isExpired()
{
return $this->credentials->isExpired();
}
/**
* {@inheritdoc}
*/
public function setAccessKeyId($key)
{
$this->credentials->setAccessKeyId($key);
return $this;
}
/**
* {@inheritdoc}
*/
public function setSecretKey($secret)
{
$this->credentials->setSecretKey($secret);
return $this;
}
/**
* {@inheritdoc}
*/
public function setSecurityToken($token)
{
$this->credentials->setSecurityToken($token);
return $this;
}
/**
* {@inheritdoc}
*/
public function setExpiration($timestamp)
{
$this->credentials->setExpiration($timestamp);
return $this;
}
}
@@ -0,0 +1,95 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Credentials;
/**
* Abstract decorator to provide a foundation for refreshable credentials
*/
abstract class AbstractRefreshableCredentials extends AbstractCredentialsDecorator
{
/**
* Get the underlying credentials, refreshing if necessary.
*
* @return Credentials
*/
public function getCredentials()
{
if ($this->credentials->isExpired()) {
$this->refresh();
}
return new Credentials(
$this->credentials->getAccessKeyId(),
$this->credentials->getSecretKey(),
$this->credentials->getSecurityToken(),
$this->credentials->getExpiration()
);
}
/**
* {@inheritdoc}
*/
public function getAccessKeyId()
{
if ($this->credentials->isExpired()) {
$this->refresh();
}
return $this->credentials->getAccessKeyId();
}
/**
* {@inheritdoc}
*/
public function getSecretKey()
{
if ($this->credentials->isExpired()) {
$this->refresh();
}
return $this->credentials->getSecretKey();
}
/**
* {@inheritdoc}
*/
public function getSecurityToken()
{
if ($this->credentials->isExpired()) {
$this->refresh();
}
return $this->credentials->getSecurityToken();
}
/**
* {@inheritdoc}
*/
public function serialize()
{
if ($this->credentials->isExpired()) {
$this->refresh();
}
return $this->credentials->serialize();
}
/**
* Attempt to get new credentials
*/
abstract protected function refresh();
}
@@ -0,0 +1,74 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Credentials;
use Guzzle\Cache\CacheAdapterInterface;
/**
* Credentials decorator used to implement caching credentials
*/
class CacheableCredentials extends AbstractRefreshableCredentials
{
/**
* @var CacheAdapterInterface Cache adapter used to store credentials
*/
protected $cache;
/**
* @var string Cache key used to store the credentials
*/
protected $cacheKey;
/**
* CacheableCredentials is a decorator that decorates other credentials
*
* @param CredentialsInterface $credentials Credentials to adapt
* @param CacheAdapterInterface $cache Cache to use to store credentials
* @param string $cacheKey Cache key of the credentials
*/
public function __construct(CredentialsInterface $credentials, CacheAdapterInterface $cache, $cacheKey)
{
$this->cache = $cache;
$this->cacheKey = $cacheKey;
parent::__construct($credentials);
}
/**
* Attempt to get new credentials from cache or from the adapted object
*/
protected function refresh()
{
if (!$cache = $this->cache->fetch($this->cacheKey)) {
// The credentials were not found, so try again and cache if new
$this->credentials->getAccessKeyId();
if (!$this->credentials->isExpired()) {
// The credentials were updated, so cache them
$this->cache->save($this->cacheKey, $this->credentials, $this->credentials->getExpiration() - time());
}
} else {
// The credentials were found in cache, so update the adapter object
// if the cached credentials are not expired
if (!$cache->isExpired()) {
$this->credentials->setAccessKeyId($cache->getAccessKeyId());
$this->credentials->setSecretKey($cache->getSecretKey());
$this->credentials->setSecurityToken($cache->getSecurityToken());
$this->credentials->setExpiration($cache->getExpiration());
}
}
}
}
@@ -0,0 +1,352 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Credentials;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\RequiredExtensionNotLoadedException;
use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\FromConfigInterface;
use Guzzle\Cache\CacheAdapterInterface;
use Guzzle\Cache\DoctrineCacheAdapter;
use Guzzle\Common\Collection;
/**
* Basic implementation of the AWSCredentials interface that allows callers to
* pass in the AWS access key and secret access in the constructor.
*/
class Credentials implements CredentialsInterface, FromConfigInterface
{
const ENV_KEY = 'AWS_ACCESS_KEY_ID';
const ENV_SECRET = 'AWS_SECRET_KEY';
const ENV_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';
const ENV_PROFILE = 'AWS_PROFILE';
/** @var string AWS Access Key ID */
protected $key;
/** @var string AWS Secret Access Key */
protected $secret;
/** @var string AWS Security Token */
protected $token;
/** @var int Time to die of token */
protected $ttd;
/**
* Get the available keys for the factory method
*
* @return array
*/
public static function getConfigDefaults()
{
return array(
Options::KEY => null,
Options::SECRET => null,
Options::TOKEN => null,
Options::TOKEN_TTD => null,
Options::PROFILE => null,
Options::CREDENTIALS_CACHE => null,
Options::CREDENTIALS_CACHE_KEY => null,
Options::CREDENTIALS_CLIENT => null
);
}
/**
* Factory method for creating new credentials. This factory method will
* create the appropriate credentials object with appropriate decorators
* based on the passed configuration options.
*
* @param array $config Options to use when instantiating the credentials
*
* @return CredentialsInterface
* @throws InvalidArgumentException If the caching options are invalid
* @throws RuntimeException If using the default cache and APC is disabled
*/
public static function factory($config = array())
{
// Add default key values
foreach (self::getConfigDefaults() as $key => $value) {
if (!isset($config[$key])) {
$config[$key] = $value;
}
}
// Set up the cache
$cache = $config[Options::CREDENTIALS_CACHE];
$cacheKey = $config[Options::CREDENTIALS_CACHE_KEY] ?:
'credentials_' . ($config[Options::KEY] ?: crc32(gethostname()));
if (
$cacheKey &&
$cache instanceof CacheAdapterInterface &&
$cached = self::createFromCache($cache, $cacheKey)
) {
return $cached;
}
// Create the credentials object
if (!$config[Options::KEY] || !$config[Options::SECRET]) {
$credentials = self::createFromEnvironment($config);
} else {
// Instantiate using short or long term credentials
$credentials = new static(
$config[Options::KEY],
$config[Options::SECRET],
$config[Options::TOKEN],
$config[Options::TOKEN_TTD]
);
}
// Check if the credentials are refreshable, and if so, configure caching
$cache = $config[Options::CREDENTIALS_CACHE];
if ($cacheKey && $cache) {
$credentials = self::createCache($credentials, $cache, $cacheKey);
}
return $credentials;
}
/**
* Create credentials from the credentials ini file in the HOME directory.
*
* @param string|null $profile Pass a specific profile to use. If no
* profile is specified we will attempt to use
* the value specified in the AWS_PROFILE
* environment variable. If AWS_PROFILE is not
* set, the "default" profile is used.
* @param string|null $filename Pass a string to specify the location of the
* credentials files. If null is passed, the
* SDK will attempt to find the configuration
* file at in your HOME directory at
* ~/.aws/credentials.
* @return CredentialsInterface
* @throws \RuntimeException if the file cannot be found, if the file is
* invalid, or if the profile is invalid.
*/
public static function fromIni($profile = null, $filename = null)
{
if (!$filename) {
$filename = self::getHomeDir() . '/.aws/credentials';
}
if (!$profile) {
$profile = self::getEnvVar(self::ENV_PROFILE) ?: 'default';
}
if (!is_readable($filename) || ($data = parse_ini_file($filename, true)) === false) {
throw new \RuntimeException("Invalid AWS credentials file: {$filename}.");
}
if (!isset($data[$profile]['aws_access_key_id']) || !isset($data[$profile]['aws_secret_access_key'])) {
throw new \RuntimeException("Invalid AWS credentials profile {$profile} in {$filename}.");
}
return new self(
$data[$profile]['aws_access_key_id'],
$data[$profile]['aws_secret_access_key'],
isset($data[$profile]['aws_security_token'])
? $data[$profile]['aws_security_token']
: null
);
}
/**
* Constructs a new BasicAWSCredentials object, with the specified AWS
* access key and AWS secret key
*
* @param string $accessKeyId AWS access key ID
* @param string $secretAccessKey AWS secret access key
* @param string $token Security token to use
* @param int $expiration UNIX timestamp for when credentials expire
*/
public function __construct($accessKeyId, $secretAccessKey, $token = null, $expiration = null)
{
$this->key = trim($accessKeyId);
$this->secret = trim($secretAccessKey);
$this->token = $token;
$this->ttd = $expiration;
}
public function serialize()
{
return json_encode(array(
Options::KEY => $this->key,
Options::SECRET => $this->secret,
Options::TOKEN => $this->token,
Options::TOKEN_TTD => $this->ttd
));
}
public function unserialize($serialized)
{
$data = json_decode($serialized, true);
$this->key = $data[Options::KEY];
$this->secret = $data[Options::SECRET];
$this->token = $data[Options::TOKEN];
$this->ttd = $data[Options::TOKEN_TTD];
}
public function getAccessKeyId()
{
return $this->key;
}
public function getSecretKey()
{
return $this->secret;
}
public function getSecurityToken()
{
return $this->token;
}
public function getExpiration()
{
return $this->ttd;
}
public function isExpired()
{
return $this->ttd !== null && time() >= $this->ttd;
}
public function setAccessKeyId($key)
{
$this->key = $key;
return $this;
}
public function setSecretKey($secret)
{
$this->secret = $secret;
return $this;
}
public function setSecurityToken($token)
{
$this->token = $token;
return $this;
}
public function setExpiration($timestamp)
{
$this->ttd = $timestamp;
return $this;
}
/**
* When no keys are provided, attempt to create them based on the
* environment or instance profile credentials.
*
* @param array|Collection $config
*
* @return CredentialsInterface
*/
private static function createFromEnvironment($config)
{
// Get key and secret from ENV variables
$envKey = self::getEnvVar(self::ENV_KEY);
if (!($envSecret = self::getEnvVar(self::ENV_SECRET))) {
// Use AWS_SECRET_ACCESS_KEY if AWS_SECRET_KEY was not set
$envSecret = self::getEnvVar(self::ENV_SECRET_ACCESS_KEY);
}
// Use credentials from the environment variables if available
if ($envKey && $envSecret) {
return new static($envKey, $envSecret);
}
try {
// Use credentials from the INI file in HOME directory if available
return self::fromIni($config[Options::PROFILE]);
} catch (\RuntimeException $e) {
// Otherwise, try using instance profile credentials (available on EC2 instances)
return new RefreshableInstanceProfileCredentials(
new static('', '', '', 1),
$config[Options::CREDENTIALS_CLIENT]
);
}
}
private static function createFromCache(CacheAdapterInterface $cache, $cacheKey)
{
$cached = $cache->fetch($cacheKey);
if ($cached instanceof CredentialsInterface && !$cached->isExpired()) {
return new CacheableCredentials($cached, $cache, $cacheKey);
}
return null;
}
private static function createCache(CredentialsInterface $credentials, $cache, $cacheKey)
{
if ($cache === 'true' || $cache === true) {
// If no cache adapter was provided, then create one for the user
// @codeCoverageIgnoreStart
if (!extension_loaded('apc')) {
throw new RequiredExtensionNotLoadedException('PHP has not been compiled with APC. Unable to cache '
. 'the credentials.');
} elseif (!class_exists('Doctrine\Common\Cache\ApcCache')) {
throw new RuntimeException(
'Cannot set ' . Options::CREDENTIALS_CACHE . ' to true because the Doctrine cache component is '
. 'not installed. Either install doctrine/cache or pass in an instantiated '
. 'Guzzle\Cache\CacheAdapterInterface object'
);
}
// @codeCoverageIgnoreEnd
$cache = new DoctrineCacheAdapter(new \Doctrine\Common\Cache\ApcCache());
} elseif (!($cache instanceof CacheAdapterInterface)) {
throw new InvalidArgumentException('Unable to utilize caching with the specified options');
}
// Decorate the credentials with a cache
return new CacheableCredentials($credentials, $cache, $cacheKey);
}
private static function getHomeDir()
{
// On Linux/Unix-like systems, use the HOME environment variable
if ($homeDir = self::getEnvVar('HOME')) {
return $homeDir;
}
// Get the HOMEDRIVE and HOMEPATH values for Windows hosts
$homeDrive = self::getEnvVar('HOMEDRIVE');
$homePath = self::getEnvVar('HOMEPATH');
return ($homeDrive && $homePath) ? $homeDrive . $homePath : null;
}
/**
* Fetches the value of an environment variable by checking $_SERVER and getenv().
*
* @param string $var Name of the environment variable
*
* @return mixed|null
*/
private static function getEnvVar($var)
{
return isset($_SERVER[$var]) ? $_SERVER[$var] : getenv($var);
}
}
@@ -0,0 +1,96 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Credentials;
/**
* Provides access to the AWS credentials used for accessing AWS services: AWS
* access key ID, secret access key, and security token. These credentials are
* used to securely sign requests to AWS services.
*/
interface CredentialsInterface extends \Serializable
{
/**
* Returns the AWS access key ID for this credentials object.
*
* @return string
*/
public function getAccessKeyId();
/**
* Returns the AWS secret access key for this credentials object.
*
* @return string
*/
public function getSecretKey();
/**
* Get the associated security token if available
*
* @return string|null
*/
public function getSecurityToken();
/**
* Get the UNIX timestamp in which the credentials will expire
*
* @return int|null
*/
public function getExpiration();
/**
* Set the AWS access key ID for this credentials object.
*
* @param string $key AWS access key ID
*
* @return self
*/
public function setAccessKeyId($key);
/**
* Set the AWS secret access key for this credentials object.
*
* @param string $secret AWS secret access key
*
* @return CredentialsInterface
*/
public function setSecretKey($secret);
/**
* Set the security token to use with this credentials object
*
* @param string $token Security token
*
* @return self
*/
public function setSecurityToken($token);
/**
* Set the UNIX timestamp in which the credentials will expire
*
* @param int $timestamp UNIX timestamp expiration
*
* @return self
*/
public function setExpiration($timestamp);
/**
* Check if the credentials are expired
*
* @return bool
*/
public function isExpired();
}
@@ -0,0 +1,68 @@
<?php
namespace Aws\Common\Credentials;
/**
* A blank set of credentials. AWS clients must be provided credentials, but
* there are some types of requests that do not need authentication. This class
* can be used to pivot on that scenario, and also serve as a mock credentials
* object when testing
*
* @codeCoverageIgnore
*/
class NullCredentials implements CredentialsInterface
{
public function getAccessKeyId()
{
return '';
}
public function getSecretKey()
{
return '';
}
public function getSecurityToken()
{
return null;
}
public function getExpiration()
{
return null;
}
public function isExpired()
{
return false;
}
public function serialize()
{
return 'N;';
}
public function unserialize($serialized)
{
// Nothing to do here.
}
public function setAccessKeyId($key)
{
// Nothing to do here.
}
public function setSecretKey($secret)
{
// Nothing to do here.
}
public function setSecurityToken($token)
{
// Nothing to do here.
}
public function setExpiration($timestamp)
{
// Nothing to do here.
}
}
@@ -0,0 +1,91 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Credentials;
use Aws\Common\InstanceMetadata\InstanceMetadataClient;
use Aws\Common\Exception\InstanceProfileCredentialsException;
/**
* Credentials decorator used to implement retrieving credentials from the
* EC2 metadata server
*/
class RefreshableInstanceProfileCredentials extends AbstractRefreshableCredentials
{
/**
* @var InstanceMetadataClient
*/
protected $client;
/** @var bool */
private $customClient;
/**
* Constructs a new instance profile credentials decorator
*
* @param CredentialsInterface $credentials Credentials to adapt
* @param InstanceMetadataClient $client Client used to get new credentials
*/
public function __construct(CredentialsInterface $credentials, InstanceMetadataClient $client = null)
{
parent::__construct($credentials);
$this->setClient($client);
}
public function setClient(InstanceMetadataClient $client = null)
{
$this->customClient = null !== $client;
$this->client = $client ?: InstanceMetadataClient::factory();
}
public function serialize()
{
$serializable = array(
'credentials' => parent::serialize(),
'customClient' => $this->customClient,
);
if ($this->customClient) {
$serializable['client'] = serialize($this->client);
}
return json_encode($serializable);
}
public function unserialize($value)
{
$serialized = json_decode($value, true);
parent::unserialize($serialized['credentials']);
$this->customClient = $serialized['customClient'];
$this->client = $this->customClient ?
unserialize($serialized['client'])
: InstanceMetadataClient::factory();
}
/**
* Attempt to get new credentials from the instance profile
*
* @throws InstanceProfileCredentialsException On error
*/
protected function refresh()
{
$credentials = $this->client->getInstanceProfileCredentials();
// Expire the token 5 minutes early to pre-fetch before expiring.
$this->credentials->setAccessKeyId($credentials->getAccessKeyId())
->setSecretKey($credentials->getSecretKey())
->setSecurityToken($credentials->getSecurityToken())
->setExpiration($credentials->getExpiration() - 300);
}
}
@@ -0,0 +1,55 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common;
/**
* Represents an enumerable set of values
*/
abstract class Enum
{
/**
* @var array A cache of all enum values to increase performance
*/
protected static $cache = array();
/**
* Returns the names (or keys) of all of constants in the enum
*
* @return array
*/
public static function keys()
{
return array_keys(static::values());
}
/**
* Return the names and values of all the constants in the enum
*
* @return array
*/
public static function values()
{
$class = get_called_class();
if (!isset(self::$cache[$class])) {
$reflected = new \ReflectionClass($class);
self::$cache[$class] = $reflected->getConstants();
}
return self::$cache[$class];
}
}
@@ -0,0 +1,167 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Enum;
use Aws\Common\Enum;
/**
* Contains enumerable default factory options that can be passed to a client's factory method
*/
class ClientOptions extends Enum
{
/**
* AWS Access Key ID
*
* @deprecated Use "credentials" instead.
*/
const KEY = 'key';
/**
* AWS secret access key
*
* @deprecated Use "credentials" instead.
*/
const SECRET = 'secret';
/**
* Custom AWS security token to use with request authentication.
*
* @deprecated Use "credentials" instead.
*/
const TOKEN = 'token';
/**
* Provide an array of "key", "secret", and "token" or an instance of
* `Aws\Common\Credentials\CredentialsInterface`.
*/
const CREDENTIALS = 'credentials';
/**
* @var string Name of a credential profile to read from your ~/.aws/credentials file
*/
const PROFILE = 'profile';
/**
* @var string UNIX timestamp for when the custom credentials expire
*/
const TOKEN_TTD = 'token.ttd';
/**
* @var string Used to cache credentials when using providers that require HTTP requests. Set the trueto use the
* default APC cache or provide a `Guzzle\Cache\CacheAdapterInterface` object.
*/
const CREDENTIALS_CACHE = 'credentials.cache';
/**
* @var string Optional custom cache key to use with the credentials
*/
const CREDENTIALS_CACHE_KEY = 'credentials.cache.key';
/**
* @var string Pass this option to specify a custom `Guzzle\Http\ClientInterface` to use if your credentials require
* a HTTP request (e.g. RefreshableInstanceProfileCredentials)
*/
const CREDENTIALS_CLIENT = 'credentials.client';
/**
* @var string Region name (e.g. 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', etc...)
*/
const REGION = 'region';
/**
* @var string URI Scheme of the base URL (e.g. 'https', 'http').
*/
const SCHEME = 'scheme';
/**
* @var string Specify the name of the service
*/
const SERVICE = 'service';
/**
* Instead of using a `region` and `scheme`, you can specify a custom base
* URL for the client.
*
* @deprecated Use the "endpoint" option instead.
*/
const BASE_URL = 'base_url';
/**
* @var string You can optionally provide a custom signature implementation used to sign requests
*/
const SIGNATURE = 'signature';
/**
* @var string Set to explicitly override the service name used in signatures
*/
const SIGNATURE_SERVICE = 'signature.service';
/**
* @var string Set to explicitly override the region name used in signatures
*/
const SIGNATURE_REGION = 'signature.region';
/**
* @var string Option key holding an exponential backoff plugin
*/
const BACKOFF = 'client.backoff';
/**
* @var string Option key holding the exponential backoff retries
*/
const BACKOFF_RETRIES = 'client.backoff.retries';
/**
* @var string `Guzzle\Log\LogAdapterInterface` object used to log backoff retries. Use 'debug' to emit PHP
* warnings when a retry is issued.
*/
const BACKOFF_LOGGER = 'client.backoff.logger';
/**
* @var string Optional template to use for exponential backoff log messages. See
* `Guzzle\Plugin\Backoff\BackoffLogger` for formatting information.
*/
const BACKOFF_LOGGER_TEMPLATE = 'client.backoff.logger.template';
/**
* Set to true to use the bundled CA cert or pass the full path to an SSL
* certificate bundle. This option should be modified when you encounter
* curl error code 60. Set to "system" to use the cacert bundle on your
* system.
*/
const SSL_CERT = 'ssl.certificate_authority';
/**
* @var string Service description to use with the client
*/
const SERVICE_DESCRIPTION = 'service.description';
/**
* @var string Whether or not modeled responses have transformations applied to them
*/
const MODEL_PROCESSING = 'command.model_processing';
/**
* @var bool Set to false to disable validation
*/
const VALIDATION = 'validation';
/**
* @var string API version used by the client
*/
const VERSION = 'version';
}
@@ -0,0 +1,31 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Enum;
use Aws\Common\Enum;
/**
* Contains enumerable date format values used in the SDK
*/
class DateFormat extends Enum
{
const ISO8601 = 'Ymd\THis\Z';
const ISO8601_S3 = 'Y-m-d\TH:i:s\Z';
const RFC1123 = 'D, d M Y H:i:s \G\M\T';
const RFC2822 = \DateTime::RFC2822;
const SHORT = 'Ymd';
}
@@ -0,0 +1,63 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Enum;
use Aws\Common\Enum;
/**
* Contains enumerable region code values. These should be useful in most cases,
* with Amazon S3 being the most notable exception
*
* @link http://docs.aws.amazon.com/general/latest/gr/rande.html AWS Regions and Endpoints
*/
class Region extends Enum
{
const US_EAST_1 = 'us-east-1';
const VIRGINIA = 'us-east-1';
const NORTHERN_VIRGINIA = 'us-east-1';
const US_WEST_1 = 'us-west-1';
const CALIFORNIA = 'us-west-1';
const NORTHERN_CALIFORNIA = 'us-west-1';
const US_WEST_2 = 'us-west-2';
const OREGON = 'us-west-2';
const EU_WEST_1 = 'eu-west-1';
const IRELAND = 'eu-west-1';
const EU_CENTRAL_1 = 'eu-central-1';
const FRANKFURT = 'eu-central-1';
const AP_SOUTHEAST_1 = 'ap-southeast-1';
const SINGAPORE = 'ap-southeast-1';
const AP_SOUTHEAST_2 = 'ap-southeast-2';
const SYDNEY = 'ap-southeast-2';
const AP_NORTHEAST_1 = 'ap-northeast-1';
const TOKYO = 'ap-northeast-1';
const SA_EAST_1 = 'sa-east-1';
const SAO_PAULO = 'sa-east-1';
const CN_NORTH_1 = 'cn-north-1';
const BEIJING = 'cn-north-1';
const US_GOV_WEST_1 = 'us-gov-west-1';
const GOV_CLOUD_US = 'us-gov-west-1';
}
@@ -0,0 +1,53 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Enum;
use Aws\Common\Enum;
/**
* Contains enumerable byte-size values
*/
class Size extends Enum
{
const B = 1;
const BYTE = 1;
const BYTES = 1;
const KB = 1024;
const KILOBYTE = 1024;
const KILOBYTES = 1024;
const MB = 1048576;
const MEGABYTE = 1048576;
const MEGABYTES = 1048576;
const GB = 1073741824;
const GIGABYTE = 1073741824;
const GIGABYTES = 1073741824;
const TB = 1099511627776;
const TERABYTE = 1099511627776;
const TERABYTES = 1099511627776;
const PB = 1125899906842624;
const PETABYTE = 1125899906842624;
const PETABYTES = 1125899906842624;
const EB = 1152921504606846976;
const EXABYTE = 1152921504606846976;
const EXABYTES = 1152921504606846976;
}
@@ -0,0 +1,46 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Enum;
use Aws\Common\Enum;
/**
* Contains enumerable time values
*/
class Time extends Enum
{
const SECOND = 1;
const SECONDS = 1;
const MINUTE = 60;
const MINUTES = 60;
const HOUR = 3600;
const HOURS = 3600;
const DAY = 86400;
const DAYS = 86400;
const WEEK = 604800;
const WEEKS = 604800;
const MONTH = 2592000;
const MONTHS = 2592000;
const YEAR = 31557600;
const YEARS = 31557600;
}
@@ -0,0 +1,55 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Enum;
use Aws\Common\Enum;
/**
* User-Agent header strings for various high level operations
*/
class UaString extends Enum
{
/**
* @var string Name of the option used to add to the UA string
*/
const OPTION = 'ua.append';
/**
* @var string Resource iterator
*/
const ITERATOR = 'ITR';
/**
* @var string Resource waiter
*/
const WAITER = 'WTR';
/**
* @var string Session handlers (e.g. Amazon DynamoDB session handler)
*/
const SESSION = 'SES';
/**
* @var string Multipart upload helper for Amazon S3
*/
const MULTIPART_UPLOAD = 'MUP';
/**
* @var string Command executed during a batch transfer
*/
const BATCH = 'BAT';
}
@@ -0,0 +1,30 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* "Marker Interface" implemented by every exception in the AWS SDK
*/
interface AwsExceptionInterface
{
public function getCode();
public function getLine();
public function getFile();
public function getMessage();
public function getPrevious();
public function getTrace();
}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL BadMethodCallException.
*/
class BadMethodCallException extends \BadMethodCallException implements AwsExceptionInterface {}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL DomainException.
*/
class DomainException extends \DomainException implements AwsExceptionInterface {}
@@ -0,0 +1,36 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Interface used to create AWS exception
*/
interface ExceptionFactoryInterface
{
/**
* Returns an AWS service specific exception
*
* @param RequestInterface $request Unsuccessful request
* @param Response $response Unsuccessful response that was encountered
*
* @return \Exception|AwsExceptionInterface
*/
public function fromResponse(RequestInterface $request, Response $response);
}
@@ -0,0 +1,59 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Converts generic Guzzle response exceptions into AWS specific exceptions
*/
class ExceptionListener implements EventSubscriberInterface
{
/**
* @var ExceptionFactoryInterface Factory used to create new exceptions
*/
protected $factory;
/**
* @param ExceptionFactoryInterface $factory Factory used to create exceptions
*/
public function __construct(ExceptionFactoryInterface $factory)
{
$this->factory = $factory;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array('request.error' => array('onRequestError', -1));
}
/**
* Throws a more meaningful request exception if available
*
* @param Event $event Event emitted
*/
public function onRequestError(Event $event)
{
$e = $this->factory->fromResponse($event['request'], $event['response']);
$event->stopPropagation();
throw $e;
}
}
@@ -0,0 +1,50 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Aws\Common\Exception\RuntimeException;
/**
* Exception thrown when an error occurs with instance profile credentials
*/
class InstanceProfileCredentialsException extends RuntimeException
{
/**
* @var string
*/
protected $statusCode;
/**
* Set the error response code received from the instance metadata
*
* @param string $code Response code
*/
public function setStatusCode($code)
{
$this->statusCode = $code;
}
/**
* Get the error response code from the service
*
* @return string|null
*/
public function getStatusCode()
{
return $this->statusCode;
}
}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL InvalidArgumentException.
*/
class InvalidArgumentException extends \InvalidArgumentException implements AwsExceptionInterface {}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL LogicException.
*/
class LogicException extends \LogicException implements AwsExceptionInterface {}
@@ -0,0 +1,55 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Aws\Common\Model\MultipartUpload\TransferStateInterface;
/**
* Thrown when a {@see Aws\Common\MultipartUpload\TransferInterface} object encounters an error during transfer
*/
class MultipartUploadException extends RuntimeException
{
/**
* @var TransferStateInterface State of the transfer when the error was encountered
*/
protected $state;
/**
* @param TransferStateInterface $state Transfer state
* @param \Exception $exception Last encountered exception
*/
public function __construct(TransferStateInterface $state, \Exception $exception = null)
{
parent::__construct(
'An error was encountered while performing a multipart upload: ' . $exception->getMessage(),
0,
$exception
);
$this->state = $state;
}
/**
* Get the state of the transfer
*
* @return TransferStateInterface
*/
public function getState()
{
return $this->state;
}
}
@@ -0,0 +1,103 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Aws\Common\Exception\Parser\ExceptionParserInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Attempts to create exceptions by inferring the name from the code and a base
* namespace that contains exceptions. Exception classes are expected to be in
* upper camelCase and always end in 'Exception'. 'Exception' will be appended
* if it is not present in the exception code.
*/
class NamespaceExceptionFactory implements ExceptionFactoryInterface
{
/**
* @var ExceptionParserInterface $parser Parser used to parse responses
*/
protected $parser;
/**
* @var string Base namespace containing exception classes
*/
protected $baseNamespace;
/**
* @var string Default class to instantiate if a match is not found
*/
protected $defaultException;
/**
* @param ExceptionParserInterface $parser Parser used to parse exceptions
* @param string $baseNamespace Namespace containing exceptions
* @param string $defaultException Default class to use if one is not mapped
*/
public function __construct(
ExceptionParserInterface $parser,
$baseNamespace,
$defaultException = 'Aws\Common\Exception\ServiceResponseException'
) {
$this->parser = $parser;
$this->baseNamespace = $baseNamespace;
$this->defaultException = $defaultException;
}
/**
* {@inheritdoc}
*/
public function fromResponse(RequestInterface $request, Response $response)
{
$parts = $this->parser->parse($request, $response);
// Removing leading 'AWS.' and embedded periods
$className = $this->baseNamespace . '\\' . str_replace(array('AWS.', '.'), '', $parts['code']);
if (substr($className, -9) !== 'Exception') {
$className .= 'Exception';
}
$className = class_exists($className) ? $className : $this->defaultException;
return $this->createException($className, $request, $response, $parts);
}
/**
* Create an prepare an exception object
*
* @param string $className Name of the class to create
* @param RequestInterface $request Request
* @param Response $response Response received
* @param array $parts Parsed exception data
*
* @return \Exception
*/
protected function createException($className, RequestInterface $request, Response $response, array $parts)
{
$class = new $className($parts['message']);
if ($class instanceof ServiceResponseException) {
$class->setExceptionCode($parts['code']);
$class->setExceptionType($parts['type']);
$class->setResponse($response);
$class->setRequest($request);
$class->setRequestId($parts['request_id']);
}
return $class;
}
}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL OverflowException.
*/
class OutOfBoundsException extends \OutOfBoundsException implements AwsExceptionInterface {}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL OverflowException.
*/
class OverflowException extends \OverflowException implements AwsExceptionInterface {}
@@ -0,0 +1,66 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception\Parser;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Parses JSON encoded exception responses
*/
abstract class AbstractJsonExceptionParser implements ExceptionParserInterface
{
/**
* {@inheritdoc}
*/
public function parse(RequestInterface $request, Response $response)
{
// Build array of default error data
$data = array(
'code' => null,
'message' => null,
'type' => $response->isClientError() ? 'client' : 'server',
'request_id' => (string) $response->getHeader('x-amzn-RequestId'),
'parsed' => null
);
// Parse the json and normalize key casings
if (null !== $json = json_decode($response->getBody(true), true)) {
$data['parsed'] = array_change_key_case($json);
}
// Do additional, protocol-specific parsing and return the result
$data = $this->doParse($data, $response);
// Remove "Fault" suffix from exception names
if (isset($data['code']) && strpos($data['code'], 'Fault')) {
$data['code'] = preg_replace('/^([a-zA-Z]+)Fault$/', '$1', $data['code']);
}
return $data;
}
/**
* Pull relevant exception data out of the parsed json
*
* @param array $data The exception data
* @param Response $response The response from the service containing the error
*
* @return array
*/
abstract protected function doParse(array $data, Response $response);
}
@@ -0,0 +1,109 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception\Parser;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Parses default XML exception responses
*/
class DefaultXmlExceptionParser implements ExceptionParserInterface
{
public function parse(RequestInterface $request, Response $response)
{
$data = array(
'code' => null,
'message' => null,
'type' => $response->isClientError() ? 'client' : 'server',
'request_id' => null,
'parsed' => null
);
$body = $response->getBody(true);
if (!$body) {
$this->parseHeaders($request, $response, $data);
return $data;
}
try {
$xml = new \SimpleXMLElement($body);
$this->parseBody($xml, $data);
return $data;
} catch (\Exception $e) {
// Gracefully handle parse errors. This could happen when the
// server responds with a non-XML response (e.g., private beta
// services).
$data['code'] = 'PhpInternalXmlParseError';
$data['message'] = 'A non-XML response was received';
return $data;
}
}
/**
* Parses additional exception information from the response headers
*
* @param RequestInterface $request Request that was issued
* @param Response $response The response from the request
* @param array $data The current set of exception data
*/
protected function parseHeaders(RequestInterface $request, Response $response, array &$data)
{
$data['message'] = $response->getStatusCode() . ' ' . $response->getReasonPhrase();
if ($requestId = $response->getHeader('x-amz-request-id')) {
$data['request_id'] = $requestId;
$data['message'] .= " (Request-ID: $requestId)";
}
}
/**
* Parses additional exception information from the response body
*
* @param \SimpleXMLElement $body The response body as XML
* @param array $data The current set of exception data
*/
protected function parseBody(\SimpleXMLElement $body, array &$data)
{
$data['parsed'] = $body;
$namespaces = $body->getDocNamespaces();
if (isset($namespaces[''])) {
// Account for the default namespace being defined and PHP not being able to handle it :(
$body->registerXPathNamespace('ns', $namespaces['']);
$prefix = 'ns:';
} else {
$prefix = '';
}
if ($tempXml = $body->xpath("//{$prefix}Code[1]")) {
$data['code'] = (string) $tempXml[0];
}
if ($tempXml = $body->xpath("//{$prefix}Message[1]")) {
$data['message'] = (string) $tempXml[0];
}
$tempXml = $body->xpath("//{$prefix}RequestId[1]");
if (empty($tempXml)) {
$tempXml = $body->xpath("//{$prefix}RequestID[1]");
}
if (isset($tempXml[0])) {
$data['request_id'] = (string) $tempXml[0];
}
}
}
@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception\Parser;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Interface used to parse exceptions into an associative array of data
*/
interface ExceptionParserInterface
{
/**
* Parses an exception into an array of data containing at minimum the
* following array keys:
* - type: Exception type
* - code: Exception code
* - message: Exception message
* - request_id: Request ID
* - parsed: The parsed representation of the data (array, SimpleXMLElement, etc)
*
* @param RequestInterface $request
* @param Response $response Unsuccessful response
*
* @return array
*/
public function parse(RequestInterface $request, Response $response);
}
@@ -0,0 +1,41 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception\Parser;
use Guzzle\Http\Message\Response;
/**
* Parses JSON encoded exception responses from query services
*/
class JsonQueryExceptionParser extends AbstractJsonExceptionParser
{
/**
* {@inheritdoc}
*/
protected function doParse(array $data, Response $response)
{
if ($json = $data['parsed']) {
if (isset($json['__type'])) {
$parts = explode('#', $json['__type']);
$data['code'] = isset($parts[1]) ? $parts[1] : $parts[0];
}
$data['message'] = isset($json['message']) ? $json['message'] : null;
}
return $data;
}
}
@@ -0,0 +1,48 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception\Parser;
use Guzzle\Http\Message\Response;
/**
* Parses JSON encoded exception responses from REST services
*/
class JsonRestExceptionParser extends AbstractJsonExceptionParser
{
/**
* {@inheritdoc}
*/
protected function doParse(array $data, Response $response)
{
// Merge in error data from the JSON body
if ($json = $data['parsed']) {
$data = array_replace($data, $json);
}
// Correct error type from services like Amazon Glacier
if (!empty($data['type'])) {
$data['type'] = strtolower($data['type']);
}
// Retrieve the error code from services like Amazon Elastic Transcoder
if ($code = (string) $response->getHeader('x-amzn-ErrorType')) {
$data['code'] = substr($code, 0, strpos($code, ':'));
}
return $data;
}
}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* Thrown when a particular PHP extension is required to execute the guarded logic, but the extension is not loaded
*/
class RequiredExtensionNotLoadedException extends RuntimeException {}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL RuntimeException.
*/
class RuntimeException extends \RuntimeException implements AwsExceptionInterface {}
@@ -0,0 +1,221 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\Response;
/**
* Default AWS exception
*/
class ServiceResponseException extends RuntimeException
{
/**
* @var Response Response
*/
protected $response;
/**
* @var RequestInterface Request
*/
protected $request;
/**
* @var string Request ID
*/
protected $requestId;
/**
* @var string Exception type (client / server)
*/
protected $exceptionType;
/**
* @var string Exception code
*/
protected $exceptionCode;
/**
* Set the exception code
*
* @param string $code Exception code
*/
public function setExceptionCode($code)
{
$this->exceptionCode = $code;
}
/**
* Get the exception code
*
* @return string|null
*/
public function getExceptionCode()
{
return $this->exceptionCode;
}
/**
* Set the exception type
*
* @param string $type Exception type
*/
public function setExceptionType($type)
{
$this->exceptionType = $type;
}
/**
* Get the exception type (one of client or server)
*
* @return string|null
*/
public function getExceptionType()
{
return $this->exceptionType;
}
/**
* Set the request ID
*
* @param string $id Request ID
*/
public function setRequestId($id)
{
$this->requestId = $id;
}
/**
* Get the Request ID
*
* @return string|null
*/
public function getRequestId()
{
return $this->requestId;
}
/**
* Set the associated response
*
* @param Response $response Response
*/
public function setResponse(Response $response)
{
$this->response = $response;
}
/**
* Get the associated response object
*
* @return Response|null
*/
public function getResponse()
{
return $this->response;
}
/**
* Set the associated request
*
* @param RequestInterface $request
*/
public function setRequest(RequestInterface $request)
{
$this->request = $request;
}
/**
* Get the associated request object
*
* @return RequestInterface|null
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the status code of the response
*
* @return int|null
*/
public function getStatusCode()
{
return $this->response ? $this->response->getStatusCode() : null;
}
/**
* Cast to a string
*
* @return string
*/
public function __toString()
{
$message = get_class($this) . ': '
. 'AWS Error Code: ' . $this->getExceptionCode() . ', '
. 'Status Code: ' . $this->getStatusCode() . ', '
. 'AWS Request ID: ' . $this->getRequestId() . ', '
. 'AWS Error Type: ' . $this->getExceptionType() . ', '
. 'AWS Error Message: ' . $this->getMessage();
// Add the User-Agent if available
if ($this->request) {
$message .= ', ' . 'User-Agent: ' . $this->request->getHeader('User-Agent');
}
return $message;
}
/**
* 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.
*
* Same as `getRequestId()` method, but matches the interface for SDKv3.
*
* @return string|null Returns null if no response was received
*/
public function getAwsRequestId()
{
return $this->requestId;
}
/**
* Get the AWS error type.
*
* Same as `getExceptionType()` method, but matches the interface for SDKv3.
*
* @return string|null Returns null if no response was received
*/
public function getAwsErrorType()
{
return $this->exceptionType;
}
/**
* Get the AWS error code.
*
* Same as `getExceptionCode()` method, but matches the interface for SDKv3.
*
* @return string|null Returns null if no response was received
*/
public function getAwsErrorCode()
{
return $this->exceptionCode;
}
}
@@ -0,0 +1,24 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
use Guzzle\Http\Exception\CurlException;
/**
* Transfer request exception
*/
class TransferException extends CurlException implements AwsExceptionInterface {}
@@ -0,0 +1,22 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Exception;
/**
* AWS SDK namespaced version of the SPL UnexpectedValueException.
*/
class UnexpectedValueException extends \UnexpectedValueException implements AwsExceptionInterface {}
@@ -0,0 +1,69 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Facade;
use Aws\Common\Aws;
/**
* Base facade class that handles the delegation logic
*
* @deprecated "Facades" are being removed in version 3.0 of the SDK.
*/
abstract class Facade implements FacadeInterface
{
/** @var Aws */
protected static $serviceBuilder;
/**
* Mounts the facades by extracting information from the service builder config and using creating class aliases
*
* @param string|null $targetNamespace Namespace that the facades should be mounted to. Defaults to global namespace
*
* @param Aws $serviceBuilder
*/
public static function mountFacades(Aws $serviceBuilder, $targetNamespace = null)
{
self::$serviceBuilder = $serviceBuilder;
require_once __DIR__ . '/facade-classes.php';
foreach ($serviceBuilder->getConfig() as $service) {
if (isset($service['alias'], $service['class'])) {
$facadeClass = __NAMESPACE__ . '\\' . $service['alias'];
$facadeAlias = ltrim($targetNamespace . '\\' . $service['alias'], '\\');
if (!class_exists($facadeAlias) && class_exists($facadeClass)) {
// @codeCoverageIgnoreStart
class_alias($facadeClass, $facadeAlias);
// @codeCoverageIgnoreEnd
}
}
}
}
/**
* Returns the instance of the client that the facade operates on
*
* @return \Aws\Common\Client\AwsClientInterface
*/
public static function getClient()
{
return self::$serviceBuilder->get(static::getServiceBuilderKey());
}
public static function __callStatic($method, $args)
{
return call_user_func_array(array(self::getClient(), $method), $args);
}
}
@@ -0,0 +1,34 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Facade;
/**
* Interface that defines a client facade. Facades are convenient static classes that allow you to run client methods
* statically on a default instance from the service builder. The facades themselves are aliased into the global
* namespace for ease of use.
*
* @deprecated "Facades" are being removed in version 3.0 of the SDK.
*/
interface FacadeInterface
{
/**
* Returns the key used to access the client instance from the Service Builder
*
* @return string
*/
public static function getServiceBuilderKey();
}
@@ -0,0 +1,283 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Facade;
/**
* The following classes are used to implement the static client facades and are aliased into the global namespaced. We
* discourage the use of these classes directly by their full namespace since they are not autoloaded and are considered
* an implementation detail that could possibly be changed in the future.
*/
// @codeCoverageIgnoreStart
class AutoScaling extends Facade
{
public static function getServiceBuilderKey()
{
return 'autoscaling';
}
}
class CloudFormation extends Facade
{
public static function getServiceBuilderKey()
{
return 'cloudformation';
}
}
class CloudFront extends Facade
{
public static function getServiceBuilderKey()
{
return 'cloudfront';
}
}
class CloudSearch extends Facade
{
public static function getServiceBuilderKey()
{
return 'cloudsearch';
}
}
class CloudTrail extends Facade
{
public static function getServiceBuilderKey()
{
return 'cloudtrail';
}
}
class CloudWatch extends Facade
{
public static function getServiceBuilderKey()
{
return 'cloudwatch';
}
}
class DataPipeline extends Facade
{
public static function getServiceBuilderKey()
{
return 'datapipeline';
}
}
class DirectConnect extends Facade
{
public static function getServiceBuilderKey()
{
return 'directconnect';
}
}
class DynamoDb extends Facade
{
public static function getServiceBuilderKey()
{
return 'dynamodb';
}
}
class Ec2 extends Facade
{
public static function getServiceBuilderKey()
{
return 'ec2';
}
}
class ElastiCache extends Facade
{
public static function getServiceBuilderKey()
{
return 'elasticache';
}
}
class ElasticBeanstalk extends Facade
{
public static function getServiceBuilderKey()
{
return 'elasticbeanstalk';
}
}
class ElasticLoadBalancing extends Facade
{
public static function getServiceBuilderKey()
{
return 'elasticloadbalancing';
}
}
class ElasticTranscoder extends Facade
{
public static function getServiceBuilderKey()
{
return 'elastictranscoder';
}
}
class Emr extends Facade
{
public static function getServiceBuilderKey()
{
return 'emr';
}
}
class Glacier extends Facade
{
public static function getServiceBuilderKey()
{
return 'glacier';
}
}
class Iam extends Facade
{
public static function getServiceBuilderKey()
{
return 'iam';
}
}
class ImportExport extends Facade
{
public static function getServiceBuilderKey()
{
return 'importexport';
}
}
class Kinesis extends Facade
{
public static function getServiceBuilderKey()
{
return 'kinesis';
}
}
class OpsWorks extends Facade
{
public static function getServiceBuilderKey()
{
return 'opsworks';
}
}
class Rds extends Facade
{
public static function getServiceBuilderKey()
{
return 'rds';
}
}
class Redshift extends Facade
{
public static function getServiceBuilderKey()
{
return 'redshift';
}
}
class Route53 extends Facade
{
public static function getServiceBuilderKey()
{
return 'route53';
}
}
class S3 extends Facade
{
public static function getServiceBuilderKey()
{
return 's3';
}
}
class SimpleDb extends Facade
{
public static function getServiceBuilderKey()
{
return 'sdb';
}
}
class Ses extends Facade
{
public static function getServiceBuilderKey()
{
return 'ses';
}
}
class Sns extends Facade
{
public static function getServiceBuilderKey()
{
return 'sns';
}
}
class Sqs extends Facade
{
public static function getServiceBuilderKey()
{
return 'sqs';
}
}
class StorageGateway extends Facade
{
public static function getServiceBuilderKey()
{
return 'storagegateway';
}
}
class Sts extends Facade
{
public static function getServiceBuilderKey()
{
return 'sts';
}
}
class Support extends Facade
{
public static function getServiceBuilderKey()
{
return 'support';
}
}
class Swf extends Facade
{
public static function getServiceBuilderKey()
{
return 'swf';
}
}
// @codeCoverageIgnoreEnd
@@ -0,0 +1,87 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Hash;
use Aws\Common\Exception\LogicException;
/**
* Encapsulates the creation of a hash from streamed chunks of data
*/
class ChunkHash implements ChunkHashInterface
{
/**
* @var resource The hash context as created by `hash_init()`
*/
protected $context;
/**
* @var string The resulting hash in hex form
*/
protected $hash;
/**
* @var string The resulting hash in binary form
*/
protected $hashRaw;
/**
* {@inheritdoc}
*/
public function __construct($algorithm = self::DEFAULT_ALGORITHM)
{
HashUtils::validateAlgorithm($algorithm);
$this->context = hash_init($algorithm);
}
/**
* {@inheritdoc}
*/
public function addData($data)
{
if (!$this->context) {
throw new LogicException('You may not add more data to a finalized chunk hash.');
}
hash_update($this->context, $data);
return $this;
}
/**
* {@inheritdoc}
*/
public function getHash($returnBinaryForm = false)
{
if (!$this->hash) {
$this->hashRaw = hash_final($this->context, true);
$this->hash = HashUtils::binToHex($this->hashRaw);
$this->context = null;
}
return $returnBinaryForm ? $this->hashRaw : $this->hash;
}
/**
* {@inheritdoc}
*/
public function __clone()
{
if ($this->context) {
$this->context = hash_copy($this->context);
}
}
}
@@ -0,0 +1,52 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Hash;
/**
* Interface for objects that encapsulate the creation of a hash from streamed chunks of data
*/
interface ChunkHashInterface
{
const DEFAULT_ALGORITHM = 'sha256';
/**
* Constructs the chunk hash and sets the algorithm to use for hashing
*
* @param string $algorithm A valid hash algorithm name as returned by `hash_algos()`
*
* @return self
*/
public function __construct($algorithm = 'sha256');
/**
* Add a chunk of data to be hashed
*
* @param string $data Data to be hashed
*
* @return self
*/
public function addData($data);
/**
* Return the results of the hash
*
* @param bool $returnBinaryForm If true, returns the hash in binary form instead of hex form
*
* @return string
*/
public function getHash($returnBinaryForm = false);
}
@@ -0,0 +1,76 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Hash;
use Aws\Common\Exception\InvalidArgumentException;
/**
* Contains hashing utilities
*/
class HashUtils
{
/**
* Converts a hash in hex form to binary form
*
* @param string $hash Hash in hex form
*
* @return string Hash in binary form
*/
public static function hexToBin($hash)
{
// If using PHP 5.4, there is a native function to convert from hex to binary
static $useNative;
if ($useNative === null) {
$useNative = function_exists('hex2bin');
}
if (!$useNative && strlen($hash) % 2 !== 0) {
$hash = '0' . $hash;
}
return $useNative ? hex2bin($hash) : pack("H*", $hash);
}
/**
* Converts a hash in binary form to hex form
*
* @param string $hash Hash in binary form
*
* @return string Hash in hex form
*/
public static function binToHex($hash)
{
return bin2hex($hash);
}
/**
* Checks if the algorithm specified exists and throws an exception if it does not
*
* @param string $algorithm Name of the algorithm to validate
*
* @return bool
* @throws InvalidArgumentException if the algorithm doesn't exist
*/
public static function validateAlgorithm($algorithm)
{
if (!in_array($algorithm, hash_algos(), true)) {
throw new InvalidArgumentException("The hashing algorithm specified ({$algorithm}) does not exist.");
}
return true;
}
}
@@ -0,0 +1,195 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Hash;
use Aws\Common\Enum\Size;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\LogicException;
use Guzzle\Http\EntityBody;
/**
* Encapsulates the creation of a tree hash from streamed chunks of data
*/
class TreeHash implements ChunkHashInterface
{
/**
* @var string The algorithm used for hashing
*/
protected $algorithm;
/**
* @var array Set of binary checksums from which the tree hash is derived
*/
protected $checksums = array();
/**
* @var string The resulting hash in hex form
*/
protected $hash;
/**
* @var string The resulting hash in binary form
*/
protected $hashRaw;
/**
* Create a tree hash from an array of existing tree hash checksums
*
* @param array $checksums Set of checksums
* @param bool $inBinaryForm Whether or not the checksums are already in binary form
* @param string $algorithm A valid hash algorithm name as returned by `hash_algos()`
*
* @return TreeHash
*/
public static function fromChecksums(array $checksums, $inBinaryForm = false, $algorithm = self::DEFAULT_ALGORITHM)
{
$treeHash = new self($algorithm);
// Convert checksums to binary form if provided in hex form and add them to the tree hash
$treeHash->checksums = $inBinaryForm ? $checksums : array_map('Aws\Common\Hash\HashUtils::hexToBin', $checksums);
// Pre-calculate hash
$treeHash->getHash();
return $treeHash;
}
/**
* Create a tree hash from a content body
*
* @param string|resource|EntityBody $content Content to create a tree hash for
* @param string $algorithm A valid hash algorithm name as returned by `hash_algos()`
*
* @return TreeHash
*/
public static function fromContent($content, $algorithm = self::DEFAULT_ALGORITHM)
{
$treeHash = new self($algorithm);
// Read the data in 1MB chunks and add to tree hash
$content = EntityBody::factory($content);
while ($data = $content->read(Size::MB)) {
$treeHash->addData($data);
}
// Pre-calculate hash
$treeHash->getHash();
return $treeHash;
}
/**
* Validates an entity body with a tree hash checksum
*
* @param string|resource|EntityBody $content Content to create a tree hash for
* @param string $checksum The checksum to use for validation
* @param string $algorithm A valid hash algorithm name as returned by `hash_algos()`
*
* @return bool
*/
public static function validateChecksum($content, $checksum, $algorithm = self::DEFAULT_ALGORITHM)
{
$treeHash = self::fromContent($content, $algorithm);
return ($checksum === $treeHash->getHash());
}
/**
* {@inheritdoc}
*/
public function __construct($algorithm = self::DEFAULT_ALGORITHM)
{
HashUtils::validateAlgorithm($algorithm);
$this->algorithm = $algorithm;
}
/**
* {@inheritdoc}
* @throws LogicException if the root tree hash is already calculated
* @throws InvalidArgumentException if the data is larger than 1MB
*/
public function addData($data)
{
// Error if hash is already calculated
if ($this->hash) {
throw new LogicException('You may not add more data to a finalized tree hash.');
}
// Make sure that only 1MB chunks or smaller get passed in
if (strlen($data) > Size::MB) {
throw new InvalidArgumentException('The chunk of data added is too large for tree hashing.');
}
// Store the raw hash of this data segment
$this->checksums[] = hash($this->algorithm, $data, true);
return $this;
}
/**
* Add a checksum to the tree hash directly
*
* @param string $checksum The checksum to add
* @param bool $inBinaryForm Whether or not the checksum is already in binary form
*
* @return self
* @throws LogicException if the root tree hash is already calculated
*/
public function addChecksum($checksum, $inBinaryForm = false)
{
// Error if hash is already calculated
if ($this->hash) {
throw new LogicException('You may not add more checksums to a finalized tree hash.');
}
// Convert the checksum to binary form if necessary
$this->checksums[] = $inBinaryForm ? $checksum : HashUtils::hexToBin($checksum);
return $this;
}
/**
* {@inheritdoc}
*/
public function getHash($returnBinaryForm = false)
{
if (!$this->hash) {
// Perform hashes up the tree to arrive at the root checksum of the tree hash
$hashes = $this->checksums;
while (count($hashes) > 1) {
$sets = array_chunk($hashes, 2);
$hashes = array();
foreach ($sets as $set) {
$hashes[] = (count($set) === 1) ? $set[0] : hash($this->algorithm, $set[0] . $set[1], true);
}
}
$this->hashRaw = $hashes[0];
$this->hash = HashUtils::binToHex($this->hashRaw);
}
return $returnBinaryForm ? $this->hashRaw : $this->hash;
}
/**
* @return array Array of raw checksums composing the tree hash
*/
public function getChecksums()
{
return $this->checksums;
}
}
@@ -0,0 +1,85 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common;
use Guzzle\Http\Url;
/**
* Utility class for parsing regions and services from URLs
*/
class HostNameUtils
{
const DEFAULT_REGION = 'us-east-1';
const DEFAULT_GOV_REGION = 'us-gov-west-1';
/**
* Parse the AWS region name from a URL
*
*
* @param Url $url HTTP URL
*
* @return string
* @link http://docs.aws.amazon.com/general/latest/gr/rande.html
*/
public static function parseRegionName(Url $url)
{
// If we don't recognize the domain, just return the default
if (substr($url->getHost(), -14) != '.amazonaws.com') {
return self::DEFAULT_REGION;
}
$serviceAndRegion = substr($url->getHost(), 0, -14);
// Special handling for S3 regions
$separator = strpos($serviceAndRegion, 's3') === 0 ? '-' : '.';
$separatorPos = strpos($serviceAndRegion, $separator);
// If don't detect a separator, then return the default region
if ($separatorPos === false) {
return self::DEFAULT_REGION;
}
$region = substr($serviceAndRegion, $separatorPos + 1);
// All GOV regions currently use the default GOV region
if ($region == 'us-gov') {
return self::DEFAULT_GOV_REGION;
}
return $region;
}
/**
* Parse the AWS service name from a URL
*
* @param Url $url HTTP URL
*
* @return string Returns a service name (or empty string)
* @link http://docs.aws.amazon.com/general/latest/gr/rande.html
*/
public static function parseServiceName(Url $url)
{
// The service name is the first part of the host
$parts = explode('.', $url->getHost(), 2);
// Special handling for S3
if (stripos($parts[0], 's3') === 0) {
return 's3';
}
return $parts[0];
}
}
@@ -0,0 +1,102 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\InstanceMetadata;
use Aws\Common\Enum\ClientOptions as Options;
use Aws\Common\Exception\InstanceProfileCredentialsException;
use Aws\Common\Credentials\Credentials;
use Aws\Common\Client\AbstractClient;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\RequestFactory;
/**
* Client used for interacting with the Amazon EC2 instance metadata server
*/
class InstanceMetadataClient extends AbstractClient
{
/**
* Factory method to create a new InstanceMetadataClient using an array
* of configuration options.
*
* The configuration options accepts the following array keys and values:
* - base_url: Override the base URL of the instance metadata server
* - version: Version of the metadata server to interact with
*
* @param array|Collection $config Configuration options
*
* @return InstanceMetadataClient
*/
public static function factory($config = array())
{
$config = Collection::fromConfig($config, array(
Options::BASE_URL => 'http://169.254.169.254/{version}/',
'version' => 'latest',
'request.options' => array(
'connect_timeout' => 5,
'timeout' => 10
)
), array('base_url', 'version'));
return new self($config);
}
/**
* Constructor override
*/
public function __construct(Collection $config)
{
$this->setConfig($config);
$this->setBaseUrl($config->get(Options::BASE_URL));
$this->defaultHeaders = new Collection();
$this->setRequestFactory(RequestFactory::getInstance());
}
/**
* Get instance profile credentials
*
* @return Credentials
* @throws InstanceProfileCredentialsException
*/
public function getInstanceProfileCredentials()
{
try {
$request = $this->get('meta-data/iam/security-credentials/');
$credentials = trim($request->send()->getBody(true));
$result = $this->get("meta-data/iam/security-credentials/{$credentials}")->send()->json();
} catch (\Exception $e) {
$message = sprintf('Error retrieving credentials from the instance profile metadata server. When you are'
. ' not running inside of Amazon EC2, you must provide your AWS access key ID and secret access key in'
. ' the "key" and "secret" options when creating a client or provide an instantiated'
. ' Aws\\Common\\Credentials\\CredentialsInterface object. (%s)', $e->getMessage());
throw new InstanceProfileCredentialsException($message, $e->getCode());
}
// Ensure that the status code was successful
if ($result['Code'] !== 'Success') {
$e = new InstanceProfileCredentialsException('Unexpected response code: ' . $result['Code']);
$e->setStatusCode($result['Code']);
throw $e;
}
return new Credentials(
$result['AccessKeyId'],
$result['SecretAccessKey'],
$result['Token'],
strtotime($result['Expiration'])
);
}
}
@@ -0,0 +1,50 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\InstanceMetadata\Waiter;
use Aws\Common\Waiter\AbstractResourceWaiter;
use Guzzle\Http\Exception\CurlException;
/**
* Waits until the instance metadata service is responding. Will send up to
* 4 requests with a 5 second delay between each try. Each try can last up to
* 11 seconds to complete if the service is not responding.
*
* @codeCoverageIgnore
*/
class ServiceAvailable extends AbstractResourceWaiter
{
protected $interval = 5;
protected $maxAttempts = 4;
/**
* {@inheritdoc}
*/
public function doWait()
{
$request = $this->client->get();
try {
$request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 10)
->set(CURLOPT_TIMEOUT, 10);
$request->send();
return true;
} catch (CurlException $e) {
return false;
}
}
}
@@ -0,0 +1,169 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Iterator;
use Aws\Common\Enum\UaString as Ua;
use Aws\Common\Exception\RuntimeException;
Use Guzzle\Service\Resource\Model;
use Guzzle\Service\Resource\ResourceIterator;
/**
* Iterate over a client command
*/
class AwsResourceIterator extends ResourceIterator
{
/**
* @var Model Result of a command
*/
protected $lastResult = null;
/**
* Provides access to the most recent result obtained by the iterator. This makes it easier to extract any
* additional information from the result which you do not have access to from the values emitted by the iterator
*
* @return Model|null
*/
public function getLastResult()
{
return $this->lastResult;
}
/**
* {@inheritdoc}
* This AWS specific version of the resource iterator provides a default implementation of the typical AWS iterator
* process. It relies on configuration and extension to implement the operation-specific logic of handling results
* and nextTokens. This method will loop until resources are acquired or there are no more iterations available.
*/
protected function sendRequest()
{
do {
// Prepare the request including setting the next token
$this->prepareRequest();
if ($this->nextToken) {
$this->applyNextToken();
}
// Execute the request and handle the results
$this->command->add(Ua::OPTION, Ua::ITERATOR);
$this->lastResult = $this->command->getResult();
$resources = $this->handleResults($this->lastResult);
$this->determineNextToken($this->lastResult);
// If no resources collected, prepare to reiterate before yielding
if ($reiterate = empty($resources) && $this->nextToken) {
$this->command = clone $this->originalCommand;
}
} while ($reiterate);
return $resources;
}
protected function prepareRequest()
{
// Get the limit parameter key to set
$limitKey = $this->get('limit_key');
if ($limitKey && ($limit = $this->command->get($limitKey))) {
$pageSize = $this->calculatePageSize();
// If the limit of the command is different than the pageSize of the iterator, use the smaller value
if ($limit && $pageSize) {
$realLimit = min($limit, $pageSize);
$this->command->set($limitKey, $realLimit);
}
}
}
protected function handleResults(Model $result)
{
$results = array();
// Get the result key that contains the results
if ($resultKey = $this->get('result_key')) {
$results = $this->getValueFromResult($result, $resultKey) ?: array();
}
return $results;
}
protected function applyNextToken()
{
// Get the token parameter key to set
if ($tokenParam = $this->get('input_token')) {
// Set the next token. Works with multi-value tokens
if (is_array($tokenParam)) {
if (is_array($this->nextToken) && count($tokenParam) === count($this->nextToken)) {
foreach (array_combine($tokenParam, $this->nextToken) as $param => $token) {
$this->command->set($param, $token);
}
} else {
throw new RuntimeException('The definition of the iterator\'s token parameter and the actual token '
. 'value are not compatible.');
}
} else {
$this->command->set($tokenParam, $this->nextToken);
}
}
}
protected function determineNextToken(Model $result)
{
$this->nextToken = null;
// If the value of "more_results" is true or there is no "more_results" to check, then try to get the next token
$moreKey = $this->get('more_results');
if ($moreKey === null || $this->getValueFromResult($result, $moreKey)) {
// Get the token key to check
if ($tokenKey = $this->get('output_token')) {
// Get the next token's value. Works with multi-value tokens
if (is_array($tokenKey)) {
$this->nextToken = array();
foreach ($tokenKey as $key) {
$this->nextToken[] = $this->getValueFromResult($result, $key);
}
} else {
$this->nextToken = $this->getValueFromResult($result, $tokenKey);
}
}
}
}
/**
* Extracts the value from the result using Collection::getPath. Also adds some additional logic for keys that need
* to access n-1 indexes (e.g., ImportExport, Kinesis). The n-1 logic only works for the known cases. We will switch
* to a jmespath implementation in the future to cover all cases
*
* @param Model $result
* @param string $key
*
* @return mixed|null
*/
protected function getValueFromResult(Model $result, $key)
{
// Special handling for keys that need to access n-1 indexes
if (strpos($key, '#') !== false) {
$keyParts = explode('#', $key, 2);
$items = $result->getPath(trim($keyParts[0], '/'));
if ($items && is_array($items)) {
$index = count($items) - 1;
$key = strtr($key, array('#' => $index));
}
}
// Get the value
return $result->getPath($key);
}
}
@@ -0,0 +1,106 @@
<?php
namespace Aws\Common\Iterator;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Common\Collection;
use Guzzle\Service\Command\CommandInterface;
use Guzzle\Service\Resource\ResourceIteratorFactoryInterface;
/**
* Resource iterator factory used to instantiate the default AWS resource iterator with the correct configuration or
* use a concrete iterator class if one exists
*/
class AwsResourceIteratorFactory implements ResourceIteratorFactoryInterface
{
/**
* @var array Default configuration values for iterators
*/
protected static $defaultIteratorConfig = array(
'input_token' => null,
'output_token' => null,
'limit_key' => null,
'result_key' => null,
'more_results' => null,
);
/**
* @var array Legacy configuration options mapped to their new names
*/
private static $legacyConfigOptions = array(
'token_param' => 'input_token',
'token_key' => 'output_token',
'limit_param' => 'limit_key',
'more_key' => 'more_results',
);
/**
* @var array Iterator configuration for each iterable operation
*/
protected $config;
/**
* @var ResourceIteratorFactoryInterface Another factory that will be used first to instantiate the iterator
*/
protected $primaryIteratorFactory;
/**
* @param array $config An array of configuration values for the factory
* @param ResourceIteratorFactoryInterface $primaryIteratorFactory Another factory to use for chain of command
*/
public function __construct(array $config, ResourceIteratorFactoryInterface $primaryIteratorFactory = null)
{
$this->primaryIteratorFactory = $primaryIteratorFactory;
$this->config = array();
foreach ($config as $name => $operation) {
$this->config[$name] = $operation + self::$defaultIteratorConfig;
}
}
public function build(CommandInterface $command, array $options = array())
{
// Get the configuration data for the command
$commandName = $command->getName();
$commandSupported = isset($this->config[$commandName]);
$options = $this->translateLegacyConfigOptions($options);
$options += $commandSupported ? $this->config[$commandName] : array();
// Instantiate the iterator using the primary factory (if one was provided)
if ($this->primaryIteratorFactory && $this->primaryIteratorFactory->canBuild($command)) {
$iterator = $this->primaryIteratorFactory->build($command, $options);
} elseif (!$commandSupported) {
throw new InvalidArgumentException("Iterator was not found for {$commandName}.");
} else {
// Instantiate a generic AWS resource iterator
$iterator = new AwsResourceIterator($command, $options);
}
return $iterator;
}
public function canBuild(CommandInterface $command)
{
if ($this->primaryIteratorFactory) {
return $this->primaryIteratorFactory->canBuild($command);
} else {
return isset($this->config[$command->getName()]);
}
}
/**
* @param array $config The config for a single operation
*
* @return array The modified config with legacy options translated
*/
private function translateLegacyConfigOptions($config)
{
foreach (self::$legacyConfigOptions as $legacyOption => $newOption) {
if (isset($config[$legacyOption])) {
$config[$newOption] = $config[$legacyOption];
unset($config[$legacyOption]);
}
}
return $config;
}
}
@@ -0,0 +1,270 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\MultipartUploadException;
use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\AbstractHasDispatcher;
use Guzzle\Http\EntityBody;
use Guzzle\Http\EntityBodyInterface;
use Guzzle\Service\Command\OperationCommand;
use Guzzle\Service\Resource\Model;
/**
* Abstract class for transfer commonalities
*/
abstract class AbstractTransfer extends AbstractHasDispatcher implements TransferInterface
{
const BEFORE_UPLOAD = 'multipart_upload.before_upload';
const AFTER_UPLOAD = 'multipart_upload.after_upload';
const BEFORE_PART_UPLOAD = 'multipart_upload.before_part_upload';
const AFTER_PART_UPLOAD = 'multipart_upload.after_part_upload';
const AFTER_ABORT = 'multipart_upload.after_abort';
const AFTER_COMPLETE = 'multipart_upload.after_complete';
/**
* @var AwsClientInterface Client used for the transfers
*/
protected $client;
/**
* @var TransferStateInterface State of the transfer
*/
protected $state;
/**
* @var EntityBody Data source of the transfer
*/
protected $source;
/**
* @var array Associative array of options
*/
protected $options;
/**
* @var int Size of each part to upload
*/
protected $partSize;
/**
* @var bool Whether or not the transfer has been stopped
*/
protected $stopped = false;
/**
* Construct a new transfer object
*
* @param AwsClientInterface $client Client used for the transfers
* @param TransferStateInterface $state State used to track transfer
* @param EntityBody $source Data source of the transfer
* @param array $options Array of options to apply
*/
public function __construct(
AwsClientInterface $client,
TransferStateInterface $state,
EntityBody $source,
array $options = array()
) {
$this->client = $client;
$this->state = $state;
$this->source = $source;
$this->options = $options;
$this->init();
$this->partSize = $this->calculatePartSize();
}
public function __invoke()
{
return $this->upload();
}
/**
* {@inheritdoc}
*/
public static function getAllEvents()
{
return array(
self::BEFORE_PART_UPLOAD,
self::AFTER_UPLOAD,
self::BEFORE_PART_UPLOAD,
self::AFTER_PART_UPLOAD,
self::AFTER_ABORT,
self::AFTER_COMPLETE
);
}
/**
* {@inheritdoc}
*/
public function abort()
{
$command = $this->getAbortCommand();
$result = $command->getResult();
$this->state->setAborted(true);
$this->stop();
$this->dispatch(self::AFTER_ABORT, $this->getEventData($command));
return $result;
}
/**
* {@inheritdoc}
*/
public function stop()
{
$this->stopped = true;
return $this->state;
}
/**
* {@inheritdoc}
*/
public function getState()
{
return $this->state;
}
/**
* Get the array of options associated with the transfer
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Set an option on the transfer
*
* @param string $option Name of the option
* @param mixed $value Value to set
*
* @return self
*/
public function setOption($option, $value)
{
$this->options[$option] = $value;
return $this;
}
/**
* Get the source body of the upload
*
* @return EntityBodyInterface
*/
public function getSource()
{
return $this->source;
}
/**
* {@inheritdoc}
* @throws MultipartUploadException when an error is encountered. Use getLastException() to get more information.
* @throws RuntimeException when attempting to upload an aborted transfer
*/
public function upload()
{
if ($this->state->isAborted()) {
throw new RuntimeException('The transfer has been aborted and cannot be uploaded');
}
$this->stopped = false;
$eventData = $this->getEventData();
$this->dispatch(self::BEFORE_UPLOAD, $eventData);
try {
$this->transfer();
$this->dispatch(self::AFTER_UPLOAD, $eventData);
if ($this->stopped) {
return null;
} else {
$result = $this->complete();
$this->dispatch(self::AFTER_COMPLETE, $eventData);
}
} catch (\Exception $e) {
throw new MultipartUploadException($this->state, $e);
}
return $result;
}
/**
* Get an array used for event notifications
*
* @param OperationCommand $command Command to include in event data
*
* @return array
*/
protected function getEventData(OperationCommand $command = null)
{
$data = array(
'transfer' => $this,
'source' => $this->source,
'options' => $this->options,
'client' => $this->client,
'part_size' => $this->partSize,
'state' => $this->state
);
if ($command) {
$data['command'] = $command;
}
return $data;
}
/**
* Hook to initialize the transfer
*/
protected function init() {}
/**
* Determine the upload part size based on the size of the source data and
* taking into account the acceptable minimum and maximum part sizes.
*
* @return int The part size
*/
abstract protected function calculatePartSize();
/**
* Complete the multipart upload
*
* @return Model Returns the result of the complete multipart upload command
*/
abstract protected function complete();
/**
* Hook to implement in subclasses to perform the actual transfer
*/
abstract protected function transfer();
/**
* Fetches the abort command fom the concrete implementation
*
* @return OperationCommand
*/
abstract protected function getAbortCommand();
}
@@ -0,0 +1,164 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Aws\Common\Exception\RuntimeException;
/**
* State of a multipart upload
*/
abstract class AbstractTransferState implements TransferStateInterface
{
/**
* @var UploadIdInterface Object holding params used to identity the upload part
*/
protected $uploadId;
/**
* @var array Array of parts where the part number is the index
*/
protected $parts = array();
/**
* @var bool Whether or not the transfer was aborted
*/
protected $aborted = false;
/**
* Construct a new transfer state object
*
* @param UploadIdInterface $uploadId Upload identifier object
*/
public function __construct(UploadIdInterface $uploadId)
{
$this->uploadId = $uploadId;
}
/**
* {@inheritdoc}
*/
public function getUploadId()
{
return $this->uploadId;
}
/**
* Get a data value from the transfer state's uploadId
*
* @param string $key Key to retrieve (e.g. Bucket, Key, UploadId, etc)
*
* @return string|null
*/
public function getFromId($key)
{
$params = $this->uploadId->toParams();
return isset($params[$key]) ? $params[$key] : null;
}
/**
* {@inheritdoc}
*/
public function getPart($partNumber)
{
return isset($this->parts[$partNumber]) ? $this->parts[$partNumber] : null;
}
/**
* {@inheritdoc}
*/
public function addPart(UploadPartInterface $part)
{
$partNumber = $part->getPartNumber();
$this->parts[$partNumber] = $part;
return $this;
}
/**
* {@inheritdoc}
*/
public function hasPart($partNumber)
{
return isset($this->parts[$partNumber]);
}
/**
* {@inheritdoc}
*/
public function getPartNumbers()
{
return array_keys($this->parts);
}
/**
* {@inheritdoc}
*/
public function setAborted($aborted)
{
$this->aborted = (bool) $aborted;
return $this;
}
/**
* {@inheritdoc}
*/
public function isAborted()
{
return $this->aborted;
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->parts);
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new \ArrayIterator($this->parts);
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(get_object_vars($this));
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
foreach (get_object_vars($this) as $property => $oldValue) {
if (array_key_exists($property, $data)) {
$this->{$property} = $data[$property];
} else {
throw new RuntimeException("The {$property} property could be restored during unserialization.");
}
}
}
}
@@ -0,0 +1,148 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Http\EntityBody;
/**
* Easily create a multipart uploader used to quickly and reliably upload a
* large file or data stream to Amazon S3 using multipart uploads
*/
abstract class AbstractUploadBuilder
{
/**
* @var AwsClientInterface Client used to transfer requests
*/
protected $client;
/**
* @var TransferStateInterface State of the transfer
*/
protected $state;
/**
* @var EntityBody Source of the data
*/
protected $source;
/**
* @var array Array of headers to set on the object
*/
protected $headers = array();
/**
* Return a new instance of the UploadBuilder
*
* @return static
*/
public static function newInstance()
{
return new static;
}
/**
* Set the client used to connect to the AWS service
*
* @param AwsClientInterface $client Client to use
*
* @return $this
*/
public function setClient(AwsClientInterface $client)
{
$this->client = $client;
return $this;
}
/**
* Set the state of the upload. This is useful for resuming from a previously started multipart upload.
* You must use a local file stream as the data source if you wish to resume from a previous upload.
*
* @param TransferStateInterface|string $state Pass a TransferStateInterface object or the ID of the initiated
* multipart upload. When an ID is passed, the builder will create a
* state object using the data from a ListParts API response.
*
* @return $this
*/
public function resumeFrom($state)
{
$this->state = $state;
return $this;
}
/**
* Set the data source of the transfer
*
* @param resource|string|EntityBody $source Source of the transfer. Pass a string to transfer from a file on disk.
* You can also stream from a resource returned from fopen or a Guzzle
* {@see EntityBody} object.
*
* @return $this
* @throws InvalidArgumentException when the source cannot be found or opened
*/
public function setSource($source)
{
// Use the contents of a file as the data source
if (is_string($source)) {
if (!file_exists($source)) {
throw new InvalidArgumentException("File does not exist: {$source}");
}
// Clear the cache so that we send accurate file sizes
clearstatcache(true, $source);
$source = fopen($source, 'r');
}
$this->source = EntityBody::factory($source);
if ($this->source->isSeekable() && $this->source->getSize() == 0) {
throw new InvalidArgumentException('Empty body provided to upload builder');
}
return $this;
}
/**
* Specify the headers to set on the upload
*
* @param array $headers Headers to add to the uploaded object
*
* @return $this
*/
public function setHeaders(array $headers)
{
$this->headers = $headers;
return $this;
}
/**
* Build the appropriate uploader based on the builder options
*
* @return TransferInterface
*/
abstract public function build();
/**
* Initiate the multipart upload
*
* @return TransferStateInterface
*/
abstract protected function initiateMultipartUpload();
}
@@ -0,0 +1,89 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Aws\Common\Exception\InvalidArgumentException;
/**
* An object that encapsulates the data identifying an upload
*/
abstract class AbstractUploadId implements UploadIdInterface
{
/**
* @var array Expected values (with defaults)
*/
protected static $expectedValues = array();
/**
* @var array Params representing the identifying information
*/
protected $data = array();
/**
* {@inheritdoc}
*/
public static function fromParams($data)
{
$uploadId = new static();
$uploadId->loadData($data);
return $uploadId;
}
/**
* {@inheritdoc}
*/
public function toParams()
{
return $this->data;
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize($this->data);
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$this->loadData(unserialize($serialized));
}
/**
* Loads an array of data into the UploadId by extracting only the needed keys
*
* @param array $data Data to load
*
* @throws InvalidArgumentException if a required key is missing
*/
protected function loadData($data)
{
$data = array_replace(static::$expectedValues, array_intersect_key($data, static::$expectedValues));
foreach ($data as $key => $value) {
if (isset($data[$key])) {
$this->data[$key] = $data[$key];
} else {
throw new InvalidArgumentException("A required key [$key] was missing from the UploadId.");
}
}
}
}
@@ -0,0 +1,101 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Aws\Common\Exception\InvalidArgumentException;
/**
* An object that encapsulates the data for an upload part
*/
abstract class AbstractUploadPart implements UploadPartInterface
{
/**
* @var array A map of external array keys to internal property names
*/
protected static $keyMap = array();
/**
* @var int The number of the upload part representing its order in the overall upload
*/
protected $partNumber;
/**
* {@inheritdoc}
*/
public static function fromArray($data)
{
$part = new static();
$part->loadData($data);
return $part;
}
/**
* {@inheritdoc}
*/
public function getPartNumber()
{
return $this->partNumber;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
$array = array();
foreach (static::$keyMap as $key => $property) {
$array[$key] = $this->{$property};
}
return $array;
}
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize($this->toArray());
}
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$this->loadData(unserialize($serialized));
}
/**
* Loads an array of data into the upload part by extracting only the needed keys
*
* @param array|\Traversable $data Data to load into the upload part value object
*
* @throws InvalidArgumentException if a required key is missing
*/
protected function loadData($data)
{
foreach (static::$keyMap as $key => $property) {
if (isset($data[$key])) {
$this->{$property} = $data[$key];
} else {
throw new InvalidArgumentException("A required key [$key] was missing from the upload part.");
}
}
}
}
@@ -0,0 +1,66 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Guzzle\Common\HasDispatcherInterface;
use Guzzle\Service\Resource\Model;
/**
* Interface for transferring the contents of a data source to an AWS service via a multipart upload interface
*/
interface TransferInterface extends HasDispatcherInterface
{
/**
* Upload the source to using a multipart upload
*
* @return Model|null Result of the complete multipart upload command or null if uploading was stopped
*/
public function upload();
/**
* Abort the upload
*
* @return Model Returns the result of the abort multipart upload command
*/
public function abort();
/**
* Get the current state of the upload
*
* @return TransferStateInterface
*/
public function getState();
/**
* Stop the transfer and retrieve the current state.
*
* This allows you to stop and later resume a long running transfer if needed.
*
* @return TransferStateInterface
*/
public function stop();
/**
* Set an option on the transfer object
*
* @param string $option Option to set
* @param mixed $value The value to set
*
* @return self
*/
public function setOption($option, $value);
}
@@ -0,0 +1,92 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
use Aws\Common\Client\AwsClientInterface;
/**
* State of a multipart upload
*/
interface TransferStateInterface extends \Countable, \IteratorAggregate, \Serializable
{
/**
* Create the transfer state from the results of list parts request
*
* @param AwsClientInterface $client Client used to send the request
* @param UploadIdInterface $uploadId Params needed to identify the upload and form the request
*
* @return self
*/
public static function fromUploadId(AwsClientInterface $client, UploadIdInterface $uploadId);
/**
* Get the params used to identify an upload part
*
* @return UploadIdInterface
*/
public function getUploadId();
/**
* Get the part information of a specific part
*
* @param int $partNumber Part to retrieve
*
* @return UploadPartInterface
*/
public function getPart($partNumber);
/**
* Add a part to the transfer state
*
* @param UploadPartInterface $part The part to add
*
* @return self
*/
public function addPart(UploadPartInterface $part);
/**
* Check if a specific part has been uploaded
*
* @param int $partNumber Part to check
*
* @return bool
*/
public function hasPart($partNumber);
/**
* Get a list of all of the uploaded part numbers
*
* @return array
*/
public function getPartNumbers();
/**
* Set whether or not the transfer has been aborted
*
* @param bool $aborted Set to true to mark the transfer as aborted
*
* @return self
*/
public function setAborted($aborted);
/**
* Check if the transfer has been marked as aborted
*
* @return bool
*/
public function isAborted();
}
@@ -0,0 +1,39 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
/**
* An object that encapsulates the data identifying an upload
*/
interface UploadIdInterface extends \Serializable
{
/**
* Create an UploadId from an array
*
* @param array $data Data representing the upload identification
*
* @return self
*/
public static function fromParams($data);
/**
* Returns the array form of the upload identification for use as command params
*
* @return array
*/
public function toParams();
}
@@ -0,0 +1,46 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Model\MultipartUpload;
/**
* An object that encapsulates the data for an upload part
*/
interface UploadPartInterface extends \Serializable
{
/**
* Create an upload part from an array
*
* @param array|\Traversable $data Data representing the upload part
*
* @return self
*/
public static function fromArray($data);
/**
* Returns the part number of the upload part which is used as an identifier
*
* @return int
*/
public function getPartNumber();
/**
* Returns the array form of the upload part
*
* @return array
*/
public function toArray();
}
@@ -0,0 +1,362 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
return array(
'class' => 'Aws\Common\Aws',
'services' => array(
'default_settings' => array(
'params' => array()
),
'autoscaling' => array(
'alias' => 'AutoScaling',
'extends' => 'default_settings',
'class' => 'Aws\AutoScaling\AutoScalingClient'
),
'cloudformation' => array(
'alias' => 'CloudFormation',
'extends' => 'default_settings',
'class' => 'Aws\CloudFormation\CloudFormationClient'
),
'cloudfront' => array(
'alias' => 'CloudFront',
'extends' => 'default_settings',
'class' => 'Aws\CloudFront\CloudFrontClient'
),
'cloudfront_20120505' => array(
'extends' => 'cloudfront',
'params' => array(
'version' => '2012-05-05'
)
),
'cloudhsm' => array(
'alias' => 'CloudHsm',
'extends' => 'default_settings',
'class' => 'Aws\CloudHsm\CloudHsmClient'
),
'cloudsearch' => array(
'alias' => 'CloudSearch',
'extends' => 'default_settings',
'class' => 'Aws\CloudSearch\CloudSearchClient'
),
'cloudsearch_20110201' => array(
'extends' => 'cloudsearch',
'params' => array(
'version' => '2011-02-01'
)
),
'cloudsearchdomain' => array(
'alias' => 'CloudSearchDomain',
'extends' => 'default_settings',
'class' => 'Aws\CloudSearchDomain\CloudSearchDomainClient'
),
'cloudtrail' => array(
'alias' => 'CloudTrail',
'extends' => 'default_settings',
'class' => 'Aws\CloudTrail\CloudTrailClient'
),
'cloudwatch' => array(
'alias' => 'CloudWatch',
'extends' => 'default_settings',
'class' => 'Aws\CloudWatch\CloudWatchClient'
),
'cloudwatchlogs' => array(
'alias' => 'CloudWatchLogs',
'extends' => 'default_settings',
'class' => 'Aws\CloudWatchLogs\CloudWatchLogsClient'
),
'cognito-identity' => array(
'alias' => 'CognitoIdentity',
'extends' => 'default_settings',
'class' => 'Aws\CognitoIdentity\CognitoIdentityClient'
),
'cognitoidentity' => array('extends' => 'cognito-identity'),
'cognito-sync' => array(
'alias' => 'CognitoSync',
'extends' => 'default_settings',
'class' => 'Aws\CognitoSync\CognitoSyncClient'
),
'cognitosync' => array('extends' => 'cognito-sync'),
'codecommit' => array(
'alias' => 'CodeCommit',
'extends' => 'default_settings',
'class' => 'Aws\CodeCommit\CodeCommitClient'
),
'codedeploy' => array(
'alias' => 'CodeDeploy',
'extends' => 'default_settings',
'class' => 'Aws\CodeDeploy\CodeDeployClient'
),
'codepipeline' => array(
'alias' => 'CodePipeline',
'extends' => 'default_settings',
'class' => 'Aws\CodePipeline\CodePipelineClient'
),
'config' => array(
'alias' => 'ConfigService',
'extends' => 'default_settings',
'class' => 'Aws\ConfigService\ConfigServiceClient'
),
'datapipeline' => array(
'alias' => 'DataPipeline',
'extends' => 'default_settings',
'class' => 'Aws\DataPipeline\DataPipelineClient'
),
'devicefarm' => array(
'alias' => 'DeviceFarm',
'extends' => 'default_settings',
'class' => 'Aws\DeviceFarm\DeviceFarmClient'
),
'directconnect' => array(
'alias' => 'DirectConnect',
'extends' => 'default_settings',
'class' => 'Aws\DirectConnect\DirectConnectClient'
),
'ds' => array(
'alias' => 'DirectoryService',
'extends' => 'default_settings',
'class' => 'Aws\DirectoryService\DirectoryServiceClient'
),
'dynamodb' => array(
'alias' => 'DynamoDb',
'extends' => 'default_settings',
'class' => 'Aws\DynamoDb\DynamoDbClient'
),
'dynamodb_20111205' => array(
'extends' => 'dynamodb',
'params' => array(
'version' => '2011-12-05'
)
),
'dynamodbstreams' => array(
'alias' => 'DynamoDbStreams',
'extends' => 'default_settings',
'class' => 'Aws\DynamoDbStreams\DynamoDbStreamsClient'
),
'ec2' => array(
'alias' => 'Ec2',
'extends' => 'default_settings',
'class' => 'Aws\Ec2\Ec2Client'
),
'ecs' => array(
'alias' => 'Ecs',
'extends' => 'default_settings',
'class' => 'Aws\Ecs\EcsClient'
),
'elasticache' => array(
'alias' => 'ElastiCache',
'extends' => 'default_settings',
'class' => 'Aws\ElastiCache\ElastiCacheClient'
),
'elasticbeanstalk' => array(
'alias' => 'ElasticBeanstalk',
'extends' => 'default_settings',
'class' => 'Aws\ElasticBeanstalk\ElasticBeanstalkClient'
),
'efs' => array(
'alias' => 'Efs',
'extends' => 'default_settings',
'class' => 'Aws\Efs\EfsClient'
),
'elasticloadbalancing' => array(
'alias' => 'ElasticLoadBalancing',
'extends' => 'default_settings',
'class' => 'Aws\ElasticLoadBalancing\ElasticLoadBalancingClient'
),
'elastictranscoder' => array(
'alias' => 'ElasticTranscoder',
'extends' => 'default_settings',
'class' => 'Aws\ElasticTranscoder\ElasticTranscoderClient'
),
'emr' => array(
'alias' => 'Emr',
'extends' => 'default_settings',
'class' => 'Aws\Emr\EmrClient'
),
'glacier' => array(
'alias' => 'Glacier',
'extends' => 'default_settings',
'class' => 'Aws\Glacier\GlacierClient'
),
'kinesis' => array(
'alias' => 'Kinesis',
'extends' => 'default_settings',
'class' => 'Aws\Kinesis\KinesisClient'
),
'kms' => array(
'alias' => 'Kms',
'extends' => 'default_settings',
'class' => 'Aws\Kms\KmsClient'
),
'lambda' => array(
'alias' => 'Lambda',
'extends' => 'default_settings',
'class' => 'Aws\Lambda\LambdaClient'
),
'iam' => array(
'alias' => 'Iam',
'extends' => 'default_settings',
'class' => 'Aws\Iam\IamClient'
),
'importexport' => array(
'alias' => 'ImportExport',
'extends' => 'default_settings',
'class' => 'Aws\ImportExport\ImportExportClient'
),
'machinelearning' => array(
'alias' => 'MachineLearning',
'extends' => 'default_settings',
'class' => 'Aws\MachineLearning\MachineLearningClient'
),
'opsworks' => array(
'alias' => 'OpsWorks',
'extends' => 'default_settings',
'class' => 'Aws\OpsWorks\OpsWorksClient'
),
'rds' => array(
'alias' => 'Rds',
'extends' => 'default_settings',
'class' => 'Aws\Rds\RdsClient'
),
'redshift' => array(
'alias' => 'Redshift',
'extends' => 'default_settings',
'class' => 'Aws\Redshift\RedshiftClient'
),
'route53' => array(
'alias' => 'Route53',
'extends' => 'default_settings',
'class' => 'Aws\Route53\Route53Client'
),
'route53domains' => array(
'alias' => 'Route53Domains',
'extends' => 'default_settings',
'class' => 'Aws\Route53Domains\Route53DomainsClient'
),
's3' => array(
'alias' => 'S3',
'extends' => 'default_settings',
'class' => 'Aws\S3\S3Client'
),
'sdb' => array(
'alias' => 'SimpleDb',
'extends' => 'default_settings',
'class' => 'Aws\SimpleDb\SimpleDbClient'
),
'ses' => array(
'alias' => 'Ses',
'extends' => 'default_settings',
'class' => 'Aws\Ses\SesClient'
),
'sns' => array(
'alias' => 'Sns',
'extends' => 'default_settings',
'class' => 'Aws\Sns\SnsClient'
),
'sqs' => array(
'alias' => 'Sqs',
'extends' => 'default_settings',
'class' => 'Aws\Sqs\SqsClient'
),
'ssm' => array(
'alias' => 'Ssm',
'extends' => 'default_settings',
'class' => 'Aws\Ssm\SsmClient'
),
'storagegateway' => array(
'alias' => 'StorageGateway',
'extends' => 'default_settings',
'class' => 'Aws\StorageGateway\StorageGatewayClient'
),
'sts' => array(
'alias' => 'Sts',
'extends' => 'default_settings',
'class' => 'Aws\Sts\StsClient'
),
'support' => array(
'alias' => 'Support',
'extends' => 'default_settings',
'class' => 'Aws\Support\SupportClient'
),
'swf' => array(
'alias' => 'Swf',
'extends' => 'default_settings',
'class' => 'Aws\Swf\SwfClient'
),
'workspaces' => array(
'alias' => 'WorkSpaces',
'extends' => 'default_settings',
'class' => 'Aws\WorkSpaces\WorkSpacesClient'
),
)
);
@@ -0,0 +1,79 @@
<?php
return array(
'version' => 2,
'endpoints' => array(
'*/*' => array(
'endpoint' => '{service}.{region}.amazonaws.com'
),
'cn-north-1/*' => array(
'endpoint' => '{service}.{region}.amazonaws.com.cn',
'signatureVersion' => 'v4'
),
'us-gov-west-1/iam' => array(
'endpoint' => 'iam.us-gov.amazonaws.com'
),
'us-gov-west-1/sts' => array(
'endpoint' => 'sts.us-gov-west-1.amazonaws.com'
),
'us-gov-west-1/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'*/cloudfront' => array(
'endpoint' => 'cloudfront.amazonaws.com',
'credentialScope' => array(
'region' => 'us-east-1'
)
),
'*/iam' => array(
'endpoint' => 'iam.amazonaws.com',
'credentialScope' => array(
'region' => 'us-east-1'
)
),
'*/importexport' => array(
'endpoint' => 'importexport.amazonaws.com',
'credentialScope' => array(
'region' => 'us-east-1'
)
),
'*/route53' => array(
'endpoint' => 'route53.amazonaws.com',
'credentialScope' => array(
'region' => 'us-east-1'
)
),
'*/sts' => array(
'endpoint' => 'sts.amazonaws.com',
'credentialScope' => array(
'region' => 'us-east-1'
)
),
'us-east-1/sdb' => array(
'endpoint' => 'sdb.amazonaws.com'
),
'us-east-1/s3' => array(
'endpoint' => 's3.amazonaws.com'
),
'us-west-1/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'us-west-2/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'eu-west-1/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'ap-southeast-1/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'ap-southeast-2/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'ap-northeast-1/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
),
'sa-east-1/s3' => array(
'endpoint' => 's3-{region}.amazonaws.com'
)
)
);
@@ -0,0 +1,138 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
return array(
'includes' => array('_aws'),
'services' => array(
'sdk1_settings' => array(
'extends' => 'default_settings',
'params' => array(
'certificate_authority' => false
)
),
'v1.autoscaling' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonAS'
),
'v1.cloudformation' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonCloudFormation'
),
'v1.cloudfront' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonCloudFront'
),
'v1.cloudsearch' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonCloudSearch'
),
'v1.cloudwatch' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonCloudWatch'
),
'v1.dynamodb' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonDynamoDB'
),
'v1.ec2' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonEC2'
),
'v1.elasticache' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonElastiCache'
),
'v1.elasticbeanstalk' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonElasticBeanstalk'
),
'v1.elb' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonELB'
),
'v1.emr' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonEMR'
),
'v1.iam' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonIAM'
),
'v1.importexport' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonImportExport'
),
'v1.rds' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonRDS'
),
'v1.s3' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonS3'
),
'v1.sdb' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonSDB'
),
'v1.ses' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonSES'
),
'v1.sns' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonSNS'
),
'v1.sqs' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonSQS'
),
'v1.storagegateway' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonStorageGateway'
),
'v1.sts' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonSTS'
),
'v1.swf' => array(
'extends' => 'sdk1_settings',
'class' => 'AmazonSWF'
)
)
);
@@ -0,0 +1,67 @@
<?php
namespace Aws\Common;
/**
* Provides endpoints based on a rules configuration file.
*/
class RulesEndpointProvider
{
/** @var array */
private $patterns;
/**
* @param array $patterns Hash of endpoint patterns mapping to endpoint
* configurations.
*/
public function __construct(array $patterns)
{
$this->patterns = $patterns;
}
/**
* Creates and returns the default RulesEndpointProvider based on the
* public rule sets.
*
* @return self
*/
public static function fromDefaults()
{
return new self(require __DIR__ . '/Resources/public-endpoints.php');
}
public function __invoke(array $args = array())
{
if (!isset($args['service'])) {
throw new \InvalidArgumentException('Requires a "service" value');
}
if (!isset($args['region'])) {
throw new \InvalidArgumentException('Requires a "region" value');
}
foreach ($this->getKeys($args['region'], $args['service']) as $key) {
if (isset($this->patterns['endpoints'][$key])) {
return $this->expand($this->patterns['endpoints'][$key], $args);
}
}
throw new \RuntimeException('Could not resolve endpoint');
}
private function expand(array $config, array $args)
{
$scheme = isset($args['scheme']) ? $args['scheme'] : 'https';
$config['endpoint'] = $scheme . '://' . str_replace(
array('{service}', '{region}'),
array($args['service'], $args['region']),
$config['endpoint']
);
return $config;
}
private function getKeys($region, $service)
{
return array("$region/$service", "$region/*", "*/$service", "*/*");
}
}
@@ -0,0 +1,44 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;
abstract class AbstractSignature implements SignatureInterface
{
/**
* Provides the timestamp used for the class (used for mocking PHP's time() function)
*
* @return int
*/
protected function getTimestamp()
{
return time();
}
/**
* @codeCoverageIgnore
*/
public function createPresignedUrl(
RequestInterface $request,
CredentialsInterface $credentials,
$expires
) {
throw new \BadMethodCallException(__METHOD__ . ' not implemented');
}
}
@@ -0,0 +1,42 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
/**
* Interface for signatures that use specific region and service names when
* signing requests.
*/
interface EndpointSignatureInterface extends SignatureInterface
{
/**
* Set the service name instead of inferring it from a request URL
*
* @param string $service Name of the service used when signing
*
* @return self
*/
public function setServiceName($service);
/**
* Set the region name instead of inferring it from a request URL
*
* @param string $region Name of the region used when signing
*
* @return self
*/
public function setRegionName($region);
}
@@ -0,0 +1,52 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;
/**
* Interface used to provide interchangeable strategies for signing requests
* using the various AWS signature protocols.
*/
interface SignatureInterface
{
/**
* Signs the specified request with an AWS signing protocol by using the
* provided AWS account credentials and adding the required headers to the
* request.
*
* @param RequestInterface $request Request to add a signature to
* @param CredentialsInterface $credentials Signing credentials
*/
public function signRequest(RequestInterface $request, CredentialsInterface $credentials);
/**
* Create a pre-signed URL
*
* @param RequestInterface $request Request to sign
* @param CredentialsInterface $credentials Credentials used to sign
* @param int|string|\DateTime $expires The time at which the URL should expire. This can be a Unix timestamp, a
* PHP DateTime object, or a string that can be evaluated by strtotime
* @return string
*/
public function createPresignedUrl(
RequestInterface $request,
CredentialsInterface $credentials,
$expires
);
}
@@ -0,0 +1,88 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
use Aws\Common\Credentials\AbstractRefreshableCredentials;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Credentials\NullCredentials;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listener used to sign requests before they are sent over the wire
*/
class SignatureListener implements EventSubscriberInterface
{
/**
* @var CredentialsInterface
*/
protected $credentials;
/**
* @var SignatureInterface
*/
protected $signature;
/**
* Construct a new request signing plugin
*
* @param CredentialsInterface $credentials Credentials used to sign requests
* @param SignatureInterface $signature Signature implementation
*/
public function __construct(CredentialsInterface $credentials, SignatureInterface $signature)
{
$this->credentials = $credentials;
$this->signature = $signature;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array(
'request.before_send' => array('onRequestBeforeSend', -255),
'client.credentials_changed' => array('onCredentialsChanged')
);
}
/**
* Updates the listener with new credentials if the client is updated
*
* @param Event $event Event emitted
*/
public function onCredentialsChanged(Event $event)
{
$this->credentials = $event['credentials'];
}
/**
* Signs requests before they are sent
*
* @param Event $event Event emitted
*/
public function onRequestBeforeSend(Event $event)
{
$creds = $this->credentials instanceof AbstractRefreshableCredentials
? $this->credentials->getCredentials()
: $this->credentials;
if(!$creds instanceof NullCredentials) {
$this->signature->signRequest($event['request'], $creds);
}
}
}
@@ -0,0 +1,109 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;
/**
* Implementation of Signature Version 2
* @link http://aws.amazon.com/articles/1928
*/
class SignatureV2 extends AbstractSignature
{
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
{
// refresh the cached timestamp
$timestamp = $this->getTimestamp(true);
// set values we need in CanonicalizedParameterString
$this->addParameter($request, 'Timestamp', gmdate('c', $timestamp));
$this->addParameter($request, 'SignatureVersion', '2');
$this->addParameter($request, 'SignatureMethod', 'HmacSHA256');
$this->addParameter($request, 'AWSAccessKeyId', $credentials->getAccessKeyId());
if ($token = $credentials->getSecurityToken()) {
$this->addParameter($request, 'SecurityToken', $token);
}
// Get the path and ensure it's absolute
$path = '/' . ltrim($request->getUrl(true)->normalizePath()->getPath(), '/');
// build string to sign
$sign = $request->getMethod() . "\n"
. $request->getHost() . "\n"
. $path . "\n"
. $this->getCanonicalizedParameterString($request);
// Add the string to sign to the request for debugging purposes
$request->getParams()->set('aws.string_to_sign', $sign);
$signature = base64_encode(
hash_hmac(
'sha256',
$sign,
$credentials->getSecretKey(),
true
)
);
$this->addParameter($request, 'Signature', $signature);
}
/**
* Add a parameter key and value to the request according to type
*
* @param RequestInterface $request The request
* @param string $key The name of the parameter
* @param string $value The value of the parameter
*/
public function addParameter(RequestInterface $request, $key, $value)
{
if ($request->getMethod() == 'POST') {
$request->setPostField($key, $value);
} else {
$request->getQuery()->set($key, $value);
}
}
/**
* Get the canonicalized query/parameter string for a request
*
* @param RequestInterface $request Request used to build canonicalized string
*
* @return string
*/
private function getCanonicalizedParameterString(RequestInterface $request)
{
if ($request->getMethod() == 'POST') {
$params = $request->getPostFields()->toArray();
} else {
$params = $request->getQuery()->toArray();
}
// Don't resign a previous signature value
unset($params['Signature']);
uksort($params, 'strcmp');
$str = '';
foreach ($params as $key => $val) {
$str .= rawurlencode($key) . '=' . rawurlencode($val) . '&';
}
return substr($str, 0, -1);
}
}
@@ -0,0 +1,52 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Enum\DateFormat;
use Guzzle\Http\Message\RequestInterface;
/**
* Implementation of Signature Version 3 HTTPS
* @link http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/RESTAuthentication.html
*/
class SignatureV3Https extends AbstractSignature
{
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
{
// Add a date header if one is not set
if (!$request->hasHeader('date') && !$request->hasHeader('x-amz-date')) {
$request->setHeader('Date', gmdate(DateFormat::RFC1123, $this->getTimestamp()));
}
// Add the security token if one is present
if ($credentials->getSecurityToken()) {
$request->setHeader('x-amz-security-token', $credentials->getSecurityToken());
}
// Determine the string to sign
$stringToSign = (string) ($request->getHeader('Date') ?: $request->getHeader('x-amz-date'));
$request->getParams()->set('aws.string_to_sign', $stringToSign);
// Calculate the signature
$signature = base64_encode(hash_hmac('sha256', $stringToSign, $credentials->getSecretKey(), true));
// Add the authorization header to the request
$headerFormat = 'AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s';
$request->setHeader('X-Amzn-Authorization', sprintf($headerFormat, $credentials->getAccessKeyId(), $signature));
}
}
@@ -0,0 +1,477 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Signature;
use Aws\Common\Credentials\CredentialsInterface;
use Aws\Common\Enum\DateFormat;
use Aws\Common\HostNameUtils;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\RequestFactory;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\QueryString;
use Guzzle\Http\Url;
use Guzzle\Stream\Stream;
/**
* Signature Version 4
* @link http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
*/
class SignatureV4 extends AbstractSignature implements EndpointSignatureInterface
{
/** @var string Cache of the default empty entity-body payload */
const DEFAULT_PAYLOAD = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
/** @var string Explicitly set service name */
protected $serviceName;
/** @var string Explicitly set region name */
protected $regionName;
/** @var int Maximum number of hashes to cache */
protected $maxCacheSize = 50;
/** @var array Cache of previously signed values */
protected $hashCache = array();
/** @var int Size of the hash cache */
protected $cacheSize = 0;
/**
* @param string $serviceName Bind the signing to a particular service name
* @param string $regionName Bind the signing to a particular region name
*/
public function __construct($serviceName = null, $regionName = null)
{
$this->serviceName = $serviceName;
$this->regionName = $regionName;
}
/**
* Set the service name instead of inferring it from a request URL
*
* @param string $service Name of the service used when signing
*
* @return self
*/
public function setServiceName($service)
{
$this->serviceName = $service;
return $this;
}
/**
* Set the region name instead of inferring it from a request URL
*
* @param string $region Name of the region used when signing
*
* @return self
*/
public function setRegionName($region)
{
$this->regionName = $region;
return $this;
}
/**
* Set the maximum number of computed hashes to cache
*
* @param int $maxCacheSize Maximum number of hashes to cache
*
* @return self
*/
public function setMaxCacheSize($maxCacheSize)
{
$this->maxCacheSize = $maxCacheSize;
return $this;
}
public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
{
$timestamp = $this->getTimestamp();
$longDate = gmdate(DateFormat::ISO8601, $timestamp);
$shortDate = substr($longDate, 0, 8);
// Remove any previously set Authorization headers so that retries work
$request->removeHeader('Authorization');
// Requires a x-amz-date header or Date
if ($request->hasHeader('x-amz-date') || !$request->hasHeader('Date')) {
$request->setHeader('x-amz-date', $longDate);
} else {
$request->setHeader('Date', gmdate(DateFormat::RFC1123, $timestamp));
}
// Add the security token if one is present
if ($credentials->getSecurityToken()) {
$request->setHeader('x-amz-security-token', $credentials->getSecurityToken());
}
// Parse the service and region or use one that is explicitly set
$region = $this->regionName;
$service = $this->serviceName;
if (!$region || !$service) {
$url = Url::factory($request->getUrl());
$region = $region ?: HostNameUtils::parseRegionName($url);
$service = $service ?: HostNameUtils::parseServiceName($url);
}
$credentialScope = $this->createScope($shortDate, $region, $service);
$payload = $this->getPayload($request);
$signingContext = $this->createSigningContext($request, $payload);
$signingContext['string_to_sign'] = $this->createStringToSign(
$longDate,
$credentialScope,
$signingContext['canonical_request']
);
// Calculate the signing key using a series of derived keys
$signingKey = $this->getSigningKey($shortDate, $region, $service, $credentials->getSecretKey());
$signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey);
$request->setHeader('Authorization', "AWS4-HMAC-SHA256 "
. "Credential={$credentials->getAccessKeyId()}/{$credentialScope}, "
. "SignedHeaders={$signingContext['signed_headers']}, Signature={$signature}");
// Add debug information to the request
$request->getParams()->set('aws.signature', $signingContext);
}
public function createPresignedUrl(
RequestInterface $request,
CredentialsInterface $credentials,
$expires
) {
$request = $this->createPresignedRequest($request, $credentials);
$query = $request->getQuery();
$httpDate = gmdate(DateFormat::ISO8601, $this->getTimestamp());
$shortDate = substr($httpDate, 0, 8);
$scope = $this->createScope(
$shortDate,
$this->regionName,
$this->serviceName
);
$this->addQueryValues($scope, $request, $credentials, $expires);
$payload = $this->getPresignedPayload($request);
$context = $this->createSigningContext($request, $payload);
$stringToSign = $this->createStringToSign(
$httpDate,
$scope,
$context['canonical_request']
);
$key = $this->getSigningKey(
$shortDate,
$this->regionName,
$this->serviceName,
$credentials->getSecretKey()
);
$query['X-Amz-Signature'] = hash_hmac('sha256', $stringToSign, $key);
return $request->getUrl();
}
/**
* Converts a POST request to a GET request by moving POST fields into the
* query string.
*
* Useful for pre-signing query protocol requests.
*
* @param EntityEnclosingRequestInterface $request Request to clone
*
* @return RequestInterface
* @throws \InvalidArgumentException if the method is not POST
*/
public static function convertPostToGet(EntityEnclosingRequestInterface $request)
{
if ($request->getMethod() !== 'POST') {
throw new \InvalidArgumentException('Expected a POST request but '
. 'received a ' . $request->getMethod() . ' request.');
}
$cloned = RequestFactory::getInstance()
->cloneRequestWithMethod($request, 'GET');
// Move POST fields to the query if they are present
foreach ($request->getPostFields() as $name => $value) {
$cloned->getQuery()->set($name, $value);
}
return $cloned;
}
/**
* Get the payload part of a signature from a request.
*
* @param RequestInterface $request
*
* @return string
*/
protected function getPayload(RequestInterface $request)
{
// Calculate the request signature payload
if ($request->hasHeader('x-amz-content-sha256')) {
// Handle streaming operations (e.g. Glacier.UploadArchive)
return (string) $request->getHeader('x-amz-content-sha256');
}
if ($request instanceof EntityEnclosingRequestInterface) {
if ($request->getMethod() == 'POST' && count($request->getPostFields())) {
return hash('sha256', (string) $request->getPostFields());
} elseif ($body = $request->getBody()) {
return Stream::getHash($request->getBody(), 'sha256');
}
}
return self::DEFAULT_PAYLOAD;
}
/**
* Get the payload of a request for use with pre-signed URLs.
*
* @param RequestInterface $request
*
* @return string
*/
protected function getPresignedPayload(RequestInterface $request)
{
return $this->getPayload($request);
}
protected function createCanonicalizedPath(RequestInterface $request)
{
$doubleEncoded = rawurlencode(ltrim($request->getPath(), '/'));
return '/' . str_replace('%2F', '/', $doubleEncoded);
}
private function createStringToSign($longDate, $credentialScope, $creq)
{
return "AWS4-HMAC-SHA256\n{$longDate}\n{$credentialScope}\n"
. hash('sha256', $creq);
}
private function createPresignedRequest(
RequestInterface $request,
CredentialsInterface $credentials
) {
// POST requests can be sent as GET requests instead by moving the
// POST fields into the query string.
if ($request instanceof EntityEnclosingRequestInterface
&& $request->getMethod() === 'POST'
&& strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === 0
) {
$sr = RequestFactory::getInstance()
->cloneRequestWithMethod($request, 'GET');
// Move POST fields to the query if they are present
foreach ($request->getPostFields() as $name => $value) {
$sr->getQuery()->set($name, $value);
}
} else {
$sr = clone $request;
}
// Make sure to handle temporary credentials
if ($token = $credentials->getSecurityToken()) {
$sr->setHeader('X-Amz-Security-Token', $token);
$sr->getQuery()->set('X-Amz-Security-Token', $token);
}
$this->moveHeadersToQuery($sr);
return $sr;
}
/**
* Create the canonical representation of a request
*
* @param RequestInterface $request Request to canonicalize
* @param string $payload Request payload (typically the value
* of the x-amz-content-sha256 header.
*
* @return array Returns an array of context information including:
* - canonical_request
* - signed_headers
*/
private function createSigningContext(RequestInterface $request, $payload)
{
$signable = array(
'host' => true,
'date' => true,
'content-md5' => true
);
// Normalize the path as required by SigV4 and ensure it's absolute
$canon = $request->getMethod() . "\n"
. $this->createCanonicalizedPath($request) . "\n"
. $this->getCanonicalizedQueryString($request) . "\n";
$canonHeaders = array();
foreach ($request->getHeaders()->getAll() as $key => $values) {
$key = strtolower($key);
if (isset($signable[$key]) || substr($key, 0, 6) === 'x-amz-') {
$values = $values->toArray();
if (count($values) == 1) {
$values = $values[0];
} else {
sort($values);
$values = implode(',', $values);
}
$canonHeaders[$key] = $key . ':' . preg_replace('/\s+/', ' ', $values);
}
}
ksort($canonHeaders);
$signedHeadersString = implode(';', array_keys($canonHeaders));
$canon .= implode("\n", $canonHeaders) . "\n\n"
. $signedHeadersString . "\n"
. $payload;
return array(
'canonical_request' => $canon,
'signed_headers' => $signedHeadersString
);
}
/**
* Get a hash for a specific key and value. If the hash was previously
* cached, return it
*
* @param string $shortDate Short date
* @param string $region Region name
* @param string $service Service name
* @param string $secretKey Secret Access Key
*
* @return string
*/
private function getSigningKey($shortDate, $region, $service, $secretKey)
{
$cacheKey = $shortDate . '_' . $region . '_' . $service . '_' . $secretKey;
// Retrieve the hash form the cache or create it and add it to the cache
if (!isset($this->hashCache[$cacheKey])) {
// When the cache size reaches the max, then just clear the cache
if (++$this->cacheSize > $this->maxCacheSize) {
$this->hashCache = array();
$this->cacheSize = 0;
}
$dateKey = hash_hmac('sha256', $shortDate, 'AWS4' . $secretKey, true);
$regionKey = hash_hmac('sha256', $region, $dateKey, true);
$serviceKey = hash_hmac('sha256', $service, $regionKey, true);
$this->hashCache[$cacheKey] = hash_hmac('sha256', 'aws4_request', $serviceKey, true);
}
return $this->hashCache[$cacheKey];
}
/**
* Get the canonicalized query string for a request
*
* @param RequestInterface $request
* @return string
*/
private function getCanonicalizedQueryString(RequestInterface $request)
{
$queryParams = $request->getQuery()->getAll();
unset($queryParams['X-Amz-Signature']);
if (empty($queryParams)) {
return '';
}
$qs = '';
ksort($queryParams);
foreach ($queryParams as $key => $values) {
if (is_array($values)) {
sort($values);
} elseif ($values === 0) {
$values = array('0');
} elseif (!$values) {
$values = array('');
}
foreach ((array) $values as $value) {
if ($value === QueryString::BLANK) {
$value = '';
}
$qs .= rawurlencode($key) . '=' . rawurlencode($value) . '&';
}
}
return substr($qs, 0, -1);
}
private function convertExpires($expires)
{
if ($expires instanceof \DateTime) {
$expires = $expires->getTimestamp();
} elseif (!is_numeric($expires)) {
$expires = strtotime($expires);
}
$duration = $expires - time();
// Ensure that the duration of the signature is not longer than a week
if ($duration > 604800) {
throw new \InvalidArgumentException('The expiration date of a '
. 'signature version 4 presigned URL must be less than one '
. 'week');
}
return $duration;
}
private function createScope($shortDate, $region, $service)
{
return $shortDate
. '/' . $region
. '/' . $service
. '/aws4_request';
}
private function addQueryValues(
$scope,
RequestInterface $request,
CredentialsInterface $credentials,
$expires
) {
$credential = $credentials->getAccessKeyId() . '/' . $scope;
// Set query params required for pre-signed URLs
$request->getQuery()
->set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256')
->set('X-Amz-Credential', $credential)
->set('X-Amz-Date', gmdate('Ymd\THis\Z', $this->getTimestamp()))
->set('X-Amz-SignedHeaders', 'Host')
->set('X-Amz-Expires', $this->convertExpires($expires));
}
private function moveHeadersToQuery(RequestInterface $request)
{
$query = $request->getQuery();
foreach ($request->getHeaders() as $name => $header) {
if (substr($name, 0, 5) == 'x-amz') {
$query[$header->getName()] = (string) $header;
}
if ($name !== 'host') {
$request->removeHeader($name);
}
}
}
}
@@ -0,0 +1,53 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Client\AwsClientInterface;
use Aws\Common\Exception\RuntimeException;
/**
* Abstract waiter implementation used to wait on resources
*/
abstract class AbstractResourceWaiter extends AbstractWaiter implements ResourceWaiterInterface
{
/**
* @var AwsClientInterface
*/
protected $client;
/**
* {@inheritdoc}
*/
public function setClient(AwsClientInterface $client)
{
$this->client = $client;
return $this;
}
/**
* {@inheritdoc}
*/
public function wait()
{
if (!$this->client) {
throw new RuntimeException('No client has been specified on the waiter');
}
parent::wait();
}
}
@@ -0,0 +1,146 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Exception\RuntimeException;
use Guzzle\Common\AbstractHasDispatcher;
/**
* Abstract wait implementation
*/
abstract class AbstractWaiter extends AbstractHasDispatcher implements WaiterInterface
{
protected $attempts = 0;
protected $config = array();
/**
* {@inheritdoc}
*/
public static function getAllEvents()
{
return array(
// About to check if the waiter needs to wait
'waiter.before_attempt',
// About to sleep
'waiter.before_wait',
);
}
/**
* The max attempts allowed by the waiter
*
* @return int
*/
public function getMaxAttempts()
{
return isset($this->config[self::MAX_ATTEMPTS]) ? $this->config[self::MAX_ATTEMPTS] : 10;
}
/**
* Get the amount of time in seconds to delay between attempts
*
* @return int
*/
public function getInterval()
{
return isset($this->config[self::INTERVAL]) ? $this->config[self::INTERVAL] : 0;
}
/**
* {@inheritdoc}
*/
public function setMaxAttempts($maxAttempts)
{
$this->config[self::MAX_ATTEMPTS] = $maxAttempts;
return $this;
}
/**
* {@inheritdoc}
*/
public function setInterval($interval)
{
$this->config[self::INTERVAL] = $interval;
return $this;
}
/**
* Set config options associated with the waiter
*
* @param array $config Options to set
*
* @return self
*/
public function setConfig(array $config)
{
if (isset($config['waiter.before_attempt'])) {
$this->getEventDispatcher()->addListener('waiter.before_attempt', $config['waiter.before_attempt']);
unset($config['waiter.before_attempt']);
}
if (isset($config['waiter.before_wait'])) {
$this->getEventDispatcher()->addListener('waiter.before_wait', $config['waiter.before_wait']);
unset($config['waiter.before_wait']);
}
$this->config = $config;
return $this;
}
/**
* {@inheritdoc}
*/
public function wait()
{
$this->attempts = 0;
do {
$this->dispatch('waiter.before_attempt', array(
'waiter' => $this,
'config' => $this->config,
));
if ($this->doWait()) {
break;
}
if (++$this->attempts >= $this->getMaxAttempts()) {
throw new RuntimeException('Wait method never resolved to true after ' . $this->attempts . ' attempts');
}
$this->dispatch('waiter.before_wait', array(
'waiter' => $this,
'config' => $this->config,
));
if ($this->getInterval()) {
usleep($this->getInterval() * 1000000);
}
} while (1);
}
/**
* Method to implement in subclasses
*
* @return bool Return true when successful, false on failure
*/
abstract protected function doWait();
}
@@ -0,0 +1,82 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\RuntimeException;
/**
* Callable wait implementation
*/
class CallableWaiter extends AbstractWaiter
{
/**
* @var callable Callable function
*/
protected $callable;
/**
* @var array Additional context for the callable function
*/
protected $context = array();
/**
* Set the callable function to call in each wait attempt
*
* @param callable $callable Callable function
*
* @return self
* @throws InvalidArgumentException when the method is not callable
*/
public function setCallable($callable)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException('Value is not callable');
}
$this->callable = $callable;
return $this;
}
/**
* Set additional context for the callable function. This data will be passed into the callable function as the
* second argument
*
* @param array $context Additional context
*
* @return self
*/
public function setContext(array $context)
{
$this->context = $context;
return $this;
}
/**
* {@inheritdoc}
*/
public function doWait()
{
if (!$this->callable) {
throw new RuntimeException('No callable was specified for the wait method');
}
return call_user_func($this->callable, $this->attempts, $this->context);
}
}
@@ -0,0 +1,90 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Exception\InvalidArgumentException;
/**
* Factory that utilizes multiple factories for creating waiters
*/
class CompositeWaiterFactory implements WaiterFactoryInterface
{
/**
* @var array Array of factories
*/
protected $factories;
/**
* @param array $factories Array of factories used to instantiate waiters
*/
public function __construct(array $factories)
{
$this->factories = $factories;
}
/**
* {@inheritdoc}
*/
public function build($waiter)
{
if (!($factory = $this->getFactory($waiter))) {
throw new InvalidArgumentException("Waiter was not found matching {$waiter}.");
}
return $factory->build($waiter);
}
/**
* {@inheritdoc}
*/
public function canBuild($waiter)
{
return (bool) $this->getFactory($waiter);
}
/**
* Add a factory to the composite factory
*
* @param WaiterFactoryInterface $factory Factory to add
*
* @return self
*/
public function addFactory(WaiterFactoryInterface $factory)
{
$this->factories[] = $factory;
return $this;
}
/**
* Get the factory that matches the waiter name
*
* @param string $waiter Name of the waiter
*
* @return WaiterFactoryInterface|bool
*/
protected function getFactory($waiter)
{
foreach ($this->factories as $factory) {
if ($factory->canBuild($waiter)) {
return $factory;
}
}
return false;
}
}
@@ -0,0 +1,225 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Exception\InvalidArgumentException;
use Aws\Common\Exception\RuntimeException;
use Aws\Common\Exception\ServiceResponseException;
use Guzzle\Service\Resource\Model;
use Guzzle\Service\Exception\ValidationException;
/**
* Resource waiter driven by configuration options
*/
class ConfigResourceWaiter extends AbstractResourceWaiter
{
/**
* @var WaiterConfig Waiter configuration
*/
protected $waiterConfig;
/**
* @param WaiterConfig $waiterConfig Waiter configuration
*/
public function __construct(WaiterConfig $waiterConfig)
{
$this->waiterConfig = $waiterConfig;
$this->setInterval($waiterConfig->get(WaiterConfig::INTERVAL));
$this->setMaxAttempts($waiterConfig->get(WaiterConfig::MAX_ATTEMPTS));
}
/**
* {@inheritdoc}
*/
public function setConfig(array $config)
{
foreach ($config as $key => $value) {
if (substr($key, 0, 7) == 'waiter.') {
$this->waiterConfig->set(substr($key, 7), $value);
}
}
if (!isset($config[self::INTERVAL])) {
$config[self::INTERVAL] = $this->waiterConfig->get(WaiterConfig::INTERVAL);
}
if (!isset($config[self::MAX_ATTEMPTS])) {
$config[self::MAX_ATTEMPTS] = $this->waiterConfig->get(WaiterConfig::MAX_ATTEMPTS);
}
return parent::setConfig($config);
}
/**
* Get the waiter's configuration data
*
* @return WaiterConfig
*/
public function getWaiterConfig()
{
return $this->waiterConfig;
}
/**
* {@inheritdoc}
*/
protected function doWait()
{
$params = $this->config;
// remove waiter settings from the operation's input
foreach (array_keys($params) as $key) {
if (substr($key, 0, 7) == 'waiter.') {
unset($params[$key]);
}
}
$operation = $this->client->getCommand($this->waiterConfig->get(WaiterConfig::OPERATION), $params);
try {
return $this->checkResult($this->client->execute($operation));
} catch (ValidationException $e) {
throw new InvalidArgumentException(
$this->waiterConfig->get(WaiterConfig::WAITER_NAME) . ' waiter validation failed: ' . $e->getMessage(),
$e->getCode(),
$e
);
} catch (ServiceResponseException $e) {
// Check if this exception satisfies a success or failure acceptor
$transition = $this->checkErrorAcceptor($e);
if (null !== $transition) {
return $transition;
}
// Check if this exception should be ignored
foreach ((array) $this->waiterConfig->get(WaiterConfig::IGNORE_ERRORS) as $ignore) {
if ($e->getExceptionCode() == $ignore) {
// This exception is ignored, so it counts as a failed attempt rather than a fast-fail
return false;
}
}
// Allow non-ignore exceptions to bubble through
throw $e;
}
}
/**
* Check if an exception satisfies a success or failure acceptor
*
* @param ServiceResponseException $e
*
* @return bool|null Returns true for success, false for failure, and null for no transition
*/
protected function checkErrorAcceptor(ServiceResponseException $e)
{
if ($this->waiterConfig->get(WaiterConfig::SUCCESS_TYPE) == 'error') {
if ($e->getExceptionCode() == $this->waiterConfig->get(WaiterConfig::SUCCESS_VALUE)) {
// Mark as a success
return true;
}
}
// Mark as an attempt
return null;
}
/**
* Check to see if the response model satisfies a success or failure state
*
* @param Model $result Result model
*
* @return bool
* @throws RuntimeException
*/
protected function checkResult(Model $result)
{
// Check if the result evaluates to true based on the path and output model
if ($this->waiterConfig->get(WaiterConfig::SUCCESS_TYPE) == 'output' &&
$this->checkPath(
$result,
$this->waiterConfig->get(WaiterConfig::SUCCESS_PATH),
$this->waiterConfig->get(WaiterConfig::SUCCESS_VALUE)
)
) {
return true;
}
// It did not finish waiting yet. Determine if we need to fail-fast based on the failure acceptor.
if ($this->waiterConfig->get(WaiterConfig::FAILURE_TYPE) == 'output') {
$failureValue = $this->waiterConfig->get(WaiterConfig::FAILURE_VALUE);
if ($failureValue) {
$key = $this->waiterConfig->get(WaiterConfig::FAILURE_PATH);
if ($this->checkPath($result, $key, $failureValue, false)) {
// Determine which of the results triggered the failure
$triggered = array_intersect(
(array) $this->waiterConfig->get(WaiterConfig::FAILURE_VALUE),
array_unique((array) $result->getPath($key))
);
// fast fail because the failure case was satisfied
throw new RuntimeException(
'A resource entered into an invalid state of "'
. implode(', ', $triggered) . '" while waiting with the "'
. $this->waiterConfig->get(WaiterConfig::WAITER_NAME) . '" waiter.'
);
}
}
}
return false;
}
/**
* Check to see if the path of the output key is satisfied by the value
*
* @param Model $model Result model
* @param string $key Key to check
* @param string $checkValue Compare the key to the value
* @param bool $all Set to true to ensure all value match or false to only match one
*
* @return bool
*/
protected function checkPath(Model $model, $key = null, $checkValue = array(), $all = true)
{
// If no key is set, then just assume true because the request succeeded
if (!$key) {
return true;
}
if (!($result = $model->getPath($key))) {
return false;
}
$total = $matches = 0;
foreach ((array) $result as $value) {
$total++;
foreach ((array) $checkValue as $check) {
if ($value == $check) {
$matches++;
break;
}
}
}
// When matching all values, ensure that the match count matches the total count
if ($all && $total != $matches) {
return false;
}
return $matches > 0;
}
}
@@ -0,0 +1,34 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Client\AwsClientInterface;
/**
* Interface used in conjunction with clients to wait on a resource
*/
interface ResourceWaiterInterface extends WaiterInterface
{
/**
* Set the client associated with the waiter
*
* @param AwsClientInterface $client Client to use with the waiter
*
* @return self
*/
public function setClient(AwsClientInterface $client);
}
@@ -0,0 +1,106 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Inflection\Inflector;
use Guzzle\Inflection\InflectorInterface;
/**
* Factory for creating {@see WaiterInterface} objects using a convention of
* storing waiter classes in the Waiter folder of a client class namespace using
* a snake_case to CamelCase conversion (e.g. camel_case => CamelCase).
*/
class WaiterClassFactory implements WaiterFactoryInterface
{
/**
* @var array List of namespaces used to look for classes
*/
protected $namespaces;
/**
* @var InflectorInterface Inflector used to inflect class names
*/
protected $inflector;
/**
* @param array|string $namespaces Namespaces of waiter objects
* @param InflectorInterface $inflector Inflector used to resolve class names
*/
public function __construct($namespaces = array(), InflectorInterface $inflector = null)
{
$this->namespaces = (array) $namespaces;
$this->inflector = $inflector ?: Inflector::getDefault();
}
/**
* Registers a namespace to check for Waiters
*
* @param string $namespace Namespace which contains Waiter classes
*
* @return self
*/
public function registerNamespace($namespace)
{
array_unshift($this->namespaces, $namespace);
return $this;
}
/**
* {@inheritdoc}
*/
public function build($waiter)
{
if (!($className = $this->getClassName($waiter))) {
throw new InvalidArgumentException("Waiter was not found matching {$waiter}.");
}
return new $className();
}
/**
* {@inheritdoc}
*/
public function canBuild($waiter)
{
return $this->getClassName($waiter) !== null;
}
/**
* Get the name of a waiter class
*
* @param string $waiter Waiter name
*
* @return string|null
*/
protected function getClassName($waiter)
{
$waiterName = $this->inflector->camel($waiter);
// Determine the name of the class to load
$className = null;
foreach ($this->namespaces as $namespace) {
$potentialClassName = $namespace . '\\' . $waiterName;
if (class_exists($potentialClassName)) {
return $potentialClassName;
}
}
return null;
}
}
@@ -0,0 +1,67 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Guzzle\Common\Collection;
/**
* Configuration info of a waiter object
*/
class WaiterConfig extends Collection
{
const WAITER_NAME = 'name';
const MAX_ATTEMPTS = 'max_attempts';
const INTERVAL = 'interval';
const OPERATION = 'operation';
const IGNORE_ERRORS = 'ignore_errors';
const DESCRIPTION = 'description';
const SUCCESS_TYPE = 'success.type';
const SUCCESS_PATH = 'success.path';
const SUCCESS_VALUE = 'success.value';
const FAILURE_TYPE = 'failure.type';
const FAILURE_PATH = 'failure.path';
const FAILURE_VALUE = 'failure.value';
/**
* @param array $data Array of configuration directives
*/
public function __construct(array $data = array())
{
$this->data = $data;
$this->extractConfig();
}
/**
* Create the command configuration variables
*/
protected function extractConfig()
{
// Populate success.* and failure.* if specified in acceptor.*
foreach ($this->data as $key => $value) {
if (substr($key, 0, 9) == 'acceptor.') {
$name = substr($key, 9);
if (!isset($this->data["success.{$name}"])) {
$this->data["success.{$name}"] = $value;
}
if (!isset($this->data["failure.{$name}"])) {
$this->data["failure.{$name}"] = $value;
}
unset($this->data[$key]);
}
}
}
}
@@ -0,0 +1,98 @@
<?php
/**
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
namespace Aws\Common\Waiter;
use Aws\Common\Exception\InvalidArgumentException;
use Guzzle\Inflection\Inflector;
use Guzzle\Inflection\InflectorInterface;
/**
* Factory for creating {@see WaiterInterface} objects using a configuration DSL.
*/
class WaiterConfigFactory implements WaiterFactoryInterface
{
/**
* @var array Configuration directives
*/
protected $config;
/**
* @var InflectorInterface Inflector used to inflect class names
*/
protected $inflector;
/**
* @param array $config Array of configuration directives
* @param InflectorInterface $inflector Inflector used to resolve class names
*/
public function __construct(
array $config,
InflectorInterface $inflector = null
) {
$this->config = $config;
$this->inflector = $inflector ?: Inflector::getDefault();
}
/**
* {@inheritdoc}
*/
public function build($waiter)
{
return new ConfigResourceWaiter($this->getWaiterConfig($waiter));
}
/**
* {@inheritdoc}
*/
public function canBuild($waiter)
{
return isset($this->config[$waiter]) || isset($this->config[$this->inflector->camel($waiter)]);
}
/**
* Get waiter configuration data, taking __default__ and extensions into account
*
* @param string $name Waiter name
*
* @return WaiterConfig
* @throws InvalidArgumentException
*/
protected function getWaiterConfig($name)
{
if (!$this->canBuild($name)) {
throw new InvalidArgumentException('No waiter found matching "' . $name . '"');
}
// inflect the name if needed
$name = isset($this->config[$name]) ? $name : $this->inflector->camel($name);
$waiter = new WaiterConfig($this->config[$name]);
$waiter['name'] = $name;
// Always use __default__ as the basis if it's set
if (isset($this->config['__default__'])) {
$parentWaiter = new WaiterConfig($this->config['__default__']);
$waiter = $parentWaiter->overwriteWith($waiter);
}
// Allow for configuration extensions
if (isset($this->config[$name]['extends'])) {
$waiter = $this->getWaiterConfig($this->config[$name]['extends'])->overwriteWith($waiter);
}
return $waiter;
}
}

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