Browse Source

Merge pull request #6335 from mailcow/staging

Update 2025-02
FreddleSpl0it 6 months ago
parent
commit
2d1ef41d32
47 changed files with 259 additions and 99 deletions
  1. 1 1
      .github/workflows/check_prs_if_on_staging.yml
  2. 1 1
      .github/workflows/pr_to_nightly.yml
  3. 9 5
      .github/workflows/rebuild_backup_image.yml
  4. 3 1
      .gitignore
  5. 1 1
      data/Dockerfiles/backup/Dockerfile
  6. 1 1
      data/Dockerfiles/dovecot/quota_notify.py
  7. 5 0
      data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf
  8. 5 0
      data/Dockerfiles/dovecot/syslog-ng.conf
  9. 4 5
      data/Dockerfiles/netfilter/main.py
  10. 2 1
      data/Dockerfiles/nginx/bootstrap.py
  11. 4 3
      data/Dockerfiles/postfix/postfix.sh
  12. 1 1
      data/Dockerfiles/rspamd/Dockerfile
  13. 3 4
      data/Dockerfiles/sogo/Dockerfile
  14. 3 3
      data/Dockerfiles/sogo/bootstrap-sogo.sh
  15. 6 6
      data/assets/ssl-example/dhparams.pem
  16. 1 0
      data/conf/dovecot/dovecot.conf
  17. 2 2
      data/conf/mysql/my.cnf
  18. 4 2
      data/conf/nginx/templates/sites-default.conf.j2
  19. 2 3
      data/conf/postfix/main.cf
  20. 5 0
      data/conf/redis/redis-conf.sh
  21. 1 0
      data/conf/rspamd/local.d/options.inc
  22. BIN
      data/conf/sogo/custom-fulllogo.png
  23. 44 0
      data/conf/sogo/custom-fulllogo.svg
  24. 16 0
      data/conf/sogo/custom-shortlogo.svg
  25. 3 0
      data/web/edit.php
  26. 22 4
      data/web/inc/functions.address_rewriting.inc.php
  27. 2 2
      data/web/lang/lang.cs-cz.json
  28. 2 2
      data/web/lang/lang.da-dk.json
  29. 2 2
      data/web/lang/lang.de-de.json
  30. 3 3
      data/web/lang/lang.en-gb.json
  31. 9 4
      data/web/lang/lang.es-es.json
  32. 2 2
      data/web/lang/lang.fi-fi.json
  33. 2 2
      data/web/lang/lang.fr-fr.json
  34. 3 3
      data/web/lang/lang.it-it.json
  35. 1 1
      data/web/lang/lang.ja-jp.json
  36. 3 3
      data/web/lang/lang.ko-kr.json
  37. 2 2
      data/web/lang/lang.nl-nl.json
  38. 2 2
      data/web/lang/lang.pt-br.json
  39. 2 2
      data/web/lang/lang.ro-ro.json
  40. 2 2
      data/web/lang/lang.ru-ru.json
  41. 2 2
      data/web/lang/lang.sk-sk.json
  42. 2 2
      data/web/lang/lang.sv-se.json
  43. 6 1
      data/web/sogo-auth.php
  44. 22 16
      docker-compose.yml
  45. 1 1
      generate_config.sh
  46. 1 1
      helper-scripts/backup_and_restore.sh
  47. 39 0
      update.sh

+ 1 - 1
.github/workflows/check_prs_if_on_staging.yml

@@ -12,7 +12,7 @@ jobs:
       - name: Send message
         uses: thollander/actions-comment-pull-request@v3.0.1
         with:
-          GITHUB_TOKEN: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
+          github-token: ${{ secrets.CHECKIFPRISSTAGING_ACTION_PAT }}
           message: |
                    Thanks for contributing!
 

+ 1 - 1
.github/workflows/pr_to_nightly.yml

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

+ 9 - 5
.github/workflows/rebuild_backup_image.yml

@@ -9,6 +9,8 @@ on:
 jobs:
   docker_image_build:
     runs-on: ubuntu-latest
+    permissions:
+      packages: write
     steps:
       - name: Checkout
         uses: actions/checkout@v4
@@ -19,17 +21,19 @@ jobs:
       - name: Set up Docker Buildx
         uses: docker/setup-buildx-action@v3
 
-      - name: Login to Docker Hub
+      - name: Login to GHCR
+        if: github.event_name != 'pull_request'
         uses: docker/login-action@v3
         with:
-          username: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_USERNAME }}
-          password: ${{ secrets.BACKUPIMAGEBUILD_ACTION_DOCKERHUB_TOKEN }}
+          registry: ghcr.io
+          username: ${{ github.repository_owner }}
+          password: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Build and push
-        uses: docker/build-push-action@v6
+        uses: docker/build-push-action@v5
         with:
           context: .
           platforms: linux/amd64,linux/arm64
           file: data/Dockerfiles/backup/Dockerfile
           push: true
-          tags: mailcow/backup:latest
+          tags: ghcr.io/mailcow/backup:latest

+ 3 - 1
.gitignore

@@ -47,7 +47,9 @@ data/conf/sogo/custom-theme.js
 data/conf/sogo/plist_ldap
 data/conf/sogo/sieve.creds
 data/conf/sogo/cron.creds
-data/conf/sogo/sogo-full.svg
+data/conf/sogo/custom-fulllogo.svg
+data/conf/sogo/custom-shortlogo.svg
+data/conf/sogo/custom-fulllogo.png
 data/gitea/
 data/gogs/
 data/hooks/dovecot/*

+ 1 - 1
data/Dockerfiles/backup/Dockerfile

@@ -1,3 +1,3 @@
 FROM debian:bookworm-slim
 
-RUN apt update && apt install pigz
+RUN apt update && apt install pigz -y --no-install-recommends

+ 1 - 1
data/Dockerfiles/dovecot/quota_notify.py

@@ -23,7 +23,7 @@ else:
 
 while True:
   try:
-    r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, password=os.environ['REDISPASS'])
+    r = redis.StrictRedis(host='redis', decode_responses=True, port=6379, db=0, username='quota_notify', password='')
     r.ping()
   except Exception as ex:
     print('%s - trying again...'  % (ex))

+ 5 - 0
data/Dockerfiles/dovecot/syslog-ng-redis_slave.conf

@@ -38,8 +38,13 @@ filter f_replica {
   not match("User has no mail_replica in userdb" value("MESSAGE"));
   not match("Error: sync: Unknown user in remote" value("MESSAGE"));
 };
+filter f_dovecot_auth_try {
+  not match("- trying the next passdb" value("MESSAGE")) and
+  not match("- trying the next userdb" value("MESSAGE"));
+};
 log {
   source(s_dgram);
+  filter(f_dovecot_auth_try);
   filter(f_replica);
   destination(d_stdout);
   filter(f_mail);

+ 5 - 0
data/Dockerfiles/dovecot/syslog-ng.conf

@@ -38,8 +38,13 @@ filter f_replica {
   not match("User has no mail_replica in userdb" value("MESSAGE"));
   not match("Error: sync: Unknown user in remote" value("MESSAGE"));
 };
+filter f_dovecot_auth_try {
+  not match("- trying the next passdb" value("MESSAGE")) and
+  not match("- trying the next userdb" value("MESSAGE"));
+};
 log {
   source(s_dgram);
+  filter(f_dovecot_auth_try);
   filter(f_replica);
   destination(d_stdout);
   filter(f_mail);

+ 4 - 5
data/Dockerfiles/netfilter/main.py

@@ -85,11 +85,10 @@ def refreshF2bregex():
     f2bregex[3] = r'warning: .*\[([0-9a-f\.:]+)\]: SASL .+ authentication failed: (?!.*Connection lost to authentication server).+'
     f2bregex[4] = r'warning: non-SMTP command from .*\[([0-9a-f\.:]+)]:.+'
     f2bregex[5] = r'NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+'
-    f2bregex[6] = r'-login: Disconnected.+ \(auth failed, .+\): user=.*, method=.+, rip=([0-9a-f\.:]+),'
-    f2bregex[7] = r'-login: Aborted login.+ \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
-    f2bregex[8] = r'-login: Aborted login.+ \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+'
-    f2bregex[9] = r'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
-    f2bregex[10] = r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
+    f2bregex[6] = r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)'
+    f2bregex[7] = r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): unknown user \(SHA1 of given password: [a-f0-9]+\)'
+    f2bregex[8] = r'SOGo.+ Login from \'([0-9a-f\.:]+)\' for user .+ might not have worked'
+    f2bregex[9] = r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+'
     r.set('F2B_REGEX', json.dumps(f2bregex, ensure_ascii=False))
   else:
     try:

+ 2 - 1
data/Dockerfiles/nginx/bootstrap.py

@@ -43,10 +43,11 @@ def nginx_conf(env, template_vars):
 def prepare_template_vars():
   ipv4_network = os.getenv("IPV4_NETWORK", "172.22.1")
   additional_server_names = os.getenv("ADDITIONAL_SERVER_NAMES", "")
+  trusted_proxies = os.getenv("TRUSTED_PROXIES", "")
 
   template_vars = {
     'IPV4_NETWORK': ipv4_network,
-    'TRUSTED_NETWORK': os.getenv("TRUSTED_NETWORK", False),
+    'TRUSTED_PROXIES': [item.strip() for item in trusted_proxies.split(",") if item.strip()],
     'SKIP_RSPAMD': os.getenv("SKIP_RSPAMD", "n").lower() in ("y", "yes"),
     'SKIP_SOGO': os.getenv("SKIP_SOGO", "n").lower() in ("y", "yes"),
     'NGINX_USE_PROXY_PROTOCOL': os.getenv("NGINX_USE_PROXY_PROTOCOL", "n").lower() in ("y", "yes"),

+ 4 - 3
data/Dockerfiles/postfix/postfix.sh

@@ -416,10 +416,11 @@ postscreen_dnsbl_sites = wl.mailspike.net=127.0.0.[18;19;20]*-2
   bl.mailspike.net=127.0.0.[10;11;12]*4
 EOF
 fi
-DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S')
 
-# Remove discontinued Nixspam DNSBL from existing dns_blocklists.cf
-sed -i '/ix\.dnsbl\.manitu\.net\*2/d' /opt/postfix/conf/dns_blocklists.cf
+# Remove discontinued DNSBLs from existing dns_blocklists.cf
+sed -i '/ix\.dnsbl\.manitu\.net\*2/d' /opt/postfix/conf/dns_blocklists.cf # Nixspam
+
+DNSBL_CONFIG=$(grep -v '^#' /opt/postfix/conf/dns_blocklists.cf | grep '\S')
 
 if [ ! -z "$DNSBL_CONFIG" ]; then
   echo -e "\e[33mChecking if ASN for your IP is listed for Spamhaus Bad ASN List...\e[0m"

+ 1 - 1
data/Dockerfiles/rspamd/Dockerfile

@@ -2,7 +2,7 @@ FROM debian:bookworm-slim
 LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG DEBIAN_FRONTEND=noninteractive
-ARG RSPAMD_VER=rspamd_3.10.2-1~b8a232043
+ARG RSPAMD_VER=rspamd_3.11.0-2~90a175b45
 ARG CODENAME=bookworm
 ENV LC_ALL=C
 

+ 3 - 4
data/Dockerfiles/sogo/Dockerfile

@@ -4,7 +4,7 @@ LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
 
 ARG DEBIAN_FRONTEND=noninteractive
 ARG DEBIAN_VERSION=bookworm
-ARG SOGO_DEBIAN_REPOSITORY=http://www.axis.cz/linux/debian
+ARG SOGO_DEBIAN_REPOSITORY=https://packagingv2.sogo.nu/sogo-nightly-debian/
 # renovate: datasource=github-releases depName=tianon/gosu versioning=semver-coerced extractVersion=^(?<version>.*)$
 ARG GOSU_VERSION=1.17
 ENV LC_ALL=C
@@ -33,9 +33,8 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
   && gosu nobody true \
   && mkdir /usr/share/doc/sogo \
   && touch /usr/share/doc/sogo/empty.sh \
-  && wget http://www.axis.cz/linux/debian/axis-archive-keyring.deb -O /tmp/axis-archive-keyring.deb \
-  && apt install -y /tmp/axis-archive-keyring.deb \
-  && echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} sogo-v5" > /etc/apt/sources.list.d/sogo.list \
+  && wget -O- https://keys.openpgp.org/vks/v1/by-fingerprint/74FFC6D72B925A34B5D356BDF8A27B36A6E2EAE9 | gpg --dearmor | apt-key add - \
+  && echo "deb [trusted=yes] ${SOGO_DEBIAN_REPOSITORY} ${DEBIAN_VERSION} main" > /etc/apt/sources.list.d/sogo.list \
   && apt-get update && apt-get install -y --no-install-recommends \
     sogo \
     sogo-activesync \

+ 3 - 3
data/Dockerfiles/sogo/bootstrap-sogo.sh

@@ -30,7 +30,7 @@ if [[ "${MASTER}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
   mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "DROP VIEW IF EXISTS sogo_view"
   while [[ ${VIEW_OK} != 'OK' ]]; do
     mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS} ${DBNAME} << EOF
-CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) AS 
+CREATE VIEW sogo_view (c_uid, domain, c_name, c_password, c_cn, mail, aliases, ad_aliases, ext_acl, kind, multiple_bookings) AS
 SELECT
    mailbox.username,
    mailbox.domain,
@@ -240,8 +240,8 @@ chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
 #  fi
 #fi
 
-# Copy logo, if any
-[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
+# Rename custom logo, if any
+[[ -f /etc/sogo/sogo-full.svg ]] && mv /etc/sogo/sogo-full.svg /etc/sogo/custom-fulllogo.svg
 
 # Rsync web content
 echo "Syncing web content with named volume"

+ 6 - 6
data/assets/ssl-example/dhparams.pem

@@ -1,8 +1,8 @@
 -----BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA9iHB0CRDhV8wfBgqnmvuJpl0fzL3qL75R4ZvQHlfMNLrxuIz2x9D
-9zcDhPcBTVzV5Ay0AAkke4wP6r6wDQqXqBP4Y8IOkYAyLh3jM40jfHQzQt+5JdQl
-ond3kiscBsFOch/vMfSLMu3lAb0YhPNTvrxhMz7LcVAWYl82swASupdiKR+MgaQr
-XsugpmDKsHW60VmIM9B7K9Y+rNHwvMWkmISd0KxA8oOy1WJvsVEissMALZDE3c4w
-2xHmO2lXxgEx3aez28736t4m/KW3g9Zr31a1M0KusmfY//fGkPk4NUrLBOS2xrgp
-Y/rG1qSBdcVyerM0Ki93qCyHKYu4ene0OwIBAg==
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
 -----END DH PARAMETERS-----

+ 1 - 0
data/conf/dovecot/dovecot.conf

@@ -278,6 +278,7 @@ imap_max_line_length = 2 M
 #auth_cache_negative_ttl = 0
 #auth_cache_ttl = 30 s
 #auth_cache_size = 2 M
+auth_verbose_passwords = sha1:6
 service replicator {
   process_min_avail = 1
 }

+ 2 - 2
data/conf/mysql/my.cnf

@@ -1,7 +1,7 @@
 [mysqld]
 character-set-client-handshake = FALSE
 character-set-server           = utf8mb4
-collation-server               = utf8mb4_unicode_ci
+collation-server               = utf8mb4_general_ci
 #innodb_file_per_table          = TRUE
 #innodb_file_format             = barracuda
 #innodb_large_prefix            = TRUE
@@ -20,7 +20,7 @@ thread_cache_size       = 8
 query_cache_type        = 0
 query_cache_size        = 0
 max_heap_table_size     = 48M
-thread_stack            = 192K
+thread_stack            = 256K
 skip-host-cache
 skip-name-resolve
 log-warnings            = 0

+ 4 - 2
data/conf/nginx/templates/sites-default.conf.j2

@@ -52,10 +52,12 @@ set_real_ip_from 10.0.0.0/8;
 set_real_ip_from 172.16.0.0/12;
 set_real_ip_from 192.168.0.0/16;
 set_real_ip_from fc00::/7;
-{% if not TRUSTED_NETWORK %}
+{% for TRUSTED_PROXY in TRUSTED_PROXIES %}
+set_real_ip_from {{ TRUSTED_PROXY }};
+{% endfor %}
+{% if not NGINX_USE_PROXY_PROTOCOL %}
 real_ip_header X-Forwarded-For;
 {% else %}
-set_real_ip_from {{ TRUSTED_NETWORK }};
 real_ip_header proxy_protocol;
 {% endif %}
 real_ip_recursive on;

+ 2 - 3
data/conf/postfix/main.cf

@@ -162,10 +162,9 @@ transport_maps = pcre:/opt/postfix/conf/custom_transport.pcre,
   proxy:mysql:/opt/postfix/conf/sql/mysql_relay_ne.cf,
   proxy:mysql:/opt/postfix/conf/sql/mysql_transport_maps.cf
 smtp_sasl_auth_soft_bounce = no
-postscreen_discard_ehlo_keywords = silent-discard, dsn, chunking
-smtpd_discard_ehlo_keywords = chunking, silent-discard
+postscreen_discard_ehlo_keywords = chunking, silent-discard, smtputf8, dsn
+smtpd_discard_ehlo_keywords = chunking, silent-discard, smtputf8
 compatibility_level = 3.7
-smtputf8_enable = no
 # Define protocols for SMTPS and submission service
 submission_smtpd_tls_mandatory_protocols = >=TLSv1.2
 smtps_smtpd_tls_mandatory_protocols = >=TLSv1.2

+ 5 - 0
data/conf/redis/redis-conf.sh

@@ -2,6 +2,11 @@
 
 cat <<EOF > /redis.conf
 requirepass $REDISPASS
+user quota_notify on nopass ~QW_* -@all +get +hget +ping
 EOF
 
+if [ -n "$REDISMASTERPASS" ]; then
+  echo "masterauth $REDISMASTERPASS" >> /redis.conf
+fi
+
 exec redis-server /redis.conf

+ 1 - 0
data/conf/rspamd/local.d/options.inc

@@ -3,6 +3,7 @@ dns {
 }
 map_watch_interval = 30s;
 task_timeout = 30s;
+enable_mime_utf = true;
 disable_monitoring = true;
 # In case a task times out (like DNS lookup), soft reject the message
 # instead of silently accepting the message without further processing.

BIN
data/conf/sogo/custom-fulllogo.png


+ 44 - 0
data/conf/sogo/custom-fulllogo.svg

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY st0 "fill:#50BD37;">
+]>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="640px" height="350px" viewBox="78.712 58.488 640 350" style="enable-background:new 78.712 58.488 640 350;"
+	 xml:space="preserve">
+<path style="&st0;" d="M648.541,145.679c-9.947,0-17.009-7.278-17.009-17.048c0-9.777,7.062-17.057,17.009-17.057
+	c10.024,0,17.086,7.279,17.086,17.057C665.627,138.401,658.565,145.679,648.541,145.679z M648.511,94.893
+	c-19.693,0-33.679,14.4-33.679,33.738c0,19.33,13.985,33.729,33.679,33.729c19.822,0,33.808-14.4,33.808-33.729
+	C682.318,109.293,668.333,94.893,648.511,94.893z M648.482,179.843c-29.889,0-51.123-21.868-51.123-51.212
+	c0-29.353,21.234-51.209,51.123-51.209c30.082,0,51.307,21.856,51.307,51.209C699.789,157.975,678.564,179.843,648.482,179.843z
+	 M648.442,58.488c-40.929,0-69.995,29.946-69.995,70.143c0,40.189,29.066,70.125,69.995,70.125c41.194,0,70.27-29.937,70.27-70.125
+	C718.712,88.434,689.637,58.488,648.442,58.488z M158.166,183.902l-21.018-5.008c-19.131-4.396-28.849-9.413-28.849-23.21
+	c0-15.684,15.99-21.965,30.419-21.965c14.667,0,25.382,7.329,31.693,18.737c0.02,0.048,0.051,0.097,0.09,0.157
+	c0.127,0.247,0.276,0.484,0.403,0.731l0.03-0.02c1.985,3.002,5.323,5.008,8.919,5.008c6.122,0,10.558-4.425,10.558-10.547
+	c0-2.341-0.504-4.82-1.601-6.688c-10.764-18.302-28.513-26.192-48.838-26.192c-27.594,0-54.262,13.797-54.262,44.218
+	c0,27.921,27.605,36.079,37.64,38.578l20.069,4.71c15.368,3.763,27.912,8.791,27.912,23.517c0,16.938-17.561,23.943-34.499,23.943
+	c-17.245,0-30.015-9.37-38.814-22.37h-0.01c-1.956-3-4.988-4.328-8.702-4.328c-5.984,0-10.805,5.185-10.587,11.162
+	c0.098,2.438,0.909,4.637,2.153,6.405c13.787,20.633,33.728,28.41,55.96,28.41c28.543,0,57.085-13.143,57.085-45.132
+	C193.918,203.325,178.551,188.613,158.166,183.902z M298.479,250.312c-33.866,0-55.199-25.403-55.199-58.331
+	c0-32.939,21.333-58.343,55.199-58.343c34.192,0,55.516,25.403,55.516,58.343C353.996,224.91,332.672,250.312,298.479,250.312z
+	 M298.479,114.823c-45.471,0-77.777,32.93-77.777,77.158c0,44.217,32.306,77.146,77.777,77.146
+	c45.786,0,78.093-32.929,78.093-77.146C376.572,147.753,344.266,114.823,298.479,114.823z M518.715,234.312
+	c-0.771,0.74-1.549,1.472-2.399,2.175c-1.106,1.014-2.391,2.112-3.854,3.208c-8.829,6.391-19.979,10.094-33.017,10.094
+	c-33.876,0-55.198-25.402-55.198-58.332c0-32.939,21.322-58.342,55.198-58.342c34.183,0,55.506,25.403,55.506,58.342
+	C534.951,208.653,529.135,223.774,518.715,234.312z M468.097,317.938c2.528,0,5.146-0.168,7.863-0.504
+	c5.018-0.631,9.588-0.909,13.729-0.909c19.24,0.109,29.036,5.7,34.943,12.158c5.895,6.499,8.168,15.311,8.158,22.796
+	c0.01,3.586-0.555,6.795-1.177,8.721c-2.944,8.93-8.888,15.002-17.996,19.576c-9.035,4.484-21.095,6.777-33.707,6.757
+	c-4.514,0-9.105-0.288-13.639-0.831c-8.573-0.987-19.911-4.671-28.13-11.093c-4.138-3.199-6.458-6.991-8.858-11.485
+	c-2.379-4.514-2.783-9.748-2.783-16.442v-0.742c0-12.346,4.84-20.544,11.051-26.5c3.07-2.904,5.69-5.064,7.99-6.438
+	c0.366-0.218,0.438-0.416,0.755-0.593C452.39,316.014,459.684,317.968,468.097,317.938z M479.445,114.301
+	c-45.471,0-77.786,32.929-77.786,77.157c0,29.887,14.765,54.598,38.378,67.489c-0.314,0.314-0.621,0.641-0.916,0.966
+	c-6.104,6.687-9.226,15.25-9.236,23.913c-0.008,3.821,0.624,7.741,1.977,11.494c-3.062,1.956-6.717,4.634-10.46,8.147
+	c-9.026,8.408-18.734,22.541-19.021,42.097c-0.01,0.454-0.01,0.829-0.01,1.118c-0.01,10.071,2.379,19.157,6.459,26.774
+	c6.133,11.466,15.683,19.445,25.539,24.77c9.917,5.334,20.257,8.166,29.273,9.274c5.373,0.643,10.826,0.988,16.268,0.988
+	c15.151-0.02,30.261-2.578,43.409-9.019c13.085-6.34,24.333-17.253,29.192-32.562c1.443-4.553,2.212-9.719,2.231-15.428
+	c-0.02-11.595-3.349-25.759-13.767-37.452c-10.421-11.734-27.654-19.566-51.288-19.459c-5.138,0-10.606,0.356-16.426,1.078
+	c-1.877,0.227-3.596,0.334-5.166,0.334c-7.239-0.048-10.872-2.053-13.036-4.098c-2.133-2.084-3.2-4.839-3.229-8.058
+	c-0.01-3.28,1.284-6.727,3.467-9.078c2.231-2.332,5.008-3.91,9.846-3.97c0.436,0,0.9,0.01,1.374,0.05
+	c3.101,0.216,6.112,0.325,9.037,0.325c24.188,0.047,42.38-7.448,54.756-17.759c12.415-10.312,18.971-22.854,22.071-32.76l-0.04-0.01
+	c3.37-8.899,5.197-18.715,5.197-29.166C557.539,147.229,525.234,114.301,479.445,114.301z"/>
+</svg>

+ 16 - 0
data/conf/sogo/custom-shortlogo.svg

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+	<!ENTITY st0 "fill:#50BD37;">
+]>
+<svg version="1.1" id="SOGo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="140.263px" height="140.269px" viewBox="499.737 0 140.263 140.269"
+	 style="enable-background:new 499.737 0 140.263 140.269;" xml:space="preserve">
+<path style="&st0;" d="M569.697,87.024c-9.928,0-16.975-7.264-16.975-17.017c0-9.757,7.047-17.022,16.975-17.022
+	c10.006,0,17.054,7.265,17.054,17.022C586.751,79.76,579.703,87.024,569.697,87.024z M569.667,36.335
+	c-19.657,0-33.614,14.372-33.614,33.673c0,19.294,13.955,33.667,33.614,33.667c19.787,0,33.745-14.372,33.745-33.667
+	C603.411,50.707,589.454,36.335,569.667,36.335z M569.639,121.123c-29.833,0-51.025-21.825-51.025-51.115
+	c0-29.296,21.192-51.111,51.025-51.111c30.025,0,51.213,21.815,51.213,51.111C620.852,99.298,599.664,121.123,569.639,121.123z
+	 M569.602,0c-40.854,0-69.864,29.889-69.864,70.007c0,40.112,29.01,69.993,69.864,69.993c41.116,0,70.136-29.88,70.136-69.993
+	C639.737,29.889,610.719,0,569.602,0z"/>
+</svg>

+ 3 - 0
data/web/edit.php

@@ -166,6 +166,9 @@ if (isset($_SESSION['mailcow_cc_role'])) {
         if (substr($result['recipient_map_old'], 0, 1) == '@') {
           $result['recipient_map_old'] = substr($result['recipient_map_old'], 1);
         }
+        if (substr($result['recipient_map_new'], 0, 1) == '@') {
+          $result['recipient_map_new'] = substr($result['recipient_map_new'], 1);
+        }
         $template = 'edit/recipient_map.twig';
         $template_data = ['map' => $map];
     }

+ 22 - 4
data/web/inc/functions.address_rewriting.inc.php

@@ -270,6 +270,9 @@ function recipient_map($_action, $_data = null, $attr = null) {
         $old_dest = substr($old_dest, 1);
       }
       $new_dest = strtolower(trim($_data['recipient_map_new']));
+      if (substr($new_dest, 0, 1) == '@') {
+        $new_dest = substr($new_dest, 1);
+      }
       $active = intval($_data['active']);
       if (is_valid_domain_name($old_dest)) {
         $old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46);
@@ -285,7 +288,13 @@ function recipient_map($_action, $_data = null, $attr = null) {
         );
         return false;
       }
-      if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
+      if (is_valid_domain_name($new_dest)) {
+        $new_dest_sane = '@' . idn_to_ascii($new_dest, 0, INTL_IDNA_VARIANT_UTS46);
+      }
+      elseif (filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
+        $new_dest_sane = $new_dest;
+      }
+      else {
         $_SESSION['return'][] = array(
           'type' => 'danger',
           'log' => array(__FUNCTION__, $_action, $_data, $_attr),
@@ -308,7 +317,7 @@ function recipient_map($_action, $_data = null, $attr = null) {
         (:old_dest, :new_dest, :active)");
       $stmt->execute(array(
         ':old_dest' => $old_dest_sane,
-        ':new_dest' => $new_dest,
+        ':new_dest' => $new_dest_sane,
         ':active' => $active
       ));
       $_SESSION['return'][] = array(
@@ -325,6 +334,9 @@ function recipient_map($_action, $_data = null, $attr = null) {
           $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
           $new_dest = (!empty($_data['recipient_map_new'])) ? $_data['recipient_map_new'] : $is_now['recipient_map_new'];
           $old_dest = (!empty($_data['recipient_map_old'])) ? $_data['recipient_map_old'] : $is_now['recipient_map_old'];
+          if (substr($new_dest, 0, 1) == '@') {
+            $new_dest = substr($new_dest, 1);
+          }
           if (substr($old_dest, 0, 1) == '@') {
             $old_dest = substr($old_dest, 1);
           }
@@ -351,7 +363,13 @@ function recipient_map($_action, $_data = null, $attr = null) {
           );
           continue;
         }
-        if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
+        if (is_valid_domain_name($new_dest)) {
+          $new_dest_sane = '@' . idn_to_ascii($new_dest, 0, INTL_IDNA_VARIANT_UTS46);
+        }
+        elseif (filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
+          $new_dest_sane = $new_dest;
+        }
+        else {
           $_SESSION['return'][] = array(
             'type' => 'danger',
             'log' => array(__FUNCTION__, $_action, $_data, $_attr),
@@ -378,7 +396,7 @@ function recipient_map($_action, $_data = null, $attr = null) {
             WHERE `id`= :id");
         $stmt->execute(array(
           ':old_dest' => $old_dest_sane,
-          ':new_dest' => $new_dest,
+          ':new_dest' => $new_dest_sane,
           ':active' => $active,
           ':id' => $id
         ));

+ 2 - 2
data/web/lang/lang.cs-cz.json

@@ -869,7 +869,7 @@
         "recipient_map": "Mapa příjemce",
         "recipient_map_info": "Mapy příjemců slouží k nahrazení cílové adresy zprávy před doručením.",
         "recipient_map_new": "Nový přijemce",
-        "recipient_map_new_info": "Cílová adresa mapy příjemce musí být platná emailová adresa.",
+        "recipient_map_new_info": "Cílová adresa mapy příjemce musí být emailová adresa nebo název domény.",
         "recipient_map_old": "Původní příjemce",
         "recipient_map_old_info": "Původní příjemce musí být platná emailová adresa nebo název domény.",
         "recipient_maps": "Mapy příjemců",
@@ -1307,4 +1307,4 @@
         "session_token": "Token formuláře není platný: Token mismatch",
         "session_ua": "Token formuláře není platný: User-Agent validation error"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.da-dk.json

@@ -706,7 +706,7 @@
         "recipient_map": "Modtagerkort",
         "recipient_map_info": "Modtagerkort bruges til at erstatte destinationsadressen i en meddelelse, før den leveres.",
         "recipient_map_new": "Ny modtager",
-        "recipient_map_new_info": "Modtagerkortdestination skal være en gyldig e-mail-adresse.",
+        "recipient_map_new_info": "Modtagerkortdestination skal være gyldige e-mail-adresser eller et domænenavn.",
         "recipient_map_old": "Original modtager",
         "recipient_map_old_info": "En modtager kortlægger den originale destination, skal være gyldige e-mail-adresser eller et domænenavn.",
         "recipient_maps": "Modtagerkort",
@@ -1089,4 +1089,4 @@
             "first": "Først"
         }
     }
-}
+}

+ 2 - 2
data/web/lang/lang.de-de.json

@@ -881,7 +881,7 @@
         "recipient_map": "Empfängerumschreibung",
         "recipient_map_info": "Empfängerumschreibung ersetzen den Empfänger einer E-Mail vor dem Versand.",
         "recipient_map_new": "Neuer Empfänger",
-        "recipient_map_new_info": "Der neue Empfänger muss eine E-Mail-Adresse sein.",
+        "recipient_map_new_info": "Der neue Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
         "recipient_map_old": "Original-Empfänger",
         "recipient_map_old_info": "Der originale Empfänger muss eine E-Mail-Adresse oder ein Domainname sein.",
         "recipient_maps": "Empfängerumschreibungen",
@@ -1336,4 +1336,4 @@
         "hour": "Nachrichten / Stunde",
         "day": "Nachrichten / Tag"
     }
-}
+}

+ 3 - 3
data/web/lang/lang.en-gb.json

@@ -887,9 +887,9 @@
         "recipient_map": "Recipient map",
         "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
         "recipient_map_new": "New recipient",
-        "recipient_map_new_info": "Recipient map destination must be a valid email address.",
+        "recipient_map_new_info": "Recipient map destination must be a valid email addresses or a domain name.",
         "recipient_map_old": "Original recipient",
-        "recipient_map_old_info": "A recipient maps original destination must be valid email addresses or a domain name.",
+        "recipient_map_old_info": "A recipient maps original destination must be a valid email addresses or a domain name.",
         "recipient_maps": "Recipient maps",
         "relay_all": "Relay all recipients",
         "relay_unknown": "Relay unknown mailboxes",
@@ -1336,4 +1336,4 @@
         "session_token": "Form token invalid: Token mismatch",
         "session_ua": "Form token invalid: User-Agent validation error"
     }
-}
+}

+ 9 - 4
data/web/lang/lang.es-es.json

@@ -24,7 +24,9 @@
         "protocol_access": "Cambiar protocolo de acceso",
         "quarantine_category": "Cambiar categoría de las notificaciones de cuarentena",
         "domain_relayhost": "Cambiar relayhost por un dominio",
-        "extend_sender_acl": "Permitir extender la ACL del remitente por direcciones externas"
+        "extend_sender_acl": "Permitir extender la ACL del remitente por direcciones externas",
+        "pw_reset": "Permitir el reset  de la contraseña del usario mailcow",
+        "sogo_access": "Permitir la gestión del acceso a SOGo"
     },
     "add": {
         "activate_filter_warn": "Todos los demás filtros se desactivarán cuando este filtro se active.",
@@ -95,7 +97,10 @@
         "app_password": "Añadir contraseña para la app",
         "public_comment": "Comentarios públicos",
         "disable_login": "Desactivar login (el correo entrante seguirá activo)",
-        "comment_info": "Los comentarios privados no son visibles al usuario, mientras que los comentarios públicos aparecerán sobre la información general del usuario"
+        "comment_info": "Los comentarios privados no son visibles al usuario, mientras que los comentarios públicos aparecerán sobre la información general del usuario",
+        "dry": "Simular la sincronización",
+        "private_comment": "Comentario privado",
+        "app_passwd_protocols": "Protocolos autorizados para la contraseña de la aplicación"
     },
     "admin": {
         "access": "Acceso",
@@ -539,7 +544,7 @@
         "recipient_map": "Regla de destinatario",
         "recipient_map_info": "Las reglas de destinatarios se utilizan para reemplazar la dirección de destino en un mensaje antes de que se entregue.",
         "recipient_map_new": "Destinatario nuevo",
-        "recipient_map_new_info": "El destino de la regla debe ser una dirección de correo válida.",
+        "recipient_map_new_info": "El destino de la regla debe ser una dirección de correo electrónico válida o un nombre de dominio.",
         "recipient_map_old": "Destinatario original",
         "recipient_map_old_info": "El destino original de una regla de destinatario debe ser una dirección de correo electrónico válida o un nombre de dominio.",
         "recipient_maps": "Reglas de destinatario",
@@ -777,4 +782,4 @@
         "fuzzy_learn_error": "Error aprendiendo hash: %s",
         "ip_invalid": "IP inválida omitida: %s"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.fi-fi.json

@@ -607,7 +607,7 @@
         "recipient_map": "Vastaanottajien yhdistämis määritykset",
         "recipient_map_info": "Vastaanottajan karttoja käytetään korvaamaan viestin kohde osoite ennen sen toimittamista.",
         "recipient_map_new": "Uusi vastaanottaja",
-        "recipient_map_new_info": "Vastaanottajan yhdistämis kartan kohteen on oltava kelvollinen sähköposti osoite.",
+        "recipient_map_new_info": "Vastaanottajan yhdistämis kartan kohteen on oltava kelvollinen sähköposti osoite tai verkkotunnus alueen nimi.",
         "recipient_map_old": "Alkuperäinen vastaanottaja",
         "recipient_map_old_info": "Vastaanottajan yhdistämis määritysten alkuperäisen kohteen on oltava kelvollinen sähköposti osoite tai verkkotunnus alueen nimi.",
         "recipient_maps": "Vastaanottajien yhdistämis määritykset",
@@ -906,4 +906,4 @@
             "last": "Edellinen"
         }
     }
-}
+}

+ 2 - 2
data/web/lang/lang.fr-fr.json

@@ -834,7 +834,7 @@
         "recipient_map": "Carte du destinataire",
         "recipient_map_info": "Les cartes des destinataires sont utilisées pour remplacer l’adresse de destination d’un message avant sa livraison.",
         "recipient_map_new": "Nouveau destinataire",
-        "recipient_map_new_info": "La destination de la carte du destinataire doit être une adresse de courriel valide.",
+        "recipient_map_new_info": "La destination de la carte du destinataire doit être une adresse de courriel valide ou un nom de domaine.",
         "recipient_map_old": "Destinataire original",
         "recipient_map_old_info": "Une carte de destination originale doit être une adresse de courriel valide ou un nom de domaine.",
         "recipient_maps": "Cartes des bénéficiaires",
@@ -1335,4 +1335,4 @@
         "hour": "msgs / heure",
         "day": "msgs / jour"
     }
-}
+}

+ 3 - 3
data/web/lang/lang.it-it.json

@@ -822,9 +822,9 @@
         "recipient_map": "Recipient map",
         "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
         "recipient_map_new": "New recipient",
-        "recipient_map_new_info": "Recipient map destination must be a valid email address.",
+        "recipient_map_new_info": "Recipient map destination must be a valid email addresses or a domain name.",
         "recipient_map_old": "Original recipient",
-        "recipient_map_old_info": "A recipient maps original destination must be valid email addresses or a domain name.",
+        "recipient_map_old_info": "A recipient maps original destination must be a valid email addresses or a domain name.",
         "recipient_maps": "Recipient maps",
         "relay_all": "Trasmettere a tutti i destinatari",
         "remove": "Rimuovi",
@@ -1306,4 +1306,4 @@
         },
         "decimal": "."
     }
-}
+}

+ 1 - 1
data/web/lang/lang.ja-jp.json

@@ -887,7 +887,7 @@
         "recipient_map": "受信者マップ",
         "recipient_map_info": "受信者マップは、メッセージが配信される前に宛先アドレスを置き換えるために使用されます。",
         "recipient_map_new": "新しい受信者",
-        "recipient_map_new_info": "受信者マップの宛先は有効なメールアドレスである必要があります。",
+        "recipient_map_new_info": "受信者マップの宛先は有効なメールアドレスまたはドメイン名である必要があります。",
         "recipient_map_old": "元の受信者",
         "recipient_map_old_info": "受信者マップの元の宛先は有効なメールアドレスまたはドメイン名である必要があります。",
         "recipient_maps": "受信者マップ",

+ 3 - 3
data/web/lang/lang.ko-kr.json

@@ -673,9 +673,9 @@
         "recipient_map": "Recipient map",
         "recipient_map_info": "Recipient maps are used to replace the destination address on a message before it is delivered.",
         "recipient_map_new": "New recipient",
-        "recipient_map_new_info": "Recipient map destination must be a valid email address.",
+        "recipient_map_new_info": "Recipient map destination must be a valid email addresses or a domain name.",
         "recipient_map_old": "Original recipient",
-        "recipient_map_old_info": "A recipient maps original destination must be valid email addresses or a domain name.",
+        "recipient_map_old_info": "A recipient maps original destination must be a valid email addresses or a domain name.",
         "recipient_maps": "Recipient maps",
         "relay_all": "모든 수신자에게 릴레이",
         "remove": "Remove",
@@ -1018,4 +1018,4 @@
         "session_token": "Form token invalid: Token mismatch",
         "session_ua": "Form token invalid: User-Agent validation error"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.nl-nl.json

@@ -764,7 +764,7 @@
         "recipient_map": "Ontvanger-map",
         "recipient_map_info": "Ontvanger-maps worden gebruikt om het doeladres van een bericht te vervangen voordat het in een mailbox terecht komt.",
         "recipient_map_new": "Nieuwe ontvanger",
-        "recipient_map_new_info": "De bestemming van een ontvanger-map dient een geldig mailadres te zijn.",
+        "recipient_map_new_info": "De bestemming van een ontvanger-map dient een geldig mailadres of domeinnaam te zijn.",
         "recipient_map_old": "Oorspronkelijke ontvanger",
         "recipient_map_old_info": "De oorspronkelijke bestemming van een ontvanger-map dient een geldig mailadres of domeinnaam te zijn.",
         "recipient_maps": "Ontvanger-maps",
@@ -1165,4 +1165,4 @@
         "search": "Zoeken:",
         "zeroRecords": "Geen overeenkomsten gevonden"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.pt-br.json

@@ -882,7 +882,7 @@
         "recipient_map": "Mapa do destinatário",
         "recipient_map_info": "Os mapas de destinatários são usados para substituir o endereço de destino em uma mensagem antes que ela seja entregue.",
         "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.",
+        "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_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_maps": "Mapas de destinatários",
@@ -1330,4 +1330,4 @@
         "session_token": "Token de formulário inválido: incompatibilidade de token",
         "session_ua": "Token de formulário inválido: erro de validação do agente de usuário"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.ro-ro.json

@@ -789,7 +789,7 @@
         "recipient_map": "Hartă destinatar",
         "recipient_map_info": "Hărțile destinatarilor sunt folosite pentru a înlocui adresa de destinație a unui mesaj înainte de a fi livrat.",
         "recipient_map_new": "Destinatar nou",
-        "recipient_map_new_info": "Destinația hărții destinatarului trebuie să fie o adresă de email validă.",
+        "recipient_map_new_info": "Destinația hărții destinatarului trebuie să fie adrese de email valide sau nume de domeniu.",
         "recipient_map_old": "Destinatar original",
         "recipient_map_old_info": "Destinația originală a hărților destinatarilor trebuie să fie adrese de email valide sau nume de domeniu.",
         "recipient_maps": "Hărți destinatar",
@@ -1208,4 +1208,4 @@
         "expand_all": "Expandează tot",
         "decimal": ","
     }
-}
+}

+ 2 - 2
data/web/lang/lang.ru-ru.json

@@ -887,7 +887,7 @@
         "recipient_map": "Перезапись получателя",
         "recipient_map_info": "Перезапись получателя используются для замены получателя в сообщении до его доставки.",
         "recipient_map_new": "Перезапись на",
-        "recipient_map_new_info": "Должен быть действующим почтовым ящиком.",
+        "recipient_map_new_info": "Должен быть действующим почтовым ящиком или доменом.",
         "recipient_map_old": "Получатель",
         "recipient_map_old_info": "Должен быть действующим почтовым ящиком или доменом.",
         "recipient_maps": "Перезапись получателя",
@@ -1336,4 +1336,4 @@
         "session_token": "Неверный токен формы: несоответствие токена",
         "session_ua": "Неверный токен формы: ошибка проверки User-Agent"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.sk-sk.json

@@ -822,7 +822,7 @@
         "recipient_map": "Mapa príjemcu",
         "recipient_map_info": "Mapy príjemcov sú používané ako náhrada cieľovej adresy u správy pred doručením.",
         "recipient_map_new": "Nový príjemca",
-        "recipient_map_new_info": "Mapový cieľ príjemcu musí byť platná emailová adresa.",
+        "recipient_map_new_info": "Mapový cieľ príjemcu musí byť platná emailová adresa alebo meno domény.",
         "recipient_map_old": "Originálny príjemca",
         "recipient_map_old_info": "Originálny cieľ mapy príjemcu musí byť platná emailová adresa alebo meno domény.",
         "recipient_maps": "Mapy príjemcov",
@@ -1256,4 +1256,4 @@
         "session_token": "Formulárový token neplatný: Tokenová nezhoda",
         "session_ua": "Formulárový token neplatný: User-Agent validation error"
     }
-}
+}

+ 2 - 2
data/web/lang/lang.sv-se.json

@@ -726,7 +726,7 @@
         "recipient_map": "Mottagaromskrivning",
         "recipient_map_info": "En omskrivning av mottagaradressen används för att ersätta destinationsadressen i ett meddelande innan den levereras.",
         "recipient_map_new": "Ny mottagare",
-        "recipient_map_new_info": "Den ursprungliga mottagaren måste vara en giltig e-postadress.",
+        "recipient_map_new_info": "Den ursprungliga mottagaren måste vara en giltiga e-postadresser eller ett domännamn.",
         "recipient_map_old": "Ursprunglig mottagaren",
         "recipient_map_old_info": "Den ursprungliga mottagaren måste vara en giltiga e-postadresser eller ett domännamn.",
         "recipient_maps": "Skriv om mottagaradressen",
@@ -1110,4 +1110,4 @@
         "session_token": "Formulär-nyckeln är ogiltig: Nyckeln matchar inte",
         "session_ua": "Formulär-nyckeln är ogiltig: User-Agenten kunde inte valideras"
     }
-}
+}

+ 6 - 1
data/web/sogo-auth.php

@@ -72,7 +72,12 @@ elseif (isset($_GET['login'])) {
 // only check for admin-login on sogo GUI requests
 elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) {
   // this is an nginx auth_request call, we check for existing sogo-sso session variables
-  session_start();
+  require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
+  if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php')) {
+    include_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php';
+  }
+  require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
+
   // extract email address from "/SOGo/so/user@domain/xy"
   $url_parts = explode("/", $_SERVER['HTTP_X_ORIGINAL_URI']);
   $email_list = array(

+ 22 - 16
docker-compose.yml

@@ -1,7 +1,7 @@
 services:
 
     unbound-mailcow:
-      image: mailcow/unbound:1.23
+      image: ghcr.io/mailcow/unbound:1.23
       environment:
         - TZ=${TZ}
         - SKIP_UNBOUND_HEALTHCHECK=${SKIP_UNBOUND_HEALTHCHECK:-n}
@@ -17,7 +17,7 @@ services:
             - unbound
 
     mysql-mailcow:
-      image: mariadb:10.5
+      image: mariadb:10.11
       depends_on:
         - unbound-mailcow
         - netfilter-mailcow
@@ -42,8 +42,8 @@ services:
             - mysql
 
     redis-mailcow:
-      image: redis:7-alpine
-      entrypoint: /redis-conf.sh
+      image: redis:7.4.2-alpine
+      entrypoint: ["/bin/sh","/redis-conf.sh"]
       volumes:
         - redis-vol-1:/data/
         - ./data/conf/redis/redis-conf.sh:/redis-conf.sh:z
@@ -55,6 +55,7 @@ services:
       environment:
         - TZ=${TZ}
         - REDISPASS=${REDISPASS}
+        - REDISMASTERPASS=${REDISMASTERPASS:-}
       sysctls:
         - net.core.somaxconn=4096
       networks:
@@ -64,7 +65,7 @@ services:
             - redis
 
     clamd-mailcow:
-      image: mailcow/clamd:1.70
+      image: ghcr.io/mailcow/clamd:1.70
       restart: always
       depends_on:
         unbound-mailcow:
@@ -83,7 +84,7 @@ services:
             - clamd
 
     rspamd-mailcow:
-      image: mailcow/rspamd:1.99
+      image: ghcr.io/mailcow/rspamd:2.0
       stop_grace_period: 30s
       depends_on:
         - dovecot-mailcow
@@ -116,7 +117,7 @@ services:
             - rspamd
 
     php-fpm-mailcow:
-      image: mailcow/phpfpm:1.92
+      image: ghcr.io/mailcow/phpfpm:1.92
       command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
       depends_on:
         - redis-mailcow
@@ -182,7 +183,7 @@ services:
             - phpfpm
 
     sogo-mailcow:
-      image: mailcow/sogo:1.128
+      image: ghcr.io/mailcow/sogo:1.129
       environment:
         - DBNAME=${DBNAME}
         - DBUSER=${DBUSER}
@@ -207,6 +208,9 @@ services:
         - ./data/conf/sogo/:/etc/sogo/: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-shortlogo.svg:/usr/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.png:/usr/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-sogo.js:/usr/lib/GNUstep/SOGo/WebServerResources/js/custom-sogo.js:z
         - mysql-socket-vol-1:/var/run/mysqld/
@@ -230,7 +234,7 @@ services:
             - sogo
 
     dovecot-mailcow:
-      image: mailcow/dovecot:2.3
+      image: ghcr.io/mailcow/dovecot:2.31
       depends_on:
         - mysql-mailcow
         - netfilter-mailcow
@@ -317,7 +321,7 @@ services:
             - dovecot
 
     postfix-mailcow:
-      image: mailcow/postfix:1.79
+      image: ghcr.io/mailcow/postfix:1.80
       depends_on:
         mysql-mailcow:
           condition: service_started
@@ -373,7 +377,7 @@ services:
         - php-fpm-mailcow
         - sogo-mailcow
         - rspamd-mailcow
-      image: mailcow/nginx:1.02
+      image: ghcr.io/mailcow/nginx:1.03
       dns:
         - ${IPV4_NETWORK:-172.22.1}.254
       environment:
@@ -391,6 +395,8 @@ services:
         - RSPAMDHOST=${RSPAMDHOST:-}
         - REDISHOST=${REDISHOST:-}
         - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
+        - NGINX_USE_PROXY_PROTOCOL=${NGINX_USE_PROXY_PROTOCOL:-n}
+        - TRUSTED_PROXIES=${TRUSTED_PROXIES:-}
       volumes:
         - ./data/web:/web:ro,z
         - ./data/conf/rspamd/dynmaps:/dynmaps:ro,z
@@ -413,7 +419,7 @@ services:
           condition: service_started
         unbound-mailcow:
           condition: service_healthy
-      image: mailcow/acme:1.91
+      image: ghcr.io/mailcow/acme:1.91
       dns:
         - ${IPV4_NETWORK:-172.22.1}.254
       environment:
@@ -451,7 +457,7 @@ services:
             - acme
 
     netfilter-mailcow:
-      image: mailcow/netfilter:1.60
+      image: ghcr.io/mailcow/netfilter:1.61
       stop_grace_period: 30s
       restart: always
       privileged: true
@@ -471,7 +477,7 @@ services:
         - /lib/modules:/lib/modules:ro
 
     watchdog-mailcow:
-      image: mailcow/watchdog:2.06
+      image: ghcr.io/mailcow/watchdog:2.06
       dns:
         - ${IPV4_NETWORK:-172.22.1}.254
       tmpfs:
@@ -543,7 +549,7 @@ services:
             - watchdog
 
     dockerapi-mailcow:
-      image: mailcow/dockerapi:2.10
+      image: ghcr.io/mailcow/dockerapi:2.10
       security_opt:
         - label=disable
       restart: always
@@ -563,7 +569,7 @@ services:
             - dockerapi
 
     olefy-mailcow:
-      image: mailcow/olefy:1.13
+      image: ghcr.io/mailcow/olefy:1.13
       restart: always
       environment:
         - TZ=${TZ}

+ 1 - 1
generate_config.sh

@@ -26,7 +26,7 @@ for bin in openssl curl docker git awk sha1sum grep cut; do
 done
 
 # Check Docker Version (need at least 24.X)
-docker_version=$(docker -v | grep -oP '\d+\.\d+\.\d+' | head -n 1 | cut -d '.' -f 1)
+docker_version=$(docker version --format '{{.Server.Version}}' | cut -d '.' -f 1)
 
 if [[ $docker_version -lt 24 ]]; then
   echo -e "\e[31mCannot find Docker with a Version higher or equals 24.0.0\e[0m"

+ 1 - 1
helper-scripts/backup_and_restore.sh

@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-DEBIAN_DOCKER_IMAGE="mailcow/backup:latest"
+DEBIAN_DOCKER_IMAGE="ghcr.io/mailcow/backup:latest"
 
 if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then
   BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}"

+ 39 - 0
update.sh

@@ -710,6 +710,44 @@ migrate_solr_config_options() {
   fi
 }
 
+detect_major_update() {
+  if [ ${BRANCH} == "master" ]; then
+    # Array with major versions
+    # Add major versions here
+    MAJOR_VERSIONS=(
+      "2025-02"
+    )
+
+    current_version=$(git describe --tags $(git rev-list --tags --max-count=1))
+    release_url="https://github.com/mailcow/mailcow-dockerized/releases/tag"
+
+    updates_to_apply=()
+
+    for version in "${MAJOR_VERSIONS[@]}"; do
+      if [[ "$current_version" < "$version" ]]; then
+        updates_to_apply+=("$version")
+      fi
+    done
+
+    if [[ ${#updates_to_apply[@]} -gt 0 ]]; then
+      echo -e "\e[33m\nMAJOR UPDATES to be applied:\e[0m"
+      for update in "${updates_to_apply[@]}"; do
+        echo "$update - $release_url/$update"
+      done
+
+      echo -e "\n⚠️  Please read the release notes before proceeding.\n"
+
+      read -p "Do you want to proceed with the update? [y/n] " response
+      if [[ "${response}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
+        echo "Proceeding with the update..."
+      else
+        echo "Update canceled. Exiting."
+        exit 1
+      fi
+    fi
+  fi
+}
+
 ############## End Function Section ##############
 
 # Check permissions
@@ -1345,6 +1383,7 @@ if [ ! "$FORCE" ]; then
     echo "OK, exiting."
     exit 0
   fi
+  detect_major_update
   migrate_docker_nat
 fi