Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/exercise/hotpotatoes.lib.php (4 issues)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Code library for HotPotatoes integration.
6
 *
7
 * @package chamilo.exercise
8
 *
9
 * @author Istvan Mandak (original author)
10
 */
11
12
/* TODO: This is a global variable with too simple name, conflicts are possible.
13
Better eliminate it. Correct the test unit too. */
14
$dbTable = Database::get_course_table(TABLE_DOCUMENT);
15
16
/**
17
 * Creates a hotpotato directory.
18
 *
19
 * If a directory of that name already exists, don't create any.
20
 * If a file of that name exists, remove it and create a directory.
21
 *
22
 * @param string $base_work_dir Wanted path
23
 *
24
 * @return bool Always true so far
25
 */
26
function hotpotatoes_init($base_work_dir)
27
{
28
    //global $_course, $_user;
29
    $document_path = $base_work_dir.'/';
30
    if (!is_dir($document_path)) {
31
        if (is_file($document_path)) {
32
            @unlink($document_path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

32
            /** @scrutinizer ignore-unhandled */ @unlink($document_path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
33
        }
34
        @mkdir($document_path, api_get_permissions_for_new_directories());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

34
        /** @scrutinizer ignore-unhandled */ @mkdir($document_path, api_get_permissions_for_new_directories());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
35
36
        return true;
37
    } else {
38
        return false;
39
    }
40
    //why create a .htaccess here?
41
    //if (!is_file($document_path.".htacces"))
42
    //{
43
    //        if (!($fp = fopen($document_path.".htaccess", "w"))) {
44
    //    }
45
    //    $str = "order deny,allow\nallow from all";
46
    //    if (!fwrite($fp,$str)) { }
47
    //}
48
}
49
50
/**
51
 * Gets the title of the quiz file given as parameter.
52
 *
53
 * @param string $fname File name
54
 * @param string $fpath File path
55
 *
56
 * @return string The exercise title
57
 */
58
function GetQuizName($fname, $fpath)
59
{
60
    $title = GetComment($fname);
61
    if (trim($title) == '') {
62
        if (file_exists($fpath.$fname)) {
63
            if (!($fp = @fopen($fpath.$fname, 'r'))) {
64
                //die('Could not open Quiz input.');
65
                return basename($fname);
66
            }
67
68
            $contents = @fread($fp, filesize($fpath.$fname));
69
            @fclose($fp);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

69
            /** @scrutinizer ignore-unhandled */ @fclose($fp);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
70
71
            $title = api_get_title_html($contents);
72
        }
73
    }
74
    if ($title == '') {
75
        $title = basename($fname);
76
    }
77
78
    return (string) $title;
79
}
80
81
/**
82
 * Gets the comment about a file from the corresponding database record.
83
 *
84
 * @param string $path       File path
85
 * @param string $courseCode (if not given, the course will be taken from the context)
86
 *
87
 * @return string comment from the database record
88
 *                Added conditional to the table if is empty
89
 */
90
function GetComment($path, $courseCode = '')
91
{
92
    $dbTable = Database::get_course_table(TABLE_DOCUMENT);
93
    $course_info = api_get_course_info($courseCode);
94
    $path = Database::escape_string($path);
95
    if (!empty($course_info) && !empty($path)) {
96
        $query = "SELECT comment FROM $dbTable WHERE c_id = {$course_info['real_id']}";
97
        $result = Database::query($query);
98
        while ($row = Database::fetch_array($result)) {
99
            return $row[0];
100
        }
101
    }
102
103
    return null;
104
}
105
106
/**
107
 * Sets the comment in the database for a particular path.
108
 *
109
 * @param string $path    File path
110
 * @param string $comment Comment to set
111
 *
112
 * @return Doctrine\DBAL\Driver\Statement|null
113
 *                                             Result of the database operation
114
 *                                             (Database::query will output some message directly on error anyway)
115
 */
116
function SetComment($path, $comment)
117
{
118
    $dbTable = Database::get_course_table(TABLE_DOCUMENT);
119
    $path = Database::escape_string($path);
120
    $comment = Database::escape_string($comment);
121
    $course_id = api_get_course_int_id();
122
    $query = "UPDATE $dbTable SET comment = '$comment'
123
              WHERE $course_id AND path = '$path'";
124
    $result = Database::query($query);
125
126
    return $result;
127
}
128
129
/**
130
 * Reads the file contents into a string.
131
 *
132
 * @param string $full_file_path Urlencoded path
133
 *
134
 * @return string The file contents or false on security error
135
 */
136
function ReadFileCont($full_file_path)
137
{
138
    if (empty($full_file_path)) {
139
        return false;
140
    }
141
    if (Security::check_abs_path(dirname($full_file_path).'/', api_get_path(SYS_COURSE_PATH))) {
142
        if (is_file($full_file_path)) {
143
            if (!($fp = fopen(urldecode($full_file_path), 'r'))) {
144
                return '';
145
            }
146
            $contents = fread($fp, filesize($full_file_path));
147
            fclose($fp);
148
149
            return $contents;
150
        }
151
    }
152
153
    return false;
154
}
155
156
/**
157
 * Writes the file contents into the given file path.
158
 *
159
 * @param string $full_file_path Urlencoded path
160
 * @param string $content        The file contents
161
 *
162
 * @return bool True on success, false on security error
163
 */
164
function WriteFileCont($full_file_path, $content)
165
{
166
    // Check if this is not an attack, trying to get into other directories or something like that.
167
    $_course = api_get_course_info();
168
    if (Security::check_abs_path(
169
        dirname($full_file_path).'/',
170
        api_get_path(SYS_COURSE_PATH).$_course['path'].'/'
171
    )) {
172
        // Check if this is not an attack, trying to upload a php file or something like that.
173
        if (basename($full_file_path) != Security::filter_filename(basename($full_file_path))) {
174
            return false;
175
        }
176
        //if (!($fp = fopen(urldecode($full_file_path), 'w'))) {
177
        //die('Could not open Quiz input.');
178
        //}
179
        $fp = fopen(urldecode($full_file_path), 'w');
180
        if ($fp !== false) {
181
            fwrite($fp, $content);
182
            fclose($fp);
183
184
            return true;
185
        }
186
    }
187
188
    return false;
189
}
190
191
/**
192
 * Gets the name of an img whose path is given (without directories or extensions).
193
 *
194
 * @param string $imageTag An image tag (<img src="...." ...>)
195
 *
196
 * @return string The image file name or an empty string
197
 */
198
function GetImgName($imageTag)
199
{
200
    // Select src tag from img tag.
201
    $match = [];
202
    //preg_match('/(src=(["\'])1.*(["\'])1)/i', $imageTag, $match);            //src
203
    preg_match('/src(\s)*=(\s)*[\'"]([^\'"]*)[\'"]/i', $imageTag, $match); //get the img src as contained between " or '
204
    //list($key, $srctag) = each($match);
205
    $src = $match[3];
206
    //$src = substr($srctag, 5, (strlen($srctag) - 7));
207
    if (stristr($src, 'http') === false) {
208
        // Valid or invalid image name.
209
        if ($src == '') {
210
            return '';
211
        } else {
212
            $tmp_src = basename($src);
213
            if ($tmp_src == '') {
214
                return $src;
215
            } else {
216
                return $tmp_src;
217
            }
218
        }
219
    } else {
220
        // The img tag contained "http", which means it is probably external. Ignore it.
221
        return '';
222
    }
223
}
224
225
/**
226
 * Gets the source path of an image tag.
227
 *
228
 * @param string $imageTag An image tag
229
 *
230
 * @return string The image source or ""
231
 */
232
function GetSrcName($imageTag)
233
{
234
    // Select src tag from img tag.
235
    $match = [];
236
    preg_match("|(src=\".*\" )|U", $imageTag, $match); //src
237
    $srctag = reset($match);
238
    $src = substr($srctag, 5, (strlen($srctag) - 7));
239
    if (stristr($src, 'http') === false) {
240
        // valid or invalid image name
241
        return $src;
242
    } else {
243
        return '';
244
    }
245
}
246
247
/**
248
 * Gets the image parameters from an image path.
249
 *
250
 * @param string $fname     File name
251
 * @param string $fpath     File path
252
 * @param array  $imgparams Reference to a list of image parameters (emptied, then used to return results)
253
 * @param int    $imgcount  Reference to a counter of images (emptied, then used to return results)
254
 */
255
function GetImgParams($fname, $fpath, &$imgparams, &$imgcount)
256
{
257
    // Select img tags from context.
258
    $imgparams = [];
259
    //phpinfo();
260
    $contents = ReadFileCont("$fpath"."$fname");
261
    $matches = [];
262
    preg_match_all('(<img .*>)', $contents, $matches);
263
    $imgcount = 0;
264
    foreach ($matches as $match) {
265
        // Each match consists of a key and a value.
266
        foreach ($match as $imageTag) {
267
            $imgname = GetImgName($imageTag);
268
            if ($imgname != '' && !in_array($imgname, $imgparams)) {
269
                array_push($imgparams, $imgname); // name (+ type) of the images in the html test
270
                $imgcount = $imgcount + 1; // number of images in the html test
271
            }
272
        }
273
    }
274
}
275
276
/**
277
 * Generates a list of hidden fields with the image params given as parameter to this function.
278
 *
279
 * @param array $imgparams List of image parameters
280
 *
281
 * @return string String containing the hidden parameters built from the list given
282
 */
283
function GenerateHiddenList($imgparams)
284
{
285
    $list = '';
286
    if (is_array($imgparams)) {
287
        foreach ($imgparams as $string) {
288
            $list .= "<input type=\"hidden\" name=\"imgparams[]\" value=\"$string\" />\n";
289
        }
290
    }
291
292
    return $list;
293
}
294
295
/**
296
 * Searches for a node in the given array.
297
 *
298
 * @param array  $array Reference to the array to search
299
 * @param string $node  Node we are looking for in the array
300
 *
301
 * @return mixed Node name or false if not found
302
 */
303
function myarraysearch(&$array, $node)
304
{
305
    $match = false;
306
    $tmp_array = [];
307
    for ($i = 0; $i < count($array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

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

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

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
308
        if (!strcmp($array[$i], $node)) {
309
            $match = $node;
310
        } else {
311
            array_push($tmp_array, $array[$i]);
312
        }
313
    }
314
    $array = $tmp_array;
315
316
    return $match;
317
}
318
319
/**
320
 * Searches an image name into an array.
321
 *
322
 * @param array  $imgparams Reference to an array to search
323
 * @param string $string    String to look for
324
 *
325
 * @return mixed String given if found, false otherwise
326
 *
327
 * @uses     \myarraysearch    This function is just an additional layer on the myarraysearch() function
328
 */
329
function CheckImageName(&$imgparams, $string)
330
{
331
    $checked = myarraysearch($imgparams, $string);
332
333
    return $checked;
334
}
335
336
/**
337
 * Replaces an image tag by ???
338
 *
339
 * @param string $content The content to replace
340
 *
341
 * @return string The modified content
342
 */
343
function ReplaceImgTag($content)
344
{
345
    $newcontent = $content;
346
    $matches = [];
347
    preg_match_all('(<img .*>)', $content, $matches);
348
    foreach ($matches as $match) {
349
        foreach ($match as $imageTag) {
350
            $imgname = GetSrcName($imageTag);
351
            if ($imgname != '') {
352
                $prehref = $imgname;
353
                $posthref = basename($imgname);
354
                $newcontent = str_replace($prehref, $posthref, $newcontent);
355
            }
356
        }
357
    }
358
359
    return $newcontent;
360
}
361
362
/**
363
 * Fills the folder name up to a certain length with "0".
364
 *
365
 * @param string $name  Original folder name
366
 * @param int    $nsize Length to reach
367
 *
368
 * @return string Modified folder name
369
 */
370
function FillFolderName($name, $nsize)
371
{
372
    $str = '';
373
    for ($i = 0; $i < $nsize - strlen($name); $i++) {
374
        $str .= '0';
375
    }
376
    $str .= $name;
377
378
    return $str;
379
}
380
381
/**
382
 * Generates the HotPotato folder tree.
383
 *
384
 * @param string $folder Folder path
385
 *
386
 * @return string Folder name (modified)
387
 */
388
function GenerateHpFolder($folder)
389
{
390
    $filelist = [];
391
    if ($dir = @opendir($folder)) {
392
        while (($file = readdir($dir)) !== false) {
393
            if ($file != '.') {
394
                if ($file != '..') {
395
                    $full_name = $folder.'/'.$file;
396
                    if (is_dir($full_name)) {
397
                        $filelist[] = $file;
398
                    }
399
                }
400
            }
401
        }
402
    }
403
    $name = '';
404
    $w = true;
405
    while ($w == true) {
406
        $name = FillFolderName(mt_rand(1, 99999), 6);
407
        $checked = myarraysearch($filelist, $name);
408
        // As long as we find the name in the array, continue looping. As soon as we have a new element, quit.
409
        if (!$checked) {
410
            $w = false;
411
        }
412
    }
413
414
    return $name;
415
}
416
417
/**
418
 * Gets the folder name (strips down path).
419
 *
420
 * @param string $fname Path
421
 *
422
 * @return string Folder name stripped down
423
 */
424
function GetFolderName($fname)
425
{
426
    $name = explode('/', $fname);
427
    $name = $name[count($name) - 2];
428
429
    return $name;
430
}
431
432
/**
433
 * Gets the folder path (with out the name of the folder itself) ?
434
 *
435
 * @param string $fname Path
436
 *
437
 * @return string Path stripped down
438
 */
439
function GetFolderPath($fname)
440
{
441
    $str = '';
442
    $name = explode('/', $fname);
443
    for ($i = 0; $i < sizeof($name) - 1; $i++) {
444
        $str = $str.$name[$i].'/';
445
    }
446
447
    return $str;
448
}
449
450
/**
451
 * Checks if there are subfolders.
452
 *
453
 * @param string $path Path
454
 *
455
 * @return int 1 if a subfolder was found, 0 otherwise
456
 */
457
function CheckSubFolder($path)
458
{
459
    $folder = GetFolderPath($path);
460
    $dflag = 0;
461
    if ($dir = @opendir($folder)) {
462
        while (($file = readdir($dir)) !== false) {
463
            if ($file != '.') {
464
                if ($file != '..') {
465
                    $full_name = $folder.'/'.$file;
466
                    if (is_dir($full_name)) {
467
                        $dflag = 1; // first directory
468
                    }
469
                }
470
            }
471
        }
472
    }
473
474
    return $dflag;
475
}
476
477
/**
478
 * Hotpotato Garbage Collector.
479
 *
480
 * @param string $folder  Path
481
 * @param int    $flag    Flag
482
 * @param int    $user_id User id
483
 */
484
function HotPotGCt($folder, $flag, $user_id)
485
{
486
    // Garbage Collector
487
    $filelist = [];
488
    if ($dir = @opendir($folder)) {
489
        while (($file = readdir($dir)) !== false) {
490
            if ($file != '.') {
491
                if ($file != '..') {
492
                    $full_name = $folder.'/'.$file;
493
                    if (is_dir($full_name)) {
494
                        HotPotGCt($folder.'/'.$file, $flag, $user_id);
495
                    } else {
496
                        $filelist[] = $file;
497
                    }
498
                }
499
            }
500
        }
501
        closedir($dir);
502
    }
503
504
    foreach ($filelist as $val) {
505
        if (stristr($val, $user_id.'.t.html')) {
506
            if ($flag == 1) {
507
                my_delete($folder.'/'.$val);
508
            } else {
509
                echo $folder.'/'.$val.'<br />';
510
            }
511
        }
512
    }
513
}
514
515
/**
516
 * Deletes an attempt from TABLE_STATISTIC_TRACK_E_HOTPOTATOES.
517
 *
518
 * @param int $id
519
 */
520
function deleteAttempt($id)
521
{
522
    $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
523
    $id = intval($id);
524
    $sql = "DELETE FROM $table WHERE id = $id";
525
    Database::query($sql);
526
}
527