r97695 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r97694‎ | r97695 | r97696 >
Date:03:31, 21 September 2011
Author:nelson
Status:deferred
Tags:
Comment:
Add php-cloudfiles; Use more single-quotes; add SwiftForeign* classes; other style fixes.
Modified paths:
  • /trunk/extensions/SwiftMedia/SwiftMedia.body.php (modified) (history)
  • /trunk/extensions/SwiftMedia/SwiftMedia.php (modified) (history)
  • /trunk/extensions/SwiftMedia/copyover (modified) (history)
  • /trunk/extensions/SwiftMedia/php-cloudfiles (added) (history)
  • /trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles.php (added) (history)
  • /trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles_exceptions.php (added) (history)
  • /trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles_http.php (added) (history)

Diff [purge]

Index: trunk/extensions/SwiftMedia/SwiftMedia.php
@@ -9,6 +9,9 @@
1010 );
1111
1212 $wgAutoloadClasses['SwiftFile'] =
 13+ $wgAutoloadClasses['SwiftForeignDBFile'] =
 14+ $wgAutoloadClasses['SwiftForeignDBRepo'] =
 15+ $wgAutoloadClasses['SwiftForeignDBviaLBRepo'] =
1316 $wgAutoloadClasses['SwiftRepo'] = dirname(__FILE__) . '/SwiftMedia.body.php';
1417 $wgAutoloadClasses['CF_Authentication'] =
1518 $wgAutoloadClasses['CF_Connection'] =
Index: trunk/extensions/SwiftMedia/copyover
@@ -1,6 +1,8 @@
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/*~
57 scp rnelson@alsted.wikimedia.org:/etc/swift/proxy-server.conf proxy-server.sample
68 scp rnelson@alsted.wikimedia.org:/usr/local/lib/python2.6/dist-packages/wmf/{client.py,__init__.py,rewrite.py} wmf/
79
Index: trunk/extensions/SwiftMedia/SwiftMedia.body.php
@@ -60,7 +60,9 @@
6161 * Note: $unused param is only here to avoid an E_STRICT
6262 */
6363 static function newFromTitle( $title, $repo, $unused = null ) {
64 - if ( empty($title) ) { return null; }
 64+ if ( empty($title) ) {
 65+ return null;
 66+ }
6567 return new self( $title, $repo );
6668 }
6769
@@ -82,7 +84,7 @@
8385 */
8486 function __construct( $title, $repo ) {
8587 if ( !is_object( $title ) ) {
86 - throw new MWException( __CLASS__ . " constructor given bogus title." );
 88+ throw new MWException( __CLASS__ . ' constructor given bogus title.' );
8789 }
8890
8991 parent::__construct( $title, $repo );
@@ -97,19 +99,19 @@
98100 /** getViewURL inherited */
99101 /** isVisible inherited */
100102
101 - function getPath() {
 103+ public function getPath() {
102104 $this->tempPath = $this->repo->getLocalCopy($this->repo->container, $this->getRel());
103105 return $this->tempPath;
104106 }
105107
106108 /** Get the path of the archive directory, or a particular file if $suffix is specified */
107 - function getArchivePath( $suffix = false ) {
 109+ public function getArchivePath( $suffix = false ) {
108110 $this->tempPath = $this->repo->getLocalCopy($this->repo->getZoneContainer('public'), $this->getArchiveRel( $suffix ));
109111 return $this->tempPath;
110112 }
111113
112114 /** Get the path of the thumbnail directory, or a particular file if $suffix is specified */
113 - function getThumbPath( $suffix = false ) {
 115+ public function getThumbPath( $suffix = false ) {
114116 $path = $this->getRel();
115117 if ( $suffix !== false ) {
116118 $path .= '/' . $suffix;
@@ -138,11 +140,11 @@
139141 *
140142 * @return MediaTransformOutput | false
141143 */
142 - function maybeDoTransform( $thumbName, $thumbUrl, $params, $flags ) {
 144+ private function maybeDoTransform( $thumbName, $thumbUrl, $params, $flags ) {
143145 global $wgIgnoreImageErrors, $wgThumbnailEpoch, $wgTmpDirectory;
144146
145147 // get a temporary place to put the original.
146 - $thumbPath = tempnam( $wgTmpDirectory, 'transform_out_') . "." . pathinfo( $thumbName, PATHINFO_EXTENSION );
 148+ $thumbPath = tempnam( $wgTmpDirectory, 'transform_out_') . '.' . pathinfo( $thumbName, PATHINFO_EXTENSION );
147149
148150 if ( $this->repo && $this->repo->canTransformVia404() && !($flags & self::RENDER_NOW ) ) {
149151 return $this->handler->getTransform( $this, $thumbPath, $thumbUrl, $params );
@@ -150,16 +152,16 @@
151153
152154 // see if the file exists, and if it exists, is not too old.
153155 $conn = $this->repo->connect();
154 - $container = $this->repo->get_container($conn,$this->repo->container . "%2Fthumb");
 156+ $container = $this->repo->get_container($conn,$this->repo->container . '%2Fthumb');
155157 try {
156 - $pic = $container->get_object($this->getRel() . "/" . $thumbName);
 158+ $pic = $container->get_object($this->getRel() . "/$thumbName");
157159 } catch (NoSuchObjectException $e) {
158160 $pic = NULL;
159161 }
160162 if ( $pic ) {
161163 $thumbTime = $pic->last_modified;
162 - $tm = strptime($thumbTime, "%a, %d %b %Y %H:%M:%S GMT");
163 - $thumbGMT = gmmktime($tm["tm_hour"], $tm["tm_min"], $tm["tm_sec"], $tm["tm_mon"]+1, $tm["tm_mday"], $tm["tm_year"] + 1900);
 164+ $tm = strptime($thumbTime, '%a, %d %b %Y %H:%M:%S GMT');
 165+ $thumbGMT = gmmktime($tm['tm_hour'], $tm['tm_min'], $tm['tm_sec'], $tm['tm_mon']+1, $tm['tm_mday'], $tm['tm_year'] + 1900);
164166 wfDebug( __METHOD__.": $thumbName is dated $thumbGMT\n" );
165167 if ( gmdate( 'YmdHis', $thumbGMT ) >= $wgThumbnailEpoch ) {
166168
@@ -181,8 +183,8 @@
182184 // what if they didn't actually write out a thumbnail? Check the file size.
183185 if ( $thumb && file_exists( $thumbPath ) && filesize( $thumbPath ) ) {
184186 // Store the thumbnail into Swift, but in the thumb version of the container.
185 - wfDebug( __METHOD__ . "Creating thumb " . $this->getRel() . "/" . $thumbName . "\n" );
186 - $this->repo->write_swift_object( $thumbPath, $container, $this->getRel() . "/" . $thumbName );
 187+ wfDebug( __METHOD__ . 'Creating thumb ' . $this->getRel() . "/$thumbName\n" );
 188+ $this->repo->write_swift_object( $thumbPath, $container, $this->getRel() . "/$thumbName" );
187189 // php-cloudfiles throws exceptions, so failure never gets here.
188190 }
189191
@@ -201,14 +203,14 @@
202204 /**
203205 * We have nothing to do here.
204206 */
205 - function migrateThumbFile( $thumbName ) {
 207+ protected function migrateThumbFile( $thumbName ) {
206208 return;
207209 }
208210 /**
209211 * Get the public root directory of the repository.
210212 */
211 - function getRootDirectory() {
212 - throw new MWException( __METHOD__.": not implemented" );
 213+ protected function getRootDirectory() {
 214+ throw new MWException( __METHOD__.': not implemented' );
213215 }
214216
215217
@@ -230,9 +232,9 @@
231233 $prefix = $this->getRel();
232234 }
233235 $conn = $this->repo->connect();
234 - $container = $this->repo->get_container($conn,$this->repo->container . "%2Fthumb");
 236+ $container = $this->repo->get_container($conn,$this->repo->container . '%2Fthumb');
235237 $files = $container->list_objects(0, NULL, $prefix);
236 - array_unshift($files, "unused"); # return an unused $dir.
 238+ array_unshift($files, 'unused'); # return an unused $dir.
237239 return $files;
238240 }
239241
@@ -245,15 +247,15 @@
246248 global $wgExcludeFromThumbnailPurge;
247249
248250 $conn = $this->repo->connect();
249 - $container = $this->repo->get_container($conn,$this->repo->container . "%2Fthumb");
 251+ $container = $this->repo->get_container($conn,$this->repo->container . '%2Fthumb');
250252 foreach ( $files as $file ) {
251253 // Only remove files not in the $wgExcludeFromThumbnailPurge configuration variable
252 - $ext = pathinfo( "$file", PATHINFO_EXTENSION );
 254+ $ext = pathinfo( $file, PATHINFO_EXTENSION );
253255 if ( in_array( $ext, $wgExcludeFromThumbnailPurge ) ) {
254256 continue;
255257 }
256258
257 - wfDebug( __METHOD__ . " deleting " . $container->name . "/$file\n");
 259+ wfDebug( __METHOD__ . ' deleting ' . $container->name . "/$file\n");
258260 $this->repo->swift_delete($container, $file);
259261 }
260262 }
@@ -314,12 +316,12 @@
315317 *
316318 * @return CF_Connection
317319 */
318 - function connect() {
 320+ protected function connect() {
319321 $auth = new CF_Authentication($this->swiftuser, $this->swiftkey, NULL, $this->authurl);
320322 try {
321323 $auth->authenticate();
322324 } catch (AuthenticationException $e) {
323 - throw new MWException( "We can't authenticate ourselves." );
 325+ throw new MWException( 'We can't authenticate ourselves.' );
324326 } catch (InvalidResponseException $e) {
325327 throw new MWException( __METHOD__ . "unexpected response '$e'" );
326328 }
@@ -332,7 +334,7 @@
333335 *
334336 * @return CF_Container
335337 */
336 - function get_container($conn, $cont) {
 338+ protected function get_container($conn, $cont) {
337339 try {
338340 return $conn->get_container($cont);
339341 } catch (NoSuchContainerException $e) {
@@ -349,14 +351,14 @@
350352 *
351353 * @return CF_Container
352354 */
353 - function write_swift_object( $srcPath, $dstc, $dstRel) {
 355+ protected function write_swift_object( $srcPath, $dstc, $dstRel) {
354356 try {
355357 $obj = $dstc->create_object($dstRel);
356358 $obj->load_from_filename( $srcPath, True);
357359 } catch (SyntaxException $e) {
358 - throw new MWException( "missing required parameters" );
 360+ throw new MWException( 'missing required parameters' );
359361 } catch (BadContentTypeException $e) {
360 - throw new MWException( "No Content-Type was/could be set" );
 362+ throw new MWException( 'No Content-Type was/could be set' );
361363 } catch (InvalidResponseException $e) {
362364 throw new MWException( __METHOD__ . "unexpected response '$e'" );
363365 } catch (IOException $e) {
@@ -370,7 +372,7 @@
371373 * an Internal Error on them.
372374 *
373375 */
374 - function swift_delete( $container, $rel ) {
 376+ protected function swift_delete( $container, $rel ) {
375377 try {
376378 $container->delete_object($rel);
377379 } catch (SyntaxException $e) {
@@ -488,7 +490,7 @@
489491 */
490492
491493 function append( $srcPath, $toAppendPath, $flags = 0 ){
492 - throw new MWException( __METHOD__.": Not yet implemented." );
 494+ throw new MWException( __METHOD__.': Not yet implemented.' );
493495 // I think we need to count the number of files whose names
494496 // start with $toAppendPath, then add that count (with leading zeroes) to
495497 // the end of $toAppendPath and write the chunk there.
@@ -513,7 +515,7 @@
514516 */
515517 function appendFinish( $toAppendPath ){
516518 $conn = $this->connect();
517 - $container = $this->repo->get_container( $conn, $this->repo->container . "%2Ftemp" );
 519+ $container = $this->repo->get_container( $conn, $this->repo->container . '%2Ftemp' );
518520 $parts = $container->list_objects( 0, NULL, $srcPath ); // FIXME: $srcPath is undefined
519521 // list_objects() returns a sorted list.
520522
@@ -544,7 +546,7 @@
545547 * @return FileRepoStatus
546548 */
547549 function deleteBatch( $sourceDestPairs ) {
548 - wfDebug( __METHOD__ . " deleting " . var_export($sourceDestPairs, true) . "\n");
 550+ wfDebug( __METHOD__ . ' deleting ' . var_export($sourceDestPairs, true) . '\n');
549551
550552 /**
551553 * Move the files
@@ -602,7 +604,9 @@
603605
604606 // FIXME: do we really need to reject empty titles?
605607 function newFile( $title, $time = false ) {
606 - if ( empty($title) ) { return null; }
 608+ if ( empty($title) ) {
 609+ return null;
 610+ }
607611 return parent::newFile( $title, $time );
608612 }
609613
@@ -613,13 +617,13 @@
614618 * @param $dstContainer CF_Container
615619 * @param $dstRel String: relative path to the destination.
616620 */
617 - function swiftcopy($srcContainer, $srcRel, $dstContainer, $dstRel ) {
 621+ protected function swiftcopy($srcContainer, $srcRel, $dstContainer, $dstRel ) {
618622 // The destination must exist already.
619623 $obj = $dstContainer->create_object($dstRel);
620 - $obj->content_type = "text/plain";
 624+ $obj->content_type = 'text/plain';
621625
622626 try {
623 - $obj->write(".");
 627+ $obj->write('.');
624628 } catch (SyntaxException $e ) {
625629 throw new MWException( "Write failed: $e" );
626630 } catch (BadContentTypeException $e ) {
@@ -633,15 +637,15 @@
634638 try {
635639 $obj = $dstContainer->get_object($dstRel);
636640 } catch (NoSuchObjectException $e) {
637 - throw new MWException( "The object we just created does not exist: " . $dstContainer->name . "/$dstRel: $e" );
 641+ throw new MWException( 'The object we just created does not exist: ' . $dstContainer->name . "/$dstRel: $e" );
638642 }
639643
640 - wfDebug( __METHOD__ . " copying to " . $dstContainer->name . "/" . $dstRel . " from " . $srcContainer->name . "/" . $srcRel . "\n");
 644+ wfDebug( __METHOD__ . ' copying to ' . $dstContainer->name . "/$dstRel from " . $srcContainer->name . "/$srcRel\n");
641645
642646 try {
643 - $obj->copy($srcContainer->name . "/" . $srcRel);
 647+ $obj->copy($srcContainer->name . "/$srcRel");
644648 } catch (SyntaxException $e ) {
645 - throw new MWException( "Source file does not exist: " . $srcContainer->name . "/$srcRel: $e" );
 649+ throw new MWException( 'Source file does not exist: ' . $srcContainer->name . "/$srcRel: $e" );
646650 } catch (MisMatchedChecksumException $e ) {
647651 throw new MWException( "Checksums do not match: $e" );
648652 } catch (InvalidResponseException $e ) {
@@ -802,22 +806,22 @@
803807 * Makes no sense in our context -- don't let anybody call it.
804808 */
805809 function getZonePath( $zone ) {
806 - throw new MWException( __METHOD__.": not implemented" );
 810+ throw new MWException( __METHOD__.': not implemented' );
807811 }
808812
809813 /**
810814 * Get the Swift container corresponding to one of the three basic zones
811815 */
812 - function getZoneContainer( $zone ) {
 816+ protected function getZoneContainer( $zone ) {
813817 switch ( $zone ) {
814818 case 'public':
815819 return $this->container;
816820 case 'temp':
817 - return $this->container . "%2Ftemp";
 821+ return $this->container . '%2Ftemp';
818822 case 'deleted':
819 - return $this->container . "%2Fdeleted";
 823+ return $this->container . '%2Fdeleted';
820824 case 'thumb':
821 - return $this->container . "%2Fthumb";
 825+ return $this->container . '%2Fthumb';
822826 default:
823827 return false;
824828 }
@@ -826,9 +830,9 @@
827831 /**
828832 * Get a local path corresponding to a virtual URL
829833 */
830 - function getContainerRel( $url ) {
 834+ protected function getContainerRel( $url ) {
831835 if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
832 - throw new MWException( __METHOD__.": unknown protocol" );
 836+ throw new MWException( __METHOD__.': unknown protocol' );
833837 }
834838
835839 $bits = explode( '/', substr( $url, 9 ), 3 );
@@ -837,13 +841,12 @@
838842 }
839843 list( $repo, $zone, $rel ) = $bits;
840844 if ( $repo !== $this->name ) {
841 - throw new MWException( __METHOD__.": fetching from a foreign repo is not supported" );
 845+ throw new MWException( __METHOD__.': fetching from a foreign repo is not supported' );
842846 }
843847 $container = $this->getZoneContainer( $zone );
844848 if ( $container === false) {
845849 throw new MWException( __METHOD__.": invalid zone: $zone" );
846850 }
847 - wfDebug( __METHOD__.": Z$zone C$container R$rel\n" );
848851 return array($container, rawurldecode( $rel ));
849852 }
850853
@@ -882,10 +885,10 @@
883886 * SwiftFile->tempPath so it will be deleted when the object goes out of
884887 * scope.
885888 */
886 - function getLocalCopy($container, $rel) {
 889+ protected function getLocalCopy($container, $rel) {
887890
888891 // get a temporary place to put the original.
889 - $tempPath = tempnam( wfTempDir(), 'swift_in_' ) . "." . pathinfo( $rel, PATHINFO_EXTENSION );
 892+ $tempPath = tempnam( wfTempDir(), 'swift_in_' ) . '.' . pathinfo( $rel, PATHINFO_EXTENSION );
890893
891894 /* Fetch the image out of Swift */
892895 $conn = $this->connect();
@@ -897,7 +900,7 @@
898901 throw new MWException( "Unable to open original file at $container/$rel");
899902 }
900903
901 - wfDebug( __METHOD__ . " writing to " . $tempPath . "\n");
 904+ wfDebug( __METHOD__ . " writing to $tempPath\n");
902905 try {
903906 $obj->save_to_filename( $tempPath);
904907 } catch (IOException $e) {
@@ -910,11 +913,11 @@
911914 }
912915
913916
914 - /**
 917+ /**
915918 * Get properties of a file with a given virtual URL
916919 * The virtual URL must refer to this repo
917920 */
918 - function getFileProps( $virtualUrl ) {
 921+ function getFileProps( $virtualUrl ) {
919922 $path = $this->resolveVirtualUrl( $virtualUrl );
920923 $ret = File::getPropsFromPath( $path );
921924 unlink( $path );
@@ -1116,7 +1119,7 @@
11171120
11181121 # Don't destroy file info of missing files
11191122 if ( !$this->fileExists ) {
1120 - wfDebug( __METHOD__.": file does not exist, aborting\n" );
 1123+ wfDebug( __METHOD__.': file does not exist, aborting\n' );
11211124 wfProfileOut( __METHOD__ );
11221125 return;
11231126 }
@@ -1124,7 +1127,7 @@
11251128 $dbw = $this->repo->getMasterDB();
11261129 list( $major, $minor ) = self::splitMime( $this->mime );
11271130
1128 - wfDebug(__METHOD__.': upgrading '.$this->archive_name." to the current schema\n");
 1131+ wfDebug(__METHOD__.': upgrading '.$this->archive_name.' to the current schema\n');
11291132 $dbw->update( 'oldimage',
11301133 array(
11311134 'oi_width' => $this->width,
@@ -1174,3 +1177,205 @@
11751178 return Revision::userCanBitfield( $this->deleted, $field );
11761179 }
11771180 }
 1181+
 1182+/**
 1183+ * Foreign file with an accessible MediaWiki database
 1184+ *
 1185+ * @file
 1186+ * @ingroup FileRepo
 1187+ */
 1188+
 1189+/**
 1190+ * Foreign file with an accessible MediaWiki database
 1191+ *
 1192+ * @ingroup FileRepo
 1193+ */
 1194+class SwiftForeignDBFile extends SwiftFile {
 1195+ static function newFromTitle( $title, $repo, $unused = null ) {
 1196+ return new self( $title, $repo );
 1197+ }
 1198+
 1199+ /**
 1200+ * Create a ForeignDBFile from a title
 1201+ * Do not call this except from inside a repo class.
 1202+ */
 1203+ static function newFromRow( $row, $repo ) {
 1204+ $title = Title::makeTitle( NS_FILE, $row->img_name );
 1205+ $file = new self( $title, $repo );
 1206+ $file->loadFromRow( $row );
 1207+ return $file;
 1208+ }
 1209+
 1210+ function publish( $srcPath, $flags = 0 ) {
 1211+ $this->readOnlyError();
 1212+ }
 1213+
 1214+ function recordUpload( $oldver, $desc, $license = '', $copyStatus = '', $source = '',
 1215+ $watch = false, $timestamp = false ) {
 1216+ $this->readOnlyError();
 1217+ }
 1218+ function restore( $versions = array(), $unsuppress = false ) {
 1219+ $this->readOnlyError();
 1220+ }
 1221+ function delete( $reason, $suppress = false ) {
 1222+ $this->readOnlyError();
 1223+ }
 1224+ function move( $target ) {
 1225+ $this->readOnlyError();
 1226+ }
 1227+
 1228+ function getDescriptionUrl() {
 1229+ // Restore remote behaviour
 1230+ return File::getDescriptionUrl();
 1231+ }
 1232+
 1233+ function getDescriptionText() {
 1234+ // Restore remote behaviour
 1235+ return File::getDescriptionText();
 1236+ }
 1237+}
 1238+
 1239+/**
 1240+ * A foreign repository with an accessible MediaWiki database
 1241+ *
 1242+ * @file
 1243+ * @ingroup FileRepo
 1244+ */
 1245+
 1246+/**
 1247+ * A foreign repository with an accessible MediaWiki database
 1248+ *
 1249+ * @ingroup FileRepo
 1250+ */
 1251+class SwiftForeignDBRepo extends SwiftRepo {
 1252+ # Settings
 1253+ var $dbType, $dbServer, $dbUser, $dbPassword, $dbName, $dbFlags,
 1254+ $tablePrefix, $hasSharedCache;
 1255+
 1256+ # Other stuff
 1257+ var $dbConn;
 1258+ var $fileFactory = array( 'SwiftForeignDBFile', 'newFromTitle' );
 1259+ var $fileFromRowFactory = array( 'SwiftForeignDBFile', 'newFromRow' );
 1260+
 1261+ function __construct( $info ) {
 1262+ parent::__construct( $info );
 1263+ $this->dbType = $info['dbType'];
 1264+ $this->dbServer = $info['dbServer'];
 1265+ $this->dbUser = $info['dbUser'];
 1266+ $this->dbPassword = $info['dbPassword'];
 1267+ $this->dbName = $info['dbName'];
 1268+ $this->dbFlags = $info['dbFlags'];
 1269+ $this->tablePrefix = $info['tablePrefix'];
 1270+ $this->hasSharedCache = $info['hasSharedCache'];
 1271+ }
 1272+
 1273+ function getMasterDB() {
 1274+ if ( !isset( $this->dbConn ) ) {
 1275+ $this->dbConn = DatabaseBase::newFromType( $this->dbType,
 1276+ array(
 1277+ 'server' => $this->dbServer,
 1278+ 'user' => $this->dbUser,
 1279+ 'password' => $this->dbPassword,
 1280+ 'dbname' => $this->dbName,
 1281+ 'flags' => $this->dbFlags,
 1282+ 'tableprefix' => $this->tablePrefix
 1283+ )
 1284+ );
 1285+ }
 1286+ return $this->dbConn;
 1287+ }
 1288+
 1289+ function getSlaveDB() {
 1290+ return $this->getMasterDB();
 1291+ }
 1292+
 1293+ function hasSharedCache() {
 1294+ return $this->hasSharedCache;
 1295+ }
 1296+
 1297+ /**
 1298+ * Get a key on the primary cache for this repository.
 1299+ * Returns false if the repository's cache is not accessible at this site.
 1300+ * The parameters are the parts of the key, as for wfMemcKey().
 1301+ */
 1302+ function getSharedCacheKey( /*...*/ ) {
 1303+ if ( $this->hasSharedCache() ) {
 1304+ $args = func_get_args();
 1305+ array_unshift( $args, $this->dbName, $this->tablePrefix );
 1306+ return call_user_func_array( 'wfForeignMemcKey', $args );
 1307+ } else {
 1308+ return false;
 1309+ }
 1310+ }
 1311+
 1312+ function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
 1313+ throw new MWException( get_class($this) . ': write operations are not supported' );
 1314+ }
 1315+ function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ) {
 1316+ throw new MWException( get_class($this) . ': write operations are not supported' );
 1317+ }
 1318+ function deleteBatch( $sourceDestPairs ) {
 1319+ throw new MWException( get_class($this) . ': write operations are not supported' );
 1320+ }
 1321+}
 1322+
 1323+/**
 1324+ * A foreign repository with a MediaWiki database accessible via the configured LBFactory
 1325+ *
 1326+ * @file
 1327+ * @ingroup FileRepo
 1328+ */
 1329+
 1330+/**
 1331+ * A foreign repository with a MediaWiki database accessible via the configured LBFactory
 1332+ *
 1333+ * @ingroup FileRepo
 1334+ */
 1335+class SwiftForeignDBViaLBRepo extends LocalRepo {
 1336+ var $wiki, $dbName, $tablePrefix;
 1337+ var $fileFactory = array( 'SwiftForeignDBFile', 'newFromTitle' );
 1338+ var $fileFromRowFactory = array( 'SwiftForeignDBFile', 'newFromRow' );
 1339+
 1340+ function __construct( $info ) {
 1341+ parent::__construct( $info );
 1342+ $this->wiki = $info['wiki'];
 1343+ list( $this->dbName, $this->tablePrefix ) = wfSplitWikiID( $this->wiki );
 1344+ $this->hasSharedCache = $info['hasSharedCache'];
 1345+ }
 1346+
 1347+ function getMasterDB() {
 1348+ return wfGetDB( DB_MASTER, array(), $this->wiki );
 1349+ }
 1350+
 1351+ function getSlaveDB() {
 1352+ return wfGetDB( DB_SLAVE, array(), $this->wiki );
 1353+ }
 1354+ function hasSharedCache() {
 1355+ return $this->hasSharedCache;
 1356+ }
 1357+
 1358+ /**
 1359+ * Get a key on the primary cache for this repository.
 1360+ * Returns false if the repository's cache is not accessible at this site.
 1361+ * The parameters are the parts of the key, as for wfMemcKey().
 1362+ */
 1363+ function getSharedCacheKey( /*...*/ ) {
 1364+ if ( $this->hasSharedCache() ) {
 1365+ $args = func_get_args();
 1366+ array_unshift( $args, $this->wiki );
 1367+ return implode( ':', $args );
 1368+ } else {
 1369+ return false;
 1370+ }
 1371+ }
 1372+
 1373+ function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
 1374+ throw new MWException( get_class($this) . ': write operations are not supported' );
 1375+ }
 1376+ function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ) {
 1377+ throw new MWException( get_class($this) . ': write operations are not supported' );
 1378+ }
 1379+ function deleteBatch( $fileMap ) {
 1380+ throw new MWException( get_class($this) . ': write operations are not supported' );
 1381+ }
 1382+}
Index: trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles.php
@@ -0,0 +1,2352 @@
 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_exceptions.php
@@ -0,0 +1,41 @@
 2+<?php
 3+/**
 4+ * Custom Exceptions for the CloudFiles API
 5+ *
 6+ * Requres PHP 5.x (for Exceptions and OO syntax)
 7+ *
 8+ * See COPYING for license information.
 9+ *
 10+ * @author Eric "EJ" Johnson <ej@racklabs.com>
 11+ * @copyright Copyright (c) 2008, Rackspace US, Inc.
 12+ * @package php-cloudfiles-exceptions
 13+ */
 14+
 15+/**
 16+ * Custom Exceptions for the CloudFiles API
 17+ * @package php-cloudfiles-exceptions
 18+ */
 19+class SyntaxException extends Exception { }
 20+class AuthenticationException extends Exception { }
 21+class InvalidResponseException extends Exception { }
 22+class NonEmptyContainerException extends Exception { }
 23+class NoSuchObjectException extends Exception { }
 24+class NoSuchContainerException extends Exception { }
 25+class NoSuchAccountException extends Exception { }
 26+class MisMatchedChecksumException extends Exception { }
 27+class IOException extends Exception { }
 28+class CDNNotEnabledException extends Exception { }
 29+class BadContentTypeException extends Exception { }
 30+class InvalidUTF8Exception extends Exception { }
 31+class ConnectionNotOpenException extends Exception { }
 32+
 33+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 34+
 35+/*
 36+ * Local variables:
 37+ * tab-width: 4
 38+ * c-basic-offset: 4
 39+ * c-hanging-comment-ender-p: nil
 40+ * End:
 41+ */
 42+?>
Index: trunk/extensions/SwiftMedia/php-cloudfiles/cloudfiles_http.php
@@ -0,0 +1,1397 @@
 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+?>

Status & tagging log