xref: /plugin/upgrade/helper.php (revision 4cefc1466e60f057698ac64dc9bcf2c3d38221d5)
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