MediaWiki  1.23.14
UploadFromUrl.php
Go to the documentation of this file.
1 <?php
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 
48  public static function isAllowed( $user ) {
49  if ( !$user->isAllowed( 'upload_by_url' ) ) {
50  return 'upload_by_url';
51  }
52  return parent::isAllowed( $user );
53  }
54 
59  public static function isEnabled() {
60  global $wgAllowCopyUploads;
61  return $wgAllowCopyUploads && parent::isEnabled();
62  }
63 
72  public static function isAllowedHost( $url ) {
73  global $wgCopyUploadsDomains;
74  if ( !count( $wgCopyUploadsDomains ) ) {
75  return true;
76  }
77  $parsedUrl = wfParseUrl( $url );
78  if ( !$parsedUrl ) {
79  return false;
80  }
81  $valid = false;
82  foreach ( $wgCopyUploadsDomains as $domain ) {
83  // See if the domain for the upload matches this whitelisted domain
84  $whitelistedDomainPieces = explode( '.', $domain );
85  $uploadDomainPieces = explode( '.', $parsedUrl['host'] );
86  if ( count( $whitelistedDomainPieces ) === count( $uploadDomainPieces ) ) {
87  $valid = true;
88  // See if all the pieces match or not (excluding wildcards)
89  foreach ( $whitelistedDomainPieces as $index => $piece ) {
90  if ( $piece !== '*' && $piece !== $uploadDomainPieces[$index] ) {
91  $valid = false;
92  }
93  }
94  if ( $valid ) {
95  // We found a match, so quit comparing against the list
96  break;
97  }
98  }
99  /* Non-wildcard test
100  if ( $parsedUrl['host'] === $domain ) {
101  $valid = true;
102  break;
103  }
104  */
105  }
106  return $valid;
107  }
108 
115  public static function isAllowedUrl( $url ) {
116  if ( !isset( self::$allowedUrls[$url] ) ) {
117  $allowed = true;
118  wfRunHooks( 'IsUploadAllowedFromUrl', array( $url, &$allowed ) );
119  self::$allowedUrls[$url] = $allowed;
120  }
121  return self::$allowedUrls[$url];
122  }
123 
134  public function initialize( $name, $url, $async = false ) {
135  global $wgAllowAsyncCopyUploads;
136 
137  $this->mUrl = $url;
138  $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false;
139  if ( $async ) {
140  throw new MWException( 'Asynchronous copy uploads are no longer possible as of r81612.' );
141  }
142 
143  $tempPath = $this->mAsync ? null : $this->makeTemporaryFile();
144  # File size and removeTempFile will be filled in later
145  $this->initializePathInfo( $name, $tempPath, 0, false );
146  }
147 
152  public function initializeFromRequest( &$request ) {
153  $desiredDestName = $request->getText( 'wpDestFile' );
154  if ( !$desiredDestName ) {
155  $desiredDestName = $request->getText( 'wpUploadFileURL' );
156  }
157  $this->initialize(
158  $desiredDestName,
159  trim( $request->getVal( 'wpUploadFileURL' ) ),
160  false
161  );
162  }
163 
168  public static function isValidRequest( $request ) {
169  global $wgUser;
170 
171  $url = $request->getVal( 'wpUploadFileURL' );
172  return !empty( $url )
173  && Http::isValidURI( $url )
174  && $wgUser->isAllowed( 'upload_by_url' );
175  }
176 
180  public function getSourceType() {
181  return 'url';
182  }
183 
191  public function fetchFile( $httpOptions = array() ) {
192  if ( !Http::isValidURI( $this->mUrl ) ) {
193  return Status::newFatal( 'http-invalid-url' );
194  }
195 
196  if ( !self::isAllowedHost( $this->mUrl ) ) {
197  return Status::newFatal( 'upload-copy-upload-invalid-domain' );
198  }
199  if ( !self::isAllowedUrl( $this->mUrl ) ) {
200  return Status::newFatal( 'upload-copy-upload-invalid-url' );
201  }
202  if ( !$this->mAsync ) {
203  return $this->reallyFetchFile( $httpOptions );
204  }
205  return Status::newGood();
206  }
207 
213  protected function makeTemporaryFile() {
214  $tmpFile = TempFSFile::factory( 'URL' );
215  $tmpFile->bind( $this );
216  return $tmpFile->getPath();
217  }
218 
226  public function saveTempFileChunk( $req, $buffer ) {
227  $nbytes = fwrite( $this->mTmpHandle, $buffer );
228 
229  if ( $nbytes == strlen( $buffer ) ) {
230  $this->mFileSize += $nbytes;
231  } else {
232  // Well... that's not good!
233  fclose( $this->mTmpHandle );
234  $this->mTmpHandle = false;
235  }
236 
237  return $nbytes;
238  }
239 
247  protected function reallyFetchFile( $httpOptions = array() ) {
248  global $wgCopyUploadProxy, $wgCopyUploadTimeout;
249  if ( $this->mTempPath === false ) {
250  return Status::newFatal( 'tmp-create-error' );
251  }
252 
253  // Note the temporary file should already be created by makeTemporaryFile()
254  $this->mTmpHandle = fopen( $this->mTempPath, 'wb' );
255  if ( !$this->mTmpHandle ) {
256  return Status::newFatal( 'tmp-create-error' );
257  }
258 
259  $this->mRemoveTempFile = true;
260  $this->mFileSize = 0;
261 
262  $options = $httpOptions + array(
263  'followRedirects' => true,
264  );
265  if ( $wgCopyUploadProxy !== false ) {
266  $options['proxy'] = $wgCopyUploadProxy;
267  }
268  if ( $wgCopyUploadTimeout && !isset( $options['timeout'] ) ) {
269  $options['timeout'] = $wgCopyUploadTimeout;
270  }
271  $req = MWHttpRequest::factory( $this->mUrl, $options );
272  $req->setCallback( array( $this, 'saveTempFileChunk' ) );
273  $status = $req->execute();
274 
275  if ( $this->mTmpHandle ) {
276  // File got written ok...
277  fclose( $this->mTmpHandle );
278  $this->mTmpHandle = null;
279  } else {
280  // We encountered a write error during the download...
281  return Status::newFatal( 'tmp-write-error' );
282  }
283 
284  if ( !$status->isOk() ) {
285  return $status;
286  }
287 
288  return $status;
289  }
290 
296  public function verifyUpload() {
297  if ( $this->mAsync ) {
298  return array( 'status' => UploadBase::OK );
299  }
300  return parent::verifyUpload();
301  }
302 
308  public function checkWarnings() {
309  if ( $this->mAsync ) {
310  $this->mIgnoreWarnings = false;
311  return array();
312  }
313  return parent::checkWarnings();
314  }
315 
322  public function verifyTitlePermissions( $user ) {
323  if ( $this->mAsync ) {
324  return true;
325  }
326  return parent::verifyTitlePermissions( $user );
327  }
328 
338  public function performUpload( $comment, $pageText, $watch, $user ) {
339  if ( $this->mAsync ) {
340  $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user );
341 
342  return Status::newFatal( 'async', $sessionKey );
343  }
344 
345  return parent::performUpload( $comment, $pageText, $watch, $user );
346  }
347 
355  protected function insertJob( $comment, $pageText, $watch, $user ) {
356  $sessionKey = $this->stashSession();
357  $job = new UploadFromUrlJob( $this->getTitle(), array(
358  'url' => $this->mUrl,
359  'comment' => $comment,
360  'pageText' => $pageText,
361  'watch' => $watch,
362  'userName' => $user->getName(),
363  'leaveMessage' => $this->mAsync == 'async-leavemessage',
364  'ignoreWarnings' => $this->mIgnoreWarnings,
365  'sessionId' => session_id(),
366  'sessionKey' => $sessionKey,
367  ) );
368  $job->initializeSessionData();
369  JobQueueGroup::singleton()->push( $job );
370  return $sessionKey;
371  }
372 
373 }
$wgUser
$wgUser
Definition: Setup.php:572
$request
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled also a ContextSource error or success you ll probably need to make sure the header is varied on WebRequest $request
Definition: hooks.txt:1961
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
UploadFromUrl\$mUrl
$mUrl
Definition: UploadFromUrl.php:32
UploadFromUrl\fetchFile
fetchFile( $httpOptions=array())
Download the file (if not async)
Definition: UploadFromUrl.php:191
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
UploadFromUrl\insertJob
insertJob( $comment, $pageText, $watch, $user)
Definition: UploadFromUrl.php:355
UploadFromUrl\$mTmpHandle
$mTmpHandle
Definition: UploadFromUrl.php:35
UploadFromUrl\verifyUpload
verifyUpload()
Wrapper around the parent function in order to defer verifying the upload until the file really has b...
Definition: UploadFromUrl.php:296
UploadFromUrl\saveTempFileChunk
saveTempFileChunk( $req, $buffer)
Callback: save a chunk of the result of a HTTP request to the temporary file.
Definition: UploadFromUrl.php:226
UploadFromUrl\initialize
initialize( $name, $url, $async=false)
Entry point for API upload.
Definition: UploadFromUrl.php:134
wfParseUrl
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
Definition: GlobalFunctions.php:802
MWException
MediaWiki exception.
Definition: MWException.php:26
UploadFromUrl
Implements uploading from a HTTP resource.
Definition: UploadFromUrl.php:31
UploadFromUrl\$mTempPath
$mTempPath
Definition: UploadFromUrl.php:35
UploadFromUrl\makeTemporaryFile
makeTemporaryFile()
Create a new temporary file in the URL subdirectory of wfTempDir().
Definition: UploadFromUrl.php:213
UploadFromUrl\checkWarnings
checkWarnings()
Wrapper around the parent function in order to defer checking warnings until the file really has been...
Definition: UploadFromUrl.php:308
UploadFromUrl\isEnabled
static isEnabled()
Checks if the upload from URL feature is enabled.
Definition: UploadFromUrl.php:59
UploadFromUrl\isValidRequest
static isValidRequest( $request)
Definition: UploadFromUrl.php:168
UploadFromUrl\getSourceType
getSourceType()
Definition: UploadFromUrl.php:180
UploadFromUrl\$allowedUrls
static $allowedUrls
Definition: UploadFromUrl.php:37
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4066
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
$comment
$comment
Definition: importImages.php:107
UploadFromUrl\reallyFetchFile
reallyFetchFile( $httpOptions=array())
Download the file, save it to the temporary file and update the file size and set $mRemoveTempFile to...
Definition: UploadFromUrl.php:247
$options
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1530
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
TempFSFile\factory
static factory( $prefix, $extension='')
Make a new temporary file on the file system.
Definition: TempFSFile.php:44
UploadFromUrl\isAllowedUrl
static isAllowedUrl( $url)
Checks whether the URL is not allowed.
Definition: UploadFromUrl.php:115
UploadFromUrlJob
Job for asynchronous upload-by-url.
Definition: UploadFromUrlJob.php:33
UploadFromUrl\performUpload
performUpload( $comment, $pageText, $watch, $user)
Wrapper around the parent function in order to defer uploading to the job queue for asynchronous uplo...
Definition: UploadFromUrl.php:338
UploadFromUrl\verifyTitlePermissions
verifyTitlePermissions( $user)
Wrapper around the parent function in order to defer checking protection until we are sure that the f...
Definition: UploadFromUrl.php:322
UploadFromUrl\isAllowedHost
static isAllowedHost( $url)
Checks whether the URL is for an allowed host The domains in the whitelist can include wildcard chara...
Definition: UploadFromUrl.php:72
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
$job
if(count( $args)< 1) $job
Definition: recompressTracked.php:42
JobQueueGroup\singleton
static singleton( $wiki=false)
Definition: JobQueueGroup.php:61
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Http\isValidURI
static isValidURI( $uri)
Checks that the given URI is a valid one.
Definition: HttpFunctions.php:178
UploadFromUrl\$mAsync
$mAsync
Definition: UploadFromUrl.php:32
UploadFromUrl\isAllowed
static isAllowed( $user)
Checks if the user is allowed to use the upload-by-URL feature.
Definition: UploadFromUrl.php:48
UploadFromUrl\$mIgnoreWarnings
$mIgnoreWarnings
Definition: UploadFromUrl.php:33
UploadFromUrl\initializeFromRequest
initializeFromRequest(&$request)
Entry point for SpecialUpload.
Definition: UploadFromUrl.php:152
MWHttpRequest\factory
static factory( $url, $options=null)
Generate a new request object.
Definition: HttpFunctions.php:290
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63