<?php

	$help = "Available parameters:
-f: (required) name of file containing domains to transfer
-d: (optional) how to handle DNS zones; allowed values:
    0: (default) do not create DNS zones in Openprovider; use existing
       nameservers
    1: create DNS zones in Openprovider based on TransIP DNS records;
       put the domain on the Openprovider nameservers after transfer;
       existing zones will not be overwritten
    overwrite: create DNS zones, overwrite any existing zones
-h: (optional) how to handle contacts; allowed values:
    0: (default) the script checks if another TransIP contact with the
       same data was processed before; if so, re-use that contact instead
       of creating a new one
    1: create a new Openprovider handle for every domain contact, even if
       the data is similar to a previously processed contact
-v: (optional) whether or not to show extended information during runtime;
    allowed values:
    0: (default) don't show additional ouput
    1: show additional ouput
-t: (optional) whether to run in test mode or not; allowed values:
    0: run live
    1: (default) run in test mode / read-only; no contacts or zones will
       be created, no domains will be transferred

Examples:
  Run in test mode, full debugging output:
    php transfer-from-transip.php -f domains.txt -v1 -t1

  Run in live mode: transfer domains while create DNS zones (but do not
  overwrite existing ones), show limited output:
    php transfer-from-transip.php -f domains.txt -t0 -d1

Full documentation is available at https://openprovider.zendesk.com/hc/en-us/articles/360019994414
";
	use Transip\Api\Library\TransipAPI;
	// Openprovider API credentials
	$auth = array('username' => '', 'hash' => '');

	// TransIP and Openprovider API
	require_once(__DIR__ . '/../vendor/autoload.php');
//	require_once('Transip/DomainService.php');
	require_once('API.php');
	$api = new OP_API ('https://api.openprovider.eu');

	// Your login name on the TransIP website.
	$login = '';

	// If the generated token should only be usable by whitelisted IP addresses in your Controlpanel
	$generateWhitelistOnlyTokens = true;

	// One of your private keys; these can be requested via your Controlpanel
	$privateKey = <<< EOD
		-----BEGIN PRIVATE KEY-----
		
		-----END PRIVATE KEY-----
	EOD;

	$apiT = new TransipAPI(
		$login,
		$privateKey,
		$generateWhitelistOnlyTokens
	);

	$results = [];
	$fileResult = fopen("result.txt", 'w+');
	$resultsTransfer = [];
	$fileResultTransfer = fopen("result_transfer.txt", 'w+');


	// Read runtime parameters
	$opts = getopt('f:d:t:h:v:', array('help'));
	$domainFile      = $opts['f']; // filename (required)
	$withDns         = $opts['d']; // 0, 1 or overwrite (default = 0)
	$forceNewHandles = $opts['h']; // 0 or 1 (default = 0)
	$verbose         = $opts['v']; // 0 or 1 (default = 0)
	$testMode        = $opts['t']; // 0 or 1 (default = 1)

	if (isset($opts['help'])) {
		echo $help;
		exit;
	}


	// Get list with domains to transfer
	if (file_exists($domainFile)) {
		$domains = file($domainFile, FILE_IGNORE_NEW_LINES);
	}
	else {
		die("Cannot find domain list; file [$f] does not exist\n");
	}

	// Check other parameters
	if (!in_array($withDns, array(0, 1, 'overwrite'))) {
		$withDns = 0;
	}
	if (!in_array($testMode, array(0, 1))) {
		$testMode = 1;
	}
	if (!in_array($forceNewHandles, array(0, 1))) {
		$forceNewHandles = 0;
	}
	if (!in_array($verbose, array(0, 1))) {
		$verbose = 0;
	}

	// Keep track of handles that were already created
	$handleRelations = array();
	if (!$forceNewHandles) {
		if (file_exists('transip-handle-relations.dat')) {
			if ($f = fopen('transip-handle-relations.dat', 'r')) {
				while (!feof($f)) {
					list($md5, $opHandle) = fgetcsv($f);
					$handleRelations[$md5] = $opHandle;
				}
				fclose($f);
			}
			else {
				die("Cannot open file transip-handle-relations.dat\n");
			}
		}
		$fHandles = fopen('transip-handle-relations.dat', 'a');
	}

	foreach ($domains as $dom) {
		$continue = true;

		if ($verbose) {
			echo "Starting domain $dom\n";
			echo "Retrieve data for domain $dom from TransIP API\n";
		}
		try {
			$domain = $apiT->domains()->getByName($dom);
			if ($verbose) {
				echo "TransIP returned the following data for domain $dom:\n";
				print_r($domain);
			}
		}
		catch(SoapFault $e) {
			// It is possible that an error occurs when connecting to the TransIP Soap API,
			// those errors will be thrown as a SoapFault exception.
			echo "@@ERROR: An error occurred while querying the TransIP API for domain [$dom]: ".htmlspecialchars($e->getMessage())."\n";
			continue;
		}

		$args = array();

		// Nameservers
		if ($verbose) {
			echo "Preparing nameservers for domain [$dom]\n";
		}
		$dnsEntries = $apiT->domainDns()->getByDomainName($dom);
		if ($withDns) {
			if (!$dnsEntries) {
				echo "@@ERROR: no DNS entries found for import in Openprovider for $dom; skipping this domain\n";
				array_push($results, $dom."   @@ERROR: no DNS entries found for import in Openprovider for $dom; skipping this domain\n");
				continue;
			}
			if (createOpenproviderZone($dom, $dnsEntries)) {
				$args['nsGroup'] = 'dns-openprovider';
			}
			else {
				echo "@@ERROR: while creating DNS zone for $dom; skipping this domain\n";
				continue;
			}
		}
		else {
			$nameservers = $apiT->domainNameserver()->getByDomainName($dom);
			if (isset($nameservers)) {
				foreach ($nameservers as $ns) {
					$args['nameServers'][] = array(
						'name' => $ns->getHostname(),
						'ip'   => (isset($ns->ipv4) ? $ns->getIpv4() : NULL),
						'ip6'  => (isset($ns->ipv6) ? $ns->getIpv6() : NULL),
					);
				}
			}
		}

		// Contacts (handles)
		if ($verbose) {
			echo "Preparing contact handles for domain [$dom]\n";
		}
		$contacts = $apiT->domainContact()->getByDomainName($dom);
		foreach ($contacts as $contact) {
			$type = '';
			$contactType = $contact->getType();
			switch ($contactType) {
				case 'registrant'     : $type = 'owner'; break;
				case 'administrative' : $type = 'admin'; break;
				case 'technical'      : $type = 'tech'; break;
				default               : echo "@@WARNING: invalid contact type ".$contactType." for domain [$dom]; skipping\n"; break;
			}
			unset($contactType);

			$concat = $contact->getType() .
				$contact->getFirstName() .
				$contact->getLastName() .
				$contact->getNumber() .
				$contact->getStreet() .
				$contact->getPostalCode() .
				$contact->getCompanyName() .
				$contact->getCountry() .
				$contact->getCompanyKvk() .
				$contact->getCity() .
				$contact->getEmail();
			$hash = md5($concat);
			if (!$forceNewHandles && isset($handleRelations[$hash])) {
				$opHandle = $handleRelations[$hash];
			}
			else {
				if ($opHandle = createOpenproviderContact($contact)) {
					if (!$testMode) {
						fwrite($fHandles, $hash.','.$opHandle."\n");
						$handleRelations[$hash] = $opHandle;
					}
				}
				else {
					echo "@@ERROR while creating Openprovider contact for $type of $dom\n";
					array_push($resultsTransfer, $dom . "  @@ERROR while creating Openprovider contact for $type of $dom\n");
					$continue = false;
					continue 2;
				}
			}
			$args[$type.'Handle'] = $opHandle;
		}

		// Authcode
		$args['authCode'] = $domain->getAuthCode();

		// Unlock domain, if required
		if ($verbose) {
			echo "Unlocking domain [$dom] (if applicable)\n";
		}
		if ($domain->isTransferLocked()) {
			if ($testMode) {
				echo "TEST MODE: skipping unlocking domain\n";
			}
			else {
				try {
					$domainObj = $apiT->domains()->getByName($dom);
					$domainObj->setIsTransferLocked(false);
					$res = $apiT->domains()->update($domainObj);
				}
				catch(SoapFault $e)
				{
					// It is possible that an error occurs when connecting to the TransIP Soap API,
					// those errors will be thrown as a SoapFault exception.
					echo "@@ERROR: An error occurred while unlocking domain [$dom]: " . htmlspecialchars($e->getMessage())."\n";
					array_push($resultsTransfer, $dom . "  @@ERROR: An error occurred while unlocking domain [$dom]: " . htmlspecialchars($e->getMessage())."\n");
				}
			}
		}

		list($d, $e) = explode('.', $dom, 2);
		$args['domain'] = array(
			'name' => $d,
			'extension' => $e,
		);
		$args['period'] = 1;

		if ($verbose) {
			echo "Start transfer for domain [$dom] in Openprovider with the following data array:\n";
			print_r($args);
		}
		if ($testMode) {
			echo "TEST MODE: skip domain transfer for [$dom]\n";
		}
		else {
			$request = new OP_Request;
			$request->setCommand('transferDomainRequest')
				->setAuth($auth)
				->setArgs($args);
			$reply = $api->process($request);
			if ($reply->getFaultCode() != 0) {
				echo "@@ERROR on domain transfer [$dom]: ".$reply->getFaultCode().' - '.$reply->getFaultString()."\n";
				array_push($resultsTransfer, $dom . "  @@ERROR on domain transfer [$dom]: ".$reply->getFaultCode().' - '.$reply->getFaultString()."\n");
				if ($verbose) {
					echo "Full data array:\n";
					print_r($args);
				}
			}
			else {
				echo "Transfer for $dom successfully requested\n";
				array_push($resultsTransfer, $dom . "  Transfer for $dom successfully requested\n");
			}
		}
	}
	foreach ($results as $result) {
		fwrite($fileResult, $result);
	}

	foreach ($resultsTransfer as $result) {
		fwrite($fileResultTransfer, $result);
	}

	fclose($fileResultTransfer);
	fclose($fileResult);
	fclose($fHandles);

	function createOpenproviderZone($domain, $dnsEntries) {
		global $api, $auth;
		global $testMode, $withDns, $verbose;
		global $results;

		list($d, $e) = explode('.', $domain, 2);

		// Check if a zone already exists
		$request = new OP_Request;
		$request->setCommand('retrieveZoneDnsRequest')
			->setAuth($auth)
			->setArgs(array(
				'name' => $domain,
				'withRecords' => false,
				'withHistory' => false
			));
		$reply = $api->process($request);
		$response = $reply->getValue();
		if ($response) {
			if ($withDns == 'overwrite') {
				if ($testMode) {
					echo "@@WARNING: TEST MODE: Zone for [$domain] already exists, skip removing of existing zone\n";
				}
				else {
					echo "@@WARNING: Zone for [$domain] already exists; removing existing zone\n";

					$request = new OP_Request;
					$request->setCommand('deleteZoneDnsRequest')
						->setAuth($auth)
						->setArgs(array(
							'domain' => array(
								'name' => $d,
								'extension' => $e
							),
						));
					$reply = $api->process($request);
					$response = $reply->getValue();
				}
			}
			else {
				echo "@@WARNING: Zone for [$domain] already exists; skipping import\n";
				array_push($results, $domain."   @@WARNING: Zone for [$domain] already exists; skipping import\n" );
			}
		}

		$records = array();
		foreach ($dnsEntries as $record) {
			if (in_array($record->getType(), array('NS', 'SOA'))) {
				continue;
			}

			if (in_array($record->getType(), array('MX', 'SRV'))) {
				list($prio, $value) = explode(' ', $record->getContent(), 2);
			}
			else {
				$prio = NULL;
				$value = $record->getContent();
			}

			$records[] = array(
				'type'  => $record->getType(),
				'name'  => ($record->getName() == '@' ? '' : $record->getName()),
				'value' => ($value == '@' ? $domain : $value),
				'prio'  => $prio,
				'ttl'   => ($record->getExpire() < 600 ? 600 : $record->getExpire()),
			);
		}

		if ($testMode) {
			echo "TEST MODE - skip DNS zone creation for $domain\n";
			if ($verbose) {
				echo "Zone contents:\n";
				print_r($records);
			}
			return true;
		}
		else {
			$request = new OP_Request;
			$request->setCommand('createZoneDnsRequest')
				->setAuth($auth)
				->setArgs(array(
					'domain' => array(
						'name' => $d,
						'extension' => $e,
					),
					'type' => 'master',
					'records' => $records,
				));
			$reply = $api->process($request);
			if ($reply->getFaultCode() != 0) {
				echo "@@ERROR on DNS zone creation for [$domain]: ".$reply->getFaultCode().' - '.$reply->getFaultString()."; full data array:\n";
				array_push($results, $domain."   @@ERROR on DNS zone creation for [$domain]: ".$reply->getFaultCode().' - '.$reply->getFaultString()."; full data array:\n" );
				print_r($records);
				return false;
			}
			else {
				if ($verbose) {
					echo "DNS zone for $domain successfully created with the following records:\n";
					array_push($results, $domain, "   DNS zone for $domain successfully created with the following records:\n");
					print_r($records);
				}
				return true;
			}
		}
	}

	function createOpenproviderContact($contact) {
		global $api, $auth;
		global $testMode, $verbose;
		global $resultsTransfer;
		// Split telephone number
		// Country code separated?
		$matches = array();
		if (preg_match('/^(\+?\d+)[ \-\.](.*)$/', $contact->getPhoneNumber(), $matches)) {
			$tel1 = (substr($matches[1], 0, 1) == '+' ? '' : '+').$matches[1];
			$rest = preg_replace('/[^\d]/', '', $matches[2]);
		}
		// Else consider first 2 numbers the country code
		else {
			$tel = preg_replace('/[^\d]/', '', $contact->getPhoneNumber());
			$tel1 = '+'.substr($tel, 0, 2);
			$rest = substr($tel, 2);
		}
		$tel2 = substr($rest, 0, 2);
    	$tel3 = substr($rest, 2);

		// Split address number
		$matches = array();
		if (preg_match('/^\d+$/', $contact->getNumber())) {
			$number = $contact->getNumber();
			$suffix = NULL;
		}
		else if (preg_match('/^(\d+)([^\d].*)$/', $contact->getNumber(), $matches)) {
			$number = $matches[1];
			$suffix = $matches[2];
		}
		else {
			$number = 1;
			$suffix = $contact->getNumber();
		}

		$args = array(
			'companyName' => $contact->getCompanyName(),
			'name' => array(
				'firstName' => $contact->getFirstName(),
//				'prefix' => $contact->middleName,
				'lastName' => $contact->getLastName(),
			),
			'phone' => array(
				'countryCode' => $tel1,
				'areaCode' => $tel2,
				'subscriberNumber' => $tel3,
			),
			'address' => array(
				 'street' => $contact->getStreet(),
				 'number' => $number,
				 'suffix' => $suffix,
				 'zipcode' => $contact->getPostalCode(),
				 'city' => $contact->getCity(),
				 'country' => strtoupper($contact->getCountry()),
			),
			'email' => $contact->getEmail(),
			'additionalData' => ($contact->getCompanyKvk() ? array('companyRegistrationNumber' => $contact->getCompanyKvk()) : NULL),
		);

		if ($testMode) {
			echo "TEST MODE - skip handle creation\n";
			if ($verbose) {
				echo "Handle details are the following:\n";
				print_r($args);
			}
			return true;
		}
		else {
			$request = new OP_Request;
			$request->setCommand('createCustomerRequest')
				->setAuth($auth)
				->setArgs($args);
			$reply = $api->process($request);
			if ($reply->getFaultCode() != 0) {
				echo "@@ERROR on contact creation for [$domain]: ".$reply->getFaultCode().' - '.$reply->getFaultString()."; full data array:\n";
				array_push($resultsTransfer, $domain .  "  @@ERROR on contact creation for [$domain]: ".$reply->getFaultCode().' - '.$reply->getFaultString()."; full data array:\n");
				print_r($args);
				return false;
			}
			else {
				$response = $reply->getValue();
				if ($verbose) {
					echo "Successfully created handle ".$response['handle']."\n";
				}
				return $response['handle'];
			}
		}
	}

?>
