r113481 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r113480‎ | r113481 | r113482 >
Date:16:25, 9 March 2012
Author:reedy
Status:ok
Tags:
Comment:
Replace UploadFromStash/UploadStash with copy from trunk at r105822

Bug 35032 - UploadFromStash is broken in 1.18.1
Modified paths:
  • /branches/REL1_18/phase3/includes/upload/UploadFromStash.php (replaced) (history)
  • /branches/REL1_18/phase3/includes/upload/UploadStash.php (replaced) (history)

Diff [purge]

Index: branches/REL1_18/phase3/includes/upload/UploadFromStash.php
@@ -9,13 +9,18 @@
1010
1111 class UploadFromStash extends UploadBase {
1212 protected $mFileKey, $mVirtualTempPath, $mFileProps, $mSourceType;
13 -
 13+
1414 // an instance of UploadStash
1515 private $stash;
16 -
 16+
1717 //LocalFile repo
1818 private $repo;
19 -
 19+
 20+ /**
 21+ * @param $user User
 22+ * @param $stash UploadStash
 23+ * @param $repo FileRepo
 24+ */
2025 public function __construct( $user = false, $stash = false, $repo = false ) {
2126 // user object. sometimes this won't exist, as when running from cron.
2227 $this->user = $user;
@@ -29,16 +34,25 @@
3035 if( $stash ) {
3136 $this->stash = $stash;
3237 } else {
33 - wfDebug( __METHOD__ . " creating new UploadStash instance for " . $user->getId() . "\n" );
 38+ if( $user ) {
 39+ wfDebug( __METHOD__ . " creating new UploadStash instance for " . $user->getId() . "\n" );
 40+ } else {
 41+ wfDebug( __METHOD__ . " creating new UploadStash instance with no user\n" );
 42+ }
 43+
3444 $this->stash = new UploadStash( $this->repo, $this->user );
3545 }
3646
3747 return true;
3848 }
39 -
 49+
 50+ /**
 51+ * @param $key string
 52+ * @return bool
 53+ */
4054 public static function isValidKey( $key ) {
4155 // this is checked in more detail in UploadStash
42 - return preg_match( UploadStash::KEY_FORMAT_REGEX, $key );
 56+ return (bool)preg_match( UploadStash::KEY_FORMAT_REGEX, $key );
4357 }
4458
4559 /**
@@ -47,16 +61,23 @@
4862 * @return Boolean
4963 */
5064 public static function isValidRequest( $request ) {
51 - return self::isValidKey( $request->getText( 'wpFileKey' ) || $request->getText( 'wpSessionKey' ) );
 65+ // this passes wpSessionKey to getText() as a default when wpFileKey isn't set.
 66+ // wpSessionKey has no default which guarantees failure if both are missing
 67+ // (though that should have been caught earlier)
 68+ return self::isValidKey( $request->getText( 'wpFileKey', $request->getText( 'wpSessionKey' ) ) );
5269 }
5370
 71+ /**
 72+ * @param $key string
 73+ * @param $name string
 74+ */
5475 public function initialize( $key, $name = 'upload_file' ) {
5576 /**
5677 * Confirming a temporarily stashed upload.
5778 * We don't want path names to be forged, so we keep
5879 * them in the session on the server and just give
5980 * an opaque key to the user agent.
60 - */
 81+ */
6182 $metadata = $this->stash->getMetadata( $key );
6283 $this->initializePathInfo( $name,
6384 $this->getRealPath ( $metadata['us_path'] ),
@@ -74,17 +95,20 @@
7596 * @param $request WebRequest
7697 */
7798 public function initializeFromRequest( &$request ) {
78 - $fileKey = $request->getText( 'wpFileKey' ) || $request->getText( 'wpSessionKey' );
 99+ // sends wpSessionKey as a default when wpFileKey is missing
 100+ $fileKey = $request->getText( 'wpFileKey', $request->getText( 'wpSessionKey' ) );
79101
80 - $desiredDestName = $request->getText( 'wpDestFile' );
81 - if( !$desiredDestName ) {
82 - $desiredDestName = $request->getText( 'wpUploadFile' ) || $request->getText( 'filename' );
83 - }
 102+ // chooses one of wpDestFile, wpUploadFile, filename in that order.
 103+ $desiredDestName = $request->getText( 'wpDestFile', $request->getText( 'wpUploadFile', $request->getText( 'filename' ) ) );
 104+
84105 return $this->initialize( $fileKey, $desiredDestName );
85106 }
86107
87 - public function getSourceType() {
88 - return $this->mSourceType;
 108+ /**
 109+ * @return string
 110+ */
 111+ public function getSourceType() {
 112+ return $this->mSourceType;
89113 }
90114
91115 /**
@@ -97,20 +121,23 @@
98122 }
99123
100124 /**
101 - * There is no need to stash the image twice
 125+ * Stash the file.
 126+ *
 127+ * @return UploadStashFile
102128 */
103 - public function stashFile( $key = null ) {
104 - if ( !empty( $this->mLocalFile ) ) {
105 - return $this->mLocalFile;
106 - }
107 - return parent::stashFile( $key );
 129+ public function stashFile() {
 130+ // replace mLocalFile with an instance of UploadStashFile, which adds some methods
 131+ // that are useful for stashed files.
 132+ $this->mLocalFile = parent::stashFile();
 133+ return $this->mLocalFile;
108134 }
109135
110136 /**
111 - * Alias for stashFile
 137+ * This should return the key instead of the UploadStashFile instance, for backward compatibility.
 138+ * @return String
112139 */
113 - public function stashSession( $key = null ) {
114 - return $this->stashFile( $key );
 140+ public function stashSession() {
 141+ return $this->stashFile()->getFileKey();
115142 }
116143
117144 /**
@@ -123,11 +150,15 @@
124151
125152 /**
126153 * Perform the upload, then remove the database record afterward.
 154+ * @param $comment string
 155+ * @param $pageText string
 156+ * @param $watch bool
 157+ * @param $user User
 158+ * @return Status
127159 */
128160 public function performUpload( $comment, $pageText, $watch, $user ) {
129161 $rv = parent::performUpload( $comment, $pageText, $watch, $user );
130162 $this->unsaveUploadedFile();
131163 return $rv;
132164 }
133 -
134 -}
\ No newline at end of file
 165+}
Index: branches/REL1_18/phase3/includes/upload/UploadStash.php
@@ -10,6 +10,9 @@
1111 * We accomplish this using a database table, with ownership checking as you might expect. See SpecialUploadStash, which
1212 * implements a web interface to some files stored this way.
1313 *
 14+ * UploadStash right now is *mostly* intended to show you one user's slice of the entire stash. The user parameter is only optional
 15+ * because there are few cases where we clean out the stash from an automated script. In the future we might refactor this.
 16+ *
1417 * UploadStash represents the entire stash of temporary files.
1518 * UploadStashFile is a filestore for the actual physical disk files.
1619 * UploadFromStash extends UploadBase, and represents a single stashed file as it is moved from the stash to the regular file repository
@@ -19,13 +22,6 @@
2023 // Format of the key for files -- has to be suitable as a filename itself (e.g. ab12cd34ef.jpg)
2124 const KEY_FORMAT_REGEX = '/^[\w-\.]+\.\w*$/';
2225
23 - // When a given stashed file can't be loaded, wait for the slaves to catch up. If they're more than MAX_LAG
24 - // behind, throw an exception instead. (at what point is broken better than slow?)
25 - const MAX_LAG = 30;
26 -
27 - // Age of the repository in hours. That is, after how long will files be assumed abandoned and deleted?
28 - const REPO_AGE = 6;
29 -
3026 /**
3127 * repository that this uses to store temp files
3228 * public because we sometimes need to get a LocalFile within the same repo.
@@ -73,6 +69,7 @@
7470
7571 /**
7672 * Get a file and its metadata from the stash.
 73+ * The noAuth param is a bit janky but is required for automated scripts which clean out the stash.
7774 *
7875 * @param $key String: key under which file information is stored
7976 * @param $noAuth Boolean (optional) Don't check authentication. Used by maintenance scripts.
@@ -94,26 +91,10 @@
9592 }
9693 }
9794
98 - $dbr = $this->repo->getSlaveDb();
99 -
10095 if ( !isset( $this->fileMetadata[$key] ) ) {
101 - // try this first. if it fails to find the row, check for lag, wait, try again. if its still missing, throw an exception.
102 - // this more complex solution keeps things moving for page loads with many requests
103 - // (ie. validating image ownership) when replag is high
10496 if ( !$this->fetchFileMetadata( $key ) ) {
105 - $lag = $dbr->getLag();
106 - if ( $lag > 0 && $lag <= self::MAX_LAG ) {
107 - // if there's not too much replication lag, just wait for the slave to catch up to our last insert.
108 - sleep( ceil( $lag ) );
109 - } elseif ( $lag > self::MAX_LAG ) {
110 - // that's a lot of lag to introduce into the middle of the UI.
111 - throw new UploadStashMaxLagExceededException(
112 - 'Couldn\'t load stashed file metadata, and replication lag is above threshold: (MAX_LAG=' . self::MAX_LAG . ')'
113 - );
114 - }
115 -
116 - // now that the waiting has happened, try again
117 - $this->fetchFileMetadata( $key );
 97+ // If nothing was received, it's likely due to replication lag. Check the master to see if the record is there.
 98+ $this->fetchFileMetadata( $key, DB_MASTER );
11899 }
119100
120101 if ( !isset( $this->fileMetadata[$key] ) ) {
@@ -172,13 +153,12 @@
173154 *
174155 * @param $path String: path to file you want stashed
175156 * @param $sourceType String: the type of upload that generated this file (currently, I believe, 'file' or null)
176 - * @param $key String: optional, unique key for this file. Used for directory hashing when storing, otherwise not important
177157 * @throws UploadStashBadPathException
178158 * @throws UploadStashFileException
179159 * @throws UploadStashNotLoggedInException
180160 * @return UploadStashFile: file, or null on failure
181161 */
182 - public function stashFile( $path, $sourceType = null, $key = null ) {
 162+ public function stashFile( $path, $sourceType = null ) {
183163 if ( ! file_exists( $path ) ) {
184164 wfDebug( __METHOD__ . " tried to stash file at '$path', but it doesn't exist\n" );
185165 throw new UploadStashBadPathException( "path doesn't exist" );
@@ -198,17 +178,16 @@
199179 }
200180
201181 // If no key was supplied, make one. a mysql insertid would be totally reasonable here, except
202 - // that some users of this function might expect to supply the key instead of using the generated one.
203 - if ( is_null( $key ) ) {
204 - // some things that when combined will make a suitably unique key.
205 - // see: http://www.jwz.org/doc/mid.html
206 - list ($usec, $sec) = explode( ' ', microtime() );
207 - $usec = substr($usec, 2);
208 - $key = wfBaseConvert( $sec . $usec, 10, 36 ) . '.' .
209 - wfBaseConvert( mt_rand(), 10, 36 ) . '.'.
210 - $this->userId . '.' .
211 - $extension;
212 - }
 182+ // that for historical reasons, the key is this random thing instead. At least it's not guessable.
 183+ //
 184+ // some things that when combined will make a suitably unique key.
 185+ // see: http://www.jwz.org/doc/mid.html
 186+ list ($usec, $sec) = explode( ' ', microtime() );
 187+ $usec = substr($usec, 2);
 188+ $key = wfBaseConvert( $sec . $usec, 10, 36 ) . '.' .
 189+ wfBaseConvert( mt_rand(), 10, 36 ) . '.'.
 190+ $this->userId . '.' .
 191+ $extension;
213192
214193 $this->fileProps[$key] = $fileProps;
215194
@@ -249,31 +228,8 @@
250229 wfDebug( __METHOD__ . " inserting $stashPath under $key\n" );
251230 $dbw = $this->repo->getMasterDb();
252231
253 - // select happens on the master so this can all be in a transaction, which
254 - // avoids a race condition that's likely with multiple people uploading from the same
255 - // set of files
256 - $dbw->begin();
257 - // first, check to see if it's already there.
258 - $row = $dbw->selectRow(
259 - 'uploadstash',
260 - 'us_user, us_timestamp',
261 - array( 'us_key' => $key ),
262 - __METHOD__
263 - );
264 -
265 - // The current user can't have this key if:
266 - // - the key is owned by someone else and
267 - // - the age of the key is less than REPO_AGE
268 - if ( is_object( $row ) ) {
269 - if ( $row->us_user != $this->userId &&
270 - $row->wfTimestamp( TS_UNIX, $row->us_timestamp ) > time() - UploadStash::REPO_AGE * 3600
271 - ) {
272 - $dbw->rollback();
273 - throw new UploadStashWrongOwnerException( "Attempting to upload a duplicate of a file that someone else has stashed" );
274 - }
275 - }
276 -
277232 $this->fileMetadata[$key] = array(
 233+ 'us_id' => $dbw->nextSequenceValue( 'uploadstash_us_id_seq' ),
278234 'us_user' => $this->userId,
279235 'us_key' => $key,
280236 'us_orig_path' => $path,
@@ -290,14 +246,11 @@
291247 'us_status' => 'finished'
292248 );
293249
294 - // if a row exists but previous checks on it passed, let the current user take over this key.
295 - $dbw->replace(
 250+ $dbw->insert(
296251 'uploadstash',
297 - 'us_key',
298252 $this->fileMetadata[$key],
299253 __METHOD__
300254 );
301 - $dbw->commit();
302255
303256 // store the insertid in the class variable so immediate retrieval (possibly laggy) isn't necesary.
304257 $this->fileMetadata[$key]['us_id'] = $dbw->insertId();
@@ -320,7 +273,7 @@
321274 throw new UploadStashNotLoggedInException( __METHOD__ . ' No user is logged in, files must belong to users' );
322275 }
323276
324 - wfDebug( __METHOD__ . " clearing all rows for user $userId\n" );
 277+ wfDebug( __METHOD__ . ' clearing all rows for user ' . $this->userId . "\n" );
325278 $dbw = $this->repo->getMasterDb();
326279 $dbw->delete(
327280 'uploadstash',
@@ -414,7 +367,7 @@
415368 $res = $dbr->select(
416369 'uploadstash',
417370 'us_key',
418 - array( 'us_key' => $key ),
 371+ array( 'us_user' => $this->userId ),
419372 __METHOD__
420373 );
421374
@@ -468,9 +421,16 @@
469422 * @param $key String: key
470423 * @return boolean
471424 */
472 - protected function fetchFileMetadata( $key ) {
 425+ protected function fetchFileMetadata( $key, $readFromDB = DB_SLAVE ) {
473426 // populate $fileMetadata[$key]
474 - $dbr = $this->repo->getSlaveDb();
 427+ $dbr = null;
 428+ if( $readFromDB === DB_MASTER ) {
 429+ // sometimes reading from the master is necessary, if there's replication lag.
 430+ $dbr = $this->repo->getMasterDb();
 431+ } else {
 432+ $dbr = $this->repo->getSlaveDb();
 433+ }
 434+
475435 $row = $dbr->selectRow(
476436 'uploadstash',
477437 '*',
@@ -483,22 +443,7 @@
484444 return false;
485445 }
486446
487 - $this->fileMetadata[$key] = array(
488 - 'us_user' => $row->us_user,
489 - 'us_key' => $row->us_key,
490 - 'us_orig_path' => $row->us_orig_path,
491 - 'us_path' => $row->us_path,
492 - 'us_size' => $row->us_size,
493 - 'us_sha1' => $row->us_sha1,
494 - 'us_mime' => $row->us_mime,
495 - 'us_media_type' => $row->us_media_type,
496 - 'us_image_width' => $row->us_image_width,
497 - 'us_image_height' => $row->us_image_height,
498 - 'us_image_bits' => $row->us_image_bits,
499 - 'us_source_type' => $row->us_source_type,
500 - 'us_timestamp' => $row->us_timestamp,
501 - 'us_status' => $row->us_status
502 - );
 447+ $this->fileMetadata[$key] = (array)$row;
503448
504449 return true;
505450 }
@@ -698,5 +643,4 @@
699644 class UploadStashZeroLengthFileException extends MWException {};
700645 class UploadStashNotLoggedInException extends MWException {};
701646 class UploadStashWrongOwnerException extends MWException {};
702 -class UploadStashMaxLagExceededException extends MWException {};
703647 class UploadStashNoSuchKeyException extends MWException {};
Property changes on: branches/REL1_18/phase3/includes/upload/UploadStash.php
___________________________________________________________________
Modified: svn:mergeinfo
704648 Reverse-merged /trunk/phase3/includes/upload/UploadStash.php:r92580,92713,92765,92884,92886-92887,92894,92898,92907,92932,93065,93149,93151,93233-93234,93258,93266,93516-93518,93818-93822,93834,93847,93858,93891,93935-93936,94068,94155,94235,94346,94372,94422,94425,94444,94448,94456,94498,94601,94728,94825,94862,94995-94997

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r105822Follow up r105821, as usual, commit the files I forgot to `svn add`dantman19:49, 11 December 2011

Status & tagging log