xref: /plugin/upgrade/admin.php (revision 27c6a3e69b508309747ba2073c9a3da14622c842)
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 Gohrif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
1234aae6dbSAndreas Gohrrequire_once DOKU_PLUGIN.'admin.php';
13d391262fSAndreas Gohrrequire_once DOKU_PLUGIN.'upgrade/VerboseTarLib.class.php';
14e90f5147SAndreas Gohrrequire_once DOKU_PLUGIN.'upgrade/HTTPClient.php';
1534aae6dbSAndreas Gohr
1668cf950eSAndreas Gohruse dokuwiki\plugin\upgrade\DokuHTTPClient;
1768cf950eSAndreas Gohr
18ff284f1fSAndreas Gohrclass admin_plugin_upgrade extends DokuWiki_Admin_Plugin {
1934aae6dbSAndreas Gohr    private $tgzurl;
2034aae6dbSAndreas Gohr    private $tgzfile;
2134aae6dbSAndreas Gohr    private $tgzdir;
22f378cb7eSAndreas Gohr    private $tgzversion;
23f378cb7eSAndreas Gohr    private $pluginversion;
2434aae6dbSAndreas Gohr
254ed3677eSAndreas Gohr    protected $haderrors = false;
264ed3677eSAndreas Gohr
2734aae6dbSAndreas Gohr    public function __construct() {
2834aae6dbSAndreas Gohr        global $conf;
2934aae6dbSAndreas Gohr
3034aae6dbSAndreas Gohr        $branch = 'stable';
3134aae6dbSAndreas Gohr
32bd08ebd1SAndreas Gohr        $this->tgzurl        = "https://github.com/splitbrain/dokuwiki/archive/$branch.tar.gz";
33d391262fSAndreas Gohr        $this->tgzfile       = $conf['tmpdir'].'/dokuwiki-upgrade.tgz';
34d391262fSAndreas Gohr        $this->tgzdir        = $conf['tmpdir'].'/dokuwiki-upgrade/';
35f378cb7eSAndreas Gohr        $this->tgzversion    = "https://raw.githubusercontent.com/splitbrain/dokuwiki/$branch/VERSION";
36f378cb7eSAndreas Gohr        $this->pluginversion = "https://raw.githubusercontent.com/splitbrain/dokuwiki-plugin-upgrade/master/plugin.info.txt";
3734aae6dbSAndreas Gohr    }
3834aae6dbSAndreas Gohr
39bd08ebd1SAndreas Gohr    public function getMenuSort() {
40bd08ebd1SAndreas Gohr        return 555;
41bd08ebd1SAndreas Gohr    }
4234aae6dbSAndreas Gohr
4341163f44SAndreas Gohr    public function handle() {
4441163f44SAndreas Gohr        if($_REQUEST['step'] && !checkSecurityToken()) {
4541163f44SAndreas Gohr            unset($_REQUEST['step']);
4641163f44SAndreas Gohr        }
4734aae6dbSAndreas Gohr    }
4834aae6dbSAndreas Gohr
4934aae6dbSAndreas Gohr    public function html() {
5034aae6dbSAndreas Gohr        $abrt = false;
5134aae6dbSAndreas Gohr        $next = false;
5234aae6dbSAndreas Gohr
5334aae6dbSAndreas Gohr        echo '<h1>'.$this->getLang('menu').'</h1>';
5434aae6dbSAndreas Gohr
55fb522b50SAndreas Gohr        self::_say('<div id="plugin__upgrade">');
5675e9d164SAndreas Gohr        // enable auto scroll
5775e9d164SAndreas Gohr        ?>
5875e9d164SAndreas Gohr        <script language="javascript" type="text/javascript">
59d391262fSAndreas Gohr            var plugin_upgrade = window.setInterval(function () {
60d4376367SAndreas Gohr                var obj = document.getElementById('plugin__upgrade');
6175e9d164SAndreas Gohr                if (obj) obj.scrollTop = obj.scrollHeight;
6275e9d164SAndreas Gohr            }, 25);
6375e9d164SAndreas Gohr        </script>
6475e9d164SAndreas Gohr        <?php
6575e9d164SAndreas Gohr
6675e9d164SAndreas Gohr        // handle current step
6722c39b33SAndreas Gohr        $this->_stepit($abrt, $next);
6834aae6dbSAndreas Gohr
6975e9d164SAndreas Gohr        // disable auto scroll
7075e9d164SAndreas Gohr        ?>
7175e9d164SAndreas Gohr        <script language="javascript" type="text/javascript">
7275e9d164SAndreas Gohr            window.setTimeout(function () {
73d391262fSAndreas Gohr                window.clearInterval(plugin_upgrade);
7475e9d164SAndreas Gohr            }, 50);
7575e9d164SAndreas Gohr        </script>
7675e9d164SAndreas Gohr        <?php
77fb522b50SAndreas Gohr        self::_say('</div>');
7875e9d164SAndreas Gohr
794ed3677eSAndreas Gohr        $careful = '';
804ed3677eSAndreas Gohr        if($this->haderrors) {
814ed3677eSAndreas Gohr            echo '<div id="plugin__upgrade_careful">'.$this->getLang('careful').'</div>';
824ed3677eSAndreas Gohr            $careful = 'careful';
834ed3677eSAndreas Gohr        }
844ed3677eSAndreas Gohr
859f8d4355SAndreas Gohr        $action = script();
869f8d4355SAndreas Gohr        echo '<form action="' . $action . '" method="post" id="plugin__upgrade_form">';
8734aae6dbSAndreas Gohr        echo '<input type="hidden" name="do" value="admin" />';
88d391262fSAndreas Gohr        echo '<input type="hidden" name="page" value="upgrade" />';
8941163f44SAndreas Gohr        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
9038665ab9SAndreas Gohr        if($next) echo '<button type="submit" name="step[' . $next . ']" value="1" class="button continue '.$careful.'">' . $this->getLang('btn_continue') . ' ➡</button>';
919f8d4355SAndreas Gohr        if($abrt) echo '<button type="submit" name="step[cancel]" value="1" class="button abort">✖ ' . $this->getLang('btn_abort') . '</button>';
9234aae6dbSAndreas Gohr        echo '</form>';
93eb52c46aSAndreas Gohr
94eb52c46aSAndreas Gohr        $this->_progress($next);
95eb52c46aSAndreas Gohr    }
96eb52c46aSAndreas Gohr
97eb52c46aSAndreas Gohr    /**
98eb52c46aSAndreas Gohr     * Display a progress bar of all steps
99eb52c46aSAndreas Gohr     *
100eb52c46aSAndreas Gohr     * @param string $next the next step
101eb52c46aSAndreas Gohr     */
102eb52c46aSAndreas Gohr    private function _progress($next) {
103eb52c46aSAndreas Gohr        $steps  = array('version', 'download', 'unpack', 'check', 'upgrade');
104eb52c46aSAndreas Gohr        $active = true;
105eb52c46aSAndreas Gohr        $count = 0;
106eb52c46aSAndreas Gohr
107eb52c46aSAndreas Gohr        echo '<div id="plugin__upgrade_meter"><ol>';
108eb52c46aSAndreas Gohr        foreach($steps as $step) {
109eb52c46aSAndreas Gohr            $count++;
110eb52c46aSAndreas Gohr            if($step == $next) $active = false;
111eb52c46aSAndreas Gohr            if($active) {
112eb52c46aSAndreas Gohr                echo '<li class="active">';
113eb52c46aSAndreas Gohr                echo '<span class="step">✔</span>';
114eb52c46aSAndreas Gohr            } else {
115eb52c46aSAndreas Gohr                echo '<li>';
116eb52c46aSAndreas Gohr                echo '<span class="step">'.$count.'</span>';
117eb52c46aSAndreas Gohr            }
118eb52c46aSAndreas Gohr
119eb52c46aSAndreas Gohr            echo '<span class="stage">'.$this->getLang('step_'.$step).'</span>';
120eb52c46aSAndreas Gohr            echo '</li>';
121eb52c46aSAndreas Gohr        }
122eb52c46aSAndreas Gohr        echo '</ol></div>';
12334aae6dbSAndreas Gohr    }
12434aae6dbSAndreas Gohr
125bd08ebd1SAndreas Gohr    /**
126bd08ebd1SAndreas Gohr     * Decides the current step and executes it
127bd08ebd1SAndreas Gohr     *
128bd08ebd1SAndreas Gohr     * @param bool $abrt
129bd08ebd1SAndreas Gohr     * @param bool $next
130bd08ebd1SAndreas Gohr     */
13134aae6dbSAndreas Gohr    private function _stepit(&$abrt, &$next) {
1324bed5591SAndreas Gohr
13334aae6dbSAndreas Gohr        if(isset($_REQUEST['step']) && is_array($_REQUEST['step'])) {
13434aae6dbSAndreas Gohr            $step = array_shift(array_keys($_REQUEST['step']));
13534aae6dbSAndreas Gohr        } else {
13634aae6dbSAndreas Gohr            $step = '';
13734aae6dbSAndreas Gohr        }
13834aae6dbSAndreas Gohr
139fb8e77caSAndreas Gohr        if($step == 'cancel' || $step == 'done') {
14034aae6dbSAndreas Gohr            # cleanup
14134aae6dbSAndreas Gohr            @unlink($this->tgzfile);
1426b2d1b30SAndreas Gohr            $this->_rdel($this->tgzdir);
143fb8e77caSAndreas Gohr            if($step == 'cancel') $step = '';
14434aae6dbSAndreas Gohr        }
14534aae6dbSAndreas Gohr
14634aae6dbSAndreas Gohr        if($step) {
14734aae6dbSAndreas Gohr            $abrt = true;
14834aae6dbSAndreas Gohr            $next = false;
149f378cb7eSAndreas Gohr            if($step == 'version') {
150f378cb7eSAndreas Gohr                $this->_step_version();
151f378cb7eSAndreas Gohr                $next = 'download';
152fb8e77caSAndreas Gohr            } elseif ($step == 'done') {
153fb8e77caSAndreas Gohr                $this->_step_done();
154fb8e77caSAndreas Gohr                $next = '';
155fb8e77caSAndreas Gohr                $abrt = '';
156f378cb7eSAndreas Gohr            } elseif(!file_exists($this->tgzfile)) {
15734aae6dbSAndreas Gohr                if($this->_step_download()) $next = 'unpack';
15834aae6dbSAndreas Gohr            } elseif(!is_dir($this->tgzdir)) {
15934aae6dbSAndreas Gohr                if($this->_step_unpack()) $next = 'check';
16034aae6dbSAndreas Gohr            } elseif($step != 'upgrade') {
1613c38de15SAndreas Gohr                if($this->_step_check()) $next = 'upgrade';
16234aae6dbSAndreas Gohr            } elseif($step == 'upgrade') {
163fb8e77caSAndreas Gohr                if($this->_step_copy()) {
164fb8e77caSAndreas Gohr                    $next = 'done';
165fb8e77caSAndreas Gohr                    $abrt = '';
166fb8e77caSAndreas Gohr                }
16734aae6dbSAndreas Gohr            } else {
16875e9d164SAndreas Gohr                echo 'uhm. what happened? where am I? This should not happen';
16934aae6dbSAndreas Gohr            }
17034aae6dbSAndreas Gohr        } else {
17134aae6dbSAndreas Gohr            # first time run, show intro
17234aae6dbSAndreas Gohr            echo $this->locale_xhtml('step0');
17334aae6dbSAndreas Gohr            $abrt = false;
174f378cb7eSAndreas Gohr            $next = 'version';
17534aae6dbSAndreas Gohr        }
17634aae6dbSAndreas Gohr    }
17734aae6dbSAndreas Gohr
178bd08ebd1SAndreas Gohr    /**
179bd08ebd1SAndreas Gohr     * Output the given arguments using vsprintf and flush buffers
180bd08ebd1SAndreas Gohr     */
181fb522b50SAndreas Gohr    public static function _say() {
18234aae6dbSAndreas Gohr        $args = func_get_args();
1836e7afd39SAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/images/blank.gif" width="16" height="16" alt="" /> ';
1846e7afd39SAndreas Gohr        echo vsprintf(array_shift($args)."<br />\n", $args);
1856e7afd39SAndreas Gohr        flush();
1866e7afd39SAndreas Gohr        ob_flush();
1876e7afd39SAndreas Gohr    }
1886e7afd39SAndreas Gohr
1896e7afd39SAndreas Gohr    /**
1906e7afd39SAndreas Gohr     * Print a warning using the given arguments with vsprintf and flush buffers
1916e7afd39SAndreas Gohr     */
1924ed3677eSAndreas Gohr    public function _warn() {
1934ed3677eSAndreas Gohr        $this->haderrors = true;
1944ed3677eSAndreas Gohr
1956e7afd39SAndreas Gohr        $args = func_get_args();
1966e7afd39SAndreas Gohr        echo '<img src="'.DOKU_BASE.'lib/images/error.png" width="16" height="16" alt="!" /> ';
19734aae6dbSAndreas Gohr        echo vsprintf(array_shift($args)."<br />\n", $args);
19834aae6dbSAndreas Gohr        flush();
19934aae6dbSAndreas Gohr        ob_flush();
20034aae6dbSAndreas Gohr    }
20134aae6dbSAndreas Gohr
2026b2d1b30SAndreas Gohr    /**
2036b2d1b30SAndreas Gohr     * Recursive delete
2046b2d1b30SAndreas Gohr     *
2059285faa5SAndreas Gohr     * @author Jon Hassall
2069285faa5SAndreas Gohr     * @link   http://de.php.net/manual/en/function.unlink.php#87045
2076b2d1b30SAndreas Gohr     */
2089285faa5SAndreas Gohr    private function _rdel($dir) {
2099285faa5SAndreas Gohr        if(!$dh = @opendir($dir)) {
210bd08ebd1SAndreas Gohr            return false;
2119285faa5SAndreas Gohr        }
2129285faa5SAndreas Gohr        while(false !== ($obj = readdir($dh))) {
2139285faa5SAndreas Gohr            if($obj == '.' || $obj == '..') continue;
2149285faa5SAndreas Gohr
2159285faa5SAndreas Gohr            if(!@unlink($dir.'/'.$obj)) {
2169285faa5SAndreas Gohr                $this->_rdel($dir.'/'.$obj);
2179285faa5SAndreas Gohr            }
2189285faa5SAndreas Gohr        }
2199285faa5SAndreas Gohr        closedir($dh);
220e32047e3SAndreas Gohr        return @rmdir($dir);
2216b2d1b30SAndreas Gohr    }
2226b2d1b30SAndreas Gohr
223bd08ebd1SAndreas Gohr    /**
224f378cb7eSAndreas Gohr     * Check various versions
225f378cb7eSAndreas Gohr     *
226f378cb7eSAndreas Gohr     * @return bool
227f378cb7eSAndreas Gohr     */
228f378cb7eSAndreas Gohr    private function _step_version() {
229f378cb7eSAndreas Gohr        $ok = true;
230f378cb7eSAndreas Gohr
231c37aa5dcSAndreas Gohr        // we need SSL - only newer HTTPClients check that themselves
232c37aa5dcSAndreas Gohr        if(!in_array('ssl', stream_get_transports())) {
233c37aa5dcSAndreas Gohr            $this->_warn($this->getLang('vs_ssl'));
234c37aa5dcSAndreas Gohr            $ok = false;
235c37aa5dcSAndreas Gohr        }
236c37aa5dcSAndreas Gohr
237f378cb7eSAndreas Gohr        // get the available version
238f378cb7eSAndreas Gohr        $http       = new DokuHTTPClient();
239f378cb7eSAndreas Gohr        $tgzversion = $http->get($this->tgzversion);
240f378cb7eSAndreas Gohr        if(!$tgzversion) {
2416e7afd39SAndreas Gohr            $this->_warn($this->getLang('vs_tgzno').' '.hsc($http->error));
242f378cb7eSAndreas Gohr            $ok = false;
243f378cb7eSAndreas Gohr        }
2440ab52135SAndreas Gohr        $tgzversionnum = $this->dateFromVersion($tgzversion);
2450ab52135SAndreas Gohr        if($tgzversionnum === 0) {
2466e7afd39SAndreas Gohr            $this->_warn($this->getLang('vs_tgzno'));
247f378cb7eSAndreas Gohr            $ok            = false;
248f378cb7eSAndreas Gohr        } else {
249fb522b50SAndreas Gohr            self::_say($this->getLang('vs_tgz'), $tgzversion);
250f378cb7eSAndreas Gohr        }
251f378cb7eSAndreas Gohr
252f378cb7eSAndreas Gohr        // get the current version
253f378cb7eSAndreas Gohr        $version = getVersion();
2540ab52135SAndreas Gohr        $versionnum = $this->dateFromVersion($version);
255fb522b50SAndreas Gohr        self::_say($this->getLang('vs_local'), $version);
256f378cb7eSAndreas Gohr
257f378cb7eSAndreas Gohr        // compare versions
258f378cb7eSAndreas Gohr        if(!$versionnum) {
2596e7afd39SAndreas Gohr            $this->_warn($this->getLang('vs_localno'));
260f378cb7eSAndreas Gohr            $ok = false;
261f378cb7eSAndreas Gohr        } else if($tgzversionnum) {
262f378cb7eSAndreas Gohr            if($tgzversionnum < $versionnum) {
2636e7afd39SAndreas Gohr                $this->_warn($this->getLang('vs_newer'));
264f378cb7eSAndreas Gohr                $ok = false;
265*27c6a3e6SAndreas Gohr            } elseif($tgzversionnum == $versionnum && $tgzversion == $version) {
2666e7afd39SAndreas Gohr                $this->_warn($this->getLang('vs_same'));
267f378cb7eSAndreas Gohr                $ok = false;
268f378cb7eSAndreas Gohr            }
269f378cb7eSAndreas Gohr        }
270f378cb7eSAndreas Gohr
271f378cb7eSAndreas Gohr        // check plugin version
272f378cb7eSAndreas Gohr        $pluginversion = $http->get($this->pluginversion);
273f378cb7eSAndreas Gohr        if($pluginversion) {
274f378cb7eSAndreas Gohr            $plugininfo = linesToHash(explode("\n", $pluginversion));
275f378cb7eSAndreas Gohr            $myinfo     = $this->getInfo();
276f378cb7eSAndreas Gohr            if($plugininfo['date'] > $myinfo['date']) {
2777cb119ecSAndreas Gohr                $this->_warn($this->getLang('vs_plugin'), $plugininfo['date']);
278f378cb7eSAndreas Gohr                $ok = false;
279f378cb7eSAndreas Gohr            }
280f378cb7eSAndreas Gohr        }
281f378cb7eSAndreas Gohr
2829bbe795aSAndreas Gohr        // check if PHP is up to date
283cd074bc5SAndreas Gohr        $minphp = '5.6';
2849bbe795aSAndreas Gohr        if(version_compare(phpversion(), $minphp, '<')) {
285a92a10d4SAndreas Gohr            $this->_warn($this->getLang('vs_php'), $minphp, phpversion());
2869bbe795aSAndreas Gohr            $ok = false;
2879bbe795aSAndreas Gohr        }
2889bbe795aSAndreas Gohr
289f378cb7eSAndreas Gohr        return $ok;
290f378cb7eSAndreas Gohr    }
291f378cb7eSAndreas Gohr
292f378cb7eSAndreas Gohr    /**
293fb8e77caSAndreas Gohr     * Redirect to the start page
294fb8e77caSAndreas Gohr     */
295fb8e77caSAndreas Gohr    private function _step_done() {
2964877bd65SAndreas Gohr        if (function_exists('opcache_reset')) {
2974877bd65SAndreas Gohr            opcache_reset();
2984877bd65SAndreas Gohr        }
299fb8e77caSAndreas Gohr        echo $this->getLang('finish');
30061f0f3bfSAndreas Gohr        echo "<script type='text/javascript'>location.href='".DOKU_URL."';</script>";
301fb8e77caSAndreas Gohr    }
302fb8e77caSAndreas Gohr
303fb8e77caSAndreas Gohr    /**
304bd08ebd1SAndreas Gohr     * Download the tarball
305bd08ebd1SAndreas Gohr     *
306bd08ebd1SAndreas Gohr     * @return bool
307bd08ebd1SAndreas Gohr     */
30834aae6dbSAndreas Gohr    private function _step_download() {
309fb522b50SAndreas Gohr        self::_say($this->getLang('dl_from'), $this->tgzurl);
31034aae6dbSAndreas Gohr
31158774abaSFrancis GUDIN        @set_time_limit(300);
31234aae6dbSAndreas Gohr        @ignore_user_abort();
31334aae6dbSAndreas Gohr
31434aae6dbSAndreas Gohr        $http          = new DokuHTTPClient();
31558774abaSFrancis GUDIN        $http->timeout = 300;
31634aae6dbSAndreas Gohr        $data          = $http->get($this->tgzurl);
31734aae6dbSAndreas Gohr
31834aae6dbSAndreas Gohr        if(!$data) {
3196e7afd39SAndreas Gohr            $this->_warn($http->error);
3206e7afd39SAndreas Gohr            $this->_warn($this->getLang('dl_fail'));
32134aae6dbSAndreas Gohr            return false;
32234aae6dbSAndreas Gohr        }
32334aae6dbSAndreas Gohr
32434aae6dbSAndreas Gohr        if(!io_saveFile($this->tgzfile, $data)) {
3256e7afd39SAndreas Gohr            $this->_warn($this->getLang('dl_fail'));
32634aae6dbSAndreas Gohr            return false;
32734aae6dbSAndreas Gohr        }
32834aae6dbSAndreas Gohr
329fb522b50SAndreas Gohr        self::_say($this->getLang('dl_done'), filesize_h(strlen($data)));
3303c38de15SAndreas Gohr
33134aae6dbSAndreas Gohr        return true;
33234aae6dbSAndreas Gohr    }
33334aae6dbSAndreas Gohr
334bd08ebd1SAndreas Gohr    /**
335bd08ebd1SAndreas Gohr     * Unpack the tarball
336bd08ebd1SAndreas Gohr     *
337bd08ebd1SAndreas Gohr     * @return bool
338bd08ebd1SAndreas Gohr     */
33934aae6dbSAndreas Gohr    private function _step_unpack() {
340fb522b50SAndreas Gohr        self::_say('<b>'.$this->getLang('pk_extract').'</b>');
34134aae6dbSAndreas Gohr
34258774abaSFrancis GUDIN        @set_time_limit(300);
34334aae6dbSAndreas Gohr        @ignore_user_abort();
34434aae6dbSAndreas Gohr
345d04a99cdSAndreas Gohr        try {
346d04a99cdSAndreas Gohr            $tar = new VerboseTar();
347d04a99cdSAndreas Gohr            $tar->open($this->tgzfile);
348d04a99cdSAndreas Gohr            $tar->extract($this->tgzdir, 1);
349d04a99cdSAndreas Gohr            $tar->close();
350d04a99cdSAndreas Gohr        } catch (Exception $e) {
351d04a99cdSAndreas Gohr            $this->_warn($e->getMessage());
3526e7afd39SAndreas Gohr            $this->_warn($this->getLang('pk_fail'));
35334aae6dbSAndreas Gohr            return false;
35434aae6dbSAndreas Gohr        }
35534aae6dbSAndreas Gohr
356fb522b50SAndreas Gohr        self::_say($this->getLang('pk_done'));
357738c0102SAndreas Gohr
358fb522b50SAndreas Gohr        self::_say(
359bd08ebd1SAndreas Gohr            $this->getLang('pk_version'),
360738c0102SAndreas Gohr            hsc(file_get_contents($this->tgzdir.'/VERSION')),
361bd08ebd1SAndreas Gohr            getVersion()
362bd08ebd1SAndreas Gohr        );
36334aae6dbSAndreas Gohr        return true;
36434aae6dbSAndreas Gohr    }
36534aae6dbSAndreas Gohr
366bd08ebd1SAndreas Gohr    /**
367bd08ebd1SAndreas Gohr     * Check permissions of files to change
368bd08ebd1SAndreas Gohr     *
369bd08ebd1SAndreas Gohr     * @return bool
370bd08ebd1SAndreas Gohr     */
3713c38de15SAndreas Gohr    private function _step_check() {
372fb522b50SAndreas Gohr        self::_say($this->getLang('ck_start'));
3733c38de15SAndreas Gohr        $ok = $this->_traverse('', true);
37434aae6dbSAndreas Gohr        if($ok) {
375fb522b50SAndreas Gohr            self::_say('<b>'.$this->getLang('ck_done').'</b>');
37634aae6dbSAndreas Gohr        } else {
3776e7afd39SAndreas Gohr            $this->_warn('<b>'.$this->getLang('ck_fail').'</b>');
37834aae6dbSAndreas Gohr        }
3793c38de15SAndreas Gohr        return $ok;
3803c38de15SAndreas Gohr    }
38134aae6dbSAndreas Gohr
382bd08ebd1SAndreas Gohr    /**
383bd08ebd1SAndreas Gohr     * Copy over new files
384bd08ebd1SAndreas Gohr     *
385bd08ebd1SAndreas Gohr     * @return bool
386bd08ebd1SAndreas Gohr     */
3873c38de15SAndreas Gohr    private function _step_copy() {
388fb522b50SAndreas Gohr        self::_say($this->getLang('cp_start'));
3893c38de15SAndreas Gohr        $ok = $this->_traverse('', false);
3903c38de15SAndreas Gohr        if($ok) {
391fb522b50SAndreas Gohr            self::_say('<b>'.$this->getLang('cp_done').'</b>');
39263712694SAndreas Gohr            $this->_rmold();
393fb522b50SAndreas Gohr            self::_say('<b>'.$this->getLang('finish').'</b>');
39434aae6dbSAndreas Gohr        } else {
3956e7afd39SAndreas Gohr            $this->_warn('<b>'.$this->getLang('cp_fail').'</b>');
39634aae6dbSAndreas Gohr        }
39734aae6dbSAndreas Gohr        return $ok;
39834aae6dbSAndreas Gohr    }
39934aae6dbSAndreas Gohr
400bd08ebd1SAndreas Gohr    /**
401bd08ebd1SAndreas Gohr     * Delete outdated files
402bd08ebd1SAndreas Gohr     */
40363712694SAndreas Gohr    private function _rmold() {
404a85c9abbSAndreas Gohr        global $conf;
405a85c9abbSAndreas Gohr
40663712694SAndreas Gohr        $list = file($this->tgzdir.'data/deleted.files');
40763712694SAndreas Gohr        foreach($list as $line) {
40863712694SAndreas Gohr            $line = trim(preg_replace('/#.*$/', '', $line));
40963712694SAndreas Gohr            if(!$line) continue;
41063712694SAndreas Gohr            $file = DOKU_INC.$line;
41163712694SAndreas Gohr            if(!file_exists($file)) continue;
4127b7da9aaSAndreas Gohr
4137b7da9aaSAndreas Gohr            // check that the given file is an case sensitive match
4147b7da9aaSAndreas Gohr            if(basename(realpath($file)) != basename($file)) {
415fb522b50SAndreas Gohr                self::_say($this->getLang('rm_mismatch'), hsc($line));
416a6a26032SAndreas Gohr                continue;
4177b7da9aaSAndreas Gohr            }
4187b7da9aaSAndreas Gohr
41963712694SAndreas Gohr            if((is_dir($file) && $this->_rdel($file)) ||
420bd08ebd1SAndreas Gohr                @unlink($file)
421bd08ebd1SAndreas Gohr            ) {
422fb522b50SAndreas Gohr                self::_say($this->getLang('rm_done'), hsc($line));
42363712694SAndreas Gohr            } else {
4246e7afd39SAndreas Gohr                $this->_warn($this->getLang('rm_fail'), hsc($line));
42563712694SAndreas Gohr            }
42663712694SAndreas Gohr        }
427331439fbSAndreas Gohr        // delete install
428331439fbSAndreas Gohr        @unlink(DOKU_INC.'install.php');
429de440c89SAndreas Gohr
430de440c89SAndreas Gohr        // make sure update message will be gone
431de440c89SAndreas Gohr        @touch(DOKU_INC.'doku.php');
432a85c9abbSAndreas Gohr        @unlink($conf['cachedir'].'/messages.txt');
43363712694SAndreas Gohr    }
43463712694SAndreas Gohr
435bd08ebd1SAndreas Gohr    /**
436bd08ebd1SAndreas Gohr     * Traverse over the given dir and compare it to the DokuWiki dir
437bd08ebd1SAndreas Gohr     *
438bd08ebd1SAndreas Gohr     * Checks what files need an update, tests for writability and copies
439bd08ebd1SAndreas Gohr     *
440bd08ebd1SAndreas Gohr     * @param string $dir
441bd08ebd1SAndreas Gohr     * @param bool   $dryrun do not copy but only check permissions
442bd08ebd1SAndreas Gohr     * @return bool
443bd08ebd1SAndreas Gohr     */
44434aae6dbSAndreas Gohr    private function _traverse($dir, $dryrun) {
44534aae6dbSAndreas Gohr        $base = $this->tgzdir;
44634aae6dbSAndreas Gohr        $ok   = true;
44734aae6dbSAndreas Gohr
44834aae6dbSAndreas Gohr        $dh = @opendir($base.'/'.$dir);
449bd08ebd1SAndreas Gohr        if(!$dh) return false;
45034aae6dbSAndreas Gohr        while(($file = readdir($dh)) !== false) {
45134aae6dbSAndreas Gohr            if($file == '.' || $file == '..') continue;
45234aae6dbSAndreas Gohr            $from = "$base/$dir/$file";
45334aae6dbSAndreas Gohr            $to   = DOKU_INC."$dir/$file";
45434aae6dbSAndreas Gohr
45534aae6dbSAndreas Gohr            if(is_dir($from)) {
45634aae6dbSAndreas Gohr                if($dryrun) {
45734aae6dbSAndreas Gohr                    // just check for writability
45834aae6dbSAndreas Gohr                    if(!is_dir($to)) {
45934aae6dbSAndreas Gohr                        if(is_dir(dirname($to)) && !is_writable(dirname($to))) {
4606e7afd39SAndreas Gohr                            $this->_warn('<b>'.$this->getLang('tv_noperm').'</b>', hsc("$dir/$file"));
46134aae6dbSAndreas Gohr                            $ok = false;
46234aae6dbSAndreas Gohr                        }
46334aae6dbSAndreas Gohr                    }
46434aae6dbSAndreas Gohr                }
46534aae6dbSAndreas Gohr
46634aae6dbSAndreas Gohr                // recursion
46734aae6dbSAndreas Gohr                if(!$this->_traverse("$dir/$file", $dryrun)) {
46834aae6dbSAndreas Gohr                    $ok = false;
46934aae6dbSAndreas Gohr                }
47034aae6dbSAndreas Gohr            } else {
471f2fa6d10SAndreas Gohr                $fmd5 = md5(@file_get_contents($from));
472f2fa6d10SAndreas Gohr                $tmd5 = md5(@file_get_contents($to));
4736deeb3b1SAndreas Gohr                if($fmd5 != $tmd5 || !file_exists($to)) {
47434aae6dbSAndreas Gohr                    if($dryrun) {
47534aae6dbSAndreas Gohr                        // just check for writability
47634aae6dbSAndreas Gohr                        if((file_exists($to) && !is_writable($to)) ||
477bd08ebd1SAndreas Gohr                            (!file_exists($to) && is_dir(dirname($to)) && !is_writable(dirname($to)))
478bd08ebd1SAndreas Gohr                        ) {
47934aae6dbSAndreas Gohr
4806e7afd39SAndreas Gohr                            $this->_warn('<b>'.$this->getLang('tv_noperm').'</b>', hsc("$dir/$file"));
48134aae6dbSAndreas Gohr                            $ok = false;
48234aae6dbSAndreas Gohr                        } else {
483fb522b50SAndreas Gohr                            self::_say($this->getLang('tv_upd'), hsc("$dir/$file"));
48434aae6dbSAndreas Gohr                        }
48534aae6dbSAndreas Gohr                    } else {
48634aae6dbSAndreas Gohr                        // check dir
48734aae6dbSAndreas Gohr                        if(io_mkdir_p(dirname($to))) {
4887b7da9aaSAndreas Gohr                            // remove existing (avoid case sensitivity problems)
4897b7da9aaSAndreas Gohr                            if(file_exists($to) && !@unlink($to)) {
4907b7da9aaSAndreas Gohr                                $this->_warn('<b>'.$this->getLang('tv_nodel').'</b>', hsc("$dir/$file"));
4917b7da9aaSAndreas Gohr                                $ok = false;
4927b7da9aaSAndreas Gohr                            }
49334aae6dbSAndreas Gohr                            // copy
49434aae6dbSAndreas Gohr                            if(!copy($from, $to)) {
4956e7afd39SAndreas Gohr                                $this->_warn('<b>'.$this->getLang('tv_nocopy').'</b>', hsc("$dir/$file"));
49634aae6dbSAndreas Gohr                                $ok = false;
49734aae6dbSAndreas Gohr                            } else {
498fb522b50SAndreas Gohr                                self::_say($this->getLang('tv_done'), hsc("$dir/$file"));
49934aae6dbSAndreas Gohr                            }
50034aae6dbSAndreas Gohr                        } else {
5016e7afd39SAndreas Gohr                            $this->_warn('<b>'.$this->getLang('tv_nodir').'</b>', hsc("$dir"));
50234aae6dbSAndreas Gohr                            $ok = false;
50334aae6dbSAndreas Gohr                        }
50434aae6dbSAndreas Gohr                    }
50534aae6dbSAndreas Gohr                }
50634aae6dbSAndreas Gohr            }
50734aae6dbSAndreas Gohr        }
50834aae6dbSAndreas Gohr        closedir($dh);
50934aae6dbSAndreas Gohr        return $ok;
51034aae6dbSAndreas Gohr    }
5110ab52135SAndreas Gohr
5120ab52135SAndreas Gohr    /**
5130ab52135SAndreas Gohr     * Figure out the release date from the version string
5140ab52135SAndreas Gohr     *
5150ab52135SAndreas Gohr     * @param $version
5160ab52135SAndreas Gohr     * @return int|string returns 0 if the version can't be read
5170ab52135SAndreas Gohr     */
5180ab52135SAndreas Gohr    public function dateFromVersion($version) {
5190ab52135SAndreas Gohr        if(preg_match('/(^|[^\d])(\d\d\d\d-\d\d-\d\d)([^\d]|$)/i', $version, $m)) {
5200ab52135SAndreas Gohr            return $m[2];
5210ab52135SAndreas Gohr        }
5220ab52135SAndreas Gohr        return 0;
5230ab52135SAndreas Gohr    }
52434aae6dbSAndreas Gohr}
52534aae6dbSAndreas Gohr
52634aae6dbSAndreas Gohr// vim:ts=4:sw=4:et:enc=utf-8:
527