48 if ( !$performer->
isAllowed(
'upload_by_url' ) ) {
49 return 'upload_by_url';
52 return parent::isAllowed( $performer );
62 return $allowCopyUploads && parent::isEnabled();
76 if ( !count( $domains ) ) {
79 $parsedUrl = $urlUtils->parse(
$url );
84 foreach ( $domains as $domain ) {
86 $domainPieces = explode(
'.', $domain );
87 $uploadDomainPieces = explode(
'.', $parsedUrl[
'host'] );
88 if ( count( $domainPieces ) === count( $uploadDomainPieces ) ) {
91 foreach ( $domainPieces as $index => $piece ) {
92 if ( $piece !==
'*' && $piece !== $uploadDomainPieces[$index] ) {
116 if ( !isset( $params[
'filename'] ) || !isset( $params[
'url'] ) ) {
122 return sha1( sprintf(
"%s|||%s", $params[
'filename'], $params[
'url'] ) );
133 $uploadCacheKey =
$request->getText(
'wpCacheKey',
$request->getText(
'key',
'' ) );
134 if ( $uploadCacheKey !==
'' ) {
135 return $uploadCacheKey;
137 $desiredDestName =
$request->getText(
'wpDestFile' );
138 if ( !$desiredDestName ) {
139 $desiredDestName =
$request->getText(
'wpUploadFileURL' );
143 'filename' => $desiredDestName,
144 'url' => trim(
$request->getVal(
'wpUploadFileURL' ) )
158 $page =
wfMessage(
'copyupload-allowed-domains' )->inContentLanguage()->plain();
160 foreach ( explode(
"\n", $page ) as $line ) {
162 $line = preg_replace(
"/^\\s*([^#]*)\\s*((.*)?)$/",
"\\1", $line );
164 $line = trim( $line );
166 if ( $line !==
'' ) {
182 if ( !isset( self::$allowedUrls[
$url] ) ) {
185 ->onIsUploadAllowedFromUrl(
$url, $allowed );
186 self::$allowedUrls[
$url] = $allowed;
189 return self::$allowedUrls[
$url];
210 $tempPath = $initTempFile ? $this->makeTemporaryFile() :
null;
211 $fileSize = $initTempFile ? 0 :
null;
213 $this->initializePathInfo( $name, $tempPath, $fileSize,
false );
221 $desiredDestName =
$request->getText(
'wpDestFile' );
222 if ( !$desiredDestName ) {
223 $desiredDestName =
$request->getText(
'wpUploadFileURL' );
227 trim(
$request->getVal(
'wpUploadFileURL' ) )
236 $user = RequestContext::getMain()->getUser();
242 ->getPermissionManager()
243 ->userHasRight( $user,
'upload_by_url' );
261 $status = $this->canFetchFile();
262 if ( !$status->isGood() ) {
265 return $this->reallyFetchFile( $httpOptions );
274 return $this->canFetchFileFromUrl( $this->mUrl );
277 private function canFetchFileFromUrl(
string $url ):
Status {
279 return Status::newFatal(
'http-invalid-url',
$url );
281 if ( !self::isAllowedHost(
$url ) ) {
282 return Status::newFatal(
'upload-copy-upload-invalid-domain' );
284 if ( !self::isAllowedUrl(
$url ) ) {
285 return Status::newFatal(
'upload-copy-upload-invalid-url' );
287 return Status::newGood();
296 $tmpFile = MediaWikiServices::getInstance()->getTempFSFileFactory()
297 ->newTempFSFile(
'URL',
'urlupload_' );
298 $tmpFile->bind( $this );
300 return $tmpFile->getPath();
311 wfDebugLog(
'fileupload',
'Received chunk of ' . strlen( $buffer ) .
' bytes' );
312 $nbytes = fwrite( $this->mTmpHandle, $buffer );
314 if ( $nbytes == strlen( $buffer ) ) {
315 $this->mFileSize += $nbytes;
320 'Short write ' . $nbytes .
'/' . strlen( $buffer ) .
321 ' bytes, aborting with ' . $this->mFileSize .
' uploaded so far'
323 fclose( $this->mTmpHandle );
324 $this->mTmpHandle =
false;
337 $copyUploadProxy = MediaWikiServices::getInstance()->getMainConfig()
338 ->get( MainConfigNames::CopyUploadProxy );
339 $copyUploadTimeout = MediaWikiServices::getInstance()->getMainConfig()
340 ->get( MainConfigNames::CopyUploadTimeout );
343 $this->mTmpHandle = fopen( $this->mTempPath,
'wb' );
344 if ( !$this->mTmpHandle ) {
345 return Status::newFatal(
'tmp-create-error' );
347 wfDebugLog(
'fileupload',
'Temporary file created "' . $this->mTempPath .
'"' );
349 $this->mRemoveTempFile =
true;
350 $this->mFileSize = 0;
352 $options = $httpOptions + [
'followRedirects' => false ];
354 if ( $copyUploadProxy !==
false ) {
355 $options[
'proxy'] = $copyUploadProxy;
358 if ( $copyUploadTimeout && !isset( $options[
'timeout'] ) ) {
359 $options[
'timeout'] = $copyUploadTimeout;
363 'Starting download from "' . $this->mUrl .
'" ' .
364 '<' . implode(
',', array_keys( array_filter( $options ) ) ) .
'>'
370 $attemptsLeft = $options[
'maxRedirects'] ?? 5;
371 $targetUrl = $this->mUrl;
372 $requestFactory = MediaWikiServices::getInstance()->getHttpRequestFactory();
374 $req = $requestFactory->create( $targetUrl, $options, __METHOD__ );
375 $req->setCallback( $this->saveTempFileChunk( ... ) );
376 $status = $req->execute();
377 if ( !$req->isRedirect() ) {
380 $targetUrl = $req->getFinalUrl();
381 $redirectedUrlFetchable = $this->canFetchFileFromUrl( $targetUrl );
382 if ( !$redirectedUrlFetchable->isGood() ) {
383 return $redirectedUrlFetchable;
386 ftruncate( $this->mTmpHandle, 0 );
387 rewind( $this->mTmpHandle );
389 }
while ( $attemptsLeft > 0 );
391 if ( $attemptsLeft == 0 ) {
392 return Status::newFatal(
'upload-too-many-redirects' );
395 if ( $this->mTmpHandle ) {
397 fclose( $this->mTmpHandle );
398 $this->mTmpHandle =
null;
401 return Status::newFatal(
'tmp-write-error' );
404 if ( $status->isOK() ) {
405 wfDebugLog(
'fileupload',
'Download by URL completed successfully.' );
407 wfDebugLog(
'fileupload', $status->getWikiText(
false,
false,
'en' ) );
410 'Download by URL completed with HTTP status ' . $req->getStatus()
419class_alias( UploadFromUrl::class,
'UploadFromUrl' );
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
if(!defined('MW_SETUP_CALLBACK'))
Group all the pieces relevant to the context of a request into one instance.
A class containing constants representing the names of configuration variables.
const AllowCopyUploads
Name constant for the AllowCopyUploads setting, for use with Config::get()
const CopyUploadAllowOnWikiDomainConfig
Name constant for the CopyUploadAllowOnWikiDomainConfig setting, for use with Config::get()
const CopyUploadsDomains
Name constant for the CopyUploadsDomains setting, for use with Config::get()