59 private const MAX_US_PROPS_SIZE = 65535;
95 $this->user = $user ?? RequestContext::getMain()->getUser();
111 public function getFile( $key, $noAuth =
false ) {
112 if ( !preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
114 wfMessage(
'uploadstash-bad-path-bad-format', $key )
118 if ( !$noAuth && !$this->user->isRegistered() ) {
124 if ( !isset( $this->fileMetadata[$key] ) ) {
131 if ( !isset( $this->fileMetadata[$key] ) ) {
133 wfMessage(
'uploadstash-file-not-found', $key )
142 isset( $this->fileMetadata[$key][
'us_props'] ) && strlen( $this->fileMetadata[$key][
'us_props'] )
144 $this->fileProps[$key] = unserialize( $this->fileMetadata[$key][
'us_props'] );
146 wfDebug( __METHOD__ .
" fetched props for $key from file" );
147 $path = $this->fileMetadata[$key][
'us_path'];
148 $this->fileProps[$key] = $this->repo->getFileProps(
$path );
152 if ( !$this->files[$key]->exists() ) {
153 wfDebug( __METHOD__ .
" tried to get file at $key, but it doesn't exist" );
160 if ( !$noAuth && $this->fileMetadata[$key][
'us_user'] != $this->user->getId() ) {
162 wfMessage(
'uploadstash-wrong-owner', $key )
166 return $this->files[$key];
178 return $this->fileMetadata[$key];
190 return $this->fileProps[$key];
207 if ( !is_file(
$path ) ) {
208 wfDebug( __METHOD__ .
" tried to stash file at '$path', but it doesn't exist" );
219 wfDebug( __METHOD__ .
" stashing file at '$path'" );
224 if ( !preg_match(
"/\\.\\Q$extension\\E$/",
$path ) ) {
225 $pathWithGoodExtension =
"$path.$extension";
227 $pathWithGoodExtension =
$path;
235 [ $usec, $sec ] = explode(
' ', microtime() );
236 $usec = substr( $usec, 2 );
237 $key = \Wikimedia\base_convert( $sec . $usec, 10, 36 ) .
'.' .
238 \Wikimedia\base_convert( (
string)mt_rand(), 10, 36 ) .
'.' .
239 $this->user->getId() .
'.' .
244 if ( !preg_match( self::KEY_FORMAT_REGEX, $key ) ) {
246 wfMessage(
'uploadstash-bad-path-bad-format', $key )
250 wfDebug( __METHOD__ .
" key for '$path': $key" );
253 $storeStatus = $this->repo->storeTemp( basename( $pathWithGoodExtension ),
$path );
255 if ( !$storeStatus->isOK() ) {
264 foreach ( $storeStatus->getMessages(
'error' ) as $msg ) {
267 foreach ( $storeStatus->getMessages(
'warning' ) as $msg ) {
273 $stashPath = $storeStatus->value;
276 if ( !$this->user->isRegistered() ) {
283 wfDebug( __METHOD__ .
" inserting $stashPath under $key" );
284 $dbw = $this->repo->getPrimaryDB();
286 $serializedFileProps = serialize(
$fileProps );
287 if ( strlen( $serializedFileProps ) > self::MAX_US_PROPS_SIZE ) {
292 $serializedFileProps = serialize(
$fileProps );
296 'us_user' => $this->user->getId(),
298 'us_orig_path' =>
$path,
299 'us_path' => $stashPath,
300 'us_props' => $dbw->encodeBlob( $serializedFileProps ),
308 'us_source_type' => $sourceType,
309 'us_timestamp' => $dbw->timestamp(),
310 'us_status' =>
'finished'
313 $dbw->newInsertQueryBuilder()
314 ->insertInto(
'uploadstash' )
316 ->caller( __METHOD__ )->execute();
320 $insertRow[
'us_id'] = $dbw->insertId();
322 $this->fileMetadata[$key] = $insertRow;
324 # create the UploadStashFile object for this file.
338 if ( !$this->user->isRegistered() ) {
344 wfDebug( __METHOD__ .
' clearing all rows for user ' . $this->user->getId() );
345 $dbw = $this->repo->getPrimaryDB();
346 $dbw->newDeleteQueryBuilder()
347 ->deleteFrom(
'uploadstash' )
348 ->where( [
'us_user' => $this->user->getId() ] )
349 ->caller( __METHOD__ )->execute();
353 $this->fileMetadata = [];
367 if ( !$this->user->isRegistered() ) {
373 $dbw = $this->repo->getPrimaryDB();
377 $row = $dbw->newSelectQueryBuilder()
378 ->select(
'us_user' )
379 ->from(
'uploadstash' )
380 ->where( [
'us_key' => $key ] )
381 ->caller( __METHOD__ )->fetchRow();
385 wfMessage(
'uploadstash-no-such-key', $key )
389 if ( $row->us_user != $this->user->getId() ) {
391 wfMessage(
'uploadstash-wrong-owner', $key )
405 wfDebug( __METHOD__ .
" clearing row $key" );
410 $dbw = $this->repo->getPrimaryDB();
412 $dbw->newDeleteQueryBuilder()
413 ->deleteFrom(
'uploadstash' )
414 ->where( [
'us_key' => $key ] )
415 ->caller( __METHOD__ )->execute();
420 $this->files[$key]->remove();
422 unset( $this->files[$key] );
423 unset( $this->fileMetadata[$key] );
435 if ( !$this->user->isRegistered() ) {
441 $res = $this->repo->getReplicaDB()->newSelectQueryBuilder()
443 ->from(
'uploadstash' )
444 ->where( [
'us_user' => $this->user->getId() ] )
445 ->caller( __METHOD__ )->fetchResultSet();
447 if ( $res->numRows() == 0 ) {
454 foreach ( $res as $row ) {
455 $keys[] = $row->us_key;
474 $n = strrpos(
$path,
'.' );
476 if ( $n !==
false ) {
477 $extension = $n ? substr(
$path, $n + 1 ) :
'';
481 $mimeType = $magic->guessMimeType(
$path );
482 $extension = $magic->getExtensionFromMimeTypeOrNull( $mimeType ) ??
'';
485 $extension = File::normalizeExtension( $extension );
486 if ( in_array( $extension, $prohibitedFileExtensions ) ) {
508 $dbr = $this->repo->getPrimaryDB();
510 $dbr = $this->repo->getReplicaDB();
513 $row = $dbr->newSelectQueryBuilder()
515 'us_user',
'us_key',
'us_orig_path',
'us_path',
'us_props',
516 'us_size',
'us_sha1',
'us_mime',
'us_media_type',
517 'us_image_width',
'us_image_height',
'us_image_bits',
518 'us_source_type',
'us_timestamp',
'us_status',
520 ->from(
'uploadstash' )
521 ->where( [
'us_key' => $key ] )
522 ->caller( __METHOD__ )->fetchRow();
524 if ( !is_object( $row ) ) {
529 $this->fileMetadata[$key] = (array)$row;
530 $this->fileMetadata[$key][
'us_props'] = $dbr->decodeBlob( $row->us_props );
545 $this->fileMetadata[$key][
'us_path'],
547 $this->fileMetadata[$key][
'us_sha1'],
548 $this->fileMetadata[$key][
'us_mime'] ??
false
550 if ( $file->getSize() === 0 ) {
555 $this->files[$key] = $file;
562class_alias( UploadStash::class,
'UploadStash' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
MimeMagic helper wrapper.
Group all the pieces relevant to the context of a request into one instance.
A class containing constants representing the names of configuration variables.
const ProhibitedFileExtensions
Name constant for the ProhibitedFileExtensions setting, for use with Config::get()