107 parent::__construct( $config );
109 $this->swiftAuthUrl = $config[
'swiftAuthUrl'];
110 $this->swiftUser = $config[
'swiftUser'];
111 $this->swiftKey = $config[
'swiftKey'];
113 $this->authTTL = isset( $config[
'swiftAuthTTL'] )
114 ? $config[
'swiftAuthTTL']
116 $this->swiftTempUrlKey = isset( $config[
'swiftTempUrlKey'] )
117 ? $config[
'swiftTempUrlKey']
119 $this->shardViaHashLevels = isset( $config[
'shardViaHashLevels'] )
120 ? $config[
'shardViaHashLevels']
122 $this->rgwS3AccessKey = isset( $config[
'rgwS3AccessKey'] )
123 ? $config[
'rgwS3AccessKey']
125 $this->rgwS3SecretKey = isset( $config[
'rgwS3SecretKey'] )
126 ? $config[
'rgwS3SecretKey']
135 if ( !empty( $config[
'cacheAuthInfo'] ) ) {
136 if ( PHP_SAPI ===
'cli' ) {
141 }
catch ( Exception
$e ) {
153 if ( !mb_check_encoding( $relStoragePath,
'UTF-8' ) ) {
155 } elseif ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
159 return $relStoragePath;
164 if ( $rel ===
null ) {
182 if ( isset(
$params[
'headers'] ) ) {
185 if ( preg_match(
'/^content-(type|length)$/',
$name ) ) {
187 } elseif ( preg_match(
'/^(x-)?content-/',
$name ) ) {
189 } elseif ( preg_match(
'/^content-(disposition)/',
$name ) ) {
195 if ( isset( $headers[
'content-disposition'] ) ) {
197 foreach ( explode(
';', $headers[
'content-disposition'] )
as $part ) {
198 $part = trim( $part );
199 $new = ( $disposition ===
'' ) ? $part :
"{$disposition};{$part}";
200 if ( strlen( $new ) <= 255 ) {
206 $headers[
'content-disposition'] = $disposition;
216 if ( $dstRel ===
null ) {
217 $status->fatal(
'backend-fail-invalidpath',
$params[
'dst'] );
227 'url' =>
array( $dstCont, $dstRel ),
229 'content-length' => strlen(
$params[
'content'] ),
230 'etag' => md5(
$params[
'content'] ),
231 'content-type' => $contentType,
232 'x-object-meta-sha1base36' => $sha1Hash
238 $method = __METHOD__;
239 $handler =
function (
array $request,
Status $status ) use ( $be, $method,
$params ) {
240 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request[
'response'];
241 if ( $rcode === 201 ) {
243 } elseif ( $rcode === 412 ) {
244 $status->fatal(
'backend-fail-contenttype',
$params[
'dst'] );
246 $be->onError( $status, $method,
$params, $rerr, $rcode, $rdesc );
251 if ( !empty(
$params[
'async'] ) ) {
252 $status->value = $opHandle;
264 if ( $dstRel ===
null ) {
265 $status->fatal(
'backend-fail-invalidpath',
$params[
'dst'] );
271 $sha1Hash = sha1_file(
$params[
'src'] );
273 if ( $sha1Hash ===
false ) {
274 $status->fatal(
'backend-fail-store',
$params[
'src'],
$params[
'dst'] );
281 $handle = fopen(
$params[
'src'],
'rb' );
282 if ( $handle ===
false ) {
283 $status->fatal(
'backend-fail-store',
$params[
'src'],
$params[
'dst'] );
290 'url' =>
array( $dstCont, $dstRel ),
292 'content-length' => filesize(
$params[
'src'] ),
293 'etag' => md5_file(
$params[
'src'] ),
294 'content-type' => $contentType,
295 'x-object-meta-sha1base36' => $sha1Hash
301 $method = __METHOD__;
302 $handler =
function (
array $request,
Status $status ) use ( $be, $method,
$params ) {
303 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request[
'response'];
304 if ( $rcode === 201 ) {
306 } elseif ( $rcode === 412 ) {
307 $status->fatal(
'backend-fail-contenttype',
$params[
'dst'] );
309 $be->onError( $status, $method,
$params, $rerr, $rcode, $rdesc );
314 if ( !empty(
$params[
'async'] ) ) {
315 $status->value = $opHandle;
327 if ( $srcRel ===
null ) {
328 $status->fatal(
'backend-fail-invalidpath',
$params[
'src'] );
334 if ( $dstRel ===
null ) {
335 $status->fatal(
'backend-fail-invalidpath',
$params[
'dst'] );
342 'url' =>
array( $dstCont, $dstRel ),
344 'x-copy-from' =>
'/' . rawurlencode( $srcCont ) .
345 '/' . str_replace(
"%2F",
"/", rawurlencode( $srcRel ) )
350 $method = __METHOD__;
351 $handler =
function (
array $request,
Status $status ) use ( $be, $method,
$params ) {
352 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request[
'response'];
353 if ( $rcode === 201 ) {
355 } elseif ( $rcode === 404 ) {
356 $status->fatal(
'backend-fail-copy',
$params[
'src'],
$params[
'dst'] );
358 $be->onError( $status, $method,
$params, $rerr, $rcode, $rdesc );
363 if ( !empty(
$params[
'async'] ) ) {
364 $status->value = $opHandle;
376 if ( $srcRel ===
null ) {
377 $status->fatal(
'backend-fail-invalidpath',
$params[
'src'] );
383 if ( $dstRel ===
null ) {
384 $status->fatal(
'backend-fail-invalidpath',
$params[
'dst'] );
392 'url' =>
array( $dstCont, $dstRel ),
394 'x-copy-from' =>
'/' . rawurlencode( $srcCont ) .
395 '/' . str_replace(
"%2F",
"/", rawurlencode( $srcRel ) )
399 if (
"{$srcCont}/{$srcRel}" !==
"{$dstCont}/{$dstRel}" ) {
401 'method' =>
'DELETE',
402 'url' =>
array( $srcCont, $srcRel ),
408 $method = __METHOD__;
409 $handler =
function (
array $request,
Status $status ) use ( $be, $method,
$params ) {
410 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request[
'response'];
411 if ( $request[
'method'] ===
'PUT' && $rcode === 201 ) {
413 } elseif ( $request[
'method'] ===
'DELETE' && $rcode === 204 ) {
415 } elseif ( $rcode === 404 ) {
416 $status->fatal(
'backend-fail-move',
$params[
'src'],
$params[
'dst'] );
418 $be->onError( $status, $method,
$params, $rerr, $rcode, $rdesc );
423 if ( !empty(
$params[
'async'] ) ) {
424 $status->value = $opHandle;
436 if ( $srcRel ===
null ) {
437 $status->fatal(
'backend-fail-invalidpath',
$params[
'src'] );
443 'method' =>
'DELETE',
444 'url' =>
array( $srcCont, $srcRel ),
449 $method = __METHOD__;
450 $handler =
function (
array $request,
Status $status ) use ( $be, $method,
$params ) {
451 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request[
'response'];
452 if ( $rcode === 204 ) {
454 } elseif ( $rcode === 404 ) {
455 if ( empty(
$params[
'ignoreMissingSource'] ) ) {
456 $status->fatal(
'backend-fail-delete',
$params[
'src'] );
459 $be->onError( $status, $method,
$params, $rerr, $rcode, $rdesc );
464 if ( !empty(
$params[
'async'] ) ) {
465 $status->value = $opHandle;
477 if ( $srcRel ===
null ) {
478 $status->fatal(
'backend-fail-invalidpath',
$params[
'src'] );
484 $stat = $this->
getFileStat(
array(
'src' => $params[
'src'],
'latest' => 1 ) );
485 if ( $stat && !isset( $stat[
'xattr'] ) ) {
489 $status->fatal(
'backend-fail-describe',
$params[
'src'] );
497 $metaHdrs[
"x-object-meta-$name"] =
$value;
499 $customHdrs = $this->
sanitizeHdrs( $params ) + $stat[
'xattr'][
'headers'];
503 'url' =>
array( $srcCont, $srcRel ),
504 'headers' => $metaHdrs + $customHdrs
508 $method = __METHOD__;
509 $handler =
function (
array $request,
Status $status ) use ( $be, $method,
$params ) {
510 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request[
'response'];
511 if ( $rcode === 202 ) {
513 } elseif ( $rcode === 404 ) {
514 $status->fatal(
'backend-fail-describe',
$params[
'src'] );
516 $be->onError( $status, $method,
$params, $rerr, $rcode, $rdesc );
521 if ( !empty(
$params[
'async'] ) ) {
522 $status->value = $opHandle;
535 if ( is_array( $stat ) ) {
537 } elseif ( $stat ===
null ) {
538 $status->fatal(
'backend-fail-internal', $this->
name );
544 if ( $stat ===
false ) {
554 if ( empty(
$params[
'noAccess'] ) ) {
559 if ( is_array( $stat ) ) {
563 array( $this->swiftUser ),
564 array( $this->swiftUser )
566 } elseif ( $stat ===
false ) {
567 $status->fatal(
'backend-fail-usable',
$params[
'dir'] );
569 $status->fatal(
'backend-fail-internal', $this->
name );
579 if ( is_array( $stat ) ) {
583 array( $this->swiftUser,
'.r:*' ),
584 array( $this->swiftUser )
586 } elseif ( $stat ===
false ) {
587 $status->fatal(
'backend-fail-usable',
$params[
'dir'] );
589 $status->fatal(
'backend-fail-internal', $this->
name );
605 if ( $stat ===
false ) {
607 } elseif ( !is_array( $stat ) ) {
608 $status->fatal(
'backend-fail-internal', $this->
name );
614 if ( $stat[
'count'] == 0 ) {
627 return reset( $stats );
658 if ( isset( $objHdrs[
'x-object-meta-sha1base36'] ) ) {
663 trigger_error(
"$path was not stored with SHA-1 metadata.", E_USER_WARNING );
667 $objHdrs[
'x-object-meta-sha1base36'] =
false;
674 if ( $status->isOK() ) {
677 $hash = $tmpFile->getSha1Base36();
678 if (
$hash !==
false ) {
679 $objHdrs[
'x-object-meta-sha1base36'] =
$hash;
681 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
683 'url' => $this->
storageUrl( $auth, $srcCont, $srcRel ),
686 if ( $rcode >= 200 && $rcode <= 299 ) {
692 trigger_error(
"Unable to set SHA-1 metadata for $path", E_USER_WARNING );
693 $objHdrs[
'x-object-meta-sha1base36'] =
false;
710 if ( $srcRel ===
null || !$auth ) {
711 $contents[
$path] =
false;
715 $handle = fopen(
'php://temp',
'wb' );
719 'url' => $this->
storageUrl( $auth, $srcCont, $srcRel ),
725 $contents[
$path] =
false;
728 $opts =
array(
'maxConnsPerHost' =>
$params[
'concurrency'] );
729 $reqs = $this->http->runMulti( $reqs, $opts );
730 foreach ( $reqs
as $path => $op ) {
731 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $op[
'response'];
732 if ( $rcode >= 200 && $rcode <= 299 ) {
733 rewind( $op[
'stream'] );
734 $contents[
$path] = stream_get_contents( $op[
'stream'] );
735 } elseif ( $rcode === 404 ) {
736 $contents[
$path] =
false;
738 $this->
onError(
null, __METHOD__,
739 array(
'src' =>
$path ) + $ep, $rerr, $rcode, $rdesc );
741 fclose( $op[
'stream'] );
748 $prefix = (
$dir ==
'' ) ?
null :
"{$dir}/";
749 $status = $this->
objectListing( $fullCont,
'names', 1,
null, $prefix );
750 if ( $status->isOk() ) {
751 return ( count( $status->value ) ) > 0;
792 if ( $after === INF ) {
798 $prefix = (
$dir ==
'' ) ?
null :
"{$dir}/";
800 if ( !empty(
$params[
'topOnly'] ) ) {
802 if ( !$status->isOk() ) {
805 $objects = $status->value;
806 foreach ( $objects
as $object ) {
807 if ( substr( $object, -1 ) ===
'/' ) {
813 $getParentDir =
function (
$path ) {
818 $lastDir = $getParentDir( $after );
821 if ( !$status->isOk() ) {
825 $objects = $status->value;
827 foreach ( $objects
as $object ) {
828 $objectDir = $getParentDir( $object );
830 if ( $objectDir !==
false && $objectDir !==
$dir ) {
835 if ( strcmp( $objectDir, $lastDir ) > 0 ) {
838 $dirs[] =
"{$pDir}/";
839 $pDir = $getParentDir( $pDir );
840 }
while ( $pDir !==
false
841 && strcmp( $pDir, $lastDir ) > 0
842 && strlen( $pDir ) > strlen(
$dir )
845 $lastDir = $objectDir;
850 if ( count( $objects ) <
$limit ) {
853 $after = end( $objects );
872 if ( $after === INF ) {
878 $prefix = (
$dir ==
'' ) ?
null :
"{$dir}/";
881 if ( !empty(
$params[
'topOnly'] ) ) {
882 if ( !empty(
$params[
'adviseStat'] ) ) {
889 if ( !empty(
$params[
'adviseStat'] ) ) {
897 if ( !$status->isOk() ) {
901 $objects = $status->value;
905 if ( count( $objects ) <
$limit ) {
908 $after = end( $objects );
909 $after = is_object( $after ) ? $after->name : $after;
926 foreach ( $objects
as $object ) {
927 if ( is_object( $object ) ) {
928 if ( isset( $object->subdir ) || !isset( $object->name ) ) {
934 'size' => (
int)$object->bytes,
936 'md5' => ctype_xdigit( $object->hash ) ? $object->hash :
null,
939 $names[] =
array( $object->name, $stat );
940 } elseif ( substr( $object, -1 ) !==
'/' ) {
942 $names[] =
array( $object,
null );
956 $this->cheapCache->set(
$path,
'stat', $val );
962 if ( !isset( $stat[
'xattr'] ) ) {
968 return $stat[
'xattr'];
977 if ( !isset( $stat[
'sha1'] ) ) {
983 return $stat[
'sha1'];
993 if ( $srcRel ===
null ) {
994 $status->fatal(
'backend-fail-invalidpath',
$params[
'src'] );
999 $status->fatal(
'backend-fail-stream',
$params[
'src'] );
1004 $handle = fopen(
'php://output',
'wb' );
1006 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1008 'url' => $this->
storageUrl( $auth, $srcCont, $srcRel ),
1011 'stream' => $handle,
1014 if ( $rcode >= 200 && $rcode <= 299 ) {
1016 } elseif ( $rcode === 404 ) {
1017 $status->fatal(
'backend-fail-stream',
$params[
'src'] );
1019 $this->
onError( $status, __METHOD__,
$params, $rerr, $rcode, $rdesc );
1026 $tmpFiles =
array();
1037 if ( $srcRel ===
null || !$auth ) {
1038 $tmpFiles[
$path] =
null;
1046 $handle = fopen( $tmpFile->getPath(),
'wb' );
1050 'url' => $this->
storageUrl( $auth, $srcCont, $srcRel ),
1053 'stream' => $handle,
1059 $tmpFiles[
$path] = $tmpFile;
1062 $opts =
array(
'maxConnsPerHost' =>
$params[
'concurrency'] );
1063 $reqs = $this->http->runMulti( $reqs, $opts );
1064 foreach ( $reqs
as $path => $op ) {
1065 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $op[
'response'];
1066 fclose( $op[
'stream'] );
1067 if ( $rcode >= 200 && $rcode <= 299
1069 && $tmpFiles[
$path]->getSize() == $rhdrs[
'content-length']
1072 } elseif ( $rcode === 404 ) {
1073 $tmpFiles[
$path] =
false;
1075 $tmpFiles[
$path] =
null;
1076 $this->
onError(
null, __METHOD__,
1077 array(
'src' =>
$path ) + $ep, $rerr, $rcode, $rdesc );
1085 if ( $this->swiftTempUrlKey !=
'' ||
1086 ( $this->rgwS3AccessKey !=
'' && $this->rgwS3SecretKey !=
'' )
1089 if ( $srcRel ===
null ) {
1099 $expires = time() + $ttl;
1101 if ( $this->swiftTempUrlKey !=
'' ) {
1102 $url = $this->
storageUrl( $auth, $srcCont, $srcRel );
1104 $contPath = parse_url( $this->
storageUrl( $auth, $srcCont ), PHP_URL_PATH );
1105 $signature = hash_hmac(
'sha1',
1106 "GET\n{$expires}\n{$contPath}/{$srcRel}",
1107 $this->swiftTempUrlKey
1110 return "{$url}?temp_url_sig={$signature}&temp_url_expires={$expires}";
1113 $spath =
'/' . rawurlencode( $srcCont ) .
'/' .
1114 str_replace(
'%2F',
'/', rawurlencode( $srcRel ) );
1116 $signature = base64_encode( hash_hmac(
1118 "GET\n\n\n{$expires}\n{$spath}",
1119 $this->rgwS3SecretKey,
1125 str_replace(
'/swift/v1',
'',
1128 'Signature' => $signature,
1129 'Expires' => $expires,
1130 'AWSAccessKeyId' => $this->rgwS3AccessKey )
1152 if ( !empty(
$params[
'latest'] ) ) {
1153 $hdrs[
'x-newest'] =
'true';
1160 $statuses =
array();
1164 foreach ( $fileOpHandles
as $index => $fileOpHandle ) {
1172 $httpReqsByStage =
array();
1173 foreach ( $fileOpHandles
as $index => $fileOpHandle ) {
1174 $reqs = $fileOpHandle->httpOp;
1176 foreach ( $reqs
as $stage => &$req ) {
1177 list( $container, $relPath ) = $req[
'url'];
1178 $req[
'url'] = $this->
storageUrl( $auth, $container, $relPath );
1179 $req[
'headers'] = isset( $req[
'headers'] ) ? $req[
'headers'] :
array();
1181 $httpReqsByStage[$stage][$index] = $req;
1187 $reqCount = count( $httpReqsByStage );
1188 for ( $stage = 0; $stage < $reqCount; ++$stage ) {
1189 $httpReqs = $this->http->runMulti( $httpReqsByStage[$stage] );
1190 foreach ( $httpReqs
as $index => $httpReq ) {
1192 $callback = $fileOpHandles[$index]->callback;
1193 call_user_func_array( $callback,
array( $httpReq, $statuses[$index] ) );
1196 if ( !$statuses[$index]->isOK() ) {
1197 $stages = count( $fileOpHandles[$index]->httpOp );
1198 for (
$s = ( $stage + 1 );
$s < $stages; ++
$s ) {
1199 unset( $httpReqsByStage[
$s][$index] );
1235 $status->fatal(
'backend-fail-connect', $this->
name );
1240 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1242 'url' => $this->
storageUrl( $auth, $container ),
1244 'x-container-read' => implode(
',', $readGrps ),
1245 'x-container-write' => implode(
',', $writeGrps )
1249 if ( $rcode != 204 && $rcode !== 202 ) {
1250 $status->fatal(
'backend-fail-internal', $this->
name );
1267 if ( $bypassCache ) {
1268 $this->containerStatCache->clear( $container );
1269 } elseif ( !$this->containerStatCache->has( $container,
'stat' ) ) {
1272 if ( !$this->containerStatCache->has( $container,
'stat' ) ) {
1278 wfProfileIn( __METHOD__ .
"-{$this->name}-miss" );
1279 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1281 'url' => $this->
storageUrl( $auth, $container ),
1286 if ( $rcode === 204 ) {
1288 'count' => $rhdrs[
'x-container-object-count'],
1289 'bytes' => $rhdrs[
'x-container-bytes-used']
1291 if ( $bypassCache ) {
1294 $this->containerStatCache->set( $container,
'stat', $stat );
1297 } elseif ( $rcode === 404 ) {
1300 $this->
onError(
null, __METHOD__,
1301 array(
'cont' => $container ), $rerr, $rcode, $rdesc );
1307 return $this->containerStatCache->get( $container,
'stat' );
1322 $status->fatal(
'backend-fail-connect', $this->name );
1328 if ( empty(
$params[
'noAccess'] ) ) {
1329 $readGrps =
array(
'.r:*', $this->swiftUser );
1331 $readGrps =
array( $this->swiftUser );
1333 $writeGrps =
array( $this->swiftUser );
1335 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1337 'url' => $this->
storageUrl( $auth, $container ),
1339 'x-container-read' => implode(
',', $readGrps ),
1340 'x-container-write' => implode(
',', $writeGrps )
1344 if ( $rcode === 201 ) {
1346 } elseif ( $rcode === 202 ) {
1349 $this->
onError( $status, __METHOD__,
$params, $rerr, $rcode, $rdesc );
1367 $status->fatal(
'backend-fail-connect', $this->name );
1372 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1373 'method' =>
'DELETE',
1374 'url' => $this->
storageUrl( $auth, $container ),
1378 if ( $rcode >= 200 && $rcode <= 299 ) {
1379 $this->containerStatCache->clear( $container );
1380 } elseif ( $rcode === 404 ) {
1382 } elseif ( $rcode === 409 ) {
1383 $this->
onError( $status, __METHOD__,
$params, $rerr, $rcode, $rdesc );
1385 $this->
onError( $status, __METHOD__,
$params, $rerr, $rcode, $rdesc );
1404 $fullCont,
$type,
$limit, $after =
null, $prefix =
null, $delim =
null
1410 $status->fatal(
'backend-fail-connect', $this->name );
1416 if (
$type ===
'info' ) {
1417 $query[
'format'] =
'json';
1419 if ( $after !==
null ) {
1420 $query[
'marker'] = $after;
1422 if ( $prefix !==
null ) {
1423 $query[
'prefix'] = $prefix;
1425 if ( $delim !==
null ) {
1426 $query[
'delimiter'] = $delim;
1429 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1431 'url' => $this->
storageUrl( $auth, $fullCont ),
1436 $params =
array(
'cont' => $fullCont,
'prefix' => $prefix,
'delim' => $delim );
1437 if ( $rcode === 200 ) {
1438 if (
$type ===
'info' ) {
1441 $status->value = explode(
"\n", trim( $rbody ) );
1443 } elseif ( $rcode === 204 ) {
1444 $status->value =
array();
1445 } elseif ( $rcode === 404 ) {
1448 $this->
onError( $status, __METHOD__,
$params, $rerr, $rcode, $rdesc );
1455 foreach ( $containerInfo
as $container => $info ) {
1456 $this->containerStatCache->set( $container,
'stat', $info );
1468 if ( $srcRel ===
null ) {
1469 $stats[
$path] =
false;
1471 } elseif ( !$auth ) {
1472 $stats[
$path] =
null;
1478 if ( $cstat ===
false ) {
1479 $stats[
$path] =
false;
1481 } elseif ( !is_array( $cstat ) ) {
1482 $stats[
$path] =
null;
1488 'url' => $this->
storageUrl( $auth, $srcCont, $srcRel ),
1493 $opts =
array(
'maxConnsPerHost' =>
$params[
'concurrency'] );
1494 $reqs = $this->http->runMulti( $reqs, $opts );
1497 if ( array_key_exists(
$path, $stats ) ) {
1501 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $reqs[
$path][
'response'];
1502 if ( $rcode === 200 || $rcode === 204 ) {
1506 $metadata =
array();
1508 if ( strpos(
$name,
'x-object-meta-' ) === 0 ) {
1509 $metadata[substr(
$name, strlen(
'x-object-meta-' ) )] =
$value;
1518 'size' => isset( $rhdrs[
'content-length'] ) ? (
int)$rhdrs[
'content-length'] : 0,
1519 'sha1' => $rhdrs[
'x-object-meta-sha1base36'],
1521 'md5' => ctype_xdigit( $rhdrs[
'etag'] ) ? $rhdrs[
'etag'] :
null,
1522 'xattr' =>
array(
'metadata' => $metadata,
'headers' => $headers )
1524 if ( $this->isRGW ) {
1525 $stat[
'latest'] =
true;
1527 } elseif ( $rcode === 404 ) {
1531 $this->
onError(
null, __METHOD__,
$params, $rerr, $rcode, $rdesc );
1533 $stats[
$path] = $stat;
1543 if ( $this->authErrorTimestamp !==
null ) {
1544 if ( ( time() - $this->authErrorTimestamp ) < 60 ) {
1547 $this->authErrorTimestamp =
null;
1553 if ( !$this->authCreds || $reAuth ) {
1554 $this->authSessionTimestamp = 0;
1556 $creds = $this->srvCache->get( $cacheKey );
1558 if ( isset( $creds[
'auth_token'] ) && isset( $creds[
'storage_url'] ) ) {
1559 $this->authCreds = $creds;
1561 $this->authSessionTimestamp = time() - ceil( $this->authTTL / 2 );
1563 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->http->run(
array(
1565 'url' =>
"{$this->swiftAuthUrl}/v1.0",
1567 'x-auth-user' => $this->swiftUser,
1568 'x-auth-key' => $this->swiftKey
1572 if ( $rcode >= 200 && $rcode <= 299 ) {
1573 $this->authCreds =
array(
1574 'auth_token' => $rhdrs[
'x-auth-token'],
1575 'storage_url' => $rhdrs[
'x-storage-url']
1577 $this->srvCache->set( $cacheKey, $this->authCreds, ceil( $this->authTTL / 2 ) );
1578 $this->authSessionTimestamp = time();
1579 } elseif ( $rcode === 401 ) {
1580 $this->
onError(
null, __METHOD__,
array(),
"Authentication failed.", $rcode );
1581 $this->authErrorTimestamp = time();
1585 $this->
onError(
null, __METHOD__,
array(),
"HTTP return code: $rcode", $rcode );
1586 $this->authErrorTimestamp = time();
1592 if ( substr( $this->authCreds[
'storage_url'], -3 ) ===
'/v1' ) {
1593 $this->isRGW =
true;
1606 protected function storageUrl(
array $creds, $container =
null, $object =
null ) {
1607 $parts =
array( $creds[
'storage_url'] );
1608 if ( strlen( $container ) ) {
1609 $parts[] = rawurlencode( $container );
1611 if ( strlen( $object ) ) {
1612 $parts[] = str_replace(
"%2F",
"/", rawurlencode( $object ) );
1615 return implode(
'/', $parts );
1623 return array(
'x-auth-token' => $creds[
'auth_token'] );
1633 return 'swiftcredentials:' . md5( $username .
':' . $this->swiftAuthUrl );
1647 public function onError( $status, $func,
array $params, $err =
'', $code = 0, $desc =
'' ) {
1648 if ( $status instanceof
Status ) {
1649 $status->fatal(
'backend-fail-internal', $this->name );
1651 if ( $code == 401 ) {
1656 ( $err ?
": $err" :
"" )
1724 $this->container = $fullCont;
1726 if ( substr( $this->dir, -1 ) ===
'/' ) {
1727 $this->dir = substr( $this->dir, 0, -1 );
1729 if ( $this->dir ==
'' ) {
1730 $this->suffixStart = 0;
1732 $this->suffixStart = strlen( $this->dir ) + 1;
1741 public function key() {
1748 public function next() {
1750 next( $this->bufferIter );
1754 if ( !$this->
valid() && count( $this->bufferIter ) ) {
1756 $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
1764 public function rewind() {
1766 $this->bufferAfter =
null;
1768 $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE, $this->params
1777 if ( $this->bufferIter ===
null ) {
1780 return ( current( $this->bufferIter ) !==
false );
1806 return substr(
current( $this->bufferIter ), $this->suffixStart, -1 );
1824 $relPath = substr(
$path, $this->suffixStart );
1825 if ( is_array( $stat ) ) {
1826 $storageDir = rtrim( $this->params[
'dir'],
'/' );
1827 $this->backend->loadListingStatInternal(
"$storageDir/$relPath", $stat );