PageRenderTime 65ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/filemanager/inc/jupload.php

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