<?php

require_once("lib-tk/include/config/internal/states.php");
require_once("lib-tk/include/config/internal/countries.php");
require_once("lib-mbapi/include/modules/module.php");
require_once("lib-tk/include/xml/xml.php");
require_once("lib-tk/include/crypt/crypt.php");
require_once("lib-tk/functions.php");
require_once("lib-billing/include/config/registrar.php");
require_once("lib-mbapi/include/curl/curl.php");
require_once("lib-action/include/input/input.php");
require_once("objects/ClientContact.php");

$i = 0;
/**
 * The constant to represent a successful registrar execution.
 */
define("REGISTRAR_SUCCESS", $i);
$i++;

/**
 * The constant to represent a failed registrar execution.
 */
define("REGISTRAR_FAILURE", $i);
$i++;

/**
 * The constant to represent a failed connection to a physical registrar during execution.
 */
define("REGISTRAR_CONNECTION_FAILURE", $i);
$i++;

/**
 * The constant to represent a failed requirement during parameter validation.
 */
define("REGISTRAR_PARAM_FAILURE", $i);
$i++;

/**
 * The constant to identify a registrar's charge capability.
 */
define("REGISTRAR_REGISTERDOMAIN", $i);
$i++;

/**
 * The constant to identify a registrar's transfer capability.
 */
define("REGISTRAR_TRANSFERDOMAIN", $i);
$i++;

/**
 * The constant to identify a registrar's renew domains capability.
 */
define("REGISTRAR_RENEWDOMAIN", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return contact info.
 */
define("REGISTRAR_GETDOMAINCONTACTDATA", $i);
$i++;

/**
 * The constant to identify a registrar's capability to update contact info.
 */
define("REGISTRAR_SETDOMAINCONTACTDATA", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return domain auto-renewal options.
 */
define("REGISTRAR_GETdomainRenewTypeOPTIONS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to update domain auto-renewal options.
 */
define("REGISTRAR_SETdomainRenewTypeOPTIONS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return domain registrar-lock options.
 */
define("REGISTRAR_GETDOMAINREGISTRARLOCKOPTIONS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to update domain registrar-lock options.
 */
define("REGISTRAR_SETDOMAINREGISTRARLOCKOPTIONS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's access password.
 */
define("REGISTRAR_GETDOMAINACCESSPASSWORD", $i);
$i++;

/**
 * The constant to identify a registrar's capability to update a domain's access password.
 */
define("REGISTRAR_SETDOMAINACCESSPASSWORD", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's expiration date.
 */
define("REGISTRAR_GETDOMAINEXPDATE", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's extended information.
 */
define("REGISTRAR_GETDOMAINEXTENDEDINFO", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's availability for registration.
 */
define("REGISTRAR_CHECKDOMAINAVAILABILITY", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's registrar info.
 */
define("REGISTRAR_CHECKDOMAINTRANSFERAVAILABILITY", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's renew type (manual or auto).
 */
define("REGISTRAR_GETDOMAINRENEWTYPEOPTIONS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to set a domain's renew type (manual or auto).
 */
define("REGISTRAR_SETDOMAINRENEWTYPEOPTIONS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's DNS info.
 */
define("REGISTRAR_GETDOMAINDNS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to set a domain's DNS info.
 */
define("REGISTRAR_SETDOMAINDNS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a reseller's account balance.
 */
define("REGISTRAR_CHECKACCOUNTBALANCE", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a list of TLDs with prices.
 */
define("REGISTRAR_GETTLDPRICING", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a domain's transfer status.
 */
define("REGISTRAR_GETTRANSFERSTATUS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to return a list of TLDs.
 */
define("REGISTRAR_GETTLDLIST", $i);
$i++;

/**
 * The constant to identify a registrar's supported TLDs.
 */
define("REGISTRAR_TLDS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to retrun a domain's renew date.
 */
define("REGISTRAR_GETDOMAINRENEWDATE", $i);
$i++;

/**
 * The constant to identify a registrar's capability to do a live renewal using our system instead of the queue.
 */
define("REGISTRAR_LIVERENEW", $i);
$i++;

/**
 * The constant to identify a registrar's capability to fetch all domains in the account.
 */
define("REGISTRAR_GETDOMAINS", $i);
$i++;

/**
 * The constant to identify a registrar's capability to upload documents uses in domain registration.
 */
define("REGISTRAR_UPLOADDOCUMENTS", $i);
$i++;

/**
 * An interface for all Registrar modules to implement. If a Registrar module does not follow this interface
 * exactly, it is not portable among the application, and may fail at some points.  This interface
 * was written using a PHP4 implementation, but later should be changed for PHP5 implementation in order
 * to create truly abstract methods.
 */
abstract class Registrar extends MB_Module
{
	const DOMAIN_AVAIL_STATUS_AVAIL = "available";
	const DOMAIN_AVAIL_STATUS_NOT_AVAIL = "not-available";
	const DOMAIN_AVAIL_STATUS_CANT_CHECK = "cant-check";

	const STATUS_STABLE = "stable";
	const STATUS_TESTING = "testing";

	/**
	 * The status returned by the last execution of this Registrar.
	 */
	public $status;

	/**
	 * The account id or call status code returned by the last execution of this Registrar.
	 */
	public $successCode;

	/**
	 * The capabilities provided by this Registrar object. The only required capability for a
	 * registrar is the "charge" capability.
	 */
	public $capabilities;

	/**
	 * The TLDs that this registrar supports.
	 */
	public $tlds;

	/**
	 * The response from the registrar.
	 */
	public $response;

	/**
	 * A set of default configuration parameters for all registrars (currently not in use).
	 */
	public $configParams;

	/**
	 * The constructor for a Registrar object. For the most part this constructor should not be overridden
	 * by Registrar implementations. This method is designed to take the <params> element of an MBAPI
	 * command's input XML. It uses getDBCol() and getDBXMLCol() to associate array element names with
	 * database column names, so literally when coming from an MBAPI command, no modification to the
	 * <params> element is needed. Be sure to pass in the <params> element as an array, and not raw XML.
	 *
	 * @param  params  The parameters needed by a Registrar object. All of the following parameters are
	 *                 required here, and if they are not provided, all calls to this class' methods will
	 *                 fail. Validity can be checked using the isValid() method. The following parameters
	 *                 are required:
	 *                   clientID, packageID, configID, domainID, registerTerm
	 *
	 *                   registrantFirstName, registrantLastName, registrantRole, registrantCompany,
	 *                   registrantEmail, registrantAddress, registrantAddress2, registrantCity,
	 *                   registrantState, registrantZip,
	 *                   registrantCountry, registrantPhone, registrantFax
	 */
	public function Registrar($input)
	{
		parent::__construct($input);
		$this->setConfigID();

		dbg($input);

		$this->hasExecuted = $this->hasExecutedSuccessfully = 0;
		$this->status = REGISTRAR_FAILURE;
		$this->successCode = $this->valid = $this->accountID = 0;
		$this->connectionInfo = NULL;
		$this->tlds = array();
		$this->configParams = array();
		$this->response = "";
		$this->timestamp = mktime();
		$this->input = $input;

		$this->capabilities[REGISTRAR_UPLOADDOCUMENTS] = 0;

		if(!is_array($input)) {
			$this->status = REGISTRAR_PARAM_FAILURE;
		} else {

			if ($input["UID"]) {
				$this->UID = trim($input["UID"]);
			}
			if ($input["PW"]) {
				$this->PW = $input["PW"];
			}

			$subCommand = $this->input["subCommand"];
			if ("" == $subCommand) {
				$this->hasExecuted = 1;
				return;
			}
            
            dbg("subCommand:".$subCommand);
			switch ($subCommand) {
	         	// primary domain actions
				case "checkDomainAvailability":
					$this->checkDomainAvailability();
				break;
				case "checkDomainTransferAvailability":
					$this->checkDomainTransferAvailability();
				break;
				case "registerDomain":
					$this->registrarFormatPhone();
					$this->registerDomain();
				break;
				case "renewDomain":
				case "renew":
					$this->registrarFormatPhone();
					$this->renewDomain();
				break;
				case "transferDomain":
					$this->transferDomain();
				break;
				// manage contact data
				case "getDomainContactData":
					$this->getDomainContactData();
				break;
				case "setDomainContactData":
					$this->setDomainContactData();
				break;
				// manage auto-renew options
				case "getDomainRenewTypeOptions":
					$this->getdomainRenewTypeOptions();
				break;
				case "setDomainRenewTypeOptions":
					$this->setdomainRenewTypeOptions();
				break;
				// manage extended info
				case "getDomainExpDate":
					$this->getDomainExpDate();
				break;
				// manage exp date
				case "getDomainExtendedInfo":
					$this->getDomainExtendedInfo();
				break;
				// manage auto-renew options
				case "getDomainRegistrarLockOptions":
					$this->getDomainRegistrarLockOptions();
				break;
				case "setDomainRegistrarLockOptions":
					$this->setDomainRegistrarLockOptions();
				break;
				// manage access password
				case "getDomainAccessPassword":
					$this->getDomainAccessPassword();
				break;
				case "setDomainAccessPassword":
					$this->setDomainAccessPassword();
				break;
				// manage DNS
				case "getDomainDNS":
					$this->getDomainDNS();
				break;
				case "setDomainDNS":
					$this->setDomainDNS();
				break;
				case "getDomains":
					$this->getDomains();
				break;
           		case "getTLDList":
					$this->getTLDList();
				break;
				case "checkAccountBalance":
					$this->checkAccountBalance();
				break;
				default:
					$this->hasExecuted = 1;
					$this->numErrors ++;
					$this->errors[] = array("No subCommand supplied to Registrar.");
				break;

			} // (strtolower($this->params["subCommand"]))

		} // (!is_array($input))

	}

	public function getParameter($name) {
		$connection = DatabaseManager::getConnection();
		$query = "
			SELECT
				parameters.config_param_value as value
			FROM
				config_params as parameters,
				config_groups as modules,
				config_groups as registrars,
				config_groups as domains
			WHERE
				domains.parent_config_group_id = 0 AND
				domains.config_group_name = 'domain' AND
				registrars.parent_config_group_id = domains.config_group_id AND
				registrars.config_group_name = 'registrar' AND
				modules.parent_config_group_id = registrars.config_group_id AND
				modules.config_group_name = '" . strtolower(get_class($this)) . "' AND
				parameters.config_group_id = modules.config_group_id AND
				parameters.config_param_name = " . $connection->quote($name) . "
			ORDER BY
				parameters.config_param_id DESC
			LIMIT
				1
		";
		return $connection->getOne($query);
	}

	/**
	 * This method will change the phone and fax number to match the format expected by the
	 * domain registrar [+] [country code] . [phone number]
	 */
	protected function registrarFormatPhone()
	{
		if ("" != $this->input['registrantPhone'] && !preg_match("/\+[[:digit:]]+\.[[:digit:]]+/", $this->input['registrantPhone'])) {
			$phoneComponents = billing_get_phone_src($this->input['registrantPhone']);
			if ("" == $phoneComponents["country_code"]) {
				$phoneComponents["country_code"] = ("" != $this->input["registrantCountryPhoneCode"]) ? $this->input["registrantCountryPhoneCode"] : 1;
			}
			$this->input['registrantPhone'] = "+{$phoneComponents['country_code']}.{$phoneComponents['area_code']}{$phoneComponents['number']}{$phoneComponents['extension']}";
		}
		if ("" != $this->input['registrantFax'] && !preg_match("/\+[[:digit:]]+\.[[:digit:]]+/", $this->input['registrantFax'])) {
			$faxComponents = billing_get_phone_src($this->input['registrantFax']);
			if ("" == $faxComponents["country_code"]) {
				$faxComponents["country_code"] = ("" != $this->input["registrantCountryPhoneCode"]) ? $this->input["registrantCountryPhoneCode"] : 1;
			}
			$this->input['registrantFax'] = "+{$faxComponents['country_code']}.{$faxComponents['area_code']}{$faxComponents['number']}{$faxComponents['extension']}";
		}
	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for checking a domain's availability for registration.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function checkDomainAvailability() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for checking a domain's availability for transfer.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function checkDomainTransferAvailability() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for registering a new domain name.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function registerDomain() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for transfering an existing domain name.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function transferDomain() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for renewing a new domain name.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function renewDomain() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's expiration date.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomainExpDate() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's extended information.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomainExtendedInfo() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's contact information.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomainContactData() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for updating a domain's contact information.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function setDomainContactData() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's auto-renewal options.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getdomainRenewTypeOptions() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's auto-renewal options.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function checkAccountBalance() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for setting a domain's auto-renewal options.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function setdomainRenewTypeOptions() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's registrar lock options.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomainRegistrarLockOptions() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for setting a domain's registrar lock options.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function setDomainRegistrarLockOptions() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's access password.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomainAccessPassword() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for setting a domain's access password.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function setDomainAccessPassword() {

	}
	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for fetching a domain's name servers and dns setttings.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomainDNS() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for setting a domain's name servers and dns settings.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function setDomainDNS() {

	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for grabbing a list of domains in registrar account.
	 *
	 * @return  string xmlData of containting results of the remote registrar connection
	 */
	function getDomains() {

	}

	/**
	 * This method will set the valid name/value that are sourced from the attrobutes map.
	 *
	 * @param  var[]  array of valid fields to check
	 * @param  string  the contact type to pull.  Used when updating all
	 *
	 * @return  array An array of post fields
     */
    function getPostValues($validParams=array(), $type=null) {
        // load the attributes map
	  	$map = $this->getAttributesMap();
        // see if we have to test for valid params
        $c = count($validParams);
		// Loop the map and pull the needed fields
		$postValues = array();
		foreach($map as $k => $v) {
			if($type) {
				// if this element has a value it's part of this registrar's API, but only add it if it's set
				if($v && strlen($this->input["domainContactData"][$type][$k])>0 && (($c && in_array($v,$validParams)) || !$c)) {
					$postValues[$v] = $this->input["domainContactData"][$type][$k];
				}
			} else {
				// if this element has a value it's part of this registrar's API, but only add it if it's set
				if($v && strlen($this->input[$k])>0 && (($c && in_array($v,$validParams)) || !$c)) {
					$postValues[$v] = $this->input[$k];
				}
			}
		}

        return $postValues;
    }

	/**
	 * Get an array of capabilities that this Registrar object supports. The array is of the form:
	 *   REGISTRAR_CAPABILITY_CONSTANT => INTEGER
	 * If the INTEGER does not equal 0 (resolves to true), it can be assumed that this Registrar has
	 * the capability represented by REGISTRAR_CAPABILITY_CONSTANT, and if the REGISTRAR_CAPABILITY_CONSTANT
	 * array element is not set or is equal to 0, this Registrar object does not have that capability.
	 * The REGISTRAR_CAPABILITY_CONSTANT constants are defined at the top of this file.
	 *
	 * @return  An array of capabilities of this Registrar object.
	 */
	function getCapabilities() {
		return $this->capabilities;
	}

    /**
	 * This check for curl errors and set the proper panel settings if errors are found
	 *
	 */
	function getCurlErrors($c) {
    	if($c->numErrors>0){
		 	foreach($c->errors as $value) {
				$this->errors[] = array("cURL Error:", $value);
				$this->numErrors ++;
		 	}
		 	$this->hasExecutedSuccessfully = 0;
		 	$this->arrayData = $c->resultArray;
		}
	}

	/**
	 * This method will return the XML string with curl connection info if that data exists.  Time
	 * items will be in seconds with millisecond resolution and the size items will be in units of bytes
	 *
	 * @return  string  The XML string containing the cURL connection results.
	 */
	function getRegistrarCurlInfo() {
		$loopSpacer = " ";
	 	$connectionSets = "";
	 	$connectionSummaryData = array();
	 	$numSets = 0;
		if($this->connectionInfo){
			if(is_array($this->connectionInfo)){
				//Build the connection info XML
				$connectionSets .= "       <curlConnectionDataSets>\n";
				foreach($this->connectionInfo as $key => $value) {
				 	if (is_array($value)) {
						$connectionSets .= "          <curlConnectionDataSet>\n";
						foreach($value as $key1 => $value1) {
							$connectionSets .=  "            <$key1>".$value1."</$key1>\n";
						}
						$connectionSets .= "          </curlConnectionDataSet>\n";
						// add to summary data
						$connectionSummaryData["time_total"] += $value["time_total"];
						$connectionSummaryData["ave_time_namelookup"] += $value["time_namelookup"]; // ave
						$connectionSummaryData["ave_time_connect"] += $value["time_connect"]; // ave
						$connectionSummaryData["ave_time_pretransfer"] += $value["time_pretransfer"]; // ave
						$connectionSummaryData["ave_time_starttransfer"] += $value["time_starttransfer"]; // ave
						$connectionSummaryData["size_download"] += $value["size_download"];
						$connectionSummaryData["size_upload"] += $value["size_upload"];
						$connectionSummaryData["size_header"] += $value["size_header"];
						$connectionSummaryData["size_request"] += $value["size_request"];
						$connectionSummaryData["ave_speed_download"] += $value["speed_download"]; // ave
						$connectionSummaryData["ave_speed_upload"] += $value["speed_upload"]; // ave
						$numSets ++;
					} else {
						$connectionSets .=  "          <$key>".$value."</$key>\n";
					}
				}
				$connectionSets .= "        </curlConnectionDataSets>\n";
			}
		}
	 	$connectionSummary = "   <curlConnectionDataSummary>\n";
	 	$connectionSummary .=  "          <total_connections>".$numSets."</total_connections>\n";
		foreach($connectionSummaryData as $key => $value) {
			if (eregi("ave",$key) && $numSets != 0) {
				$value = round(($value / $numSets),4);
			}
			$connectionSummary .=  "          <$key>".$value."</$key>\n";
		}
	 	$connectionSummary .= "       </curlConnectionDataSummary>\n";
		return $connectionSummary.$connectionSets;
	}

	/**
	 * helper method to jump through return hoops of our structure
	 */
	function createReturn(array $data = array(), $status = true)
	{
		if ($status) {
			$this->hasExecutedSuccessfully = 1;
			$this->arrayData = $data;
		} else {
			foreach ($data as $e) {
				$this->errors[] = array(get_class($this)."'s error: ", $e);
			}
			$this->numErrors++;
			$this->hasExecutedSuccessfully = 0;
		}
		$this->hasExecuted = 1;
		$this->xmlData = $this->getRegistrarReturnXML();
		return $this->xmlData;
	}

	/**
	 * This method will return the XML string with curl connection info if that data exists.  Time
	 * items will be in seconds with millisecond resolution and the size items will be in units of bytes
	 *
	 * @return  string  The XML string containing the registrar results and cURL connection results.
	 */
	function getRegistrarReturnXML() {
		//$loopSpacer = " ";
		//return $this->getNestedXML($this->arrayData)."     ".$this->getRegistrarCurlInfo();
		return toXML($this->arrayData);
	}

	/**
	 * This method will return nested XML strings if data exists. We may move this method outside of the registrar
	 * class or it can be copied to other module classes.
	 *
	 * @return  string  The nested XML string.
	 * @author  Michael Fountain
	 */
	function getNestedXML($array) {
	 	global $loopSpacer;
	 	$currentSpacer = $loopSpacer."  ";
	 	$result = "";
		if (is_array($array)) {
	 	 	foreach($array as $key => $value) {
	 	 	 	if (is_array($value)) {
	 	 	 	 	#if (((INT) $key) || $key == 0) {
					#	$key = "response";
	 	 	 	 	#}
	 	 	 	 	$result .= $currentSpacer."      <$key>\n";
					$result .= $this->getNestedXML($value);
					$result .= $currentSpacer."      </$key>\n";
	 	 	 	} else {
					$result .= $currentSpacer."        <$key>$value</$key>\n";
				}
	 	 	}
		}
		return $result;
	}

	/**
	 * This method sets the $CONF array index value for this instance of a registrar
	 *
	 * @return  1 if the action executed successfully, and false if otherwise.
	 */
	function setConfigID() {
		//Find the CONF index for this registrar
		global $CONF;

		//Fix arrayness if only one registrar in the group
		$registrars = $CONF["domain"]["registrar"];
		if(is_array($registrars)) {
			foreach($registrars as $key => $value) {
				$temp = $value;

				if ($temp["modulePassword"]) {
                    require_once 'object_managers/CryptManager.php';
                    $temp["modulePassword"] = CryptManager::decrypt($temp["modulePassword"], null, false);
				}
				if(is_array($temp)) {
					if(!$temp[0] && count($temp) > 0) {
						unset($CONF["domain"]["registrar"][$key]);
						$CONF["domain"]["registrar"][$key][$value["configGroupID"]] = $temp;
						$temp = "";
					}
				}
			}
		}

		if($CONF["domain"]["registrar"][strtolower($this->stringRep)]) {
			foreach($CONF["domain"]["registrar"][strtolower($this->stringRep)] as $key => $value) {
				if($value["configGroupID"] == (int) $this->input["configGroupID"]) {
					//Use this key to get the registrar's config options out of $CONF
					$id = $key;
				}
			}
		}
		//Push the config items onto this class if the id is found
		if($id) {
			$this->config = $CONF["domain"]["registrar"][strtolower($this->stringRep)][$id];
			if(!notNull( $this->UID )) {
				$this->UID = trim($CONF["domain"]["registrar"][strtolower($this->stringRep)][$id]["moduleUsername"]);
			}
			if(!notNull( $this->PW )) {
				$this->PW = $CONF["domain"]["registrar"][strtolower($this->stringRep)][$id]["modulePassword"];
			}
		}
		$this->configID = $id;

	}
	/**
	 * This method returns an array of Japanese prefectures
	 *
	 * @return  Array of prefectures
	 */
	function getJapanesePrefectures() {

		$validPrefectures = array(
			"Hokkaido" => 1, "Aomori" => 2, "Iwate" => 3, "Miyagi" => 4, "Akita" => 5, "Yamagata" => 6,
			"Fukushima" => 7, "Ibaragi" => 8, "Tochigi" => 9, "Gumma" => 10, "Saitama" => 11, "Chiba" => 12,
			"Tokyo" => 13, "Kanagawa" => 14, "Niigata" => 15, "Toyama" => 16, "Ishikawa" => 17, "Fukui" => 18,
			"Yamanashi" => 19, "Nagano" => 20, "Gifu" => 21, "Shizuoka" => 22, "Aichi" => 23, "Mie" => 24,
			"Shiga" => 25, "Kyoto" => 26, "Osaka" => 27, "Hyogo" => 28, "Nara" => 29, "Wakayama" => 30,
			"Tottori" => 31, "Shimane" => 32, "Okayama" => 33, "Hiroshima" => 34, "Yamaguchi" => 35, "Tokushima" => 36,
			"Kagawa" => 37, "Ehime" => 38, "Kochi" => 39, "Fukuoka" => 40, "Saga" => 41, "Nagasaki" => 42,
			"Kumamoto" => 43, "Oita" => 44, "Miyazaki" => 45, "Kagoshima" => 46, "Okinawa" => 47);

		return $validPrefectures;
	}

	/**
	 * This method returns the PUNYcode version of an IDN domain name
	 *
	 * @return  Domain SLD in PUNYcode format
	 */
	function getPUNYCode($sld){
		if (utf8_decode($sld) !== $sld){
			require_once mb_path("/lib-domain/include/idna/idna_convert.class.php");
			$idn = new Net_IDNA_php4();
			$encoded = $idn->encode($sld);
			$decoded = $idn->decode($encoded);

			// Double check that the encoding works
			if ($sld === $decoded){
				$sld = str_replace("&#45;", "-", $encoded);
			}
		}
		return $sld;
	}

	/**
	 *	Grab a list of the current TLD's
	 *
	 *	@author Brian Smith
	 */
	function getTLDs( $tld = false, $grabContents = false ) {
		global $CONF;
		$start = dirname( __FILE__).str_repeat("/..", 4);

		if ( ($dh = opendir($start.'/lib-domain/include/ccTLDs/')) == false) {
			$this->addError(TRANS_ACTIONERROR, "Failed to open or find TLD List");
			return false;
		}

		$excludeList = array('.', '..', 'CVS');
		$localeLang = $CONF["user"]["locale"]->language;
		$localeCountry = $CONF["user"]["locale"]->country;

		$localeCode = $localeLang."_".$localeCountry;

		$localeCode2='';
		if (strpos($localeLang,'_')) {
			$t = explode('_', $localeLang);
			$localeCode2 = $t[0]."_".$localeCountry;
			unset($t);
		}


		$ret = array();
		while( ($file = readdir($dh)) !== false ) {
			if (!in_array($file, $excludeList)) {
				$found = false;
				if ($tld == false) {
					// en_us.tld.us.xml
					$regex = "/^(?:$localeCode|$localeCode2)\.tld\.(.+)\.xml$/";
					if (preg_match($regex, $file, $match)) {
						if ($grabContents) {
							if ($match[1] == $grabContents) {
								$ret[ $match[1] ] = $this->_returnCCTLD( $file );
							}
						} else {
							$ret[] = $match[1];
						}
					}

					if (!$found) {
						// en.tld.us.xml
						$regex = "/^$localeLang\.tld\.(.+).xml$/";

						if (preg_match($regex, $file, $match)) {
							if ($grabContents) {
								if ($match[1] == $grabContents) {
									$ret[ $match[1] ] = $this->_returnCCTLD( $file );
								}
							} else {
								$ret[] = $match[1];
							}
						}
					}
				} else {
					$file_l = strtolower($file);
					switch( true ) {
					case ($file_l == strtolower("$localeCode.tld.$tld.xml")):
					case ($file_l == strtolower("$localeCode2.tld.$tld.xml")):
					case ($file_l == strtolower("$localeLang.tld.$tld.xml")):
						if ($grabContents) {
							$ret[ $tld ] = $this->_returnCCTLD( $file );
						} else {
							$ret[] = $tld;
						}
						break;
					}
				}

			}
		}
		return $ret;
	}

	/**
	 * This method needs to be overridden by children. When overridden, this function should contain
	 * the registrar specific code for validating an account or sub-account push.
	 *
	 * @return  string xmlData of containting results of the remote parking connection
	 */
	function validateAccount() {
		$this->arrayData["accountIsValid"] = 1;
		$this->arrayData["externalID"] = "";
		// Return
		$this->hasExecuted = 1;
		$this->xmlData = $this->getParkingReturnXML();
		return $this->xmlData;
	}

	function _returnCCTLD($file) {
		$start = dirname( __FILE__).str_repeat("/..", 4);
		$dir = $start.'/lib-domain/include/ccTLDs/';

		$fileLoc = $dir.$file;

		$xml = new MBXML(file_get_contents($fileLoc));

		$contents = $xml->toArray();

		if (isset($contents['interface-response']) && isset($contents['interface-response'][0]['attributes'])) {
			$contents = correctArray($contents);

			if (is_array($contents['interface-response'][0]['attributes'])) {
				return $contents['interface-response'][0]['attributes'];
			}
		}
		return false;
	}

	// For all registrar always true except Rucenter
	public function isDone()
	{
		return true;
	}

	public function hideContactForm($tld)
	{
		return 0;
	}

	/**
	 * Returns list of parameters that can be
	 * configured to create registrar
	 *
	 * @return array
	 */
	public function addDefaultConfigParams()
	{
		// getDefaultConfigOptions method should be implemented
		// in concrete registrar module
		$options = $this->getDefaultConfigOptions();

		$defaultConfigParams = array();
    	$index = 0;
    	foreach ($options as $group => $items) {
    		foreach ($items as $item) {
    			$defaultConfigParams[] = array(
	    			NULL,
	    			NULL,
	    			$item[0],
	    			$item[3],
	    			$group,
	    			$item[4],
	    			$item[5],
	    			$item[1],
	    			$item[2],
	    			$index++,
	    			0,
	    			($item[1] == "r" ? ($item[6] != NULL ? $item[6] : array(array(NULL,NULL,'1','TRANS_YES',2), array(NULL,NULL,'0','TRANS_NO',1))) : NULL)
	    		);
    		}
    	}

    	return $defaultConfigParams;
	}

	public function getRegistrantParams($action, $domain_sld, $domain_tld, ClientContact $contact = NULL, $withTLDParams = true)
	{
		$prefix = "{$domain_sld}_";
		require_once("object_managers/CountryManager.php");
		$countries = array();
		foreach (CountryManager::getCountries() as $code => $name) {
			$countries[] = array($code, $name);
		}
		$contact_data = array();
		$client_id = is_null($contact) ? NULL : $contact->client_id;
		if (! is_null($contact)) {
			$prior_subscr = is_null($contact->id) ? NULL : $this->getPriorSubscription($this->input["configGroupID"], $client_id);
			if (! is_null($prior_subscr)) {
				$contact_data["first_name"] = $prior_subscr->PackageAttributes["registrantFirstName"]->value;
				$contact_data["last_name"] = $prior_subscr->PackageAttributes["registrantLastName"]->value;
				$contact_data["address_1"] = $prior_subscr->PackageAttributes["registrantAddress1"]->value;
				$contact_data["address_2"] = $prior_subscr->PackageAttributes["registrantAddress2"]->value;
				$contact_data["city"] = $prior_subscr->PackageAttributes["registrantCity"]->value;
				$contact_data["state"] = $prior_subscr->PackageAttributes["registrantState"]->value;
				$contact_data["zip"] = $prior_subscr->PackageAttributes["registrantPostalCode"]->value;
				$contact_data["company"] = $prior_subscr->PackageAttributes["registrantOrganizationName"]->value;
				$contact_data["email"] = $prior_subscr->PackageAttributes["registrantEmail"]->value;
				$contact_data["phone1"] = $prior_subscr->PackageAttributes["registrantPhone"]->value;
				$contact_data["fax"] = $prior_subscr->PackageAttributes["registrantFax"]->value;
				$contact_data["country"] = $prior_subscr->PackageAttributes["registrantCountry"]->value;
				$contact_data["job_title"] = $prior_subscr->PackageAttributes["registrantJobTitle"]->value;
			}  else {
				$contact_data["first_name"] = $contact->contact_first_name;
				$contact_data["last_name"] = $contact->contact_last_name;
				$contact_data["address_1"] = $contact->contact_address_1;
				$contact_data["address_2"] = $contact->contact_address_2;
				$contact_data["city"] = $contact->contact_city;
				$contact_data["state"] = $contact->contact_state;
				$contact_data["zip"] = $contact->contact_zip;
				$contact_data["company"] = $contact->contact_company;
				$contact_data["email"] = $contact->contact_email;
				$contact_data["phone1"] = $contact->contact_phone1;
				$contact_data["fax"] = $contact->contact_fax;
				$contact_data["country"] = $contact->Country->countries_iso_2;
			}
		}

        $inputs = array (
            array(
                "id"		=> "registrantOrganizationName",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_COMPANY_NAME"),
                "required"	=> 0,
                "default"	=> isset($contact_data["company"]) ? $contact_data["company"] : "",
            ),
            array(
                "id"		=> "registrantJobTitle",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_DOMAIN_JOB_TITLE"),
                "required"	=> 0,
                "default"	=> isset($contact_data["job_title"]) ? $contact_data["job_title"] : "",
            ),
            array(
                "id"		=> "registrantFirstName",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_FIRST_NAME"),
                "required"	=> 1,
                "default"	=> isset($contact_data["first_name"]) ? $contact_data["first_name"] : "",
            ),
            array(
                "id"		=> "registrantLastName",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_LAST_NAME"),
                "required"	=> 1,
                "default"	=> isset($contact_data["last_name"]) ? $contact_data["last_name"] : "",
            ),
            array(
                "id"		=> "registrantAddress1",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_ADDRESS1"),
                "required"	=> 1,
                "default"	=> isset($contact_data["address_1"]) ? $contact_data["address_1"] : "",
            ),
            array(
                "id"		=> "registrantAddress2",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_ADDRESS2"),
                "required"	=> 0,
                "default"	=> isset($contact_data["address_2"]) ? $contact_data["address_2"] : "",
            ),
            array(
                "id"		=> "registrantCity",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_CITY"),
                "required"	=> 1,
                "default"	=> isset($contact_data["city"]) ? $contact_data["city"] : "",
            ),
            array(
                "id"		=> "registrantState",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_STATE"),
                "required"	=> 1,
                "default"	=> isset($contact_data["state"]) ? $contact_data["state"] : "",
            ),
            array(
                "id"		=> "registrantCountry",
                "type"		=> "s",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_COUNTRY"),
                "required"	=> 1,
                "items"		=> $countries,
                "default"	=> isset($contact_data["country"]) ? $contact_data["country"] : "",
            ),
            array(
                "id"		=> "registrantPostalCode",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_ZIP"),
                "required"	=> 1,
                "default"	=> isset($contact_data["zip"]) ? $contact_data["zip"] : "",
            ),
            array(
                "id"		=> "registrantEmail",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_EMAIL"),
                "required"	=> 0,
                "default"	=> isset($contact_data["email"]) ? $contact_data["email"] : "",
            ),
            array(
                "id"		=> "registrantPhone",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_PHONE1"),
                "required"	=> 1,
                "default"	=> isset($contact_data["phone1"]) ? $contact_data["phone1"] : "",
            ),
            array(
                "id"		=> "registrantFax",
                "type"		=> "t",
                "title"		=> blmsg("TRANS_ORDERFORM_CONTACT_FAX"),
                "required"	=> 0,
                "default"	=> isset($contact_data["fax"]) ? $contact_data["fax"] : "",
            ),
        );

		if ("transfer" == $action) {
            $inputs[] = array(
                "id" => "domainTransferKey",
                "title" => blmsg("TRANS_DOMAIN_TRANSFER_AUTHCODE_TITLE"),
                "description" => blmsg("TRANS_DOMAIN_TRANSFER_AUTHCODE_DESC"),
                "type" => "t",
                "required" => 1,
            );
		}

		$params = array(array(
			"title" => "",
			"description" => "",
			"inputs" => $inputs,
		));

		foreach ($params as &$group) {
			foreach ($group["inputs"] as &$param) {
				if (! is_null($this->collected_registrant_params)) {
					$param["default"] = $this->collected_registrant_params[$param["id"]];
				}
				$param["id"] = $prefix.$param["id"];
			}
		}
		if ($withTLDParams) {
			$tld_params = $this->convertTLDParamsToFormDescriptor($this->getTLDParamsData($domain_tld), $client_id, $prefix);
			if (! is_null($tld_params)) {
				foreach ($tld_params as $panel) {
					$params[] = $panel;
				}
			}
		}
		return $params;
	}

	public function collectRegistrantParams($action, $domain_sld, $domain_tld, ClientContact $contact = NULL)
	{
		$collected_data = array();
		$errors_list = array();
		foreach ($form_descriptor = $this->getRegistrantParams($action, $domain_sld, $domain_tld, $contact) as $group) {
			foreach ($group["inputs"] as $param) {
				$value = bgpc($param["id"]);
				$collected_data[preg_replace("/^{$domain_sld}_/", "", $param["id"])] = $value;

				if(1 == $param["required"] && (is_null($value) || "" == $value)) {
					$param["error_msg"] = $errors_list[$param["id"]] = blmsg("TRANS_INVALID_INPUT_VALUE");
				}
			}
		}
		return array(
			"data" => $this->collected_registrant_params = $collected_data,
			"form_descriptor" => $form_descriptor,
			"errors_list" => !empty($errors_list) ? $errors_list : NULL,
		);
	}

    public function getTLDList()
    {
        return $this->capabilities[REGISTRAR_TLDS];
    }

	protected function getTLDParamsData($tld)
	{
		return correctArray(getTLDParamsData($tld));
	}

	protected function getCurlObject($url = '', $fields = '', $cli = 0, $config_identity = NULL)
	{
		if (is_null($config_identity)) {
			$config_identity = 'Registrar_'.get_class($this);
		}
		$c = new Curl($url, $fields, $cli, $config_identity);
		return $c;
	}

	protected function getPriorSubscription($registrar_id, $client_id)
	{
		$package_domains = Factory()->PackageDomainList(array("registrar_id" => $registrar_id, "domain_active" => 1));
		foreach (array_reverse($package_domains) as $domain) {
			$package = $domain->getPackage();
			if (! is_null($package) && $client_id == $package->client_id) {
				return $package;
			}
		}
		return NULL;
	}

	protected function convertTLDParamsToFormDescriptor($tld_params, $client_id, $prefix = "")
	{
		if (is_null($tld_params)) {
			return NULL;
		}
		$prior_subscription = is_null($client_id) ? NULL : $this->getPriorSubscription($this->input["configGroupID"], $client_id);

		$converted_params = array(
			"title" => $tld_params["title"],
			"description" => $tld_params["additionalMessage"],
			"inputs" => array(),
		);


		foreach ($tld_params["attributes"] as $attr) {
				$input_type = "s";
				$default_value = ! is_null($prior_subscription) && isset($prior_subscription->PackageAttributes[$attr["name"]])
					? $prior_subscription->PackageAttributes[$attr["name"]]->value
					: $attr["default"];
				if (isset($attr["userdefined"]) && "true" == strtolower($attr["userdefined"])) {
					$input_type = isset($attr["multiline"]) && "true" == strtolower($attr["multiline"])
						? "a" : "t";
				}
				$items = $value = NULL;
				if ("s" == $input_type) {
					if (1 != count($attr["options"])) {
						$items = array();
						foreach ($attr["options"] as $option_desc) {
							$params_to_show = $description = NULL;
							if (isset($option_desc[0])) {
								$option_desc = $option_desc[0];
							}
							if (isset($option_desc["paramsToShow"])) {
								$params_to_show = array();
								foreach ($option_desc["paramsToShow"] as $param_id) {
									$params_to_show[] = $prefix.$param_id;
								}
							}
							if (isset($option_desc["description"])) {
								$description = $option_desc["description"];
								$input_type = (isset($attr["inputType"]) && "select" == strtolower($attr["inputType"])) ? 's' : 'r' ;
							}
							$items[] = array(
								$option_desc["value"],
								$option_desc["title"],
								$params_to_show,
								$description,
							);
						}
					} else {
						$input_type = 'b';
						$value = (isset($attr["options"][0])) ? $attr["options"][0]["value"] : NULL;
                        if (is_null($default_value) && !is_null($value)) {
                            $default_value = $value;
                        }
					}
				}
				$converted_params["inputs"][] = array(
					"id" => $prefix.$attr["name"],
                    "validators" => isset($attr["validators"]) ? $attr["validators"] : null,
					"title" => $attr["description"],
					"description" => isset($attr["hint"]) ? $attr["hint"] : NULL,
					"required" => $attr["required"],
					"default" => $default_value,
					"value" => $value,
					"type" => $input_type,
					"items" => $items,
                    "hidden" => isset($attr["hidden"]) ? $attr["hidden"] : NULL,
					"read_only" => isset($attr["readonly"]) ? $attr["readonly"] : 0,
				);
			}
		return array($converted_params);
	}

	protected $collected_registrant_params;
}