Code Duplication    Length = 814-814 lines in 2 locations

htdocs/class/xoopseditor/tinymce4/external_plugins/filemanager/uploader/jupload.php 1 location

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

htdocs/class/xoopseditor/tinymce4bootstrap/external_plugins/filemanager/uploader/jupload.php 1 location

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