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

namespace 	CodePunch\Config;
use 		CodePunch\Base\Util as UTIL;
use			CodePunch\Base\Text as TEXT;
use			CodePunch\DB\Audit as AUDIT;

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

class Tasks  {
	
	private $authentication = null;
	private $webshotscript = "";
	private $webshotfolder = "";
	
	public function __construct($auth=null) {
		$this->authentication = $auth;
	}
	
	///////////////////////////////////////////////////////////////////////////
	// Start of User callbale tasks
	// Update API.php getTasksList with names of acceptable tasks
	// when new tasks are added here.
	public static function set_RDAP_Servers($auth)
	{
		if($auth)
			\CodePunch\LU\RDAP::fill($auth->getDatabase());
	}
	
	public static function import_SSL_Certificates($auth)
	{
		if($auth)
			$auth->getDatabase()->importSSLCertsFromSubdomains($auth, true);
	}
	
	// End of User callbale tasks
	///////////////////////////////////////////////////////////////////////////
	
	public function getAuthentication() {
		return $this->authentication;
	}
	
	// This is called from cron to do any periodic tasks during idle time
	public function periodic($lookupManager) {
		if(UTIL::is_cli()) {
			$this->doWebshotsIfEnabled();
			if($this->getAuthentication()->getElapsedTime() < 5) {
				// If PRO refresh IP data
				$this->doIPDataRefresh($lookupManager);
			}
			if($this->getAuthentication()->getElapsedTime() < 5) {
				$DNS = new \CodePunch\LU\DNS($lookupManager);
				$DNS->scanForSubdomains("auto", 5, 0, "", 5, 250);
			}
			// 2025 June
			// Run RDAP Server refresh once every day.
			$setup = new \CodePunch\Config\Settings($this->getAuthentication());
			$lastrdaprun = $setup->getOption("rdap_server_init_run", "");
			if($lastrdaprun == "" || ($lastrdaprun != "" && UTIL::is_a_date($lastrdaprun))) {
				if((time() - strtotime($lastrdaprun)) > (3600*24)) {
					$setup->setOption("rdap_server_init_run", date("Y-m-d H:i:s"));
					\CodePunch\LU\RDAP::fill($this->authentication->getDatabase());
				}
			}
			// End 2025 June
		}
	}
	
	private function doIPDataRefresh($lookupManager) {
		UTIL::debug_cli_print("IP Data Refresh.");
		$ipv4class = $this->getAuthentication()->getProClass('IPV4');
		if($ipv4class) {
			$logs = array();
			$db = $this->getAuthentication()->getDatabase();
			$days = 15;
			$checkdate = date("Y-m-d H:i:s", time() - ($days*24*3600));
			$rows = $db->getFromTable("*", $db->getIPV4TableName(), "ipas_checked_at IS NULL OR ipas_checked_at < ?", array($checkdate), "ipas_checked_at", "asc", 5);
			if($rows !== false && count($rows)) {
				UTIL::debug_cli_print($rows);
				foreach($rows as $r) {
					$ipv4 = $r['ipv4'];
					$ipv4class->updateIPV4($r, $logs);
				}
				if(count($logs)) {
					$lookupManager->emailDNSAlerts($logs);
					AUDIT::add($db, AUDIT::DNS_RECORD_CHANGE, $logs);
				}
			}
		}
	}
	
	private function doWebshotsIfEnabled() {
		$setup = new \CodePunch\Config\Settings($this->getAuthentication());
		$dowebshots = $setup->getBoolean('do_webshots', false);
		if($dowebshots) {
			$wsdomain = $setup->getOption("webshot_domains", "[IN_Business Domains] OR [IN_Important Domains] OR [IN_Webshot Domains]");
			$wsdays = intval($setup->getOption("webshot_days", "30"));
			$wsmaxcount = intval($setup->getOption("webshot_maxcount", "1"));
			$nodejsoptions = $setup->getOption("webshot_nodejsoptions", "--ac");
			$nodejsoptions = str_ireplace(array("|","\r","\t"), "\n", $nodejsoptions);
			$nsjoptions = explode("\n", $nodejsoptions);
			return $this->webshots($wsdomain, $wsdays, $wsmaxcount, $nsjoptions);
		}
		else
			UTIL::debug_cli_print("Webshots disabled.");
	}
	
	public function webshots($domain="", $days=14, $maxcount=1, $nodejsoptions=array("--ac")) {
		$fp = UTIL::get_lock_file("websites");
		if($fp) {
			$this->runWebshots($domain, $days, $maxcount, $nodejsoptions);
			flock($fp, LOCK_UN);
			fclose($fp);
			return true;
		}
		else {
			UTIL::debug_cli_print("Unable to get lock file for webshots.\n");
		}
		return false;
	}
	
	private function runWebshots($domain="", $days=14, $maxcount=4, $nodejsoptions=[]) {
		$db = $this->getAuthentication()->getDatabase();
		$this->webshotscript = "";
		if($this->webshotscript == "") {
			$this->webshotfolder = "websites";
			$nodejsfile = "websites.js";
			$outfolder = UTIL::get_log_folder_path() . $this->webshotfolder . DIRECTORY_SEPARATOR;
			if(is_dir($outfolder)) {
				if(!is_file($outfolder . $nodejsfile)) {
					copy(UTIL::get_install_folder_path() . 'lib' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . $nodejsfile, $outfolder . $nodejsfile);
				}
			}
			if(!is_file($outfolder . $nodejsfile)) {
				// Support version 4 webshots settings and folders
				$this->webshotfolder = "webshots";
				$outfolder = UTIL::get_log_folder_path() . $this->webshotfolder . DIRECTORY_SEPARATOR;
				$nodejsfile = "shot.js";
				if(!is_file($outfolder . $nodejsfile))
					UTIL::debug_cli_print("Missing script file for webshot generation.");
			}
			
			$puppeteer = $outfolder . "node_modules" . DIRECTORY_SEPARATOR . "puppeteer" . DIRECTORY_SEPARATOR . "package.json";
			if(is_file($outfolder . $nodejsfile) && is_file($puppeteer)) {
				chdir($outfolder);
				$this->webshotscript = "node --unhandled-rejections=strict $nodejsfile -u http://{{DOMAIN}}";
				foreach($nodejsoptions as $opt) {
					$opt = trim($opt);
					if($opt != "")
						$this->webshotscript .= " " . $opt;
				}
			}
		}
		else {
			if(!is_file($this->webshotscript)) {
				UTIL::debug_cli_print("Missing script file for webshot generation.");
				$this->webshotscript = "";
			}
		}
		if($this->webshotscript != "") {
			$dc = date("Y-m-d H:i:s", time()-($days*24*3600));
			$sql = "(websites_checked_at IS NULL OR websites_checked_at < ?) AND home_page_url IS NOT NULL";
			$params = array($dc);
			if($domain != "") {
				if(UTIL::starts_with($domain, "[") && UTIL::ends_with($domain, "]")) {
					$sql .= " AND (" . $db->customExpandWhere($domain) . ")";
				}
				else if(strtolower($domain) != "all") {
					$params[] = $domain;
					$sql .= " AND domain=?";
				}
			}
			else {
				$sql .= " AND " . $db->customExpandWhere("([IN_Business Domains] OR [IN_Important Domains])");
			}
			$rows = $db->getFromTable("sid,domain,websites_checked_at,redirect_last_url", $db->getDomainTableName() . " d", $sql, $params, 'websites_checked_at', 'asc');
			if($rows !== false) {
				$count = count($rows);
				if(!$count)
					UTIL::debug_cli_print("No candidate domains found for webshots");
				$index = 0;
				foreach($rows as $di) {
					$di = array_change_key_case($di, CASE_LOWER);
					$domain = $di['domain'];
					$url = $di['redirect_last_url'];
					$sid = $di['sid'];
					$index++;
					$perc = intval(($index/$count)*100);
					UTIL::debug_cli_print("\t($perc%)\t$index/$count] $domain");
					$this->webshot($sid, $domain, $url);
					if($index >= $maxcount && $maxcount)
						break;
				}
			}
			else
				UTIL::debug_cli_print(\CodePunch\Base\RecentlyLoggedErrors::getLast());
		}
	}
	
	private function webshot($sid, $domain, $url) {
		$db = $this->getAuthentication()->getDatabase();
		$cookiefolder = UTIL::get_log_folder_path() . $this->webshotfolder . DIRECTORY_SEPARATOR . "cookies" . DIRECTORY_SEPARATOR;
		$start_ts = date('Y-m-d H:i:s');
		$cm = $this->getAuthentication()->getProClass('Cookies');
		// Find all subdomains
		$badsubdomains = ['mail'];
		for($i = 0; $i < 20; $i++) {
			$badsubdomains[] = 'ns' . (string)$i;
			$badsubdomains[] = 'dns' . (string)$i;
		}
		$query = "d.sid = ? AND s.auto_added = 0 AND s.subdomain NOT IN (?)";
		$select = "s.hid,s.subdomain,s.hostname";
		$iglist = array($badsubdomains, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
		$data = $db->getDomainReportRows($query, array($sid, $iglist), $select, "d.sid", "asc");
		if($data !== false && is_array($data) && count($data)) {
			foreach($data as $di) {
				$current_ts = date("Y-m-d H:i:s");
				$hostname = str_replace("@.", "", $di['s_hostname']);
				$subdomain = $di['s_subdomain'];
				$sd = str_replace("@", "", $subdomain);
				$ascii_hostname = UTIL::idn_convert_to_host_name($domain, $sd);
				UTIL::debug_cli_print("\t\t".$hostname . ": " . $ascii_hostname);
				$command = $this->webshotscript;
				if(stripos($command, "{{DOMAIN}}") !== false)
					$command = str_ireplace("{{DOMAIN}}", $ascii_hostname, $command);
				else
					$command .= " $ascii_hostname";
				exec($command, $output, $retval);
				$db->deleteFromTable($db->getSubdomainTableName(), "(auto_added=? OR auto_added = ?) AND sid=? AND subdomain=?", array(\CodePunch\LU\LookupManager::SD_WEBSHOT_ROWS, \CodePunch\LU\LookupManager::SD_COOKIE_ROWS, $sid, $subdomain));
				$da = array('sid'=>$sid, 'auto_added'=>\CodePunch\LU\LookupManager::SD_WEBSHOT_ROWS, 'added_on'=>$current_ts, 'subdomain'=>$subdomain);
				$db->insertIntoTable($db->getSubdomainTableName(), $da);
				
				if($cm) {
					$jsonfile = $cookiefolder . $ascii_hostname . ".json";
					$cm->save($sid, $url, $domain, $subdomain, $current_ts, $jsonfile);
				}
			}
		}

		$domain_data = array();
		$domain_data['domain'] = $domain;
		$domain_data['websites_checked_at'] = $start_ts;
		$db->updateDomainTable($domain_data);
	}
}
