* This is an HTTP client class for Cloud Files. It uses PHP's cURL module
* to handle the actual HTTP request/response. This is NOT a generic HTTP
* client class and is only used to abstract out the HTTP communication for
* the PHP Cloud Files API.
* This module was designed to re-use existing HTTP(S) connections between
* subsequent operations. For example, performing multiple PUT operations
* will re-use the same connection.
* This modules also provides support for streaming content into and out
* of Cloud Files. The majority (all?) of the PHP HTTP client modules expect
* to read the server's response into a string variable. This will not work
* with large files without killing your server. Methods like,
* get_object_to_stream() and put_object() take an open filehandle
* argument for streaming data out of or into Cloud Files.
* Requres PHP 5.x (for Exceptions and OO syntax)
* See COPYING for license information.
* @author Eric "EJ" Johnson <ej@racklabs.com>
* @copyright Copyright (c) 2008, Rackspace US, Inc.
* @package php-cloudfiles-http
require_once("cloudfiles_exceptions.php");
@define("PHP_CF_VERSION", "1.7.11");
@define("USER_AGENT", sprintf("PHP-CloudFiles/%s", PHP_CF_VERSION));
@define("MAX_HEADER_NAME_LEN", 128);
@define("MAX_HEADER_VALUE_LEN", 256);
@define("ACCOUNT_CONTAINER_COUNT", "X-Account-Container-Count");
@define("ACCOUNT_BYTES_USED", "X-Account-Bytes-Used");
@define("ACCOUNT_KEY", "X-Account-Meta-Key");
@define("ACCOUNT_METADATA_HEADER_PREFIX", "X-Account-Meta-");
@define("CONTAINER_OBJ_COUNT", "X-Container-Object-Count");
@define("CONTAINER_BYTES_USED", "X-Container-Bytes-Used");
@define("CONTAINER_METADATA_HEADER_PREFIX", "X-Container-Meta-");
@define("DELETE_AFTER", "X-Delete-After");
@define("DELETE_AT", "X-Delete-At");
@define("MANIFEST_HEADER", "X-Object-Manifest");
@define("METADATA_HEADER_PREFIX", "X-Object-Meta-");
@define("CONTENT_HEADER_PREFIX", "Content-");
@define("ACCESS_CONTROL_HEADER_PREFIX", "Access-Control-");
@define("ORIGIN_HEADER", "Origin");
@define("CDN_URI", "X-CDN-URI");
@define("CDN_SSL_URI", "X-CDN-SSL-URI");
@define("CDN_STREAMING_URI", "X-CDN-Streaming-URI");
@define("CDN_ENABLED", "X-CDN-Enabled");
@define("CDN_LOG_RETENTION", "X-Log-Retention");
@define("CDN_ACL_USER_AGENT", "X-User-Agent-ACL");
@define("CDN_ACL_REFERRER", "X-Referrer-ACL");
@define("CDN_TTL", "X-TTL");
@define("CDNM_URL", "X-CDN-Management-Url");
@define("STORAGE_URL", "X-Storage-Url");
@define("AUTH_TOKEN", "X-Auth-Token");
@define("AUTH_USER_HEADER", "X-Auth-User");
@define("AUTH_KEY_HEADER", "X-Auth-Key");
@define("AUTH_USER_HEADER_LEGACY", "X-Storage-User");
@define("AUTH_KEY_HEADER_LEGACY", "X-Storage-Pass");
@define("AUTH_TOKEN_LEGACY", "X-Storage-Token");
@define("CDN_EMAIL", "X-Purge-Email");
@define("DESTINATION", "Destination");
@define("ETAG_HEADER", "ETag");
@define("LAST_MODIFIED_HEADER", "Last-Modified");
@define("CONTENT_TYPE_HEADER", "Content-Type");
@define("CONTENT_LENGTH_HEADER", "Content-Length");
@define("USER_AGENT_HEADER", "User-Agent");
* HTTP/cURL wrapper for Cloud Files
* This class should not be used directly. It's only purpose is to abstract
* out the HTTP communication from the main API.
* @package php-cloudfiles-http
class UpdraftPlus_CF_Http
# Authentication instance variables
# Request/response variables
private $response_status;
private $response_reason;
# Variables used for content/header callbacks
private $_user_read_progress_callback_func;
private $_user_write_progress_callback_func;
private $_write_callback_type;
private $_account_metadata;
private $_account_container_count;
private $_account_bytes_used;
private $_container_metadata;
private $_container_object_count;
private $_container_bytes_used;
private $_obj_delete_after;
private $_obj_last_modified;
private $_obj_content_type;
private $_obj_content_length;
private $_obj_write_resource;
private $_obj_write_string;
private $_cdn_streaming_uri;
private $_cdn_log_retention;
private $_cdn_acl_user_agent;
private $_cdn_acl_referrer;
function __construct($api_version)
$this->cabundle_path = NULL;
$this->api_version = $api_version;
$this->storage_url = NULL;
$this->auth_token = NULL;
$this->response_status = NULL;
$this->response_reason = NULL;
# Curl connections array - since there is no way to "re-set" the
# connection paramaters for a cURL handle, we keep an array of
# the unique use-cases and funnel all of those same type
# requests through the appropriate curl connection.
$this->connections = array(
"GET_CALL" => NULL, # GET objects/containers/lists
"PUT_OBJ" => NULL, # PUT object
"HEAD" => NULL, # HEAD requests
"PUT_CONT" => NULL, # PUT container
"DEL_POST" => NULL, # DELETE containers/objects, POST objects
"COPY" => null, # COPY objects
$this->_user_read_progress_callback_func = NULL;
$this->_user_write_progress_callback_func = NULL;
$this->_write_callback_type = NULL;
$this->_text_list = array();
$this->_return_list = NULL;
$this->_account_metadata = array();
$this->_account_key = NULL;
$this->_account_container_count = 0;
$this->_account_bytes_used = 0;
$this->_container_metadata = array();
$this->_container_object_count = 0;
$this->_container_bytes_used = 0;
$this->_obj_delete_after = NULL;
$this->_obj_delete_at = NULL;
$this->_obj_write_resource = NULL;
$this->_obj_write_string = "";
$this->_obj_last_modified = NULL;
$this->_obj_content_type = NULL;
$this->_obj_content_length = NULL;
$this->_obj_metadata = array();
$this->_obj_manifest = NULL;
$this->_obj_headers = NULL;
$this->_cdn_enabled = NULL;
$this->_cdn_ssl_uri = NULL;
$this->_cdn_streaming_uri = NULL;
$this->_cdn_log_retention = NULL;
$this->_cdn_acl_user_agent = NULL;
$this->_cdn_acl_referrer = NULL;
# The OS list with a PHP without an updated CA File for CURL to
# connect to SSL Websites. It is the first 3 letters of the PHP_OS
$OS_CAFILE_NONUPDATED=array(
// We don't want this happening automatically - since the UpdraftPlus default is to use our own certificate already
// if (in_array((strtolower (substr(PHP_OS, 0,3))), $OS_CAFILE_NONUPDATED))
// $this->ssl_use_cabundle();
function ssl_use_cabundle($path=NULL)
$this->cabundle_path = $path;
$this->cabundle_path = UPDRAFTPLUS_DIR.'/includes/cacert.pem';
if (!file_exists($this->cabundle_path)) {
throw new IOException("Could not use CA bundle: "
# Uses separate cURL connection to authenticate
function authenticate($user, $pass, $acct=NULL, $host=NULL)
sprintf("%s: %s", AUTH_USER_HEADER_LEGACY, $user),
sprintf("%s: %s", AUTH_KEY_HEADER_LEGACY, $pass),
$path[] = rawurlencode(sprintf("v%d",$this->api_version));
$path[] = rawurlencode($acct);
sprintf("%s: %s", AUTH_USER_HEADER, $user),
sprintf("%s: %s", AUTH_KEY_HEADER, $pass),
$url = implode("/", $path);
if (!is_null($this->cabundle_path)) {
curl_setopt($curl_ch, CURLOPT_CAINFO, $this->cabundle_path);
if (defined('UPDRAFTPLUS_SSL_DISABLEVERIFY')) {
curl_setopt($curl_ch, CURLOPT_SSL_VERIFYPEER, UPDRAFTPLUS_SSL_DISABLEVERIFY);
} elseif (UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify')) {
curl_setopt($curl_ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl_ch, CURLOPT_VERBOSE, $this->dbug);
curl_setopt($curl_ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl_ch, CURLOPT_MAXREDIRS, 4);
curl_setopt($curl_ch, CURLOPT_HEADER, 0);
curl_setopt($curl_ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl_ch, CURLOPT_USERAGENT, USER_AGENT);
curl_setopt($curl_ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl_ch, CURLOPT_HEADERFUNCTION,array(&$this,'_auth_hdr_cb'));
curl_setopt($curl_ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl_ch, CURLOPT_URL, $url);
return array($this->response_status, $this->response_reason,
$this->storage_url, $this->cdnm_url, $this->auth_token);
function list_cdn_containers($enabled_only)
$url_path = $this->_make_path("CDN");
$this->_write_callback_type = "TEXT_LIST";
$return_code = $this->_send_request($conn_type, $url_path .
$return_code = $this->_send_request($conn_type, $url_path);
$this->error_str .= ": Failed to obtain valid HTTP response.";
return array(0,$this->error_str,array());
if ($return_code == 401) {
return array($return_code,"Unauthorized",array());
if ($return_code == 404) {
return array($return_code,"Account not found.",array());
if ($return_code == 204) {
return array($return_code,"Account has no CDN enabled Containers.",
if ($return_code == 200) {
return array($return_code,$this->response_reason,$this->_text_list);
$this->error_str = "Unexpected HTTP response: ".$this->response_reason;
return array($return_code,$this->error_str,array());
# (CDN) DELETE /v1/Account/Container or /v1/Account/Container/Object
function purge_from_cdn($path, $email=null)
throw new SyntaxException("Path not set");
$url_path = $this->_make_path("CDN", NULL, $path);
$hdrs = array(CDN_EMAIL => $email);
$return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"DELETE");
$return_code = $this->_send_request("DEL_POST",$url_path,null,"DELETE");
# (CDN) POST /v1/Account/Container
function update_cdn_container($container_name, $ttl=86400, $cdn_log_retention=False,
$cdn_acl_user_agent="", $cdn_acl_referrer)
if ($container_name == "")
throw new SyntaxException("Container name not set.");
if ($container_name != "0" and !isset($container_name))
throw new SyntaxException("Container name not set.");
$url_path = $this->_make_path("CDN", $container_name);
CDN_LOG_RETENTION => $cdn_log_retention ? "True" : "False",
CDN_ACL_USER_AGENT => $cdn_acl_user_agent,
CDN_ACL_REFERRER => $cdn_acl_referrer,
$return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST");
if ($return_code == 401) {
$this->error_str = "Unauthorized";
return array($return_code, $this->error_str, NULL);
if ($return_code == 404) {
$this->error_str = "Container not found.";
return array($return_code, $this->error_str, NULL);
if ($return_code != 202) {
$this->error_str="Unexpected HTTP response: ".$this->response_reason;
return array($return_code, $this->error_str, NULL);
return array($return_code, "Accepted", $this->_cdn_uri, $this->_cdn_ssl_uri);
# (CDN) PUT /v1/Account/Container
function add_cdn_container($container_name, $ttl=86400)
if ($container_name == "")
throw new SyntaxException("Container name not set.");
if ($container_name != "0" and !isset($container_name))
throw new SyntaxException("Container name not set.");
$url_path = $this->_make_path("CDN", $container_name);
$return_code = $this->_send_request("PUT_CONT", $url_path, $hdrs);
if ($return_code == 401) {
$this->error_str = "Unauthorized";
return array($return_code,$this->response_reason,False);
if (!in_array($return_code, array(201,202))) {
$this->error_str="Unexpected HTTP response: ".$this->response_reason;
return array($return_code,$this->response_reason,False);
return array($return_code,$this->response_reason,$this->_cdn_uri,
# (CDN) POST /v1/Account/Container
function remove_cdn_container($container_name)
if ($container_name == "")
throw new SyntaxException("Container name not set.");
if ($container_name != "0" and !isset($container_name))
throw new SyntaxException("Container name not set.");
$url_path = $this->_make_path("CDN", $container_name);
$hdrs = array(CDN_ENABLED => "False");
$return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST");
if ($return_code == 401) {
$this->error_str = "Unauthorized";
return array($return_code, $this->error_str);
if ($return_code == 404) {
$this->error_str = "Container not found.";
return array($return_code, $this->error_str);
if ($return_code != 202) {
$this->error_str="Unexpected HTTP response: ".$this->response_reason;
return array($return_code, $this->error_str);
return array($return_code, "Accepted");
function head_cdn_container($container_name)
if ($container_name == "")
throw new SyntaxException("Container name not set.");
if ($container_name != "0" and !isset($container_name))
throw new SyntaxException("Container name not set.");
$url_path = $this->_make_path("CDN", $container_name);
$return_code = $this->_send_request($conn_type, $url_path, NULL, "GET", True);
$this->error_str .= ": Failed to obtain valid HTTP response.";
return array(0,$this->error_str,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if ($return_code == 401) {
return array($return_code,"Unauthorized",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if ($return_code == 404) {
return array($return_code,"Account not found.",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if ($return_code == 204) {
return array($return_code,$this->response_reason,
$this->_cdn_enabled, $this->_cdn_ssl_uri,
$this->_cdn_streaming_uri,
$this->_cdn_uri, $this->_cdn_ttl,
$this->_cdn_log_retention,
$this->_cdn_acl_user_agent,
return array($return_code,$this->response_reason,
$this->_cdn_log_retention,
$this->_cdn_acl_user_agent,
$this->_cdn_acl_referrer,
function list_containers($limit=0, $marker=NULL)
$url_path = $this->_make_path();
$params[] = "limit=$limit";
$params[] = "marker=".rawurlencode($marker);
$url_path .= "?" . implode("&", $params);
$this->_write_callback_type = "TEXT_LIST";
$return_code = $this->_send_request($conn_type, $url_path);
$this->error_str .= ": Failed to obtain valid HTTP response.";
return array(0,$this->error_str,array());
if ($return_code == 204) {
return array($return_code, "Account has no containers.", array());
if ($return_code == 404) {
$this->error_str = "Invalid account name for authentication token.";
return array($return_code,$this->error_str,array());
if ($return_code == 200) {
return array($return_code, $this->response_reason, $this->_text_list);
$this->error_str = "Unexpected HTTP response: ".$this->response_reason;
return array($return_code,$this->error_str,array());