瀏覽代碼

Merge pull request #5250 from mailcow/staging

2023-05
Niklas Meyer 2 年之前
父節點
當前提交
74bcec45f1

+ 39 - 0
.github/workflows/update_postscreen_access_list.yml

@@ -0,0 +1,39 @@
+name: Update postscreen_access.cidr
+
+on:
+  schedule:
+    # Monthly
+    - cron: "0 0 1 * *"
+  workflow_dispatch: # Allow to run workflow manually
+
+permissions:
+  contents: read # to fetch code (actions/checkout)
+  
+  
+jobs:
+  Update-postscreen_access_cidr:
+   runs-on: ubuntu-latest
+   steps:
+    - name: Checkout
+      uses: actions/checkout@v3
+
+    - name: Generate postscreen_access.cidr
+      run: |
+          bash helper-scripts/update_postscreen_whitelist.sh
+
+    - name: Create Pull Request
+      uses: peter-evans/create-pull-request@v5
+      with:
+        token: ${{ secrets.mailcow_action_Update_postscreen_access_cidr_pat }}
+        commit-message: update postscreen_access.cidr
+        committer: milkmaker <milkmaker@mailcow.de>
+        author: milkmaker <milkmaker@mailcow.de>
+        signoff: false
+        branch: update/postscreen_access.cidr
+        base: staging
+        delete-branch: true
+        add-paths: |
+          data/conf/postfix/postscreen_access.cidr
+        title: '[Postfix] update postscreen_access.cidr'
+        body: |
+          This PR updates the postscreen_access.cidr using GitHub Actions and [helper-scripts/update_postscreen_whitelist.sh](https://github.com/mailcow/mailcow-dockerized/blob/master/helper-scripts/update_postscreen_whitelist.sh)

+ 4 - 2
data/Dockerfiles/dockerapi/dockerapi.py

@@ -9,6 +9,7 @@ import os
 import json
 import json
 import asyncio
 import asyncio
 import redis
 import redis
+import platform
 from datetime import datetime
 from datetime import datetime
 import logging
 import logging
 from logging.config import dictConfig
 from logging.config import dictConfig
@@ -370,7 +371,7 @@ class DockerUtils:
         return exec_run_handler('utf8_text_only', sieve_return)
         return exec_run_handler('utf8_text_only', sieve_return)
   # api call: container_post - post_action: exec - cmd: sieve - task: print
   # api call: container_post - post_action: exec - cmd: sieve - task: print
   def container_post__exec__sieve__print(self, container_id, request_json):
   def container_post__exec__sieve__print(self, container_id, request_json):
-    if 'username' in request.json and 'script_name' in request_json:
+    if 'username' in request_json and 'script_name' in request_json:
       for container in self.docker_client.containers.list(filters={"id": container_id}):
       for container in self.docker_client.containers.list(filters={"id": container_id}):
         cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]  
         cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request_json['username'].replace("'", "'\\''") + "' '" + request_json['script_name'].replace("'", "'\\''") + "'"]  
         sieve_return = container.exec_run(cmd)
         sieve_return = container.exec_run(cmd)
@@ -485,7 +486,8 @@ async def get_host_stats(wait=5):
         "swap": psutil.swap_memory()
         "swap": psutil.swap_memory()
       },
       },
       "uptime": time.time() - psutil.boot_time(),
       "uptime": time.time() - psutil.boot_time(),
-      "system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
+      "system_time": system_time.strftime("%d.%m.%Y %H:%M:%S"),
+      "architecture": platform.machine()
     }
     }
 
 
     redis_client.set('host_stats', json.dumps(host_stats), ex=10)
     redis_client.set('host_stats', json.dumps(host_stats), ex=10)

+ 18 - 0
data/Dockerfiles/phpfpm/docker-entrypoint.sh

@@ -172,6 +172,24 @@ BEGIN
 END;
 END;
 //
 //
 DELIMITER ;
 DELIMITER ;
+DROP EVENT IF EXISTS clean_sasl_log;
+DELIMITER //
+CREATE EVENT clean_sasl_log
+ON SCHEDULE EVERY 1 DAY DO
+BEGIN
+  DELETE sasl_log.* FROM sasl_log
+    LEFT JOIN (
+      SELECT username, service, MAX(datetime) AS lastdate
+      FROM sasl_log
+      GROUP BY username, service
+    ) AS last ON sasl_log.username = last.username AND sasl_log.service = last.service
+    WHERE datetime < DATE_SUB(NOW(), INTERVAL 31 DAY) AND datetime < lastdate;
+  DELETE FROM sasl_log
+    WHERE username NOT IN (SELECT username FROM mailbox) AND
+    datetime < DATE_SUB(NOW(), INTERVAL 31 DAY);
+END;
+//
+DELIMITER ;
 EOF
 EOF
 fi
 fi
 
 

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

@@ -24,6 +24,11 @@ mail_plugins = </etc/dovecot/mail_plugins
 mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
 mail_attachment_fs = crypt:set_prefix=mail_crypt_global:posix:
 mail_attachment_dir = /var/attachments
 mail_attachment_dir = /var/attachments
 mail_attachment_min_size = 128k
 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
 # Dovecot 2.2
 #ssl_protocols = !SSLv3
 #ssl_protocols = !SSLv3

+ 2 - 1
data/conf/rspamd/custom/bad_asn.map

@@ -27,4 +27,5 @@
 #197518 2 #Rackmarkt SL, Spain
 #197518 2 #Rackmarkt SL, Spain
 #197695 2 #Domain names registrar REG.RU Ltd, Russia
 #197695 2 #Domain names registrar REG.RU Ltd, Russia
 #198068 2 #P.A.G.M. OU, Estonia
 #198068 2 #P.A.G.M. OU, Estonia
-#201942 5 #Soltia Consulting SL, Spain
+#201942 5 #Soltia Consulting SL, Spain
+#213373 4 #IP Connect Inc

+ 3 - 1
data/web/inc/functions.address_rewriting.inc.php

@@ -49,7 +49,9 @@ function bcc($_action, $_data = null, $_attr = null) {
       }
       }
       elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
       elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
         $mailbox = mailbox('get', 'mailbox_details', $local_dest);
         $mailbox = mailbox('get', 'mailbox_details', $local_dest);
-        if ($mailbox === false && array_key_exists($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
+        $shared_aliases = mailbox('get', 'shared_aliases');
+        $direct_aliases = mailbox('get', 'direct_aliases');
+        if ($mailbox === false && in_array($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
           $_SESSION['return'][] = array(
           $_SESSION['return'][] = array(
             'type' => 'danger',
             'type' => 'danger',
             'log' => array(__FUNCTION__, $_action, $_data, $_attr),
             'log' => array(__FUNCTION__, $_action, $_data, $_attr),

+ 36 - 2
data/web/inc/functions.mailbox.inc.php

@@ -3965,6 +3965,39 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
           }
           }
           return $aliasdomaindata;
           return $aliasdomaindata;
         break;
         break;
+        case 'shared_aliases':
+          $shared_aliases = array();
+          $stmt = $pdo->query("SELECT `address` FROM `alias`
+            WHERE `goto` REGEXP ','
+            AND `address` NOT LIKE '@%'
+            AND `goto` != `address`");
+          $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+          while($row = array_shift($rows)) {
+            $domain = explode("@", $row['address'])[1];
+            if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $shared_aliases[] = $row['address'];
+            }
+          }
+
+          return $shared_aliases;
+        break;
+        case 'direct_aliases':
+          $direct_aliases = array();
+          $stmt = $pdo->query("SELECT `address` FROM `alias`
+            WHERE `goto` NOT LIKE '%,%'
+            AND `address` NOT LIKE '@%'
+            AND `goto` != `address`");
+          $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+          while($row = array_shift($rows)) {
+            $domain = explode("@", $row['address'])[1];
+            if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $direct_aliases[] = $row['address'];
+            }
+          }
+
+          return $direct_aliases;
+        break;
         case 'domains':
         case 'domains':
           $domains = array();
           $domains = array();
           if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
           if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
@@ -4956,9 +4989,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             $stmt->execute(array(
             $stmt->execute(array(
               ':username' => $username
               ':username' => $username
             ));
             ));
-            $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+            $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as OR `send_as` = :send_as");
             $stmt->execute(array(
             $stmt->execute(array(
-              ':username' => $username
+              ':logged_in_as' => $username,
+              ':send_as' => $username
             ));
             ));
             // fk, better safe than sorry
             // fk, better safe than sorry
             $stmt = $pdo->prepare("DELETE FROM `user_acl` WHERE `username` = :username");
             $stmt = $pdo->prepare("DELETE FROM `user_acl` WHERE `username` = :username");

+ 1 - 1
data/web/inc/triggers.inc.php

@@ -63,7 +63,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
         unset($_SESSION['index_query_string']);
         unset($_SESSION['index_query_string']);
         if (in_array('mobileconfig', $http_parameters)) {
         if (in_array('mobileconfig', $http_parameters)) {
             if (in_array('only_email', $http_parameters)) {
             if (in_array('only_email', $http_parameters)) {
-                header("Location: /mobileconfig.php?email_only");
+                header("Location: /mobileconfig.php?only_email");
                 die();
                 die();
             }
             }
             header("Location: /mobileconfig.php");
             header("Location: /mobileconfig.php");

+ 10 - 0
data/web/js/build/013-mailcow.js

@@ -1,3 +1,13 @@
+const LOCALE = undefined;
+const DATETIME_FORMAT = {
+  year: "numeric",
+  month: "2-digit",
+  day: "2-digit",
+  hour: "2-digit",
+  minute: "2-digit",
+  second: "2-digit"
+};
+
 $(document).ready(function() {
 $(document).ready(function() {
   // mailcow alert box generator
   // mailcow alert box generator
   window.mailcow_alert_box = function(message, type) {
   window.mailcow_alert_box = function(message, type) {

+ 14 - 16
data/web/js/site/debug.js

@@ -1,13 +1,3 @@
-const LOCALE = undefined;
-const DATETIME_FORMAT = {
-  year: "numeric",
-  month: "2-digit",
-  day: "2-digit",
-  hour: "2-digit",
-  minute: "2-digit",
-  second: "2-digit"
-};
-
 $(document).ready(function() {
 $(document).ready(function() {
   // Parse seconds ago to date
   // Parse seconds ago to date
   // Get "now" timestamp
   // Get "now" timestamp
@@ -43,7 +33,7 @@ $(document).ready(function() {
   if (mailcow_info.branch === "master"){
   if (mailcow_info.branch === "master"){
     check_update(mailcow_info.version_tag, mailcow_info.project_url);
     check_update(mailcow_info.version_tag, mailcow_info.project_url);
   }
   }
-  $("#maiclow_version").click(function(){
+  $("#mailcow_version").click(function(){
     if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
     if (mailcow_cc_role !== "admin" && mailcow_cc_role !== "domainadmin" || mailcow_info.branch !== "master")
       return;
       return;
 
 
@@ -829,13 +819,10 @@ jQuery(function($){
       url: '/api/v1/get/rspamd/actions',
       url: '/api/v1/get/rspamd/actions',
       async: true,
       async: true,
       success: function(data){
       success: function(data){
-        console.log(data);
-
         var total = 0;
         var total = 0;
         $(data).map(function(){total += this[1];});
         $(data).map(function(){total += this[1];});
         var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
         var labels = $.makeArray($(data).map(function(){return this[0] + ' ' + Math.round(this[1]/total * 100) + '%';}));
         var values = $.makeArray($(data).map(function(){return this[1];}));
         var values = $.makeArray($(data).map(function(){return this[1];}));
-        console.log(values);
 
 
         var graphdata = {
         var graphdata = {
           labels: labels,
           labels: labels,
@@ -951,12 +938,15 @@ jQuery(function($){
           title: 'Score',
           title: 'Score',
           data: 'score',
           data: 'score',
           defaultContent: '',
           defaultContent: '',
+          class: 'text-nowrap',
           createdCell: function(td, cellData) {
           createdCell: function(td, cellData) {
             $(td).attr({
             $(td).attr({
               "data-order": cellData.sortBy,
               "data-order": cellData.sortBy,
               "data-sort": cellData.sortBy
               "data-sort": cellData.sortBy
             });
             });
-            $(td).html(cellData.value);
+          },    
+          render: function (data) {
+            return data.value;
           }
           }
         },
         },
         {
         {
@@ -979,7 +969,9 @@ jQuery(function($){
               "data-order": cellData.sortBy,
               "data-order": cellData.sortBy,
               "data-sort": cellData.sortBy
               "data-sort": cellData.sortBy
             });
             });
-            $(td).html(cellData.value);
+          },    
+          render: function (data) {
+            return data.value;
           }
           }
         },
         },
         {
         {
@@ -1302,6 +1294,12 @@ function update_stats(timeout=5){
       $("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
       $("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
       $("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
       $("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
       $("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
       $("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
+      if (data.architecture == "aarch64"){
+        $("#host_architecture").html('<span data-bs-toggle="tooltip" data-bs-placement="top" title="' + lang_debug.wip +'">' + data.architecture + ' ⚠️</span>');
+      }
+      else {
+        $("#host_architecture").html(data.architecture);
+      }
 
 
       // update cpu and mem chart
       // update cpu and mem chart
       var cpu_chart = Chart.getChart("host_cpu_chart");
       var cpu_chart = Chart.getChart("host_cpu_chart");

+ 33 - 23
data/web/js/site/mailbox.js

@@ -1458,30 +1458,37 @@ jQuery(function($){
   }
   }
   function draw_bcc_table() {
   function draw_bcc_table() {
     $.get("/api/v1/get/bcc-destination-options", function(data){
     $.get("/api/v1/get/bcc-destination-options", function(data){
+      var optgroup = "";
       // Domains
       // Domains
-      var optgroup = "<optgroup label='" + lang.domains + "'>";
-      $.each(data.domains, function(index, domain){
-        optgroup += "<option value='" + domain + "'>" + domain + "</option>";
-      });
-      optgroup += "</optgroup>";
-      $('#bcc-local-dest').append(optgroup);
-      // Alias domains
-      var optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
-      $.each(data.alias_domains, function(index, alias_domain){
-        optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>";
-      });
-      optgroup += "</optgroup>"
-      $('#bcc-local-dest').append(optgroup);
-      // Mailboxes and aliases
-      $.each(data.mailboxes, function(mailbox, aliases){
-        var optgroup = "<optgroup label='" + mailbox + "'>";
-        $.each(aliases, function(index, alias){
-          optgroup += "<option value='" + alias + "'>" + alias + "</option>";
+      if (data.domains && data.domains.length > 0) {
+        optgroup = "<optgroup label='" + lang.domains + "'>";
+        $.each(data.domains, function(index, domain){
+          optgroup += "<option value='" + domain + "'>" + domain + "</option>";
         });
         });
         optgroup += "</optgroup>";
         optgroup += "</optgroup>";
         $('#bcc-local-dest').append(optgroup);
         $('#bcc-local-dest').append(optgroup);
-      });
-      // Finish
+      }
+      // Alias domains
+      if (data.alias_domains && data.alias_domains.length > 0) {
+        optgroup = "<optgroup label='" + lang.domain_aliases + "'>";
+        $.each(data.alias_domains, function(index, alias_domain){
+          optgroup += "<option value='" + alias_domain + "'>" + alias_domain + "</option>";
+        });
+        optgroup += "</optgroup>"
+        $('#bcc-local-dest').append(optgroup);
+      }
+      // Mailboxes and aliases
+      if (data.mailboxes && Object.keys(data.mailboxes).length > 0) {
+        $.each(data.mailboxes, function(mailbox, aliases){
+          optgroup = "<optgroup label='" + mailbox + "'>";
+          $.each(aliases, function(index, alias){
+            optgroup += "<option value='" + alias + "'>" + alias + "</option>";
+          });
+          optgroup += "</optgroup>";
+          $('#bcc-local-dest').append(optgroup);
+        });
+      }
+      // Recreate picker
       $('#bcc-local-dest').selectpicker('refresh');
       $('#bcc-local-dest').selectpicker('refresh');
     });
     });
 
 
@@ -2326,16 +2333,19 @@ jQuery(function($){
   // detect element visibility changes
   // detect element visibility changes
   function onVisible(element, callback) {
   function onVisible(element, callback) {
     $(document).ready(function() {
     $(document).ready(function() {
-      element_object = document.querySelector(element);
+      let element_object = document.querySelector(element);
       if (element_object === null) return;
       if (element_object === null) return;
 
 
-      new IntersectionObserver((entries, observer) => {
+      let observer = new IntersectionObserver((entries, observer) => {
         entries.forEach(entry => {
         entries.forEach(entry => {
           if(entry.intersectionRatio > 0) {
           if(entry.intersectionRatio > 0) {
             callback(element_object);
             callback(element_object);
+            observer.unobserve(element_object);
           }
           }
         });
         });
-      }).observe(element_object);
+      })
+      
+      observer.observe(element_object);
     });
     });
   }
   }
 
 

+ 19 - 6
data/web/js/site/user.js

@@ -127,6 +127,20 @@ jQuery(function($){
     }
     }
   }
   }
 
 
+  
+  function createSortableDate(td, cellData, date_string = false) {
+    if (date_string)
+      var date = new Date(cellData);
+    else
+      var date = new Date(cellData ? cellData * 1000 : 0);
+
+    var timestamp = date.getTime();
+    $(td).attr({
+      "data-order": timestamp,
+      "data-sort": timestamp
+    });
+    $(td).html(date.toLocaleDateString(LOCALE, DATETIME_FORMAT));
+  }
   function draw_tla_table() {
   function draw_tla_table() {
     // just recalc width if instance already exists
     // just recalc width if instance already exists
     if ($.fn.DataTable.isDataTable('#tla_table') ) {
     if ($.fn.DataTable.isDataTable('#tla_table') ) {
@@ -144,6 +158,7 @@ jQuery(function($){
            "tr" +
            "tr" +
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
            "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
       language: lang_datatables,
       language: lang_datatables,
+      order: [[4, 'desc']],
       ajax: {
       ajax: {
         type: "GET",
         type: "GET",
         url: "/api/v1/get/time_limited_aliases",
         url: "/api/v1/get/time_limited_aliases",
@@ -191,18 +206,16 @@ jQuery(function($){
           title: lang.alias_valid_until,
           title: lang.alias_valid_until,
           data: 'validity',
           data: 'validity',
           defaultContent: '',
           defaultContent: '',
-          render: function (data, type) {
-            var date = new Date(data ? data * 1000 : 0);
-            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+          createdCell: function(td, cellData) {
+            createSortableDate(td, cellData)
           }
           }
         },
         },
         {
         {
           title: lang.created_on,
           title: lang.created_on,
           data: 'created',
           data: 'created',
           defaultContent: '',
           defaultContent: '',
-          render: function (data, type) {
-            var date = new Date(data.replace(/-/g, "/"));
-            return date.toLocaleDateString(undefined, {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
+          createdCell: function(td, cellData) {
+            createSortableDate(td, cellData, true)
           }
           }
         },
         },
         {
         {

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

@@ -541,7 +541,7 @@
         "inactive": "Neaktivní",
         "inactive": "Neaktivní",
         "kind": "Druh",
         "kind": "Druh",
         "last_modified": "Naposledy změněn",
         "last_modified": "Naposledy změněn",
-        "lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
+        "lookup_mx": "Cíl je regulární výraz který se shoduje s MX záznamem (<code>.*\\.google\\.com</code> směřuje veškerou poštu na MX které jsou cílem pro google.com přes tento skok)",
         "mailbox": "Úprava mailové schránky",
         "mailbox": "Úprava mailové schránky",
         "mailbox_quota_def": "Výchozí kvóta schránky",
         "mailbox_quota_def": "Výchozí kvóta schránky",
         "mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",
         "mailbox_relayhost_info": "Aplikované jen na uživatelskou schránku a přímé aliasy, přepisuje předávající server domény.",

+ 5 - 3
data/web/lang/lang.de-de.json

@@ -216,7 +216,7 @@
         "loading": "Bitte warten...",
         "loading": "Bitte warten...",
         "login_time": "Zeit",
         "login_time": "Zeit",
         "logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
         "logo_info": "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.",
-        "lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
+        "lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
         "main_name": "\"mailcow UI\" Name",
         "main_name": "\"mailcow UI\" Name",
         "merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
         "merged_vars_hint": "Ausgegraute Reihen wurden aus der Datei <code>vars.(local.)inc.php</code> gelesen und können hier nicht verändert werden.",
         "message": "Nachricht",
         "message": "Nachricht",
@@ -498,6 +498,7 @@
         }
         }
     },
     },
     "debug": {
     "debug": {
+        "architecture": "Architektur",
         "chart_this_server": "Chart (dieser Server)",
         "chart_this_server": "Chart (dieser Server)",
         "containers_info": "Container-Information",
         "containers_info": "Container-Information",
         "container_running": "Läuft",
         "container_running": "Läuft",
@@ -534,7 +535,8 @@
         "update_available": "Es ist ein Update verfügbar",
         "update_available": "Es ist ein Update verfügbar",
         "no_update_available": "Das System ist auf aktuellem Stand",
         "no_update_available": "Das System ist auf aktuellem Stand",
         "update_failed": "Es konnte nicht nach einem Update gesucht werden",
         "update_failed": "Es konnte nicht nach einem Update gesucht werden",
-        "username": "Benutzername"
+        "username": "Benutzername",
+        "wip": "Aktuell noch in Arbeit"
     },
     },
     "diagnostics": {
     "diagnostics": {
         "cname_from_a": "Wert abgeleitet von A/AAAA-Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.",
         "cname_from_a": "Wert abgeleitet von A/AAAA-Eintrag. Wird unterstützt, sofern der Eintrag auf die korrekte Ressource zeigt.",
@@ -593,7 +595,7 @@
         "inactive": "Inaktiv",
         "inactive": "Inaktiv",
         "kind": "Art",
         "kind": "Art",
         "last_modified": "Zuletzt geändert",
         "last_modified": "Zuletzt geändert",
-        "lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
+        "lookup_mx": "Ziel mit MX vergleichen (Regex, etwa <code>.*\\.google\\.com</code>, um alle Ziele mit MX *google.com zu routen)",
         "mailbox": "Mailbox bearbeiten",
         "mailbox": "Mailbox bearbeiten",
         "mailbox_quota_def": "Standard-Quota einer Mailbox",
         "mailbox_quota_def": "Standard-Quota einer Mailbox",
         "mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
         "mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",

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

@@ -218,7 +218,7 @@
         "loading": "Please wait...",
         "loading": "Please wait...",
         "login_time": "Login time",
         "login_time": "Login time",
         "logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
         "logo_info": "Your image will be scaled to a height of 40px for the top navigation bar and a max. width of 250px for the start page. A scalable graphic is highly recommended.",
-        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
+        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
         "main_name": "\"mailcow UI\" name",
         "main_name": "\"mailcow UI\" name",
         "merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
         "merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
         "message": "Message",
         "message": "Message",
@@ -498,6 +498,7 @@
         }
         }
     },
     },
     "debug": {
     "debug": {
+        "architecture": "Architecture",
         "chart_this_server": "Chart (this server)",
         "chart_this_server": "Chart (this server)",
         "containers_info": "Container information",
         "containers_info": "Container information",
         "container_running": "Running",
         "container_running": "Running",
@@ -534,7 +535,8 @@
         "update_available": "There is an update available",
         "update_available": "There is an update available",
         "no_update_available": "The System is on the latest version",
         "no_update_available": "The System is on the latest version",
         "update_failed": "Could not check for an Update",
         "update_failed": "Could not check for an Update",
-        "username": "Username"
+        "username": "Username",
+        "wip": "Currently Work in Progress"
     },
     },
     "diagnostics": {
     "diagnostics": {
         "cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
         "cname_from_a": "Value derived from A/AAAA record. This is supported as long as the record points to the correct resource.",
@@ -593,7 +595,7 @@
         "inactive": "Inactive",
         "inactive": "Inactive",
         "kind": "Kind",
         "kind": "Kind",
         "last_modified": "Last modified",
         "last_modified": "Last modified",
-        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
+        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
         "mailbox": "Edit mailbox",
         "mailbox": "Edit mailbox",
         "mailbox_quota_def": "Default mailbox quota",
         "mailbox_quota_def": "Default mailbox quota",
         "mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
         "mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",

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

@@ -588,7 +588,7 @@
         "unchanged_if_empty": "Si non modifié, laisser en blanc",
         "unchanged_if_empty": "Si non modifié, laisser en blanc",
         "username": "Nom d'utilisateur",
         "username": "Nom d'utilisateur",
         "validate_save": "Valider et sauver",
         "validate_save": "Valider et sauver",
-        "lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
+        "lookup_mx": "La destination est une expression régulière qui doit correspondre avec le nom du MX (<code>.*\\.google\\.com</code> pour acheminer tout le courrier destiné à un MX se terminant par google.com via ce saut)",
         "mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
         "mailbox_relayhost_info": "S'applique uniquement à la boîte aux lettres et aux alias directs, remplace le relayhost du domaine."
     },
     },
     "footer": {
     "footer": {

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

@@ -213,7 +213,7 @@
         "loading": "Caricamento in corso...",
         "loading": "Caricamento in corso...",
         "login_time": "Ora di accesso",
         "login_time": "Ora di accesso",
         "logo_info": "La tua immagine verrà ridimensionata a 40px di altezza, quando verrà usata nella barra di navigazione in alto, ed ad una larghezza massima di 250px nella schermata iniziale. È altamente consigliato l'utilizzo di un'immagine modulabile.",
         "logo_info": "La tua immagine verrà ridimensionata a 40px di altezza, quando verrà usata nella barra di navigazione in alto, ed ad una larghezza massima di 250px nella schermata iniziale. È altamente consigliato l'utilizzo di un'immagine modulabile.",
-        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
+        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
         "main_name": "Nome \"mailcow UI\"",
         "main_name": "Nome \"mailcow UI\"",
         "merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
         "merged_vars_hint": "Greyed out rows were merged from <code>vars.(local.)inc.php</code> and cannot be modified.",
         "message": "Messaggio",
         "message": "Messaggio",
@@ -554,7 +554,7 @@
         "hostname": "Hostname",
         "hostname": "Hostname",
         "inactive": "Inattivo",
         "inactive": "Inattivo",
         "kind": "Genere",
         "kind": "Genere",
-        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
+        "lookup_mx": "Destination is a regular expression to match against MX name (<code>.*\\.google\\.com</code> to route all mail targeted to a MX ending in google.com over this hop)",
         "mailbox": "Modifica casella di posta",
         "mailbox": "Modifica casella di posta",
         "mailbox_quota_def": "Default mailbox quota",
         "mailbox_quota_def": "Default mailbox quota",
         "mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
         "mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",

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

@@ -539,7 +539,7 @@
         "inactive": "Inactiv",
         "inactive": "Inactiv",
         "kind": "Fel",
         "kind": "Fel",
         "last_modified": "Ultima modificare",
         "last_modified": "Ultima modificare",
-        "lookup_mx": "Destinația este o expresie regulată care potrivită cu numele MX (<code>.*google\\.com</code> pentru a direcționa toate e-mailurile vizate către un MX care se termină în google.com peste acest hop)",
+        "lookup_mx": "Destinația este o expresie regulată care potrivită cu numele MX (<code>.*\\.google\\.com</code> pentru a direcționa toate e-mailurile vizate către un MX care se termină în google.com peste acest hop)",
         "mailbox": "Editează căsuța poștală",
         "mailbox": "Editează căsuța poștală",
         "mailbox_quota_def": "Cota implicită a căsuței poștale",
         "mailbox_quota_def": "Cota implicită a căsuței poștale",
         "mailbox_relayhost_info": "Aplicat numai căsuței poștale și aliasurilor directe, suprascrie un transport dependent de domeniu.",
         "mailbox_relayhost_info": "Aplicat numai căsuței poștale și aliasurilor directe, suprascrie un transport dependent de domeniu.",

+ 3 - 1
data/web/lang/lang.ru-ru.json

@@ -336,7 +336,9 @@
         "validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
         "validate_license_now": "Получить лицензию на основе GUID с сервера лицензий",
         "verify": "Проверить",
         "verify": "Проверить",
         "yes": "&#10003;",
         "yes": "&#10003;",
-        "queue_unban": "разблокировать"
+        "queue_unban": "разблокировать",
+        "f2b_ban_time_increment": "Время бана увеличивается с каждым баном",
+        "f2b_max_ban_time": "Максимальное время блокировки"
     },
     },
     "danger": {
     "danger": {
         "access_denied": "Доступ запрещён, или указаны неверные данные",
         "access_denied": "Доступ запрещён, или указаны неверные данные",

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

@@ -213,7 +213,7 @@
         "loading": "Čakajte prosím ...",
         "loading": "Čakajte prosím ...",
         "login_time": "Čas prihlásenia",
         "login_time": "Čas prihlásenia",
         "logo_info": "Váš obrázok bude upravený na výšku 40px pre vrchný navigačný riadok a na maximálnu šírku 250px pre úvodnú stránku. Odporúča sa škálovateľná grafika.",
         "logo_info": "Váš obrázok bude upravený na výšku 40px pre vrchný navigačný riadok a na maximálnu šírku 250px pre úvodnú stránku. Odporúča sa škálovateľná grafika.",
-        "lookup_mx": "Cieľ je regulárny výraz ktorý sa porovnáva s MX záznamom (<code>.*google\\.com</code> smeruje všetku poštu určenú pre MX ktoré sú cieľom pre google.com cez tento skok)",
+        "lookup_mx": "Cieľ je regulárny výraz ktorý sa porovnáva s MX záznamom (<code>.*\\.google\\.com</code> smeruje všetku poštu určenú pre MX ktoré sú cieľom pre google.com cez tento skok)",
         "main_name": "\"mailcow UI\" názov",
         "main_name": "\"mailcow UI\" názov",
         "merged_vars_hint": "Sivé riadky boli načítané z <code>vars.(local.)inc.php</code> a nemôžu byť modifikované cez UI.",
         "merged_vars_hint": "Sivé riadky boli načítané z <code>vars.(local.)inc.php</code> a nemôžu byť modifikované cez UI.",
         "message": "Správa",
         "message": "Správa",
@@ -539,7 +539,7 @@
         "inactive": "Neaktívny",
         "inactive": "Neaktívny",
         "kind": "Druh",
         "kind": "Druh",
         "last_modified": "Naposledy upravené",
         "last_modified": "Naposledy upravené",
-        "lookup_mx": "Cieľ je regulárny výraz ktorý sa zhoduje s MX záznamom (<code>.*google\\.com</code> smeruje všetku poštu na MX ktoré sú cieľom pre google.com cez tento skok)",
+        "lookup_mx": "Cieľ je regulárny výraz ktorý sa zhoduje s MX záznamom (<code>.*\\.google\\.com</code> smeruje všetku poštu na MX ktoré sú cieľom pre google.com cez tento skok)",
         "mailbox": "Upraviť mailovú schránku",
         "mailbox": "Upraviť mailovú schránku",
         "mailbox_quota_def": "Predvolená veľkosť mailovej schránky",
         "mailbox_quota_def": "Predvolená veľkosť mailovej schránky",
         "mailbox_relayhost_info": "Aplikované len na používateľské schránky a priame aliasy, prepisuje doménového preposielateľa.",
         "mailbox_relayhost_info": "Aplikované len na používateľské schránky a priame aliasy, prepisuje doménového preposielateľa.",

+ 2 - 2
data/web/lang/lang.zh-cn.json

@@ -213,7 +213,7 @@
         "loading": "请等待...",
         "loading": "请等待...",
         "login_time": "登录时间",
         "login_time": "登录时间",
         "logo_info": "你的图片将会在顶部导航栏被缩放为 40px 高,在起始页被缩放为最大 250px 高。强烈推荐使用能较好适应缩放的图片。",
         "logo_info": "你的图片将会在顶部导航栏被缩放为 40px 高,在起始页被缩放为最大 250px 高。强烈推荐使用能较好适应缩放的图片。",
-        "lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
+        "lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*\\.google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
         "main_name": "Mailcow UI 的名称",
         "main_name": "Mailcow UI 的名称",
         "merged_vars_hint": "灰色行来自 <code>vars.(local.)inc.php</code> 文件并且无法修改。",
         "merged_vars_hint": "灰色行来自 <code>vars.(local.)inc.php</code> 文件并且无法修改。",
         "message": "消息",
         "message": "消息",
@@ -544,7 +544,7 @@
         "hostname": "主机名",
         "hostname": "主机名",
         "inactive": "禁用",
         "inactive": "禁用",
         "kind": "类型",
         "kind": "类型",
-        "lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
+        "lookup_mx": "应当为一个正则表达式,用于匹配 MX 记录 (例如 <code>.*\\.google\\.com</code> 将转发所有拥有以 google.com 结尾的 MX 记录的邮件)",
         "mailbox": "编辑邮箱",
         "mailbox": "编辑邮箱",
         "mailbox_quota_def": "邮箱默认配额",
         "mailbox_quota_def": "邮箱默认配额",
         "mailbox_relayhost_info": "只适用于邮箱和邮箱别名,不会覆盖域名的中继主机。",
         "mailbox_relayhost_info": "只适用于邮箱和邮箱别名,不会覆盖域名的中继主机。",

+ 2 - 2
data/web/lang/lang.zh-tw.json

@@ -213,7 +213,7 @@
         "loading": "請稍等...",
         "loading": "請稍等...",
         "login_time": "登入時間",
         "login_time": "登入時間",
         "logo_info": "你的起始頁面圖片會在頂部導覽列的限制下被縮放為 40px 高,以及最大 250px 高度。強烈推薦使用能較好縮放的圖片。",
         "logo_info": "你的起始頁面圖片會在頂部導覽列的限制下被縮放為 40px 高,以及最大 250px 高度。強烈推薦使用能較好縮放的圖片。",
-        "lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
+        "lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*\\.google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
         "main_name": "\"mailcow UI\" 名稱",
         "main_name": "\"mailcow UI\" 名稱",
         "merged_vars_hint": "灰色列來自 <code>vars.(local.)inc.php</code> 並且不能修改。",
         "merged_vars_hint": "灰色列來自 <code>vars.(local.)inc.php</code> 並且不能修改。",
         "message": "訊息",
         "message": "訊息",
@@ -540,7 +540,7 @@
         "inactive": "停用",
         "inactive": "停用",
         "kind": "種類",
         "kind": "種類",
         "last_modified": "上次修改時間",
         "last_modified": "上次修改時間",
-        "lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
+        "lookup_mx": "目的地是可以用來匹配 MX 紀錄的正規表達式 (<code>.*\\.google\\.com</code> 會將所有 MX 結尾於 google.com 的郵件轉發到此主機。)",
         "mailbox": "編輯信箱",
         "mailbox": "編輯信箱",
         "mailbox_quota_def": "預設信箱容量配額",
         "mailbox_quota_def": "預設信箱容量配額",
         "mailbox_relayhost_info": "只會套用於信箱和直接別名,不會覆寫域名中繼主機。",
         "mailbox_relayhost_info": "只會套用於信箱和直接別名,不會覆寫域名中繼主機。",

+ 7 - 1
data/web/templates/debug.twig

@@ -49,6 +49,12 @@
                           <p><b>{{ hostname }}</b></p>
                           <p><b>{{ hostname }}</b></p>
                         </div></td>
                         </div></td>
                       </tr>
                       </tr>
+                      <tr>
+                        <td>{{ lang.debug.architecture }}</td>
+                        <td class="text-break"><div>
+                          <p id="host_architecture">-</p>
+                        </div></td>
+                      </tr>
                       <tr>
                       <tr>
                         <td>IPs</td>
                         <td>IPs</td>
                         <td class="text-break">
                         <td class="text-break">
@@ -70,7 +76,7 @@
                         <td>Version</td>
                         <td>Version</td>
                         <td class="text-break">
                         <td class="text-break">
                           <div class="fw-bolder">
                           <div class="fw-bolder">
-                            <p ><a href="#" id="maiclow_version">{{ mailcow_info.version_tag }}</a></p>
+                            <p ><a href="#" id="mailcow_version">{{ mailcow_info.version_tag }}</a></p>
                             <p id="mailcow_update"></p>
                             <p id="mailcow_update"></p>
                           </div>
                           </div>
                         </td>
                         </td>

+ 15 - 5
data/web/templates/user.twig

@@ -12,11 +12,21 @@
         <li><button class="dropdown-item" role="tab" aria-selected="false" aria-controls="tab-config-f2b" data-bs-toggle="tab" data-bs-target="#tab-user-settings">{{ lang.user.mailbox_settings }}</button></li>
         <li><button class="dropdown-item" role="tab" aria-selected="false" aria-controls="tab-config-f2b" data-bs-toggle="tab" data-bs-target="#tab-user-settings">{{ lang.user.mailbox_settings }}</button></li>
       </ul>
       </ul>
     </li>
     </li>
+    {% if acl.spam_alias == 1 %}
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="SpamAliases" role="tab" data-bs-toggle="tab" data-bs-target="#SpamAliases">{{ lang.user.spam_aliases }}</button></li>
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="SpamAliases" role="tab" data-bs-toggle="tab" data-bs-target="#SpamAliases">{{ lang.user.spam_aliases }}</button></li>
+    {% endif %}
+    {% if acl.spam_score == 1 %}
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Spamfilter" role="tab" data-bs-toggle="tab" data-bs-target="#Spamfilter">{{ lang.user.spamfilter }}</button></li>
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Spamfilter" role="tab" data-bs-toggle="tab" data-bs-target="#Spamfilter">{{ lang.user.spamfilter }}</button></li>
+    {% endif %}
+    {% if acl.syncjobs == 1 %}
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Syncjobs" role="tab" data-bs-toggle="tab" data-bs-target="#Syncjobs">{{ lang.user.sync_jobs }}</button></li>
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Syncjobs" role="tab" data-bs-toggle="tab" data-bs-target="#Syncjobs">{{ lang.user.sync_jobs }}</button></li>
+    {% endif %}
+    {% if acl.app_passwds == 1 %}
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="AppPasswds" role="tab" data-bs-toggle="tab" data-bs-target="#AppPasswds">{{ lang.user.app_passwds }}</button></li>
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="AppPasswds" role="tab" data-bs-toggle="tab" data-bs-target="#AppPasswds">{{ lang.user.app_passwds }}</button></li>
+    {% endif %}
+    {% if acl.pushover == 1 %}
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Pushover" role="tab" data-bs-toggle="tab" data-bs-target="#Pushover">Pushover API</button></li>
     <li class="nav-item" role="presentation"><button class="nav-link" role="tab" aria-selected="false" aria-controls="Pushover" role="tab" data-bs-toggle="tab" data-bs-target="#Pushover">Pushover API</button></li>
+    {% endif %}
   </ul>
   </ul>
 
 
   <div class="row">
   <div class="row">
@@ -25,11 +35,11 @@
         {% include 'user/tab-user-auth.twig' %}
         {% include 'user/tab-user-auth.twig' %}
         {% include 'user/tab-user-details.twig' %}
         {% include 'user/tab-user-details.twig' %}
         {% include 'user/tab-user-settings.twig' %}
         {% include 'user/tab-user-settings.twig' %}
-        {% include 'user/SpamAliases.twig' %}
-        {% include 'user/Spamfilter.twig' %}
-        {% include 'user/Syncjobs.twig' %}
-        {% include 'user/AppPasswds.twig' %}
-        {% include 'user/Pushover.twig' %}
+        {% if acl.spam_alias == 1 %}{% include 'user/SpamAliases.twig' %}{% endif %}
+        {% if acl.spam_score == 1 %}{% include 'user/Spamfilter.twig' %}{% endif %}
+        {% if acl.syncjobs == 1 %}{% include 'user/Syncjobs.twig' %}{% endif %}
+        {% if acl.app_passwds == 1 %}{% include 'user/AppPasswds.twig' %}{% endif %}
+        {% if acl.pushover == 1 %}{% include 'user/Pushover.twig' %}{% endif %}
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>

+ 2 - 2
docker-compose.yml

@@ -106,7 +106,7 @@ services:
             - rspamd
             - rspamd
 
 
     php-fpm-mailcow:
     php-fpm-mailcow:
-      image: mailcow/phpfpm:1.83
+      image: mailcow/phpfpm:1.84
       command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
       command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
       depends_on:
       depends_on:
         - redis-mailcow
         - redis-mailcow
@@ -510,7 +510,7 @@ services:
             - watchdog
             - watchdog
 
 
     dockerapi-mailcow:
     dockerapi-mailcow:
-      image: mailcow/dockerapi:2.03
+      image: mailcow/dockerapi:2.04
       security_opt:
       security_opt:
         - label=disable
         - label=disable
       restart: always
       restart: always

+ 1 - 1
helper-scripts/docker-compose.override.yml.d/EXTERNAL_MYSQL_SOCKET/docker-compose.override.yml

@@ -26,6 +26,6 @@ services:
         - /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
         - /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock
 
 
     mysql-mailcow:
     mysql-mailcow:
-      image: alpine:3.17
+      image: alpine:3.18
       command: /bin/true
       command: /bin/true
       restart: "no"
       restart: "no"

+ 10 - 3
helper-scripts/update_postscreen_whitelist.sh

@@ -6,7 +6,7 @@ SPFTOOLS_DIR=${WORKING_DIR}/spf-tools
 POSTWHITE_DIR=${WORKING_DIR}/postwhite
 POSTWHITE_DIR=${WORKING_DIR}/postwhite
 POSTWHITE_CONF=${POSTWHITE_DIR}/postwhite.conf
 POSTWHITE_CONF=${POSTWHITE_DIR}/postwhite.conf
 
 
-COSTOM_HOSTS="web.de gmx.net mail.de freenet.de arcor.de unity-mail.de"
+CUSTOM_HOSTS='"web.de gmx.net mail.de freenet.de arcor.de unity-mail.de"'
 STATIC_HOSTS=(
 STATIC_HOSTS=(
     "194.25.134.0/24 permit # t-online.de"
     "194.25.134.0/24 permit # t-online.de"
 )
 )
@@ -19,16 +19,23 @@ function set_config() {
     sudo sed -i "s@^\($1\s*=\s*\).*\$@\1$2@" ${POSTWHITE_CONF}
     sudo sed -i "s@^\($1\s*=\s*\).*\$@\1$2@" ${POSTWHITE_CONF}
 }
 }
 
 
-set_config custom_hosts ${COSTOM_HOSTS}
+set_config custom_hosts "${CUSTOM_HOSTS}"
 set_config reload_postfix no
 set_config reload_postfix no
 set_config postfixpath /.
 set_config postfixpath /.
 set_config spftoolspath ${WORKING_DIR}/spf-tools
 set_config spftoolspath ${WORKING_DIR}/spf-tools
 set_config whitelist .${SCRIPT_DIR}/../data/conf/postfix/postscreen_access.cidr
 set_config whitelist .${SCRIPT_DIR}/../data/conf/postfix/postscreen_access.cidr
 set_config yahoo_static_hosts ${POSTWHITE_DIR}/yahoo_static_hosts.txt
 set_config yahoo_static_hosts ${POSTWHITE_DIR}/yahoo_static_hosts.txt
 
 
+#Fix URL for Yahoo!: https://github.com/stevejenkins/postwhite/issues/59
+sudo sed -i \
+      -e 's#yahoo_url="https://help.yahoo.com/kb/SLN23997.html"#yahoo_url="https://senders.yahooinc.com/outbound-mail-servers/"#' \
+      -e 's#echo "ipv6:$line";#echo "ipv6:$line" | grep -v "ipv6:::";#' \
+      -e 's#`command -v wget`#`command -v skip-wget`#' \
+      ${POSTWHITE_DIR}/scrape_yahoo
+
 cd ${POSTWHITE_DIR}
 cd ${POSTWHITE_DIR}
 ./postwhite ${POSTWHITE_CONF}
 ./postwhite ${POSTWHITE_CONF}
 
 
 ( IFS=$'\n'; echo "${STATIC_HOSTS[*]}" >> "${SCRIPT_DIR}/../data/conf/postfix/postscreen_access.cidr")
 ( IFS=$'\n'; echo "${STATIC_HOSTS[*]}" >> "${SCRIPT_DIR}/../data/conf/postfix/postscreen_access.cidr")
 
 
-rm -r ${WORKING_DIR}
+rm -r ${WORKING_DIR}

+ 5 - 5
update.sh

@@ -188,7 +188,7 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
         echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
         echo -e "\e[33mNotice: You'll have to update this Compose Version via your Package Manager manually!\e[0m"
       else
       else
         echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
         echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
-        echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
+        echo -e "\e[31mPlease update/install it manually regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
         exit 1
         exit 1
       fi
       fi
   elif docker-compose > /dev/null 2>&1; then
   elif docker-compose > /dev/null 2>&1; then
@@ -203,14 +203,14 @@ if ! [[ "${DOCKER_COMPOSE_VERSION}" =~ ^(native|standalone)$ ]]; then
         echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
         echo -e "\e[33mNotice: For an automatic update of docker-compose please use the update_compose.sh scripts located at the helper-scripts folder.\e[0m"
       else
       else
         echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
         echo -e "\e[31mCannot find Docker Compose with a Version Higher than 2.X.X.\e[0m" 
-        echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
+        echo -e "\e[31mPlease update/install regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
         exit 1
         exit 1
       fi
       fi
     fi
     fi
 
 
   else
   else
     echo -e "\e[31mCannot find Docker Compose.\e[0m" 
     echo -e "\e[31mCannot find Docker Compose.\e[0m" 
-    echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
+    echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
     exit 1
     exit 1
   fi
   fi
 
 
@@ -223,7 +223,7 @@ elif [ "${DOCKER_COMPOSE_VERSION}" == "native" ]; then
     if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
     if ! $COMPOSE_COMMAND > /dev/null 2>&1 || ! $COMPOSE_COMMAND --version | grep "^2." > /dev/null 2>&1; then
       # IF it cannot find Standalone in > 2.X, then script stops
       # IF it cannot find Standalone in > 2.X, then script stops
       echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m" 
       echo -e "\e[31mCannot find Docker Compose or the Version is lower then 2.X.X.\e[0m" 
-      echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
+      echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
       exit 1
       exit 1
     fi
     fi
       # If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
       # If it finds the standalone Plugin it will use this instead and change the mailcow.conf Variable accordingly
@@ -243,7 +243,7 @@ elif [ "${DOCKER_COMPOSE_VERSION}" == "standalone" ]; then
     if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
     if ! $COMPOSE_COMMAND > /dev/null 2>&1; then
       # IF it cannot find Native in > 2.X, then script stops
       # IF it cannot find Native in > 2.X, then script stops
       echo -e "\e[31mCannot find Docker Compose.\e[0m" 
       echo -e "\e[31mCannot find Docker Compose.\e[0m" 
-      echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/mailcow-dockerized-docs/i_u_m/i_u_m_install/\e[0m"
+      echo -e "\e[31mPlease install it regarding to this doc site: https://docs.mailcow.email/i_u_m/i_u_m_install/\e[0m"
       exit 1
       exit 1
     fi
     fi
       # If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly
       # If it finds the native Plugin it will use this instead and change the mailcow.conf Variable accordingly