<?php
/**
 * Automatic updater for phpList 3
 *
 * @author Xheni Myrtaj <xheni@phplist.com>
 */

class UpdateException extends \Exception
{
}

class updater
{
    /** @var bool */
    private $availableUpdate = false;
    const DOWNLOAD_PATH = '../tmp_uploaded_update';
    const ELIGIBLE_SESSION_KEY = 'phplist_updater_eligible';
    private $excludedFiles = array(
        'dl.php',
        'index.php',
        'index.html',
        'lt.php',
        'ut.php',
        'api.php',
    );
    private $database_host;
    private $database_name;
    private $database_user;
    private $database_password;
    private $database_port;
    private $database_socket;
    private $table_prefix;

    public function __construct()
    {
        if (isset($_SERVER['ConfigFile']) && is_file($_SERVER['ConfigFile'])) {
            include $_SERVER['ConfigFile'];
        } elseif (file_exists($f = $this->getConfigFilePath())) {
            include $f;
        } else {
            throw new \UpdateException("Error: Cannot find config file");
        }

        $this->database_host = $database_host;
        $this->database_name = $database_name;
        $this->database_user = $database_user;
        $this->database_password = $database_password;
        $this->database_port = isset($database_port) ? $database_port : null;
        $this->database_socket = isset($database_socket) ? $database_socket : null;
        $this->table_prefix = isset($table_prefix) ? $table_prefix : 'phplist_';
    }

    public function isAuthenticated()
    {

        ini_set('session.name','phpListSession');
        ini_set('session.cookie_samesite','Strict');
        ini_set('session.use_only_cookies',1);
        ini_set('session.cookie_httponly',1);        session_start();
        if (isset($_SESSION[self::ELIGIBLE_SESSION_KEY]) && $_SESSION[self::ELIGIBLE_SESSION_KEY] === true) {
            return true;
        }

        return false;
    }

    public function deauthUpdaterSession()
    {
        unset($_SESSION[self::ELIGIBLE_SESSION_KEY]);
        unlink(__DIR__ . '/../config/actions.txt');
    }

    /**
     * Return true if there is an update available
     * @return bool
     */
    public function availableUpdate()
    {
        return $this->availableUpdate;
    }

    /**
     * Returns current version of phpList.
     *
     * @return string
     * @throws UpdateException
     */
    public function getCurrentVersion()
    {
        $table_name = $this->table_prefix . 'config';
        $prepStmt = $this->getConnection()->prepare("SELECT value FROM {$table_name} WHERE item=?");
        $prepStmt->execute(['version']);
        $result = $prepStmt->fetch(PDO::FETCH_ASSOC);

        if ($result === false) {
            throw new UpdateException('No production version found.');
        }

        return $result['value'];
    }

    /**
     * Checks if there is an Update Available
     * @return string
     * @throws \Exception
     */
    function checkIfThereIsAnUpdate()
    {
        $serverResponse = $this->getResponseFromServer();

        if (isset($serverResponse['version']) && isset($serverResponse['versionstring'])) {
            $version = $serverResponse['version'];
            $versionString = $serverResponse['versionstring'];

            if (version_compare($this->getCurrentVersion(), $version) < 0) {
                $this->availableUpdate = true;
                $updateMessage = 'Update to ' . htmlentities($versionString) . ' is available.  ';

                if (isset($serverResponse['autoupdater'])
                    && !($serverResponse['autoupdater'] === 1 || $serverResponse['autoupdater'] === '1')) {
                    $this->availableUpdate = false;
                    $updateMessage .= '<br />The automatic updater is disabled for this update.';
                }
            } else {
                $updateMessage = 'phpList is up-to-date.';
            }
        } else {
            $updateMessage = 'Unable to identify new version';
        }

        return $updateMessage;

    }

    /**
     * Return version data from server
     * @return array
     * @throws \Exception
     */
    private function getResponseFromServer()
    {
        $serverUrl = "https://download.phplist.org/version.json";
        $updateUrl = $serverUrl . '?version=' . $this->getCurrentVersion();

        // create a new cURL resource
        $ch = curl_init();
        // set URL and other appropriate options
        // Disable SSL verification
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        // Will return the response, if false it print the response
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        // Set the url
        curl_setopt($ch, CURLOPT_URL, $updateUrl);
        // Execute
        $responseFromServer = curl_exec($ch);
        // Closing
        curl_close($ch);

        // decode json
        $responseFromServer = json_decode($responseFromServer, true);
        return $responseFromServer;
    }

    private function getDownloadUrl()
    {
        // todo: error handling
        $response = $this->getResponseFromServer();
        if (isset($response['url'])) {
            return $response['url'];
        }
        // todo error handling
    }


    /**
     * Checks write permissions and returns files that are not writable
     * @return array
     */
    function checkWritePermissions()
    {

        $directory = new \RecursiveDirectoryIterator(__DIR__ . '/../', \RecursiveDirectoryIterator::SKIP_DOTS); // Exclude dot files
        /** @var SplFileInfo[] $iterator */
        $iterator = new \RecursiveIteratorIterator($directory, RecursiveIteratorIterator::CHILD_FIRST);
        $files = array();
        foreach ($iterator as $info) {
            $path = $info->getRealPath();
            if (!is_writable($path) && !empty($path)) {
                $files[] = $path;
            }
        }
        return $files;
    }

    /**
     * @return array
     */
    function checkRequiredFiles()
    {
        $expectedFiles = array(
            '.' => 1,
            '..' => 1,
            'admin' => 1,
            'config' => 1,
            'images' => 1,
            'js' => 1,
            'styles' => 1,
            'texts' => 1,
            '.htaccess' => 1,
            'dl.php' => 1,
            'index.html' => 1,
            'index.php' => 1,
            'lt.php' => 1,
            'ut.php' => 1,
            'updater' => 1,
            'base' => 1,
            'api.php' =>1,
        );

        $existingFiles = scandir(__DIR__ . '/../');

        foreach ($existingFiles as $fileName) {

            if (isset($expectedFiles[$fileName])) {
                unset($expectedFiles[$fileName]);
            } else {
                $expectedFiles[$fileName] = 1;
            }
        }

        return $expectedFiles;

    }

    /**
     *
     * Recursively delete a directory and all of it's contents
     *
     * @param string $dir absolute path to directory to delete
     * @return bool
     * @throws UpdateException
     */

    private function rmdir_recursive($dir)
    {

        if (false === file_exists($dir)) {
            throw new \UpdateException("$dir doesn't exist.");
        }

        /** @var SplFileInfo[] $files */
        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::CHILD_FIRST
        );
        foreach ($files as $fileinfo) {
            if ($fileinfo->isDir()) {
                if (false === rmdir($fileinfo->getRealPath())) {
                    if (false === unlink($fileinfo)) {
                        throw new \UpdateException("Could not delete $fileinfo");
                    }
                }
            } else {
                if (false === unlink($fileinfo->getRealPath())) {
                    if (false === unlink($fileinfo)) {
                        throw new \UpdateException("Could not delete $fileinfo");
                    }
                }
            }
        }
        return rmdir($dir);
    }

    /**
     * Delete dirs/files except config and other files that we want to keep
     * @throws UpdateException
     */

    function deleteFiles()
    {

        $excludedFolders = array(
            'config',
            'tmp_uploaded_update',
            'updater',
            '.',
            '..',
        );

        $filesTodelete = scandir(__DIR__ . '/../');

        foreach ($filesTodelete as $fileName) {
            $absolutePath = __DIR__ . '/../' . $fileName;
            $is_dir = false;
            if (is_dir($absolutePath)) {
                $is_dir = true;
                if (in_array($fileName, $excludedFolders)) {
                    continue;
                }

            } else if (is_file($absolutePath)) {
                if (in_array($fileName, $this->excludedFiles)) {
                    continue;
                }

            }


            if ($is_dir) {
                $this->rmdir_recursive($absolutePath);
            } else {
                unlink($absolutePath);
            }
        }

    }

    /**
     * Get config file path
     * @return string
     */
    function getConfigFilePath()
    {
        return  __DIR__ . '/../config/config.php';
    }

    /**
     * Get a PDO connection
     * @return PDO
     * @throws UpdateException
     */
    function getConnection()
    {
        static $pdo = null;

        if ($pdo !== null) {
            return $pdo;
        }
        $charset = 'utf8mb4';
        $dsn = "mysql:dbname=$this->database_name;charset=$charset;";

        if ($this->database_socket !== null) {
            $dsn .= "socket=$this->database_socket";
        } else {
            $dsn .= "host=$this->database_host";

            if ($this->database_port !== null) {
                $dsn .=";port=$this->database_port";
            }
        }
        $options = array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        );
        try {
            $pdo = new PDO($dsn, $this->database_user, $this->database_password, $options);
        } catch (\PDOException $e) {
            throw new \PDOException($e->getMessage(), (int)$e->getCode());
        }

        return $pdo;
    }

    /**
     *Set the maintenance mode
     * @return bool true - maintenance mode is set; false - maintenance mode could not be set because an update is already running
     * @throws UpdateException
     */
    function addMaintenanceMode()
    {
        $table_name = $this->table_prefix . 'config';
        $prepStmt = $this->getConnection()->prepare("SELECT * FROM {$table_name} WHERE item=?");
        $prepStmt->execute(array('update_in_progress'));
        $result = $prepStmt->fetch(PDO::FETCH_ASSOC);
        if ($result === false) {
            // the row does not exist => no update running
            $this->getConnection()
                ->prepare("INSERT INTO {$table_name}(`item`,`editable`,`value`) VALUES (?,0,?)")
                ->execute(array('update_in_progress', 1));
        } elseif ($result['value'] == 0) {
            $this->getConnection()
                ->prepare("UPDATE {$table_name} SET `value`=? WHERE `item`=?")
                ->execute(array(1, 'update_in_progress'));
        } else {
            // the row exists and is not 0 => there is an update running
            return false;
        }
        $name = 'maintenancemode';
        $value = "Update process";
        $sql = "UPDATE {$table_name} SET value =?, editable =? where item =? ";
        $this->getConnection()->prepare($sql)->execute(array($value, 0, $name));
    }

    /**
     *Clear the maintenance mode and remove the update_in_progress lock
     * @throws UpdateException
     */
    function removeMaintenanceMode()
    {
        $table_name = $this->table_prefix . 'config';
        $name = 'maintenancemode';
        $value = '';
        $sql = "UPDATE {$table_name} SET value =?, editable =? where item =? ";
        $this->getConnection()->prepare($sql)->execute(array($value, 0, $name));
        $this->getConnection()
            ->prepare("UPDATE {$table_name} SET `value`=? WHERE `item`=?")
            ->execute(array(0, "update_in_progress"));
    }

    /**
     * Download and unzip phpList from remote server
     *
     * @throws UpdateException
     */
    function downloadUpdate()
    {
        /** @var string $url */
        $url = $this->getDownloadUrl();
        $zipFile = tempnam(sys_get_temp_dir(), 'phplist-update');
        if ($zipFile === false) {
            throw new UpdateException("Error: Temporary file cannot be created");
        }
        // Get The Zip File From Server
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, true);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_AUTOREFERER, true);
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_FILE, fopen($zipFile, 'w+'));
        $page = curl_exec($ch);
        if (!$page) {
            throw new \UpdateException('Error: ' . curl_error($ch));
        }
        curl_close($ch);

        // extract files
        $this->unZipFiles($zipFile, self::DOWNLOAD_PATH);

    }

    /**
     * Creates temporary dir
     * @throws UpdateException
     */
    function temp_dir()
    {

        $tempdir = mkdir(self::DOWNLOAD_PATH, 0700);
        if ($tempdir === false) {
            throw new UpdateException("Error: Could not create temporary file");
        }
    }


    function cleanUp()
    {
        if (function_exists('opcache_reset')) {
            opcache_reset();
        }
    }

    /**
     * @throws UpdateException
     */
    function replacePHPEntryPoints()
    {
        $entryPoints = array(
            'dl.php',
            'index.html',
            'index.php',
            'lt.php',
            'ut.php',
        );

        foreach ($entryPoints as $key => $fileName) {
            $current = "Update in progress \n";
            $content = file_put_contents(__DIR__ . '/../' . $fileName, $current);
            if ($content === FALSE) {
                throw new UpdateException("Error: Could not write to the $fileName");
            }
        }

    }

    /**
     * Returns true if the file/dir is excluded otherwise false.
     * @param $file
     * @return bool
     */
    function isExcluded($file)
    {

        $excludedFolders = array(
            'config',
            'tmp_uploaded_update',
            'updater',
            '.',
            '..',
        );


        if (in_array($file, $excludedFolders)) {
            return true;
        } else if (in_array($file, $this->excludedFiles)) {
            return true;
        }
        return false;
    }

    /**
     * Move new files in place.
     * @throws UpdateException
     */
    function moveNewFiles()
    {
        $rootDir = __DIR__ . '/../tmp_uploaded_update/phplist/public_html/lists';
        $downloadedFiles = scandir($rootDir);
        if (count($downloadedFiles) <= 2) {
            throw new UpdateException("Error: Download folder is empty!");
        }

        foreach ($downloadedFiles as $fileName) {
            if ($this->isExcluded($fileName)) {
                continue;
            }
            $oldFile = $rootDir . '/' . $fileName;
            $newFile = __DIR__ . '/../' . $fileName;
            $state = rename($oldFile, $newFile);
            if ($state === false) {
                throw new UpdateException("Error: Could not move new files");
            }
        }
    }

    /**
     * Move entry points in place.
     */
    function moveEntryPHPpoints()
    {
        $rootDir = __DIR__ . '/../tmp_uploaded_update/phplist/public_html/lists';
        $downloadedFiles = scandir($rootDir);

        foreach ($downloadedFiles as $filename) {
            $oldFile = $rootDir . '/' . $filename;
            $newFile = __DIR__ . '/../' . $filename;
            if (in_array($filename, $this->excludedFiles)) {
                rename($oldFile, $newFile);
            }
        }

    }


    /**
     *  Back up old files to the location specified by the user.
     * @param $destination 'path' to backup zip
     * @throws UpdateException
     */
    function backUpFiles($destination)
    {
        $iterator = new \RecursiveDirectoryIterator(realpath(__DIR__ . '/../'), FilesystemIterator::SKIP_DOTS);
        /** @var SplFileInfo[] $iterator */
        /** @var  $iterator */
        $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST);

        $zip = new ZipArchive();
        $resZip = $zip->open($destination, ZipArchive::CREATE);
        if ($resZip === false) {
            throw new \UpdateException("Error: Could not create back up of phpList directory. Please make sure that the argument is valid or writable and try again without reloading the page.");
        }
        $zip->addEmptyDir('lists');

        foreach ($iterator as $file) {
            $prefix = realpath(__DIR__ . '/../');
            $name = 'lists/' . substr($file->getRealPath(), strlen($prefix) + 1);
            if ($file->isDir()) {
                $zip->addEmptyDir($name);
                continue;
            }
            if ($file->isFile()) {
                $zip->addFromString($name, file_get_contents($file->getRealPath()));
                continue;
            }
        }
        $state = $zip->close();
        if ($state === false) {
            throw new UpdateException('Error: Could not create back up of phpList directory. Please make sure that the argument is valid or is writable and try again without reloading the page.');
        }

    }

    /**
     * Extract Zip Files
     * @param string $toBeExtracted
     * @param string $extractPath
     * @throws UpdateException
     */
    function unZipFiles($toBeExtracted, $extractPath)
    {
        $zip = new ZipArchive;

        /* Open the Zip file */
        if ($zip->open($toBeExtracted) !== true) {
            throw new \UpdateException("Error: Unable to open the Zip File");
        }
        /* Extract Zip File */
        if (!$zip->extractTo($extractPath)) {
            throw new \UpdateException("Error: Unable to extract the Zip File");
        }
        $zip->close();

    }

    /**
     * Delete temporary downloaded files
     * @throws UpdateException
     */
    function deleteTemporaryFiles()
    {
        $isTempDirDeleted = $this->rmdir_recursive(self::DOWNLOAD_PATH);
        if ($isTempDirDeleted === false) {
            throw new \UpdateException("Error: Could not delete temporary files!");
        }

    }

    /**
     * @throws UpdateException
     */
    function recoverFiles()
    {
        $this->unZipFiles('backup.zip', self::DOWNLOAD_PATH);
    }

    /**
     * @param int $action
     * @throws UpdateException
     */
    function writeActions($action)
    {
        $actionsdir = __DIR__ . '/../config/actions.txt';
        if (!file_exists($actionsdir)) {
            $actionsFile = fopen($actionsdir, "w+");
            if ($actionsFile === false) {
                throw new \UpdateException("Error: Could not create actions file in the config directory, please change permissions");
            }
        }
        $written = file_put_contents($actionsdir, json_encode(array('continue' => false, 'step' => $action)));
        if ($written === false) {
            throw new \UpdateException("Error: Could not write on $actionsdir");
        }
    }

    /**
     * Return the current step
     * @return mixed array of json data
     * @throws UpdateException
     */
    function currentUpdateStep()
    {
        $actionsdir = __DIR__ . '/../config/actions.txt';
        if (file_exists($actionsdir)) {
            $status = file_get_contents($actionsdir);
            if ($status === false) {
                throw new \UpdateException("Cannot read content from $actionsdir");
            }
            $decodedJson = json_decode($status, true);
            if (!is_array($decodedJson)) {
                throw new \UpdateException('JSON data cannot be decoded!');
            }

        } else {
            return array('step' => 0, 'continue' => true);
        }
        return $decodedJson;

    }

    /**
     * Check if config folder is writable. Required to be writable in order to write steps.
     */
    function checkConfig()
    {
        $configdir = __DIR__ . '/../config/';
        if (!is_dir($configdir) || !is_writable($configdir)) {
            die("Cannot update because config directory is not writable.");
        }
    }

    /**
     * Check if required php modules are installed.
     */
    function checkphpmodules()
    {

        $phpmodules = array('curl', 'pdo', 'zip');
        $notinstalled = array();

        foreach ($phpmodules as $value) {
            if (!extension_loaded($value)) {
                array_push($notinstalled, $value);
            }
        }
        if (count($notinstalled) > 0) {
            $message = "The following php modules are required. Please install them to continue." . '<br>';
            foreach ($notinstalled as $value) {
                $message .= $value . '<br>';
            }
            die($message);
        }
    }

    /**
     * Move plugins in temporary folder to prevent them from being overwritten.
     * @throws UpdateException
     */
    function movePluginsInTempFolder()
    {
        $oldDir = __DIR__ . '/../admin/plugins';
        $newDir = __DIR__ . '/../tmp_uploaded_update/tempplugins';
        $state = rename($oldDir, $newDir);
        if ($state === false) {
            throw new UpdateException("Could not move plugins directory");
        }
    }

    /**
     * Move any additional plugin files and directories back to the admin directory.
     * @throws UpdateException
     */
    function movePluginsInPlace()
    {
        $oldDir = realpath(__DIR__ . '/../tmp_uploaded_update/tempplugins');
        $newDir = realpath(__DIR__ . '/../admin/plugins');

        $existingPluginFiles = scandir($oldDir);
        $newPluginFiles = scandir($newDir);
        $additional = array_diff($existingPluginFiles, $newPluginFiles);

        foreach ($additional as $file) {
            $state = rename("$oldDir/$file", "$newDir/$file");

            if ($state === false) {
                throw new UpdateException("Could not restore plugin $file.");
            }
        }
    }

    /**
     * Update updater to a new location before temp folder is deleted!
     * @throws UpdateException
     */
    function moveUpdater()
    {
        $rootDir = __DIR__ . '/../tmp_uploaded_update/phplist/public_html/lists';
        $oldFile = $rootDir . '/updater';
        $newFile = __DIR__ . '/../tempupdater';
        $state = rename($oldFile, $newFile);
        if ($state === false) {
            throw new UpdateException("Could not move updater");
        }
    }

    /**
     * Replace new updater as the final step
     * @throws UpdateException
     */
    function replaceNewUpdater()
    {
        $newUpdater = realpath(__DIR__ . '/../tempupdater');
        $oldUpdater = realpath(__DIR__ . '/../updater');

        $this->rmdir_recursive($oldUpdater);
        $state = rename($newUpdater, $oldUpdater);
        if ($state === false) {
            throw new UpdateException("Could not move the new updater in place");
        }
    }
}

try {
    $update = new updater();
    if (!$update->isAuthenticated()) {
        die('No permission to access updater.');
    }
    $update->checkConfig();
    $update->checkphpmodules();

} catch (\Exception $e) {
    die($e->getMessage());
}

/**
 *
 *
 *
 */
if (isset($_POST['action'])) {
    try {
        set_time_limit(0);

        //ensure that $action is integer

        $action = (int)$_POST['action'];

        header('Content-Type: application/json');
        $writeStep = true;
        switch ($action) {
            case 0:
                $statusJson = $update->currentUpdateStep();
                echo json_encode(array('status' => $statusJson, 'autocontinue' => true));
                break;
            case 1:
                $updateMessage = $update->checkIfThereIsAnUpdate();
                $isThereAnUpdate = $update->availableUpdate();
                if ($isThereAnUpdate === false) {
                    echo(json_encode(array('continue' => false, 'response' => $updateMessage)));
                } else {
                    echo(json_encode(array('continue' => true, 'response' => $updateMessage)));
                }
                break;
            case 2:
                echo(json_encode(array('continue' => true, 'autocontinue' => true, 'response' => 'Starting integrity check')));
                break;
            case 3:
                $unexpectedFiles = $update->checkRequiredFiles();
                if (count($unexpectedFiles) !== 0) {
                    $elements = "Error: The following files are either not expected and should be removed, or are missing but required and should be put back in place \n";
                    foreach ($unexpectedFiles as $key => $fileName) {
                        $elements .= $key . "\n";
                    }
                    echo(json_encode(array('retry' => true, 'continue' => false, 'response' => $elements)));
                } else {
                    echo(json_encode(array('continue' => true, 'response' => 'Integrity check successful', 'autocontinue' => true)));
                }
                break;
            case 4:
                $notWriteableFiles = $update->checkWritePermissions();
                if (count($notWriteableFiles) !== 0) {
                    $notWriteableElements = "Error: No write permission for the following files: \n";;
                    foreach ($notWriteableFiles as $key => $fileName) {
                        $notWriteableElements .= $fileName . "\n";
                    }
                    echo(json_encode(array('retry' => true, 'continue' => false, 'response' => $notWriteableElements)));
                } else {
                    echo(json_encode(array('continue' => true, 'response' => 'Write check successful.', 'autocontinue' => true)));
                }
                break;
            case 5:
                echo(json_encode(array('continue' => true, 'response' => 'Do you want a backup? <form><input type="radio" name="create_backup" value="true">Yes<br><input type="radio" name="create_backup" value="false" checked>No</form>')));
                break;
            case 6:
                $createBackup = $_POST['create_backup'];
                if ($createBackup === 'true') {
                    echo(json_encode(array('continue' => true, 'response' => 'Choose location where to backup the /lists directory. Please make sure to choose a location outside the web root:<br> <form onsubmit="return false;"><input type="text" id="backuplocation" size="55" name="backup_location" placeholder="/var/backup.zip" /></form>')));
                } else {
                    echo(json_encode(array('continue' => true, 'response' => '', 'autocontinue' => true)));
                }
                break;
            case 7:
                $createBackup = $_POST['create_backup'];
                if ($createBackup === 'true') {
                    $backupLocation = realpath(dirname($_POST['backup_location']));
                    $phplistRootFolder = realpath(__DIR__ . '/../../');
                    if (strpos($backupLocation, $phplistRootFolder) === 0) {
                        echo(json_encode(array('retry' => true, 'continue' => false, 'response' => 'Error: Please choose a folder outside of your phpList installation.')));
                        break;
                    }
                    if (!preg_match("/^.*\.(zip)$/i", $_POST['backup_location'])) {
                        echo(json_encode(array('retry' => true, 'continue' => false, 'response' => 'Error: Please add .zip extension.')));
                        break;
                    }
                    try {
                        $update->backUpFiles($_POST['backup_location']);
                        echo(json_encode(array('continue' => true, 'response' => 'Backup has been created')));
                    } catch (\Exception $e) {
                        echo(json_encode(array('retry' => true, 'continue' => false, 'response' => $e->getMessage())));
                        break;
                    }
                } else {
                    echo(json_encode(array('continue' => true, 'response' => 'No back up created', 'autocontinue' => true)));
                }

                break;
            case 8:
                echo(json_encode(array('continue' => true, 'autocontinue' => true, 'response' => 'Download in progress')));
                break;
            case 9:
                try {
                    $update->downloadUpdate();
                    echo(json_encode(array('continue' => true, 'response' => 'The update has been downloaded!')));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 10:
                $on = $update->addMaintenanceMode();
                if ($on === false) {
                    echo(json_encode(array('continue' => false, 'response' => 'Cannot set the maintenance mode on!')));
                } else {
                    echo(json_encode(array('continue' => true, 'response' => 'Set maintenance mode on', 'autocontinue' => true)));
                }
                break;
            case 11:
                try {
                    $update->replacePHPEntryPoints();
                    echo(json_encode(array('continue' => true, 'response' => 'Replaced entry points', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 12:
                try {
                    $update->movePluginsInTempFolder();
                    echo(json_encode(array('continue' => true, 'response' => 'Backing up the plugins', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 13:
                try {
                    $update->deleteFiles();
                    echo(json_encode(array('continue' => true, 'response' => 'Old files have been deleted!', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 14:
                try {
                    $update->moveNewFiles();
                    echo(json_encode(array('continue' => true, 'response' => 'Moved new files in place!', 'autocontinue' => true)));

                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 15:
                try {
                    $update->movePluginsInPlace();
                    echo(json_encode(array('continue' => true, 'response' => 'Moved plugins in place!', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 16:
                try {
                    $update->moveEntryPHPpoints();
                    echo(json_encode(array('continue' => true, 'response' => 'Moved new entry points in place!', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 17:
                try {
                    $update->moveUpdater();
                    echo(json_encode(array('continue' => true, 'response' => 'Moved new entry points in place!', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 18:
                try {
                    $update->deleteTemporaryFiles();
                    echo(json_encode(array('continue' => true, 'response' => 'Deleted temporary files!', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 19:
                try {
                    $update->removeMaintenanceMode();
                    echo(json_encode(array('continue' => true, 'response' => 'Removed maintenance mode', 'autocontinue' => true)));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
            case 20:
                $writeStep = false;
                try {
                    $update->replaceNewUpdater();
                    $update->deauthUpdaterSession();
                    echo(json_encode(array('continue' => true, 'nextUrl' => '../admin/', 'response' => 'Updated successfully.')));
                } catch (\Exception $e) {
                    echo(json_encode(array('continue' => false, 'response' => $e->getMessage())));
                }
                break;
        }
    } catch (\Exception $e) {
        echo(json_encode(array('continue' => false, 'response' => 'Error: ' . $e->getMessage())));
    }

    if ($writeStep) {
        try {
            $update->writeActions($action - 1);
        } catch (\Exception $e) {

        }
    }
} else {
    ?>

    <html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">


        <style>
            /* http://meyerweb.com/eric/tools/css/reset/
               v2.0 | 20110126
               License: none (public domain)
            */
            html, body, div, span, applet, object, iframe,
            h1, h2, h3, h4, h5, h6, p, blockquote, pre,
            a, abbr, acronym, address, big, cite, code,
            del, dfn, em, img, ins, kbd, q, s, samp,
            small, strike, strong, sub, sup, tt, var,
            b, u, i, center,
            dl, dt, dd, ol, ul, li,
            fieldset, form, label, legend,
            table, caption, tbody, tfoot, thead, tr, th, td,
            article, aside, canvas, details, embed,
            figure, figcaption, footer, header, hgroup,
            menu, nav, output, ruby, section, summary,
            time, mark, audio, video {
                margin: 0;
                padding: 0;
                border: 0;
                font-size: 100%;
                font: inherit;
                vertical-align: baseline;
            }

            /* HTML5 display-role reset for older browsers */
            article, aside, details, figcaption, figure,
            footer, header, hgroup, menu, nav, section {
                display: block;
            }

            body {
                line-height: 1;
            }

            ol, ul {
                list-style: none;
            }

            blockquote, q {
                quotes: none;
            }

            blockquote:before, blockquote:after,
            q:before, q:after {
                content: '';
                content: none;
            }

            table {
                border-collapse: collapse;
                border-spacing: 0;
            }

            /** phpList CSS **/
            body {
                background-color: #FAFAFA;
                font-family: 'Source Sans Pro', sans-serif;
                margin-top: 50px;
            }

            button.right {
                background-color: #21AE8A;
                color: white;
                border-radius: 5px;
                height: 40px;
                padding-left: 30px;
                padding-right: 30px;
                font-size: 15px;
                text-transform: uppercase;
                margin-top: 20px;
                border: none;
                font-family: "Montserrat", SemiBold;
                font-weight:600;
            }

            button:disabled {
                background-color: lightgrey !important;
            }

            .right {
                float: right;
            }

            @media only screen and (min-width: 1200px) {
                #center {
                    margin: auto;
                    width: 70%;
                }
            }

            @media only screen and (max-width: 350px) {
                #steps {
                    width: 100% !important;
                }
            }

            @media only screen and (max-width: 800px) {
                #center {
                    width: 100%;
                }

                .divider {
                    visibility: hidden;
                }
            }

            @media only screen and (min-width: 800px) and (max-width: 1200px) {
                #center {
                    margin: auto;
                    width: 90%;
                }
            }

            @media only screen and (min-width: 1200px) and (max-width: 1400px) {

                #center {
                    margin: auto;
                    max-width: 75%;
                }

                #display {
                    max-width: 70%;
                    margin: 0 auto;
                }

            }

            @media only screen and (min-width: 890px) and (max-width: 1100px) {

                #container {
                    width: 80%;
                    margin: 0 auto;
                }
            }

            @media only screen and (min-width: 1101px) {

                #container {
                    width: 60%;
                    margin: 0 auto;
                }
            }

            @media only screen and (min-width: 700px) and (max-width: 889px) {

                #container {
                    width: 95%;
                    margin: 0 auto;
                }
            }


            #display {
                background-color: white;
                padding-left: 20px;
                padding-top: 20px;
                padding-bottom: 20px;
                border-radius: 12px;
                width: 80%;
                margin: 0 auto;
            }

            #logo {
                color: #8C8C8C;
                font-size: 20px;
                text-align: center;
                margin-bottom: 50px;
                cursor: pointer;
            }

            #logo img {
                margin-bottom: 20px;
            }

            #logo h1 {
                margin-top: 34px;
            }

            #steps h2 {
                font-size: 15px;
                color: #8C8C8C;
                width: 50%;
                text-align: center;
                margin-left: 6px;
                display: flex;
            }

            #steps {
                width: 64%;
                margin: auto;
                padding-bottom: 27px;
            }

            #first-step {
                width: calc((25% - 70px) / 2) !important;
                float: left;
                height: 1px;
            }

            .step {
                width: 25%;
                float: left;
            }

            .last-step {
                width: 70px;
            }

            .step-image {
                width: 64px;
                height: 64px;
                border-radius: 100px;
                margin-bottom: 12px;
                float: left;
                background-color: #fff;
                box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.14);
            }

            .step-image svg {
                width: 50%;
                padding-top: 32%;
                padding-left: 24%;
            }

            .active {
                background-color: #56A3D2;
                border: 0;
            }

            .active svg path {
                fill: white;
            }

            .clear {
                clear: both;
            }

            .divider {
                border-top: 1px dashed #253746;
                width: inherit;
                margin-top: 30px;
            }

            .hidden {
                display: none;
            }

            i {
                border: solid #ffffff;
                border-width: 0 2px 2px 0;
                display: inline-block;
                padding: 3px;
            }

            .option-heading:before {
                content: "\25bc";
            }

            .option-heading.is-active:before {
                content: "\25b2";
            }

            /* Helpers */
            .is-hidden {
                display: block;
                background-color: rgb(255, 255, 255);
            }

            #footer_updater {
                position: absolute;
                bottom: 0px;
                height: 20px;
            }

            .option-heading {
                color: #fff;
                background: #4B8CCA;
                /* padding: 3px 17px; */
                border-radius: 5px;
                width: 50px;
                height: 19px;
                padding-top: 7px;
                display: block;
            }

            ul li {
                color: #8A9798;
                margin-bottom: 8px;
                display: flex;
                font-size: 14px;
            }

            #pointsList span {
                font-family: 'Source Sans Pro', Light;
            }

            li.final {
                color: #4B8CCA;
                font-family: Montserrat, Sans-Serif;
                font-size: 24px;
                letter-spacing: 0.3px;
                margin-bottom: 9px;
            }

            li.migrate a {
                color: #253746;
                font-family: Montserrat, SemiBold;
                font-size: 18px;
                letter-spacing: 0.3px;
                margin-bottom: 20px;
                text-decoration: none;
            }

            #success-message {
                font-size: 14px;
                font-family: Source Sans Pro, Light;
                line-height: 22px;
                color: #2C2C2C;
            }

            #next-step {
                margin-top: 40px;
            }

            .outer {
                /*position: absolute;*/
                position: fixed;
                bottom: 0px;
                width: 100%;
                margin: 0px auto;
                text-align: center;
            }

            .inner {
                display: none;
                background-color: #fff;
                box-shadow: 9px 5px 6px 4px rgba(0, 0, 0, 0.16);
            }

            button.info-footer {
                background-color: #4B8CCA;
                color: white;
                margin-top: 0px;
                border: none;
                width: 50px;
                height: 25px;
                border-radius: 5px 5px 0px 0px;
                box-shadow: none;
            }

            #sqr {
                width:225px;
                height:180px;
                background-image: url(images/square.svg);
                background-repeat: no-repeat;
            }

            #triangle_down {
                width: 0;
                height: 0;
                border-top: 140px solid #20a3bf;
                border-left: 70px solid transparent;
                border-right: 70px solid transparent;
            }

            input.book {
                width: 90px;
                height: 30px;
                border: 1px dashed #21AE8A;
                background: #fff;
                margin: 0 auto;
                color: #21AE8A;
                text-transform: uppercase;
                font-size: 12px;
                font-weight: 600;
                cursor: pointer;
                font-family: "Montserrat", SemiBold;
            }

            #database-upgrade.right {
                margin-top: 40px;
                font-size: 12px;
                padding: 1px 10px;
            }

            .listItems {
                background-image: url('images/check.svg');
                background-repeat: no-repeat;
            }

            #pointsList img {
                margin-right: 6px;
            }

            .container {
                text-align: center;
            }

            p.greatValue {
                float: left;
                color: rgb(75, 140, 202);
                font-weight: 500;
                font-size: 10px;
                margin-left: 23px;
                margin-top: 29px;
                font-family: "Source Sans Pro", Regular;
            }

            p.messages {
                text-align: center;
                color: #253746;
                margin-top: 35px;
                font-size: 14px;
                font-family: 'Source Sans Pro', Light;
            }

            p.price {
                text-align: center;
                color: #4B8CCA;
                font-size: 24px;
                font-family: 'Montserrat', Regular;
                line-height: 7px;
                margin-top: 5px;
            }

            p.subscribers {
                text-align: center;
                margin-top: 14px;
                margin-bottom: 10px;
                font-size: 12px;
                font-family: 'Source Sans Pro', Regular;
            }

            #wrap {
                text-align: center;
                width: 100%;
            }

            #left {
                display: inline-block;
                margin-top: 3px;
                margin-right: 50px;
                padding: 20px 15px;
                width: 498px;
            }

            #right {
                display: inline-block;
                margin-top: 3px;
                padding-bottom: 20px;
            }

            div.cutomMinHeight {
                min-height: 900px !important;
            }
            p.paidSupport {
                color: #8A9798;
                font-size: 14px;
                margin-top: 14px;
                font-family: 'Source Sans Pro', Light;

            }
            a.support {
                color: #4b8cca;
                text-decoration: none;
            }
            svg.performUpdate {
                padding-top: 39%;
                padding-left: 28%;
            }

            #arrowdown {
                width: 21px;
                height: 12px;
                background-image: url(images/arrow_down.png);
                background-repeat: no-repeat;
                margin: 0 auto;
                -moz-transition: all 1.5s ease-out;
                -webkit-transition: all 1.5s ease-out;
                -o-transition: all 1.5s ease-out;
                transition: all 1.5s ease-out;
            }
        </style>
    </head>
    <body>

    <div id="center">
        <div class="fixed">
            <div id="logo" title="Go back to phpList dashboard" onclick="location.href='../admin';">
                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="175"
                     height="54.598" viewBox="0 0 175 54.598">
                    <defs>
                        <linearGradient id="linear-gradient" x1="0.763" y1="0.237" x2="0.257" y2="0.745"
                                        gradientUnits="objectBoundingBox">
                            <stop offset="0" stop-color="#fff"/>
                            <stop offset="0.15" stop-color="#fbfcfc"/>
                            <stop offset="0.28" stop-color="#f0f2f3"/>
                            <stop offset="0.42" stop-color="#dee1e3"/>
                            <stop offset="0.55" stop-color="#c3c9cd"/>
                            <stop offset="0.67" stop-color="#a1aab0"/>
                            <stop offset="0.8" stop-color="#78848d"/>
                            <stop offset="0.92" stop-color="#485865"/>
                            <stop offset="1" stop-color="#233746"/>
                        </linearGradient>
                        <linearGradient id="linear-gradient-2" x1="0.236" y1="0.778" x2="0.717" y2="0.294"
                                        gradientUnits="objectBoundingBox">
                            <stop offset="0" stop-color="#fff"/>
                            <stop offset="0.15" stop-color="#fbfcfc"/>
                            <stop offset="0.29" stop-color="#f0f2f3"/>
                            <stop offset="0.42" stop-color="#dee0e3"/>
                            <stop offset="0.55" stop-color="#c3c8cd"/>
                            <stop offset="0.67" stop-color="#a1aab0"/>
                            <stop offset="0.8" stop-color="#78848d"/>
                            <stop offset="0.92" stop-color="#485864"/>
                            <stop offset="1" stop-color="#243746"/>
                        </linearGradient>
                    </defs>
                    <g id="Artwork_3" data-name="Artwork 3" transform="translate(87.5 27.299)">
                        <g id="Artwork_3-2" data-name="Artwork 3" transform="translate(-87.5 -27.299)">
                            <g id="Group_343" data-name="Group 343" transform="translate(62.756 10.828)">
                                <g id="Group_342" data-name="Group 342" transform="translate(106.498)">
                                    <path id="Path_799" data-name="Path 799"
                                          d="M541.063,41.334v-.413a1.175,1.175,0,0,0-.064-.444.571.571,0,0,0-.286-.286h0a.6.6,0,0,0,.349-.54.762.762,0,0,0-.286-.635,1.333,1.333,0,0,0-.825-.222H539v2.825h.444V40.381h.6a.6.6,0,0,1,.413.127.54.54,0,0,1,.127.413,4.921,4.921,0,0,0,.032.54v.1h.476v-.19Zm-.444-1.778a.381.381,0,0,1-.19.317.921.921,0,0,1-.476.127h-.508V39.08h.54a.825.825,0,0,1,.476.127A.445.445,0,0,1,540.619,39.556Z"
                                          transform="translate(-537.159 -37.306)" fill="#243746"/>
                                    <path id="Path_800" data-name="Path 800"
                                          d="M538.564,35.5a2.73,2.73,0,0,0-1.048-1.016,2.952,2.952,0,0,0-2.889,0,2.73,2.73,0,0,0-1.048,1.016,2.825,2.825,0,0,0,1.048,3.873,2.952,2.952,0,0,0,2.889,0,2.825,2.825,0,0,0,1.048-3.873Zm-2.476,3.873a2.444,2.444,0,0,1-1.27-.349,2.381,2.381,0,0,1-.889-.889,2.445,2.445,0,0,1-.317-1.206,2.413,2.413,0,0,1,.317-1.238,2.349,2.349,0,0,1,.889-.889,2.476,2.476,0,0,1,1.238-.317,2.444,2.444,0,0,1,1.27.349,2.381,2.381,0,0,1,.889.889,2.476,2.476,0,0,1,0,2.412,2.381,2.381,0,0,1-.889.889,2.444,2.444,0,0,1-1.238.349Z"
                                          transform="translate(-533.2 -34.111)" fill="#243746"/>
                                </g>
                                <path id="Path_801" data-name="Path 801"
                                      d="M274.011,50.491a7.072,7.072,0,0,1,1.809,5.269v9.079h-4.6v-8.38a4.208,4.208,0,0,0-.825-2.825,2.9,2.9,0,0,0-2.381-.921,3.65,3.65,0,0,0-2.762,1.079A4.432,4.432,0,0,0,264.2,57v7.841h-4.6V47.6a4.6,4.6,0,0,1,4.6-4.6h0v7.618a6.126,6.126,0,0,1,2.222-1.4,8,8,0,0,1,2.825-.508A6.5,6.5,0,0,1,274.011,50.491Z"
                                      transform="translate(-239.951 -40.178)" fill="#243746"/>
                                <path id="Path_802" data-name="Path 802"
                                      d="M384.493,47.3V63.87h10.348v3.9H379.7V52.093A4.793,4.793,0,0,1,384.493,47.3Z"
                                      transform="translate(-321.927 -43.113)" fill="#243746"/>
                                <path id="Path_803" data-name="Path 803"
                                      d="M433.614,47.821a2.886,2.886,0,0,1,4.127-4.031,2.6,2.6,0,0,1,.794,1.936,2.889,2.889,0,0,1-.794,2.1,3.016,3.016,0,0,1-4.127.032Z"
                                      transform="translate(-358.161 -40.175)" fill="#243746"/>
                                <rect id="Rectangle_185" data-name="Rectangle 185" width="4.603" height="13.888"
                                      transform="translate(75.231 10.757)" fill="#243746"/>
                                <path id="Path_804" data-name="Path 804"
                                      d="M334.184,64.171a7.3,7.3,0,0,0-2.857-2.857A8.1,8.1,0,0,0,327.3,60.3a6.11,6.11,0,0,0-4.984,2.063V60.3a4.6,4.6,0,0,0-4.412,4.635v17.2h4.6V74.71a6.179,6.179,0,0,0,4.793,1.9,8.1,8.1,0,0,0,4.031-1.016,7.3,7.3,0,0,0,2.857-2.857,9.289,9.289,0,0,0,0-8.571Zm-4.7,7.11a3.873,3.873,0,0,1-2.92,1.174,3.987,3.987,0,1,1,2.92-6.857,3.682,3.682,0,0,1,.794,1.143,5.4,5.4,0,0,1,.254,1.746,5.9,5.9,0,0,1-.127,1.143,3.9,3.9,0,0,1-.921,1.651Z"
                                      transform="translate(-279.745 -51.985)" fill="#243746"/>
                                <path id="Path_805" data-name="Path 805"
                                      d="M469.477,68.863a4.761,4.761,0,0,0-2.159-1.333,27.173,27.173,0,0,0-3.174-.7,14.188,14.188,0,0,1-2.539-.571,1.048,1.048,0,0,1-.794-1.048,1.206,1.206,0,0,1,.73-1.048,4.655,4.655,0,0,1,2.222-.413,9.205,9.205,0,0,1,4.571,1.206l1.524-3.27a9.364,9.364,0,0,0-2.762-1.016,15.459,15.459,0,0,0-3.365-.381,10.856,10.856,0,0,0-3.936.635,5.619,5.619,0,0,0-2.539,1.809A4.349,4.349,0,0,0,456.4,65.4a3.818,3.818,0,0,0,.921,2.762,4.984,4.984,0,0,0,2.19,1.365,23.2,23.2,0,0,0,3.206.667,11.776,11.776,0,0,1,2.444.508,1.017,1.017,0,0,1,.794.984q0,1.46-2.92,1.46a10.793,10.793,0,0,1-2.952-.413,10.127,10.127,0,0,1-2.159-.889l-1.524,3.3a11.174,11.174,0,0,0,2.6.984,15.4,15.4,0,0,0,3.873.476,11.491,11.491,0,0,0,4.031-.635,5.65,5.65,0,0,0,2.571-1.778,4.19,4.19,0,0,0,.889-2.635A3.687,3.687,0,0,0,469.477,68.863Z"
                                      transform="translate(-374.279 -51.98)" fill="#243746"/>
                                <path id="Path_806" data-name="Path 806"
                                      d="M215.032,68.457a8.634,8.634,0,0,0-1.048-4.285,7.3,7.3,0,0,0-2.857-2.857A8.1,8.1,0,0,0,207.1,60.3a6.109,6.109,0,0,0-4.984,2.063V60.3a4.6,4.6,0,0,0-4.412,4.635v17.2h4.6V74.71a6.179,6.179,0,0,0,4.793,1.9,8.094,8.094,0,0,0,4.031-1.016,7.3,7.3,0,0,0,2.857-2.857,8.634,8.634,0,0,0,1.048-4.285Zm-12.761,0h0a3.873,3.873,0,0,1,4.031-3.968,4,4,0,0,1,2.92,1.111,3.619,3.619,0,0,1,1.079,2.1,6.188,6.188,0,0,1,.063.7,4.1,4.1,0,0,1-1.111,2.92A4.022,4.022,0,0,1,202.3,68.52Z"
                                      transform="translate(-197.7 -51.985)" fill="#243746"/>
                                <path id="Path_807" data-name="Path 807"
                                      d="M514.988,66.8a2.952,2.952,0,0,1-1.873.6,1.9,1.9,0,0,1-1.46-.54,2.159,2.159,0,0,1-.508-1.555V59.623h3.714a3.555,3.555,0,0,0-1.047-2.508h0a3.968,3.968,0,0,0-2.666-1.016h0l.1-4h-4.6l-.1,4h-.667a1.778,1.778,0,1,0,0,3.555h.667v5.682A5.555,5.555,0,0,0,508.1,69.59a6.154,6.154,0,0,0,4.381,1.46,8.476,8.476,0,0,0,2.1-.254,4.761,4.761,0,0,0,1.682-.762Z"
                                      transform="translate(-406.839 -46.39)" fill="#243746"/>
                            </g>
                            <g id="Group_345" data-name="Group 345">
                                <circle id="Ellipse_63" data-name="Ellipse 63" cx="27.299" cy="27.299" r="27.299"
                                        fill="#243746"/>
                                <path id="Path_808" data-name="Path 808"
                                      d="M41.283,40.462l2.666-2.635.222-.222a6.953,6.953,0,0,0,.667-.794,7.364,7.364,0,1,0-12.126-.349,6.158,6.158,0,0,0,.825,1.079l.317.317,2.6,2.6-5.65,5.555L12.9,28.146A27.711,27.711,0,0,0,11,31.034L28.363,48.4l-2.571,2.539-.381.381a7.237,7.237,0,0,0-2,4.6h0a6.912,6.912,0,0,0,1.238,4.508,7.364,7.364,0,0,0,13.078-1.778,6.285,6.285,0,0,0,.349-2.7,7.364,7.364,0,0,0-2.127-4.825l-2.762-2.762,5.682-5.523L56.742,60.714a27.139,27.139,0,0,0,1.873-2.92ZM33.538,53.6a3.968,3.968,0,1,1-5.714.127l.286-.317,2.635-2.6Zm2.444-18.411a3.968,3.968,0,1,1,5.777.1l-.127.127L38.87,38.081Z"
                                      transform="translate(-7.508 -17.131)" fill="#fff"/>
                                <g id="Group_344" data-name="Group 344" transform="translate(18.062 18.189)">
                                    <path id="Path_809" data-name="Path 809"
                                          d="M104.006,59.712l-.127.1-2.666,2.635L98.8,60.062l2.73-2.7.063-.063Z"
                                          transform="translate(-85.5 -57.3)" fill="url(#linear-gradient)"/>
                                    <path id="Path_810" data-name="Path 810"
                                          d="M62.074,100.844l-2.635,2.571-.159.127L56.9,101.162l.222-.222L59.693,98.4Z"
                                          transform="translate(-56.9 -85.354)" fill="url(#linear-gradient-2)"/>
                                </g>
                            </g>
                        </g>
                    </g>
                </svg>

                <h1 style="font-family: 'Montserrat', Regular;font-size: 18px;cursor:auto;">Updating phpList to the latest
                    version</h1>
            </div>
            <div id="steps">
                <div id="first-step"></div>
                <div class="step">
                    <div class="step-image active">

                        <svg xmlns="http://www.w3.org/2000/svg" width="27.483" height="25.403"
                             viewBox="0 0 27.483 25.403">
                            <g id="Integrity_check" data-name="Integrity check" transform="translate(0 0)">
                                <g id="Group_211" data-name="Group 211">
                                    <g id="Path_218" data-name="Path 218" transform="translate(0 -7.524)" fill="#fff">
                                        <path d="M23.808,16.226v-.994l3.674-.171V13.218l-3.674-.171v-.765H20.935V8.911A1.388,1.388,0,0,0,19.55,7.524H7.932A1.388,1.388,0,0,0,6.546,8.911v3.372H3.674v.765L0,13.218v1.844l3.674.171v.994H6.546v2.142H3.674v.763L0,19.3v1.843l3.674.172v.994H6.546v2.372H3.674v.765L0,25.619v1.843l3.674.171v.994H6.546v2.914a1.387,1.387,0,0,0,1.386,1.385H19.55a1.386,1.386,0,0,0,1.385-1.385V28.627h2.873v-.994l3.674-.171V25.619l-3.674-.171v-.765H20.935V22.311h2.873v-.994l3.674-.172V19.3l-3.674-.171v-.763H20.935V16.226Zm2.946,10.087v.452l-3.674.171v.96H21.05V25.412h2.03v.732ZM23.08,20.625v.959H21.05V19.1h2.03v.732L26.754,20v.454Zm3.674-6.71v.453l-3.674.171v.96H21.05V13.011h2.03v.732ZM19.55,32.2H7.932a.658.658,0,0,1-.657-.656V8.911a.658.658,0,0,1,.657-.658H19.55a.658.658,0,0,1,.656.658v22.63A.657.657,0,0,1,19.55,32.2ZM.728,26.766v-.452L4.4,26.143v-.732h2.03V27.9H4.4v-.96Zm0-6.313V20L4.4,19.828V19.1h2.03v2.486H4.4v-.959Zm0-6.086v-.453L4.4,13.743v-.732h2.03V15.5H4.4v-.96Z"
                                              stroke="none"/>
                                        <path d="M 7.931596755981445 7.524005889892578 L 19.55012512207031 7.524005889892578 C 20.31339645385742 7.524005889892578 20.93525695800781 8.146415710449219 20.93525695800781 8.91064453125 L 20.93525695800781 12.28284454345703 L 23.80833625793457 12.28284454345703 L 23.80833625793457 13.04763412475586 L 27.48283576965332 13.21833419799805 L 27.48283576965332 15.06194496154785 L 23.80833625793457 15.23264503479004 L 23.80833625793457 16.22646522521973 L 20.93525695800781 16.22646522521973 L 20.93525695800781 18.36890411376953 L 23.80833625793457 18.36890411376953 L 23.80833625793457 19.13216400146484 L 27.48283576965332 19.30328559875488 L 27.48283576965332 21.14592552185059 L 23.80833625793457 21.31759452819824 L 23.80833625793457 22.31141471862793 L 20.93525695800781 22.31141471862793 L 20.93525695800781 24.68343353271484 L 23.80833625793457 24.68343353271484 L 23.80833625793457 25.44821548461914 L 27.48283576965332 25.61934471130371 L 27.48283576965332 27.4625358581543 L 23.80833625793457 27.6336555480957 L 23.80833625793457 28.62747573852539 L 20.93525695800781 28.62747573852539 L 20.93525695800781 31.54160499572754 C 20.93525695800781 32.3063850402832 20.31339645385742 32.92672348022461 19.55012512207031 32.92672348022461 L 7.931735992431641 32.92672348022461 C 7.167505264282227 32.92672348022461 6.545646667480469 32.3063850402832 6.545646667480469 31.54160499572754 L 6.545646667480469 28.62747573852539 L 3.673526763916016 28.62747573852539 L 3.673526763916016 27.6336555480957 L -3.814697265625e-06 27.4625358581543 L -3.814697265625e-06 25.61934471130371 L 3.673526763916016 25.4482250213623 L 3.673526763916016 24.68344497680664 L 6.545646667480469 24.68344497680664 L 6.545646667480469 22.31141471862793 L 3.673526763916016 22.31141471862793 L 3.673526763916016 21.31759452819824 L -3.814697265625e-06 21.14592552185059 L -3.814697265625e-06 19.30328559875488 L 3.673526763916016 19.13216400146484 L 3.673526763916016 18.36890411376953 L 6.545646667480469 18.36890411376953 L 6.545646667480469 16.22646522521973 L 3.673526763916016 16.22646522521973 L 3.673526763916016 15.23264503479004 L -3.814697265625e-06 15.06194496154785 L -3.814697265625e-06 13.21833419799805 L 3.673526763916016 13.04763412475586 L 3.673526763916016 12.28284454345703 L 6.545505523681641 12.28284454345703 L 6.545505523681641 8.91064453125 C 6.545505523681641 8.146274566650391 7.167366027832031 7.524005889892578 7.931596755981445 7.524005889892578 Z M 19.55012512207031 32.19705581665039 C 19.91213607788086 32.19705581665039 20.20654678344727 31.90222549438477 20.20640563964844 31.54063415527344 L 20.20640563964844 8.91064453125 C 20.20640563964844 8.547115325927734 19.91157531738281 8.252296447753906 19.54998588562012 8.252296447753906 L 7.931596755981445 8.252296447753906 C 7.569036483764648 8.252296447753906 7.274215698242188 8.547115325927734 7.274215698242188 8.91064453125 L 7.274215698242188 31.54063415527344 C 7.274215698242188 31.90222549438477 7.569036483764648 32.19705581665039 7.931596755981445 32.19705581665039 L 19.55012512207031 32.19705581665039 Z M 23.07963562011719 15.49775505065918 L 23.07963562011719 14.5380744934082 L 26.75412559509277 14.3669548034668 L 26.75412559509277 13.91428375244141 L 23.07963562011719 13.74261474609375 L 23.07963562011719 13.01100540161133 L 21.04997634887695 13.01100540161133 L 21.04997634887695 15.49775505065918 L 23.07963562011719 15.49775505065918 Z M 6.432306289672852 15.49830436706543 L 6.432306289672852 13.01155471801758 L 4.402095794677734 13.01155471801758 L 4.402095794677734 13.7431640625 L 0.7275962829589844 13.91428375244141 L 0.7275962829589844 14.3669548034668 L 4.402095794677734 14.53863525390625 L 4.402095794677734 15.49830436706543 L 6.432306289672852 15.49830436706543 Z M 6.432306289672852 21.58229446411133 L 6.432306289672852 19.09609413146973 L 4.402095794677734 19.09609413146973 L 4.402095794677734 19.82825469970703 L 0.7275962829589844 19.99937438964844 L 0.7275962829589844 20.45288467407227 L 4.402095794677734 20.62358474731445 L 4.402095794677734 21.58229446411133 L 6.432306289672852 21.58229446411133 Z M 23.07963562011719 21.58325386047363 L 23.07963562011719 20.62455558776855 L 26.75412559509277 20.4539852142334 L 26.75412559509277 20.00033378601074 L 23.07963562011719 19.82922554016113 L 23.07963562011719 19.09705543518066 L 21.04997634887695 19.09705543518066 L 21.04997634887695 21.58325386047363 L 23.07963562011719 21.58325386047363 Z M 6.432306289672852 27.89696502685547 L 6.432306289672852 25.41118431091309 L 4.402095794677734 25.41118431091309 L 4.402095794677734 26.14278411865234 L 0.7275962829589844 26.31391525268555 L 0.7275962829589844 26.76603507995605 L 4.402095794677734 26.93673515319824 L 4.402095794677734 27.89696502685547 L 6.432306289672852 27.89696502685547 Z M 23.07963562011719 27.89738464355469 L 23.07963562011719 26.93715476989746 L 26.75412559509277 26.76603507995605 L 26.75412559509277 26.31391525268555 L 23.07963562011719 26.14320373535156 L 23.07963562011719 25.41159439086914 L 21.04997634887695 25.41159439086914 L 21.04997634887695 27.89738464355469 L 23.07963562011719 27.89738464355469 Z"
                                              stroke="none" fill="#707070"/>
                                    </g>
                                    <g id="Ellipse_53" data-name="Ellipse 53" transform="translate(8.193 1.813)"
                                       fill="#fff"
                                       stroke="#707070" stroke-width="1">
                                        <circle cx="0.9" cy="0.9" r="0.9" stroke="none"/>
                                        <circle cx="0.9" cy="0.9" r="0.4" fill="none"/>
                                    </g>
                                    <g id="Ellipse_54" data-name="Ellipse 54" transform="translate(8.193 5.813)"
                                       fill="#fff"
                                       stroke="#707070" stroke-width="1">
                                        <circle cx="0.9" cy="0.9" r="0.9" stroke="none"/>
                                        <circle cx="0.9" cy="0.9" r="0.4" fill="none"/>
                                    </g>
                                    <g id="Ellipse_55" data-name="Ellipse 55" transform="translate(17.193 21.813)"
                                       fill="#fff" stroke="#707070" stroke-width="1">
                                        <circle cx="0.9" cy="0.9" r="0.9" stroke="none"/>
                                        <circle cx="0.9" cy="0.9" r="0.4" fill="none"/>
                                    </g>
                                </g>
                            </g>
                        </svg>

                    </div>
                    <hr class="divider"/>
                    <div class="clear"></div>
                    <h2>Initialize</h2>
                </div>

                <div class="step">
                    <div class="step-image ">

                        <svg xmlns="http://www.w3.org/2000/svg" width="27.337" height="27.637"
                             viewBox="0 0 27.337 27.637">
                            <g id="Replace_files" data-name="Replace files" transform="translate(0 0)">
                                <path id="Path_214" data-name="Path 214"
                                      d="M48.3,15.4c.01-.012.019-.025.028-.038v0c.008-.013.016-.026.023-.039l.006-.012.015-.032.005-.012c.006-.015.011-.03.016-.045v0c0-.014.008-.029.011-.044l0-.013c0-.012,0-.024.005-.035s0-.009,0-.014,0-.032,0-.048V8.615c0-.016,0-.032,0-.048s0-.009,0-.014,0-.024-.005-.036l0-.013c0-.015-.007-.03-.011-.044v0c0-.015-.01-.03-.016-.045L48.37,8.4l-.015-.032-.006-.012c-.007-.013-.015-.026-.023-.039v0c-.009-.013-.018-.026-.028-.038l-.009-.011-.024-.027-.006-.006L40.181.157a.54.54,0,0,0-.763,0L29.946,9.629H26.669a.54.54,0,1,0,0,1.08h2.2l-2.983,2.983H22.821a.54.54,0,1,0,0,1.08H24.8l-.642.642a.54.54,0,0,0,0,.763l1.578,1.578H23.777a.54.54,0,1,0,0,1.08h3.042L29.8,21.817H26.669a.54.54,0,1,0,0,1.08h4.212l4.581,4.581a.54.54,0,0,0,.763,0L48.257,15.447l.005-.006.025-.027L48.3,15.4ZM45.413,11.84l1.922-1.922v3.845ZM35.844,26.333,32.408,22.9h1.643a.54.54,0,0,0,0-1.08H31.329l-2.983-2.983h1.371a.54.54,0,1,0,0-1.08H27.266L25.306,15.8l1.024-1.024h5.791a.54.54,0,1,0,0-1.08H27.41l2.983-2.983h6.459a.54.54,0,0,0,0-1.08h-5.38L39.8,1.3l7.312,7.312-2.844,2.844a.54.54,0,0,0,0,.763l2.844,2.844Zm0,0"
                                      transform="translate(-21.078 0.001)" fill="#253746" fill-rule="evenodd"/>
                                <path id="Path_215" data-name="Path 215"
                                      d="M133.485,104.217h2.646a.54.54,0,1,0,0-1.08h-2.646a.54.54,0,1,0,0,1.08Zm0,0"
                                      transform="translate(-125.769 -97.57)" fill="#253746" fill-rule="evenodd"/>
                                <path id="Path_216" data-name="Path 216"
                                      d="M54.122,179.482a.54.54,0,1,0-.54-.54A.541.541,0,0,0,54.122,179.482Zm0,0"
                                      transform="translate(-50.69 -168.772)" fill="#253746" fill-rule="evenodd"/>
                                <path id="Path_217" data-name="Path 217"
                                      d="M.54,328.934a.54.54,0,1,0,.54.54A.541.541,0,0,0,.54,328.934Zm0,0"
                                      transform="translate(0 -311.179)" fill="#253746" fill-rule="evenodd"/>
                            </g>
                        </svg>


                    </div>
                    <hr class="divider"/>
                    <div class="clear"></div>
                    <h2>Back Up</h2>
                </div>
                <div class="step">
                    <div class="step-image">
                        <svg xmlns="http://www.w3.org/2000/svg" width="23.015" height="21.33"
                             viewBox="0 0 23.015 21.33">
                            <g id="download" transform="translate(0 0)">
                                <g id="Group_210" data-name="Group 210">
                                    <path id="Path_211" data-name="Path 211"
                                          d="M22.356,228.248a.657.657,0,0,0-.659.659v6a2.959,2.959,0,0,1-2.955,2.955H4.274a2.959,2.959,0,0,1-2.955-2.955v-6.1a.659.659,0,0,0-1.319,0v6.1a4.278,4.278,0,0,0,4.274,4.274H18.741a4.278,4.278,0,0,0,4.274-4.274v-6A.66.66,0,0,0,22.356,228.248Z"
                                          transform="translate(0 -217.849)" fill="#253746"/>
                                    <path id="Path_212" data-name="Path 212"
                                          d="M140.615,33.344a.664.664,0,0,0,.464.2.643.643,0,0,0,.464-.2l4.191-4.191a.66.66,0,1,0-.933-.933l-3.062,3.067V17.909a.659.659,0,1,0-1.319,0V31.288l-3.067-3.067a.66.66,0,0,0-.933.933Z"
                                          transform="translate(-129.571 -17.25)" fill="#253746"/>
                                </g>
                            </g>
                        </svg>
                    </div>
                    <hr class="divider"/>
                    <div class="clear"></div>
                    <h2>Download</h2>
                </div>
                <div class="step last-step">
                    <div class="step-image">
                        <svg xmlns="http://www.w3.org/2000/svg" width="22.512" height="16.01"
                             viewBox="0 0 22.512 16.01" class="performUpdate">
                            <path id="Path_219" data-name="Path 219"
                                  d="M16100.607-997.888c-2.889,2.889-14.332,14.2-14.332,14.2l-6.68-6.679"
                                  transform="translate(-16078.847 998.638)" fill="none" stroke="#253746"
                                  stroke-linecap="round" stroke-width="1.5"/>
                        </svg>

                    </div>
                    <div class="clear"></div>
                    <h2>Perform update</h2>
                </div>

                <div class="clear"></div>
            </div>

            <div id="display">
                <span id="current-step" class="hidden"> </span>
                <span id="success-message">Updater is loading</span><br/>
                <span id="error-message"></span><br>
                <div>
                    <button id="next-step" class="right">Next</button>
                    <button id="database-upgrade" class="right" style="visibility: hidden;">Upgrade database</button>

                </div>
            </div>
        </div>
    </div>

    <!-- Info updater section -->
    <div class="outer">
        <button class="info-footer" id="button">
            <div id="arrowdown"></div>
        </button>
        <div class="inner">
            <div id="wrap">
                <div id="left">
                    <ul>
                        <li class="final">The Final Upgrade?</li>
                        <li class="migrate">Migrate to phpList.com and forget about the tech</li>
                    </ul>
                    <ul style="float:left;margin-right: 18px;">
                        <li>
                            <div id="pointsList">
                                <img src="images/check.svg">
                                <span>Seamless background updating</span>
                            </div>
                        </li>
                        <li>
                            <div id="pointsList">
                                <img src="images/check.svg">
                                <span>Managed DMARC, SPF, and DKIM</span>
                            </div>
                        </li>
                        <li>
                            <div id="pointsList">
                                <img src="images/check.svg">
                                <span>Database import for existing data</span>
                            </div>
                        </li>
                    </ul>
                    <ul>
                        <li>
                            <div id="pointsList">
                                <img src="images/check.svg">
                                <span>Expert technical support</span>
                            </div>
                        </li>
                        <li>
                            <div id="pointsList">
                                <img src="images/check.svg">
                                <span>Scale up to 30 million messages per month</span>
                            </div>
                        </li>
                        <li>
                            <div id="pointsList">
                                <img src="images/check.svg">
                                <span>Custom domains and unlimited users</span>
                            </div>
                        </li>
                    </ul>
                    <p class="paidSupport">Happy with your existing installation? Paid support by independent consultants <a href="https://www.phplist.org/paid-support/" class="support" target="_blank">here</a>.</p>
                </div>
                <div id="right">
                    <div id="sqr">
                        <div class="container" style="margin-left: 22px;">
                            <p class="greatValue">Great value</p>
                            <br>
                            <p class="messages">9000 messages</p><br>
                            <p class="price">Price $1</p>
                            <p class="subscribers">3000 Subscribers</p>
                            <input type="button" onclick="window.open('https://phplist.com/chooseplan')" value="Book"
                                   style="width: 90px;height: 30px; border: 1px dashed #21AE8A; background: #fff; margin: 0 auto;"
                                   class="book"/>
                        </div>

                    </div>
                </div>
            </div>
        </div>
    </div><!-- .inner -->
    </div><!-- .outer -->

    <!-- Load jquery-3.6.0.min.js file -->
    <script type="text/javascript" src="../admin/js/jquery-3.6.0.min.js"></script>

    <!-- script for arrow animation -->
    <script type="text/javascript">
        var rotated = false;

        document.getElementById('button').onclick = function() {
            var div = document.getElementById('arrowdown'),
                deg = rotated ? 0 : 180;

            div.style.webkitTransform = 'rotate('+deg+'deg)';
            div.style.mozTransform    = 'rotate('+deg+'deg)';
            div.style.msTransform     = 'rotate('+deg+'deg)';
            div.style.oTransform      = 'rotate('+deg+'deg)';
            div.style.transform       = 'preserve-3d('+deg+'deg)';

            rotated = !rotated;
        }
    </script>

    <!-- script for slideToggle -->
    <script type="text/javascript">
        $('.outer button').on("click", function () {
            $('.inner').slideToggle(1000, function () {
                $('.inner p').show(100);
            });
        });
    </script>
    <!-- Arrow transition -->
    <script type="text/javascript">
        $("#center").addClass("cutomMinHeight");
        $(".fixed").addClass("cutomMinHeight");
    </script>

    <script>
        let previousFormActions = null;

        function takeAction(action, formValues, callback) {
            let req = new XMLHttpRequest();
            let url = "<?php echo htmlentities($_SERVER['REQUEST_URI'])?>";
            req.open('POST', url, true);
            req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            req.onload = callback;

            let body = "action=" + action;

            if (previousFormActions !== null) {
                body = body + "&" + previousFormActions;
            }
            if (formValues) {
                body = body + "&" + formValues;
                previousFormActions = previousFormActions + "&" + formValues;
            }

            req.send(body);
        }

        takeAction(0, null, function () {
            setCurrentStep(JSON.parse(this.responseText).status.step);
            executeNextStep();
        });

        function setCurrentStep(action) {
            document.getElementById("current-step").innerText = action;
        }

        function showErrorMessage(error) {
            document.getElementById("error-message").innerText = error;
        }

        function showSuccessMessage(success) {
            document.getElementById("error-message").innerText = '';
            document.getElementById("success-message").innerHTML = success;
        }

        function setCurrentActionItem(step) {
            const stepActionMap = {
                1: 0,
                2: 0,
                3: 0,
                4: 0,
                5: 1,
                6: 1,
                7: 1,
                8: 2,
                9: 2,
                10: 3,
                11: 3,
                12: 3,
                13: 3,
                14: 3,
                15: 3,
                16: 3,
                17: 3,
                18: 3,
                19: 3,
                20: 3,
            };

            let steps = document.querySelectorAll('.step-image');
            steps.forEach(function (element) {
                element.classList.remove('active');
            });
            steps[stepActionMap[step]].classList.add('active');

            return stepActionMap[step];
        }

        function executeNextStep(formParams) {
            let nextStep = parseInt(document.getElementById("current-step").innerText) + 1;
            setCurrentActionItem(nextStep);
            document.getElementById('next-step').disabled = true;
            takeAction(nextStep, formParams, function () {
                let continueResponse = JSON.parse(this.responseText).continue;
                let responseMessage = JSON.parse(this.responseText).response;
                let retryResponse = JSON.parse(this.responseText).retry;
                let autocontinue = JSON.parse(this.responseText).autocontinue;
                let nextUrl = JSON.parse(this.responseText).nextUrl;
                if (continueResponse === true) {
                    showSuccessMessage(responseMessage);
                    setCurrentStep(nextStep);
                    document.getElementById('next-step').disabled = false;
                    if (autocontinue === true) {
                        executeNextStep();
                    }
                    if (nextUrl) {
                        document.getElementById("next-step").addEventListener("click", function () {
                            window.location = nextUrl;
                        });
                    }
                } else {
                    showErrorMessage(responseMessage);
                    if (retryResponse === true) {
                        setCurrentStep(nextStep - 1);
                        document.getElementById('next-step').disabled = false;
                    }
                }
            });
        }

        document.getElementById("next-step").addEventListener("click", function () {
            let backupform = document.querySelector('form');
            if (backupform !== null) {
                let formParams = new URLSearchParams(new FormData(backupform)).toString();
                executeNextStep(formParams);
            } else {
                executeNextStep(null);
            }

        });
    </script>
    </body>
    </html>
<?php } ?>
