r101245 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r101244‎ | r101245 | r101246 >
Date:03:25, 29 October 2011
Author:nelson
Status:ok (Comments)
Tags:
Comment:
improve function documentation; move from %2F to - in container names; move to current version of php-cloudfiles.
Modified paths:
  • /trunk/extensions/SwiftMedia/SwiftMedia.body.php (modified) (history)
  • /trunk/extensions/SwiftMedia/copyover (modified) (history)
  • /trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles.php (deleted) (history)
  • /trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles_http.php (deleted) (history)
  • /trunk/extensions/SwiftMedia/wmf/rewrite.py (modified) (history)

Diff [purge]

Index: trunk/extensions/SwiftMedia/copyover
@@ -1,8 +1,6 @@
22 #!/bin/sh
33
44 scp rnelson@ersch.wikimedia.org:/var/www/extensions/SwiftMedia/{SwiftMedia.body.php,SwiftMedia.i18n.php,SwiftMedia.php,TODO} .
5 -scp -r rnelson@ersch.wikimedia.org:/usr/share/php-cloudfiles .
6 -rm php-cloudfiles/*~
75 scp rnelson@alsted.wikimedia.org:/etc/swift/proxy-server.conf proxy-server.sample
86 scp rnelson@alsted.wikimedia.org:/usr/local/lib/python2.6/dist-packages/wmf/{client.py,__init__.py,rewrite.py} wmf/
97 scp rnelson@alsted.wikimedia.org:test_rewrite.py .
Index: trunk/extensions/SwiftMedia/wmf/rewrite.py
@@ -11,10 +11,6 @@
1212 import wmf.client
1313 import time
1414
15 -# the auth system turns our login and key into an account / token pair.
16 -# the account remains valid forever, but the token times out.
17 -account = 'AUTH_dea4a45c-a80b-43b5-8e8b-e452f0dc778f'
18 -
1915 # Copy2 is hairy. If we were only opening a URL, and returning it, we could
2016 # just return the open file handle, and webob would take care of reading from
2117 # the socket and returning the data to the client machine. If we were only
@@ -33,12 +29,17 @@
3430 """
3531 token = None
3632
37 - def __init__(self, conn, app, url, container, obj, authurl, login, key, content_type=None, modified=None):
 33+ def __init__(self, conn, app, url, container, obj, authurl, login, key,
 34+ content_type=None, modified=None):
3835 self.app = app
3936 self.conn = conn
4037 if self.token is None:
4138 (account, self.token) = wmf.client.get_auth(authurl, login, key)
4239 if modified is not None:
 40+ # The issue here is that we need to keep the timestamp between the
 41+ # thumb server and us. The Migration-Timestamp header was in 1.2,
 42+ # but was deprecated. They likely have a different solution for
 43+ # setting the timestamp on an uploaded file.
4344 h = {'!Migration-Timestamp!': '%s' % modified}
4445 else:
4546 h = {}
@@ -46,9 +47,11 @@
4748 container, obj, content_type=content_type, headers=h)
4849
4950 def __iter__(self):
 51+ # We're an iterator; we get passed back to wsgi as a consumer.
5052 return self
5153
5254 def next(self):
 55+ # We read from the thumb server, write out to Swift, and return it.
5356 data = self.conn.read(4096)
5457 if not data:
5558 # if we get a 401 error, it's okay, but we should re-auth.
@@ -57,7 +60,8 @@
5861 except wmf.client.ClientException, err:
5962 self.app.logger.warn("PUT Status: %d" % err.http_status)
6063 if err.http_status == 401:
61 - # not worth retrying the write.
 64+ # not worth retrying the write. Thumb will get saved
 65+ # the next time.
6266 self.token = None
6367 else:
6468 raise
@@ -66,6 +70,10 @@
6771 return data
6872
6973 class ObjectController(object):
 74+ """
 75+ We're an object controller that doesn't actually do anything, but we
 76+ will need these arguments later
 77+ """
7078
7179 def __init__(self):
7280 self.response_args = []
@@ -78,7 +86,7 @@
7987 """
8088 Rewrite Media Store URLs so that swift knows how to deal.
8189
82 - Mostly it's a question of inserting the AUTH_ string, and escaping the %2F's in the container section.
 90+ Mostly it's a question of inserting the AUTH_ string, and changing / to - in the container section.
8391 """
8492
8593 def __init__(self, app, conf):
@@ -92,16 +100,22 @@
93101 self.user_agent = conf['user_agent'].strip()
94102
95103 def handle404(self, reqorig, url, container, obj):
96 - """ return a webob.Response which reads the request, but from the thumb host.
97104 """
 105+ Return a webob.Response which fetches the thumbnail from the thumb
 106+ host, potentially writes it out to Swift so we don't 404 next time,
 107+ and returns it. Note also that the thumb host might write it out
 108+ to Swift so we don't have to.
 109+ """
98110 # go to the thumb media store for unknown files
99111 reqorig.host = self.thumbhost
100 - # upload doesn't like our User-agent, otherwise we could call it using urllib2.url()
 112+ # upload doesn't like our User-agent, otherwise we could call it
 113+ # using urllib2.url()
101114 opener = urllib2.build_opener()
102115 opener.addheaders = [('User-agent', self.user_agent)]
103 - # At least in theory, we shouldn't be handing out links to files that we don't have
104 - # (or in the case of thumbs, can't generate). However, someone may have a formerly
105 - # valid link to a file, so we should do them the favor of giving them a 404.
 116+ # At least in theory, we shouldn't be handing out links to originals
 117+ # that we don't have (or in the case of thumbs, can't generate).
 118+ # However, someone may have a formerly valid link to a file, so we
 119+ # should do them the favor of giving them a 404.
106120 try:
107121 upcopy = opener.open(reqorig.url)
108122 except urllib2.HTTPError,status:
@@ -127,7 +141,7 @@
128142 return resp
129143
130144 def __call__(self, env, start_response):
131 - #try:
 145+ #try: commented-out while debugging so you can see where stuff happened.
132146 req = webob.Request(env)
133147 # PUT requests never need rewriting.
134148 if req.method == 'PUT':
@@ -146,14 +160,14 @@
147161 match = re.match(r'/(.*?)/(.*?)/(.*)', req.path)
148162 if match:
149163 # Our target URL is as follows:
150 - # https://alsted.wikimedia.org:8080/v1/AUTH_6790933748e741268babd69804c6298b/wikipedia%252Fen/2/25/Machinesmith.png
 164+ # https://alsted.wikimedia.org:8080/v1/AUTH_6790933748e741268babd69804c6298b/wikipedia-en/2/25/Machinesmith.png
151165
152166 # quote slashes in the container name
153 - container = "%s%%2F%s" % (match.group(1), match.group(2)) #02
 167+ container = "%s-%s" % (match.group(1), match.group(2)) #02
154168 obj = match.group(3)
155169 # include the thumb in the container.
156170 if obj.startswith("thumb/"): #03
157 - container += "%2Fthumb"
 171+ container += "-thumb"
158172 obj = obj[len("thumb/"):]
159173
160174 if not obj:
Index: trunk/extensions/SwiftMedia/SwiftMedia.body.php
@@ -28,29 +28,13 @@
2929 * @private
3030 */
3131 var
32 - $conn; # our connection to the Swift proxy.
33 - # $fileExists, # does the file file exist on disk? (loadFromXxx)
34 - # $historyLine, # Number of line to return by nextHistoryLine() (constructor)
35 - # $historyRes, # result of the query for the file's history (nextHistoryLine)
36 - # $width, # \
37 - # $height, # |
38 - # $bits, # --- returned by getimagesize (loadFromXxx)
39 - # $attr, # /
40 - # $media_type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
41 - # $mime, # MIME type, determined by MimeMagic::guessMimeType
42 - # $major_mime, # Major mime type
43 - # $minor_mime, # Minor mime type
44 - # $size, # Size in bytes (loadFromXxx)
45 - # $metadata, # Handler-specific metadata
46 - # $timestamp, # Upload timestamp
47 - # $sha1, # SHA-1 base 36 content hash
48 - # $user, $user_text, # User, who uploaded the file
49 - # $description, # Description of current revision of the file
50 - # $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
51 - # $upgraded, # Whether the row was upgraded on load
52 - # $locked, # True if the image row is locked
53 - # $missing, # True if file is not present in file system. Not to be cached in memcached
54 - # $deleted; # Bitfield akin to rev_deleted
 32+ $conn, # our connection to the Swift proxy.
 33+ $fileExists, # does the file file exist on disk? (loadFromXxx)
 34+ $dataLoaded, # Whether or not all this has been loaded from the database (loadFromXxx)
 35+ $swiftuser,
 36+ $swiftkey,
 37+ $authurl,
 38+ $container;
5539 /**#@-*/
5640
5741 /**
@@ -185,7 +169,7 @@
186170
187171 // see if the file exists, and if it exists, is not too old.
188172 $conn = $this->repo->connect();
189 - $container = $this->repo->get_container( $conn, $this->repo->container . '%2Fthumb' );
 173+ $container = $this->repo->get_container( $conn, $this->repo->container . '-thumb' );
190174 try {
191175 $pic = $container->get_object( $this->getRel() . "/$thumbName" );
192176 } catch ( NoSuchObjectException $e ) {
@@ -216,7 +200,7 @@
217201 // what if they didn't actually write out a thumbnail? Check the file size.
218202 if ( $thumb && file_exists( $thumbPath ) && filesize( $thumbPath ) ) {
219203 // Store the thumbnail into Swift, but in the thumb version of the container.
220 - wfDebug( __METHOD__ . 'Creating thumb ' . $this->getRel() . "/$thumbName\n" );
 204+ wfDebug( __METHOD__ . ': creating thumb ' . $this->getRel() . "/$thumbName\n" );
221205 $this->repo->write_swift_object( $thumbPath, $container, $this->getRel() . "/$thumbName" );
222206 // php-cloudfiles throws exceptions, so failure never gets here.
223207 }
@@ -264,7 +248,7 @@
265249 $prefix = $this->getRel();
266250 }
267251 $conn = $this->repo->connect();
268 - $container = $this->repo->get_container( $conn, $this->repo->container . '%2Fthumb' );
 252+ $container = $this->repo->get_container( $conn, $this->repo->container . '-thumb' );
269253 $files = $container->list_objects( 0, NULL, $prefix );
270254 array_unshift( $files, 'unused' ); # return an unused $dir.
271255 return $files;
@@ -272,14 +256,14 @@
273257
274258 /**
275259 * Delete cached transformed files
276 - * @param $dir string If needed for this repo, the directory prefix.
 260+ * @param $dir string Should always be the 'unused' we specified earlier.
277261 * @param $files array of strings listing the thumbs to be deleted.
278262 */
279263 function purgeThumbList( $dir, $files ) {
280264 global $wgExcludeFromThumbnailPurge;
281265
282266 $conn = $this->repo->connect();
283 - $container = $this->repo->get_container( $conn, $this->repo->container . '%2Fthumb' );
 267+ $container = $this->repo->get_container( $conn, $this->repo->container . '-thumb' );
284268 foreach ( $files as $file ) {
285269 // Only remove files not in the $wgExcludeFromThumbnailPurge configuration variable
286270 $ext = pathinfo( $file, PATHINFO_EXTENSION );
@@ -544,7 +528,7 @@
545529 function append( $srcPath, $toAppendPath, $flags = 0 ) {
546530 // Count the number of files whose names start with $toAppendPath
547531 $conn = $this->connect();
548 - $container = $this->repo->get_container( $conn, $this->repo->container . "%2Ftemp" );
 532+ $container = $this->repo->get_container( $conn, $this->repo->container . "-temp" );
549533 $nextone = count( $container->list_objects( 0, NULL, $srcPath ) );
550534
551535 // Do the append to the next name
@@ -562,7 +546,7 @@
563547 */
564548 function appendFinish( $toAppendPath ) {
565549 $conn = $this->connect();
566 - $container = $this->repo->get_container( $conn, $this->repo->container . '%2Ftemp' );
 550+ $container = $this->repo->get_container( $conn, $this->repo->container . '-temp' );
567551 $parts = $container->list_objects( 0, NULL, $toAppendPath);
568552 // list_objects() returns a sorted list.
569553
@@ -671,8 +655,6 @@
672656 throw new MWException( "Missing Content-Type: $e" );
673657 } catch ( MisMatchedChecksumException $e ) {
674658 throw new MWException( __METHOD__ . "should not happen: '$e'" );
675 - # } catch (InvalidResponseException $e ) {
676 - # throw new MWException( __METHOD__ . "unexpected response '$e'" );
677659 }
678660
679661 try {
@@ -681,16 +663,20 @@
682664 throw new MWException( 'The object we just created does not exist: ' . $dstContainer->name . "/$dstRel: $e" );
683665 }
684666
 667+ try {
 668+ $srcObj = $srcContainer->get_object( $srcRel );
 669+ } catch ( NoSuchObjectException $e ) {
 670+ throw new MWException( 'Source file does not exist: ' . $srcContainer->name . "/$srcRel: $e" );
 671+ }
 672+
685673 wfDebug( __METHOD__ . ' copying to ' . $dstContainer->name . "/$dstRel from " . $srcContainer->name . "/$srcRel\n" );
686674
687675 try {
688 - $obj->copy( $srcContainer->name . "/$srcRel" );
 676+ $dstContainer->copy_object_from($srcObj,$srcContainer,$dstRel);
689677 } catch ( SyntaxException $e ) {
690678 throw new MWException( 'Source file does not exist: ' . $srcContainer->name . "/$srcRel: $e" );
691679 } catch ( MisMatchedChecksumException $e ) {
692680 throw new MWException( "Checksums do not match: $e" );
693 - # } catch (InvalidResponseException $e ) {
694 - # throw new MWException( __METHOD__ . "unexpected response '$e'" );
695681 }
696682 }
697683
@@ -875,11 +861,11 @@
876862 case 'public':
877863 return $this->container;
878864 case 'temp':
879 - return $this->container . '%2Ftemp';
 865+ return $this->container . '-temp';
880866 case 'deleted':
881 - return $this->container . '%2Fdeleted';
 867+ return $this->container . '-deleted';
882868 case 'thumb':
883 - return $this->container . '%2Fthumb';
 869+ return $this->container . '-thumb';
884870 default:
885871 return false;
886872 }
Index: trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles.php
@@ -1,2352 +0,0 @@
2 -<?php
3 -/**
4 - * This is the PHP Cloud Files API.
5 - *
6 - * <code>
7 - * # Authenticate to Cloud Files. The default is to automatically try
8 - * # to re-authenticate if an authentication token expires.
9 - * #
10 - * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
11 - * # file. This API ships with a newer version obtained directly from
12 - * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
13 - * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
14 - * #
15 - * $auth = new CF_Authentication($username, $api_key);
16 - * # $auth->ssl_use_cabundle(); # bypass cURL's old CA bundle
17 - * $auth->authenticate();
18 - *
19 - * # Establish a connection to the storage system
20 - * #
21 - * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
22 - * # file. This API ships with a newer version obtained directly from
23 - * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
24 - * # call the CF_Connection instance's 'ssl_use_cabundle()' method.
25 - * #
26 - * $conn = new CF_Connection($auth);
27 - * # $conn->ssl_use_cabundle(); # bypass cURL's old CA bundle
28 - *
29 - * # Create a remote Container and storage Object
30 - * #
31 - * $images = $conn->create_container("photos");
32 - * $bday = $images->create_object("first_birthday.jpg");
33 - *
34 - * # Upload content from a local file by streaming it. Note that we use
35 - * # a "float" for the file size to overcome PHP's 32-bit integer limit for
36 - * # very large files.
37 - * #
38 - * $fname = "/home/user/photos/birthdays/birthday1.jpg"; # filename to upload
39 - * $size = (float) sprintf("%u", filesize($fname));
40 - * $fp = open($fname, "r");
41 - * $bday->write($fp, $size);
42 - *
43 - * # Or... use a convenience function instead
44 - * #
45 - * $bday->load_from_filename("/home/user/photos/birthdays/birthday1.jpg");
46 - *
47 - * # Now, publish the "photos" container to serve the images by CDN.
48 - * # Use the "$uri" value to put in your web pages or send the link in an
49 - * # email message, etc.
50 - * #
51 - * $uri = $images->make_public();
52 - *
53 - * # Or... print out the Object's public URI
54 - * #
55 - * print $bday->public_uri();
56 - * </code>
57 - *
58 - * See the included tests directory for additional sample code.
59 - *
60 - * Requres PHP 5.x (for Exceptions and OO syntax) and PHP's cURL module.
61 - *
62 - * It uses the supporting "cloudfiles_http.php" module for HTTP(s) support and
63 - * allows for connection re-use and streaming of content into/out of Cloud Files
64 - * via PHP's cURL module.
65 - *
66 - * See COPYING for license information.
67 - *
68 - * @author Eric "EJ" Johnson <ej@racklabs.com>
69 - * @copyright Copyright (c) 2008, Rackspace US, Inc.
70 - * @package php-cloudfiles
71 - */
72 -
73 -/**
74 - */
75 -require_once("cloudfiles_exceptions.php");
76 -require("cloudfiles_http.php");
77 -define("DEFAULT_CF_API_VERSION", 1);
78 -define("MAX_CONTAINER_NAME_LEN", 256);
79 -define("MAX_OBJECT_NAME_LEN", 1024);
80 -define("MAX_OBJECT_SIZE", 5*1024*1024*1024+1);
81 -define("US_AUTHURL", "https://auth.api.rackspacecloud.com");
82 -define("UK_AUTHURL", "https://lon.auth.api.rackspacecloud.com");
83 -/**
84 - * Class for handling Cloud Files Authentication, call it's {@link authenticate()}
85 - * method to obtain authorized service urls and an authentication token.
86 - *
87 - * Example:
88 - * <code>
89 - * # Create the authentication instance
90 - * #
91 - * $auth = new CF_Authentication("username", "api_key");
92 - *
93 - * # NOTE: For UK Customers please specify your AuthURL Manually
94 - * # There is a Predfined constant to use EX:
95 - * #
96 - * # $auth = new CF_Authentication("username, "api_key", NULL, UK_AUTHURL);
97 - * # Using the UK_AUTHURL keyword will force the api to use the UK AuthUrl.
98 - * # rather then the US one. The NULL Is passed for legacy purposes and must
99 - * # be passed to function correctly.
100 - *
101 - * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
102 - * # file. This API ships with a newer version obtained directly from
103 - * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
104 - * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
105 - * #
106 - * # $auth->ssl_use_cabundle(); # bypass cURL's old CA bundle
107 - *
108 - * # Perform authentication request
109 - * #
110 - * $auth->authenticate();
111 - * </code>
112 - *
113 - * @package php-cloudfiles
114 - */
115 -class CF_Authentication
116 -{
117 - public $dbug;
118 - public $username;
119 - public $api_key;
120 - public $auth_host;
121 - public $account;
122 -
123 - /**
124 - * Instance variables that are set after successful authentication
125 - */
126 - public $storage_url;
127 - public $cdnm_url;
128 - public $auth_token;
129 -
130 - /**
131 - * Class constructor (PHP 5 syntax)
132 - *
133 - * @param string $username Mosso username
134 - * @param string $api_key Mosso API Access Key
135 - * @param string $account <i>Account name</i>
136 - * @param string $auth_host <i>Authentication service URI</i>
137 - */
138 - function __construct($username=NULL, $api_key=NULL, $account=NULL, $auth_host=US_AUTHURL)
139 - {
140 -
141 - $this->dbug = False;
142 - $this->username = $username;
143 - $this->api_key = $api_key;
144 - $this->account_name = $account;
145 - $this->auth_host = $auth_host;
146 -
147 - $this->storage_url = NULL;
148 - $this->cdnm_url = NULL;
149 - $this->auth_token = NULL;
150 -
151 - $this->cfs_http = new CF_Http(DEFAULT_CF_API_VERSION);
152 - }
153 -
154 - /**
155 - * Use the Certificate Authority bundle included with this API
156 - *
157 - * Most versions of PHP with cURL support include an outdated Certificate
158 - * Authority (CA) bundle (the file that lists all valid certificate
159 - * signing authorities). The SSL certificates used by the Cloud Files
160 - * storage system are perfectly valid but have been created/signed by
161 - * a CA not listed in these outdated cURL distributions.
162 - *
163 - * As a work-around, we've included an updated CA bundle obtained
164 - * directly from cURL's web site (http://curl.haxx.se). You can direct
165 - * the API to use this CA bundle by calling this method prior to making
166 - * any remote calls. The best place to use this method is right after
167 - * the CF_Authentication instance has been instantiated.
168 - *
169 - * You can specify your own CA bundle by passing in the full pathname
170 - * to the bundle. You can use the included CA bundle by leaving the
171 - * argument blank.
172 - *
173 - * @param string $path Specify path to CA bundle (default to included)
174 - */
175 - function ssl_use_cabundle($path=NULL)
176 - {
177 - $this->cfs_http->ssl_use_cabundle($path);
178 - }
179 -
180 - /**
181 - * Attempt to validate Username/API Access Key
182 - *
183 - * Attempts to validate credentials with the authentication service. It
184 - * either returns <kbd>True</kbd> or throws an Exception. Accepts a single
185 - * (optional) argument for the storage system API version.
186 - *
187 - * Example:
188 - * <code>
189 - * # Create the authentication instance
190 - * #
191 - * $auth = new CF_Authentication("username", "api_key");
192 - *
193 - * # Perform authentication request
194 - * #
195 - * $auth->authenticate();
196 - * </code>
197 - *
198 - * @param string $version API version for Auth service (optional)
199 - * @return boolean <kbd>True</kbd> if successfully authenticated
200 - * @throws AuthenticationException invalid credentials
201 - * @throws InvalidResponseException invalid response
202 - */
203 - function authenticate($version=DEFAULT_CF_API_VERSION)
204 - {
205 - list($status,$reason,$surl,$curl,$atoken) =
206 - $this->cfs_http->authenticate($this->username, $this->api_key,
207 - $this->account_name, $this->auth_host);
208 -
209 - if ($status == 401) {
210 - throw new AuthenticationException("Invalid username or access key.");
211 - }
212 - if ($status != 204 && $status != 200) {
213 - throw new InvalidResponseException(
214 - "Unexpected response (".$status."): ".$reason);
215 - }
216 -
217 - if (!($surl || $curl) || !$atoken) {
218 - throw new InvalidResponseException(
219 - "Expected headers missing from auth service.");
220 - }
221 - $this->storage_url = $surl;
222 - $this->cdnm_url = $curl;
223 - $this->auth_token = $atoken;
224 - return True;
225 - }
226 - /**
227 - * Use Cached Token and Storage URL's rather then grabbing from the Auth System
228 - *
229 - * Example:
230 - * <code>
231 - * #Create an Auth instance
232 - * $auth = new CF_Authentication();
233 - * #Pass Cached URL's and Token as Args
234 - * $auth->load_cached_credentials("auth_token", "storage_url", "cdn_management_url");
235 - * </code>
236 - *
237 - * @param string $auth_token A Cloud Files Auth Token (Required)
238 - * @param string $storage_url The Cloud Files Storage URL (Required)
239 - * @param string $cdnm_url CDN Management URL (Required)
240 - * @return boolean <kbd>True</kbd> if successful
241 - * @throws SyntaxException If any of the Required Arguments are missing
242 - */
243 - function load_cached_credentials($auth_token, $storage_url, $cdnm_url)
244 - {
245 - if(!$storage_url || !$cdnm_url)
246 - {
247 - throw new SyntaxException("Missing Required Interface URL's!");
248 - return False;
249 - }
250 - if(!$auth_token)
251 - {
252 - throw new SyntaxException("Missing Auth Token!");
253 - return False;
254 - }
255 -
256 - $this->storage_url = $storage_url;
257 - $this->cdnm_url = $cdnm_url;
258 - $this->auth_token = $auth_token;
259 - return True;
260 - }
261 - /**
262 - * Grab Cloud Files info to be Cached for later use with the load_cached_credentials method.
263 - *
264 - * Example:
265 - * <code>
266 - * #Create an Auth instance
267 - * $auth = new CF_Authentication("UserName","API_Key");
268 - * $auth->authenticate();
269 - * $array = $auth->export_credentials();
270 - * </code>
271 - *
272 - * @return array of url's and an auth token.
273 - */
274 - function export_credentials()
275 - {
276 - $arr = array();
277 - $arr['storage_url'] = $this->storage_url;
278 - $arr['cdnm_url'] = $this->cdnm_url;
279 - $arr['auth_token'] = $this->auth_token;
280 -
281 - return $arr;
282 - }
283 -
284 -
285 - /**
286 - * Make sure the CF_Authentication instance has authenticated.
287 - *
288 - * Ensures that the instance variables necessary to communicate with
289 - * Cloud Files have been set from a previous authenticate() call.
290 - *
291 - * @return boolean <kbd>True</kbd> if successfully authenticated
292 - */
293 - function authenticated()
294 - {
295 - if (!($this->storage_url || $this->cdnm_url) || !$this->auth_token) {
296 - return False;
297 - }
298 - return True;
299 - }
300 -
301 - /**
302 - * Toggle debugging - set cURL verbose flag
303 - */
304 - function setDebug($bool)
305 - {
306 - $this->dbug = $bool;
307 - $this->cfs_http->setDebug($bool);
308 - }
309 -}
310 -
311 -/**
312 - * Class for establishing connections to the Cloud Files storage system.
313 - * Connection instances are used to communicate with the storage system at
314 - * the account level; listing and deleting Containers and returning Container
315 - * instances.
316 - *
317 - * Example:
318 - * <code>
319 - * # Create the authentication instance
320 - * #
321 - * $auth = new CF_Authentication("username", "api_key");
322 - *
323 - * # Perform authentication request
324 - * #
325 - * $auth->authenticate();
326 - *
327 - * # Create a connection to the storage/cdn system(s) and pass in the
328 - * # validated CF_Authentication instance.
329 - * #
330 - * $conn = new CF_Connection($auth);
331 - *
332 - * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
333 - * # file. This API ships with a newer version obtained directly from
334 - * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
335 - * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
336 - * #
337 - * # $conn->ssl_use_cabundle(); # bypass cURL's old CA bundle
338 - * </code>
339 - *
340 - * @package php-cloudfiles
341 - */
342 -class CF_Connection
343 -{
344 - public $dbug;
345 - public $cfs_http;
346 - public $cfs_auth;
347 -
348 - /**
349 - * Pass in a previously authenticated CF_Authentication instance.
350 - *
351 - * Example:
352 - * <code>
353 - * # Create the authentication instance
354 - * #
355 - * $auth = new CF_Authentication("username", "api_key");
356 - *
357 - * # Perform authentication request
358 - * #
359 - * $auth->authenticate();
360 - *
361 - * # Create a connection to the storage/cdn system(s) and pass in the
362 - * # validated CF_Authentication instance.
363 - * #
364 - * $conn = new CF_Connection($auth);
365 - *
366 - * # If you are connecting via Rackspace servers and have access
367 - * # to the servicenet network you can set the $servicenet to True
368 - * # like this.
369 - *
370 - * $conn = new CF_Connection($auth, $servicenet=True);
371 - *
372 - * </code>
373 - *
374 - * If the environement variable RACKSPACE_SERVICENET is defined it will
375 - * force to connect via the servicenet.
376 - *
377 - * @param obj $cfs_auth previously authenticated CF_Authentication instance
378 - * @param boolean $servicenet enable/disable access via Rackspace servicenet.
379 - * @throws AuthenticationException not authenticated
380 - */
381 - function __construct($cfs_auth, $servicenet=False)
382 - {
383 - if (isset($_ENV['RACKSPACE_SERVICENET']))
384 - $servicenet=True;
385 - $this->cfs_http = new CF_Http(DEFAULT_CF_API_VERSION);
386 - $this->cfs_auth = $cfs_auth;
387 - if (!$this->cfs_auth->authenticated()) {
388 - $e = "Need to pass in a previously authenticated ";
389 - $e .= "CF_Authentication instance.";
390 - throw new AuthenticationException($e);
391 - }
392 - $this->cfs_http->setCFAuth($this->cfs_auth, $servicenet=$servicenet);
393 - $this->dbug = False;
394 - }
395 -
396 - /**
397 - * Toggle debugging of instance and back-end HTTP module
398 - *
399 - * @param boolean $bool enable/disable cURL debugging
400 - */
401 - function setDebug($bool)
402 - {
403 - $this->dbug = (boolean) $bool;
404 - $this->cfs_http->setDebug($this->dbug);
405 - }
406 -
407 - /**
408 - * Close a connection
409 - *
410 - * Example:
411 - * <code>
412 - *
413 - * $conn->close();
414 - *
415 - * </code>
416 - *
417 - * Will close all current cUrl active connections.
418 - *
419 - */
420 - public function close()
421 - {
422 - $this->cfs_http->close();
423 - }
424 -
425 - /**
426 - * Cloud Files account information
427 - *
428 - * Return an array of two floats (since PHP only supports 32-bit integers);
429 - * number of containers on the account and total bytes used for the account.
430 - *
431 - * Example:
432 - * <code>
433 - * # ... authentication code excluded (see previous examples) ...
434 - * #
435 - * $conn = new CF_Authentication($auth);
436 - *
437 - * list($quantity, $bytes) = $conn->get_info();
438 - * print "Number of containers: " . $quantity . "\n";
439 - * print "Bytes stored in container: " . $bytes . "\n";
440 - * </code>
441 - *
442 - * @return array (number of containers, total bytes stored)
443 - * @throws InvalidResponseException unexpected response
444 - */
445 - function get_info()
446 - {
447 - list($status, $reason, $container_count, $total_bytes) =
448 - $this->cfs_http->head_account();
449 - #if ($status == 401 && $this->_re_auth()) {
450 - # return $this->get_info();
451 - #}
452 - if ($status < 200 || $status > 299) {
453 - throw new InvalidResponseException(
454 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
455 - }
456 - return array($container_count, $total_bytes);
457 - }
458 -
459 - /**
460 - * Create a Container
461 - *
462 - * Given a Container name, return a Container instance, creating a new
463 - * remote Container if it does not exit.
464 - *
465 - * Example:
466 - * <code>
467 - * # ... authentication code excluded (see previous examples) ...
468 - * #
469 - * $conn = new CF_Authentication($auth);
470 - *
471 - * $images = $conn->create_container("my photos");
472 - * </code>
473 - *
474 - * @param string $container_name container name
475 - * @return CF_Container
476 - * @throws SyntaxException invalid name
477 - * @throws InvalidResponseException unexpected response
478 - */
479 - function create_container($container_name=NULL)
480 - {
481 - if ($container_name != "0" and !isset($container_name))
482 - throw new SyntaxException("Container name not set.");
483 -
484 - if (!isset($container_name) or $container_name == "")
485 - throw new SyntaxException("Container name not set.");
486 -
487 - if (strpos($container_name, "/") !== False) {
488 - $r = "Container name '".$container_name;
489 - $r .= "' cannot contain a '/' character.";
490 - throw new SyntaxException($r);
491 - }
492 - if (strlen($container_name) > MAX_CONTAINER_NAME_LEN) {
493 - throw new SyntaxException(sprintf(
494 - "Container name exeeds %d bytes.",
495 - MAX_CONTAINER_NAME_LEN));
496 - }
497 -
498 - $return_code = $this->cfs_http->create_container($container_name);
499 - if (!$return_code) {
500 - throw new InvalidResponseException("Invalid response ("
501 - . $return_code. "): " . $this->cfs_http->get_error());
502 - }
503 - #if ($status == 401 && $this->_re_auth()) {
504 - # return $this->create_container($container_name);
505 - #}
506 - if ($return_code != 201 && $return_code != 202) {
507 - throw new InvalidResponseException(
508 - "Invalid response (".$return_code."): "
509 - . $this->cfs_http->get_error());
510 - }
511 - return new CF_Container($this->cfs_auth, $this->cfs_http, $container_name);
512 - }
513 -
514 - /**
515 - * Delete a Container
516 - *
517 - * Given either a Container instance or name, remove the remote Container.
518 - * The Container must be empty prior to removing it.
519 - *
520 - * Example:
521 - * <code>
522 - * # ... authentication code excluded (see previous examples) ...
523 - * #
524 - * $conn = new CF_Authentication($auth);
525 - *
526 - * $conn->delete_container("my photos");
527 - * </code>
528 - *
529 - * @param string|obj $container container name or instance
530 - * @return boolean <kbd>True</kbd> if successfully deleted
531 - * @throws SyntaxException missing proper argument
532 - * @throws InvalidResponseException invalid response
533 - * @throws NonEmptyContainerException container not empty
534 - * @throws NoSuchContainerException remote container does not exist
535 - */
536 - function delete_container($container=NULL)
537 - {
538 - $container_name = NULL;
539 -
540 - if (is_object($container)) {
541 - if (get_class($container) == "CF_Container") {
542 - $container_name = $container->name;
543 - }
544 - }
545 - if (is_string($container)) {
546 - $container_name = $container;
547 - }
548 -
549 - if ($container_name != "0" and !isset($container_name))
550 - throw new SyntaxException("Must specify container object or name.");
551 -
552 - $return_code = $this->cfs_http->delete_container($container_name);
553 -
554 - if (!$return_code) {
555 - throw new InvalidResponseException("Failed to obtain http response");
556 - }
557 - #if ($status == 401 && $this->_re_auth()) {
558 - # return $this->delete_container($container);
559 - #}
560 - if ($return_code == 409) {
561 - throw new NonEmptyContainerException(
562 - "Container must be empty prior to removing it.");
563 - }
564 - if ($return_code == 404) {
565 - throw new NoSuchContainerException(
566 - "Specified container did not exist to delete.");
567 - }
568 - if ($return_code != 204) {
569 - throw new InvalidResponseException(
570 - "Invalid response (".$return_code."): "
571 - . $this->cfs_http->get_error());
572 - }
573 - return True;
574 - }
575 -
576 - /**
577 - * Return a Container instance
578 - *
579 - * For the given name, return a Container instance if the remote Container
580 - * exists, otherwise throw a Not Found exception.
581 - *
582 - * Example:
583 - * <code>
584 - * # ... authentication code excluded (see previous examples) ...
585 - * #
586 - * $conn = new CF_Authentication($auth);
587 - *
588 - * $images = $conn->get_container("my photos");
589 - * print "Number of Objects: " . $images->count . "\n";
590 - * print "Bytes stored in container: " . $images->bytes . "\n";
591 - * </code>
592 - *
593 - * @param string $container_name name of the remote Container
594 - * @return container CF_Container instance
595 - * @throws NoSuchContainerException thrown if no remote Container
596 - * @throws InvalidResponseException unexpected response
597 - */
598 - function get_container($container_name=NULL)
599 - {
600 - list($status, $reason, $count, $bytes) =
601 - $this->cfs_http->head_container($container_name);
602 - #if ($status == 401 && $this->_re_auth()) {
603 - # return $this->get_container($container_name);
604 - #}
605 - if ($status == 404) {
606 - throw new NoSuchContainerException("Container not found.");
607 - }
608 - if ($status < 200 || $status > 299) {
609 - throw new InvalidResponseException(
610 - "Invalid response: ".$this->cfs_http->get_error());
611 - }
612 - return new CF_Container($this->cfs_auth, $this->cfs_http,
613 - $container_name, $count, $bytes);
614 - }
615 -
616 - /**
617 - * Return array of Container instances
618 - *
619 - * Return an array of CF_Container instances on the account. The instances
620 - * will be fully populated with Container attributes (bytes stored and
621 - * Object count)
622 - *
623 - * Example:
624 - * <code>
625 - * # ... authentication code excluded (see previous examples) ...
626 - * #
627 - * $conn = new CF_Authentication($auth);
628 - *
629 - * $clist = $conn->get_containers();
630 - * foreach ($clist as $cont) {
631 - * print "Container name: " . $cont->name . "\n";
632 - * print "Number of Objects: " . $cont->count . "\n";
633 - * print "Bytes stored in container: " . $cont->bytes . "\n";
634 - * }
635 - * </code>
636 - *
637 - * @return array An array of CF_Container instances
638 - * @throws InvalidResponseException unexpected response
639 - */
640 - function get_containers($limit=0, $marker=NULL)
641 - {
642 - list($status, $reason, $container_info) =
643 - $this->cfs_http->list_containers_info($limit, $marker);
644 - #if ($status == 401 && $this->_re_auth()) {
645 - # return $this->get_containers();
646 - #}
647 - if ($status < 200 || $status > 299) {
648 - throw new InvalidResponseException(
649 - "Invalid response: ".$this->cfs_http->get_error());
650 - }
651 - $containers = array();
652 - foreach ($container_info as $name => $info) {
653 - $containers[] = new CF_Container($this->cfs_auth, $this->cfs_http,
654 - $info['name'], $info["count"], $info["bytes"], False);
655 - }
656 - return $containers;
657 - }
658 -
659 - /**
660 - * Return list of remote Containers
661 - *
662 - * Return an array of strings containing the names of all remote Containers.
663 - *
664 - * Example:
665 - * <code>
666 - * # ... authentication code excluded (see previous examples) ...
667 - * #
668 - * $conn = new CF_Authentication($auth);
669 - *
670 - * $container_list = $conn->list_containers();
671 - * print_r($container_list);
672 - * Array
673 - * (
674 - * [0] => "my photos",
675 - * [1] => "my docs"
676 - * )
677 - * </code>
678 - *
679 - * @param integer $limit restrict results to $limit Containers
680 - * @param string $marker return results greater than $marker
681 - * @return array list of remote Containers
682 - * @throws InvalidResponseException unexpected response
683 - */
684 - function list_containers($limit=0, $marker=NULL)
685 - {
686 - list($status, $reason, $containers) =
687 - $this->cfs_http->list_containers($limit, $marker);
688 - #if ($status == 401 && $this->_re_auth()) {
689 - # return $this->list_containers($limit, $marker);
690 - #}
691 - if ($status < 200 || $status > 299) {
692 - throw new InvalidResponseException(
693 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
694 - }
695 - return $containers;
696 - }
697 -
698 - /**
699 - * Return array of information about remote Containers
700 - *
701 - * Return a nested array structure of Container info.
702 - *
703 - * Example:
704 - * <code>
705 - * # ... authentication code excluded (see previous examples) ...
706 - * #
707 - *
708 - * $container_info = $conn->list_containers_info();
709 - * print_r($container_info);
710 - * Array
711 - * (
712 - * ["my photos"] =>
713 - * Array
714 - * (
715 - * ["bytes"] => 78,
716 - * ["count"] => 2
717 - * )
718 - * ["docs"] =>
719 - * Array
720 - * (
721 - * ["bytes"] => 37323,
722 - * ["count"] => 12
723 - * )
724 - * )
725 - * </code>
726 - *
727 - * @param integer $limit restrict results to $limit Containers
728 - * @param string $marker return results greater than $marker
729 - * @return array nested array structure of Container info
730 - * @throws InvalidResponseException unexpected response
731 - */
732 - function list_containers_info($limit=0, $marker=NULL)
733 - {
734 - list($status, $reason, $container_info) =
735 - $this->cfs_http->list_containers_info($limit, $marker);
736 - #if ($status == 401 && $this->_re_auth()) {
737 - # return $this->list_containers_info($limit, $marker);
738 - #}
739 - if ($status < 200 || $status > 299) {
740 - throw new InvalidResponseException(
741 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
742 - }
743 - return $container_info;
744 - }
745 -
746 - /**
747 - * Return list of Containers that have been published to the CDN.
748 - *
749 - * Return an array of strings containing the names of published Containers.
750 - * Note that this function returns the list of any Container that has
751 - * ever been CDN-enabled regardless of it's existence in the storage
752 - * system.
753 - *
754 - * Example:
755 - * <code>
756 - * # ... authentication code excluded (see previous examples) ...
757 - * #
758 - * $conn = new CF_Authentication($auth);
759 - *
760 - * $public_containers = $conn->list_public_containers();
761 - * print_r($public_containers);
762 - * Array
763 - * (
764 - * [0] => "images",
765 - * [1] => "css",
766 - * [2] => "javascript"
767 - * )
768 - * </code>
769 - *
770 - * @param bool $enabled_only Will list all containers ever CDN enabled if * set to false or only currently enabled CDN containers if set to true. * Defaults to false.
771 - * @return array list of published Container names
772 - * @throws InvalidResponseException unexpected response
773 - */
774 - function list_public_containers($enabled_only=False)
775 - {
776 - list($status, $reason, $containers) =
777 - $this->cfs_http->list_cdn_containers($enabled_only);
778 - #if ($status == 401 && $this->_re_auth()) {
779 - # return $this->list_public_containers();
780 - #}
781 - if ($status < 200 || $status > 299) {
782 - throw new InvalidResponseException(
783 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
784 - }
785 - return $containers;
786 - }
787 -
788 - /**
789 - * Set a user-supplied callback function to report download progress
790 - *
791 - * The callback function is used to report incremental progress of a data
792 - * download functions (e.g. $container->list_objects(), $obj->read(), etc).
793 - * The specified function will be periodically called with the number of
794 - * bytes transferred until the entire download is complete. This callback
795 - * function can be useful for implementing "progress bars" for large
796 - * downloads.
797 - *
798 - * The specified callback function should take a single integer parameter.
799 - *
800 - * <code>
801 - * function read_callback($bytes_transferred) {
802 - * print ">> downloaded " . $bytes_transferred . " bytes.\n";
803 - * # ... do other things ...
804 - * return;
805 - * }
806 - *
807 - * $conn = new CF_Connection($auth_obj);
808 - * $conn->set_read_progress_function("read_callback");
809 - * print_r($conn->list_containers());
810 - *
811 - * # output would look like this:
812 - * #
813 - * >> downloaded 10 bytes.
814 - * >> downloaded 11 bytes.
815 - * Array
816 - * (
817 - * [0] => fuzzy.txt
818 - * [1] => space name
819 - * )
820 - * </code>
821 - *
822 - * @param string $func_name the name of the user callback function
823 - */
824 - function set_read_progress_function($func_name)
825 - {
826 - $this->cfs_http->setReadProgressFunc($func_name);
827 - }
828 -
829 - /**
830 - * Set a user-supplied callback function to report upload progress
831 - *
832 - * The callback function is used to report incremental progress of a data
833 - * upload functions (e.g. $obj->write() call). The specified function will
834 - * be periodically called with the number of bytes transferred until the
835 - * entire upload is complete. This callback function can be useful
836 - * for implementing "progress bars" for large uploads/downloads.
837 - *
838 - * The specified callback function should take a single integer parameter.
839 - *
840 - * <code>
841 - * function write_callback($bytes_transferred) {
842 - * print ">> uploaded " . $bytes_transferred . " bytes.\n";
843 - * # ... do other things ...
844 - * return;
845 - * }
846 - *
847 - * $conn = new CF_Connection($auth_obj);
848 - * $conn->set_write_progress_function("write_callback");
849 - * $container = $conn->create_container("stuff");
850 - * $obj = $container->create_object("foo");
851 - * $obj->write("The callback function will be called during upload.");
852 - *
853 - * # output would look like this:
854 - * # >> uploaded 51 bytes.
855 - * #
856 - * </code>
857 - *
858 - * @param string $func_name the name of the user callback function
859 - */
860 - function set_write_progress_function($func_name)
861 - {
862 - $this->cfs_http->setWriteProgressFunc($func_name);
863 - }
864 -
865 - /**
866 - * Use the Certificate Authority bundle included with this API
867 - *
868 - * Most versions of PHP with cURL support include an outdated Certificate
869 - * Authority (CA) bundle (the file that lists all valid certificate
870 - * signing authorities). The SSL certificates used by the Cloud Files
871 - * storage system are perfectly valid but have been created/signed by
872 - * a CA not listed in these outdated cURL distributions.
873 - *
874 - * As a work-around, we've included an updated CA bundle obtained
875 - * directly from cURL's web site (http://curl.haxx.se). You can direct
876 - * the API to use this CA bundle by calling this method prior to making
877 - * any remote calls. The best place to use this method is right after
878 - * the CF_Authentication instance has been instantiated.
879 - *
880 - * You can specify your own CA bundle by passing in the full pathname
881 - * to the bundle. You can use the included CA bundle by leaving the
882 - * argument blank.
883 - *
884 - * @param string $path Specify path to CA bundle (default to included)
885 - */
886 - function ssl_use_cabundle($path=NULL)
887 - {
888 - $this->cfs_http->ssl_use_cabundle($path);
889 - }
890 -
891 - #private function _re_auth()
892 - #{
893 - # $new_auth = new CF_Authentication(
894 - # $this->cfs_auth->username,
895 - # $this->cfs_auth->api_key,
896 - # $this->cfs_auth->auth_host,
897 - # $this->cfs_auth->account);
898 - # $new_auth->authenticate();
899 - # $this->cfs_auth = $new_auth;
900 - # $this->cfs_http->setCFAuth($this->cfs_auth);
901 - # return True;
902 - #}
903 -}
904 -
905 -/**
906 - * Container operations
907 - *
908 - * Containers are storage compartments where you put your data (objects).
909 - * A container is similar to a directory or folder on a conventional filesystem
910 - * with the exception that they exist in a flat namespace, you can not create
911 - * containers inside of containers.
912 - *
913 - * You also have the option of marking a Container as "public" so that the
914 - * Objects stored in the Container are publicly available via the CDN.
915 - *
916 - * @package php-cloudfiles
917 - */
918 -class CF_Container
919 -{
920 - public $cfs_auth;
921 - public $cfs_http;
922 - public $name;
923 - public $object_count;
924 - public $bytes_used;
925 -
926 - public $cdn_enabled;
927 - public $cdn_ssl_uri;
928 - public $cdn_uri;
929 - public $cdn_ttl;
930 - public $cdn_log_retention;
931 - public $cdn_acl_user_agent;
932 - public $cdn_acl_referrer;
933 -
934 - /**
935 - * Class constructor
936 - *
937 - * Constructor for Container
938 - *
939 - * @param obj $cfs_auth CF_Authentication instance
940 - * @param obj $cfs_http HTTP connection manager
941 - * @param string $name name of Container
942 - * @param int $count number of Objects stored in this Container
943 - * @param int $bytes number of bytes stored in this Container
944 - * @throws SyntaxException invalid Container name
945 - */
946 - function __construct(&$cfs_auth, &$cfs_http, $name, $count=0,
947 - $bytes=0, $docdn=True)
948 - {
949 - if (strlen($name) > MAX_CONTAINER_NAME_LEN) {
950 - throw new SyntaxException("Container name exceeds "
951 - . "maximum allowed length.");
952 - }
953 - if (strpos($name, "/") !== False) {
954 - throw new SyntaxException(
955 - "Container names cannot contain a '/' character.");
956 - }
957 - $this->cfs_auth = $cfs_auth;
958 - $this->cfs_http = $cfs_http;
959 - $this->name = $name;
960 - $this->object_count = $count;
961 - $this->bytes_used = $bytes;
962 - $this->cdn_enabled = NULL;
963 - $this->cdn_uri = NULL;
964 - $this->cdn_ssl_uri = NULL;
965 - $this->cdn_ttl = NULL;
966 - $this->cdn_log_retention = NULL;
967 - $this->cdn_acl_user_agent = NULL;
968 - $this->cdn_acl_referrer = NULL;
969 - if ($this->cfs_http->getCDNMUrl() != NULL && $docdn) {
970 - $this->_cdn_initialize();
971 - }
972 - }
973 -
974 - /**
975 - * String representation of Container
976 - *
977 - * Pretty print the Container instance.
978 - *
979 - * @return string Container details
980 - */
981 - function __toString()
982 - {
983 - $me = sprintf("name: %s, count: %.0f, bytes: %.0f",
984 - $this->name, $this->object_count, $this->bytes_used);
985 - if ($this->cfs_http->getCDNMUrl() != NULL) {
986 - $me .= sprintf(", cdn: %s, cdn uri: %s, cdn ttl: %.0f, logs retention: %s",
987 - $this->is_public() ? "Yes" : "No",
988 - $this->cdn_uri, $this->cdn_ttl,
989 - $this->cdn_log_retention ? "Yes" : "No"
990 - );
991 -
992 - if ($this->cdn_acl_user_agent != NULL) {
993 - $me .= ", cdn acl user agent: " . $this->cdn_acl_user_agent;
994 - }
995 -
996 - if ($this->cdn_acl_referrer != NULL) {
997 - $me .= ", cdn acl referrer: " . $this->cdn_acl_referrer;
998 - }
999 -
1000 -
1001 - }
1002 - return $me;
1003 - }
1004 -
1005 - /**
1006 - * Enable Container content to be served via CDN or modify CDN attributes
1007 - *
1008 - * Either enable this Container's content to be served via CDN or
1009 - * adjust its CDN attributes. This Container will always return the
1010 - * same CDN-enabled URI each time it is toggled public/private/public.
1011 - *
1012 - * Example:
1013 - * <code>
1014 - * # ... authentication code excluded (see previous examples) ...
1015 - * #
1016 - * $conn = new CF_Authentication($auth);
1017 - *
1018 - * $public_container = $conn->create_container("public");
1019 - *
1020 - * # CDN-enable the container and set it's TTL for a month
1021 - * #
1022 - * $public_container->make_public(86400/2); # 12 hours (86400 seconds/day)
1023 - * </code>
1024 - *
1025 - * @param int $ttl the time in seconds content will be cached in the CDN
1026 - * @returns string the CDN enabled Container's URI
1027 - * @throws CDNNotEnabledException CDN functionality not returned during auth
1028 - * @throws AuthenticationException if auth token is not valid/expired
1029 - * @throws InvalidResponseException unexpected response
1030 - */
1031 - function make_public($ttl=86400)
1032 - {
1033 - if ($this->cfs_http->getCDNMUrl() == NULL) {
1034 - throw new CDNNotEnabledException(
1035 - "Authentication response did not indicate CDN availability");
1036 - }
1037 - if ($this->cdn_uri != NULL) {
1038 - # previously published, assume we're setting new attributes
1039 - list($status, $reason, $cdn_uri, $cdn_ssl_uri) =
1040 - $this->cfs_http->update_cdn_container($this->name,$ttl,
1041 - $this->cdn_log_retention,
1042 - $this->cdn_acl_user_agent,
1043 - $this->cdn_acl_referrer);
1044 - #if ($status == 401 && $this->_re_auth()) {
1045 - # return $this->make_public($ttl);
1046 - #}
1047 - if ($status == 404) {
1048 - # this instance _thinks_ the container was published, but the
1049 - # cdn management system thinks otherwise - try again with a PUT
1050 - list($status, $reason, $cdn_uri, $cdn_ssl_uri) =
1051 - $this->cfs_http->add_cdn_container($this->name,$ttl);
1052 -
1053 - }
1054 - } else {
1055 - # publish it for first time
1056 - list($status, $reason, $cdn_uri, $cdn_ssl_uri) =
1057 - $this->cfs_http->add_cdn_container($this->name,$ttl);
1058 - }
1059 - #if ($status == 401 && $this->_re_auth()) {
1060 - # return $this->make_public($ttl);
1061 - #}
1062 - if (!in_array($status, array(201,202))) {
1063 - throw new InvalidResponseException(
1064 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1065 - }
1066 - $this->cdn_enabled = True;
1067 - $this->cdn_ttl = $ttl;
1068 - $this->cdn_ssl_uri = $cdn_ssl_uri;
1069 - $this->cdn_uri = $cdn_uri;
1070 - $this->cdn_log_retention = False;
1071 - $this->cdn_acl_user_agent = "";
1072 - $this->cdn_acl_referrer = "";
1073 - return $this->cdn_uri;
1074 - }
1075 - /**
1076 - * Purge Containers objects from CDN Cache.
1077 - * Example:
1078 - * <code>
1079 - * # ... authentication code excluded (see previous examples) ...
1080 - * #
1081 - * $conn = new CF_Authentication($auth);
1082 - * $container = $conn->get_container("cdn_enabled");
1083 - * $container->purge_from_cdn("user@domain.com");
1084 - * # or
1085 - * $container->purge_from_cdn();
1086 - * # or
1087 - * $container->purge_from_cdn("user1@domain.com,user2@domain.com");
1088 - * @returns boolean True if successful
1089 - * @throws CDNNotEnabledException if CDN Is not enabled on this connection
1090 - * @throws InvalidResponseException if the response expected is not returned
1091 - */
1092 - function purge_from_cdn($email=null)
1093 - {
1094 - if (!$this->cfs_http->getCDNMUrl())
1095 - {
1096 - throw new CDNNotEnabledException(
1097 - "Authentication response did not indicate CDN availability");
1098 - }
1099 - $status = $this->cfs_http->purge_from_cdn($this->name, $email);
1100 - if ($status < 199 or $status > 299) {
1101 - throw new InvalidResponseException(
1102 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1103 - }
1104 - return True;
1105 - }
1106 - /**
1107 - * Enable ACL restriction by User Agent for this container.
1108 - *
1109 - * Example:
1110 - * <code>
1111 - * # ... authentication code excluded (see previous examples) ...
1112 - * #
1113 - * $conn = new CF_Authentication($auth);
1114 - *
1115 - * $public_container = $conn->get_container("public");
1116 - *
1117 - * # Enable ACL by Referrer
1118 - * $public_container->acl_referrer("Mozilla");
1119 - * </code>
1120 - *
1121 - * @returns boolean True if successful
1122 - * @throws CDNNotEnabledException CDN functionality not returned during auth
1123 - * @throws AuthenticationException if auth token is not valid/expired
1124 - * @throws InvalidResponseException unexpected response
1125 - */
1126 - function acl_user_agent($cdn_acl_user_agent="") {
1127 - if ($this->cfs_http->getCDNMUrl() == NULL) {
1128 - throw new CDNNotEnabledException(
1129 - "Authentication response did not indicate CDN availability");
1130 - }
1131 - list($status,$reason) =
1132 - $this->cfs_http->update_cdn_container($this->name,
1133 - $this->cdn_ttl,
1134 - $this->cdn_log_retention,
1135 - $cdn_acl_user_agent,
1136 - $this->cdn_acl_referrer
1137 - );
1138 - if (!in_array($status, array(202,404))) {
1139 - throw new InvalidResponseException(
1140 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1141 - }
1142 - $this->cdn_acl_user_agent = $cdn_acl_user_agent;
1143 - return True;
1144 - }
1145 -
1146 - /**
1147 - * Enable ACL restriction by referer for this container.
1148 - *
1149 - * Example:
1150 - * <code>
1151 - * # ... authentication code excluded (see previous examples) ...
1152 - * #
1153 - * $conn = new CF_Authentication($auth);
1154 - *
1155 - * $public_container = $conn->get_container("public");
1156 - *
1157 - * # Enable Referrer
1158 - * $public_container->acl_referrer("http://www.example.com/gallery.php");
1159 - * </code>
1160 - *
1161 - * @returns boolean True if successful
1162 - * @throws CDNNotEnabledException CDN functionality not returned during auth
1163 - * @throws AuthenticationException if auth token is not valid/expired
1164 - * @throws InvalidResponseException unexpected response
1165 - */
1166 - function acl_referrer($cdn_acl_referrer="") {
1167 - if ($this->cfs_http->getCDNMUrl() == NULL) {
1168 - throw new CDNNotEnabledException(
1169 - "Authentication response did not indicate CDN availability");
1170 - }
1171 - list($status,$reason) =
1172 - $this->cfs_http->update_cdn_container($this->name,
1173 - $this->cdn_ttl,
1174 - $this->cdn_log_retention,
1175 - $this->cdn_acl_user_agent,
1176 - $cdn_acl_referrer
1177 - );
1178 - if (!in_array($status, array(202,404))) {
1179 - throw new InvalidResponseException(
1180 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1181 - }
1182 - $this->cdn_acl_referrer = $cdn_acl_referrer;
1183 - return True;
1184 - }
1185 -
1186 - /**
1187 - * Enable log retention for this CDN container.
1188 - *
1189 - * Enable CDN log retention on the container. If enabled logs will
1190 - * be periodically (at unpredictable intervals) compressed and
1191 - * uploaded to a ".CDN_ACCESS_LOGS" container in the form of
1192 - * "container_name.YYYYMMDDHH-XXXX.gz". Requires CDN be enabled on
1193 - * the account.
1194 - *
1195 - * Example:
1196 - * <code>
1197 - * # ... authentication code excluded (see previous examples) ...
1198 - * #
1199 - * $conn = new CF_Authentication($auth);
1200 - *
1201 - * $public_container = $conn->get_container("public");
1202 - *
1203 - * # Enable logs retention.
1204 - * $public_container->log_retention(True);
1205 - * </code>
1206 - *
1207 - * @returns boolean True if successful
1208 - * @throws CDNNotEnabledException CDN functionality not returned during auth
1209 - * @throws AuthenticationException if auth token is not valid/expired
1210 - * @throws InvalidResponseException unexpected response
1211 - */
1212 - function log_retention($cdn_log_retention=False) {
1213 - if ($this->cfs_http->getCDNMUrl() == NULL) {
1214 - throw new CDNNotEnabledException(
1215 - "Authentication response did not indicate CDN availability");
1216 - }
1217 - list($status,$reason) =
1218 - $this->cfs_http->update_cdn_container($this->name,
1219 - $this->cdn_ttl,
1220 - $cdn_log_retention,
1221 - $this->cdn_acl_user_agent,
1222 - $this->cdn_acl_referrer
1223 - );
1224 - if (!in_array($status, array(202,404))) {
1225 - throw new InvalidResponseException(
1226 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1227 - }
1228 - $this->cdn_log_retention = $cdn_log_retention;
1229 - return True;
1230 - }
1231 -
1232 - /**
1233 - * Disable the CDN sharing for this container
1234 - *
1235 - * Use this method to disallow distribution into the CDN of this Container's
1236 - * content.
1237 - *
1238 - * NOTE: Any content already cached in the CDN will continue to be served
1239 - * from its cache until the TTL expiration transpires. The default
1240 - * TTL is typically one day, so "privatizing" the Container will take
1241 - * up to 24 hours before the content is purged from the CDN cache.
1242 - *
1243 - * Example:
1244 - * <code>
1245 - * # ... authentication code excluded (see previous examples) ...
1246 - * #
1247 - * $conn = new CF_Authentication($auth);
1248 - *
1249 - * $public_container = $conn->get_container("public");
1250 - *
1251 - * # Disable CDN accessability
1252 - * # ... still cached up to a month based on previous example
1253 - * #
1254 - * $public_container->make_private();
1255 - * </code>
1256 - *
1257 - * @returns boolean True if successful
1258 - * @throws CDNNotEnabledException CDN functionality not returned during auth
1259 - * @throws AuthenticationException if auth token is not valid/expired
1260 - * @throws InvalidResponseException unexpected response
1261 - */
1262 - function make_private()
1263 - {
1264 - if ($this->cfs_http->getCDNMUrl() == NULL) {
1265 - throw new CDNNotEnabledException(
1266 - "Authentication response did not indicate CDN availability");
1267 - }
1268 - list($status,$reason) = $this->cfs_http->remove_cdn_container($this->name);
1269 - #if ($status == 401 && $this->_re_auth()) {
1270 - # return $this->make_private();
1271 - #}
1272 - if (!in_array($status, array(202,404))) {
1273 - throw new InvalidResponseException(
1274 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1275 - }
1276 - $this->cdn_enabled = False;
1277 - $this->cdn_ttl = NULL;
1278 - $this->cdn_uri = NULL;
1279 - $this->cdn_ssl_uri = NULL;
1280 - $this->cdn_log_retention = NULL;
1281 - $this->cdn_acl_user_agent = NULL;
1282 - $this->cdn_acl_referrer = NULL;
1283 - return True;
1284 - }
1285 -
1286 - /**
1287 - * Check if this Container is being publicly served via CDN
1288 - *
1289 - * Use this method to determine if the Container's content is currently
1290 - * available through the CDN.
1291 - *
1292 - * Example:
1293 - * <code>
1294 - * # ... authentication code excluded (see previous examples) ...
1295 - * #
1296 - * $conn = new CF_Authentication($auth);
1297 - *
1298 - * $public_container = $conn->get_container("public");
1299 - *
1300 - * # Display CDN accessability
1301 - * #
1302 - * $public_container->is_public() ? print "Yes" : print "No";
1303 - * </code>
1304 - *
1305 - * @returns boolean True if enabled, False otherwise
1306 - */
1307 - function is_public()
1308 - {
1309 - return $this->cdn_enabled == True ? True : False;
1310 - }
1311 -
1312 - /**
1313 - * Create a new remote storage Object
1314 - *
1315 - * Return a new Object instance. If the remote storage Object exists,
1316 - * the instance's attributes are populated.
1317 - *
1318 - * Example:
1319 - * <code>
1320 - * # ... authentication code excluded (see previous examples) ...
1321 - * #
1322 - * $conn = new CF_Authentication($auth);
1323 - *
1324 - * $public_container = $conn->get_container("public");
1325 - *
1326 - * # This creates a local instance of a storage object but only creates
1327 - * # it in the storage system when the object's write() method is called.
1328 - * #
1329 - * $pic = $public_container->create_object("baby.jpg");
1330 - * </code>
1331 - *
1332 - * @param string $obj_name name of storage Object
1333 - * @return obj CF_Object instance
1334 - */
1335 - function create_object($obj_name=NULL)
1336 - {
1337 - return new CF_Object($this, $obj_name);
1338 - }
1339 -
1340 - /**
1341 - * Return an Object instance for the remote storage Object
1342 - *
1343 - * Given a name, return a Object instance representing the
1344 - * remote storage object.
1345 - *
1346 - * Example:
1347 - * <code>
1348 - * # ... authentication code excluded (see previous examples) ...
1349 - * #
1350 - * $conn = new CF_Authentication($auth);
1351 - *
1352 - * $public_container = $conn->get_container("public");
1353 - *
1354 - * # This call only fetches header information and not the content of
1355 - * # the storage object. Use the Object's read() or stream() methods
1356 - * # to obtain the object's data.
1357 - * #
1358 - * $pic = $public_container->get_object("baby.jpg");
1359 - * </code>
1360 - *
1361 - * @param string $obj_name name of storage Object
1362 - * @return obj CF_Object instance
1363 - */
1364 - function get_object($obj_name=NULL)
1365 - {
1366 - return new CF_Object($this, $obj_name, True);
1367 - }
1368 -
1369 - /**
1370 - * Return a list of Objects
1371 - *
1372 - * Return an array of strings listing the Object names in this Container.
1373 - *
1374 - * Example:
1375 - * <code>
1376 - * # ... authentication code excluded (see previous examples) ...
1377 - * #
1378 - * $images = $conn->get_container("my photos");
1379 - *
1380 - * # Grab the list of all storage objects
1381 - * #
1382 - * $all_objects = $images->list_objects();
1383 - *
1384 - * # Grab subsets of all storage objects
1385 - * #
1386 - * $first_ten = $images->list_objects(10);
1387 - *
1388 - * # Note the use of the previous result's last object name being
1389 - * # used as the 'marker' parameter to fetch the next 10 objects
1390 - * #
1391 - * $next_ten = $images->list_objects(10, $first_ten[count($first_ten)-1]);
1392 - *
1393 - * # Grab images starting with "birthday_party" and default limit/marker
1394 - * # to match all photos with that prefix
1395 - * #
1396 - * $prefixed = $images->list_objects(0, NULL, "birthday");
1397 - *
1398 - * # Assuming you have created the appropriate directory marker Objects,
1399 - * # you can traverse your pseudo-hierarchical containers
1400 - * # with the "path" argument.
1401 - * #
1402 - * $animals = $images->list_objects(0,NULL,NULL,"pictures/animals");
1403 - * $dogs = $images->list_objects(0,NULL,NULL,"pictures/animals/dogs");
1404 - * </code>
1405 - *
1406 - * @param int $limit <i>optional</i> only return $limit names
1407 - * @param int $marker <i>optional</i> subset of names starting at $marker
1408 - * @param string $prefix <i>optional</i> Objects whose names begin with $prefix
1409 - * @param string $path <i>optional</i> only return results under "pathname"
1410 - * @return array array of strings
1411 - * @throws InvalidResponseException unexpected response
1412 - */
1413 - function list_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL)
1414 - {
1415 - list($status, $reason, $obj_list) =
1416 - $this->cfs_http->list_objects($this->name, $limit,
1417 - $marker, $prefix, $path);
1418 - #if ($status == 401 && $this->_re_auth()) {
1419 - # return $this->list_objects($limit, $marker, $prefix, $path);
1420 - #}
1421 - if ($status < 200 || $status > 299) {
1422 - throw new InvalidResponseException(
1423 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1424 - }
1425 - return $obj_list;
1426 - }
1427 -
1428 - /**
1429 - * Return an array of Objects
1430 - *
1431 - * Return an array of Object instances in this Container.
1432 - *
1433 - * Example:
1434 - * <code>
1435 - * # ... authentication code excluded (see previous examples) ...
1436 - * #
1437 - * $images = $conn->get_container("my photos");
1438 - *
1439 - * # Grab the list of all storage objects
1440 - * #
1441 - * $all_objects = $images->get_objects();
1442 - *
1443 - * # Grab subsets of all storage objects
1444 - * #
1445 - * $first_ten = $images->get_objects(10);
1446 - *
1447 - * # Note the use of the previous result's last object name being
1448 - * # used as the 'marker' parameter to fetch the next 10 objects
1449 - * #
1450 - * $next_ten = $images->list_objects(10, $first_ten[count($first_ten)-1]);
1451 - *
1452 - * # Grab images starting with "birthday_party" and default limit/marker
1453 - * # to match all photos with that prefix
1454 - * #
1455 - * $prefixed = $images->get_objects(0, NULL, "birthday");
1456 - *
1457 - * # Assuming you have created the appropriate directory marker Objects,
1458 - * # you can traverse your pseudo-hierarchical containers
1459 - * # with the "path" argument.
1460 - * #
1461 - * $animals = $images->get_objects(0,NULL,NULL,"pictures/animals");
1462 - * $dogs = $images->get_objects(0,NULL,NULL,"pictures/animals/dogs");
1463 - * </code>
1464 - *
1465 - * @param int $limit <i>optional</i> only return $limit names
1466 - * @param int $marker <i>optional</i> subset of names starting at $marker
1467 - * @param string $prefix <i>optional</i> Objects whose names begin with $prefix
1468 - * @param string $path <i>optional</i> only return results under "pathname"
1469 - * @return array array of strings
1470 - * @throws InvalidResponseException unexpected response
1471 - */
1472 - function get_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL)
1473 - {
1474 - list($status, $reason, $obj_array) =
1475 - $this->cfs_http->get_objects($this->name, $limit,
1476 - $marker, $prefix, $path);
1477 - #if ($status == 401 && $this->_re_auth()) {
1478 - # return $this->get_objects($limit, $marker, $prefix, $path);
1479 - #}
1480 - if ($status < 200 || $status > 299) {
1481 - throw new InvalidResponseException(
1482 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1483 - }
1484 - $objects = array();
1485 - foreach ($obj_array as $obj) {
1486 - $tmp = new CF_Object($this, $obj["name"], False, False);
1487 - $tmp->content_type = $obj["content_type"];
1488 - $tmp->content_length = (float) $obj["bytes"];
1489 - $tmp->set_etag($obj["hash"]);
1490 - $tmp->last_modified = $obj["last_modified"];
1491 - $objects[] = $tmp;
1492 - }
1493 - return $objects;
1494 - }
1495 -
1496 - /**
1497 - * Delete a remote storage Object
1498 - *
1499 - * Given an Object instance or name, permanently remove the remote Object
1500 - * and all associated metadata.
1501 - *
1502 - * Example:
1503 - * <code>
1504 - * # ... authentication code excluded (see previous examples) ...
1505 - * #
1506 - * $conn = new CF_Authentication($auth);
1507 - *
1508 - * $images = $conn->get_container("my photos");
1509 - *
1510 - * # Delete specific object
1511 - * #
1512 - * $images->delete_object("disco_dancing.jpg");
1513 - * </code>
1514 - *
1515 - * @param obj $obj name or instance of Object to delete
1516 - * @return boolean <kbd>True</kbd> if successfully removed
1517 - * @throws SyntaxException invalid Object name
1518 - * @throws NoSuchObjectException remote Object does not exist
1519 - * @throws InvalidResponseException unexpected response
1520 - */
1521 - function delete_object($obj)
1522 - {
1523 - $obj_name = NULL;
1524 - if (is_object($obj)) {
1525 - if (get_class($obj) == "CF_Object") {
1526 - $obj_name = $obj->name;
1527 - }
1528 - }
1529 - if (is_string($obj)) {
1530 - $obj_name = $obj;
1531 - }
1532 - if (!$obj_name) {
1533 - throw new SyntaxException("Object name not set.");
1534 - }
1535 - $status = $this->cfs_http->delete_object($this->name, $obj_name);
1536 - #if ($status == 401 && $this->_re_auth()) {
1537 - # return $this->delete_object($obj);
1538 - #}
1539 - if ($status == 404) {
1540 - $m = "Specified object '".$this->name."/".$obj_name;
1541 - $m.= "' did not exist to delete.";
1542 - throw new NoSuchObjectException($m);
1543 - }
1544 - if ($status != 204) {
1545 - throw new InvalidResponseException(
1546 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1547 - }
1548 - return True;
1549 - }
1550 -
1551 - /**
1552 - * Helper function to create "path" elements for a given Object name
1553 - *
1554 - * Given an Object whos name contains '/' path separators, this function
1555 - * will create the "directory marker" Objects of one byte with the
1556 - * Content-Type of "application/folder".
1557 - *
1558 - * It assumes the last element of the full path is the "real" Object
1559 - * and does NOT create a remote storage Object for that last element.
1560 - */
1561 - function create_paths($path_name)
1562 - {
1563 - if ($path_name[0] == '/') {
1564 - $path_name = mb_substr($path_name, 0, 1);
1565 - }
1566 - $elements = explode('/', $path_name, -1);
1567 - $build_path = "";
1568 - foreach ($elements as $idx => $val) {
1569 - if (!$build_path) {
1570 - $build_path = $val;
1571 - } else {
1572 - $build_path .= "/" . $val;
1573 - }
1574 - $obj = new CF_Object($this, $build_path);
1575 - $obj->content_type = "application/directory";
1576 - $obj->write(".", 1);
1577 - }
1578 - }
1579 -
1580 - /**
1581 - * Internal method to grab CDN/Container info if appropriate to do so
1582 - *
1583 - * @throws InvalidResponseException unexpected response
1584 - */
1585 - private function _cdn_initialize()
1586 - {
1587 - list($status, $reason, $cdn_enabled, $cdn_ssl_uri, $cdn_uri, $cdn_ttl,
1588 - $cdn_log_retention, $cdn_acl_user_agent, $cdn_acl_referrer) =
1589 - $this->cfs_http->head_cdn_container($this->name);
1590 - #if ($status == 401 && $this->_re_auth()) {
1591 - # return $this->_cdn_initialize();
1592 - #}
1593 - if (!in_array($status, array(204,404))) {
1594 - throw new InvalidResponseException(
1595 - "Invalid response (".$status."): ".$this->cfs_http->get_error());
1596 - }
1597 - $this->cdn_enabled = $cdn_enabled;
1598 - $this->cdn_ssl_uri = $cdn_ssl_uri;
1599 - $this->cdn_uri = $cdn_uri;
1600 - $this->cdn_ttl = $cdn_ttl;
1601 - $this->cdn_log_retention = $cdn_log_retention;
1602 - $this->cdn_acl_user_agent = $cdn_acl_user_agent;
1603 - $this->cdn_acl_referrer = $cdn_acl_referrer;
1604 - }
1605 -
1606 - #private function _re_auth()
1607 - #{
1608 - # $new_auth = new CF_Authentication(
1609 - # $this->cfs_auth->username,
1610 - # $this->cfs_auth->api_key,
1611 - # $this->cfs_auth->auth_host,
1612 - # $this->cfs_auth->account);
1613 - # $new_auth->authenticate();
1614 - # $this->cfs_auth = $new_auth;
1615 - # $this->cfs_http->setCFAuth($this->cfs_auth);
1616 - # return True;
1617 - #}
1618 -}
1619 -
1620 -
1621 -/**
1622 - * Object operations
1623 - *
1624 - * An Object is analogous to a file on a conventional filesystem. You can
1625 - * read data from, or write data to your Objects. You can also associate
1626 - * arbitrary metadata with them.
1627 - *
1628 - * @package php-cloudfiles
1629 - */
1630 -class CF_Object
1631 -{
1632 - public $container;
1633 - public $name;
1634 - public $last_modified;
1635 - public $content_type;
1636 - public $content_length;
1637 - public $metadata;
1638 - public $manifest;
1639 - private $etag;
1640 -
1641 - /**
1642 - * Class constructor
1643 - *
1644 - * @param obj $container CF_Container instance
1645 - * @param string $name name of Object
1646 - * @param boolean $force_exists if set, throw an error if Object doesn't exist
1647 - */
1648 - function __construct(&$container, $name, $force_exists=False, $dohead=True)
1649 - {
1650 - if ($name[0] == "/") {
1651 - $r = "Object name '".$name;
1652 - $r .= "' cannot contain begin with a '/' character.";
1653 - throw new SyntaxException($r);
1654 - }
1655 - if (strlen($name) > MAX_OBJECT_NAME_LEN) {
1656 - throw new SyntaxException("Object name exceeds "
1657 - . "maximum allowed length.");
1658 - }
1659 - $this->container = $container;
1660 - $this->name = $name;
1661 - $this->etag = NULL;
1662 - $this->_etag_override = False;
1663 - $this->last_modified = NULL;
1664 - $this->content_type = NULL;
1665 - $this->content_length = 0;
1666 - $this->metadata = array();
1667 - $this->manifest = NULL;
1668 - if ($dohead) {
1669 - if (!$this->_initialize() && $force_exists) {
1670 - throw new NoSuchObjectException("No such object '".$name."'");
1671 - }
1672 - }
1673 - }
1674 -
1675 - /**
1676 - * String representation of Object
1677 - *
1678 - * Pretty print the Object's location and name
1679 - *
1680 - * @return string Object information
1681 - */
1682 - function __toString()
1683 - {
1684 - return $this->container->name . "/" . $this->name;
1685 - }
1686 -
1687 - /**
1688 - * Internal check to get the proper mimetype.
1689 - *
1690 - * This function would go over the available PHP methods to get
1691 - * the MIME type.
1692 - *
1693 - * By default it will try to use the PHP fileinfo library which is
1694 - * available from PHP 5.3 or as an PECL extension
1695 - * (http://pecl.php.net/package/Fileinfo).
1696 - *
1697 - * It will get the magic file by default from the system wide file
1698 - * which is usually available in /usr/share/magic on Unix or try
1699 - * to use the file specified in the source directory of the API
1700 - * (share directory).
1701 - *
1702 - * if fileinfo is not available it will try to use the internal
1703 - * mime_content_type function.
1704 - *
1705 - * @param string $handle name of file or buffer to guess the type from
1706 - * @return boolean <kbd>True</kbd> if successful
1707 - * @throws BadContentTypeException
1708 - */
1709 - function _guess_content_type($handle) {
1710 - if ($this->content_type)
1711 - return;
1712 -
1713 - if (function_exists("finfo_open")) {
1714 - $local_magic = dirname(__FILE__) . "/share/magic";
1715 - $finfo = @finfo_open(FILEINFO_MIME, $local_magic);
1716 -
1717 - if (!$finfo)
1718 - $finfo = @finfo_open(FILEINFO_MIME);
1719 -
1720 - if ($finfo) {
1721 -
1722 - if (is_file((string)$handle))
1723 - $ct = @finfo_file($finfo, $handle);
1724 - else
1725 - $ct = @finfo_buffer($finfo, $handle);
1726 -
1727 - /* PHP 5.3 fileinfo display extra information like
1728 - charset so we remove everything after the ; since
1729 - we are not into that stuff */
1730 - if ($ct) {
1731 - $extra_content_type_info = strpos($ct, "; ");
1732 - if ($extra_content_type_info)
1733 - $ct = substr($ct, 0, $extra_content_type_info);
1734 - }
1735 -
1736 - if ($ct && $ct != 'application/octet-stream')
1737 - $this->content_type = $ct;
1738 -
1739 - @finfo_close($finfo);
1740 - }
1741 - }
1742 -
1743 - if (!$this->content_type && (string)is_file($handle) && function_exists("mime_content_type")) {
1744 - $this->content_type = @mime_content_type($handle);
1745 - }
1746 -
1747 - if (!$this->content_type) {
1748 - throw new BadContentTypeException("Required Content-Type not set");
1749 - }
1750 - return True;
1751 - }
1752 -
1753 - /**
1754 - * String representation of the Object's public URI
1755 - *
1756 - * A string representing the Object's public URI assuming that it's
1757 - * parent Container is CDN-enabled.
1758 - *
1759 - * Example:
1760 - * <code>
1761 - * # ... authentication/connection/container code excluded
1762 - * # ... see previous examples
1763 - *
1764 - * # Print out the Object's CDN URI (if it has one) in an HTML img-tag
1765 - * #
1766 - * print "<img src='$pic->public_uri()' />\n";
1767 - * </code>
1768 - *
1769 - * @return string Object's public URI or NULL
1770 - */
1771 - function public_uri()
1772 - {
1773 - if ($this->container->cdn_enabled) {
1774 - return $this->container->cdn_uri . "/" . $this->name;
1775 - }
1776 - return NULL;
1777 - }
1778 -
1779 - /**
1780 - * String representation of the Object's public SSL URI
1781 - *
1782 - * A string representing the Object's public SSL URI assuming that it's
1783 - * parent Container is CDN-enabled.
1784 - *
1785 - * Example:
1786 - * <code>
1787 - * # ... authentication/connection/container code excluded
1788 - * # ... see previous examples
1789 - *
1790 - * # Print out the Object's CDN SSL URI (if it has one) in an HTML img-tag
1791 - * #
1792 - * print "<img src='$pic->public_ssl_uri()' />\n";
1793 - * </code>
1794 - *
1795 - * @return string Object's public SSL URI or NULL
1796 - */
1797 - function public_ssl_uri()
1798 - {
1799 - if ($this->container->cdn_enabled) {
1800 - return $this->container->cdn_ssl_uri . "/" . $this->name;
1801 - }
1802 - return NULL;
1803 - }
1804 -
1805 - /**
1806 - * Read the remote Object's data
1807 - *
1808 - * Returns the Object's data. This is useful for smaller Objects such
1809 - * as images or office documents. Object's with larger content should use
1810 - * the stream() method below.
1811 - *
1812 - * Pass in $hdrs array to set specific custom HTTP headers such as
1813 - * If-Match, If-None-Match, If-Modified-Since, Range, etc.
1814 - *
1815 - * Example:
1816 - * <code>
1817 - * # ... authentication/connection/container code excluded
1818 - * # ... see previous examples
1819 - *
1820 - * $my_docs = $conn->get_container("documents");
1821 - * $doc = $my_docs->get_object("README");
1822 - * $data = $doc->read(); # read image content into a string variable
1823 - * print $data;
1824 - *
1825 - * # Or see stream() below for a different example.
1826 - * #
1827 - * </code>
1828 - *
1829 - * @param array $hdrs user-defined headers (Range, If-Match, etc.)
1830 - * @return string Object's data
1831 - * @throws InvalidResponseException unexpected response
1832 - */
1833 - function read($hdrs=array())
1834 - {
1835 - list($status, $reason, $data) =
1836 - $this->container->cfs_http->get_object_to_string($this, $hdrs);
1837 - #if ($status == 401 && $this->_re_auth()) {
1838 - # return $this->read($hdrs);
1839 - #}
1840 - if (($status < 200) || ($status > 299
1841 - && $status != 412 && $status != 304)) {
1842 - throw new InvalidResponseException("Invalid response (".$status."): "
1843 - . $this->container->cfs_http->get_error());
1844 - }
1845 - return $data;
1846 - }
1847 -
1848 - /**
1849 - * Streaming read of Object's data
1850 - *
1851 - * Given an open PHP resource (see PHP's fopen() method), fetch the Object's
1852 - * data and write it to the open resource handle. This is useful for
1853 - * streaming an Object's content to the browser (videos, images) or for
1854 - * fetching content to a local file.
1855 - *
1856 - * Pass in $hdrs array to set specific custom HTTP headers such as
1857 - * If-Match, If-None-Match, If-Modified-Since, Range, etc.
1858 - *
1859 - * Example:
1860 - * <code>
1861 - * # ... authentication/connection/container code excluded
1862 - * # ... see previous examples
1863 - *
1864 - * # Assuming this is a web script to display the README to the
1865 - * # user's browser:
1866 - * #
1867 - * <?php
1868 - * // grab README from storage system
1869 - * //
1870 - * $my_docs = $conn->get_container("documents");
1871 - * $doc = $my_docs->get_object("README");
1872 - *
1873 - * // Hand it back to user's browser with appropriate content-type
1874 - * //
1875 - * header("Content-Type: " . $doc->content_type);
1876 - * $output = fopen("php://output", "w");
1877 - * $doc->stream($output); # stream object content to PHP's output buffer
1878 - * fclose($output);
1879 - * ?>
1880 - *
1881 - * # See read() above for a more simple example.
1882 - * #
1883 - * </code>
1884 - *
1885 - * @param resource $fp open resource for writing data to
1886 - * @param array $hdrs user-defined headers (Range, If-Match, etc.)
1887 - * @return string Object's data
1888 - * @throws InvalidResponseException unexpected response
1889 - */
1890 - function stream(&$fp, $hdrs=array())
1891 - {
1892 - list($status, $reason) =
1893 - $this->container->cfs_http->get_object_to_stream($this,$fp,$hdrs);
1894 - #if ($status == 401 && $this->_re_auth()) {
1895 - # return $this->stream($fp, $hdrs);
1896 - #}
1897 - if (($status < 200) || ($status > 299
1898 - && $status != 412 && $status != 304)) {
1899 - throw new InvalidResponseException("Invalid response (".$status."): "
1900 - .$reason);
1901 - }
1902 - return True;
1903 - }
1904 -
1905 - /**
1906 - * Store new Object metadata
1907 - *
1908 - * Write's an Object's metadata to the remote Object. This will overwrite
1909 - * an prior Object metadata.
1910 - *
1911 - * Example:
1912 - * <code>
1913 - * # ... authentication/connection/container code excluded
1914 - * # ... see previous examples
1915 - *
1916 - * $my_docs = $conn->get_container("documents");
1917 - * $doc = $my_docs->get_object("README");
1918 - *
1919 - * # Define new metadata for the object
1920 - * #
1921 - * $doc->metadata = array(
1922 - * "Author" => "EJ",
1923 - * "Subject" => "How to use the PHP tests",
1924 - * "Version" => "1.2.2"
1925 - * );
1926 - *
1927 - * # Push the new metadata up to the storage system
1928 - * #
1929 - * $doc->sync_metadata();
1930 - * </code>
1931 - *
1932 - * @return boolean <kbd>True</kbd> if successful, <kbd>False</kbd> otherwise
1933 - * @throws InvalidResponseException unexpected response
1934 - */
1935 - function sync_metadata()
1936 - {
1937 - if (!empty($this->metadata) || $this->manifest) {
1938 - $status = $this->container->cfs_http->update_object($this);
1939 - #if ($status == 401 && $this->_re_auth()) {
1940 - # return $this->sync_metadata();
1941 - #}
1942 - if ($status != 202) {
1943 - throw new InvalidResponseException("Invalid response ("
1944 - .$status."): ".$this->container->cfs_http->get_error());
1945 - }
1946 - return True;
1947 - }
1948 - return False;
1949 - }
1950 - /**
1951 - * Store new Object manifest
1952 - *
1953 - * Write's an Object's manifest to the remote Object. This will overwrite
1954 - * an prior Object manifest.
1955 - *
1956 - * Example:
1957 - * <code>
1958 - * # ... authentication/connection/container code excluded
1959 - * # ... see previous examples
1960 - *
1961 - * $my_docs = $conn->get_container("documents");
1962 - * $doc = $my_docs->get_object("README");
1963 - *
1964 - * # Define new manifest for the object
1965 - * #
1966 - * $doc->manifest = "container/prefix";
1967 - *
1968 - * # Push the new manifest up to the storage system
1969 - * #
1970 - * $doc->sync_manifest();
1971 - * </code>
1972 - *
1973 - * @return boolean <kbd>True</kbd> if successful, <kbd>False</kbd> otherwise
1974 - * @throws InvalidResponseException unexpected response
1975 - */
1976 -
1977 - function sync_manifest()
1978 - {
1979 - return $this->sync_metadata();
1980 - }
1981 - /**
1982 - * Upload Object's data to Cloud Files
1983 - *
1984 - * Write data to the remote Object. The $data argument can either be a
1985 - * PHP resource open for reading (see PHP's fopen() method) or an in-memory
1986 - * variable. If passing in a PHP resource, you must also include the $bytes
1987 - * parameter.
1988 - *
1989 - * Example:
1990 - * <code>
1991 - * # ... authentication/connection/container code excluded
1992 - * # ... see previous examples
1993 - *
1994 - * $my_docs = $conn->get_container("documents");
1995 - * $doc = $my_docs->get_object("README");
1996 - *
1997 - * # Upload placeholder text in my README
1998 - * #
1999 - * $doc->write("This is just placeholder text for now...");
2000 - * </code>
2001 - *
2002 - * @param string|resource $data string or open resource
2003 - * @param float $bytes amount of data to upload (required for resources)
2004 - * @param boolean $verify generate, send, and compare MD5 checksums
2005 - * @return boolean <kbd>True</kbd> when data uploaded successfully
2006 - * @throws SyntaxException missing required parameters
2007 - * @throws BadContentTypeException if no Content-Type was/could be set
2008 - * @throws MisMatchedChecksumException $verify is set and checksums unequal
2009 - * @throws InvalidResponseException unexpected response
2010 - */
2011 - function write($data=NULL, $bytes=0, $verify=True)
2012 - {
2013 - if (!$data && !is_string($data)) {
2014 - throw new SyntaxException("Missing data source.");
2015 - }
2016 - if ($bytes > MAX_OBJECT_SIZE) {
2017 - throw new SyntaxException("Bytes exceeds maximum object size.");
2018 - }
2019 - if ($verify) {
2020 - if (!$this->_etag_override) {
2021 - $this->etag = $this->compute_md5sum($data);
2022 - }
2023 - } else {
2024 - $this->etag = NULL;
2025 - }
2026 -
2027 - $close_fh = False;
2028 - if (!is_resource($data)) {
2029 - # A hack to treat string data as a file handle. php://memory feels
2030 - # like a better option, but it seems to break on Windows so use
2031 - # a temporary file instead.
2032 - #
2033 - $fp = fopen("php://temp", "wb+");
2034 - #$fp = fopen("php://memory", "wb+");
2035 - fwrite($fp, $data, strlen($data));
2036 - rewind($fp);
2037 - $close_fh = True;
2038 - $this->content_length = (float) strlen($data);
2039 - if ($this->content_length > MAX_OBJECT_SIZE) {
2040 - throw new SyntaxException("Data exceeds maximum object size");
2041 - }
2042 - $ct_data = substr($data, 0, 64);
2043 - } else {
2044 - $this->content_length = $bytes;
2045 - $fp = $data;
2046 - $ct_data = fread($data, 64);
2047 - rewind($data);
2048 - }
2049 -
2050 - $this->_guess_content_type($ct_data);
2051 -
2052 - list($status, $reason, $etag) =
2053 - $this->container->cfs_http->put_object($this, $fp);
2054 - #if ($status == 401 && $this->_re_auth()) {
2055 - # return $this->write($data, $bytes, $verify);
2056 - #}
2057 - if ($status == 412) {
2058 - if ($close_fh) { fclose($fp); }
2059 - throw new SyntaxException("Missing Content-Type header");
2060 - }
2061 - if ($status == 422) {
2062 - if ($close_fh) { fclose($fp); }
2063 - throw new MisMatchedChecksumException(
2064 - "Supplied and computed checksums do not match.");
2065 - }
2066 - if ($status != 201) {
2067 - if ($close_fh) { fclose($fp); }
2068 - throw new InvalidResponseException("Invalid response (".$status."): "
2069 - . $this->container->cfs_http->get_error());
2070 - }
2071 - if (!$verify) {
2072 - $this->etag = $etag;
2073 - }
2074 - if ($close_fh) { fclose($fp); }
2075 - return True;
2076 - }
2077 -
2078 - /**
2079 - * Copy one Object to another Object to Cloud Files
2080 - *
2081 - * Example:
2082 - * <code>
2083 - * # ... authentication/connection/container code excluded
2084 - * # ... see previous examples
2085 - *
2086 - * $my_docs = $conn->get_container("documents");
2087 - * $doc = $my_docs->get_object("README");
2088 - *
2089 - * # Copy README.txt on top of this object (which you must have
2090 - * already written something to).
2091 - * #
2092 - * $doc->copy("/documents/README.txt");
2093 - * </code>
2094 - *
2095 - * @param string $source Name of existing object
2096 - * @return boolean <kbd>True</kbd> when data uploaded successfully
2097 - * @throws SyntaxException missing required parameters
2098 - * @throws BadContentTypeException if no Content-Type was/could be set
2099 - * @throws MisMatchedChecksumException $verify is set and checksums unequal
2100 - * @throws InvalidResponseException unexpected response
2101 - */
2102 - function copy($source)
2103 - {
2104 - if (!$source && !is_string($source)) {
2105 - throw new SyntaxException("Missing data source.");
2106 - }
2107 - list($status, $reason, $etag) =
2108 - $this->container->cfs_http->put_object($this, $source);
2109 - #if ($status == 401 && $this->_re_auth()) {
2110 - # return $this->copy($data, $source);
2111 - #}
2112 - if ($status == 412) {
2113 - throw new SyntaxException("Missing Content-Type header");
2114 - }
2115 - if ($status == 422) {
2116 - throw new MisMatchedChecksumException(
2117 - "Supplied and computed checksums do not match.");
2118 - }
2119 - if ($status != 201) {
2120 - throw new InvalidResponseException("Invalid response (".$status."): "
2121 - . $this->container->cfs_http->get_error());
2122 - }
2123 - return True;
2124 - }
2125 -
2126 - /**
2127 - * Upload Object data from local filename
2128 - *
2129 - * This is a convenience function to upload the data from a local file. A
2130 - * True value for $verify will cause the method to compute the Object's MD5
2131 - * checksum prior to uploading.
2132 - *
2133 - * Example:
2134 - * <code>
2135 - * # ... authentication/connection/container code excluded
2136 - * # ... see previous examples
2137 - *
2138 - * $my_docs = $conn->get_container("documents");
2139 - * $doc = $my_docs->get_object("README");
2140 - *
2141 - * # Upload my local README's content
2142 - * #
2143 - * $doc->load_from_filename("/home/ej/cloudfiles/readme");
2144 - * </code>
2145 - *
2146 - * @param string $filename full path to local file
2147 - * @param boolean $verify enable local/remote MD5 checksum validation
2148 - * @return boolean <kbd>True</kbd> if data uploaded successfully
2149 - * @throws SyntaxException missing required parameters
2150 - * @throws BadContentTypeException if no Content-Type was/could be set
2151 - * @throws MisMatchedChecksumException $verify is set and checksums unequal
2152 - * @throws InvalidResponseException unexpected response
2153 - * @throws IOException error opening file
2154 - */
2155 - function load_from_filename($filename, $verify=True)
2156 - {
2157 - $fp = @fopen($filename, "r");
2158 - if (!$fp) {
2159 - throw new IOException("Could not open file for reading: ".$filename);
2160 - }
2161 -
2162 - clearstatcache();
2163 -
2164 - $size = (float) sprintf("%u", filesize($filename));
2165 - if ($size > MAX_OBJECT_SIZE) {
2166 - throw new SyntaxException("File size exceeds maximum object size.");
2167 - }
2168 -
2169 - $this->_guess_content_type($filename);
2170 -
2171 - $this->write($fp, $size, $verify);
2172 - fclose($fp);
2173 - return True;
2174 - }
2175 -
2176 - /**
2177 - * Save Object's data to local filename
2178 - *
2179 - * Given a local filename, the Object's data will be written to the newly
2180 - * created file.
2181 - *
2182 - * Example:
2183 - * <code>
2184 - * # ... authentication/connection/container code excluded
2185 - * # ... see previous examples
2186 - *
2187 - * # Whoops! I deleted my local README, let me download/save it
2188 - * #
2189 - * $my_docs = $conn->get_container("documents");
2190 - * $doc = $my_docs->get_object("README");
2191 - *
2192 - * $doc->save_to_filename("/home/ej/cloudfiles/readme.restored");
2193 - * </code>
2194 - *
2195 - * @param string $filename name of local file to write data to
2196 - * @return boolean <kbd>True</kbd> if successful
2197 - * @throws IOException error opening file
2198 - * @throws InvalidResponseException unexpected response
2199 - */
2200 - function save_to_filename($filename)
2201 - {
2202 - $fp = @fopen($filename, "wb");
2203 - if (!$fp) {
2204 - throw new IOException("Could not open file for writing: ".$filename);
2205 - }
2206 - $result = $this->stream($fp);
2207 - fclose($fp);
2208 - return $result;
2209 - }
2210 - /**
2211 - * Purge this Object from CDN Cache.
2212 - * Example:
2213 - * <code>
2214 - * # ... authentication code excluded (see previous examples) ...
2215 - * #
2216 - * $conn = new CF_Authentication($auth);
2217 - * $container = $conn->get_container("cdn_enabled");
2218 - * $obj = $container->get_object("object");
2219 - * $obj->purge_from_cdn("user@domain.com");
2220 - * # or
2221 - * $obj->purge_from_cdn();
2222 - * # or
2223 - * $obj->purge_from_cdn("user1@domain.com,user2@domain.com");
2224 - * @returns boolean True if successful
2225 - * @throws CDNNotEnabledException if CDN Is not enabled on this connection
2226 - * @throws InvalidResponseException if the response expected is not returned
2227 - */
2228 - function purge_from_cdn($email=null)
2229 - {
2230 - if (!$this->container->cfs_http->getCDNMUrl())
2231 - {
2232 - throw new CDNNotEnabledException(
2233 - "Authentication response did not indicate CDN availability");
2234 - }
2235 - $status = $this->container->cfs_http->purge_from_cdn($this->container->name . "/" . $this->name, $email);
2236 - if ($status < 199 or $status > 299) {
2237 - throw new InvalidResponseException(
2238 - "Invalid response (".$status."): ".$this->container->cfs_http->get_error());
2239 - }
2240 - return True;
2241 - }
2242 -
2243 - /**
2244 - * Set Object's MD5 checksum
2245 - *
2246 - * Manually set the Object's ETag. Including the ETag is mandatory for
2247 - * Cloud Files to perform end-to-end verification. Omitting the ETag forces
2248 - * the user to handle any data integrity checks.
2249 - *
2250 - * @param string $etag MD5 checksum hexidecimal string
2251 - */
2252 - function set_etag($etag)
2253 - {
2254 - $this->etag = $etag;
2255 - $this->_etag_override = True;
2256 - }
2257 -
2258 - /**
2259 - * Object's MD5 checksum
2260 - *
2261 - * Accessor method for reading Object's private ETag attribute.
2262 - *
2263 - * @return string MD5 checksum hexidecimal string
2264 - */
2265 - function getETag()
2266 - {
2267 - return $this->etag;
2268 - }
2269 -
2270 - /**
2271 - * Compute the MD5 checksum
2272 - *
2273 - * Calculate the MD5 checksum on either a PHP resource or data. The argument
2274 - * may either be a local filename, open resource for reading, or a string.
2275 - *
2276 - * <b>WARNING:</b> if you are uploading a big file over a stream
2277 - * it could get very slow to compute the md5 you probably want to
2278 - * set the $verify parameter to False in the write() method and
2279 - * compute yourself the md5 before if you have it.
2280 - *
2281 - * @param filename|obj|string $data filename, open resource, or string
2282 - * @return string MD5 checksum hexidecimal string
2283 - */
2284 - function compute_md5sum(&$data)
2285 - {
2286 -
2287 - if (function_exists("hash_init") && is_resource($data)) {
2288 - $ctx = hash_init('md5');
2289 - while (!feof($data)) {
2290 - $buffer = fgets($data, 65536);
2291 - hash_update($ctx, $buffer);
2292 - }
2293 - $md5 = hash_final($ctx, false);
2294 - rewind($data);
2295 - } elseif ((string)is_file($data)) {
2296 - $md5 = md5_file($data);
2297 - } else {
2298 - $md5 = md5($data);
2299 - }
2300 - return $md5;
2301 - }
2302 -
2303 - /**
2304 - * PRIVATE: fetch information about the remote Object if it exists
2305 - */
2306 - private function _initialize()
2307 - {
2308 - list($status, $reason, $etag, $last_modified, $content_type,
2309 - $content_length, $metadata) =
2310 - $this->container->cfs_http->head_object($this);
2311 - #if ($status == 401 && $this->_re_auth()) {
2312 - # return $this->_initialize();
2313 - #}
2314 - if ($status == 404) {
2315 - return False;
2316 - }
2317 - if ($status < 200 || $status > 299) {
2318 - throw new InvalidResponseException("Invalid response (".$status."): "
2319 - . $this->container->cfs_http->get_error());
2320 - }
2321 - $this->etag = $etag;
2322 - $this->last_modified = $last_modified;
2323 - $this->content_type = $content_type;
2324 - $this->content_length = $content_length;
2325 - $this->metadata = $metadata;
2326 - $this->manifest = NULL;
2327 - return True;
2328 - }
2329 -
2330 - #private function _re_auth()
2331 - #{
2332 - # $new_auth = new CF_Authentication(
2333 - # $this->cfs_auth->username,
2334 - # $this->cfs_auth->api_key,
2335 - # $this->cfs_auth->auth_host,
2336 - # $this->cfs_auth->account);
2337 - # $new_auth->authenticate();
2338 - # $this->container->cfs_auth = $new_auth;
2339 - # $this->container->cfs_http->setCFAuth($this->cfs_auth);
2340 - # return True;
2341 - #}
2342 -}
2343 -
2344 -/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2345 -
2346 -/*
2347 - * Local variables:
2348 - * tab-width: 4
2349 - * c-basic-offset: 4
2350 - * c-hanging-comment-ender-p: nil
2351 - * End:
2352 - */
2353 -?>
Index: trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles_http.php
@@ -1,1397 +0,0 @@
2 -<?php
3 -/**
4 - * This is an HTTP client class for Cloud Files. It uses PHP's cURL module
5 - * to handle the actual HTTP request/response. This is NOT a generic HTTP
6 - * client class and is only used to abstract out the HTTP communication for
7 - * the PHP Cloud Files API.
8 - *
9 - * This module was designed to re-use existing HTTP(S) connections between
10 - * subsequent operations. For example, performing multiple PUT operations
11 - * will re-use the same connection.
12 - *
13 - * This modules also provides support for streaming content into and out
14 - * of Cloud Files. The majority (all?) of the PHP HTTP client modules expect
15 - * to read the server's response into a string variable. This will not work
16 - * with large files without killing your server. Methods like,
17 - * get_object_to_stream() and put_object() take an open filehandle
18 - * argument for streaming data out of or into Cloud Files.
19 - *
20 - * Requres PHP 5.x (for Exceptions and OO syntax)
21 - *
22 - * See COPYING for license information.
23 - *
24 - * @author Eric "EJ" Johnson <ej@racklabs.com>
25 - * @copyright Copyright (c) 2008, Rackspace US, Inc.
26 - * @package php-cloudfiles-http
27 - */
28 -
29 -/**
30 - */
31 -require_once("cloudfiles_exceptions.php");
32 -
33 -define("PHP_CF_VERSION", "1.7.9");
34 -define("USER_AGENT", sprintf("PHP-CloudFiles/%s", PHP_CF_VERSION));
35 -define("ACCOUNT_CONTAINER_COUNT", "X-Account-Container-Count");
36 -define("ACCOUNT_BYTES_USED", "X-Account-Bytes-Used");
37 -define("CONTAINER_OBJ_COUNT", "X-Container-Object-Count");
38 -define("CONTAINER_BYTES_USED", "X-Container-Bytes-Used");
39 -define("METADATA_HEADER", "X-Object-Meta-");
40 -define("MANIFEST_HEADER", "X-Object-Manifest");
41 -define("CDN_URI", "X-CDN-URI");
42 -define("CDN_SSL_URI", "X-CDN-SSL-URI");
43 -define("CDN_ENABLED", "X-CDN-Enabled");
44 -define("CDN_LOG_RETENTION", "X-Log-Retention");
45 -define("CDN_ACL_USER_AGENT", "X-User-Agent-ACL");
46 -define("CDN_ACL_REFERRER", "X-Referrer-ACL");
47 -define("CDN_TTL", "X-TTL");
48 -define("CDNM_URL", "X-CDN-Management-Url");
49 -define("STORAGE_URL", "X-Storage-Url");
50 -define("AUTH_TOKEN", "X-Auth-Token");
51 -define("AUTH_USER_HEADER", "X-Auth-User");
52 -define("AUTH_KEY_HEADER", "X-Auth-Key");
53 -define("AUTH_USER_HEADER_LEGACY", "X-Storage-User");
54 -define("AUTH_KEY_HEADER_LEGACY", "X-Storage-Pass");
55 -define("AUTH_TOKEN_LEGACY", "X-Storage-Token");
56 -define("CDN_EMAIL", "X-Purge-Email");
57 -/**
58 - * HTTP/cURL wrapper for Cloud Files
59 - *
60 - * This class should not be used directly. It's only purpose is to abstract
61 - * out the HTTP communication from the main API.
62 - *
63 - * @package php-cloudfiles-http
64 - */
65 -class CF_Http
66 -{
67 - private $error_str;
68 - private $dbug;
69 - private $cabundle_path;
70 - private $api_version;
71 -
72 - # Authentication instance variables
73 - #
74 - private $storage_url;
75 - private $cdnm_url;
76 - private $auth_token;
77 -
78 - # Request/response variables
79 - #
80 - private $response_status;
81 - private $response_reason;
82 - private $connections;
83 -
84 - # Variables used for content/header callbacks
85 - #
86 - private $_user_read_progress_callback_func;
87 - private $_user_write_progress_callback_func;
88 - private $_write_callback_type;
89 - private $_text_list;
90 - private $_account_container_count;
91 - private $_account_bytes_used;
92 - private $_container_object_count;
93 - private $_container_bytes_used;
94 - private $_obj_etag;
95 - private $_obj_last_modified;
96 - private $_obj_content_type;
97 - private $_obj_content_length;
98 - private $_obj_metadata;
99 - private $_obj_manifest;
100 - private $_obj_write_resource;
101 - private $_obj_write_string;
102 - private $_cdn_enabled;
103 - private $_cdn_ssl_uri;
104 - private $_cdn_uri;
105 - private $_cdn_ttl;
106 - private $_cdn_log_retention;
107 - private $_cdn_acl_user_agent;
108 - private $_cdn_acl_referrer;
109 -
110 - function __construct($api_version)
111 - {
112 - $this->dbug = False;
113 - $this->cabundle_path = NULL;
114 - $this->api_version = $api_version;
115 - $this->error_str = NULL;
116 -
117 - $this->storage_url = NULL;
118 - $this->cdnm_url = NULL;
119 - $this->auth_token = NULL;
120 -
121 - $this->response_status = NULL;
122 - $this->response_reason = NULL;
123 -
124 - # Curl connections array - since there is no way to "re-set" the
125 - # connection paramaters for a cURL handle, we keep an array of
126 - # the unique use-cases and funnel all of those same type
127 - # requests through the appropriate curl connection.
128 - #
129 - $this->connections = array(
130 - "GET_CALL" => NULL, # GET objects/containers/lists
131 - "PUT_OBJ" => NULL, # PUT object
132 - "HEAD" => NULL, # HEAD requests
133 - "PUT_CONT" => NULL, # PUT container
134 - "DEL_POST" => NULL, # DELETE containers/objects, POST objects
135 - );
136 -
137 - $this->_user_read_progress_callback_func = NULL;
138 - $this->_user_write_progress_callback_func = NULL;
139 - $this->_write_callback_type = NULL;
140 - $this->_text_list = array();
141 - $this->_return_list = NULL;
142 - $this->_account_container_count = 0;
143 - $this->_account_bytes_used = 0;
144 - $this->_container_object_count = 0;
145 - $this->_container_bytes_used = 0;
146 - $this->_obj_write_resource = NULL;
147 - $this->_obj_write_string = "";
148 - $this->_obj_etag = NULL;
149 - $this->_obj_last_modified = NULL;
150 - $this->_obj_content_type = NULL;
151 - $this->_obj_content_length = NULL;
152 - $this->_obj_metadata = array();
153 - $this->_obj_manifest = NULL;
154 - $this->_cdn_enabled = NULL;
155 - $this->_cdn_ssl_uri = NULL;
156 - $this->_cdn_uri = NULL;
157 - $this->_cdn_ttl = NULL;
158 - $this->_cdn_log_retention = NULL;
159 - $this->_cdn_acl_user_agent = NULL;
160 - $this->_cdn_acl_referrer = NULL;
161 -
162 - # The OS list with a PHP without an updated CA File for CURL to
163 - # connect to SSL Websites. It is the first 3 letters of the PHP_OS
164 - # variable.
165 - $OS_CAFILE_NONUPDATED=array(
166 - "win","dar"
167 - );
168 -
169 - if (in_array((strtolower (substr(PHP_OS, 0,3))), $OS_CAFILE_NONUPDATED))
170 - $this->ssl_use_cabundle();
171 -
172 - }
173 -
174 - function ssl_use_cabundle($path=NULL)
175 - {
176 - if ($path) {
177 - $this->cabundle_path = $path;
178 - } else {
179 - $this->cabundle_path = dirname(__FILE__) . "/share/cacert.pem";
180 - }
181 - if (!file_exists($this->cabundle_path)) {
182 - throw new IOException("Could not use CA bundle: "
183 - . $this->cabundle_path);
184 - }
185 - return;
186 - }
187 -
188 - # Uses separate cURL connection to authenticate
189 - #
190 - function authenticate($user, $pass, $acct=NULL, $host=NULL)
191 - {
192 - $path = array();
193 - if (isset($acct)){
194 - $headers = array(
195 - sprintf("%s: %s", AUTH_USER_HEADER_LEGACY, $user),
196 - sprintf("%s: %s", AUTH_KEY_HEADER_LEGACY, $pass),
197 - );
198 - $path[] = $host;
199 - $path[] = rawurlencode(sprintf("v%d",$this->api_version));
200 - $path[] = rawurlencode($acct);
201 - } else {
202 - $headers = array(
203 - sprintf("%s: %s", AUTH_USER_HEADER, $user),
204 - sprintf("%s: %s", AUTH_KEY_HEADER, $pass),
205 - );
206 - $path[] = $host;
207 - }
208 - $path[] = "v1.0";
209 - $url = implode("/", $path);
210 -
211 - $curl_ch = curl_init();
212 - if (!is_null($this->cabundle_path)) {
213 - curl_setopt($curl_ch, CURLOPT_SSL_VERIFYPEER, True);
214 - curl_setopt($curl_ch, CURLOPT_CAINFO, $this->cabundle_path);
215 - }
216 - curl_setopt($curl_ch, CURLOPT_SSL_VERIFYPEER, False);
217 - curl_setopt($curl_ch, CURLOPT_VERBOSE, $this->dbug);
218 - curl_setopt($curl_ch, CURLOPT_FOLLOWLOCATION, 1);
219 - curl_setopt($curl_ch, CURLOPT_MAXREDIRS, 4);
220 - curl_setopt($curl_ch, CURLOPT_HEADER, 0);
221 - curl_setopt($curl_ch, CURLOPT_HTTPHEADER, $headers);
222 - curl_setopt($curl_ch, CURLOPT_USERAGENT, USER_AGENT);
223 - curl_setopt($curl_ch, CURLOPT_RETURNTRANSFER, TRUE);
224 - curl_setopt($curl_ch, CURLOPT_HEADERFUNCTION,array(&$this,'_auth_hdr_cb'));
225 - curl_setopt($curl_ch, CURLOPT_CONNECTTIMEOUT, 10);
226 - curl_setopt($curl_ch, CURLOPT_URL, $url);
227 - curl_exec($curl_ch);
228 - curl_close($curl_ch);
229 -
230 - return array($this->response_status, $this->response_reason,
231 - $this->storage_url, $this->cdnm_url, $this->auth_token);
232 - }
233 -
234 - # (CDN) GET /v1/Account
235 - #
236 - function list_cdn_containers($enabled_only)
237 - {
238 - $conn_type = "GET_CALL";
239 - $url_path = $this->_make_path("CDN");
240 -
241 - $this->_write_callback_type = "TEXT_LIST";
242 - if ($enabled_only)
243 - {
244 - $return_code = $this->_send_request($conn_type, $url_path .
245 - '/?enabled_only=true');
246 - }
247 - else
248 - {
249 - $return_code = $this->_send_request($conn_type, $url_path);
250 - }
251 - if (!$return_code) {
252 - $this->error_str .= ": Failed to obtain valid HTTP response.";
253 - array(0,$this->error_str,array());
254 - }
255 - if ($return_code == 401) {
256 - return array($return_code,"Unauthorized",array());
257 - }
258 - if ($return_code == 404) {
259 - return array($return_code,"Account not found.",array());
260 - }
261 - if ($return_code == 204) {
262 - return array($return_code,"Account has no CDN enabled Containers.",
263 - array());
264 - }
265 - if ($return_code == 200) {
266 - $this->create_array();
267 - return array($return_code,$this->response_reason,$this->_text_list);
268 - }
269 - $this->error_str = "Unexpected HTTP response: ".$this->response_reason;
270 - return array($return_code,$this->error_str,array());
271 - }
272 -
273 - # (CDN) DELETE /v1/Account/Container or /v1/Account/Container/Object
274 - #
275 - function purge_from_cdn($path, $email=null)
276 - {
277 - if(!$path)
278 - throw new SyntaxException("Path not set");
279 - $url_path = $this->_make_path("CDN", NULL, $path);
280 - if($email)
281 - {
282 - $hdrs = array(CDN_EMAIL => $email);
283 - $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"DELETE");
284 - }
285 - else
286 - $return_code = $this->_send_request("DEL_POST",$url_path,null,"DELETE");
287 - return $return_code;
288 - }
289 -
290 - # (CDN) POST /v1/Account/Container
291 - function update_cdn_container($container_name, $ttl=86400, $cdn_log_retention=False,
292 - $cdn_acl_user_agent="", $cdn_acl_referrer)
293 - {
294 - if ($container_name == "")
295 - throw new SyntaxException("Container name not set.");
296 -
297 - if ($container_name != "0" and !isset($container_name))
298 - throw new SyntaxException("Container name not set.");
299 -
300 - $url_path = $this->_make_path("CDN", $container_name);
301 - $hdrs = array(
302 - CDN_ENABLED => "True",
303 - CDN_TTL => $ttl,
304 - CDN_LOG_RETENTION => $cdn_log_retention ? "True" : "False",
305 - CDN_ACL_USER_AGENT => $cdn_acl_user_agent,
306 - CDN_ACL_REFERRER => $cdn_acl_referrer,
307 - );
308 - $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST");
309 - if ($return_code == 401) {
310 - $this->error_str = "Unauthorized";
311 - return array($return_code, $this->error_str, NULL);
312 - }
313 - if ($return_code == 404) {
314 - $this->error_str = "Container not found.";
315 - return array($return_code, $this->error_str, NULL);
316 - }
317 - if ($return_code != 202) {
318 - $this->error_str="Unexpected HTTP response: ".$this->response_reason;
319 - return array($return_code, $this->error_str, NULL);
320 - }
321 - return array($return_code, "Accepted", $this->_cdn_uri, $this->_cdn_ssl_uri);
322 -
323 - }
324 -
325 - # (CDN) PUT /v1/Account/Container
326 - #
327 - function add_cdn_container($container_name, $ttl=86400)
328 - {
329 - if ($container_name == "")
330 - throw new SyntaxException("Container name not set.");
331 -
332 - if ($container_name != "0" and !isset($container_name))
333 - throw new SyntaxException("Container name not set.");
334 -
335 - $url_path = $this->_make_path("CDN", $container_name);
336 - $hdrs = array(
337 - CDN_ENABLED => "True",
338 - CDN_TTL => $ttl,
339 - );
340 - $return_code = $this->_send_request("PUT_CONT", $url_path, $hdrs);
341 - if ($return_code == 401) {
342 - $this->error_str = "Unauthorized";
343 - return array($return_code,$this->response_reason,False);
344 - }
345 - if (!in_array($return_code, array(201,202))) {
346 - $this->error_str="Unexpected HTTP response: ".$this->response_reason;
347 - return array($return_code,$this->response_reason,False);
348 - }
349 - return array($return_code,$this->response_reason,$this->_cdn_uri,
350 - $this->_cdn_ssl_uri);
351 - }
352 -
353 - # (CDN) POST /v1/Account/Container
354 - #
355 - function remove_cdn_container($container_name)
356 - {
357 - if ($container_name == "")
358 - throw new SyntaxException("Container name not set.");
359 -
360 - if ($container_name != "0" and !isset($container_name))
361 - throw new SyntaxException("Container name not set.");
362 -
363 - $url_path = $this->_make_path("CDN", $container_name);
364 - $hdrs = array(CDN_ENABLED => "False");
365 - $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST");
366 - if ($return_code == 401) {
367 - $this->error_str = "Unauthorized";
368 - return array($return_code, $this->error_str);
369 - }
370 - if ($return_code == 404) {
371 - $this->error_str = "Container not found.";
372 - return array($return_code, $this->error_str);
373 - }
374 - if ($return_code != 202) {
375 - $this->error_str="Unexpected HTTP response: ".$this->response_reason;
376 - return array($return_code, $this->error_str);
377 - }
378 - return array($return_code, "Accepted");
379 - }
380 -
381 - # (CDN) HEAD /v1/Account
382 - #
383 - function head_cdn_container($container_name)
384 - {
385 - if ($container_name == "")
386 - throw new SyntaxException("Container name not set.");
387 -
388 - if ($container_name != "0" and !isset($container_name))
389 - throw new SyntaxException("Container name not set.");
390 -
391 - $conn_type = "HEAD";
392 - $url_path = $this->_make_path("CDN", $container_name);
393 - $return_code = $this->_send_request($conn_type, $url_path, NULL, "GET", True);
394 -
395 - if (!$return_code) {
396 - $this->error_str .= ": Failed to obtain valid HTTP response.";
397 - return array(0,$this->error_str,NULL,NULL,NULL,NULL,NULL,NULL);
398 - }
399 - if ($return_code == 401) {
400 - return array($return_code,"Unauthorized",NULL,NULL,NULL,NULL,NULL,NULL);
401 - }
402 - if ($return_code == 404) {
403 - return array($return_code,"Account not found.",NULL,NULL,NULL,NULL,NULL,NULL);
404 - }
405 - if ($return_code == 204) {
406 - return array($return_code,$this->response_reason,
407 - $this->_cdn_enabled, $this->_cdn_ssl_uri,
408 - $this->_cdn_uri, $this->_cdn_ttl,
409 - $this->_cdn_log_retention,
410 - $this->_cdn_acl_user_agent,
411 - $this->_cdn_acl_referrer
412 - );
413 - }
414 - return array($return_code,$this->response_reason,
415 - NULL,NULL,NULL,
416 - $this->_cdn_log_retention,
417 - $this->_cdn_acl_user_agent,
418 - $this->_cdn_acl_referrer
419 - );
420 - }
421 -
422 - # GET /v1/Account
423 - #
424 - function list_containers($limit=0, $marker=NULL)
425 - {
426 - $conn_type = "GET_CALL";
427 - $url_path = $this->_make_path();
428 -
429 - $limit = intval($limit);
430 - $params = array();
431 - if ($limit > 0) {
432 - $params[] = "limit=$limit";
433 - }
434 - if ($marker) {
435 - $params[] = "marker=".rawurlencode($marker);
436 - }
437 - if (!empty($params)) {
438 - $url_path .= "?" . implode("&", $params);
439 - }
440 -
441 - $this->_write_callback_type = "TEXT_LIST";
442 - $return_code = $this->_send_request($conn_type, $url_path);
443 -
444 - if (!$return_code) {
445 - $this->error_str .= ": Failed to obtain valid HTTP response.";
446 - return array(0,$this->error_str,array());
447 - }
448 - if ($return_code == 204) {
449 - return array($return_code, "Account has no containers.", array());
450 - }
451 - if ($return_code == 404) {
452 - $this->error_str = "Invalid account name for authentication token.";
453 - return array($return_code,$this->error_str,array());
454 - }
455 - if ($return_code == 200) {
456 - $this->create_array();
457 - return array($return_code, $this->response_reason, $this->_text_list);
458 - }
459 - $this->error_str = "Unexpected HTTP response: ".$this->response_reason;
460 - return array($return_code,$this->error_str,array());
461 - }
462 -
463 - # GET /v1/Account?format=json
464 - #
465 - function list_containers_info($limit=0, $marker=NULL)
466 - {
467 - $conn_type = "GET_CALL";
468 - $url_path = $this->_make_path() . "?format=json";
469 -
470 - $limit = intval($limit);
471 - $params = array();
472 - if ($limit > 0) {
473 - $params[] = "limit=$limit";
474 - }
475 - if ($marker) {
476 - $params[] = "marker=".rawurlencode($marker);
477 - }
478 - if (!empty($params)) {
479 - $url_path .= "&" . implode("&", $params);
480 - }
481 -
482 - $this->_write_callback_type = "OBJECT_STRING";
483 - $return_code = $this->_send_request($conn_type, $url_path);
484 -
485 - if (!$return_code) {
486 - $this->error_str .= ": Failed to obtain valid HTTP response.";
487 - return array(0,$this->error_str,array());
488 - }
489 - if ($return_code == 204) {
490 - return array($return_code, "Account has no containers.", array());
491 - }
492 - if ($return_code == 404) {
493 - $this->error_str = "Invalid account name for authentication token.";
494 - return array($return_code,$this->error_str,array());
495 - }
496 - if ($return_code == 200) {
497 - $json_body = json_decode($this->_obj_write_string, True);
498 - return array($return_code, $this->response_reason, $json_body);
499 - }
500 - $this->error_str = "Unexpected HTTP response: ".$this->response_reason;
501 - return array($return_code,$this->error_str,array());
502 - }
503 -
504 - # HEAD /v1/Account
505 - #
506 - function head_account()
507 - {
508 - $conn_type = "HEAD";
509 -
510 - $url_path = $this->_make_path();
511 - $return_code = $this->_send_request($conn_type,$url_path);
512 -
513 - if (!$return_code) {
514 - $this->error_str .= ": Failed to obtain valid HTTP response.";
515 - array(0,$this->error_str,0,0);
516 - }
517 - if ($return_code == 404) {
518 - return array($return_code,"Account not found.",0,0);
519 - }
520 - if ($return_code == 204) {
521 - return array($return_code,$this->response_reason,
522 - $this->_account_container_count, $this->_account_bytes_used);
523 - }
524 - return array($return_code,$this->response_reason,0,0);
525 - }
526 -
527 - # PUT /v1/Account/Container
528 - #
529 - function create_container($container_name)
530 - {
531 - if ($container_name == "")
532 - throw new SyntaxException("Container name not set.");
533 -
534 - if ($container_name != "0" and !isset($container_name))
535 - throw new SyntaxException("Container name not set.");
536 -
537 - $url_path = $this->_make_path("STORAGE", $container_name);
538 - $return_code = $this->_send_request("PUT_CONT",$url_path);
539 -
540 - if (!$return_code) {
541 - $this->error_str .= ": Failed to obtain valid HTTP response.";
542 - return False;
543 - }
544 - return $return_code;
545 - }
546 -
547 - # DELETE /v1/Account/Container
548 - #
549 - function delete_container($container_name)
550 - {
551 - if ($container_name == "")
552 - throw new SyntaxException("Container name not set.");
553 -
554 - if ($container_name != "0" and !isset($container_name))
555 - throw new SyntaxException("Container name not set.");
556 -
557 - $url_path = $this->_make_path("STORAGE", $container_name);
558 - $return_code = $this->_send_request("DEL_POST",$url_path,array(),"DELETE");
559 -
560 - if (!$return_code) {
561 - $this->error_str .= ": Failed to obtain valid HTTP response.";
562 - }
563 - if ($return_code == 409) {
564 - $this->error_str = "Container must be empty prior to removing it.";
565 - }
566 - if ($return_code == 404) {
567 - $this->error_str = "Specified container did not exist to delete.";
568 - }
569 - if ($return_code != 204) {
570 - $this->error_str = "Unexpected HTTP return code: $return_code.";
571 - }
572 - return $return_code;
573 - }
574 -
575 - # GET /v1/Account/Container
576 - #
577 - function list_objects($cname,$limit=0,$marker=NULL,$prefix=NULL,$path=NULL)
578 - {
579 - if (!$cname) {
580 - $this->error_str = "Container name not set.";
581 - return array(0, $this->error_str, array());
582 - }
583 -
584 - $url_path = $this->_make_path("STORAGE", $cname);
585 -
586 - $limit = intval($limit);
587 - $params = array();
588 - if ($limit > 0) {
589 - $params[] = "limit=$limit";
590 - }
591 - if ($marker) {
592 - $params[] = "marker=".rawurlencode($marker);
593 - }
594 - if ($prefix) {
595 - $params[] = "prefix=".rawurlencode($prefix);
596 - }
597 - if ($path) {
598 - $params[] = "path=".rawurlencode($path);
599 - }
600 - if (!empty($params)) {
601 - $url_path .= "?" . implode("&", $params);
602 - }
603 -
604 - $conn_type = "GET_CALL";
605 - $this->_write_callback_type = "TEXT_LIST";
606 - $return_code = $this->_send_request($conn_type,$url_path);
607 -
608 - if (!$return_code) {
609 - $this->error_str .= ": Failed to obtain valid HTTP response.";
610 - return array(0,$this->error_str,array());
611 - }
612 - if ($return_code == 204) {
613 - $this->error_str = "Container has no Objects.";
614 - return array($return_code,$this->error_str,array());
615 - }
616 - if ($return_code == 404) {
617 - $this->error_str = "Container has no Objects.";
618 - return array($return_code,$this->error_str,array());
619 - }
620 - if ($return_code == 200) {
621 - $this->create_array();
622 - return array($return_code,$this->response_reason, $this->_text_list);
623 - }
624 - $this->error_str = "Unexpected HTTP response code: $return_code";
625 - return array(0,$this->error_str,array());
626 - }
627 -
628 - # GET /v1/Account/Container?format=json
629 - #
630 - function get_objects($cname,$limit=0,$marker=NULL,$prefix=NULL,$path=NULL)
631 - {
632 - if (!$cname) {
633 - $this->error_str = "Container name not set.";
634 - return array(0, $this->error_str, array());
635 - }
636 -
637 - $url_path = $this->_make_path("STORAGE", $cname);
638 -
639 - $limit = intval($limit);
640 - $params = array();
641 - $params[] = "format=json";
642 - if ($limit > 0) {
643 - $params[] = "limit=$limit";
644 - }
645 - if ($marker) {
646 - $params[] = "marker=".rawurlencode($marker);
647 - }
648 - if ($prefix) {
649 - $params[] = "prefix=".rawurlencode($prefix);
650 - }
651 - if ($path) {
652 - $params[] = "path=".rawurlencode($path);
653 - }
654 - if (!empty($params)) {
655 - $url_path .= "?" . implode("&", $params);
656 - }
657 -
658 - $conn_type = "GET_CALL";
659 - $this->_write_callback_type = "OBJECT_STRING";
660 - $return_code = $this->_send_request($conn_type,$url_path);
661 -
662 - if (!$return_code) {
663 - $this->error_str .= ": Failed to obtain valid HTTP response.";
664 - return array(0,$this->error_str,array());
665 - }
666 - if ($return_code == 204) {
667 - $this->error_str = "Container has no Objects.";
668 - return array($return_code,$this->error_str,array());
669 - }
670 - if ($return_code == 404) {
671 - $this->error_str = "Container has no Objects.";
672 - return array($return_code,$this->error_str,array());
673 - }
674 - if ($return_code == 200) {
675 - $json_body = json_decode($this->_obj_write_string, True);
676 - return array($return_code,$this->response_reason, $json_body);
677 - }
678 - $this->error_str = "Unexpected HTTP response code: $return_code";
679 - return array(0,$this->error_str,array());
680 - }
681 -
682 -
683 - # HEAD /v1/Account/Container
684 - #
685 - function head_container($container_name)
686 - {
687 -
688 - if ($container_name == "") {
689 - $this->error_str = "Container name not set.";
690 - return False;
691 - }
692 -
693 - if ($container_name != "0" and !isset($container_name)) {
694 - $this->error_str = "Container name not set.";
695 - return False;
696 - }
697 -
698 - $conn_type = "HEAD";
699 -
700 - $url_path = $this->_make_path("STORAGE", $container_name);
701 - $return_code = $this->_send_request($conn_type,$url_path);
702 -
703 - if (!$return_code) {
704 - $this->error_str .= ": Failed to obtain valid HTTP response.";
705 - array(0,$this->error_str,0,0);
706 - }
707 - if ($return_code == 404) {
708 - return array($return_code,"Container not found.",0,0);
709 - }
710 - if ($return_code == 204 || $return_code == 200) {
711 - return array($return_code,$this->response_reason,
712 - $this->_container_object_count, $this->_container_bytes_used);
713 - }
714 - return array($return_code,$this->response_reason,0,0);
715 - }
716 -
717 - # GET /v1/Account/Container/Object
718 - #
719 - function get_object_to_string(&$obj, $hdrs=array())
720 - {
721 - if (!is_object($obj) || get_class($obj) != "CF_Object") {
722 - throw new SyntaxException(
723 - "Method argument is not a valid CF_Object.");
724 - }
725 -
726 - $conn_type = "GET_CALL";
727 -
728 - $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name);
729 - $this->_write_callback_type = "OBJECT_STRING";
730 - $return_code = $this->_send_request($conn_type,$url_path,$hdrs);
731 -
732 - if (!$return_code) {
733 - $this->error_str .= ": Failed to obtain valid HTTP response.";
734 - return array($return_code0,$this->error_str,NULL);
735 - }
736 - if ($return_code == 404) {
737 - $this->error_str = "Object not found.";
738 - return array($return_code0,$this->error_str,NULL);
739 - }
740 - if (($return_code < 200) || ($return_code > 299
741 - && $return_code != 412 && $return_code != 304)) {
742 - $this->error_str = "Unexpected HTTP return code: $return_code";
743 - return array($return_code,$this->error_str,NULL);
744 - }
745 - return array($return_code,$this->response_reason, $this->_obj_write_string);
746 - }
747 -
748 - # GET /v1/Account/Container/Object
749 - #
750 - function get_object_to_stream(&$obj, &$resource=NULL, $hdrs=array())
751 - {
752 - if (!is_object($obj) || get_class($obj) != "CF_Object") {
753 - throw new SyntaxException(
754 - "Method argument is not a valid CF_Object.");
755 - }
756 - if (!is_resource($resource)) {
757 - throw new SyntaxException(
758 - "Resource argument not a valid PHP resource.");
759 - }
760 -
761 - $conn_type = "GET_CALL";
762 -
763 - $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name);
764 - $this->_obj_write_resource = $resource;
765 - $this->_write_callback_type = "OBJECT_STREAM";
766 - $return_code = $this->_send_request($conn_type,$url_path,$hdrs);
767 -
768 - if (!$return_code) {
769 - $this->error_str .= ": Failed to obtain valid HTTP response.";
770 - return array($return_code,$this->error_str);
771 - }
772 - if ($return_code == 404) {
773 - $this->error_str = "Object not found.";
774 - return array($return_code,$this->error_str);
775 - }
776 - if (($return_code < 200) || ($return_code > 299
777 - && $return_code != 412 && $return_code != 304)) {
778 - $this->error_str = "Unexpected HTTP return code: $return_code";
779 - return array($return_code,$this->error_str);
780 - }
781 - return array($return_code,$this->response_reason);
782 - }
783 -
784 - # PUT /v1/Account/Container/Object
785 - #
786 - function put_object(&$obj, &$fp)
787 - {
788 - if (!is_object($obj) || get_class($obj) != "CF_Object") {
789 - throw new SyntaxException(
790 - "Method argument is not a valid CF_Object.");
791 - }
792 - $source = NULL;
793 - if (is_string($fp)) {
794 - $source = $fp;
795 - } elseif (!is_resource($fp)) {
796 - throw new SyntaxException(
797 - "File pointer argument is not a valid resource.");
798 - }
799 -
800 - $conn_type = "PUT_OBJ";
801 - $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name);
802 -
803 - $hdrs = $this->_metadata_headers($obj);
804 -
805 - $etag = $obj->getETag();
806 - if (!$source && isset($etag)) {
807 - $hdrs[] = "ETag: " . $etag;
808 - }
809 - if ($source) {
810 - // If we don't include a content-type it will copy over the existing one.
811 - } elseif (!$obj->content_type) {
812 - $hdrs[] = "Content-Type: application/octet-stream";
813 - } else {
814 - $hdrs[] = "Content-Type: " . $obj->content_type;
815 - }
816 -
817 - $this->_init($conn_type);
818 - if ($source) {
819 - $hdrs[] = "X-Copy-From: " . rawurlencode($source);
820 - curl_setopt($this->connections[$conn_type],
821 - CURLOPT_INFILESIZE, 0);
822 - } else {
823 - curl_setopt($this->connections[$conn_type],
824 - CURLOPT_INFILE, $fp);
825 - if (!$obj->content_length) {
826 - # We don''t know the Content-Length, so assumed "chunked" PUT
827 - #
828 - curl_setopt($this->connections[$conn_type], CURLOPT_UPLOAD, True);
829 - $hdrs[] = 'Transfer-Encoding: chunked';
830 - } else {
831 - # We know the Content-Length, so use regular transfer
832 - #
833 - curl_setopt($this->connections[$conn_type],
834 - CURLOPT_INFILESIZE, $obj->content_length);
835 - }
836 - }
837 - $return_code = $this->_send_request($conn_type,$url_path,$hdrs);
838 -
839 - if (!$return_code) {
840 - $this->error_str .= ": Failed to obtain valid HTTP response.";
841 - return array(0,$this->error_str,NULL);
842 - }
843 - if ($return_code == 412) {
844 - $this->error_str = "Missing Content-Type header";
845 - return array($return_code,$this->error_str,NULL);
846 - }
847 - if ($return_code == 422) {
848 - $this->error_str = "Derived and computed checksums do not match.";
849 - return array($return_code,$this->error_str,NULL);
850 - }
851 - if ($return_code != 201) {
852 - $this->error_str = "Unexpected HTTP return code: $return_code";
853 - return array($return_code,$this->error_str,NULL);
854 - }
855 - return array($return_code,$this->response_reason,$this->_obj_etag);
856 - }
857 -
858 - # POST /v1/Account/Container/Object
859 - #
860 - function update_object(&$obj)
861 - {
862 - if (!is_object($obj) || get_class($obj) != "CF_Object") {
863 - throw new SyntaxException(
864 - "Method argument is not a valid CF_Object.");
865 - }
866 -
867 - if (!is_array($obj->metadata) && !$obj->manifest) {
868 -
869 - $this->error_str = "Metadata array is empty.";
870 - return 0;
871 - }
872 -
873 - $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name);
874 -
875 - $hdrs = $this->_metadata_headers($obj);
876 - $return_code = $this->_send_request("DEL_POST",$url_path,$hdrs,"POST");
877 - if (!$return_code) {
878 - $this->error_str .= ": Failed to obtain valid HTTP response.";
879 - return 0;
880 - }
881 - if ($return_code == 404) {
882 - $this->error_str = "Account, Container, or Object not found.";
883 - }
884 - if ($return_code != 202) {
885 - $this->error_str = "Unexpected HTTP return code: $return_code";
886 - }
887 - return $return_code;
888 - }
889 -
890 - # HEAD /v1/Account/Container/Object
891 - #
892 - function head_object(&$obj)
893 - {
894 - if (!is_object($obj) || get_class($obj) != "CF_Object") {
895 - throw new SyntaxException(
896 - "Method argument is not a valid CF_Object.");
897 - }
898 -
899 - $conn_type = "HEAD";
900 -
901 - $url_path = $this->_make_path("STORAGE", $obj->container->name,$obj->name);
902 - $return_code = $this->_send_request($conn_type,$url_path);
903 -
904 - if (!$return_code) {
905 - $this->error_str .= ": Failed to obtain valid HTTP response.";
906 - return array(0, $this->error_str." ".$this->response_reason,
907 - NULL, NULL, NULL, NULL, array());
908 - }
909 -
910 - if ($return_code == 404) {
911 - return array($return_code, $this->response_reason,
912 - NULL, NULL, NULL, NULL, array());
913 - }
914 - if ($return_code == 204 || $return_code == 200) {
915 - return array($return_code,$this->response_reason,
916 - $this->_obj_etag,
917 - $this->_obj_last_modified,
918 - $this->_obj_content_type,
919 - $this->_obj_content_length,
920 - $this->_obj_metadata,
921 - $this->_obj_manifest);
922 - }
923 - $this->error_str = "Unexpected HTTP return code: $return_code";
924 - return array($return_code, $this->error_str." ".$this->response_reason,
925 - NULL, NULL, NULL, NULL, array());
926 - }
927 -
928 - # DELETE /v1/Account/Container/Object
929 - #
930 - function delete_object($container_name, $object_name)
931 - {
932 - if ($container_name == "") {
933 - $this->error_str = "Container name not set.";
934 - return 0;
935 - }
936 -
937 - if ($container_name != "0" and !isset($container_name)) {
938 - $this->error_str = "Container name not set.";
939 - return 0;
940 - }
941 -
942 - if (!$object_name) {
943 - $this->error_str = "Object name not set.";
944 - return 0;
945 - }
946 -
947 - $url_path = $this->_make_path("STORAGE", $container_name,$object_name);
948 - $return_code = $this->_send_request("DEL_POST",$url_path,NULL,"DELETE");
949 - if (!$return_code) {
950 - $this->error_str .= ": Failed to obtain valid HTTP response.";
951 - return 0;
952 - }
953 - if ($return_code == 404) {
954 - $this->error_str = "Specified container did not exist to delete.";
955 - }
956 - if ($return_code != 204) {
957 - $this->error_str = "Unexpected HTTP return code: $return_code.";
958 - }
959 - return $return_code;
960 - }
961 -
962 - function get_error()
963 - {
964 - return $this->error_str;
965 - }
966 -
967 - function setDebug($bool)
968 - {
969 - $this->dbug = $bool;
970 - foreach ($this->connections as $k => $v) {
971 - if (!is_null($v)) {
972 - curl_setopt($this->connections[$k], CURLOPT_VERBOSE, $this->dbug);
973 - }
974 - }
975 - }
976 -
977 - function getCDNMUrl()
978 - {
979 - return $this->cdnm_url;
980 - }
981 -
982 - function getStorageUrl()
983 - {
984 - return $this->storage_url;
985 - }
986 -
987 - function getAuthToken()
988 - {
989 - return $this->auth_token;
990 - }
991 -
992 - function setCFAuth($cfs_auth, $servicenet=False)
993 - {
994 - if ($servicenet) {
995 - $this->storage_url = "https://snet-" . substr($cfs_auth->storage_url, 8);
996 - } else {
997 - $this->storage_url = $cfs_auth->storage_url;
998 - }
999 - $this->auth_token = $cfs_auth->auth_token;
1000 - $this->cdnm_url = $cfs_auth->cdnm_url;
1001 - }
1002 -
1003 - function setReadProgressFunc($func_name)
1004 - {
1005 - $this->_user_read_progress_callback_func = $func_name;
1006 - }
1007 -
1008 - function setWriteProgressFunc($func_name)
1009 - {
1010 - $this->_user_write_progress_callback_func = $func_name;
1011 - }
1012 -
1013 - private function _header_cb($ch, $header)
1014 - {
1015 - preg_match("/^HTTP\/1\.[01] (\d{3}) (.*)/", $header, $matches);
1016 - if (isset($matches[1])) {
1017 - $this->response_status = $matches[1];
1018 - }
1019 - if (isset($matches[2])) {
1020 - $this->response_reason = $matches[2];
1021 - }
1022 - if (stripos($header, CDN_ENABLED) === 0) {
1023 - $val = trim(substr($header, strlen(CDN_ENABLED)+1));
1024 - if (strtolower($val) == "true") {
1025 - $this->_cdn_enabled = True;
1026 - } elseif (strtolower($val) == "false") {
1027 - $this->_cdn_enabled = False;
1028 - } else {
1029 - $this->_cdn_enabled = NULL;
1030 - }
1031 - return strlen($header);
1032 - }
1033 - if (stripos($header, CDN_URI) === 0) {
1034 - $this->_cdn_uri = trim(substr($header, strlen(CDN_URI)+1));
1035 - return strlen($header);
1036 - }
1037 - if (stripos($header, CDN_SSL_URI) === 0) {
1038 - $this->_cdn_ssl_uri = trim(substr($header, strlen(CDN_SSL_URI)+1));
1039 - return strlen($header);
1040 - }
1041 - if (stripos($header, CDN_TTL) === 0) {
1042 - $this->_cdn_ttl = trim(substr($header, strlen(CDN_TTL)+1))+0;
1043 - return strlen($header);
1044 - }
1045 - if (stripos($header, MANIFEST_HEADER) === 0) {
1046 - $this->_obj_manifest = trim(substr($header, strlen(MANIFEST_HEADER)+1));
1047 - return strlen($header);
1048 - }
1049 - if (stripos($header, CDN_LOG_RETENTION) === 0) {
1050 - $this->_cdn_log_retention =
1051 - trim(substr($header, strlen(CDN_LOG_RETENTION)+1)) == "True" ? True : False;
1052 - return strlen($header);
1053 - }
1054 -
1055 - if (stripos($header, CDN_ACL_USER_AGENT) === 0) {
1056 - $this->_cdn_acl_user_agent =
1057 - trim(substr($header, strlen(CDN_ACL_USER_AGENT)+1));
1058 - return strlen($header);
1059 - }
1060 -
1061 - if (stripos($header, CDN_ACL_REFERRER) === 0) {
1062 - $this->_cdn_acl_referrer =
1063 - trim(substr($header, strlen(CDN_ACL_REFERRER)+1));
1064 - return strlen($header);
1065 - }
1066 -
1067 - if (stripos($header, ACCOUNT_CONTAINER_COUNT) === 0) {
1068 - $this->_account_container_count = (float) trim(substr($header,
1069 - strlen(ACCOUNT_CONTAINER_COUNT)+1))+0;
1070 - return strlen($header);
1071 - }
1072 - if (stripos($header, ACCOUNT_BYTES_USED) === 0) {
1073 - $this->_account_bytes_used = (float) trim(substr($header,
1074 - strlen(ACCOUNT_BYTES_USED)+1))+0;
1075 - return strlen($header);
1076 - }
1077 - if (stripos($header, CONTAINER_OBJ_COUNT) === 0) {
1078 - $this->_container_object_count = (float) trim(substr($header,
1079 - strlen(CONTAINER_OBJ_COUNT)+1))+0;
1080 - return strlen($header);
1081 - }
1082 - if (stripos($header, CONTAINER_BYTES_USED) === 0) {
1083 - $this->_container_bytes_used = (float) trim(substr($header,
1084 - strlen(CONTAINER_BYTES_USED)+1))+0;
1085 - return strlen($header);
1086 - }
1087 - if (stripos($header, METADATA_HEADER) === 0) {
1088 - # $header => X-Object-Meta-Foo: bar baz
1089 - $temp = substr($header, strlen(METADATA_HEADER));
1090 - # $temp => Foo: bar baz
1091 - $parts = explode(":", $temp);
1092 - # $parts[0] => Foo
1093 - $val = substr(strstr($temp, ":"), 1);
1094 - # $val => bar baz
1095 - $this->_obj_metadata[$parts[0]] = trim($val);
1096 - return strlen($header);
1097 - }
1098 - if (stripos($header, "ETag:") === 0) {
1099 - # $header => ETag: abc123def456...
1100 - $val = substr(strstr($header, ":"), 1);
1101 - # $val => abc123def456...
1102 - $this->_obj_etag = trim($val);
1103 - return strlen($header);
1104 - }
1105 - if (stripos($header, "Last-Modified:") === 0) {
1106 - $val = substr(strstr($header, ":"), 1);
1107 - $this->_obj_last_modified = trim($val);
1108 - return strlen($header);
1109 - }
1110 - if (stripos($header, "Content-Type:") === 0) {
1111 - $val = substr(strstr($header, ":"), 1);
1112 - $this->_obj_content_type = trim($val);
1113 - return strlen($header);
1114 - }
1115 - if (stripos($header, "Content-Length:") === 0) {
1116 - $val = substr(strstr($header, ":"), 1);
1117 - $this->_obj_content_length = (float) trim($val)+0;
1118 - return strlen($header);
1119 - }
1120 - return strlen($header);
1121 - }
1122 -
1123 - private function _read_cb($ch, $fd, $length)
1124 - {
1125 - $data = fread($fd, $length);
1126 - $len = strlen($data);
1127 - if (isset($this->_user_write_progress_callback_func)) {
1128 - call_user_func($this->_user_write_progress_callback_func, $len);
1129 - }
1130 - return $data;
1131 - }
1132 -
1133 - private function _write_cb($ch, $data)
1134 - {
1135 - $dlen = strlen($data);
1136 - switch ($this->_write_callback_type) {
1137 - case "TEXT_LIST":
1138 - $this->_return_list = $this->_return_list . $data;
1139 - //= explode("\n",$data); # keep tab,space
1140 - //his->_text_list[] = rtrim($data,"\n\r\x0B"); # keep tab,space
1141 - break;
1142 - case "OBJECT_STREAM":
1143 - fwrite($this->_obj_write_resource, $data, $dlen);
1144 - break;
1145 - case "OBJECT_STRING":
1146 - $this->_obj_write_string .= $data;
1147 - break;
1148 - }
1149 - if (isset($this->_user_read_progress_callback_func)) {
1150 - call_user_func($this->_user_read_progress_callback_func, $dlen);
1151 - }
1152 - return $dlen;
1153 - }
1154 -
1155 - private function _auth_hdr_cb($ch, $header)
1156 - {
1157 - preg_match("/^HTTP\/1\.[01] (\d{3}) (.*)/", $header, $matches);
1158 - if (isset($matches[1])) {
1159 - $this->response_status = $matches[1];
1160 - }
1161 - if (isset($matches[2])) {
1162 - $this->response_reason = $matches[2];
1163 - }
1164 - if (stripos($header, STORAGE_URL) === 0) {
1165 - $this->storage_url = trim(substr($header, strlen(STORAGE_URL)+1));
1166 - }
1167 - if (stripos($header, CDNM_URL) === 0) {
1168 - $this->cdnm_url = trim(substr($header, strlen(CDNM_URL)+1));
1169 - }
1170 - if (stripos($header, AUTH_TOKEN) === 0) {
1171 - $this->auth_token = trim(substr($header, strlen(AUTH_TOKEN)+1));
1172 - }
1173 - if (stripos($header, AUTH_TOKEN_LEGACY) === 0) {
1174 - $this->auth_token = trim(substr($header,strlen(AUTH_TOKEN_LEGACY)+1));
1175 - }
1176 - return strlen($header);
1177 - }
1178 -
1179 - private function _make_headers($hdrs=NULL)
1180 - {
1181 - $new_headers = array();
1182 - $has_stoken = False;
1183 - $has_uagent = False;
1184 - if (is_array($hdrs)) {
1185 - foreach ($hdrs as $h => $v) {
1186 - if (is_int($h)) {
1187 - $parts = explode(":", $v);
1188 - $header = $parts[0];
1189 - $value = trim(substr(strstr($v, ":"), 1));
1190 - } else {
1191 - $header = $h;
1192 - $value = trim($v);
1193 - }
1194 -
1195 - if (stripos($header, AUTH_TOKEN) === 0) {
1196 - $has_stoken = True;
1197 - }
1198 - if (stripos($header, "user-agent") === 0) {
1199 - $has_uagent = True;
1200 - }
1201 - $new_headers[] = $header . ": " . $value;
1202 - }
1203 - }
1204 - if (!$has_stoken) {
1205 - $new_headers[] = AUTH_TOKEN . ": " . $this->auth_token;
1206 - }
1207 - if (!$has_uagent) {
1208 - $new_headers[] = "User-Agent: " . USER_AGENT;
1209 - }
1210 - return $new_headers;
1211 - }
1212 -
1213 - private function _init($conn_type, $force_new=False)
1214 - {
1215 - if (!array_key_exists($conn_type, $this->connections)) {
1216 - $this->error_str = "Invalid CURL_XXX connection type";
1217 - return False;
1218 - }
1219 -
1220 - if (is_null($this->connections[$conn_type]) || $force_new) {
1221 - $ch = curl_init();
1222 - } else {
1223 - return;
1224 - }
1225 -
1226 - if ($this->dbug) { curl_setopt($ch, CURLOPT_VERBOSE, 1); }
1227 -
1228 - if (!is_null($this->cabundle_path)) {
1229 - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, True);
1230 - curl_setopt($ch, CURLOPT_CAINFO, $this->cabundle_path);
1231 - }
1232 - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, False);
1233 - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
1234 - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
1235 - curl_setopt($ch, CURLOPT_MAXREDIRS, 4);
1236 - curl_setopt($ch, CURLOPT_HEADER, 0);
1237 - curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this, '_header_cb'));
1238 -
1239 - if ($conn_type == "GET_CALL") {
1240 - curl_setopt($ch, CURLOPT_WRITEFUNCTION, array(&$this, '_write_cb'));
1241 - }
1242 -
1243 - if ($conn_type == "PUT_OBJ") {
1244 - curl_setopt($ch, CURLOPT_PUT, 1);
1245 - curl_setopt($ch, CURLOPT_READFUNCTION, array(&$this, '_read_cb'));
1246 - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1247 - }
1248 - if ($conn_type == "HEAD") {
1249 - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "HEAD");
1250 - curl_setopt($ch, CURLOPT_NOBODY, 1);
1251 - }
1252 - if ($conn_type == "PUT_CONT") {
1253 - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1254 - curl_setopt($ch, CURLOPT_INFILESIZE, 0);
1255 - curl_setopt($ch, CURLOPT_NOBODY, 1);
1256 - }
1257 - if ($conn_type == "DEL_POST") {
1258 - curl_setopt($ch, CURLOPT_NOBODY, 1);
1259 - }
1260 - $this->connections[$conn_type] = $ch;
1261 - return;
1262 - }
1263 -
1264 - private function _reset_callback_vars()
1265 - {
1266 - $this->_text_list = array();
1267 - $this->_return_list = NULL;
1268 - $this->_account_container_count = 0;
1269 - $this->_account_bytes_used = 0;
1270 - $this->_container_object_count = 0;
1271 - $this->_container_bytes_used = 0;
1272 - $this->_obj_etag = NULL;
1273 - $this->_obj_last_modified = NULL;
1274 - $this->_obj_content_type = NULL;
1275 - $this->_obj_content_length = NULL;
1276 - $this->_obj_metadata = array();
1277 - $this->_obj_manifest = NULL;
1278 - $this->_obj_write_string = "";
1279 - $this->_cdn_enabled = NULL;
1280 - $this->_cdn_ssl_uri = NULL;
1281 - $this->_cdn_uri = NULL;
1282 - $this->_cdn_ttl = NULL;
1283 - $this->response_status = 0;
1284 - $this->response_reason = "";
1285 - }
1286 -
1287 - private function _make_path($t="STORAGE",$c=NULL,$o=NULL)
1288 - {
1289 - $path = array();
1290 - switch ($t) {
1291 - case "STORAGE":
1292 - $path[] = $this->storage_url; break;
1293 - case "CDN":
1294 - $path[] = $this->cdnm_url; break;
1295 - }
1296 - if ($c == "0")
1297 - $path[] = rawurlencode($c);
1298 -
1299 - if ($c) {
1300 - $path[] = rawurlencode($c);
1301 - }
1302 - if ($o) {
1303 - # mimic Python''s urllib.quote() feature of a "safe" '/' character
1304 - #
1305 - $path[] = str_replace("%2F","/",rawurlencode($o));
1306 - }
1307 - return implode("/",$path);
1308 - }
1309 -
1310 - private function _metadata_headers(&$obj)
1311 - {
1312 - $hdrs = array();
1313 - if ($obj->manifest)
1314 - $hdrs[MANIFEST_HEADER] = $obj->manifest;
1315 - foreach ($obj->metadata as $k => $v) {
1316 - if (strpos($k,":") !== False) {
1317 - throw new SyntaxException(
1318 - "Metadata keys cannot contain a ':' character.");
1319 - }
1320 - $k = trim($k);
1321 - $key = sprintf("%s%s", METADATA_HEADER, $k);
1322 - if (!array_key_exists($key, $hdrs)) {
1323 - if (strlen($k) > 128 || strlen($v) > 256) {
1324 - $this->error_str = "Metadata key or value exceeds ";
1325 - $this->error_str .= "maximum length: ($k: $v)";
1326 - return 0;
1327 - }
1328 - $hdrs[] = sprintf("%s%s: %s", METADATA_HEADER, $k, trim($v));
1329 - }
1330 - }
1331 - return $hdrs;
1332 - }
1333 -
1334 - private function _send_request($conn_type, $url_path, $hdrs=NULL, $method="GET", $force_new=False)
1335 - {
1336 - $this->_init($conn_type, $force_new);
1337 - $this->_reset_callback_vars();
1338 - $headers = $this->_make_headers($hdrs);
1339 -
1340 - if (gettype($this->connections[$conn_type]) == "unknown type")
1341 - throw new ConnectionNotOpenException (
1342 - "Connection is not open."
1343 - );
1344 -
1345 - switch ($method) {
1346 - case "DELETE":
1347 - curl_setopt($this->connections[$conn_type],
1348 - CURLOPT_CUSTOMREQUEST, "DELETE");
1349 - break;
1350 - case "POST":
1351 - curl_setopt($this->connections[$conn_type],
1352 - CURLOPT_CUSTOMREQUEST, "POST");
1353 - default:
1354 - break;
1355 - }
1356 -
1357 - curl_setopt($this->connections[$conn_type],
1358 - CURLOPT_HTTPHEADER, $headers);
1359 -
1360 - curl_setopt($this->connections[$conn_type],
1361 - CURLOPT_URL, $url_path);
1362 -
1363 - if (!curl_exec($this->connections[$conn_type]) && curl_errno($this->connections[$conn_type]) !== 0) {
1364 - $this->error_str = "(curl error: "
1365 - . curl_errno($this->connections[$conn_type]) . ") ";
1366 - $this->error_str .= curl_error($this->connections[$conn_type]);
1367 - return False;
1368 - }
1369 - return curl_getinfo($this->connections[$conn_type], CURLINFO_HTTP_CODE);
1370 - }
1371 -
1372 - function close()
1373 - {
1374 - foreach ($this->connections as $cnx) {
1375 - if (isset($cnx)) {
1376 - curl_close($cnx);
1377 - $this->connections[$cnx] = NULL;
1378 - }
1379 - }
1380 - }
1381 - private function create_array()
1382 - {
1383 - $this->_text_list = explode("\n",rtrim($this->_return_list,"\n\x0B"));
1384 - return True;
1385 - }
1386 -
1387 -}
1388 -
1389 -/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
1390 -
1391 -/*
1392 - * Local variables:
1393 - * tab-width: 4
1394 - * c-basic-offset: 4
1395 - * c-hanging-comment-ender-p: nil
1396 - * End:
1397 - */
1398 -?>

Comments

#Comment by Aaron Schulz (talk | contribs)   06:26, 29 October 2011

Also changes swiftcopy().

Where are the new cloudfiles?

#Comment by RussNelson (talk | contribs)   01:41, 30 October 2011

Yes, the change to swiftcopy is what allows us to use the standard php-cloudfiles. At the time I got started, php-cloudfiles had no support for store-to-store copies, although Swift had support.

AFAIK, we will not have any need for our own copy, so I removed them from SVN. https://github.com/rackspace/php-cloudfiles is the original source.

Status & tagging log