From a194cd80d0a1985c125151ec9fcd42ed730f3054 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Sat, 7 Jul 2018 13:13:28 +0200 Subject: [PATCH 01/30] [fix] vhost http template missing SSLENGINE and CERT instructions in 443 Vhost --- etc/alternc/templates/apache2/vhost-http.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/etc/alternc/templates/apache2/vhost-http.conf b/etc/alternc/templates/apache2/vhost-http.conf index c163bc86..5754a8e9 100644 --- a/etc/alternc/templates/apache2/vhost-http.conf +++ b/etc/alternc/templates/apache2/vhost-http.conf @@ -10,6 +10,11 @@ RewriteCond %{REQUEST_FILENAME} !/.well-known/acme-challenge/ RewriteRule ^/(.*)$ http://%%fqdn%%/$1 [R=301,L] + SSLEngine On + SSLCertificateFile %%CRT%% + SSLCertificateKeyFile %%KEY%% + %%CHAINLINE%% + From ed7aaa3151ae946f61ebfa67f57c1d7d9dee654f Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Sun, 8 Jul 2018 13:01:35 +0200 Subject: [PATCH 02/30] [enh] first version of class-and-hooks-based update_domains.php, includes ssl certficate mechanism --- bureau/class/m_apache.php | 179 ++++++++++++++++++ .../{class_system_bind.php => m_bind.php} | 55 +++++- bureau/class/m_dom.php | 101 +++++++++- bureau/class/m_ssl.php | 90 +++++++-- src/update_domains.sh | 9 +- 5 files changed, 415 insertions(+), 19 deletions(-) create mode 100644 bureau/class/m_apache.php rename bureau/class/{class_system_bind.php => m_bind.php} (92%) diff --git a/bureau/class/m_apache.php b/bureau/class/m_apache.php new file mode 100644 index 00000000..e497e8e1 --- /dev/null +++ b/bureau/class/m_apache.php @@ -0,0 +1,179 @@ +shouldreload=false; + } + + // launched for each FQDN for which we want a new vhost template + function hook_updatedomains_web_add($subdomid) { + global $msg,$db; + + $db->query("SELECT sd.*, dt.only_dns, dt.has_https_option, m.login FROM domaines_type dt, sub_domaines sd LEFT JOIN membres m ON m.uid=sd.compte WHERE dt.name=sd.type AND sd.web_action!='OK' AND id=?;",array($subdomid)); + $db->next_record(); + $subdom=$db->Record; + + // security : only AlternC account's UIDs + if ($subdom["compte"]<1999) { + $msg->raise("ERROR","apache","Subdom ".$subdom["id"]." for domain ".$subdom["sub"].".".$subdom["domaine"]." has id ".$subdom["compte"].". Skipped"); + return 1; + } + + // search for the template file: + $template = $this->templatedir."/".strtolower($subdom["type"]); + if ($subdom["has_https_option"] && in_array($subdom["https"],$this->httpsmodes)) { + $template.="-".$subdom["https"]; + } + $template.=".conf"; + if (!is_file($template)) { + $msg->raise("ERROR","apache","Template $template not found for subdom ".$subdom["id"]." for domain ".$subdom["sub"].".".$subdom["domaine"].". Skipped"); + return 1; + } + + $subdom["fqdn"]=$subdom["sub"].(($subdom["sub"])?".":"").$subdom["domaine"]; + // SSL information $subdom["certificate_id"] may be ZERO => it means "take id 0 which is snakeoil cert" + $cert = $ssl->get_certificate_path($subdom["certificate_id"]); + if ($cert["chain"]) { + $chainline="SSLCertificateChainFile ".$cert["chain"]; + } else { + $chainline=""; + } + // Replace needed vars in template file + $tpl=file_get_contents($template); + $tpl = strtr($tpl, array( + "%%LOGIN%%" => $subdom['login'], + "%%fqdn%%" => $subdom['fqdn'], + "%%document_root%%" => getuserpath($subdom['login']) . $subdom['valeur'], + "%%account_root%%" => getuserpath($subdom['login']), + "%%redirect%%" => $subdom['valeur'], + "%%UID%%" => $subdom['compte'], + "%%GID%%" => $subdom['compte'], + "%%mail_account%%" => $subdom['mail'], + "%%user%%" => "FIXME", + "%%CRT%%" => $cert["cert"], + "%%KEY%%" => $cert["key"], + "%%CHAINLINE%%" => $chainline, + )); + // and write the template + $confdir = $this->vhostroot."/".substr($subdom["compte"],-1)."/".$subdom["compte"]; + @mkdir($confdir,0755,true); + file_put_contents($confdir."/".$subdom["fqdn"].".conf"); + $this->shouldreload=true; + + return 0; // shell meaning => OK ;) + } // hook_updatedomains_web_add + + + // ------------------------------------------------------------ + /** + * launched for each FQDN for which we want to delete a vhost template + */ + function hook_updatedomains_web_del($subdom) { + $confdir = $this->vhostroot."/".substr($subdom["compte"],-1)."/".$subdom["compte"]; + @unlink($confdir."/".$subdom["fqdn"].".conf"); + $this->shouldreload=true; + } + + + // ------------------------------------------------------------ + /** + * launched at the very end of updatedomains + */ + function hook_updatedomains_web_post() { + global $msg; + if ($this->shouldreload) { + + // concatenate all files into one + $this->concat(); + + // reload apache + $ret=0; + exec("apache2ctl graceful 2>&1",$out,$ret); + touch($this->reloadfile); + if ($ret!=0) { + $msg->raise("ERROR","apache","Error while reloading apache, error code is $ret\n".implode("\n",$out)); + } else { + $msg->raise("INFO","apache","Apache reloaded"); + } + } + + } + + // ------------------------------------------------------------ + /** + * Concatenate all files under $this->vhostroot + * into one (mindepth=2 though), + * this function is faster than any shell stuff :D + */ + private function concat() { + global $msg; + $d=opendir($this->vhostroot); + $f=fopen($this->vhostroot."/vhosts_all.conf.new","wb"); + if (!$f) { + $msg->raise("FATAL","apache","Can't write vhosts_all file"); + return false; + } + while (($c=readdir($d))!==false) { + if (substr($c,0,1)!="." && is_dir($this->vhostroot."/".$c)) { + $this->subconcat($f,$this->vhostroot."/".$c); + } + } + closedir($d); + fclose($f); + } + + private function subconcat($f,$root) { + // recursive cat :) + $d=opendir($root); + while (($c=readdir($d))!==false) { + if (substr($c,0,1)!=".") { + if (is_dir($root."/".$c)) { + $this->subconcat($f,$root."/".$c); // RECURSIVE CALL + } + if (is_file($root."/".$c)) { + fputs($f,file_get_contents($root."/".$c)."\n"); + } + } + } + closedir($d); + } + +} // m_apache + diff --git a/bureau/class/class_system_bind.php b/bureau/class/m_bind.php similarity index 92% rename from bureau/class/class_system_bind.php rename to bureau/class/m_bind.php index 34d045b4..c5080629 100644 --- a/bureau/class/class_system_bind.php +++ b/bureau/class/m_bind.php @@ -19,11 +19,16 @@ */ /** - * bind9 file management class + * Manages BIND 9+ zone management templates in AlternC 3.5+ * - * @copyright AlternC-Team 2000-2017 https://alternc.com/ + * @copyright AlternC-Team 2000-2018 https://alternc.com/ */ -class system_bind { +class m_bind { + + var $shouldreload; + var $shouldreconfig; + + var $ZONE_TEMPLATE ="/etc/alternc/templates/bind/templates/zone.template"; var $NAMED_TEMPLATE ="/etc/alternc/templates/bind/templates/named.template"; var $NAMED_CONF ="/var/lib/alternc/bind/automatic.conf"; @@ -39,6 +44,47 @@ class system_bind { var $cache_domain_summary = array(); var $zone_file_directory = '/var/lib/alternc/bind/zones/'; + + // launched before any action by updatedomains + function hook_updatedomains_dns_pre() { + $this->shouldreload=false; + $this->shouldreconfig=false; + } + + // launched for each ZONE for which we want a zone update (or create) + function hook_updatedomains_dns_add($domain) { + + } + + // launched for each ZONE for which we want a zone DELETE + function hook_updatedomains_dns_del($domain) { + + } + + // launched at the very end of updatedomains + function hook_updatedomains_dns_post() { + global $msg; + if ($this->shouldreload) { + $ret=0; + exec($this->rndc." reload 2>&1",$out,$ret); + if ($ret!=0) { + $msg->raise("ERROR","bind","Error while reloading bind, error code is $ret\n".implode("\n",$out)); + } else { + $msg->raise("INFO","bind","Bind reloaded"); + } + } + if ($this->shouldreconfig) { + $ret=0; + exec($this->rndc." reload 2>&1",$out,$ret); + if ($ret!=0) { + $msg->raise("ERROR","bind","Error while reconfiguring bind, error code is $ret\n".implode("\n",$out)); + } else { + $msg->raise("INFO","bind","Bind reconfigured"); + } + } + } + + /** * Return the part of the conf we got from the database @@ -521,5 +567,6 @@ class system_bind { } -} /* Class system_bind */ + +} // m_bind diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 64d50a7f..bafab4de 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1770,10 +1770,15 @@ class m_dom { if ($this->islocked) { $msg->raise("ERROR", "dom", _("--- Program error --- Lock already obtained!")); } - while (file_exists($this->fic_lock_cron)) { + // wait for the file to disappear, or at most 15min: + while (file_exists($this->fic_lock_cron) && filemtime($this->fic_lock_cron)>(time()-900)) { + clearstatcache(); sleep(2); } + @touch($this->fic_lock_cron); $this->islocked = true; + // extra safe : + register_shutdown_function(array("m_dom","unlock"),1); return true; } @@ -1783,12 +1788,13 @@ class m_dom { * return true * @access private */ - function unlock() { + function unlock($isshutdown=0) { global $msg; $msg->debug("dom", "unlock"); - if (!$this->islocked) { + if (!$isshutdown && !$this->islocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); } + @unlink($this->fic_lock_cron); $this->islocked = false; return true; } @@ -1891,6 +1897,95 @@ class m_dom { } + /** + * complex process to manage domain and subdomain updates + * Launched every minute by a cron as root + * should launch hooks for each domain or subdomain, + * so that apache & bind could do their job + */ + function update_domains() { + if (posix_getuid()!=0) { + echo "FATAL: please lauch me as root\n"; + exit(); + } + + $dom->lock(); + + // fix in case we forgot to delete SUBDOMAINS before deleting a DOMAIN + $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';"); + + // Search for things to do on DOMAINS: + $db->query("SELECT * FROM domaines WHERE dns_action!='OK';"); + $alldoms=array(); + while ($db->next_record()) { + $alldoms[$db->Record["id"]]=$db->Record; + } + // now launch hooks + if (count($alldoms)) { + $hooks->invoke("hook_updatedomains_dns_pre"); + foreach($alldoms as $id=>$onedom) { + if ($onedom["gesdns"]==0 || $onedom["dns_action"]=="DELETE") { + $ret = $hooks->invoke("hook_updatedomains_dns_del",array(array($onedom))); + } else { + $ret = $hooks->invoke("hook_updatedomains_dns_add",array(array($onedom))); + } + + if ($onedom["dns_action"]=="DELETE") { + $db->query("DELETE FROM domaines WHERE domaine=?;",array($onedom)); + } else { + // we keep the highest result returned by hooks... + rsort($ret,SORT_NUMERIC); $returncode=$ret[0]; + $db->query("UPDATE domaines SET dns_result=?, dns_action='OK' WHERE domaine=?;",array($returncode,$onedom)); + } + } + $hooks->invoke("hook_updatedomains_dns_post"); + } + + + // Search for things to do on SUB-DOMAINS: + $db->query("SELECT sd.*, dt.only_dns FROM domaines_type dt, sub_domaines sd WHERE dt.name=sd.type AND sd.web_action!='OK';"); + $alldoms=array(); + $ignore=array(); + while ($db->next_record()) { + // only_dns=1 => weird, we should not have web_action SET to something else than OK ... anyway, skip it + if ($db->Record["only_dns"]) { + $ignore[]=$db->Record["id"]; + } else { + $alldoms[$db->Record["id"]]=$db->Record; + } + } + foreach($ignore as $id) { + // @FIXME (unsure it's useful) maybe we could check that no file exist for this subdomain ? + $db->query("UPDATE sub_domaines SET web_action='OK' WHERE id=?;",array($id)); + } + // now launch hooks + if (count($alldoms)) { + $hooks->invoke("hook_updatedomains_web_pre"); + foreach($alldoms as $id=>$subdom) { + // is it a delete (DISABLED or DELETE) + if ($subdom["web_action"]=="DELETE" || strtoupper(substr($subdom["enable"],0,7))=="DISABLE") { + $ret = $hooks->invoke("hook_updatedomains_web_del",array($subdom["id"])); + } else { + $hooks->invoke("hook_updatedomains_web_before",array($subdom["id"])); // give a chance to get SSL cert before ;) + $ret = $hooks->invoke("hook_updatedomains_web_add",array($subdom["id"])); + $hooks->invoke("hook_updatedomains_web_after",array($subdom["id"])); + } + + if ($subdom["web_action"]=="DELETE") { + $db->query("DELETE FROM sub_domaines WHERE id=?;",array($id)); + } else { + // we keep the highest result returned by hooks... + rsort($ret,SORT_NUMERIC); $returncode=$ret[0]; + $db->query("UPDATE sub_domaines SET web_result=?, web_action='OK' WHERE id=?;",array($returncode,$id)); + } + } + $hooks->invoke("hook_updatedomains_web_post"); + } + + $dom->unlock(); + } + + /** * Return an array with all the needed parameters to generate conf * of a vhost. diff --git a/bureau/class/m_ssl.php b/bureau/class/m_ssl.php index 109241c4..69b0a6f0 100644 --- a/bureau/class/m_ssl.php +++ b/bureau/class/m_ssl.php @@ -372,7 +372,34 @@ INSTR(CONCAT(sd.sub,IF(sd.sub!='','.',''),sd.domaine),'.')+1))=? return $db->Record; } - + + // ----------------------------------------------------------------- + /** Return paths to certificate, key, and chain for a certificate + * given it's ID. + * @param $id integer the certificate by id + * @return array cert, key, chain (not mandatory) with full path. + */ + function get_certificate_path($id) { + global $db, $msg, $cuid; + $msg->log("ssl", "get_certificate_path",$id); + $id = intval($id); + $db->query("SELECT id FROM certificates WHERE id=?;",array($id)); + if (!$db->next_record()) { + $msg->raise("ERROR","ssl", _("Can't find this Certificate")); + // Return cert 0 info :) + $id=0; + } + $chain=self::KEY_REPOSITORY."/".floor($id/1000)."/".$id.".chain"; + if (!file_exists($chain)) + $chain=false; + + return array( + "cert" => self::KEY_REPOSITORY."/".floor($id/1000)."/".$id.".cert", + "key" => self::KEY_REPOSITORY."/".floor($id/1000)."/".$id.".key", + "chain" => $chain + ); + } + // ----------------------------------------------------------------- /** Return all the valid certificates that can be used for a specific FQDN * return the list of certificates by order of preference @@ -492,6 +519,12 @@ INSTR(CONCAT(sd.sub,IF(sd.sub!='','.',''),sd.domaine),'.')+1))=? $msg->raise("ERROR","ssl", _("Can't save the Key/Crt/Chain now. Please try later.")); return false; } + $this->write_cert_file(array( + "id"=>$id, + "sslcrt"=>$crt, + "sslkey"=>$key, + "sslchain"=>$chain + )); return $id; } @@ -612,6 +645,33 @@ SELECT ?,?,?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?, ?, sslcsr FROM certificate } + + // ----------------------------------------------------------------- + /** Launched by hosting_functions.sh launched by update_domaines.sh + * Action may be create/postinst/delete/enable/disable + * Change the template for this domain name to have the proper CERTIFICATE + * An algorithm determine the best possible certificate, which may be a BAD one + * (like a generic self-signed for localhost as a last chance) + */ + public function hook_updatedomains_web_before($subdomid) { + global $db, $msg, $dom; + $msg->log("ssl", "hook_updatedomains_web_before($subdomid)"); + + $db->query("SELECT sd.*, dt.only_dns, dt.has_https_option, m.login FROM domaines_type dt, sub_domaines sd LEFT JOIN membres m ON m.uid=sd.compte WHERE dt.name=sd.type AND sd.web_action!='OK' AND id=?;",array($subdomid)); + $db->next_record(); + $subdom=$db->Record; + $domtype=$dom->domains_type_get($subdom["type"]); + // the domain type must be a "dns_only=false" one: + if ($domtype["only_dns"]==true) { + return; // nothing to do : this domain type does not involve Vhosts + } + $subdom["fqdn"]=$subdom["sub"].(($subdom["sub"])?".":"").$subdom["domaine"]; + + list($cert) = $this->get_valid_certs($fqdn, $subdom["provider"]); + // Edit certif_hosts: + $db->query("UPDATE sub_domaines SET certificate_id=? WHERE id=?;",array($cert["id"], $subdom["id"])); + } + // ---------------------------------------------------------------- /** Search for the best certificate for a user and a fqdn @@ -627,7 +687,25 @@ SELECT ?,?,?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?, ?, sslcsr FROM certificate // get the first good certificate: list($cert) = $this->get_valid_certs($fqdn, $subdom["provider"]); + $this->write_cert_file($cert); + // we have the files, let's fill the output array : + $output=array( + "id" => $cert["id"], + "crt" => $CRTDIR . "/" . $cert["id"].".pem", + "key" => $CRTDIR . "/" . $cert["id"].".key", + ); + if (file_exists($CRTDIR . "/" . $cert["id"].".chain")) { + $output["chain"] = $CRTDIR . "/" . $cert["id"].".chain"; + } + return $output; + } + + // ----------------------------------------------------------------- + /** Write certificate file into KEY_REPOSITORY + * @param $cert array an array with ID sslcrt sslkey sslchain + */ + function write_cert_file($cert) { // we split the certificates by 1000 $CRTDIR = self::KEY_REPOSITORY . "/" . floor($cert["id"]/1000); @mkdir($CRTDIR,0750,true); @@ -659,16 +737,6 @@ SELECT ?,?,?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?, ?, sslcsr FROM certificate chmod($CRTDIR . "/" . $cert["id"].".chain",0640); } } - // we have the files, let's fill the output array : - $output=array( - "id" => $cert["id"], - "crt" => $CRTDIR . "/" . $cert["id"].".pem", - "key" => $CRTDIR . "/" . $cert["id"].".key", - ); - if (file_exists($CRTDIR . "/" . $cert["id"].".chain")) { - $output["chain"] = $CRTDIR . "/" . $cert["id"].".chain"; - } - return $output; } diff --git a/src/update_domains.sh b/src/update_domains.sh index feb2f923..3869ac72 100755 --- a/src/update_domains.sh +++ b/src/update_domains.sh @@ -1,5 +1,12 @@ #!/bin/bash -# Update domain next-gen by fufroma +# This is now done using PHP-only scripting + +/usr/lib/alternc/update_domains.php + +exit + +# legacy code here + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin for CONFIG_FILE in \ From 9b7332f35405574868093ad526b6618879487aca Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Sun, 8 Jul 2018 22:03:27 +0200 Subject: [PATCH 03/30] [enh] adding DKIM and AUTODISCOVER management in SUB_DOMAINES, finishing BIND update_domains.php --- bureau/class/functions.php | 51 +++ bureau/class/m_bind.php | 643 +++++++++-------------------------- bureau/class/m_dom.php | 2 +- bureau/class/m_mail.php | 147 +++++++- install/mysql.sql | 8 +- install/upgrades/3.5.0.1.sql | 33 +- install/upgrades/3.5.0.2.php | 32 ++ 7 files changed, 407 insertions(+), 509 deletions(-) create mode 100644 install/upgrades/3.5.0.2.php diff --git a/bureau/class/functions.php b/bureau/class/functions.php index b9c94817..cd9e782c 100755 --- a/bureau/class/functions.php +++ b/bureau/class/functions.php @@ -1245,3 +1245,54 @@ function _dovecot_hash($password) { $hash = _sha512cr($password); return '{SHA512-CRYPT}' . $hash; } + + +// ------------------------------------------------------------ +/** + * Add the line of text $line into file $file. + * do not duplicate (check) + * @param $file string the full path to the file where we should add the line + * @param $line string the line to add (without the termination \n, WILL BE ADDED) + * @return boolean TRUE if the line has been added, or FALSE if the line ALREADY EXISTED + */ +function add_line_to_file($file,$line) { + $f=fopen($file,"rb"); + $found=false; + while($s=fgets($f,1024)) { + if (trim($s)==$line) { + $found=true; + return false; + } + } + fclose($f); + $f=fopen($file,"ab"); + fputs($f,trim($line)."\n"); + fclose($f); + return true; +} + + +// ------------------------------------------------------------ +/** + * Remove the line of text $line from file $file. + * @param $file string the full path to the file where we should remove the line + * @param $line string the line to add (without the termination \n, WILL BE REMOVED) + * @return boolean TRUE if the line has been found and removed, or FALSE if the line DIDN'T EXIST + */ +function del_line_from_file($file,$line) { + $f=fopen($file,"rb"); + $g=fopen($file.".new","wb"); + $found=false; + while($s=fgets($f,1024)) { + if (trim($s)!=$line) { + fputs($g,$s); + } else { + $found=true; + } + } + fclose($f); + fclose($g); + rename($file.".new",$file); // overwrite atomically + return $found; +} + diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index c5080629..e85b42ad 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -27,41 +27,132 @@ class m_bind { var $shouldreload; var $shouldreconfig; - var $ZONE_TEMPLATE ="/etc/alternc/templates/bind/templates/zone.template"; var $NAMED_TEMPLATE ="/etc/alternc/templates/bind/templates/named.template"; var $NAMED_CONF ="/var/lib/alternc/bind/automatic.conf"; var $RNDC ="/usr/sbin/rndc"; - var $dkim_trusted_host_file = "/etc/opendkim/TrustedHosts"; - var $dkim_keytable_file = "/etc/opendkim/KeyTable"; - var $dkim_signingtable_file = "/etc/opendkim/SigningTable"; + var $zone_file_directory = '/var/lib/alternc/bind/zones'; - var $cache_conf_db = array(); - var $cache_get_persistent = array(); - var $cache_zone_file = array(); - var $cache_domain_summary = array(); - var $zone_file_directory = '/var/lib/alternc/bind/zones/'; - - - // launched before any action by updatedomains + // ------------------------------------------------------------ + /** Hook launched before any action by updatedomains + * initialize the reload/reconfig flags used by POST + * @NOTE launched as ROOT + */ function hook_updatedomains_dns_pre() { $this->shouldreload=false; $this->shouldreconfig=false; } - // launched for each ZONE for which we want a zone update (or create) - function hook_updatedomains_dns_add($domain) { - + + // ------------------------------------------------------------ + /** + * Hook launched for each ZONE for which we want a zone update (or create) + * update the zone, create it if necessary, + * and ask for reload or reconfig of bind9 depending on what happened + * @NOTE launched as ROOT + */ + function hook_updatedomains_dns_add($dominfo) { + global $L_FQDN,$L_NS1_HOSTNAME,$L_NS2_HOSTNAME,$L_DEFAULT_MX,$L_DEFAULT_SECONDARY_MX,$L_PUBLIC_IP,$L_PUBLIC_IPV6; + + $domain = $dominfo["domaine"]; + $ttl = $dominfo["zonettl"]; + + // does it already exist? + if (file_exists($this->zone_file_directory."/".$domain)) { + list($islocked,$serial,$more)=$this->read_zone($domain); + $serial++; // only increment serial for new zones + } else { + $more=""; + $serial=date("Ymd")."00"; + $islocked=false; + } + if ($islocked) return; + + // Prepare a new zonefile from a template + $zone = file_get_contents($this->ZONE_TEMPLATE); + // substitute ALTERNC & domain variables + $zone = strtr($zone, array( + "%%fqdn%%" => "$L_FQDN", + "%%ns1%%" => "$L_NS1_HOSTNAME", + "%%ns2%%" => "$L_NS2_HOSTNAME", + "%%DEFAULT_MX%%" => "$L_DEFAULT_MX", + "%%DEFAULT_SECONDARY_MX%%" => "$L_DEFAULT_SECONDARY_MX", + "@@fqdn@@" => "$L_FQDN", + "@@ns1@@" => "$L_NS1_HOSTNAME", + "@@ns2@@" => "$L_NS2_HOSTNAME", + "@@DEFAULT_MX@@" => "$L_DEFAULT_MX", + "@@DEFAULT_SECONDARY_MX@@" => "$L_DEFAULT_SECONDARY_MX", + "@@DOMAINE@@" => $domain, + "@@SERIAL@@" => $serial, + "@@PUBLIC_IP@@" => "$L_PUBLIC_IP", + "@@PUBLIC_IPV6@@" => "$L_PUBLIC_IPV6", + "@@ZONETTL@@" => $ttl, + )); + + // add the SUBDOMAIN entries + $zone .= $this->conf_from_db($domain); + + // add the "END ALTERNC CONF line"; + $zone .= ";;; END ALTERNC AUTOGENERATE CONFIGURATION\n"; + + // add the manually entered info: + $zone .= $more; + file_put_contents($this->zone_file_directory."/".$domain,$zone); + + // add the line into bind9 conf: + if (add_line_to_file( + $this->NAMED_CONF, + trim(strtr( + file_get_contents($this->NAMED_TEMPLATE), + array( + "@@DOMAIN@@" => $DOMAIN, + "@@ZONE_FILE@@" => $this->zone_file_directory."/".$domain + ) + ))) + ) { + $this->shouldreconfig=true; + } else { + $this->shouldreload=true; + } } - // launched for each ZONE for which we want a zone DELETE - function hook_updatedomains_dns_del($domain) { - + + // ------------------------------------------------------------ + /** + * Hook launched for each ZONE for which we want a zone DELETE + * remove the zone and its file, + * and if any action happened, ask for bind RECONFIG at posttime + * @NOTE launched as ROOT + */ + function hook_updatedomains_dns_del($dominfo) { + $domain = $dominfo["domaine"]; + if (remove_line_from_file( + $this->NAMED_CONF, + trim(strtr( + file_get_contents($this->NAMED_TEMPLATE), + array( + "@@DOMAIN@@" => $domain, + "@@ZONE_FILE@@" => $this->zone_file_directory."/".$domain + ) + ))) + ) { + $this->shouldreconfig=true; + } else { + return; + } + @unlink($this->zone_file_directory."/".$domain); } - // launched at the very end of updatedomains + + // ------------------------------------------------------------ + /** + * Hook function launched at the very end of updatedomains + * here, we just reload OR reconfig (or both) bind9 depending + * on what happened before. + * @NOTE launched as ROOT + */ function hook_updatedomains_dns_post() { global $msg; if ($this->shouldreload) { @@ -84,489 +175,63 @@ class m_bind { } } + + // ------------------------------------------------------------ + /** + * read a zone file for $domain, + * @param $domain string the domain name + * @return array with 3 informations: + * is the domain locked? (boolean), what's the current serial (integer), the data after alternc conf (string of lines) + */ + function read_zone($domain) { + $f=fopen($this->zone_file_directory."/".$domain,"rb"); + $islocked=false; + $more=""; + $serial=date("Ymd")."00"; + while ($s=fgets($f,4096)) { + if (preg_match("#\;\s*LOCKED:YES#i",$s)) { + $islocked=true; + } + if (preg_match("/\s*(\d{10})\s+\;\sserial\s?/", $s,$mat)) { + $serial=$mat[1]; + } + if (preg_match('/\;\s*END\sALTERNC\sAUTOGENERATE\sCONFIGURATION(.*)/s', $s)) { + break; + } + } + while ($s=fgets($f,4096)) { + $more.=$s; + } + return array($islocked,$serial,$more); + } + // ------------------------------------------------------------ /** - * Return the part of the conf we got from the database - * + * Return the part of the conf we got from the sub_domaines table * @global m_mysql $db * @param string $domain - * @return array $this->cache_conf_db + * @return string a zonefile excerpt */ - function conf_from_db($domain=false) { + function conf_from_db($domain) { global $db; - // Use cache, fill cache if empty - if (empty($this->cache_conf_db)) { - $db->query(" - select - sd.domaine, - replace(replace(dt.entry,'%TARGET%',sd.valeur), '%SUB%', if(length(sd.sub)>0,sd.sub,'@')) as entry - from + $db->query(" + SELECT + REPLACE(REPLACE(dt.entry,'%TARGET%',sd.valeur), '%SUB%', if(length(sd.sub)>0,sd.sub,'@')) AS ENTRY + FROM sub_domaines sd, domaines_type dt - where + WHERE sd.type=dt.name - and sd.enable in ('ENABLE', 'ENABLED') - order by entry ;"); - $t=array(); - while ($db->next_record()) { - $t[$db->f('domaine')][] = $db->f('entry'); - } - $this->cache_conf_db = $t; + AND sd.enable IN ('ENABLE', 'ENABLED') + ORDER BY ENTRY ;"); + $t=array(); + while ($db->next_record()) { + $t.= $db->f('entry')."\n"; } - if ($domain) { - if (isset($this->cache_conf_db[$domain])) { - return $this->cache_conf_db[$domain]; - } else { - return array(); - } - } // if domain - return $this->cache_conf_db; + return $t; } - - /** - * Return full path of the zone configuration file - * - * @param string $domain - * @return string - */ - function get_zone_file_uri($domain) { - return $this->zone_file_directory.$domain; - } - - - /** - * - * @param string $domain - * @return string zone file path - */ - function get_zone_file($domain) { - // Use cache, fill cache if empty - if (!isset($this->cache_zone_file[$domain]) ) { - if (file_exists($this->get_zone_file_uri($domain))) { - $this->cache_zone_file[$domain] = @file_get_contents($this->get_zone_file_uri($domain)); - } else { - $this->cache_zone_file[$domain] = false; - } - } - return $this->cache_zone_file[$domain] ; - } - - - /** - * - * @param string $domain - * @return string - */ - function get_serial($domain) { - // Return the next serial the domain must have. - // Choose between a generated and an incremented. - - // Calculated : - $calc = date('Ymd').'00'."\n"; - - // Old one : - $old=$calc; // default value - $file = $this->get_zone_file($domain); - preg_match_all("/\s*(\d{10})\s+\;\sserial\s?/", $file, $output_array); - if (isset($output_array[1][0]) && !empty($output_array[1][0])) { - $old = $output_array[1][0]; - } - - // Return max between newly calculated, and old one incremented - return max(array($calc,$old)) + 1 ; - } - - - /** - * Return lines that are after ;;; END ALTERNC AUTOGENERATE CONFIGURATION - * - * @param string $domain - * @return string - */ - function get_persistent($domain) { - if ( ! isset($this->cache_get_persistent[$domain] )) { - preg_match_all('/\;\s*END\sALTERNC\sAUTOGENERATE\sCONFIGURATION(.*)/s', $this->get_zone_file($domain), $output_array); - if (isset($output_array[1][0]) && !empty($output_array[1][0])) { - $this->cache_get_persistent[$domain] = $output_array[1][0]; - } else { - $this->cache_get_persistent[$domain] = false; - } - } // isset - return $this->cache_get_persistent[$domain]; - } - - - /** - * - * @return string - */ - function get_zone_header() { - return file_get_contents($this->ZONE_TEMPLATE); - } - - - /** - * - * @global m_dom $dom - * @param string $domain - * @return array Retourne un tableau - */ - function get_domain_summary($domain=false) { - global $dom; - - // Use cache if is filled, if not, fill it - if (empty($this->cache_domain_summary)) { - $this->cache_domain_summary = $dom->get_domain_all_summary(); - } - - if ($domain) return $this->cache_domain_summary[$domain]; - else return $this->cache_domain_summary; - } - - - /** - * - * @param string $domain - * @return boolean - */ - function dkim_delete($domain) { - $target_dir = "/etc/opendkim/keys/$domain"; - if (file_exists($target_dir)) { - @unlink("$target_dir/alternc_private"); - @unlink("$target_dir/alternc.txt"); - @rmdir($target_dir); - } - return true; - } - - - /** - * Generate the domain DKIM key - * - * @param string $domain - * @return null|boolean - */ - function dkim_generate_key($domain) { - // Stop here if we do not manage the mail - $domainInfo = $this->get_domain_summary($domain); - if ( ! $domainInfo['gesmx'] ) return; - - $target_dir = "/etc/opendkim/keys/$domain"; - - if (file_exists($target_dir.'/alternc.txt')) return; // Do not generate if exist - - if (! is_dir($target_dir)) mkdir($target_dir); // create dir - - // Generate the key - $old_dir=getcwd(); - chdir($target_dir); - exec('opendkim-genkey -r -d '.escapeshellarg($domain).' -s "alternc" '); - chdir($old_dir); - - // opendkim must be owner of the key - chown("$target_dir/alternc.private", 'opendkim'); - chgrp("$target_dir/alternc.private", 'opendkim'); - - return true; // FIXME handle error - } - - - /** - * Refresh DKIM configuration: be sure to list the domain having a private key (and only them) - */ - function dkim_refresh_list() { - // so ugly... but there is only 1 pass, not 3. Still ugly. - $trusted_host_new = "# WARNING: this file is auto generated by AlternC.\n# Add your changes after the last line\n"; - $keytable_new = "# WARNING: this file is auto generated by AlternC.\n# Add your changes after the last line\n"; - $signingtable_new = "# WARNING: this file is auto generated by AlternC.\n# Add your changes after the last line\n"; - - # Generate automatic entry - foreach ($this->get_domain_summary() as $domain => $ds ) { - // Skip if delete in progress, or if we do not manage dns or mail - if ( ! $ds['gesdns'] || ! $ds['gesmx'] || strtoupper($ds['dns_action']) == 'DELETE' ) continue; - - // Skip if there is no key generated - if (! file_exists("/etc/opendkim/keys/$domain/alternc.txt")) continue; - - // Modif the files. - $trusted_host_new.="$domain\n"; - $keytable_new .="alternc._domainkey.$domain $domain:alternc:/etc/opendkim/keys/$domain/alternc.private\n"; - $signingtable_new.="$domain alternc._domainkey.$domain\n"; - } - $trusted_host_new.="# END AUTOMATIC FILE. ADD YOUR CHANGES AFTER THIS LINE\n"; - $keytable_new .="# END AUTOMATIC FILE. ADD YOUR CHANGES AFTER THIS LINE\n"; - $signingtable_new.="# END AUTOMATIC FILE. ADD YOUR CHANGES AFTER THIS LINE\n"; - - # Get old files - $trusted_host_old=@file_get_contents($this->dkim_trusted_host_file); - $keytable_old =@file_get_contents($this->dkim_keytable_file); - $signingtable_old=@file_get_contents($this->dkim_signingtable_file); - - # Keep manuel entry - preg_match_all('/\#\s*END\ AUTOMATIC\ FILE\.\ ADD\ YOUR\ CHANGES\ AFTER\ THIS\ LINE(.*)/s', $trusted_host_old, $output_array); - if (isset($output_array[1][0]) && !empty($output_array[1][0])) { - $trusted_host_new.=$output_array[1][0]; - } - preg_match_all('/\#\s*END\ AUTOMATIC\ FILE\.\ ADD\ YOUR\ CHANGES\ AFTER\ THIS\ LINE(.*)/s', $keytable_old, $output_array); - if (isset($output_array[1][0]) && !empty($output_array[1][0])) { - $keytable_new.=$output_array[1][0]; - } - preg_match_all('/\#\s*END\ AUTOMATIC\ FILE\.\ ADD\ YOUR\ CHANGES\ AFTER\ THIS\ LINE(.*)/s', $signingtable_old, $output_array); - if (isset($output_array[1][0]) && !empty($output_array[1][0])) { - $signingtable_new.=$output_array[1][0]; - } - - // Save if there are some diff - if ( $trusted_host_new != $trusted_host_old ) { - file_put_contents($this->dkim_trusted_host_file, $trusted_host_new); - } - if ( $keytable_new != $keytable_old ) { - file_put_contents($this->dkim_keytable_file, $keytable_new); - } - if ( $signingtable_new != $signingtable_old ) { - file_put_contents($this->dkim_signingtable_file, $signingtable_new); - } - - } - - - /** - * - * @param string $domain - * @return string - */ - function dkim_entry($domain) { - $keyfile="/etc/opendkim/keys/$domain/alternc.txt"; - $domainInfo = $this->get_domain_summary($domain); - if (! file_exists($keyfile) && $domainInfo['gesmx'] ) { - $this->dkim_generate_key($domain); - } - return @file_get_contents($keyfile); - } - - - /** - * Conditionnal generation autoconfig entry for outlook / thunderbird - * If entry with the same name allready exist, skip it. - * - * @param string $domain - * @return string - */ - function mail_autoconfig_entry($domain) { - $zone= implode("\n",$this->conf_from_db($domain))."\n".$this->get_persistent($domain); - - $entry=''; - $domainInfo = $this->get_domain_summary($domain); - if ( $domainInfo['gesmx'] ) { - // If we manage the mail - - // Check if there is no the same entry (defined or manual) - // can be toto IN A or toto.fqdn.tld. IN A - if (! preg_match("/autoconfig(\s|\.".str_replace('.','\.',$domain)."\.)/", $zone )) { - $entry.="autoconfig IN CNAME %%fqdn%%.\n"; - } - if (! preg_match("/autodiscover(\s|\.".str_replace('.','\.',$domain)."\.)/", $zone )) { - $entry.="autodiscover IN CNAME %%fqdn%%.\n"; - } - } // if gesmx - return $entry; - } - - - /** - * - * Return a fully generated zone - * - * @global string $L_FQDN - * @global string $L_NS1_HOSTNAME - * @global string $L_NS2_HOSTNAME - * @global string $L_DEFAULT_MX - * @global string $L_DEFAULT_SECONDARY_MX - * @global string $L_PUBLIC_IP - * @param string $domain - * @return string - */ - function get_zone($domain) { - global $L_FQDN, $L_NS1_HOSTNAME, $L_NS2_HOSTNAME, $L_DEFAULT_MX, $L_DEFAULT_SECONDARY_MX, $L_PUBLIC_IP; - - $zone =$this->get_zone_header(); - $zone.=implode("\n",$this->conf_from_db($domain)); - $zone.="\n;;;HOOKED ENTRY\n"; - - $zone.= $this->dkim_entry($domain); - $zone.= $this->mail_autoconfig_entry($domain); - - $zone.="\n;;; END ALTERNC AUTOGENERATE CONFIGURATION\n"; - $zone.=$this->get_persistent($domain); - $domainInfo = $this->get_domain_summary($domain); - - // FIXME check those vars - $zone = strtr($zone, array( - "%%fqdn%%"=>"$L_FQDN", - "%%ns1%%"=>"$L_NS1_HOSTNAME", - "%%ns2%%"=>"$L_NS2_HOSTNAME", - "%%DEFAULT_MX%%"=>"$L_DEFAULT_MX", - "%%DEFAULT_SECONDARY_MX%%"=>"$L_DEFAULT_SECONDARY_MX", - "@@fqdn@@"=>"$L_FQDN", - "@@ns1@@"=>"$L_NS1_HOSTNAME", - "@@ns2@@"=>"$L_NS2_HOSTNAME", - "@@DEFAULT_MX@@"=>"$L_DEFAULT_MX", - "@@DEFAULT_SECONDARY_MX@@"=>"$L_DEFAULT_SECONDARY_MX", - "@@DOMAINE@@"=>"$domain", - "@@SERIAL@@"=>$this->get_serial($domain), - "@@PUBLIC_IP@@"=>"$L_PUBLIC_IP", - "@@ZONETTL@@"=> $domainInfo['zonettl'], - )); - - return $zone; - } - - - /** - * - * @param string $domain - */ - function reload_zone($domain) { - exec($this->RNDC." reload ".escapeshellarg($domain), $output, $return_value); - if ($return_value != 0 ) { - echo "ERROR: Reload zone failed for zone $domain\n"; - } - } - - - /** - * return true if zone is locked - * - * @param string $domain - * @return boolean - */ - function is_locked($domain) { - preg_match_all("/(\;\s*LOCKED:YES)/i", $this->get_zone_file($domain), $output_array); - if (isset($output_array[1][0]) && !empty($output_array[1][0])) { - return true; - } - return false; - } - - - /** - * - * @global m_mysql $db - * @global m_dom $dom - * @param string $domain - * @return boolean - */ - function save_zone($domain) { - global $db, $dom; - - // Do not save if the zone is LOCKED - if ( $this->is_locked($domain)) { - $dom->set_dns_result($domain, "The zone file of this domain is locked. Contact your administrator."); // If edit, change dummy_for_translation - $dom->set_dns_action($domain, 'OK'); - return false; - } - - // Save file, and apply chmod/chown - $file=$this->get_zone_file_uri($domain); - file_put_contents($file, $this->get_zone($domain)); - chown($file, 'bind'); - chmod($file, 0640); - - $dom->set_dns_action($domain, 'OK'); - return true; // fixme add tests - } - - - /** - * Delete the zone configuration file - * - * @param string $domain - * @return boolean - */ - function delete_zone($domain) { - $file=$this->get_zone_file_uri($domain); - if (file_exists($file)) { - unlink($file); - } - $this->dkim_delete($domain); - return true; - } - - - /** - * - * @global m_hooks $hooks - * @return boolean - */ - function reload_named() { - global $hooks; - // Generate the new conf file - $new_named_conf="// DO NOT EDIT\n// This file is generated by Alternc.\n// Every changes you'll make will be overwrited.\n"; - $tpl=file_get_contents($this->NAMED_TEMPLATE); - foreach ($this->get_domain_summary() as $domain => $ds ) { - if ( ! $ds['gesdns'] || strtoupper($ds['dns_action']) == 'DELETE' ) continue; - $new_named_conf.=strtr($tpl, array("@@DOMAINE@@"=>$domain, "@@ZONE_FILE@@"=>$this->get_zone_file_uri($domain))); - } - - // Get the actual conf file - $old_named_conf = @file_get_contents($this->NAMED_CONF); - - // Apply new configuration only if there are some differences - if ($old_named_conf != $new_named_conf ) { - file_put_contents($this->NAMED_CONF,$new_named_conf); - chown($this->NAMED_CONF, 'bind'); - chmod($this->NAMED_CONF, 0640); - exec($this->RNDC." reconfig"); - $hooks->invoke_scripts("/usr/lib/alternc/reload.d", array('dns_reconfig') ); - } - - return true; - } - - - /** - * Regenerate bind configuration and load it - * - * @global m_hooks $hooks - * @param boolean $all - * @return boolean - */ - function regenerate_conf($all=false) { - global $hooks; - - foreach ($this->get_domain_summary() as $domain => $ds ) { - if ( ! $ds['gesdns'] && strtoupper($ds['dns_action']) == 'OK' ) continue; // Skip if we do not manage DNS and is up-to-date for this domain - - if ( (strtoupper($ds['dns_action']) == 'DELETE' ) || - (strtoupper($ds['dns_action']) == 'UPDATE' && $ds['gesdns']==false ) // in case we update the zone to disable DNS management - ) { - $this->delete_zone($domain); - continue; - } - - if ( ( $all || strtoupper($ds['dns_action']) == 'UPDATE' ) && $ds['gesdns'] ) { - $this->save_zone($domain); - $this->reload_zone($domain); - $hooks->invoke_scripts("/usr/lib/alternc/reload.d", array('dns_reload_zone', $domain) ); - } - } // end foreach domain - - $this->dkim_refresh_list(); - $this->reload_named(); - return true; - } - - - /** - * - */ - private function dummy_for_translation() { - _("The zone file of this domain is locked. Contact your administrator."); - } - - } // m_bind diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index bafab4de..8cabd458 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1442,7 +1442,7 @@ class m_dom { * TRUE sinon. * */ - function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 86400) { + function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 3600) { global $db, $msg, $hooks; $msg->log("dom", "edit_domain", $dom . "/" . $dns . "/" . $gesmx); // Locked ? diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index b7f02735..9a370fe7 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -469,9 +469,9 @@ ORDER BY if ($db->next_record()) { $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND (sub='' AND valeur LIKE 'v=spf1 %') OR (sub='_dmarc' AND valeur LIKE 'v=dmarc1;%');", array($db->Record["domaine"])); $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND (type='defmx' OR type='defmx2');", array($db->Record["domaine"])); - $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id= ? ;", array($dom_id)); + $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id= ? ;", array($dom_id)); } - + return true; } @@ -912,6 +912,7 @@ ORDER BY } + // ------------------------------------------------------------ /** * hook function called by AlternC when a domain is created for * the current user account using the SLAVE DOMAIN feature @@ -928,6 +929,7 @@ ORDER BY } + // ------------------------------------------------------------ /** * hook function called by AlternC when a domain is created for * the current user account @@ -937,7 +939,7 @@ ORDER BY * @access private */ function hook_dom_add_mx_domain($domain_id) { - global $msg, $mem, $db; + global $msg, $mem, $db, $L_FQDN; $msg->log("mail", "hook_dom_add_mx_domain", $domain_id); $db->query("SELECT value FROM variable where name='mailname_bounce';"); @@ -947,8 +949,9 @@ ORDER BY } $mailname = $db->f("value"); // set spf & dmarc for this domain - $db->query("SELECT domaine FROM domaines WHERE id= ?;", array($domain_id)); + $db->query("SELECT domaine,compte FROM domaines WHERE id= ?;", array($domain_id)); if ($db->next_record()) { + $this->set_dns_autoconf($db->Record["domaine"],$db->Record["compte"]); if ($spf = variable_get("default_spf_value")) { $this->set_dns_spf($db->Record["domaine"], $spf); } @@ -960,13 +963,14 @@ ORDER BY } + // ------------------------------------------------------------ /** * hook function called by variables when a variable is changed * @access private */ function hook_variable_set($name, $old, $new) { global $msg, $db; - $msg->log("mail", "hook_variable_set($name,$old,$new)"); + $msg->log("mail", "hook_variable_set($name,$old,$new)"); if ($name == "default_spf_value") { $new = trim($new); @@ -991,7 +995,34 @@ ORDER BY } } + + // ------------------------------------------------------------ + /** + * Add dns entries for autodiscover / autoconf on the domain + */ + function set_dns_autoconf($domain,$uid=-1) { + global $db, $L_FQDN, $cuid; + $changed=false; + if ($uid==-1) $uid=$cuid; + $db->query("SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autodiscover' AND type='autodiscover';",array($domain)); + if (!$db->next_record()) { + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autodiscover', type='autodiscover';",array($domain,$uid)); + $changed=true; + } + $db->query("SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autoconfig' AND type='autodiscover';",array($domain)); + if (!$db->next_record()) { + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autoconfig', type='autodiscover';",array($domain,$uid)); + $changed=true; + } + if ($changed) { + $db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain)); + } + return $changed; + } + + + // ------------------------------------------------------------ /** * Set or UPDATE the DNS record for the domain $dom(str) to be $spf * account's login is current and if not it's $login. @@ -1022,6 +1053,7 @@ ORDER BY } + // ------------------------------------------------------------ /** * Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc * account's login is current and if not it's $login. @@ -1055,5 +1087,110 @@ ORDER BY } + /** Manage DKIM when adding / removing a domain MX management */ + var $shouldreloaddkim; + + + // ------------------------------------------------------------ + /** + * Hook launched before doing anything dns-related + */ + function hook_updatedomains_dns_pre() { + global $db; + // for each domain where we don't have the MX or the DNS, remove the DKIM setup + $this->shouldreloaddkim=false; + $db->query("SELECT domaine,gesdns,gesmx FROM domaines WHERE dns_action!='OK';"); + $add=array(); + $del=array(); + while ($db->next_record()) { + if ($db->Record["gesdns"]==0 || $db->Record["gesmx"]==0) { + $del[]=$db->Record["domaine"]; + } else { + $add[]=$db->Record["domaine"]; + } + } + foreach($add as $domain) { + $this->dkim_add($domain); + } + foreach($del as $domain) { + $this->dkim_del($domain); + } + } + + + // ------------------------------------------------------------ + /** + * Hook launched after doing anything dns-related + */ + function hook_updatedomains_dns_post() { + if ($this->shouldreloaddkim) { + exec("service opendkim reload"); + $this->shouldreloaddkim=false; + } + } + + + // ------------------------------------------------------------ + /** + * Add a domain into OpenDKIM configuration + */ + function dkim_add($domain) { + global $db; + $target_dir = "/etc/opendkim/keys/$domain"; + if (file_exists($target_dir.'/alternc.txt')) return; // Do not generate if exist + $this->shouldreloaddkim=true; + if (! is_dir($target_dir)) mkdir($target_dir); // create dir + // Generate the key + $old_dir=getcwd(); + chdir($target_dir); + exec('opendkim-genkey -b 1200 -r -d '.escapeshellarg($domain).' -s "alternc" '); + chdir($old_dir); + // opendkim must be owner of the key + chown("$target_dir/alternc.private", 'opendkim'); + chgrp("$target_dir/alternc.private", 'opendkim'); + + // Add line into files: + add_line_to_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private"); + add_line_to_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); + // Add subdomaine entry + $dkim_key=$this->dkim_get_entry($domain); + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='', type='dkim', valeur=?;",array($uid,$domain,$dkim_key)); + // no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain + } + + + // ------------------------------------------------------------ + /** + * Delete a domain from OpenDKIM configuration + */ + function dkim_del($domain) { + $target_dir = "/etc/opendkim/keys/$domain"; + if (file_exists($target_dir)) { + $this->shouldreloaddkim=true; + @unlink("$target_dir/alternc_private"); + @unlink("$target_dir/alternc.txt"); + @rmdir($target_dir); + del_line_from_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private"); + del_line_from_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); + } + } + + + // ------------------------------------------------------------ + /** + * return the content of the TXT information to be added into the DB for DKIM subdomains + * @param $domain string the name of the domain name + * @return string the TXT entry (without quotes) + * or false if an error occurred + **/ + function dkim_get_entry($domain) { + $key=file_get_contents("/etc/opendkim/keys/".$domain."/alternc.txt"); + if (preg_match('#alternc._domainkey IN TXT "(.*)"#',$key,$mat)) { + return $mat[1]; + } + return false; + } + + // @TODO hook after reloading DNS zones => if necessary, restart opendkim } /* Class m_mail */ diff --git a/install/mysql.sql b/install/mysql.sql index 2e43504c..4fd129f0 100644 --- a/install/mysql.sql +++ b/install/mysql.sql @@ -115,7 +115,7 @@ CREATE TABLE IF NOT EXISTS domaines ( noerase tinyint(4) NOT NULL default '0', dns_action enum ('OK','UPDATE','DELETE') NOT NULL default 'UPDATE', dns_result varchar(255) not null default '', - zonettl int(10) unsigned NOT NULL default '86400', + zonettl int(10) unsigned NOT NULL default '3600', PRIMARY KEY (id), UNIQUE KEY (domaine) ) ENGINE=InnoDB; @@ -479,8 +479,10 @@ CREATE TABLE IF NOT EXISTS `domaines_type` ( PRIMARY KEY ( `name` ) ) ENGINE=InnoDB COMMENT = 'Type of domains allowed'; -INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) values -('vhost', 'Locally hosted', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'ALL'), +INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES +('dkim', 'DKIM Key', 'NONE', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'), +('autodiscover', 'Autodiscover and autoconf for email', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'), + ('vhost', 'Locally hosted', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'ALL'), ('url', 'URL redirection', 'URL', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2', false, false, false, 'ALL'), ('ip', 'IPv4 redirect', 'IP', '%SUB% IN A %TARGET%', 'url,ip,ipv6,txt,mx,mx2,defmx,defmx2', true, true, false, 'ALL'), ('ipv6', 'IPv6 redirect', 'IPV6', '%SUB% IN AAAA %TARGET%', 'ip,ipv6,txt,mx,mx2,defmx,defmx2', true, true, true, 'ALL'), diff --git a/install/upgrades/3.5.0.1.sql b/install/upgrades/3.5.0.1.sql index 49fc7f44..0cb4a949 100644 --- a/install/upgrades/3.5.0.1.sql +++ b/install/upgrades/3.5.0.1.sql @@ -1,13 +1,22 @@ --- upgrade from 3.4.10 and 3.4.11 (a bug prevented them to be inserted :/ ) +-- migrating DKIM to be inside sub_domaines table +INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES +('dkim', 'DKIM Key', 'NONE', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'); +-- migrating AUTODISCOVER / AUTOCONF to be inside sub_domaines table +INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES +('autodiscover', 'Autodiscover and autoconf for email', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'); + + +-- upgrade from 3.4.10 and 3.4.11 (a bug prevented them to be inserted :/ ) ALTER TABLE mailbox MODIFY `lastlogin` DATETIME NOT NULL DEFAULT 0; ALTER TABLE mailbox ADD `lastloginsasl` DATETIME NOT NULL DEFAULT 0 AFTER `lastlogin`; +ALTER TABLE `domaines` MODIFY `zonettl` INT(10) UNSIGNED NOT NULL default '3600'; -ALTER TABLE `membres` MODIFY `pass` varchar(255); -ALTER TABLE `ftpusers` MODIFY `encrypted_password` varchar(255); +-- upgrade to better hashes ($6$, 20000 loops) in membres and ftpusers +ALTER TABLE `membres` MODIFY `pass` VARCHAR(255); +ALTER TABLE `ftpusers` MODIFY `encrypted_password` VARCHAR(255); -- upgrade to merge alternc-ssl into alternc + change the way we work on SSL - DROP TABLE IF EXISTS `certif_alias`; ALTER TABLE `certificates` @@ -28,7 +37,8 @@ ALTER TABLE `domaines_type` UPDATE `domaines_type` SET `has_https_option`=1 WHERE name='vhost'; -- Backport old certif_hosts data to sub_domaines -UPDATE `sub_domaines` LEFT JOIN `certif_hosts` ON `sub_domaines`.`id` = `certif_hosts`.`sub` SET `sub_domaines`.`certificate_id` = `certif_hosts`.`certif` WHERE 1; +UPDATE `sub_domaines` LEFT JOIN `certif_hosts` ON `sub_domaines`.`id` = `certif_hosts`.`sub` + SET `sub_domaines`.`certificate_id` = `certif_hosts`.`certif`; DROP TABLE IF EXISTS `certif_hosts`; -- Set https status (http,https,both) @@ -37,21 +47,22 @@ UPDATE `sub_domaines` SET `https` = "both" WHERE `type` LIKE '%-mixssl' AND http UPDATE `sub_domaines` SET `https` = "http" WHERE https = ''; UPDATE `sub_domaines` SET `type` = REPLACE(`type`,'-ssl',''); UPDATE `sub_domaines` SET `type` = REPLACE(`type`,'-mixssl',''); --- Disable https status when domains_type don't provide this -UPDATE `sub_domaines` SET `https` = '' WHERE type IN (select name FROM domaines_type WHERE has_https_option = 0); +-- Disable https status when domains_type don't use it +UPDATE `sub_domaines` SET `https` = '' WHERE type IN (SELECT name FROM domaines_type WHERE has_https_option = 0); --- When two sudomain exists, we consider sub_domains with http and https feature +-- When two subdomain exists, we consider sub_domains with http and https feature UPDATE sub_domaines AS sd INNER JOIN (SELECT MIN(id) id FROM `sub_domaines` GROUP BY domaine,sub,type HAVING count(id) > 1) sd1 ON sd.id = sd1.id SET `https` = "both"; -- Delete duplicate lines -DELETE sd1 FROM sub_domaines sd1, sub_domaines sd2 WHERE sd1.id > sd2.id AND sd1.domaine = sd2.domaine AND sd1.sub = sd2.sub AND sd1.type = sd2.type AND sd1.https <> '' AND sd2.https <> ''; +DELETE sd1 FROM sub_domaines sd1, sub_domaines sd2 + WHERE sd1.id > sd2.id AND sd1.domaine = sd2.domaine AND sd1.sub = sd2.sub AND sd1.type = sd2.type + AND sd1.https <> '' AND sd2.https <> ''; -- we need to regenerate all vhost, they will be by AlternC.install -- UPDATE `sub_domaines` SET `web_action` = 'UPDATE'; - -- change some variable names : UPDATE variable @@ -72,6 +83,6 @@ DELETE FROM variable WHERE name IN ( 'ftp_human_name' ); --- we'd like to prepare IPv6 ;) +-- we'd like to prepare for IPv6 ;) ALTER TABLE `domaines_type` CHANGE `entry` `entry` TEXT DEFAULT ''; diff --git a/install/upgrades/3.5.0.2.php b/install/upgrades/3.5.0.2.php new file mode 100644 index 00000000..4395db26 --- /dev/null +++ b/install/upgrades/3.5.0.2.php @@ -0,0 +1,32 @@ +query("SELECT * FROM domaines WHERE gesdns=1 AND gesmx=1;"); +$add=array(); +while ($db->next_record()) { + $add[$db->Record["domaine"]]=$db->Record["compte"]; +} +foreach($add as $domain => $id) { + // Convert DKIM keys into SUB_DOMAINES table + if (file_exists("/etc/opendkim/keys/".$domain."/alternc.txt")) { + $dkim_key = $mail->dkim_get_entry($domain); + if ($dkim_key) { + // Add subdomain dkim entry + $db->query("INSERT INTO sub_domaines + SET compte=?, domaine=?, sub='@', valeur=?, type='dkim', web_action='OK', web_result=0, enable='ENABLED';", + array($id, $domain, $dkim_key) + ); + // Alternc.INSTALL WILL reload DNS zones anyway, so fear not we don't set dns_action="RELOAD" here. + } + } + // Convert autodiscover into SUB_DOMAINES table + $db->query("INSERT INTO sub_domaines + SET compte=?, domaine=?, sub='@', valeur='', type='autodiscover', web_action='UPDATE', web_result=0, enable='ENABLED';", + array($id, $domain) + ); +} + From f898407d3de2b352906a19e73523029d0241a1d0 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Sun, 8 Jul 2018 22:19:58 +0200 Subject: [PATCH 04/30] [enh] adding update_domains.php in cron, fixing panel to NOT include autodiscover from now --- .gitignore | 1 + debian/alternc.cron.d | 4 +- etc/alternc/templates/alternc/bureau.conf | 18 ------- .../templates/apache2/autodiscover-both.conf | 53 +++++++++++++++++++ src/update_domains.php | 10 ++++ 5 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 etc/alternc/templates/apache2/autodiscover-both.conf create mode 100644 src/update_domains.php diff --git a/.gitignore b/.gitignore index c871c2c5..3c668233 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ bureau/locales/fr_FR/LC_MESSAGES/messages.po~ bureau/locales/it_IT/LC_MESSAGES/messages.po~ bureau/locales/nl_NL/LC_MESSAGES/messages.po~ bureau/locales/pt_BR/LC_MESSAGES/messages.po~ +.tx/alternc.alternc diff --git a/debian/alternc.cron.d b/debian/alternc.cron.d index f324f260..0929f5fc 100644 --- a/debian/alternc.cron.d +++ b/debian/alternc.cron.d @@ -14,8 +14,8 @@ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 0 5 * * * alterncpanel /usr/lib/alternc/sqlbackup.sh -t daily 0 4 * * 0 alterncpanel /usr/lib/alternc/sqlbackup.sh -t weekly -# Every 5 minutes, spool waiting domain changes -*/5 * * * * root /usr/lib/alternc/update_domains.sh +# Every minute, spool waiting domain changes +* * * * * root /usr/lib/alternc/update_domains.php # Every 5 minutes, do mails actions */5 * * * * root /usr/lib/alternc/update_mails.sh diff --git a/etc/alternc/templates/alternc/bureau.conf b/etc/alternc/templates/alternc/bureau.conf index 6d677e87..d834648b 100644 --- a/etc/alternc/templates/alternc/bureau.conf +++ b/etc/alternc/templates/alternc/bureau.conf @@ -5,10 +5,6 @@ DocumentRoot /usr/share/alternc/panel/admin ServerName %%fqdn%% - # Mail autoconfig - ServerAlias autoconfig.* - ServerAlias autodiscover.* - RewriteEngine on RewriteRule ^/admin/(.*) /$1 [R=301,L] @@ -17,20 +13,6 @@ RewriteEngine On RewriteRule ^webmail /webmail-redirect.php [L] - # Mail autoconfig - RewriteRule ^/mail/mailautoconfig.xml$ /mailautoconfig_thunderbird.php [L] - RewriteRule ^/mail/config-v1.1.xml$ /mailautoconfig_thunderbird.php [L] - RewriteRule ^mail/mailautoconfig.xml$ /mailautoconfig_thunderbird.php [L] - RewriteRule ^mail/config-v1.1.xml$ /mailautoconfig_thunderbird.php [L] - RewriteRule ^/autodiscover/autodiscover.xml$ /mailautoconfig_outlook.php [L] - RewriteRule ^/Autodiscover/Autodiscover.xml$ /mailautoconfig_outlook.php [L] - RewriteRule ^/Autodiscover.xml$ mailautoconfig_outlook.php [L] - RewriteRule ^/autodiscover.xml$ mailautoconfig_outlook.php [L] - RewriteRule ^autodiscover/autodiscover.xml$ /mailautoconfig_outlook.php [L] - RewriteRule ^Autodiscover/Autodiscover.xml$ /mailautoconfig_outlook.php [L] - RewriteRule ^Autodiscover.xml$ mailautoconfig_outlook.php [L] - RewriteRule ^autodiscover.xml$ mailautoconfig_outlook.php [L] - # will be used to define aliases such as /javascript /webmail /squirrelmail ... IncludeOptional /etc/alternc/apache-panel.d/*.conf diff --git a/etc/alternc/templates/apache2/autodiscover-both.conf b/etc/alternc/templates/apache2/autodiscover-both.conf new file mode 100644 index 00000000..123a09f3 --- /dev/null +++ b/etc/alternc/templates/apache2/autodiscover-both.conf @@ -0,0 +1,53 @@ + + + DocumentRoot /usr/share/alternc/panel/admin + + AssignUserId alterncpanel alterncpanel + SetEnv LOGIN "0000-panel" + + ServerName %%fqdn%% + + # Mail autoconfig + RewriteRule ^/mail/mailautoconfig.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^/mail/config-v1.1.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^mail/mailautoconfig.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^mail/config-v1.1.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^/autodiscover/autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^/Autodiscover/Autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^/Autodiscover.xml$ mailautoconfig_outlook.php [L] + RewriteRule ^/autodiscover.xml$ mailautoconfig_outlook.php [L] + RewriteRule ^autodiscover/autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^Autodiscover/Autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^Autodiscover.xml$ mailautoconfig_outlook.php [L] + RewriteRule ^autodiscover.xml$ mailautoconfig_outlook.php [L] + + + + + DocumentRoot /usr/share/alternc/panel/admin + + AssignUserId alterncpanel alterncpanel + SetEnv LOGIN "0000-panel" + + ServerName %%fqdn%% + + # Mail autoconfig + RewriteRule ^/mail/mailautoconfig.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^/mail/config-v1.1.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^mail/mailautoconfig.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^mail/config-v1.1.xml$ /mailautoconfig_thunderbird.php [L] + RewriteRule ^/autodiscover/autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^/Autodiscover/Autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^/Autodiscover.xml$ mailautoconfig_outlook.php [L] + RewriteRule ^/autodiscover.xml$ mailautoconfig_outlook.php [L] + RewriteRule ^autodiscover/autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^Autodiscover/Autodiscover.xml$ /mailautoconfig_outlook.php [L] + RewriteRule ^Autodiscover.xml$ mailautoconfig_outlook.php [L] + RewriteRule ^autodiscover.xml$ mailautoconfig_outlook.php [L] + + SSLEngine On + SSLCertificateFile %%CRT%% + SSLCertificateKeyFile %%KEY%% + %%CHAINLINE%% + + diff --git a/src/update_domains.php b/src/update_domains.php new file mode 100644 index 00000000..8589e55e --- /dev/null +++ b/src/update_domains.php @@ -0,0 +1,10 @@ +#!/usr/bin/php -q +update_domains(); + From b6a2f743267ca1556baee0a6620da60c4a8419fc Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 15:02:32 +0200 Subject: [PATCH 05/30] [fix] fix misc bugs at alternc.install time --- bureau/class/m_dom.php | 4 +- src/generate_apache_conf.php | 85 ------------------------------------ src/generate_bind_conf.php | 24 ---------- src/rebuild_all_webconf.sh | 2 - 4 files changed, 2 insertions(+), 113 deletions(-) delete mode 100755 src/generate_apache_conf.php delete mode 100755 src/generate_bind_conf.php diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 8cabd458..58755dcc 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1909,7 +1909,7 @@ class m_dom { exit(); } - $dom->lock(); + $this->lock(); // fix in case we forgot to delete SUBDOMAINS before deleting a DOMAIN $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';"); @@ -1982,7 +1982,7 @@ class m_dom { $hooks->invoke("hook_updatedomains_web_post"); } - $dom->unlock(); + $this->unlock(); } diff --git a/src/generate_apache_conf.php b/src/generate_apache_conf.php deleted file mode 100755 index 579d814e..00000000 --- a/src/generate_apache_conf.php +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/php -q -query("select count(*) as c from sub_domaines where web_action != 'OK';"); -if (! $db->next_record()) $nb_todo = 0; -$nb_todo = $db->f('c'); - -// But, we may have forced it -if ( ! in_array('force', $argv) && $nb_todo < 1) { - die('0'); -} - -$todo = $dom->generation_todo(); -$parameters = $dom->generation_parameters(); - -// Generate apache conf -$conf = $dom->generate_apacheconf(); - -if (! $conf) { - die("Error: generate empty configuration\n"); -} - -// Add some headers -$conf2 = "###BEGIN OF ALTERNC AUTO-GENERATED FILE - DO NOT EDIT MANUALLY### -# Generation: ".date('Y-m-d H:i:s'); - -// Do we need to include manual configuration ? -if ( is_dir( ALTERNC_VHOST_MANUALCONF ) ) { - $conf2.="\n## Manual VHOST\nInclude ".ALTERNC_VHOST_MANUALCONF."\n" ; -} else { - $conf2.="\n## Manual VHOST directory missing (".ALTERNC_VHOST_MANUALCONF.")\n" ; -} - -$conf2.="\n$conf\n\n###END OF ALTERNC AUTO-GENERATED FILE - DO NOT EDIT MANUALLY###\n"; - -// Write the conf ! -if (! file_put_contents(ALTERNC_VHOST_FILE, $conf2) ) { - die("Error: writing content\n"); -} - -// Update the database to inform that we did the job -foreach ( $todo as $taction=>$tlist){ - foreach ($tlist as $ttype) { - foreach($ttype as $tid) { - $dom->subdomain_modif_are_done($tid, $taction); - } - } -} - -// Hooks ! -foreach (array('DELETE', 'CREATE', 'UPDATE', 'ENABLE', 'DISABLE') as $y) { - if (!isset($todo[$y]) || empty($todo[$y])) continue; - $dom->generate_conf_oldhook($y, $todo); // old hooks for compatibility - $hooks->invoke("hook_genconf", array($y, $todo[$y], $parameters)); // modern hooks -} - -echo $nb_todo; - diff --git a/src/generate_bind_conf.php b/src/generate_bind_conf.php deleted file mode 100755 index 4907007d..00000000 --- a/src/generate_bind_conf.php +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/php -q -regenerate_conf($force); - diff --git a/src/rebuild_all_webconf.sh b/src/rebuild_all_webconf.sh index cf7fcefa..176b2587 100755 --- a/src/rebuild_all_webconf.sh +++ b/src/rebuild_all_webconf.sh @@ -4,7 +4,6 @@ . /usr/lib/alternc/functions.sh echo "This script will rebuild all web configuration and regenerate DNS." -echo "Only files in $VHOST_MANUALCONF will be preserved." echo "Use --force to skip confirmation" if [ ! "$1" == "--force" ] ; then @@ -20,7 +19,6 @@ mysql_query "update domaines set dns_action = 'UPDATE' WHERE dns_action != ' echo "Now launching update_domains to rebuild." /usr/lib/alternc/update_domains.sh -/usr/lib/alternc/generate_bind_conf.php --force echo "Finish." From f50f45370770bfaa6b9cf765161cedd78a96f2c7 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 15:10:08 +0200 Subject: [PATCH 06/30] [fix] missing global + proper shebang on upgrade script --- bureau/class/m_dom.php | 1 + install/upgrades/3.5.0.2.php | 1 + 2 files changed, 2 insertions(+) diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 58755dcc..12346c17 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1904,6 +1904,7 @@ class m_dom { * so that apache & bind could do their job */ function update_domains() { + global $db, $hooks; if (posix_getuid()!=0) { echo "FATAL: please lauch me as root\n"; exit(); diff --git a/install/upgrades/3.5.0.2.php b/install/upgrades/3.5.0.2.php index 4395db26..22c83c15 100644 --- a/install/upgrades/3.5.0.2.php +++ b/install/upgrades/3.5.0.2.php @@ -1,3 +1,4 @@ +#!/usr/bin/php -q Date: Tue, 17 Jul 2018 15:11:31 +0200 Subject: [PATCH 07/30] [fix] no need for www-data to be in ssl-cert group --- install/alternc.install | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/alternc.install b/install/alternc.install index acec6919..099ec093 100755 --- a/install/alternc.install +++ b/install/alternc.install @@ -354,9 +354,8 @@ do fi done -# ensure dovecot, postfix, apache, can access ssl certificates: +# ensure dovecot, postfix, can access ssl certificates: adduser dovecot ssl-cert -adduser www-data ssl-cert adduser postfix ssl-cert run-parts --arg=certificates /usr/lib/alternc/install.d From 7f94d1f03f7525cb246a4f3763ccea6ba948bf27 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 15:21:55 +0200 Subject: [PATCH 08/30] =?UTF-8?q?[fix]=20dpkg-statoverride:=20erreur:=20un?= =?UTF-8?q?e=20d=C3=A9rogation=20pour=20=C2=AB=C2=A0/var/spool/postfix/var?= =?UTF-8?q?/run/saslauthd=C2=A0=C2=BB=20existe=20d=C3=A9j=C3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/alternc.postinst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/debian/alternc.postinst b/debian/alternc.postinst index 74c973c0..73e436d0 100644 --- a/debian/alternc.postinst +++ b/debian/alternc.postinst @@ -42,8 +42,9 @@ case "$1" in # corriger les permissions du chroot mkdir -p /var/spool/postfix/var/run/saslauthd || true - dpkg-statoverride --quiet --update --add root sasl 710 /var/spool/postfix/var/run/saslauthd || true - + if ! dpkg-statoverride --list /var/spool/postfix/var/run/saslauthd >/dev/null ; then + dpkg-statoverride --quiet --update --add root sasl 710 /var/spool/postfix/var/run/saslauthd || true + fi db_get "alternc/alternc_mail" VMAIL_HOME="$RET" From 36976cbf24a1c2d9aa0f23c37492759b4eee6335 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 15:22:19 +0200 Subject: [PATCH 09/30] [fix] domislocked should be a global variable (...) since we may need it at php shutdown time :/ --- bureau/class/m_dom.php | 58 +++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 12346c17..5354218b 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -56,14 +56,6 @@ class m_dom { */ var $fic_lock_cron = "/run/alternc/cron.lock"; - /** - * Le cron a-t-il รฉtรฉ bloquรฉ ? - * Il faut appeler les fonctions privรฉes lock et unlock entre les - * appels aux domaines. - * @access private - */ - var $islocked = false; - var $type_local = "VHOST"; var $type_url = "URL"; var $type_ip = "IP"; @@ -84,9 +76,10 @@ class m_dom { * Constructeur */ function m_dom() { - global $L_FQDN; + global $L_FQDN, $domislocked; $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')); variable_get('mailname_bounce', $L_FQDN, 'FQDN of the mail server, used to create vhost virtual mail_adress.', array('desc' => 'FQDN', 'type' => 'string')); + $domislocked=false; } @@ -694,11 +687,11 @@ class m_dom { * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. */ function add_domain($domain, $dns, $noerase = false, $force = false, $isslave = false, $slavedom = "") { - global $db, $msg, $quota, $L_FQDN, $tld, $cuid, $hooks; + global $db, $msg, $quota, $L_FQDN, $tld, $cuid, $hooks, $domislocked; $msg->log("dom", "add_domain", $domain); // Locked ? - if (!$this->islocked) { + if (!$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1037,10 +1030,10 @@ class m_dom { * */ function get_domain_all($dom) { - global $db, $msg, $cuid; + global $db, $msg, $cuid, $domislocked; $msg->debug("dom", "get_domain_all", $dom); // Locked ? - if (!$this->islocked) { + if (!$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1098,10 +1091,10 @@ class m_dom { * Retourne FALSE si une erreur s'est produite. */ function get_sub_domain_all($sub_domain_id) { - global $db, $msg, $cuid; + global $db, $msg, $cuid, $domislocked; $msg->debug("dom", "get_sub_domain_all", $sub_domain_id); // Locked ? - if (!$this->islocked) { + if (!$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1263,10 +1256,10 @@ class m_dom { * @return boolean true if the preference has been set */ function set_subdomain_ssl_provider($sub_domain_id,$provider) { - global $db, $msg, $cuid, $ssl; + global $db, $msg, $cuid, $ssl, $domislocked; $msg->log("dom", "set_sub_domain_ssl_provider", $sub_domain_id." / ".$provider); // Locked ? - if (!$this->islocked) { + if (!$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1311,10 +1304,10 @@ class m_dom { * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. */ function set_sub_domain($dom, $sub, $type, $dest, $sub_domain_id = 0, $https) { - global $db, $msg, $cuid, $bro; + global $db, $msg, $cuid, $bro, $domislocked; $msg->log("dom", "set_sub_domain", $dom . "/" . $sub . "/" . $type . "/" . $dest); // Locked ? - if (!$this->islocked) { + if (!$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1396,10 +1389,10 @@ class m_dom { * */ function del_sub_domain($sub_domain_id) { - global $db, $msg; + global $db, $msg, $domislocked; $msg->log("dom", "del_sub_domain", $sub_domain_id); // Locked ? - if (!$this->islocked) { + if (!$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1443,10 +1436,10 @@ class m_dom { * */ function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 3600) { - global $db, $msg, $hooks; + global $db, $msg, $hooksthis; $msg->log("dom", "edit_domain", $dom . "/" . $dns . "/" . $gesmx); // Locked ? - if (!$this->islocked && !$force) { + if (!$domislocked && !$force) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); return false; } @@ -1765,18 +1758,18 @@ class m_dom { * @access private */ function lock() { - global $msg; + global $msg,$domislocked; $msg->debug("dom", "lock"); - if ($this->islocked) { + if ($domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- Lock already obtained!")); } // wait for the file to disappear, or at most 15min: - while (file_exists($this->fic_lock_cron) && filemtime($this->fic_lock_cron)>(time()-900)) { + while (file_exists(m_dom::fic_lock_cron) && filemtime(m_dom::fic_lock_cron)>(time()-900)) { clearstatcache(); sleep(2); } - @touch($this->fic_lock_cron); - $this->islocked = true; + @touch(m_dom::fic_lock_cron); + $domislocked = true; // extra safe : register_shutdown_function(array("m_dom","unlock"),1); return true; @@ -1789,13 +1782,14 @@ class m_dom { * @access private */ function unlock($isshutdown=0) { - global $msg; + global $msg,$domislocked; $msg->debug("dom", "unlock"); - if (!$isshutdown && !$this->islocked) { + if (!$isshutdown && !m_dom::islocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); } - @unlink($this->fic_lock_cron); - $this->islocked = false; + // don't use $this since we may be called by register_shutdown_function out of an object instance. + @unlink(m_dom::fic_lock_cron); + $domislocked = false; return true; } From 74454396542879f50f749e64f0ee6deb6f3ddecb Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 15:50:29 +0200 Subject: [PATCH 10/30] [enh] proper name for autodiscover + fix global in m_dom --- bureau/class/m_dom.php | 4 ++-- install/mysql.sql | 2 +- install/upgrades/3.5.0.1.sql | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 5354218b..77b8bf64 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -54,7 +54,7 @@ class m_dom { * du domaine par update_domains.sh * @access private */ - var $fic_lock_cron = "/run/alternc/cron.lock"; + const fic_lock_cron = "/run/alternc/cron.lock"; var $type_local = "VHOST"; var $type_url = "URL"; @@ -1784,7 +1784,7 @@ class m_dom { function unlock($isshutdown=0) { global $msg,$domislocked; $msg->debug("dom", "unlock"); - if (!$isshutdown && !m_dom::islocked) { + if (!$isshutdown && !$domislocked) { $msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!")); } // don't use $this since we may be called by register_shutdown_function out of an object instance. diff --git a/install/mysql.sql b/install/mysql.sql index 4fd129f0..26373fd3 100644 --- a/install/mysql.sql +++ b/install/mysql.sql @@ -481,7 +481,7 @@ CREATE TABLE IF NOT EXISTS `domaines_type` ( INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES ('dkim', 'DKIM Key', 'NONE', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'), -('autodiscover', 'Autodiscover and autoconf for email', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'), +('autodiscover', 'Email autoconfiguration', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'), ('vhost', 'Locally hosted', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'ALL'), ('url', 'URL redirection', 'URL', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2', false, false, false, 'ALL'), ('ip', 'IPv4 redirect', 'IP', '%SUB% IN A %TARGET%', 'url,ip,ipv6,txt,mx,mx2,defmx,defmx2', true, true, false, 'ALL'), diff --git a/install/upgrades/3.5.0.1.sql b/install/upgrades/3.5.0.1.sql index 0cb4a949..2c31a493 100644 --- a/install/upgrades/3.5.0.1.sql +++ b/install/upgrades/3.5.0.1.sql @@ -4,7 +4,7 @@ INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibil ('dkim', 'DKIM Key', 'NONE', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'); -- migrating AUTODISCOVER / AUTOCONF to be inside sub_domaines table INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES -('autodiscover', 'Autodiscover and autoconf for email', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'); +('autodiscover', 'Email autoconfiguration', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'); -- upgrade from 3.4.10 and 3.4.11 (a bug prevented them to be inserted :/ ) From dbfc59097b67141ade490d938d8ee1ed8bb96c1a Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 16:07:48 +0200 Subject: [PATCH 11/30] [fix] misc bugs in update_domaines.php, --- bureau/admin/dom_edit.php | 1 - bureau/class/db_mysql.php | 20 ++++++++++++++++++++ bureau/class/m_apache.php | 5 ++++- bureau/class/m_bind.php | 6 +++--- bureau/class/m_dom.php | 9 +++++---- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/bureau/admin/dom_edit.php b/bureau/admin/dom_edit.php index 88d4c629..6986eb6b 100755 --- a/bureau/admin/dom_edit.php +++ b/bureau/admin/dom_edit.php @@ -197,7 +197,6 @@ if (!$r["sub"][$i]["only_dns"]) { __("HTTP and HTTPS"); break; default: - __("Unknown"); break; } } diff --git a/bureau/class/db_mysql.php b/bureau/class/db_mysql.php index 8e5a3c36..9ec79968 100644 --- a/bureau/class/db_mysql.php +++ b/bureau/class/db_mysql.php @@ -242,6 +242,26 @@ class DB_Sql { return $data; } + /* pdo equivalent of fetch() */ + function fetch($mode=PDO::FETCH_ASSOC) { + if (!$this->pdo_query) { + $this->halt("next_record called with no query pending."); + return FALSE; + } + + $data = $this->pdo_query->fetch($mode); + $this->Errno = $this->pdo_query->errorCode(); + $this->Error = $this->pdo_query->errorInfo(); + + if ($data == FALSE) { + if ($this->Auto_Free) + $this->free(); + return FALSE; + } + + return $data; + } + /** * table locking */ diff --git a/bureau/class/m_apache.php b/bureau/class/m_apache.php index e497e8e1..1c26bd4d 100644 --- a/bureau/class/m_apache.php +++ b/bureau/class/m_apache.php @@ -105,7 +105,10 @@ class m_apache { /** * launched for each FQDN for which we want to delete a vhost template */ - function hook_updatedomains_web_del($subdom) { + function hook_updatedomains_web_del($subdomid) { + $db->query("SELECT sd.*, dt.only_dns, dt.has_https_option, m.login FROM domaines_type dt, sub_domaines sd LEFT JOIN membres m ON m.uid=sd.compte WHERE dt.name=sd.type AND sd.web_action!='OK' AND id=?;",array($subdomid)); + $db->next_record(); + $subdom=$db->Record; $confdir = $this->vhostroot."/".substr($subdom["compte"],-1)."/".$subdom["compte"]; @unlink($confdir."/".$subdom["fqdn"].".conf"); $this->shouldreload=true; diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index e85b42ad..5610e236 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -128,7 +128,7 @@ class m_bind { */ function hook_updatedomains_dns_del($dominfo) { $domain = $dominfo["domaine"]; - if (remove_line_from_file( + if (del_line_from_file( $this->NAMED_CONF, trim(strtr( file_get_contents($this->NAMED_TEMPLATE), @@ -157,7 +157,7 @@ class m_bind { global $msg; if ($this->shouldreload) { $ret=0; - exec($this->rndc." reload 2>&1",$out,$ret); + exec($this->RNDC." reload 2>&1",$out,$ret); if ($ret!=0) { $msg->raise("ERROR","bind","Error while reloading bind, error code is $ret\n".implode("\n",$out)); } else { @@ -166,7 +166,7 @@ class m_bind { } if ($this->shouldreconfig) { $ret=0; - exec($this->rndc." reload 2>&1",$out,$ret); + exec($this->RNDC." reload 2>&1",$out,$ret); if ($ret!=0) { $msg->raise("ERROR","bind","Error while reconfiguring bind, error code is $ret\n".implode("\n",$out)); } else { diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 77b8bf64..029fe1c7 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1065,13 +1065,14 @@ class m_dom { $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)); // Pas de webmail, on le cochera si on le trouve. $r["sub"] = array(); - $data = $db->fetchAll(); - foreach($data as $i=>$record) { + $i=0; + while ($record=$db->fetch()) { $r["sub"][$i] = $record; // FIXME : replace sub by name and dest by valeur in the code that exploits this function : $r["sub"][$i]["name"] = $record["sub"]; $r["sub"][$i]["dest"] = $record["valeur"]; $r["sub"][$i]["fqdn"] = ((!empty($r["sub"][$i]["name"])) ? $r["sub"][$i]["name"] . "." : "") . $r["name"]; + $i++; } $db->free(); return $r; @@ -1920,9 +1921,9 @@ class m_dom { $hooks->invoke("hook_updatedomains_dns_pre"); foreach($alldoms as $id=>$onedom) { if ($onedom["gesdns"]==0 || $onedom["dns_action"]=="DELETE") { - $ret = $hooks->invoke("hook_updatedomains_dns_del",array(array($onedom))); + $ret = $hooks->invoke("hook_updatedomains_dns_del",array($onedom)); } else { - $ret = $hooks->invoke("hook_updatedomains_dns_add",array(array($onedom))); + $ret = $hooks->invoke("hook_updatedomains_dns_add",array($onedom)); } if ($onedom["dns_action"]=="DELETE") { From cdf07913da5da799f05c017f690b9eaebe796da7 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 16:59:54 +0200 Subject: [PATCH 12/30] [fix] misc bugs in update_domaines.php, --- bureau/admin/html-head.php | 4 ++-- bureau/class/m_apache.php | 6 +++--- bureau/class/m_bind.php | 4 ++-- bureau/class/m_dom.php | 25 +++++++++++++------------ bureau/class/m_mail.php | 16 ++++++++-------- bureau/class/m_ssl.php | 2 +- 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/bureau/admin/html-head.php b/bureau/admin/html-head.php index 18e9b00f..635ef878 100644 --- a/bureau/admin/html-head.php +++ b/bureau/admin/html-head.php @@ -39,7 +39,7 @@ if (!defined("ALTERNC_PANEL")) exit(); // must be included ;) if (file_exists("styles/style-custom.css") ) { echo ''; } -if (count($addhead['css'])) { +if (isset($addhead) && count($addhead['css'])) { foreach($addhead['css'] as $css) echo $css."\n"; } $favicon = variable_get('favicon', 'favicon.ico' ,'You can specify a favicon, for example /images/my_logo.ico', array('desc'=>'URL','type'=>'string')); @@ -53,7 +53,7 @@ $favicon = variable_get('favicon', 'favicon.ico' ,'You can specify a favicon, fo diff --git a/bureau/class/m_apache.php b/bureau/class/m_apache.php index 1c26bd4d..bf69ab54 100644 --- a/bureau/class/m_apache.php +++ b/bureau/class/m_apache.php @@ -44,7 +44,7 @@ class m_apache { // launched for each FQDN for which we want a new vhost template function hook_updatedomains_web_add($subdomid) { - global $msg,$db; + global $msg,$db,$ssl,$L_FQDN; $db->query("SELECT sd.*, dt.only_dns, dt.has_https_option, m.login FROM domaines_type dt, sub_domaines sd LEFT JOIN membres m ON m.uid=sd.compte WHERE dt.name=sd.type AND sd.web_action!='OK' AND id=?;",array($subdomid)); $db->next_record(); @@ -85,7 +85,7 @@ class m_apache { "%%redirect%%" => $subdom['valeur'], "%%UID%%" => $subdom['compte'], "%%GID%%" => $subdom['compte'], - "%%mail_account%%" => $subdom['mail'], + "%%mail_account%%" => $subdom['login']."@".$L_FQDN, "%%user%%" => "FIXME", "%%CRT%%" => $cert["cert"], "%%KEY%%" => $cert["key"], @@ -94,7 +94,7 @@ class m_apache { // and write the template $confdir = $this->vhostroot."/".substr($subdom["compte"],-1)."/".$subdom["compte"]; @mkdir($confdir,0755,true); - file_put_contents($confdir."/".$subdom["fqdn"].".conf"); + file_put_contents($confdir."/".$subdom["fqdn"].".conf",$tpl); $this->shouldreload=true; return 0; // shell meaning => OK ;) diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index 5610e236..5b328703 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -107,7 +107,7 @@ class m_bind { trim(strtr( file_get_contents($this->NAMED_TEMPLATE), array( - "@@DOMAIN@@" => $DOMAIN, + "@@DOMAIN@@" => $domain, "@@ZONE_FILE@@" => $this->zone_file_directory."/".$domain ) ))) @@ -225,7 +225,7 @@ class m_bind { sd.type=dt.name AND sd.enable IN ('ENABLE', 'ENABLED') ORDER BY ENTRY ;"); - $t=array(); + $t=""; while ($db->next_record()) { $t.= $db->f('entry')."\n"; } diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 029fe1c7..2261bf6e 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -635,35 +635,35 @@ class m_dom { * @param string $dom nom de domaine รฉ effacer * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. */ - function del_domain($dom) { + function del_domain($domain) { global $db, $msg, $hooks; - $msg->log("dom", "del_domain", $dom); - $dom = strtolower($dom); + $msg->log("dom", "del_domain", $domain); + $domain = strtolower($domain); $this->lock(); - if (!$r = $this->get_domain_all($dom)) { + if (!$r = $this->get_domain_all($domain)) { return false; } $this->unlock(); // Call Hooks to delete the domain and the MX management: // TODO : the 2 calls below are using an OLD hook call, FIXME: remove them when unused - $hooks->invoke("alternc_del_domain", array($dom)); - $hooks->invoke("alternc_del_mx_domain", array($dom)); + $hooks->invoke("alternc_del_domain", array($domain)); + $hooks->invoke("alternc_del_mx_domain", array($domain)); // New hook calls: $hooks->invoke("hook_dom_del_domain", array($r["id"])); $hooks->invoke("hook_dom_del_mx_domain", array($r["id"])); // Now mark the domain for deletion: - $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ?;", array($dom)); - $this->set_dns_action($dom, 'DELETE'); + $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ?;", array($domain)); + $this->set_dns_action($domain, 'DELETE'); return true; } - function domshort($dom, $sub = "") { - return str_replace("-", "", str_replace(".", "", empty($sub) ? "" : "$sub.") . $dom); + function domshort($domain, $sub = "") { + return str_replace("-", "", str_replace(".", "", empty($sub) ? "" : "$sub.") . $domain); } @@ -1302,9 +1302,10 @@ class m_dom { * de $type (url, ip, dossier...) * @param string $https the HTTPS behavior : HTTP(redirect https to http), * HTTPS(redirect http to https) or BOTH (both hosted at the same place) + * or nothing "" when not applicable for this domain type. * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. */ - function set_sub_domain($dom, $sub, $type, $dest, $sub_domain_id = 0, $https) { + function set_sub_domain($dom, $sub, $type, $dest, $sub_domain_id = 0, $https="") { global $db, $msg, $cuid, $bro, $domislocked; $msg->log("dom", "set_sub_domain", $dom . "/" . $sub . "/" . $type . "/" . $dest); // Locked ? @@ -1931,7 +1932,7 @@ class m_dom { } else { // we keep the highest result returned by hooks... rsort($ret,SORT_NUMERIC); $returncode=$ret[0]; - $db->query("UPDATE domaines SET dns_result=?, dns_action='OK' WHERE domaine=?;",array($returncode,$onedom)); + $db->query("UPDATE domaines SET dns_result=?, dns_action='OK' WHERE domaine=?;",array($returncode,$onedom["domaine"])); } } $hooks->invoke("hook_updatedomains_dns_post"); diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index 9a370fe7..06b158db 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -1099,21 +1099,21 @@ ORDER BY global $db; // for each domain where we don't have the MX or the DNS, remove the DKIM setup $this->shouldreloaddkim=false; - $db->query("SELECT domaine,gesdns,gesmx FROM domaines WHERE dns_action!='OK';"); + $db->query("SELECT domaine,compte,gesdns,gesmx FROM domaines WHERE dns_action!='OK';"); $add=array(); $del=array(); while ($db->next_record()) { if ($db->Record["gesdns"]==0 || $db->Record["gesmx"]==0) { - $del[]=$db->Record["domaine"]; + $del[]=$db->Record; } else { - $add[]=$db->Record["domaine"]; + $add[]=$db->Record; } } foreach($add as $domain) { - $this->dkim_add($domain); + $this->dkim_add($domain["domaine"],$domain["compte"]); } foreach($del as $domain) { - $this->dkim_del($domain); + $this->dkim_del($domain["domaine"],$domain["compte"]); } } @@ -1134,7 +1134,7 @@ ORDER BY /** * Add a domain into OpenDKIM configuration */ - function dkim_add($domain) { + function dkim_add($domain,$uid) { global $db; $target_dir = "/etc/opendkim/keys/$domain"; if (file_exists($target_dir.'/alternc.txt')) return; // Do not generate if exist @@ -1154,7 +1154,7 @@ ORDER BY add_line_to_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); // Add subdomaine entry $dkim_key=$this->dkim_get_entry($domain); - $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='', type='dkim', valeur=?;",array($uid,$domain,$dkim_key)); + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='', type='dkim', valeur=?;",array($domain,$uid,$dkim_key)); // no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain } @@ -1163,7 +1163,7 @@ ORDER BY /** * Delete a domain from OpenDKIM configuration */ - function dkim_del($domain) { + function dkim_del($domain,$uid) { $target_dir = "/etc/opendkim/keys/$domain"; if (file_exists($target_dir)) { $this->shouldreloaddkim=true; diff --git a/bureau/class/m_ssl.php b/bureau/class/m_ssl.php index 69b0a6f0..de486b5b 100644 --- a/bureau/class/m_ssl.php +++ b/bureau/class/m_ssl.php @@ -667,7 +667,7 @@ SELECT ?,?,?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?, ?, sslcsr FROM certificate } $subdom["fqdn"]=$subdom["sub"].(($subdom["sub"])?".":"").$subdom["domaine"]; - list($cert) = $this->get_valid_certs($fqdn, $subdom["provider"]); + list($cert) = $this->get_valid_certs($subdom["fqdn"], $subdom["provider"]); // Edit certif_hosts: $db->query("UPDATE sub_domaines SET certificate_id=? WHERE id=?;",array($cert["id"], $subdom["id"])); } From 1e4f2783d8c8181c687ba8832c02205597f19f0b Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 17:03:28 +0200 Subject: [PATCH 13/30] [fix] bug that make it impossible to launch upgrades properly : alternc_status is overwriten at postinst :/ --- install/mysql.sql | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/install/mysql.sql b/install/mysql.sql index 26373fd3..556bd341 100644 --- a/install/mysql.sql +++ b/install/mysql.sql @@ -795,7 +795,6 @@ CREATE TABLE IF NOT EXISTS `certificates` ( --- make it re-exec-proof -DELETE FROM alternc_status WHERE name='alternc_version'; -INSERT INTO alternc_status SET name='alternc_version',value='3.5.0.1.sql'; +-- make it re-exec-proof -- BUT don't overwrite existing value ! +INSERT IGNORE INTO alternc_status SET name='alternc_version',value='3.5.0.1.sql'; From e705f377b937774d76fe6d564461d0f61b8e171c Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 17:11:15 +0200 Subject: [PATCH 14/30] [fix] more bugs in update_domaines.php => not renaming vhosts_all.conf.new to vhosts_all.conf.new + ignoring deletion of DNS_ONLY sub_domaines entries --- bureau/class/m_apache.php | 1 + bureau/class/m_dom.php | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bureau/class/m_apache.php b/bureau/class/m_apache.php index bf69ab54..87767e65 100644 --- a/bureau/class/m_apache.php +++ b/bureau/class/m_apache.php @@ -160,6 +160,7 @@ class m_apache { } closedir($d); fclose($f); + rename($this->vhostroot."/vhosts_all.conf.new", $this->vhostroot."/vhosts_all.conf"); } private function subconcat($f,$root) { diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 2261bf6e..484a16e4 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1943,14 +1943,22 @@ class m_dom { $db->query("SELECT sd.*, dt.only_dns FROM domaines_type dt, sub_domaines sd WHERE dt.name=sd.type AND sd.web_action!='OK';"); $alldoms=array(); $ignore=array(); + $delete=array(); while ($db->next_record()) { // only_dns=1 => weird, we should not have web_action SET to something else than OK ... anyway, skip it if ($db->Record["only_dns"]) { - $ignore[]=$db->Record["id"]; + if ($db->Record["web_action"]=="DELETE") { + $delete[]=$db->Record["id"]; + } else { + $ignore[]=$db->Record["id"]; + } } else { $alldoms[$db->Record["id"]]=$db->Record; } } + foreach($delete as $id) { + $db->query("DELETE FROM sub_domaines WHERE id=?;",array($id)); + } foreach($ignore as $id) { // @FIXME (unsure it's useful) maybe we could check that no file exist for this subdomain ? $db->query("UPDATE sub_domaines SET web_action='OK' WHERE id=?;",array($id)); From 5c32a54edb2b682d81a28ffb8ded8b3a61d7d685 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 17:20:39 +0200 Subject: [PATCH 15/30] [fix] French vs. English in template name :/ + fixing case issue making zone empty --- bureau/class/m_bind.php | 2 +- etc/alternc/templates/bind/templates/named.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index 5b328703..12369ef9 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -227,7 +227,7 @@ class m_bind { ORDER BY ENTRY ;"); $t=""; while ($db->next_record()) { - $t.= $db->f('entry')."\n"; + $t.= $db->f('ENTRY')."\n"; } return $t; } diff --git a/etc/alternc/templates/bind/templates/named.template b/etc/alternc/templates/bind/templates/named.template index 9c7f7cff..13ed7fb7 100644 --- a/etc/alternc/templates/bind/templates/named.template +++ b/etc/alternc/templates/bind/templates/named.template @@ -1 +1 @@ -zone "@@DOMAINE@@" { type master; file "@@ZONE_FILE@@"; allow-query { any; }; }; +zone "@@DOMAIN@@" { type master; file "@@ZONE_FILE@@"; allow-query { any; }; }; From 3bc65aed3f62985b68b92975b1114d76cccc42f3 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 17:28:14 +0200 Subject: [PATCH 16/30] [fix] substitute variables AFTER mysql queries in bind zone file --- bureau/class/m_bind.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index 12369ef9..b8b88d9a 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -72,6 +72,10 @@ class m_bind { // Prepare a new zonefile from a template $zone = file_get_contents($this->ZONE_TEMPLATE); + + // add the SUBDOMAIN entries + $zone .= $this->conf_from_db($domain); + // substitute ALTERNC & domain variables $zone = strtr($zone, array( "%%fqdn%%" => "$L_FQDN", @@ -91,9 +95,6 @@ class m_bind { "@@ZONETTL@@" => $ttl, )); - // add the SUBDOMAIN entries - $zone .= $this->conf_from_db($domain); - // add the "END ALTERNC CONF line"; $zone .= ";;; END ALTERNC AUTOGENERATE CONFIGURATION\n"; From e3d2b844189b95da695a5d759d4827d1b7de2879 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 17:39:14 +0200 Subject: [PATCH 17/30] [fix] reading multiline dkim key + fixing autodiscover zone having NULL name --- bureau/class/m_bind.php | 2 +- bureau/class/m_mail.php | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index b8b88d9a..7cb493b8 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -223,7 +223,7 @@ class m_bind { sub_domaines sd, domaines_type dt WHERE - sd.type=dt.name + sd.type=dt.name AND sd.enable IN ('ENABLE', 'ENABLED') ORDER BY ENTRY ;"); $t=""; diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index 06b158db..e8496866 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -1007,12 +1007,12 @@ ORDER BY $db->query("SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autodiscover' AND type='autodiscover';",array($domain)); if (!$db->next_record()) { - $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autodiscover', type='autodiscover';",array($domain,$uid)); + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autodiscover', type='autodiscover', valeur='';",array($domain,$uid)); $changed=true; } $db->query("SELECT domaine,sub,type,valeur FROM sub_domaines WHERE domaine=? AND sub='autoconfig' AND type='autodiscover';",array($domain)); if (!$db->next_record()) { - $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autoconfig', type='autodiscover';",array($domain,$uid)); + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='autoconfig', type='autodiscover', valeur='';",array($domain,$uid)); $changed=true; } if ($changed) { @@ -1184,10 +1184,30 @@ ORDER BY * or false if an error occurred **/ function dkim_get_entry($domain) { + global $msg; $key=file_get_contents("/etc/opendkim/keys/".$domain."/alternc.txt"); + // easy: monoline key if (preg_match('#alternc._domainkey IN TXT "(.*)"#',$key,$mat)) { return $mat[1]; + } else { + // Need to parse a multiligne key: + $inkey=false; $result=""; + $lines=explode("\n",$key); + foreach($lines as $line) { + if (preg_match('#alternc._domainkey IN TXT \( "(.*)"#',$key,$mat)) { + $result.=$mat[1]; $inkey=true; continue; + } + if ($inkey && preg_match('# "(.*)" \)#',$key,$mat)) { + $result.=$mat[1]; $inkey=false; break; + } + if ($inkey && preg_match('# "(.*)" #',$key,$mat)) { + $result.=$mat[1]; $inkey=true; continue; + } + } + if ($result) + return $result; } + $msg->debug("mail","dkim_get_entry($domain) failed"); return false; } From 291812ceade845817d379ad3ae32724ab0faddb6 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 17:48:15 +0200 Subject: [PATCH 18/30] fixing dkim get entry function --- bureau/class/m_mail.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index e8496866..15ed9fbb 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -1154,7 +1154,7 @@ ORDER BY add_line_to_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); // Add subdomaine entry $dkim_key=$this->dkim_get_entry($domain); - $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='', type='dkim', valeur=?;",array($domain,$uid,$dkim_key)); + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='alternc._domainkey', type='dkim', valeur=?;",array($domain,$uid,$dkim_key)); // no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain } @@ -1194,13 +1194,13 @@ ORDER BY $inkey=false; $result=""; $lines=explode("\n",$key); foreach($lines as $line) { - if (preg_match('#alternc._domainkey IN TXT \( "(.*)"#',$key,$mat)) { + if (preg_match('#alternc._domainkey\s+IN\s+TXT\s+\( "(.*)"#',$line,$mat)) { $result.=$mat[1]; $inkey=true; continue; } - if ($inkey && preg_match('# "(.*)" \)#',$key,$mat)) { + if ($inkey && preg_match('#^\s*"(.*)"\s*\)#',$line,$mat)) { $result.=$mat[1]; $inkey=false; break; } - if ($inkey && preg_match('# "(.*)" #',$key,$mat)) { + if ($inkey && preg_match('#^\s*"(.*)"\s*$#',$line,$mat)) { $result.=$mat[1]; $inkey=true; continue; } } From 49e81b55bf20e9e956c3dd776bb09f69e22905d5 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:10:39 +0200 Subject: [PATCH 19/30] [fix] add ::1 to slaveip.conf template ... it's 2018 people ! --- etc/alternc/templates/bind/slaveip.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/alternc/templates/bind/slaveip.conf b/etc/alternc/templates/bind/slaveip.conf index 7212d291..2b8aa971 100644 --- a/etc/alternc/templates/bind/slaveip.conf +++ b/etc/alternc/templates/bind/slaveip.conf @@ -5,6 +5,7 @@ acl "allslaves" { { 127.0.0.1; + ::1; //AUTO-SLAVES// }; }; From 1182cada25513ddd54e266a5ee3742b75a111f77 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:16:56 +0200 Subject: [PATCH 20/30] [fix] now write ssl cert as ROOT, not as alterncpanel --- bureau/class/m_ssl.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bureau/class/m_ssl.php b/bureau/class/m_ssl.php index de486b5b..d51de2c3 100644 --- a/bureau/class/m_ssl.php +++ b/bureau/class/m_ssl.php @@ -519,12 +519,6 @@ INSTR(CONCAT(sd.sub,IF(sd.sub!='','.',''),sd.domaine),'.')+1))=? $msg->raise("ERROR","ssl", _("Can't save the Key/Crt/Chain now. Please try later.")); return false; } - $this->write_cert_file(array( - "id"=>$id, - "sslcrt"=>$crt, - "sslkey"=>$key, - "sslchain"=>$chain - )); return $id; } @@ -668,6 +662,7 @@ SELECT ?,?,?, FROM_UNIXTIME(?), FROM_UNIXTIME(?), ?, ?, sslcsr FROM certificate $subdom["fqdn"]=$subdom["sub"].(($subdom["sub"])?".":"").$subdom["domaine"]; list($cert) = $this->get_valid_certs($subdom["fqdn"], $subdom["provider"]); + $this->write_cert_file($cert); // Edit certif_hosts: $db->query("UPDATE sub_domaines SET certificate_id=? WHERE id=?;",array($cert["id"], $subdom["id"])); } From 5aeeac1c3100595ea72415ca9a70b669b7a3a29e Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:20:06 +0200 Subject: [PATCH 21/30] [fix] cancel button not canceling :/ --- bureau/admin/dom_edit.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bureau/admin/dom_edit.inc.php b/bureau/admin/dom_edit.inc.php index a4a63617..ca64bbdb 100755 --- a/bureau/admin/dom_edit.inc.php +++ b/bureau/admin/dom_edit.inc.php @@ -147,7 +147,7 @@ $dom->unlock(); } ?> - + From e06e2b3abd0efa25dfc2d5d41efad5914711491f Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:21:25 +0200 Subject: [PATCH 22/30] dkim domaine type is taking TXT as paramter --- install/mysql.sql | 2 +- install/upgrades/3.5.0.1.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/mysql.sql b/install/mysql.sql index 556bd341..89faba08 100644 --- a/install/mysql.sql +++ b/install/mysql.sql @@ -480,7 +480,7 @@ CREATE TABLE IF NOT EXISTS `domaines_type` ( ) ENGINE=InnoDB COMMENT = 'Type of domains allowed'; INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES -('dkim', 'DKIM Key', 'NONE', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'), +('dkim', 'DKIM Key', 'TXT', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'), ('autodiscover', 'Email autoconfiguration', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'), ('vhost', 'Locally hosted', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'ALL'), ('url', 'URL redirection', 'URL', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2', false, false, false, 'ALL'), diff --git a/install/upgrades/3.5.0.1.sql b/install/upgrades/3.5.0.1.sql index 2c31a493..f46a1ed4 100644 --- a/install/upgrades/3.5.0.1.sql +++ b/install/upgrades/3.5.0.1.sql @@ -1,7 +1,7 @@ -- migrating DKIM to be inside sub_domaines table INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES -('dkim', 'DKIM Key', 'NONE', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'); +('dkim', 'DKIM Key', 'TXT', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN'); -- migrating AUTODISCOVER / AUTOCONF to be inside sub_domaines table INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable) VALUES ('autodiscover', 'Email autoconfiguration', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN'); From 230d3ffdd87a4b2d2ce48db64277d83c52fd220d Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:25:14 +0200 Subject: [PATCH 23/30] [fix] missing global in apache conf --- bureau/class/m_apache.php | 1 + bureau/class/m_ssl.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bureau/class/m_apache.php b/bureau/class/m_apache.php index 87767e65..98f4f17f 100644 --- a/bureau/class/m_apache.php +++ b/bureau/class/m_apache.php @@ -106,6 +106,7 @@ class m_apache { * launched for each FQDN for which we want to delete a vhost template */ function hook_updatedomains_web_del($subdomid) { + global $db; $db->query("SELECT sd.*, dt.only_dns, dt.has_https_option, m.login FROM domaines_type dt, sub_domaines sd LEFT JOIN membres m ON m.uid=sd.compte WHERE dt.name=sd.type AND sd.web_action!='OK' AND id=?;",array($subdomid)); $db->next_record(); $subdom=$db->Record; diff --git a/bureau/class/m_ssl.php b/bureau/class/m_ssl.php index d51de2c3..73229021 100644 --- a/bureau/class/m_ssl.php +++ b/bureau/class/m_ssl.php @@ -394,7 +394,7 @@ INSTR(CONCAT(sd.sub,IF(sd.sub!='','.',''),sd.domaine),'.')+1))=? $chain=false; return array( - "cert" => self::KEY_REPOSITORY."/".floor($id/1000)."/".$id.".cert", + "cert" => self::KEY_REPOSITORY."/".floor($id/1000)."/".$id.".pem", "key" => self::KEY_REPOSITORY."/".floor($id/1000)."/".$id.".key", "chain" => $chain ); From e09006b69e1f86b09d00bc947dfd57e774a48a99 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:29:57 +0200 Subject: [PATCH 24/30] [fix] fix bad http/https/both switcher --- bureau/admin/dom_edit.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bureau/admin/dom_edit.inc.php b/bureau/admin/dom_edit.inc.php index ca64bbdb..9396c38b 100755 --- a/bureau/admin/dom_edit.inc.php +++ b/bureau/admin/dom_edit.inc.php @@ -130,8 +130,8 @@ $dom->unlock(); From 0b0f08c739e0dffd6b82dbdfab823d3dbe989dd6 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:47:49 +0200 Subject: [PATCH 25/30] removing unused code from m_dom, adding translation request for new domaines types --- bureau/class/m_dom.php | 205 +---------------------- phpunit/tests/bureau/class/m_domTest.php | 95 ----------- 2 files changed, 3 insertions(+), 297 deletions(-) diff --git a/bureau/class/m_dom.php b/bureau/class/m_dom.php index 484a16e4..ead61a90 100644 --- a/bureau/class/m_dom.php +++ b/bureau/class/m_dom.php @@ -1991,200 +1991,6 @@ class m_dom { } - /** - * Return an array with all the needed parameters to generate conf - * of a vhost. - * If no parameters, return the parameters for ALL the vhost. - * Optionnal parameters: id of the sub_domaines - * */ - function generation_parameters($id = null, $only_apache = true) { - global $db, $msg; - $msg->log("dom", "generation_parameters"); - $params = ""; - /** 2016_05_18 : this comments was here before escaping the request... is there still something to do here ? - * // BUG BUG BUG FIXME - * // Suppression de comptes -> membres existe pas -> domaines a supprimer ne sont pas lister - */ - $query = " - select - sd.id as sub_id, - lower(sd.type) as type, - m.login, - m.uid as uid, - if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine) as fqdn, - concat_ws('@',m.login,v.value) as mail, - sd.valeur - from - sub_domaines sd left join membres m on sd.compte=m.uid, - variable v, - domaines_type dt - where - v.name='mailname_bounce' - and lower(dt.name) = lower(sd.type)"; - $query_args = array(); - - if (!is_null($id) && intval($id) == $id) { - $query .= " AND sd.id = ? "; - array_push($query_args, intval($id)); - } - if ($only_apache) { - $query .=" and dt.only_dns is false "; - } - - $query .= " - order by - m.login, - sd.domaine, - sd.sub;"; - - - $db->query($query, $query_args); - - $r = array(); - while ($db->next_record()) { - $r[$db->Record['sub_id']] = $db->Record; - } - return $r; - } - - - /** - * Return an array with all informations of the domains_type - * used to generate Apache conf. - * Die if templates missing. - * Warning: an Apache domains_type must have 'only_dns' == TRUE - * - * */ - function generation_domains_type() { - global $dom; - $d = array(); - foreach ($dom->domains_type_lst() as $k => $v) { - if ($v['only_dns'] == true) { - continue; - } - if (!$j = file_get_contents(ALTERNC_APACHE2_GEN_TMPL_DIR . '/' . strtolower($k) . '.conf')) { - die("Error: missing file for $k"); - } - $d[$k] = $v; - $d[$k]['tpl'] = $j; - } - return $d; - } - - - /** - * Launch old fashionned hooks as there was in AlternC 1.0 - * @TODO: do we still need that? - */ - function generate_conf_oldhook($action, $lst_sub, $sub_obj = null) { - if (is_null($sub_obj)) { - $sub_obj = $this->generation_parameters(null, false); - } - if (!isset($lst_sub[strtoupper($action)]) || empty($lst_sub[strtoupper($action)])) { - return false; - } - - $lst_by_type = $lst_sub[strtoupper($action)]; - - foreach ($lst_by_type as $type => $lid_arr) { - $script = "/etc/alternc/functions_hosting/hosting_" . strtolower($type) . ".sh"; - if (!@is_executable($script)) { - continue; - } - foreach ($lid_arr as $lid) { - $o = $sub_obj[$lid]; - $cmd = $script . " " . escapeshellcmd(strtolower($action)) . " "; - $cmd .= escapeshellcmd($o['fqdn']) . " " . escapeshellcmd($o['valeur']); - - system($cmd); - } - } // foreach $lst_by_type - } - - - /** - * Generate apache configuration. - * Die if a specific FQDN have 2 vhost conf. - * - * */ - function generate_apacheconf($p = null) { - // Get the parameters - $lst = $this->generation_parameters($p); - - $gdt = $this->generation_domains_type(); - - // Initialize duplicate check - $check_dup = array(); - - $ret = ''; - foreach ($lst as $p) { - // Check if duplicate - if (in_array($p['fqdn'], $check_dup)) { - die("Error: duplicate fqdn : " . $p['fqdn']); - } else { - $check_dup[] = $p['fqdn']; - } - - // Get the needed template - $tpl = $gdt[$p['type']] ['tpl']; - - // Replace needed vars - $tpl = strtr($tpl, array( - "%%LOGIN%%" => $p['login'], - "%%fqdn%%" => $p['fqdn'], - "%%document_root%%" => getuserpath($p['login']) . $p['valeur'], - "%%account_root%%" => getuserpath($p['login']), - "%%redirect%%" => $p['valeur'], - "%%UID%%" => $p['uid'], - "%%GID%%" => $p['uid'], - "%%mail_account%%" => $p['mail'], - "%%user%%" => "FIXME", - )); - - // Security check - if ($p['uid'] < 1999) { // if UID is not an AlternC uid - $ret.= "# ERROR: Sub_id: " . $p['sub_id'] . "- The uid seem to be dangerous\n"; - continue; - } - - // Return the conf - $ret.= "# Sub_id: " . $p['sub_id'] . "\n" . $tpl; - } - - return $ret; - } - - - /** - * Return an array with the list of id of sub_domains waiting for an action - */ - function generation_todo() { - global $db, $msg; - $msg->debug("dom", "generation_todo"); - $db->query("select id as sub_id, web_action, type from sub_domaines where web_action !='ok';"); - $r = array(); - while ($db->next_record()) { - $r[strtoupper($db->Record['web_action'])][strtoupper($db->Record['type'])][] = $db->f('sub_id'); - } - return $r; - } - - - function subdomain_modif_are_done($sub_domain_id, $action) { - global $db; - $sub_domain_id = intval($sub_domain_id); - switch (strtolower($action)) { - case "delete": - $sql = "DELETE FROM sub_domaines WHERE id =$sub_domain_id;"; - break; - default: - $sql = "UPDATE sub_domaines SET web_action='OK' WHERE id='$sub_domain_id'; "; - } - $db->query($sql); - return true; - } - - /** * @param string $dns_action */ @@ -2195,15 +2001,8 @@ class m_dom { } - function set_dns_result($domain, $dns_result) { - global $db; - $db->query("UPDATE domaines SET dns_result= ? WHERE domaine= ?; ", array($dns_result, $domain)); - return true; - } - - /** - * List if there is problems in the domains. + * List if there are problems on the domain. * Problems can appear when editing domains type properties */ function get_problems($domain) { @@ -2260,6 +2059,8 @@ class m_dom { _("Default mail server"); _("Default backup mail server"); _("AlternC panel access"); + _("DKIM Key"); + _("Email autoconfiguration"); } } /* Class m_domains */ diff --git a/phpunit/tests/bureau/class/m_domTest.php b/phpunit/tests/bureau/class/m_domTest.php index 12892330..343cceea 100644 --- a/phpunit/tests/bureau/class/m_domTest.php +++ b/phpunit/tests/bureau/class/m_domTest.php @@ -690,101 +690,6 @@ class m_domTest extends TestCase ); } - /** - * @covers m_dom::generation_parameters - * @todo Implement testGeneration_parameters(). - */ - public function testGeneration_parameters() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::generation_domains_type - * @todo Implement testGeneration_domains_type(). - */ - public function testGeneration_domains_type() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::generate_conf_oldhook - * @todo Implement testGenerate_conf_oldhook(). - */ - public function testGenerate_conf_oldhook() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::generate_apacheconf - * @todo Implement testGenerate_apacheconf(). - */ - public function testGenerate_apacheconf() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::generation_todo - * @todo Implement testGeneration_todo(). - */ - public function testGeneration_todo() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::subdomain_modif_are_done - * @todo Implement testSubdomain_modif_are_done(). - */ - public function testSubdomain_modif_are_done() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::set_dns_action - * @todo Implement testSet_dns_action(). - */ - public function testSet_dns_action() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @covers m_dom::set_dns_result - * @todo Implement testSet_dns_result(). - */ - public function testSet_dns_result() - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } /** * @covers m_dom::get_problems From 1716d14b235624871213d6620deb7c07621c1cea Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 17 Jul 2018 18:50:18 +0200 Subject: [PATCH 26/30] [cleanup] removing all unused files from former update_domains.sh --- src/functions_dns.sh | 223 -------------------------------------- src/functions_hosting.sh | 225 --------------------------------------- src/update_domains.sh | 170 ----------------------------- 3 files changed, 618 deletions(-) delete mode 100755 src/functions_dns.sh delete mode 100644 src/functions_hosting.sh diff --git a/src/functions_dns.sh b/src/functions_dns.sh deleted file mode 100755 index 7ec2f602..00000000 --- a/src/functions_dns.sh +++ /dev/null @@ -1,223 +0,0 @@ -#!/bin/bash -# dns.sh next-gen by Fufroma - -# Init some vars -. /etc/alternc/local.sh -. /usr/lib/alternc/functions.sh - -# Init some other vars -ZONE_TEMPLATE="/etc/alternc/templates/bind/templates/zone.template" -NAMED_TEMPLATE="/etc/alternc/templates/bind/templates/named.template" -NAMED_CONF="/var/lib/alternc/bind/automatic.conf" -RNDC="/usr/sbin/rndc" - -dns_zone_file() { - echo "/var/lib/alternc/bind/zones/$1" -} - -dns_is_locked() { - local domain=$1 - if [ ! -r "$(dns_zone_file $domain)" ] ; then - return 1 - fi - grep "LOCKED:YES" "$(dns_zone_file $domain)" - return $? -} - -dns_get_serial() { - local domain=$1 - local serial=$(( $(grep "; serial" $(dns_zone_file $domain) 2>/dev/null|awk '{ print $1;}') + 1 )) - local serial2=$(date +%Y%m%d00) - if [ $serial -gt $serial2 ] ; then - echo $serial - else - echo $serial2 - fi -} - -dns_get_zonettl() { - local domain=$1 - local zonettl=$( - $MYSQL_DO "SELECT zonettl FROM domaines d WHERE d.domaine='$domain';" - ) - # default value - if [ "$zonettl" == "" ] ; then - zonettl="86400" - fi - if [ "$zonettl" -eq "0" ] ; then - zonettl="86400" - fi - echo $zonettl -} - -dns_chmod() { - local domain=$1 - chgrp bind $(dns_zone_file $domain) - chmod 640 $(dns_zone_file $domain) - return 0 -} - -dns_named_conf() { - local domain=$1 - - if [ ! -f "$(dns_zone_file $domain)" ] ; then - echo Error : no file $(dns_zone_file $domain) - return 1 - fi - - # Add the entry - grep -q "\"${domain/./\\.}\"" "$NAMED_CONF" - if [ $? -ne 0 ] ; then - local tempo=$(cat "$NAMED_TEMPLATE") - tempo=${tempo/@@DOMAINE@@/$domain} - tempo=${tempo/@@ZONE_FILE@@/$(dns_zone_file $domain)} - echo $tempo >> "$NAMED_CONF" - # Kindly ask Bind to reload its configuration - # (the zone file is already created and populated) - $RNDC reconfig - # Hook it ! - run-parts --arg=dns_reconfig --arg="$domain" /usr/lib/alternc/reload.d - fi - -} - -dns_delete() { - local domain=$1 - - # Delete the zone file - if [ -w "$(dns_zone_file $domain)" ] ; then - rm -f "$(dns_zone_file $domain)" - fi - - local reg_domain=${domain/./\\.} - - # Remove from the named conf - local file=$(cat "$NAMED_CONF") - echo -e "$file" |grep -v "\"$reg_domain\"" > "$NAMED_CONF" - - # Remove the conf from openDKIM - rm -rf "/etc/opendkim/keys/$domain" - grep -v "^$reg_domain\$" /etc/opendkim/TrustedHosts >/etc/opendkim/TrustedHosts.alternc-tmp && mv /etc/opendkim/TrustedHosts.alternc-tmp /etc/opendkim/TrustedHosts - grep -v "^alternc\._domainkey\.$reg_domain " /etc/opendkim/KeyTable >/etc/opendkim/KeyTable.alternc-tmp && mv /etc/opendkim/KeyTable.alternc-tmp /etc/opendkim/KeyTable - grep -v "^$domain alternc\._domainkey\.$reg_domain\$" /etc/opendkim/SigningTable >/etc/opendkim/SigningTable.alternc-tmp && mv /etc/opendkim/SigningTable.alternc-tmp /etc/opendkim/SigningTable - - # Ask the dns server for restart - $RNDC reconfig - # Hook it ! - run-parts --arg=dns_reconfig --arg="$domain" /usr/lib/alternc/reload.d -} - -# DNS regenerate -dns_regenerate() { - local domain=$1 - local manual_tag=";;; END ALTERNC AUTOGENERATE CONFIGURATION" - local zone_file=$(dns_zone_file $domain) - - # Check if locked - dns_is_locked "$domain" - if [ $? -eq 0 ]; then - echo "DNS $domain LOCKED" - return 1 - fi - - # Get the serial number if there is one - local serial=$(dns_get_serial "$domain") - - # Get the zone ttl - local zonettl=$(dns_get_zonettl "$domain") - - # Generate the headers with the template - local file=$(cat "$ZONE_TEMPLATE") - - # Add the entry - file=$( - echo -e "$file" - $MYSQL_DO "select distinct replace(replace(dt.entry,'%TARGET%',sd.valeur), '%SUB%', if(length(sd.sub)>0,sd.sub,'@')) as entry from sub_domaines sd,domaines_type dt where sd.type=dt.name and sd.domaine='$domain' and sd.enable in ('ENABLE', 'ENABLED') order by entry ;" - ) - - ##### Mail autodetect for thunderbird / outlook - START - # If $file contain DEFAULT_MX - if [ ! -z "$(echo -e "$file" |egrep 'DEFAULT_MX' )" ] ; then - # If $file ! contain autoconfig -> add entry - if [ -z "$(echo -e "$file" |egrep '^autoconfig' )" ] ; then - file="$(echo -e "$file" ; echo -e "autoconfig IN CNAME $FQDN.\n")" - fi - # if $file ! contain autodiscover -> add entry - if [ -z "$(echo -e "$file" |egrep '^autodiscover' )" ] ; then - file="$(echo -e "$file" ; echo -e "autodiscover IN CNAME $FQDN.\n")" - fi - fi # End if containt DEFAULT_MX - ##### Mail autodetect for thunderbird / outlook - END - - ##### OpenDKIM signature management - START - # If $file contain DEFAULT_MX - if [ ! -z "$(echo -e "$file" |egrep 'DEFAULT_MX' )" ] ; then - # If necessary, we generate the key: - if [ ! -d "/etc/opendkim/keys/$domain" ] ; then - mkdir -p "/etc/opendkim/keys/$domain" - - pushd "/etc/opendkim/keys/$domain" >/dev/null - opendkim-genkey -r -d "$domain" -s "alternc" - chown opendkim:opendkim alternc.private - popd - - local reg_domain=${domain/./\\.} - - grep -q "^$reg_domain\$" /etc/opendkim/TrustedHosts || echo "$domain" >>/etc/opendkim/TrustedHosts - grep -q "^alternc\._domainkey\.$reg_domain " /etc/opendkim/KeyTable || echo "alternc._domainkey.$domain $domain:alternc:/etc/opendkim/keys/$domain/alternc.private" >> /etc/opendkim/KeyTable - grep -q "^$domain alternc\._domainkey\.$reg_domain\$" /etc/opendkim/SigningTable || echo "$domain alternc._domainkey.$domain" >> /etc/opendkim/SigningTable - fi - # we add alternc._domainkey with the proper key - - if [ -r "/etc/opendkim/keys/$domain/alternc.txt" ] ; then - file="$(echo -e "$file" ; cat "/etc/opendkim/keys/$domain/alternc.txt")" - fi - fi - ##### OpenDKIM signature management - END - - # Replace the vars by their values - # Here we can add dynamic value for the default MX - file=$( echo -e "$file" | sed -e " - s/%%fqdn%%/$FQDN/g; - s/%%ns1%%/$NS1_HOSTNAME/g; - s/%%ns2%%/$NS2_HOSTNAME/g; - s/%%DEFAULT_MX%%/$DEFAULT_MX/g; - s/%%DEFAULT_SECONDARY_MX%%/$DEFAULT_SECONDARY_MX/g; - s/@@fqdn@@/$FQDN/g; - s/@@ns1@@/$NS1_HOSTNAME/g; - s/@@ns2@@/$NS2_HOSTNAME/g; - s/@@DEFAULT_MX@@/$DEFAULT_MX/g; - s/@@DEFAULT_SECONDARY_MX@@/$DEFAULT_SECONDARY_MX/g; - s/@@DOMAINE@@/$domain/g; - s/@@SERIAL@@/$serial/g; - s/@@PUBLIC_IP@@/$PUBLIC_IP/g; - s/@@ZONETTL@@/$zonettl/g; - " ) - - # Add the manually entered resource records (after the special tag ;;; END ALTERNC AUTOGENERATE CONFIGURATION) - if [ -r "$zone_file" ] ; then - file=$( - echo -e "$file" - grep -A 10000 "$manual_tag" "$zone_file" - ) - fi - # Add the special tag at the end of the zone, if it is not here yet: - if ! echo -e "$file" | grep -q "$manual_tag" - then - file=$(echo -e "$file"; echo "$manual_tag") - fi - - # Init the file - echo -e "$file" > "$zone_file" - - # And set his rights - dns_chmod $domain - # Add it to named conf - dns_named_conf $domain - - # Hook it ! - run-parts --arg=dns_reload_zone --arg="$domain" /usr/lib/alternc/reload.d - - # ask bind to reload the zone - $RNDC reload $domain -} diff --git a/src/functions_hosting.sh b/src/functions_hosting.sh deleted file mode 100644 index 61bd7207..00000000 --- a/src/functions_hosting.sh +++ /dev/null @@ -1,225 +0,0 @@ -#!/bin/bash - -. /usr/lib/alternc/functions.sh - -TEMPLATE_DIR="/etc/alternc/templates/apache2" -HOSTING_DIR="/etc/alternc/functions_hosting" - -HTML_HOME="$ALTERNC_HTML" -VHOST_DIR="/var/lib/alternc/apache-vhost" - -launch_hooks() { - local ACTION=$1 - - if [ ! $2 ] ; then - # If no VTYPE specified - return 0 - fi - - local VTYPE=$2 - - EXITCODE=0 - if [ -x "$HOSTING_DIR/hosting_$VTYPE.sh" ] ; then - # If a specific script exist for this VTYPE, - # we launch it, and return his return code - "$HOSTING_DIR/hosting_$VTYPE.sh" "$1" "$2" "$3" "$4" - EXITCODE=$? - fi - # also launch ssl update domains hook - /usr/lib/alternc/update_certs.sh "$1" "$2" "$3" "$4" - - # No specific script, return 0 - return "$EXITCODE" -} - -host_conffile() { - # Return the absolute path of a conf file for a FQDN - local FQDN="$1" - local U_ID=$(get_uid_by_domain "$FQDN") - local CONFFILE="$VHOST_DIR/${U_ID:(-1)}/$U_ID/$FQDN.conf" - echo $CONFFILE - return 0 -} - -host_create() { - # Function to create a vhost for a website - # First, it look if there is a special file for - # this type of vhost - # If there isn't, it use the default function - # and the template file provided - - local VTYPE="$1" - - launch_hooks "create" "$1" "$2" "$3" "$4" - if [ $? -gt 10 ] ; then - # If the hooks return a value > 10 - # it's mean we do not continue the - # "default" actions - return $? - fi - - # There is no special script, I use the standart template - # If I do not found template manualy define, I look - # If there is an existing template with the good name - - # First, usefull vars. Some may be empty or false, it's - # OK, it will be solve in the "case" below - local FQDN=$2 - local MAIL_ACCOUNT=$3 - local REDIRECT=$4 # Yes, TARGET_DIR and REDIRECT are the same - local TARGET_DIR=$4 # It's used by different template - local U_ID=$(get_uid_by_domain "$FQDN") - local G_ID="$U_ID" - local USER=$(get_account_by_domain $FQDN) - local user_letter=`print_user_letter "$USER"` - local DOCUMENT_ROOT="${HTML_HOME}/${user_letter}/${USER}$TARGET_DIR" - local ACCOUNT_ROOT="${HTML_HOME}/${user_letter}/${USER}/" - local FILE_TARGET=$(host_conffile "$FQDN") - - # In case VTYPE don't have the same name as the template file, - # here we can define it - local TEMPLATE='' - case $VTYPE in -# "example") -# TEMPLATE="$TEMPLATE_DIR/an-example.conf" -# ;; - *) - # No template found, look if there is some in the - # template dir - [ -r "$TEMPLATE_DIR/$VTYPE" ] && TEMPLATE="$TEMPLATE_DIR/$VTYPE" - [ ! "$TEMPLATE" ] && [ -r "$TEMPLATE_DIR/$VTYPE.conf" ] && TEMPLATE="$TEMPLATE_DIR/$VTYPE.conf" - ;; - esac - - # If TEMPLATE is empty, stop right here - [ ! "$TEMPLATE" ] && return 6 - - # Forbid generation for website with UID/GID == 0 - if [[ $U_ID == 0 || $G_ID == 0 ]] ; then - log_error "Fatal error: update_domains/function_dns/host_create : FQDN = $FQDN - TYPE = $VTYPE - UID = $U_ID - GID = $G_ID . Stopping generation" - return 7 - fi - - # Create a new conf file - local TMP_FILE=$(mktemp "/tmp/alternc_host.XXXXXX") - cp "$TEMPLATE" "$TMP_FILE" - - # Substitute special characters : - FQDN2="`echo $FQDN | sed -e 's/\\\\/\\\\\\\\/g' -e 's/#/\\\\#/g' -e 's/&/\\\\\\&/g'`" - DOCUMENT_ROOT2="`echo $DOCUMENT_ROOT | sed -e 's/\\\\/\\\\\\\\/g' -e 's/#/\\\\#/g' -e 's/&/\\\\\\&/g'`" - ACCOUNT_ROOT2="`echo $ACCOUNT_ROOT | sed -e 's/\\\\/\\\\\\\\/g' -e 's/#/\\\\#/g' -e 's/&/\\\\\\&/g'`" - REDIRECT2="`echo $REDIRECT | sed -e 's/\\\\/\\\\\\\\/g' -e 's/#/\\\\#/g' -e 's/&/\\\\\\&/g'`" - USER2="`echo $USER | sed -e 's/\\\\/\\\\\\\\/g' -e 's/#/\\\\#/g' -e 's/&/\\\\\\&/g'`" - - # Put the good value in the conf file - sed -i \ - -e "s#%%LOGIN%%#$USER#g" \ - -e "s#%%fqdn%%#$FQDN2#g" \ - -e "s#%%document_root%%#$DOCUMENT_ROOT2#g" \ - -e "s#%%account_root%%#$ACCOUNT_ROOT2#g" \ - -e "s#%%redirect%%#$REDIRECT2#g" \ - -e "s#%%UID%%#$U_ID#g" \ - -e "s#%%GID%%#$G_ID#g" \ - -e "s#%%mail_account%%#$MAIL_ACCOUNT#g" \ - -e "s#%%user%%#$USER2#g" \ - $TMP_FILE - - ## Fix for wildcard - if [[ "$FQDN2" == "*."* ]]; then - sed -i "s/ServerName/ServerAlias/" $TMP_FILE - fi - - # Check if all is right in the conf file - # If not, put a debug message -# NO : redirect and document_root COULD contains legitimate %% expressions (...) -# local ISNOTGOOD=$(grep "%%" "$TMP_FILE") -# [ "$ISNOTGOOD" ] && (echo "# There was a probleme in the generation : $ISNOTGOOD" > "$TMP_FILE" ; return 44 ) - - # Put the conf file in prod - mkdir -p "$(dirname "$FILE_TARGET")" - mv -f "$TMP_FILE" "$FILE_TARGET" - - # Execute post-install hooks - launch_hooks "postinst" "$1" "$2" "$3" "$4" - if [ $? -gt 10 ] ; then - # If the hooks return a value > 10 - # it's mean we do not continue the - # "default" actions - return $? - fi - - # All is quit, we return 0 - return 0 -} - -host_disable() { - host_change_enable "disable" "$1" "$2" "$3" "$4" -} - -host_enable() { - host_change_enable "enable" "$1" "$2" "$3" "$4" -} - -host_change_enable() { - # Function to enable or disable a host - local STATE=$1 - - # Execute hooks - launch_hooks "$1" "$2" "$3" "$4" - if [ $? -gt 10 ] ; then - # If the hooks return a value > 10 - # it's mean we do not continue the - # "default" actions - return $? - fi - - local TYPE=$2 # no use here, but one day, maybe... So here he is - local FQDN=$3 - local FENABLED=$(host_conffile "$FQDN") - local FDISABLED="$FENABLED-disabled" - - case $STATE in - "enable") - local SOURCE="$FDISABLED" - local TARGET="$FENABLED" - ;; - "disable") - local TARGET="$FDISABLED" - local SOURCE="$FENABLED" - ;; - *) - return 1 - ;; - esac - - if [ ! -e "$TARGET" ] && [ -e "$SOURCE" ] ; then - # If the "target" file do not exist and the "source" file exist - mv -f "$SOURCE" "$TARGET" - else - return 2 - fi -} - -host_delete() { - local VTYPE=$1 - local FQDN=$2 - # Execute post-install hooks - launch_hooks "delete" "$1" "$2" "$3" "$4" - if [ $? -gt 10 ] ; then - # If the hooks return a value > 10 - # it's mean we do not continue the - # "default" actions - return $? - fi - - # Fix of a longstanding BUG: we only DELETE the vhost file if the type is a vhost one ! - if [ -f "${TEMPLATE_DIR}/${VTYPE}.conf" ] - then - local FENABLED=$(host_conffile "$FQDN") - local FDISABLED="$FENABLED-disabled" - - [ -w "$FENABLED" ] && rm -f "$FENABLED" - [ -w "$FDISABLED" ] && rm -f "$FDISABLED" - fi -} - diff --git a/src/update_domains.sh b/src/update_domains.sh index 3869ac72..403581e6 100755 --- a/src/update_domains.sh +++ b/src/update_domains.sh @@ -3,173 +3,3 @@ /usr/lib/alternc/update_domains.php -exit - -# legacy code here - -PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - -for CONFIG_FILE in \ - /etc/alternc/local.sh \ - /usr/lib/alternc/functions.sh \ - /usr/lib/alternc/functions_hosting.sh \ - /usr/lib/alternc/functions_dns.sh - do - if [ ! -r "$CONFIG_FILE" ]; then - echo "Can't access $CONFIG_FILE." - exit 1 - fi - . "$CONFIG_FILE" -done - -stop_if_jobs_locked - -# Some vars -umask 022 -LOCK_FILE="/usr/share/alternc/panel/cron.lock" # FIXME doesn't seem clean to be here -OLDIFS="$IFS" -NEWIFS=" " -RELOAD_WEB="$(mktemp /tmp/alternc_reload_web.XXXX)" -RELOAD_DNS="$(mktemp /tmp/alternc_reload_dns.XXXX)" -B="ยตยตยงยง" # Strange letters to make split in query - -# Somes check before start operations -if [ `id -u` -ne 0 ]; then - log_error "must be launched as root" -elif [ -z "$DEFAULT_MX" -o -z "$PUBLIC_IP" ]; then - log_error "Bad configuration. Please use: dpkg-reconfigure alternc" -elif [ -f "$LOCK_FILE" ]; then - process=$(ps f -p `cat "$LOCK_FILE"|tail -1`|tail -1|awk '{print $NF;}') - if [ "$(basename $process)" = "$(basename "$0")" ] ; then - log_error "last cron unfinished or stale lock file ($LOCK_FILE)." - else - rm "$LOCK_FILE" - fi -fi - -# backward compatibility: single-server setup -if [ -z "$ALTERNC_SLAVES" ] ; then - ALTERNC_SLAVES="localhost" -fi - -# We lock the application -echo $$ > "$LOCK_FILE" - -echo "" > "$RELOAD_WEB" -echo "" > "$RELOAD_DNS" - -# For domains we want to delete completely, make sure all the tags are all right -# set sub_domaines.web_action = delete where domaines.dns_action = DELETE -mysql_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';" - -# Sub_domaines we want to delete -# sub_domaines.web_action = delete -for sub in $( mysql_query "select concat_ws('$B',lower(sd.type), if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine)) from sub_domaines sd where web_action ='DELETE';") ; do - host_delete ${sub/$B/ } - mysql_query "delete from sub_domaines where concat_ws('$B',lower(type), if(length(sub)>0,concat_ws('.',sub,domaine),domaine)) = '$sub' and web_action ='DELETE';" - echo 1 > "$RELOAD_WEB" -done - -# Sub domaines we want to update -# sub_domaines.web_action = update and sub_domains.only_dns = false -IFS="$NEWIFS" -mysql_query " -select concat_ws('$IFS',sd.id, lower(sd.type), if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine), concat_ws('@',m.login,v.value), sd.valeur ) -from sub_domaines sd,membres m,variable v -where sd.compte=m.uid and sd.web_action ='UPDATE' and v.name='mailname_bounce' -;" | while read sdid type domain mail valeur ; do - host_create "$type" "$domain" "$mail" "$valeur" - mysql_query "update sub_domaines sd set web_action='OK',web_result='$?' where sd.id = '$sdid' ; " - echo 1 > "$RELOAD_WEB" -done - -# Domaine to enable -mysql_query "select concat_ws('$IFS',sd.id, lower(sd.type),if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine),sd.valeur) from sub_domaines sd where sd.enable ='ENABLE' ;"|while read sdid type domain valeur ; do - host_enable "$type" "$domain" "$valeur" - mysql_query "update sub_domaines sd set enable='ENABLED' where sd.id = '$sdid' ;" - echo 1 > "$RELOAD_WEB" -done - -# Domains to disable -mysql_query "select concat_ws('$IFS', sd.id, lower(sd.type),if(length(sd.sub)>0,concat_ws('.',sd.sub,sd.domaine),sd.domaine),sd.valeur) from sub_domaines sd where sd.enable ='DISABLE' ;"|while read sdid type domain valeur ; do - host_disable "$type" "$domain" "$valeur" - mysql_query "update sub_domaines sd set enable='DISABLED' where sd.id = '$sdid' ;" - echo 1 > "$RELOAD_WEB" -done - -# Domains we do not want to be the DNS serveur anymore : -# domaines.dns_action = UPDATE and domaines.gesdns = 0 -for dom in `mysql_query "select domaine from domaines where dns_action = 'UPDATE' and gesdns = 0;"| tr '\n' ' '` -do - dns_delete $dom - mysql_query "update domaines set dns_action = 'OK', dns_result = '$?' where domaine = '$dom'" - echo 1 >"$RELOAD_DNS" -done - -# Domains we have to update the dns : -# domaines.dns_action = UPDATE -for dom in `mysql_query "select domaine from domaines where dns_action = 'UPDATE';" | tr '\n' ' '` -do - echo "dns_regenerate : domain=/$dom/" - dns_regenerate $dom - mysql_query "update domaines set dns_action = 'OK', dns_result = '$?' where domaine = '$dom'" - echo 1 >"$RELOAD_DNS" -done - -# Domains we want to delete completely, now we do it -# domaines.dns_action = DELETE -for dom in `mysql_query "select domaine from domaines where dns_action = 'DELETE';" | tr '\n' ' '` -do - dns_delete $dom - # Web configurations have already bean cleaned previously - mysql_query "delete from sub_domaines where domaine='$dom'; delete from domaines where domaine='$dom';" - echo 1 >"$RELOAD_DNS" -done - -if [ ! -z "$(cat "$RELOAD_WEB")" ] ; then - - # Just to encourage user to use THIS directory and not another one - test -d "$VHOST_MANUALCONF" || mkdir -p "$VHOST_MANUALCONF" - - # Concat the apaches files - tempo=$(mktemp "$VHOST_FILE.XXXXX") - - ( - echo "###BEGIN OF ALTERNC AUTO-GENERATED FILE - DO NOT EDIT MANUALLY###" - find "$VHOST_DIR"/ -mindepth 2 -type f -iname "*.conf" -print0 | xargs -0 cat - echo "###END OF ALTERNC AUTO-GENERATED FILE - DO NOT EDIT MANUALLY###" - ) > "$tempo" - - if [ $? -ne 0 ] ; then - log_error " web file concatenation failed" - fi - touch "$VHOST_FILE" - if [ ! -w "$VHOST_FILE" ] ; then - log_error "cannot write on $VHOST_FILE" - fi - mv "$tempo" "$VHOST_FILE" - - # We must reload apache - # we assume we run apache on the master - /usr/lib/alternc/alternc_reload apache || true - # Launch hooks for apache reload - run-parts --arg=web_reload /usr/lib/alternc/reload.d -fi - -# If we added / edited / deleted at least one dns zone file, we go here in the end: -if [ ! -z "$(cat "$RELOAD_DNS")" ] ; then - service opendkim restart - run-parts --arg=dns_reload /usr/lib/alternc/reload.d -fi - -## FIXME : move the slave part into the /usr/lib/alternc/reload.d directory to be an hook -#for slave in $ALTERNC_SLAVES; do -# if [ "$slave" != "localhost" ]; then -# ssh alternc@$slave alternc_reload 'apache' || true -# fi -#done - -rm -f "$LOCK_FILE" "$RELOAD_ZONES" "$RELOAD_WEB" "$INOTIFY_UPDATE_DOMAIN" "$RELOAD_DNS" - -exit 0 - From af3751742af21e5b262cda3cad3ede683320b35e Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Wed, 18 Jul 2018 10:56:35 +0200 Subject: [PATCH 27/30] [fix] autodiscover vhost had wrong filename --- .../apache2/{autodiscover-both.conf => autodiscover.conf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename etc/alternc/templates/apache2/{autodiscover-both.conf => autodiscover.conf} (100%) diff --git a/etc/alternc/templates/apache2/autodiscover-both.conf b/etc/alternc/templates/apache2/autodiscover.conf similarity index 100% rename from etc/alternc/templates/apache2/autodiscover-both.conf rename to etc/alternc/templates/apache2/autodiscover.conf From e118f31397599a100156af4b6472c892d8004d95 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Wed, 18 Jul 2018 11:02:50 +0200 Subject: [PATCH 28/30] [fix] opendkim add and del are now idempotent --- bureau/class/m_mail.php | 45 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/bureau/class/m_mail.php b/bureau/class/m_mail.php index 15ed9fbb..3570fccd 100644 --- a/bureau/class/m_mail.php +++ b/bureau/class/m_mail.php @@ -1137,25 +1137,32 @@ ORDER BY function dkim_add($domain,$uid) { global $db; $target_dir = "/etc/opendkim/keys/$domain"; - if (file_exists($target_dir.'/alternc.txt')) return; // Do not generate if exist - $this->shouldreloaddkim=true; - if (! is_dir($target_dir)) mkdir($target_dir); // create dir - // Generate the key - $old_dir=getcwd(); - chdir($target_dir); - exec('opendkim-genkey -b 1200 -r -d '.escapeshellarg($domain).' -s "alternc" '); - chdir($old_dir); - // opendkim must be owner of the key - chown("$target_dir/alternc.private", 'opendkim'); - chgrp("$target_dir/alternc.private", 'opendkim'); - // Add line into files: - add_line_to_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private"); - add_line_to_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); - // Add subdomaine entry - $dkim_key=$this->dkim_get_entry($domain); - $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='alternc._domainkey', type='dkim', valeur=?;",array($domain,$uid,$dkim_key)); - // no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain + // Create a dkim key when it's not already there : + if (!file_exists($target_dir.'/alternc.txt')) { + $this->shouldreloaddkim=true; + if (! is_dir($target_dir)) mkdir($target_dir); // create dir + // Generate the key, 1200 bits (better than 1024) + $old_dir=getcwd(); + chdir($target_dir); + exec('opendkim-genkey -b 1200 -r -d '.escapeshellarg($domain).' -s "alternc" '); + chdir($old_dir); + // opendkim must be owner of the key + chown("$target_dir/alternc.private", 'opendkim'); + chgrp("$target_dir/alternc.private", 'opendkim'); + + add_line_to_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private"); + add_line_to_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); + } + + // Search for the subdomain entry, if it's not already there, create it: + $db->query("SELECT id FROM sub_domaines WHERE domaine=? AND sub='alternc._domainkey';",array($domain)); + if (!$db->next_record()) { + // Add subdomaine entry + $dkim_key=$this->dkim_get_entry($domain); + $db->query("INSERT INTO sub_domaines SET domaine=?, compte=?, sub='alternc._domainkey', type='dkim', valeur=?;",array($domain,$uid,$dkim_key)); + // no need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK, so dns WILL BE reloaded for this domain + } } @@ -1173,6 +1180,8 @@ ORDER BY del_line_from_file("/etc/opendkim/KeyTable","alternc._domainkey.".$domain." ".$domain.":alternc:/etc/opendkim/keys/".$domain."/alternc.private"); del_line_from_file("/etc/opendkim/SigningTable",$domain." alternc._domainkey.".$domain); } + $db->query("DELETE FROM sub_domaines WHERE domaine=? AND sub='alternc._domainkey';",array($domain)); + // No need to do DNS_ACTION="UPDATE" => we are in the middle of a HOOK } From 13564a9d73643f00a37e749f2e6d11780b8dcab1 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 16 Oct 2018 23:04:46 +0200 Subject: [PATCH 29/30] =?UTF-8?q?[fix]=C2=A0m=5Flxc=20didn't=20work=20on?= =?UTF-8?q?=20new=20=20instead=20of=20...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bureau/class/m_lxc.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bureau/class/m_lxc.php b/bureau/class/m_lxc.php index ed91a883..1f31a3c6 100644 --- a/bureau/class/m_lxc.php +++ b/bureau/class/m_lxc.php @@ -99,8 +99,8 @@ class m_lxc implements vm { } } - $msg = serialize($params); - if (fwrite($fp, $msg . "\n") < 0) { + $message = serialize($params); + if (fwrite($fp, $message . "\n") < 0) { $this->error[] = 'Unable to send data'; return FALSE; } @@ -135,22 +135,22 @@ class m_lxc implements vm { $pass = $pass ? $pass : $mem->user['pass']; $uid = $uid ? $uid : $mem->user['uid']; - $msgg = array('action' => 'start', 'login' => $login, 'pass' => $pass, 'uid' => $uid); - $msgg['mysql_host'] = $mysql->dbus->Host; + $message = array('action' => 'start', 'login' => $login, 'pass' => $pass, 'uid' => $uid); + $message['mysql_host'] = $mysql->dbus->Host; - $res = $this->sendMessage($msgg); + $res = $this->sendMessage($message); if ($res === FALSE) { return $this->error; } else { $data = unserialize($res); $error = (int) $data['error']; $hostname = $data['hostname']; - $msg = $data['msg']; + $message = $data['msg']; $date_start = 'NOW()'; $uid = $mem->user['uid']; if ($error != 0) { - $msg->raise("ERROR", 'lxc', _($msg)); + $msg->raise("ERROR", 'lxc', _($message)); return FALSE; } $db->query("INSERT INTO vm_history (ip,date_start,uid,serialized_object) VALUES (?, ?, ?, ?);", array($hostname, $date_start, $uid, $res)); @@ -166,8 +166,8 @@ class m_lxc implements vm { global $mem; $login = $login ? $login : $mem->user['login']; - $msgg = array('action' => 'get', 'login' => $login); - $res = $this->sendMessage($msgg); + $message = array('action' => 'get', 'login' => $login); + $res = $this->sendMessage($message); if (!$res) { return FALSE; } From b4aa7c3957df2cc8bc63117f90c55b895680d4d0 Mon Sep 17 00:00:00 2001 From: Benjamin Sonntag Date: Tue, 16 Oct 2018 23:08:54 +0200 Subject: [PATCH 30/30] =?UTF-8?q?[fix]=C2=A0m=5Fbind.php=20fixing=20reconf?= =?UTF-8?q?ig=20instead=20of=20reload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bureau/class/m_bind.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bureau/class/m_bind.php b/bureau/class/m_bind.php index 7cb493b8..5403de51 100644 --- a/bureau/class/m_bind.php +++ b/bureau/class/m_bind.php @@ -167,7 +167,7 @@ class m_bind { } if ($this->shouldreconfig) { $ret=0; - exec($this->RNDC." reload 2>&1",$out,$ret); + exec($this->RNDC." reconfig 2>&1",$out,$ret); if ($ret!=0) { $msg->raise("ERROR","bind","Error while reconfiguring bind, error code is $ret\n".implode("\n",$out)); } else {