<?php
/*
 * Copyright (c) 2025, Tribal Limited
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Zenario, Tribal Limited nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TRIBAL LTD BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
if (!defined('NOT_ACCESSED_DIRECTLY')) exit('This file may not be directly accessed');





//Check cookie acceptence.
//Caching supports the simple options; i.e. undecided (""), accepted ("a") and rejected ("r").
//If someone accepts some but not others, we don't support caching like this and should turn caching off.
$simpleCookieOptions = true;
$cookiesAccepted = $_COOKIE['z_cookies_accepted'] ?? '';
if ($cookiesAccepted && $cookiesAccepted != '1') {
	$cookiesAccepted = array_flip(explode(',', $cookiesAccepted));
	
	$numCookieOptions =
		(int) isset($cookiesAccepted['functionality'])
	 +	(int) isset($cookiesAccepted['analytics'])
	 +	(int) isset($cookiesAccepted['social_media']);
	
	$simpleCookieOptions = $numCookieOptions == 3;
}


//A couple more checks for whether caching should be on or off.
//Don't allow page caching if an Admin is logged in.
//Don't allow it if a Visitor is not logged in as an Extranet User, but has the z_extranet_auto_login Cookie set,
//as they're probably about to be automatically logged in.
if ($simpleCookieOptions
 && !isset($_SESSION['admin_logged_into_site'])
 && !(empty($_SESSION['extranetUserID']) && isset($_COOKIE['z_extranet_auto_login']))) {
	
	//Work out what cache-flags to use:
		//u = extranet user logged in
		//g = GET request present that is not registered using registerGetRequest() and is not a CMS variable
		//p = POST request present
		//s = SESSION variable present that is not in the exception list
		//c = COOKIE present that is not in the exception list
	//We can work all of these out exactly except for "g", as registerGetRequest() lets module developers register
	//anything dynamically. There's a bit of logic later that handles this by checking both cases.
	
	ze::$saveEnv = [];
	ze::$saveEnv['u'] = 'u';
	ze::$saveEnv['g'] = 'g';
	ze::$saveEnv['s'] = 's';
	
	ze::$cacheEnv = [];
	ze::$cacheEnv['u'] = '';
	ze::$cacheEnv['g'] = '';
	ze::$cacheEnv['s'] = '';
	
	if (!empty($_SESSION['extranetUserID']) || isset($_COOKIE['z_extranet_auto_login'])) {
		ze::$cacheEnv['u'] = 'u';
	}
	if (!empty($_POST)) {
		ze::$cacheEnv['g'] = 'g';
	}
	
	//Look out for core requests. These should be stored separately.
	//Also, to save space, we'll shorten the names.
	ze::$knownReq = [];
	foreach ($_REQUEST as $request => &$value) {
		if (isset(ze::$cacheCoreVars[$request])) {
			$request = ze::$cacheCoreVars[$request];
			ze::$knownReq[$request] = $value;
		}
	}
		
	//Note down any non-core GET requests
	ksort($_GET);
	ze::$allReq = ze::$knownReq;
	foreach ($_GET as $request => &$value) {
		
		//PHP has a feature where request keys can be numeric.
		//We never use this, but if people try to hack the URL they can trigger it. This line is
		//here to prevent a PHP error if that has happened.
		$request = (string) $request;
		
		if (!isset(ze::$cacheCoreVars[$request])) {
			//Use "z" as an escape character.
			//Anything that's one character long, or already begins with a z, should have a z put in front of it.
			if ($request[0] === 'z' || !isset($request[1])) {
				$request = 'z'. $request;
			}
			ze::$allReq[$request] = $value;
		}
	}
	
	
	
	foreach ($_COOKIE as $request => &$value) {
		if (!ze\cache::friendlyCookieVar($request)) {
			ze::$cacheEnv['s'] = 's';
			break;
		}
	}
	
	foreach ($_SESSION as $request => &$value) {
		if (!ze\cache::friendlySessionVar($request)) {
			ze::$cacheEnv['s'] = 's';
			break;
		}
	}
	unset($value);
	
	
	//Get two checksums from the GET requests.
	//$chDirAllRequests is a checksum of every GET request
	//$chDirKnownRequests is a checksum of just the CMS variable, e.g. cID, cType...
	$chDirAllRequests = ze\cache::pageRequestHash(ze::$allReq);
	$chDirKnownRequests = ze\cache::pageRequestHash(ze::$knownReq);
	
	//Loop through every possible combination of cache-flag
	//(I've tried to order this by the most common settings first,
	//to minimise the number of loops when we have a hit.)
	for ($chS = 's';; $chS = ze::$cacheEnv['s']) {
			for ($chG = 'g';; $chG = ze::$cacheEnv['g']) {
					for ($chU = 'u';; $chU = ze::$cacheEnv['u']) {
							
							
							//Plugins can opt out of caching if there are any unrecognised or
							//unregistered $_GET requests.
							//If this is the case, then we must insist that the $_GET requests
							//of the cached page match the current $_GET request - i.e. we
							//must use $chDirAllRequests.
							//If this is not the case then we must check both $chDirAllRequests
							//and $chDirKnownRequests as we weren't exactly sure of the value of "g"
							//as mentioned above.
							if ((file_exists(($chPath = 'cache/pages/'. $chDirAllRequests. $chU. $chG. $chS. '/'). 'page.html'))
							 || ($chG && (file_exists(($chPath = 'cache/pages/'. $chDirKnownRequests. $chU. $chG. $chS. '/'). 'page.html')))) {
								
								ze\cache::logStats(['hits', 'total']);
								touch($chPath. 'accessed');
								
								
								//Try and record the destCID and destCType as we would a normal page view
								if ($tagId = file_get_contents($chPath. 'tag_id')) {
									$tag = explode('_', $tagId, 2);
									if (isset($tag[1])) {
										if ($cID = (int) $tag[1]) {
											$_SESSION['destCID'] = $cID;
											$_SESSION['destCType'] = $tag[0];
										}
									}
								}
							
							
								//If there are cached images on this page, mark that they've been accessed
								if (file_exists($chPath. 'cached_files')) {
									foreach (file($chPath. 'cached_files', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $cachedImage) {
										if (is_dir($cachedImage)) {
											touch($cachedImage. 'accessed');
										} else {
											//Delete the cached copy as its images are missing
											ze\cache::deleteDir($chPath);
										
											//Continue the loop looking for any more cached copies of this page.
											//Most likely if any exist they will need deleting because their images will be missing too,
											//and it's a good idea to clean up.
											continue 2;
										}
									}
								}
								
								//When using implied consent, watch out for the flag to set the $_SESSION['z_cookies_accepted'] variable
								if (empty($_COOKIE['z_cookies_accepted']) && file_exists($chPath. 'consent_implied')) {
									$_SESSION['z_cookies_accepted'] = true;
								}
								
								//Output the contents of the page, being careful to replace the [[%browser%]] string with the actual browser class
								$page = file_get_contents($chPath. 'page.html');
								if (false !== $pos = strpos($page, '<body class="desktop no_js [[%browser%]]')) {
									echo substr($page, 0, $pos), '<body class="desktop no_js '. ze\cache::browserBodyClass(), substr($page, $pos + 40);
								} else {
									echo $page;
								}
								
								//Check to see if the "caching_debug_info" and "limit_caching_debug_info_by_ip"
								//site settings were set. We can't check the database, but we'll write some info
								//in a file to remember what the settings were. We'll get away with doing this
								//as the cache directory is cleared when these settings are changed.
								if (file_exists($chPath. 'show_cache_info')) {
									$limit_caching_debug_info_by_ip = file_get_contents($chPath. 'show_cache_info');
									
									//Show the debug info to this visitor if they should see it.
									if (ze\cache::shouldSeeDebugInfo($limit_caching_debug_info_by_ip)) {
										ze\cache::showDebugInfo(true);
									}
								}
								
								echo "\n</body>\n</html>";
								exit;
							}
						
						if ($chU == ze::$cacheEnv['u']) break;
					}
				if ($chG == ze::$cacheEnv['g']) break;
			}
		if ($chS == ze::$cacheEnv['s']) break;
	}
}