14e3e87e4SAndreas Gohr<?php 24e3e87e4SAndreas Gohr/** 34e3e87e4SAndreas Gohr * DokuWiki Plugin upgrade (Helper Component) 44e3e87e4SAndreas Gohr * 54e3e87e4SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 64e3e87e4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 74e3e87e4SAndreas Gohr */ 84e3e87e4SAndreas Gohr 94e3e87e4SAndreas Gohruse dokuwiki\plugin\upgrade\HTTP\DokuHTTPClient; 104e3e87e4SAndreas Gohruse splitbrain\PHPArchive\FileInfo; 114e3e87e4SAndreas Gohruse splitbrain\PHPArchive\Tar; 124e3e87e4SAndreas Gohr 134e3e87e4SAndreas Gohrclass helper_plugin_upgrade extends DokuWiki_Plugin 144e3e87e4SAndreas Gohr{ 154e3e87e4SAndreas Gohr /** @var string download URL for the new DokuWiki release */ 164e3e87e4SAndreas Gohr public $tgzurl; 174e3e87e4SAndreas Gohr /** @var string full path to where the file will be downloaded to */ 184e3e87e4SAndreas Gohr public $tgzfile; 194e3e87e4SAndreas Gohr /** @var string full path to where the file will be extracted to */ 204e3e87e4SAndreas Gohr public $tgzdir; 214e3e87e4SAndreas Gohr /** @var string URL to the VERSION file of the new DokuWiki release */ 224e3e87e4SAndreas Gohr public $tgzversion; 236741b585SAndreas Gohr /** @var string URL to the composer.json file of the new DokuWiki release */ 24*4cefc146SAndreas Gohr protected $composer; 254e3e87e4SAndreas Gohr /** @var string URL to the plugin.info.txt file of the upgrade plugin */ 264e3e87e4SAndreas Gohr public $pluginversion; 274e3e87e4SAndreas Gohr 284e3e87e4SAndreas Gohr /** @var admin_plugin_upgrade|cli_plugin_upgrade */ 294e3e87e4SAndreas Gohr protected $logger; 304e3e87e4SAndreas Gohr 314e3e87e4SAndreas Gohr public function __construct() 324e3e87e4SAndreas Gohr { 334e3e87e4SAndreas Gohr global $conf; 344e3e87e4SAndreas Gohr 354e3e87e4SAndreas Gohr $branch = 'stable'; 364e3e87e4SAndreas Gohr 374e3e87e4SAndreas Gohr $this->tgzurl = "https://github.com/splitbrain/dokuwiki/archive/$branch.tar.gz"; 384e3e87e4SAndreas Gohr $this->tgzfile = $conf['tmpdir'] . '/dokuwiki-upgrade.tgz'; 394e3e87e4SAndreas Gohr $this->tgzdir = $conf['tmpdir'] . '/dokuwiki-upgrade/'; 404e3e87e4SAndreas Gohr $this->tgzversion = "https://raw.githubusercontent.com/splitbrain/dokuwiki/$branch/VERSION"; 416741b585SAndreas Gohr $this->composer = "https://raw.githubusercontent.com/splitbrain/dokuwiki/$branch/composer.json"; 424e3e87e4SAndreas Gohr $this->pluginversion = "https://raw.githubusercontent.com/splitbrain/dokuwiki-plugin-upgrade/master/plugin.info.txt"; 434e3e87e4SAndreas Gohr } 444e3e87e4SAndreas Gohr 454e3e87e4SAndreas Gohr /** 464e3e87e4SAndreas Gohr * @param admin_plugin_upgrade|cli_plugin_upgrade $logger Logger object 474e3e87e4SAndreas Gohr * @return void 484e3e87e4SAndreas Gohr */ 494e3e87e4SAndreas Gohr public function setLogger($logger) 504e3e87e4SAndreas Gohr { 514e3e87e4SAndreas Gohr $this->logger = $logger; 524e3e87e4SAndreas Gohr } 534e3e87e4SAndreas Gohr 544e3e87e4SAndreas Gohr // region Steps 554e3e87e4SAndreas Gohr 564e3e87e4SAndreas Gohr /** 574e3e87e4SAndreas Gohr * Check various versions 584e3e87e4SAndreas Gohr * 594e3e87e4SAndreas Gohr * @return bool 604e3e87e4SAndreas Gohr */ 614e3e87e4SAndreas Gohr public function checkVersions() 624e3e87e4SAndreas Gohr { 634e3e87e4SAndreas Gohr $ok = true; 644e3e87e4SAndreas Gohr 654e3e87e4SAndreas Gohr // we need SSL - only newer HTTPClients check that themselves 664e3e87e4SAndreas Gohr if (!in_array('ssl', stream_get_transports())) { 674e3e87e4SAndreas Gohr $this->log('error', $this->getLang('vs_ssl')); 684e3e87e4SAndreas Gohr $ok = false; 694e3e87e4SAndreas Gohr } 704e3e87e4SAndreas Gohr 714e3e87e4SAndreas Gohr // get the available version 724e3e87e4SAndreas Gohr $http = new DokuHTTPClient(); 734e3e87e4SAndreas Gohr $tgzversion = trim($http->get($this->tgzversion)); 744e3e87e4SAndreas Gohr if (!$tgzversion) { 754e3e87e4SAndreas Gohr $this->log('error', $this->getLang('vs_tgzno') . ' ' . hsc($http->error)); 764e3e87e4SAndreas Gohr $ok = false; 774e3e87e4SAndreas Gohr } 784e3e87e4SAndreas Gohr $tgzversionnum = $this->dateFromVersion($tgzversion); 794e3e87e4SAndreas Gohr if ($tgzversionnum === 0) { 804e3e87e4SAndreas Gohr $this->log('error', $this->getLang('vs_tgzno')); 814e3e87e4SAndreas Gohr $ok = false; 824e3e87e4SAndreas Gohr } else { 834e3e87e4SAndreas Gohr $this->log('notice', $this->getLang('vs_tgz'), $tgzversion); 844e3e87e4SAndreas Gohr } 854e3e87e4SAndreas Gohr 864e3e87e4SAndreas Gohr // get the current version 874e3e87e4SAndreas Gohr $versiondata = getVersionData(); 884e3e87e4SAndreas Gohr $version = trim($versiondata['date']); 894e3e87e4SAndreas Gohr $versionnum = $this->dateFromVersion($version); 904e3e87e4SAndreas Gohr $this->log('notice', $this->getLang('vs_local'), $version); 914e3e87e4SAndreas Gohr 924e3e87e4SAndreas Gohr // compare versions 934e3e87e4SAndreas Gohr if (!$versionnum) { 944e3e87e4SAndreas Gohr $this->log('warning', $this->getLang('vs_localno')); 954e3e87e4SAndreas Gohr $ok = false; 964e3e87e4SAndreas Gohr } else if ($tgzversionnum) { 974e3e87e4SAndreas Gohr if ($tgzversionnum < $versionnum) { 984e3e87e4SAndreas Gohr $this->log('warning', $this->getLang('vs_newer')); 994e3e87e4SAndreas Gohr $ok = false; 1004e3e87e4SAndreas Gohr } elseif ($tgzversionnum == $versionnum && $tgzversion == $version) { 1014e3e87e4SAndreas Gohr $this->log('warning', $this->getLang('vs_same')); 1024e3e87e4SAndreas Gohr $ok = false; 1034e3e87e4SAndreas Gohr } 1044e3e87e4SAndreas Gohr } 1054e3e87e4SAndreas Gohr 1064e3e87e4SAndreas Gohr // check plugin version 1074e3e87e4SAndreas Gohr $pluginversion = $http->get($this->pluginversion); 1084e3e87e4SAndreas Gohr if ($pluginversion) { 1094e3e87e4SAndreas Gohr $plugininfo = linesToHash(explode("\n", $pluginversion)); 1104e3e87e4SAndreas Gohr $myinfo = $this->getInfo(); 1114e3e87e4SAndreas Gohr if ($plugininfo['date'] > $myinfo['date']) { 1124e3e87e4SAndreas Gohr $this->log('warning', $this->getLang('vs_plugin'), $plugininfo['date']); 1134e3e87e4SAndreas Gohr $ok = false; 1144e3e87e4SAndreas Gohr } 1154e3e87e4SAndreas Gohr } 1164e3e87e4SAndreas Gohr 1174e3e87e4SAndreas Gohr // check if PHP is up to date 1186741b585SAndreas Gohr $json = $http->get($this->composer); 1196741b585SAndreas Gohr $data = json_decode($json, true); 1206741b585SAndreas Gohr $minphp = $data['config']['platform']['php']; 1214e3e87e4SAndreas Gohr if (version_compare(phpversion(), $minphp, '<')) { 1224e3e87e4SAndreas Gohr $this->log('error', $this->getLang('vs_php'), $minphp, phpversion()); 1234e3e87e4SAndreas Gohr $ok = false; 1244e3e87e4SAndreas Gohr } 1254e3e87e4SAndreas Gohr 1264e3e87e4SAndreas Gohr return $ok; 1274e3e87e4SAndreas Gohr } 1284e3e87e4SAndreas Gohr 1294e3e87e4SAndreas Gohr /** 1304e3e87e4SAndreas Gohr * Download the tarball 1314e3e87e4SAndreas Gohr * 1324e3e87e4SAndreas Gohr * @return bool 1334e3e87e4SAndreas Gohr */ 1344e3e87e4SAndreas Gohr public function downloadTarball() 1354e3e87e4SAndreas Gohr { 1364e3e87e4SAndreas Gohr $this->log('notice', $this->getLang('dl_from'), $this->tgzurl); 1374e3e87e4SAndreas Gohr 1384e3e87e4SAndreas Gohr @set_time_limit(300); 1394e3e87e4SAndreas Gohr @ignore_user_abort(); 1404e3e87e4SAndreas Gohr 1414e3e87e4SAndreas Gohr $http = new DokuHTTPClient(); 1424e3e87e4SAndreas Gohr $http->timeout = 300; 1434e3e87e4SAndreas Gohr $data = $http->get($this->tgzurl); 1444e3e87e4SAndreas Gohr 1454e3e87e4SAndreas Gohr if (!$data) { 1464e3e87e4SAndreas Gohr $this->log('error', $http->error); 1474e3e87e4SAndreas Gohr $this->log('error', $this->getLang('dl_fail')); 1484e3e87e4SAndreas Gohr return false; 1494e3e87e4SAndreas Gohr } 1504e3e87e4SAndreas Gohr 151fc9fd1daSAndreas Gohr io_mkdir_p(dirname($this->tgzfile)); 152fc9fd1daSAndreas Gohr if (!file_put_contents($this->tgzfile, $data)) { 1534e3e87e4SAndreas Gohr $this->log('error', $this->getLang('dl_fail')); 1544e3e87e4SAndreas Gohr return false; 1554e3e87e4SAndreas Gohr } 1564e3e87e4SAndreas Gohr 1574e3e87e4SAndreas Gohr $this->log('success', $this->getLang('dl_done'), filesize_h(strlen($data))); 1584e3e87e4SAndreas Gohr return true; 1594e3e87e4SAndreas Gohr } 1604e3e87e4SAndreas Gohr 1614e3e87e4SAndreas Gohr /** 1624e3e87e4SAndreas Gohr * Unpack the tarball 1634e3e87e4SAndreas Gohr * 1644e3e87e4SAndreas Gohr * @return bool 1654e3e87e4SAndreas Gohr */ 1664e3e87e4SAndreas Gohr public function extractTarball() 1674e3e87e4SAndreas Gohr { 1684e3e87e4SAndreas Gohr $this->log('notice', '<b>' . $this->getLang('pk_extract') . '</b>'); 1694e3e87e4SAndreas Gohr 1704e3e87e4SAndreas Gohr @set_time_limit(300); 1714e3e87e4SAndreas Gohr @ignore_user_abort(); 1724e3e87e4SAndreas Gohr 1734e3e87e4SAndreas Gohr try { 1744e3e87e4SAndreas Gohr $tar = new Tar(); 1754e3e87e4SAndreas Gohr $tar->setCallback(function ($file) { 1764e3e87e4SAndreas Gohr /** @var FileInfo $file */ 1774e3e87e4SAndreas Gohr $this->log('info', $file->getPath()); 1784e3e87e4SAndreas Gohr }); 1794e3e87e4SAndreas Gohr $tar->open($this->tgzfile); 1804e3e87e4SAndreas Gohr $tar->extract($this->tgzdir, 1); 1814e3e87e4SAndreas Gohr $tar->close(); 1824e3e87e4SAndreas Gohr } catch (Exception $e) { 1834e3e87e4SAndreas Gohr $this->log('error', $e->getMessage()); 1844e3e87e4SAndreas Gohr $this->log('error', $this->getLang('pk_fail')); 1854e3e87e4SAndreas Gohr return false; 1864e3e87e4SAndreas Gohr } 1874e3e87e4SAndreas Gohr 1884e3e87e4SAndreas Gohr $this->log('success', $this->getLang('pk_done')); 1894e3e87e4SAndreas Gohr 1904e3e87e4SAndreas Gohr $this->log( 1914e3e87e4SAndreas Gohr 'notice', 1924e3e87e4SAndreas Gohr $this->getLang('pk_version'), 1934e3e87e4SAndreas Gohr hsc(file_get_contents($this->tgzdir . '/VERSION')), 1944e3e87e4SAndreas Gohr getVersion() 1954e3e87e4SAndreas Gohr ); 1964e3e87e4SAndreas Gohr return true; 1974e3e87e4SAndreas Gohr } 1984e3e87e4SAndreas Gohr 1994e3e87e4SAndreas Gohr /** 2004e3e87e4SAndreas Gohr * Check permissions of files to change 2014e3e87e4SAndreas Gohr * 2024e3e87e4SAndreas Gohr * @return bool 2034e3e87e4SAndreas Gohr */ 2044e3e87e4SAndreas Gohr public function checkPermissions() 2054e3e87e4SAndreas Gohr { 2064e3e87e4SAndreas Gohr $this->log('notice', $this->getLang('ck_start')); 2074e3e87e4SAndreas Gohr $ok = $this->traverseCheckAndCopy('', true); 2084e3e87e4SAndreas Gohr if ($ok) { 2094e3e87e4SAndreas Gohr $this->log('success', '<b>' . $this->getLang('ck_done') . '</b>'); 2104e3e87e4SAndreas Gohr } else { 2114e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('ck_fail') . '</b>'); 2124e3e87e4SAndreas Gohr } 2134e3e87e4SAndreas Gohr return $ok; 2144e3e87e4SAndreas Gohr } 2154e3e87e4SAndreas Gohr 2164e3e87e4SAndreas Gohr /** 2174e3e87e4SAndreas Gohr * Copy over new files 2184e3e87e4SAndreas Gohr * 2194e3e87e4SAndreas Gohr * @return bool 2204e3e87e4SAndreas Gohr */ 2214e3e87e4SAndreas Gohr public function copyFiles() 2224e3e87e4SAndreas Gohr { 2234e3e87e4SAndreas Gohr $this->log('notice', $this->getLang('cp_start')); 2244e3e87e4SAndreas Gohr $ok = $this->traverseCheckAndCopy('', false); 2254e3e87e4SAndreas Gohr if ($ok) { 2264e3e87e4SAndreas Gohr $this->log('success', '<b>' . $this->getLang('cp_done') . '</b>'); 2274e3e87e4SAndreas Gohr } else { 2284e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('cp_fail') . '</b>'); 2294e3e87e4SAndreas Gohr } 2304e3e87e4SAndreas Gohr return $ok; 2314e3e87e4SAndreas Gohr } 2324e3e87e4SAndreas Gohr 2334e3e87e4SAndreas Gohr /** 2344e3e87e4SAndreas Gohr * Delete outdated files 2354e3e87e4SAndreas Gohr */ 2364e3e87e4SAndreas Gohr public function deleteObsoleteFiles() 2374e3e87e4SAndreas Gohr { 2384e3e87e4SAndreas Gohr global $conf; 2394e3e87e4SAndreas Gohr 2404e3e87e4SAndreas Gohr $list = file($this->tgzdir . 'data/deleted.files'); 2414e3e87e4SAndreas Gohr foreach ($list as $line) { 2424e3e87e4SAndreas Gohr $line = trim(preg_replace('/#.*$/', '', $line)); 2434e3e87e4SAndreas Gohr if (!$line) continue; 2444e3e87e4SAndreas Gohr $file = DOKU_INC . $line; 2454e3e87e4SAndreas Gohr if (!file_exists($file)) continue; 2464e3e87e4SAndreas Gohr 2474e3e87e4SAndreas Gohr // check that the given file is a case sensitive match 2484e3e87e4SAndreas Gohr if (basename(realpath($file)) != basename($file)) { 2494e3e87e4SAndreas Gohr $this->log('info', $this->getLang('rm_mismatch'), hsc($line)); 2504e3e87e4SAndreas Gohr continue; 2514e3e87e4SAndreas Gohr } 2524e3e87e4SAndreas Gohr 2534e3e87e4SAndreas Gohr if ((is_dir($file) && $this->recursiveDelete($file)) || 2544e3e87e4SAndreas Gohr @unlink($file) 2554e3e87e4SAndreas Gohr ) { 2564e3e87e4SAndreas Gohr $this->log('info', $this->getLang('rm_done'), hsc($line)); 2574e3e87e4SAndreas Gohr } else { 2584e3e87e4SAndreas Gohr $this->log('error', $this->getLang('rm_fail'), hsc($line)); 2594e3e87e4SAndreas Gohr } 2604e3e87e4SAndreas Gohr } 2614e3e87e4SAndreas Gohr // delete install 2624e3e87e4SAndreas Gohr @unlink(DOKU_INC . 'install.php'); 2634e3e87e4SAndreas Gohr 2644e3e87e4SAndreas Gohr // make sure update message will be gone 2654e3e87e4SAndreas Gohr @touch(DOKU_INC . 'doku.php'); 2664e3e87e4SAndreas Gohr @unlink($conf['cachedir'] . '/messages.txt'); 2674e3e87e4SAndreas Gohr 2684e3e87e4SAndreas Gohr // clear opcache 2694e3e87e4SAndreas Gohr if (function_exists('opcache_reset')) { 2704e3e87e4SAndreas Gohr opcache_reset(); 2714e3e87e4SAndreas Gohr } 2724e3e87e4SAndreas Gohr 2734e3e87e4SAndreas Gohr $this->log('success', '<b>' . $this->getLang('finish') . '</b>'); 2744e3e87e4SAndreas Gohr return true; 2754e3e87e4SAndreas Gohr } 2764e3e87e4SAndreas Gohr 2774e3e87e4SAndreas Gohr /** 2784e3e87e4SAndreas Gohr * Remove the downloaded and extracted files 2794e3e87e4SAndreas Gohr * 2804e3e87e4SAndreas Gohr * @return bool 2814e3e87e4SAndreas Gohr */ 2824e3e87e4SAndreas Gohr public function cleanUp() 2834e3e87e4SAndreas Gohr { 2844e3e87e4SAndreas Gohr @unlink($this->tgzfile); 2854e3e87e4SAndreas Gohr $this->recursiveDelete($this->tgzdir); 2864e3e87e4SAndreas Gohr return true; 2874e3e87e4SAndreas Gohr } 2884e3e87e4SAndreas Gohr 2894e3e87e4SAndreas Gohr // endregion 2904e3e87e4SAndreas Gohr 2914e3e87e4SAndreas Gohr /** 2924e3e87e4SAndreas Gohr * Traverse over the given dir and compare it to the DokuWiki dir 2934e3e87e4SAndreas Gohr * 2944e3e87e4SAndreas Gohr * Checks what files need an update, tests for writability and copies 2954e3e87e4SAndreas Gohr * 2964e3e87e4SAndreas Gohr * @param string $dir 2974e3e87e4SAndreas Gohr * @param bool $dryrun do not copy but only check permissions 2984e3e87e4SAndreas Gohr * @return bool 2994e3e87e4SAndreas Gohr */ 3004e3e87e4SAndreas Gohr private function traverseCheckAndCopy($dir, $dryrun) 3014e3e87e4SAndreas Gohr { 3024e3e87e4SAndreas Gohr $base = $this->tgzdir; 3034e3e87e4SAndreas Gohr $ok = true; 3044e3e87e4SAndreas Gohr 3054e3e87e4SAndreas Gohr $dh = @opendir($base . '/' . $dir); 3064e3e87e4SAndreas Gohr if (!$dh) return false; 3074e3e87e4SAndreas Gohr while (($file = readdir($dh)) !== false) { 3084e3e87e4SAndreas Gohr if ($file == '.' || $file == '..') continue; 3094e3e87e4SAndreas Gohr $from = "$base/$dir/$file"; 3104e3e87e4SAndreas Gohr $to = DOKU_INC . "$dir/$file"; 3114e3e87e4SAndreas Gohr 3124e3e87e4SAndreas Gohr if (is_dir($from)) { 3134e3e87e4SAndreas Gohr if ($dryrun) { 3144e3e87e4SAndreas Gohr // just check for writability 3154e3e87e4SAndreas Gohr if (!is_dir($to)) { 3164e3e87e4SAndreas Gohr if (is_dir(dirname($to)) && !is_writable(dirname($to))) { 3174e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('tv_noperm') . '</b>', hsc("$dir/$file")); 3184e3e87e4SAndreas Gohr $ok = false; 3194e3e87e4SAndreas Gohr } 3204e3e87e4SAndreas Gohr } 3214e3e87e4SAndreas Gohr } 3224e3e87e4SAndreas Gohr 3234e3e87e4SAndreas Gohr // recursion 3244e3e87e4SAndreas Gohr if (!$this->traverseCheckAndCopy("$dir/$file", $dryrun)) { 3254e3e87e4SAndreas Gohr $ok = false; 3264e3e87e4SAndreas Gohr } 3274e3e87e4SAndreas Gohr } else { 3284e3e87e4SAndreas Gohr $fmd5 = md5(@file_get_contents($from)); 3294e3e87e4SAndreas Gohr $tmd5 = md5(@file_get_contents($to)); 3304e3e87e4SAndreas Gohr if ($fmd5 != $tmd5 || !file_exists($to)) { 3314e3e87e4SAndreas Gohr if ($dryrun) { 3324e3e87e4SAndreas Gohr // just check for writability 3334e3e87e4SAndreas Gohr if ((file_exists($to) && !is_writable($to)) || 3344e3e87e4SAndreas Gohr (!file_exists($to) && is_dir(dirname($to)) && !is_writable(dirname($to))) 3354e3e87e4SAndreas Gohr ) { 3364e3e87e4SAndreas Gohr 3374e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('tv_noperm') . '</b>', hsc("$dir/$file")); 3384e3e87e4SAndreas Gohr $ok = false; 3394e3e87e4SAndreas Gohr } else { 3404e3e87e4SAndreas Gohr $this->log('info', $this->getLang('tv_upd'), hsc("$dir/$file")); 3414e3e87e4SAndreas Gohr } 3424e3e87e4SAndreas Gohr } else { 3434e3e87e4SAndreas Gohr // check dir 3444e3e87e4SAndreas Gohr if (io_mkdir_p(dirname($to))) { 3454e3e87e4SAndreas Gohr // remove existing (avoid case sensitivity problems) 3464e3e87e4SAndreas Gohr if (file_exists($to) && !@unlink($to)) { 3474e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('tv_nodel') . '</b>', hsc("$dir/$file")); 3484e3e87e4SAndreas Gohr $ok = false; 3494e3e87e4SAndreas Gohr } 3504e3e87e4SAndreas Gohr // copy 3514e3e87e4SAndreas Gohr if (!copy($from, $to)) { 3524e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('tv_nocopy') . '</b>', hsc("$dir/$file")); 3534e3e87e4SAndreas Gohr $ok = false; 3544e3e87e4SAndreas Gohr } else { 3554e3e87e4SAndreas Gohr $this->log('info', $this->getLang('tv_done'), hsc("$dir/$file")); 3564e3e87e4SAndreas Gohr } 3574e3e87e4SAndreas Gohr } else { 3584e3e87e4SAndreas Gohr $this->log('error', '<b>' . $this->getLang('tv_nodir') . '</b>', hsc("$dir")); 3594e3e87e4SAndreas Gohr $ok = false; 3604e3e87e4SAndreas Gohr } 3614e3e87e4SAndreas Gohr } 3624e3e87e4SAndreas Gohr } 3634e3e87e4SAndreas Gohr } 3644e3e87e4SAndreas Gohr } 3654e3e87e4SAndreas Gohr closedir($dh); 3664e3e87e4SAndreas Gohr return $ok; 3674e3e87e4SAndreas Gohr } 3684e3e87e4SAndreas Gohr 3694e3e87e4SAndreas Gohr // region utilities 3704e3e87e4SAndreas Gohr 3714e3e87e4SAndreas Gohr /** 3724e3e87e4SAndreas Gohr * Figure out the release date from the version string 3734e3e87e4SAndreas Gohr * 3744e3e87e4SAndreas Gohr * @param $version 3754e3e87e4SAndreas Gohr * @return int|string returns 0 if the version can't be read 3764e3e87e4SAndreas Gohr */ 3774e3e87e4SAndreas Gohr protected function dateFromVersion($version) 3784e3e87e4SAndreas Gohr { 3794e3e87e4SAndreas Gohr if (preg_match('/(^|\D)(\d\d\d\d-\d\d-\d\d)(\D|$)/i', $version, $m)) { 3804e3e87e4SAndreas Gohr return $m[2]; 3814e3e87e4SAndreas Gohr } 3824e3e87e4SAndreas Gohr return 0; 3834e3e87e4SAndreas Gohr } 3844e3e87e4SAndreas Gohr 3854e3e87e4SAndreas Gohr /** 3864e3e87e4SAndreas Gohr * Recursive delete 3874e3e87e4SAndreas Gohr * 3884e3e87e4SAndreas Gohr * @author Jon Hassall 3894e3e87e4SAndreas Gohr * @link http://de.php.net/manual/en/function.unlink.php#87045 3904e3e87e4SAndreas Gohr */ 3914e3e87e4SAndreas Gohr protected function recursiveDelete($dir) 3924e3e87e4SAndreas Gohr { 3934e3e87e4SAndreas Gohr if (!$dh = @opendir($dir)) { 3944e3e87e4SAndreas Gohr return false; 3954e3e87e4SAndreas Gohr } 3964e3e87e4SAndreas Gohr while (false !== ($obj = readdir($dh))) { 3974e3e87e4SAndreas Gohr if ($obj == '.' || $obj == '..') continue; 3984e3e87e4SAndreas Gohr 3994e3e87e4SAndreas Gohr if (!@unlink($dir . '/' . $obj)) { 4004e3e87e4SAndreas Gohr $this->recursiveDelete($dir . '/' . $obj); 4014e3e87e4SAndreas Gohr } 4024e3e87e4SAndreas Gohr } 4034e3e87e4SAndreas Gohr closedir($dh); 4044e3e87e4SAndreas Gohr return @rmdir($dir); 4054e3e87e4SAndreas Gohr } 4064e3e87e4SAndreas Gohr 4074e3e87e4SAndreas Gohr /** 4084e3e87e4SAndreas Gohr * Log a message 4094e3e87e4SAndreas Gohr * 4104e3e87e4SAndreas Gohr * @param string ...$level , $msg 4114e3e87e4SAndreas Gohr */ 4124e3e87e4SAndreas Gohr protected function log() 4134e3e87e4SAndreas Gohr { 4144e3e87e4SAndreas Gohr $args = func_get_args(); 4154e3e87e4SAndreas Gohr $level = array_shift($args); 4164e3e87e4SAndreas Gohr $msg = array_shift($args); 4174e3e87e4SAndreas Gohr $msg = vsprintf($msg, $args); 4184e3e87e4SAndreas Gohr if ($this->logger) $this->logger->log($level, $msg); 4194e3e87e4SAndreas Gohr } 4204e3e87e4SAndreas Gohr 4214e3e87e4SAndreas Gohr // endregion 4224e3e87e4SAndreas Gohr} 423