DigitalOcean.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <?php
  2. namespace MGModule\DNSManager2\mgLibs\custom\dns\submodules;
  3. use \MGModule\DNSManager2\mgLibs\custom\dns;
  4. use \MGModule\DNSManager2\mgLibs\custom\dns\exceptions;
  5. use \MGModule\DNSManager2\mgLibs\custom\dns\interfaces;
  6. use \MGModule\DNSManager2\mgLibs\custom\dns\utils\Patterns;
  7. class DigitalOcean extends dns\SubmoduleAbstract implements
  8. interfaces\SubmoduleIPInterface, interfaces\SubmoduleTTLInterface, interfaces\SubmoduleImportInterface
  9. {
  10. public $configFields = array(
  11. 'token' => array(
  12. 'friendlyName' => 'Access Token',
  13. 'name' => 'apikey', // 1
  14. 'type' => 'text',
  15. ),
  16. 'default_ip' => array(
  17. 'friendlyName' => 'Default IP',
  18. 'name' => 'ip', // 1
  19. 'type' => 'text',
  20. )
  21. );
  22. protected $hostname = 'https://api.digitalocean.com/v2/';
  23. public $availableTypes = array('A', 'AAAA', 'NS', 'MX', 'CNAME', 'SRV', 'TXT');
  24. public function testConnection()
  25. {
  26. $connection = $this->get('domains');
  27. if ($connection === false)
  28. {
  29. return array(
  30. 'status' => false,
  31. 'error' => $this->error
  32. );
  33. }
  34. else
  35. {
  36. return array(
  37. 'status' => true,
  38. 'error' => ''
  39. );
  40. }
  41. }
  42. private function addDot(&$record)
  43. {
  44. $record['type'] = strtoupper($record['type']);
  45. if ($record['data'] != '@' && ($record['type'] == 'SRV' || $record['type'] == 'MX' || $record['type'] == 'NS' || $record['type'] == 'CNAME'))
  46. {
  47. $record['data'] = trim($record['data']);
  48. if (substr($record['data'], strlen($record['data']) - 1) != '.')
  49. {
  50. $record['data'] = $record['data'] . '.';
  51. }
  52. }
  53. }
  54. private function processRecordParams($record, &$params)
  55. {
  56. $method = 'process' . $record->type . 'RecordParams';
  57. if (method_exists($this, $method))
  58. {
  59. $this->$method($record, $params);
  60. }
  61. else
  62. {
  63. $params['data'] = $record->rdata->toString();
  64. }
  65. $this->addDot($params);
  66. }
  67. private function processMXRecordParams($record, &$params)
  68. {
  69. $params['priority'] = $record->rdata->preference;
  70. $params['data'] = $record->rdata->exchange;
  71. }
  72. private function processRecord(dns\record\Record $record, $data)
  73. {
  74. $method = 'process' . $record->type . 'Record';
  75. if (method_exists($this, $method))
  76. {
  77. $this->$method($record, $data);
  78. }
  79. }
  80. private function processMXRecord(dns\record\Record $record, $data)
  81. {
  82. $record->rdata->preference = $data['priority'];
  83. $record->rdata->exchange = ($data['data'] == '@') ? $this->domain : $data['data'];
  84. }
  85. private function processSRVRecord(dns\record\Record $record, $data)
  86. {
  87. $record->rdata->priority = $data['priority'];
  88. $record->rdata->weight = $data['weight'];
  89. $record->rdata->port = $data['port'];
  90. $record->rdata->target = ($data['data'] == '@') ? $this->domain : $data['data'];
  91. }
  92. private function processSRVRecordParams(dns\record\Record $record, &$params)
  93. {
  94. $params['data'] = ($record->rdata->target == $this->domain . '.' || $record->rdata->target == $this->domain) ? '@' : $record->rdata->target;
  95. }
  96. public function getRecords($recordType = false)
  97. {
  98. $return = $this->getRecordsArray($this->getDomainUrl($this->domain, array('records')), $recordType);
  99. return $return;
  100. }
  101. protected function getRecordsArray($url, $recordType = false)
  102. {
  103. $result = $this->get($url);
  104. if ($result == FALSE)
  105. {
  106. throw new exceptions\DNSSubmoduleException('Unable to fetch records from server', dns\SubmoduleExceptionCodes::COMMAND_ERROR);
  107. }
  108. $return = array();
  109. foreach ($result['domain_records'] as $item)
  110. {
  111. if (in_array((string) $item['type'], $recordType !== false ? array(strtoupper($recordType)) : $this->getAvailableRecordTypes() ))
  112. {
  113. $item['data'] = ($item['data'] == '@') ? $this->domain : $item['data'];
  114. $this->addDot($item);
  115. $record = dns\record\Record::tryToCreateFromArray((array) $item);
  116. $record->line = $item['id'];
  117. $record->rdata->fromString((string) $item['data']);
  118. $this->processRecord($record, $item);
  119. $return[] = $record;
  120. }
  121. }
  122. if ((int) $result['meta']['total'] > count($result['domain_records']) &&
  123. isset($result['links']['pages']['next']))
  124. {
  125. $moreRecords = $this->getRecordsArray(array('fullurl' => $result['links']['pages']['next']));
  126. if (!empty($moreRecords))
  127. {
  128. $return = array_merge($return, $moreRecords);
  129. }
  130. }
  131. return $return;
  132. }
  133. public function addRecord(dns\record\Record $record)
  134. {
  135. $params = array(
  136. 'type' => $record->type,
  137. 'name' => $record->nameToAbsolute($this->domain),
  138. 'data' => $record->rdata->toString(),
  139. 'port' => $record->rdata->port,
  140. 'priority' => $record->rdata->priority,
  141. 'ttl' => $record->ttl,
  142. 'weight' => $record->rdata->weight,
  143. );
  144. $this->processRecordParams($record, $params);
  145. $this->get($this->getDomainUrl($this->domain, array('records')), $this->setPOSTparams($params), 'POST');
  146. return true;
  147. }
  148. public function editRecord(dns\record\Record $record)
  149. {
  150. $records = $this->getRecords();
  151. if (empty($records))
  152. {
  153. return false;
  154. }
  155. $lines = array();
  156. foreach ($records as $r)
  157. {
  158. $lines[$r['line']] = $r;
  159. }
  160. if (!isset($lines[$record['line']]) || !$record->isEqualTo($lines[$record['line']]))
  161. {
  162. $input = $record->toMergedArray(false);
  163. $input['domain'] = $this->domain;
  164. $input['name'] = $record->nameToAbsolute($this->domain);
  165. $this->processRecordParams($record, $input);
  166. $this->get($this->getDomainUrl($this->domain, array('records', $record->line)), $this->setPOSTparams($input), 'PUT');
  167. }
  168. }
  169. public function deleteRecord(dns\record\Record $record)
  170. {
  171. $this->get($this->getDomainUrl($this->domain, array('records', $record->line)), array(), 'DELETE');
  172. return true;
  173. }
  174. public function zoneExists()
  175. {
  176. try
  177. {
  178. return $this->getZoneID() !== false;
  179. }
  180. catch (\Exception $ex)
  181. {
  182. return false;
  183. }
  184. }
  185. public function getZoneID()
  186. {
  187. try
  188. {
  189. $out = $this->get($this->getDomainUrl($this->domain, array('records')));
  190. }
  191. catch (\Exception $ex)
  192. {
  193. return false;
  194. }
  195. if ($out === false)
  196. {
  197. return false;
  198. }
  199. foreach ($out['domains'] as $key => $domain)
  200. {
  201. if ($domain == $this->domain)
  202. {
  203. return $domain;
  204. }
  205. }
  206. }
  207. public function activateZone()
  208. {
  209. $params = array('name' => $this->domain, 'ip_address' => $this->config['default_ip']);
  210. $this->get('domains', $this->setPOSTparams($params), 'POST');
  211. return true;
  212. }
  213. public function terminateZone()
  214. {
  215. $this->get($this->getDomainUrl($this->domain), array(), 'DELETE');
  216. }
  217. public function getZones()
  218. {
  219. $out = array();
  220. $zoneResult = $this->get($this->getDomainUrl());
  221. foreach ($zoneResult['domains'] as $zone)
  222. {
  223. $out[(string) $zone['name']] = '';
  224. }
  225. try
  226. {
  227. $res = $this->get('droplets');
  228. foreach ($res['droplets'] as $zone)
  229. {
  230. if (isset($out[(string) $zone['name']]))
  231. {
  232. $out[(string) $zone['name']] = (string) $zone['networks']['v4'][0]['ip_address'];
  233. }
  234. }
  235. }
  236. catch (exceptions\DNSSubmoduleException $exc)
  237. {
  238. }
  239. return $out;
  240. }
  241. protected function setGETParams($params)
  242. {
  243. return array('GET' => $params);
  244. }
  245. protected function setPOSTparams($params)
  246. {
  247. return array('POST' => $params);
  248. }
  249. protected function getDomainUrl($domain = "", $additional = array())
  250. {
  251. if (!empty($additional))
  252. {
  253. $addurl = '';
  254. foreach ($additional as $value)
  255. {
  256. $addurl .= '/' . $value;
  257. }
  258. }
  259. return 'domains/' . $domain . $addurl;
  260. }
  261. public function processURL($url, $get = array())
  262. {
  263. if (isset($url['fullurl']))
  264. {
  265. return $url['fullurl'];
  266. }
  267. if (!empty($get))
  268. {
  269. $additional = '?' . http_build_query($get);
  270. }
  271. return $this->hostname . $url . $additional;
  272. }
  273. private function get($function, $params = array(), $type = 'GET')
  274. {
  275. $url = $this->processURL($function, $params['GET']);
  276. $this->checkData();
  277. $ch = curl_init();
  278. $chOptions = array(
  279. CURLOPT_URL => $url,
  280. CURLOPT_RETURNTRANSFER => true,
  281. CURLOPT_SSL_VERIFYPEER => false,
  282. CURLOPT_SSL_VERIFYHOST => false,
  283. CURLOPT_TIMEOUT => 30,
  284. CURLOPT_HTTPHEADER => array(
  285. 'Content-Type: application/json'
  286. )
  287. );
  288. if (is_array($params['POST']) && !empty($params['POST']))
  289. {
  290. $post = json_encode($params['POST']);
  291. $chOptions[CURLOPT_POSTFIELDS] = $post;
  292. $chOptions[CURLOPT_HTTPHEADER][] = 'Content-Length: ' . strlen($post);
  293. }
  294. $header = 'Authorization: Bearer ' . $this->config['token'];
  295. $chOptions[CURLOPT_HTTPHEADER][] = $header;
  296. if (isset($params['POST']) && $type == 'GET')
  297. {
  298. $type = 'POST';
  299. }
  300. if ($type !== false)
  301. {
  302. $chOptions[CURLOPT_CUSTOMREQUEST] = $type;
  303. }
  304. curl_setopt_array($ch, $chOptions);
  305. $out = curl_exec($ch);
  306. $out_info = curl_getinfo($ch);
  307. if ($type === 'DELETE')
  308. {
  309. if (is_string($out) && strlen($out) == 0)
  310. {
  311. return $out;
  312. }
  313. }
  314. if ($out_info['http_code'] != 200 && ( $out_info['http_code'] == 301 || $out_info['http_code'] == 302 ))
  315. {
  316. throw new exceptions\DNSSubmoduleException('SSL is required', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
  317. }
  318. if ($out === false || $out == '')
  319. {
  320. throw new exceptions\DNSSubmoduleException("cURL Error: " . curl_errno($ch) . " - " . curl_error($ch), dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
  321. }
  322. $decodeInfo = json_decode($out, TRUE);
  323. if (isset($decodeInfo['id']))
  324. {
  325. $this->error = $decodeInfo['id'];
  326. if (isset($decodeInfo['message']))
  327. {
  328. throw new exceptions\DNSSubmoduleException($decodeInfo['message'], dns\SubmoduleExceptionCodes::INVALID_PARAMETERS);
  329. }
  330. }
  331. curl_close($ch);
  332. return $decodeInfo;
  333. }
  334. private function checkData()
  335. {
  336. if ($this->config['token'] == '' || empty($this->config['token']))
  337. {
  338. throw new exceptions\DNSSubmoduleException('Api key is required', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM);
  339. }
  340. }
  341. }