<?php

class FileManager
{
    public $restrictSuffixes = [];
    public $forbidSuffixes = [];
    public $currentUser;
    public $userDefinition = [
        'SYSTEM' => [],
        'MASTER' => [],
        'USER' => [
            'read' => [],
            'write' => [],
        ],
        'DESIGNER' => [
            'read' => [],
            'write' => [],
        ],
    ];

    public function __construct($user = 'SYSTEM')
    {
        $this->changeUser($user);
        $this->forbidSuffixes();
    }

    public function changeUser($user = 'SYSTEM')
    {
        $this->currentUser = $user;

        // nastavit povolene slozky
        $f = $GLOBALS['cfg']['path']['root'];
        $this->userDefinition['USER']['read'][] = $f;
        $this->userDefinition['DESIGNER']['read'][] = $f;

        $f = $GLOBALS['cfg']['path']['storage'].'*';
        $this->userDefinition['USER']['read'][] = $f;
        $this->userDefinition['USER']['write'][] = $f;
        $this->userDefinition['DESIGNER']['read'][] = $f;
        $this->userDefinition['DESIGNER']['write'][] = $f;

        $f = $GLOBALS['cfg']['path']['smarty']['templates'].'*';
        $this->userDefinition['USER']['read'][] = $f;
        $this->userDefinition['DESIGNER']['read'][] = $f;
        $this->userDefinition['DESIGNER']['write'][] = $f;
    }

    // omezuje pripony souboru, ktere se smi zobrazit
    public function restrictSuffixes($restrictSuffixes = null)
    {
        if (is_null($restrictSuffixes) || !is_array($restrictSuffixes)) {
            $restrictSuffixes = [];
        }
        $this->restrictSuffixes = $restrictSuffixes;
    }

    // omezuje pripony souboru, ktere se nesmi zobrazit
    public function forbidSuffixes($forbidSuffixes = null)
    {
        if (is_null($forbidSuffixes) || !is_array($forbidSuffixes)) {
            $forbidSuffixes = [];
        }

        // podle usera zakazat dalsi pripony
        switch ($this->currentUser) {
            case 'MASTER':
            case 'SYSTEM':
                break;
            default:
                $forbidSuffixes2 = ['php', 'phtml', 'inc', 'sql', 'log', 'htaccess', 'htpasswd'];
                $forbidSuffixes = array_merge($forbidSuffixes, $forbidSuffixes2);
                break;
        }

        $this->forbidSuffixes = $forbidSuffixes;
    }

    // vytvari slozku
    public function createFolder($folder, $path = '')
    {
        // rozdeli cestu a zalozi vsechny nadslozky az po posledni slozku
        $temp = explode('/', $folder);
        $makedirs = [];
        $lastdir = '';

        for ($x = 0; $x < count($temp); $x++) {
            if (!empty($temp[$x])) {
                $lastdir .= ($lastdir != '') ? '/'.trim($temp[$x]) : trim($temp[$x]);
                if ($temp[$x] != '.' && $temp[$x] != '..') {
                    $makedirs[] = $lastdir;
                }
            }
        }
        unset($temp, $lastdir);

        $chmod = 0777;

        for ($x = 0; $x < count($makedirs); $x++) {
            if (!file_exists($path.$makedirs[$x])) {
                // zkontrolovat pravo k adresari
                if ($this->verifyUserAccessRight2Folder($path.$makedirs[$x], 'write') == false) {
                    return false;
                }

                @mkdir($path.$makedirs[$x], $chmod);
                // echo $path . $makedirs[$x] . "<hr>";
            }
        }

        return true;
    }

    // rekurzivni funkce
    // DODELAT
    public function copyFolder($fromFolder, $toFolder, $current = '')
    {
        $dp = @opendir($fromFolder.$current);
        if (!$dp) {
            exit('Nepodarilo se otevrit slozku '.$fromFolder);
        }

        while ($fileThis = readdir($dp)) {
            if ($fileThis == '.' || $fileThis == '..') {
                continue;
            }

            // odkaz je slozka
            if (is_dir($fromFolder.$current.'/'.$fileThis)) {
                // echo "zalozit:    " . $toFolder . $current."/".$fileThis."<br>";
                $this->createFolder($toFolder.$current.'/'.$fileThis);
                $this->copyFolder($fromFolder, $toFolder, $current.'/'.$fileThis);
            } // odkaz je soubor
            else {
                // echo "zkopirovat: " . $toFolder . $current."/".$fileThis."<br>";
                $this->copyFile($fromFolder.$current.'/'.$fileThis, $toFolder.$current.'/'.$fileThis);
            }
        }

        return true;
    }

    // kopiruje soubory
    public function copyFile($from, $to)
    {
        $folder = $this->checkPathLastSlash(dirname($to));
        $filename = basename($to);

        // zkontrolovat pravo k adresari
        /*if($this->verifyUserAccessRight2Folder($folder, 'write') == false)
        {
            return false;
        }
        // zkontrolovat pravo k souboru
        if($this->verifyUserAccessRight2File($folder, $filename, 'write') == false)
        {
            return false;
        }*/
        copy($from, $to);
        chmod($to, 0777);

        return true;
    }

    // vytvari prazdny soubor soubory
    public function createFile($file, $data)
    {
        $folder = $this->checkPathLastSlash(dirname($file));
        $filename = basename($file);

        // zkontrolovat pravo k adresari
        if ($this->verifyUserAccessRight2Folder($folder, 'write') == false) {
            return false;
        }
        // zkontrolovat pravo k souboru
        if ($this->verifyUserAccessRight2File($folder, $filename, 'write') == false) {
            return false;
        }

        $fp = @fopen($file, 'w');
        if ($fp) {
            fwrite($fp, $data);
            fclose($fp);
        } else {
            return false;
        }

        return true;
    }

    // prepise obsah souboru
    public function updateFileContent($file, $data)
    {
        $folder = $this->checkPathLastSlash(dirname($file));
        $filename = basename($file);

        // zkontrolovat pravo k adresari
        if ($this->verifyUserAccessRight2Folder($folder, 'write') == false) {
            return false;
        }
        // zkontrolovat pravo k souboru
        if ($this->verifyUserAccessRight2File($folder, $filename, 'write') == false) {
            return false;
        }

        $fp = @fopen($file, 'w+');
        if ($fp) {
            fwrite($fp, $data);
            fclose($fp);
        } else {
            return false;
        }

        return true;
    }

    // zjisti velikost obsahu adresare
    public function getFolderSize($folder)
    {
        $size = 0;

        if (!is_dir($folder)) {
            return -1;
        }

        $dp = @opendir($folder);
        if (!$dp) {
            return -2;
        }

        while ($fileThis = readdir($dp)) {
            if ($fileThis == '.' || $fileThis == '..') {
                continue;
            }

            // odkaz je slozka
            if (is_dir($folder.'/'.$fileThis)) {
                $cSize = $this->getFolderSize($folder.'/'.$fileThis);
            } // odkaz je soubor
            else {
                $cSize = filesize($folder.'/'.$fileThis);
            }

            if ($cSize > 0) {
                $size += $cSize;
            }
        }

        return $size;
    }

    // kovertovat udaje o bajtech do jine jednotky
    public function reCalculateFileSize($size, $unit = 'MB', $dec = 2)
    {
        switch ($unit) {
            case 'KB':
                $size = $size / 1024;
                break;
            case 'MB':
                $size = $size / 1024 / 1024;
                break;
        }

        $size = number_format($size, $dec, '.', '');

        return $size;
    }

    // --------------------------------------------------
    // zjistuje, jestli je mozno do slozky zapisovat
    public function isFolderWriteable($folder)
    {
        // 16895 - drwxrwxrwx
        return fileperms($folder) == 16895;
    }

    // --------------------------------------------------
    // zjistuje, jestli je mozno zapisovat do souboru
    public function isFileWriteable($file)
    {
        return is_writable($file);
    }

    // --------------------------------------------------
    // nastavi prava na zapis do slozky
    public function makeFolderWriteable($folder)
    {
        return @chmod($folder, 0777);
    }

    // --------------------------------------------------
    // nastavi prava na zapis do souboru
    public function makeFileWriteable($file)
    {
        return @chmod($file, 0777);
    }

    // rekurzivni funkce
    // smaze kompletni obsah slozky a jejich podslozek
    public function deleteFolder($folder)
    {
        $dp = @opendir($folder);
        if (!$dp) {
            return false;
        }
        // die("Nepodarilo se otevrit slozku ".$folder);

        while ($fileThis = readdir($dp)) {
            if ($fileThis == '.' || $fileThis == '..') {
                continue;
            }

            // odkaz je slozka
            if (is_dir($folder.'/'.$fileThis)) {
                $this->deleteFolder($folder.'/'.$fileThis);
            } // odkaz je soubor
            else {
                $this->deleteFile($folder.'/'.$fileThis);
            }
        }

        @closedir($dp);

        // smazat samotnou slozku
        if (!@rmdir($folder)) {
            @chmod($folder, 0777);
            @rmdir($folder);
        }

        return true;
    }

    // smaze soubor
    public function deleteFile($file)
    {
        if (!file_exists($file)) {
            return false;
        }

        $folder = $this->checkPathLastSlash(dirname($file));
        $filename = basename($file);

        // zkontrolovat pravo k adresari
        if ($this->verifyUserAccessRight2Folder($folder, 'write') == false) {
            // die("Zakazana slozka: ".$folder);
            return false;
        }
        // zkontrolovat pravo k souboru
        if ($this->verifyUserAccessRight2File($folder, $filename, 'write') == false) {
            // die("Zakazany soubor: ".$file);
            return false;
        }
        // die($file);
        // $class['FileManager']->deleteFile('d:\webs-cms\vyvoj\industrial-advisors/templates/Forms-form.tpl');

        // smazat
        if (!@unlink($file)) {
            @chmod($file, 0777);
            @unlink($file);
        }

        return true;
    }

    // prejmenuje soubor
    public function renameFile($file, $newname)
    {
        if (!file_exists($file)) {
            return false;
        }

        // -------------------------------------------------------------
        // nejdrive zkontrolovat slozku, z ktere prejmenovavame
        $folder = $this->checkPathLastSlash(dirname($file));
        $filename = basename($file);

        // zkontrolovat pravo k adresari
        if ($this->verifyUserAccessRight2Folder($folder, 'write') == false) {
            return false;
        }
        // zkontrolovat pravo k souboru
        if ($this->verifyUserAccessRight2File($folder, $filename, 'write') == false) {
            return false;
        }

        // -------------------------------------------------------------
        // zkontrolovat slozku, do ktere prejmenovavame
        $folder = $this->checkPathLastSlash(dirname($newname));
        $filename = basename($newname);

        // zkontrolovat pravo k adresari
        if ($this->verifyUserAccessRight2Folder($folder, 'write') == false) {
            return false;
        }
        // zkontrolovat pravo k souboru
        if ($this->verifyUserAccessRight2File($folder, $filename, 'write') == false) {
            return false;
        }

        // -------------------------------------------------------------
        // prejmenovat
        if (!@rename($file, $newname)) {
            @chmod($folder, 0777);
            @rename($file, $newname);
        }

        return true;
    }

    // ----------------------------------------------------------

    public function browseFolder($folder, $subfolder = '', $getFolders = true, $getFiles = true, $browseRecursive = false, $fullpath = false)
    {
        //  vraci vsechny podslozky dane slozky v poli
        if (!($dp = opendir($folder.$subfolder))) {
            return false;
        }
        $i = 0;
        $darr = [];

        while ($fileThis = readdir($dp)) {
            if ($fileThis == '.' || $fileThis == '..') {
                continue;
            }

            // odkaz je slozka
            if (is_dir($folder.$subfolder.$fileThis)) {
                if ($getFolders) {
                    $darr[] = ($fullpath) ? $folder.$subfolder.$fileThis.'/' : $subfolder.$fileThis.'/';
                }
                if ($browseRecursive) {
                    $arr = $this->browseFolder($folder, $subfolder.$fileThis.'/', $getFolders, $getFiles, $browseRecursive, $fullpath);
                    $darr = array_merge($darr, $arr);
                }
            } // odkaz je soubor
            else {
                if ($getFiles) {
                    $darr[] = ($fullpath) ? $folder.$subfolder.$fileThis : $subfolder.$fileThis;
                }
            }

            $i++;
        }
        closedir($dp);
        /*
        echo '<pre>';
        print_r($darr);
        exit;
        */

        return $darr;
    }

    // ----------------------------------------------------------
    // fce vraci obsah urcite slozky. Defaultne je volana systemem
    // kdyz je ale volana uzivatelem, vraci vzdy jen adresare, na
    // ktere ma uzivatel pravo.

    public function getSubFolders($folder)
    {
        // nejdrive zkontrolovat vubec danou slozku
        if ($this->verifyUserAccessRight2Folder($folder, 'read') == false) {
            return false;
        }

        //  vraci vsechny podslozky dane slozky v poli
        if (!($dp = opendir($folder))) {
            return false;
        }

        $i = 0;
        while ($dc = readdir($dp)) {
            if (!is_dir($folder.$dc) || $dc == '.' || $dc == '..') {
                continue;
            }

            if ($this->verifyUserAccessRight2Folder($folder.$dc.'/', 'read') == false) {
                continue;
            }

            $darr[$i] = $dc;
            $i++;
        }
        closedir($dp);

        return $darr;
    }

    // ----------------------------------------------------------
    // fce vraci obsah urcite slozky. Defaultne je volana systemem
    // kdyz je ale volana uzivatelem, vraci vzdy jen soubory, na ktere ma uzivatel pravo.

    public function getFolderFiles($folder)
    {
        // nejdrive zkontrolovat vubec danou slozku
        if ($this->verifyUserAccessRight2Folder($folder, 'read') == false) {
            return false;
        }

        //  vraci vsechny soubory dane slozky v poli
        if (!($dp = opendir($folder))) {
            return false;
        }

        $i = 0;
        while ($dc = readdir($dp)) {
            // pokud neni slozka
            if (is_dir($folder.$dc) || $dc == '.' || $dc == '..') {
                continue;
            }
            if ($this->verifyUserAccessRight2File($folder, $dc) == false) {
                continue;
            }

            $darr[$i] = $dc;
            $i++;
        }
        closedir($dp);

        return $darr;
    }

    // ----------------------------------------------------------

    public function getIconFile($suffix, $width = 16, $height = 16)
    {
        $suffixType = null;
        $suffix = strtolower($suffix);

        switch ($suffix) {
            case 'avi':
            case 'mpeg':
            case 'mpg':
                $suffixType = 'video';
                break;
            case 'mp3':
            case 'wav':
            case 'ogg':
            case 'ogg':
                $suffixType = 'audio';
                break;
            case 'txt':
            case 'dat':
            case 'php':
            case 'php3':
            case 'php4':
            case 'php5':
            case 'dtd':
            case 'srt':
            case 'sub':
                $suffixType = 'txt';
                break;
            case 'bmp':
            case 'gif':
            case 'jpg':
            case 'jpeg':
            case 'png':
                $suffixType = 'image';
                break;
            case 'csv':
            case 'xls':
                $suffixType = 'excel';
                break;
            case 'htm':
            case 'html':
            case 'xml':
            case 'xhtml':
            case 'dhtml':
                $suffixType = 'explorer';
                break;
            case 'pdf':
                $suffixType = $suffix;
                break;
            case 'zip':
                $suffixType = $suffix;
                break;
            case 'rar':
                $suffixType = $suffix;
                break;
            case 'chm':
                $suffixType = $suffix;
                break;
            case 'doc':
                $suffixType = $suffix;
                break;
        }

        $icon = (is_null($suffixType)) ? 'type_'.$width.'x'.$height.'_unknown.gif' : 'type_'.$width.'x'.$height.'_'.$suffixType.'.gif';
        // $icon = 'filetypes/'.$icon;

        return $icon;
    }

    // ----------------------------------------------------------
    // fce vraci koncovku souboru

    public function isFileContentEditable($suffix)
    {
        $suffix = strtolower($suffix);

        return ereg('^(txt|html|htm|tpl|ini|dat|csv|js|css|xml|php|php3|php4|php5|java|xhtml|dtd|srt|sub|htaccess|htpasswd)$', $suffix);
    }

    // ------------------------------------------------
    // fce zkontroluje, zda ma uzivatel pravo videt danou slozku a jeji obsah
    public function verifyUserAccessRight2File($folder, $filename, $action = 'read')
    {
        // system ma pravo nahlizet a zapisovat do vsech slozek
        if ($this->currentUser == 'SYSTEM') {
            return true;
        }

        // unixove soubory
        if (strpos($filename, '.ht') !== false && strpos($filename, '.ht') == 0) {
            return false;
        }

        // ziskat priponu souboru
        $suffix = strtolower(substr(strrchr($filename, '.'), 1));

        // vlastni definovane prijatelne pripony
        if (count($this->restrictSuffixes) > 0 && array_search($suffix, $this->restrictSuffixes) === false) {
            return false;
        }

        // vlastni definovane zakazane pripony
        if (array_search($suffix, $this->forbidSuffixes) !== false) {
            return false;
        }

        return true;
    }

    // ------------------------------------------------
    // fce zkontroluje, zda ma uzivatel pravo videt dany soubor
    public function verifyUserAccessRight2Folder($folder, $action = 'read')
    {
        global $class;

        if ($action != 'read' && $action != 'write') {
            trigger_error('FileManager->verifyUserAccessRight2Folder: spatny atribut $action='.$action, E_USER_ERROR);

            return false;
        }

        // system ma pravo nahlizet a zapisovat do vsech slozek
        if ($this->currentUser == 'SYSTEM') {
            return true;
        }

        // lomitko nakonec
        $folder = $this->checkPathLastSlash($folder);

        // bezny uzivatel
        if (isset($this->userDefinition[$this->currentUser])) {
            // pokud slozku nalezneme v povolenych slozkach pro danou operaci
            for ($x = 0; $x < count($this->userDefinition[$this->currentUser][$action]); $x++) {
                $c_folder = $this->userDefinition[$this->currentUser][$action][$x];

                // kdyz je slozka definovana i se vsemi podrizenymi
                if (substr($c_folder, -1) == '*') {
                    $c_folder = substr($c_folder, 0, -1);

                    $pos = strpos($folder, $c_folder);
                    if ($pos !== false && $pos == 0) {
                        return true;
                    }
                } // je presne dana slozka, kterou muze videt
                else {
                    if ($folder == $c_folder) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    // ----------------------------------------------------------
    // fce vraci koncovku souboru

    public function getFileSuffix($filename)
    {
        return strtolower(substr(strrchr($filename, '.'), 1));
    }

    // ----------------------------------------------------------
    // fce zjistuje, jestli cesta konci na lomitko, kdyz ne, tak
    // prida lomitko na konec

    public function checkPathLastSlash($path)
    {
        $s = substr($path, -1);

        return ($s == '/' || $s == '\\') ? $path : $path.'/';
    }

    // ----------------------------------------------------------

    public function checkFileName($filename)
    {
        return eregi("^[-a-z0-9\_\./]+$", $filename);
    }

    // ----------------------------------------------------------

    public function clearOutFileName($string)
    {
        $tbl = ["\xc3\xa1" => 'a', "\xc3\xa4" => 'a', "\xc4\x8d" => 'c', "\xc4\x8f" => 'd', "\xc3\xa9" => 'e', "\xc4\x9b" => 'e', "\xc3\xad" => 'i', "\xc4\xbe" => 'l', "\xc4\xba" => 'l', "\xc5\x88" => 'n', "\xc3\xb3" => 'o', "\xc3\xb6" => 'o', "\xc5\x91" => 'o', "\xc3\xb4" => 'o', "\xc5\x99" => 'r', "\xc5\x95" => 'r', "\xc5\xa1" => 's', "\xc5\xa5" => 't', "\xc3\xba" => 'u', "\xc5\xaf" => 'u', "\xc3\xbc" => 'u', "\xc5\xb1" => 'u', "\xc3\xbd" => 'y', "\xc5\xbe" => 'z', "\xc3\x81" => 'A', "\xc3\x84" => 'A', "\xc4\x8c" => 'C', "\xc4\x8e" => 'D', "\xc3\x89" => 'E', "\xc4\x9a" => 'E', "\xc3\x8d" => 'I', "\xc4\xbd" => 'L', "\xc4\xb9" => 'L', "\xc5\x87" => 'N', "\xc3\x93" => 'O', "\xc3\x96" => 'O', "\xc5\x90" => 'O', "\xc3\x94" => 'O', "\xc5\x98" => 'R', "\xc5\x94" => 'R', "\xc5\xa0" => 'S', "\xc5\xa4" => 'T', "\xc3\x9a" => 'U', "\xc5\xae" => 'U', "\xc3\x9c" => 'U', "\xc5\xb0" => 'U', "\xc3\x9d" => 'Y', "\xc5\xbd" => 'Z'];
        $string = strtr($string, $tbl);

        $string = eregi_replace("[^a-z^A-Z^0-9^\-^\.^\_]", '_', $string);
        $string = eregi_replace("\_+", '_', $string);
        $string = eregi_replace("^\_+", '', $string);
        $string = eregi_replace("\_+$", '', $string);
        $string = trim($string);

        return $string;
    }

    // ----------------------------------------------------------

    public function sortOutput($inputArr, $column, $direction)
    {
        $inputArr = (array) $inputArr;
        switch ($column) {
            case 'filename':
                if ($direction == 'ASC') {
                    usort($inputArr, [$this, '_sort_filename_ASC']);
                } else {
                    usort($inputArr, [$this, '_sort_filename_DESC']);
                }
                break;
            case 'filetype':
                if ($direction == 'ASC') {
                    usort($inputArr, [$this, '_sort_filetype_ASC']);
                } else {
                    usort($inputArr, [$this, '_sort_filetype_DESC']);
                }
                break;
            case 'filesize':
                if ($direction == 'ASC') {
                    usort($inputArr, [$this, '_sort_filesize_ASC']);
                } else {
                    usort($inputArr, [$this, '_sort_filesize_DESC']);
                }
                break;
            case 'filemtime':
                if ($direction == 'ASC') {
                    usort($inputArr, [$this, '_sort_filemtime_ASC']);
                } else {
                    usort($inputArr, [$this, '_sort_filemtime_DESC']);
                }
                break;
        }

        return $inputArr;
    }

    // ----------------------------------------------------------

    public function _sort_filename_ASC($a, $b)
    {
        $propA = $a['filename'];
        $propB = $b['filename'];

        return strcmp($propA, $propB);
    }

    public function _sort_filename_DESC($a, $b)
    {
        $propA = $a['path_abs'];
        $propB = $b['path_abs'];

        return strcmp($propA, $propB) * (-1);
    }

    public function _sort_filetype_ASC($a, $b)
    {
        $propA = strtolower($a['filetype']);
        $propB = strtolower($b['filetype']);

        return strcmp($propA, $propB);
    }

    public function _sort_filetype_DESC($a, $b)
    {
        $propA = strtolower($a['filetype']);
        $propB = strtolower($b['filetype']);

        return strcmp($propA, $propB) * (-1);
    }

    public function _sort_filesize_ASC($a, $b)
    {
        $propA = filesize($a['path_abs']);
        $propB = filesize($b['path_abs']);

        if ($propA == $propB) {
            return 0;
        }

        return ($propA < $propB) ? -1 : 1;
    }

    public function _sort_filesize_DESC($a, $b)
    {
        $propA = filesize($a['path_abs']);
        $propB = filesize($b['path_abs']);

        if ($propA == $propB) {
            return 0;
        }

        return ($propA > $propB) ? -1 : 1;
    }

    public function _sort_filemtime_ASC($a, $b)
    {
        $propA = filemtime($a['path_abs']);
        $propB = filemtime($b['path_abs']);

        if ($propA == $propB) {
            return 0;
        }

        return ($propA < $propB) ? -1 : 1;
    }

    public function _sort_filemtime_DESC($a, $b)
    {
        $propA = filemtime($a['path_abs']);
        $propB = filemtime($b['path_abs']);

        if ($propA == $propB) {
            return 0;
        }

        return ($propA > $propB) ? -1 : 1;
    }

    // ----------------------------------------------------------
}
