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