<?php
/**
 * @license    http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.html
 * @author     Francois Merciol <dokuplugin@merciol.fr>
 *
 * Plugin AdecWatt: file transfert
 */

class adecWattDB {
    // ========================================
    var $dbDirName;
    var $excludeFileName;
    var $userDbName;
    var $aclDbName;
    var $nameDb;
    var $idDb;
    var $aclDb;
    var $login;
    var $id;
    var $groups;
    var $fpLog;

    function __construct ($plugin) {
        global $conf;
        // date_default_timezone_set ("UTC");
        $this->dbDirName =
            ((!$conf['savedir'] || strpos ($conf['savedir'], '.') === 0) ? DOKU_INC : "").
            $conf['savedir'].'/'.trim ($plugin->getConf ('dataDir').'/');
        $this->excludeFileName = explode ("|", ".|..|timestamp");
        $this->userDbName = $this->dbDirName.'users.txt';
        $this->aclDbName = $this->dbDirName.'acl.txt';
        $this->fpLog = fopen ('/home/parlenet/adec.log', 'a+');
    }
    
    function tmpLog ($msg) {
        fwrite ($this->fpLog, date ("YmdHis ").$msg.NL);
        fflush ($this->fpLog);
    }

    // ========================================
    function getInfos () {
        global $INPUT;
        $this->login = $INPUT->server->str ('REMOTE_USER');
        if (!$this->login)
            return;
        $this->readUser ();
        $this->id = $this->nameDb [$this->login];
        if ($this->id === null)
            return;
        $this->groups = explode (',', $this->idDb[$this->id]);
    }

    function getRoles ($name) {
        $this->getInfos ();
        if (!$this->login) {
            echo "Not connected!".NL;
            return;
        }
        global $INFO;
        if (isset ($INFO['userinfo'] ['grps']) && in_array ('admin', $INFO ['userinfo'] ['grps'])) {
            // si admin
        }
        if (!$this->id) {
            "1|Visitor";
        }
        echo  $this->id."|".$this->idDb[$this->id].NL;
    }

    function readUser () {
        $this->nameDb = array ();
        $this->idDb = array ();
        $user = file ($this->userDbName);
        foreach ($user as $line) {
            $line = trim ($line);
            if (empty ($line) || ($line[0] == '#'))
                continue; // skip blank lines & comments
            list ($id, $name, $roles) = explode ('|', $line, 3);
            // XXX test nom double
            $this->nameDb [$name] = $id;
            $this->idDb [$id] = $roles;
        }
    }

    // ========================================
    function readAcl () {
        $this->aclDb = array ();
        $acl = file ($this->aclDbName);
        foreach ($acl as $line) {
            $line = trim ($line);
            if (empty ($line) || ($line[0] == '#'))
                continue; // skip blank lines & comments
            list ($starts, $rest) = preg_split ('/[ \t]+/', $line, 2);
            $user = array ();
            $group = array ();
            foreach (explode (',', $rest) as $word) {
                $word = trim ($word);
                if ($word[0] == '@')
                    $group[] = substr ($word, 1);
                else
                    $user[] = $word;
            }
            $this->aclDb [$starts] = array ("users" => $user, "groups" => $group);
        }
    }

    function checkAcl ($path) {
        foreach (array_keys ($this->aclDb) as $starts)
            if (substr ($path, 0, strlen ($starts)) === $starts &&
            (in_array ($this->login, $this->aclDb[$starts]["users"]) || array_intersect ($this->groups, $this->aclDb[$starts]["groups"])))
                return true;
        return false;
    }

    // ========================================
    function cleanTmp () {
        $tmpDir = sys_get_temp_dir ();
        if (!$dh = opendir ($tmpDir))
            return;
        $today = time ();
        $aMoment = 60;
        while (($child = readdir ($dh)) !== false) {
            $fileName = $tmpDir."/".$child;
            if (!is_file ($fileName) || filesize ($fileName))
                continue;
            if ($today - filemtime ($fileName) < $aMoment)
                continue;
            @unlink ($fileName);
        }
    }

    function getTreeInfo ($fileName, $name) {
        if (is_file ($fileName))
            return date ("YmdHis ", filemtime ($fileName)).filesize ($fileName)." ".$name.NL;
        if (!is_dir ($fileName))
            return "";
        $result = "";
        if ($dh = opendir ($fileName)) {
            while (($child = readdir ($dh)) !== false) {
                if (in_array (strtolower ($child), $this->excludeFileName))
                    continue;
                if (is_file ($child) && eregi ('.*\.back', $child, $b))
                    continue;
                $result .= $this->getTreeInfo ($fileName."/".$child, $name."/".$child);
            }
            closedir($dh);
        }
        return $result;
    }
    function getTmpFile () {
        return tempnam (sys_get_temp_dir (), "download");
    }
    function getDataFileName ($version, $fileName) {
        return $this->dbDirName."/".$this->checkPath ("/".$version."/".$fileName);
    }
    function checkPath ($path) {
        return trim (str_replace (array ("//", "/./", "/../"), "/", "/".$path), "/");
    }
    function getPostZipFile ($cmd) {
        if (!$_FILES) {
            echo "No zip file sended!".NL;
            return null;
        }
        $file = $_FILES [$cmd];
        if (!$file)
            $file = $_FILES [array_keys ($_FILES)[0]];
        if (!$file['tmp_name']) {
            echo "Empty zip file!".NL;
            return null;
        }
        $zip = new ZipArchive;
        if ($zip->open ($file['tmp_name']) !== TRUE) {
            echo "Can't open zip ".$file['tmp_name']."!".NL;
            return null;
        }
        return $zip;
    }
    function getPostZipFileListContent ($cmd, $name) {
        $zip = $this->getPostZipFile ($cmd);
        if (!$zip)
            return "";
        $listFilesName = $zip->getFromName ($name);
        $listFilesName = str_replace ("\r", "", $listFilesName);
        $zip->close ();
        return $listFilesName;
    }

    // ========================================
    // directory
    // from name return zip [file:info]
    function zipList ($version, $name) {
        $this->cleanTmp ();
        $fileName = $this->dbDirName."/".$this->checkPath ("/".$version."/".$name);
        $this->sendZipText ($name, $this->getTreeInfo ($fileName, $name));
    }

    // get files
    // from zip file contains list
    function zipGets ($version, $name) {
        $listFilesName = $this->getPostZipFileListContent ('zipGets', $name);
        if (!$listFilesName) {
            echo "No request!".NL;
            return;
        }
        $tmpFileName = $this->getTmpFile ();
        $zip = $this->sendZipOpen ($tmpFileName);
        if (!$zip)
            return;
        foreach (explode ("\n", $listFilesName) as $fileName) {
            if (!$fileName)
                continue;
            $zip->addFile ($this->getDataFileName ($version, $fileName), $fileName);
        }
        $this->sendZipClose ($zip, $tmpFileName);
    }

    // put files
    // from zip contains all files
    function zipPuts ($version, $name) {
        $this->cleanTmp ();
        $this->getInfos ();
        if (!$this->login) {
            echo "Not connected!".NL;
            return;
        }
        $this->readAcl ();
        $tmpFileName = $this->getTmpFile ();
        $zip = new ZipArchive;
        if ($zip->open ($tmpFileName, ZipArchive::CREATE) !== TRUE) {
            echo "Can't open $tmpFileName".NL;
            return;
        }
        $zip = $this->getPostZipFile ($cmd);
        if (!$zip) {
            echo "No zip!".NL;
            return;
        }
        $validFiles = array ();
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $entry = $this->checkPath ("/".$zip->getNameIndex ($i));
            if (!$this->checkAcl ($entry))
                continue;
            $validFiles [] = $entry;
        }
        if (!$validFiles) {
            echo "No file sended!".NL;
            return;
        }
        $pathExtract = $this->dbDirName."/".$version."/";
        foreach ($validFiles as $oldFile) {
            $filename = realpath ($pathExtract.$oldFile);
            if (is_file ($filename))
                rename ($filename, dirname ($filename)."/".basename ($filename).".back");
        }
        $zip->extractTo ($pathExtract, $validFiles);
        // change time
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $entry = $this->checkPath ("/".$zip->getNameIndex ($i));
            if (! in_array ($entry, $validFiles))
                continue;
            $filename = realpath ($pathExtract.$entry);
            $this->tmpLog ("coucou touch :".$filename." ".$zip->statIndex ($i)["mtime"]);
            touch ($filename, $zip->statIndex ($i)["mtime"]);
        }
        $this->sendZipText ($name, implode (NL, $validFiles));
        $zip->close ();
    }

    // remove all files
    // from zip contains filesname
    function zipRemove ($version, $name) {
        $this->cleanTmp ();
        $listFilesName = $this->getPostZipFileListContent ('zipRemove', $name);
        if (!$listFilesName) {
            echo "No request!".NL;
            return;
        }
        $this->getInfos ();
        if (!$this->login) {
            echo "Not connected!".NL;
            return;
        }
        $this->readAcl ();
        $succes = array ();
        foreach (explode ("\n", $listFilesName) as $fileName) {
            if (!$fileName)
                continue;
            $fileName = $this->checkPath ("/".$fileName);
            if (!$this->checkAcl ($fileName))
                continue;
            unlink ($this->getDataFileName ($version, $fileName));
            $succes[] = $fileName;
        }
        $this->sendZipText ($name, implode (NL, $succes));
    }

    // get one file
    // from querry-string (not used)
    function getZip ($version, $name) {
        $name = $this->checkPath ($name);
        $tmpFileName = $this->getTmpFile ();
        $zip = $this->sendZipOpen ($tmpFileName);
        if (!$zip)
            return;
        $zip->addFile ($this->getDataFileName ($version, $name), $name);
        $this->sendZipClose ($zip, $tmpFileName);
    }

    // ========================================
    function sendZipOpen ($tmpFileName) {
        $zip = new ZipArchive;
        if ($zip->open ($tmpFileName, ZipArchive::CREATE) !== TRUE) {
            echo "Can't open $tmpFileName".NL;
            return null;
        }
        return $zip;
    }
    function sendZipClose ($zip, $tmpFileName) {
        $zip->close ();
        $this->sendAbsFile ($tmpFileName, "application/zip");
        unlink ($tmpFileName);
    }
    function sendZipText ($name, $content) {
        $tmpFileName = $this->getTmpFile ();
        $zip = $this->sendZipOpen ($tmpFileName);
        if (!$zip)
            return;
        $zip->addFromString ($name, $content);
        $this->sendZipClose ($zip, $tmpFileName);
    }
    function sendAbsFile ($fileName, $mimeType) {
        if (!is_file ($fileName)) {
            echo "File not found <$fileName> <$mimeType>!".NL;
            return;
        }
        header ("Content-Type: $mimeType");
        echo file_get_contents ($fileName);
    }

    // ========================================
    // old API
    // ========================================
    function sendDataFile ($version, $fileName, $mimeType) {
        $this->sendAbsFile ($this->getDataFileName ($version, $fileName), $mimeType);
    }
    function getDir ($version, $name) {
        $this->sendDataFile ($version, $name."/timestamp", "text/plain");
    }
    function getDataFile ($version, $name) {
        $mimeType = "";
        if (eregi ('.*\.lpt$', $name, $b))
            $mimeType = "application/lpt";
        elseif (eregi ('.*\.lpi$', $name, $b))
            $mimeType = "text/plain";
        elseif (eregi ('.*\.png$', $name, $b))
            $mimeType = "image/png";
        elseif (eregi ('.*\.jpg$', $name, $b))
            $mimeType = "image/jpg";
        elseif (eregi ('.*\.jar$', $name, $b))
            $mimeType = "application/java";
        elseif (eregi ('.*\.xml', $name, $b))
            $mimeType = "application/xml";
        else {
            echo "Bad type $name!".NL;
            return;
        }
        $this->sendDataFile ($version, $name, $mimeType);
    }
    // ========================================
}