Creating additional modules for domain registrars

From ISPWiki

Jump to: navigation, search

If you want to add a registrar, you will need to write a script that will be named as dr[nameregistrar], and locate it into the /usr/local/ispmgr/sbin directory. The set of symbols followingdr is a registrar's internal name the interface elements will use.

Contents

Script features

The script should support the following commands:

check <domain name> <registrar_id> - check domain's availability. If a domain is available, code 0 is used, if not, code 1.

register <domain_id> - register a domain

renew <domain_id> - renew a domain

transfer <domain_id> - transfer a domain

update ns <domain_id> - renew domain's NS servers

set status <domain_id[,domain_id[...]]> - define a domain's status and registration length

fix <domain_id[,domain_id[...]]> - once a month synchronize billing and registrar's data.

validate - verify domain's contact data. An XML will be sent to stdin.

Domain statuses

1 - Not paid;

2 - Delegated (Active);

3 - Registered (Not delegated);

4 - Removed;

5 - In progress (Registration);

6 - In progress (Renewal);

7 - In progress (Transfer).

The validate command

This command verifies contact data when registering a domain. The following XML is sent to stdin:

<?xml version="1.0" encoding="UTF-8"?>

<doc>

 <node ame="NAME_FIELD1">VALUE1</node>
		
 <node name="NAME_FIELD1">VALUE1</node>
		

</doc>

The script will terminate with 0. In case of invalid field, its name should be sent to stdout (such as NAME_FIELD1).

The fix command

You should synchronize billing data with that of a registrar. Each domain will be checked on a monthly basis.

Registrar data have a higher priority than the billing data.

Note that the most important fields are domain status, expiration date.

Database

In order to write a module you will need the data from the tables domain, tld, domaincontact, registrar. The structure of domain, domaincontact can be found in the article Database structure.

The tld table - top level domains

  • id - id
  • name - name

The registrar table - top level domain

  • id - registrar id
  • name - registrar name
  • url - URL-address to the registrar's web site
  • username - user name
  • password - password

This description is not full.

Script example on PHP

Following is an example written in PHP for the Webnames registrar. Crete the /usr/local/ispmgr/sbin/drphpwebnames file with the content given below. After you have finished, the newly created registrar will be accessible from the billing interface.

#!/usr/bin/php
<?php
/*
Describe a registrar class.
*/
class WebNames {
      private $url;
      private $login;
      private $password;

//Class constructor. Save url, username, and password to gain access to the gateway.
       public function __construct($login, $password, $url) {
		$this->url = $url; 
		$this->login = $login;
		$this->password = $password;
	}

//Function to send queries to a registrar's server. It will return an answer line.
	public function invoke($request) {
		$arr = array("username" => $this->login, "password" => $this->password, "interface_revision" => "1","interface_lang" => "ru") + $request;
		$data = "";
		foreach ($arr as $key => $val) {
			$data .= "&".$key."=".$val;
		}
		$data = iconv("UTF-8" ,"cp1251", $data);
		Debug("Response: \n" .$data);
		$curl_client = curl_init($this->url);
		curl_setopt($curl_client, CURLOPT_SSL_VERIFYPEER, 1);
		curl_setopt($curl_client, CURLOPT_POST, 1);
		curl_setopt($curl_client, CURLOPT_RETURNTRANSFER, "1");
		curl_setopt($curl_client, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
		curl_setopt($curl_client, CURLOPT_POSTFIELDS, $data);
		$output = curl_exec($curl_client);
		Debug("Request: \n".$output);
		return $output;
      }

//Function to check a domain's status. If the domain is available, it will return true, if not, false
	public function Check($DomainName) {
		$data = array("thisPage" => "pispCheckDomain","domain_name" => $DomainName);
		$res = $this->invoke($data);
		if (substr_count($res, "is Available") != 0)
			return true;
		return false;
	}
//Function to register a domain that receives an array with domain data. 
	public function Register($domain) {
		$data = array("thisPage" => "pispRegistration", 
				"domain_name" => $domain["dname"],  
				"period" =>  GetPeriod($domain["pid"])) + 
				$this->GetContact($domain) +
				$this->GetNS($domain);
		return $this->invoke($data);
	}
 //Function to transfer a domain that receives an array with domain data. 
	public function Transfer($domain) {
		$data = array("thisPage" => "pispInitiateTransfer",
			"domain_name" => $domain["dname"],
			"period" => GetPeriod($domain["pid"]), 
			"authinfo" => $domain["auth_code"]) + 
			$this->GetContact($domain) +
			$this->GetNS($domain);

		return $this->invoke($data);
	}
 //Function to renew a domain that receives an array with domain data. 
	public function Renew($domain) {
		$data = array("thisPage"=>"pispRenewDomain",
			"domain_name" => $domain["dname"],
			"period" => GetPeriod($domain["pid"]));
		return $this->invoke($data);
	}
//Function to renew domain NS servers that receives an array with domain data. 
	public function UpdateNS($domain) {
		$data = array("thisPage" => "pispRedelegation",
			"domain_name" => $domain["dname"]) + $this->GetNS($domain);
		return $this->invoke($data);
	}
 //Function to define a domain status; used to define the length of registration and the status of a domain
	public function SetStatus($domain) {
		$data = array("thisPage" => "pispDomainInfo",
			"domain_name" => $domain["dname"]);
		return $this->invoke($data);
	}
 //Function returns an array with domain contact data. It is used during the process of domain's registration or transfer.
	function GetContact($domain) {
		$message = array();

		if ($domain["bill"] == "") {
			$query = mysql_query("select * from domaincontact where id =  ".$domain["owner"]);
			$owner = mysql_fetch_array($query);
			if ($owner["ctype"] == "company")
				$message += array(
					"org" => $owner["company"], 
					"org_r" => $owner["company_ru"], 
					"kpp" => $owner["kpp"], 
					"address_r" => $owner["la_state"]." ".$owner["la_postcode"]." ".$owner["la_city"]." ".$owner["la_address"]
				);
			else {
				$birthdate = substr($owner["birthdate"], 8, 2).".".substr($owner["birthdate"], 5, 2).".".substr($owner["birthdate"], 0, 4);

			$message += array(
					"person" => $owner["firstname"]." ".$owner["middlename"]." ".$owner["lastname"],
					"private_person" => $owner["private"],
					"person_r" => $owner["lastname_ru"]." ".$owner["firstname_ru"]." ".$owner["middlename_ru"],
					"birth_date" => $birthdate,
					"passport" => $owner["passport_series"]. " issued ".$owner["passport_org"]." ".substr($owner["passport_date"], 8, 2).".".substr($owner["passport_date"], 5, 2).".".substr($owner["passport_date"], 0, 4),
					"residence" => $owner["la_postcode"]." ".$owner["la_state"]." ".$owner["la_city"]." ".$owner["la_address"]
				);
			}

			$fax = ($owner["fax"] <> ""?"%2B".substr($owner["fax"],1):"");
			$message += array(
				"code" => $owner["inn"],
				"phone" => "%2B" . substr($owner["phone"],1),
				"fax" => $fax,
				"e_mail" => $owner["email"], 
				"country" => $this->GetCountry($owner["la_country"]),
				"p_addr" => $owner["pa_postcode"].", ".$owner["pa_state"].", ".$owner["pa_city"].", ".$owner["pa_address"].", ".$owner["pa_addressee"]
				);
		} else {
			$ctype = array("owner", "admin", "tech", "bill");
			for ($i = 0; $i < 4; $i++) {
				$query = mysql_query("select * from domaincontact where id =  ".$domain[$ctype[$i]]);
				$contact = mysql_fetch_array($query);
				$message += array(
					$ctype[$i]{0} . "_company" => $contact["company"],
					$ctype[$i]{0} . "_first_name" => $contact["firstname"],
					$ctype[$i]{0} . "_last_name" => $contact["lastname"],
					$ctype[$i]{0} . "_email" => $contact["email"],
					$ctype[$i]{0} . "_phone" => $this->GetPhone($contact["phone"]),
					$ctype[$i]{0} . "_fax" => $this->GetPhone($contact["fax"]),
					$ctype[$i]{0} . "_addr" => $contact["la_address"],
					$ctype[$i]{0} . "_city" => $contact["la_city"],
					$ctype[$i]{0} . "_state" => $contact["la_state"],
					$ctype[$i]{0} . "_postcode" => $contact["la_postcode"],
					$ctype[$i]{0} . "_country_code" => $this->GetCountry($contact["la_country"])
				);
			}
		}

		return $message ;
	}
//Function returns the data with domain NS servers. It is used during the process of registration, transfer, change of NS servers.
	function GetNS($domain) {
		$message = array();
		for ($i = 0; $i < 4; $i++) {
			$value = $domain["ns".$i];

			if ($value <> "") {
				if (substr_count($value, "/") <> 0) {
					$host = strtok($value, "/");
					$ip = strtok("");
					$message += array("ns".$i => $value,
							  "ns".$i."ip" => $ip);
				} else
					$message += array("ns".$i => $value);
			}
		}

		return $message;
	}
//Get a country's ISO code using ID from the database
	function GetCountry($id) {
		$query = mysql_query("select * from country where id =  ".$id);
		$contact = mysql_fetch_array($query);
		return $contact["iso2"];
	}
 //Returns a modified phone number, which is required for registering gTLD domains ans used in the GetContact function.
	function GetPhone($phone) {
		$pos = strpos($phone, " ");
		$res = "%2B" . substr($phone, 1, $pos-1).".";
		$pos2 = strpos($phone, " ", $pos+1);
		$res .= substr($phone, $pos+1, $pos2-$pos-1).substr($phone, $pos2+1);
		return $res;
	}
 //Function gets an answer line from the registrar $res, operation name, domain ID - theses data are required to return information to billing in case of error. It parses the line and returns an array with parameters keys => value.
	public function GetResultParam($DomainID, $operation, $res) {
		$result = array();
		$pos = strpos($res, ":");
		$stat = substr($res, 0, $pos);
		$res = substr($res, $pos+1);
		
		if ($stat <> "Success") {
			echo "\nerror\n";
			if ($stat == "Error") {
				SendResult($DomainID, $operation, $res);
				exit(1);
			}
			SendResult($DomainID, $operation, "The registrar has returned an error");
			exit(1);
		}

		$arr = explode(";", $res);
		foreach ($arr as $str) {
			if ($str == "")
				break;
			$str = trim($str);
			list($key, $value) = explode(" ", $str);
			$result += array($key => $value);
		}
		SendResult($DomainID, $operation, "OK");
		return $result;
	}
} // close the WebNames class

//Set a custom error handler to log them, rather than pass to standard output: 
set_error_handler("tmErrorHandler");

// Open the log file
$log_file = fopen("/usr/local/ispmgr/var/drphpwebnames.log", "a");
fwrite($log_file, "=======".date("M j H:i:s") . "[] " ."=======\n");

// Read the billing's configuration file to define parameters for connecting to the database

$arr = file ("/usr/local/ispmgr/etc/billmgr.conf"); 

$conf = array();
 //Parse the configuration file and write to the array
foreach ($arr as $str) {
	list($key, $value) = explode(" ", $str);
	if ($key <> "path" && $value != "") {
		$conf += array(chop($key) => chop($value));
	}
} 
//specify variables for connecting to the database
$dbhost = ($conf["DBHost"] == "") ? "localhost": $conf["DBHost"];// database host name  
$dbusername = ($conf["DBUser"] == ""? "root": $conf["DbUser"]); // database user 
$dbpass = $conf["DBPassword"]; 					// database password 
$dbname = ($conf["DBName"] == ""? "billmgr": $conf["DBName"]);  // database name 

// connection to the database
$dbconnect = @mysql_connect ($dbhost, $dbusername, $dbpass) or exit("Unable to connect to the database server!"); 
mysql_set_charset("utf8", $dbconnect);
mysql_select_db($dbname) or exit("Unable to connect to the $dbname! database");

//Log the received parameters
foreach($argv as $line_num => $line) {
	Debug($line_num.":".$line);
}

//Output script's information, if parameters were passed incorrectly.
if ($argc<2) {
	usage();
	exit(1);
}
//Define variables.
$domain_id = "";
$registrar_id = "";
$DomainName = "";
$domain = array();
$registrar = array();
$url="";
$username="XXXXXXXX";
$password="XXXXXXXX";

//Interpret input parameters and specify values for corresponding variables. If parameters are not enough, output usage()
//the first parameter must be an operation name
if (($argv[1] == "register" || $argv[1] == "renew" || $argv[1] == "transfer") && ($argv[2] != "")) {
	$domain_id = $argv[2]; 
}
elseif (($argv[1] == "set" || $argv[1] == "update") && ($argv[3] != "")) {
	$domain_id = $argv[3];
}
elseif ($argv[1] == "check" && $argv[2] != "" && $argv[3] != "") {
	$DomainName = $argv[2];
	$registrar_id = $argv[3];
}
else {
	usage();
	exit();
}

//If the domain's ID is not blank, we will receive information about the database. Use the same procedure for a registrar, his ID can be taken from the registrar field of the domain table
if ($domain_id != "") {
	global $domain, $DomainName;

	$query = mysql_query("SELECT * FROM domain where pid = ".$domain_id);
	$domain = mysql_fetch_array($query);
	$query = mysql_query("SELECT * FROM tld where id = ".$domain["tld"]);
	$tld = mysql_fetch_array($query); // get a top level domain from the tld table. 

	$DomainName = $domain["name"] . "." . $tld["name"]; //Define a fully qualified domain name
	$domain += array("dname" => $DomainName);

	Debug("domainname = $DomainName");
	$registrar_id = $domain["registrar"];
}
//If a registrar is not blank, get information from the base or return an error.
if ($registrar_id != "") {
	global $url, $username, $password, $registrar;
	$query = mysql_query("SELECT * FROM registrar where id = '".$registrar_id."'");
	$registrar = mysql_fetch_array($query) or die('Invalid query: ' . mysql_error());
	if (!$registrar) {
		Debug("Registrar not found");
		exit(1);
	}

	$url = ($registrar["url"] != ""? $registrar["url"]:$url);
	$username = $registrar["username"];
	$password = $registrar["password"];

}
else {
	Debug("Registrar empty");
	exit(1);
}
//Create an object of the WebNames class. The input parameters are those received from the registrar
$regmodule = new WebNames($username, $password, $url);

//Define an operation
switch($argv[1]) {
	case "check":
//check availability
		$res = $regmodule->Check($DomainName);
		if ($res == false)
			exit(1); //Finish with code 1

		break;
	
	case "register":
//Register a domain
		$res = $regmodule->Register($domain);
//Interpret the result
		$regmodule->GetResultParam($domain_id, $argv[1], $res);
		break;

	case "transfer":
//Transfer a domain
		$res = $regmodule->Transfer($domain);
		$regmodule->GetResultParam($domain_id, $argv[1], $res);
		break;

	case "renew":
//Renew a domain
		$res = $regmodule->Transfer($domain);
		$regmodule->GetResultParam($domain_id, $argv[1], $res);
		break;

	case "set":
//Note! We considered the example with only one domain' ID transferred, though several domans' Ids with no spaces may be transferred as wel

//Get a domain's status
		$res = $regmodule->SetStatus($domain);
//Get an array of the registrar response parameters
		$resparam = $regmodule->GetResultParam($domain_id, $argv[1], $res);
//Convert the status of Webnames into that of the billing
		$status = $resparam["Status"];
		if ($status{0} == 'Y')
			$status = "2";
		else if ($status{0} == 'N' || $status{0} == 'B')
			$status = "3";
		else if ($status{0} == 'T')
			$status = "6";
//Return these data to the billing. Call the domain.set function
		exec("/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml  domain.set item=" . $domain_id . " status=" . $status . " expire=".$resparam["ExpirationDate"]);

		break;

	case "update":
//Renew NS servers
		$res = $regmodule->UpdateNS($domain);
		$regmodule->GetResultParam($domain_id, $argv[1], $res);
		break;
}

//LongTask does not start the check function. For all of the other functions specify that everything has finished successfully
if ($argv[1] != "check")
	exec("/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml longtask.finish elid=". getenv("MGR_LT_PID") ." status=ok");

exit(0);

//Function to log the received line
function Debug($data) {
	global $log_file;
	fwrite($log_file, date("M j H:i:s") . " " . $data."\n");
} 

//Error handler
function tmErrorHandler($errno, $errstr, $errfile, $errline) {
	global $log_file;
	fwrite($log_file, date("M j H:i:s") . " " . "Error [" . $errno . "] ErrMsg: " . $errstr . ". In file: " . $errfile . ". In line: " . $errline . "\n");
   return true;
}

//Function returns domain operation's results to the billing. Used in GetResultParam. 
//domain.result - places a banner and sends a message in case of error
function SendResult($DomainID, $operation, $res) {
	if ($res == "OK") {
		exec("/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml  domain.result item=".$DomainID." operation=".$operation." state=ok");
	}
	else {
//note: if you can unparse an answer line from the registrar and find out the filed where a user made a mistake, they can be transferred via the errorparams parameter. For example 'errorparams=passport,email,phone'. contactid - ID of the contact where mistakes are detected

		exec("/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml  domain.result item=".$DomainID." operation=".$operation." state=error message=\"".$res."\" errorparams=  contactid=");
	}
}
//Define the length of registration. It can be specified in months, days. 
function GetPeriod($domain_id) {
	$query = mysql_query("select p.plength as plength, p.ptype as type from item i left join priceperiod p on i.period = p.id where i.id =  ".$domain_id);
	$entry = mysql_fetch_array($query);
	$period = $entry["plength"];
	switch ($entry["type"]) {
	case 1: // month
		$period = $period / 12;
		break;
	case 2: // day
		$period = $period / 365;
		break;
	case 3: // year
		break;
	default:
		throw Exception('invalid period');
	}

	return $period;
}


//Function to output information on script usage: 
function usage() {
	print(
	    "Commands:                                  \n" .
	    "  check <domain name> <registrar_id>       \n" .
	    "  register <domain_id>                     \n" .
	    "  renew <domain_id>                        \n" .
	    "  transfer <domain_id>                     \n" . 
	    "  set status <domain_id[,domain_id[...]]>  \n" .
	    "  update ns <domain_id>                    \n"
	    );
}

?>
Was this helpful? Yes | No
Views
Personal tools