Partial fix to bug 23167
[lhc/web/wiklou.git] / includes / upload / UploadFromUrl.php
1 <?php
2 /**
3 * @file
4 * @ingroup upload
5 *
6 * Implements uploading from a HTTP resource.
7 *
8 * @author Bryan Tong Minh
9 * @author Michael Dale
10 */
11 class UploadFromUrl extends UploadBase {
12 protected $mTempDownloadPath;
13
14 /**
15 * Checks if the user is allowed to use the upload-by-URL feature. If the
16 * user is allowed, pass on permissions checking to the parent.
17 */
18 public static function isAllowed( $user ) {
19 if( !$user->isAllowed( 'upload_by_url' ) )
20 return 'upload_by_url';
21 return parent::isAllowed( $user );
22 }
23
24 /**
25 * Checks if the upload from URL feature is enabled
26 */
27 public static function isEnabled() {
28 global $wgAllowCopyUploads;
29 return $wgAllowCopyUploads && parent::isEnabled();
30 }
31
32 /**
33 * Entry point for API upload
34 */
35 public function initialize( $name, $url, $na, $nb = false ) {
36 global $wgTmpDirectory;
37
38 $localFile = tempnam( $wgTmpDirectory, 'WEBUPLOAD' );
39 $this->initializePathInfo( $name, $localFile, 0, true );
40
41 $this->mUrl = trim( $url );
42 }
43
44 /**
45 * Entry point for SpecialUpload
46 * @param $request Object: WebRequest object
47 */
48 public function initializeFromRequest( &$request ) {
49 $desiredDestName = $request->getText( 'wpDestFile' );
50 if( !$desiredDestName )
51 $desiredDestName = $request->getText( 'wpUploadFileURL' );
52 return $this->initialize(
53 $desiredDestName,
54 $request->getVal( 'wpUploadFileURL' ),
55 false
56 );
57 }
58
59 /**
60 * @param $request Object: WebRequest object
61 */
62 public static function isValidRequest( $request ){
63 if( !$request->getVal( 'wpUploadFileURL' ) )
64 return false;
65 // check that is a valid url:
66 return self::isValidUrl( $request->getVal( 'wpUploadFileURL' ) );
67 }
68
69 public static function isValidUrl( $url ) {
70 // Only allow HTTP or FTP for now
71 return (bool)preg_match( '!^(http://|ftp://)!', $url );
72 }
73
74 /**
75 * Do the real fetching stuff
76 */
77 function fetchFile() {
78 if( !self::isValidUrl( $this->mUrl ) ) {
79 return Status::newFatal( 'upload-proto-error' );
80 }
81 $res = $this->curlCopy();
82 if( $res !== true ) {
83 return Status::newFatal( $res );
84 }
85 return Status::newGood();
86 }
87
88 /**
89 * Safe copy from URL
90 * Returns true if there was an error, false otherwise
91 */
92 private function curlCopy() {
93 global $wgOut;
94
95 # Open temporary file
96 $this->mCurlDestHandle = @fopen( $this->mTempPath, "wb" );
97 if( $this->mCurlDestHandle === false ) {
98 # Could not open temporary file to write in
99 return 'upload-file-error';
100 }
101
102 $ch = curl_init();
103 curl_setopt( $ch, CURLOPT_HTTP_VERSION, 1.0); # Probably not needed, but apparently can work around some bug
104 curl_setopt( $ch, CURLOPT_TIMEOUT, 10); # 10 seconds timeout
105 curl_setopt( $ch, CURLOPT_LOW_SPEED_LIMIT, 512); # 0.5KB per second minimum transfer speed
106 curl_setopt( $ch, CURLOPT_URL, $this->mUrl);
107 curl_setopt( $ch, CURLOPT_WRITEFUNCTION, array( $this, 'uploadCurlCallback' ) );
108 curl_exec( $ch );
109 $error = curl_errno( $ch );
110 curl_close( $ch );
111
112 fclose( $this->mCurlDestHandle );
113 unset( $this->mCurlDestHandle );
114
115 if( $error )
116 return "upload-curl-error$errornum";
117
118 return true;
119 }
120
121 /**
122 * Callback function for CURL-based web transfer
123 * Write data to file unless we've passed the length limit;
124 * if so, abort immediately.
125 * @access private
126 */
127 function uploadCurlCallback( $ch, $data ) {
128 global $wgMaxUploadSize;
129 $length = strlen( $data );
130 $this->mFileSize += $length;
131 if( $this->mFileSize > $wgMaxUploadSize ) {
132 return 0;
133 }
134 fwrite( $this->mCurlDestHandle, $data );
135 return $length;
136 }
137 }