Merge branch 'master' into pu

This commit is contained in:
Kienan Stewart 2019-03-15 16:55:44 -04:00
commit 81d3105d2b
No known key found for this signature in database
GPG Key ID: 075A846E78FE47EA
51 changed files with 1588 additions and 1768 deletions

5
.gitignore vendored
View File

@ -41,3 +41,8 @@ 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
# Added for running tests; currently not used otherwise
composer.lock
composer.json
vendor/

View File

@ -2,14 +2,16 @@ language: php
services:
- mysql
php:
- 7.1
- 7.0
- 5.6
- 5.5
- 5.4
- 5.3
script: ../vendor/bin/phpunit --coverage-clover=coverage.clover
script:
- grep --exclude-dir=../.git/ --exclude-dir=../vendor/ -l -r -e '#!/bin/[bash|sh]' ../ | uniq | xargs shellcheck
- ../vendor/bin/phpcs --ignore=../vendor/ ../
- ../vendor/bin/phpunit --coverage-clover=coverage.clover
before_script:
- composer require phpunit/dbunit
- composer require 'phpunit/dbunit=<3.0.2' squizlabs/php_codesniffer
- mysql -e 'create database alternc_test DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;'
- cd phpunit
after_script:

View File

@ -48,7 +48,7 @@ install-common:
# Installer and upgrade scripts
test -d $(DESTDIR)/usr/share/alternc/install || mkdir -p $(DESTDIR)/usr/share/alternc/install
cp -r install/* $(DESTDIR)/usr/share/alternc/install
chmod a+x $(DESTDIR)/usr/share/alternc/install/alternc.install $(DESTDIR)/usr/share/alternc/install/dopo.sh $(DESTDIR)/usr/share/alternc/install/mysql.sh $(DESTDIR)/usr/share/alternc/install/newone.php $(DESTDIR)/usr/share/alternc/install/reset_root.php $(DESTDIR)/usr/share/alternc/install/upgrade_check.sh $(DESTDIR)/usr/share/alternc/install/upgrades/*.php $(DESTDIR)/usr/share/alternc/install/upgrades/*.sh
chmod a+x $(DESTDIR)/usr/share/alternc/install/alternc.install $(DESTDIR)/usr/share/alternc/install/dopo.sh $(DESTDIR)/usr/share/alternc/install/mysql.sh $(DESTDIR)/usr/share/alternc/install/newone.php $(DESTDIR)/usr/share/alternc/install/reset_root.php $(DESTDIR)/usr/share/alternc/install/upgrade_check.sh $(DESTDIR)/usr/share/alternc/install/upgrades/*.php $(DESTDIR)/usr/share/alternc/install/upgrades/*.sh $(DESTDIR)/usr/share/alternc/install/variables.php
# install AlternC itself:

View File

@ -147,7 +147,7 @@ $dom->unlock();
}
?></button>
<?php if ($isedit) { ?>
<button class="inb cancel" name="cancel" onclick="document.location = 'dom_edit.php?domain=<?php echo $domain; ?>'"><?php __("Cancel"); ?></button>
<button class="inb cancel" type="button" name="cancel" onclick="document.location = 'dom_edit.php?domain=<?php echo $domain; ?>'"><?php __("Cancel"); ?></button>
<?php } ?>
</td>
</tr>

View File

@ -39,7 +39,7 @@ if (!defined("ALTERNC_PANEL")) exit(); // must be included ;)
if (file_exists("styles/style-custom.css") ) {
echo '<link rel="stylesheet" href="styles/style-custom.css" type="text/css" />';
}
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
<script src="/javascript/jquery-ui/jquery-ui.min.js" type="text/javascript"></script>
<script src="/javascript/jquery-tablesorter/jquery.tablesorter.min.js" type="text/javascript"></script>
<?php
if (count($addhead['js'])) {
if (isset($addhead) && count($addhead['js'])) {
foreach($addhead['js'] as $js) echo $js."\n";
}
?>

View File

@ -87,7 +87,8 @@ if ( empty($logo) || ! $logo ) {
</div>
</div>
<div class="block_login_page">
<a href="request_reset.php"><?php echo _('Request new password'); ?></a>
<br />
<?php __("You must accept the session cookie to log-in"); ?>
<br />
<?php echo _("If you want to use a different language, choose it in the list below"); ?>

View File

@ -81,7 +81,9 @@ echo "<p>";
<input type="password" style="display: none" id="fakePassword" name="fakePassword" value="" />
<table border="1" cellspacing="0" cellpadding="4" class="tedit" >
<tr><th><?php __("Old password"); ?></th><td><input type="password" class="int" name="oldpass" value="<?php isset($oldpass) ? : $oldpass=""; ehe($oldpass); ?>" size="20" maxlength="128" /></td></tr>
<?php if ($mem->requires_old_password_for_change()): ?>
<tr><th><?php __("Old password"); ?></th><td><input type="password" class="int" name="oldpass" value="<?php isset($oldpass) ? : $oldpass=""; ehe($oldpass); ?>" size="20" maxlength="128" /></td></tr>
<?php endif; ?>
<tr><th><?php __("New password"); ?> (1)</th><td><input type="password" class="int" autocomplete="off" id="newpass" name="newpass" value="<?php isset($newpass) ? : $newpass=""; ehe($newpass); ?>" size="20" maxlength="60" /><?php display_div_generate_password(DEFAULT_PASS_SIZE,"#newpass","#newpass2",$passwd_classcount); ?></td></tr>
<tr><th><?php __("New password"); ?> (2)</th><td><input type="password" class="int" autocomplete="off" id="newpass2" name="newpass2" value="<?php isset($newpass2) ? : $newpass2=""; ehe($newpass2);?>" size="20" maxlength="61" /></td></tr>
<tr class="trbtn"><td colspan="3"><input type="submit" class="inb ok" name="submit" value="<?php __("Change my password"); ?>" /></td></tr>
@ -134,8 +136,13 @@ if ($mem->user["su"]) {
</div> <!-- tabsmem -->
<script type="text/javascript">
document.forms['main'].oldpass.focus();
$(function() {$( "#tabsmem" ).tabs();});
if (document.forms['main'].getElementsByClassName("oldpass").length > 0) {
document.forms['main'].oldpass.focus();
}
else {
document.getElementById('newpass').focus();
}
$(function() {$( "#tabsmem" ).tabs();});
</script>
<?php include_once("foot.php"); ?>

View File

@ -0,0 +1,72 @@
<?php
require_once("../class/config_nochk.php");
$request = FALSE;
$valid_request = TRUE;
if (isset($_REQUEST['name_or_email'])) {
$request = TRUE;
// Inserted into the global namespace by config.php
$valid_request = !$fatalcsrf;
if ($fatalcsrf) {
$msg->raise('ERROR', _('Failed to validate CSRF token'));
}
}
// Show the form if nothing was submitted, or if what was submitted is not
// a valid request (eg. doesn't pass CSRF).
$show_form = !$request || ($request && !$valid_request);
if ($request && $valid_request) {
$mem->send_reset_url($_REQUEST['name_or_email']);
}
if (!isset($charset) || ! $charset) {
$charset="UTF-8";
}
@header("Content-Type: text/html; charset=$charset");
require_once("html-head.php");
?>
<body class="login_page">
<div id="global">
<div id="content">
<?php
// Getting logo. C.f. admin/index.php
$logo = variable_get('logo_login', '' ,'You can specify a logo for the login page, example /images/my_logo.png .', array('desc'=>'URL','type'=>'string'));
if ( empty($logo) || ! $logo ) {
$logo = 'images/logo.png';
}
?>
<p id='logo'> <img src="<?php echo $logo; ?>" border="0" height="100px" alt="<?php __("Web Hosting Control Panel"); ?>" title="<?php __("Web Hosting Control Panel"); ?>" />
</p>
<p>&nbsp;</p>
<?php echo $msg->msg_html_all(); ?>
<br/>
<div class="block_list">
<?php if ($show_form): ?>
<div class="block_login_page">
<div class="menu-box">
<div class="menu-title"><?php echo _('Password reset'); ?></div>
<form action="request_reset.php" method="post" name="passwordreset">
<?php csrf_get(); ?>
<div>
<label for="name_or_email"><?php echo _('Username or e-mail'); ?></label>
<input type="text" class="int" name="name_or_email">
</div>
<div class="submit"><input type="submit" class="inb" name="submit"></div>
</form>
</div>
</div>
<div class="block_list">
<p><?php echo _('An e-mail with instructions will be sent'); ?></p>
</div>
<?php else: ?>
<div><p><a href="index.php"><?php __('Return to login page'); ?></a></p></div>
<?php endif; ?>
</div>
</div>
</div>
</body>

15
bureau/admin/reset.php Normal file
View File

@ -0,0 +1,15 @@
<?php
require_once("../class/config_nochk.php");
if (isset($_GET['uid']) && isset($_GET['token']) && isset($_GET['timestamp'])) {
// We may have received a one-time use link.
$logged_in = $mem->temporary_login($_GET['uid'], $_GET['timestamp'],
$_GET['token']);
if ($logged_in) {
$msg->raise('INFO', 'admin/reset', _('Please change your password'));
header("Location: /mem_param.php");
exit;
}
}
header("Location: /index.php");

View File

@ -1,525 +0,0 @@
<?php
/*
----------------------------------------------------------------------
LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
To read the license please visit http://www.gnu.org/copyleft/gpl.html
----------------------------------------------------------------------
*/
/**
* bind9 file management class
*
* @copyright AlternC-Team 2000-2017 https://alternc.com/
*/
class system_bind {
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 $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/';
/**
* Return the part of the conf we got from the database
*
* @global m_mysql $db
* @param string $domain
* @return array $this->cache_conf_db
*/
function conf_from_db($domain=false) {
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
sub_domaines sd,
domaines_type dt
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;
}
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 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);
$hooks->invoke_scripts("/usr/lib/alternc/reload.d", array('dns_reload_zone', $domain) );
$this->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.");
}
} /* Class system_bind */

View File

@ -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
*/

View File

@ -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;
}

View File

@ -241,7 +241,7 @@ class m_action {
* @global m_messages $msg
* @global m_mysql $db
* @param type $all
* @return boolean
* @return boolean|int The number of rows purged; False is there was an error
*/
function purge($all = null) {
global $msg, $db;

185
bureau/class/m_apache.php Normal file
View File

@ -0,0 +1,185 @@
<?php
/*
----------------------------------------------------------------------
LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
To read the license please visit http://www.gnu.org/copyleft/gpl.html
----------------------------------------------------------------------
*/
/**
* Manages APACHE 2.4+ vhosts templates in AlternC 3.5+
*
* @copyright AlternC-Team 2000-2018 https://alternc.com/
*/
class m_apache {
var $shouldreload;
// only values allowed for https in subdomaines table.
var $httpsmodes=array("http","https","both");
// Slave AlternC instances can know the last reload time thanks to this
var $reloadfile="/run/alternc/apache-reload";
// Where do we find apache template files ?
var $templatedir="/etc/alternc/templates/apache2";
// Where do we store all Apache vhosts ?
var $vhostroot="/var/lib/alternc/apache-vhost/";
// launched before any action by updatedomains
function hook_updatedomains_web_pre() {
$this->shouldreload=false;
}
// launched for each FQDN for which we want a new vhost template
function hook_updatedomains_web_add($subdomid) {
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();
$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['login']."@".$L_FQDN,
"%%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",$tpl);
$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($subdomid) {
global $db,$msg;
$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"];
$deleteme= $subdom["sub"].(($subdom["sub"])?".":"").$subdom["domaine"].".conf";
@unlink($confdir."/".$deleteme);
$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);
rename($this->vhostroot."/vhosts_all.conf.new", $this->vhostroot."/vhosts_all.conf");
}
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

238
bureau/class/m_bind.php Normal file
View File

@ -0,0 +1,238 @@
<?php
/*
----------------------------------------------------------------------
LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
To read the license please visit http://www.gnu.org/copyleft/gpl.html
----------------------------------------------------------------------
*/
/**
* Manages BIND 9+ zone management templates in AlternC 3.5+
*
* @copyright AlternC-Team 2000-2018 https://alternc.com/
*/
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 $zone_file_directory = '/var/lib/alternc/bind/zones';
// ------------------------------------------------------------
/** 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;
}
// ------------------------------------------------------------
/**
* 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);
// add the SUBDOMAIN entries
$zone .= $this->conf_from_db($domain);
// 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 "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;
}
}
// ------------------------------------------------------------
/**
* 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 (del_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);
}
// ------------------------------------------------------------
/**
* 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) {
$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." 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 {
$msg->raise("INFO","bind","Bind reconfigured");
}
}
}
// ------------------------------------------------------------
/**
* 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 sub_domaines table
* @global m_mysql $db
* @param string $domain
* @return string a zonefile excerpt
*/
function conf_from_db($domain) {
global $db;
$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
sd.type=dt.name
AND sd.enable IN ('ENABLE', 'ENABLED')
ORDER BY ENTRY ;");
$t="";
while ($db->next_record()) {
$t.= $db->f('ENTRY')."\n";
}
return $t;
}
} // m_bind

View File

@ -54,15 +54,7 @@ class m_dom {
* du domaine par update_domains.sh
* @access private
*/
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;
const fic_lock_cron = "/run/alternc/cron.lock";
var $type_local = "VHOST";
var $type_url = "URL";
@ -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;
}
@ -642,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);
}
@ -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;
}
@ -1072,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;
@ -1098,10 +1092,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 +1257,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;
}
@ -1308,13 +1302,14 @@ 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) {
global $db, $msg, $cuid, $bro;
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 ?
if (!$this->islocked) {
if (!$domislocked) {
$msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
return false;
}
@ -1396,10 +1391,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;
}
@ -1442,11 +1437,11 @@ class m_dom {
* TRUE sinon.
*
*/
function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 86400) {
global $db, $msg, $hooks;
function edit_domain($dom, $dns, $gesmx, $force = false, $ttl = 3600) {
global $db, $msg, $hooks, $domislocked;
$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,15 +1760,20 @@ 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!"));
}
while (file_exists($this->fic_lock_cron)) {
// wait for the file to disappear, or at most 15min:
while (file_exists(m_dom::fic_lock_cron) && filemtime(m_dom::fic_lock_cron)>(time()-900)) {
clearstatcache();
sleep(2);
}
$this->islocked = true;
@touch(m_dom::fic_lock_cron);
$domislocked = true;
// extra safe :
register_shutdown_function(array("m_dom","unlock"),1);
return true;
}
@ -1783,13 +1783,15 @@ class m_dom {
* return true
* @access private
*/
function unlock() {
global $msg;
function unlock($isshutdown=0) {
global $msg,$domislocked;
$msg->debug("dom", "unlock");
if (!$this->islocked) {
if (!$isshutdown && !$domislocked) {
$msg->raise("ERROR", "dom", _("--- Program error --- No lock on the domains!"));
}
$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;
}
@ -1892,199 +1894,103 @@ 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 ";
* 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() {
global $db, $hooks;
if (posix_getuid()!=0) {
echo "FATAL: please lauch me as root\n";
exit();
}
$query .= "
order by
m.login,
sd.domaine,
sd.sub;";
$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';");
$db->query($query, $query_args);
$r = array();
// Search for things to do on DOMAINS:
$db->query("SELECT * FROM domaines WHERE dns_action!='OK';");
$alldoms=array();
while ($db->next_record()) {
$r[$db->Record['sub_id']] = $db->Record;
$alldoms[$db->Record["id"]]=$db->Record;
}
return $r;
}
// 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($onedom));
} else {
$ret = $hooks->invoke("hook_updatedomains_dns_add",array($onedom));
}
/**
* 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 ($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["domaine"]));
}
}
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;
$hooks->invoke("hook_updatedomains_dns_post");
}
$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']);
// 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();
$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"]) {
if ($db->Record["web_action"]=="DELETE") {
$delete[]=$db->Record["id"];
} else {
$ignore[]=$db->Record["id"];
}
} else {
$check_dup[] = $p['fqdn'];
$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));
}
// 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"]));
}
// 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;
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));
}
}
// Return the conf
$ret.= "# Sub_id: " . $p['sub_id'] . "\n" . $tpl;
$hooks->invoke("hook_updatedomains_web_post");
}
return $ret;
$this->unlock();
}
/**
* 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
*/
@ -2095,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) {
@ -2160,6 +2059,8 @@ class m_dom {
_("Default mail server");
_("Default backup mail server");
_("AlternC panel access");
_("DKIM Key");
_("Email autoconfiguration");
}
} /* Class m_domains */

View File

@ -53,6 +53,11 @@ class m_hooks {
// existe on l'execute et on rajoute ce qu'elle a retourné dans
// un tableau
$val = array();
if (!$classes) {
// Leaving early if classes isn't set prevents PHP warnings.
// Happens frequently when running PHPUnit tests.
return $val;
}
foreach ($classes as $c) {
global $$c;
if (method_exists($$c, $hname)) {

View File

@ -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;
}

View File

@ -309,7 +309,7 @@ ORDER BY
}
$db->query("SELECT a.id, a.address, a.password, a.`enabled`, a.mail_action, d.domaine AS domain, m.quota, m.quota*1024*1024 AS quotabytes, q.quota_dovecot as used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.domain_id
FROM ((domaines d, address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN dovecot_quota q ON CONCAT(a.address,'@',d.domaine) = q.user) LEFT JOIN recipient r ON r.address_id=a.id
WHERE " . $where . " AND d.id=a.domain_id " . $limit . " ;", $query_args);
WHERE " . $where . " AND d.id=a.domain_id ORDER BY a.address ASC " . $limit . " ;", $query_args);
if (!$db->next_record()) {
$msg->raise("ERROR", "mail", _("No email found for this query"));
return array();
@ -466,12 +466,18 @@ ORDER BY
}
}
$db->query("SELECT domaine FROM domaines WHERE id= ? ;", array($dom_id));
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));
if (!$db->next_record()) {
return false;
}
$domain=$db->Record["domaine"];
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND (type='defmx' OR type='defmx2');", array($domain));
$this->del_dns_dmarc($domain);
$this->del_dns_spf($domain);
$this->del_dns_autoconf($domain);
$this->dkim_del($domain);
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id= ? ;", array($dom_id));
return true;
}
@ -912,6 +918,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 +935,7 @@ ORDER BY
}
// ------------------------------------------------------------
/**
* hook function called by AlternC when a domain is created for
* the current user account
@ -937,7 +945,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,26 +955,30 @@ 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()) {
$domaine=$db->Record["domaine"];
$compte=$db->Record["compte"];
$this->set_dns_autoconf($domaine,$compte);
if ($spf = variable_get("default_spf_value")) {
$this->set_dns_spf($db->Record["domaine"], $spf);
$this->set_dns_spf($domaine, $spf);
}
if ($dmarc = variable_get("default_dmarc_value")) {
$this->set_dns_dmarc($db->Record["domaine"], $dmarc);
$this->set_dns_dmarc($domaine, $dmarc);
}
}
return $this->create_alias($domain_id, 'postmaster', $mem->user['login'] . '@' . $mailname);
}
// ------------------------------------------------------------
/**
* 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 +1003,44 @@ 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', 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', valeur='';",array($domain,$uid));
$changed=true;
}
if ($changed) {
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain));
}
return $changed;
}
// ------------------------------------------------------------
/**
* delete the autoconf / autodiscover vhosts when removing a domain as MX
*/
function del_dns_autoconf($domain) {
global $db, $L_FQDN, $cuid;
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='autodiscover' AND sub='autoconfig';", array($domain));
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='autodiscover' AND sub='autodiscover';", array($domain));
}
// ------------------------------------------------------------
/**
* 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.
@ -999,7 +1048,8 @@ ORDER BY
* @access private
*/
function set_dns_spf($domain, $spf, $previous = -1, $uid = -1, $login = -1) {
global $db, $cuid, $mem;
global $db, $cuid, $mem, $msg;
$msg->debug("mail","set_dns_spf($domain, $spf, $previous, $uid, $login)");
// defaults
if ($uid === -1) {
$uid = intval($cuid);
@ -1021,7 +1071,18 @@ ORDER BY
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain));
}
// ------------------------------------------------------------
/**
* delete the SPF entries in the sub_domaine table for a domain
* called by del_domain or del_mx_domain by hooks :
*/
function del_dns_spf($domain) {
global $db;
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND sub='' AND valeur LIKE 'v=spf1 %';", array($domain));
}
// ------------------------------------------------------------
/**
* 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.
@ -1029,7 +1090,8 @@ ORDER BY
* @access private
*/
function set_dns_dmarc($domain, $dmarc, $previous = -1, $uid = -1, $login = -1) {
global $db, $cuid, $mem, $L_FQDN;
global $db, $cuid, $mem, $L_FQDN, $msg;
$msg->debug("mail","set_dns_dmarc($domain, $dmarc, $previous, $uid, $login)");
// defaults
if ($uid === -1) {
$uid = intval($cuid);
@ -1055,5 +1117,151 @@ ORDER BY
}
// ------------------------------------------------------------
/**
* delete the DMARC entries in the sub_domaine table for a domain
* called by del_domain or del_mx_domain by hooks :
*/
function del_dns_dmarc($domain) {
global $db;
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND sub='' AND valeur LIKE 'v=dmarc1 %';", array($domain));
}
/** 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,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;
} else {
$add[]=$db->Record;
}
}
foreach($add as $domain) {
$this->dkim_add($domain["domaine"],$domain["compte"]);
}
foreach($del as $domain) {
$this->dkim_del($domain["domaine"]);
}
}
// ------------------------------------------------------------
/**
* 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,$uid) {
global $db;
$target_dir = "/etc/opendkim/keys/$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
}
}
// ------------------------------------------------------------
/**
* Delete a domain from OpenDKIM configuration
*/
function dkim_del($domain) {
global $db;
$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);
}
$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
}
// ------------------------------------------------------------
/**
* 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) {
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\s+IN\s+TXT\s+\( "(.*)"#',$line,$mat)) {
$result.=$mat[1]; $inkey=true; continue;
}
if ($inkey && preg_match('#^\s*"(.*)"\s*\)#',$line,$mat)) {
$result.=$mat[1]; $inkey=false; break;
}
if ($inkey && preg_match('#^\s*"(.*)"\s*$#',$line,$mat)) {
$result.=$mat[1]; $inkey=true; continue;
}
}
if ($result)
return $result;
}
$msg->debug("mail","dkim_get_entry($domain) failed");
return false;
}
// @TODO hook after reloading DNS zones => if necessary, restart opendkim
} /* Class m_mail */

View File

@ -41,7 +41,6 @@ class m_mem {
*/
var $local;
/**
* Password kind used in this class (hook for admin class)
*/
@ -401,10 +400,14 @@ class m_mem {
$msg->raise("ERROR", "mem", _("You are not allowed to change your password."));
return false;
}
if (!password_verify($oldpass, $this->user['pass'])) {
$msg->raise("ERROR", "mem", _("The old password is incorrect"));
return false;
if ($this->requires_old_password_for_change()) {
if (!password_verify($oldpass, $this->user['pass'])) {
$msg->raise("ERROR", "mem", _("The old password is incorrect"));
return false;
}
}
if ($newpass != $newpass2) {
$msg->raise("ERROR", "mem", _("The new passwords are differents, please retry"));
return false;
@ -418,11 +421,12 @@ class m_mem {
$newpass = password_hash($newpass, PASSWORD_BCRYPT);
$db->query("UPDATE membres SET pass= ? WHERE uid= ?;", array($newpass, $cuid));
$msg->init_msgs();
setcookie('require_old_password', '', 1);
return true;
}
/**
/**
* Change the administrator preferences of an admin account
* @param integer $admlist visualisation mode of the account list (0=large 1=short)
* @return boolean TRUE if the preferences has been changed, FALSE if not.
@ -678,4 +682,264 @@ Cordially.
return true;
}
/**
* Sends a password-reset URL.
*/
public function send_reset_url($email_or_login) {
global $msg, $L_FQDN, $L_HOSTING, $db;
// Look up user by email_or_login.
$db->query("SELECT * FROM membres WHERE login = ? OR mail = ? ;", array($email_or_login, $email_or_login));
$msg->log('mem', 'send_reset_url', 'Password reset requested for: ' . $email_or_login);
// Give user feedback, even if we don't have an account stored.
$msg->raise('INFO', 'mem', _('An e-mail with information on how to connect has been sent to the owner of the account if one exists'));
// It is possible here that a user could have multiple accounts for a
// single e-mail since 'mail' is not a uniqe key in the membres table.
// For the moment we'll just take the first account.
if (!$db->num_rows()) {
$msg->log('mem', 'send_reset_url', 'No member found with login or mail ' . $email_or_login);
return FALSE;
}
if ($db->num_rows()) {
$db->next_record();
// Get a reset URL for the current timestamp.
$url = $this->generate_reset_url($db->f('uid'));
$mail = $db->f('mail');
}
if (!$url || !$mail) {
return FALSE;
}
$duration = variable_get('password_reset_expiration', 86400, 'The number of seconds for which a password reset link is valid');
$duration_hours = ($duration / 3600.0) . ' ' . _('hours');
$message = sprintf(_('
Hi,
someone requested a password reset for your account at %s (%s).
You may connect to your account and change your account by clicking on the following URL or copying it into your browser :
%s
This link may only be used once. You should change your password in your account settings once connected. This link will only be valid for %s, and no changes will be made if it is not used.
'), $L_HOSTING, $L_FQDN, $url, $duration_hours);
mail($mail, "Password reset request on {$L_HOSTING}", $message, "From: postmaster@{$L_FQDN}\nReply-to: postmaster@{$L_FQDN}");
$msg->log('mem', 'send_reset_url', "Password reset e-mail sent for account {$uid} at {$mail}");
}
/**
* Generate a reset URL for an account given it's e-mail or login.
*
* @param $email_or_login
* A string with the email or login.
*
* @returns string|boolean
* A reset URL or FALSE in case of error.
*/
function generate_reset_url($uid) {
global $db;
$db->query("SELECT * FROM membres WHERE uid = ? ;", array($uid));
if (!$db->num_rows()) {
return FALSE;
}
if ($db->num_rows()) {
$db->next_record();
// Get a reset URL for the current timestamp.
return $this->_get_reset_url(time(), $db->f('uid'), $db->f('login'), $db->f('pass'));
}
return FALSE;
}
/**
* Builds a full reset URL from the uid, login, password and timestamp.
*
* @returns string
* A full URL.
*/
function _get_reset_url($timestamp, $uid, $login, $password) {
global $db, $L_FQDN;
$salt = variable_get('salt_password_reset', base64_encode(random_bytes(128)), 'The salt used when hasing password resets - change to invalidate all existing reset tokens') . $password;
$data = $timestamp . $uid . $login;
$token = hash_hmac('sha512', $data, $salt);
// @TODO: Not sure where the bureau's preferred protocol is stored, but
// since 3.5.0 https seems to be the default.
return 'https://' . $L_FQDN . '/reset.php?' . http_build_query(array(
'uid' => $uid,
'timestamp' => $timestamp,
'token' => $token,
));
}
/**
* Logs a user in from a one-time login link.
*/
function temporary_login($uid, $timestamp, $token, $restrictip = 0, $authip_token = false) {
global $db, $msg, $cuid, $authip;
if (!$this->validate_reset_url($uid, $timestamp, $token)) {
return FALSE;
}
$msg->log("mem", "temporary_login", $username);
if ($msg->has_msgs("ERROR")) {
return FALSE;
}
$db->query("select * from membres where uid= ? ;", array($uid));
if ($db->num_rows() == 0) {
return FALSE;
}
$db->next_record();
// No password verification for temporary logins, the validation
// is in validate_reset_url instead.
if (!$db->f("enabled")) {
$msg->raise("ERROR", "mem", _("This account is locked, contact the administrator."));
return FALSE;
}
$this->user = $db->Record;
$cuid = $db->f("uid");
if (panel_islocked() && $cuid != 2000) {
$msg->raise("ALERT", "mem", _("This website is currently under maintenance, login is currently disabled."));
return FALSE;
}
// AuthIP
$allowed_ip = FALSE;
if ($authip_token) {
$allowed_ip = $this->authip_tokencheck($authip_token);
}
$aga = $authip->get_allowed('panel');
foreach ($aga as $k => $v) {
if ($authip->is_in_subnet(get_remote_ip(), $v['ip'], $v['subnet'])) {
$allowed = TRUE;
}
}
// Error if there is rules, the IP is not allowed and it's not in the whitelisted IP
if (sizeof($aga) > 1 && !$allowed_ip && !$authip->is_wl(get_remote_ip())) {
$msg->raise("ERROR", "mem", _("Your IP isn't allowed to connect"));
return FALSE;
}
// End AuthIP
if ($restrictip) {
$ip = get_remote_ip();
} else {
$ip = "";
}
// Close sessions that are more than 2 days old.
$db->query("DELETE FROM sessions WHERE DATE_ADD(ts,INTERVAL 2 DAY)<NOW();");
// Delete old impersonations.
if (isset($_COOKIE["oldid"])) {
setcookie('oldid', '', 0, '/');
}
// Open the session
$sess = md5(mt_rand().mt_rand().mt_rand());
$_REQUEST["session"] = $sess;
$db->query("insert into sessions (sid,ip,uid) values (?, ?, ?);", array($sess, $ip, $cuid));
setcookie("session", $sess, 0, "/");
$msg->init_msgs();
// Fill in $local.
$db->query("SELECT * FROM local WHERE uid= ? ;", array($cuid));
if ($db->num_rows()) {
$db->next_record();
$this->local = $db->Record;
}
$this->resetlast();
// Set a cookie parameter to allow password change without requiring
// previous one.
$db->query('select lastlogin, pass from membres where uid = ?;', array($uid));
if ($db->num_rows()) {
$db->next_record();
$cookie_data = $cuid . $db->f('lastlogin');
$salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
$c = setcookie('require_old_password', hash_hmac('sha512', $cookie_data, $salt), 0, '/');
if (!$c) {
$msg->log('mem', 'temporary_login', 'Failed to set cookie require_old_password');
}
}
return TRUE;
}
function requires_old_password_for_change() {
global $cuid, $db;
$cookie = $_COOKIE['require_old_password'];
if (!$cookie) {
return TRUE;
}
$db->query('select lastlogin, pass from membres where uid = ?;', array($cuid));
if ($db->num_rows()) {
$db->next_record();
$cookie_data = $cuid . $db->f('lastlogin');
$salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
if ($cookie == hash_hmac('sha512', $cookie_data, $salt)) {
return FALSE;
}
}
return TRUE;
}
/**
* Validates a reset URL that has been received.
*/
function validate_reset_url($uid, $timestamp, $token) {
global $cuid, $db, $msg;
// Do not log a person in if they are logged in already.
if ($this->checkid(false)) {
$msg->raise('ERROR', 'mem', _('You are already logged in, you may not use a one-time login link'));
$msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the user is already connected');
return FALSE;
}
// The timestamp is older than the age limit - invalid.
$fail_message = _('The login-link has already been used or is expired');
$duration = variable_get('password_reset_expiration', 86400, 'The number of seconds for which a password reset link is valid');
if (time() - $timestamp >= $duration) {
$msg->raise('ERROR', 'mem', $fail_message);
$msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the time elapsed is greater than limit of ' . $duration);
return FALSE;
}
$db->query("SELECT * FROM membres WHERE uid = ? ;", array($uid));
if (!$db->num_rows()) {
$msg->raise('ERROR', 'mem', $fail_message);
$msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since a user with ID ' . $uid. ' does not exist');
return FALSE;
}
$db->next_record();
$last_login = strtotime($db->f('lastlogin'));
// The timestamp is older than the most recent login - invalid.
if ($last_login >= time() || $last_login >= $timestamp) {
$msg->raise('ERROR', 'mem', $fail_message);
$msg->log('mem', 'validate_reset_url', "Refused one-time log-in since the most recent login was more recent than the timestamp in the log-in link. Last: {$last_login}, Timestamp: {$timestamp}");
return FALSE;
}
// The account is locked or cannot change pass - invalid.
if (!$db->f('enabled') || !$db->f('canpass')) {
$msg->raise('ERROR', 'mem', $fail_message);
$msg->log('mem', 'validate_reset_url', 'Refused one-time log-in since the user account is disabled or cannot change it\'s password.');
return FALSE;
}
// Using the current user info and timestamp the tokens generated
// do not match - invalid. (Eg. user password changed, salt changed).
$salt = variable_get('salt_password_reset', base64_encode(random_bytes(128))) . $db->f('pass');
$data = $timestamp . $uid . $db->f('login');
$ref_token = hash_hmac('sha512', $data, $salt);
if ($token != $ref_token) {
$msg->raise('ERROR', 'mem', $fail_message);
return FALSE;
}
$msg->raise('INFO', 'mem', _('You have used a one-time login link. Please set a new password now.'));
return TRUE;
}
} /* Class m_mem */

View File

@ -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.".pem",
"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
@ -620,6 +647,34 @@ 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($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"]));
}
// ----------------------------------------------------------------
/** Search for the best certificate for a user and a fqdn
@ -635,7 +690,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);
@ -667,16 +740,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;
}

View File

@ -1601,24 +1601,20 @@ msgid "Quit"
msgstr "Fermer"
#: ../admin/bro_main.php:73
#, fuzzy, php-format
msgid "The folder '%s' was successfully created"
msgstr "L'utilisateur %s a été effacée avec succès"
msgstr "Le dossier %s a été effacé avec succès"
#: ../admin/bro_main.php:79
#, fuzzy, php-format
msgid "The file '%s' was successfully created"
msgstr "L'Email a été modifié avec succès"
msgstr "Le fihier été créé avec succès"
#: ../admin/bro_main.php:94
#, fuzzy, php-format
msgid "The folder '%s' was successfully deleted"
msgstr "L'utilisateur %s a été effacée avec succès"
msgstr "Le dossier %s a été effacé avec succès"
#: ../admin/bro_main.php:96
#, fuzzy, php-format
msgid "The file '%s' was successfully deleted"
msgstr "L'adresse email %s a été effacée avec succès"
msgstr "Le fichier %s a été effacé avec succès"
#: ../admin/bro_main.php:102
#, php-format
@ -1663,39 +1659,32 @@ msgid "The file '%s' was successfully moved to '%s'"
msgstr "L'Email a été modifié avec succès"
#: ../admin/bro_main.php:145
#, fuzzy
msgid "The files / folders were successfully moved"
msgstr "Le répertoire protégé %s a été déprotégé avec succès"
msgstr "Les fichiers / répertoires %s ont été déplacé avec succès"
#: ../admin/bro_main.php:153
#, fuzzy, php-format
msgid "The folder '%s' was successfully renamed to '%s'"
msgstr "L'Email a été modifié avec succès"
msgstr "Le dossier '%s' a été renommé '%s' avec succès"
#: ../admin/bro_main.php:155
#, fuzzy, php-format
msgid "The file '%s' was successfully renamed to '%s'"
msgstr "L'Email a été modifié avec succès"
msgstr "Le fichier '%s' a été renommé '%s' avec succès"
#: ../admin/bro_main.php:157
#, fuzzy
msgid "The files / folders were successfully renamed"
msgstr "Le compte AlternC a été renouvelé avec succès"
msgstr "Les fichiers / répertoires ont été renommés avec succès"
#: ../admin/bro_main.php:162
#, fuzzy, php-format
msgid "The file '%s' was successfully uploaded"
msgstr "L'adresse email %s a été effacée avec succès"
msgstr "Le fichier '%s' a été téléversé avec succès"
#: ../admin/bro_main.php:167
#, fuzzy
msgid "The permissions were successfully set"
msgstr "L'Email a été modifié avec succès"
msgstr "Les permissions ont été appliquées avec succès"
#: ../admin/bro_main.php:175
#, fuzzy, php-format
msgid "The extraction of the file '%s' succeeded"
msgstr "Le mot de passe de l'utilisateur %s à été modifié avec succés"
msgstr "L'extraction du fichier '%s' a été effectuée avec succés"
#: ../admin/bro_main.php:180 ../class/m_bro.php:74
msgid "File browser"
@ -2713,12 +2702,10 @@ msgid "Folder %s is protected"
msgstr ""
#: ../admin/hta_doadduser.php:45
#, fuzzy, php-format
msgid "The user %s was added to the protected folder %s"
msgstr "Modification de l'utilisateur %s dans le répertoire protégé %s"
msgstr "L'utilisateur %s a été ajouté au répertoire protégé %s"
#: ../admin/hta_dodeluser.php:39
#, fuzzy, php-format
msgid "The user '%s' was successfully deleted"
msgstr "L'utilisateur %s a été effacée avec succès"
@ -4273,9 +4260,8 @@ msgid "Create this new MySQL user"
msgstr "Créer ce nouvel utilisateur MySQL"
#: ../admin/sql_users_del.php:38 ../class/m_mysql.php:770
#, fuzzy, php-format
msgid "The user '%s' has been successfully deleted"
msgstr "L'utilisateur %s a été effacée avec succès"
msgstr "L'utilisateur MySQL %s a été effacé avec succès"
#: ../admin/sql_users_del.php:49
msgid "MySQL users"
@ -4294,14 +4280,12 @@ msgid "Yes, delete the MySQL user"
msgstr "Oui, effacer cet utilisateur MySQL"
#: ../admin/sql_users_doadd.php:41 ../admin/sql_users_doadd.php:50
#, fuzzy, php-format
msgid "The user '%s' has been successfully created."
msgstr "L'utilisateur %s a été effacée avec succès"
msgstr "L'utilisateur %s a été ajouté avec succès"
#: ../admin/sql_users_dopassword.php:35
#, fuzzy, php-format
msgid "Password changed for user '%s'."
msgstr "Permettre le changement de mot de passe ?"
msgstr "Mot de passe changé pour l'utilisateur '%s'."
#: ../admin/sql_users_dorights.php:49
msgid "The rights has been successfully applied to the user"

View File

@ -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

View File

@ -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"

10
debian/control vendored
View File

@ -59,12 +59,13 @@ Depends: debianutils (>= 1.13.1)
, opendkim-tools
, dovecot-sieve
, dovecot-managesieved
, default-mysql-client
, default-mysql-client | mysql-client | mariadb-client
, php5-curl | php7.0-curl
, quota
, pwgen
, lsb-release
, ${misc:Depends}
Recommends: default-mysql-server
Recommends: default-mysql-server | mysql-server | mariadb-server
, ntp
, unzip
, bzip2
@ -148,8 +149,9 @@ Depends: debianutils (>= 1.13.1)
, opendkim-tools
, dovecot-sieve
, dovecot-managesieved
, default-mysql-client
, php5-curl
, default-mysql-client | mysql-client | mariadb-client
, php5-curl | php7.0-curl
, lsb-release
, ${misc:Depends}
Recommends:
quota

View File

@ -14,6 +14,7 @@ AssignUserId www-data www-data
Options +FollowSymLinks
AllowOverride None
Require all denied
Satisfy Any
</Directory>
#### End security parameters

View File

@ -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

View File

@ -0,0 +1,57 @@
<VirtualHost *:80>
DocumentRoot /usr/share/alternc/panel/admin
AssignUserId alterncpanel alterncpanel
SetEnv LOGIN "0000-panel"
ServerName %%fqdn%%
RewriteEngine On
# 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]
RewriteRule (.*) - [F]
</VirtualHost>
<VirtualHost *:443>
DocumentRoot /usr/share/alternc/panel/admin
AssignUserId alterncpanel alterncpanel
SetEnv LOGIN "0000-panel"
ServerName %%fqdn%%
RewriteEngine On
# 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]
RewriteRule (.*) - [F]
SSLEngine On
SSLCertificateFile %%CRT%%
SSLCertificateKeyFile %%KEY%%
%%CHAINLINE%%
</VirtualHost>

View File

@ -7,6 +7,7 @@
<Directory "%%document_root%%">
php_admin_value open_basedir "%%account_root%%:/usr/share/php/"
php_admin_value upload_tmp_dir %%account_root%%/tmp
php_admin_value sys_temp_dir %%account_root%%/tmp
php_admin_value sendmail_path '/usr/lib/alternc/sendmail "%%mail_account%%" '
php_admin_flag mail.add_x_header on
Options +MultiViews -FollowSymLinks +SymLinksIfOwnerMatch
@ -21,6 +22,7 @@
<Directory "%%document_root%%">
php_admin_value open_basedir "%%account_root%%:/usr/share/php/"
php_admin_value upload_tmp_dir %%account_root%%/tmp
php_admin_value sys_temp_dir %%account_root%%/tmp
php_admin_value sendmail_path '/usr/lib/alternc/sendmail "%%mail_account%%" '
php_admin_flag mail.add_x_header on
Options +MultiViews -FollowSymLinks +SymLinksIfOwnerMatch

View File

@ -8,10 +8,12 @@
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteCond %{REQUEST_FILENAME} !/.well-known/acme-challenge/
RewriteRule ^/(.*)$ http://%%fqdn%%/$1 [R=301,L]
SSLEngine On
SSLCertificateFile %%CRT%%
SSLCertificateKeyFile %%KEY%%
%%CHAINLINE%%
</Virtualhost>
<VirtualHost *:80>
ServerName %%fqdn%%
@ -21,6 +23,7 @@
<Directory "%%document_root%%">
php_admin_value open_basedir "%%account_root%%:/usr/share/php/"
php_admin_value upload_tmp_dir %%account_root%%/tmp
php_admin_value sys_temp_dir %%account_root%%/tmp
php_admin_value sendmail_path '/usr/lib/alternc/sendmail "%%mail_account%%" '
php_admin_flag mail.add_x_header on
Options -MultiViews -FollowSymLinks +SymLinksIfOwnerMatch

View File

@ -17,6 +17,7 @@
<Directory "%%document_root%%">
php_admin_value open_basedir "%%account_root%%:/usr/share/php/"
php_admin_value upload_tmp_dir %%account_root%%/tmp
php_admin_value sys_temp_dir %%account_root%%/tmp
php_admin_value sendmail_path '/usr/lib/alternc/sendmail "%%mail_account%%" '
php_admin_flag mail.add_x_header on
Options +MultiViews -FollowSymLinks +SymLinksIfOwnerMatch

View File

@ -5,6 +5,7 @@
acl "allslaves" {
{
127.0.0.1;
::1;
//AUTO-SLAVES//
};
};

View File

@ -1 +1 @@
zone "@@DOMAINE@@" { type master; file "@@ZONE_FILE@@"; allow-query { any; }; };
zone "@@DOMAIN@@" { type master; file "@@ZONE_FILE@@"; allow-query { any; }; };

View File

@ -76,6 +76,14 @@ fi
. /usr/lib/alternc/functions.sh
# get the information on running Sysv or Systemd init & boot system
if [ -e /run/systemd/system ]
then
SYSTEMD=1
else
SYSTEMD=0
fi
# Lock the jobs !
lock_jobs
@ -91,7 +99,7 @@ TEMPLATE_DIR="/etc/alternc/templates"
# Find needed configuration files (without the initial '/')
# replace this one unconditionnally
CONFIG_FILES="etc/alternc/bureau.conf etc/apache2/envvars etc/alternc/apache2.conf etc/alternc/apache_logformat.conf etc/alternc/phpmyadmin.inc.php"
CONFIG_FILES="etc/alternc/bureau.conf etc/apache2/envvars etc/alternc/apache2.conf etc/alternc/phpmyadmin.inc.php"
if [ -e /etc/bind/named.conf ]; then
CONFIG_FILES="$CONFIG_FILES etc/bind/named.conf.options"
@ -365,9 +373,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
@ -377,7 +384,6 @@ if [ -x /usr/sbin/apache2 ]; then
run-parts --arg=apache2 /usr/lib/alternc/install.d
a2enmod mpm_itk
s="apache2"
# unused from AlternC 1.0, FIXME: remove it later
if [ -L /etc/apache2/mods-enabled/vhost_alias.load ]
then
@ -410,7 +416,7 @@ if [ -x /usr/sbin/apache2 ]; then
a2enconf alternc
fi
SERVICES="$SERVICES $s"
SERVICES="$SERVICES apache2"
fi
# Manage sudoers.d include appearing in Squeeze:
@ -608,6 +614,9 @@ fi
chmod 640 /var/log/alternc/bureau.log /var/log/alternc/update_domains.log
chown alterncpanel:adm /var/log/alternc/bureau.log /var/log/alternc/update_domains.log
# Launch a script that will populate AlternC variables as needed
su - alterncpanel -s /bin/bash -c /usr/share/alternc/install/variables.php
# Creating admin user if needed
HAS_ROOT=`mysql --defaults-file=/etc/alternc/my.cnf -e "SELECT COUNT(*) FROM membres WHERE login = 'admin' OR login = 'root' and su = 1" | tail -1`
@ -668,6 +677,16 @@ if [ "$(lsb_release -s -c)" == 'stretch' ] ; then
systemctl daemon-reload
fi
if [ "$SYSTEMD" = "1" -a "$(lsb_release -s -c)" = "stretch" ] ; then
/lib/opendkim/opendkim.service.generate
# Without adding '-u opendkim' after the service file is generated, opendkim
# will run as root, which we do not want.
if [ "$(grep -c 'u opendkim' /etc/systemd/system/opendkim.service.d/override.conf)" == 0 ] ; then
sed -i -e 's/inet:8891@127.0.0.1/& -u opendkim/' /etc/systemd/system/opendkim.service.d/override.conf
fi
systemctl daemon-reload
fi
# Add opendkim to service to restart
SERVICES="$SERVICES opendkim bind9"

View File

@ -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,16 +479,11 @@ 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, has_https_option) values
INSERT IGNORE INTO `domaines_type` (name, description, target, entry, compatibility, only_dns, need_dns, advanced, enable,has_https_option) VALUES
-- Default vhost type to maintains compatibility across versions.
-- This is overloaded depending on the value of the https column in sub_domaines
('vhost', 'Locally hosted', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'ALL', true),
-- The following 3 types (vhost-http, vhost-https, vhost-both) are overloads for vhost
-- and are "disabled" to not be available from the interface, but still be valid domaine types
-- when checking in m_ssl::updateDomain.
('vhost-http','Locally hosted with http->https', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'NONE', false),
('vhost-https','Locally hosted with http->https', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'NONE', false),
('vhost-both', 'Locally hosted with http and https', 'DIRECTORY', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, false, false, 'NONE', false),
('dkim', 'DKIM Key', 'TXT', '%SUB% IN TXT "%TARGET%"', 'txt,defmx,defmx2,mx,mx2,url,ip,ipv6', true, true, true, 'ADMIN', false),
('autodiscover', 'Email autoconfiguration', 'NONE', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2,mx,mx2', false, true, true, 'ADMIN', false),
('url', 'URL redirection', 'URL', '%SUB% IN A @@PUBLIC_IP@@', 'txt,defmx,defmx2', false, false, false, 'ALL', false),
('ip', 'IPv4 redirect', 'IP', '%SUB% IN A %TARGET%', 'url,ip,ipv6,txt,mx,mx2,defmx,defmx2', true, true, false, 'ALL', false),
('ipv6', 'IPv6 redirect', 'IPV6', '%SUB% IN AAAA %TARGET%', 'ip,ipv6,txt,mx,mx2,defmx,defmx2', true, true, true, 'ALL', false),
@ -801,7 +796,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.2.php';

View File

@ -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', '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');
-- 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 '';

View File

@ -0,0 +1,33 @@
#!/usr/bin/php -q
<?php
// we don't check our AlternC session
if(!chdir("/usr/share/alternc/panel"))
exit(1);
require("/usr/share/alternc/panel/class/config_nochk.php");
$db->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)
);
}

17
install/variables.php Normal file
View File

@ -0,0 +1,17 @@
#!/usr/bin/php
<?php
/**
* This script set the required variables for AlternC at install time.
* only create missing variable, don't overwrite them
*/
// bootstrap
if(!chdir("/usr/share/alternc/panel"))
exit(1);
require("/usr/share/alternc/panel/class/config_nochk.php");
variable_set('default_spf_value','a mx ?all', 'This variable (if set) tells the SPF/TXT DNS field that will be published as a SPF entry, telling which server(s) are allowed to send email for this one. You can still change it later in the advanced DNS entries for ONE domain, this entry is only set once for new domains (or when you change it here).');
variable_set('default_dmarc_value', 'p=none;pct=100;rua=%%ADMINMAIL%%;aspf=r;adkim=r', 'This variable (if set) tells the DMARC/TXT DNS field that will be published as a DMARC entry, telling which policy you apply to this domain name. You can still change it later in the advanced DNS entries for ONE domain, this entry is only set once for new domains (or when you change it here). You can use %%ADMINMAIL%% or %%USERMAIL%% to substitute to admin-account or alternc user-account email address.');
// strict dmarc would be 'p=reject;pct=100;rua=%%ADMINMAIL%%;aspf=s;adkim=s'

View File

@ -5,6 +5,8 @@
*/
use PHPUnit\Framework\TestCase;
use PHPUnit\DbUnit\TestCaseTrait;
use PHPUnit\DbUnit\DataSet\YamlDataSet;
use PHPUnit\DbUnit\DataSet\CompositeDataSet;
abstract class AlterncTest extends TestCase
{
@ -40,10 +42,10 @@ abstract class AlterncTest extends TestCase
if( !is_file($file) ){
throw new \Exception("missing $file");
}
$dataSet = new PHPUnit_Extensions_Database_DataSet_YamlDataSet($file);
$dataSet = new YamlDataSet($file);
$datasetList[] = $dataSet;
}
$compositeDataSet = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet($datasetList);
$compositeDataSet = new CompositeDataSet($datasetList);
return $dataSet;
}

View File

@ -174,7 +174,7 @@ class m_actionTest extends AlterncTest {
*/
public function testPurge() {
$result = $this->object->purge();
$this->assertEquals(0, $result);
$this->assertEquals(1, $result);
$expectedTable = $this->loadDataSet("actions-empty.yml")->getTable("actions");
$currentTable = $this->getConnection()->createQueryTable('actions', 'SELECT * FROM actions');
$this->assertTablesEqual($expectedTable, $currentTable);

View File

@ -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

View File

@ -320,6 +320,9 @@ class m_mailTest extends AlterncTest
public function testCreate_alias()
{
// Test #1580
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
/**

View File

@ -22,50 +22,58 @@
# Purpose of file: Fix permission, ACL and ownership of AlternC's files
# ----------------------------------------------------------------------
#
red () { echo -e "\e[31m$@ \e[0m" ; }
usage () {
[[ -n "$@" ]] && red "$@\n"
cat<<End-of-message
Four optionals argument to chose from
-l string : a specific login to fix
-u integer : a specific uid to fix
-f string : a specific file to fix according to a given uid
-d string : a specific subdirectory to fix according to a given uid
# four optionals argument to chose from
# -l string : a specific login to fix
# -u integer : a specific uid to fix
# -f string : a specific file to fix according to a given uid
# -d string : a specific subdirectory to fix according to a given uid
# The u and l switch are used to fix a given user whole directory including his base directory ($ALTERNC_HTML/<letter>/<login>/
# The f and d switch are used to fix a given file or directory under the user's base directory. They use the base directory to get the permissions they should use.
# Be sure to have correct base directory permissions before attemplting to fix use those two switch
The u and l switch are used to fix a given user whole directory including his base directory ($ALTERNC_HTML/<letter>/<login>/
The f and d switch are used to fix a given file or directory under the user's base directory. They use the base directory to get the permissions they should use.
Be sure to have correct base directory permissions before attemplting to fix use those two switch
End-of-message
exit 1
}
query="SELECT uid,login FROM membres ORDER BY login"
sub_dir=""
file=""
LOCK_FIXPERMS="/etc/alternc/disable_all_fixperms"
if [ -f "$LOCK_FIXPERMS" ] ; then
(
echo " ------------- "
echo '/!\ WARNING /!\ '
echo "The fixperms script is disabled"
echo "To enable it, delete $LOCK_FIXPERMS "
echo " ------------- "
) 1>&2
exit 0
usage "
------------------------------------
/!\ WARNING /!\
The fixperms script is disabled
To enable it, delete $LOCK_FIXPERMS
------------------------------------
"
fi
while getopts "l:u:f:d:" optname
while getopts "hl:u:f:d:" optname
do
case "$optname" in
"h") usage
;;
"l")
if [[ "$OPTARG" =~ ^[a-zA-Z0-9_]+$ ]] ; then
query="SELECT uid,login FROM membres WHERE login LIKE '$OPTARG' ORDER BY login"
else
echo "Bad login provided"
exit
usage "Bad login provided"
fi
;;
"u")
if [[ "$OPTARG" =~ ^[0-9]+$ ]] ; then
query="SELECT uid,login FROM membres WHERE uid LIKE '$OPTARG' ORDER BY login"
else
echo "Bad uid provided"
exit
usage "Bad uid provided"
fi
;;
"f")
@ -79,17 +87,14 @@ do
echo $sub_dir
;;
"?")
echo "Unknown option $OPTARG - stop processing"
exit
usage "Unknown option $OPTARG - stop processing"
;;
":")
echo "No argument value for option $OPTARG - stop processing"
exit
usage "No argument value for option $OPTARG - stop processing"
;;
*)
# Should not occur
echo "Unknown error while processing options"
exit
usage "Unknown error while processing options"
;;
esac
done

View File

@ -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
}

View File

@ -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
}

View File

@ -1,85 +0,0 @@
#!/usr/bin/php -q
<?php
/**
*
* Generate Apache configuration for AlternC
*
* To force generation, /launch/generate_apache_conf.php force
*
* Return the number of vhost modified, return 0 if no action
*
**/
require_once("/usr/share/alternc/panel/class/config_nochk.php");
ini_set("display_errors", 1);
/*
FIXME :
- add security check
*/
// Check if we can modify Apache conf
@touch(ALTERNC_VHOST_FILE);
if ( ! is_writable( ALTERNC_VHOST_FILE )) {
die("Error: ".ALTERNC_VHOST_FILE." is not writable\n");
}
// Do we need to regenerate apache conf ?
$db->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;

View File

@ -1,24 +0,0 @@
#!/usr/bin/php -q
<?php
/**
*
* Generate Bind configuration for AlternC
*
* To force generation, /launch/generate_bind_conf.php --force
*
*
**/
require_once("/usr/share/alternc/panel/class/config_nochk.php");
ini_set("display_errors", 1);
$bind = new system_bind();
$force = false;
if (in_array('--force', $argv)) { // Want to force
$force=true;
}
$bind->regenerate_conf($force);

View File

@ -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."

View File

@ -1,35 +1,33 @@
#!/bin/bash
#!/usr/bin/php
<?php
# Rebuild the bind configuration's file
# with the IP of the slave dns
// Rebuild the bind configuration's file
// with the IP of the slave dns
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
FLAGFILE="/run/alternc/refresh_slave";
TPL="/etc/alternc/templates/bind/slaveip.conf"
TARGET="/var/lib/alternc/bind/slaveip.conf"
$FLAGFILE="/run/alternc/refresh_slave";
$TPL="/etc/alternc/templates/bind/slaveip.conf";
$TARGET="/var/lib/alternc/bind/slaveip.conf";
if [ ! -e "$FLAGFILE" ] ; then
# Nothing to do
exit 0
fi
if (!file_exists($FLAGFILE)) exit(0);
# Source some functions
. /usr/lib/alternc/functions.sh
// Bootstrap
require_once("/usr/share/alternc/panel/class/config_nochk.php");
TMP=$(mktemp /tmp/slaveip.conf.XXXX)
$db->query("SELECT ip,class FROM slaveip;");
$str="";
while ($db->next_record()) {
$str.=" ".$db->Record["ip"]."/".$db->Record["class"].";\n";
}
# Get the slave IP. Remove the "newline" caracters
val=$(mysql_query "SELECT concat(ip,'::',class,'; ') FROM slaveip;"|tr '\n' ' ')
file_put_contents($TARGET, str_replace("//AUTO-SLAVES//",$str, file_get_contents($TPL) ) );
# Add the slaves to the templates, re-add the missing "/" separator of subnet
cat "$TPL" | sed -e "s/\/\/AUTO-SLAVES\/\//$val/g" -e "s/::/\//g" > "$TMP"
chown($TARGET,"root");
chgrp($TARGET,"bind");
chmod($TARGET,0640);
# Activate the new configuration
mv "$TMP" "$TARGET"
chown root:bind "$TARGET"
chmod 640 "$TARGET"
putenv("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
passthru("rndc reconfig");
unlink($FLAGFILE);
invoke-rc.d bind9 reload
# Remove FLAGSLAVE file
rm -f "$FLAGFILE"

10
src/update_domains.php Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/php -q
<?php
// bootstrap
require_once("/usr/share/alternc/panel/class/config_nochk.php");
putenv("PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
$dom->update_domains();

View File

@ -1,168 +1,4 @@
#!/bin/bash
# Update domain next-gen by fufroma
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, if(length(sd.https)>0,concat_ws('-',sd.type,sd.https),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, if(length(sd.https)>0,concat_ws('-',sd.type,sd.https),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, if(length(sd.https)>0,concat_ws('-',sd.type,sd.https),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
# This is now done using PHP-only scripting
/usr/lib/alternc/update_domains.php