Completed
Push — master ( 3032f7...6249a5 )
by Julito
10:51
created

cleanZipFilesAllowHtaccess()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 2
dl 0
loc 24
rs 9.7998
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
 * FILE UPLOAD LIBRARY.
10
 *
11
 * This is the file upload library for Chamilo.
12
 * Include/require it in your code to use its functionality.
13
 *
14
 * @package chamilo.library
15
 *
16
 * @todo test and reorganise
17
 */
18
19
/**
20
 * Changes the file name extension from .php to .phps
21
 * Useful for securing a site.
22
 *
23
 * @author Hugues Peeters <[email protected]>
24
 *
25
 * @param string $file_name Name of a file
26
 *
27
 * @return string the filename phps'ized
28
 */
29
function php2phps($file_name)
30
{
31
    return preg_replace('/\.(php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
32
}
33
34
/**
35
 * Renames .htaccess & .HTACCESS to htaccess.txt.
36
 *
37
 * @param string $filename
38
 *
39
 * @return string
40
 */
41
function htaccess2txt($filename)
42
{
43
    return str_replace(['.htaccess', '.HTACCESS'], ['htaccess.txt', 'htaccess.txt'], $filename);
44
}
45
46
/**
47
 * This function executes our safety precautions
48
 * more functions can be added.
49
 *
50
 * @param string $filename
51
 *
52
 * @return string
53
 *
54
 * @see php2phps()
55
 * @see htaccess2txt()
56
 */
57
function disable_dangerous_file($filename)
58
{
59
    return htaccess2txt(php2phps($filename));
60
}
61
62
/**
63
 * Returns the name without extension, used for the title.
64
 *
65
 * @param string $name
66
 *
67
 * @return name without the extension
68
 */
69
function get_document_title($name)
70
{
71
    // If they upload .htaccess...
72
    $name = disable_dangerous_file($name);
73
    $ext = substr(strrchr($name, '.'), 0);
74
75
    if (empty($ext)) {
76
        return substr($name, 0, strlen($name));
77
    }
78
79
    return substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
80
}
81
82
/**
83
 * This function checks if the upload succeeded.
84
 *
85
 * @param array $uploaded_file ($_FILES)
86
 *
87
 * @return true if upload succeeded
88
 */
89
function process_uploaded_file($uploaded_file, $show_output = true)
90
{
91
    // Checking the error code sent with the file upload.
92
    if (isset($uploaded_file['error'])) {
93
        switch ($uploaded_file['error']) {
94
            case 1:
95
                // The uploaded file exceeds the upload_max_filesize directive in php.ini.
96
                if ($show_output) {
97
                    Display::addFlash(
98
                        Display::return_message(
99
                            get_lang('UplExceedMaxServerUpload').ini_get('upload_max_filesize'),
100
                            'error'
101
                        )
102
                    );
103
                }
104
105
                return false;
106
            case 2:
107
                // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
108
                // Not used at the moment, but could be handy if we want to limit the size of an upload
109
                // (e.g. image upload in html editor).
110
                $max_file_size = (int) $_POST['MAX_FILE_SIZE'];
111
                if ($show_output) {
112
                    Display::addFlash(
113
                        Display::return_message(
114
                            get_lang('UplExceedMaxPostSize').format_file_size($max_file_size),
115
                            'error'
116
                        )
117
                    );
118
                }
119
120
                return false;
121
            case 3:
122
                // The uploaded file was only partially uploaded.
123
                if ($show_output) {
124
                    Display::addFlash(
125
                        Display::return_message(
126
                            get_lang('UplPartialUpload').' '.get_lang('PleaseTryAgain'),
127
                            'error'
128
                        )
129
                    );
130
                }
131
132
                return false;
133
            case 4:
134
                // No file was uploaded.
135
                if ($show_output) {
136
                    Display::addFlash(
137
                        Display::return_message(
138
                            get_lang('UplNoFileUploaded').' '.get_lang('UplSelectFileFirst'),
139
                            'error'
140
                        )
141
                    );
142
                }
143
144
                return false;
145
        }
146
    }
147
148
    if (!file_exists($uploaded_file['tmp_name'])) {
149
        // No file was uploaded.
150
        if ($show_output) {
151
            Display::addFlash(Display::return_message(get_lang('UplUploadFailed'), 'error'));
152
        }
153
154
        return false;
155
    }
156
157
    if (file_exists($uploaded_file['tmp_name'])) {
158
        $filesize = filesize($uploaded_file['tmp_name']);
159
        if (empty($filesize)) {
160
            // No file was uploaded.
161
            if ($show_output) {
162
                Display::addFlash(
163
                    Display::return_message(
164
                        get_lang('UplUploadFailedSizeIsZero'),
165
                        'error'
166
                    )
167
                );
168
            }
169
170
            return false;
171
        }
172
    }
173
174
    $course_id = api_get_course_id();
175
176
    //Checking course quota if we are in a course
177
    if (!empty($course_id)) {
178
        $max_filled_space = DocumentManager::get_course_quota();
179
        // Check if there is enough space to save the file
180
        if (!DocumentManager::enough_space($uploaded_file['size'], $max_filled_space)) {
181
            if ($show_output) {
182
                Display::addFlash(
183
                    Display::return_message(
184
                        get_lang('UplNotEnoughSpace'),
185
                        'error'
186
                    )
187
                );
188
            }
189
190
            return false;
191
        }
192
    }
193
194
    // case 0: default: We assume there is no error, the file uploaded with success.
195
    return true;
196
}
197
198
/**
199
 * This function does the save-work for the documents.
200
 * It handles the uploaded file and adds the properties to the database
201
 * If unzip=1 and the file is a zipfile, it is extracted
202
 * If we decide to save ALL kinds of documents in one database,
203
 * we could extend this with a $type='document', 'scormdocument',...
204
 *
205
 * @param array  $courseInfo
206
 * @param array  $uploadedFile            ($_FILES)
207
 *                                        array(
208
 *                                        'name' => 'picture.jpg',
209
 *                                        'tmp_name' => '...', // absolute path
210
 *                                        );
211
 * @param string $documentDir             Example: /var/www/chamilo/courses/ABC/document
212
 * @param string $uploadPath              Example: /folder1/folder2/
213
 * @param int    $userId
214
 * @param int    $groupId                 group.id
215
 * @param int    $toUserId                User ID, or NULL for everybody
216
 * @param int    $unzip                   1/0
217
 * @param string $whatIfFileExists        overwrite, rename or warn if exists (default)
218
 * @param bool   $output                  optional output parameter
219
 * @param bool   $onlyUploadFile
220
 * @param string $comment
221
 * @param int    $sessionId
222
 * @param bool   $treat_spaces_as_hyphens
223
 * @param string $uploadKey
224
 * @param int    $parentId
225
 * @param $content
226
 *
227
 * So far only use for unzip_uploaded_document function.
228
 * If no output wanted on success, set to false.
229
 *
230
 * @return CDocument|false
231
 */
232
function handle_uploaded_document(
233
    $courseInfo,
234
    $uploadedFile,
235
    $documentDir,
236
    $uploadPath,
237
    $userId,
238
    $groupId = 0,
239
    $toUserId = null,
240
    $unzip = 0,
241
    $whatIfFileExists = '',
242
    $output = true,
243
    $onlyUploadFile = false,
244
    $comment = null,
245
    $sessionId = null,
246
    $treat_spaces_as_hyphens = true,
247
    $uploadKey = '',
248
    $parentId = 0,
249
    $content = null
250
) {
251
    if (!$userId) {
252
        return false;
253
    }
254
255
    $userInfo = api_get_user_info();
256
    $uploadedFile['name'] = stripslashes($uploadedFile['name']);
257
    // Add extension to files without one (if possible)
258
    $uploadedFile['name'] = add_ext_on_mime($uploadedFile['name'], $uploadedFile['type']);
259
    $sessionId = (int) $sessionId;
260
    if (empty($sessionId)) {
261
        $sessionId = api_get_session_id();
262
    }
263
264
    $group = api_get_group_entity($groupId);
265
266
    // Just in case process_uploaded_file is not called
267
    $maxSpace = DocumentManager::get_course_quota();
268
269
    // Check if there is enough space to save the file
270
    if (!DocumentManager::enough_space($uploadedFile['size'], $maxSpace)) {
271
        if ($output) {
272
            Display::addFlash(Display::return_message(get_lang('UplNotEnoughSpace'), 'error'));
273
        }
274
275
        return false;
276
    }
277
278
    // If the want to unzip, check if the file has a .zip (or ZIP,Zip,ZiP,...) extension
279
    if ($unzip == 1 && preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
280
        return unzip_uploaded_document(
281
            $courseInfo,
282
            $userInfo,
283
            $uploadedFile,
284
            $uploadPath,
285
            $documentDir,
286
            $maxSpace,
287
            $sessionId,
288
            $groupId,
289
            $output,
290
            $onlyUploadFile,
291
            $whatIfFileExists
292
        );
293
    } elseif ($unzip == 1 && !preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
294
        // We can only unzip ZIP files (no gz, tar,...)
295
        if ($output) {
296
            Display::addFlash(
297
                Display::return_message(get_lang('UplNotAZip')." ".get_lang('PleaseTryAgain'), 'error')
298
            );
299
        }
300
301
        return false;
302
    } else {
303
        // Clean up the name, only ASCII characters should stay. (and strict)
304
        $cleanName = api_replace_dangerous_char($uploadedFile['name'], $treat_spaces_as_hyphens);
305
306
        // No "dangerous" files
307
        $cleanName = disable_dangerous_file($cleanName);
308
309
        // Checking file extension
310
        if (!filter_extension($cleanName)) {
311
            if ($output) {
312
                Display::addFlash(
313
                    Display::return_message(get_lang('UplUnableToSaveFileFilteredExtension'), 'error')
314
                );
315
            }
316
317
            return false;
318
        } else {
319
            // If the upload path differs from / (= root) it will need a slash at the end
320
            if ($uploadPath != '/') {
321
                $uploadPath = $uploadPath.'/';
322
            }
323
324
            // Full path to where we want to store the file with trailing slash
325
            $whereToSave = $documentDir.$uploadPath;
326
327
            // Just upload the file "as is"
328
            if ($onlyUploadFile) {
329
                $errorResult = moveUploadedFile($uploadedFile, $whereToSave.$cleanName);
330
                if ($errorResult) {
331
                    return $whereToSave.$cleanName;
332
                } else {
333
                    return $errorResult;
334
                }
335
            }
336
337
            /*
338
                Based in the clean name we generate a new filesystem name
339
                Using the session_id and group_id if values are not empty
340
            */
341
            $fileSystemName = DocumentManager::fixDocumentName(
342
                $cleanName,
343
                'file',
344
                $courseInfo,
345
                $sessionId,
346
                $groupId
347
            );
348
349
            // Name of the document without the extension (for the title)
350
            $documentTitle = get_document_title($uploadedFile['name']);
351
352
            // Size of the uploaded file (in bytes)
353
            $fileSize = $uploadedFile['size'];
354
355
            // Example: /folder/picture.jpg
356
            $filePath = $uploadPath.$fileSystemName;
357
358
            $docId = DocumentManager::get_document_id(
359
                $courseInfo,
360
                $filePath,
361
                $sessionId
362
            );
363
364
            $documentRepo = Container::$container->get('Chamilo\CourseBundle\Repository\CDocumentRepository');
365
            $document = $documentRepo->find($docId);
366
367
            if (!($content instanceof UploadedFile)) {
368
                $request = Container::getRequest();
369
                $content = $request->files->get($uploadKey);
370
                if (is_array($content)) {
371
                    $content = $content[0];
372
                }
373
            }
374
375
            // What to do if the target file exists
376
            switch ($whatIfFileExists) {
377
                // Overwrite the file if it exists
378
                case 'overwrite':
379
                    if ($document) {
380
                        // Update file size
381
                        update_existing_document(
382
                            $courseInfo,
383
                            $document->getIid(),
384
                            $uploadedFile['size']
385
                        );
386
387
                        $document = DocumentManager::addFileToDocument(
388
                            $document,
389
                            $filePath,
390
                            null,
391
                            $content,
392
                            null,
393
                            null,
394
                            $group
395
                        );
396
397
                        // Display success message with extra info to user
398
                        if ($document && $output) {
399
                            Display::addFlash(
400
                                Display::return_message(
401
                                    get_lang('UplUploadSucceeded').'<br /> '.
402
                                    $document->getTitle().' '.get_lang('UplFileOverwritten'),
403
                                    'confirmation',
404
                                    false
405
                                )
406
                            );
407
                        }
408
409
                        return $document;
410
                    } else {
411
                        // Put the document data in the database
412
                        $document = DocumentManager::addDocument(
413
                            $courseInfo,
414
                            $filePath,
415
                            'file',
416
                            $fileSize,
417
                            $documentTitle,
418
                            $comment,
419
                            0,
420
                            null,
421
                            $groupId,
422
                            $sessionId,
423
                            0,
424
                            true,
425
                            $content
426
                        );
427
428
                        // Display success message to user
429
                        if ($document) {
430
                            Display::addFlash(
431
                                Display::return_message(
432
                                    get_lang('UplUploadSucceeded').'<br /> '.$documentTitle,
433
                                    'confirmation',
434
                                    false
435
                                )
436
                            );
437
                        }
438
439
                        return $document;
440
                    }
441
                    break;
442
                case 'rename':
443
                    // Rename the file if it exists
444
                    $cleanName = DocumentManager::getUniqueFileName(
445
                        $uploadPath,
446
                        $cleanName,
447
                        $courseInfo,
448
                        $sessionId,
449
                        $groupId
450
                    );
451
452
                    $fileSystemName = DocumentManager::fixDocumentName(
453
                        $cleanName,
454
                        'file',
455
                        $courseInfo,
456
                        $sessionId,
457
                        $groupId
458
                    );
459
460
                    $documentTitle = disable_dangerous_file($cleanName);
461
                    $filePath = $uploadPath.$fileSystemName;
462
463
                    // Put the document data in the database
464
                    $document = DocumentManager::addDocument(
465
                        $courseInfo,
466
                        $filePath,
467
                        'file',
468
                        $fileSize,
469
                        $documentTitle,
470
                        $comment, // comment
471
                        0, // read only
472
                        true, // save visibility
473
                        $groupId,
474
                        $sessionId,
475
                        0,
476
                        true,
477
                        $content,
478
                        $parentId
479
                    );
480
481
                    // Display success message to user
482
                    if ($output) {
483
                        Display::addFlash(
484
                            Display::return_message(
485
                                get_lang('UplUploadSucceeded').'<br />'.
486
                                get_lang('UplFileSavedAs').' '.$document->getTitle(),
487
                                'success',
488
                                false
489
                            )
490
                        );
491
                    }
492
493
                    return $document;
494
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
495
                case 'nothing':
496
                    if ($document) {
497
                        if ($output) {
498
                            Display::addFlash(
499
                                Display::return_message(
500
                                    $uploadPath.$cleanName.' '.get_lang('UplAlreadyExists'),
501
                                    'warning',
502
                                    false
503
                                )
504
                            );
505
                        }
506
                        break;
507
                    }
508
                    // no break
509
                default:
510
                    // Only save the file if it doesn't exist or warn user if it does exist
511
                    if ($document) {
512
                        if ($output) {
513
                            Display::addFlash(
514
                                Display::return_message($cleanName.' '.get_lang('UplAlreadyExists'), 'warning', false)
515
                            );
516
                        }
517
                    } else {
518
                        // Put the document data in the database
519
                        $document = DocumentManager::addDocument(
520
                            $courseInfo,
521
                            $filePath,
522
                            'file',
523
                            $fileSize,
524
                            $documentTitle,
525
                            $comment,
526
                            0,
527
                            true,
528
                            $groupId,
529
                            $sessionId,
530
                            0,
531
                            true,
532
                            $content
533
                        );
534
535
                        if ($document) {
536
                            // Display success message to user
537
                            if ($output) {
538
                                Display::addFlash(
539
                                    Display::return_message(
540
                                        get_lang('UplUploadSucceeded').'<br /> '.$documentTitle,
541
                                        'confirm',
542
                                        false
543
                                    )
544
                                );
545
                            }
546
547
                            return $document;
548
                        } else {
549
                            if ($output) {
550
                                Display::addFlash(
551
                                    Display::return_message(
552
                                        get_lang('UplUnableToSaveFile'),
553
                                        'error',
554
                                        false
555
                                    )
556
                                );
557
                            }
558
                        }
559
                    }
560
                    break;
561
            }
562
        }
563
    }
564
}
565
566
/**
567
 * @param string $file
568
 * @param string $storePath
569
 *
570
 * @return bool
571
 */
572
function moveUploadedFile($file, $storePath)
573
{
574
    $handleFromFile = isset($file['from_file']) && $file['from_file'] ? true : false;
575
    $moveFile = isset($file['move_file']) && $file['move_file'] ? true : false;
576
    if ($moveFile) {
577
        $copied = copy($file['tmp_name'], $storePath);
578
579
        if (!$copied) {
580
            return false;
581
        }
582
    }
583
    if ($handleFromFile) {
584
        return file_exists($file['tmp_name']);
585
    } else {
586
        return move_uploaded_file($file['tmp_name'], $storePath);
587
    }
588
}
589
590
/**
591
 * Checks if there is enough place to add a file on a directory
592
 * on the base of a maximum directory size allowed
593
 * deprecated: use enough_space instead!
594
 *
595
 * @author Hugues Peeters <[email protected]>
596
 *
597
 * @param int    $file_size     Size of the file in byte
598
 * @param string $dir           Path of the directory where the file should be added
599
 * @param int    $max_dir_space Maximum size of the diretory in byte
600
 *
601
 * @return bool true if there is enough space, false otherwise
602
 *
603
 * @see enough_size() uses  dir_total_space() function
604
 */
605
function enough_size($file_size, $dir, $max_dir_space)
606
{
607
    // If the directory is the archive directory, safely ignore the size limit
608
    if (api_get_path(SYS_ARCHIVE_PATH) == $dir) {
609
        return true;
610
    }
611
612
    if ($max_dir_space) {
613
        $already_filled_space = dir_total_space($dir);
614
        if (($file_size + $already_filled_space) > $max_dir_space) {
615
            return false;
616
        }
617
    }
618
619
    return true;
620
}
621
622
/**
623
 * Computes the size already occupied by a directory and is subdirectories.
624
 *
625
 * @author Hugues Peeters <[email protected]>
626
 *
627
 * @param string $dir_path Size of the file in byte
628
 *
629
 * @return int Return the directory size in bytes
630
 */
631
function dir_total_space($dir_path)
632
{
633
    $save_dir = getcwd();
634
    chdir($dir_path);
635
    $handle = opendir($dir_path);
636
    $sumSize = 0;
637
    $dirList = [];
638
    while ($element = readdir($handle)) {
639
        if ($element == '.' || $element == '..') {
640
            continue; // Skip the current and parent directories
641
        }
642
        if (is_file($element)) {
643
            $sumSize += filesize($element);
644
        }
645
        if (is_dir($element)) {
646
            $dirList[] = $dir_path.'/'.$element;
647
        }
648
    }
649
650
    closedir($handle);
651
652
    if (sizeof($dirList) > 0) {
653
        foreach ($dirList as $j) {
654
            $sizeDir = dir_total_space($j); // Recursivity
655
            $sumSize += $sizeDir;
656
        }
657
    }
658
    chdir($save_dir); // Return to initial position
659
660
    return $sumSize;
661
}
662
663
/**
664
 * Tries to add an extension to files without extension
665
 * Some applications on Macintosh computers don't add an extension to the files.
666
 * This subroutine try to fix this on the basis of the MIME type sent
667
 * by the browser.
668
 *
669
 * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
670
 *        We don't have solution for this kind of situation
671
 *
672
 * @author Hugues Peeters <[email protected]>
673
 * @author Bert Vanderkimpen
674
 *
675
 * @param string $file_name Name of the file
676
 * @param string $file_type Type of the file
677
 *
678
 * @return string File name
679
 */
680
function add_ext_on_mime($file_name, $file_type)
681
{
682
    // Check whether the file has an extension AND whether the browser has sent a MIME Type
683
684
    if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
685
        // Build a "MIME-types / extensions" connection table
686
        static $mime_type = [];
687
688
        $mime_type[] = 'application/msword';
689
        $extension[] = '.doc';
690
        $mime_type[] = 'application/rtf';
691
        $extension[] = '.rtf';
692
        $mime_type[] = 'application/vnd.ms-powerpoint';
693
        $extension[] = '.ppt';
694
        $mime_type[] = 'application/vnd.ms-excel';
695
        $extension[] = '.xls';
696
        $mime_type[] = 'application/pdf';
697
        $extension[] = '.pdf';
698
        $mime_type[] = 'application/postscript';
699
        $extension[] = '.ps';
700
        $mime_type[] = 'application/mac-binhex40';
701
        $extension[] = '.hqx';
702
        $mime_type[] = 'application/x-gzip';
703
        $extension[] = 'tar.gz';
704
        $mime_type[] = 'application/x-shockwave-flash';
705
        $extension[] = '.swf';
706
        $mime_type[] = 'application/x-stuffit';
707
        $extension[] = '.sit';
708
        $mime_type[] = 'application/x-tar';
709
        $extension[] = '.tar';
710
        $mime_type[] = 'application/zip';
711
        $extension[] = '.zip';
712
        $mime_type[] = 'application/x-tar';
713
        $extension[] = '.tar';
714
        $mime_type[] = 'text/html';
715
        $extension[] = '.html';
716
        $mime_type[] = 'text/plain';
717
        $extension[] = '.txt';
718
        $mime_type[] = 'text/rtf';
719
        $extension[] = '.rtf';
720
        $mime_type[] = 'img/gif';
721
        $extension[] = '.gif';
722
        $mime_type[] = 'img/jpeg';
723
        $extension[] = '.jpg';
724
        $mime_type[] = 'img/png';
725
        $extension[] = '.png';
726
        $mime_type[] = 'audio/midi';
727
        $extension[] = '.mid';
728
        $mime_type[] = 'audio/mpeg';
729
        $extension[] = '.mp3';
730
        $mime_type[] = 'audio/x-aiff';
731
        $extension[] = '.aif';
732
        $mime_type[] = 'audio/x-pn-realaudio';
733
        $extension[] = '.rm';
734
        $mime_type[] = 'audio/x-pn-realaudio-plugin';
735
        $extension[] = '.rpm';
736
        $mime_type[] = 'audio/x-wav';
737
        $extension[] = '.wav';
738
        $mime_type[] = 'video/mpeg';
739
        $extension[] = '.mpg';
740
        $mime_type[] = 'video/mpeg4-generic';
741
        $extension[] = '.mp4';
742
        $mime_type[] = 'video/quicktime';
743
        $extension[] = '.mov';
744
        $mime_type[] = 'video/x-msvideo';
745
        $extension[] = '.avi';
746
747
        $mime_type[] = 'video/x-ms-wmv';
748
        $extension[] = '.wmv';
749
        $mime_type[] = 'video/x-flv';
750
        $extension[] = '.flv';
751
        $mime_type[] = 'image/svg+xml';
752
        $extension[] = '.svg';
753
        $mime_type[] = 'image/svg+xml';
754
        $extension[] = '.svgz';
755
        $mime_type[] = 'video/ogg';
756
        $extension[] = '.ogv';
757
        $mime_type[] = 'audio/ogg';
758
        $extension[] = '.oga';
759
        $mime_type[] = 'application/ogg';
760
        $extension[] = '.ogg';
761
        $mime_type[] = 'application/ogg';
762
        $extension[] = '.ogx';
763
        $mime_type[] = 'application/x-freemind';
764
        $extension[] = '.mm';
765
766
        $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12';
767
        $extension[] = '.docm';
768
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
769
        $extension[] = '.docx';
770
        $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12';
771
        $extension[] = '.dotm';
772
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template';
773
        $extension[] = '.dotx';
774
        $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12';
775
        $extension[] = '.potm';
776
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template';
777
        $extension[] = '.potx';
778
        $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12';
779
        $extension[] = '.ppam';
780
        $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12';
781
        $extension[] = '.ppsm';
782
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow';
783
        $extension[] = '.ppsx';
784
        $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12';
785
        $extension[] = '.pptm';
786
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
787
        $extension[] = '.pptx';
788
        $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12';
789
        $extension[] = '.xlam';
790
        $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12';
791
        $extension[] = '.xlsb';
792
        $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12';
793
        $extension[] = '.xlsm';
794
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
795
        $extension[] = '.xlsx';
796
        $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12';
797
        $extension[] = '.xltm';
798
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template';
799
        $extension[] = '.xltx';
800
801
        // Test on PC (files with no extension get application/octet-stream)
802
        //$mime_type[] = 'application/octet-stream';      $extension[] = '.ext';
803
        // Check whether the MIME type sent by the browser is within the table
804
        foreach ($mime_type as $key => &$type) {
805
            if ($type == $file_type) {
806
                $file_name .= $extension[$key];
807
                break;
808
            }
809
        }
810
811
        unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
812
    }
813
814
    return $file_name;
815
}
816
817
/**
818
 * Manages all the unzipping process of an uploaded file.
819
 *
820
 * @author Hugues Peeters <[email protected]>
821
 *
822
 * @param array  $uploaded_file    - follows the $_FILES Structure
823
 * @param string $upload_path      - destination of the upload.
824
 *                                 This path is to append to $base_work_dir
825
 * @param string $base_work_dir    - base working directory of the module
826
 * @param int    $max_filled_space - amount of bytes to not exceed in the base
827
 *                                 working directory
828
 *
829
 * @return bool true if it succeeds false otherwise
830
 */
831
function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space)
832
{
833
    $zip_file = new PclZip($uploaded_file['tmp_name']);
834
835
    // Check the zip content (real size and file extension)
836
    if (file_exists($uploaded_file['tmp_name'])) {
837
        $zip_content_array = $zip_file->listContent();
838
        $ok_scorm = false;
839
        $realFileSize = 0;
840
        foreach ($zip_content_array as &$this_content) {
841
            if (preg_match('~.(php.*|phtml)$~i', $this_content['filename'])) {
842
                Display::addFlash(
843
                    Display::return_message(get_lang('ZipNoPhp'))
844
                );
845
846
                return false;
847
            } elseif (stristr($this_content['filename'], 'imsmanifest.xml')) {
848
                $ok_scorm = true;
849
            } elseif (stristr($this_content['filename'], 'LMS')) {
850
                $ok_plantyn_scorm1 = true;
851
            } elseif (stristr($this_content['filename'], 'REF')) {
852
                $ok_plantyn_scorm2 = true;
853
            } elseif (stristr($this_content['filename'], 'SCO')) {
854
                $ok_plantyn_scorm3 = true;
855
            } elseif (stristr($this_content['filename'], 'AICC')) {
856
                $ok_aicc_scorm = true;
857
            }
858
            $realFileSize += $this_content['size'];
859
        }
860
861
        if (($ok_plantyn_scorm1 && $ok_plantyn_scorm2 && $ok_plantyn_scorm3) || $ok_aicc_scorm) {
862
            $ok_scorm = true;
863
        }
864
865
        if (!$ok_scorm && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) {
866
            Display::addFlash(
867
                Display::return_message(get_lang('NotScormContent'))
868
            );
869
870
            return false;
871
        }
872
873
        if (!enough_size($realFileSize, $base_work_dir, $max_filled_space)) {
874
            Display::addFlash(
875
                Display::return_message(get_lang('NoSpace'))
876
            );
877
878
            return false;
879
        }
880
881
        // It happens on Linux that $upload_path sometimes doesn't start with '/'
882
        if ($upload_path[0] != '/' && substr($base_work_dir, -1, 1) != '/') {
883
            $upload_path = '/'.$upload_path;
884
        }
885
886
        if ($upload_path[strlen($upload_path) - 1] == '/') {
887
            $upload_path = substr($upload_path, 0, -1);
888
        }
889
890
        /*	Uncompressing phase */
891
892
        /*
893
            The first version, using OS unzip, is not used anymore
894
            because it does not return enough information.
895
            We need to process each individual file in the zip archive to
896
            - add it to the database
897
            - parse & change relative html links
898
        */
899
        if (PHP_OS == 'Linux' && !get_cfg_var('safe_mode') && false) { // *** UGent, changed by OC ***
900
            // Shell Method - if this is possible, it gains some speed
901
            exec("unzip -d \"".$base_work_dir.$upload_path."/\"".$uploaded_file['name']." ".$uploaded_file['tmp_name']);
902
        } else {
903
            // PHP method - slower...
904
            $save_dir = getcwd();
905
            chdir($base_work_dir.$upload_path);
906
            $unzippingState = $zip_file->extract();
907
            for ($j = 0; $j < count($unzippingState); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
908
                $state = $unzippingState[$j];
909
910
                // Fix relative links in html files
911
                $extension = strrchr($state['stored_filename'], '.');
912
            }
913
            if ($dir = @opendir($base_work_dir.$upload_path)) {
914
                while ($file = readdir($dir)) {
915
                    if ($file != '.' && $file != '..') {
916
                        $filetype = 'file';
917
                        if (is_dir($base_work_dir.$upload_path.'/'.$file)) {
918
                            $filetype = 'folder';
919
                        }
920
921
                        $safe_file = api_replace_dangerous_char($file);
922
                        @rename($base_work_dir.$upload_path.'/'.$file, $base_work_dir.$upload_path.'/'.$safe_file);
923
                        set_default_settings($upload_path, $safe_file, $filetype);
924
                    }
925
                }
926
927
                closedir($dir);
928
            } else {
929
                error_log('Could not create directory '.$base_work_dir.$upload_path.' to unzip files');
930
            }
931
            chdir($save_dir); // Back to previous dir position
932
        }
933
    }
934
935
    return true;
936
}
937
938
/**
939
 * Manages all the unzipping process of an uploaded document
940
 * This uses the item_property table for properties of documents.
941
 *
942
 * @author Hugues Peeters <[email protected]>
943
 * @author Bert Vanderkimpen
944
 *
945
 * @param array  $courseInfo
946
 * @param array  $userInfo
947
 * @param array  $uploaded_file    - follows the $_FILES Structure
948
 * @param string $uploadPath       - destination of the upload.
949
 *                                 This path is to append to $base_work_dir
950
 * @param string $base_work_dir    - base working directory of the module
951
 * @param int    $maxFilledSpace   - amount of bytes to not exceed in the base
952
 *                                 working directory
953
 * @param int    $sessionId
954
 * @param int    $groupId          group.id
955
 * @param bool   $output           Optional. If no output not wanted on success, set to false.
956
 * @param bool   $onlyUploadFile
957
 * @param string $whatIfFileExists (only works if $onlyUploadFile is false)
958
 *
959
 * @return bool true if it succeeds false otherwise
960
 */
961
function unzip_uploaded_document(
962
    $courseInfo,
963
    $userInfo,
964
    $uploaded_file,
965
    $uploadPath,
966
    $base_work_dir,
967
    $maxFilledSpace,
968
    $sessionId = 0,
969
    $groupId = 0,
970
    $output = true,
971
    $onlyUploadFile = false,
972
    $whatIfFileExists = 'overwrite'
973
) {
974
    $zip = new PclZip($uploaded_file['tmp_name']);
975
976
    // Check the zip content (real size and file extension)
977
    $zip_content_array = (array) $zip->listContent();
978
    $realSize = 0;
979
    foreach ($zip_content_array as &$this_content) {
980
        $realSize += $this_content['size'];
981
    }
982
983
    if (!DocumentManager::enough_space($realSize, $maxFilledSpace)) {
984
        echo Display::return_message(get_lang('UplNotEnoughSpace'), 'error');
985
986
        return false;
987
    }
988
989
    $folder = api_get_unique_id();
990
    $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
991
    mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
992
993
    // Uncompress zip file
994
    // We extract using a callback function that "cleans" the path
995
    $zip->extract(
996
        PCLZIP_OPT_PATH,
997
        $destinationDir,
998
        PCLZIP_CB_PRE_EXTRACT,
999
        'clean_up_files_in_zip',
1000
        PCLZIP_OPT_REPLACE_NEWER
1001
    );
1002
1003
    if ($onlyUploadFile === false) {
1004
        // Add all documents in the unzipped folder to the database
1005
        add_all_documents_in_folder_to_database(
1006
            $courseInfo,
1007
            $userInfo,
1008
            $base_work_dir,
1009
            $destinationDir,
1010
            $sessionId,
1011
            $groupId,
1012
            $output,
1013
            ['path' => $uploadPath],
1014
            $whatIfFileExists
1015
        );
1016
    } else {
1017
        // Copy result
1018
        $fs = new \Symfony\Component\Filesystem\Filesystem();
1019
        $fs->mirror($destinationDir, $base_work_dir.$uploadPath, null, ['overwrite']);
1020
    }
1021
1022
    if (is_dir($destinationDir)) {
1023
        rmdirr($destinationDir);
1024
    }
1025
1026
    return true;
1027
}
1028
1029
/**
1030
 * This function is a callback function that is used while extracting a zipfile
1031
 * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract.
1032
 *
1033
 * @param array $p_event
1034
 * @param array $p_header
1035
 *
1036
 * @return int (If the function returns 1, then the extraction is resumed, if 0 the path was skipped)
1037
 */
1038
function clean_up_files_in_zip($p_event, &$p_header)
1039
{
1040
    $originalStoredFileName = $p_header['stored_filename'];
1041
    $baseName = basename($originalStoredFileName);
1042
    // Skip files
1043
    $skipFiles = [
1044
        '__MACOSX',
1045
        '.Thumbs.db',
1046
        'Thumbs.db',
1047
    ];
1048
1049
    if (in_array($baseName, $skipFiles)) {
1050
        return 0;
1051
    }
1052
    $modifiedStoredFileName = clean_up_path($originalStoredFileName);
1053
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
1054
1055
    return 1;
1056
}
1057
1058
/**
1059
 * Allow .htaccess file
1060
 *
1061
 * @param $p_event
1062
 * @param $p_header
1063
 *
1064
 * @return int
1065
 */
1066
function cleanZipFilesAllowHtaccess($p_event, &$p_header)
1067
{
1068
    $originalStoredFileName = $p_header['stored_filename'];
1069
    $baseName = basename($originalStoredFileName);
1070
1071
    $allowFiles = ['.htaccess'];
1072
    if (in_array($baseName, $allowFiles)) {
1073
        return 1;
1074
    }
1075
1076
    // Skip files
1077
    $skipFiles = [
1078
        '__MACOSX',
1079
        '.Thumbs.db',
1080
        'Thumbs.db'
1081
    ];
1082
1083
    if (in_array($baseName, $skipFiles)) {
1084
        return 0;
1085
    }
1086
    $modifiedStoredFileName = clean_up_path($originalStoredFileName);
1087
    $p_header['filename'] = str_replace($originalStoredFileName, $modifiedStoredFileName, $p_header['filename']);
1088
1089
    return 1;
1090
}
1091
1092
1093
/**
1094
 * This function cleans up a given path
1095
 * by eliminating dangerous file names and cleaning them.
1096
 *
1097
 * @param string $path
1098
 *
1099
 * @return string
1100
 *
1101
 * @see disable_dangerous_file()
1102
 * @see api_replace_dangerous_char()
1103
 */
1104
function clean_up_path($path)
1105
{
1106
    // Split the path in folders and files
1107
    $path_array = explode('/', $path);
1108
    // Clean up every folder and filename in the path
1109
    foreach ($path_array as $key => &$val) {
1110
        // We don't want to lose the dots in ././folder/file (cfr. zipfile)
1111
        if ($val != '.') {
1112
            $val = disable_dangerous_file(api_replace_dangerous_char($val));
1113
        }
1114
    }
1115
    // Join the "cleaned" path (modified in-place as passed by reference)
1116
    $path = implode('/', $path_array);
1117
    filter_extension($path);
1118
1119
    return $path;
1120
}
1121
1122
/**
1123
 * Checks if the file is dangerous, based on extension and/or mimetype.
1124
 * The list of extensions accepted/rejected can be found from
1125
 * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include').
1126
 *
1127
 * @param string $filename passed by reference. The filename will be modified
1128
 *                         if filter rules say so! (you can include path but the filename should look like 'abc.html')
1129
 *
1130
 * @return int 0 to skip file, 1 to keep file
1131
 */
1132
function filter_extension(&$filename)
1133
{
1134
    if (substr($filename, -1) == '/') {
1135
        return 1; // Authorize directories
1136
    }
1137
    $blacklist = api_get_setting('upload_extensions_list_type');
1138
    if ($blacklist != 'whitelist') { // if = blacklist
1139
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_blacklist')));
1140
1141
        $skip = api_get_setting('upload_extensions_skip');
1142
        $ext = strrchr($filename, '.');
1143
        $ext = substr($ext, 1);
1144
        if (empty($ext)) {
1145
            return 1; // We're in blacklist mode, so accept empty extensions
1146
        }
1147
        if (in_array(strtolower($ext), $extensions)) {
1148
            if ($skip == 'true') {
1149
                return 0;
1150
            } else {
1151
                $new_ext = api_get_setting('upload_extensions_replace_by');
1152
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
1153
1154
                return 1;
1155
            }
1156
        } else {
1157
            return 1;
1158
        }
1159
    } else {
1160
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_whitelist')));
1161
        $skip = api_get_setting('upload_extensions_skip');
1162
        $ext = strrchr($filename, '.');
1163
        $ext = substr($ext, 1);
1164
        if (empty($ext)) {
1165
            return 1; // Accept empty extensions
1166
        }
1167
        if (!in_array(strtolower($ext), $extensions)) {
1168
            if ($skip == 'true') {
1169
                return 0;
1170
            } else {
1171
                $new_ext = api_get_setting('upload_extensions_replace_by');
1172
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
1173
1174
                return 1;
1175
            }
1176
        } else {
1177
            return 1;
1178
        }
1179
    }
1180
}
1181
1182
/**
1183
 * Updates an existing document in the database
1184
 * as the file exists, we only need to change the size.
1185
 *
1186
 * @param array $_course
1187
 * @param int   $documentId
1188
 * @param int   $filesize
1189
 * @param int   $readonly
1190
 *
1191
 * @return bool true /false
1192
 */
1193
function update_existing_document($_course, $documentId, $filesize, $readonly = 0)
1194
{
1195
    $document_table = Database::get_course_table(TABLE_DOCUMENT);
1196
    $documentId = intval($documentId);
1197
    $filesize = intval($filesize);
1198
    $readonly = intval($readonly);
1199
    $course_id = $_course['real_id'];
1200
1201
    $sql = "UPDATE $document_table SET
1202
            size = '$filesize',
1203
            readonly = '$readonly'
1204
			WHERE c_id = $course_id AND id = $documentId";
1205
    if (Database::query($sql)) {
1206
        return true;
1207
    } else {
1208
        return false;
1209
    }
1210
}
1211
1212
/**
1213
 * This function updates the last_edit_date, last edit user id on all folders in a given path.
1214
 *
1215
 * @param array  $_course
1216
 * @param string $path
1217
 * @param int    $user_id
1218
 */
1219
function item_property_update_on_folder($_course, $path, $user_id)
1220
{
1221
    // If we are in the root, just return... no need to update anything
1222
    if ($path == '/') {
1223
        return;
1224
    }
1225
1226
    $user_id = intval($user_id);
1227
1228
    // If the given path ends with a / we remove it
1229
    $endchar = substr($path, strlen($path) - 1, 1);
1230
    if ($endchar == '/') {
1231
        $path = substr($path, 0, strlen($path) - 1);
1232
    }
1233
1234
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
1235
1236
    // Get the time
1237
    $time = api_get_utc_datetime();
1238
1239
    // Det all paths in the given path
1240
    // /folder/subfolder/subsubfolder/file
1241
    // if file is updated, subsubfolder, subfolder and folder are updated
1242
    $exploded_path = explode('/', $path);
1243
    $course_id = api_get_course_int_id();
1244
    $newpath = '';
1245
    foreach ($exploded_path as $key => &$value) {
1246
        // We don't want a slash before our first slash
1247
        if ($key != 0) {
1248
            $newpath .= '/'.$value;
1249
            // Select ID of given folder
1250
            $folder_id = DocumentManager::get_document_id($_course, $newpath);
1251
1252
            if ($folder_id) {
1253
                $sql = "UPDATE $table SET
1254
				        lastedit_date = '$time',
1255
				        lastedit_type = 'DocumentInFolderUpdated', 
1256
				        lastedit_user_id='$user_id'
1257
						WHERE 
1258
						    c_id = $course_id AND 
1259
						    tool='".TOOL_DOCUMENT."' AND 
1260
						    ref = '$folder_id'";
1261
                Database::query($sql);
1262
            }
1263
        }
1264
    }
1265
}
1266
1267
/**
1268
 * Adds file to document table in database
1269
 * deprecated: use file_set_default_settings instead.
1270
 *
1271
 * @author	Olivier Cauberghe <[email protected]>
1272
 *
1273
 * @param	path,filename
1274
 * action:	Adds an entry to the document table with the default settings
1275
 */
1276
function set_default_settings($upload_path, $filename, $filetype = 'file')
1277
{
1278
    $dbTable = Database::get_course_table(TABLE_DOCUMENT);
1279
    global $default_visibility;
1280
1281
    if (!$default_visibility) {
1282
        $default_visibility = 'v';
1283
    }
1284
    $filetype = Database::escape_string($filetype);
1285
1286
    $upload_path = str_replace('\\', '/', $upload_path);
1287
    $upload_path = str_replace('//', '/', $upload_path);
1288
1289
    if ($upload_path == '/') {
1290
        $upload_path = '';
1291
    } elseif (!empty($upload_path) && $upload_path[0] != '/') {
1292
        $upload_path = "/$upload_path";
1293
    }
1294
1295
    $endchar = substr($filename, strlen($filename) - 1, 1);
1296
1297
    if ($endchar == '/') {
1298
        $filename = substr($filename, 0, -1);
1299
    }
1300
    $filename = Database::escape_string($filename);
1301
    $query = "SELECT count(*) as bestaat FROM $dbTable
1302
              WHERE path='$upload_path/$filename'";
1303
    $result = Database::query($query);
1304
    $row = Database::fetch_array($result);
1305
    if ($row['bestaat'] > 0) {
1306
        $query = "UPDATE $dbTable SET
1307
		            path='$upload_path/$filename',
1308
		            visibility='$default_visibility',
1309
		            filetype='$filetype'
1310
		          WHERE path='$upload_path/$filename'";
1311
    } else {
1312
        $query = "INSERT INTO $dbTable (path,visibility,filetype)
1313
		          VALUES('$upload_path/$filename','$default_visibility','$filetype')";
1314
    }
1315
    Database::query($query);
1316
}
1317
1318
/**
1319
 * Retrieves the image path list in a html file.
1320
 *
1321
 * @author Hugues Peeters <[email protected]>
1322
 *
1323
 * @param string $html_file
1324
 *
1325
 * @return array -  images path list
1326
 */
1327
function search_img_from_html($html_file)
1328
{
1329
    $img_path_list = [];
1330
1331
    if (!$fp = fopen($html_file, 'r')) {
1332
        return;
1333
    }
1334
1335
    // Aearch and store occurences of the <img> tag in an array
1336
    $size_file = (filesize($html_file) === 0) ? 1 : filesize($html_file);
1337
    if (isset($fp) && $fp !== false) {
1338
        $buffer = fread($fp, $size_file);
1339
        if (strlen($buffer) >= 0 && $buffer !== false) {
1340
        } else {
1341
            die('<center>Can not read file.</center>');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1342
        }
1343
    } else {
1344
        die('<center>Can not read file.</center>');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1345
    }
1346
    $matches = [];
1347
    if (preg_match_all('~<[[:space:]]*img[^>]*>~i', $buffer, $matches)) {
1348
        $img_tag_list = $matches[0];
1349
    }
1350
1351
    fclose($fp);
1352
    unset($buffer);
1353
1354
    // Search the image file path from all the <IMG> tag detected
1355
1356
    if (sizeof($img_tag_list) > 0) {
1357
        foreach ($img_tag_list as &$this_img_tag) {
1358
            if (preg_match('~src[[:space:]]*=[[:space:]]*[\"]{1}([^\"]+)[\"]{1}~i', $this_img_tag, $matches)) {
1359
                $img_path_list[] = $matches[1];
1360
            }
1361
        }
1362
        $img_path_list = array_unique($img_path_list); // Remove duplicate entries
1363
    }
1364
1365
    return $img_path_list;
1366
}
1367
1368
/**
1369
 * Creates a new directory trying to find a directory name
1370
 * that doesn't already exist.
1371
 *
1372
 * @author  Hugues Peeters <[email protected]>
1373
 * @author  Bert Vanderkimpen
1374
 *
1375
 * @param array  $_course                 current course information
1376
 * @param int    $user_id                 current user id
1377
 * @param int    $session_id
1378
 * @param int    $to_group_id             group.id
1379
 * @param int    $to_user_id
1380
 * @param string $base_work_dir           /var/www/chamilo/courses/ABC/document
1381
 * @param string $desired_dir_name        complete path of the desired name
1382
 *                                        Example: /folder1/folder2
1383
 * @param string $title                   "folder2"
1384
 * @param int    $visibility              (0 for invisible, 1 for visible, 2 for deleted)
1385
 * @param bool   $generateNewNameIfExists
1386
 * @param bool   $sendNotification        depends in conf setting "send_notification_when_document_added"
1387
 *
1388
 * @return CDocument|false
1389
 */
1390
function create_unexisting_directory(
1391
    $_course,
1392
    $user_id,
1393
    $session_id,
1394
    $to_group_id,
1395
    $to_user_id,
1396
    $base_work_dir,
1397
    $desired_dir_name,
1398
    $title = '',
1399
    $visibility = '',
1400
    $generateNewNameIfExists = false,
1401
    $sendNotification = true
1402
) {
1403
    $course_id = $_course['real_id'];
1404
    $session_id = (int) $session_id;
1405
1406
    $folderExists = DocumentManager::folderExists(
1407
        $desired_dir_name,
1408
        $_course,
1409
        $session_id,
1410
        $to_group_id
1411
    );
1412
1413
    if ($folderExists === true) {
1414
        if ($generateNewNameIfExists) {
1415
            $counter = 1;
1416
            while (1) {
1417
                $folderExists = DocumentManager::folderExists(
1418
                    $desired_dir_name.'_'.$counter,
1419
                    $_course,
1420
                    $session_id,
1421
                    $to_group_id
1422
                );
1423
1424
                if ($folderExists === false) {
1425
                    break;
1426
                }
1427
                $counter++;
1428
            }
1429
            $desired_dir_name = $desired_dir_name.'_'.$counter;
1430
        }
1431
    }
1432
1433
    $systemFolderName = $desired_dir_name;
1434
1435
    // Adding suffix
1436
    $suffix = DocumentManager::getDocumentSuffix(
1437
        $_course,
1438
        $session_id,
1439
        $to_group_id
1440
    );
1441
1442
    $systemFolderName .= $suffix;
1443
1444
    if ($title == null) {
1445
        $title = basename($desired_dir_name);
1446
    }
1447
1448
    // Check if pathname already exists inside document table
1449
    $table = Database::get_course_table(TABLE_DOCUMENT);
1450
    $sql = "SELECT id, path FROM $table
1451
            WHERE
1452
                c_id = $course_id AND
1453
                path = '".Database::escape_string($systemFolderName)."'";
1454
    $rs = Database::query($sql);
1455
    if (Database::num_rows($rs) == 0) {
1456
        $document = DocumentManager::addDocument(
1457
            $_course,
1458
            $systemFolderName,
1459
            'folder',
1460
            0,
1461
            $title,
1462
            null,
1463
            0,
1464
            true,
1465
            $to_group_id,
1466
            $session_id,
1467
            $user_id,
1468
            $sendNotification
1469
        );
1470
1471
        if ($document) {
1472
            return $document;
1473
        }
1474
    } else {
1475
        $document = Database::fetch_array($rs);
1476
        $documentData = DocumentManager::get_document_data_by_id(
1477
            $document['id'],
1478
            $_course['code'],
1479
            false,
1480
            $session_id
1481
        );
1482
1483
        $em = Database::getManager();
1484
        $document = $em->getRepository('ChamiloCourseBundle:CDocument')->find($documentData['iid']);
1485
1486
        return $document;
1487
    }
1488
1489
    return false;
1490
}
1491
1492
/**
1493
 * Handles uploaded missing images.
1494
 *
1495
 * @author Hugues Peeters <[email protected]>
1496
 * @author Bert Vanderkimpen
1497
 *
1498
 * @param array  $_course
1499
 * @param array  $uploaded_file_collection - follows the $_FILES Structure
1500
 * @param string $base_work_dir
1501
 * @param string $missing_files_dir
1502
 * @param int    $user_id
1503
 * @param int    $to_group_id              group.id
1504
 */
1505
function move_uploaded_file_collection_into_directory(
1506
    $_course,
1507
    $uploaded_file_collection,
1508
    $base_work_dir,
1509
    $missing_files_dir,
1510
    $user_id,
1511
    $to_group_id,
1512
    $to_user_id,
1513
    $max_filled_space
1514
) {
1515
    $number_of_uploaded_images = count($uploaded_file_collection['name']);
1516
    $list = [];
1517
    for ($i = 0; $i < $number_of_uploaded_images; $i++) {
1518
        $missing_file['name'] = $uploaded_file_collection['name'][$i];
1519
        $missing_file['type'] = $uploaded_file_collection['type'][$i];
1520
        $missing_file['tmp_name'] = $uploaded_file_collection['tmp_name'][$i];
1521
        $missing_file['error'] = $uploaded_file_collection['error'][$i];
1522
        $missing_file['size'] = $uploaded_file_collection['size'][$i];
1523
1524
        $upload_ok = process_uploaded_file($missing_file);
1525
        if ($upload_ok) {
1526
            $list[] = handle_uploaded_document(
1527
                $_course,
1528
                $missing_file,
1529
                $base_work_dir,
1530
                $missing_files_dir,
1531
                $user_id,
1532
                $to_group_id,
1533
                $to_user_id,
1534
                $max_filled_space,
1535
                0,
1536
                'overwrite'
1537
            );
1538
        }
1539
        unset($missing_file);
1540
    }
1541
1542
    return $list;
1543
}
1544
1545
/**
1546
 * Opens the old html file and replace the src path into the img tag
1547
 * This also works for files in subdirectories.
1548
 *
1549
 * @param $original_img_path is an array
1550
 * @param $new_img_path is an array
1551
 */
1552
function replace_img_path_in_html_file($original_img_path, $new_img_path, $html_file)
1553
{
1554
    // Open the file
1555
    $fp = fopen($html_file, 'r');
1556
    $buffer = fread($fp, filesize($html_file));
1557
    $new_html_content = '';
1558
1559
    // Fix the image tags
1560
    for ($i = 0, $fileNb = count($original_img_path); $i < $fileNb; $i++) {
1561
        $replace_what = $original_img_path[$i];
1562
        // We only need the directory and the filename /path/to/file_html_files/missing_file.gif -> file_html_files/missing_file.gif
1563
        $exploded_file_path = explode('/', $new_img_path[$i]);
1564
        $replace_by = $exploded_file_path[count($exploded_file_path) - 2].'/'.$exploded_file_path[count($exploded_file_path) - 1];
1565
        $buffer = str_replace($replace_what, $replace_by, $buffer);
1566
    }
1567
1568
    $new_html_content .= $buffer;
1569
1570
    @fclose($fp);
1571
1572
    // Write the resulted new file
1573
1574
    if (!$fp = fopen($html_file, 'w')) {
1575
        return;
1576
    }
1577
1578
    if (!fwrite($fp, $new_html_content)) {
1579
        return;
1580
    }
1581
}
1582
1583
/**
1584
 * Checks the extension of a file, if it's .htm or .html
1585
 * we use search_img_from_html to get all image paths in the file.
1586
 *
1587
 * @param string $file
1588
 *
1589
 * @return array paths
1590
 *
1591
 * @see check_for_missing_files() uses search_img_from_html()
1592
 */
1593
function check_for_missing_files($file)
1594
{
1595
    if (strrchr($file, '.') == '.htm' || strrchr($file, '.') == '.html') {
1596
        $img_file_path = search_img_from_html($file);
1597
1598
        return $img_file_path;
1599
    }
1600
1601
    return false;
1602
}
1603
1604
/**
1605
 * This function builds a form that asks for the missing images in a html file
1606
 * maybe we should do this another way?
1607
 *
1608
 * @param array  $missing_files
1609
 * @param string $upload_path
1610
 * @param string $file_name
1611
 *
1612
 * @return string the form
1613
 */
1614
function build_missing_files_form($missing_files, $upload_path, $file_name)
1615
{
1616
    // Do we need a / or not?
1617
    $added_slash = ($upload_path == '/') ? '' : '/';
1618
    $folder_id = DocumentManager::get_document_id(api_get_course_info(), $upload_path);
1619
    // Build the form
1620
    $form = "<p><strong>".get_lang('MissingImagesDetected')."</strong></p>"
1621
        ."<form method=\"post\" action=\"".api_get_self()."\" enctype=\"multipart/form-data\">"
1622
        // Related_file is the path to the file that has missing images
1623
        ."<input type=\"hidden\" name=\"related_file\" value=\"".$upload_path.$added_slash.$file_name."\" />"
1624
        ."<input type=\"hidden\" name=\"upload_path\" value=\"".$upload_path."\" />"
1625
        ."<input type=\"hidden\" name=\"id\" value=\"".$folder_id."\" />"
1626
        ."<table border=\"0\">";
1627
    foreach ($missing_files as &$this_img_file_path) {
1628
        $form .= "<tr>"
1629
            ."<td>".basename($this_img_file_path)." : </td>"
1630
            ."<td>"
1631
            ."<input type=\"file\" name=\"img_file[]\"/>"
1632
            ."<input type=\"hidden\" name=\"img_file_path[]\" value=\"".$this_img_file_path."\" />"
1633
            ."</td>"
1634
            ."</tr>";
1635
    }
1636
    $form .= "</table>"
1637
        ."<button type='submit' name=\"cancel_submit_image\" value=\"".get_lang('Cancel')."\" class=\"cancel\">".get_lang('Cancel')."</button>"
1638
        ."<button type='submit' name=\"submit_image\" value=\"".get_lang('Ok')."\" class=\"save\">".get_lang('Ok')."</button>"
1639
        ."</form>";
1640
1641
    return $form;
1642
}
1643
1644
/**
1645
 * This recursive function can be used during the upgrade process form older
1646
 * versions of Chamilo
1647
 * It crawls the given directory, checks if the file is in the DB and adds
1648
 * it if it's not.
1649
 *
1650
 * @param array  $courseInfo
1651
 * @param array  $userInfo
1652
 * @param string $base_work_dir    course document dir
1653
 * @param string $folderPath       folder to read
1654
 * @param int    $sessionId
1655
 * @param int    $groupId          group.id
1656
 * @param bool   $output
1657
 * @param array  $parent
1658
 * @param string $whatIfFileExists
1659
 *
1660
 * @return bool
1661
 */
1662
function add_all_documents_in_folder_to_database(
1663
    $courseInfo,
1664
    $userInfo,
1665
    $base_work_dir,
1666
    $folderPath,
1667
    $sessionId = 0,
1668
    $groupId = 0,
1669
    $output = false,
1670
    $parent = [],
1671
    $whatIfFileExists = 'overwrite'
1672
) {
1673
    if (empty($userInfo) || empty($courseInfo)) {
1674
        return false;
1675
    }
1676
1677
    $userId = $userInfo['user_id'];
1678
1679
    // Open dir
1680
    $handle = opendir($folderPath);
1681
1682
    if (is_dir($folderPath)) {
1683
        // Run trough
1684
        while ($file = readdir($handle)) {
1685
            if ($file == '.' || $file == '..') {
1686
                continue;
1687
            }
1688
1689
            $parentPath = '';
1690
            if (!empty($parent) && isset($parent['path'])) {
1691
                $parentPath = $parent['path'];
1692
                if ($parentPath == '/') {
1693
                    $parentPath = '';
1694
                }
1695
            }
1696
1697
            $completePath = $parentPath.'/'.$file;
1698
            $sysFolderPath = $folderPath.'/'.$file;
1699
1700
            // Is directory?
1701
            if (is_dir($sysFolderPath)) {
1702
                $folderExists = DocumentManager::folderExists(
1703
                    $completePath,
1704
                    $courseInfo,
1705
                    $sessionId,
1706
                    $groupId
1707
                );
1708
1709
                if ($folderExists === true) {
1710
                    switch ($whatIfFileExists) {
1711
                        case 'overwrite':
1712
                            $documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
1713
                            if ($documentId) {
1714
                                $newFolderData = DocumentManager::get_document_data_by_id(
1715
                                    $documentId,
1716
                                    $courseInfo['code'],
1717
                                    false,
1718
                                    $sessionId
1719
                                );
1720
                            }
1721
                            break;
1722
                        case 'rename':
1723
                            $newFolderData = create_unexisting_directory(
1724
                                $courseInfo,
1725
                                $userId,
1726
                                $sessionId,
1727
                                $groupId,
1728
                                null,
1729
                                $base_work_dir,
1730
                                $completePath,
1731
                                null,
1732
                                null,
1733
                                true
1734
                            );
1735
                            break;
1736
                        case 'nothing':
1737
                            if ($output) {
1738
                                $documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
1739
                                if ($documentId) {
1740
                                    $folderData = DocumentManager::get_document_data_by_id(
1741
                                        $documentId,
1742
                                        $courseInfo['code'],
1743
                                        false,
1744
                                        $sessionId
1745
                                    );
1746
                                    Display::addFlash(
1747
                                        Display::return_message(
1748
                                            $folderData['path'].' '.get_lang('UplAlreadyExists'),
1749
                                            'warning'
1750
                                        )
1751
                                    );
1752
                                }
1753
                            }
1754
                            continue 2;
1755
                            break;
1756
                    }
1757
                } else {
1758
                    $newFolderData = create_unexisting_directory(
1759
                        $courseInfo,
1760
                        $userId,
1761
                        $sessionId,
1762
                        $groupId,
1763
                        null,
1764
                        $base_work_dir,
1765
                        $completePath,
1766
                        null,
1767
                        null,
1768
                        false
1769
                    );
1770
                }
1771
1772
                // Recursive
1773
                add_all_documents_in_folder_to_database(
1774
                    $courseInfo,
1775
                    $userInfo,
1776
                    $base_work_dir,
1777
                    $sysFolderPath,
1778
                    $sessionId,
1779
                    $groupId,
1780
                    $output,
1781
                    $newFolderData,
1782
                    $whatIfFileExists
1783
                );
1784
            } else {
1785
                // Rename
1786
                $uploadedFile = [
1787
                    'name' => $file,
1788
                    'tmp_name' => $sysFolderPath,
1789
                    'size' => filesize($sysFolderPath),
1790
                    'type' => null,
1791
                    'from_file' => true,
1792
                    'move_file' => true,
1793
                ];
1794
1795
                handle_uploaded_document(
1796
                    $courseInfo,
1797
                    $uploadedFile,
1798
                    $base_work_dir,
1799
                    $parentPath,
1800
                    $userId,
1801
                    $groupId,
1802
                    null,
1803
                    0,
1804
                    $whatIfFileExists,
1805
                    $output,
1806
                    false,
1807
                    null,
1808
                    $sessionId
1809
                );
1810
            }
1811
        }
1812
    }
1813
}
1814