From 1f01f37d4b7e80b30d8f5d8f9479080353352fe8 Mon Sep 17 00:00:00 2001 From: chris <chris@teacherscpd.co.uk> Date: Thu, 25 Aug 2005 01:22:55 +0200 Subject: [PATCH] plugin manager, an admin plugin darcs-hash:20050824232255-50fdc-b5eb608833470cea87d457e029f530b916431b61.gz --- lib/plugins/plugin/admin.php | 686 ++++++++++++++++++++ lib/plugins/plugin/inc/tarlib.class.php | 677 +++++++++++++++++++ lib/plugins/plugin/inc/zip.lib.php | 353 ++++++++++ lib/plugins/plugin/lang/en/admin_plugin.txt | 7 + lib/plugins/plugin/lang/en/lang.php | 58 ++ lib/plugins/plugin/style.css | 49 ++ 6 files changed, 1830 insertions(+) create mode 100644 lib/plugins/plugin/admin.php create mode 100644 lib/plugins/plugin/inc/tarlib.class.php create mode 100644 lib/plugins/plugin/inc/zip.lib.php create mode 100644 lib/plugins/plugin/lang/en/admin_plugin.txt create mode 100644 lib/plugins/plugin/lang/en/lang.php create mode 100644 lib/plugins/plugin/style.css diff --git a/lib/plugins/plugin/admin.php b/lib/plugins/plugin/admin.php new file mode 100644 index 000000000..536dc525b --- /dev/null +++ b/lib/plugins/plugin/admin.php @@ -0,0 +1,686 @@ +<?php +/** + * Plugin management functions + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Christopher Smith <chris@jalakai.co.uk> + */ + +// todo +// - maintain a history of file modified +// - allow a plugin to contain extras to be copied to the current template (extra/tpl/) +// - to images (lib/images/) [ not needed, should go in lib/plugin/images/ ] + +if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); +if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); +require_once(DOKU_PLUGIN.'admin.php'); + + // language stuff here for now ... move to language files when complete + +// global $lang; + + //--------------------------[ GLOBALS ]------------------------------------------------ + // note: probably should be dokuwiki wide globals, where it can be access by pluginutils.php + global $common_plugin_files, $common_plugin_types; + $common_plugin_types = array('syntax', 'admin'); + $common_plugin_files = array("style.css", "screen.css", "print.css", "script.js"); + +/** + * All DokuWiki plugins to extend the admin function + * need to inherit from this class + */ +class admin_plugin_plugin extends DokuWiki_Admin_Plugin { + + var $disabled = 0; + var $plugin = ''; + var $cmd = ''; + var $handler = NULL; + + var $functions = array('delete','update','settings','info'); // require a plugin name + var $commands = array('manage','refresh','download'); // don't require a plugin name + var $plugin_list = array(); + + var $msg = ''; + var $error = ''; + + function admin_plugin_plugin() { + global $conf; + $this->disabled = (!isset($conf['pluginmanager']) || ($conf['pluginmanager'] == 0)); + } + + /** + * return some info + */ + function getInfo(){ + $disabled = ($this->disabled) ? '(disabled)' : ''; + + return array( + 'author' => 'Christopher Smith', + 'email' => 'chris@jalakai.co.uk', + 'date' => '2005-08-10', + 'name' => 'Plugin Manager', + 'desc' => "Manage Plugins, including automated plugin installer $disabled", + 'url' => 'http://wiki.splitbrain.org/plugin:adminplugin', + ); + } + + /** + * return prompt for admin menu + */ + function getMenuText($language) { + if (!$this->disabled) + return parent::getMenuText($language); + + return ''; + } + + /** + * return sort order for position in admin menu + */ + function getMenuSort() { + return 20; + } + + /** + * handle user request + */ + function handle() { + global $ID, $lang; + + if ($this->disabled) return; + + $this->plugin = $_REQUEST['plugin']; + $this->cmd = $_REQUEST['fn']; + if (is_array($this->cmd)) $this->cmd = key($this->cmd); + + sort($this->plugin_list = plugin_list()); + + // verify $_REQUEST vars + if (in_array($this->cmd, $this->commands)) { + $this->plugin = ''; + } else if (!in_array($this->cmd, $this->functions) || !in_array($this->plugin, $this->plugin_list)) { + $this->cmd = 'manage'; + $this->plugin = ''; + } + + // create object to handle the command + $class = "admin_plugin_".$this->cmd; + if (!class_exists($class)) $class = 'admin_plugin_manage'; + + $this->handler = & new $class($this, $plugin); + $this->msg = $this->handler->process(); + } + + /** + * output appropriate html + */ + function html() { + + if ($this->disabled) return; + + // enable direct access to language strings +// if (!$this->localised) $this->setupLocale(); + $this->setupLocale(); + + if ($this->handler === NULL) $this->handler = & new admin_plugin_manage(); + if (!$this->plugin_list) sort($this->plugin_list = plugin_list()); + + ptln('<div id="plugin_manager">'); + $this->handler->html(); + ptln('</div><!-- #plugin_manager -->'); + } + +} + +class admin_plugin_manage { + + var $manager = NULL; + var $lang = array(); + var $plugin = ''; + var $downloaded = array(); + + function admin_plugin_manage(&$manager, $plugin) { + $this->manager = & $manager; + $this->plugin = $plugin; + $this->lang = & $manager->lang; + } + + function process() { + return ''; + } + + function html() { + + print $this->manager->plugin_locale_xhtml('admin_plugin'); + + // FIXME, these probably shouldn't be here any more! + if (!$this->manager->msg) $this->manager->msg = ' '; + ptln("<p>{$manager->msg}</p>"); + + if ($this->manager->error) { + ptln("<p class='error'>".str_replace("\n","<br>",$this->manager->error)."</p>"); + } + + $this->html_menu(); + } + + // build our standard menu + function html_menu($listPlugins = true) { + global $ID; + + ptln('<div class="pm_menu">'); + + ptln('<div class="common">'); + ptln(' <form action="'.wl($ID).'" method="post">'); + ptln(' <fieldset class="hidden">',4); + ptln(' <input type="hidden" name="do" value="admin" />'); + ptln(' <input type="hidden" name="page" value="plugin" />'); + ptln(' </fieldset>'); + ptln(' <fieldset>'); + ptln(' <legend>'.$this->lang['refresh'].'</legend>'); + ptln(' <h3 class="legend">'.$this->lang['refresh'].'</h3>'); + ptln(' <input type="submit" class="button" name="fn[refresh]" value="'.$this->lang['btn_refresh'].'" />'); + ptln(' <p>'.$this->lang['refresh_x'].'</p>'); + ptln(' </fieldset>'); + ptln(' <fieldset>'); + ptln(' <legend>'.$this->lang['download'].'</legend>'); + ptln(' <h3 class="legend">'.$this->lang['download'].'</h3>'); + ptln(' <input type="submit" class="button" name="fn[download]" value="'.$this->lang['btn_download'].'" />'); + ptln(' <label for="url">'.$this->lang['url'].'<input name="url" id="url" class="field" type="text" maxlength="200" /></label>'); + ptln(' </fieldset>'); + ptln(' </form>'); + ptln('</div>'); + + if ($listPlugins) { + ptln('<h2>'.$this->lang['manage'].'</h2>'); + ptln('<div class="plugins">'); + $this->html_pluginlist(); + ptln('</div>'); + } + + ptln('</div>'); + } + + function html_pluginlist() { + + foreach ($this->manager->plugin_list as $plugin) { + + $new = (in_array($plugin, $this->downloaded)) ? ' class="new"' : ''; + + ptln(' <form action="'.wl($ID).'" method="post" '.$new.'>'); + ptln(' <fieldset>'); + ptln(' <legend>'.$plugin.'</legend>'); + ptln(' <h3 class="legend">'.$plugin.'</h3>'); + ptln(' <input type="hidden" name="do" value="admin" />'); + ptln(' <input type="hidden" name="page" value="plugin" />'); + ptln(' <input type="hidden" name="plugin" value="'.$plugin.'" />'); + + $this->html_button('delete', false, 6); + $this->html_button('update', !$this->plugin_readlog($plugin, 'url'), 6); + $this->html_button('settings', !@file_exists(DOKU_PLUGIN.$plugin.'/settings.php'), 6); + $this->html_button('info', false, 6); + + ptln(' </fieldset>'); + ptln(' </form>'); + } + } + + function html_button($btn, $disabled=false, $indent=0) { + $disabled = ($disabled) ? 'disabled="disabled"' : ''; + ptln('<input type="submit" class="button" '.$disabled.' name="fn['.$btn.']" value="'.$this->lang['btn_'.$btn].'" />',$indent); + } + + /** + * Rebuild aggregated files & update latest plugin date + */ + function refresh() { + global $lang; + global $common_plugin_files; + + sort($this->manager->plugin_list = plugin_list()); + + foreach ($common_plugin_files as $file) { + $aggregate = ''; + + // could replace with an class/object based aggregator, + // that way special files could have their own aggregator + foreach ($this->manager->plugin_list as $plugin) { + if (@file_exists(DOKU_PLUGIN."$plugin/$file")) { + $contents = @file_get_contents(DOKU_PLUGIN."$plugin/$file")."\n"; + + // url conversion for css files + if (is_css($file)) { + $contents = preg_replace('/(url\([\'\"]?)([^\/](?![a-zA-Z0-9]+:\/\/).*?)([\'\"]?\))/','$1'.$plugin.'/$2$3',$contents); + } + + $aggregate .= $contents; + } + } + + if (trim($aggregate)) { + if (!io_savefile(DOKU_PLUGIN."plugin_$file", $aggregate)) { + $this->manager->error .= sprintf($this->lang['error_write'],$file); + } + } + } + + // update latest plugin date - FIXME + return (!$this->manager->error); + } + + // log + function plugin_writelog($plugin, $cmd, $data) { + + $file = DOKU_PLUGIN.$plugin.'/manager.dat'; + + switch ($cmd) { + case 'install' : + $url = $data[0]; + $date = date('r'); + if (!$fp = @fopen($file, 'w')) return; + fwrite($fp, "installed=$date\nurl=$url\n"); + fclose($fp); + break; + + case 'update' : + $date = date('r'); + if (!$fp = @fopen($file, 'w+')) return; + fwrite($fp, "updated=$date\n"); + fclose($fp); + break; + } + } + + function plugin_readlog($plugin, $field) { + static $log = array(); + $file = DOKU_PLUGIN.$plugin.'/manager.dat'; + + if (!isset($log[$plugin])) { + $tmp = @file_get_contents($file); + if (!$tmp) return ''; + $log[$plugin] = & $tmp; + } + + if ($field == 'ALL') { + return $log[$plugin]; + } + + if (preg_match_all('/'.$field.'=(.*)$/m',$log[$plugin], $match=array())) + return implode("\n", $match[1]); + + return ''; + } + } + + class admin_plugin_refresh extends admin_plugin_manage { + + function process() { + $this->refresh(); + + if (!$this->manager->error) return $this->lang['refreshed']; + } + + function html() { + + parent::html(); + + ptln('<div class="pm_info">'); + ptln('<h2>'.$this->lang['refreshing'].'</h2>'); + ptln('<p>'.$this->lang['refreshed'].'</p>'); + ptln('</div>'); + } + + } + + class admin_plugin_download extends admin_plugin_manage { + + var $overwrite = false; + + function process() { + global $lang, $conf; + + $plugin_url = $_REQUEST['url']; + if (!preg_match("/[^\/]*$/", $plugin_url, $matches = array()) || !$matches[0]) { + $this->manager->error = $this->lang['error_badurl'].'\n'; + return ''; + } + + $file = $matches[0]; + $folder = "p".md5($file.date('r')); // tmp folder name - will be empty (should really make sure it doesn't already exist) + $tmp = DOKU_PLUGIN."tmp/$folder"; + + if (!$this->manager->error && !ap_mkdir($tmp)) { + $this->manager->error = $this->lang['error_dir_create'].'\n'; + $folder = ''; + } + + if (!$this->manager->error && !io_download($plugin_url, "$tmp/$file")) { + $this->manager->error = sprintf($this->lang['error_download'],$url)."\n"; + } + + ap_decompress("$tmp/$file", $tmp); + + // search tmp/$folder for the directory that has been created + // move that directory to lib/plugins/ + if ($dh = @opendir("$tmp/")) { + while (false !== ($f = readdir($dh))) { + if ($f == '.' || $f == '..' || $f == 'tmp') continue; + if (!is_dir("$tmp/$f")) continue; + + // check to make sure we aren't overwriting anything + if (file_exists(DOKU_PLUGIN."/$f")) { + // remember our settings, ask the user to confirm overwrite, FIXME + continue; + } + + ap_copy("$tmp/$f", DOKU_PLUGIN.$f); + $this->downloaded[] = $f; + $this->plugin_writelog($f, 'install', array($plugin_url)); + } + closedir($dh); + } + + // cleanup + if ($folder && is_dir(DOKU_PLUGIN."tmp/$folder")) ap_delete(DOKU_PLUGIN."tmp/$folder"); + + if (!$this->manager->error) { + $this->refresh(); + } + + return ''; + } + + function html() { + parent::html(); + + ptln('<div class="pm_info">'); + ptln('<h2>'.$this->lang['downloading'].'</h2>'); + + if ($this->manager->error) { + ptln('<p class="error">'.$this->manager->error.'</p>'); + } else if (count($this->downloaded) == 1) { + ptln('<p>'.sprintf($this->lang['downloaded'],$this->downloaded[0]).'</p>'); + } else if (count($this->downloaded)) { // more than one plugin in the download + ptln('<p>'.$this->lang['downloads'].'</p>'); + ptln('<ul>'); + foreach ($this->downloaded as $plugin) { + ptln('<li>'.$plugin.'</li>',2); + } + ptln('</ul>'); + } else { // none found in download + ptln('<p>'.$this->lang['download_none'].'</p>'); + } + ptln('</div>'); + } + + } + + class admin_plugin_delete extends admin_plugin_manage { + + function process() { + + $deleted = $this->manager->plugin; + ap_delete(DOKU_PLUGIN.$deleted); + $this->plugin = ''; + + $this->refresh(); + return "Plugin $deleted deleted"; + } + } + + class admin_plugin_info extends admin_plugin_manage { + + var $plugin_info = array(); // the plugin itself + var $details = array(); // any component plugins + + function process() { + + // sanity check + if (!$this->manager->plugin) { return; } + + $component_list = ap_plugin_components($this->manager->plugin); + usort($component_list, ap_component_sort); + + foreach ($component_list as $component) { + if ($obj = & plugin_load($component['type'],$component['name']) === NULL) continue; + + $this->details[] = array_merge($obj->getInfo(), array('type' => $component['type'])); + unset($obj); + } + + // review details to simplify things + foreach($this->details as $info) { + foreach($info as $item => $value) { + if (!isset($this->plugin_info[$item])) { $this->plugin_info[$item] = $value; continue; } + if ($this->plugin_info[$item] != $value) $this->plugin_info[$item] = ''; + } + } + } + + function html() { + + // output the standard menu stuff + parent::html(); + + // sanity check + if (!$this->manager->plugin) { return; } + + ptln('<div class="pm_info">'); + ptln("<h2>Plugin: {$this->manager->plugin}</h2>"); + + // collect pertinent information from the log + $installed = $this->plugin_readlog($this->manager->plugin, 'installed'); + $source = $this->plugin_readlog($this->manager->plugin, 'url'); + $updated = substr(strrchr("\n".$this->plugin_readlog($this->manager->plugin, 'updated'), '\n'), 1); + + ptln("<dl>",2); + ptln("<dt>".$this->manager->getLang('installed').'</dt><dd>'.($installed ? $installed : $this->manager->getLang('unknown'))."</dd>",4); + if ($updated) ptln("<dt>".$this->manager->getLang('lastupdate').'</dt><dd>'.$updated."</dd>",4); + ptln("<dt>".$this->manager->getLang('source').'</dt><dd>'.($source ? $source : $this->manager->getLang('unknown'))."</dd>",4); + ptln("</dl>",2); + + if (count($this->details) == 0) { + ptln("<p>This plugin returned no information, it may be invalid.</p>",2); + } else { + + ptln("<dl>",2); + if ($this->plugin_info['name']) ptln("<dt>Name</dt><dd>".$this->out($this->plugin_info['name'])."</dd>",4); + if ($this->plugin_info['type']) ptln("<dt>Type</dt><dd>".$this->out($this->plugin_info['type'])."</dd>",4); + if ($this->plugin_info['desc']) ptln("<dt>Description</dt><dd>".$this->out($this->plugin_info['desc'])."</dd>",4); + if ($this->plugin_info['author']) ptln("<dt>Author</dt><dd>".$this->manager->plugin_email($this->plugin_info['email'], $this->plugin_info['author'])."</dd>",4); + if ($this->plugin_info['url']) ptln("<dt>Web</dt><dd>".$this->manager->plugin_link($this->plugin_info['url'], '', 'urlextern')."</dd>",4); + ptln("</dl>",2); + + if (count($this->details) > 1) { + ptln("<h3>Components</h3>",2); + ptln("<div>",2); + + foreach ($this->details as $info) { + + ptln("<dl>",4); + if (!$this->plugin_info['name']) ptln("<dt>Name</dt><dd>".$this->out($info['name'])."</dd>",6); + if (!$this->plugin_info['type']) ptln("<dt>Type</dt><dd>".$this->out($info['type'])."</dd>",6); + if (!$this->plugin_info['desc']) ptln("<dt>Description</dt><dd>".$this->out($info['desc'])."</dd>",6); + if (!$this->plugin_info['author']) ptln("<dt>Author</dt><dd>".$this->manager->plugin_email($info['email'], $info['author'])."</dd>",6); + if (!$this->plugin_info['url']) ptln("<dt>Web</dt><dd>".$this->manager->plugin_link($info['url'], '', 'urlextern')."</dd>",6); + ptln("</dl>",4); + + } + ptln("</div>",2); + } + } + ptln("</div>"); + } + + // simple output filter, make html entities safe and convert new lines to <br /> + function out($text) { + return str_replace("\n",'<br />',htmlentities($text)); + } + + } + + //--------------[ to do ]--------------------------------------- + class admin_plugin_update extends admin_plugin_manage { + + function html() { + parent::html(); + + ptln('<div class="pm_info">'); + ptln('<h2>'.$this->lang['updating'].'</h2>'); + + if ($this->manager->error) { + ptln('<p class="error">'.$this->manager->error.'</p>'); + } else if (count($this->downloaded) == 1) { + ptln('<p>'.sprintf($this->lang['downloaded'],$this->downloaded[0]).'</p>'); + } else if (count($this->downloaded)) { // more than one plugin in the download + ptln('<p>'.$this->lang['downloads'].'</p>'); + ptln('<ul>'); + foreach ($this->downloaded as $plugin) { + ptln('<li>'.$plugin.'</li>',2); + } + ptln('</ul>'); + } else { // none found in download + ptln('<p>'.$this->lang['download_none'].'</p>'); + } + ptln('<p>Under Construction</p>'); + ptln('</div>'); + } + } + class admin_plugin_settings extends admin_plugin_manage {} + + //--------------[ utilities ]----------------------------------- + + function is_css($f) { return (substr($f, -4) == '.css'); } + + // generate an admin plugin href + function apl($pl, $fn) { return wl($ID,"do=admin&page=plugin".($pl?"&plugin=$pl":"").($fn?"&fn=$fn":"")); } + + // generate a complete admin plugin link (may change to button) + function ap_link($pl, $fn, $txt) { + return '<a href="'.apl($pl, $fn).'">['.$txt.']</a>'; + } + + // decompress wrapper + function ap_decompress($file, $target) { + + // decompression library doesn't like target folders ending in "/" + if (substr($target, -1) == "/") $target = substr($target, 0, -1); + + // .tar, .tar.bz, .tar.gz + if (preg_match("/\.tar(\.bz2?|\.gz)?$/", $file)) { + + require_once(DOKU_PLUGIN."plugin/inc/tarlib.class.php"); + + $tar = new CompTar($file, COMPRESS_DETECT); + $ok = $tar->Extract(FULL_ARCHIVE, $target, '', 0777); + + // sort something out for handling tar error messages meaningfully + if ($ok<0) ptln("<p>tar error:".$tar->TarErrorStr($ok)."</p>"); + return ($ok<0?false:true); + } + + if (substr($file, -4) == ".zip") { + + require_once(DOKU_PLUGIN."plugin/inc/zip.lib.php"); + + $zip = new zip(); + $ok = $zip->Extract($file, $target); + + // sort something out for handling zip error messages meaningfully + if ($ok==-1) ptln("<p>zip error:</p>"); + return ($ok==-1?false:true); + } + + if (substr($file, -4) == ".rar") { + // not yet supported -- fix me + return false; + } + + // unsupported file type + return false; + } + + // possibly should use io_MakeFileDir, not sure about using its method of error handling + function ap_mkdir($d) { + global $conf; + + umask($conf['dmask']); + $ok = io_mkdir_p($d); + umask($conf['umask']); + return $ok; + } + + // copy with recursive sub-directory support + function ap_copy($src, $dst) { + + if (is_dir($src)) { + if (!$dh = @opendir($src)) return false; + + if ($ok = ap_mkdir($dst)) { + while ($ok && $f = readdir($dh)) { + if ($f == '..' || $f == '.') continue; + $ok = ap_copy("$src/$f", "$dst/$f"); + } + } + + closedir($dh); + return $ok; + + } else { + if (!@copy($src,$dst)) return false; + touch($dst,filemtime($src)); + } + + return true; + } + + // delete, with recursive sub-directory support + function ap_delete($path) { + + if (!is_string($path) || $path == "") return; + + if (is_dir($path)) { + if (!$dh = @opendir($path)) return; + + while ($f = readdir($dh)) { + if ($f == '..' || $f == '.') continue; + ap_delete("$path/$f"); + } + + closedir($dh); + rmdir($path); + return; + + } else { + unlink($path); + } + } + + // return a list (name & type) of all the component plugins that make up this plugin + // can this move to pluginutils? + function ap_plugin_components($plugin) { + + global $common_plugin_types; + + $components = array(); + $path = DOKU_PLUGIN.$plugin.'/'; + + foreach ($common_plugin_types as $type) { + if (file_exists($path.$type.'.php')) { $components[] = array('name'=>$plugin, 'type'=>$type); continue; } + + if ($dh = @opendir($path.$type.'/')) { + while (false !== ($cp = readdir($dh))) { + if ($cp == '.' || $cp == '..' || strtolower(substr($cp,-4)) != '.php') continue; + $components[] = array('name'=>$plugin.'_'.substr($cp, 0, -4), 'type'=>$type); + } + closedir($dh); + } + } + return $components; + } + + function ap_component_sort($a, $b) { + if ($a['name'] == $b['name']) return 0; + return ($a['name'] < $b['name']) ? -1 : 1; + } + diff --git a/lib/plugins/plugin/inc/tarlib.class.php b/lib/plugins/plugin/inc/tarlib.class.php new file mode 100644 index 000000000..cda8254d5 --- /dev/null +++ b/lib/plugins/plugin/inc/tarlib.class.php @@ -0,0 +1,677 @@ +<?php + +/* + +---------------------------------------------+ + | TAR format class - Creates TAR archives | + +---------------------------------------------+ + | This class is part or the MaxgComp suite | + +---------------------------------------------+ + | Created by the Maxg Network (maxg.info) | + | http://docs.maxg.info for help & license. | + +---------------------------------------------+ + | Author: Bouchon <tarlib@bouchon.org> (Maxg) | + +---------------------------------------------+ +* Modified for Dokuwiki +* @author Christopher Smith <chris@jalakai.co.uk> +*/ + +define('COMPRESS_GZIP',1); +define('COMPRESS_BZIP',2); +define('COMPRESS_AUTO',3); +define('COMPRESS_NONE',0); + +define('TARLIB_VERSION','1.2'); +define('FULL_ARCHIVE',-1); + +define('ARCHIVE_DYNAMIC',0); +define('ARCHIVE_RENAMECOMP',5); +define('COMPRESS_DETECT',-1); + +class CompTar +{ + var $_comptype; + var $_compzlevel; + var $_fp; + var $_memdat; + var $_nomf; + var $_result; + + function CompTar($p_filen = ARCHIVE_DYNAMIC , $p_comptype = COMPRESS_AUTO, $p_complevel = 9) + { + $this->_nomf = $p_filen; $flag=0; + if($p_comptype && $p_comptype % 5 == 0){$p_comptype /= ARCHIVE_RENAMECOMP; $flag=1;} + + if($p_complevel > 0 && $p_complevel <= 9) $this->_compzlevel = $p_complevel; + else $p_complevel = 9; + + if($p_comptype == COMPRESS_DETECT) + { + if(strtolower(substr($p_filen,-3)) == '.gz') $p_comptype = COMPRESS_GZIP; +// elseif(strtolower(substr($p_filen,-4)) == '.bz2') $p_comptype = COMPRESS_BZIP; -- CS + elseif(preg_match( "/\.bz2?$/", $p_filen)) $p_comptype = COMPRESS_BZIP; //--CS bz or bz2 + else $p_comptype = COMPRESS_NONE; + } + + switch($p_comptype) + { + case COMPRESS_GZIP: + if(!extension_loaded('zlib')) $this->_result = -1; + $this->_comptype = COMPRESS_GZIP; + break; + + case COMPRESS_BZIP: + if(!extension_loaded('bz2')) $this->_result = -2; + $this->_comptype = COMPRESS_BZIP; + break; + + case COMPRESS_AUTO: + if(extension_loaded('zlib')) + $this->_comptype = COMPRESS_GZIP; + elseif(extension_loaded('bz2')) + $this->_comptype = COMPRESS_BZIP; + else + $this->_comptype = COMPRESS_NONE; + break; + + default: + $this->_comptype = COMPRESS_NONE; + } + + if($this->_result < 0) $this->_comptype = COMPRESS_NONE; + + if($flag) $this->_nomf.= '.'.$this->getCompression(1); + $this->_result = true; + } + + function setArchive($p_name='', $p_comp = COMPRESS_AUTO, $p_level=9) + { + $this->_CompTar(); + $this->CompTar($p_name, $p_comp, $p_level); + return $this->_result; + } + + function getCompression($ext = false) + { + $exts = Array('tar','tar.gz','tar.bz2'); + if($ext) return $exts[$this->_comptype]; + return $this->_comptype; + } + + function setCompression($p_comp = COMPRESS_AUTO) + { + $this->setArchive($this->_nomf, $p_comp, $this->_compzlevel); + return $this->_compzlevel; + } + + function getDynamicArchive() + { + return $this->_encode($this->_memdat); + } + + function writeArchive($p_archive) + { + if(!$this->_memdat) return -7; + $fp = @fopen($p_archive, 'wb'); + if(!$fp) return -6; + + fwrite($fp, $this->_memdat); + fclose($fp); + + return true; + } + + function sendClient($name = '', $archive = '', $headers = TRUE) + { + if(!$name && !$this->_nomf) return -9; + if(!$archive && !$this->_memdat) return -10; + if(!$name) $name = basename($this->_nomf); + + if($archive){ if(!file_exists($archive)) return -11; } + else $decoded = $this->getDynamicArchive(); + + if($headers) + { + header('Content-Type: application/x-gtar'); + header('Content-Disposition: attachment; filename='.basename($name)); + header('Accept-Ranges: bytes'); + header('Content-Length: '.($archive ? filesize($archive) : strlen($decoded))); + } + + if($archive) + { + $fp = @fopen($archive,'rb'); + if(!$fp) return -4; + + while(!foef($fp)) echo fread($fp,2048); + } + else + { + echo $decoded; + } + + return true; + } + + function Extract($p_what = FULL_ARCHIVE, $p_to = '.', $p_remdir='', $p_mode = 0755) + { + if(!$this->_OpenRead()) return -4; +// if(!@is_dir($p_to)) if(!@mkdir($p_to, 0777)) return -8; --CS + if(!@is_dir($p_to)) if(!$this->_dirApp($p_to)) return -8; //--CS (route through correct dir fn) + + $ok = $this->_extractList($p_to, $p_what, $p_remdir, $p_mode); + $this->_CompTar(); + + return $ok; + } + + function Create($p_filelist,$p_add='',$p_rem='') + { + if(!$fl = $this->_fetchFilelist($p_filelist)) return -5; + if(!$this->_OpenWrite()) return -6; + + $ok = $this->_addFileList($fl,$p_add,$p_rem); + + if($ok) $this->_writeFooter(); + else{ $this->_CompTar(); @unlink($this->_nomf); } + + return $ok; + } + + function Add($p_filelist, $p_add = '', $p_rem = '') + { + if (($this->_nomf != ARCHIVE_DYNAMIC && @is_file($this->_nomf)) || ($this->_nomf == ARCHIVE_DYNAMIC && !$this->_memdat)) + return $this->Create($p_filelist, $p_add, $p_rem); + + if(!$fl = $this->_fetchFilelist($p_filelist)) return -5; + return $this->_append($fl, $p_add, $p_rem); + } + + function ListContents() + { + if(!$this->_nomf) return -3; + if(!$this->_OpenRead()) return -4; + + $result = Array(); + + while ($dat = $this->_read(512)) + { + $dat = $this->_readHeader($dat); + if(!is_array($dat)) continue; + + $this->_seek(ceil($dat['size']/512)*512,1); + $result[] = $dat; + } + + return $result; + } + + function TarErrorStr($i) + { + $ecodes = Array( + 1 => TRUE, + 0 => "Undocumented error", + -1 => "Can't use COMPRESS_GZIP compression : ZLIB extensions are not loaded !", + -2 => "Can't use COMPRESS_BZIP compression : BZ2 extensions are not loaded !", + -3 => "You must set a archive file to read the contents !", + -4 => "Can't open the archive file for read !", + -5 => "Invalide file list !", + -6 => "Can't open the archive in write mode !", + -7 => "There is no ARCHIVE_DYNAMIC to write !", + -8 => "Can't create the directory to extract files !", + -9 => "Please pass a archive name to send if you made created an ARCHIVE_DYNAMIC !", + -10 => "You didn't pass an archive filename and there is no stored ARCHIVE_DYNAMIC !", + -11 => "Given archive doesn't exist !" + ); + + return isset($ecodes[$i]) ? $ecodes[$i] : $ecodes[0]; + } + + function TarInfo($headers = true) + { + if($headers) + { + ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html> + +<head> + <title>MaxgComp TAR</title> + <style type="text/css"> + body{margin: 20px;} + body,td{font-size:10pt;font-family: arial;} + </style> + <meta name="Author" content="The Maxg Network, http://maxg.info" /> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +</head> + +<body bgcolor="#EFEFEF"> +<?php + } +?> +<table border="0" align="center" width="500" cellspacing="4" cellpadding="5" style="border:1px dotted black;"> +<tr> + <td align="center" bgcolor="#DFDFEF" colspan="3" style="font-size:15pt;font-color:#330000;border:1px solid black;">MaxgComp TAR</td> +</tr> +<tr> + <td colspan="2" bgcolor="#EFEFFE" style="border:1px solid black;">This software was created by the Maxg Network, <a href="http://maxg.info" target="_blank" style="text-decoration:none;color:#333366;">http://maxg.info</a> + <br />It is distributed under the GNU <a href="http://www.gnu.org/copyleft/lesser.html" target="_blank" style="text-decoration:none;color:#333366;">Lesser General Public License</a> + <br />You can find the documentation of this class <a href="http://docs.maxg.info" target="_blank" style="text-decoration:none;color:#333366;">here</a></td> + <td width="60" bgcolor="#EFEFFE" style="border:1px solid black;" align="center"><img src="http://img.maxg.info/menu/tar.gif" border="0" alt="MaxgComp TAR" /></td> +</tr> +<tr> + <td width="50%" align="center" style="border:1px solid black;" bgcolor="#DFDFEF">MaxgComp TAR version</td> + <td colspan="2" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"><?=TARLIB_VERSION?></td> +</tr> +<tr> + <td width="50%" align="center" style="border:1px solid black;" bgcolor="#DFDFEF">ZLIB extensions</td> + <td colspan="2" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"><?=(extension_loaded('zlib') ? '<b>Yes</b>' : '<i>No</i>')?></td> +</tr> +<tr> + <td width="50%" align="center" style="border:1px solid black;" bgcolor="#DFDFEF">BZ2 extensions</td> + <td colspan="2" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"><?=(extension_loaded('bz2') ? '<b>Yes</b>' : '<i>No</i>')?></td> +</tr> +<tr> + <td width="50%" align="center" style="border:1px solid black;" bgcolor="#DFDFEF">Allow URL fopen</td> + <td colspan="2" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"><?=(ini_get('allow_url_fopen') ? '<b>Yes</b>' : '<i>No</i>')?></td> +</tr> +<tr> + <td width="50%" align="center" style="border:1px solid black;" bgcolor="#DFDFEF">Time limit</td> + <td colspan="2" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"><?=ini_get('max_execution_time')?></td> +</tr> +<tr> + <td width="50%" align="center" style="border:1px solid black;" bgcolor="#DFDFEF">PHP Version</td> + <td colspan="2" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"><?=phpversion()?></td> +</tr> +<tr> + <td colspan="3" align="center" bgcolor="#EFEFFE" style="border:1px solid black;"> + <i>Special thanks to « Vincent Blavet » for his PEAR::Archive_Tar class</i> + </td> +</tr> +</table> +<?php + if($headers) echo '</body></html>'; + } + + function _seek($p_flen, $tell=0) + { + if($this->_nomf === ARCHIVE_DYNAMIC) + $this->_memdat=substr($this->_memdat,0,($tell ? strlen($this->_memdat) : 0) + $p_flen); + elseif($this->_comptype == COMPRESS_GZIP) + @gzseek($this->_fp, ($tell ? @gztell($this->_fp) : 0)+$p_flen); + elseif($this->_comptype == COMPRESS_BZIP) + @fseek($this->_fp, ($tell ? @ftell($this->_fp) : 0)+$p_flen); + else + @fseek($this->_fp, ($tell ? @ftell($this->_fp) : 0)+$p_flen); + } + + function _OpenRead() + { + if($this->_comptype == COMPRESS_GZIP) + $this->_fp = @gzopen($this->_nomf, 'rb'); + elseif($this->_comptype == COMPRESS_BZIP) + $this->_fp = @bzopen($this->_nomf, 'rb'); + else + $this->_fp = @fopen($this->_nomf, 'rb'); + + return ($this->_fp ? true : false); + } + + function _OpenWrite($add = 'w') + { + if($this->_nomf === ARCHIVE_DYNAMIC) return true; + + if($this->_comptype == COMPRESS_GZIP) + $this->_fp = @gzopen($this->_nomf, $add.'b'.$this->_compzlevel); + elseif($this->_comptype == COMPRESS_BZIP) + $this->_fp = @bzopen($this->_nomf, $add.'b'); + else + $this->_fp = @fopen($this->_nomf, $add.'b'); + + return ($this->_fp ? true : false); + } + + function _CompTar() + { + if($this->_nomf === ARCHIVE_DYNAMIC || !$this->_fp) return; + + if($this->_comptype == COMPRESS_GZIP) @gzclose($this->_fp); + elseif($this->_comptype == COMPRESS_BZIP) @bzclose($this->_fp); + else @fclose($this->_fp); + } + + function _read($p_len) + { + if($this->_comptype == COMPRESS_GZIP) + return @gzread($this->_fp,$p_len); + elseif($this->_comptype == COMPRESS_BZIP) + return @bzread($this->_fp,$p_len); + else + return @fread($this->_fp,$p_len); + } + + function _write($p_data) + { + if($this->_nomf === ARCHIVE_DYNAMIC) $this->_memdat .= $p_data; + elseif($this->_comptype == COMPRESS_GZIP) + return @gzwrite($this->_fp,$p_data); + + elseif($this->_comptype == COMPRESS_BZIP) + return @bzwrite($this->_fp,$p_data); + + else + return @fwrite($this->_fp,$p_data); + } + + function _encode($p_dat) + { + if($this->_comptype == COMPRESS_GZIP) + return gzencode($p_dat, $this->_compzlevel); + elseif($this->_comptype == COMPRESS_BZIP) + return bzcompress($p_dat, $this->_compzlevel); + else return $p_dat; + } + + function _readHeader($p_dat) + { + if (!$p_dat || strlen($p_dat) != 512) return false; + + for ($i=0, $chks=0; $i<148; $i++) + $chks += ord($p_dat[$i]); + + for ($i=156,$chks+=256; $i<512; $i++) + $chks += ord($p_dat[$i]); + + $headers = @unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $p_dat); + if(!$headers) return false; + + $return['checksum'] = OctDec(trim($headers['checksum'])); + if ($return['checksum'] != $chks) return false; + + $return['filename'] = trim($headers['filename']); + $return['mode'] = OctDec(trim($headers['mode'])); + $return['uid'] = OctDec(trim($headers['uid'])); + $return['gid'] = OctDec(trim($headers['gid'])); + $return['size'] = OctDec(trim($headers['size'])); + $return['mtime'] = OctDec(trim($headers['mtime'])); + $return['typeflag'] = $headers['typeflag']; + $return['link'] = trim($headers['link']); + $return['uname'] = trim($headers['uname']); + $return['gname'] = trim($headers['gname']); + + return $return; + } + + function _fetchFilelist($p_filelist) + { + if(!$p_filelist || (is_array($p_filelist) && !@count($p_filelist))) return false; + + if(is_string($p_filelist)) + { + $p_filelist = explode('|',$p_filelist); + if(!is_array($p_filelist)) $p_filelist = Array($p_filelist); + } + + return $p_filelist; + } + + function _addFileList($p_fl, $p_addir, $p_remdir) + { + foreach($p_fl as $file) + { + if(($file == $this->_nomf && $this->_nomf != ARCHIVE_DYNAMIC) || !$file || (!file_exists($file) && !is_array($file))) + continue; + + if (!$this->_addFile($file, $p_addir, $p_remdir)) + continue; + + if (@is_dir($file)) + { + $d = @opendir($file); + + if(!$d) continue; + readdir($d); readdir($d); + + while($f = readdir($d)) + { + if($file != ".") $tmplist[0] = "$file/$f"; + else $tmplist[0] = $d; + + $this->_addFileList($tmplist, $p_addir, $p_remdir); + } + + closedir($d); unset($tmplist,$f); + } + } + return true; + } + + function _addFile($p_fn, $p_addir = '', $p_remdir = '') + { + if(is_array($p_fn)) list($p_fn, $data) = $p_fn; + $sname = $p_fn; + + if($p_remdir) + { + if(substr($p_remdir,-1) != '/') $p_remdir .= "/"; + + if(substr($sname, 0, strlen($p_remdir)) == $p_remdir) + $sname = substr($sname, strlen($p_remdir)); + } + + if($p_addir) $sname = $p_addir.(substr($p_addir,-1) == '/' ? '' : "/").$sname; + + if(strlen($sname) > 99) return; + + if(@is_dir($p_fn)) + { + if(!$this->_writeFileHeader($p_fn, $sname)) return false; + } + else + { + if(!$data) + { + $fp = fopen($p_fn, 'rb'); + if(!$fp) return false; + } + + if(!$this->_writeFileHeader($p_fn, $sname, ($data ? strlen($data) : FALSE))) return false; + + if(!$data) + { + while(!feof($fp)) + { + $packed = pack("a512", fread($fp,512)); + $this->_write($packed); + } + fclose($fp); + } + else + { + for($s = 0; $s < strlen($data); $s += 512) + $this->_write(pack("a512",substr($data,$s,512))); + } + } + + return true; + } + + function _writeFileHeader($p_file, $p_sname, $p_data=false) + { + if(!$p_data) + { + if (!$p_sname) $p_sname = $p_file; + $p_sname = $this->_pathTrans($p_sname); + + $h_info = stat($p_file); + $h[0] = sprintf("%6s ", DecOct($h_info[4])); + $h[] = sprintf("%6s ", DecOct($h_info[5])); + $h[] = sprintf("%6s ", DecOct(fileperms($p_file))); + clearstatcache(); + $h[] = sprintf("%11s ", DecOct(filesize($p_file))); + $h[] = sprintf("%11s", DecOct(filemtime($p_file))); + + $dir = @is_dir($p_file) ? '5' : ''; + } + else + { + $dir = ''; + $p_data = sprintf("%11s ", DecOct($p_data)); + $time = sprintf("%11s ", DecOct(time())); + $h = Array(" 0 "," 0 "," 40777 ",$p_data,$time); + } + + $data_first = pack("a100a8a8a8a12A12", $p_sname, $h[2], $h[0], $h[1], $h[3], $h[4]); + $data_last = pack("a1a100a6a2a32a32a8a8a155a12", $dir, '', '', '', '', '', '', '', '', ""); + + for ($i=0,$chks=0; $i<148; $i++) + $chks += ord($data_first[$i]); + + for ($i=156, $chks+=256, $j=0; $i<512; $i++, $j++) + $chks += ord($data_last[$j]); + + $this->_write($data_first); + + $chks = pack("a8",sprintf("%6s ", DecOct($chks))); + $this->_write($chks.$data_last); + + return true; + } + + function _append($p_filelist, $p_addir="", $p_remdir="") + { + if(!$this->_fp) if(!$this->_OpenWrite('a')) return -6; + + if($this->_nomf == ARCHIVE_DYNAMIC) + { + $s = strlen($this->_memdat); + $this->_memdat = substr($this->_memdat,0,-512); + } + else + { + $s = filesize($this->_nomf); + $this->_seek($s-512); + } + + $ok = $this->_addFileList($p_filelist, $p_addir, $p_remdir); + $this->_writeFooter(); + + return $ok; + } + + function _pathTrans($p_dir) + { + if ($p_dir) + { + $subf = explode("/", $p_dir); $r=''; + + for ($i=count($subf)-1; $i>=0; $i--) + { + if ($subf[$i] == ".") {} + else if ($subf[$i] == "..") $i--; + else if (!$subf[$i] && $i!=count($subf)-1 && $i) {} + else $r = $subf[$i].($i!=(count($subf)-1) ? "/".$r : ""); + } + } + return $r; + } + + function _writeFooter() + { + $this->_write(pack("a512", "")); + } + + function _extractList($p_to, $p_files, $p_remdir, $p_mode = 0755) + { + if (!$p_to || ($p_to[0]!="/"&&substr($p_to,0,3)!="../"&&substr($p_to,1,3)!=":\\")) /*" // <- PHP Coder bug */ + $p_to = "./$p_to"; + + if ($p_remdir && substr($p_remdir,-1)!='/') $p_remdir .= '/'; + $p_remdirs = strlen($p_remdir); + while($dat = $this->_read(512)) + { + $headers = $this->_readHeader($dat); + if(!$headers['filename']) continue; + + if($p_files == -1 || $p_files[0] == -1) $extract = true; + else + { + $extract = false; + + foreach($p_files as $f) + { + if(substr($f,-1) == "/") { + if((strlen($headers['filename']) > strlen($f)) && (substr($headers['filename'],0,strlen($f))==$f)) { + $extract = true; break; + } + } + elseif($f == $headers['filename']) { + $extract = true; break; + } + } + } + + if ($extract) + { + $det[] = $headers; + if ($p_remdir && substr($headers['filename'],0,$p_remdirs)==$p_remdir) + $headers['filename'] = substr($headers['filename'],$p_remdirs); + + if($headers['filename'].'/' == $p_remdir && $headers['typeflag']=='5') continue; + + if ($p_to != "./" && $p_to != "/") + { + while($p_to{-1}=="/") $p_to = substr($p_to,0,-1); + + if($headers['filename']{0} == "/") + $headers['filename'] = $p_to.$headers['filename']; + else + $headers['filename'] = $p_to."/".$headers['filename']; + } + + $ok = $this->_dirApp($headers['typeflag']=="5" ? $headers['filename'] : dirname($headers['filename'])); + if($ok < 0) return $ok; + + if (!$headers['typeflag']) + { + if (!$fp = @fopen($headers['filename'], "wb")) return -6; + $n = floor($headers['size']/512); + + for ($i=0; $i<$n; $i++) fwrite($fp, $this->_read(512),512); + if (($headers['size'] % 512) != 0) fwrite($fp, $this->_read(512), $headers['size'] % 512); + + fclose($fp); + touch($headers['filename'], $headers['mtime']); + chmod($headers['filename'], $p_mode); + } + else + { + $this->_seek(ceil($headers['size']/512)*512,1); + } + }else $this->_seek(ceil($headers['size']/512)*512,1); + } + return $det; + } + +function _dirApp($d) + { +// map to dokuwiki function (its more robust) + return ap_mkdir($d); +/* + $d = explode('/', $d); + $base = ''; + + foreach($d as $f) + { + if(!is_dir($base.$f)) + { + $ok = @mkdir($base.$f, 0777); + if(!$ok) return false; + } + $base .= "$f/"; + } +*/ + } + +} + diff --git a/lib/plugins/plugin/inc/zip.lib.php b/lib/plugins/plugin/inc/zip.lib.php new file mode 100644 index 000000000..351b68e70 --- /dev/null +++ b/lib/plugins/plugin/inc/zip.lib.php @@ -0,0 +1,353 @@ +<?php + +/** + * @author bouchon + * @link http://dev.maxg.info + * @link http://forum.maxg.info + * + * Modified for Dokuwiki + * @author Christopher Smith <chris@jalakai.co.uk> + */ + +class zip +{ + + var $datasec, $ctrl_dir = array(); + var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; + var $old_offset = 0; var $dirs = Array("."); + + function get_List($zip_name) + { + $zip = @fopen($zip_name, 'rb'); + if(!$zip) return(0); + $centd = $this->ReadCentralDir($zip,$zip_name); + + @rewind($zip); + @fseek($zip, $centd['offset']); + + for ($i=0; $i<$centd['entries']; $i++) + { + $header = $this->ReadCentralFileHeaders($zip); + $header['index'] = $i;$info['filename'] = $header['filename']; + $info['stored_filename'] = $header['stored_filename']; + $info['size'] = $header['size'];$info['compressed_size']=$header['compressed_size']; + $info['crc'] = strtoupper(dechex( $header['crc'] )); + $info['mtime'] = $header['mtime']; $info['comment'] = $header['comment']; + $info['folder'] = ($header['external']==0x41FF0010||$header['external']==16)?1:0; + $info['index'] = $header['index'];$info['status'] = $header['status']; + $ret[]=$info; unset($header); + } + return $ret; + } + + function Add($files,$compact) + { + if(!is_array($files[0])) $files=Array($files); + + for($i=0;$files[$i];$i++){ + $fn = $files[$i]; + if(!in_Array(dirname($fn[0]),$this->dirs)) + $this->add_Dir(dirname($fn[0])); + if(basename($fn[0])) + $ret[basename($fn[0])]=$this->add_File($fn[1],$fn[0],$compact); + } + return $ret; + } + + function get_file() + { + $data = implode('', $this -> datasec); + $ctrldir = implode('', $this -> ctrl_dir); + + return $data . $ctrldir . $this -> eof_ctrl_dir . + pack('v', sizeof($this -> ctrl_dir)).pack('v', sizeof($this -> ctrl_dir)). + pack('V', strlen($ctrldir)) . pack('V', strlen($data)) . "\x00\x00"; + } + + function add_dir($name) + { + $name = str_replace("\\", "/", $name); + $fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + + $fr .= pack("V",0).pack("V",0).pack("V",0).pack("v", strlen($name) ); + $fr .= pack("v", 0 ).$name.pack("V", 0).pack("V", 0).pack("V", 0); + $this -> datasec[] = $fr; + + $new_offset = strlen(implode("", $this->datasec)); + + $cdrec = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + $cdrec .= pack("V",0).pack("V",0).pack("V",0).pack("v", strlen($name) ); + $cdrec .= pack("v", 0 ).pack("v", 0 ).pack("v", 0 ).pack("v", 0 ); + $ext = "\xff\xff\xff\xff"; + $cdrec .= pack("V", 16 ).pack("V", $this -> old_offset ).$name; + + $this -> ctrl_dir[] = $cdrec; + $this -> old_offset = $new_offset; + $this -> dirs[] = $name; + } + + function add_File($data, $name, $compact = 1) + { + $name = str_replace('\\', '/', $name); + $dtime = dechex($this->DosTime()); + + $hexdtime = '\x' . $dtime[6] . $dtime[7].'\x'.$dtime[4] . $dtime[5] + . '\x' . $dtime[2] . $dtime[3].'\x'.$dtime[0].$dtime[1]; + eval('$hexdtime = "' . $hexdtime . '";'); + + if($compact) + $fr = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00".$hexdtime; + else $fr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00".$hexdtime; + $unc_len = strlen($data); $crc = crc32($data); + + if($compact){ + $zdata = gzcompress($data); $c_len = strlen($zdata); + $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); + }else{ + $zdata = $data; + } + $c_len=strlen($zdata); + $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len); + $fr .= pack('v', strlen($name)).pack('v', 0).$name.$zdata; + + $fr .= pack('V', $crc).pack('V', $c_len).pack('V', $unc_len); + + $this -> datasec[] = $fr; + $new_offset = strlen(implode('', $this->datasec)); + if($compact) + $cdrec = "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00"; + else $cdrec = "\x50\x4b\x01\x02\x14\x00\x0a\x00\x00\x00\x00\x00"; + $cdrec .= $hexdtime.pack('V', $crc).pack('V', $c_len).pack('V', $unc_len); + $cdrec .= pack('v', strlen($name) ).pack('v', 0 ).pack('v', 0 ); + $cdrec .= pack('v', 0 ).pack('v', 0 ).pack('V', 32 ); + $cdrec .= pack('V', $this -> old_offset ); + + $this -> old_offset = $new_offset; + $cdrec .= $name; + $this -> ctrl_dir[] = $cdrec; + return true; + } + + function DosTime() { + $timearray = getdate(); + if ($timearray['year'] < 1980) { + $timearray['year'] = 1980; $timearray['mon'] = 1; + $timearray['mday'] = 1; $timearray['hours'] = 0; + $timearray['minutes'] = 0; $timearray['seconds'] = 0; + } + return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | + ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); + } + + function Extract ( $zn, $to, $index = Array(-1) ) + { + if(!@is_dir($to)) @mkdir($to,0777); + $ok = 0; $zip = @fopen($zn,'rb'); + if(!$zip) return(-1); + $cdir = $this->ReadCentralDir($zip,$zn); + $pos_entry = $cdir['offset']; + + if(!is_array($index)){ $index = array($index); } + for($i=0; $index[$i];$i++){ + if(intval($index[$i])!=$index[$i]||$index[$i]>$cdir['entries']) + return(-1); + } + + for ($i=0; $i<$cdir['entries']; $i++) + { + @fseek($zip, $pos_entry); + $header = $this->ReadCentralFileHeaders($zip); + $header['index'] = $i; $pos_entry = ftell($zip); + @rewind($zip); fseek($zip, $header['offset']); + if(in_array("-1",$index)||in_array($i,$index)) + $stat[$header['filename']]=$this->ExtractFile($header, $to, $zip); + + } + fclose($zip); + return $stat; + } + + function ReadFileHeader($zip) + { + $binary_data = fread($zip, 30); + $data = unpack('vchk/vid/vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $binary_data); + + $header['filename'] = fread($zip, $data['filename_len']); + if ($data['extra_len'] != 0) { + $header['extra'] = fread($zip, $data['extra_len']); + } else { $header['extra'] = ''; } + + $header['compression'] = $data['compression'];$header['size'] = $data['size']; + $header['compressed_size'] = $data['compressed_size']; + $header['crc'] = $data['crc']; $header['flag'] = $data['flag']; + $header['mdate'] = $data['mdate'];$header['mtime'] = $data['mtime']; + + if ($header['mdate'] && $header['mtime']){ + $hour=($header['mtime']&0xF800)>>11;$minute=($header['mtime']&0x07E0)>>5; + $seconde=($header['mtime']&0x001F)*2;$year=(($header['mdate']&0xFE00)>>9)+1980; + $month=($header['mdate']&0x01E0)>>5;$day=$header['mdate']&0x001F; + $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year); + }else{$header['mtime'] = time();} + + $header['stored_filename'] = $header['filename']; + $header['status'] = "ok"; + return $header; + } + + function ReadCentralFileHeaders($zip){ + $binary_data = fread($zip, 46); + $header = unpack('vchkid/vid/vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $binary_data); + + if ($header['filename_len'] != 0) + $header['filename'] = fread($zip,$header['filename_len']); + else $header['filename'] = ''; + + if ($header['extra_len'] != 0) + $header['extra'] = fread($zip, $header['extra_len']); + else $header['extra'] = ''; + + if ($header['comment_len'] != 0) + $header['comment'] = fread($zip, $header['comment_len']); + else $header['comment'] = ''; + + if ($header['mdate'] && $header['mtime']) + { + $hour = ($header['mtime'] & 0xF800) >> 11; + $minute = ($header['mtime'] & 0x07E0) >> 5; + $seconde = ($header['mtime'] & 0x001F)*2; + $year = (($header['mdate'] & 0xFE00) >> 9) + 1980; + $month = ($header['mdate'] & 0x01E0) >> 5; + $day = $header['mdate'] & 0x001F; + $header['mtime'] = mktime($hour, $minute, $seconde, $month, $day, $year); + } else { + $header['mtime'] = time(); + } + $header['stored_filename'] = $header['filename']; + $header['status'] = 'ok'; + if (substr($header['filename'], -1) == '/') + $header['external'] = 0x41FF0010; + return $header; + } + + function ReadCentralDir($zip,$zip_name) + { + $size = filesize($zip_name); + if ($size < 277) $maximum_size = $size; + else $maximum_size=277; + + @fseek($zip, $size-$maximum_size); + $pos = ftell($zip); $bytes = 0x00000000; + + while ($pos < $size) + { + $byte = @fread($zip, 1); $bytes=($bytes << 8) | Ord($byte); + if ($bytes == 0x504b0506){ $pos++; break; } $pos++; + } + + $data=unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', + fread($zip, 18)); + + if ($data['comment_size'] != 0) + $centd['comment'] = fread($zip, $data['comment_size']); + else $centd['comment'] = ''; $centd['entries'] = $data['entries']; + $centd['disk_entries'] = $data['disk_entries']; + $centd['offset'] = $data['offset'];$centd['disk_start'] = $data['disk_start']; + $centd['size'] = $data['size']; $centd['disk'] = $data['disk']; + return $centd; + } + + function ExtractFile($header,$to,$zip) + { + $header = $this->readfileheader($zip); + + if(substr($to,-1)!="/") $to.="/"; + if(substr($header['filename'],-1)=="/") + { +// @mkdir($to.$header['filename']); --CS + $this->_mkdir($to.$header['filename']); //-- CS + return +2; + } + +// $pth = explode("/",dirname($header['filename'])); +// for($i=0,$tmp="";isset($pth[$i]);$i++){ +// if(!$pth[$i]) continue; +// if(!is_dir($to.$tmp.$pth[$i])) @mkdir($to.$pth[$i],0777); +// $tmp.=$pth[$i]."/"; +// } + if (!$this->_mkdir($to.dirname($header['filename']))) return (-1); //--CS + + if (!($header['external']==0x41FF0010)&&!($header['external']==16)) + { + if ($header['compression']==0) + { + $fp = @fopen($to.$header['filename'], 'wb'); + if(!$fp) return(-1); + $size = $header['compressed_size']; + + while ($size != 0) + { + $read_size = ($size < 2048 ? $size : 2048); + $buffer = fread($zip, $read_size); + $binary_data = pack('a'.$read_size, $buffer); + @fwrite($fp, $binary_data, $read_size); + $size -= $read_size; + } + fclose($fp); + touch($to.$header['filename'], $header['mtime']); + + }else{ + if (!is_dir(dirname($to.$header['filename']))) $this->_mkdir(dirname($to.$header['filename'])); //-CS + $fp = fopen($to.$header['filename'].'.gz','wb'); + if(!$fp) return(-1); + $binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($header['compression']), + Chr(0x00), time(), Chr(0x00), Chr(3)); + + fwrite($fp, $binary_data, 10); + $size = $header['compressed_size']; + + while ($size != 0) + { + $read_size = ($size < 1024 ? $size : 1024); + $buffer = fread($zip, $read_size); + $binary_data = pack('a'.$read_size, $buffer); + @fwrite($fp, $binary_data, $read_size); + $size -= $read_size; + } + + $binary_data = pack('VV', $header['crc'], $header['size']); + fwrite($fp, $binary_data,8); fclose($fp); + + $gzp = @gzopen($to.$header['filename'].'.gz','rb'); + if(!$gzp){ + @gzclose($gzp); @unlink($to.$header['filename']); + die("Archive is compressed whereas ZLIB is not enabled."); + } + $fp = @fopen($to.$header['filename'],'wb'); + if(!$fp) return(-1); + $size = $header['size']; + + while ($size != 0) + { + $read_size = ($size < 2048 ? $size : 2048); + $buffer = gzread($gzp, $read_size); + $binary_data = pack('a'.$read_size, $buffer); + @fwrite($fp, $binary_data, $read_size); + $size -= $read_size; + } + fclose($fp); gzclose($gzp); + + touch($to.$header['filename'], $header['mtime']); + @unlink($to.$header['filename'].'.gz'); + + }} + return true; + } + + //--CS start + // centralize mkdir calls and use dokuwiki io functions + function _mkdir($d) { + return ap_mkdir($d); + } + //--CS end +} + diff --git a/lib/plugins/plugin/lang/en/admin_plugin.txt b/lib/plugins/plugin/lang/en/admin_plugin.txt new file mode 100644 index 000000000..45a93ca45 --- /dev/null +++ b/lib/plugins/plugin/lang/en/admin_plugin.txt @@ -0,0 +1,7 @@ +====== Plugin Management ====== + +On this page you can manage everything to do with Dokuwiki [[doku>wiki:plugins|plugins]]. +To be able to download and install a plugin your plugin folder must +be writeable by the webserver. + + diff --git a/lib/plugins/plugin/lang/en/lang.php b/lib/plugins/plugin/lang/en/lang.php new file mode 100644 index 000000000..fb9934688 --- /dev/null +++ b/lib/plugins/plugin/lang/en/lang.php @@ -0,0 +1,58 @@ +<?php +/** + * english language file + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Christopher Smith <chris@jalakai.co.uk> + */ + +// settings must be present and set appropriately for the language +$lang['encoding'] = 'utf-8'; +$lang['direction'] = 'ltr'; + +// for admin plugins, the menu prompt to be displayed in the admin menu +// if set here, the plugin doesn't need to override the getMenuText() method +$lang['menu'] = 'Manage Plugins...'; + +// custom language strings for the plugin +$lang['refresh'] = "Refresh list of installed plugins"; +$lang['refresh_x'] = "Use this option if you have altered any of your plugins manually"; +$lang['download'] = "Download and install a new plugin"; +$lang['manage'] = "Installed Plugins"; +$lang['btn_info'] = 'info'; +$lang['btn_update'] = 'update'; +$lang['btn_delete'] = 'delete'; +$lang['btn_settings'] = 'settings'; +$lang['btn_refresh'] = 'Refresh'; +$lang['btn_download'] = 'Download'; +$lang['url'] = 'URL'; +//$lang[''] = ''; + +$lang['installed'] = 'Installed:'; +$lang['lastupdate'] = 'Last updated:'; +$lang['source'] = 'Source:'; +$lang['unknown'] = 'unknown'; + +// ..ing = header message +// ..ed = success message + +$lang['refreshing'] = 'Refreshing ...'; +$lang['refreshed'] = 'Plugin refresh completed.'; + +$lang['updating'] = 'Updating ...'; +$lang['updated'] = 'Plugin %s updated successfully'; + +$lang['downloading'] = 'Downloading ...'; +$lang['downloaded'] = 'Plugin %s installed successfully'; +$lang['downloads'] = 'The following plugins have been installed successfully:'; +$lang['download_none'] = 'No plugins found, or there has been an unknown problem during downloading and installing.'; + +// error messages +$lang['error_download'] = 'Unable to download the plugin file: %s'; +$lang['error_write'] = 'Unable to write create aggregate file %s'; +$lang['error_badurl'] = 'Suspect bad url - unable to determine file name from the url'; +$lang['error_dircreate'] = 'Unable to create temporary folder to receive download'; +//$lang['error_'] = ''; + + +//Setup VIM: ex: et ts=2 enc=utf-8 : diff --git a/lib/plugins/plugin/style.css b/lib/plugins/plugin/style.css new file mode 100644 index 000000000..9bc5bc38e --- /dev/null +++ b/lib/plugins/plugin/style.css @@ -0,0 +1,49 @@ +/* + * admin plugin extension - style additions + * + * @author Christopher Smith chris@jalakai.co.uk + * @link http://wiki.jalakai.co.uk/dokuwiki/doku.php/tutorials/adminplugin + */ + +/* overall containing div */ +#plugin_manager {} + +#plugin_manager h2 {margin-left: 0;} +#plugin_manager form { display: block; margin: 0; padding: 0;} +#plugin_manager form:after {content:'.'; display: block; clear: both; visibility: hidden; height: 0;} +#plugin_manager legend {display: none;} +#plugin_manager .legend {color: black; display: block; margin: 0; padding: 0; font-size: 1em; line-height: 1.4em; font-weight: normal; text-align: left;} +#plugin_manager fieldset {border: 0; width: auto;} +#plugin_manager .button {float: right; margin: 0 0.3em 2px 0;} +#plugin_manager p, #plugin_manager label {text-align: left;} +#plugin_manager .hidden {display: none;} +#plugin_manager .new {background: #dee7ec;} +#plugin_manager input[disabled] {color: #ccc; border-color: #ccc;} /* IE won't understand but doesn't require it */ + +#plugin_manager .pm_menu, #plugin_manager .pm_info {margin-left: 0; text-align: left; overflow: hidden;} +#plugin_manager .pm_menu {float: left; width: 48%;} +#plugin_manager .pm_info {float: right; width: 50%;} + +#plugin_manager .common {border-bottom: 1px solid #8cacbb;} +#plugin_manager .common form { border: 1px solid #8cacbb; border-bottom: 0;} +#plugin_manager .common fieldset { margin: 0; padding: 0;} +#plugin_manager .common .legend { background: #dee7ec; margin-bottom: 0.3em; padding-left: 0.5em;} +#plugin_manager .common label { padding: 0 0 0.5em 0.5em; } +#plugin_manager .common input {width: auto; margin: 0 1em;} +#plugin_manager .common .button { width: 6em; } +#plugin_manager .common p {border-bottom: 1px solid #8cacbb; padding: 0 0 0.5em 0.5em; margin-bottom: 0;} + +#plugin_manager .plugins {border-bottom: 1px solid #8cacbb;} +#plugin_manager .plugins form { border: 1px solid #8cacbb; border-bottom: 0;} +#plugin_manager .plugins fieldset { } +#plugin_manager .plugins .legend { float: left; } +#plugin_manager .plugins .button { } + +#plugin_manager .pm_info h3 {margin-left: 0; } +#plugin_manager .pm_info dl { overflow: hidden; margin: 1em 0; padding: 0;} +#plugin_manager .pm_info dt { width: 6em; float: left; clear: left; margin:0; padding: 0;} +#plugin_manager .pm_info dd { margin:0 0 0 7em; padding: 0;} + + +/* end admin plugin styles */ + -- GitLab