<?php
declare(strict_types = 1);

/**
 * Mise à jour de l'application.
 *
 * @license https://www.gnu.org/licenses/gpl-3.0.html
 * @link https://www.igalerie.org/
 */
class Update
{
	/**
	 * Regexp pour le numéro de version.
	 *
	 * @var string
	 */
	const VERSION_REGEXP = '`^[-a-z\d\._]{1,18}$`';



	/**
	 * Peut-on récupérer le contenu d'un fichier distant ?
	 *
	 * @return bool
	 */
	public static function isAllowedHTTPOrigin(): bool
	{
		// cURL
		if (function_exists('curl_init'))
		{
			return TRUE;
		}

		// file_get_contents
		if (function_exists('ini_get') && System::getIniBool('allow_url_fopen'))
		{
			return TRUE;
		}

		return FALSE;
	}

	/**
	 * Récupère et vérifie l'archive de la version $version.
	 *
	 * @param array $version
	 *
	 * @return bool
	 */
	public static function getArchive(array $version): bool
	{
		if (!preg_match(self::VERSION_REGEXP, (string) $version['version'])
		|| !preg_match('`^[a-f0-9]{32}$`', (string) $version['md5']))
		{
			trigger_error('regexp error', E_USER_WARNING);
			return FALSE;
		}

		$file = 'igalerie-' . $version['version'] . '.zip?' . Security::key(32);
		$dest = GALLERY_ROOT . '/latestversion.zip';
		$data = self::_getContents(System::APP_WEBSITE . $file);

		if (!$data || File::putContents($dest, $data) === FALSE)
		{
			trigger_error('file error', E_USER_WARNING);
			return FALSE;
		}

		if (md5_file($dest) != $version['md5'])
		{
			trigger_error('md5 error', E_USER_WARNING);
			return FALSE;
		}

		if (!filesize($dest))
		{
			trigger_error('filesize error', E_USER_WARNING);
			return FALSE;
		}

		return TRUE;
	}

	/**
	 * Retourne les informations de la dernière version de l'application
	 * sous forme d'un tableau, ou FALSE si une erreur est survenue.
	 *
	 * @return mixed
	 */
	public static function getLatestVersion()
	{
		$url = System::APP_WEBSITE . 'latestversion.json?' . Security::key(32);
		if (!$latest_version = self::_getContents($url))
		{
			trigger_error('file error', E_USER_WARNING);
			return FALSE;
		}

		if (!Utility::isJson($latest_version))
		{
			trigger_error('json error', E_USER_WARNING);
			return FALSE;
		}

		$latest_version = Utility::jsonDecode($latest_version);

		if (!is_array($latest_version))
		{
			trigger_error('array error', E_USER_WARNING);
			return FALSE;
		}

		if (preg_match(self::VERSION_REGEXP, (string) $latest_version['version'])
		&& preg_match('`^[a-f0-9]{32}$`', (string) $latest_version['md5'])
		&& preg_match(self::VERSION_REGEXP, (string) $latest_version['php']))
		{
			return
			[
				'md5' => (string) $latest_version['md5'],
				'php' => (string) $latest_version['php'],
				'version' => (string) $latest_version['version']
			];
		}

		trigger_error('regexp error', E_USER_WARNING);
		return FALSE;
	}

	/**
	 * Extrait les fichiers de l'archive et remplace
	 * tous les fichiers de l'application.
	 *
	 * @return bool
	 */
	public static function replaceFiles(): bool
	{
		$file = GALLERY_ROOT . '/latestversion.zip';

		$zip = new Zip;

		if ($zip->open($file) !== TRUE)
		{
			trigger_error('zip open error', E_USER_WARNING);
			return FALSE;
		}

		if ($zip->extractSubdirTo(dirname($file), 'igalerie'))
		{
			trigger_error('zip extract error', E_USER_WARNING);
			return FALSE;
		}

		$zip->close();
		File::unlink($file);

		return TRUE;
	}



	/**
	 * Récupération du contenu d'un fichier distant.
	 *
	 * @param string $url
	 *
	 * @return mixed
	 */
	private static function _getContents(string $url)
	{
		// cURL
		if (function_exists('curl_init'))
		{
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
			curl_setopt($ch, CURLOPT_TIMEOUT, 5);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
			$data = curl_exec($ch);
			if ($data)
			{
				return $data;
			}
		}

		// file_get_contents
		if (function_exists('ini_get') && System::getIniBool('allow_url_fopen'))
		{
			$context = stream_context_create(['http' => ['timeout' => 3]]);
			return file_get_contents($url, FALSE, $context);
		}

		return FALSE;
	}
}
?>