http://www.mediawiki.org/wiki/Special:Code/MediaWiki/74014
Revision: 74014
Author: neilk
Date: 2010-09-30 11:04:11 +0000 (Thu, 30 Sep 2010)
Log Message:
-----------
SpecialSessionStash now can serve thumbnails to client, with proper HTTP errors
on failure
Modified Paths:
--------------
branches/uploadwizard/phase3/includes/AutoLoader.php
branches/uploadwizard/phase3/includes/SpecialPage.php
branches/uploadwizard/phase3/includes/upload/SessionStash.php
branches/uploadwizard/phase3/languages/messages/MessagesEn.php
Added Paths:
-----------
branches/uploadwizard/phase3/includes/specials/SpecialSessionStash.php
Modified: branches/uploadwizard/phase3/includes/AutoLoader.php
===================================================================
--- branches/uploadwizard/phase3/includes/AutoLoader.php 2010-09-30
07:46:37 UTC (rev 74013)
+++ branches/uploadwizard/phase3/includes/AutoLoader.php 2010-09-30
11:04:11 UTC (rev 74014)
@@ -632,6 +632,7 @@
'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
'SpecialRecentchangeslinked' =>
'includes/specials/SpecialRecentchangeslinked.php',
'SpecialSearch' => 'includes/specials/SpecialSearch.php',
+ 'SpecialSessionStash' => 'includes/specials/SpecialSessionStash.php',
'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php',
'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
'SpecialTags' => 'includes/specials/SpecialTags.php',
Modified: branches/uploadwizard/phase3/includes/SpecialPage.php
===================================================================
--- branches/uploadwizard/phase3/includes/SpecialPage.php 2010-09-30
07:46:37 UTC (rev 74013)
+++ branches/uploadwizard/phase3/includes/SpecialPage.php 2010-09-30
11:04:11 UTC (rev 74014)
@@ -149,6 +149,7 @@
'MIMEsearch' => array( 'SpecialPage',
'MIMEsearch' ),
'FileDuplicateSearch' => array( 'SpecialPage',
'FileDuplicateSearch' ),
'Upload' => 'SpecialUpload',
+ 'SessionStash' => array( 'SpecialSessionStash',
'SessionStash', 'upload' ),
# Wiki data and tools
'Statistics' => 'SpecialStatistics',
Added: branches/uploadwizard/phase3/includes/specials/SpecialSessionStash.php
===================================================================
--- branches/uploadwizard/phase3/includes/specials/SpecialSessionStash.php
(rev 0)
+++ branches/uploadwizard/phase3/includes/specials/SpecialSessionStash.php
2010-09-30 11:04:11 UTC (rev 74014)
@@ -0,0 +1,117 @@
+<?php
+/**
+ * Special:SessionStash
+ *
+ * Web access for files temporarily stored by SessionStash.
+ *
+ * For example -- files that were uploaded with the UploadWizard extension are
stored temporarily
+ * before committing them to the db. But we want to see their thumbnails and
get other information
+ * about them.
+ *
+ * Since this is based on the user's session, in effect this creates a private
temporary file area.
+ * However, the URLs for the files cannot be shared.
+ *
+ * @file
+ * @ingroup SpecialPage
+ * @ingroup Upload
+ */
+
+class SpecialSessionStash extends SpecialPage {
+
+ static $HttpErrors = array(
+ 400 => 'Bad Request',
+ 403 => 'Access Denied',
+ 404 => 'File not found',
+ 500 => 'Internal Server Error',
+ );
+
+ // SessionStash
+ private $stash;
+
+ // we should not be reading in really big files and serving them out
+ private $maxServeFileSize = 262144; // 256K
+
+ // $request is the request (usually wgRequest)
+ // $subpage is everything in the URL after Special:SessionStash
+ public function __construct( $request=null, $subpage=null ) {
+ global $wgRequest;
+
+ parent::__construct( 'SessionStash', 'upload' );
+
+ $this->stash = new SessionStash();
+
+ }
+
+ /**
+ * If file available in stash, cats it out to the client as a simple
HTTP response.
+ * n.b. Most sanity checking done in SessionStashLocalFile, so this is
straightforward.
+ *
+ * @param {String} subpage, e.g. in
http://sample.com/wiki/Special:SessionStash/foo.jpg, the "foo".
+ * @return {Boolean} success
+ */
+ public function execute( $subPage ) {
+ global $wgOut;
+
+ // prevent callers from doing standard HTML output -- we'll
take it from here
+ $wgOut->disable();
+
+ // global $wgScriptPath, $wgLang, $wgUser, $wgOut;
+ wfDebug( __METHOD__ . " in subpage for $subPage \n" );
+
+ try {
+ $file = $this->getStashFile( $subPage );
+ if ( $file->getSize() > $this->maxServeFileSize ) {
+ throw new MWException( 'file size too large' );
+ }
+ $this->outputFile( $file );
+ return true;
+
+ } catch( SessionStashFileNotFoundException $e ) {
+ wfHttpError( 404, self::$HttpErrors[404],
$e->getCode(), $e->getMessage() );
+
+ } catch( SessionStashBadPathException $e ) {
+ wfHttpError( 403, self::$HttpErrors[403],
$e->getCode(), $e->getMessage() );
+
+ } catch( Exception $e ) {
+ wfHttpError( $code, self::$HttpErrors[$code],
$e->getCode(), $e->getMessage() );
+
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Convert the incoming url portion (subpage of Special page) into a
stashed file, if available.
+ * @param {String} $subPage
+ * @return {File} file object
+ * @throws MWException, SessionStashFileNotFoundException,
SessionStashBadPathException
+ */
+ private function getStashFile( $subPage );
+ // due to an implementation quirk (and trying to be compatible
with older method)
+ // the stash key doesn't have an extension
+ $key = $subPage;
+ $n = strrpos( $subPage, '.' );
+ if ( $n !== false ) {
+ $key = $n ? substr( $subPage, 0, $n ) : $subPage;
+ }
+
+ $file = $this->stash->getFile( $key );
+ return $file;
+ }
+
+ /**
+ * Output HTTP response for file
+ * Side effects, obviously, of echoing lots of stuff to stdout.
+ * @param {File} file
+ */
+ private function outputFile( $file ) {
+ header( 'Content-Type: ' . $file->getMimeType() );
+ header( 'Content-Transfer-Encoding: binary' );
+ header( 'Expires: Sun, 17-Jan-2038 19:14:07 GMT' );
+ header( 'Pragma: public' );
+ header( 'Content-Length: ' . $file->getSize() );
+ readfile( $file->getPath() );
+ }
+}
+
Property changes on:
branches/uploadwizard/phase3/includes/specials/SpecialSessionStash.php
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: branches/uploadwizard/phase3/includes/upload/SessionStash.php
===================================================================
--- branches/uploadwizard/phase3/includes/upload/SessionStash.php
2010-09-30 07:46:37 UTC (rev 74013)
+++ branches/uploadwizard/phase3/includes/upload/SessionStash.php
2010-09-30 11:04:11 UTC (rev 74014)
@@ -3,11 +3,13 @@
* SessionStash is intended to accomplish a few things:
* - enable applications to temporarily stash files without publishing them
to the wiki.
* - Several parts of MediaWiki do this in similar ways: UploadBase,
UploadWizard, and FirefoggChunkedExtension
- * the idea is to unify them all here
+ * And there are several that reimplement stashing from scratch, in
idiosyncratic ways. The idea is to unify them all here.
+ * Mostly all of them are the same except for storing some custom
fields, which we subsume into the data array.
* - enable applications to find said files later, as long as the session or
temp files haven't been purged.
* - enable the uploading user (and *ONLY* the uploading user) to access
said files, and thumbnails of said files, via a URL.
* We accomplish this by making the session serve as a URL->file mapping,
on the assumption that nobody else can access
- * the session, even the uploading user.
+ * the session, even the uploading user. See SpecialSessionStash, which
implements a web interface to some files stored this way.
+ *
*/
class SessionStash {
@@ -34,6 +36,11 @@
if ( is_null( $repo ) ) {
$repo = RepoGroup::singleton()->getLocalRepo();
}
+
+ // sanity check repo. If we want to mock the repo later this
should be modified.
+ if ( ! is_dir( $repo->getZonePath( 'temp' ) ) ) {
+ throw new MWException( 'invalid repo or cannot read
repo temp dir' );
+ }
$this->repo = $repo;
if ( ! isset( $_SESSION ) ) {
@@ -47,6 +54,10 @@
$this->baseUrl = SpecialPage::getTitleFor( 'SessionStash'
)->getLocalURL();
}
+ /**
+ * Get the base of URLs by which one can access the files
+ * @return {String} url
+ */
public function getBaseUrl() {
return $this->baseUrl;
}
@@ -55,14 +66,22 @@
* Get a file from the stash.
* May throw exception if session data cannot be parsed due to schema
change.
* @param {Integer} key
- * @return {null|SessionStashItem} null if no such item, or the item
+ * @return {null|SessionStashItem} null if no such item or item out of
date, or the item
*/
public function getFile( $key ) {
- if ( !array_key_exists( $key, $this->files ) ) {
+ if ( !isset( $this->files[$key] ) ) {
+ wfDebug( "checking key = <$key> is in session\n" );
+ if ( !isset(
$_SESSION[UploadBase::SESSION_KEYNAME][$key] ) ) {
+ wfDebug( "checking key = <$key> is in session -
it isn't\n" );
+ wfDebug( print_r(
$_SESSION[UploadBase::SESSION_KEYNAME], 1 ) );
+ throw new SessionStashFileNotFoundException();
+ }
+
$stashData =
$_SESSION[UploadBase::SESSION_KEYNAME][$key];
-
+
+ // guards against PHP class changing while session data
doesn't
if ($stashData['version'] !==
UploadBase::SESSION_VERSION ) {
- throw new MWException( 'session item schema
does not match current software' );
+ return self::$error['outdated session version'];
}
// The path is flattened in with the other random props
so we have to dig it out.
@@ -74,7 +93,10 @@
$data[ $stashKey ] = $stashVal;
}
}
- $this->files[$key] = new SessionStashFile( $this,
$this->repo, $path, $key, $data );
+
+ $file = new SessionStashFile( $this, $this->repo,
$path, $key, $data );
+ $this->files[$key] = $file;
+
}
return $this->files[$key];
}
@@ -145,9 +167,25 @@
$this->sessionStash = $stash;
$this->sessionKey = $key;
$this->sessionData = $data;
+
+ // resolve mwrepo:// urls
if ( $repo->isVirtualUrl( $path ) ) {
$path = $repo->resolveVirtualUrl( $path );
}
+
+ // check if path appears to be sane, no parent traverals, and
is in this repo's temp zone.
+ if ( ( ! $repo->validateFilename( $path ) ) ||
+ ( strpos( $path, $repo->getZonePath( 'temp' ) ) !== 0 )
) {
+ throw new SessionStashBadPathException();
+ }
+
+ wfDebug( "checking if path exists and is good: $path " );
+ // check if path exists! and is a plain file.
+ if ( ! $repo->fileExists( $path, $repo::FILES_ONLY ) ) {
+ wfDebug( "checking if path exists and is good: $path
-- no!! " );
+ throw new SessionStashFileNotFoundException();
+ }
+
parent::__construct( false, $repo, $path, false );
// we will be initializing from some tmpnam files that don't
have extensions.
@@ -158,6 +196,36 @@
}
/**
+ * Test if a path looks like it's in the right place
+ *
+ * @param {String} $path
+ * @return {Boolean}
+ */
+ public function isPathValid( $path ) {
+
+ if ( strval( $filename ) == '' ) {
+ return false;
+ }
+
+ /**
+ * Lifted this bit from extensions/WebStore::validateFilename.
+ * Use the same traversal protection as
Title::secureAndSplit()
+ */
+ if ( strpos( $filename, '.' ) !== false &&
+ ( $filename === '.' || $filename === '..' ||
+ strpos( $filename, './' ) === 0 ||
+ strpos( $filename, '../' ) === 0 ||
+ strpos( $filename, '/./' ) !== false ||
+ strpos( $filename, '/../' ) !== false ) ) {
+ return false;
+ }
+
+
+ return true;
+
+ }
+
+ /**
* A method needed by the file transforming and scaling routines in
File.php
* We do not necessarily care about doing the description at this point
* @return {String} the empty string
@@ -193,7 +261,7 @@
}
if ( is_null( $extension ) ) {
- throw 'cannot determine extension';
+ throw new MWException( 'cannot determine extension' );
}
$this->extension = parent::normalizeExtension( $extension );
@@ -325,3 +393,7 @@
}
}
+
+class SessionStashFileNotFoundException extends MWException {};
+class SessionStashBadPathException extends MWException {};
+
Modified: branches/uploadwizard/phase3/languages/messages/MessagesEn.php
===================================================================
--- branches/uploadwizard/phase3/languages/messages/MessagesEn.php
2010-09-30 07:46:37 UTC (rev 74013)
+++ branches/uploadwizard/phase3/languages/messages/MessagesEn.php
2010-09-30 11:04:11 UTC (rev 74014)
@@ -460,6 +460,7 @@
'RevisionMove' => array( 'RevisionMove' ),
'ComparePages' => array( 'ComparePages' ),
'Badtitle' => array( 'Badtitle' ),
+ 'SessionStash' => array( 'SessionStash' ),
);
/**
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs