Creating additional modules for domain registrars
From ISPWiki
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"
);
}
?>
