xref: /plugin/upgrade/admin.php (revision 34aae6db7e6952fa49c22c6dc013e0e2d0dff57a)
1*34aae6dbSAndreas Gohr<?php
2*34aae6dbSAndreas Gohr/**
3*34aae6dbSAndreas Gohr * DokuWiki Plugin update (Admin Component)
4*34aae6dbSAndreas Gohr *
5*34aae6dbSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6*34aae6dbSAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
7*34aae6dbSAndreas Gohr */
8*34aae6dbSAndreas Gohr
9*34aae6dbSAndreas Gohr// must be run within Dokuwiki
10*34aae6dbSAndreas Gohrif (!defined('DOKU_INC')) die();
11*34aae6dbSAndreas Gohr
12*34aae6dbSAndreas Gohrif (!defined('DOKU_LF')) define('DOKU_LF', "\n");
13*34aae6dbSAndreas Gohrif (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
14*34aae6dbSAndreas Gohrif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
15*34aae6dbSAndreas Gohr
16*34aae6dbSAndreas Gohrrequire_once DOKU_PLUGIN.'admin.php';
17*34aae6dbSAndreas Gohrrequire_once DOKU_PLUGIN.'update/VerboseTarLib.class.php';
18*34aae6dbSAndreas Gohr
19*34aae6dbSAndreas Gohrclass admin_plugin_update extends DokuWiki_Admin_Plugin {
20*34aae6dbSAndreas Gohr    private $tgzurl;
21*34aae6dbSAndreas Gohr    private $tgzfile;
22*34aae6dbSAndreas Gohr    private $tgzdir;
23*34aae6dbSAndreas Gohr
24*34aae6dbSAndreas Gohr    public function __construct(){
25*34aae6dbSAndreas Gohr        global $conf;
26*34aae6dbSAndreas Gohr
27*34aae6dbSAndreas Gohr        $branch = 'stable';
28*34aae6dbSAndreas Gohr
29*34aae6dbSAndreas Gohr        $this->tgzurl  = 'http://github.com/splitbrain/dokuwiki/tarball/'.$branch;
30*34aae6dbSAndreas Gohr        $this->tgzfile = $conf['tmpdir'].'/dokuwiki-update.tgz';
31*34aae6dbSAndreas Gohr        $this->tgzdir  = $conf['tmpdir'].'/dokuwiki-update/';
32*34aae6dbSAndreas Gohr    }
33*34aae6dbSAndreas Gohr
34*34aae6dbSAndreas Gohr    function getMenuSort() { return 555; }
35*34aae6dbSAndreas Gohr
36*34aae6dbSAndreas Gohr    function handle() {
37*34aae6dbSAndreas Gohr    }
38*34aae6dbSAndreas Gohr
39*34aae6dbSAndreas Gohr    public function html() {
40*34aae6dbSAndreas Gohr        $abrt = false;
41*34aae6dbSAndreas Gohr        $next = false;
42*34aae6dbSAndreas Gohr
43*34aae6dbSAndreas Gohr        echo '<h1>' . $this->getLang('menu') . '</h1>';
44*34aae6dbSAndreas Gohr#FIXME check and abort on safemode
45*34aae6dbSAndreas Gohr
46*34aae6dbSAndreas Gohr        $this->_stepit(&$abrt, &$next);
47*34aae6dbSAndreas Gohr
48*34aae6dbSAndreas Gohr#FIXME add security check
49*34aae6dbSAndreas Gohr        echo '<form action="" method="get">';
50*34aae6dbSAndreas Gohr        echo '<input type="hidden" name="do" value="admin" />';
51*34aae6dbSAndreas Gohr        echo '<input type="hidden" name="page" value="update" />';
52*34aae6dbSAndreas Gohr        if($next) echo '<input type="submit" name="step['.$next.']" value="Continue" />';
53*34aae6dbSAndreas Gohr        if($abrt) echo '<input type="submit" name="step[cancel]" value="Abort" />';
54*34aae6dbSAndreas Gohr        echo '</form>';
55*34aae6dbSAndreas Gohr    }
56*34aae6dbSAndreas Gohr
57*34aae6dbSAndreas Gohr    private function _stepit(&$abrt, &$next){
58*34aae6dbSAndreas Gohr        if(isset($_REQUEST['step']) && is_array($_REQUEST['step'])){
59*34aae6dbSAndreas Gohr            $step = array_shift(array_keys($_REQUEST['step']));
60*34aae6dbSAndreas Gohr        }else{
61*34aae6dbSAndreas Gohr            $step = '';
62*34aae6dbSAndreas Gohr        }
63*34aae6dbSAndreas Gohr
64*34aae6dbSAndreas Gohr        if($step == 'cancel'){
65*34aae6dbSAndreas Gohr            # cleanup
66*34aae6dbSAndreas Gohr            @unlink($this->tgzfile);
67*34aae6dbSAndreas Gohr
68*34aae6dbSAndreas Gohr            $step = '';
69*34aae6dbSAndreas Gohr        }
70*34aae6dbSAndreas Gohr
71*34aae6dbSAndreas Gohr        if($step){
72*34aae6dbSAndreas Gohr            $abrt = true;
73*34aae6dbSAndreas Gohr            $next = false;
74*34aae6dbSAndreas Gohr            $this->_say('<div id="plugin__update">');
75*34aae6dbSAndreas Gohr            if(!file_exists($this->tgzfile)){
76*34aae6dbSAndreas Gohr                if($this->_step_download()) $next = 'unpack';
77*34aae6dbSAndreas Gohr            }elseif(!is_dir($this->tgzdir)){
78*34aae6dbSAndreas Gohr                if($this->_step_unpack()) $next = 'check';
79*34aae6dbSAndreas Gohr            }elseif($step != 'upgrade'){
80*34aae6dbSAndreas Gohr                if($this->_step_copy(true)) $next = 'upgrade';
81*34aae6dbSAndreas Gohr            }elseif($step == 'upgrade'){
82*34aae6dbSAndreas Gohr                if($this->_step_copy(false)) $next = 'cancel';
83*34aae6dbSAndreas Gohr            }else{
84*34aae6dbSAndreas Gohr                #continue
85*34aae6dbSAndreas Gohr                echo 'huh';
86*34aae6dbSAndreas Gohr            }
87*34aae6dbSAndreas Gohr            $this->_say('</div>');
88*34aae6dbSAndreas Gohr        }else{
89*34aae6dbSAndreas Gohr            # first time run, show intro
90*34aae6dbSAndreas Gohr            echo $this->locale_xhtml('step0');
91*34aae6dbSAndreas Gohr            $abrt = false;
92*34aae6dbSAndreas Gohr            $next = 'download';
93*34aae6dbSAndreas Gohr        }
94*34aae6dbSAndreas Gohr    }
95*34aae6dbSAndreas Gohr
96*34aae6dbSAndreas Gohr    private function _say(){
97*34aae6dbSAndreas Gohr        $args = func_get_args();
98*34aae6dbSAndreas Gohr        echo vsprintf(array_shift($args)."<br />\n",$args);
99*34aae6dbSAndreas Gohr        flush();
100*34aae6dbSAndreas Gohr        ob_flush();
101*34aae6dbSAndreas Gohr    }
102*34aae6dbSAndreas Gohr
103*34aae6dbSAndreas Gohr    private function _step_download(){
104*34aae6dbSAndreas Gohr        $this->_say('Downloading from %s',$this->tgzurl);
105*34aae6dbSAndreas Gohr
106*34aae6dbSAndreas Gohr        @set_time_limit(120);
107*34aae6dbSAndreas Gohr        @ignore_user_abort();
108*34aae6dbSAndreas Gohr
109*34aae6dbSAndreas Gohr        $http = new DokuHTTPClient();
110*34aae6dbSAndreas Gohr        $http->timeout = 120;
111*34aae6dbSAndreas Gohr        $data = $http->get($this->tgzurl);
112*34aae6dbSAndreas Gohr
113*34aae6dbSAndreas Gohr        if(!$data){
114*34aae6dbSAndreas Gohr            $this->_say($http->error);
115*34aae6dbSAndreas Gohr            $this->_say("Download failed.");
116*34aae6dbSAndreas Gohr            return false;
117*34aae6dbSAndreas Gohr        }
118*34aae6dbSAndreas Gohr
119*34aae6dbSAndreas Gohr        $this->_say('Received %d bytes',strlen($data));
120*34aae6dbSAndreas Gohr
121*34aae6dbSAndreas Gohr        if(!io_saveFile($this->tgzfile,$data)){
122*34aae6dbSAndreas Gohr            $this->_say("Failed to save download.");
123*34aae6dbSAndreas Gohr            return false;
124*34aae6dbSAndreas Gohr        }
125*34aae6dbSAndreas Gohr
126*34aae6dbSAndreas Gohr        return true;
127*34aae6dbSAndreas Gohr    }
128*34aae6dbSAndreas Gohr
129*34aae6dbSAndreas Gohr    private function _step_unpack(){
130*34aae6dbSAndreas Gohr        global $conf;
131*34aae6dbSAndreas Gohr        $this->_say('Extracting the archive...');
132*34aae6dbSAndreas Gohr
133*34aae6dbSAndreas Gohr        @set_time_limit(120);
134*34aae6dbSAndreas Gohr        @ignore_user_abort();
135*34aae6dbSAndreas Gohr
136*34aae6dbSAndreas Gohr        $tar = new VerboseTarLib($this->tgzfile);
137*34aae6dbSAndreas Gohr        if($tar->_initerror < 0){
138*34aae6dbSAndreas Gohr            $this->_say($tar->TarErrorStr($tar->_initerror));
139*34aae6dbSAndreas Gohr            $this->_say('Extraction failed on init');
140*34aae6dbSAndreas Gohr            return false;
141*34aae6dbSAndreas Gohr        }
142*34aae6dbSAndreas Gohr
143*34aae6dbSAndreas Gohr        $ok = $tar->Extract(VerboseTarLib::FULL_ARCHIVE,$this->tgzdir,1,$conf['fmode'],'/^(_cs|_test|\.gitignore)/');
144*34aae6dbSAndreas Gohr        if($ok < 1){
145*34aae6dbSAndreas Gohr            $this->_say($tar->TarErrorStr($ok));
146*34aae6dbSAndreas Gohr            $this->_say('Extraction failed');
147*34aae6dbSAndreas Gohr            return false;
148*34aae6dbSAndreas Gohr        }
149*34aae6dbSAndreas Gohr
150*34aae6dbSAndreas Gohr        $this->_say('Extraction done.');
151*34aae6dbSAndreas Gohr        return true;
152*34aae6dbSAndreas Gohr    }
153*34aae6dbSAndreas Gohr
154*34aae6dbSAndreas Gohr    private function _step_copy($dryrun=true){
155*34aae6dbSAndreas Gohr        $ok = $this->_traverse('',$dryrun);
156*34aae6dbSAndreas Gohr        if($dryrun){
157*34aae6dbSAndreas Gohr            if($ok){
158*34aae6dbSAndreas Gohr                $this->_say('<b>All files are writable, ready to upgrade</b>');
159*34aae6dbSAndreas Gohr            }else{
160*34aae6dbSAndreas Gohr                $this->_say('<b>Some files aren\'t writable. Uprade not possible.</b>');
161*34aae6dbSAndreas Gohr            }
162*34aae6dbSAndreas Gohr        }else{
163*34aae6dbSAndreas Gohr            if($ok){
164*34aae6dbSAndreas Gohr                $this->_say('<b>All files upgraded successfully</b>');
165*34aae6dbSAndreas Gohr
166*34aae6dbSAndreas Gohr                #FIXME delete unused files
167*34aae6dbSAndreas Gohr            }else{
168*34aae6dbSAndreas Gohr                $this->_say('<b>Some files couldn\'t be upgraded. Uh-oh. Better check manually.</b>');
169*34aae6dbSAndreas Gohr            }
170*34aae6dbSAndreas Gohr        }
171*34aae6dbSAndreas Gohr        return $ok;
172*34aae6dbSAndreas Gohr    }
173*34aae6dbSAndreas Gohr
174*34aae6dbSAndreas Gohr    private function _traverse($dir,$dryrun){
175*34aae6dbSAndreas Gohr        $base = $this->tgzdir;
176*34aae6dbSAndreas Gohr        $ok = true;
177*34aae6dbSAndreas Gohr
178*34aae6dbSAndreas Gohr        $dh = @opendir($base.'/'.$dir);
179*34aae6dbSAndreas Gohr        if(!$dh) return;
180*34aae6dbSAndreas Gohr        while(($file = readdir($dh)) !== false){
181*34aae6dbSAndreas Gohr            if($file == '.' || $file == '..') continue;
182*34aae6dbSAndreas Gohr            $from = "$base/$dir/$file";
183*34aae6dbSAndreas Gohr            $to   = DOKU_INC."$dir/$file";
184*34aae6dbSAndreas Gohr
185*34aae6dbSAndreas Gohr            if(is_dir($from)){
186*34aae6dbSAndreas Gohr                if($dryrun){
187*34aae6dbSAndreas Gohr                    // just check for writability
188*34aae6dbSAndreas Gohr                    if(!is_dir($to)){
189*34aae6dbSAndreas Gohr                        if(is_dir(dirname($to)) && !is_writable(dirname($to))){
190*34aae6dbSAndreas Gohr                            $this->_say("<b>%s is not writable</b>",hsc("$dir/$file"));
191*34aae6dbSAndreas Gohr                            $ok = false;
192*34aae6dbSAndreas Gohr                        }
193*34aae6dbSAndreas Gohr                    }
194*34aae6dbSAndreas Gohr                }
195*34aae6dbSAndreas Gohr
196*34aae6dbSAndreas Gohr                // recursion
197*34aae6dbSAndreas Gohr                if(!$this->_traverse("$dir/$file",$dryrun)){
198*34aae6dbSAndreas Gohr                    $ok = false;
199*34aae6dbSAndreas Gohr                }
200*34aae6dbSAndreas Gohr            }else{
201*34aae6dbSAndreas Gohr                $fmd5 = md5($from);
202*34aae6dbSAndreas Gohr                $tmd5 = md5($to);
203*34aae6dbSAndreas Gohr                if($fmd5 != $tmd5){
204*34aae6dbSAndreas Gohr                    if($dryrun){
205*34aae6dbSAndreas Gohr                        // just check for writability
206*34aae6dbSAndreas Gohr                        if( (file_exists($to) && !is_writable($to)) ||
207*34aae6dbSAndreas Gohr                            (!file_exists($to) && is_dir(dirname($to)) && !is_writable(dirname($to))) ){
208*34aae6dbSAndreas Gohr
209*34aae6dbSAndreas Gohr                            $this->_say("<b>%s is not writable</b>",hsc("$dir/$file"));
210*34aae6dbSAndreas Gohr                            $ok = false;
211*34aae6dbSAndreas Gohr                        }else{
212*34aae6dbSAndreas Gohr                            $this->_say("%s needs update",hsc("$dir/$file"));
213*34aae6dbSAndreas Gohr                        }
214*34aae6dbSAndreas Gohr                    }else{
215*34aae6dbSAndreas Gohr                        // check dir
216*34aae6dbSAndreas Gohr                        if(io_mkdir_p(dirname($to))){
217*34aae6dbSAndreas Gohr                            // copy
218*34aae6dbSAndreas Gohr                            if(!copy($from,$to)){
219*34aae6dbSAndreas Gohr                                $this->_say("<b>%s couldn't be copied</b>",hsc("$dir/$file"));
220*34aae6dbSAndreas Gohr                                $ok = false;
221*34aae6dbSAndreas Gohr                            }else{
222*34aae6dbSAndreas Gohr                                $this->_say("%s updated",hsc("$dir/$file"));
223*34aae6dbSAndreas Gohr                            }
224*34aae6dbSAndreas Gohr                        }else{
225*34aae6dbSAndreas Gohr                            $this->_say("<b>failed to create %s</b>",hsc("$dir"));
226*34aae6dbSAndreas Gohr                            $ok = false;
227*34aae6dbSAndreas Gohr                        }
228*34aae6dbSAndreas Gohr                    }
229*34aae6dbSAndreas Gohr                }
230*34aae6dbSAndreas Gohr            }
231*34aae6dbSAndreas Gohr        }
232*34aae6dbSAndreas Gohr        closedir($dh);
233*34aae6dbSAndreas Gohr        return $ok;
234*34aae6dbSAndreas Gohr    }
235*34aae6dbSAndreas Gohr}
236*34aae6dbSAndreas Gohr
237*34aae6dbSAndreas Gohr// vim:ts=4:sw=4:et:enc=utf-8:
238