File indexing completed on 2024-06-23 05:55:45

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Service_WindowsAzure
0017  * @subpackage Storage
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://todo     name_todo
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Service_WindowsAzure_Storage
0025  */
0026 // require_once 'Zend/Service/WindowsAzure/Storage.php';
0027 
0028 /**
0029  * @see Zend_Service_WindowsAzure_Storage_BlobInstance
0030  */
0031 // require_once 'Zend/Service/WindowsAzure/Storage/BlobInstance.php';
0032 
0033 /**
0034  * @see Zend_Service_WindowsAzure_Storage_BlobContainer
0035  */
0036 // require_once 'Zend/Service/WindowsAzure/Storage/BlobContainer.php';
0037 
0038 /**
0039  * @see Zend_Service_WindowsAzure_Storage_PageRegionInstance
0040  */
0041 // require_once 'Zend/Service/WindowsAzure/Storage/PageRegionInstance.php';
0042 
0043 /**
0044  * @see Zend_Service_WindowsAzure_Storage_LeaseInstance
0045  */
0046 // require_once 'Zend/Service/WindowsAzure/Storage/LeaseInstance.php';
0047 
0048 /**
0049  * @see Zend_Service_WindowsAzure_Storage_Blob_Stream
0050  */
0051 // require_once 'Zend/Service/WindowsAzure/Storage/Blob/Stream.php';
0052 
0053 /**
0054  * @see Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
0055  */
0056 // require_once 'Zend/Service/WindowsAzure/Credentials/SharedAccessSignature.php';
0057 /**
0058  * @category   Zend
0059  * @package    Zend_Service_WindowsAzure
0060  * @subpackage Storage
0061  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0062  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0063  */
0064 class Zend_Service_WindowsAzure_Storage_Blob extends Zend_Service_WindowsAzure_Storage
0065 {
0066   /**
0067    * ACL - Private access
0068    */
0069   const ACL_PRIVATE = null;
0070 
0071   /**
0072    * ACL - Public access (read all blobs)
0073    *
0074    * @deprecated Use ACL_PUBLIC_CONTAINER or ACL_PUBLIC_BLOB instead.
0075    */
0076   const ACL_PUBLIC = 'container';
0077   
0078   /**
0079    * ACL - Blob Public access (read all blobs)
0080    */
0081   const ACL_PUBLIC_BLOB = 'blob';
0082 
0083   /**
0084    * ACL - Container Public access (enumerate and read all blobs)
0085    */
0086   const ACL_PUBLIC_CONTAINER = 'container';
0087 
0088   /**
0089    * Blob lease constants
0090    */
0091   const LEASE_ACQUIRE = 'acquire';
0092   const LEASE_RENEW   = 'renew';
0093   const LEASE_RELEASE = 'release';
0094   const LEASE_BREAK   = 'break';
0095 
0096   /**
0097    * Maximal blob size (in bytes)
0098    */
0099   const MAX_BLOB_SIZE = 67108864;
0100 
0101   /**
0102    * Maximal blob transfer size (in bytes)
0103    */
0104   const MAX_BLOB_TRANSFER_SIZE = 4194304;
0105 
0106   /**
0107    * Blob types
0108    */
0109   const BLOBTYPE_BLOCK = 'BlockBlob';
0110   const BLOBTYPE_PAGE  = 'PageBlob';
0111 
0112   /**
0113    * Put page write options
0114    */
0115   const PAGE_WRITE_UPDATE = 'update';
0116   const PAGE_WRITE_CLEAR  = 'clear';
0117 
0118   /**
0119    * Stream wrapper clients
0120    *
0121    * @var array
0122    */
0123   protected static $_wrapperClients = array();
0124 
0125   /**
0126    * SharedAccessSignature credentials
0127    *
0128    * @var Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
0129    */
0130   protected $_sharedAccessSignatureCredentials = null;
0131 
0132   /**
0133    * Creates a new Zend_Service_WindowsAzure_Storage_Blob instance
0134    *
0135    * @param string $host Storage host name
0136    * @param string $accountName Account name for Windows Azure
0137    * @param string $accountKey Account key for Windows Azure
0138    * @param boolean $usePathStyleUri Use path-style URI's
0139    * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
0140    */
0141   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)
0142   {
0143     parent::__construct($host, $accountName, $accountKey, $usePathStyleUri, $retryPolicy);
0144 
0145     // API version
0146     $this->_apiVersion = '2009-09-19';
0147 
0148     // SharedAccessSignature credentials
0149     $this->_sharedAccessSignatureCredentials = new Zend_Service_WindowsAzure_Credentials_SharedAccessSignature($accountName, $accountKey, $usePathStyleUri);
0150   }
0151 
0152   /**
0153    * Check if a blob exists
0154    *
0155    * @param string $containerName Container name
0156    * @param string $blobName      Blob name
0157    * @param string $snapshotId    Snapshot identifier
0158    * @return boolean
0159    */
0160   public function blobExists($containerName = '', $blobName = '', $snapshotId = null)
0161   {
0162     if ($containerName === '') {
0163       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0164       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0165     }
0166     if (!self::isValidContainerName($containerName)) {
0167       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0168       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.');
0169     }
0170     if ($blobName === '') {
0171       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0172       throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
0173     }
0174 
0175     // Get blob instance
0176     try {
0177       $this->getBlobInstance($containerName, $blobName, $snapshotId);
0178     } catch (Zend_Service_WindowsAzure_Exception $e) {
0179       return false;
0180     }
0181 
0182     return true;
0183   }
0184 
0185   /**
0186    * Check if a container exists
0187    *
0188    * @param string $containerName Container name
0189    * @return boolean
0190    */
0191   public function containerExists($containerName = '')
0192   {
0193     if ($containerName === '') {
0194       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0195       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0196     }
0197     if (!self::isValidContainerName($containerName)) {
0198       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0199       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.');
0200     }
0201       
0202     // List containers
0203     $containers = $this->listContainers($containerName, 1);
0204     foreach ($containers as $container) {
0205       if ($container->Name == $containerName) {
0206         return true;
0207       }
0208     }
0209 
0210     return false;
0211   }
0212 
0213   /**
0214    * Create container
0215    *
0216    * @param string $containerName Container name
0217    * @param array  $metadata      Key/value pairs of meta data
0218    * @return object Container properties
0219    * @throws Zend_Service_WindowsAzure_Exception
0220    */
0221   public function createContainer($containerName = '', $metadata = array())
0222   {
0223     if ($containerName === '') {
0224       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0225       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0226     }
0227     if (!self::isValidContainerName($containerName)) {
0228       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0229       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.');
0230     }
0231     if (!is_array($metadata)) {
0232       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0233       throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.');
0234     }
0235       
0236     // Create metadata headers
0237     $headers = array();
0238     $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
0239 
0240     // Perform request
0241     $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);
0242     if ($response->isSuccessful()) {
0243       
0244       return new Zend_Service_WindowsAzure_Storage_BlobContainer(
0245       $containerName,
0246       $response->getHeader('Etag'),
0247       $response->getHeader('Last-modified'),
0248       $metadata
0249       );
0250     } else {
0251       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0252       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0253     }
0254   }
0255   
0256   /**
0257    * Create container if it does not exist
0258    *
0259    * @param string $containerName Container name
0260    * @param array  $metadata      Key/value pairs of meta data
0261    * @throws Zend_Service_WindowsAzure_Exception
0262    */
0263   public function createContainerIfNotExists($containerName = '', $metadata = array())
0264   {
0265     if (!$this->containerExists($containerName)) {
0266       $this->createContainer($containerName, $metadata);
0267     }
0268   }
0269 
0270   /**
0271    * Get container ACL
0272    *
0273    * @param string $containerName Container name
0274    * @param bool   $signedIdentifiers Display only private/blob/container or display signed identifiers?
0275    * @return string Acl, to be compared with Zend_Service_WindowsAzure_Storage_Blob::ACL_*
0276    * @throws Zend_Service_WindowsAzure_Exception
0277    */
0278   public function getContainerAcl($containerName = '', $signedIdentifiers = false)
0279   {
0280     if ($containerName === '') {
0281       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0282       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0283     }
0284     if (!self::isValidContainerName($containerName)) {
0285       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0286       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.');
0287     }
0288 
0289     // Perform request
0290     $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);
0291     if ($response->isSuccessful()) {
0292       if ($signedIdentifiers == false)  {
0293         // Only private/blob/container
0294         $accessType = $response->getHeader(Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-public-access');
0295         if (strtolower($accessType) == 'true') {
0296           $accessType = self::ACL_PUBLIC_CONTAINER;
0297         }
0298         return $accessType;
0299       } else {
0300         // Parse result
0301         $result = $this->_parseResponse($response);
0302         if (!$result) {
0303           return array();
0304         }
0305 
0306         $entries = null;
0307         if ($result->SignedIdentifier) {
0308           if (count($result->SignedIdentifier) > 1) {
0309             $entries = $result->SignedIdentifier;
0310           } else {
0311             $entries = array($result->SignedIdentifier);
0312           }
0313         }
0314 
0315         // require_once 'Zend/Service/WindowsAzure/Storage/SignedIdentifier.php';
0316         // Return value
0317         $returnValue = array();
0318         foreach ($entries as $entry) {
0319           $returnValue[] = new Zend_Service_WindowsAzure_Storage_SignedIdentifier(
0320           $entry->Id,
0321           $entry->AccessPolicy ? $entry->AccessPolicy->Start ? $entry->AccessPolicy->Start : '' : '',
0322           $entry->AccessPolicy ? $entry->AccessPolicy->Expiry ? $entry->AccessPolicy->Expiry : '' : '',
0323           $entry->AccessPolicy ? $entry->AccessPolicy->Permission ? $entry->AccessPolicy->Permission : '' : ''
0324           );
0325         }
0326 
0327         // Return
0328         return $returnValue;
0329       }
0330     } else {
0331       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0332       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0333     }
0334   }
0335 
0336   /**
0337    * Set container ACL
0338    *
0339    * @param string $containerName Container name
0340    * @param bool $acl Zend_Service_WindowsAzure_Storage_Blob::ACL_*
0341    * @param array $signedIdentifiers Signed identifiers
0342    * @throws Zend_Service_WindowsAzure_Exception
0343    */
0344   public function setContainerAcl($containerName = '', $acl = self::ACL_PRIVATE, $signedIdentifiers = array())
0345   {
0346     if ($containerName === '') {
0347       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0348       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0349     }
0350     if (!self::isValidContainerName($containerName)) {
0351       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0352       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.');
0353     }
0354 
0355     // Headers
0356     $headers = array();
0357 
0358     // Acl specified?
0359     if ($acl != self::ACL_PRIVATE && !is_null($acl) && $acl != '') {
0360       $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-public-access'] = $acl;
0361     }
0362 
0363     // Policies
0364     $policies = null;
0365     if (is_array($signedIdentifiers) && count($signedIdentifiers) > 0) {
0366       $policies  = '';
0367       $policies .= '<?xml version="1.0" encoding="utf-8"?>' . "\r\n";
0368       $policies .= '<SignedIdentifiers>' . "\r\n";
0369       foreach ($signedIdentifiers as $signedIdentifier) {
0370         $policies .= '  <SignedIdentifier>' . "\r\n";
0371         $policies .= '    <Id>' . $signedIdentifier->Id . '</Id>' . "\r\n";
0372         $policies .= '    <AccessPolicy>' . "\r\n";
0373         if ($signedIdentifier->Start != '')
0374         $policies .= '      <Start>' . $signedIdentifier->Start . '</Start>' . "\r\n";
0375         if ($signedIdentifier->Expiry != '')
0376         $policies .= '      <Expiry>' . $signedIdentifier->Expiry . '</Expiry>' . "\r\n";
0377         if ($signedIdentifier->Permissions != '')
0378         $policies .= '      <Permission>' . $signedIdentifier->Permissions . '</Permission>' . "\r\n";
0379         $policies .= '    </AccessPolicy>' . "\r\n";
0380         $policies .= '  </SignedIdentifier>' . "\r\n";
0381       }
0382       $policies .= '</SignedIdentifiers>' . "\r\n";
0383     }
0384 
0385     // Perform request
0386     $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);
0387     if (!$response->isSuccessful()) {
0388       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0389       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0390     }
0391   }
0392 
0393   /**
0394    * Get container
0395    *
0396    * @param string $containerName  Container name
0397    * @return Zend_Service_WindowsAzure_Storage_BlobContainer
0398    * @throws Zend_Service_WindowsAzure_Exception
0399    */
0400   public function getContainer($containerName = '')
0401   {
0402     if ($containerName === '') {
0403       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0404       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0405     }
0406     if (!self::isValidContainerName($containerName)) {
0407       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0408       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.');
0409     }
0410 
0411     // Perform request
0412     $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);
0413     if ($response->isSuccessful()) {
0414       // Parse metadata
0415       $metadata = $this->_parseMetadataHeaders($response->getHeaders());
0416 
0417       // Return container
0418       return new Zend_Service_WindowsAzure_Storage_BlobContainer(
0419       $containerName,
0420       $response->getHeader('Etag'),
0421       $response->getHeader('Last-modified'),
0422       $metadata
0423       );
0424     } else {
0425       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0426       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0427     }
0428   }
0429 
0430   /**
0431    * Get container metadata
0432    *
0433    * @param string $containerName  Container name
0434    * @return array Key/value pairs of meta data
0435    * @throws Zend_Service_WindowsAzure_Exception
0436    */
0437   public function getContainerMetadata($containerName = '')
0438   {
0439     if ($containerName === '') {
0440       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0441       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0442     }
0443     if (!self::isValidContainerName($containerName)) {
0444       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0445       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.');
0446     }
0447 
0448     return $this->getContainer($containerName)->Metadata;
0449   }
0450 
0451   /**
0452    * Set container metadata
0453    *
0454    * 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.
0455    *
0456    * @param string $containerName      Container name
0457    * @param array  $metadata           Key/value pairs of meta data
0458    * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
0459    * @throws Zend_Service_WindowsAzure_Exception
0460    */
0461   public function setContainerMetadata($containerName = '', $metadata = array(), $additionalHeaders = array())
0462   {
0463     if ($containerName === '') {
0464       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0465       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0466     }
0467     if (!self::isValidContainerName($containerName)) {
0468       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0469       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.');
0470     }
0471     if (!is_array($metadata)) {
0472       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0473       throw new Zend_Service_WindowsAzure_Exception('Meta data should be an array of key and value pairs.');
0474     }
0475     if (count($metadata) == 0) {
0476       return;
0477     }
0478 
0479     // Create metadata headers
0480     $headers = array();
0481     $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
0482 
0483     // Additional headers?
0484     foreach ($additionalHeaders as $key => $value) {
0485       $headers[$key] = $value;
0486     }
0487 
0488     // Perform request
0489     $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);
0490     if (!$response->isSuccessful()) {
0491       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0492       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0493     }
0494   }
0495 
0496   /**
0497    * Delete container
0498    *
0499    * @param string $containerName      Container name
0500    * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
0501    * @throws Zend_Service_WindowsAzure_Exception
0502    */
0503   public function deleteContainer($containerName = '', $additionalHeaders = array())
0504   {
0505     if ($containerName === '') {
0506       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0507       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0508     }
0509     if (!self::isValidContainerName($containerName)) {
0510       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0511       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.');
0512     }
0513       
0514     // Additional headers?
0515     $headers = array();
0516     foreach ($additionalHeaders as $key => $value) {
0517       $headers[$key] = $value;
0518     }
0519 
0520     // Perform request
0521     $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);
0522     if (!$response->isSuccessful()) {
0523       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0524       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0525     }
0526   }
0527 
0528   /**
0529    * List containers
0530    *
0531    * @param string $prefix     Optional. Filters the results to return only containers whose name begins with the specified prefix.
0532    * @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)
0533    * @param string $marker     Optional string value that identifies the portion of the list to be returned with the next list operation.
0534    * @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')
0535    * @param int    $currentResultCount Current result count (internal use)
0536    * @return array
0537    * @throws Zend_Service_WindowsAzure_Exception
0538    */
0539   public function listContainers($prefix = null, $maxResults = null, $marker = null, $include = null, $currentResultCount = 0)
0540   {
0541     // Build query string
0542     $queryString = array('comp=list');
0543     if (!is_null($prefix)) {
0544       $queryString[] = 'prefix=' . $prefix;
0545     }
0546     if (!is_null($maxResults)) {
0547       $queryString[] = 'maxresults=' . $maxResults;
0548     }
0549     if (!is_null($marker)) {
0550       $queryString[] = 'marker=' . $marker;
0551     }
0552     if (!is_null($include)) {
0553       $queryString[] = 'include=' . $include;
0554     }
0555     $queryString = self::createQueryStringFromArray($queryString);
0556      
0557     // Perform request
0558     $response = $this->_performRequest('', $queryString, Zend_Http_Client::GET, array(), false, null, Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_LIST);
0559     if ($response->isSuccessful()) {
0560       $xmlContainers = $this->_parseResponse($response)->Containers->Container;
0561       $xmlMarker = (string)$this->_parseResponse($response)->NextMarker;
0562 
0563       $containers = array();
0564       if (!is_null($xmlContainers)) {
0565         for ($i = 0; $i < count($xmlContainers); $i++) {
0566           
0567           $containers[] = new Zend_Service_WindowsAzure_Storage_BlobContainer(
0568           (string)$xmlContainers[$i]->Name,
0569           (string)$xmlContainers[$i]->Etag,
0570           (string)$xmlContainers[$i]->LastModified,
0571           $this->_parseMetadataElement($xmlContainers[$i])
0572           );
0573         }
0574       }
0575       $currentResultCount = $currentResultCount + count($containers);
0576       if (!is_null($maxResults) && $currentResultCount < $maxResults) {
0577         if (!is_null($xmlMarker) && $xmlMarker != '') {
0578           $containers = array_merge($containers, $this->listContainers($prefix, $maxResults, $xmlMarker, $include, $currentResultCount));
0579         }
0580       }
0581       if (!is_null($maxResults) && count($containers) > $maxResults) {
0582         $containers = array_slice($containers, 0, $maxResults);
0583       }
0584        
0585       return $containers;
0586     } else {
0587       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0588       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0589     }
0590   }
0591 
0592   /**
0593    * Put blob
0594    *
0595    * @param string $containerName      Container name
0596    * @param string $blobName           Blob name
0597    * @param string $localFileName      Local file name to be uploaded
0598    * @param array  $metadata           Key/value pairs of meta data
0599    * @param string $leaseId            Lease identifier
0600    * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
0601    * @return object Partial blob properties
0602    * @throws Zend_Service_WindowsAzure_Exception
0603    */
0604   public function putBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
0605   {
0606     if ($containerName === '') {
0607       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0608       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0609     }
0610     if (!self::isValidContainerName($containerName)) {
0611       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0612       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.');
0613     }
0614     if ($blobName === '') {
0615       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0616       throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
0617     }
0618     if ($localFileName === '') {
0619       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0620       throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
0621     }
0622     if (!file_exists($localFileName)) {
0623       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0624       throw new Zend_Service_WindowsAzure_Exception('Local file not found.');
0625     }
0626     if ($containerName === '$root' && strpos($blobName, '/') !== false) {
0627       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0628       throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
0629     }
0630       
0631     // Check file size
0632     if (filesize($localFileName) >= self::MAX_BLOB_SIZE) {
0633       return $this->putLargeBlob($containerName, $blobName, $localFileName, $metadata, $leaseId, $additionalHeaders);
0634     }
0635 
0636     // Put the data to Windows Azure Storage
0637     return $this->putBlobData($containerName, $blobName, file_get_contents($localFileName), $metadata, $leaseId, $additionalHeaders);
0638   }
0639 
0640   /**
0641    * Put blob data
0642    *
0643    * @param string $containerName      Container name
0644    * @param string $blobName           Blob name
0645    * @param mixed  $data           Data to store
0646    * @param array  $metadata           Key/value pairs of meta data
0647    * @param string $leaseId            Lease identifier
0648    * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
0649    * @return object Partial blob properties
0650    * @throws Zend_Service_WindowsAzure_Exception
0651    */
0652   public function putBlobData($containerName = '', $blobName = '', $data = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
0653   {
0654     if ($containerName === '') {
0655       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0656       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0657     }
0658     if (!self::isValidContainerName($containerName)) {
0659       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0660       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.');
0661     }
0662     if ($blobName === '') {
0663       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0664       throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
0665     }
0666     if ($containerName === '$root' && strpos($blobName, '/') !== false) {
0667       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0668       throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
0669     }
0670 
0671     // Create metadata headers
0672     $headers = array();
0673     if (!is_null($leaseId)) {
0674       $headers['x-ms-lease-id'] = $leaseId;
0675     }
0676     $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
0677 
0678     // Additional headers?
0679     foreach ($additionalHeaders as $key => $value) {
0680       $headers[$key] = $value;
0681     }
0682 
0683     // Specify blob type
0684     $headers[Zend_Service_WindowsAzure_Storage::PREFIX_STORAGE_HEADER . 'blob-type'] = self::BLOBTYPE_BLOCK;
0685 
0686     // Resource name
0687     $resourceName = self::createResourceName($containerName , $blobName);
0688 
0689     // Perform request
0690     $response = $this->_performRequest($resourceName, '', Zend_Http_Client::PUT, $headers, false, $data, Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB, Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_WRITE);
0691     if ($response->isSuccessful()) {
0692       
0693       return new Zend_Service_WindowsAzure_Storage_BlobInstance(
0694       $containerName,
0695       $blobName,
0696       null,
0697       $response->getHeader('Etag'),
0698       $response->getHeader('Last-modified'),
0699       $this->getBaseUrl() . '/' . $containerName . '/' . $blobName,
0700       strlen($data),
0701         '',
0702         '',
0703         '',
0704       false,
0705       $metadata
0706       );
0707     } else {
0708       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0709       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0710     }
0711   }
0712 
0713   /**
0714    * Put large blob (> 64 MB)
0715    *
0716    * @param string $containerName Container name
0717    * @param string $blobName Blob name
0718    * @param string $localFileName Local file name to be uploaded
0719    * @param array  $metadata      Key/value pairs of meta data
0720    * @param string $leaseId       Lease identifier
0721    * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
0722    * @return object Partial blob properties
0723    * @throws Zend_Service_WindowsAzure_Exception
0724    */
0725   public function putLargeBlob($containerName = '', $blobName = '', $localFileName = '', $metadata = array(), $leaseId = null, $additionalHeaders = array())
0726   {
0727     if ($containerName === '') {
0728       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0729       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0730     }
0731     if (!self::isValidContainerName($containerName)) {
0732       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0733       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.');
0734     }
0735     if ($blobName === '') {
0736       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0737       throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
0738     }
0739     if ($localFileName === '') {
0740       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0741       throw new Zend_Service_WindowsAzure_Exception('Local file name is not specified.');
0742     }
0743     if (!file_exists($localFileName)) {
0744       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0745       throw new Zend_Service_WindowsAzure_Exception('Local file not found.');
0746     }
0747     if ($containerName === '$root' && strpos($blobName, '/') !== false) {
0748       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0749       throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
0750     }
0751       
0752     // Check file size
0753     if (filesize($localFileName) < self::MAX_BLOB_SIZE) {
0754       return $this->putBlob($containerName, $blobName, $localFileName, $metadata, $leaseId, $additionalHeaders);
0755     }
0756       
0757     // Determine number of parts
0758     $numberOfParts = ceil( filesize($localFileName) / self::MAX_BLOB_TRANSFER_SIZE );
0759 
0760     // Generate block id's
0761     $blockIdentifiers = array();
0762     for ($i = 0; $i < $numberOfParts; $i++) {
0763       $blockIdentifiers[] = $this->_generateBlockId($i);
0764     }
0765 
0766     // Open file
0767     $fp = fopen($localFileName, 'r');
0768     if ($fp === false) {
0769       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0770       throw new Zend_Service_WindowsAzure_Exception('Could not open local file.');
0771     }
0772       
0773     // Upload parts
0774     for ($i = 0; $i < $numberOfParts; $i++) {
0775       // Seek position in file
0776       fseek($fp, $i * self::MAX_BLOB_TRANSFER_SIZE);
0777         
0778       // Read contents
0779       $fileContents = fread($fp, self::MAX_BLOB_TRANSFER_SIZE);
0780         
0781       // Put block
0782       $this->putBlock($containerName, $blobName, $blockIdentifiers[$i], $fileContents, $leaseId);
0783         
0784       // Dispose file contents
0785       $fileContents = null;
0786       unset($fileContents);
0787     }
0788 
0789     // Close file
0790     fclose($fp);
0791 
0792     // Put block list
0793     $this->putBlockList($containerName, $blobName, $blockIdentifiers, $metadata, $leaseId, $additionalHeaders);
0794 
0795     // Return information of the blob
0796     return $this->getBlobInstance($containerName, $blobName, null, $leaseId);
0797   }
0798     
0799   /**
0800    * Put large blob block
0801    *
0802    * @param string $containerName Container name
0803    * @param string $blobName      Blob name
0804    * @param string $identifier    Block ID
0805    * @param array  $contents      Contents of the block
0806    * @param string $leaseId       Lease identifier
0807    * @throws Zend_Service_WindowsAzure_Exception
0808    */
0809   public function putBlock($containerName = '', $blobName = '', $identifier = '', $contents = '', $leaseId = null)
0810   {
0811     if ($containerName === '') {
0812       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0813       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0814     }
0815     if (!self::isValidContainerName($containerName)) {
0816       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0817       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.');
0818     }
0819     if ($identifier === '') {
0820       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0821       throw new Zend_Service_WindowsAzure_Exception('Block identifier is not specified.');
0822     }
0823     if (strlen($contents) > self::MAX_BLOB_TRANSFER_SIZE) {
0824       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0825       throw new Zend_Service_WindowsAzure_Exception('Block size is too big.');
0826     }
0827     if ($containerName === '$root' && strpos($blobName, '/') !== false) {
0828       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0829       throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
0830     }
0831 
0832     // Headers
0833     $headers = array();
0834     if (!is_null($leaseId)) {
0835       $headers['x-ms-lease-id'] = $leaseId;
0836     }
0837       
0838     // Resource name
0839     $resourceName = self::createResourceName($containerName , $blobName);
0840 
0841     // Upload
0842     $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);
0843     if (!$response->isSuccessful()) {
0844       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0845       throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0846     }
0847   }
0848 
0849   /**
0850    * Put block list
0851    *
0852    * @param string $containerName      Container name
0853    * @param string $blobName           Blob name
0854    * @param array $blockList           Array of block identifiers
0855    * @param array  $metadata           Key/value pairs of meta data
0856    * @param string $leaseId            Lease identifier
0857    * @param array  $additionalHeaders  Additional headers. See http://msdn.microsoft.com/en-us/library/dd179371.aspx for more information.
0858    * @throws Zend_Service_WindowsAzure_Exception
0859    */
0860   public function putBlockList($containerName = '', $blobName = '', $blockList = array(), $metadata = array(), $leaseId = null, $additionalHeaders = array())
0861   {
0862     if ($containerName === '') {
0863       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0864       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0865     }
0866     if (!self::isValidContainerName($containerName)) {
0867       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0868       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.');
0869     }
0870     if ($blobName === '') {
0871       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0872       throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
0873     }
0874     if (count($blockList) == 0) {
0875       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0876       throw new Zend_Service_WindowsAzure_Exception('Block list does not contain any elements.');
0877     }
0878     if ($containerName === '$root' && strpos($blobName, '/') !== false) {
0879       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0880       throw new Zend_Service_WindowsAzure_Exception('Blobs stored in the root container can not have a name containing a forward slash (/).');
0881     }
0882 
0883     // Generate block list
0884     $blocks = '';
0885     foreach ($blockList as $block) {
0886       $blocks .= '  <Latest>' . base64_encode($block) . '</Latest>' . "\n";
0887     }
0888 
0889     // Generate block list request
0890     $fileContents = utf8_encode(implode("\n", array(
0891         '<?xml version="1.0" encoding="utf-8"?>',
0892         '<BlockList>',
0893         $blocks,
0894         '</BlockList>'
0895       )));
0896 
0897       // Create metadata headers
0898       $headers = array();
0899       if (!is_null($leaseId)) {
0900         $headers['x-ms-lease-id'] = $leaseId;
0901       }
0902       $headers = array_merge($headers, $this->_generateMetadataHeaders($metadata));
0903 
0904       // Additional headers?
0905       foreach ($additionalHeaders as $key => $value) {
0906         $headers[$key] = $value;
0907       }
0908 
0909       // Resource name
0910       $resourceName = self::createResourceName($containerName , $blobName);
0911 
0912       // Perform request
0913       $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);
0914       if (!$response->isSuccessful()) {
0915         // require_once 'Zend/Service/WindowsAzure/Exception.php';
0916         throw new Zend_Service_WindowsAzure_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0917       }
0918   }
0919 
0920   /**
0921    * Get block list
0922    *
0923    * @param string $containerName Container name
0924    * @param string $blobName      Blob name
0925    * @param string $snapshotId    Snapshot identifier
0926    * @param string $leaseId       Lease identifier
0927    * @param integer $type         Type of block list to retrieve. 0 = all, 1 = committed, 2 = uncommitted
0928    * @return array
0929    * @throws Zend_Service_WindowsAzure_Exception
0930    */
0931   public function getBlockList($containerName = '', $blobName = '', $snapshotId = null, $leaseId = null, $type = 0)
0932   {
0933     if ($containerName === '') {
0934       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0935       throw new Zend_Service_WindowsAzure_Exception('Container name is not specified.');
0936     }
0937     if (!self::isValidContainerName($containerName)) {
0938       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0939       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.');
0940     }
0941     if ($blobName === '') {
0942       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0943       throw new Zend_Service_WindowsAzure_Exception('Blob name is not specified.');
0944     }
0945     if ($type < 0 || $type > 2) {
0946       // require_once 'Zend/Service/WindowsAzure/Exception.php';
0947       throw new Zend_Service_WindowsAzure_Exception('Invalid type of block list to retrieve.');
0948     }
0949 
0950     // Set $blockListType
0951     $blockListType = 'all';
0952     if ($type == 1) {
0953       $blockListType = 'committed';
0954     }
0955     if ($type == 2) {
0956       $blockListType = 'uncommitted';
0957     }
0958 
0959     // Headers
0960     $headers = array();
0961     if (!is_null($leaseId)) {
0962       $headers['x-ms-lease-id'] = $leaseId;
0963     }
0964 
0965     // Build query string
0966     $queryString = array('comp=blocklist', 'blocklisttype=' . $blockListType);
0967     if (!is_null($snapshotId)) {
0968       $queryString[] = 'snapshot=' . $snapshotId;
0969     }
0970     $queryString = self::createQueryStringFromArray($queryString);
0971 
0972     // Resource name
0973     $resourceName = self::createResourceName($containerName , $blobName);
0974       
0975     // Perform request
0976     $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);
0977     if ($response->isSuccessful()) {
0978       // Parse response
0979       $blockList = $this->_parseResponse($response);
0980 
0981       // Create return value
0982       $returnValue = array();
0983       if ($blockList->CommittedBlocks) {
0984         foreach ($blockList->CommittedBlocks->Block as $block) {
0985           $returnValue['CommittedBlocks'][] = (object)array(
0986                   'Name' => (string)$block->Name,
0987                   'Size' => (string)$block->Size
0988           );
0989         }
0990       }
0991       if ($blockList->UncommittedBlocks)  {
0992         foreach ($blockList->UncommittedBlocks->Block as $block) {
0993           $returnValue['UncommittedBlocks'][] = (object)array(
0994                   'Name' => (string)$block->Name,
0995                   'Size' => (string)$block->Size
0996           );
0997         }
0998       }
0999 
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 }