Index: branches/new-upload/phase3/maintenance/http_session_download.php |
— | — | @@ -0,0 +1,30 @@ |
| 2 | +<?php |
| 3 | +/* |
| 4 | + * simple entry point to initiate a background download |
| 5 | + * |
| 6 | + * arguments: |
| 7 | + * |
| 8 | + * -sid {$session_id} -usk {$upload_session_key} |
| 9 | + */ |
| 10 | + |
| 11 | +global $optionsWithArgs; |
| 12 | +$optionsWithArgs = Array('sid', 'usk'); |
| 13 | + |
| 14 | +require_once( 'commandLine.inc' ); |
| 15 | + |
| 16 | +if(!isset($options['sid']) || !isset($options['usk'])){ |
| 17 | + print<<<EOT |
| 18 | + simple entry point to initiate a background download |
| 19 | + |
| 20 | + Usage: http_session_download.php [options] |
| 21 | + Options: |
| 22 | + --sid the session id (required) |
| 23 | + --usk the upload session key (also required) |
| 24 | +EOT; |
| 25 | + |
| 26 | + exit(); |
| 27 | +} |
| 28 | +//run the download: |
| 29 | +Http::doSessionIdDownload( $options['sid'], $options['usk'] ); |
| 30 | + |
| 31 | +?> |
\ No newline at end of file |
Index: branches/new-upload/phase3/skins/common/upload.js |
— | — | @@ -153,7 +153,7 @@ |
154 | 154 | if( fname.lastIndexOf('.')!=-1 ){ |
155 | 155 | var ext = fname.substr( fname.lastIndexOf('.')+1 ); |
156 | 156 | for(var i=0; i < wgFileExtensions.length; i++){ |
157 | | - if( strtolower( wgFileExtensions[i] ) == strtolower( ext ) ) |
| 157 | + if( wgFileExtensions[i].toLowerCase() == ext.toLowerCase() ) |
158 | 158 | found = true; |
159 | 159 | } |
160 | 160 | } |
Index: branches/new-upload/phase3/includes/GlobalFunctions.php |
— | — | @@ -2170,9 +2170,9 @@ |
2171 | 2171 | # This is a hack to work around PHP's flawed invocation of cmd.exe |
2172 | 2172 | # http://news.php.net/php.internals/21796 |
2173 | 2173 | $cmd = '"' . $cmd . '"'; |
2174 | | - } |
| 2174 | + } |
| 2175 | + |
2175 | 2176 | wfDebug( "wfShellExec: $cmd\n" ); |
2176 | | - |
2177 | 2177 | $retval = 1; // error by default? |
2178 | 2178 | ob_start(); |
2179 | 2179 | passthru( $cmd, $retval ); |
— | — | @@ -2185,6 +2185,15 @@ |
2186 | 2186 | return $output; |
2187 | 2187 | } |
2188 | 2188 | /** |
| 2189 | + * Executes a shell command in the background. Passes back the PID of the operation |
| 2190 | + * |
| 2191 | + * @param string $cmd |
| 2192 | + */ |
| 2193 | +function wfShellBackgroundExec(&$cmd){ |
| 2194 | + $pid = shell_exec( "nohup $cmd > /dev/null & echo $!" ); |
| 2195 | + return $pid; |
| 2196 | +} |
| 2197 | +/** |
2189 | 2198 | * Checks if the current instance can execute a shell command |
2190 | 2199 | * |
2191 | 2200 | */ |
Index: branches/new-upload/phase3/includes/UploadFromUrl.php |
— | — | @@ -16,16 +16,22 @@ |
17 | 17 | } |
18 | 18 | /*entry point for Api upload:: ASYNC_DOWNLOAD (if possible) */ |
19 | 19 | function initialize( $name, $url ) { |
20 | | - global $wgTmpDirectory; |
| 20 | + global $wgTmpDirectory, $wgPhpCliPath; |
21 | 21 | |
22 | | - if(!$this->dl_mode) |
| 22 | + if(!$this->dl_mode && $wgPhpCliPath && wfShellExecEnabled() ){ |
23 | 23 | $this->dl_mode = Http::ASYNC_DOWNLOAD; |
| 24 | + }else{ |
| 25 | + $this->dl_mode = Http::SYNC_DOWNLOAD; |
| 26 | + } |
24 | 27 | |
25 | 28 | $local_file = tempnam( $wgTmpDirectory, 'WEBUPLOAD' ); |
26 | 29 | parent::initialize( $name, $local_file, 0, true ); |
27 | 30 | |
28 | 31 | $this->mUrl = trim( $url ); |
29 | 32 | } |
| 33 | + public function isAsync(){ |
| 34 | + return $this->dl_mode == Http::ASYNC_DOWNLOAD; |
| 35 | + } |
30 | 36 | /*entry point for SpecialUpload no ASYNC_DOWNLOAD possible: */ |
31 | 37 | function initializeFromRequest( &$request ) { |
32 | 38 | //set dl mode if not set: |
— | — | @@ -43,18 +49,20 @@ |
44 | 50 | /** |
45 | 51 | * Do the real fetching stuff |
46 | 52 | */ |
47 | | - function fetchFile( ) { |
| 53 | + function fetchFile( ) { |
48 | 54 | //entry point for SpecialUplaod |
49 | 55 | if( stripos($this->mUrl, 'http://') !== 0 && stripos($this->mUrl, 'ftp://') !== 0 ) { |
50 | 56 | return Status::newFatal('upload-proto-error'); |
51 | 57 | } |
52 | 58 | //print "fetchFile:: $this->dl_mode"; |
53 | | - //now do the actual download to the shared target: |
54 | | - $status = Http::doDownload ( $this->mUrl, $this->mTempPath, $this->dl_mode); |
| 59 | + |
| 60 | + //now do the actual download to the target file: |
| 61 | + $status = Http::doDownload ( $this->mUrl, $this->mTempPath, $this->dl_mode ); |
| 62 | + |
55 | 63 | //update the local filesize var: |
56 | | - $this->mFileSize = filesize( $this->mTempPath ); |
57 | | - return $status; |
58 | | - |
| 64 | + $this->mFileSize = filesize( $this->mTempPath ); |
| 65 | + |
| 66 | + return $status; |
59 | 67 | } |
60 | 68 | |
61 | 69 | static function isValidRequest( $request ){ |
Index: branches/new-upload/phase3/includes/api/ApiUpload.php |
— | — | @@ -38,7 +38,7 @@ |
39 | 39 | } |
40 | 40 | |
41 | 41 | public function execute() { |
42 | | - global $wgUser; |
| 42 | + global $wgUser; |
43 | 43 | |
44 | 44 | $this->getMain()->requestWriteMode(); |
45 | 45 | $this->mParams = $this->extractRequestParams(); |
— | — | @@ -46,7 +46,7 @@ |
47 | 47 | |
48 | 48 | // Add the uploaded file to the params array |
49 | 49 | $this->mParams['file'] = $request->getFileName( 'file' ); |
50 | | - |
| 50 | + |
51 | 51 | // Check whether upload is enabled |
52 | 52 | if( !UploadBase::isEnabled() ) |
53 | 53 | $this->dieUsageMsg( array( 'uploaddisabled' ) ); |
— | — | @@ -81,9 +81,33 @@ |
82 | 82 | $request->getFileTempName( 'file' ), |
83 | 83 | $request->getFileSize( 'file' ) |
84 | 84 | ); |
85 | | - } elseif( isset( $this->mParams['url'] ) ) { |
| 85 | + } elseif( isset( $this->mParams['url'] ) ) { |
| 86 | + |
86 | 87 | $this->mUpload = new UploadFromUrl(); |
87 | | - $this->mUpload->initialize( $this->mParams['filename'], $this->mParams['url']); |
| 88 | + $this->mUpload->initialize( $this->mParams['filename'], $this->mParams['url']); |
| 89 | + |
| 90 | + $status = $this->mUpload->fetchFile(); |
| 91 | + if( !$status->isOK() ){ |
| 92 | + $this->dieUsage( 'fetchfilerror', $status->getWikiText()); |
| 93 | + } |
| 94 | + //check if we doing a async request (return session info) |
| 95 | + if( $this->mUpload->isAsync() ){ |
| 96 | + $upload_session_key = $status->value; |
| 97 | + //update the session with anything with the params we will need to finish up the upload later on: |
| 98 | + if(!isset($_SESSION['wsDownload'][$upload_session_key])) |
| 99 | + $_SESSION['wsDownload'][$upload_session_key] = array(); |
| 100 | + |
| 101 | + $sd =& $_SESSION['wsDownload'][$upload_session_key]; |
| 102 | + |
| 103 | + //do a wholesale copy of mParams |
| 104 | + $sd['mParams'] = $this->mParams; |
| 105 | + |
| 106 | + |
| 107 | + return $this->getResult()->addValue( null, $this->getModuleName(), |
| 108 | + array( 'upload_session_key' => $upload_session_key |
| 109 | + )); |
| 110 | + } |
| 111 | + //else the file downloaded in place continue with validation: |
88 | 112 | } |
89 | 113 | } |
90 | 114 | |
— | — | @@ -216,12 +240,12 @@ |
217 | 241 | 'file' => null, |
218 | 242 | 'chunk' => null, |
219 | 243 | 'url' => null, |
| 244 | + 'enablechunks' => null, |
220 | 245 | 'comment' => array( |
221 | 246 | ApiBase :: PARAM_DFLT => '' |
222 | 247 | ), |
223 | 248 | 'watch' => false, |
224 | | - 'ignorewarnings' => false, |
225 | | - 'enablechunks' => false, |
| 249 | + 'ignorewarnings' => false, |
226 | 250 | 'done' => false, |
227 | 251 | 'sessionkey' => null, |
228 | 252 | 'chunksessionkey'=> null, |
— | — | @@ -234,10 +258,10 @@ |
235 | 259 | 'file' => 'File contents', |
236 | 260 | 'chunk'=> 'Chunk File Contents', |
237 | 261 | 'url' => 'Url to upload from', |
| 262 | + 'enablechunks' => 'Boolean If we are in chunk mode; accepts many small file POSTs', |
238 | 263 | 'comment' => 'Upload comment or initial page text', |
239 | 264 | 'watch' => 'Watch the page', |
240 | | - 'ignorewarnings' => 'Ignore any warnings', |
241 | | - 'enablechunks' => 'Boolean If we are in chunk mode; accepts many small file POSTs', |
| 265 | + 'ignorewarnings' => 'Ignore any warnings', |
242 | 266 | 'done' => 'When used with "chunks", Is sent to notify the api The last chunk is being uploaded.', |
243 | 267 | 'sessionkey' => 'Session key in case there were any warnings.', |
244 | 268 | 'chunksessionkey'=> 'Used to sync uploading of chunks', |
Index: branches/new-upload/phase3/includes/DefaultSettings.php |
— | — | @@ -3354,11 +3354,17 @@ |
3355 | 3355 | $wgAllowSpecialInclusion = true; |
3356 | 3356 | |
3357 | 3357 | /** |
3358 | | - * Timeout for HTTP requests done via CURL |
| 3358 | + * Timeout for HTTP requests done via CURL |
| 3359 | + * |
3359 | 3360 | */ |
3360 | | -$wgHTTPTimeout = 3; |
| 3361 | +$wgHTTPTimeout = 3; |
3361 | 3362 | |
3362 | 3363 | /** |
| 3364 | + * Timeout for large http file copy over http (default 2 hours) |
| 3365 | + */ |
| 3366 | +$wgHTTPFileTimeout = 60*60*2; |
| 3367 | + |
| 3368 | +/** |
3363 | 3369 | * Proxy to use for CURL requests. |
3364 | 3370 | */ |
3365 | 3371 | $wgHTTPProxy = false; |
Index: branches/new-upload/phase3/includes/HttpFunctions.php |
— | — | @@ -10,87 +10,169 @@ |
11 | 11 | const ASYNC_DOWNLOAD = 2; //asynchronous upload we should spawn out another process and monitor progress if possible) |
12 | 12 | |
13 | 13 | var $body = ''; |
| 14 | + |
14 | 15 | /** |
15 | 16 | * Simple wrapper for Http::request( 'GET' ) |
16 | 17 | */ |
17 | 18 | public static function get( $url, $opts = array() ) { |
18 | 19 | $opt['method'] = 'GET'; |
19 | | - $req = newReq($url, $opts ); |
20 | | - return $req->request(); |
| 20 | + $req = new HttpRequest($url, $opts ); |
| 21 | + return $req->doRequest(); |
21 | 22 | } |
22 | 23 | /** |
23 | 24 | * Simple wrapper for Http::request( 'POST' ) |
24 | 25 | */ |
25 | 26 | public static function post( $url, $opts = array() ) { |
26 | 27 | $opts['method']='POST'; |
27 | | - $req = newReq($url, $opts ); |
28 | | - return $req->request(); |
| 28 | + $req = new HttpRequest( $url, $opts ); |
| 29 | + return $req->doRequest(); |
29 | 30 | } |
30 | 31 | public static function doDownload( $url, $target_file_path , $dl_mode = self::SYNC_DOWNLOAD ){ |
31 | | - global $wgPhpCliPath, $wgMaxUploadSize; |
| 32 | + global $wgPhpCliPath, $wgMaxUploadSize; |
32 | 33 | //do a quick check to HEAD to insure the file size is not > $wgMaxUploadSize to large no need to download it |
33 | 34 | $head = get_headers($url, 1); |
34 | 35 | if(isset($head['Content-Length']) && $head['Content-Length'] > $wgMaxUploadSize){ |
35 | | - return Status::newFatal('requested file length ' .$head['Content-Length'] . ' is greater than $wgMaxUploadSize: ' . $wgMaxUploadSize); |
| 36 | + return Status::newFatal('requested file length ' . $head['Content-Length'] . ' is greater than $wgMaxUploadSize: ' . $wgMaxUploadSize); |
36 | 37 | } |
37 | 38 | |
38 | 39 | //check if we can find phpCliPath (for doing a background shell request to php to do the download: |
39 | 40 | if( $wgPhpCliPath && wfShellExecEnabled() && $dl_mode == self::ASYNC_DOWNLOAD){ |
40 | 41 | //setup session and shell call: |
41 | | - return self::initBackgroundDownload( $url, $target_file_path ); |
42 | | - |
43 | | - //return success status and download_session_key |
44 | | - |
45 | | - //(separate ajax request can now check on the status of the shell exec)... and even kill or cancel it) |
46 | | - |
| 42 | + return self::initBackgroundDownload( $url, $target_file_path ); |
47 | 43 | }else if( $dl_mode== self::SYNC_DOWNLOAD ){ |
48 | 44 | //else just download as much as we can in the time we have left: |
49 | 45 | $opts['method']='GET'; |
50 | 46 | $opts['target_file_path'] = $target_file_path; |
51 | | - $req = self::newReq($url, $opts ); |
52 | | - return $req->request(); |
| 47 | + $req = new HttpRequest($url, $opts ); |
| 48 | + return $req->doRequest(); |
53 | 49 | } |
54 | 50 | } |
55 | | - //setup a new request |
56 | | - public static function newReq($url, $opt){ |
57 | | - $req = new Http(); |
58 | | - $req->url = $url; |
59 | | - $req->method = (isset($opt['method']))?$opt['method']:'GET'; |
60 | | - $req->target_file_path = (isset($opt['target_file_path']))?$opt['target_file_path']:false; |
61 | | - return $req; |
62 | | - } |
63 | 51 | /** |
64 | 52 | * a non blocking request (generally an exit point in the application) |
65 | 53 | * should write to a file location and give updates |
66 | 54 | * |
67 | 55 | */ |
68 | 56 | private function initBackgroundDownload( $url, $target_file_path ){ |
69 | | - global $wgMaxUploadSize; |
| 57 | + global $wgMaxUploadSize, $IP, $wgPhpCliPath; |
70 | 58 | $status = Status::newGood(); |
| 59 | + |
71 | 60 | //generate a session id with all the details for the download (pid, target_file_path ) |
72 | | - |
73 | | - //later add in (destName & description) |
| 61 | + $upload_session_key = self::getUploadSessionKey(); |
74 | 62 | $session_id = session_id(); |
75 | | - print "should spin out a process with id: $session_id\n"; |
76 | | - //maintenance/http_download.php passing it a upload session_id |
| 63 | + |
| 64 | + //store the url and target path: |
| 65 | + $_SESSION[ 'wsDownload' ][$upload_session_key]['url'] = $url; |
| 66 | + $_SESSION[ 'wsDownload' ][$upload_session_key]['target_file_path'] = $target_file_path; |
| 67 | + |
| 68 | + //run the background download request: |
| 69 | + $cmd = $wgPhpCliPath . ' ' . $IP . "/maintenance/http_session_download.php --sid {$session_id} --usk {$upload_session_key}"; |
| 70 | + $pid = wfShellBackgroundExec($cmd , $retval); |
| 71 | + wfDebug('GOT PID: '. $pid . " running cmd: $cmd\n" ); |
| 72 | + //the pid is not of much use since we won't be visiting this same apache any-time soon. |
| 73 | + if(!$pid) |
| 74 | + return Status::newFatal('could not run background shell exec'); |
77 | 75 | |
78 | | - //return status |
| 76 | + //update the status value with the $upload_session_key (for the user to check on the status of the upload) |
| 77 | + $status->value = $upload_session_key; |
| 78 | + |
| 79 | + //return good status with |
79 | 80 | return $status; |
80 | | - } |
| 81 | + } |
| 82 | + function getUploadSessionKey(){ |
| 83 | + $key = mt_rand( 0, 0x7fffffff ); |
| 84 | + $_SESSION['wsUploadData'][$key] = array(); |
| 85 | + return $key; |
| 86 | + } |
81 | 87 | /** |
82 | 88 | * used to run a session based download. Is initiated via the shell. |
83 | 89 | * |
84 | | - * @param string $dn_session_id // the session id to grab download details from |
| 90 | + * @param string $session_id // the session id to grab download details from |
| 91 | + * @param string $upload_session_key //the key of the given upload session |
| 92 | + * (a given client could have started a few http uploads at once) |
85 | 93 | */ |
86 | | - public static function doSessionIdDownload( $dn_session_id ){ |
87 | | - //get all the vars we need from session_id |
88 | | - session_id( $dn_session_id ); |
89 | | - $url = $_SESSION[ 'wsDownload' ][ 'url' ]; |
90 | | - $target_file_path_path = $_SESSION[ 'wsDownload' ][ 'target_file_path_path' ]; |
| 94 | + public static function doSessionIdDownload( $session_id, $upload_session_key ){ |
| 95 | + |
| 96 | + //set session to the provided key: |
| 97 | + session_id($session_id); |
| 98 | + //start the session |
| 99 | + if( session_start() === false){ |
| 100 | + wfDebug( __METHOD__ . ' could not start session'); |
| 101 | + } |
| 102 | + //get all the vars we need from session_id |
| 103 | + $sessionData = $_SESSION[ 'wsDownload' ][$upload_session_key]; |
| 104 | + //close down the session so we can other http queries can get session updates: |
| 105 | + session_write_close(); |
| 106 | + |
| 107 | + $req = new HttpRequest( $sessionData['url'], array( |
| 108 | + 'target_file_path' => $sessionData['target_file_path'], |
| 109 | + 'upload_session_key' => $upload_session_key |
| 110 | + ) ); |
| 111 | + $status = $req->doRequest(); |
| 112 | + if( $status->isOK() ){ |
| 113 | + //regrab the updated session data: |
| 114 | + $sessionData = $_SESSION[ 'wsDownload' ][$upload_session_key]; |
| 115 | + //done with 'download' now to inject and display warnings |
| 116 | + wfDebug("\nDONE with session download now to inject:\n"); |
| 117 | + wfDebug( print_r($_SESSION, true)); |
| 118 | + |
| 119 | + |
| 120 | + //$faxReq = new FauxRequest($sessionData['mParam'], true); |
| 121 | + //print_r($faxReq); |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + /** |
| 126 | + * Check if the URL can be served by localhost |
| 127 | + * @param $url string Full url to check |
| 128 | + * @return bool |
| 129 | + */ |
| 130 | + public static function isLocalURL( $url ) { |
| 131 | + global $wgCommandLineMode, $wgConf; |
| 132 | + if ( $wgCommandLineMode ) { |
| 133 | + return false; |
| 134 | + } |
91 | 135 | |
92 | | - //new req here: |
| 136 | + // Extract host part |
| 137 | + $matches = array(); |
| 138 | + if ( preg_match( '!^http://([\w.-]+)[/:].*$!', $url, $matches ) ) { |
| 139 | + $host = $matches[1]; |
| 140 | + // Split up dotwise |
| 141 | + $domainParts = explode( '.', $host ); |
| 142 | + // Check if this domain or any superdomain is listed in $wgConf as a local virtual host |
| 143 | + $domainParts = array_reverse( $domainParts ); |
| 144 | + for ( $i = 0; $i < count( $domainParts ); $i++ ) { |
| 145 | + $domainPart = $domainParts[$i]; |
| 146 | + if ( $i == 0 ) { |
| 147 | + $domain = $domainPart; |
| 148 | + } else { |
| 149 | + $domain = $domainPart . '.' . $domain; |
| 150 | + } |
| 151 | + if ( $wgConf->isLocalVHost( $domain ) ) { |
| 152 | + return true; |
| 153 | + } |
| 154 | + } |
| 155 | + } |
| 156 | + return false; |
93 | 157 | } |
| 158 | + |
94 | 159 | /** |
| 160 | + * Return a standard user-agent we can use for external requests. |
| 161 | + */ |
| 162 | + public static function userAgent() { |
| 163 | + global $wgVersion; |
| 164 | + return "MediaWiki/$wgVersion"; |
| 165 | + } |
| 166 | +} |
| 167 | +class HttpRequest{ |
| 168 | + var $target_file_path; |
| 169 | + var $upload_session_key; |
| 170 | + function __construct($url, $opt){ |
| 171 | + $this->url = $url; |
| 172 | + $this->method = (isset($opt['method']))?$opt['method']:'GET'; |
| 173 | + $this->target_file_path = (isset($opt['target_file_path']))?$opt['target_file_path']:false; |
| 174 | + $this->upload_session_key = (isset($opt['upload_session_key']))?$opt['upload_session_key']:false; |
| 175 | + } |
| 176 | +/** |
95 | 177 | * Get the contents of a file by HTTP |
96 | 178 | * @param $url string Full URL to act on |
97 | 179 | * @param $Opt associative array Optional array of options: |
— | — | @@ -98,10 +180,7 @@ |
99 | 181 | * 'target_file_path' => if curl should output to a target file |
100 | 182 | * 'adapter' => 'curl', 'soket' |
101 | 183 | */ |
102 | | - private function request() { |
103 | | - global $wgHTTPTimeout, $wgHTTPProxy, $wgTitle; |
104 | | - |
105 | | - wfDebug( __METHOD__ . ": $method $url\n" ); |
| 184 | + public function doRequest() { |
106 | 185 | # Use curl if available |
107 | 186 | if ( function_exists( 'curl_init' ) ) { |
108 | 187 | return $this->doCurlReq(); |
— | — | @@ -110,20 +189,22 @@ |
111 | 190 | } |
112 | 191 | } |
113 | 192 | private function doCurlReq(){ |
114 | | - $status = Status::newGood(); |
115 | | - $c = curl_init( $this->url ); |
116 | | - |
| 193 | + global $wgHTTPFileTimeout, $wgHTTPProxy, $wgTitle; |
| 194 | + |
| 195 | + $status = Status::newGood(); |
| 196 | + $c = curl_init( $this->url ); |
| 197 | + |
117 | 198 | //proxy setup: |
118 | | - if ( self::isLocalURL( $this->url ) ) { |
| 199 | + if ( Http::isLocalURL( $this->url ) ) { |
119 | 200 | curl_setopt( $c, CURLOPT_PROXY, 'localhost:80' ); |
120 | 201 | } else if ($wgHTTPProxy) { |
121 | 202 | curl_setopt($c, CURLOPT_PROXY, $wgHTTPProxy); |
122 | 203 | } |
123 | | - |
124 | | - curl_setopt( $c, CURLOPT_TIMEOUT, $wgHTTPTimeout ); |
| 204 | + |
| 205 | + curl_setopt( $c, CURLOPT_TIMEOUT, $wgHTTPFileTimeout ); |
125 | 206 | |
126 | 207 | |
127 | | - curl_setopt( $c, CURLOPT_USERAGENT, self :: userAgent() ); |
| 208 | + curl_setopt( $c, CURLOPT_USERAGENT, Http::userAgent() ); |
128 | 209 | |
129 | 210 | if ( $this->method == 'POST' ) { |
130 | 211 | curl_setopt( $c, CURLOPT_POST, true ); |
— | — | @@ -143,7 +224,7 @@ |
144 | 225 | |
145 | 226 | //set the write back function (if we are writing to a file) |
146 | 227 | if( $this->target_file_path ){ |
147 | | - $cwrite = new simpleFileWriter( $this->target_file_path ); |
| 228 | + $cwrite = new simpleFileWriter( $this->target_file_path, $this->upload_session_key ); |
148 | 229 | curl_setopt( $c, CURLOPT_WRITEFUNCTION, array($cwrite, 'callbackWriteBody') ); |
149 | 230 | } |
150 | 231 | |
— | — | @@ -213,55 +294,18 @@ |
214 | 295 | } |
215 | 296 | return $status; |
216 | 297 | } |
217 | | - /** |
218 | | - * Check if the URL can be served by localhost |
219 | | - * @param $url string Full url to check |
220 | | - * @return bool |
221 | | - */ |
222 | | - public static function isLocalURL( $url ) { |
223 | | - global $wgCommandLineMode, $wgConf; |
224 | | - if ( $wgCommandLineMode ) { |
225 | | - return false; |
226 | | - } |
227 | | - |
228 | | - // Extract host part |
229 | | - $matches = array(); |
230 | | - if ( preg_match( '!^http://([\w.-]+)[/:].*$!', $url, $matches ) ) { |
231 | | - $host = $matches[1]; |
232 | | - // Split up dotwise |
233 | | - $domainParts = explode( '.', $host ); |
234 | | - // Check if this domain or any superdomain is listed in $wgConf as a local virtual host |
235 | | - $domainParts = array_reverse( $domainParts ); |
236 | | - for ( $i = 0; $i < count( $domainParts ); $i++ ) { |
237 | | - $domainPart = $domainParts[$i]; |
238 | | - if ( $i == 0 ) { |
239 | | - $domain = $domainPart; |
240 | | - } else { |
241 | | - $domain = $domainPart . '.' . $domain; |
242 | | - } |
243 | | - if ( $wgConf->isLocalVHost( $domain ) ) { |
244 | | - return true; |
245 | | - } |
246 | | - } |
247 | | - } |
248 | | - return false; |
249 | | - } |
250 | | - |
251 | | - /** |
252 | | - * Return a standard user-agent we can use for external requests. |
253 | | - */ |
254 | | - public static function userAgent() { |
255 | | - global $wgVersion; |
256 | | - return "MediaWiki/$wgVersion"; |
257 | | - } |
258 | 298 | } |
259 | 299 | /** |
260 | | - * a simpleFileWriter |
| 300 | + * a simpleFileWriter with session id updates |
| 301 | + * |
261 | 302 | */ |
262 | 303 | class simpleFileWriter{ |
263 | 304 | var $target_file_path; |
264 | | - var $status = null; |
265 | | - function simpleFileWriter($target_file_path){ |
| 305 | + var $status = null; |
| 306 | + var $session_id = null; |
| 307 | + static $session_update_interval = 2; //how offten to update the session while downloading |
| 308 | + |
| 309 | + function simpleFileWriter($target_file_path, $session_id=false){ |
266 | 310 | $this->target_file_path = $target_file_path; |
267 | 311 | $this->status = Status::newGood(); |
268 | 312 | //open the file: |
— | — | @@ -269,22 +313,60 @@ |
270 | 314 | if( $this->fp === false ){ |
271 | 315 | $this->status = Status::newFatal('HTTP::could-not-open-file-for-writing'); |
272 | 316 | } |
| 317 | + //true start time |
| 318 | + $this->prevTime = time(); |
273 | 319 | } |
274 | 320 | public function callbackWriteBody($ch, $data_packet){ |
275 | 321 | global $wgMaxUploadSize; |
276 | 322 | //check file size: |
277 | 323 | clearstatcache(); |
278 | | - if( filesize( $this->target_file_path) > $wgMaxUploadSize){ |
279 | | - $this->status = Status::newFatal('HTTP::file-has-grown-beyond-upload-limit-killing: '. filesize( $this->target_file_path) . ' > ' . $wgMaxUploadSize); |
280 | | - return ; |
281 | | - } |
| 324 | + $this->current_fsize = filesize( $this->target_file_path); |
| 325 | + |
| 326 | + if( $this->current_fsize > $wgMaxUploadSize){ |
| 327 | + wfDebug( __METHOD__ . ' http download too large'); |
| 328 | + $this->status = Status::newFatal('HTTP::file-has-grown-beyond-upload-limit-killing: downloaded more than ' . |
| 329 | + Language::formatSize($wgMaxUploadSize) . ' '); |
| 330 | + return 0; |
| 331 | + } |
| 332 | + |
282 | 333 | //write out the content |
283 | 334 | if( fwrite($this->fp, $data_packet) === false){ |
284 | 335 | $this->status = Status::newFatal('HTTP::could-not-write-to-file'); |
285 | 336 | return 0; |
286 | | - } |
| 337 | + } |
| 338 | + |
| 339 | + //if more than 2 second have passed update_session_progress |
| 340 | + if($this->upload_session_key && (time() - $this->prevTime) > self::session_update_interval) { |
| 341 | + $this->prevTime = time(); |
| 342 | + $session_status = $this->update_session_progress(); |
| 343 | + if( !$session_status->isOK() ){ |
| 344 | + $this->status = $session_status; |
| 345 | + wfDebug( __METHOD__ . ' update session failed or was canceled'); |
| 346 | + return 0; |
| 347 | + } |
| 348 | + } |
287 | 349 | return strlen($data_packet); |
288 | 350 | } |
| 351 | + public function update_session_progress(){ |
| 352 | + $status = Status::newGood(); |
| 353 | + //start the session |
| 354 | + if( session_start() === false){ |
| 355 | + wfDebug( __METHOD__ . ' could not start session'); |
| 356 | + exit(0); |
| 357 | + } |
| 358 | + $sd =& $_SESSION[ 'wsDownload' ][$this->upload_session_key]; |
| 359 | + //check if the user canceled the request: |
| 360 | + if( $sd['user_cancel'] == true ){ |
| 361 | + //kill the download |
| 362 | + return Status::newFatal('user-canceled-request'); |
| 363 | + } |
| 364 | + //update the progress bytes download so far: |
| 365 | + $sd['loaded'] = $this->current_fsize; |
| 366 | + wfDebug('set session loaded amount to: ' . $sd['loaded']); |
| 367 | + //close down the session so we can other http queries can get session updates: |
| 368 | + session_write_close(); |
| 369 | + return $status; |
| 370 | + } |
289 | 371 | public function close(){ |
290 | 372 | if(false === fclose( $this->fp )){ |
291 | 373 | $this->status = Status::newFatal('HTTP::could-not-close-file'); |
Index: branches/new-upload/phase3/includes/specials/SpecialUpload.php |
— | — | @@ -82,15 +82,15 @@ |
83 | 83 | * @access public |
84 | 84 | */ |
85 | 85 | function execute() { |
86 | | - global $wgUser, $wgOut; |
87 | | - |
| 86 | + global $wgUser, $wgOut; |
| 87 | + |
88 | 88 | # Check uploading enabled |
89 | 89 | if( !UploadBase::isEnabled() ) { |
90 | 90 | $wgOut->showErrorPage( 'uploaddisabled', 'uploaddisabledtext' ); |
91 | 91 | return; |
92 | 92 | } |
93 | 93 | |
94 | | - # Check permissions |
| 94 | + # Check permissions |
95 | 95 | if( $this->mUpload ) { |
96 | 96 | $permission = $this->mUpload->isAllowed( $wgUser ); |
97 | 97 | } else { |
— | — | @@ -114,7 +114,7 @@ |
115 | 115 | if( wfReadOnly() ) { |
116 | 116 | $wgOut->readOnlyPage(); |
117 | 117 | return; |
118 | | - } |
| 118 | + } |
119 | 119 | if( $this->mReUpload ) { |
120 | 120 | // User choose to cancel upload |
121 | 121 | if( !$this->mUpload->unsaveUploadedFile() ) { |