Index: trunk/phase3/tests/phpunit/includes/filerepo/FileBackendTest.php |
— | — | @@ -412,21 +412,22 @@ |
413 | 413 | /** |
414 | 414 | * @dataProvider provider_testCreate |
415 | 415 | */ |
416 | | - public function testCreate( $op, $dest, $alreadyExists, $okStatus, $newSize ) { |
| 416 | + public function testCreate( $op, $alreadyExists, $okStatus, $newSize ) { |
417 | 417 | $this->backend = $this->singleBackend; |
418 | 418 | $this->tearDownFiles(); |
419 | | - $this->doTestCreate( $op, $dest, $alreadyExists, $okStatus, $newSize ); |
| 419 | + $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize ); |
420 | 420 | $this->tearDownFiles(); |
421 | 421 | |
422 | 422 | $this->backend = $this->multiBackend; |
423 | 423 | $this->tearDownFiles(); |
424 | | - $this->doTestCreate( $op, $dest, $alreadyExists, $okStatus, $newSize ); |
| 424 | + $this->doTestCreate( $op, $alreadyExists, $okStatus, $newSize ); |
425 | 425 | $this->tearDownFiles(); |
426 | 426 | } |
427 | 427 | |
428 | | - private function doTestCreate( $op, $dest, $alreadyExists, $okStatus, $newSize ) { |
| 428 | + private function doTestCreate( $op, $alreadyExists, $okStatus, $newSize ) { |
429 | 429 | $backendName = $this->backendClass(); |
430 | 430 | |
| 431 | + $dest = $op['dst']; |
431 | 432 | $this->backend->prepare( array( 'dir' => dirname( $dest ) ) ); |
432 | 433 | |
433 | 434 | $oldText = 'blah...blah...waahwaah'; |
— | — | @@ -477,44 +478,52 @@ |
478 | 479 | public function provider_testCreate() { |
479 | 480 | $cases = array(); |
480 | 481 | |
481 | | - $source = $this->baseStorePath() . '/unittest-cont2/myspacefile.txt'; |
| 482 | + $dest = $this->baseStorePath() . '/unittest-cont2/myspacefile.txt'; |
482 | 483 | |
483 | | - $dummyText = 'hey hey'; |
484 | | - $op = array( 'op' => 'create', 'content' => $dummyText, 'dst' => $source ); |
| 484 | + $op = array( 'op' => 'create', 'content' => 'test test testing', 'dst' => $dest ); |
485 | 485 | $cases[] = array( |
486 | 486 | $op, // operation |
487 | | - $source, // source |
488 | 487 | false, // no dest already exists |
489 | 488 | true, // succeeds |
490 | | - strlen( $dummyText ) |
| 489 | + strlen( $op['content'] ) |
491 | 490 | ); |
492 | 491 | |
| 492 | + $op2 = $op; |
| 493 | + $op2['content'] = "\n"; |
493 | 494 | $cases[] = array( |
494 | | - $op, // operation |
495 | | - $source, // source |
| 495 | + $op2, // operation |
| 496 | + false, // no dest already exists |
| 497 | + true, // succeeds |
| 498 | + strlen( $op2['content'] ) |
| 499 | + ); |
| 500 | + |
| 501 | + $op2 = $op; |
| 502 | + $op2['content'] = "fsf\n waf 3kt"; |
| 503 | + $cases[] = array( |
| 504 | + $op2, // operation |
496 | 505 | true, // dest already exists |
497 | 506 | false, // fails |
498 | | - strlen( $dummyText ) |
| 507 | + strlen( $op2['content'] ) |
499 | 508 | ); |
500 | 509 | |
501 | 510 | $op2 = $op; |
| 511 | + $op2['content'] = "egm'g gkpe gpqg eqwgwqg"; |
502 | 512 | $op2['overwrite'] = true; |
503 | 513 | $cases[] = array( |
504 | 514 | $op2, // operation |
505 | | - $source, // source |
506 | 515 | true, // dest already exists |
507 | 516 | true, // succeeds |
508 | | - strlen( $dummyText ) |
| 517 | + strlen( $op2['content'] ) |
509 | 518 | ); |
510 | 519 | |
511 | 520 | $op2 = $op; |
| 521 | + $op2['content'] = "39qjmg3-qg"; |
512 | 522 | $op2['overwriteSame'] = true; |
513 | 523 | $cases[] = array( |
514 | 524 | $op2, // operation |
515 | | - $source, // source |
516 | 525 | true, // dest already exists |
517 | 526 | false, // succeeds |
518 | | - strlen( $dummyText ) |
| 527 | + strlen( $op2['content'] ) |
519 | 528 | ); |
520 | 529 | |
521 | 530 | return $cases; |
Index: trunk/phase3/includes/filerepo/backend/FileOp.php |
— | — | @@ -213,7 +213,7 @@ |
214 | 214 | } |
215 | 215 | |
216 | 216 | /** |
217 | | - * Get required operation parameters |
| 217 | + * Get the file operation parameters |
218 | 218 | * |
219 | 219 | * @return Array (required params list, optional params list) |
220 | 220 | */ |
Index: trunk/phase3/includes/filerepo/backend/TempFSFile.php |
— | — | @@ -1,7 +1,6 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | 4 | * @file |
5 | | - * @ingroup FileRepo |
6 | 5 | * @ingroup FileBackend |
7 | 6 | */ |
8 | 7 | |
— | — | @@ -9,7 +8,6 @@ |
10 | 9 | * This class is used to hold the location and do limited manipulation |
11 | 10 | * of files stored temporarily (usually this will be $wgTmpDirectory) |
12 | 11 | * |
13 | | - * @ingroup FileRepo |
14 | 12 | * @ingroup FileBackend |
15 | 13 | */ |
16 | 14 | class TempFSFile extends FSFile { |
Index: trunk/phase3/includes/filerepo/backend/SwiftFileBackend.php |
— | — | @@ -138,6 +138,12 @@ |
139 | 139 | $obj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD |
140 | 140 | // Note: metadata keys stored as [Upper case char][[Lower case char]...] |
141 | 141 | $obj->metadata = array( 'Sha1base36' => $sha1Hash ); |
| 142 | + // Manually set the ETag (https://github.com/rackspace/php-cloudfiles/issues/59). |
| 143 | + // The MD5 here will be checked within Swift against its own MD5. |
| 144 | + $obj->set_etag( md5( $params['content'] ) ); |
| 145 | + // Use the same content type as StreamFile for security |
| 146 | + $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] ); |
| 147 | + // Actually write the object in Swift |
142 | 148 | $obj->write( $params['content'] ); |
143 | 149 | } catch ( BadContentTypeException $e ) { |
144 | 150 | $status->fatal( 'backend-fail-contenttype', $params['dst'] ); |
— | — | @@ -201,6 +207,11 @@ |
202 | 208 | $obj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD |
203 | 209 | // Note: metadata keys stored as [Upper case char][[Lower case char]...] |
204 | 210 | $obj->metadata = array( 'Sha1base36' => $sha1Hash ); |
| 211 | + // The MD5 here will be checked within Swift against its own MD5. |
| 212 | + $obj->set_etag( md5_file( $params['src'] ) ); |
| 213 | + // Use the same content type as StreamFile for security |
| 214 | + $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] ); |
| 215 | + // Actually write the object in Swift |
205 | 216 | $obj->load_from_filename( $params['src'], True ); // calls $obj->write() |
206 | 217 | } catch ( BadContentTypeException $e ) { |
207 | 218 | $status->fatal( 'backend-fail-contenttype', $params['dst'] ); |
— | — | @@ -585,7 +596,7 @@ |
586 | 597 | // Create a new temporary file... |
587 | 598 | $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext ); |
588 | 599 | if ( $tmpFile ) { |
589 | | - $handle = fopen( $tmpFile->getPath(), 'w' ); |
| 600 | + $handle = fopen( $tmpFile->getPath(), 'wb' ); |
590 | 601 | if ( $handle ) { |
591 | 602 | $obj->stream( $handle, $this->headersFromParams( $params ) ); |
592 | 603 | fclose( $handle ); |
Index: trunk/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -792,7 +792,7 @@ |
793 | 793 | } |
794 | 794 | |
795 | 795 | // Build up the temp file using the source chunks (in order)... |
796 | | - $tmpHandle = fopen( $tmpPath, 'a' ); |
| 796 | + $tmpHandle = fopen( $tmpPath, 'ab' ); |
797 | 797 | if ( $tmpHandle === false ) { |
798 | 798 | $status->fatal( 'backend-fail-opentemp', $tmpPath ); |
799 | 799 | return $status; |
— | — | @@ -1448,8 +1448,9 @@ |
1449 | 1449 | // Allow certain directories to be above the hash dirs so as |
1450 | 1450 | // to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab"). |
1451 | 1451 | // They must be 2+ chars to avoid any hash directory ambiguity. |
| 1452 | + $m = array(); |
1452 | 1453 | if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) { |
1453 | | - return '.' . str_pad( $m['shard'], $hashLevels, '0', STR_PAD_LEFT ); |
| 1454 | + return '.' . $m['shard']; |
1454 | 1455 | } |
1455 | 1456 | return null; // failed to match |
1456 | 1457 | } |
Index: trunk/phase3/includes/filerepo/backend/FSFile.php |
— | — | @@ -1,14 +1,12 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | 4 | * @file |
5 | | - * @ingroup FileRepo |
6 | 5 | * @ingroup FileBackend |
7 | 6 | */ |
8 | 7 | |
9 | 8 | /** |
10 | 9 | * Class representing a non-directory file on the file system |
11 | 10 | * |
12 | | - * @ingroup FileRepo |
13 | 11 | * @ingroup FileBackend |
14 | 12 | */ |
15 | 13 | class FSFile { |
Index: trunk/phase3/includes/StreamFile.php |
— | — | @@ -74,7 +74,7 @@ |
75 | 75 | // Cancel output buffering and gzipping if set |
76 | 76 | wfResetOutputBuffers(); |
77 | 77 | |
78 | | - $type = self::getType( $path ); |
| 78 | + $type = self::contentTypeFromPath( $path ); |
79 | 79 | if ( $type && $type != 'unknown/unknown' ) { |
80 | 80 | header( "Content-type: $type" ); |
81 | 81 | } else { |
— | — | @@ -115,12 +115,13 @@ |
116 | 116 | } |
117 | 117 | |
118 | 118 | /** |
119 | | - * Determine the filetype we're dealing with |
| 119 | + * Determine the file type of a file based on the path |
| 120 | + * |
120 | 121 | * @param $filename string Storage path or file system path |
121 | 122 | * @param $safe bool Whether to do retroactive upload blacklist checks |
122 | 123 | * @return null|string |
123 | 124 | */ |
124 | | - protected static function getType( $filename, $safe = true ) { |
| 125 | + public static function contentTypeFromPath( $filename, $safe = true ) { |
125 | 126 | global $wgTrivialMimeDetection; |
126 | 127 | |
127 | 128 | $ext = strrchr( $filename, '.' ); |