UploadStashFileNotFoundException and UploadStashBadPathException are defined in uploa...
[lhc/web/wiklou.git] / includes / specials / SpecialUploadStash.php
1 <?php
2 /**
3 * Special:UploadStash
4 *
5 * Web access for files temporarily stored by UploadStash.
6 *
7 * For example -- files that were uploaded with the UploadWizard extension are stored temporarily
8 * before committing them to the db. But we want to see their thumbnails and get other information
9 * about them.
10 *
11 * Since this is based on the user's session, in effect this creates a private temporary file area.
12 * However, the URLs for the files cannot be shared.
13 *
14 * @file
15 * @ingroup SpecialPage
16 * @ingroup Upload
17 */
18
19 require dirname( __FILE__ ) . '/../upload/UploadStash.php';
20
21 class SpecialUploadStash extends SpecialPage {
22
23 static $HttpErrors = array( // FIXME: Use OutputPage::getStatusMessage() --RK
24 400 => 'Bad Request',
25 403 => 'Access Denied',
26 404 => 'File not found',
27 500 => 'Internal Server Error',
28 );
29
30 // UploadStash
31 private $stash;
32
33 // we should not be reading in really big files and serving them out
34 private $maxServeFileSize = 262144; // 256K
35
36 // $request is the request (usually wgRequest)
37 // $subpage is everything in the URL after Special:UploadStash
38 // FIXME: These parameters don't match SpecialPage::__construct()'s params at all, and are unused --RK
39 public function __construct( $request = null, $subpage = null ) {
40 parent::__construct( 'UploadStash', 'upload' );
41 $this->stash = new UploadStash();
42 }
43
44 /**
45 * If file available in stash, cats it out to the client as a simple HTTP response.
46 * n.b. Most sanity checking done in UploadStashLocalFile, so this is straightforward.
47 *
48 * @param {String} $subPage: subpage, e.g. in http://example.com/wiki/Special:UploadStash/foo.jpg, the "foo.jpg" part
49 * @return {Boolean} success
50 */
51 public function execute( $subPage ) {
52 global $wgOut, $wgUser;
53
54 if ( !$this->userCanExecute( $wgUser ) ) {
55 $this->displayRestrictionError();
56 return;
57 }
58
59 // prevent callers from doing standard HTML output -- we'll take it from here
60 $wgOut->disable();
61
62 try {
63 $file = $this->getStashFile( $subPage );
64 if ( $file->getSize() > $this->maxServeFileSize ) {
65 throw new MWException( 'file size too large' );
66 }
67 $this->outputFile( $file );
68 return true;
69
70 } catch( UploadStashFileNotFoundException $e ) {
71 $code = 404;
72 } catch( UploadStashBadPathException $e ) {
73 $code = 403;
74 } catch( Exception $e ) {
75 $code = 500;
76 }
77
78 wfHttpError( $code, self::$HttpErrors[$code], $e->getCode(), $e->getMessage() );
79 return false;
80 }
81
82
83 /**
84 * Convert the incoming url portion (subpage of Special page) into a stashed file, if available.
85 * @param {String} $subPage
86 * @return {File} file object
87 * @throws MWException, UploadStashFileNotFoundException, UploadStashBadPathException
88 */
89 private function getStashFile( $subPage ) {
90 // due to an implementation quirk (and trying to be compatible with older method)
91 // the stash key doesn't have an extension
92 $key = $subPage;
93 $n = strrpos( $subPage, '.' );
94 if ( $n !== false ) {
95 $key = $n ? substr( $subPage, 0, $n ) : $subPage;
96 }
97
98 try {
99 $file = $this->stash->getFile( $key );
100 } catch ( UploadStashFileNotFoundException $e ) {
101 // if we couldn't find it, and it looks like a thumbnail,
102 // and it looks like we have the original, go ahead and generate it
103 $matches = array();
104 if ( ! preg_match( '/^(\d+)px-(.*)$/', $key, $matches ) ) {
105 // that doesn't look like a thumbnail. re-raise exception
106 throw $e;
107 }
108
109 list( $dummy, $width, $origKey ) = $matches;
110
111 // do not trap exceptions, if key is in bad format, or file not found,
112 // let exceptions propagate to caller.
113 $origFile = $this->stash->getFile( $origKey );
114
115 // ok we're here so the original must exist. Generate the thumbnail.
116 // because the file is a UploadStashFile, this thumbnail will also be stashed,
117 // and a thumbnailFile will be created in the thumbnailImage composite object
118 $thumbnailImage = $origFile->transform( array( 'width' => $width ) );
119 if ( !$thumbnailImage ) {
120 throw new MWException( 'Could not obtain thumbnail' );
121 }
122 $file = $thumbnailImage->thumbnailFile;
123 }
124
125 return $file;
126 }
127
128 /**
129 * Output HTTP response for file
130 * Side effects, obviously, of echoing lots of stuff to stdout.
131 * @param {File} file
132 */
133 private function outputFile( $file ) {
134 header( 'Content-Type: ' . $file->getMimeType(), true );
135 header( 'Content-Transfer-Encoding: binary', true );
136 header( 'Expires: Sun, 17-Jan-2038 19:14:07 GMT', true );
137 header( 'Pragma: public', true );
138 header( 'Content-Length: ' . $file->getSize(), true ); // FIXME: PHP can handle Content-Length for you just fine --RK
139 readfile( $file->getPath() );
140 }
141 }
142