From 3522f3e83f85d75d0dbf5e7fa9647166a0f65507 Mon Sep 17 00:00:00 2001
From: "Tobias \"Knight\" S"
Date: Sun, 5 Aug 2018 04:54:09 +0200
Subject: [PATCH 001/109] kitterman.com redirects to TLS by default
curl -I http://www.kitterman.com/dmarc/assistant.html
HTTP/1.1 302 Found
Date: Sun, 05 Aug 2018 02:53:18 GMT
Server: Apache/2.4.34
Location: https://www.kitterman.com/dmarc/assistant.html
Content-Type: text/html; charset=iso-8859-1
---
data/web/inc/ajax/dns_diagnostics.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data/web/inc/ajax/dns_diagnostics.php b/data/web/inc/ajax/dns_diagnostics.php
index 45ff07e2b2..dd67dec943 100644
--- a/data/web/inc/ajax/dns_diagnostics.php
+++ b/data/web/inc/ajax/dns_diagnostics.php
@@ -74,7 +74,7 @@
// Init records array
$spf_link = 'SPF Record Syntax
';
-$dmarc_link = 'DMARC Assistant';
+$dmarc_link = 'DMARC Assistant';
$records = array();
if ($_SESSION['mailcow_cc_role'] == "admin") {
From 73f880ffba7ee709e9b20ce3c52c9ffe6c0fd5b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Sun, 5 Aug 2018 22:31:53 +0200
Subject: [PATCH 002/109] [Web] Workaround for missing function when using API
(fixes #1640) [Web] Fixes SQL error on admin username change
---
data/web/inc/functions.inc.php | 42 ----------------------------
data/web/inc/init_db.inc.php | 4 +--
data/web/inc/prerequisites.inc.php | 44 ++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 44 deletions(-)
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 3ca51b452a..d99661cb09 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -3,48 +3,6 @@ function hash_password($password) {
$salt_str = bin2hex(openssl_random_pseudo_bytes(8));
return "{SSHA256}".base64_encode(hash('sha256', $password . $salt_str, true) . $salt_str);
}
-function get_remote_ip($anonymize = null) {
- global $ANONYMIZE_IPS;
- if ($anonymize === null) {
- $anonymize = $ANONYMIZE_IPS;
- }
- elseif ($anonymize !== true && $anonymize !== false) {
- $anonymize = true;
- }
- $remote = '';
- if ($_SERVER['HTTP_CLIENT_IP']) {
- $remote = $_SERVER['HTTP_CLIENT_IP'];
- }
- elseif ($_SERVER['HTTP_X_FORWARDED_FOR']) {
- $remote = $_SERVER['HTTP_X_FORWARDED_FOR'];
- }
- elseif ($_SERVER['HTTP_X_FORWARDED']) {
- $remote = $_SERVER['HTTP_X_FORWARDED'];
- }
- elseif ($_SERVER['HTTP_FORWARDED_FOR']) {
- $remote = $_SERVER['HTTP_FORWARDED_FOR'];
- }
- elseif ($_SERVER['HTTP_FORWARDED']) {
- $remote = $_SERVER['HTTP_FORWARDED'];
- }
- elseif ($_SERVER['REMOTE_ADDR']) {
- $remote = $_SERVER['REMOTE_ADDR'];
- }
- if (filter_var($remote, FILTER_VALIDATE_IP) === false) {
- return '0.0.0.0';
- }
- if ($anonymize) {
- if (strlen(inet_pton($remote)) == 4) {
- return inet_ntop(inet_pton($remote) & inet_pton("255.255.255.0"));
- }
- elseif (strlen(inet_pton($remote)) == 16) {
- return inet_ntop(inet_pton($remote) & inet_pton('ffff:ffff:ffff:ffff:0000:0000:0000:0000'));
- }
- }
- else {
- return $remote;
- }
-}
function last_login($user) {
global $pdo;
try {
diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php
index a3b67283e6..b938a56b92 100644
--- a/data/web/inc/init_db.inc.php
+++ b/data/web/inc/init_db.inc.php
@@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
- $db_version = "31072018_2319";
+ $db_version = "05072018_2319";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -147,7 +147,7 @@ function init_db_schema() {
"col" => "username",
"ref" => "admin.username",
"delete" => "CASCADE",
- "update" => "NO ACTION"
+ "update" => "CASCADE"
)
)
),
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index ad201a687b..1370155e7f 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -52,6 +52,50 @@
exit;
}
+// TODO: Move function
+function get_remote_ip($anonymize = null) {
+ global $ANONYMIZE_IPS;
+ if ($anonymize === null) {
+ $anonymize = $ANONYMIZE_IPS;
+ }
+ elseif ($anonymize !== true && $anonymize !== false) {
+ $anonymize = true;
+ }
+ $remote = '';
+ if ($_SERVER['HTTP_CLIENT_IP']) {
+ $remote = $_SERVER['HTTP_CLIENT_IP'];
+ }
+ elseif ($_SERVER['HTTP_X_FORWARDED_FOR']) {
+ $remote = $_SERVER['HTTP_X_FORWARDED_FOR'];
+ }
+ elseif ($_SERVER['HTTP_X_FORWARDED']) {
+ $remote = $_SERVER['HTTP_X_FORWARDED'];
+ }
+ elseif ($_SERVER['HTTP_FORWARDED_FOR']) {
+ $remote = $_SERVER['HTTP_FORWARDED_FOR'];
+ }
+ elseif ($_SERVER['HTTP_FORWARDED']) {
+ $remote = $_SERVER['HTTP_FORWARDED'];
+ }
+ elseif ($_SERVER['REMOTE_ADDR']) {
+ $remote = $_SERVER['REMOTE_ADDR'];
+ }
+ if (filter_var($remote, FILTER_VALIDATE_IP) === false) {
+ return '0.0.0.0';
+ }
+ if ($anonymize) {
+ if (strlen(inet_pton($remote)) == 4) {
+ return inet_ntop(inet_pton($remote) & inet_pton("255.255.255.0"));
+ }
+ elseif (strlen(inet_pton($remote)) == 16) {
+ return inet_ntop(inet_pton($remote) & inet_pton('ffff:ffff:ffff:ffff:0000:0000:0000:0000'));
+ }
+ }
+ else {
+ return $remote;
+ }
+}
+
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// Set language
From ff0b9246b5ca0fa8f3e37243596f8d311e8cc8dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Sun, 5 Aug 2018 22:35:02 +0200
Subject: [PATCH 003/109] [ClamAV] Add whitelist file for ClamAV, fixes #1607
[ACME] Test for CAA before running script, fixes #1632
---
data/Dockerfiles/acme/docker-entrypoint.sh | 26 +++++++++++++++++++++-
data/Dockerfiles/clamd/bootstrap.sh | 4 ++++
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh
index 7f4f1fcbe3..08d234bf50 100755
--- a/data/Dockerfiles/acme/docker-entrypoint.sh
+++ b/data/Dockerfiles/acme/docker-entrypoint.sh
@@ -67,7 +67,7 @@ get_ipv4(){
local IPV4_SRCS=
local TRY=
IPV4_SRCS[0]="api.ipify.org"
- IPV4_SRCS[1]="ifconfig.co"-
+ IPV4_SRCS[1]="ifconfig.co"
IPV4_SRCS[2]="icanhazip.com"
IPV4_SRCS[3]="v4.ident.me"
IPV4_SRCS[4]="ipecho.net/plain"
@@ -153,6 +153,19 @@ while true; do
IPV6=$(get_ipv6)
log_f "OK" no_date
+ # Hard-fail on CAA errors for MAILCOW_HOSTNAME
+ MH_PARENT_DOMAIN=$(echo ${MAILCOW_HOSTNAME} | cut -d. -f2-)
+ MH_CAAS=( $(dig CAA ${MH_PARENT_DOMAIN} +short | sed -n 's/\d issue "\(.*\)"/\1/p') )
+ if [[ ! -z ${MH_CAAS} ]]; then
+ if [[ ${MH_CAAS[@]} =~ "letsencrypt.org" ]]; then
+ echo "Validated CAA for parent domain ${MH_PARENT_DOMAIN}"
+ else
+ echo "Skipping ACME validation: Lets Encrypt disallowed for ${MAILCOW_HOSTNAME} by CAA record, retrying in 1h..."
+ sleep 1h
+ exec $(readlink -f "$0")
+ fi
+ fi
+
# Container ids may have changed
CONTAINERS_RESTART=($(curl --silent http://dockerapi:8080/containers/json | jq -r '.[] | {name: .Config.Labels["com.docker.compose.service"], id: .Id}' | jq -rc 'select( .name | tostring | contains("nginx-mailcow") or contains("postfix-mailcow") or contains("dovecot-mailcow")) | .id' | tr "\n" " "))
@@ -249,6 +262,17 @@ while true; do
fi
for SAN in "${ADDITIONAL_SAN_ARR[@]}"; do
+ # Skip on CAA errors for SAN
+ SAN_PARENT_DOMAIN=$(echo ${SAN} | cut -d. -f2-)
+ SAN_CAAS=( $(dig CAA ${SAN_PARENT_DOMAIN} +short | sed -n 's/\d issue "\(.*\)"/\1/p') )
+ if [[ ! -z ${SAN_CAAS} ]]; then
+ if [[ ${SAN_CAAS[@]} =~ "letsencrypt.org" ]]; then
+ echo "Validated CAA for parent domain ${SAN_PARENT_DOMAIN} of ${SAN}"
+ else
+ echo "Skipping ACME validation for ${SAN}: Lets Encrypt disallowed for ${SAN} by CAA record"
+ continue
+ fi
+ fi
if [[ ${SAN} == ${MAILCOW_HOSTNAME} ]]; then
continue
fi
diff --git a/data/Dockerfiles/clamd/bootstrap.sh b/data/Dockerfiles/clamd/bootstrap.sh
index 76a6b79bd4..ba1cee85a1 100755
--- a/data/Dockerfiles/clamd/bootstrap.sh
+++ b/data/Dockerfiles/clamd/bootstrap.sh
@@ -14,6 +14,10 @@ chown root:tty /dev/console
chmod g+rw /dev/console
# Prepare
+[[ ! -f /var/lib/clamav/whitelist.ign2 ]] && touch /var/lib/clamav/whitelist.ign2
+dos2unix /var/lib/clamav/whitelist.ign2
+sed -i '/^\s*$/d' /var/lib/clamav/whitelist.ign2
+
BACKGROUND_TASKS=()
(
From 66d1bc12c09c519c48e5e369e8d73b301f2fde76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Sun, 5 Aug 2018 22:37:07 +0200
Subject: [PATCH 004/109] [Nginx] Set client_max_body_size = 0
---
data/conf/nginx/site.conf | 2 ++
1 file changed, 2 insertions(+)
diff --git a/data/conf/nginx/site.conf b/data/conf/nginx/site.conf
index babf5ce3f3..d25d3b6155 100644
--- a/data/conf/nginx/site.conf
+++ b/data/conf/nginx/site.conf
@@ -39,6 +39,8 @@ server {
index index.php index.html;
+ client_max_body_size 0;
+
include /etc/nginx/conf.d/listen_plain.active;
include /etc/nginx/conf.d/listen_ssl.active;
include /etc/nginx/conf.d/server_name.active;
From 0273f95950442425807bb9b8590982c9c10ed078 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Sun, 5 Aug 2018 22:37:49 +0200
Subject: [PATCH 005/109] [Compose] New images for ClamAV (+mount whitelist
file) and ACME
---
docker-compose.yml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index c1ab99d7ad..b7fd62eb29 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -53,7 +53,7 @@ services:
- redis
clamd-mailcow:
- image: mailcow/clamd:1.12
+ image: mailcow/clamd:1.13
build: ./data/Dockerfiles/clamd
restart: always
tty: true
@@ -62,6 +62,7 @@ services:
- SKIP_CLAMD=${SKIP_CLAMD:-n}
volumes:
- ./data/conf/clamav/:/etc/clamav/
+ - ./data/conf/clamav/whitelist.ign2:/var/lib/clamav/whitelist.ign2
dns:
- ${IPV4_NETWORK:-172.22.1}.254
networks:
@@ -278,7 +279,7 @@ services:
depends_on:
- nginx-mailcow
- mysql-mailcow
- image: mailcow/acme:1.35
+ image: mailcow/acme:1.36
build: ./data/Dockerfiles/acme
dns:
- ${IPV4_NETWORK:-172.22.1}.254
From d83537cda0f34ab8f0239b6e2632c6b93b768b46 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Sun, 5 Aug 2018 22:38:06 +0200
Subject: [PATCH 006/109] [ClamAV] Add whitelist template for ClamAV
---
data/conf/clamav/whitelist.ign2 | 1 +
1 file changed, 1 insertion(+)
create mode 100644 data/conf/clamav/whitelist.ign2
diff --git a/data/conf/clamav/whitelist.ign2 b/data/conf/clamav/whitelist.ign2
new file mode 100644
index 0000000000..70ef98b9f3
--- /dev/null
+++ b/data/conf/clamav/whitelist.ign2
@@ -0,0 +1 @@
+Example-Signature.Ignore-1
From 6bd818ceeccf358be1a9beb9e589b2989149cbc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Mon, 6 Aug 2018 15:15:24 +0200
Subject: [PATCH 007/109] [ACME] Remove third-party IP tools
---
data/Dockerfiles/acme/docker-entrypoint.sh | 14 ++++----------
docker-compose.yml | 2 +-
2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh
index 08d234bf50..c9a91dfa79 100755
--- a/data/Dockerfiles/acme/docker-entrypoint.sh
+++ b/data/Dockerfiles/acme/docker-entrypoint.sh
@@ -66,12 +66,8 @@ get_ipv4(){
local IPV4=
local IPV4_SRCS=
local TRY=
- IPV4_SRCS[0]="api.ipify.org"
- IPV4_SRCS[1]="ifconfig.co"
- IPV4_SRCS[2]="icanhazip.com"
- IPV4_SRCS[3]="v4.ident.me"
- IPV4_SRCS[4]="ipecho.net/plain"
- IPV4_SRCS[5]="ip4.mailcow.email"
+ IPV4_SRCS[0]="ip4.mailcow.email"
+ IPV4_SRCS[1]="ip4.korves.net"
until [[ ! -z ${IPV4} ]] || [[ ${TRY} -ge 10 ]]; do
IPV4=$(curl --connect-timeout 3 -m 10 -L4s ${IPV4_SRCS[$RANDOM % ${#IPV4_SRCS[@]} ]} | grep -E "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")
[[ ! -z ${TRY} ]] && sleep 1
@@ -84,10 +80,8 @@ get_ipv6(){
local IPV6=
local IPV6_SRCS=
local TRY=
- IPV6_SRCS[0]="ifconfig.co"
- IPV6_SRCS[1]="icanhazip.com"
- IPV6_SRCS[2]="v6.ident.me"
- IPV6_SRCS[3]="ip6.mailcow.email"
+ IPV6_SRCS[0]="ip6.korves.net"
+ IPV6_SRCS[1]="ip6.mailcow.email"
until [[ ! -z ${IPV6} ]] || [[ ${TRY} -ge 10 ]]; do
IPV6=$(curl --connect-timeout 3 -m 10 -L6s ${IPV6_SRCS[$RANDOM % ${#IPV6_SRCS[@]} ]} | grep "^\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}$")
[[ ! -z ${TRY} ]] && sleep 1
diff --git a/docker-compose.yml b/docker-compose.yml
index b7fd62eb29..e01bf2d3b3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -279,7 +279,7 @@ services:
depends_on:
- nginx-mailcow
- mysql-mailcow
- image: mailcow/acme:1.36
+ image: mailcow/acme:1.37
build: ./data/Dockerfiles/acme
dns:
- ${IPV4_NETWORK:-172.22.1}.254
From 02e567f76bedca07712d3e6e109bf5f978e7bfdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Wed, 8 Aug 2018 23:59:38 +0200
Subject: [PATCH 008/109] [Dovecot] Set CONTROL path for shared namespace and
remove index
---
data/conf/dovecot/dovecot.conf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf
index 820a825b4b..9b38aa3574 100644
--- a/data/conf/dovecot/dovecot.conf
+++ b/data/conf/dovecot/dovecot.conf
@@ -175,7 +175,7 @@ namespace {
type = shared
separator = /
prefix = Shared/%%u/
- location = maildir:%%h/:INDEX=~/Shared/%%u:INDEXPVT=~/Shared/%%u
+ location = maildir:%%h/:CONTROL=~/Shared/%%u:INDEXPVT=~/Shared/%%u
subscriptions = no
list = children
}
From d5e81b987b7150046559db9ac4a6f1d7bdec6912 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Mon, 13 Aug 2018 08:31:09 +0200
Subject: [PATCH 009/109] [Dovecot] Set from address for sieve generated
addresses, fixes #1662
---
data/conf/dovecot/dovecot.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/data/conf/dovecot/dovecot.conf b/data/conf/dovecot/dovecot.conf
index 9b38aa3574..187c61b49c 100644
--- a/data/conf/dovecot/dovecot.conf
+++ b/data/conf/dovecot/dovecot.conf
@@ -262,6 +262,7 @@ plugin {
quota_rule2 = Trash:storage=+100%%
sieve = /var/vmail/sieve/%u.sieve
sieve_plugins = sieve_imapsieve sieve_extprograms
+ sieve_vacation_send_from_recipient = yes
# From elsewhere to Spam folder
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY
From a11cce67654e7cd8a268b58592a25801e710861d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Mon, 13 Aug 2018 23:20:40 +0200
Subject: [PATCH 010/109] [Web] Fixes for BCC map input fields [Web] Allow to
edit alias address [Web] $_SESSION['return'] now contains arrays and allows
multiple returned messages and log entries [Web] Some language string changes
[Web] General SQL exception handler, remove all try catch handlers [Web]
Alias table now has an ID as primary key [Web] Be more aggressive with
localStorage cleaning
---
data/web/admin.php | 2 +-
data/web/autodiscover.php | 4 +-
data/web/edit.php | 20 +-
data/web/inc/footer.inc.php | 6 +-
.../inc/functions.address_rewriting.inc.php | 259 +-
.../inc/functions.autoconfiguration.inc.php | 119 -
data/web/inc/functions.customize.inc.php | 38 +-
data/web/inc/functions.dkim.inc.php | 56 +-
data/web/inc/functions.docker.inc.php | 8 +-
data/web/inc/functions.domain_admin.inc.php | 291 +-
data/web/inc/functions.fail2ban.inc.php | 56 +-
data/web/inc/functions.fwdhost.inc.php | 46 +-
data/web/inc/functions.inc.php | 493 +--
data/web/inc/functions.mailbox.inc.php | 3871 +++++++----------
data/web/inc/functions.policy.inc.php | 258 +-
data/web/inc/functions.quarantine.inc.php | 314 +-
data/web/inc/functions.relayhost.inc.php | 101 +-
data/web/inc/functions.rsettings.inc.php | 84 +-
data/web/inc/init_db.inc.php | 15 +-
data/web/inc/prerequisites.inc.php | 19 +
data/web/inc/sessions.inc.php | 4 +-
data/web/js/api.js | 21 +-
data/web/js/debug.js | 2 +
data/web/js/mailbox.js | 7 +-
data/web/json_api.php | 6 +-
data/web/lang/lang.de.php | 51 +-
data/web/lang/lang.en.php | 54 +-
data/web/modals/admin.php | 4 +-
data/web/modals/mailbox.php | 4 +-
data/web/quarantine.php | 2 +
30 files changed, 2703 insertions(+), 3512 deletions(-)
delete mode 100644 data/web/inc/functions.autoconfiguration.inc.php
diff --git a/data/web/admin.php b/data/web/admin.php
index ce3deb69ad..d97bc141d2 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -261,7 +261,7 @@
else {
?>
-
+
↳ Alias-Domain: =htmlspecialchars($alias_domain);?>
=$lang['admin']['dkim_key_missing'];?>
diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php
index 391b4a9303..f5bcbf8fec 100644
--- a/data/web/autodiscover.php
+++ b/data/web/autodiscover.php
@@ -74,7 +74,7 @@
$redis->lTrim('AUTODISCOVER_LOG', 0, 100);
}
catch (RedisException $e) {
- $_SESSION['return'] = array(
+ $_SESSION['return'][] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
@@ -128,7 +128,7 @@
$redis->lTrim('AUTODISCOVER_LOG', 0, 100);
}
catch (RedisException $e) {
- $_SESSION['return'] = array(
+ $_SESSION['return'][] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
diff --git a/data/web/edit.php b/data/web/edit.php
index d605d3f458..1efbbed3b2 100644
--- a/data/web/edit.php
+++ b/data/web/edit.php
@@ -28,6 +28,12 @@
diff --git a/data/web/img/cow_mailcow.svg b/data/web/img/cow_mailcow.svg
index bea3f82630..d457782144 100644
--- a/data/web/img/cow_mailcow.svg
+++ b/data/web/img/cow_mailcow.svg
@@ -13,9 +13,9 @@
id="Layer_1"
x="0px"
y="0px"
- width="446.22601"
- height="396.02499"
- viewBox="0 0 446.226 396.02499"
+ width="374.82101"
+ height="356.871"
+ viewBox="0 0 374.821 356.871"
enable-background="new 0 0 1600 1200"
xml:space="preserve"
inkscape:version="0.91 r13725"
@@ -36,19 +36,19 @@
inkscape:window-height="1138"
id="namedview140"
showgrid="false"
- inkscape:zoom="1.1125147"
- inkscape:cx="261.00704"
- inkscape:cy="233.97883"
+ inkscape:zoom="1.5733334"
+ inkscape:cx="264.82839"
+ inkscape:cy="340.43592"
inkscape:window-x="814"
inkscape:window-y="0"
inkscape:window-maximized="0"
- inkscape:current-layer="Layer_1"
+ inkscape:current-layer="g3"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" /> 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
- );
- return false;
- }
- if ($redis->hGet('DKIM_PUB_KEYS', $domain)) {
+ $domains = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['domains']));
+ $domains = array_filter($domains);
+ foreach ($domains as $domain) {
+ if (!is_valid_domain_name($domain) || !is_numeric($key_length)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
- return false;
- }
- if (!ctype_alnum($dkim_selector)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
- );
- return false;
- }
- $config = array(
- "digest_alg" => "sha256",
- "private_key_bits" => $key_length,
- "private_key_type" => OPENSSL_KEYTYPE_RSA,
- );
- if ($keypair_ressource = openssl_pkey_new($config)) {
- $key_details = openssl_pkey_get_details($keypair_ressource);
- $pubKey = implode(array_slice(
- array_filter(
- explode(PHP_EOL, $key_details['key'])
- ), 1, -1)
+ continue;
+ }
+ if ($redis->hGet('DKIM_PUB_KEYS', $domain)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data),
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
- // Save public key and selector to redis
- try {
- $redis->hSet('DKIM_PUB_KEYS', $domain, $pubKey);
- $redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
+ continue;
}
- catch (RedisException $e) {
+ if (!ctype_alnum($dkim_selector)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => array('redis_error', $e)
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
- return false;
+ continue;
}
- // Export private key and save private key to redis
- openssl_pkey_export($keypair_ressource, $privKey);
- if (isset($privKey) && !empty($privKey)) {
+ $config = array(
+ "digest_alg" => "sha256",
+ "private_key_bits" => $key_length,
+ "private_key_type" => OPENSSL_KEYTYPE_RSA,
+ );
+ if ($keypair_ressource = openssl_pkey_new($config)) {
+ $key_details = openssl_pkey_get_details($keypair_ressource);
+ $pubKey = implode(array_slice(
+ array_filter(
+ explode(PHP_EOL, $key_details['key'])
+ ), 1, -1)
+ );
+ // Save public key and selector to redis
try {
- $redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, trim($privKey));
+ $redis->hSet('DKIM_PUB_KEYS', $domain, $pubKey);
+ $redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
@@ -77,23 +65,79 @@ function dkim($_action, $_data = null) {
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
- return false;
+ continue;
+ }
+ // Export private key and save private key to redis
+ openssl_pkey_export($keypair_ressource, $privKey);
+ if (isset($privKey) && !empty($privKey)) {
+ try {
+ $redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, trim($privKey));
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
}
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data),
+ 'msg' => array('dkim_added', $domain)
+ );
+ }
+ else {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data),
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
+ );
+ continue;
}
+ }
+ break;
+ case 'duplicate':
+ if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
- 'type' => 'success',
+ 'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_added'
+ 'msg' => 'access_denied'
);
- return true;
+ return false;
}
- else {
+ $from_domain = $_data['from_domain'];
+ $from_domain_dkim = dkim('details', $from_domain);
+ if (empty($from_domain_dkim)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
+ 'msg' => array('dkim_domain_or_sel_invalid', $from_domain)
+ );
+ continue;
+ }
+ $to_domains = (array)$_data['to_domain'];
+ $to_domains = array_filter($to_domains);
+ foreach ($to_domains as $to_domain) {
+ try {
+ $redis->hSet('DKIM_PUB_KEYS', $to_domain, $from_domain_dkim['pubkey']);
+ $redis->hSet('DKIM_SELECTORS', $to_domain, $from_domain_dkim['dkim_selector']);
+ $redis->hSet('DKIM_PRIV_KEYS', $from_domain_dkim['dkim_selector'] . '.' . $to_domain, trim($from_domain_dkim['privkey']));
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_data),
+ 'msg' => array('dkim_duplicated', $from_domain, $to_domain)
);
- return false;
}
break;
case 'import':
@@ -129,7 +173,7 @@ function dkim($_action, $_data = null) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
@@ -137,7 +181,7 @@ function dkim($_action, $_data = null) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
@@ -145,7 +189,7 @@ function dkim($_action, $_data = null) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_domain_or_sel_invalid'
+ 'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
@@ -178,7 +222,7 @@ function dkim($_action, $_data = null) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
- 'msg' => 'dkim_added'
+ 'msg' => array('dkim_added', $domain)
);
return true;
break;
@@ -203,13 +247,7 @@ function dkim($_action, $_data = null) {
}
$dkimdata['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data;
$dkimdata['dkim_selector'] = $redis->hGet('DKIM_SELECTORS', $_data);
- $dkimdata['privkey'] = $redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . $_data);
- if ($GLOBALS['SHOW_DKIM_PRIV_KEYS'] === true) {
- $dkimdata['privkey'] = base64_encode($redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . '.' . $_data));
- }
- else {
- $dkimdata['privkey'] = base64_encode('Please set $SHOW_DKIM_PRIV_KEYS to true to show DKIM private keys.');
- }
+ $dkimdata['privkey'] = base64_encode($redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . '.' . $_data));
}
return $dkimdata;
break;
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 92d55e8b0b..0db7259450 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -1291,86 +1291,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
}
break;
- case 'ratelimit':
- if (!is_array($_data['object'])) {
- $objects = array();
- $objects[] = $_data['object'];
- }
- else {
- $objects = $_data['object'];
- }
- foreach ($objects as $object) {
- $rl_value = intval($_data['rl_value']);
- $rl_frame = $_data['rl_frame'];
- if (!in_array($rl_frame, array('s', 'm', 'h'))) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => 'rl_timeframe'
- );
- continue;
- }
- if (is_valid_domain_name($object)) {
- if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => 'access_denied'
- );
- continue;
- }
- }
- elseif (filter_var($object, FILTER_VALIDATE_EMAIL)) {
- if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => 'access_denied'
- );
- continue;
- }
- }
- else {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => 'access_denied'
- );
- continue;
- }
- if (empty($rl_value)) {
- try {
- $redis->hDel('RL_VALUE', $object);
- }
- catch (RedisException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => array('redis_error', $e)
- );
- continue;
- }
- }
- else {
- try {
- $redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
- }
- catch (RedisException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => array('redis_error', $e)
- );
- continue;
- }
- }
- $_SESSION['return'][] = array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => array('object_modified', $object)
- );
- }
- break;
case 'syncjob':
if (!is_array($_data['id'])) {
$ids = array();
@@ -2670,51 +2590,6 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
}
return $aliases;
break;
- case 'ratelimit':
- if (is_valid_domain_name($_data)) {
- if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => 'access_denied'
- );
- return false;
- }
- }
- elseif (filter_var($_data, FILTER_VALIDATE_EMAIL)) {
- if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => 'access_denied'
- );
- return false;
- }
- }
- else {
- return false;
- }
- try {
- if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
- $rl = explode(' / 1', $rl_value);
- $data['value'] = $rl[0];
- $data['frame'] = $rl[1];
- return $data;
- }
- else {
- return false;
- }
- }
- catch (RedisException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
- 'msg' => array('redis_error', $e)
- );
- return false;
- }
- return false;
- break;
case 'alias_details':
$aliasdata = array();
$stmt = $pdo->prepare("SELECT
@@ -2759,6 +2634,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
break;
case 'alias_domain_details':
$aliasdomaindata = array();
+ $rl = ratelimit('get', 'domain', $_data);
$stmt = $pdo->prepare("SELECT
`alias_domain`,
`target_domain`,
@@ -2775,6 +2651,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$aliasdomaindata['alias_domain'] = $row['alias_domain'];
$aliasdomaindata['target_domain'] = $row['target_domain'];
$aliasdomaindata['active'] = $row['active'];
+ $aliasdomaindata['rl'] = $rl;
$aliasdomaindata['active_int'] = $row['active_int'];
$aliasdomaindata['created'] = $row['created'];
$aliasdomaindata['modified'] = $row['modified'];
@@ -2848,6 +2725,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
AND `domain` = :domain");
$stmt->execute(array(':domain' => $row['domain']));
$MailboxDataDomain = $stmt->fetch(PDO::FETCH_ASSOC);
+ $rl = ratelimit('get', 'domain', $_data);
$domaindata['max_new_mailbox_quota'] = ($row['quota'] * 1048576) - $MailboxDataDomain['in_use'];
if ($domaindata['max_new_mailbox_quota'] > ($row['maxquota'] * 1048576)) {
$domaindata['max_new_mailbox_quota'] = ($row['maxquota'] * 1048576);
@@ -2864,6 +2742,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$domaindata['relayhost'] = $row['relayhost'];
$domaindata['backupmx'] = $row['backupmx'];
$domaindata['backupmx_int'] = $row['backupmx_int'];
+ $domaindata['rl'] = $rl;
$domaindata['active'] = $row['active'];
$domaindata['active_int'] = $row['active_int'];
$domaindata['relay_all_recipients'] = $row['relay_all_recipients'];
@@ -2887,6 +2766,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false;
}
$mailboxdata = array();
+ $rl = ratelimit('get', 'mailbox', $_data);
$stmt = $pdo->prepare("SELECT
`domain`.`backupmx`,
`mailbox`.`username`,
@@ -2918,6 +2798,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$mailboxdata['max_new_quota'] = ($DomainQuota['maxquota'] * 1048576);
}
$mailboxdata['username'] = $row['username'];
+ $mailboxdata['rl'] = $rl;
$mailboxdata['is_relayed'] = $row['backupmx'];
$mailboxdata['name'] = $row['name'];
$mailboxdata['active'] = $row['active'];
diff --git a/data/web/inc/functions.ratelimit.inc.php b/data/web/inc/functions.ratelimit.inc.php
new file mode 100644
index 0000000000..8218a7bd5a
--- /dev/null
+++ b/data/web/inc/functions.ratelimit.inc.php
@@ -0,0 +1,188 @@
+ 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => 'rl_timeframe'
+ );
+ continue;
+ }
+ if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ continue;
+ }
+ if (empty($rl_value)) {
+ try {
+ $redis->hDel('RL_VALUE', $object);
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
+ }
+ else {
+ try {
+ $redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('object_modified', $object)
+ );
+ }
+ break;
+ case 'mailbox':
+ if (!is_array($_data['object'])) {
+ $objects = array();
+ $objects[] = $_data['object'];
+ }
+ else {
+ $objects = $_data['object'];
+ }
+ foreach ($objects as $object) {
+ $rl_value = intval($_data['rl_value']);
+ $rl_frame = $_data['rl_frame'];
+ if (!in_array($rl_frame, array('s', 'm', 'h'))) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => 'rl_timeframe'
+ );
+ continue;
+ }
+ if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)
+ || ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => 'access_denied'
+ );
+ continue;
+ }
+ if (empty($rl_value)) {
+ try {
+ $redis->hDel('RL_VALUE', $object);
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
+ }
+ else {
+ try {
+ $redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
+ }
+ $_SESSION['return'][] = array(
+ 'type' => 'success',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('object_modified', $object)
+ );
+ }
+ break;
+ }
+ break;
+ case 'get':
+ switch ($_scope) {
+ case 'domain':
+ if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+ return false;
+ }
+ try {
+ if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
+ $rl = explode(' / 1', $rl_value);
+ $data['value'] = $rl[0];
+ $data['frame'] = $rl[1];
+ return $data;
+ }
+ else {
+ return false;
+ }
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('redis_error', $e)
+ );
+ return false;
+ }
+ return false;
+ break;
+ case 'mailbox':
+ if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)
+ || ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
+ return false;
+ }
+ try {
+ if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
+ $rl = explode(' / 1', $rl_value);
+ $data['value'] = $rl[0];
+ $data['frame'] = $rl[1];
+ return $data;
+ }
+ else {
+ return false;
+ }
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
+ 'msg' => array('redis_error', $e)
+ );
+ return false;
+ }
+ return false;
+ break;
+ }
+ break;
+ }
+}
\ No newline at end of file
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index c07d4d2dd4..a5e03672f3 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -132,6 +132,7 @@ function get_remote_ip($anonymize = null) {
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.ratelimit.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.relayhost.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rsettings.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fail2ban.inc.php';
diff --git a/data/web/js/admin.js b/data/web/js/admin.js
index a4fe801e3f..24725f606a 100644
--- a/data/web/js/admin.js
+++ b/data/web/js/admin.js
@@ -9,6 +9,10 @@ jQuery(function($){
e.preventDefault();
$('#import_dkim_arrow').toggleClass("animation");
});
+ $("#duplicate_dkim_legend").on('click', function(e) {
+ e.preventDefault();
+ $('#duplicate_dkim_arrow').toggleClass("animation");
+ });
$("#rspamd_preset_1").on('click', function(e) {
e.preventDefault();
$("form[data-id=rsetting]").find("#desc").val(lang.rsettings_preset_1);
@@ -19,6 +23,14 @@ jQuery(function($){
$("form[data-id=rsetting]").find("#desc").val(lang.rsettings_preset_2);
$("form[data-id=rsetting]").find("#content").val('priority = 10;\nrcpt = "/postmaster@.*/";\nwant_spam = yes;');
});
+ $("#dkim_missing_keys").on('click', function(e) {
+ e.preventDefault();
+ var domains = [];
+ $('.dkim_missing').each(function() {
+ domains.push($(this).val());
+ });
+ $('#dkim_add_domains').val(domains);
+ });
function draw_domain_admins() {
ft_domainadmins = FooTable.init('#domainadminstable', {
"columns": [
diff --git a/data/web/js/debug.js b/data/web/js/debug.js
index 37f457460a..bf08716730 100644
--- a/data/web/js/debug.js
+++ b/data/web/js/debug.js
@@ -171,7 +171,7 @@ jQuery(function($){
{"name":"user","title":"User"},
{"name":"role","title":"Role"},
{"name":"remote","title":"IP"},
- {"name":"msg","title":lang.message},
+ {"name":"msg","title":lang.message,"style":{"word-break":"break-all"}},
{"name":"call","title":"Call","breakpoints": "all"},
],
"rows": $.ajax({
diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js
index 072eea63a1..95b4dc61e0 100644
--- a/data/web/js/mailbox.js
+++ b/data/web/js/mailbox.js
@@ -264,6 +264,7 @@ jQuery(function($){
},
},
{"name":"max_quota_for_mbox","title":lang.mailbox_quota,"breakpoints":"xs sm","style":{"width":"125px"}},
+ {"name":"rl","title":"RL","breakpoints":"xs sm md","style":{"width":"125px"}},
{"name":"backupmx","filterable": false,"style":{"maxWidth":"120px","width":"120px"},"title":lang.backup_mx,"breakpoints":"xs sm"},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"240px","width":"240px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
@@ -280,6 +281,13 @@ jQuery(function($){
item.aliases = item.aliases_in_domain + " / " + item.max_num_aliases_for_domain;
item.mailboxes = item.mboxes_in_domain + " / " + item.max_num_mboxes_for_domain;
item.quota = item.quota_used_in_domain + "/" + item.max_quota_for_domain;
+ if (!item.rl) {
+ item.rl = '∞';
+ } else {
+ item.rl = $.map(item.rl, function(e){
+ return e;
+ }).join('/1');
+ }
item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
item.chkbox = '';
item.action = '';
@@ -342,6 +350,7 @@ jQuery(function($){
},
},
{"name":"messages","filterable": false,"title":lang.msg_num,"breakpoints":"xs sm md"},
+ {"name":"rl","title":"RL","breakpoints":"xs sm md","style":{"width":"125px"}},
{"name":"active","filterable": false,"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"min-width":"250px","text-align":"right"},"type":"html","title":lang.action,"breakpoints":"xs sm md"}
],
@@ -357,6 +366,13 @@ jQuery(function($){
$.each(data, function (i, item) {
item.quota = item.quota_used + "/" + item.quota;
item.max_quota_for_mbox = humanFileSize(item.max_quota_for_mbox);
+ if (!item.rl) {
+ item.rl = '∞';
+ } else {
+ item.rl = $.map(item.rl, function(e){
+ return e;
+ }).join('/1');
+ }
item.chkbox = '
';
if (role == "admin") {
item.action = '
' +
diff --git a/data/web/json_api.php b/data/web/json_api.php
index bbf452f915..fba933c48f 100644
--- a/data/web/json_api.php
+++ b/data/web/json_api.php
@@ -135,6 +135,9 @@ function process_add_return($return) {
case "dkim":
process_add_return(dkim('add', $attr));
break;
+ case "dkim_duplicate":
+ process_add_return(dkim('duplicate', $attr));
+ break;
case "dkim_import":
process_add_return(dkim('import', $attr));
break;
@@ -181,6 +184,7 @@ function process_get_return($data) {
break;
}
break;
+
case "domain":
switch ($object) {
case "all":
@@ -208,6 +212,67 @@ function process_get_return($data) {
}
break;
+ case "rl-domain":
+ switch ($object) {
+ case "all":
+ $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
+ if (!empty($domains)) {
+ foreach ($domains as $domain) {
+ if ($details = ratelimit('get', 'domain', $domain)) {
+ $details['domain'] = $domain;
+ $data[] = $details;
+ }
+ else {
+ continue;
+ }
+ }
+ process_get_return($data);
+ }
+ else {
+ echo '{}';
+ }
+ break;
+
+ default:
+ $data = ratelimit('get', 'domain', $object);
+ process_get_return($data);
+ break;
+ }
+ break;
+
+ case "rl-mbox":
+ switch ($object) {
+ case "all":
+ $domains = mailbox('get', 'domains');
+ if (!empty($domains)) {
+ foreach ($domains as $domain) {
+ $mailboxes = mailbox('get', 'mailboxes', $domain);
+ if (!empty($mailboxes)) {
+ foreach ($mailboxes as $mailbox) {
+ if ($details = ratelimit('get', 'mailbox', $mailbox)) {
+ $details['mailbox'] = $mailbox;
+ $data[] = $details;
+ }
+ else {
+ continue;
+ }
+ }
+ }
+ }
+ process_get_return($data);
+ }
+ else {
+ echo '{}';
+ }
+ break;
+
+ default:
+ $data = ratelimit('get', 'mailbox', $object);
+ process_get_return($data);
+ break;
+ }
+ break;
+
case "relayhost":
switch ($object) {
case "all":
@@ -968,8 +1033,11 @@ function process_edit_return($return) {
case "domain":
process_edit_return(mailbox('edit', 'domain', array_merge(array('domain' => $items), $attr)));
break;
- case "ratelimit":
- process_edit_return(mailbox('edit', 'ratelimit', array_merge(array('object' => $items), $attr)));
+ case "rl-domain":
+ process_edit_return(ratelimit('edit', 'domain', array_merge(array('object' => $items), $attr)));
+ break;
+ case "rl-mbox":
+ process_edit_return(ratelimit('edit', 'mailbox', array_merge(array('object' => $items), $attr)));
break;
case "alias-domain":
process_edit_return(mailbox('edit', 'alias_domain', array_merge(array('alias_domain' => $items), $attr)));
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index 3125f2ae19..ef8805e3ea 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -65,6 +65,7 @@
$lang['danger']['dkim_domain_or_sel_invalid'] = 'DKIM-Domain oder Selektor nicht korrekt: %s';
$lang['success']['dkim_removed'] = 'DKIM-Key wurde entfernt';
$lang['success']['dkim_added'] = 'DKIM-Key wurde hinzugefügt';
+$lang['success']['dkim_duplicated'] = "DKIM-Key der Domain %s wurde auf Domain %s kopiert";
$lang['danger']['access_denied'] = 'Zugriff verweigert oder unvollständige/ungültige Daten';
$lang['danger']['domain_invalid'] = 'Domainname %s ist ungültig';
$lang['danger']['mailbox_quota_exceeds_domain_quota'] = 'Maximale Größe für Mailboxen überschreitet das Domain Speicherlimit';
@@ -249,6 +250,7 @@
$lang['mailbox']['description'] = 'Beschreibung';
$lang['mailbox']['resources'] = 'Ressourcen';
$lang['mailbox']['domains'] = 'Domains';
+$lang['admin']['domain_s'] = 'Domain(s)';
$lang['mailbox']['mailboxes'] = 'Mailboxen';
$lang['mailbox']['mailbox_quota'] = 'Max. Größe einer Mailbox';
$lang['mailbox']['domain_quota'] = 'Gesamtspeicher';
@@ -436,7 +438,9 @@
$lang['admin']['additional_rows'] = ' zusätzliche Zeilen geladen'; // parses to 'n additional rows were added'
$lang['admin']['private_key'] = 'Private Key';
$lang['admin']['import'] = 'Importieren';
+$lang['admin']['duplicate'] = 'Duplizieren';
$lang['admin']['import_private_key'] = 'Private Key importieren';
+$lang['admin']['duplicate_dkim'] = 'DKIM duplizieren';
$lang['admin']['f2b_parameters'] = 'Fail2ban Parameter';
$lang['admin']['f2b_ban_time'] = 'Banzeit (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. Versuche';
@@ -453,6 +457,11 @@
$lang['admin']['dkim_key_valid'] = 'Key gültig';
$lang['admin']['dkim_key_unused'] = 'Key ohne Zuweisung';
$lang['admin']['dkim_key_missing'] = 'Key fehlt';
+$lang['admin']['dkim_from'] = 'Von';
+$lang['admin']['dkim_to'] = 'Nach';
+$lang['admin']['dkim_from_title'] = 'Quellobjekt für Duplizierung';
+$lang['admin']['dkim_to_title'] = 'Ziel-Objekt(e) werden überschrieben';
+$lang['admin']['dkim_domains_wo_keys'] = "Domains mit fehlenden Keys auswählen";
$lang['admin']['add'] = 'Hinzufügen';
$lang['add']['add_domain_restart'] = 'Domain hinzufügen und SOGo neustarten';
$lang['add']['add_domain_only'] = 'Nur Domain hinzufügen';
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index ed2fbf25bc..6f345fb0b5 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -68,7 +68,8 @@
$lang['danger']['dkim_domain_or_sel_invalid'] = "DKIM domain or selector invalid: %s";
$lang['success']['dkim_removed'] = "DKIM key %s has been removed";
-$lang['success']['dkim_added'] = "DKIM key has been saved";
+$lang['success']['dkim_added'] = "DKIM key %s has been saved";
+$lang['success']['dkim_duplicated'] = "DKIM key for domain %s has been copied to %s";
$lang['danger']['access_denied'] = "Access denied or invalid form data";
$lang['danger']['domain_invalid'] = "Domain name %s is invalid";
$lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds domain quota limit";
@@ -250,6 +251,7 @@
$lang['mailbox']['alias'] = 'Alias';
$lang['mailbox']['aliases'] = 'Aliases';
$lang['mailbox']['domains'] = 'Domains';
+$lang['admin']['domain_s'] = 'Domain/s';
$lang['mailbox']['mailboxes'] = 'Mailboxes';
$lang['mailbox']['resources'] = 'Resources';
$lang['mailbox']['mailbox_quota'] = 'Max. size of a mailbox';
@@ -445,7 +447,13 @@
$lang['admin']['additional_rows'] = ' additional rows were added'; // parses to 'n additional rows were added'
$lang['admin']['private_key'] = 'Private key';
$lang['admin']['import'] = 'Import';
+$lang['admin']['duplicate'] = 'Duplicate';
$lang['admin']['import_private_key'] = 'Import private key';
+$lang['admin']['duplicate_dkim'] = 'Duplicate DKIM record';
+$lang['admin']['dkim_from'] = 'From';
+$lang['admin']['dkim_to'] = 'To';
+$lang['admin']['dkim_from_title'] = 'Source domain to copy data from';
+$lang['admin']['dkim_to_title'] = 'Target domain/s - will be overwritten';
$lang['admin']['f2b_parameters'] = 'Fail2ban parameters';
$lang['admin']['f2b_ban_time'] = 'Ban time (s)';
$lang['admin']['f2b_max_attempts'] = 'Max. attempts';
@@ -465,6 +473,7 @@
$lang['admin']['dkim_key_missing'] = 'Key missing';
$lang['admin']['dkim_add_key'] = 'Add ARC/DKIM key';
$lang['admin']['dkim_keys'] = 'ARC/DKIM keys';
+$lang['admin']['dkim_domains_wo_keys'] = "Select domains with missing keys";
$lang['admin']['add'] = 'Add';
$lang['add']['add_domain_restart'] = 'Add domain and restart SOGo';
$lang['add']['add_domain_only'] = 'Add domain only';
From d28fd4070b8b21ca58185e8af56e5142c7847004 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9?=
Date: Tue, 21 Aug 2018 21:06:22 +0200
Subject: [PATCH 022/109] [Web] Allow to set rate limit in add_domain modal
[Web] Cleanup rate limits when deleting objects
---
data/web/inc/functions.mailbox.inc.php | 16 +++++++++++++
data/web/inc/functions.ratelimit.inc.php | 4 ++--
data/web/lang/lang.de.php | 1 +
data/web/lang/lang.en.php | 5 ++--
data/web/modals/mailbox.php | 30 ++++++++++++++++++------
5 files changed, 45 insertions(+), 11 deletions(-)
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 0db7259450..13a3393406 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -415,6 +415,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
return false;
}
+ if (!empty(intval($_data['rl_value']))) {
+ ratelimit('edit', 'domain', array('rl_value' => $rl_value, 'rl_frame' => $_data['rl_frame'], 'object' => $domain));
+ }
if (!empty($restart_sogo)) {
$restart_reponse = json_decode(docker('post', 'sogo-mailcow', 'restart'), true);
if ($restart_reponse['type'] == "success") {
@@ -3094,6 +3097,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
));
try {
$redis->hDel('DOMAIN_MAP', $domain);
+ $redis->hDel('RL_VALUE', $domain);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
@@ -3182,6 +3186,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
));
try {
$redis->hDel('DOMAIN_MAP', $alias_domain);
+ $redis->hDel('RL_VALUE', $domain);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
@@ -3301,6 +3306,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
':address' => $gotos['address']
));
}
+ try {
+ $redis->hDel('RL_VALUE', $username);
+ }
+ catch (RedisException $e) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+ 'msg' => array('redis_error', $e)
+ );
+ continue;
+ }
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
diff --git a/data/web/inc/functions.ratelimit.inc.php b/data/web/inc/functions.ratelimit.inc.php
index 8218a7bd5a..2ead02e405 100644
--- a/data/web/inc/functions.ratelimit.inc.php
+++ b/data/web/inc/functions.ratelimit.inc.php
@@ -62,7 +62,7 @@ function ratelimit($_action, $_scope, $_data = null) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
- 'msg' => array('object_modified', $object)
+ 'msg' => array('rl_saved', $object)
);
}
break;
@@ -123,7 +123,7 @@ function ratelimit($_action, $_scope, $_data = null) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
- 'msg' => array('object_modified', $object)
+ 'msg' => array('rl_saved', $object)
);
}
break;
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index ef8805e3ea..5741f968c5 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -42,6 +42,7 @@
$lang['danger']['domain_cannot_match_hostname'] = "Domain darf nicht dem Hostnamen entsprechen";
$lang['warning']['domain_added_sogo_failed'] = "Domain wurde hinzugefügt; SOGo konnte nicht neugestartet werden";
$lang['danger']['rl_timeframe'] = "Ratelimit Zeitraum ist inkorrekt";
+$lang['success']['rl_saved'] = "Ratelimit für Objekt %s wurde gesetzt";
$lang['success']['deleted_syncjobs'] = "Syncjobs gelöscht: %s";
$lang['success']['deleted_syncjob'] = "Syncjobs ID %s gelöscht";
$lang['success']['delete_filters'] = "Filter gelöscht: %s";
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index 6f345fb0b5..0a04f64998 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -41,7 +41,8 @@
$lang['danger']['filter_type'] = "Wrong filter type";
$lang['danger']['domain_cannot_match_hostname'] = "Domain cannot match hostname";
$lang['warning']['domain_added_sogo_failed'] = "Added domain but failed to restart SOGo, please check your server logs.";
-$lang['danger']['rl_timeframe'] = "Ratelimit time frame is incorrect";
+$lang['danger']['rl_timeframe'] = "Rate limit time frame is incorrect";
+$lang['success']['rl_saved'] = "Rate limit for object %s saved";
$lang['success']['deleted_syncjobs'] = "Deleted syncjobs: %s";
$lang['success']['deleted_syncjob'] = "Deleted syncjob ID %s";
$lang['success']['delete_filters'] = "Deleted filters: %s";
@@ -489,7 +490,7 @@
$lang['admin']['rsetting_content'] = 'Rule content';
$lang['admin']['rsetting_none'] = 'No rule available';
$lang['admin']['rsetting_no_selection'] = 'Please select a rule';
-$lang['admin']['rsettings_preset_1'] = 'Disable all but DKIM and ratelimit for authenticated users';
+$lang['admin']['rsettings_preset_1'] = 'Disable all but DKIM and rate limit for authenticated users';
$lang['admin']['rsettings_preset_2'] = 'Postmasters want spam';
$lang['admin']['rsettings_insert_preset'] = 'Insert example preset "%s"';
$lang['admin']['rsetting_add_rule'] = 'Add rule';
diff --git a/data/web/modals/mailbox.php b/data/web/modals/mailbox.php
index 94f881e4a8..b4a518930a 100644
--- a/data/web/modals/mailbox.php
+++ b/data/web/modals/mailbox.php
@@ -123,6 +123,28 @@
+
+
+
+
-
+
-
-
-
-
API
-
+
+
+
diff --git a/data/web/css/admin.css b/data/web/css/admin.css
index a53d721c79..bc89f1ddf3 100644
--- a/data/web/css/admin.css
+++ b/data/web/css/admin.css
@@ -65,12 +65,6 @@ body.modal-open {
font-size:9pt;
background:transparent;
}
-.bootstrap-select {
- width: auto!important;
-}
.table-condensed .input-sm {
width: 100%!important;
}
-.full-width-select {
- width: 100%!important;
-}
diff --git a/data/web/css/mailbox.css b/data/web/css/mailbox.css
index da2e96e362..488150d39a 100644
--- a/data/web/css/mailbox.css
+++ b/data/web/css/mailbox.css
@@ -5,9 +5,6 @@ table.footable>tbody>tr.footable-empty>td {
.pagination a {
text-decoration: none !important;
}
-.panel panel-default {
- overflow: visible !important;
-}
.btn-group {
width: max-content;
}
diff --git a/data/web/css/mailcow.css b/data/web/css/mailcow.css
index 374688a199..04d1b87449 100644
--- a/data/web/css/mailcow.css
+++ b/data/web/css/mailcow.css
@@ -148,3 +148,13 @@ nav .glyphicon {
color: #5a5a5a;
white-space: nowrap;
}
+.haveibeenpwned {
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+.full-width-select {
+ width: 100%!important;
+}
\ No newline at end of file
diff --git a/data/web/edit.php b/data/web/edit.php
index c4903c2859..417f2a7cec 100644
--- a/data/web/edit.php
+++ b/data/web/edit.php
@@ -92,7 +92,7 @@
-
+
@@ -278,14 +302,14 @@
-
@@ -648,19 +648,19 @@
-
+
-
+
-
-
+
+
-
+
@@ -754,21 +754,21 @@
diff --git a/data/web/autodiscover.php b/data/web/autodiscover.php
index f5bcbf8fec..9a167af771 100644
--- a/data/web/autodiscover.php
+++ b/data/web/autodiscover.php
@@ -27,7 +27,8 @@
}
}
-$dsn = $database_type . ":host=" . $database_host . ";dbname=" . $database_name;
+//$dsn = $database_type . ":host=" . $database_host . ";dbname=" . $database_name;
+$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
diff --git a/data/web/edit.php b/data/web/edit.php
index 417f2a7cec..a2eb4fb728 100644
--- a/data/web/edit.php
+++ b/data/web/edit.php
@@ -39,13 +39,13 @@
@@ -58,7 +58,7 @@
@@ -92,7 +92,7 @@
@@ -189,7 +189,7 @@
-
+
@@ -570,7 +570,7 @@
-
+
@@ -589,19 +589,19 @@
@@ -613,7 +613,7 @@
@@ -636,13 +636,13 @@
@@ -984,7 +984,7 @@
diff --git a/data/web/inc/footer.inc.php b/data/web/inc/footer.inc.php
index 14d2acf248..9e8daf7b81 100644
--- a/data/web/inc/footer.inc.php
+++ b/data/web/inc/footer.inc.php
@@ -59,7 +59,7 @@ function setLang(sel) {
});
$('#u2f_status_auth').html(' Initializing, please wait...
');
$('#ConfirmTFAModal').on('shown.bs.modal', function(){
- $(this).find('#token').focus();
+ $(this).find('input[name=token]').focus();
// If U2F
if(document.getElementById("u2f_auth_data") !== null) {
$.ajax({
@@ -146,7 +146,7 @@ function setLang(sel) {
});
// CSRF
- $('').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form');
+ $('').attr('name', 'csrf_token').appendTo('form');
if (sessionStorage.scrollTop != "undefined") {
$(window).scrollTop(sessionStorage.scrollTop);
}
diff --git a/data/web/inc/functions.docker.inc.php b/data/web/inc/functions.docker.inc.php
index dbd26375c9..9c63eec7fa 100644
--- a/data/web/inc/functions.docker.inc.php
+++ b/data/web/inc/functions.docker.inc.php
@@ -2,10 +2,13 @@
function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $extra_headers = null) {
global $DOCKER_TIMEOUT;
$curl = curl_init();
- curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' ));
+ curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: application/json' ));
+ // We are using our mail certificates for dockerapi, the names will not match, the certs are trusted anyway
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
switch($action) {
case 'get_id':
- curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/json');
+ curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
@@ -38,7 +41,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
}
return false;
case 'containers':
- curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/json');
+ curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
@@ -74,7 +77,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
break;
case 'info':
if (empty($service_name)) {
- curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/json');
+ curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
@@ -82,7 +85,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
else {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id)) {
- curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/json');
+ curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/json');
}
else {
// logger(array('return' => array(
@@ -144,7 +147,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
if (!empty($attr1)) {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id) && ctype_alnum($attr1)) {
- curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/' . $attr1);
+ curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/' . $attr1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
if (!empty($attr2)) {
@@ -158,19 +161,19 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
- logger(array('return' => array(array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
- 'msg' => $err,
- ))));
+ // logger(array('return' => array(array(
+ // 'type' => 'danger',
+ // 'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
+ // 'msg' => $err,
+ // ))));
return $err;
}
else {
curl_close($curl);
- logger(array('return' => array(array(
- 'type' => 'success',
- 'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
- ))));
+ // logger(array('return' => array(array(
+ // 'type' => 'success',
+ // 'log' => array(__FUNCTION__, $action, $service_name, $attr1, $attr2, $extra_headers),
+ // ))));
if (empty($response)) {
return true;
}
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 94eada4f3c..f915d5d077 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -18,6 +18,17 @@ function last_login($user) {
return false;
}
}
+function flush_memcached() {
+ try {
+ $m = new Memcached();
+ $m->addServer('memcached', 11211);
+ $m->flush();
+ }
+ catch ( Exception $e ) {
+ // Dunno
+ }
+}
+
function logger($_data = false) {
/*
logger() will be called as last function
@@ -510,6 +521,7 @@ function update_sogo_static_view() {
$stmt = $pdo->query("REPLACE INTO _sogo_static_view SELECT * from sogo_view");
$stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
}
+ flush_memcached();
}
function edit_user_account($_data) {
global $lang;
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
index 8393c5f660..e06ac397f0 100644
--- a/data/web/inc/functions.mailbox.inc.php
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -2776,6 +2776,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`mailbox`.`active` AS `active_int`,
CASE `mailbox`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
`mailbox`.`domain`,
+ `mailbox`.`maildir`,
`mailbox`.`quota`,
`quota2`.`bytes`,
`attributes`,
@@ -2806,6 +2807,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$mailboxdata['active'] = $row['active'];
$mailboxdata['active_int'] = $row['active_int'];
$mailboxdata['domain'] = $row['domain'];
+ $mailboxdata['maildir'] = $row['maildir'];
$mailboxdata['quota'] = $row['quota'];
$mailboxdata['attributes'] = json_decode($row['attributes'], true);
$mailboxdata['quota_used'] = intval($row['bytes']);
@@ -3054,6 +3056,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
+ $exec_fields = array('cmd' => 'maildir_cleanup', 'maildir' => $domain);
+ $maildir_gc = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
+ if ($maildir_gc['type'] != 'success') {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+ 'msg' => 'Could not move maildir to garbage collector: ' . $maildir_gc['msg']
+ );
+ }
+ return false;
$stmt = $pdo->prepare("DELETE FROM `domain` WHERE `domain` = :domain");
$stmt->execute(array(
':domain' => $domain,
@@ -3078,17 +3090,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$stmt->execute(array(
':domain' => '%@'.$domain,
));
- $stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :domain");
+ $stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` LIKE :domain");
$stmt->execute(array(
':domain' => '%@'.$domain,
));
- $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` = :domain");
+ $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` LIKE :domain");
$stmt->execute(array(
':domain' => '%@'.$domain,
));
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :domain");
$stmt->execute(array(
- ':domain' => '%@'.$domain,
+ ':domain' => $domain,
));
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :domain");
$stmt->execute(array(
@@ -3227,6 +3239,16 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
+ $maildir = mailbox('get', 'mailbox_details', $username)['maildir'];
+ $exec_fields = array('cmd' => 'maildir_cleanup', 'maildir' => $maildir);
+ $maildir_gc = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
+ if ($maildir_gc['type'] != 'success') {
+ $_SESSION['return'][] = array(
+ 'type' => 'warning',
+ 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+ 'msg' => 'Could not move maildir to garbage collector: ' . $maildir_gc['msg']
+ );
+ }
$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `goto` = :username");
$stmt->execute(array(
':username' => $username
diff --git a/data/web/inc/init_db.inc.php b/data/web/inc/init_db.inc.php
index 58566ab735..571713be1c 100644
--- a/data/web/inc/init_db.inc.php
+++ b/data/web/inc/init_db.inc.php
@@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
- $db_version = "01092018_1902";
+ $db_version = "21092018_1902";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index aedda083fd..209c7184dc 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -35,7 +35,7 @@
$mins -= $hrs * 60;
$offset = sprintf('%+d:%02d', $hrs*$sgn, $mins);
-$dsn = $database_type . ":host=" . $database_host . ";dbname=" . $database_name;
+$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
@@ -46,11 +46,20 @@
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
+// Stop when SQL connection fails
?>
-Connection to database failed.
The following error was reported:
=$e->getMessage();?>
+Connection to database failed.
The following error was reported:
=$e->getMessage();?>
+Connection to dockerapi container failed.
The following error was reported:
=$errno;?> - =$errstr;?>
+' +
' Test' +
' ' + lang.edit + '' +
- ' ' + lang.remove + '' +
+ ' ' + lang.remove + '' +
'';
item.chkbox = '';
});
} else if (table == 'forwardinghoststable') {
$.each(data, function (i, item) {
item.action = '';
if (item.keep_spam == "yes") {
item.keep_spam = lang.no;
@@ -145,7 +145,7 @@ jQuery(function($){
item.chkbox = '';
item.action = '';
});
diff --git a/data/web/js/api.js b/data/web/js/api.js
index a573d7f441..e8293dbc68 100644
--- a/data/web/js/api.js
+++ b/data/web/js/api.js
@@ -71,7 +71,7 @@ $(document).ready(function() {
});
// General API edit actions
- $(document).on('click', '#edit_selected', function(e) {
+ $(document).on('click', "[data-action='edit_selected']", function(e) {
e.preventDefault();
var id = $(this).data('id');
var api_url = $(this).data('api-url');
@@ -159,7 +159,7 @@ $(document).ready(function() {
});
// General API add actions
- $(document).on('click', '#add_item', function(e) {
+ $(document).on('click', "[data-action='add_item']", function(e) {
e.preventDefault();
var id = $(this).data('id');
var api_url = $(this).data('api-url');
@@ -252,7 +252,7 @@ $(document).ready(function() {
});
// General API delete actions
- $(document).on('click', '#delete_selected', function(e) {
+ $(document).on('click', "[data-action='delete_selected']", function(e) {
e.preventDefault();
var id = $(this).data('id');
// If clicked element #delete_selected has data-item attribute, it is added to "items"
@@ -283,6 +283,7 @@ $(document).ready(function() {
keyboard: false
})
.one('click', '#IsConfirmed', function(e) {
+ if (is_active($('#IsConfirmed'))) { return false; }
$.ajax({
type: "POST",
dataType: "json",
diff --git a/data/web/js/edit.js b/data/web/js/edit.js
index f91f6b70dc..45690710ec 100644
--- a/data/web/js/edit.js
+++ b/data/web/js/edit.js
@@ -10,37 +10,43 @@ $(document).ready(function() {
});
$("#disable_sender_check").click(function( event ) {
if ($("form[data-id='editmailbox'] #disable_sender_check:checked").length > 0) {
- $('#sender_acl').prop('disabled', true);
- $('#sender_acl').selectpicker('refresh');
+ $('#editSelectSenderACL').prop('disabled', true);
+ $('#editSelectSenderACL').selectpicker('refresh');
}
else {
- $('#sender_acl').prop('disabled', false);
- $('#sender_acl').selectpicker('refresh');
+ $('#editSelectSenderACL').prop('disabled', false);
+ $('#editSelectSenderACL').selectpicker('refresh');
}
});
if ($("form[data-id='editalias'] .goto_checkbox:checked").length > 0) {
$('#textarea_alias_goto').prop('disabled', true);
}
+
$("#script_data").numberedtextarea({allowTabChar: true});
+
+ $("#mailbox-password-warning-close").click(function( event ) {
+ $('#mailbox-passwd-hidden-info').addClass('hidden');
+ $('#mailbox-passwd-form-groups').removeClass('hidden');
+ });
});
-if ($("#multiple_bookings_select").val() == "custom") {
+if ($("#editSelectMultipleBookings").val() == "custom") {
$("#multiple_bookings_custom_div").show();
- $("#multiple_bookings").val($("#multiple_bookings_custom").val());
+ $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
}
-$("#multiple_bookings_select").change(function() {
- $("#multiple_bookings").val($("#multiple_bookings_select").val());
- if ($("#multiple_bookings").val() == "custom") {
+$("#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();
}
});
-if ($("#sender_acl option[value='\*']:selected").length > 0){
+if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
$("#sender_acl_disabled").show();
}
-$('#sender_acl').change(function() {
- if ($("#sender_acl option[value='\*']:selected").length > 0){
+$('#editSelectSenderACL').change(function() {
+ if ($("#editSelectSenderACL option[value='\*']:selected").length > 0){
$("#sender_acl_disabled").show();
}
else {
@@ -48,7 +54,7 @@ $('#sender_acl').change(function() {
}
});
$("#multiple_bookings_custom").bind("change keypress keyup blur", function() {
- $("#multiple_bookings").val($("#multiple_bookings_custom").val());
+ $('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
});
jQuery(function($){
// http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js
index 34f3ec20b5..836ec872ee 100644
--- a/data/web/js/mailbox.js
+++ b/data/web/js/mailbox.js
@@ -6,7 +6,6 @@ $(document).ready(function() {
var domain_list = [];
$.ajax({
dataType: 'json',
- 'async': false,
url: '/api/v1/get/domain/all',
jsonp: false,
error: function () {
@@ -85,10 +84,10 @@ $(document).ready(function() {
event.preventDefault();
$('[data-hibp]').trigger('input');
var random_passwd = Math.random().toString(36).slice(-8)
- $('#password').prop('type', 'text');
- $('#password').val(random_passwd);
- $('#password2').prop('type', 'text');
- $('#password2').val(random_passwd);
+ $(this).closest("form").find("input[name='password']").prop('type', 'text');
+ $(this).closest("form").find("input[name='password2']").prop('type', 'text');
+ $(this).closest("form").find("input[name='password']").val(random_passwd);
+ $(this).closest("form").find("input[name='password2']").val(random_passwd);
});
$(".goto_checkbox").click(function( event ) {
@@ -302,7 +301,7 @@ jQuery(function($){
item.action = '';
if (role == "admin") {
item.action += '
' + lang.edit + '' +
- '
' + lang.remove + '';
+ '
' + lang.remove + '';
}
else {
item.action += '
' + lang.edit + '';
@@ -386,14 +385,14 @@ jQuery(function($){
if (acl_data.login_as === 1) {
item.action = '
';
}
else {
item.action = '
';
}
item.in_use = '
' +
@@ -461,7 +460,7 @@ jQuery(function($){
}
item.action = '
';
item.chkbox = '
';
item.name = escapeHtml(item.name);
@@ -520,7 +519,7 @@ jQuery(function($){
$.each(data, function (i, item) {
item.action = '
';
item.chkbox = '
';
item.local_dest = escapeHtml(item.local_dest);
@@ -583,7 +582,7 @@ jQuery(function($){
item.recipient_map_new = escapeHtml(item.recipient_map_new);
item.action = '
';
item.chkbox = '
';
});
@@ -638,7 +637,7 @@ jQuery(function($){
$.each(data, function (i, item) {
item.action = '
';
item.chkbox = '
';
item.goto = escapeHtml(item.goto.replace(/,/g, " "));
@@ -713,7 +712,7 @@ jQuery(function($){
$.each(data, function (i, item) {
item.action = '
' +
'
';
item.chkbox = '
';
@@ -781,7 +780,7 @@ jQuery(function($){
item.server_w_port = escapeHtml(item.user1) + '@' + item.host1 + ':' + item.port1;
item.action = '
';
item.chkbox = '
';
if (item.is_running == 1) {
@@ -852,7 +851,7 @@ jQuery(function($){
item.filter_type = '
' + item.filter_type.charAt(0).toUpperCase() + item.filter_type.slice(1).toLowerCase() + '
'
item.action = '
';
item.chkbox = '
'
});
diff --git a/data/web/js/mailcow.js b/data/web/js/mailcow.js
index d9019c235d..89cfabf53d 100644
--- a/data/web/js/mailcow.js
+++ b/data/web/js/mailcow.js
@@ -110,7 +110,7 @@ $(document).ready(function() {
// Disable disallowed inputs
$('[data-acl="0"]').each(function(){
- if ($(this).attr('class') == 'btn-group') {
+ if ($(this).hasClass('btn-group')) {
$(this).find('a').each(function(){
$(this).removeClass('dropdown-toggle')
.removeAttr('data-toggle')
@@ -124,7 +124,7 @@ $(document).ready(function() {
$(this).find('button').each(function() {
$(this).attr("disabled", true);
});
- } else if ($(this).attr('class') == 'input-group') {
+ } else if ($(this).hasClass('input-group')) {
$(this).find('input').each(function() {
$(this).removeClass('dropdown-toggle')
.removeAttr('data-toggle')
diff --git a/data/web/js/quarantine.js b/data/web/js/quarantine.js
index 21ec9ddc9d..f84380990a 100644
--- a/data/web/js/quarantine.js
+++ b/data/web/js/quarantine.js
@@ -28,7 +28,7 @@ jQuery(function($){
$.each(data, function (i, item) {
item.action = '
';
item.chkbox = '
';
});
diff --git a/data/web/js/user.js b/data/web/js/user.js
index 226d039a48..5649d6c6df 100644
--- a/data/web/js/user.js
+++ b/data/web/js/user.js
@@ -64,7 +64,7 @@ jQuery(function($){
$.each(data, function (i, item) {
if (acl_data.spam_alias === 1) {
item.action = '
';
item.chkbox = '
';
item.address = escapeHtml(item.address);
@@ -123,7 +123,7 @@ jQuery(function($){
if (acl_data.syncjobs === 1) {
item.action = '
';
item.chkbox = '
';
}
diff --git a/data/web/mailbox.php b/data/web/mailbox.php
index 7e865fbb71..70d8dbb68a 100644
--- a/data/web/mailbox.php
+++ b/data/web/mailbox.php
@@ -44,10 +44,10 @@
=$lang['mailbox']['quick_actions'];?>
if($_SESSION['mailcow_cc_role'] == "admin"): ?>
@@ -74,10 +74,10 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_mailbox'];?>
@@ -101,10 +101,10 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_resource'];?>
@@ -134,10 +134,10 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_domain_alias'];?>
@@ -161,10 +161,10 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_alias'];?>
@@ -188,12 +188,12 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['user']['create_syncjob'];?>
@@ -218,13 +218,13 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_filter'];?>
@@ -249,13 +249,13 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_bcc_entry'];?>
@@ -277,10 +277,10 @@
=$lang['mailbox']['toggle_all'];?>
=$lang['mailbox']['quick_actions'];?>
=$lang['mailbox']['add_recipient_map_entry'];?>
diff --git a/data/web/modals/admin.php b/data/web/modals/admin.php
index 8bca0d87e5..2cce623e64 100644
--- a/data/web/modals/admin.php
+++ b/data/web/modals/admin.php
@@ -17,13 +17,13 @@
@@ -35,7 +35,7 @@
@@ -60,7 +60,7 @@
@@ -79,13 +79,13 @@
@@ -97,7 +97,7 @@
@@ -115,11 +115,11 @@