PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.16.5
UpdraftPlus: WP Backup & Migration Plugin v1.16.5
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / includes / cloudfiles / cloudfiles.php

cloudfiles.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at includes/cloudfiles/cloudfiles.php

2,633 lines 90.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This is the PHP Cloud Files API.
4 *
5 * <code>
6 * # Authenticate to Cloud Files. The default is to automatically try
7 * # to re-authenticate if an authentication token expires.
8 * #
9 * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
10 * # file. This API ships with a newer version obtained directly from
11 * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
12 * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
13 * #
14 * $auth = new CF_Authentication($username, $api_key);
15 * # $auth->ssl_use_cabundle(); # bypass cURL's old CA bundle
16 * $auth->authenticate();
17 *
18 * # Establish a connection to the storage system
19 * #
20 * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
21 * # file. This API ships with a newer version obtained directly from
22 * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
23 * # call the CF_Connection instance's 'ssl_use_cabundle()' method.
24 * #
25 * $conn = new CF_Connection($auth);
26 * # $conn->ssl_use_cabundle(); # bypass cURL's old CA bundle
27 *
28 * # Create a remote Container and storage Object
29 * #
30 * $images = $conn->create_container("photos");
31 * $bday = $images->create_object("first_birthday.jpg");
32 *
33 * # Upload content from a local file by streaming it. Note that we use
34 * # a "float" for the file size to overcome PHP's 32-bit integer limit for
35 * # very large files.
36 * #
37 * $fname = "/home/user/photos/birthdays/birthday1.jpg"; # filename to upload
38 * $size = (float) sprintf("%u", filesize($fname));
39 * $fp = open($fname, "r");
40 * $bday->write($fp, $size);
41 *
42 * # Or... use a convenience function instead
43 * #
44 * $bday->load_from_filename("/home/user/photos/birthdays/birthday1.jpg");
45 *
46 * # Now, publish the "photos" container to serve the images by CDN.
47 * # Use the "$uri" value to put in your web pages or send the link in an
48 * # email message, etc.
49 * #
50 * $uri = $images->make_public();
51 *
52 * # Or... print out the Object's public URI
53 * #
54 * print $bday->public_uri();
55 * </code>
56 *
57 * See the included tests directory for additional sample code.
58 *
59 * Requres PHP 5.x (for Exceptions and OO syntax) and PHP's cURL module.
60 *
61 * It uses the supporting "cloudfiles_http.php" module for HTTP(s) support and
62 * allows for connection re-use and streaming of content into/out of Cloud Files
63 * via PHP's cURL module.
64 *
65 * See COPYING for license information.
66 *
67 * @author Eric "EJ" Johnson <ej@racklabs.com>
68 * @copyright Copyright (c) 2008, Rackspace US, Inc.
69 * @package php-cloudfiles
70 */
71
72 /**
73 */
74 require_once(UPDRAFTPLUS_DIR."/includes/cloudfiles/cloudfiles_exceptions.php");
75 require_once(UPDRAFTPLUS_DIR."/includes/cloudfiles/cloudfiles_http.php");
76 @define("DEFAULT_CF_API_VERSION", 1);
77 @define("MAX_CONTAINER_NAME_LEN", 256);
78 @define("MAX_OBJECT_NAME_LEN", 1024);
79 @define("MAX_OBJECT_SIZE", 5*1024*1024*1024+1);
80 @define("US_AUTHURL", "https://auth.api.rackspacecloud.com");
81 @define("UK_AUTHURL", "https://lon.auth.api.rackspacecloud.com");
82 /**
83 * Class for handling Cloud Files Authentication, call it's {@link authenticate()}
84 * method to obtain authorized service urls and an authentication token.
85 *
86 * Example:
87 * <code>
88 * # Create the authentication instance
89 * #
90 * $auth = new CF_Authentication("username", "api_key");
91 *
92 * # NOTE: For UK Customers please specify your AuthURL Manually
93 * # There is a Predfined constant to use EX:
94 * #
95 * # $auth = new CF_Authentication("username, "api_key", NULL, UK_AUTHURL);
96 * # Using the UK_AUTHURL keyword will force the api to use the UK AuthUrl.
97 * # rather then the US one. The NULL Is passed for legacy purposes and must
98 * # be passed to function correctly.
99 *
100 * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
101 * # file. This API ships with a newer version obtained directly from
102 * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
103 * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
104 * #
105 * # $auth->ssl_use_cabundle(); # bypass cURL's old CA bundle
106 *
107 * # Perform authentication request
108 * #
109 * $auth->authenticate();
110 * </code>
111 *
112 * @package php-cloudfiles
113 */
114 class UpdraftPlus_CF_Authentication
115 {
116 public $dbug;
117 public $username;
118 public $api_key;
119 public $auth_host;
120 public $account;
121
122 /**
123 * Instance variables that are set after successful authentication
124 */
125 public $storage_url;
126 public $cdnm_url;
127 public $auth_token;
128
129 /**
130 * Class constructor (PHP 5 syntax)
131 *
132 * @param string $username Mosso username
133 * @param string $api_key Mosso API Access Key
134 * @param string $account <i>Account name</i>
135 * @param string $auth_host <i>Authentication service URI</i>
136 */
137 function __construct($username=NULL, $api_key=NULL, $account=NULL, $auth_host=US_AUTHURL)
138 {
139
140 $this->dbug = False;
141 $this->username = $username;
142 $this->api_key = $api_key;
143 $this->account_name = $account;
144 $this->auth_host = $auth_host;
145
146 $this->storage_url = NULL;
147 $this->cdnm_url = NULL;
148 $this->auth_token = NULL;
149
150 $this->cfs_http = new UpdraftPlus_CF_Http(DEFAULT_CF_API_VERSION);
151 }
152
153 /**
154 * Use the Certificate Authority bundle included with this API
155 *
156 * Most versions of PHP with cURL support include an outdated Certificate
157 * Authority (CA) bundle (the file that lists all valid certificate
158 * signing authorities). The SSL certificates used by the Cloud Files
159 * storage system are perfectly valid but have been created/signed by
160 * a CA not listed in these outdated cURL distributions.
161 *
162 * As a work-around, we've included an updated CA bundle obtained
163 * directly from cURL's web site (http://curl.haxx.se). You can direct
164 * the API to use this CA bundle by calling this method prior to making
165 * any remote calls. The best place to use this method is right after
166 * the CF_Authentication instance has been instantiated.
167 *
168 * You can specify your own CA bundle by passing in the full pathname
169 * to the bundle. You can use the included CA bundle by leaving the
170 * argument blank.
171 *
172 * @param string $path Specify path to CA bundle (default to included)
173 */
174 function ssl_use_cabundle($path=NULL)
175 {
176 $this->cfs_http->ssl_use_cabundle($path);
177 }
178
179 /**
180 * Attempt to validate Username/API Access Key
181 *
182 * Attempts to validate credentials with the authentication service. It
183 * either returns <kbd>True</kbd> or throws an Exception. Accepts a single
184 * (optional) argument for the storage system API version.
185 *
186 * Example:
187 * <code>
188 * # Create the authentication instance
189 * #
190 * $auth = new CF_Authentication("username", "api_key");
191 *
192 * # Perform authentication request
193 * #
194 * $auth->authenticate();
195 * </code>
196 *
197 * @param string $version API version for Auth service (optional)
198 * @return boolean <kbd>True</kbd> if successfully authenticated
199 * @throws AuthenticationException invalid credentials
200 * @throws InvalidResponseException invalid response
201 */
202 function authenticate($version=DEFAULT_CF_API_VERSION)
203 {
204 list($status,$reason,$surl,$curl,$atoken) =
205 $this->cfs_http->authenticate($this->username, $this->api_key,
206 $this->account_name, $this->auth_host);
207
208 if ($status == 401) {
209 throw new AuthenticationException("Invalid username or access key.");
210 }
211 if ($status < 200 || $status > 299) {
212 throw new InvalidResponseException(
213 "Unexpected response (".$status."): ".$reason);
214 }
215
216 if (!($surl || $curl) || !$atoken) {
217 throw new InvalidResponseException(
218 "Expected headers missing from auth service.");
219 }
220 $this->storage_url = $surl;
221 $this->cdnm_url = $curl;
222 $this->auth_token = $atoken;
223 return True;
224 }
225 /**
226 * Use Cached Token and Storage URL's rather then grabbing from the Auth System
227 *
228 * Example:
229 * <code>
230 * #Create an Auth instance
231 * $auth = new CF_Authentication();
232 * #Pass Cached URL's and Token as Args
233 * $auth->load_cached_credentials("auth_token", "storage_url", "cdn_management_url");
234 * </code>
235 *
236 * @param string $auth_token A Cloud Files Auth Token (Required)
237 * @param string $storage_url The Cloud Files Storage URL (Required)
238 * @param string $cdnm_url CDN Management URL (Required)
239 * @return boolean <kbd>True</kbd> if successful
240 * @throws SyntaxException If any of the Required Arguments are missing
241 */
242 function load_cached_credentials($auth_token, $storage_url, $cdnm_url)
243 {
244 if(!$storage_url || !$cdnm_url)
245 {
246 throw new SyntaxException("Missing Required Interface URL's!");
247 return False;
248 }
249 if(!$auth_token)
250 {
251 throw new SyntaxException("Missing Auth Token!");
252 return False;
253 }
254
255 $this->storage_url = $storage_url;
256 $this->cdnm_url = $cdnm_url;
257 $this->auth_token = $auth_token;
258 return True;
259 }
260 /**
261 * Grab Cloud Files info to be Cached for later use with the load_cached_credentials method.
262 *
263 * Example:
264 * <code>
265 * #Create an Auth instance
266 * $auth = new CF_Authentication("UserName","API_Key");
267 * $auth->authenticate();
268 * $array = $auth->export_credentials();
269 * </code>
270 *
271 * @return array of url's and an auth token.
272 */
273 function export_credentials()
274 {
275 $arr = array();
276 $arr['storage_url'] = $this->storage_url;
277 $arr['cdnm_url'] = $this->cdnm_url;
278 $arr['auth_token'] = $this->auth_token;
279
280 return $arr;
281 }
282
283
284 /**
285 * Make sure the CF_Authentication instance has authenticated.
286 *
287 * Ensures that the instance variables necessary to communicate with
288 * Cloud Files have been set from a previous authenticate() call.
289 *
290 * @return boolean <kbd>True</kbd> if successfully authenticated
291 */
292 function authenticated()
293 {
294 if (!($this->storage_url || $this->cdnm_url) || !$this->auth_token) {
295 return False;
296 }
297 return True;
298 }
299
300 /**
301 * Toggle debugging - set cURL verbose flag
302 */
303 function setDebug($bool)
304 {
305 $this->dbug = $bool;
306 $this->cfs_http->setDebug($bool);
307 }
308 }
309
310 /**
311 * Class for establishing connections to the Cloud Files storage system.
312 * Connection instances are used to communicate with the storage system at
313 * the account level; listing and deleting Containers and returning Container
314 * instances.
315 *
316 * Example:
317 * <code>
318 * # Create the authentication instance
319 * #
320 * $auth = new CF_Authentication("username", "api_key");
321 *
322 * # Perform authentication request
323 * #
324 * $auth->authenticate();
325 *
326 * # Create a connection to the storage/cdn system(s) and pass in the
327 * # validated CF_Authentication instance.
328 * #
329 * $conn = new CF_Connection($auth);
330 *
331 * # NOTE: Some versions of cURL include an outdated certificate authority (CA)
332 * # file. This API ships with a newer version obtained directly from
333 * # cURL's web site (http://curl.haxx.se). To use the newer CA bundle,
334 * # call the CF_Authentication instance's 'ssl_use_cabundle()' method.
335 * #
336 * # $conn->ssl_use_cabundle(); # bypass cURL's old CA bundle
337 * </code>
338 *
339 * @package php-cloudfiles
340 */
341 class UpdraftPlus_CF_Connection
342 {
343 public $dbug;
344 public $cfs_http;
345 public $cfs_auth;
346
347 /**
348 * Pass in a previously authenticated CF_Authentication instance.
349 *
350 * Example:
351 * <code>
352 * # Create the authentication instance
353 * #
354 * $auth = new CF_Authentication("username", "api_key");
355 *
356 * # Perform authentication request
357 * #
358 * $auth->authenticate();
359 *
360 * # Create a connection to the storage/cdn system(s) and pass in the
361 * # validated CF_Authentication instance.
362 * #
363 * $conn = new CF_Connection($auth);
364 *
365 * # If you are connecting via Rackspace servers and have access
366 * # to the servicenet network you can set the $servicenet to True
367 * # like this.
368 *
369 * $conn = new CF_Connection($auth, $servicenet=True);
370 *
371 * </code>
372 *
373 * If the environement variable RACKSPACE_SERVICENET is defined it will
374 * force to connect via the servicenet.
375 *
376 * @param obj $cfs_auth previously authenticated CF_Authentication instance
377 * @param boolean $servicenet enable/disable access via Rackspace servicenet.
378 * @throws AuthenticationException not authenticated
379 */
380 function __construct($cfs_auth, $servicenet=False)
381 {
382 if (isset($_ENV['RACKSPACE_SERVICENET']))
383 $servicenet=True;
384 $this->cfs_http = new UpdraftPlus_CF_Http(DEFAULT_CF_API_VERSION);
385 $this->cfs_auth = $cfs_auth;
386 if (!$this->cfs_auth->authenticated()) {
387 $e = "Need to pass in a previously authenticated ";
388 $e .= "CF_Authentication instance.";
389 throw new AuthenticationException($e);
390 }
391 $this->cfs_http->setCFAuth($this->cfs_auth, $servicenet=$servicenet);
392 $this->dbug = False;
393 }
394
395 /**
396 * Toggle debugging of instance and back-end HTTP module
397 *
398 * @param boolean $bool enable/disable cURL debugging
399 */
400 function setDebug($bool)
401 {
402 $this->dbug = (boolean) $bool;
403 $this->cfs_http->setDebug($this->dbug);
404 }
405
406 /**
407 * Close a connection
408 *
409 * Example:
410 * <code>
411 *
412 * $conn->close();
413 *
414 * </code>
415 *
416 * Will close all current cUrl active connections.
417 *
418 */
419 public function close()
420 {
421 $this->cfs_http->close();
422 }
423
424 /**
425 * Cloud Files account information
426 *
427 * Return an array of two floats (since PHP only supports 32-bit integers);
428 * number of containers on the account and total bytes used for the account.
429 *
430 * Example:
431 * <code>
432 * # ... authentication code excluded (see previous examples) ...
433 * #
434 * $conn = new CF_Connection($auth);
435 *
436 * list($quantity, $bytes) = $conn->get_info();
437 * print "Number of containers: " . $quantity . "\n";
438 * print "Bytes stored in container: " . $bytes . "\n";
439 * </code>
440 *
441 * @return array (number of containers, total bytes stored)
442 * @throws InvalidResponseException unexpected response
443 */
444 function get_info()
445 {
446 list($status, $reason, $container_count, $total_bytes) =
447 $this->cfs_http->head_account();
448 #if ($status == 401 && $this->_re_auth()) {
449 # return $this->get_info();
450 #}
451 if ($status < 200 || $status > 299) {
452 throw new InvalidResponseException(
453 "Invalid response (".$status."): ".$this->cfs_http->get_error());
454 }
455 return array($container_count, $total_bytes);
456 }
457
458 /**
459 * Create a Container
460 *
461 * Given a Container name, return a Container instance, creating a new
462 * remote Container if it does not exit.
463 *
464 * Example:
465 * <code>
466 * # ... authentication code excluded (see previous examples) ...
467 * #
468 * $conn = new CF_Connection($auth);
469 *
470 * $images = $conn->create_container("my photos");
471 * </code>
472 *
473 * @param string $container_name container name
474 * @return CF_Container
475 * @throws SyntaxException invalid name
476 * @throws InvalidResponseException unexpected response
477 */
478 function create_container($container_name=NULL)
479 {
480 if ($container_name != "0" and !isset($container_name))
481 throw new SyntaxException("Container name not set.");
482
483 if (!isset($container_name) or $container_name == "")
484 throw new SyntaxException("Container name not set.");
485
486 if (strpos($container_name, "/") !== False) {
487 $r = "Container name '".$container_name;
488 $r .= "' cannot contain a '/' character.";
489 throw new SyntaxException($r);
490 }
491 if (strlen($container_name) > MAX_CONTAINER_NAME_LEN) {
492 throw new SyntaxException(sprintf(
493 "Container name exeeds %d bytes.",
494 MAX_CONTAINER_NAME_LEN));
495 }
496
497 $return_code = $this->cfs_http->create_container($container_name);
498 if (!$return_code) {
499 throw new InvalidResponseException("Invalid response ("
500 . $return_code. "): " . $this->cfs_http->get_error());
501 }
502 #if ($status == 401 && $this->_re_auth()) {
503 # return $this->create_container($container_name);
504 #}
505 if ($return_code != 201 && $return_code != 202) {
506 throw new InvalidResponseException(
507 "Invalid response (".$return_code."): "
508 . $this->cfs_http->get_error());
509 }
510 return new UpdraftPlus_CF_Container($this->cfs_auth, $this->cfs_http, $container_name);
511 }
512
513 /**
514 * Delete a Container
515 *
516 * Given either a Container instance or name, remove the remote Container.
517 * The Container must be empty prior to removing it.
518 *
519 * Example:
520 * <code>
521 * # ... authentication code excluded (see previous examples) ...
522 * #
523 * $conn = new CF_Connection($auth);
524 *
525 * $conn->delete_container("my photos");
526 * </code>
527 *
528 * @param string|obj $container container name or instance
529 * @return boolean <kbd>True</kbd> if successfully deleted
530 * @throws SyntaxException missing proper argument
531 * @throws InvalidResponseException invalid response
532 * @throws NonEmptyContainerException container not empty
533 * @throws NoSuchContainerException remote container does not exist
534 */
535 function delete_container($container=NULL)
536 {
537 $container_name = NULL;
538
539 if (is_object($container)) {
540 if (get_class($container) == "UpdraftPlus_CF_Container") {
541 $container_name = $container->name;
542 }
543 }
544 if (is_string($container)) {
545 $container_name = $container;
546 }
547
548 if ($container_name != "0" and !isset($container_name))
549 throw new SyntaxException("Must specify container object or name.");
550
551 $return_code = $this->cfs_http->delete_container($container_name);
552
553 if (!$return_code) {
554 throw new InvalidResponseException("Failed to obtain http response");
555 }
556 #if ($status == 401 && $this->_re_auth()) {
557 # return $this->delete_container($container);
558 #}
559 if ($return_code == 409) {
560 throw new NonEmptyContainerException(
561 "Container must be empty prior to removing it.");
562 }
563 if ($return_code == 404) {
564 throw new NoSuchContainerException(
565 "Specified container did not exist to delete.");
566 }
567 if ($return_code != 204) {
568 throw new InvalidResponseException(
569 "Invalid response (".$return_code."): "
570 . $this->cfs_http->get_error());
571 }
572 return True;
573 }
574
575 /**
576 * Return a Container instance
577 *
578 * For the given name, return a Container instance if the remote Container
579 * exists, otherwise throw a Not Found exception.
580 *
581 * Example:
582 * <code>
583 * # ... authentication code excluded (see previous examples) ...
584 * #
585 * $conn = new CF_Connection($auth);
586 *
587 * $images = $conn->get_container("my photos");
588 * print "Number of Objects: " . $images->count . "\n";
589 * print "Bytes stored in container: " . $images->bytes . "\n";
590 * </code>
591 *
592 * @param string $container_name name of the remote Container
593 * @return container CF_Container instance
594 * @throws NoSuchContainerException thrown if no remote Container
595 * @throws InvalidResponseException unexpected response
596 */
597 function get_container($container_name=NULL)
598 {
599 list($status, $reason, $count, $bytes) =
600 $this->cfs_http->head_container($container_name);
601 #if ($status == 401 && $this->_re_auth()) {
602 # return $this->get_container($container_name);
603 #}
604 if ($status == 404) {
605 throw new NoSuchContainerException("Container not found.");
606 }
607 if ($status < 200 || $status > 299) {
608 throw new InvalidResponseException(
609 "Invalid response: ".$this->cfs_http->get_error());
610 }
611 return new UpdraftPlus_3CF_Container($this->cfs_auth, $this->cfs_http,
612 $container_name, $count, $bytes);
613 }
614
615 /**
616 * Return array of Container instances
617 *
618 * Return an array of CF_Container instances on the account. The instances
619 * will be fully populated with Container attributes (bytes stored and
620 * Object count)
621 *
622 * Example:
623 * <code>
624 * # ... authentication code excluded (see previous examples) ...
625 * #
626 * $conn = new CF_Connection($auth);
627 *
628 * $clist = $conn->get_containers();
629 * foreach ($clist as $cont) {
630 * print "Container name: " . $cont->name . "\n";
631 * print "Number of Objects: " . $cont->count . "\n";
632 * print "Bytes stored in container: " . $cont->bytes . "\n";
633 * }
634 * </code>
635 *
636 * @return array An array of CF_Container instances
637 * @throws InvalidResponseException unexpected response
638 */
639 function get_containers($limit=0, $marker=NULL)
640 {
641 list($status, $reason, $container_info) =
642 $this->cfs_http->list_containers_info($limit, $marker);
643 #if ($status == 401 && $this->_re_auth()) {
644 # return $this->get_containers();
645 #}
646 if ($status < 200 || $status > 299) {
647 throw new InvalidResponseException(
648 "Invalid response: ".$this->cfs_http->get_error());
649 }
650 $containers = array();
651 foreach ($container_info as $name => $info) {
652 $containers[] = new UpdraftPlus_CF_Container($this->cfs_auth, $this->cfs_http,
653 $info['name'], $info["count"], $info["bytes"], False);
654 }
655 return $containers;
656 }
657
658 /**
659 * Return list of remote Containers
660 *
661 * Return an array of strings containing the names of all remote Containers.
662 *
663 * Example:
664 * <code>
665 * # ... authentication code excluded (see previous examples) ...
666 * #
667 * $conn = new CF_Connection($auth);
668 *
669 * $container_list = $conn->list_containers();
670 * print_r($container_list);
671 * Array
672 * (
673 * [0] => "my photos",
674 * [1] => "my docs"
675 * )
676 * </code>
677 *
678 * @param integer $limit restrict results to $limit Containers
679 * @param string $marker return results greater than $marker
680 * @return array list of remote Containers
681 * @throws InvalidResponseException unexpected response
682 */
683 function list_containers($limit=0, $marker=NULL)
684 {
685 list($status, $reason, $containers) =
686 $this->cfs_http->list_containers($limit, $marker);
687 #if ($status == 401 && $this->_re_auth()) {
688 # return $this->list_containers($limit, $marker);
689 #}
690 if ($status < 200 || $status > 299) {
691 throw new InvalidResponseException(
692 "Invalid response (".$status."): ".$this->cfs_http->get_error());
693 }
694 return $containers;
695 }
696
697 /**
698 * Return array of information about remote Containers
699 *
700 * Return a nested array structure of Container info.
701 *
702 * Example:
703 * <code>
704 * # ... authentication code excluded (see previous examples) ...
705 * #
706 *
707 * $container_info = $conn->list_containers_info();
708 * print_r($container_info);
709 * Array
710 * (
711 * ["my photos"] =>
712 * Array
713 * (
714 * ["bytes"] => 78,
715 * ["count"] => 2
716 * )
717 * ["docs"] =>
718 * Array
719 * (
720 * ["bytes"] => 37323,
721 * ["count"] => 12
722 * )
723 * )
724 * </code>
725 *
726 * @param integer $limit restrict results to $limit Containers
727 * @param string $marker return results greater than $marker
728 * @return array nested array structure of Container info
729 * @throws InvalidResponseException unexpected response
730 */
731 function list_containers_info($limit=0, $marker=NULL)
732 {
733 list($status, $reason, $container_info) =
734 $this->cfs_http->list_containers_info($limit, $marker);
735 #if ($status == 401 && $this->_re_auth()) {
736 # return $this->list_containers_info($limit, $marker);
737 #}
738 if ($status < 200 || $status > 299) {
739 throw new InvalidResponseException(
740 "Invalid response (".$status."): ".$this->cfs_http->get_error());
741 }
742 return $container_info;
743 }
744
745 /**
746 * Return list of Containers that have been published to the CDN.
747 *
748 * Return an array of strings containing the names of published Containers.
749 * Note that this function returns the list of any Container that has
750 * ever been CDN-enabled regardless of it's existence in the storage
751 * system.
752 *
753 * Example:
754 * <code>
755 * # ... authentication code excluded (see previous examples) ...
756 * #
757 * $conn = new CF_Connection($auth);
758 *
759 * $public_containers = $conn->list_public_containers();
760 * print_r($public_containers);
761 * Array
762 * (
763 * [0] => "images",
764 * [1] => "css",
765 * [2] => "javascript"
766 * )
767 * </code>
768 *
769 * @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.
770 * @return array list of published Container names
771 * @throws InvalidResponseException unexpected response
772 */
773 function list_public_containers($enabled_only=False)
774 {
775 list($status, $reason, $containers) =
776 $this->cfs_http->list_cdn_containers($enabled_only);
777 #if ($status == 401 && $this->_re_auth()) {
778 # return $this->list_public_containers();
779 #}
780 if ($status < 200 || $status > 299) {
781 throw new InvalidResponseException(
782 "Invalid response (".$status."): ".$this->cfs_http->get_error());
783 }
784 return $containers;
785 }
786
787 /**
788 * Set a user-supplied callback function to report download progress
789 *
790 * The callback function is used to report incremental progress of a data
791 * download functions (e.g. $container->list_objects(), $obj->read(), etc).
792 * The specified function will be periodically called with the number of
793 * bytes transferred until the entire download is complete. This callback
794 * function can be useful for implementing "progress bars" for large
795 * downloads.
796 *
797 * The specified callback function should take a single integer parameter.
798 *
799 * <code>
800 * function read_callback($bytes_transferred) {
801 * print ">> downloaded " . $bytes_transferred . " bytes.\n";
802 * # ... do other things ...
803 * return;
804 * }
805 *
806 * $conn = new CF_Connection($auth_obj);
807 * $conn->set_read_progress_function("read_callback");
808 * print_r($conn->list_containers());
809 *
810 * # output would look like this:
811 * #
812 * >> downloaded 10 bytes.
813 * >> downloaded 11 bytes.
814 * Array
815 * (
816 * [0] => fuzzy.txt
817 * [1] => space name
818 * )
819 * </code>
820 *
821 * @param string $func_name the name of the user callback function
822 */
823 function set_read_progress_function($func_name)
824 {
825 $this->cfs_http->setReadProgressFunc($func_name);
826 }
827
828 /**
829 * Set a user-supplied callback function to report upload progress
830 *
831 * The callback function is used to report incremental progress of a data
832 * upload functions (e.g. $obj->write() call). The specified function will
833 * be periodically called with the number of bytes transferred until the
834 * entire upload is complete. This callback function can be useful
835 * for implementing "progress bars" for large uploads/downloads.
836 *
837 * The specified callback function should take a single integer parameter.
838 *
839 * <code>
840 * function write_callback($bytes_transferred) {
841 * print ">> uploaded " . $bytes_transferred . " bytes.\n";
842 * # ... do other things ...
843 * return;
844 * }
845 *
846 * $conn = new CF_Connection($auth_obj);
847 * $conn->set_write_progress_function("write_callback");
848 * $container = $conn->create_container("stuff");
849 * $obj = $container->create_object("foo");
850 * $obj->write("The callback function will be called during upload.");
851 *
852 * # output would look like this:
853 * # >> uploaded 51 bytes.
854 * #
855 * </code>
856 *
857 * @param string $func_name the name of the user callback function
858 */
859 function set_write_progress_function($func_name)
860 {
861 $this->cfs_http->setWriteProgressFunc($func_name);
862 }
863
864 /**
865 * Use the Certificate Authority bundle included with this API
866 *
867 * Most versions of PHP with cURL support include an outdated Certificate
868 * Authority (CA) bundle (the file that lists all valid certificate
869 * signing authorities). The SSL certificates used by the Cloud Files
870 * storage system are perfectly valid but have been created/signed by
871 * a CA not listed in these outdated cURL distributions.
872 *
873 * As a work-around, we've included an updated CA bundle obtained
874 * directly from cURL's web site (http://curl.haxx.se). You can direct
875 * the API to use this CA bundle by calling this method prior to making
876 * any remote calls. The best place to use this method is right after
877 * the CF_Authentication instance has been instantiated.
878 *
879 * You can specify your own CA bundle by passing in the full pathname
880 * to the bundle. You can use the included CA bundle by leaving the
881 * argument blank.
882 *
883 * @param string $path Specify path to CA bundle (default to included)
884 */
885 function ssl_use_cabundle($path=NULL)
886 {
887 $this->cfs_http->ssl_use_cabundle($path);
888 }
889
890 #private function _re_auth()
891 #{
892 # $new_auth = new CF_Authentication(
893 # $this->cfs_auth->username,
894 # $this->cfs_auth->api_key,
895 # $this->cfs_auth->auth_host,
896 # $this->cfs_auth->account);
897 # $new_auth->authenticate();
898 # $this->cfs_auth = $new_auth;
899 # $this->cfs_http->setCFAuth($this->cfs_auth);
900 # return True;
901 #}
902 }
903
904 /**
905 * Container operations
906 *
907 * Containers are storage compartments where you put your data (objects).
908 * A container is similar to a directory or folder on a conventional filesystem
909 * with the exception that they exist in a flat namespace, you can not create
910 * containers inside of containers.
911 *
912 * You also have the option of marking a Container as "public" so that the
913 * Objects stored in the Container are publicly available via the CDN.
914 *
915 * @package php-cloudfiles
916 */
917 class UpdraftPlus_CF_Container
918 {
919 public $cfs_auth;
920 public $cfs_http;
921 public $name;
922 public $object_count;
923 public $bytes_used;
924 public $metadata;
925 public $cdn_enabled;
926 public $cdn_streaming_uri;
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->metadata = array();
963 $this->cdn_enabled = NULL;
964 $this->cdn_uri = NULL;
965 $this->cdn_ssl_uri = NULL;
966 $this->cdn_streaming_uri = NULL;
967 $this->cdn_ttl = NULL;
968 $this->cdn_log_retention = NULL;
969 $this->cdn_acl_user_agent = NULL;
970 $this->cdn_acl_referrer = NULL;
971 if ($this->cfs_http->getCDNMUrl() != NULL && $docdn) {
972 $this->_cdn_initialize();
973 }
974 }
975
976 /**
977 * String representation of Container
978 *
979 * Pretty print the Container instance.
980 *
981 * @return string Container details
982 */
983 function __toString()
984 {
985 $me = sprintf("name: %s, count: %.0f, bytes: %.0f",
986 $this->name, $this->object_count, $this->bytes_used);
987 if ($this->cfs_http->getCDNMUrl() != NULL) {
988 $me .= sprintf(", cdn: %s, cdn uri: %s, cdn ttl: %.0f, logs retention: %s",
989 $this->is_public() ? "Yes" : "No",
990 $this->cdn_uri, $this->cdn_ttl,
991 $this->cdn_log_retention ? "Yes" : "No"
992 );
993
994 if ($this->cdn_acl_user_agent != NULL) {
995 $me .= ", cdn acl user agent: " . $this->cdn_acl_user_agent;
996 }
997
998 if ($this->cdn_acl_referrer != NULL) {
999 $me .= ", cdn acl referrer: " . $this->cdn_acl_referrer;
1000 }
1001
1002
1003 }
1004 return $me;
1005 }
1006
1007 /**
1008 * Enable Container content to be served via CDN or modify CDN attributes
1009 *
1010 * Either enable this Container's content to be served via CDN or
1011 * adjust its CDN attributes. This Container will always return the
1012 * same CDN-enabled URI each time it is toggled public/private/public.
1013 *
1014 * Example:
1015 * <code>
1016 * # ... authentication code excluded (see previous examples) ...
1017 * #
1018 * $conn = new CF_Connection($auth);
1019 *
1020 * $public_container = $conn->create_container("public");
1021 *
1022 * # CDN-enable the container and set it's TTL for a month
1023 * #
1024 * $public_container->make_public(86400/2); # 12 hours (86400 seconds/day)
1025 * </code>
1026 *
1027 * @param int $ttl the time in seconds content will be cached in the CDN
1028 * @returns string the CDN enabled Container's URI
1029 * @throws CDNNotEnabledException CDN functionality not returned during auth
1030 * @throws AuthenticationException if auth token is not valid/expired
1031 * @throws InvalidResponseException unexpected response
1032 */
1033 function make_public($ttl=86400)
1034 {
1035 if ($this->cfs_http->getCDNMUrl() == NULL) {
1036 throw new CDNNotEnabledException(
1037 "Authentication response did not indicate CDN availability");
1038 }
1039 if ($this->cdn_uri != NULL) {
1040 # previously published, assume we're setting new attributes
1041 list($status, $reason, $cdn_uri, $cdn_ssl_uri) =
1042 $this->cfs_http->update_cdn_container($this->name,$ttl,
1043 $this->cdn_log_retention,
1044 $this->cdn_acl_user_agent,
1045 $this->cdn_acl_referrer);
1046 #if ($status == 401 && $this->_re_auth()) {
1047 # return $this->make_public($ttl);
1048 #}
1049 if ($status == 404) {
1050 # this instance _thinks_ the container was published, but the
1051 # cdn management system thinks otherwise - try again with a PUT
1052 list($status, $reason, $cdn_uri, $cdn_ssl_uri) =
1053 $this->cfs_http->add_cdn_container($this->name,$ttl);
1054
1055 }
1056 } else {
1057 # publish it for first time
1058 list($status, $reason, $cdn_uri, $cdn_ssl_uri) =
1059 $this->cfs_http->add_cdn_container($this->name,$ttl);
1060 }
1061 #if ($status == 401 && $this->_re_auth()) {
1062 # return $this->make_public($ttl);
1063 #}
1064 if (!in_array($status, array(201,202))) {
1065 throw new InvalidResponseException(
1066 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1067 }
1068 $this->cdn_enabled = True;
1069 $this->cdn_ttl = $ttl;
1070 $this->cdn_ssl_uri = $cdn_ssl_uri;
1071 $this->cdn_uri = $cdn_uri;
1072 $this->cdn_log_retention = False;
1073 $this->cdn_acl_user_agent = "";
1074 $this->cdn_acl_referrer = "";
1075 return $this->cdn_uri;
1076 }
1077 /**
1078 * Purge Containers objects from CDN Cache.
1079 * Example:
1080 * <code>
1081 * # ... authentication code excluded (see previous examples) ...
1082 * #
1083 * $conn = new CF_Connection($auth);
1084 * $container = $conn->get_container("cdn_enabled");
1085 * $container->purge_from_cdn("user@domain.com");
1086 * # or
1087 * $container->purge_from_cdn();
1088 * # or
1089 * $container->purge_from_cdn("user1@domain.com,user2@domain.com");
1090 * @returns boolean True if successful
1091 * @throws CDNNotEnabledException if CDN Is not enabled on this connection
1092 * @throws InvalidResponseException if the response expected is not returned
1093 */
1094 function purge_from_cdn($email=null)
1095 {
1096 if (!$this->cfs_http->getCDNMUrl())
1097 {
1098 throw new CDNNotEnabledException(
1099 "Authentication response did not indicate CDN availability");
1100 }
1101 $status = $this->cfs_http->purge_from_cdn($this->name, $email);
1102 if ($status < 199 or $status > 299) {
1103 throw new InvalidResponseException(
1104 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1105 }
1106 return True;
1107 }
1108 /**
1109 * Enable ACL restriction by User Agent for this container.
1110 *
1111 * Example:
1112 * <code>
1113 * # ... authentication code excluded (see previous examples) ...
1114 * #
1115 * $conn = new CF_Connection($auth);
1116 *
1117 * $public_container = $conn->get_container("public");
1118 *
1119 * # Enable ACL by Referrer
1120 * $public_container->acl_referrer("Mozilla");
1121 * </code>
1122 *
1123 * @returns boolean True if successful
1124 * @throws CDNNotEnabledException CDN functionality not returned during auth
1125 * @throws AuthenticationException if auth token is not valid/expired
1126 * @throws InvalidResponseException unexpected response
1127 */
1128 function acl_user_agent($cdn_acl_user_agent="") {
1129 if ($this->cfs_http->getCDNMUrl() == NULL) {
1130 throw new CDNNotEnabledException(
1131 "Authentication response did not indicate CDN availability");
1132 }
1133 list($status,$reason) =
1134 $this->cfs_http->update_cdn_container($this->name,
1135 $this->cdn_ttl,
1136 $this->cdn_log_retention,
1137 $cdn_acl_user_agent,
1138 $this->cdn_acl_referrer
1139 );
1140 if (!in_array($status, array(202,404))) {
1141 throw new InvalidResponseException(
1142 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1143 }
1144 $this->cdn_acl_user_agent = $cdn_acl_user_agent;
1145 return True;
1146 }
1147
1148 /**
1149 * Enable ACL restriction by referer for this container.
1150 *
1151 * Example:
1152 * <code>
1153 * # ... authentication code excluded (see previous examples) ...
1154 * #
1155 * $conn = new CF_Connection($auth);
1156 *
1157 * $public_container = $conn->get_container("public");
1158 *
1159 * # Enable Referrer
1160 * $public_container->acl_referrer("http://www.example.com/gallery.php");
1161 * </code>
1162 *
1163 * @returns boolean True if successful
1164 * @throws CDNNotEnabledException CDN functionality not returned during auth
1165 * @throws AuthenticationException if auth token is not valid/expired
1166 * @throws InvalidResponseException unexpected response
1167 */
1168 function acl_referrer($cdn_acl_referrer="") {
1169 if ($this->cfs_http->getCDNMUrl() == NULL) {
1170 throw new CDNNotEnabledException(
1171 "Authentication response did not indicate CDN availability");
1172 }
1173 list($status,$reason) =
1174 $this->cfs_http->update_cdn_container($this->name,
1175 $this->cdn_ttl,
1176 $this->cdn_log_retention,
1177 $this->cdn_acl_user_agent,
1178 $cdn_acl_referrer
1179 );
1180 if (!in_array($status, array(202,404))) {
1181 throw new InvalidResponseException(
1182 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1183 }
1184 $this->cdn_acl_referrer = $cdn_acl_referrer;
1185 return True;
1186 }
1187
1188 /**
1189 * Enable log retention for this CDN container.
1190 *
1191 * Enable CDN log retention on the container. If enabled logs will
1192 * be periodically (at unpredictable intervals) compressed and
1193 * uploaded to a ".CDN_ACCESS_LOGS" container in the form of
1194 * "container_name.YYYYMMDDHH-XXXX.gz". Requires CDN be enabled on
1195 * the account.
1196 *
1197 * Example:
1198 * <code>
1199 * # ... authentication code excluded (see previous examples) ...
1200 * #
1201 * $conn = new CF_Connection($auth);
1202 *
1203 * $public_container = $conn->get_container("public");
1204 *
1205 * # Enable logs retention.
1206 * $public_container->log_retention(True);
1207 * </code>
1208 *
1209 * @returns boolean True if successful
1210 * @throws CDNNotEnabledException CDN functionality not returned during auth
1211 * @throws AuthenticationException if auth token is not valid/expired
1212 * @throws InvalidResponseException unexpected response
1213 */
1214 function log_retention($cdn_log_retention=False) {
1215 if ($this->cfs_http->getCDNMUrl() == NULL) {
1216 throw new CDNNotEnabledException(
1217 "Authentication response did not indicate CDN availability");
1218 }
1219 list($status,$reason) =
1220 $this->cfs_http->update_cdn_container($this->name,
1221 $this->cdn_ttl,
1222 $cdn_log_retention,
1223 $this->cdn_acl_user_agent,
1224 $this->cdn_acl_referrer
1225 );
1226 if (!in_array($status, array(202,404))) {
1227 throw new InvalidResponseException(
1228 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1229 }
1230 $this->cdn_log_retention = $cdn_log_retention;
1231 return True;
1232 }
1233
1234 /**
1235 * Disable the CDN sharing for this container
1236 *
1237 * Use this method to disallow distribution into the CDN of this Container's
1238 * content.
1239 *
1240 * NOTE: Any content already cached in the CDN will continue to be served
1241 * from its cache until the TTL expiration transpires. The default
1242 * TTL is typically one day, so "privatizing" the Container will take
1243 * up to 24 hours before the content is purged from the CDN cache.
1244 *
1245 * Example:
1246 * <code>
1247 * # ... authentication code excluded (see previous examples) ...
1248 * #
1249 * $conn = new CF_Connection($auth);
1250 *
1251 * $public_container = $conn->get_container("public");
1252 *
1253 * # Disable CDN accessability
1254 * # ... still cached up to a month based on previous example
1255 * #
1256 * $public_container->make_private();
1257 * </code>
1258 *
1259 * @returns boolean True if successful
1260 * @throws CDNNotEnabledException CDN functionality not returned during auth
1261 * @throws AuthenticationException if auth token is not valid/expired
1262 * @throws InvalidResponseException unexpected response
1263 */
1264 function make_private()
1265 {
1266 if ($this->cfs_http->getCDNMUrl() == NULL) {
1267 throw new CDNNotEnabledException(
1268 "Authentication response did not indicate CDN availability");
1269 }
1270 list($status,$reason) = $this->cfs_http->remove_cdn_container($this->name);
1271 #if ($status == 401 && $this->_re_auth()) {
1272 # return $this->make_private();
1273 #}
1274 if (!in_array($status, array(202,404))) {
1275 throw new InvalidResponseException(
1276 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1277 }
1278 $this->cdn_enabled = False;
1279 $this->cdn_ttl = NULL;
1280 $this->cdn_uri = NULL;
1281 $this->cdn_ssl_uri = NULL;
1282 $this->cdn_streaming_uri - NULL;
1283 $this->cdn_log_retention = NULL;
1284 $this->cdn_acl_user_agent = NULL;
1285 $this->cdn_acl_referrer = NULL;
1286 return True;
1287 }
1288
1289 /**
1290 * Check if this Container is being publicly served via CDN
1291 *
1292 * Use this method to determine if the Container's content is currently
1293 * available through the CDN.
1294 *
1295 * Example:
1296 * <code>
1297 * # ... authentication code excluded (see previous examples) ...
1298 * #
1299 * $conn = new CF_Connection($auth);
1300 *
1301 * $public_container = $conn->get_container("public");
1302 *
1303 * # Display CDN accessability
1304 * #
1305 * $public_container->is_public() ? print "Yes" : print "No";
1306 * </code>
1307 *
1308 * @returns boolean True if enabled, False otherwise
1309 */
1310 function is_public()
1311 {
1312 return $this->cdn_enabled == True ? True : False;
1313 }
1314
1315 /**
1316 * Create a new remote storage Object
1317 *
1318 * Return a new Object instance. If the remote storage Object exists,
1319 * the instance's attributes are populated.
1320 *
1321 * Example:
1322 * <code>
1323 * # ... authentication code excluded (see previous examples) ...
1324 * #
1325 * $conn = new CF_Connection($auth);
1326 *
1327 * $public_container = $conn->get_container("public");
1328 *
1329 * # This creates a local instance of a storage object but only creates
1330 * # it in the storage system when the object's write() method is called.
1331 * #
1332 * $pic = $public_container->create_object("baby.jpg");
1333 * </code>
1334 *
1335 * @param string $obj_name name of storage Object
1336 * @return obj CF_Object instance
1337 */
1338 function create_object($obj_name=NULL)
1339 {
1340 return new UpdraftPlus_CF_Object($this, $obj_name);
1341 }
1342
1343 /**
1344 * Return an Object instance for the remote storage Object
1345 *
1346 * Given a name, return a Object instance representing the
1347 * remote storage object.
1348 *
1349 * Example:
1350 * <code>
1351 * # ... authentication code excluded (see previous examples) ...
1352 * #
1353 * $conn = new CF_Connection($auth);
1354 *
1355 * $public_container = $conn->get_container("public");
1356 *
1357 * # This call only fetches header information and not the content of
1358 * # the storage object. Use the Object's read() or stream() methods
1359 * # to obtain the object's data.
1360 * #
1361 * $pic = $public_container->get_object("baby.jpg");
1362 * </code>
1363 *
1364 * @param string $obj_name name of storage Object
1365 * @return obj CF_Object instance
1366 */
1367 function get_object($obj_name=NULL)
1368 {
1369 return new UpdraftPlus_CF_Object($this, $obj_name, True);
1370 }
1371
1372 /**
1373 * Return a list of Objects
1374 *
1375 * Return an array of strings listing the Object names in this Container.
1376 *
1377 * Example:
1378 * <code>
1379 * # ... authentication code excluded (see previous examples) ...
1380 * #
1381 * $images = $conn->get_container("my photos");
1382 *
1383 * # Grab the list of all storage objects
1384 * #
1385 * $all_objects = $images->list_objects();
1386 *
1387 * # Grab subsets of all storage objects
1388 * #
1389 * $first_ten = $images->list_objects(10);
1390 *
1391 * # Note the use of the previous result's last object name being
1392 * # used as the 'marker' parameter to fetch the next 10 objects
1393 * #
1394 * $next_ten = $images->list_objects(10, $first_ten[count($first_ten)-1]);
1395 *
1396 * # Grab images starting with "birthday_party" and default limit/marker
1397 * # to match all photos with that prefix
1398 * #
1399 * $prefixed = $images->list_objects(0, NULL, "birthday");
1400 *
1401 * # Assuming you have created the appropriate directory marker Objects,
1402 * # you can traverse your pseudo-hierarchical containers
1403 * # with the "path" argument.
1404 * #
1405 * $animals = $images->list_objects(0,NULL,NULL,"pictures/animals");
1406 * $dogs = $images->list_objects(0,NULL,NULL,"pictures/animals/dogs");
1407 * </code>
1408 *
1409 * @param int $limit <i>optional</i> only return $limit names
1410 * @param int $marker <i>optional</i> subset of names starting at $marker
1411 * @param string $prefix <i>optional</i> Objects whose names begin with $prefix
1412 * @param string $path <i>optional</i> only return results under "pathname"
1413 * @return array array of strings
1414 * @throws InvalidResponseException unexpected response
1415 */
1416 function list_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL)
1417 {
1418 list($status, $reason, $obj_list) =
1419 $this->cfs_http->list_objects($this->name, $limit,
1420 $marker, $prefix, $path);
1421 #if ($status == 401 && $this->_re_auth()) {
1422 # return $this->list_objects($limit, $marker, $prefix, $path);
1423 #}
1424 if ($status < 200 || $status > 299) {
1425 throw new InvalidResponseException(
1426 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1427 }
1428 return $obj_list;
1429 }
1430
1431 /**
1432 * Return an array of Objects
1433 *
1434 * Return an array of Object instances in this Container.
1435 *
1436 * Example:
1437 * <code>
1438 * # ... authentication code excluded (see previous examples) ...
1439 * #
1440 * $images = $conn->get_container("my photos");
1441 *
1442 * # Grab the list of all storage objects
1443 * #
1444 * $all_objects = $images->get_objects();
1445 *
1446 * # Grab subsets of all storage objects
1447 * #
1448 * $first_ten = $images->get_objects(10);
1449 *
1450 * # Note the use of the previous result's last object name being
1451 * # used as the 'marker' parameter to fetch the next 10 objects
1452 * #
1453 * $next_ten = $images->list_objects(10, $first_ten[count($first_ten)-1]);
1454 *
1455 * # Grab images starting with "birthday_party" and default limit/marker
1456 * # to match all photos with that prefix
1457 * #
1458 * $prefixed = $images->get_objects(0, NULL, "birthday");
1459 *
1460 * # Assuming you have created the appropriate directory marker Objects,
1461 * # you can traverse your pseudo-hierarchical containers
1462 * # with the "path" argument.
1463 * #
1464 * $animals = $images->get_objects(0,NULL,NULL,"pictures/animals");
1465 * $dogs = $images->get_objects(0,NULL,NULL,"pictures/animals/dogs");
1466 * </code>
1467 *
1468 * @param int $limit <i>optional</i> only return $limit names
1469 * @param int $marker <i>optional</i> subset of names starting at $marker
1470 * @param string $prefix <i>optional</i> Objects whose names begin with $prefix
1471 * @param string $path <i>optional</i> only return results under "pathname"
1472 * @return array array of strings
1473 * @throws InvalidResponseException unexpected response
1474 */
1475 function get_objects($limit=0, $marker=NULL, $prefix=NULL, $path=NULL, $delimiter=NULL)
1476 {
1477 list($status, $reason, $obj_array) =
1478 $this->cfs_http->get_objects($this->name, $limit,
1479 $marker, $prefix, $path, $delimiter);
1480 #if ($status == 401 && $this->_re_auth()) {
1481 # return $this->get_objects($limit, $marker, $prefix, $path);
1482 #}
1483 if ($status < 200 || $status > 299) {
1484 throw new InvalidResponseException(
1485 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1486 }
1487 $objects = array();
1488 foreach ($obj_array as $obj) {
1489 if(!isset($obj['subdir'])) {
1490 $tmp = new UpdraftPlus_CF_Object($this, $obj["name"], False, False);
1491 $tmp->content_type = $obj["content_type"];
1492 $tmp->content_length = (float) $obj["bytes"];
1493 $tmp->set_etag($obj["hash"]);
1494 $tmp->last_modified = $obj["last_modified"];
1495 $objects[] = $tmp;
1496 }
1497 }
1498 return $objects;
1499 }
1500
1501 /**
1502 * Copy a remote storage Object to a target Container
1503 *
1504 * Given an Object instance or name and a target Container instance or name, copy copies the remote Object
1505 * and all associated metadata.
1506 *
1507 * Example:
1508 * <code>
1509 * # ... authentication code excluded (see previous examples) ...
1510 * #
1511 * $conn = new CF_Connection($auth);
1512 *
1513 * $images = $conn->get_container("my photos");
1514 *
1515 * # Copy specific object
1516 * #
1517 * $images->copy_object_to("disco_dancing.jpg","container_target");
1518 * </code>
1519 *
1520 * @param obj $obj name or instance of Object to copy
1521 * @param obj $container_target name or instance of target Container
1522 * @param string $dest_obj_name name of target object (optional - uses source name if omitted)
1523 * @param array $metadata metadata array for new object (optional)
1524 * @param array $headers header fields array for the new object (optional)
1525 * @return boolean <kbd>true</kbd> if successfully copied
1526 * @throws SyntaxException invalid Object/Container name
1527 * @throws NoSuchObjectException remote Object does not exist
1528 * @throws InvalidResponseException unexpected response
1529 */
1530 function copy_object_to($obj,$container_target,$dest_obj_name=NULL,$metadata=NULL,$headers=NULL)
1531 {
1532 $obj_name = NULL;
1533 if (is_object($obj)) {
1534 if (get_class($obj) == "UpdraftPlus_CF_Object") {
1535 $obj_name = $obj->name;
1536 }
1537 }
1538 if (is_string($obj)) {
1539 $obj_name = $obj;
1540 }
1541 if (!$obj_name) {
1542 throw new SyntaxException("Object name not set.");
1543 }
1544
1545 if ($dest_obj_name === NULL) {
1546 $dest_obj_name = $obj_name;
1547 }
1548
1549 $container_name_target = NULL;
1550 if (is_object($container_target)) {
1551 if (get_class($container_target) == "UpdraftPlus_CF_Container") {
1552 $container_name_target = $container_target->name;
1553 }
1554 }
1555 if (is_string($container_target)) {
1556 $container_name_target = $container_target;
1557 }
1558 if (!$container_name_target) {
1559 throw new SyntaxException("Container name target not set.");
1560 }
1561
1562 $status = $this->cfs_http->copy_object($obj_name,$dest_obj_name,$this->name,$container_name_target,$metadata,$headers);
1563 if ($status == 404) {
1564 $m = "Specified object '".$this->name."/".$obj_name;
1565 $m.= "' did not exist as source to copy from or '".$container_name_target."' did not exist as target to copy to.";
1566 throw new NoSuchObjectException($m);
1567 }
1568 if ($status < 200 || $status > 299) {
1569 throw new InvalidResponseException(
1570 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1571 }
1572 return true;
1573 }
1574
1575 /**
1576 * Copy a remote storage Object from a source Container
1577 *
1578 * Given an Object instance or name and a source Container instance or name, copy copies the remote Object
1579 * and all associated metadata.
1580 *
1581 * Example:
1582 * <code>
1583 * # ... authentication code excluded (see previous examples) ...
1584 * #
1585 * $conn = new CF_Connection($auth);
1586 *
1587 * $images = $conn->get_container("my photos");
1588 *
1589 * # Copy specific object
1590 * #
1591 * $images->copy_object_from("disco_dancing.jpg","container_source");
1592 * </code>
1593 *
1594 * @param obj $obj name or instance of Object to copy
1595 * @param obj $container_source name or instance of source Container
1596 * @param string $dest_obj_name name of target object (optional - uses source name if omitted)
1597 * @param array $metadata metadata array for new object (optional)
1598 * @param array $headers header fields array for the new object (optional)
1599 * @return boolean <kbd>true</kbd> if successfully copied
1600 * @throws SyntaxException invalid Object/Container name
1601 * @throws NoSuchObjectException remote Object does not exist
1602 * @throws InvalidResponseException unexpected response
1603 */
1604 function copy_object_from($obj,$container_source,$dest_obj_name=NULL,$metadata=NULL,$headers=NULL)
1605 {
1606 $obj_name = NULL;
1607 if (is_object($obj)) {
1608 if (get_class($obj) == "UpdraftPlus_CF_Object") {
1609 $obj_name = $obj->name;
1610 }
1611 }
1612 if (is_string($obj)) {
1613 $obj_name = $obj;
1614 }
1615 if (!$obj_name) {
1616 throw new SyntaxException("Object name not set.");
1617 }
1618
1619 if ($dest_obj_name === NULL) {
1620 $dest_obj_name = $obj_name;
1621 }
1622
1623 $container_name_source = NULL;
1624 if (is_object($container_source)) {
1625 if (get_class($container_source) == "UpdraftPlus_CF_Container") {
1626 $container_name_source = $container_source->name;
1627 }
1628 }
1629 if (is_string($container_source)) {
1630 $container_name_source = $container_source;
1631 }
1632 if (!$container_name_source) {
1633 throw new SyntaxException("Container name source not set.");
1634 }
1635
1636 $status = $this->cfs_http->copy_object($obj_name,$dest_obj_name,$container_name_source,$this->name,$metadata,$headers);
1637 if ($status == 404) {
1638 $m = "Specified object '".$container_name_source."/".$obj_name;
1639 $m.= "' did not exist as source to copy from or '".$this->name."/".$obj_name."' did not exist as target to copy to.";
1640 throw new NoSuchObjectException($m);
1641 }
1642 if ($status < 200 || $status > 299) {
1643 throw new InvalidResponseException(
1644 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1645 }
1646
1647 return true;
1648 }
1649
1650 /**
1651 * Move a remote storage Object to a target Container
1652 *
1653 * Given an Object instance or name and a target Container instance or name, move copies the remote Object
1654 * and all associated metadata and deletes the source Object afterwards
1655 *
1656 * Example:
1657 * <code>
1658 * # ... authentication code excluded (see previous examples) ...
1659 * #
1660 * $conn = new CF_Connection($auth);
1661 *
1662 * $images = $conn->get_container("my photos");
1663 *
1664 * # Move specific object
1665 * #
1666 * $images->move_object_to("disco_dancing.jpg","container_target");
1667 * </code>
1668 *
1669 * @param obj $obj name or instance of Object to move
1670 * @param obj $container_target name or instance of target Container
1671 * @param string $dest_obj_name name of target object (optional - uses source name if omitted)
1672 * @param array $metadata metadata array for new object (optional)
1673 * @param array $headers header fields array for the new object (optional)
1674 * @return boolean <kbd>true</kbd> if successfully moved
1675 * @throws SyntaxException invalid Object/Container name
1676 * @throws NoSuchObjectException remote Object does not exist
1677 * @throws InvalidResponseException unexpected response
1678 */
1679 function move_object_to($obj,$container_target,$dest_obj_name=NULL,$metadata=NULL,$headers=NULL)
1680 {
1681 $retVal = false;
1682
1683 if(self::copy_object_to($obj,$container_target,$dest_obj_name,$metadata,$headers)) {
1684 $retVal = self::delete_object($obj,$this->name);
1685 }
1686
1687 return $retVal;
1688 }
1689
1690 /**
1691 * Move a remote storage Object from a source Container
1692 *
1693 * Given an Object instance or name and a source Container instance or name, move copies the remote Object
1694 * and all associated metadata and deletes the source Object afterwards
1695 *
1696 * Example:
1697 * <code>
1698 * # ... authentication code excluded (see previous examples) ...
1699 * #
1700 * $conn = new CF_Connection($auth);
1701 *
1702 * $images = $conn->get_container("my photos");
1703 *
1704 * # Move specific object
1705 * #
1706 * $images->move_object_from("disco_dancing.jpg","container_target");
1707 * </code>
1708 *
1709 * @param obj $obj name or instance of Object to move
1710 * @param obj $container_source name or instance of target Container
1711 * @param string $dest_obj_name name of target object (optional - uses source name if omitted)
1712 * @param array $metadata metadata array for new object (optional)
1713 * @param array $headers header fields array for the new object (optional)
1714 * @return boolean <kbd>true</kbd> if successfully moved
1715 * @throws SyntaxException invalid Object/Container name
1716 * @throws NoSuchObjectException remote Object does not exist
1717 * @throws InvalidResponseException unexpected response
1718 */
1719 function move_object_from($obj,$container_source,$dest_obj_name=NULL,$metadata=NULL,$headers=NULL)
1720 {
1721 $retVal = false;
1722
1723 if(self::copy_object_from($obj,$container_source,$dest_obj_name,$metadata,$headers)) {
1724 $retVal = self::delete_object($obj,$container_source);
1725 }
1726
1727 return $retVal;
1728 }
1729
1730 /**
1731 * Delete a remote storage Object
1732 *
1733 * Given an Object instance or name, permanently remove the remote Object
1734 * and all associated metadata.
1735 *
1736 * Example:
1737 * <code>
1738 * # ... authentication code excluded (see previous examples) ...
1739 * #
1740 * $conn = new CF_Connection($auth);
1741 *
1742 * $images = $conn->get_container("my photos");
1743 *
1744 * # Delete specific object
1745 * #
1746 * $images->delete_object("disco_dancing.jpg");
1747 * </code>
1748 *
1749 * @param obj $obj name or instance of Object to delete
1750 * @param obj $container name or instance of Container in which the object resides (optional)
1751 * @return boolean <kbd>True</kbd> if successfully removed
1752 * @throws SyntaxException invalid Object name
1753 * @throws NoSuchObjectException remote Object does not exist
1754 * @throws InvalidResponseException unexpected response
1755 */
1756 function delete_object($obj,$container=NULL)
1757 {
1758 $obj_name = NULL;
1759 if (is_object($obj)) {
1760 if (get_class($obj) == "UpdraftPlus_CF_Object") {
1761 $obj_name = $obj->name;
1762 }
1763 }
1764 if (is_string($obj)) {
1765 $obj_name = $obj;
1766 }
1767 if (!$obj_name) {
1768 throw new SyntaxException("Object name not set.");
1769 }
1770
1771 $container_name = NULL;
1772
1773 if($container === NULL) {
1774 $container_name = $this->name;
1775 }
1776 else {
1777 if (is_object($container)) {
1778 if (get_class($container) == "UpdraftPlus_CF_Container") {
1779 $container_name = $container->name;
1780 }
1781 }
1782 if (is_string($container)) {
1783 $container_name = $container;
1784 }
1785 if (!$container_name) {
1786 throw new SyntaxException("Container name source not set.");
1787 }
1788 }
1789
1790 $status = $this->cfs_http->delete_object($container_name, $obj_name);
1791 #if ($status == 401 && $this->_re_auth()) {
1792 # return $this->delete_object($obj);
1793 #}
1794 if ($status == 404) {
1795 $m = "Specified object '".$container_name."/".$obj_name;
1796 $m.= "' did not exist to delete.";
1797 throw new NoSuchObjectException($m);
1798 }
1799 if ($status != 204) {
1800 throw new InvalidResponseException(
1801 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1802 }
1803 return True;
1804 }
1805
1806 /**
1807 * Helper function to create "path" elements for a given Object name
1808 *
1809 * Given an Object whos name contains '/' path separators, this function
1810 * will create the "directory marker" Objects of one byte with the
1811 * Content-Type of "application/directory".
1812 *
1813 * It assumes the last element of the full path is the "real" Object
1814 * and does NOT create a remote storage Object for that last element.
1815 */
1816 function create_paths($path_name)
1817 {
1818 if ($path_name[0] == '/') {
1819 $path_name = mb_substr($path_name, 0, 1);
1820 }
1821 $elements = explode('/', $path_name, -1);
1822 $build_path = "";
1823 foreach ($elements as $idx => $val) {
1824 if (!$build_path) {
1825 $build_path = $val;
1826 } else {
1827 $build_path .= "/" . $val;
1828 }
1829 $obj = new UpdraftPlus_CF_Object($this, $build_path);
1830 $obj->content_type = "application/directory";
1831 $obj->write(".", 1);
1832 }
1833 }
1834
1835 /**
1836 * Internal method to grab CDN/Container info if appropriate to do so
1837 *
1838 * @throws InvalidResponseException unexpected response
1839 */
1840 private function _cdn_initialize()
1841 {
1842 list($status, $reason, $cdn_enabled, $cdn_ssl_uri, $cdn_streaming_uri, $cdn_uri, $cdn_ttl,
1843 $cdn_log_retention, $cdn_acl_user_agent, $cdn_acl_referrer) =
1844 $this->cfs_http->head_cdn_container($this->name);
1845 #if ($status == 401 && $this->_re_auth()) {
1846 # return $this->_cdn_initialize();
1847 #}
1848 if (!in_array($status, array(204,404))) {
1849 throw new InvalidResponseException(
1850 "Invalid response (".$status."): ".$this->cfs_http->get_error());
1851 }
1852 $this->cdn_enabled = $cdn_enabled;
1853 $this->cdn_streaming_uri = $cdn_streaming_uri;
1854 $this->cdn_ssl_uri = $cdn_ssl_uri;
1855 $this->cdn_uri = $cdn_uri;
1856 $this->cdn_ttl = $cdn_ttl;
1857 $this->cdn_log_retention = $cdn_log_retention;
1858 $this->cdn_acl_user_agent = $cdn_acl_user_agent;
1859 $this->cdn_acl_referrer = $cdn_acl_referrer;
1860 }
1861
1862 #private function _re_auth()
1863 #{
1864 # $new_auth = new CF_Authentication(
1865 # $this->cfs_auth->username,
1866 # $this->cfs_auth->api_key,
1867 # $this->cfs_auth->auth_host,
1868 # $this->cfs_auth->account);
1869 # $new_auth->authenticate();
1870 # $this->cfs_auth = $new_auth;
1871 # $this->cfs_http->setCFAuth($this->cfs_auth);
1872 # return True;
1873 #}
1874 }
1875
1876
1877 /**
1878 * Object operations
1879 *
1880 * An Object is analogous to a file on a conventional filesystem. You can
1881 * read data from, or write data to your Objects. You can also associate
1882 * arbitrary metadata with them.
1883 *
1884 * @package php-cloudfiles
1885 */
1886 class UpdraftPlus_CF_Object
1887 {
1888 public $container;
1889 public $name;
1890 public $last_modified;
1891 public $content_type;
1892 public $content_length;
1893 public $metadata;
1894 public $headers;
1895 public $manifest;
1896 private $etag;
1897
1898 /**
1899 * Class constructor
1900 *
1901 * @param obj $container CF_Container instance
1902 * @param string $name name of Object
1903 * @param boolean $force_exists if set, throw an error if Object doesn't exist
1904 */
1905 function __construct(&$container, $name, $force_exists=False, $dohead=True)
1906 {
1907 if ($name[0] == "/") {
1908 $r = "Object name '".$name;
1909 $r .= "' cannot contain begin with a '/' character.";
1910 throw new SyntaxException($r);
1911 }
1912 if (strlen($name) > MAX_OBJECT_NAME_LEN) {
1913 throw new SyntaxException("Object name exceeds "
1914 . "maximum allowed length.");
1915 }
1916 $this->container = $container;
1917 $this->name = $name;
1918 $this->etag = NULL;
1919 $this->_etag_override = False;
1920 $this->last_modified = NULL;
1921 $this->content_type = NULL;
1922 $this->content_length = 0;
1923 $this->metadata = array();
1924 $this->headers = array();
1925 $this->manifest = NULL;
1926 if ($dohead) {
1927 if (!$this->_initialize() && $force_exists) {
1928 throw new NoSuchObjectException("No such object '".$name."'");
1929 }
1930 }
1931 }
1932
1933 /**
1934 * String representation of Object
1935 *
1936 * Pretty print the Object's location and name
1937 *
1938 * @return string Object information
1939 */
1940 function __toString()
1941 {
1942 return $this->container->name . "/" . $this->name;
1943 }
1944
1945 /**
1946 * Internal check to get the proper mimetype.
1947 *
1948 * This function would go over the available PHP methods to get
1949 * the MIME type.
1950 *
1951 * By default it will try to use the PHP fileinfo library which is
1952 * available from PHP 5.3 or as an PECL extension
1953 * (http://pecl.php.net/package/Fileinfo).
1954 *
1955 * It will get the magic file by default from the system wide file
1956 * which is usually available in /usr/share/magic on Unix or try
1957 * to use the file specified in the source directory of the API
1958 * (share directory).
1959 *
1960 * if fileinfo is not available it will try to use the internal
1961 * mime_content_type function.
1962 *
1963 * @param string $handle name of file or buffer to guess the type from
1964 * @return boolean <kbd>True</kbd> if successful
1965 * @throws BadContentTypeException
1966 */
1967 function _guess_content_type($handle) {
1968 if ($this->content_type)
1969 return;
1970
1971 if (function_exists("finfo_open")) {
1972 $local_magic = dirname(__FILE__) . "/share/magic";
1973 $finfo = @finfo_open(FILEINFO_MIME, $local_magic);
1974
1975 if (!$finfo)
1976 $finfo = @finfo_open(FILEINFO_MIME);
1977
1978 if ($finfo) {
1979
1980 if (is_file((string)$handle))
1981 $ct = @finfo_file($finfo, $handle);
1982 else
1983 $ct = @finfo_buffer($finfo, $handle);
1984
1985 /* PHP 5.3 fileinfo display extra information like
1986 charset so we remove everything after the ; since
1987 we are not into that stuff */
1988 if ($ct) {
1989 $extra_content_type_info = strpos($ct, "; ");
1990 if ($extra_content_type_info)
1991 $ct = substr($ct, 0, $extra_content_type_info);
1992 }
1993
1994 if ($ct && $ct != 'application/octet-stream')
1995 $this->content_type = $ct;
1996
1997 @finfo_close($finfo);
1998 }
1999 }
2000
2001 if (!$this->content_type && (string)is_file($handle) && function_exists("mime_content_type")) {
2002 $this->content_type = @mime_content_type($handle);
2003 }
2004
2005 if (!$this->content_type) {
2006 throw new BadContentTypeException("Required Content-Type not set");
2007 }
2008 return True;
2009 }
2010
2011 /**
2012 * String representation of the Object's public URI
2013 *
2014 * A string representing the Object's public URI assuming that it's
2015 * parent Container is CDN-enabled.
2016 *
2017 * Example:
2018 * <code>
2019 * # ... authentication/connection/container code excluded
2020 * # ... see previous examples
2021 *
2022 * # Print out the Object's CDN URI (if it has one) in an HTML img-tag
2023 * #
2024 * print "<img src='$pic->public_uri()' />\n";
2025 * </code>
2026 *
2027 * @return string Object's public URI or NULL
2028 */
2029 function public_uri()
2030 {
2031 if ($this->container->cdn_enabled) {
2032 return $this->container->cdn_uri . "/" . $this->name;
2033 }
2034 return NULL;
2035 }
2036
2037 /**
2038 * String representation of the Object's public SSL URI
2039 *
2040 * A string representing the Object's public SSL URI assuming that it's
2041 * parent Container is CDN-enabled.
2042 *
2043 * Example:
2044 * <code>
2045 * # ... authentication/connection/container code excluded
2046 * # ... see previous examples
2047 *
2048 * # Print out the Object's CDN SSL URI (if it has one) in an HTML img-tag
2049 * #
2050 * print "<img src='$pic->public_ssl_uri()' />\n";
2051 * </code>
2052 *
2053 * @return string Object's public SSL URI or NULL
2054 */
2055 function public_ssl_uri()
2056 {
2057 if ($this->container->cdn_enabled) {
2058 return $this->container->cdn_ssl_uri . "/" . $this->name;
2059 }
2060 return NULL;
2061 }
2062 /**
2063 * String representation of the Object's public Streaming URI
2064 *
2065 * A string representing the Object's public Streaming URI assuming that it's
2066 * parent Container is CDN-enabled.
2067 *
2068 * Example:
2069 * <code>
2070 * # ... authentication/connection/container code excluded
2071 * # ... see previous examples
2072 *
2073 * # Print out the Object's CDN Streaming URI (if it has one) in an HTML img-tag
2074 * #
2075 * print "<img src='$pic->public_streaming_uri()' />\n";
2076 * </code>
2077 *
2078 * @return string Object's public Streaming URI or NULL
2079 */
2080 function public_streaming_uri()
2081 {
2082 if ($this->container->cdn_enabled) {
2083 return $this->container->cdn_streaming_uri . "/" . $this->name;
2084 }
2085 return NULL;
2086 }
2087
2088 /**
2089 * Read the remote Object's data
2090 *
2091 * Returns the Object's data. This is useful for smaller Objects such
2092 * as images or office documents. Object's with larger content should use
2093 * the stream() method below.
2094 *
2095 * Pass in $hdrs array to set specific custom HTTP headers such as
2096 * If-Match, If-None-Match, If-Modified-Since, Range, etc.
2097 *
2098 * Example:
2099 * <code>
2100 * # ... authentication/connection/container code excluded
2101 * # ... see previous examples
2102 *
2103 * $my_docs = $conn->get_container("documents");
2104 * $doc = $my_docs->get_object("README");
2105 * $data = $doc->read(); # read image content into a string variable
2106 * print $data;
2107 *
2108 * # Or see stream() below for a different example.
2109 * #
2110 * </code>
2111 *
2112 * @param array $hdrs user-defined headers (Range, If-Match, etc.)
2113 * @return string Object's data
2114 * @throws InvalidResponseException unexpected response
2115 */
2116 function read($hdrs=array())
2117 {
2118 list($status, $reason, $data) =
2119 $this->container->cfs_http->get_object_to_string($this, $hdrs);
2120 #if ($status == 401 && $this->_re_auth()) {
2121 # return $this->read($hdrs);
2122 #}
2123 if (($status < 200) || ($status > 299
2124 && $status != 412 && $status != 304)) {
2125 throw new InvalidResponseException("Invalid response (".$status."): "
2126 . $this->container->cfs_http->get_error());
2127 }
2128 return $data;
2129 }
2130
2131 /**
2132 * Streaming read of Object's data
2133 *
2134 * Given an open PHP resource (see PHP's fopen() method), fetch the Object's
2135 * data and write it to the open resource handle. This is useful for
2136 * streaming an Object's content to the browser (videos, images) or for
2137 * fetching content to a local file.
2138 *
2139 * Pass in $hdrs array to set specific custom HTTP headers such as
2140 * If-Match, If-None-Match, If-Modified-Since, Range, etc.
2141 *
2142 * Example:
2143 * <code>
2144 * # ... authentication/connection/container code excluded
2145 * # ... see previous examples
2146 *
2147 * # Assuming this is a web script to display the README to the
2148 * # user's browser:
2149 * #
2150 * <?php
2151 * // grab README from storage system
2152 * //
2153 * $my_docs = $conn->get_container("documents");
2154 * $doc = $my_docs->get_object("README");
2155 *
2156 * // Hand it back to user's browser with appropriate content-type
2157 * //
2158 * header("Content-Type: " . $doc->content_type);
2159 * $output = fopen("php://output", "w");
2160 * $doc->stream($output); # stream object content to PHP's output buffer
2161 * fclose($output);
2162 * ?>
2163 *
2164 * # See read() above for a more simple example.
2165 * #
2166 * </code>
2167 *
2168 * @param resource $fp open resource for writing data to
2169 * @param array $hdrs user-defined headers (Range, If-Match, etc.)
2170 * @return string Object's data
2171 * @throws InvalidResponseException unexpected response
2172 */
2173 function stream(&$fp, $hdrs=array())
2174 {
2175 list($status, $reason) =
2176 $this->container->cfs_http->get_object_to_stream($this,$fp,$hdrs);
2177 #if ($status == 401 && $this->_re_auth()) {
2178 # return $this->stream($fp, $hdrs);
2179 #}
2180 if (($status < 200) || ($status > 299
2181 && $status != 412 && $status != 304)) {
2182 throw new InvalidResponseException("Invalid response (".$status."): "
2183 .$reason);
2184 }
2185 return True;
2186 }
2187
2188 /**
2189 * Store new Object metadata
2190 *
2191 * Write's an Object's metadata to the remote Object. This will overwrite
2192 * an prior Object metadata.
2193 *
2194 * Example:
2195 * <code>
2196 * # ... authentication/connection/container code excluded
2197 * # ... see previous examples
2198 *
2199 * $my_docs = $conn->get_container("documents");
2200 * $doc = $my_docs->get_object("README");
2201 *
2202 * # Define new metadata for the object
2203 * #
2204 * $doc->metadata = array(
2205 * "Author" => "EJ",
2206 * "Subject" => "How to use the PHP tests",
2207 * "Version" => "1.2.2"
2208 * );
2209 *
2210 * # Define additional headers for the object
2211 * #
2212 * $doc->headers = array(
2213 * "Content-Disposition" => "attachment",
2214 * );
2215 *
2216 * # Push the new metadata up to the storage system
2217 * #
2218 * $doc->sync_metadata();
2219 * </code>
2220 *
2221 * @return boolean <kbd>True</kbd> if successful, <kbd>False</kbd> otherwise
2222 * @throws InvalidResponseException unexpected response
2223 */
2224 function sync_metadata()
2225 {
2226 if (!empty($this->metadata) || !empty($this->headers) || $this->manifest) {
2227 $status = $this->container->cfs_http->update_object($this);
2228 #if ($status == 401 && $this->_re_auth()) {
2229 # return $this->sync_metadata();
2230 #}
2231 if ($status != 202) {
2232 throw new InvalidResponseException("Invalid response ("
2233 .$status."): ".$this->container->cfs_http->get_error());
2234 }
2235 return True;
2236 }
2237 return False;
2238 }
2239 /**
2240 * Store new Object manifest
2241 *
2242 * Write's an Object's manifest to the remote Object. This will overwrite
2243 * an prior Object manifest.
2244 *
2245 * Example:
2246 * <code>
2247 * # ... authentication/connection/container code excluded
2248 * # ... see previous examples
2249 *
2250 * $my_docs = $conn->get_container("documents");
2251 * $doc = $my_docs->get_object("README");
2252 *
2253 * # Define new manifest for the object
2254 * #
2255 * $doc->manifest = "container/prefix";
2256 *
2257 * # Push the new manifest up to the storage system
2258 * #
2259 * $doc->sync_manifest();
2260 * </code>
2261 *
2262 * @return boolean <kbd>True</kbd> if successful, <kbd>False</kbd> otherwise
2263 * @throws InvalidResponseException unexpected response
2264 */
2265
2266 function sync_manifest()
2267 {
2268 return $this->sync_metadata();
2269 }
2270 /**
2271 * Upload Object's data to Cloud Files
2272 *
2273 * Write data to the remote Object. The $data argument can either be a
2274 * PHP resource open for reading (see PHP's fopen() method) or an in-memory
2275 * variable. If passing in a PHP resource, you must also include the $bytes
2276 * parameter.
2277 *
2278 * Example:
2279 * <code>
2280 * # ... authentication/connection/container code excluded
2281 * # ... see previous examples
2282 *
2283 * $my_docs = $conn->get_container("documents");
2284 * $doc = $my_docs->get_object("README");
2285 *
2286 * # Upload placeholder text in my README
2287 * #
2288 * $doc->write("This is just placeholder text for now...");
2289 * </code>
2290 *
2291 * @param string|resource $data string or open resource
2292 * @param float $bytes amount of data to upload (required for resources)
2293 * @param boolean $verify generate, send, and compare MD5 checksums
2294 * @return boolean <kbd>True</kbd> when data uploaded successfully
2295 * @throws SyntaxException missing required parameters
2296 * @throws BadContentTypeException if no Content-Type was/could be set
2297 * @throws MisMatchedChecksumException $verify is set and checksums unequal
2298 * @throws InvalidResponseException unexpected response
2299 */
2300 function write($data=NULL, $bytes=0, $verify=True)
2301 {
2302 if (!$data && !is_string($data)) {
2303 throw new SyntaxException("Missing data source.");
2304 }
2305 if ($bytes > MAX_OBJECT_SIZE) {
2306 throw new SyntaxException("Bytes exceeds maximum object size.");
2307 }
2308 if ($verify) {
2309 if (!$this->_etag_override) {
2310 $this->etag = $this->compute_md5sum($data);
2311 }
2312 } else {
2313 $this->etag = NULL;
2314 }
2315
2316 $close_fh = False;
2317 if (!is_resource($data)) {
2318 # A hack to treat string data as a file handle. php://memory feels
2319 # like a better option, but it seems to break on Windows so use
2320 # a temporary file instead.
2321 #
2322 $fp = fopen("php://temp", "wb+");
2323 #$fp = fopen("php://memory", "wb+");
2324 fwrite($fp, $data, strlen($data));
2325 rewind($fp);
2326 $close_fh = True;
2327 $this->content_length = (float) strlen($data);
2328 if ($this->content_length > MAX_OBJECT_SIZE) {
2329 throw new SyntaxException("Data exceeds maximum object size");
2330 }
2331 $ct_data = substr($data, 0, 64);
2332 } else {
2333 // The original Rackspace library used rewind() instead of ftell/fseek here - which meant fseek(0), which was sometimes wrong
2334 $fpos = ftell($data);
2335 $this->content_length = $bytes;
2336 $fp = $data;
2337 $ct_data = fread($data, 64);
2338 fseek($data, $fpos);
2339 }
2340
2341 $this->_guess_content_type($ct_data);
2342
2343 list($status, $reason, $etag) =
2344 $this->container->cfs_http->put_object($this, $fp);
2345 #if ($status == 401 && $this->_re_auth()) {
2346 # return $this->write($data, $bytes, $verify);
2347 #}
2348 if ($status == 412) {
2349 if ($close_fh) { fclose($fp); }
2350 throw new SyntaxException("Missing Content-Type header");
2351 }
2352 if ($status == 422) {
2353 if ($close_fh) { fclose($fp); }
2354 throw new MisMatchedChecksumException(
2355 "Supplied and computed checksums do not match.");
2356 }
2357 if ($status != 201) {
2358 if ($close_fh) { fclose($fp); }
2359 throw new InvalidResponseException("Invalid response (".$status."): "
2360 . $this->container->cfs_http->get_error());
2361 }
2362 if (!$verify) {
2363 $this->etag = $etag;
2364 }
2365 if ($close_fh) { fclose($fp); }
2366 return True;
2367 }
2368
2369 /**
2370 * Upload Object data from local filename
2371 *
2372 * This is a convenience function to upload the data from a local file. A
2373 * True value for $verify will cause the method to compute the Object's MD5
2374 * checksum prior to uploading.
2375 *
2376 * Example:
2377 * <code>
2378 * # ... authentication/connection/container code excluded
2379 * # ... see previous examples
2380 *
2381 * $my_docs = $conn->get_container("documents");
2382 * $doc = $my_docs->get_object("README");
2383 *
2384 * # Upload my local README's content
2385 * #
2386 * $doc->load_from_filename("/home/ej/cloudfiles/readme");
2387 * </code>
2388 *
2389 * @param string $filename full path to local file
2390 * @param boolean $verify enable local/remote MD5 checksum validation
2391 * @return boolean <kbd>True</kbd> if data uploaded successfully
2392 * @throws SyntaxException missing required parameters
2393 * @throws BadContentTypeException if no Content-Type was/could be set
2394 * @throws MisMatchedChecksumException $verify is set and checksums unequal
2395 * @throws InvalidResponseException unexpected response
2396 * @throws IOException error opening file
2397 */
2398 function load_from_filename($filename, $verify=True)
2399 {
2400 $fp = @fopen($filename, "r");
2401 if (!$fp) {
2402 throw new IOException("Could not open file for reading: ".$filename);
2403 }
2404
2405 clearstatcache();
2406
2407 $size = (float) sprintf("%u", filesize($filename));
2408 if ($size > MAX_OBJECT_SIZE) {
2409 throw new SyntaxException("File size exceeds maximum object size.");
2410 }
2411
2412 $this->_guess_content_type($filename);
2413
2414 $this->write($fp, $size, $verify);
2415 fclose($fp);
2416 return True;
2417 }
2418
2419 /**
2420 * Save Object's data to local filename
2421 *
2422 * Given a local filename, the Object's data will be written to the newly
2423 * created file.
2424 *
2425 * Example:
2426 * <code>
2427 * # ... authentication/connection/container code excluded
2428 * # ... see previous examples
2429 *
2430 * # Whoops! I deleted my local README, let me download/save it
2431 * #
2432 * $my_docs = $conn->get_container("documents");
2433 * $doc = $my_docs->get_object("README");
2434 *
2435 * $doc->save_to_filename("/home/ej/cloudfiles/readme.restored");
2436 * </code>
2437 *
2438 * @param string $filename name of local file to write data to
2439 * @return boolean <kbd>True</kbd> if successful
2440 * @throws IOException error opening file
2441 * @throws InvalidResponseException unexpected response
2442 */
2443 function save_to_filename($filename)
2444 {
2445 $fp = @fopen($filename, "wb");
2446 if (!$fp) {
2447 throw new IOException("Could not open file for writing: ".$filename);
2448 }
2449 $result = $this->stream($fp);
2450 fclose($fp);
2451 return $result;
2452 }
2453 /**
2454 * Purge this Object from CDN Cache.
2455 * Example:
2456 * <code>
2457 * # ... authentication code excluded (see previous examples) ...
2458 * #
2459 * $conn = new CF_Connection($auth);
2460 * $container = $conn->get_container("cdn_enabled");
2461 * $obj = $container->get_object("object");
2462 * $obj->purge_from_cdn("user@domain.com");
2463 * # or
2464 * $obj->purge_from_cdn();
2465 * # or
2466 * $obj->purge_from_cdn("user1@domain.com,user2@domain.com");
2467 * </code>
2468 * @returns boolean True if successful
2469 * @throws CDNNotEnabledException if CDN Is not enabled on this connection
2470 * @throws InvalidResponseException if the response expected is not returned
2471 */
2472 function purge_from_cdn($email=null)
2473 {
2474 if (!$this->container->cfs_http->getCDNMUrl())
2475 {
2476 throw new CDNNotEnabledException(
2477 "Authentication response did not indicate CDN availability");
2478 }
2479 $status = $this->container->cfs_http->purge_from_cdn($this->container->name . "/" . $this->name, $email);
2480 if ($status < 199 or $status > 299) {
2481 throw new InvalidResponseException(
2482 "Invalid response (".$status."): ".$this->container->cfs_http->get_error());
2483 }
2484 return True;
2485 }
2486
2487 /**
2488 * Set Object's MD5 checksum
2489 *
2490 * Manually set the Object's ETag. Including the ETag is mandatory for
2491 * Cloud Files to perform end-to-end verification. Omitting the ETag forces
2492 * the user to handle any data integrity checks.
2493 *
2494 * @param string $etag MD5 checksum hexidecimal string
2495 */
2496 function set_etag($etag)
2497 {
2498 $this->etag = $etag;
2499 $this->_etag_override = True;
2500 }
2501
2502 /**
2503 * Object's MD5 checksum
2504 *
2505 * Accessor method for reading Object's private ETag attribute.
2506 *
2507 * @return string MD5 checksum hexidecimal string
2508 */
2509 function getETag()
2510 {
2511 return $this->etag;
2512 }
2513
2514 /**
2515 * Compute the MD5 checksum
2516 *
2517 * Calculate the MD5 checksum on either a PHP resource or data. The argument
2518 * may either be a local filename, open resource for reading, or a string.
2519 *
2520 * <b>WARNING:</b> if you are uploading a big file over a stream
2521 * it could get very slow to compute the md5 you probably want to
2522 * set the $verify parameter to False in the write() method and
2523 * compute yourself the md5 before if you have it.
2524 *
2525 * @param filename|obj|string $data filename, open resource, or string
2526 * @return string MD5 checksum hexidecimal string
2527 */
2528 function compute_md5sum(&$data)
2529 {
2530
2531 if (function_exists("hash_init") && is_resource($data)) {
2532 $ctx = hash_init('md5');
2533 $fpos = ftell($data);
2534 while (!feof($data)) {
2535 $buffer = fgets($data, 65536);
2536 hash_update($ctx, $buffer);
2537 }
2538 $md5 = hash_final($ctx, false);
2539 fseek($data, $fpos);
2540 } elseif ((string)is_file($data)) {
2541 $md5 = md5_file($data);
2542 } else {
2543 $md5 = md5($data);
2544 }
2545 return $md5;
2546 }
2547
2548 /**
2549 * PRIVATE: fetch information about the remote Object if it exists
2550 */
2551 private function _initialize()
2552 {
2553 list($status, $reason, $etag, $last_modified, $content_type,
2554 $content_length, $metadata, $manifest, $headers) =
2555 $this->container->cfs_http->head_object($this);
2556 #if ($status == 401 && $this->_re_auth()) {
2557 # return $this->_initialize();
2558 #}
2559 if ($status == 404) {
2560 return False;
2561 }
2562 if ($status < 200 || $status > 299) {
2563 throw new InvalidResponseException("Invalid response (".$status."): "
2564 . $this->container->cfs_http->get_error());
2565 }
2566 $this->etag = $etag;
2567 $this->last_modified = $last_modified;
2568 $this->content_type = $content_type;
2569 $this->content_length = $content_length;
2570 $this->metadata = $metadata;
2571 $this->headers = $headers;
2572 $this->manifest = $manifest;
2573 return True;
2574 }
2575 /**
2576 * Generate a Temp Url for a object
2577 * Example:
2578 * <code>
2579 * # ... authentication code excluded (see previous examples) ...
2580 * $conn = new CF_Connection($auth);
2581 * $container = $conn->get_container("foo");
2582 * $obj = $container->get_object("foo");
2583 * $tempurl = $obj->get_temp_url("shared_secret", "expire_time_in_seconds", "{HTTP_METHOD}"); (note: replace {HTTP_METHOD} with the request method: GET, POST, PUT, DELETE, etc.
2584 * </code>
2585 * @returns The temp url
2586 */
2587 public function get_temp_url($key, $expires, $method)
2588 {
2589
2590 $expires += time();
2591 $url = $this->container->cfs_http->getStorageUrl() . '/' . $this->container->name . '/' . $this->name;
2592 return $url . '?temp_url_sig=' . hash_hmac('sha1', strtoupper($method) .
2593 "\n" . $expires . "\n" . parse_url($url, PHP_URL_PATH), $key) .
2594 '&temp_url_expires=' . $expires;
2595 }
2596 /**
2597 * Generate hidden input for form post.
2598 * @returns array Returns an associative array with form post input.
2599 */
2600 public function get_form_post_input($key, $expires, $redirect, $max_file_size=5368709120, $max_file_count=1)
2601 {
2602
2603 $expires += time();
2604 $url = $this->container->cfs_http->getStorageUrl() . '/' . $this->container->name . '/' . $this->name;
2605 $form_post = array('action' => $url, 'redirect' => $redirect, 'max_file_size' => $max_file_size, 'expires' => $expires, 'file' => $this->name);
2606 $form_post['signature'] = hash_hmac('sha1', parse_url($url, PHP_URL_PATH) . "\n" . $redirect . "\n" . $max_file_size . "\n" . $max_file_count . "\n" . $expires, $key );
2607 return $form_post;
2608 }
2609 #private function _re_auth()
2610 #{
2611 # $new_auth = new CF_Authentication(
2612 # $this->cfs_auth->username,
2613 # $this->cfs_auth->api_key,
2614 # $this->cfs_auth->auth_host,
2615 # $this->cfs_auth->account);
2616 # $new_auth->authenticate();
2617 # $this->container->cfs_auth = $new_auth;
2618 # $this->container->cfs_http->setCFAuth($this->cfs_auth);
2619 # return True;
2620 #}
2621 }
2622
2623 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
2624
2625 /*
2626 * Local variables:
2627 * tab-width: 4
2628 * c-basic-offset: 4
2629 * c-hanging-comment-ender-p: nil
2630 * End:
2631 */
2632 ?>
2633