Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.90% covered (warning)
83.90%
245 / 292
61.11% covered (warning)
61.11%
11 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
SeedDMS_Core_File
83.90% covered (warning)
83.90%
245 / 292
61.11% covered (warning)
61.11%
11 / 18
85.72
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 / 25
0.00% covered (danger)
0.00%
0 / 1
156
 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    static public 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    static public 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    static public 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    static public 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    static public 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    static public 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    static public 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            break;
137        case 'application/gzip':
138            if($fileType == '.xopp')
139                $mimetype = 'application/x-xopp';
140            break;
141        }
142        return $mimetype;
143    } /* }}} */
144
145    /**
146     * Turn an integer into a string with units for bytes
147     *
148     * @param integer $size
149     * @param array $sizes list of units for 10^0, 10^3, 10^6, ..., 10^(n*3) bytes
150     * @return string
151     */
152    static public function format_filesize($size, $sizes = array('Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')) { /* {{{ */
153        if ($size == 0) return('0 Bytes');
154        if ($size == 1) return('1 Byte');
155        return (round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $sizes[$i]);
156    } /* }}} */
157
158    /**
159     * Parses a string like '[0-9]+ *[BKMGT]*' into an integer
160     *
161     * B,K,M,G,T stand for byte, kilo byte, mega byte, giga byte, tera byte
162     * If the last character is omitted, bytes are assumed. Arbitrary
163     * spaces between the number and the unit are allowed.
164     *
165     * @param $str string to be parsed
166     * @return bool|int
167     */
168    static public function parse_filesize($str) { /* {{{ */
169        if(!preg_match('/^([0-9]+) *([BKMGT]*)$/', trim($str), $matches))
170            return false;
171        $value = $matches[1];
172        $unit = $matches[2] ? $matches[2] : 'B';
173        switch($unit) {
174            case 'T':
175                return $value * 1024 * 1024 * 1024 *1024;
176                break;
177            case 'G':
178                return $value * 1024 * 1024 * 1024;
179                break;
180            case 'M':
181                return $value * 1024 * 1024;
182                break;
183            case 'K':
184                return $value * 1024;
185                break;
186            default;
187                return (int) $value;
188                break;
189        }
190        return false;
191    } /* }}} */
192
193    /**
194     * Check if file exists
195     *
196     * @param $file name of file to be checked
197     * @return bool true if file exists
198     */
199    static public function file_exists($file) { /* {{{ */
200        return file_exists($file);
201    } /* }}} */
202
203    /**
204     * Calculate the checksum of a file
205     *
206     * This method calculates the md5 sum of the file's content.
207     *
208     * @param $file name of file
209     * @return string md5 sum of file
210     */
211    static public function checksum($file) { /* {{{ */
212        return md5_file($file);
213    } /* }}} */
214
215    /**
216     * Return file extension by mimetype
217     *
218     * This methods returns the common file extension for a given mime type.
219     *
220     * @param $string mimetype
221     * @return string file extension with the dot or an empty string
222     */
223    static public function fileExtension($mimetype) { /* {{{ */
224        switch($mimetype) {
225        case "application/pdf":
226        case "image/png":
227        case "image/gif":
228        case "image/jpg":
229            $expect = substr($mimetype, -3, 3);
230            break;
231        default:
232            $mime_map = [
233                'video/3gpp2' => '3g2',
234                'video/3gp' => '3gp',
235                'video/3gpp' => '3gp',
236                'application/x-compressed' => '7zip',
237                'audio/x-acc'=> 'aac',
238                'audio/ac3' => 'ac3',
239                'application/postscript' => 'ps',
240                'audio/x-aiff' => 'aif',
241                'audio/aiff' => 'aif',
242                'audio/x-au' => 'au',
243                'video/x-msvideo' => 'avi',
244                'video/msvideo' => 'avi',
245                'video/avi' => 'avi',
246                'application/x-troff-msvideo' => 'avi',
247                'application/macbinary' => 'bin',
248                'application/mac-binary' => 'bin',
249                'application/x-binary' => 'bin',
250                'application/x-macbinary' => 'bin',
251                'image/bmp' => 'bmp',
252                'image/x-bmp' => 'bmp',
253                'image/x-bitmap' => 'bmp',
254                'image/x-xbitmap' => 'bmp',
255                'image/x-win-bitmap' => 'bmp',
256                'image/x-windows-bmp' => 'bmp',
257                'image/ms-bmp' => 'bmp',
258                'image/x-ms-bmp' => 'bmp',
259                'application/bmp' => 'bmp',
260                'application/x-bmp' => 'bmp',
261                'application/x-win-bitmap' => 'bmp',
262                'application/cdr' => 'cdr',
263                'application/coreldraw' => 'cdr',
264                'application/x-cdr' => 'cdr',
265                'application/x-coreldraw' => 'cdr',
266                'image/cdr' => 'cdr',
267                'image/x-cdr' => 'cdr',
268                'zz-application/zz-winassoc-cdr' => 'cdr',
269                'application/mac-compactpro' => 'cpt',
270                'application/pkix-crl' => 'crl',
271                'application/pkcs-crl' => 'crl',
272                'application/x-x509-ca-cert' => 'crt',
273                'application/pkix-cert' => 'crt',
274                'text/css' => 'css',
275                'text/x-comma-separated-values' => 'csv',
276                'text/comma-separated-values' => 'csv',
277                'application/vnd.msexcel' => 'xls',
278                'application/x-director' => 'dcr',
279                'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
280                'application/x-dvi' => 'dvi',
281                'message/rfc822' => 'eml',
282                'application/x-msdownload' => 'exe',
283                'video/x-f4v' => 'f4v',
284                'audio/x-flac' => 'flac',
285                'video/x-flv' => 'flv',
286                'image/gif' => 'gif',
287                'application/gpg-keys' => 'gpg',
288                'application/x-gtar' => 'tar.gz',
289                'application/x-gzip' => 'gzip',
290                'application/mac-binhex40' => 'hqx',
291                'application/mac-binhex' => 'hqx',
292                'application/x-binhex40' => 'hqx',
293                'application/x-mac-binhex40' => 'hqx',
294                'text/html' => 'html',
295                'image/x-icon' => 'ico',
296                'image/x-ico' => 'ico',
297                'image/vnd.microsoft.icon' => 'ico',
298                'text/calendar' => 'ics',
299                'application/java-archive' => 'jar',
300                'application/x-java-application' => 'jar',
301                'application/x-jar' => 'jar',
302                'image/jp2' => 'jp2',
303                'video/mj2'=> 'jp2',
304                'image/jpx' => 'jp2',
305                'image/jpm' => 'jp2',
306                'image/jpeg' => 'jpeg',
307                'image/pjpeg' => 'jpeg',
308                'application/x-javascript' => 'js',
309                'application/json' => 'json',
310                'text/json' => 'json',
311                'application/vnd.google-earth.kml+xml' => 'kml',
312                'application/vnd.google-earth.kmz' => 'kmz',
313                'text/x-log' => 'log',
314                'audio/x-m4a' => 'm4a',
315                'application/vnd.mpegurl' => 'm4u',
316                'text/markdown' => 'md',
317                'audio/midi' => 'mid',
318                'application/vnd.mif' => 'mif',
319                'video/quicktime' => 'mov',
320                'video/x-sgi-movie' => 'movie',
321                'audio/mpeg' => 'mp3',
322                'audio/mpg' => 'mp3',
323                'audio/mpeg3' => 'mp3',
324                'audio/mp3' => 'mp3',
325                'video/mp4' => 'mp4',
326                'video/mpeg' => 'mpeg',
327                'application/oda' => 'oda',
328                'audio/ogg' => 'ogg',
329                'video/ogg' => 'ogg',
330                'application/ogg' => 'ogg',
331                'application/x-pkcs10' => 'p10',
332                'application/pkcs10' => 'p10',
333                'application/x-pkcs12' => 'p12',
334                'application/x-pkcs7-signature' => 'p7a',
335                'application/pkcs7-mime' => 'p7c',
336                'application/x-pkcs7-mime' => 'p7c',
337                'application/x-pkcs7-certreqresp' => 'p7r',
338                'application/pkcs7-signature' => 'p7s',
339                'application/pdf' => 'pdf',
340                'application/octet-stream' => 'pdf',
341                'application/x-x509-user-cert' => 'pem',
342                'application/x-pem-file' => 'pem',
343                'application/pgp' => 'pgp',
344                'application/x-httpd-php' => 'php',
345                'application/php' => 'php',
346                'application/x-php' => 'php',
347                'text/php' => 'php',
348                'text/x-php' => 'php',
349                'application/x-httpd-php-source' => 'php',
350                'image/png' => 'png',
351                'image/x-png' => 'png',
352                'application/powerpoint' => 'ppt',
353                'application/vnd.ms-powerpoint' => 'ppt',
354                'application/vnd.ms-office' => 'ppt',
355                'application/msword' => 'doc',
356                'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
357                'application/x-photoshop' => 'psd',
358                'image/vnd.adobe.photoshop' => 'psd',
359                'audio/x-realaudio' => 'ra',
360                'audio/x-pn-realaudio' => 'ram',
361                'application/x-rar' => 'rar',
362                'application/rar' => 'rar',
363                'application/x-rar-compressed' => 'rar',
364                'audio/x-pn-realaudio-plugin' => 'rpm',
365                'application/x-pkcs7' => 'rsa',
366                'text/rtf' => 'rtf',
367                'text/richtext' => 'rtx',
368                'video/vnd.rn-realvideo' => 'rv',
369                'application/x-stuffit' => 'sit',
370                'application/smil' => 'smil',
371                'text/srt' => 'srt',
372                'image/svg+xml' => 'svg',
373                'application/x-shockwave-flash' => 'swf',
374                'application/x-tar' => 'tar',
375                'application/x-gzip-compressed' => 'tgz',
376                'image/tiff' => 'tiff',
377                'text/plain' => 'txt',
378                'text/x-vcard' => 'vcf',
379                'application/videolan' => 'vlc',
380                'text/vtt' => 'vtt',
381                'audio/x-wav' => 'wav',
382                'audio/wave' => 'wav',
383                'audio/wav' => 'wav',
384                'application/wbxml' => 'wbxml',
385                'video/webm' => 'webm',
386                'audio/x-ms-wma' => 'wma',
387                'application/wmlc' => 'wmlc',
388                'video/x-ms-wmv' => 'wmv',
389                'video/x-ms-asf' => 'wmv',
390                'application/xhtml+xml' => 'xhtml',
391                'application/excel' => 'xl',
392                'application/msexcel' => 'xls',
393                'application/x-msexcel' => 'xls',
394                'application/x-ms-excel' => 'xls',
395                'application/x-excel' => 'xls',
396                'application/x-dos_ms_excel' => 'xls',
397                'application/xls' => 'xls',
398                'application/x-xls' => 'xls',
399                'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
400                'application/vnd.ms-excel' => 'xlsx',
401                'application/xml' => 'xml',
402                'text/xml' => 'xml',
403                'text/xsl' => 'xsl',
404                'application/xspf+xml' => 'xspf',
405                'application/x-compress' => 'z',
406                'application/x-zip' => 'zip',
407                'application/zip' => 'zip',
408                'application/x-zip-compressed' => 'zip',
409                'application/s-compressed' => 'zip',
410                'multipart/x-zip' => 'zip',
411                'text/x-scriptzsh' => 'zsh',
412            ];
413            $expect = isset($mime_map[$mimetype]) === true ? $mime_map[$mimetype] : '';
414        }
415        return $expect;
416    } /* }}} */
417
418    /**
419     * Rename a directory
420     *
421     * @param $old name of directory to be renamed
422     * @param $new new name of directory
423     * @return bool
424     */
425    static public function renameDir($old, $new) { /* {{{ */
426        return @rename($old, $new);
427    } /* }}} */
428
429    /**
430     * Create a directory
431     *
432     * @param $path path of new directory
433     * @return bool
434     */
435    static public function makeDir($path) { /* {{{ */
436        
437        if( !is_dir( $path ) ){
438            $res=@mkdir( $path , 0777, true);
439            if (!$res) return false;
440        }
441
442        return true;
443
444/* some old code 
445        if (strncmp($path, DIRECTORY_SEPARATOR, 1) == 0) {
446            $mkfolder = DIRECTORY_SEPARATOR;
447        }
448        else {
449            $mkfolder = "";
450        }
451        $path = preg_split( "/[\\\\\/]/" , $path );
452        for(  $i=0 ; isset( $path[$i] ) ; $i++ )
453        {
454            if(!strlen(trim($path[$i])))continue;
455            $mkfolder .= $path[$i];
456
457            if( !is_dir( $mkfolder ) ){
458                $res=@mkdir( "$mkfolder" ,  0777);
459                if (!$res) return false;
460            }
461            $mkfolder .= DIRECTORY_SEPARATOR;
462        }
463
464        return true;
465
466        // patch from alekseynfor safe_mod or open_basedir
467
468        global $settings;
469        $path = substr_replace ($path, "/", 0, strlen($settings->_contentDir));
470        $mkfolder = $settings->_contentDir;
471
472        $path = preg_split( "/[\\\\\/]/" , $path );
473
474        for(  $i=0 ; isset( $path[$i] ) ; $i++ )
475        {
476            if(!strlen(trim($path[$i])))continue;
477            $mkfolder .= $path[$i];
478
479            if( !is_dir( $mkfolder ) ){
480                $res= @mkdir( "$mkfolder" ,  0777);
481                if (!$res) return false;
482            }
483            $mkfolder .= DIRECTORY_SEPARATOR;
484        }
485
486        return true;
487*/
488    } /* }}} */
489
490    /**
491     * Delete directory
492     *
493     * This method recursively deletes a directory including all its files.
494     *
495     * @param $path path of directory to be deleted
496     * @return bool
497     */
498    static public function removeDir($path) { /* {{{ */
499        $handle = @opendir($path);
500        if(!$handle)
501            return false;
502        while ($entry = @readdir($handle) )
503        {
504            if ($entry == ".." || $entry == ".")
505                continue;
506            else if (is_dir($path . DIRECTORY_SEPARATOR . $entry))
507            {
508                if (!self::removeDir($path . DIRECTORY_SEPARATOR . $entry ))
509                    return false;
510            }
511            else
512            {
513                if (!@unlink($path . DIRECTORY_SEPARATOR . $entry))
514                    return false;
515            }
516        }
517        @closedir($handle);
518        return @rmdir($path);
519    } /* }}} */
520
521    /**
522     * Copy a directory
523     *
524     * This method copies a directory recursively including all its files.
525     *
526     * @param $sourcePath path of directory to copy
527     * @param $targetPath path of target directory
528     * @return bool
529     */
530    static public function copyDir($sourcePath, $targetPath) { /* {{{ */
531        if (mkdir($targetPath, 0777)) {
532            $handle = @opendir($sourcePath);
533            while ($entry = @readdir($handle) ) {
534                if ($entry == ".." || $entry == ".")
535                    continue;
536                else if (is_dir($sourcePath . $entry)) {
537                    if (!self::copyDir($sourcePath . DIRECTORY_SEPARATOR . $entry, $targetPath . DIRECTORY_SEPARATOR . $entry))
538                        return false;
539                } else {
540                    if (!@copy($sourcePath . DIRECTORY_SEPARATOR . $entry, $targetPath . DIRECTORY_SEPARATOR . $entry))
541                        return false;
542                }
543            }
544            @closedir($handle);
545        }
546        else
547            return false;
548
549        return true;
550    } /* }}} */
551
552    /**
553     * Move a directory
554     *
555     * @param $sourcePath path of directory to be moved
556     * @param $targetPath new name of directory
557     * @return bool
558     */
559    static public function moveDir($sourcePath, $targetPath) { /* {{{ */
560        if (!self::copyDir($sourcePath, $targetPath))
561            return false;
562        return self::removeDir($sourcePath);
563    } /* }}} */
564
565    // code by Kioob (php.net manual)
566    /**
567     * Compress a file with gzip
568     *
569     * @param $source path of file to be compressed
570     * @param bool $level level of compression
571     * @return bool|string file name of compressed file or false in case of an error
572     */
573    static public function gzcompressfile($source, $level=false) { /* {{{ */
574        $dest=$source.'.gz';
575        $mode='wb'.$level;
576        $error=false;
577        if($fp_out=@gzopen($dest,$mode)) {
578            if($fp_in=@fopen($source,'rb')) {
579                while(!feof($fp_in))
580                    @gzwrite($fp_out,fread($fp_in,1024*512));
581                @fclose($fp_in);
582            }
583            else $error=true;
584            @gzclose($fp_out);
585        }
586        else $error=true;
587
588        if($error) return false;
589        else return $dest;
590    } /* }}} */
591}