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