Completed
Push — 1.10.x ( 91da24...ebaaef )
by Julito
59:51
created

fileUpload.lib.php ➔ add_all_documents_in_folder_to_database()   D

Complexity

Conditions 13
Paths 16

Size

Total Lines 109
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 13
eloc 77
c 1
b 1
f 0
nc 16
nop 8
dl 0
loc 109
rs 4.9922

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 *	FILE UPLOAD LIBRARY
6
 *
7
 *	This is the file upload library for Chamilo.
8
 *	Include/require it in your code to use its functionality.
9
 *
10
 *	@package chamilo.library
11
 *	@todo test and reorganise
12
 */
13
14
/**
15
 * Changes the file name extension from .php to .phps
16
 * Useful for securing a site.
17
 *
18
 * @author - Hugues Peeters <[email protected]>
19
 * @param  - file_name (string) name of a file
20
 * @return - the filenam phps'ized
0 ignored issues
show
Documentation introduced by
The doc-type - could not be parsed: Unknown type name "-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
21
 */
22
function php2phps($file_name) {
23
    return preg_replace('/\.(php.?|phtml.?)(\.){0,1}.*$/i', '.phps', $file_name);
24
}
25
26
/**
27
 * Renames .htaccess & .HTACCESS to htaccess.txt
28
 *
29
 * @param string $filename
30
 * @return string
31
 */
32
function htaccess2txt($filename) {
33
    return str_replace(array('.htaccess', '.HTACCESS'), array('htaccess.txt', 'htaccess.txt'), $filename);
34
}
35
36
/**
37
 * This function executes our safety precautions
38
 * more functions can be added
39
 *
40
 * @param string $filename
41
 * @return string
42
 * @see php2phps()
43
 * @see htaccess2txt()
44
 */
45
function disable_dangerous_file($filename) {
46
    return htaccess2txt(php2phps($filename));
47
}
48
49
/**
50
 * This function generates a unique name for a file on a given location
51
 * file names are changed to name_#.ext
52
 *
53
 * @param string $path
54
 * @param string $name
55
 *
56
 * @deprecated
57
 *
58
 * @return string new unique name
59
 */
60
function unique_name($path, $name)
61
{
62
    $ext = substr(strrchr($name, '.'), 0);
63
    $name_no_ext = substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
64
    $n = 0;
65
    $unique = '';
66
    while (file_exists($path . $name_no_ext . $unique . $ext)) {
67
        $unique = '_' . ++$n;
68
    }
69
    return $name_no_ext . $unique . $ext;
70
}
71
72
/**
73
 * Returns the name without extension, used for the title
74
 *
75
 * @param string $name
76
 * @return name without the extension
77
 */
78
function get_document_title($name) {
79
    // If they upload .htaccess...
80
    $name = disable_dangerous_file($name);
81
    $ext = substr(strrchr($name, '.'), 0);
82
    return substr($name, 0, strlen($name) - strlen(strstr($name, $ext)));
83
}
84
85
/**
86
 * This function checks if the upload succeeded
87
 *
88
 * @param array $uploaded_file ($_FILES)
89
 * @return true if upload succeeded
90
 */
91
function process_uploaded_file($uploaded_file, $show_output = true)
92
{
93
    // Checking the error code sent with the file upload.
94
    if (isset($uploaded_file['error'])) {
95
        switch ($uploaded_file['error']) {
96
            case 1:
97
                // The uploaded file exceeds the upload_max_filesize directive in php.ini.
98
                if ($show_output) {
99
                    Display::display_error_message(get_lang('UplExceedMaxServerUpload').ini_get('upload_max_filesize'));
100
                }
101
102
                return false;
103
            case 2:
104
                // The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
105
                // Not used at the moment, but could be handy if we want to limit the size of an upload (e.g. image upload in html editor).
106
                $max_file_size = intval($_POST['MAX_FILE_SIZE']);
107
                if ($show_output) {
108
                    Display::display_error_message(get_lang('UplExceedMaxPostSize'). format_file_size($max_file_size));
109
                }
110
111
                return false;
112
            case 3:
113
                // The uploaded file was only partially uploaded.
114
                if ($show_output) {
115
                    Display::display_error_message(get_lang('UplPartialUpload').' '.get_lang('PleaseTryAgain'));
116
                }
117
118
                return false;
119
            case 4:
120
                // No file was uploaded.
121
                if ($show_output) {
122
                    Display::display_error_message(get_lang('UplNoFileUploaded').' '. get_lang('UplSelectFileFirst'));
123
                }
124
125
                return false;
126
        }
127
    }
128
129
    if (!file_exists($uploaded_file['tmp_name'])) {
130
        // No file was uploaded.
131
        if ($show_output) {
132
            Display::display_error_message(get_lang('UplUploadFailed'));
133
        }
134
        return false;
135
    }
136
137
    if (file_exists($uploaded_file['tmp_name'])) {
138
        $filesize = filesize($uploaded_file['tmp_name']);
139
        if (empty($filesize)) {
140
            // No file was uploaded.
141
            if ($show_output) {
142
                Display::display_error_message(get_lang('UplUploadFailedSizeIsZero'));
143
            }
144
145
            return false;
146
        }
147
    }
148
149
    $course_id = api_get_course_id();
150
    //Checking course quota if we are in a course
151
152
    if (!empty($course_id)) {
153
        $max_filled_space = DocumentManager::get_course_quota();
154
        // Check if there is enough space to save the file
155
        if (!DocumentManager::enough_space($uploaded_file['size'], $max_filled_space)) {
156
            if ($show_output) {
157
                Display::display_error_message(get_lang('UplNotEnoughSpace'));
158
            }
159
160
            return false;
161
        }
162
    }
163
164
    // case 0: default: We assume there is no error, the file uploaded with success.
165
    return true;
166
}
167
168
/**
169
 * This function does the save-work for the documents.
170
 * It handles the uploaded file and adds the properties to the database
171
 * If unzip=1 and the file is a zipfile, it is extracted
172
 * If we decide to save ALL kinds of documents in one database,
173
 * we could extend this with a $type='document', 'scormdocument',...
174
 *
175
 * @param array $courseInfo
176
 * @param array $uploadedFile ($_FILES)
177
 * array(
178
 *  'name' => 'picture.jpg',
179
 *  'tmp_name' => '...', // absolute path
180
 * );
181
 * @param string $documentDir Example: /var/www/chamilo/courses/ABC/document
182
 * @param string $uploadPath Example: /folder1/folder2/
183
 * @param int $userId
184
 * @param int $groupId, 0 for everybody
0 ignored issues
show
Documentation introduced by
There is no parameter named $groupId,. Did you maybe mean $groupId?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
185
 * @param int $toUserId, NULL for everybody
0 ignored issues
show
Documentation introduced by
There is no parameter named $toUserId,. Did you maybe mean $userId?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
186
 * @param int $unzip 1/0
187
 * @param string $whatIfFileExists overwrite, rename or warn if exists (default)
188
 * @param boolean $output Optional output parameter.
189
 * @param bool $onlyUploadFile
190
 * @param string $comment
191
 * @param int $sessionId
192
 *
193
 * So far only use for unzip_uploaded_document function.
194
 * If no output wanted on success, set to false.
195
 * @param string $comment
196
 * @return string path of the saved file
197
 */
198
function handle_uploaded_document(
199
    $courseInfo,
200
    $uploadedFile,
201
    $documentDir,
202
    $uploadPath,
203
    $userId,
204
    $groupId = 0,
205
    $toUserId = null,
206
    $unzip = 0,
207
    $whatIfFileExists = '',
208
    $output = true,
209
    $onlyUploadFile = false,
210
    $comment = null,
211
    $sessionId = null
212
) {
213
    if (!$userId) {
214
        return false;
215
    }
216
217
    $userInfo = api_get_user_info();
218
219
    $uploadedFile['name'] = stripslashes($uploadedFile['name']);
220
    // Add extension to files without one (if possible)
221
    $uploadedFile['name'] = add_ext_on_mime($uploadedFile['name'], $uploadedFile['type']);
222
223
    if (empty($sessionId)) {
224
        $sessionId = api_get_session_id();
225
    } else {
226
        $sessionId = intval($sessionId);
227
    }
228
229
    // Just in case process_uploaded_file is not called
230
    $maxSpace = DocumentManager::get_course_quota();
231
232
    // Check if there is enough space to save the file
233 View Code Duplication
    if (!DocumentManager::enough_space($uploadedFile['size'], $maxSpace)) {
234
        if ($output) {
235
            Display::addFlash(Display::return_message(get_lang('UplNotEnoughSpace'), 'error'));
236
        }
237
238
        return false;
239
    }
240
241
    // If the want to unzip, check if the file has a .zip (or ZIP,Zip,ZiP,...) extension
242
    if ($unzip == 1 && preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
243
        return unzip_uploaded_document(
244
            $courseInfo,
245
            $userInfo,
0 ignored issues
show
Security Bug introduced by
It seems like $userInfo defined by api_get_user_info() on line 217 can also be of type false; however, unzip_uploaded_document() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
246
            $uploadedFile,
247
            $uploadPath,
248
            $documentDir,
249
            $maxSpace,
250
            $sessionId,
251
            $groupId,
252
            $output
253
        );
254
    } elseif ($unzip == 1 && !preg_match('/.zip$/', strtolower($uploadedFile['name']))) {
255
        // We can only unzip ZIP files (no gz, tar,...)
256
        if ($output) {
257
            Display::addFlash(
258
                Display::return_message(get_lang('UplNotAZip')." ".get_lang('PleaseTryAgain'), 'error')
259
            );
260
        }
261
262
        return false;
263
    } else {
264
        // Clean up the name, only ASCII characters should stay. (and strict)
265
        $cleanName = api_replace_dangerous_char($uploadedFile['name'], 'strict');
266
267
        // No "dangerous" files
268
        $cleanName = disable_dangerous_file($cleanName);
269
270
        // Checking file extension
271
        if (!filter_extension($cleanName)) {
272
            if ($output) {
273
                Display::addFlash(
274
                    Display::return_message(get_lang('UplUnableToSaveFileFilteredExtension'), 'error')
275
                );
276
            }
277
278
            return false;
279
        } else {
280
281
            // If the upload path differs from / (= root) it will need a slash at the end
282
            if ($uploadPath != '/') {
283
                $uploadPath = $uploadPath.'/';
284
            }
285
286
            // Full path to where we want to store the file with trailing slash
287
            $whereToSave = $documentDir.$uploadPath;
288
289
            // At least if the directory doesn't exist, tell so
290
            if (!is_dir($whereToSave)) {
291 View Code Duplication
                if (!mkdir($whereToSave, api_get_permissions_for_new_directories())) {
292
                    if ($output) {
293
                        Display::addFlash(
294
                            Display::return_message(get_lang('DestDirectoryDoesntExist').' ('.$uploadPath.')', 'error')
295
                        );
296
                    }
297
298
                    return false;
299
                }
300
            }
301
302
            // Just upload the file "as is"
303
            if ($onlyUploadFile) {
304
                $errorResult = moveUploadedFile($uploadedFile, $whereToSave.$cleanName);
305
                if ($errorResult) {
306
307
                    return $whereToSave.$cleanName;
308
                } else {
309
310
                    return $errorResult;
311
                }
312
            }
313
314
            /*
315
                Based in the clean name we generate a new filesystem name
316
                Using the session_id and group_id if values are not empty
317
            */
318
319
            /*$fileExists = DocumentManager::documentExists(
320
                $uploadPath.$cleanName,
321
                $courseInfo,
322
                $sessionId,
323
                $groupId
324
            );*/
325
326
            $fileSystemName = DocumentManager::fixDocumentName(
327
                $cleanName,
328
                'file',
329
                $courseInfo,
330
                $sessionId,
331
                $groupId
332
            );
333
334
            // Name of the document without the extension (for the title)
335
            $documentTitle = get_document_title($uploadedFile['name']);
336
337
            // Size of the uploaded file (in bytes)
338
            $fileSize = $uploadedFile['size'];
339
340
            // File permissions
341
            $filePermissions = api_get_permissions_for_new_files();
342
343
            // Example: /var/www/chamilo/courses/xxx/document/folder/picture.jpg
344
            $fullPath = $whereToSave.$fileSystemName;
345
346
            // Example: /folder/picture.jpg
347
            $filePath = $uploadPath.$fileSystemName;
348
349
            $docId = DocumentManager::get_document_id(
350
                $courseInfo,
351
                $filePath,
352
                $sessionId
353
            );
354
355
            $documentList = DocumentManager::getDocumentByPathInCourse(
356
                $courseInfo,
357
                $filePath //$filePath
358
            );
359
360
            // This means that the path already exists in this course.
361
            if (!empty($documentList) && $whatIfFileExists != 'overwrite') {
362
                //$found = false;
363
                // Checking if we are talking about the same course + session
364
                /*foreach ($documentList as $document) {
365
                    if ($document['session_id'] == $sessionId) {
366
                        $found = true;
367
                        break;
368
                    }
369
                }*/
370
371
                //if ($found == false) {
372
                    $whatIfFileExists = 'rename';
373
                //}
374
            }
375
376
            // What to do if the target file exists
377
            switch ($whatIfFileExists) {
378
                // Overwrite the file if it exists
379
                case 'overwrite':
380
                    // Check if the target file exists, so we can give another message
381
                    $fileExists = file_exists($fullPath);
382
383
                    if (moveUploadedFile($uploadedFile, $fullPath)) {
384
                        chmod($fullPath, $filePermissions);
385
386
                        if ($fileExists && $docId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $docId of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
387
                            // UPDATE DATABASE
388
                            $documentId = DocumentManager::get_document_id(
389
                                $courseInfo,
390
                                $filePath
391
                            );
392
                            if (is_numeric($documentId)) {
393
                                // Update file size
394
                                update_existing_document(
395
                                    $courseInfo,
396
                                    $documentId,
397
                                    $uploadedFile['size']
398
                                );
399
400
                                // Update document item_property
401
                                api_item_property_update(
402
                                    $courseInfo,
403
                                    TOOL_DOCUMENT,
404
                                    $documentId,
405
                                    'DocumentUpdated',
406
                                    $userId,
407
                                    $groupId,
408
                                    $toUserId,
409
                                    null,
410
                                    null,
411
                                    $sessionId
412
                                );
413
414
                                // Redo visibility
415
                                api_set_default_visibility(
416
                                    $documentId,
417
                                    TOOL_DOCUMENT,
418
                                    null,
419
                                    $courseInfo
420
                                );
421
                            } else {
422
                                // There might be cases where the file exists on disk but there is no registration of that in the database
423
                                // In this case, and if we are in overwrite mode, overwrite and create the db record
424
                                $documentId = add_document(
425
                                    $courseInfo,
426
                                    $filePath,
427
                                    'file',
428
                                    $fileSize,
429
                                    $documentTitle,
430
                                    $comment,
431
                                    0,
432
                                    true,
433
                                    $groupId,
434
                                    $sessionId
435
                                );
436
437 View Code Duplication
                                if ($documentId) {
438
                                    // Put the document in item_property update
439
                                    api_item_property_update(
440
                                        $courseInfo,
441
                                        TOOL_DOCUMENT,
442
                                        $documentId,
443
                                        'DocumentAdded',
444
                                        $userId,
445
                                        $groupId,
446
                                        $toUserId,
447
                                        null,
448
                                        null,
449
                                        $sessionId
450
                                    );
451
452
                                    // Redo visibility
453
                                    api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
454
                                }
455
                            }
456
457
                            // If the file is in a folder, we need to update all parent folders
458
                            item_property_update_on_folder($courseInfo, $uploadPath, $userId);
459
                            // Display success message with extra info to user
460
                            if ($output) {
461
                                Display::addFlash(
462
                                    Display::return_message(
463
                                        get_lang('UplUploadSucceeded') . '<br /> ' . $documentTitle . ' ' . get_lang('UplFileOverwritten'),
464
                                        'confirmation',
465
                                        false
466
                                    )
467
                                );
468
                            }
469
470
                            return $filePath;
471
                        } else {
472
473
                            // Put the document data in the database
474
                            $documentId = add_document(
475
                                $courseInfo,
476
                                $filePath,
477
                                'file',
478
                                $fileSize,
479
                                $documentTitle,
480
                                $comment,
481
                                0,
482
                                true,
483
                                $groupId,
484
                                $sessionId
485
                            );
486
487 View Code Duplication
                            if ($documentId) {
488
                                // Put the document in item_property update
489
                                api_item_property_update(
490
                                    $courseInfo,
491
                                    TOOL_DOCUMENT,
492
                                    $documentId,
493
                                    'DocumentAdded',
494
                                    $userId,
495
                                    $groupId,
496
                                    $toUserId,
497
                                    null,
498
                                    null,
499
                                    $sessionId
500
                                );
501
502
                                // Redo visibility
503
                                api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
504
                            }
505
                            // If the file is in a folder, we need to update all parent folders
506
                            item_property_update_on_folder($courseInfo, $uploadPath, $userId);
507
                            // Display success message to user
508 View Code Duplication
                            if ($output) {
509
                                Display::addFlash(
510
                                    Display::return_message(
511
                                        get_lang('UplUploadSucceeded').'<br /> '.$documentTitle,
512
                                        'confirmation',
513
                                        false
514
                                    )
515
                                );
516
                            }
517
518
                            return $filePath;
519
                        }
520
                    } else {
521
                        if ($output) {
522
                            Display::addFlash(
523
                                Display::return_message(
524
                                    get_lang('UplUnableToSaveFile'),
525
                                    'error',
526
                                    false
527
                                )
528
                            );
529
                        }
530
531
                        return false;
532
                    }
533
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
534
                case 'rename':
535
                    // Rename the file if it exists
536
537
                    // Always rename.
538
                    $cleanName = DocumentManager::getUniqueFileName(
539
                        $uploadPath,
540
                        $cleanName,
541
                        $courseInfo,
542
                        $sessionId,
543
                        $groupId
544
                    );
545
546
                    $fileSystemName = DocumentManager::fixDocumentName(
547
                        $cleanName,
548
                        'file',
549
                        $courseInfo,
550
                        $sessionId,
551
                        $groupId
552
                    );
553
554
                    $documentTitle = get_document_title($cleanName);
555
556
                    $fullPath = $whereToSave.$fileSystemName;
557
                    $filePath = $uploadPath.$fileSystemName;
558
559
                    if (moveUploadedFile($uploadedFile, $fullPath)) {
560
561
                        chmod($fullPath, $filePermissions);
562
                        // Put the document data in the database
563
                        $documentId = add_document(
564
                            $courseInfo,
565
                            $filePath,
566
                            'file',
567
                            $fileSize,
568
                            $documentTitle,
569
                            $comment, // comment
570
                            0, // read only
571
                            true, // save visibility
572
                            $groupId,
573
                            $sessionId
574
                        );
575
576 View Code Duplication
                        if ($documentId) {
577
                            // Update document item_property
578
                            api_item_property_update(
579
                                $courseInfo,
580
                                TOOL_DOCUMENT,
581
                                $documentId,
582
                                'DocumentAdded',
583
                                $userId,
584
                                $groupId,
585
                                $toUserId,
586
                                null,
587
                                null,
588
                                $sessionId
589
                            );
590
591
                            // Redo visibility
592
                            api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
593
                        }
594
595
                        // If the file is in a folder, we need to update all parent folders
596
                        item_property_update_on_folder($courseInfo, $uploadPath, $userId);
597
598
                        // Display success message to user
599
                        if ($output) {
600
601
                            Display::addFlash(
602
                                Display::return_message(
603
                                    get_lang('UplUploadSucceeded') . '<br />' . get_lang('UplFileSavedAs') .' '.$documentTitle,
604
                                    'confirmation',
605
                                    false
606
                                )
607
                            );
608
                        }
609
610
                        return $filePath;
611
                    } else {
612
                        if ($output) {
613
                            Display::addFlash(
614
                                Display::return_message(
615
                                    get_lang('UplUnableToSaveFile'),
616
                                    'error',
617
                                    false
618
                                )
619
                            );
620
                        }
621
622
                        return false;
623
                    }
624
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
625
                default:
626
                    // Only save the file if it doesn't exist or warn user if it does exist
627
                    if (file_exists($fullPath) && $docId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $docId of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
628
                        if ($output) {
629
                            Display::addFlash(
630
                                Display::return_message(
631
                                    $cleanName.' '.get_lang('UplAlreadyExists'),
632
                                    'error',
633
                                    false
634
                                )
635
                            );
636
                        }
637
                    } else {
638
                        if (moveUploadedFile($uploadedFile, $fullPath)) {
639
                            chmod($fullPath, $filePermissions);
640
641
                            // Put the document data in the database
642
                            $documentId = add_document(
643
                                $courseInfo,
644
                                $filePath,
645
                                'file',
646
                                $fileSize,
647
                                $documentTitle,
648
                                $comment,
649
                                0,
650
                                true,
651
                                $groupId,
652
                                $sessionId
653
                            );
654
655 View Code Duplication
                            if ($documentId) {
656
                                // Update document item_property
657
                                api_item_property_update(
658
                                    $courseInfo,
659
                                    TOOL_DOCUMENT,
660
                                    $documentId,
661
                                    'DocumentAdded',
662
                                    $userId,
663
                                    $groupId,
664
                                    $toUserId,
665
                                    null,
666
                                    null,
667
                                    $sessionId
668
                                );
669
                                // Redo visibility
670
                                api_set_default_visibility($documentId, TOOL_DOCUMENT, null, $courseInfo);
671
                            }
672
673
                            // If the file is in a folder, we need to update all parent folders
674
                            item_property_update_on_folder(
675
                                $courseInfo,
676
                                $uploadPath,
677
                                $userId
678
                            );
679
680
                            // Display success message to user
681 View Code Duplication
                            if ($output) {
682
                                Display::addFlash(
683
                                    Display::return_message(
684
                                        get_lang('UplUploadSucceeded').'<br /> '.$documentTitle,
685
                                        'confirmation',
686
                                        false
687
                                    )
688
                                );
689
                            }
690
691
                            return $filePath;
692
                        } else {
693
                            if ($output) {
694
                                Display::addFlash(
695
                                    Display::return_message(
696
                                        get_lang('UplUnableToSaveFile'),
697
                                        'error',
698
                                        false
699
                                    )
700
                                );
701
                            }
702
703
                            return false;
704
                        }
705
                    }
706
                    break;
707
            }
708
        }
709
    }
710
}
711
712
/**
713
 * @param string $file
714
 * @param string $storePath
715
 *
716
 * @return bool
717
 */
718
function moveUploadedFile($file, $storePath)
719
{
720
    $handleFromFile = isset($file['from_file']) && $file['from_file'] ? true : false;
721
    $moveFile = isset($file['move_file']) && $file['move_file'] ? true : false;
722
    if ($moveFile) {
723
        copy($file['tmp_name'], $storePath);
724
    }
725
    if ($handleFromFile) {
726
        return file_exists($file['tmp_name']);
727
    } else {
728
        return move_uploaded_file($file['tmp_name'], $storePath);
729
    }
730
}
731
732
/**
733
 * Checks if there is enough place to add a file on a directory
734
 * on the base of a maximum directory size allowed
735
 * deprecated: use enough_space instead!
736
 * @author - Hugues Peeters <[email protected]>
737
 * @param  - file_size (int) - size of the file in byte
738
 * @param  - dir (string) - Path of the directory
739
 *           whe the file should be added
740
 * @param  - max_dir_space (int) - maximum size of the diretory in byte
741
 * @return - boolean true if there is enough space,
0 ignored issues
show
Documentation introduced by
The doc-type - could not be parsed: Unknown type name "-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
742
 *				boolean false otherwise
743
 *
744
 * @see    - enough_size() uses  dir_total_space() function
745
 */
746
function enough_size($file_size, $dir, $max_dir_space)
747
{
748
    // If the directory is the archive directory, safely ignore the size limit
749
    if (api_get_path(SYS_ARCHIVE_PATH) == $dir) {
750
        return true;
751
    }
752
753
    if ($max_dir_space) {
754
        $already_filled_space = dir_total_space($dir);
755
        if (($file_size + $already_filled_space) > $max_dir_space) {
756
            return false;
757
        }
758
    }
759
760
    return true;
761
}
762
763
/**
764
 * Computes the size already occupied by a directory and is subdirectories
765
 *
766
 * @author - Hugues Peeters <[email protected]>
767
 * @param  - dir_path (string) - size of the file in byte
768
 * @return - int - return the directory size in bytes
0 ignored issues
show
Documentation introduced by
The doc-type - could not be parsed: Unknown type name "-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
769
 */
770
function dir_total_space($dir_path)
771
{
772
    $save_dir = getcwd();
773
    chdir($dir_path) ;
774
    $handle = opendir($dir_path);
775
    $sumSize = 0;
776
    $dirList = array();
777
    while ($element = readdir($handle)) {
778
        if ( $element == '.' || $element == '..') {
779
            continue; // Skip the current and parent directories
780
        }
781
        if (is_file($element)) {
782
            $sumSize += filesize($element);
783
        }
784
        if (is_dir($element)) {
785
            $dirList[] = $dir_path.'/'.$element;
786
        }
787
    }
788
789
    closedir($handle) ;
790
791
    if (sizeof($dirList) > 0) {
792
        foreach ($dirList as $j) {
793
            $sizeDir = dir_total_space($j);	// Recursivity
794
            $sumSize += $sizeDir;
795
        }
796
    }
797
    chdir($save_dir); // Return to initial position
798
799
    return $sumSize;
800
}
801
802
/**
803
 * Tries to add an extension to files without extension
804
 * Some applications on Macintosh computers don't add an extension to the files.
805
 * This subroutine try to fix this on the basis of the MIME type sent
806
 * by the browser.
807
 *
808
 * Note : some browsers don't send the MIME Type (e.g. Netscape 4).
809
 *        We don't have solution for this kind of situation
810
 *
811
 * @author - Hugues Peeters <[email protected]>
812
 * @author - Bert Vanderkimpen
813
 * @param  - file_name (string) - Name of the file
814
 * @param  - file_type (string) - Type of the file
815
 * @return - file_name (string)
0 ignored issues
show
Documentation introduced by
The doc-type - could not be parsed: Unknown type name "-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
816
 */
817
function add_ext_on_mime($file_name, $file_type)
818
{
819
    // Check whether the file has an extension AND whether the browser has sent a MIME Type
820
821
    if (!preg_match('/^.*\.[a-zA-Z_0-9]+$/', $file_name) && $file_type) {
822
823
        // Build a "MIME-types / extensions" connection table
824
825
        static $mime_type = array();
826
827
        $mime_type[] = 'application/msword';             $extension[] = '.doc';
828
        $mime_type[] = 'application/rtf';                $extension[] = '.rtf';
829
        $mime_type[] = 'application/vnd.ms-powerpoint';  $extension[] = '.ppt';
830
        $mime_type[] = 'application/vnd.ms-excel';       $extension[] = '.xls';
831
        $mime_type[] = 'application/pdf';                $extension[] = '.pdf';
832
        $mime_type[] = 'application/postscript';         $extension[] = '.ps';
833
        $mime_type[] = 'application/mac-binhex40';       $extension[] = '.hqx';
834
        $mime_type[] = 'application/x-gzip';             $extension[] = 'tar.gz';
835
        $mime_type[] = 'application/x-shockwave-flash';  $extension[] = '.swf';
836
        $mime_type[] = 'application/x-stuffit';          $extension[] = '.sit';
837
        $mime_type[] = 'application/x-tar';              $extension[] = '.tar';
838
        $mime_type[] = 'application/zip';                $extension[] = '.zip';
839
        $mime_type[] = 'application/x-tar';              $extension[] = '.tar';
840
        $mime_type[] = 'text/html';                      $extension[] = '.html';
841
        $mime_type[] = 'text/plain';                     $extension[] = '.txt';
842
        $mime_type[] = 'text/rtf';                       $extension[] = '.rtf';
843
        $mime_type[] = 'img/gif';                        $extension[] = '.gif';
844
        $mime_type[] = 'img/jpeg';                       $extension[] = '.jpg';
845
        $mime_type[] = 'img/png';                        $extension[] = '.png';
846
        $mime_type[] = 'audio/midi';                     $extension[] = '.mid';
847
        $mime_type[] = 'audio/mpeg';                     $extension[] = '.mp3';
848
        $mime_type[] = 'audio/x-aiff';                   $extension[] = '.aif';
849
        $mime_type[] = 'audio/x-pn-realaudio';           $extension[] = '.rm';
850
        $mime_type[] = 'audio/x-pn-realaudio-plugin';    $extension[] = '.rpm';
851
        $mime_type[] = 'audio/x-wav';                    $extension[] = '.wav';
852
        $mime_type[] = 'video/mpeg';                     $extension[] = '.mpg';
853
        $mime_type[] = 'video/mpeg4-generic';            $extension[] = '.mp4';
854
        $mime_type[] = 'video/quicktime';                $extension[] = '.mov';
855
        $mime_type[] = 'video/x-msvideo';                $extension[] = '.avi';
856
857
        $mime_type[] = 'video/x-ms-wmv';                 $extension[] = '.wmv';
858
        $mime_type[] = 'video/x-flv';                    $extension[] = '.flv';
859
        $mime_type[] = 'image/svg+xml';                  $extension[] = '.svg';
860
        $mime_type[] = 'image/svg+xml';                  $extension[] = '.svgz';
861
        $mime_type[] = 'video/ogg';                  	 $extension[] = '.ogv';
862
        $mime_type[] = 'audio/ogg';                  	 $extension[] = '.oga';
863
        $mime_type[] = 'application/ogg';                $extension[] = '.ogg';
864
        $mime_type[] = 'application/ogg';                $extension[] = '.ogx';
865
        $mime_type[] = 'application/x-freemind';         $extension[] = '.mm';
866
867
        $mime_type[] = 'application/vnd.ms-word.document.macroEnabled.12';							$extension[] = '.docm';
868
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';	$extension[] = '.docx';
869
        $mime_type[] = 'application/vnd.ms-word.template.macroEnabled.12';							$extension[] = '.dotm';
870
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.template';	$extension[] = '.dotx';
871
        $mime_type[] = 'application/vnd.ms-powerpoint.template.macroEnabled.12';					$extension[] = '.potm';
872
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.template';		$extension[] = '.potx';
873
        $mime_type[] = 'application/vnd.ms-powerpoint.addin.macroEnabled.12';						$extension[] = '.ppam';
874
        $mime_type[] = 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12';					$extension[] = '.ppsm';
875
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.slideshow';	$extension[] = '.ppsx';
876
        $mime_type[] = 'application/vnd.ms-powerpoint.presentation.macroEnabled.12';				$extension[] = '.pptm';
877
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';	$extension[] = '.pptx';
878
        $mime_type[] = 'application/vnd.ms-excel.addin.macroEnabled.12';							$extension[] = '.xlam';
879
        $mime_type[] = 'application/vnd.ms-excel.sheet.binary.macroEnabled.12';						$extension[] = '.xlsb';
880
        $mime_type[] = 'application/vnd.ms-excel.sheet.macroEnabled.12';							$extension[] = '.xlsm';
881
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';			$extension[] = '.xlsx';
882
        $mime_type[] = 'application/vnd.ms-excel.template.macroEnabled.12';							$extension[] = '.xltm';
883
        $mime_type[] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.template';		$extension[] = '.xltx';
884
885
        // Test on PC (files with no extension get application/octet-stream)
886
        //$mime_type[] = 'application/octet-stream';      $extension[] = '.ext';
887
888
        // Check whether the MIME type sent by the browser is within the table
889
890
        foreach ($mime_type as $key => & $type) {
891
            if ($type == $file_type) {
892
                $file_name .=  $extension[$key];
893
                break;
894
            }
895
        }
896
897
        unset($mime_type, $extension, $type, $key); // Delete to eschew possible collisions
898
    }
899
900
    return $file_name;
901
}
902
903
/**
904
 *
905
 * @author Hugues Peeters <[email protected]>
906
 *
907
 * @param  array $uploaded_file - follows the $_FILES Structure
908
 * @param  string $base_work_dir - base working directory of the module
909
 * @param  string $upload_path  - destination of the upload.
910
 *                               This path is to append to $base_work_dir
911
 * @param  int $max_filled_space - amount of bytes to not exceed in the base
912
 *                               working directory
913
 *
914
 * @return boolean true if it succeds, false otherwise
915
 */
916
function treat_uploaded_file($uploaded_file, $base_work_dir, $upload_path, $max_filled_space, $uncompress = '')
917
{
918
    $uploaded_file['name'] = stripslashes($uploaded_file['name']);
919
920
    if (!enough_size($uploaded_file['size'], $base_work_dir, $max_filled_space)) {
921
        return api_failure::set_failure('not_enough_space');
922
    }
923
924
    if ($uncompress == 'unzip' && preg_match('/.zip$/', strtolower($uploaded_file['name']))) {
925
        return unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space);
926
    } else {
927
        $file_name = trim($uploaded_file['name']);
928
929
        // CHECK FOR NO DESIRED CHARACTERS
930
        $file_name = api_replace_dangerous_char($file_name, 'strict');
931
932
        // TRY TO ADD AN EXTENSION TO FILES WITOUT EXTENSION
933
        $file_name = add_ext_on_mime($file_name, $uploaded_file['type']);
934
935
        // HANDLE PHP FILES
936
        $file_name = ($file_name);
0 ignored issues
show
Bug introduced by
Why assign $file_name to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
937
938
        // COPY THE FILE TO THE DESIRED DESTINATION
939
        if (move_uploaded_file($uploaded_file['tmp_name'], $base_work_dir.$upload_path.'/'.$file_name)) {
940
            set_default_settings($upload_path, $file_name);
941
        }
942
943
        return true;
944
    }
945
}
946
947
/**
948
 * Manages all the unzipping process of an uploaded file
949
 *
950
 * @author Hugues Peeters <[email protected]>
951
 *
952
 * @param  array  $uploaded_file - follows the $_FILES Structure
953
 * @param  string $upload_path   - destination of the upload.
954
 *                                This path is to append to $base_work_dir
955
 * @param  string $base_work_dir  - base working directory of the module
956
 * @param  int $max_filled_space  - amount of bytes to not exceed in the base
957
 *                                working directory
958
 *
959
 * @return boolean true if it succeeds false otherwise
960
 */
961
function unzip_uploaded_file($uploaded_file, $upload_path, $base_work_dir, $max_filled_space)
962
{
963
    $zip_file = new PclZip($uploaded_file['tmp_name']);
964
965
    // Check the zip content (real size and file extension)
966
    if (file_exists($uploaded_file['tmp_name'])) {
967
        $zip_content_array = $zip_file->listContent();
968
        $ok_scorm = false;
969
        $realFileSize = 0;
970
        foreach ($zip_content_array as & $this_content) {
971
            if (preg_match('~.(php.*|phtml)$~i', $this_content['filename'])) {
972
                return api_failure::set_failure('php_file_in_zip_file');
973
            } elseif (stristr($this_content['filename'], 'imsmanifest.xml')) {
974
                $ok_scorm = true;
975
            } elseif (stristr($this_content['filename'], 'LMS')) {
976
                $ok_plantyn_scorm1 = true;
977
            } elseif (stristr($this_content['filename'], 'REF')) {
978
                $ok_plantyn_scorm2 = true;
979
            } elseif (stristr($this_content['filename'], 'SCO')) {
980
                $ok_plantyn_scorm3 = true;
981
            } elseif (stristr($this_content['filename'], 'AICC')) {
982
                $ok_aicc_scorm = true;
983
            }
984
            $realFileSize += $this_content['size'];
985
        }
986
987
        if (($ok_plantyn_scorm1 && $ok_plantyn_scorm2 && $ok_plantyn_scorm3) || $ok_aicc_scorm) {
988
            $ok_scorm = true;
989
        }
990
991
        if (!$ok_scorm && defined('CHECK_FOR_SCORM') && CHECK_FOR_SCORM) {
992
            return api_failure::set_failure('not_scorm_content');
993
        }
994
995
        if (!enough_size($realFileSize, $base_work_dir, $max_filled_space)) {
996
            return api_failure::set_failure('not_enough_space');
997
        }
998
999
        // It happens on Linux that $upload_path sometimes doesn't start with '/'
1000
        if ($upload_path[0] != '/' && substr($base_work_dir,-1,1) != '/') {
1001
            $upload_path = '/'.$upload_path;
1002
        }
1003
1004
        if ($upload_path[strlen($upload_path) - 1] == '/') {
1005
            $upload_path=substr($upload_path, 0, -1);
1006
        }
1007
1008
        /*	Uncompressing phase */
1009
1010
        /*
1011
            The first version, using OS unzip, is not used anymore
1012
            because it does not return enough information.
1013
            We need to process each individual file in the zip archive to
1014
            - add it to the database
1015
            - parse & change relative html links
1016
        */
1017
        if (PHP_OS == 'Linux' && ! get_cfg_var('safe_mode') && false) { // *** UGent, changed by OC ***
1018
            // Shell Method - if this is possible, it gains some speed
1019
            exec("unzip -d \"".$base_work_dir.$upload_path."/\"".$uploaded_file['name']." " .$uploaded_file['tmp_name']);
1020
        } else {
1021
            // PHP method - slower...
1022
            $save_dir = getcwd();
1023
            chdir($base_work_dir.$upload_path);
1024
            $unzippingState = $zip_file->extract();
1025 View Code Duplication
            for ($j=0; $j < count($unzippingState); $j++) {
1026
                $state = $unzippingState[$j];
1027
1028
                // Fix relative links in html files
1029
                $extension = strrchr($state['stored_filename'], '.');
1030
            }
1031
            if ($dir = @opendir($base_work_dir.$upload_path)) {
1032
                while ($file = readdir($dir)) {
1033
                    if ($file != '.' && $file != '..') {
1034
1035
                        $filetype = 'file';
1036
                        if (is_dir($base_work_dir.$upload_path.'/'.$file)) $filetype = 'folder';
1037
1038
                        $safe_file = api_replace_dangerous_char($file, 'strict');
1039
                        @rename($base_work_dir.$upload_path.'/'.$file,$base_work_dir.$upload_path.'/'.$safe_file);
1040
                        set_default_settings($upload_path, $safe_file,$filetype);
1041
                    }
1042
                }
1043
1044
                closedir($dir);
1045
            } else {
1046
                error_log('Could not create directory '.$base_work_dir.$upload_path.' to unzip files');
1047
            }
1048
            chdir($save_dir); // Back to previous dir position
1049
        }
1050
    }
1051
1052
    return true;
1053
}
1054
1055
/**
1056
 * Manages all the unzipping process of an uploaded document
1057
 * This uses the item_property table for properties of documents
1058
 *
1059
 * @author Hugues Peeters <[email protected]>
1060
 * @author Bert Vanderkimpen
1061
 *
1062
 * @param array  $courseInfo
1063
 * @param array  $userInfo
1064
 * @param array  $uploaded_file - follows the $_FILES Structure
1065
 * @param string $upload_path   - destination of the upload.
0 ignored issues
show
Bug introduced by
There is no parameter named $upload_path. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1066
 *                               This path is to append to $base_work_dir
1067
 * @param string $base_work_dir  - base working directory of the module
1068
 * @param int    $maxFilledSpace  - amount of bytes to not exceed in the base
1069
 *                                working directory
1070
 * @param int $sessionId
1071
 * @param int $groupId
1072
 * @param boolean $output Optional. If no output not wanted on success, set to false.
1073
 *
1074
 * @return boolean true if it succeeds false otherwise
1075
 */
1076
function unzip_uploaded_document(
1077
    $courseInfo,
1078
    $userInfo,
1079
    $uploaded_file,
1080
    $uploadPath,
1081
    $base_work_dir,
1082
    $maxFilledSpace,
1083
    $sessionId = 0,
1084
    $groupId = 0,
1085
    $output = true
1086
) {
1087
    $zip = new PclZip($uploaded_file['tmp_name']);
1088
1089
    // Check the zip content (real size and file extension)
1090
    $zip_content_array = (array)$zip->listContent();
1091
1092
    $realSize = 0;
1093
    foreach ($zip_content_array as & $this_content) {
1094
        $realSize += $this_content['size'];
1095
    }
1096
1097
    if (!DocumentManager::enough_space($realSize, $maxFilledSpace)) {
1098
        Display::display_error_message(get_lang('UplNotEnoughSpace'));
1099
        return false;
1100
    }
1101
1102
    $folder = api_get_unique_id();
1103
    $destinationDir = api_get_path(SYS_ARCHIVE_PATH).$folder;
1104
    mkdir($destinationDir, api_get_permissions_for_new_directories(), true);
1105
1106
    /*	Uncompress zip file*/
1107
    // We extract using a callback function that "cleans" the path
1108
    $zip->extract(
1109
        PCLZIP_OPT_PATH,
1110
        $destinationDir,
1111
        PCLZIP_CB_PRE_EXTRACT,
1112
        'clean_up_files_in_zip',
1113
        PCLZIP_OPT_REPLACE_NEWER
1114
    );
1115
1116
    // Add all documents in the unzipped folder to the database
1117
    add_all_documents_in_folder_to_database(
1118
        $courseInfo,
1119
        $userInfo,
1120
        $base_work_dir,
1121
        $destinationDir,
1122
        $sessionId,
1123
        $groupId,
1124
        $output,
1125
        array('path' => $uploadPath)
1126
    );
1127
1128
    if (is_dir($destinationDir)) {
1129
        rmdirr($destinationDir);
1130
    }
1131
1132
    return true;
1133
}
1134
1135
/**
1136
 * This function is a callback function that is used while extracting a zipfile
1137
 * http://www.phpconcept.net/pclzip/man/en/index.php?options-pclzip_cb_pre_extract
1138
 *
1139
 * @param $p_event
1140
 * @param $p_header
1141
 * @return 1 (If the function returns 1, then the extraction is resumed)
0 ignored issues
show
Documentation introduced by
The doc-type 1 could not be parsed: Unknown type name "1" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1142
 */
1143
function clean_up_files_in_zip($p_event, &$p_header)
1144
{
1145
    $res = clean_up_path($p_header['filename']);
1146
    return $res;
1147
}
1148
1149
/**
1150
 * This function cleans up a given path
1151
 * by eliminating dangerous file names and cleaning them
1152
 *
1153
 * @param string $path
1154
 * @return $path
0 ignored issues
show
Documentation introduced by
The doc-type $path could not be parsed: Unknown type name "$path" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1155
 * @see disable_dangerous_file()
1156
 * @see api_replace_dangerous_char()
1157
 */
1158
function clean_up_path(&$path)
1159
{
1160
    // Split the path in folders and files
1161
    $path_array = explode('/', $path);
1162
    // Clean up every foler and filename in the path
1163
    foreach ($path_array as $key => & $val) {
1164
        // We don't want to lose the dots in ././folder/file (cfr. zipfile)
1165
        if ($val != '.') {
1166
            $val = disable_dangerous_file(api_replace_dangerous_char($val));
1167
        }
1168
    }
1169
    // Join the "cleaned" path (modified in-place as passed by reference)
1170
    $path = implode('/', $path_array);
1171
    $res = filter_extension($path);
1172
    return $res;
1173
}
1174
1175
/**
1176
 * Checks if the file is dangerous, based on extension and/or mimetype.
1177
 * The list of extensions accepted/rejected can be found from
1178
 * api_get_setting('upload_extensions_exclude') and api_get_setting('upload_extensions_include')
1179
 * @param	string 	filename passed by reference. The filename will be modified
1180
 * if filter rules say so! (you can include path but the filename should look like 'abc.html')
1181
 * @return	int		0 to skip file, 1 to keep file
1182
 */
1183
function filter_extension(&$filename)
1184
{
1185
    if (substr($filename, -1) == '/') {
1186
        return 1;  // Authorize directories
1187
    }
1188
    $blacklist = api_get_setting('upload_extensions_list_type');
1189
    if ($blacklist != 'whitelist') { // if = blacklist
1190
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_blacklist')));
1191
1192
        $skip = api_get_setting('upload_extensions_skip');
1193
        $ext = strrchr($filename, '.');
1194
        $ext = substr($ext, 1);
1195
        if (empty($ext)) {
1196
            return 1; // We're in blacklist mode, so accept empty extensions
1197
        }
1198 View Code Duplication
        if (in_array(strtolower($ext), $extensions)) {
1199
            if ($skip == 'true') {
1200
                return 0;
1201
            } else {
1202
                $new_ext = api_get_setting('upload_extensions_replace_by');
1203
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
1204
                return 1;
1205
            }
1206
        } else {
1207
            return 1;
1208
        }
1209
    } else {
1210
        $extensions = explode(';', strtolower(api_get_setting('upload_extensions_whitelist')));
1211
        $skip = api_get_setting('upload_extensions_skip');
1212
        $ext = strrchr($filename, '.');
1213
        $ext = substr($ext, 1);
1214
        if (empty($ext)) {
1215
            return 1; // Accept empty extensions
1216
        }
1217 View Code Duplication
        if (!in_array(strtolower($ext), $extensions)) {
1218
            if ($skip == 'true') {
1219
                return 0;
1220
            } else {
1221
                $new_ext = api_get_setting('upload_extensions_replace_by');
1222
                $filename = str_replace('.'.$ext, '.'.$new_ext, $filename);
1223
                return 1;
1224
            }
1225
        } else {
1226
            return 1;
1227
        }
1228
    }
1229
}
1230
1231
/**
1232
 * Adds a new document to the database
1233
 *
1234
 * @param array $_course
1235
 * @param string $path
1236
 * @param string $filetype
1237
 * @param int $filesize
1238
 * @param string $title
1239
 * @param string $comment
1240
 * @param int $readonly
1241
 * @param bool $save_visibility
1242
 * @param int $group_id
1243
 * @param int $session_id Session ID, if any
1244
 * @param int $userId creator id
1245
 *
1246
 * @return int id if inserted document
1247
 */
1248
function add_document(
1249
    $_course,
1250
    $path,
1251
    $filetype,
1252
    $filesize,
1253
    $title,
1254
    $comment = null,
1255
    $readonly = 0,
1256
    $save_visibility = true,
1257
    $group_id = null,
1258
    $session_id = 0,
1259
    $userId = 0
1260
) {
1261
    $session_id = empty($session_id) ? api_get_session_id() : $session_id;
1262
    $userId = empty($userId) ? api_get_user_id() : $userId;
1263
1264
    $readonly = intval($readonly);
1265
    $c_id = $_course['real_id'];
1266
    $table_document = Database::get_course_table(TABLE_DOCUMENT);
1267
1268
    $params = [
1269
        'c_id' => $c_id,
1270
        'path' => $path,
1271
        'filetype' => $filetype,
1272
        'size' => $filesize,
1273
        'title' => $title,
1274
        'comment' => $comment,
1275
        'readonly' => $readonly,
1276
        'session_id' => $session_id,
1277
    ];
1278
    $documentId = Database::insert($table_document, $params);
1279
    if ($documentId) {
1280
        $sql = "UPDATE $table_document SET id = iid WHERE iid = $documentId";
1281
        Database::query($sql);
1282
1283
        if ($save_visibility) {
1284
            api_set_default_visibility($documentId, TOOL_DOCUMENT, $group_id, $_course, $session_id, $userId);
1285
        }
1286
1287
        return $documentId;
1288
    } else {
1289
        return false;
1290
    }
1291
}
1292
1293
/**
1294
 * Updates an existing document in the database
1295
 * as the file exists, we only need to change the size
1296
 *
1297
 * @param array $_course
1298
 * @param int $documentId
1299
 * @param int $filesize
1300
 * @param int $readonly
1301
 * @return boolean true /false
1302
 */
1303
function update_existing_document($_course, $documentId, $filesize, $readonly = 0)
1304
{
1305
    $document_table = Database::get_course_table(TABLE_DOCUMENT);
1306
    $documentId 	= intval($documentId);
1307
    $filesize 		= intval($filesize);
1308
    $readonly 		= intval($readonly);
1309
    $course_id 		= $_course['real_id'];
1310
1311
    $sql = "UPDATE $document_table SET
1312
            size = '$filesize',
1313
            readonly = '$readonly'
1314
			WHERE c_id = $course_id AND id = $documentId";
1315
    if (Database::query($sql)) {
1316
        return true;
1317
    } else {
1318
        return false;
1319
    }
1320
}
1321
1322
/**
1323
 * This function updates the last_edit_date, last edit user id on all folders in a given path
1324
 *
1325
 * @param array $_course
1326
 * @param string $path
1327
 * @param int $user_id
1328
 */
1329
function item_property_update_on_folder($_course, $path, $user_id)
1330
{
1331
    // If we are in the root, just return... no need to update anything
1332
    if ($path == '/') {
1333
        return;
1334
    }
1335
1336
    $user_id = intval($user_id);
1337
1338
    // If the given path ends with a / we remove it
1339
    $endchar = substr($path, strlen($path) - 1, 1);
1340
    if ($endchar == '/') {
1341
        $path = substr($path, 0, strlen($path) - 1);
1342
    }
1343
1344
    $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
1345
1346
    // Get the time
1347
    $time = date('Y-m-d H:i:s', time());
1348
1349
    // Det all paths in the given path
1350
    // /folder/subfolder/subsubfolder/file
1351
    // if file is updated, subsubfolder, subfolder and folder are updated
1352
1353
    $exploded_path = explode('/', $path);
1354
    $course_id = api_get_course_int_id();
1355
    $newpath = '';
1356
    foreach ($exploded_path as $key => & $value) {
1357
        // We don't want a slash before our first slash
1358
        if ($key != 0) {
1359
            $newpath .= '/'.$value;
1360
1361
            //echo 'path= '.$newpath.'<br />';
1362
            // Select ID of given folder
1363
            $folder_id = DocumentManager::get_document_id($_course, $newpath);
1364
1365
            if ($folder_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $folder_id of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1366
                $sql = "UPDATE $table SET
1367
				        lastedit_date='$time',lastedit_type='DocumentInFolderUpdated', lastedit_user_id='$user_id'
1368
						WHERE c_id = $course_id AND tool='".TOOL_DOCUMENT."' AND ref='$folder_id'";
1369
                Database::query($sql);
1370
            }
1371
        }
1372
    }
1373
}
1374
1375
/**
1376
 * Returns the directory depth of the file.
1377
 *
1378
 * @author	Olivier Cauberghe <[email protected]>
1379
 * @param	path+filename eg: /main/document/document.php
1380
 * @return	The directory depth
1381
 */
1382
function get_levels($filename) {
1383
    $levels = explode('/', $filename);
1384
    if (empty($levels[count($levels) - 1])) {
1385
        unset($levels[count($levels) - 1]);
1386
    }
1387
    return count($levels);
1388
}
1389
1390
/**
1391
 * Adds file to document table in database
1392
 * deprecated: use file_set_default_settings instead
1393
 *
1394
 * @author	Olivier Cauberghe <[email protected]>
1395
 * @param	path,filename
1396
 * action:	Adds an entry to the document table with the default settings.
1397
 */
1398
function set_default_settings($upload_path, $filename, $filetype = 'file')
1399
{
1400
    $dbTable = Database::get_course_table(TABLE_DOCUMENT);
1401
    global $default_visibility;
1402
1403
    if (!$default_visibility) {
1404
        $default_visibility = 'v';
1405
    }
1406
    $filetype = Database::escape_string($filetype);
1407
1408
    $upload_path = str_replace('\\', '/', $upload_path);
1409
    $upload_path = str_replace('//', '/', $upload_path);
1410
1411
    if ($upload_path == '/') {
1412
        $upload_path='';
1413
    } elseif (!empty($upload_path) && $upload_path[0] != '/') {
1414
        $upload_path="/$upload_path";
1415
    }
1416
1417
    $endchar = substr($filename, strlen($filename) - 1, 1);
1418
1419
    if ($endchar == '/') {
1420
        $filename = substr($filename, 0, -1);
1421
    }
1422
    $filename = Database::escape_string($filename);
1423
    $query = "SELECT count(*) as bestaat FROM $dbTable
1424
              WHERE path='$upload_path/$filename'";
1425
    $result = Database::query($query);
1426
    $row = Database::fetch_array($result);
1427
    if ($row['bestaat'] > 0) {
1428
        $query = "UPDATE $dbTable SET
1429
		            path='$upload_path/$filename',
1430
		            visibility='$default_visibility',
1431
		            filetype='$filetype'
1432
		          WHERE path='$upload_path/$filename'";
1433
    } else {
1434
        $query = "INSERT INTO $dbTable (path,visibility,filetype)
1435
		          VALUES('$upload_path/$filename','$default_visibility','$filetype')";
1436
    }
1437
    Database::query($query);
1438
}
1439
1440
/**
1441
 * Retrieves the image path list in a html file
1442
 *
1443
 * @author Hugues Peeters <[email protected]>
1444
 * @param  string $html_file
1445
 * @return array -  images path list
1446
 */
1447
function search_img_from_html($html_file) {
1448
1449
    $img_path_list = array();
1450
1451
    if (!$fp = fopen($html_file, 'r')) {
1452
        return ;
1453
    }
1454
1455
    // Aearch and store occurences of the <img> tag in an array
1456
    $size_file = (filesize($html_file) === 0) ? 1 : filesize($html_file);
1457
    if (isset($fp) && $fp !== false) {
1458
        $buffer = fread($fp, $size_file);
1459
        if (strlen($buffer) >= 0 && $buffer !== false) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1460
            //
1461
        } else {
1462
            die('<center>Can not read file.</center>');
1463
        }
1464
    } else {
1465
        die('<center>Can not read file.</center>');
1466
    }
1467
    $matches = array();
1468
    if (preg_match_all('~<[[:space:]]*img[^>]*>~i', $buffer, $matches)) {
1469
        $img_tag_list = $matches[0];
1470
    }
1471
1472
    fclose ($fp);
1473
    unset($buffer);
1474
1475
    // Search the image file path from all the <IMG> tag detected
1476
1477
    if (sizeof($img_tag_list) > 0) {
1478
        foreach ($img_tag_list as & $this_img_tag) {
1479
            if (preg_match('~src[[:space:]]*=[[:space:]]*[\"]{1}([^\"]+)[\"]{1}~i', $this_img_tag, $matches)) {
1480
                $img_path_list[] = $matches[1];
1481
            }
1482
        }
1483
        $img_path_list = array_unique($img_path_list); // Remove duplicate entries
1484
    }
1485
1486
    return $img_path_list;
1487
}
1488
1489
/**
1490
 * Creates a new directory trying to find a directory name
1491
 * that doesn't already exist
1492
 * (we could use unique_name() here...)
1493
 *
1494
 * @author  Hugues Peeters <[email protected]>
1495
 * @author  Bert Vanderkimpen
1496
 * @param   array   $_course current course information
1497
 * @param   int     $user_id current user id
1498
 * @param   int     $session_id
1499
 * @param   int     $to_group_id
1500
 * @param   int     $to_user_id
1501
 * @param   string  $base_work_dir /var/www/chamilo/courses/ABC/document
1502
 * @param   string  $desired_dir_name complete path of the desired name
1503
 * Example: /folder1/folder2
1504
 * @param   string  $title "folder2"
1505
 * @param   int     $visibility (0 for invisible, 1 for visible, 2 for deleted)
1506
 * @param   bool $generateNewNameIfExists
1507
 * @return  string  actual directory name if it succeeds,
1508
 *          boolean false otherwise
1509
 */
1510
function create_unexisting_directory(
1511
    $_course,
1512
    $user_id,
1513
    $session_id,
1514
    $to_group_id,
1515
    $to_user_id,
1516
    $base_work_dir,
1517
    $desired_dir_name,
1518
    $title = null,
1519
    $visibility = null,
1520
    $generateNewNameIfExists = false
1521
) {
1522
    $course_id = $_course['real_id'];
1523
    $session_id = intval($session_id);
1524
1525
    $folderExists = DocumentManager::folderExists(
1526
        $desired_dir_name,
1527
        $_course,
1528
        $session_id,
1529
        $to_group_id
1530
    );
1531
1532
    if ($folderExists === true) {
1533
        if ($generateNewNameIfExists) {
1534
            $counter = 1;
1535
            while (1) {
1536
                $folderExists = DocumentManager::folderExists(
1537
                    $desired_dir_name.'_'.$counter,
1538
                    $_course,
1539
                    $session_id,
1540
                    $to_group_id
1541
                );
1542
1543
                if ($folderExists === false) {
1544
                    break;
1545
                }
1546
                $counter++;
1547
            }
1548
            $desired_dir_name = $desired_dir_name.'_'.$counter;
1549
        } else {
1550
1551
            return false;
1552
        }
1553
    }
1554
1555
    $systemFolderName = $desired_dir_name;
1556
1557
    // Adding suffix
1558
    $suffix = DocumentManager::getDocumentSuffix(
1559
        $_course,
1560
        $session_id,
1561
        $to_group_id
1562
    );
1563
1564
    $systemFolderName .= $suffix;
1565
1566
    if ($title == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $title of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1567
        $title = basename($desired_dir_name);
1568
    }
1569
1570
    if (!is_dir($base_work_dir.$systemFolderName)) {
1571
        $result = mkdir(
1572
            $base_work_dir . $systemFolderName,
1573
            api_get_permissions_for_new_directories(),
1574
            true
1575
        );
1576
1577
        if ($result) {
1578
1579
            // Check if pathname already exists inside document table
1580
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
1581
            $sql = "SELECT id, path FROM $tbl_document
1582
                    WHERE
1583
                        c_id = $course_id AND
1584
                        (
1585
                            path = '" . Database::escape_string($systemFolderName). "'
1586
                        )
1587
            ";
1588
1589
            $rs = Database::query($sql);
1590
            if (Database::num_rows($rs) == 0) {
1591
                $document_id = add_document(
1592
                    $_course,
1593
                    $systemFolderName,
1594
                    'folder',
1595
                    0,
1596
                    $title,
1597
                    null,
1598
                    0,
1599
                    true,
1600
                    $to_group_id,
1601
                    $session_id,
1602
                    $user_id
1603
                );
1604
1605
                if ($document_id) {
1606
                    // Update document item_property
1607
                    if (!empty($visibility)) {
1608
                        $visibilities = array(
1609
                            0 => 'invisible',
1610
                            1 => 'visible',
1611
                            2 => 'delete'
1612
                        );
1613
                        api_item_property_update(
1614
                            $_course,
1615
                            TOOL_DOCUMENT,
1616
                            $document_id,
1617
                            $visibilities[$visibility],
1618
                            $user_id,
1619
                            $to_group_id,
1620
                            $to_user_id,
1621
                            null,
1622
                            null,
1623
                            $session_id
1624
                        );
1625
                    } else {
1626
                        api_item_property_update(
1627
                            $_course,
1628
                            TOOL_DOCUMENT,
1629
                            $document_id,
1630
                            'FolderCreated',
1631
                            $user_id,
1632
                            $to_group_id,
1633
                            $to_user_id,
1634
                            null,
1635
                            null,
1636
                            $session_id
1637
                        );
1638
                    }
1639
1640
                    $documentData = DocumentManager::get_document_data_by_id(
1641
                        $document_id,
1642
                        $_course['code'],
1643
                        false,
1644
                        $session_id
1645
                    );
1646
1647
                    return $documentData;
1648
                }
1649
            } else {
1650
                $document = Database::fetch_array($rs);
1651
                $documentData = DocumentManager::get_document_data_by_id(
1652
                    $document['id'],
1653
                    $_course['code'],
1654
                    false,
1655
                    $session_id
1656
                );
1657
1658
                /* This means the folder NOT exist in the filesystem
1659
                 (now this was created) but there is a record in the Database*/
1660
1661
                return $documentData;
1662
            }
1663
        }
1664
    }
1665
1666
    return false;
1667
}
1668
1669
/**
1670
 * Handles uploaded missing images
1671
 *
1672
 * @author Hugues Peeters <[email protected]>
1673
 * @author Bert Vanderkimpen
1674
 * @param array $_course
1675
 * @param array $uploaded_file_collection - follows the $_FILES Structure
1676
 * @param string $base_work_dir
1677
 * @param string $missing_files_dir
1678
 * @param int $user_id
1679
 * @param int $max_filled_space
1680
 */
1681
function move_uploaded_file_collection_into_directory(
1682
    $_course,
1683
    $uploaded_file_collection,
1684
    $base_work_dir,
1685
    $missing_files_dir,
1686
    $user_id,
1687
    $to_group_id,
1688
    $to_user_id,
1689
    $max_filled_space
1690
) {
1691
    $number_of_uploaded_images = count($uploaded_file_collection['name']);
1692
    $new_file_list = array();
1693
    for ($i = 0; $i < $number_of_uploaded_images; $i++) {
1694
        $missing_file['name'] = $uploaded_file_collection['name'][$i];
1695
        $missing_file['type'] = $uploaded_file_collection['type'][$i];
1696
        $missing_file['tmp_name'] = $uploaded_file_collection['tmp_name'][$i];
1697
        $missing_file['error'] = $uploaded_file_collection['error'][$i];
1698
        $missing_file['size'] = $uploaded_file_collection['size'][$i];
1699
1700
        $upload_ok = process_uploaded_file($missing_file);
1701
        if ($upload_ok) {
1702
            $new_file_list[] = handle_uploaded_document(
1703
                $_course,
1704
                $missing_file,
1705
                $base_work_dir,
1706
                $missing_files_dir,
1707
                $user_id,
1708
                $to_group_id,
1709
                $to_user_id,
1710
                $max_filled_space,
1711
                0,
1712
                'overwrite'
1713
            );
1714
        }
1715
        unset($missing_file);
1716
    }
1717
    return $new_file_list;
1718
}
1719
1720
/**
1721
 * Opens the old html file and replace the src path into the img tag
1722
 * This also works for files in subdirectories.
1723
 * @param $original_img_path is an array
1724
 * @param $new_img_path is an array
1725
 */
1726
function replace_img_path_in_html_file($original_img_path, $new_img_path, $html_file)
1727
{
1728
    // Open the file
1729
1730
    $fp = fopen($html_file, 'r');
1731
    $buffer = fread($fp, filesize($html_file));
1732
    $new_html_content = '';
1733
1734
    // Fix the image tags
1735
1736
    for ($i = 0, $fileNb = count($original_img_path); $i < $fileNb ; $i++) {
1737
        $replace_what = $original_img_path[$i];
1738
        // We only need the directory and the filename /path/to/file_html_files/missing_file.gif -> file_html_files/missing_file.gif
1739
        $exploded_file_path = explode('/', $new_img_path[$i]);
1740
        $replace_by = $exploded_file_path[count($exploded_file_path) - 2].'/'.$exploded_file_path[count($exploded_file_path) - 1];
1741
        $buffer = str_replace($replace_what, $replace_by, $buffer);
1742
    }
1743
1744
    $new_html_content .= $buffer;
1745
1746
    @fclose($fp);
1747
1748
    // Write the resulted new file
1749
1750
    if (!$fp = fopen($html_file, 'w')) {
1751
        return;
1752
    }
1753
1754
    if (!fwrite($fp, $new_html_content)) {
1755
        return;
1756
    }
1757
}
1758
1759
/**
1760
 * Creates a file containing an html redirection to a given url
1761
 *
1762
 * @author Hugues Peeters <[email protected]>
1763
 * @param string $file_path
1764
 * @param string $url
1765
 * @return void
1766
 */
1767
function create_link_file($file_path, $url)
1768
{
1769
    $file_content = '<html>'
1770
        .'<head>'
1771
        .'<meta http-equiv="refresh" content="1;url='.$url.'">'
1772
        .'</head>'
1773
        .'<body>'
1774
        .'</body>'
1775
        .'</html>';
1776
    if (file_exists($file_path)) {
1777
        if (!($fp = fopen ($file_path, 'w'))) {
1778
            return false;
1779
        }
1780
        return fwrite($fp, $file_content);
1781
    }
1782
}
1783
1784
/**
1785
 * Opens html file $full_file_name;
1786
 * Parses the hyperlinks; and
1787
 * Writes the result back in the html file.
1788
 *
1789
 * @author Roan Embrechts
1790
 * @version 0.1
1791
 */
1792
function api_replace_links_in_html($upload_path, $full_file_name)
1793
{
1794
    // Open the file
1795
    if (file_exists($full_file_name)) {
1796
        $fp = fopen($full_file_name, 'r');
1797
        $buffer = fread ($fp, filesize ($full_file_name));
1798
1799
        // Parse the contents
1800
        $new_html_content = api_replace_links_in_string($upload_path, $buffer);
1801
1802
        // Write the result
1803
        $fp = fopen($full_file_name, 'w');
1804
        fwrite($fp, $new_html_content);
1805
    }
1806
}
1807
1808
/**
1809
deprecated: use api_replace_parameter instead
1810
1811
Parse the buffer string provided as parameter
1812
Replace the a href tags so they are displayed correctly.
1813
- works for files in root and subdirectories
1814
- replace relative hyperlinks to use showinframes.php?file= ...
1815
- add target="_self" to all absolute hyperlinks
1816
- leave local anchors untouched (e.g. #CHAPTER1)
1817
- leave links with download.php and showinframes.php untouched
1818
1819
@author Roan Embrechts
1820
@version 0.6
1821
 */
1822
function api_replace_links_in_string($upload_path, $buffer) {
1823
    // Search for hyperlinks
1824
    $matches = array();
1825
    if (preg_match_all('/<a[\s]*href[^<]*>/i', $buffer, $matches)) {
1826
        $tag_list = $matches[0];
1827
    }
1828
1829
    // Search the filepath of all detected <a href> tags
1830 View Code Duplication
    if (sizeof($tag_list) > 0) {
1831
        $file_path_list = array();
1832
        $href_list = array();
1833
1834
        foreach ($tag_list as & $this_tag) {
1835
            /* Match case insensitive, the stuff between the two ~ :
1836
                a href = <exactly one quote><one or more non-quotes><exactly one ">
1837
                e.g. a href="www.google.be", A HREF =   "info.html"
1838
                to match ["] escape the " or else PHP interprets it
1839
                [\"]{1} --> matches exactly one "
1840
                +	1 or more (like * is 0 or more)
1841
                [\s]* matches whitespace
1842
                $matches contains captured subpatterns
1843
                the only one here is ([^\"]+) --> matches[1]
1844
            */
1845
            if (preg_match("~a href[\s]*=[\s]*[\"]{1}([^\"]+)[\"]{1}~i", $this_tag, $matches)) {
1846
                $file_path_list[] = $matches[1]; // older
1847
                $href_list[] = $matches[0]; // to also add target="_self"
1848
            }
1849
        }
1850
    }
1851
1852
    // Replace the original hyperlinks by the correct ones
1853
    for ($count = 0; $count < sizeof($href_list); $count++) {
1854
1855
        $replace_what[$count] = $href_list[$count];
1856
1857
        $is_absolute_hyperlink = strpos($replace_what[$count], 'http');
1858
        $is_local_anchor = strpos($replace_what[$count], "#");
1859
        if (!$is_absolute_hyperlink && !$is_local_anchor) {
1860
            // This is a relative hyperlink
1861
            if ((strpos($replace_what[$count], 'showinframes.php') === false) &&
1862
                (strpos($replace_what[$count], 'download.php') === false)
1863
            ) {
1864
                // Fix the link to use showinframes.php
1865
                $replace_by[$count] = 'a href = "showinframes.php?file='.$upload_path.'/'.$file_path_list[$count].'" target="_self"';
1866
            } else {
1867
                // URL has been already fixed, leave it as is
1868
                $replace_by[$count] = $replace_what[$count];
1869
            }
1870
        } elseif ($is_absolute_hyperlink) {
1871
            $replace_by[$count] = 'a href="'.$file_path_list[$count].'" target ="_self"';
1872
        } else {
1873
            // Don't change anything
1874
            $replace_by[$count] = $replace_what[$count];
1875
        }
1876
        //Display::display_normal_message('Link replaced by ' . $replace_by[$count]); // debug
1877
    }
1878
1879
    $buffer = str_replace($replace_what, $replace_by, $buffer);
1880
    return $buffer;
1881
}
1882
1883
/**
1884
EXPERIMENTAL - function seems to work, needs more testing
1885
1886
@param $upload_path is the path where the document is stored, like "/archive/"
1887
if it is the root level, the function expects "/"
1888
otherwise "/path/"
1889
1890
This function parses all tags with $param_name parameters.
1891
so the tags are displayed correctly.
1892
1893
--------------
1894
Algorithm v1.0
1895
--------------
1896
given a string and a parameter,
1897
 * OK find all tags in that string with the specified parameter (like href or src)
1898
 * OK for every one of these tags, find the src|href|... part to edit it
1899
 * OK change the src|href|... part to use download.php (or showinframes.php)
1900
 * OK do some special stuff for hyperlinks
1901
1902
Exceptions
1903
 * OK if download.php or showinframes.php is already in the tag, leave it alone
1904
 * OK if mailto is in the tag, leave it alone
1905
 * OK if the src|href param contains http://, it's absolute --> leave it alone
1906
1907
Special for hyperlinks (a href...)
1908
 * OK add target="_self"
1909
 * OK use showinframes.php instead of download.php
1910
1911
@author Roan Embrechts
1912
@version 1.1
1913
 */
1914
function api_replace_parameter($upload_path, $buffer, $param_name = 'src')
1915
{
1916
    // Search for tags with $param_name as a parameter
1917
1918
    /*
1919
    // [\s]*	matches whitespace
1920
    // [\"=a-z] matches ", = and a-z
1921
    // ([\s]*[a-z]*)*	matches all whitespace and normal alphabet
1922
    //					characters a-z combinations but seems too slow
1923
    //	perhaps ([\s]*[a-z]*) a maximum number of times ?
1924
    // [\s]*[a-z]*[\s]*	matches many tags
1925
    // the ending "i" means to match case insensitive (a matches a and A)
1926
    */
1927
    $matches = array();
1928
    if (preg_match_all('/<[a-z]+[^<]*'.$param_name.'[^<]*>/i', $buffer, $matches)) {
1929
        $tag_list = $matches[0];
1930
    }
1931
1932
    // Search the filepath of parameter $param_name in all detected tags
1933
1934 View Code Duplication
    if (sizeof($tag_list) > 0) {
1935
        $file_path_list = array();
1936
        $href_list = array();
1937
1938
        foreach ($tag_list as & $this_tag) {
1939
            //Display::display_normal_message(htmlentities($this_tag)); //debug
1940
            if ( preg_match("~".$param_name."[\s]*=[\s]*[\"]{1}([^\"]+)[\"]{1}~i", $this_tag, $matches)) {
1941
                $file_path_list[] = $matches[1]; // older
1942
                $href_list[] = $matches[0]; // to also add target="_self"
1943
            }
1944
        }
1945
    }
1946
1947
    // Replace the original tags by the correct ones
1948
1949
    for ($count = 0; $count < sizeof($href_list); $count++) {
1950
        $replace_what[$count] = $href_list[$count];
1951
1952
        $is_absolute_hyperlink = strpos($replace_what[$count], 'http');
1953
        $is_local_anchor = strpos($replace_what[$count], '#');
1954
        if (!$is_absolute_hyperlink && !$is_local_anchor) {
1955
            if ((strpos($replace_what[$count], 'showinframes.php') === false)
1956
                && (strpos($replace_what[$count], 'download.php') === false)
1957
                && (strpos($replace_what[$count], 'mailto') === false)) {
1958
1959
                // Fix the link to use download.php or showinframes.php
1960
                if (preg_match("/<a([\s]*[\"\/:'=a-z0-9]*){5}href[^<]*>/i", $tag_list[$count])) {
1961
                    $replace_by[$count] = " $param_name =\"showinframes.php?file=" . $upload_path.$file_path_list[$count]."\" target=\"_self\" ";
1962
                } else {
1963
                    $replace_by[$count] = " $param_name =\"download.php?doc_url=" . $upload_path.$file_path_list[$count]."\" ";
1964
                }
1965
            } else {
1966
                // "mailto" or url already fixed, leave as is
1967
                //$message .= "Already fixed or contains mailto: ";
1968
                $replace_by[$count] = $replace_what[$count];
1969
            }
1970
        } elseif ($is_absolute_hyperlink) {
1971
            //$message .= "Absolute hyperlink, don't change, add target=_self: ";
1972
            $replace_by[$count] = " $param_name=\"" . $file_path_list[$count] . "\" target =\"_self\"";
1973
        } else {
1974
            // Don't change anything
1975
            //$message .= "Local anchor, don't change: ";
1976
            $replace_by[$count] = $replace_what[$count];
1977
        }
1978
        //$message .= "In tag $count, <b>" . htmlentities($tag_list[$count])
1979
        //	. "</b>, parameter <b>" . $replace_what[$count] . "</b> replaced by <b>" . $replace_by[$count] . "</b><br>"; //debug
1980
    }
1981
    //if ($message) api_display_debug_info($message); //debug
1982
    $buffer = str_replace($replace_what, $replace_by, $buffer);
1983
1984
    return $buffer;
1985
}
1986
1987
/**
1988
 * Checks the extension of a file, if it's .htm or .html
1989
 * we use search_img_from_html to get all image paths in the file
1990
 *
1991
 * @param string $file
1992
 * @return array paths
1993
 * @see check_for_missing_files() uses search_img_from_html()
1994
 */
1995
function check_for_missing_files($file)
1996
{
1997
    if (strrchr($file, '.') == '.htm' || strrchr($file, '.') == '.html') {
1998
        $img_file_path = search_img_from_html($file);
1999
        return $img_file_path;
2000
    }
2001
    return false;
2002
}
2003
2004
/**
2005
 * This function builds a form that asks for the missing images in a html file
2006
 * maybe we should do this another way?
2007
 *
2008
 * @param array $missing_files
2009
 * @param string $upload_path
2010
 * @param string $file_name
2011
 * @return string the form
2012
 */
2013
function build_missing_files_form($missing_files, $upload_path, $file_name)
2014
{
2015
    // Do we need a / or not?
2016
    $added_slash = ($upload_path == '/') ? '' : '/';
2017
    $folder_id      = DocumentManager::get_document_id(api_get_course_info(), $upload_path);
2018
    // Build the form
2019
    $form = "<p><strong>".get_lang('MissingImagesDetected')."</strong></p>"
2020
        ."<form method=\"post\" action=\"".api_get_self()."\" enctype=\"multipart/form-data\">"
2021
        // Related_file is the path to the file that has missing images
2022
        ."<input type=\"hidden\" name=\"related_file\" value=\"".$upload_path.$added_slash.$file_name."\" />"
2023
        ."<input type=\"hidden\" name=\"upload_path\" value=\"".$upload_path."\" />"
2024
        ."<input type=\"hidden\" name=\"id\" value=\"".$folder_id."\" />"
2025
        ."<table border=\"0\">";
2026
    foreach ($missing_files as & $this_img_file_path) {
2027
        $form .= "<tr>"
2028
            ."<td>".basename($this_img_file_path)." : </td>"
2029
            ."<td>"
2030
            ."<input type=\"file\" name=\"img_file[]\"/>"
2031
            ."<input type=\"hidden\" name=\"img_file_path[]\" value=\"".$this_img_file_path."\" />"
2032
            ."</td>"
2033
            ."</tr>";
2034
    }
2035
    $form .= "</table>"
2036
        ."<button type='submit' name=\"cancel_submit_image\" value=\"".get_lang('Cancel')."\" class=\"cancel\">".get_lang('Cancel')."</button>"
2037
        ."<button type='submit' name=\"submit_image\" value=\"".get_lang('Ok')."\" class=\"save\">".get_lang('Ok')."</button>"
2038
        ."</form>";
2039
    return $form;
2040
}
2041
2042
/**
2043
 * This recursive function can be used during the upgrade process form older
2044
 * versions of Chamilo
2045
 * It crawls the given directory, checks if the file is in the DB and adds
2046
 * it if it's not
2047
 *
2048
 * @param array $courseInfo
2049
 * @param array $userInfo
2050
 * @param string $base_work_dir
2051
 * @param string $folderPath
2052
 * @param int $sessionId
2053
 * @param int $groupId
2054
 * @param bool $output
2055
 * @param array $parent
2056
 * @param string $uploadPath
0 ignored issues
show
Bug introduced by
There is no parameter named $uploadPath. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2057
 *
2058
 */
2059
function add_all_documents_in_folder_to_database(
2060
    $courseInfo,
2061
    $userInfo,
2062
    $base_work_dir,
2063
    $folderPath,
2064
    $sessionId = 0,
2065
    $groupId = 0,
2066
    $output = false,
2067
    $parent = array()
2068
) {
2069
    if (empty($userInfo) || empty($courseInfo)) {
2070
        return false;
2071
    }
2072
2073
    $userId = $userInfo['user_id'];
2074
2075
    // Open dir
2076
    $handle = opendir($folderPath);
2077
    if (is_dir($folderPath)) {
2078
        // Run trough
2079
        while ($file = readdir($handle)) {
2080
2081
            if ($file == '.' || $file == '..') {
2082
                continue;
2083
            }
2084
2085
            $parentPath = null;
2086
2087
            if (!empty($parent) && isset($parent['path'])) {
2088
                $parentPath = $parent['path'];
2089
                if ($parentPath == '/') {
2090
                    $parentPath = null;
2091
                }
2092
            }
2093
2094
            $completePath = $parentPath.'/'.$file;
2095
            $sysFolderPath = $folderPath.'/'.$file;
2096
2097
            // Is directory?
2098
            if (is_dir($sysFolderPath)) {
2099
2100
                $folderExists = DocumentManager::folderExists(
2101
                    $completePath,
2102
                    $courseInfo,
2103
                    $sessionId,
2104
                    $groupId
2105
                );
2106
2107
                if ($folderExists === true) {
2108
                    $documentId = DocumentManager::get_document_id($courseInfo, $completePath, $sessionId);
2109
                    if ($documentId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $documentId of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2110
                        $newFolderData = DocumentManager::get_document_data_by_id($documentId, $courseInfo, false, $sessionId);
2111
                    }
2112
                } else {
2113
                   $newFolderData = create_unexisting_directory(
2114
                        $courseInfo,
2115
                        $userId,
2116
                        $sessionId,
2117
                        $groupId,
2118
                        null,
2119
                        $base_work_dir,
2120
                        $completePath,
2121
                        null,
2122
                        null,
2123
                        false
2124
                    );
2125
                }
2126
2127
                // Recursive
2128
                add_all_documents_in_folder_to_database(
2129
                    $courseInfo,
2130
                    $userInfo,
2131
                    $base_work_dir,
2132
                    $sysFolderPath,
2133
                    $sessionId,
2134
                    $groupId,
2135
                    $output,
2136
                    $newFolderData
0 ignored issues
show
Security Bug introduced by
It seems like $newFolderData defined by create_unexisting_direct...ath, null, null, false) on line 2113 can also be of type false; however, add_all_documents_in_folder_to_database() does only seem to accept array, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
2137
                );
2138
            } else {
2139
                // Rename
2140
                $uploadedFile = array(
2141
                    'name' => $file,
2142
                    'tmp_name' => $sysFolderPath,
2143
                    'size' => filesize($sysFolderPath),
2144
                    'type' => null,
2145
                    'from_file' => true,
2146
                    'move_file' => true
2147
                );
2148
2149
                handle_uploaded_document(
2150
                    $courseInfo,
2151
                    $uploadedFile,
2152
                    $base_work_dir,
2153
                    $parentPath,
2154
                    $userId,
2155
                    $groupId,
2156
                    null,
2157
                    0,
2158
                    'overwrite',
2159
                    $output,
2160
                    false,
2161
                    null,
2162
                    $sessionId
2163
                );
2164
            }
2165
        }
2166
    }
2167
}
2168