<?php
###############################################################################
# Verify.php
#
#
# @author Anil Kumar <akumar@codepunch.com>
# @link   https://codepunch.com
#
############################################################################### 

namespace 	CodePunch\UI\Modules;

use 		CodePunch\Base\Util as UTIL;
use			CodePunch\Base\Text as TEXT;
use 		Exception;

###############################################################################

class Verify  {
	
	###########################################################################
	
	public function get($auth, $params=null) 
	{		
		if($params && is_array($params))
			$connection_error = implode("\n", $params);
		else  if($params) 
			$connection_error = $params;
		
		$result['header'] = "Database";
		$db = null;
		if($auth) {
			$db = $auth->getDatabase();
			$connparams = $auth->getDBALConnectionParams();
		}
		if($db == null) {
			try {
				$connparams = \CodePunch\Config\ConfigRoot::loadcfg();
				$db = new \CodePunch\DB\DomainDB($connparams, $connparams['prefix']);
			}
			catch(Exception $e) {
				$connection_error = $e->getMessage();
				$db = null;
			}
		}
		if($db) {
			$driver = "";
			$dbhost = "-";
			$platform = $db->connection->getDatabasePlatform();
			$driver = $platform->getName();
			$dbhost = UTIL::get_from_array($connparams['host'], "-");
			$dbuser = UTIL::get_from_array($connparams['user'], "-");
			if($dbuser != "")
				$cninfo = "User <code>$dbuser</code> connected to <code>$driver</code> server";
			else
				$cninfo  = "Connected to <code>$driver</code> server";
			if($dbhost != "")
				$cninfo .= " at $dbhost";
			$paras[] = "<p>$cninfo</p>";
			$paras[] = "<p>Server version is <code>" . $db->connection->getWrappedConnection()->getServerVersion() . "</code></p>";
			$result['status'] = "OK";
		}
		else {
			$pts = false;
			if($auth) {
				if($auth->validate($pts) != \CodePunch\Config\Auth::UNKNOWN)
					$connection_error = "Invalid Licence";
			}
			$paras[] = "<h4>Error: $connection_error</h4>";
			$result['status'] = "Error";
		}
		
		$result['paras'] = $paras;
		$output[] = $result;
			
		// We can run the tests now.
		$output = array_merge($output, $this->getTests($auth));
		
		// ... and show the results
		return $this->formatTestResults($output);
	}
	
	###########################################################################
	
	public function formatTestResults($results) 
	{
		if(UTIL::is_cli()) {
			$title  = "Verify Installation / Repair Tables";
			ob_start();
			foreach($results as $result) {
				UTIL::print("\n");
				$heading = UTIL::get_from_array($result['header'], "");
				$status = UTIL::get_from_array($result['status'], "");
				$paras = UTIL::get_from_array($result['paras'], array());
				$theheading = strip_tags(htmlspecialchars_decode($heading)) . ": " . strip_tags($status);
				UTIL::print($theheading);
				for($i = 0; $i < strlen($theheading); $i++)
					echo "-";
				UTIL::print("\n");
				
				$subheading = true;
				foreach($paras as $o) {
					if(stristr($o, "<h4>") !== false) {
						if($subheading == false)
							UTIL::print("\n");
						$subheading = true;
					}
					else
						$subheading = false;
					$thepara = strip_tags(htmlspecialchars_decode($o));
					if($subheading)
						$thepara = "-- $thepara --";
					UTIL::print($thepara);
					if($subheading)
						UTIL::print("\n");
				}
			}
			UTIL::print("\n");
			$block = ob_get_contents();
			ob_clean();
		}
		else {
			$title  = "<h1>Verify Installation / Repair Tables</h1>\n";
			$title .= "<p class=\"mb-0 text-center\">You can click on any of the panels below to see additional information.</p>";
			$block    = "<div id=\"testresults\">\n";
			foreach($results as $result) {
				$heading = UTIL::get_from_array($result['header'], "");
				$status = strtolower(UTIL::get_from_array($result['status'], ""));
				$paras = UTIL::get_from_array($result['paras'], array());
				$block .= "<div class=\"accordion-card\">\n";
				$block .= "<div class=\"theme-bg accordion accordion-$status\">$heading</div>\n";
				if(isset($paras[0]) && $paras[0] != strip_tags($paras[0])) {
					$block .= "<div class=\"accordion-panel\" style=\"padding-left: 15px; padding-right: 30px; overflow-x:auto;\">\n";
					foreach($paras as $para) 
						$block .= "$para\n";
					$block .= "</div>\n";
				}
				else {
					$block .= "<div>\n<ul class=\"list-group list-group-flush\">\n";
					foreach($paras as $para) 
						$block .= "<li class=\"list-group-item\">$para</li>\n";
					$block .= "</ul>\n</div>\n";
				}
				$block .= "</div>\n";
			}
			$block .= "</div>\n";
		}
		$folder = UTIL::get_install_folder_path();
		$home = file_get_contents($folder . 'lib/layouts/modules/dashboard/verify.htm');
		return array('heading'=>$title, 'body'=>$block, 'home'=>$home);
	}
	
	###########################################################################
	
	public function getTests($auth) 
	{
		$output = array();
		
		# PHP Version
		$result['status'] = "Info";
		$result['header'] = "PHP version &amp; Extensions";
		$paras = array();
		$phpver = phpversion();
		$paras[] = "<h4>PHP <code>$phpver</code> was loaded from the Ini file at <code>" . php_ini_loaded_file() . "</code></h4>";
		$paras[] = "<p>Watch My Domains SED v6 Standard / Demo / Professional Editions require PHP 8.1 and above.</p>";
		if(version_compare(PHP_VERSION, '8.1.0') >= 0)
			$result['status'] = 'ok';
		else 
			$result['status'] = "alert";
		
		$requiredext = array("Core", "PDO", "Reflection", "SimpleXML", "ctype", "curl", "date", "filter", "hash", "imap", "intl", "json", "mbstring", "mysqli", "openssl", "pcre", "session", "soap", "spl", "standard", "xdebug", "xml", "zip");
		$extensions = get_loaded_extensions(false);
		$paras[] = "<h4>Extensions</h4>";
		$extinfo = "<p>";
		foreach($extensions as $e) {
			$extinfo .= "$e, ";
		}
		foreach($requiredext as $e) {
			if(!UTIL::in_array_casei($e, $extensions))
				$extinfo .= "<code>$e</code>, ";
		}
		$extinfo = trim($extinfo, ", ");
		$paras[] = $extinfo . "</p>";
		
		$paras[] = "<h4>Curl / SSL</h4>";
		$testurl = "https://example.com";
		$paras[] = "<p>Attempting to get contents of <i><u>$testurl</u></i> using PHP: cURL</p>";
		$curltest = UTIL::curl_get_url($testurl);
		if(isset($curltest['error'])) {
			$paras[] = "<p><code>" . $curltest['error'] . "</code></p>";
			$result['status'] = "Error";
		}
		else {
			if(intval($curltest['status']) == 200) 
				$curlok = "OK";
			else {
				$result['status'] = "alert";
				$curlok = "Error";
			}
			$paras[] = "<p>HTTP Status Code: " . $curltest['status'] . " $curlok</p>";
		}
		
		if(!class_exists('ZipArchive')) {
			$paras[] = "<h4>ZipArchive</h4>";
			$paras[] = "<p>Missing ZipArchive PHP class. This is used by the command line tool (sed.php) for application updates.</p>";
			$result['status'] = "alert";
		}
		
		if(!function_exists('bcmod')) {
			$paras[] = "<h4>php-bcmath</h4>";
			$paras[] = "<p>Missing php-bcmath. This is used for SSL lookups.</p>";
			$result['status'] = "alert";
		}

		if (!extension_loaded('mbstring')) {
			$paras[] = "<h4>Multibyte String Support is Required!</h4>";
			$paras[] = "<p><code>Missing php multibyte string support (mbstring)</code></p>";
			$result['status'] = "Error";
		}

		$result['paras'] = $paras;
		
		$output[] = $result;
		unset($result);
		
		# App Folder
		$cronname = "Cron";
		$isWindows = false;
		if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
			$isWindows = true;
			$cronname = "Task Scheduler";
		}
		$result['header'] = "Folders and Paths (" . PHP_OS . ")";
		$result['status'] = "ok";
		$rooturl = UTIL::get_root_url();
		$paras = array();
		$paras[] = "<h4><span>Installation Folder</span></h4>";
		$paras[] = "<pre>" . UTIL::get_install_folder_path() . "</pre>";
		$paras[] = "<h4>Web URL</h4>";
		$paras[] = "<pre>" . $rooturl . "</pre>";
		if(!$isWindows) 
			$cronentry01 = "* * * * * php " .  UTIL::get_install_folder_path() . "cron.php >/dev/null 2>&1";
		else {
			$phpexepath = "path/to/php.exe";
			$phppath = getenv('PHPRC') . DIRECTORY_SEPARATOR . 'php.exe';
			if(is_file($phppath))
			  $phpexepath = $phppath;
			$cronentry01 = "$phpexepath \"" .  UTIL::get_install_folder_path() . "cron.php\"";
		}
		$paras[] = "<h4>Cron Entry</h4>";
		$paras[] = "<p>Use the following as your cron entry to manage the tasks.</p>";
		$paras[] = "<pre>"  . $cronentry01 . "</pre>";
		if($isWindows) {
			$paras[] = "<p>Remember to set the correct path to the command line PHP binary. Please see the documentation or README.txt for details on using the Task Scheduler to process the required tasks.</p>";
			$paras[] = "<p>Please see <a target=\"blank\" href=\"https://domainpunch.com/sed/guidev6/install/wintask.php\">Windows Task Scheduler Setup</a> (will open in new tab) for more details.</p>";
		}
		
		$paras[] = "<h4>Log Folder</h4>";
		$paras[] = "<pre>" . UTIL::get_log_folder_path() . "</pre>";
		$logfolder = UTIL::get_log_folder_path();
		$modename = UTIL::is_cli() ? "PHP CLI" : "web server";
		if(is_writable($logfolder))
			$paras[] = "<p>The log folder is currently <code>writable</code> from $modename.</p>";
		else {
			$paras[] = "<p>The log folder is currently <code>not writable</code> from $modename.</p>";
			$result['status'] = "alert";
		}
		$paras[] = "<p>The log folder can be changed in the <code>config.php</code>. This folder should be writable from both web server and PHP command line so that all the errors get logged.</p>";
		
		$result['paras'] = $paras;
		$output[] = $result;
		unset($result);
		
		if(UTIL::is_in_debug_mode() && $auth) {
			ob_start();
			phpinfo();
			$pinfo = ob_get_contents();
			ob_clean();
	
			$paras = array();
			$result['status'] = "info";
			$result['header'] = "PHP Info";
			$status['info'] = "Output from phpinfo()";
			$paras[] = $pinfo;
			$result['paras'] = $paras;
			$output[] = $result;
			unset($result);
		}
		
		$result['status'] = "info";
		$result['header'] = "ionCube Loader";
		$paras = array();
		$ioncubestatus = \CodePunch\Config\ConfigRoot::check_ioncube_status();
		if($ioncubestatus < 0) {
			$paras[] = "<p>Watch My Domains SED demo, basic and standard version installations require ionCube Loader v10 or greater. ";
			if($ioncubestatus == \CodePunch\Config\ConfigRoot::IONCUBE_REQUIRED_NOT_AVAILABLE)
				$paras[] = "It is not installed. ";
			else if($ioncubestatus == \CodePunch\Config\ConfigRoot::IONCUBE_REQUIRED_WRONG_VERSION)
				$paras[] = "A wrong version is installed. ";
			else if($ioncubestatus == \CodePunch\Config\ConfigRoot::IONCUBE_REQUIRED_UNSUPPORTED_PHP)
				$paras[] = "PHP 8.0 and 8.2 are currently not supported by ioncube loader. Please update or change PHP to 8.1.";
			$paras[] = "You can download and install ionCube loader for free from <a href=\"http://www.ioncube.com/loaders.php\" target=\"_blank\">ioncube.com</a>.</p>";
			$paras[] = "<p><b>Important:</b> Windows/Debian/Ubuntu servers may use a different php.ini file for CLI and so ionCube Loader may need to be configured in two places; first for Apache/Web Server and then for PHP command line. For example, WAMP server has the php.ini for CLI at wamp/bin/php/phpx.x.x/ folder and Debian has it at /etc/php5/cli/php.ini</p>";
			$result['status'] = 'Error';
		}
		else if($ioncubestatus == \CodePunch\Config\ConfigRoot::IONCUBE_NOT_REQUIRED) {
			$paras[] = "<p>You are using the Professional Edition, so ionCube Loader is not required.</p>";
			$result['status'] = 'ok';
		}
		else {
			$result['status'] = 'ok';
		}
		if(extension_loaded('ionCube Loader')) {
			$ion_status = UTIL::ioncube_loader_version_array();
			$ion_version = $ion_status['version'];
			$paras[]  = "<p>ionCube Loader v$ion_version is installed.</p>";
		}
		$result['paras'] = $paras;
		$output[] = $result;
		unset($result);
		
		$result['status'] = "Info";
		$result['header'] = "Time zones";
		$paras = array();
		
		$phptimezone = date_default_timezone_get();
		$paras[] = "<p>Current PHP Time: " . date("Y-m-d H:i:s") .  ", Time zone: <b>$phptimezone</b>" . "</p>";
		$ct = false;
		if($auth && $auth->getDatabase())
			$ct = $auth->getDatabase()->getCurrentTime();
		if($ct !== false) {
			$gmdiff = strtotime($ct) - strtotime(gmdate("Y-m-d H:i:s"));
			$mysqltimezone = timezone_name_from_abbr("", $gmdiff, false);
			$paras[] = "<p>Current Database Time: $ct, Time zone: <b>$mysqltimezone</b></p>";
			$tdiff = strtotime(date("Y-m-d H:i:s")) - strtotime($ct);
			if(abs($tdiff) > 1) {
				$result['status'] = "ok";
				$paras[] = "<p>There is a time zone difference between the Database server and PHP. This is normally not a problem but please be aware that all date and timestamps stored in database tables are based on the PHP session time zone.</p>";
			}
			else
				$result['status'] = "ok";
		}
		else {
			$result['status'] = "info";
		}
		$paras[] = "<p>Please verify that the PHP time is same when run from command line. Windows/Debian/Ubuntu servers may use a different php.ini file for CLI and so may have a different timezone setting in CLI. This is unlikely to actually happen, but is mentioned here because this problem is very difficult to debug if it happens. You can run verify.php from the console (php /path/to/verify.php) and check that the time shown there matches with this.</p>";

		$result['paras'] = $paras;
		$output[] = $result;
		unset($result);
		
		$result['status'] = "Info";
		$result['header'] = "Whois";
		$paras = array();

		$server = "whois.crsnic.net";
		$wout = UTIL::Whois("domain example.com", $server);
		if($wout['status'] === false) {
			$result['status'] = "error";
			if($wout['errno'] == 13)
				$paras[] = "<p>Please <b>check if SELinux is enabled</b>.</p>";
			$paras[] = "<p>You may want to see <a target=\"_blank\" href=\"https://domainpunch.com/sed/guidev6/install/troubleshooting.php\">the trouble shooting page</a></p>";
			$paras[] = "<p>Unable to make a port 43 whois connection to whois.crsnic.net!</p>";
			$whoisdata = "Error - unable to connect to $server.\n\n" . $wout['error'] . "\n\n";
			$paras[] = "<p>" . $whoisdata . "</p>";
		}
		else {
			$paras[] = "<p>Able to make port 43 whois connections</p>";
			$result['status'] = "ok";
			if(!UTIL::is_cli())
				$paras[] = "<p>" . str_replace("\n", "<br>", $wout['data']) . "</p>";
		}

		$result['paras'] = $paras;
		$output[] = $result;
		unset($result);
		
		return $output;
	}
	
	###########################################################################
	
	public static function show($auth, $error=null)
	{
		if(UTIL::is_cli()) {
			$verify = new \CodePunch\UI\Modules\Verify();
			$vdata = $verify->get($auth, $error);
			echo "\n";
			UTIL::print($vdata['heading']);
			UTIL::print($vdata['body']);
			exit;
		}
	}
	
	###########################################################################
}
