Compare commits

..

44 Commits

Author SHA1 Message Date
Michael Kuron dfc99fb265 Escape generated password in mobileconfig
Escape ampersand, less than, greater than to avoid generating invalid XML.

Fixes #7171
2026-05-02 16:24:01 +02:00
milkmaker 886dbcc419 Translations update from Weblate (#7190)
* [Web] Updated lang.az-az.json

[Web] Added lang.az-az.json

Co-authored-by: Nemoralis <nemoralis@duck.com>
Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: milkmaker <milkmaker@mailcow.de>

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

Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>

* [Web] Updated lang.pt-br.json

Co-authored-by: André Ribas <andre@ribassu.com>

---------

Co-authored-by: Nemoralis <nemoralis@duck.com>
Co-authored-by: Peter <magic@kthx.at>
Co-authored-by: Matjaž Tekavec <matjaz@moj-svet.si>
Co-authored-by: André Ribas <andre@ribassu.com>
2026-04-11 21:25:03 +02:00
milkmaker dc15994d40 update postscreen_access.cidr (#7177) 2026-04-02 11:50:05 +02:00
FreddleSpl0it ec24825280 Merge pull request #7173 from mailcow/fix/escaping
[Web][Dovecot] Improve input validation and escaping
2026-03-31 09:45:23 +02:00
FreddleSpl0it 5a00b5124b [Web][Dovecot] Add parameterized queries and input validation for quarantine_category 2026-03-29 12:08:45 +02:00
FreddleSpl0it 8c039f694f Improve template URI escaping and parameter handling 2026-03-19 12:48:43 +01:00
FreddleSpl0it 95bf46c1e4 escape HTML in autodiscover logs 2026-03-19 12:44:50 +01:00
FreddleSpl0it edde35156d escape HTML in qitem details 2026-03-19 12:44:30 +01:00
FreddleSpl0it 84e3c32f13 escape HTML in last logins 2026-03-19 12:44:00 +01:00
FreddleSpl0it ecb848493b add missing object-level access control 2026-03-19 12:42:45 +01:00
FreddleSpl0it 8a65b9d1c6 add missing access control 2026-03-19 12:41:47 +01:00
FreddleSpl0it ed9264fd2a [Web] Allow force_tfa for LDAP and Keycloak users 2026-03-13 14:13:25 +01:00
FreddleSpl0it 7817dda43f [ACME] Skip subdomains covered by wildcards (DNS-01 challenge only) 2026-03-13 13:08:18 +01:00
FreddleSpl0it 018e292854 Merge pull request #7134 from mailcow/fix/7112-2
[ACME] Skip autodiscover/mta-sts subdomains covered by wildcard certificates
2026-03-13 12:39:10 +01:00
FreddleSpl0it 127fb1e8f5 [ACME] Skip autodiscover/mta-sts subdomains covered by wildcard certificates 2026-03-13 12:35:22 +01:00
milkmaker 09f09cb850 [Web] Updated lang.hu-hu.json (#7130) 2026-03-12 15:01:54 +01:00
FreddleSpl0it d4bf377a96 Merge pull request #7121 from rezzorix/fix/theme-localstorage-staging
Fix theme localStorage collision with rspamd UI
2026-03-12 07:47:03 +01:00
FreddleSpl0it abd6fe8c79 Merge pull request #7124 from mailcow/fix/7112
[ACME] Fix wildcard certificate conflict with MAILCOW_HOSTNAME
2026-03-12 07:46:02 +01:00
FreddleSpl0it 5f8382ef44 Merge pull request #7123 from mailcow/fix/7115
[Web] Fix LDAP/Keycloak login TypeError - missing JSON decode for attributes
2026-03-12 07:45:06 +01:00
rezzorix 03eccd4e42 added/fix: use mailcow_theme in bundled dark mode JS 2026-03-12 14:11:31 +08:00
FreddleSpl0it 1da8d1c894 [ACME] Fix wildcard certificate conflict with MAILCOW_HOSTNAME 2026-03-11 09:33:16 +01:00
FreddleSpl0it d1feebf164 [Web] Fix LDAP/Keycloak login TypeError - missing JSON decode for attributes 2026-03-11 09:18:03 +01:00
rezzorix 293b885a85 Fix theme localStorage collision with rspamd UI 2026-03-11 13:32:53 +08:00
FreddleSpl0it 1e0850193a Merge pull request #7111 from mailcow/fix/7110
[SOGo] Add ActiveSync support
2026-03-10 13:37:58 +01:00
FreddleSpl0it 33acf56526 [SOGo] Add ActiveSync support 2026-03-10 13:13:00 +01:00
FreddleSpl0it bea9ad7e8f Merge pull request #7108 from mailcow/fix/7105
[SOGo] Fix draft folder creation by adding /var/spool/sogo directory
2026-03-10 10:38:50 +01:00
FreddleSpl0it e7ea3aa608 [SOGo] Fix draft folder creation by adding /var/spool/sogo directory 2026-03-10 10:31:50 +01:00
FreddleSpl0it 5888e248c3 Merge pull request #7103 from mailcow/fix/7102
[Watchdog] Fix Nagios MariaDB client SSL compatibility with Alpine 3.23
2026-03-10 10:04:18 +01:00
FreddleSpl0it 2e176339ba [Watchdog] Fix Nagios MariaDB client SSL compatibility with Alpine 3.23 2026-03-10 10:01:18 +01:00
FreddleSpl0it 8f883f3d37 Rename dns-101 to dns-01 2026-03-09 15:27:33 +01:00
FreddleSpl0it 709117fe19 prevent false positives in option detection in new_options.sh 2026-03-09 15:19:46 +01:00
FreddleSpl0it 82ea418423 Extend --dev flag to skip _modules update 2026-03-09 15:13:36 +01:00
FreddleSpl0it fd24163c6e prevent false positives in option detection in new_options.sh 2026-03-09 15:13:13 +01:00
FreddleSpl0it 3caef45a12 Merge pull request #7100 from mailcow/feat/rspamd-3.14.3
[Rspamd] Update to 3.14.3-1
2026-03-09 08:15:10 +01:00
FreddleSpl0it 8760e7e5db [Rspamd] Update to 3.14.3-1 2026-03-09 07:59:34 +01:00
FreddleSpl0it e848226062 [SOGo] Update paths from /usr/lib/ to /usr/local/lib/ 2026-03-06 16:26:13 +01:00
FreddleSpl0it efaeb77e13 Merge pull request #7093 from mailcow/fix/7054
[SOGo][Web] use incremental updates for mailbox/alias/resource sync in sogo_static_view
2026-03-06 09:23:00 +01:00
FreddleSpl0it 569b4cf985 Merge pull request #7098 from mailcow/feat/sogo-5.12.5
[SOGo] Update to 5.12.5
2026-03-06 09:21:09 +01:00
FreddleSpl0it 6c857243ec [SOGo] Update to 5.12.5 2026-03-06 09:18:10 +01:00
renovate[bot] 905e93627c Update docker/build-push-action action to v7 (#7097) 2026-03-06 07:02:04 +01:00
renovate[bot] 598ea21827 chore(deps): update docker/setup-buildx-action action to v4 (#7096)
Signed-off-by: milkmaker <milkmaker@mailcow.de>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-05 11:03:21 +01:00
milkmaker 6012cf4486 [Web] Updated lang.nl-nl.json (#7095)
Co-authored-by: Tom18314 <tomstokmans5@gmail.com>
2026-03-04 23:03:02 +01:00
FreddleSpl0it fe71f84c82 [SOGo] Fix custom UI patches after moving to source-compiled SOGo 2026-03-04 14:55:52 +01:00
FreddleSpl0it 4e33c7143f [SOGo][Web] use incremental updates for mailbox/alias/resource sync in sogo_static_view 2026-03-04 11:16:48 +01:00
66 changed files with 1107 additions and 844 deletions
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
uses: docker/setup-qemu-action@v4 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Login to GHCR - name: Login to GHCR
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
@@ -30,7 +30,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
+2 -2
View File
@@ -64,7 +64,7 @@ adapt_new_options() {
sed -i --follow-symlinks '$a\' mailcow.conf sed -i --follow-symlinks '$a\' mailcow.conf
for option in ${CONFIG_ARRAY[@]}; do for option in ${CONFIG_ARRAY[@]}; do
if grep -q "${option}" mailcow.conf; then if grep -q "^#\?${option}=" mailcow.conf; then
continue continue
fi fi
@@ -302,7 +302,7 @@ adapt_new_options() {
;; ;;
ACME_DNS_PROVIDER) ACME_DNS_PROVIDER)
echo '# DNS provider for DNS-01 challenge (e.g. dns_cf, dns_azure, dns_gd, etc.)' >> mailcow.conf echo '# DNS provider for DNS-01 challenge (e.g. dns_cf, dns_azure, dns_gd, etc.)' >> mailcow.conf
echo '# See the dns-101 provider documentation for more information.' >> mailcow.conf echo '# See the dns-01 provider documentation for more information.' >> mailcow.conf
echo 'ACME_DNS_PROVIDER=dns_xxx' >> mailcow.conf echo 'ACME_DNS_PROVIDER=dns_xxx' >> mailcow.conf
;; ;;
ACME_ACCOUNT_EMAIL) ACME_ACCOUNT_EMAIL)
+38 -7
View File
@@ -253,10 +253,20 @@ while true; do
unset VALIDATED_CONFIG_DOMAINS_SUBDOMAINS unset VALIDATED_CONFIG_DOMAINS_SUBDOMAINS
declare -a VALIDATED_CONFIG_DOMAINS_SUBDOMAINS declare -a VALIDATED_CONFIG_DOMAINS_SUBDOMAINS
for SUBDOMAIN in "${ADDITIONAL_WC_ARR[@]}"; do for SUBDOMAIN in "${ADDITIONAL_WC_ARR[@]}"; do
if [[ "${SUBDOMAIN}.${SQL_DOMAIN}" != "${MAILCOW_HOSTNAME}" ]]; then FULL_SUBDOMAIN="${SUBDOMAIN}.${SQL_DOMAIN}"
if check_domain "${SUBDOMAIN}.${SQL_DOMAIN}"; then
VALIDATED_CONFIG_DOMAINS_SUBDOMAINS+=("${SUBDOMAIN}.${SQL_DOMAIN}") # Skip if subdomain matches MAILCOW_HOSTNAME
fi if [[ "${FULL_SUBDOMAIN}" == "${MAILCOW_HOSTNAME}" ]]; then
continue
fi
# Skip if subdomain is covered by a wildcard in ADDITIONAL_SAN
if is_covered_by_wildcard "${FULL_SUBDOMAIN}"; then
log_f "Subdomain '${FULL_SUBDOMAIN}' is covered by wildcard - skipping explicit subdomain"
continue
fi
# Validate and add subdomain
if check_domain "${FULL_SUBDOMAIN}"; then
VALIDATED_CONFIG_DOMAINS_SUBDOMAINS+=("${FULL_SUBDOMAIN}")
fi fi
done done
VALIDATED_CONFIG_DOMAINS+=("${VALIDATED_CONFIG_DOMAINS_SUBDOMAINS[*]}") VALIDATED_CONFIG_DOMAINS+=("${VALIDATED_CONFIG_DOMAINS_SUBDOMAINS[*]}")
@@ -273,7 +283,10 @@ while true; do
fi fi
# Only add mta-sts subdomain for alias domains # Only add mta-sts subdomain for alias domains
if [[ "mta-sts.${alias_domain}" != "${MAILCOW_HOSTNAME}" ]]; then if [[ "mta-sts.${alias_domain}" != "${MAILCOW_HOSTNAME}" ]]; then
if check_domain "mta-sts.${alias_domain}"; then # Skip if mta-sts subdomain is covered by a wildcard
if is_covered_by_wildcard "mta-sts.${alias_domain}"; then
log_f "Alias domain mta-sts subdomain 'mta-sts.${alias_domain}' is covered by wildcard - skipping"
elif check_domain "mta-sts.${alias_domain}"; then
VALIDATED_CONFIG_DOMAINS+=("mta-sts.${alias_domain}") VALIDATED_CONFIG_DOMAINS+=("mta-sts.${alias_domain}")
fi fi
fi fi
@@ -308,13 +321,31 @@ while true; do
done done
fi fi
# Check if MAILCOW_HOSTNAME is covered by a wildcard in ADDITIONAL_SAN
MAILCOW_HOSTNAME_COVERED=0
if [[ ! -z ${VALIDATED_MAILCOW_HOSTNAME} ]]; then
if is_covered_by_wildcard "${VALIDATED_MAILCOW_HOSTNAME}"; then
MAILCOW_PARENT_DOMAIN=$(echo ${VALIDATED_MAILCOW_HOSTNAME} | cut -d. -f2-)
log_f "MAILCOW_HOSTNAME '${VALIDATED_MAILCOW_HOSTNAME}' is covered by wildcard '*.${MAILCOW_PARENT_DOMAIN}' - skipping explicit hostname"
MAILCOW_HOSTNAME_COVERED=1
fi
fi
# Unique domains for server certificate # Unique domains for server certificate
if [[ ${ENABLE_SSL_SNI} == "y" ]]; then if [[ ${ENABLE_SSL_SNI} == "y" ]]; then
# create certificate for server name and fqdn SANs only # create certificate for server name and fqdn SANs only
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs)) if [[ ${MAILCOW_HOSTNAME_COVERED} == "1" ]]; then
SERVER_SAN_VALIDATED=($(echo ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
else
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
fi
else else
# create certificate for all domains, including all subdomains from other domains [*] # create certificate for all domains, including all subdomains from other domains [*]
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs)) if [[ ${MAILCOW_HOSTNAME_COVERED} == "1" ]]; then
SERVER_SAN_VALIDATED=($(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
else
SERVER_SAN_VALIDATED=(${VALIDATED_MAILCOW_HOSTNAME} $(echo ${VALIDATED_CONFIG_DOMAINS[*]} ${ADDITIONAL_VALIDATED_SAN[*]} | xargs -n1 | sort -u | xargs))
fi
fi fi
if [[ ! -z ${SERVER_SAN_VALIDATED[*]} ]]; then if [[ ! -z ${SERVER_SAN_VALIDATED[*]} ]]; then
CERT_NAME=${SERVER_SAN_VALIDATED[0]} CERT_NAME=${SERVER_SAN_VALIDATED[0]}
+29
View File
@@ -135,3 +135,32 @@ verify_challenge_path(){
return 1 return 1
fi fi
} }
# Check if a domain is covered by a wildcard (*.example.com) in ADDITIONAL_SAN
# Usage: is_covered_by_wildcard "subdomain.example.com"
# Returns: 0 if covered, 1 if not covered
# Note: Only returns 0 (covered) when DNS-01 challenge is enabled,
# as wildcards cannot be validated with HTTP-01 challenge
is_covered_by_wildcard() {
local DOMAIN=$1
# Only skip if DNS challenge is enabled (wildcards require DNS-01)
if [[ ${ACME_DNS_CHALLENGE} != "y" ]]; then
return 1
fi
# Return early if no ADDITIONAL_SAN is set
if [[ -z ${ADDITIONAL_SAN} ]]; then
return 1
fi
# Extract parent domain (e.g., mail.example.com -> example.com)
local PARENT_DOMAIN=$(echo ${DOMAIN} | cut -d. -f2-)
# Check if ADDITIONAL_SAN contains a wildcard for this parent domain
if [[ "${ADDITIONAL_SAN}" == *"*.${PARENT_DOMAIN}"* ]]; then
return 0 # Covered by wildcard
fi
return 1 # Not covered
}
+1 -1
View File
@@ -7,7 +7,7 @@ else
__dns_loader_standalone=0 __dns_loader_standalone=0
fi fi
CONFIG_PATH="${ACME_DNS_CONFIG_FILE:-/etc/acme/dns-101.conf}" CONFIG_PATH="${ACME_DNS_CONFIG_FILE:-/etc/acme/dns-01.conf}"
if [[ ! -f "${CONFIG_PATH}" ]]; then if [[ ! -f "${CONFIG_PATH}" ]]; then
if [[ $__dns_loader_standalone -eq 1 ]]; then if [[ $__dns_loader_standalone -eq 1 ]]; then
@@ -12,7 +12,7 @@ CERT_DOMAINS=(${DOMAINS[@]})
CERT_DOMAIN=${CERT_DOMAINS[0]} CERT_DOMAIN=${CERT_DOMAINS[0]}
ACME_BASE=/var/lib/acme ACME_BASE=/var/lib/acme
# Load optional DNS provider secrets from /etc/acme/dns-101.conf # Load optional DNS provider secrets from /etc/acme/dns-01.conf
if [[ -f /srv/load-dns-config.sh ]]; then if [[ -f /srv/load-dns-config.sh ]]; then
source /srv/load-dns-config.sh source /srv/load-dns-config.sh
if declare -F log_f >/dev/null; then if declare -F log_f >/dev/null; then
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.22 FROM alpine:3.21
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
+65 -90
View File
@@ -44,109 +44,90 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
else else
QUOTA_TABLE=quota2replica QUOTA_TABLE=quota2replica
fi fi
cat <<EOF > /etc/dovecot/conf.d/12-mysql.conf
# Autogenerated by mailcow - DO NOT TOUCH!
mysql /var/run/mysqld/mysqld.sock {
dbname=${DBNAME}
user=${DBUSER}
password=${DBPASS}
ssl = no
}
EOF
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-quota.conf cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-quota.conf
# Autogenerated by mailcow # Autogenerated by mailcow
dict_map priv/quota/storage { connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
sql_table = ${QUOTA_TABLE} map {
pattern = priv/quota/storage
table = ${QUOTA_TABLE}
username_field = username username_field = username
value_field bytes { value_field = bytes
}
} }
map {
dict_map priv/quota/messages { pattern = priv/quota/messages
sql_table = ${QUOTA_TABLE} table = ${QUOTA_TABLE}
username_field = username username_field = username
value_field messages { value_field = messages
}
} }
EOF EOF
# Create dict used for sieve pre and postfilters # Create dict used for sieve pre and postfilters
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
# Autogenerated by mailcow # Autogenerated by mailcow
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
dict_map priv/sieve/name/\$script_name { map {
sql_table = sieve_before pattern = priv/sieve/name/\$script_name
table = sieve_before
username_field = username username_field = username
value_field id { value_field = id
} fields {
script_name = \$script_name
# The script name field in the table to query
key_field script_name {
value = \$script_name
} }
} }
map {
dict_map priv/sieve/data/\$id { pattern = priv/sieve/data/\$id
sql_table = sieve_before table = sieve_before
username_field = username username_field = username
value_field script_data { value_field = script_data
} fields {
key_field id { id = \$id
value = \$id
} }
} }
EOF EOF
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
# Autogenerated by mailcow # Autogenerated by mailcow
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
dict_map priv/sieve/name/\$script_name { map {
sql_table = sieve_after pattern = priv/sieve/name/\$script_name
table = sieve_after
username_field = username username_field = username
value_field id { value_field = id
} fields {
key_field script_name { script_name = \$script_name
value = \$script_name
} }
} }
map {
dict_map priv/sieve/data/\$id { pattern = priv/sieve/data/\$id
sql_table = sieve_after table = sieve_after
username_field = username username_field = username
value_field script_data { value_field = script_data
} fields {
key_field id { id = \$id
value = \$id
} }
} }
EOF EOF
if [[ "${ACL_ANYONE}" == "allow" ]]; then echo -n ${ACL_ANYONE} > /etc/dovecot/acl_anyone
echo -n "yes" > /etc/dovecot/acl_anyone
else
echo -n "no" > /etc/dovecot/acl_anyone
fi
if [[ "${SKIP_FTS}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then if [[ "${SKIP_FTS}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo -e "\e[33mDetecting SKIP_FTS=y... not enabling Flatcurve (FTS) then...\e[0m" echo -e "\e[33mDetecting SKIP_FTS=y... not enabling Flatcurve (FTS) then...\e[0m"
echo -n 'quota quota_clone acl mail_crypt mail_crypt_acl mail_log mail_compress notify lazy_expunge' > /etc/dovecot/mail_plugins echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
echo -n 'quota quota_clone imap_quota imap_acl acl imap_sieve mail_crypt mail_crypt_acl mail_compress notify mail_log' > /etc/dovecot/mail_plugins_imap echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify listescape replication mail_log' > /etc/dovecot/mail_plugins_imap
echo -n 'quota quota_clone sieve acl mail_crypt mail_crypt_acl mail_compress notify' > /etc/dovecot/mail_plugins_lmtp echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
else else
echo -e "\e[32mDetecting SKIP_FTS=n... enabling Flatcurve (FTS)\e[0m" echo -e "\e[32mDetecting SKIP_FTS=n... enabling Flatcurve (FTS)\e[0m"
echo -n 'quota quota_clone acl mail_crypt mail_crypt_acl mail_log mail_compress notify fts fts_flatcurve lazy_expunge' > /etc/dovecot/mail_plugins echo -n 'quota acl zlib mail_crypt mail_crypt_acl mail_log notify fts fts_flatcurve listescape replication lazy_expunge' > /etc/dovecot/mail_plugins
echo -n 'quota quota_clone imap_quota imap_acl acl imap_sieve mail_crypt mail_crypt_acl mail_compress notify mail_log fts fts_flatcurve' > /etc/dovecot/mail_plugins_imap echo -n 'quota imap_quota imap_acl acl zlib imap_zlib imap_sieve mail_crypt mail_crypt_acl notify mail_log fts fts_flatcurve listescape replication' > /etc/dovecot/mail_plugins_imap
echo -n 'quota quota_clone sieve acl mail_crypt mail_crypt_acl mail_compress fts fts_flatcurve notify' > /etc/dovecot/mail_plugins_lmtp echo -n 'quota sieve acl zlib mail_crypt mail_crypt_acl fts fts_flatcurve notify listescape replication' > /etc/dovecot/mail_plugins_lmtp
fi fi
chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl chmod 644 /etc/dovecot/mail_plugins /etc/dovecot/mail_plugins_imap /etc/dovecot/mail_plugins_lmtp /templates/quarantine.tpl
cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-userdb.conf cat <<EOF > /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
# Autogenerated by mailcow # Autogenerated by mailcow
query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%{user | domain }}/%{user | username }/Maildir:VOLATILEDIR=/var/volatile/%{user}:INDEX=/var/vmail_index/%{user}') AS mail, '%{protocol}' AS protocol, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%{user}' AND (active = '1' OR active = '2') driver = mysql
connect = "host=/var/run/mysqld/mysqld.sock dbname=${DBNAME} user=${DBUSER} password=${DBPASS}"
user_query = SELECT CONCAT(JSON_UNQUOTE(JSON_VALUE(attributes, '$.mailbox_format')), mailbox_path_prefix, '%d/%n/${MAILDIR_SUB}:VOLATILEDIR=/var/volatile/%u:INDEX=/var/vmail_index/%u') AS mail, '%s' AS protocol, 5000 AS uid, 5000 AS gid, concat('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND (active = '1' OR active = '2')
iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2'; iterate_query = SELECT username FROM mailbox WHERE active = '1' OR active = '2';
EOF EOF
@@ -177,8 +158,8 @@ for cert_dir in /etc/ssl/mail/*/ ; do
domains=($(cat ${cert_dir}domains)) domains=($(cat ${cert_dir}domains))
for domain in ${domains[@]}; do for domain in ${domains[@]}; do
echo 'local_name '${domain}' {' >> /etc/dovecot/sni.conf; echo 'local_name '${domain}' {' >> /etc/dovecot/sni.conf;
echo ' ssl_server_cert_file = '${cert_dir}'cert.pem' >> /etc/dovecot/sni.conf; echo ' ssl_cert = <'${cert_dir}'cert.pem' >> /etc/dovecot/sni.conf;
echo ' ssl_server_key_file = '${cert_dir}'key.pem' >> /etc/dovecot/sni.conf; echo ' ssl_key = <'${cert_dir}'key.pem' >> /etc/dovecot/sni.conf;
echo '}' >> /etc/dovecot/sni.conf; echo '}' >> /etc/dovecot/sni.conf;
done done
done done
@@ -202,13 +183,11 @@ else
fi fi
cat <<EOF > /etc/dovecot/shared_namespace.conf cat <<EOF > /etc/dovecot/shared_namespace.conf
# Autogenerated by mailcow # Autogenerated by mailcow
namespace shared { namespace {
type = shared type = shared
separator = / separator = /
prefix = Shared/\$user/ prefix = Shared/%%u/
mail_driver = maildir location = maildir:%%h${MAILDIR_SUB_SHARED}:INDEX=~${MAILDIR_SUB_SHARED}/Shared/%%u
mail_path = %{owner_home}${MAILDIR_SUB_SHARED}
mail_index_private_path = ~${MAILDIR_SUB_SHARED}/Shared/%{owner_user}
subscriptions = no subscriptions = no
list = children list = children
} }
@@ -218,7 +197,7 @@ EOF
cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf cat <<EOF > /etc/dovecot/sogo_trusted_ip.conf
# Autogenerated by mailcow # Autogenerated by mailcow
remote ${IPV4_NETWORK}.248 { remote ${IPV4_NETWORK}.248 {
auth_allow_cleartext = yes disable_plaintext_auth = no
} }
EOF EOF
@@ -227,13 +206,9 @@ RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass echo -n ${RAND_PASS} > /etc/phpfpm/sogo-sso.pass
cat <<EOF > /etc/dovecot/sogo-sso.conf cat <<EOF > /etc/dovecot/sogo-sso.conf
# Autogenerated by mailcow # Autogenerated by mailcow
passdb static { passdb {
fields { driver = static
allow_real_nets=${IPV4_NETWORK}.248/32 args = allow_nets=${IPV4_NETWORK}.248/32 password={plain}${RAND_PASS}
}
password={plain}${RAND_PASS}
} }
EOF EOF
@@ -261,9 +236,9 @@ fi
if [[ "${SKIP_FTS}" =~ ^([nN][oO]|[nN])+$ ]]; then if [[ "${SKIP_FTS}" =~ ^([nN][oO]|[nN])+$ ]]; then
echo -e "\e[94mConfiguring FTS Settings...\e[0m" echo -e "\e[94mConfiguring FTS Settings...\e[0m"
echo -e "\e[94mSetting FTS Memory Limit (per process) to ${FTS_HEAP} MB\e[0m" echo -e "\e[94mSetting FTS Memory Limit (per process) to ${FTS_HEAP} MB\e[0m"
sed -i "s/vsz_limit\s*=\s*[0-9]*\s*MB*/vsz_limit=${FTS_HEAP} MB/" /etc/dovecot/conf.d/35-fts.conf sed -i "s/vsz_limit\s*=\s*[0-9]*\s*MB*/vsz_limit=${FTS_HEAP} MB/" /etc/dovecot/conf.d/fts.conf
echo -e "\e[94mSetting FTS Process Limit to ${FTS_PROCS}\e[0m" echo -e "\e[94mSetting FTS Process Limit to ${FTS_PROCS}\e[0m"
sed -i "s/process_limit\s*=\s*[0-9]*/process_limit=${FTS_PROCS}/" /etc/dovecot/conf.d/35-fts.conf sed -i "s/process_limit\s*=\s*[0-9]*/process_limit=${FTS_PROCS}/" /etc/dovecot/conf.d/fts.conf
fi fi
# 401 is user dovecot # 401 is user dovecot
@@ -275,16 +250,16 @@ else
chown 401 /mail_crypt/ecprivkey.pem /mail_crypt/ecpubkey.pem chown 401 /mail_crypt/ecprivkey.pem /mail_crypt/ecpubkey.pem
fi fi
# # Fix OpenSSL 3.X TLS1.0, 1.1 support (https://community.mailcow.email/d/4062-hi-all/20) # Fix OpenSSL 3.X TLS1.0, 1.1 support (https://community.mailcow.email/d/4062-hi-all/20)
# if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.conf /etc/dovecot/extra.conf; then if grep -qE 'ssl_min_protocol\s*=\s*(TLSv1|TLSv1\.1)\s*$' /etc/dovecot/dovecot.conf /etc/dovecot/extra.conf; then
# sed -i '/\[openssl_init\]/a ssl_conf = ssl_configuration' /etc/ssl/openssl.cnf sed -i '/\[openssl_init\]/a ssl_conf = ssl_configuration' /etc/ssl/openssl.cnf
# echo "[ssl_configuration]" >> /etc/ssl/openssl.cnf echo "[ssl_configuration]" >> /etc/ssl/openssl.cnf
# echo "system_default = tls_system_default" >> /etc/ssl/openssl.cnf echo "system_default = tls_system_default" >> /etc/ssl/openssl.cnf
# echo "[tls_system_default]" >> /etc/ssl/openssl.cnf echo "[tls_system_default]" >> /etc/ssl/openssl.cnf
# echo "MinProtocol = TLSv1" >> /etc/ssl/openssl.cnf echo "MinProtocol = TLSv1" >> /etc/ssl/openssl.cnf
# echo "CipherString = DEFAULT@SECLEVEL=0" >> /etc/ssl/openssl.cnf echo "CipherString = DEFAULT@SECLEVEL=0" >> /etc/ssl/openssl.cnf
# fi fi
# Compile sieve scripts # Compile sieve scripts
sievec /var/vmail/sieve/global_sieve_before.sieve sievec /var/vmail/sieve/global_sieve_before.sieve
@@ -47,7 +47,7 @@ try:
if max_score == "": if max_score == "":
max_score = 9999.0 max_score = 9999.0
def query_mysql(query, headers = True, update = False): def query_mysql(query, params = None, headers = True, update = False):
while True: while True:
try: try:
cnx = MySQLdb.connect(user=os.environ.get('DBUSER'), password=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci") cnx = MySQLdb.connect(user=os.environ.get('DBUSER'), password=os.environ.get('DBPASS'), database=os.environ.get('DBNAME'), charset="utf8mb4", collation="utf8mb4_general_ci")
@@ -57,7 +57,10 @@ try:
else: else:
break break
cur = cnx.cursor() cur = cnx.cursor()
cur.execute(query) if params:
cur.execute(query, params)
else:
cur.execute(query)
if not update: if not update:
result = [] result = []
columns = tuple( [d[0] for d in cur.description] ) columns = tuple( [d[0] for d in cur.description] )
@@ -76,7 +79,7 @@ try:
def notify_rcpt(rcpt, msg_count, quarantine_acl, category): def notify_rcpt(rcpt, msg_count, quarantine_acl, category):
if category == "add_header": category = "add header" if category == "add_header": category = "add header"
meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")' % (rcpt, max_score, category, category)) meta_query = query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = %s AND score < %s AND (action = %s OR "all" = %s)', (rcpt, max_score, category, category))
print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count)) print("%s: %d of %d messages qualify for notification" % (rcpt, len(meta_query), msg_count))
if len(meta_query) == 0: if len(meta_query) == 0:
return return
@@ -130,7 +133,7 @@ try:
server.sendmail(msg['From'], [str(redirect)] + [str(bcc)], text) server.sendmail(msg['From'], [str(redirect)] + [str(bcc)], text)
server.quit() server.quit()
for res in meta_query: for res in meta_query:
query_mysql('UPDATE quarantine SET notified = 1 WHERE id = "%d"' % (res['id']), update = True) query_mysql('UPDATE quarantine SET notified = 1 WHERE id = %s', (res['id'],), update = True)
r.hset('Q_LAST_NOTIFIED', record['rcpt'], time_now) r.hset('Q_LAST_NOTIFIED', record['rcpt'], time_now)
break break
except Exception as ex: except Exception as ex:
@@ -138,7 +141,7 @@ try:
print('%s' % (ex)) print('%s' % (ex))
time.sleep(3) time.sleep(3)
records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %f AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt' % (max_score)) records = query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %s AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt', (max_score,))
for record in records: for record in records:
attrs = '' attrs = ''
@@ -156,7 +159,7 @@ try:
except Exception as ex: except Exception as ex:
print('Could not determine last notification for %s, assuming never' % (record['rcpt'])) print('Could not determine last notification for %s, assuming never' % (record['rcpt']))
last_notification = 0 last_notification = 0
attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = "%s"' % (record['rcpt'])) attrs_json = query_mysql('SELECT attributes FROM mailbox WHERE username = %s', (record['rcpt'],))
attrs = attrs_json[0]['attributes'] attrs = attrs_json[0]['attributes']
if isinstance(attrs, str): if isinstance(attrs, str):
# if attr is str then just load it # if attr is str then just load it
+1 -1
View File
@@ -2,7 +2,7 @@ FROM debian:trixie-slim
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG RSPAMD_VER=rspamd_3.14.2-82~90302bc ARG RSPAMD_VER=rspamd_3.14.3-1~236eb65
ARG CODENAME=trixie ARG CODENAME=trixie
ENV LC_ALL=C ENV LC_ALL=C
+12 -6
View File
@@ -3,7 +3,7 @@
# Version: SOGo-5.12.4 # Version: SOGo-5.12.4
# #
# Applied security patches: # Applied security patches:
# - 16ab99e7cf8db2c30b211f0d5e338d7f9e3a9efb: XSS vulnerability in theme parameter # -
# #
# To add new patches, modify SOGO_SECURITY_PATCHES ARG below with space-separated commit hashes # To add new patches, modify SOGO_SECURITY_PATCHES ARG below with space-separated commit hashes
@@ -12,10 +12,10 @@ FROM debian:bookworm
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>" LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ARG SOGO_VERSION=SOGo-5.12.4 ARG SOGO_VERSION=SOGo-5.12.5
ARG SOPE_VERSION=SOPE-5.12.4 ARG SOPE_VERSION=SOPE-5.12.5
# Security patches to apply (space-separated commit hashes) # Security patches to apply (space-separated commit hashes)
ARG SOGO_SECURITY_PATCHES="16ab99e7cf8db2c30b211f0d5e338d7f9e3a9efb" ARG SOGO_SECURITY_PATCHES=""
# renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$ # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
ARG GOSU_VERSION=1.19 ARG GOSU_VERSION=1.19
ENV LC_ALL=C ENV LC_ALL=C
@@ -26,6 +26,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
git \ git \
build-essential \ build-essential \
gobjc \ gobjc \
pkg-config \
gnustep-make \ gnustep-make \
gnustep-base-runtime \ gnustep-base-runtime \
libgnustep-base-dev \ libgnustep-base-dev \
@@ -40,6 +41,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libcurl4-openssl-dev \ libcurl4-openssl-dev \
libzip-dev \ libzip-dev \
libytnef0-dev \ libytnef0-dev \
libwbxml2-dev \
curl \ curl \
ca-certificates \ ca-certificates \
# Runtime dependencies # Runtime dependencies
@@ -68,6 +70,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libcurl4 \ libcurl4 \
libzip4 \ libzip4 \
libytnef0 \ libytnef0 \
libwbxml2-1 \
# Download gosu # Download gosu
&& dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \ && dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" \
&& wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \ && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" \
@@ -97,6 +100,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& ./configure --disable-debug --disable-strip \ && ./configure --disable-debug --disable-strip \
&& make -j$(nproc) \ && make -j$(nproc) \
&& make install \ && make install \
&& cd /tmp/sogo/ActiveSync \
&& . /usr/share/GNUstep/Makefiles/GNUstep.sh \
&& make -j$(nproc) install \
&& cd / \ && cd / \
&& rm -rf /tmp/sogo \ && rm -rf /tmp/sogo \
# Strip binaries # Strip binaries
@@ -146,8 +152,8 @@ RUN echo "/usr/lib64" > /etc/ld.so.conf.d/sogo.conf \
# Create sogo user and group # Create sogo user and group
RUN groupadd -r -g 999 sogo \ RUN groupadd -r -g 999 sogo \
&& useradd -r -u 999 -g sogo -d /var/lib/sogo -s /bin/bash -c "SOGo Daemon" sogo \ && useradd -r -u 999 -g sogo -d /var/lib/sogo -s /bin/bash -c "SOGo Daemon" sogo \
&& mkdir -p /var/lib/sogo /var/run/sogo /var/log/sogo \ && mkdir -p /var/lib/sogo /var/run/sogo /var/log/sogo /var/spool/sogo \
&& chown -R sogo:sogo /var/lib/sogo /var/run/sogo /var/log/sogo && chown -R sogo:sogo /var/lib/sogo /var/run/sogo /var/log/sogo /var/spool/sogo
# Create symlinks for SOGo binaries # Create symlinks for SOGo binaries
RUN ln -s /usr/local/sbin/sogod /usr/sbin/sogod \ RUN ln -s /usr/local/sbin/sogod /usr/sbin/sogod \
+2 -2
View File
@@ -1,5 +1,5 @@
--- /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:57.987504204 +0200 --- /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:57.987504204 +0200
+++ /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:35.918291298 +0200 +++ /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox 2018-08-17 18:29:35.918291298 +0200
@@ -46,7 +46,7 @@ @@ -46,7 +46,7 @@
</md-item-template> </md-item-template>
</md-autocomplete> </md-autocomplete>
+11 -7
View File
@@ -130,18 +130,22 @@ chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
# Patch ACLs # Patch ACLs
#if [[ ${ACL_ANYONE} == 'allow' ]]; then #if [[ ${ACL_ANYONE} == 'allow' ]]; then
# #enable any or authenticated targets for ACL # #enable any or authenticated targets for ACL
# if patch -R -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then # if patch -R -sfN --dry-run /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then
# patch -R /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff; # patch -R /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff;
# fi # fi
#else #else
# #disable any or authenticated targets for ACL # #disable any or authenticated targets for ACL
# if patch -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then # if patch -sfN --dry-run /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff > /dev/null; then
# patch /usr/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff; # patch /usr/local/lib/GNUstep/SOGo/Templates/UIxAclEditor.wox < /acl.diff;
# fi # fi
#fi #fi
if patch -R -sfN --dry-run /usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff > /dev/null; then # Apply custom UI patch (reverse patch to ADD buttons)
patch -R /usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff; if patch -R -sfN --dry-run /usr/local/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff > /dev/null; then
echo "Applying navMailcowBtns patch (reverse to add buttons)..."
patch -R /usr/local/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox < /navMailcowBtns.diff;
else
echo "navMailcowBtns patch already applied or cannot be applied"
fi fi
# Rename custom logo, if any # Rename custom logo, if any
@@ -149,7 +153,7 @@ fi
# Rsync web content # Rsync web content
echo "Syncing web content with named volume" echo "Syncing web content with named volume"
rsync -a /usr/lib/GNUstep/SOGo/. /sogo_web/ rsync -a /usr/local/lib/GNUstep/SOGo/. /sogo_web/
# Chown backup path # Chown backup path
chown -R sogo:sogo /sogo_backup chown -R sogo:sogo /sogo_backup
+1
View File
@@ -37,5 +37,6 @@ RUN apk add --update \
COPY watchdog.sh /watchdog.sh COPY watchdog.sh /watchdog.sh
COPY check_mysql_slavestatus.sh /usr/lib/nagios/plugins/check_mysql_slavestatus.sh COPY check_mysql_slavestatus.sh /usr/lib/nagios/plugins/check_mysql_slavestatus.sh
COPY check_dns.sh /usr/lib/mailcow/check_dns.sh COPY check_dns.sh /usr/lib/mailcow/check_dns.sh
COPY client.cnf /etc/my.cnf.d/client.cnf
CMD ["/watchdog.sh"] CMD ["/watchdog.sh"]
+3
View File
@@ -0,0 +1,3 @@
[client]
ssl = false
ssl-verify-server-cert = false
+4 -4
View File
@@ -38,7 +38,7 @@ if [[ ! -p /tmp/com_pipe ]]; then
fi fi
# Wait for containers # Wait for containers
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do while ! mariadb-admin status --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
echo "Waiting for SQL..." echo "Waiting for SQL..."
sleep 2 sleep 2
done done
@@ -359,8 +359,8 @@ mysql_checks() {
while [ ${err_count} -lt ${THRESHOLD} ]; do while [ ${err_count} -lt ${THRESHOLD} ]; do
touch /tmp/mysql-mailcow; echo "$(tail -50 /tmp/mysql-mailcow)" > /tmp/mysql-mailcow touch /tmp/mysql-mailcow; echo "$(tail -50 /tmp/mysql-mailcow)" > /tmp/mysql-mailcow
err_c_cur=${err_count} err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_mysql -s /var/run/mysqld/mysqld.sock -u ${DBUSER} -p ${DBPASS} -d ${DBNAME} 2>> /tmp/mysql-mailcow 1>&2; err_count=$(( ${err_count} + $? )) /usr/lib/nagios/plugins/check_mysql -f /etc/my.cnf.d/client.cnf -s /var/run/mysqld/mysqld.sock -u ${DBUSER} -p ${DBPASS} -d ${DBNAME} 2>> /tmp/mysql-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
/usr/lib/nagios/plugins/check_mysql_query -s /var/run/mysqld/mysqld.sock -u ${DBUSER} -p ${DBPASS} -d ${DBNAME} -q "SELECT COUNT(*) FROM information_schema.tables" 2>> /tmp/mysql-mailcow 1>&2; err_count=$(( ${err_count} + $? )) /usr/lib/nagios/plugins/check_mysql_query -f /etc/my.cnf.d/client.cnf -s /var/run/mysqld/mysqld.sock -u ${DBUSER} -p ${DBPASS} -d ${DBNAME} -q "SELECT COUNT(*) FROM information_schema.tables" 2>> /tmp/mysql-mailcow 1>&2; err_count=$(( ${err_count} + $? ))
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
progress "MySQL/MariaDB" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c} progress "MySQL/MariaDB" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
@@ -384,7 +384,7 @@ mysql_repl_checks() {
while [ ${err_count} -lt ${THRESHOLD} ]; do while [ ${err_count} -lt ${THRESHOLD} ]; do
touch /tmp/mysql_repl_checks; echo "$(tail -50 /tmp/mysql_repl_checks)" > /tmp/mysql_repl_checks touch /tmp/mysql_repl_checks; echo "$(tail -50 /tmp/mysql_repl_checks)" > /tmp/mysql_repl_checks
err_c_cur=${err_count} err_c_cur=${err_count}
/usr/lib/nagios/plugins/check_mysql_slavestatus.sh -S /var/run/mysqld/mysqld.sock -u root -p ${DBROOT} 2>> /tmp/mysql_repl_checks 1>&2; err_count=$(( ${err_count} + $? )) /usr/lib/nagios/plugins/check_mysql_slavestatus.sh -o /etc/my.cnf.d/client.cnf -S /var/run/mysqld/mysqld.sock -u root -p ${DBROOT} 2>> /tmp/mysql_repl_checks 1>&2; err_count=$(( ${err_count} + $? ))
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1 [ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} )) [ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
progress "MySQL/MariaDB replication" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c} progress "MySQL/MariaDB replication" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
+5 -9
View File
@@ -1,5 +1,4 @@
function auth_password_verify(request, password) function auth_password_verify(request, password)
request.domain = request.auth_user:match("@(.+)") or nil
if request.domain == nil then if request.domain == nil then
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user" return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "No such user"
end end
@@ -10,10 +9,10 @@ function auth_password_verify(request, password)
https.TIMEOUT = 30 https.TIMEOUT = 30
local req = { local req = {
username = request.auth_user, username = request.user,
password = password, password = password,
real_rip = request.remote_ip, real_rip = request.real_rip,
service = request.protocol service = request.service
} }
local req_json = json.encode(req) local req_json = json.encode(req)
local res = {} local res = {}
@@ -34,6 +33,7 @@ function auth_password_verify(request, password)
-- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry, -- Returning PASSDB_RESULT_INTERNAL_FAILURE keeps the existing cache entry,
-- even if the TTL has expired. Useful to avoid cache eviction during backend issues. -- even if the TTL has expired. Useful to avoid cache eviction during backend issues.
if c ~= 200 and c ~= 401 then if c ~= 200 and c ~= 401 then
dovecot.i_info("HTTP request failed with " .. c .. " for user " .. request.user)
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error" return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Upstream error"
end end
@@ -46,7 +46,7 @@ function auth_password_verify(request, password)
end end
if response_json.success == true then if response_json.success == true then
return dovecot.auth.PASSDB_RESULT_OK, { msg = "" } return dovecot.auth.PASSDB_RESULT_OK, ""
end end
return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate" return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "Failed to authenticate"
@@ -55,7 +55,3 @@ end
function auth_passdb_lookup(req) function auth_passdb_lookup(req)
return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "" return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, ""
end end
function auth_passdb_get_cache_key()
return "%{protocol}:%{user | username}\t:%{password}"
end
-3
View File
@@ -1,3 +0,0 @@
# /etc/dovecot/conf.d/05-core.conf
# Core, single-line settings that don't fit elsewhere.
recipient_delimiter = +
-13
View File
@@ -1,13 +0,0 @@
# /etc/dovecot/conf.d/10-logging.conf
# Logging and debug.
#mail_debug = yes
#auth_debug = yes
#log_debug = category=fts-flatcurve
log_path = syslog
log_timestamp = "%Y-%m-%d %H:%M:%S "
login_log_format_elements = "user=<%{user}> method=%{mechanism} rip=%{remote_ip} lip=%{local_ip} mpid=%{mail_pid} %{secured} session=<%{session}>"
# Mail event logging.
mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
mail_log_fields = uid box msgid size
mail_log_cached_only = yes
-10
View File
@@ -1,10 +0,0 @@
# /etc/dovecot/conf.d/10-mail.conf
# Mail storage paths and core mail settings.
mail_home = /var/vmail/%{user | domain }/%{user | username }
mail_driver = maildir
mail_path = ~/Maildir
mail_index_path = /var/vmail_index/%{user}
mail_plugins = </etc/dovecot/mail_plugins
mail_shared_explicit_inbox = yes
mailbox_list_storage_escape_char = "\\"
mail_prefetch_count = 30
-13
View File
@@ -1,13 +0,0 @@
# /etc/dovecot/conf.d/10-ssl.conf
# TLS/SSL settings.
ssl_min_protocol = TLSv1.2
ssl_cipher_list = ALL:!ADH:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL:!eNULL:!3DES:!MD5:!PSK:!DSS:!RC4:!SEED:!IDEA:+HIGH:+MEDIUM
ssl_options = no_ticket
#ssl_dh_parameters_length = 2048
ssl_server {
prefer_ciphers = server
dh_file = /etc/ssl/mail/dhparams.pem
cert_file = /etc/ssl/mail/cert.pem
key_file = /etc/ssl/mail/key.pem
}
-3
View File
@@ -1,3 +0,0 @@
# /etc/dovecot/conf.d/11-sql.conf
# Default SQL driver used by SQL-based dicts/userdb.
sql_driver = mysql
-8
View File
@@ -1,8 +0,0 @@
# Autogenerated by mailcow - DO NOT TOUCH!
mysql /var/run/mysqld/mysqld.sock {
dbname=mailcow
user=mailcow
password=D8O9BIivJc7Pb2VCfpAeLbAzUOZ0
ssl = no
}
@@ -1,7 +0,0 @@
# /etc/dovecot/conf.d/12-storage-attachments.conf
# External attachment storage.
fs mail_ext_attachment {
fs_driver = posix
mail_ext_attachment_path = /var/attachments
mail_ext_attachment_min_size = 128k
}
@@ -1,10 +0,0 @@
# /etc/dovecot/conf.d/15-performance.conf
# Performance and mailbox tuning.
# Enable only when you do not manually touch cur/.
maildir_very_dirty_syncs = yes
# NFS examples | Only modify if using NFS!:
#mm ap_disable = yes
#mail_fsync = always
#mail_nfs_index = yes
#mail_nfs_storage = yes
-40
View File
@@ -1,40 +0,0 @@
# /etc/dovecot/conf.d/20-auth.conf
# Authentication mechanisms, master/user separation, passdb chain, auth cache.
auth_mechanisms = plain login
auth_allow_cleartext = yes
auth_master_user_separator = *
auth_cache_verify_password_with_worker = yes
auth_cache_negative_ttl = 60s
auth_cache_ttl = 300s
auth_cache_size = 10M
auth_verbose_passwords = sha1:6
# 1) Lua password verification (blocking, return mapping).
passdb lua {
driver = lua
lua_file = /etc/dovecot/auth/passwd-verify.lua
lua_settings {
blocking=yes
result_success = return-ok
result_failure = continue
result_internalfail = continue
}
}
# 2) Master password for master user logins.
passdb master {
driver = passwd-file
passwd_file_path = /etc/dovecot/dovecot-master.passwd
master = yes
skip = authenticated
}
# 3) Mandatory return layer: empty Lua (e.g. for forced reset).
passdb empty-lua {
driver = lua
lua_file = /etc/dovecot/auth/passwd-verify.lua
lua_settings {
blocking = yes
}
}
-11
View File
@@ -1,11 +0,0 @@
# /etc/dovecot/conf.d/20-userdb.conf
# User database chain.
userdb passwd {
driver = passwd-file
passwd_file_path = /etc/dovecot/dovecot-master.userdb
}
userdb sql {
!include /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
skip = found
}
-144
View File
@@ -1,144 +0,0 @@
# /etc/dovecot/conf.d/25-services.conf
# All service listeners and workers.
# doveadm remote admin
# Set doveadm_password in extra.conf.
service doveadm {
inet_listener doveadm {
port = 12345
}
vsz_limit = 2048 MB
}
# dict
service dict {
unix_listener dict {
mode = 0660
user = vmail
group = vmail
}
}
# log
service log {
user = dovenull
}
# config socket
service config {
unix_listener config {
user = root
group = vmail
mode = 0660
}
}
# anvil socket
service anvil {
unix_listener anvil {
user = vmail
group = vmail
mode = 0660
}
}
# auth sockets and inet
service auth {
inet_listener auth-inet {
port = 10001
}
unix_listener auth-master {
mode = 0600
user = vmail
}
unix_listener auth-userdb {
mode = 0600
user = vmail
}
vsz_limit = 2G
}
# managesieve login
service managesieve-login {
inet_listener sieve {
port = 4190
}
inet_listener sieve_haproxy {
port = 14190
haproxy = yes
}
service_restart_request_count = 1
process_min_avail = 2
vsz_limit = 1G
}
# imap login
service imap-login {
service_restart_request_count = 1
process_min_avail = 2
process_limit = 10000
vsz_limit = 1G
user = dovenull
inet_listener imap_haproxy {
port = 10143
haproxy = yes
}
inet_listener imaps_haproxy {
port = 10993
ssl = yes
haproxy = yes
}
}
# pop3 login
service pop3-login {
service_restart_request_count = 1
process_min_avail = 1
vsz_limit = 1G
inet_listener pop3_haproxy {
port = 10110
haproxy = yes
}
inet_listener pop3s_haproxy {
port = 10995
ssl = yes
haproxy = yes
}
}
# imap worker
service imap {
executable = imap
user = vmail
vsz_limit = 1G
}
# managesieve worker
service managesieve {
process_limit = 256
}
# lmtp
service lmtp {
inet_listener lmtp-inet {
port = 24
}
user = vmail
}
# quota warning hook
service quota-warning {
executable = script /usr/local/bin/quota_notify.py
user = vmail
unix_listener quota-warning {
user = vmail
}
}
# stats
service stats {
unix_listener stats-writer {
mode = 0660
user = vmail
}
}
@@ -1,17 +0,0 @@
# /etc/dovecot/conf.d/30-protocols.conf
# IMAP protocol specifics.
protocol imap {
mail_plugins = </etc/dovecot/mail_plugins_imap
imap_metadata = yes
}
# LMTP protocol specifics.
protocol lmtp {
mail_plugins = </etc/dovecot/mail_plugins_lmtp
auth_socket_path = /var/run/dovecot/auth-master
}
# ManageSieve protocol specifics.
protocol sieve {
managesieve_logout_format = bytes=%i/%o
}
-45
View File
@@ -1,45 +0,0 @@
# mailcow FTS Flatcurve Settings, change them as you like.
# Maximum term length can be set via the 'maxlen' argument (maxlen is
# specified in bytes, not number of UTF-8 characters)
language_tokenizer_address_token_maxlen = 100
language_tokenizer_generic_algorithm = simple
language_tokenizer_generic_token_maxlen = 30
# These are not flatcurve settings, but required for Dovecot FTS. See
# Dovecot FTS Configuration link above for further information.
language en {
default = yes
language_filters = lowercase snowball english-possessive stopwords
}
language de {
language_filters = lowercase snowball stopwords
}
language es {
language_filters = lowercase snowball stopwords
}
language_tokenizers = generic email-address
fts_search_timeout = 300s
fts_autoindex = yes
# Tweak this setting if you only want to ensure big and frequent folders are indexed, not all.
fts_autoindex_max_recent_msgs = 20
fts flatcurve {
substring_search = no
}
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
service indexer-worker {
# Max amount of simultaniously running indexer jobs.
process_limit=1
# Max amount of RAM used by EACH indexer process.
vsz_limit=128 MB
}
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
-12
View File
@@ -1,12 +0,0 @@
# /etc/dovecot/conf.d/40-acl.conf
# ACL and shared mailboxes.
imap_acl_allow_anyone = </etc/dovecot/acl_anyone
acl_sharing_map {
dict file {
path = /var/vmail/shared-mailboxes.db
}
}
acl_driver = vfile
acl_user = %{user}
@@ -1,7 +0,0 @@
# /etc/dovecot/conf.d/40-attributes.conf
# User/mail attributes.
mail_attribute {
dict file {
path = /etc/dovecot/dovecot-attributes
}
}
-25
View File
@@ -1,25 +0,0 @@
# /etc/dovecot/conf.d/50-quota.conf
# Quota configuration and notifications.
quota "User quota" {
driver = count
warning warn-95 {
quota_storage_percentage = 95
execute quota-warning {
args = 95 %{user}
}
}
warning warn-80 {
quota_storage_percentage = 80
execute quota-warning {
args = 80 %{user}
}
}
}
quota_clone {
dict proxy {
name = mysql_quota
}
}
@@ -1,97 +0,0 @@
# /etc/dovecot/conf.d/60-sieve-pipeline.conf
# Complete Sieve pipeline: personal/global scripts, plugins, limits, training.
# Global before/after (file and dict)
sieve_script before {
type = before
driver = file
path = /var/vmail/sieve/global_sieve_before.sieve
}
sieve_script before2 {
type = before
driver = dict
name = active
dict proxy {
name = sieve_before
}
bin_path = /var/vmail/sieve_before_bindir/%{user}
}
sieve_script after {
type = after
driver = file
path = /var/vmail/sieve/global_sieve_after.sieve
}
sieve_script after2 {
type = after
driver = dict
name = active
dict proxy {
name = sieve_after
}
bin_path = /var/vmail/sieve_after_bindir/%{user}
}
# Personal scripts
sieve_script personal {
type = personal
driver = file
path = ~/sieve
active_path = ~/.dovecot.sieve
}
# Plugins and behavior
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_vacation_send_from_recipient = yes
sieve_redirect_envelope_from = recipient
# IMAPSieve training
imapsieve_from Junk {
sieve_script ham {
type = before
cause = copy
path = /usr/lib/dovecot/sieve/report-ham.sieve
}
}
mailbox Junk {
sieve_script spam {
type = before
cause = copy
path = /usr/lib/dovecot/sieve/report-spam.sieve
}
}
# Extprograms and extensions
sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
sieve_plugins {
sieve_extprograms = yes
}
sieve_global_extensions {
vnd.dovecot.pipe = yes
vnd.dovecot.execute = yes
}
# Limits and duplicate handling
sieve_max_script_size = 1M
sieve_max_redirects = 100
sieve_max_actions = 101
sieve_quota_script_count = 0
sieve_quota_storage_size = 0
sieve_vacation_min_period = 5s
sieve_vacation_max_period = 365d
sieve_vacation_default_period = 60s
sieve_duplicate_default_period = 1m
sieve_duplicate_max_period = 7d
sieve_extensions {
vacation-seconds = yes
editheader = yes
}
# pipe sockets in /var/run/dovecot/sieve-pipe
sieve_pipe_socket_dir = sieve-pipe
# execute sockets in /var/run/dovecot/sieve-execute
sieve_execute_socket_dir = sieve-execute
-6
View File
@@ -1,6 +0,0 @@
# /etc/dovecot/conf.d/70-crypto.conf
# Global mail-crypt keys.
crypt_global_private_key global {
crypt_private_key_file = /mail_crypt/ecprivkey.pem
}
crypt_global_public_key_file = /mail_crypt/ecpubkey.pem
@@ -1,3 +0,0 @@
# /etc/dovecot/conf.d/80-compress.conf
# Compression settings.
mail_compress_write_method = lz4
-18
View File
@@ -1,18 +0,0 @@
# /etc/dovecot/conf.d/90-dict.conf
# Dict declarations and SQL bindings.
dict_server {
dict sieve_after {
driver = sql
!include /etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
}
dict sieve_before {
driver = sql
!include /etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
}
dict mysql_quota {
driver = sql
!include /etc/dovecot/sql/dovecot-dict-sql-quota.conf
}
}
-7
View File
@@ -1,7 +0,0 @@
# /etc/dovecot/conf.d/90-limits.conf
# Connection and memory limits; doveadm port.
mail_max_userip_connections = 500
imap_max_line_length = 2 M
default_client_limit = 10400
default_vsz_limit = 1024 M
doveadm_port = 12345
-22
View File
@@ -1,22 +0,0 @@
# /etc/dovecot/conf.d/99-includes.conf
# Late includes and site-specific bits.
# Mailbox layout includes (if used)
!include /etc/dovecot/dovecot.folders.conf
# Optional replication
!include_try /etc/dovecot/mail_replica.conf
# Existing includes you already had
!include_try /etc/dovecot/sni.conf
!include_try /etc/dovecot/sogo_trusted_ip.conf
!include_try /etc/dovecot/shared_namespace.conf
!include_try /etc/dovecot/conf.d/fts.conf
# Remote auth override
remote 127.0.0.1 {
auth_allow_cleartext = yes
}
# Outbound submission target
submission_host = postfix:588
+37
View File
@@ -0,0 +1,37 @@
# mailcow FTS Flatcurve Settings, change them as you like.
plugin {
fts_autoindex = yes
fts_autoindex_exclude = \Junk
fts_autoindex_exclude2 = \Trash
# Tweak this setting if you only want to ensure big and frequent folders are indexed, not all.
fts_autoindex_max_recent_msgs = 20
fts = flatcurve
# Maximum term length can be set via the 'maxlen' argument (maxlen is
# specified in bytes, not number of UTF-8 characters)
fts_tokenizer_email_address = maxlen=100
fts_tokenizer_generic = algorithm=simple maxlen=30
# These are not flatcurve settings, but required for Dovecot FTS. See
# Dovecot FTS Configuration link above for further information.
fts_languages = en es de
fts_tokenizers = generic email-address
# OPTIONAL: Recommended default FTS core configuration
fts_filters = normalizer-icu snowball stopwords
fts_filters_en = lowercase snowball english-possessive stopwords
fts_index_timeout = 300s
}
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
service indexer-worker {
# Max amount of simultaniously running indexer jobs.
process_limit=1
# Max amount of RAM used by EACH indexer process.
vsz_limit=128 MB
}
### THIS PART WILL BE CHANGED BY MODIFYING mailcow.conf AUTOMATICALLY DURING RUNTIME! ###
+306 -29
View File
@@ -1,34 +1,311 @@
# /etc/dovecot/dovecot.conf # --------------------------------------------------------------------------
# Base file kept minimal. All real config lives under conf.d/. # Please create a file "extra.conf" for persistent overrides to dovecot.conf
dovecot_config_version = 2.4.0 # --------------------------------------------------------------------------
dovecot_storage_version = 2.4.0 # LDAP example:
#passdb {
# args = /etc/dovecot/ldap/passdb.conf
# driver = ldap
#}
listen = *,[::] auth_mechanisms = plain login
#mail_debug = yes
#auth_debug = yes
#log_debug = category=fts-flatcurve # Activate Logging for Flatcurve FTS Searchings
log_path = syslog
disable_plaintext_auth = yes
# Uncomment on NFS share
#mmap_disable = yes
#mail_fsync = always
#mail_nfs_index = yes
#mail_nfs_storage = yes
login_log_format_elements = "user=<%u> method=%m rip=%r lip=%l mpid=%e %c %k"
mail_home = /var/vmail/%d/%n
mail_location = maildir:~/
mail_plugins = </etc/dovecot/mail_plugins
mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
mail_attachment_dir = /var/attachments
mail_attachment_min_size = 128k
# Significantly speeds up very large mailboxes, but is only safe to enable if
# you do not manually modify the files in the `cur` directories in
# mailcowdockerized_vmail-vol-1.
# https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-performance/
maildir_very_dirty_syncs = yes
# Dovecot 2.2
#ssl_protocols = !SSLv3
# Dovecot 2.3
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
ssl_cipher_list = ALL:!ADH:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL:!eNULL:!3DES:!MD5:!PSK:!DSS:!RC4:!SEED:!IDEA:+HIGH:+MEDIUM
# Default in Dovecot 2.3
ssl_options = no_compression no_ticket
# New in Dovecot 2.3
ssl_dh = </etc/ssl/mail/dhparams.pem
# Dovecot 2.2
#ssl_dh_parameters_length = 2048
log_timestamp = "%Y-%m-%d %H:%M:%S "
recipient_delimiter = +
auth_master_user_separator = *
mail_shared_explicit_inbox = yes
mail_prefetch_count = 30
passdb {
driver = lua
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes cache_key=%s:%u:%w
result_success = return-ok
result_failure = continue
result_internalfail = continue
}
# try a master passwd
passdb {
driver = passwd-file
args = /etc/dovecot/dovecot-master.passwd
master = yes
skip = authenticated
}
# check for regular password - if empty (e.g. force-passwd-reset), previous pass=yes passdbs also fail
# a return of the following passdb is mandatory
passdb {
driver = lua
args = file=/etc/dovecot/auth/passwd-verify.lua blocking=yes
}
# Set doveadm_password=your-secret-password in data/conf/dovecot/extra.conf (create if missing)
service doveadm {
inet_listener {
port = 12345
}
vsz_limit=2048 MB
}
!include /etc/dovecot/dovecot.folders.conf
protocols = imap sieve lmtp pop3 protocols = imap sieve lmtp pop3
service dict {
unix_listener dict {
mode = 0660
user = vmail
group = vmail
}
}
service log {
user = dovenull
}
service config {
unix_listener config {
user = root
group = vmail
mode = 0660
}
}
service auth {
inet_listener auth-inet {
port = 10001
}
unix_listener auth-master {
mode = 0600
user = vmail
}
unix_listener auth-userdb {
mode = 0600
user = vmail
}
vsz_limit = 2G
}
service managesieve-login {
inet_listener sieve {
port = 4190
}
inet_listener sieve_haproxy {
port = 14190
haproxy = yes
}
service_count = 1
process_min_avail = 2
vsz_limit = 1G
}
service imap-login {
service_count = 1
process_min_avail = 2
process_limit = 10000
vsz_limit = 1G
user = dovenull
inet_listener imap_haproxy {
port = 10143
haproxy = yes
}
inet_listener imaps_haproxy {
port = 10993
ssl = yes
haproxy = yes
}
}
service pop3-login {
service_count = 1
process_min_avail = 1
vsz_limit = 1G
inet_listener pop3_haproxy {
port = 10110
haproxy = yes
}
inet_listener pop3s_haproxy {
port = 10995
ssl = yes
haproxy = yes
}
}
service imap {
executable = imap
user = vmail
vsz_limit = 1G
}
service managesieve {
process_limit = 256
}
service lmtp {
inet_listener lmtp-inet {
port = 24
}
user = vmail
}
listen = *,[::]
ssl_cert = </etc/ssl/mail/cert.pem
ssl_key = </etc/ssl/mail/key.pem
userdb {
driver = passwd-file
args = /etc/dovecot/dovecot-master.userdb
}
userdb {
args = /etc/dovecot/sql/dovecot-dict-sql-userdb.conf
driver = sql
skip = found
}
protocol imap {
mail_plugins = </etc/dovecot/mail_plugins_imap
imap_metadata = yes
}
mail_attribute_dict = file:%h/dovecot-attributes
protocol lmtp {
mail_plugins = </etc/dovecot/mail_plugins_lmtp
auth_socket_path = /var/run/dovecot/auth-master
}
protocol sieve {
managesieve_logout_format = bytes=%i/%o
}
plugin {
# Allow "any" or "authenticated" to be used in ACLs
acl_anyone = </etc/dovecot/acl_anyone
acl_shared_dict = file:/var/vmail/shared-mailboxes.db
acl = vfile
acl_user = %u
quota = dict:Userquota::proxy::sqlquota
quota_rule2 = Trash:storage=+100%%
sieve = /var/vmail/sieve/%u.sieve
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_vacation_send_from_recipient = yes
sieve_redirect_envelope_from = recipient
# From elsewhere to Spam folder
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_before = file:/usr/lib/dovecot/sieve/report-spam.sieve
# END
# From Spam folder to elsewhere
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/usr/lib/dovecot/sieve/report-ham.sieve
# END
master_user = %u
quota_warning = storage=95%% quota-warning 95 %u
quota_warning2 = storage=80%% quota-warning 80 %u
sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
sieve_extensions = +notify +imapflags +vacation-seconds +editheader
sieve_max_script_size = 1M
sieve_max_redirects = 100
sieve_max_actions = 101
sieve_quota_max_scripts = 0
sieve_quota_max_storage = 0
listescape_char = "\\"
sieve_vacation_min_period = 5s
sieve_vacation_max_period = 0
sieve_vacation_default_period = 60s
sieve_before = /var/vmail/sieve/global_sieve_before.sieve
sieve_before2 = dict:proxy::sieve_before;name=active;bindir=/var/vmail/sieve_before_bindir
sieve_after = dict:proxy::sieve_after;name=active;bindir=/var/vmail/sieve_after_bindir
sieve_after2 = /var/vmail/sieve/global_sieve_after.sieve
sieve_duplicate_default_period = 1m
sieve_duplicate_max_period = 7d
!include_try /etc/dovecot/conf.d/05-core.conf # -- Global keys
!include_try /etc/dovecot/conf.d/10-logging.conf mail_crypt_global_private_key = </mail_crypt/ecprivkey.pem
!include_try /etc/dovecot/conf.d/10-mail.conf mail_crypt_global_public_key = </mail_crypt/ecpubkey.pem
!include_try /etc/dovecot/conf.d/10-ssl.conf mail_crypt_save_version = 2
!include_try /etc/dovecot/conf.d/11-sql.conf
!include_try /etc/dovecot/conf.d/12-mysql.conf
!include_try /etc/dovecot/conf.d/12-storage-attachments.conf
!include_try /etc/dovecot/conf.d/15-performance.conf
!include_try /etc/dovecot/conf.d/20-auth.conf
!include_try /etc/dovecot/conf.d/20-userdb.conf
!include_try /etc/dovecot/conf.d/25-services.conf
!include_try /etc/dovecot/conf.d/30-protocols.conf
!include_try /etc/dovecot/conf.d/35-fts.conf
!include_try /etc/dovecot/conf.d/40-acl.conf
!include_try /etc/dovecot/conf.d/40-attributes.conf
!include_try /etc/dovecot/conf.d/50-quota.conf
!include_try /etc/dovecot/conf.d/60-sieve-pipeline.conf
!include_try /etc/dovecot/conf.d/70-crypto.conf
!include_try /etc/dovecot/conf.d/80-compress.conf
!include_try /etc/dovecot/conf.d/80-mail-logging.conf
!include_try /etc/dovecot/conf.d/90-limits.conf
!include_try /etc/dovecot/conf.d/90-dict.conf
!include_try /etc/dovecot/conf.d/99-includes.conf
# Last: local overrides # Enable compression while saving, lz4 Dovecot v2.3.17+
zlib_save = lz4
mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
mail_log_fields = uid box msgid size
mail_log_cached_only = yes
# Try set mail_replica
!include_try /etc/dovecot/mail_replica.conf
}
service quota-warning {
executable = script /usr/local/bin/quota_notify.py
# use some unprivileged user for executing the quota warnings
user = vmail
unix_listener quota-warning {
user = vmail
}
}
dict {
sqlquota = mysql:/etc/dovecot/sql/dovecot-dict-sql-quota.conf
sieve_after = mysql:/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf
sieve_before = mysql:/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf
}
remote 127.0.0.1 {
disable_plaintext_auth = no
}
submission_host = postfix:588
mail_max_userip_connections = 500
service stats {
unix_listener stats-writer {
mode = 0660
user = vmail
}
}
imap_max_line_length = 2 M
auth_cache_verify_password_with_worker = yes
auth_cache_negative_ttl = 60s
auth_cache_ttl = 300s
auth_cache_size = 10M
auth_verbose_passwords = sha1:6
service replicator {
process_min_avail = 1
}
service aggregator {
fifo_listener replication-notify-fifo {
user = vmail
}
unix_listener replication-notify {
user = vmail
}
}
service replicator {
unix_listener replicator-doveadm {
mode = 0666
}
}
replication_max_conns = 10
doveadm_port = 12345
replication_dsync_parameters = -d -l 30 -U -n INBOX
# <Includes>
!include_try /etc/dovecot/sni.conf
!include_try /etc/dovecot/sogo_trusted_ip.conf
!include_try /etc/dovecot/extra.conf !include_try /etc/dovecot/extra.conf
!include_try /etc/dovecot/shared_namespace.conf
!include_try /etc/dovecot/conf.d/fts.conf
# </Includes>
default_client_limit = 10400
default_vsz_limit = 1024 M
+1 -6
View File
@@ -1,14 +1,10 @@
namespace inbox { namespace inbox {
inbox = yes inbox = yes
location =
separator = / separator = /
mailbox storage/* {
quota_storage_extra = 100M
}
mailbox "Trash" { mailbox "Trash" {
auto = subscribe auto = subscribe
special_use = \Trash special_use = \Trash
quota_storage_percentage = 100
fts_autoindex = no
} }
mailbox "Deleted Messages" { mailbox "Deleted Messages" {
special_use = \Trash special_use = \Trash
@@ -199,7 +195,6 @@ namespace inbox {
mailbox "Junk" { mailbox "Junk" {
auto = subscribe auto = subscribe
special_use = \Junk special_use = \Junk
fts_autoindex = no
} }
mailbox "Junk-E-Mail" { mailbox "Junk-E-Mail" {
special_use = \Junk special_use = \Junk
@@ -261,19 +261,19 @@ location ~* /sogo$ {
} }
location /SOGo.woa/WebServerResources/ { location /SOGo.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/; alias /usr/local/lib/GNUstep/SOGo/WebServerResources/;
} }
location /.woa/WebServerResources/ { location /.woa/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/; alias /usr/local/lib/GNUstep/SOGo/WebServerResources/;
} }
location /SOGo/WebServerResources/ { location /SOGo/WebServerResources/ {
alias /usr/lib/GNUstep/SOGo/WebServerResources/; alias /usr/local/lib/GNUstep/SOGo/WebServerResources/;
} }
location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) { location (^/SOGo/so/ControlPanel/Products/[^/]*UI/Resources/.*\.(jpg|png|gif|css|js)$) {
alias /usr/lib/GNUstep/SOGo/$1.SOGo/Resources/$2; alias /usr/local/lib/GNUstep/SOGo/$1.SOGo/Resources/$2;
} }
{% endif %} {% endif %}
+85 -13
View File
@@ -1,6 +1,6 @@
# Whitelist generated by Postwhite v3.4 on Sun Mar 1 00:29:01 UTC 2026 # Whitelist generated by Postwhite v3.4 on Wed Apr 1 00:33:31 UTC 2026
# https://github.com/stevejenkins/postwhite/ # https://github.com/stevejenkins/postwhite/
# 2174 total rules # 2246 total rules
2a00:1450:4000::/36 permit 2a00:1450:4000::/36 permit
2a01:111:f400::/48 permit 2a01:111:f400::/48 permit
2a01:111:f403:2800::/53 permit 2a01:111:f403:2800::/53 permit
@@ -31,8 +31,10 @@
2a02:a60:0:5::/64 permit 2a02:a60:0:5::/64 permit
2a0f:f640::/56 permit 2a0f:f640::/56 permit
2c0f:fb50:4000::/36 permit 2c0f:fb50:4000::/36 permit
2.207.151.32/27 permit
2.207.151.53 permit 2.207.151.53 permit
2.207.217.30 permit 2.207.217.30 permit
2.207.223.160/27 permit
3.64.237.68 permit 3.64.237.68 permit
3.65.3.180 permit 3.65.3.180 permit
3.70.123.177 permit 3.70.123.177 permit
@@ -58,8 +60,8 @@
8.40.222.0/23 permit 8.40.222.0/23 permit
8.40.222.250/31 permit 8.40.222.250/31 permit
12.130.86.238 permit 12.130.86.238 permit
13.107.213.51 permit 13.107.213.38 permit
13.107.246.51 permit 13.107.246.38 permit
13.108.16.0/20 permit 13.108.16.0/20 permit
13.110.208.0/21 permit 13.110.208.0/21 permit
13.110.209.0/24 permit 13.110.209.0/24 permit
@@ -368,6 +370,7 @@
64.132.88.0/23 permit 64.132.88.0/23 permit
64.132.92.0/24 permit 64.132.92.0/24 permit
64.181.194.190 permit 64.181.194.190 permit
64.181.213.254 permit
64.207.219.7 permit 64.207.219.7 permit
64.207.219.8 permit 64.207.219.8 permit
64.207.219.9 permit 64.207.219.9 permit
@@ -453,7 +456,11 @@
66.218.75.252/31 permit 66.218.75.252/31 permit
66.218.75.254 permit 66.218.75.254 permit
66.220.144.128/25 permit 66.220.144.128/25 permit
66.220.144.178 permit
66.220.144.179 permit
66.220.155.0/24 permit 66.220.155.0/24 permit
66.220.155.178 permit
66.220.155.179 permit
66.220.157.0/25 permit 66.220.157.0/25 permit
66.231.80.0/20 permit 66.231.80.0/20 permit
66.240.227.0/24 permit 66.240.227.0/24 permit
@@ -518,6 +525,8 @@
69.162.98.0/24 permit 69.162.98.0/24 permit
69.169.224.0/20 permit 69.169.224.0/20 permit
69.171.232.0/24 permit 69.171.232.0/24 permit
69.171.232.180 permit
69.171.232.181 permit
69.171.244.0/23 permit 69.171.244.0/23 permit
70.42.149.35 permit 70.42.149.35 permit
72.3.185.0/24 permit 72.3.185.0/24 permit
@@ -639,6 +648,7 @@
77.238.189.148/30 permit 77.238.189.148/30 permit
79.135.106.0/24 permit 79.135.106.0/24 permit
79.135.107.0/24 permit 79.135.107.0/24 permit
80.225.160.128/25 permit
81.169.146.243 permit 81.169.146.243 permit
81.169.146.245 permit 81.169.146.245 permit
81.169.146.246 permit 81.169.146.246 permit
@@ -657,6 +667,9 @@
82.165.159.45 permit 82.165.159.45 permit
82.165.159.130 permit 82.165.159.130 permit
82.165.159.131 permit 82.165.159.131 permit
84.8.68.0/25 permit
84.8.192.128/25 permit
84.8.224.128/25 permit
85.9.206.169 permit 85.9.206.169 permit
85.9.210.45 permit 85.9.210.45 permit
85.158.136.0/21 permit 85.158.136.0/21 permit
@@ -1408,6 +1421,20 @@
129.146.88.28 permit 129.146.88.28 permit
129.146.147.105 permit 129.146.147.105 permit
129.146.236.58 permit 129.146.236.58 permit
129.148.135.0/25 permit
129.148.148.0/25 permit
129.148.164.0/25 permit
129.148.180.0/25 permit
129.148.215.0/25 permit
129.149.6.0/25 permit
129.149.22.0/25 permit
129.149.38.0/25 permit
129.149.52.0/25 permit
129.149.68.0/25 permit
129.149.84.0/25 permit
129.149.100.0/25 permit
129.149.118.0/25 permit
129.149.126.0/25 permit
129.151.67.221 permit 129.151.67.221 permit
129.153.62.216 permit 129.153.62.216 permit
129.153.104.71 permit 129.153.104.71 permit
@@ -1420,16 +1447,19 @@
129.159.22.159 permit 129.159.22.159 permit
129.159.87.137 permit 129.159.87.137 permit
129.213.195.191 permit 129.213.195.191 permit
130.35.116.0/25 permit
130.61.9.72 permit 130.61.9.72 permit
130.162.39.83 permit 130.162.39.83 permit
130.248.172.0/24 permit 130.248.172.0/24 permit
130.248.173.0/24 permit 130.248.173.0/24 permit
131.186.12.0/25 permit
131.253.30.0/24 permit 131.253.30.0/24 permit
131.253.121.0/26 permit 131.253.121.0/26 permit
132.145.13.209 permit 132.145.13.209 permit
132.226.26.225 permit 132.226.26.225 permit
132.226.49.32 permit 132.226.49.32 permit
132.226.56.24 permit 132.226.56.24 permit
134.98.248.128/25 permit
134.128.64.0/19 permit 134.128.64.0/19 permit
134.128.96.0/19 permit 134.128.96.0/19 permit
134.170.27.8 permit 134.170.27.8 permit
@@ -1460,6 +1490,10 @@
136.147.182.0/24 permit 136.147.182.0/24 permit
136.147.224.0/20 permit 136.147.224.0/20 permit
136.179.50.206 permit 136.179.50.206 permit
136.248.224.128/25 permit
136.248.232.128/25 permit
138.1.108.0/25 permit
138.1.170.0/24 permit
139.60.152.0/22 permit 139.60.152.0/22 permit
139.138.35.44 permit 139.138.35.44 permit
139.138.46.121 permit 139.138.46.121 permit
@@ -1468,6 +1502,7 @@
139.138.57.55 permit 139.138.57.55 permit
139.138.58.119 permit 139.138.58.119 permit
139.167.79.86 permit 139.167.79.86 permit
139.177.108.0/25 permit
139.180.17.0/24 permit 139.180.17.0/24 permit
140.238.148.191 permit 140.238.148.191 permit
141.148.55.217 permit 141.148.55.217 permit
@@ -1506,6 +1541,9 @@
146.88.28.0/24 permit 146.88.28.0/24 permit
146.148.116.76 permit 146.148.116.76 permit
147.154.32.0/25 permit 147.154.32.0/25 permit
147.154.63.0/24 permit
147.154.126.0/24 permit
147.154.191.0/24 permit
147.243.1.47 permit 147.243.1.47 permit
147.243.1.48 permit 147.243.1.48 permit
147.243.1.153 permit 147.243.1.153 permit
@@ -1517,12 +1555,16 @@
149.72.234.184 permit 149.72.234.184 permit
149.72.248.236 permit 149.72.248.236 permit
149.97.173.180 permit 149.97.173.180 permit
149.118.160.128/25 permit
150.136.21.199 permit 150.136.21.199 permit
150.230.98.160 permit 150.230.98.160 permit
151.145.38.14 permit 151.145.38.14 permit
152.67.105.195 permit 152.67.105.195 permit
152.69.200.236 permit 152.69.200.236 permit
152.70.155.126 permit 152.70.155.126 permit
155.248.135.128/25 permit
155.248.140.0/25 permit
155.248.148.0/25 permit
155.248.208.51 permit 155.248.208.51 permit
155.248.220.138 permit 155.248.220.138 permit
155.248.234.149 permit 155.248.234.149 permit
@@ -1531,10 +1573,14 @@
157.58.30.128/25 permit 157.58.30.128/25 permit
157.58.196.96/29 permit 157.58.196.96/29 permit
157.58.249.3 permit 157.58.249.3 permit
157.137.32.128/25 permit
157.137.96.128/25 permit
157.151.208.65 permit 157.151.208.65 permit
157.255.1.64/29 permit 157.255.1.64/29 permit
158.101.211.207 permit 158.101.211.207 permit
158.247.16.0/20 permit 158.247.16.0/20 permit
158.247.100.0/25 permit
159.13.4.0/25 permit
159.92.154.0/24 permit 159.92.154.0/24 permit
159.92.155.0/24 permit 159.92.155.0/24 permit
159.92.157.0/24 permit 159.92.157.0/24 permit
@@ -1568,6 +1614,7 @@
161.71.64.0/20 permit 161.71.64.0/20 permit
162.88.4.0/23 permit 162.88.4.0/23 permit
162.88.8.0/24 permit 162.88.8.0/24 permit
162.88.24.0/23 permit
162.88.24.0/24 permit 162.88.24.0/24 permit
162.88.25.0/24 permit 162.88.25.0/24 permit
162.88.36.0/24 permit 162.88.36.0/24 permit
@@ -1585,10 +1632,12 @@
164.152.23.32 permit 164.152.23.32 permit
164.152.25.241 permit 164.152.25.241 permit
164.177.132.168/30 permit 164.177.132.168/30 permit
165.1.100.0/25 permit
165.173.128.0/24 permit 165.173.128.0/24 permit
165.173.180.1 permit 165.173.180.1 permit
165.173.180.250/31 permit 165.173.180.250/31 permit
165.173.182.250/31 permit 165.173.182.250/31 permit
165.173.189.205 permit
166.78.68.0/22 permit 166.78.68.0/22 permit
166.78.68.221 permit 166.78.68.221 permit
166.78.69.169 permit 166.78.69.169 permit
@@ -1607,9 +1656,12 @@
167.89.75.126 permit 167.89.75.126 permit
167.89.75.136 permit 167.89.75.136 permit
167.89.75.164 permit 167.89.75.164 permit
167.89.101.2 permit
167.89.101.192/28 permit
167.220.67.232/29 permit 167.220.67.232/29 permit
168.107.248.128/25 permit
168.110.160.128/25 permit
168.110.248.128/25 permit
168.129.184.128/25 permit
168.129.248.128/25 permit
168.138.5.36 permit 168.138.5.36 permit
168.138.73.51 permit 168.138.73.51 permit
168.138.77.31 permit 168.138.77.31 permit
@@ -1745,8 +1797,24 @@
192.18.139.154 permit 192.18.139.154 permit
192.18.145.36 permit 192.18.145.36 permit
192.18.152.58 permit 192.18.152.58 permit
192.22.32.128/25 permit
192.22.96.128/25 permit
192.22.160.128/25 permit
192.22.224.128/25 permit
192.28.128.0/18 permit 192.28.128.0/18 permit
192.29.24.0/25 permit
192.29.44.0/25 permit
192.29.72.0/25 permit
192.29.88.0/25 permit
192.29.103.128/25 permit 192.29.103.128/25 permit
192.29.134.0/25 permit
192.29.151.128/25 permit
192.29.172.0/25 permit
192.29.178.0/25 permit
192.29.200.0/25 permit
192.29.216.0/25 permit
192.29.232.0/25 permit
192.29.248.0/25 permit
192.30.252.0/22 permit 192.30.252.0/22 permit
192.161.144.0/20 permit 192.161.144.0/20 permit
192.162.87.0/24 permit 192.162.87.0/24 permit
@@ -1754,11 +1822,6 @@
192.237.159.42 permit 192.237.159.42 permit
192.237.159.43 permit 192.237.159.43 permit
192.254.112.0/20 permit 192.254.112.0/20 permit
192.254.112.60 permit
192.254.112.98/31 permit
192.254.113.10 permit
192.254.113.101 permit
192.254.114.176 permit
193.109.254.0/23 permit 193.109.254.0/23 permit
193.122.128.100 permit 193.122.128.100 permit
193.123.56.63 permit 193.123.56.63 permit
@@ -1927,6 +1990,7 @@
207.211.30.128/25 permit 207.211.30.128/25 permit
207.211.31.0/25 permit 207.211.31.0/25 permit
207.211.41.113 permit 207.211.41.113 permit
207.211.132.0/25 permit
207.218.90.0/24 permit 207.218.90.0/24 permit
207.218.90.122 permit 207.218.90.122 permit
207.250.68.0/24 permit 207.250.68.0/24 permit
@@ -1934,6 +1998,8 @@
208.43.21.28/30 permit 208.43.21.28/30 permit
208.43.21.64/29 permit 208.43.21.64/29 permit
208.43.21.72/30 permit 208.43.21.72/30 permit
208.56.9.224 permit
208.56.13.196 permit
208.64.132.0/22 permit 208.64.132.0/22 permit
208.71.40.63 permit 208.71.40.63 permit
208.71.40.64/31 permit 208.71.40.64/31 permit
@@ -1960,6 +2026,7 @@
208.71.42.214 permit 208.71.42.214 permit
208.72.249.240/29 permit 208.72.249.240/29 permit
208.75.120.0/22 permit 208.75.120.0/22 permit
208.76.62.0/23 permit
208.76.62.0/24 permit 208.76.62.0/24 permit
208.76.63.0/24 permit 208.76.63.0/24 permit
208.82.237.96/29 permit 208.82.237.96/29 permit
@@ -2120,6 +2187,7 @@
216.136.168.80/28 permit 216.136.168.80/28 permit
216.139.64.0/19 permit 216.139.64.0/19 permit
216.145.221.0/24 permit 216.145.221.0/24 permit
216.146.32.0/23 permit
216.146.32.0/24 permit 216.146.32.0/24 permit
216.146.33.0/24 permit 216.146.33.0/24 permit
216.198.0.0/18 permit 216.198.0.0/18 permit
@@ -2140,6 +2208,7 @@
223.165.120.0/23 permit 223.165.120.0/23 permit
2001:0868:0100:0600::/64 permit 2001:0868:0100:0600::/64 permit
2001:4860:4000::/36 permit 2001:4860:4000::/36 permit
2001:4860:4864::/56 permit
2001:748:100:40::2:0/112 permit 2001:748:100:40::2:0/112 permit
2001:748:400:1300::3 permit 2001:748:400:1300::3 permit
2001:748:400:1300::4 permit 2001:748:400:1300::4 permit
@@ -2157,10 +2226,12 @@
2001:748:400:3301::3 permit 2001:748:400:3301::3 permit
2001:748:400:3301::4 permit 2001:748:400:3301::4 permit
2404:6800:4000::/36 permit 2404:6800:4000::/36 permit
2404:6800:4864::/56 permit
2607:13c0:0001:0000:0000:0000:0000:7000/116 permit 2607:13c0:0001:0000:0000:0000:0000:7000/116 permit
2607:13c0:0002:0000:0000:0000:0000:1000/116 permit 2607:13c0:0002:0000:0000:0000:0000:1000/116 permit
2607:13c0:0004:0000:0000:0000:0000:0000/116 permit 2607:13c0:0004:0000:0000:0000:0000:0000/116 permit
2607:f8b0:4000::/36 permit 2607:f8b0:4000::/36 permit
2607:f8b0:4864::/56 permit
2620:109:c003:104::/64 permit 2620:109:c003:104::/64 permit
2620:109:c003:104::215 permit 2620:109:c003:104::215 permit
2620:109:c006:104::/64 permit 2620:109:c006:104::/64 permit
@@ -2172,8 +2243,9 @@
2620:10d:c09c:400::8:1 permit 2620:10d:c09c:400::8:1 permit
2620:119:50c0:207::/64 permit 2620:119:50c0:207::/64 permit
2620:119:50c0:207::215 permit 2620:119:50c0:207::215 permit
2620:1ec:46::51 permit 2620:1ec:46::38 permit
2620:1ec:bdf::51 permit 2620:1ec:bdf::38 permit
2800:3f0:4000::/36 permit 2800:3f0:4000::/36 permit
2800:3f0:4864::/56 permit
49.12.4.251 permit # checks.mailcow.email 49.12.4.251 permit # checks.mailcow.email
2a01:4f8:c17:7906::10 permit # checks.mailcow.email 2a01:4f8:c17:7906::10 permit # checks.mailcow.email
+4
View File
@@ -287,6 +287,8 @@ function user_login($user, $pass, $extra = null){
return false; return false;
} }
$row['attributes'] = json_decode($row['attributes'], true);
// check for tfa authenticators // check for tfa authenticators
$authenticators = get_tfa($user); $authenticators = get_tfa($user);
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) { if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
@@ -343,6 +345,8 @@ function user_login($user, $pass, $extra = null){
return false; return false;
} }
$row['attributes'] = json_decode($row['attributes'], true);
// check for tfa authenticators // check for tfa authenticators
$authenticators = get_tfa($user); $authenticators = get_tfa($user);
if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) { if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0 && !$is_internal) {
+8
View File
@@ -108,6 +108,14 @@ function fwdhost($_action, $_data = null) {
} }
break; break;
case 'delete': case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$hosts = (array)$_data['forwardinghost']; $hosts = (array)$_data['forwardinghost'];
foreach ($hosts as $host) { foreach ($hosts as $host) {
try { try {
+118 -29
View File
@@ -9,6 +9,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data_log = $_data; $_data_log = $_data;
!isset($_data_log['password']) ?: $_data_log['password'] = '*'; !isset($_data_log['password']) ?: $_data_log['password'] = '*';
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*'; !isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
// Track mailboxes affected by alias operations for incremental SOGo updates
$update_sogo_mailboxes = array();
switch ($_action) { switch ($_action) {
case 'add': case 'add':
switch ($_type) { switch ($_type) {
@@ -886,6 +890,17 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('alias_added', $address, $id) 'msg' => array('alias_added', $address, $id)
); );
// Track affected mailboxes for SOGo update
if (!empty($goto)) {
$gotos = array_map('trim', explode(',', $goto));
foreach ($gotos as $g) {
if (filter_var($g, FILTER_VALIDATE_EMAIL) &&
!in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) {
$update_sogo_mailboxes[] = $g;
}
}
}
} }
break; break;
case 'alias_domain': case 'alias_domain':
@@ -1096,10 +1111,21 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : 0; $relayhost = (isset($_data['relayhost'])) ? intval($_data['relayhost']) : 0;
$quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']); $quarantine_notification = (isset($_data['quarantine_notification'])) ? strval($_data['quarantine_notification']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
$quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']); $quarantine_category = (isset($_data['quarantine_category'])) ? strval($_data['quarantine_category']) : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
// Validate quarantine_category
if (!in_array($quarantine_category, array('add_header', 'reject', 'all'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'quarantine_category_invalid'
);
return false;
}
$quota_b = ($quota_m * 1048576); $quota_b = ($quota_m * 1048576);
$attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : ''; $attribute_hash = (!empty($_data['attribute_hash'])) ? $_data['attribute_hash'] : '';
if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){ if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){
$force_pw_update = 0; $force_pw_update = 0;
}
if ($authsource == 'generic-oidc'){
$force_tfa = 0; $force_tfa = 0;
} }
$mailbox_attrs = json_encode( $mailbox_attrs = json_encode(
@@ -1368,15 +1394,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
), $_extra); ), $_extra);
} }
try { // Track affected mailboxes for SOGo update
update_sogo_static_view($username); $update_sogo_mailboxes[] = $username;
} catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
}
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
@@ -1607,6 +1626,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('resource_added', htmlspecialchars($name)) 'msg' => array('resource_added', htmlspecialchars($name))
); );
// Track affected mailboxes for SOGo update
$update_sogo_mailboxes[] = $name;
break; break;
case 'domain_templates': case 'domain_templates':
if ($_SESSION['mailcow_cc_role'] != "admin") { if ($_SESSION['mailcow_cc_role'] != "admin") {
@@ -1720,6 +1742,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['tagged_mail_handler']); $attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['tagged_mail_handler']);
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']); $attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_notification']);
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']); $attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : strval($MAILBOX_DEFAULT_ATTRIBUTES['quarantine_category']);
// Validate quarantine_category
if (!in_array($attr["quarantine_category"], array('add_header', 'reject', 'all'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
'msg' => 'quarantine_category_invalid'
);
return false;
}
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s"; $attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : "s";
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : ""; $attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : "";
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']); $attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update']);
@@ -2049,6 +2080,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false; return false;
} }
foreach ($usernames as $username) { foreach ($usernames as $username) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
if ($_data['spam_score'] == "default") { if ($_data['spam_score'] == "default") {
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')"); AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
@@ -2725,6 +2764,28 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('alias_modified', htmlspecialchars($address)) 'msg' => array('alias_modified', htmlspecialchars($address))
); );
// Track affected mailboxes for SOGo update (both old and new goto addresses)
// Old goto: to remove alias from their view
if (!empty($is_now['goto'])) {
$old_gotos = array_map('trim', explode(',', $is_now['goto']));
foreach ($old_gotos as $g) {
if (filter_var($g, FILTER_VALIDATE_EMAIL) &&
!in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) {
$update_sogo_mailboxes[] = $g;
}
}
}
// New goto: to add alias to their view
if (!empty($goto)) {
$new_gotos = array_map('trim', explode(',', $goto));
foreach ($new_gotos as $g) {
if (filter_var($g, FILTER_VALIDATE_EMAIL) &&
!in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) {
$update_sogo_mailboxes[] = $g;
}
}
}
} }
break; break;
case 'domain': case 'domain':
@@ -3093,6 +3154,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){ if (in_array($authsource, array('keycloak', 'generic-oidc', 'ldap'))){
$force_pw_update = 0; $force_pw_update = 0;
}
if ($authsource == 'generic-oidc'){
$force_tfa = 0; $force_tfa = 0;
} }
$pw_recovery_email = (isset($_data['pw_recovery_email']) && $authsource == 'mailcow') ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email']; $pw_recovery_email = (isset($_data['pw_recovery_email']) && $authsource == 'mailcow') ? $_data['pw_recovery_email'] : $is_now['attributes']['recovery_email'];
@@ -3439,15 +3502,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'msg' => array('mailbox_modified', $username) 'msg' => array('mailbox_modified', $username)
); );
try { // Track affected mailboxes for SOGo update
update_sogo_static_view($username); $update_sogo_mailboxes[] = $username;
} catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
}
} }
return true; return true;
break; break;
@@ -3760,6 +3816,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : $is_now['tagged_mail_handler']; $attr["tagged_mail_handler"] = (!empty($_data['tagged_mail_handler'])) ? $_data['tagged_mail_handler'] : $is_now['tagged_mail_handler'];
$attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification']; $attr["quarantine_notification"] = (!empty($_data['quarantine_notification'])) ? $_data['quarantine_notification'] : $is_now['quarantine_notification'];
$attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category']; $attr["quarantine_category"] = (!empty($_data['quarantine_category'])) ? $_data['quarantine_category'] : $is_now['quarantine_category'];
// Validate quarantine_category
if (!in_array($attr["quarantine_category"], array('add_header', 'reject', 'all'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_extra),
'msg' => 'quarantine_category_invalid'
);
continue;
}
$attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame']; $attr["rl_frame"] = (!empty($_data['rl_frame'])) ? $_data['rl_frame'] : $is_now['rl_frame'];
$attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : $is_now['rl_value']; $attr["rl_value"] = (!empty($_data['rl_value'])) ? $_data['rl_value'] : $is_now['rl_value'];
$attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : $is_now['force_pw_update']; $attr["force_pw_update"] = isset($_data['force_pw_update']) ? intval($_data['force_pw_update']) : $is_now['force_pw_update'];
@@ -4076,6 +4141,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('resource_modified', htmlspecialchars($name)) 'msg' => array('resource_modified', htmlspecialchars($name))
); );
// Track affected mailboxes for SOGo update
$update_sogo_mailboxes[] = $name;
} }
break; break;
case 'domain_wide_footer': case 'domain_wide_footer':
@@ -5780,6 +5848,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
// Track affected mailboxes for SOGo update (capture before deletion)
if (!empty($alias_data['goto'])) {
$gotos = array_map('trim', explode(',', $alias_data['goto']));
foreach ($gotos as $g) {
if (filter_var($g, FILTER_VALIDATE_EMAIL) &&
!in_array($g, array('null@localhost', 'spam@localhost', 'ham@localhost'))) {
$update_sogo_mailboxes[] = $g;
}
}
}
$stmt = $pdo->prepare("DELETE FROM `alias` WHERE `id` = :id"); $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `id` = :id");
$stmt->execute(array( $stmt->execute(array(
':id' => $alias_data['id'] ':id' => $alias_data['id']
@@ -6038,20 +6118,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
continue; continue;
} }
try {
update_sogo_static_view($username);
}catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => $e->getMessage()
);
}
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('mailbox_removed', htmlspecialchars($username)) 'msg' => array('mailbox_removed', htmlspecialchars($username))
); );
// Track affected mailboxes for SOGo update
$update_sogo_mailboxes[] = $username;
} }
return true; return true;
break; break;
@@ -6153,6 +6227,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr), 'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('resource_removed', htmlspecialchars($name)) 'msg' => array('resource_removed', htmlspecialchars($name))
); );
// Track affected mailboxes for SOGo update
$update_sogo_mailboxes[] = $name;
} }
break; break;
case 'tags_domain': case 'tags_domain':
@@ -6259,9 +6336,21 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
} }
break; break;
} }
if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource')) && getenv('SKIP_SOGO') != "y") { if ($_action != 'get' && in_array($_type, array('domain', 'alias', 'alias_domain', 'resource', 'mailbox')) && getenv('SKIP_SOGO') != "y") {
try { try {
update_sogo_static_view(); if (($_type == 'alias' || $_type == 'resource' || $_type == 'mailbox') && !empty($update_sogo_mailboxes)) {
// INCREMENTAL UPDATE: Update only affected mailboxes/resources
$update_sogo_mailboxes = array_unique($update_sogo_mailboxes);
foreach ($update_sogo_mailboxes as $mailbox) {
update_sogo_static_view($mailbox);
}
}
else {
// FULL REBUILD: For domain and alias_domain operations or if no tracked mailboxes
// Domain operations affect all mailboxes
// Alias_domain operations affect entire target domain
update_sogo_static_view();
}
}catch (PDOException $e) { }catch (PDOException $e) {
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
+1 -1
View File
@@ -89,7 +89,7 @@ $globalVariables = [
'app_links' => $app_links, 'app_links' => $app_links,
'app_links_processed' => $app_links_processed, 'app_links_processed' => $app_links_processed,
'is_root_uri' => (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) == '/'), 'is_root_uri' => (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) == '/'),
'uri' => $_SERVER['REQUEST_URI'], 'uri' => parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?: '/',
]; ];
foreach ($globalVariables as $globalVariableName => $globalVariableValue) { foreach ($globalVariables as $globalVariableName => $globalVariableValue) {
+3 -1
View File
@@ -13,7 +13,9 @@ $twig = new Environment($loader, [
// functions // functions
$twig->addFunction(new TwigFunction('query_string', function (array $params = []) { $twig->addFunction(new TwigFunction('query_string', function (array $params = []) {
return http_build_query(array_merge($_GET, $params)); $allowed = ['lang', 'mobileconfig'];
$filtered = array_intersect_key($_GET, array_flip($allowed));
return http_build_query(array_merge($filtered, $params));
})); }));
$twig->addFunction(new TwigFunction('is_uri', function (string $uri, string $where = null) { $twig->addFunction(new TwigFunction('is_uri', function (string $uri, string $where = null) {
+2 -2
View File
@@ -345,7 +345,7 @@ $(document).ready(function() {
$('.main-logo-dark').addClass('d-none'); $('.main-logo-dark').addClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_dark.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_dark.png');
localStorage.setItem('theme', 'light'); localStorage.setItem('mailcow_theme', 'light');
}else{ }else{
$('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">'); $('head').append('<link id="dark-mode-theme" rel="stylesheet" type="text/css" href="/css/themes/mailcow-darkmode.css">');
$('#dark-mode-toggle').prop('checked', true); $('#dark-mode-toggle').prop('checked', true);
@@ -353,7 +353,7 @@ $(document).ready(function() {
$('.main-logo-dark').removeClass('d-none'); $('.main-logo-dark').removeClass('d-none');
if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo').length) $('#rspamd_logo').attr('src', '/img/rspamd_logo_light.png');
if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png'); if ($('#rspamd_logo_sm').length) $('#rspamd_logo_sm').attr('src', '/img/rspamd_logo_light.png');
localStorage.setItem('theme', 'dark'); localStorage.setItem('mailcow_theme', 'dark');
} }
} }
+5
View File
@@ -1128,6 +1128,11 @@ jQuery(function($){
item.ua = escapeHtml(item.ua); item.ua = escapeHtml(item.ua);
} }
item.ua = '<span style="font-size:small">' + item.ua + '</span>'; item.ua = '<span style="font-size:small">' + item.ua + '</span>';
if (item.user == null) {
item.user = 'unknown';
} else {
item.user = escapeHtml(item.user);
}
if (item.service == "activesync") { if (item.service == "activesync") {
item.service = '<span class="badge fs-6 bg-info">ActiveSync</span>'; item.service = '<span class="badge fs-6 bg-info">ActiveSync</span>';
} }
+4 -3
View File
@@ -1,5 +1,6 @@
$(document).ready(function() { $(document).ready(function() {
var theme = localStorage.getItem("theme"); var theme = localStorage.getItem("mailcow_theme");
localStorage.clear(); if (theme !== null) {
localStorage.setItem("theme", theme); localStorage.setItem("mailcow_theme", theme);
}
}); });
+6 -6
View File
@@ -226,18 +226,18 @@ jQuery(function($){
} }
if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) { if (typeof data.fuzzy_hashes === 'object' && data.fuzzy_hashes !== null && data.fuzzy_hashes.length !== 0) {
$.each(data.fuzzy_hashes, function (index, value) { $.each(data.fuzzy_hashes, function (index, value) {
$('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + value + '</p>'); $('#qid_detail_fuzzy').append('<p style="font-family:monospace">' + escapeHtml(value) + '</p>');
}); });
} else { } else {
$('#qid_detail_fuzzy').append('-'); $('#qid_detail_fuzzy').append('-');
} }
if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') { if (typeof data.score !== 'undefined' && typeof data.action !== 'undefined') {
if (data.action == "add header") { if (data.action == "add header") {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.junk_folder + '</span>'); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + escapeHtml(data.score) + '</b> - ' + lang.junk_folder + '</span>');
} else if (data.action == "reject") { } else if (data.action == "reject") {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + data.score + '</b> - ' + lang.rejected + '</span>'); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-danger"><b>' + escapeHtml(data.score) + '</b> - ' + lang.rejected + '</span>');
} else if (data.action == "rewrite subject") { } else if (data.action == "rewrite subject") {
$('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + data.score + '</b> - ' + lang.rewrite_subject + '</span>'); $('#qid_detail_score').append('<span class="label-rspamd-action badge fs-6 bg-warning"><b>' + escapeHtml(data.score) + '</b> - ' + lang.rewrite_subject + '</span>');
} }
} }
if (typeof data.recipients !== 'undefined') { if (typeof data.recipients !== 'undefined') {
@@ -254,8 +254,8 @@ jQuery(function($){
qAtts.text(''); qAtts.text('');
$.each(data.attachments, function(index, value) { $.each(data.attachments, function(index, value) {
qAtts.append( qAtts.append(
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' + '<p><a href="/inc/ajax/qitem_details.php?id=' + escapeHtml(qitem) + '&amp;att=' + index + '" target="_blank">' + escapeHtml(value[0]) + '</a> (' + escapeHtml(value[1]) + ')' +
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>' ' - <small><a href="' + escapeHtml(value[3]) + '" target="_blank">' + lang.check_hash + '</a></small></p>'
); );
}); });
} }
+2 -2
View File
@@ -98,8 +98,8 @@ jQuery(function($){
var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"}); var local_datetime = datetime.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
var service = '<div class="badge bg-secondary">' + item.service.toUpperCase() + '</div>'; var service = '<div class="badge bg-secondary">' + item.service.toUpperCase() + '</div>';
var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-key-fill"></i><span class="ms-1">' + escapeHtml(item.app_password_name || "App") + '</span></a>' : ''; var app_password = item.app_password ? ' <a href="/edit/app-passwd/' + item.app_password + '"><i class="bi bi-key-fill"></i><span class="ms-1">' + escapeHtml(item.app_password_name || "App") + '</span></a>' : '';
var real_rip = item.real_rip.startsWith("Web") ? item.real_rip : '<a href="https://bgp.tools/prefix/' + item.real_rip + '" target="_blank">' + item.real_rip + "</a>"; var real_rip = item.real_rip.startsWith("Web") ? escapeHtml(item.real_rip) : '<a href="https://bgp.tools/prefix/' + escapeHtml(item.real_rip) + '" target="_blank">' + escapeHtml(item.real_rip) + "</a>";
var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + item.location.toLowerCase() + '"></span>' : ''; var ip_location = item.location ? ' <span class="flag-icon flag-icon-' + escapeHtml(item.location.toLowerCase()) + '"></span>' : '';
var ip_data = real_rip + ip_location + app_password; var ip_data = real_rip + ip_location + app_password;
$(".last-sasl-login").append(` $(".last-sasl-login").append(`
+5
View File
@@ -0,0 +1,5 @@
{
"acl": {
"login_as": "E-poçt qutusu istifadəçisi olaraq daxil ol"
}
}
+1
View File
@@ -512,6 +512,7 @@
"pushover_credentials_missing": "Pushover Token und/oder Key fehlen", "pushover_credentials_missing": "Pushover Token und/oder Key fehlen",
"pushover_key": "Pushover Key hat das falsche Format", "pushover_key": "Pushover Key hat das falsche Format",
"pushover_token": "Pushover Token hat das falsche Format", "pushover_token": "Pushover Token hat das falsche Format",
"quarantine_category_invalid": "Quarantäne-Kategorie muss eine der folgenden sein: add_header, reject, all",
"quota_not_0_not_numeric": "Speicherplatz muss numerisch und >= 0 sein", "quota_not_0_not_numeric": "Speicherplatz muss numerisch und >= 0 sein",
"recipient_map_entry_exists": "Eine Empfängerumschreibung für Objekt \"%s\" existiert bereits", "recipient_map_entry_exists": "Eine Empfängerumschreibung für Objekt \"%s\" existiert bereits",
"recovery_email_failed": "E-Mail zur Wiederherstellung konnte nicht gesendet werden. Bitte wenden Sie sich an Ihren Administrator.", "recovery_email_failed": "E-Mail zur Wiederherstellung konnte nicht gesendet werden. Bitte wenden Sie sich an Ihren Administrator.",
+1
View File
@@ -513,6 +513,7 @@
"pushover_credentials_missing": "Pushover token and or key missing", "pushover_credentials_missing": "Pushover token and or key missing",
"pushover_key": "Pushover key has a wrong format", "pushover_key": "Pushover key has a wrong format",
"pushover_token": "Pushover token has a wrong format", "pushover_token": "Pushover token has a wrong format",
"quarantine_category_invalid": "Quarantine category must be one of: add_header, reject, all",
"quota_not_0_not_numeric": "Quota must be numeric and >= 0", "quota_not_0_not_numeric": "Quota must be numeric and >= 0",
"recipient_map_entry_exists": "A Recipient map entry \"%s\" exists", "recipient_map_entry_exists": "A Recipient map entry \"%s\" exists",
"recovery_email_failed": "Could not send a recovery email. Please contact your administrator.", "recovery_email_failed": "Could not send a recovery email. Please contact your administrator.",
+18 -2
View File
@@ -1144,7 +1144,8 @@
"subscribeall": "Feliratkozás minden mappára", "subscribeall": "Feliratkozás minden mappára",
"syncjob": "Szinkronizálási feladat hozzáadása", "syncjob": "Szinkronizálási feladat hozzáadása",
"internal": "Belső", "internal": "Belső",
"internal_info": "Belső álnevek csak a saját domain vagy domain álnév számára elérhető." "internal_info": "Belső álnevek csak a saját domain vagy domain álnév számára elérhető.",
"sender_allowed": "Küldés engedélyezése ezzel az aliasszal"
}, },
"danger": { "danger": {
"access_denied": "Hozzáférés megtagatva vagy nem megfelelő űrlap adat", "access_denied": "Hozzáférés megtagatva vagy nem megfelelő űrlap adat",
@@ -1245,6 +1246,21 @@
"pushover_key": "A pushover kulcs rossz formátumú", "pushover_key": "A pushover kulcs rossz formátumú",
"pushover_token": "A Pushover token rossz formátumú", "pushover_token": "A Pushover token rossz formátumú",
"quota_not_0_not_numeric": "A kvótának numerikusnak és >= 0-nak kell lennie.", "quota_not_0_not_numeric": "A kvótának numerikusnak és >= 0-nak kell lennie.",
"recipient_map_entry_exists": "Létezik egy \"%s\" címzett-térkép bejegyzés" "recipient_map_entry_exists": "Létezik egy \"%s\" címzett-térkép bejegyzés",
"redis_error": "Redis hiba lépett fel: %s",
"relayhost_invalid": "A(z) %s elem érvénytelen a leképezésben.",
"release_send_failed": "Az üzenet felszabadítása sikertelen: %s",
"reset_f2b_regex": "A regex-szűrő időtúllépés miatt nem állt le. Próbálja újra, vagy várjon egy kicsit, és töltse újra az oldalt.",
"resource_invalid": "A(z) %s erőforrásnév érvénytelen",
"rl_timeframe": "Érvénytelen időkeret a lekérdezési korláthoz",
"rspamd_ui_pw_length": "A Rspamd UI jelszónak legalább 6 karakter hosszúnak kell lennie.",
"script_empty": "A szkript nem lehet üres",
"sender_acl_invalid": "A küldőhöz tartozó ACL-érték (%s) érvénytelen",
"set_acl_failed": "Az ACL beállítása meghiúsult",
"settings_map_invalid": "Érvénytelen beállítás-leképezési azonosító: %s",
"recovery_email_failed": "A helyreállítási email kiküldése sikertelen. Kérlek, lépj kapcsolatba az adminisztrátorral!",
"reset_token_limit_exceeded": "Túl sok visszaállítási kísérlet. Kérjük, várjon, mielőtt újra próbálkozna.",
"required_data_missing": "Hiányzik a(z) szükséges %s adat",
"tfa_removal_blocked": "A kétfaktoros hitelesítés nem távolítható el, mert elengedhetetlen a fiókod használatához."
} }
} }
+212 -21
View File
@@ -26,7 +26,8 @@
"syncjobs": "Sync jobs", "syncjobs": "Sync jobs",
"tls_policy": "Versleutelingsbeleid", "tls_policy": "Versleutelingsbeleid",
"unlimited_quota": "Onbeperkte quota voor mailboxen", "unlimited_quota": "Onbeperkte quota voor mailboxen",
"domain_desc": "Wijzig domeinbeschrijving" "domain_desc": "Wijzig domeinbeschrijving",
"pw_reset": "Toegang om mailcow gebruikers wachtwoord te resetten"
}, },
"add": { "add": {
"activate_filter_warn": "Alle andere filters worden gedeactiveerd zolang deze geactiveerd is.", "activate_filter_warn": "Alle andere filters worden gedeactiveerd zolang deze geactiveerd is.",
@@ -104,7 +105,11 @@
"validate": "Verifieer", "validate": "Verifieer",
"validation_success": "Succesvol geverifieerd", "validation_success": "Succesvol geverifieerd",
"tags": "Tags", "tags": "Tags",
"bcc_dest_format": "BCC-bestemming moet één geldig e-mailadres zijn.<br>Als u een kopie naar meerdere adressen wilt sturen, maak dan een alias aan en gebruik die hier." "bcc_dest_format": "BCC-bestemming moet één geldig e-mailadres zijn.<br>Als u een kopie naar meerdere adressen wilt sturen, maak dan een alias aan en gebruik die hier.",
"dry": "Synchronisatie simuleren",
"internal": "Intern",
"internal_info": "Interne aliassen zijn alleen toegankelijk vanuit het eigen (alias)domein.",
"sender_allowed": "Toestaan om te verzenden als deze alias"
}, },
"admin": { "admin": {
"access": "Toegang", "access": "Toegang",
@@ -138,7 +143,7 @@
"arrival_time": "Aankomsttijd", "arrival_time": "Aankomsttijd",
"authed_user": "Geauthenticeerde gebruiker", "authed_user": "Geauthenticeerde gebruiker",
"ays": "Weet je zeker dat je deze actie wilt uitvoeren?", "ays": "Weet je zeker dat je deze actie wilt uitvoeren?",
"ban_list_info": "Bekijk de lijst met verbannen IP-adressen hieronder: <b>netwerk (resterende tijd) - [acties]</b>.<br />Rode labels geven een permanente verbanning aan.<br />Het kan enkele seconden duren voordat wijzigingen hieronder zichtbaar zijn.", "ban_list_info": "Bekijk de lijst met verbannen IP-adressen hieronder: <b>netwerk (resterende tijd) - [acties]</b>.<br />IP-adressen die in de wachtrij staan om te worden gedeblokkeerd, worden binnen enkele seconden uit de actieve banlijst verwijderd.<br />\n<br />Rode labels geven actieve permanente blokkades door denylisting aan.",
"change_logo": "Logo", "change_logo": "Logo",
"configuration": "Instellingen", "configuration": "Instellingen",
"convert_html_to_text": "Converteer HTML naar plaintext", "convert_html_to_text": "Converteer HTML naar plaintext",
@@ -171,7 +176,7 @@
"excludes": "Exclusief", "excludes": "Exclusief",
"f2b_ban_time": "Verbanningstijd (s)", "f2b_ban_time": "Verbanningstijd (s)",
"f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning", "f2b_ban_time_increment": "Verbanningstijd wordt verhoogd met elk verbanning",
"f2b_blacklist": "Netwerken/hosts op de blacklist", "f2b_blacklist": "Netwerken/hosts op de denylist",
"f2b_filter": "Regex-filters", "f2b_filter": "Regex-filters",
"f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>", "f2b_list_info": "Een host of netwerk op de blacklist staat altijd boven eenzelfde op de whitelist. <b>Het doorvoeren van wijzigingen kan enkele seconden in beslag nemen.</b>",
"f2b_max_attempts": "Maximaal aantal pogingen", "f2b_max_attempts": "Maximaal aantal pogingen",
@@ -206,7 +211,7 @@
"link": "Link", "link": "Link",
"loading": "Even geduld aub...", "loading": "Even geduld aub...",
"logo_info": "De afbeelding zal worden geschaald naar een hoogte van 40px voor de navigatiebar, en naar een breedte van 250px voor de startpagina.", "logo_info": "De afbeelding zal worden geschaald naar een hoogte van 40px voor de navigatiebar, en naar een breedte van 250px voor de startpagina.",
"lookup_mx": "Match bestemming aan MX (gebruik .outlook.com om alle mail gericht aan MX *.outlook.com over deze hop te laten gaan)", "lookup_mx": "Bestemming is een reguliere expressie die wordt gematcht met de MX-naam (<code>.*\\.google\\.com</code> om alle e-mail die gericht is aan een MX die eindigt op google.com via deze hop te routeren)",
"main_name": "\"Mailcow\"", "main_name": "\"Mailcow\"",
"merged_vars_hint": "Grijze rijen zijn samengevoegd van <code>vars.(local.)inc.php</code> en kunnen niet worden gewijzigd.", "merged_vars_hint": "Grijze rijen zijn samengevoegd van <code>vars.(local.)inc.php</code> en kunnen niet worden gewijzigd.",
"message": "Bericht", "message": "Bericht",
@@ -275,7 +280,7 @@
"rspamd_com_settings": "Een beschrijving voor deze instelling zal automatisch worden gegenereerd, gebruik de onderstaande presets als voorbeeld. Raadpleeg de <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd-documentatie</a> voor meer informatie.", "rspamd_com_settings": "Een beschrijving voor deze instelling zal automatisch worden gegenereerd, gebruik de onderstaande presets als voorbeeld. Raadpleeg de <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">Rspamd-documentatie</a> voor meer informatie.",
"rspamd_global_filters": "Globale filters", "rspamd_global_filters": "Globale filters",
"rspamd_global_filters_agree": "Ik ben me ervan bewust dat aanpassingen desastreuze gevolgen kunnen hebben", "rspamd_global_filters_agree": "Ik ben me ervan bewust dat aanpassingen desastreuze gevolgen kunnen hebben",
"rspamd_global_filters_info": "Ieder globaal filter heeft zijn eigen functie, zie de namen.", "rspamd_global_filters_info": "Globale filtermaps bevatten verschillende soorten globale deny- en allowlists.",
"rspamd_global_filters_regex": "De velden kunnen uitsluitend regular expressions bevatten met het formaat \"/pattern/options\", bijvoorbeeld <code>/.+@domain\\.tld/i</code>.<br>Ondanks dat alle invoer wordt gecontroleerd op fouten, is het toch mogelijk dat Rspamd onbruikbaar wordt als deze de invoer niet kan lezen.<br>Als je problemen ervaart, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">herstart Rspamd</a> dan om de filters opnieuw te laten lezen.<br>Elementen op de blacklist zijn uitgesloten van de quarantaine.", "rspamd_global_filters_regex": "De velden kunnen uitsluitend regular expressions bevatten met het formaat \"/pattern/options\", bijvoorbeeld <code>/.+@domain\\.tld/i</code>.<br>Ondanks dat alle invoer wordt gecontroleerd op fouten, is het toch mogelijk dat Rspamd onbruikbaar wordt als deze de invoer niet kan lezen.<br>Als je problemen ervaart, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">herstart Rspamd</a> dan om de filters opnieuw te laten lezen.<br>Elementen op de blacklist zijn uitgesloten van de quarantaine.",
"rspamd_settings_map": "Rspamd", "rspamd_settings_map": "Rspamd",
"sal_level": "Moo-level", "sal_level": "Moo-level",
@@ -337,7 +342,71 @@
"admins_ldap": "LDAP administrators", "admins_ldap": "LDAP administrators",
"api_read_only": "Alleen-lezen toegang", "api_read_only": "Alleen-lezen toegang",
"api_read_write": "Lees en schrijf toegang", "api_read_write": "Lees en schrijf toegang",
"login_time": "Login tijd" "login_time": "Login tijd",
"admin_quicklink": "Snelle link naar de admin-loginpagina verbergen",
"allowed_methods": "Toegestane toegangsmethoden",
"app_hide": "Verberg voor login",
"copy_to_clipboard": "Tekst gekopieerd naar het klembord!",
"login_page": "Loginpagina",
"domainadmin_quicklink": "Snelle link naar de domeinbeheerder-loginpagina verbergen",
"f2b_manage_external": "Fail2Ban extern beheren",
"f2b_manage_external_info": "Fail2Ban zal nog steeds de banlijst bijhouden, maar zal niet actief regels instellen om verkeer te blokkeren. Gebruik de onderstaande gegenereerde banlijst om het verkeer extern te blokkeren.",
"filter": "Filter",
"force_sso_text": "Als een externe OIDC-provider is geconfigureerd, verbergt deze optie de standaard Mailcow-loginformulieren en wordt alleen de Single SignOn-knop weergegeven.",
"force_sso": "Mailcow-login uitschakelen en alleen Single SignOn tonen",
"iam": "Identiteitsprovider",
"iam_attribute_field": "Attribuutveld",
"iam_authorize_url": "Autorisatie endpoint",
"iam_auth_flow": "Autorisatie flow",
"iam_auth_flow_info": "Naast de Authorization Code Flow (Standard Flow in Keycloak), die wordt gebruikt voor Single SignOn-login, ondersteunt Mailcow ook een authenticatiestroom met directe inloggegevens. De Mailpassword Flow probeert de gebruikersgegevens te valideren door gebruik te maken van de Keycloak Admin REST API. Mailcow haalt het gehashte wachtwoord op uit het attribuut <code>mailcow_password</code>, dat in Keycloak is gekoppeld.",
"iam_client_id": "Client ID",
"iam_client_secret": "Client Secret",
"iam_client_scopes": "Clientscopes",
"iam_default_template": "Standaardsjabloon",
"iam_default_template_description": "Als er geen sjabloon aan een gebruiker is toegewezen, wordt het standaardsjabloon gebruikt voor het aanmaken van de mailbox, maar niet voor het bijwerken van de mailbox.",
"iam_description": "Configureer een externe provider voor authenticatie. De mailboxen van gebruikers worden automatisch aangemaakt bij hun eerste login, mits er een attribuutkoppeling is ingesteld.",
"iam_extra_permission": "Om de volgende instellingen te laten werken, heeft de mailcow-client in Keycloak een <code>service account</code> nodig en de toestemming <code>view-users</code>.",
"iam_host": "Hostname",
"iam_host_info": "Voer één of meerdere LDAP-hosts in, gescheiden door kommas.",
"iam_import_users": "Importeer gebruikers",
"iam_login_provisioning": "Gebruikers automatisch aanmaken bij het inloggen",
"iam_mapping": "Attribuutkoppeling",
"iam_bindpass": "Bind-wachtwoord",
"iam_periodic_full_sync": "Periodieke volledige synchronisatie",
"iam_port": "Poort",
"iam_realm": "Realm",
"iam_redirect_url": "Redirect-URL",
"iam_rest_flow": "Mailwachtwoord-flow",
"iam_server_url": "Server Url",
"iam_sso": "Single SignOn",
"iam_sync_interval": "Synchronisatie-/importinterval (min)",
"iam_test_connection": "Verbinding testen",
"iam_token_url": "Token endpoint",
"iam_userinfo_url": "Gebruikersinfo endpoint",
"iam_username_field": "Gebruikersnaam veld",
"iam_use_ssl": "Gebruik SSL",
"iam_use_ssl_info": "Als SSL wordt ingeschakeld en de poort is ingesteld op 389, wordt deze automatisch aangepast naar 636.",
"iam_use_tls": "Gebruik StartTLS",
"iam_use_tls_info": "Als TLS wordt ingeschakeld, moet je de standaardpoort voor je LDAP-server (389) gebruiken. SSL-poorten kunnen niet worden gebruikt.",
"iam_version": "Versie",
"ignore_ssl_error": "Negeer SSL foutmeldingen",
"ip_check_opt_in": "Kies ervoor om de externe diensten <strong>ipv4.mailcow.email</strong> en <strong>ipv6.mailcow.email</strong> te gebruiken om externe IP-adressen te bepalen.",
"needs_restart": "vereist een herstart",
"no": "&#10005;",
"password_reset_info": "Als er geen herstel-e-mailadres is opgegeven, kan deze functie niet worden gebruikt.",
"password_reset_settings": "Instellingen voor wachtwoordherstel",
"password_reset_tmpl_html": "HTML sjabloon",
"password_reset_tmpl_text": "Tekst sjabloon",
"password_settings": "Wachtwoordinstellingen",
"quicklink_text": "Toon of verberg snelle links naar andere inlogpaginas onder het inlogformulier",
"queue_unban": "deblokkeren",
"reset_password_vars": "<code>{{link}}</code> De gegenereerde link om het wachtwoord te resetten<br><code>{{username}}</code> De mailboxnaam van de gebruiker die het wachtwoordherstel heeft aangevraagd<br><code>{{username2}}</code> De naam van de herstelmailbox<br><code>{{date}}</code> De datum waarop het verzoek tot wachtwoordherstel is gedaan<br><code>{{token_lifetime}}</code> De geldigheidsduur van de token in minuten<br><code>{{hostname}}</code> De mailcow-hostnaam",
"restore_template": "Leeg laten om het standaardsjabloon te herstellen.",
"task": "Taak",
"transport_test_rcpt_info": "&#8226; Gebruik null@hosted.mailcow.de om relaying naar een externe bestemming te testen.",
"user_link": "Gebruikerslink",
"user_quicklink": "Snelle link naar de gebruikers-loginpagina verbergen",
"yes": "&#10003;"
}, },
"danger": { "danger": {
"access_denied": "Toegang geweigerd of ongeldige gegevens", "access_denied": "Toegang geweigerd of ongeldige gegevens",
@@ -463,7 +532,27 @@
"demo_mode_enabled": "Demo modus is ingeschakeld", "demo_mode_enabled": "Demo modus is ingeschakeld",
"template_exists": "Sjabloon %s bestaat al", "template_exists": "Sjabloon %s bestaat al",
"template_id_invalid": "Sjabloon ID %s ongeldig", "template_id_invalid": "Sjabloon ID %s ongeldig",
"template_name_invalid": "Sjabloon naam ongeldig" "template_name_invalid": "Sjabloon naam ongeldig",
"tfa_removal_blocked": "Twee-factor-authenticatie kan niet worden verwijderd omdat dit vereist is voor uw account.",
"authsource_in_use": "De identiteitsprovider kan niet worden gewijzigd of verwijderd omdat deze momenteel door één of meerdere gebruikers wordt gebruikt.",
"cors_invalid_origin": "Ongeldige Allow-Origin opgegeven",
"extended_sender_acl_denied": "Ontbrekende ACL om externe afzenderadressen in te stellen",
"generic_server_error": "Er is een onverwachte serverfout opgetreden. Neem contact op met uw beheerder.",
"iam_test_connection": "Verbinding mislukt",
"img_dimensions_exceeded": "De afbeelding overschrijdt de maximale afbeeldingsgrootte",
"img_size_exceeded": "De afbeelding overschrijdt de maximale afbeeldingsgrootte",
"invalid_reset_token": "Ongeldige reset-token",
"max_age_invalid": "Maximale leeftijd %s is ongeldig",
"mode_invalid": "Mode %s is ongeldig",
"mx_invalid": "MX-record %s is ongeldig",
"password_reset_invalid_user": "Mailbox niet gevonden of er is geen herstel-e-mailadres ingesteld",
"password_reset_na": "Wachtwoordherstel is momenteel niet beschikbaar. Neem contact op met uw beheerder.",
"recovery_email_failed": "Kon geen herstel-e-mail verzenden. Neem contact op met uw beheerder.",
"required_data_missing": "Vereiste gegevens %s ontbreken",
"reset_token_limit_exceeded": "De limiet voor reset-tokens is overschreden. Probeer het later opnieuw.",
"to_invalid": "Ontvanger mag niet leeg zijn",
"webauthn_username_failed": "De geselecteerde authenticator behoort tot een ander account",
"version_invalid": "Versie %s is ongeldig"
}, },
"debug": { "debug": {
"chart_this_server": "Grafiek (deze server)", "chart_this_server": "Grafiek (deze server)",
@@ -520,7 +609,7 @@
"alias": "Wijzig alias", "alias": "Wijzig alias",
"allow_from_smtp": "Sta enkel de volgende IP-adressen toe voor <b>SMTP</b>", "allow_from_smtp": "Sta enkel de volgende IP-adressen toe voor <b>SMTP</b>",
"allow_from_smtp_info": "Laat leeg om alle afzenders toe te staan.<br>IPv4/IPv6-adressen en netwerken.", "allow_from_smtp_info": "Laat leeg om alle afzenders toe te staan.<br>IPv4/IPv6-adressen en netwerken.",
"allowed_protocols": "Toegestane protocollen", "allowed_protocols": "Toegestane protocollen voor directe gebruikerstoegang (heeft geen invloed op protocollen voor app-wachtwoorden).",
"app_name": "Naam van app", "app_name": "Naam van app",
"app_passwd": "Appwachtwoord", "app_passwd": "Appwachtwoord",
"automap": "Probeer mappen automatisch te koppelen (\"Verzonden items\", \"Verzonden\" => \"Verzonden\" etc.)", "automap": "Probeer mappen automatisch te koppelen (\"Verzonden items\", \"Verzonden\" => \"Verzonden\" etc.)",
@@ -605,7 +694,7 @@
"sogo_visible_info": "Wanneer verborgen zal een alias niet worden weergegeven als een selecteerbaar verzendadres. Deze optie beïnvloedt uitsluitend objecten die kunnen worden weergegeven in SOGo (gedeelde of niet-gedeelde aliasadressen die naar minstens één mailbox verwijzen).", "sogo_visible_info": "Wanneer verborgen zal een alias niet worden weergegeven als een selecteerbaar verzendadres. Deze optie beïnvloedt uitsluitend objecten die kunnen worden weergegeven in SOGo (gedeelde of niet-gedeelde aliasadressen die naar minstens één mailbox verwijzen).",
"spam_alias": "Maak een nieuw tijdelijk alias aan, of pas deze aan", "spam_alias": "Maak een nieuw tijdelijk alias aan, of pas deze aan",
"spam_filter": "Spamfilter", "spam_filter": "Spamfilter",
"spam_policy": "Voeg items toe of verwijder items van de white- of blacklist", "spam_policy": "Voeg items toe of verwijder items van de deny- en allowlists",
"spam_score": "Stel een aangepaste spamscore in", "spam_score": "Stel een aangepaste spamscore in",
"subfolder2": "Synchroniseer in submap op bestemming<br><small>(leeg = gebruik geen submappen)</small>", "subfolder2": "Synchroniseer in submap op bestemming<br><small>(leeg = gebruik geen submappen)</small>",
"syncjob": "Wijzig sync job", "syncjob": "Wijzig sync job",
@@ -624,7 +713,47 @@
"acl": "ACL (Toestemming)", "acl": "ACL (Toestemming)",
"domain_footer": "Domeinbreede footer", "domain_footer": "Domeinbreede footer",
"domain_footer_html": "HTML footer", "domain_footer_html": "HTML footer",
"mailbox_relayhost_info": "Wordt alleen toegepast op de mailbox en directe aliassen, maar heft een domein relayhost op." "mailbox_relayhost_info": "Wordt alleen toegepast op de mailbox en directe aliassen, maar heft een domein relayhost op.",
"custom_attributes": "Aangepaste attributen",
"domain_footer_info_vars": {
"auth_user": "{= auth_user =} - Geauthenticeerde gebruikersnaam opgegeven door een MTA",
"from_user": "{= from_user =} - Afzendergedeelte van het e-mailadres, bijvoorbeeld bij \"moo@mailcow.tld\" wordt \"moo\" teruggegeven",
"from_name": "{= from_name =} - Naam van de afzender, bijvoorbeeld bij \"Mailcow <moo@mailcow.tld>\" wordt \"Mailcow\" teruggegeven",
"from_addr": "{= from_addr =} - Adres van de afzender",
"from_domain": "{= from_domain =} - Domein van de afzender",
"custom": "{= foo =} - Als de mailbox het aangepaste attribuut \"foo\" met de waarde \"bar\" heeft, wordt \"bar\" teruggegeven."
},
"domain_footer_plain": "PLAIN-voettekst",
"domain_footer_skip_replies": "Voettekst negeren bij antwoord-e-mails",
"footer_exclude": "Uitsluiten van voettekst",
"internal": "Intern",
"internal_info": "Interne aliassen zijn alleen toegankelijk vanuit het eigen (alias)domein.",
"sender_allowed": "Toestaan om als deze alias te verzenden",
"sender_allowed_info": "Als dit is uitgeschakeld, kan deze alias alleen e-mail ontvangen. Gebruik de afzender-ACL om dit te overschrijven en specifieke mailboxen toestemming te geven om te verzenden.",
"lookup_mx": "Bestemming is een reguliere expressie die wordt gematcht met de MX-naam (<code>.*.google.com</code> om alle e-mail die gericht is aan een MX die eindigt op google.com via deze hop te routeren)",
"mailbox_rename": "Hernoem mailbox",
"mailbox_rename_agree": "Ik heb een backup gemaakt.",
"mailbox_rename_warning": "BELANGRIJK! Maak een back-up voordat u de mailbox hernoemt.",
"mailbox_rename_alias": "Alias automatisch aanmaken.",
"mailbox_rename_title": "Nieuwe naam van de lokale mailbox",
"mta_sts": "MTA-STS",
"mta_sts_info": "<a href='https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#SMTP_MTA_Strict_Transport_Security' target='_blank'>MTA-STS</a> is een standaard die afdwingt dat e-mailbezorging tussen mailservers TLS met geldige certificaten gebruikt. <br>\nDeze wordt gebruikt wanneer <a target='_blank' href='https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities'>DANE</a> niet mogelijk is vanwege ontbrekende of niet-ondersteunde DNSSEC.<br>\n<b>Opmerking</b>: Als het ontvangende domein DANE met DNSSEC ondersteunt, heeft DANE <b>altijd</b> de voorkeur MTA-STS fungeert alleen als een fallback.",
"mta_sts_version": "Versie",
"mta_sts_version_info": "Definieert de versie van de MTA-STS-standaard momenteel is alleen <code>STSv1</code> geldig.",
"mta_sts_mode": "Modus",
"mta_sts_mode_info": "Er zijn drie modus om uit te kiezen:<ul><li><em>testing</em> het beleid wordt alleen gemonitord, overtredingen hebben geen impact.</li><li><em>enforce</em> het beleid wordt strikt afgedwongen, verbindingen zonder geldige TLS worden geweigerd.</li><li><em>none</em> het beleid wordt gepubliceerd maar niet toegepast.</li></ul>",
"mta_sts_max_age": "Maximale leeftijd",
"mta_sts_max_age_info": "Tijd in seconden dat ontvangende mailservers dit beleid mogen cachen voordat het opnieuw wordt opgehaald.",
"mta_sts_mx": "MX server",
"mta_sts_mx_info": "Staat verzending alleen toe naar expliciet vermelde mailserver-hostnamen; de verzendende MTA controleert of de DNS MX-hostnaam overeenkomt met de beleidslijst en staat levering alleen toe met een geldig TLS-certificaat (beschermt tegen MITM).",
"mta_sts_mx_notice": "Meerdere MX-servers kunnen worden opgegeven (gescheiden door kommas).",
"none_inherit": "Geen / Erven",
"password_recovery_email": "E-mail voor wachtwoordherstel",
"pushover": "Pushover",
"quota_warning_bcc": "Quotum waarschuwing BCC",
"quota_warning_bcc_info": "Waarschuwingen zullen in afzonderlijke exemplaren naar de volgende ontvangers worden gestuurd. Het onderwerp zal worden gevolgd door de bijbehorende gebruikersnaam tussen haakjes, bijvoorbeeld: <code>Contingentwaarschuwing (user@example.com)</code>.",
"sogo_access": "Verleen directe logintoegang tot SOGo",
"sogo_access_info": "Na het inloggen wordt de gebruiker automatisch doorgestuurd naar SOGo"
}, },
"footer": { "footer": {
"cancel": "Annuleren", "cancel": "Annuleren",
@@ -637,18 +766,21 @@
"restart_container": "Herstart container", "restart_container": "Herstart container",
"restart_container_info": "<b>Belangrijk:</b> Een herstart kan enige tijd in beslag nemen, wacht aub totdat dit proces voltooid is.<br>Deze pagina zal zichzelf verversen zodra het proces voltooid is.", "restart_container_info": "<b>Belangrijk:</b> Een herstart kan enige tijd in beslag nemen, wacht aub totdat dit proces voltooid is.<br>Deze pagina zal zichzelf verversen zodra het proces voltooid is.",
"restart_now": "Nu herstarten", "restart_now": "Nu herstarten",
"restarting_container": "Container wordt herstart, even geduld aub..." "restarting_container": "Container wordt herstart, even geduld aub...",
"hibp_check": "Controleer tegen haveibeenpwned.com",
"nothing_selected": "Niets geselecteerd"
}, },
"header": { "header": {
"administration": "Configuratie & details", "administration": "Configuratie & details",
"apps": "Apps", "apps": "Apps",
"debug": "Systeeminformatie", "debug": "Informatie",
"email": "E-Mail", "email": "E-Mail",
"mailcow_config": "Beheer", "mailcow_config": "Beheer",
"quarantine": "Quarantaine", "quarantine": "Quarantaine",
"restart_netfilter": "Herstart netfilter", "restart_netfilter": "Herstart netfilter",
"restart_sogo": "Herstart SOGo", "restart_sogo": "Herstart SOGo",
"user_settings": "Gebruikersinstellingen" "user_settings": "Gebruikersinstellingen",
"mailcow_system": "Systeem"
}, },
"info": { "info": {
"awaiting_tfa_confirmation": "In afwachting van tweefactorauthenticatie...", "awaiting_tfa_confirmation": "In afwachting van tweefactorauthenticatie...",
@@ -662,7 +794,22 @@
"mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.", "mobileconfig_info": "Log in als mailboxgebruiker om het Apple-verbindingsprofiel te downloaden.",
"other_logins": "of aanmelden met", "other_logins": "of aanmelden met",
"password": "Wachtwoord", "password": "Wachtwoord",
"username": "Gebruikersnaam" "username": "Gebruikersnaam",
"back_to_mailcow": "Terug naar mailcow",
"forgot_password": "> Wachtwoord vergeten?",
"invalid_pass_reset_token": "De reset-wachtwoordtoken is ongeldig of verlopen.<br>Vraag een nieuwe link voor het resetten van het wachtwoord aan.",
"login_linkstext": "Niet de juiste login?",
"login_usertext": "Log in als gebruiker",
"login_domainadmintext": "Login in als domeinbeheerder",
"login_admintext": "Log in als administrator",
"login_user": "Gebruikerslogin",
"login_dadmin": "Domeinbeheerder login",
"login_admin": "Administrator login",
"new_password": "Nieuw wachtwoord",
"new_password_confirm": "Bevestig nieuw wachtwoord",
"reset_password": "Herstel wachtwoord",
"request_reset_password": "Verzoek wachtwoord wijziging",
"email": "E-mailadres"
}, },
"mailbox": { "mailbox": {
"action": "Handeling", "action": "Handeling",
@@ -782,7 +929,7 @@
"sieve_preset_5": "Autoreply (vakantie)", "sieve_preset_5": "Autoreply (vakantie)",
"sieve_preset_6": "Weiger mail met antwoord", "sieve_preset_6": "Weiger mail met antwoord",
"sieve_preset_7": "Forward en behoud/verwijder", "sieve_preset_7": "Forward en behoud/verwijder",
"sieve_preset_8": "Verwijder mail verstuurd naar een aliasadres van de afzender", "sieve_preset_8": "E-mail van een specifieke afzender doorsturen, als gelezen markeren en in een submap plaatsen",
"sieve_preset_header": "Zie de onderstaande voorbeelden. Raadpleeg <a href=\"https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)\" target=\"_blank\">Wikipedia</a> voor meer informatie.", "sieve_preset_header": "Zie de onderstaande voorbeelden. Raadpleeg <a href=\"https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)\" target=\"_blank\">Wikipedia</a> voor meer informatie.",
"sogo_visible": "Alias tonen in SOGo", "sogo_visible": "Alias tonen in SOGo",
"sogo_visible_n": "Verberg alias in SOGo", "sogo_visible_n": "Verberg alias in SOGo",
@@ -809,7 +956,37 @@
"toggle_all": "Selecteer alles", "toggle_all": "Selecteer alles",
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
"waiting": "Wachten", "waiting": "Wachten",
"weekly": "Wekelijks" "weekly": "Wekelijks",
"add_alias_expand": "Alias uitbreiden over alias-domeinen",
"add_template": "Sjabloon toevoegen",
"all_domains": "Alle domeinen",
"catch_all": "Catch-All",
"created_on": "Aangemaakt op",
"domain_templates": "Domein sjablonen",
"domain_quota_total": "Totale domein-opslagruimte",
"goto_ham": "Leren als <b>Ham</b>",
"goto_spam": "Leren als <b>spam</b>",
"iam": "Identiteitsprovider",
"internal": "Intern",
"last_pw_change": "Laatste wachtwoordwijziging",
"mailbox_templates": "Mailbox sjablonen",
"no": "&#10005;",
"open_logs": "Open logs",
"recipient": "Ontvanger",
"relay_unknown": "Onbekende mailboxen relayen",
"sender": "Afzender",
"syncjob_check_log": "Controleer log",
"syncjob_last_run_result": "Laatste uitvoeringsresultaat",
"syncjob_EX_OK": "Succes",
"syncjob_EXIT_CONNECTION_FAILURE": "Verbindingsprobleem",
"syncjob_EXIT_TLS_FAILURE": "Probleem met versleutelde verbinding",
"syncjob_EXIT_AUTHENTICATION_FAILURE": "Authenticatieprobleem",
"syncjob_EXIT_OVERQUOTA": "Doel mailbox is over quota",
"syncjob_EXIT_CONNECTION_FAILURE_HOST1": "Kan geen verbinding maken met de externe server",
"syncjob_EXIT_AUTHENTICATION_FAILURE_USER1": "Verkeerde gebruikersnaam of wachtwoord",
"templates": "Sjablonen",
"template": "Sjabloon",
"yes": "&#10003;"
}, },
"oauth2": { "oauth2": {
"access_denied": "Log in als een mailboxgebruiker om toegang via OAuth te verlenen", "access_denied": "Log in als een mailboxgebruiker om toegang via OAuth te verlenen",
@@ -842,7 +1019,7 @@
"notified": "Verwittigd", "notified": "Verwittigd",
"qhandler_success": "Aanvraag succesvol verzonden naar het systeem. Je kunt nu het venster sluiten.", "qhandler_success": "Aanvraag succesvol verzonden naar het systeem. Je kunt nu het venster sluiten.",
"qid": "Rspamd QID", "qid": "Rspamd QID",
"qinfo": "Het quarantainesysteem slaat een kopie van zowel geweigerde mail (voor de afzender zal het lijken alsof de mail <em>niet</em> afgeleverd is), als mail die afgeleverd is de spamfolder, op in de database.\r\n <br>\"Markeer als spam en verwijder\" traint het systeem om soortgelijke mails in de toekomst opnieuw als spam te markeren.\r\n <br>Wanneer er meerdere berichten tegelijkertijd worden behandeld kan het mogelijk enige tijd duren.<br>Elementen op de blacklist zijn uitgesloten van de quarantaine.", "qinfo": "Het quarantainesysteem slaat een kopie van zowel geweigerde mail (voor de afzender zal het lijken alsof de mail <em>niet</em> afgeleverd is), als mail die afgeleverd is de spamfolder, op in de database.\n <br>\"Markeer als spam en verwijder\" traint het systeem om soortgelijke mails in de toekomst opnieuw als spam te markeren.\n <br>Wanneer er meerdere berichten tegelijkertijd worden behandeld kan het mogelijk enige tijd duren.<br>Elementen op de denylist zijn uitgesloten van de quarantaine.",
"qitem": "Quarantaine-item", "qitem": "Quarantaine-item",
"quarantine": "Quarantaine", "quarantine": "Quarantaine",
"quick_actions": "Handelingen", "quick_actions": "Handelingen",
@@ -874,7 +1051,19 @@
"toggle_all": "Selecteer alles" "toggle_all": "Selecteer alles"
}, },
"queue": { "queue": {
"queue_manager": "Queue manager" "queue_manager": "Queue manager",
"delete": "Verwijder alles",
"flush": "Wachtrij leegmaken",
"info": "De mailwachtrij bevat alle e-mails die wachten op aflevering. Als een e-mail lange tijd in de mailwachtrij blijft staan, wordt deze automatisch door het systeem verwijderd.<br>De foutmelding van de betreffende e-mail geeft informatie over waarom de e-mail niet kon worden afgeleverd.",
"legend": "Functies voor acties in de mailwachtrij:",
"ays": "Bevestig dat u alle items uit de huidige wachtrij wilt verwijderen.",
"deliver_mail": "Afleveren",
"deliver_mail_legend": "Probeert de geselecteerde e-mails opnieuw te bezorgen.",
"hold_mail": "Vasthouden",
"hold_mail_legend": "Houdt de geselecteerde e-mails vast. (Voorkomt verdere afleverpogingen)",
"show_message": "Bericht weergeven",
"unban": "Wachtrij deblokkeren",
"unhold_mail": "Vrijgeven"
}, },
"start": { "start": {
"help": "Toon/verberg hulppaneel", "help": "Toon/verberg hulppaneel",
@@ -994,7 +1183,8 @@
"start_fido2_validation": "Start FIDO2-validatie", "start_fido2_validation": "Start FIDO2-validatie",
"fido2_auth": "Aanmelden met FIDO2", "fido2_auth": "Aanmelden met FIDO2",
"fido2_success": "Apparaat succesvol geregistreerd", "fido2_success": "Apparaat succesvol geregistreerd",
"fido2_validation_failed": "Validatie mislukt" "fido2_validation_failed": "Validatie mislukt",
"set_fido2_touchid": "Registreer Touch ID op Apple M1"
}, },
"user": { "user": {
"action": "Handeling", "action": "Handeling",
@@ -1161,6 +1351,7 @@
"loadingRecords": "Laden...", "loadingRecords": "Laden...",
"processing": "Wachten alstublieft..", "processing": "Wachten alstublieft..",
"search": "Zoeken:", "search": "Zoeken:",
"zeroRecords": "Geen overeenkomsten gevonden" "zeroRecords": "Geen overeenkomsten gevonden",
"infoFiltered": "(gefilterd uit _MAX_ totale vermeldingen)"
} }
} }
+36 -22
View File
@@ -111,7 +111,8 @@
"validation_success": "Validado com sucesso", "validation_success": "Validado com sucesso",
"dry": "Simular sincronização", "dry": "Simular sincronização",
"internal": "Interno", "internal": "Interno",
"internal_info": "Aliases internos são acessíveis apenas a partir do próprio domínio ou alias de domínio." "internal_info": "Aliases internos são acessíveis apenas a partir do próprio domínio ou alias de domínio.",
"sender_allowed": "Permitir enviar como este alias"
}, },
"admin": { "admin": {
"access": "Acesso", "access": "Acesso",
@@ -151,7 +152,7 @@
"arrival_time": "Hora de chegada (hora do servidor)", "arrival_time": "Hora de chegada (hora do servidor)",
"authed_user": "Usuário autoritário", "authed_user": "Usuário autoritário",
"ays": "Tem certeza de que deseja continuar?", "ays": "Tem certeza de que deseja continuar?",
"ban_list_info": "Veja uma lista de IPs banidos abaixo: <b>rede (tempo restante de banimento) - [ações]</b>. <br />Os IPs na fila para serem desbanidos serão removidos da lista de banimentos ativos em alguns segundos. <br />Rótulos vermelhos indicam proibições permanentes ativas na lista negra.", "ban_list_info": "Veja abaixo a lista de IPs banidos: <b>rede (tempo restante do banimento) - [ações]</b>.<br />IPs na fila para serem desbanidos serão removidos da lista de banimentos ativos em alguns segundos.<br />Rótulos vermelhos indicam banimentos permanentes ativos na lista de bloqueio.",
"change_logo": "Alterar logotipo", "change_logo": "Alterar logotipo",
"logo_normal_label": "Normal", "logo_normal_label": "Normal",
"logo_dark_label": "Invertido para o modo escuro", "logo_dark_label": "Invertido para o modo escuro",
@@ -188,9 +189,9 @@
"excludes": "Exclui esses destinatários", "excludes": "Exclui esses destinatários",
"f2b_ban_time": "Tempo (s) de proibição", "f2b_ban_time": "Tempo (s) de proibição",
"f2b_ban_time_increment": "O tempo de banimento é incrementado com cada banimento", "f2b_ban_time_increment": "O tempo de banimento é incrementado com cada banimento",
"f2b_blacklist": "Redes/hosts na lista negra", "f2b_blacklist": "Redes/hosts na lista de bloqueio",
"f2b_filter": "Filtros Regex", "f2b_filter": "Filtros Regex",
"f2b_list_info": "Um host ou rede na lista negra sempre superará uma entidade na lista branca. <b>As atualizações da lista levarão alguns segundos para serem aplicadas.</b>", "f2b_list_info": "Um host ou rede na lista de bloqueio sempre terá prioridade sobre uma entidade na lista de permissões. <b>As atualizações das listas levarão alguns segundos para serem aplicadas.</b>",
"f2b_manage_external": "Gerenciar Fail2Ban externamente", "f2b_manage_external": "Gerenciar Fail2Ban externamente",
"f2b_manage_external_info": "O Fail2ban ainda manterá a lista de banimentos, mas não definirá ativamente regras para bloquear o tráfego. Use a lista de banimento gerada abaixo para bloquear externamente o tráfego.", "f2b_manage_external_info": "O Fail2ban ainda manterá a lista de banimentos, mas não definirá ativamente regras para bloquear o tráfego. Use a lista de banimento gerada abaixo para bloquear externamente o tráfego.",
"f2b_max_attempts": "Máximo de tentativas", "f2b_max_attempts": "Máximo de tentativas",
@@ -200,7 +201,7 @@
"f2b_parameters": "Parâmetros do Fail2ban", "f2b_parameters": "Parâmetros do Fail2ban",
"f2b_regex_info": "Registros considerados: SoGo, Postfix, Dovecot, PHP-FPM.", "f2b_regex_info": "Registros considerados: SoGo, Postfix, Dovecot, PHP-FPM.",
"f2b_retry_window": "Repita a (s) janela (s) para o máximo de tentativas", "f2b_retry_window": "Repita a (s) janela (s) para o máximo de tentativas",
"f2b_whitelist": "Redes/hosts incluídos na lista branca", "f2b_whitelist": "Redes/hosts na lista de permissões",
"filter_table": "Tabela de filtros", "filter_table": "Tabela de filtros",
"forwarding_hosts": "Anfitriões de encaminhamento", "forwarding_hosts": "Anfitriões de encaminhamento",
"forwarding_hosts_add_hint": "Você pode especificar endereços IPv4/IPv6, redes em notação CIDR, nomes de host (que serão resolvidos para endereços IP) ou nomes de domínio (que serão resolvidos para endereços IP consultando registros SPF ou, na ausência deles, registros MX).", "forwarding_hosts_add_hint": "Você pode especificar endereços IPv4/IPv6, redes em notação CIDR, nomes de host (que serão resolvidos para endereços IP) ou nomes de domínio (que serão resolvidos para endereços IP consultando registros SPF ou, na ausência deles, registros MX).",
@@ -320,8 +321,8 @@
"rspamd_com_settings": "Um nome de configuração será gerado automaticamente, veja os exemplos de predefinições abaixo. Para obter mais detalhes, consulte a documentação <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">do Rspamd</a>", "rspamd_com_settings": "Um nome de configuração será gerado automaticamente, veja os exemplos de predefinições abaixo. Para obter mais detalhes, consulte a documentação <a href=\"https://rspamd.com/doc/configuration/settings.html#settings-structure\" target=\"_blank\">do Rspamd</a>",
"rspamd_global_filters": "Mapas de filtro globais", "rspamd_global_filters": "Mapas de filtro globais",
"rspamd_global_filters_agree": "Eu vou ter cuidado!", "rspamd_global_filters_agree": "Eu vou ter cuidado!",
"rspamd_global_filters_info": "Os mapas de filtros globais contêm diferentes tipos de listas negras e brancas globais.", "rspamd_global_filters_info": "Os mapas de filtros globais contêm diferentes tipos de listas globais de bloqueio e de permissões.",
"rspamd_global_filters_regex": "Seus nomes explicam seu propósito. <code>Todo o conteúdo deve conter uma expressão regular válida no formato /padrão/opções (por exemplo, /. + @domain\\ .tld/i</code>). <br>\r\n Embora verificações rudimentares estejam sendo executadas em cada linha de regex, a funcionalidade do Rspamd pode ser interrompida se não conseguir ler a sintaxe corretamente. <br>\r\n O Rspamd tentará ler o conteúdo do mapa quando alterado. Se você tiver problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie o Rspamd</a> para forçar o recarregamento do mapa. <br>Os elementos da lista negra são excluídos da quarentena.", "rspamd_global_filters_regex": "Os nomes explicam sua finalidade. Todo o conteúdo deve conter uma expressão regular válida no formato \"/padrão/opções\" (por exemplo, <code>/.[+@domain.tld](mailto:+@domain.tld)/i</code>).<br>\nEmbora verificações básicas sejam executadas em cada linha de regex, a funcionalidade do Rspamd pode ser comprometida se ele não conseguir ler a sintaxe corretamente.<br>\nO Rspamd tentará ler o conteúdo do mapa quando ele for alterado. Se você tiver problemas, <a href=\"\" data-toggle=\"modal\" data-container=\"rspamd-mailcow\" data-target=\"#RestartContainer\">reinicie o Rspamd</a> para forçar o recarregamento do mapa.<br>Elementos na lista de bloqueio são excluídos da quarentena.",
"rspamd_settings_map": "Mapa de configurações do Rspamd", "rspamd_settings_map": "Mapa de configurações do Rspamd",
"sal_level": "Nível de humor", "sal_level": "Nível de humor",
"save": "Salvar alterações", "save": "Salvar alterações",
@@ -556,7 +557,9 @@
"mode_invalid": "Modo %s é inválido", "mode_invalid": "Modo %s é inválido",
"mx_invalid": "Registro MX %s é inválido", "mx_invalid": "Registro MX %s é inválido",
"required_data_missing": "Dados obrigatórios %s estão ausentes", "required_data_missing": "Dados obrigatórios %s estão ausentes",
"version_invalid": "Versão %s é inválida" "version_invalid": "Versão %s é inválida",
"tfa_removal_blocked": "A autenticação de dois fatores não pode ser removida, pois é obrigatória para a sua conta.",
"quarantine_category_invalid": "A categoria da quarentena deve ser uma das seguintes: add_header, reject, all"
}, },
"datatables": { "datatables": {
"collapse_all": "Recolher tudo", "collapse_all": "Recolher tudo",
@@ -746,13 +749,13 @@
"sieve_desc": "Breve descrição", "sieve_desc": "Breve descrição",
"sieve_type": "Tipo de filtro", "sieve_type": "Tipo de filtro",
"skipcrossduplicates": "Ignore mensagens duplicadas entre pastas (primeiro a chegar, primeiro a ser servido)", "skipcrossduplicates": "Ignore mensagens duplicadas entre pastas (primeiro a chegar, primeiro a ser servido)",
"sogo_access": "Encaminhamento direto para o SOGoo", "sogo_access": "Encaminhamento direto para o SOGo",
"sogo_access_info": "Depois de fazer login, o usuário é automaticamente redirecionado para o SOGo.", "sogo_access_info": "Após o login, o usuário é automaticamente redirecionado para o SOGo.",
"sogo_visible": "O alias é visível no SoGo", "sogo_visible": "O alias é visível no SoGo",
"sogo_visible_info": "Essa opção afeta somente objetos, que podem ser exibidos no SoGo (endereços de alias compartilhados ou não compartilhados apontando para pelo menos uma mailbox local). Se estiver oculto, um alias não aparecerá como remetente selecionável no SoGo.", "sogo_visible_info": "Essa opção afeta somente objetos, que podem ser exibidos no SoGo (endereços de alias compartilhados ou não compartilhados apontando para pelo menos uma mailbox local). Se estiver oculto, um alias não aparecerá como remetente selecionável no SoGo.",
"spam_alias": "Crie ou altere endereços de alias com limite de tempo", "spam_alias": "Crie ou altere endereços de alias com limite de tempo",
"spam_filter": "Filtro de spam", "spam_filter": "Filtro de spam",
"spam_policy": "Adicionar ou remover itens da lista branca/negra", "spam_policy": "Adicionar ou remover itens da lista de permissões/bloqueio",
"spam_score": "Defina uma pontuação de spam personalizada", "spam_score": "Defina uma pontuação de spam personalizada",
"subfolder2": "Sincronizar na subpasta no destino <br><small>(vazio = não usar subpasta</small>)", "subfolder2": "Sincronizar na subpasta no destino <br><small>(vazio = não usar subpasta</small>)",
"syncjob": "Editar tarefa de sincronização", "syncjob": "Editar tarefa de sincronização",
@@ -781,7 +784,9 @@
"mta_sts_max_age_info": "Tempo em segundos que servidores de email de recepção podem armazenar esta política em cache até buscar novamente.", "mta_sts_max_age_info": "Tempo em segundos que servidores de email de recepção podem armazenar esta política em cache até buscar novamente.",
"mta_sts_mx": "Servidor MX", "mta_sts_mx": "Servidor MX",
"mta_sts_mx_info": "Permite envio apenas para nomes de host de servidor de email explicitamente listados; o MTA de envio verifica se o nome do host DNS MX corresponde à lista de políticas e permite entrega apenas com certificado TLS válido (protege contra MITM).", "mta_sts_mx_info": "Permite envio apenas para nomes de host de servidor de email explicitamente listados; o MTA de envio verifica se o nome do host DNS MX corresponde à lista de políticas e permite entrega apenas com certificado TLS válido (protege contra MITM).",
"mta_sts_mx_notice": "Múltiplos servidores MX podem ser especificados (separados por vírgulas)." "mta_sts_mx_notice": "Múltiplos servidores MX podem ser especificados (separados por vírgulas).",
"sender_allowed": "Permitir enviar como este alias",
"sender_allowed_info": "Se desativado, este alias poderá apenas receber e-mails. Use a ACL de remetente para substituir essa configuração e conceder a caixas de correio específicas permissão para enviar."
}, },
"fido2": { "fido2": {
"confirm": "Confirme", "confirm": "Confirme",
@@ -970,7 +975,7 @@
"recipient_map_new": "Novo destinatário", "recipient_map_new": "Novo destinatário",
"recipient_map_new_info": "O destino do mapa do destinatário deve ser um endereço de e-mail válido ou um nome de domínio.", "recipient_map_new_info": "O destino do mapa do destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
"recipient_map_old": "Destinatário original", "recipient_map_old": "Destinatário original",
"recipient_map_old_info": "O destino original do mapa de um destinatário deve ser um endereço de e-mail válido ou um nome de domínio.", "recipient_map_old_info": "O destino original do mapa de destinatário deve ser um endereço de e-mail válido ou um nome de domínio.",
"recipient_maps": "Mapas de destinatários", "recipient_maps": "Mapas de destinatários",
"relay_all": "Retransmita todos os destinatários", "relay_all": "Retransmita todos os destinatários",
"relay_unknown": "Retransmitir mailboxes desconhecidas", "relay_unknown": "Retransmitir mailboxes desconhecidas",
@@ -1061,7 +1066,7 @@
"notified": "Notificado", "notified": "Notificado",
"qhandler_success": "Solicitação enviada com sucesso para o sistema. Agora você pode fechar a janela.", "qhandler_success": "Solicitação enviada com sucesso para o sistema. Agora você pode fechar a janela.",
"qid": "Respand AID", "qid": "Respand AID",
"qinfo": "O sistema de quarentena salvará as mensagens rejeitadas no banco de dados (o remetente <em>não</em> terá a impressão de uma mensagem entregue), bem como as mensagens, que são entregues como cópia na pasta Lixo eletrônico de uma mailbox.\n <br>“Aprenda como spam e exclua” aprenderá uma mensagem como spam por meio do Teorema de Bayes e também calculará hashes difusos para negar mensagens semelhantes no futuro.\n <br>Esteja ciente de que aprender várias mensagens pode ser demorado, dependendo do seu sistema. <br>Os elementos da lista negra são excluídos da quarentena.", "qinfo": "O sistema de quarentena irá salvar no banco de dados os e-mails rejeitados (o remetente <em>não</em> terá a impressão de que o e-mail foi entregue), bem como os e-mails que são entregues como cópia na pasta de spam de uma mailbox.\n<br>“Aprender como spam e excluir” irá classificar a mensagem como spam por meio do Teorema de Bayes e também calcular hashes fuzzy para bloquear mensagens semelhantes no futuro.\n<br>Esteja ciente de que o aprendizado de múltiplas mensagens pode, dependendo do seu sistema, levar tempo. <br>Elementos na lista de bloqueio são excluídos da quarentena.",
"qitem": "Item de quarentena", "qitem": "Item de quarentena",
"quarantine": "Quarentena", "quarantine": "Quarentena",
"quick_actions": "Ações", "quick_actions": "Ações",
@@ -1237,7 +1242,12 @@
"webauthn": "Autenticação WebAuthn", "webauthn": "Autenticação WebAuthn",
"waiting_usb_auth": "<i>Aguardando o dispositivo USB...</i> <br><br>Toque no botão no seu dispositivo USB agora.", "waiting_usb_auth": "<i>Aguardando o dispositivo USB...</i> <br><br>Toque no botão no seu dispositivo USB agora.",
"waiting_usb_register": "<i>Aguardando o dispositivo USB...</i> <br><br>Digite sua senha acima e confirme seu registro tocando no botão no seu dispositivo USB.", "waiting_usb_register": "<i>Aguardando o dispositivo USB...</i> <br><br>Digite sua senha acima e confirme seu registro tocando no botão no seu dispositivo USB.",
"yubi_otp": "Autenticação Yubico OTP" "yubi_otp": "Autenticação Yubico OTP",
"force_tfa": "Forçar o cadastro de 2FA no login",
"force_tfa_info": "O usuário será obrigado a configurar a autenticação de dois fatores antes de acessar o painel.",
"setup_title": "Autenticação de Dois Fatores Obrigatória",
"setup_required": "Sua conta exige autenticação de dois fatores. Configure um método de 2FA para continuar.",
"cancel_setup": "Cancelar e sair"
}, },
"user": { "user": {
"action": "Ação", "action": "Ação",
@@ -1276,7 +1286,7 @@
"delete_ays": "Confirme o processo de exclusão.", "delete_ays": "Confirme o processo de exclusão.",
"direct_aliases": "Endereços de alias diretos", "direct_aliases": "Endereços de alias diretos",
"direct_aliases_desc": "Os endereços de alias diretos são afetados pelo filtro de spam e pelas configurações da política TLS.", "direct_aliases_desc": "Os endereços de alias diretos são afetados pelo filtro de spam e pelas configurações da política TLS.",
"direct_protocol_access": "Esse usuário da mailbox tem <b>acesso externo direto</b> aos seguintes protocolos e aplicativos. Essa configuração é controlada pelo administrador. As senhas de aplicativos podem ser criadas para conceder acesso a protocolos e aplicativos individuais. <br>O botão “Login no webmail” fornece login único no SoGo e está sempre disponível.", "direct_protocol_access": "Este usuário da mailbox possui <b>acesso direto e externo</b> aos seguintes protocolos e aplicações. Esta configuração é controlada pelo seu administrador. Senhas de aplicativo podem ser criadas para conceder acesso a protocolos e aplicações específicos.<br>O botão “Webmail” fornece login único (SSO) para o SOGo e está sempre disponível.",
"eas_reset": "Redefinir o cache do dispositivo ActiveSync", "eas_reset": "Redefinir o cache do dispositivo ActiveSync",
"eas_reset_help": "Em muitos casos, uma redefinição do cache do dispositivo ajudará a recuperar um perfil quebrado do ActiveSync. <br><b>Atenção:</b> Todos os elementos serão baixados novamente!", "eas_reset_help": "Em muitos casos, uma redefinição do cache do dispositivo ajudará a recuperar um perfil quebrado do ActiveSync. <br><b>Atenção:</b> Todos os elementos serão baixados novamente!",
"eas_reset_now": "Reinicie agora", "eas_reset_now": "Reinicie agora",
@@ -1352,12 +1362,12 @@
"sogo_profile_reset": "Redefinir perfil SoGo", "sogo_profile_reset": "Redefinir perfil SoGo",
"sogo_profile_reset_help": "Isso destruirá o perfil SoGo de um usuário e <b>excluirá todos os dados de contato e calendário irrecuperáveis</b>.", "sogo_profile_reset_help": "Isso destruirá o perfil SoGo de um usuário e <b>excluirá todos os dados de contato e calendário irrecuperáveis</b>.",
"sogo_profile_reset_now": "Redefina o perfil agora", "sogo_profile_reset_now": "Redefina o perfil agora",
"spam_aliases": "Aliases de e-mail temporários", "spam_aliases": "Aliases de e-mail de spam",
"spam_score_reset": "Redefinir para o padrão do servidor", "spam_score_reset": "Redefinir para o padrão do servidor",
"spamfilter": "Filtro de spam", "spamfilter": "Filtro de spam",
"spamfilter_behavior": "Avaliação", "spamfilter_behavior": "Avaliação",
"spamfilter_bl": "Lista negra", "spamfilter_bl": "Lista de bloqueio",
"spamfilter_bl_desc": "Endereços de e-mail na lista negra para <b>sempre</b> serem classificados como spam e rejeitados. E-mails rejeitados <b>não</b> serão copiados para a quarentena. Podem ser usados curingas. Um filtro é aplicado a aliases diretos (aliases com uma única caixa de correio de destino), excluindo aliases abrangentes e a própria mailbox.", "spamfilter_bl_desc": "Endereços de e-mail na lista de bloqueio para <b>sempre</b> classificar como spam e rejeitados. E-mails rejeitados <b>não</b> serão copiados para a quarentena. Coringas podem ser utilizados. O filtro é aplicado apenas a aliases diretos (aliases com uma única mailbox de destino), excluindo aliases catch-all e a própria mailbox.",
"spamfilter_default_score": "Valores padrão", "spamfilter_default_score": "Valores padrão",
"spamfilter_green": "Verde: esta mensagem não é spam", "spamfilter_green": "Verde: esta mensagem não é spam",
"spamfilter_hint": "O primeiro valor descreve a “pontuação baixa de spam”, o segundo representa a “alta pontuação de spam”.", "spamfilter_hint": "O primeiro valor descreve a “pontuação baixa de spam”, o segundo representa a “alta pontuação de spam”.",
@@ -1368,8 +1378,8 @@
"spamfilter_table_empty": "Não há dados para exibir", "spamfilter_table_empty": "Não há dados para exibir",
"spamfilter_table_remove": "remover", "spamfilter_table_remove": "remover",
"spamfilter_table_rule": "Regra", "spamfilter_table_rule": "Regra",
"spamfilter_wl": "Lista branca", "spamfilter_wl": "Lista de permissões",
"spamfilter_wl_desc": "Os endereços de e-mail incluídos na lista branca são programados para <b>nunca</b> serem classificados como spam. Podem ser usados curingas. Um filtro é aplicado a aliases diretos (aliases com uma única mailbox de destino), excluindo aliases abrangentes e a própria mailbox.", "spamfilter_wl_desc": "Endereços de e-mail na lista de permissões são configurados para <b>nunca</b> serem classificados como spam. Coringas podem ser utilizados. O filtro é aplicado apenas a aliases diretos (aliases com uma única mailbox de destino), excluindo aliases catch-all e a própria mailbox.",
"spamfilter_yellow": "Amarelo: esta mensagem pode ser spam, será marcada como spam e movida para sua pasta de lixo eletrônico", "spamfilter_yellow": "Amarelo: esta mensagem pode ser spam, será marcada como spam e movida para sua pasta de lixo eletrônico",
"status": "Status", "status": "Status",
"sync_jobs": "Trabalhos de sincronização", "sync_jobs": "Trabalhos de sincronização",
@@ -1408,7 +1418,11 @@
"authentication": "Autenticação", "authentication": "Autenticação",
"overview": "Visão geral", "overview": "Visão geral",
"protocols": "Protocolos", "protocols": "Protocolos",
"tfa_info": "A autenticação de dois fatores ajuda a proteger sua conta. Se você habilitá-la, precisará de senhas de aplicativo para fazer login em aplicativos ou serviços que não suportam autenticação de dois fatores (por exemplo, clientes de email)." "tfa_info": "A autenticação de dois fatores ajuda a proteger sua conta. Se você habilitá-la, precisará de senhas de aplicativo para fazer login em aplicativos ou serviços que não suportam autenticação de dois fatores (por exemplo, clientes de email).",
"expire_never": "Nunca expirar",
"forever": "Para sempre",
"pw_update_required": "Sua conta exige a alteração de senha. Defina uma nova senha para continuar.",
"spam_aliases_info": "Um alias de spam é um endereço de e-mail temporário que pode ser usado para proteger endereços de e-mail reais. <br>Opcionalmente, pode-se definir um tempo de expiração para que o alias seja automaticamente desativado após o período definido, descartando efetivamente endereços que tenham sido abusados ou vazados."
}, },
"warning": { "warning": {
"cannot_delete_self": "Não é possível excluir o usuário conectado", "cannot_delete_self": "Não é possível excluir o usuário conectado",
+2 -1
View File
@@ -558,7 +558,8 @@
"mode_invalid": "Način %s ni veljaven", "mode_invalid": "Način %s ni veljaven",
"mx_invalid": "Zapis MX %s je neveljaven", "mx_invalid": "Zapis MX %s je neveljaven",
"version_invalid": "Različica %s je neveljavna", "version_invalid": "Različica %s je neveljavna",
"tfa_removal_blocked": "Dvofaktorske avtentikacije ni mogoče odstraniti, ker je obvezna za vaš račun." "tfa_removal_blocked": "Dvofaktorske avtentikacije ni mogoče odstraniti, ker je obvezna za vaš račun.",
"quarantine_category_invalid": "Kategorija karantene mora biti ena od: add_header, reject, all"
}, },
"debug": { "debug": {
"containers_info": "Informacije o zabojniku", "containers_info": "Informacije o zabojniku",
+1 -1
View File
@@ -52,7 +52,7 @@ if (isset($_GET['app_password'])) {
else else
$platform = $_SERVER['HTTP_USER_AGENT']; $platform = $_SERVER['HTTP_USER_AGENT'];
$password = password_generate(); $password = htmlspecialchars(password_generate(), ENT_NOQUOTES);
$attr = array( $attr = array(
'app_name' => $platform, 'app_name' => $platform,
+3 -3
View File
@@ -11,8 +11,8 @@
<link rel="stylesheet" href="{{ css_path }}"> <link rel="stylesheet" href="{{ css_path }}">
<script> <script>
// check if darkmode is preferred by OS or set by localStorage // check if darkmode is preferred by OS or set by localStorage
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && localStorage.getItem("theme") !== "light" || if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches && localStorage.getItem("mailcow_theme") !== "light" ||
localStorage.getItem("theme") === "dark") { localStorage.getItem("mailcow_theme") === "dark") {
var head = document.getElementsByTagName('head')[0]; var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link'); var link = document.createElement('link');
link.id = 'dark-mode-theme'; link.id = 'dark-mode-theme';
@@ -193,7 +193,7 @@ $(window).scroll(function() {
}); });
// Select language and reopen active URL without POST // Select language and reopen active URL without POST
function setLang(sel) { function setLang(sel) {
$.post( '{{ uri }}', {lang: sel} ); $.post( '{{ uri|escape("js") }}', {lang: sel} );
window.location.href = window.location.pathname + window.location.search; window.location.href = window.location.pathname + window.location.search;
} }
// FIDO2 functions // FIDO2 functions
+12 -12
View File
@@ -84,7 +84,7 @@ services:
- clamd - clamd
rspamd-mailcow: rspamd-mailcow:
image: ghcr.io/mailcow/rspamd:3.14.2 image: ghcr.io/mailcow/rspamd:3.14.3-1
stop_grace_period: 30s stop_grace_period: 30s
depends_on: depends_on:
- dovecot-mailcow - dovecot-mailcow
@@ -200,7 +200,7 @@ services:
- phpfpm - phpfpm
sogo-mailcow: sogo-mailcow:
image: ghcr.io/mailcow/sogo:5.12.4-2 image: ghcr.io/mailcow/sogo:5.12.5-3
environment: environment:
- DBNAME=${DBNAME} - DBNAME=${DBNAME}
- DBUSER=${DBUSER} - DBUSER=${DBUSER}
@@ -225,12 +225,12 @@ services:
- ./data/hooks/sogo:/hooks:Z - ./data/hooks/sogo:/hooks:Z
- ./data/conf/sogo/:/etc/sogo/:z - ./data/conf/sogo/:/etc/sogo/:z
- ./data/web/inc/init_db.inc.php:/init_db.inc.php:z - ./data/web/inc/init_db.inc.php:/init_db.inc.php:z
- ./data/conf/sogo/custom-favicon.ico:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z - ./data/conf/sogo/custom-favicon.ico:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo.ico:z
- ./data/conf/sogo/custom-shortlogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-compact.svg:z - ./data/conf/sogo/custom-shortlogo.svg:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo-compact.svg:z
- ./data/conf/sogo/custom-fulllogo.svg:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg:z - ./data/conf/sogo/custom-fulllogo.svg:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg:z
- ./data/conf/sogo/custom-fulllogo.png:/usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-logo.png:z - ./data/conf/sogo/custom-fulllogo.png:/usr/local/lib/GNUstep/SOGo/WebServerResources/img/sogo-logo.png:z
- ./data/conf/sogo/custom-theme.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z - ./data/conf/sogo/custom-theme.js:/usr/local/lib/GNUstep/SOGo/WebServerResources/js/theme.js:z
- ./data/conf/sogo/custom-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z - ./data/conf/sogo/custom-sogo.js:/usr/local/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
- mysql-socket-vol-1:/var/run/mysqld/:z - mysql-socket-vol-1:/var/run/mysqld/:z
- sogo-web-vol-1:/sogo_web - sogo-web-vol-1:/sogo_web
- sogo-userdata-backup-vol-1:/sogo_backup - sogo-userdata-backup-vol-1:/sogo_backup
@@ -252,7 +252,7 @@ services:
- sogo - sogo
dovecot-mailcow: dovecot-mailcow:
image: ghcr.io/mailcow/dovecot:2.3.21.1-1 image: ghcr.io/mailcow/dovecot:2.3.21.1-2
depends_on: depends_on:
- mysql-mailcow - mysql-mailcow
- netfilter-mailcow - netfilter-mailcow
@@ -449,7 +449,7 @@ services:
- ./data/web/inc/functions.inc.php:/mailcowauth/functions.inc.php:z - ./data/web/inc/functions.inc.php:/mailcowauth/functions.inc.php:z
- ./data/web/inc/functions.auth.inc.php:/mailcowauth/functions.auth.inc.php:z - ./data/web/inc/functions.auth.inc.php:/mailcowauth/functions.auth.inc.php:z
- ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z - ./data/web/inc/sessions.inc.php:/mailcowauth/sessions.inc.php:z
- sogo-web-vol-1:/usr/lib/GNUstep/SOGo/ - sogo-web-vol-1:/usr/local/lib/GNUstep/SOGo/
ports: ports:
- "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}" - "${HTTPS_BIND:-}:${HTTPS_PORT:-443}:${HTTPS_PORT:-443}"
- "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}" - "${HTTP_BIND:-}:${HTTP_PORT:-80}:${HTTP_PORT:-80}"
@@ -465,7 +465,7 @@ services:
condition: service_started condition: service_started
unbound-mailcow: unbound-mailcow:
condition: service_healthy condition: service_healthy
image: ghcr.io/mailcow/acme:1.96 image: ghcr.io/mailcow/acme:1.97
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
@@ -526,7 +526,7 @@ services:
- /lib/modules:/lib/modules:ro - /lib/modules:/lib/modules:ro
watchdog-mailcow: watchdog-mailcow:
image: ghcr.io/mailcow/watchdog:2.10 image: ghcr.io/mailcow/watchdog:2.11
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
tmpfs: tmpfs:
+1 -1
View File
@@ -299,7 +299,7 @@ ACME_DNS_CHALLENGE=n
ACME_DNS_PROVIDER=dns_xxx ACME_DNS_PROVIDER=dns_xxx
ACME_ACCOUNT_EMAIL=me@example.com ACME_ACCOUNT_EMAIL=me@example.com
# You will need to pass provider-specific environment variables to the acme-mailcow container. # You will need to pass provider-specific environment variables to the acme-mailcow container.
# See the dns-101 provider documentation for more information. # See the dns-01 provider documentation for more information.
# for example for Azure DNS: # for example for Azure DNS:
#AZUREDNS_SUBSCRIPTIONID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx #AZUREDNS_SUBSCRIPTIONID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
#AZUREDNS_TENANTID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx #AZUREDNS_TENANTID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+41 -24
View File
@@ -19,30 +19,48 @@ if [ ! -f "${PWD}/mailcow.conf" ]; then
fi fi
BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)" BRANCH="$(cd "${SCRIPT_DIR}" && git rev-parse --abbrev-ref HEAD)"
# Check for --dev flag early to skip _modules update
for arg in "$@"; do
if [[ "$arg" == "--dev" || "$arg" == "-d" ]]; then
echo -e "\e[32mRunning in Developer mode...\e[0m"
DEV=y
break
fi
done
MODULE_DIR="${SCRIPT_DIR}/_modules" MODULE_DIR="${SCRIPT_DIR}/_modules"
# Calculate hash before fetch
if [[ -d "${MODULE_DIR}" && -n "$(ls -A "${MODULE_DIR}" 2>/dev/null)" ]]; then if [ ! "$DEV" ]; then
MODULES_HASH_BEFORE=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}') # Calculate hash before fetch
if [[ -d "${MODULE_DIR}" && -n "$(ls -A "${MODULE_DIR}" 2>/dev/null)" ]]; then
MODULES_HASH_BEFORE=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}')
else
MODULES_HASH_BEFORE="EMPTY"
fi
echo -e "\e[33mFetching latest _modules from origin/${BRANCH}…\e[0m"
git fetch origin "${BRANCH}"
git checkout "origin/${BRANCH}" -- _modules
if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then
echo -e "\e[31mError: _modules is still missing or empty after fetch!\e[0m"
exit 2
fi
# Calculate hash after fetch
MODULES_HASH_AFTER=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}')
# Check if modules changed
if [[ "${MODULES_HASH_BEFORE}" != "${MODULES_HASH_AFTER}" ]]; then
echo -e "\e[33m_modules have been updated. Please restart the update script.\e[0m"
exit 2
fi
else else
MODULES_HASH_BEFORE="EMPTY" echo -e "\e[33mDeveloper mode: Skipping _modules update from git\e[0m"
fi if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then
echo -e "\e[31mError: _modules directory is missing or empty!\e[0m"
echo -e "\e[33mFetching latest _modules from origin/${BRANCH}…\e[0m" exit 2
git fetch origin "${BRANCH}" fi
git checkout "origin/${BRANCH}" -- _modules
if [[ ! -d "${MODULE_DIR}" || -z "$(ls -A "${MODULE_DIR}")" ]]; then
echo -e "\e[31mError: _modules is still missing or empty after fetch!\e[0m"
exit 2
fi
# Calculate hash after fetch
MODULES_HASH_AFTER=$(find "${MODULE_DIR}" -type f -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | awk '{print $1}')
# Check if modules changed
if [[ "${MODULES_HASH_BEFORE}" != "${MODULES_HASH_AFTER}" ]]; then
echo -e "\e[33m_modules have been updated. Please restart the update script.\e[0m"
exit 2
fi fi
source _modules/scripts/core.sh source _modules/scripts/core.sh
@@ -151,8 +169,7 @@ while (($#)); do
FORCE=y FORCE=y
;; ;;
-d|--dev) -d|--dev)
echo -e "\e[32mRunning in Developer mode...\e[0m" # Already handled at the top of the script before _modules update
DEV=y
;; ;;
--legacy) --legacy)
CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)" CURRENT_BRANCH="$(cd "${SCRIPT_DIR}"; git rev-parse --abbrev-ref HEAD)"