00001 <?php
00002
00008 abstract class FileRepo {
00009 const DELETE_SOURCE = 1;
00010 const FIND_PRIVATE = 1;
00011 const FIND_IGNORE_REDIRECT = 2;
00012 const OVERWRITE = 2;
00013 const OVERWRITE_SAME = 4;
00014
00015 var $thumbScriptUrl, $transformVia404;
00016 var $descBaseUrl, $scriptDirUrl, $articleUrl, $fetchDescription, $initialCapital;
00017 var $pathDisclosureProtection = 'paranoid';
00018 var $descriptionCacheExpiry, $apiThumbCacheExpiry, $hashLevels;
00019
00024 var $fileFactory = false, $oldFileFactory = false;
00025 var $fileFactoryKey = false, $oldFileFactoryKey = false;
00026
00027 function __construct( $info ) {
00028
00029 $this->name = $info['name'];
00030
00031
00032 $this->initialCapital = true;
00033 foreach ( array( 'descBaseUrl', 'scriptDirUrl', 'articleUrl', 'fetchDescription',
00034 'thumbScriptUrl', 'initialCapital', 'pathDisclosureProtection',
00035 'descriptionCacheExpiry', 'apiThumbCacheExpiry', 'hashLevels' ) as $var )
00036 {
00037 if ( isset( $info[$var] ) ) {
00038 $this->$var = $info[$var];
00039 }
00040 }
00041 $this->transformVia404 = !empty( $info['transformVia404'] );
00042 }
00043
00047 static function isVirtualUrl( $url ) {
00048 return substr( $url, 0, 9 ) == 'mwrepo://';
00049 }
00050
00060 function newFile( $title, $time = false ) {
00061 if ( !($title instanceof Title) ) {
00062 $title = Title::makeTitleSafe( NS_FILE, $title );
00063 if ( !is_object( $title ) ) {
00064 return null;
00065 }
00066 }
00067 if ( $time ) {
00068 if ( $this->oldFileFactory ) {
00069 return call_user_func( $this->oldFileFactory, $title, $this, $time );
00070 } else {
00071 return false;
00072 }
00073 } else {
00074 return call_user_func( $this->fileFactory, $title, $this );
00075 }
00076 }
00077
00086 function findFile( $title, $time = false, $flags = 0 ) {
00087 if ( !($title instanceof Title) ) {
00088 $title = Title::makeTitleSafe( NS_FILE, $title );
00089 if ( !is_object( $title ) ) {
00090 return false;
00091 }
00092 }
00093 # First try the current version of the file to see if it precedes the timestamp
00094 $img = $this->newFile( $title );
00095 if ( !$img ) {
00096 return false;
00097 }
00098 if ( $img->exists() && ( !$time || $img->getTimestamp() == $time ) ) {
00099 return $img;
00100 }
00101 # Now try an old version of the file
00102 if ( $time !== false ) {
00103 $img = $this->newFile( $title, $time );
00104 if ( $img && $img->exists() ) {
00105 if ( !$img->isDeleted(File::DELETED_FILE) ) {
00106 return $img;
00107 } else if ( ($flags & FileRepo::FIND_PRIVATE) && $img->userCan(File::DELETED_FILE) ) {
00108 return $img;
00109 }
00110 }
00111 }
00112
00113 # Now try redirects
00114 if ( $flags & FileRepo::FIND_IGNORE_REDIRECT ) {
00115 return false;
00116 }
00117 $redir = $this->checkRedirect( $title );
00118 if( $redir && $redir->getNamespace() == NS_FILE) {
00119 $img = $this->newFile( $redir );
00120 if( !$img ) {
00121 return false;
00122 }
00123 if( $img->exists() ) {
00124 $img->redirectedFrom( $title->getDBkey() );
00125 return $img;
00126 }
00127 }
00128 return false;
00129 }
00130
00131
00132
00133
00134
00135
00136 function findFiles( $titles ) {
00137 $result = array();
00138 foreach ( $titles as $index => $title ) {
00139 $file = $this->findFile( $title );
00140 if ( $file )
00141 $result[$file->getTitle()->getDBkey()] = $file;
00142 }
00143 return $result;
00144 }
00145
00155 function newFileFromKey( $sha1, $time = false ) {
00156 if ( $time ) {
00157 if ( $this->oldFileFactoryKey ) {
00158 return call_user_func( $this->oldFileFactoryKey, $sha1, $this, $time );
00159 } else {
00160 return false;
00161 }
00162 } else {
00163 return call_user_func( $this->fileFactoryKey, $sha1, $this );
00164 }
00165 }
00166
00175 function findFileFromKey( $sha1, $time = false, $flags = 0 ) {
00176 # First try the current version of the file to see if it precedes the timestamp
00177 $img = $this->newFileFromKey( $sha1 );
00178 if ( !$img ) {
00179 return false;
00180 }
00181 if ( $img->exists() && ( !$time || $img->getTimestamp() == $time ) ) {
00182 return $img;
00183 }
00184 # Now try an old version of the file
00185 if ( $time !== false ) {
00186 $img = $this->newFileFromKey( $sha1, $time );
00187 if ( $img->exists() ) {
00188 if ( !$img->isDeleted(File::DELETED_FILE) ) {
00189 return $img;
00190 } else if ( ($flags & FileRepo::FIND_PRIVATE) && $img->userCan(File::DELETED_FILE) ) {
00191 return $img;
00192 }
00193 }
00194 }
00195 return false;
00196 }
00197
00201 function getThumbScriptUrl() {
00202 return $this->thumbScriptUrl;
00203 }
00204
00208 function canTransformVia404() {
00209 return $this->transformVia404;
00210 }
00211
00215 function getNameFromTitle( $title ) {
00216 global $wgCapitalLinks;
00217 if ( $this->initialCapital != $wgCapitalLinks ) {
00218 global $wgContLang;
00219 $name = $title->getUserCaseDBKey();
00220 if ( $this->initialCapital ) {
00221 $name = $wgContLang->ucfirst( $name );
00222 }
00223 } else {
00224 $name = $title->getDBkey();
00225 }
00226 return $name;
00227 }
00228
00229 static function getHashPathForLevel( $name, $levels ) {
00230 if ( $levels == 0 ) {
00231 return '';
00232 } else {
00233 $hash = md5( $name );
00234 $path = '';
00235 for ( $i = 1; $i <= $levels; $i++ ) {
00236 $path .= substr( $hash, 0, $i ) . '/';
00237 }
00238 return $path;
00239 }
00240 }
00241
00246 function getHashPath( $name ) {
00247 return self::getHashPathForLevel( $name, $this->hashLevels );
00248 }
00249
00253 function getName() {
00254 return $this->name;
00255 }
00256
00266 function getDescriptionUrl( $name ) {
00267 $encName = wfUrlencode( $name );
00268 if ( !is_null( $this->descBaseUrl ) ) {
00269 # "http://example.com/wiki/Image:"
00270 return $this->descBaseUrl . $encName;
00271 }
00272 if ( !is_null( $this->articleUrl ) ) {
00273 # "http://example.com/wiki/$1"
00274 #
00275 # We use "Image:" as the canonical namespace for
00276 # compatibility across all MediaWiki versions.
00277 return str_replace( '$1',
00278 "Image:$encName", $this->articleUrl );
00279 }
00280 if ( !is_null( $this->scriptDirUrl ) ) {
00281 # "http://example.com/w"
00282 #
00283 # We use "Image:" as the canonical namespace for
00284 # compatibility across all MediaWiki versions,
00285 # and just sort of hope index.php is right. ;)
00286 return $this->scriptDirUrl .
00287 "/index.php?title=Image:$encName";
00288 }
00289 return false;
00290 }
00291
00300 function getDescriptionRenderUrl( $name, $lang = null ) {
00301 $query = 'action=render';
00302 if ( !is_null( $lang ) ) {
00303 $query .= '&uselang=' . $lang;
00304 }
00305 if ( isset( $this->scriptDirUrl ) ) {
00306 return $this->scriptDirUrl . '/index.php?title=' .
00307 wfUrlencode( 'Image:' . $name ) .
00308 "&$query";
00309 } else {
00310 $descUrl = $this->getDescriptionUrl( $name );
00311 if ( $descUrl ) {
00312 return wfAppendQuery( $descUrl, $query );
00313 } else {
00314 return false;
00315 }
00316 }
00317 }
00318
00332 function store( $srcPath, $dstZone, $dstRel, $flags = 0 ) {
00333 $status = $this->storeBatch( array( array( $srcPath, $dstZone, $dstRel ) ), $flags );
00334 if ( $status->successCount == 0 ) {
00335 $status->ok = false;
00336 }
00337 return $status;
00338 }
00339
00346 abstract function storeBatch( $triplets, $flags = 0 );
00347
00356 abstract function storeTemp( $originalName, $srcPath );
00357
00364 function freeTemp( $virtualUrl ) {
00365 return true;
00366 }
00367
00382 function publish( $srcPath, $dstRel, $archiveRel, $flags = 0 ) {
00383 $status = $this->publishBatch( array( array( $srcPath, $dstRel, $archiveRel ) ), $flags );
00384 if ( $status->successCount == 0 ) {
00385 $status->ok = false;
00386 }
00387 if ( isset( $status->value[0] ) ) {
00388 $status->value = $status->value[0];
00389 } else {
00390 $status->value = false;
00391 }
00392 return $status;
00393 }
00394
00401 abstract function publishBatch( $triplets, $flags = 0 );
00402
00419 abstract function deleteBatch( $sourceDestPairs );
00420
00430 function delete( $srcRel, $archiveRel ) {
00431 return $this->deleteBatch( array( array( $srcRel, $archiveRel ) ) );
00432 }
00433
00439 abstract function getFileProps( $virtualUrl );
00440
00446 function enumFiles( $callback ) {
00447 throw new MWException( 'enumFiles is not supported by ' . get_class( $this ) );
00448 }
00449
00453 function validateFilename( $filename ) {
00454 if ( strval( $filename ) == '' ) {
00455 return false;
00456 }
00457 if ( wfIsWindows() ) {
00458 $filename = strtr( $filename, '\\', '/' );
00459 }
00463 if ( strpos( $filename, '.' ) !== false &&
00464 ( $filename === '.' || $filename === '..' ||
00465 strpos( $filename, './' ) === 0 ||
00466 strpos( $filename, '../' ) === 0 ||
00467 strpos( $filename, '/./' ) !== false ||
00468 strpos( $filename, '/../' ) !== false ) )
00469 {
00470 return false;
00471 } else {
00472 return true;
00473 }
00474 }
00475
00479 function paranoidClean( $param ) { return '[hidden]'; }
00480 function passThrough( $param ) { return $param; }
00481
00485 function getErrorCleanupFunction() {
00486 switch ( $this->pathDisclosureProtection ) {
00487 case 'none':
00488 $callback = array( $this, 'passThrough' );
00489 break;
00490 default:
00491 $callback = array( $this, 'paranoidClean' );
00492 }
00493 return $callback;
00494 }
00500 function newFatal( $message ) {
00501 $params = func_get_args();
00502 array_unshift( $params, $this );
00503 return call_user_func_array( array( 'FileRepoStatus', 'newFatal' ), $params );
00504 }
00505
00509 function newGood( $value = null ) {
00510 return FileRepoStatus::newGood( $this, $value );
00511 }
00512
00517 function cleanupDeletedBatch( $storageKeys ) {}
00518
00526 function checkRedirect( $title ) {
00527 return false;
00528 }
00529
00535 function invalidateImageRedirect( $title ) {
00536 global $wgMemc;
00537 $memcKey = $this->getMemcKey( "image_redirect:" . md5( $title->getPrefixedDBkey() ) );
00538 $wgMemc->delete( $memcKey );
00539 }
00540
00541 function findBySha1( $hash ) {
00542 return array();
00543 }
00544
00549 public function getDisplayName() {
00550
00551 if ( $this->name == 'local' ) {
00552 return null;
00553 }
00554 $repoName = wfMsg( 'shared-repo-name-' . $this->name );
00555 if ( !wfEmptyMsg( 'shared-repo-name-' . $this->name, $repoName ) ) {
00556 return $repoName;
00557 }
00558 return wfMsg( 'shared-repo' );
00559 }
00560
00561 function getSlaveDB() {
00562 return wfGetDB( DB_SLAVE );
00563 }
00564
00565 function getMasterDB() {
00566 return wfGetDB( DB_MASTER );
00567 }
00568
00569 function getMemcKey( $key ) {
00570 return wfWikiID( $this->getSlaveDB() ) . ":{$key}";
00571 }
00572 }