PageRenderTime 90ms CodeModel.GetById 2ms app.highlight 76ms RepoModel.GetById 1ms app.codeStats 0ms

/Zend/Service/WindowsAzure/Storage/Blob.php

http://grupal.googlecode.com/
PHP | 2142 lines | 1334 code | 203 blank | 605 comment | 293 complexity | bbe524e7b9c75018ac3124745dfd78cf MD5 | raw file
   1<?php
   2/**
   3 * Zend Framework
   4 *
   5 * LICENSE
   6 *
   7 * This source file is subject to the new BSD license that is bundled
   8 * with this package in the file LICENSE.txt.
   9 * It is also available through the world-wide-web at this URL:
  10 * http://framework.zend.com/license/new-bsd
  11 * If you did not receive a copy of the license and are unable to
  12 * obtain it through the world-wide-web, please send an email
  13 * to license@zend.com so we can send you a copy immediately.
  14 *
  15 * @category   Zend
  16 * @package    Zend_Service_WindowsAzure
  17 * @subpackage Storage
  18 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19 * @license    http://todo     name_todo
  20 * @version    $Id: Blob.php 24697 2012-03-23 13:11:04Z ezimuel $
  21 */
  22
  23/**
  24 * @see Zend_Service_WindowsAzure_Storage
  25 */
  26require_once 'Zend/Service/WindowsAzure/Storage.php';
  27
  28/**
  29 * @see Zend_Service_WindowsAzure_Storage_BlobInstance
  30 */
  31require_once 'Zend/Service/WindowsAzure/Storage/BlobInstance.php';
  32
  33/**
  34 * @see Zend_Service_WindowsAzure_Storage_BlobContainer
  35 */
  36require_once 'Zend/Service/WindowsAzure/Storage/BlobContainer.php';
  37
  38/**
  39 * @see Zend_Service_WindowsAzure_Storage_PageRegionInstance
  40 */
  41require_once 'Zend/Service/WindowsAzure/Storage/PageRegionInstance.php';
  42
  43/**
  44 * @see Zend_Service_WindowsAzure_Storage_LeaseInstance
  45 */
  46require_once 'Zend/Service/WindowsAzure/Storage/LeaseInstance.php';
  47
  48/**
  49 * @see Zend_Service_WindowsAzure_Storage_Blob_Stream
  50 */
  51require_once 'Zend/Service/WindowsAzure/Storage/Blob/Stream.php';
  52
  53/**
  54 * @see Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
  55 */
  56require_once 'Zend/Service/WindowsAzure/Credentials/SharedAccessSignature.php';
  57/**
  58 * @category   Zend
  59 * @package    Zend_Service_WindowsAzure
  60 * @subpackage Storage
  61 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  62 * @license    http://framework.zend.com/license/new-bsd     New BSD License
  63 */
  64class Zend_Service_WindowsAzure_Storage_Blob extends Zend_Service_WindowsAzure_Storage
  65{
  66	/**
  67	 * ACL - Private access
  68	 */
  69	const ACL_PRIVATE = null;
  70
  71	/**
  72	 * ACL - Public access (read all blobs)
  73	 *
  74	 * @deprecated Use ACL_PUBLIC_CONTAINER or ACL_PUBLIC_BLOB instead.
  75	 */
  76	const ACL_PUBLIC = 'container';
  77	
  78	/**
  79	 * ACL - Blob Public access (read all blobs)
  80	 */
  81	const ACL_PUBLIC_BLOB = 'blob';
  82
  83	/**
  84	 * ACL - Container Public access (enumerate and read all blobs)
  85	 */
  86	const ACL_PUBLIC_CONTAINER = 'container';
  87
  88	/**
  89	 * Blob lease constants
  90	 */
  91	const LEASE_ACQUIRE = 'acquire';
  92	const LEASE_RENEW   = 'renew';
  93	const LEASE_RELEASE = 'release';
  94	const LEASE_BREAK   = 'break';
  95
  96	/**
  97	 * Maximal blob size (in bytes)
  98	 */
  99	const MAX_BLOB_SIZE = 67108864;
 100
 101	/**
 102	 * Maximal blob transfer size (in bytes)
 103	 */
 104	const MAX_BLOB_TRANSFER_SIZE = 4194304;
 105
 106	/**
 107	 * Blob types
 108	 */
 109	const BLOBTYPE_BLOCK = 'BlockBlob';
 110	const BLOBTYPE_PAGE  = 'PageBlob';
 111
 112	/**
 113	 * Put page write options
 114	 */
 115	const PAGE_WRITE_UPDATE = 'update';
 116	const PAGE_WRITE_CLEAR  = 'clear';
 117
 118	/**
 119	 * Stream wrapper clients
 120	 *
 121	 * @var array
 122	 */
 123	protected static $_wrapperClients = array();
 124
 125	/**
 126	 * SharedAccessSignature credentials
 127	 *
 128	 * @var Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
 129	 */
 130	protected $_sharedAccessSignatureCredentials = null;
 131
 132	/**
 133	 * Creates a new Zend_Service_WindowsAzure_Storage_Blob instance
 134	 *
 135	 * @param string $host Storage host name
 136	 * @param string $accountName Account name for Windows Azure
 137	 * @param string $accountKey Account key for Windows Azure
 138	 * @param boolean $usePathStyleUri Use path-style URI's
 139	 * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
 140	 */
 141	public function __construct($host = Zend_Service_WindowsAzure_Storage::URL_DEV_BLOB, $accountName = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_ACCOUNT, $accountKey = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_KEY, $usePathStyleUri = false, Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null)
 142	{
 143		parent::__construct($host, $accountName, $accountKey, $usePathStyleUri, $retryPolicy);
 144
 145		// API version
 146		$this->_apiVersion = '2009-09-19';
 147
 148		// SharedAccessSignature credentials
 149		$this->_sharedAccessSignatureCredentials = new Zend_Service_WindowsAzure_Credentials_SharedAccessSignature($accountName, $accountKey, $usePathStyleUri);
 150	}
 151
 152	/**
 153	 * Check if a blob exists
 154	 *
 155	 * @param string $containerName Container name
 156	 * @param string $blobName      Blob name
 157	 * @param string $snapshotId    Snapshot identifier
 158	 * @return boolean
 159	 */
 160	public function blobExists($containerName = '', $blobName = '', $snapshotId = null)
 161	{
 162		if ($containerName === '') {
 163			require_once 'Zend/Service/WindowsAzure/Exception.php';
 164			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 165		}
 166		if (!self::isValidContainerName($containerName)) {
 167			require_once 'Zend/Service/WindowsAzure/Exception.php';
 168			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 169		}
 170		if ($blobName === '') {
 171			require_once 'Zend/Service/WindowsAzure/Exception.php';
 172			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
 173		}
 174
 175		// Get blob instance
 176		try {
 177			$this->getBlobInstance($containerName, $blobName, $snapshotId);
 178		} catch (Zend_Service_WindowsAzure_Exception $e) {
 179			return false;
 180		}
 181
 182		return true;
 183	}
 184
 185	/**
 186	 * Check if a container exists
 187	 *
 188	 * @param string $containerName Container name
 189	 * @return boolean
 190	 */
 191	public function containerExists($containerName = '')
 192	{
 193		if ($containerName === '') {
 194			require_once 'Zend/Service/WindowsAzure/Exception.php';
 195			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 196		}
 197		if (!self::isValidContainerName($containerName)) {
 198			require_once 'Zend/Service/WindowsAzure/Exception.php';
 199			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 200		}
 201			
 202		// List containers
 203		$containers = $this->listContainers($containerName, 1);
 204		foreach ($containers as $container) {
 205			if ($container->Name == $containerName) {
 206				return true;
 207			}
 208		}
 209
 210		return false;
 211	}
 212
 213	/**
 214	 * Create container
 215	 *
 216	 * @param string $containerName Container name
 217	 * @param array  $metadata      Key/value pairs of meta data
 218	 * @return object Container properties
 219	 * @throws Zend_Service_WindowsAzure_Exception
 220	 */
 221	public function createContainer($containerName = '', $metadata = array())
 222	{
 223		if ($containerName === '') {
 224			require_once 'Zend/Service/WindowsAzure/Exception.php';
 225			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 226		}
 227		if (!self::isValidContainerName($containerName)) {
 228			require_once 'Zend/Service/WindowsAzure/Exception.php';
 229			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 230		}
 231		if (!is_array($metadata)) {
 232			require_once 'Zend/Service/WindowsAzure/Exception.php';
 233			throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.');
 234		}
 235			
 236		// Create metadata headers
 237		$headers = array();
 238		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
 239
 240		// Perform request
 241		$response = $this->_performRequest($containerName, '?restype=container', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 242		if ($response->isSuccessful()) {
 243			
 244			return new Zend_Service_WindowsAzure_Storage_BlobContainer(
 245			$containerName,
 246			$response->getHeader('Etag'),
 247			$response->getHeader('Last-modified'),
 248			$metadata
 249			);
 250		} else {
 251			require_once 'Zend/Service/WindowsAzure/Exception.php';
 252			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 253		}
 254	}
 255	
 256	/**
 257	 * Create container if it does not exist
 258	 *
 259	 * @param string $containerName Container name
 260	 * @param array  $metadata      Key/value pairs of meta data
 261	 * @throws Zend_Service_WindowsAzure_Exception
 262	 */
 263	public function createContainerIfNotExists($containerName = '', $metadata = array())
 264	{
 265		if (!$this->containerExists($containerName)) {
 266			$this->createContainer($containerName, $metadata);
 267		}
 268	}
 269
 270	/**
 271	 * Get container ACL
 272	 *
 273	 * @param string $containerName Container name
 274	 * @param bool   $signedIdentifiers Display only private/blob/container or display signed identifiers?
 275	 * @return string Acl, to be compared with Zend_Service_WindowsAzure_Storage_Blob::ACL_*
 276	 * @throws Zend_Service_WindowsAzure_Exception
 277	 */
 278	public function getContainerAcl($containerName = '', $signedIdentifiers = false)
 279	{
 280		if ($containerName === '') {
 281			require_once 'Zend/Service/WindowsAzure/Exception.php';
 282			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 283		}
 284		if (!self::isValidContainerName($containerName)) {
 285			require_once 'Zend/Service/WindowsAzure/Exception.php';
 286			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 287		}
 288
 289		// Perform request
 290		$response = $this->_performRequest($containerName, '?restype=container&comp=acl', Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
 291		if ($response->isSuccessful()) {
 292			if ($signedIdentifiers == false)  {
 293				// Only private/blob/container
 294				$accessType = $response->getHeader(Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-public-access');
 295				if (strtolower($accessType) == 'true') {
 296					$accessType = self::ACL_PUBLIC_CONTAINER;
 297				}
 298				return $accessType;
 299			} else {
 300				// Parse result
 301				$result = $this->_parseResponse($response);
 302				if (!$result) {
 303					return array();
 304				}
 305
 306				$entries = null;
 307				if ($result->SignedIdentifier) {
 308					if (count($result->SignedIdentifier) > 1) {
 309						$entries = $result->SignedIdentifier;
 310					} else {
 311						$entries = array($result->SignedIdentifier);
 312					}
 313				}
 314
 315				require_once 'Zend/Service/WindowsAzure/Storage/SignedIdentifier.php';
 316				// Return value
 317				$returnValue = array();
 318				foreach ($entries as $entry) {
 319					$returnValue[] = new Zend_Service_WindowsAzure_Storage_SignedIdentifier(
 320					$entry->Id,
 321					$entry->AccessPolicy ? $entry->AccessPolicy->Start ? $entry->AccessPolicy->Start : '' : '',
 322					$entry->AccessPolicy ? $entry->AccessPolicy->Expiry ? $entry->AccessPolicy->Expiry : '' : '',
 323					$entry->AccessPolicy ? $entry->AccessPolicy->Permission ? $entry->AccessPolicy->Permission : '' : ''
 324					);
 325				}
 326
 327				// Return
 328				return $returnValue;
 329			}
 330		} else {
 331			require_once 'Zend/Service/WindowsAzure/Exception.php';
 332			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 333		}
 334	}
 335
 336	/**
 337	 * Set container ACL
 338	 *
 339	 * @param string $containerName Container name
 340	 * @param bool $acl Zend_Service_WindowsAzure_Storage_Blob::ACL_*
 341	 * @param array $signedIdentifiers Signed identifiers
 342	 * @throws Zend_Service_WindowsAzure_Exception
 343	 */
 344	public function setContainerAcl($containerName = '', $acl = self::ACL_PRIVATE, $signedIdentifiers = array())
 345	{
 346		if ($containerName === '') {
 347			require_once 'Zend/Service/WindowsAzure/Exception.php';
 348			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 349		}
 350		if (!self::isValidContainerName($containerName)) {
 351			require_once 'Zend/Service/WindowsAzure/Exception.php';
 352			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 353		}
 354
 355		// Headers
 356		$headers = array();
 357
 358		// Acl specified?
 359		if ($acl != self::ACL_PRIVATE && !is_null($acl) && $acl != '') {
 360			$headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-public-access'] = $acl;
 361		}
 362
 363		// Policies
 364		$policies = null;
 365		if (is_array($signedIdentifiers) && count($signedIdentifiers) > 0) {
 366			$policies  = '';
 367			$policies .= '<?xml version="1.0" encoding="utf-8"?>' . "\r\n";
 368			$policies .= '<SignedIdentifiers>' . "\r\n";
 369			foreach ($signedIdentifiers as $signedIdentifier) {
 370				$policies .= '  <SignedIdentifier>' . "\r\n";
 371				$policies .= '    <Id>' . $signedIdentifier->Id . '</Id>' . "\r\n";
 372				$policies .= '    <AccessPolicy>' . "\r\n";
 373				if ($signedIdentifier->Start != '')
 374				$policies .= '      <Start>' . $signedIdentifier->Start . '</Start>' . "\r\n";
 375				if ($signedIdentifier->Expiry != '')
 376				$policies .= '      <Expiry>' . $signedIdentifier->Expiry . '</Expiry>' . "\r\n";
 377				if ($signedIdentifier->Permissions != '')
 378				$policies .= '      <Permission>' . $signedIdentifier->Permissions . '</Permission>' . "\r\n";
 379				$policies .= '    </AccessPolicy>' . "\r\n";
 380				$policies .= '  </SignedIdentifier>' . "\r\n";
 381			}
 382			$policies .= '</SignedIdentifiers>' . "\r\n";
 383		}
 384
 385		// Perform request
 386		$response = $this->_performRequest($containerName, '?restype=container&comp=acl', Zend_Http_Client::PUT, $headers, false, $policies, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 387		if (!$response->isSuccessful()) {
 388			require_once 'Zend/Service/WindowsAzure/Exception.php';
 389			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 390		}
 391	}
 392
 393	/**
 394	 * Get container
 395	 *
 396	 * @param string $containerName  Container name
 397	 * @return Zend_Service_WindowsAzure_Storage_BlobContainer
 398	 * @throws Zend_Service_WindowsAzure_Exception
 399	 */
 400	public function getContainer($containerName = '')
 401	{
 402		if ($containerName === '') {
 403			require_once 'Zend/Service/WindowsAzure/Exception.php';
 404			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 405		}
 406		if (!self::isValidContainerName($containerName)) {
 407			require_once 'Zend/Service/WindowsAzure/Exception.php';
 408			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 409		}
 410
 411		// Perform request
 412		$response = $this->_performRequest($containerName, '?restype=container', Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
 413		if ($response->isSuccessful()) {
 414			// Parse metadata
 415			$metadata = $this->_parseMetadataHeaders($response->getHeaders());
 416
 417			// Return container
 418			return new Zend_Service_WindowsAzure_Storage_BlobContainer(
 419			$containerName,
 420			$response->getHeader('Etag'),
 421			$response->getHeader('Last-modified'),
 422			$metadata
 423			);
 424		} else {
 425			require_once 'Zend/Service/WindowsAzure/Exception.php';
 426			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 427		}
 428	}
 429
 430	/**
 431	 * Get container metadata
 432	 *
 433	 * @param string $containerName  Container name
 434	 * @return array Key/value pairs of meta data
 435	 * @throws Zend_Service_WindowsAzure_Exception
 436	 */
 437	public function getContainerMetadata($containerName = '')
 438	{
 439		if ($containerName === '') {
 440			require_once 'Zend/Service/WindowsAzure/Exception.php';
 441			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 442		}
 443		if (!self::isValidContainerName($containerName)) {
 444			require_once 'Zend/Service/WindowsAzure/Exception.php';
 445			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 446		}
 447
 448		return $this->getContainer($containerName)->Metadata;
 449	}
 450
 451	/**
 452	 * Set container metadata
 453	 *
 454	 * Calling the Set Container Metadata operation overwrites all existing metadata that is associated with the container. It's not possible to modify an individual name/value pair.
 455	 *
 456	 * @param string $containerName      Container name
 457	 * @param array  $metadata           Key/value pairs of meta data
 458	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
 459	 * @throws Zend_Service_WindowsAzure_Exception
 460	 */
 461	public function setContainerMetadata($containerName = '', $metadata = array(), $additionalHeaders = array())
 462	{
 463		if ($containerName === '') {
 464			require_once 'Zend/Service/WindowsAzure/Exception.php';
 465			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 466		}
 467		if (!self::isValidContainerName($containerName)) {
 468			require_once 'Zend/Service/WindowsAzure/Exception.php';
 469			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 470		}
 471		if (!is_array($metadata)) {
 472			require_once 'Zend/Service/WindowsAzure/Exception.php';
 473			throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.');
 474		}
 475		if (count($metadata) == 0) {
 476			return;
 477		}
 478
 479		// Create metadata headers
 480		$headers = array();
 481		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
 482
 483		// Additional headers?
 484		foreach ($additionalHeaders as $key => $value) {
 485			$headers[$key] = $value;
 486		}
 487
 488		// Perform request
 489		$response = $this->_performRequest($containerName, '?restype=container&comp=metadata', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 490		if (!$response->isSuccessful()) {
 491			require_once 'Zend/Service/WindowsAzure/Exception.php';
 492			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 493		}
 494	}
 495
 496	/**
 497	 * Delete container
 498	 *
 499	 * @param string $containerName      Container name
 500	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
 501	 * @throws Zend_Service_WindowsAzure_Exception
 502	 */
 503	public function deleteContainer($containerName = '', $additionalHeaders = array())
 504	{
 505		if ($containerName === '') {
 506			require_once 'Zend/Service/WindowsAzure/Exception.php';
 507			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 508		}
 509		if (!self::isValidContainerName($containerName)) {
 510			require_once 'Zend/Service/WindowsAzure/Exception.php';
 511			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 512		}
 513			
 514		// Additional headers?
 515		$headers = array();
 516		foreach ($additionalHeaders as $key => $value) {
 517			$headers[$key] = $value;
 518		}
 519
 520		// Perform request
 521		$response = $this->_performRequest($containerName, '?restype=container', Zend_Http_Client::DELETE, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 522		if (!$response->isSuccessful()) {
 523			require_once 'Zend/Service/WindowsAzure/Exception.php';
 524			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 525		}
 526	}
 527
 528	/**
 529	 * List containers
 530	 *
 531	 * @param string $prefix     Optional. Filters the results to return only containers whose name begins with the specified prefix.
 532	 * @param int    $maxResults Optional. Specifies the maximum number of containers to return per call to Azure storage. This does NOT affect list size returned by this function. (maximum: 5000)
 533	 * @param string $marker     Optional string value that identifies the portion of the list to be returned with the next list operation.
 534	 * @param string $include    Optional. Include this parameter to specify that the container's metadata be returned as part of the response body. (allowed values: '', 'metadata')
 535	 * @param int    $currentResultCount Current result count (internal use)
 536	 * @return array
 537	 * @throws Zend_Service_WindowsAzure_Exception
 538	 */
 539	public function listContainers($prefix = null, $maxResults = null, $marker = null, $include = null, $currentResultCount = 0)
 540	{
 541		// Build query string
 542		$queryString = array('comp=list');
 543		if (!is_null($prefix)) {
 544			$queryString[] = 'prefix=' . $prefix;
 545		}
 546		if (!is_null($maxResults)) {
 547			$queryString[] = 'maxresults=' . $maxResults;
 548		}
 549		if (!is_null($marker)) {
 550			$queryString[] = 'marker=' . $marker;
 551		}
 552		if (!is_null($include)) {
 553			$queryString[] = 'include=' . $include;
 554		}
 555		$queryString = self::createQueryStringFromArray($queryString);
 556		 
 557		// Perform request
 558		$response = $this->_performRequest('', $queryString, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_LIST);
 559		if ($response->isSuccessful()) {
 560			$xmlContainers = $this->_parseResponse($response)->Containers->Container;
 561			$xmlMarker = (string)$this->_parseResponse($response)->NextMarker;
 562
 563			$containers = array();
 564			if (!is_null($xmlContainers)) {
 565				for ($i = 0; $i < count($xmlContainers); $i++) {
 566					
 567					$containers[] = new Zend_Service_WindowsAzure_Storage_BlobContainer(
 568					(string)$xmlContainers[$i]->Name,
 569					(string)$xmlContainers[$i]->Etag,
 570					(string)$xmlContainers[$i]->LastModified,
 571					$this->_parseMetadataElement($xmlContainers[$i])
 572					);
 573				}
 574			}
 575			$currentResultCount = $currentResultCount + count($containers);
 576			if (!is_null($maxResults) && $currentResultCount < $maxResults) {
 577				if (!is_null($xmlMarker) && $xmlMarker != '') {
 578					$containers = array_merge($containers, $this->listContainers($prefix, $maxResults, $xmlMarker, $include, $currentResultCount));
 579				}
 580			}
 581			if (!is_null($maxResults) && count($containers) > $maxResults) {
 582				$containers = array_slice($containers, 0, $maxResults);
 583			}
 584			 
 585			return $containers;
 586		} else {
 587			require_once 'Zend/Service/WindowsAzure/Exception.php';
 588			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 589		}
 590	}
 591
 592	/**
 593	 * Put blob
 594	 *
 595	 * @param string $containerName      Container name
 596	 * @param string $blobName           Blob name
 597	 * @param string $localFileName      Local file name to be uploaded
 598	 * @param array  $metadata           Key/value pairs of meta data
 599	 * @param string $leaseId            Lease identifier
 600	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
 601	 * @return object Partial blob properties
 602	 * @throws Zend_Service_WindowsAzure_Exception
 603	 */
 604	public function putBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
 605	{
 606		if ($containerName === '') {
 607			require_once 'Zend/Service/WindowsAzure/Exception.php';
 608			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 609		}
 610		if (!self::isValidContainerName($containerName)) {
 611			require_once 'Zend/Service/WindowsAzure/Exception.php';
 612			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 613		}
 614		if ($blobName === '') {
 615			require_once 'Zend/Service/WindowsAzure/Exception.php';
 616			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
 617		}
 618		if ($localFileName === '') {
 619			require_once 'Zend/Service/WindowsAzure/Exception.php';
 620			throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
 621		}
 622		if (!file_exists($localFileName)) {
 623			require_once 'Zend/Service/WindowsAzure/Exception.php';
 624			throw new Zend_Service_WindowsAzure_Exception('Local file not found.');
 625		}
 626		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
 627			require_once 'Zend/Service/WindowsAzure/Exception.php';
 628			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
 629		}
 630			
 631		// Check file size
 632		if (filesize($localFileName) >= self::MAX_BLOB_SIZE) {
 633			return $this->putLargeBlob($containerName, $blobName, $localFileName, $metadata, $leaseId, $additionalHeaders);
 634		}
 635
 636		// Put the data to Windows Azure Storage
 637		return $this->putBlobData($containerName, $blobName, file_get_contents($localFileName), $metadata, $leaseId, $additionalHeaders);
 638	}
 639
 640	/**
 641	 * Put blob data
 642	 *
 643	 * @param string $containerName      Container name
 644	 * @param string $blobName           Blob name
 645	 * @param mixed  $data      		 Data to store
 646	 * @param array  $metadata           Key/value pairs of meta data
 647	 * @param string $leaseId            Lease identifier
 648	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
 649	 * @return object Partial blob properties
 650	 * @throws Zend_Service_WindowsAzure_Exception
 651	 */
 652	public function putBlobData($containerName = '', $blobName = '', $data = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
 653	{
 654		if ($containerName === '') {
 655			require_once 'Zend/Service/WindowsAzure/Exception.php';
 656			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 657		}
 658		if (!self::isValidContainerName($containerName)) {
 659			require_once 'Zend/Service/WindowsAzure/Exception.php';
 660			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 661		}
 662		if ($blobName === '') {
 663			require_once 'Zend/Service/WindowsAzure/Exception.php';
 664			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
 665		}
 666		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
 667			require_once 'Zend/Service/WindowsAzure/Exception.php';
 668			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
 669		}
 670
 671		// Create metadata headers
 672		$headers = array();
 673		if (!is_null($leaseId)) {
 674			$headers['x-ms-lease-id'] = $leaseId;
 675		}
 676		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
 677
 678		// Additional headers?
 679		foreach ($additionalHeaders as $key => $value) {
 680			$headers[$key] = $value;
 681		}
 682
 683		// Specify blob type
 684		$headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-type'] = self::BLOBTYPE_BLOCK;
 685
 686		// Resource name
 687		$resourceName = self::createResourceName($containerName , $blobName);
 688
 689		// Perform request
 690		$response = $this->_performRequest($resourceName, '', Zend_Http_Client::PUT, $headers, false, $data, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 691		if ($response->isSuccessful()) {
 692			
 693			return new Zend_Service_WindowsAzure_Storage_BlobInstance(
 694			$containerName,
 695			$blobName,
 696			null,
 697			$response->getHeader('Etag'),
 698			$response->getHeader('Last-modified'),
 699			$this->getBaseUrl() . '/' . $containerName . '/' . $blobName,
 700			strlen($data),
 701				'',
 702				'',
 703				'',
 704			false,
 705			$metadata
 706			);
 707		} else {
 708			require_once 'Zend/Service/WindowsAzure/Exception.php';
 709			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 710		}
 711	}
 712
 713	/**
 714	 * Put large blob (> 64 MB)
 715	 *
 716	 * @param string $containerName Container name
 717	 * @param string $blobName Blob name
 718	 * @param string $localFileName Local file name to be uploaded
 719	 * @param array  $metadata      Key/value pairs of meta data
 720	 * @param string $leaseId       Lease identifier
 721	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
 722	 * @return object Partial blob properties
 723	 * @throws Zend_Service_WindowsAzure_Exception
 724	 */
 725	public function putLargeBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
 726	{
 727		if ($containerName === '') {
 728			require_once 'Zend/Service/WindowsAzure/Exception.php';
 729			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 730		}
 731		if (!self::isValidContainerName($containerName)) {
 732			require_once 'Zend/Service/WindowsAzure/Exception.php';
 733			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 734		}
 735		if ($blobName === '') {
 736			require_once 'Zend/Service/WindowsAzure/Exception.php';
 737			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
 738		}
 739		if ($localFileName === '') {
 740			require_once 'Zend/Service/WindowsAzure/Exception.php';
 741			throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
 742		}
 743		if (!file_exists($localFileName)) {
 744			require_once 'Zend/Service/WindowsAzure/Exception.php';
 745			throw new Zend_Service_WindowsAzure_Exception('Local file not found.');
 746		}
 747		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
 748			require_once 'Zend/Service/WindowsAzure/Exception.php';
 749			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
 750		}
 751			
 752		// Check file size
 753		if (filesize($localFileName) < self::MAX_BLOB_SIZE) {
 754			return $this->putBlob($containerName, $blobName, $localFileName, $metadata, $leaseId, $additionalHeaders);
 755		}
 756			
 757		// Determine number of parts
 758		$numberOfParts = ceil( filesize($localFileName) / self::MAX_BLOB_TRANSFER_SIZE );
 759
 760		// Generate block id's
 761		$blockIdentifiers = array();
 762		for ($i = 0; $i < $numberOfParts; $i++) {
 763			$blockIdentifiers[] = $this->_generateBlockId($i);
 764		}
 765
 766		// Open file
 767		$fp = fopen($localFileName, 'r');
 768		if ($fp === false) {
 769			require_once 'Zend/Service/WindowsAzure/Exception.php';
 770			throw new Zend_Service_WindowsAzure_Exception('Could not open local file.');
 771		}
 772			
 773		// Upload parts
 774		for ($i = 0; $i < $numberOfParts; $i++) {
 775			// Seek position in file
 776			fseek($fp, $i * self::MAX_BLOB_TRANSFER_SIZE);
 777				
 778			// Read contents
 779			$fileContents = fread($fp, self::MAX_BLOB_TRANSFER_SIZE);
 780				
 781			// Put block
 782			$this->putBlock($containerName, $blobName, $blockIdentifiers[$i], $fileContents, $leaseId);
 783				
 784			// Dispose file contents
 785			$fileContents = null;
 786			unset($fileContents);
 787		}
 788
 789		// Close file
 790		fclose($fp);
 791
 792		// Put block list
 793		$this->putBlockList($containerName, $blobName, $blockIdentifiers, $metadata, $leaseId, $additionalHeaders);
 794
 795		// Return information of the blob
 796		return $this->getBlobInstance($containerName, $blobName, null, $leaseId);
 797	}
 798		
 799	/**
 800	 * Put large blob block
 801	 *
 802	 * @param string $containerName Container name
 803	 * @param string $blobName      Blob name
 804	 * @param string $identifier    Block ID
 805	 * @param array  $contents      Contents of the block
 806	 * @param string $leaseId       Lease identifier
 807	 * @throws Zend_Service_WindowsAzure_Exception
 808	 */
 809	public function putBlock($containerName = '', $blobName = '', $identifier = '', $contents = '', $leaseId = null)
 810	{
 811		if ($containerName === '') {
 812			require_once 'Zend/Service/WindowsAzure/Exception.php';
 813			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 814		}
 815		if (!self::isValidContainerName($containerName)) {
 816			require_once 'Zend/Service/WindowsAzure/Exception.php';
 817			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 818		}
 819		if ($identifier === '') {
 820			require_once 'Zend/Service/WindowsAzure/Exception.php';
 821			throw new Zend_Service_WindowsAzure_Exception('Block identifier is not specified.');
 822		}
 823		if (strlen($contents) > self::MAX_BLOB_TRANSFER_SIZE) {
 824			require_once 'Zend/Service/WindowsAzure/Exception.php';
 825			throw new Zend_Service_WindowsAzure_Exception('Block size is too big.');
 826		}
 827		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
 828			require_once 'Zend/Service/WindowsAzure/Exception.php';
 829			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
 830		}
 831
 832		// Headers
 833		$headers = array();
 834		if (!is_null($leaseId)) {
 835			$headers['x-ms-lease-id'] = $leaseId;
 836		}
 837			
 838		// Resource name
 839		$resourceName = self::createResourceName($containerName , $blobName);
 840
 841		// Upload
 842		$response = $this->_performRequest($resourceName, '?comp=block&blockid=' . base64_encode($identifier), Zend_Http_Client::PUT, $headers, false, $contents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 843		if (!$response->isSuccessful()) {
 844			require_once 'Zend/Service/WindowsAzure/Exception.php';
 845			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 846		}
 847	}
 848
 849	/**
 850	 * Put block list
 851	 *
 852	 * @param string $containerName      Container name
 853	 * @param string $blobName           Blob name
 854	 * @param array $blockList           Array of block identifiers
 855	 * @param array  $metadata           Key/value pairs of meta data
 856	 * @param string $leaseId            Lease identifier
 857	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
 858	 * @throws Zend_Service_WindowsAzure_Exception
 859	 */
 860	public function putBlockList($containerName = '', $blobName = '', $blockList = array(), $metadata = array(), $leaseId = null, $additionalHeaders = array())
 861	{
 862		if ($containerName === '') {
 863			require_once 'Zend/Service/WindowsAzure/Exception.php';
 864			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 865		}
 866		if (!self::isValidContainerName($containerName)) {
 867			require_once 'Zend/Service/WindowsAzure/Exception.php';
 868			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 869		}
 870		if ($blobName === '') {
 871			require_once 'Zend/Service/WindowsAzure/Exception.php';
 872			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
 873		}
 874		if (count($blockList) == 0) {
 875			require_once 'Zend/Service/WindowsAzure/Exception.php';
 876			throw new Zend_Service_WindowsAzure_Exception('Block list does not contain any elements.');
 877		}
 878		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
 879			require_once 'Zend/Service/WindowsAzure/Exception.php';
 880			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
 881		}
 882
 883		// Generate block list
 884		$blocks = '';
 885		foreach ($blockList as $block) {
 886			$blocks .= '  <Latest>' . base64_encode($block) . '</Latest>' . "\n";
 887		}
 888
 889		// Generate block list request
 890		$fileContents = utf8_encode(implode("\n", array(
 891				'<?xml version="1.0" encoding="utf-8"?>',
 892				'<BlockList>',
 893				$blocks,
 894				'</BlockList>'
 895			)));
 896
 897			// Create metadata headers
 898			$headers = array();
 899			if (!is_null($leaseId)) {
 900				$headers['x-ms-lease-id'] = $leaseId;
 901			}
 902			$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
 903
 904			// Additional headers?
 905			foreach ($additionalHeaders as $key => $value) {
 906				$headers[$key] = $value;
 907			}
 908
 909			// Resource name
 910			$resourceName = self::createResourceName($containerName , $blobName);
 911
 912			// Perform request
 913			$response = $this->_performRequest($resourceName, '?comp=blocklist', Zend_Http_Client::PUT, $headers, false, $fileContents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
 914			if (!$response->isSuccessful()) {
 915				require_once 'Zend/Service/WindowsAzure/Exception.php';
 916				throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
 917			}
 918	}
 919
 920	/**
 921	 * Get block list
 922	 *
 923	 * @param string $containerName Container name
 924	 * @param string $blobName      Blob name
 925	 * @param string $snapshotId    Snapshot identifier
 926	 * @param string $leaseId       Lease identifier
 927	 * @param integer $type         Type of block list to retrieve. 0 = all, 1 = committed, 2 = uncommitted
 928	 * @return array
 929	 * @throws Zend_Service_WindowsAzure_Exception
 930	 */
 931	public function getBlockList($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $type = 0)
 932	{
 933		if ($containerName === '') {
 934			require_once 'Zend/Service/WindowsAzure/Exception.php';
 935			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
 936		}
 937		if (!self::isValidContainerName($containerName)) {
 938			require_once 'Zend/Service/WindowsAzure/Exception.php';
 939			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
 940		}
 941		if ($blobName === '') {
 942			require_once 'Zend/Service/WindowsAzure/Exception.php';
 943			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
 944		}
 945		if ($type < 0 || $type > 2) {
 946			require_once 'Zend/Service/WindowsAzure/Exception.php';
 947			throw new Zend_Service_WindowsAzure_Exception('Invalid type of block list to retrieve.');
 948		}
 949
 950		// Set $blockListType
 951		$blockListType = 'all';
 952		if ($type == 1) {
 953			$blockListType = 'committed';
 954		}
 955		if ($type == 2) {
 956			$blockListType = 'uncommitted';
 957		}
 958
 959		// Headers
 960		$headers = array();
 961		if (!is_null($leaseId)) {
 962			$headers['x-ms-lease-id'] = $leaseId;
 963		}
 964
 965		// Build query string
 966		$queryString = array('comp=blocklist', 'blocklisttype=' . $blockListType);
 967		if (!is_null($snapshotId)) {
 968			$queryString[] = 'snapshot=' . $snapshotId;
 969		}
 970		$queryString = self::createQueryStringFromArray($queryString);
 971
 972		// Resource name
 973		$resourceName = self::createResourceName($containerName , $blobName);
 974			
 975		// Perform request
 976		$response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
 977		if ($response->isSuccessful()) {
 978			// Parse response
 979			$blockList = $this->_parseResponse($response);
 980
 981			// Create return value
 982			$returnValue = array();
 983			if ($blockList->CommittedBlocks) {
 984				foreach ($blockList->CommittedBlocks->Block as $block) {
 985					$returnValue['CommittedBlocks'][] = (object)array(
 986			            'Name' => (string)$block->Name,
 987			            'Size' => (string)$block->Size
 988					);
 989				}
 990			}
 991			if ($blockList->UncommittedBlocks)  {
 992				foreach ($blockList->UncommittedBlocks->Block as $block) {
 993					$returnValue['UncommittedBlocks'][] = (object)array(
 994			            'Name' => (string)$block->Name,
 995			            'Size' => (string)$block->Size
 996					);
 997				}
 998			}
 999
1000			return $returnValue;
1001		} else {
1002			require_once 'Zend/Service/WindowsAzure/Exception.php';
1003			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1004		}
1005	}
1006
1007	/**
1008	 * Create page blob
1009	 *
1010	 * @param string $containerName      Container name
1011	 * @param string $blobName           Blob name
1012	 * @param int    $size      		 Size of the page blob in bytes
1013	 * @param array  $metadata           Key/value pairs of meta data
1014	 * @param string $leaseId            Lease identifier
1015	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1016	 * @return object Partial blob properties
1017	 * @throws Zend_Service_WindowsAzure_Exception
1018	 */
1019	public function createPageBlob($containerName = '', $blobName = '', $size = 0, $metadata = array(), $leaseId = null, $additionalHeaders = array())
1020	{
1021		if ($containerName === '') {
1022			require_once 'Zend/Service/WindowsAzure/Exception.php';
1023			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1024		}
1025		if (!self::isValidContainerName($containerName)) {
1026			require_once 'Zend/Service/WindowsAzure/Exception.php';
1027			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1028		}
1029		if ($blobName === '') {
1030			require_once 'Zend/Service/WindowsAzure/Exception.php';
1031			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1032		}
1033		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1034			require_once 'Zend/Service/WindowsAzure/Exception.php';
1035			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1036		}
1037		if ($size <= 0) {
1038			require_once 'Zend/Service/WindowsAzure/Exception.php';
1039			throw new Zend_Service_WindowsAzure_Exception('Page blob size must be specified.');
1040		}
1041
1042		// Create metadata headers
1043		$headers = array();
1044		if (!is_null($leaseId)) {
1045			$headers['x-ms-lease-id'] = $leaseId;
1046		}
1047		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
1048
1049		// Additional headers?
1050		foreach ($additionalHeaders as $key => $value) {
1051			$headers[$key] = $value;
1052		}
1053
1054		// Specify blob type & blob length
1055		$headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-type'] = self::BLOBTYPE_PAGE;
1056		$headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-content-length'] = $size;
1057		$headers['Content-Length'] = 0;
1058
1059		// Resource name
1060		$resourceName = self::createResourceName($containerName , $blobName);
1061
1062		// Perform request
1063		$response = $this->_performRequest($resourceName, '', Zend_Http_Client::PUT, $headers, false, '', Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1064		if ($response->isSuccessful()) {
1065			
1066			return new Zend_Service_WindowsAzure_Storage_BlobInstance(
1067			$containerName,
1068			$blobName,
1069			null,
1070			$response->getHeader('Etag'),
1071			$response->getHeader('Last-modified'),
1072			$this->getBaseUrl() . '/' . $containerName . '/' . $blobName,
1073			$size,
1074				'',
1075				'',
1076				'',
1077			false,
1078			$metadata
1079			);
1080		} else {
1081			require_once 'Zend/Service/WindowsAzure/Exception.php';
1082			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1083		}
1084	}
1085
1086	/**
1087	 * Put page in page blob
1088	 *
1089	 * @param string $containerName      Container name
1090	 * @param string $blobName           Blob name
1091	 * @param int    $startByteOffset    Start byte offset
1092	 * @param int    $endByteOffset      End byte offset
1093	 * @param mixed  $contents			 Page contents
1094	 * @param string $writeMethod        Write method (Zend_Service_WindowsAzure_Storage_Blob::PAGE_WRITE_*)
1095	 * @param string $leaseId            Lease identifier
1096	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1097	 * @throws Zend_Service_WindowsAzure_Exception
1098	 */
1099	public function putPage($containerName = '', $blobName = '', $startByteOffset = 0, $endByteOffset = 0, $contents = '', $writeMethod = self::PAGE_WRITE_UPDATE, $leaseId = null, $additionalHeaders = array())
1100	{
1101		if ($containerName === '') {
1102			require_once 'Zend/Service/WindowsAzure/Exception.php';
1103			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1104		}
1105		if (!self::isValidContainerName($containerName)) {
1106			require_once 'Zend/Service/WindowsAzure/Exception.php';
1107			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1108		}
1109		if ($blobName === '') {
1110			require_once 'Zend/Service/WindowsAzure/Exception.php';
1111			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1112		}
1113		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1114			require_once 'Zend/Service/WindowsAzure/Exception.php';
1115			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1116		}
1117		if ($startByteOffset % 512 != 0) {
1118			require_once 'Zend/Service/WindowsAzure/Exception.php';
1119			throw new Zend_Service_WindowsAzure_Exception('Start byte offset must be a modulus of 512.');
1120		}
1121		if (($endByteOffset + 1) % 512 != 0) {
1122			require_once 'Zend/Service/WindowsAzure/Exception.php';
1123			throw new Zend_Service_WindowsAzure_Exception('End byte offset must be a modulus of 512 minus 1.');
1124		}
1125
1126		// Determine size
1127		$size = strlen($contents);
1128		if ($size >= self::MAX_BLOB_TRANSFER_SIZE) {
1129			require_once 'Zend/Service/WindowsAzure/Exception.php';
1130			throw new Zend_Service_WindowsAzure_Exception('Page blob size must not be larger than ' + self::MAX_BLOB_TRANSFER_SIZE . ' bytes.');
1131		}
1132
1133		// Create metadata headers
1134		$headers = array();
1135		if (!is_null($leaseId)) {
1136			$headers['x-ms-lease-id'] = $leaseId;
1137		}
1138
1139		// Additional headers?
1140		foreach ($additionalHeaders as $key => $value) {
1141			$headers[$key] = $value;
1142		}
1143
1144		// Specify range
1145		$headers['Range'] = 'bytes=' . $startByteOffset . '-' . $endByteOffset;
1146
1147		// Write method
1148		$headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'page-write'] = $writeMethod;
1149
1150		// Resource name
1151		$resourceName = self::createResourceName($containerName , $blobName);
1152
1153		// Perform request
1154		$response = $this->_performRequest($resourceName, '?comp=page', Zend_Http_Client::PUT, $headers, false, $contents, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1155		if (!$response->isSuccessful()) {
1156			require_once 'Zend/Service/WindowsAzure/Exception.php';
1157			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1158		}
1159	}
1160
1161	/**
1162	 * Put page in page blob
1163	 *
1164	 * @param string $containerName      Container name
1165	 * @param string $blobName           Blob name
1166	 * @param int    $startByteOffset    Start byte offset
1167	 * @param int    $endByteOffset      End byte offset
1168	 * @param string $leaseId            Lease identifier
1169	 * @return array Array of page ranges
1170	 * @throws Zend_Service_WindowsAzure_Exception
1171	 */
1172	public function getPageRegions($containerName = '', $blobName = '', $startByteOffset = 0, $endByteOffset = 0, $leaseId = null)
1173	{
1174		if ($containerName === '') {
1175			require_once 'Zend/Service/WindowsAzure/Exception.php';
1176			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1177		}
1178		if (!self::isValidContainerName($containerName)) {
1179			require_once 'Zend/Service/WindowsAzure/Exception.php';
1180			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1181		}
1182		if ($blobName === '') {
1183			require_once 'Zend/Service/WindowsAzure/Exception.php';
1184			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1185		}
1186		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1187			require_once 'Zend/Service/WindowsAzure/Exception.php';
1188			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1189		}
1190		if ($startByteOffset % 512 != 0) {
1191			require_once 'Zend/Service/WindowsAzure/Exception.php';
1192			throw new Zend_Service_WindowsAzure_Exception('Start byte offset must be a modulus of 512.');
1193		}
1194		if ($endByteOffset > 0 && ($endByteOffset + 1) % 512 != 0) {
1195			require_once 'Zend/Service/WindowsAzure/Exception.php';
1196			throw new Zend_Service_WindowsAzure_Exception('End byte offset must be a modulus of 512 minus 1.');
1197		}
1198
1199		// Create metadata headers
1200		$headers = array();
1201		if (!is_null($leaseId)) {
1202			$headers['x-ms-lease-id'] = $leaseId;
1203		}
1204
1205		// Specify range?
1206		if ($endByteOffset > 0) {
1207			$headers['Range'] = 'bytes=' . $startByteOffset . '-' . $endByteOffset;
1208		}
1209
1210		// Resource name
1211		$resourceName = self::createResourceName($containerName , $blobName);
1212
1213		// Perform request
1214		$response = $this->_performRequest($resourceName, '?comp=pagelist', Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1215		if ($response->isSuccessful()) {
1216			$result = $this->_parseResponse($response);
1217			$xmlRanges = null;
1218			if (count($result->PageRange) > 1) {
1219				$xmlRanges = $result->PageRange;
1220			} else {
1221				$xmlRanges = array($result->PageRange);
1222			}
1223			
1224			
1225			$ranges = array();
1226			
1227			for ($i = 0; $i < count($xmlRanges); $i++) {
1228				$ranges[] = new Zend_Service_WindowsAzure_Storage_PageRegionInstance(
1229				(int)$xmlRanges[$i]->Start,
1230				(int)$xmlRanges[$i]->End
1231				);
1232			}
1233			 
1234			return $ranges;
1235		} else {
1236			require_once 'Zend/Service/WindowsAzure/Exception.php';
1237			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1238		}
1239	}
1240		
1241	/**
1242	 * Copy blob
1243	 *
1244	 * @param string $sourceContainerName       Source container name
1245	 * @param string $sourceBlobName            Source blob name
1246	 * @param string $destinationContainerName  Destination container name
1247	 * @param string $destinationBlobName       Destination blob name
1248	 * @param array  $metadata                  Key/value pairs of meta data
1249	 * @param string $sourceSnapshotId          Source snapshot identifier
1250	 * @param string $destinationLeaseId        Destination lease identifier
1251	 * @param array  $additionalHeaders         Additional headers. See http://msdn.microsoft.com/en-us/library/dd894037.aspx for more information.
1252	 * @return object Partial blob properties
1253	 * @throws Zend_Service_WindowsAzure_Exception
1254	 */
1255	public function copyBlob($sourceContainerName = '', $sourceBlobName = '', $destinationContainerName = '', $destinationBlobName = '', $metadata = array(), $sourceSnapshotId = null, $destinationLeaseId = null, $additionalHeaders = array())
1256	{
1257		if ($sourceContainerName === '') {
1258			require_once 'Zend/Service/WindowsAzure/Exception.php';
1259			throw new Zend_Service_WindowsAzure_Exception('Source container name is not specified.');
1260		}
1261		if (!self::isValidContainerName($sourceContainerName)) {
1262			require_once 'Zend/Service/WindowsAzure/Exception.php';
1263			throw new Zend_Service_WindowsAzure_Exception('Source container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1264		}
1265		if ($sourceBlobName === '') {
1266			require_once 'Zend/Service/WindowsAzure/Exception.php';
1267			throw new Zend_Service_WindowsAzure_Exception('Source blob name is not specified.');
1268		}
1269		if ($destinationContainerName === '') {
1270			require_once 'Zend/Service/WindowsAzure/Exception.php';
1271			throw new Zend_Service_WindowsAzure_Exception('Destination container name is not specified.');
1272		}
1273		if (!self::isValidContainerName($destinationContainerName)) {
1274			require_once 'Zend/Service/WindowsAzure/Exception.php';
1275			throw new Zend_Service_WindowsAzure_Exception('Destination container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1276		}
1277		if ($destinationBlobName === '') {
1278			require_once 'Zend/Service/WindowsAzure/Exception.php';
1279			throw new Zend_Service_WindowsAzure_Exception('Destination blob name is not specified.');
1280		}
1281		if ($sourceContainerName === '$root' && strpos($sourceBlobName, '/') !== false) {
1282		require_once 'Zend/Service/WindowsAzure/Exception.php';
1283			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1284		}
1285		if ($destinationContainerName === '$root' && strpos($destinationBlobName, '/') !== false) {
1286			require_once 'Zend/Service/WindowsAzure/Exception.php';
1287			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1288		}
1289
1290		// Create metadata headers
1291		$headers = array();
1292		if (!is_null($destinationLeaseId)) {
1293			$headers['x-ms-lease-id'] = $destinationLeaseId;
1294		}
1295		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
1296
1297		// Additional headers?
1298		foreach ($additionalHeaders as $key => $value) {
1299			$headers[$key] = $value;
1300		}
1301
1302		// Resource names
1303		$sourceResourceName = self::createResourceName($sourceContainerName, $sourceBlobName);
1304		if (!is_null($sourceSnapshotId)) {
1305			$sourceResourceName .= '?snapshot=' . $sourceSnapshotId;
1306		}
1307		$destinationResourceName = self::createResourceName($destinationContainerName, $destinationBlobName);
1308
1309		// Set source blob
1310		$headers["x-ms-copy-source"] = '/' . $this->_accountName . '/' . $sourceResourceName;
1311
1312		// Perform request
1313		$response = $this->_performRequest($destinationResourceName, '', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1314		if ($response->isSuccessful()) {
1315			
1316			return new Zend_Service_WindowsAzure_Storage_BlobInstance(
1317			$destinationContainerName,
1318			$destinationBlobName,
1319			null,
1320			$response->getHeader('Etag'),
1321			$response->getHeader('Last-modified'),
1322			$this->getBaseUrl() . '/' . $destinationContainerName . '/' . $destinationBlobName,
1323			0,
1324				'',
1325				'',
1326				'',
1327			false,
1328			$metadata
1329			);
1330		} else {
1331			require_once 'Zend/Service/WindowsAzure/Exception.php';
1332			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1333		}
1334	}
1335
1336	/**
1337	 * Get blob
1338	 *
1339	 * @param string $containerName      Container name
1340	 * @param string $blobName           Blob name
1341	 * @param string $localFileName      Local file name to store downloaded blob
1342	 * @param string $snapshotId         Snapshot identifier
1343	 * @param string $leaseId            Lease identifier
1344	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1345	 * @throws Zend_Service_WindowsAzure_Exception
1346	 */
1347	public function getBlob($containerName = '', $blobName = '', $localFileName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array())
1348	{
1349		if ($containerName === '') {
1350			require_once 'Zend/Service/WindowsAzure/Exception.php';
1351			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1352		}
1353		if (!self::isValidContainerName($containerName)) {
1354			require_once 'Zend/Service/WindowsAzure/Exception.php';
1355			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1356		}
1357		if ($blobName === '') {
1358			require_once 'Zend/Service/WindowsAzure/Exception.php';
1359			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1360		}
1361		if ($localFileName === '') {
1362			require_once 'Zend/Service/WindowsAzure/Exception.php';
1363			throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
1364		}
1365
1366		// Fetch data
1367		file_put_contents($localFileName, $this->getBlobData($containerName, $blobName, $snapshotId, $leaseId, $additionalHeaders));
1368	}
1369
1370	/**
1371	 * Get blob data
1372	 *
1373	 * @param string $containerName      Container name
1374	 * @param string $blobName           Blob name
1375	 * @param string $snapshotId         Snapshot identifier
1376	 * @param string $leaseId            Lease identifier
1377	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1378	 * @return mixed Blob contents
1379	 * @throws Zend_Service_WindowsAzure_Exception
1380	 */
1381	public function getBlobData($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array())
1382	{
1383		if ($containerName === '') {
1384			require_once 'Zend/Service/WindowsAzure/Exception.php';
1385			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1386		}
1387		if (!self::isValidContainerName($containerName)) {
1388			require_once 'Zend/Service/WindowsAzure/Exception.php';
1389			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1390		}
1391		if ($blobName === '') {
1392			require_once 'Zend/Service/WindowsAzure/Exception.php';
1393			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1394		}
1395
1396		// Build query string
1397		$queryString = array();
1398		if (!is_null($snapshotId)) {
1399			$queryString[] = 'snapshot=' . $snapshotId;
1400		}
1401		$queryString = self::createQueryStringFromArray($queryString);
1402
1403		// Additional headers?
1404		$headers = array();
1405		if (!is_null($leaseId)) {
1406			$headers['x-ms-lease-id'] = $leaseId;
1407		}
1408		foreach ($additionalHeaders as $key => $value) {
1409			$headers[$key] = $value;
1410		}
1411
1412		// Resource name
1413		$resourceName = self::createResourceName($containerName , $blobName);
1414
1415		// Perform request
1416		$response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::GET, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
1417		if ($response->isSuccessful()) {
1418			return $response->getBody();
1419		} else {
1420			require_once 'Zend/Service/WindowsAzure/Exception.php';
1421			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1422		}
1423	}
1424
1425	/**
1426	 * Get blob instance
1427	 *
1428	 * @param string $containerName      Container name
1429	 * @param string $blobName           Blob name
1430	 * @param string $snapshotId         Snapshot identifier
1431	 * @param string $leaseId            Lease identifier
1432	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1433	 * @return Zend_Service_WindowsAzure_Storage_BlobInstance
1434	 * @throws Zend_Service_WindowsAzure_Exception
1435	 */
1436	public function getBlobInstance($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array())
1437	{
1438		if ($containerName === '') {
1439			require_once 'Zend/Service/WindowsAzure/Exception.php';
1440			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1441		}
1442		if (!self::isValidContainerName($containerName)) {
1443			require_once 'Zend/Service/WindowsAzure/Exception.php';
1444			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1445		}
1446		if ($blobName === '') {
1447			require_once 'Zend/Service/WindowsAzure/Exception.php';
1448			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1449		}
1450		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1451			require_once 'Zend/Service/WindowsAzure/Exception.php';
1452			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1453		}
1454
1455		// Build query string
1456		$queryString = array();
1457		if (!is_null($snapshotId)) {
1458			$queryString[] = 'snapshot=' . $snapshotId;
1459		}
1460		$queryString = self::createQueryStringFromArray($queryString);
1461		 
1462		// Additional headers?
1463		$headers = array();
1464		if (!is_null($leaseId)) {
1465			$headers['x-ms-lease-id'] = $leaseId;
1466		}
1467		foreach ($additionalHeaders as $key => $value) {
1468			$headers[$key] = $value;
1469		}
1470
1471		// Resource name
1472		$resourceName = self::createResourceName($containerName , $blobName);
1473
1474		// Perform request
1475		$response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::HEAD, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ);
1476		if ($response->isSuccessful()) {
1477			// Parse metadata
1478			$metadata = $this->_parseMetadataHeaders($response->getHeaders());
1479			
1480			// Return blob
1481			return new Zend_Service_WindowsAzure_Storage_BlobInstance(
1482				$containerName,
1483				$blobName,
1484				$snapshotId,
1485				$response->getHeader('Etag'),
1486				$response->getHeader('Last-modified'),
1487				$this->getBaseUrl() . '/' . $containerName . '/' . $blobName,
1488				$response->getHeader('Content-Length'),
1489				$response->getHeader('Content-Type'),
1490				$response->getHeader('Content-Encoding'),
1491				$response->getHeader('Content-Language'),
1492				$response->getHeader('Cache-Control'),
1493				$response->getHeader('x-ms-blob-type'),
1494				$response->getHeader('x-ms-lease-status'),
1495				false,
1496				$metadata
1497			);
1498		} else {
1499			require_once 'Zend/Service/WindowsAzure/Exception.php';
1500			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1501		}
1502	}
1503
1504	/**
1505	 * Get blob metadata
1506	 *
1507	 * @param string $containerName  Container name
1508	 * @param string $blobName       Blob name
1509	 * @param string $snapshotId     Snapshot identifier
1510	 * @param string $leaseId        Lease identifier
1511	 * @return array Key/value pairs of meta data
1512	 * @throws Zend_Service_WindowsAzure_Exception
1513	 */
1514	public function getBlobMetadata($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null)
1515	{
1516		if ($containerName === '') {
1517			require_once 'Zend/Service/WindowsAzure/Exception.php';
1518			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1519		}
1520		if (!self::isValidContainerName($containerName)) {
1521			require_once 'Zend/Service/WindowsAzure/Exception.php';
1522			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1523		}
1524		if ($blobName === '') {
1525			require_once 'Zend/Service/WindowsAzure/Exception.php';
1526			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1527		}
1528		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1529			require_once 'Zend/Service/WindowsAzure/Exception.php';
1530			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1531		}
1532
1533		return $this->getBlobInstance($containerName, $blobName, $snapshotId, $leaseId)->Metadata;
1534	}
1535
1536	/**
1537	 * Set blob metadata
1538	 *
1539	 * Calling the Set Blob Metadata operation overwrites all existing metadata that is associated with the blob. It's not possible to modify an individual name/value pair.
1540	 *
1541	 * @param string $containerName      Container name
1542	 * @param string $blobName           Blob name
1543	 * @param array  $metadata           Key/value pairs of meta data
1544	 * @param string $leaseId            Lease identifier
1545	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1546	 * @throws Zend_Service_WindowsAzure_Exception
1547	 */
1548	public function setBlobMetadata($containerName = '', $blobName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
1549	{
1550		if ($containerName === '') {
1551			require_once 'Zend/Service/WindowsAzure/Exception.php';
1552			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1553		}
1554		if (!self::isValidContainerName($containerName)) {
1555			require_once 'Zend/Service/WindowsAzure/Exception.php';
1556			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1557		}
1558		if ($blobName === '') {
1559			require_once 'Zend/Service/WindowsAzure/Exception.php';
1560			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1561		}
1562		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1563			require_once 'Zend/Service/WindowsAzure/Exception.php';
1564			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1565		}
1566		if (count($metadata) == 0) {
1567			return;
1568		}
1569
1570		// Create metadata headers
1571		$headers = array();
1572		if (!is_null($leaseId)) {
1573			$headers['x-ms-lease-id'] = $leaseId;
1574		}
1575		$headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
1576
1577		// Additional headers?
1578		foreach ($additionalHeaders as $key => $value) {
1579			$headers[$key] = $value;
1580		}
1581
1582		// Perform request
1583		$response = $this->_performRequest($containerName . '/' . $blobName, '?comp=metadata', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1584		if (!$response->isSuccessful()) {
1585			require_once 'Zend/Service/WindowsAzure/Exception.php';
1586			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1587		}
1588	}
1589
1590	/**
1591	 * Set blob properties
1592	 *
1593	 * All available properties are listed at http://msdn.microsoft.com/en-us/library/ee691966.aspx and should be provided in the $additionalHeaders parameter.
1594	 *
1595	 * @param string $containerName      Container name
1596	 * @param string $blobName           Blob name
1597	 * @param string $leaseId            Lease identifier
1598	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1599	 * @throws Zend_Service_WindowsAzure_Exception
1600	 */
1601	public function setBlobProperties($containerName = '', $blobName = '', $leaseId = null, $additionalHeaders = array())
1602	{
1603		if ($containerName === '') {
1604			require_once 'Zend/Service/WindowsAzure/Exception.php';
1605			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1606		}
1607		if (!self::isValidContainerName($containerName)) {
1608			require_once 'Zend/Service/WindowsAzure/Exception.php';
1609			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1610		}
1611		if ($blobName === '') {
1612			require_once 'Zend/Service/WindowsAzure/Exception.php';
1613			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1614		}
1615		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1616			require_once 'Zend/Service/WindowsAzure/Exception.php';
1617			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1618		}
1619		if (count($additionalHeaders) == 0) {
1620			require_once 'Zend/Service/WindowsAzure/Exception.php';
1621			throw new Zend_Service_WindowsAzure_Exception('No additional headers are specified.');
1622		}
1623
1624		// Create headers
1625		$headers = array();
1626
1627		// Lease set?
1628		if (!is_null($leaseId)) {
1629			$headers['x-ms-lease-id'] = $leaseId;
1630		}
1631
1632		// Additional headers?
1633		foreach ($additionalHeaders as $key => $value) {
1634			$headers[$key] = $value;
1635		}
1636
1637		// Perform request
1638		$response = $this->_performRequest($containerName . '/' . $blobName, '?comp=properties', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1639		if (!$response->isSuccessful()) {
1640			require_once 'Zend/Service/WindowsAzure/Exception.php';
1641			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1642		}
1643	}
1644
1645	/**
1646	 * Get blob properties
1647	 *
1648	 * @param string $containerName      Container name
1649	 * @param string $blobName           Blob name
1650	 * @param string $snapshotId         Snapshot identifier
1651	 * @param string $leaseId            Lease identifier
1652	 * @return Zend_Service_WindowsAzure_Storage_BlobInstance
1653	 * @throws Zend_Service_WindowsAzure_Exception
1654	 */
1655	public function getBlobProperties($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null)
1656	{
1657		if ($containerName === '') {
1658			require_once 'Zend/Service/WindowsAzure/Exception.php';
1659			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1660		}
1661		if (!self::isValidContainerName($containerName)) {
1662			require_once 'Zend/Service/WindowsAzure/Exception.php';
1663			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1664		}
1665		if ($blobName === '') {
1666			require_once 'Zend/Service/WindowsAzure/Exception.php';
1667			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1668		}
1669		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1670			require_once 'Zend/Service/WindowsAzure/Exception.php';
1671			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1672		}
1673
1674		return $this->getBlobInstance($containerName, $blobName, $snapshotId, $leaseId);
1675	}
1676
1677	/**
1678	 * Delete blob
1679	 *
1680	 * @param string $containerName      Container name
1681	 * @param string $blobName           Blob name
1682	 * @param string $snapshotId         Snapshot identifier
1683	 * @param string $leaseId            Lease identifier
1684	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1685	 * @throws Zend_Service_WindowsAzure_Exception
1686	 */
1687	public function deleteBlob($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $additionalHeaders = array())
1688	{
1689		if ($containerName === '') {
1690			require_once 'Zend/Service/WindowsAzure/Exception.php';
1691			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1692		}
1693		if (!self::isValidContainerName($containerName)) {
1694			require_once 'Zend/Service/WindowsAzure/Exception.php';
1695			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1696		}
1697		if ($blobName === '') {
1698			require_once 'Zend/Service/WindowsAzure/Exception.php';
1699			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1700		}
1701		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1702			require_once 'Zend/Service/WindowsAzure/Exception.php';
1703			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1704		}
1705
1706		// Build query string
1707		$queryString = array();
1708		if (!is_null($snapshotId)) {
1709			$queryString[] = 'snapshot=' . $snapshotId;
1710		}
1711		$queryString = self::createQueryStringFromArray($queryString);
1712			
1713		// Additional headers?
1714		$headers = array();
1715		if (!is_null($leaseId)) {
1716			$headers['x-ms-lease-id'] = $leaseId;
1717		}
1718		foreach ($additionalHeaders as $key => $value) {
1719			$headers[$key] = $value;
1720		}
1721
1722		// Resource name
1723		$resourceName = self::createResourceName($containerName , $blobName);
1724
1725		// Perform request
1726		$response = $this->_performRequest($resourceName, $queryString, Zend_Http_Client::DELETE, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1727		if (!$response->isSuccessful()) {
1728			require_once 'Zend/Service/WindowsAzure/Exception.php';
1729			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1730		}
1731	}
1732
1733	/**
1734	 * Snapshot blob
1735	 *
1736	 * @param string $containerName      Container name
1737	 * @param string $blobName           Blob name
1738	 * @param array  $metadata           Key/value pairs of meta data
1739	 * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
1740	 * @return string Date/Time value representing the snapshot identifier.
1741	 * @throws Zend_Service_WindowsAzure_Exception
1742	 */
1743	public function snapshotBlob($containerName = '', $blobName = '', $metadata = array(), $additionalHeaders = array())
1744	{
1745		if ($containerName === '') {
1746			require_once 'Zend/Service/WindowsAzure/Exception.php';
1747			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1748		}
1749		if (!self::isValidContainerName($containerName)) {
1750			require_once 'Zend/Service/WindowsAzure/Exception.php';
1751			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1752		}
1753		if ($blobName === '') {
1754			require_once 'Zend/Service/WindowsAzure/Exception.php';
1755			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1756		}
1757		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1758			require_once 'Zend/Service/WindowsAzure/Exception.php';
1759			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1760		}
1761
1762		// Additional headers?
1763		$headers = array();
1764		foreach ($additionalHeaders as $key => $value) {
1765			$headers[$key] = $value;
1766		}
1767
1768		// Resource name
1769		$resourceName = self::createResourceName($containerName , $blobName);
1770
1771		// Perform request
1772		$response = $this->_performRequest($resourceName, '?comp=snapshot', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1773		if ($response->isSuccessful()) {
1774			return $response->getHeader('x-ms-snapshot');
1775		} else {
1776			require_once 'Zend/Service/WindowsAzure/Exception.php';
1777			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1778		}
1779	}
1780
1781	/**
1782	 * Lease blob - See (http://msdn.microsoft.com/en-us/library/ee691972.aspx)
1783	 *
1784	 * @param string $containerName      Container name
1785	 * @param string $blobName           Blob name
1786	 * @param string $leaseAction        Lease action (Zend_Service_WindowsAzure_Storage_Blob::LEASE_*)
1787	 * @param string $leaseId            Lease identifier, required to renew the lease or to release the lease.
1788	 * @return Zend_Service_WindowsAzure_Storage_LeaseInstance Lease instance
1789	 * @throws Zend_Service_WindowsAzure_Exception
1790	 */
1791	public function leaseBlob($containerName = '', $blobName = '', $leaseAction = self::LEASE_ACQUIRE, $leaseId = null)
1792	{
1793		if ($containerName === '') {
1794			require_once 'Zend/Service/WindowsAzure/Exception.php';
1795			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1796		}
1797		if (!self::isValidContainerName($containerName)) {
1798			require_once 'Zend/Service/WindowsAzure/Exception.php';
1799			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1800		}
1801		if ($blobName === '') {
1802			require_once 'Zend/Service/WindowsAzure/Exception.php';
1803			throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
1804		}
1805		if ($containerName === '$root' && strpos($blobName, '/') !== false) {
1806			require_once 'Zend/Service/WindowsAzure/Exception.php';
1807			throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
1808		}
1809
1810		// Additional headers?
1811		$headers = array();
1812		$headers['x-ms-lease-action'] = strtolower($leaseAction);
1813		if (!is_null($leaseId)) {
1814			$headers['x-ms-lease-id'] = $leaseId;
1815		}
1816
1817		// Resource name
1818		$resourceName = self::createResourceName($containerName , $blobName);
1819
1820		// Perform request
1821		$response = $this->_performRequest($resourceName, '?comp=lease', Zend_Http_Client::PUT, $headers, false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
1822		
1823		
1824		
1825		if ($response->isSuccessful()) {
1826			return new Zend_Service_WindowsAzure_Storage_LeaseInstance(
1827			$containerName,
1828			$blobName,
1829			$response->getHeader('x-ms-lease-id'),
1830			$response->getHeader('x-ms-lease-time'));
1831		} else {
1832			require_once 'Zend/Service/WindowsAzure/Exception.php';
1833			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1834		}
1835	}
1836
1837	/**
1838	 * List blobs
1839	 *
1840	 * @param string $containerName Container name
1841	 * @param string $prefix     Optional. Filters the results to return only blobs whose name begins with the specified prefix.
1842	 * @param string $delimiter  Optional. Delimiter, i.e. '/', for specifying folder hierarchy
1843	 * @param int    $maxResults Optional. Specifies the maximum number of blobs to return per call to Azure storage. This does NOT affect list size returned by this function. (maximum: 5000)
1844	 * @param string $marker     Optional string value that identifies the portion of the list to be returned with the next list operation.
1845	 * @param string $include    Optional. Specifies that the response should include one or more of the following subsets: '', 'metadata', 'snapshots', 'uncommittedblobs'). Multiple values can be added separated with a comma (,)
1846	 * @param int    $currentResultCount Current result count (internal use)
1847	 * @return array
1848	 * @throws Zend_Service_WindowsAzure_Exception
1849	 */
1850	public function listBlobs($containerName = '', $prefix = '', $delimiter = '', $maxResults = null, $marker = null, $include = null, $currentResultCount = 0)
1851	{
1852		if ($containerName === '') {
1853			require_once 'Zend/Service/WindowsAzure/Exception.php';
1854			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1855		}
1856		if (!self::isValidContainerName($containerName)) {
1857			require_once 'Zend/Service/WindowsAzure/Exception.php';
1858			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1859		}
1860			
1861		// Build query string
1862		$queryString = array('restype=container', 'comp=list');
1863		if (!is_null($prefix)) {
1864			$queryString[] = 'prefix=' . $prefix;
1865		}
1866		if ($delimiter !== '') {
1867			$queryString[] = 'delimiter=' . $delimiter;
1868		}
1869		if (!is_null($maxResults)) {
1870			$queryString[] = 'maxresults=' . $maxResults;
1871		}
1872		if (!is_null($marker)) {
1873			$queryString[] = 'marker=' . $marker;
1874		}
1875		if (!is_null($include)) {
1876			$queryString[] = 'include=' . $include;
1877		}
1878		$queryString = self::createQueryStringFromArray($queryString);
1879
1880		// Perform request
1881		$response = $this->_performRequest($containerName, $queryString, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_LIST);
1882		if ($response->isSuccessful()) {
1883			// Return value
1884			$blobs = array();
1885	
1886			// Blobs
1887			$xmlBlobs = $this->_parseResponse($response)->Blobs->Blob;
1888			if (!is_null($xmlBlobs)) {
1889				
1890				for ($i = 0; $i < count($xmlBlobs); $i++) {
1891					$properties = (array)$xmlBlobs[$i]->Properties;
1892						
1893					$blobs[] = new Zend_Service_WindowsAzure_Storage_BlobInstance(
1894					$containerName,
1895					(string)$xmlBlobs[$i]->Name,
1896					(string)$xmlBlobs[$i]->Snapshot,
1897					(string)$properties['Etag'],
1898					(string)$properties['Last-Modified'],
1899					(string)$xmlBlobs[$i]->Url,
1900					(string)$properties['Content-Length'],
1901					(string)$properties['Content-Type'],
1902					(string)$properties['Content-Encoding'],
1903					(string)$properties['Content-Language'],
1904					(string)$properties['Cache-Control'],
1905					(string)$properties['BlobType'],
1906					(string)$properties['LeaseStatus'],
1907					false,
1908					$this->_parseMetadataElement($xmlBlobs[$i])
1909					);
1910				}
1911			}
1912				
1913			// Blob prefixes (folders)
1914			$xmlBlobs = $this->_parseResponse($response)->Blobs->BlobPrefix;
1915				
1916			if (!is_null($xmlBlobs)) {
1917				
1918				for ($i = 0; $i < count($xmlBlobs); $i++) {
1919					$blobs[] = new Zend_Service_WindowsAzure_Storage_BlobInstance(
1920					$containerName,
1921					(string)$xmlBlobs[$i]->Name,
1922					null,
1923						'',
1924						'',
1925						'',
1926					0,
1927						'',
1928						'',
1929						'',
1930						'',
1931						'',
1932						'',
1933					true,
1934					$this->_parseMetadataElement($xmlBlobs[$i])
1935					);
1936				}
1937			}
1938				
1939			// More blobs?
1940			$xmlMarker = (string)$this->_parseResponse($response)->NextMarker;
1941			$currentResultCount = $currentResultCount + count($blobs);
1942			if (!is_null($maxResults) && $currentResultCount < $maxResults) {
1943				if (!is_null($xmlMarker) && $xmlMarker != '') {
1944					$blobs = array_merge($blobs, $this->listBlobs($containerName, $prefix, $delimiter, $maxResults, $marker, $include, $currentResultCount));
1945				}
1946			}
1947			if (!is_null($maxResults) && count($blobs) > $maxResults) {
1948				$blobs = array_slice($blobs, 0, $maxResults);
1949			}
1950				
1951			return $blobs;
1952		} else {
1953			require_once 'Zend/Service/WindowsAzure/Exception.php';
1954			throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
1955		}
1956	}
1957
1958	/**
1959	 * Generate shared access URL
1960	 *
1961	 * @param string $containerName  Container name
1962	 * @param string $blobName       Blob name
1963	 * @param string $resource       Signed resource - container (c) - blob (b)
1964	 * @param string $permissions    Signed permissions - read (r), write (w), delete (d) and list (l)
1965	 * @param string $start          The time at which the Shared Access Signature becomes valid.
1966	 * @param string $expiry         The time at which the Shared Access Signature becomes invalid.
1967	 * @param string $identifier     Signed identifier
1968	 * @return string
1969	 */
1970	public function generateSharedAccessUrl($containerName = '', $blobName = '', $resource = 'b', $permissions = 'r', $start = '', $expiry = '', $identifier = '')
1971	{
1972		if ($containerName === '') {
1973			require_once 'Zend/Service/WindowsAzure/Exception.php';
1974			throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
1975		}
1976		if (!self::isValidContainerName($containerName)) {
1977			require_once 'Zend/Service/WindowsAzure/Exception.php';
1978			throw new Zend_Service_WindowsAzure_Exception('Container name does not adhere to container naming conventions. See http://msdn.microsoft.com/en-us/library/dd135715.aspx for more information.');
1979		}
1980
1981		// Resource name
1982		$resourceName = self::createResourceName($containerName , $blobName);
1983
1984		// Generate URL
1985		return $this->getBaseUrl() . '/' . $resourceName . '?' .
1986		$this->_sharedAccessSignatureCredentials->createSignedQueryString(
1987		$resourceName,
1988		        '',
1989		$resource,
1990		$permissions,
1991		$start,
1992		$expiry,
1993		$identifier);
1994	}
1995
1996	/**
1997	 * Register this object as stream wrapper client
1998	 *
1999	 * @param  string $name Protocol name
2000	 * @return Zend_Service_WindowsAzure_Storage_Blob
2001	 */
2002	public function registerAsClient($name)
2003	{
2004		self::$_wrapperClients[$name] = $this;
2005		return $this;
2006	}
2007
2008	/**
2009	 * Unregister this object as stream wrapper client
2010	 *
2011	 * @param  string $name Protocol name
2012	 * @return Zend_Service_WindowsAzure_Storage_Blob
2013	 */
2014	public function unregisterAsClient($name)
2015	{
2016		unset(self::$_wrapperClients[$name]);
2017		return $this;
2018	}
2019
2020	/**
2021	 * Get wrapper client for stream type
2022	 *
2023	 * @param  string $name Protocol name
2024	 * @return Zend_Service_WindowsAzure_Storage_Blob
2025	 */
2026	public static function getWrapperClient($name)
2027	{
2028		return self::$_wrapperClients[$name];
2029	}
2030
2031	/**
2032	 * Register this object as stream wrapper
2033	 *
2034	 * @param  string $name Protocol name
2035	 */
2036	public function registerStreamWrapper($name = 'azure')
2037	{
2038		stream_register_wrapper($name, 'Zend_Service_WindowsAzure_Storage_Blob_Stream');
2039		$this->registerAsClient($name);
2040	}
2041
2042	/**
2043	 * Unregister this object as stream wrapper
2044	 *
2045	 * @param  string $name Protocol name
2046	 * @return Zend_Service_WindowsAzure_Storage_Blob
2047	 */
2048	public function unregisterStreamWrapper($name = 'azure')
2049	{
2050		stream_wrapper_unregister($name);
2051		$this->unregisterAsClient($name);
2052	}
2053
2054	/**
2055	 * Create resource name
2056	 *
2057	 * @param string $containerName  Container name
2058	 * @param string $blobName Blob name
2059	 * @return string
2060	 */
2061	public static function createResourceName($containerName = '', $blobName = '')
2062	{
2063		// Resource name
2064		$resourceName = $containerName . '/' . $blobName;
2065		if ($containerName === '' || $containerName === '$root') {
2066			$resourceName = $blobName;
2067		}
2068		if ($blobName === '') {
2069			$resourceName = $containerName;
2070		}
2071
2072		return $resourceName;
2073	}
2074
2075	/**
2076	 * Is valid container name?
2077	 *
2078	 * @param string $containerName Container name
2079	 * @return boolean
2080	 */
2081	public static function isValidContainerName($containerName = '')
2082	{
2083		if ($containerName == '$root') {
2084			return true;
2085		}
2086
2087		if (preg_match("/^[a-z0-9][a-z0-9-]*$/", $containerName) === 0) {
2088			return false;
2089		}
2090
2091		if (strpos($containerName, '--') !== false) {
2092			return false;
2093		}
2094
2095		if (strtolower($containerName) != $containerName) {
2096			return false;
2097		}
2098
2099		if (strlen($containerName) < 3 || strlen($containerName) > 63) {
2100			return false;
2101		}
2102
2103		if (substr($containerName, -1) == '-') {
2104			return false;
2105		}
2106
2107		return true;
2108	}
2109
2110	/**
2111	 * Get error message from Zend_Http_Response
2112	 *
2113	 * @param Zend_Http_Response $response Repsonse
2114	 * @param string $alternativeError Alternative error message
2115	 * @return string
2116	 */
2117	protected function _getErrorMessage(Zend_Http_Response $response, $alternativeError = 'Unknown error.')
2118	{
2119		$response = $this->_parseResponse($response);
2120		if ($response && $response->Message) {
2121			return (string)$response->Message;
2122		} else {
2123			return $alternativeError;
2124		}
2125	}
2126
2127	/**
2128	 * Generate block id
2129	 *
2130	 * @param int $part Block number
2131	 * @return string Windows Azure Blob Storage block number
2132	 */
2133	protected function _generateBlockId($part = 0)
2134	{
2135		$returnValue = $part;
2136		while (strlen($returnValue) < 64) {
2137			$returnValue = '0' . $returnValue;
2138		}
2139
2140		return $returnValue;
2141	}
2142}