MediaWiki REL1_34
UploadFromUrl.php
Go to the documentation of this file.
1<?php
26
35 protected $mUrl;
36
38
39 protected static $allowedUrls = [];
40
50 public static function isAllowed( UserIdentity $user ) {
51 if ( !MediaWikiServices::getInstance()
53 ->userHasRight( $user, 'upload_by_url' )
54 ) {
55 return 'upload_by_url';
56 }
57
58 return parent::isAllowed( $user );
59 }
60
65 public static function isEnabled() {
67
68 return $wgAllowCopyUploads && parent::isEnabled();
69 }
70
79 public static function isAllowedHost( $url ) {
81 if ( !count( $wgCopyUploadsDomains ) ) {
82 return true;
83 }
84 $parsedUrl = wfParseUrl( $url );
85 if ( !$parsedUrl ) {
86 return false;
87 }
88 $valid = false;
89 foreach ( $wgCopyUploadsDomains as $domain ) {
90 // See if the domain for the upload matches this whitelisted domain
91 $whitelistedDomainPieces = explode( '.', $domain );
92 $uploadDomainPieces = explode( '.', $parsedUrl['host'] );
93 if ( count( $whitelistedDomainPieces ) === count( $uploadDomainPieces ) ) {
94 $valid = true;
95 // See if all the pieces match or not (excluding wildcards)
96 foreach ( $whitelistedDomainPieces as $index => $piece ) {
97 if ( $piece !== '*' && $piece !== $uploadDomainPieces[$index] ) {
98 $valid = false;
99 }
100 }
101 if ( $valid ) {
102 // We found a match, so quit comparing against the list
103 break;
104 }
105 }
106 /* Non-wildcard test
107 if ( $parsedUrl['host'] === $domain ) {
108 $valid = true;
109 break;
110 }
111 */
112 }
113
114 return $valid;
115 }
116
123 public static function isAllowedUrl( $url ) {
124 if ( !isset( self::$allowedUrls[$url] ) ) {
125 $allowed = true;
126 Hooks::run( 'IsUploadAllowedFromUrl', [ $url, &$allowed ] );
127 self::$allowedUrls[$url] = $allowed;
128 }
129
130 return self::$allowedUrls[$url];
131 }
132
140 public function initialize( $name, $url ) {
141 $this->mUrl = $url;
142
143 $tempPath = $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 );
161 }
162
167 public static function isValidRequest( $request ) {
168 global $wgUser;
169
170 $url = $request->getVal( 'wpUploadFileURL' );
171
172 return !empty( $url )
173 && MediaWikiServices::getInstance()
174 ->getPermissionManager()
175 ->userHasRight( $wgUser, 'upload_by_url' );
176 }
177
181 public function getSourceType() {
182 return 'url';
183 }
184
192 public function fetchFile( $httpOptions = [] ) {
193 if ( !Http::isValidURI( $this->mUrl ) ) {
194 return Status::newFatal( 'http-invalid-url', $this->mUrl );
195 }
196
197 if ( !self::isAllowedHost( $this->mUrl ) ) {
198 return Status::newFatal( 'upload-copy-upload-invalid-domain' );
199 }
200 if ( !self::isAllowedUrl( $this->mUrl ) ) {
201 return Status::newFatal( 'upload-copy-upload-invalid-url' );
202 }
203 return $this->reallyFetchFile( $httpOptions );
204 }
205
211 protected function makeTemporaryFile() {
212 $tmpFile = MediaWikiServices::getInstance()->getTempFSFileFactory()
213 ->newTempFSFile( 'URL', 'urlupload_' );
214 $tmpFile->bind( $this );
215
216 return $tmpFile->getPath();
217 }
218
226 public function saveTempFileChunk( $req, $buffer ) {
227 wfDebugLog( 'fileupload', 'Received chunk of ' . strlen( $buffer ) . ' bytes' );
228 $nbytes = fwrite( $this->mTmpHandle, $buffer );
229
230 if ( $nbytes == strlen( $buffer ) ) {
231 $this->mFileSize += $nbytes;
232 } else {
233 // Well... that's not good!
235 'fileupload',
236 'Short write ' . $nbytes . '/' . strlen( $buffer ) .
237 ' bytes, aborting with ' . $this->mFileSize . ' uploaded so far'
238 );
239 fclose( $this->mTmpHandle );
240 $this->mTmpHandle = false;
241 }
242
243 return $nbytes;
244 }
245
253 protected function reallyFetchFile( $httpOptions = [] ) {
255 if ( $this->mTempPath === false ) {
256 return Status::newFatal( 'tmp-create-error' );
257 }
258
259 // Note the temporary file should already be created by makeTemporaryFile()
260 $this->mTmpHandle = fopen( $this->mTempPath, 'wb' );
261 if ( !$this->mTmpHandle ) {
262 return Status::newFatal( 'tmp-create-error' );
263 }
264 wfDebugLog( 'fileupload', 'Temporary file created "' . $this->mTempPath . '"' );
265
266 $this->mRemoveTempFile = true;
267 $this->mFileSize = 0;
268
269 $options = $httpOptions + [ 'followRedirects' => true ];
270
271 if ( $wgCopyUploadProxy !== false ) {
272 $options['proxy'] = $wgCopyUploadProxy;
273 }
274
275 if ( $wgCopyUploadTimeout && !isset( $options['timeout'] ) ) {
276 $options['timeout'] = $wgCopyUploadTimeout;
277 }
279 'fileupload',
280 'Starting download from "' . $this->mUrl . '" ' .
281 '<' . implode( ',', array_keys( array_filter( $options ) ) ) . '>'
282 );
283 $req = MWHttpRequest::factory( $this->mUrl, $options, __METHOD__ );
284 $req->setCallback( [ $this, 'saveTempFileChunk' ] );
285 $status = $req->execute();
286
287 if ( $this->mTmpHandle ) {
288 // File got written ok...
289 fclose( $this->mTmpHandle );
290 $this->mTmpHandle = null;
291 } else {
292 // We encountered a write error during the download...
293 return Status::newFatal( 'tmp-write-error' );
294 }
295
296 wfDebugLog( 'fileupload', $status );
297 if ( $status->isOK() ) {
298 wfDebugLog( 'fileupload', 'Download by URL completed successfully.' );
299 } else {
301 'fileupload',
302 'Download by URL completed with HTTP status ' . $req->getStatus()
303 );
304 }
305
306 return $status;
307 }
308}
getPermissionManager()
int bool $wgCopyUploadTimeout
Different timeout for upload by url This could be useful since when fetching large files,...
$wgCopyUploadsDomains
A list of domains copy uploads can come from.
$wgCopyUploadProxy
Proxy to use for copy upload requests.
$wgAllowCopyUploads
Allow for upload to be copied from an URL.
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
MediaWikiServices is the service locator for the application scope of MediaWiki.
UploadBase and subclasses are the backend of MediaWiki's file uploads.
initializePathInfo( $name, $tempPath, $fileSize, $removeTempFile=false)
Initialize the path information.
Implements uploading from a HTTP resource.
makeTemporaryFile()
Create a new temporary file in the URL subdirectory of wfTempDir().
static isValidRequest( $request)
static isAllowed(UserIdentity $user)
Checks if the user is allowed to use the upload-by-URL feature.
initializeFromRequest(&$request)
Entry point for SpecialUpload.
reallyFetchFile( $httpOptions=[])
Download the file, save it to the temporary file and update the file size and set $mRemoveTempFile to...
initialize( $name, $url)
Entry point for API upload.
fetchFile( $httpOptions=[])
Download the file.
saveTempFileChunk( $req, $buffer)
Callback: save a chunk of the result of a HTTP request to the temporary file.
static isAllowedHost( $url)
Checks whether the URL is for an allowed host The domains in the whitelist can include wildcard chara...
static isAllowedUrl( $url)
Checks whether the URL is not allowed.
static isEnabled()
Checks if the upload from URL feature is enabled.
Interface for objects representing user identity.
return true
Definition router.php:94