Merge pull request #5040 from mailcow/staging

2023-02
This commit is contained in:
Patrick Schult 2023-02-02 15:31:34 +01:00 committed by GitHub
commit df8775d4c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 5295 additions and 5111 deletions

View File

@ -26,7 +26,7 @@ jobs:
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }} password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v3 uses: docker/build-push-action@v4
with: with:
context: . context: .
file: data/Dockerfiles/backup/Dockerfile file: data/Dockerfiles/backup/Dockerfile

View File

@ -1,20 +0,0 @@
name: "Tweet trigger release"
on:
release:
types: [published]
jobs:
tweet:
runs-on: ubuntu-latest
steps:
- name: "Get Release Tag"
run: |
RELEASE_TAG=$(curl https://api.github.com/repos/mailcow/mailcow-dockerized/releases/latest | jq -r '.tag_name')
- name: Tweet-trigger-publish-release
uses: mugi111/tweet-trigger-release@v1.2
with:
consumer_key: ${{ secrets.CONSUMER_KEY }}
consumer_secret: ${{ secrets.CONSUMER_SECRET }}
access_token_key: ${{ secrets.ACCESS_TOKEN_KEY }}
access_token_secret: ${{ secrets.ACCESS_TOKEN_SECRET }}
tweet_body: 'A new mailcow update has just been released! Checkout the GitHub Page for changelog and more informations: https://github.com/mailcow/mailcow-dockerized/releases/latest'

View File

@ -699,6 +699,38 @@ paths:
type: string type: string
type: object type: object
summary: Create Domain Admin user summary: Create Domain Admin user
/api/v1/add/sso/domain-admin:
post:
responses:
"401":
$ref: "#/components/responses/Unauthorized"
"200":
content:
application/json:
examples:
response:
value:
token: "591F6D-5C3DD2-7455CD-DAF1C1-AA4FCC"
description: OK
headers: { }
tags:
- Single Sign-On
description: >-
Using this endpoint you can issue a token for Domain Admin user. This token can be used for
autologin Domain Admin user by using query_string var sso_token={token}. Token expiration time is 30s
operationId: Issue Domain Admin SSO token
requestBody:
content:
application/json:
schema:
example:
username: testadmin
properties:
username:
description: the username for the admin user
type: object
type: object
summary: Issue Domain Admin SSO token
/api/v1/edit/da-acl: /api/v1/edit/da-acl:
post: post:
responses: responses:
@ -1999,7 +2031,7 @@ paths:
- domain.tld - domain.tld
- domain2.tld - domain2.tld
properties: properties:
items: items:
type: array type: array
items: items:
type: string type: string
@ -2993,7 +3025,7 @@ paths:
application/json: application/json:
schema: schema:
type: array type: array
items: items:
type: object type: object
properties: properties:
log: log:
@ -5586,6 +5618,8 @@ tags:
description: Manage DKIM keys description: Manage DKIM keys
- name: Domain admin - name: Domain admin
description: Create or udpdate domain admin users description: Create or udpdate domain admin users
- name: Single Sign-On
description: Issue tokens for users
- name: Address Rewriting - name: Address Rewriting
description: Create BCC maps or recipient maps description: Create BCC maps or recipient maps
- name: Outgoing TLS Policy Map Overrides - name: Outgoing TLS Policy Map Overrides

View File

@ -77,4 +77,22 @@ li .dtr-data {
table.dataTable>tbody>tr.child span.dtr-title { table.dataTable>tbody>tr.child span.dtr-title {
width: 30%; width: 30%;
max-width: 250px; max-width: 250px;
} }
div.dataTables_wrapper div.dataTables_filter {
text-align: left;
}
div.dataTables_wrapper div.dataTables_length {
text-align: right;
}
.dataTables_paginate, .dataTables_length, .dataTables_filter {
margin: 10px 0!important;
}
td.dt-text-right {
text-align: end !important;
}
th.dt-text-right {
text-align: end !important;
}

View File

@ -370,14 +370,3 @@ button[aria-expanded='true'] > .caret {
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { .btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #f0f0f0 !important; background-color: #f0f0f0 !important;
} }
div.dataTables_wrapper div.dataTables_filter {
text-align: left;
}
div.dataTables_wrapper div.dataTables_length {
text-align: right;
}
.dataTables_paginate, .dataTables_length, .dataTables_filter {
margin: 10px 0!important;
}

View File

@ -203,6 +203,9 @@
text-align: left; text-align: left;
} }
.senders-mw220 {
max-width: 100% !important;
}
} }
@media (max-width: 350px) { @media (max-width: 350px) {

View File

@ -1,102 +1,104 @@
.pagination a { .pagination a {
text-decoration: none !important; text-decoration: none !important;
} }
.panel.panel-default { .panel.panel-default {
overflow: visible !important; overflow: visible !important;
} }
.table-responsive { .table-responsive {
overflow: visible !important; overflow: visible !important;
} }
.table-responsive { .table-responsive {
overflow-x: scroll !important; overflow-x: scroll !important;
} }
.footer-add-item { .footer-add-item {
display: block; display: block;
text-align: center; text-align: center;
font-style: italic; font-style: italic;
padding: 10px; padding: 10px;
background: #F5F5F5; background: #F5F5F5;
} }
@media (min-width: 992px) { @media (min-width: 992px) {
.container { .container {
width: 100%; width: 100%;
} }
} }
@media (min-width: 1920px) { @media (min-width: 1920px) {
.container { .container {
width: 80%; width: 80%;
} }
} }
.mass-actions-quarantine { .mass-actions-quarantine {
user-select: none; user-select: none;
} }
.inputMissingAttr { .inputMissingAttr {
border-color: #FF4136; border-color: #FF4136;
} }
.modal#qidDetailModal p { .modal#qidDetailModal p {
word-break: break-all; word-break: break-all;
} }
span#qid_detail_score { span#qid_detail_score {
font-weight: 700; font-weight: 700;
margin-left: 5px; margin-left: 5px;
} }
span.rspamd-symbol { span.rspamd-symbol {
display: inline-block; display: inline-block;
margin: 2px 6px 2px 0; margin: 2px 6px 2px 0;
border-radius: 4px; border-radius: 4px;
padding: 0 7px; padding: 0 7px;
} }
span.rspamd-symbol.positive { span.rspamd-symbol.positive {
background: #4CAF50; background: #4CAF50;
border: 1px solid #4CAF50; border: 1px solid #4CAF50;
color: white; color: white;
} }
span.rspamd-symbol.negative { span.rspamd-symbol.negative {
background: #ff4136; background: #ff4136;
border: 1px solid #ff4136; border: 1px solid #ff4136;
color: white; color: white;
} }
span.rspamd-symbol.neutral { span.rspamd-symbol.neutral {
background: #f5f5f5; background: #f5f5f5;
color: #333; color: #333;
border: 1px solid #ccc; border: 1px solid #ccc;
} }
span.rspamd-symbol span.score { span.rspamd-symbol span.score {
font-weight: 700; font-weight: 700;
} }
span.mail-address-item { span.mail-address-item {
background-color: #f5f5f5; background-color: #f5f5f5;
border-radius: 4px; border-radius: 4px;
border: 1px solid #ccc; border: 1px solid #ccc;
padding: 2px 7px; padding: 2px 7px;
display: inline-block; display: inline-block;
margin: 2px 6px 2px 0; margin: 2px 6px 2px 0;
} }
table tbody tr { table tbody tr {
cursor: pointer; cursor: pointer;
} }
table tbody tr td input[type="checkbox"] { table tbody tr td input[type="checkbox"] {
cursor: pointer; cursor: pointer;
} }
.label-rspamd-action { .label-rspamd-action {
font-size:110%; font-size:110%;
margin:20px; margin:20px;
} }
.senders-mw220 {
max-width: 220px;
}

View File

@ -1,407 +1,468 @@
<?php <?php
function domain_admin($_action, $_data = null) { function domain_admin($_action, $_data = null) {
global $pdo; global $pdo;
global $lang; global $lang;
$_data_log = $_data; $_data_log = $_data;
!isset($_data_log['password']) ?: $_data_log['password'] = '*'; !isset($_data_log['password']) ?: $_data_log['password'] = '*';
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*'; !isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
!isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*'; !isset($_data_log['user_old_pass']) ?: $_data_log['user_old_pass'] = '*';
!isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*'; !isset($_data_log['user_new_pass']) ?: $_data_log['user_new_pass'] = '*';
!isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*'; !isset($_data_log['user_new_pass2']) ?: $_data_log['user_new_pass2'] = '*';
switch ($_action) { switch ($_action) {
case 'add': case 'add':
$username = strtolower(trim($_data['username'])); $username = strtolower(trim($_data['username']));
$password = $_data['password']; $password = $_data['password'];
$password2 = $_data['password2']; $password2 = $_data['password2'];
$domains = (array)$_data['domains']; $domains = (array)$_data['domains'];
$active = intval($_data['active']); $active = intval($_data['active']);
if ($_SESSION['mailcow_cc_role'] != "admin") { if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
return false; return false;
} }
if (empty($domains)) { if (empty($domains)) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'domain_invalid' 'msg' => 'domain_invalid'
); );
return false; return false;
} }
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') { if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username) 'msg' => array('username_invalid', $username)
); );
return false; return false;
} }
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` $stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
WHERE `username` = :username"); WHERE `username` = :username");
$stmt->execute(array(':username' => $username)); $stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `admin` $stmt = $pdo->prepare("SELECT `username` FROM `admin`
WHERE `username` = :username"); WHERE `username` = :username");
$stmt->execute(array(':username' => $username)); $stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins` $stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
WHERE `username` = :username"); WHERE `username` = :username");
$stmt->execute(array(':username' => $username)); $stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
foreach ($num_results as $num_results_each) { foreach ($num_results as $num_results_each) {
if ($num_results_each != 0) { if ($num_results_each != 0) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_exists', htmlspecialchars($username)) 'msg' => array('object_exists', htmlspecialchars($username))
); );
return false; return false;
} }
} }
if (password_check($password, $password2) !== true) { if (password_check($password, $password2) !== true) {
continue; continue;
} }
$password_hashed = hash_password($password); $password_hashed = hash_password($password);
$valid_domains = 0; $valid_domains = 0;
foreach ($domains as $domain) { foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) { if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_invalid', htmlspecialchars($domain)) 'msg' => array('domain_invalid', htmlspecialchars($domain))
); );
continue; continue;
} }
$valid_domains++; $valid_domains++;
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username, :domain, :created, :active)"); VALUES (:username, :domain, :created, :active)");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
':domain' => $domain, ':domain' => $domain,
':created' => date('Y-m-d H:i:s'), ':created' => date('Y-m-d H:i:s'),
':active' => $active ':active' => $active
)); ));
} }
if ($valid_domains != 0) { if ($valid_domains != 0) {
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`) $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '0', :active)"); VALUES (:username, :password_hashed, '0', :active)");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
':password_hashed' => $password_hashed, ':password_hashed' => $password_hashed,
':active' => $active ':active' => $active
)); ));
} }
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)"); $stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
$stmt->execute(array( $stmt->execute(array(
':username' => $username ':username' => $username
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_added', htmlspecialchars($username)) 'msg' => array('domain_admin_added', htmlspecialchars($username))
); );
break; break;
case 'edit': case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") { if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
return false; return false;
} }
// Administrator // Administrator
if ($_SESSION['mailcow_cc_role'] == "admin") { if ($_SESSION['mailcow_cc_role'] == "admin") {
if (!is_array($_data['username'])) { if (!is_array($_data['username'])) {
$usernames = array(); $usernames = array();
$usernames[] = $_data['username']; $usernames[] = $_data['username'];
} }
else { else {
$usernames = $_data['username']; $usernames = $_data['username'];
} }
foreach ($usernames as $username) { foreach ($usernames as $username) {
$is_now = domain_admin('details', $username); $is_now = domain_admin('details', $username);
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null; $domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
if (!empty($is_now)) { if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active']; $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains']; $domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username']; $username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
} }
else { else {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
continue; continue;
} }
$password = $_data['password']; $password = $_data['password'];
$password2 = $_data['password2']; $password2 = $_data['password2'];
if (!empty($domains)) { if (!empty($domains)) {
foreach ($domains as $domain) { foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) { if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_invalid', htmlspecialchars($domain)) 'msg' => array('domain_invalid', htmlspecialchars($domain))
); );
continue 2; continue 2;
} }
} }
} }
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) { if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new) 'msg' => array('username_invalid', $username_new)
); );
continue; continue;
} }
if ($username_new != $username) { if ($username_new != $username) {
if (!empty(domain_admin('details', $username_new)['username'])) { if (!empty(domain_admin('details', $username_new)['username'])) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new) 'msg' => array('username_invalid', $username_new)
); );
continue; continue;
} }
} }
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
)); ));
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username_new' => $username_new, ':username_new' => $username_new,
':username' => $username ':username' => $username
)); ));
if (!empty($domains)) { if (!empty($domains)) {
foreach ($domains as $domain) { foreach ($domains as $domain) {
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`) $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username_new, :domain, :created, :active)"); VALUES (:username_new, :domain, :created, :active)");
$stmt->execute(array( $stmt->execute(array(
':username_new' => $username_new, ':username_new' => $username_new,
':domain' => $domain, ':domain' => $domain,
':created' => date('Y-m-d H:i:s'), ':created' => date('Y-m-d H:i:s'),
':active' => $active ':active' => $active
)); ));
} }
} }
if (!empty($password)) { if (!empty($password)) {
if (password_check($password, $password2) !== true) { if (password_check($password, $password2) !== true) {
return false; return false;
} }
$password_hashed = hash_password($password); $password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':password_hashed' => $password_hashed, ':password_hashed' => $password_hashed,
':username_new' => $username_new, ':username_new' => $username_new,
':username' => $username, ':username' => $username,
':active' => $active ':active' => $active
)); ));
if (isset($_data['disable_tfa'])) { if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username)); $stmt->execute(array(':username' => $username));
} }
else { else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username)); $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
} }
} }
else { else {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username_new' => $username_new, ':username_new' => $username_new,
':username' => $username, ':username' => $username,
':active' => $active ':active' => $active
)); ));
if (isset($_data['disable_tfa'])) { if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username)); $stmt->execute(array(':username' => $username));
} }
else { else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username)); $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
} }
} }
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_modified', htmlspecialchars($username)) 'msg' => array('domain_admin_modified', htmlspecialchars($username))
); );
} }
return true; return true;
} }
// Domain administrator // Domain administrator
// Can only edit itself // Can only edit itself
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") { elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
$username = $_SESSION['mailcow_cc_username']; $username = $_SESSION['mailcow_cc_username'];
$password_old = $_data['user_old_pass']; $password_old = $_data['user_old_pass'];
$password_new = $_data['user_new_pass']; $password_new = $_data['user_new_pass'];
$password_new2 = $_data['user_new_pass2']; $password_new2 = $_data['user_new_pass2'];
$stmt = $pdo->prepare("SELECT `password` FROM `admin` $stmt = $pdo->prepare("SELECT `password` FROM `admin`
WHERE `username` = :user"); WHERE `username` = :user");
$stmt->execute(array(':user' => $username)); $stmt->execute(array(':user' => $username));
$row = $stmt->fetch(PDO::FETCH_ASSOC); $row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!verify_hash($row['password'], $password_old)) { if (!verify_hash($row['password'], $password_old)) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
return false; return false;
} }
if (password_check($password_new, $password_new2) !== true) { if (password_check($password_new, $password_new2) !== true) {
return false; return false;
} }
$password_hashed = hash_password($password_new); $password_hashed = hash_password($password_new);
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username"); $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':password_hashed' => $password_hashed, ':password_hashed' => $password_hashed,
':username' => $username ':username' => $username
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_modified', htmlspecialchars($username)) 'msg' => array('domain_admin_modified', htmlspecialchars($username))
); );
} }
break; break;
case 'delete': case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") { if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
return false; return false;
} }
$usernames = (array)$_data['username']; $usernames = (array)$_data['username'];
foreach ($usernames as $username) { foreach ($usernames as $username) {
if (empty(domain_admin('details', $username))) { if (empty(domain_admin('details', $username))) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username) 'msg' => array('username_invalid', $username)
); );
continue; continue;
} }
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username"); $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
)); ));
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username"); $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
)); ));
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username"); $stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
)); ));
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username"); $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
)); ));
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username"); $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
$stmt->execute(array( $stmt->execute(array(
':username' => $username, ':username' => $username,
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_removed', htmlspecialchars($username)) 'msg' => array('domain_admin_removed', htmlspecialchars($username))
); );
} }
break; break;
case 'get': case 'get':
$domainadmins = array(); $domainadmins = array();
if ($_SESSION['mailcow_cc_role'] != "admin") { if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log), 'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied' 'msg' => 'access_denied'
); );
return false; return false;
} }
$stmt = $pdo->query("SELECT DISTINCT $stmt = $pdo->query("SELECT DISTINCT
`username` `username`
FROM `domain_admins` FROM `domain_admins`
WHERE `username` IN ( WHERE `username` IN (
SELECT `username` FROM `admin` SELECT `username` FROM `admin`
WHERE `superadmin`!='1' WHERE `superadmin`!='1'
)"); )");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) { while ($row = array_shift($rows)) {
$domainadmins[] = $row['username']; $domainadmins[] = $row['username'];
} }
return $domainadmins; return $domainadmins;
break; break;
case 'details': case 'details':
$domainadmindata = array(); $domainadmindata = array();
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) { if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
return false; return false;
} }
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) { elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false; return false;
} }
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) { if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
return false; return false;
} }
$stmt = $pdo->prepare("SELECT $stmt = $pdo->prepare("SELECT
`tfa`.`active` AS `tfa_active`, `tfa`.`active` AS `tfa_active`,
`domain_admins`.`username`, `domain_admins`.`username`,
`domain_admins`.`created`, `domain_admins`.`created`,
`domain_admins`.`active` AS `active` `domain_admins`.`active` AS `active`
FROM `domain_admins` FROM `domain_admins`
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username` LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
WHERE `domain_admins`.`username`= :domain_admin"); WHERE `domain_admins`.`username`= :domain_admin");
$stmt->execute(array( $stmt->execute(array(
':domain_admin' => $_data ':domain_admin' => $_data
)); ));
$row = $stmt->fetch(PDO::FETCH_ASSOC); $row = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($row)) { if (empty($row)) {
return false; return false;
} }
$domainadmindata['username'] = $row['username']; $domainadmindata['username'] = $row['username'];
$domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active']; $domainadmindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
$domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active']; $domainadmindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
$domainadmindata['active'] = $row['active']; $domainadmindata['active'] = $row['active'];
$domainadmindata['active_int'] = $row['active']; $domainadmindata['active_int'] = $row['active'];
$domainadmindata['created'] = $row['created']; $domainadmindata['created'] = $row['created'];
// GET SELECTED // GET SELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain` $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` IN ( WHERE `domain` IN (
SELECT `domain` FROM `domain_admins` SELECT `domain` FROM `domain_admins`
WHERE `username`= :domain_admin)"); WHERE `username`= :domain_admin)");
$stmt->execute(array(':domain_admin' => $_data)); $stmt->execute(array(':domain_admin' => $_data));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) { while($row = array_shift($rows)) {
$domainadmindata['selected_domains'][] = $row['domain']; $domainadmindata['selected_domains'][] = $row['domain'];
} }
// GET UNSELECTED // GET UNSELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain` $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` NOT IN ( WHERE `domain` NOT IN (
SELECT `domain` FROM `domain_admins` SELECT `domain` FROM `domain_admins`
WHERE `username`= :domain_admin)"); WHERE `username`= :domain_admin)");
$stmt->execute(array(':domain_admin' => $_data)); $stmt->execute(array(':domain_admin' => $_data));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) { while($row = array_shift($rows)) {
$domainadmindata['unselected_domains'][] = $row['domain']; $domainadmindata['unselected_domains'][] = $row['domain'];
} }
if (!isset($domainadmindata['unselected_domains'])) { if (!isset($domainadmindata['unselected_domains'])) {
$domainadmindata['unselected_domains'] = ""; $domainadmindata['unselected_domains'] = "";
} }
return $domainadmindata; return $domainadmindata;
break; break;
} }
} }
function domain_admin_sso($_action, $_data) {
global $pdo;
switch ($_action) {
case 'check':
$token = $_data;
$stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
$stmt->execute(array(
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
));
$return = $stmt->fetch(PDO::FETCH_ASSOC);
return empty($return['username']) ? false : $return['username'];
case 'issue':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$username = $_data['username'];
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results < 1) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('object_doesnt_exist', htmlspecialchars($username))
);
return false;
}
$token = implode('-', array(
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3)))
));
$stmt = $pdo->prepare("INSERT INTO `da_sso` (`username`, `token`)
VALUES (:username, :token)");
$stmt->execute(array(
':username' => $username,
':token' => $token
));
// perform cleanup
$pdo->query("DELETE FROM `da_sso` WHERE created < DATE_SUB(NOW(), INTERVAL '30' SECOND);");
return ['token' => $token];
break;
}
}

View File

@ -1739,7 +1739,7 @@ function verify_tfa_login($username, $_data) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'), 'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'authenticator not found') 'msg' => array('webauthn_authenticator_failed')
); );
return false; return false;
} }
@ -1748,11 +1748,20 @@ function verify_tfa_login($username, $_data) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'), 'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'publicKey not found') 'msg' => array('webauthn_publickey_failed')
); );
return false; return false;
} }
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_username_failed')
);
return false;
}
try { try {
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']); $WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
} }
@ -1784,21 +1793,12 @@ function verify_tfa_login($username, $_data) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'danger', 'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'), 'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'could not determine user role') 'msg' => array('webauthn_role_failed')
); );
return false; return false;
} }
} }
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'user who requests does not match with sql entry')
);
return false;
}
$_SESSION["mailcow_cc_username"] = $process_webauthn['username']; $_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
$_SESSION['tfa_id'] = $process_webauthn['id']; $_SESSION['tfa_id'] = $process_webauthn['id'];
$_SESSION['authReq'] = null; $_SESSION['authReq'] = null;

View File

@ -5264,7 +5264,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
break; break;
} }
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource'))) { if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'mailbox', 'resource')) && getenv('SKIP_SOGO') != "y") {
update_sogo_static_view(); update_sogo_static_view();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,140 +1,140 @@
<?php <?php
// Start session // Start session
if (session_status() !== PHP_SESSION_ACTIVE) { if (session_status() !== PHP_SESSION_ACTIVE) {
ini_set("session.cookie_httponly", 1); ini_set("session.cookie_httponly", 1);
ini_set('session.gc_maxlifetime', $SESSION_LIFETIME); ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
} }
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") { strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") {
if (session_status() !== PHP_SESSION_ACTIVE) { if (session_status() !== PHP_SESSION_ACTIVE) {
ini_set("session.cookie_secure", 1); ini_set("session.cookie_secure", 1);
} }
$IS_HTTPS = true; $IS_HTTPS = true;
} }
elseif (isset($_SERVER['HTTPS'])) { elseif (isset($_SERVER['HTTPS'])) {
if (session_status() !== PHP_SESSION_ACTIVE) { if (session_status() !== PHP_SESSION_ACTIVE) {
ini_set("session.cookie_secure", 1); ini_set("session.cookie_secure", 1);
} }
$IS_HTTPS = true; $IS_HTTPS = true;
} }
else { else {
$IS_HTTPS = false; $IS_HTTPS = false;
} }
if (session_status() !== PHP_SESSION_ACTIVE) { if (session_status() !== PHP_SESSION_ACTIVE) {
session_start(); session_start();
} }
if (!isset($_SESSION['CSRF']['TOKEN'])) { if (!isset($_SESSION['CSRF']['TOKEN'])) {
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32)); $_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
} }
// Set session UA // Set session UA
if (!isset($_SESSION['SESS_REMOTE_UA'])) { if (!isset($_SESSION['SESS_REMOTE_UA'])) {
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
} }
// Keep session active // Keep session active
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) { if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) {
session_unset(); session_unset();
session_destroy(); session_destroy();
} }
$_SESSION['LAST_ACTIVITY'] = time(); $_SESSION['LAST_ACTIVITY'] = time();
// API // API
if (!empty($_SERVER['HTTP_X_API_KEY'])) { if (!empty($_SERVER['HTTP_X_API_KEY'])) {
$stmt = $pdo->prepare("SELECT * FROM `api` WHERE `api_key` = :api_key AND `active` = '1';"); $stmt = $pdo->prepare("SELECT * FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
$stmt->execute(array( $stmt->execute(array(
':api_key' => preg_replace('/[^a-zA-Z0-9-]/', '', $_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); $api_return = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($api_return['api_key'])) { if (!empty($api_return['api_key'])) {
$skip_ip_check = ($api_return['skip_ip_check'] == 1); $skip_ip_check = ($api_return['skip_ip_check'] == 1);
$remote = get_remote_ip(false); $remote = get_remote_ip(false);
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from'])); $allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $api_return['allow_from']));
if ($skip_ip_check === true || ip_acl($remote, $allow_from)) { if ($skip_ip_check === true || ip_acl($remote, $allow_from)) {
$_SESSION['mailcow_cc_username'] = 'API'; $_SESSION['mailcow_cc_username'] = 'API';
$_SESSION['mailcow_cc_role'] = 'admin'; $_SESSION['mailcow_cc_role'] = 'admin';
$_SESSION['mailcow_cc_api'] = true; $_SESSION['mailcow_cc_api'] = true;
if ($api_return['access'] == 'rw') { if ($api_return['access'] == 'rw') {
$_SESSION['mailcow_cc_api_access'] = 'rw'; $_SESSION['mailcow_cc_api_access'] = 'rw';
} }
else { else {
$_SESSION['mailcow_cc_api_access'] = 'ro'; $_SESSION['mailcow_cc_api_access'] = 'ro';
} }
} }
else { else {
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']); $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']); error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
http_response_code(401); http_response_code(401);
echo json_encode(array( echo json_encode(array(
'type' => 'error', 'type' => 'error',
'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR'] 'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR']
)); ));
unset($_POST); unset($_POST);
exit(); exit();
} }
} }
else { else {
$redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']); $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']);
error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']); error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']);
http_response_code(401); http_response_code(401);
echo json_encode(array( echo json_encode(array(
'type' => 'error', 'type' => 'error',
'msg' => 'authentication failed' 'msg' => 'authentication failed'
)); ));
unset($_POST); unset($_POST);
exit(); exit();
} }
} }
// Handle logouts // Handle logouts
if (isset($_POST["logout"])) { if (isset($_POST["logout"])) {
if (isset($_SESSION["dual-login"])) { if (isset($_SESSION["dual-login"])) {
$_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"]; $_SESSION["mailcow_cc_username"] = $_SESSION["dual-login"]["username"];
$_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"]; $_SESSION["mailcow_cc_role"] = $_SESSION["dual-login"]["role"];
unset($_SESSION["dual-login"]); unset($_SESSION["dual-login"]);
header("Location: /mailbox"); header("Location: /mailbox");
exit(); exit();
} }
else { else {
session_regenerate_id(true); session_regenerate_id(true);
session_unset(); session_unset();
session_destroy(); session_destroy();
session_write_close(); session_write_close();
header("Location: /"); header("Location: /");
} }
} }
// Check session // Check session
function session_check() { function session_check() {
if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) { if (isset($_SESSION['mailcow_cc_api']) && $_SESSION['mailcow_cc_api'] === true) {
return true; return true;
} }
if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) { if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'warning', 'type' => 'warning',
'msg' => 'session_ua' 'msg' => 'session_ua'
); );
return false; return false;
} }
if (!empty($_POST)) { if (!empty($_POST)) {
if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) { if ($_SESSION['CSRF']['TOKEN'] != $_POST['csrf_token']) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'warning', 'type' => 'warning',
'msg' => 'session_token' 'msg' => 'session_token'
); );
return false; return false;
} }
unset($_POST['csrf_token']); unset($_POST['csrf_token']);
$_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32)); $_SESSION['CSRF']['TOKEN'] = bin2hex(random_bytes(32));
$_SESSION['CSRF']['TIME'] = time(); $_SESSION['CSRF']['TIME'] = time();
} }
return true; return true;
} }
if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) { if (isset($_SESSION['mailcow_cc_role']) && session_check() === false) {
$_POST = array(); $_POST = array();
$_FILES = array(); $_FILES = array();
} }

View File

@ -1,4 +1,15 @@
<?php <?php
// SSO Domain Admin
if (!empty($_GET['sso_token'])) {
$username = domain_admin_sso('check', $_GET['sso_token']);
if ($username !== false) {
$_SESSION['mailcow_cc_username'] = $username;
$_SESSION['mailcow_cc_role'] = 'domainadmin';
header('Location: /mailbox');
}
}
if (isset($_POST["verify_tfa_login"])) { if (isset($_POST["verify_tfa_login"])) {
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) { if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username']; $_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
@ -6,7 +17,7 @@ if (isset($_POST["verify_tfa_login"])) {
unset($_SESSION['pending_mailcow_cc_username']); unset($_SESSION['pending_mailcow_cc_username']);
unset($_SESSION['pending_mailcow_cc_role']); unset($_SESSION['pending_mailcow_cc_role']);
unset($_SESSION['pending_tfa_methods']); unset($_SESSION['pending_tfa_methods']);
header("Location: /user"); header("Location: /user");
} else { } else {
unset($_SESSION['pending_mailcow_cc_username']); unset($_SESSION['pending_mailcow_cc_username']);
@ -34,7 +45,7 @@ if (isset($_POST["quick_delete"])) {
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$login_user = strtolower(trim($_POST["login_user"])); $login_user = strtolower(trim($_POST["login_user"]));
$as = check_login($login_user, $_POST["pass_user"]); $as = check_login($login_user, $_POST["pass_user"]);
if ($as == "admin") { if ($as == "admin") {
$_SESSION['mailcow_cc_username'] = $login_user; $_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "admin"; $_SESSION['mailcow_cc_role'] = "admin";

View File

@ -124,7 +124,7 @@ $MAILCOW_APPS = array(
); );
// Rows until pagination begins // Rows until pagination begins
$PAGINATION_SIZE = 20; $PAGINATION_SIZE = 25;
// Default number of rows/lines to display (log table) // Default number of rows/lines to display (log table)
$LOG_LINES = 1000; $LOG_LINES = 1000;

File diff suppressed because it is too large Load Diff

View File

@ -34,7 +34,7 @@ $(document).ready(function() {
}); });
// set update loop container list // set update loop container list
containersToUpdate = {} containersToUpdate = {};
// set default ChartJs Font Color // set default ChartJs Font Color
Chart.defaults.color = '#999'; Chart.defaults.color = '#999';
// create host cpu and mem charts // create host cpu and mem charts
@ -44,14 +44,13 @@ $(document).ready(function() {
check_update(mailcow_info.version_tag, mailcow_info.project_url); check_update(mailcow_info.version_tag, mailcow_info.project_url);
} }
$("#maiclow_version").click(function(){ $("#maiclow_version").click(function(){
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
mailcow_info.branch !== "master")
return; return;
showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag); showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
}) })
// get public ips // get public ips
$("#host_show_ip").click(function(){ $("#host_show_ip").click(function(){
$("#host_show_ip").find(".text").addClass("d-none"); $("#host_show_ip").find(".text").addClass("d-none");
$("#host_show_ip").find(".spinner-border").removeClass("d-none"); $("#host_show_ip").find(".spinner-border").removeClass("d-none");
@ -76,7 +75,7 @@ $(document).ready(function() {
$("#host_ipv6").addClass("d-block"); $("#host_ipv6").addClass("d-block");
}).catch(function(error){ }).catch(function(error){
console.log(error); console.log(error);
$("#host_ipv6").removeClass("d-none"); $("#host_ipv6").removeClass("d-none");
$("#host_ipv6").addClass("d-block"); $("#host_ipv6").addClass("d-block");
$("#host_ipv6").addClass("text-danger"); $("#host_ipv6").addClass("text-danger");
@ -119,10 +118,11 @@ jQuery(function($){
} }
var table = $('#autodiscover_log').DataTable({ var table = $('#autodiscover_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -188,10 +188,11 @@ jQuery(function($){
} }
var table = $('#postfix_log').DataTable({ var table = $('#postfix_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -242,10 +243,11 @@ jQuery(function($){
} }
var table = $('#watchdog_log').DataTable({ var table = $('#watchdog_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -300,10 +302,11 @@ jQuery(function($){
} }
var table = $('#api_log').DataTable({ var table = $('#api_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -352,7 +355,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-api-logs', '#api_log'); hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
}); });
@ -365,10 +368,11 @@ jQuery(function($){
} }
var table = $('#rl_log').DataTable({ var table = $('#rl_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -455,7 +459,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log'); hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
}); });
@ -468,10 +472,11 @@ jQuery(function($){
} }
var table = $('#ui_logs').DataTable({ var table = $('#ui_logs').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -538,7 +543,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log'); hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
}); });
@ -551,10 +556,11 @@ jQuery(function($){
} }
var table = $('#sasl_logs').DataTable({ var table = $('#sasl_logs').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -598,7 +604,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs'); hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
}); });
@ -611,10 +617,11 @@ jQuery(function($){
} }
var table = $('#acme_log').DataTable({ var table = $('#acme_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -647,7 +654,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log'); hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
}); });
@ -660,10 +667,11 @@ jQuery(function($){
} }
var table = $('#netfilter_log').DataTable({ var table = $('#netfilter_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -701,7 +709,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log'); hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
}); });
@ -714,10 +722,11 @@ jQuery(function($){
} }
var table = $('#sogo_log').DataTable({ var table = $('#sogo_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -755,7 +764,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log'); hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
}); });
@ -768,10 +777,11 @@ jQuery(function($){
} }
var table = $('#dovecot_log').DataTable({ var table = $('#dovecot_log').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -883,10 +893,11 @@ jQuery(function($){
} }
var table = $('#rspamd_history').DataTable({ var table = $('#rspamd_history').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
pageLength: log_pagination_size,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"tr" + "tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
@ -983,7 +994,7 @@ jQuery(function($){
} }
] ]
}); });
table.on('responsive-resize', function (e, datatable, columns){ table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history'); hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
}); });
@ -998,31 +1009,31 @@ jQuery(function($){
item.rcpt = escapeHtml(item.rcpt_smtp.join(", ")); item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
} }
item.symbols = Object.keys(item.symbols).sort(function (a, b) { item.symbols = Object.keys(item.symbols).sort(function (a, b) {
if (item.symbols[a].score === 0) return 1 if (item.symbols[a].score === 0) return 1;
if (item.symbols[b].score === 0) return -1 if (item.symbols[b].score === 0) return -1;
if (item.symbols[b].score < 0 && item.symbols[a].score < 0) { if (item.symbols[b].score < 0 && item.symbols[a].score < 0) {
return item.symbols[a].score - item.symbols[b].score return item.symbols[a].score - item.symbols[b].score;
} }
if (item.symbols[b].score > 0 && item.symbols[a].score > 0) { if (item.symbols[b].score > 0 && item.symbols[a].score > 0) {
return item.symbols[b].score - item.symbols[a].score return item.symbols[b].score - item.symbols[a].score;
} }
return item.symbols[b].score - item.symbols[a].score return item.symbols[b].score - item.symbols[a].score;
}).map(function(key) { }).map(function(key) {
var sym = item.symbols[key]; var sym = item.symbols[key];
if (sym.score < 0) { if (sym.score < 0) {
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)' sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)';
} }
else if (sym.score === 0) { else if (sym.score === 0) {
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)' sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)';
} }
else { else {
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)' sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)';
} }
var str = '<strong>' + key + '</strong> ' + sym.score_formatted; var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
if (sym.options) { if (sym.options) {
str += ' [' + escapeHtml(sym.options.join(", ")) + "]"; str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
} }
return str return str;
}).join('<br>\n'); }).join('<br>\n');
item.subject = escapeHtml(item.subject); item.subject = escapeHtml(item.subject);
var scan_time = item.time_real.toFixed(3); var scan_time = item.time_real.toFixed(3);
@ -1155,14 +1166,14 @@ jQuery(function($){
} }
}); });
} }
return data return data;
}; };
$('.add_log_lines').on('click', function (e) { $('.add_log_lines').on('click', function (e) {
e.preventDefault(); e.preventDefault();
var log_table= $(this).data("table") var log_table= $(this).data("table");
var new_nrows = $(this).data("nrows") var new_nrows = $(this).data("nrows");
var post_process = $(this).data("post-process") var post_process = $(this).data("post-process");
var log_url = $(this).data("log-url") var log_url = $(this).data("log-url");
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) { if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
console.log("no data-table or data-nrows or log_url or data-post-process attr found"); console.log("no data-table or data-nrows or log_url or data-post-process attr found");
return; return;
@ -1184,9 +1195,9 @@ jQuery(function($){
}) })
function hideTableExpandCollapseBtn(tab, table){ function hideTableExpandCollapseBtn(tab, table){
if ($(table).hasClass('collapsed')) if ($(table).hasClass('collapsed'))
$(tab).find(".table_collapse_option").show(); $(tab).find(".table_collapse_option").show();
else else
$(tab).find(".table_collapse_option").hide(); $(tab).find(".table_collapse_option").hide();
} }
// detect element visibility changes // detect element visibility changes
@ -1220,7 +1231,6 @@ jQuery(function($){
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph()); onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
// start polling host stats if tab is active // start polling host stats if tab is active
onVisible("[id^=tab-containers]", () => update_stats()); onVisible("[id^=tab-containers]", () => update_stats());
// start polling container stats if collapse is active // start polling container stats if collapse is active
@ -1303,9 +1313,9 @@ function update_stats(timeout=5){
if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift(); if (mem_chart.data.labels.length > 30) mem_chart.data.labels.shift();
cpu_chart.data.datasets[0].data.push(data.cpu.usage); cpu_chart.data.datasets[0].data.push(data.cpu.usage);
if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift(); if (cpu_chart.data.datasets[0].data.length > 30) cpu_chart.data.datasets[0].data.shift();
mem_chart.data.datasets[0].data.push(data.memory.usage); mem_chart.data.datasets[0].data.push(data.memory.usage);
if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift(); if (mem_chart.data.datasets[0].data.length > 30) mem_chart.data.datasets[0].data.shift();
cpu_chart.update(); cpu_chart.update();
mem_chart.update(); mem_chart.update();
@ -1464,23 +1474,23 @@ function createReadWriteChart(chart_id, read_lable, write_lable){
}; };
var optionsNet = { var optionsNet = {
interaction: { interaction: {
mode: 'index' mode: 'index'
}, },
scales: { scales: {
yAxis: { yAxis: {
min: 0, min: 0,
grid: { grid: {
display: false display: false
}, },
ticks: { ticks: {
callback: function(i, index, ticks) { callback: function(i, index, ticks) {
return formatBytes(i); return formatBytes(i);
} }
} }
}, },
xAxis: { xAxis: {
grid: { grid: {
display: false display: false
} }
} }
} }
@ -1528,13 +1538,13 @@ function createHostCpuAndMemChart(){
}; };
var optionsCpu = { var optionsCpu = {
interaction: { interaction: {
mode: 'index' mode: 'index'
}, },
scales: { scales: {
yAxis: { yAxis: {
min: 0, min: 0,
grid: { grid: {
display: false display: false
}, },
ticks: { ticks: {
callback: function(i, index, ticks) { callback: function(i, index, ticks) {
@ -1544,7 +1554,7 @@ function createHostCpuAndMemChart(){
}, },
xAxis: { xAxis: {
grid: { grid: {
display: false display: false
} }
} }
} }
@ -1566,13 +1576,13 @@ function createHostCpuAndMemChart(){
}; };
var optionsMem = { var optionsMem = {
interaction: { interaction: {
mode: 'index' mode: 'index'
}, },
scales: { scales: {
yAxis: { yAxis: {
min: 0, min: 0,
grid: { grid: {
display: false display: false
}, },
ticks: { ticks: {
callback: function(i, index, ticks) { callback: function(i, index, ticks) {
@ -1582,7 +1592,7 @@ function createHostCpuAndMemChart(){
}, },
xAxis: { xAxis: {
grid: { grid: {
display: false display: false
} }
} }
} }
@ -1678,22 +1688,22 @@ function parseGithubMarkdownLinks(inputText) {
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim; replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => { replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
if (matched.includes('github.com')){ if (matched.includes('github.com')){
// return short link if it's github link // return short link if it's github link
last_uri_path = matched.split('/'); last_uri_path = matched.split('/');
last_uri_path = last_uri_path[last_uri_path.length - 1]; last_uri_path = last_uri_path[last_uri_path.length - 1];
// adjust Full Changelog link to match last git version and new git version, if link is a compare link // adjust Full Changelog link to match last git version and new git version, if link is a compare link
if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){ if (matched.includes('/compare/') && mailcow_info.last_version_tag !== ''){
matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag); matched = matched.replace(last_uri_path, mailcow_info.last_version_tag + '...' + mailcow_info.version_tag);
last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag; last_uri_path = mailcow_info.last_version_tag + '...' + mailcow_info.version_tag;
} }
return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>'; return '<a href="' + matched + '" target="_blank">' + last_uri_path + '</a><br>';
}; };
// if it's not a github link, return complete link // if it's not a github link, return complete link
return '<a href="' + matched + '" target="_blank">' + matched + '</a>'; return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
}); });
return replacedText; return replacedText;

View File

@ -1,220 +1,222 @@
$(document).ready(function() { $(document).ready(function() {
$(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); }); $(".arrow-toggle").on('click', function(e) { e.preventDefault(); $(this).find('.arrow').toggleClass("animation"); });
$("#pushover_delete").click(function() { return confirm(lang.delete_ays); }); $("#pushover_delete").click(function() { return confirm(lang.delete_ays); });
$(".goto_checkbox").click(function( event ) { $(".goto_checkbox").click(function( event ) {
$("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false); $("form[data-id='editalias'] .goto_checkbox").not(this).prop('checked', false);
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) { if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
$('#textarea_alias_goto').prop('disabled', true); $('#textarea_alias_goto').prop('disabled', true);
} }
else { else {
$("#textarea_alias_goto").removeAttr('disabled'); $("#textarea_alias_goto").removeAttr('disabled');
} }
}); });
$("#disable_sender_check").click(function( event ) { $("#disable_sender_check").click(function( event ) {
if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) { if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) {
$('#editSelectSenderACL').prop('disabled', true); $('#editSelectSenderACL').prop('disabled', true);
$('#editSelectSenderACL').selectpicker('refresh'); $('#editSelectSenderACL').selectpicker('refresh');
} }
else { else {
$('#editSelectSenderACL').prop('disabled', false); $('#editSelectSenderACL').prop('disabled', false);
$('#editSelectSenderACL').selectpicker('refresh'); $('#editSelectSenderACL').selectpicker('refresh');
} }
}); });
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) { if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
$('#textarea_alias_goto').prop('disabled', true); $('#textarea_alias_goto').prop('disabled', true);
} }
$("#mailbox-password-warning-close").click(function( event ) { $("#mailbox-password-warning-close").click(function( event ) {
$('#mailbox-passwd-hidden-info').addClass('hidden'); $('#mailbox-passwd-hidden-info').addClass('hidden');
$('#mailbox-passwd-form-groups').removeClass('hidden'); $('#mailbox-passwd-form-groups').removeClass('hidden');
}); });
// Sender ACL // Sender ACL
if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){ if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
$("#sender_acl_disabled").show(); $("#sender_acl_disabled").show();
} }
$('#editSelectSenderACL').change(function() { $('#editSelectSenderACL').change(function() {
if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){ if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
$("#sender_acl_disabled").show(); $("#sender_acl_disabled").show();
} }
else { else {
$("#sender_acl_disabled").hide(); $("#sender_acl_disabled").hide();
} }
}); });
// Resources // Resources
if ($("#editSelectMultipleBookings").val() == "custom") { if ($("#editSelectMultipleBookings").val() == "custom") {
$("#multiple_bookings_custom_div").show(); $("#multiple_bookings_custom_div").show();
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val()); $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
} }
$("#editSelectMultipleBookings").change(function() { $("#editSelectMultipleBookings").change(function() {
$('input[name=multiple_bookings]').val($("#editSelectMultipleBookings").val()); $('input[name=multiple_bookings]').val($("#editSelectMultipleBookings").val());
if ($('input[name=multiple_bookings]').val() == "custom") { if ($('input[name=multiple_bookings]').val() == "custom") {
$("#multiple_bookings_custom_div").show(); $("#multiple_bookings_custom_div").show();
} }
else { else {
$("#multiple_bookings_custom_div").hide(); $("#multiple_bookings_custom_div").hide();
} }
}); });
$("#multiple_bookings_custom").bind("change keypress keyup blur", function() { $("#multiple_bookings_custom").bind("change keypress keyup blur", function() {
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val()); $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
}); });
// load tags // load tags
if ($('#tags').length){ if ($('#tags').length){
var tagsEl = $('#tags').parent().find('.tag-values')[0]; var tagsEl = $('#tags').parent().find('.tag-values')[0];
console.log($(tagsEl).val()) console.log($(tagsEl).val())
var tags = JSON.parse($(tagsEl).val()); var tags = JSON.parse($(tagsEl).val());
$(tagsEl).val(""); $(tagsEl).val("");
for (var i = 0; i < tags.length; i++) for (var i = 0; i < tags.length; i++)
addTag($('#tags'), tags[i]); addTag($('#tags'), tags[i]);
} }
}); });
jQuery(function($){ jQuery(function($){
// http://stackoverflow.com/questions/46155/validate-email-address-in-javascript // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
function validateEmail(email) { function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email); return re.test(email);
} }
function draw_wl_policy_domain_table() { function draw_wl_policy_domain_table() {
$('#wl_policy_domain_table').DataTable({ $('#wl_policy_domain_table').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + pageLength: pagination_size,
"tr" + dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", "tr" +
language: lang_datatables, "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
ajax: { language: lang_datatables,
type: "GET", ajax: {
url: '/api/v1/get/policy_wl_domain/' + table_for_domain, type: "GET",
dataSrc: function(data){ url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
$.each(data, function (i, item) { dataSrc: function(data){
if (!validateEmail(item.object)) { $.each(data, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />'; if (!validateEmail(item.object)) {
} item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
else { }
item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />'; else {
} item.chkbox = '<input type="checkbox" disabled title="' + lang_user.spamfilter_table_domain_policy + '" />';
}); }
});
return data;
} return data;
}, }
columns: [ },
{ columns: [
// placeholder, so checkbox will not block child row toggle {
title: '', // placeholder, so checkbox will not block child row toggle
data: null, title: '',
searchable: false, data: null,
orderable: false, searchable: false,
defaultContent: '' orderable: false,
}, defaultContent: ''
{ },
title: '', {
data: 'chkbox', title: '',
searchable: false, data: 'chkbox',
orderable: false, searchable: false,
defaultContent: '' orderable: false,
}, defaultContent: ''
{ },
title: 'ID', {
data: 'prefid', title: 'ID',
defaultContent: '' data: 'prefid',
}, defaultContent: ''
{ },
title: lang_user.spamfilter_table_rule, {
data: 'value', title: lang_user.spamfilter_table_rule,
defaultContent: '' data: 'value',
}, defaultContent: ''
{ },
title: 'Scope', {
data: 'object', title: 'Scope',
defaultContent: '' data: 'object',
} defaultContent: ''
] }
}); ]
} });
function draw_bl_policy_domain_table() { }
$('#bl_policy_domain_table').DataTable({ function draw_bl_policy_domain_table() {
responsive: true, $('#bl_policy_domain_table').DataTable({
processing: true, responsive: true,
serverSide: false, processing: true,
stateSave: true, serverSide: false,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + stateSave: true,
"tr" + pageLength: pagination_size,
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
language: lang_datatables, "tr" +
ajax: { "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
type: "GET", language: lang_datatables,
url: '/api/v1/get/policy_bl_domain/' + table_for_domain, ajax: {
dataSrc: function(data){ type: "GET",
$.each(data, function (i, item) { url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
if (!validateEmail(item.object)) { dataSrc: function(data){
item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />'; $.each(data, function (i, item) {
} if (!validateEmail(item.object)) {
else { item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />'; }
} else {
}); item.chkbox = '<input type="checkbox" disabled tooltip="' + lang_user.spamfilter_table_domain_policy + '" />';
}
return data; });
}
}, return data;
columns: [ }
{ },
// placeholder, so checkbox will not block child row toggle columns: [
title: '', {
data: null, // placeholder, so checkbox will not block child row toggle
searchable: false, title: '',
orderable: false, data: null,
defaultContent: '' searchable: false,
}, orderable: false,
{ defaultContent: ''
title: '', },
data: 'chkbox', {
searchable: false, title: '',
orderable: false, data: 'chkbox',
defaultContent: '' searchable: false,
}, orderable: false,
{ defaultContent: ''
title: 'ID', },
data: 'prefid', {
defaultContent: '' title: 'ID',
}, data: 'prefid',
{ defaultContent: ''
title: lang_user.spamfilter_table_rule, },
data: 'value', {
defaultContent: '' title: lang_user.spamfilter_table_rule,
}, data: 'value',
{ defaultContent: ''
title: 'Scope', },
data: 'object', {
defaultContent: '' title: 'Scope',
} data: 'object',
] defaultContent: ''
}); }
} ]
});
}
// detect element visibility changes
function onVisible(element, callback) {
$(document).ready(function() { // detect element visibility changes
element_object = document.querySelector(element); function onVisible(element, callback) {
if (element_object === null) return; $(document).ready(function() {
element_object = document.querySelector(element);
new IntersectionObserver((entries, observer) => { if (element_object === null) return;
entries.forEach(entry => {
if(entry.intersectionRatio > 0) { new IntersectionObserver((entries, observer) => {
callback(element_object); entries.forEach(entry => {
observer.disconnect(); if(entry.intersectionRatio > 0) {
} callback(element_object);
}); observer.disconnect();
}).observe(element_object); }
}); });
} }).observe(element_object);
// Draw Table if tab is active });
onVisible("[id^=wl_policy_domain_table]", () => draw_wl_policy_domain_table()); }
onVisible("[id^=bl_policy_domain_table]", () => draw_bl_policy_domain_table()); // Draw Table if tab is active
}); onVisible("[id^=wl_policy_domain_table]", () => draw_wl_policy_domain_table());
onVisible("[id^=bl_policy_domain_table]", () => draw_bl_policy_domain_table());
});

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +1,71 @@
jQuery(function($){ jQuery(function($){
var qitem = $('legend').data('hash'); var qitem = $('legend').data('hash');
var qError = $("#qid_error"); var qError = $("#qid_error");
$.ajax({ $.ajax({
url: '/inc/ajax/qitem_details.php', url: '/inc/ajax/qitem_details.php',
data: { hash: qitem }, data: { hash: qitem },
dataType: 'json', dataType: 'json',
success: function(data){ success: function(data){
$('[data-id="qitems_single"]').each(function(index) { $('[data-id="qitems_single"]').each(function(index) {
$(this).attr("data-item", qitem); $(this).attr("data-item", qitem);
}); });
$('#qid_detail_subj').text(data.subject); $('#qid_detail_subj').text(data.subject);
$('#qid_detail_hfrom').text(data.header_from); $('#qid_detail_hfrom').text(data.header_from);
$('#qid_detail_efrom').text(data.env_from); $('#qid_detail_efrom').text(data.env_from);
$('#qid_detail_score').html(''); $('#qid_detail_score').html('');
$('#qid_detail_symbols').html(''); $('#qid_detail_symbols').html('');
$('#qid_detail_recipients').html(''); $('#qid_detail_recipients').html('');
$('#qid_detail_fuzzy').html(''); $('#qid_detail_fuzzy').html('');
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) { if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$.each(data.fuzzy_hashes, function (index, value) { $.each(data.fuzzy_hashes, function (index, value) {
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>'); $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
}); });
} else { } else {
$('#qid_detail_fuzzy').append('-'); $('#qid_detail_fuzzy').append('-');
} }
if (typeof data.symbols !== 'undefined') { if (typeof data.symbols !== 'undefined') {
data.symbols.sort(function (a, b) { data.symbols.sort(function (a, b) {
if (a.score === 0) return 1 if (a.score === 0) return 1;
if (b.score === 0) return -1 if (b.score === 0) return -1;
if (b.score < 0 && a.score < 0) { if (b.score < 0 && a.score < 0) {
return a.score - b.score return a.score - b.score;
} }
if (b.score > 0 && a.score > 0) { if (b.score > 0 && a.score > 0) {
return b.score - a.score return b.score - a.score;
} }
return b.score - a.score return b.score - a.score;
}) })
$.each(data.symbols, function (index, value) { $.each(data.symbols, function (index, value) {
var highlightClass = '' var highlightClass = '';
if (value.score > 0) highlightClass = 'negative' if (value.score > 0) highlightClass = 'negative';
else if (value.score < 0) highlightClass = 'positive' else if (value.score < 0) highlightClass = 'positive';
else highlightClass = 'neutral' else highlightClass = 'neutral';
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>'); $('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
}); });
$('[data-bs-toggle="tooltip"]').tooltip() $('[data-bs-toggle="tooltip"]').tooltip();
} }
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') { if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
if (data.action === "add header") { if (data.action === "add header") {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>'); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
} else if (data.action === "reject") { } else if (data.action === "reject") {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>'); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
} else if (data.action === "rewrite subject") { } else if (data.action === "rewrite subject") {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>'); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
} }
} }
if (typeof data.recipients !== 'undefined') { if (typeof data.recipients !== 'undefined') {
$.each(data.recipients, function(index, value) { $.each(data.recipients, function(index, value) {
var elem = $('<span class="mail-address-item"></span>'); var elem = $('<span class="mail-address-item"></span>');
elem.text(value.address + ' (' + value.type.toUpperCase() + ')'); elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
$('#qid_detail_recipients').append(elem); $('#qid_detail_recipients').append(elem);
}); });
} }
}, },
error: function(data){ error: function(data){
if (typeof data.error !== 'undefined') { if (typeof data.error !== 'undefined') {
qError.text("Error loading quarantine item"); qError.text("Error loading quarantine item");
qError.show(); qError.show();
} }
} }
}); });
}); });

View File

@ -1,286 +1,297 @@
// Base64 functions // Base64 functions
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}}; var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
jQuery(function($){ jQuery(function($){
acl_data = JSON.parse(acl); acl_data = JSON.parse(acl);
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"}; var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})} function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]} function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
$(".refresh_table").on('click', function(e) { $(".refresh_table").on('click', function(e) {
e.preventDefault(); e.preventDefault();
var table_name = $(this).data('table'); var table_name = $(this).data('table');
$('#' + table_name).DataTable().ajax.reload(); $('#' + table_name).DataTable().ajax.reload();
}); });
function draw_quarantine_table() { function draw_quarantine_table() {
var table = $('#quarantinetable').DataTable({ var table = $('#quarantinetable').DataTable({
responsive: true, responsive: true,
processing: true, processing: true,
serverSide: false, serverSide: false,
stateSave: true, stateSave: true,
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + pageLength: pagination_size,
"tr" + order: [[2, 'desc']],
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", lengthMenu: [
language: lang_datatables, [10, 25, 50, 100, -1],
initComplete: function(){ [10, 25, 50, 100, 'all']
hideTableExpandCollapseBtn('#quarantinetable'); ],
}, pagingType: 'first_last_numbers',
ajax: { aColumns: [
type: "GET", { sWidth: '8.25%' },
url: "/api/v1/get/quarantine/all", { sClass: 'classDataTable' }
dataSrc: function(data){ ],
$.each(data, function (i, item) { dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
if (item.subject === null) { "tr" +
item.subject = ''; "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
} else { language: lang_datatables,
item.subject = escapeHtml(item.subject); initComplete: function(){
} hideTableExpandCollapseBtn('#quarantinetable');
if (item.score === null) { },
item.score = '-'; ajax: {
} type: "GET",
if (item.virus_flag > 0) { url: "/api/v1/get/quarantine/all",
item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>'; dataSrc: function(data){
} else { $.each(data, function (i, item) {
item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>'; if (item.subject === null) {
} item.subject = '';
if (item.action === "reject") { } else {
item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>'; item.subject = escapeHtml(item.subject);
} else if (item.action === "add header") { }
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>'; if (item.score === null) {
} else if (item.action === "rewrite subject") { item.score = '-';
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>'; }
} if (item.virus_flag > 0) {
if(item.notified > 0) { item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
item.notified = '&#10004;'; } else {
} else { item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
item.notified = '&#10006;'; }
} if (item.action === "reject") {
if (acl_data.login_as === 1) { item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
item.action = '<div class="btn-group">' + } else if (item.action === "add header") {
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-info show_qid_info"><i class="bi bi-box-arrow-up-right"></i> ' + lang.show_item + '</a>' + item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
'<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' + } else if (item.action === "rewrite subject") {
'</div>'; item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
} }
else { if(item.notified > 0) {
item.action = '<div class="btn-group">' + item.notified = '&#10004;';
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' + } else {
'</div>'; item.notified = '&#10006;';
} }
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />'; if (acl_data.login_as === 1) {
}); item.action = '<div class="btn-group">' +
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-info show_qid_info"><i class="bi bi-box-arrow-up-right"></i> ' + lang.show_item + '</a>' +
return data; '<a href="#" data-action="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-xs-half btn-danger"><i class="bi bi-trash"></i> ' + lang.remove + '</a>' +
} '</div>';
}, }
columns: [ else {
{ item.action = '<div class="btn-group">' +
// placeholder, so checkbox will not block child row toggle '<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><i class="bi bi-file-earmark-text"></i> ' + lang.show_item + '</a>' +
title: '', '</div>';
data: null, }
searchable: false, item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
orderable: false, });
defaultContent: ''
}, return data;
{ }
title: '', },
data: 'chkbox', columns: [
searchable: false, {
orderable: false, // placeholder, so checkbox will not block child row toggle
defaultContent: '' title: '',
}, data: null,
{ searchable: false,
title: 'ID', orderable: false,
data: 'id', defaultContent: ''
defaultContent: '' },
}, {
{ title: '',
title: lang.qid, data: 'chkbox',
data: 'qid', searchable: false,
defaultContent: '' orderable: false,
}, defaultContent: ''
{ },
title: lang.sender, {
data: 'sender', title: 'ID',
defaultContent: '' data: 'id',
}, defaultContent: ''
{ },
title: lang.subj, {
data: 'subject', title: lang.qid,
defaultContent: '' data: 'qid',
}, defaultContent: ''
{ },
title: lang.rspamd_result, {
data: 'rspamdaction', title: lang.sender,
defaultContent: '' data: 'sender',
}, className: 'senders-mw220',
{ defaultContent: ''
title: lang.rcpt, },
data: 'rcpt', {
defaultContent: '' title: lang.subj,
}, data: 'subject',
{ defaultContent: ''
title: lang.danger, },
data: 'virus', {
defaultContent: '' title: lang.rspamd_result,
}, data: 'rspamdaction',
{ defaultContent: ''
title: lang.spam_score, },
data: 'score', {
defaultContent: '' title: lang.rcpt,
}, data: 'rcpt',
{ defaultContent: ''
title: lang.notified, },
data: 'notified', {
defaultContent: '' title: lang.danger,
}, data: 'virus',
{ defaultContent: ''
title: lang.received, },
data: 'created', {
defaultContent: '', title: lang.spam_score,
createdCell: function(td, cellData) { data: 'score',
$(td).attr({ defaultContent: ''
"data-order": cellData, },
"data-sort": cellData {
}); title: lang.notified,
data: 'notified',
var date = new Date(cellData ? cellData * 1000 : 0); defaultContent: ''
var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"}); },
$(td).html(dateString); {
} title: lang.received,
}, data: 'created',
{ defaultContent: '',
title: lang.action, createdCell: function(td, cellData) {
data: 'action', $(td).attr({
className: 'text-md-end dt-sm-head-hidden dt-body-right', "data-order": cellData,
defaultContent: '' "data-sort": cellData
}, });
]
}); var date = new Date(cellData ? cellData * 1000 : 0);
var dateString = date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
table.on('responsive-resize', function (e, datatable, columns){ $(td).html(dateString);
hideTableExpandCollapseBtn('#quarantinetable'); }
}); },
} {
title: lang.action,
$('body').on('click', '.show_qid_info', function (e) { data: 'action',
e.preventDefault(); className: 'dt-text-right dt-sm-head-hidden',
var qitem = $(this).attr('data-item'); defaultContent: ''
var qError = $("#qid_error"); },
]
$('#qidDetailModal').modal('show'); });
qError.hide();
table.on('responsive-resize', function (e, datatable, columns){
$.ajax({ hideTableExpandCollapseBtn('#quarantinetable');
url: '/inc/ajax/qitem_details.php', });
data: { id: qitem }, }
dataType: 'json',
success: function(data){ $('body').on('click', '.show_qid_info', function (e) {
e.preventDefault();
$('[data-id="qitems_single"]').each(function(index) { var qitem = $(this).attr('data-item');
$(this).attr("data-item", qitem); var qError = $("#qid_error");
});
$('#qidDetailModal').modal('show');
$("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')"); qError.hide();
$("#quick_release_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_release', '_blank')");
$("#quick_delete_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_delete', '_blank')"); $.ajax({
url: '/inc/ajax/qitem_details.php',
$('#qid_detail_subj').text(data.subject); data: { id: qitem },
$('#qid_detail_hfrom').text(data.header_from); dataType: 'json',
$('#qid_detail_efrom').text(data.env_from); success: function(data){
$('#qid_detail_score').html('');
$('#qid_detail_recipients').html(''); $('[data-id="qitems_single"]').each(function(index) {
$('#qid_detail_symbols').html(''); $(this).attr("data-item", qitem);
$('#qid_detail_fuzzy').html(''); });
if (typeof data.symbols !== 'undefined') {
data.symbols.sort(function (a, b) { $("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
if (a.score === 0) return 1 $("#quick_release_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_release', '_blank')");
if (b.score === 0) return -1 $("#quick_delete_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&quick_delete', '_blank')");
if (b.score < 0 && a.score < 0) {
return a.score - b.score $('#qid_detail_subj').text(data.subject);
} $('#qid_detail_hfrom').text(data.header_from);
if (b.score > 0 && a.score > 0) { $('#qid_detail_efrom').text(data.env_from);
return b.score - a.score $('#qid_detail_score').html('');
} $('#qid_detail_recipients').html('');
return b.score - a.score $('#qid_detail_symbols').html('');
}) $('#qid_detail_fuzzy').html('');
$.each(data.symbols, function (index, value) { if (typeof data.symbols !== 'undefined') {
var highlightClass = '' data.symbols.sort(function (a, b) {
if (value.score > 0) highlightClass = 'negative' if (a.score === 0) return 1;
else if (value.score < 0) highlightClass = 'positive' if (b.score === 0) return -1;
else highlightClass = 'neutral' if (b.score < 0 && a.score < 0) {
$('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>'); return a.score - b.score;
}); }
$('[data-bs-toggle="tooltip"]').tooltip() if (b.score > 0 && a.score > 0) {
} return b.score - a.score;
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) { }
$.each(data.fuzzy_hashes, function (index, value) { return b.score - a.score;
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>'); })
}); $.each(data.symbols, function (index, value) {
} else { var highlightClass = '';
$('#qid_detail_fuzzy').append('-'); if (value.score > 0) highlightClass = 'negative';
} else if (value.score < 0) highlightClass = 'positive';
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') { else highlightClass = 'neutral';
if (data.action == "add header") { $('#qid_detail_symbols').append('<span data-bs-toggle="tooltip" class="rspamd-symbol ' + highlightClass + '" title="' + (value.options ? value.options.join(', ') : '') + '">' + value.name + ' (<span class="score">' + value.score + '</span>)</span>');
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>'); });
} else if (data.action == "reject") { $('[data-bs-toggle="tooltip"]').tooltip();
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>'); }
} else if (data.action == "rewrite subject") { if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>'); $.each(data.fuzzy_hashes, function (index, value) {
} $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
} });
if (typeof data.recipients !== 'undefined') { } else {
$.each(data.recipients, function(index, value) { $('#qid_detail_fuzzy').append('-');
var elem = $('<span class="mail-address-item"></span>'); }
elem.text(value.address + ' (' + value.type.toUpperCase() + ')'); if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
$('#qid_detail_recipients').append(elem); if (data.action == "add header") {
}); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>');
} } else if (data.action == "reject") {
$('#qid_detail_text').text(data.text_plain); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>');
$('#qid_detail_text_from_html').text(data.text_html); } else if (data.action == "rewrite subject") {
var qAtts = $("#qid_detail_atts"); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>');
if (typeof data.attachments !== 'undefined') { }
qAtts.text(''); }
$.each(data.attachments, function(index, value) { if (typeof data.recipients !== 'undefined') {
qAtts.append( $.each(data.recipients, function(index, value) {
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' + var elem = $('<span class="mail-address-item"></span>');
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>' elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
); $('#qid_detail_recipients').append(elem);
}); });
} }
else { $('#qid_detail_text').text(data.text_plain);
qAtts.text('-'); $('#qid_detail_text_from_html').text(data.text_html);
} var qAtts = $("#qid_detail_atts");
}, if (typeof data.attachments !== 'undefined') {
error: function(data){ qAtts.text('');
if (typeof data.error !== 'undefined') { $.each(data.attachments, function(index, value) {
$('#qid_detail_subj').text('-'); qAtts.append(
$('#qid_detail_hfrom').text('-'); '<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
$('#qid_detail_efrom').text('-'); ' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
$('#qid_detail_score').html('-'); );
$('#qid_detail_recipients').html('-'); });
$('#qid_detail_symbols').html('-'); }
$('#qid_detail_fuzzy').html('-'); else {
$('#qid_detail_text').text('-'); qAtts.text('-');
$('#qid_detail_text_from_html').text('-'); }
qError.text("Error loading quarantine item"); },
qError.show(); error: function(data){
} if (typeof data.error !== 'undefined') {
} $('#qid_detail_subj').text('-');
}); $('#qid_detail_hfrom').text('-');
}); $('#qid_detail_efrom').text('-');
$('#qid_detail_score').html('-');
$('body').on('click', 'span.footable-toggle', function () { $('#qid_detail_recipients').html('-');
event.stopPropagation(); $('#qid_detail_symbols').html('-');
}) $('#qid_detail_fuzzy').html('-');
$('#qid_detail_text').text('-');
// Initial table drawings $('#qid_detail_text_from_html').text('-');
draw_quarantine_table(); qError.text("Error loading quarantine item");
qError.show();
}
function hideTableExpandCollapseBtn(table){ }
if ($(table).hasClass('collapsed')) });
$(".table_collapse_option").show(); });
else
$(".table_collapse_option").hide(); $('body').on('click', 'span.footable-toggle', function () {
} event.stopPropagation();
}); })
// Initial table drawings
draw_quarantine_table();
function hideTableExpandCollapseBtn(table){
if ($(table).hasClass('collapsed'))
$(".table_collapse_option").show();
else
$(".table_collapse_option").hide();
}
});

View File

@ -1,127 +1,128 @@
jQuery(function($){ jQuery(function($){
$(".refresh_table").on('click', function(e) { $(".refresh_table").on('click', function(e) {
e.preventDefault(); e.preventDefault();
var table_name = $(this).data('table'); var table_name = $(this).data('table');
$('#' + table_name).DataTable().ajax.reload(); $('#' + table_name).DataTable().ajax.reload();
}); });
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]} function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
// Queue item // Queue item
$('#showQueuedMsg').on('show.bs.modal', function (e) { $('#showQueuedMsg').on('show.bs.modal', function (e) {
$('#queue_msg_content').text(lang.loading); $('#queue_msg_content').text(lang.loading);
button = $(e.relatedTarget) button = $(e.relatedTarget)
if (button != null) { if (button != null) {
$('#queue_id').text(button.data('queue-id')); $('#queue_id').text(button.data('queue-id'));
}
$.ajax({
type: 'GET',
url: '/api/v1/get/postcat/' + button.data('queue-id'),
dataType: 'text',
complete: function (data) {
$('#queue_msg_content').text(data.responseText);
}
});
})
function draw_queue() {
// just recalc width if instance already exists
if ($.fn.DataTable.isDataTable('#queuetable') ) {
$('#queuetable').DataTable().columns.adjust().responsive.recalc();
return;
} }
$.ajax({
type: 'GET',
url: '/api/v1/get/postcat/' + button.data('queue-id'),
dataType: 'text',
complete: function (data) {
$('#queue_msg_content').text(data.responseText);
}
});
})
$('#queuetable').DataTable({ function draw_queue() {
responsive: true, // just recalc width if instance already exists
processing: true, if ($.fn.DataTable.isDataTable('#queuetable') ) {
serverSide: false, $('#queuetable').DataTable().columns.adjust().responsive.recalc();
stateSave: true, return;
dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" + }
"tr" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>", $('#queuetable').DataTable({
language: lang_datatables, responsive: true,
ajax: { processing: true,
type: "GET", serverSide: false,
url: "/api/v1/get/mailq/all", stateSave: true,
dataSrc: function(data){ pageLength: pagination_size,
$.each(data, function (i, item) { dom: "<'row'<'col-sm-12 col-md-6'f><'col-sm-12 col-md-6'l>>" +
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />'; "tr" +
rcpts = $.map(item.recipients, function(i) { "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
return escapeHtml(i); language: lang_datatables,
}); ajax: {
item.recipients = rcpts.join('<hr style="margin:1px!important">'); type: "GET",
item.action = '<div class="btn-group">' + url: "/api/v1/get/mailq/all",
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' + dataSrc: function(data){
$.each(data, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
rcpts = $.map(item.recipients, function(i) {
return escapeHtml(i);
});
item.recipients = rcpts.join('<hr style="margin:1px!important">');
item.action = '<div class="btn-group">' +
'<a href="#" data-bs-toggle="modal" data-bs-target="#showQueuedMsg" data-queue-id="' + encodeURI(item.queue_id) + '" class="btn btn-xs btn-secondary">' + lang.show_message + '</a>' +
'</div>'; '</div>';
}); });
return data; return data;
} }
}, },
columns: [ columns: [
{ {
// placeholder, so checkbox will not block child row toggle // placeholder, so checkbox will not block child row toggle
title: '', title: '',
data: null, data: null,
searchable: false, searchable: false,
orderable: false, orderable: false,
defaultContent: '' defaultContent: ''
}, },
{ {
title: '', title: '',
data: 'chkbox', data: 'chkbox',
searchable: false, searchable: false,
orderable: false, orderable: false,
defaultContent: '' defaultContent: ''
}, },
{ {
title: 'QID', title: 'QID',
data: 'queue_id', data: 'queue_id',
defaultContent: '' defaultContent: ''
}, },
{ {
title: 'Queue', title: 'Queue',
data: 'queue_name', data: 'queue_name',
defaultContent: '' defaultContent: ''
}, },
{ {
title: lang_admin.arrival_time, title: lang_admin.arrival_time,
data: 'arrival_time', data: 'arrival_time',
defaultContent: '', defaultContent: '',
render: function (data, type){ render: function (data, type){
var date = new Date(data ? data * 1000 : 0); var date = new Date(data ? data * 1000 : 0);
return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"}); return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
} }
}, },
{ {
title: lang_admin.message_size, title: lang_admin.message_size,
data: 'message_size', data: 'message_size',
defaultContent: '', defaultContent: '',
render: function (data, type){ render: function (data, type){
return humanFileSize(data); return humanFileSize(data);
} }
}, },
{ {
title: lang_admin.sender, title: lang_admin.sender,
data: 'sender', data: 'sender',
defaultContent: '' defaultContent: ''
}, },
{ {
title: lang_admin.recipients, title: lang_admin.recipients,
data: 'recipients', data: 'recipients',
defaultContent: '' defaultContent: ''
}, },
{ {
title: lang_admin.action, title: lang_admin.action,
data: 'action', data: 'action',
className: 'text-md-end dt-sm-head-hidden dt-body-right', className: 'dt-sm-head-hidden dt-text-right',
defaultContent: '' defaultContent: ''
}, },
] ]
}); });
} }
draw_queue(); draw_queue();
}) })

File diff suppressed because it is too large Load Diff

View File

@ -288,6 +288,18 @@ if (isset($_GET['query'])) {
case "domain-admin": case "domain-admin":
process_add_return(domain_admin('add', $attr)); process_add_return(domain_admin('add', $attr));
break; break;
case "sso":
switch ($object) {
case "domain-admin":
$data = domain_admin_sso('issue', $attr);
if($data) {
echo json_encode($data);
exit(0);
}
process_add_return($data);
break;
}
break;
case "admin": case "admin":
process_add_return(admin('add', $attr)); process_add_return(admin('add', $attr));
break; break;

View File

@ -339,7 +339,8 @@
"oauth2_add_client": "Füge OAuth2 Client hinzu", "oauth2_add_client": "Füge OAuth2 Client hinzu",
"api_read_only": "Schreibgeschützter Zugriff", "api_read_only": "Schreibgeschützter Zugriff",
"api_read_write": "Lese-Schreib-Zugriff", "api_read_write": "Lese-Schreib-Zugriff",
"oauth2_apps": "OAuth2 Apps" "oauth2_apps": "OAuth2 Apps",
"queue_unban": "entsperren"
}, },
"danger": { "danger": {
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten", "access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
@ -366,7 +367,7 @@
"domain_not_empty": "Domain %s ist nicht leer", "domain_not_empty": "Domain %s ist nicht leer",
"domain_not_found": "Domain %s nicht gefunden", "domain_not_found": "Domain %s nicht gefunden",
"domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein", "domain_quota_m_in_use": "Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein",
"extended_sender_acl_denied": "Keine Rechte zum setzen von externen Absenderadressen", "extended_sender_acl_denied": "Keine Rechte zum Setzen von externen Absenderadressen",
"extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig", "extra_acl_invalid": "Externe Absenderadresse \"%s\" ist ungültig",
"extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain", "extra_acl_invalid_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s", "fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
@ -454,17 +455,23 @@
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen", "totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits", "transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
"webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s", "webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s",
"webauthn_authenticator_failed": "Der ausgewählte Authenticator wurde nicht gefunden",
"webauthn_publickey_failed": "Zu dem ausgewählten Authenticator wurde kein Publickey hinterlegt",
"webauthn_username_failed": "Der ausgewählte Authenticator gehört zu einem anderen Konto",
"unknown": "Ein unbekannter Fehler trat auf", "unknown": "Ein unbekannter Fehler trat auf",
"unknown_tfa_method": "Unbekannte TFA-Methode", "unknown_tfa_method": "Unbekannte TFA-Methode",
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL", "unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
"username_invalid": "Benutzername %s kann nicht verwendet werden", "username_invalid": "Benutzername %s kann nicht verwendet werden",
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an", "validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
"value_missing": "Bitte alle Felder ausfüllen", "value_missing": "Bitte alle Felder ausfüllen",
"yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s" "yotp_verification_failed": "Yubico OTP-Verifizierung fehlgeschlagen: %s",
"template_exists": "Vorlage %s existiert bereits",
"template_id_invalid": "Vorlagen-ID %s ungültig",
"template_name_invalid": "Name der Vorlage ungültig"
}, },
"datatables": { "datatables": {
"collapse_all": "Alle Einklappen", "collapse_all": "Alle Einklappen",
"decimal": "", "decimal": ",",
"emptyTable": "Keine Daten in der Tabelle vorhanden", "emptyTable": "Keine Daten in der Tabelle vorhanden",
"expand_all": "Alle Ausklappen", "expand_all": "Alle Ausklappen",
"info": "_START_ bis _END_ von _TOTAL_ Einträgen", "info": "_START_ bis _END_ von _TOTAL_ Einträgen",
@ -498,7 +505,7 @@
"current_time": "Systemzeit", "current_time": "Systemzeit",
"disk_usage": "Festplattennutzung", "disk_usage": "Festplattennutzung",
"docs": "Dokumente", "docs": "Dokumente",
"error_show_ip": "konnte die öffentlichen IP Adressen nicht auflösen", "error_show_ip": "Konnte die öffentlichen IP Adressen nicht auflösen",
"external_logs": "Externe Logs", "external_logs": "Externe Logs",
"history_all_servers": "History (alle Server)", "history_all_servers": "History (alle Server)",
"in_memory_logs": "In-memory Logs", "in_memory_logs": "In-memory Logs",
@ -651,7 +658,8 @@
"title": "Objekt bearbeiten", "title": "Objekt bearbeiten",
"unchanged_if_empty": "Unverändert, wenn leer", "unchanged_if_empty": "Unverändert, wenn leer",
"username": "Benutzername", "username": "Benutzername",
"validate_save": "Validieren und speichern" "validate_save": "Validieren und speichern",
"pushover_sound": "Ton"
}, },
"fido2": { "fido2": {
"confirm": "Bestätigen", "confirm": "Bestätigen",
@ -692,7 +700,8 @@
"quarantine": "Quarantäne", "quarantine": "Quarantäne",
"restart_netfilter": "Netfilter neustarten", "restart_netfilter": "Netfilter neustarten",
"restart_sogo": "SOGo neustarten", "restart_sogo": "SOGo neustarten",
"user_settings": "Benutzereinstellungen" "user_settings": "Benutzereinstellungen",
"mailcow_system": "System"
}, },
"info": { "info": {
"awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung", "awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung",
@ -1236,7 +1245,8 @@
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem", "syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem",
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung", "syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem", "syncjob_EXIT_AUTHENTICATION_FAILURE": "Authentifizierungsproblem",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Falscher Benutzername oder Passwort" "syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Falscher Benutzername oder Passwort",
"pushover_sound": "Ton"
}, },
"warning": { "warning": {
"cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen", "cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen",

View File

@ -458,6 +458,9 @@
"totp_verification_failed": "TOTP verification failed", "totp_verification_failed": "TOTP verification failed",
"transport_dest_exists": "Transport destination \"%s\" exists", "transport_dest_exists": "Transport destination \"%s\" exists",
"webauthn_verification_failed": "WebAuthn verification failed: %s", "webauthn_verification_failed": "WebAuthn verification failed: %s",
"webauthn_authenticator_failed": "The selected authenticator was not found",
"webauthn_publickey_failed": "No public key was stored for the selected authenticator",
"webauthn_username_failed": "The selected authenticator belongs to another account",
"unknown": "An unknown error occurred", "unknown": "An unknown error occurred",
"unknown_tfa_method": "Unknown TFA method", "unknown_tfa_method": "Unknown TFA method",
"unlimited_quota_acl": "Unlimited quota prohibited by ACL", "unlimited_quota_acl": "Unlimited quota prohibited by ACL",
@ -468,7 +471,7 @@
}, },
"datatables": { "datatables": {
"collapse_all": "Collapse All", "collapse_all": "Collapse All",
"decimal": "", "decimal": ".",
"emptyTable": "No data available in table", "emptyTable": "No data available in table",
"expand_all": "Expand All", "expand_all": "Expand All",
"info": "Showing _START_ to _END_ of _TOTAL_ entries", "info": "Showing _START_ to _END_ of _TOTAL_ entries",

View File

@ -106,7 +106,8 @@
"username": "Používateľské meno", "username": "Používateľské meno",
"validate": "Overiť", "validate": "Overiť",
"validation_success": "Úspešne overené", "validation_success": "Úspešne overené",
"app_passwd_protocols": "Povolené protokoly k heslu aplikácie" "app_passwd_protocols": "Povolené protokoly k heslu aplikácie",
"tags": "Štítky"
}, },
"admin": { "admin": {
"access": "Prístup", "access": "Prístup",

View File

@ -57,7 +57,7 @@
</div> </div>
</div> <!-- /col-md-12 --> </div> <!-- /col-md-12 -->
</div> <!-- /row --> </div> <!-- /row -->
</div> </div>
{% include 'modals/admin.twig' %} {% include 'modals/admin.twig' %}
@ -66,7 +66,7 @@ var lang = {{ lang_admin|raw }};
var lang_datatables = {{ lang_datatables|raw }}; var lang_datatables = {{ lang_datatables|raw }};
var admin_username = '{{ mailcow_cc_username }}'; var admin_username = '{{ mailcow_cc_username }}';
var csrf_token = '{{ csrf_token }}'; var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}'; var pagination_size = Math.trunc('{{ pagination_size }}');
var log_pagination_size = '{{ log_pagination_size }}'; var log_pagination_size = Math.trunc('{{ log_pagination_size }}');
</script> </script>
{% endblock %} {% endblock %}

File diff suppressed because one or more lines are too long

View File

@ -46,7 +46,7 @@
<div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div> <div class="col-sm-3 col-5 text-end">{{ lang.fido2.known_ids }}:</div>
<div class="col-sm-9 col-7"> <div class="col-sm-9 col-7">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover table-condensed" id="fido2_keys"> <table class="table table-striped table-hover table-condensed w-100" id="fido2_keys">
<tr> <tr>
<th>ID</th> <th>ID</th>
<th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th> <th style="min-width:240px;text-align: right">{{ lang.admin.action }}</th>

View File

@ -26,7 +26,7 @@
var lang_user = {{ lang_user|raw }}; var lang_user = {{ lang_user|raw }};
var lang_datatables = {{ lang_datatables|raw }}; var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}'; var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}'; var pagination_size = Math.trunc('{{ pagination_size }}');
var table_for_domain = '{{ domain }}'; var table_for_domain = '{{ domain }}';
</script> </script>
{% endblock %} {% endblock %}

View File

@ -58,7 +58,7 @@
var lang_rl = {{ lang_rl|raw }}; var lang_rl = {{ lang_rl|raw }};
var lang_datatables = {{ lang_datatables|raw }}; var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}'; var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}'; var pagination_size = Math.trunc('{{ pagination_size }}');
var role = '{{ role }}'; var role = '{{ role }}';
var is_dual = {{ is_dual }}; var is_dual = {{ is_dual }};
var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }}; var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }};

View File

@ -37,7 +37,7 @@
</p> </p>
{% endif %} {% endif %}
</p> </p>
<table id="quarantinetable" class="table table-striped"></table> <table id="quarantinetable" class="table table-striped w-100"></table>
<div class="mass-actions-quarantine mt-4"> <div class="mass-actions-quarantine mt-4">
<div class="btn-group" data-acl="{{ acl.quarantine }}"> <div class="btn-group" data-acl="{{ acl.quarantine }}">
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="qitems" href="#"><i class="bi bi-check-all"></i> {{ lang.quarantine.toggle_all }}</a> <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="qitems" href="#"><i class="bi bi-check-all"></i> {{ lang.quarantine.toggle_all }}</a>
@ -66,7 +66,7 @@ var acl = '{{ acl_json|raw }}';
var lang = {{ lang_quarantine|raw }}; var lang = {{ lang_quarantine|raw }};
var lang_datatables = {{ lang_datatables|raw }}; var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}'; var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}'; var pagination_size = Math.trunc('{{ pagination_size }}');
var role = '{{ role }}'; var role = '{{ role }}';
</script> </script>
{% endblock %} {% endblock %}

View File

@ -55,7 +55,7 @@
var lang = {{ lang_queue|raw }}; var lang = {{ lang_queue|raw }};
var lang_datatables = {{ lang_datatables|raw }}; var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}'; var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}'; var pagination_size = Math.trunc('{{ pagination_size }}');
var table_for_domain = '{{ domain }}'; var table_for_domain = '{{ domain }}';
</script> </script>
{% endblock %} {% endblock %}

View File

@ -4,7 +4,7 @@
var acl = '{{ acl_json|raw }}'; var acl = '{{ acl_json|raw }}';
var lang = {{ lang_user|raw }}; var lang = {{ lang_user|raw }};
var csrf_token = '{{ csrf_token }}'; var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}'; var pagination_size = Math.trunc('{{ pagination_size }}');
var mailcow_cc_username = '{{ mailcow_cc_username }}'; var mailcow_cc_username = '{{ mailcow_cc_username }}';
var user_spam_score = [{{ user_spam_score }}]; var user_spam_score = [{{ user_spam_score }}];
var lang_datatables = {{ lang_datatables|raw }}; var lang_datatables = {{ lang_datatables|raw }};

View File

@ -169,7 +169,7 @@ services:
- phpfpm - phpfpm
sogo-mailcow: sogo-mailcow:
image: mailcow/sogo:1.114 image: mailcow/sogo:1.115
environment: environment:
- DBNAME=${DBNAME} - DBNAME=${DBNAME}
- DBUSER=${DBUSER} - DBUSER=${DBUSER}