<?php
/**
 * Action Plugin ArchiveUpload
 * 
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Michael Klier chi@chimeric.de
 */

/**
 * DokuWiki Action Plugin Archive Upload
 *
 * @author Michael Klier <chi@chimeric.de>
 */
class action_plugin_archiveupload extends DokuWiki_Action_Plugin {

    var $tmpdir = '';

    /**
     * return some info
     */
    function getInfo() {
        return array(
                'author' => 'Michael Klier',
                'email'  => 'chi@chimeric.de',
                'date'   => @file_get_contents(DOKU_PLUGIN.'archiveupload/VERSION'),
                'name'   => 'ArchiveUpload',
                'desc'   => 'Allows you to unpack uploaded archives.',
                'url'    => 'http://dokuwiki.org/plugin:archiveupload'
            );
    }

     /**
      * Registers our callback functions
      */
    function register(Doku_Event_Handler $controller) {
        $controller->register_hook('HTML_UPLOADFORM_OUTPUT', 'BEFORE', $this, 'handle_form_output');
        $controller->register_hook('MEDIA_UPLOAD_FINISH', 'BEFORE', $this, 'handle_media_upload');
        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'AFTER', $this, 'metaheaders_after');
    }
    /**
     * Disables new uploader in favor of old
     * 
     * @author Myron Turner <turnermm02@shaw.ca>
     */
     function metaheaders_after(&$event, $param) {
            ptln( "\n<script type='text/javascript'>\n //<![CDATA[\n");
            ptln("qq = {};\n //]]>\n</script>");
     }
    /**
     * Adds a checkbox
     * 
     * @author Michael Klier <chi@chimeric.de>
     */
    function handle_form_output(&$event, $param) {
        global $INFO;
        if($this->getConf('manageronly')) {
            if(!$INFO['isadmin'] && !$INFO['ismanager']) return;
        }
        $event->data->addElement(form_makeCheckboxField('unpack', 0, $this->getLang('unpack')));
    }

    /**
     * MEDIA_UPLOAD_FINISH handler
     * 
     * @author Michael Klier <chi@chimeric.de>
     */
    function handle_media_upload(&$event, $param) {
        global $INFO;

        // nothing todo
        if(!isset($_REQUEST['unpack'])) return;

        if($this->getConf('manageronly')) {
            if(!$INFO['isadmin'] && !$INFO['ismanager']) return;
        }

        // our turn - prevent default action
        $event->preventDefault();

        call_user_func_array(array($this,'extract_archive'), $event->data);
    }

    /**
     * Uploads an extracts an archive
     * FIXME add bz and bz2 support
     *
     * @author Michael Klier <chi@chimeric.de>
     */
    function extract_archive($fn_tmp, $fn, $id, $imime) {
        global $lang;
        global $conf;

        $dir = io_mktmpdir();
        if($dir) {
            $this->tmpdir = $dir;
        } else {
            msg('Failed to create tmp dir, check permissions of cache/ directory', -1);
            return false;
        }

        // failed to create tmp dir stop here
        if(!$this->tmpdir) return false;

        $ext = substr($fn, strrpos($fn,'.')+1);

        if(in_array($ext, array('tar','gz','tgz','zip'))) {

            //prepare directory
            //FIXME needed? do it later?
            io_createNamespace($id, 'media');

            if(move_uploaded_file($fn_tmp, $fn)) {

                chmod($fn, $conf['fmode']);

                if($this->decompress($fn, dirname($fn))) {
                    msg($this->getLang('decompr_succ'), 1);
                } else {
                    msg($this->getLang('decompr_err'), -1);
                }

                // delete archive after decompression
                // fixme check for success?
                unlink($fn);

            } else {
                msg($lang['uploadfail'], -1);
            }

        } else {
            msg($this->getLang('unsupported_ftype'), -1);
            return false;
        }

        // remove tmpdir in any case
        rmdir($this->tmpdir);

        // fixme do a sweepNS here, just in case?
    }

    /**
     * Decompress an archive (adopted from plugin manager)
     *
     * @author Christopher Smith <chris@jalakai.co.uk>
     * @author Michael Klier <chi@chimeric.de>
     */
    function decompress($file, $target) {

        // need to source plugin manager because otherwise the ZipLib doesn't work
        // FIXME fix ZipLib.class.php
        require_once(DOKU_INC.'lib/plugins/plugin/admin.php');

        // decompression library doesn't like target folders ending in "/"
        if(substr($target, -1) == "/") $target = substr($target, 0, -1);

        $ext = substr($file, strrpos($file,'.')+1);

        if(in_array($ext, array('tar','gz','tgz'))) {

            require_once(DOKU_INC."inc/TarLib.class.php");

            if(strpos($ext, 'gz') !== false) $compress_type = COMPRESS_GZIP;
            //else if (strpos($ext,'bz') !== false) $compress_type = COMPRESS_BZIP; // FIXME bz2 support
            else $compress_type = COMPRESS_NONE;

            $tar = new TarLib($file, $compress_type);
            $ok  = $tar->Extract(FULL_ARCHIVE, $this->tmpdir, '', 0777);

            if($ok) {
                $files = $tar->ListContents();
                $this->postProcessFiles($target, $files);
                return true;
            } else {
               return false;
            }

        } else if ($ext == 'zip') {

            require_once(DOKU_INC."inc/ZipLib.class.php");

            $zip = new ZipLib();
            $ok  = $zip->Extract($file, $this->tmpdir);

            if($ok) {
                $files = $zip->get_List($file);
                $this->postProcessFiles($target, $files);
                return true;
            } else {
                return false;
            }

        }

        // unsupported file type
        return false;
    }

    /**
     * Checks the mime type and fixes the permission and filenames of the
     * extracted files and sends a notification email per uploaded file
     *
     * @author Michael Klier <chi@chimeric.de>
     */
    function postProcessFiles($dir, $files) {
        global $conf;
        global $lang;

        require_once(DOKU_INC.'inc/media.php');
        $reldir = preg_replace("#".$conf['mediadir']."#", '/', $dir) . '/';

        // get filetype regexp
        $types = array_keys(getMimeTypes());
        $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
        $regex = join('|',$types);

        $dirs     = array();
        $tmp_dirs = array();

        foreach($files as $file) {
            $fn_old = $file['filename'];                                // original filename
            $fn_new = str_replace('/',':',$fn_old);                     // target filename
            $fn_new = str_replace(':', '/', cleanID($fn_new));

            if(substr($fn_old, -1) == '/') { 
                // given file is a directory
                io_mkdir_p($dir.'/'.$fn_new);
                chmod($dir.'/'.$fn_new, $conf['dmode']);
                array_push($dirs, $dir.'/'.$fn_new);
                array_push($tmp_dirs, $this->tmpdir.'/'.$fn_old);
            } else {
                list($ext, $imime) = mimetype($this->tmpdir.'/'.$fn_old);
                
                if(preg_match('/\.('.$regex.')$/i',$fn_old)){
                    // check for overwrite
                    if(@file_exists($dir.'/'.$fn_new) && (!$_POST['ow'] || $auth < AUTH_DELETE)){
                        msg($lang['uploadexist'],0);
                        continue;
                    }

                    // check for valid content
                    $ok = media_contentcheck($this->tmpdir.'/'.$fn_old,$imime);
                    if($ok == -1){
                        msg(sprintf($lang['uploadbadcontent'],".$ext"),-1);
                        unlink($this->tmpdir.'/'.$fn_old);
                        continue;
                    }elseif($ok == -2){
                        msg($lang['uploadspam'],-1);
                        unlink($this->tmpdir.'/'.$fn_old);
                        continue;
                    }elseif($ok == -3){
                        msg($lang['uploadxss'],-1);
                        unlink($this->tmpdir.'/'.$fn_old);
                        continue;
                    }

                    // everything's ok - lets move the file
                    // FIXME check for success ??
                    rename($this->tmpdir.'/'.$fn_old, $dir.'/'.$fn_new);
                    chmod($dir.'/'.$fn_new, $conf['fmode']);

                    // send notification mail
                    $id = cleanID(str_replace('/',':',$reldir.'/'.$fn_new));
                    media_notify($id, $dir.'/'.$fn_new, $imime); 
                    msg($lang['uploadsucc'], 1);

                } else {
                    msg($lang['uploadwrong'],-1);
                    @unlink($this->tmpdir.'/'.$fn_old);
                    continue;
                }
            }
        }

        // done - remove eventually left over empty dirs in destination directory 
        natsort($dirs);
        $dirs = array_reverse($dirs);
        foreach($dirs as $dir) {
            @rmdir($dir);
        }

        // do the same for the tmp dir
        natsort($tmp_dirs);
        $dirs = array_reverse($tmp_dirs);
        foreach($dirs as $dir) {
            @rmdir($dir);
        }
    }
}
// vim:ts=4:sw=4:et:enc=utf8:
