<?php

/* -- BEGIN LICENSE BLOCK ----------------------------------
 *
 * This file is part of Dotclear 2.
 *
 * Copyright (c) 2003-2012 Olivier Meunier and Association Dotclear
 * Licensed under the GPL version 2.0 license.
 * See LICENSE file or
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 * -- END LICENSE BLOCK ------------------------------------
 */
class NetInstall
{
    public static function init()
    {
        error_reporting(E_ALL & ~E_NOTICE);

        if (function_exists('date_default_timezone_set')) {
            date_default_timezone_set('UTC');
        } elseif (!ini_get('safe_mode') && function_exists('putenv')) {
            putenv('TZ=UTC');
        }

        define('DC_LOADER_VERSION', '1.1');

        define('DC_LOADER_SERVICE', 'https://download.dotclear.org/loader/static/');
        define('DC_LOADER_ARCHIVE', 'https://download.dotclear.org/latest.zip');
        define('DC_LOADER_LANG', self::getLanguage());
    }

    public static function __($str)
    {
        if (!isset($GLOBALS['__l10n'])) {
            return $str;
        }
        if (array_key_exists($str, $GLOBALS['__l10n']) && $GLOBALS['__l10n'][$str] <> '') {
            return $GLOBALS['__l10n'][$str];
        }

        return $str;
    }

    public static function getLanguage($default = 'en')
    {
        if (!empty($_REQUEST['lang'])) {
            return $_REQUEST['lang'];
        }
        if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            $languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
            $l         = explode(';', $languages[0]);
            $dlang     = substr(trim($l[0]), 0, 2);

            return strtolower($dlang);
        }

        return $default;
    }

    public static function fetchRemote($src, &$dest, $step = 0) # Rudimentary HTTP client
    {
        if ($step > 3) {
            return false;
        }

        $src    = parse_url($src);
        $host   = $src['host'];
        $path   = $src['path'];
        $port   = $src['scheme'] == 'https' ? 443 : 80;
        $scheme = $src['scheme'] == 'https' ? 'ssl://' : '';

        if (($s = @fsockopen($scheme . $host, $port, $errno, $errstr, 5)) === false) {
            return false;
        }

        fwrite(
            $s,
            'GET ' . $path . " HTTP/1.0\r\n"
            . 'Host: ' . $host . "\r\n"
            . "User-Agent: Dotclear Net Install\r\n"
            . "Accept: text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*\r\n"
            . "\r\n"
        );

        $i          = 0;
        $in_content = false;
        while (!feof($s)) {
            $line = fgets($s, 4096);

            if (rtrim($line, "\r\n") == '' && !$in_content) {
                $in_content = true;
                $i++;

                continue;
            }

            if ($i == 0) {
                if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', rtrim($line, "\r\n"), $m)) {
                    fclose($s);

                    return false;
                }
                $status = (int) $m[2];
                if ($status < 200 || $status >= 400) {
                    fclose($s);

                    return false;
                }
            }

            if (!$in_content) {
                if (preg_match('/Location:\s+?(.+)$/', rtrim($line, "\r\n"), $m)) {
                    fclose($s);

                    return self::fetchRemote(trim($m[1]), $dest, $step + 1);
                }
                $i++;

                continue;
            }

            if (is_resource($dest)) {
                fwrite($dest, $line);
            } else {
                $dest .= $line;
            }

            $i++;
        }

        fclose($s);

        return true;
    }

    public static function getLocation()
    {
        $server_name = explode(':', $_SERVER['HTTP_HOST']);
        $server_name = $server_name[0];
        if ($_SERVER['SERVER_PORT'] == '443') {
            $scheme = 'https';
            $port   = '';
        } elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
            $scheme = 'https';
            $port   = ($_SERVER['SERVER_PORT'] != '443') ? ':' . $_SERVER['SERVER_PORT'] : '';
        } else {
            $scheme = 'http';
            $port   = ($_SERVER['SERVER_PORT'] != '80') ? ':' . $_SERVER['SERVER_PORT'] : '';
        }

        $loc = preg_replace('#(\\\|/)$#', '', dirname($_SERVER['SCRIPT_NAME']));

        return $scheme . '://' . $server_name . $port . $loc . '/';
    }

    public static function openPage()
    {
        header('Content-Type: text/html; charset=UTF-8');
        echo
        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" ' .
        ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . "\n" .
        '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . DC_LOADER_LANG . '" lang="' . DC_LOADER_LANG . '">' . "\n" .
        "<head>\n" .
        '  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' . "\n" .
        '  <title>' . __('Dotclear NetInstall') . '</title>' . "\n" .
        '  <meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />' . "\n" .
        '  <link rel="stylesheet" type="text/css" media="screen" href="' . DC_LOADER_SERVICE . 'install.css" />' . "\n" .
        '</head>' . "\n" .
        '<body  id="dotclear-admin" class="install">' . "\n" .
        '<div id="content">' . "\n" .
        '<h1>' . __('Dotclear NetInstall') . '</h1>' . "\n" .
        '<div id="main">' . "\n";
    }

    public static function closePage()
    {
        echo
        '</div>' . "\n" .
        '</div>' . "\n" .
        '</body>' . "\n" .
        '</html>';
    }

    public static function cleanFiles()
    {
        @unlink(dirname(__FILE__) . '/dcl_files.php');
        @unlink(dirname(__FILE__) . '/dcl_unzip.php');
        @unlink(dirname(__FILE__) . '/dotclear-install.zip');
    }

    public static function grabFiles()
    {
        $failed    = true;
        $lib_files = @fopen(dirname(__FILE__) . '/dcl_files.php', 'wb');
        $lib_unzip = @fopen(dirname(__FILE__) . '/dcl_unzip.php', 'wb');
        $dc_zip    = @fopen(dirname(__FILE__) . '/dotclear-install.zip', 'wb');

        if (!$lib_files || !$lib_unzip || !$dc_zip) {
            return false;
        }

        if (self::fetchRemote(DC_LOADER_SERVICE . 'lib.files.php', $lib_files)) {
            if (self::fetchRemote(DC_LOADER_SERVICE . 'class.unzip.php', $lib_unzip)) {
                if (self::fetchRemote(DC_LOADER_ARCHIVE, $dc_zip)) {
                    $failed = false;
                }
            }
        }

        fclose($lib_files);
        fclose($lib_unzip);
        fclose($dc_zip);

        if ($failed) {
            self::cleanFiles();

            return false;
        }

        return true;
    }

    public static function writeMessage($level, $title, $lines)
    {
        if (empty($lines)) {
            return;
        }

        echo
        '<div class="msg1">' .
        '<h2>' . $title . '</h2>';
        $level = ' class=' . $level;
        foreach ($lines as $line) {
            echo '<p' . $level . '>' . $line . '</p>';
            $level = '';
        }
        echo '</div>';
    }

    public static function nextAction($label, $step, $more = '')
    {
        echo
        '<form action="' . $_SERVER['SCRIPT_NAME'] . '" method="post">' .
        $more .
        '<p><input type="hidden" name="step" value="' . $step . '" />' .
        '<input type="hidden" name="lang" value="' . DC_LOADER_LANG . '" />' .
        '<input type="submit" name="submit" value="' . $label . '" />' .
        '</p></form>';
    }

    public static function initializeL10n()
    {
        NetInstall::fetchRemote('https://download.dotclear.org/loader/static/l10n/' . DC_LOADER_LANG . '.php', $l10n);
        NetInstall::fetchRemote('https://download.dotclear.org/loader/static/l10n/' . DC_LOADER_LANG . '.md5', $md5);
        if (($l10n) && (md5((string) $l10n) === trim((string) $md5) || trim((string) $md5) === 'debug')) {
            eval('?>' . $l10n);
        }
    }
}

function __($str)
{
    return NetInstall::__($str);
}

NetInstall::init();

$can_write = is_writable(dirname(__FILE__));
$can_fetch = false;
if ((bool) ini_get('allow_url_fopen')) {
    $can_fetch = true;
}
if (function_exists('curl_init')) {
    $can_fetch = true;
    define('DC_LOADER_CURL', true);
}

$step   = !empty($_REQUEST['step']) ? (int) $_REQUEST['step'] : 1;
$php_ok = (!version_compare(phpversion(), '8.2', '<'));

if (!$php_ok) {
    $step = 1;
}

if (!$can_fetch) {
    NetInstall::openPage();
    echo
    '<h2>' . __('NetInstall') . '</h2>' . "\n";
    NetInstall::writeMessage('warning', __('Damnit!'), [
        __('Due to restrictions in your PHP configuration, NetInstall cannot get its job done.'),
        sprintf(
            __('Please see the <a href="%s">Dotclear documentation</a> to perform a normal installation.'),
            __('https://dotclear.org/documentation/2.0/admin/install')
        ),
        __('Really sorry for the inconvenience.'),
    ]);
    NetInstall::closePage();
    exit;
}

NetInstall::initializeL10n();

switch ($step) {
    case 1: {
        NetInstall::openPage();
        echo
        '<h2>' . __('Welcome to NetInstall') . '</h2>' . "\n" .
        '<p>' . __('This tool is meant to retrieve the latest Dotclear 2 archive and unzip it in your webspace.') . '</p>' .
        '<p>' . __('Right after then, you will be redirect to the Dotclear 2 Setup Wizard.') . '</p>';

        if (!$can_write) {
            NetInstall::writeMessage('message', __('Write access is needed'), [
                __('It looks like NetInstall wont be able to write in the current directory, and this is required to follow on.'),
                __('Please try to change the permissions to allow write access, then reload this page by hitting the Refresh button.'),
            ]);
            NetInstall::nextAction(__('Refresh'), 1);
        } elseif (!$php_ok) {
            NetInstall::writeMessage('message', __('PHP 8.1+ is required'), [
                sprintf(__('It appears your webhost is currently running PHP %s.'), PHP_VERSION),
                __('Please try to change the PHP version to allow installation, then reload this page by hitting the Refresh button.'),
            ]);
            NetInstall::nextAction(__('Refresh'), 1);
        } else {
            NetInstall::nextAction(
                __('Retrieve and unzip Dotclear'),
                2,
                '<p><label for="destination">' . __('Destination:') . '</label> ' .
                NetInstall::getLocation() .
                '<input type="text" id="destination" name="destination" ' .
                'value="dotclear" size="15" maxlength="100" /></p>'
            );
        }
        NetInstall::closePage();

        break;
    }
    case 2: {
        $msg   = [__('WTF are you doing here that way?!')];
        $level = 'error';
        $text  = '';
        if (!empty($_POST['submit']) && isset($_POST['destination'])) {
            $msg  = [];
            $dest = preg_replace('/[^A-Za-z0-9_\/-]/', '', $_POST['destination']);
            $dest = preg_replace('#/+#', '/', $dest);

            if (file_exists(dirname(__FILE__) . '/./' . $dest . '/inc/config.php') || file_exists(dirname(__FILE__) . '/./' . $dest . '/conf/dotclear.ini')) {
                $level = 'message';
                $msg[] = __('It seems like a previous Dotclear installation is still sitting in that space.');
                $msg[] = __('You need to rename or remove it before we can go further...');
            } elseif (NetInstall::grabFiles()) {
                $lib_files = dirname(__FILE__) . '/dcl_files.php';
                $lib_unzip = dirname(__FILE__) . '/dcl_unzip.php';
                $dc_zip    = dirname(__FILE__) . '/dotclear-install.zip';
                if (!file_exists($lib_files) || !file_exists($lib_unzip) || !file_exists($dc_zip)) {
                    $msg[] = __('Files needed for this automatic installation could not be downloaded.');
                    $msg[] = sprintf(
                        __('Please see the <a href="%s">Dotclear documentation</a> to perform a normal installation.'),
                        __('https://dotclear.org/documentation/2.0/admin/install')
                    );
                    $msg[] = __('Really sorry for the inconvenience.');
                }

                require $lib_files;
                require $lib_unzip;
                $uz    = new fileUnzip($dc_zip);
                $files = $uz->getList();
                if (count($files) == 0) {
                    $msg[] = __('The integrity of the downloaded archive could not be verified.');
                    $msg[] = sprintf(
                        __('Please see the <a href="%s">Dotclear documentation</a> to perform a normal installation.'),
                        __('https://dotclear.org/documentation/2.0/admin/install')
                    );
                    $msg[] = __('Really sorry for the inconvenience.');
                }

                foreach ($files as $k => $v) {
                    if ($v['is_dir']) {
                        continue;
                    }
                    $t = preg_replace('#^dotclear/#', './' . $dest . '/', $k);
                    $uz->unzip($k, $t);
                }

                if (!is_dir(dirname(__FILE__) . '/./' . $dest)) {
                    $msg[] = __('The downloaded archive file could not be extracted.');
                    $msg[] = sprintf(
                        __('Please see the <a href="%s">Dotclear documentation</a> to perform a normal installation.'),
                        __('https://dotclear.org/documentation/2.0/admin/install')
                    );
                    $msg[] = __('Really sorry for the inconvenience.');
                } else {
                    # Remove files, create public directory, and self-destruction
                    files::makeDir('./' . $dest . '/public');
                    NetInstall::cleanFiles();

                    $redir = preg_replace('#/+#', '/', dirname($_SERVER['SCRIPT_NAME']) . '/' . $dest . '/admin/install/index.php');

                    header('Location: ' . $redir);
                }
            } else {
                $msg[] = __('An error occurred while grabbing the necessary files to go on.');
                $msg[] = sprintf(
                    __('Please see the <a href="%s">Dotclear documentation</a> to perform a normal installation.'),
                    __('https://dotclear.org/documentation/2.0/admin/install')
                );
                $msg[] = __('Really sorry for the inconvenience.');
            }
        }
        NetInstall::openPage();
        NetInstall::writeMessage($level, __('Something went wrong ...'), $msg);
        echo $text;
        NetInstall::closePage();

        break;
    }
}
