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

namespace 	CodePunch\UI;

use 		CodePunch\Base\Util as UTIL;
use			CodePunch\Base\Text as TEXT;
use			CodePunch\DB\Audit as AUDIT;
use 		CodePunch\Base\CPLogger;
use 		Exception;

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

class API {
	
	public $authentication = null;
	
	###########################################################################
	
	public function __construct($auth)
	{ 
		$this->authentication = $auth;
	}
	
	###########################################################################
	
	public function init()
	{
		$responce = array('status'=>'notok', 'error'=>'');
		try {
			$cmd = UTIL::get_sanitized_request_string("c", "");
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			$swipein = UTIL::get_request_data_boolean("sw", true);
			$responce['validate'] = $this->authentication->validateSession(false, false);
			
			// Get Authentications
			// Loggedin? Timedout?
			if($cmd == "grid")
				$this->authentication->authorizationQuit($responce['validate']);
			else if($responce['validate'] != \CodePunch\Config\Auth::VALID)
				throw new Exception(TEXT::get("notloggedin"));
			
			// Does the user have any rights?
			$rights = $this->authentication->getUserAccess();
			
			if(!$rights)
				throw new Exception(TEXT::get("user_permdenied"));
			
			if($cmd == "get" || $cmd == "list" || ($cmd == "grid" && ($oper == "" || $oper== "view"))) {
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_VIEW))
					throw new Exception(TEXT::get("user_perm_view_denied"));
			}
			if($cmd == "set" && $target == "luq") {		
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_LOOKUP))
					throw new Exception(TEXT::get("user_perm_lookup_denied"));
			}
			if($cmd == "set" && $oper == "colmodel") {		
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_UICHANGE))
					throw new Exception(TEXT::get("user_perm_ui_denied"));
			}
			if($cmd == "set" && $oper == "datafields") {		
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_UICHANGE))
					throw new Exception(TEXT::get("user_perm_ui_denied"));
			}
			
			// Background Task
			if($cmd == "set" && $target == "task") {
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
					throw new Exception(TEXT::get("user_permdenied"));
			}

			//  Category Editing
			if( ($cmd == "set" && $target == "category") ||
				($cmd == "get" && $target == "category") ||
				($cmd == "grid" && $target == "category" && $oper == "edit")
				){
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL &&
					(!($rights&\CodePunch\DB\DomainDB::ALLOW_CATEGORY_EDIT) ||
					!($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER))
				) 
					throw new Exception(TEXT::get("user_perm_categoryedit_denied"));
			}
			
			//  Query Editing
			if( ($cmd == "set" && $target == "query") ||
				($cmd == "get" && $target == "query") ||
				($cmd == "grid" && $target == "query" && $oper == "edit")
				){
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL &&
					(!($rights&\CodePunch\DB\DomainDB::ALLOW_AUTOQUERY_EDIT) ||
					!($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER))
				) 
					throw new Exception(TEXT::get("user_perm_queryedit_denied"));
			}

			//
			if($cmd == "set" && ($target == "suffixlist" || $target == "whois" || $target == "rdap" || $target == "proxy")) {
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
					throw new Exception(TEXT::get("user_perm_edit_denied"));
			}
			
			// Admin level rquired for seeing proxy settings
			if($cmd == "get" && $target == "proxy") {
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
					throw new Exception(TEXT::get("user_permdenied"));
			}

			// Any set command or grid edit command
			if($cmd == "set" || ($cmd == "grid" && $oper == "edit")) {
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_EDIT))
					throw new Exception(TEXT::get("user_perm_edit_denied"));
			}
			
			//  Category Add / Del
			if( ($cmd == "del" && $target == "category") ||
				($cmd == "grid" && $target == "category" && $oper == "del") ||
				($cmd == "grid" && $target == "category" && $oper == "add") 
				){
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
					throw new Exception(TEXT::get("user_perm_categoryedit_denied"));
			}
			
			//  Query Add / Del
			if( ($cmd == "del" && $target == "query") ||
				($cmd == "grid" && $target == "query" && $oper == "del") ||
				($cmd == "grid" && $target == "query" && $oper == "add") 
				){
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
					throw new Exception(TEXT::get("user_perm_queryedit_denied"));
			}
			
			if($cmd == "add" || ($cmd == "grid" && $oper == "add")) {			
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_ADD))
					throw new Exception(TEXT::get("user_perm_add_denied"));
			}
			
			if($cmd == "del" || ($cmd == "grid" && $oper == "del")) {		
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_DELETE))
					throw new Exception(TEXT::get("user_perm_delete_denied"));
			}
			if($cmd == "admin") {
				if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
					throw new Exception(TEXT::get("user_perm_admin_denied"));
			}
			if($cmd == "download" && $oper == "del") {
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_DELETE))
					throw new Exception(TEXT::get("user_perm_delete_denied"));
			}
			if($cmd == "download") {
				if(!($rights&\CodePunch\DB\DomainDB::ALLOW_DOWNLOAD))
					throw new Exception(TEXT::get("user_perm_download_denied"));
			}
			
			// Reset session timeout counter if everything is OK
			if($responce['validate'] == \CodePunch\Config\Auth::VALID && $swipein)
				$this->authentication->swipeIn();
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$responce['error'] = $e->getMessage();
			echo json_encode($responce);
			exit;
		}
		return $responce;
	}
	
	###########################################################################
	
	public function grid()
	{
		$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
		if($oper == "add" || $oper == "del" || $oper == "edit")
			return $this->edit_grid();
		else if($oper == "" || $oper == "get")
			return $this->get_grid();
	}
	
	###########################################################################
	
	private function get_grid()
	{
		$responce = $this->init();
		try {
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$auth = $this->authentication;
			$db = $auth->getDatabase();
			$cid = UTIL::get_request_data_integer("cid", 0);
			$columns = trim(UTIL::get_sanitized_request_string("columns", ""), " \r\n\t,");
			$page = UTIL::get_request_data_integer("page", 1);
			$limit = UTIL::get_request_data_integer("rows", 20);
			$sidx = UTIL::get_sanitized_request_string("sidx", "");
			$sord = strtolower(UTIL::get_sanitized_request_string("sord", "asc"));
			$sord = ($sord != "asc" && $sord != "desc") ? "asc" : $sord;
			$tablealias = "";
			$params = array();			
			$grid = new \CodePunch\UI\DataGrid($auth);
			
			// Sanitize the sort column if the grid is for a report
			$reportname = UTIL::get_sanitized_request_string("_repname", "");
			// d.sid/sid is used as default sort column by the reportgrid JS
			// Change from default to the report's native sort column
			if($reportname != "" && ($sidx == "" || $sidx == "sid" || $sidx == "d.sid")) {
				$sortcolumn = $db->findOneOf($db->getReportSchedulerTableName(), "name", $reportname, "sortcolumn");
				if($sortcolumn !== false && $sortcolumn != "") {
					$sidx = $sortcolumn;
				}
			}
			if($columns != "") {
				if($target == "domain") {
					$tablealias = "";//"d.";  // TODO: Fix this!! use d. ?
					$where = $this->getWhereForDomains($grid, $tablealias, $params);
					$hdays = UTIL::get_request_data_integer("highlight", \CodePunch\DB\DomainDB::DEFAULT_HIGHLIGHT_DAYS);
					$db->setRowHighlights($hdays);
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridDomainTableData($db->getDomainTableName(), "sid", $columns, $sidx, $sord, $cid, $where, $params, array(), $page, $limit);
				}
				else if($target == "category") {
					$validcids = $auth->isAdmin() ? array() : $db->getCategoryIDsForCurrentUser($auth);
					if(!in_array(1, $validcids) && count($validcids))
						$validcids[] = 1;
					$where = $this->getWhere($grid, $tablealias, $params);
					if(count($validcids)) {
						$where[] = "cid in (?)";
						$params[] = array($validcids, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
					}
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridTableData($db->getCategoryTableName(), "cid", $columns, $sidx, $sord, $where, $params, $page, $limit);
					// Block the total domain count for non-admin users
					if(isset($responce->rows[0]['id']) && isset($responce->rows[0]['dcount'])) {
						$rights = $auth->getUserAccess();
						if($responce->rows[0]['id'] == 1 && !$auth->isAdmin() && !($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER))
							$responce->rows[0]['dcount'] = "-";
					}
				}
				else if($target == "query") {
					$where = $this->getWhere($grid, $tablealias, $params);
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridTableData($db->getAutoQueryTableName(), "id", $columns, $sidx, $sord, $where, $params, $page, $limit);
				}
				else if($target == "dns") {
					$where = $this->getWhere($grid, $tablealias, $params);
					$where[] = "sid = ? AND (auto_Added=?)";
					$params[] = UTIL::get_request_data_integer("sid", 0);
					$params[] = \CodePunch\LU\LookupManager::SD_AUTO_DNS_ROWS;
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridTableData($db->getSubdomainTableName(), "hid", $columns, $sidx, $sord, $where, $params, $page, $limit);
				}
				else if($target == "ssl") {
					$where = $this->getWhere($grid, $tablealias, $params);
					$where[] = "sid = ? AND (auto_Added=?)";
					$params[] = UTIL::get_request_data_integer("sid", 0);
					$params[] = \CodePunch\LU\LookupManager::SD_AUTO_SSL_ROWS;
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridTableData($db->getSubdomainTableName(), "hid", $columns, $sidx, $sord, $where, $params, $page, $limit);
				}
				else if($target == "suffixlist") {
					$where = $this->getWhere($grid, $tablealias, $params);
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridTableData($db->getDomainSuffixListTableName(), "id", $columns, $sidx, $sord, $where, $params, $page, $limit);
				}
				else if($target == "tlds") {
					$where = $this->getWhere($grid, $tablealias, $params);
					$where = implode(" AND ", $where);
					$responce = $grid->jqGridTableData($db->getTLDsTableName(), "tld", $columns, $sidx, $sord, $where, $params, $page, $limit);
				}
				else if($target == "sslcerts") {
					if(!$auth->getProClass('SSL', true))
						throw new Exception(TEXT::get("require_pro_edition"));
					$rights = $this->authentication->getUserAccess();
					if($this->authentication->isAdmin() || ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER)) {
						$days = UTIL::get_request_data_integer("highlight", \CodePunch\DB\DomainDB::DEFAULT_HIGHLIGHT_DAYS);
						$daysinfuture = date("Y-m-d", time() + $days*24*3600);
						$queries = array(
							"UPDATE " . $db->getSSLCertificateTableName() . " SET r_h_disp = 0",
							"UPDATE " . $db->getSSLCertificateTableName() . " SET r_h_disp = 15 WHERE valid_to IS NOT NULL AND valid_to <  '$daysinfuture'",
						);
						foreach($queries as $query) {
							try {
								if($query != "") {
									$statement = $db->connection->prepare($query);
									$statement->execute();
								}
							}
							catch (PDOException $e) {
								$db->setError($e->getMessage());
							}
						}
						
						$where = $this->getWhere($grid, $tablealias, $params);
						$where = implode(" AND ", $where);
						$responce = $grid->jqGridTableData($db->getSSLCertificateTableName(), "id", $columns, $sidx, $sord, $where, $params, $page, $limit);
					}
					else
						throw new Exception(TEXT::get("user_permdenied"));
				}
				else 
					throw new Exception(TEXT::get("code_ui_bad_parameters", "action"));
				
			}
			else
				throw new Exception(TEXT::get("code_ui_bad_parameters", "columns"));
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$responce['error'] = $e->getMessage();
		}
		return json_encode($responce, UTIL::is_in_debug_mode() ? JSON_PRETTY_PRINT : 0);
	}
	
	###########################################################################
	
	private function edit_grid()
	{
		$responce = $this->init();
		try {
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			if($target == "domain") 
				$responce = $this->domainGridEdit($responce);
			else if($target == "category") 
				$responce = $this->categoryGridEdit($responce);
			else if($target == "query") 
				$responce = $this->autoQueryGridEdit($responce);
			else if($target == "dns") 
				$responce = $this->dnsGridEdit($responce);
			else if($target == "ssl") 
				//$responce = $this->sslGridEdit($responce);
				$responce = $this->dnsGridEdit($responce);
			else if($target == "sslcerts") 
				$responce = $this->sslcertGridEdit($responce);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$response['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce);
	}
	
	###########################################################################
	
	public function add() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			if($target == "domain" && $oper == "")
				$responce = $this->addDomains($responce);
			else if($target == "domain" && $oper == "csv")
				$responce = $this->importData($responce);
			else if($target == "domain" && $oper == "subdomains")
				$responce = $this->addSubdomains($responce);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$response['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce);
	}
	
	###########################################################################
	
	public function download() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			
			if($target == "post") 
				$responce = $this->exportPostData($responce);
			else if($target == "domain") 
				$responce = $this->downloadCSVData($responce);
			else if($target == "csvdata" && $oper == "get")
				$responce = $this->getScheduledReport($responce);
			else if($target == "csvdata" && $oper == "del")
				$responce = $this->deleteScheduledReport($responce);
			else if($target == "report")
				$responce = $this->downloadCSVData($responce);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$response['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce, UTIL::is_in_debug_mode() ? JSON_PRETTY_PRINT : 0);
	}
	
	###########################################################################
	
	public function admin() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			
			if($target == "custom" && $oper == "add")
				$responce = $this->addCustomDomainColumn($responce);
			else if($target == "custom" && $oper == "del")
				$responce = $this->deleteCustomDomainColumn($responce);
			
			else if($target == "users" && $oper == "add")
				$responce = $this->addUser($responce);
			else if($target == "users" && $oper == "list")
				$responce = $this->getUserList($responce);
			else if($target == "users" && $oper == "del")
				$responce = $this->deleteUser($responce);
			else if($target == "users" && $oper == "edit")
				$responce = $this->editUser($responce);
			else if($target == "users" && $oper == "get")
				$responce = $this->getUserInfo($responce);
			
			else if($target == "regapi" && $oper == "list")
				$responce = $this->getRegistrarAPIProfiles($responce);
			else if($target == "regapi" && $oper == "set")
				$responce = $this->setRegistrarAPIProfile($responce);
			else if($target == "regapi" && $oper == "add")
				$responce = $this->addRegistrarAPIProfile($responce);
			else if($target == "regapi" && $oper == "del")
				$responce = $this->deleteRegistrarAPIProfile($responce);
			else if($target == "regapi" && $oper == "test")
				$responce = $this->testRegistrarAPIProfile($responce);
			else if($target == "regapi" && $oper == "action")
				$responce = $this->actionRegistrarAPIProfile($responce);
			
			else if($target == "license" && $oper == "set")
				$responce = $this->setLicense($responce);
			
			else if($target == "category" && $oper == "get")
				$responce = $this->getAllCategoryInfo($responce);
			else if($target == "category" && $oper == "set")
				$responce = $this->setAllCategoryInfo($responce);
			else if($target == "category" && $oper == "remove")
				$responce = $this->deleteDomainsFromCategories($responce);
			else if($target == "category" && $oper == "del")
				$responce = $this->deleteCategories($responce);
			else if($target == "category" && $oper == "add")
				$responce = $this->addDomainsToCategories($responce);
			
			else if($target == "category" && $oper == "user")
				$responce = $this->setCategoriesForUser($responce);
			
			else if($target == "query" && $oper == "get")
				$responce = $this->getAllAutoQueryInfo($responce);
			else if($target == "query" && $oper == "set")
				$responce = $this->setAllAutoQueryInfo($responce);
			else if($target == "query" && $oper == "reset")
				$responce = $this->resetAllAutoQueryInfo($responce);
			
			else if($target == "domain" && $oper == "set")
				$responce = $this->setDomainColumnData($responce);
			else if($target == "domain" && $oper == "get")
				$responce = $this->getDomainColumnData($responce);
			
			else if($target == "audit" && $oper == "list")
				$responce = $this->getAuditLogList($responce);
			
			else if($target == "suffixlist" && $oper == "add")
				$responce = $this->addToDomainSuffixList($responce);
			else if($target == "suffixlist" && $oper == "del")
				$responce = $this->delFromDomainSuffixList($responce);
			else if($target == "suffixlist" && $oper == "get")
				$responce = $this->getDomainSuffixList($responce);
			else if($target == "suffixtlds" && $oper == "get")
				$responce = $this->getDomainSuffixTLDList($responce);

			else if($target == "tables" && $oper == "repair")
				$responce = $this->repairTables($responce);
			
			else if($target == "log" && $oper == "list")
				$responce = $this->getErrorLogFileList($responce);
			else if($target == "log" && $oper == "get")
				$responce = $this->getErrorLogFile($responce);
			else if($target == "log" && $oper == "del")
				$responce = $this->deleteErrorLogFile($responce);
			
			else if($target == "email" && $oper == "set")
				$responce = $this->setEmailConfig($responce);
			else if($target == "email" && $oper == "get")
				$responce = $this->getEmailConfig($responce);
			else if($target == "email" && $oper == "test")
				$responce = $this->testEmailConfig($responce);
			else if($target == "email" && $oper == "report")
				$responce = $this->emailReport($responce);
			
			else if($target == "dnsalert" && $oper == "get")
				$responce = $this->getDNSAlertSettings($responce);
			else if($target == "dnsalert" && $oper == "set")
				$responce = $this->setDNSAlertSettings($responce);
			else if($target == "dnsalert" && $oper == "status")
				$responce = $this->setDNSAlertStatus($responce);
					
			else if($target == "auth")
				$responce = $this->doAuthenticationOps($responce);
			
			else if($target == "schedule") 
				$responce = $this->adminSchedule($oper, $responce);
			
			else if($target == "branding" && $oper == "get") 
				$responce = $this->getBranding($responce);
			else if($target == "branding" && $oper == "set") 
				$responce = $this->setBranding($responce);
			
			else if($target == "options" && $oper == "get") 
				$responce = $this->getOptions($responce);
			else if($target == "options" && $oper == "set") 
				$responce = $this->setOptions($responce);
			
			else if($target == "config" && $oper == "get") 
				$responce = $this->getConfig($responce);
			else if($target == "config" && $oper == "set") 
				$responce = $this->setConfig($responce);
			else if($target == "config") {
				$response['error'] = "Missing target";
			}
			
			else if($target == "report" && $oper == "del")
				$responce = $this->deleteReportSchedule($responce);
			else if($target == "report" && $oper == "add")
				$responce = $this->addReportSchedule($responce);
			else if($target == "report" && $oper == "get")
				$responce = $this->getReportSchedules($responce);
			else if($target == "report" && $oper == "set")
				$responce = $this->setReportSchedule($responce);
			else if($target == "report" && $oper == "reset")
				$responce = $this->resetReportSchedule($responce);
			else if($target == "report" && $oper == "list")
				$responce = $this->getReportSchedules($responce);
			else if($target == "report" && $oper == "sort")
				$responce = $this->sortReportSchedules($responce);
			else if($target == "report" && $oper == "download")
				$responce = $this->downloadCSVData($responce);
			else if($target == "report" && $oper == "setcss")
				$responce = $this->setReportStyleSheet($responce);
			else if($target == "report" && $oper == "getcss")
				$responce = $this->getReportStyleSheet($responce);
			else 
				$responce['error'] = sprintf(TEXT::get("unknown_S_command"), "admin");
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$response['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce, UTIL::is_in_debug_mode() ? JSON_PRETTY_PRINT : 0);
	}
	
	###########################################################################
	
	public function delete() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			
			$auth = $this->authentication;
			$db = $auth->getDatabase();
			if($target == "subdomain" || $target == "txtrecord") {
				$sid = UTIL::get_request_data_integer("id", 0);
				$responce['sid'] = $sid;
				$subdomain = UTIL::get_sanitized_request_string("subdomain", "");
				
				// Verify that id belongs in one of the  allowed categories
				if($db->isDomainInAnyAllowedCategory($sid, $auth) === false) {
					$responce['error'] = TEXT::get("user_permdenied");
					return $responce;
				}
				if($subdomain != "") {
					$sdtype = ($target == "subdomain") ? \CodePunch\LU\LookupManager::SD_USER_ROWS : \CodePunch\LU\LookupManager::SD_TXT_ROWS;
					$status = $db->deleteFromTable($db->getSubdomainTableName(), "sid=? AND subdomain=? AND auto_added=?",  array($sid, $subdomain, $sdtype));
					if($status !== false && $status) {
						$responce['status'] = 'ok';
					}
				}
			}
			
			// Category Editing
			else if($target == "category")
				$responce = $this->deleteCategories($responce);
			
			// Safe delete domains
			else if($target == "domain" && $oper == "safe")
				$responce = $this->safeDeleteDomains($responce);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$response['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce);
	}
	
	###########################################################################
	
	public function get() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			
			if($target == "domain" && ($oper == "" || $oper == "all" || $oper == "cids"))
				$responce = $this->get_domain($responce);
			else if($target == "domain" && $oper == "catmap") 
				$responce = $this->getDomainCategoryMap($responce);
			else if($target == "luq")
				$responce = $this->getLUQInfo($responce);
			else if($target == "domain" && $oper == "column")
				$responce = $this->getProp($responce);
			else if($target == "domain" && $oper == "history")
				$responce = $this->getDomainHistory($responce);
			else if($target == "domain" && $oper == "colmodel")
				$responce = $this->getDomainColumnModel($responce);
			else if($target == "domain" && $oper == "datafields")
				$responce = $this->getDomainDataFields($responce);
			else if($target == "domain" && $oper == "parse")
				$responce = $this->parseDomainName($responce);
			else if($target == "regalias" && ($oper == "column" || $oper == ""))
				$responce = $this->getProp($responce);
			else if($target == "users" && $oper == "info")
				$responce = $this->getCurrentUserInfo($responce);
			else if($target == "whois" && $oper == "server")
				$responce = $this->getWhoisserver($responce);
			else if($target == "rdap" && $oper == "server")
				$responce = $this->getRDAPserver($responce);
			else if($target == "proxy" && $oper == "server")
				$responce = $this->getProxyServer($responce);
			else if($target == "suffixlist")
				$responce = $this->getDomainSuffixList($responce);
			else if($target == "suffixtlds")
				$responce = $this->getDomainSuffixTLDList($responce);
			else if($target == "report" && $oper == "colmodel")
				$responce = $this->getReportColumnModel($responce);
			else if($target == "report" && $oper == "preview")
				$responce = $this->getReportPreview($responce);
			else if($target == "time")
				$responce = $this->getServerTime($responce);
			else if($target == "category" && $oper == "info")
				$responce = $this->getAllCategoryInfo($responce);
			else if($target == "query" && $oper == "info")
				$responce = $this->getAllAutoQueryInfo($responce);
			else if($target == "ui")
				$responce = $this->getUI($responce, false);
			else if($target == "webshot")
				$responce = $this->getWebshot($responce, false);
			else if($target == "cookies")
				$responce = $this->getCookies($responce, false);
			else if($target == "sslcerts" && $oper == "colmodel")
				$responce = $this->getSSLCertColumnModel($responce);
			else if($target == "sslcerts" && $oper == "row")
				$responce = $this->getSSLCertData($responce);
			else if($target == "sslcerts" && $oper == "report")
				$responce = $this->getSSLCertReport($responce);
			else if($target == "config" && $oper == "get")
				$responce = $this->getConfig($responce);
			
			return json_encode($responce, UTIL::is_in_debug_mode() ? JSON_PRETTY_PRINT : 0);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$responce['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce);
	}
	
	###########################################################################
	
	public function set() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			if($target == "domain" && $oper == "column")
				$responce = $this->setProp($responce);
			else if($target == "domain" && $oper == "parse")
				$responce = $this->parseWhois($responce);
			else if($target == "domain" && $oper == "edit")
				$responce = $this->editDomains($responce);
			else if($target == "domain" && $oper == "colmodel")
				$responce = $this->setDomainColumnModel($responce);
			else if($target == "domain" && $oper == "datafields")
				$responce = $this->setDomainDataFields($responce);
			else if($target == "report" && $oper == "colmodel")
				$responce = $this->setReportColumnModel($responce);
			else if($target == "regalias" && ($oper == "column" || $oper == ""))
				$responce = $this->setProp($responce);
			else if($target == "category" && $oper == "sort")
				$responce = $this->categorySort($responce);
			else if($target == "query" && $oper == "sort")
				$responce = $this->categorySort($responce);
			else if($target == "luq" && $oper == "process")
				$responce = $this->processLookupQueue($responce);
			else if($target == "luq" && ($oper == "add" || $oper == ""))
				$responce = $this->lookupDomains($responce);
			else if($target == "luq" && $oper == "schedule")
				$responce = $this->scheduleLookups($responce);
			else if($target == "luq" && $oper == "del")
				$responce = $this->deleteLookupQueue($responce);
			else if($target == "whois" && $oper == "server")
				$responce = $this->setWhoisserver($responce);
			else if($target == "rdap" && $oper == "server")
				$responce = $this->setRDAPserver($responce);
			else if($target == "rdap" && $oper == "lus")
				$responce = $this->setRDAP_LUS($responce);
			else if($target == "proxy" && $oper == "server")
				$responce = $this->setProxyServer($responce);
			else if($target == "subdomain" && $oper == "colmodel")
				$responce = $this->setSubdomainColumnModel($responce);
			
			// category Editing
			else if($target == "category" && $oper == "info")
				$responce = $this->setAllCategoryInfo($responce);
			else if($target == "category" && $oper == "addto")
				$responce = $this->addDomainsToCategories($responce);
			else if($target == "category" && $oper == "delfrom")
				$responce = $this->deleteDomainsFromCategories($responce);
			
			// query Editing
			else if($target == "query" && $oper == "info")
				$responce = $this->setAllAutoQueryInfo($responce);

			// Add a BG Task
			else if($target == "task" && $oper == "add")
				$responce = $this->addBackgroundTask($responce);
			
			else if($target == "ui")
				$responce = $this->setUI($responce);
			
			else if($target == "sslcerts" && $oper == "colmodel")
				$responce = $this->setSSLCertColumnModel($responce);
			else if($target == "sslcerts" && $oper == "report")
				$responce = $this->setSSLCertReport($responce);
			
			return json_encode($responce, UTIL::is_in_debug_mode() ? JSON_PRETTY_PRINT : 0);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$responce['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce);
	}
	
	###########################################################################
	
	public function list() 
	{
		$responce = $this->init();
		try { 
			$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
			$oper = strtolower(UTIL::get_sanitized_request_string("oper", ""));
			if($target == "category" && $oper == "user")
				$responce = $this->getCategoriesForUser($responce);
			else if($target == "category" && $oper == "domain")
				$responce = $this->getCategoriesForDomain($responce);
			else if($target == "domain" && $oper == "columns")
				$responce = $this->GetAllDomainColumns($responce);
			else if($target == "domain" && $oper == "history")
				$responce = $this->GetDomainHistoryList($responce);
			else if($target == "report" && $oper == "columns")
				$responce = $this->GetAllReportColumns($responce);
			else if($target == "report" && $oper == "")
				$responce = $this->getReportSchedules($responce);
			else if($target == "domain" && $oper == "colmodel")
				$responce = $this->getDomainColumnModel($responce);
			else if($target == "subdomain" && $oper == "columns")
				$responce = $this->GetAllSubdomainColumns($responce);
			else if($target == "ui" && $oper == "fonts")
				$responce = $this->getGoogleFontList($responce);
			else if($target == "ui" && $oper == "themes")
				$responce = $this->getThemeList($responce);
			else if($target == "ui" && ($oper == "" || $oper == "all"))
				$responce = $this->getUI($responce);
			else if($target == "tasks")
				$responce = $this->getTasksList($responce);
			return json_encode($responce, UTIL::is_in_debug_mode() ? JSON_PRETTY_PRINT : 0);
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$responce['error'] = TEXT::get("db_query_error_see_log");
		}
		return json_encode($responce);
	}
	
	###########################################################################
	
	private function adminSchedule($oper, $responce)
	{
		if($oper == "get")
			$responce = $this->getLookupSchedule($responce);
		else if($oper == "add")
			$responce = $this->addLookupSchedule($responce);
		else if($oper == "list")
			$responce = $this->getLookupScheduleList($responce);
		else if($oper == "set")
			$responce = $this->updateLookupSchedule($responce);
		else if($oper == "del")
			$responce = $this->deleteLookupSchedule($responce);
		else if($oper == "reset")
			$responce = $this->resetLookupSchedule($responce);
		else
			$responce['error'] = sprintf(TEXT::get("unknown_SS_command"), "admin", "schedule");
		return $responce;
	}
	
	###########################################################################
	
	private function getWhereForAutoQuery(&$params, $expand=true)
	{
		$where = "";
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$aqid = UTIL::get_request_data_integer("aqid", 0);
		if($aqid > 0) {
			$aqwhere = $db->findOneOf($db->getAutoQueryTableName(), "id", $aqid, "query");
			$aqparams = $db->findOneOf($db->getAutoQueryTableName(), "id", $aqid, "params");
			$pcount = substr_count($aqwhere, "?");
			$aqparams = str_replace(array("\n","\r","\t"), ",", $aqparams);
			$aqp = array_filter(explode(",", $aqparams));
			if(count($aqp) == $pcount) {
				$params = is_array($params) ? array_merge($params, $aqp) : $aqp;
				if($expand)
					$aqwhere = $db->customExpandWhere($aqwhere);
				$where = $aqwhere;
			}
			else {
				$logger = new \CodePunch\Base\CPLogger();
				$logger->error("Auto-query parameter counts don't match: $aqid - $aqwhere - $aqparams");
			}
		}
		return $where;
	}
	
	###########################################################################
	
	private function getWhere($grid, $tablealias, &$params)
	{
		$whereSearch = $grid->jqGridSearchData($tablealias, $params);
		$whereAutoQuery = $this->getWhereForAutoQuery($params);
		$where = array();
		$where[] = $whereSearch == "" ? "" : "($whereSearch) ";
		$where[] = $whereAutoQuery == "" ? "" : "($whereAutoQuery) ";
		$where = array_values(array_filter($where));
		return $where; 
	}
	
	###########################################################################
	
	private function getWhereForDomains($grid, $tablealias, &$params, $expand=true)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$whereSearch = $grid->jqGridSearchData($tablealias, $params);
		$whereAutoQuery = $this->getWhereForAutoQuery($params, $expand);
		$whereCustomQuery = UTIL::get_sanitized_request_string("query", "");
		if($expand)
			$whereCustomQuery = $db->customExpandWhere($whereCustomQuery);
		$customParams = UTIL::get_sanitized_request_string("params", "");
		$cparams = explode("\n", $customParams);
		foreach($cparams as $p) {
			if($p != "")
				$params[] = $p;
		}
		$where = array();
		$where[] = $whereSearch == "" ? "" : "($whereSearch) ";
		$where[] = $whereAutoQuery == "" ? "" : "($whereAutoQuery) ";
		$where[] = $whereCustomQuery == "" ? "" : "($whereCustomQuery) ";
		
		$reportname = UTIL::get_sanitized_request_string("_repname", "");
		if($reportname != "") {
			$whereReport = $db->findOneOf($db->getReportSchedulerTableName(), "name", $reportname, "query");
			if($expand)
				$whereReport = $db->customExpandWhere($whereReport);
			$where[] = $whereReport == "" ? "" : "($whereReport) ";
			$paramReport = $db->findOneOf($db->getReportSchedulerTableName(), "name", $reportname, "params");
			if(strstr($paramReport, "\n") !== false)
				$rparams = explode("\n", $paramReport);
			else
				$rparams = explode(",", $paramReport);
			foreach($rparams as $p) {
				if($p != "")
					$params[] = $p;
			}
		}
		
		$where = array_values(array_filter($where));
		return $where; 
	}
	
	###########################################################################
	
	private function getDomainHistory($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$id = UTIL::get_request_data_integer("id", 0);
		if($id >= 0) {
			$di = $db->getFromTable("tvalue,fvalue,lookedup_at", $db->getDataHistoryTableName(), "id=?", array($id));
			if($di !== false && isset($di[0])) {
				$d = $db->fetchRow($di, 0);
				$responce['history'] = $d;
				$responce['status'] = 'ok';
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getDomainHistoryList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$id = UTIL::get_request_data_integer("id", 0);
		$numresults = UTIL::get_request_data_integer("num", 10);
		$responce['history'] = array();
		if($id >= 0) {
			$di = $db->getFromTable("id,lookedup_at,ftype,fvalue", $db->getDataHistoryTableName(), "sid=?", array($id), "lookedup_at", "DESC", $numresults);
			if($di !== false) {
				$index = 0;
				foreach($di as $d) {
					$d = $db->fetchRow($di, $index++);
					$responce['history'][] = $d;
				}
				$responce['status'] = 'ok';
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function get_domain($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$id = UTIL::get_request_data_integer("id", 0);
		if($id == 0) {
			$domain = UTIL::get_sanitized_request_string("domain", "");
			if($domain != "")
				$id = $db->getDomainID($domain);
			$id = $id === false ? 0 : $id;
		}
		
		// Verify that id belongs in one of the  allowed categories
		if($db->isDomainInAnyAllowedCategory($id, $auth) === false) {
			$responce['error'] = TEXT::get("user_permdenied");
			return $responce;
		}

		// Category IDS for domain
		$rows = $db->getFromTable("cid", $db->getCategoryConnectionTableName(), "did=?", array($id));
		$responce['cids'] = array();
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$responce['cids'][] = $row['cid'];
			}
		}
		$oper = UTIL::get_sanitized_request_string("oper", "");
		if($oper == "cids") {
			if(isset($responce['cids'])) {
				$cnames = array();
				$cids = $responce['cids'];
				foreach($cids as $cid) {
					$cname = $db->findOneOf($db->getCategoryTableName(), "cid", $cid, "name");
					$cnames[] = $cname;
				}
				$responce['domain'] = $db->getDomainName($id);
				$responce['categories'] = $cnames;
				$responce['status'] = 'ok';
			}
			return $responce;
		}
		
		$set_alerts = UTIL::get_request_data_boolean("alerts", false);
		if($set_alerts)
			$db->setRowHighlights(\CodePunch\DB\DomainDB::DEFAULT_HIGHLIGHT_DAYS, $id);
		$registry_whois = "-";
		$di = $db->getFromTable("*", $db->getDomainTableName(), "sid=?", array($id));
		$row = $db->fetchRow($di, 0, true, true);
		$cia = $db->getDetailsOfAllDomainColumns();
		if($row !== false) {
			$responce['status'] = 'ok';
			$info = array();
			foreach($row as $key=>&$value) {
				$ci = UTIL::get_from_array($cia[$key], array());
				// Convert bools to 1 or 0 (fix PostgreSQL Boolean handling)
				$ci = UTIL::array_bool_to_int($ci);
				$label = UTIL::get_from_array($ci['label'], $key);
				$info[$key] = $ci;
				// Convert bools to 1 or 0 (fix PostgreSQL Boolean handling)
				$value = UTIL::bool_to_int($value);
			}
			if(isset($row['registry_whois']))
					$row['registry_whois'] = preg_replace("/<img[^>]+\>/i", "(image removed) ", $row['registry_whois']);
			if(isset($row['registrar_whois']))
					$row['registrar_whois'] = preg_replace("/<img[^>]+\>/i", "(image removed) ", $row['registrar_whois']);
			$responce['data'] = $row;
			$responce['info'] = $info;
		}
		
		// Lookup Queue Entries for Domain
		$responce['luq'] = array();
		$di = $db->getFromTable("*", $db->getLookupQueueTableName(), "sid=?", array($id));
		if($di !== false) {
			$index = 0;
			foreach($di as $d) {
				$d = $db->fetchRow($di, $index++);
				$d['label'] = \CodePunch\LU\LookupManager::getLookupTypeLabel($d['lutype']);
				$responce['luq'][] = $d;
			}
		}
		
		$responce['history'] = array();
		$hstsql = "sid=? AND tvalue != ? AND (ftype = ? OR ftype = ?)";
		$hstdata = array($id, "", \CodePunch\LU\LookupManager::DOMAIN_RECORDS, \CodePunch\LU\LookupManager::AUTH_DOMAIN_RECORDS);
		// ::WARNING:: PLATFORM DEPENDENT CODE //
		// Oracle can't handle 'text' fields in WHERE 
		if($db->getPlatformName() == "oracle") {
			$hstsql = "sid=? AND (ftype = ? OR ftype = ?)";
			$hstdata = array($id, \CodePunch\LU\LookupManager::DOMAIN_RECORDS, \CodePunch\LU\LookupManager::AUTH_DOMAIN_RECORDS);
		}
		$di = $db->getFromTable("id,lookedup_at,ftype", $db->getDataHistoryTableName(), $hstsql, $hstdata, "lookedup_at", "DESC", 100);
		if($di !== false) {
			$index = 0;
			foreach($di as $d) {
				$d = $db->fetchRow($di, $index++);
				$responce['history'][] = $d;
			}
		}
		
		// Subdomain entries for domain
		$rows = $db->getFromTable("subdomain", $db->getSubdomainTableName(), "sid=? AND (auto_added=?) AND subdomain != ?", array($id, \CodePunch\LU\LookupManager::SD_USER_ROWS, "@"));
		$responce['subdomains'] = array();
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$responce['subdomains'][] = $row['subdomain'];
			}
		}
		
		// TXT entries for domain
		$rows = $db->getFromTable("subdomain", $db->getSubdomainTableName(), "sid=? AND (auto_added=?) AND subdomain != ?", array($id, \CodePunch\LU\LookupManager::SD_TXT_ROWS, "@"));
		$responce['txtrecords'] = array();
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$responce['txtrecords'][] = $row['subdomain'];
			}
		}
		
		return $responce;
	}
	
	###########################################################################
	
	private function get_domain_columns($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$table = $db->getDomainTableName();
		
		$idname = "id";
		$idtype = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
		$idname = "sid";
		$validcolumns = array_keys($db->getDetailsOfAllDomainColumns());
		
		if(isset($validcolumns)) {
			$ids = explode(",", UTIL::get_sanitized_request_string("id", ""));
			$columns = explode(",", UTIL::get_sanitized_request_string("columns", ""));
			
			$datacolumns = array();
			foreach($columns as $column) {
				if(in_array($column, $validcolumns))
					$datacolumns[] = $column;
			}
			$rows = $db->getFromTable($datacolumns, $table, "$idname in (?)", array(array($ids, $idtype)));
			if($rows !== false) {
				$index = 0;
				foreach($rows as &$row)
					$row = $db->fetchRow($rows, $index++);
				$responce['status'] = 'ok';
				$responce['data'] = $rows;
			}
		}
		return $responce;
	}
	
	private function getDomainCategoryMap($response) {

		// Parse and sanitize the incoming domain IDs
		$rawIds = UTIL::get_sanitized_request_string("id", "");
		$sids   = array_values(array_filter(
			array_map('intval', explode(",", $rawIds)),
			fn($id) => $id > 0
		));

		if (empty($sids)) {
			$response['status'] = 'error';
			$response['error']  = 'No valid domain IDs provided';
			return $response;
		}

		$auth  = $this->authentication;
		$db    = $auth->getDatabase();
		$table = $db->getCategoryConnectionTableName();

		$intArray   = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
		$restricted = $auth
			&& !$auth->isAdmin()
			&& !(($auth->getUserAccess()) & \CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER);

		if ($restricted) {
			$validCids = $this->getCategoryIDsForCurrentUser($auth);
			$rows = $db->getFromTable(
				"did, cid",
				$table,
				"did IN (?) AND cid IN (?)",
				[
					[$sids,      $intArray],
					[$validCids, $intArray],
				]
			);
		} else {
			$rows = $db->getFromTable(
				"did, cid",
				$table,
				"did IN (?)",
				[
					[$sids, $intArray],
				]
			);
		}

		if ($rows === false) {
			$response['status'] = 'error';
			$response['error']  = 'Database error fetching category memberships';
			return $response;
		}

		$map = [];
		$rows = array_map(fn($row) => array_change_key_case($row, CASE_LOWER), $rows);
		foreach ($rows as $row) {
			$map[(int)$row['did']][] = (int)$row['cid'];
		}

		$response['status'] = 'ok';
		$response['data']   = $map;
		return $response;
	}
	
	###########################################################################
	
	private function getDomainColumnData($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$datatable = $db->getDatacolumnsTableName();
		$table = $db->getTableNameWithoutPrefix($db->getDomainTableName());
		$name = UTIL::get_sanitized_request_string("name", "");
		if(UTIL::starts_with($name, "s.")) {
			$table = $db->getTableNameWithoutPrefix($db->getSubdomainTableName());
			$name = substr($name, 2);
		}
		else if(UTIL::starts_with($name, "d.")) 
			$name = substr($name, 2);

		$rows = $db->getFromTable("*", $datatable, "name=? AND tablename=? AND server=?", array($name, $table, "*"));
		if($rows !== false && isset($rows[0])) {
			$row = $db->fetchRow($rows, 0);
			$responce['data'] = $row;
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setDomainColumnData($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$datatable = $db->getDatacolumnsTableName();
		$table = $db->getTableNameWithoutPrefix($db->getDomainTableName());
		
		$name = UTIL::get_sanitized_request_string("name", "");
		if(UTIL::starts_with($name, "s.")) {
			$table = $db->getTableNameWithoutPrefix($db->getSubdomainTableName());
			$name = substr($name, 2);
		}
		else if(UTIL::starts_with($name, "d.")) 
			$name = substr($name, 2);

		$idata = array();
		if(UTIL::is_request_key_set('label'))
			$idata['label'] = UTIL::get_sanitized_request_string("label", "");
		if(UTIL::is_request_key_set('width'))
			$idata['width'] = UTIL::get_request_data_integer("width", "");
		if(UTIL::is_request_key_set('acslevel'))
			$idata['acslevel'] = UTIL::get_request_data_integer("acslevel", "");
		$idata['server'] = "*";
		
		$rows = $db->getFromTable("*", $datatable, "name=? AND tablename=? AND server=?", array($name, $table, "*"));
		if($rows !== false && isset($rows[0])) {
			$status = $db->updateTable($datatable, $idata, "name=? AND tablename=?", array($name, $table));
			if($status !== false && $status)
				$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getProp($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$target = UTIL::get_sanitized_request_string("t", "");
		
		if($target == "regalias"){
			$table = $db->getRegistrarAliasTableName();
			$idname = "name";
			$validcolumns = array('name','alias');
			$idtype = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
		}
		else if($target == "domain"){
			$table = $db->getDomainTableName();
			$idname = "sid";
			$validcolumns = array_keys($db->getDetailsOfAllDomainColumns());
			$idtype = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
		}
		else if($target == "category") {
			$table = $db->getCategoryTableName();
			$idname = "cid";
			$validcolumns = array('cid','name');
			$idtype = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
		}
		
		if(isset($validcolumns)) {
			$ids = UTIL::get_sanitized_request_string("id", "");
			if($target == "regalias")
				$ids = explode("##", $ids);
			else
				$ids = explode(",", $ids);
			$columns = explode(",", UTIL::get_sanitized_request_string("columns", ""));
			
			$datacolumns = array();
			foreach($columns as $column) {
				if(in_array($column, $validcolumns))
					$datacolumns[] = $column;
			}

			$rows = $db->getFromTable($datacolumns, $table, "$idname in (?)", array(array($ids, $idtype)));
			if($rows !== false) {
				$index = 0;
				foreach($rows as &$row)
					$row = $db->fetchRow($rows, $index++);
				$responce['status'] = 'ok';
				$responce['data'] = $rows;
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setProp($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$target = UTIL::get_sanitized_request_string("t", "domain");
		
		if($target == "regalias"){
			$table = $db->getRegistrarAliasTableName();
			$idname = "name";
			$validcolumns = array('name','alias');
		}
		else if($target == "domain"){
			$table = $db->getDomainTableName();
			$idname = "sid";
			$validcolumns = array_keys($db->getDetailsOfAllDomainColumns());
		}
		else if($target == "category"){
			$table = $db->getCategoryTableName();
			$idname = "cid";
			$validcolumns = array('cid','name');
		}
		
		$validdata = array();
		$idvalue = "";
		
		$requestdata = UTIL::get_unsafe_request_data_array(array());
		foreach($requestdata as $key=>$data) {
			if(in_array($key, $validcolumns) && $key !== "id" && $key != $idname) {
				$validdata[$key] = UTIL::sanitize_string($data);
				if(strtolower($validdata[$key]) == "null")
					$validdata[$key] = null;
				// Format Dates
				if($table == $db->getDomainTableName())
					$db->formatDate($key, $validdata[$key]);
			}
			else if($key == $idname)
				$idvalue = $data;
		}

		if($idvalue != "") {
			$exists = $db->getFromTable(implode(",", array_keys($validdata)), $table, "$idname=?", array($idvalue));
			if($exists === false || count($exists) == 0) {
				$validdata[$idname] = $idvalue;
				if($db->insertIntoTable($table, $validdata)) 
					$responce['status'] = 'ok';
				return $responce;
			}
			$count = $db->updateTable($table, $validdata, "$idname=?", array($idvalue));
			$responce['count'] = $count;
			if($count) 
				$responce['status'] = 'ok';
			if(!$count)
				$responce['error'] = 'nothing changed';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function addSubdomains($responce)
	{
		$added = 0;
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$sddata = UTIL::get_sanitized_request_string("data", "");
		$domain = UTIL::get_sanitized_request_string("domain", "");
		$sdtype = strtolower(UTIL::get_sanitized_request_string("type", ""));
		if($domain != "") {
			$sid = $db->getDomainID($domain);
			if($sid !== false && $sid > 0) {
				if($sddata != "") {
					$rows = explode("\n", $sddata);
					foreach($rows as $row) {
						$row = trim($row, " \r\n\t .:;");
						if($row != "") {
							if($sdtype == "txt") {
								if($db->addTXTName($sid, $row))
									$added++;
							}
							else {
								if($db->addSubdomain($sid, $row))
									$added++;
							}
						}
					}
				}
			}
		}
		if($added) {
			$responce['added'] = $added;
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function addDomains($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$domaindata = UTIL::get_sanitized_request_string("data", "");
		$tlds = UTIL::get_sanitized_request_string("tlds", "");
		$regapi = UTIL::get_sanitized_request_string("regapi", "");
		$emptycat = UTIL::get_request_data_boolean("emptycat");
		$cid = UTIL::get_request_data_integer("cid", 0);
		$bgproc = UTIL::get_request_data_boolean("bgproc", false);
		$responce['category'] = $cid;
		
		if($emptycat && $cid > 1) {
			$status = $db->deleteFromTable($db->getCategoryConnectionTableName(), "cid=?", array($cid));
		}
		
		if($domaindata == "" && $regapi != "") {
			$bgproc = true;
			$regapis = explode(",", $regapi);
			if($bgproc) {
				// Add BG job
				$job = "add;{$regapi};$cid";
				$setup = new \CodePunch\Config\Settings($auth);
				$status = $setup->addPendingJob($job);
				$responce['status'] = $status ? 'ok' : 'notok';
				if($status)
					$responce['error'] = "A background job to add domains was created. Please close this panel and wait a while (2-3 minutes) for the domains to be added.";
			}
			else {
				$responce['added'] = 0;
				$responce['failed'] = 0;
				$responce['error'] = '';
				$import = new \CodePunch\DB\ImportManager($auth);
				foreach($regapis as $api) {
					$domaindata = $import->getDomainsFromRegistrar($api, $supportswhois);
					$info = $this->addDomainsFromText($domaindata, $cid, $supportswhois ? $api : "");
					$responce['added'] += $info['added'];
					$responce['failed'] += $info['failed'];
					$responce['status'] = $info['status'];
					$responce['error'] .= " " . $info['error'];
				}
			}
		}
		else {
			$info = $this->addDomainsFromText($domaindata, $cid, "", $tlds);
			$responce['added'] = $info['added'];
			$responce['failed'] = $info['failed'];
			$responce['status'] = $info['status'];
			$responce['error'] = $info['error'];
		}
		return $responce;
	}
	
	###############################################################################
	
	public function addDomainsFromText($domaindata, $cid, $profile="", $tlds="")
	{
		$info = array('added'=>0, 'failed'=>0, 'status'=>'notok', 'error'=>'');
		
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		if($domaindata != "") {
			$appendtlds = array();
			if($tlds != "") {
				$tlds = str_replace(";", " ", $tlds);
				$tlds = trim(str_replace(",", " ", $tlds));
				$appendtlds = array_unique(explode(" ", $tlds));
				if(count($appendtlds)) {
					$tldlist = array();
					foreach($appendtlds as $tld)
						$tldlist[] = "." . trim($tld, ".");
					$appendtlds = $tldlist;
				}
			}
			$domains = array();
			$domaindata = str_replace("\t", "\n", $domaindata);
			$rows = explode("\n", $domaindata);
			foreach($rows as $row) {
				$row = trim($row);
				if(strpos($row, ",") !== false || strpos($row, ":") !== false) {
					$row = str_replace(",", ":", $row);
					$cslist = array_map('trim', explode(":", $row));
					$firstword = null;
					foreach($cslist as $word) {
						if(!$firstword) {
							$firstword = $word;
							$idx = mb_strpos($firstword, '.');
							if($idx !== false)
								$firstword = mb_substr($firstword, 0, $idx);
							if(!UTIL::starts_with($word, ".") && !UTIL::ends_with($word, ".") && strpos($word, ".") !== false)
								$domains[]  = $word;
							continue;
						}
						$entry = $firstword . "." . trim($word, ".");
						if(!UTIL::starts_with($entry, ".") && !UTIL::ends_with($entry, ".") && strpos($entry, ".") !== false)
							$domains[]  = $entry;
					}
					//UTIL::debug_print($domains);
				}
				else if(!UTIL::starts_with($row, ".") && !UTIL::ends_with($row, ".") && strpos($row, ".") !== false)
					$domains[]  = $row;
				else if(count($appendtlds) && strlen($row) && strpos($row, ".") === false) {
					foreach($appendtlds as $tld)
						$domains[] = $row . $tld;
				}
			}
			$ai = $db->addDomains($domains, $profile, $cid, $profile);
			$added = $ai['added'];
			if(count($added)) {
				$auditinfo = array();
				$info['status'] = 'ok';
				$info['added'] = count($added);
				$lookup = new \CodePunch\LU\LookupManager($auth);
				$setup = new \CodePunch\Config\Settings($auth);
				foreach($added as $sid) {
					$status = $lookup->addDefaultLookups($sid);
					$failures = 0;
					foreach($status as $s) {
						if(!$s)
							$failures++;
					}
					$sddata = $setup->getOption('default_sub_domains', "");
					if($db->addDefaultSubdomains($sid, $sddata) !== true)
						$failures++;
					if($failures)
						$info['failed'] = $failures;
					if(count($auditinfo) < 25)
						$auditinfo[] = $db->getDomainName($sid);
				}
				$formatted = UTIL::implode_limited(",", $auditinfo, 20);
				AUDIT::add($db, AUDIT::ADD_DOMAINS, count($added) . " domains added", $formatted);
			}
			else {
				if($cid > 1)
					$info['status'] = 'ok';
				$info['error'] = "No new domain added.";
			}
		}
		return $info;
	}
	
	###############################################################################

	private function domainGridEdit($responce)
	{
		$db = $this->authentication->getDatabase();	
		$validkeys = $db->getEditableDomainColumnNames();
		
		// Cleanup Request Table.
		$fkeys = array();
		$rd = UTIL::get_unsafe_request_data_array();
		foreach($rd as $key=>$value) {
			if(UTIL::starts_with($key, "d_"))
				$fkeys[] = substr($key, 2);
		}
		foreach($fkeys as $fk) {
			$v = $_REQUEST["d_".$fk];
			unset($_REQUEST["d_".$fk]);
			$_REQUEST[$fk] = $v;
		}
		
		$tabledata = array(
			array('table'=>$db->getDomainTableName(), 'id'=>'sid', 'uname'=>'domain'),
			array('table'=>$db->getCategoryConnectionTableName(),'id'=>'did'),
			array('table'=>$db->getSubdomainTableName(),'id'=>'sid'),
			array('table'=>$db->getLookupQueueTableName(),'id'=>'sid'),
			array('table'=>$db->getDataHistoryTableName(),'id'=>'sid')
		);
		
		$rights = $this->authentication->getUserAccess();
		$opmode = UTIL::get_sanitized_request_string('oper', "");
		if($opmode == "del") {
			$responce = $this->deleteFromGrid($responce, UTIL::get_sanitized_request_string("id", ""), $tabledata, array(), AUDIT::DELETE_DOMAINS);
			$db->setCategoryCounts();
		}
		else if($opmode == "edit") {
			$responce = $this->editGrid($responce, $db->getDomainTableName(), "sid", $validkeys, AUDIT::EDIT_DOMAIN);
		}
		else
			$responce['error'] = TEXT::get("code_ui_bad_parameters");
		return $responce;
	}
	
	###############################################################################

	private function categoryGridEdit($responce)
	{
		$db = $this->authentication->getDatabase();	
		$validkeys = array('name', 'description', 'caticon');
		$tabledata = array(
			array('table'=>$db->getCategoryTableName(), 'id'=>'cid', 'uname'=>'name'),
			array('table'=>$db->getCategoryConnectionTableName(),'id'=>'cid'),
			array('table'=>$db->getUserCategoryAccessTableName(),'id'=>'cid'),
		);
		
		$rights = $this->authentication->getUserAccess();
		if($this->authentication->isAdmin() || (($rights&\CodePunch\DB\DomainDB::ALLOW_CATEGORY_EDIT) && ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER))) {
			$opmode = UTIL::get_sanitized_request_string('oper', "");
			if($opmode == "add") {
				$validkeys[] = 'sortindex';
				// Force a large sortindex so that the new addition is at the end.
				if(!isset($_REQUEST['sortindex']))
					$_REQUEST['sortindex'] = 9999;
				$responce = $this->addToGrid($responce, $db->getCategoryTableName(), "name", "cid", $validkeys, AUDIT::ADD_CATEGORY);
			}
			else if($opmode == "del") 
				$responce = $this->deleteFromGrid($responce, UTIL::get_sanitized_request_string("id", ""), $tabledata, array(1), AUDIT::DELETE_CATEGORY);
			else if($opmode == "edit") {
				$responce = $this->editGrid($responce, $db->getCategoryTableName(), "cid", $validkeys, AUDIT::EDIT_CATEGORY);
			}
			else
				$responce['error'] = TEXT::get("code_ui_bad_parameters");
		}
		else
			$responce['error'] = TEXT::get("user_permdenied");
		return $responce;
	}
	
	###########################################################################
	
	private function categorySort($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$target = strtolower(UTIL::get_sanitized_request_string("t", ""));
		if($target == "category") {
			$tablename = $db->getCategoryTableName();
			$idname = "cid";
		}
		else {
			$tablename = $db->getAutoQueryTableName();
			$idname = "id";
		}
		$responce['target'] = $target;
		
		$cids = UTIL::get_sanitized_request_string("id", "");
		$cids = explode(",", $cids);
		if(count($cids) > 1) {
			// List of ids already sorted.
			$updated = 0;
			$cinfo = $db->getFromTable("$idname,sortindex", $tablename);
			if($cinfo !== false) {
				foreach($cinfo as &$ci) {
					$cid = $ci[$idname];
					$ci['sortindex'] = count($cinfo)+1;
					if($cid == 1)
						$ci['sortindex'] = 0;
					else {
						$newsi = array_search($ci[$idname], $cids);
						if($newsi !== false)
							$ci['sortindex'] = $newsi + 1;
					}
					$status = $db->updateTable($tablename, array('sortindex'=>$ci['sortindex']), "$idname=?", array($cid));
					if($status !== false)
						$updated++;
				}
				if($updated) {
					$responce['status'] = 'ok';
					$responce['updated'] = $updated;
				}
			}
		}
		else if(count($cids) == 1 && ($cids[0] == "alpha" || $cids[0] == "count")) {
			// Sort alphabetically or on count
			$inc = UTIL::get_request_data_integer('pos', 0);
		}
		else {
			// Move one item up or down.
			
			$cid = UTIL::get_request_data_integer('id', 0);
			$inc = UTIL::get_request_data_integer('pos', 0);
			
			if($cid > 1 && $inc != 0) {
				$responce['id'] = $cid;
				$ids = $db->getFromTable($idname, $tablename, "", array(), "sortindex", "asc");
				if($ids !== false) {
					$cids = array();
					$index = 0;
					foreach($ids as $id) {
						$id = $db->fetchRow($ids, $index++);
						$cids[] = $id[$idname];
					}
					$foundat = array_search($cid, $cids);
					if($foundat !== false && (($inc == 1 && $foundat < (count($cids)-1)) || ($inc == -1 && $foundat > 1))) {
						if($inc == 1 && $foundat < count($cids)-1 && $foundat > 0) {
							$temp = $cids[$foundat];
							$cids[$foundat] = $cids[$foundat+1];
							$cids[$foundat+1] = $temp;
						}
						else if($inc == -1 && $foundat > 1) {
							$temp = $cids[$foundat];
							$cids[$foundat] = $cids[$foundat-1];
							$cids[$foundat-1] = $temp;
						}
						$sortindex = 1;
						foreach($cids as $id) {
							if($id > 1) {
								$db->updateTable($tablename, array('sortindex'=>$sortindex), "$idname=?", array($id));
								$sortindex++;
							}
							else if($id == 1)
								$db->updateTable($tablename, array('sortindex'=>0), "$idname=?", array(1));
						}
						$responce['status'] = 'ok';
					}
				}
			}
		}
		return $responce;
	}
	
	###############################################################################
	
	private function addToGrid($responce, $maintable, $uniquekey, $idname, $validkeys, $auditaction=false)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$responce['added'] = 0;
		$data = UTIL::get_sanitized_request_string_array($validkeys);
		if(!isset($data[$uniquekey]) || $data[$uniquekey] == "")
			$responce['error'] = TEXT::get("error_bad_or_empty_user_input");
		else {
			$id = $db->findOneOf($maintable, $uniquekey, $data[$uniquekey], $idname);
			if($id === false) {
				$responce['added'] = $db->insertIntoTable($maintable, $data);
				if($responce['added'] !== false)
					$responce['status'] = 'ok';
			}
			else
				$responce['error'] = "Already exists!";
		}
		return $responce;
	}
	
	###############################################################################
	// Delete one or more entries from the grid tables. 
	// The action also will be added to the audit trail.
	
	private function deleteFromGrid($responce, $ids, $tabledata, $protected_ids, $auditaction=false)
	{
		if(count($tabledata)) {
			$status = $this->authentication->getDatabase()->deleteRowsByIDFromTables($ids, $tabledata, $protected_ids);
			$responce['deleted'] = $status['delcounts'][$tabledata[0]['table']];
			$responce['status'] = $responce['deleted'] ? 'ok' : $responce['status'];
			$responce['entries'] = $status['entries'];
			if($responce['deleted'] && $auditaction !== false) {
				$entries = UTIL::implode_limited(",", $status['entries'], 40);
				$formatted = sprintf(TEXT::get("format_N_entriesdeleted_S"), $responce['deleted'], $entries);
				AUDIT::add($this->authentication->getDatabase(), $auditaction, null, $formatted);
			}
		}
		return $responce;
	}
	
	###############################################################################
	// Single Row Edit
	
	private function editGrid($responce, $maintable, $idname, $validkeys, $auditaction=false)
	{
		if(!in_array("id", $validkeys))
			$validkeys[] = "id";
		$db = $this->authentication->getDatabase();
		$data = UTIL::get_sanitized_request_string_array($validkeys);
		if(count($data)) {
			$fdata = array();
			foreach($data as $key => $val) {
				if($val == "") {
					$ci = $db->getReportColumnInfo($key);
					if($ci && ($ci['fieldtype'] == 'date' || $ci['fieldtype'] == 'datetime'))
						$val = null;
				}
				// Format Dates
				if($maintable == $db->getSubdomainTableName() || $maintable == $db->getDomainTableName()) {
					$db->formatDate($key, $val);
				}
				$fdata[$key] = $val;
			}
			if($maintable == $db->getAutoQueryTableName()) {
				$id = UTIL::get_from_array($data['id'], false);
				if($id !== false && $id == 1) {
					$responce['error'] = "This row can't be edited";
					throw new Exception("Editing the auto query for all domains is not allowed.");
					return $responce;
				}
			}
			
			$arraykeys = array_keys($fdata);
			$arraykeys = array_diff($arraykeys, array("id"));
			$idval = UTIL::get_from_array($fdata['id'], false);
			$olddata = $db->getFromTable(implode(",", $arraykeys), $maintable, "$idname=?", array($idval));
			//UTIL::debug_print($fdata);UTIL::debug_print($idval);
			if($db->editTable($maintable, $fdata, $idname)) {
				$responce['status'] = 'ok';
				unset($fdata['id']);
				if(isset($olddata[0])) {
					$olddata = $db->fetchRow($olddata, 0);
					$changes = array();
					foreach($arraykeys as $key) {
						if($olddata[$key] != $fdata[$key])
							$changes[] = "$key: $olddata[$key] => $fdata[$key]";
					}
					$auditparams = implode(", ", $changes);
				}
				else
					$auditparams = UTIL::array_to_text($fdata, ", ", 30);
				if($auditaction !== false)
					AUDIT::add($this->authentication->getDatabase(), $auditaction, null, $auditparams);
				if($maintable == $db->getDomainTableName() && $idname == "sid") 
					$db->setDomainRowEditedToNow($idval);

				// Check if subdomain table, if yes copy the data to the persistent row.
				// May 17 2020
				if($maintable == $db->getSubdomainTableName()) {
					$sddata = $db->getFromTable("sid,subdomain,auto_added,record_type,record_value", $maintable, "hid=?", array($idval));
					if($sddata !== false && is_array($sddata)) {
						$sddata = $db->fetchRow($sddata, 0);
						$aamode = \CodePunch\LU\LookupManager::SD_MAN_MISC_ROWS;
						if($sddata['auto_added'] == \CodePunch\LU\LookupManager::SD_AUTO_SSL_ROWS) {
							$aamode = \CodePunch\LU\LookupManager::SD_MAN_SSL_ROWS;
							$mainrow = $db->getFromTable("hid", $maintable, "sid=? AND subdomain=? AND auto_added=?", array($sddata['sid'], $sddata['subdomain'], $aamode));
						}
						else if($sddata['auto_added'] == \CodePunch\LU\LookupManager::SD_AUTO_DNS_ROWS) {
							$aamode = \CodePunch\LU\LookupManager::SD_MAN_DNS_ROWS;
							$mainrow = $db->getFromTable("hid", $maintable, "sid=? AND subdomain=? AND record_type=? AND record_value=? AND auto_added=?", array($sddata['sid'], $sddata['subdomain'], $sddata['record_type'], $sddata['record_value'], $aamode));
						}
						if(isset($mainrow)) {
							$fdata['manual_edited_at'] = date("Y-m-d H:i:s");
							if($mainrow !== false && is_array($mainrow) && count($mainrow) == 1) {
								$mainrow = $db->fetchRow($mainrow, 0);
								$fdata['id'] = $mainrow['hid'];
								if($db->editTable($maintable, $fdata, $idname)) {
								}
							}
							else {
								$fdata['auto_added'] = $aamode;
								$fdata['record_type'] = $sddata['record_type'];
								$fdata['record_value'] = $sddata['record_value'];
								$fdata['sid'] = $sddata['sid'];
								$fdata['subdomain'] = $sddata['subdomain'];
								if($db->insertIntoTable($maintable, $fdata)) {
								}
							}
						}
					}
				}
				//
			}
			else
				$responce['error'] = "nothing changed";
		}
		else 
			$responce['error'] = "nothing to edit";
		return $responce;
	}

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

	private function autoQueryGridEdit($responce)
	{
		$db = $this->authentication->getDatabase();
		$validkeys = array('name', 'description', 'query', 'params', 'qicon');
		$tabledata[] = array('table'=>$db->getAutoQueryTableName(), 'id'=>'id', 'uname'=>'name');
		$rights = $this->authentication->getUserAccess();
		if($this->authentication->isAdmin() || (($rights&\CodePunch\DB\DomainDB::ALLOW_AUTOQUERY_EDIT) && ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER))) {
			$opmode = UTIL::get_sanitized_request_string('oper', "");
			if($opmode == "add") {
				$validkeys[] = 'sortindex';
				// Force a large sortindex so that the new addition is at the end.
				if(!isset($_REQUEST['sortindex']))
					$_REQUEST['sortindex'] = 9999;
				$responce = $this->addToGrid($responce, $db->getAutoQueryTableName(), "name", "id", $validkeys, AUDIT::ADD_AUTOQUERY);
			}
			else if($opmode == "del") {
				$responce = $this->deleteFromGrid($responce, UTIL::get_sanitized_request_string("id", ""), $tabledata, array(1), AUDIT::DELETE_AUTOQUERY);
			}
			else if($opmode == "edit") {
				$responce = $this->editGrid($responce, $db->getAutoQueryTableName(), "id", $validkeys, AUDIT::EDIT_AUTOQUERY);
			}
			else
				$responce['error'] = TEXT::get("code_ui_bad_parameters");
		}
		else
			$responce['error'] = TEXT::get("user_permdenied");
		return $responce;
	}
	
	###############################################################################

	private function dnsGridEdit($responce)
	{
		$db = $this->authentication->getDatabase();
		//$validkeys = array('notes_a', 'notes_b', 'notes_c', 'notes_d', 'subdomain', 'ip', 'record_value');
		$validkeys = $db->getEditableSubdomainColumnNames();
		
		// Cleanup Request Table.
		$fkeys = array();
		$rd = UTIL::get_unsafe_request_data_array();
		foreach($rd as $key=>$value) {
			if(UTIL::starts_with($key, "s_"))
				$fkeys[] = substr($key, 2);
		}
		foreach($fkeys as $fk) {
			$v = $_REQUEST["s_".$fk];
			unset($_REQUEST["s_".$fk]);
			$_REQUEST[$fk] = $v;
		}

		$tabledata[] = array('table'=>$db->getSubdomainTableName(), 'id'=>'hid', 'uname'=>'subdomain');
		if($this->authentication->isAdmin()) {
			$opmode = UTIL::get_sanitized_request_string('oper', "");
			if($opmode == "add") {
				$responce = $this->addToGrid($responce, $db->getSubdomainTableName(), "subdomain", "id", $validkeys, AUDIT::ADD_SUBDOMAIN);
			}
			else if($opmode == "del") {
				$responce = $this->deleteFromGrid($responce, UTIL::get_sanitized_request_string("id", ""), $tabledata, array(1), AUDIT::DELETE_SUBDOMAIN);
			}
			else if($opmode == "edit") {
				$responce = $this->editGrid($responce, $db->getSubdomainTableName(), "hid", $validkeys, AUDIT::EDIT_SUBDOMAIN);
			}
			else
				$responce['error'] = TEXT::get("code_ui_bad_parameters");
		}
		else
			$responce['error'] = TEXT::get("user_permdenied");
		return $responce;
	}
	
	###############################################################################

	private function sslcertGridEdit($responce)
	{
		$db = $this->authentication->getDatabase();
		$validkeys = array('notes_a', 'notes_b', 'notes_c', 'notes_d');
		$tablename = $db->getSSLCertificateTableName();
		if($this->authentication->isAdmin()) {
			$opmode = UTIL::get_sanitized_request_string('oper', "");
			if($opmode == "del") {
				$delids = UTIL::get_sanitized_request_string('id', "");
				$status = $db->deleteRowsByID($db->getSSLCertificateTableName(), $delids, "id", array(), "issued_to");
				$responce['deleted'] = $status['delcounts'];
				$responce['status'] = $responce['deleted'] ? 'ok' : $responce['status'];
				$responce['entries'] = $status['entries'];
			}
			else if($opmode == "edit") {
				$ids = UTIL::get_sanitized_request_string('id', "");
				$data = UTIL::get_sanitized_request_string_array($validkeys);
				if(count($data)) {
					$fdata = array();
					foreach($data as $key => $val) {
						$fdata[$key] = $val;
					}
					$ids = explode(",", $ids);
					$changed = 0;
					foreach($ids as $id) {
						$status = $db->updateTable($db->getSSLCertificateTableName(), $fdata, "id=?", array($id));
						if($status !== false)
							$changed++;
					}
					if($changed) {
						$responce['status'] = 'ok';
						$responce['edited'] = $changed;
					}
				}
			}
			else if($opmode == "add") {
				$pemdata = UTIL::get_sanitized_request_string('pem', "");
				if($pemdata != "") {
					$status = $db->importSSCertificate($this->authentication, $pemdata, "", "");
					if($status) {
						$responce['status'] = 'ok';
					}
				}
			}
			else
				$responce['error'] = TEXT::get("code_ui_bad_parameters");
		}
		else
			$responce['error'] = TEXT::get("user_permdenied");
		return $responce;
	}
	
	###########################################################################
	
	private function editDomains($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$column = UTIL::get_sanitized_request_string('columns', "");
		$value = UTIL::get_sanitized_request_string('data', "");
		$sids = explode(",", UTIL::get_sanitized_request_string('id', ""));
		$validkeys = $db->getEditableDomainColumnNames();
		if($column != "") {
			$updated = 0;
			if(strtolower($column) == "manual_edited_at" && strtolower($value) == "null") {
				foreach($sids as $sid) {
					if($db->resetDomainRowEdited($sid))
						$updated++;
				}
				if($updated)
					$responce['status'] = 'ok';
			}
			else if(in_array($column, $validkeys)) {
				foreach($sids as $sid) {
					if($value == "" || strtolower($value) == "null") {
						$ci = $db->getReportColumnInfo($column);
						if($ci['fieldtype'] == 'date' || $ci['fieldtype'] == 'datetime')
							$value = null;
					}
					// Format Dates
					$db->formatDate($column, $value);
					if($db->updateTable($db->getDomainTableName(), array($column=>$value), "sid=?", array($sid))) {
						$updated++;
						$db->setDomainRowEditedToNow($sid);
					}
				}
				if(count($sids))
					$responce['status'] = 'ok';
			}
			else
				$responce['error'] = "Not an Editable Column";
			$responce['updated'] = $updated;
		}
		return $responce;
	}
	
	###########################################################################
	
	private function scheduleLookups($responce)
	{
		$auth = $this->authentication;
		$lutype = UTIL::get_sanitized_request_string('type', "0");
		$lutype = intval($lutype);
		if($lutype > 0) {
			$lookup = new \CodePunch\LU\LookupManager($auth);
			$maxrows = UTIL::get_request_data_integer("limit", 180);
			$lufreq = UTIL::get_request_data_integer("ri", 180);
			$lunits = UTIL::get_request_data_integer("riu", \CodePunch\LU\LookupManager::LU_DAY);
			if($lunits != \CodePunch\LU\LookupManager::LU_MINUTE && $lunits != \CodePunch\LU\LookupManager::LU_ALWAYS) {
				$processed = $lookup->queueDomains($lutype, $lufreq, $lunits, "", "", $maxrows);
				if($processed) {
					$responce['status'] = 'ok';
					$responce['count'] = $processed;
					AUDIT::add($auth->getDatabase(), AUDIT::SCHEDULE_LOOKUPS, "$processed Domains",  $lutype);
				}
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function deleteLookupQueue($responce)
	{
		$auth = $this->authentication;
		$lutype = UTIL::get_sanitized_request_string('type', "0");
		$lutype = intval($lutype);
		if($lutype >= 0) {
			$lookup = new \CodePunch\LU\LookupManager($auth);
			$status = $lookup->removeFromLookupQueue($lutype);
			if($status !== false && $status) {
				$responce['status'] = 'ok';
				$responce['count'] = $status;
				AUDIT::add($auth->getDatabase(), AUDIT::DELETE_LOOKUP_QUEUE, "$status Entries",  $lutype);
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function lookupDomains($responce)
	{
		$what = UTIL::get_sanitized_request_string('what', "");
		if($what != "") {
			$sids = UTIL::get_sanitized_request_string('id', "");
			$interval = UTIL::get_request_data_integer("ri", 1440);
			$responce['count'] = 0;
			$auth = $this->authentication;
			$lookup = new \CodePunch\LU\LookupManager($auth);
			$ids = explode(",", $sids);
			for($i = 0; $i < count($ids); $i++) {
				$sid = $ids[$i];
				if(ctype_digit($sid))
					$responce['count'] = $lookup->addMultiLookups($sid, $what, $interval);
			}
			$responce['status'] = 'ok';
		}
		else
			$responce['error'] = "specify what to lookup";
		return $responce;
	}
	
	###############################################################################
	
	private function getServerTime($responce)
	{
		$responce['server_time'] = date("Y-m-d H:i:s");
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###############################################################################
	
	private function getLUQInfo($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$id = UTIL::get_request_data_integer("id", 0);
		
		$di = $auth->getDatabase()->getFromTable("*", $auth->getDatabase()->getLookupQueueTableName(), "sid=?", array($id));
		$responce['luqinfo'] = $di; // TODO: convert keys to lowercase?
			
		$responce['server_time'] = date("Y-m-d H:i:s");
		$responce['queue_size'] = $db->getRowCount($db->getLookupQueueTableName());
		$responce['server_time_zone'] = date_default_timezone_get(); 
		
		$responce['last_lookup_interval'] = -1;
		$responce['last_seen_at'] = "";
		$responce['last_lookup_at'] = "";
		$lookup = new \CodePunch\LU\LookupManager($auth);
		$status = $lookup->getStatus();
		if($status !== false) {
			if($status['last_seen_at'] != null && $status['last_seen_at'] !== false && $status['last_seen_at'] != "")
				$responce['last_seen_at'] = date("Y-m-d H:i:s", strtotime($status['last_seen_at']));
			if($status['last_lookup_at'] != null && $status['last_lookup_at'] !== false && $status['last_lookup_at'] != "")
				$responce['last_lookup_at'] = date("Y-m-d H:i:s", strtotime($status['last_lookup_at']));
			$responce['last_lookup_interval'] = time()-strtotime($status['last_lookup_at']);
		}

		$responce['ipw_size']   = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::IP_WHOIS);
		$responce['dw_size']    = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::DOMAIN_RECORDS);
		$responce['ip_size']    = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::ROOT_DNS);
		$responce['sdw_size']   = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::AUTH_DOMAIN_RECORDS);
		$responce['ssl_size']   = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::SSL_CERTS);
		$responce['ping_size']  = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::PING_RESPONSE);
		$responce['alexa_size'] = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::ALEXA_DATA);
		$responce['dns_size']   = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::SUB_DOMAINS);
		$responce['gi_size']    = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::GOOGLE_INDEX);
		$responce['http_size']  = $db->getRowCount($db->getLookupQueueTableName(), "lutype=" . \CodePunch\LU\LookupManager::HTTP_WEBSITE);
		$responce['timeout']    = $auth->getSessionTimeout();
		$responce['status'] = 'ok';

		return $responce;
	}
	
	###########################################################################
	
	private function processLookupQueue($responce)
	{
		$auth = $this->authentication;
		$cron = new \CodePunch\Config\Cron($auth);
		$responce = $cron->process(50,40);
		$responce['status'] = 'ok';
		if(isset($responce['time']))
			$responce['time'] = round($responce['time'], 2) . " seconds";
		return $responce;
	}
	
	###########################################################################
	
	private function getCategoriesForDomain($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$userid = $auth->getUserID();
		if($auth->isAdmin())
			$rows = $db->getFromTable("cid,name", $db->getCategoryTableName());
		else
			$rows = $db->getFromTable("cid", $db->getUserCategoryAccessTableName(), "userid=?", array($userid));
		$cids = array();
		$cnames = array();
		if($rows !== false && is_array($rows)) {
			foreach($rows as $row) {
				$cids[] = $row['cid'];
				$cnames[$row['cid']] = $db->findOneOf($db->getCategoryTableName(), "cid", $row['cid'], "name");;
			}
		}
		$ids = UTIL::get_sanitized_request_string('id', "");
		$ids = array_unique(explode(",", $ids));
		$rows = $db->getFromTable("cid,did", $db->getCategoryConnectionTableName(), "did IN (?) AND cid IN (?)", array(array($ids, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY), array($cids, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)));
		if($rows !== false && is_array($rows)) {
			$conn = array();
			foreach($rows as $row) {
				$did = intval($row['did']);
				if(!isset($conn[$did][0]))
					$conn[$did] = array();
				$conn[$did][] = array('cid'=>intval($row['cid']), 'name'=>$cnames[intval($row['cid'])]);
			}
			$responce['categories'] = $conn;
			$responce['status'] = 'ok';
		}
		return $responce;
	}

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

	private function getCategoriesForUser($responce)
	{
		$userid = UTIL::get_request_data_integer("id", 0);
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		if($userid == 0)
			$userid = $auth->getUserID();
		else {
			// Allow only Admin 
			$rights = $this->authentication->getUserAccess();
			if($rights != \CodePunch\DB\DomainDB::ALLOW_ADMINLEVEL)
				throw new Exception(TEXT::get("user_perm_view_denied"));
		}
		
		$responce['ids'] = array();
		if($auth->isAdmin())
			$rows = $db->getFromTable("cid", $db->getCategoryTableName());
		else
			$rows = $db->getFromTable("cid", $db->getUserCategoryAccessTableName(), "userid=?", array($userid));
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$name = $db->findOneOf($db->getCategoryTableName(), "cid", $row['cid'], "name");
				$cinfo = $db->getFromTable("name,caticon,sortindex", $db->getCategoryTableName(), "cid=?", array($row['cid']));
				if($cinfo !== false && isset($cinfo[0])) {
					$info = $db->fetchRow($cinfo, 0);
					$info['id'] = $row['cid'];
					$info['userids'] = array();
					$uinfo =  $db->getFromTable("userid", $db->getUserCategoryAccessTableName(), "cid=?", array($row['cid']));  
					if($uinfo !== false && is_array($uinfo)) {
						foreach($uinfo as &$ui) {
							$ui = array_change_key_case($ui, CASE_LOWER);
							if(isset($ui['userid']))
								$info['userids'][] = intval($ui['userid']);
						}
					}
					$responce['ids'][] = $info;
				}
			}
			usort($responce['ids'], function ($item1, $item2) {
				return $item1['sortindex'] <=> $item2['sortindex'];
			});
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getAutoQuery($responce)
	{
		$aqid = UTIL::get_request_data_integer('id', 0);
		$auth = $this->authentication;
		$rights = $this->authentication->getUserAccess();
		$db = $auth->getDatabase();		
		$responce['ids'] = array();
		if($aqid > 1 && ($auth->isAdmin() || (($rights&\CodePunch\DB\DomainDB::ALLOW_AUTOQUERY_EDIT) && ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER)))) {
			$rows = $db->getFromTable("*", $db->getAutoQueryTableName(), "id=?", array($aqid));
			if($rows !== false && isset($rows[0])) {
				$row = $db->fetchRow($rows, 0);
				$responce['data'] = $row;
				$responce['status'] = 'ok';
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getAutoQueryList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();		
		$responce['ids'] = array();
		$rights = $this->authentication->getUserAccess();
		if($auth->isAdmin() || (($rights&\CodePunch\DB\DomainDB::ALLOW_AUTOQUERY_EDIT) && ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER))) {
			$rows = $db->getFromTable("id", $db->getAutoQueryTableName(), "", array(), "sortindex", "asc");
			if($rows !== false) {
				$index = 0;
				foreach($rows as $row) {
					$row = $db->fetchRow($rows, $index++);
					$name = $db->findOneOf($db->getAutoQueryTableName(), "id", $row['id'], "name");
					$cinfo = $db->getFromTable("name,qicon,query,params", $db->getAutoQueryTableName(), "id=?", array($row['id']));
					if($cinfo !== false && isset($cinfo[0])) {
						$info = $db->fetchRow($cinfo, 0);
						$info['id'] = $row['id'];
						$responce['ids'][] = $info;
					}
				}
				$responce['status'] = 'ok';
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setCategoriesForUser($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$userid = UTIL::get_request_data_integer("uid", 0);
		$catids = UTIL::get_sanitized_request_string("cid", "");
		$cids = explode(",", $catids);
		$added= 0;
		if($auth->isAdmin() && $userid > 0) {
			$userstatus = $db->getRowCount($db->getUserTableName(), "id=?", array($userid));
			if($userstatus == 1) {
				$status = $db->deleteFromTable($db->getUserCategoryAccessTableName(), "userid=?", array($userid));
				if($status !== false) {
					foreach($cids as $cid) {
						$status = $db->insertIntoTable($db->getUserCategoryAccessTableName(), array('userid'=>$userid, 'cid'=>$cid));
						if($status) 
							$added++;
					}
					$responce['status'] = 'ok';
					$responce['added'] = $added;
				}
			}
			
		}
		return $responce;
	}
	
	###########################################################################
	# Required because of PostgreSQL bool handling
	
	private function get_bool_adjusted_domain_column_details()
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$rows = $db->getColumnDetails($db->getDomainTableName());
		if($rows !== false) {
			foreach($rows as &$row) {
				$row = UTIL::array_bool_to_int($row);
			}
		}
		return $rows;
	}
	
	###########################################################################
	
	private function GetAllDomainColumns($responce)
	{
		$groupby = UTIL::get_sanitized_request_string("group", "");
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$rows = $db->getColumnDetails($db->getDomainTableName());
		if($rows !== false) {
			foreach($rows as &$row) {
				$row = UTIL::array_bool_to_int($row);
			}
			if($groupby == "type") {
				$typeinfo = array('string'=>array(), 'boolean' => array(), 'integer'=>array(), 'date'=>array(), 'datetime'=>array());
				$keys = array_keys($typeinfo);
				foreach($rows as $column=>$data) {
					foreach($keys as $key) {
						if($data['fieldtype'] == $key && $column != 'r_h_disp' && $column != "sid") {
							$typeinfo[$key][] = array('name'=>$column, 'label'=>$data['label']);
							break;
						}
					}
				}
				$responce['columns'] = $typeinfo;
			}
			else 
				$responce['columns'] = $rows;
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function GetAllSubdomainColumns($responce)
	{
		$groupby = UTIL::get_sanitized_request_string("group", "");
		$grid = strtolower(UTIL::get_sanitized_request_string("grid", ""));
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$rows = $db->getColumnDetails($db->getSubdomainTableName());
		if($rows !== false) {
			foreach($rows as &$row) {
				$row = UTIL::array_bool_to_int($row);
			}
			if($groupby == "type") {
				$typeinfo = array('string'=>array(), 'boolean' => array(), 'integer'=>array(), 'date'=>array(), 'datetime'=>array());
				$keys = array_keys($typeinfo);
				foreach($rows as $column=>$data) {
					foreach($keys as $key) {
						if($data['fieldtype'] == $key && $column != 'r_h_disp' && $column != "sid") {
							$typeinfo[$key][] = array('name'=>$column, 'label'=>$data['label']);
							break;
						}
					}
				}
				$responce['columns'] = $typeinfo;
			}
			else {
				if($grid == "dns" || $grid == "ssl") {
					$nrows = array();
					if($grid == "dns")
						$allowed = array('hid','sid','subdomain','ip','record_type','record_value',
											'auto_added','added_on','manual_edited_at',
											'ptr', 'edited', 'ttl', 'notes_a', 'notes_b', 'notes_c', 'notes_d', 'dnsbl_lookup_results');
					else
						$allowed = array('hid','sid','subdomain','ip','ssl_valid_to','ssl_valid_from',
											'auto_added','added_on','manual_edited_at',
											'ptr', 'edited', 'ssl_issued_to', 'ssl_issued_by', 'subject_key_id', 'subject_alt_name',
											'signature_type', 'serial', 'notes_a', 'notes_b', 'notes_c', 'notes_d', 'dnsbl_lookup_results');
					foreach($rows as $k=>$r) {
						if(in_array(strtolower($k), $allowed))
							$nrows[$k] = $r;
					}
					$rows = $nrows;
				}
				$responce['columns'] = $rows;
			}
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function GetAllReportColumns($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$responce['columns'] = array();
		$responce['columns']['s.hostname'] = array(
			"label"=> "Hostname",
            "editable"=> "0",
            "gridview"=> "1",
            "custom"=> "0",
            "fieldtype"=> "datetime",
            "width"=> "178",
            "acslevel"=> "0");

        $responce['columns']['d.days_to_expiry'] = array(
            "label"=> "Days",
            "editable"=> "0",
            "gridview"=> "1",
            "custom"=> "0",
            "fieldtype"=> "integer",
            "width"=> "80",
            "acslevel"=> "0");

		$rows = $db->getColumnDetails($db->getDomainTableName(), "gridview=?", array('1'));
		if($rows !== false) {
			foreach($rows as $column=>&$data) {
				$newcolumn = "d." . $column;
				$responce['columns'][$newcolumn] = UTIL::array_bool_to_int($data);
			}
			$responce['status'] = 'ok';
		}
		$rows = $db->getColumnDetails($db->getSubdomainTableName(), "gridview=?", array('1'));
		if($rows !== false) {
			foreach($rows as $column=>&$data) {
				$newcolumn = "s." . $column;
				$responce['columns'][$newcolumn] = UTIL::array_bool_to_int($data);
			}
			$responce['status'] = 'ok';
		}
		
		$showdetails = UTIL::get_request_data_boolean("details", true);
		if(!$showdetails) {
			$columnnames = array_keys($responce['columns']);
			$responce['columns'] = $columnnames;
		}

		return $responce;
	}
	
	###########################################################################
	
	private function parseWhois($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$sids = explode(",", UTIL::get_sanitized_request_string("id", ""));
		$responce['count'] = 0;
		foreach($sids as $sid) {
			$sid = intval($sid);
			if($this->parse_whois($sid)) {
				$responce['status'] = 'ok';
				$responce['count']++;
			}
		}
		if(!$responce['count'])
			$responce['error'] = 'nothing changed';
		return $responce;
	}
	
	###########################################################################
	
	private function parse_whois($did)
	{
		$parsed = 0;
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$domain = $db->getDomainName($did);
		if($domain !== false) {
			$di = $db->getFromTable("primary_whois_server,secondary_whois_server,registry_whois,registrar_whois", $db->getDomainTableName(), "sid=?", $did);
			if($di !== false && isset($di[0])) {
				$di = $db->fetchRow($di, 0);
				$server = $di['primary_whois_server'];
				$whoisdata = $di["registry_whois"];
				if(substr($whoisdata, 0, 1) == "{") {
					$dataarray = \CodePunch\LU\RDAP::reParse($domain, $whoisdata, 0);
				}
				else {
					$whoisdata = html_entity_decode($whoisdata, ENT_QUOTES | ENT_HTML5);
					$whoisdata = str_replace("<br>", "\n", $whoisdata);
					$parser = new \CodePunch\LU\WhoisParser($db, $whoisdata, $server);
					$dataarray = $parser->postProcess($domain);
				}
				if(is_array($dataarray)) {
					$dataarray['sid'] = $did;
					unset($dataarray['primary_whois_checked_at']);
					unset($dataarray['registry_whois']);
					unset($dataarray['primary_whois_server']);
					unset($dataarray['secondary_whois_server']);
					if($db->updateDomainTable($dataarray))
						$parsed++;

					$whoisdata = $di["registrar_whois"];
					if(strlen($whoisdata)) {
						if(substr($whoisdata, 0, 1) == "{") {
							$data_array02 = \CodePunch\LU\RDAP::reParse($domain, $whoisdata, 1);
						}
						else {
							$secondary_whois_server = $di['secondary_whois_server'];
							$whoisdata = str_replace("<br>", "\n", $whoisdata);
							$parser = new \CodePunch\LU\WhoisParser($db, $whoisdata, $secondary_whois_server);
							$data_array02 = $parser->processRegistrarData($domain);
						}
						if(is_array($data_array02)) {
							$data_array02['sid'] = $did;
							unset($data_array02['secondary_whois_checked_at']);
							unset($data_array02['registrar_whois']);
							$data_array02 = $db->analyseRegistrarDomainData($did, $data_array02);
							if($db->updateDomainTable($data_array02))
								$parsed++;
						}
					}
				}
			}
		}
		return $parsed;
	}

	###########################################################################
	
	private function getReportHTML($name)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$table = $db->getReportSchedulerTableName();
		if($name != "" && $name !== false) {
			$rm = new \CodePunch\UI\ReportManager($auth);
			$responce = array();
			$rm->getReport($name, \CodePunch\UI\ReportManager::SEND_EMAIL_NO, true, $responce);
			return $responce['report'];
		}
		return "";
	}
	
	###########################################################################
	
	private function emailReport($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$repid = UTIL::get_sanitized_request_string("id", 0);
		$table = $db->getReportSchedulerTableName();
		if($name == "" && $repid > 0)
			$name = $db->findOneOf($table, "id", $repid, "name");
		if($name != "" && $name !== false) {
			$rm = new \CodePunch\UI\ReportManager($auth);
			$rm->getReport($name, \CodePunch\UI\ReportManager::SEND_EMAIL_YES, true, $responce);
			if($responce['emailed'])
				$responce['status'] = 'ok';
			$responce['name'] = $name;
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getReportPreview($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$repid = UTIL::get_sanitized_request_string("id", 0);
		$table = $db->getReportSchedulerTableName();
		if($name == "" && $repid > 0)
			$name = $db->findOneOf($table, "id", $repid, "name");
		if($name != "" && $name !== false) {
			$rm = new \CodePunch\UI\ReportManager($auth);
			$rm->getReport($name, \CodePunch\UI\ReportManager::SEND_EMAIL_NO, true, $responce);
			$responce['status'] = 'ok';
			$responce['name'] = $name;
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getReportColumnModel($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("group", "");
		if($name != "") {
			$setup = new \CodePunch\Config\Settings($auth);
			$responce['colmodel'] = $setup->getReportColumnModelByName($name);
			$responce['status'] = 'ok';
			$responce['name'] = $name;
		}
		return $responce;
	}
	
	##########################################################################
	
	private function getSSLCertData($responce)
	{
		$rights = $this->authentication->getUserAccess();
		if($this->authentication->isAdmin() || ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER)) {
			$auth = $this->authentication;
			$db = $auth->getDatabase();
			$id = UTIL::get_request_data_integer("id", 0);
			if($id > 0) {
				$rows = $db->getFromTable("*", $db->getSSLCertificateTableName(), "id=?", array($id));
				if($rows !== false && count($rows)) {
					$row = $db->fetchRow($rows, 0);
					$responce['status'] = 'ok';
					$responce['data'] = $row;
				}
			}
		}
		else
			throw new Exception(TEXT::get("user_permdenied"));
		return $responce;
	}
	
	###########################################################################
	
	private function getColumnModel($columns) {
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$dateformat = $setup->getOption("date_display_format", "YYYY-MMM-DD");
		$colmodel = array();
		foreach($columns as $c) {
			$crow = array();
			$crow['name'] = $c;
			$width = intval($setup->getOption("col-width-" . $c, 150));
			if($width <= 0 || $width > 500)
				$width = 150;
			$crow['label'] = trim(ucwords(str_replace("_", " ", $c)));
			$crow['width'] = array('default'=>$width);
			$crow['resize'] = true;
			$crow['sortable'] = true;
			$crow['search'] = array('type'=>'text');
			if($c == 'r_h_disp') {
				$crow['width'] = array('default'=>24);
				$crow['label'] = "!";
				$crow['formatter'] = "highlightFormatter";
				unset($crow['search']);
				$crow['classes'] = 'dtcell tc-icon';
			}
			else if($c == 'valid_from' || $c == 'valid_to') {
				$crow['formatter'] = 'date';
				$crow['formatoptions'] = array('srcformat'=>'Y-m-d','newformat'=>$dateformat);
			}
			else if($c == 'added_on' || $c == 'manual_edited_at') {
				$crow['formatter'] = 'date';
				$crow['formatoptions'] = array('srcformat'=>'Y-m-d H:i:s','newformat'=>"$dateformat H:i:s");
			}
			$colmodel[] = $crow;
		}
		return $colmodel;
	}
	
	###########################################################################
	
	private function getSSLCertReport($responce)
	{
		if($this->authentication->isAdmin()) {
			$setup = new \CodePunch\Config\Settings($this->authentication);
			$responce = $setup->getSSLCertSettings($responce);
			$responce['status'] = 'ok';
		}
		else
			throw new Exception(TEXT::get("user_permdenied"));
		return $responce;
	}
	
	###########################################################################
	
	private function setSSLCertReport($responce)
	{
		if($this->authentication->isAdmin()) {
			$setup = new \CodePunch\Config\Settings($this->authentication);
			$changed = 0;
			if(UTIL::is_request_key_set("emails")) {
				$emails = UTIL::get_sanitized_request_string("emails", "");
				$setup->setOption("sslbrowse-emails", $emails);
				$changed++;
			}
			if(UTIL::is_request_key_set("autorun")) {
				$autorun = UTIL::get_request_data_boolean("autorun", false);
				$setup->setOption("sslbrowse-autorun", $autorun ? "1" : "0");
				$changed++;
			}
			if(UTIL::is_request_key_set("frequency")) {
				$frequency = UTIL::get_request_data_integer("frequency", 0);
				$setup->setOption("sslbrowse-frequency", $frequency);
				$changed++;
			}
			if(UTIL::is_request_key_set("runits")) {
				$runits = UTIL::get_request_data_integer("runits", 0);
				$setup->setOption("sslbrowse-runits", $runits);
				$changed++;
			}
			if($changed) {
				$setup->setOption("sslbrowse-lastrun", null);
				$responce['status'] = 'ok';
			}
			$responce = $setup->getSSLCertSettings($responce);
			$responce['count'] = $changed;
		}
		else
			throw new Exception(TEXT::get("user_permdenied"));
		return $responce;
	}
	
	###########################################################################
	
	private function getSSLCertColumnModel($responce)
	{
		$rights = $this->authentication->getUserAccess();
		if($this->authentication->isAdmin() || ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER)) {
			$setup = new \CodePunch\Config\Settings($this->authentication);
			$columns = $setup->getOption("sslcert_columns", "issued_to;issued_by;valid_from;valid_to;added_on;serial;subject_alt_name;thumbprint;base_domain;notes_a;notes_b;notes_c;notes_d");
			$columns = 'r_h_disp;' . $columns;
			$columns = explode(";", $columns);
			$colmodel = $this->getColumnModel($columns);
			$responce['status'] = 'ok';
			$responce['colmodel'] = $colmodel;
			$responce['current'] = $columns;
			$responce['columns'] = array("valid_from", "valid_to", "issued_to", "issued_by", "issuer_organization", "issuer_country", "subject_key_id", "subject_alt_name_text", "subject_alt_name", "notes_a", "notes_b", "notes_c", "notes_d", "signature_type", "thumbprint", "base_domain", "live_at", "serial", "added_on", "manual_edited_at", "edited", "auto_added");
		}
		else
			throw new Exception(TEXT::get("user_permdenied"));
		return $responce;
	}
	
	###########################################################################
	
	private function setSSLCertColumnModel($responce)
	{
		$rights = $this->authentication->getUserAccess();
		if($this->authentication->isAdmin() || ($rights&\CodePunch\DB\DomainDB::ALLOW_ALL_DOMAINS_EVER)) {
			$done = 0;
			$setup = new \CodePunch\Config\Settings($this->authentication);
			if(UTIL::is_request_key_set("columns")) {
				$columns = UTIL::get_sanitized_request_string("columns", "");
				if($columns == "" || $columns == "reset")
					$columns = "issued_to;issued_by;valid_from;valid_to;added_on;serial;subject_alt_name;thumbprint;base_domain;notes_a;notes_b;notes_c;notes_d";
				$columns = str_replace(",", ";", $columns);
				$setup->setOption("sslcert_columns", $columns);
				$columns = explode(";", $columns);
				$responce['colmodel'] = $this->getColumnModel($columns);
				$done++;
			}
			if(UTIL::is_request_key_set("column") && UTIL::is_request_key_set("width")) {
				$column = UTIL::get_sanitized_request_string("column", "");
				$width = UTIL::get_request_data_integer("width", 150, 20, 500);
				$setup->setOption("col-width-" . $column, $width);
				$done++;
			}
			if($done)
				$responce['status'] = 'ok';
		}
		else
			throw new Exception(TEXT::get("user_permdenied"));
		return $responce;
	}
	
	###########################################################################
	
	private function getDomainDataFields($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$responce['fields'] = $setup->getText("domain_data_fields", '');
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function setDomainDataFields($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$columns = UTIL::get_sanitized_request_string("fields", "");
		$columns = explode(";", $columns);
		foreach($columns as &$column) {
			if(UTIL::starts_with($column, "s.") || UTIL::starts_with($column, "d."))
				$column = substr($column, 2);
		}
		$columns = implode(";", $columns);
		$setup = new \CodePunch\Config\Settings($auth);
		if($setup->setText("domain_data_fields", $columns)) {
			$responce['status'] = 'ok';
		}
		else
			$responce['error'] = '';
		return $responce;
	}
	
	###########################################################################
	
	private function getDomainColumnModel($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$dgroup = UTIL::get_sanitized_request_string("group", "");
		if($dgroup != "") {
			$setup = new \CodePunch\Config\Settings($auth);
			$responce['colmodel'] = $setup->getDomainColumnModelByName($dgroup);
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setDomainColumnModel($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$dgroup = UTIL::get_sanitized_request_string("group", "");
		$columns = UTIL::get_sanitized_request_string("columns", "");
		if($dgroup != "") {
			$responce['columns'] = strlen($columns) ? 'set' : 'reset';
			$columns = explode(";", $columns);
			foreach($columns as &$column) {
				if(UTIL::starts_with($column, "s.") || UTIL::starts_with($column, "d."))
					$column = substr($column, 2);
			}
			$columns = implode(";", $columns);
			$setup = new \CodePunch\Config\Settings($auth);
			if($setup->setOption($dgroup, $columns)) {
				$responce['colmodel'] = $setup->getDomainColumnModelByName($dgroup);
				$responce['status'] = 'ok';
			}
			else
				$responce['error'] = 'nothing changed';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setReportColumnModel($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$dgroup = UTIL::get_sanitized_request_string("group", "");
		$columns = UTIL::get_sanitized_request_string("columns", "");
		$sidx = UTIL::get_sanitized_request_string("sidx", "");
		$sord = strtolower(UTIL::get_sanitized_request_string("sord", ""));
		if($dgroup != "") {
			$setup = new \CodePunch\Config\Settings($auth);
			if($setup->setReportColumnModelByName($dgroup, $columns) !== false) {
				$responce['colmodel'] = $setup->getReportColumnModelByName($dgroup);
				$responce['status'] = 'ok';
				$responce['name'] = $dgroup;
			}
			else
				$responce['error'] = 'nothing changed';
			
			$data = array();
			if($sidx != "")
				$data['sortcolumn'] = $sidx;
			if($sord == "asc")
				$data['descending'] = 0;
			else if($sord == "desc")
				$data['descending'] = 1;
			if(count($data))
				$db->updateTable($db->getReportSchedulerTableName(), $data, "name=?", $dgroup);
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setSubdomainColumnModel($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$dgroup = UTIL::get_sanitized_request_string("group", "");
		$columns = UTIL::get_sanitized_request_string("columns", "");
		if($dgroup != "") {
			$responce['columns'] = strlen($columns) ? 'set' : 'reset';
			$columns = explode(";", $columns);
			foreach($columns as &$column) {
				if(UTIL::starts_with($column, "s.") || UTIL::starts_with($column, "d."))
					$column = substr($column, 2);
			}
			$columns = implode(";", $columns);
			$setup = new \CodePunch\Config\Settings($auth);
			if($setup->setOption($dgroup, $columns)) {
				$responce['colmodel'] = $setup->getSubdomainColumnModelByname($dgroup);
				$responce['status'] = 'ok';
				$responce['name'] = $dgroup;
			}
			else
				$responce['error'] = 'nothing changed';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function addCustomDomainColumn($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$fname = trim(UTIL::get_sanitized_request_string("name", ""));
		$fname = htmlspecialchars_decode($fname);
		$flabel = $fname;
		$fname = strtolower(UTIL::strip_punctuation($fname, true));
		$ftype = UTIL::get_sanitized_request_string("type", "");
		$fdefault = UTIL::get_sanitized_request_string("default", "empty");
		$fdefault = ($fdefault == "") ? "empty" : $fdefault;
		$fdefault = (strtolower($fdefault) == "null") ? NULL : $fdefault;
		$findex = UTIL::get_sanitized_request_string("index", "");
		$fwidth = UTIL::get_request_data_integer("width", "");
		$getcolumns = UTIL::get_request_data_boolean("columns", false);
		
		$dcolumns = $db->getDetailsOfAllDomainColumns();
		if(isset($dcolumns[$fname]['custom'])) {
			if($dcolumns[$fname]['custom'] == 0) {
				$responce['error'] = 'a default column with same name exists';
				return $responce;
			}
		}
		
		if($db->connection->getDatabasePlatform()->getReservedKeywordsList()->isKeyword($fname)) {
			$responce['error'] = "<b><i>$fname</i></b> is a reserved keyword";
			return $responce;
		}
		
		$rows = $db->getFromTable("id", $db->getDatacolumnsTableName(), "name=? AND tablename=?", array($fname, \CodePunch\DB\DomainDB::DOMAIN_TABLE));
		if($rows !== false && isset($rows[0]))
			$responce['error'] = 'already exists';
		else {
			// Create Domain Field
			$keys = array();
			$columninfo = array('name'=>$fname, 'type'=>$ftype, 'options'=>array());
			if($fdefault != "empty") {
				$columninfo['options'] = array('default'=>$fdefault);
				$columninfo['options']['notnull'] = true;
			}
			else
				$columninfo['options']['notnull'] = false;
			if($ftype == "string")
				$columninfo['options']['length'] = $fwidth;
			if($findex == "index")
				$keys = array('index'=>$fname);
			else if($findex == "unique")
				$keys = array('unique'=>$fname);
			$istatus = $db->insertColumnsAndKeys($db->getDomainTableName(),  array($columninfo), $keys);
			if($istatus) {
				AUDIT::add($db, \CodePunch\DB\Audit::ADD_CUSTOM_COLUMN, "Added $fname", "");
				$gridview = ($ftype == "text") ? 0 : 1;
				$datacolumn = array('name'=>$fname,'tablename'=>\CodePunch\DB\DomainDB::DOMAIN_TABLE,'server'=>'*','label'=>$flabel,'editable'=>1,'gridview'=>$gridview,'width'=>100,'fieldtype'=>$ftype,'custom'=>1);
				if($db->insertIntoTable($db->getDatacolumnsTableName(), $datacolumn) === false)
					$responce['error'] = "Unable to insert column info into data columns table"; 
				if($getcolumns)
					$responce['columns'] = $this->get_bool_adjusted_domain_column_details();
				$responce['status'] = 'ok';
			}
			else {
				$responce['error'] = TEXT::get('db_custom_col_add_error');
			}
		}
		
		return $responce;
	}
	
	###########################################################################
	
	private function deleteCustomDomainColumn($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$fname = UTIL::get_sanitized_request_string("name", "");
		$cleardb = UTIL::get_request_data_boolean("clear", false);
		$getcolumns = UTIL::get_request_data_boolean("columns", false);
		
		// Verify that $fname is a custom column
		$customfields = $db->getCustomDomainColumnNames();
		if($fname != "" && in_array($fname, $customfields)) {
			$db->deleteFromTable($db->getDatacolumnsTableName(), "name=?", array($fname));
			if($cleardb) {
				$tablename = $db->getDomainTableName();
				$sql = array();
				$sql[] = "ALTER TABLE $tablename DROP COLUMN $fname";
				foreach($sql as $s) {
					try {
						$statement = $db->connection->prepare($s);
						$statement->execute();
					}
					catch(Exception $e) {
						$logger = new \CodePunch\Base\CPLogger();
						$logger->error($e->getMessage());
					}
				}
				$responce['wipe'] = 'ok';
			}
			AUDIT::add($db, \CodePunch\DB\Audit::DELETE_CUSTOM_COLUMN, "Deleted $fname", "");
			$responce['status'] = 'ok';
			if($getcolumns)
				$responce['columns'] = $this->get_bool_adjusted_domain_column_details();
		}
		else
			$responce['error'] = "Not a valid custom column";
			
		return $responce;
	}
	
	###############################################################################
	
	private function setReportStyleSheet($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$cssdata = trim(UTIL::get_sanitized_request_string("css", ""));
		if($cssdata == "" || strlen($cssdata) < 24) {
			$folder = UTIL::get_install_folder_path();
			$datafile = $folder . "lib/layouts/css/reports/default.css";
			$cssdata = file_get_contents($datafile);
		}
		if($setup->setText("reports_css", $cssdata))
			$responce['status'] = 'ok';
		return $responce;
	}
	
	###############################################################################
	
	private function getReportStyleSheet($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$responce['css'] = $setup->getText("reports_css", "");
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###############################################################################
	
	private function downloadCSVData($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$multitable = UTIL::get_request_data_integer("mt", 0);
		$outputmode = strtolower(UTIL::get_sanitized_request_string("type", "csv"));
		$csvdownload = ($outputmode == "csv") ? 1 : 0;
		$columns = strtolower(trim(UTIL::get_sanitized_request_string("columns", "")));
		$colmode = UTIL::get_sanitized_request_string("colmode", "");
		$tablealias = "";
		$grid = new \CodePunch\UI\DataGrid($auth);
		$params = array();
		$where = $this->getWhereForDomains($grid, $tablealias, $params, false);
		$cid = UTIL::get_request_data_integer("cid", 0);
		$where[] = $db->getCategoryQueryForCurrentUser($cid, $auth);
		$sids = UTIL::get_sanitized_request_string("id", "");
		if($sids != "") {
			$sids = explode(",", $sids);
			foreach($sids as &$sid) {
				$sid = intval($sid);
			}
			$sids = array_unique($sids);
			$sids = implode(",", $sids);
			$where[] = "(d.sid IN ($sids))";
		}
		$sidx = UTIL::get_sanitized_request_string("sidx", "");
		$sord = UTIL::get_sanitized_request_string("sord", "asc");
		$delimiter = UTIL::get_sanitized_request_string("csv_sep", ",");
		$description = str_replace(array(",",";","\t"), " ", UTIL::get_sanitized_request_string("description", ""));
		$report = UTIL::get_sanitized_request_string("_repname", "");
		if($report != "") {
			$rows = $db->getFromTable("columns,sortcolumn,descending", $db->getReportSchedulerTableName(), "name=?", $report);
			if($rows !== false && isset($rows[0])) {
				$row = $db->fetchRow($rows, 0);
				if(isset($row['sortcolumn']) && $sidx == "")
					$sidx = $row['sortcolumn'];
				if(isset($row['descending']) && $sord == "")
					$sord = $row['descending'] ? "desc" : "asc";
				if(isset($row['columns']))
					$reportcolumns = array_filter(explode(";", $row['columns']));
				if(!isset($reportcolumns) || (isset($reportcolumns) && !count($reportcolumns))) {
					$clist = \CodePunch\Config\Settings::get_default_columns_for_groups();
					$columnlist = UTIL::get_from_array($clist[$report], "");
					$reportcolumns = array_filter(explode(";", $columnlist));
				}
			}
		}

		$validcolumns = $db->getGridReportColumnNames($multitable);
		if(isset($reportcolumns)) {
			$validreportcolumns = array();
			foreach($reportcolumns as $col) {
				if(UTIL::in_array_casei($col, $validcolumns))
					$validreportcolumns[] = $col;
				else if(UTIL::in_array_casei("s.".$col, $validcolumns) && $multitable)
					$validreportcolumns[] = "s." . $col;
				else if(UTIL::in_array_casei("d.".$col, $validcolumns))
					$validreportcolumns[] = "d." . $col;
				else if(UTIL::in_array_casei(str_replace("d.", "", $col), $validcolumns) && !$multitable)
					$validreportcolumns[] = $col;
			}
			$reportcolumns = $validreportcolumns;
			$reportcolumns = array_diff($reportcolumns, array('r_h_disp', 's.r_h_disp'));
		}
		
		if($colmode == "all") 
			$allowedcolumns = $validcolumns;
		else if($colmode == "domain") 
			$allowedcolumns = array("domain");
		else {
			if(isset($reportcolumns))
				$selected = $reportcolumns;
			else
				$selected = array_filter(explode(",", $columns));
			$allowedcolumns = array();
			foreach($selected as $col) {
				if(UTIL::in_array_casei($col, $validcolumns))
					$allowedcolumns[] = $col;
				else if(UTIL::in_array_casei("d.".$col, $validcolumns))
					$allowedcolumns[] = $col;
				else if(UTIL::in_array_casei("s.".$col, $validcolumns) && $multitable)
					$allowedcolumns[] = $col;
				else if(UTIL::in_array_casei(str_replace("d.", "", $col), $validcolumns) && !$multitable)
					$allowedcolumns[] = $col;
			}
		}
		
		$allowedcolumns = array_diff($allowedcolumns, array('r_h_disp', 's.r_h_disp', 'd.r_h_disp'));
		if($colmode == "all") {
			$preferredcolumns = isset($reportcolumns) ? $reportcolumns : array_filter(explode(",", $columns));
			$allowedcolumns = array_diff($allowedcolumns, $preferredcolumns);
			$allowedcolumns = array_merge($preferredcolumns, $allowedcolumns);
		}
		
		$filename = str_replace(array(",",";","\t"), "", UTIL::get_sanitized_request_string("filename", ""));
		$query =  implode(" AND ", array_filter($where));
		$select = implode(",", $allowedcolumns);
		$bgproc = UTIL::get_request_data_boolean("bgproc", false);
		if($bgproc && $csvdownload) {
			$uid = $auth->getUserID();
			$params = implode("|", $params);
			if($delimiter == ";")
				$delimiter = "semicolon";
			$taskinfo = "report;$select;$query;$params;$sidx;$sord;$filename;$uid;$delimiter;$description";
			$setup = new \CodePunch\Config\Settings($auth);
			$status = $setup->addPendingJob($taskinfo);
			$responce['status'] = $status ? 'ok' : 'notok';
			if($status)
				$responce['error'] = "A background job to generate the report was created. You will be able to download the report when it is ready.";
			AUDIT::add($db, AUDIT::DOMAIN_CSV_DOWNLOAD, "Background Job Created");
		}
		else {
			if($filename == "") {
				if($report != "") 
					$filename = str_replace(" ", "_", strtolower($report));
				else
					$filename = "domain-data.csv";
			}
			if(!UTIL::ends_with(strtolower($filename), ".csv"))
				$filename .= ".csv";
			$setup = new \CodePunch\Config\Settings($auth);
			$maxrows = $setup->getOption('max_download_rows', 5000);
			if($maxrows < 1000 || $maxrows > 10000)
				$maxrows = 5000;
			$data = $db->saveDomainRowsToCSVFile("", $allowedcolumns, $query, $params, $sidx, $sord, 500, $maxrows, $delimiter);
			if($data != "" && $csvdownload) {
				header("Content-Type:application/csv");
				header("Content-Disposition:attachment;filename=$filename");
				echo $data;
				AUDIT::add($db, AUDIT::DOMAIN_CSV_DOWNLOAD, "$filename");
				exit;
			}
			else {
				$responce['data'] = $data;
				$responce['status'] = 'ok';
			}
		}
		return $responce;
	}

	###############################################################################
	// id = n will fetch report with id
	// mode=csv will download the report csv data
	// if no id is specified, a list of all available reports will be returned

	private function getScheduledReport($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$uid = $auth->getUserID();
		if($db->isPrivilegedUser($uid)) {
			$query = "";
			$params = array();
		}
		else {
			$query = "userid=?";
			$params = array($uid);
		}
		$rid = UTIL::get_request_data_integer("id", 0);
		if($rid == 0) {
			$rows = $db->getFromTable("id,name,created_at,userid,description", $db->getReportsTableName(), $query, $params, "created_at", "desc", 100);
			if($rows !== false && is_array($rows)) {
				foreach($rows as &$row) {
					$row = array_change_key_case($row, CASE_LOWER);
					if(isset($row['userid']))
						$row['user'] = $db->getUserNameFromId($row['userid']);
					if(isset($row['name'])) {
						$reptime = $row['created_at'];
						$repname = $row['name'] . " @ $reptime";
						$row['name'] = $repname;
					}
				}
				$responce['server_time'] = date("Y-m-d H:i:s");
				$responce['data'] = $rows;
				$responce['status'] = 'ok';
			}
		}
		else {
			$query = $query == "" ? "id = ?" : $query . " AND id=?";
			$params[] = $rid;
			$rows = $db->getFromTable("id,name,created_at,userid,report,description", $db->getReportsTableName(), $query, $params, "created_at", "desc", 100);
			if($rows !== false && is_array($rows)) {
				$row = array_change_key_case($rows[0], CASE_LOWER);
				if(isset($row['userid']))
					$row['user'] = $db->getUserNameFromId($row['userid']);
				$mode = UTIL::get_sanitized_request_string("mode", "");
				if($mode == "csv") {
					$filename = str_replace(array(",",";","\t"), "", UTIL::get_sanitized_request_string("filename", ""));
					if($filename == "") {
						$reptime = $row['created_at'];
						$filename = str_replace(array(" ", "-", ":"), "_", $row['name'] . " $reptime");
					}
					if($filename == "")
						$filename = "domain-data.csv";
					if(!UTIL::ends_with(strtolower($filename), ".csv"))
						$filename .= ".csv";
					header("Content-Type:application/csv");
					header("Content-Disposition:attachment;filename=$filename");
					// See ReportManager.php > runCSVReport
					// This conversion is to compensate the conversion there.
					// The DB has some storage issue. This double conversion probably fixes it
					// The report column uses utf8mb3 and the DB session charset is not UTF-8.
					echo mb_convert_encoding($row['report'], 'ISO-8859-1', 'UTF-8');
					exit;
				}
				else {
					$responce['data'] = $row;
					$responce['status'] = 'ok';
				}
			}
		}
		return $responce;
	}

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

	private function deleteScheduledReport($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$rid = UTIL::get_request_data_integer("id", 0);
		if($rid != 0) {
			$uid = $auth->getUserID();
			if($db->isPrivilegedUser($uid)) {
				$query = "id=?";
				$params = array($rid);
			}
			else {
				$query = "id=? AND userid=?";
				$params = array($rid,$uid);
			}
			$status = $db->deleteFromTable($db->getReportsTableName(), $query, $params);
			if($status) {
				if($db->isPrivilegedUser($uid)) {
					$query = "";
					$params = array();
				}
				else {
					$query = "userid=?";
					$params = array($uid);
				}
				$rows = $db->getFromTable("id,name,created_at,userid,description", $db->getReportsTableName(), $query, $params, "created_at", "desc", 100);
				if($rows !== false && is_array($rows)) {
					foreach($rows as &$row) {
						$row = array_change_key_case($row, CASE_LOWER);
						if(isset($row['userid']))
							$row['user'] = $db->getUserNameFromId($row['userid']);
						if(isset($row['name'])) {
							$reptime = $row['created_at'];
							$repname = $row['name'] . " @ $reptime";
							$row['name'] = $repname;
						}
					}
					$responce['server_time'] = date("Y-m-d H:i:s");
					$responce['data'] = $rows;
					$responce['status'] = 'ok';
				}
			}
			else {
				$responce['error'] = 'Unable to delete entry';
			}
		}
		return $responce;
	}

	###############################################################################
	
	private function exportPostData($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		header('Content-Encoding: UTF-8');
		$csvdata = UTIL::get_sanitized_request_string("export", "");
		$csvdata = str_replace("&#34;", '"', $csvdata);
		$filename = UTIL::get_sanitized_request_string("filename", "domain-columns.csv");
		$mode = strtolower(UTIL::get_sanitized_request_string("type", "txt"));
		if($mode == "txt") 
			header('Content-type: text/txt; charset=UTF-8');
		else
			header('Content-type: text/csv; charset=UTF-8');
		header('Content-Length: ' . strlen($csvdata));
		header('Content-Disposition: attachment; filename=' . $filename);
		header('Content-Transfer-Encoding: binary');
		$fsize = strlen($csvdata);
		AUDIT::add($db, AUDIT::DOMAIN_CSV_DOWNLOAD, "$filename ({$fsize} bytes)");
		echo $csvdata;
		exit;
	}

	###############################################################################
	
	private function importData($responce)
	{
		$file = UTIL::get_sanitized_request_string("f", "");
		if(empty($_FILES['csv_file']['name'])) {
			if($file == "") {
				$responce['error'] = 'Please select a file to upload';
				return $responce;
			}
		}
		return $this->doFileUploadProc($responce, $file);
	}
	
	###############################################################################
	
	private function doFileUploadProc($responce, $file)
	{
		if($file == "") {
			$file_name = $_FILES['csv_file']['name'];
			$file_error = $_FILES['csv_file']['error'];
			$file_tmp = $_FILES['csv_file']['tmp_name'];
			$file_type = $_FILES["csv_file"]["type"];
			$file_status = is_uploaded_file($file_tmp);
			$file_size = $_FILES["csv_file"]["size"];
		}
		else {
			$path = UTIL::get_install_folder_path() . $file;
			$file_name = basename($path);
			$file_error = 0;
			$file_tmp = $path;
			$file_type = UTIL::get_mime_type($path);
			$file_status = is_file($file_tmp);
			$file_size = filesize($file_tmp);
		}
		
		if($file_error > 0) {
			$responce['error'] = $file_error;
			return $responce;
		}
		if(empty($file_name)) {
			$responce['error'] = 'No file sent.';
			return $responce;
		}

		$tmp = $file_tmp;

		$responce['data'] = array();
		if($file_status)
		{		
			$responce['data']['file'] = $file_name;
			$responce['data']['type'] = $file_type;
			
			$sep = UTIL::get_sanitized_request_string("csv_sep", ",");
				
			$size = $file_size / 1024;
			$displaysep = "TAB";
			if($sep == ";")
				$displaysep = "Semicolon";
			else if($sep == ",")
				$displaysep = "Comma";
			
			$responce['data']['size'] = round($size, 3);
			$responce['data']['seperator'] = $displaysep;
			
			$f = fopen($tmp, 'r');
			$first_line = fgets($f);
			fclose($f);
			if (mb_check_encoding($first_line, 'UTF-8')) 
				$responce = $this->uploadCSVData($responce, $tmp, $sep);	
			else {
				$responce['data']['error'] = "The file is not UTF-8 or ANSI Encoded.";	
				$responce['error'] = "The file is not UTF-8 or ANSI Encoded.";	
			}
		}
		return $responce;
	}
	
	###############################################################################

	private function uploadCSVData(&$responce, $filename, $sep=",")
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$cid = UTIL::get_request_data_integer("cid", 0);
		$add_to_luq = UTIL::get_request_data_boolean("csv-luq", false);
		$writeprotect = UTIL::get_request_data_boolean("csv-writeprotect", false);
		$emptycat = UTIL::get_request_data_boolean("emptycat");
		
		if($emptycat && $cid > 1) {
			$status = $db->deleteFromTable($db->getCategoryConnectionTableName(), "cid=?", array($cid));
		}
		
		$columninfo = $db->getDetailsOfAllDomainColumns();
		$validfields = array_keys($columninfo);
		$labelinfo = array();
		foreach($validfields as $f) {
			$label = strtolower($columninfo[$f]['label']);
			$labelinfo[$label] = $f;
		}
		
		$i = 0;
		$row = 0;
		$numColumns = 0;
		$columnHeaders = array();
		$usablefields = array();
		$usablecolindexes = array();
		$ignoredcolumns = array();
		$responce['failed'] = 0;
		$responce['added'] = 0;
		$responce['existing'] = 0;
		$responce['luq'] = 0;
		$responce['subdomains'] = 0;
		$sep = UTIL::sanitizeCSVDelimiter($sep);
		$logger = new \CodePunch\Base\CPLogger();
		$added_domains = array();
		$file_handle = fopen($filename, "r");

		while (!feof($file_handle) ) {
			$text_line = fgetcsv($file_handle, 2048, $sep);
			$column_count = is_array($text_line) ? count($text_line) : 0;
			
			$row++;
			# ignore empty rows at top.
			if($column_count == 1 && $i == 0 && $text_line[0] == "")
				continue;
			
			if($i == 0) {
				$numColumns = $column_count;
				$columnHeaders = $text_line;
				$cindex = 0;
				foreach($columnHeaders as $column) {
					$column = str_replace(chr(0), '', $column);
					$column = trim($column, "\" ");
					if(in_array($column, $validfields)) {
						$field = $column;
						$usablecolindexes[] = $cindex;
						$usablefields[] = $field;
						$responce['columns'][] = $field;
						$responce['column_index'][] = $cindex;
					}
					else if(isset($labelinfo[strtolower($column)])) {
						$field = $labelinfo[strtolower($column)];
						$usablecolindexes[] = $cindex;
						$usablefields[] = $field;
						$responce['columns'][] = $field;
						$responce['column_index'][] = $cindex;
					}
					else {
						$ignoredcolumns[] = $column;
					}
					$cindex++;
				}
				if(count($usablecolindexes))
					$responce['status'] = 'ok';
				else
					$responce['error'] = "No usable columns found"; 
			}
			else if(count($usablecolindexes)) {
				$domain = "";
				if($numColumns && $numColumns == $column_count) {
					$domainData = array();
					$cc = 0;
					foreach($usablecolindexes as $ci) {
						$centry = str_replace(chr(0), '', $text_line[$ci]);
						$key = $usablefields[$cc++];
						if($key == "domain")
							$domain = trim($centry);
						else if($centry != "") {
							$domainData[$key] = $centry;
						}
					}
					if($domain != "") {
						$ai = $db->addDomain($domain, "", $cid);
						$addDefaultSD = false;
						$addToLUQ = false;
						if($ai !== false) {
							if(count($added_domains) < 25)  // Sample for Audit Entry
								$added_domains[] = $domain;
							$sid = UTIL::get_from_array($ai['added'][0], 0);
							if($sid > 0) {
								$addDefaultSD = true;
								if($add_to_luq && $writeprotect === false)
									$addToLUQ = true;
								$responce['added']++;
							}
							if($sid == 0) {
								$sid = UTIL::get_from_array($ai['existing'][0], 0);
								$responce['existing']++;
							}
							if($sid > 0) {
								$lookup = new \CodePunch\LU\LookupManager($auth);
								$setup = new \CodePunch\Config\Settings($auth);
								if($addToLUQ) {
									$status = $lookup->addDefaultLookups($sid);
									foreach($status as $s) {
										if($s)
											$responce['luq']++;
									}
								}
								if($addDefaultSD) {
									$sddata = $setup->getOption('default_sub_domains', "");
									$sadded = $db->addDefaultSubdomains($sid, $sddata);
									$responce['subdomains'] += $sadded;
								}
								
								$domainData['sid'] = $sid;
								if($writeprotect)
									$domainData['write_protect'] = '1';
								// Format Dates
								$db->formatDates($domainData);
								$status = $db->updateDomainTable($domainData);
								if($status === false) {
									$responce['failed']++;
									$logger->error("Column update failed for $domain");
								}
							}
						}
					}
					unset($domainData);
				}
				else
				{
				}
			}
			$i++;
		}
		
		// Construct any error messages
		// Add Audit Entries
		$formatted = UTIL::implode_limited(", ", $added_domains, 20);
		AUDIT::add($db, AUDIT::IMPORT_CSV_DATA, $responce['added'] . " domains imported from file", $formatted);
		
		fclose($file_handle);
		return $responce;
	}
	
	###########################################################################
	
	private function get_user_list()
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$users = array();
		$rows = $db->getFromTable("id,name,display_name", $db->getUserTableName(), "id in (SELECT userid FROM " . $db->getUserGroupAccessTableName() . " WHERE gid=1 AND userid=" . $db->getUserTableName() . ".id)");
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$users[] = array('id'=>$row['id'], 'name'=>$row['name'], 'dname'=>$row['display_name'], 'admin'=>true);
			}
		}
		$rows = $db->getFromTable("id,name,display_name", $db->getUserTableName(), "id NOT in (SELECT userid FROM " . $db->getUserGroupAccessTableName() . " WHERE gid=1 AND userid="  . $db->getUserTableName() . ".id)");
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$users[] = array('id'=>$row['id'], 'name'=>$row['name'], 'dname'=>$row['display_name'], 'admin'=>false);
			}
		}
		return $users;
	}
	
	###########################################################################
	
	private function getUserList($responce)
	{
		$users = $this->get_user_list();
		$responce['status']  = 'ok';
		$responce['users'] = $users;
		return $responce;
	}
	
	###########################################################################
	
	private function getUserInfo($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		
		$rows = $db->getFromTable("id,name,display_name,added_on,last_sign_in_stamp,active,rights,acslevel", $db->getUserTableName(), "name=?", array($name));
		if($rows !== false && isset($rows[0])) {
			$row = $db->fetchRow($rows, 0);
			$row = UTIL::array_bool_to_int($row);
			$userid = $row['id'];
			$rid = $db->getFromTable("id", $db->getUserGroupAccessTableName(), "gid=1 AND userid=?", array($userid));
			if($rid !== false && isset($rid[0])) {
				$row['admin'] = true;
				$row['rights'] = 65535;
			}
			else
				$row['admin'] = false;
			$row['cids'] = array();
			$cids = $db->getFromTable("cid", $db->getUserCategoryAccessTableName(), "userid=?", array($userid));
			if($cids !== false) {
				$index = 0;
				foreach($cids as $cid) {
					$cid = $db->fetchRow($cids, $index++);
					$row['cids'][] = intval($cid['cid']); 
				}
			}
			if(isset($row['display_name'])) {
				$row['dname'] = $row['display_name'];
				unset($row['display_name']);
			}
			$responce['status'] = 'ok';
			$responce['data'] = $row;
		}
		
		return $responce;
	}
	
	###########################################################################
	
	private function addUser($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$pass = UTIL::get_sanitized_request_string("pass", "");
		$dname = UTIL::get_sanitized_request_string("dname", "");
		$rights = UTIL::get_request_data_integer("rights", 0);
		$status = UTIL::get_request_data_integer("status", 0);
		$acslevel = UTIL::get_request_data_integer("acslevel", 0);
		$getlist = UTIL::get_request_data_boolean("list", false);
		$cids = UTIL::get_sanitized_request_string("cids", "");
		$cids = explode(",", $cids);
		$groups = array();
		if($rights == 65535)
			$groups[] = "admin";
		if(strlen($name) >= 5 && strlen($pass) >= 8) {
			try {
				if($db->createUser($name, $pass, $status, $groups, $cids, $rights, $dname, $acslevel)) {
					$responce['status'] = 'ok';
					if($getlist)
						$responce['users'] =  $this->get_user_list();
					AUDIT::add($this->authentication->getDatabase(), AUDIT::ADD_USER, null, $name);
				}
			}
			catch(Exception $e) {
				$logger = new \CodePunch\Base\CPLogger();
				$logger->error($e->getMessage());
				$responce['error'] = $e->getMessage();
			}
		}
		else
			$responce['error'] = 'Invalid user name or password';
		
		return $responce;
	}
	
	###########################################################################
	
	private function createSAMLUser()
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = "samluser";
		$pass = UTIL::random_string(10);
		$dname = "SAML User";
		$rights = \CodePunch\DB\DomainDB::ALLOW_VIEW;
		$status = 1;
		$acslevel = 0;
		$cids = array();
		$groups = array();
		$uid = $db->findOneOf($db->getUserTableName(), "name", $name, "id");
		if($uid === false) {
			try {
				if($db->createUser($name, $pass, $status, $groups, $cids, $rights, $dname, $acslevel)) {
					return $db->findOneOf($db->getUserTableName(), "name", $name, "id");
				}
			}
			catch(Exception $e) {
				$logger = new \CodePunch\Base\CPLogger();
				$logger->error($e->getMessage());
			}
		}
		else
			return $uid;
		return false;
	}
	
	###########################################################################
	
	private function editUser($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$pass = UTIL::get_sanitized_request_string("pass", "");
		if(strlen($name) >= 6) {
			if($pass != "" && strlen($pass) < 8)
				$responce['error'] = 'The password should be 8 characters or more';
			else {
				$uid = $db->findOneOf($db->getUserTableName(), "name", $name, "id");
				if($uid !== false && $uid > 0) {
					$uidata = array('id'=>$uid);
					$admin = false;
					if(UTIL::is_request_key_set('dname'))
						$uidata['display_name'] = UTIL::get_sanitized_request_string("dname", "");
					if(UTIL::is_request_key_set('status'))
						$uidata['active'] = UTIL::get_request_data_integer("status", 0);
					if(UTIL::is_request_key_set('acslevel'))
						$uidata['acslevel'] = UTIL::get_request_data_integer("acslevel", 0);
					if(UTIL::is_request_key_set('pass')) 
						$uidata['password'] = UTIL::generateHash($pass);
					if(UTIL::is_request_key_set('rights')) {
						$rights = UTIL::get_request_data_integer("rights", 0);
						$uidata['rights'] = $rights;
						if($rights == 65535)
							$admin = true;
						if($admin) 
							$groups = array('admin');
						else
							$groups = array('standard');
						$db->deleteFromTable($db->getUserGroupAccessTableName(), "userid=?", array($uid));
						$db->addUserToGroupsByName($uid, $groups);
					}
					$db->editTable($db->getUserTableName(), $uidata, "id");
					if(UTIL::is_request_key_set('cids')) {
						$cids = UTIL::get_sanitized_request_string("cids", "");
						$cids = explode(",", $cids);
						$db->deleteFromTable($db->getUserCategoryAccessTableName(), "userid=?", array($uid));
						$db->addUserToCategoriesByID($uid, $cids);
					}
					if($uidata['active'] == 0) {
						// deactivate Google 2FA
						$edata = array('id'=>$uid, 'google_2fa_code'=>'', 'google_2fa_init'=>'');
						$db->editTable($db->getUserTableName(), $edata, "id");
					}
					$responce['status'] = 'ok';
				}
				else
					$responce['error'] = "user $name not found";
			}
		}
		else
			$responce['error'] = 'user name should be 6 characters or more';
		
		return $responce;
	}
	
	###########################################################################
	
	private function deleteUser($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$getlist = UTIL::get_request_data_boolean("list", false);
		
		$uid = $db->findOneOf($db->getUserTableName(), "name", $name, "id");
		if($uid !== false && $uid > 0) {
			$db->deleteFromTable($db->getUserGroupAccessTableName(), "userid=?", array($uid));
			$db->deleteFromTable($db->getUserCategoryAccessTableName(), "userid=?", array($uid));
			$db->deleteFromTable($db->getUserTableName(), "name=?", array($name));
			$responce['status'] = 'ok';
			if($getlist)
				$responce['users'] =  $this->get_user_list();
		}
		else
			$responce['error'] = "User $name not found";
		
		return $responce;
	}
	
	###########################################################################
	
	private function getCurrentUserInfo($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$cids = $db->getCategoryIDsForCurrentUser($auth);
		$cinfo = array();
		foreach($cids as $cid) {
			$name = $db->findOneOf($db->getCategoryTableName(), "cid", $cid, "name");
			$cinfo[] = array('cid'=>$cid, 'name'=>$name);
		}
		$licenseinfo = $auth->getLicense();
		
		$responce['status'] = 'ok';
		$responce['data'] = array(
			'rights'=>$auth->getUserAccess(),
			'uid'=>$auth->getUserID(),
			'cids'=>$cinfo,
			'license'=>$licenseinfo
		);
		return $responce;
	}
	
	###########################################################################
	
	private function getGoogleFontList($responce)
	{
		$fontcat = UTIL::get_sanitized_request_string("cat", "");
		$responce['status'] = 'ok';
		$responce['fonts'] = UTIL::get_google_font_list("AIzaSyDirUljZQibWx4T3k-iXn6-rCkq6neYzsU", $fontcat);
		return $responce;
	}
	
	###########################################################################
	
	private function getThemeList($responce)
	{
		$responce['status'] = 'ok';
		$responce['themes'] = array();
		$colormode = UTIL::get_sanitized_request_string("mode", "light");
		$folder = realpath(UTIL::get_install_folder_path() . "lib/layouts/colors/$colormode/themes/");
		$themes = UTIL::find_all_matched_folders($folder);
		foreach($themes as $theme) {
			$tf = realpath(UTIL::get_install_folder_path() . "lib/layouts/colors/$colormode/themes/$theme/") . DIRECTORY_SEPARATOR . "styles.css";
			if(is_file($tf) && $theme != 'Contrast')
				$responce['themes'][] = $theme;
		}
		sort($responce['themes']);
		$responce['current'] = strtolower(\CodePunch\UI\Layout::getThemeName());
		return $responce;
	}

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

	private function getTasksList($responce)
	{
		$responce['status'] = 'ok';
		$responce['tasks'] = array('import_SSL_Certificates', 'set_RDAP_Servers');
		sort($responce['tasks']);
		return $responce;
	}

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

	private function getWebshot($responce)
	{
		$domain = UTIL::get_sanitized_request_string("domain", "");
		$webshotfolder = UTIL::get_log_folder_path() . "websites" . DIRECTORY_SEPARATOR . "webshots" . DIRECTORY_SEPARATOR;
		if(!is_dir($webshotfolder))
			$webshotfolder = UTIL::get_log_folder_path() . "webshots" . DIRECTORY_SEPARATOR;
		$domain = UTIL::idn_convert($domain);
		$imgfile = $webshotfolder . "$domain.jpg";
		if(!is_file($imgfile)) 
			$imgfile = $webshotfolder . "$domain.png";
		else if(!is_file($imgfile)) 
			$imgfile = $webshotfolder . "$domain.webp";
		if(is_file($imgfile)) {
			UTIL::show_image_from_file($imgfile);
			exit;
		}
		else {
			UTIL::show_image_from_file(UTIL::get_install_folder_path() . "lib/layouts/images/icons/256/transparent.png");
			exit;
		}
		return $responce;
	}
	
	###########################################################################

	private function getCookies($responce, $getall=false)
	{
		return $responce;
	}

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

	private function getUI($responce, $getall=true)
	{
		if($getall) {
			$responce = $this->getThemeList($responce);
			$responce = $this->getGoogleFontList($responce);
		}
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$responce['minimize_transparency_level'] = $setup->getBoolean("minimize_transparency_level", true) ? 1 : 0;
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################

	private function setUI($responce)
	{
		$ignore_transparency = strtolower(UTIL::get_sanitized_request_string("minimize_transparency_level", ""));
		if($ignore_transparency != "") {
			$ignore_transparency = UTIL::str_to_bool($ignore_transparency);
			$setup = new \CodePunch\Config\Settings($this->authentication);
			$setup->setOption("minimize_transparency_level", $ignore_transparency ? 1 : 0);
			$responce['minimize_transparency_level'] = $setup->getBoolean("minimize_transparency_level", true) ? 1 : 0;
			$responce['status'] = 'ok';
		}
		return $responce;
	}

	###########################################################################
	
	private function deleteRegistrarAPIProfile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		if($db->hasRow($db->getAPIProfilesTableName(), "name", $name)) {
			$status = $db->deleteFromTable($db->getAPIProfilesTableName(), "name=?", array($name));
			if($status !== false && $status > 0)
				$responce['status'] = 'ok';
			AUDIT::add($this->authentication->getDatabase(), AUDIT::REGISTRAR_PROFILE_DELETED, null, $name);
		}
		else
			$responce['error'] = 'profile not found';
		return $responce;
	}

	###########################################################################
	
	private function addRegistrarAPIProfile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$registrar = UTIL::get_sanitized_request_string("registrar", "");
		$class = "\\CodePunch\\LU\\Registrars\\" . $registrar;
		if(class_exists($class))  {
			$keys = $class::getKeyNames();
			
			$apikeys = "";
			foreach($keys as $key) {
				$apikeys .= "[$key]=\n";
			}
			$rows = $db->getFromTable("*", $db->getAPIProfilesTableName(), "name=?", array($name));
			if($rows !== false && !isset($rows[0])) {
				$status = $db->insertIntoTable($db->getAPIProfilesTableName(), array('apikeys'=>$apikeys,'name'=>$name, 'registrar'=>$registrar));
				if($status && $status !== false) {
					$responce['status'] = 'ok';
					AUDIT::add($this->authentication->getDatabase(), AUDIT::REGISTRAR_PROFILE_CREATED, "for $registrar", $name);
				}
			}
		}
		else {
			$responce['error'] = "Unknown Registrar ($registrar)";
		}
		return $responce;
	}

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

	private function setRegistrarAPIProfile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$name = UTIL::get_sanitized_request_string("name", "");
		$keys = UTIL::get_sanitized_request_string("keys", "");
		$data = UTIL::get_sanitized_request_string("data", "");
		$keynames = explode("\n", $keys);
		$udata = explode("\n", $data);
		$apikeys = "";
		if(count($keynames) == count($udata)) {
			$index = 0;
			foreach($keynames as $k) {
				$kvalue = $udata[$index++];
				$kvalue = $auth->encrypt($kvalue);
				$apikeys .= "[$k]=" . $kvalue . "\n";
			}
			$apikeys = trim($apikeys);
			$rows = $db->getFromTable("*", $db->getAPIProfilesTableName(), "name=?", array($name));
			if($rows !== false && isset($rows[0])) {
				$status = $db->updateTable($db->getAPIProfilesTableName(), array('apikeys'=>$apikeys), "name=?", array($name));
				if($status && $status !== false) {
					AUDIT::add($this->authentication->getDatabase(), AUDIT::REGISTRAR_PROFILE_EDITED, $keys, $name);
					$ci = $db->getRegistrarAPIClass($name, $auth);
					if($ci['class'] != "" && count($ci['params'])) {
						$luManager = new \CodePunch\LU\LookupManager($auth);
						$regapi = new $ci['class']($luManager, ...$ci['params']);
						if(method_exists($regapi, "isWorking")) {
							$status = $regapi->isWorking();
							if($status !== true) {
								$responce['error'] = $status;
								return $responce;
							}
						}
					}
					$responce['status'] = 'ok';
				}
			}
		}
		else
			$responce['error'] = 'Key data pair mismatch: please provide all the details';
		return $responce;
	}
	
	###########################################################################

	private function actionRegistrarAPIProfile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$apiprofile = UTIL::get_sanitized_request_string("name", "");
		$method = UTIL::get_sanitized_request_string("action", "");
		$ci = $db->getRegistrarAPIClass($apiprofile, $auth);
		if($ci['class'] != "" && count($ci['params'])) {
			$luManager = new \CodePunch\LU\LookupManager($auth);
			$regapi = new $ci['class']($luManager, ...$ci['params']);
			if(method_exists($regapi, $method)) {
				$responce['action'] = $method;
				$responce['api'] = $apiprofile;
				$responce['registrar'] = $ci['registrar'];
				try {
					if($method == "balance") {
						$status = $regapi->balance();
						$responce['status'] = 'ok';
						$responce['data'] = $status;
					}
					else if($method == "pricing") {
						// Prices are cached for 1 day into DB
						$for = UTIL::get_sanitized_request_string("for", "register");
						$tld = UTIL::get_sanitized_request_string("tld", "");
						if($tld == "") {
							$domain = UTIL::get_sanitized_request_string("domain", "");
							if($domain != "") {
								$di  = UTIL::clean_domain_name($domain, $db->getDomainSuffixList());
								$tld = $di['tld'];
								$responce['domain'] = $domain;
							}
						}
						$promocode = UTIL::get_sanitized_request_string("promocode", "");
						$onedaybefore = date("Y-m-d H:i:s", time() - (24*3600)); // 1 day before
						$rows = $db->getFromTable("prices,last_update", $db->getTLDPriceTableName(), "tld=? AND profile=? AND type=? AND last_update > ?", array($tld, $apiprofile, $for, $onedaybefore));
						if($rows && isset($rows[0])) {
							$row = $db->fetchRow($rows, 0);
							$status = json_decode($row['prices']);
							$responce['cached_at'] = $row['last_update'];
						}
						else
							$status = $regapi->pricing(array('action'=>$for, 'tld'=>$tld, 'promocode'=>$promocode));
						$responce['for'] = $for;
						$responce['tld'] = $tld;
						$responce['status'] = 'ok';
						$responce['data'] = $status;
						$prices = json_encode($status);
						if(!isset($responce['cached_at'])) {
							$nowtime = date("Y-m-d H:i:s");
							if($db->hasRow($db->getTLDPriceTableName(), array('tld', 'profile', 'type'), array($tld, $apiprofile, $for))) {
								$db->updateTable($db->getTLDPriceTableName(), array('prices'=>$prices, 'last_update'=>$nowtime), "tld=? AND profile=? AND type=?", array($tld, $apiprofile, $for));
							}
							else {
								$db->insertIntoTable($db->getTLDPriceTableName(), array('tld'=>$tld, 'profile'=>$apiprofile, 'type'=>$for, 'prices'=>$prices, 'last_update'=>$nowtime));
							}
						}
					}
					else if($method == "availcheck") {
						$domains = UTIL::get_sanitized_request_string("domains", "");
						$status = $regapi->availcheck($domains);
						$responce['status'] = 'ok';
						$responce['data'] = $status;
					}
					else if($method == "renew") {
						$domain = UTIL::get_sanitized_request_string("domain", "");
						$years = UTIL::get_request_data_integer("years", 1);
						$promocode = UTIL::get_sanitized_request_string("promocode", "");
						$payid = UTIL::get_sanitized_request_string("payid", "");
						$requestdata = array('domain'=>$domain, 'years'=>$years, 'promocode'=>$promocode, 'payid'=>$payid);
						$status = $regapi->renew($requestdata);
						$responce['status'] = 'ok';
						$responce['data'] = $status;
						$params = "$domain for $years year(s)";
						AUDIT::add($this->authentication->getDatabase(), AUDIT::DOMAIN_RENEWAL, $params);
						// Lookup the Domain 
						$lookup = new \CodePunch\LU\LookupManager($auth);
						$sid = $db->getDomainID($domain);
						if($sid !== false && ctype_digit($sid))
							$lookup->addMultiLookups($sid, 1, 0); // 1 = DOMAIN_RECORD lookup, interval = 0
					}
					else if($method == "register") {
						$domain = UTIL::get_sanitized_request_string("domain", "");
						$sampledomain = UTIL::get_sanitized_request_string("copyfrom", "");
						$years = UTIL::get_request_data_integer("years", 1);
						$promocode = UTIL::get_sanitized_request_string("promocode", "");
						$payid = UTIL::get_sanitized_request_string("payid", "");
						$requestdata = array('domain'=>$domain, 'years'=>$years, 'promocode'=>$promocode, 'payid'=>$payid);
						if($sampledomain != "")
							$requestdata['copyfrom'] = $sampledomain;
						else {
							$domaindata = UTIL::get_sanitized_request_string("data", "");
							if($domaindata == "") {
								// Find the first domain with the requested API profile.
								$rows = $db->getFromTable("domain", $db->getDomainTableName(), "api_profile=?", array($apiprofile), "", "asc", 1);
								if($rows && count($rows)) {
									$row = $db->fetchRow($rows, 0);
									$sampledomain = $row['domain'];
									$requestdata['copyfrom'] = $sampledomain;
								}
							}
							else {
								$domaindata = json_decode($domaindata, true);
								$requestdata['data'] = $domaindata;
							}
						}
						$status = $regapi->register($requestdata);
						$responce['status'] = 'ok';
						$responce['data'] = $status;
						if(UTIL::str_to_bool($status['registered']) === true) {
							$params = "$domain for $years year(s) at $apiprofile";
							if($sampledomain != "")
								$params .= ", contact and NS copied from $sampledomain";
							AUDIT::add($this->authentication->getDatabase(), AUDIT::DOMAIN_NEW_REGISTRATION, $params);
							// add the name
							$cid = UTIL::get_request_data_integer("cid", 0);
							$das = $this->addDomainsFromText($domain, $cid, $apiprofile);
						}
					}
					else if($method == "getcreatedata") {
						$domain = UTIL::get_sanitized_request_string("domain", "");
						if($domain == "") {
							// Find the first domain with the requested API profile.
							$rows = $db->getFromTable("domain", $db->getDomainTableName(), "api_profile=?", array($apiprofile), "", "asc", 1);
							if($rows && count($rows)) {
								$row = $db->fetchRow($rows, 0);
								$domain = $row['domain'];
							}
						}
						$status = $regapi->getcreatedata($domain);
						$responce['status'] = 'ok';
						$responce['data'] = $status;
					}
					else if($method == "getns") {
						$domain = UTIL::get_sanitized_request_string("domain", "");
						$status = $regapi->getns($domain);
						$responce['status'] = 'ok';
						$responce['data'] = $status;
					}
					else if($method == "setns") {
						$domain = UTIL::get_sanitized_request_string("domain", "");
						$ns = UTIL::get_sanitized_request_string("ns", "");
						$status = $regapi->setns(array('domain'=>$domain, 'ns'=>$ns));
						$responce['status'] = 'ok';
						$responce['data'] = $status;
						$params = "$domain - $ns";
						AUDIT::add($this->authentication->getDatabase(), AUDIT::DOMAIN_REGISTRAR_NS_SET, $params);
					}
				}
				catch(Exception $e) {
					$logger = new \CodePunch\Base\CPLogger();
					$domain = !isset($domain) ? "" : $domain;
					$responce['error'] = "Registrar API##$apiprofile##$method##$domain## " . $e->getMessage();
					$logger->error($responce['error']);
				}
			}
			else {
				$regsupported = $regapi->supported();
				$apisupported = array('balance', 'pricing', 'availcheck', 'getns', 'setns', 'renew', 'register', 'getcreatedata');
				$supported = array_intersect($regsupported, $apisupported);
				$responce['error'] = "$method is currently not supported. The only supported methods for this registrar / data source are " . implode(", ", $supported);
			}
		}
		else
			$responce['error'] = "$apiprofile not found";
		return $responce;
	}

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

	private function testRegistrarAPIProfile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$apiprofile = UTIL::get_sanitized_request_string("name", "");
		$ci = $db->getRegistrarAPIClass($apiprofile, $auth);
		if($ci['class'] != "" && count($ci['params'])) {
			$luManager = new \CodePunch\LU\LookupManager($auth);
			$regapi = new $ci['class']($luManager, ...$ci['params']);
			if(method_exists($regapi, "isWorking")) {
				$status = $regapi->isWorking();
				if($status === true)
					$responce['status'] = 'ok';
				else
					$responce['error'] = $status;
			}
			else
				$responce['error'] = "Unable to test at this time. Please check by attempting to import data.";
		}
		return $responce;
	}

	###########################################################################
	
	private function getRegistrarAPIProfiles($responce)
	{
		$responce['data'] = array();
		$responce['profiles'] = array();
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$rows = $db->getFromTable("*", $db->getAPIProfilesTableName());
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$responce['profiles'][] = $row['name'];
				$info = array();
				$db->getRegistrarAPIInfo($row, $info, $auth);
				// Mask the keys for security
				foreach($info['params'] as &$p) {
					if($p != "")
						$p = "***";
				}
				$responce['data'][$row['name']] = $info;
			}
		}
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function setLicense($responce)
	{
		$code = UTIL::get_sanitized_request_string("data", "");
		$code = str_replace(["\n", "\r", " ", "\t"], "", $code);
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$status = $setup->setEncryptedOption('license_key_code_v6', $code);
		if($status !== false && $status == 1) {
			$responce['status'] = 'ok';
			$params = ($code == "-" ? "License Code Reset" : "License Code Set");
			AUDIT::add($this->authentication->getDatabase(), AUDIT::GENERIC_ACTION, $params);
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getBackgroundImageList($responce)
	{
		$imagefiles = array();
		$folder = UTIL::get_install_folder_path() . "lib/layouts/images/";
		$flist = array('stock', 'custom', 'backgrounds');
		foreach($flist as $f) {
			$matches = glob($folder . $f . DIRECTORY_SEPARATOR . "*.{jpg,png,jpeg,webp}", GLOB_BRACE);
			if($matches !== false && is_array($matches)) {
				foreach($matches as $match) {
					$fn = basename($match);
					$imagefiles[$fn] = "lib/layouts/images/$f/$fn";
				}
			}
		}
		$responce['bgfiles'] = $imagefiles;
		return $responce;
	}
	
	###########################################################################
	
	private function getAllCategoryInfo($responce)
	{
		$responce = $this->getCategoriesForUser($responce);
		$folder = UTIL::get_install_folder_path() . "lib/layouts/images/icons/categories/light/";
		$files = UTIL::find_all_matched_files($folder, "*.png");
		foreach($files as $file)
			$responce['icons'][] = $file;
		sort($responce['icons']);
		if(UTIL::get_request_data_boolean("users", false) && $this->authentication->isAdmin())
			return $this->getUserList($responce);
		else
			return $responce;
	}
	
	###########################################################################
	
	private function getAllAutoQueryInfo($responce)
	{
		$aqid = UTIL::get_request_data_integer('id', 0);
		if($aqid == 0) {
			$responce = $this->getAutoQueryList($responce);
			$folder = UTIL::get_install_folder_path() . "lib/layouts/images/icons/categories/light/";
			$files = UTIL::find_all_matched_files($folder, "*.png");
			foreach($files as $file)
				$responce['icons'][] = $file;
			sort($responce['icons']);
		}
		else if($aqid > 1) 
			$responce = $this->getAutoQuery($responce);
		return $responce;
	}
	
	###########################################################################
	
	private function setAllCategoryInfo($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$cid = UTIL::get_request_data_integer("id", 0);
		$name = UTIL::get_sanitized_request_string("name", "");
		$caticon = UTIL::get_sanitized_request_string("icon", "");
		$sortids = UTIL::get_sanitized_request_string("ids", "");
		$userids = UTIL::get_sanitized_request_string("uids", "");
		
		$changed = 0;
		if($cid > 1) {
			$cdata = array();
			if($caticon != "")
				$cdata['caticon'] = $caticon;
			if($name != "")
				$cdata['name'] = $name;
			$incr = $db->updateTable($db->getCategoryTableName(), $cdata, "cid=?", array($cid));
			if($incr) 
				$changed += $incr;
			
			$db->deleteFromTable($db->getUserCategoryAccessTableName(), "cid=?", array($cid));
			if($userids != "") {
				$uids = explode(",", $userids);
				foreach($uids as $uid) {
					$status = $db->insertIntoTable($db->getUserCategoryAccessTableName(), array('cid'=>$cid, 'userid'=>$uid));
					if($status) 
						$changed++;
				}
			}
			
			$responce['icon'] = $caticon;
			$responce['name'] = $name;
			$responce['id'] = $cid;
		}
		if($sortids != "") {
			$cids = explode(",", $sortids);
			$sortindex = 1;
			foreach($cids as $id) {
				if($id > 1) {
					if($db->updateTable($db->getCategoryTableName(), array('sortindex'=>$sortindex), "cid=?", array(intval($id))))
						$changed++;
					$sortindex++;
				}
			}
		}
		
		$responce['status'] = 'ok';
		$responce['changed'] = $changed;
		return $responce;
	}
	
	###########################################################################
	
	private function resetAllAutoQueryInfo($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$tabledatafolder = UTIL::get_install_folder_path() . "lib/php/CodePunch/DB/Init/";
		$wipe = UTIL::get_request_data_integer("wipe", 0);
		if($wipe)
			$db->truncateTable($db->getAutoQueryTableName());
		$db->updateInitDataFromFile($tabledatafolder, array($db->getTableNameWithoutPrefix($db->getAutoQueryTableName())), "name");
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function setAllAutoQueryInfo($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$aqid = UTIL::get_request_data_integer("id", 0);
		$name = UTIL::get_sanitized_request_string("name", "");
		$aqicon = UTIL::get_sanitized_request_string("icon", "");
		$sortids = UTIL::get_sanitized_request_string("ids", "");
		$query = UTIL::get_sanitized_request_string("query", "");
		$params = UTIL::get_sanitized_request_string("params", "");
		
		$changed = 0;
		if($aqid > 1) {
			$cdata = array();
			if($aqicon != "")
				$cdata['qicon'] = $aqicon;
			if($name != "")
				$cdata['name'] = $name;
			$cdata['query'] = $query;
			$cdata['params'] = $params;
			$incr = $db->updateTable($db->getAutoQueryTableName(), $cdata, "id=?", array($aqid));
			if($incr) 
				$changed += $incr;
			$responce['icon'] = $aqicon;
			$responce['name'] = $name;
			$responce['id'] = $aqid;
		}
		if($sortids != "") {
			$cids = explode(",", $sortids);
			$sortindex = 1;
			foreach($cids as $id) {
				if($id > 1) {
					if($db->updateTable($db->getAutoQueryTableName(), array('sortindex'=>$sortindex), "id=?", array(intval($id))))
						$changed++;
					$sortindex++;
				}
			}
		}
		
		$responce['status'] = 'ok';
		$responce['changed'] = $changed;
		return $responce;
	}
	
	###########################################################################
	
	private function testEmailConfig($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();

		$setup = new \CodePunch\Config\Settings($auth);
		$rootUrl = $setup->getOption("application_root_url", "");

		$toemail = UTIL::get_sanitized_request_string("email", "");

		if (filter_var($toemail, FILTER_VALIDATE_EMAIL)) {
			$recipients = array($toemail => '');
			$mailer = new \CodePunch\Base\Mailer($auth);

			$subject = "Test Email - Watch My Domains SED";

			$message =
				"Hello,\n\n" .
				"This is a test email sent from a Watch My Domains SED installation.\n\n" .
				"You received this message because this email address was entered as a test recipient during email configuration.\n\n" .
				"This confirms that the current outgoing email settings can send messages successfully.\n\n" .
				($rootUrl !== "" ? "Installation URL:\n" . $rootUrl . "\n\n" : "") .
				"Note: Alert and notification delivery depends on the recipient settings configured in the application.\n\n" .
				"Thank you,\n" .
				"The Watch My Domains Team";

			$altbody =
				"Test email from Watch My Domains SED" .
				($rootUrl !== "" ? " at " . $rootUrl : "") .
				". This confirms outgoing email settings can send messages. Alert recipients are configured separately.";

			$status = $mailer->send(
				array($recipients),
				$subject,
				$message,
				$altbody
			);

			if ($status !== false) {
				$responce['status'] = 'ok';
			}
		} else {
			$responce['error'] = 'Invalid email address';
		}

		return $responce;
	}

	###########################################################################
	
	private function getEmailConfig($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$required = array(
			'email_send_method', 'email_from_address', 'email_from_name',
			'email_reply_address', 'email_reply_name',
			'email_word_wrap',
			'email_smtp_server', 'email_smtp_user', 'email_smtp_secure', 'email_smtp_port',
			'email_recipients'
			);
			
		$edata = array();
		$setup = new \CodePunch\Config\Settings($auth);
		foreach($required as $r) {
			$value = $setup->getOption($r, '');
			$edata[$r] = $value;
		}
		$responce['data'] = $edata;
		$responce['status'] = 'ok';
		
		return $responce;
	}
	
	###########################################################################
	
	private function setEmailConfig($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$added = 0;
		$required = array(
			'email_send_method', 'email_from_address', 'email_from_name',
			'email_reply_address', 'email_reply_name',
			'email_word_wrap', 'email_recipients'
			);
		$method = UTIL::get_sanitized_request_string("email_send_method", "default");
		if($method == "smtp") {
			$required = array_merge($required, 
				array('email_smtp_server', 'email_smtp_user', 'email_smtp_secure', 'email_smtp_port','email_smtp_password'));	
		}
		$responce['method'] = $method;

		$setup = new \CodePunch\Config\Settings($auth);
		$edata = UTIL::get_unsafe_request_data_array();

		foreach($edata as $key=>$value) {
			if(in_array($key, $required)) {
				if($key != "email_smtp_password")
					$value = UTIL::sanitize_string($value);
				else if($value != "")
					$value = $auth->encrypt($value);
				if($value != "") {
					$setup->setOption($key, $value);
					$added++;
				}
			}
		}
		if($added) {
			$responce['status'] = 'ok';
		}
		
		return $responce;
	}
	
	###########################################################################
	
	private function doAuthenticationOps($responce)
	{
		$auth = $this->authentication;
		$oper = UTIL::get_sanitized_request_string("oper", "");
		if($oper == "encrypt") {
			$data = UTIL::get_sanitized_request_string("data", "");
			$responce['data'] = $auth->encrypt($data);
			$responce['status'] = 'ok';
		}
		else if($oper == "resetcli") {
			$key = $auth->getRemoteKey(true);
			$responce['data'] = $key;
			$responce['status'] = 'ok';
		}
		else if($oper == "get") {
			$setup = new \CodePunch\Config\Settings($auth);
			$secdata['max_login_attempts'] = $setup->getOption('max_login_attempts', \CodePunch\Config\ConfigRoot::DEFAULT_MAX_LOGIN_COUNT);
			$secdata['login_lockout_minutes'] = $setup->getOption('login_lockout_minutes', \CodePunch\Config\ConfigRoot::DEFAULT_LOGIN_LOCKOUT_MINUTES);
			$secdata['auth_timeout_minutes'] = $setup->getOption('auth_timeout_minutes', \CodePunch\Config\ConfigRoot::DEFAULT_SESSION_TIMEOUT);
			$responce['data'] = $secdata;
			$responce['status'] = 'ok';
		}
		else if($oper == "set") {
			$setup = new \CodePunch\Config\Settings($auth);
			$max_login_attempts = UTIL::get_request_data_integer("max_login_attempts", \CodePunch\Config\ConfigRoot::DEFAULT_MAX_LOGIN_COUNT);
			$login_lockout_minutes = UTIL::get_request_data_integer("login_lockout_minutes", \CodePunch\Config\ConfigRoot::DEFAULT_LOGIN_LOCKOUT_MINUTES);
			$auth_timeout_minutes = UTIL::get_request_data_integer("auth_timeout_minutes", \CodePunch\Config\ConfigRoot::DEFAULT_SESSION_TIMEOUT);
			$setup->setOption("max_login_attempts", $max_login_attempts);
			$setup->setOption("login_lockout_minutes", $login_lockout_minutes);
			$setup->setOption("auth_timeout_minutes", $auth_timeout_minutes);
			$secdata['max_login_attempts'] = $setup->getOption('max_login_attempts', \CodePunch\Config\ConfigRoot::DEFAULT_MAX_LOGIN_COUNT);
			$secdata['login_lockout_minutes'] = $setup->getOption('login_lockout_minutes', \CodePunch\Config\ConfigRoot::DEFAULT_LOGIN_LOCKOUT_MINUTES);
			$secdata['auth_timeout_minutes'] = $setup->getOption('auth_timeout_minutes', \CodePunch\Config\ConfigRoot::DEFAULT_SESSION_TIMEOUT);
			$responce['data'] = $secdata;
			$responce['status'] = 'ok';
		}
		else if($oper == "set2fa") {
			$setup = new \CodePunch\Config\Settings($auth);
			$authmode = UTIL::get_sanitized_request_string("mode", "");
			$cookiedays = UTIL::get_request_data_integer("cookie", 0);
			if($authmode != "") {
				$status = $setup->setOption("twofa_cookie_set_days", $cookiedays);
				$status = $setup->setOption("2fa_provider", $authmode);
				if($status === true || $status > 0)
					$responce['status'] = 'ok';
				else {
					if($setup->getOption("2fa_provider", "") == $authmode)
						$responce['status'] = 'ok';
				}
				$authmode = $setup->getOption("2fa_provider", "");
				if($authmode != "") {
					$keyset = 0;
					$class = "\\CodePunch\\Config\\Security\\" . $authmode;
					if(class_exists($class)) {
						$sc = new $class($auth);
						$keys = $sc->getKeys();
						foreach($keys as $key => $info) {
							$rs = strtolower($authmode) . "_key_" . strtolower(str_replace(" ", "_", $key));
							$keyval = UTIL::get_sanitized_request_string($rs, "");
							if($keyval != "" && str_replace("#", "", $keyval) != "") {
								$setup->setEncryptedOption($rs, $keyval);
								$keyset++;
							}
						}
					}
					if($keyset) 
						$responce['status'] = 'ok';
					$responce['keys'] = $keyset;
				}
			}
		}
		else if($oper == "get2fa") {
			$setup = new \CodePunch\Config\Settings($auth);
			$responce['cookie'] = $setup->getOption("twofa_cookie_set_days", 0);
			$authmode = UTIL::get_sanitized_request_string("mode", "");
			if($authmode == "") 
				$authmode = $setup->getOption("2fa_provider", "");
			if($authmode != "") {
				$responce['mode'] = $authmode;
				$responce['keyvals'] = array();
				$responce['keys'] = array();
				$responce['enabled'] = false;
				$class = "\\CodePunch\\Config\\Security\\" . $authmode;
				if(class_exists($class)) {
					$sc = new $class($auth);
					$responce['status'] = 'ok';
					$keys = $sc->getKeys();
					foreach($keys as $key => $info) {
						$rs = strtolower($authmode) . "_key_" . strtolower(str_replace(" ", "_", $key));
						$keyval = $setup->getEncryptedOption($rs, "");
						$responce['keyvals'][] = str_repeat('#', strlen($keyval));
					}
					$responce['keys'] = $keys;
					$responce['enabled'] = $sc->isEnabled();
					$responce['devcookie'] = $sc->allowDeviceCookie();
				}
				else if($authmode == "default") {
					$responce['status'] = 'ok';
				}
			}
		}
		else if($oper == "getsaml") {
			$setup = new \CodePunch\Config\Settings($auth);
			$samlidp = UTIL::get_sanitized_request_string("idp", "");
			if($samlidp == "") 
				$samlidp = $setup->getOption("saml_idp", "");
			if($samlidp != "") {
				$responce['saml_force'] = $setup->getBoolean("saml_force", false) ? 1 : 0;
				$responce['idp'] = $samlidp;
				$responce['keyvals'] = array();
				$responce['keys'] = array();
				$responce['enabled'] = false;
				$responce['saml_match'] = $setup->getOption("saml_match", "samluser");
				$responce['saml_allow_logout'] = $setup->getBoolean("saml_allow_logout", true) ? 1 : 0;
				$class = "\\CodePunch\\Config\\SAML\\" . $samlidp;
				if(class_exists($class)) {
					$sc = new $class($auth, false);
					$responce['status'] = 'ok';
					$keys = $sc->getOptions();
					foreach($keys as $key => $info) {
						if(count($info) >= 3) {
							$rs = strtolower($samlidp) . "_" . strtolower(str_replace(array(" ","-"), array("_","_"), $key));
							if($info[0] == "string" || $info[0] == "boolean")
								$keyval = $setup->getEncryptedOption($rs, "");
							else if($info[0] == "text") {
								$keyval = $setup->getText($rs, "");
								if($keyval != "")
									$keyval = $auth->decrypt($keyval);
							}
							$responce['keyvals'][$key] = $keyval; 
						}
					}
					$responce['keys'] = $keys;
					$responce['enabled'] = $sc->isEnabled();
				}
				else if($samlidp == "default") {
					$responce['status'] = 'ok';
				}
			}
		}
		else if($oper == "setsaml") {
			$responce['error'] = '';
			$setup = new \CodePunch\Config\Settings($auth);
			$samlidp = UTIL::get_sanitized_request_string("idp", "");
			$matchmode = UTIL::get_sanitized_request_string("saml_match", "samluser");
			if($samlidp == "" || $samlidp == "default") {
				$setup->setOption("saml_idp", "default");
				$responce['status'] = 'ok';
			}
			if($samlidp != "") {
				$class = "\\CodePunch\\Config\\SAML\\" . $samlidp;
				if(class_exists($class)) {
					$setup->setOption("saml_idp", $samlidp);
					$setup->setOption("saml_match", $matchmode);
					$sc = new $class($auth, false);
					$keys = $sc->getOptions();
					foreach($keys as $key => $info) {
						if(count($info) >= 3) {
							$sk = strtolower($samlidp) . "_" . strtolower(str_replace(array(" ","-"),  array("_","_"), $key));
							$keyval = UTIL::get_sanitized_request_string($sk, "");
							//if($keyval != "")  {
								if($info[0] == "string" || $info[0] == "boolean")
									$setup->setEncryptedOption($sk, $keyval);
								else if($info[0] == "text") 
									$setup->setText($sk, $auth->encrypt($keyval));
							//}
						}
					}
					$responce['status'] = 'ok';
					$responce['samluser'] = $this->createSAMLUser();
				}
				else
					$responce['error'] = "Not Found";
			}
			if($responce['status'] == 'ok') {
				$forcesaml = UTIL::get_request_data_boolean("saml_force", false);
				$saml_allow_logout = UTIL::get_request_data_boolean("saml_allow_logout", true);
				$setup->setOption("saml_force", $forcesaml ? "1" : "0");
				$setup->setOption("saml_allow_logout", $saml_allow_logout ? "1" : "0");
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getAuditLogList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$maxcount = UTIL::get_request_data_integer("count", 5000);
		if($maxcount <= 0 && $maxcount > 5000)
			$maxcount = 5000;
		$keyword = UTIL::get_sanitized_request_string("q", "");
		$data = $db->getAuditData($auth, $maxcount, $keyword);
		$responce['status'] = count($data) ? 'ok' : 'notok';
		$responce['data'] = $data;
		return $responce;
	}
	
	###########################################################################
	
	private function safeDeleteDomains($responce) {
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$sids = explode(",", UTIL::get_sanitized_request_string("id", ""));
		$cid = UTIL::get_request_data_integer("cid", 0);
		if($cid > 1 && count($sids)) {
			// Find the actual sids to delete
			$where = "sid IN (?) AND sid NOT IN(SELECT DISTINCT(did) from " . $db->getCategoryConnectionTableName() . " WHERE cid != ?)";
			$params = array();
			$params[] = array($sids, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
			$params[] = $cid;
			$ddata = $db->getFromTable('sid', $db->getDomainTableName(), $where, $params);
			if($ddata !== false && count($ddata)) {
				$dids = array();
				foreach($ddata as $d)
					$dids[] = $d['sid'];
				$tabledata = array(
					array('table'=>$db->getDomainTableName(), 'id'=>'sid', 'uname'=>'domain'),
					array('table'=>$db->getCategoryConnectionTableName(),'id'=>'did'),
					array('table'=>$db->getSubdomainTableName(),'id'=>'sid'),
					array('table'=>$db->getLookupQueueTableName(),'id'=>'sid'),
					array('table'=>$db->getDataHistoryTableName(),'id'=>'sid')
				);
				$responce = $this->deleteFromGrid($responce, implode(",", $dids), $tabledata, array(), AUDIT::DELETE_DOMAINS);
				$db->setCategoryCounts();
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function addDomainsToCategories($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$cids = explode(",", UTIL::get_sanitized_request_string("cid", ""));
		$sids = explode(",", UTIL::get_sanitized_request_string("id", ""));
		$responce['added'] = 0;
		if(count($sids)) {
			foreach($cids as $cid) {
				if($cid > 1) {
					$added = $db->addDomainIDsToCategoryByID($sids, $cid);
					$responce['added'] +=  $added;
				}
			}
		}
		if($responce['added']) {
			$db->setCategoryCounts();
			$responce['status'] = 'ok';
		}
		else
			$responce['error'] = 'nothing changed';
		return $responce;
	}
	
	###########################################################################
	
	private function deleteDomainsFromCategories($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$cids = explode(",", UTIL::get_sanitized_request_string("cid", ""));
		$sids = UTIL::get_sanitized_request_string("id", "");
		if(strtolower($sids) != "all")
			$sids = explode(",", $sids);
		$responce['deleted'] = 0;
		if(count($sids)) {
			foreach($cids as $cid) {
				if($cid > 1) {
					if(is_array($sids)) {
						if(count($sids)) {
							foreach($sids as $sid) {
								if($db->removeDomainIDFromCategoryByID($sid, $cid))
									$responce['deleted']++;
							}
						}
					}
					else if(strtolower($sids) == "all")
						$responce['deleted'] += $db->deleteFromTable($this->getCategoryConnectionTableName(), "cid=?", array($cid));
				}
			}
		}
		if($responce['deleted']) {
			$db->setCategoryCounts();
			$responce['status'] = 'ok';
		}
		else
			$responce['error'] = 'nothing changed';
		return $responce;
	}
	
	###########################################################################
	
	private function parseDomainName($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$domaindata = UTIL::get_sanitized_request_string("data", "");
		$count = 0;
		$responce['data'] = array();
		if($domaindata != "") {
			$rows = explode("\n", $domaindata);
			foreach($rows as $row) {
				$row = trim($row);
				$di  = UTIL::clean_domain_name($row, $db->getDomainSuffixList());
				$di['input'] = $row;
				$di['ascii'] = UTIL::idn_convert($di['domain']);
				$responce['data'][]  = $di;
				$count++;
			}
		}
		if($count)
			$responce['status'] = 'ok';
		return $responce;
	}

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

	private function getDomainSuffixTLDList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$dslist = $db->getFromTable("tld", $db->getDomainSuffixListTableName(), "", array(), "tld", "ASC");
		if($dslist !== false) {
			$suffixlist = array();
			$responce['status'] = 'ok';
			array_walk_recursive($dslist, function($a) use (&$suffixlist) { $suffixlist[] = $a; });
			$responce['data'] = array_values(array_unique($suffixlist));
		}
		return $responce;
	}

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

	private function getDomainSuffixList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$tld = UTIL::get_sanitized_request_string("tld", "");
		$dslist = $db->getDomainSuffixList();
		if($dslist != null) {
			if($tld != "")
				$dslist = $dslist[$tld];
			if(count($dslist))
				$responce['status'] = 'ok';
			$responce['data'] = $dslist;
		}
		return $responce;
	}

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

	private function addToDomainSuffixList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$suffixdata = UTIL::get_sanitized_request_string("data", "");
		$count = 0;
		if($suffixdata != "") {
			$rows = explode("\n", $suffixdata);
			foreach($rows as $row) {
				$row = trim($row);
				if(!$db->hasRow($db->getDomainSuffixListTableName(), "suffix", $row)) {
					$tld = $row;
					$parts = explode(".", $row);
					if(count($parts) > 1)
						$tld = $parts[count($parts)-1];
					$idata = array('suffix'=>$row, 'tld'=>$tld, 'manual_added'=>1);
					$count += $db->insertIntoTable($db->getDomainSuffixListTableName(), $idata) ? 1 : 0;
				}
			}
		}
		$responce['count'] = $count;
		if($count)
			$responce['status'] = 'ok';
		return $responce;
	}

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

	private function delFromDomainSuffixList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$suffixdata = UTIL::get_sanitized_request_string("data", "");
		$count = 0;
		if($suffixdata != "") {
			$rows = explode("\n", $suffixdata);
			foreach($rows as $row) {
				$row = trim($row);
				if($db->hasRow($db->getDomainSuffixListTableName(), "suffix", $row)) {
					$count += $db->deleteFromTable($db->getDomainSuffixListTableName(), "suffix=? AND manual_added = ?", array($row, 1));
				}
			}
		}
		if($count)
			$responce['status'] = 'ok';
		$responce['count'] = $count;
		return $responce;
	}
	
	###########################################################################

	private function getProxyServer($responce)
	{
		$auth = $this->authentication;
		$setup = new \CodePunch\Config\Settings($auth);
		$responce['proxy_host'] = $setup->getOption('ext_proxy_host', '');
		$responce['proxy_port'] = $setup->getOption('ext_proxy_port', '');
		$responce['proxy_user'] = $setup->getOption('ext_proxy_user', '');
		$responce['proxy_type'] = $setup->getOption('ext_proxy_user', 'socks5');
		$responce['proxy_http'] = $setup->getOption('proxy_http', '0');
		$responce['proxy_whois'] = $setup->getOption('proxy_whois', '1');
		$responce['proxy_rdap'] = $setup->getOption('proxy_rdap', '1');
		$responce['ignore_proxy_connection_intervals'] = $setup->getOption('ignore_proxy_connection_intervals', '1');
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################

	private function setProxyServer($responce)
	{
		$auth = $this->authentication;
		$setup = new \CodePunch\Config\Settings($auth);
		
		$proxy_rdap = UTIL::get_sanitized_request_string("proxy_rdap", "1");
		$proxy_whois = UTIL::get_sanitized_request_string("proxy_whois", "1");
		$proxy_http = UTIL::get_sanitized_request_string("proxy_http", "0");
		$setup->setOption('proxy_rdap', $proxy_rdap);
		$setup->setOption('proxy_whois', $proxy_whois);
		$setup->setOption('proxy_http', $proxy_http);
		
		$host = UTIL::get_sanitized_request_string("proxy_host", "");
		$port = UTIL::get_sanitized_request_string("proxy_port", "");
		$type = UTIL::get_sanitized_request_string("proxy_type", "socks5");
		$ignore_ci = UTIL::get_sanitized_request_string("ignore_proxy_connection_intervals", "1");
		
		$user = UTIL::get_sanitized_request_string("proxy_user", "");
		$password = UTIL::get_sanitized_request_string("proxy_password", "");
		if($password != "" && strlen($password)) {
			$setup->setEncryptedOption('ext_proxy_password', $password);
		}
		$setup->setOption('ext_proxy_user', $user);
		$setup->setOption('ext_proxy_host', $host);
		$setup->setOption('ext_proxy_port', $port);
		$setup->setOption('ext_proxy_type', $type);
		$setup->setOption('ignore_proxy_connection_intervals', $ignore_ci);
		$responce['status'] = 'ok';
		return $responce;
	}

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

	private function getRDAPserver($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$tld = UTIL::get_sanitized_request_string("tld", "");
		if($tld != "") {
			$responce['set'] = false;
			$rdapserver = $db->getRDAPServerForTLD($tld);
			if($rdapserver === false || $rdapserver == "") {
				$lum = new \CodePunch\LU\LookupManager($auth);
				$info = $lum->findRDAPServer("", $tld);
				$rdapserver = $info['server'];
				if($rdapserver != "" && $rdapserver !== false) {
					$responce['set'] = true;
					$responce['error'] = "RDAP server '$rdapserver' detected for '$tld'";
				}
			}
		}
		$rows = $db->getFromTable("server,rdap_server,lu_sequence", $db->getTLDsTableName(), "tld=?", array($tld));
		if($rows != false && isset($rows[0]))
			$responce = array_merge($responce, $db->fetchRow($rows, 0));
		if($rdapserver == "")
			$responce['error'] = "Unable to find RDAP server for '$tld'";
		else
			$responce['status'] = 'ok';
		return $responce;
	}

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

	private function setRDAPserver($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$tld = UTIL::get_sanitized_request_string("tld", "");
		$lus = UTIL::get_request_data_integer("lus", -1);
		$wserver = UTIL::get_sanitized_request_string("server", "");
		$responce['count'] = 0;
		if($tld != "") {
			if($tld == "*" && $wserver == "" && $lus >= 0 && $lus < 3) {
				$responce['count'] = $db->updateTable($db->getTLDsTableName(), array('lu_sequence'=>$lus), "1=1", array());
			}
			else {
				if($wserver != "" && $wserver != "unknown") {
					if($db->setRDAPServerForTLD($tld, $wserver))
						$responce['count']++;
				}
				if($lus >= 0 && $lus < 3) {
					if($db->updateTable($db->getTLDsTableName(), array('lu_sequence'=>$lus), "tld=?", array($tld)))
						$responce['count']++;
				}
				$rows = $db->getFromTable("server,rdap_server,lu_sequence", $db->getTLDsTableName(), "tld=?", array($tld));
				if($rows != false && isset($rows[0]))
					$responce = array_merge($responce, $db->fetchRow($rows, 0));
			}
		}
		if($responce['count'])
			$responce['status'] = 'ok';
		return $responce;
	}

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

	private function setRDAP_LUS($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$lus = UTIL::get_request_data_integer("lus", -1);
		$wserver = UTIL::get_sanitized_request_string("server", "");
		$responce['count'] = 0;
		if($wserver == "" && $lus >= 0 && $lus < 3) {
			$responce['count'] = $db->updateTable($db->getTLDsTableName(), array('lu_sequence'=>$lus), "1=1", array());
		}
		else if($lus >= 0 && $lus < 3) {
			$responce['count'] = $db->updateTable($db->getTLDsTableName(), array('lu_sequence'=>$lus), "rdap_server=?", array($wserver));
		}
		if($responce['count'])
			$responce['status'] = 'ok';
		return $responce;
	}

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

	private function getWhoisserver($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();	
		$tld = UTIL::get_sanitized_request_string("tld", "");
		$server = UTIL::get_sanitized_request_string("server", "");
		if($tld != "") {
			$server = "";
			$responce['set'] = false;
			$server = $db->getWhoisServerForTLD($tld);
			if($server == "") {
				$lum = new \CodePunch\LU\LookupManager($auth);
				$info = $lum->findWhoisServer("", $tld, 2);
				$server = $info['server'];
				if($server != "" && $server !== false) {
					$responce['set'] = true;
					$responce['error'] = "Whois server '$server' detected for '$tld'";
				}
			}
		}
		if($server != "") {
			$rows = $db->getFromTable("*", $db->getWhoisServersTableName(), "server=?", array($server));
			if($rows != false && isset($rows[0]))
				$responce = array_merge($responce, $db->fetchRow($rows, 0));
			else {
				$responce['server'] = $server;
				$responce['port'] = 43;
				$responce['query'] = '';
				$responce['searchtoken'] = '';
				$responce['bannedtext'] = '';
				$responce['cleantoken_1'] = '';
				$responce['cleantoken_2'] = '';
				$responce['proxy'] = '';
				$responce['dateformat'] = 'auto';
				$responce['xlate'] = '';
				$responce['maxconnections'] = '';
				$responce['conninterval'] = '';
			}
			
			$table = $db->getConnectionsTableName();
			$client = $auth->getServerID();
			$rows = $db->getFromTable("*", $table, "client=? AND server=?", array($client, $server));
			if($rows !== false && is_array($rows) && isset($rows[0])) {
				$row = array_change_key_case($rows[0], CASE_LOWER);
				$responce['last_connected_at'] = $row['last_connected_at'];
				$responce['stop_lookups'] = $row['stop_lookups'];;
			}
			else {
				$responce['stop_lookups'] = false;
				$responce['last_connected_at'] = '';
			}
			//$responce = UTIL::array_bool_to_int($responce);
			$responce['status'] = 'ok';
		}
		else
			$responce['error'] = "Unable to find whois server for '$tld'";
		
		return $responce;
	}
	
	###########################################################################
	
	private function setWhoisserver($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();	
		$tld = UTIL::get_sanitized_request_string("tld", "");
		$wserver = UTIL::get_sanitized_request_string("server", "");
		$responce['count'] = 0;
		if($tld != "") {
			if($wserver != "" && $wserver != "unknown") {
				if($db->setWhoisServerForTLD($tld, $wserver)) 
					$responce['status'] = 'ok';
			}
		}
		if($wserver != "") {
			$validkeys = array('port', 'query', 'searchtoken', 'bannedtext',
						'cleantoken_1', 'cleantoken_2', 'proxy', 'dateformat',
						'xlate', 'maxconnections', 'conninterval');
			$idata = UTIL::get_sanitized_request_string_array($validkeys);
			// Make sure that connection interval doesn't get set to 0 by mistake
			if(isset($idata['conninterval'])) {
				if($idata['conninterval'] == "")
					$idata['conninterval'] = "10";
			}
			$count = 0;
			if(count($idata)) {
				if($db->hasRow($db->getWhoisServersTableName(), "server", $wserver)) {
					$count = $db->updateTable($db->getWhoisServersTableName(), $idata, "server=?", array($wserver));
				}
				else {
					$idata['server'] = $wserver;
					$count = $db->insertIntoTable($db->getWhoisServersTableName(), $idata) ? 1 : 0;
				}
				if($count) 
					$responce['status'] = 'ok';
			}
			
			$data = UTIL::get_sanitized_request_string_array(array('last_connected_at', 'stop_lookups'));
			$cdata = array();
			foreach($data as $key=>$val) {
				if(strtolower($val) == "null")
					$val = null;
				$cdata[$key] = $val;
			}
			$ccount = 0;
			if(count($cdata)) {
				$client = $auth->getServerID();
				if($db->hasRow($db->getConnectionsTableName(), array("server",'client'), array($wserver,$client)))
					$ccount = $db->updateTable($db->getConnectionsTableName(), $cdata, "client=? AND server=?", array($client,$wserver));
				else {
					$cdata['server'] = $wserver;
					$cdata['client'] = $client;
					$ccount = $db->insertIntoTable($db->getConnectionsTableName(), $cdata) ? 1 : 0;
				}
				if($ccount) 
					$responce['status'] = 'ok';
				else if($responce['status'] == 'ok') {
					//$responce['status'] = 'notok';
					//$responce['error'] = 'Connection Table Update Failed';
				}
			}
			$responce['count'] = $ccount + $count;
			if(!count($idata) && !count($cdata) && $responce['status'] != 'ok')
				$responce['error'] = 'No data to set or nothing has changed.';
		}
		else
			$responce['error'] = 'Missing server';
		
		return $responce;
	}
	
	###########################################################################
	
	private function getLookupScheduleList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$lutable = $db->getLookupSchedulerTableName();
		$rows = $db->getFromTable("*", $lutable);
		if($rows !== false) {
			$responce['status'] = 'ok';
			$responce['schedules'] = array();
			$index = 0;
			foreach($rows as $r) {
				$row = $db->fetchRow($rows, $index++);
				// Convert bools to 1 or 0 (fix PostgreSQL Boolean handling)
				$row = UTIL::array_bool_to_int($row);
				$responce['schedules'][] = $row;
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function getLookupSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$lutable = $db->getLookupSchedulerTableName();
		$name = UTIL::get_sanitized_request_string("name", "");
		$schid = UTIL::get_request_data_integer("id", 0);
		if($schid == 0 && $name != "")
			$schid = $db->findOneOf($lutable, "name", $name, "id");
		if($schid > 0 && $schid !== false) {
			$rows = $db->getFromTable("*", $lutable, "id=?", $schid);
			if($rows !== false && isset($rows[0])) {
				$responce['status'] = 'ok';
				$row = $db->fetchRow($rows, 0);
				// Convert bools to 1 or 0 (fix PostgreSQL Boolean handling)
				$row = UTIL::array_bool_to_int($row);
				$responce = array_merge($responce, $row);
			}
		}
		else {
			$rows = $db->getFromTable("*", $lutable);
			if($rows !== false) {
				$responce['status'] = 'ok';
				$responce['schedules'] = array();
				$index = 0;
				foreach($rows as $r) {
					$row = $db->fetchRow($rows, $index++);
					// Convert bools to 1 or 0 (fix PostgreSQL Boolean handling)
					$row = UTIL::array_bool_to_int($row);
					$responce['schedules'][] = $row;
				}
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function resetLookupSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$lutable = $db->getLookupSchedulerTableName();
		if(!$db->truncateTable($lutable))
			$responce['truncate'] = 0;
		else
			$responce['truncate'] = 1;
		$tabledatafolder = UTIL::get_install_folder_path() . "lib/php/CodePunch/DB/Init/";
		$table = $db->getTableNameWithoutPrefix($lutable);
		$updated = $db->updateInitDataFromFile($tabledatafolder, array($table));
		if($updated)
			$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function deleteLookupSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$lutable = $db->getLookupSchedulerTableName();
		$name = UTIL::get_sanitized_request_string("name", "");
		$schid = UTIL::get_request_data_integer("id", 0);
		if($schid == 0 && $name != "")
			$schid = $db->findOneOf($lutable, "name", $name, "id");
		if($schid !== false && $schid > 0) {
			$delete01 = $db->deleteFromTable($lutable, "id=?", array($schid));
			if($delete01) 
				$responce['status'] = 'ok';
		}
		else
			$responce['error'] = 'Not found in scheduler list';
		
		return $responce;
	}
	
	###########################################################################
	
	private function addLookupSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$lutable = $db->getLookupSchedulerTableName();
		$name = UTIL::get_sanitized_request_string("name", "");
		if($name != "") {
			$lusid = $db->findOneOf($lutable, "name", $name, "id");
			if($lusid !== false && $lusid > 0) {
				$responce['error'] = "A schedule with same name already exists.";
			}
			else {
				if($db->insertIntoTable($lutable, array('name'=>$name))) {
					$responce['status'] = 'ok';
					return $this->updateLookupSchedule($responce);
				}
			}
		}
		else
			$responce['error'] = 'Empty name or name not specified';
		
		return $responce;
	}
	
	###########################################################################
	
	private function updateLookupSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$lutable = $db->getLookupSchedulerTableName();		
		$validkeys = array('lutype', 'frequency', 'lunits', 'enabled', 'query', 'params');
		$data = UTIL::get_sanitized_request_string_array($validkeys);
		$name = UTIL::get_sanitized_request_string("name", "");

		// May 18 2020
		if(isset($data['frequency']) && isset($data['lunits'])) {
			if($data['lunits'] == \CodePunch\LU\LookupManager::LU_MINUTE) {
				$frequency = intval($data['frequency']);
				if($frequency < 15)
					$data['frequency'] = 15;
			}
		}
		//

		$responce['updated'] = false;
		$responce['categories'] = 0;
		if($name != "" && count(array_keys($data))) {
			$count = $db->updateTable($lutable, $data, "name=?", array($name));
			if($count && $count !== false) {
				$responce['updated'] = true;
				$responce['status'] = 'ok';
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function deleteReportSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$table = $db->getReportSchedulerTableName();
		$name = UTIL::get_sanitized_request_string("name", "");
		$repid = UTIL::get_request_data_integer("id", 0);
		if($repid == 0 && $name != "")
			$repid = $db->findOneOf($table, "name", $name, "id");
		if($repid !== false && $repid > 0) {
			$delete01 = $db->deleteFromTable($table, "id=?", array($repid));
			if($delete01) 
				$responce['status'] = 'ok';
		}
		else
			$responce['error'] = 'Not found in report list';
		
		return $responce;
	}
	
	###########################################################################
	
	private function addReportSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$table = $db->getReportSchedulerTableName();
		$name = UTIL::get_sanitized_request_string("name", "");
		if($db->hasRow($table, "name", $name)) 
			$responce['error'] = 'Report with same name already exists';
		else {
			$validkeys = array('frequency', 'runits', 'last_runat', 'autorun', 'query', 'params', 'sortindex', 'sortcolumn', 'desc', 'emails', 'name', 'columns');
			$data = UTIL::get_sanitized_request_string_array($validkeys);
			if(!isset($data['sortindex']))
				$data['sortindex'] = 21;
			if($db->insertIntoTable($table, $data))
				$responce['status'] = 'ok';
		}
		
		return $responce;
	}
	
	###########################################################################
	# If name or id is specified, will return the matching entry
	# Otherwise all the entries are returned.
	
	private function getReportSchedules($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$table = $db->getReportSchedulerTableName();
		$name = UTIL::get_sanitized_request_string("_repname", "");
		$repid = UTIL::get_request_data_integer("id", 0);
		if($repid == 0 && $name != "")
			$repid = $db->findOneOf($table, "name", $name, "id");
		if($repid > 0 && $repid !== false) {
			$rows = $db->getFromTable("*", $table, "id=?", $repid, "sortindex");
			if($rows !== false && isset($rows[0])) {
				$responce['status'] = 'ok';
				$row = $db->fetchRow($rows, 0);
				if(isset($row['sortcolumn']))
					$row['sortcolumn'] = $db->getColumnNameWithTablePrefix($row['sortcolumn']);
				$responce = array_merge($responce, $row);
				
				// Get Grid Query and Params
				if($name != "") {
					$tablealias = "";
					$params = array();
					$grid = new \CodePunch\UI\DataGrid($auth);
					$where = $this->getWhereForDomains($grid, $tablealias, $params, false);
					$query =  implode(" AND ", $where);
					$cid = UTIL::get_request_data_integer("cid", 0);
					$categoryQuery = $db->getCategoryQueryForCurrentUser($cid, null); // Allow for all cids (pass $auth=null) because this is just the query string
					if($categoryQuery != "")
						$query = "($query) AND ($categoryQuery)";
					$responce['gquery'] = trim($query);
					$responce['gparams'] = trim(implode(",", $params));
				}
			}
		}
		else {
			$rows = $db->getFromTable("*", $table, "", array(), "sortindex");
			if($rows !== false) {
				$responce['status'] = 'ok';
				$responce['schedules'] = array();
				$index = 0;
				foreach($rows as $r) {
					$row = $db->fetchRow($rows, $index++);
					if(isset($row['sortcolumn']))
						$row['sortcolumn'] = $db->getColumnNameWithTablePrefix($row['sortcolumn']);
					$responce['schedules'][] = $row;
				}
			}
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setReportSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$table = $db->getReportSchedulerTableName();
		
		$validkeys = array('frequency', 'runits', 'last_runat', 'autorun', 'query', 'params', 'sortindex', 'sortcolumn', 'descending', 'emails', 'columns');
		$data = UTIL::get_sanitized_request_string_array($validkeys);
		// Reset last_runat
		if(!isset($data['last_runat']))
			$data['last_runat'] = NULL;
		$name = UTIL::get_sanitized_request_string("name", "");
		$responce['updated'] = false;
		if($name != "" && count(array_keys($data))) {
			$count = $db->updateTable($table, $data, "name=?", array($name));
			if($count && $count !== false) {
				$responce['updated'] = true;
				$responce['status'] = 'ok';
			}
		}
		
		return $responce;
	}
	
	###########################################################################
	
	private function resetReportSchedule($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$lutable = $db->getReportSchedulerTableName();
		//if(!$db->truncateTable($lutable))
		//	$responce['truncate'] = 0;
		//else
		//	$responce['truncate'] = 1;
		$tabledatafolder = UTIL::get_install_folder_path() . "lib/php/CodePunch/DB/Init/";
		$table = $db->getTableNameWithoutPrefix($lutable);
		$updated = $db->updateInitDataFromFile($tabledatafolder, array($table), "name");
		if($updated)
			$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function sortReportSchedules($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$sortids = UTIL::get_sanitized_request_string("ids", "");
		$names = UTIL::get_sanitized_request_string("names", "");
		$table = $db->getReportSchedulerTableName();

		$changed = 0;
		if($sortids == "" && $names != "") {
			$names = explode(",", $names);
			$sortindex = 1;
			foreach($names as $name) {
				if($name != "") {
					if($db->updateTable($table, array('sortindex'=>$sortindex), "name=?", array($name)))
						$changed++;
					$sortindex++;
				}
			}
		}
		else if($sortids != "") {
			$rids = explode(",", $sortids);
			$sortindex = 1;
			foreach($rids as $id) {
				if($id > 1) {
					if($db->updateTable($table, array('sortindex'=>$sortindex), "id=?", array(intval($id))))
						$changed++;
					$sortindex++;
				}
			}
		}
		if($changed) {
			$responce['status'] = 'ok';
			$responce['changed'] = $changed;
		}

		$names = array();
		$rows = $db->getFromTable("name", $table, "", array(), "sortindex");
		if($rows !== false) {
			$index = 0;
			foreach($rows as $row) {
				$row = $db->fetchRow($rows, $index++);
				$names[] = $row['name'];
			}
			$responce['reports'] = $names;
		}

		return $responce;
	}

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

	private function getDNSAlertSettings($responce)
	{
		/*
		if(!class_exists('Net_DNS2_Resolver')) {
			$ipath = stream_resolve_include_path('Net/DNS2.php');
			if($ipath !== false && file_exists($ipath))
				require_once('Net/DNS2.php');
		}
		*/
				
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$responce['dns_monitor_enabled'] = $setup->getBoolean("dns_monitor_enabled", false);
		$responce['email_dns_alerts'] = $setup->getBoolean("email_dns_alerts", false);
		$responce['dnsbl_checks_enabled'] = $setup->getBoolean("dnsbl_checks_enabled", false);
		$responce['dns_alert_recipients'] = $setup->getOption("dns_alert_recipients", "");
		$responce['global_nameserver_ip'] = $setup->getOption("global_nameserver_ip", "");
		
		// Indicate if NetDNS2 is available.
		// Pear version has to be system installed.
		// https://github.com/mikepultz/netdns2 <- Default / Shipped with SED v6.1+
		// Shipped one has preference, but could be removed for other reasons on
		// specific installations. It is in the lib/php/NetDNS2 folder
		// These classes are automatically loaded by lib/php/loader.php
		$responce['default_net_dns2'] = class_exists('\NetDNS2\Resolver') ? "1" : "0";
		$responce['pear_net_dns2'] = class_exists('Net_DNS2_Resolver') ? "1" : "0";
		// First check for the shipped/default one
		// $responce['php_net_dns2'] <- Any NetDNS2 status
		$responce['php_net_dns2'] = $responce['default_net_dns2'];
		if($responce['php_net_dns2'] == "0") {
			$responce['php_net_dns2'] = $responce['pear_net_dns2'];
		}

		$responce['email_ping_alerts'] = $setup->getOption("email_ping_alerts", 0);
		$responce['email_http_alerts'] = $setup->getOption("email_http_alerts", 0);

		// Added April 2020
		$responce['dns_monitor_records'] = $setup->getOption("dns_monitor_records", "");
		$responce['email_alerts_delay'] = $setup->getOption("email_alerts_delay", "5");
		//

		// June 13 2021
		//$responce['alert_new_dns_entries'] = $setup->getBoolean("alert_new_dns_entries", false);

		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	# Enable/Disable alerts for all or a set of domains identified by the 
	# ids (sid)
	
	private function setDNSAlertStatus($responce)
	{
		$responce['command'] = "dnsalert";
		$updated = 0;
		$domains = "";
		$db = $this->authentication->getDatabase();
		$dns_alert_enabled = UTIL::get_request_data_boolean("disable", false);
		$alert_value = $dns_alert_enabled ? 1 : 0;
		$sids = UTIL::get_sanitized_request_string('id', "");
		$responce['sids'] = $sids;
		if(strtolower($sids) != "all") {
			$domains = [];
			$sids = explode(",", $sids);
			if(count($sids)) {
				UTIL::debug_print($sids);
				UTIL::debug_print($alert_value);
				foreach($sids as $sid) {
					if($db->updateTable($db->getDomainTableName(), array('disable_alerts'=>$alert_value), "sid=?", array($sid))) {
						$domain = $db->findOneOf($db->getDomainTableName(), "sid", $sid, "domain");
						$domains[] = $domain;
						$updated++;
					}
				}
			}
			else
				$responce['error'] = "invalid or missing domain ids";
		}
		else {
			// Enable disable alerts for all domains.
			if($db->updateTable($db->getDomainTableName(), array('disable_alerts'=>$alert_value), "1=1", array())) {
				$updated = $db->getRowCount($db->getDomainTableName());
				$domains = "all";
			}
		}
		if($updated) {
			$responce['status'] = 'ok';
			$responce['updated'] = $updated;
			
			// Update AUDIT
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setDNSAlertSettings($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$dns_monitor_enabled = UTIL::get_request_data_boolean("dns_monitor_enabled", false);
		$email_dns_alerts = UTIL::get_request_data_boolean("email_dns_alerts", false);
		$dnsbl_checks_enabled = UTIL::get_request_data_boolean("dnsbl_checks_enabled", false);
		$dns_alert_recipients = UTIL::get_sanitized_request_string("dns_alert_recipients", "");
		$global_nameserver_ip = UTIL::get_sanitized_request_string("global_nameserver_ip", "");
		$setup->setOption("dns_monitor_enabled", $dns_monitor_enabled);
		$setup->setOption("email_dns_alerts", $email_dns_alerts);
		$setup->setOption("dns_alert_recipients", $dns_alert_recipients);
		$setup->setOption("global_nameserver_ip", $global_nameserver_ip);
		$setup->setOption("dnsbl_checks_enabled", $dnsbl_checks_enabled);

		// Added April 2020
		$dns_monitor_records = UTIL::get_sanitized_request_string("dns_monitor_records", "");
		$email_alerts_delay = UTIL::get_sanitized_request_string("email_alerts_delay", "5");
		$setup->setOption("dns_monitor_records", $dns_monitor_records);
		$setup->setOption("email_alerts_delay", $email_alerts_delay);
		//

		// Added May 23 2021
		$email_ping_alerts = UTIL::get_request_data_integer("email_ping_alerts", 0, 0, 2);
		$setup->setOption("email_ping_alerts", $email_ping_alerts);
		$email_http_alerts = UTIL::get_request_data_integer("email_http_alerts", 0, 0, 2);
		$setup->setOption("email_http_alerts", $email_http_alerts);

		// June 13 2021
		//$alert_new_dns_entries = UTIL::get_request_data_boolean("alert_new_dns_entries", false);
		//$setup->setOption("alert_new_dns_entries", $alert_new_dns_entries);

		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function getOptions($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$availoptions = $setup->getUserOptions();
		$optioncount = 0;
		$getall = false;
		if(UTIL::is_request_key_set("all"))
			$getall = true;
		$optiondata = array();
		foreach($availoptions as $key=>$defval) {
			if(UTIL::is_request_key_set($key)) {
				$responce[$key] = $setup->getOption($key, $defval);
				$optioncount++;
			}
			else if($getall) 
				$optiondata[$key] = $setup->getOption($key, $defval);
		}
		if($getall)
			$responce['data'] = $optiondata;
		if($optioncount || $getall)
			$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function setOptions($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$availoptions = $setup->getUserOptions();
		$optioncount = 0;
		foreach($availoptions as $key=>$defval) {
			if(UTIL::is_request_key_set($key)) {
				$value = UTIL::get_sanitized_request_string($key, "");
				if($value == "")
					$value = $defval;
				$setup->setOption($key, $value);
				$responce[$key] = $value;
				$optioncount++;
			}
		}
		if($optioncount)
			$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function getConfig($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$cname = UTIL::get_sanitized_request_string("configname", "");
		if (
			$cname &&
			strlen($cname) > 7 &&
			strpos($cname, "config_") === 0 &&
			preg_match('/^config_[A-Za-z0-9_]+$/', $cname)
		) {
			$cdata = $setup->getText($cname, "");
			$responce['data'] = $cdata;
			$responce['status'] = 'ok';
		}
		else {
			$responce['error'] = 'Missing or invalid configname';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function setConfig($responce)
	{
		$setup  = new \CodePunch\Config\Settings($this->authentication);
		$cname  = UTIL::get_sanitized_request_string("configname", "");
		$cvalue = UTIL::get_sanitized_request_string("configdata", "");

		if (
			$cname &&
			strlen($cname) > 7 &&
			strpos($cname, "config_") === 0 &&
			preg_match('/^config_[A-Za-z0-9_]+$/', $cname)
		) {
			$setup->setText($cname, $cvalue);
			$responce['status'] = 'ok';
			// no error key on success
		} else {
			$responce['status'] = 'notok';             // legacy requirement
			$responce['error']  = 'invalid config key'; // explicit error message
		}

		return $responce;
	}

	
	###########################################################################
	
	private function getBranding($responce)
	{
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$defbranding = $setup->getDefaultBranding();
		$branding = array();
		foreach($defbranding as $key=>$defval) {
			$branding[$key] = $setup->getOption($key, $defval);
		}
		$responce['data'] = $branding;
		$responce = $this->getBackgroundImageList($responce);
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function setBranding($responce)
	{
		$changed = 0;
		$setup = new \CodePunch\Config\Settings($this->authentication);
		$defbranding = $setup->getDefaultBranding();
		foreach($defbranding as $key=>$defval) {
			$value = UTIL::get_sanitized_request_string($key, "");
			$setup->setOption($key, $value);
			$defbranding[$key] = $value;
		}
		$responce['data'] = $defbranding;
		$responce['status'] = 'ok';
		return $responce;
	}
	
	###########################################################################
	
	private function getErrorLogFileList($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		
		$logDirectory = UTIL::get_log_folder_path();
		$responce['data'] = UTIL::find_all_matched_files($logDirectory, "*.txt");
		if(count($responce['data'])) {
			$responce['status'] = 'ok';
			$responce['folder'] = $logDirectory;
		}
		
		return $responce;
	}
	
	###########################################################################
	
	private function getErrorLogFile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$file = basename(UTIL::get_sanitized_request_string("name", ""));
		$folder = UTIL::get_log_folder_path();
		$logfile = $folder . $file;
		if(is_file($logfile)) {
			$responce['data'] = UTIL::file_get_contents_utf8($logfile); //utf8_encode(file_get_contents($logfile)); TODO: Look into this more
			$responce['status'] = 'ok';
		}
		return $responce;
	}
	
	###########################################################################
	
	private function deleteErrorLogFile($responce)
	{
		$auth = $this->authentication;
		$db = $auth->getDatabase();
		$file = basename(UTIL::get_sanitized_request_string("name", ""));
		$folder = UTIL::get_log_folder_path();
		if(strtolower($file) == "all") {
			$files = UTIL::find_all_matched_files($folder, "*.txt");
		}
		else
			$files = array($file);
		
		$deleted = 0;
		foreach($files as $file) {
			$logfile = $folder . $file;
			if(is_file($logfile)) {
				if(unlink($logfile)) {
					$deleted++;
				}
			}
		}
		
		if($deleted) {
			$responce['status'] = 'ok';
			$responce['data'] = UTIL::find_all_matched_files($folder, "*.txt");
			$responce['folder'] = $folder;
		}
		return $responce;
	}
	
	###########################################################################
	
	private function repairTables($responce)
	{
		$updated = \CodePunch\DB\DomainDB::repairTables();
		if($updated)
			$responce['status'] = 'ok';
		return $responce;
	}

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

	private function addBackgroundTask($responce)
	{
		// Add BG Task
		$auth = $this->authentication;
		$taskmethod = UTIL::get_sanitized_request_string("name", "");
		$tasklist = get_class_methods('\CodePunch\Config\Tasks');
		if($taskmethod != "" && in_array($taskmethod, $tasklist)) {
			$job = "task;{$taskmethod}";
			$setup = new \CodePunch\Config\Settings($auth);
			$status = $setup->addPendingJob($job);
			$responce['status'] = $status ? 'ok' : 'notok';
		}
		else
			$responce['error'] = "No task name specified";
		return $responce;
	}

	###########################################################################
	
	public static function initAuth(&$error)
	{
		$auth = null;
		try {
			$ioncubestatus = \CodePunch\Config\ConfigRoot::check_ioncube_status();
			if($ioncubestatus >= 0)
				$auth = new \CodePunch\Config\Auth();
		}
		catch(Exception $e) {
			$logger = new \CodePunch\Base\CPLogger();
			$logger->error($e->getMessage());
			$error = $e->getMessage();
		}
		return $auth;
	}
	
	###########################################################################
	
	public static function api()
	{
		$cmd = UTIL::get_sanitized_request_string("c", "");
		$authstatus = UTIL::get_sanitized_request_string("auth", "");
		$error = "";
		$auth = ($authstatus != "none") ? self::initAuth($error) : null;
		if(($cmd == "css" || $cmd == "js")) {
			$layout = new \CodePunch\UI\Layout($auth);
			$name = basename(trim(UTIL::get_sanitized_request_string("name", ""), " \r\n\t,"));
			$file = basename(trim(UTIL::get_sanitized_request_string("file", ""), " \r\n\t,"));
			$docompress = true;
			$createminified = false;
			if(substr($file,0,1) == "@") {
				$docompress = false;
				$file = substr($file, 1);
			}
			$location = trim(UTIL::get_sanitized_request_string("loc", ""), " \r\n\t,");
			$folder = UTIL::get_install_folder_path();
			if($cmd == "js") {
				$files = explode("|", $file);
				$jsdata = "";
				if($name != "") {
					$finalfile = $folder . "lib/layouts/" . ($location != "" ? "$location/" : "") . "js/$name";
					if(is_file($finalfile)) {
						// Find the timestamp
						$ts01 = filemtime($finalfile);
					}
					else
						$createminified = true;
				}
				foreach($files as $f) {
					$templatefile = true;
					if($location == "") {
						$thefile = $folder . "lib/layouts/js/src/$f";
						$templatefile = false;
					}
					else {
						$thefile = $folder . "lib/layouts/$location/js/templates/$f";
						if(!is_file($thefile)) {
							$thefile = $folder . "lib/layouts/$location/js/src/$f";
							$templatefile = false;
						}
					}
					if(!$createminified && isset($ts01) && is_file($thefile)) {
						$ts = filemtime($thefile);
						if($ts > $ts01)
							$createminified = true;
					}
					$jsdata .= $layout->getJSFile($thefile, $templatefile) . "\n";
				}
				if(!$createminified && isset($ts01)) {
					header("Content-type: application/javascript");
					$jsdata = $layout->getJSFile($finalfile, false) . "\n";
				}
				else {
					require_once $folder . 'lib/php/JShrink/Minifier.php';
					header("Content-type: application/javascript");
					$jsdata = $docompress ? \JShrink\Minifier::minify($jsdata) : $jsdata;
					if($createminified && count($files)) {
						$header = "/*!\n * Watch My Domains SED v6\n * https://domainpunch.com/sed/\n * (c) 2010-2025 Softnik Technologies\n */\n";
						file_put_contents($finalfile, $header.$jsdata);
					}
				}
				echo $jsdata;
			}
			else {
				$files = explode("|", $file);
				$cssdata = "";
				foreach($files as $f) {
					$thefile = $folder . "lib/layouts/$location/css/src/$f";
					if(is_file($thefile))
						$cssdata .= trim(file_get_contents($thefile)) . "\n/* $thefile */\n\n";
				}
				$buffer = $layout->getCSSData($cssdata);
				UTIL::outputCSSFile($buffer, $docompress);
			}
			exit;
		}
		if($cmd == "auth" && $auth) {
			if(!UTIL::is_request_key_set('logout')) {
				$user = trim(UTIL::get_sanitized_request_string("user", ""), " \r\n\t,");
				$pass = trim(UTIL::get_sanitized_request_string("pass", ""), " \r\n\t,");
				$status = $auth->Authenticate($user, $pass);
			}
			else {
				$status = array('status' => 'ok');
				$status['validate'] = $auth->validateSession(false, false);
			}
			echo json_encode($status);
		}
		else if($auth) {
			if(UTIL::is_in_debug_mode())
				header('Content-Type: application/json');
			$processor = new \CodePunch\UI\API($auth);
			if($cmd == "report") {
				$responce = $processor->init();
				$name = UTIL::get_sanitized_request_string("name", "");
				$report = $processor->getReportHTML($name);
				echo $report;
				exit;
			}
			else if($cmd == "grid") 
				echo($processor->grid());
			else if($cmd == "add") 
				echo($processor->add());
			else if($cmd == "del") 
				echo($processor->delete());
			else if($cmd == "set") 
				echo($processor->set());
			else if($cmd == "list") 
				echo($processor->list());
			else if($cmd == "admin") 
				echo($processor->admin());
			else if($cmd == "download") 
				echo($processor->download());
			else
				echo($processor->get());
		}
		else if(!$auth)	{
			if($cmd == "auth") {
				$responce = array('status'=>'notok', 'user'=>'', 'group'=>'', 'error'=>$error);
				echo json_encode($responce);
			}
			else
				echo $error;
		}				
	}
	
	###############################################################################
}
