PageRenderTime 3ms CodeModel.GetById 58ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

/filemanager/uploader/jupload.php

https://github.com/lihouyu/ever-simple-blog
PHP | 772 lines | 561 code | 44 blank | 167 comment | 180 complexity | 267e1ad905228e256f7aa52864b5ff04 MD5 | raw file
  1<?php
  2
  3/**
  4 * This class manage upload, with use of the JUpload applet. It's both a sample to show how to use the applet, and
  5 * a class you can use directly into your own application.
  6 *
  7 * Recommandation: Don't update its code !
  8 *
  9 * By doing this, you'll be able to reuse directly any update coming from the JUpload project, instead of reporting your
 10 * modifications into any new version of this class. This guarantees you that your project can use the last version of
 11 * JUpload, without any code modification. We work so that the applet behavior remains unchanged, but from time to time,
 12 * a change can appear.
 13 *
 14 * Sample:
 15 * - See the index.php samples, in the same folder.
 16 *
 17 * Notes:
 18 * - maxChunkSize: this class use a default maxChunkSize of 500K (or less, depending on the script max size). This allows
 19 * upload of FILES OF ANY SIZE on quite all ISP hosting. If it's too big for you (the max upload size of your ISP is less
 20 * than 500K), or if you want no chunk at all, you can, of course, override this default value.
 21 *
 22 *
 23 *
 24 * Parameters:
 25 * - $appletparams contains a map for applet parameters: key is the applet parameter name. The value is the value to transmit
 26 * 		to the applet. See the applet documentation for information on all applet parameters.
 27 * - $classparams contains the parameter specific for the JUpload class below. Here are the main class parameters:
 28 * 		- demo_mode. Files are uploaded to the server, but not stored on its hard drive. That is: you can simulate the global
 29 * 		behavior, but won't consume hard drive space. This mode is used on sourceforge web site.
 30 *
 31 *
 32 * Output generated for uploaded files:
 33 * - $files is an array of array. This can be managed by (a) the function given in the callbackAfterUploadManagement class
 34 * 		parameter, or (b) within the page whose URL is given in the afterUploadURL applet parameter, or (c) you can Extend the
 35 * 		class and redeclare defaultAfterUploadManagement() to your needs.
 36 * 	See the defaultAfterUploadManagement() for a sample on howto manage this array.
 37 *
 38 *   This array contains:
 39 *     - One entry per file. Each entry is an array, that contains all files properties, stored as $key=>$value.
 40 * 		The available keys are:
 41 * 		  - name: the filename, as it is now stored on the system.
 42 * 		  - size: the file size
 43 * 		  - path: the absolute path, where the file has been stored.
 44 * 			- fullName: the canonical file name (i.e. including the absolute path)
 45 * 		  - md5sum: the md5sum of the file, if further control is needed.
 46 * 			- mimetype: the calculated mime type of the file
 47 * 		  - If the formData applet parameter is used: all attributes (key and value) uploaded by the applet, are put here,
 48 * 			repeated for each file.
 49 *
 50 * 		Note: if you are using a callback function (i.e. callbackAfterUploadManagement) and you do not see a global 'object' you
 51 * 					are expecting then it might have been destroyed by PHP - c.f. http://bugs.php.net/bug.php?id=39693
 52 *
 53 */
 54
 55class JUpload {
 56
 57	var $appletparams;
 58	var $classparams;
 59	var $files;
 60
 61	public function JUpload($appletparams = array(), $classparams = array()) {
 62		if (gettype($classparams) != 'array')
 63		$this->abort('Invalid type of parameter classparams: Expecting an array');
 64		if (gettype($appletparams) != 'array')
 65		$this->abort('Invalid type of parameter appletparams: Expecting an array');
 66
 67		// set some defaults for the applet params
 68		if (!isset($appletparams['afterUploadURL']))
 69		$appletparams['afterUploadURL'] = $_SERVER['PHP_SELF'] . '?afterupload=1';
 70		if (!isset($appletparams['name']))
 71		$appletparams['name'] = 'JUpload';
 72		if (!isset($appletparams['archive']))
 73		$appletparams['archive'] = 'wjhk.jupload.jar';
 74		if (!isset($appletparams['code']))
 75		$appletparams['code'] = 'wjhk.jupload2.JUploadApplet';
 76		if (!isset($appletparams['debugLevel']))
 77		$appletparams['debugLevel'] = 0;
 78		if (!isset($appletparams['httpUploadParameterType']))
 79		$appletparams['httpUploadParameterType'] = 'array';
 80		if (!isset($appletparams['showLogWindow']))
 81		$appletparams['showLogWindow'] = ($appletparams['debugLevel'] > 0) ? 'true' : 'false';
 82		if (!isset($appletparams['width']))
 83		$appletparams['width'] = 640;
 84		if (!isset($appletparams['height']))
 85		$appletparams['height'] = ($appletparams['showLogWindow'] == 'true') ? 500 : 300;
 86		if (!isset($appletparams['mayscript']))
 87		$appletparams['mayscript'] = 'true';
 88		if (!isset($appletparams['scriptable']))
 89		$appletparams['scriptable'] = 'false';
 90		//if (!isset($appletparams['stringUploadSuccess']))
 91		$appletparams['stringUploadSuccess'] = 'SUCCESS';
 92		//if (!isset($appletparams['stringUploadError']))
 93		$appletparams['stringUploadError'] = 'ERROR: (.*)';
 94		$maxpost = $this->tobytes(ini_get('post_max_size'));
 95		$maxmem = $this->tobytes(ini_get('memory_limit'));
 96		$maxfs = $this->tobytes(ini_get('upload_max_filesize'));
 97		$obd = ini_get('open_basedir');
 98		if (!isset($appletparams['maxChunkSize'])) {
 99			$maxchunk = ($maxpost < $maxmem) ? $maxpost : $maxmem;
100			$maxchunk = ($maxchunk < $maxfs) ? $maxchunk : $maxfs;
101			$maxchunk /= 4;
102			$optchunk = (500000 > $maxchunk) ? $maxchunk : 500000;
103			$appletparams['maxChunkSize'] = $optchunk;
104		}
105		$appletparams['maxChunkSize'] = $this->tobytes($appletparams['maxChunkSize']);
106		if (!isset($appletparams['maxFileSize']))
107		$appletparams['maxFileSize'] = $maxfs;
108		$appletparams['maxFileSize'] = $this->tobytes($appletparams['maxFileSize']);
109		if (isset($classparams['errormail'])) {
110			$appletparams['urlToSendErrorTo'] = $_SERVER["PHP_SELF"] . '?errormail';
111		}
112
113		// Same for class parameters
114		if (!isset($classparams['demo_mode']))
115		$classparams['demo_mode'] = false;
116		if ($classparams['demo_mode']) {
117			$classparams['create_destdir'] = false;
118			$classparams['allow_subdirs'] = true;
119			$classparams['allow_zerosized'] = true;
120			$classparams['duplicate'] = 'overwrite';
121		}
122		if (!isset($classparams['debug_php']))											// set true to log some messages in PHP log
123		$classparams['debug_php'] = false;
124		if (!isset($this->classparams['allowed_mime_types']))				// array of allowed MIME type
125		$classparams['allowed_mime_types'] = 'all';
126		if (!isset($this->classparams['allowed_file_extensions'])) 	// array of allowed file extensions
127		$classparams['allowed_file_extensions'] = 'all';
128		if (!isset($classparams['verbose_errors']))						// shouldn't display server info on a production site!
129		$classparams['verbose_errors'] = true;
130		if (!isset($classparams['session_regenerate']))
131		$classparams['session_regenerate'] = false;
132		if (!isset($classparams['create_destdir']))
133		$classparams['create_destdir'] = true;
134		if (!isset($classparams['allow_subdirs']))
135		$classparams['allow_subdirs'] = false;
136		if (!isset($classparams['spaces_in_subdirs']))
137		$classparams['spaces_in_subdirs'] = false;
138		if (!isset($classparams['convert_spaces']))         // set to true to convert spaces in filenames to _
139		$classparams['convert_spaces'] = false;
140		if (!isset($classparams['allow_zerosized']))
141		$classparams['allow_zerosized'] = false;
142		if (!isset($classparams['duplicate']))
143		$classparams['duplicate'] = 'rename';
144		if (!isset($classparams['dirperm']))
145		$classparams['dirperm'] = 0755;
146		if (!isset($classparams['fileperm']))
147		$classparams['fileperm'] = 0644;
148		if (!isset($classparams['destdir'])) {
149			if ($obd != '')
150			$classparams['destdir'] = $obd;
151			else
152			$classparams['destdir'] = '/var/tmp/jupload_test';
153		}else{
154			$classparams['destdir']=str_replace('~',' ',$classparams['destdir']);
155		}
156		if ($classparams['create_destdir']) {
157			$_umask = umask(0); 	// override the system mask
158			@mkdir($classparams['destdir'], $classparams['dirperm']);
159			umask($_umask);
160		}
161		if (!is_dir($classparams['destdir']) && is_writable($classparams['destdir']))
162		$this->abort('Destination dir not accessible');
163		if (!isset($classparams['tmp_prefix']))
164		$classparams['tmp_prefix'] = 'jutmp.';
165		if (!isset($classparams['var_prefix']))
166		$classparams['var_prefix'] = 'juvar.';
167		if (!isset($classparams['jscript_wrapper']))
168		$classparams['jscript_wrapper'] = 'JUploadSetProperty';
169		if (!isset($classparams['tag_jscript']))
170		$classparams['tag_jscript'] = '<!--JUPLOAD_JSCRIPT-->';
171		if (!isset($classparams['tag_applet']))
172		$classparams['tag_applet'] = '<!--JUPLOAD_APPLET-->';
173		if (!isset($classparams['tag_flist']))
174		$classparams['tag_flist'] = '<!--JUPLOAD_FILES-->';
175		if (!isset($classparams['http_flist_start']))
176		$classparams['http_flist_start'] =
177            		"<table border='1'><TR><TH>Filename</TH><TH>file size</TH><TH>Relative path</TH><TH>Full name</TH><TH>md5sum</TH><TH>Specific parameters</TH></TR>";
178		if (!isset($classparams['http_flist_end']))
179		$classparams['http_flist_end'] = "</table>\n";
180		if (!isset($classparams['http_flist_file_before']))
181		$classparams['http_flist_file_before'] = "<tr><td>";
182		if (!isset($classparams['http_flist_file_between']))
183		$classparams['http_flist_file_between'] = "</td><td>";
184		if (!isset($classparams['http_flist_file_after']))
185		$classparams['http_flist_file_after'] = "</td></tr>\n";
186
187		$this->appletparams = $appletparams;
188		$this->classparams = $classparams;
189		$this->page_start();
190	}
191
192	/**
193	 * Return an array of uploaded files * The array contains: name, size, tmp_name, error,
194	 * relativePath, md5sum, mimetype, fullName, path
195	 */
196	public function uploadedfiles() {
197		return $this->files;
198	}
199
200	/**
201	 * Log a message on the current output, as a HTML comment.
202	 */
203	protected function logDebug($function, $msg, $htmlComment=true) {
204		$output = "[DEBUG] [$function] $msg";
205		if ($htmlComment) {
206			echo("<!-- $output -->\r\n");
207		} else {
208			echo("$output\r\n");
209		}
210	}
211
212	/**
213	 * Log a message to the PHP log.
214	 * Declared "protected" so it may be Extended if you require customised logging (e.g. particular log file location).
215	 */
216	protected function logPHPDebug($function, $msg) {
217		if ($this->classparams['debug_php'] === true) {
218			$output = "[DEBUG] [$function] ".$this->arrayexpand($msg);
219			error_log($output);
220		}
221	}
222
223	private function arrayexpand($array) {
224		$output = '';
225		if (is_array($array)) {
226			foreach ($array as $key => $value) {
227				$output .= "\n ".$key.' => '.$this->arrayexpand($value);
228			}
229		} else {
230			$output .= $array;
231		}
232		return $output;
233	}
234
235
236	/**
237	 * Convert a value ending in 'G','M' or 'K' to bytes
238	 *
239	 */
240	private function tobytes($val) {
241		$val = trim($val);
242		$last = fix_strtolower($val{strlen($val)-1});
243		switch($last) {
244			case 'g':
245				$val *= 1024;
246			case 'm':
247				$val *= 1024;
248			case 'k':
249				$val *= 1024;
250		}
251		return $val;
252	}
253
254	/**
255	 * Build a string, containing a javascript wrapper function
256	 * for setting applet properties via JavaScript. This is necessary,
257	 * because we use the "modern" method of including the applet (using
258	 * <object> resp. <embed> tags) in order to trigger automatic JRE downloading.
259	 * Therefore, in Netscape-like browsers, the applet is accessible via
260	 * the document.embeds[] array while in others, it is accessible via the
261	 * document.applets[] array.
262	 *
263	 * @return A string, containing the necessary wrapper function (named JUploadSetProperty)
264	 */
265	private function str_jsinit() {
266		$N = "\n";
267		$name = $this->appletparams['name'];
268		$ret = '<script type="text/javascript">'.$N;
269		$ret .= '<!--'.$N;
270		$ret .= 'function '.$this->classparams['jscript_wrapper'].'(name, value) {'.$N;
271		$ret .= '  document.applets["'.$name.'"] == null || document.applets["'.$name.'"].setProperty(name,value);'.$N;
272		$ret .= '  document.embeds["'.$name.'"] == null || document.embeds["'.$name.'"].setProperty(name,value);'.$N;
273		$ret .= '}'.$N;
274		$ret .= '//-->'.$N;
275		$ret .= '</script>';
276		return $ret;
277	}
278
279	/**
280	 * Build a string, containing the applet tag with all parameters.
281	 *
282	 * @return A string, containing the applet tag
283	 */
284	private function str_applet() {
285		$N = "\n";
286		$params = $this->appletparams;
287		// return the actual applet tag
288		$ret = '<object classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"'.$N;
289		$ret .= '  codebase = "http://java.sun.com/update/1.5.0/jinstall-1_5-windows-i586.cab#Version=5,0,0,3"'.$N;
290		$ret .= '  width = "'.$params['width'].'"'.$N;
291		$ret .= '  height = "'.$params['height'].'"'.$N;
292		$ret .= '  name = "'.$params['name'].'">'.$N;
293		foreach ($params as $key => $val) {
294			if ($key != 'width' && $key != 'height')
295			$ret .= '  <param name = "'.$key.'" value = "'.$val.'" />'.$N;
296		}
297		$ret .= '  <comment>'.$N;
298		$ret .= '    <embed'.$N;
299		$ret .= '      type = "application/x-java-applet;version=1.5"'.$N;
300		foreach ($params as $key => $val)
301		$ret .= '      '.$key.' = "'.$val.'"'.$N;
302		$ret .= '      pluginspage = "http://java.sun.com/products/plugin/index.html#download">'.$N;
303		$ret .= '      <noembed>'.$N;
304		$ret .= '        Java 1.5 or higher plugin required.'.$N;
305		$ret .= '      </noembed>'.$N;
306		$ret .= '    </embed>'.$N;
307		$ret .= '  </comment>'.$N;
308		$ret .= '</object>';
309		return $ret;
310	}
311
312	private function abort($msg = '') {
313		$this->cleanup();
314		if ($msg != '')
315		die(str_replace('(.*)',$msg,$this->appletparams['stringUploadError'])."\n");
316		exit;
317	}
318
319	private function warning($msg = '') {
320		$this->cleanup();
321		if ($msg != '')
322		echo('WARNING: '.$msg."\n");
323		echo $this->appletparams['stringUploadSuccess']."\n";
324		exit;
325	}
326
327	private function cleanup() {
328		// remove all uploaded files of *this* request
329		if (isset($_FILES)) {
330			foreach ($_FILES as $key => $val)
331			@unlink($val['tmp_name']);
332		}
333		// remove accumulated file, if any.
334		@unlink($this->classparams['destdir'].'/'.$this->classparams['tmp_prefix'].session_id());
335		@unlink($this->classparams['destdir'].'/'.$this->classparams['tmp_prefix'].'tmp'.session_id());
336		// reset session var
337		$_SESSION['RF'][$this->classparams['var_prefix'].'size'] = 0;
338		return;
339	}
340
341	private function mkdirp($path) {
342		// create subdir (hierary) below destdir;
343		$dirs = explode('/', $path);
344		$path = $this->classparams['destdir'];
345		foreach ($dirs as $dir) {
346			$path .= '/'.$dir;
347			if (!file_exists($path)) {  // @ does NOT always supress the error!
348				$_umask = umask(0); 	// override the system mask
349				@mkdir($path, $this->classparams['dirperm']);
350				umask($_umask);
351			}
352		}
353		if (!is_dir($path) && is_writable($path))
354		$this->abort('Destination dir not accessible');
355	}
356
357	/**
358	 * This method:
359	 * - Replaces some potentially dangerous characters by '_' (in the given name an relative path)
360	 * - Checks if a files of the same name already exists.
361	 * 		- If no: no problem.
362	 * 		- If yes, and the duplicate class param is set to rename, the file is renamed.
363	 * 		- If yes, and the duplicate class param is set to overwrite, the file is not renamed. The existing one will be erased.
364	 * 		- If yes, and the duplicate class param is set to reject, an error is thrown.
365	 */
366	private function dstfinal(&$name, &$subdir) {
367		$name = preg_replace('![`$\\\\/|]!', '_', $name);
368		if ($this->classparams['convert_spaces']) {
369            $name = str_replace(' ', '_', $name);
370		}
371		if ($this->classparams['allow_subdirs'] && ($subdir != '')) {
372			$subdir = trim(preg_replace('!\\\\!','/',$subdir),'/');
373			$subdir = preg_replace('![`$|]!', '_', $subdir);
374			if (!$this->classparams['spaces_in_subdirs']) {
375				$subdir = str_replace(' ','_',$subdir);
376			}
377			// recursively create subdir
378			if (!$this->classparams['demo_mode'])
379			$this->mkdirp($subdir);
380			// append a slash
381			$subdir .= '/';
382		} else {
383			$subdir = '';
384		}
385		$ret = $this->classparams['destdir'].'/'.$subdir.$name;
386		if (file_exists($ret)) {
387			if ($this->classparams['duplicate'] == 'overwrite') {
388				return $ret;
389			}
390			if ($this->classparams['duplicate'] == 'reject') {
391				$this->abort('A file with the same name already exists');
392			}
393			if ($this->classparams['duplicate'] == 'warning') {
394				$this->warning("File $name already exists - rejected");
395			}
396			if ($this->classparams['duplicate'] == 'rename') {
397				$cnt = 1;
398				$dir = $this->classparams['destdir'].'/'.$subdir;
399				$ext = strrchr($name, '.');
400				if ($ext) {
401					$nameWithoutExtension = substr($name, 0, strlen($name) - strlen($ext));
402				} else {
403					$ext = '';
404					$nameWithoutExtension = $name;
405				}
406
407				$rtry = $dir.$nameWithoutExtension.'_'.$cnt.$ext;
408				while (file_exists($rtry)) {
409					$cnt++;
410					$rtry = $dir.$nameWithoutExtension.'._'.$cnt.$ext;
411				}
412				//We store the result name in the byReference name parameter.
413				$name = $nameWithoutExtension.'_'.$cnt.$ext;
414				$ret = $rtry;
415			}
416		}
417		return $ret;
418	}
419
420	/**
421	 * Example function to process the files uploaded.  This one simply displays the files' data.
422	 *
423	 */
424	public function defaultAfterUploadManagement() {
425		$flist = '[defaultAfterUploadManagement] Nb uploaded files is: ' . sizeof($this->files);
426		$flist = $this->classparams['http_flist_start'];
427		foreach ($this->files as $f) {
428			//$f is an array, that contains all info about the uploaded file.
429			$this->logDebug('defaultAfterUploadManagement', "  Reading file ${f['name']}");
430			$flist .= $this->classparams['http_flist_file_before'];
431			$flist .= $f['name'];
432			$flist .= $this->classparams['http_flist_file_between'];
433			$flist .= $f['size'];
434			$flist .= $this->classparams['http_flist_file_between'];
435			$flist .= $f['relativePath'];
436			$flist .= $this->classparams['http_flist_file_between'];
437			$flist .= $f['fullName'];
438			$flist .= $this->classparams['http_flist_file_between'];
439			$flist .= $f['md5sum'];
440			$addBR = false;
441			foreach ($f as $key=>$value) {
442				//If it's a specific key, let's display it:
443				if ($key != 'name' && $key != 'size' && $key != 'relativePath' && $key != 'fullName' && $key != 'md5sum') {
444					if ($addBR) {
445						$flist .= "<br>";
446					} else {
447						// First line. We must add a new 'official' list separator.
448						$flist .= $this->classparams['http_flist_file_between'];
449						$addBR = true;
450					}
451					$flist .= "$key => $value";
452				}
453			}
454			$flist .= $this->classparams['http_flist_file_after'];
455	}
456	$flist .= $this->classparams['http_flist_end'];
457
458	return $flist;
459}
460
461/**
462 * Generation of the applet tag, and necessary things around (js content). Insertion of this into the content of the
463 * page.
464 * See the tag_jscript and tag_applet class parameters.
465 */
466private function generateAppletTag($str) {
467	$this->logDebug('generateAppletTag', 'Entering function');
468	$str = preg_replace('/'.$this->classparams['tag_jscript'].'/', $this->str_jsinit(), $str);
469	return preg_replace('/'.$this->classparams['tag_applet'].'/', $this->str_applet(), $str);
470}
471
472/**
473 * This function is called when constructing the page, when we're not reveiving uploaded files. It 'just' construct
474 * the applet tag, by calling the relevant function.
475 *
476 * This *must* be public, because it is called from PHP's output buffering
477 */
478public function interceptBeforeUpload($str) {
479	$this->logDebug('interceptBeforeUpload', 'Entering function');
480	return $this->generateAppletTag($str);
481}
482
483/**
484 * This function displays the uploaded files description in the current page (see tag_flist class parameter)
485 *
486 * This *must* be public, because it is called from PHP's output buffering.
487 */
488public function interceptAfterUpload($str) {
489	$this->logDebug('interceptAfterUpload', 'Entering function');
490	$this->logPHPDebug('interceptAfterUpload', $this->files);
491
492	if (count($this->files) > 0) {
493		if (isset($this->classparams['callbackAfterUploadManagement'])) {
494			$this->logDebug('interceptAfterUpload', 'Before call of ' .$this->classparams['callbackAfterUploadManagement']);
495			$strForFListContent = call_user_func($this->classparams['callbackAfterUploadManagement'], $this, $this->files);
496		} else {
497			$strForFListContent = $this->defaultAfterUploadManagement();
498		}
499		$str = preg_replace('/'.$this->classparams['tag_flist'].'/', $strForFListContent, $str);
500	}
501	return $this->generateAppletTag($str);
502}
503
504/**
505 * This method manages the receiving of the debug log, when an error occurs.
506 */
507private function receive_debug_log() {
508	// handle error report
509	if (isset($_POST['description']) && isset($_POST['log'])) {
510		$msg = $_POST['log'];
511		mail($this->classparams['errormail'], $_POST['description'], $msg);
512	} else {
513		if (isset($_SERVER['SERVER_ADMIN']))
514		mail($_SERVER['SERVER_ADMIN'], 'Empty jupload error log',
515                    'An empty log has just been posted.');
516		$this->logPHPDebug('receive_debug_log', 'Empty error log received');
517	}
518	exit;
519}
520
521/**
522 * This method is the heart of the system. It manage the files sent by the applet, check the incoming parameters (md5sum) and
523 * reconstruct the files sent in chunk mode.
524 *
525 * The result is stored in the $files array, and can then be managed by the function given in the callbackAfterUploadManagement
526 * class parameter, or within the page whose URL is given in the afterUploadURL applet parameter.
527 * Or you can Extend the class and redeclare defaultAfterUploadManagement() to your needs.
528 */
529private function receive_uploaded_files() {
530	$this->logDebug('receive_uploaded_files', 'Entering POST management');
531
532	if (session_id() == '') {
533		session_start();
534	}
535	// we check for the session *after* handling possible error log
536	// because an error could have happened because the session-id is missing.
537	if (!isset($_SESSION['RF'][$this->classparams['var_prefix'].'size'])) {
538		$this->abort('Invalid session (in afterupload, POST, check of size)');
539	}
540	if (!isset($_SESSION['RF'][$this->classparams['var_prefix'].'files'])) {
541		$this->abort('Invalid session (in afterupload, POST, check of files)');
542	}
543	$this->files = $_SESSION['RF'][$this->classparams['var_prefix'].'files'];
544	if (!is_array($this->files)) {
545		$this->abort('Invalid session (in afterupload, POST, is_array(files))');
546	}
547	if ($this->appletparams['sendMD5Sum'] == 'true'  &&  !isset($_POST['md5sum'])) {
548		$this->abort('Required POST variable md5sum is missing');
549	}
550	$cnt = 0;
551	foreach ($_FILES as $key => $value) {
552		//Let's read the $_FILES data
553		if (isset($files_data)) {
554			unset($files_data);
555		}
556		$jupart			= (isset($_POST['jupart']))		 		? (int)$_POST['jupart']		: 0;
557		$jufinal		= (isset($_POST['jufinal']))	 		? (int)$_POST['jufinal']	: 1;
558		$relpaths		= (isset($_POST['relpathinfo'])) 	? $_POST['relpathinfo']		: null;
559		$md5sums		= (isset($_POST['md5sum']))				? $_POST['md5sum']				: null;
560		$mimetypes 	= (isset($_POST['mimetype'])) 	 	? $_POST['mimetype'] 			: null;
561		//$relpaths = (isset($_POST["relpathinfo$cnt"])) ? $_POST["relpathinfo$cnt"] : null;
562		//$md5sums = (isset($_POST["md5sum$cnt"])) ? $_POST["md5sum$cnt"] : null;
563
564		if (gettype($relpaths) == 'string') {
565			$relpaths = array($relpaths);
566		}
567		if (gettype($md5sums) == 'string') {
568			$md5sums = array($md5sums);
569		}
570		if ($this->appletparams['sendMD5Sum'] == 'true'  && !is_array($md5sums)) {
571			$this->abort('Expecting an array of MD5 checksums');
572		}
573		if (!is_array($relpaths)) {
574			$this->abort('Expecting an array of relative paths');
575		}
576		if (!is_array($mimetypes)) {
577			$this->abort('Expecting an array of MIME types');
578		}
579		// Check the MIME type (note: this is easily forged!)
580		if (isset($this->classparams['allowed_mime_types']) && is_array($this->classparams['allowed_mime_types'])) {
581			if (!in_array($mimetypes[$cnt], $this->classparams['allowed_mime_types'])) {
582				$this->abort('MIME type '.$mimetypes[$cnt].' not allowed');
583			}
584		}
585		if (isset($this->classparams['allowed_file_extensions']) && is_array($this->classparams['allowed_file_extensions'])) {
586			$fileExtension = substr(strrchr($value['name'][$cnt], "."), 1);
587			if (!in_array($fileExtension, $this->classparams['allowed_file_extensions'])) {
588				$this->abort('File extension '.$fileExtension.' not allowed');
589			}
590		}
591
592		$dstdir = $this->classparams['destdir'];
593		$dstname = $dstdir.'/'.$this->classparams['tmp_prefix'].session_id();
594		$tmpname = $dstdir.'/'.$this->classparams['tmp_prefix'].'tmp'.session_id();
595
596		// Controls are now done. Let's store the current uploaded files properties in an array, for future use.
597		$files_data['name']					= $value['name'][$cnt];
598		$files_data['size']					= 'not calculated yet';
599		$files_data['tmp_name']			= $value['tmp_name'][$cnt];
600		$files_data['error']    		= $value['error'][$cnt];
601		$files_data['relativePath'] = $relpaths[$cnt];
602		$files_data['md5sum']  			= $md5sums[$cnt];
603		$files_data['mimetype']  		= $mimetypes[$cnt];
604
605		if (!move_uploaded_file($files_data['tmp_name'], $tmpname)) {
606			if ($classparams['verbose_errors']) {
607				$this->abort("Unable to move uploaded file (from ${files_data['tmp_name']} to $tmpname)");
608		} else {
609			trigger_error("Unable to move uploaded file (from ${files_data['tmp_name']} to $tmpname)",E_USER_WARNING);
610			$this->abort("Unable to move uploaded file");
611	}
612}
613
614// In demo mode, no file storing is done. We just delete the newly uploaded file.
615if ($this->classparams['demo_mode']) {
616	if ($jufinal || (!$jupart)) {
617		if ($jupart) {
618			$files_data['size']		= ($jupart-1) * $this->appletparams['maxChunkSize'] + filesize($tmpname);
619		} else {
620			$files_data['size']		= filesize($tmpname);
621		}
622		$files_data['fullName']	= 'Demo mode<BR>No file storing';
623		array_push($this->files, $files_data);
624	}
625	unlink($tmpname);
626	$cnt++;
627	continue;
628}
629//If we get here, the upload is a real one (no demo)
630if ($jupart) {
631	// got a chunk of a multi-part upload
632	$len = filesize($tmpname);
633	$_SESSION['RF'][$this->classparams['var_prefix'].'size'] += $len;
634	if ($len > 0) {
635		$src = fopen($tmpname, 'rb');
636		$dst = fopen($dstname, ($jupart == 1) ? 'wb' : 'ab');
637		while ($len > 0) {
638			$rlen = ($len > 8192) ? 8192 : $len;
639			$buf = fread($src, $rlen);
640			if (!$buf) {
641				fclose($src);
642				fclose($dst);
643				unlink($dstname);
644				$this->abort('read IO error');
645			}
646			if (!fwrite($dst, $buf, $rlen)) {
647				fclose($src);
648				fclose($dst);
649				unlink($dstname);
650				$this->abort('write IO error');
651			}
652			$len -= $rlen;
653		}
654		fclose($src);
655		fclose($dst);
656		unlink($tmpname);
657	}
658	if ($jufinal) {
659		// This is the last chunk. Check total lenght and
660		// rename it to it's final name.
661		$dlen = filesize($dstname);
662		if ($dlen != $_SESSION['RF'][$this->classparams['var_prefix'].'size'])
663		$this->abort('file size mismatch');
664		if ($this->appletparams['sendMD5Sum'] == 'true' ) {
665			if ($md5sums[$cnt] != md5_file($dstname))
666			$this->abort('MD5 checksum mismatch');
667		}
668		// remove zero sized files
669		if (($dlen > 0) || $this->classparams['allow_zerosized']) {
670			$dstfinal = $this->dstfinal($files_data['name'],$files_data['relativePath']);
671			if (!rename($dstname, $dstfinal))
672			$this->abort('rename IO error');
673			$_umask = umask(0); 	// override the system mask
674			if (!chmod($dstfinal, $this->classparams['fileperm']))
675				$this->abort('chmod IO error');
676			umask($_umask);
677			$files_data['size']		= filesize($dstfinal);
678			$files_data['fullName']	= $dstfinal;
679			$files_data['path']	= fix_dirname($dstfinal);
680			array_push($this->files, $files_data);
681		} else {
682			unlink($dstname);
683		}
684		// reset session var
685		$_SESSION['RF'][$this->classparams['var_prefix'].'size'] = 0;
686	}
687} else {
688	// Got a single file upload. Trivial.
689	if ($this->appletparams['sendMD5Sum'] == 'true' ) {
690		if ($md5sums[$cnt] != md5_file($tmpname))
691			$this->abort('MD5 checksum mismatch');
692	}
693	$dstfinal = $this->dstfinal($files_data['name'],$files_data['relativePath']);
694	if (!rename($tmpname, $dstfinal))
695	$this->abort('rename IO error');
696	$_umask = umask(0); 	// override the system mask
697	if (!chmod($dstfinal, $this->classparams['fileperm']))
698		$this->abort('chmod IO error');
699	umask($_umask);
700	$files_data['size']		= filesize($dstfinal);
701	$files_data['fullName']	= $dstfinal;
702	$files_data['path']	= fix_dirname($dstfinal);
703	array_push($this->files, $files_data);
704}
705$cnt++;
706}
707
708echo $this->appletparams['stringUploadSuccess']."\n";
709$_SESSION['RF'][$this->classparams['var_prefix'].'files'] = $this->files;
710session_write_close();
711exit;
712}
713
714/**
715 *
716 *
717 */
718private function page_start() {
719	$this->logDebug('page_start', 'Entering function');
720
721	// If the applet checks for the serverProtocol, it issues a HEAD request
722	// -> Simply return an empty doc.
723	if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
724		// Nothing to do
725
726	} else if ($_SERVER['REQUEST_METHOD'] == 'GET') {
727		// A GET request means: return upload page
728		$this->logDebug('page_start', 'Entering GET management');
729
730		if (session_id() == '') {
731			session_start();
732		}
733		if (isset($_GET['afterupload'])) {
734			$this->logDebug('page_start', 'afterupload is set');
735			if (!isset($_SESSION['RF'][$this->classparams['var_prefix'].'files'])) {
736				$this->abort('Invalid session (in afterupload, GET, check of $_SESSION["RF"]): files array is not set');
737			}
738			$this->files = $_SESSION['RF'][$this->classparams['var_prefix'].'files'];
739			if (!is_array($this->files)) {
740				$this->abort('Invalid session (in afterupload, GET, check of is_array(files)): files is not an array');
741			}
742			// clear session data ready for new upload
743			$_SESSION['RF'][$this->classparams['var_prefix'].'files'] = array();
744
745			// start intercepting the content of the calling page, to display the upload result.
746			ob_start(array(& $this, 'interceptAfterUpload'));
747
748		} else {
749			$this->logDebug('page_start', 'afterupload is not set');
750			if ($this->classparams['session_regenerate']) {
751				session_regenerate_id(true);
752			}
753			$this->files = array();
754			$_SESSION['RF'][$this->classparams['var_prefix'].'size'] = 0;
755			$_SESSION['RF'][$this->classparams['var_prefix'].'files'] = $this->files;
756			// start intercepting the content of the calling page, to display the applet tag.
757			ob_start(array(& $this, 'interceptBeforeUpload'));
758		}
759
760	} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
761		// If we got a POST request, this is the real work.
762		if (isset($_GET['errormail'])) {
763			//Hum, an error occurs on server side. Let's manage the debug log, that we just received.
764			$this->receive_debug_log();
765		} else {
766			$this->receive_uploaded_files();
767		}
768	}
769}
770}
771
772// PHP end tag omitted intentionally!!