PageRenderTime 52ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/administrator/components/com_akeeba/akeeba/plugins/utils/azure.php

https://bitbucket.org/kraymitchell/saiu
PHP | 2333 lines | 1048 code | 247 blank | 1038 comment | 159 complexity | a187f70fe7125c5dddfbc57882615055 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, BSD-3-Clause, LGPL-2.1, GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Copyright (c) 2009, RealDolmen
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * * Neither the name of RealDolmen nor the
  14. * names of its contributors may be used to endorse or promote products
  15. * derived from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY RealDolmen ''AS IS'' AND ANY
  18. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. * DISCLAIMED. IN NO EVENT SHALL RealDolmen BE LIABLE FOR ANY
  21. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. *
  28. * @category Microsoft
  29. * @package Microsoft
  30. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  31. * @license http://phpazure.codeplex.com/license
  32. */
  33. class AEUtilAzureBaseException extends Exception {}
  34. class AEUtilAzureHttpException extends AEUtilAzureBaseException {}
  35. class AEUtilAzureHttpTransportException extends AEUtilAzureHttpException {}
  36. class AEUtilAzureAPIException extends AEUtilAzureBaseException {}
  37. class AEUtilAzureRetryPolicyException extends AEUtilAzureAPIException {}
  38. /**
  39. * @category Microsoft
  40. * @package Microsoft_Http
  41. * @subpackage Transport
  42. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  43. * @license http://phpazure.codeplex.com/license
  44. */
  45. abstract class AEUtilAzureHttpTransport
  46. {
  47. /** HTTP VERBS */
  48. const VERB_GET = 'GET';
  49. const VERB_PUT = 'PUT';
  50. const VERB_POST = 'POST';
  51. const VERB_DELETE = 'DELETE';
  52. const VERB_HEAD = 'HEAD';
  53. const VERB_MERGE = 'MERGE';
  54. /**
  55. * Use proxy?
  56. *
  57. * @var boolean
  58. */
  59. protected $_useProxy = false;
  60. /**
  61. * Proxy url
  62. *
  63. * @var string
  64. */
  65. protected $_proxyUrl = '';
  66. /**
  67. * Proxy port
  68. *
  69. * @var int
  70. */
  71. protected $_proxyPort = 80;
  72. /**
  73. * Proxy credentials
  74. *
  75. * @var string
  76. */
  77. protected $_proxyCredentials = '';
  78. /**
  79. * Set proxy
  80. *
  81. * @param boolean $useProxy Use proxy?
  82. * @param string $proxyUrl Proxy URL
  83. * @param int $proxyPort Proxy port
  84. * @param string $proxyCredentials Proxy credentials
  85. */
  86. public function setProxy($useProxy = false, $proxyUrl = '', $proxyPort = 80, $proxyCredentials = '')
  87. {
  88. $this->_useProxy = $useProxy;
  89. $this->_proxyUrl = $proxyUrl;
  90. $this->_proxyPort = $proxyPort;
  91. $this->_proxyCredentials = $proxyCredentials;
  92. }
  93. /**
  94. * User agent string
  95. *
  96. * @var string
  97. */
  98. protected $_userAgent = 'AEUtilAzureHttpTransport';
  99. /**
  100. * Perform GET request
  101. *
  102. * @param $url Url to request
  103. * @param $variables Array of key-value pairs to use in the request
  104. * @param $headers Array of key-value pairs to use as additional headers
  105. * @param $rawBody Raw body to send to server
  106. * @return AEUtilAzureHttpResponse
  107. */
  108. public function get($url, $variables = array(), $headers = array(), $rawBody = null)
  109. {
  110. return $this->request(self::VERB_GET, $url, $variables, $headers, $rawBody);
  111. }
  112. /**
  113. * Perform PUT request
  114. *
  115. * @param $url Url to request
  116. * @param $variables Array of key-value pairs to use in the request
  117. * @param $headers Array of key-value pairs to use as additional headers
  118. * @param $rawBody Raw body to send to server
  119. * @return AEUtilAzureHttpResponse
  120. */
  121. public function put($url, $variables = array(), $headers = array(), $rawBody = null)
  122. {
  123. return $this->request(self::VERB_PUT, $url, $variables, $headers, $rawBody);
  124. }
  125. /**
  126. * Perform POST request
  127. *
  128. * @param $url Url to request
  129. * @param $variables Array of key-value pairs to use in the request
  130. * @param $headers Array of key-value pairs to use as additional headers
  131. * @param $rawBody Raw body to send to server
  132. * @return AEUtilAzureHttpResponse
  133. */
  134. public function post($url, $variables = array(), $headers = array(), $rawBody = null)
  135. {
  136. return $this->request(self::VERB_POST, $url, $variables, $headers, $rawBody);
  137. }
  138. /**
  139. * Perform DELETE request
  140. *
  141. * @param $url Url to request
  142. * @param $variables Array of key-value pairs to use in the request
  143. * @param $headers Array of key-value pairs to use as additional headers
  144. * @param $rawBody Raw body to send to server
  145. * @return AEUtilAzureHttpResponse
  146. */
  147. public function delete($url, $variables = array(), $headers = array(), $rawBody = null)
  148. {
  149. return $this->request(self::VERB_DELETE, $url, $variables, $headers, $rawBody);
  150. }
  151. /**
  152. * Perform request
  153. *
  154. * @param $httpVerb Http verb to use in the request
  155. * @param $url Url to request
  156. * @param $variables Array of key-value pairs to use in the request
  157. * @param $headers Array of key-value pairs to use as additional headers
  158. * @param $rawBody Raw body to send to server
  159. * @return AEUtilAzureHttpResponse
  160. */
  161. public abstract function request($httpVerb, $url, $variables = array(), $headers = array(), $rawBody = null);
  162. /**
  163. * Create channel
  164. *
  165. * @param $type string Transport channel type
  166. * @return AEUtilAzureHttpTransport
  167. */
  168. public static function createChannel($type = 'AEUtilAzureHttpTransportCurl')
  169. {
  170. return new $type();
  171. }
  172. }
  173. /**
  174. * @category Microsoft
  175. * @package Microsoft_Http
  176. * @subpackage Transport
  177. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  178. * @license http://phpazure.codeplex.com/license
  179. */
  180. class AEUtilAzureHttpTransportCurl extends AEUtilAzureHttpTransport
  181. {
  182. /**
  183. * AEUtilAzureHttpTransportCurl constructor
  184. */
  185. public function __construct()
  186. {
  187. if (!extension_loaded('curl')) {
  188. throw new AEUtilAzureHttpTransportException('cURL extension has to be loaded to use AEUtilAzureHttpTransportCurl.');
  189. }
  190. }
  191. /**
  192. * Perform request
  193. *
  194. * @param $httpVerb Http verb to use in the request
  195. * @param $url Url to request
  196. * @param $variables Array of key-value pairs to use in the request
  197. * @param $headers Array of key-value pairs to use as additional headers
  198. * @param $rawBody Raw body to send to server
  199. * @return AEUtilAzureHttpResponse
  200. */
  201. public function request($httpVerb, $url, $variables = array(), $headers = array(), $rawBody = null)
  202. {
  203. // Create a new cURL instance
  204. $curlHandle = curl_init();
  205. @curl_setopt($curlHandle, CURLOPT_CAINFO, AKEEBA_CACERT_PEM);
  206. curl_setopt($curlHandle, CURLOPT_USERAGENT, $this->_userAgent);
  207. curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, true);
  208. curl_setopt($curlHandle, CURLOPT_TIMEOUT, 120);
  209. // Set URL
  210. curl_setopt($curlHandle, CURLOPT_URL, $url);
  211. // Set HTTP parameters (version and request method)
  212. curl_setopt($curlHandle, CURL_HTTP_VERSION_1_1, true);
  213. switch ($httpVerb) {
  214. case AEUtilAzureHttpTransport::VERB_GET:
  215. curl_setopt($curlHandle, CURLOPT_HTTPGET, true);
  216. break;
  217. case AEUtilAzureHttpTransport::VERB_POST:
  218. curl_setopt($curlHandle, CURLOPT_POST, true);
  219. break;
  220. /*case AEUtilAzureHttpTransport::VERB_PUT:
  221. curl_setopt($curlHandle, CURLOPT_PUT, true);
  222. break;*/
  223. case AEUtilAzureHttpTransport::VERB_HEAD:
  224. // http://stackoverflow.com/questions/770179/php-curl-head-request-takes-a-long-time-on-some-sites
  225. curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, 'HEAD');
  226. curl_setopt($curlHandle, CURLOPT_NOBODY, true);
  227. break;
  228. default:
  229. curl_setopt($curlHandle, CURLOPT_CUSTOMREQUEST, $httpVerb);
  230. break;
  231. }
  232. // Clear Content-Length header
  233. $headers["Content-Length"] = 0;
  234. // Ensure headers are returned
  235. curl_setopt($curlHandle, CURLOPT_HEADER, true);
  236. // Do not verify SSl peer (Windows versions of cURL have an outdated CA)
  237. curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, (stristr(PHP_OS, 'WIN') ? false : true));
  238. // Set proxy?
  239. if ($this->_useProxy)
  240. {
  241. curl_setopt($curlHandle, CURLOPT_PROXY, $this->_proxyUrl);
  242. curl_setopt($curlHandle, CURLOPT_PROXYPORT, $this->_proxyPort);
  243. curl_setopt($curlHandle, CURLOPT_PROXYUSERPWD, $this->_proxyCredentials);
  244. }
  245. // Ensure response is returned
  246. curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
  247. // Set post fields / raw data
  248. // http://www.php.net/manual/en/function.curl-setopt.php#81161
  249. if (!is_null($rawBody) || (!is_null($variables) && count($variables) > 0))
  250. {
  251. if (!is_null($rawBody))
  252. {
  253. unset($headers["Content-Length"]);
  254. $headers["Content-Length"] = strlen($rawBody);
  255. }
  256. curl_setopt($curlHandle, CURLOPT_POSTFIELDS, is_null($rawBody) ? $variables : $rawBody);
  257. }
  258. // Set Content-Type header if required
  259. if (!isset($headers["Content-Type"])) {
  260. $headers["Content-Type"] = '';
  261. }
  262. // Disable Expect: 100-Continue
  263. // http://be2.php.net/manual/en/function.curl-setopt.php#82418
  264. $headers["Expect"] = '';
  265. // Add additional headers to cURL instance
  266. $curlHeaders = array();
  267. foreach ($headers as $key => $value)
  268. {
  269. $curlHeaders[] = $key.': '.$value;
  270. }
  271. curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $curlHeaders);
  272. // DEBUG: curl_setopt($curlHandle, CURLINFO_HEADER_OUT, true);
  273. // Execute request
  274. $rawResponse = curl_exec($curlHandle);
  275. $response = null;
  276. if ($rawResponse)
  277. {
  278. $response = AEUtilAzureHttpResponse::fromString($rawResponse);
  279. // DEBUG: var_dump($url);
  280. // DEBUG: var_dump(curl_getinfo($curlHandle,CURLINFO_HEADER_OUT));
  281. // DEBUG: var_dump($rawResponse);
  282. }
  283. else
  284. {
  285. throw new AEUtilAzureHttpTransportException('cURL error occured during request for ' . $url . ': ' . curl_errno($curlHandle) . ' - ' . curl_error($curlHandle));
  286. }
  287. curl_close($curlHandle);
  288. return $response;
  289. }
  290. }
  291. /**
  292. * AEUtilAzureHttpResponse
  293. *
  294. * This class is partially based on Zend Framework Zend_Http_Response - http://framework.zend.com
  295. *
  296. * @category Microsoft
  297. * @package Microsoft_Http
  298. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  299. * @license http://phpazure.codeplex.com/license
  300. */
  301. class AEUtilAzureHttpResponse
  302. {
  303. /**
  304. * List of all known HTTP response status codes
  305. *
  306. * @var array
  307. */
  308. protected static $_statusMessages = array(
  309. // Informational 1xx
  310. 100 => 'Continue',
  311. 101 => 'Switching Protocols',
  312. // Success 2xx
  313. 200 => 'OK',
  314. 201 => 'Created',
  315. 202 => 'Accepted',
  316. 203 => 'Non-Authoritative Information',
  317. 204 => 'No Content',
  318. 205 => 'Reset Content',
  319. 206 => 'Partial Content',
  320. // Redirection 3xx
  321. 300 => 'Multiple Choices',
  322. 301 => 'Moved Permanently',
  323. 302 => 'Found', // 1.1
  324. 303 => 'See Other',
  325. 304 => 'Not Modified',
  326. 305 => 'Use Proxy',
  327. // 306 is deprecated but reserved
  328. 307 => 'Temporary Redirect',
  329. // Client Error 4xx
  330. 400 => 'Bad Request',
  331. 401 => 'Unauthorized',
  332. 402 => 'Payment Required',
  333. 403 => 'Forbidden',
  334. 404 => 'Not Found',
  335. 405 => 'Method Not Allowed',
  336. 406 => 'Not Acceptable',
  337. 407 => 'Proxy Authentication Required',
  338. 408 => 'Request Timeout',
  339. 409 => 'Conflict',
  340. 410 => 'Gone',
  341. 411 => 'Length Required',
  342. 412 => 'Precondition Failed',
  343. 413 => 'Request Entity Too Large',
  344. 414 => 'Request-URI Too Long',
  345. 415 => 'Unsupported Media Type',
  346. 416 => 'Requested Range Not Satisfiable',
  347. 417 => 'Expectation Failed',
  348. // Server Error 5xx
  349. 500 => 'Internal Server Error',
  350. 501 => 'Not Implemented',
  351. 502 => 'Bad Gateway',
  352. 503 => 'Service Unavailable',
  353. 504 => 'Gateway Timeout',
  354. 505 => 'HTTP Version Not Supported',
  355. 509 => 'Bandwidth Limit Exceeded'
  356. );
  357. /**
  358. * The HTTP version (1.0, 1.1)
  359. *
  360. * @var string
  361. */
  362. protected $_version;
  363. /**
  364. * The HTTP response code
  365. *
  366. * @var int
  367. */
  368. protected $_code;
  369. /**
  370. * The HTTP response code as string
  371. * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
  372. *
  373. * @var string
  374. */
  375. protected $_message;
  376. /**
  377. * The HTTP response headers array
  378. *
  379. * @var array
  380. */
  381. protected $_headers = array();
  382. /**
  383. * The HTTP response body
  384. *
  385. * @var string
  386. */
  387. protected $_body;
  388. /**
  389. * HTTP response constructor
  390. *
  391. * @param int $code Response code (200, 404, 500, ...)
  392. * @param array $headers Headers array
  393. * @param string $body Response body
  394. * @param string $version HTTP version
  395. * @throws AEUtilAzureHttpException
  396. */
  397. public function __construct($code, $headers, $body = null, $version = '1.1')
  398. {
  399. // Code
  400. $this->_code = $code;
  401. // Message
  402. $this->_message = self::$_statusMessages[$code];
  403. // Body
  404. $this->_body = $body;
  405. // Version
  406. if (! preg_match('|^\d\.\d$|', $version)) {
  407. throw new AEUtilAzureHttpException('No valid HTTP version was passed: ' . $version);
  408. }
  409. $this->_version = $version;
  410. // Headers
  411. if (!is_array($headers))
  412. {
  413. throw new AEUtilAzureHttpException('No valid headers were passed');
  414. }
  415. else
  416. {
  417. foreach ($headers as $name => $value) {
  418. if (is_int($name))
  419. list($name, $value) = explode(":", $value, 1);
  420. $this->_headers[ucwords(strtolower($name))] = trim($value);
  421. }
  422. }
  423. }
  424. /**
  425. * Check whether the response is an error
  426. *
  427. * @return boolean
  428. */
  429. public function isError()
  430. {
  431. $restype = floor($this->code / 100);
  432. return ($restype == 4 || $restype == 5);
  433. }
  434. /**
  435. * Check whether the response in successful
  436. *
  437. * @return boolean
  438. */
  439. public function isSuccessful()
  440. {
  441. $restype = floor($this->_code / 100);
  442. return ($restype == 2 || $restype == 1);
  443. }
  444. /**
  445. * Check whether the response is a redirection
  446. *
  447. * @return boolean
  448. */
  449. public function isRedirect()
  450. {
  451. $restype = floor($this->_code / 100);
  452. return ($restype == 3);
  453. }
  454. /**
  455. * Get the HTTP version (1.0, 1.1)
  456. *
  457. * @return string
  458. */
  459. public function getVersion()
  460. {
  461. return $this->_version;
  462. }
  463. /**
  464. * Get the HTTP response code
  465. *
  466. * @return int
  467. */
  468. public function getCode()
  469. {
  470. return $this->_code;
  471. }
  472. /**
  473. * Get the HTTP response code as string
  474. * (e.g. 'Not Found' for 404 or 'Internal Server Error' for 500)
  475. *
  476. * @return string
  477. */
  478. public function getMessage()
  479. {
  480. return $this->_message;
  481. }
  482. /**
  483. * Get the HTTP response headers array
  484. *
  485. * @return array
  486. */
  487. public function getHeaders()
  488. {
  489. if (!is_array($this->_headers))
  490. {
  491. $this->_headers = array();
  492. }
  493. return $this->_headers;
  494. }
  495. /**
  496. * Get a specific header as string, or null if it is not set
  497. *
  498. * @param string $header
  499. * @return string|array|null
  500. */
  501. public function getHeader($header)
  502. {
  503. $header = ucwords(strtolower($header));
  504. if (!is_string($header) || ! isset($this->_headers[$header])) return null;
  505. return $this->_headers[$header];
  506. }
  507. /**
  508. * The HTTP response body
  509. *
  510. * @return string
  511. */
  512. public function getBody()
  513. {
  514. return $this->_body;
  515. }
  516. /**
  517. * Extract the response code from a response string
  518. *
  519. * @param string $responseString
  520. * @return int
  521. */
  522. public static function extractCode($responseString)
  523. {
  524. preg_match("|^HTTP/[\d\.x]+ (\d+)|", $responseString, $m);
  525. if (isset($m[1])) {
  526. return (int) $m[1];
  527. } else {
  528. return false;
  529. }
  530. }
  531. /**
  532. * Extract the HTTP message from a response
  533. *
  534. * @param string $responseString
  535. * @return string
  536. */
  537. public static function extractMessage($responseString)
  538. {
  539. preg_match("|^HTTP/[\d\.x]+ \d+ ([^\r\n]+)|", $responseString, $m);
  540. if (isset($m[1])) {
  541. return $m[1];
  542. } else {
  543. return false;
  544. }
  545. }
  546. /**
  547. * Extract the HTTP version from a response
  548. *
  549. * @param string $responseString
  550. * @return string
  551. */
  552. public static function extractVersion($responseString)
  553. {
  554. preg_match("|^HTTP/([\d\.x]+) \d+|", $responseString, $m);
  555. if (isset($m[1])) {
  556. return $m[1];
  557. } else {
  558. return false;
  559. }
  560. }
  561. /**
  562. * Extract the headers from a response string
  563. *
  564. * @param string $responseString
  565. * @return array
  566. */
  567. public static function extractHeaders($responseString)
  568. {
  569. $headers = array();
  570. // First, split body and headers
  571. $parts = preg_split('|(?:\r?\n){2}|m', $responseString, 2);
  572. if (! $parts[0]) return $headers;
  573. // Split headers part to lines
  574. $lines = explode("\n", $parts[0]);
  575. unset($parts);
  576. $last_header = null;
  577. foreach($lines as $line) {
  578. $line = trim($line, "\r\n");
  579. if ($line == "") break;
  580. if (preg_match("|^([\w-]+):\s+(.+)|", $line, $m)) {
  581. unset($last_header);
  582. $h_name = strtolower($m[1]);
  583. $h_value = $m[2];
  584. if (isset($headers[$h_name])) {
  585. if (! is_array($headers[$h_name])) {
  586. $headers[$h_name] = array($headers[$h_name]);
  587. }
  588. $headers[$h_name][] = $h_value;
  589. } else {
  590. $headers[$h_name] = $h_value;
  591. }
  592. $last_header = $h_name;
  593. } elseif (preg_match("|^\s+(.+)$|", $line, $m) && $last_header !== null) {
  594. if (is_array($headers[$last_header])) {
  595. end($headers[$last_header]);
  596. $last_header_key = key($headers[$last_header]);
  597. $headers[$last_header][$last_header_key] .= $m[1];
  598. } else {
  599. $headers[$last_header] .= $m[1];
  600. }
  601. }
  602. }
  603. return $headers;
  604. }
  605. /**
  606. * Extract the body from a response string
  607. *
  608. * @param string $response_str
  609. * @return string
  610. */
  611. public static function extractBody($responseString)
  612. {
  613. $parts = preg_split('|(?:\r?\n){2}|m', $responseString, 2);
  614. if (isset($parts[1])) {
  615. return $parts[1];
  616. }
  617. return '';
  618. }
  619. /**
  620. * Create a new AEUtilAzureHttpResponse object from a string
  621. *
  622. * @param string $response_str
  623. * @return AEUtilAzureHttpResponse
  624. */
  625. public static function fromString($response_str)
  626. {
  627. $code = self::extractCode($response_str);
  628. $headers = self::extractHeaders($response_str);
  629. $body = self::extractBody($response_str);
  630. $version = self::extractVersion($response_str);
  631. $message = self::extractMessage($response_str);
  632. return new AEUtilAzureHttpResponse($code, $headers, $body, $version, $message);
  633. }
  634. }
  635. /**
  636. * @category Microsoft
  637. * @package Microsoft_WindowsAzure
  638. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  639. * @license http://phpazure.codeplex.com/license
  640. */
  641. abstract class AEUtilAzureCredentials
  642. {
  643. /**
  644. * Development storage account and key
  645. */
  646. const DEVSTORE_ACCOUNT = "devstoreaccount1";
  647. const DEVSTORE_KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==";
  648. /**
  649. * HTTP header prefixes
  650. */
  651. const PREFIX_PROPERTIES = "x-ms-prop-";
  652. const PREFIX_METADATA = "x-ms-meta-";
  653. const PREFIX_STORAGE_HEADER = "x-ms-";
  654. /**
  655. * Permissions
  656. */
  657. const PERMISSION_READ = "r";
  658. const PERMISSION_WRITE = "w";
  659. const PERMISSION_DELETE = "d";
  660. const PERMISSION_LIST = "l";
  661. /**
  662. * Account name for Windows Azure
  663. *
  664. * @var string
  665. */
  666. protected $_accountName = '';
  667. /**
  668. * Account key for Windows Azure
  669. *
  670. * @var string
  671. */
  672. protected $_accountKey = '';
  673. /**
  674. * Use path-style URI's
  675. *
  676. * @var boolean
  677. */
  678. protected $_usePathStyleUri = false;
  679. /**
  680. * Creates a new AEUtilAzureCredentials instance
  681. *
  682. * @param string $accountName Account name for Windows Azure
  683. * @param string $accountKey Account key for Windows Azure
  684. * @param boolean $usePathStyleUri Use path-style URI's
  685. */
  686. public function __construct($accountName = AEUtilAzureCredentials::DEVSTORE_ACCOUNT, $accountKey = AEUtilAzureCredentials::DEVSTORE_KEY, $usePathStyleUri = false)
  687. {
  688. $this->_accountName = $accountName;
  689. $this->_accountKey = base64_decode($accountKey);
  690. $this->_usePathStyleUri = $usePathStyleUri;
  691. }
  692. /**
  693. * Set account name for Windows Azure
  694. *
  695. * @param string $value
  696. */
  697. public function setAccountName($value = AEUtilAzureCredentials::DEVSTORE_ACCOUNT)
  698. {
  699. $this->_accountName = $value;
  700. }
  701. /**
  702. * Set account key for Windows Azure
  703. *
  704. * @param string $value
  705. */
  706. public function setAccountkey($value = AEUtilAzureCredentials::DEVSTORE_KEY)
  707. {
  708. $this->_accountKey = base64_decode($value);
  709. }
  710. /**
  711. * Set use path-style URI's
  712. *
  713. * @param boolean $value
  714. */
  715. public function setUsePathStyleUri($value = false)
  716. {
  717. $this->_usePathStyleUri = $value;
  718. }
  719. /**
  720. * Sign request URL with credentials
  721. *
  722. * @param string $requestUrl Request URL
  723. * @param string $resourceType Resource type
  724. * @param string $requiredPermission Required permission
  725. * @return string Signed request URL
  726. */
  727. public abstract function signRequestUrl($requestUrl = '');
  728. /**
  729. * Sign request headers with credentials
  730. *
  731. * @param string $httpVerb HTTP verb the request will use
  732. * @param string $path Path for the request
  733. * @param string $queryString Query string for the request
  734. * @param array $headers x-ms headers to add
  735. * @param boolean $forTableStorage Is the request for table storage?
  736. * @param string $resourceType Resource type
  737. * @param string $requiredPermission Required permission
  738. * @return array Array of headers
  739. */
  740. public abstract function signRequestHeaders($httpVerb = AEUtilAzureHttpTransport::VERB_GET, $path = '/', $queryString = '', $headers = null, $forTableStorage = false, $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ);
  741. /**
  742. * Prepare query string for signing
  743. *
  744. * @param string $value Original query string
  745. * @return string Query string for signing
  746. */
  747. protected function prepareQueryStringForSigning($value)
  748. {
  749. // Check for 'comp='
  750. if (strpos($value, 'comp=') === false)
  751. {
  752. // If not found, no query string needed
  753. return '';
  754. }
  755. else
  756. {
  757. // If found, make sure it is the only parameter being used
  758. if (strlen($value) > 0 && strpos($value, '?') === 0)
  759. $value = substr($value, 1);
  760. // Split parts
  761. $queryParts = explode('&', $value);
  762. foreach ($queryParts as $queryPart)
  763. {
  764. if (strpos($queryPart, 'comp=') !== false)
  765. return '?' . $queryPart;
  766. }
  767. // Should never happen...
  768. return '';
  769. }
  770. }
  771. }
  772. /**
  773. * @category Microsoft
  774. * @package Microsoft_WindowsAzure
  775. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  776. * @license http://phpazure.codeplex.com/license
  777. */
  778. class AEUtilAzureSharedKeyCredentials extends AEUtilAzureCredentials
  779. {
  780. /**
  781. * Sign request URL with credentials
  782. *
  783. * @param string $requestUrl Request URL
  784. * @param string $resourceType Resource type
  785. * @param string $requiredPermission Required permission
  786. * @return string Signed request URL
  787. */
  788. public function signRequestUrl($requestUrl = '', $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ)
  789. {
  790. return $requestUrl;
  791. }
  792. /**
  793. * Sign request headers with credentials
  794. *
  795. * @param string $httpVerb HTTP verb the request will use
  796. * @param string $path Path for the request
  797. * @param string $queryString Query string for the request
  798. * @param array $headers x-ms headers to add
  799. * @param boolean $forTableStorage Is the request for table storage?
  800. * @param string $resourceType Resource type
  801. * @param string $requiredPermission Required permission
  802. * @return array Array of headers
  803. */
  804. public function signRequestHeaders($httpVerb = AEUtilAzureHttpTransport::VERB_GET, $path = '/', $queryString = '', $headers = null, $forTableStorage = false, $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ)
  805. {
  806. // http://github.com/sriramk/winazurestorage/blob/214010a2f8931bac9c96dfeb337d56fe084ca63b/winazurestorage.py
  807. // Determine path
  808. if ($this->_usePathStyleUri)
  809. $path = substr($path, strpos($path, '/'));
  810. // Determine query
  811. $queryString = $this->prepareQueryStringForSigning($queryString);
  812. // Canonicalized headers
  813. $canonicalizedHeaders = array();
  814. // Request date
  815. $requestDate = '';
  816. if (isset($headers[self::PREFIX_STORAGE_HEADER . 'date']))
  817. {
  818. $requestDate = $headers[self::PREFIX_STORAGE_HEADER . 'date'];
  819. }
  820. else
  821. {
  822. $requestDate = gmdate('D, d M Y H:i:s', time()) . ' GMT'; // RFC 1123
  823. $canonicalizedHeaders[] = self::PREFIX_STORAGE_HEADER . 'date:' . $requestDate;
  824. }
  825. // Build canonicalized headers
  826. if (!is_null($headers))
  827. {
  828. foreach ($headers as $header => $value) {
  829. if (is_bool($value))
  830. $value = $value === true ? 'True' : 'False';
  831. $headers[$header] = $value;
  832. if (substr($header, 0, strlen(self::PREFIX_STORAGE_HEADER)) == self::PREFIX_STORAGE_HEADER)
  833. $canonicalizedHeaders[] = strtolower($header) . ':' . $value;
  834. }
  835. }
  836. sort($canonicalizedHeaders);
  837. // Build canonicalized resource string
  838. $canonicalizedResource = '/' . $this->_accountName;
  839. if ($this->_usePathStyleUri)
  840. $canonicalizedResource .= '/' . $this->_accountName;
  841. $canonicalizedResource .= $path;
  842. if ($queryString !== '')
  843. $canonicalizedResource .= $queryString;
  844. // Create string to sign
  845. $stringToSign = array();
  846. $stringToSign[] = strtoupper($httpVerb); // VERB
  847. $stringToSign[] = ""; // Content-MD5
  848. $stringToSign[] = ""; // Content-Type
  849. $stringToSign[] = "";
  850. // Date already in $canonicalizedHeaders
  851. // $stringToSign[] = self::PREFIX_STORAGE_HEADER . 'date:' . $requestDate; // Date
  852. if (!$forTableStorage && count($canonicalizedHeaders) > 0)
  853. $stringToSign[] = implode("\n", $canonicalizedHeaders); // Canonicalized headers
  854. $stringToSign[] = $canonicalizedResource; // Canonicalized resource
  855. $stringToSign = implode("\n", $stringToSign);
  856. $signString = base64_encode(hash_hmac('sha256', $stringToSign, $this->_accountKey, true));
  857. // Sign request
  858. $headers[self::PREFIX_STORAGE_HEADER . 'date'] = $requestDate;
  859. $headers['Authorization'] = 'SharedKey ' . $this->_accountName . ':' . $signString;
  860. // Return headers
  861. return $headers;
  862. }
  863. }
  864. /**
  865. * @category Microsoft
  866. * @package Microsoft_WindowsAzure
  867. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  868. * @license http://phpazure.codeplex.com/license
  869. */
  870. class AEUtilAzureSharedSignatureCredentials extends AEUtilAzureCredentials
  871. {
  872. /**
  873. * Permission set
  874. *
  875. * @var array
  876. */
  877. protected $_permissionSet = array();
  878. /**
  879. * Creates a new AEUtilAzureSharedSignatureCredentials instance
  880. *
  881. * @param string $accountName Account name for Windows Azure
  882. * @param string $accountKey Account key for Windows Azure
  883. * @param boolean $usePathStyleUri Use path-style URI's
  884. * @param array $permissionSet Permission set
  885. */
  886. public function __construct($accountName = AEUtilAzureCredentials::DEVSTORE_ACCOUNT, $accountKey = AEUtilAzureCredentials::DEVSTORE_KEY, $usePathStyleUri = false, $permissionSet = array())
  887. {
  888. parent::__construct($accountName, $accountKey, $usePathStyleUri);
  889. $this->_permissionSet = $permissionSet;
  890. }
  891. /**
  892. * Get permission set
  893. *
  894. * @return array
  895. */
  896. public function getPermissionSet()
  897. {
  898. return $this->_permissionSet;
  899. }
  900. /**
  901. * Set permisison set
  902. *
  903. * Warning: fine-grained permissions should be added prior to coarse-grained permissions.
  904. * For example: first add blob permissions, end with container-wide permissions.
  905. *
  906. * Warning: the signed access signature URL must match the account name of the
  907. * AEUtilAzureSharedSignatureCredentials instance
  908. *
  909. * @param array $value Permission set
  910. */
  911. public function setPermissionSet($value = array())
  912. {
  913. foreach ($value as $url)
  914. {
  915. if (strpos($url, $this->_accountName) === false)
  916. throw new AEUtilAzureAPIException('The permission set can only contain URLs for the account name specified in the AEUtilAzureSharedSignatureCredentials instance.');
  917. }
  918. $this->_permissionSet = $value;
  919. }
  920. /**
  921. * Create signature
  922. *
  923. * @param string $path Path for the request
  924. * @param string $resource Signed resource - container (c) - blob (b)
  925. * @param string $permissions Signed permissions - read (r), write (w), delete (d) and list (l)
  926. * @param string $start The time at which the Shared Access Signature becomes valid.
  927. * @param string $expiry The time at which the Shared Access Signature becomes invalid.
  928. * @param string $identifier Signed identifier
  929. * @return string
  930. */
  931. public function createSignature($path = '/', $resource = 'b', $permissions = 'r', $start = '', $expiry = '', $identifier = '')
  932. {
  933. // Determine path
  934. if ($this->_usePathStyleUri)
  935. $path = substr($path, strpos($path, '/'));
  936. // Add trailing slash to $path
  937. if (substr($path, 0, 1) !== '/')
  938. $path = '/' . $path;
  939. // Build canonicalized resource string
  940. $canonicalizedResource = '/' . $this->_accountName;
  941. if ($this->_usePathStyleUri)
  942. $canonicalizedResource .= '/' . $this->_accountName;
  943. $canonicalizedResource .= $path;
  944. // Create string to sign
  945. $stringToSign = array();
  946. $stringToSign[] = $permissions;
  947. $stringToSign[] = $start;
  948. $stringToSign[] = $expiry;
  949. $stringToSign[] = $canonicalizedResource;
  950. $stringToSign[] = $identifier;
  951. $stringToSign = implode("\n", $stringToSign);
  952. $signature = base64_encode(hash_hmac('sha256', $stringToSign, $this->_accountKey, true));
  953. return $signature;
  954. }
  955. /**
  956. * Create signed query string
  957. *
  958. * @param string $path Path for the request
  959. * @param string $queryString Query string for the request
  960. * @param string $resource Signed resource - container (c) - blob (b)
  961. * @param string $permissions Signed permissions - read (r), write (w), delete (d) and list (l)
  962. * @param string $start The time at which the Shared Access Signature becomes valid.
  963. * @param string $expiry The time at which the Shared Access Signature becomes invalid.
  964. * @param string $identifier Signed identifier
  965. * @return string
  966. */
  967. public function createSignedQueryString($path = '/', $queryString = '', $resource = 'b', $permissions = 'r', $start = '', $expiry = '', $identifier = '')
  968. {
  969. // Parts
  970. $parts = array();
  971. if ($start !== '')
  972. $parts[] = 'st=' . urlencode($start);
  973. $parts[] = 'se=' . urlencode($expiry);
  974. $parts[] = 'sr=' . $resource;
  975. $parts[] = 'sp=' . $permissions;
  976. if ($identifier !== '')
  977. $parts[] = 'si=' . urlencode($identifier);
  978. $parts[] = 'sig=' . urlencode($this->createSignature($path, $resource, $permissions, $start, $expiry, $identifier));
  979. // Assemble parts and query string
  980. if ($queryString != '')
  981. $queryString .= '&';
  982. $queryString .= implode('&', $parts);
  983. return $queryString;
  984. }
  985. /**
  986. * Permission matches request?
  987. *
  988. * @param string $permissionUrl Permission URL
  989. * @param string $requestUrl Request URL
  990. * @param string $resourceType Resource type
  991. * @param string $requiredPermission Required permission
  992. * @return string Signed request URL
  993. */
  994. public function permissionMatchesRequest($permissionUrl = '', $requestUrl = '', $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ)
  995. {
  996. // Build requirements
  997. $requiredResourceType = $resourceType;
  998. if ($requiredResourceType == AEUtilAzureStorage::RESOURCE_BLOB)
  999. $requiredResourceType .= AEUtilAzureStorage::RESOURCE_CONTAINER;
  1000. // Parse permission url
  1001. $parsedPermissionUrl = parse_url($permissionUrl);
  1002. // Parse permission properties
  1003. $permissionParts = explode('&', $parsedPermissionUrl['query']);
  1004. // Parse request url
  1005. $parsedRequestUrl = parse_url($requestUrl);
  1006. // Check if permission matches request
  1007. $matches = true;
  1008. foreach ($permissionParts as $part)
  1009. {
  1010. list($property, $value) = explode('=', $part, 2);
  1011. if ($property == 'sr')
  1012. {
  1013. $matches = $matches && (strpbrk($value, $requiredResourceType) !== false);
  1014. }
  1015. if ($property == 'sp')
  1016. {
  1017. $matches = $matches && (strpbrk($value, $requiredPermission) !== false);
  1018. }
  1019. }
  1020. // Ok, but... does the resource match?
  1021. $matches = $matches && (strpos($parsedRequestUrl['path'], $parsedPermissionUrl['path']) !== false);
  1022. // Return
  1023. return $matches;
  1024. }
  1025. /**
  1026. * Sign request URL with credentials
  1027. *
  1028. * @param string $requestUrl Request URL
  1029. * @param string $resourceType Resource type
  1030. * @param string $requiredPermission Required permission
  1031. * @return string Signed request URL
  1032. */
  1033. public function signRequestUrl($requestUrl = '', $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ)
  1034. {
  1035. // Look for a matching permission
  1036. foreach ($this->getPermissionSet() as $permittedUrl)
  1037. {
  1038. if ($this->permissionMatchesRequest($permittedUrl, $requestUrl, $resourceType, $requiredPermission))
  1039. {
  1040. // This matches, append signature data
  1041. $parsedPermittedUrl = parse_url($permittedUrl);
  1042. if (strpos($requestUrl, '?') === false)
  1043. $requestUrl .= '?';
  1044. else
  1045. $requestUrl .= '&';
  1046. $requestUrl .= $parsedPermittedUrl['query'];
  1047. // Return url
  1048. return $requestUrl;
  1049. }
  1050. }
  1051. // Return url, will be unsigned...
  1052. return $requestUrl;
  1053. }
  1054. /**
  1055. * Sign request with credentials
  1056. *
  1057. * @param string $httpVerb HTTP verb the request will use
  1058. * @param string $path Path for the request
  1059. * @param string $queryString Query string for the request
  1060. * @param array $headers x-ms headers to add
  1061. * @param boolean $forTableStorage Is the request for table storage?
  1062. * @param string $resourceType Resource type
  1063. * @param string $requiredPermission Required permission
  1064. * @return array Array of headers
  1065. */
  1066. public function signRequestHeaders($httpVerb = AEUtilAzureHttpTransport::VERB_GET, $path = '/', $queryString = '', $headers = null, $forTableStorage = false, $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ)
  1067. {
  1068. return $headers;
  1069. }
  1070. }
  1071. /**
  1072. * @category Microsoft
  1073. * @package Microsoft_WindowsAzure
  1074. * @subpackage RetryPolicy
  1075. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  1076. * @license http://phpazure.codeplex.com/license
  1077. */
  1078. abstract class AEUtilAzureRetryPolicy
  1079. {
  1080. /**
  1081. * Execute function under retry policy
  1082. *
  1083. * @param string|array $function Function to execute
  1084. * @param array $parameters Parameters for function call
  1085. * @return mixed
  1086. */
  1087. public abstract function execute($function, $parameters = array());
  1088. /**
  1089. * Create a AEUtilAzureNoRetryPolicy instance
  1090. *
  1091. * @return AEUtilAzureNoRetryPolicy
  1092. */
  1093. public static function noRetry()
  1094. {
  1095. return new AEUtilAzureNoRetryPolicy();
  1096. }
  1097. /**
  1098. * Create a AEUtilAzureRetryNPolicy instance
  1099. *
  1100. * @param int $count Number of retries
  1101. * @param int $intervalBetweenRetries Interval between retries (in milliseconds)
  1102. * @return AEUtilAzureRetryNPolicy
  1103. */
  1104. public static function retryN($count = 1, $intervalBetweenRetries = 0)
  1105. {
  1106. return new AEUtilAzureRetryNPolicy($count, $intervalBetweenRetries);
  1107. }
  1108. }
  1109. /**
  1110. * @category Microsoft
  1111. * @package Microsoft_WindowsAzure
  1112. * @subpackage RetryPolicy
  1113. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  1114. * @license http://phpazure.codeplex.com/license
  1115. */
  1116. class AEUtilAzureNoRetryPolicy extends AEUtilAzureRetryPolicy
  1117. {
  1118. /**
  1119. * Execute function under retry policy
  1120. *
  1121. * @param string|array $function Function to execute
  1122. * @param array $parameters Parameters for function call
  1123. * @return mixed
  1124. */
  1125. public function execute($function, $parameters = array())
  1126. {
  1127. $returnValue = null;
  1128. try
  1129. {
  1130. $returnValue = call_user_func_array($function, $parameters);
  1131. return $returnValue;
  1132. }
  1133. catch (Exception $ex)
  1134. {
  1135. throw $ex;
  1136. }
  1137. }
  1138. }
  1139. /**
  1140. * @category Microsoft
  1141. * @package Microsoft_WindowsAzure
  1142. * @subpackage RetryPolicy
  1143. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  1144. * @license http://phpazure.codeplex.com/license
  1145. */
  1146. class AEUtilAzureRetryNPolicy extends AEUtilAzureRetryPolicy
  1147. {
  1148. /**
  1149. * Number of retries
  1150. *
  1151. * @var int
  1152. */
  1153. protected $_retryCount = 1;
  1154. /**
  1155. * Interval between retries (in milliseconds)
  1156. *
  1157. * @var int
  1158. */
  1159. protected $_retryInterval = 0;
  1160. /**
  1161. * Constructor
  1162. *
  1163. * @param int $count Number of retries
  1164. * @param int $intervalBetweenRetries Interval between retries (in milliseconds)
  1165. */
  1166. public function __construct($count = 1, $intervalBetweenRetries = 0)
  1167. {
  1168. $this->_retryCount = $count;
  1169. $this->_retryInterval = $intervalBetweenRetries;
  1170. }
  1171. /**
  1172. * Execute function under retry policy
  1173. *
  1174. * @param string|array $function Function to execute
  1175. * @param array $parameters Parameters for function call
  1176. * @return mixed
  1177. */
  1178. public function execute($function, $parameters = array())
  1179. {
  1180. $returnValue = null;
  1181. for ($retriesLeft = $this->_retryCount; $retriesLeft >= 0; --$retriesLeft)
  1182. {
  1183. try
  1184. {
  1185. $returnValue = call_user_func_array($function, $parameters);
  1186. return $returnValue;
  1187. }
  1188. catch (Exception $ex)
  1189. {
  1190. if ($retriesLeft == 1)
  1191. throw new AEUtilAzureRetryPolicyException("Exceeded retry count of " . $this->_retryCount . ". " . $ex->getMessage());
  1192. usleep($this->_retryInterval * 1000);
  1193. }
  1194. }
  1195. }
  1196. }
  1197. /**
  1198. * @category Microsoft
  1199. * @package Microsoft_WindowsAzure
  1200. * @subpackage Storage
  1201. * @copyright Copyright (c) 2009, RealDolmen (http://www.realdolmen.com)
  1202. * @license http://phpazure.codeplex.com/license
  1203. */
  1204. class AEUtilAzureStorage
  1205. {
  1206. /**
  1207. * Development storage URLS
  1208. */
  1209. const URL_DEV_BLOB = "127.0.0.1:10000";
  1210. const URL_DEV_QUEUE = "127.0.0.1:10001";
  1211. const URL_DEV_TABLE = "127.0.0.1:10002";
  1212. /**
  1213. * Live storage URLS
  1214. */
  1215. const URL_CLOUD_BLOB = "blob.core.windows.net";
  1216. const URL_CLOUD_QUEUE = "queue.core.windows.net";
  1217. const URL_CLOUD_TABLE = "table.core.windows.net";
  1218. /**
  1219. * Resource types
  1220. */
  1221. const RESOURCE_UNKNOWN = "unknown";
  1222. const RESOURCE_CONTAINER = "c";
  1223. const RESOURCE_BLOB = "b";
  1224. const RESOURCE_TABLE = "t";
  1225. const RESOURCE_ENTITY = "e";
  1226. const RESOURCE_QUEUE = "q";
  1227. /**
  1228. * Current API version
  1229. *
  1230. * @var string
  1231. */
  1232. protected $_apiVersion = '2009-04-14';
  1233. /**
  1234. * Storage host name
  1235. *
  1236. * @var string
  1237. */
  1238. protected $_host = '';
  1239. /**
  1240. * Account name for Windows Azure
  1241. *
  1242. * @var string
  1243. */
  1244. protected $_accountName = '';
  1245. /**
  1246. * Account key for Windows Azure
  1247. *
  1248. * @var string
  1249. */
  1250. protected $_accountKey = '';
  1251. /**
  1252. * Use path-style URI's
  1253. *
  1254. * @var boolean
  1255. */
  1256. protected $_usePathStyleUri = false;
  1257. /**
  1258. * AEUtilAzureCredentials instance
  1259. *
  1260. * @var AEUtilAzureCredentials
  1261. */
  1262. protected $_credentials = null;
  1263. /**
  1264. * AEUtilAzureRetryPolicy instance
  1265. *
  1266. * @var AEUtilAzureRetryPolicy
  1267. */
  1268. protected $_retryPolicy = null;
  1269. /**
  1270. * Use proxy?
  1271. *
  1272. * @var boolean
  1273. */
  1274. protected $_useProxy = false;
  1275. /**
  1276. * Proxy url
  1277. *
  1278. * @var string
  1279. */
  1280. protected $_proxyUrl = '';
  1281. /**
  1282. * Proxy port
  1283. *
  1284. * @var int
  1285. */
  1286. protected $_proxyPort = 80;
  1287. /**
  1288. * Proxy credentials
  1289. *
  1290. * @var string
  1291. */
  1292. protected $_proxyCredentials = '';
  1293. /**
  1294. * Creates a new AEUtilAzureStorage instance
  1295. *
  1296. * @param string $host Storage host name
  1297. * @param string $accountName Account name for Windows Azure
  1298. * @param string $accountKey Account key for Windows Azure
  1299. * @param boolean $usePathStyleUri Use path-style URI's
  1300. * @param AEUtilAzureRetryPolicy $retryPolicy Retry policy to use when making requests
  1301. */
  1302. public function __construct($host = self::URL_DEV_BLOB, $accountName = AEUtilAzureCredentials::DEVSTORE_ACCOUNT, $accountKey = AEUtilAzureCredentials::DEVSTORE_KEY, $usePathStyleUri = false, AEUtilAzureRetryPolicy $retryPolicy = null)
  1303. {
  1304. $this->_host = $host;
  1305. $this->_accountName = $accountName;
  1306. $this->_accountKey = $accountKey;
  1307. $this->_usePathStyleUri = $usePathStyleUri;
  1308. // Using local storage?
  1309. if (!$this->_usePathStyleUri && ($this->_host == self::URL_DEV_BLOB || $this->_host == self::URL_DEV_QUEUE || $this->_host == self::URL_DEV_TABLE)) // Local storage
  1310. $this->_usePathStyleUri = true;
  1311. if (is_null($this->_credentials))
  1312. $this->_credentials = new AEUtilAzureSharedKeyCredentials($this->_accountName, $this->_accountKey, $this->_usePathStyleUri);
  1313. $this->_retryPolicy = $retryPolicy;
  1314. if (is_null($this->_retryPolicy))
  1315. $this->_retryPolicy = AEUtilAzureRetryPolicy::noRetry();
  1316. }
  1317. /**
  1318. * Set retry policy to use when making requests
  1319. *
  1320. * @param AEUtilAzureRetryPolicy $retryPolicy Retry policy to use when making requests
  1321. */
  1322. public function setRetryPolicy(AEUtilAzureRetryPolicy $retryPolicy = null)
  1323. {
  1324. $this->_retryPolicy = $retryPolicy;
  1325. if (is_null($this->_retryPolicy))
  1326. $this->_retryPolicy = AEUtilAzureRetryPolicy::noRetry();
  1327. }
  1328. /**
  1329. * Set proxy
  1330. *
  1331. * @param boolean $useProxy Use proxy?
  1332. * @param string $proxyUrl Proxy URL
  1333. * @param int $proxyPort Proxy port
  1334. * @param string $proxyCredentials Proxy credentials
  1335. */
  1336. public function setProxy($useProxy = false, $proxyUrl = '', $proxyPort = 80, $proxyCredentials = '')
  1337. {
  1338. $this->_useProxy = $useProxy;
  1339. $this->_proxyUrl = $proxyUrl;
  1340. $this->_proxyPort = $proxyPort;
  1341. $this->_proxyCredentials = $proxyCredentials;
  1342. }
  1343. /**
  1344. * Returns the Windows Azure account name
  1345. *
  1346. * @return string
  1347. */
  1348. public function getAccountName()
  1349. {
  1350. return $this->_accountName;
  1351. }
  1352. /**
  1353. * Get base URL for creating requests
  1354. *
  1355. * @return string
  1356. */
  1357. public function getBaseUrl()
  1358. {
  1359. if ($this->_usePathStyleUri)
  1360. return 'http://' . $this->_host . '/' . $this->_accountName;
  1361. else
  1362. return 'http://' . $this->_accountName . '.' . $this->_host;
  1363. }
  1364. /**
  1365. * Set AEUtilAzureCredentials instance
  1366. *
  1367. * @param AEUtilAzureCredentials $credentials AEUtilAzureCredentials instance to use for request signing.
  1368. */
  1369. public function setCredentials(AEUtilAzureCredentials $credentials)
  1370. {
  1371. $this->_credentials = $credentials;
  1372. $this->_credentials->setAccountName($this->_accountName);
  1373. $this->_credentials->setAccountkey($this->_accountKey);
  1374. $this->_credentials->setUsePathStyleUri($this->_usePathStyleUri);
  1375. }
  1376. /**
  1377. * Get AEUtilAzureCredentials instance
  1378. *
  1379. * @return AEUtilAzureCredentials
  1380. */
  1381. public function getCredentials()
  1382. {
  1383. return $this->_credentials;
  1384. }
  1385. /**
  1386. * Perform request using AEUtilAzureHttpTransport channel
  1387. *
  1388. * @param string $path Path
  1389. * @param string $queryString Query string
  1390. * @param string $httpVerb HTTP verb the request will use
  1391. * @param array $headers x-ms headers to add
  1392. * @param boolean $forTableStorage Is the request for table storage?
  1393. * @param mixed $rawData Optional RAW HTTP data to be sent over the wire
  1394. * @param string $resourceType Resource type
  1395. * @param string $requiredPermission Required permission
  1396. * @return AEUtilAzureHttpResponse
  1397. */
  1398. protected function performRequest($path = '/', $queryString = '', $httpVerb = AEUtilAzureHttpTransport::VERB_GET, $headers = array(), $forTableStorage = false, $rawData = null, $resourceType = AEUtilAzureStorage::RESOURCE_UNKNOWN, $requiredPermission = AEUtilAzureCredentials::PERMISSION_READ)
  1399. {
  1400. // Clean path
  1401. if (strpos($path, '/') !== 0)
  1402. $path = '/' . $path;
  1403. // Clean headers
  1404. if (is_null($headers))
  1405. $headers = array();
  1406. // Add version header
  1407. $headers['x-ms-version'] = $this->_apiVersion;
  1408. // URL encoding
  1409. $path = self::urlencode($path);
  1410. $queryString = self::urlencode($queryString);
  1411. // Generate URL and sign request
  1412. $requestUrl = $this->_credentials->signRequestUrl($this->getBaseUrl() . $path . $queryString, $resourceType, $requiredPermission);
  1413. $requestHeaders = $this->_credentials->signRequestHeaders($httpVerb, $path, $queryString, $headers, $forTableStorage, $resourceType, $requiredPermission);
  1414. $requestClient = AEUtilAzureHttpTransport::createChannel();
  1415. if ($this->_useProxy)
  1416. {
  1417. $requestClient->setProxy($this->_useProxy, $this->_proxyUrl, $this->_proxyPort, $this->_proxyCredentials);
  1418. }
  1419. $response = $this->_retryPolicy->execute(
  1420. array($requestClient, 'request'),
  1421. array($httpVerb, $requestUrl, array(), $requestHeaders, $rawData)
  1422. );
  1423. $requestClient = null;
  1424. unset($requestClient);
  1425. return $response;
  1426. }
  1427. /**
  1428. * Builds a query string from an array of elements
  1429. *
  1430. * @param array Array of elements
  1431. * @return string Assembled query string
  1432. */
  1433. public static function createQueryStringFromArray($queryString)
  1434. {
  1435. return count($queryString) > 0 ? '?' . implode('&', $queryString) : '';
  1436. }
  1437. /**
  1438. * Parse result from AEUtilAzureHttpResponse
  1439. *
  1440. * @param AEUtilAzureHttpResponse $response Response from HTTP call
  1441. * @return object
  1442. * @throws AEUtilAzureAPIException
  1443. */
  1444. protected function parseResponse(AEUtilAzureHttpResponse $response = null)
  1445. {
  1446. if (is_null($response))
  1447. throw new AEUtilAzureAPIException('Response should not be null.');
  1448. $xml = @simplexml_load_string($response->getBody());
  1449. if ($xml !== false)
  1450. {
  1451. // Fetch all namespaces
  1452. $namespaces = array_merge($xml->getNamespaces(true), $xml->getDocNamespaces(true));
  1453. // Register all namespace prefixes
  1454. foreach ($namespaces as $prefix => $ns) {
  1455. if ($prefix != '')
  1456. $xml->registerXPathNamespace($p

Large files files are truncated, but you can click here to view the full file