[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Backend for uploading files from a HTTP resource. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup Upload 22 */ 23 24 /** 25 * Implements uploading from a HTTP resource. 26 * 27 * @ingroup Upload 28 * @author Bryan Tong Minh 29 * @author Michael Dale 30 */ 31 class UploadFromUrl extends UploadBase { 32 protected $mAsync, $mUrl; 33 protected $mIgnoreWarnings = true; 34 35 protected $mTempPath, $mTmpHandle; 36 37 protected static $allowedUrls = array(); 38 39 /** 40 * Checks if the user is allowed to use the upload-by-URL feature. If the 41 * user is not allowed, return the name of the user right as a string. If 42 * the user is allowed, have the parent do further permissions checking. 43 * 44 * @param User $user 45 * 46 * @return bool|string 47 */ 48 public static function isAllowed( $user ) { 49 if ( !$user->isAllowed( 'upload_by_url' ) ) { 50 return 'upload_by_url'; 51 } 52 53 return parent::isAllowed( $user ); 54 } 55 56 /** 57 * Checks if the upload from URL feature is enabled 58 * @return bool 59 */ 60 public static function isEnabled() { 61 global $wgAllowCopyUploads; 62 63 return $wgAllowCopyUploads && parent::isEnabled(); 64 } 65 66 /** 67 * Checks whether the URL is for an allowed host 68 * The domains in the whitelist can include wildcard characters (*) in place 69 * of any of the domain levels, e.g. '*.flickr.com' or 'upload.*.gov.uk'. 70 * 71 * @param string $url 72 * @return bool 73 */ 74 public static function isAllowedHost( $url ) { 75 global $wgCopyUploadsDomains; 76 if ( !count( $wgCopyUploadsDomains ) ) { 77 return true; 78 } 79 $parsedUrl = wfParseUrl( $url ); 80 if ( !$parsedUrl ) { 81 return false; 82 } 83 $valid = false; 84 foreach ( $wgCopyUploadsDomains as $domain ) { 85 // See if the domain for the upload matches this whitelisted domain 86 $whitelistedDomainPieces = explode( '.', $domain ); 87 $uploadDomainPieces = explode( '.', $parsedUrl['host'] ); 88 if ( count( $whitelistedDomainPieces ) === count( $uploadDomainPieces ) ) { 89 $valid = true; 90 // See if all the pieces match or not (excluding wildcards) 91 foreach ( $whitelistedDomainPieces as $index => $piece ) { 92 if ( $piece !== '*' && $piece !== $uploadDomainPieces[$index] ) { 93 $valid = false; 94 } 95 } 96 if ( $valid ) { 97 // We found a match, so quit comparing against the list 98 break; 99 } 100 } 101 /* Non-wildcard test 102 if ( $parsedUrl['host'] === $domain ) { 103 $valid = true; 104 break; 105 } 106 */ 107 } 108 109 return $valid; 110 } 111 112 /** 113 * Checks whether the URL is not allowed. 114 * 115 * @param string $url 116 * @return bool 117 */ 118 public static function isAllowedUrl( $url ) { 119 if ( !isset( self::$allowedUrls[$url] ) ) { 120 $allowed = true; 121 wfRunHooks( 'IsUploadAllowedFromUrl', array( $url, &$allowed ) ); 122 self::$allowedUrls[$url] = $allowed; 123 } 124 125 return self::$allowedUrls[$url]; 126 } 127 128 /** 129 * Entry point for API upload 130 * 131 * @param string $name 132 * @param string $url 133 * @param bool|string $async Whether the download should be performed 134 * asynchronous. False for synchronous, async or async-leavemessage for 135 * asynchronous download. 136 * @throws MWException 137 */ 138 public function initialize( $name, $url, $async = false ) { 139 global $wgAllowAsyncCopyUploads; 140 141 $this->mUrl = $url; 142 $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false; 143 if ( $async ) { 144 throw new MWException( 'Asynchronous copy uploads are no longer possible as of r81612.' ); 145 } 146 147 $tempPath = $this->mAsync ? null : $this->makeTemporaryFile(); 148 # File size and removeTempFile will be filled in later 149 $this->initializePathInfo( $name, $tempPath, 0, false ); 150 } 151 152 /** 153 * Entry point for SpecialUpload 154 * @param WebRequest $request 155 */ 156 public function initializeFromRequest( &$request ) { 157 $desiredDestName = $request->getText( 'wpDestFile' ); 158 if ( !$desiredDestName ) { 159 $desiredDestName = $request->getText( 'wpUploadFileURL' ); 160 } 161 $this->initialize( 162 $desiredDestName, 163 trim( $request->getVal( 'wpUploadFileURL' ) ), 164 false 165 ); 166 } 167 168 /** 169 * @param WebRequest $request 170 * @return bool 171 */ 172 public static function isValidRequest( $request ) { 173 global $wgUser; 174 175 $url = $request->getVal( 'wpUploadFileURL' ); 176 177 return !empty( $url ) 178 && Http::isValidURI( $url ) 179 && $wgUser->isAllowed( 'upload_by_url' ); 180 } 181 182 /** 183 * @return string 184 */ 185 public function getSourceType() { 186 return 'url'; 187 } 188 189 /** 190 * Download the file (if not async) 191 * 192 * @param array $httpOptions Array of options for MWHttpRequest. Ignored if async. 193 * This could be used to override the timeout on the http request. 194 * @return Status 195 */ 196 public function fetchFile( $httpOptions = array() ) { 197 if ( !Http::isValidURI( $this->mUrl ) ) { 198 return Status::newFatal( 'http-invalid-url' ); 199 } 200 201 if ( !self::isAllowedHost( $this->mUrl ) ) { 202 return Status::newFatal( 'upload-copy-upload-invalid-domain' ); 203 } 204 if ( !self::isAllowedUrl( $this->mUrl ) ) { 205 return Status::newFatal( 'upload-copy-upload-invalid-url' ); 206 } 207 if ( !$this->mAsync ) { 208 return $this->reallyFetchFile( $httpOptions ); 209 } 210 211 return Status::newGood(); 212 } 213 214 /** 215 * Create a new temporary file in the URL subdirectory of wfTempDir(). 216 * 217 * @return string Path to the file 218 */ 219 protected function makeTemporaryFile() { 220 $tmpFile = TempFSFile::factory( 'URL' ); 221 $tmpFile->bind( $this ); 222 223 return $tmpFile->getPath(); 224 } 225 226 /** 227 * Callback: save a chunk of the result of a HTTP request to the temporary file 228 * 229 * @param mixed $req 230 * @param string $buffer 231 * @return int Number of bytes handled 232 */ 233 public function saveTempFileChunk( $req, $buffer ) { 234 $nbytes = fwrite( $this->mTmpHandle, $buffer ); 235 236 if ( $nbytes == strlen( $buffer ) ) { 237 $this->mFileSize += $nbytes; 238 } else { 239 // Well... that's not good! 240 fclose( $this->mTmpHandle ); 241 $this->mTmpHandle = false; 242 } 243 244 return $nbytes; 245 } 246 247 /** 248 * Download the file, save it to the temporary file and update the file 249 * size and set $mRemoveTempFile to true. 250 * 251 * @param array $httpOptions Array of options for MWHttpRequest 252 * @return Status 253 */ 254 protected function reallyFetchFile( $httpOptions = array() ) { 255 global $wgCopyUploadProxy, $wgCopyUploadTimeout; 256 if ( $this->mTempPath === false ) { 257 return Status::newFatal( 'tmp-create-error' ); 258 } 259 260 // Note the temporary file should already be created by makeTemporaryFile() 261 $this->mTmpHandle = fopen( $this->mTempPath, 'wb' ); 262 if ( !$this->mTmpHandle ) { 263 return Status::newFatal( 'tmp-create-error' ); 264 } 265 266 $this->mRemoveTempFile = true; 267 $this->mFileSize = 0; 268 269 $options = $httpOptions + array( 'followRedirects' => true ); 270 271 if ( $wgCopyUploadProxy !== false ) { 272 $options['proxy'] = $wgCopyUploadProxy; 273 } 274 275 if ( $wgCopyUploadTimeout && !isset( $options['timeout'] ) ) { 276 $options['timeout'] = $wgCopyUploadTimeout; 277 } 278 $req = MWHttpRequest::factory( $this->mUrl, $options ); 279 $req->setCallback( array( $this, 'saveTempFileChunk' ) ); 280 $status = $req->execute(); 281 282 if ( $this->mTmpHandle ) { 283 // File got written ok... 284 fclose( $this->mTmpHandle ); 285 $this->mTmpHandle = null; 286 } else { 287 // We encountered a write error during the download... 288 return Status::newFatal( 'tmp-write-error' ); 289 } 290 291 if ( !$status->isOk() ) { 292 return $status; 293 } 294 295 return $status; 296 } 297 298 /** 299 * Wrapper around the parent function in order to defer verifying the 300 * upload until the file really has been fetched. 301 * @return array|mixed 302 */ 303 public function verifyUpload() { 304 if ( $this->mAsync ) { 305 return array( 'status' => UploadBase::OK ); 306 } 307 308 return parent::verifyUpload(); 309 } 310 311 /** 312 * Wrapper around the parent function in order to defer checking warnings 313 * until the file really has been fetched. 314 * @return array 315 */ 316 public function checkWarnings() { 317 if ( $this->mAsync ) { 318 $this->mIgnoreWarnings = false; 319 320 return array(); 321 } 322 323 return parent::checkWarnings(); 324 } 325 326 /** 327 * Wrapper around the parent function in order to defer checking protection 328 * until we are sure that the file can actually be uploaded 329 * @param User $user 330 * @return bool|mixed 331 */ 332 public function verifyTitlePermissions( $user ) { 333 if ( $this->mAsync ) { 334 return true; 335 } 336 337 return parent::verifyTitlePermissions( $user ); 338 } 339 340 /** 341 * Wrapper around the parent function in order to defer uploading to the 342 * job queue for asynchronous uploads 343 * @param string $comment 344 * @param string $pageText 345 * @param bool $watch 346 * @param User $user 347 * @return Status 348 */ 349 public function performUpload( $comment, $pageText, $watch, $user ) { 350 if ( $this->mAsync ) { 351 $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user ); 352 353 return Status::newFatal( 'async', $sessionKey ); 354 } 355 356 return parent::performUpload( $comment, $pageText, $watch, $user ); 357 } 358 359 /** 360 * @param string $comment 361 * @param string $pageText 362 * @param bool $watch 363 * @param User $user 364 * @return string 365 */ 366 protected function insertJob( $comment, $pageText, $watch, $user ) { 367 $sessionKey = $this->stashSession(); 368 $job = new UploadFromUrlJob( $this->getTitle(), array( 369 'url' => $this->mUrl, 370 'comment' => $comment, 371 'pageText' => $pageText, 372 'watch' => $watch, 373 'userName' => $user->getName(), 374 'leaveMessage' => $this->mAsync == 'async-leavemessage', 375 'ignoreWarnings' => $this->mIgnoreWarnings, 376 'sessionId' => session_id(), 377 'sessionKey' => $sessionKey, 378 ) ); 379 $job->initializeSessionData(); 380 JobQueueGroup::singleton()->push( $job ); 381 382 return $sessionKey; 383 } 384 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |