فهرست منبع

Merge pull request #5727 from mailcow/fix/domain-wide-footer

[Rspamd] apply domain wide footer to alias domains
Patrick Schult 1 سال پیش
والد
کامیت
7d3f9fa407

+ 23 - 2
data/conf/rspamd/dynmaps/footer.php

@@ -56,21 +56,42 @@ $empty_footer = json_encode(array(
 error_log("FOOTER: checking for domain " . $domain . ", user " . $username . " and address " . $from . PHP_EOL);
 
 try {
-  $stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude`, `skip_replies` FROM `domain_wide_footer` 
+  // try get $target_domain if $domain is an alias_domain
+  $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` 
+    WHERE `alias_domain` = :alias_domain");
+  $stmt->execute(array(
+    ':alias_domain' => $domain
+  ));
+  $alias_domain = $stmt->fetch(PDO::FETCH_ASSOC);
+  if (!$alias_domain) {
+    $target_domain = $domain;
+  } else {
+    $target_domain = $alias_domain['target_domain'];
+  }
+
+  // get footer associated with the domain
+  $stmt = $pdo->prepare("SELECT `plain`, `html`, `mbox_exclude`, `alias_domain_exclude`, `skip_replies` FROM `domain_wide_footer` 
     WHERE `domain` = :domain");
   $stmt->execute(array(
-    ':domain' => $domain
+    ':domain' => $target_domain
   ));
   $footer = $stmt->fetch(PDO::FETCH_ASSOC);
+
+  // check if the sender is excluded
   if (in_array($from, json_decode($footer['mbox_exclude']))){
     $footer = false;
   }
+  if (in_array($domain, json_decode($footer['alias_domain_exclude']))){
+    $footer = false;
+  }
   if (empty($footer)){
     echo $empty_footer;
     exit;
   }
   error_log("FOOTER: " . json_encode($footer) . PHP_EOL);
 
+  // footer will be applied
+  // get custom mailbox attributes to insert into the footer
   $stmt = $pdo->prepare("SELECT `custom_attributes` FROM `mailbox` WHERE `username` = :username");
   $stmt->execute(array(
     ':username' => $username

+ 2 - 1
data/web/edit.php

@@ -59,7 +59,8 @@ if (isset($_SESSION['mailcow_cc_role'])) {
             'domain_details' => $result,
             'domain_footer' => $domain_footer,
             'mailboxes' => mailbox('get', 'mailboxes', $_GET["domain"]),
-            'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address')
+            'aliases' => mailbox('get', 'aliases', $_GET["domain"], 'address'),
+            'alias_domains' => mailbox('get', 'alias_domains', $_GET["domain"])
           ];
       }
     }

+ 45 - 20
data/web/inc/functions.mailbox.inc.php

@@ -3438,30 +3438,54 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
           $footers['plain'] = isset($_data['plain']) ? $_data['plain'] : '';
           $footers['skip_replies'] = isset($_data['skip_replies']) ? (int)$_data['skip_replies'] : 0;
           $footers['mbox_exclude'] = array();
-          if (isset($_data["mbox_exclude"])){
-            if (!is_array($_data["mbox_exclude"])) {
-              $_data["mbox_exclude"] = array($_data["mbox_exclude"]);
-            }
-            foreach ($_data["mbox_exclude"] as $mailbox) {
-              if (!filter_var($mailbox, FILTER_VALIDATE_EMAIL)) {
-                $_SESSION['return'][] = array(
-                  'type' => 'danger',
-                  'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
-                  'msg' => array('username_invalid', $mailbox)
-                );
-                continue;
+          $footers['alias_domain_exclude'] = array();
+          if (isset($_data["exclude"])){
+            if (!is_array($_data["exclude"])) {
+              $_data["exclude"] = array($_data["exclude"]);
+            }
+            foreach ($_data["exclude"] as $exclude) {
+              if (filter_var($exclude, FILTER_VALIDATE_EMAIL)) {
+                $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `address` = :address
+                  UNION
+                  SELECT `username` FROM `mailbox` WHERE `username` = :username");
+                $stmt->execute(array(
+                  ':address' => $exclude,
+                  ':username' => $exclude,
+                ));
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                if(!$row){
+                  $_SESSION['return'][] = array(
+                    'type' => 'danger',
+                    'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                    'msg' => array('username_invalid', $exclude)
+                  );
+                  continue;
+                }
+                array_push($footers['mbox_exclude'], $exclude);
               }
-              $is_now = mailbox('get', 'mailbox_details', $mailbox);            
-              if(empty($is_now)){
+              elseif (is_valid_domain_name($exclude)) {
+                $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain` = :alias_domain");
+                $stmt->execute(array(
+                  ':alias_domain' => $exclude,
+                ));
+                $row = $stmt->fetch(PDO::FETCH_ASSOC);
+                if(!$row){
+                  $_SESSION['return'][] = array(
+                    'type' => 'danger',
+                    'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                    'msg' => array('username_invalid', $exclude)
+                  );
+                  continue;
+                }
+                array_push($footers['alias_domain_exclude'], $exclude);
+              }
+              else {
                 $_SESSION['return'][] = array(
                   'type' => 'danger',
                   'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
-                  'msg' => array('username_invalid', $mailbox)
+                  'msg' => array('username_invalid', $exclude)
                 );
-                continue;
               }
-              
-              array_push($footers['mbox_exclude'], $mailbox);
             }
           }
           foreach ($domains as $domain) {
@@ -3486,12 +3510,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             try {
               $stmt = $pdo->prepare("DELETE FROM `domain_wide_footer` WHERE `domain`= :domain");
               $stmt->execute(array(':domain' => $domain));
-              $stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`, `skip_replies`) VALUES (:domain, :html, :plain, :mbox_exclude, :skip_replies)");
+              $stmt = $pdo->prepare("INSERT INTO `domain_wide_footer` (`domain`, `html`, `plain`, `mbox_exclude`, `alias_domain_exclude`, `skip_replies`) VALUES (:domain, :html, :plain, :mbox_exclude, :alias_domain_exclude, :skip_replies)");
               $stmt->execute(array(
                 ':domain' => $domain,
                 ':html' => $footers['html'],
                 ':plain' => $footers['plain'],
                 ':mbox_exclude' => json_encode($footers['mbox_exclude']),
+                ':alias_domain_exclude' => json_encode($footers['alias_domain_exclude']),
                 ':skip_replies' => $footers['skip_replies'],
               ));
             }
@@ -4649,7 +4674,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
           }
 
           try {
-            $stmt = $pdo->prepare("SELECT `html`, `plain`, `mbox_exclude`, `skip_replies` FROM `domain_wide_footer`
+            $stmt = $pdo->prepare("SELECT `html`, `plain`, `mbox_exclude`, `alias_domain_exclude`, `skip_replies` FROM `domain_wide_footer`
               WHERE `domain` = :domain");
             $stmt->execute(array(
               ':domain' => $domain

+ 2 - 1
data/web/inc/init_db.inc.php

@@ -3,7 +3,7 @@ function init_db_schema() {
   try {
     global $pdo;
 
-    $db_version = "08012024_1442";
+    $db_version = "09022024_1433";
 
     $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
     $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -273,6 +273,7 @@ function init_db_schema() {
           "html" => "LONGTEXT",
           "plain" => "LONGTEXT",
           "mbox_exclude" => "JSON NOT NULL DEFAULT ('[]')",
+          "alias_domain_exclude" => "JSON NOT NULL DEFAULT ('[]')",
           "skip_replies" => "TINYINT(1) NOT NULL DEFAULT '0'"
         ),
         "keys" => array(

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

@@ -613,6 +613,7 @@
         "extended_sender_acl_info": "Der DKIM-Domainkey der externen Absenderdomain sollte in diesen Server importiert werden, falls vorhanden.<br>\r\n  Wird SPF verwendet, muss diesem Server der Versand gestattet werden.<br>\r\n  Wird eine Domain oder Alias-Domain zu diesem Server hinzugefügt, die sich mit der externen Absenderadresse überschneidet, wird der externe Absender hier entfernt.<br>\r\n  Ein Eintrag @domain.tld erlaubt den Versand als *@domain.tld",
         "force_pw_update": "Erzwinge Passwortänderung bei nächstem Login",
         "force_pw_update_info": "Dem Benutzer wird lediglich der Zugang zur %s ermöglicht, App Passwörter funktionieren weiterhin.",
+        "footer_exclude": "von Fußzeile ausschließen",
         "full_name": "Voller Name",
         "gal": "Globales Adressbuch",
         "gal_info": "Das globale Adressbuch enthält alle Objekte einer Domain und kann durch keinen Benutzer geändert werden. Die Verfügbarkeitsinformation in SOGo ist nur bei eingeschaltetem globalen Adressbuch ersichtlich <b>Zum Anwenden einer Änderung muss SOGo neugestartet werden.</b>",
@@ -631,7 +632,6 @@
         "max_quota": "Max. Größe per Mailbox (MiB)",
         "maxage": "Maximales Alter in Tagen einer Nachricht, die kopiert werden soll<br><small>(0 = alle Nachrichten kopieren)</small>",
         "maxbytespersecond": "Max. Übertragungsrate in Bytes/s (0 für unlimitiert)",
-        "mbox_exclude": "Mailboxen ausschließen",
         "mbox_rl_info": "Dieses Limit wird auf den SASL Loginnamen angewendet und betrifft daher alle Absenderadressen, die der eingeloggte Benutzer verwendet. Bei Mailbox Ratelimit überwiegt ein Domain-weites Ratelimit.",
         "mins_interval": "Intervall (min)",
         "multiple_bookings": "Mehrfaches Buchen",

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

@@ -613,6 +613,7 @@
         "extended_sender_acl_info": "A DKIM domain key should be imported, if available.<br>\r\n  Remember to add this server to the corresponding SPF TXT record.<br>\r\n  Whenever a domain or alias domain is added to this server, that overlaps with an external address, the external address is removed.<br>\r\n  Use @domain.tld to allow to send as *@domain.tld.",
         "force_pw_update": "Force password update at next login",
         "force_pw_update_info": "This user will only be able to login to %s. App passwords remain useable.",
+        "footer_exclude": "Exclude from footer",
         "full_name": "Full name",
         "gal": "Global Address List",
         "gal_info": "The GAL contains all objects of a domain and cannot be edited by any user. Free/busy information in SOGo is missing, if disabled! <b>Restart SOGo to apply changes.</b>",
@@ -631,7 +632,6 @@
         "max_quota": "Max. quota per mailbox (MiB)",
         "maxage": "Maximum age of messages in days that will be polled from remote<br><small>(0 = ignore age)</small>",
         "maxbytespersecond": "Max. bytes per second <br><small>(0 = unlimited)</small>",
-        "mbox_exclude": "Exclude mailboxes",
         "mbox_rl_info": "This rate limit is applied on the SASL login name, it matches any \"from\" address used by the logged-in user. A mailbox rate limit overrides a domain-wide rate limit.",
         "mins_interval": "Interval (min)",
         "multiple_bookings": "Multiple bookings",

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

@@ -609,6 +609,7 @@
         "extended_sender_acl_info": "Uma chave de domínio DKIM deve ser importada, se disponível. <br>\r\n Lembre-se de adicionar esse servidor ao registro TXT SPF correspondente. <br>\r\n Sempre que um domínio ou domínio de alias é adicionado a esse servidor, que se sobrepõe a um endereço externo, o endereço externo é removido. <br>\r\n Use @domain .tld para permitir o envio como * @domain .tld.",
         "force_pw_update": "Forçar a atualização da senha no próximo login",
         "force_pw_update_info": "Esse usuário só poderá fazer login em %s. As senhas do aplicativo permanecem utilizáveis.",
+        "footer_exclude": "Excluir do rodapé",
         "full_name": "Nome completo",
         "gal": "Lista de endereços global",
         "gal_info": "A GAL contém todos os objetos de um domínio e não pode ser editada por nenhum usuário. Faltam informações de disponibilidade no SoGo, se desativadas! <b>Reinicie o SoGo para aplicar as alterações.</b>",
@@ -687,8 +688,7 @@
         "unchanged_if_empty": "Se inalterado, deixe em branco",
         "username": "Nome de usuário",
         "validate_save": "Valide e salve",
-        "custom_attributes": "Atributos personalizados",
-        "mbox_exclude": "Excluir mailboxes"
+        "custom_attributes": "Atributos personalizados"
     },
     "fido2": {
         "confirm": "Confirme",

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

@@ -546,6 +546,7 @@
         "extended_sender_acl_info": "Для внешних доменов должен быть импортирован или сгенерирован доменный ключ DKIM с соответствующей записью TXT в домене, если внешний домен использует DMARC.<br>\r\n  Не забудьте добавить этот сервер к соответствующей записи SPF TXT внешнего домена.<br>\r\n  Добавление домена из списка внешних адресов в mailcow автоматически удалит соответствующие записи из внешних адресов пользователей.<br>\r\n  Чтобы разрешить пользователю отправку от имени *@domain.tld, укажите @domain.tld.",
         "force_pw_update": "Требовать смены пароля при следующем входе в систему",
         "force_pw_update_info": "Пользователь должен будет войти в %s и сменить свой пароль. mailcow OAuth2, SOGo, EAS, IMAP/POP3 и SMTP будут не доступны до смены пароля.",
+        "footer_exclude": "Исключить из нижнего колонтитула",
         "full_name": "Полное имя",
         "gal": "GAL - Глобальная адресная книга",
         "gal_info": "GAL содержит все объекты домена и не подлежит редактированию. Информация о занятости в SOGo будет отсутствовать для домена, если данная функция будет отключена! <b>Требуется перезапустить SOGo, чтобы применить изменения.</b>",
@@ -635,7 +636,6 @@
         "domain_footer": "Нижний колонтитул домена",
         "domain_footer_html": "HTML нижний колонтитул",
         "domain_footer_plain": "ПРОСТОЙ нижний колонтитул",
-        "mbox_exclude": "Исключить почтовые ящики",
         "custom_attributes": "Пользовательские атрибуты"
     },
     "fido2": {

+ 2 - 2
data/web/lang/lang.uk-ua.json

@@ -561,6 +561,7 @@
         "extended_sender_acl": "Зовнішні адреси пошти",
         "force_pw_update": "Вимагати зміну пароля при наступному вході до системи",
         "force_pw_update_info": "Цей користувач зможе увійти лише в %s. Паролі додатків залишаються придатними для використання.",
+        "footer_exclude": "Виключити з нижнього колонтитула",
         "full_name": "Повне ім'я",
         "gal": "GAL - Глобальна адресна книга",
         "generate": "згенерувати",
@@ -659,8 +660,7 @@
         },
         "domain_footer_html": "Нижній колонтитул HTML",
         "domain_footer_plain": "ЗВИЧАЙНИЙ нижній колонтитул",
-        "custom_attributes": "Користувацькі атрибути",
-        "mbox_exclude": "Виключити поштові скриньки"
+        "custom_attributes": "Користувацькі атрибути"
     },
     "fido2": {
         "confirm": "Підтвердити",

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

@@ -569,6 +569,7 @@
         "extended_sender_acl_info": "如果可以的話,請匯入 DKIM 域名金鑰。<br>\r\n別忘記將此伺服器新增到相應的 SPF TXT 中。<br>\r\n當域名或域名別名被新增時,若其與此外部寄件人地址交疊,則外部寄件人地址會被移除。<br>\r\n填入 @domain.tld 以允許作為 *@domain.tld 發送郵件。",
         "force_pw_update": "在下一次登入時強制要求更新密碼",
         "force_pw_update_info": "此使用者只能登入至 %s。應用程式密碼仍可正常使用",
+        "footer_exclude": "从页脚排除",
         "full_name": "全名",
         "gal": "全域聯絡人清單",
         "gal_info": "<b>全域聯絡人清單</b>包含了域名下的所有物件,且使用者不可編輯。如果關閉,使用者的 空閒/繁忙 訊息將不能在 SOGo 中顯示。<b>重新啟動 SOGo 以應用更改。</b>",
@@ -648,7 +649,6 @@
         "validate_save": "驗證並儲存",
         "domain_footer_info": "網域範圍的頁尾將會新增至與該網域內的位址關聯的所有外發電子郵件。 <br> 以下變數可用於頁尾:",
         "custom_attributes": "自訂屬性",
-        "mbox_exclude": "排除信箱",
         "pushover_sound": "聲音"
     },
     "fido2": {

+ 7 - 2
data/web/templates/edit/domain.twig

@@ -298,9 +298,9 @@
 {{ lang.edit.domain_footer_info_vars.custom }}</pre>
                     <form class="form-horizontal mt-4" data-id="domain_footer">
                       <div class="row mb-4">
-                        <label class="control-label col-sm-2" for="mbox_exclude">{{ lang.edit.mbox_exclude }}</label>
+                        <label class="control-label col-sm-2" for="exclude">{{ lang.edit.footer_exclude }}</label>
                         <div class="col-sm-10">
-                          <select data-live-search="true" data-width="100%" style="width:100%" id="editMboxExclude" name="mbox_exclude" size="10" multiple>
+                          <select data-live-search="true" data-width="100%" style="width:100%" id="editFooterExclude" name="exclude" size="10" multiple>
                             {% for mailbox in mailboxes %}
                               <option value="{{ mailbox }}" {% if mailbox in domain_footer.mbox_exclude %}selected{% endif %}>
                                 {{ mailbox }}
@@ -311,6 +311,11 @@
                                 {{ alias }}
                               </option>
                             {% endfor %}
+                            {% for alias_domain in alias_domains %}
+                              <option data-subtext="Alias-Domain" value="{{ alias_domain }}" {% if alias_domain in domain_footer.alias_domain_exclude %}selected{% endif %}>
+                                {{ alias_domain }}
+                              </option>
+                            {% endfor %}
                           </select>
                         </div>
                       </div>