Alternc  latest
Alternc logiel libre pour l'hébergement
m_dom.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  ----------------------------------------------------------------------
5  LICENSE
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License (GPL)
9  as published by the Free Software Foundation; either version 2
10  of the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  To read the license please visit http://www.gnu.org/copyleft/gpl.html
18  ----------------------------------------------------------------------
19 */
20 
21 define('SLAVE_FLAG', "/run/alternc/refresh_slave");
22 
23 /**
24  * Classe de gestion des domaines de l'hébergé.
25  *
26  * Cette classe permet de gérer les domaines / sous-domaines, redirections
27  * dns et mx des domaines d'un membre hébergé.<br />
28  *
29  * @copyright AlternC-Team 2000-2017 https://alternc.com/
30  */
31 class m_dom {
32 
33  /**
34  * $domains : Cache des domaines du membre
35  * @access private
36  */
37  var $domains;
38 
39  /**
40  * $dns : Liste des dns trouvés par la fonction whois
41  * @access private
42  */
43  var $dns;
44 
45  /**
46  * Flag : a-t-on trouvé un sous-domaine Webmail pour ce domaine ?
47  * @access private
48  */
49  var $webmail;
50 
51  /**
52  * Systéme de verrouillage du cron
53  * Ce fichier permet de verrouiller le cron en attendant la validation
54  * du domaine par update_domains.sh
55  * @access private
56  */
57  const fic_lock_cron = "/run/alternc/cron.lock";
58 
59  var $type_local = "VHOST";
60  var $type_url = "URL";
61  var $type_ip = "IP";
62  var $type_webmail = "WEBMAIL";
63  var $type_ipv6 = "IPV6";
64  var $type_cname = "CNAME";
65  var $type_txt = "TXT";
66  var $type_defmx = "DEFMX";
67  var $type_defmx2 = "DEFMX2";
68  var $action_insert = "0";
69  var $action_update = "1";
70  var $action_delete = "2";
73 
74 
75  /**
76  * Constructeur
77  */
78  function m_dom() {
79  global $L_FQDN, $domislocked;
80  $this->tld_no_check_at_all = variable_get('tld_no_check_at_all', 0, 'Disable ALL check on the TLD (users will be able to add any domain)', array('desc' => 'Disabled', 'type' => 'boolean'));
81  variable_get('mailname_bounce', $L_FQDN, 'FQDN of the mail server, used to create vhost virtual mail_adress.', array('desc' => 'FQDN', 'type' => 'string'));
82  $domislocked=false;
83  }
84 
85 
86  function get_panel_url_list() {
87  global $db, $msg;
88  $msg->debug("dom", "get_panel_url_list");
89  $db->query("SELECT sd.id as sub_id, if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine) as fqdn from sub_domaines sd where type = 'PANEL';");
90  $t = array();
91  while ($db->next_record()) {
92  $t[intval($db->f('sub_id'))] = $db->f('fqdn');
93  }
94  return $t;
95  }
96 
97 
98  /**
99  * @param string $fqdn
100  */
101  public static function get_sub_domain_id_and_member_by_name($fqdn) {
102  global $db, $msg;
103  $msg->debug("dom", "get_sub_domain_by_name");
104  $db->query("select sd.* from sub_domaines sd where if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine) = ?;", array($fqdn));
105  if (!$db->next_record()) {
106  return false;
107  }
108  return array('sub_id' => intval($db->f('id')), 'member_id' => intval($db->f('compte')));
109  }
110 
111 
112  /**
113  * hook function called by the menu class
114  * to add menu to the left panel
115  */
116  function hook_menu() {
117  global $quota;
118  $obj = array(
119  'title' => _("Domains"),
120  'link' => 'toggle',
121  'pos' => 20,
122  'links' => array(),
123  );
124 
125  if ($quota->cancreate("dom")) {
126  $obj['links'][] = array(
127  'txt' => _("Add a domain"),
128  'url' => "dom_add.php",
129  );
130  }
131 
132  foreach ($this->enum_domains() as $d) {
133  $obj['links'][] = array(
134  'txt' => htmlentities($d),
135  'url' => "dom_edit.php?domain=" . urlencode($d),
136  );
137  }
138 
139  return $obj;
140  }
141 
142 
143  /**
144  * Retourne un tableau contenant les types de domaines
145  *
146  * @return array retourne un tableau indexé contenant la liste types de domaines
147  * authorisé. Retourne FALSE si une erreur s'est produite.
148  */
149  function domains_type_lst() {
150  global $db, $msg;
151  $msg->debug("dom", "domains_type_lst");
152  if (empty($this->cache_domains_type_lst)) {
153  $db->query("select * from domaines_type order by advanced;");
154  $this->cache_domains_type_lst = array();
155  while ($db->next_record()) {
156  $this->cache_domains_type_lst[strtolower($db->Record["name"])] = $db->Record;
157  }
158  }
160  }
161 
162 
163  // returns array(ALL,NONE,ADMIN)
165  global $db, $msg, $cuid;
166  $msg->debug("dom", "domains_type_enable_values");
167  $db->query("desc domaines_type;");
168  $r = array();
169  while ($db->next_record()) {
170  if ($db->f('Field') == 'enable') {
171  $tab = explode(",", substr($db->f('Type'), 5, -1));
172  foreach ($tab as $t) {
173  $r[] = substr($t, 1, -1);
174  }
175  }
176  }
177  return $r;
178  }
179 
180 
181  /**
182  * @param integer $type
183  * all = 'NONE','URL','DIRECTORY','IP','IPV6','DOMAIN','TXT'
184  */
185  function domains_type_target_values($type = null) {
186  global $db, $msg;
187  $msg->debug("dom", "domains_type_target_values");
188  if (is_null($type)) {
189  $db->query("desc domaines_type;");
190  $r = array();
191  while ($db->next_record()) {
192  if ($db->f('Field') == 'target') {
193  $tab = explode(",", substr($db->f('Type'), 5, -1));
194  foreach ($tab as $t) {
195  $r[] = substr($t, 1, -1);
196  }
197  }
198  }
199  return $r;
200  } else {
201  $db->query("select target from domaines_type where name= ? ;", array($type));
202  if (!$db->next_record()) {
203  return false;
204  }
205  return $db->f('target');
206  }
207  }
208 
209 
210  function import_manual_dns_zone($zone, $domain, $detect_redirect = true, $save = false) {
211  global $msg;
212  if ($save) {
213  if (!$this->import_manual_dns_prep_zone($domain)) {
214  $msg->raise("ERROR", 'dom', _("Err: failed to prepare the zone"));
215  return false;
216  }
217  }
218 
219  $val = array();
220  foreach (explode("\n", $zone) as $z) {
221  $z = trim($z);
222  if (empty($z)) {
223  continue;
224  }
225  $val[] = $this->import_manual_dns_entry($z, $domain, $detect_redirect, $save);
226  }
227  return $val;
228  }
229 
230 
231  /**
232  * @param string $zone
233  */
234  function import_manual_dns_entry($zone, $domain, $detect_redirect = true, $save = false) {
235  global $msg;
236  $msg->log("dom", "import_manual_dns_entry");
237  $zone = trim($zone);
238  if (empty($zone)) {
239  return false;
240  }
241 
242  $domain = trim($domain);
243  if (empty($domain)) {
244  $msg->raise("ERROR", "dom", _("Missing domain name"));
245  return false;
246  }
247 
248  $val = array(
249  'status' => 'err', // can be 'ok', 'err', 'warn'
250  'comment' => 'no val',
251  'entry_old' => $zone,
252  'entry_new' => array('domain' => $domain),
253  );
254 
255  // Examples:
256  // ; hello comment
257  if (preg_match('/^;/', $zone, $ret)) {
258  $val['status'] = 'ok';
259  $val['comment'] = 'Just a comment, do not import';
260  } else
261  // Examples:
262  // $TTL 86400'
263  if (preg_match('/^\$TTL\h+(?P<ttl>[\dMHDmhd]+)/', $zone, $ret)) {
264  $val['status'] = 'ok';
265  $val['comment'] = 'Set TTL to ' . $ret['ttl'];
266  $val['entry_new']['type'] = 'set_ttl';
267  $val['entry_new']['value'] = $ret['ttl'];
268  } else
269 
270  // Examples:
271  // @ IN AAAA 127.2.1.5
272  // reseau IN AAAA 145.214.44.55
273  if (preg_match('/^(?P<sub>[\w\.@\-]*)\h*(?P<ttl>\d*)\h*IN\h+AAAA\h+(?P<target>[0-9A-F:]{2,40})/i', $zone, $ret)) {
274 
275  // Check if it is just a redirect
276  if (substr($ret['sub'], -1) == '.') { // if ending by a "." it is allready a FQDN
277  $url = "http://" . $ret['sub'];
278  } else {
279  if ($ret['sub'] == '@' || empty($ret['sub'])) {
280  $url = "http://" . $domain;
281  } else {
282  $url = "http://" . $ret['sub'] . "." . $domain;
283  }
284  }
285  if ($detect_redirect && $dst_url = $this->is_it_a_redirect($url)) {
286  $val['status'] = 'warn';
287  $val['comment'] = "Became a redirect to $dst_url";
288  $val['entry_new']['type'] = 'URL';
289  $val['entry_new']['sub'] = $ret['sub'];
290  $val['entry_new']['value'] = $dst_url;
291  } else {
292  $val['status'] = 'ok';
293  $val['comment'] = "Create entry AAAA with " . $ret['sub'] . " go to " . $ret['target'] . " with ttl " . $ret['ttl'];
294  $val['entry_new']['type'] = 'IPV6';
295  $val['entry_new']['sub'] = $ret['sub'];
296  $val['entry_new']['value'] = $ret['target'];
297  }
298  } else
299 
300 
301  // Examples:
302  // @ IN A 127.2.1.5
303  // reseau IN A 145.214.44.55
304  if (preg_match('/^(?P<sub>[\w\.@\-]*)\h*(?P<ttl>\d*)\h*IN\h+A\h+(?P<target>\d+\.\d+\.\d+\.\d+)/i', $zone, $ret)) {
305  // Check if it is just a redirect
306  if (substr($ret['sub'], -1) == '.') { // if ending by a "." it is allready a FQDN
307  $url = "http://" . $ret['sub'];
308  } else {
309  if ($ret['sub'] == '@' || empty($ret['sub'])) {
310  $url = "http://" . $domain;
311  } else {
312  $url = "http://" . $ret['sub'] . "." . $domain;
313  }
314  }
315  if ($detect_redirect && $dst_url = $this->is_it_a_redirect($url)) {
316  $val['status'] = 'warn';
317  $val['comment'] = "Became a redirect to $dst_url";
318  $val['entry_new']['type'] = 'URL';
319  $val['entry_new']['sub'] = $ret['sub'];
320  $val['entry_new']['value'] = $dst_url;
321  } else {
322  $val['status'] = 'ok';
323  $val['comment'] = "Create entry A with " . $ret['sub'] . " go to " . $ret['target'] . " with ttl " . $ret['ttl'];
324  $val['entry_new']['type'] = 'IP';
325  $val['entry_new']['sub'] = $ret['sub'];
326  $val['entry_new']['value'] = $ret['target'];
327  }
328  } else
329 
330  // Examples:
331  // @ IN NS ns.example.tld.
332  // ns 3600 IN NS 145.214.44.55
333  if (preg_match('/^(?P<sub>[\-\w\.@]*)\h*(?P<ttl>\d*)\h*IN\h+NS\h+(?P<target>[\w\.\-]+)/i', $zone, $ret)) {
334  if (empty($ret['sub']) || $ret['sub'] == '@') {
335  $val['status'] = 'warn';
336  $val['comment'] = "Won't migrate it, there will get a new value";
337  } else {
338  $val['status'] = 'ok';
339  $val['comment'] = "Create entry NS with " . $ret['sub'] . " go to " . $ret['target'] . " with ttl " . $ret['ttl'];
340  $val['entry_new']['type'] = 'FIXME-NS';
341  $val['entry_new']['sub'] = $ret['sub'];
342  $val['entry_new']['value'] = $ret['target'];
343  }
344  } else
345 
346  // Examples:
347  // agenda IN CNAME ghs.google.com.
348  // www 3600 IN CNAME @
349  if (preg_match('/^(?P<sub>[\-\w\.@]*)\h*(?P<ttl>\d*)\h*IN\h+CNAME\h+(?P<target>[@\w+\.\-]+)/i', $zone, $ret)) {
350  if (substr($ret['sub'], -1) == '.') { // if ending by a "." it is allready a FQDN
351  $url = "http://" . $ret['sub'];
352  } else {
353  if ($ret['sub'] == '@' || empty($ret['sub'])) {
354  $url = "http://" . $domain;
355  } else {
356  $url = "http://" . $ret['sub'] . "." . $domain;
357  }
358  }
359  if ($detect_redirect && $dst_url = $this->is_it_a_redirect($url)) {
360  $val['status'] = 'warn';
361  $val['comment'] = "Became a redirect to $dst_url";
362  $val['entry_new']['type'] = 'URL';
363  $val['entry_new']['sub'] = $ret['sub'];
364  $val['entry_new']['value'] = $dst_url;
365  } else {
366  $val['status'] = 'ok';
367  $val['comment'] = "Create entry CNAME with " . $ret['sub'] . " go to " . $ret['target'] . " with ttl " . $ret['ttl'];
368  $val['entry_new']['type'] = 'CNAME';
369  $val['entry_new']['sub'] = $ret['sub'];
370  $val['entry_new']['value'] = $ret['target'];
371  }
372  } else
373 
374  // Examples:
375  // @ IN MX 10 aspmx.l.google.com.
376  // arf 3600 IN MX 20 pouet.fr.
377  if (preg_match('/^(?P<sub>[\-\w\.@]*)\h*(?P<ttl>\d*)\h*IN\h+MX\h+(?P<weight>\d+)\h+(?P<target>[@\w+\.\-]+)/i', $zone, $ret)) {
378  $val['status'] = 'warn';
379  $val['comment'] = "Create entry MX with " . $ret['sub'] . " go to " . $ret['target'] . " with ttl " . $ret['ttl'] . " and weight 5 (initial weight was " . $ret['weight'] . ")";
380  $val['entry_new']['type'] = 'MX';
381  $val['entry_new']['sub'] = $ret['sub'];
382  $val['entry_new']['value'] = $ret['target'];
383  } else
384 
385  // Examples:
386  // _sip._tcp IN SRV 1 100 5061 sip.example.tld.
387  if (preg_match('/^(?P<sub>[\_\w\.@\-]+)\h+(?P<ttl>\d*)\h*IN\h+SRV\h+/i', $zone, $ret)) {
388  $val['status'] = 'err';
389  $val['comment'] = "Please add yourself the entry $zone";
390  } else
391 
392  // Examples:
393  // @ IN TXT "google-site-verification=jjjjjjjjjjjjjjjjjjjjjjjjsdsdjlksjdljdslgNj5"
394  if (preg_match('/^(?P<sub>[\_\w\.@\-]*)\h*(?P<ttl>\d*)\h*IN\h+TXT\h+\"(?P<target>.+)\"/i', $zone, $ret)) {
395  $val['status'] = 'ok';
396  $val['comment'] = "Create TXT entry with " . $ret['sub'] . " go to " . $ret['target'];
397  $val['entry_new']['type'] = 'TXT';
398  $val['entry_new']['sub'] = $ret['sub'];
399  $val['entry_new']['value'] = $ret['target'];
400  } else {
401 
402  // WTF can it be ?
403  $val['comment'] = "Unknow: $zone";
404  }
405 
406  if ($save) {
407  return $this->import_manual_dns_entry_doit($val);
408  }
409 
410  return $val;
411  }
412 
413 
414  private function import_manual_dns_entry_doit($entry) {
415  $entry['did_it'] = 0;
416  if ($entry['status'] == 'err') {
417  return $entry;
418  }
419 
420  $val = $entry['entry_new'];
421 
422  if (empty($val['type'])) {
423  return false;
424  }
425 
426  switch ($val['type']) {
427  case "set_ttl":
428  $entry['did_it'] = $this->set_ttl($this->get_domain_byname($val['domain']), $val['value']);
429  return $entry;
430  }
431 
432  // If it is an unknown domains type
433  if (!array_key_exists(strtolower($val['type']), $this->domains_type_lst())) {
434  echo "what is this shit ?\n";
435  print_r($entry);
436  return $entry;
437  }
438 
439  // If the subdomain is @, we want an empty subdomain
440  if ($val['sub'] == '@') {
441  $val['sub'] = '';
442  }
443 
444  $this->lock();
445  $entry['did_it'] = $this->set_sub_domain($val['domain'], $val['sub'], $val['type'], $val['value']);
446  $this->unlock();
447 
448  return $entry;
449  }
450 
451 
452  private function import_manual_dns_prep_zone($domain) {
453  global $msg;
454  // Prepare a domain to be importer :
455  // * create the domain
456  // * delete all automatic subdomain
457  // * set no mx
458  $this->lock();
459 
460  // function add_domain($domain,$dns,$noerase=0,$force=0,$isslave=0,$slavedom="")
461  if (!$this->add_domain($domain, true, false, true)) {
462  $msg->raise("ERROR", 'dom', "Error adding domain");
463  return false;
464  }
465 
466  // Set no mx
467  $this->edit_domain($domain, true, false);
468 
469  $d = $this->get_domain_all($domain);
470  foreach ($d['sub'] as $sd) {
471  $this->del_sub_domain($sd['id']);
472  }
473 
474  $this->unlock();
475 
476  return true;
477  }
478 
479 
480  // Take an URL, and return FALSE is there is no redirection,
481  // and the target URL if there is one (HTTP CODE 301 & 302)
482  // CURL is needed
483 
484  /**
485  * @param string $url
486  */
487  function is_it_a_redirect($url) {
488  try {
489  $params = array('http' => array(
490  'method' => 'HEAD',
491  'ignore_errors' => true
492  ));
493 
494  $context = stream_context_create($params);
495  $fp = @fopen($url, 'rb', false, $context);
496  $result = @stream_get_contents($fp);
497 
498  if ($result === false) {
499  throw new Exception("Could not read data from {$url}");
500  }
501  if (strstr($http_response_header[0], '301') || strstr($http_response_header[0], '302')) {
502  // This is a redirection
503  if (preg_match('/Location:\h+(?P<target>[\-\w:\/.\?\=.]+)/', implode("\n", $http_response_header), $ret)) {
504  // check if it is a redirection to himself
505  preg_match('/\/\/(?P<host>[\w\.\-]+)\//', ( substr($url, -1) == '/' ? $url : $url . '/'), $original_cname);
506  preg_match('/\/\/(?P<host>[\w\.\-]+)\//', $ret['target'], $target_url);
507  if (isset($target_url['host']) && ( $target_url['host'] == $original_cname['host'] )) { // if it's a redirection to himself (sub pages, http to https...)
508  return false; // do not do a redirection (we must point to the server)
509  }
510 
511  // If it is a redirection to a sub directory
512  // (we know it is a redirection to a sub directory because it's not a complete URI)
513  if (substr($ret['target'], 0, 4) != 'http') {
514  return 'http://' . $original_cname['host'] . '/' . $ret['target'];
515  }
516  return $ret['target'];
517  }
518  } else { // it isn't a redirection
519  return false;
520  }
521  } catch (Exception $e) {
522  return false;
523  }
524  }
525 
526 
527  function domains_type_regenerate($name) {
528  global $db, $msg, $cuid;
529  $db->query("update sub_domaines set web_action='UPDATE' where lower(type) = lower(?) ;", array($name));
530  $db->query("update domaines d, sub_domaines sd set d.dns_action = 'UPDATE' where lower(sd.type)=lower(?);", array($name));
531  return true;
532  }
533 
534 
535  function domains_type_get($name) {
536  global $db;
537  $db->query("select * from domaines_type where name= ?;", array($name));
538  $db->next_record();
539  return $db->Record;
540  }
541 
542 
543  function domains_type_del($name) {
544  global $db;
545  $db->query("delete domaines_type where name= ? ;", array($name));
546  return true;
547  }
548 
549 
550  function domains_type_update($name, $description, $target, $entry, $compatibility, $enable, $only_dns, $need_dns, $advanced, $create_tmpdir, $create_targetdir,$has_https_option=0) {
551  global $msg, $db;
552  // The name MUST contain only letter and digits, it's an identifier after all ...
553  if (!preg_match("#^[a-z0-9]+$#", $name)) {
554  $msg->raise("ERROR", "dom", _("The name MUST contain only letter and digits"));
555  return false;
556  }
557  $only_dns = intval($only_dns);
558  $need_dns = intval($need_dns);
559  $advanced = intval($advanced);
560  $has_https_option = intval($has_https_option);
561  $create_tmpdir = intval($create_tmpdir);
562  $create_targetdir = intval($create_targetdir);
563  $db->query("UPDATE domaines_type SET description= ?, target= ?, entry= ?, compatibility= ?, enable= ?, need_dns= ?, only_dns= ?, advanced= ?,create_tmpdir= ?,create_targetdir= ?, has_https_option=? where name= ?;", array($description, $target, $entry, $compatibility, $enable, $need_dns, $only_dns, $advanced, $create_tmpdir, $create_targetdir, $has_https_option, $name));
564  return true;
565  }
566 
567 
568  function sub_domain_change_status($sub_id, $status) {
569  global $db, $msg;
570  $msg->log("dom", "sub_domain_change_status");
571  $sub_id = intval($sub_id);
572  $status = strtoupper($status);
573  if (!in_array($status, array('ENABLE', 'DISABLE'))) {
574  return false;
575  }
576 
577  $jh = $this->get_sub_domain_all($sub_id);
578  if ($status == 'ENABLE') { // check compatibility with existing sub_domains
579  if (!$this->can_create_subdomain($jh['domain'], $jh['name'], $jh['type'], $sub_id)) {
580  $msg->raise("ERROR", "dom", _("The parameters for this subdomain and domain type are invalid. Please check for subdomain entries incompatibility"));
581  return false;
582  }
583  }
584 
585  $db->query("update sub_domaines set enable= ? where id = ? ;", array($status, intval($sub_id)));
586  $this->set_dns_action($jh['domain'], 'UPDATE');
587 
588  return true;
589  }
590 
591 
592  /**
593  * Retourne un tableau contenant les domaines d'un membre.
594  * Par défaut le membre connecté
595  *
596  * @return array retourne un tableau indexé contenant la liste des
597  * domaines hébergés sur le compte courant. Retourne FALSE si une
598  * erreur s'est produite.
599  */
600  function enum_domains($uid = -1) {
601  global $db, $msg, $cuid;
602  $msg->debug("dom", "enum_domains");
603  if ($uid == -1) {
604  $uid = $cuid;
605  }
606  $db->query("SELECT * FROM domaines WHERE compte= ? ORDER BY domaine ASC;", array($uid));
607  $this->domains = array();
608  if ($db->num_rows() > 0) {
609  while ($db->next_record()) {
610  $this->domains[] = $db->f("domaine");
611  }
612  }
613  return $this->domains;
614  }
615 
616  function del_domain_cancel($dom) {
617  global $db, $msg, $classes, $cuid;
618  $msg->log("dom", "del_domain_cancel", $dom);
619  $dom = strtolower($dom);
620  $db->query("UPDATE sub_domaines SET web_action='UPDATE' WHERE domaine= ?;", array($dom));
621  $this->set_dns_action($dom, 'UPDATE');
622  # TODO : some work with domain sensitive classes
623  return true;
624  }
625 
626 
627  /**
628  * Efface un domaine du membre courant, et tous ses sous-domaines
629  *
630  * Cette fonction efface un domaine et tous ses sous-domaines, ainsi que
631  * les autres services attachés é celui-ci. Elle appelle donc les autres
632  * classe. Chaque classe peut déclarer une fonction del_dom qui sera
633  * appellée lors de la destruction d'un domaine.
634  *
635  * @param string $dom nom de domaine é effacer
636  * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon.
637  */
638  function del_domain($domain) {
639  global $db, $msg, $hooks;
640  $msg->log("dom", "del_domain", $domain);
641  $domain = strtolower($domain);
642 
643  $this->lock();
644  if (!$r = $this->get_domain_all($domain)) {
645  return false;
646  }
647  $this->unlock();
648 
649  // Call Hooks to delete the domain and the MX management:
650  // TODO : the 2 calls below are using an OLD hook call, FIXME: remove them when unused
651  $hooks->invoke("alternc_del_domain", array($domain));
652  $hooks->invoke("alternc_del_mx_domain", array($domain));
653  // New hook calls:
654  $hooks->invoke("hook_dom_del_domain", array($r["id"]));
655  $hooks->invoke("hook_dom_del_mx_domain", array($r["id"]));
656 
657  // Now mark the domain for deletion:
658  $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ?;", array($domain));
659  $this->set_dns_action($domain, 'DELETE');
660 
661  return true;
662  }
663 
664 
665  function domshort($domain, $sub = "") {
666  return str_replace("-", "", str_replace(".", "", empty($sub) ? "" : "$sub.") . $domain);
667  }
668 
669 
670  /**
671  * Installe un domaine sur le compte courant.
672  *
673  * <p>Si le domaine existe déjé ou est interdit, ou est celui du serveur,
674  * l'installation est refusée. Si l'hébergement DNS est demandé, la fonction
675  * checkhostallow vérifiera que le domaine peut étre installé conformément
676  * aux demandes des super-admin.
677  * Si le dns n'est pas demandé, le domaine peut étre installé s'il est en
678  * seconde main d'un tld (exemple : test.eu.org ou test.com, mais pas
679  * toto.test.org ou test.test.asso.fr)</p>
680  * <p>Chaque classe peut définir une fonction add_dom($dom) qui sera
681  * appellée lors de l'installation d'un nouveau domaine.</p>
682  *
683  * @param boolean $dns 1 ou 0 pour héberger le DNS du domaine ou pas.
684  * @param boolean $noerase 1 ou 0 pour rendre le domaine inamovible ou non
685  * @param boolean $force 1 ou 0, si 1, n'effectue pas les tests de DNS.
686  * force ne devrait étre utilisé que par le super-admin.
687  * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon.
688  */
689  function add_domain($domain, $dns, $noerase = false, $force = false, $isslave = false, $slavedom = "") {
690  global $db, $msg, $quota, $L_FQDN, $tld, $cuid, $hooks, $domislocked;
691  $msg->log("dom", "add_domain", $domain);
692 
693  // Locked ?
694  if (!$domislocked) {
695  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
696  return false;
697  }
698  // Verifie que le domaine est rfc-compliant
699  $domain = strtolower($domain);
700  $t = checkfqdn($domain);
701  if ($t) {
702  $msg->raise("ERROR", "dom", _("The domain name is syntaxically incorrect"));
703  return false;
704  }
705  // Interdit les domaines clés (table forbidden_domains) sauf en cas FORCE
706  $db->query("SELECT domain FROM forbidden_domains WHERE domain= ? ;", array($domain));
707  if ($db->num_rows() && !$force) {
708  $msg->raise("ERROR", "dom", _("The requested domain is forbidden in this server, please contact the administrator"));
709  return false;
710  }
711  if ($domain == $L_FQDN || $domain == "www.$L_FQDN") {
712  $msg->raise("ERROR", "dom", _("This domain is the server's domain! You cannot host it on your account!"));
713  return false;
714  }
715  $db->query("SELECT compte FROM domaines WHERE domaine= ?;", array($domain));
716  if ($db->num_rows()) {
717  $msg->raise("ERROR", "dom", _("The domain already exist"));
718  return false;
719  }
720  $db->query("SELECT compte FROM `sub_domaines` WHERE sub != \"\" AND concat( sub, \".\", domaine )= ? OR domaine= ?;", array($domain, $domain));
721  if ($db->num_rows()) {
722  $msg->raise("ERROR", "dom", _("The domain already exist"));
723  return false;
724  }
725  $this->dns = $this->whois($domain);
726  if (!$force) {
727  $v = checkhostallow($domain, $this->dns);
728  if ($v == -1) {
729  $msg->raise("ERROR", "dom", _("The last member of the domain name is incorrect or cannot be hosted in that server"));
730  return false;
731  }
732  if ($dns && $v == -2) {
733  $msg->raise("ALERT", "dom", _("The domain cannot be found in the whois database"));
734  return false;
735  }
736  if ($dns && $v == -3) {
737  $msg->raise("ALERT", "dom", _("The domain cannot be found in the whois database"));
738  return false;
739  }
740 
741  if ($dns) {
742  $dns = "1";
743  } else {
744  $dns = "0";
745  }
746  // mode 5 : force DNS to NO.
747  if ($tld[$v] == 5) {
748  $dns = 0;
749  }
750  // It must be a real domain (no subdomain)
751  if (!$dns) {
752  $v = checkhostallow_nodns($domain);
753  if ($v) {
754  $msg->raise("ERROR", "dom", _("The requested domain is forbidden in this server, please contact the administrator"));
755  return false;
756  }
757  }
758  }
759  // Check the quota :
760  if (!$quota->cancreate("dom")) {
761  $msg->raise("ALERT", "dom", _("Your domain quota is over, you cannot create more domain names"));
762  return false;
763  }
764  if ($noerase) {
765  $noerase = "1";
766  } else {
767  $noerase = "0";
768  }
769  if ($dns) {
770  $gesmx = "1";
771  } else {
772  $gesmx = "0"; // do not host mx by default if not hosting the DNS
773  }
774  $db->query("INSERT INTO domaines (compte,domaine,gesdns,gesmx,noerase,dns_action) VALUES (?, ?, ?, ?, ?, 'UPDATE');", array($cuid,$domain,$dns,$gesmx,$noerase));
775  if (!($id = $db->lastid())) {
776  $msg->raise("ERROR", "dom", _("An unexpected error occured when creating the domain"));
777  return false;
778  }
779 
780  if ($isslave) {
781  $isslave = true;
782  $db->query("SELECT domaine FROM domaines WHERE compte= ? AND domaine= ?;", array($cuid, $slavedom));
783  $db->next_record();
784  if (!$db->Record["domaine"]) {
785  $msg->raise("ERROR", "dom", _("Domain '%s' not found"), $slavedom);
786  $isslave = false;
787  }
788  // Point to the master domain :
789  $this->create_default_subdomains($domain, $slavedom);
790  }
791  if (!$isslave) {
792  $this->create_default_subdomains($domain);
793  }
794 
795  // TODO: Old hooks, FIXME: when unused remove them
796  $hooks->invoke("alternc_add_domain", array($domain));
797  if ($isslave) {
798  $hooks->invoke("alternc_add_slave_domain", array($domain));
799  }
800  // New Hooks:
801  $hooks->invoke("hook_dom_add_domain", array($id));
802  if ($gesmx) {
803  $hooks->invoke("hook_dom_add_mx_domain", array($id));
804  }
805  if ($isslave) {
806  $hooks->invoke("hook_dom_add_slave_domain", array($id, $slavedom));
807  }
808  return true;
809  }
810 
811 
812  /**
813  * @param string $domain
814  */
815  function create_default_subdomains($domain, $target_domain = "") {
816  global $db, $msg;
817  $msg->log("dom", "create_default_subdomains", $domain);
818  $query = "SELECT sub, domain_type, domain_type_parameter FROM default_subdomains WHERE (concerned = 'SLAVE' or concerned = 'BOTH') and enabled=1;";
819  if (empty($target_domain)) {
820  $query = "SELECT sub, domain_type, domain_type_parameter FROM default_subdomains WHERE (concerned = 'MAIN' or concerned = 'BOTH') and enabled=1;";
821  }
822  $domaindir = $this->domdefaultdir($domain);
823  $db->query($query);
824  $jj = array();
825  while ($db->next_record()) {
826  $jj[] = Array("domain_type_parameter" => $db->f('domain_type_parameter'), "sub" => $db->f('sub'), "domain_type" => $db->f('domain_type'));
827  }
828  $src_var = array("%%SUB%%", "%%DOMAIN%%", "%%DOMAINDIR%%", "%%TARGETDOM%%");
829  foreach ($jj as $j) {
830  $trg_var = array($j['sub'], $domain, $domaindir, $target_domain);
831  $domain_type_parameter = str_ireplace($src_var, $trg_var, $j['domain_type_parameter']);
832  $this->set_sub_domain($domain, $j['sub'], strtolower($j['domain_type']), $domain_type_parameter);
833  }
834  }
835 
836 
837  /**
838  * @param string $domain
839  */
840  function domdefaultdir($domain) {
841  return "/www/" . $this->domshort($domain);
842  }
843 
844 
845  function dump_axfr($domain, $ns = 'localhost') {
846  $axfr = array();
847  exec('/usr/bin/dig AXFR "' . escapeshellcmd($domain) . '" @"' . escapeshellcmd($ns) . '"', $axfr);
848  return $axfr;
849  }
850 
851 
853  global $db, $msg;
854  $msg->debug("dom", "lst_default_subdomains");
855  $c = array();
856  $db->query("select * from default_subdomains;");
857 
858  while ($db->next_record()) {
859  $c[] = array('id' => $db->f('id'),
860  'sub' => $db->f('sub'),
861  'domain_type' => $db->f('domain_type'),
862  'domain_type_parameter' => $db->f('domain_type_parameter'),
863  'concerned' => $db->f('concerned'),
864  'enabled' => $db->f('enabled')
865  );
866  }
867 
868  return $c;
869  }
870 
871 
872  function update_default_subdomains($arr) {
873  global $msg;
874  $msg->log("dom", "update_default_subdomains");
875  $ok = true;
876  foreach ($arr as $a) {
877  if (!isset($a['id'])) {
878  $a['id'] = null;
879  }
880  if (!empty($a['sub']) || !empty($a['domain_type_parameter'])) {
881 
882  if (!isset($a['enabled'])) {
883  $a['enabled'] = 0;
884  }
885  if (!$this->update_one_default($a['domain_type'], $a['sub'], $a['domain_type_parameter'], $a['concerned'], $a['enabled'], $a['id'])) {
886  $ok = false;
887  }
888  }
889  }
890  return $ok;
891  }
892 
893 
894  function update_one_default($domain_type, $sub, $domain_type_parameter, $concerned, $enabled, $id = null) {
895  global $db, $msg;
896  $msg->log("dom", "update_one_default");
897 
898  if ($id == null) {
899  $db->query("INSERT INTO default_subdomains values ('', ?, ?, ?, ?, ?);", array($sub, $domain_type, $domain_type_parameter, $concerned, $enabled));
900  } else {
901  $db->query("UPDATE default_subdomains set sub= ?, domain_type= ?, domain_type_parameter= ?, concerned= ?, enabled= ? where id= ?;", array($sub, $domain_type, $domain_type_parameter, $concerned, $enabled, $id));
902  }
903  return true;
904  //update
905  }
906 
907 
908  function del_default_type($id) {
909  global $msg, $db;
910  $msg->log("dom", "del_default_type");
911 
912  if (!$db->query("delete from default_subdomains where id= ?;", array($id))) {
913  $msg->raise("ERROR", "dom", _("Could not delete default type"));
914  return false;
915  }
916 
917  return true;
918  }
919 
920 
921  /**
922  * Return the NS of a server by interrogating its parent zone.
923  *
924  * @param string $domain FQDN we are searching for
925  * @return array Return the authoritative NS of this domain
926  * or FALSE if an error occurred
927  *
928  */
929  function whois($domain) {
930  global $msg;
931  $msg->debug("dom", "whois", $domain);
932 
933  $domain=trim($domain,"."); // strip initial/final .
934  $parent=$domain; $loopmax=32;
935  do {
936  $parent=substr($parent,strpos($parent,".")+1);
937  $parent=trim($parent,".");
938  if (!$parent) {
939  $msg->raise("ALERT", "dom", _("The domain has no parent. Check syntax"));
940  return false; // no . in this fqdn??
941  }
942  // ask the parent for its NS (no +trace)
943  $out=array();
944  exec("dig +short NS ".escapeshellarg($parent),$out);
945  $loopmax--;
946  } while (!count($out) && $loopmax); // will stop when : we have no parent, or
947  if (!count($out)) {
948  return false; // bad exit of the loop
949  }
950  $parentns=trim($out[0]);
951 
952  // we take the first NS of the SOA of the parent and interrogate it for the child domain:
953  $out=array();
954  exec("dig NS ".escapeshellarg($domain)." ".escapeshellarg("@".$parentns),$out);
955  // we scan the dig result for authoritative information :
956  $ns=array();
957  foreach($out as $line) {
958  if (preg_match('#^'.str_replace(".","\\.",$domain).'\..*IN\s*NS\s*(.*)$#',$line,$mat)) {
959  $ns[]=trim($mat[1]);
960  }
961  }
962  return $ns;
963  } // whois
964 
965 
966  /**
967  * vérifie la presence d'un champs mx valide sur un serveur DNS
968  * $domaine est le domaine dont on veux véririfer les MX
969  * $ref_domaine est le domaine avec lequel on veux comparer les MX
970  * si $ref_domaine == '', on prend les MX par default
971  *
972  * @param string $domaine
973  */
974  function checkmx($domaine, $ref_domain = '') {
975  global $L_DEFAULT_MX, $L_DEFAULT_SECONDARY_MX;
976 
977  $ref_mx = array();
978  $mxhosts = array();
979  if (!empty($ref_domain)) {
980  getmxrr($ref_domain, $ref_mx);
981  } else {
982  $ref_mx = array($L_DEFAULT_MX, $L_DEFAULT_SECONDARY_MX);
983  }
984 
985  if (empty($ref_mx)) {
986  // No reference mx
987  return 3;
988  }
989 
990  //récupére les champs mx
991  if (!getmxrr($domaine, $mxhosts)) {
992  //aucun héte mx spécifié
993  return 1;
994  }
995 
996  if (empty($mxhosts)) {
997  // no mx on the target domaine
998  return 1;
999  }
1000 
1001  $intersect = array_intersect($mxhosts, $ref_mx);
1002 
1003  if (empty($intersect)) {
1004  // no shared mx server
1005  return 2;
1006  }
1007 
1008  return 0;
1009  }
1010 
1011 
1012  /**
1013  * retourne TOUTES les infos d'un domaine
1014  *
1015  * @param string $dom Domaine dont on souhaite les informations
1016  * @return array Retourne toutes les infos du domaine sous la forme d'un
1017  * tableau associatif comme suit :<br /><pre>
1018  * $r["name"] = Nom fqdn
1019  * $r["dns"] = Gestion du dns ou pas ?
1020  * $r["mx"] = Valeur du champs MX si "dns"=true
1021  * $r["mail"] = Heberge-t-on le mail ou pas ? (si "dns"=false)
1022  * $r["nsub"] = Nombre de sous-domaines
1023  * $r["sub"] = tableau associatif des sous-domaines
1024  * $r["sub"][0-(nsub-1)]["name"] = nom du sous-domaine (NON-complet)
1025  * $r["sub"][0-(nsub-1)]["dest"] = Destination (url, ip, local ...)
1026  * $r["sub"][0-(nsub-1)]["type"] = Type (0-n) de la redirection.
1027  * $r["sub"][0-(nsub-1)]["https"] = is https properly enabled for this subdomain? (http/https/both)
1028  * </pre>
1029  * Retourne FALSE si une erreur s'est produite.
1030  *
1031  */
1032  function get_domain_all($dom) {
1033  global $db, $msg, $cuid, $domislocked;
1034  $msg->debug("dom", "get_domain_all", $dom);
1035  // Locked ?
1036  if (!$domislocked) {
1037  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1038  return false;
1039  }
1040  $t = checkfqdn($dom);
1041  if ($t) {
1042  $msg->raise("ERROR", "dom", _("The domain name is syntaxically incorrect"));
1043  return false;
1044  }
1045  $r = array();
1046  $r["name"] = $dom;
1047  $db->query("SELECT * FROM domaines WHERE compte= ? AND domaine= ?;", array($cuid, $dom));
1048  if ($db->num_rows() == 0) {
1049  $msg->raise("ERROR", "dom", sprintf(_("Domain '%s' not found"), $dom));
1050  return false;
1051  }
1052  $db->next_record();
1053  $r["id"] = $db->Record["id"];
1054  $r["dns"] = $db->Record["gesdns"];
1055  $r["dns_action"] = $db->Record["dns_action"];
1056  $r["dns_result"] = $db->Record["dns_result"];
1057  $r["mail"] = $db->Record["gesmx"];
1058  $r["zonettl"] = $db->Record["zonettl"];
1059  $r['noerase'] = $db->Record['noerase'];
1060  $db->free();
1061  $db->query("SELECT COUNT(*) AS cnt FROM sub_domaines WHERE compte= ? AND domaine= ?;", array($cuid, $dom));
1062  $db->next_record();
1063  $r["nsub"] = $db->Record["cnt"];
1064  $db->free();
1065  $db->query("SELECT sd.*, dt.description AS type_desc, dt.only_dns, dt.advanced, dt.has_https_option FROM sub_domaines sd LEFT JOIN domaines_type dt on UPPER(dt.name)=UPPER(sd.type) WHERE compte= ? AND domaine= ? ORDER BY dt.advanced,sd.sub,sd.type ;", array($cuid, $dom));
1066  // Pas de webmail, on le cochera si on le trouve.
1067  $r["sub"] = array();
1068  $i=0;
1069  while ($record=$db->fetch()) {
1070  $r["sub"][$i] = $record;
1071  // FIXME : replace sub by name and dest by valeur in the code that exploits this function :
1072  $r["sub"][$i]["name"] = $record["sub"];
1073  $r["sub"][$i]["dest"] = $record["valeur"];
1074  $r["sub"][$i]["fqdn"] = ((!empty($r["sub"][$i]["name"])) ? $r["sub"][$i]["name"] . "." : "") . $r["name"];
1075  $i++;
1076  }
1077  $db->free();
1078  return $r;
1079  } // get_domain_all
1080 
1081 
1082  /**
1083  * Retourne TOUTES les infos d'un sous domaine du compte courant.
1084  *
1085  * @param integer sub_domain_id id du subdomain
1086  * @return array Retourne un tableau associatif contenant les
1087  * informations du sous-domaine demandé.<pre>
1088  * $r["name"]= nom du sous-domaine (NON-complet)
1089  * $r["dest"]= Destination (url, ip, local ...)
1090  * </pre>
1091  * $r["type"]= Type (0-n) de la redirection.
1092  * Retourne FALSE si une erreur s'est produite.
1093  */
1094  function get_sub_domain_all($sub_domain_id) {
1095  global $db, $msg, $cuid, $domislocked;
1096  $msg->debug("dom", "get_sub_domain_all", $sub_domain_id);
1097  // Locked ?
1098  if (!$domislocked) {
1099  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1100  return false;
1101  }
1102  $db->query("select sd.*, dt.description as type_desc, dt.only_dns, dt.advanced from sub_domaines sd, domaines_type dt where compte= ? and sd.id= ? and upper(dt.name)=upper(sd.type) ORDER BY dt.advanced, sd.sub;", array($cuid, $sub_domain_id));
1103  if ($db->num_rows() == 0) {
1104  $msg->raise("ERROR", "dom", _("The sub-domain does not exist"));
1105  return false;
1106  }
1107  $db->next_record();
1108  $r = array();
1109  $r["id"] = $db->Record["id"];
1110  $r["name"] = $db->Record["sub"];
1111  $r["domain"] = $db->Record["domaine"];
1112  $r["dest"] = $db->Record["valeur"];
1113  $r["enable"] = $db->Record["enable"];
1114  $r["type"] = $db->Record["type"];
1115  $r["type_desc"] = $db->Record["type_desc"];
1116  $r["only_dns"] = $db->Record["only_dns"];
1117  $r["web_action"] = $db->Record["web_action"];
1118  $r["https"] = $db->Record["https"];
1119  $db->free();
1120  return $r;
1121  } // get_sub_domain_all
1122 
1123 
1124  function clean_https_value($type, $https) {
1125  global $db;
1126  $db->query("select has_https_option from domaines_type where name= ? ;", array($type));
1127  if (!$db->next_record()) {
1128  return "";
1129  }
1130  if ($db->Record["has_https_option"]) {
1131  $https=strtolower($https);
1132  if ($https!="http" && $https!="https" && $https!="both") {
1133  return "both";
1134  }
1135  return $https;
1136  } else return "";
1137  }
1138 
1139 
1140  /**
1141  * @param integer $type
1142  * @param string $value
1143  */
1144  function check_type_value($type, $value) {
1145  global $msg;
1146 
1147  // check the type we can have in domaines_type.target
1148  switch ($this->domains_type_target_values($type)) {
1149  case 'NONE':
1150  if (empty($value) or is_null($value)) {
1151  return true;
1152  }
1153  break;
1154  case 'URL':
1155  if ($value == strval($value)) {
1156  if (filter_var($value, FILTER_VALIDATE_URL)) {
1157  return true;
1158  } else {
1159  $msg->raise("ERROR", "dom", _("invalid url"));
1160  return false;
1161  }
1162  }
1163  break;
1164  case 'DIRECTORY':
1165  if (substr($value, 0, 1) != "/") {
1166  $value = "/" . $value;
1167  }
1168  if (!checkuserpath($value)) {
1169  $msg->raise("ERROR", "dom", _("The folder you entered is incorrect or does not exist"));
1170  return false;
1171  }
1172  return true;
1173  case 'IP':
1174  if (checkip($value)) {
1175  return true;
1176  } else {
1177  $msg->raise("ERROR", "dom", _("The ip address is invalid"));
1178  return false;
1179  }
1180  break;
1181  case 'IPV6':
1182  if (checkipv6($value)) {
1183  return true;
1184  } else {
1185  $msg->raise("ERROR", "dom", _("The ip address is invalid"));
1186  return false;
1187  }
1188  break;
1189  case 'DOMAIN':
1190  if (checkcname($value)) {
1191  return true;
1192  } else {
1193  $msg->raise("ERROR", "dom", _("The name you entered is incorrect or not fully qualified (it must end with a DOT, like example.com<b>.</b>)"));
1194  return false;
1195  }
1196  break;
1197  case 'TXT':
1198  if ($value == strval($value)) {
1199  return true;
1200  } else {
1201  $msg->raise("ERROR", "dom", _("The TXT value you entered is incorrect"));
1202  return false;
1203  }
1204  break;
1205  default:
1206  $msg->raise("ERROR", "dom", _("Invalid domain type selected, please check"));
1207  return false;
1208  }
1209  return false;
1210  }
1211 
1212 
1213  /**
1214  * Check the compatibility of the POSTed parameters with the chosen
1215  * domain type
1216  *
1217  * @param string $dom FQDN of the domain name
1218  * @param string $sub SUBdomain
1219  * @return boolean tell you if the subdomain can be installed there
1220  */
1221  function can_create_subdomain($dom, $sub, $type, $sub_domain_id = 0) {
1222  global $db, $msg;
1223 
1224  $sub_domain_id=intval($sub_domain_id);
1225  $msg->log("dom", "can_create_subdomain", $dom . "/" . $sub . "/" .$type . "/" . $sub_domain_id);
1226 
1227  // Get the compatibility list for this domain type
1228  $db->query("select upper(compatibility) as compatibility from domaines_type where upper(name)=upper(?);", array($type));
1229  if (!$db->next_record()) {
1230  return false;
1231  }
1232  $compatibility_lst = explode(",", $db->f('compatibility'));
1233 
1234  // Get the list of type of subdomains already here who have the same name
1235  $db->query("select * from sub_domaines where sub= ? and domaine= ? and not id = ? and web_action != 'DELETE' and enable not in ('DISABLED', 'DISABLE') ", array($sub, $dom, $sub_domain_id));
1236  #$db->query("select * from sub_domaines where sub='$sub' and domaine='$dom';");
1237  while ($db->next_record()) {
1238  // And if there is a domain with a incompatible type, return false
1239  if (!in_array(strtoupper($db->f('type')), $compatibility_lst)) {
1240  return false;
1241  }
1242  }
1243 
1244  // Forbidden to create a CNAME RR on the domain APEX (RFC 1912)
1245  if ($type == 'cname' && $sub == '')
1246  return false;
1247 
1248  // All is right, go ! Create ur domain !
1249  return true;
1250  }
1251 
1252 
1253  /**
1254  * set the HTTPS preference for a subdomain.
1255  * @param integer the sub_domain_id (will be checked against the user ID identity)
1256  * @param string the provider (if not empty, will be checked against an existing certificate for this subdomain)
1257  * @return boolean true if the preference has been set
1258  */
1259  function set_subdomain_ssl_provider($sub_domain_id,$provider) {
1260  global $db, $msg, $cuid, $ssl, $domislocked;
1261  $msg->log("dom", "set_sub_domain_ssl_provider", $sub_domain_id." / ".$provider);
1262  // Locked ?
1263  if (!$domislocked) {
1264  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1265  return false;
1266  }
1267  $db->query("SELECT * FROM sub_domaines WHERE id=?",array($sub_domain_id));
1268  if (!$db->next_record() || $db->Record["compte"]!=$cuid) {
1269  $msg->raise("ERROR", "dom", _("Subdomain not found"));
1270  return false;
1271  }
1272  $fqdn=$db->Record["sub"].(($db->Record["sub"])?".":"").$db->Record["domaine"];
1273  $certs = $ssl->get_valid_certs($fqdn);
1274  $provider=strtolower(trim($provider));
1275  if ($provider) {
1276  $found=false;
1277  foreach($certs as $cert) {
1278  if ($cert["provider"]==$provider) {
1279  $found=true;
1280  }
1281  }
1282  if (!$found) {
1283  $msg->raise("ERROR", "dom", _("No certificate found for this provider and this subdomain"));
1284  return false;
1285  }
1286  }
1287  $db->query("UPDATE sub_domaines SET web_action=?, provider=? WHERE id=?",array("UPDATE",$provider,$sub_domain_id));
1288  return true;
1289  }
1290 
1291 
1292  /**
1293  * Modifier les information du sous-domaine demandé.
1294  *
1295  * <b>Note</b> : si le sous-domaine $sub.$dom n'existe pas, il est créé.<br />
1296  * <b>Note : TODO</b> : vérification de concordance de $dest<br />
1297  *
1298  * @param string $dom Domaine dont on souhaite modifier/ajouter un sous domaine
1299  * @param string $sub Sous domaine é modifier / créer
1300  * @param integer $type Type de sous-domaine (local, ip, url ...)
1301  * @param string $dest Destination du sous-domaine, dépend de la valeur
1302  * de $type (url, ip, dossier...)
1303  * @param string $https the HTTPS behavior : HTTP(redirect https to http),
1304  * HTTPS(redirect http to https) or BOTH (both hosted at the same place)
1305  * or nothing "" when not applicable for this domain type.
1306  * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon.
1307  */
1308  function set_sub_domain($dom, $sub, $type, $dest, $sub_domain_id = 0, $https="") {
1309  global $db, $msg, $cuid, $bro, $domislocked;
1310  $msg->log("dom", "set_sub_domain", $dom . "/" . $sub . "/" . $type . "/" . $dest);
1311  // Locked ?
1312  if (!$domislocked) {
1313  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1314  return false;
1315  }
1316  $dest = trim($dest);
1317  $sub = trim(trim($sub), ".");
1318  $dom = strtolower($dom);
1319  $sub = strtolower($sub);
1320  // if (!(($sub == '*') || ($sub=="") || (preg_match('/([a-z0-9][\.\-a-z0-9]*)?[a-z0-9]/', $sub)))) {
1321  $fqdn = checkfqdn($sub);
1322  // Special cases : * (all subdomains at once) and '' empty subdomain are allowed.
1323  if (($sub != '*' && $sub != '') && !($fqdn == 0 || $fqdn == 4)) {
1324  $msg->raise("ALERT", "dom", _("There is some forbidden characters in the sub domain (only A-Z 0-9 and - are allowed)"));
1325  return false;
1326  }
1327 
1328  if (!$this->check_type_value($type, $dest)) {
1329  // error raised by check_type_value
1330  return false;
1331  }
1332  $https=$this->clean_https_value($type, $https);
1333 
1334  // On a épuré $dir des problémes eventuels ... On est en DESSOUS du dossier de l'utilisateur.
1335  if (($t = checkfqdn($dom))) {
1336  $msg->raise("ERROR", "dom", _("The domain name is syntaxically incorrect"));
1337  return false;
1338  }
1339 
1340  if (!$this->can_create_subdomain($dom, $sub, $type, $sub_domain_id)) {
1341  $msg->raise("ERROR", "dom", _("The parameters for this subdomain and domain type are invalid. Please check for subdomain entries incompatibility"));
1342  return false;
1343  }
1344 
1345  if ($sub_domain_id!=0) { // It's not a creation, it's an edit. Delete the old one
1346  $this->del_sub_domain($sub_domain_id);
1347  }
1348 
1349  // Re-create the one we want
1350  if (!$db->query("INSERT INTO sub_domaines (compte,domaine,sub,valeur,type,web_action,https) VALUES (?, ?, ?, ?, ?, 'UPDATE',?);", array( $cuid , $dom , $sub , $dest , $type, $https ))) {
1351  $msg->raise("ERROR", "dom", _("The parameters for this subdomain and domain type are invalid. Please check for subdomain entries incompatibility"));
1352  return false;
1353  }
1354 
1355  // Create TMP dir and TARGET dir if needed by the domains_type
1356  $dest_root = $bro->get_userid_root($cuid);
1357  //$domshort = $this->domshort($dom, $sub);
1358  $db->query("select create_tmpdir, create_targetdir from domaines_type where name = ?;", array($type));
1359  $db->next_record();
1360  if ($db->f('create_tmpdir')) {
1361  if (!is_dir($dest_root . "/tmp")) {
1362  if (!@mkdir($dest_root . "/tmp", 0777, true)) {
1363  $msg->raise("ERROR", "dom", _("Cannot write to the destination folder"));
1364  }
1365  }
1366  }
1367  if ($db->f('create_targetdir')) {
1368  $dirr = $dest_root . $dest;
1369  $dirr = str_replace('//', '/', $dirr);
1370 
1371  if (!is_dir($dirr)) {
1372  $old = umask(0);
1373  if (!@mkdir($dirr, 0770, true)) {
1374  $msg->raise("ERROR", "dom", _("Cannot write to the destination folder"));
1375  }
1376  umask($old);
1377  }
1378  }
1379 
1380  // Tell to update the DNS file
1381  $db->query("update domaines set dns_action='UPDATE' where domaine= ?;", array($dom));
1382 
1383  return true;
1384  }
1385 
1386 
1387  /**
1388  * Supprime le sous-domaine demandé
1389  *
1390  * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon.
1391  *
1392  */
1393  function del_sub_domain($sub_domain_id) {
1394  global $db, $msg, $domislocked;
1395  $msg->log("dom", "del_sub_domain", $sub_domain_id);
1396  // Locked ?
1397  if (!$domislocked) {
1398  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1399  return false;
1400  }
1401  if (!$r = $this->get_sub_domain_all($sub_domain_id)) {
1402  $msg->raise("ERROR", "dom", _("The sub-domain does not exist"));
1403  return false;
1404  } else {
1405  $db->query("update sub_domaines set web_action='DELETE' where id= ?; ", array($sub_domain_id));
1406  $db->query("update domaines set dns_action='UPDATE' where domaine= ?;", array($r['domain']));
1407  }
1408  return true;
1409  }
1410 
1411 
1412  /**
1413  * @param integer $dom_id
1414  */
1415  function set_ttl($dom_id, $ttl) {
1416  global $msg;
1417  $msg->log("dom", "set_ttl", "$dom_id / $ttl");
1418  $this->lock();
1419  $domaine = $this->get_domain_byid($dom_id);
1420  $d = $this->get_domain_all($domaine);
1421 
1422  $j = $this->edit_domain($domaine, $d['dns'], $d['mail'], false, $ttl);
1423  $this->unlock();
1424  return $j;
1425  }
1426 
1427 
1428  /**
1429  * Modifie les information du domaine précisé.
1430  *
1431  * @param string $dom Domaine du compte courant que l'on souhaite modifier
1432  * @param boolean $dns Vaut 1 ou 0 pour héberger ou pas le DNS du domaine
1433  * @param boolean $gesmx Héberge-t-on le emails du domaines sur ce serveur ?
1434  * @param boolean $force Faut-il passer les checks DNS ou MX ? (admin only)
1435  * @return boolean appelle $mail->add_dom ou $ma->del_dom si besoin, en
1436  * fonction du champs MX. Retourne FALSE si une erreur s'est produite,
1437  * TRUE sinon.
1438  *
1439  */
1440  function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 3600) {
1441  global $db, $msg, $hooks, $domislocked;
1442  $msg->log("dom", "edit_domain", $dom . "/" . $dns . "/" . $gesmx);
1443  // Locked ?
1444  if (!$domislocked && !$force) {
1445  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1446  return false;
1447  }
1448  if ($dns == true && !$force) {
1449  $this->dns = $this->whois($dom);
1450  $v = checkhostallow($dom, $this->dns);
1451  if ($v == -1) {
1452  $msg->raise("ERROR", "dom", _("The last member of the domain name is incorrect or cannot be hosted in that server"));
1453  return false;
1454  }
1455  if ($dns && $v == -2) {
1456  $msg->raise("ALERT", "dom", _("The domain cannot be found in the Whois database"));
1457  return false;
1458  }
1459  if ($dns && $v == -3) {
1460  $msg->raise("ALERT", "dom", _("The DNS of this domain do not match the server's DNS. Please change your domain's DNS before you install it again"));
1461  return false;
1462  }
1463  }
1464 
1465  // Can't have ttl == 0. There is also a check in function_dns
1466  if ($ttl == 0) {
1467  $ttl = 3600;
1468  }
1469 
1470  $t = checkfqdn($dom);
1471  if ($t) {
1472  $msg->raise("ERROR", "dom", _("The domain name is syntaxically incorrect"));
1473  return false;
1474  }
1475  if (!$r = $this->get_domain_all($dom)) {
1476  // Le domaine n'existe pas, Failure
1477  $msg->raise("ERROR", "dom", _("The domain name %s does not exist"), $dom);
1478  return false;
1479  }
1480  if ($dns != "1") {
1481  $dns = "0";
1482  }
1483  // On vérifie que des modifications ont bien eu lieu :)
1484  if ($r["dns"] == $dns && $r["mail"] == $gesmx && $r["zonettl"] == $ttl) {
1485  $msg->raise("INFO", "dom", _("No change has been requested..."));
1486  return true;
1487  }
1488 
1489  // si gestion mx uniquement, vérification du dns externe
1490  if ($dns == "0" && $gesmx == "1" && !$force) {
1491  $vmx = $this->checkmx($dom);
1492  if ($vmx == 1) {
1493  $msg->raise("ALERT", "dom", _("There is no MX record pointing to this server, and you are asking us to host the mail here. Make sure to update your MX entries or no mail will be received"));
1494  }
1495 
1496  if ($vmx == 2) {
1497  // Serveur non spécifié parmi les champx mx
1498  $msg->raise("ALERT", "dom", _("There is no MX record pointing to this server, and you are asking us to host the mail here. Make sure to update your MX entries or no mail will be received"));
1499  }
1500  }
1501 
1502  if ($gesmx && !$r["mail"]) {
1503  $hooks->invoke("hook_dom_add_mx_domain", array($r["id"]));
1504  }
1505 
1506  if (!$gesmx && $r["mail"]) { // on a dissocié le MX : on détruit donc l'entree dans LDAP
1507  $hooks->invoke("hook_dom_del_mx_domain", array($r["id"]));
1508  }
1509 
1510  $db->query("UPDATE domaines SET gesdns= ?, gesmx= ?, zonettl= ? WHERE domaine= ?", array($dns, $gesmx, $ttl, $dom));
1511  $this->set_dns_action($dom, 'UPDATE');
1512 
1513  return true;
1514  }
1515 
1516 
1517  /* Slave dns ip managment */
1518 
1519 
1520  /** Return the list of ip addresses and classes that are allowed access to domain list
1521  * through AXFR Transfers from the bind server.
1522  */
1523  function enum_slave_ip() {
1524  global $db, $msg;
1525  $db->query("SELECT * FROM slaveip;");
1526  if (!$db->next_record()) {
1527  return false;
1528  }
1529  $res = array();
1530  do {
1531  $res[] = $db->Record;
1532  } while ($db->next_record());
1533  return $res;
1534  }
1535 
1536 
1537  /**
1538  * Add an ip address (or a ip class) to the list of allowed slave ip access list.
1539  */
1540  function add_slave_ip($ip, $class = "32") {
1541  global $db, $msg;
1542  if (!checkip($ip)) {
1543  $msg->raise("ERROR", "dom", _("The IP address you entered is incorrect"));
1544  return false;
1545  }
1546  $class = intval($class);
1547  if ($class < 8 || $class > 32) {
1548  $class = 32;
1549  }
1550  $db->query("SELECT * FROM slaveip WHERE ip= ? AND class= ?;", array($ip, $class));
1551  if ($db->next_record()) {
1552  $msg->raise("ERROR", "err", _("The requested domain is forbidden in this server, please contact the administrator"));
1553  return false;
1554  }
1555  $db->query("INSERT INTO slaveip (ip,class) VALUES (?, ?);", array($ip, $class));
1556  $f = fopen(SLAVE_FLAG, "w");
1557  fputs($f, "yopla");
1558  fclose($f);
1559  return true;
1560  }
1561 
1562 
1563  /**
1564  * Remove an ip address (or a ip class) from the list of allowed slave ip access list.
1565  */
1566  function del_slave_ip($ip) {
1567  global $db, $msg;
1568  if (!checkip($ip)) {
1569  $msg->raise("ERROR", "dom", _("The IP address you entered is incorrect"));
1570  return false;
1571  }
1572  $db->query("DELETE FROM slaveip WHERE ip= ?;", array($ip));
1573  $f = fopen(SLAVE_FLAG, "w");
1574  fputs($f, "yopla");
1575  fclose($f);
1576  return true;
1577  }
1578 
1579 
1580  /**
1581  * Check for a slave account
1582  */
1583  function check_slave_account($login, $pass) {
1584  global $db;
1585  $db->query("SELECT * FROM slaveaccount WHERE login= ? AND pass= ?;", array($login, $pass));
1586  if ($db->next_record()) {
1587  return true;
1588  }
1589  return false;
1590  }
1591 
1592  /**
1593  * Out (echo) the complete hosted domain list :
1594  */
1595  function echo_domain_list($integrity = false) {
1596  global $db;
1597  $db->query("SELECT domaine FROM domaines WHERE gesdns=1 ORDER BY domaine");
1598  $tt = "";
1599  while ($db->next_record()) {
1600  #echo $db->f("domaine")."\n";
1601  $tt.=$db->f("domaine") . "\n";
1602  }
1603  echo $tt;
1604  if ($integrity) {
1605  echo md5($tt) . "\n";
1606  }
1607  return true;
1608  }
1609 
1610 
1611  /**
1612  * Returns the complete hosted domain list :
1613  */
1614  function get_domain_list($uid = -1) {
1615  global $db;
1616  $uid = intval($uid);
1617  $res = array();
1618  $sql = "";
1619 
1620  $query = "SELECT domaine FROM domaines WHERE gesdns=1 ";
1621  $query_args = array();
1622  if ($uid != -1) {
1623  $query .= " AND compte= ? ";
1624  array_push($query_args, $uid);
1625  }
1626  $query .= " ORDER BY domaine;";
1627  $db->query($query, $query_args);
1628  while ($db->next_record()) {
1629  $res[] = $db->f("domaine");
1630  }
1631  return $res;
1632  }
1633 
1634  /**
1635  *
1636  * @return array
1637  */
1639  global $db;
1640  $res = array();
1641  $db->query("SELECT domaine, gesdns, gesmx, dns_action, zonettl FROM domaines ORDER BY domaine");
1642  while ($db->next_record()) {
1643  $res[$db->f("domaine")] = array(
1644  "gesdns" => $db->f("gesdns"),
1645  "gesmx" => $db->f("gesmx"),
1646  "dns_action" => $db->f("dns_action"),
1647  "zonettl" => $db->f("zonettl"),
1648  );
1649  }
1650  return $res;
1651  }
1652 
1653 
1654  /** Returns the name of a domain for the current user, from it's domain_id
1655  * @param $dom_id integer the domain_id to search for
1656  * @return string the domain name, or false with an error raised.
1657  */
1658  function get_domain_byid($dom_id) {
1659  global $db, $msg, $cuid;
1660  $dom_id = intval($dom_id);
1661  $db->query("SELECT domaine FROM domaines WHERE id= ? AND compte= ?;", array($dom_id, $cuid));
1662  if ($db->next_record()) {
1663  $domain = $db->f("domaine");
1664  if (!$domain) {
1665  $msg->raise("ERROR", "dom", _("This domain is not installed in your account"));
1666  return false;
1667  } else {
1668  return $domain;
1669  }
1670  } else {
1671  $msg->raise("ERROR", "dom", _("This domain is not installed in your account"));
1672  return false;
1673  }
1674  }
1675 
1676 
1677  /** Returns the id of a domain for the current user, from it's domain name
1678  * @param $domain string the domain name to search for
1679  * @return integer the domain id, or false with an error raised.
1680  */
1681  function get_domain_byname($domain) {
1682  global $db, $msg, $cuid;
1683  $domain = trim($domain);
1684  $db->query("SELECT id FROM domaines WHERE domaine= ? AND compte= ?;", array($domain, $cuid));
1685  if ($db->next_record()) {
1686  $id = $db->f("id");
1687  if (!$id) {
1688  $msg->raise("ERROR", "dom", _("This domain is not installed in your account"));
1689  return false;
1690  } else {
1691  return $id;
1692  }
1693  } else {
1694  $msg->raise("ERROR", "dom", _("This domain is not installed in your account"));
1695  return false;
1696  }
1697  }
1698 
1699 
1700  /**
1701  * Count all domains, for all users
1702  */
1703  function count_domains_all() {
1704  global $db;
1705  $db->query("SELECT COUNT(*) AS count FROM domaines;");
1706  if ($db->next_record()) {
1707  return $db->f('count');
1708  } else {
1709  return 0;
1710  }
1711  }
1712 
1713 
1714  /**
1715  * Return the list of allowed slave accounts
1716  */
1717  function enum_slave_account() {
1718  global $db;
1719  $db->query("SELECT * FROM slaveaccount;");
1720  $res = array();
1721  while ($db->next_record()) {
1722  $res[] = $db->Record;
1723  }
1724  if (!count($res)) {
1725  return false;
1726  }
1727  return $res;
1728  }
1729 
1730 
1731  /**
1732  * Add a slave account that will be allowed to access the domain list
1733  */
1734  function add_slave_account($login, $pass) {
1735  global $db, $msg;
1736  $db->query("SELECT * FROM slaveaccount WHERE login= ?", array($login));
1737  if ($db->next_record()) {
1738  $msg->raise("ERROR", "dom", _("The specified slave account already exists"));
1739  return false;
1740  }
1741  $db->query("INSERT INTO slaveaccount (login,pass) VALUES (?, ?)", array($login, $pass));
1742  return true;
1743  }
1744 
1745 
1746  /**
1747  * Remove a slave account
1748  */
1749  function del_slave_account($login) {
1750  global $db, $msg;
1751  $db->query("DELETE FROM slaveaccount WHERE login= ?", array($login));
1752  return true;
1753  }
1754 
1755  /* Private */
1756 
1757 
1758  /**
1759  * Try to lock a domain
1760  * @access private
1761  */
1762  function lock() {
1763  global $msg,$domislocked;
1764  $msg->debug("dom", "lock");
1765  if ($domislocked) {
1766  $msg->raise("ERROR", "dom", _("--- Program error --- Lock already obtained!"));
1767  }
1768  // wait for the file to disappear, or at most 15min:
1769  while (file_exists(m_dom::fic_lock_cron) && filemtime(m_dom::fic_lock_cron)>(time()-900)) {
1770  clearstatcache();
1771  sleep(2);
1772  }
1773  @touch(m_dom::fic_lock_cron);
1774  $domislocked = true;
1775  // extra safe :
1776  register_shutdown_function(array("m_dom","unlock"),1);
1777  return true;
1778  }
1779 
1780 
1781  /**
1782  * Unlock the cron for domain management
1783  * return true
1784  * @access private
1785  */
1786  function unlock($isshutdown=0) {
1787  global $msg,$domislocked;
1788  $msg->debug("dom", "unlock");
1789  if (!$isshutdown && !$domislocked) {
1790  $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
1791  }
1792  // don't use $this since we may be called by register_shutdown_function out of an object instance.
1793  @unlink(m_dom::fic_lock_cron);
1794  $domislocked = false;
1795  return true;
1796  }
1797 
1798 
1799  /**
1800  * Declare that a domain's emails are hosted in this server :
1801  * This adds 2 MX entries in this domain (if required)
1802  */
1803  function hook_dom_add_mx_domain($dom_id) {
1804  global $msg;
1805  $domain = $this->get_domain_byid($dom_id);
1806  $msg->log("dom", "hook_dom_add_mx_domain");
1807  $this->set_sub_domain($domain, '', $this->type_defmx, '');
1808  if (!empty($GLOBALS['L_DEFAULT_SECONDARY_MX'])) {
1809  $this->set_sub_domain($domain, '', $this->type_defmx2, '');
1810  }
1811  return true;
1812  }
1813 
1814 
1815  /**
1816  * Delete an account (all his domains)
1817  */
1818  function admin_del_member() {
1819  global $msg;
1820  $msg->log("dom", "alternc_del_member");
1821  $li = $this->enum_domains();
1822  foreach ($li as $dom) {
1823  $this->del_domain($dom);
1824  }
1825  return true;
1826  }
1827 
1828 
1829  /**
1830  * Returns the used quota for the $name service for the current user.
1831  * @param $name string name of the quota
1832  * @return integer the number of service used or false if an error occured
1833  * @access private
1834  */
1835  function hook_quota_get() {
1836  global $db, $msg, $cuid;
1837  $msg->debug("dom", "get_quota");
1838  $q = Array("name" => "dom", "description" => _("Domain name"), "used" => 0);
1839  $db->query("SELECT COUNT(*) AS cnt FROM domaines WHERE compte= ?", array($cuid));
1840  if ($db->next_record()) {
1841  $q['used'] = $db->f("cnt");
1842  }
1843  return $q;
1844  }
1845 
1846 
1847  /**
1848  * Returns the global domain(s) configuration(s) of a particular user
1849  * No parameters needed
1850  */
1851  function alternc_export_conf() {
1852  global $msg;
1853  $msg->log("dom", "export");
1854  $this->enum_domains();
1855  $str = "";
1856  foreach ($this->domains as $d) {
1857  $str.= " <domaines>\n";
1858  $str.=" <nom>" . $d . "</nom>\n";
1859  $this->lock();
1860  $s = $this->get_domain_all($d);
1861  $this->unlock();
1862  if (empty($s["dns"])) {
1863  $s["dns"] = "non";
1864  } else {
1865  $s["dns"] = "oui";
1866  }
1867  $str.=" <dns>" . $s["dns"] . "</dns>\n";
1868 
1869  if (empty($s["mx"])) {
1870  $s["mx"] = "non";
1871  } else {
1872  $s["mx"] = "oui";
1873  }
1874 
1875  $str.=" <mx>" . $s["mx"] . "</mx>\n";
1876 
1877  if (empty($s["mail"])) {
1878  $s["mail"] = "non";
1879  }
1880  $str.=" <mail>" . $s["mail"] . "</mail>\n";
1881  if (is_array($s["sub"])) {
1882  foreach ($s["sub"] as $sub) {
1883  $str.=" <subdomain>\n";
1884  $str.=" <enabled>" . $sub["enable"] . " </enabled>\n";
1885  $str.=" <destination>" . $sub["dest"] . " </destination>\n";
1886  $str.=" <type>" . $sub["type"] . " </type>\n";
1887  $str.=" </subdomain>\n";
1888  }
1889  }
1890  $str.=" </domaines>\n";
1891  }
1892  return $str;
1893  }
1894 
1895 
1896  /**
1897  * complex process to manage domain and subdomain updates
1898  * Launched every minute by a cron as root
1899  * should launch hooks for each domain or subdomain,
1900  * so that apache & bind could do their job
1901  */
1902  function update_domains() {
1903  global $db, $hooks;
1904  if (posix_getuid()!=0) {
1905  echo "FATAL: please lauch me as root\n";
1906  exit();
1907  }
1908 
1909  $this->lock();
1910 
1911  // fix in case we forgot to delete SUBDOMAINS before deleting a DOMAIN
1912  $db->query("UPDATE sub_domaines sd, domaines d SET sd.web_action = 'DELETE' WHERE sd.domaine = d.domaine AND sd.compte=d.compte AND d.dns_action = 'DELETE';");
1913 
1914  // Search for things to do on DOMAINS:
1915  $db->query("SELECT * FROM domaines WHERE dns_action!='OK';");
1916  $alldoms=array();
1917  while ($db->next_record()) {
1918  $alldoms[$db->Record["id"]]=$db->Record;
1919  }
1920  // now launch hooks
1921  if (count($alldoms)) {
1922  $hooks->invoke("hook_updatedomains_dns_pre");
1923  foreach($alldoms as $id=>$onedom) {
1924  if ($onedom["gesdns"]==0 || $onedom["dns_action"]=="DELETE") {
1925  $ret = $hooks->invoke("hook_updatedomains_dns_del",array($onedom));
1926  } else {
1927  $ret = $hooks->invoke("hook_updatedomains_dns_add",array($onedom));
1928  }
1929 
1930  if ($onedom["dns_action"]=="DELETE") {
1931  $db->query("DELETE FROM domaines WHERE domaine=?;",array($onedom['domaine']));
1932  } else {
1933  // we keep the highest result returned by hooks...
1934  rsort($ret,SORT_NUMERIC); $returncode=$ret[0];
1935  $db->query("UPDATE domaines SET dns_result=?, dns_action='OK' WHERE domaine=?;",array($returncode,$onedom["domaine"]));
1936  }
1937  }
1938  $hooks->invoke("hook_updatedomains_dns_post");
1939  }
1940 
1941 
1942  // Search for things to do on SUB-DOMAINS:
1943  $db->query("SELECT sd.*, dt.only_dns FROM domaines_type dt, sub_domaines sd WHERE dt.name=sd.type AND sd.web_action!='OK';");
1944  $alldoms=array();
1945  $ignore=array();
1946  $delete=array();
1947  while ($db->next_record()) {
1948  // only_dns=1 => weird, we should not have web_action SET to something else than OK ... anyway, skip it
1949  if ($db->Record["only_dns"]) {
1950  if ($db->Record["web_action"]=="DELETE") {
1951  $delete[]=$db->Record["id"];
1952  } else {
1953  $ignore[]=$db->Record["id"];
1954  }
1955  } else {
1956  $alldoms[$db->Record["id"]]=$db->Record;
1957  }
1958  }
1959  foreach($delete as $id) {
1960  $db->query("DELETE FROM sub_domaines WHERE id=?;",array($id));
1961  }
1962  foreach($ignore as $id) {
1963  // @FIXME (unsure it's useful) maybe we could check that no file exist for this subdomain ?
1964  $db->query("UPDATE sub_domaines SET web_action='OK' WHERE id=?;",array($id));
1965  }
1966  // now launch hooks
1967  if (count($alldoms)) {
1968  $hooks->invoke("hook_updatedomains_web_pre");
1969  foreach($alldoms as $id=>$subdom) {
1970  // is it a delete (DISABLED or DELETE)
1971  if ($subdom["web_action"]=="DELETE" || strtoupper(substr($subdom["enable"],0,7))=="DISABLE") {
1972  $ret = $hooks->invoke("hook_updatedomains_web_del",array($subdom["id"]));
1973  } else {
1974  $hooks->invoke("hook_updatedomains_web_before",array($subdom["id"])); // give a chance to get SSL cert before ;)
1975  $ret = $hooks->invoke("hook_updatedomains_web_add",array($subdom["id"]));
1976  $hooks->invoke("hook_updatedomains_web_after",array($subdom["id"]));
1977  }
1978 
1979  if ($subdom["web_action"]=="DELETE") {
1980  $db->query("DELETE FROM sub_domaines WHERE id=?;",array($id));
1981  } else {
1982  // we keep the highest result returned by hooks...
1983  rsort($ret,SORT_NUMERIC); $returncode=$ret[0];
1984  $db->query("UPDATE sub_domaines SET web_result=?, web_action='OK' WHERE id=?;",array($returncode,$id));
1985  }
1986  }
1987  $hooks->invoke("hook_updatedomains_web_post");
1988  }
1989 
1990  $this->unlock();
1991  }
1992 
1993 
1994  /**
1995  * @param string $dns_action
1996  */
1997  function set_dns_action($domain, $dns_action) {
1998  global $db;
1999  $db->query("UPDATE domaines SET dns_action= ? WHERE domaine= ?; ", array($dns_action, $domain));
2000  return true;
2001  }
2002 
2003 
2004  /**
2005  * List if there are problems on the domain.
2006  * Problems can appear when editing domains type properties
2007  */
2008  function get_problems($domain) {
2009  $this->lock();
2010  $da = $this->get_domain_all($domain);
2011  $this->unlock();
2012 
2013  $errors = array();
2014  // Check if there is more than 1 apache conf
2015  // by subdomain
2016  $tmp = array();
2017  foreach ($da['sub'] as $sub) {
2018  if ($sub['web_action'] != 'OK') {
2019  continue;
2020  }
2021  if (!$sub['only_dns']) {
2022  if (!isset($tmp[$sub['fqdn']])) {
2023  $tmp[$sub['fqdn']] = 0;
2024  }
2025  $tmp[$sub['fqdn']] ++;
2026  if ($tmp[$sub['fqdn']] >= 2) {
2027  $errors[$sub['fqdn']] = sprintf(_("Problem on %s: there is more than 1 web configuration going to be generated for this sub-domain."), $sub['fqdn']);
2028  }
2029  }
2030  }
2031 
2032  // Check if we know each type of subdomain
2033  // Example: we may not know WEBMAIL if we upgrade from a previous setup
2034  foreach ($da['sub'] as $sub) {
2035  if (is_null($sub['type_desc'])) {
2036  $errors[$sub['fqdn']] = sprintf(_("Problem on %s: we do not know domain's type <b>%s</b>."), $sub['fqdn'], $sub['type']);
2037  }
2038  }
2039 
2040  // TODO: add a full compatibility check.
2041 
2042  return $errors;
2043  }
2044 
2045 
2046  function default_domain_type() {
2047  // This function is only used to allow translation of default domain types:
2048  _("Locally hosted");
2049  _("URL redirection");
2050  _("IPv4 redirect");
2051  _("Webmail access");
2052  _("Squirrelmail Webmail access");
2053  _("Roundcube Webmail access");
2054  _("IPv6 redirect");
2055  _("CNAME DNS entry");
2056  _("TXT DNS entry");
2057  _("MX DNS entry");
2058  _("secondary MX DNS entry");
2059  _("Default mail server");
2060  _("Default backup mail server");
2061  _("AlternC panel access");
2062  _("DKIM Key");
2063  _("Email autoconfiguration");
2064  }
2065 
2066 } /* Class m_domains */
Classe de gestion des domaines de l'hébergé.
Definition: m_dom.php:31
get_domain_byid($dom_id)
Returns the name of a domain for the current user, from it's domain_id.
Definition: m_dom.php:1658
set_ttl($dom_id, $ttl)
Definition: m_dom.php:1415
update_domains()
complex process to manage domain and subdomain updates Launched every minute by a cron as root should...
Definition: m_dom.php:1902
unlock($isshutdown=0)
Unlock the cron for domain management return true @access private.
Definition: m_dom.php:1786
$webmail
Flag : a-t-on trouvé un sous-domaine Webmail pour ce domaine ? @access private.
Definition: m_dom.php:49
whois($domain)
Return the NS of a server by interrogating its parent zone.
Definition: m_dom.php:929
domains_type_lst()
Retourne un tableau contenant les types de domaines.
Definition: m_dom.php:149
$tld_no_check_at_all
Definition: m_dom.php:71
sub_domain_change_status($sub_id, $status)
Definition: m_dom.php:568
const fic_lock_cron
Systéme de verrouillage du cron Ce fichier permet de verrouiller le cron en attendant la validation d...
Definition: m_dom.php:57
count_domains_all()
Count all domains, for all users.
Definition: m_dom.php:1703
$action_insert
Definition: m_dom.php:68
$type_defmx2
Definition: m_dom.php:67
import_manual_dns_prep_zone($domain)
Definition: m_dom.php:452
import_manual_dns_entry($zone, $domain, $detect_redirect=true, $save=false)
Definition: m_dom.php:234
$type_local
Definition: m_dom.php:59
del_default_type($id)
Definition: m_dom.php:908
add_slave_account($login, $pass)
Add a slave account that will be allowed to access the domain list.
Definition: m_dom.php:1734
domshort($domain, $sub="")
Definition: m_dom.php:665
admin_del_member()
Delete an account (all his domains)
Definition: m_dom.php:1818
dump_axfr($domain, $ns='localhost')
Definition: m_dom.php:845
lst_default_subdomains()
Definition: m_dom.php:852
add_domain($domain, $dns, $noerase=false, $force=false, $isslave=false, $slavedom="")
Installe un domaine sur le compte courant.
Definition: m_dom.php:689
$type_ipv6
Definition: m_dom.php:63
check_type_value($type, $value)
Definition: m_dom.php:1144
$action_update
Definition: m_dom.php:69
get_problems($domain)
List if there are problems on the domain.
Definition: m_dom.php:2008
$type_defmx
Definition: m_dom.php:66
set_dns_action($domain, $dns_action)
Definition: m_dom.php:1997
get_domain_byname($domain)
Returns the id of a domain for the current user, from it's domain name.
Definition: m_dom.php:1681
edit_domain($dom, $dns, $gesmx, $force=false, $ttl=3600)
Modifie les information du domaine précisé.
Definition: m_dom.php:1440
can_create_subdomain($dom, $sub, $type, $sub_domain_id=0)
Check the compatibility of the POSTed parameters with the chosen domain type.
Definition: m_dom.php:1221
static get_sub_domain_id_and_member_by_name($fqdn)
Definition: m_dom.php:101
get_domain_all($dom)
retourne TOUTES les infos d'un domaine
Definition: m_dom.php:1032
import_manual_dns_entry_doit($entry)
Definition: m_dom.php:414
domains_type_enable_values()
Definition: m_dom.php:164
add_slave_ip($ip, $class="32")
Add an ip address (or a ip class) to the list of allowed slave ip access list.
Definition: m_dom.php:1540
$type_webmail
Definition: m_dom.php:62
$type_cname
Definition: m_dom.php:64
domains_type_regenerate($name)
Definition: m_dom.php:527
default_domain_type()
Definition: m_dom.php:2046
alternc_export_conf()
Returns the global domain(s) configuration(s) of a particular user No parameters needed.
Definition: m_dom.php:1851
m_dom()
Constructeur.
Definition: m_dom.php:78
get_domain_all_summary()
Definition: m_dom.php:1638
domains_type_target_values($type=null)
Definition: m_dom.php:185
lock()
Try to lock a domain @access private.
Definition: m_dom.php:1762
$action_delete
Definition: m_dom.php:70
get_sub_domain_all($sub_domain_id)
Retourne TOUTES les infos d'un sous domaine du compte courant.
Definition: m_dom.php:1094
get_domain_list($uid=-1)
Returns the complete hosted domain list :
Definition: m_dom.php:1614
del_domain($domain)
Efface un domaine du membre courant, et tous ses sous-domaines.
Definition: m_dom.php:638
clean_https_value($type, $https)
Definition: m_dom.php:1124
is_it_a_redirect($url)
Definition: m_dom.php:487
del_domain_cancel($dom)
Definition: m_dom.php:616
hook_quota_get()
Returns the used quota for the $name service for the current user.
Definition: m_dom.php:1835
checkmx($domaine, $ref_domain='')
vérifie la presence d'un champs mx valide sur un serveur DNS $domaine est le domaine dont on veux vér...
Definition: m_dom.php:974
set_subdomain_ssl_provider($sub_domain_id, $provider)
set the HTTPS preference for a subdomain.
Definition: m_dom.php:1259
hook_dom_add_mx_domain($dom_id)
Declare that a domain's emails are hosted in this server : This adds 2 MX entries in this domain (if ...
Definition: m_dom.php:1803
enum_slave_ip()
Return the list of ip addresses and classes that are allowed access to domain list through AXFR Trans...
Definition: m_dom.php:1523
update_default_subdomains($arr)
Definition: m_dom.php:872
$dns
$dns : Liste des dns trouvés par la fonction whois @access private
Definition: m_dom.php:43
$type_ip
Definition: m_dom.php:61
domains_type_update($name, $description, $target, $entry, $compatibility, $enable, $only_dns, $need_dns, $advanced, $create_tmpdir, $create_targetdir, $has_https_option=0)
Definition: m_dom.php:550
create_default_subdomains($domain, $target_domain="")
Definition: m_dom.php:815
domdefaultdir($domain)
Definition: m_dom.php:840
del_sub_domain($sub_domain_id)
Supprime le sous-domaine demandé
Definition: m_dom.php:1393
get_panel_url_list()
Definition: m_dom.php:86
enum_slave_account()
Return the list of allowed slave accounts.
Definition: m_dom.php:1717
update_one_default($domain_type, $sub, $domain_type_parameter, $concerned, $enabled, $id=null)
Definition: m_dom.php:894
hook_menu()
hook function called by the menu class to add menu to the left panel
Definition: m_dom.php:116
domains_type_get($name)
Definition: m_dom.php:535
enum_domains($uid=-1)
Retourne un tableau contenant les domaines d'un membre.
Definition: m_dom.php:600
import_manual_dns_zone($zone, $domain, $detect_redirect=true, $save=false)
Definition: m_dom.php:210
del_slave_ip($ip)
Remove an ip address (or a ip class) from the list of allowed slave ip access list.
Definition: m_dom.php:1566
domains_type_del($name)
Definition: m_dom.php:543
$type_url
Definition: m_dom.php:60
$cache_domains_type_lst
Definition: m_dom.php:72
$domains
$domains : Cache des domaines du membre @access private
Definition: m_dom.php:37
$type_txt
Definition: m_dom.php:65
check_slave_account($login, $pass)
Check for a slave account.
Definition: m_dom.php:1583
set_sub_domain($dom, $sub, $type, $dest, $sub_domain_id=0, $https="")
Modifier les information du sous-domaine demandé.
Definition: m_dom.php:1308
echo_domain_list($integrity=false)
Out (echo) the complete hosted domain list :
Definition: m_dom.php:1595
del_slave_account($login)
Remove a slave account.
Definition: m_dom.php:1749
$res
Definition: index.php:111
const SLAVE_FLAG
Definition: m_dom.php:21
$c
Definition: mem_param.php:46