hello 404 Not Found
Al-HUWAITI Shell
Al-huwaiti


Server : Apache
System : Linux webm006.cluster103.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User : chryzalihi ( 621211)
PHP Version : 8.3.31
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
Directory :  /home/c/h/r/chryzalihi/www/wp-content/languages/themes/the/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/c/h/r/chryzalihi/www/wp-content/languages/themes/the/ObjectStore.tar
Enum/ReturnType.php000060400000001526152441707310010306 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Enum;

/**
 * Enumerated types for return types
 *
 * @package OpenCloud\ObjectStore\Enum
 */
class ReturnType
{
    const RESPONSE_ARRAY = 'RESPONSE_ARRAY';
    const DATA_OBJECT_ARRAY = 'DATA_OBJECT_ARRAY';
}
Enum/.htaccess000044400000000424152441707310007250 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Constants/Header.php000060400000001712152441707310010422 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Constants;

/**
 * Constants for different request and metadata headers.
 */
class Header
{
    const OBJECT_COUNT = 'Object-Count';
    const BYTES_USED = 'Bytes-Used';
    const ACCESS_LOGS = 'Access-Log-Delivery';

    const TRANS_ID = 'Trans-Id';
    const ENABLED = 'Enabled';
    const LOG_RETENTION = 'Log-Retention';
}
Constants/.htaccess000044400000000424152441707310010320 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Constants/UrlType.php000060400000001663152441707310010643 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Constants;

/**
 * Enumerated constants used in CloudFiles for URL types.
 */
class UrlType
{
    const CDN = 'CDN';
    const SSL = 'SSL';
    const STREAMING = 'Streaming';
    const IOS_STREAMING = 'IOS-Streaming';

    const TAR = 'tar';
    const TAR_GZ = 'tar.gz';
    const TAR_BZ2 = 'tar.bz2';
}
Upload/AbstractTransfer.php000060400000013452152441707310011756 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use Exception;
use Guzzle\Http\EntityBody;
use OpenCloud\Common\Exceptions\RuntimeException;
use OpenCloud\Common\Http\Client;
use OpenCloud\ObjectStore\Exception\UploadException;

/**
 * Contains abstract functionality for transfer objects.
 */
class AbstractTransfer
{
    /**
     * Minimum chunk size is 1MB.
     */
    const MIN_PART_SIZE = 1048576;

    /**
     * Maximum chunk size is 5GB.
     */
    const MAX_PART_SIZE = 5368709120;

    /**
     * Default chunk size is 1GB.
     */
    const DEFAULT_PART_SIZE = 1073741824;

    /**
     * @var \OpenCloud\Common\Http\Client The client object which handles all HTTP interactions
     */
    protected $client;

    /**
     * @var \Guzzle\Http\EntityBody The payload being transferred
     */
    protected $entityBody;

    /**
     * The current state of the transfer responsible for, among other things, holding an itinerary of uploaded parts
     *
     * @var \OpenCloud\ObjectStore\Upload\TransferState
     */
    protected $transferState;

    /**
     * @var array User-defined key/pair options
     */
    protected $options;

    /**
     * @var int
     */
    protected $partSize;

    /**
     * @var array Defaults that will always override user-defined options
     */
    protected $defaultOptions = array(
        'concurrency'    => true,
        'partSize'       => self::DEFAULT_PART_SIZE,
        'prefix'         => 'segment',
        'doPartChecksum' => true
    );

    /**
     * @return static
     */
    public static function newInstance()
    {
        return new static();
    }

    /**
     * @param Client $client
     * @return $this
     */
    public function setClient(Client $client)
    {
        $this->client = $client;

        return $this;
    }

    /**
     * @param EntityBody $entityBody
     * @return $this
     */
    public function setEntityBody(EntityBody $entityBody)
    {
        $this->entityBody = $entityBody;

        return $this;
    }

    /**
     * @param TransferState $transferState
     * @return $this
     */
    public function setTransferState(TransferState $transferState)
    {
        $this->transferState = $transferState;

        return $this;
    }

    /**
     * @return array
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * @param $options
     * @return $this
     */
    public function setOptions($options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * @param $option The key being updated
     * @param $value  The option's value
     * @return $this
     */
    public function setOption($option, $value)
    {
        $this->options[$option] = $value;

        return $this;
    }

    public function getPartSize()
    {
        return $this->partSize;
    }

    /**
     * @return $this
     */
    public function setup()
    {
        $this->options = array_merge($this->defaultOptions, $this->options);
        $this->partSize = $this->validatePartSize();

        return $this;
    }

    /**
     * Make sure the part size falls within a valid range
     *
     * @return mixed
     */
    protected function validatePartSize()
    {
        $min = min($this->options['partSize'], self::MAX_PART_SIZE);

        return max($min, self::MIN_PART_SIZE);
    }

    /**
     * Initiates the upload procedure.
     *
     * @return \Guzzle\Http\Message\Response
     * @throws RuntimeException If the transfer is not in a "running" state
     * @throws UploadException  If any errors occur during the upload
     * @codeCoverageIgnore
     */
    public function upload()
    {
        if (!$this->transferState->isRunning()) {
            throw new RuntimeException('The transfer has been aborted.');
        }

        try {
            $this->transfer();
            $response = $this->createManifest();
        } catch (Exception $e) {
            throw new UploadException($this->transferState, $e);
        }

        return $response;
    }

    /**
     * With large uploads, you must create a manifest file. Although each segment or TransferPart remains
     * individually addressable, the manifest file serves as the unified file (i.e. the 5GB download) which, when
     * retrieved, streams all the segments concatenated.
     *
     * @link http://docs.rackspace.com/files/api/v1/cf-devguide/content/Large_Object_Creation-d1e2019.html
     * @return \Guzzle\Http\Message\Response
     * @codeCoverageIgnore
     */
    private function createManifest()
    {
        $parts = array();

        foreach ($this->transferState as $part) {
            $parts[] = (object) array(
                'path'       => $part->getPath(),
                'etag'       => $part->getETag(),
                'size_bytes' => $part->getContentLength()
            );
        }

        $headers = array(
            'Content-Length'    => 0,
            'X-Object-Manifest' => sprintf('%s/%s/%s/',
                $this->options['containerName'],
                $this->options['objectName'],
                $this->options['prefix']
            )
        );

        $url = clone $this->options['containerUrl'];
        $url->addPath($this->options['objectName']);

        return $this->client->put($url, $headers)->send();
    }
}
Upload/.htaccess000044400000000424152441707310007570 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Upload/ContainerMigration.php000060400000013107152441707310012277 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use Guzzle\Batch\BatchBuilder;
use Guzzle\Common\Collection;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Url;
use OpenCloud\ObjectStore\Resource\Container;

/**
 * Class responsible for migrating the contents of one container to another
 *
 * @package OpenCloud\ObjectStore\Upload
 */
class ContainerMigration
{
    /** @var \Guzzle\Batch\Batch */
    protected $readQueue;

    /** @var \Guzzle\Batch\Batch */
    protected $writeQueue;

    /** @var \OpenCloud\ObjectStore\Resource\Container */
    protected $oldContainer;

    /** @var \OpenCloud\ObjectStore\Resource\Container */
    protected $newContainer;

    /** @var \Guzzle\Common\Collection */
    protected $options = array();

    protected $defaults = array(
        'read.batchLimit'  => 1000,
        'read.pageLimit'   => 10000,
        'write.batchLimit' => 100
    );

    /**
     * @param Container $old     Source container
     * @param Container $new     Target container
     * @param array     $options Options that configure process
     * @return ContainerMigration
     */
    public static function factory(Container $old, Container $new, array $options = array())
    {
        $migration = new self();

        $migration->setOldContainer($old);
        $migration->setNewContainer($new);
        $migration->setOptions($options);

        $migration->setupReadQueue();
        $migration->setupWriteQueue();

        return $migration;
    }

    /**
     * @param Container $old
     */
    public function setOldContainer(Container $old)
    {
        $this->oldContainer = $old;
    }

    /**
     * @return Container
     */
    public function getOldContainer()
    {
        return $this->oldContainer;
    }

    /**
     * @param Container $new
     */
    public function setNewContainer(Container $new)
    {
        $this->newContainer = $new;
    }

    /**
     * @return Container
     */
    public function getNewContainer()
    {
        return $this->newContainer;
    }

    /**
     * @param array $options
     */
    public function setOptions(array $options)
    {
        $this->options = Collection::fromConfig($options, $this->defaults);
    }

    /**
     * @return \Guzzle\Common\Collection
     */
    public function getOptions()
    {
        return $this->options;
    }

    /**
     * Set the read queue as a {@see \Guzzle\Batch\Batch} queue using the {@see \Guzzle\Batch\BatchBuilder}
     */
    public function setupReadQueue()
    {
        $this->readQueue = BatchBuilder::factory()
            ->transferRequests($this->options->get('read.batchLimit'))
            ->build();
    }

    /**
     * Set the write queue as a {@see \Guzzle\Batch\Batch} queue using the {@see \Guzzle\Batch\BatchBuilder}
     */
    public function setupWriteQueue()
    {
        $this->writeQueue = BatchBuilder::factory()
            ->transferRequests($this->options->get('write.batchLimit'))
            ->build();
    }

    /**
     * @return \Guzzle\Http\ClientInterface
     */
    private function getClient()
    {
        return $this->newContainer->getService()->getClient();
    }

    /**
     * Create a collection of files to be migrated and add them to the read queue
     */
    protected function enqueueGetRequests()
    {
        $files = $this->oldContainer->objectList(array(
            'limit.total' => false,
            'limit.page'  => $this->options->get('read.pageLimit')
        ));

        foreach ($files as $file) {
            $this->readQueue->add(
                $this->getClient()->get($file->getUrl())
            );
        }
    }

    /**
     * Send the read queue (in order to gather more information about individual files)
     *
     * @return array Responses
     */
    protected function sendGetRequests()
    {
        $this->enqueueGetRequests();

        return $this->readQueue->flush();
    }

    /**
     * Create a tailored PUT request for each file
     *
     * @param Response $response
     * @return \Guzzle\Http\Message\EntityEnclosingRequestInterface
     */
    protected function createPutRequest(Response $response)
    {
        $segments = Url::factory($response->getEffectiveUrl())->getPathSegments();
        $name = end($segments);

        // Retrieve content and metadata
        $file = $this->newContainer->dataObject()->setName($name);
        $file->setMetadata($response->getHeaders(), true);

        return $this->getClient()->put(
            $file->getUrl(),
            $file::stockHeaders($file->getMetadata()->toArray()),
            $response->getBody()
        );
    }

    /**
     * Initiate the transfer process
     *
     * @return array PUT responses
     */
    public function transfer()
    {
        $requests = $this->sendGetRequests();
        $this->readQueue = null;

        foreach ($requests as $key => $request) {
            $this->writeQueue->add(
                $this->createPutRequest($request->getResponse())
            );
            unset($requests[$key]);
        }

        return $this->writeQueue->flush();
    }
}
Upload/TransferPart.php000060400000010371152441707310011116 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use Guzzle\Http\Message\Response;
use Guzzle\Http\Url;
use OpenCloud\Common\Constants\Header;

/**
 * Represents an individual part of the EntityBody being uploaded.
 *
 * @codeCoverageIgnore
 */
class TransferPart
{
    /**
     * @var int Its position in the upload queue.
     */
    protected $partNumber;

    /**
     * @var string This upload's ETag checksum.
     */
    protected $eTag;

    /**
     * @var int The length of this upload in bytes.
     */
    protected $contentLength;

    /**
     * @var string The API path of this upload.
     */
    protected $path;

    /**
     * @param int $contentLength
     * @return $this
     */
    public function setContentLength($contentLength)
    {
        $this->contentLength = $contentLength;

        return $this;
    }

    /**
     * @return int
     */
    public function getContentLength()
    {
        return $this->contentLength;
    }

    /**
     * @param  string $etag
     * @return $this
     */
    public function setETag($etag)
    {
        $this->etag = $etag;

        return $this;
    }

    /**
     * @return string
     */
    public function getETag()
    {
        return $this->etag;
    }

    /**
     * @param int $partNumber
     * @return $this
     */
    public function setPartNumber($partNumber)
    {
        $this->partNumber = $partNumber;

        return $this;
    }

    /**
     * @return int
     */
    public function getPartNumber()
    {
        return $this->partNumber;
    }

    /**
     * @param $path
     * @return $this
     */
    public function setPath($path)
    {
        $this->path = $path;

        return $this;
    }

    /**
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }

    /**
     * Create the request needed for this upload to the API.
     *
     * @param EntityBody $part    The entity body being uploaded
     * @param int        $number  Its number/position, needed for name
     * @param OpenStack  $client  Client responsible for issuing requests
     * @param array      $options Set by the Transfer object
     * @return OpenCloud\Common\Http\Request
     */
    public static function createRequest($part, $number, $client, $options)
    {
        $name = sprintf('%s/%s/%d', $options['objectName'], $options['prefix'], $number);
        $url = clone $options['containerUrl'];
        $url->addPath($name);

        $headers = array(
            Header::CONTENT_LENGTH => $part->getContentLength(),
            Header::CONTENT_TYPE   => $part->getContentType()
        );

        if ($options['doPartChecksum'] === true) {
            $headers['ETag'] = $part->getContentMd5();
        }

        $request = $client->put($url, $headers, $part);

        if (isset($options['progress'])) {
            $request->getCurlOptions()->add('progress', true);
            if (is_callable($options['progress'])) {
                $request->getCurlOptions()->add('progressCallback', $options['progress']);
            }
        }

        return $request;
    }

    /**
     * Construct a TransferPart from a HTTP response delivered by the API.
     *
     * @param Response $response
     * @param int      $partNumber
     * @return TransferPart
     */
    public static function fromResponse(Response $response, $partNumber = 1)
    {
        $responseUri = Url::factory($response->getEffectiveUrl());

        $object = new self();

        $object->setPartNumber($partNumber)
            ->setContentLength($response->getHeader(Header::CONTENT_LENGTH))
            ->setETag($response->getHeader(Header::ETAG))
            ->setPath($responseUri->getPath());

        return $object;
    }
}
Upload/ConsecutiveTransfer.php000060400000004425152441707310012502 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use Guzzle\Http\EntityBody;
use Guzzle\Http\ReadLimitEntityBody;
use OpenCloud\Common\Constants\Size;

/**
 * A transfer type which executes consecutively - i.e. it will upload an entire EntityBody and then move on to the next
 * in a linear fashion. There is no concurrency here.
 *
 * @codeCoverageIgnore
 */
class ConsecutiveTransfer extends AbstractTransfer
{
    public function transfer()
    {
        while (!$this->entityBody->isConsumed()) {
            if ($this->entityBody->getContentLength() && $this->entityBody->isSeekable()) {
                // Stream directly from the data
                $body = new ReadLimitEntityBody($this->entityBody, $this->partSize, $this->entityBody->ftell());
            } else {
                // If not-seekable, read the data into a new, seekable "buffer"
                $body = EntityBody::factory();
                $output = true;
                while ($body->getContentLength() < $this->partSize && $output !== false) {
                    // Write maximum of 10KB at a time
                    $length = min(10 * Size::KB, $this->partSize - $body->getContentLength());
                    $output = $body->write($this->entityBody->read($length));
                }
            }

            if ($body->getContentLength() == 0) {
                break;
            }

            $request = TransferPart::createRequest(
                $body,
                $this->transferState->count() + 1,
                $this->client,
                $this->options
            );

            $response = $request->send();

            $this->transferState->addPart(TransferPart::fromResponse($response));
        }
    }
}
Upload/DirectorySync.php000060400000015100152441707310011277 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use DirectoryIterator;
use Guzzle\Http\EntityBody;
use OpenCloud\Common\Collection\ResourceIterator;
use OpenCloud\Common\Exceptions\InvalidArgumentError;
use OpenCloud\ObjectStore\Resource\Container;

/**
 * DirectorySync upload class, in charge of creating, replacing and delete data objects on the API. The goal of
 * this execution is to sync local directories with remote CloudFiles containers so that they are consistent.
 *
 * @package OpenCloud\ObjectStore\Upload
 */
class DirectorySync
{
    /**
     * @var string The path to the directory you're syncing.
     */
    private $basePath;
    /**
     * @var ResourceIterator A collection of remote files in Swift.
     */
    private $remoteFiles;
    /**
     * @var AbstractContainer The Container object you are syncing.
     */
    private $container;

    /**
     * Basic factory method to instantiate a new DirectorySync object with all the appropriate properties.
     *
     * @param           $path      The local path
     * @param Container $container The container you're syncing
     * @return DirectorySync
     */
    public static function factory($path, Container $container)
    {
        $transfer = new self();
        $transfer->setBasePath($path);
        $transfer->setContainer($container);
        $transfer->setRemoteFiles($container->objectList());

        return $transfer;
    }

    /**
     * @param $path
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     */
    public function setBasePath($path)
    {
        if (!file_exists($path)) {
            throw new InvalidArgumentError(sprintf('%s does not exist', $path));
        }

        $this->basePath = $path;
    }

    /**
     * @param ResourceIterator $remoteFiles
     */
    public function setRemoteFiles(ResourceIterator $remoteFiles)
    {
        $this->remoteFiles = $remoteFiles;
    }

    /**
     * @param Container $container
     */
    public function setContainer(Container $container)
    {
        $this->container = $container;
    }

    /**
     * Execute the sync process. This will collect all the remote files from the API and do a comparison. There are
     * four scenarios that need to be dealt with:
     *
     * - Exists locally, exists remotely (identical checksum) = no action
     * - Exists locally, exists remotely (diff checksum) = local overwrites remote
     * - Exists locally, not exists remotely = local is written to remote
     * - Not exists locally, exists remotely = remote file is deleted
     */
    public function execute()
    {
        $localFiles = $this->traversePath($this->basePath);

        $this->remoteFiles->rewind();
        $this->remoteFiles->populateAll();

        $entities = array();
        $requests = array();
        $deletePaths = array();

        // Handle PUT requests (create/update files)
        foreach ($localFiles as $filename) {
            $callback = $this->getCallback($filename);
            $filePath = rtrim($this->basePath, '/') . '/' . $filename;

            if (!is_readable($filePath)) {
                continue;
            }

            $entities[] = $entityBody = EntityBody::factory(fopen($filePath, 'r+'));

            if (false !== ($remoteFile = $this->remoteFiles->search($callback))) {
                // if different, upload updated version
                if ($remoteFile->getEtag() != $entityBody->getContentMd5()) {
                    $requests[] = $this->container->getClient()->put(
                        $remoteFile->getUrl(),
                        $remoteFile->getMetadata()->toArray(),
                        $entityBody
                    );
                }
            } else {
                // upload new file
                $url = clone $this->container->getUrl();
                $url->addPath($filename);

                $requests[] = $this->container->getClient()->put($url, array(), $entityBody);
            }
        }

        // Handle DELETE requests
        foreach ($this->remoteFiles as $remoteFile) {
            $remoteName = $remoteFile->getName();
            if (!in_array($remoteName, $localFiles)) {
                $deletePaths[] = sprintf('/%s/%s', $this->container->getName(), $remoteName);
            }
        }

        // send update/create requests
        if (count($requests)) {
            $this->container->getClient()->send($requests);
        }

        // bulk delete
        if (count($deletePaths)) {
            $this->container->getService()->bulkDelete($deletePaths);
        }

        // close all streams
        if (count($entities)) {
            foreach ($entities as $entity) {
                $entity->close();
            }
        }
    }

    /**
     * Given a path, traverse it recursively for nested files.
     *
     * @param $path
     * @return array
     */
    private function traversePath($path)
    {
        $filenames = array();

        $directory = new DirectoryIterator($path);

        foreach ($directory as $file) {
            if ($file->isDot()) {
                continue;
            }
            if ($file->isDir()) {
                $filenames = array_merge($filenames, $this->traversePath($file->getPathname()));
            } else {
                $filenames[] = $this->trimFilename($file);
            }
        }

        return $filenames;
    }

    /**
     * Given a path, trim away leading slashes and strip the base path.
     *
     * @param $file
     * @return string
     */
    private function trimFilename($file)
    {
        return ltrim(str_replace($this->basePath, '', $file->getPathname()), '/');
    }

    /**
     * Get the callback used to do a search function on the remote iterator.
     *
     * @param $name     The name of the file we're looking for.
     * @return callable
     */
    private function getCallback($name)
    {
        $name = trim($name, '/');

        return function ($remoteFile) use ($name) {
            if ($remoteFile->getName() == $name) {
                return true;
            }

            return false;
        };
    }
}
Upload/TransferState.php000060400000003313152441707310011266 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

/**
 * Represents the current state of the Transfer.
 *
 * @codeCoverageIgnore
 */
class TransferState
{
    /**
     * @var array Holds all of the parts which have been transferred.
     */
    protected $completedParts = array();

    /**
     * @var bool
     */
    protected $running;

    /**
     * @return $this
     */
    public static function factory()
    {
        $self = new self();

        return $self->init();
    }

    /**
     * @param TransferPart $part
     */
    public function addPart(TransferPart $part)
    {
        $this->completedParts[] = $part;
    }

    /**
     * @return int
     */
    public function count()
    {
        return count($this->completedParts);
    }

    /**
     * @return bool
     */
    public function isRunning()
    {
        return $this->running;
    }

    /**
     * @return $this
     */
    public function init()
    {
        $this->running = true;

        return $this;
    }

    /**
     * @return $this
     */
    public function cancel()
    {
        $this->running = false;

        return $this;
    }
}
Upload/TransferBuilder.php000060400000007263152441707310011604 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use Guzzle\Http\EntityBody;
use OpenCloud\Common\Exceptions\InvalidArgumentError;
use OpenCloud\ObjectStore\Resource\Container;

/**
 * Factory which creates Transfer objects, either ConcurrentTransfer or ConsecutiveTransfer.
 */
class TransferBuilder
{
    /**
     * @var Container The container being uploaded to
     */
    protected $container;

    /**
     * @var EntityBody The data payload.
     */
    protected $entityBody;

    /**
     * @var array A key/value pair of options.
     */
    protected $options = array();

    /**
     * @return TransferBuilder
     */
    public static function newInstance()
    {
        return new self();
    }

    /**
     * @param type $options Available configuration options:
     *
     * * `concurrency'    <bool>   The number of concurrent workers.
     * * `partSize'       <int>    The size, in bytes, for the chunk
     * * `doPartChecksum' <bool>   Enable or disable MD5 checksum in request (ETag)
     *
     * If you are uploading FooBar, its chunks will have the following naming structure:
     *
     * FooBar/1
     * FooBar/2
     * FooBar/3
     *
     * @return \OpenCloud\ObjectStore\Upload\UploadBuilder
     */
    public function setOptions($options)
    {
        $this->options = $options;

        return $this;
    }

    /**
     * @param $key   The option name
     * @param $value The option value
     * @return $this
     */
    public function setOption($key, $value)
    {
        $this->options[$key] = $value;

        return $this;
    }

    /**
     * @param Container $container
     * @return $this
     */
    public function setContainer(Container $container)
    {
        $this->container = $container;

        return $this;
    }

    /**
     * @param EntityBody $entityBody
     * @return $this
     */
    public function setEntityBody(EntityBody $entityBody)
    {
        $this->entityBody = $entityBody;

        return $this;
    }

    /**
     * Build the transfer.
     *
     * @return mixed
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     */
    public function build()
    {
        // Validate properties
        if (!$this->container || !$this->entityBody || !$this->options['objectName']) {
            throw new InvalidArgumentError('A container, entity body and object name must be set');
        }

        // Create TransferState object for later use
        $transferState = TransferState::factory();

        // Instantiate Concurrent-/ConsecutiveTransfer
        $transferClass = isset($this->options['concurrency']) && $this->options['concurrency'] > 1
            ? __NAMESPACE__ . '\\ConcurrentTransfer'
            : __NAMESPACE__ . '\\ConsecutiveTransfer';

        return $transferClass::newInstance()
            ->setClient($this->container->getClient())
            ->setEntityBody($this->entityBody)
            ->setTransferState($transferState)
            ->setOptions($this->options)
            ->setOption('containerName', $this->container->getName())
            ->setOption('containerUrl', $this->container->getUrl())
            ->setup();
    }
}
Upload/ConcurrentTransfer.php000060400000006746152441707310012345 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Upload;

use Guzzle\Http\EntityBody;
use Guzzle\Http\ReadLimitEntityBody;

/**
 * A transfer type which executes in a concurrent fashion, i.e. with multiple workers uploading at once. Each worker is
 * charged with uploading a particular chunk of data. The entity body is fragmented into n pieces - calculated by
 * dividing the total size by the individual part size.
 *
 * @codeCoverageIgnore
 */
class ConcurrentTransfer extends AbstractTransfer
{
    public function transfer()
    {
        $totalParts = (int) ceil($this->entityBody->getContentLength() / $this->partSize);
        $workers = min($totalParts, $this->options['concurrency']);
        $parts = $this->collectParts($workers);

        while ($this->transferState->count() < $totalParts) {
            $completedParts = $this->transferState->count();
            $requests = array();

            // Iterate over number of workers until total completed parts is what we need it to be
            for ($i = 0; $i < $workers && ($completedParts + $i) < $totalParts; $i++) {
                // Offset is the current pointer multiplied by the standard chunk length
                $offset = ($completedParts + $i) * $this->partSize;
                $parts[$i]->setOffset($offset);

                // If this segment is empty (i.e. buffering a half-full chunk), break the iteration
                if ($parts[$i]->getContentLength() == 0) {
                    break;
                }

                // Add this to the request queue for later processing
                $requests[] = TransferPart::createRequest(
                    $parts[$i],
                    $this->transferState->count() + $i + 1,
                    $this->client,
                    $this->options
                );
            }

            // Iterate over our queued requests and process them
            foreach ($this->client->send($requests) as $response) {
                // Add this part to the TransferState
                $this->transferState->addPart(TransferPart::fromResponse($response));
            }
        }
    }

    /**
     * Partitions the entity body into an array - each worker is represented by a key, and the value is a
     * ReadLimitEntityBody object, whose read limit is fixed based on this object's partSize value. This will always
     * ensure the chunks are sent correctly.
     *
     * @param int    The total number of workers
     * @return array The worker array
     */
    private function collectParts($workers)
    {
        $uri = $this->entityBody->getUri();

        $array = array(new ReadLimitEntityBody($this->entityBody, $this->partSize));

        for ($i = 1; $i < $workers; $i++) {
            // Need to create a fresh EntityBody, otherwise you'll get weird 408 responses
            $array[] = new ReadLimitEntityBody(new EntityBody(fopen($uri, 'r')), $this->partSize);
        }

        return $array;
    }
}
CDNService.php000060400000003524152441707310007206 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore;

use OpenCloud\ObjectStore\Resource\CDNContainer;
use OpenCloud\ObjectStore\Resource\ContainerMetadata;

/**
 * This is the CDN version of the ObjectStore service.
 */
class CDNService extends AbstractService
{
    const DEFAULT_NAME = 'cloudFilesCDN';
    const DEFAULT_TYPE = 'rax:object-cdn';

    /**
     * List CDN-enabled containers.
     *
     * @param array $filter
     * @return \OpenCloud\Common\Collection\PaginatedIterator
     */
    public function listContainers(array $filter = array())
    {
        $filter['format'] = 'json';
        return $this->resourceList('CDNContainer', $this->getUrl(null, $filter), $this);
    }

    public function cdnContainer($data)
    {
        $container = new CDNContainer($this, $data);

        $metadata = new ContainerMetadata();
        $metadata->setArray(array(
            'Streaming-Uri' => $data->cdn_streaming_uri,
            'Ios-Uri' => $data->cdn_ios_uri,
            'Ssl-Uri' => $data->cdn_ssl_uri,
            'Enabled' => $data->cdn_enabled,
            'Ttl' => $data->ttl,
            'Log-Retention' => $data->log_retention,
            'Uri' => $data->cdn_uri,
        ));

        $container->setMetadata($metadata);

        return $container;
    }
}
Exception/ObjectNotFoundException.php000060400000002205152441707310013754 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Exception;

class ObjectNotFoundException extends \RuntimeException
{
    public static function factory($name, \Exception $exception)
    {
        $message = sprintf(
            "%s could not be found. The API returned this HTTP response:\n\n%s",
            $name,
            (string) $exception->getResponse()
        );

        $e = new self($message);

        $e->name = $name;
        $e->response = $exception->getResponse();
        $e->request = $exception->getRequest();

        return $e;
    }
}
Exception/ContainerException.php000060400000001277152441707310013023 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Exception;

class ContainerException extends \Exception
{
}
Exception/StreamException.php000060400000001274152441707310012331 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Exception;

class StreamException extends \Exception
{
}
Exception/UploadException.php000060400000002074152441707310012321 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Exception;

class UploadException extends \Exception
{
    protected $state;

    public function __construct($state, \Exception $exception = null)
    {
        parent::__construct(
            'An error was encountered while performing an upload: ' . $exception->getMessage(),
            0,
            $exception
        );

        $this->state = $state;
    }

    public function getState()
    {
        return $this->state;
    }
}
Exception/BulkOperationException.php000060400000001772152441707310013657 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Exception;

class BulkOperationException extends \Exception
{
    public function __construct(array $errors)
    {
        $output = '';

        foreach ($errors as $error) {
            $output .= "$error[0]: $error[1]" . PHP_EOL;
        }

        parent::__construct(
            'These errors occurred while performing an archive upload: ' . $output
        );
    }
}
Exception/.htaccess000044400000000424152441707310010302 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Resource/Account.php000060400000005216152441707310010444 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

/**
 * Represents an account that interacts with the CloudFiles API.
 *
 * @link http://docs.rackspace.com/files/api/v1/cf-devguide/content/Accounts-d1e421.html
 */
class Account extends AbstractResource
{
    const METADATA_LABEL = 'Account';

    /**
     * @var string The temporary URL secret for this account
     */
    private $tempUrlSecret;

    public function getUrl($path = null, array $query = array())
    {
        return $this->getService()->getUrl();
    }

    /**
     * Convenience method.
     *
     * @return \OpenCloud\Common\Metadata
     */
    public function getDetails()
    {
        return $this->retrieveMetadata();
    }

    /**
     * @return null|string|int
     */
    public function getObjectCount()
    {
        return $this->metadata->getProperty('Object-Count');
    }

    /**
     * @return null|string|int
     */
    public function getContainerCount()
    {
        return $this->metadata->getProperty('Container-Count');
    }

    /**
     * @return null|string|int
     */
    public function getBytesUsed()
    {
        return $this->metadata->getProperty('Bytes-Used');
    }

    /**
     * Sets the secret value for the temporary URL.
     *
     * @link http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html
     *
     * @param null $secret The value to set the secret to. If left blank, a random hash is generated.
     * @return $this
     */
    public function setTempUrlSecret($secret = null)
    {
        if (!$secret) {
            $secret = sha1(rand(1, 99999));
        }

        $this->tempUrlSecret = $secret;

        $this->saveMetadata($this->appendToMetadata(array('Temp-Url-Key' => $secret)));

        return $this;
    }

    /**
     * @return null|string
     */
    public function getTempUrlSecret()
    {
        if (null === $this->tempUrlSecret) {
            $this->retrieveMetadata();
            $this->tempUrlSecret = $this->metadata->getProperty('Temp-Url-Key');
        }

        return $this->tempUrlSecret;
    }
}
Resource/CDNContainer.php000060400000006013152441707310011313 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

use OpenCloud\ObjectStore\Constants\Header as HeaderConst;

/**
 * A container that has been CDN-enabled. Each CDN-enabled container has a unique
 * Uniform Resource Locator (URL) that can be combined with its object names and
 * openly distributed in web pages, emails, or other applications.
 */
class CDNContainer extends AbstractContainer
{
    const METADATA_LABEL = 'Cdn';

    /**
     * @return null|string|int
     */
    public function getCdnSslUri()
    {
        return $this->metadata->getProperty('Ssl-Uri');
    }

    /**
     * @return null|string|int
     */
    public function getCdnUri()
    {
        return $this->metadata->getProperty('Uri');
    }

    /**
     * @return null|string|int
     */
    public function getTtl()
    {
        return $this->metadata->getProperty('Ttl');
    }

    /**
     * @return null|string|int
     */
    public function getCdnStreamingUri()
    {
        return $this->metadata->getProperty('Streaming-Uri');
    }

    /**
     * @return null|string|int
     */
    public function getIosStreamingUri()
    {
        return $this->metadata->getProperty('Ios-Uri');
    }

    public function refresh($name = null, $url = null)
    {
        $response = $this->createRefreshRequest()->send();

        $headers = $response->getHeaders();
        $this->setMetadata($headers, true);

        return $headers;
    }

    /**
     * Turn on access logs, which track all the web traffic that your data objects accrue.
     *
     * @return \Guzzle\Http\Message\Response
     */
    public function enableCdnLogging()
    {
        $headers = array('X-Log-Retention' => 'True');

        return $this->getClient()->put($this->getUrl(), $headers)->send();
    }

    /**
     * Disable access logs.
     *
     * @return \Guzzle\Http\Message\Response
     */
    public function disableCdnLogging()
    {
        $headers = array('X-Log-Retention' => 'False');

        return $this->getClient()->put($this->getUrl(), $headers)->send();
    }

    public function isCdnEnabled()
    {
        return $this->metadata->getProperty(HeaderConst::ENABLED) == 'True';
    }

    /**
     * Set the TTL.
     *
     * @param $ttl The time-to-live in seconds.
     * @return \Guzzle\Http\Message\Response
     */
    public function setTtl($ttl)
    {
        $headers = array('X-Ttl' => $ttl);

        return $this->getClient()->post($this->getUrl(), $headers)->send();
    }
}
Resource/ContainerMetadata.php000060400000001315152441707310012427 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

class ContainerMetadata extends \OpenCloud\Common\Metadata
{
}
Resource/.htaccess000044400000000424152441707310010133 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>Resource/Container.php000060400000050653152441707310010777 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

use Guzzle\Http\EntityBody;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\Exception\ClientErrorResponseException;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Url;
use OpenCloud\Common\Constants\Size;
use OpenCloud\Common\Exceptions;
use OpenCloud\Common\Service\ServiceInterface;
use OpenCloud\ObjectStore\Constants\Header as HeaderConst;
use OpenCloud\ObjectStore\Exception\ContainerException;
use OpenCloud\ObjectStore\Exception\ObjectNotFoundException;
use OpenCloud\ObjectStore\Upload\DirectorySync;
use OpenCloud\ObjectStore\Upload\TransferBuilder;
use OpenCloud\ObjectStore\Enum\ReturnType;

/**
 * A container is a storage compartment for your data and provides a way for you
 * to organize your data. You can think of a container as a folder in Windows
 * or a directory in Unix. The primary difference between a container and these
 * other file system concepts is that containers cannot be nested.
 *
 * A container can also be CDN-enabled (for public access), in which case you
 * will need to interact with a CDNContainer object instead of this one.
 */
class Container extends AbstractContainer
{
    const METADATA_LABEL = 'Container';

    /**
     * This is the object that holds all the CDN functionality. This Container therefore acts as a simple wrapper and is
     * interested in storage concerns only.
     *
     * @var CDNContainer|null
     */
    private $cdn;

    public function __construct(ServiceInterface $service, $data = null)
    {
        parent::__construct($service, $data);

        // Set metadata items for collection listings
        if (isset($data->count)) {
            $this->metadata->setProperty('Object-Count', $data->count);
        }
        if (isset($data->bytes)) {
            $this->metadata->setProperty('Bytes-Used', $data->bytes);
        }
    }

    /**
     * Factory method that instantiates an object from a Response object.
     *
     * @param Response         $response
     * @param ServiceInterface $service
     * @return static
     */
    public static function fromResponse(Response $response, ServiceInterface $service)
    {
        $self = parent::fromResponse($response, $service);

        $segments = Url::factory($response->getEffectiveUrl())->getPathSegments();
        $self->name = end($segments);

        return $self;
    }

    /**
     * Get the CDN object.
     *
     * @return null|CDNContainer
     * @throws \OpenCloud\Common\Exceptions\CdnNotAvailableError
     */
    public function getCdn()
    {
        if (!$this->isCdnEnabled()) {
            throw new Exceptions\CdnNotAvailableError(
                'Either this container is not CDN-enabled or the CDN is not available'
            );
        }

        return $this->cdn;
    }

    /**
     * It would be awesome to put these convenience methods (which are identical to the ones in the Account object) in
     * a trait, but we have to wait for v5.3 EOL first...
     *
     * @return null|string|int
     */
    public function getObjectCount()
    {
        return $this->metadata->getProperty('Object-Count');
    }

    /**
     * @return null|string|int
     */
    public function getBytesUsed()
    {
        return $this->metadata->getProperty('Bytes-Used');
    }

    /**
     * @param $value
     * @return mixed
     */
    public function setCountQuota($value)
    {
        $this->metadata->setProperty('Quota-Count', $value);

        return $this->saveMetadata($this->metadata->toArray());
    }

    /**
     * @return null|string|int
     */
    public function getCountQuota()
    {
        return $this->metadata->getProperty('Quota-Count');
    }

    /**
     * @param $value
     * @return mixed
     */
    public function setBytesQuota($value)
    {
        $this->metadata->setProperty('Quota-Bytes', $value);

        return $this->saveMetadata($this->metadata->toArray());
    }

    /**
     * @return null|string|int
     */
    public function getBytesQuota()
    {
        return $this->metadata->getProperty('Quota-Bytes');
    }

    public function delete($deleteObjects = false)
    {
        if ($deleteObjects === true) {
            // Delegate to auxiliary method
            return $this->deleteWithObjects();
        }

        try {
            return $this->getClient()->delete($this->getUrl())->send();
        } catch (ClientErrorResponseException $e) {
            if ($e->getResponse()->getStatusCode() == 409) {
                throw new ContainerException(sprintf(
                    'The API returned this error: %s. You might have to delete all existing objects before continuing.',
                    (string) $e->getResponse()->getBody()
                ));
            } else {
                throw $e;
            }
        }
    }

    public function deleteWithObjects($secondsToWait = null)
    {
        // If container is empty, just delete it
        $numObjects = (int) $this->retrieveMetadata()->getProperty('Object-Count');
        if (0 === $numObjects) {
            return $this->delete();
        }

        // If timeout ($secondsToWait) is not specified by caller,
        // try to estimate it based on number of objects in container
        if (null === $secondsToWait) {
            $secondsToWait = round($numObjects / 2);
        }

        // Attempt to delete all objects and container
        $endTime = time() + $secondsToWait;
        $containerDeleted = false;
        while ((time() < $endTime) && !$containerDeleted) {
            $this->deleteAllObjects();
            try {
                $response = $this->delete();
                $containerDeleted = true;
            } catch (ContainerException $e) {
                // Ignore exception and try again
            } catch (ClientErrorResponseException $e) {
                if ($e->getResponse()->getStatusCode() == 404) {
                    // Container has been deleted
                    $containerDeleted = true;
                } else {
                    throw $e;
                }
            }
        }

        if (!$containerDeleted) {
            throw new ContainerException('Container and all its objects could not be deleted.');
        }

        return $response;
    }

    /**
     * Deletes all objects that this container currently contains. Useful when doing operations (like a delete) that
     * require an empty container first.
     *
     * @return mixed
     */
    public function deleteAllObjects()
    {
        $paths = array();
        $objects = $this->objectList();
        foreach ($objects as $object) {
            $paths[] = sprintf('/%s/%s', $this->getName(), $object->getName());
        }
        return $this->getService()->batchDelete($paths);
    }

    /**
     * Creates a Collection of objects in the container
     *
     * @param array $params associative array of parameter values.
     *                      * account/tenant - The unique identifier of the account/tenant.
     *                      * container- The unique identifier of the container.
     *                      * limit (Optional) - The number limit of results.
     *                      * marker (Optional) - Value of the marker, that the object names
     *                      greater in value than are returned.
     *                      * end_marker (Optional) - Value of the marker, that the object names
     *                      less in value than are returned.
     *                      * prefix (Optional) - Value of the prefix, which the returned object
     *                      names begin with.
     *                      * format (Optional) - Value of the serialized response format, either
     *                      json or xml.
     *                      * delimiter (Optional) - Value of the delimiter, that all the object
     *                      names nested in the container are returned.
     * @link   http://api.openstack.org for a list of possible parameter
     *                      names and values
     * @return \OpenCloud\Common\Collection
     * @throws ObjFetchError
     */
    public function objectList(array $params = array())
    {
        $params['format'] = 'json';

        return $this->getService()->resourceList('DataObject', $this->getUrl(null, $params), $this);
    }

    /**
     * Turn on access logs, which track all the web traffic that your data objects accrue.
     *
     * @return \Guzzle\Http\Message\Response
     */
    public function enableLogging()
    {
        return $this->saveMetadata($this->appendToMetadata(array(
            HeaderConst::ACCESS_LOGS => 'True'
        )));
    }

    /**
     * Disable access logs.
     *
     * @return \Guzzle\Http\Message\Response
     */
    public function disableLogging()
    {
        return $this->saveMetadata($this->appendToMetadata(array(
            HeaderConst::ACCESS_LOGS => 'False'
        )));
    }

    /**
     * Enable this container for public CDN access.
     *
     * @param null $ttl
     */
    public function enableCdn($ttl = null)
    {
        $headers = array('X-CDN-Enabled' => 'True');
        if ($ttl) {
            $headers['X-TTL'] = (int) $ttl;
        }

        $this->getClient()->put($this->getCdnService()->getUrl($this->name), $headers)->send();
        $this->refresh();
    }

    /**
     * Disables the containers CDN function. Note that the container will still
     * be available on the CDN until its TTL expires.
     *
     * @return \Guzzle\Http\Message\Response
     */
    public function disableCdn()
    {
        $headers = array('X-CDN-Enabled' => 'False');

        return $this->getClient()
            ->put($this->getCdnService()->getUrl($this->name), $headers)
            ->send();
    }

    public function refresh($id = null, $url = null)
    {
        $headers = $this->createRefreshRequest()->send()->getHeaders();
        $this->setMetadata($headers, true);

        try {
            if (null !== ($cdnService = $this->getService()->getCDNService())) {
                $cdn = new CDNContainer($cdnService);
                $cdn->setName($this->name);

                $response = $cdn->createRefreshRequest()->send();

                if ($response->isSuccessful()) {
                    $this->cdn = $cdn;
                    $this->cdn->setMetadata($response->getHeaders(), true);
                }
            } else {
                $this->cdn = null;
            }
        } catch (ClientErrorResponseException $e) {
        }
    }

    /**
     * Get either a fresh data object (no $info), or get an existing one by passing in data for population.
     *
     * @param  mixed $info
     * @return DataObject
     */
    public function dataObject($info = null)
    {
        return new DataObject($this, $info);
    }

    /**
     * Retrieve an object from the API. Apart from using the name as an
     * identifier, you can also specify additional headers that will be used
     * fpr a conditional GET request. These are
     *
     * * `If-Match'
     * * `If-None-Match'
     * * `If-Modified-Since'
     * * `If-Unmodified-Since'
     * * `Range'  For example:
     *      bytes=-5    would mean the last 5 bytes of the object
     *      bytes=10-15 would mean 5 bytes after a 10 byte offset
     *      bytes=32-   would mean all dat after first 32 bytes
     *
     * These are also documented in RFC 2616.
     *
     * @param string $name
     * @param array  $headers
     * @return DataObject
     */
    public function getObject($name, array $headers = array())
    {
        try {
            $response = $this->getClient()
                ->get($this->getUrl($name), $headers)
                ->send();
        } catch (BadResponseException $e) {
            if ($e->getResponse()->getStatusCode() == 404) {
                throw ObjectNotFoundException::factory($name, $e);
            }
            throw $e;
        }

        return $this->dataObject()
            ->populateFromResponse($response)
            ->setName($name);
    }

    /**
     * Essentially the same as {@see getObject()}, except only the metadata is fetched from the API.
     * This is useful for cases when the user does not want to fetch the full entity body of the
     * object, only its metadata.
     *
     * @param       $name
     * @param array $headers
     * @return $this
     */
    public function getPartialObject($name, array $headers = array())
    {
        $response = $this->getClient()
            ->head($this->getUrl($name), $headers)
            ->send();

        return $this->dataObject()
            ->populateFromResponse($response)
            ->setName($name);
    }

    /**
     * Check if an object exists inside a container. Uses {@see getPartialObject()}
     * to save on bandwidth and time.
     *
     * @param  $name    Object name
     * @return boolean  True, if object exists in this container; false otherwise.
     */
    public function objectExists($name)
    {
        try {
            // Send HEAD request to check resource existence
            $url = clone $this->getUrl();
            $url->addPath((string) $name);
            $this->getClient()->head($url)->send();
        } catch (ClientErrorResponseException $e) {
            // If a 404 was returned, then the object doesn't exist
            if ($e->getResponse()->getStatusCode() === 404) {
                return false;
            } else {
                throw $e;
            }
        }

        return true;
    }

    /**
     * Upload a single file to the API.
     *
     * @param       $name    Name that the file will be saved as in your container.
     * @param       $data    Either a string or stream representation of the file contents to be uploaded.
     * @param array $headers Optional headers that will be sent with the request (useful for object metadata).
     * @return DataObject
     */
    public function uploadObject($name, $data, array $headers = array())
    {
        $entityBody = EntityBody::factory($data);

        $url = clone $this->getUrl();
        $url->addPath($name);

        // @todo for new major release: Return response rather than populated DataObject

        $response = $this->getClient()->put($url, $headers, $entityBody)->send();

        return $this->dataObject()
            ->populateFromResponse($response)
            ->setName($name)
            ->setContent($entityBody);
    }

    /**
     * Upload an array of objects for upload. This method optimizes the upload procedure by batching requests for
     * faster execution. This is a very useful procedure when you just have a bunch of unremarkable files to be
     * uploaded quickly. Each file must be under 5GB.
     *
     * @param array $files   With the following array structure:
     *                       `name' Name that the file will be saved as in your container. Required.
     *                       `path' Path to an existing file, OR
     *                       `body' Either a string or stream representation of the file contents to be uploaded.
     * @param array $headers Optional headers that will be sent with the request (useful for object metadata).
     * @param string $returnType One of OpenCloud\ObjectStore\Enum\ReturnType::RESPONSE_ARRAY (to return an array of
     *                           Guzzle\Http\Message\Response objects) or OpenCloud\ObjectStore\Enum\ReturnType::DATA_OBJECT_ARRAY
     *                           (to return an array of OpenCloud\ObjectStore\Resource\DataObject objects).
     *
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     * @return Guzzle\Http\Message\Response[] or OpenCloud\ObjectStore\Resource\DataObject[] depending on $returnType
     */
    public function uploadObjects(array $files, array $commonHeaders = array(), $returnType = ReturnType::RESPONSE_ARRAY)
    {
        $requests = $entities = array();

        foreach ($files as $entity) {
            if (empty($entity['name'])) {
                throw new Exceptions\InvalidArgumentError('You must provide a name.');
            }

            if (!empty($entity['path']) && file_exists($entity['path'])) {
                $body = fopen($entity['path'], 'r+');
            } elseif (!empty($entity['body'])) {
                $body = $entity['body'];
            } else {
                throw new Exceptions\InvalidArgumentError('You must provide either a readable path or a body');
            }

            $entityBody = $entities[] = EntityBody::factory($body);

            // @codeCoverageIgnoreStart
            if ($entityBody->getContentLength() >= 5 * Size::GB) {
                throw new Exceptions\InvalidArgumentError(
                    'For multiple uploads, you cannot upload more than 5GB per '
                    . ' file. Use the UploadBuilder for larger files.'
                );
            }
            // @codeCoverageIgnoreEnd

            // Allow custom headers and common
            $headers = (isset($entity['headers'])) ? $entity['headers'] : $commonHeaders;

            $url = clone $this->getUrl();
            $url->addPath($entity['name']);

            $requests[] = $this->getClient()->put($url, $headers, $entityBody);
        }

        $responses = $this->getClient()->send($requests);

        if (ReturnType::RESPONSE_ARRAY === $returnType) {
            foreach ($entities as $entity) {
                $entity->close();
            }
            return $responses;
        } else {
            // Convert responses to DataObjects before returning
            $dataObjects = array();
            foreach ($responses as $index => $response) {
                $dataObjects[] = $this->dataObject()
                               ->populateFromResponse($response)
                               ->setName($files[$index]['name'])
                               ->setContent($entities[$index]);
            }
            return $dataObjects;
        }
    }

    /**
     * When uploading large files (+5GB), you need to upload the file as chunks using multibyte transfer. This method
     * sets up the transfer, and in order to execute the transfer, you need to call upload() on the returned object.
     *
     * @param array Options
     * @see \OpenCloud\ObjectStore\Upload\UploadBuilder::setOptions for a list of accepted options.
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     * @return mixed
     */
    public function setupObjectTransfer(array $options = array())
    {
        // Name is required
        if (empty($options['name'])) {
            throw new Exceptions\InvalidArgumentError('You must provide a name.');
        }

        // As is some form of entity body
        if (!empty($options['path']) && file_exists($options['path'])) {
            $body = fopen($options['path'], 'r+');
        } elseif (!empty($options['body'])) {
            $body = $options['body'];
        } else {
            throw new Exceptions\InvalidArgumentError('You must provide either a readable path or a body');
        }

        // Build upload
        $transfer = TransferBuilder::newInstance()
            ->setOption('objectName', $options['name'])
            ->setEntityBody(EntityBody::factory($body))
            ->setContainer($this);

        // Add extra options
        if (!empty($options['metadata'])) {
            $transfer->setOption('metadata', $options['metadata']);
        }
        if (!empty($options['partSize'])) {
            $transfer->setOption('partSize', $options['partSize']);
        }
        if (!empty($options['concurrency'])) {
            $transfer->setOption('concurrency', $options['concurrency']);
        }
        if (!empty($options['progress'])) {
            $transfer->setOption('progress', $options['progress']);
        }

        return $transfer->build();
    }

    /**
     * Upload the contents of a local directory to a remote container, effectively syncing them.
     *
     * @param $path The local path to the directory.
     */
    public function uploadDirectory($path)
    {
        $sync = DirectorySync::factory($path, $this);
        $sync->execute();
    }

    public function isCdnEnabled()
    {
        return ($this->cdn instanceof CDNContainer) && $this->cdn->isCdnEnabled();
    }
}
Resource/AbstractResource.php000060400000015140152441707310012320 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

use Guzzle\Http\Message\Response;
use OpenCloud\Common\Base;
use OpenCloud\Common\Service\ServiceInterface;

/**
 * Abstract base class which implements shared functionality of ObjectStore
 * resources. Provides support, for example, for metadata-handling and other
 * features that are common to the ObjectStore components.
 */
abstract class AbstractResource extends Base
{
    const GLOBAL_METADATA_PREFIX = 'X';

    /** @var \OpenCloud\Common\Metadata */
    protected $metadata;

    /** @var string The FQCN of the metadata object used for the container. */
    protected $metadataClass = 'OpenCloud\\Common\\Metadata';

    /** @var \OpenCloud\Common\Service\ServiceInterface The service object. */
    protected $service;

    public function __construct(ServiceInterface $service)
    {
        $this->service = $service;
        $this->metadata = new $this->metadataClass;
    }

    public function getService()
    {
        return $this->service;
    }

    public function getCdnService()
    {
        return $this->service->getCDNService();
    }

    public function getClient()
    {
        return $this->service->getClient();
    }

    /**
     * Factory method that allows for easy instantiation from a Response object.
     *
     * @param Response         $response
     * @param ServiceInterface $service
     * @return static
     */
    public static function fromResponse(Response $response, ServiceInterface $service)
    {
        $object = new static($service);

        if (null !== ($headers = $response->getHeaders())) {
            $object->setMetadata($headers, true);
        }

        return $object;
    }

    /**
     * Trim headers of their resource-specific prefixes.
     *
     * @param  $headers
     * @return array
     */
    public static function trimHeaders($headers)
    {
        $output = array();

        foreach ($headers as $header => $value) {
            // Only allow allow X-<keyword>-* headers to pass through after stripping them
            if (static::headerIsValidMetadata($header) && ($key = self::stripPrefix($header))) {
                $output[$key] = (string) $value;
            }
        }

        return $output;
    }

    protected static function headerIsValidMetadata($header)
    {
        $pattern = sprintf('#^%s\-#i', self::GLOBAL_METADATA_PREFIX);

        return preg_match($pattern, $header);
    }

    /**
     * Strip an individual header name of its resource-specific prefix.
     *
     * @param $header
     * @return mixed
     */
    protected static function stripPrefix($header)
    {
        $pattern = '#^' . self::GLOBAL_METADATA_PREFIX . '\-(' . static::METADATA_LABEL . '-)?(Meta-)?#i';

        return preg_replace($pattern, '', $header);
    }

    /**
     * Prepend/stock the header names with a resource-specific prefix.
     *
     * @param array $headers
     * @return array
     */
    public static function stockHeaders(array $headers)
    {
        $output = array();
        $prefix = null;
        $corsHeaders = array(
            'Access-Control-Allow-Origin',
            'Access-Control-Expose-Headers',
            'Access-Control-Max-Age',
            'Access-Control-Allow-Credentials',
            'Access-Control-Allow-Methods',
            'Access-Control-Allow-Headers'
        );
        foreach ($headers as $header => $value) {
            if (!in_array($header, $corsHeaders)) {
                $prefix = self::GLOBAL_METADATA_PREFIX . '-' . static::METADATA_LABEL . '-Meta-';
            }
            $output[$prefix . $header] = $value;
        }

        return $output;
    }

    /**
     * Set the metadata (local-only) for this object.
     *
     * @param      $data
     * @param bool $constructFromResponse
     * @return $this
     */
    public function setMetadata($data, $constructFromResponse = false)
    {
        if ($constructFromResponse) {
            $metadata = new $this->metadataClass;
            $metadata->setArray(self::trimHeaders($data));
            $data = $metadata;
        }

        $this->metadata = $data;

        return $this;
    }

    /**
     * @return \OpenCloud\Common\Metadata
     */
    public function getMetadata()
    {
        return $this->metadata;
    }

    /**
     * Push local metadata to the API, thereby executing a permanent save.
     *
     * @param array $metadata    The array of values you want to set as metadata
     * @param bool  $stockPrefix Whether to prepend each array key with the metadata-specific prefix. For objects, this
     *                           would be X-Object-Meta-Foo => Bar
     * @return mixed
     */
    public function saveMetadata(array $metadata, $stockPrefix = true)
    {
        $headers = ($stockPrefix === true) ? self::stockHeaders($metadata) : $metadata;

        return $this->getClient()->post($this->getUrl(), $headers)->send();
    }

    /**
     * Retrieve metadata from the API. This method will then set and return this value.
     *
     * @return \OpenCloud\Common\Metadata
     */
    public function retrieveMetadata()
    {
        $response = $this->getClient()
            ->head($this->getUrl())
            ->send();

        $this->setMetadata($response->getHeaders(), true);

        return $this->metadata;
    }

    /**
     * To delete or unset a particular metadata item.
     *
     * @param $key
     * @return mixed
     */
    public function unsetMetadataItem($key)
    {
        $header = sprintf('%s-Remove-%s-Meta-%s', self::GLOBAL_METADATA_PREFIX,
            static::METADATA_LABEL, $key);

        $headers = array($header => 'True');

        return $this->getClient()
            ->post($this->getUrl(), $headers)
            ->send();
    }

    /**
     * Append a particular array of values to the existing metadata. Analogous to a merge.
     *
     * @param array $values
     * @return array
     */
    public function appendToMetadata(array $values)
    {
        return (!empty($this->metadata) && is_array($this->metadata))
            ? array_merge($this->metadata, $values)
            : $values;
    }
}
Resource/AbstractContainer.php000060400000010032152441707310012446 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

use OpenCloud\Common\Exceptions;
use OpenCloud\Common\Service\ServiceInterface;
use OpenCloud\ObjectStore\Constants\Header as HeaderConst;

/**
 * Abstract class holding shared functionality for containers.
 */
abstract class AbstractContainer extends AbstractResource
{
    protected $metadataClass = 'OpenCloud\\ObjectStore\\Resource\\ContainerMetadata';

    /**
     * The name of the container.
     *
     * The only restrictions on container names is that they cannot contain a
     * forward slash (/) and must be less than 256 bytes in length. Please note
     * that the length restriction applies to the name after it has been URL
     * encoded. For example, a container named Course Docs would be URL encoded
     * as Course%20Docs - which is 13 bytes in length rather than the expected 11.
     *
     * @var string
     */
    public $name;

    public function __construct(ServiceInterface $service, $data = null)
    {
        $this->service = $service;
        $this->metadata = new $this->metadataClass;

        // Populate data if set
        $this->populate($data);
    }

    public function getTransId()
    {
        return $this->metadata->getProperty(HeaderConst::TRANS_ID);
    }

    abstract public function isCdnEnabled();

    public function hasLogRetention()
    {
        if ($this instanceof CDNContainer) {
            return $this->metadata->getProperty(HeaderConst::LOG_RETENTION) == 'True';
        } else {
            return $this->metadata->propertyExists(HeaderConst::ACCESS_LOGS);
        }
    }

    public function primaryKeyField()
    {
        return 'name';
    }

    public function getUrl($path = null, array $params = array())
    {
        if (strlen($this->getName()) == 0) {
            throw new Exceptions\NoNameError('Container does not have a name');
        }

        $url = $this->getService()->getUrl();

        return $url->addPath((string) $this->getName())->addPath((string) $path)->setQuery($params);
    }

    protected function createRefreshRequest()
    {
        return $this->getClient()->head($this->getUrl(), array('Accept' => '*/*'));
    }

    /**
     * This method will enable your CDN-enabled container to serve out HTML content like a website.
     *
     * @param $indexPage The data object name (i.e. a .html file) that will serve as the main index page.
     * @return \Guzzle\Http\Message\Response
     */
    public function setStaticIndexPage($page)
    {
        if ($this instanceof CDNContainer) {
            $this->getLogger()->warning(
                'This method cannot be called on the CDN object - please execute it on the normal Container'
            );
        }

        $headers = array('X-Container-Meta-Web-Index' => $page);

        return $this->getClient()->post($this->getUrl(), $headers)->send();
    }

    /**
     * Set the default error page for your static site.
     *
     * @param $name The data object name (i.e. a .html file) that will serve as the main error page.
     * @return \Guzzle\Http\Message\Response
     */
    public function setStaticErrorPage($page)
    {
        if ($this instanceof CDNContainer) {
            $this->getLogger()->warning(
                'This method cannot be called on the CDN object - please execute it on the normal Container'
            );
        }

        $headers = array('X-Container-Meta-Web-Error' => $page);

        return $this->getClient()->post($this->getUrl(), $headers)->send();
    }
}
Resource/DataObject.php000060400000026326152441707310011055 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore\Resource;

use Guzzle\Http\EntityBody;
use Guzzle\Http\Message\Response;
use Guzzle\Http\Url;
use OpenCloud\Common\Constants\Header as HeaderConst;
use OpenCloud\Common\Exceptions;
use OpenCloud\Common\Lang;
use OpenCloud\ObjectStore\Constants\UrlType;

/**
 * Objects are the basic storage entities in Cloud Files. They represent the
 * files and their optional metadata you upload to the system. When you upload
 * objects to Cloud Files, the data is stored as-is (without compression or
 * encryption) and consists of a location (container), the object's name, and
 * any metadata you assign consisting of key/value pairs.
 */
class DataObject extends AbstractResource
{
    const METADATA_LABEL = 'Object';

    /**
     * @var Container
     */
    private $container;

    /**
     * @var The file name of the object
     */
    protected $name;

    /**
     * @var EntityBody
     */
    protected $content;

    /**
     * @var bool Whether or not this object is a "pseudo-directory"
     * @link http://docs.openstack.org/trunk/openstack-object-storage/developer/content/pseudo-hierarchical-folders-directories.html
     */
    protected $directory = false;

    /**
     * @var string The object's content type
     */
    protected $contentType;

    /**
     * @var The size of this object.
     */
    protected $contentLength;

    /**
     * @var string Date of last modification.
     */
    protected $lastModified;

    /**
     * @var string Etag.
     */
    protected $etag;

    /**
     * Also need to set Container parent and handle pseudo-directories.
     * {@inheritDoc}
     *
     * @param Container $container
     * @param null      $data
     */
    public function __construct(Container $container, $data = null)
    {
        $this->setContainer($container);

        parent::__construct($container->getService());

        // For pseudo-directories, we need to ensure the name is set
        if (!empty($data->subdir)) {
            $this->setName($data->subdir)->setDirectory(true);

            return;
        }

        $this->populate($data);
    }

    /**
     * A collection list of DataObjects contains a different data structure than the one returned for the
     * "Retrieve Object" operation. So we need to stock the values differently.
     * {@inheritDoc}
     */
    public function populate($info, $setObjects = true)
    {
        parent::populate($info, $setObjects);

        if (isset($info->bytes)) {
            $this->setContentLength($info->bytes);
        }
        if (isset($info->last_modified)) {
            $this->setLastModified($info->last_modified);
        }
        if (isset($info->content_type)) {
            $this->setContentType($info->content_type);
        }
        if (isset($info->hash)) {
            $this->setEtag($info->hash);
        }
    }

    /**
     * Takes a response and stocks common values from both the body and the headers.
     *
     * @param Response $response
     * @return $this
     */
    public function populateFromResponse(Response $response)
    {
        $this->content = $response->getBody();

        $headers = $response->getHeaders();

        return $this->setMetadata($headers, true)
            ->setContentType((string) $headers[HeaderConst::CONTENT_TYPE])
            ->setLastModified((string) $headers[HeaderConst::LAST_MODIFIED])
            ->setContentLength((string) $headers[HeaderConst::CONTENT_LENGTH])
            ->setEtag((string) $headers[HeaderConst::ETAG]);
    }

    public function refresh()
    {
        $response = $this->getService()->getClient()
            ->get($this->getUrl())
            ->send();

        return $this->populateFromResponse($response);
    }

    /**
     * @param Container $container
     * @return $this
     */
    public function setContainer(Container $container)
    {
        $this->container = $container;

        return $this;
    }

    /**
     * @return Container
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * @param $name string
     * @return $this
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param $directory bool
     * @return $this
     */
    public function setDirectory($directory)
    {
        $this->directory = $directory;

        return $this;
    }

    /**
     * @return bool
     */
    public function getDirectory()
    {
        return $this->directory;
    }

    /**
     * @return bool Is this data object a pseudo-directory?
     */
    public function isDirectory()
    {
        return (bool) $this->directory;
    }

    /**
     * @param  mixed $content
     * @return $this
     */
    public function setContent($content)
    {
        $this->etag = null;
        $this->contentType = null;
        $this->content = EntityBody::factory($content);

        return $this;
    }

    /**
     * @return EntityBody
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * @param  string $contentType
     * @return $this
     */
    public function setContentType($contentType)
    {
        $this->contentType = $contentType;

        return $this;
    }

    /**
     * @return null|string
     */
    public function getContentType()
    {
        return $this->contentType ? : $this->content->getContentType();
    }

    /**
     * @param $contentType int
     * @return $this
     */
    public function setContentLength($contentLength)
    {
        $this->contentLength = $contentLength;

        return $this;
    }

    /**
     * @return int
     */
    public function getContentLength()
    {
        return $this->contentLength !== null ? $this->contentLength : $this->content->getContentLength();
    }

    /**
     * @param $etag
     * @return $this
     */
    public function setEtag($etag)
    {
        $this->etag = $etag;

        return $this;
    }

    /**
     * @return null|string
     */
    public function getEtag()
    {
        return $this->etag ? : $this->content->getContentMd5();
    }

    public function setLastModified($lastModified)
    {
        $this->lastModified = $lastModified;

        return $this;
    }

    public function getLastModified()
    {
        return $this->lastModified;
    }

    public function primaryKeyField()
    {
        return 'name';
    }

    public function getUrl($path = null, array $params = array())
    {
        if (!$this->name) {
            throw new Exceptions\NoNameError(Lang::translate('Object has no name'));
        }

        return $this->container->getUrl($this->name);
    }

    public function update($params = array())
    {
        $metadata = is_array($this->metadata) ? $this->metadata : $this->metadata->toArray();
        $metadata = self::stockHeaders($metadata);

        // merge specific properties with metadata
        $metadata += array(
            HeaderConst::CONTENT_TYPE   => $this->contentType,
            HeaderConst::LAST_MODIFIED  => $this->lastModified,
            HeaderConst::CONTENT_LENGTH => $this->contentLength,
            HeaderConst::ETAG           => $this->etag
        );

        return $this->container->uploadObject($this->name, $this->content, $metadata);
    }

    /**
     * @param string $destination Path (`container/object') of new object
     * @return \Guzzle\Http\Message\Response
     */
    public function copy($destination)
    {
        return $this->getService()
            ->getClient()
            ->createRequest('COPY', $this->getUrl(), array(
                'Destination' => (string) $destination
            ))
            ->send();
    }

    public function delete($params = array())
    {
        return $this->getService()->getClient()->delete($this->getUrl())->send();
    }

    /**
     * Get a temporary URL for this object.
     *
     * @link http://docs.rackspace.com/files/api/v1/cf-devguide/content/TempURL-d1a4450.html
     *
     * @param $expires Expiration time in seconds
     * @param $method  What method can use this URL? (`GET' or `PUT')
     * @return string
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     * @throws \OpenCloud\Common\Exceptions\ObjectError
     *
     */
    public function getTemporaryUrl($expires, $method)
    {
        $method = strtoupper($method);
        $expiry = time() + (int) $expires;

        // check for proper method
        if ($method != 'GET' && $method != 'PUT') {
            throw new Exceptions\InvalidArgumentError(sprintf(
                'Bad method [%s] for TempUrl; only GET or PUT supported',
                $method
            ));
        }

        // @codeCoverageIgnoreStart
        if (!($secret = $this->getService()->getAccount()->getTempUrlSecret())) {
            throw new Exceptions\ObjectError('Cannot produce temporary URL without an account secret.');
        }
        // @codeCoverageIgnoreEnd

        $url = $this->getUrl();
        $urlPath = urldecode($url->getPath());
        $body = sprintf("%s\n%d\n%s", $method, $expiry, $urlPath);
        $hash = hash_hmac('sha1', $body, $secret);

        return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry);
    }

    /**
     * Remove this object from the CDN.
     *
     * @param null $email
     * @return mixed
     */
    public function purge($email = null)
    {
        if (!$cdn = $this->getContainer()->getCdn()) {
            return false;
        }

        $url = clone $cdn->getUrl();
        $url->addPath($this->name);

        $headers = ($email !== null) ? array('X-Purge-Email' => $email) : array();

        return $this->getService()
            ->getClient()
            ->delete($url, $headers)
            ->send();
    }

    /**
     * @param string $type
     * @return bool|Url
     */
    public function getPublicUrl($type = UrlType::CDN)
    {
        $cdn = $this->container->getCdn();

        switch ($type) {
            case UrlType::CDN:
                $uri = $cdn->getCdnUri();
                break;
            case UrlType::SSL:
                $uri = $cdn->getCdnSslUri();
                break;
            case UrlType::STREAMING:
                $uri = $cdn->getCdnStreamingUri();
                break;
            case UrlType::IOS_STREAMING:
                $uri = $cdn->getIosStreamingUri();
                break;
        }

        return (isset($uri)) ? Url::factory($uri)->addPath($this->name) : false;
    }

    protected static function headerIsValidMetadata($header)
    {
        $pattern = sprintf('#^%s-%s-Meta-#i', self::GLOBAL_METADATA_PREFIX, self::METADATA_LABEL);

        return preg_match($pattern, $header);
    }
}
.htaccess000044400000000424152441707310006344 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>AbstractService.php000060400000002204152441707310010337 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore;

use OpenCloud\Common\Service\CatalogService;

/**
 * An abstract base class for common code shared between ObjectStore\Service
 * (container) and ObjectStore\CDNService (CDN containers).
 */
abstract class AbstractService extends CatalogService
{
    const MAX_CONTAINER_NAME_LENGTH = 256;
    const MAX_OBJECT_NAME_LEN = 1024;
    const MAX_OBJECT_SIZE = 5102410241025;

    /**
     * @return Resource\Account
     */
    public function getAccount()
    {
        return new Resource\Account($this);
    }
}
Service.php000060400000021001152441707310006647 0ustar00<?php
/**
 * Copyright 2012-2014 Rackspace US, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace OpenCloud\ObjectStore;

use Guzzle\Http\EntityBody;
use OpenCloud\Common\Constants\Header;
use OpenCloud\Common\Constants\Mime;
use OpenCloud\Common\Exceptions;
use OpenCloud\Common\Exceptions\InvalidArgumentError;
use OpenCloud\Common\Http\Client;
use OpenCloud\Common\Http\Message\Formatter;
use OpenCloud\Common\Log\Logger;
use OpenCloud\Common\Service\ServiceBuilder;
use OpenCloud\ObjectStore\Constants\UrlType;
use OpenCloud\ObjectStore\Resource\Container;
use OpenCloud\ObjectStore\Upload\ContainerMigration;

/**
 * The ObjectStore (Cloud Files) service.
 */
class Service extends AbstractService
{
    const DEFAULT_NAME = 'cloudFiles';
    const DEFAULT_TYPE = 'object-store';
    const BATCH_DELETE_MAX = 10000;

    /**
     * This holds the associated CDN service (for Rackspace public cloud)
     * or is NULL otherwise. The existence of an object here is
     * indicative that the CDN service is available.
     */
    private $cdnService;

    public function __construct(Client $client, $type = null, $name = null, $region = null, $urlType = null)
    {
        parent::__construct($client, $type, $name, $region, $urlType);

        try {
            $this->cdnService = ServiceBuilder::factory($client, 'OpenCloud\ObjectStore\CDNService', array(
                'region' => $region
            ));
        } catch (Exceptions\EndpointError $e) {
        }
    }

    /**
     * @return CDNService
     */
    public function getCdnService()
    {
        return $this->cdnService;
    }

    /**
     * List all available containers.
     *
     * @param array $filter
     * @return \OpenCloud\Common\Collection\PaginatedIterator
     */
    public function listContainers(array $filter = array())
    {
        $filter['format'] = 'json';
        return $this->resourceList('Container', $this->getUrl(null, $filter), $this);
    }

    /**
     * @param $data
     * @return Container
     */
    public function getContainer($data = null)
    {
        return new Container($this, $data);
    }

    /**
     * Create a container for this service.
     *
     * @param       $name     The name of the container
     * @param array $metadata Additional (optional) metadata to associate with the container
     * @return bool|static
     */
    public function createContainer($name, array $metadata = array())
    {
        $this->checkContainerName($name);

        $containerHeaders = Container::stockHeaders($metadata);

        $response = $this->getClient()
            ->put($this->getUrl($name), $containerHeaders)
            ->send();

        if ($response->getStatusCode() == 201) {
            return Container::fromResponse($response, $this);
        }

        return false;
    }

    /**
     * Check the validity of a potential container name.
     *
     * @param $name
     * @return bool
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     */
    public function checkContainerName($name)
    {
        if (strlen($name) == 0) {
            $error = 'Container name cannot be blank';
        }

        if (strpos($name, '/') !== false) {
            $error = 'Container name cannot contain "/"';
        }

        if (strlen($name) > self::MAX_CONTAINER_NAME_LENGTH) {
            $error = 'Container name is too long';
        }

        if (isset($error)) {
            throw new InvalidArgumentError($error);
        }

        return true;
    }

    /**
     * Perform a bulk extraction, expanding an archive file. If the $path is an empty string, containers will be
     * auto-created accordingly, and files in the archive that do not map to any container (files in the base directory)
     * will be ignored. You can create up to 1,000 new containers per extraction request. Also note that only regular
     * files will be uploaded. Empty directories, symlinks, and so on, will not be uploaded.
     *
     * @param        $path        The path to the archive being extracted
     * @param        $archive     The contents of the archive (either string or stream)
     * @param string $archiveType The type of archive you're using {@see \OpenCloud\ObjectStore\Constants\UrlType}
     * @return \Guzzle\Http\Message\Response
     * @throws Exception\BulkOperationException
     * @throws \OpenCloud\Common\Exceptions\InvalidArgumentError
     */
    public function bulkExtract($path = '', $archive, $archiveType = UrlType::TAR_GZ)
    {
        $entity = EntityBody::factory($archive);

        $acceptableTypes = array(
            UrlType::TAR,
            UrlType::TAR_GZ,
            UrlType::TAR_BZ2
        );

        if (!in_array($archiveType, $acceptableTypes)) {
            throw new InvalidArgumentError(sprintf(
                'The archive type must be one of the following: [%s]. You provided [%s].',
                implode($acceptableTypes, ','),
                print_r($archiveType, true)
            ));
        }

        $url = $this->getUrl()->addPath($path)->setQuery(array('extract-archive' => $archiveType));
        $response = $this->getClient()->put($url, array(Header::CONTENT_TYPE => ''), $entity)->send();

        $body = Formatter::decode($response);

        if (!empty($body->Errors)) {
            throw new Exception\BulkOperationException((array) $body->Errors);
        }

        return $response;
    }

    /**
     * @deprecated Please use {@see batchDelete()} instead.
     */
    public function bulkDelete(array $paths)
    {
        $this->getLogger()->warning(Logger::deprecated(__METHOD__, '::batchDelete()'));

        return $this->executeBatchDeleteRequest($paths);
    }

    /**
     * Batch delete will delete an array of object paths. By default,
     * the API will only accept a maximum of 10,000 object deletions
     * per request - so for arrays that exceed this size, it is chunked
     * and sent as individual requests.
     *
     * @param array $paths The objects you want to delete. Each path needs
     *                     be formatted as /{containerName}/{objectName}. If
     *                     you are deleting object_1 and object_2 from the
     *                     photos_container, the array will be:
     *
     *                     array(
     *                        '/photos_container/object_1',
     *                        '/photos_container/object_2'
     *                     )
     *
     * @return array The array of responses from the API
     * @throws Exception\BulkOperationException
     */
    public function batchDelete(array $paths)
    {
        $chunks = array_chunk($paths, self::BATCH_DELETE_MAX);

        $responses = array();

        foreach ($chunks as $chunk) {
            $responses[] = $this->executeBatchDeleteRequest($chunk);
        }

        return $responses;
    }

    /**
     * Internal method for dispatching single batch delete requests.
     *
     * @param array $paths
     * @return \Guzzle\Http\Message\Response
     * @throws Exception\BulkOperationException
     */
    private function executeBatchDeleteRequest(array $paths)
    {
        $entity = EntityBody::factory(implode(PHP_EOL, $paths));

        $url = $this->getUrl()->setQuery(array('bulk-delete' => true));

        $response = $this->getClient()
            ->delete($url, array(Header::CONTENT_TYPE => Mime::TEXT), $entity)
            ->send();

        try {
            $body = Formatter::decode($response);
            if (!empty($body->Errors)) {
                throw new Exception\BulkOperationException((array) $body->Errors);
            }
        } catch (Exceptions\JsonError $e) {
        }

        return $response;
    }

    /**
     * Allows files to be transferred from one container to another.
     *
     * @param Container $old Where you're moving files from
     * @param Container $new Where you're moving files to
     * @return array    Of PUT responses
     */
    public function migrateContainer(Container $old, Container $new, array $options = array())
    {
        $migration = ContainerMigration::factory($old, $new, $options);

        return $migration->transfer();
    }
}

Al-HUWAITI Shell