xref: /plugin/upgrade/admin.php (revision 22c39b330eec12eab7368b3e09e1fb8b29c3361b)
134aae6dbSAndreas Gohr<?php
234aae6dbSAndreas Gohr/**
3d391262fSAndreas Gohr * DokuWiki Plugin upgrade (Admin Component)
434aae6dbSAndreas Gohr *
534aae6dbSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
634aae6dbSAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
734aae6dbSAndreas Gohr */
834aae6dbSAndreas Gohr
934aae6dbSAndreas Gohr// must be run within Dokuwiki
1034aae6dbSAndreas Gohrif (!defined('DOKU_INC')) die();
1134aae6dbSAndreas Gohr
1234aae6dbSAndreas Gohrif (!defined('DOKU_LF')) define('DOKU_LF', "\n");
1334aae6dbSAndreas Gohrif (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
1434aae6dbSAndreas Gohrif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
1534aae6dbSAndreas Gohr
1634aae6dbSAndreas Gohrrequire_once DOKU_PLUGIN.'admin.php';
17d391262fSAndreas Gohrrequire_once DOKU_PLUGIN.'upgrade/VerboseTarLib.class.php';
1834aae6dbSAndreas Gohr
19ff284f1fSAndreas Gohrclass admin_plugin_upgrade extends DokuWiki_Admin_Plugin {
2034aae6dbSAndreas Gohr    private $tgzurl;
2134aae6dbSAndreas Gohr    private $tgzfile;
2234aae6dbSAndreas Gohr    private $tgzdir;
2334aae6dbSAndreas Gohr
2434aae6dbSAndreas Gohr    public function __construct(){
2534aae6dbSAndreas Gohr        global $conf;
2634aae6dbSAndreas Gohr
2734aae6dbSAndreas Gohr        $branch = 'stable';
2834aae6dbSAndreas Gohr
2934aae6dbSAndreas Gohr        $this->tgzurl  = 'http://github.com/splitbrain/dokuwiki/tarball/'.$branch;
30d391262fSAndreas Gohr        $this->tgzfile = $conf['tmpdir'].'/dokuwiki-upgrade.tgz';
31d391262fSAndreas Gohr        $this->tgzdir  = $conf['tmpdir'].'/dokuwiki-upgrade/';
3234aae6dbSAndreas Gohr    }
3334aae6dbSAndreas Gohr
3441163f44SAndreas Gohr    public function getMenuSort() { return 555; }
3534aae6dbSAndreas Gohr
3641163f44SAndreas Gohr    public function handle() {
3741163f44SAndreas Gohr        if($_REQUEST['step'] && !checkSecurityToken()){
3841163f44SAndreas Gohr            unset($_REQUEST['step']);
3941163f44SAndreas Gohr        }
4034aae6dbSAndreas Gohr    }
4134aae6dbSAndreas Gohr
4234aae6dbSAndreas Gohr    public function html() {
4341163f44SAndreas Gohr        global $ID;
4434aae6dbSAndreas Gohr        $abrt = false;
4534aae6dbSAndreas Gohr        $next = false;
4634aae6dbSAndreas Gohr
4734aae6dbSAndreas Gohr        echo '<h1>' . $this->getLang('menu') . '</h1>';
4834aae6dbSAndreas Gohr
49d391262fSAndreas Gohr        $this->_say('<div id="plugin__upgrade">');
5075e9d164SAndreas Gohr        // enable auto scroll
5175e9d164SAndreas Gohr        ?>
5275e9d164SAndreas Gohr        <script language="javascript" type="text/javascript">
53d391262fSAndreas Gohr            var plugin_upgrade = window.setInterval(function(){
54d391262fSAndreas Gohr                var obj = $('plugin__upgrade');
5575e9d164SAndreas Gohr                if(obj) obj.scrollTop = obj.scrollHeight;
5675e9d164SAndreas Gohr            },25);
5775e9d164SAndreas Gohr        </script>
5875e9d164SAndreas Gohr        <?php
5975e9d164SAndreas Gohr
6075e9d164SAndreas Gohr        // handle current step
61*22c39b33SAndreas Gohr        $this->_stepit($abrt, $next);
6234aae6dbSAndreas Gohr
6375e9d164SAndreas Gohr        // disable auto scroll
6475e9d164SAndreas Gohr        ?>
6575e9d164SAndreas Gohr        <script language="javascript" type="text/javascript">
6675e9d164SAndreas Gohr            window.setTimeout(function(){
67d391262fSAndreas Gohr                window.clearInterval(plugin_upgrade);
6875e9d164SAndreas Gohr            },50);
6975e9d164SAndreas Gohr        </script>
7075e9d164SAndreas Gohr        <?php
7175e9d164SAndreas Gohr        $this->_say('</div>');
7275e9d164SAndreas Gohr
73d391262fSAndreas Gohr        echo '<form action="" method="get" id="plugin__upgrade_form">';
7434aae6dbSAndreas Gohr        echo '<input type="hidden" name="do" value="admin" />';
75d391262fSAndreas Gohr        echo '<input type="hidden" name="page" value="upgrade" />';
7641163f44SAndreas Gohr        echo '<input type="hidden" name="sectok" value="'.getSecurityToken().'" />';
7775e9d164SAndreas Gohr        if($next) echo '<input type="submit" name="step['.$next.']" value="Continue" class="button continue" />';
7875e9d164SAndreas Gohr        if($abrt) echo '<input type="submit" name="step[cancel]" value="Abort" class="button abort" />';
7934aae6dbSAndreas Gohr        echo '</form>';
8034aae6dbSAndreas Gohr    }
8134aae6dbSAndreas Gohr
8234aae6dbSAndreas Gohr    private function _stepit(&$abrt, &$next){
834bed5591SAndreas Gohr        global $conf;
844bed5591SAndreas Gohr        if($conf['safemodehack']){
854bed5591SAndreas Gohr            $abrt = false;
864bed5591SAndreas Gohr            $next = false;
874bed5591SAndreas Gohr            echo $this->locale_xhtml('safemode');
884bed5591SAndreas Gohr        }
894bed5591SAndreas Gohr
9034aae6dbSAndreas Gohr        if(isset($_REQUEST['step']) && is_array($_REQUEST['step'])){
9134aae6dbSAndreas Gohr            $step = array_shift(array_keys($_REQUEST['step']));
9234aae6dbSAndreas Gohr        }else{
9334aae6dbSAndreas Gohr            $step = '';
9434aae6dbSAndreas Gohr        }
9534aae6dbSAndreas Gohr
9634aae6dbSAndreas Gohr        if($step == 'cancel'){
9734aae6dbSAndreas Gohr            # cleanup
9834aae6dbSAndreas Gohr            @unlink($this->tgzfile);
996b2d1b30SAndreas Gohr            $this->_rdel($this->tgzdir);
10034aae6dbSAndreas Gohr            $step = '';
10134aae6dbSAndreas Gohr        }
10234aae6dbSAndreas Gohr
10334aae6dbSAndreas Gohr        if($step){
10434aae6dbSAndreas Gohr            $abrt = true;
10534aae6dbSAndreas Gohr            $next = false;
10634aae6dbSAndreas Gohr            if(!file_exists($this->tgzfile)){
10734aae6dbSAndreas Gohr                if($this->_step_download()) $next = 'unpack';
10834aae6dbSAndreas Gohr            }elseif(!is_dir($this->tgzdir)){
10934aae6dbSAndreas Gohr                if($this->_step_unpack()) $next = 'check';
11034aae6dbSAndreas Gohr            }elseif($step != 'upgrade'){
1113c38de15SAndreas Gohr                if($this->_step_check()) $next = 'upgrade';
11234aae6dbSAndreas Gohr            }elseif($step == 'upgrade'){
1133c38de15SAndreas Gohr                if($this->_step_copy()) $next = 'cancel';
11434aae6dbSAndreas Gohr            }else{
11575e9d164SAndreas Gohr                echo 'uhm. what happened? where am I? This should not happen';
11634aae6dbSAndreas Gohr            }
11734aae6dbSAndreas Gohr        }else{
11834aae6dbSAndreas Gohr            # first time run, show intro
11934aae6dbSAndreas Gohr            echo $this->locale_xhtml('step0');
12034aae6dbSAndreas Gohr            $abrt = false;
12134aae6dbSAndreas Gohr            $next = 'download';
12234aae6dbSAndreas Gohr        }
12334aae6dbSAndreas Gohr    }
12434aae6dbSAndreas Gohr
12534aae6dbSAndreas Gohr    private function _say(){
12634aae6dbSAndreas Gohr        $args = func_get_args();
12734aae6dbSAndreas Gohr        echo vsprintf(array_shift($args)."<br />\n",$args);
12834aae6dbSAndreas Gohr        flush();
12934aae6dbSAndreas Gohr        ob_flush();
13034aae6dbSAndreas Gohr    }
13134aae6dbSAndreas Gohr
1326b2d1b30SAndreas Gohr    /**
1336b2d1b30SAndreas Gohr     * Recursive delete
1346b2d1b30SAndreas Gohr     *
1359285faa5SAndreas Gohr     * @author Jon Hassall
1369285faa5SAndreas Gohr     * @link http://de.php.net/manual/en/function.unlink.php#87045
1376b2d1b30SAndreas Gohr     */
1389285faa5SAndreas Gohr    private function _rdel($dir) {
1399285faa5SAndreas Gohr        if(!$dh = @opendir($dir)) {
1409285faa5SAndreas Gohr            return;
1419285faa5SAndreas Gohr        }
1429285faa5SAndreas Gohr        while (false !== ($obj = readdir($dh))) {
1439285faa5SAndreas Gohr            if($obj == '.' || $obj == '..') continue;
1449285faa5SAndreas Gohr
1459285faa5SAndreas Gohr            if (!@unlink($dir . '/' . $obj)) {
1469285faa5SAndreas Gohr                $this->_rdel($dir.'/'.$obj);
1479285faa5SAndreas Gohr            }
1489285faa5SAndreas Gohr        }
1499285faa5SAndreas Gohr        closedir($dh);
150e32047e3SAndreas Gohr        return @rmdir($dir);
1516b2d1b30SAndreas Gohr    }
1526b2d1b30SAndreas Gohr
15334aae6dbSAndreas Gohr    private function _step_download(){
1543c38de15SAndreas Gohr        $this->_say($this->getLang('dl_from'),$this->tgzurl);
15534aae6dbSAndreas Gohr
15634aae6dbSAndreas Gohr        @set_time_limit(120);
15734aae6dbSAndreas Gohr        @ignore_user_abort();
15834aae6dbSAndreas Gohr
15934aae6dbSAndreas Gohr        $http = new DokuHTTPClient();
16034aae6dbSAndreas Gohr        $http->timeout = 120;
16134aae6dbSAndreas Gohr        $data = $http->get($this->tgzurl);
16234aae6dbSAndreas Gohr
16334aae6dbSAndreas Gohr        if(!$data){
16434aae6dbSAndreas Gohr            $this->_say($http->error);
1653c38de15SAndreas Gohr            $this->_say($this->getLang('dl_fail'));
16634aae6dbSAndreas Gohr            return false;
16734aae6dbSAndreas Gohr        }
16834aae6dbSAndreas Gohr
16934aae6dbSAndreas Gohr        if(!io_saveFile($this->tgzfile,$data)){
1703c38de15SAndreas Gohr            $this->_say($this->getLang('dl_fail'));
17134aae6dbSAndreas Gohr            return false;
17234aae6dbSAndreas Gohr        }
17334aae6dbSAndreas Gohr
174a7b56078SAndreas Gohr        $this->_say($this->getLang('dl_done'),filesize_h(strlen($data)));
1753c38de15SAndreas Gohr
17634aae6dbSAndreas Gohr        return true;
17734aae6dbSAndreas Gohr    }
17834aae6dbSAndreas Gohr
17934aae6dbSAndreas Gohr    private function _step_unpack(){
18034aae6dbSAndreas Gohr        global $conf;
1813c38de15SAndreas Gohr        $this->_say('<b>'.$this->getLang('pk_extract').'</b>');
18234aae6dbSAndreas Gohr
18334aae6dbSAndreas Gohr        @set_time_limit(120);
18434aae6dbSAndreas Gohr        @ignore_user_abort();
18534aae6dbSAndreas Gohr
18634aae6dbSAndreas Gohr        $tar = new VerboseTarLib($this->tgzfile);
18734aae6dbSAndreas Gohr        if($tar->_initerror < 0){
18834aae6dbSAndreas Gohr            $this->_say($tar->TarErrorStr($tar->_initerror));
1893c38de15SAndreas Gohr            $this->_say($this->getLang('pk_fail'));
19034aae6dbSAndreas Gohr            return false;
19134aae6dbSAndreas Gohr        }
19234aae6dbSAndreas Gohr
19334aae6dbSAndreas Gohr        $ok = $tar->Extract(VerboseTarLib::FULL_ARCHIVE,$this->tgzdir,1,$conf['fmode'],'/^(_cs|_test|\.gitignore)/');
19434aae6dbSAndreas Gohr        if($ok < 1){
19534aae6dbSAndreas Gohr            $this->_say($tar->TarErrorStr($ok));
1963c38de15SAndreas Gohr            $this->_say($this->getLang('pk_fail'));
19734aae6dbSAndreas Gohr            return false;
19834aae6dbSAndreas Gohr        }
19934aae6dbSAndreas Gohr
2003c38de15SAndreas Gohr        $this->_say($this->getLang('pk_done'));
201738c0102SAndreas Gohr
2023c38de15SAndreas Gohr        $this->_say($this->getLang('pk_version'),
203738c0102SAndreas Gohr                    hsc(file_get_contents($this->tgzdir.'/VERSION')),
204738c0102SAndreas Gohr                    getVersion());
20534aae6dbSAndreas Gohr        return true;
20634aae6dbSAndreas Gohr    }
20734aae6dbSAndreas Gohr
2083c38de15SAndreas Gohr    private function _step_check(){
2093c38de15SAndreas Gohr        $this->_say($this->getLang('ck_start'));
2103c38de15SAndreas Gohr        $ok = $this->_traverse('',true);
21134aae6dbSAndreas Gohr        if($ok){
2123c38de15SAndreas Gohr            $this->_say('<b>'.$this->getLang('ck_done').'</b>');
21334aae6dbSAndreas Gohr        }else{
2143c38de15SAndreas Gohr            $this->_say('<b>'.$this->getLang('ck_fail').'</b>');
21534aae6dbSAndreas Gohr        }
2163c38de15SAndreas Gohr        return $ok;
2173c38de15SAndreas Gohr    }
21834aae6dbSAndreas Gohr
2193c38de15SAndreas Gohr    private function _step_copy(){
2203c38de15SAndreas Gohr        $this->_say($this->getLang('cp_start'));
2213c38de15SAndreas Gohr        $ok = $this->_traverse('',false);
2223c38de15SAndreas Gohr        if($ok){
2233c38de15SAndreas Gohr            $this->_say('<b>'.$this->getLang('cp_done').'</b>');
22463712694SAndreas Gohr            $this->_rmold();
225e32047e3SAndreas Gohr            $this->_say('<b>'.$this->getLang('finish').'</b>');
22634aae6dbSAndreas Gohr        }else{
2273c38de15SAndreas Gohr            $this->_say('<b>'.$this->getLang('cp_fail').'</b>');
22834aae6dbSAndreas Gohr        }
22934aae6dbSAndreas Gohr        return $ok;
23034aae6dbSAndreas Gohr    }
23134aae6dbSAndreas Gohr
23263712694SAndreas Gohr    private function _rmold(){
23363712694SAndreas Gohr        $list = file($this->tgzdir.'data/deleted.files');
23463712694SAndreas Gohr        foreach($list as $line){
23563712694SAndreas Gohr            $line = trim(preg_replace('/#.*$/','',$line));
23663712694SAndreas Gohr            if(!$line) continue;
23763712694SAndreas Gohr            $file = DOKU_INC.$line;
23863712694SAndreas Gohr            if(!file_exists($file)) continue;
23963712694SAndreas Gohr            if( (is_dir($file) && $this->_rdel($file)) ||
24063712694SAndreas Gohr                @unlink($file)){
241e32047e3SAndreas Gohr                $this->_say($this->getLang('rm_done'),hsc($line));
24263712694SAndreas Gohr            }else{
243e32047e3SAndreas Gohr                $this->_say($this->getLang('rm_fail'),hsc($line));
24463712694SAndreas Gohr            }
24563712694SAndreas Gohr        }
24663712694SAndreas Gohr    }
24763712694SAndreas Gohr
24834aae6dbSAndreas Gohr    private function _traverse($dir,$dryrun){
24934aae6dbSAndreas Gohr        $base = $this->tgzdir;
25034aae6dbSAndreas Gohr        $ok = true;
25134aae6dbSAndreas Gohr
25234aae6dbSAndreas Gohr        $dh = @opendir($base.'/'.$dir);
25334aae6dbSAndreas Gohr        if(!$dh) return;
25434aae6dbSAndreas Gohr        while(($file = readdir($dh)) !== false){
25534aae6dbSAndreas Gohr            if($file == '.' || $file == '..') continue;
25634aae6dbSAndreas Gohr            $from = "$base/$dir/$file";
25734aae6dbSAndreas Gohr            $to   = DOKU_INC."$dir/$file";
25834aae6dbSAndreas Gohr
25934aae6dbSAndreas Gohr            if(is_dir($from)){
26034aae6dbSAndreas Gohr                if($dryrun){
26134aae6dbSAndreas Gohr                    // just check for writability
26234aae6dbSAndreas Gohr                    if(!is_dir($to)){
26334aae6dbSAndreas Gohr                        if(is_dir(dirname($to)) && !is_writable(dirname($to))){
264a7b56078SAndreas Gohr                            $this->_say('<b>'.$this->getLang('tv_noperm').'</b>',hsc("$dir/$file"));
26534aae6dbSAndreas Gohr                            $ok = false;
26634aae6dbSAndreas Gohr                        }
26734aae6dbSAndreas Gohr                    }
26834aae6dbSAndreas Gohr                }
26934aae6dbSAndreas Gohr
27034aae6dbSAndreas Gohr                // recursion
27134aae6dbSAndreas Gohr                if(!$this->_traverse("$dir/$file",$dryrun)){
27234aae6dbSAndreas Gohr                    $ok = false;
27334aae6dbSAndreas Gohr                }
27434aae6dbSAndreas Gohr            }else{
275f2fa6d10SAndreas Gohr                $fmd5 = md5(@file_get_contents($from));
276f2fa6d10SAndreas Gohr                $tmd5 = md5(@file_get_contents($to));
2776deeb3b1SAndreas Gohr                if($fmd5 != $tmd5 || !file_exists($to)){
27834aae6dbSAndreas Gohr                    if($dryrun){
27934aae6dbSAndreas Gohr                        // just check for writability
28034aae6dbSAndreas Gohr                        if( (file_exists($to) && !is_writable($to)) ||
28134aae6dbSAndreas Gohr                            (!file_exists($to) && is_dir(dirname($to)) && !is_writable(dirname($to))) ){
28234aae6dbSAndreas Gohr
283a7b56078SAndreas Gohr                            $this->_say('<b>'.$this->getLang('tv_noperm').'</b>',hsc("$dir/$file"));
28434aae6dbSAndreas Gohr                            $ok = false;
28534aae6dbSAndreas Gohr                        }else{
2863c38de15SAndreas Gohr                            $this->_say($this->getLang('tv_upd'),hsc("$dir/$file"));
28734aae6dbSAndreas Gohr                        }
28834aae6dbSAndreas Gohr                    }else{
28934aae6dbSAndreas Gohr                        // check dir
29034aae6dbSAndreas Gohr                        if(io_mkdir_p(dirname($to))){
29134aae6dbSAndreas Gohr                            // copy
29234aae6dbSAndreas Gohr                            if(!copy($from,$to)){
2933c38de15SAndreas Gohr                                $this->_say('<b>'.$this->getLang('tv_nocopy').'</b>',hsc("$dir/$file"));
29434aae6dbSAndreas Gohr                                $ok = false;
29534aae6dbSAndreas Gohr                            }else{
2963c38de15SAndreas Gohr                                $this->_say($this->getLang('tv_done'),hsc("$dir/$file"));
29734aae6dbSAndreas Gohr                            }
29834aae6dbSAndreas Gohr                        }else{
2993c38de15SAndreas Gohr                            $this->_say('<b>'.$this->getLang('tv_nodir').'</b>',hsc("$dir"));
30034aae6dbSAndreas Gohr                            $ok = false;
30134aae6dbSAndreas Gohr                        }
30234aae6dbSAndreas Gohr                    }
30334aae6dbSAndreas Gohr                }
30434aae6dbSAndreas Gohr            }
30534aae6dbSAndreas Gohr        }
30634aae6dbSAndreas Gohr        closedir($dh);
30734aae6dbSAndreas Gohr        return $ok;
30834aae6dbSAndreas Gohr    }
30934aae6dbSAndreas Gohr}
31034aae6dbSAndreas Gohr
31134aae6dbSAndreas Gohr// vim:ts=4:sw=4:et:enc=utf-8:
312