Passed
Push — master ( 08314e...a4ad40 )
by Julito
10:14
created

clean_up_path()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 4
nop 2
dl 0
loc 19
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ResourceLink;
5
use Chamilo\CoreBundle\Framework\Container;
6
use Chamilo\CourseBundle\Entity\CDocument;
7
use Symfony\Component\HttpFoundation\File\UploadedFile;
8
9
/**
10
 * Changes the file name extension from .php to .phps
11
 * Useful for securing a site.
12
 *
13
 * @author Hugues Peeters <[email protected]>
14
 *
15
 * @param string $file_name Name of a file
16
 *
17
 * @return string the filename phps'ized
18
 */
19
function php2phps($file_name)
20
{
21
    return preg_replace('/\.(phar.?|php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
22
}
23
24
/**
25
 * Renames .htaccess & .HTACCESS to htaccess.txt.
26
 *
27
 * @param string $filename
28
 *
29
 * @return string
30
 */
31
function htaccess2txt($filename)
32
{
33
    return str_replace(['.htaccess', '.HTACCESS'], ['htaccess.txt', 'htaccess.txt'], $filename);
34
}
35
36
/**
37
 * This function executes our safety precautions
38
 * more functions can be added.
39
 *
40
 * @param string $filename
41
 *
42
 * @return string
43
 *
44
 * @see php2phps()
45
 * @see htaccess2txt()
46
 */
47
function disable_dangerous_file($filename)
48
{
49
    return htaccess2txt(php2phps($filename));
50
}
51
52
/**
53
 * Returns the name without extension, used for the title.
54
 *
55
 * @param string $name
56
 *
57
 * @return name without the extension
58
 */
59
function get_document_title($name)
60
{
61
    // If they upload .htaccess...
62
    $name = disable_dangerous_file($name);
63
    $ext = substr(strrchr($name, '.'), 0);
64
65
    if (empty($ext)) {
66
        return substr($name, 0, strlen($name));
67
    }
68
69
    return substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
70
}
71
72
/**
73
 * This function checks if the upload succeeded.
74
 *
75
 * @return true if upload succeeded
76
 */
77
function process_uploaded_file($uploadedFileData, $show_output = true)
78
{
79
    $uploadedFile = [];
80
    if ($uploadedFileData instanceof UploadedFile) {
81
        $uploadedFile['error'] = $uploadedFileData->getError();
82
        $uploadedFile['tmp_name'] = $uploadedFileData->getPathname();
83
        $uploadedFile['size'] = $uploadedFileData->getSize();
84
    } else {
85
        $uploadedFile = $uploadedFileData;
86
    }
87
88
    // Checking the error code sent with the file upload.
89
    if (isset($uploadedFile['error'])) {
90
        switch ($uploadedFile['error']) {
91
            case 1:
92
                // The uploaded file exceeds the upload_max_filesize directive in php.ini.
93
                if ($show_output) {
94
                    Display::addFlash(
95
                        Display::return_message(
96
                            get_lang('The uploaded file exceeds the maximum filesize allowed by the server:').ini_get('upload_max_filesize'),
97
                            'error'
98
                        )
99
                    );
100
                }
101
102
                return false;
103
            case 2:
104
                // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
105
                // Not used at the moment, but could be handy if we want to limit the size of an upload
106
                // (e.g. image upload in html editor).
107
                $max_file_size = (int) $_POST['MAX_FILE_SIZE'];
108
                if ($show_output) {
109
                    Display::addFlash(
110
                        Display::return_message(
111
                            get_lang('The file size exceeds the maximum allowed setting:').format_file_size($max_file_size),
112
                            'error'
113
                        )
114
                    );
115
                }
116
117
                return false;
118
            case 3:
119
                // The uploaded file was only partially uploaded.
120
                if ($show_output) {
121
                    Display::addFlash(
122
                        Display::return_message(
123
                            get_lang('The uploaded file was only partially uploaded.').' '.get_lang('Please Try Again!'),
124
                            'error'
125
                        )
126
                    );
127
                }
128
129
                return false;
130
            case 4:
131
                // No file was uploaded.
132
                if ($show_output) {
133
                    Display::addFlash(
134
                        Display::return_message(
135
                            get_lang('No file was uploaded.').' '.get_lang('Please select a file before pressing the upload button.'),
136
                            'error'
137
                        )
138
                    );
139
                }
140
141
                return false;
142
        }
143
    }
144
145
    if (!file_exists($uploadedFile['tmp_name'])) {
146
        // No file was uploaded.
147
        if ($show_output) {
148
            Display::addFlash(Display::return_message(get_lang('The file upload has failed.'), 'error'));
149
        }
150
151
        return false;
152
    }
153
154
    if (file_exists($uploadedFile['tmp_name'])) {
155
        $filesize = filesize($uploadedFile['tmp_name']);
156
        if (empty($filesize)) {
157
            // No file was uploaded.
158
            if ($show_output) {
159
                Display::addFlash(
160
                    Display::return_message(
161
                        get_lang('The file upload has failed.SizeIsZero'),
162
                        'error'
163
                    )
164
                );
165
            }
166
167
            return false;
168
        }
169
    }
170
171
    $course_id = api_get_course_id();
172
173
    //Checking course quota if we are in a course
174
    if (!empty($course_id)) {
175
        $max_filled_space = DocumentManager::get_course_quota();
176
        // Check if there is enough space to save the file
177
        if (!DocumentManager::enough_space($uploadedFile['size'], $max_filled_space)) {
178
            if ($show_output) {
179
                Display::addFlash(
180
                    Display::return_message(
181
                        get_lang('There is not enough space to upload this file.'),
182
                        'error'
183
                    )
184
                );
185
            }
186
187
            return false;
188
        }
189
    }
190
191
    // case 0: default: We assume there is no error, the file uploaded with success.
192
    return true;
193
}
194
195
/**
196
 * Tries to add an extension to files without extension
197
 * Some applications on Macintosh computers don't add an extension to the files.
198
 * This subroutine try to fix this on the basis of the MIME type sent
199
 * by the browser.
200
 *
201
 * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
202
 *        We don't have solution for this kind of situation
203
 *
204
 * @author Hugues Peeters <[email protected]>
205
 * @author Bert Vanderkimpen
206
 *
207
 * @param string $file_name Name of the file
208
 * @param string $file_type Type of the file
209
 *
210
 * @return string File name
211
 */
212
function add_ext_on_mime($file_name, $file_type)
213
{
214
    // Check whether the file has an extension AND whether the browser has sent a MIME Type
215
216
    if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
217
        // Build a "MIME-types / extensions" connection table
218
        static $mime_type = [];
219
220
        $mime_type[] = 'application/msword';
221
        $extension[] = '.doc';
222
        $mime_type[] = 'application/rtf';
223
        $extension[] = '.rtf';
224
        $mime_type[] = 'application/vnd.ms-powerpoint';
225
        $extension[] = '.ppt';
226
        $mime_type[] = 'application/vnd.ms-excel';
227
        $extension[] = '.xls';
228
        $mime_type[] = 'application/pdf';
229
        $extension[] = '.pdf';
230
        $mime_type[] = 'application/postscript';
231
        $extension[] = '.ps';
232
        $mime_type[] = 'application/mac-binhex40';
233
        $extension[] = '.hqx';
234
        $mime_type[] = 'application/x-gzip';
235
        $extension[] = 'tar.gz';
236
        $mime_type[] = 'application/x-shockwave-flash';
237
        $extension[] = '.swf';
238
        $mime_type[] = 'application/x-stuffit';
239
        $extension[] = '.sit';
240
        $mime_type[] = 'application/x-tar';
241
        $extension[] = '.tar';
242
        $mime_type[] = 'application/zip';
243
        $extension[] = '.zip';
244
        $mime_type[] = 'application/x-tar';
245
        $extension[] = '.tar';
246
        $mime_type[] = 'text/html';
247
        $extension[] = '.html';
248
        $mime_type[] = 'text/plain';
249
        $extension[] = '.txt';
250
        $mime_type[] = 'text/rtf';
251
        $extension[] = '.rtf';
252
        $mime_type[] = 'img/gif';
253
        $extension[] = '.gif';
254
        $mime_type[] = 'img/jpeg';
255
        $extension[] = '.jpg';
256
        $mime_type[] = 'img/png';
257
        $extension[] = '.png';
258
        $mime_type[] = 'audio/midi';
259
        $extension[] = '.mid';
260
        $mime_type[] = 'audio/mpeg';
261
        $extension[] = '.mp3';
262
        $mime_type[] = 'audio/x-aiff';
263
        $extension[] = '.aif';
264
        $mime_type[] = 'audio/x-pn-realaudio';
265
        $extension[] = '.rm';
266
        $mime_type[] = 'audio/x-pn-realaudio-plugin';
267
        $extension[] = '.rpm';
268
        $mime_type[] = 'audio/x-wav';
269
        $extension[] = '.wav';
270
        $mime_type[] = 'video/mpeg';
271
        $extension[] = '.mpg';
272
        $mime_type[] = 'video/mpeg4-generic';
273
        $extension[] = '.mp4';
274
        $mime_type[] = 'video/quicktime';
275
        $extension[] = '.mov';
276
        $mime_type[] = 'video/x-msvideo';
277
        $extension[] = '.avi';
278
279
        $mime_type[] = 'video/x-ms-wmv';
280
        $extension[] = '.wmv';
281
        $mime_type[] = 'video/x-flv';
282
        $extension[] = '.flv';
283
        $mime_type[] = 'image/svg+xml';
284
        $extension[] = '.svg';
285
        $mime_type[] = 'image/svg+xml';
286
        $extension[] = '.svgz';
287
        $mime_type[] = 'video/ogg';
288
        $extension[] = '.ogv';
289
        $mime_type[] = 'audio/ogg';
290
        $extension[] = '.oga';
291
        $mime_type[] = 'application/ogg';
292
        $extension[] = '.ogg';
293
        $mime_type[] = 'application/ogg';
294
        $extension[] = '.ogx';
295
        $mime_type[] = 'application/x-freemind';
296
        $extension[] = '.mm';
297
298
        $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12';
299
        $extension[] = '.docm';
300
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
301
        $extension[] = '.docx';
302
        $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12';
303
        $extension[] = '.dotm';
304
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template';
305
        $extension[] = '.dotx';
306
        $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12';
307
        $extension[] = '.potm';
308
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template';
309
        $extension[] = '.potx';
310
        $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12';
311
        $extension[] = '.ppam';
312
        $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12';
313
        $extension[] = '.ppsm';
314
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow';
315
        $extension[] = '.ppsx';
316
        $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12';
317
        $extension[] = '.pptm';
318
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
319
        $extension[] = '.pptx';
320
        $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12';
321
        $extension[] = '.xlam';
322
        $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12';
323
        $extension[] = '.xlsb';
324
        $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12';
325
        $extension[] = '.xlsm';
326
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
327
        $extension[] = '.xlsx';
328
        $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12';
329
        $extension[] = '.xltm';
330
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template';
331
        $extension[] = '.xltx';
332
333
        // Test on PC (files with no extension get application/octet-stream)
334
        //$mime_type[] = 'application/octet-stream';      $extension[] = '.ext';
335
        // Check whether the MIME type sent by the browser is within the table
336
        foreach ($mime_type as $key => &$type) {
337
            if ($type == $file_type) {
338
                $file_name .= $extension[$key];
339
                break;
340
            }
341
        }
342
343
        unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
344
    }
345
346
    return $file_name;
347
}
348
349
/**
350
 * This function is a callback function that is used while extracting a zipfile
351
 * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract.
352
 *
353
 * @param array $p_event
354
 * @param array $p_header
355
 *
356
 * @return int (If the function returns 1, then the extraction is resumed, if 0 the path was skipped)
357
 */
358
function clean_up_files_in_zip($p_event, &$p_header)
359
{
360
    $originalStoredFileName = $p_header['stored_filename'];
361
    $baseName = basename($originalStoredFileName);
362
    // Skip files
363
    $skipFiles = [
364
        '__MACOSX',
365
        '.Thumbs.db',
366
        'Thumbs.db',
367
    ];
368
369
    if (in_array($baseName, $skipFiles)) {
370
        return 0;
371
    }
372
    $modifiedStoredFileName = clean_up_path($originalStoredFileName);
373
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
374
375
    return 1;
376
}
377
378
function cleanZipFilesNoRename($p_event, &$p_header)
379
{
380
    $originalStoredFileName = $p_header['stored_filename'];
381
    $baseName = basename($originalStoredFileName);
382
    // Skip files
383
    $skipFiles = [
384
        '__MACOSX',
385
        '.Thumbs.db',
386
        'Thumbs.db',
387
    ];
388
389
    if (in_array($baseName, $skipFiles)) {
390
        return 0;
391
    }
392
    $modifiedStoredFileName = clean_up_path($originalStoredFileName, false);
393
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
394
395
    return 1;
396
}
397
398
/**
399
 * Allow .htaccess file.
400
 *
401
 * @param $p_event
402
 * @param $p_header
403
 *
404
 * @return int
405
 */
406
function cleanZipFilesAllowHtaccess($p_event, &$p_header)
407
{
408
    $originalStoredFileName = $p_header['stored_filename'];
409
    $baseName = basename($originalStoredFileName);
410
411
    $allowFiles = ['.htaccess'];
412
    if (in_array($baseName, $allowFiles)) {
413
        return 1;
414
    }
415
416
    // Skip files
417
    $skipFiles = [
418
        '__MACOSX',
419
        '.Thumbs.db',
420
        'Thumbs.db',
421
    ];
422
423
    if (in_array($baseName, $skipFiles)) {
424
        return 0;
425
    }
426
    $modifiedStoredFileName = clean_up_path($originalStoredFileName);
427
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
428
429
    return 1;
430
}
431
432
/**
433
 * This function cleans up a given path
434
 * by eliminating dangerous file names and cleaning them.
435
 *
436
 * @param string $path
437
 * @param bool   $replaceName
438
 *
439
 * @return string
440
 *
441
 * @see disable_dangerous_file()
442
 * @see api_replace_dangerous_char()
443
 */
444
function clean_up_path($path, $replaceName = true)
445
{
446
    // Split the path in folders and files
447
    $path_array = explode('/', $path);
448
    // Clean up every folder and filename in the path
449
    foreach ($path_array as $key => &$val) {
450
        // We don't want to lose the dots in ././folder/file (cfr. zipfile)
451
        if ('.' != $val) {
452
            if ($replaceName) {
453
                $val = api_replace_dangerous_char($val);
454
            }
455
            $val = disable_dangerous_file($val);
456
        }
457
    }
458
    // Join the "cleaned" path (modified in-place as passed by reference)
459
    $path = implode('/', $path_array);
460
    filter_extension($path);
461
462
    return $path;
463
}
464
465
/**
466
 * Checks if the file is dangerous, based on extension and/or mimetype.
467
 * The list of extensions accepted/rejected can be found from
468
 * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include').
469
 *
470
 * @param string $filename passed by reference. The filename will be modified
471
 *                         if filter rules say so! (you can include path but the filename should look like 'abc.html')
472
 *
473
 * @return int 0 to skip file, 1 to keep file
474
 */
475
function filter_extension(&$filename)
476
{
477
    if ('/' == substr($filename, -1)) {
478
        return 1; // Authorize directories
479
    }
480
    $blacklist = api_get_setting('upload_extensions_list_type');
481
    if ('whitelist' != $blacklist) { // if = blacklist
482
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_blacklist')));
483
484
        $skip = api_get_setting('upload_extensions_skip');
485
        $ext = strrchr($filename, '.');
486
        $ext = substr($ext, 1);
487
        if (empty($ext)) {
488
            return 1; // We're in blacklist mode, so accept empty extensions
489
        }
490
        if (in_array(strtolower($ext), $extensions)) {
491
            if ('true' == $skip) {
492
                return 0;
493
            } else {
494
                $new_ext = api_get_setting('upload_extensions_replace_by');
495
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
496
497
                return 1;
498
            }
499
        } else {
500
            return 1;
501
        }
502
    } else {
503
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_whitelist')));
504
        $skip = api_get_setting('upload_extensions_skip');
505
        $ext = strrchr($filename, '.');
506
        $ext = substr($ext, 1);
507
        if (empty($ext)) {
508
            return 1; // Accept empty extensions
509
        }
510
        if (!in_array(strtolower($ext), $extensions)) {
511
            if ('true' == $skip) {
512
                return 0;
513
            } else {
514
                $new_ext = api_get_setting('upload_extensions_replace_by');
515
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
516
517
                return 1;
518
            }
519
        } else {
520
            return 1;
521
        }
522
    }
523
}
524
525
/**
526
 * Creates a new directory trying to find a directory name
527
 * that doesn't already exist.
528
 *
529
 * @author  Hugues Peeters <[email protected]>
530
 * @author  Bert Vanderkimpen
531
 *
532
 * @param array  $_course                 current course information
533
 * @param int    $user_id                 current user id
534
 * @param int    $session_id
535
 * @param int    $to_group_id             group.id
536
 * @param int    $to_user_id
537
 * @param string $base_work_dir           /var/www/chamilo/courses/ABC/document
538
 * @param string $desired_dir_name        complete path of the desired name
539
 *                                        Example: /folder1/folder2
540
 * @param string $title                   "folder2"
541
 * @param int    $visibility              (0 for invisible, 1 for visible, 2 for deleted)
542
 * @param bool   $generateNewNameIfExists
543
 * @param bool   $sendNotification        depends in conf setting "send_notification_when_document_added"
544
 * @param array  $parentInfo
545
 *
546
 * @return CDocument|false
547
 */
548
function create_unexisting_directory(
549
    $_course,
550
    $user_id,
551
    $session_id,
552
    $to_group_id,
553
    $to_user_id,
554
    $base_work_dir,
555
    $desired_dir_name,
556
    $title = '',
557
    $visibility = '',
558
    $generateNewNameIfExists = false,
559
    $sendNotification = true,
560
    $parentInfo = null
561
) {
562
    $course_id = $_course['real_id'];
563
    $session_id = (int) $session_id;
564
565
    $parentId = 0;
566
    if (!empty($parentInfo)) {
567
        if (is_array($parentInfo) && isset($parentInfo['iid'])) {
568
            $parentId = $parentInfo['iid'];
569
        }
570
        if ($parentInfo instanceof CDocument) {
571
            $parentId = $parentInfo->getIid();
572
        }
573
    }
574
575
    $document = DocumentManager::addDocument(
576
        $_course,
577
        $desired_dir_name,
578
        'folder',
579
        0,
580
        $title,
581
        null,
582
        0,
583
        true,
584
        $to_group_id,
585
        $session_id,
586
        $user_id,
587
        $sendNotification,
588
        '',
589
        $parentId
590
    );
591
592
    if ($document) {
593
        return $document;
594
    }
595
596
    $folderExists = DocumentManager::folderExists(
597
        $desired_dir_name,
598
        $_course,
599
        $session_id,
600
        $to_group_id
601
    );
602
603
    if (true === $folderExists) {
604
        if ($generateNewNameIfExists) {
605
            $counter = 1;
606
            while (1) {
607
                $folderExists = DocumentManager::folderExists(
608
                    $desired_dir_name.'_'.$counter,
609
                    $_course,
610
                    $session_id,
611
                    $to_group_id
612
                );
613
614
                if (false === $folderExists) {
615
                    break;
616
                }
617
                $counter++;
618
            }
619
            $desired_dir_name = $desired_dir_name.'_'.$counter;
620
        }
621
    }
622
623
    $systemFolderName = $desired_dir_name;
624
625
    // Adding suffix
626
    $suffix = DocumentManager::getDocumentSuffix(
627
        $_course,
628
        $session_id,
629
        $to_group_id
630
    );
631
632
    $systemFolderName .= $suffix;
633
634
    if (null == $title) {
635
        $title = basename($desired_dir_name);
636
    }
637
638
    // Check if pathname already exists inside document table
639
    $table = Database::get_course_table(TABLE_DOCUMENT);
640
    $sql = "SELECT iid, path FROM $table
641
            WHERE
642
                c_id = $course_id AND
643
                path = '".Database::escape_string($systemFolderName)."'";
644
    $rs = Database::query($sql);
645
646
    $parentId = 0;
647
    if (!empty($parentInfo) && isset($parentInfo['iid'])) {
648
        $parentId = $parentInfo['iid'];
649
    }
650
651
    if (0 == Database::num_rows($rs)) {
652
        $document = DocumentManager::addDocument(
653
            $_course,
654
            $systemFolderName,
655
            'folder',
656
            0,
657
            $title,
658
            null,
659
            0,
660
            true,
661
            $to_group_id,
662
            $session_id,
663
            $user_id,
664
            $sendNotification,
665
            '',
666
            $parentId
667
        );
668
669
        if ($document) {
670
            return $document;
671
        }
672
    } else {
673
        $document = Database::fetch_array($rs);
674
        $documentData = DocumentManager::get_document_data_by_id(
675
            $document['iid'],
676
            $_course['code'],
677
            false,
678
            $session_id
679
        );
680
681
        if ($documentData) {
682
            $document = Container::getDocumentRepository()->find($documentData['iid']);
683
684
            return $document;
685
        }
686
    }
687
688
    return false;
689
}
690