File indexing completed on 2025-03-02 05:29:46

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Service_Amazon
0017  * @subpackage Ec2
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Service_Amazon_Abstract
0025  */
0026 // require_once 'Zend/Service/Amazon/Abstract.php';
0027 
0028 /**
0029  * @see Zend_Service_Amazon_Ec2_Response
0030  */
0031 // require_once 'Zend/Service/Amazon/Ec2/Response.php';
0032 
0033 /**
0034  * @see Zend_Service_Amazon_Ec2_Exception
0035  */
0036 // require_once 'Zend/Service/Amazon/Ec2/Exception.php';
0037 
0038 /**
0039  * Provides the basic functionality to send a request to the Amazon Ec2 Query API
0040  *
0041  * @category   Zend
0042  * @package    Zend_Service_Amazon
0043  * @subpackage Ec2
0044  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0045  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0046  */
0047 abstract class Zend_Service_Amazon_Ec2_Abstract extends Zend_Service_Amazon_Abstract
0048 {
0049     /**
0050      * The HTTP query server
0051      */
0052     protected $_ec2Endpoint = 'ec2.amazonaws.com';
0053 
0054     /**
0055      * The API version to use
0056      */
0057     protected $_ec2ApiVersion = '2009-04-04';
0058 
0059     /**
0060      * Signature Version
0061      */
0062     protected $_ec2SignatureVersion = '2';
0063 
0064     /**
0065      * Signature Encoding Method
0066      */
0067     protected $_ec2SignatureMethod = 'HmacSHA256';
0068 
0069     /**
0070      * Period after which HTTP request will timeout in seconds
0071      */
0072     protected $_httpTimeout = 10;
0073 
0074     /**
0075      * @var string Amazon Region
0076      */
0077     protected static $_defaultRegion = null;
0078 
0079     /**
0080      * @var string Amazon Region
0081      */
0082     protected $_region;
0083 
0084     /**
0085      * An array that contains all the valid Amazon Ec2 Regions.
0086      *
0087      * @var array
0088      */
0089     protected static $_validEc2Regions = array('eu-west-1', 'us-east-1');
0090 
0091     /**
0092      * Create Amazon client.
0093      *
0094      * @param  string $access_key       Override the default Access Key
0095      * @param  string $secret_key       Override the default Secret Key
0096      * @param  string $region           Sets the AWS Region
0097      * @return void
0098      */
0099     public function __construct($accessKey=null, $secretKey=null, $region=null)
0100     {
0101         if(!$region) {
0102             $region = self::$_defaultRegion;
0103         } else {
0104             // make rue the region is valid
0105             if(!empty($region) && !in_array(strtolower($region), self::$_validEc2Regions, true)) {
0106                 // require_once 'Zend/Service/Amazon/Exception.php';
0107                 throw new Zend_Service_Amazon_Exception('Invalid Amazon Ec2 Region');
0108             }
0109         }
0110 
0111         $this->_region = $region;
0112 
0113         parent::__construct($accessKey, $secretKey);
0114     }
0115 
0116     /**
0117      * Set which region you are working in.  It will append the
0118      * end point automaticly
0119      *
0120      * @param string $region
0121      */
0122     public static function setRegion($region)
0123     {
0124         if(in_array(strtolower($region), self::$_validEc2Regions, true)) {
0125             self::$_defaultRegion = $region;
0126         } else {
0127             // require_once 'Zend/Service/Amazon/Exception.php';
0128             throw new Zend_Service_Amazon_Exception('Invalid Amazon Ec2 Region');
0129         }
0130     }
0131 
0132     /**
0133      * Method to fetch the AWS Region
0134      *
0135      * @return string
0136      */
0137     protected function _getRegion()
0138     {
0139         return (!empty($this->_region)) ? $this->_region . '.' : '';
0140     }
0141 
0142     /**
0143      * Sends a HTTP request to the queue service using Zend_Http_Client
0144      *
0145      * @param  array $params List of parameters to send with the request
0146      * @return Zend_Service_Amazon_Ec2_Response
0147      * @throws Zend_Service_Amazon_Ec2_Exception
0148      */
0149     protected function sendRequest(array $params = array())
0150     {
0151         $url = 'https://' . $this->_getRegion() . $this->_ec2Endpoint . '/';
0152 
0153         $params = $this->addRequiredParameters($params);
0154 
0155         try {
0156             /* @var $request Zend_Http_Client */
0157             $request = self::getHttpClient();
0158             $request->resetParameters();
0159 
0160             $request->setConfig(array(
0161                 'timeout' => $this->_httpTimeout
0162             ));
0163 
0164             $request->setUri($url);
0165             $request->setMethod(Zend_Http_Client::POST);
0166             $request->setParameterPost($params);
0167 
0168             $httpResponse = $request->request();
0169             
0170 
0171         } catch (Zend_Http_Client_Exception $zhce) {
0172             $message = 'Error in request to AWS service: ' . $zhce->getMessage();
0173             throw new Zend_Service_Amazon_Ec2_Exception($message, $zhce->getCode(), $zhce);
0174         }
0175         $response = new Zend_Service_Amazon_Ec2_Response($httpResponse);
0176         $this->checkForErrors($response);
0177 
0178         return $response;
0179     }
0180 
0181     /**
0182      * Adds required authentication and version parameters to an array of
0183      * parameters
0184      *
0185      * The required parameters are:
0186      * - AWSAccessKey
0187      * - SignatureVersion
0188      * - Timestamp
0189      * - Version and
0190      * - Signature
0191      *
0192      * If a required parameter is already set in the <tt>$parameters</tt> array,
0193      * it is overwritten.
0194      *
0195      * @param array $parameters the array to which to add the required
0196      *                          parameters.
0197      *
0198      * @return array
0199      */
0200     protected function addRequiredParameters(array $parameters)
0201     {
0202         $parameters['AWSAccessKeyId']   = $this->_getAccessKey();
0203         $parameters['SignatureVersion'] = $this->_ec2SignatureVersion;
0204         $parameters['Timestamp']        = gmdate('Y-m-d\TH:i:s\Z');
0205         $parameters['Version']          = $this->_ec2ApiVersion;
0206         $parameters['SignatureMethod']  = $this->_ec2SignatureMethod;
0207         $parameters['Signature']        = $this->signParameters($parameters);
0208 
0209         return $parameters;
0210     }
0211 
0212     /**
0213      * Computes the RFC 2104-compliant HMAC signature for request parameters
0214      *
0215      * This implements the Amazon Web Services signature, as per the following
0216      * specification:
0217      *
0218      * 1. Sort all request parameters (including <tt>SignatureVersion</tt> and
0219      *    excluding <tt>Signature</tt>, the value of which is being created),
0220      *    ignoring case.
0221      *
0222      * 2. Iterate over the sorted list and append the parameter name (in its
0223      *    original case) and then its value. Do not URL-encode the parameter
0224      *    values before constructing this string. Do not use any separator
0225      *    characters when appending strings.
0226      *
0227      * @param array  $parameters the parameters for which to get the signature.
0228      * @param string $secretKey  the secret key to use to sign the parameters.
0229      *
0230      * @return string the signed data.
0231      */
0232     protected function signParameters(array $paramaters)
0233     {
0234         $data = "POST\n";
0235         $data .= $this->_getRegion() . $this->_ec2Endpoint . "\n";
0236         $data .= "/\n";
0237 
0238         uksort($paramaters, 'strcmp');
0239         unset($paramaters['Signature']);
0240 
0241         $arrData = array();
0242         foreach($paramaters as $key => $value) {
0243             $arrData[] = $key . '=' . str_replace("%7E", "~", rawurlencode($value));
0244         }
0245 
0246         $data .= implode('&', $arrData);
0247 
0248         // require_once 'Zend/Crypt/Hmac.php';
0249         $hmac = Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'SHA256', $data, Zend_Crypt_Hmac::BINARY);
0250 
0251         return base64_encode($hmac);
0252     }
0253 
0254     /**
0255      * Checks for errors responses from Amazon
0256      *
0257      * @param Zend_Service_Amazon_Ec2_Response $response the response object to
0258      *                                                   check.
0259      *
0260      * @return void
0261      *
0262      * @throws Zend_Service_Amazon_Ec2_Exception if one or more errors are
0263      *         returned from Amazon.
0264      */
0265     private function checkForErrors(Zend_Service_Amazon_Ec2_Response $response)
0266     {
0267         $xpath = new DOMXPath($response->getDocument());
0268         $list  = $xpath->query('//Error');
0269         if ($list->length > 0) {
0270             $node    = $list->item(0);
0271             $code    = $xpath->evaluate('string(Code/text())', $node);
0272             $message = $xpath->evaluate('string(Message/text())', $node);
0273             throw new Zend_Service_Amazon_Ec2_Exception($message, 0, $code);
0274         }
0275 
0276     }
0277 }