_host = $host; $this->_accountName = $accountName; $this->_accountKey = $accountKey; $this->_usePathStyleUri = $usePathStyleUri; // Using local storage? if (!$this->_usePathStyleUri && ($this->_host == self::URL_DEV_BLOB || $this->_host == self::URL_DEV_QUEUE || $this->_host == self::URL_DEV_TABLE) ) { // Local storage $this->_usePathStyleUri = true; } if (is_null($this->_credentials)) { $this->_credentials = new Zend_Service_WindowsAzure_Credentials_SharedKey( $this->_accountName, $this->_accountKey, $this->_usePathStyleUri); } $this->_retryPolicy = $retryPolicy; if (is_null($this->_retryPolicy)) { $this->_retryPolicy = Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract::noRetry(); } // Setup default Zend_Http_Client channel $options = array( 'adapter' => 'Zend_Http_Client_Adapter_Proxy' ); if (function_exists('curl_init')) { // Set cURL options if cURL is used afterwards $options['curloptions'] = array( CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 120, ); } $this->_httpClientChannel = new Zend_Http_Client(null, $options); } /** * Set the HTTP client channel to use * * @param Zend_Http_Client_Adapter_Interface|string $adapterInstance Adapter instance or adapter class name. */ public function setHttpClientChannel($adapterInstance = 'Zend_Http_Client_Adapter_Proxy') { $this->_httpClientChannel->setAdapter($adapterInstance); } /** * Retrieve HTTP client channel * * @return Zend_Http_Client_Adapter_Interface */ public function getHttpClientChannel() { return $this->_httpClientChannel; } /** * Set retry policy to use when making requests * * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests */ public function setRetryPolicy(Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null) { $this->_retryPolicy = $retryPolicy; if (is_null($this->_retryPolicy)) { $this->_retryPolicy = Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract::noRetry(); } } /** * Set proxy * * @param boolean $useProxy Use proxy? * @param string $proxyUrl Proxy URL * @param int $proxyPort Proxy port * @param string $proxyCredentials Proxy credentials */ public function setProxy($useProxy = false, $proxyUrl = '', $proxyPort = 80, $proxyCredentials = '') { $this->_useProxy = $useProxy; $this->_proxyUrl = $proxyUrl; $this->_proxyPort = $proxyPort; $this->_proxyCredentials = $proxyCredentials; if ($this->_useProxy) { $credentials = explode(':', $this->_proxyCredentials); $this->_httpClientChannel->setConfig(array( 'proxy_host' => $this->_proxyUrl, 'proxy_port' => $this->_proxyPort, 'proxy_user' => $credentials[0], 'proxy_pass' => $credentials[1], )); } else { $this->_httpClientChannel->setConfig(array( 'proxy_host' => '', 'proxy_port' => 8080, 'proxy_user' => '', 'proxy_pass' => '', )); } } /** * Returns the Windows Azure account name * * @return string */ public function getAccountName() { return $this->_accountName; } /** * Get base URL for creating requests * * @return string */ public function getBaseUrl() { if ($this->_usePathStyleUri) { return 'http://' . $this->_host . '/' . $this->_accountName; } else { return 'http://' . $this->_accountName . '.' . $this->_host; } } /** * Set Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance * * @param Zend_Service_WindowsAzure_Credentials_CredentialsAbstract $credentials Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance to use for request signing. */ public function setCredentials(Zend_Service_WindowsAzure_Credentials_CredentialsAbstract $credentials) { $this->_credentials = $credentials; $this->_credentials->setAccountName($this->_accountName); $this->_credentials->setAccountkey($this->_accountKey); $this->_credentials->setUsePathStyleUri($this->_usePathStyleUri); } /** * Get Zend_Service_WindowsAzure_Credentials_CredentialsAbstract instance * * @return Zend_Service_WindowsAzure_Credentials_CredentialsAbstract */ public function getCredentials() { return $this->_credentials; } /** * Perform request using Zend_Http_Client channel * * @param string $path Path * @param string $queryString Query string * @param string $httpVerb HTTP verb the request will use * @param array $headers x-ms headers to add * @param boolean $forTableStorage Is the request for table storage? * @param mixed $rawData Optional RAW HTTP data to be sent over the wire * @param string $resourceType Resource type * @param string $requiredPermission Required permission * @return Zend_Http_Response */ protected function _performRequest( $path = '/', $queryString = '', $httpVerb = Zend_Http_Client::GET, $headers = array(), $forTableStorage = false, $rawData = null, $resourceType = Zend_Service_WindowsAzure_Storage::RESOURCE_UNKNOWN, $requiredPermission = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ ) { // Clean path if (strpos($path, '/') !== 0) { $path = '/' . $path; } // Clean headers if (is_null($headers)) { $headers = array(); } // Ensure cUrl will also work correctly: // - disable Content-Type if required // - disable Expect: 100 Continue if (!isset($headers["Content-Type"])) { $headers["Content-Type"] = ''; } $headers["Expect"]= ''; // Add version header $headers['x-ms-version'] = $this->_apiVersion; // URL encoding $path = self::urlencode($path); $queryString = self::urlencode($queryString); // Generate URL and sign request $requestUrl = $this->_credentials ->signRequestUrl($this->getBaseUrl() . $path . $queryString, $resourceType, $requiredPermission); $requestHeaders = $this->_credentials ->signRequestHeaders($httpVerb, $path, $queryString, $headers, $forTableStorage, $resourceType, $requiredPermission, $rawData); // Prepare request $this->_httpClientChannel->resetParameters(true); $this->_httpClientChannel->setUri($requestUrl); $this->_httpClientChannel->setHeaders($requestHeaders); $this->_httpClientChannel->setRawData($rawData); // Execute request $response = $this->_retryPolicy->execute( array($this->_httpClientChannel, 'request'), array($httpVerb) ); return $response; } /** * Parse result from Zend_Http_Response * * @param Zend_Http_Response $response Response from HTTP call * @return object * @throws Zend_Service_WindowsAzure_Exception */ protected function _parseResponse(Zend_Http_Response $response = null) { if (is_null($response)) { require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Response should not be null.'); } $xml = Zend_Xml_Security::scan($response->getBody()); if ($xml !== false) { // Fetch all namespaces $namespaces = array_merge($xml->getNamespaces(true), $xml->getDocNamespaces(true)); // Register all namespace prefixes foreach ($namespaces as $prefix => $ns) { if ($prefix != '') { $xml->registerXPathNamespace($prefix, $ns); } } } return $xml; } /** * Generate metadata headers * * @param array $metadata * @return HTTP headers containing metadata */ protected function _generateMetadataHeaders($metadata = array()) { // Validate if (!is_array($metadata)) { return array(); } // Return headers $headers = array(); foreach ($metadata as $key => $value) { if (strpos($value, "\r") !== false || strpos($value, "\n") !== false) { require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Metadata cannot contain newline characters.'); } if (!self::isValidMetadataName($key)) { require_once 'Zend/Service/WindowsAzure/Exception.php'; throw new Zend_Service_WindowsAzure_Exception('Metadata name does not adhere to metadata naming conventions. See http://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx for more information.'); } $headers["x-ms-meta-" . strtolower($key)] = $value; } return $headers; } /** * Parse metadata headers * * @param array $headers HTTP headers containing metadata * @return array */ protected function _parseMetadataHeaders($headers = array()) { // Validate if (!is_array($headers)) { return array(); } // Return metadata $metadata = array(); foreach ($headers as $key => $value) { if (substr(strtolower($key), 0, 10) == "x-ms-meta-") { $metadata[str_replace("x-ms-meta-", '', strtolower($key))] = $value; } } return $metadata; } /** * Parse metadata XML * * @param SimpleXMLElement $parentElement Element containing the Metadata element. * @return array */ protected function _parseMetadataElement($element = null) { // Metadata present? if (!is_null($element) && isset($element->Metadata) && !is_null($element->Metadata)) { return get_object_vars($element->Metadata); } return array(); } /** * Generate ISO 8601 compliant date string in UTC time zone * * @param int $timestamp * @return string */ public function isoDate($timestamp = null) { $tz = @date_default_timezone_get(); @date_default_timezone_set('UTC'); if (is_null($timestamp)) { $timestamp = time(); } $returnValue = str_replace('+00:00', '.0000000Z', @date('c', $timestamp)); @date_default_timezone_set($tz); return $returnValue; } /** * URL encode function * * @param string $value Value to encode * @return string Encoded value */ public static function urlencode($value) { return str_replace(' ', '%20', $value); } /** * Is valid metadata name? * * @param string $metadataName Metadata name * @return boolean */ public static function isValidMetadataName($metadataName = '') { if (preg_match("/^[a-zA-Z0-9_@][a-zA-Z0-9_]*$/", $metadataName) === 0) { return false; } if ($metadataName == '') { return false; } return true; } /** * Builds a query string from an array of elements * * @param array Array of elements * @return string Assembled query string */ public static function createQueryStringFromArray($queryString) { return count($queryString) > 0 ? '?' . implode('&', $queryString) : ''; } }