diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..25dac52 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +.gitignore export-ignore +*.md export-ignore +LICENSE export-ignore diff --git a/README-AR.md b/README-AR.md new file mode 100644 index 0000000..8af7bef --- /dev/null +++ b/README-AR.md @@ -0,0 +1,39 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ + +# وحدة Domainnameapi + +هذه الوحدة هي تكامل لـ 'domainnameapi.com' تم تطويرها لـ Blesta. (التحديث 15 أغسطس 2024) + +## المتطلبات + +- يجب أن يكون لديك إصدار Blesta 5.3 وما فوق. +- يجب أن يكون إصدار PHP 7.4 وما فوق. +- يجب تفعيل ملحق PHP Soap. + +## التثبيت + +انسخ الملفات التي قمت بتحميلها إلى blesta/components/modules/domainnameapi/ +ثم قم بتفعيلها من خلال القائمة إعدادات > وحدات. + + +## الرموز المرجعية ورموز الأخطاء مع الشروحات + +| الكود | الشرح | التفاصيل | +|-------|-------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | تمت العملية بنجاح | +| 1001 | Command completed successfully; action pending. | تمت العملية بنجاح؛ إلا أن هناك إجراء قيد الانتظار. | +| 2003 | Required parameter missing | معلومة مفقودة مطلوبة. مثال؛ عدم تقديم معلومات الهاتف في بيانات الاتصال. | +| 2105 | Object is not eligible for renewal | الكائن غير مؤهل للتجديد، تم قفل عمليات التحديث عليه. يجب ألا يكون حالة الكائن "clientupdateprohibited". قد تنجم هذه المشكلة عن حالات أخرى. | +| 2200 | Authentication error | خطأ في المصادقة، الرمز الأماني غير صحيح أو النطاق مسجل في شركة تسجيل أخرى. | +| 2302 | Object exists | اسم النطاق أو معلومات خادم الأسماء موجودة بالفعل في قاعدة البيانات. لا يمكن تسجيلها. | +| 2303 | Object does not exist | اسم النطاق أو معلومات خادم الأسماء غير موجودة في قاعدة البيانات. يجب إنشاء تسجيل جديد. | +| 2304 | Object status prohibits operation | حالة النطاق تمنع العملية. النطاق غير مؤهل للتحديث، تم قفل عمليات التحديث عليه. حالة النطاق لا يجب أن تكون "clientupdateprohibited". قد تنجم هذه المشكلة عن حالات أخرى. | diff --git a/README-AZ.md b/README-AZ.md new file mode 100644 index 0000000..9d78a63 --- /dev/null +++ b/README-AZ.md @@ -0,0 +1,38 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ +# Domainnameapi Modulu + +Bu modul, Blesta üçün inkişaf etdirilmiş 'domainnameapi.com' inteqrasiyasıdır. (Yenilənmə 15 Avqust 2024) + +## Tələblər + +- Blesta'nın 5.3 və yuxarı versiyası tələb olunur. +- PHP-nin 7.4 və yuxarı versiyası tələb olunur. +- PHP Soap əlavəsi aktivləşdirilməlidir. + +## Quraşdırma + +Yüklədiyiniz faylları blesta/components/modules/domainnameapi/ yerləşdirin. +Parametrlər > Modullar menyusundan aktivləşdirin. + + +## Qayıdış və Səhv Kodları, Təfərrüatları + +| Kod | Açıqlama | Təfərrüat | +|------|-------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | Əmr uğurla icra edildi | +| 1001 | Command completed successfully; action pending. | Əmr uğurla icra edildi; amal gözlənilir | +| 2003 | Required parameter missing | Tələb olunan parametr yoxdur. Məsələn; Əlaqə məlumatlarında telefon qeyd edilməməsi | +| 2105 | Object is not eligible for renewal | Nəqliyyat yenilənməyə layiq deyil, yeniləmə əməliyyatları qapalıdır. "clientupdateprohibited" vəziyyət durumu olmamalıdır. Digər vəziyyət durumlarından səbəb götürə bilər | +| 2200 | Authentication error | Doğrulama səhvi, təhlükəsizlik kodu yanlışdır və ya domen başqa bir qeydiyyat şirkətində mövcuddur | +| 2302 | Object exists | Domen adı və ya ad server məlumatları verilənlər bazasında mövcuddur. Qeydiyyat edilə bilməz | +| 2303 | Object does not exist | Domen adı və ya ad server məlumatları verilənlər bazasında mövcud deyil. Yeni qeydiyyat yaradılmalıdır | +| 2304 | Object status prohibits operation | Domen vəziyyəti əməliyyat üçün maneə törədir, yeniləmə əməliyyatları qapalıdır. Vəziyyət "clientupdateprohibited" olmamalıdır. Digər vəziyyət durumlarından səbəb götürə bilər | diff --git a/README-DE.md b/README-DE.md new file mode 100644 index 0000000..fc68e4b --- /dev/null +++ b/README-DE.md @@ -0,0 +1,39 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ +# Domainnameapi Modul + +Dieses Modul ist eine Integration von 'domainnameapi.com', die für Blesta entwickelt wurde. (Aktualisierung 15. August 2024) + +## Anforderungen + +- Blesta Version 5.3 oder höher ist erforderlich. +- PHP Version 7.4 oder höher ist erforderlich. +- Die PHP Soap-Erweiterung muss aktiviert sein. + +## Installation + +Kopieren Sie die heruntergeladenen Dateien nach blesta/components/modules/domainnameapi/. +Aktivieren Sie es über das Menü Einstellungen > Module. + + + +## Rückgabe- und Fehlercodes mit Erklärungen + +| Code | Erklärung | Details | +|------|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | Befehl erfolgreich ausgeführt | +| 1001 | Command completed successfully; action pending. | Befehl erfolgreich ausgeführt; Aktion ausstehend | +| 2003 | Required parameter missing | Erforderlicher Parameter fehlt. Zum Beispiel: Telefonnummer in den Kontaktdaten fehlt | +| 2105 | Object is not eligible for renewal | Objekt ist nicht zur Verlängerung berechtigt, Update-Aktionen gesperrt. Der Status darf nicht "clientupdateprohibited" sein. Möglicherweise aufgrund anderer Statusbedingungen. | +| 2200 | Authentication error | Authentifizierungsfehler, Berechtigungscode ungültig oder Domain ist bei einem anderen Registrar registriert. | +| 2302 | Object exists | Domänenname oder Nameserver-Informationen sind bereits in der Datenbank vorhanden. Kann nicht registriert werden. | +| 2303 | Object does not exist | Domänenname oder Nameserver-Informationen sind in der Datenbank nicht vorhanden. Neue Registrierung erforderlich. | +| 2304 | Object status prohibits operation | Objektstatus verbietet die Aktion, Updates gesperrt. Der Status darf nicht "clientupdateprohibited" sein. Möglicherweise aufgrund anderer Statusbedingungen. | diff --git a/README-EN.md b/README-EN.md new file mode 100644 index 0000000..35733b2 --- /dev/null +++ b/README-EN.md @@ -0,0 +1,40 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ +# Domainnameapi Module + +This module is an integration of 'domainnameapi.com' developed for Blesta. (Updated on August 15, 2024) + +## Requirements + +- Blesta version 5.3 or above is required. +- PHP version 7.4 or above is required. +- PHP Soap extension must be enabled. + +## Installation + +Copy the downloaded files to blesta/components/modules/domainnameapi/. +Activate it from the Settings > Modules menu. + + + +## Return and Error Codes with Explanations + +| Code | Explanation | Details | +|------|-------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | Command completed successfully | +| 1001 | Command completed successfully; action pending. | Command completed successfully; action pending | +| 2003 | Required parameter missing | Required parameter missing. For example: Missing phone number in contact information | +| 2105 | Object is not eligible for renewal | Object is not eligible for renewal, update actions locked. Status must not be "clientupdateprohibited". May be due to other status conditions. | +| 2200 | Authentication error | Authentication error, authorization code incorrect, or domain is registered with another registrar. | +| 2302 | Object exists | Domain name or nameserver information already exists in the database. Cannot be registered. | +| 2303 | Object does not exist | Domain name or nameserver information does not exist in the database. New registration required. | +| 2304 | Object status prohibits operation | Object status prohibits the action, updates locked. Status must not be "clientupdateprohibited". May be due to other status conditions. | + diff --git a/README-FR.md b/README-FR.md new file mode 100644 index 0000000..a5c19ad --- /dev/null +++ b/README-FR.md @@ -0,0 +1,37 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ +# Module Domainnameapi + +Ce module est une intégration de 'domainnameapi.com' développée pour Blesta. (Mise à jour le 15 août 2024) + +## Exigences + +- La version 5.3 ou supérieure de Blesta est requise. +- La version 7.4 ou supérieure de PHP est requise. +- L'extension PHP Soap doit être activée. + +## Installation + +Copiez les fichiers téléchargés dans blesta/components/modules/domainnameapi/. +Activez-le depuis le menu Paramètres > Modules. + +## Codes de Retour et d'Erreur avec Explications + +| Code | Explication | Détails | +|------|-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | Commande exécutée avec succès | +| 1001 | Command completed successfully; action pending. | Commande exécutée avec succès ; action en attente | +| 2003 | Required parameter missing | Paramètre requis manquant. Par exemple : Numéro de téléphone manquant dans les informations de contact | +| 2105 | Object is not eligible for renewal | L'objet n'est pas éligible pour le renouvellement, les actions de mise à jour sont verrouillées. Le statut ne doit pas être "clientupdateprohibited". Peut être dû à d'autres conditions de statut. | +| 2200 | Authentication error | Erreur d'authentification, code d'autorisation incorrect, ou le domaine est enregistré chez un autre registrar. | +| 2302 | Object exists | Le nom de domaine ou les informations de serveur de noms existent déjà dans la base de données. Ne peut pas être enregistré. | +| 2303 | Object does not exist | Le nom de domaine ou les informations de serveur de noms n'existent pas dans la base de données. Nouvel enregistrement requis. | +| 2304 | Object status prohibits operation | Le statut de l'objet interdit l'action, les mises à jour sont verrouillées. Le statut ne doit pas être "clientupdateprohibited". Peut être dû à d'autres conditions de statut. | diff --git a/README-NL.md b/README-NL.md new file mode 100644 index 0000000..75d9987 --- /dev/null +++ b/README-NL.md @@ -0,0 +1,40 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ + +# Domainnameapi Module + +Deze module is een integratie van 'domainnameapi.com' ontwikkeld voor Blesta. (Bijgewerkt op 15 augustus 2024) + +## Vereisten + +- Blesta versie 5.3 of hoger is vereist. +- PHP versie 7.4 of hoger is vereist. +- De PHP Soap-extensie moet zijn ingeschakeld. + +## Installatie + +Kopieer de gedownloade bestanden naar blesta/components/modules/domainnameapi/. +Activeer het vanuit het menu Instellingen > Modules. + + + +## Terugkeer- en Foutcodes met Uitleg + +| Code | Uitleg | Details | +|------|-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | Opdracht succesvol uitgevoerd | +| 1001 | Command completed successfully; action pending. | Opdracht succesvol uitgevoerd; actie in behandeling | +| 2003 | Required parameter missing | Vereiste parameter ontbreekt. Bijvoorbeeld: Ontbrekend telefoonnummer in contactgegevens | +| 2105 | Object is not eligible for renewal | Object komt niet in aanmerking voor vernieuwing, update-acties vergrendeld. Status mag niet "clientupdateprohibited" zijn. Kan te wijten zijn aan andere statusvoorwaarden. | +| 2200 | Authentication error | Authenticatiefout, autorisatiecode onjuist of domein is geregistreerd bij een andere registrar. | +| 2302 | Object exists | Domeinnaam of nameserver-informatie bestaat al in de database. Kan niet worden geregistreerd. | +| 2303 | Object does not exist | Domeinnaam of nameserver-informatie bestaat niet in de database. Nieuwe registratie vereist. | +| 2304 | Object status prohibits operation | Objectstatus verbiedt de actie, updates vergrendeld. Status mag niet "clientupdateprohibited" zijn. Kan te wijten zijn aan andere statusvoorwaarden. | diff --git a/README.md b/README.md new file mode 100644 index 0000000..51bf1a6 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +
+ TR TR + | EN EN + | AZ AZ + | DE DE + | FR FR + | AR AR + | NL NL +
+ +# Domainnameapi Modülü + +Bu modül, Blesta için geliştirilmiş bir 'domainnameapi.com' entegrasyonudur. (Güncelleme 15 Ağustos 2024) + +## Gereksinimler + +- Blesta'nin 5.3 ve üzeri sürümü gerekmektedir. +- PHP'nin 7.4 ve üzeri sürümü gerekmektedir. +- PHP Soap eklentisi etkinleştirilmelidir. + +## Kurulum + +İndirdiğiniz dosyaları blesta/components/modules/domainnameapi/ +Ayarlar>Modüller menüsünden aktifleştirin. + +## Dönüş ve Hata Kodları ile Açıklamaları + +| Kod | Açıklama | Detay | +|------|-------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 1000 | Command completed successfully | İşlem başarılı. | +| 1001 | Command completed successfully; action pending. | İşlem başarılı. Fakat işlem şu an tamamlanmak için kuyruğa alındı. | +| 2003 | Required parameter missing | Parametre eksik hatası. Örneğin; Kontak bilgisinde telefon girişi yapılmaması. | +| 2105 | Object is not eligible for renewal | Domain durumu yenilemeye müsait değil, güncelleme işlemlerine kilitlenmiştir. Durum durumu "clientupdateprohibited" olmamalı. Diğer durum durumlarından kaynaklanabilir. | +| 2200 | Authentication error | Yetki hatası, güvenlik kodu hatalı veya domain başka bir kayıt firmasında bulunuyor. | +| 2302 | Object exists | Domain adı veya name server bilgisi veritabanında mevcut. Kayıt edilemez. | +| 2303 | Object does not exist | Domain adı veya name server bilgisi veritabanında mevcut değil. Yeni kayıt oluşturulmalı. | +| 2304 | Object status prohibits operation | Domain durumu güncellemeye müsait değildir, güncelleme işlemlerine kilitlenmiştir. Durum durumu "clientupdateprohibited" olmamalı. Diğer durum durumlarından kaynaklanabilir. | + diff --git a/components/modules/domainnameapi/LICENSE b/components/modules/domainnameapi/LICENSE new file mode 100644 index 0000000..dd1fdc9 --- /dev/null +++ b/components/modules/domainnameapi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Blesta + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/components/modules/domainnameapi/README.md b/components/modules/domainnameapi/README.md new file mode 100644 index 0000000..fbb2a51 --- /dev/null +++ b/components/modules/domainnameapi/README.md @@ -0,0 +1,35 @@ +# DomainNameApi Module + +[![Build Status](https://travis-ci.org/blesta/module-domainnameapi.svg?branch=master)](https://travis-ci.org/blesta/module-domainnameapi) [![Coverage Status](https://coveralls.io/repos/github/blesta/module-domainnameapi/badge.svg?branch=master)](https://coveralls.io/github/blesta/module-domainnameapi?branch=master) + +This is a module for Blesta that integrates with [DomainNameApi](https://www.domainnameapi.com/). + +## Install the Module + +1. You can install the module via composer: + + ``` + composer require blesta/domainnameapi + ``` + +2. OR upload the source code to a /components/modules/domainnameapi/ directory within +your Blesta installation path. + + For example: + + ``` + /var/www/html/blesta/components/modules/domainnameapi/ + ``` + +3. Log in to your admin Blesta account and navigate to +> Settings > Modules + +4. Find the Domainnameapi module and click the "Install" button to install it + +5. You're done! + +### Blesta Compatibility + +|Blesta Version|Module Version| +|--------------|--------------| +|>= v5.3.0|v1.0.0+| diff --git a/components/modules/domainnameapi/apis/api.php b/components/modules/domainnameapi/apis/api.php new file mode 100644 index 0000000..494bd23 --- /dev/null +++ b/components/modules/domainnameapi/apis/api.php @@ -0,0 +1,1889 @@ + + */ + +/** + * Class DomainNameAPI_PHPLibrary + * @package DomainNameApi + * @version 2.0.14 + */ + +/* + * This library was written really long before the PSR-7 standards and was not structured according to most coding disciplines. It has only optimized from legacy version. + * The code inherited from the 1st version has been revamped to create the 2nd version, and a complete overhaul is planned for the 3rd version. + */ + +namespace DomainNameApi; + +class DomainNameAPI_PHPLibrary +{ + /** + * Version of the library + */ + const VERSION = '2.0.14'; + + /** + * Error reporting enabled + */ + private $errorReportingEnabled = true; + /** + * Error Reporting Will send this sentry endpoint, if errorReportingEnabled is true + * This request does not include sensitive informations, sensitive informations are filtered. + * @var string $errorReportingDsn + */ + private $errorReportingDsn = 'https://d4e2d61e4af2d4c68fb21ab93bf51ff2@o4507492369039360.ingest.de.sentry.io/4507492373954640'; + + /** + * Api Username + * + * @var string $serviceUsername + */ + private $serviceUsername = "ownername"; + + /* + * Api Password + * @var string $servicePassword + */ + private $servicePassword = "ownerpass"; + + /** + * Api Service Soap URL + * @var string $serviceUrl + */ + private $serviceUrl = "https://whmcs.domainnameapi.com/DomainApi.svc"; + public $lastRequest = []; + public $lastResponse = []; + public $lastParsedResponse = []; + public $lastFunction = ''; + private $service; + private $startAt; + + private $errorTriggered = []; + + + /** + * DomainNameAPI_PHPLibrary constructor. + * @param string $userName + * @param string $password + * @param bool $testMode + * @throws \Exception | \SoapFault + */ + public function __construct($userName = "ownername", $password = "ownerpass", $testMode = false) + { + $this->startAt = microtime(true); + self::setCredentials($userName, $password); + self::useTestMode($testMode); + + try { + // Create unique connection + $this->service = new \SoapClient($this->serviceUrl . "?singlewsdl", [ + "encoding" => "UTF-8", + 'features' => SOAP_SINGLE_ELEMENT_ARRAYS, + 'exceptions' => true, + 'connection_timeout' => 20, + ]); + } catch (\SoapFault $e) { + $this->sendErrorToSentryAsync($e); + throw new \Exception("SOAP Connection Error: " . $e->getMessage()); + } catch (\Exception $e) { + $this->sendErrorToSentryAsync($e); + throw new \Exception("SOAP Error: " . $e->getMessage()); + } + } + + + /** + * Deprecated + * @param bool $value + */ + private function useTestMode($value = true) + { + //if ($value === true || $value == 'on') { + // $this->serviceUsername = 'test1.dna@apiname.com'; + // $this->servicePassword = 'FsUvpJMzQ69scpqE'; + //} + } + + + /** + * SET Username and Password + * @param $userName + * @param $password + * @return void + */ + private function setCredentials($userName, $password) + { + $this->serviceUsername = $userName; + $this->servicePassword = $password; + } + + + /** + * This method returns the last request sent to the API + * @return array|mixed + */ + public function getRequestData() + { + return $this->lastRequest; + } + + /** + * This method sets the last request sent to the API + * @return array|mixed + */ + public function setRequestData($request) + { + $this->lastRequest = $request; + } + + /** + * This method returns the last response from the API + * @return array|mixed + */ + public function getResponseData() + { + return $this->lastResponse; + } + + /** + * This method sets the last response from the API + * @return array|mixed + */ + public function setResponseData($response) + { + $this->lastResponse = $response; + } + + /** + * This method returns the last parsed response from the API + * @return array|mixed + */ + public function getParsedResponseData() + { + return $this->lastParsedResponse; + } + + /** + * This method sets the last parsed response from the API + * @return array|mixed + */ + public function setParsedResponseData($response) + { + $this->lastParsedResponse = $response; + } + + /** + * This method returns the last function called + * @return string + */ + public function getLastFunction() + { + return $this->lastFunction; + } + + /** + * This method sets the last function called + * @return string + */ + public function setLastFunction($function) + { + $this->lastFunction = $function; + } + + public function getServiceUrl() + { + return $this->serviceUrl; + } + + public function setServiceUrl($url) + { + $this->serviceUrl = $url; + } + + + /** + * This method sends anonymous error data to the Sentry server, if error reporting is enabled + * + * @return void + */ + private function sendErrorToSentryAsync(\Exception $e) + { + if (!$this->errorReportingEnabled) { + return; + } + + $skipped_errors = [ + 'Domain not found' + ]; + + foreach ($skipped_errors as $ek => $ev) { + if(strpos($e->getMessage(),$ev) !== false){ + return ; + } + } + + $elapsed_time = microtime(true) - $this->startAt; + $parsed_dsn = parse_url($this->errorReportingDsn); + + // API URL'si + $host = $parsed_dsn['host']; + $project_id = ltrim($parsed_dsn['path'], '/'); + $public_key = $parsed_dsn['user']; + $secret_key = $parsed_dsn['pass'] ?? null; + $api_url = "https://$host/api/$project_id/store/"; + + $external_ip = $this->getServerIp(); + + + + // Hata verisi + $errorData = [ + 'event_id' => bin2hex(random_bytes(16)), + 'timestamp' => gmdate('Y-m-d\TH:i:s\Z'), + 'level' => 'error', + 'logger' => 'php', + 'platform' => 'php', + 'culprit' => __FILE__, + 'message' => [ + 'formatted' => $e->getMessage() + ], + 'exception' => [ + 'values' => [ + [ + 'type' => str_replace(['DomainNameApi\DomainNameAPI_PHPLibrary'],['DNALib Exception'],self::class), + 'value' => $e->getMessage(), + 'stacktrace' => [ + 'frames' => [ + [ + 'filename' => $e->getFile(), + 'lineno' => $e->getLine(), + 'function' => str_replace([dirname(__DIR__),'DomainNameApi\DomainNameAPI_PHPLibrary'],['.','Lib'],$e->getTraceAsString()), + ] + ] + ] + ] + ] + ], + 'tags' => [ + 'handled' => 'yes', + 'level' => 'error', + 'release' => self::VERSION, + 'environment' => 'production', + 'url' => $_SERVER['REQUEST_URI'] ?? 'unknown', + 'transaction' => $_SERVER['REQUEST_METHOD'] ?? 'unknown', + 'status_code' => http_response_code(), + 'trace_id' => bin2hex(random_bytes(8)), // Trace ID örneği + 'runtime_name' => 'PHP', + 'runtime_version' => phpversion(), + 'ip_address' => $external_ip, + 'elapsed_time' => number_format($elapsed_time, 4), + + ], + 'extra' => [ + 'request_data' => $this->getRequestData(), + 'response_data' => $this->getResponseData(), + ] + ]; + + // Sentry başlığı + $sentry_auth = [ + 'sentry_version=7', + 'sentry_client=dnalib-php/' . self::VERSION, + "sentry_key=$public_key" + ]; + if ($secret_key) { + $sentry_auth[] = "sentry_secret=$secret_key"; + } + $sentry_auth_header = 'X-Sentry-Auth: Sentry ' . implode(', ', $sentry_auth); + + if(function_exists('escapeshellarg') && function_exists('exec')){ + $cmd = 'curl -X POST ' . escapeshellarg($api_url) . ' -H ' . escapeshellarg('Content-Type: application/json') . ' -H ' . escapeshellarg($sentry_auth_header) . ' -d ' . escapeshellarg(json_encode($errorData)) . ' > /dev/null 2>&1 &'; + exec($cmd); + }else{ + $jsonData = json_encode($errorData); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $api_url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); + curl_setopt($ch, CURLOPT_TIMEOUT, 2); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + $sentry_auth_header + ]); + curl_exec($ch); + curl_close($ch); + } + } + + private function getServerIp() + { + $cache_ttl = 512; // Cache süresi 512 saniye + $cache_key = 'external_ip'; + $cache_file = __DIR__ . '/ip_addr.cache'; + $current_time = time(); + + if (function_exists('apcu_fetch')) { + // APCu ile cache kontrolü + $external_ip = apcu_fetch($cache_key); + if ($external_ip !== false) { + return $external_ip; + } + } elseif (file_exists($cache_file) && ($current_time - filemtime($cache_file) < $cache_ttl)) { + // Dosya ile cache kontrolü + return file_get_contents($cache_file); + } + + // IP adresini alma ve cacheleme + try { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://ipecho.net/plain"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 2); + $external_ip = curl_exec($ch); + curl_close($ch); + + if ($external_ip !== false) { + // APCu ile cachele + if (function_exists('apcu_store')) { + apcu_store($cache_key, $external_ip, $cache_ttl); + } + // Dosya ile cachele + file_put_contents($cache_file, $external_ip); + } + + return $external_ip; + } catch (\Exception $e) { + return 'unknown'; + } + } + + + /** + * Get Current account details with balance + */ + public function GetResellerDetails() + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + 'CurrencyId' => 2 // 1: TRY, 2: USD + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + $data = $response[key($response)]; + $resp = []; + + if (isset($data['ResellerInfo'])) { + $resp['result'] = 'OK'; + $resp['id'] = $data['ResellerInfo']['Id']; + $resp['active'] = $data['ResellerInfo']['Status'] == 'Active'; + $resp['name'] = $data['ResellerInfo']['Name']; + + $active_currency = $data['ResellerInfo']['BalanceInfoList']['BalanceInfo'][0]; + $balances = []; + foreach ($data['ResellerInfo']['BalanceInfoList']['BalanceInfo'] as $k => $v) { + if ($v['CurrencyName'] == $data['ResellerInfo']['CurrencyInfo']['Code']) { + $active_currency = $v; + } + + $balances[] = [ + 'balance' => $v['Balance'], + 'currency' => $v['CurrencyName'], + 'symbol' => $v['CurrencySymbol'], + ]; + } + + $resp['balance'] = $active_currency['Balance']; + $resp['currency'] = $active_currency['CurrencyName']; + $resp['symbol'] = $active_currency['CurrencySymbol']; + $resp['balances'] = $balances; + } else { + $resp['result'] = 'ERROR'; + $resp['error'] = $this->setError("INVALID_CREDINENTIALS", "Invalid response received from server!", + "invalid username and password"); + } + + + return $resp; + }); + + + return $response; + } + + + /** + * Get Current primary Balance for your account + */ + public function GetCurrentBalance($currencyId = 2) + { + if (strtoupper($currencyId) == 'USD') { + $currencyId = 2; + } elseif (in_array(strtoupper($currencyId), ['TRY', 'TL', '1'])) { + $currencyId = 1; + } else { + $currencyId = 2; + } + + + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + 'CurrencyId' => $currencyId + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + return $response['GetCurrentBalanceResult']; + }); + + + return $response; + } + + + /** + * Check Availability , SLD and TLD must be in array + * @param array $Domains + * @param array $TLDs + * @param int $Period + * @param string $Command + * @return array + */ + public function CheckAvailability($Domains, $TLDs, $Period, $Command) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainNameList" => $Domains, + "TldList" => $TLDs, + "Period" => $Period, + "Commad" => $Command + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + //return $response; + $data = $response[key($response)]; + $available = []; + + if (isset($data["DomainAvailabilityInfoList"]['DomainAvailabilityInfo']['Tld'])) { + $buffer = $data["DomainAvailabilityInfoList"]['DomainAvailabilityInfo']; + $data = [ + 'DomainAvailabilityInfoList' => [ + 'DomainAvailabilityInfo' => [ + $buffer + ] + ] + ]; + } + + foreach ($data["DomainAvailabilityInfoList"]['DomainAvailabilityInfo'] as $name => $value) { + $available[] = [ + "TLD" => $value["Tld"], + "DomainName" => $value["DomainName"], + "Status" => $value["Status"], + "Command" => $value["Command"], + "Period" => $value["Period"], + "IsFee" => $value["IsFee"], + "Price" => $value["Price"], + "Currency" => $value["Currency"], + "Reason" => $value["Reason"], + ]; + } + + return $available; + }); + + + // Log last request and response + + return $response; + } + + + /** + * Get Domain List 0f your account + * @return array + */ + public function GetList($extra_parameters = []) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + ] + ]; + + foreach ($extra_parameters as $k => $v) { + $parameters['request'][$k] = $v; + } + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + $data = $response[key($response)]; + + if (isset($data["TotalCount"]) && is_numeric($data["TotalCount"])) { + $result["data"]["Domains"] = []; + + if (isset($data["DomainInfoList"]) && is_array($data["DomainInfoList"])) { + if (isset($data["DomainInfoList"]["DomainInfo"]["Id"])) { + $result["data"]["Domains"][] = $data["DomainInfoList"]["DomainInfo"]; + } else { + // Parse multiple domain info + foreach ($data["DomainInfoList"]["DomainInfo"] as $domainInfo) { + $result["data"]["Domains"][] = $this->parseDomainInfo($domainInfo); + } + } + } + + $result["result"] = "OK"; + $result["TotalCount"] = $data["TotalCount"]; + } else { + // Set error + $result["result"] = "ERROR"; + $result["error"] = $this->setError("INVALID_DOMAIN_LIST", "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!"); + + $this->sendErrorToSentryAsync(new \Exception("INVALID_DOMAIN_LIST: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + return $result; + }); + + + // Log last request and response + + return $response; + } + + + /** + * Return tld list and pricing matrix , required for price and tld sync + * @param int $count + */ + public function GetTldList($count = 20) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + 'IncludePriceDefinitions' => 1, + 'PageSize' => $count + ] + ]; + + + $result = self::parseCall(__FUNCTION__, $parameters, function ($response) { + $data = $response[key($response)]; + $result = []; + + // If DomainInfo a valid array + if (isset($data["TldInfoList"]) && is_array($data["TldInfoList"])) { + // Parse domain info + + $tlds = []; + + foreach ($data["TldInfoList"]['TldInfo'] as $k => $v) { + $pricing = $currencies = []; + foreach ($v['PriceInfoList']['TldPriceInfo'] as $kp => $vp) { + $pricing[strtolower($vp['TradeType'])][$vp['Period']] = $vp['Price']; + $currencies[strtolower($vp['TradeType'])] = $vp['CurrencyName']; + } + + $tlds[] = [ + 'id' => $v['Id'], + 'status' => $v['Status'], + 'maxchar' => $v['MaxCharacterCount'], + 'maxperiod' => $v['MaxRegistrationPeriod'], + 'minchar' => $v['MinCharacterCount'], + 'minperiod' => $v['MinRegistrationPeriod'], + 'tld' => $v['Name'], + 'pricing' => $pricing, + 'currencies' => $currencies, + ]; + } + + $result = [ + 'data' => $tlds, + 'result' => 'OK' + ]; + } else { + // Set error + $result = [ + 'result' => 'ERROR', + 'error' => $this->setError("INVALID_TLD_LIST", "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!") + ]; + $this->sendErrorToSentryAsync(new \Exception("INVALID_TLD_LIST: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + + return $result; + }); + + + return $result; + } + + + /** + * Get Domain details + * @param string $DomainName + * @return array + */ + public function GetDetails($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + file_put_contents(__DIR__.'/baslangic.txt',date('Y-m-d H:i:s')); + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + $data = $response[key($response)]; + + // If DomainInfo a valid array + if (isset($data["DomainInfo"]) && is_array($data["DomainInfo"])) { + // Parse domain info + $result["data"] = $this->parseDomainInfo($data["DomainInfo"]); + $result["result"] = "OK"; + } else { + // Set error + $result["result"] = "ERROR"; + $result["error"] = $this->setError("INVALID_DOMAIN_LIST", "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!"); + + $this->sendErrorToSentryAsync(new \Exception("INVALID_DOMAIN_LIST: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + return $result; + }); + file_put_contents(__DIR__.'/bitis.txt',date('Y-m-d H:i:s')); + + return $response; + } + + /** + * Modify Name Server, Nameservers must be valid array + * @param string $DomainName + * @param array $NameServers + * @return array + */ + public function ModifyNameServer($DomainName, $NameServers) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "NameServerList" => array_values($NameServers) + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + $result["data"] = []; + $result["data"]["NameServers"] = $parameters["request"]["NameServerList"]; + $result["result"] = "OK"; + + return $result; + }); + + + return $response; + } + + + /** + * Enable Theft Protection Lock for domain + * @param string $DomainName + * @return array + */ + public function EnableTheftProtectionLock($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + return [ + 'data' => [ + 'LockStatus' => true + ], + 'result' => 'OK' + ]; + }); + + + return $response; + } + + + /** + * Disable Theft Protection Lock for domain + * @param string $DomainName + * @return array + */ + public function DisableTheftProtectionLock($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) { + return [ + 'data' => [ + 'LockStatus' => false + ], + 'result' => 'OK' + ]; + }); + + + return $response; + } + + + /** + * Add Child Name Server for domain + * @param string $DomainName + * @param string $NameServer + * @param string $IPAdresses + * @return array + */ + public function AddChildNameServer($DomainName, $NameServer, $IPAdresses) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "ChildNameServer" => $NameServer, + "IpAddressList" => [$IPAdresses] + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + return [ + 'data' => [ + 'NameServer' => $parameters["request"]["ChildNameServer"], + 'IPAdresses' => $parameters["request"]["IpAddressList"] + ], + 'result' => 'OK' + ]; + }); + + return $response; + } + + + /** + * Delete Child Name Server for domain + * @param string $DomainName + * @param string $NameServer + * @return array + */ + public function DeleteChildNameServer($DomainName, $NameServer) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "ChildNameServer" => $NameServer + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + return [ + 'data' => [ + 'NameServer' => $parameters["request"]["ChildNameServer"], + ], + 'result' => 'OK' + ]; + }); + + return $response; + } + + + /** + * Modify IP of Child Name Server for domain + * @param string $DomainName + * @param string $NameServer + * @param string $IPAdresses + * @return array + */ + public function ModifyChildNameServer($DomainName, $NameServer, $IPAdresses) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "ChildNameServer" => $NameServer, + "IpAddressList" => [$IPAdresses] + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + return [ + 'data' => [ + 'NameServer' => $parameters["request"]["ChildNameServer"], + 'IPAdresses' => $parameters["request"]["IpAddressList"] + ], + 'result' => 'OK' + ]; + }); + + + return $response; + } + + // CONTACT MANAGEMENT + + /** + * Get Contacts for domain, Administrative, Billing, Technical, Registrant segments will be returned + * @param string $DomainName + * @return array + */ + public function GetContacts($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + $result = []; + + // If ContactInfo a valid array + if (isset($data["AdministrativeContact"]) && is_array($data["AdministrativeContact"]) && isset($data["TechnicalContact"]) && is_array($data["TechnicalContact"]) && isset($data["RegistrantContact"]) && is_array($data["RegistrantContact"]) && isset($data["BillingContact"]) && is_array($data["BillingContact"])) { + // Parse domain info + + $result = [ + 'data' => [ + 'contacts' => [ + 'Administrative' => $this->parseContactInfo($data["AdministrativeContact"]), + 'Billing' => $this->parseContactInfo($data["BillingContact"]), + 'Registrant' => $this->parseContactInfo($data["RegistrantContact"]), + 'Technical' => $this->parseContactInfo($data["TechnicalContact"]), + ] + ], + 'result' => 'OK' + ]; + } else { + // Set error + $result = [ + 'error' => $this->setError("INVALID_CONTACT_INTO", "Invalid response received from server!", + "Contact info is not a valid array or more than one contact info has returned!"), + 'result' => 'ERROR' + ]; + $this->sendErrorToSentryAsync(new \Exception("INVALID_CONTACT_INTO: Invalid response received from server! Contact info is not a valid array or more than one contact info has returned!")); + } + return $result; + }); + + + return $response; + } + + + /** + * Save Contacts for domain, Contacts segments will be saved as Administrative, Billing, Technical, Registrant. + * @param string $DomainName + * @param array $Contacts + * @return array + */ + public function SaveContacts($DomainName, $Contacts) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "AdministrativeContact" => $Contacts["Administrative"], + "BillingContact" => $Contacts["Billing"], + "TechnicalContact" => $Contacts["Technical"], + "RegistrantContact" => $Contacts["Registrant"] + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + $result = []; + + if ($data['OperationResult'] == 'SUCCESS') { + $result = [ + 'result' => 'OK' + ]; + } else { + // Set error + $result = [ + 'result' => 'ERROR', + 'error' => $this->setError("INVALID_CONTACT_SAVE", "Invalid response received from server!", + "Contact info is not a valid array or more than one contact info has returned!") + ]; + + $this->sendErrorToSentryAsync(new \Exception("INVALID_CONTACT_SAVE: Invalid response received from server! Contact info is not a valid array or more than one contact info has returned!")); + } + return $result; + }); + + + return $response; + } + + // DOMAIN TRANSFER (INCOMING DOMAIN) + + // Start domain transfer (Incoming domain) + /** + * Transfer Domain + * @param string $DomainName + * @param string $AuthCode + * @param int $Period + * @return array + */ + public function Transfer($DomainName, $AuthCode, $Period) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "AuthCode" => $AuthCode, + 'AdditionalAttributes' => [ + 'KeyValueOfstringstring' => [ + [ + 'Key' => 'TRANSFERPERIOD', + 'Value' => $Period + ] + ] + ] + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $result = []; + $data = $response[key($response)]; + // If DomainInfo a valid array + if (isset($data["DomainInfo"]) && is_array($data["DomainInfo"])) { + // Parse domain info + $result = [ + 'result' => 'OK', + 'data' => $this->parseDomainInfo($data["DomainInfo"]) + ]; + } else { + // Set error + $result = [ + 'result' => 'ERROR', + 'data' => $this->setError("INVALID_DOMAIN_TRANSFER_REQUEST", + "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!") + ]; + $this->sendErrorToSentryAsync(new \Exception("INVALID_DOMAIN_TRANSFER_REQUEST: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + return $result; + }); + + + return $response; + } + + + /** + * Stops Incoming Transfer + * @param string $DomainName + */ + public function CancelTransfer($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + return [ + 'result' => $data['OperationResult'] == 'SUCCESS' ? 'OK' : 'ERROR', + 'data' => [ + 'DomainName' => $parameters["request"]["DomainName"] + ] + ]; + }); + + return $response; + } + + + /** + * Approve Outgoing transfer + * @param $DomainName + * @return mixed|string[] + */ + public function ApproveTransfer($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + return [ + 'result' => $data['OperationResult'] == 'SUCCESS' ? 'OK' : 'ERROR', + 'data' => [ + 'DomainName' => $parameters["request"]["DomainName"] + ] + ]; + }); + + return $response; + } + + + /** + * Reject Outgoing transfer + * @param $DomainName + * @return mixed|string[] + */ + public function RejectTransfer($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + return [ + 'result' => $data['OperationResult'] == 'SUCCESS' ? 'OK' : 'ERROR', + 'data' => [ + 'DomainName' => $parameters["request"]["DomainName"] + ] + ]; + }); + + return $response; + } + + + /** + * Renew domain + * @param string $DomainName + * @param int $Period + * @return array + */ + public function Renew($DomainName, $Period) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "Period" => $Period + ] + ]; + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $data = $response[key($response)]; + + if (isset($data["ExpirationDate"])) { + return [ + 'result' => 'OK', + 'data' => [ + 'ExpirationDate' => $data["ExpirationDate"] + ] + ]; + } else { + return [ + 'result' => 'ERROR', + 'error' => $this->setError("INVALID_DOMAIN_RENEW", "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!") + ]; + $this->sendErrorToSentryAsync(new \Exception("INVALID_DOMAIN_RENEW: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + }); + + return $response; + } + + + // Register domain with contact information + + /** + * Register domain with contact information + * @param string $DomainName + * @param int $Period + * @param array $Contacts + * @param array $NameServers + * @param bool $TheftProtectionLock + * @param bool $PrivacyProtection + * @param array $addionalAttributes + * @return array + */ + public function RegisterWithContactInfo( + $DomainName, + $Period, + $Contacts, + $NameServers = ["dns.domainnameapi.com", "web.domainnameapi.com"], + $TheftProtectionLock = true, + $PrivacyProtection = false, + $addionalAttributes = [] + ) { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "Period" => $Period, + "NameServerList" => $NameServers, + "LockStatus" => $TheftProtectionLock, + "PrivacyProtectionStatus" => $PrivacyProtection, + "AdministrativeContact" => $Contacts["Administrative"], + "BillingContact" => $Contacts["Billing"], + "TechnicalContact" => $Contacts["Technical"], + "RegistrantContact" => $Contacts["Registrant"] + ] + ]; + + if (count($addionalAttributes) > 0) { + foreach ($addionalAttributes as $k => $v) { + $parameters['request']['AdditionalAttributes']['KeyValueOfstringstring'][] = [ + 'Key' => $k, + 'Value' => $v + ]; + } + } + + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $result = []; + $data = $response[key($response)]; + + // If DomainInfo a valid array + if (isset($data["DomainInfo"]) && is_array($data["DomainInfo"])) { + // Parse domain info + $result = [ + 'result' => 'OK', + 'data' => $this->parseDomainInfo($data["DomainInfo"]) + ]; + } else { + // Set error + $result = [ + 'result' => 'ERROR', + 'error' => $this->setError("INVALID_DOMAIN_REGISTER", "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!") + ]; + $this->sendErrorToSentryAsync(new \Exception("INVALID_DOMAIN_REGISTER: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + return $result; + }); + + + return $response; + } + + + /** + * Modify privacy protection status of domain + * @param string $DomainName + * @param bool $Status + * @param string $Reason + * @return array + */ + public function ModifyPrivacyProtectionStatus($DomainName, $Status, $Reason = "Owner request") + { + if (trim($Reason) == "") { + $Reason = "Owner request"; + } + + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName, + "ProtectPrivacy" => $Status, + "Reason" => $Reason + ] + ]; + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + return [ + 'data' => [ + 'PrivacyProtectionStatus' => $parameters["request"]["ProtectPrivacy"] + ], + 'result' => 'OK' + ]; + }); + + + return $response; + } + + + /** + * Sync from registry, domain information will be updated from registry + * @param string $DomainName + * @return array + */ + public function SyncFromRegistry($DomainName) + { + $parameters = [ + "request" => [ + "Password" => $this->servicePassword, + "UserName" => $this->serviceUsername, + "DomainName" => $DomainName + ] + ]; + + $response = self::parseCall(__FUNCTION__, $parameters, function ($response) use ($parameters) { + $result = []; + $data = $response[key($response)]; + + // If DomainInfo a valid array + if (isset($data["DomainInfo"]) && is_array($data["DomainInfo"])) { + // Parse domain info + $result = [ + 'data' => $this->parseDomainInfo($data["DomainInfo"]), + 'result' => 'OK' + ]; + } else { + // Set error + $result = [ + 'error' => $this->setError("INVALID_DOMAIN_SYNC", "Invalid response received from server!", + "Domain info is not a valid array or more than one domain info has returned!"), + 'result' => 'ERROR' + ]; + $this->sendErrorToSentryAsync(new \Exception("INVALID_DOMAIN_SYNC: Invalid response received from server! Domain info is not a valid array or more than one domain info has returned!")); + } + + return $result; + }); + + return $response; + } + + + // Convert object to array + private function objectToArray($_obj) + { + try { + $_obj = json_decode(json_encode($_obj), true); + } catch (\Exception $ex) { + } + return $_obj; + } + + // Get error if exists + private function parseError($response, $trace = true) + { + $result = false; + + if (is_null($response)) { + // Set error data + $result = []; + $result["Code"] = "INVALID_RESPONSE"; + $result["Message"] = "Invalid response or no response received from server!"; + $result["Details"] = "SOAP Connection returned null value!"; + } elseif (!is_array($response)) { + // Set error data + $result = []; + $result["Code"] = "INVALID_RESPONSE"; + $result["Message"] = "Invalid response or no response received from server!"; + $result["Details"] = "SOAP Connection returned non-array value!"; + } elseif (strtolower(key($response)) == "faultstring") { + // Handle soap fault + + $result = []; + $result["Code"] = ""; + $result["Message"] = ""; + $result["Details"] = ""; + + // Set error data + if (isset($response["faultcode"])) { + $result["Code"] = $response["faultcode"]; + } + if (isset($response["faultstring"])) { + $result["Message"] = $response["faultstring"]; + } + if (isset($response["detail"])) { + if (is_array($response["detail"])) { + if (isset($response["detail"]["ExceptionDetail"])) { + if (is_array($response["detail"]["ExceptionDetail"])) { + if (isset($response["detail"]["ExceptionDetail"]["StackTrace"])) { + $result["Details"] = $response["detail"]["ExceptionDetail"]["StackTrace"]; + } + } + } + } + } + } elseif (count($response) != 1) { + // Set error data + $result = []; + $result["Code"] = "INVALID_RESPONSE"; + $result["Message"] = "Invalid response or no response received from server!"; + $result["Details"] = "Response data contains more than one result! Only one result accepted!"; + } elseif (!isset($response[key($response)]["OperationResult"]) || !isset($response[key($response)]["ErrorCode"])) { + // Set error data + $result = []; + $result["Code"] = "INVALID_RESPONSE"; + $result["Message"] = "Invalid response or no response received from server!"; + $result["Details"] = "Operation result or Error code not received from server!"; + } elseif (strtoupper($response[key($response)]["OperationResult"]) != "SUCCESS") { + // Set error data + $result = [ + "Code" => '', + "Message" => 'Failed', + "Details" => '', + ]; + + if (isset($response[key($response)]["OperationMessage"])) { + $result["Code"] = "API_" . $response[key($response)]["ErrorCode"]; + $result['Response'] = print_r($response, true); + } + + if (isset($response[key($response)]["OperationResult"])) { + $result["Code"] .= "_" . $response[key($response)]["OperationResult"]; + } + + if (isset($response[key($response)]["OperationMessage"])) { + $result["Details"] = $response[key($response)]["OperationMessage"]; + } + } + + if (isset($result["Code"]) && $trace === true) { + $this->sendErrorToSentryAsync(new \Exception("API_ERROR: " . $result["Code"] . " - " . $result["Message"] . " - " . $result["Details"])); + } + + return $result; + } + + // Check if response contains error + private function hasError($response) + { + return ($this->parseError($response, false) === false) ? false : true; + } + + // Set error message + private function setError($Code, $Message, $Details) + { + $result = []; + $result["Code"] = $Code; + $result["Message"] = $Message; + $result["Details"] = $Details; + return $result; + } + + // Parse domain info + private function parseDomainInfo($data) + { + $result = []; + $result["ID"] = ""; + $result["Status"] = ""; + $result["DomainName"] = ""; + $result["AuthCode"] = ""; + $result["LockStatus"] = ""; + $result["PrivacyProtectionStatus"] = ""; + $result["IsChildNameServer"] = ""; + $result["Contacts"] = []; + $result["Contacts"]["Billing"] = []; + $result["Contacts"]["Technical"] = []; + $result["Contacts"]["Administrative"] = []; + $result["Contacts"]["Registrant"] = []; + $result["Contacts"]["Billing"]["ID"] = ""; + $result["Contacts"]["Technical"]["ID"] = ""; + $result["Contacts"]["Administrative"]["ID"] = ""; + $result["Contacts"]["Registrant"]["ID"] = ""; + $result["Dates"] = []; + $result["Dates"]["Start"] = ""; + $result["Dates"]["Expiration"] = ""; + $result["Dates"]["RemainingDays"] = ""; + $result["NameServers"] = []; + $result["Additional"] = []; + $result["ChildNameServers"] = []; + + foreach ($data as $attrName => $attrValue) { + switch ($attrName) { + case "Id": + if (is_numeric($attrValue)) { + $result["ID"] = $attrValue; + } + break; + + + case "Status": + + $result["Status"] = $attrValue; + break; + + + case "DomainName": + + $result["DomainName"] = $attrValue; + break; + + + case "AdministrativeContactId": + + if (is_numeric($attrValue)) { + $result["Contacts"]["Administrative"]["ID"] = $attrValue; + } + break; + + + case "BillingContactId": + + if (is_numeric($attrValue)) { + $result["Contacts"]["Billing"]["ID"] = $attrValue; + } + break; + + + case "TechnicalContactId": + + if (is_numeric($attrValue)) { + $result["Contacts"]["Technical"]["ID"] = $attrValue; + } + break; + + + case "RegistrantContactId": + + if (is_numeric($attrValue)) { + $result["Contacts"]["Registrant"]["ID"] = $attrValue; + } + break; + + + case "Auth": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["AuthCode"] = $attrValue; + } + break; + + + case "StartDate": + + $result["Dates"]["Start"] = $attrValue; + break; + + + case "ExpirationDate": + + $result["Dates"]["Expiration"] = $attrValue; + break; + + + case "LockStatus": + + if (is_bool($attrValue)) { + $result["LockStatus"] = var_export($attrValue, true); + } + break; + + + case "PrivacyProtectionStatus": + + if (is_bool($attrValue)) { + $result["PrivacyProtectionStatus"] = var_export($attrValue, true); + } + break; + + + case "IsChildNameServer": + + if (is_bool($attrValue)) { + $result["IsChildNameServer"] = var_export($attrValue, true); + } + break; + + + case "RemainingDay": + + if (is_numeric($attrValue)) { + $result["Dates"]["RemainingDays"] = $attrValue; + } + break; + + + case "NameServerList": + + if (is_array($attrValue)) { + foreach ($attrValue as $nameserverValue) { + $result["NameServers"] = $nameserverValue; + } + } + break; + + + case "AdditionalAttributes": + + if (is_array($attrValue)) { + if (isset($attrValue["KeyValueOfstringstring"])) { + foreach ($attrValue["KeyValueOfstringstring"] as $attribute) { + if (isset($attribute["Key"]) && isset($attribute["Value"])) { + $result["Additional"][$attribute["Key"]] = $attribute["Value"]; + } + } + } + } + break; + + + case "ChildNameServerInfo": + if (is_array($attrValue)) { + if (isset($attrValue["ChildNameServerInfo"]) && is_array($attrValue["ChildNameServerInfo"]) && count($attrValue["ChildNameServerInfo"]) > 0) { + foreach ($attrValue["ChildNameServerInfo"] as $attribute) { + if (isset($attribute["ChildNameServer"]) && isset($attribute["IpAddress"])) { + $ns = ""; + $IpAddresses = []; + + // Name of NameServer + if (is_string($attribute["ChildNameServer"])) { + $ns = $attribute["ChildNameServer"]; + } + + // IP adresses of NameServer + if (is_array($attribute["IpAddress"]) && isset($attribute["IpAddress"]["string"])) { + if (is_array($attribute["IpAddress"]["string"])) { + foreach ($attribute["IpAddress"]["string"] as $ip) { + if (isset($ip) && is_string($ip)) { + $IpAddresses = $ip; + } + } + } elseif (is_string($attribute["IpAddress"]["string"])) { + $IpAddresses = $attribute["IpAddress"]["string"]; + } + } + + $result["ChildNameServers"][] = [ + "ns" => $ns, + "ip" => $IpAddresses + ]; + } + } + } + } + break; + } + } + + return $result; + } + + // Parse Contact info + private function parseContactInfo($data) + { + $result = []; + $result["ID"] = ""; + $result["Status"] = ""; + $result["Additional"] = []; + $result["Address"] = []; + $result["Address"]["Line1"] = ""; + $result["Address"]["Line2"] = ""; + $result["Address"]["Line3"] = ""; + $result["Address"]["State"] = ""; + $result["Address"]["City"] = ""; + $result["Address"]["Country"] = ""; + $result["Address"]["ZipCode"] = ""; + $result["Phone"] = []; + $result["Phone"]["Phone"] = []; + $result["Phone"]["Phone"]["Number"] = ""; + $result["Phone"]["Phone"]["CountryCode"] = ""; + $result["Phone"]["Fax"]["Number"] = ""; + $result["Phone"]["Fax"]["CountryCode"] = ""; + $result["AuthCode"] = ""; + $result["FirstName"] = ""; + $result["LastName"] = ""; + $result["Company"] = ""; + $result["EMail"] = ""; + $result["Type"] = ""; + + foreach ($data as $attrName => $attrValue) { + switch ($attrName) { + case "Id": + + if (is_numeric($attrValue)) { + $result["ID"] = $attrValue; + } + break; + + + case "Status": + + $result["Status"] = $attrValue; + break; + + + case "AdditionalAttributes": + + if (is_array($attrValue)) { + if (isset($attrValue["KeyValueOfstringstring"])) { + foreach ($attrValue["KeyValueOfstringstring"] as $attribute) { + if (isset($attribute["Key"]) && isset($attribute["Value"])) { + $result["Additional"][$attribute["Key"]] = $attribute["Value"]; + } + } + } + } + break; + + + case "AddressLine1": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["Line1"] = $attrValue; + } + break; + + + case "AddressLine2": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["Line2"] = $attrValue; + } + break; + + + case "AddressLine3": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["Line3"] = $attrValue; + } + break; + + + case "Auth": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["AuthCode"] = $attrValue; + } + break; + + + case "City": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["City"] = $attrValue; + } + break; + + + case "Company": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Company"] = $attrValue; + } + break; + + + case "Country": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["Country"] = $attrValue; + } + break; + + + case "EMail": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["EMail"] = $attrValue; + } + break; + + + case "Fax": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Phone"]["Fax"]["Number"] = $attrValue; + } + break; + + + case "FaxCountryCode": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Phone"]["Fax"]["CountryCode"] = $attrValue; + } + break; + + + case "Phone": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Phone"]["Phone"]["Number"] = $attrValue; + } + break; + + + case "PhoneCountryCode": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Phone"]["Phone"]["CountryCode"] = $attrValue; + } + break; + + + case "FirstName": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["FirstName"] = $attrValue; + } + break; + + + case "LastName": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["LastName"] = $attrValue; + } + break; + + + case "State": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["State"] = $attrValue; + } + break; + + + case "ZipCode": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Address"]["ZipCode"] = $attrValue; + } + break; + + + case "Type": + + if (is_string($attrValue) && !is_null($attrValue)) { + $result["Type"] = $attrValue; + } + break; + } + } + + return $result; + } + + + private function parseCall($fn, $parameters, $_callback) + { + $result = [ + 'result' => 'ERROR', + 'error' => 'Unknown Error Occured' + ]; + + try { + // SOAP method which is same as current function name called + $_response_raw = $_response = $this->service->__soapCall($fn, [$parameters]); + + $_response_raw = $this->service->__getLastResponse(); + + + // Serialize as array + $_response = $this->objectToArray($_response); + + $this->setLastFunction($fn); + $this->setRequestData($parameters); + $this->setResponseData($_response); + + + // Check is there any error? + if (!$this->hasError($_response)) { + $result = $_callback($_response); + } else { + // Hata mesajini dondura + $result["result"] = "ERROR"; + $result["error"] = $this->parseError($_response); + } + } catch (\SoapFault $ex) { + $result["result"] = "ERROR"; + $result["error"] = $this->setError('INVALID_RESPONSE', 'Invalid Response occured', $ex->getMessage()); + $this->sendErrorToSentryAsync($ex); + } catch (\Exception $ex) { + $result["result"] = "ERROR"; + $result["error"] = $this->parseError($this->objectToArray($ex)); + $this->sendErrorToSentryAsync($ex); + } + + $this->setParsedResponseData($result); + + + return $result; + } + + + /** + * Domain is TR type + * @param $domain + * @return bool + */ + public function isTrTLD($domain) + { + preg_match('/\.com\.tr|\.net\.tr|\.org\.tr|\.biz\.tr|\.info\.tr|\.tv\.tr|\.gen\.tr|\.web\.tr|\.tel\.tr|\.name\.tr|\.bbs\.tr|\.gov\.tr|\.bel\.tr|\.pol\.tr|\.edu\.tr|\.k12\.tr|\.av\.tr|\.dr\.tr$/', + $domain, $result); + + return isset($result[0]); + } + + +} diff --git a/components/modules/domainnameapi/composer.json b/components/modules/domainnameapi/composer.json new file mode 100644 index 0000000..5863f92 --- /dev/null +++ b/components/modules/domainnameapi/composer.json @@ -0,0 +1,13 @@ +{ + "name": "blesta/domainnameapi", + "description": "DomainNameApi", + "license": "proprietary", + "type": "blesta-module", + "require": { + "blesta/composer-installer": "~1.0", + "ext-dom": "*", + "ext-simplexml": "*", + "ext-curl": "*", + "ext-soap": "*" + } +} diff --git a/components/modules/domainnameapi/config.json b/components/modules/domainnameapi/config.json new file mode 100644 index 0000000..88a820d --- /dev/null +++ b/components/modules/domainnameapi/config.json @@ -0,0 +1,34 @@ +{ + "version": "1.1.2", + "type": "registrar", + "name": "Domainnameapi", + "description": "Domainnameapi.description", + "authors": [ + { + "name": "Atak Domain", + "url": "http://www.atakdomain.com" + } + ], + "package": { + "name_key": "domain" + }, + "service": { + "name_key": "domain" + }, + "module": { + "row": "Domainnameapi.module_row", + "rows": "Domainnameapi.module_row_plural", + "row_key": "user" + }, + "email_tags": { + "module": [], + "package": [], + "service": ["domain"] + }, + "features": { + "dns_management": 1, + "email_forwarding": 0, + "id_protection": 1, + "epp_code": 1 + } +} \ No newline at end of file diff --git a/components/modules/domainnameapi/config/domainnameapi.php b/components/modules/domainnameapi/config/domainnameapi.php new file mode 100644 index 0000000..36a7636 --- /dev/null +++ b/components/modules/domainnameapi/config/domainnameapi.php @@ -0,0 +1,887 @@ + [ + 'label' => Language::_('Domainnameapi.transfer.domain', true), + 'type' => 'text' + ], + 'auth_info' => [ + 'label' => Language::_('Domainnameapi.transfer.auth_info', true), + 'type' => 'text' + ] +]); + +// Domain fields +Configure::set('Domainnameapi.domain_fields', [ + 'domain' => [ + 'label' => Language::_('Domainnameapi.domain.domain', true), + 'type' => 'text' + ], +]); + +// Nameserver fields +Configure::set('Domainnameapi.nameserver_fields', [ + 'ns1' => [ + 'label' => Language::_('Domainnameapi.nameserver.ns1', true), + 'type' => 'text' + ], + 'ns2' => [ + 'label' => Language::_('Domainnameapi.nameserver.ns2', true), + 'type' => 'text' + ], + 'ns3' => [ + 'label' => Language::_('Domainnameapi.nameserver.ns3', true), + 'type' => 'text' + ], + 'ns4' => [ + 'label' => Language::_('Domainnameapi.nameserver.ns4', true), + 'type' => 'text' + ] +]); + +// Whois fields +Configure::set('Domainnameapi.whois_sections', [ + 'Administrative', + 'Technical', + 'Registrant', + 'Billing' +]); + +// Whois fields +Configure::set('Domainnameapi.whois_fields', [ + 'AdministrativeFirstName' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.first_name', true), + 'type' => 'text' + ], + 'AdministrativeLastName' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.last_name', true), + 'type' => 'text' + ], + 'AdministrativeCompany' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.company_name', true), + 'type' => 'text' + ], + 'AdministrativeAddressLine1' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.address1', true), + 'type' => 'text' + ], + 'AdministrativeAddressLine2' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.address2', true), + 'type' => 'text' + ], + 'AdministrativeCity' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.city', true), + 'type' => 'text' + ], + 'AdministrativeState' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.state', true), + 'type' => 'text' + ], + 'AdministrativeZipCode' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.postal_code', true), + 'type' => 'text' + ], + 'AdministrativeCountry' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.country', true), + 'type' => 'text' + ], + 'AdministrativePhoneCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.phone_country_code', true), + 'type' => 'text' + ], + 'AdministrativePhone' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.phone', true), + 'type' => 'text' + ], + 'AdministrativeFaxCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.fax_country_code', true), + 'type' => 'text' + ], + 'AdministrativeFax' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.fax', true), + 'type' => 'text' + ], + 'AdministrativeEMail' => [ + 'label' => Language::_('Domainnameapi.whois.administrative.email', true), + 'type' => 'text' + ], + 'TechnicalFirstName' => [ + 'label' => Language::_('Domainnameapi.whois.technical.first_name', true), + 'type' => 'text' + ], + 'TechnicalLastName' => [ + 'label' => Language::_('Domainnameapi.whois.technical.last_name', true), + 'type' => 'text' + ], + 'TechnicalCompany' => [ + 'label' => Language::_('Domainnameapi.whois.technical.company_name', true), + 'type' => 'text' + ], + 'TechnicalAddressLine1' => [ + 'label' => Language::_('Domainnameapi.whois.technical.address1', true), + 'type' => 'text' + ], + 'TechnicalAddressLine2' => [ + 'label' => Language::_('Domainnameapi.whois.technical.address2', true), + 'type' => 'text' + ], + 'TechnicalCity' => [ + 'label' => Language::_('Domainnameapi.whois.technical.city', true), + 'type' => 'text' + ], + 'TechnicalState' => [ + 'label' => Language::_('Domainnameapi.whois.technical.state', true), + 'type' => 'text' + ], + 'TechnicalZipCode' => [ + 'label' => Language::_('Domainnameapi.whois.technical.postal_code', true), + 'type' => 'text' + ], + 'TechnicalCountry' => [ + 'label' => Language::_('Domainnameapi.whois.technical.country', true), + 'type' => 'text' + ], + 'TechnicalPhoneCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.technical.phone_country_code', true), + 'type' => 'text' + ], + 'TechnicalPhone' => [ + 'label' => Language::_('Domainnameapi.whois.technical.phone', true), + 'type' => 'text' + ], + 'TechnicalFaxCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.technical.fax_country_code', true), + 'type' => 'text' + ], + 'TechnicalFax' => [ + 'label' => Language::_('Domainnameapi.whois.technical.fax', true), + 'type' => 'text' + ], + 'TechnicalEMail' => [ + 'label' => Language::_('Domainnameapi.whois.technical.email', true), + 'type' => 'text' + ], + 'RegistrantFirstName' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.first_name', true), + 'type' => 'text' + ], + 'RegistrantLastName' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.last_name', true), + 'type' => 'text' + ], + 'RegistrantCompany' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.company_name', true), + 'type' => 'text' + ], + 'RegistrantAddressLine1' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.address1', true), + 'type' => 'text' + ], + 'RegistrantAddressLine2' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.address2', true), + 'type' => 'text' + ], + 'RegistrantCity' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.city', true), + 'type' => 'text' + ], + 'RegistrantState' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.state', true), + 'type' => 'text' + ], + 'RegistrantZipCode' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.postal_code', true), + 'type' => 'text' + ], + 'RegistrantCountry' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.country', true), + 'type' => 'text' + ], + 'RegistrantPhoneCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.phone_country_code', true), + 'type' => 'text' + ], + 'RegistrantPhone' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.phone', true), + 'type' => 'text' + ], + 'RegistrantFaxCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.fax_country_code', true), + 'type' => 'text' + ], + 'RegistrantFax' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.fax', true), + 'type' => 'text' + ], + 'RegistrantEMail' => [ + 'label' => Language::_('Domainnameapi.whois.registrant.email', true), + 'type' => 'text' + ], + 'BillingFirstName' => [ + 'label' => Language::_('Domainnameapi.whois.billing.first_name', true), + 'type' => 'text' + ], + 'BillingLastName' => [ + 'label' => Language::_('Domainnameapi.whois.billing.last_name', true), + 'type' => 'text' + ], + 'BillingCompany' => [ + 'label' => Language::_('Domainnameapi.whois.billing.company_name', true), + 'type' => 'text' + ], + 'BillingAddressLine1' => [ + 'label' => Language::_('Domainnameapi.whois.billing.address1', true), + 'type' => 'text' + ], + 'BillingAddressLine2' => [ + 'label' => Language::_('Domainnameapi.whois.billing.address2', true), + 'type' => 'text' + ], + 'BillingCity' => [ + 'label' => Language::_('Domainnameapi.whois.billing.city', true), + 'type' => 'text' + ], + 'BillingState' => [ + 'label' => Language::_('Domainnameapi.whois.billing.state', true), + 'type' => 'text' + ], + 'BillingZipCode' => [ + 'label' => Language::_('Domainnameapi.whois.billing.postal_code', true), + 'type' => 'text' + ], + 'BillingCountry' => [ + 'label' => Language::_('Domainnameapi.whois.billing.country', true), + 'type' => 'text' + ], + 'BillingPhoneCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.billing.phone_country_code', true), + 'type' => 'text' + ], + 'BillingPhone' => [ + 'label' => Language::_('Domainnameapi.whois.billing.phone', true), + 'type' => 'text' + ], + 'BillingFaxCountryCode' => [ + 'label' => Language::_('Domainnameapi.whois.billing.fax_country_code', true), + 'type' => 'text' + ], + 'BillingFax' => [ + 'label' => Language::_('Domainnameapi.whois.billing.fax', true), + 'type' => 'text' + ], + 'BillingEMail' => [ + 'label' => Language::_('Domainnameapi.whois.billing.email', true), + 'type' => 'text' + ] +]); + + +// .TR +Configure::set('Domainnameapi.domain_fields.uk', [ + 'registrant_extra_info[registrant_type]' => [ + 'label' => Language::_('Domainnameapi.domain.registrant_type', true), + 'type' => 'select', + 'options' => [ + 'IND' => Language::_('Domainnameapi.domain.registrant_type.ind', true), + 'FIND' => Language::_('Domainnameapi.domain.registrant_type.find', true), + 'LTD' => Language::_('Domainnameapi.domain.registrant_type.ltd', true), + 'PLC' => Language::_('Domainnameapi.domain.registrant_type.plc', true), + 'PTNR' => Language::_('Domainnameapi.domain.registrant_type.ptnr', true), + 'LLP' => Language::_('Domainnameapi.domain.registrant_type.llp', true), + 'IP' => Language::_('Domainnameapi.domain.registrant_type.ip', true), + 'STRA' => Language::_('Domainnameapi.domain.registrant_type.stra', true), + 'SCH' => Language::_('Domainnameapi.domain.registrant_type.sch', true), + 'RCHAR' => Language::_('Domainnameapi.domain.registrant_type.rchar', true), + 'GOV' => Language::_('Domainnameapi.domain.registrant_type.gov', true), + 'OTHER' => Language::_('Domainnameapi.domain.registrant_type.other', true), + 'CRC' => Language::_('Domainnameapi.domain.registrant_type.crc', true), + 'FCORP' => Language::_('Domainnameapi.domain.registrant_type.fcorp', true), + 'STAT' => Language::_('Domainnameapi.domain.registrant_type.stat', true), + 'FOTHER' => Language::_('Domainnameapi.domain.registrant_type.fother', true) + ] + ], + 'registrant_extra_info[registration_number]' => [ + 'label' => Language::_('Domainnameapi.domain.registration_number', true), + 'type' => 'text' + ], + 'registrant_extra_info[trading_name]' => [ + 'label' => Language::_('Domainnameapi.domain.trading_name', true), + 'type' => 'text' + ] +]); + + +// Email templates +Configure::set('Domainnameapi.email_templates', [ + 'en_us' => [ + 'lang' => 'en_us', + 'text' => 'Your new domain has been successfully registered! + +Domain: {service.domain} + +Thank you for your business!', + 'html' => '

Your new domain has been successfully registered!

+

Domain: {service.domain}

+

Thank you for your business!

' + ] +]); diff --git a/components/modules/domainnameapi/domainnameapi.php b/components/modules/domainnameapi/domainnameapi.php new file mode 100644 index 0000000..9cb2f15 --- /dev/null +++ b/components/modules/domainnameapi/domainnameapi.php @@ -0,0 +1,1689 @@ +loadConfig(dirname(__FILE__) . DS . 'config.json'); + + // Load components required by this module + Loader::loadComponents($this, ['Input']); + + // Load the language required by this module + Language::loadLang('domainnameapi', null, dirname(__FILE__) . DS . 'language' . DS); + + Configure::load('domainnameapi', dirname(__FILE__) . DS . 'config' . DS); + } + + /** + * Get a list of the TLD prices + * + * @param int|null $module_row_id The ID of the module row to fetch for the current module + * @return array A list of all TLDs and their pricing + * [tld => [currency => [year# => ['register' => price, 'transfer' => price, 'renew' => price]]]] + */ + public function getTldPricing($module_row_id=null) + { + + Loader::loadModels($this, ['Currencies']); + + // Fetch pricing from the registrar + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $all_tlds = $api->GetTldList(900); + + + + $response = []; + + if ($all_tlds['result'] == 'OK') { + foreach ($all_tlds['data'] as $k => $v) { + foreach (range($v['minperiod'], $v['maxperiod']) as $ky => $vy) { + $response['.' . $v['tld']]['USD'][$vy]['register'] = number_format($v['pricing']['registration'][1] * $vy,3); + $response['.' . $v['tld']]['USD'][$vy]['transfer'] = number_format($v['pricing']['transfer'][1] * $vy,3); + $response['.' . $v['tld']]['USD'][$vy]['renew'] = number_format($v['pricing']['renew'][1] * $vy,3); + } + } + } + + return $response; + } + + + + /** + * Adds the service to the remote server. Sets Input errors on failure, + * preventing the service from being added. + * + * @param stdClass $package A stdClass object representing the selected package + * @param array $vars An array of user supplied info to satisfy the request + * @param stdClass $parent_package A stdClass object representing the parent + * service's selected package (if the current service is an addon service) + * @param stdClass $parent_service A stdClass object representing the parent + * service of the service being added (if the current service is an addon service + * and parent service has already been provisioned) + * @param string $status The status of the service being added. These include: + * - active + * - canceled + * - pending + * - suspended + * @return array A numerically indexed array of meta fields to be stored for this service containing: + * - key The key for this meta field + * - value The value for this key + * - encrypted Whether or not this field should be encrypted (default 0, not encrypted) + * @see Module::getModule() + * @see Module::getModuleRow() + */ + public function addService($package, array $vars = null, $parent_package = null, $parent_service = null, $status = 'pending') { + + $tld = null; + $input_fields = []; + + // Set domain fields + if (isset($vars['domain'])) { + $tld = $this->getTld($vars['domain']); + } + + $whois_fields = Configure::get('Domainnameapi.whois_fields'); + $whois_sections = Configure::get('Domainnameapi.whois_sections'); + + if (array_key_exists('auth_info', $vars)) { + $input_fields = array_merge( + Configure::get('Domainnameapi.transfer_fields'), + Configure::get('Domainnameapi.nameserver_fields'), + $whois_fields, + (array)Configure::get('Domainnameapi.domain_fields' . $tld) + ); + } else { + $input_fields = array_merge( + Configure::get('Domainnameapi.domain_fields'), + Configure::get('Domainnameapi.nameserver_fields'), + $whois_fields, + (array)Configure::get('Domainnameapi.domain_fields' . $tld) + ); + } + + if (isset($vars['use_module']) && $vars['use_module'] == 'true') { + // Set registration period + $vars['period'] = 1; + foreach ($package->pricing as $pricing) { + if ($pricing->id == $vars['pricing_id']) { + $vars['period'] = $pricing->term; + break; + } + } + + // Set all whois info from client + if (!isset($this->Clients)) { + Loader::loadModels($this, ['Clients']); + } + if (!isset($this->Contacts)) { + Loader::loadModels($this, ['Contacts']); + } + + $client = $this->Clients->get($vars['client_id']); + if ($client) { + $contact_numbers = $this->Contacts->getNumbers($client->contact_id); + } + + $phone_number_full = trim($this->formatPhone(isset($contact_numbers[0]) ? $contact_numbers[0]->number : '11111111111', $client->country)); + + + + foreach ($whois_fields as $key => $value) { + if (strpos($key, 'FirstName') !== false) { + $vars[$key] = $client->first_name; + } elseif (strpos($key, 'LastName') !== false) { + $vars[$key] = $client->last_name; + } elseif (strpos($key, 'Company') !== false) { + $vars[$key] = $client->company; + } elseif (strpos($key, 'AddressLine1') !== false) { + $vars[$key] = $client->address1; + } elseif (strpos($key, 'AddressLine2') !== false) { + $vars[$key] = $client->address2; + } elseif (strpos($key, 'City') !== false) { + $vars[$key] = $client->city; + } elseif (strpos($key, 'State') !== false) { + $vars[$key] = $client->city; + } elseif (strpos($key, 'ZipCode') !== false) { + $vars[$key] = $client->zip; + } elseif (strpos($key, 'Country') !== false) { + $vars[$key] = $client->country; + } elseif (strpos($key, 'PhoneCountryCode') !== false) { + $vars[$key] = '90'; + } elseif (strpos($key, 'Phone') !== false) { + $vars[$key] = substr($phone_number_full, strpos($phone_number_full, ".") + 1); + } elseif (strpos($key, 'FaxCountryCode') !== false) { + $vars[$key] = '90'; + } elseif (strpos($key, 'Fax') !== false) { + $vars[$key] = substr($phone_number_full, strpos($phone_number_full, ".") + 1); + } elseif (strpos($key, 'EMail') !== false) { + $vars[$key] = $client->email; + } + } + + // Set country for .asia domains + if ($tld == '.asia') { + $vars['tld_data[ced_info][locality_country]'] = $client->country; + } + + $contact_data = []; + $nameserver_data = []; + + foreach ($whois_sections as $k => $section) { + $contact_data[$section] = [ + "FirstName" => $vars[$section . "FirstName"], + "LastName" => $vars[$section . "LastName"], + "Company" => $vars[$section . "Company"], + "EMail" => $vars[$section . "EMail"], + "AddressLine1" => $vars[$section . "AddressLine1"], + "State" => $vars[$section . "State"], + "City" => $vars[$section . "City"], + "Country" => $vars[$section . "Country"], + "Fax" => $vars[$section . "Fax"], + "FaxCountryCode" => '90', + "Phone" => $vars[$section . "Phone"], + "PhoneCountryCode" => '90', + "ZipCode" => $vars[$section . "ZipCode"], + "Status" => '', + "Type" => 'Contact', + ]; + + if (strlen(trim($contact_data[$section]["LastName"])) == 0) { + $contact_data[$section]["LastName"] = $contact_data[$section]["FirstName"]; + } + } + + for ($i = 1; $i <= 4; $i++) { + if (isset($vars['ns'.$i]) && !empty($vars['ns'.$i])) { + $nameserver_data[] = $vars['ns'.$i]; + } + } + + $fields = [ + 'nameservers'=>$nameserver_data, + 'contacts'=>$contact_data, + 'period'=>$vars['period'], + 'domain'=>$vars['domain'], + ]; + + if (isset($vars['transfer']) || isset($vars['auth-code'])) { + $fields['auth-code'] = $vars['auth-code']; + } + + + // Register domain + $this->registerDomain($vars['domain'], $package->module_row, $fields); + + + + if ($this->Input->errors()) { + return; + } + + // Ignore nameserver errors + $this->Input->setErrors([]); + + return [['key' => 'domain', 'value' => $vars['domain'], 'encrypted' => 0]]; + } + + $meta = []; + $fields = array_intersect_key($vars, $input_fields); + foreach ($fields as $key => $value) { + $meta[] = [ + 'key' => $key, + 'value' => $value, + 'encrypted' => 0 + ]; + } + + + return $meta; + } + + /** + * Allows the module to perform an action when the service is ready to renew. + * Sets Input errors on failure, preventing the service from renewing. + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param stdClass $parent_package A stdClass object representing the parent + * service's selected package (if the current service is an addon service) + * @param stdClass $parent_service A stdClass object representing the parent + * service of the service being renewed (if the current service is an addon service) + * @return mixed null to maintain the existing meta fields or a numerically + * indexed array of meta fields to be stored for this service containing: + * - key The key for this meta field + * - value The value for this key + * - encrypted Whether or not this field should be encrypted (default 0, not encrypted) + * @see Module::getModule() + * @see Module::getModuleRow() + */ + public function renewService($package, $service, $parent_package = null, $parent_service = null) + { + + $fields = $this->serviceFieldsToObject($service->fields); + + $vars = [ + 'year' => 1 + ]; + + // Set renew period + foreach ($package->pricing as $pricing) { + if ($pricing->id == $service->pricing_id) { + $vars['year'] = $pricing->term; + break; + } + } + + // Renew domain + $this->renewDomain($fields->domain, $package->module_row, $vars); + + return null; + } + + /** + * Validates input data when attempting to add a package, returns the meta + * data to save when adding a package. Performs any action required to add + * the package on the remote server. Sets Input errors on failure, + * preventing the package from being added. + * + * @param array An array of key/value pairs used to add the package + * @return array A numerically indexed array of meta fields to be stored for this package containing: + * - key The key for this meta field + * - value The value for this key + * - encrypted Whether or not this field should be encrypted (default 0, not encrypted) + * @see Module::getModule() + * @see Module::getModuleRow() + */ + public function addPackage(array $vars = null) + { + $meta = []; + if (isset($vars['meta']) && is_array($vars['meta'])) { + // Return all package meta fields + foreach ($vars['meta'] as $key => $value) { + $meta[] = [ + 'key' => $key, + 'value' => $value, + 'encrypted' => 0 + ]; + } + } + + return $meta; + } + + /** + * Validates input data when attempting to edit a package, returns the meta + * data to save when editing a package. Performs any action required to edit + * the package on the remote server. Sets Input errors on failure, + * preventing the package from being edited. + * + * @param stdClass $package A stdClass object representing the selected package + * @param array An array of key/value pairs used to edit the package + * @return array A numerically indexed array of meta fields to be stored for this package containing: + * - key The key for this meta field + * - value The value for this key + * - encrypted Whether or not this field should be encrypted (default 0, not encrypted) + * @see Module::getModule() + * @see Module::getModuleRow() + */ + public function editPackage($package, array $vars = null) + { + + + $meta = []; + if (isset($vars['meta']) && is_array($vars['meta'])) { + // Return all package meta fields + foreach ($vars['meta'] as $key => $value) { + $meta[] = [ + 'key' => $key, + 'value' => $value, + 'encrypted' => 0 + ]; + } + } + + return $meta; + } + + /** + * Returns the rendered view of the manage module page + * + * @param mixed $module A stdClass object representing the module and its rows + * @param array $vars An array of post data submitted to or on the manage module page + * (used to repopulate fields after an error) + * @return string HTML content containing information to display when viewing the manager module page + */ + public function manageModule($module, array &$vars) + { + // Load the view into this object, so helpers can be automatically added to the view + $this->view = new View('manage', 'default'); + $this->view->base_uri = $this->base_uri; + $this->view->setDefaultView('components' . DS . 'modules' . DS . 'domainnameapi' . DS); + + // Load the helpers required for this view + Loader::loadHelpers($this, ['Form', 'Html', 'Widget']); + + $this->view->set('module', $module); + + return $this->view->fetch(); + } + + /** + * Returns the rendered view of the add module row page + * + * @param array $vars An array of post data submitted to or on the add module + * row page (used to repopulate fields after an error) + * @return string HTML content containing information to display when viewing the add module row page + */ + public function manageAddRow(array &$vars) + { + // Load the view into this object, so helpers can be automatically added to the view + $this->view = new View('add_row', 'default'); + $this->view->base_uri = $this->base_uri; + $this->view->setDefaultView('components' . DS . 'modules' . DS . 'domainnameapi' . DS); + + // Load the helpers required for this view + Loader::loadHelpers($this, ['Form', 'Html', 'Widget']); + + // Set unspecified checkboxes + if (!empty($vars)) { + if (empty($vars['sandbox'])) { + $vars['sandbox'] = 'false'; + } + } + $this->view->set('vars', (object)$vars); + + return $this->view->fetch(); + } + + /** + * Returns the rendered view of the edit module row page + * + * @param stdClass $module_row The stdClass representation of the existing module row + * @param array $vars An array of post data submitted to or on the edit module + * row page (used to repopulate fields after an error) + * @return string HTML content containing information to display when viewing the edit module row page + */ + public function manageEditRow($module_row, array &$vars) + { + + // Load the view into this object, so helpers can be automatically added to the view + $this->view = new View('edit_row', 'default'); + $this->view->base_uri = $this->base_uri; + $this->view->setDefaultView('components' . DS . 'modules' . DS . 'domainnameapi' . DS); + + // Load the helpers required for this view + Loader::loadHelpers($this, ['Form', 'Html', 'Widget']); + + if (empty($vars)) { + $vars = $module_row->meta; + } else { + // Set unspecified checkboxes + if (empty($vars['sandbox'])) { + $vars['sandbox'] = 'false'; + } + } + $this->view->set('vars', (object)$vars); + + return $this->view->fetch(); + } + + /** + * Adds the module row on the remote server. Sets Input errors on failure, + * preventing the row from being added. + * + * @param array $vars An array of module info to add + * @return array A numerically indexed array of meta fields for the module row containing: + * - key The key for this meta field + * - value The value for this key + * - encrypted Whether or not this field should be encrypted (default 0, not encrypted) + */ + public function addModuleRow(array &$vars) + { + $meta_fields = ['user', 'key', 'sandbox']; + $encrypted_fields = ['key']; + + // Set unspecified checkboxes + if (empty($vars['sandbox'])) { + $vars['sandbox'] = 'false'; + } + + $this->Input->setRules($this->getRowRules($vars)); + + // Validate module row + if ($this->Input->validates($vars)) { + // Build the meta data for this row + $meta = []; + foreach ($vars as $key => $value) { + if (in_array($key, $meta_fields)) { + $meta[] = [ + 'key' => $key, + 'value' => $value, + 'encrypted' => in_array($key, $encrypted_fields) ? 1 : 0 + ]; + } + } + + return $meta; + } + } + + /** + * Edits the module row on the remote server. Sets Input errors on failure, + * preventing the row from being updated. + * + * @param stdClass $module_row The stdClass representation of the existing module row + * @param array $vars An array of module info to update + * @return array A numerically indexed array of meta fields for the module row containing: + * - key The key for this meta field + * - value The value for this key + * - encrypted Whether or not this field should be encrypted (default 0, not encrypted) + */ + public function editModuleRow($module_row, array &$vars) + { + // Same as adding + return $this->addModuleRow($vars); + } + + /** + * Returns all fields used when adding/editing a package, including any + * javascript to execute when the page is rendered with these fields. + * + * @param $vars stdClass A stdClass object representing a set of post fields + * @return ModuleFields A ModuleFields object, containing the fields to render + * as well as any additional HTML markup to include + */ + public function getPackageFields($vars = null) + { + Loader::loadHelpers($this, ['Html']); + + + $fields = new ModuleFields(); + + // Set all TLD checkboxes + $tld_options = $fields->label(Language::_('Domainnameapi.package_fields.tld_options', true)); + + $tlds = $this->getTlds(); + sort($tlds); + foreach ($tlds as $tld) { + $tld_label = $fields->label($tld, 'tld_' . $tld); + $tld_options->attach( + $fields->fieldCheckbox( + 'meta[tlds][]', + $tld, + (isset($vars->meta['tlds']) && in_array($tld, $vars->meta['tlds'])), + ['id' => 'tld_' . $tld], + $tld_label + ) + ); + } + $fields->setField($tld_options); + + // Set nameservers + for ($i = 1; $i <= 5; $i++) { + $type = $fields->label(Language::_('Domainnameapi.package_fields.ns' . $i, true), 'domainnameapi_ns' . $i); + $type->attach( + $fields->fieldText( + 'meta[ns][]', + ($vars->meta['ns'][$i - 1] ?? null), + ['id' => 'domainnameapi_ns' . $i] + ) + ); + $fields->setField($type); + } + + return $fields; + } + + /** + * Returns all fields to display to an admin attempting to add a service with the module + * + * @param stdClass $package A stdClass object representing the selected package + * @param $vars stdClass A stdClass object representing a set of post fields + * @return ModuleFields A ModuleFields object, containg the fields to render as well as + * any additional HTML markup to include + */ + public function getAdminAddFields($package, $vars = null) + { + Loader::loadHelpers($this, ['Form', 'Html']); + + // Set default name servers + + if (!isset($vars->ns1) && isset($package->meta->ns)) { + $i = 0; + foreach ($package->meta->ns as $ns) { + $vars->{'ns' . ($i++)} = $ns; + } + } + + // Handle transfer request + if (isset($vars->transfer) || isset($vars->auth_code)) { + return $this->arrayToModuleFields(Configure::get('Domainnameapi.transfer_fields'), null, $vars); + } else { + // Set domain fields + if (isset($vars->domain)) { + $tld = $this->getTld($vars->domain); + } + + // Handle domain registration + $module_fields = $this->arrayToModuleFields( + array_merge( + Configure::get('Domainnameapi.domain_fields'), + Configure::get('Domainnameapi.nameserver_fields'), + isset($tld) ? (array)Configure::get('Domainnameapi.domain_fields' . $tld) : [] + ), + null, + $vars + ); + + // Build the domain fields + $fields = $this->buildDomainModuleFields($vars); + if ($fields) { + $module_fields = $fields; + } + } + + return ($module_fields ?? new ModuleFields()); + } + + /** + * Returns all fields to display to a client attempting to add a service with the module + * + * @param stdClass $package A stdClass object representing the selected package + * @param $vars stdClass A stdClass object representing a set of post fields + * @return ModuleFields A ModuleFields object, containg the fields to render as well + * as any additional HTML markup to include + */ + public function getClientAddFields($package, $vars = null) + { + // Set default name servers + if (!isset($vars->ns) && isset($package->meta->ns)) { + $i = 0; + foreach ($package->meta->ns as $ns) { + $vars->{'ns' . $i++ } = $ns; + } + } + + // Handle transfer request + if (isset($vars->transfer) || isset($vars->auth_code)) { + $fields = Configure::get('Domainnameapi.transfer_fields'); + + // We should already have the domain name don't make editable + $fields['domain']['type'] = 'hidden'; + $fields['domain']['label'] = null; + + return $this->arrayToModuleFields($fields, null, $vars); + } + else { + // Set domain fields + if (isset($vars->domain)) { + $tld = $this->getTld($vars->domain); + } + + // Handle domain registration + $fields = array_merge( + Configure::get('Domainnameapi.nameserver_fields'), + Configure::get('Domainnameapi.domain_fields'), + isset($tld) ? (Configure::get('Domainnameapi.domain_fields' . $tld) ?? []) : [] + ); + + // We should already have the domain name don't make editable + $fields['domain']['type'] = 'hidden'; + $fields['domain']['label'] = null; + + $module_fields = $this->arrayToModuleFields($fields, null, $vars); + + // Build the domain fields + $domain_fields = $this->buildDomainModuleFields($vars, true); + if ($domain_fields) { + $module_fields = $domain_fields; + } + } + + // Determine whether this is an AJAX request + return ($module_fields ?? new ModuleFields()); + } + + /** + * Builds and returns the module fields for domain registration + * + * @param stdClass $vars An stdClass object representing the input vars + * @param $client True if rendering the client view, or false for the admin (optional, default false) + * return mixed The module fields for this service, or false if none could be created + */ + private function buildDomainModuleFields($vars, $client = false) + { + if (isset($vars->domain)) { + $tld = $this->getTld($vars->domain); + + $extension_fields = Configure::get('Domainnameapi.domain_fields' . $tld); + if ($extension_fields) { + // Set the fields + if ($client) { + $fields = array_merge( + Configure::get('Domainnameapi.nameserver_fields'), + Configure::get('Domainnameapi.domain_fields'), + $extension_fields + ); + } else { + $fields = array_merge( + Configure::get('Domainnameapi.domain_fields'), + Configure::get('Domainnameapi.nameserver_fields'), + $extension_fields + ); + } + + if ($client) { + // We should already have the domain name don't make editable + $fields['domain']['type'] = 'hidden'; + $fields['domain']['label'] = null; + } + + // Build the module fields + $module_fields = new ModuleFields(); + + // Allow AJAX requests + $ajax = $module_fields->fieldHidden('allow_ajax', 'true', ['id' => 'domainnameapi_allow_ajax']); + $module_fields->setField($ajax); + $please_select = ['' => Language::_('AppController.select.please', true)]; + + foreach ($fields as $key => $field) { + // Build the field + $label = $module_fields->label((isset($field['label']) ? $field['label'] : ''), $key); + + $type = null; + if ($field['type'] == 'text') { + $type = $module_fields->fieldText( + $key, + ($vars->{$key} ?? ''), + ['id' => $key] + ); + } elseif ($field['type'] == 'select') { + $type = $module_fields->fieldSelect( + $key, + (isset($field['options']) ? $please_select + $field['options'] : $please_select), + ($vars->{$key} ?? ''), + ['id' => $key] + ); + } elseif ($field['type'] == 'hidden') { + $type = $module_fields->fieldHidden( + $key, + ($vars->{$key} ?? ''), + ['id' => $key] + ); + } + + // Include a tooltip if set + if (!empty($field['tooltip'])) { + $label->attach($module_fields->tooltip($field['tooltip'])); + } + + if ($type) { + $label->attach($type); + $module_fields->setField($label); + } + } + } + } + + return ($module_fields ?? false); + } + + /** + * Returns all tabs to display to an admin when managing a service + * + * @param stdClass $service A stdClass object representing the service + * @return array An array of tabs in the format of method => title. + * Example: ['methodName' => "Title", 'methodName2' => "Title2"] + */ + public function getAdminServiceTabs($service) + { + Loader::loadModels($this, ['Packages']); + + $tabs = [ + 'tabWhois' => Language::_('Domainnameapi.tab_whois.title', true), + 'tabNameservers' => Language::_('Domainnameapi.tab_nameservers.title', true), + 'tabSettings' => Language::_('Domainnameapi.tab_settings.title', true) + ]; + + return $tabs; + } + + /** + * Returns all tabs to display to a client when managing a service. + * + * @param stdClass $service A stdClass object representing the service + * @return array An array of tabs in the format of method => title, or method => array where array contains: + * + * - name (required) The name of the link + * - icon (optional) use to display a custom icon + * - href (optional) use to link to a different URL + * Example: + * ['methodName' => "Title", 'methodName2' => "Title2"] + * ['methodName' => ['name' => "Title", 'icon' => "icon"]] + */ + public function getClientServiceTabs($service) + { + Loader::loadModels($this, ['Packages']); + + $tabs = [ + 'tabClientWhois' => [ + 'name' => Language::_('Domainnameapi.tab_whois.title', true), + 'icon' => 'fas fa-users' + ], + 'tabClientNameservers' => [ + 'name' => Language::_('Domainnameapi.tab_nameservers.title', true), + 'icon' => 'fas fa-server' + ], + 'tabClientSettings' => [ + 'name' => Language::_('Domainnameapi.tab_settings.title', true), + 'icon' => 'fas fa-cog' + ] + ]; + + return $tabs; + } + + /** + * Admin Whois tab + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + public function tabWhois($package, $service, array $get = null, array $post = null, array $files = null) + { + return $this->manageWhois('tab_whois', $package, $service, $get, $post, $files); + } + + /** + * Client Whois tab + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + public function tabClientWhois($package, $service, array $get = null, array $post = null, array $files = null) + { + return $this->manageWhois('tab_client_whois', $package, $service, $get, $post, $files); + } + + /** + * Admin Nameservers tab + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + public function tabNameservers($package, $service, array $get = null, array $post = null, array $files = null) + { + return $this->manageNameservers('tab_nameservers', $package, $service, $get, $post, $files); + } + + /** + * Admin Nameservers tab + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + public function tabClientNameservers($package, $service, array $get = null, array $post = null, array $files = null) + { + return $this->manageNameservers('tab_client_nameservers', $package, $service, $get, $post, $files); + } + + /** + * Admin Settings tab + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + public function tabSettings($package, $service, array $get = null, array $post = null, array $files = null) + { + return $this->manageSettings('tab_settings', $package, $service, $get, $post, $files); + } + + /** + * Client Settings tab + * + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + public function tabClientSettings($package, $service, array $get = null, array $post = null, array $files = null) + { + return $this->manageSettings('tab_client_settings', $package, $service, $get, $post, $files); + } + + /** + * Handle updating whois information + * + * @param string $view The view to use + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + private function manageWhois($view, $package, $service, array $get = null, array $post = null, array $files = null) + { + $this->view = new View($view, 'default'); + + // Load the helpers required for this view + Loader::loadHelpers($this, ['Form', 'Html']); + + $vars = new stdClass(); + $whois_fields = Configure::get('Domainnameapi.whois_fields'); + $whois_sections = Configure::get('Domainnameapi.whois_sections'); + $fields = $this->serviceFieldsToObject($service->fields); + + if (!empty($post)) { + // Build contacts array + + $post_data =[]; + + foreach ($whois_sections as $k => $v) { + $post_data[$v]['Type']='Contact'; + } + + foreach ($post as $k => $v) { + + foreach ($whois_sections as $ks => $vs) { + if(substr($k, 0, strlen($vs)) == $vs){ + $post_data[$vs][substr($k, strlen($vs))]=$v; + } + } + + } + + $this->setDomainContacts($fields->domain, $post_data, $package->module_row); + + $vars = (object)$post; + } else { + // Build contacts array + Loader::loadHelpers($this, ['DataStructure']); + $this->Array = $this->DataStructure->create('Array'); + + $contacts = $this->getDomainContacts($fields->domain, $package->module_row); + + + foreach ($whois_sections as $sk => $sv) { + $vars->{$sv.'FirstName'}= $contacts[$sv]['FirstName']; + $vars->{$sv.'LastName'}= $contacts[$sv]['LastName']; + $vars->{$sv.'Company'}= $contacts[$sv]['Company']; + $vars->{$sv.'EMail'}=$contacts[$sv]['EMail']; + $vars->{$sv.'Fax'}=$contacts[$sv]['Phone']['Fax']['Number']; + $vars->{$sv.'AddressLine1'}= $contacts[$sv]['Address']['Line1']; + $vars->{$sv.'AddressLine2'}=$contacts[$sv]['Address']['Line2']; + $vars->{$sv.'City'}=$contacts[$sv]['Address']['City']; + $vars->{$sv.'State'}=$contacts[$sv]['Address']['State']; + $vars->{$sv.'ZipCode'}=$contacts[$sv]['Address']['ZipCode']; + $vars->{$sv.'Country'}=$contacts[$sv]['Address']['Country']; + $vars->{$sv.'PhoneCountryCode'}=$contacts[$sv]['Phone']['Phone']['CountryCode']; + $vars->{$sv.'Phone'}=$contacts[$sv]['Phone']['Phone']['Number']; + $vars->{$sv.'FaxCountryCode'}=$contacts[$sv]['Phone']['Fax']['CountryCode']; + } + + } + + + $this->view->set('vars', $vars); + $this->view->set('fields', $this->arrayToModuleFields($whois_fields, null, $vars)->getFields()); + $this->view->set('sections', $whois_sections); + $this->view->setDefaultView('components' . DS . 'modules' . DS . 'domainnameapi' . DS); + + return $this->view->fetch(); + } + + + + /** + * Handle updating nameserver information + * + * @param string $view The view to use + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + private function manageNameservers($view, $package, $service, array $get = null, array $post = null, array $files = null) { + $this->view = new View($view, 'default'); + + // Load the helpers required for this view + Loader::loadHelpers($this, ['Form', 'Html']); + + $vars = new stdClass(); + $fields = $this->serviceFieldsToObject($service->fields); + + + + if (!empty($post)) { + die(var_dump($post)); + // Update domain nameservers + $this->setDomainNameservers($fields->domain, $package->module_row, ($post['ns'] ?? [])); + $vars = (object)$post; + } else { + // Get domain nameservers + $nameservers = $this->getDomainNameServers($fields->domain, $package->module_row); + + $vars->ns = []; + if (!empty($nameservers)) { + foreach ($nameservers as $ns) { + $vars->ns[] = $ns['url']; + } + } + } + + $this->view->set('vars', $vars); + $this->view->setDefaultView('components' . DS . 'modules' . DS . 'domainnameapi' . DS); + + return $this->view->fetch(); + } + + /** + * Handle updating settings + * + * @param string $view The view to use + * @param stdClass $package A stdClass object representing the current package + * @param stdClass $service A stdClass object representing the current service + * @param array $get Any GET parameters + * @param array $post Any POST parameters + * @param array $files Any FILES parameters + * @return string The string representing the contents of this tab + */ + private function manageSettings($view, $package, $service, array $get = null, array $post = null, array $files = null) { + $this->view = new View($view, 'default'); + + // Load the helpers required for this view + Loader::loadHelpers($this, ['Form', 'Html']); + + $vars = new stdClass(); + $fields = $this->serviceFieldsToObject($service->fields); + + $domainInfo = $this->getDomainInfo($fields->domain, $package->module_row); + + // Determine if this service has access to id_protection + $id_protection = $this->featureServiceEnabled('id_protection', $service); + + // Determine if this service has access to epp_code + $epp_code = $package->meta->epp_code ?? '0'; + + $vars->registrar_lock = $domainInfo['data']['LockStatus']=='true' ? 'true' : 'false'; + $vars->whois_privacy_state = $domainInfo['data']['PrivacyProtectionStatus']=='true' ? 'true' : 'false'; + $vars->epp_code = $domainInfo['data']['AuthCode']; + + if (!empty($post)) { + // Set domain status + if ($post['registrar_lock'] == 'true') { + $this->lockDomain($fields->domain, $package->module_row); + } else { + $this->unlockDomain($fields->domain, $package->module_row); + } + + if($id_protection){ + if($post['whois_privacy_state'] == 'true'){ + $this->enablePrivacyProtection($fields->domain, $package->module_row); + }else{ + $this->disablePrivacyProtection($fields->domain, $package->module_row); + } + } + + } + + + + + + $this->view->set('id_protection', $id_protection); + $this->view->set('epp_code', $epp_code); + $this->view->set('vars', $vars); + $this->view->setDefaultView('components' . DS . 'modules' . DS . 'domainnameapi' . DS); + + return $this->view->fetch(); + } + + /** + * Verifies that the provided domain name is available + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the domain is available, false otherwise + */ + public function checkAvailability($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $tld = $this->extractTld($domain); + $sld = $this->extractSLD($domain); + + $checkAvailability = $api->CheckAvailability([$sld], [$tld],1,'create'); + + + return $checkAvailability[0]['Status'] == 'available'; + } + + /** + * Verifies that the provided domain name is available for transfer + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the domain is available for transfer, false otherwise + */ + public function checkTransferAvailability($domain, $module_row_id = null) + { + // Prevent users from transferring an unregistered domain + return !$this->checkAvailability($domain, $module_row_id); + } + + /** + * Gets a list of contacts associated with a domain + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return array A list of contact objects with the following information: + * + * - external_id The ID of the contact in the registrar + * - email The primary email associated with the contact + * - phone The phone number associated with the contact + * - first_name The first name of the contact + * - last_name The last name of the contact + * - address1 The contact's address + * - address2 The contact's address line two + * - city The contact's city + * - state The 3-character ISO 3166-2 subdivision code + * - zip The zip/postal code for this contact + * - country The 2-character ISO 3166-1 country code + */ + public function getDomainContacts($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $contactResult = $api->GetContacts($domain); + + return $contactResult['data']['contacts']; + } + + /** + * Updates the list of contacts associated with a domain + * + * @param string $domain The domain for which to update contact info + * @param array $vars A list of contact arrays with the following information: + * + * - external_id The ID of the contact in the registrar (optional) + * - email The primary email associated with the contact + * - phone The phone number associated with the contact + * - first_name The first name of the contact + * - last_name The last name of the contact + * - address1 The contact's address + * - address2 The contact's address line two + * - city The contact's city + * - state The 3-character ISO 3166-2 subdivision code + * - zip The zip/postal code for this contact + * - country The 2-character ISO 3166-1 country code + * - * Other fields required by the registrar + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the contacts were updated, false otherwise + */ + public function setDomainContacts($domain, array $vars = [], $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $saveContact =$api->SaveContacts($domain, $vars); + + $this->processResponse($api); + + return $saveContact['status'] == 'OK'; + } + + /** + * Gets a list of basic information for a domain + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return array A list of common domain information + * + * - * The contents of the return vary depending on the registrar + */ + public function getDomainInfo($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $domainInfo = $this->fetchCache('domainInfo_'.$domain, function() use ($api, $domain) { + + return $api->GetDetails($domain); + + + }); + + return $domainInfo; + + } + + /** + * Returns whether the domain has a registrar lock + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the domain has a registrar lock, false otherwise + */ + public function getDomainIsLocked($domain, $module_row_id = null) + { + + $domain = $this->getDomainInfo($domain, $module_row_id); + + return $domain['data']['LockStatus']=='true'; + } + + /** + * Returns whether the domain has WHOIS privacy enabled + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the domain has a registrar lock, false otherwise + */ + private function getDomainIsPrivate($domain, $module_row_id = null) + { + $domain = $this->getDomainInfo($domain, $module_row_id); + + return $domain['data']['PrivacyProtectionStatus']=='true'; + } + + /** + * Gets a list of name server data associated with a domain + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return array A list of name servers, each with the following fields: + * + * - url The URL of the name server + * - ips A list of IPs for the name server + */ + public function getDomainNameServers($domain, $module_row_id = null) + { + $domainInfo = $this->getDomainInfo($domain, $module_row_id); + + $nameservers = []; + + foreach ($domainInfo['data']['NameServers'] as $nameserver) { + $nameservers[] = [ + 'url' => $nameserver ?? '', + 'ips' => [] + ]; + } + + return $nameservers; + } + + /** + * Assign new name servers to a domain + * + * @param string $domain The domain for which to assign new name servers + * @param int|null $module_row_id The ID of the module row to fetch for the current module + * @param array $vars A list of name servers to assign (e.g. [ns1, ns2]) + * @return bool True if the name servers were successfully updated, false otherwise + */ + public function setDomainNameservers($domain, $module_row_id = null, array $vars = []) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + + $nslist = []; + foreach ($vars as $k => $v) { + if(strlen($v)==0) continue; + $nslist['ns'.($k+1)]=$v; + } + + $nameserverResponse = $api->ModifyNameServer($domain, $nslist); + + $this->processResponse($api); + + return $nameserverResponse['status'] == 'OK'; + } + + /** + * Locks the given domain + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the domain was successfully locked, false otherwise + */ + public function lockDomain($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $response = $api->EnableTheftProtectionLock($domain); + $this->processResponse($api); + + return $response['status'] == 'OK'; + } + + /** + * Unlocks the given domain + * + * @param string $domain The domain to lookup + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return bool True if the domain was successfully unlocked, false otherwise + */ + public function unlockDomain($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + + $response = $api->DisableTheftProtectionLock($domain); + $this->processResponse($api); + + return $response['status'] == 'OK'; + } + + public function enablePrivacyProtection($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $response = $api->ModifyPrivacyProtectionStatus($domain,true); + $this->processResponse($api); + + return $response['status'] == 'OK'; + } + public function disablePrivacyProtection($domain, $module_row_id = null) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $response = $api->ModifyPrivacyProtectionStatus($domain,false); + $this->processResponse($api); + + return $response['status'] == 'OK'; + } + + + + /** + * Register a new domain through the registrar + * + * @param string $domain The domain to register + * @param int $module_row_id The ID of the module row to fetch for the current module + * @param array $vars A list of vars to submit with the registration request + * + * - * The contents of $vars vary depending on the registrar + * @return bool True if the domain was successfully registered, false otherwise + */ + public function registerDomain($domain, $module_row_id = null, array $vars = []) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + if($vars['auth_code']){ + + $registerResult = $api->Transfer($domain, $vars['auth_code'], $vars['period']); + + }else{ + + $registerResult = $api->RegisterWithContactInfo( + $domain, + $vars['period'], + $vars['contacts'], + $vars['nameservers'], + true, + false, + $vars['nameservers']??[], + ); + + } + + + + $this->processResponse($api); + + return $registerResult['status'] == 'OK'; + } + + /** + * Transfer a domain through the registrar + * + * @param string $domain The domain to register + * @param int $module_row_id The ID of the module row to fetch for the current module + * @param array $vars A list of vars to submit with the transfer request + * + * - * The contents of $vars vary depending on the registrar + * @return bool True if the domain was successfully transferred, false otherwise + */ + public function transferDomain($domain, $module_row_id = null, array $vars = []) + { + return $this->registerDomain($domain, $module_row_id, $vars); + } + + /** + * Renew a domain through the registrar + * + * @param string $domain The domain to renew + * @param int $module_row_id The ID of the module row to fetch for the current module + * @param array $vars A list of vars to submit with the renew request + * + * - * The contents of $vars vary depending on the registrar + * @return bool True if the domain was successfully renewed, false otherwise + */ + public function renewDomain($domain, $module_row_id = null, array $vars = []) + { + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $renewResponse = $api->Renew($domain, $vars['year']); + + $this->processResponse($api); + + return $renewResponse['status'] == 'OK'; + } + + /** + * Gets the domain expiration date + * + * @param stdClass $service The service belonging to the domain to lookup + * @param string $format The format to return the expiration date in + * @return string The domain expiration date in UTC time in the given format + * @see Services::get() + */ + public function getExpirationDate($service, $format = 'Y-m-d H:i:s') + { + Loader::loadHelpers($this, ['Date']); + + $domain = $this->getServiceDomain($service); + $module_row_id = $service->module_row_id ?? null; + + $row = $this->getModuleRow($module_row_id); + $api = $this->getApi($row->meta->user, $row->meta->key); + + $domainDetail = $api->GetDetails($domain); + + if ($domainDetail['result'] != 'OK') { + return false; + } + + + + return $this->Date->format($domainDetail['data']['Dates']['Expiration']); + } + + /** + * Gets the domain name from the given service + * + * @param stdClass $service The service from which to extract the domain name + * @return string The domain name associated with the service + */ + public function getServiceDomain($service) + { + if (isset($service->fields)) { + foreach ($service->fields as $service_field) { + if ($service_field->key == 'domain') { + return $service_field->value; + } + } + } + + return $this->getServiceName($service); + } + + /** + * Get a list of the TLDs supported by the registrar module + * + * @param int $module_row_id The ID of the module row to fetch for the current module + * @return array A list of all TLDs supported by the registrar module + */ + public function getTlds($module_row_id = null) + { + return Configure::get('Domainnameapi.tlds'); + } + + /** + * Builds and returns the rules required to add/edit a module row + * + * @param array $vars An array of key/value data pairs + * @return array An array of Input rules suitable for Input::setRules() + */ + private function getRowRules(&$vars) + { + return [ + 'user' => [ + 'valid' => [ + 'rule' => 'isEmpty', + 'negate' => true, + 'message' => Language::_('Domainnameapi.!error.user.valid', true) + ] + ], + 'key' => [ + 'valid' => [ + 'last' => true, + 'rule' => 'isEmpty', + 'negate' => true, + 'message' => Language::_('Domainnameapi.!error.key.valid', true) + ], + 'valid_connection' => [ + 'rule' => [ + [$this, 'validateConnection'], + $vars['user'], + $vars['sandbox'] ?? 'false' + ], + 'message' => Language::_('Domainnameapi.!error.key.valid_connection', true) + ] + ] + ]; + } + + /** + * Validates that the given connection details are correct by attempting to check the availability of a domain + * + * @param string $key The API key + * @param string $user The API user + * @param string $sandbox "true" if this is a sandbox account, false otherwise + * @return bool True if the connection details are valid, false otherwise + */ + public function validateConnection($key, $user, $sandbox) + { + $api = $this->getApi($user, $key); + + $resellerdetails = $api->GetResellerDetails(); + + return $resellerdetails['result']=='OK'; + } + + /** + * Initializes the DomainNameApi and returns an instance of that object + * + * @param string $username The user to connect as + * @param string $key The key to use when connecting + * @return DomainNameAPI_PHPLibrary The DomainNameApiInstance instance + * @throws SoapFault + */ + private function getApi($username, $key) + { + + if (!class_exists("\DomainNameApi\DomainNameAPI_PHPLibrary")) { + Loader::load(dirname(__FILE__) . DS . 'apis' . DS . 'api.php'); + } + + return new DomainNameAPI_PHPLibrary($username, $key); + } + + /** + * Process API response, setting an errors, and logging the request + * + * @param DomainNameAPI_PHPLibrary $api The domainnameapi API object + */ + private function processResponse(DomainNameAPI_PHPLibrary $api) + { + $this->logRequest($api); + + $response = $api->getParsedResponseData(); + + // Set errors, if any + if ($response['result'] != 'OK') { + $errors = $response["error"]["Message"] . " : " . $response["error"]["Details"]; + $this->Input->setErrors(['errors' => [$errors]]); + } + } + + /** + * Logs the API request + * + * @param DomainNameAPI_PHPLibrary $api The domainnameapi API object + */ + private function logRequest(DomainNameAPI_PHPLibrary $api) + { + $last_request = $api->getRequestData(); + $last_response = $api->getResponseData(); + $result_success = (is_array($last_response) && is_string(key($last_response)) && $last_response[key($last_response)]["OperationResult"] == "SUCCESS"); + $last_request['request']['Password'] = '******'; + $this->log($api->getServiceUrl() . '|' . $api->getLastFunction(), print_r($last_request, true), 'input', true); + $this->log($api->getServiceUrl() . '|' . $api->getLastFunction(), print_r($last_response, true), 'output', + $result_success); + } + + /** + * Returns the TLD of the given domain + * + * @param string $domain The domain to return the TLD from + * @return string The TLD of the domain + */ + private function getTld($domain) + { + $tlds = $this->getTlds(); + + $domain = strtolower($domain); + foreach ($tlds as $tld) { + if (substr($domain, -strlen($tld)) == $tld) { + return $tld; + } + } + + return strstr($domain, '.'); + } + + /** + * Formats a phone number into +NNN.NNNNNNNNNN + * + * @param string $number The phone number + * @param string $country The ISO 3166-1 alpha2 country code + * @return string The number in +NNN.NNNNNNNNNN + */ + private function formatPhone($number, $country) + { + if (!isset($this->Contacts)) { + Loader::loadModels($this, ['Contacts']); + } + + $number = preg_replace('/[^0-9+]+/', '', $number); + + return trim($this->Contacts->intlNumber($number, $country, '.')); + } + + /** + * Checks if a feature is enabled for a given service + * + * @param string $feature The name of the feature to check if it's enabled (e.g. id_protection) + * @param stdClass $service An object representing the service + * @return bool True if the feature is enabled, false otherwise + */ + private function featureServiceEnabled($feature, $service) + { + // Get service option groups + foreach ($service->options as $option) { + if ($option->option_name == $feature) { + return true; + } + } + + return false; + } + + private function extractTld($domain) + { + $parts = explode('.', $domain); + unset($parts[0]); + if(count($parts) > 2) { + unset($parts[1]); + } + $tld = implode('.', $parts); + return $tld; + } + + private function extractSld($domain) + { + $parts = explode('.', $domain); + $sld = $parts[0]; + return $sld; + } + + private function fetchCache($key, $callback) + { + // Cache anahtarını oluştur + $cacheKey = Configure::get('Blesta.company_id') . DS . 'modules' . DS . 'domainnameapi' . DS . $key; + + // Cache'den veriyi getir + $cache = Cache::fetchCache($cacheKey); + if ($cache) { + return unserialize(base64_decode($cache)); + } + + // Cache'de veri yoksa, callback fonksiyonunu çağır + $response = $callback(); + + // Caching ayarları açıksa ve cache dizini yazılabilir durumda ise, veriyi cache'e kaydet + if (Configure::get('Caching.on') && is_writable(CACHEDIR)) { + try { + Cache::writeCache($cacheKey, base64_encode(serialize($response)), strtotime(Configure::get('Blesta.cache_length')) - time()); + } catch (Exception $e) { + // Hata durumunda log'a yaz + $this->log('error while saving cache: ' . $e->getMessage()); + } + } + + return $response; + } +} diff --git a/components/modules/domainnameapi/language/ar_sa/domainnameapi.php b/components/modules/domainnameapi/language/ar_sa/domainnameapi.php new file mode 100644 index 0000000..b146b3b --- /dev/null +++ b/components/modules/domainnameapi/language/ar_sa/domainnameapi.php @@ -0,0 +1,171 @@ +Widget->clear(); + $this->Widget->create($this->_('Domainnameapi.add_row.box_title', true)); + ?> +
+ Form->create(); + ?> +
+

_('Domainnameapi.add_row.basic_title');?>

+
+
+ +
+ +
+ Form->fieldSubmit('save', $this->_('Domainnameapi.add_row.add_btn', true), ['class' => 'btn btn-primary float-right']); + ?> +
+ Form->end(); + ?> +
+ Widget->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/edit_row.pdt b/components/modules/domainnameapi/views/default/edit_row.pdt new file mode 100644 index 0000000..42b5f4e --- /dev/null +++ b/components/modules/domainnameapi/views/default/edit_row.pdt @@ -0,0 +1,40 @@ + Widget->clear(); + $this->Widget->create($this->_('Domainnameapi.edit_row.box_title', true)); + ?> +
+ Form->create(); + ?> +
+

_('Domainnameapi.edit_row.basic_title');?>

+
+
+ +
+ +
+ Form->fieldSubmit('save', $this->_('Domainnameapi.edit_row.add_btn', true), ['class' => 'btn btn-primary float-right']); + ?> +
+ Form->end(); + ?> +
+ Widget->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/images/logo.png b/components/modules/domainnameapi/views/default/images/logo.png new file mode 100644 index 0000000..edb72e2 Binary files /dev/null and b/components/modules/domainnameapi/views/default/images/logo.png differ diff --git a/components/modules/domainnameapi/views/default/manage.pdt b/components/modules/domainnameapi/views/default/manage.pdt new file mode 100644 index 0000000..e526aa1 --- /dev/null +++ b/components/modules/domainnameapi/views/default/manage.pdt @@ -0,0 +1,99 @@ + [ + 'name' => $this->_('Domainnameapi.add_module_row', true), + 'attributes' => ['href' => $this->base_uri . 'settings/company/modules/addrow/' . $module->id] + ] +]; + +$num_rows = count((isset($module->rows) ? $module->rows : [])); +if ($num_rows > 0) { + unset($link_buttons['add_account']); +} + +$this->Widget->clear(); +$this->Widget->setLinkButtons($link_buttons); + +$this->Widget->create($this->_('AdminCompanyModules.manage.boxtitle_manage', true, + (isset($module->name) ? $this->Html->safe($module->name) : null)), ['id' => 'manage_domainnameapi']); +?> + +
+

_('Domainnameapi.manage.module_rows_title'); ?>

+
+ 0) { + ?> + + + + + + + + > + + + + + + + +
_('Domainnameapi.manage.module_rows_heading.user'); ?>_('Domainnameapi.manage.module_rows_heading.key'); ?>_('Domainnameapi.manage.module_rows_heading.options'); ?>
rows[$i]->meta->user) ? $this->Html->safe($module->rows[$i]->meta->user) : null); ?>rows[$i]->meta->key) ? substr($this->Html->safe($module->rows[$i]->meta->key), + 0, 1) . str_repeat('*', + strlen($this->Html->safe($module->rows[$i]->meta->key)) - 1) : null); ?> + _('Domainnameapi.manage.module_rows.edit'); ?> + Form->create($this->base_uri . 'settings/company/modules/deleterow/'); + $this->Form->fieldHidden('id', ($module->id ?? null)); + $this->Form->fieldHidden('row_id', ($module->rows[$i]->id ?? null)); + ?> + _('Domainnameapi.manage.module_rows.delete'); ?> + Form->end(); + ?> +
+ +
+
+ _('Domainnameapi.manage.module_rows_no_results'); ?> +
+
+ + +Widget->end(); +?> + + \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/service_fields.pdt b/components/modules/domainnameapi/views/default/service_fields.pdt new file mode 100644 index 0000000..d6ea679 --- /dev/null +++ b/components/modules/domainnameapi/views/default/service_fields.pdt @@ -0,0 +1,65 @@ + + + + \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/service_fields_client.pdt b/components/modules/domainnameapi/views/default/service_fields_client.pdt new file mode 100644 index 0000000..b670861 --- /dev/null +++ b/components/modules/domainnameapi/views/default/service_fields_client.pdt @@ -0,0 +1,60 @@ + + +
+ +
+ Form, $field->type], $field->params); + + // Draw each form field associated with this label + $tooltips = []; + foreach ($field->fields as $input) { + // Collect all tooltips to be displayed at the end + if ($input->type == 'tooltip') { + $tooltips[] = $input; + continue; + } + + call_user_func_array([$this->Form, $input->type], $input->params); + + // Draw the form field's secondary label if checkbox or radio item + if (($input->type == 'fieldCheckbox' || $input->type == 'fieldRadio') && isset($input->label)) { + if (isset($input->label->params['attributes']['class'])) { + if (is_array($input->label->params['attributes']['class'])) { + $input->label->params['attributes']['class'][] = 'inline'; + } else { + $input->label->params['attributes']['class'] .= ' inline'; + } + } else { + $input->label->params['attributes']['class'] = 'inline'; + } + + call_user_func_array([$this->Form, 'label'], $input->label->params); + } + } + + foreach ($tooltips as $tooltip) { + ?> + + +
+ +
+ + \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/tab_client_nameservers.pdt b/components/modules/domainnameapi/views/default/tab_client_nameservers.pdt new file mode 100644 index 0000000..6ad9a47 --- /dev/null +++ b/components/modules/domainnameapi/views/default/tab_client_nameservers.pdt @@ -0,0 +1,26 @@ + + Form->create(); + ?> +
+
+ +
+ Form->label($this->_('Domainnameapi.tab_nameserver.field_ns', true, $i + 1), 'ns' . ($i + 1)); + $this->Form->fieldText('ns[]', ($vars->ns[$i] ?? null), ['id' => 'ns' . ($i + 1), 'class' => 'form-control', 'placeholder' => $this->_('Domainnameapi.tab_nameserver.field_ns', true, $i + 1)]); + ?> +
+ +
+
+ + Form->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/tab_client_settings.pdt b/components/modules/domainnameapi/views/default/tab_client_settings.pdt new file mode 100644 index 0000000..72d7423 --- /dev/null +++ b/components/modules/domainnameapi/views/default/tab_client_settings.pdt @@ -0,0 +1,72 @@ + + Form->create(); + ?> +
+
+

_('Domainnameapi.tab_settings.field_registrar_lock');?>

+
+
+ +
+
+ +
+
+
+ +
+

_('Domainnameapi.tab_settings.field_whois_privacy');?>

+
+
+ + +
+
+ +
+
+
+ +
+

_('Domainnameapi.tab_settings.field_epp_code');?>

+
+
+ +
+
+
+
+ + Form->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/tab_client_whois.pdt b/components/modules/domainnameapi/views/default/tab_client_whois.pdt new file mode 100644 index 0000000..687e508 --- /dev/null +++ b/components/modules/domainnameapi/views/default/tab_client_whois.pdt @@ -0,0 +1,101 @@ + + Form->create(); + ?> +
+
+ + +
+ $key) { + ?> +
+ params['for'], $key)) { + continue; + } + ?> +
+ fields as $input) { + // Collect all tooltips to be displayed at the end + if ($input->type == 'tooltip') { + $tooltips[] = $input; + continue; + } + + // Draw the form field's secondary label if checkbox or radio item + if (($input->type == 'fieldCheckbox' || $input->type == 'fieldRadio') && isset($input->label)) { + $type = ($input->type == 'fieldCheckbox' ? 'checkbox' : 'radio'); + ?> +
+ + + + +
+ params['attributes']['class'])) { + if (is_array($input->params['attributes']['class'])) { + $input->params['attributes']['class'][] = 'form-control'; + } else { + $input->params['attributes']['class'] .= ' form-control'; + } + } else { + $input->params['attributes']['class'] = 'form-control'; + } + + $input->params['attributes']['placeholder'] = ($field->params['name'] ?? null); + + // Draw the primary label/field + call_user_func_array([$this->Form, $field->type], $field->params); + call_user_func_array([$this->Form, $input->type], $input->params); + + foreach ($tooltips as $tooltip) { + ?> + + +
+ +
+ +
+
+
+ + Form->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/tab_nameservers.pdt b/components/modules/domainnameapi/views/default/tab_nameservers.pdt new file mode 100644 index 0000000..cff17c3 --- /dev/null +++ b/components/modules/domainnameapi/views/default/tab_nameservers.pdt @@ -0,0 +1,28 @@ + + Form->create(); + ?> +
+ +
+
+ Form->fieldSubmit('save', $this->_('Domainnameapi.tab_nameservers.field_submit', true), ['class' => 'btn btn-primary float-right']); + ?> +
+ Form->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/tab_settings.pdt b/components/modules/domainnameapi/views/default/tab_settings.pdt new file mode 100644 index 0000000..b3b60d5 --- /dev/null +++ b/components/modules/domainnameapi/views/default/tab_settings.pdt @@ -0,0 +1,55 @@ + + Form->create(); + ?> +
+ +
+
+ Form->fieldSubmit('save', $this->_('Domainnameapi.tab_settings.field_submit', true), ['class' => 'btn btn-primary float-right']); + ?> +
+ Form->end(); + ?> \ No newline at end of file diff --git a/components/modules/domainnameapi/views/default/tab_whois.pdt b/components/modules/domainnameapi/views/default/tab_whois.pdt new file mode 100644 index 0000000..ae3bb64 --- /dev/null +++ b/components/modules/domainnameapi/views/default/tab_whois.pdt @@ -0,0 +1,48 @@ + + Form->create(); + ?> + + +
+

_('Domainnameapi.tab_whois.section_' . $key);?>

+
+
+ +
+ +
+ Form->fieldSubmit('save', $this->_('Domainnameapi.tab_whois.field_submit', true), ['class' => 'btn btn-primary float-right']); + ?> +
+ Form->end(); + ?> \ No newline at end of file