From 9f0be1d8a84b3077e23ec96921b1bea5815adb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= Date: Thu, 11 Oct 2018 11:59:23 +0200 Subject: [PATCH] [Web] Fix require_once to always include document root [Web] Add system mails (send mails to all mailboxes via LMTP) [Web] Allow to add more administrators [Web] Fix domain administrator editing [Web] Remove some foreign keys [Web] Remove username from API [Web] Remove more .php extension from code [Web] More minor fixes --- data/web/admin.php | 149 +++++++--- data/web/autoconfig.php | 2 +- data/web/autodiscover-json.php | 4 +- data/web/autodiscover.php | 4 +- data/web/css/admin.css | 3 + data/web/debug.php | 4 +- data/web/edit.php | 305 ++++++++++++-------- data/web/inc/functions.admin.inc.php | 276 ++++++++++++++++++ data/web/inc/functions.domain_admin.inc.php | 21 +- data/web/inc/functions.inc.php | 299 +++++++++---------- data/web/inc/functions.mailbox.inc.php | 7 +- data/web/inc/init_db.inc.php | 72 +++-- data/web/inc/prerequisites.inc.php | 1 + data/web/inc/sessions.inc.php | 8 +- data/web/inc/triggers.inc.php | 15 +- data/web/inc/vars.inc.php | 7 +- data/web/index.php | 7 +- data/web/js/admin.js | 52 +++- data/web/js/api.js | 11 +- data/web/js/debug.js | 8 +- data/web/json_api.php | 37 ++- data/web/lang/lang.ca.php | 4 - data/web/lang/lang.de.php | 24 +- data/web/lang/lang.en.php | 30 +- data/web/lang/lang.es.php | 4 - data/web/lang/lang.fr.php | 4 - data/web/lang/lang.it.php | 4 - data/web/lang/lang.lv.php | 4 - data/web/lang/lang.nl.php | 4 - data/web/lang/lang.pl.php | 4 - data/web/lang/lang.pt.php | 4 - data/web/lang/lang.ru.php | 4 - data/web/mailbox.php | 8 +- data/web/mobileconfig.php | 2 +- data/web/modals/admin.php | 46 +++ data/web/quarantine.php | 4 +- data/web/user.php | 8 +- 37 files changed, 1013 insertions(+), 437 deletions(-) create mode 100644 data/web/inc/functions.admin.inc.php diff --git a/data/web/admin.php b/data/web/admin.php index 05ae3174..20dc6faa 100644 --- a/data/web/admin.php +++ b/data/web/admin.php @@ -1,8 +1,8 @@ @@ -10,6 +10,7 @@ $tfa_data = get_tfa();
@@ -17,34 +18,28 @@ $tfa_data = get_tfa();
-
- -
- -
- - ↳ a-z A-Z - _ . -
+
+
+
+
+
+ + + +
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
- - +
+ + + +
:
@@ -68,7 +63,7 @@ $tfa_data = get_tfa();
:
- @@ -79,24 +74,27 @@ $tfa_data = get_tfa(); API (experimental, work in progress) +
- +
- +
@@ -117,7 +115,7 @@ $tfa_data = get_tfa();
-
+
@@ -776,6 +774,88 @@ $tfa_data = get_tfa();
+
+
+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+ +
+ +
+ + +
+
+ + +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { + padding: 3px; +} \ No newline at end of file diff --git a/data/web/debug.php b/data/web/debug.php index d6616dcb..d3d7d794 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -1,8 +1,8 @@ diff --git a/data/web/edit.php b/data/web/edit.php index 31bb5fa3..87d563b0 100644 --- a/data/web/edit.php +++ b/data/web/edit.php @@ -1,11 +1,11 @@
@@ -18,106 +18,35 @@ require_once("inc/header.inc.php"); -

-
-
- -
- -
- -
-
-
- -
- -
- -
-
- -
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- - - -

+ if (isset($_GET["alias"]) && + !empty($_GET["alias"])) { + $alias = html_entity_decode(rawurldecode($_GET["alias"])); + $result = mailbox('get', 'alias_details', $alias); + if (!empty($result)) { + ?> +


-
+
- +
- +
- +
- -
-
-
- -
- -
-
-
- -
- + +
+ +
+
+ +
+
+ +
@@ -129,38 +58,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
-
- -
-
-
-
-
- -
-
-
-
-
-
-

ACL

-
-
-
- -
-
- -
+
@@ -172,6 +70,159 @@ if (isset($_SESSION['mailcow_cc_role'])) { +

+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+

ACL

+
+
+
+ +
+
+ +
+
+
+
+ + + +

+
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ + + 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'];
"); item.chkbox = ''; item.action = '
' + ' ' + lang.edit + '' + @@ -149,11 +185,25 @@ jQuery(function($){ ' Login' + '
'; }); + } 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 = ''; + item.action = ''; + }); } 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, 'acme-client:') + } 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 - all mailboxes 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 - all mailboxes 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";
+ +