Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/OpenStack.php')
-rw-r--r--apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/OpenStack.php1198
1 files changed, 1198 insertions, 0 deletions
diff --git a/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/OpenStack.php b/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/OpenStack.php
new file mode 100644
index 00000000000..c3e645a5406
--- /dev/null
+++ b/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/OpenStack.php
@@ -0,0 +1,1198 @@
+<?php
+/**
+ * PHP OpenCloud library.
+ *
+ * @copyright Copyright 2013 Rackspace US, Inc. See COPYING for licensing information.
+ * @license https://www.apache.org/licenses/LICENSE-2.0 Apache 2.0
+ * @version 1.6.0
+ * @author Glen Campbell <glen.campbell@rackspace.com>
+ * @author Jamie Hannaford <jamie.hannaford@rackspace.com>
+ */
+
+namespace OpenCloud;
+
+require_once __DIR__ . '/Globals.php';
+
+use OpenCloud\Common\Base;
+use OpenCloud\Common\Lang;
+use OpenCloud\Common\Exceptions;
+use OpenCloud\Common\ServiceCatalogItem;
+
+/**
+ * The OpenStack class represents a relationship (or "connection")
+ * between a user and a service.
+ *
+ * This is the primary entry point into an OpenStack system, and the only one
+ * where the developer is required to know and provide the endpoint URL (in
+ * all other cases, the endpoint is derived from the Service Catalog provided
+ * by the authentication system).
+ *
+ * Since various providers have different mechanisms for authentication, users
+ * will often use a subclass of OpenStack. For example, the Rackspace
+ * class is provided for users of Rackspace's cloud services, and other cloud
+ * providers are welcome to add their own subclasses as well.
+ *
+ * General usage example:
+ * <code>
+ * $username = 'My Username';
+ * $secret = 'My Secret';
+ * $connection = new OpenCloud\OpenStack($username, $secret);
+ * // having established the connection, we can set some defaults
+ * // this sets the default name and region of the Compute service
+ * $connection->SetDefaults('Compute', 'cloudServersOpenStack', 'ORD');
+ * // access a Compute service
+ * $chicago = $connection->Compute();
+ * // if we want to access a different service, we can:
+ * $dallas = $connection->Compute('cloudServersOpenStack', 'DFW');
+ * </code>
+ */
+class OpenStack extends Base
+{
+
+ /**
+ * This holds the HTTP User-Agent: used for all requests to the services. It
+ * is public so that, if necessary, it can be entirely overridden by the
+ * developer. However, it's strongly recomended that you use the
+ * appendUserAgent() method to APPEND your own User Agent identifier to the
+ * end of this string; the user agent information can be very valuable to
+ * service providers to track who is using their service.
+ *
+ * @var string
+ */
+ public $useragent = RAXSDK_USER_AGENT;
+
+ protected $url;
+ protected $secret = array();
+ protected $token;
+ protected $expiration = 0;
+ protected $tenant;
+ protected $catalog;
+ protected $connectTimeout = RAXSDK_CONNECTTIMEOUT;
+ protected $httpTimeout = RAXSDK_TIMEOUT;
+ protected $overlimitTimeout = RAXSDK_OVERLIMIT_TIMEOUT;
+
+ /**
+ * This associative array holds default values used to identify each
+ * service (and to select it from the Service Catalog). Use the
+ * Compute::SetDefaults() method to change the default values, or
+ * define the global constants (for example, RAXSDK_COMPUTE_NAME)
+ * BEFORE loading the OpenCloud library:
+ *
+ * <code>
+ * define('RAXSDK_COMPUTE_NAME', 'cloudServersOpenStack');
+ * include('openstack.php');
+ * </code>
+ */
+ protected $defaults = array(
+ 'Compute' => array(
+ 'name' => RAXSDK_COMPUTE_NAME,
+ 'region' => RAXSDK_COMPUTE_REGION,
+ 'urltype' => RAXSDK_COMPUTE_URLTYPE
+ ),
+ 'ObjectStore' => array(
+ 'name' => RAXSDK_OBJSTORE_NAME,
+ 'region' => RAXSDK_OBJSTORE_REGION,
+ 'urltype' => RAXSDK_OBJSTORE_URLTYPE
+ ),
+ 'Database' => array(
+ 'name' => RAXSDK_DATABASE_NAME,
+ 'region' => RAXSDK_DATABASE_REGION,
+ 'urltype' => RAXSDK_DATABASE_URLTYPE
+ ),
+ 'Volume' => array(
+ 'name' => RAXSDK_VOLUME_NAME,
+ 'region' => RAXSDK_VOLUME_REGION,
+ 'urltype' => RAXSDK_VOLUME_URLTYPE
+ ),
+ 'LoadBalancer' => array(
+ 'name' => RAXSDK_LBSERVICE_NAME,
+ 'region' => RAXSDK_LBSERVICE_REGION,
+ 'urltype' => RAXSDK_LBSERVICE_URLTYPE
+ ),
+ 'DNS' => array(
+ 'name' => RAXSDK_DNS_NAME,
+ 'region' => RAXSDK_DNS_REGION,
+ 'urltype' => RAXSDK_DNS_URLTYPE
+ ),
+ 'Orchestration' => array(
+ 'name' => RAXSDK_ORCHESTRATION_NAME,
+ 'region' => RAXSDK_ORCHESTRATION_REGION,
+ 'urltype' => RAXSDK_ORCHESTRATION_URLTYPE
+ ),
+ 'CloudMonitoring' => array(
+ 'name' => RAXSDK_MONITORING_NAME,
+ 'region' => RAXSDK_MONITORING_REGION,
+ 'urltype' => RAXSDK_MONITORING_URLTYPE
+ ),
+ 'Autoscale' => array(
+ 'name' => RAXSDK_AUTOSCALE_NAME,
+ 'region' => RAXSDK_AUTOSCALE_REGION,
+ 'urltype' => RAXSDK_AUTOSCALE_URLTYPE
+ )
+ );
+
+ private $_user_write_progress_callback_func;
+ private $_user_read_progress_callback_func;
+
+ /**
+ * Tracks file descriptors used by streaming downloads
+ *
+ * This will permit multiple simultaneous streaming downloads; the
+ * key is the URL of the object, and the value is its file descriptor.
+ *
+ * To prevent memory overflows, each array element is deleted when
+ * the end of the file is reached.
+ */
+ private $fileDescriptors = array();
+
+ /**
+ * array of options to pass to the CURL request object
+ */
+ private $curlOptions = array();
+
+ /**
+ * list of attributes to export/import
+ */
+ private $exportItems = array(
+ 'token',
+ 'expiration',
+ 'tenant',
+ 'catalog'
+ );
+
+ /**
+ * Creates a new OpenStack object
+ *
+ * The OpenStack object needs two bits of information: the URL to
+ * authenticate against, and a "secret", which is an associative array
+ * of name/value pairs. Usually, the secret will be a username and a
+ * password, but other values may be required by different authentication
+ * systems. For example, OpenStack Keystone requires a username and
+ * password, but Rackspace uses a username, tenant ID, and API key.
+ * (See OpenCloud\Rackspace for that.)
+ *
+ * @param string $url - the authentication endpoint URL
+ * @param array $secret - an associative array of auth information:
+ * * username
+ * * password
+ * @param array $options - CURL options to pass to the HttpRequest object
+ */
+ public function __construct($url, array $secret, array $options = array())
+ {
+ // check for supported version
+ // @codeCoverageIgnoreStart
+ $version = phpversion();
+ if ($version < '5.3.1') {
+ throw new Exceptions\UnsupportedVersionError(sprintf(
+ Lang::translate('PHP version [%s] is not supported'),
+ $version
+ ));
+ }
+ // @codeCoverageIgnoreEnd
+
+ // Start processing
+ $this->getLogger()->info(Lang::translate('Initializing OpenStack client'));
+
+ // Set properties
+ $this->setUrl($url);
+ $this->setSecret($secret);
+ $this->setCurlOptions($options);
+ }
+
+ /**
+ * Set user agent.
+ *
+ * @param string $useragent
+ * @return OpenCloud\OpenStack
+ */
+ public function setUserAgent($useragent)
+ {
+ $this->useragent = $useragent;
+
+ return $this;
+ }
+
+ /**
+ * Allows the user to append a user agent string
+ *
+ * Programs that are using these bindings are encouraged to add their
+ * user agent to the one supplied by this SDK. This will permit cloud
+ * providers to track users so that they can provide better service.
+ *
+ * @param string $agent an arbitrary user-agent string; e.g. "My Cloud App"
+ * @return OpenCloud\OpenStack
+ */
+ public function appendUserAgent($useragent)
+ {
+ $this->useragent .= ';' . $useragent;
+
+ return $this;
+ }
+
+ /**
+ * Get user agent.
+ *
+ * @return string
+ */
+ public function getUserAgent()
+ {
+ return $this->useragent;
+ }
+
+ /**
+ * Sets the URL which the client will access.
+ *
+ * @param string $url
+ * @return OpenCloud\OpenStack
+ */
+ public function setUrl($url)
+ {
+ $this->url = $url;
+
+ return $this;
+ }
+
+ /**
+ * Get the URL.
+ *
+ * @return string
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * Set the secret for the client.
+ *
+ * @param array $secret
+ * @return OpenCloud\OpenStack
+ */
+ public function setSecret(array $secret = array())
+ {
+ $this->secret = $secret;
+
+ return $this;
+ }
+
+ /**
+ * Get the secret.
+ *
+ * @return array
+ */
+ public function getSecret()
+ {
+ return $this->secret;
+ }
+
+ /**
+ * Set the token for this client.
+ *
+ * @param string $token
+ * @return OpenCloud\OpenStack
+ */
+ public function setToken($token)
+ {
+ $this->token = $token;
+
+ return $this;
+ }
+
+ /**
+ * Get the token for this client.
+ *
+ * @return string
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+
+ /**
+ * Set the expiration for this token.
+ *
+ * @param int $expiration
+ * @return OpenCloud\OpenStack
+ */
+ public function setExpiration($expiration)
+ {
+ $this->expiration = $expiration;
+
+ return $this;
+ }
+
+ /**
+ * Get the expiration time.
+ *
+ * @return int
+ */
+ public function getExpiration()
+ {
+ return $this->expiration;
+ }
+
+ /**
+ * Set the tenant for this client.
+ *
+ * @param string $tenant
+ * @return OpenCloud\OpenStack
+ */
+ public function setTenant($tenant)
+ {
+ $this->tenant = $tenant;
+
+ return $this;
+ }
+
+ /**
+ * Get the tenant for this client.
+ *
+ * @return string
+ */
+ public function getTenant()
+ {
+ return $this->tenant;
+ }
+
+ /**
+ * Set the service catalog.
+ *
+ * @param mixed $catalog
+ * @return OpenCloud\OpenStack
+ */
+ public function setCatalog($catalog)
+ {
+ $this->catalog = $catalog;
+
+ return $this;
+ }
+
+ /**
+ * Get the service catalog.
+ *
+ * @return array
+ */
+ public function getCatalog()
+ {
+ return $this->catalog;
+ }
+
+ /**
+ * Set (all) the cURL options.
+ *
+ * @param array $options
+ * @return OpenCloud\OpenStack
+ */
+ public function setCurlOptions(array $options)
+ {
+ $this->curlOptions = $options;
+
+ return $this;
+ }
+
+ /**
+ * Get the cURL options.
+ *
+ * @return array
+ */
+ public function getCurlOptions()
+ {
+ return $this->curlOptions;
+ }
+
+ /**
+ * Set a specific file descriptor (associated with a URL)
+ *
+ * @param string $key
+ * @param resource $value
+ * @return OpenCloud\OpenStack
+ */
+ public function setFileDescriptor($key, $value)
+ {
+ $this->descriptors[$key] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get a specific file descriptor (associated with a URL)
+ *
+ * @param string $key
+ * @return resource|false
+ */
+ public function getFileDescriptor($key)
+ {
+ return (!isset($this->descriptors[$key])) ? false : $this->descriptors[$key];
+ }
+
+ /**
+ * Get the items to be exported.
+ *
+ * @return array
+ */
+ public function getExportItems()
+ {
+ return $this->exportItems;
+ }
+
+ /**
+ * Sets the connect timeout.
+ *
+ * @param int $timeout
+ * @return OpenCloud\OpenStack
+ */
+ public function setConnectTimeout($timeout)
+ {
+ $this->connectTimeout = $timeout;
+
+ return $this;
+ }
+
+ /**
+ * Get the connect timeout.
+ *
+ * @return int
+ */
+ public function getConnectTimeout()
+ {
+ return $this->connectTimeout;
+ }
+
+ /**
+ * Set the HTTP timeout.
+ *
+ * @param int $timeout
+ * @return OpenCloud\OpenStack
+ */
+ public function setHttpTimeout($timeout)
+ {
+ $this->httpTimeout = $timeout;
+
+ return $this;
+ }
+
+ /**
+ * Get the HTTP timeout.
+ *
+ * @return int
+ */
+ public function getHttpTimeout()
+ {
+ return $this->httpTimeout;
+ }
+
+ /**
+ * Set the overlimit timeout.
+ *
+ * @param int $timeout
+ * @return OpenCloud\OpenStack
+ */
+ public function setOverlimitTimeout($timeout)
+ {
+ $this->overlimitTimeout = $timeout;
+
+ return $this;
+ }
+
+ /**
+ * Get the overlimit timeout.
+ *
+ * @return int
+ */
+ public function getOverlimitTimeout()
+ {
+ return $this->overlimitTimeout;
+ }
+
+ /**
+ * Sets default values (an array) for a service. Each array must contain a
+ * "name", "region" and "urltype" key.
+ *
+ * @param string $service
+ * @param array $value
+ * @return OpenCloud\OpenStack
+ */
+ public function setDefault($service, array $value = array())
+ {
+ if (isset($value['name']) && isset($value['region']) && isset($value['urltype'])) {
+ $this->defaults[$service] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get a specific default value for a service. If none exist, return FALSE.
+ *
+ * @param string $service
+ * @return array|false
+ */
+ public function getDefault($service)
+ {
+ return (!isset($this->defaults[$service])) ? false : $this->defaults[$service];
+ }
+
+/**
+ * Sets the timeouts for the current connection
+ *
+ * @api
+ * @param integer $t_http the HTTP timeout value (the max period that
+ * the OpenStack object will wait for any HTTP request to complete).
+ * Value is in seconds.
+ * @param integer $t_conn the Connect timeout value (the max period
+ * that the OpenStack object will wait to establish an HTTP
+ * connection). Value is in seconds.
+ * @param integer $t_overlimit the overlimit timeout value (the max period
+ * that the OpenStack object will wait to retry on an overlimit
+ * condition). Value is in seconds.
+ * @return void
+ */
+ public function setTimeouts($httpTimeout, $connectTimeout = null, $overlimitTimeout = null)
+ {
+ $this->setHttpTimeout($httpTimeout);
+
+ if (isset($connectTimeout)) {
+ $this->setConnectTimeout($connectTimeout);
+ }
+
+ if (isset($overlimitTimeout)) {
+ $this->setOverlimitTimeout($overlimitTimeout);
+ }
+ }
+
+ /**
+ * Returns the URL of this object
+ *
+ * @api
+ * @param string $subresource specified subresource
+ * @return string
+ */
+ public function url($subresource='tokens')
+ {
+ return Lang::noslash($this->url) . '/' . $subresource;
+ }
+
+ /**
+ * Returns the stored secret
+ *
+ * @return array
+ */
+ public function secret()
+ {
+ return $this->getSecret();
+ }
+
+ /**
+ * Re-authenticates session if expired.
+ */
+ public function checkExpiration()
+ {
+ if ($this->hasExpired()) {
+ $this->authenticate();
+ }
+ }
+
+ /**
+ * Checks whether token has expired.
+ *
+ * @return bool
+ */
+ public function hasExpired()
+ {
+ return time() > ($this->getExpiration() - RAXSDK_FUDGE);
+ }
+
+ /**
+ * Returns the cached token; if it has expired, then it re-authenticates
+ *
+ * @api
+ * @return string
+ */
+ public function token()
+ {
+ $this->checkExpiration();
+
+ return $this->getToken();
+ }
+
+ /**
+ * Returns the cached expiration time;
+ * if it has expired, then it re-authenticates
+ *
+ * @api
+ * @return string
+ */
+ public function expiration()
+ {
+ $this->checkExpiration();
+
+ return $this->getExpiration();
+ }
+
+ /**
+ * Returns the tenant ID, re-authenticating if necessary
+ *
+ * @api
+ * @return string
+ */
+ public function tenant()
+ {
+ $this->checkExpiration();
+
+ return $this->getTenant();
+ }
+
+ /**
+ * Returns the service catalog object from the auth service
+ *
+ * @return \stdClass
+ */
+ public function serviceCatalog()
+ {
+ $this->checkExpiration();
+
+ return $this->getCatalog();
+ }
+
+ /**
+ * Returns a Collection of objects with information on services
+ *
+ * Note that these are informational (read-only) and are not actually
+ * 'Service'-class objects.
+ */
+ public function serviceList()
+ {
+ return new Common\Collection($this, 'ServiceCatalogItem', $this->serviceCatalog());
+ }
+
+ /**
+ * Creates and returns the formatted credentials to POST to the auth
+ * service.
+ *
+ * @return string
+ */
+ public function credentials()
+ {
+ if (isset($this->secret['username']) && isset($this->secret['password'])) {
+
+ $credentials = array(
+ 'auth' => array(
+ 'passwordCredentials' => array(
+ 'username' => $this->secret['username'],
+ 'password' => $this->secret['password']
+ )
+ )
+ );
+
+ if (isset($this->secret['tenantName'])) {
+ $credentials['auth']['tenantName'] = $this->secret['tenantName'];
+ }
+
+ return json_encode($credentials);
+
+ } else {
+ throw new Exceptions\CredentialError(
+ Lang::translate('Unrecognized credential secret')
+ );
+ }
+ }
+
+ /**
+ * Authenticates using the supplied credentials
+ *
+ * @api
+ * @return void
+ * @throws AuthenticationError
+ */
+ public function authenticate()
+ {
+ // try to auth
+ $response = $this->request(
+ $this->url(),
+ 'POST',
+ array('Content-Type'=>'application/json'),
+ $this->credentials()
+ );
+
+ $json = $response->httpBody();
+
+ // check for errors
+ if ($response->HttpStatus() >= 400) {
+ throw new Exceptions\AuthenticationError(sprintf(
+ Lang::translate('Authentication failure, status [%d], response [%s]'),
+ $response->httpStatus(),
+ $json
+ ));
+ }
+
+ // Decode and check
+ $object = json_decode($json);
+ $this->checkJsonError();
+
+ // Save the token information as well as the ServiceCatalog
+ $this->setToken($object->access->token->id);
+ $this->setExpiration(strtotime($object->access->token->expires));
+ $this->setCatalog($object->access->serviceCatalog);
+
+ /**
+ * In some cases, the tenant name/id is not returned
+ * as part of the auth token, so we check for it before
+ * we set it. This occurs with pure Keystone, but not
+ * with the Rackspace auth.
+ */
+ if (isset($object->access->token->tenant)) {
+ $this->setTenant($object->access->token->tenant->id);
+ }
+ }
+
+ /**
+ * Performs a single HTTP request
+ *
+ * The request() method is one of the most frequently-used in the entire
+ * library. It performs an HTTP request using the specified URL, method,
+ * and with the supplied headers and body. It handles error and
+ * exceptions for the request.
+ *
+ * @api
+ * @param string url - the URL of the request
+ * @param string method - the HTTP method (defaults to GET)
+ * @param array headers - an associative array of headers
+ * @param string data - either a string or a resource (file pointer) to
+ * use as the request body (for PUT or POST)
+ * @return HttpResponse object
+ * @throws HttpOverLimitError, HttpUnauthorizedError, HttpForbiddenError
+ */
+ public function request($url, $method = 'GET', $headers = array(), $data = null)
+ {
+ $this->getLogger()->info('Resource [{url}] method [{method}] body [{body}]', array(
+ 'url' => $url,
+ 'method' => $method,
+ 'data' => $data
+ ));
+
+ // get the request object
+ $http = $this->getHttpRequestObject($url, $method, $this->getCurlOptions());
+
+ // set various options
+ $this->getLogger()->info('Headers: [{headers}]', array(
+ 'headers' => print_r($headers, true)
+ ));
+
+ $http->setheaders($headers);
+ $http->setHttpTimeout($this->getHttpTimeout());
+ $http->setConnectTimeout($this->getConnectTimeout());
+ $http->setOption(CURLOPT_USERAGENT, $this->getUserAgent());
+
+ // data can be either a resource or a string
+ if (is_resource($data)) {
+ // loading from or writing to a file
+ // set the appropriate callback functions
+ switch($method) {
+ // @codeCoverageIgnoreStart
+ case 'GET':
+ // need to save the file descriptor
+ $this->setFileDescriptor($url, $data);
+ // set the CURL options
+ $http->setOption(CURLOPT_FILE, $data);
+ $http->setOption(CURLOPT_WRITEFUNCTION, array($this, '_write_cb'));
+ break;
+ // @codeCoverageIgnoreEnd
+ case 'PUT':
+ case 'POST':
+ // need to save the file descriptor
+ $this->setFileDescriptor($url, $data);
+ if (!isset($headers['Content-Length'])) {
+ throw new Exceptions\HttpError(
+ Lang::translate('The Content-Length: header must be specified for file uploads')
+ );
+ }
+ $http->setOption(CURLOPT_UPLOAD, TRUE);
+ $http->setOption(CURLOPT_INFILE, $data);
+ $http->setOption(CURLOPT_INFILESIZE, $headers['Content-Length']);
+ $http->setOption(CURLOPT_READFUNCTION, array($this, '_read_cb'));
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ } elseif (is_string($data)) {
+ $http->setOption(CURLOPT_POSTFIELDS, $data);
+ } elseif (isset($data)) {
+ throw new Exceptions\HttpError(
+ Lang::translate('Unrecognized data type for PUT/POST body, must be string or resource')
+ );
+ }
+
+ // perform the HTTP request; returns an HttpResult object
+ $response = $http->execute();
+
+ // handle and retry on overlimit errors
+ if ($response->httpStatus() == 413) {
+
+ $object = json_decode($response->httpBody());
+ $this->checkJsonError();
+
+ // @codeCoverageIgnoreStart
+ if (isset($object->overLimit)) {
+ /**
+ * @TODO(glen) - The documentation says "retryAt", but
+ * the field returned is "retryAfter". If the doc changes,
+ * then there's no problem, but we'll need to fix this if
+ * they change the code to match the docs.
+ */
+ $retryAfter = $object->overLimit->retryAfter;
+ $sleepInterval = strtotime($retryAfter) - time();
+
+ if ($sleepInterval && $sleepInterval <= $this->getOverlimitTimeout()) {
+ sleep($sleepInterval);
+ $response = $http->Execute();
+ } else {
+ throw new Exceptions\HttpOverLimitError(sprintf(
+ Lang::translate('Over limit; next available request [%s][%s] is not for [%d] seconds at [%s]'),
+ $method,
+ $url,
+ $sleepInterval,
+ $retryAfter
+ ));
+ }
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ // do some common error checking
+ switch ($response->httpStatus()) {
+ case 401:
+ throw new Exceptions\HttpUnauthorizedError(sprintf(
+ Lang::translate('401 Unauthorized for [%s] [%s]'),
+ $url,
+ $response->HttpBody()
+ ));
+ break;
+ case 403:
+ throw new Exceptions\HttpForbiddenError(sprintf(
+ Lang::translate('403 Forbidden for [%s] [%s]'),
+ $url,
+ $response->HttpBody()
+ ));
+ break;
+ case 413: // limit
+ throw new Exceptions\HttpOverLimitError(sprintf(
+ Lang::translate('413 Over limit for [%s] [%s]'),
+ $url,
+ $response->HttpBody()
+ ));
+ break;
+ default:
+ // everything is fine here, we're fine, how are you?
+ break;
+ }
+
+ // free the handle
+ $http->close();
+
+ // return the HttpResponse object
+ $this->getLogger()->info('HTTP STATUS [{code}]', array(
+ 'code' => $response->httpStatus()
+ ));
+
+ return $response;
+ }
+
+ /**
+ * Sets default values for name, region, URL type for a service
+ *
+ * Once these are set (and they can also be set by defining global
+ * constants), then you do not need to specify these values when
+ * creating new service objects.
+ *
+ * @api
+ * @param string $service the name of a supported service; e.g. 'Compute'
+ * @param string $name the service name; e.g., 'cloudServersOpenStack'
+ * @param string $region the region name; e.g., 'LON'
+ * @param string $urltype the type of URL to use; e.g., 'internalURL'
+ * @return void
+ * @throws UnrecognizedServiceError
+ */
+ public function setDefaults(
+ $service,
+ $name = null,
+ $region = null,
+ $urltype = null
+ ) {
+
+ if (!isset($this->defaults[$service])) {
+ throw new Exceptions\UnrecognizedServiceError(sprintf(
+ Lang::translate('Service [%s] is not recognized'), $service
+ ));
+ }
+
+ if (isset($name)) {
+ $this->defaults[$service]['name'] = $name;
+ }
+
+ if (isset($region)) {
+ $this->defaults[$service]['region'] = $region;
+ }
+
+ if (isset($urltype)) {
+ $this->defaults[$service]['urltype'] = $urltype;
+ }
+ }
+
+ /**
+ * Allows the user to define a function for tracking uploads
+ *
+ * This can be used to implement a progress bar or similar function. The
+ * callback function is called with a single parameter, the length of the
+ * data that is being uploaded on this call.
+ *
+ * @param callable $callback the name of a global callback function, or an
+ * array($object, $functionname)
+ * @return void
+ */
+ public function setUploadProgressCallback($callback)
+ {
+ $this->_user_write_progress_callback_func = $callback;
+ }
+
+ /**
+ * Allows the user to define a function for tracking downloads
+ *
+ * This can be used to implement a progress bar or similar function. The
+ * callback function is called with a single parameter, the length of the
+ * data that is being downloaded on this call.
+ *
+ * @param callable $callback the name of a global callback function, or an
+ * array($object, $functionname)
+ * @return void
+ */
+ public function setDownloadProgressCallback($callback)
+ {
+ $this->_user_read_progress_callback_func = $callback;
+ }
+
+ /**
+ * Callback function to handle reads for file uploads
+ *
+ * Internal function for handling file uploads. Note that, although this
+ * function's visibility is public, this is only because it must be called
+ * from the HttpRequest interface. This should NOT be called by users
+ * directly.
+ *
+ * @param resource $ch a CURL handle
+ * @param resource $fd a file descriptor
+ * @param integer $length the amount of data to read
+ * @return string the data read
+ * @codeCoverageIgnore
+ */
+ public function _read_cb($ch, $fd, $length)
+ {
+ $data = fread($fd, $length);
+ $len = strlen($data);
+ if (isset($this->_user_write_progress_callback_func)) {
+ call_user_func($this->_user_write_progress_callback_func, $len);
+ }
+ return $data;
+ }
+
+ /**
+ * Callback function to handle writes for file downloads
+ *
+ * Internal function for handling file downloads. Note that, although this
+ * function's visibility is public, this is only because it must be called
+ * via the HttpRequest interface. This should NOT be called by users
+ * directly.
+ *
+ * @param resource $ch a CURL handle
+ * @param string $data the data to be written to a file
+ * @return integer the number of bytes written
+ * @codeCoverageIgnore
+ */
+ public function _write_cb($ch, $data)
+ {
+ $url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
+
+ if (false === ($fp = $this->getFileDescriptor($url))) {
+ throw new Exceptions\HttpUrlError(sprintf(
+ Lang::translate('Cannot find file descriptor for URL [%s]'), $url)
+ );
+ }
+
+ $dlen = strlen($data);
+ fwrite($fp, $data, $dlen);
+
+ // call used callback function
+ if (isset($this->_user_read_progress_callback_func)) {
+ call_user_func($this->_user_read_progress_callback_func, $dlen);
+ }
+
+ // MUST return the length to CURL
+ return $dlen;
+ }
+
+ /**
+ * exports saved token, expiration, tenant, and service catalog as an array
+ *
+ * This could be stored in a cache (APC or disk file) and reloaded using
+ * ImportCredentials()
+ *
+ * @return array
+ */
+ public function exportCredentials()
+ {
+ $this->authenticate();
+
+ $array = array();
+
+ foreach ($this->getExportItems() as $key) {
+ $array[$key] = $this->$key;
+ }
+
+ return $array;
+ }
+
+ /**
+ * imports credentials from an array
+ *
+ * Takes the same values as ExportCredentials() and reuses them.
+ *
+ * @return void
+ */
+ public function importCredentials(array $values)
+ {
+ foreach ($this->getExportItems() as $item) {
+ $this->$item = $values[$item];
+ }
+ }
+
+ /********** FACTORY METHODS **********
+ *
+ * These methods are provided to permit easy creation of services
+ * (for example, Nova or Swift) from a connection object. As new
+ * services are supported, factory methods should be provided here.
+ */
+
+ /**
+ * Creates a new ObjectStore object (Swift/Cloud Files)
+ *
+ * @api
+ * @param string $name the name of the Object Storage service to attach to
+ * @param string $region the name of the region to use
+ * @param string $urltype the URL type (normally "publicURL")
+ * @return ObjectStore
+ */
+ public function objectStore($name = null, $region = null, $urltype = null)
+ {
+ return $this->service('ObjectStore', $name, $region, $urltype);
+ }
+
+ /**
+ * Creates a new Compute object (Nova/Cloud Servers)
+ *
+ * @api
+ * @param string $name the name of the Compute service to attach to
+ * @param string $region the name of the region to use
+ * @param string $urltype the URL type (normally "publicURL")
+ * @return Compute
+ */
+ public function compute($name = null, $region = null, $urltype = null)
+ {
+ return $this->service('Compute', $name, $region, $urltype);
+ }
+
+ /**
+ * Creates a new Orchestration (heat) service object
+ *
+ * @api
+ * @param string $name the name of the Compute service to attach to
+ * @param string $region the name of the region to use
+ * @param string $urltype the URL type (normally "publicURL")
+ * @return Orchestration\Service
+ * @codeCoverageIgnore
+ */
+ public function orchestration($name = null, $region = null, $urltype = null)
+ {
+ return $this->service('Orchestration', $name, $region, $urltype);
+ }
+
+ /**
+ * Creates a new VolumeService (cinder) service object
+ *
+ * This is a factory method that is Rackspace-only (NOT part of OpenStack).
+ *
+ * @param string $name the name of the service (e.g., 'cloudBlockStorage')
+ * @param string $region the region (e.g., 'DFW')
+ * @param string $urltype the type of URL (e.g., 'publicURL');
+ */
+ public function volumeService($name = null, $region = null, $urltype = null)
+ {
+ return $this->service('Volume', $name, $region, $urltype);
+ }
+
+ /**
+ * Generic Service factory method
+ *
+ * Contains code reused by the other service factory methods.
+ *
+ * @param string $class the name of the Service class to produce
+ * @param string $name the name of the Compute service to attach to
+ * @param string $region the name of the region to use
+ * @param string $urltype the URL type (normally "publicURL")
+ * @return Service (or subclass such as Compute, ObjectStore)
+ * @throws ServiceValueError
+ */
+ public function service($class, $name = null, $region = null, $urltype = null)
+ {
+ // debug message
+ $this->getLogger()->info('Factory for class [{class}] [{name}/{region}/{urlType}]', array(
+ 'class' => $class,
+ 'name' => $name,
+ 'region' => $region,
+ 'urlType' => $urltype
+ ));
+
+ // Strips off base namespace
+ $class = preg_replace('#\\\?OpenCloud\\\#', '', $class);
+
+ // check for defaults
+ $default = $this->getDefault($class);
+
+ // report errors
+ if (!$name = $name ?: $default['name']) {
+ throw new Exceptions\ServiceValueError(sprintf(
+ Lang::translate('No value for %s name'),
+ $class
+ ));
+ }
+
+ if (!$region = $region ?: $default['region']) {
+ throw new Exceptions\ServiceValueError(sprintf(
+ Lang::translate('No value for %s region'),
+ $class
+ ));
+ }
+
+ if (!$urltype = $urltype ?: $default['urltype']) {
+ throw new Exceptions\ServiceValueError(sprintf(
+ Lang::translate('No value for %s URL type'),
+ $class
+ ));
+ }
+
+ // return the object
+ $fullclass = 'OpenCloud\\' . $class . '\\Service';
+
+ return new $fullclass($this, $name, $region, $urltype);
+ }
+
+ /**
+ * returns a service catalog item
+ *
+ * This is a helper function used to list service catalog items easily
+ */
+ public function serviceCatalogItem($info = array())
+ {
+ return new ServiceCatalogItem($info);
+ }
+
+}