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