@@ -18,106 +18,35 @@ require_once("inc/header.inc.php");
-
=$lang['edit']['alias'];?>
-
-
-
-
=$lang['info']['no_action'];?>
-
-
=$lang['edit']['domain_admin'];?>
+ if (isset($_GET["alias"]) &&
+ !empty($_GET["alias"])) {
+ $alias = html_entity_decode(rawurldecode($_GET["alias"]));
+ $result = mailbox('get', 'alias_details', $alias);
+ if (!empty($result)) {
+ ?>
+
=$lang['edit']['alias'];?>
-
-
@@ -172,6 +70,159 @@ if (isset($_SESSION['mailcow_cc_role'])) {
+
=$lang['edit']['domain_admin'];?>
+
+
+
+
+
=$lang['info']['no_action'];?>
+
+
=$lang['edit']['domain_admin'];?>
+
+
+
+
=$lang['info']['no_action'];?>
+
diff --git a/data/web/inc/functions.admin.inc.php b/data/web/inc/functions.admin.inc.php
new file mode 100644
index 00000000..79c4b9cc
--- /dev/null
+++ b/data/web/inc/functions.admin.inc.php
@@ -0,0 +1,276 @@
+ 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ global $pdo;
+ global $lang;
+ $_data_log = $_data;
+ !isset($_data_log['password']) ?: $_data_log['password'] = '*';
+ !isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
+ switch ($_action) {
+ case 'add':
+ $username = strtolower(trim($_data['username']));
+ $password = $_data['password'];
+ $password2 = $_data['password2'];
+ $active = intval($_data['active']);
+ if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'username_invalid'
+ );
+ return false;
+ }
+
+ $stmt = $pdo->prepare("SELECT `username` FROM `admin`
+ WHERE `username` = :username");
+ $stmt->execute(array(':username' => $username));
+ $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+
+ $stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
+ WHERE `username` = :username");
+ $stmt->execute(array(':username' => $username));
+ $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+
+ foreach ($num_results as $num_results_each) {
+ if ($num_results_each != 0) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('object_exists', htmlspecialchars($username))
+ );
+ return false;
+ }
+ }
+ if (!empty($password) && !empty($password2)) {
+ if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'password_complexity'
+ );
+ return false;
+ }
+ if ($password != $password2) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'password_mismatch'
+ );
+ return false;
+ }
+ $password_hashed = hash_password($password);
+ $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
+ VALUES (:username, :password_hashed, '1', :active)");
+ $stmt->execute(array(
+ ':username' => $username,
+ ':password_hashed' => $password_hashed,
+ ':active' => $active
+ ));
+ }
+ else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'password_empty'
+ );
+ return false;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('admin_added', htmlspecialchars($username))
+ );
+ break;
+ case 'edit':
+ if (!is_array($_data['username'])) {
+ $usernames = array();
+ $usernames[] = $_data['username'];
+ }
+ else {
+ $usernames = $_data['username'];
+ }
+ foreach ($usernames as $username) {
+ $is_now = admin('details', $username);
+ if (!empty($is_now)) {
+ $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
+ $username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
+ }
+ else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ continue;
+ }
+ $password = $_data['password'];
+ $password2 = $_data['password2'];
+ if ($active == 0) {
+ $left_active = 0;
+ foreach (admin('get') as $admin) {
+ $left_active = $left_active + admin('details', $admin)['active_int'];
+ }
+ if ($left_active == 1) {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'no_active_admin'
+ );
+ continue;
+ }
+ }
+ if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('username_invalid', $username_new)
+ );
+ continue;
+ }
+ if ($username_new != $username) {
+ if (!empty(admin('details', $username_new)['username'])) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('username_invalid', $username_new)
+ );
+ continue;
+ }
+ }
+ if (!empty($password) && !empty($password2)) {
+ if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'password_complexity'
+ );
+ continue;
+ }
+ if ($password != $password2) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'password_mismatch'
+ );
+ continue;
+ }
+ $password_hashed = hash_password($password);
+ $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
+ $stmt->execute(array(
+ ':password_hashed' => $password_hashed,
+ ':username_new' => $username_new,
+ ':username' => $username,
+ ':active' => $active
+ ));
+ if (isset($_data['disable_tfa'])) {
+ $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+ $stmt->execute(array(':username' => $username));
+ }
+ else {
+ $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
+ $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
+ }
+ }
+ else {
+ $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
+ $stmt->execute(array(
+ ':username_new' => $username_new,
+ ':username' => $username,
+ ':active' => $active
+ ));
+ if (isset($_data['disable_tfa'])) {
+ $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+ $stmt->execute(array(':username' => $username));
+ }
+ else {
+ $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
+ $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
+ }
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('admin_modified', htmlspecialchars($username))
+ );
+ }
+ return true;
+ break;
+ case 'delete':
+ $usernames = (array)$_data['username'];
+ foreach ($usernames as $username) {
+ if ($_SESSION['mailcow_cc_role'] == $username) {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => 'cannot_delete_self'
+ );
+ continue;
+ }
+ if (empty(admin('details', $username))) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('username_invalid', $username)
+ );
+ continue;
+ }
+ $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
+ $stmt->execute(array(
+ ':username' => $username,
+ ));
+ $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
+ $stmt->execute(array(
+ ':username' => $username,
+ ));
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('admin_removed', htmlspecialchars($username))
+ );
+ }
+ break;
+ case 'get':
+ $admins = array();
+ $stmt = $pdo->query("SELECT `username` FROM `admin` WHERE `superadmin` = '1'");
+ $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ while ($row = array_shift($rows)) {
+ $admins[] = $row['username'];
+ }
+ return $admins;
+ break;
+ case 'details':
+ $admindata = array();
+ $stmt = $pdo->prepare("SELECT
+ `tfa`.`active` AS `tfa_active_int`,
+ CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`,
+ `admin`.`username`,
+ `admin`.`created`,
+ `admin`.`active` AS `active_int`,
+ CASE `admin`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+ FROM `admin`
+ LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`admin`.`username`
+ WHERE `admin`.`username`= :admin AND `superadmin` = '1'");
+ $stmt->execute(array(
+ ':admin' => $_data
+ ));
+ $row = $stmt->fetch(PDO::FETCH_ASSOC);
+ if (empty($row)) {
+ return false;
+ }
+ $admindata['username'] = $row['username'];
+ $admindata['tfa_active'] = $row['tfa_active'];
+ $admindata['active'] = $row['active'];
+ $admindata['tfa_active_int'] = $row['tfa_active_int'];
+ $admindata['active_int'] = $row['active_int'];
+ $admindata['created'] = $row['created'];
+ return $admindata;
+ break;
+ }
+}
diff --git a/data/web/inc/functions.domain_admin.inc.php b/data/web/inc/functions.domain_admin.inc.php
index f71a42a1..bf9900e8 100644
--- a/data/web/inc/functions.domain_admin.inc.php
+++ b/data/web/inc/functions.domain_admin.inc.php
@@ -1,5 +1,4 @@
execute(array(
':username' => $username,
));
+ $stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
+ $stmt->execute(array(
+ ':username_new' => $username_new,
+ ':username' => $username
+ ));
if (!empty($domains)) {
foreach ($domains as $domain) {
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
@@ -277,7 +281,6 @@ function domain_admin($_action, $_data = null) {
WHERE `username` = :user");
$stmt->execute(array(':user' => $username));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
-
if (!verify_hash($row['password'], $password_old)) {
$_SESSION['return'][] = array(
'type' => 'danger',
@@ -286,7 +289,6 @@ function domain_admin($_action, $_data = null) {
);
return false;
}
-
if (!empty($password_new2) && !empty($password_new)) {
if ($password_new2 != $password_new) {
$_SESSION['return'][] = array(
@@ -329,7 +331,7 @@ function domain_admin($_action, $_data = null) {
}
$usernames = (array)$_data['username'];
foreach ($usernames as $username) {
- if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
+ if (empty(domain_admin('details', $username))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
@@ -345,6 +347,10 @@ function domain_admin($_action, $_data = null) {
$stmt->execute(array(
':username' => $username,
));
+ $stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
+ $stmt->execute(array(
+ ':username' => $username,
+ ));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
@@ -362,7 +368,6 @@ function domain_admin($_action, $_data = null) {
);
return false;
}
-
$stmt = $pdo->query("SELECT DISTINCT
`username`
FROM `domain_admins`
@@ -374,23 +379,19 @@ function domain_admin($_action, $_data = null) {
while ($row = array_shift($rows)) {
$domainadmins[] = $row['username'];
}
-
return $domainadmins;
break;
case 'details':
$domainadmindata = array();
-
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
return false;
}
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false;
}
-
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
return false;
}
-
$stmt = $pdo->prepare("SELECT
`tfa`.`active` AS `tfa_active_int`,
CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`,
@@ -413,7 +414,7 @@ function domain_admin($_action, $_data = null) {
$domainadmindata['active'] = $row['active'];
$domainadmindata['tfa_active_int'] = $row['tfa_active_int'];
$domainadmindata['active_int'] = $row['active_int'];
- $domainadmindata['modified'] = $row['created'];
+ $domainadmindata['created'] = $row['created'];
// GET SELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` IN (
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index af93d193..bf265062 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -28,7 +28,99 @@ function flush_memcached() {
// Dunno
}
}
-
+function sys_mail($_data) {
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'access_denied'
+ );
+ return false;
+ }
+ $excludes = $_data['mass_exclude'];
+ $includes = $_data['mass_include'];
+ $mailboxes = array();
+ $mass_from = $_data['mass_from'];
+ $mass_text = $_data['mass_text'];
+ $mass_subject = $_data['mass_subject'];
+ if (!filter_var($mass_from, FILTER_VALIDATE_EMAIL)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'From address must be a valid email address'
+ );
+ return false;
+ }
+ if (empty($mass_subject)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'Subject must not be empty'
+ );
+ return false;
+ }
+ if (empty($mass_text)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'Text must not be empty'
+ );
+ return false;
+ }
+ $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
+ foreach ($domains as $domain) {
+ foreach (mailbox('get', 'mailboxes', $domain) as $mailbox) {
+ $mailboxes[] = $mailbox;
+ }
+ }
+ if (!empty($includes)) {
+ $rcpts = array_intersect($mailboxes, $includes);
+ }
+ elseif (!empty($excludes)) {
+ $rcpts = array_diff($mailboxes, $excludes);
+ }
+ else {
+ $rcpts = $mailboxes;
+ }
+ if (!empty($rcpts)) {
+ ini_set('max_execution_time', 0);
+ ini_set('max_input_time', 0);
+ $mail = new PHPMailer;
+ $mail->Timeout = 10;
+ $mail->SMTPOptions = array(
+ 'ssl' => array(
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ 'allow_self_signed' => true
+ )
+ );
+ $mail->isSMTP();
+ $mail->Host = 'dovecot-mailcow';
+ $mail->SMTPAuth = false;
+ $mail->Port = 24;
+ $mail->setFrom($mass_from);
+ $mail->Subject = $mass_subject;
+ $mail->CharSet ="UTF-8";
+ $mail->Body = $mass_text;
+ $mail->XMailer = 'MooMassMail';
+ foreach ($rcpts as $rcpt) {
+ $mail->AddAddress($rcpt);
+ if (!$mail->send()) {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'Mailer error (RCPT "' . htmlspecialchars($rcpt) . '"): ' . str_replace('https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting', '', $mail->ErrorInfo)
+ );
+ }
+ $mail->ClearAllRecipients();
+ }
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'Mass mail job completed, sent ' . count($rcpts) . ' mails'
+ );
+}
function logger($_data = false) {
/*
logger() will be called as last function
@@ -106,21 +198,35 @@ function hasDomainAccess($username, $role, $domain) {
if (empty($domain) || !is_valid_domain_name($domain)) {
return false;
}
- if ($role != 'admin' && $role != 'domainadmin' && $role != 'user') {
+ if ($role != 'admin' && $role != 'domainadmin') {
return false;
}
- $stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins`
- WHERE (
- `active`='1'
- AND `username` = :username
- AND (`domain` = :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2))
- )
- OR 'admin' = :role");
- $stmt->execute(array(':username' => $username, ':domain1' => $domain, ':domain2' => $domain, ':role' => $role));
- $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
- if (!empty($num_results)) {
- return true;
- }
+ if ($role == 'admin') {
+ $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+ WHERE `domain` = :domain");
+ $stmt->execute(array(':domain' => $domain));
+ $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+ $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
+ WHERE `alias_domain` = :domain");
+ $stmt->execute(array(':domain' => $domain));
+ $num_results = $num_results + count($stmt->fetchAll(PDO::FETCH_ASSOC));
+ if ($num_results != 0) {
+ return true;
+ }
+ }
+ elseif ($role == 'domainadmin') {
+ $stmt = $pdo->prepare("SELECT `domain` FROM `domain_admins`
+ WHERE (
+ `active`='1'
+ AND `username` = :username
+ AND (`domain` = :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2))
+ )");
+ $stmt->execute(array(':username' => $username, ':domain1' => $domain, ':domain2' => $domain));
+ $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+ if (!empty($num_results)) {
+ return true;
+ }
+ }
return false;
}
function hasMailboxObjectAccess($username, $role, $object) {
@@ -318,6 +424,9 @@ function check_login($user, $pass) {
}
else {
unset($_SESSION['ldelay']);
+ // Reactivate TFA if it was set to "deactivate TFA for next login"
+ $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
+ $stmt->execute(array(':user' => $user));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $user, '*'),
@@ -407,110 +516,6 @@ function formatBytes($size, $precision = 2) {
}
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
}
-function edit_admin_account($_data) {
- global $lang;
- global $pdo;
- $_data_log = $_data;
- !isset($_data_log['admin_pass']) ?: $_data_log['admin_pass'] = '*';
- !isset($_data_log['admin_pass2']) ?: $_data_log['admin_pass2'] = '*';
- if ($_SESSION['mailcow_cc_role'] != "admin") {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => 'access_denied'
- );
- return false;
- }
- $username_now = $_SESSION['mailcow_cc_username'];
- $username = $_data['admin_user'];
- $password = $_data['admin_pass'];
- $password2 = $_data['admin_pass2'];
- if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => 'username_invalid'
- );
- return false;
- }
- if (!empty($password) && !empty($password2)) {
- if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => 'password_complexity'
- );
- return false;
- }
- if ($password != $password2) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => 'password_mismatch'
- );
- return false;
- }
- $password_hashed = hash_password($password);
- try {
- $stmt = $pdo->prepare("UPDATE `admin` SET
- `password` = :password_hashed,
- `username` = :username1
- WHERE `username` = :username2");
- $stmt->execute(array(
- ':password_hashed' => $password_hashed,
- ':username1' => $username,
- ':username2' => $username_now
- ));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- return false;
- }
- }
- else {
- try {
- $stmt = $pdo->prepare("UPDATE `admin` SET
- `username` = :username1
- WHERE `username` = :username2");
- $stmt->execute(array(
- ':username1' => $username,
- ':username2' => $username_now
- ));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- return false;
- }
- }
- try {
- $stmt = $pdo->prepare("UPDATE `domain_admins` SET `domain` = 'ALL', `username` = :username1 WHERE `username` = :username2");
- $stmt->execute(array(':username1' => $username, ':username2' => $username_now));
- $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username1 WHERE `username` = :username2");
- $stmt->execute(array(':username1' => $username, ':username2' => $username_now));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- return false;
- }
- $_SESSION['mailcow_cc_username'] = $username;
- $_SESSION['return'][] = array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__, $_data_log),
- 'msg' => 'admin_modified'
- );
-}
function update_sogo_static_view() {
global $pdo;
global $lang;
@@ -1113,6 +1118,11 @@ function admin_api($action, $data = null) {
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
foreach ($allow_from as $key => $val) {
if (!filter_var($val, FILTER_VALIDATE_IP)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__, $data),
+ 'msg' => array('ip_invalid', htmlspecialchars($allow_from[$key]))
+ );
unset($allow_from[$key]);
continue;
}
@@ -1133,16 +1143,24 @@ function admin_api($action, $data = null) {
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3)))
));
- $stmt = $pdo->prepare("INSERT INTO `api` (`username`, `api_key`, `active`, `allow_from`)
- SELECT `username`, :api_key, :active, :allow_from FROM `admin` WHERE `superadmin`='1' AND `active`='1'
- ON DUPLICATE KEY UPDATE `active` = :active_u, `allow_from` = :allow_from_u ;");
- $stmt->execute(array(
- ':api_key' => $api_key,
- ':active' => $active,
- ':active_u' => $active,
- ':allow_from' => $allow_from,
- ':allow_from_u' => $allow_from
- ));
+ $stmt = $pdo->query("SELECT `api_key` FROM `api`");
+ $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+ if (empty($num_results)) {
+ $stmt = $pdo->prepare("INSERT INTO `api` (`api_key`, `active`, `allow_from`)
+ VALUES (:api_key, :active, :allow_from);");
+ $stmt->execute(array(
+ ':api_key' => $api_key,
+ ':active' => $active,
+ ':allow_from' => $allow_from
+ ));
+ }
+ else {
+ $stmt = $pdo->prepare("UPDATE `api` SET `active` = :active, `allow_from` = :allow_from ;");
+ $stmt->execute(array(
+ ':active' => $active,
+ ':allow_from' => $allow_from
+ ));
+ }
break;
case "regen_key":
$api_key = implode('-', array(
@@ -1152,17 +1170,21 @@ function admin_api($action, $data = null) {
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3)))
));
- $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `username` IN
- (SELECT `username` FROM `admin` WHERE `superadmin`='1' AND `active`='1')");
+ $stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key");
$stmt->execute(array(
':api_key' => $api_key
));
break;
+ case "get":
+ $stmt = $pdo->query("SELECT * FROM `api`");
+ $apidata = $stmt->fetch(PDO::FETCH_ASSOC);
+ return $apidata;
+ break;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $data),
- 'msg' => 'admin_modified'
+ 'msg' => 'admin_api_modified'
);
}
function rspamd_ui($action, $data = null) {
@@ -1233,21 +1255,6 @@ function rspamd_ui($action, $data = null) {
break;
}
}
-function get_admin_details() {
- // No parameter to be given, only one admin should exist
- global $pdo;
- global $lang;
- $data = array();
- if ($_SESSION['mailcow_cc_role'] != 'admin') {
- return false;
- }
- $stmt = $pdo->query("SELECT `admin`.`username`, `api`.`active` AS `api_active`, `api`.`api_key`, `api`.`allow_from` FROM `admin`
- LEFT OUTER JOIN `api` ON `admin`.`username` = `api`.`username`
- WHERE `admin`.`superadmin`='1'
- AND `admin`.`active`='1'");
- $data = $stmt->fetch(PDO::FETCH_ASSOC);
- return $data;
-}
function get_u2f_registrations($username) {
global $pdo;
$sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = ? AND `active` = '1'");
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 8332e090..1a4fa3ca 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -2277,7 +2277,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false;
}
elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
- $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
+ $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain");
$stmt->execute(array(
':domain' => $_data,
));
@@ -2535,7 +2535,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false;
}
elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
- $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
+ $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` = :domain");
$stmt->execute(array(
':domain' => $_data,
));
@@ -2680,8 +2680,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
SELECT `domain` from `domain_admins`
WHERE (`active`='1' AND `username` = :username))
)
- OR ('admin'= :role)
- AND `domain` != 'ALL'");
+ OR 'admin'= :role");
$stmt->execute(array(
':username' => $_SESSION['mailcow_cc_username'],
':role' => $_SESSION['mailcow_cc_role'],
diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php
index 8ece2544..f2db0f1a 100644
--- a/data/web/inc/init_db.inc.php
+++ b/data/web/inc/init_db.inc.php
@@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
- $db_version = "03102018_1502";
+ $db_version = "07102018_1502";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -135,7 +135,6 @@ function init_db_schema() {
),
"api" => array(
"cols" => array(
- "username" => "VARCHAR(255) NOT NULL",
"api_key" => "VARCHAR(255) NOT NULL",
"allow_from" => "VARCHAR(512) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
@@ -144,16 +143,8 @@ function init_db_schema() {
),
"keys" => array(
"primary" => array(
- "" => array("username")
+ "" => array("api_key")
),
- "fkey" => array(
- "fk_username_api" => array(
- "col" => "username",
- "ref" => "admin.username",
- "delete" => "CASCADE",
- "update" => "CASCADE"
- )
- )
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
@@ -448,14 +439,6 @@ function init_db_schema() {
"keys" => array(
"primary" => array(
"" => array("username")
- ),
- "fkey" => array(
- "fk_domain_admin_acl" => array(
- "col" => "username",
- "ref" => "domain_admins.username",
- "delete" => "CASCADE",
- "update" => "NO ACTION"
- )
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
@@ -987,22 +970,49 @@ DELIMITER ;';
WHERE `username` = :username");
$stmt->execute(array(':tls_enforce_in' => $tls_options['tls_enforce_in'], ':tls_enforce_out' => $tls_options['tls_enforce_out'], ':username' => $tls_user));
}
- $_SESSION['return'][] = array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__),
- 'msg' => 'db_init_complete'
- );
-
+ if (php_sapi_name() == "cli") {
+ echo "DB initialization completed" . PHP_EOL;
+ } else {
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__),
+ 'msg' => 'db_init_complete'
+ );
+ }
// Fix ACL
$stmt = $pdo->query("INSERT INTO `user_acl` (`username`) SELECT `username` FROM `mailbox` WHERE `kind` = '' AND NOT EXISTS (SELECT `username` FROM `user_acl`);");
$stmt = $pdo->query("INSERT INTO `da_acl` (`username`) SELECT DISTINCT `username` FROM `domain_admins` WHERE `username` != 'admin' AND NOT EXISTS (SELECT `username` FROM `da_acl`);");
+ // Fix domain_admins
+ $stmt = $pdo->query("DELETE FROM `domain_admins` WHERE `domain` = 'ALL';");
}
catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__),
- 'msg' => array('mysql_error', $e)
- );
+ if (php_sapi_name() == "cli") {
+ echo "DB initialization failed: " . print_r($e, true) . PHP_EOL;
+ } else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__),
+ 'msg' => array('mysql_error', $e)
+ );
+ }
}
}
-?>
+if (php_sapi_name() == "cli") {
+ include '/web/inc/vars.inc.php';
+ $now = new DateTime();
+ $mins = $now->getOffset() / 60;
+ $sgn = ($mins < 0 ? -1 : 1);
+ $mins = abs($mins);
+ $hrs = floor($mins / 60);
+ $mins -= $hrs * 60;
+ $offset = sprintf('%+d:%02d', $hrs*$sgn, $mins);
+ $dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
+ $opt = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+ PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = '" . $offset . "', group_concat_max_len = 3423543543;",
+ ];
+ $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+ init_db_schema();
+}
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index 4cb742af..67f72b78 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -139,6 +139,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.address_rewriting.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.admin.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quarantine.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php';
diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php
index a3b81407..ac77e9ae 100644
--- a/data/web/inc/sessions.inc.php
+++ b/data/web/inc/sessions.inc.php
@@ -28,16 +28,16 @@ if (!isset($_SESSION['SESS_REMOTE_UA'])) {
// API
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
- $stmt = $pdo->prepare("SELECT `username`, `allow_from` FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
+ $stmt = $pdo->prepare("SELECT `allow_from` FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
$stmt->execute(array(
- ':api_key' => preg_replace('/[^A-Z0-9-]/i', '', $_SERVER['HTTP_X_API_KEY'])
+ ':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
));
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($api_return['username'])) {
$remote = get_remote_ip(false);
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
if (in_array($remote, $allow_from)) {
- $_SESSION['mailcow_cc_username'] = $api_return['username'];
+ $_SESSION['mailcow_cc_username'] = 'API';
$_SESSION['mailcow_cc_role'] = 'admin';
$_SESSION['mailcow_cc_api'] = true;
}
@@ -84,7 +84,7 @@ if (isset($_POST["logout"])) {
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
unset($_SESSION["dual-login"]);
- header("Location: /mailbox.php");
+ header("Location: /mailbox");
exit();
}
else {
diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php
index d37c0968..d9d87351 100644
--- a/data/web/inc/triggers.inc.php
+++ b/data/web/inc/triggers.inc.php
@@ -6,7 +6,7 @@ if (isset($_POST["verify_tfa_login"])) {
unset($_SESSION['pending_mailcow_cc_username']);
unset($_SESSION['pending_mailcow_cc_role']);
unset($_SESSION['pending_tfa_method']);
- header("Location: /user.php");
+ header("Location: /user");
}
}
@@ -17,19 +17,19 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "admin";
$_SESSION['mailcow_cc_last_login'] = last_login($login_user);
- header("Location: /admin.php");
+ header("Location: /admin");
}
elseif ($as == "domainadmin") {
$_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "domainadmin";
$_SESSION['mailcow_cc_last_login'] = last_login($login_user);
- header("Location: /mailbox.php");
+ header("Location: /mailbox");
}
elseif ($as == "user") {
$_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "user";
$_SESSION['mailcow_cc_last_login'] = last_login($login_user);
- header("Location: /user.php");
+ header("Location: /user");
}
elseif ($as != "pending") {
unset($_SESSION['pending_mailcow_cc_username']);
@@ -49,7 +49,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1")
$_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role'];
$_SESSION['mailcow_cc_username'] = $duallogin;
$_SESSION['mailcow_cc_role'] = "user";
- header("Location: /user.php");
+ header("Location: /user");
}
}
else {
@@ -58,7 +58,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1")
$_SESSION["dual-login"]["role"] = $_SESSION['mailcow_cc_role'];
$_SESSION['mailcow_cc_username'] = $duallogin;
$_SESSION['mailcow_cc_role'] = "domainadmin";
- header("Location: /user.php");
+ header("Location: /user");
}
}
}
@@ -93,5 +93,8 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
if (isset($_POST["rspamd_ui"])) {
rspamd_ui('edit', $_POST);
}
+ if (isset($_POST["mass_send"])) {
+ sys_mail($_POST);
+ }
}
?>
diff --git a/data/web/inc/vars.inc.php b/data/web/inc/vars.inc.php
index 5cf2ea94..9cb4c0da 100644
--- a/data/web/inc/vars.inc.php
+++ b/data/web/inc/vars.inc.php
@@ -87,7 +87,12 @@ $AVAILABLE_LANGUAGES = array('de', 'en', 'es', 'fr', 'lv', 'nl', 'pl', 'pt', 'ru
$DEFAULT_THEME = 'lumen';
// Password complexity as regular expression
-$PASSWD_REGEP = '.{4,}';
+// Min. 6 characters
+//$PASSWD_REGEP = '.{6,}';
+// Min. 6 characters, which must include at least one uppercase letter, one lowercase letter and one number
+// $PASSWD_REGEP = '^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{6,}$';
+// Min. 6 characters, which must include at least one letter and one number
+$PASSWD_REGEP = '^(?=.*[0-9])(?=.*[A-Za-z]).{6,}$';
// Show DKIM private keys - false by default
$SHOW_DKIM_PRIV_KEYS = false;
diff --git a/data/web/index.php b/data/web/index.php
index c839ea86..fcb35de9 100644
--- a/data/web/index.php
+++ b/data/web/index.php
@@ -1,5 +1,5 @@
@@ -109,4 +110,4 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
';
});
+ } else if (table == 'adminstable') {
+ $.each(data, function (i, item) {
+ if (admin_username == item.username) {
+ item.usr = '→ ' + item.username;
+ } else {
+ item.usr = item.username;
+ }
+ item.chkbox = '
';
+ });
}
return data
};
// Initial table drawings
draw_domain_admins();
+ draw_admins();
draw_fwd_hosts();
draw_relayhosts();
// Relayhost
diff --git a/data/web/js/api.js b/data/web/js/api.js
index e8293dbc..1a5f4b06 100644
--- a/data/web/js/api.js
+++ b/data/web/js/api.js
@@ -81,6 +81,11 @@ $(document).ready(function() {
} else {
api_reload_window = true;
}
+ if (typeof $(this).data('api-reload-location') !== 'undefined') {
+ api_reload_location = $(this).data('api-reload-location');
+ } else {
+ api_reload_location = '#';
+ }
// If clicked element #edit_selected is in a form with the same data-id as the button,
// we merge all input fields by {"name":"value"} into api-attr
if ($(this).closest("form").data('id') == id) {
@@ -151,7 +156,11 @@ $(document).ready(function() {
response_obj = JSON.parse(response);
}
if (api_reload_window === true) {
- window.location = window.location.href.split("#")[0];
+ if (api_reload_location != '#') {
+ window.location.replace(api_reload_location)
+ } else {
+ window.location = window.location.href.split("#")[0];
+ }
}
}
});
diff --git a/data/web/js/debug.js b/data/web/js/debug.js
index bf087167..dfd88d86 100644
--- a/data/web/js/debug.js
+++ b/data/web/js/debug.js
@@ -513,7 +513,13 @@ jQuery(function($){
} else if (table == 'general_syslog') {
$.each(data, function (i, item) {
if (item === null) { return true; }
- item.message = escapeHtml(item.message);
+ if (item.message.match("^base64,")) {
+ item.message = atob(item.message.slice(7));
+ item.message = item.message.replace(/(?!^)acme-client:/g, '
acme-client:')
+ item.message = item.message.replace(/acme-client:/g, '
')
+ } else {
+ item.message = escapeHtml(item.message);
+ }
var danger_class = ["emerg", "alert", "crit", "err"];
var warning_class = ["warning", "warn"];
var info_class = ["notice", "info", "debug"];
diff --git a/data/web/json_api.php b/data/web/json_api.php
index d1f6a979..bdef9fc9 100644
--- a/data/web/json_api.php
+++ b/data/web/json_api.php
@@ -144,6 +144,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "domain-admin":
process_add_return(domain_admin('add', $attr));
break;
+ case "admin":
+ process_add_return(admin('add', $attr));
+ break;
case "syncjob":
process_add_return(mailbox('add', 'syncjob', $attr));
break;
@@ -857,6 +860,31 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
break;
}
break;
+ case "admin":
+ switch ($object) {
+ case "all":
+ $admins = admin('get');
+ if (!empty($admins)) {
+ foreach ($admins as $admin) {
+ if ($details = admin('details', $admin)) {
+ $data[] = $details;
+ }
+ else {
+ continue;
+ }
+ }
+ process_get_return($data);
+ }
+ else {
+ echo '{}';
+ }
+ break;
+
+ default:
+ process_get_return(admin('details', $object));
+ break;
+ }
+ break;
case "u2f-registration":
header('Content-Type: application/javascript');
if (($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && $_SESSION["mailcow_cc_username"] == $object) {
@@ -984,6 +1012,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "domain-admin":
process_delete_return(domain_admin('delete', array('username' => $items)));
break;
+ case "admin":
+ process_delete_return(admin('delete', array('username' => $items)));
+ break;
}
break;
case "edit":
@@ -1088,6 +1119,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "domain-admin":
process_edit_return(domain_admin('edit', array_merge(array('username' => $items), $attr)));
break;
+ case "admin":
+ process_edit_return(admin('edit', array_merge(array('username' => $items), $attr)));
+ break;
case "fwdhost":
process_edit_return(fwdhost('edit', array_merge(array('fwdhost' => $items), $attr)));
break;
@@ -1104,9 +1138,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
elseif ($_SESSION['mailcow_cc_role'] == "user") {
process_edit_return(edit_user_account($attr));
}
- elseif ($_SESSION['mailcow_cc_role'] == "admin") {
- process_edit_return(edit_admin_account($attr));
- }
break;
}
break;
diff --git a/data/web/lang/lang.ca.php b/data/web/lang/lang.ca.php
index 32a10dad..91072ecf 100644
--- a/data/web/lang/lang.ca.php
+++ b/data/web/lang/lang.ca.php
@@ -204,8 +204,6 @@ $lang['mailbox']['target_address'] = 'Direcció Goto';
$lang['mailbox']['username'] = "Nom d'usuari";
$lang['mailbox']['fname'] = 'Nom complert';
$lang['mailbox']['filter_table'] = 'Filtrar taula';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'En ús (%)';
$lang['mailbox']['msg_num'] = 'Missatge #';
$lang['mailbox']['remove'] = 'Esborrar';
@@ -406,8 +404,6 @@ $lang['admin']['save'] = 'Desar els canvis';
$lang['admin']['admin'] = 'Administrador';
$lang['admin']['admin_details'] = "Editar detalls de l'administrador";
$lang['admin']['unchanged_if_empty'] = "Si no hi ha canvis, deixa'l en blanc";
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Accés';
$lang['admin']['no_record'] = 'Cap registre';
$lang['admin']['filter_table'] = 'Filtrar taula';
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index 9392f346..a59ad75c 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -72,7 +72,7 @@ $lang['success']['dkim_removed'] = 'DKIM-Key wurde entfernt';
$lang['success']['dkim_added'] = 'DKIM-Key wurde hinzugefügt';
$lang['success']['dkim_duplicated'] = "DKIM-Key der Domain %s wurde auf Domain %s kopiert";
$lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten';
-$lang['danger']['domain_invalid'] = 'Domainname %s ist ungültig';
+$lang['danger']['domain_invalid'] = 'Domainname ist leer oder ungültig';
$lang['danger']['mailbox_quota_exceeds_domain_quota'] = 'Maximale Größe für Mailboxen überschreitet das Domain Speicherlimit';
$lang['danger']['object_is_not_numeric'] = 'Wert %s ist nicht numerisch';
$lang['success']['domain_added'] = 'Domain %s wurde angelegt';
@@ -105,7 +105,9 @@ $lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden ges
$lang['success']['domain_modified'] = 'Änderungen an Domain %s wurden gespeichert';
$lang['success']['domain_admin_modified'] = 'Änderungen an Domain-Administrator %s wurden gespeichert';
$lang['success']['domain_admin_added'] = 'Domain-Administrator %s wurde angelegt';
+$lang['success']['admin_added'] = 'Administrator %s wurde angelegt';
$lang['success']['admin_modified'] = 'Änderungen am Administrator wurden gespeichert';
+$lang['success']['admin_api_modified'] = "Änderungen an API wurden gespeichert";
$lang['danger']['username_invalid'] = 'Benutzername %s kann nicht verwendet werden';
$lang['danger']['password_mismatch'] = 'Passwort-Wiederholung stimmt nicht überein';
$lang['danger']['password_complexity'] = 'Passwort entspricht nicht den Richtlinien';
@@ -129,9 +131,12 @@ $lang['success']['domain_removed'] = 'Domain %s wurde entfernt';
$lang['success']['alias_removed'] = 'Alias-Adresse %s wurde entfernt';
$lang['success']['alias_domain_removed'] = 'Alias-Domain %s wurde entfernt';
$lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfernt';
+$lang['success']['admin_removed'] = 'Administrator %s wurde entfernt';
$lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt';
$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
$lang['success']['resource_removed'] = 'Ressource %s wurde entfernt';
+$lang['warning']['cannot_delete_self'] = 'Kann derzeit eingeloggten Benutzer nicht entfernen';
+$lang['warning']['no_active_admin'] = 'Kann letzten aktiven Administrator nicht deaktivieren';
$lang['danger']['max_quota_in_use'] = 'Mailbox Speicherplatzlimit muss größer oder gleich %d MiB sein';
$lang['danger']['domain_quota_m_in_use'] = 'Domain Speicherplatzlimit muss größer oder gleich %d MiB sein';
$lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein';
@@ -279,8 +284,6 @@ $lang['mailbox']['target_address'] = 'Ziel-Adresse';
$lang['mailbox']['username'] = 'Benutzername';
$lang['mailbox']['fname'] = 'Name';
$lang['mailbox']['filter_table'] = 'Filtern';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'Prozentualer Gebrauch';
$lang['mailbox']['msg_num'] = 'Anzahl Nachrichten';
$lang['mailbox']['remove'] = 'Entfernen';
@@ -505,6 +508,7 @@ $lang['admin']['active'] = 'Aktiv';
$lang['admin']['inactive'] = 'Inaktiv';
$lang['admin']['action'] = 'Aktion';
$lang['admin']['add_domain_admin'] = 'Domain-Administrator hinzufügen';
+$lang['admin']['domain_admin'] = 'Administrator hinzufügen';
$lang['admin']['add_settings_rule'] = 'Rspamd Regel hinzufügen';
$lang['admin']['rsetting_desc'] = 'Kurze Beschreibung';
$lang['admin']['rsetting_content'] = 'Regelinhalt';
@@ -523,8 +527,6 @@ $lang['admin']['save'] = 'Änderungen speichern';
$lang['admin']['admin'] = 'Administrator';
$lang['admin']['admin_details'] = 'Administrator bearbeiten';
$lang['admin']['unchanged_if_empty'] = 'Unverändert, wenn leer';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Zugang';
$lang['admin']['no_record'] = 'Kein Eintrag';
$lang['admin']['filter_table'] = 'Tabelle Filtern';
@@ -687,3 +689,15 @@ $lang['success']['tls_policy_map_entry_saved'] = 'TLS-Richtlinieneintrag "%s" wu
$lang['success']['tls_policy_map_entry_deleted'] = 'TLS-Richtlinie mit der ID %s wurde gelöscht';
$lang['mailbox']['add_tls_policy_map'] = "TLS-Richtlinieneintrag hinzufügen";
$lang['danger']['tls_policy_map_parameter_invalid'] = "Parameter ist ungültig";
+
+$lang['admin']['sys_mails'] = 'System mails';
+$lang['admin']['subject'] = 'Subject';
+$lang['admin']['from'] = 'From';
+$lang['admin']['include_exclude'] = 'Include/Exclude';
+$lang['admin']['include_exclude_info'] = 'By default - with no selection -
are addressed';
+$lang['admin']['excludes'] = 'Excludes these recipients';
+$lang['admin']['includes'] = 'Include these recipients';
+$lang['admin']['text'] = 'Text';
+$lang['admin']['activate_send'] = 'Activate send button';
+
+$lang['warning']['ip_invalid'] = 'Ungültige IP übersprungen: %s';
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index 195bceb2..f3e7cba0 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -76,7 +76,7 @@ $lang['success']['dkim_removed'] = "DKIM key %s has been removed";
$lang['success']['dkim_added'] = "DKIM key %s has been saved";
$lang['success']['dkim_duplicated'] = "DKIM key for domain %s has been copied to %s";
$lang['danger']['access_denied'] = "Access denied or invalid form data";
-$lang['danger']['domain_invalid'] = "Domain name %s is invalid";
+$lang['danger']['domain_invalid'] = "Domain name is empty or invalid";
$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds domain quota limit";
$lang['danger']['object_is_not_numeric'] = "Value %s is not numeric";
$lang['success']['domain_added'] = "Added domain %s";
@@ -108,7 +108,9 @@ $lang['success']['aliasd_modified'] = "Changes to alias domain %s have been save
$lang['success']['domain_modified'] = "Changes to domain %s have been saved";
$lang['success']['domain_admin_modified'] = "Changes to domain administrator %s have been saved";
$lang['success']['domain_admin_added'] = "Domain administrator %s has been added";
+$lang['success']['admin_added'] = "Administrator %s has been added";
$lang['success']['admin_modified'] = "Changes to administrator have been saved";
+$lang['success']['admin_api_modified'] = "Changes to API have been saved";
$lang['danger']['username_invalid'] = "Username %s cannot be used";
$lang['danger']['password_mismatch'] = "Confirmation password does not match";
$lang['danger']['password_complexity'] = "Password does not meet the policy";
@@ -132,9 +134,12 @@ $lang['success']['domain_removed'] = "Domain %s has been removed";
$lang['success']['alias_removed'] = "Alias %s has been removed";
$lang['success']['alias_domain_removed'] = "Alias domain %s has been removed";
$lang['success']['domain_admin_removed'] = "Domain administrator %s has been removed";
+$lang['success']['admin_removed'] = "Administrator %s has been removed";
$lang['success']['mailbox_removed'] = "Mailbox %s has been removed";
$lang['success']['eas_reset'] = "ActiveSync devices for user %s were reset";
$lang['success']['resource_removed'] = "Resource %s has been removed";
+$lang['warning']['cannot_delete_self'] = "Cannot delete logged in user";
+$lang['warning']['no_active_admin'] = "Cannot deactivate last active admin";
$lang['danger']['max_quota_in_use'] = "Mailbox quota must be greater or equal to %d MiB";
$lang['danger']['domain_quota_m_in_use'] = "Domain quota must be greater or equal to %s MiB";
$lang['danger']['mailboxes_in_use'] = "Max. mailboxes must be greater or equal to %d";
@@ -281,8 +286,8 @@ $lang['mailbox']['target_address'] = 'Goto address';
$lang['mailbox']['username'] = 'Username';
$lang['mailbox']['fname'] = 'Full name';
$lang['mailbox']['filter_table'] = 'Filter table';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
+$lang['mailbox']['yes'] = '✓';
+$lang['mailbox']['no'] = '✕';
$lang['mailbox']['in_use'] = 'In use (%)';
$lang['mailbox']['msg_num'] = 'Message #';
$lang['mailbox']['remove'] = 'Remove';
@@ -517,6 +522,7 @@ $lang['admin']['active'] = 'Active';
$lang['admin']['inactive'] = 'Inactive';
$lang['admin']['action'] = 'Action';
$lang['admin']['add_domain_admin'] = 'Add domain administrator';
+$lang['admin']['add_admin'] = 'Add administrator';
$lang['admin']['add_settings_rule'] = 'Add settings rule';
$lang['admin']['rsetting_desc'] = 'Short description';
$lang['admin']['rsetting_content'] = 'Rule content';
@@ -535,8 +541,8 @@ $lang['admin']['save'] = 'Save changes';
$lang['admin']['admin'] = 'Administrator';
$lang['admin']['admin_details'] = 'Edit administrator details';
$lang['admin']['unchanged_if_empty'] = 'If unchanged leave blank';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
+$lang['admin']['yes'] = '✓';
+$lang['admin']['no'] = '✕';
$lang['admin']['access'] = 'Access';
$lang['admin']['no_record'] = 'No record';
$lang['admin']['filter_table'] = 'Filter table';
@@ -571,7 +577,7 @@ $lang['diagnostics']['cname_from_a'] = 'Value derived from A/AAAA record. This i
$lang['admin']['relay_from'] = '"From:" address';
$lang['admin']['relay_run'] = "Run test";
-$lang['admin']['api_allow_from'] = "Allow API access from these IPs";
+$lang['admin']['api_allow_from'] = "Allow API access from these IPs (separated by comma or new line)";
$lang['admin']['api_key'] = "API key";
$lang['admin']['activate_api'] = "Activate API";
$lang['admin']['regen_api_key'] = "Regenerate API key";
@@ -707,3 +713,15 @@ $lang['oauth2']['permit'] = 'Authorize application';
$lang['oauth2']['authorize_app'] = 'Authorize application';
$lang['oauth2']['deny'] = 'Deny';
$lang['oauth2']['access_denied'] = 'Please login as mailbox owner to grant access via OAuth2.';
+
+$lang['admin']['sys_mails'] = 'System mails';
+$lang['admin']['subject'] = 'Subject';
+$lang['admin']['from'] = 'From';
+$lang['admin']['include_exclude'] = 'Include/Exclude';
+$lang['admin']['include_exclude_info'] = 'By default - with no selection -
are addressed';
+$lang['admin']['excludes'] = 'Excludes these recipients';
+$lang['admin']['includes'] = 'Include these recipients';
+$lang['admin']['text'] = 'Text';
+$lang['admin']['activate_send'] = 'Activate send button';
+
+$lang['warning']['ip_invalid'] = 'Skipped invalid IP: %s';
diff --git a/data/web/lang/lang.es.php b/data/web/lang/lang.es.php
index 570cea20..0d0e033b 100644
--- a/data/web/lang/lang.es.php
+++ b/data/web/lang/lang.es.php
@@ -153,8 +153,6 @@ $lang['mailbox']['target_address'] = 'Dirección Goto';
$lang['mailbox']['username'] = 'Nombre de usuario';
$lang['mailbox']['fname'] = 'Nombre completo';
$lang['mailbox']['filter_table'] = 'Filtrar tabla';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'En uso (%)';
$lang['mailbox']['msg_num'] = 'Mensaje #';
$lang['mailbox']['remove'] = 'Eliminar';
@@ -259,7 +257,5 @@ $lang['admin']['save'] = 'Guardar cambios';
$lang['admin']['admin'] = 'Administrador';
$lang['admin']['admin_details'] = 'Editar detalles del administrador';
$lang['admin']['unchanged_if_empty'] = 'Si no hay cambios dejalo en blanco';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Acceso';
$lang['admin']['no_record'] = 'Sin registro';
diff --git a/data/web/lang/lang.fr.php b/data/web/lang/lang.fr.php
index a67a447f..6d40a02e 100644
--- a/data/web/lang/lang.fr.php
+++ b/data/web/lang/lang.fr.php
@@ -188,8 +188,6 @@ $lang['mailbox']['target_address'] = "Adresse cible";
$lang['mailbox']['username'] = "Identifiant";
$lang['mailbox']['fname'] = "Nom complet";
$lang['mailbox']['filter_table'] = "Table de filtrage";
-$lang['mailbox']['yes'] = "✔";
-$lang['mailbox']['no'] = "✘";
$lang['mailbox']['in_use'] = "Utilisation (%)";
$lang['mailbox']['msg_num'] = "Message";
$lang['mailbox']['remove'] = "Retirer";
@@ -358,8 +356,6 @@ $lang['admin']['remove'] = "Retirer";
$lang['admin']['admin'] = "Administrateur";
$lang['admin']['admin_details'] = "Éditer les informations de l'administrateur";
$lang['admin']['unchanged_if_empty'] = "Si aucun changement, laisser vide";
-$lang['admin']['yes'] = "✔";
-$lang['admin']['no'] = "✘";
$lang['admin']['access'] = "Accès";
$lang['admin']['no_record'] = "Aucun enregistrement";
$lang['admin']['filter_table'] = "Table de filtrage";
diff --git a/data/web/lang/lang.it.php b/data/web/lang/lang.it.php
index da355609..b79fcb59 100644
--- a/data/web/lang/lang.it.php
+++ b/data/web/lang/lang.it.php
@@ -191,8 +191,6 @@ $lang['mailbox']['target_address'] = 'Vai ad indirizzo';
$lang['mailbox']['username'] = 'Nome utente';
$lang['mailbox']['fname'] = 'Nome completo';
$lang['mailbox']['filter_table'] = 'Filra tabella';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'In uso (%)';
$lang['mailbox']['msg_num'] = 'Messaggio #';
$lang['mailbox']['remove'] = 'Rimuovi';
@@ -348,8 +346,6 @@ $lang['admin']['save'] = 'Salva modifiche';
$lang['admin']['admin'] = 'Amministratore';
$lang['admin']['admin_details'] = 'Modifica impostazioni amministratore';
$lang['admin']['unchanged_if_empty'] = 'Se immutato lasciare vuoto';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Accedi';
$lang['admin']['no_record'] = 'Nessun risultato';
$lang['admin']['filter_table'] = 'Tabella filtro';
diff --git a/data/web/lang/lang.lv.php b/data/web/lang/lang.lv.php
index f612607b..2fe3290e 100644
--- a/data/web/lang/lang.lv.php
+++ b/data/web/lang/lang.lv.php
@@ -206,8 +206,6 @@ $lang['mailbox']['target_address'] = 'Doties uz adresi';
$lang['mailbox']['username'] = 'Lietotājvārds';
$lang['mailbox']['fname'] = 'Pilns vārds';
$lang['mailbox']['filter_table'] = 'Filtra tabula';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'Lietošanā (%)';
$lang['mailbox']['msg_num'] = 'Vēstule #';
$lang['mailbox']['remove'] = 'Noņemt';
@@ -404,8 +402,6 @@ $lang['admin']['save'] = 'Saglabāt izmaiņas';
$lang['admin']['admin'] = 'Administrators';
$lang['admin']['admin_details'] = 'Labot administratora detaļas';
$lang['admin']['unchanged_if_empty'] = 'Ja nav veiktas izmaiņas, atstājiet tukšu';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Pieeja';
$lang['admin']['no_record'] = 'Nav ierakstu';
$lang['admin']['filter_table'] = 'Filtru tabula';
diff --git a/data/web/lang/lang.nl.php b/data/web/lang/lang.nl.php
index 3f87089b..deaba225 100644
--- a/data/web/lang/lang.nl.php
+++ b/data/web/lang/lang.nl.php
@@ -260,8 +260,6 @@ $lang['mailbox']['target_address'] = 'Doeladres';
$lang['mailbox']['username'] = 'Gebruikersnaam';
$lang['mailbox']['fname'] = 'Volledige naam';
$lang['mailbox']['filter_table'] = 'Filtertabel';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'In gebruik (%)';
$lang['mailbox']['msg_num'] = 'Bericht #';
$lang['mailbox']['remove'] = 'Verwijder';
@@ -485,8 +483,6 @@ $lang['admin']['save'] = 'Sla wijzigingen op';
$lang['admin']['admin'] = 'Beheerder';
$lang['admin']['admin_details'] = 'Wijzig beheerderdetails';
$lang['admin']['unchanged_if_empty'] = 'Laat leeg wanneer onveranderd';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Toegang';
$lang['admin']['no_record'] = 'Geen vermelding';
$lang['admin']['filter_table'] = 'Filtertabel';
diff --git a/data/web/lang/lang.pl.php b/data/web/lang/lang.pl.php
index adc4edab..140b0c32 100644
--- a/data/web/lang/lang.pl.php
+++ b/data/web/lang/lang.pl.php
@@ -193,8 +193,6 @@ $lang['mailbox']['target_address'] = 'Adres Idź do';
$lang['mailbox']['username'] = 'Nazwa użytkownika';
$lang['mailbox']['fname'] = 'Pełna nazwa';
$lang['mailbox']['filter_table'] = 'Tabela filtru';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'W użyciu (%)';
$lang['mailbox']['msg_num'] = 'Wiadomość #';
$lang['mailbox']['remove'] = 'Usuń';
@@ -360,8 +358,6 @@ $lang['admin']['save'] = 'Zapisz zmiany';
$lang['admin']['admin'] = 'Administrator';
$lang['admin']['admin_details'] = 'Edytuj szczegóły administratora';
$lang['admin']['unchanged_if_empty'] = 'W przypadku braku zmian, nie wypełniaj';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Dostęp';
$lang['admin']['no_record'] = 'Brak rekordu';
$lang['admin']['filter_table'] = 'Tabela filtru';
diff --git a/data/web/lang/lang.pt.php b/data/web/lang/lang.pt.php
index 13a86300..d6fc8649 100644
--- a/data/web/lang/lang.pt.php
+++ b/data/web/lang/lang.pt.php
@@ -140,8 +140,6 @@ $lang['mailbox']['target_address'] = 'Encaminhar para';
$lang['mailbox']['username'] = 'Usuário';
$lang['mailbox']['fname'] = 'Nome';
$lang['mailbox']['filter_table'] = 'Procurar';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'Em uso (%)';
$lang['mailbox']['msg_num'] = 'Mensagens';
$lang['mailbox']['remove'] = 'Remover';
@@ -239,7 +237,5 @@ $lang['admin']['save'] = 'Salvar';
$lang['admin']['admin'] = 'Administrador';
$lang['admin']['admin_details'] = 'Editar informações do administrator';
$lang['admin']['unchanged_if_empty'] = 'Deixar em branco para não alterar';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Acessos';
$lang['admin']['no_record'] = 'Nenhum registro';
diff --git a/data/web/lang/lang.ru.php b/data/web/lang/lang.ru.php
index fa2074f6..10a2b30c 100644
--- a/data/web/lang/lang.ru.php
+++ b/data/web/lang/lang.ru.php
@@ -189,8 +189,6 @@ $lang['mailbox']['target_address'] = 'Основной адрес';
$lang['mailbox']['username'] = 'Имя пользователя';
$lang['mailbox']['fname'] = 'Полное имя';
$lang['mailbox']['filter_table'] = 'Поиск';
-$lang['mailbox']['yes'] = '✔';
-$lang['mailbox']['no'] = '✘';
$lang['mailbox']['in_use'] = 'Использовано (%)';
$lang['mailbox']['msg_num'] = 'Письма #';
$lang['mailbox']['remove'] = 'Удалить';
@@ -359,8 +357,6 @@ $lang['admin']['save'] = 'Сохранить изменения';
$lang['admin']['admin'] = 'Администратор';
$lang['admin']['admin_details'] = 'Изменить данные администратора';
$lang['admin']['unchanged_if_empty'] = 'Если без изменений оставьте пустым';
-$lang['admin']['yes'] = '✔';
-$lang['admin']['no'] = '✘';
$lang['admin']['access'] = 'Доступ к';
$lang['admin']['no_record'] = 'Нет записей';
$lang['admin']['filter_table'] = 'Поиск';
diff --git a/data/web/mailbox.php b/data/web/mailbox.php
index 837de654..0accf20e 100644
--- a/data/web/mailbox.php
+++ b/data/web/mailbox.php
@@ -1,8 +1,7 @@
@@ -340,7 +339,8 @@ echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";