<?php
/**
 * Coppermine Photo Gallery
 *
 * v1.0 originally written by Gregory Demar
 *
 * @copyright  Copyright (c) 2003-2023 Coppermine Dev Team
 * @license    GNU General Public License version 3 or later; see LICENSE
 *
 * cpg_installer_stub.php (v3.5)
 */
error_reporting(-1);
define('UPDATER_LOG', 'installer.log.txt');

// ================================================== //
// to be added appropriately to the language file(s)
	$lang_install_php['no_zip_extn'] = 'The extension providing ZipArchive is not available';
	$lang_install_php['delete_log'] = 'Please first delete the log file: <code>'.UPDATER_LOG.'</code>';
	$lang_install_php['files_placed_title'] = 'Package files placed';
	$lang_install_php['files_placed_msg'] = 'All package files were successfully placed. Click below to complete the install.';
	$lang_install_php['file_errs_title'] = 'Package install errors';
	$lang_install_php['file_errs_msg'] = 'Not all package files were successfully placed. The issues must be corrected.';
	$lang_install_php['complete_install'] = 'Complete Install';
	$lang_install_php['select_install'] = 'Please select the package to install.';
	$lang_install_php['available_packages'] = 'Available Install Packages';
	$lang_install_php['show_all'] = 'Show All Available Versions';
	$lang_install_php['not_writeable'] = '<b>Installation can not be performed</b><br />The following directories are not writeable: ';
	$lang_install_php['perform_install'] = 'Install Selected Package';
	$lang_install_php['no_installs_title'] = 'No install available';
	$lang_install_php['no_installs_msg'] = 'No packages are currently available for you to install';
	$lang_install_php['not_possible'] = 'The server is not configured to allow this method to function.<br />PHP needs allow_url_fopen or cURL enabled.';
	$lang_install_php['not_found'] = 'Releases of CPG not found at Github';
	$lang_install_php['save_error'] = 'Could not save package file. Operation aborted.';
	$lang_install_php['pre_warning'] = '<span style="color:red">WARNING: This is a pre-release version!</span>';
	$lang_install_php['location'] = 'Install location:';
	$lang_install_php['loc_curdir'] = 'New install in the current directory';
	$lang_install_php['loc_separate'] = 'New install in a separate directory';
	$lang_install_php['upgrade_current'] = 'Upgrade/update the current CPG installation';
// ================================================== //

if (file_exists(UPDATER_LOG)) die($lang_install_php['delete_log']);

if (!extension_loaded('zip')) {
	die($lang_install_php['no_zip_extn']);
}

echo <<<EOT
<!DOCTYPE html>
<html>
<head>
<title>Coppermine Photo Gallery Installer Stub</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<style type="text/css">
input, button, label {cursor: pointer}
button { font-size: 1em }
div.contain { padding: 3em; }
.shead { font-size: 1.2em; font-weight: bold; }
.tableb { background-color: #EEE; }
td.bleed { background-color: #F77; color: #FFB; }
.boldt {color: coral; }
.iloc { margin-bottom: 1em; }
.updt { border-collapse: collapse; margin-bottom: 1em; }
.iloc input, .updt input { margin-left: 1em; margin-right: .5em; }
.oblk:hover { background-color: #EFE }
.oblk p { padding-right: 2rem; }
div.cpg_message_info { background-color: #E8FFE8; border: 1px solid #3C3; border-radius: 4px; padding: 1em; margin-bottom: 1em; }
div.cpg_message_warn { background-color: #FEEFB3; color: #9F6000; border: 1px solid #A60; border-radius: 4px; padding: 1em; margin-bottom: 1em; }
div.loader { border: 2px solid #f3f3f3; border-radius: 50%; border-top: 2px solid blue; border-bottom: 2px solid blue; width: 16px; height: 16px; animation: spin 1s linear infinite; display: none; }
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="contain">
EOT;

$show_tags = isset($_GET['all']) ? true : false;
$updater = new CPG_Updater($show_tags);
if (isset($_POST['doinst'])) {
	$indir = ($_POST['inLoc'] == 's') ? 'cpg16x/' : '';
	$pstrun = ($_POST['inLoc'] == 'u') ? 'update.php' : 'install.php';
	$rslt = $updater->performUpdate($_POST['updid'], $indir);
	if ($rslt === 0) {
		msg_box($lang_install_php['files_placed_title'], $lang_install_php['files_placed_msg'], 'cpg_message_info', $lang_install_php['complete_install'], $pstrun);
		echo <<<EOT
		<a href="{$indir}{$pstrun}"><button>{$lang_install_php['complete_install']}</button></a>
EOT;
	} else {
		msg_box($lang_install_php['file_errs_title'], $lang_install_php['file_errs_msg'], 'cpg_message_warn', $lang_install_php['complete_install'], $pstrun);
	}
} else {

	echo <<<EOT
	<script type="text/javascript">
	function hasUpdSelect(frm)
	{
		if (!$("input[name='updid']:checked").val()) {
			alert('{$lang_install_php['select_install']}');
			return false;
		}
		$('#doinst').hide();
		$('#upding').show();
		return true;
	}
	</script>
EOT;

	$updates = $updater->getUpdates();
	//echo'<pre>';var_dump($updates);echo'</pre>';
	if ($updates) {
		$badDirs = $updater->checkCpgDirs();
		if ($badDirs) {
			$bdmsg = implode(',', $badDirs);
			msg_box('',$lang_install_php['not_writeable'].$bdmsg, 'cpg_message_error', '', '');
		}
		echo '<form id="updForm" action="" method="post" onsubmit="return hasUpdSelect(this);">';
		$baseURL = $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']);
		$dirURL = $baseURL . '/cpg16x';
		$urad = '';
		$cChk = 'checked ';
		if (file_exists('include/config.inc.php')) {
			include 'include/config.inc.php';
		}
		if (!empty($CONFIG['TABLE_PREFIX'])) {
			$urad = '<input type="radio" name="inLoc" id="inLoc0" value="u" checked /><label for="inLoc0">'.$lang_install_php['upgrade_current'].'</label><br/>';
			$cChk = '';
		}
		echo <<<EOT
	<div class="iloc">
		<span class="shead">{$lang_install_php['location']}</span><br />
		{$urad}
		<input type="radio" name="inLoc" id="inLoc1" value="c" {$cChk}/><label for="inLoc1">{$lang_install_php['loc_curdir']}</label> ({$baseURL})<br/>
		<input type="radio" name="inLoc" id="inLoc2" value="s" /><label for="inLoc2">{$lang_install_php['loc_separate']}</label> ({$dirURL})
	</div>
EOT;
		echo '<table class="updt">';
		echo '<tr><td colspan="2" class="lhead shead">'.$lang_install_php['available_packages'].'</td></tr>';
		if (!$show_tags) echo '<tr><td colspan="2"><a href="?all=1">'.$lang_install_php['show_all'].'</a></td></tr>';
		foreach ($updates as $k => $updt) {
			$xmsg = '';
			if ($updt['pre']) { $xmsg .= '<br />'.$lang_install_php['pre_warning']; }
			$tclass = $k%2 ? 'tableb_alternate' : 'tableb';
			echo <<<EOT
		<tr class="oblk {$tclass}">
			<td><input type="radio" name="updid" value="{$updt['id']}" id="t{$updt['id']}" /></td>
			<td><label for="t{$updt['id']}"><p><b>{$updt['name']}</b>{$xmsg}<br />{$updt['body']}</p></label></td>
		</tr>

EOT;
		}
		echo '<tr class="oblk"><td class="bleed">';
		echo '<input type="radio" name="updid" value="-1" id="bldv" />';
		echo '</td><td class="bleed">';
		echo '<label for="bldv"><p>Get the very latest package (unreleased) with all current developer changes.<br />Recommended only for special circumstances.</p></label>';
		echo '</td></tr></table>';
		if (!$badDirs) echo '<button type="submit" name="doinst" id="doinst" class="admin">'.$lang_install_php['perform_install'].'</button>';
		echo '<div id="upding" class="loader"></div>';
		echo '</form>';
	} else {
		msg_box($lang_install_php['no_installs_title'], $lang_install_php['no_installs_msg'], 'cpg_message_warning', '', '');
	}
}
echo <<<EOT
</div>
</body>
</html>
EOT;

function msg_box ($title, $message, $class, $button='', $link='')
{
	echo <<<EOT
	<div class={$class}><span class={$class}>{$message}</span></div>
EOT;
}

class CPG_Updater
{
	protected $updates = array();

	public function __construct ($tags)
	{
		global $lang_install_php;

		$urld = $this->getUrlData('https://api.github.com:443/repos/coppermine-gallery/cpg1.6.x/'.($tags ? 'tags' : 'releases'));
		$releases = json_decode($urld);
		if (!$releases) {
			die($lang_install_php['not_found']);
		}
		foreach ($releases as $r) {
		//	if ($r->prerelease) continue;	// don't offer any prereleases
			$tag = $tags ? $r->name : $r->tag_name;
			if (preg_match('/\d+\.\d+\.\d+/', $tag, $m)) {
				$this->updates[] = array(
					'id' => isset($r->id) ? $r->id : $r->node_id,
					'tag' => $m[0],
					'pre' => isset($r->prerelease) ? $r->prerelease : false,
					'ball' => $r->zipball_url,
					'tball' => $r->tarball_url,
					'name' => $r->name,
					'body' => $this->markd(nl2br(isset($r->body) ? $r->body : ''))
					);
			}
		}
	}

	public function getUpdates ()
	{
		return $this->updates;
	}

	public function performUpdate ($id, $inDir='')
	{
		global $lang_install_php;

		$updt = null;
		if ($id < 0) {
			$updt = array('tag'=>'v1.6.devlop', 'ball'=>'https://github.com/coppermine-gallery/cpg1.6.x/archive/develop.zip');
		} else foreach ($this->updates as $u) {
			if ($u['id'] == $id) {
				$updt = $u;
				break;
			}
		}

		if (!$updt) return -1;

		@unlink(UPDATER_LOG);
		$this->logIt("GETTING: {$updt['tag']}");
		$tmpd = rtrim(ini_get('open_basedir') ?: sys_get_temp_dir(), '/');
		$tmpf = ($tmpd ?: 'albums/edit').'/cpg_upd_'.time().'.zip';
		$newUpdate = $this->getUrlData($updt['ball']);
		$dlHandler = fopen($tmpf, 'w');
		if (!fwrite($dlHandler, $newUpdate)) { die($lang_install_php['save_error']); }
		fclose($dlHandler);

		$f2p = array('anycontent.php');	//files to preserve if they already exist
		$p2s = array();	//paths to skip (whether they exist or not)
		$errs = 0;
		$zip = new ZipArchive;
		$res = $zip->open($tmpf);
		if ($res === TRUE) {
			if ($inDir) {
				@mkdir($inDir);
				$this->logIt("DIR: {$inDir}");
			}
			for ($i = 0; $i < $zip->numFiles; $i++ ) {
				$stat = $zip->statIndex($i);
				list($bd,$fp) = explode('/', $stat['name'], 2);
				if (substr($fp, -1) == '/') {
					if ($this->matchedPath($fp, $p2s)) continue;
					$fp = $inDir . $fp;
					if (!is_dir($fp)) @mkdir($fp);
					$this->logIt("DIR: {$fp}");
				} elseif ($fp) {
					if (in_array($fp, $f2p) && file_exists($fp)) continue;
					if ($this->matchedPath($fp, $p2s)) continue;
					$fc = $zip->getFromIndex($i);
					$fp = $inDir . $fp;
					if (file_put_contents($fp, $fc) === false) {
						$this->logIt("!! FAILED TO PUT: {$fp}");
						echo "FAILED TO PUT: {$fp}<br>";
						$errs++;
					} else {
						$this->logIt("PUT: {$fp}");
					}
				}
			}
			$zip->close();
		} else {
			$errs++;
			echo 'failed, code:' . $res;
		}

		unlink($tmpf);
		return $errs;
	}

	public function checkCpgDirs ()
	{
		$cpgDirs = array('.');
		$ng = array();
		foreach ($cpgDirs as $dir) {
			if (!(is_dir($dir) && is_writable($dir))) $ng[] = $dir;
		}
		return $ng;
	}

	private function getUrlData ($url)
	{
		$ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14';
		if (ini_get('allow_url_fopen')) {
			ini_set('user_agent', $ua);
			$rfc = file_get_contents($url);		//var_dump(get_headers($url),$http_response_header);
			if (!$rfc) return $this->curly($url, $ua);
			return $rfc;
		} else {
			return $this->curly($url, $ua);
		}
	}

	private function curly ($url, $agent)
	{
		if (!function_exists('curl_init')) die($lang_install_php['not_possible']);
		// create curl resource
		$ch = curl_init();
		// set url
		curl_setopt($ch, CURLOPT_URL, $url);
		//return the transfer as a string
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_USERAGENT, $agent);
//		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
//		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		// $output contains the output string
		$output = curl_exec($ch);
		// close curl resource to free up system resources
		curl_close($ch);
		// return the data
		return $output;
	}

	private function matchedPath ($file, $paths)
	{
		foreach ($paths as $p) {
			$p = str_replace('/','\/',$p);
			if (preg_match("/^{$p}/", $file)) return true;
		}
		return false;
	}

	private function markd ($txt)
	{
		// emplasized
		$txt = preg_replace('#\*\*(.*)\*\*#','<span class="boldt">$1</span>',$txt);
		// link
		$txt = preg_replace('#\[(.*)\]\((.*)\)#','<a href="$2" target="_blank">$1</a>',$txt);
		return $txt;
	}

	private function logIt ($msg)
	{
		file_put_contents(UPDATER_LOG, "{$msg}\n", FILE_APPEND);
	}

}
//EOF
