Index: trunk/phase3/includes/UploadFromUrl.php |
— | — | @@ -2,7 +2,7 @@ |
3 | 3 | |
4 | 4 | |
5 | 5 | class UploadFromUrl extends UploadBase { |
6 | | - static function isAllowed( User $user ) { |
| 6 | + static function isAllowed( $user ) { |
7 | 7 | if( !$user->isAllowed( 'upload_by_url' ) ) |
8 | 8 | return 'upload_by_url'; |
9 | 9 | return parent::isAllowed( $user ); |
— | — | @@ -12,75 +12,62 @@ |
13 | 13 | return $wgAllowCopyUploads && parent::isEnabled(); |
14 | 14 | } |
15 | 15 | |
16 | | - function initialize( $url ) { |
| 16 | + function initialize( $name, $url ) { |
17 | 17 | global $wgTmpDirectory; |
18 | 18 | $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' ); |
| 19 | + $this-initialize( $name, $local_file, 0, true ); |
19 | 20 | |
20 | | - $this->mTempPath = $local_file; |
21 | | - $this->mFileSize = 0; # Will be set by curlCopy |
22 | | - $this->mCurlError = $this->curlCopy( $url, $local_file ); |
23 | | - $pathParts = explode( '/', $url ); |
24 | | - $this->mSrcName = array_pop( $pathParts ); |
25 | | - $this->mSessionKey = false; |
26 | | - $this->mStashed = false; |
27 | | - |
28 | | - // PHP won't auto-cleanup the file |
29 | | - $this->mRemoveTempFile = file_exists( $local_file ); |
| 21 | + $this->mUrl = trim( $url ); |
30 | 22 | } |
31 | 23 | |
| 24 | + function verifyUpload() { |
| 25 | + if( stripos($this->mUrl, 'http://') !== 0 && stripos($this->mUrl, 'ftp://') !== 0 ) { |
| 26 | + return array( |
| 27 | + 'status' => self::BEFORE_PROCESSING, |
| 28 | + 'error' => 'upload-proto-error', |
| 29 | + ); |
| 30 | + } |
| 31 | + $res = $this->curlCopy(); |
| 32 | + if( $res !== true ) { |
| 33 | + return array( |
| 34 | + 'status' => self::BEFORE_PROCESSING, |
| 35 | + 'error' => $res, |
| 36 | + ); |
| 37 | + } |
| 38 | + return parent::verifyUpload(); |
| 39 | + } |
32 | 40 | |
33 | 41 | /** |
34 | 42 | * Safe copy from URL |
35 | 43 | * Returns true if there was an error, false otherwise |
36 | 44 | */ |
37 | | - private function curlCopy( $url, $dest ) { |
| 45 | + private function curlCopy() { |
38 | 46 | global $wgUser, $wgOut; |
39 | 47 | |
40 | | - // Bad bad bad! |
41 | | - if( !$wgUser->isAllowed( 'upload_by_url' ) ) { |
42 | | - $wgOut->permissionRequired( 'upload_by_url' ); |
43 | | - return true; |
44 | | - } |
45 | | - |
46 | | - # Maybe remove some pasting blanks :-) |
47 | | - $url = trim( $url ); |
48 | | - if( stripos($url, 'http://') !== 0 && stripos($url, 'ftp://') !== 0 ) { |
49 | | - # Only HTTP or FTP URLs |
50 | | - $wgOut->showErrorPage( 'upload-proto-error', 'upload-proto-error-text' ); |
51 | | - return true; |
52 | | - } |
53 | | - |
54 | 48 | # Open temporary file |
55 | 49 | $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" ); |
56 | 50 | if( $this->mCurlDestHandle === false ) { |
57 | 51 | # Could not open temporary file to write in |
58 | | - $wgOut->showErrorPage( 'upload-file-error', 'upload-file-error-text'); |
59 | | - return true; |
| 52 | + return 'upload-file-error'; |
60 | 53 | } |
61 | 54 | |
62 | 55 | $ch = curl_init(); |
63 | 56 | curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug |
64 | 57 | curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout |
65 | 58 | curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed |
66 | | - curl_setopt( $ch, CURLOPT_URL, $url); |
| 59 | + curl_setopt( $ch, CURLOPT_URL, $this->mUrl); |
67 | 60 | curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) ); |
68 | 61 | curl_exec( $ch ); |
69 | | - $error = curl_errno( $ch ) ? true : false; |
70 | | - $errornum = curl_errno( $ch ); |
71 | | - // if ( $error ) print curl_error ( $ch ) ; # Debugging output |
| 62 | + $error = curl_errno( $ch ); |
72 | 63 | curl_close( $ch ); |
73 | 64 | |
74 | 65 | fclose( $this->mCurlDestHandle ); |
75 | 66 | unset( $this->mCurlDestHandle ); |
76 | | - if( $error ) { |
77 | | - unlink( $dest ); |
78 | | - if( wfEmptyMsg( "upload-curl-error$errornum", wfMsg("upload-curl-error$errornum") ) ) |
79 | | - $wgOut->showErrorPage( 'upload-misc-error', 'upload-misc-error-text' ); |
80 | | - else |
81 | | - $wgOut->showErrorPage( "upload-curl-error$errornum", "upload-curl-error$errornum-text" ); |
82 | | - } |
| 67 | + |
| 68 | + if( $error ) |
| 69 | + return "upload-curl-error$errornum"; |
83 | 70 | |
84 | | - return $error; |
| 71 | + return true; |
85 | 72 | } |
86 | 73 | |
87 | 74 | /** |
— | — | @@ -99,12 +86,4 @@ |
100 | 87 | fwrite( $this->mCurlDestHandle, $data ); |
101 | 88 | return $length; |
102 | 89 | } |
103 | | - |
104 | | - function execute( &$resultDetails ) { |
105 | | - /* Check for curl error */ |
106 | | - if( $this->mCurlError ) { |
107 | | - return self::BEFORE_PROCESSING; |
108 | | - } |
109 | | - return parent::execute( $resultDetails ); |
110 | | - } |
111 | 90 | } |
Index: trunk/phase3/includes/UploadFromStash.php |
— | — | @@ -1,21 +1,47 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | class UploadFromStash extends UploadBase { |
5 | | - function initialize( &$sessionData ) { |
| 5 | + static function isValidSessionKey( $key, $sessionData ) { |
| 6 | + return !empty( $key ) && |
| 7 | + is_array( $sessionData ) && |
| 8 | + isset( $sessionData[$key] ) && |
| 9 | + isset( $sessionData[$key]['version'] ) && |
| 10 | + $sessionData[$key]['version'] == self::SESSION_VERSION |
| 11 | + ; |
| 12 | + } |
| 13 | + static function isValidRequest( $request ) { |
| 14 | + $sessionData = $request->getSessionData('wsUploadData'); |
| 15 | + return self::isValidSessionKey( |
| 16 | + $request->getInt( 'wpSessionKey' ), |
| 17 | + $sessionData |
| 18 | + ); |
| 19 | + } |
| 20 | + |
| 21 | + function initialize( $name, $sessionData ) { |
6 | 22 | /** |
7 | 23 | * Confirming a temporarily stashed upload. |
8 | 24 | * We don't want path names to be forged, so we keep |
9 | 25 | * them in the session on the server and just give |
10 | 26 | * an opaque key to the user agent. |
11 | 27 | */ |
| 28 | + $this->initialize( $name, |
| 29 | + $sessionData['mTempPath'], |
| 30 | + $sessionData['mFileSize'], |
| 31 | + false |
| 32 | + ); |
12 | 33 | |
13 | | - $this->mTempPath = $sessionData['mTempPath']; |
14 | | - $this->mFileSize = $sessionData['mFileSize']; |
15 | | - $this->mSrcName = $sessionData['mSrcName']; |
16 | 34 | $this->mFileProps = $sessionData['mFileProps']; |
17 | | - $this->mStashed = true; |
18 | | - $this->mRemoveTempFile = false; |
19 | 35 | } |
| 36 | + function initializeFromRequest( &$request ) { |
| 37 | + $sessionKey = $request->getInt( 'wpSessionKey' ); |
| 38 | + $sessionData = $request->getSessionData('wsUploadData'); |
| 39 | + |
| 40 | + $desiredDestName = $request->getText( 'wpDestFile' ); |
| 41 | + if( !$desiredDestName ) |
| 42 | + $desiredDestName = $request->getText( 'wpUploadFile' ); |
| 43 | + |
| 44 | + return $this->initialize( $desiredDestName, $sessionData[$sessionKey] ); |
| 45 | + } |
20 | 46 | |
21 | 47 | /* |
22 | 48 | * File has been previously verified so no need to do so again. |
Index: trunk/phase3/includes/UploadFromUpload.php |
— | — | @@ -1,12 +1,20 @@ |
2 | 2 | <?php |
3 | 3 | |
4 | 4 | class UploadFromUpload extends UploadBase { |
5 | | - function initialize( $tempPath, $fileSize, $fileName ) { |
6 | | - $this->mTempPath = $tempPath; |
7 | | - $this->mFileSize = $fileSize; |
8 | | - $this->mSrcName = $fileName; |
9 | | - $this->mSessionKey = false; |
10 | | - $this->mStashed = false; |
11 | | - $this->mRemoveTempFile = false; // PHP will handle this |
| 5 | + |
| 6 | + function initializeFromRequest( &$request ) { |
| 7 | + $desiredDestName = $request->getText( 'wpDestFile' ); |
| 8 | + if( !$desiredDestName ) |
| 9 | + $desiredDestName = $request->getText( 'wpUploadFile' ); |
| 10 | + |
| 11 | + return $this->initialize( |
| 12 | + $desiredDestName, |
| 13 | + $request->getFileTempName( 'wpUploadFile' ), |
| 14 | + $request->getFileSize( 'wpUploadFile' ) |
| 15 | + ); |
12 | 16 | } |
| 17 | + |
| 18 | + static function isValidRequest( $request ) { |
| 19 | + return (bool)$request->getFileTempName( 'wpUploadFile' ); |
| 20 | + } |
13 | 21 | } |
Index: trunk/phase3/includes/UploadBase.php |
— | — | @@ -24,38 +24,74 @@ |
25 | 25 | |
26 | 26 | const SESSION_VERSION = 2; |
27 | 27 | |
| 28 | + /* |
| 29 | + * Returns true if uploads are enabled. |
| 30 | + * Can be overriden by subclasses. |
| 31 | + */ |
28 | 32 | static function isEnabled() { |
29 | 33 | global $wgEnableUploads; |
30 | 34 | return $wgEnableUploads; |
31 | 35 | } |
32 | | - static function isAllowed( User $user ) { |
| 36 | + /* |
| 37 | + * Returns true if the user can use this upload module or else a string |
| 38 | + * identifying the missing permission. |
| 39 | + * Can be overriden by subclasses. |
| 40 | + */ |
| 41 | + static function isAllowed( $user ) { |
33 | 42 | if( !$user->isAllowed( 'upload' ) ) |
34 | 43 | return 'upload'; |
35 | 44 | return true; |
36 | 45 | } |
37 | 46 | |
38 | | - function __construct( $name ) { |
39 | | - $this->mDesiredDestName = $name; |
| 47 | + static $uploadHandlers = array( 'Stash', 'Upload', 'Url' ); |
| 48 | + static function createFromRequest( &$request, $type = null ) { |
| 49 | + $type = $type ? $type : $request->getVal( 'wpSourceType' ); |
| 50 | + if( !$type ) |
| 51 | + return null; |
| 52 | + $type = ucfirst($type); |
| 53 | + $className = 'UploadFrom'.$type; |
| 54 | + if( !in_array( $type, self::$uploadHandlers ) ) |
| 55 | + return null; |
| 56 | + if( !call_user_func( array( $className, 'isEnabled' ) ) ) |
| 57 | + return null; |
| 58 | + if( !call_user_func( array( $className, 'isValidRequest' ), $request ) ) |
| 59 | + return null; |
| 60 | + |
| 61 | + $handler = new $className; |
| 62 | + $handler->initializeFromRequest( $request ); |
| 63 | + return $handler; |
40 | 64 | } |
41 | 65 | |
| 66 | + static function isValidRequest( $request ) { |
| 67 | + return false; |
| 68 | + } |
42 | 69 | |
43 | | - function verifyUpload( &$resultDetails ) { |
| 70 | + function __construct() {} |
| 71 | + |
| 72 | + function initialize( $name, $tempPath, $fileSize, $removeTempFile = false ) { |
| 73 | + $this->mDesiredDestName = $name; |
| 74 | + $this->mTempPath = $tempPath; |
| 75 | + $this->mFileSize = $fileSize; |
| 76 | + $this->mRemoveTempFile = $removeTempFile; |
| 77 | + } |
| 78 | + |
| 79 | + function verifyUpload() { |
44 | 80 | global $wgUser; |
45 | 81 | |
46 | 82 | /** |
47 | 83 | * If there was no filename or a zero size given, give up quick. |
48 | 84 | */ |
49 | | - if( empty( $this->mFileSize ) ) { |
50 | | - return self::EMPTY_FILE; |
51 | | - } |
| 85 | + if( empty( $this->mFileSize ) ) |
| 86 | + return array( 'status' => self::EMPTY_FILE ); |
52 | 87 | |
53 | 88 | $nt = $this->getTitle(); |
54 | 89 | if( is_null( $nt ) ) { |
| 90 | + $result = array( 'status' => $this->mTitleError ); |
55 | 91 | if( $this->mTitleError == self::ILLEGAL_FILENAME ) |
56 | | - $resultDetails = array( 'filtered' => $this->mFilteredName ); |
| 92 | + $resul['filtered'] = $this->mFilteredName; |
57 | 93 | if ( $this->mTitleError == self::FILETYPE_BADTYPE ) |
58 | | - $resultDetails = array( 'finalExt' => $this->mFinalExtension ); |
59 | | - return $this->mTitleError; |
| 94 | + $result['finalExt'] = $this->mFinalExtension; |
| 95 | + return $result; |
60 | 96 | } |
61 | 97 | $this->mLocalFile = wfLocalFile( $nt ); |
62 | 98 | $this->mDestName = $this->mLocalFile->getName(); |
— | — | @@ -64,30 +100,27 @@ |
65 | 101 | * In some cases we may forbid overwriting of existing files. |
66 | 102 | */ |
67 | 103 | $overwrite = $this->checkOverwrite( $this->mDestName ); |
68 | | - if( $overwrite !== true ) { |
69 | | - $resultDetails = array( 'overwrite' => $overwrite ); |
70 | | - return self::OVERWRITE_EXISTING_FILE; |
71 | | - } |
| 104 | + if( $overwrite !== true ) |
| 105 | + return array( 'status' => self::OVERWRITE_EXISTING_FILE, 'overwrite' => $overwrite ); |
72 | 106 | |
73 | 107 | /** |
74 | 108 | * Look at the contents of the file; if we can recognize the |
75 | 109 | * type but it's corrupt or data of the wrong type, we should |
76 | 110 | * probably not accept it. |
77 | 111 | */ |
78 | | - $veri = $this->verifyFile( $this->mTempPath ); |
| 112 | + $verification = $this->verifyFile( $this->mTempPath ); |
79 | 113 | |
80 | | - if( $veri !== true ) { |
81 | | - if( !is_array( $veri ) ) |
82 | | - $veri = array( $veri ); |
83 | | - $resultDetails = array( 'veri' => $veri ); |
84 | | - return self::VERIFICATION_ERROR; |
| 114 | + if( $verification !== true ) { |
| 115 | + if( !is_array( $verification ) ) |
| 116 | + $verification = array( $verification ); |
| 117 | + $verification['status'] = self::VERIFICATION_ERROR; |
| 118 | + return $verification; |
85 | 119 | } |
86 | 120 | |
87 | 121 | $error = ''; |
88 | 122 | if( !wfRunHooks( 'UploadVerification', |
89 | 123 | array( $this->mDestName, $this->mTempPath, &$error ) ) ) { |
90 | | - $resultDetails = array( 'error' => $error ); |
91 | | - return self::UPLOAD_VERIFICATION_ERROR; |
| 124 | + return array( 'status' => self::UPLOAD_VERIFICATION_ERROR, 'error' => $error ); |
92 | 125 | } |
93 | 126 | |
94 | 127 | return self::OK; |
— | — | @@ -97,8 +130,7 @@ |
98 | 131 | * Verifies that it's ok to include the uploaded file |
99 | 132 | * |
100 | 133 | * @param string $tmpfile the full path of the temporary file to verify |
101 | | - * @param string $extension The filename extension that the file is to be served with |
102 | | - * @return mixed true of the file is verified, a WikiError object otherwise. |
| 134 | + * @return mixed true of the file is verified, a string or array otherwise. |
103 | 135 | */ |
104 | 136 | protected function verifyFile( $tmpfile ) { |
105 | 137 | $this->mFileProps = File::getPropsFromPath( $this->mTempPath, |
— | — | @@ -172,7 +204,6 @@ |
173 | 205 | |
174 | 206 | global $wgCapitalLinks; |
175 | 207 | if( $this->mDesiredDestName != $filename ) |
176 | | - // Use mFilteredName so that we don't have to bother about spaces |
177 | 208 | $warning['badfilename'] = $filename; |
178 | 209 | |
179 | 210 | global $wgCheckFileExtensions, $wgFileExtensions; |
— | — | @@ -319,12 +350,7 @@ |
320 | 351 | global $wgOut; |
321 | 352 | $repo = RepoGroup::singleton()->getLocalRepo(); |
322 | 353 | $status = $repo->storeTemp( $saveName, $tempName ); |
323 | | - if ( !$status->isGood() ) { |
324 | | - $this->showError( $status->getWikiText() ); |
325 | | - return false; |
326 | | - } else { |
327 | | - return $status->value; |
328 | | - } |
| 354 | + return $status; |
329 | 355 | } |
330 | 356 | |
331 | 357 | /** |
— | — | @@ -337,18 +363,17 @@ |
338 | 364 | * @access private |
339 | 365 | */ |
340 | 366 | function stashSession() { |
341 | | - $stash = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
| 367 | + $status = $this->saveTempUploadedFile( $this->mDestName, $this->mTempPath ); |
342 | 368 | |
343 | | - if( !$stash ) { |
| 369 | + if( !$status->isGood() ) { |
344 | 370 | # Couldn't save the file. |
345 | 371 | return false; |
346 | 372 | } |
347 | 373 | |
348 | 374 | $key = mt_rand( 0, 0x7fffffff ); |
349 | 375 | $_SESSION['wsUploadData'][$key] = array( |
350 | | - 'mTempPath' => $stash, |
| 376 | + 'mTempPath' => $status->value, |
351 | 377 | 'mFileSize' => $this->mFileSize, |
352 | | - 'mSrcName' => $this->mSrcName, |
353 | 378 | 'mFileProps' => $this->mFileProps, |
354 | 379 | 'version' => self::SESSION_VERSION, |
355 | 380 | ); |