Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.13% covered (warning)
81.13%
245 / 302
61.11% covered (warning)
61.11%
11 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
SeedDMS_Core_File
81.13% covered (warning)
81.13%
245 / 302
61.11% covered (warning)
61.11%
11 / 18
106.86
0.00% covered (danger)
0.00%
0 / 1
 renameFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 copyFile
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 linkFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 moveFile
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 fileSize
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 mimetype
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
306
 format_filesize
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 parse_filesize
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
8
 file_exists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checksum
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fileExtension
100.00% covered (success)
100.00%
189 / 189
100.00% covered (success)
100.00%
1 / 1
7
 renameDir
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeDir
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 removeDir
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
8.23
 copyDir
69.23% covered (warning)
69.23%
9 / 13
0.00% covered (danger)
0.00%
0 / 1
9.86
 moveDir
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 gzcompressfile
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2declare(strict_types=1);
3
4/**
5 * Implementation of various file system operations
6 *
7 * @category   DMS
8 * @package    SeedDMS_Core
9 * @license    GPL 2
10 * @author     Uwe Steinmann <uwe@steinmann.cx>
11 * @copyright  Copyright (C) 2002-2005 Markus Westphal,
12 *             2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli,
13 *             2010-2024 Uwe Steinmann
14 */
15
16/**
17 * Class with file operations in the document management system
18 *
19 * Use the methods of this class only for files below the content
20 * directory but not for temporäry files, cache files or log files.
21 *
22 * @category   DMS
23 * @package    SeedDMS_Core
24 * @author     Markus Westphal, Malcolm Cowe, Uwe Steinmann <uwe@steinmann.cx>
25 * @copyright  Copyright (C) 2002-2005 Markus Westphal,
26 *             2006-2008 Malcolm Cowe, 2010 Matteo Lucarelli,
27 *             2010-2024 Uwe Steinmann
28 */
29class SeedDMS_Core_File {
30    /**
31     * Rename a file
32     *
33     * @param $old old name of file
34     * @param $new new name of file
35     * @return bool
36     */
37    public static function renameFile($old, $new) { /* {{{ */
38        return @rename($old, $new);
39    } /* }}} */
40
41    /**
42     * Delete a file
43     *
44     * @param $file name of file
45     * @return bool
46     */
47    public static function removeFile($file) { /* {{{ */
48        return @unlink($file);
49    } /* }}} */
50
51    /**
52     * Make copy of file
53     *
54     * @param $source name of file to be copied
55     * @param $target name of target file
56     * @return bool
57     */
58    public static function copyFile($source, $target) { /* {{{ */
59        return @copy($source, $target);
60    } /* }}} */
61
62    /**
63     * Create symbolic link
64     *
65     * @param $source name of link
66     * @param $target name of file to link
67     * @return bool
68     */
69    public static function linkFile($source, $target) { /* {{{ */
70        return symlink($source, $target);
71    } /* }}} */
72
73    /**
74     * Move file
75     *
76     * @param $source name of file to be moved
77     * @param $target name of target file
78     * @return bool
79     */
80    public static function moveFile($source, $target) { /* {{{ */
81        if (!self::copyFile($source, $target))
82            return false;
83        return self::removeFile($source);
84    } /* }}} */
85
86    /**
87     * Return size of file
88     *
89     * @param $file name of file
90     * @return bool|int
91     */
92    public static function fileSize($file) { /* {{{ */
93        if (!$a = @fopen($file, 'r'))
94            return false;
95        fseek($a, 0, SEEK_END);
96        $filesize = ftell($a);
97        fclose($a);
98        return $filesize;
99    } /* }}} */
100
101    /**
102     * Return the mimetype of a given file
103     *
104     * This method uses finfo to determine the mimetype
105     * but will correct some mimetypes which are
106     * not propperly determined or could be more specific, e.g. text/plain
107     * when it is actually text/markdown. In thoses cases
108     * the file extension will be taken into account.
109     *
110     * @param string $filename name of file on disc
111     * @return string mimetype
112     */
113    public static function mimetype($filename) { /* {{{ */
114        $finfo = finfo_open(FILEINFO_MIME_TYPE);
115        $mimetype = finfo_file($finfo, $filename);
116
117        $lastDotIndex = strrpos($filename, ".");
118        if ($lastDotIndex === false) $fileType = ".";
119        else $fileType = substr($filename, $lastDotIndex);
120
121        switch ($mimetype) {
122        case 'application/octet-stream':
123        case 'text/plain':
124            if ($fileType == '.md')
125                $mimetype = 'text/markdown';
126            elseif ($fileType == '.tex')
127                $mimetype = 'text/x-tex';
128            elseif ($fileType == '.docx')
129                $mimetype = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
130            elseif ($fileType == '.pkpass')
131                $mimetype = 'application/vnd.apple.pkpass';
132            break;
133        case 'application/zip':
134            if ($fileType == '.pkpass')
135                $mimetype = 'application/vnd.apple.pkpass';
136            elseif ($fileType == '.numbers')
137                $mimetype = 'application/vnd.apple.numbers';
138            elseif ($fileType == '.pages')
139                $mimetype = 'application/vnd.apple.pages';
140            elseif ($fileType == '.key')
141                $mimetype = 'application/vnd.apple.keynote';
142            break;
143        case 'application/gzip':
144            if ($fileType == '.xopp')
145                $mimetype = 'application/x-xopp';
146            break;
147        case 'text/xml':
148            if ($fileType == '.html')
149                $mimetype = 'text/html';
150            break;
151        }
152        return $mimetype;
153    } /* }}} */
154
155    /**
156     * Turn an integer into a string with units for bytes
157     *
158     * @param integer $size
159     * @param array $sizes list of units for 10^0, 10^3, 10^6, ..., 10^(n*3) bytes
160     * @return string
161     */
162    public static function format_filesize($size, $sizes = array('Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')) { /* {{{ */
163        if ($size == 0) return('0 Bytes');
164        if ($size == 1) return('1 Byte');
165        return (round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $sizes[$i]);
166    } /* }}} */
167
168    /**
169     * Parses a string like '[0-9]+ *[BKMGT]*' into an integer
170     *
171     * B,K,M,G,T stand for byte, kilo byte, mega byte, giga byte, tera byte
172     * If the last character is omitted, bytes are assumed. Arbitrary
173     * spaces between the number and the unit are allowed.
174     *
175     * @param $str string to be parsed
176     * @return bool|int
177     */
178    public static function parse_filesize($str) { /* {{{ */
179        if (!preg_match('/^([0-9]+) *([BKMGT]*)$/', trim($str), $matches))
180            return false;
181        $value = $matches[1];
182        $unit = $matches[2] ? $matches[2] : 'B';
183        switch ($unit) {
184            case 'T':
185                return $value * 1024 * 1024 * 1024 *1024;
186                break;
187            case 'G':
188                return $value * 1024 * 1024 * 1024;
189                break;
190            case 'M':
191                return $value * 1024 * 1024;
192                break;
193            case 'K':
194                return $value * 1024;
195                break;
196            default:
197                return (int) $value;
198                break;
199        }
200        return false;
201    } /* }}} */
202
203    /**
204     * Check if file exists
205     *
206     * @param $file name of file to be checked
207     * @return bool true if file exists
208     */
209    public static function file_exists($file) { /* {{{ */
210        return file_exists($file);
211    } /* }}} */
212
213    /**
214     * Calculate the checksum of a file
215     *
216     * This method calculates the md5 sum of the file's content.
217     *
218     * @param $file name of file
219     * @return string md5 sum of file
220     */
221    public static function checksum($file) { /* {{{ */
222        return md5_file($file);
223    } /* }}} */
224
225    /**
226     * Return file extension by mimetype
227     *
228     * This methods returns the common file extension for a given mime type.
229     *
230     * @param $string mimetype
231     * @return string file extension with the dot or an empty string
232     */
233    public static function fileExtension($mimetype) { /* {{{ */
234        switch ($mimetype) {
235        case "application/pdf":
236        case "image/png":
237        case "image/gif":
238        case "image/jpg":
239            $expect = substr($mimetype, -3, 3);
240            break;
241        default:
242            $mime_map = [
243                'video/3gpp2' => '3g2',
244                'video/3gp' => '3gp',
245                'video/3gpp' => '3gp',
246                'application/x-compressed' => '7zip',
247                'audio/x-acc'=> 'aac',
248                'audio/ac3' => 'ac3',
249                'application/postscript' => 'ps',
250                'audio/x-aiff' => 'aif',
251                'audio/aiff' => 'aif',
252                'audio/x-au' => 'au',
253                'video/x-msvideo' => 'avi',
254                'video/msvideo' => 'avi',
255                'video/avi' => 'avi',
256                'application/x-troff-msvideo' => 'avi',
257                'application/macbinary' => 'bin',
258                'application/mac-binary' => 'bin',
259                'application/x-binary' => 'bin',
260                'application/x-macbinary' => 'bin',
261                'image/bmp' => 'bmp',
262                'image/x-bmp' => 'bmp',
263                'image/x-bitmap' => 'bmp',
264                'image/x-xbitmap' => 'bmp',
265                'image/x-win-bitmap' => 'bmp',
266                'image/x-windows-bmp' => 'bmp',
267                'image/ms-bmp' => 'bmp',
268                'image/x-ms-bmp' => 'bmp',
269                'application/bmp' => 'bmp',
270                'application/x-bmp' => 'bmp',
271                'application/x-win-bitmap' => 'bmp',
272                'application/cdr' => 'cdr',
273                'application/coreldraw' => 'cdr',
274                'application/x-cdr' => 'cdr',
275                'application/x-coreldraw' => 'cdr',
276                'image/cdr' => 'cdr',
277                'image/x-cdr' => 'cdr',
278                'zz-application/zz-winassoc-cdr' => 'cdr',
279                'application/mac-compactpro' => 'cpt',
280                'application/pkix-crl' => 'crl',
281                'application/pkcs-crl' => 'crl',
282                'application/x-x509-ca-cert' => 'crt',
283                'application/pkix-cert' => 'crt',
284                'text/css' => 'css',
285                'text/x-comma-separated-values' => 'csv',
286                'text/comma-separated-values' => 'csv',
287                'application/vnd.msexcel' => 'xls',
288                'application/x-director' => 'dcr',
289                'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
290                'application/x-dvi' => 'dvi',
291                'message/rfc822' => 'eml',
292                'application/x-msdownload' => 'exe',
293                'video/x-f4v' => 'f4v',
294                'audio/x-flac' => 'flac',
295                'video/x-flv' => 'flv',
296                'image/gif' => 'gif',
297                'application/gpg-keys' => 'gpg',
298                'application/x-gtar' => 'tar.gz',
299                'application/x-gzip' => 'gzip',
300                'application/mac-binhex40' => 'hqx',
301                'application/mac-binhex' => 'hqx',
302                'application/x-binhex40' => 'hqx',
303                'application/x-mac-binhex40' => 'hqx',
304                'text/html' => 'html',
305                'image/x-icon' => 'ico',
306                'image/x-ico' => 'ico',
307                'image/vnd.microsoft.icon' => 'ico',
308                'text/calendar' => 'ics',
309                'application/java-archive' => 'jar',
310                'application/x-java-application' => 'jar',
311                'application/x-jar' => 'jar',
312                'image/jp2' => 'jp2',
313                'video/mj2'=> 'jp2',
314                'image/jpx' => 'jp2',
315                'image/jpm' => 'jp2',
316                'image/jpeg' => 'jpeg',
317                'image/pjpeg' => 'jpeg',
318                'application/x-javascript' => 'js',
319                'application/json' => 'json',
320                'text/json' => 'json',
321                'application/vnd.google-earth.kml+xml' => 'kml',
322                'application/vnd.google-earth.kmz' => 'kmz',
323                'text/x-log' => 'log',
324                'audio/x-m4a' => 'm4a',
325                'application/vnd.mpegurl' => 'm4u',
326                'text/markdown' => 'md',
327                'audio/midi' => 'mid',
328                'application/vnd.mif' => 'mif',
329                'video/quicktime' => 'mov',
330                'video/x-sgi-movie' => 'movie',
331                'audio/mpeg' => 'mp3',
332                'audio/mpg' => 'mp3',
333                'audio/mpeg3' => 'mp3',
334                'audio/mp3' => 'mp3',
335                'video/mp4' => 'mp4',
336                'video/mpeg' => 'mpeg',
337                'application/oda' => 'oda',
338                'audio/ogg' => 'ogg',
339                'video/ogg' => 'ogg',
340                'application/ogg' => 'ogg',
341                'application/x-pkcs10' => 'p10',
342                'application/pkcs10' => 'p10',
343                'application/x-pkcs12' => 'p12',
344                'application/x-pkcs7-signature' => 'p7a',
345                'application/pkcs7-mime' => 'p7c',
346                'application/x-pkcs7-mime' => 'p7c',
347                'application/x-pkcs7-certreqresp' => 'p7r',
348                'application/pkcs7-signature' => 'p7s',
349                'application/pdf' => 'pdf',
350                'application/octet-stream' => 'pdf',
351                'application/x-x509-user-cert' => 'pem',
352                'application/x-pem-file' => 'pem',
353                'application/pgp' => 'pgp',
354                'application/x-httpd-php' => 'php',
355                'application/php' => 'php',
356                'application/x-php' => 'php',
357                'text/php' => 'php',
358                'text/x-php' => 'php',
359                'application/x-httpd-php-source' => 'php',
360                'image/png' => 'png',
361                'image/x-png' => 'png',
362                'application/powerpoint' => 'ppt',
363                'application/vnd.ms-powerpoint' => 'ppt',
364                'application/vnd.ms-office' => 'ppt',
365                'application/msword' => 'doc',
366                'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
367                'application/x-photoshop' => 'psd',
368                'image/vnd.adobe.photoshop' => 'psd',
369                'audio/x-realaudio' => 'ra',
370                'audio/x-pn-realaudio' => 'ram',
371                'application/x-rar' => 'rar',
372                'application/rar' => 'rar',
373                'application/x-rar-compressed' => 'rar',
374                'audio/x-pn-realaudio-plugin' => 'rpm',
375                'application/x-pkcs7' => 'rsa',
376                'text/rtf' => 'rtf',
377                'text/richtext' => 'rtx',
378                'video/vnd.rn-realvideo' => 'rv',
379                'application/x-stuffit' => 'sit',
380                'application/smil' => 'smil',
381                'text/srt' => 'srt',
382                'image/svg+xml' => 'svg',
383                'application/x-shockwave-flash' => 'swf',
384                'application/x-tar' => 'tar',
385                'application/x-gzip-compressed' => 'tgz',
386                'image/tiff' => 'tiff',
387                'text/plain' => 'txt',
388                'text/x-vcard' => 'vcf',
389                'application/videolan' => 'vlc',
390                'text/vtt' => 'vtt',
391                'audio/x-wav' => 'wav',
392                'audio/wave' => 'wav',
393                'audio/wav' => 'wav',
394                'application/wbxml' => 'wbxml',
395                'video/webm' => 'webm',
396                'audio/x-ms-wma' => 'wma',
397                'application/wmlc' => 'wmlc',
398                'video/x-ms-wmv' => 'wmv',
399                'video/x-ms-asf' => 'wmv',
400                'application/xhtml+xml' => 'xhtml',
401                'application/excel' => 'xl',
402                'application/msexcel' => 'xls',
403                'application/x-msexcel' => 'xls',
404                'application/x-ms-excel' => 'xls',
405                'application/x-excel' => 'xls',
406                'application/x-dos_ms_excel' => 'xls',
407                'application/xls' => 'xls',
408                'application/x-xls' => 'xls',
409                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
410                'application/vnd.ms-excel' => 'xlsx',
411                'application/xml' => 'xml',
412                'text/xml' => 'xml',
413                'text/xsl' => 'xsl',
414                'application/xspf+xml' => 'xspf',
415                'application/x-compress' => 'z',
416                'application/x-zip' => 'zip',
417                'application/zip' => 'zip',
418                'application/x-zip-compressed' => 'zip',
419                'application/s-compressed' => 'zip',
420                'multipart/x-zip' => 'zip',
421                'text/x-scriptzsh' => 'zsh',
422            ];
423            $expect = isset($mime_map[$mimetype]) === true ? $mime_map[$mimetype] : '';
424        }
425        return $expect;
426    } /* }}} */
427
428    /**
429     * Rename a directory
430     *
431     * @param $old name of directory to be renamed
432     * @param $new new name of directory
433     * @return bool
434     */
435    public static function renameDir($old, $new) { /* {{{ */
436        return @rename($old, $new);
437    } /* }}} */
438
439    /**
440     * Create a directory
441     *
442     * @param $path path of new directory
443     * @return bool
444     */
445    public static function makeDir($path) { /* {{{ */
446        if (!is_dir($path)) {
447            $res = @mkdir($path, 0777, true);
448            if (!$res) return false;
449        }
450
451        return true;
452
453/* some old code
454        if (strncmp($path, DIRECTORY_SEPARATOR, 1) == 0) {
455            $mkfolder = DIRECTORY_SEPARATOR;
456        }
457        else {
458            $mkfolder = "";
459        }
460        $path = preg_split("/[\\\\\/]/", $path);
461        for($i = 0; isset( $path[$i]); $i++)
462        {
463            if (!strlen(trim($path[$i])))continue;
464            $mkfolder .= $path[$i];
465
466            if (!is_dir( $mkfolder)){
467                $res = @mkdir("$mkfolder", 0777);
468                if (!$res) return false;
469            }
470            $mkfolder .= DIRECTORY_SEPARATOR;
471        }
472
473        return true;
474
475        // patch from alekseynfor safe_mod or open_basedir
476
477        global $settings;
478        $path = substr_replace ($path, "/", 0, strlen($settings->_contentDir));
479        $mkfolder = $settings->_contentDir;
480
481        $path = preg_split( "/[\\\\\/]/", $path);
482
483        for($i = 0; isset($path[$i]); $i++)
484        {
485            if (!strlen(trim($path[$i])))continue;
486            $mkfolder .= $path[$i];
487
488            if (!is_dir( $mkfolder)){
489                $res = @mkdir("$mkfolder", 0777);
490                if (!$res) return false;
491            }
492            $mkfolder .= DIRECTORY_SEPARATOR;
493        }
494
495        return true;
496*/
497    } /* }}} */
498
499    /**
500     * Delete directory
501     *
502     * This method recursively deletes a directory including all its files.
503     *
504     * @param $path path of directory to be deleted
505     * @return bool
506     */
507    public static function removeDir($path) { /* {{{ */
508        $handle = @opendir($path);
509        if (!$handle)
510            return false;
511        while ($entry = @readdir($handle)) {
512            if ($entry == ".." || $entry == ".")
513                continue;
514            elseif (is_dir($path . DIRECTORY_SEPARATOR . $entry)) {
515                if (!self::removeDir($path . DIRECTORY_SEPARATOR . $entry))
516                    return false;
517            } else {
518                if (!@unlink($path . DIRECTORY_SEPARATOR . $entry))
519                    return false;
520            }
521        }
522        @closedir($handle);
523        return @rmdir($path);
524    } /* }}} */
525
526    /**
527     * Copy a directory
528     *
529     * This method copies a directory recursively including all its files.
530     *
531     * @param $sourcePath path of directory to copy
532     * @param $targetPath path of target directory
533     * @return bool
534     */
535    public static function copyDir($sourcePath, $targetPath) { /* {{{ */
536        if (mkdir($targetPath, 0777)) {
537            $handle = @opendir($sourcePath);
538            while ($entry = @readdir($handle)) {
539                if ($entry == ".." || $entry == ".")
540                    continue;
541                elseif (is_dir($sourcePath . $entry)) {
542                    if (!self::copyDir($sourcePath . DIRECTORY_SEPARATOR . $entry, $targetPath . DIRECTORY_SEPARATOR . $entry))
543                        return false;
544                } else {
545                    if (!@copy($sourcePath . DIRECTORY_SEPARATOR . $entry, $targetPath . DIRECTORY_SEPARATOR . $entry))
546                        return false;
547                }
548            }
549            @closedir($handle);
550        } else {
551            return false;
552        }
553
554        return true;
555    } /* }}} */
556
557    /**
558     * Move a directory
559     *
560     * @param $sourcePath path of directory to be moved
561     * @param $targetPath new name of directory
562     * @return bool
563     */
564    public static function moveDir($sourcePath, $targetPath) { /* {{{ */
565        if (!self::copyDir($sourcePath, $targetPath))
566            return false;
567        return self::removeDir($sourcePath);
568    } /* }}} */
569
570    // code by Kioob (php.net manual)
571    /**
572     * Compress a file with gzip
573     *
574     * @param $source path of file to be compressed
575     * @param bool $level level of compression
576     * @return bool|string file name of compressed file or false in case of an error
577     */
578    public static function gzcompressfile($source, $level = false) { /* {{{ */
579        $dest = $source.'.gz';
580        $mode = 'wb'.$level;
581        $error = false;
582        if ($fp_out = @gzopen($dest, $mode)) {
583            if ($fp_in = @fopen($source, 'rb')) {
584                while (!feof($fp_in))
585                    @gzwrite($fp_out, fread($fp_in, 1024*512));
586                @fclose($fp_in);
587            } else {
588                $error = true;
589            }
590            @gzclose($fp_out);
591        } else {
592            $error = true;
593        }
594
595        if ($error) return false;
596        else return $dest;
597    } /* }}} */
598}