Compare commits

...

48 Commits

Author SHA1 Message Date
Niklas Meyer
229303c1f8 Merge pull request #5106 from mailcow/staging
2023-03
2023-03-03 17:34:24 +01:00
Niklas Meyer
fc075bc6b7 Merge pull request #5104 from svengo/patch-4
[Helper] Update expiry-dates.sh
2023-03-03 12:44:00 +01:00
DerLinkman
d04f0257c2 Fixed permission for expiry-dates.sh 2023-03-03 12:41:24 +01:00
Sven Gottwald
d11d356803 [Helper] Update expiry-dates.sh
- Use port numbers from `mailcow.conf` instead of fixed port numbers 
- reformat output
2023-03-03 12:34:23 +01:00
Niklas Meyer
c54750ef8b Merge pull request #5085 from kritzl/patch-2
Fix cursor style when hovering 'Aliases' tab
2023-03-03 12:09:14 +01:00
Niklas Meyer
510ef5196b Merge pull request #5097 from rekup/fix/URLHAUS_ABUSE_CH
fix URLHAUS_ABUSE_CH check
2023-03-03 12:04:07 +01:00
FreddleSpl0it
04e46f9f5b [Imapsync] Use pure perl code for XOAUTH2 authmech 2023-03-03 09:57:09 +01:00
milkmaker
6c0a5028c0 [Web] Updated lang.da-dk.json (#5102)
Co-authored-by: Tacaly <frederick@tacaly.com>
2023-03-02 20:02:08 +01:00
Niklas Meyer
791bbeeb39 Merge pull request #5098 from mailcow/feat/fix-raw-attr
Add raw attribute for lang.admin.hash_remove_info
2023-03-01 21:36:40 +01:00
Peter
af267ff706 Add raw attribute for lang.admin.hash_remove_info 2023-02-28 19:42:46 +01:00
Reto Kupferschmid
46cc022590 fix URLHAUS_ABUSE_CH check 2023-02-28 14:30:38 +01:00
milkmaker
1052e13af8 Translations update from Weblate (#5092)
* [Web] Updated lang.da-dk.json

Co-authored-by: Tacaly <frederick@tacaly.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

* [Web] Updated lang.pl-pl.json

Co-authored-by: KristopherMackowiak <kkriss75@gmail.com>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

---------

Co-authored-by: Tacaly <frederick@tacaly.com>
Co-authored-by: KristopherMackowiak <kkriss75@gmail.com>
2023-02-25 19:25:24 +01:00
Niklas Meyer
11e1502b12 Merge pull request #5089 from mailcow/renovate/nextcloud-server-25.x 2023-02-24 10:53:12 +01:00
renovate[bot]
02afc45a15 Update dependency nextcloud/server to v25.0.4
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-02-23 20:07:37 +00:00
kritzl
3e1cfe0d08 Fix cursor style when hovering 'Aliases' tab 2023-02-22 00:11:56 +01:00
Niklas Meyer
d20df7d73e Merge pull request #5068 from mailcow/staging
2022-02a
2023-02-17 15:52:53 +01:00
Niklas Meyer
a8c61daeaf Merge pull request #5070 from mailcow/fix/snat
[Netfilter] Fix IPv4 Subrouting not added properly
2023-02-17 15:44:16 +01:00
Niklas Meyer
1a4f11209a Updated netfilter to 1.51 2023-02-17 13:22:23 +01:00
FreddleSpl0it
04403aaf70 [Netfilter] fix setting SNAT Rule if chain is empty 2023-02-17 13:15:44 +01:00
Niklas Meyer
7f0dd7d0d7 [Nextcloud] Added bzip2 as required package 2023-02-17 12:53:31 +01:00
FreddleSpl0it
cd29ad883e Merge branch 'staging' of https://github.com/mailcow/mailcow-dockerized into staging 2023-02-16 17:12:11 +01:00
FreddleSpl0it
e1cd719a17 [Web] fix mbox percentage sorting 2023-02-16 17:12:03 +01:00
Niklas Meyer
15bb331a7d Merge pull request #5048 from mailcow/renovate/composer-composer-2.x
Update dependency composer/composer to v2.5.4
2023-02-16 17:03:45 +01:00
Niklas Meyer
6f3179bb8d [web] Change FIDO2 login to independent button 2023-02-16 17:03:09 +01:00
Niklas Meyer
29e5b87207 Changed Language strings for clearer button meaning 2023-02-16 16:30:36 +01:00
Niklas Meyer
4403bc2d18 Merge pull request #5064 from mailcow/feat/clamav-1.0.1
[CLAMAV] Update to 1.0.1
2023-02-16 14:59:17 +01:00
Niklas Meyer
63e92e0897 [CLAMAV] Update to 1.0.1 2023-02-16 14:56:56 +01:00
renovate[bot]
aa4d8b1f47 Update dependency composer/composer to v2.5.4
Signed-off-by: milkmaker <milkmaker@mailcow.de>
2023-02-15 13:51:12 +00:00
milkmaker
9054ca18be [Web] Updated lang.lv-lv.json (#5061)
Co-authored-by: Edgars Andersons <Edgars+Mailcow+Weblate@gaitenis.id.lv>
2023-02-14 19:33:12 +01:00
Niklas Meyer
38291d123f [DB] Fix espacing of special db names during upgrade 2023-02-14 10:11:55 +01:00
renovate[bot]
ca64ff2c0b Update devops-infra/action-pull-request action to v0.5.5 (#5060)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-12 23:56:14 +01:00
Tomy Hsieh
dc85f49961 feat: Change FIDO2 login to independent button 2023-02-11 21:49:21 +08:00
milkmaker
5dca4dac81 [Web] Updated lang.ru-ru.json (#5046)
Co-authored-by: Aleksandr Kliushenok <alex.1501@icloud.com>
2023-02-04 15:00:07 +01:00
Patrick Schult
df8775d4c9 Merge pull request #5040 from mailcow/staging
2023-02
2023-02-02 15:31:34 +01:00
Niklas Meyer
2bc663dcd5 Removed Twitter Action due to Twitter Paid API (soon). Thx Elon! 2023-02-02 14:55:44 +01:00
Patrick Schult
1071bb8230 Merge pull request #4967 from FELDSAM-INC/feldsam/sso
[Web] Implemented SSO for domain admins
2023-02-02 12:12:53 +01:00
Niklas Meyer
e437810eca Merge pull request #5038 from mailcow/fix/sogo-macos-fix
[Fix] SOGo Update Fix for 5.8.0 (macOS fix)
2023-02-02 11:32:35 +01:00
FreddleSpl0it
e8fd34d31f [Web] webauthn add lang strings 2023-02-02 11:28:51 +01:00
Niklas Meyer
6aebb8352e [Fix] SOGo Update Fix for 5.8.0 (macOS fix) 2023-02-02 11:03:51 +01:00
Patrick Schult
d684e0efc0 Merge pull request #5034 from mailcow/fix/skip-sogo
[Web] Skip update_sogo_static_view if sogo is disabled
2023-01-31 11:03:50 +01:00
FreddleSpl0it
64ac6a8891 [Web] Skip update_sogo_static_view if sogo is disabled 2023-01-31 10:54:16 +01:00
FreddleSpl0it
72e8180c6b [Web] datatable adjustment 2023-01-31 10:37:51 +01:00
FreddleSpl0it
d62c275004 [Web] match PAGINATION_SIZE to an existing datatable option 2023-01-31 09:49:18 +01:00
Patrick Schult
aa7f562761 Merge pull request #5011 from realizelol/staging
[BS5] Support for pagination_size + some minor improvements (to quarantine)
2023-01-31 09:43:51 +01:00
renovate[bot]
a1f033e4c1 Update docker/build-push-action action to v4 (#5032)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-30 19:58:17 +01:00
milkmaker
58ddc31db6 Translations update from Weblate (#5026)
* [Web] Updated lang.en-gb.json

Co-authored-by: Peter <magic@kthx.at>

* [Web] Updated lang.de-de.json

Co-authored-by: Peter <magic@kthx.at>

* [Web] Updated lang.sk-sk.json

Co-authored-by: Lukáš Matula <lukas@gbely.net>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: Lukáš Matula <lukas@gbely.net>
2023-01-26 20:09:52 +01:00
Kristian Feldsam
5bf62481d5 [Web] Implemented SSO for domain admins
Signed-off-by: Kristian Feldsam <feldsam@gmail.com>

Revert "[Web] Implemented SSO for domain admins"

This reverts commit 6860dc8ebe2c8f53d77df5bca7787f7cb3bb4ee0.

Signed-off-by: Kristian Feldsam <feldsam@gmail.com>
2023-01-26 15:54:44 +01:00
realizelol
6ff3f3f044 [Web] Set pageLength to pagination_size + repect savedState...
Fix width in quarantine table.
2023-01-25 23:50:39 +01:00
58 changed files with 5552 additions and 5308 deletions

View File

@@ -12,7 +12,7 @@ jobs:
with:
fetch-depth: 0
- name: Run the Action
uses: devops-infra/action-pull-request@v0.5.3
uses: devops-infra/action-pull-request@v0.5.5
with:
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}

View File

@@ -26,7 +26,7 @@ jobs:
password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
context: .
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

@@ -1,4 +1,4 @@
FROM clamav/clamav:1.0_base
FROM clamav/clamav:1.0.1-1_base
LABEL maintainer "André Peters <andre.peters@servercow.de>"

View File

@@ -21,6 +21,7 @@ RUN groupadd -g 5000 vmail \
&& touch /etc/default/locale \
&& apt-get update \
&& apt-get -y --no-install-recommends install \
build-essential \
apt-transport-https \
ca-certificates \
cpanminus \
@@ -61,6 +62,7 @@ RUN groupadd -g 5000 vmail \
libproc-processtable-perl \
libreadonly-perl \
libregexp-common-perl \
libssl-dev \
libsys-meminfo-perl \
libterm-readkey-perl \
libtest-deep-perl \
@@ -110,6 +112,8 @@ RUN groupadd -g 5000 vmail \
&& apt-get autoclean \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/* /var/tmp/* /root/.cache/
# imapsync dependencies
RUN cpan Crypt::OpenSSL::PKCS12
COPY trim_logs.sh /usr/local/bin/trim_logs.sh
COPY clean_q_aged.sh /usr/local/bin/clean_q_aged.sh

View File

@@ -8492,6 +8492,7 @@ sub xoauth2
require HTML::Entities ;
require JSON ;
require JSON::WebToken::Crypt::RSA ;
require Crypt::OpenSSL::PKCS12;
require Crypt::OpenSSL::RSA ;
require Encode::Byte ;
require IO::Socket::SSL ;
@@ -8532,8 +8533,9 @@ sub xoauth2
$sync->{ debug } and myprint( "Service account: $iss\nKey file: $keyfile\nKey password: $keypass\n");
# Get private key from p12 file (would be better in perl...)
$key = `openssl pkcs12 -in "$keyfile" -nodes -nocerts -passin pass:$keypass -nomacver`;
# Get private key from p12 file
my $pkcs12 = Crypt::OpenSSL::PKCS12->new_from_file($keyfile);
$key = $pkcs12->private_key($keypass);
$sync->{ debug } and myprint( "Private key:\n$key\n");
}

View File

@@ -359,21 +359,28 @@ def snat4(snat_target):
chain = iptc.Chain(table, 'POSTROUTING')
table.autocommit = False
new_rule = get_snat4_rule()
for position, rule in enumerate(chain.rules):
match = all((
new_rule.get_src() == rule.get_src(),
new_rule.get_dst() == rule.get_dst(),
new_rule.target.parameters == rule.target.parameters,
new_rule.target.name == rule.target.name
))
if position == 0:
if not match:
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
if match:
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
chain.delete_rule(rule)
if not chain.rules:
# if there are no rules in the chain, insert the new rule directly
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
for position, rule in enumerate(chain.rules):
match = all((
new_rule.get_src() == rule.get_src(),
new_rule.get_dst() == rule.get_dst(),
new_rule.target.parameters == rule.target.parameters,
new_rule.target.name == rule.target.name
))
if position == 0:
if not match:
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
if match:
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
chain.delete_rule(rule)
table.commit()
table.autocommit = True
except:

View File

@@ -12,7 +12,7 @@ ARG MEMCACHED_PECL_VERSION=3.2.0
# renovate: datasource=github-tags depName=phpredis/phpredis versioning=semver-coerced
ARG REDIS_PECL_VERSION=5.3.7
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced
ARG COMPOSER_VERSION=2.5.1
ARG COMPOSER_VERSION=2.5.4
RUN apk add -U --no-cache autoconf \
aspell-dev \

View File

@@ -159,8 +159,8 @@ BAZAAR_ABUSE_CH {
}
URLHAUS_ABUSE_CH {
type = "url";
filter = "full";
type = "selector";
selector = "urls";
map = "https://urlhaus.abuse.ch/downloads/text_online/";
score = 10.0;
}

View File

@@ -699,6 +699,38 @@ paths:
type: string
type: object
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:
post:
responses:
@@ -1999,7 +2031,7 @@ paths:
- domain.tld
- domain2.tld
properties:
items:
items:
type: array
items:
type: string
@@ -2993,7 +3025,7 @@ paths:
application/json:
schema:
type: array
items:
items:
type: object
properties:
log:
@@ -5586,6 +5618,8 @@ tags:
description: Manage DKIM keys
- name: Domain admin
description: Create or udpdate domain admin users
- name: Single Sign-On
description: Issue tokens for users
- name: Address Rewriting
description: Create BCC maps or recipient maps
- name: Outgoing TLS Policy Map Overrides

View File

@@ -77,4 +77,22 @@ li .dtr-data {
table.dataTable>tbody>tr.child span.dtr-title {
width: 30%;
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 {
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;
}
.senders-mw220 {
max-width: 100% !important;
}
}
@media (max-width: 350px) {

View File

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

View File

@@ -1,407 +1,468 @@
<?php
function domain_admin($_action, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
!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_pass2']) ?: $_data_log['user_new_pass2'] = '*';
switch ($_action) {
case 'add':
$username = strtolower(trim($_data['username']));
$password = $_data['password'];
$password2 = $_data['password2'];
$domains = (array)$_data['domains'];
$active = intval($_data['active']);
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (empty($domains)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'domain_invalid'
);
return false;
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
return false;
}
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
foreach ($num_results as $num_results_each) {
if ($num_results_each != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_exists', htmlspecialchars($username))
);
return false;
}
}
if (password_check($password, $password2) !== true) {
continue;
}
$password_hashed = hash_password($password);
$valid_domains = 0;
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_invalid', htmlspecialchars($domain))
);
continue;
}
$valid_domains++;
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username, :domain, :created, :active)");
$stmt->execute(array(
':username' => $username,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
if ($valid_domains != 0) {
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '0', :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':active' => $active
));
}
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_added', htmlspecialchars($username))
);
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
// Administrator
if ($_SESSION['mailcow_cc_role'] == "admin") {
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
$is_now = domain_admin('details', $username);
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$password = $_data['password'];
$password2 = $_data['password2'];
if (!empty($domains)) {
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_invalid', htmlspecialchars($domain))
);
continue 2;
}
}
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
if ($username_new != $username) {
if (!empty(domain_admin('details', $username_new)['username'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
}
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username
));
if (!empty($domains)) {
foreach ($domains as $domain) {
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username_new, :domain, :created, :active)");
$stmt->execute(array(
':username_new' => $username_new,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
}
if (!empty($password)) {
if (password_check($password, $password2) !== true) {
return false;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
else {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_modified', htmlspecialchars($username))
);
}
return true;
}
// Domain administrator
// Can only edit itself
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
$username = $_SESSION['mailcow_cc_username'];
$password_old = $_data['user_old_pass'];
$password_new = $_data['user_new_pass'];
$password_new2 = $_data['user_new_pass2'];
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
WHERE `username` = :user");
$stmt->execute(array(':user' => $username));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!verify_hash($row['password'], $password_old)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (password_check($password_new, $password_new2) !== true) {
return false;
}
$password_hashed = hash_password($password_new);
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_modified', htmlspecialchars($username))
);
}
break;
case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$usernames = (array)$_data['username'];
foreach ($usernames as $username) {
if (empty(domain_admin('details', $username))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_removed', htmlspecialchars($username))
);
}
break;
case 'get':
$domainadmins = array();
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->query("SELECT DISTINCT
`username`
FROM `domain_admins`
WHERE `username` IN (
SELECT `username` FROM `admin`
WHERE `superadmin`!='1'
)");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$domainadmins[] = $row['username'];
}
return $domainadmins;
break;
case 'details':
$domainadmindata = array();
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
return false;
}
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false;
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
return false;
}
$stmt = $pdo->prepare("SELECT
`tfa`.`active` AS `tfa_active`,
`domain_admins`.`username`,
`domain_admins`.`created`,
`domain_admins`.`active` AS `active`
FROM `domain_admins`
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
WHERE `domain_admins`.`username`= :domain_admin");
$stmt->execute(array(
':domain_admin' => $_data
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($row)) {
return false;
}
$domainadmindata['username'] = $row['username'];
$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['active'] = $row['active'];
$domainadmindata['active_int'] = $row['active'];
$domainadmindata['created'] = $row['created'];
// GET SELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` IN (
SELECT `domain` FROM `domain_admins`
WHERE `username`= :domain_admin)");
$stmt->execute(array(':domain_admin' => $_data));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$domainadmindata['selected_domains'][] = $row['domain'];
}
// GET UNSELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` NOT IN (
SELECT `domain` FROM `domain_admins`
WHERE `username`= :domain_admin)");
$stmt->execute(array(':domain_admin' => $_data));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$domainadmindata['unselected_domains'][] = $row['domain'];
}
if (!isset($domainadmindata['unselected_domains'])) {
$domainadmindata['unselected_domains'] = "";
}
return $domainadmindata;
break;
}
}
<?php
function domain_admin($_action, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
!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_pass2']) ?: $_data_log['user_new_pass2'] = '*';
switch ($_action) {
case 'add':
$username = strtolower(trim($_data['username']));
$password = $_data['password'];
$password2 = $_data['password2'];
$domains = (array)$_data['domains'];
$active = intval($_data['active']);
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (empty($domains)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'domain_invalid'
);
return false;
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
return false;
}
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
foreach ($num_results as $num_results_each) {
if ($num_results_each != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_exists', htmlspecialchars($username))
);
return false;
}
}
if (password_check($password, $password2) !== true) {
continue;
}
$password_hashed = hash_password($password);
$valid_domains = 0;
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_invalid', htmlspecialchars($domain))
);
continue;
}
$valid_domains++;
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username, :domain, :created, :active)");
$stmt->execute(array(
':username' => $username,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
if ($valid_domains != 0) {
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '0', :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':active' => $active
));
}
$stmt = $pdo->prepare("INSERT INTO `da_acl` (`username`) VALUES (:username)");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_added', htmlspecialchars($username))
);
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
// Administrator
if ($_SESSION['mailcow_cc_role'] == "admin") {
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
$is_now = domain_admin('details', $username);
$domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$password = $_data['password'];
$password2 = $_data['password2'];
if (!empty($domains)) {
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || mailbox('get', 'domain_details', $domain) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_invalid', htmlspecialchars($domain))
);
continue 2;
}
}
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
if ($username_new != $username) {
if (!empty(domain_admin('details', $username_new)['username'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
}
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("UPDATE `da_acl` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username
));
if (!empty($domains)) {
foreach ($domains as $domain) {
$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
VALUES (:username_new, :domain, :created, :active)");
$stmt->execute(array(
':username_new' => $username_new,
':domain' => $domain,
':created' => date('Y-m-d H:i:s'),
':active' => $active
));
}
}
if (!empty($password)) {
if (password_check($password, $password2) !== true) {
return false;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
else {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_modified', htmlspecialchars($username))
);
}
return true;
}
// Domain administrator
// Can only edit itself
elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
$username = $_SESSION['mailcow_cc_username'];
$password_old = $_data['user_old_pass'];
$password_new = $_data['user_new_pass'];
$password_new2 = $_data['user_new_pass2'];
$stmt = $pdo->prepare("SELECT `password` FROM `admin`
WHERE `username` = :user");
$stmt->execute(array(':user' => $username));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!verify_hash($row['password'], $password_old)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (password_check($password_new, $password_new2) !== true) {
return false;
}
$password_hashed = hash_password($password_new);
$stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_modified', htmlspecialchars($username))
);
}
break;
case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$usernames = (array)$_data['username'];
foreach ($usernames as $username) {
if (empty(domain_admin('details', $username))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('domain_admin_removed', htmlspecialchars($username))
);
}
break;
case 'get':
$domainadmins = array();
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->query("SELECT DISTINCT
`username`
FROM `domain_admins`
WHERE `username` IN (
SELECT `username` FROM `admin`
WHERE `superadmin`!='1'
)");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$domainadmins[] = $row['username'];
}
return $domainadmins;
break;
case 'details':
$domainadmindata = array();
if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
return false;
}
elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false;
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
return false;
}
$stmt = $pdo->prepare("SELECT
`tfa`.`active` AS `tfa_active`,
`domain_admins`.`username`,
`domain_admins`.`created`,
`domain_admins`.`active` AS `active`
FROM `domain_admins`
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
WHERE `domain_admins`.`username`= :domain_admin");
$stmt->execute(array(
':domain_admin' => $_data
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($row)) {
return false;
}
$domainadmindata['username'] = $row['username'];
$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['active'] = $row['active'];
$domainadmindata['active_int'] = $row['active'];
$domainadmindata['created'] = $row['created'];
// GET SELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` IN (
SELECT `domain` FROM `domain_admins`
WHERE `username`= :domain_admin)");
$stmt->execute(array(':domain_admin' => $_data));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$domainadmindata['selected_domains'][] = $row['domain'];
}
// GET UNSELECTED
$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
WHERE `domain` NOT IN (
SELECT `domain` FROM `domain_admins`
WHERE `username`= :domain_admin)");
$stmt->execute(array(':domain_admin' => $_data));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$domainadmindata['unselected_domains'][] = $row['domain'];
}
if (!isset($domainadmindata['unselected_domains'])) {
$domainadmindata['unselected_domains'] = "";
}
return $domainadmindata;
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(
'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'authenticator not found')
'msg' => array('webauthn_authenticator_failed')
);
return false;
}
@@ -1748,11 +1748,20 @@ function verify_tfa_login($username, $_data) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'publicKey not found')
'msg' => array('webauthn_publickey_failed')
);
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 {
$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(
'type' => 'danger',
'log' => array(__FUNCTION__, $username, '*'),
'msg' => array('webauthn_verification_failed', 'could not determine user role')
'msg' => array('webauthn_role_failed')
);
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['tfa_id'] = $process_webauthn['id'];
$_SESSION['authReq'] = null;

View File

@@ -5264,7 +5264,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
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();
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,4 +1,15 @@
<?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 (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) {
$_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_role']);
unset($_SESSION['pending_tfa_methods']);
header("Location: /user");
} else {
unset($_SESSION['pending_mailcow_cc_username']);
@@ -34,7 +45,7 @@ if (isset($_POST["quick_delete"])) {
if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
$login_user = strtolower(trim($_POST["login_user"]));
$as = check_login($login_user, $_POST["pass_user"]);
if ($as == "admin") {
$_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "admin";

View File

@@ -124,7 +124,7 @@ $MAILCOW_APPS = array(
);
// Rows until pagination begins
$PAGINATION_SIZE = 20;
$PAGINATION_SIZE = 25;
// Default number of rows/lines to display (log table)
$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
containersToUpdate = {}
containersToUpdate = {};
// set default ChartJs Font Color
Chart.defaults.color = '#999';
// create host cpu and mem charts
@@ -44,14 +44,13 @@ $(document).ready(function() {
check_update(mailcow_info.version_tag, mailcow_info.project_url);
}
$("#maiclow_version").click(function(){
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" ||
mailcow_info.branch !== "master")
if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
return;
showVersionModal("Version " + mailcow_info.version_tag, mailcow_info.version_tag);
})
// 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(".spinner-border").removeClass("d-none");
@@ -76,7 +75,7 @@ $(document).ready(function() {
$("#host_ipv6").addClass("d-block");
}).catch(function(error){
console.log(error);
$("#host_ipv6").removeClass("d-none");
$("#host_ipv6").addClass("d-block");
$("#host_ipv6").addClass("text-danger");
@@ -119,10 +118,11 @@ jQuery(function($){
}
var table = $('#autodiscover_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -188,10 +188,11 @@ jQuery(function($){
}
var table = $('#postfix_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -242,10 +243,11 @@ jQuery(function($){
}
var table = $('#watchdog_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -300,10 +302,11 @@ jQuery(function($){
}
var table = $('#api_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -352,7 +355,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-api-logs', '#api_log');
});
@@ -365,10 +368,11 @@ jQuery(function($){
}
var table = $('#rl_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -455,7 +459,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-rl-logs', '#rl_log');
});
@@ -468,10 +472,11 @@ jQuery(function($){
}
var table = $('#ui_logs').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -538,7 +543,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-ui-logs', '#ui_log');
});
@@ -551,10 +556,11 @@ jQuery(function($){
}
var table = $('#sasl_logs').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -598,7 +604,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-sasl-logs', '#sasl_logs');
});
@@ -611,10 +617,11 @@ jQuery(function($){
}
var table = $('#acme_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -647,7 +654,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-acme-logs', '#acme_log');
});
@@ -660,10 +667,11 @@ jQuery(function($){
}
var table = $('#netfilter_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -701,7 +709,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-netfilter-logs', '#netfilter_log');
});
@@ -714,10 +722,11 @@ jQuery(function($){
}
var table = $('#sogo_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -755,7 +764,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-sogo-logs', '#sogo_log');
});
@@ -768,10 +777,11 @@ jQuery(function($){
}
var table = $('#dovecot_log').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -883,10 +893,11 @@ jQuery(function($){
}
var table = $('#rspamd_history').DataTable({
responsive: true,
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: log_pagination_size,
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>>",
@@ -983,7 +994,7 @@ jQuery(function($){
}
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#tab-rspamd-history', '#rspamd_history');
});
@@ -998,31 +1009,31 @@ jQuery(function($){
item.rcpt = escapeHtml(item.rcpt_smtp.join(", "));
}
item.symbols = Object.keys(item.symbols).sort(function (a, b) {
if (item.symbols[a].score === 0) return 1
if (item.symbols[b].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 && 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) {
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) {
var sym = item.symbols[key];
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) {
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)'
sym.score_formatted = '(<span><b>' + sym.score + '</b></span>)';
}
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;
if (sym.options) {
str += ' [' + escapeHtml(sym.options.join(", ")) + "]";
}
return str
return str;
}).join('<br>\n');
item.subject = escapeHtml(item.subject);
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) {
e.preventDefault();
var log_table= $(this).data("table")
var new_nrows = $(this).data("nrows")
var post_process = $(this).data("post-process")
var log_url = $(this).data("log-url")
var log_table= $(this).data("table");
var new_nrows = $(this).data("nrows");
var post_process = $(this).data("post-process");
var log_url = $(this).data("log-url");
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");
return;
@@ -1184,9 +1195,9 @@ jQuery(function($){
})
function hideTableExpandCollapseBtn(tab, table){
if ($(table).hasClass('collapsed'))
$(tab).find(".table_collapse_option").show();
$(tab).find(".table_collapse_option").show();
else
$(tab).find(".table_collapse_option").hide();
$(tab).find(".table_collapse_option").hide();
}
// detect element visibility changes
@@ -1220,7 +1231,6 @@ jQuery(function($){
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
// start polling host stats if tab is active
onVisible("[id^=tab-containers]", () => update_stats());
// 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();
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);
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();
mem_chart.update();
@@ -1464,23 +1474,23 @@ function createReadWriteChart(chart_id, read_lable, write_lable){
};
var optionsNet = {
interaction: {
mode: 'index'
mode: 'index'
},
scales: {
yAxis: {
min: 0,
grid: {
display: false
display: false
},
ticks: {
callback: function(i, index, ticks) {
return formatBytes(i);
return formatBytes(i);
}
}
},
xAxis: {
grid: {
display: false
display: false
}
}
}
@@ -1528,13 +1538,13 @@ function createHostCpuAndMemChart(){
};
var optionsCpu = {
interaction: {
mode: 'index'
mode: 'index'
},
scales: {
yAxis: {
min: 0,
grid: {
display: false
display: false
},
ticks: {
callback: function(i, index, ticks) {
@@ -1544,7 +1554,7 @@ function createHostCpuAndMemChart(){
},
xAxis: {
grid: {
display: false
display: false
}
}
}
@@ -1566,13 +1576,13 @@ function createHostCpuAndMemChart(){
};
var optionsMem = {
interaction: {
mode: 'index'
mode: 'index'
},
scales: {
yAxis: {
min: 0,
grid: {
display: false
display: false
},
ticks: {
callback: function(i, index, ticks) {
@@ -1582,7 +1592,7 @@ function createHostCpuAndMemChart(){
},
xAxis: {
grid: {
display: false
display: false
}
}
}
@@ -1678,22 +1688,22 @@ function parseGithubMarkdownLinks(inputText) {
replacePattern1 = /(\b(https?):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, (matched, index, original, input_string) => {
if (matched.includes('github.com')){
// return short link if it's github link
last_uri_path = matched.split('/');
last_uri_path = last_uri_path[last_uri_path.length - 1];
if (matched.includes('github.com')){
// return short link if it's github link
last_uri_path = matched.split('/');
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
if (matched.includes('/compare/') && mailcow_info.last_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;
}
// 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 !== ''){
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;
}
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
return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
// if it's not a github link, return complete link
return '<a href="' + matched + '" target="_blank">' + matched + '</a>';
});
return replacedText;

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +1,71 @@
jQuery(function($){
var qitem = $('legend').data('hash');
var qError = $("#qid_error");
$.ajax({
url: '/inc/ajax/qitem_details.php',
data: { hash: qitem },
dataType: 'json',
success: function(data){
$('[data-id="qitems_single"]').each(function(index) {
$(this).attr("data-item", qitem);
});
$('#qid_detail_subj').text(data.subject);
$('#qid_detail_hfrom').text(data.header_from);
$('#qid_detail_efrom').text(data.env_from);
$('#qid_detail_score').html('');
$('#qid_detail_symbols').html('');
$('#qid_detail_recipients').html('');
$('#qid_detail_fuzzy').html('');
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$.each(data.fuzzy_hashes, function (index, value) {
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
});
} else {
$('#qid_detail_fuzzy').append('-');
}
if (typeof data.symbols !== 'undefined') {
data.symbols.sort(function (a, b) {
if (a.score === 0) return 1
if (b.score === 0) return -1
if (b.score < 0 && a.score < 0) {
return a.score - b.score
}
if (b.score > 0 && a.score > 0) {
return b.score - a.score
}
return b.score - a.score
})
$.each(data.symbols, function (index, value) {
var highlightClass = ''
if (value.score > 0) highlightClass = 'negative'
else if (value.score < 0) highlightClass = 'positive'
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>');
});
$('[data-bs-toggle="tooltip"]').tooltip()
}
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
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_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") {
$('#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') {
$.each(data.recipients, function(index, value) {
var elem = $('<span class="mail-address-item"></span>');
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
$('#qid_detail_recipients').append(elem);
});
}
},
error: function(data){
if (typeof data.error !== 'undefined') {
qError.text("Error loading quarantine item");
qError.show();
}
}
});
});
jQuery(function($){
var qitem = $('legend').data('hash');
var qError = $("#qid_error");
$.ajax({
url: '/inc/ajax/qitem_details.php',
data: { hash: qitem },
dataType: 'json',
success: function(data){
$('[data-id="qitems_single"]').each(function(index) {
$(this).attr("data-item", qitem);
});
$('#qid_detail_subj').text(data.subject);
$('#qid_detail_hfrom').text(data.header_from);
$('#qid_detail_efrom').text(data.env_from);
$('#qid_detail_score').html('');
$('#qid_detail_symbols').html('');
$('#qid_detail_recipients').html('');
$('#qid_detail_fuzzy').html('');
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$.each(data.fuzzy_hashes, function (index, value) {
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
});
} else {
$('#qid_detail_fuzzy').append('-');
}
if (typeof data.symbols !== 'undefined') {
data.symbols.sort(function (a, b) {
if (a.score === 0) return 1;
if (b.score === 0) return -1;
if (b.score < 0 && a.score < 0) {
return a.score - b.score;
}
if (b.score > 0 && a.score > 0) {
return b.score - a.score;
}
return b.score - a.score;
})
$.each(data.symbols, function (index, value) {
var highlightClass = '';
if (value.score > 0) highlightClass = 'negative';
else if (value.score < 0) highlightClass = 'positive';
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>');
});
$('[data-bs-toggle="tooltip"]').tooltip();
}
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
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_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") {
$('#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') {
$.each(data.recipients, function(index, value) {
var elem = $('<span class="mail-address-item"></span>');
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
$('#qid_detail_recipients').append(elem);
});
}
},
error: function(data){
if (typeof data.error !== 'undefined') {
qError.text("Error loading quarantine item");
qError.show();
}
}
});
});

View File

@@ -1,286 +1,297 @@
// 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}};
jQuery(function($){
acl_data = JSON.parse(acl);
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
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]}
$(".refresh_table").on('click', function(e) {
e.preventDefault();
var table_name = $(this).data('table');
$('#' + table_name).DataTable().ajax.reload();
});
function draw_quarantine_table() {
var table = $('#quarantinetable').DataTable({
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
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>>",
language: lang_datatables,
initComplete: function(){
hideTableExpandCollapseBtn('#quarantinetable');
},
ajax: {
type: "GET",
url: "/api/v1/get/quarantine/all",
dataSrc: function(data){
$.each(data, function (i, item) {
if (item.subject === null) {
item.subject = '';
} else {
item.subject = escapeHtml(item.subject);
}
if (item.score === null) {
item.score = '-';
}
if (item.virus_flag > 0) {
item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
} else {
item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
}
if (item.action === "reject") {
item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
} else if (item.action === "add header") {
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
} else if (item.action === "rewrite subject") {
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
}
if(item.notified > 0) {
item.notified = '&#10004;';
} else {
item.notified = '&#10006;';
}
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>' +
'<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>';
}
else {
item.action = '<div class="btn-group">' +
'<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>' +
'</div>';
}
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
});
return data;
}
},
columns: [
{
// placeholder, so checkbox will not block child row toggle
title: '',
data: null,
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: '',
data: 'chkbox',
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: 'ID',
data: 'id',
defaultContent: ''
},
{
title: lang.qid,
data: 'qid',
defaultContent: ''
},
{
title: lang.sender,
data: 'sender',
defaultContent: ''
},
{
title: lang.subj,
data: 'subject',
defaultContent: ''
},
{
title: lang.rspamd_result,
data: 'rspamdaction',
defaultContent: ''
},
{
title: lang.rcpt,
data: 'rcpt',
defaultContent: ''
},
{
title: lang.danger,
data: 'virus',
defaultContent: ''
},
{
title: lang.spam_score,
data: 'score',
defaultContent: ''
},
{
title: lang.notified,
data: 'notified',
defaultContent: ''
},
{
title: lang.received,
data: 'created',
defaultContent: '',
createdCell: function(td, cellData) {
$(td).attr({
"data-order": cellData,
"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"});
$(td).html(dateString);
}
},
{
title: lang.action,
data: 'action',
className: 'text-md-end dt-sm-head-hidden dt-body-right',
defaultContent: ''
},
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#quarantinetable');
});
}
$('body').on('click', '.show_qid_info', function (e) {
e.preventDefault();
var qitem = $(this).attr('data-item');
var qError = $("#qid_error");
$('#qidDetailModal').modal('show');
qError.hide();
$.ajax({
url: '/inc/ajax/qitem_details.php',
data: { id: qitem },
dataType: 'json',
success: function(data){
$('[data-id="qitems_single"]').each(function(index) {
$(this).attr("data-item", qitem);
});
$("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
$("#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')");
$('#qid_detail_subj').text(data.subject);
$('#qid_detail_hfrom').text(data.header_from);
$('#qid_detail_efrom').text(data.env_from);
$('#qid_detail_score').html('');
$('#qid_detail_recipients').html('');
$('#qid_detail_symbols').html('');
$('#qid_detail_fuzzy').html('');
if (typeof data.symbols !== 'undefined') {
data.symbols.sort(function (a, b) {
if (a.score === 0) return 1
if (b.score === 0) return -1
if (b.score < 0 && a.score < 0) {
return a.score - b.score
}
if (b.score > 0 && a.score > 0) {
return b.score - a.score
}
return b.score - a.score
})
$.each(data.symbols, function (index, value) {
var highlightClass = ''
if (value.score > 0) highlightClass = 'negative'
else if (value.score < 0) highlightClass = 'positive'
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>');
});
$('[data-bs-toggle="tooltip"]').tooltip()
}
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$.each(data.fuzzy_hashes, function (index, value) {
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
});
} else {
$('#qid_detail_fuzzy').append('-');
}
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
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_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") {
$('#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') {
$.each(data.recipients, function(index, value) {
var elem = $('<span class="mail-address-item"></span>');
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
$('#qid_detail_recipients').append(elem);
});
}
$('#qid_detail_text').text(data.text_plain);
$('#qid_detail_text_from_html').text(data.text_html);
var qAtts = $("#qid_detail_atts");
if (typeof data.attachments !== 'undefined') {
qAtts.text('');
$.each(data.attachments, function(index, value) {
qAtts.append(
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
);
});
}
else {
qAtts.text('-');
}
},
error: function(data){
if (typeof data.error !== 'undefined') {
$('#qid_detail_subj').text('-');
$('#qid_detail_hfrom').text('-');
$('#qid_detail_efrom').text('-');
$('#qid_detail_score').html('-');
$('#qid_detail_recipients').html('-');
$('#qid_detail_symbols').html('-');
$('#qid_detail_fuzzy').html('-');
$('#qid_detail_text').text('-');
$('#qid_detail_text_from_html').text('-');
qError.text("Error loading quarantine item");
qError.show();
}
}
});
});
$('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();
}
});
// 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}};
jQuery(function($){
acl_data = JSON.parse(acl);
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
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]}
$(".refresh_table").on('click', function(e) {
e.preventDefault();
var table_name = $(this).data('table');
$('#' + table_name).DataTable().ajax.reload();
});
function draw_quarantine_table() {
var table = $('#quarantinetable').DataTable({
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: pagination_size,
order: [[2, 'desc']],
lengthMenu: [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, 'all']
],
pagingType: 'first_last_numbers',
aColumns: [
{ sWidth: '8.25%' },
{ sClass: 'classDataTable' }
],
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>>",
language: lang_datatables,
initComplete: function(){
hideTableExpandCollapseBtn('#quarantinetable');
},
ajax: {
type: "GET",
url: "/api/v1/get/quarantine/all",
dataSrc: function(data){
$.each(data, function (i, item) {
if (item.subject === null) {
item.subject = '';
} else {
item.subject = escapeHtml(item.subject);
}
if (item.score === null) {
item.score = '-';
}
if (item.virus_flag > 0) {
item.virus = '<span class="badge fs-6 bg-danger">' + lang.high_danger + '</span>';
} else {
item.virus = '<span class="badge fs-6 bg-secondary">' + lang.neutral_danger + '</span>';
}
if (item.action === "reject") {
item.rspamdaction = '<span class="badge fs-6 bg-danger">' + lang.rejected + '</span>';
} else if (item.action === "add header") {
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.junk_folder + '</span>';
} else if (item.action === "rewrite subject") {
item.rspamdaction = '<span class="badge fs-6 bg-warning">' + lang.rewrite_subject + '</span>';
}
if(item.notified > 0) {
item.notified = '&#10004;';
} else {
item.notified = '&#10006;';
}
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>' +
'<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>';
}
else {
item.action = '<div class="btn-group">' +
'<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>' +
'</div>';
}
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
});
return data;
}
},
columns: [
{
// placeholder, so checkbox will not block child row toggle
title: '',
data: null,
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: '',
data: 'chkbox',
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: 'ID',
data: 'id',
defaultContent: ''
},
{
title: lang.qid,
data: 'qid',
defaultContent: ''
},
{
title: lang.sender,
data: 'sender',
className: 'senders-mw220',
defaultContent: ''
},
{
title: lang.subj,
data: 'subject',
defaultContent: ''
},
{
title: lang.rspamd_result,
data: 'rspamdaction',
defaultContent: ''
},
{
title: lang.rcpt,
data: 'rcpt',
defaultContent: ''
},
{
title: lang.danger,
data: 'virus',
defaultContent: ''
},
{
title: lang.spam_score,
data: 'score',
defaultContent: ''
},
{
title: lang.notified,
data: 'notified',
defaultContent: ''
},
{
title: lang.received,
data: 'created',
defaultContent: '',
createdCell: function(td, cellData) {
$(td).attr({
"data-order": cellData,
"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"});
$(td).html(dateString);
}
},
{
title: lang.action,
data: 'action',
className: 'dt-text-right dt-sm-head-hidden',
defaultContent: ''
},
]
});
table.on('responsive-resize', function (e, datatable, columns){
hideTableExpandCollapseBtn('#quarantinetable');
});
}
$('body').on('click', '.show_qid_info', function (e) {
e.preventDefault();
var qitem = $(this).attr('data-item');
var qError = $("#qid_error");
$('#qidDetailModal').modal('show');
qError.hide();
$.ajax({
url: '/inc/ajax/qitem_details.php',
data: { id: qitem },
dataType: 'json',
success: function(data){
$('[data-id="qitems_single"]').each(function(index) {
$(this).attr("data-item", qitem);
});
$("#quick_download_link").attr("onclick", "window.open('/inc/ajax/qitem_details.php?id=" + qitem + "&eml', '_blank')");
$("#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')");
$('#qid_detail_subj').text(data.subject);
$('#qid_detail_hfrom').text(data.header_from);
$('#qid_detail_efrom').text(data.env_from);
$('#qid_detail_score').html('');
$('#qid_detail_recipients').html('');
$('#qid_detail_symbols').html('');
$('#qid_detail_fuzzy').html('');
if (typeof data.symbols !== 'undefined') {
data.symbols.sort(function (a, b) {
if (a.score === 0) return 1;
if (b.score === 0) return -1;
if (b.score < 0 && a.score < 0) {
return a.score - b.score;
}
if (b.score > 0 && a.score > 0) {
return b.score - a.score;
}
return b.score - a.score;
})
$.each(data.symbols, function (index, value) {
var highlightClass = '';
if (value.score > 0) highlightClass = 'negative';
else if (value.score < 0) highlightClass = 'positive';
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>');
});
$('[data-bs-toggle="tooltip"]').tooltip();
}
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$.each(data.fuzzy_hashes, function (index, value) {
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>');
});
} else {
$('#qid_detail_fuzzy').append('-');
}
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
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_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") {
$('#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') {
$.each(data.recipients, function(index, value) {
var elem = $('<span class="mail-address-item"></span>');
elem.text(value.address + ' (' + value.type.toUpperCase() + ')');
$('#qid_detail_recipients').append(elem);
});
}
$('#qid_detail_text').text(data.text_plain);
$('#qid_detail_text_from_html').text(data.text_html);
var qAtts = $("#qid_detail_atts");
if (typeof data.attachments !== 'undefined') {
qAtts.text('');
$.each(data.attachments, function(index, value) {
qAtts.append(
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
);
});
}
else {
qAtts.text('-');
}
},
error: function(data){
if (typeof data.error !== 'undefined') {
$('#qid_detail_subj').text('-');
$('#qid_detail_hfrom').text('-');
$('#qid_detail_efrom').text('-');
$('#qid_detail_score').html('-');
$('#qid_detail_recipients').html('-');
$('#qid_detail_symbols').html('-');
$('#qid_detail_fuzzy').html('-');
$('#qid_detail_text').text('-');
$('#qid_detail_text_from_html').text('-');
qError.text("Error loading quarantine item");
qError.show();
}
}
});
});
$('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($){
$(".refresh_table").on('click', function(e) {
e.preventDefault();
var table_name = $(this).data('table');
$('#' + table_name).DataTable().ajax.reload();
});
$(".refresh_table").on('click', function(e) {
e.preventDefault();
var table_name = $(this).data('table');
$('#' + 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
$('#showQueuedMsg').on('show.bs.modal', function (e) {
$('#queue_msg_content').text(lang.loading);
button = $(e.relatedTarget)
if (button != null) {
$('#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;
// Queue item
$('#showQueuedMsg').on('show.bs.modal', function (e) {
$('#queue_msg_content').text(lang.loading);
button = $(e.relatedTarget)
if (button != null) {
$('#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);
}
});
})
$('#queuetable').DataTable({
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
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>>",
language: lang_datatables,
ajax: {
type: "GET",
url: "/api/v1/get/mailq/all",
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>' +
function draw_queue() {
// just recalc width if instance already exists
if ($.fn.DataTable.isDataTable('#queuetable') ) {
$('#queuetable').DataTable().columns.adjust().responsive.recalc();
return;
}
$('#queuetable').DataTable({
responsive: true,
processing: true,
serverSide: false,
stateSave: true,
pageLength: pagination_size,
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>>",
language: lang_datatables,
ajax: {
type: "GET",
url: "/api/v1/get/mailq/all",
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>';
});
return data;
}
},
columns: [
{
// placeholder, so checkbox will not block child row toggle
title: '',
data: null,
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: '',
data: 'chkbox',
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: 'QID',
data: 'queue_id',
defaultContent: ''
},
{
title: 'Queue',
data: 'queue_name',
defaultContent: ''
},
{
title: lang_admin.arrival_time,
data: 'arrival_time',
defaultContent: '',
render: function (data, type){
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"});
}
},
{
title: lang_admin.message_size,
data: 'message_size',
defaultContent: '',
render: function (data, type){
return humanFileSize(data);
}
},
{
title: lang_admin.sender,
data: 'sender',
defaultContent: ''
},
{
title: lang_admin.recipients,
data: 'recipients',
defaultContent: ''
},
{
title: lang_admin.action,
data: 'action',
className: 'text-md-end dt-sm-head-hidden dt-body-right',
defaultContent: ''
},
{
// placeholder, so checkbox will not block child row toggle
title: '',
data: null,
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: '',
data: 'chkbox',
searchable: false,
orderable: false,
defaultContent: ''
},
{
title: 'QID',
data: 'queue_id',
defaultContent: ''
},
{
title: 'Queue',
data: 'queue_name',
defaultContent: ''
},
{
title: lang_admin.arrival_time,
data: 'arrival_time',
defaultContent: '',
render: function (data, type){
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"});
}
},
{
title: lang_admin.message_size,
data: 'message_size',
defaultContent: '',
render: function (data, type){
return humanFileSize(data);
}
},
{
title: lang_admin.sender,
data: 'sender',
defaultContent: ''
},
{
title: lang_admin.recipients,
data: 'recipients',
defaultContent: ''
},
{
title: lang_admin.action,
data: 'action',
className: 'dt-sm-head-hidden dt-text-right',
defaultContent: ''
},
]
});
}
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":
process_add_return(domain_admin('add', $attr));
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":
process_add_return(admin('add', $attr));
break;

View File

@@ -650,7 +650,7 @@
},
"login": {
"delayed": "Přihlášení zpožděno o %s sekund.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Přihlásit",
"mobileconfig_info": "Ke stažení profilového souboru se přihlaste jako uživatel schránky.",
"other_logins": "Přihlášení klíčem",

View File

@@ -4,15 +4,15 @@
"app_passwds": "Administrer app-adgangskoder",
"bcc_maps": "BCC kort",
"delimiter_action": "Afgrænsning handling",
"eas_reset": "Nulstil EAS endheder",
"eas_reset": "Nulstil EAS enheder",
"extend_sender_acl": "Tillad at udvide afsenderens ACL med eksterne adresser",
"filters": "Filtre",
"login_as": "Login som mailboks bruger",
"prohibited": "Forbudt af ACL",
"protocol_access": "Ændre protokol adgang",
"prohibited": "Nægtet af ACL",
"protocol_access": "Skift protokol adgang",
"pushover": "Pushover",
"quarantine": "Karantæneaktioner",
"quarantine_attachments": "Karantæne vedhæftede filer",
"quarantine": "Karantænehandlinger",
"quarantine_attachments": "Karantænevedhæftede filer",
"quarantine_notification": "Skift karantænemeddelelser",
"ratelimit": "Satsgrænse",
"recipient_maps": "Modtagerkort",
@@ -20,12 +20,15 @@
"sogo_access": "Tillad styring af SOGo-adgang",
"sogo_profile_reset": "Nulstil SOGo-profil",
"spam_alias": "Midlertidige aliasser",
"spam_policy": "Sortliste / hvidliste",
"spam_policy": "Sortliste/hvidliste",
"spam_score": "Spam-score",
"syncjobs": "Synkroniserings job",
"tls_policy": "TLS politik",
"unlimited_quota": "Ubegrænset plads for mailbokse",
"domain_desc": "Skift domæne beskrivelse"
"domain_desc": "Skift domæne beskrivelse",
"domain_relayhost": "Skift relæ host for et domæne",
"mailbox_relayhost": "Skift relæ-host for en postkasse",
"quarantine_category": "Skift kategorien for karantænemeddelelse"
},
"add": {
"activate_filter_warn": "Alle andre filtre deaktiveres, når aktiv er markeret.",
@@ -59,7 +62,7 @@
"gal": "Global adresseliste",
"gal_info": "GAL indeholder alle objekter i et domæne og kan ikke redigeres af nogen bruger. Information om ledig / optaget i SOGo mangler, hvis deaktiveret! <b> Genstart SOGo for at anvende ændringer. </b>",
"generate": "generere",
"goto_ham": "Lær som <span class=\"text-success\"><b>ham</b></span>",
"goto_ham": "Lær som <span class=\"text-success\"><b>ønsket</b></span>",
"goto_null": "Kassér e-mail i stilhed",
"goto_spam": "Lær som <span class=\"text-danger\"><b>spam</b></span>",
"hostname": "Vært",
@@ -308,7 +311,9 @@
"username": "Brugernavn",
"validate_license_now": "Valider GUID mod licensserver",
"verify": "Verificere",
"yes": "&#10003;"
"yes": "&#10003;",
"ip_check_opt_in": "Opt-In for brug af tredjepartstjeneste <strong>ipv4.mailcow.email</strong> og <strong>ipv6.mailcow.email</strong> til at finde eksterne IP-adresser.",
"queue_unban": "unban"
},
"danger": {
"access_denied": "Adgang nægtet eller ugyldig formular data",
@@ -425,7 +430,8 @@
"username_invalid": "Brugernavn %s kan ikke bruges",
"validity_missing": "Tildel venligst en gyldighedsperiode",
"value_missing": "Angiv alle værdier",
"yotp_verification_failed": "Yubico OTP verifikationen mislykkedes: %s"
"yotp_verification_failed": "Yubico OTP verifikationen mislykkedes: %s",
"webauthn_publickey_failed": "Der er ikke gemt nogen offentlig nøgle for den valgte autentifikator"
},
"debug": {
"chart_this_server": "Diagram (denne server)",
@@ -442,7 +448,8 @@
"solr_status": "Solr-status",
"started_on": "Startede den",
"static_logs": "Statiske logfiler",
"system_containers": "System og Beholdere"
"system_containers": "System og Beholdere",
"error_show_ip": "Kunne ikke finde de offentlige IP-adresser"
},
"diagnostics": {
"cname_from_a": "Værdi afledt af A / AAAA-post. Dette understøttes, så længe posten peger på den korrekte ressource.",
@@ -553,7 +560,11 @@
"title": "Rediger objekt",
"unchanged_if_empty": "Lad være tomt, hvis uændret",
"username": "Brugernavn",
"validate_save": "Valider og gem"
"validate_save": "Valider og gem",
"admin": "Rediger administrator",
"lookup_mx": "Destination er et regulært udtryk, der matcher MX-navnet (<code>.*google\\.dk</code> for at dirigere al e-mail, der er målrettet til en MX, der ender på google.dk, over dette hop)",
"mailbox_relayhost_info": "Anvendt på postkassen og kun direkte aliasser, og overskriver et domæne relæ-host.",
"quota_warning_bcc": "Kvoteadvarsel BCC"
},
"footer": {
"cancel": "Afbestille",
@@ -571,7 +582,7 @@
"header": {
"administration": "Konfiguration og detailer",
"apps": "Apps",
"debug": "Systemoplysninger",
"debug": "Information",
"email": "E-Mail",
"mailcow_config": "Konfiguration",
"quarantine": "Karantæne",
@@ -586,7 +597,7 @@
},
"login": {
"delayed": "Login blev forsinket med% s sekunder.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Login",
"mobileconfig_info": "Log ind som postkassebruger for at downloade den anmodede Apple-forbindelsesprofil.",
"other_logins": "Nøgle login",
@@ -739,7 +750,10 @@
"username": "Brugernavn",
"waiting": "Venter",
"weekly": "Ugentlig",
"yes": "&#10003;"
"yes": "&#10003;",
"goto_ham": "Lær som <b>ønsket</b>",
"catch_all": "Fang-alt",
"open_logs": "Åben logfiler"
},
"oauth2": {
"access_denied": "Log ind som mailboks ejer for at give adgang via OAuth2.",
@@ -1066,5 +1080,11 @@
"quota_exceeded_scope": "Domænekvote overskredet: Kun ubegrænsede postkasser kan oprettes i dette domæneomfang.",
"session_token": "Form nøgle ugyldig: Nøgle passer ikke",
"session_ua": "Form nøgle ugyldig: Bruger-Agent gyldighedskontrols fejl"
},
"datatables": {
"lengthMenu": "Vis _MENU_ poster",
"paginate": {
"first": "Først"
}
}
}

View File

@@ -339,7 +339,8 @@
"oauth2_add_client": "Füge OAuth2 Client hinzu",
"api_read_only": "Schreibgeschützter Zugriff",
"api_read_write": "Lese-Schreib-Zugriff",
"oauth2_apps": "OAuth2 Apps"
"oauth2_apps": "OAuth2 Apps",
"queue_unban": "entsperren"
},
"danger": {
"access_denied": "Zugriff verweigert oder unvollständige/ungültige Daten",
@@ -366,7 +367,7 @@
"domain_not_empty": "Domain %s ist nicht leer",
"domain_not_found": "Domain %s nicht gefunden",
"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_domain": "Externe Absenderadresse \"%s\" verwendet eine ungültige Domain",
"fido2_verification_failed": "FIDO2-Verifizierung fehlgeschlagen: %s",
@@ -454,17 +455,23 @@
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
"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_tfa_method": "Unbekannte TFA-Methode",
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
"username_invalid": "Benutzername %s kann nicht verwendet werden",
"validity_missing": "Bitte geben Sie eine Gültigkeitsdauer an",
"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": {
"collapse_all": "Alle Einklappen",
"decimal": "",
"decimal": ",",
"emptyTable": "Keine Daten in der Tabelle vorhanden",
"expand_all": "Alle Ausklappen",
"info": "_START_ bis _END_ von _TOTAL_ Einträgen",
@@ -498,7 +505,7 @@
"current_time": "Systemzeit",
"disk_usage": "Festplattennutzung",
"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",
"history_all_servers": "History (alle Server)",
"in_memory_logs": "In-memory Logs",
@@ -651,7 +658,8 @@
"title": "Objekt bearbeiten",
"unchanged_if_empty": "Unverändert, wenn leer",
"username": "Benutzername",
"validate_save": "Validieren und speichern"
"validate_save": "Validieren und speichern",
"pushover_sound": "Ton"
},
"fido2": {
"confirm": "Bestätigen",
@@ -692,7 +700,8 @@
"quarantine": "Quarantäne",
"restart_netfilter": "Netfilter neustarten",
"restart_sogo": "SOGo neustarten",
"user_settings": "Benutzereinstellungen"
"user_settings": "Benutzereinstellungen",
"mailcow_system": "System"
},
"info": {
"awaiting_tfa_confirmation": "Warte auf TFA-Verifizierung",
@@ -701,7 +710,7 @@
},
"login": {
"delayed": "Login wurde zur Sicherheit um %s Sekunde/n verzögert.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Anmelden",
"mobileconfig_info": "Bitte als Mailbox-Benutzer einloggen, um das Verbindungsprofil herunterzuladen.",
"other_logins": "Key Login",
@@ -1236,7 +1245,8 @@
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindungsproblem",
"syncjob_EXIT_TLS_FAILURE": "Problem mit verschlüsselter Verbindung",
"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": {
"cannot_delete_self": "Kann derzeit eingeloggten Benutzer nicht entfernen",

View File

@@ -458,6 +458,9 @@
"totp_verification_failed": "TOTP verification failed",
"transport_dest_exists": "Transport destination \"%s\" exists",
"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_tfa_method": "Unknown TFA method",
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
@@ -468,7 +471,7 @@
},
"datatables": {
"collapse_all": "Collapse All",
"decimal": "",
"decimal": ".",
"emptyTable": "No data available in table",
"expand_all": "Expand All",
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
@@ -707,7 +710,7 @@
},
"login": {
"delayed": "Login was delayed by %s seconds.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Login",
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
"other_logins": "Key login",

View File

@@ -612,7 +612,7 @@
},
"login": {
"delayed": "La connexion a été retardée de %s secondes.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Connexion",
"mobileconfig_info": "Veuillez vous connecter en tant quutilisateur de la boîte pour télécharger le profil de connexion Apple demandé.",
"other_logins": "Clé d'authentification",

View File

@@ -674,7 +674,7 @@
},
"login": {
"delayed": "L'accesso è stato ritardato di %s secondi.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Login",
"mobileconfig_info": "Please login as mailbox user to download the requested Apple connection profile.",
"other_logins": "Key login",

View File

@@ -3,7 +3,8 @@
"bcc_maps": "BCC kartes",
"filters": "Filtri",
"recipient_maps": "Saņēmēja kartes",
"syncjobs": "Sinhronizācijas uzdevumi"
"syncjobs": "Sinhronizācijas uzdevumi",
"spam_score": "Mēstules novērtējums"
},
"add": {
"activate_filter_warn": "Visi pārējie filtri tiks deaktivizēti, kad aktīvs ir atzīmēts.",
@@ -104,10 +105,10 @@
"host": "Hosts",
"import": "Importēt",
"import_private_key": "Importēt privātu atslēgu",
"in_use_by": "Tiek lietots ar",
"in_use_by": "Izmanto",
"inactive": "Neaktīvs",
"link": "Saite",
"loading": "Lūdzu uzgaidiet...",
"loading": "Lūgums uzgaidīt...",
"logo_info": "Jūsu attēls augšējā navigācijas joslā tiks palielināts līdz 40 pikseļiem un maks. sākumlapas platums par 250 pikseļi. Ir ļoti ieteicama pielāgojama grafikaYour image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. Ir ļoti ieteicama pielāgojamā grafika",
"main_name": "\"mailcow UI\" nosaukums",
"merged_vars_hint": "Pelēkās rindas tika apvienotas <code>vars.(local.)inc.php</code> un nevar tikt modificētas.",
@@ -144,7 +145,10 @@
"ui_texts": "UI etiķetes un teksti",
"unchanged_if_empty": "Ja nav veiktas izmaiņas, atstājiet tukšu",
"upload": "Augšupielādēt",
"username": "Lietotājvārds"
"username": "Lietotājvārds",
"generate": "izveidot",
"message": "Ziņojums",
"last_applied": "Pēdējoreiz pielietots"
},
"danger": {
"access_denied": "Piekļuve liegta, vai nepareizi dati",
@@ -170,7 +174,7 @@
"is_alias": "%s jau ir zināms alias",
"is_alias_or_mailbox": "%s jau ir zināms alias, pastkastes vai alias addrese izvērsta no alias domēna.",
"is_spam_alias": "%s ir jau zināms spam alias",
"last_key": "Pēdējā atslēga nevar būt dzēsta",
"last_key": "Pēdējo atslēgu nevar izdzēst, tā vietā jāatspējo divpakāpju pārbaude.",
"login_failed": "Ielogošanās neveiksmīga",
"mailbox_invalid": "Pastkastes vārds ir nederīgs",
"mailbox_quota_exceeded": "Kvota pārsniedz domēna limitu (max. %d MiB)",
@@ -262,7 +266,8 @@
"title": "Labot priekšmetu",
"unchanged_if_empty": "Ja neizmainīts atstājiet tukšu",
"username": "Lietotājvārds",
"validate_save": "Apstiprināt un saglabāt"
"validate_save": "Apstiprināt un saglabāt",
"last_modified": "Pēdējoreiz mainīts"
},
"footer": {
"cancel": "Atcelt",
@@ -314,21 +319,21 @@
"bcc_destinations": "BCC galamērķi/s",
"bcc_info": "BCC kartes tiek izmantotas, lai klusu pārsūtītu visu ziņojumu kopijas uz citu adresi. Saņēmēja kartes tipa ieraksts tiek izmantots, kad vietējais galamērķis darbojas kā pasta adresāts. Sūtītāja kartes atbilst vienam un tam pašam principam. <br/>\r\n   Vietējais galamērķis netiks informēts par piegādes neveiksmi. ",
"bcc_local_dest": "Vietējais galamērķis",
"bcc_map_type": "BCC tips",
"bcc_map_type": "BCC veids",
"bcc_maps": "BCC kartes",
"bcc_rcpt_map": "saņēmēja karte",
"bcc_sender_map": "Sūtītāja karte",
"bcc_to_rcpt": "Pārslēdzieties uz adresāta kartes tipu",
"bcc_to_sender": "Pārslēgties uz sūtītāja kartes tipu",
"bcc_type": "BCC tips",
"deactivate": "Deaktivizēt",
"deactivate": "Deaktivēt",
"description": "Apraksts",
"dkim_key_length": "DKIM atslēgas garums (bits)",
"domain": "Domēns",
"domain_admins": "Domēna administratori",
"domain_aliases": "Domēna aliases",
"domain_quota": "Kvota",
"domain_quota_total": "Kopējā domēna kvota",
"domain_quota_total": "Kopējais domēna ierobežojums",
"domains": "Domēns",
"edit": "Labot",
"empty": "Nav rezultātu",
@@ -341,7 +346,7 @@
"inactive": "Neaktīvs",
"kind": "Veids",
"last_run": "Pēdējā norise",
"last_run_reset": "Nākamais grafiks",
"last_run_reset": "Ievietot sarakstā kā nākamo",
"mailbox_quota": "Maks. pastkastes izmērs",
"mailboxes": "Pastkaste",
"max_aliases": "Maks. iespejamās aliases",
@@ -374,7 +379,13 @@
"tls_enforce_out": "Piespiest TLS izejošajiem",
"toggle_all": "Pārslēgt visu",
"username": "Lietotājvārds",
"waiting": "Gaidīšana"
"waiting": "Gaidīšana",
"last_modified": "Pēdējoreiz mainīts",
"booking_0_short": "Vienmēŗ bezmaksas",
"daily": "Ik dienu",
"hourly": "Ik stundu",
"last_mail_login": "Pēdējā pieteikšanās pastkastē",
"mailbox": "Pastkaste"
},
"quarantine": {
"action": "Darbības",
@@ -547,5 +558,14 @@
"waiting": "Waiting",
"week": "Nedēļa",
"weeks": "Nedēļas"
},
"datatables": {
"paginate": {
"first": "Pirmā",
"last": "Pēdējā"
}
},
"debug": {
"last_modified": "Pēdējoreiz mainīts"
}
}

View File

@@ -598,7 +598,7 @@
},
"login": {
"delayed": "Aanmelding vertraagd met %s seconden.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Aanmelden",
"mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.",
"other_logins": "Meld aan met key",

View File

@@ -1,7 +1,8 @@
{
"acl": {
"sogo_profile_reset": "Usuń profil SOGo (webmail)",
"syncjobs": "Polecenie synchronizacji"
"syncjobs": "Polecenie synchronizacji",
"alias_domains": "Dodaj aliasy domen"
},
"add": {
"active": "Aktywny",

View File

@@ -656,7 +656,7 @@
},
"login": {
"delayed": "Conectarea a fost întârziată cu %s secunde.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Autentificare",
"mobileconfig_info": "Autentificați-vă cu adresa de email pentru a descărca profilul de conexiune Apple.",
"other_logins": "Autentificare cu cheie",

View File

@@ -37,7 +37,7 @@
"add_domain_only": "Только добавить домен",
"add_domain_restart": "Добавить домен и перезапустить SOGo",
"alias_address": "Псевдоним/ы",
"alias_address_info": "<small>Укажите почтовые адреса разделенные запятыми или, если хотите пересылать все сообщения для домена владельцам псевдонима то: <code>@example.com</code>. <b>Только домены mailcow разрешены</b>.</small>",
"alias_address_info": "<small>Адрес(а) электронной почты (через запятую) или @example.com (для перехвата всех писем для домена). <b>только домены mailcow</b>.</small>",
"alias_domain": "Псевдоним домена",
"alias_domain_info": "<small>Действительные имена доменов, раздёленные запятыми.</small>",
"app_name": "Название приложения",
@@ -335,7 +335,8 @@
"username": "Имя пользователя",
"validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
"verify": "Проверить",
"yes": "&#10003;"
"yes": "&#10003;",
"queue_unban": "разблокировать"
},
"danger": {
"access_denied": "Доступ запрещён, или указаны неверные данные",
@@ -654,7 +655,7 @@
},
"login": {
"delayed": "Вход был отложен на %s секунд.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Войти",
"mobileconfig_info": "Пожалуйста, войдите в систему как пользователь почтового аккаунта для загрузки профиля подключения Apple.",
"other_logins": "Вход с помощью ключа",

View File

@@ -106,7 +106,8 @@
"username": "Používateľské meno",
"validate": "Overiť",
"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": {
"access": "Prístup",
@@ -656,7 +657,7 @@
},
"login": {
"delayed": "Prihlásenie bolo oneskorené o %s sekúnd.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Prihlásenie",
"mobileconfig_info": "Prosím, prihláste sa ako mailový používateľ pre stiahnutie požadovaného Apple profilu.",
"other_logins": "Prihlásenie kľúčom",

View File

@@ -618,7 +618,7 @@
},
"login": {
"delayed": "Av säkerhetsskäl har inloggning inaktiverats i %s sekunder.",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Logga in",
"mobileconfig_info": "Logga in som en användare av brevlåda för att ladda ner den begärda Apple-anslutningsprofilen.",
"other_logins": "Loggain med nyckel",

View File

@@ -656,7 +656,7 @@
"awaiting_tfa_confirmation": "В очікуванні підтвердження TFA"
},
"login": {
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "Увійти",
"other_logins": "Вхід за допомогою ключа",
"password": "Пароль",

View File

@@ -661,7 +661,7 @@
},
"login": {
"delayed": "请在 %s 秒后重新登录。",
"fido2_webauthn": "使用 FIDO2/WebAuthn 登录",
"fido2_webauthn": "使用 FIDO2/WebAuthn Login 登录",
"login": "登录",
"mobileconfig_info": "请使用邮箱用户登录以下载 Apple 连接描述文件。",
"other_logins": "Key 登录",

View File

@@ -655,7 +655,7 @@
},
"login": {
"delayed": "請在 %s 秒後重新登入。",
"fido2_webauthn": "FIDO2/WebAuthn",
"fido2_webauthn": "FIDO2/WebAuthn Login",
"login": "登入",
"mobileconfig_info": "請使用信箱使用者登入以下載 Apple 連接描述檔案。",
"other_logins": "金鑰登入",

View File

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

View File

@@ -38,15 +38,8 @@
</div>
</div>
<div class="d-flex mt-4" style="position: relative">
<div class="btn-group">
<div class="btn-group">
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
<button type="button" class="btn btn-xs-lg btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</a></li>
</ul>
</div>
</div>
<button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button>
<button type="button" class="btn btn-xs-lg btn-success ms-2" id="fido2-login"><i class="bi bi-shield-fill-check"></i> {{ lang.login.fido2_webauthn }}</button>
{% if not oauth2_request %}
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-xs-lg btn-secondary ms-auto dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span>

View File

@@ -19,7 +19,7 @@
</li>
<li class="nav-item" role="presentation"><button class="nav-link" aria-controls="tab-resources" role="tab" data-bs-toggle="tab" data-bs-target="#tab-resources">{{ lang.mailbox.resources }}</button></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-bs-target="#">{{ lang.mailbox.aliases }}</a>
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.aliases }}</a>
<ul class="dropdown-menu">
<li role="presentation"><button class="dropdown-item" aria-selected="false" aria-controls="tab-mbox-aliases" role="tab" data-bs-toggle="tab" data-bs-target="#tab-mbox-aliases">{{ lang.mailbox.aliases }}</button></li>
<li role="presentation"><button class="dropdown-item" aria-selected="false" aria-controls="tab-domain-aliases" role="tab" data-bs-toggle="tab" data-bs-target="#tab-domain-aliases">{{ lang.mailbox.domain_aliases }}</button></li>
@@ -58,7 +58,7 @@
var lang_rl = {{ lang_rl|raw }};
var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}';
var pagination_size = Math.trunc('{{ pagination_size }}');
var role = '{{ role }}';
var is_dual = {{ is_dual }};
var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }};

View File

@@ -37,7 +37,7 @@
</p>
{% endif %}
</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="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>
@@ -66,7 +66,7 @@ var acl = '{{ acl_json|raw }}';
var lang = {{ lang_quarantine|raw }};
var lang_datatables = {{ lang_datatables|raw }};
var csrf_token = '{{ csrf_token }}';
var pagination_size = '{{ pagination_size }}';
var pagination_size = Math.trunc('{{ pagination_size }}');
var role = '{{ role }}';
</script>
{% endblock %}

View File

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

View File

@@ -4,7 +4,7 @@
var acl = '{{ acl_json|raw }}';
var lang = {{ lang_user|raw }};
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 user_spam_score = [{{ user_spam_score }}];
var lang_datatables = {{ lang_datatables|raw }};

View File

@@ -58,7 +58,7 @@ services:
- redis
clamd-mailcow:
image: mailcow/clamd:1.60
image: mailcow/clamd:1.61
restart: always
depends_on:
- unbound-mailcow
@@ -169,7 +169,7 @@ services:
- phpfpm
sogo-mailcow:
image: mailcow/sogo:1.114
image: mailcow/sogo:1.115
environment:
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
@@ -216,7 +216,7 @@ services:
- sogo
dovecot-mailcow:
image: mailcow/dovecot:1.22
image: mailcow/dovecot:1.23
depends_on:
- mysql-mailcow
dns:
@@ -425,7 +425,7 @@ services:
- acme
netfilter-mailcow:
image: mailcow/netfilter:1.50
image: mailcow/netfilter:1.51
stop_grace_period: 30s
depends_on:
- dovecot-mailcow

15
helper-scripts/expiry-dates.sh Normal file → Executable file
View File

@@ -3,10 +3,11 @@
[[ -f mailcow.conf ]] && source mailcow.conf
[[ -f ../mailcow.conf ]] && source ../mailcow.conf
POSTFIX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:25 -starttls smtp 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
DOVECOT=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:143 -starttls imap 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
NGINX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:443 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
echo TLS expiry dates:
echo Postfix: ${POSTFIX}
echo Dovecot: ${DOVECOT}
echo Nginx: ${NGINX}
POSTFIX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:${SMTP_PORT} -starttls smtp 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
DOVECOT=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:${IMAP_PORT} -starttls imap 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
NGINX=$(echo | openssl s_client -connect ${MAILCOW_HOSTNAME}:${HTTPS_PORT} 2>/dev/null | openssl x509 -inform pem -noout -enddate | cut -d "=" -f 2)
echo "TLS expiry dates:"
echo "Postfix: ${POSTFIX}"
echo "Dovecot: ${DOVECOT}"
echo "Nginx: ${NGINX}"

View File

@@ -1,10 +1,13 @@
#!/usr/bin/env bash
# renovate: datasource=github-releases depName=nextcloud/server versioning=semver extractVersion=^v(?<version>.*)$
NEXTCLOUD_VERSION=25.0.3
NEXTCLOUD_VERSION=25.0.4
for bin in curl dirmngr; do
if [[ -z $(which ${bin}) ]]; then echo "Cannot find ${bin}, exiting..."; exit 1; fi
echo -ne "Checking prerequisites..."
sleep 1
for bin in curl dirmngr tar bzip2; do
if [[ -z $(which ${bin}) ]]; then echo -ne "\r\033[31mCannot find ${bin}, exiting...\033[0m\n"; exit 1; fi
done
echo -ne "\r\033[32mFound all prerequisites! Continuing...\033[0m\n"
[[ -z ${1} ]] && NC_HELP=y
@@ -215,5 +218,4 @@ elif [[ ${NC_RESETPW} == "y" ]]; then
read -p "Enter the username: " NC_USER
done
docker exec -it -u www-data $(docker ps -f name=php-fpm-mailcow -q) /web/nextcloud/occ user:resetpassword ${NC_USER}
fi