Cleaner.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. <?php
  2. namespace MGModule\DNSManager2\mgLibs\custom\task;
  3. use \Exception;
  4. use \MGModule\DNSManager2 as main;
  5. use \MGModule\DNSManager2\mgLibs\custom\helpers\TimeDiffHelper;
  6. use \MGModule\DNSManager2\mgLibs\custom\manager\DefaultNotifications;
  7. use \MGModule\DNSManager2\mgLibs\custom\manager\EmailNotificationHelper;
  8. use \MGModule\DNSManager2\mgLibs\custom\manager\GlobalSettingHelper;
  9. use \MGModule\DNSManager2\mgLibs\custom\manager\NotificationHelper;
  10. use \MGModule\DNSManager2\models\custom\globalsetting\GlobalSettingEnum;
  11. use \MGModule\DNSManager2\mgLibs\custom\manager\LogHelper;
  12. use MGModule\DNSManager2\models\custom\package\item\PackageItemTypeEnum;
  13. use MGModule\DNSManager2\mgLibs\MySQL\query;
  14. use MGModule\DNSManager2\models\custom\reverse;
  15. use MGModule\DNSManager2\mgLibs\custom\dns\utils\IP;
  16. use MGModule\DNSManager2\mgLibs\custom\helpers\IPManagerIntegration;
  17. use \MGModule\DNSManager2\mgLibs\custom\vendor\IPv6;
  18. use \MGModule\DNSManager2\mgLibs\custom\task\TaskTypeCodesCodes;
  19. use \MGModule\DNSManager2\models\custom\zone\Zone;
  20. use \MGModule\DNSManager2\models\custom\task;
  21. class Cleaner extends TaskAbstract
  22. {
  23. protected $abort_after_repeats = array('main' => 0);
  24. protected $taskTypeCode = TaskTypeCodesCodes::CLEANER;
  25. public function mainDescription()
  26. {
  27. return 'Cleaner';
  28. }
  29. public function main($params)
  30. {
  31. $this->setStatus(task\TaskStatusEnum::IN_PROGRESS);
  32. $cron_cleaner_run_each = GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_RUN_EACH ? : 5);
  33. if(!$this->isReadyToRun( $cron_cleaner_run_each ))
  34. {
  35. return true;
  36. }
  37. LogHelper::addSuccessLog($this->mainDescription().' ', 'Cron '.$this->mainDescription().' Started');
  38. $last_id = GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_LAST_ID) ? : 0;
  39. $zones_per_run = GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONES_PER_RUN);
  40. $zones_per_run < 1 ? $zones_per_run = 1 : '';
  41. $rep = $this->getZonesRepoWithIDGreaterThan($last_id, $zones_per_run);
  42. $count_filtered = $rep->count();
  43. $details = array();
  44. $count_removed = 0;
  45. $notify_only = GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_NOTIFY_ONLY);
  46. $notify_only = ($notify_only == 'on' || $notify_only === false) ? true : false;
  47. foreach($rep->get() as $zone)
  48. {
  49. try
  50. {
  51. $remove = false;
  52. $reason = '';
  53. if(GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONE_NO_PACKAGE_MATCH) == 'on' && $zone->getPackage() === false)
  54. {
  55. $remove = true;
  56. $reason = "There is no matching package";
  57. }
  58. $longer_than = GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONE_UNMODIFIED_LONGER_THAN);
  59. if(!$remove && !(empty($longer_than) || $longer_than == 0) && $longer_than <= TimeDiffHelper::diff($zone->updated_at)->minutes)
  60. {
  61. $remove = true;
  62. $reason = "Zone remained unmodified for too long";
  63. }
  64. if(!$remove && GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONE_DOMAIN_NOT_EXIST) == 'on')
  65. {
  66. $result = main\mgLibs\custom\helpers\WHMCSApiHelper::domainwhois(array(
  67. 'domain' => $zone->name,
  68. ));
  69. if(isset($result['status']) && strtolower($result['status']) == 'available')
  70. {
  71. $remove = true;
  72. $reason = 'Zone domain does not exist';
  73. }
  74. }
  75. $hostingStatuses = unserialize(GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONE_INACTIVE_SERVICES));
  76. $domainStatuses = unserialize(GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONE_INACTIVE_DOMAINS));
  77. //domain and hosting in one zone
  78. if(!$remove && !is_null($zone->connectedWithType) && !is_null($zone->connectedWithRelid) && $zone->connectedWithRelid != 0
  79. && !empty($hostingStatuses) && !empty($domainStatuses)
  80. )
  81. {
  82. $hostingId = null;
  83. $domainId = null;
  84. if($zone->type == PackageItemTypeEnum::HOSTING)
  85. {
  86. $hostingId = $zone->relid;
  87. $domainId = $zone->connectedWithRelid;
  88. }
  89. else if($zone->type == PackageItemTypeEnum::DOMAIN)
  90. {
  91. $domainId = $zone->relid;
  92. $hostingId = $zone->connectedWithRelid;
  93. }
  94. $Hosting = main\models\whmcs\service\repository::factory()->setFilter('id', $hostingId)->one();
  95. if($Hosting === false || in_array($Hosting->status(), $hostingStatuses))
  96. {
  97. $removeHosting = true;
  98. $reason = 'Assigned service not active';
  99. }
  100. $domain = main\models\whmcs\domains\repository::factory()->setFilter('id', $domainId)->one();
  101. if($domain === false || in_array($domain->status(), $domainStatuses))
  102. {
  103. $removeDomain = true;
  104. }
  105. if($removeDomain && $removeHosting)
  106. {
  107. $remove = true;
  108. $reason = 'Assigned domain and service not active';
  109. }
  110. if(!$remove)
  111. {
  112. continue;
  113. }
  114. }
  115. //hosting
  116. if(!$remove && $zone->type == PackageItemTypeEnum::PRODUCT && !empty($hostingStatuses))
  117. {
  118. $Hosting = main\models\whmcs\service\repository::factory()->setFilter('id', $zone->relid)->one();
  119. if($Hosting === false || in_array($Hosting->status(), $hostingStatuses))
  120. {
  121. $remove = true;
  122. $reason = 'Assigned service not active';
  123. }
  124. }
  125. //domain
  126. if(!$remove && $zone->type == PackageItemTypeEnum::DOMAIN && !empty($domainStatuses))
  127. {
  128. $domain = main\models\whmcs\domains\repository::factory()->setFilter('id', $zone->relid)->one();
  129. if($domain === false || in_array($domain->status(), $domainStatuses))
  130. {
  131. $remove = true;
  132. $reason = 'Assigned domain not active';
  133. }
  134. }
  135. $at_least_one = unserialize(GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_ZONE_AT_LEAST_ONE));
  136. if(!$remove && !empty($at_least_one))
  137. {
  138. try
  139. {
  140. $module = $zone->getModule();
  141. $records = $module->getRecords();
  142. $available_records = $module->getAvailableRecordTypes();
  143. foreach($at_least_one as $record_type)
  144. {
  145. if(!in_array($record_type, $available_records))
  146. {
  147. continue;
  148. }
  149. foreach($records as $record)
  150. {
  151. if(strtoupper($record->type) == strtoupper($record_type))
  152. {
  153. continue 2;
  154. }
  155. }
  156. $remove = true;
  157. $reason = "Zone does not include required records defined in configuration";
  158. break;
  159. }
  160. }
  161. catch(Exception $exc)
  162. {
  163. \logModuleCall('DNS Manager', 'Cron Cleaner', "Error. When trying to remove dns zone", array($exc->getMessage(), $exc->getTraceAsString()));
  164. }
  165. }
  166. if($remove)
  167. {
  168. try
  169. {
  170. if(!$notify_only)
  171. {
  172. $module = $zone->getModule();
  173. $module->terminateZone();
  174. $zone->remove();
  175. $zoneDataToMail = $zone->toArray()['Zone'];
  176. $zoneDataToMail['zone_name'] = $zoneDataToMail['name'];
  177. main\mgLibs\custom\manager\EmailNotificationHelper::sendClientNotificationUsingZone(main\mgLibs\custom\manager\DefaultNotifications::GENERAL_ZONE_REMOVED_NOTIFICATION, $zone, $zoneDataToMail);
  178. LogHelper::addSuccessLog('Cron Cleaner', 'Remove Zone: '.$zone->name.' Reason: '.$reason);
  179. }
  180. $count_removed++;
  181. $details[$zone->name] = $reason;
  182. }
  183. catch(Exception $exc)
  184. {
  185. \logModuleCall('DNS Manager', 'Cron Cleaner', "Error. When trying to remove dns zone", array($exc->getMessage(), $exc->getTraceAsString()));
  186. }
  187. }
  188. $last_id = $zone->id;
  189. }
  190. catch(Exception $exc)
  191. {
  192. \logModuleCall('DNS Manager', 'Cron Cleaner', "Error. When trying to remove dns zone", array($exc->getMessage(), $exc->getTraceAsString()));
  193. }
  194. }
  195. if($this->getPtrRemoveSettingsStatus())
  196. {
  197. try
  198. {
  199. list($details, $count_removed) = $this->removePTRs($notify_only, $details, $count_removed);
  200. }
  201. catch(Exception $exc)
  202. {
  203. \logModuleCall('DNS Manager', 'Cron Cleaner', "Error. When trying to remove rdns", array($exc->getMessage(), $exc->getTraceAsString()));
  204. }
  205. }
  206. if($count_filtered < $zones_per_run)
  207. {
  208. $last_id = 0;
  209. }
  210. GlobalSettingHelper::setSetting(GlobalSettingEnum::CRON_CLEANER_LAST_ID, $last_id);
  211. if($count_removed > 0)
  212. {
  213. EmailNotificationHelper::sendAdminNotification(
  214. DefaultNotifications::ADMIN_CRON_CLEANER_NOTIFICATION,
  215. array(
  216. 'notify_only' => $notify_only,
  217. 'zones_removed_count' => $count_removed,
  218. 'zones_removed' => $details,
  219. )
  220. );
  221. \logModuleCall('DNS Manager', 'Cron Cleaner', $details, array(), array(
  222. 'zones_removed_count' => $count_removed,
  223. 'zones_removed' => $details,
  224. ), array());
  225. $zonesCountWord = $count_removed == 1 ? 'zone' : 'zones';
  226. if($notify_only)
  227. {
  228. NotificationHelper::addInfoNotification("Cron Cleaner - ".$count_removed." ".$zonesCountWord." should be removed");
  229. LogHelper::addSuccessLog('Cron Cleaner', "End Of Cleaning Run - ".$count_removed." ".$zonesCountWord." should be removed");
  230. }
  231. else
  232. {
  233. NotificationHelper::addInfoNotification("Cron Cleaner - ".$count_removed." ".$zonesCountWord." removed");
  234. LogHelper::addSuccessLog('Cron Cleaner', "End Of Cleaning Run - ".$count_removed." ".$zonesCountWord." removed");
  235. }
  236. }
  237. $this->setStatus(task\TaskStatusEnum::FINISHED);
  238. }
  239. private function getZonesRepoWithIDGreaterThan($id, $zones_per_run)
  240. {
  241. $rep = new main\models\custom\zone\Repository();
  242. $rep->setFilter(0, array(
  243. 'customQuery' => 'id > :last_id ',
  244. 'params' => array('last_id' => $id)
  245. ));
  246. $rep->limit($zones_per_run);
  247. return $rep;
  248. }
  249. private function removePTRs($notify_only, $details = array(), $count_removed = 0)
  250. {
  251. $ptrs = query::query(
  252. "SELECT ptrs.*, h.`id` as `hid`, h.`userid` as `hosuid`, h.`domain`, h.`domainstatus` as `hstatus` "
  253. . "FROM `dns_manager2_reverse` ptrs "
  254. . "LEFT JOIN `tblhosting` h ON (ptrs.`ip` = h.`dedicatedip` OR h.`assignedips` LIKE CONCAT('%', ptrs.`ip`, '%'))"
  255. )->fetchAll();
  256. foreach($ptrs as $ptr)
  257. {
  258. $reverse = new reverse\Reverse($ptr['id']);
  259. $delete = false;
  260. $reason = '';
  261. try
  262. {
  263. $ip = new IP($ptr['ip']);
  264. $intIp = 0;
  265. if($ip->isIPv6())
  266. {
  267. $intIp = IPv6::inet_ptoi((string) $ip);
  268. $shortIP = IPv6::inet_itop($intIp);
  269. $hosting = query::query("SELECT `id`, `domainstatus`, `userid` FROM `tblhosting` WHERE `dedicatedip` = :sip OR `assignedips` LIKE CONCAT('%', :sip2, '%')",
  270. array('sip' => $shortIP, 'sip2' => $shortIP))->fetch();
  271. $ptr['hid'] = $hosting['id'];
  272. $ptr['hstatus'] = $hosting['domainstatus'];
  273. $ptr['hosuid'] = $hosting['userid'];
  274. }
  275. }
  276. catch(Exception $exc)
  277. {
  278. \logModuleCall('DNS Manager', 'Cron Cleaner', "Error. When trying to remove rdns", array($exc->getMessage(), $exc->getTraceAsString()));
  279. continue;
  280. }
  281. if(!$ptr['hid'] && GlobalSettingHelper::getSetting(GlobalSettingEnum::RDNS_CUSTOM_IP) == 'on' && IPManagerIntegration::check())
  282. {
  283. $ipFromIPM = query::query(
  284. "SELECT * "
  285. . "FROM `ip_manager_ips` "
  286. . "WHERE " . (isset($intIp) && is_string($intIp) && $intIp != 0 ? ("`ip_v6` = " . $intIp) : "`ip` = :ip") . " ;"
  287. , array('ip' => $ptr['ip'])
  288. )->fetch();
  289. if(!empty($ipFromIPM))
  290. {
  291. $delete = true;
  292. $reason = 'IP Exist in IP Manager pools but not Active!';
  293. }
  294. }
  295. if(empty($ipFromIPM))
  296. {
  297. if(!$ptr['hid'])
  298. {
  299. $delete = true;
  300. $reason = 'Service #'. $ptr['hid'] .' Not Found!';
  301. }
  302. elseif($ptr['hid'] && $ptr['hstatus'] !== 'Active')
  303. {
  304. switch( $ptr['hstatus'] )
  305. {
  306. case 'Cancelled':
  307. if( (string)GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_PTR_SERVICE_CANCELED) === 'on' )
  308. {
  309. $reason = 'Service #' . $ptr['hid'] . ' is Cancelled!';
  310. $delete = true;
  311. }
  312. break;
  313. case 'Terminated':
  314. if( (string)GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_PTR_SERVICE_TERMINATED) === 'on' )
  315. {
  316. $reason = 'Service #' . $ptr['hid'] . ' is Terminated!';
  317. $delete = true;
  318. }
  319. break;
  320. case 'Suspended':
  321. if( (string)GlobalSettingHelper::getSetting(GlobalSettingEnum::CRON_CLEANER_PTR_SERVICE_SUSPENDED) === 'on' )
  322. {
  323. $reason = 'Service #' . $ptr['hid'] . ' is Suspended!';
  324. $delete = true;
  325. }
  326. break;
  327. }
  328. }
  329. elseif($ptr['clientid'] != $ptr['hosuid'])
  330. {
  331. $delete = true;
  332. $reason = 'Service #' . $ptr['hid'] . ' Not Belong to Clients PTR!';
  333. }
  334. }
  335. if($delete === true)
  336. {
  337. try
  338. {
  339. if(!$notify_only)
  340. {
  341. $helper = new main\mgLibs\custom\reverse\ReverseDNS($reverse);
  342. $zone = new Zone();
  343. $zone->name = $reverse->name;
  344. $zone->serverid = $reverse->serverid;
  345. $module = $zone->getModule();
  346. $helper->cleanRemove();
  347. if($module->zoneExists() && count($module->getRDNSRecord($reverse->ip)) <= 1 && (string)GlobalSettingHelper::getSetting(GlobalSettingEnum::DO_NOT_REMOVE_EMPTY_PTR_ZONES) !== 'on')
  348. {
  349. $module->terminateZone();
  350. }
  351. LogHelper::addSuccessLog('Cron Cleaner', 'Remove PTR record: '.$reverse->name.' Reason: '.$reason);
  352. }
  353. $count_removed++;
  354. $details["RDNS " . $reverse->name] = "" . $reason;
  355. }
  356. catch(Exception $exc)
  357. {
  358. \logModuleCall('DNS Manager', 'Cron Cleaner', "Error. When trying to remove rdns", array($exc->getMessage(), $exc->getTraceAsString()));
  359. }
  360. }
  361. }
  362. return array($details, $count_removed);
  363. }
  364. /**
  365. * @return bool
  366. */
  367. private function getPtrRemoveSettingsStatus()
  368. {
  369. $settings = [
  370. GlobalSettingEnum::CRON_CLEANER_PTR_SERVICE_CANCELED,
  371. GlobalSettingEnum::CRON_CLEANER_PTR_SERVICE_TERMINATED,
  372. GlobalSettingEnum::CRON_CLEANER_PTR_SERVICE_SUSPENDED
  373. ];
  374. foreach( $settings as $value )
  375. {
  376. if( (string)GlobalSettingHelper::getSetting($value) === 'on' )
  377. {
  378. return true;
  379. }
  380. }
  381. return false;
  382. }
  383. }