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.


Script features

check <domain name> <registrar_id> - domain check to see if a domain is available. 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 the billing and registrar's data.

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

balance <registrar_id> - verify the current balance of account.

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"?>


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


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

The ctune command

It changes the domain contact edit form. If this function is not supported, the script should return 1 upon execution.

The whole XML of the form where contact data should be specified is sent through the sdtin to the module. You should receive the data from the registrar and type them into the form.

Once the XML is processed, it should be passed to the stdout.

The doptune command

It enables to specify data in the domain name order form. The module receives to the stdin the XML containing all the data and changes them if necessary.

Once the XML is processed, it should be passed to the stdout.

The fix command

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

Registrar data have a higher priority than the billing data.

The most important fields are domain's status, expiration date.

Once the data have been successfully synchronized, execute the command:

update domain set datecheck = now() where pid = ID_DOMAIN;

The balance command

If the registrar's API enables to get the current balance of account, it can be saved in the billing system:

update registrar set balance = 'BALANCE' where id = REGISTRAR_ID

You can submit a trouble ticket, if the current balance is less than a specified value:

/usr/local/ispmgr/sbin/mgrctl -m billmgr -o xml tickets.edit sok=ok responsible=ADMINGROUP_ID subject=SUBJECT text=TEXT

The fixall command

This command can be executed to get a list of all domain names of the selected registrar, and performs the following operations:

- Changes the domain status into "deleted" once the registrar deletes the domains.

- changes ns-servers for the domains once the registrar changes them.

The registration module must send a response in the following format:

<domain_name> <tld> <billmgr_status> <admin> <bill> <tech> <ns1,ns2,ns3,...> \n


testdomain ru 5 320/NIC-REG 320/NIC-REG 320/NIC-REG,,

The registration module must execute this command. It should return an empty string.

Domain import

To make the registrar module support the import procedure, send 0 as a response to checkimport (the operation is completed successfully), otherwise the system will warn you that the import is not supported.

The following commands must be executed and sent to the module that processes domain registration:

  • formtuneimport - import domains with user privileges. You should send to stdout names of parameters that clients should specify for importing domains. Parameters should be separated by the ; symbol.
  • import - start domain import. Possible parameters:
drmodule import registrar -paccount=1 -palldomain=yes -pproject=1
    • registrar - registrar id in the system
    • -paccount - administrator or client id
    • -palldomain - yes if all domain names should be imported.
    • -pdomain - domain name if you want to import only one domain.
    • -pproject - project id, should be 1 for BILLmanager Standard and Advanced

If you import with client privileges, all the parameters sent in formtuneimport must be sent.

Functions that enable to add contacts and domains in the panel

Once the data are received from the registrar, they are sent and saved in BILLmanager with the domain.import function. Domain contacts are saved first, followed by domain names with contact ids.

Example of the function parameters for saving domain contacts:

func=domain.import& // Function that enables to add contacts and domains
type=contact& // Type of the object you want to add 
operation=new& // Operation type. Possible value is new
ctype=company|person& // Contact type. Possible values: company, person or generic
name=& // Contact name
company=& // Contact name in English
firstname=& // Contact name in English 
middlename=& // Middle name in English 
lastname=& // Last name in English 
company_ru=& // Company name in Russian 
firstname_ru=& // Name of the contact person in Russian 
middlename_ru=& // Middle name in Russian 
lastname_ru=& // Last name in Russian 
email=& // Email address
phone=& // Phone number 
fax=& // Fax
birthdate=& // Date of birth of the contact person 
passport=& // Passport or other document number 
inn=& // INN 
kpp=& // KPP 
private=& // Hide personal data 
la_country=& // Address. Country id  
la_state=& // Address.  State or region 
la_postcode=& // Address. ZIP code 
la_city=& // Address. City 
la_address=& // Address. Street, building 
Pa_country=& // Postal address.
pa_state=& // Postal  address.  State or region 
pa_postcode=& //  Postal address. ZIP code 
pa_city=& // Postal address. City 
pa_address=& // Postal address. Street, building 
pa_addressee=& // Postal address. Addressee 
remote_id=& // Contact id in the registrar system 
registrar=& // Registrar id 
moreparam=& // Any additional parameters separated by the   //  symbols. Those parameters should be specified as parameter_name:parameter_value
account= // Client ID if the import is performed with user privileges.  

The system will return the client id specified in BILLmanager that should be used when adding a domain.

Example of the function parameters for saving a domain:

func=domain.import& // Add an object
type=domain& // Type of the object you want to add 
operation=new& // Creation of a new object 
domain=& // Domain name and its zone
ns0=& // Name server 
ns1=& // Name server 
ns2=& // Name server 
ns3=& // Name server 
subjnic=& // Contact id from the BILLmanager database. It corresponds to client data in the registrar system.  It should be specified only if an account is registered in the registrar system.
owner=& // Contact id from the BILLmanager database. Domain owner 
admin=& // Contact id from the BILLmanager database. Administrative contact 
bill=& // Contact id from the BILLmanager database. Billing contact 
tech=& // Contact id from the BILLmanager database. Technical contact  
expire=& // Expiration date 
status=& // Domain numeric status 
registrar=& // Registrar id 
account= // Client id. If the domain is specified, it will be immediately added to the client account.

Finish import

Once all the operation required for domain import are completed, send the import results to BILLmanager.

If the operation is completed successfully, the domain.result function containing the account parameters (id of the account who is importing the domains, operation=import - operation type) is called. If one or several errors occurred during the import process, the state=error and parameter and message (containing any information related to the error) parameters will be sent as well.


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.

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) +
		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) +

		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);
			SendResult($DomainID, $operation, "The registrar has returned an error");

		$arr = explode(";", $res);
		foreach ($arr as $str) {
			if ($str == "")
			$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: 

// 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) {

//Output script's information, if parameters were passed incorrectly.
if ($argc<2) {
//Define variables.
$domain_id = "";
$registrar_id = "";
$DomainName = "";
$domain = array();
$registrar = array();

//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 {

//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");

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

else {
	Debug("Registrar empty");
//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

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

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

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

	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"]);


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

//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");


//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 = where =  ".$domain_id);
	$entry = mysql_fetch_array($query);
	$period = $entry["plength"];
	switch ($entry["type"]) {
	case 1: // month
		$period = $period / 12;
	case 2: // day
		$period = $period / 365;
	case 3: // year
		throw Exception('invalid period');

	return $period;

//Function to output information on script usage: 
function usage() {
	    "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"


Useful functions

  • registrarparam.set - enables to save additional information about objects associated with various operations with domain names. This parameter is specified for each registrar:
    1. tblName - the name of the table containing main information about an object.
    2. registrar - the id of a corresponding registrar.
    3. tblKey - the id of an object in the tblName table.
    4. param - parameter's name.
    5. value - parameter's value.

It is often used to save information about domain's contact. For example, the contact's id and password from registrar; parameter name for is is normally remote_id_registrar_code; for password - remote_pass_registrar_code.

Was this helpful? Yes | No
Personal tools