Completed
Push — master ( b3d43a...f93ee0 )
by
unknown
01:51 queued 50s
created

getReplacedByExtension()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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