git subrepo commit (merge) mailcow/src/mailcow-dockerized

subrepo: subdir:   "mailcow/src/mailcow-dockerized"
  merged:   "02ae5285"
upstream: origin:   "https://github.com/mailcow/mailcow-dockerized.git"
  branch:   "master"
  commit:   "649a5c01"
git-subrepo: version:  "0.4.3"
  origin:   "???"
  commit:   "???"
Change-Id: I870ad468fba026cc5abf3c5699ed1e12ff28b32b
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_asn.map b/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_asn.map
index fb42628..1858c55 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_asn.map
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_asn.map
@@ -1,30 +1,30 @@
 # High spam networks, disabled by default
 # ASN SCORE DESC
 # Remove comment to enable score
-#201942 5 #Soltia Consulting SL - ipinfo.io
-#16276 2 #OVH
-#12876 2 #ONLINE S.A.S
-#31034 5 #ARUBA-ASN, IT
-#12874 5 #FASTWEB, IT
-#30823 3 #PKV spam
-#42831 5 #UK Dedicated Servers Ltd
-#29119 5 #Aire Networks del Mediterraneo S.L.U.
-#13335 5 #Cloudflare
-#28753 5 #Leaseweb
-#61272 5 #Informacines sistemos ir technologijos
-#53755 5 #Input Output Flood LLC
-#29422 5 #FICIX Helsinki
-#62255 4 #Asmunda New Media Ltd
-#14061 4 #Digitalocean
-#55293 4 #A2 Hosting
-#63018 4 #US Dedicated
-#197518 2 #RACKMARKT
-#44493 2
-#46606 2
-#49505 2
-#21100 2
-#197695 2
-#198068 2
-#43146 2
-#49100 4
-#39364 4
+#12874 5 #Fastweb SpA, Italy
+#12876 2 #ONLINE S.A.S, France
+#13335 5 #Cloudflare Inc., United States
+#14061 4 #DigitalOcean LLC, United States
+#16276 2 #OVH SAS, France
+#21100 2 #ITL LLC, Ukraine
+#28753 5 #Leaseweb Deutschland GmbH, Germany
+#29119 5 #ServiHosting Networks S.L., Spain
+#29422 5 #Telia Inmics-Nebula Oy, Finland
+#30823 3 #combahton GmbH, Germany
+#31034 5 #Aruba S.p.A, Italy
+#39364 4 #Hormoz IT & Network Waves Connection Co. (PJS), Iran
+#42831 5 #UK Dedicated Servers Limited, United Kingdom
+#43146 2 #Domain names registrar REG.RU Ltd, Russia
+#44493 2 #Chelyabinsk-Signal LLC, Russia
+#46606 2 #Unified Layer, United States
+#49100 4 #Pishgaman Toseeh Ertebatat Company (Private Joint Stock), Iran
+#49505 2 #OOO Network of data-centers Selectel, Russia
+#53755 5 #Input Output Flood LLC, United States
+#55293 4 #A2 Hosting Inc., United States
+#61272 5 #Informacines sistemos ir technologijos - UAB, Lithuania
+#62255 4 #Asmunda New Media Ltd., Seychelles
+#63018 4 #Dedicated.com, United States
+#197518 2 #Rackmarkt SL, Spain
+#197695 2 #Domain names registrar REG.RU Ltd, Russia
+#198068 2 #P.A.G.M. OU, Estonia
+#201942 5 #Soltia Consulting SL, Spain
\ No newline at end of file
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_header.map b/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_header.map
new file mode 100644
index 0000000..839c3c3
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bad_header.map
@@ -0,0 +1,2 @@
+/Thread-Topic:\s[a-zA-Z]{3}\s[a-zA-Z]{2}[\s\r\n]{0,1}[^a-zA-Z0-9][\r\n]/i
+/Thread-Topic:\s[a-zA-Z]{3}\s[a-zA-Z]{2}\s[a-zA-Z]{1}\s[a-zA-Z]{5}[\s\r\n]{0,1}[^a-zA-Z0-9][\r\n]/i
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bulk_header.map b/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bulk_header.map
index 303954e..e9dc206 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bulk_header.map
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/custom/bulk_header.map
@@ -1,5 +1,5 @@
 /X-EMV-Platform; .*/i
-/.*nur-1-click*/i
+/.*nur-1-click.*/i
 /.*episerver.*/i
 /.*supergewinne.*/i
 /List-Unsubscribe.*nbps\.eu/i
@@ -16,3 +16,4 @@
 /.*dynamic-lht.*/i
 /.*light-house-traffic.*/i
 /.*newsletterplus.*/i
+/.*X-Chpo.*/i
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/dynmaps/bcc.php b/mailcow/src/mailcow-dockerized/data/conf/rspamd/dynmaps/bcc.php
new file mode 100644
index 0000000..3145fee
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/dynmaps/bcc.php
@@ -0,0 +1,88 @@
+<?php

+// File size is limited by Nginx site to 10M

+// To speed things up, we do not include prerequisites

+header('Content-Type: text/plain');

+require_once "vars.inc.php";

+// Do not show errors, we log to using error_log

+ini_set('error_reporting', 0);

+// Init database

+//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;

+$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;

+$opt = [

+    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,

+    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,

+    PDO::ATTR_EMULATE_PREPARES   => false,

+];

+try {

+  $pdo = new PDO($dsn, $database_user, $database_pass, $opt);

+}

+catch (PDOException $e) {

+  error_log("BCC MAP SQL ERROR: " . $e . PHP_EOL);

+  http_response_code(501);

+  exit;

+}

+

+function parse_email($email) {

+  if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;

+  $a = strrpos($email, '@');

+  return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));

+}

+if (!function_exists('getallheaders'))  {

+  function getallheaders() {

+    if (!is_array($_SERVER)) {

+      return array();

+    }

+    $headers = array();

+    foreach ($_SERVER as $name => $value) {

+      if (substr($name, 0, 5) == 'HTTP_') {

+        $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;

+      }

+    }

+    return $headers;

+  }

+}

+

+// Read headers

+$headers = getallheaders();

+// Get rcpt

+$rcpt = $headers['Rcpt'];

+// Get from

+$from = $headers['From'];

+// Remove tags

+$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);

+$from = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $from);

+

+try {

+  if (!empty($rcpt)) {

+    $stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'rcpt' AND `local_dest` = :local_dest AND `active` = '1'");

+    $stmt->execute(array(

+      ':local_dest' => $rcpt

+    ));

+    $bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];

+    if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {

+      error_log("BCC MAP: returning ". $bcc_dest . " for " . $rcpt . PHP_EOL);

+      http_response_code(201);

+      echo trim($bcc_dest);

+      exit;

+    }

+  }

+  if (!empty($from)) {

+    $stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'sender' AND `local_dest` = :local_dest AND `active` = '1'");

+    $stmt->execute(array(

+      ':local_dest' => $from

+    ));

+    $bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];

+    if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {

+      error_log("BCC MAP: returning ". $bcc_dest . " for " . $from . PHP_EOL);

+      http_response_code(201);

+      echo trim($bcc_dest);

+      exit;

+    }

+  }

+}

+catch (PDOException $e) {

+  error_log("BCC MAP SQL ERROR: " . $e->getMessage() . PHP_EOL);

+  http_response_code(502);

+  exit;

+}

+

diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/dynmaps/sasl_logs.php b/mailcow/src/mailcow-dockerized/data/conf/rspamd/dynmaps/sasl_logs.php
new file mode 100644
index 0000000..2d4cbe6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/dynmaps/sasl_logs.php
@@ -0,0 +1,2 @@
+<?php
+// PoC
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/composites.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/composites.conf
index 13c977c..337a2eb 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/composites.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/composites.conf
@@ -11,6 +11,11 @@
   expression = "-g+:policies & !DMARC_POLICY_ALLOW & !MAILLIST & ( FREEMAIL_ENVFROM | FREEMAIL_FROM ) & !WHITELISTED_FWD_HOST";
   score = 16.0;
 }
+# Applies to freemail with undisclosed recipients
+FREEMAIL_TO_UNDISC_RCPT {
+  expression = "FREEMAIL_FROM & ( MISSING_TO | R_UNDISC_RCPT | TO_EQ_FROM )";
+  score = 5.0;
+}
 # Bad policy from non-whitelisted senders
 # Remove SOGO_CONTACT symbol for fwd hosts and senders with broken policy
 SOGO_CONTACT_EXCLUDE {
@@ -29,23 +34,37 @@
 }
 # Applies to a content filter map
 BAD_WORD_BAD_TLD {
-  expression = "FISHY_TLD & ( BAD_WORDS | BAD_WORDS_DE )"
+  expression = "FISHY_TLD & ( BAD_WORDS | BAD_WORDS_DE )";
   score = 10.0;
 }
 # Forged with bad policies and not fwd host, keep bad policy symbols
 FORGED_W_BAD_POLICY {
-  expression = "( -g+:policies | -R_SPF_NA) & ( ~FROM_NEQ_ENVFROM | ~FORGED_SENDER ) & !WHITELISTED_FWD_HOST & !DMARC_POLICY_ALLOW"
+  expression = "( -g+:policies | -R_SPF_NA) & ( ~FROM_NEQ_ENVFROM | ~FORGED_SENDER ) & !WHITELISTED_FWD_HOST & !DMARC_POLICY_ALLOW";
   score = 3.0;
 }
 # Keep negative (good) scores for rbl, policies and hfilter, disable neural group
 WL_FWD_HOST {
-  expression = "-WHITELISTED_FWD_HOST & (^g+:rbl | ^g+:policies | ^g+:hfilter | ^g:neural)"
+  expression = "-WHITELISTED_FWD_HOST & (^g+:rbl | ^g+:policies | ^g+:hfilter | ^g:neural)";
 }
 # Exclude X-Spam like flags from scoring from fwd and sieve hosts
 UPSTREAM_CHECKS_EXCLUDE_FWD_HOST {
-  expression = "(-SIEVE_HOST | -WHITELISTED_FWD_HOST) & (^UNITEDINTERNET_SPAM | ^SPAM_FLAG | ^KLMS_SPAM | ^AOL_SPAM | ^MICROSOFT_SPAM)"
+  expression = "(-SIEVE_HOST | -WHITELISTED_FWD_HOST) & (^UNITEDINTERNET_SPAM | ^SPAM_FLAG | ^KLMS_SPAM | ^AOL_SPAM | ^MICROSOFT_SPAM)";
 }
 # Remove fuzzy group from bounces
 BOUNCE_FUZZY {
   expression = "-BOUNCE & ^g+:fuzzy";
 }
+# Remove bayes ham if fuzzy denied
+FUZZY_HAM_MISMATCH {
+  expression = "( -FUZZY_DENIED | -MAILCOW_FUZZY_DENIED | -LOCAL_FUZZY_DENIED ) & ( ^BAYES_HAM | ^NEURAL_HAM_LONG | ^NEURAL_HAM_SHORT )";
+}
+# Remove bayes spam if local fuzzy white
+FUZZY_SPAM_MISMATCH {
+  expression = "( -LOCAL_FUZZY_WHITE ) & ( ^BAYES_SPAM | ^NEURAL_SPAM_LONG | ^NEURAL_SPAM_SHORT )";
+}
+WL_FWD_HOST {
+  expression = "-WHITELISTED_FWD_HOST & (^g+:rbl | ^g+:policies | ^g+:hfilter | ^g:neural)";
+}
+ENCRYPTED_CHAT {
+  expression = "CHAT_VERSION_HEADER & ENCRYPTED_PGP";
+}
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/dkim_signing.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/dkim_signing.conf
index 13eb094..4fac27f 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/dkim_signing.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/dkim_signing.conf
@@ -32,4 +32,4 @@
 # forwards are arc signed, rejects are dkim signed
 sign_networks = "/etc/rspamd/custom/dovecot_trusted.map";
 use_domain_sign_networks = "header";
-sign_headers = "from:sender:reply-to:subject:date:message-id:to:cc:mime-version:content-type:content-transfer-encoding:resent-to:resent-cc:resent-from:resent-sender:resent-message-id:in-reply-to:references:list-id:list-help:list-owner:list-unsubscribe:list-subscribe:list-post:openpgp:autocrypt";
+sign_headers = "from:sender:reply-to:subject:date:message-id:to:cc:mime-version:content-type:content-transfer-encoding:content-language:resent-to:resent-cc:resent-from:resent-sender:resent-message-id:in-reply-to:references:list-id:list-help:list-owner:list-unsubscribe:list-subscribe:list-post:list-unsubscribe-post:disposition-notification-to:disposition-notification-options:original-recipient:openpgp:autocrypt";
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/external_services.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/external_services.conf
index f05314b..2b091ff 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/external_services.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/external_services.conf
@@ -6,4 +6,7 @@
   # mime-part regex matching in content-type or filename
   # block all macros
   extended = true;
+  max_size = 3145728;
+  timeout = 20.0;
+  retransmits = 1;
 }
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/groups.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/groups.conf
index ef599ef..9ca3409 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/groups.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/groups.conf
@@ -9,9 +9,15 @@
   "BAD_REP_POLICIES" {
     score = 2.0;
   }
+  "BAD_HEADER" {
+    score = 10.0;
+  }
   "BULK_HEADER" {
     score = 4.0;
   }
+  "ENCRYPTED_CHAT" {
+    score = -20.0;
+  }
 }
 
 group "MX" {
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/metadata_exporter.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/metadata_exporter.conf
index f29f480..b6aa150 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/metadata_exporter.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/metadata_exporter.conf
@@ -51,6 +51,7 @@
     and not task:has_symbol('GLOBAL_MIME_FROM_BL')
     and not task:has_symbol('LOCAL_BL_ASN')
     and not task:has_symbol('GLOBAL_RCPT_BL')
+    and not task:has_symbol('BAD_SUBJECT_00')
     and not task:has_symbol('MAILCOW_BLACK') then
       local action = task:get_metric_action('default')
       if action == 'reject' or action == 'add header' or action == 'rewrite subject' then
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/multimap.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/multimap.conf
index 0f05bb5..17ada99 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/multimap.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/multimap.conf
@@ -19,6 +19,22 @@
   symbols_set = ["BULK_HEADER"];
 }
 
+CHAT_VERSION_HEADER {
+  type = "header";
+  header = "Chat-Version";
+  map = "${LOCAL_CONFDIR}/custom/chat_versions.map";
+  regexp = true;
+  symbols_set = ["CHAT_VERSION_HEADER"];
+}
+
+BAD_HEADER {
+  type = "content";
+  map = "${LOCAL_CONFDIR}/custom/bad_header.map";
+  filter = "headers"
+  regexp = true;
+  symbols_set = ["BAD_HEADER"];
+}
+
 LOCAL_BL_ASN {
   require_symbols = "!MAILCOW_WHITE";
   type = "asn";
@@ -80,7 +96,6 @@
   type = "ip";
   map = "${LOCAL_CONFDIR}/custom/dovecot_trusted.map";
   symbols_set = ["SIEVE_HOST"];
-  score = -15;
 }
 
 RSPAMD_HOST {
@@ -136,7 +151,7 @@
   score = 5.0;
 }
 
-BAZAR_ABUSE_CH {
+BAZAAR_ABUSE_CH {
   type = "selector";
   selector = "attachments(hex,md5)";
   map = "https://bazaar.abuse.ch/export/txt/md5/recent/";
@@ -155,3 +170,12 @@
   map = "redis://SMTP_LIMITED_ACCESS";
   symbols_set = ["SMTP_LIMITED_ACCESS"];
 }
+
+BAD_SUBJECT_00 {
+  type = "header";
+  header = "subject";
+  regexp = true;
+  map = "http://nullnull.org/bad-subject-regex.txt";
+  score = 6.0;
+  symbols_set = ["BAD_SUBJECT_00"];
+}
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/options.inc b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/options.inc
index 4fbdfba..fcf499d 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/options.inc
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/options.inc
@@ -2,8 +2,7 @@
   enable_dnssec = true;
 }
 map_watch_interval = 30s;
-dns {
-  timeout = 4s;
-  retransmits = 2;
-}
 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.
+soft_reject_on_timeout = true;
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/policies_group.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/policies_group.conf
index 8799db1..954deac 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/policies_group.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/policies_group.conf
@@ -1,6 +1,6 @@
 symbols = {
     "ARC_REJECT" {
-        score = 0.01;
+        score = 0.1;
     }
     "R_SPF_FAIL" {
         score = 8.0;
@@ -8,6 +8,9 @@
     "R_SPF_PERMFAIL" {
         score = 8.0;
     }
+    "R_SPF_SOFTFAIL" {
+        score = 0.1;
+    }
     "R_DKIM_REJECT" {
         score = 8.0;
     }
@@ -18,6 +21,6 @@
         weight = 8.0;
     }
     "DMARC_POLICY_SOFTFAIL" {
-        weight = 0.0;
+        weight = 0.1;
     }
 }
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/rbl.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/rbl.conf
index c44b9ef..f132b4d 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/rbl.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/rbl.conf
@@ -1,12 +1,4 @@
 rbls {
-  uceprotect1 {
-    symbol = "RBL_UCEPROTECT_LEVEL1";
-    rbl = "dnsbl-1.uceprotect.net";
-  }
-  uceprotect2 {
-    symbol = "RBL_UCEPROTECT_LEVEL2";
-    rbl = "dnsbl-2.uceprotect.net";
-  }
   sorbs { 
     symbol = "RBL_SORBS"; 
     rbl = "dnsbl.sorbs.net";  
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/reputation.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/reputation.conf
index 0e3d03e..c9600b7 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/reputation.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/reputation.conf
@@ -3,7 +3,6 @@
     selector "ip" {
     }
     backend "redis" {
-      servers = "redis";
     }
     symbol = "IP_REPUTATION";
   }
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/statistics_group.conf b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/statistics_group.conf
index 7ed35b1..cf40583 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/statistics_group.conf
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/local.d/statistics_group.conf
@@ -1,6 +1,6 @@
 symbols = {
     "BAYES_SPAM" {
-        weight = 2.5;
+        weight = 4.5;
         description = "Message probably spam, probability: ";
     }
     "BAYES_HAM" {
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/lua/rspamd.local.lua b/mailcow/src/mailcow-dockerized/data/conf/rspamd/lua/rspamd.local.lua
index 3f4c326..b007f09 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/lua/rspamd.local.lua
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/lua/rspamd.local.lua
@@ -321,6 +321,116 @@
 })
 
 rspamd_config:register_symbol({
+  name = 'BCC',
+  type = 'postfilter',
+  callback = function(task)
+    local util = require("rspamd_util")
+    local rspamd_http = require "rspamd_http"
+    local rspamd_logger = require "rspamd_logger"
+
+    local from_table = {}
+    local rcpt_table = {}
+
+    if task:has_symbol('ENCRYPTED_CHAT') then
+      return -- stop
+    end
+
+    local send_mail = function(task, bcc_dest)
+      local lua_smtp = require "lua_smtp"
+      local function sendmail_cb(ret, err)
+        if not ret then
+          rspamd_logger.errx(task, 'BCC SMTP ERROR: %s', err)
+        else
+          rspamd_logger.infox(rspamd_config, "BCC SMTP SUCCESS TO %s", bcc_dest)
+        end
+      end
+      if not bcc_dest then
+        return -- stop
+      end
+      lua_smtp.sendmail({
+        task = task,
+        host = os.getenv("IPV4_NETWORK") .. '.253',
+        port = 591,
+        from = task:get_from(stp)[1].addr,
+        recipients = bcc_dest,
+        helo = 'bcc',
+        timeout = 10,
+      }, task:get_content(), sendmail_cb)
+    end
+
+    -- determine from
+    local from = task:get_from('smtp')
+    if from then
+      for _, a in ipairs(from) do
+        table.insert(from_table, a['addr']) -- add this rcpt to table
+        table.insert(from_table, '@' .. a['domain']) -- add this rcpts domain to table
+      end
+    else
+      return -- stop
+    end
+
+    -- determine rcpts
+    local rcpts = task:get_recipients('smtp')
+    if rcpts then
+      for _, a in ipairs(rcpts) do
+        table.insert(rcpt_table, a['addr']) -- add this rcpt to table
+        table.insert(rcpt_table, '@' .. a['domain']) -- add this rcpts domain to table
+      end
+    else
+      return -- stop
+    end
+
+    local action = task:get_metric_action('default')
+    rspamd_logger.infox("metric action now: %s", action)
+
+    local function rcpt_callback(err_message, code, body, headers)
+      if err_message == nil and code == 201 and body ~= nil then
+        if action == 'no action' or action == 'add header' or action == 'rewrite subject' then
+          send_mail(task, body)
+        end
+      end
+    end
+
+    local function from_callback(err_message, code, body, headers)
+      if err_message == nil and code == 201 and body ~= nil then
+        if action == 'no action' or action == 'add header' or action == 'rewrite subject' then
+          send_mail(task, body)
+        end
+      end
+    end
+
+    if rcpt_table then
+      for _,e in ipairs(rcpt_table) do
+        rspamd_logger.infox(rspamd_config, "checking bcc for rcpt address %s", e)
+        rspamd_http.request({
+          task=task,
+          url='http://nginx:8081/bcc.php',
+          body='',
+          callback=rcpt_callback,
+          headers={Rcpt=e}
+        })
+      end
+    end
+
+    if from_table then
+      for _,e in ipairs(from_table) do
+        rspamd_logger.infox(rspamd_config, "checking bcc for from address %s", e)
+        rspamd_http.request({
+          task=task,
+          url='http://nginx:8081/bcc.php',
+          body='',
+          callback=from_callback,
+          headers={From=e}
+        })
+      end
+    end
+
+    return true
+  end,
+  priority = 20
+})
+
+rspamd_config:register_symbol({
   name = 'DYN_RL_CHECK',
   type = 'prefilter',
   callback = function(task)
diff --git a/mailcow/src/mailcow-dockerized/data/conf/rspamd/override.d/worker-normal.inc b/mailcow/src/mailcow-dockerized/data/conf/rspamd/override.d/worker-normal.inc
index c0f1fb1..d206757 100644
--- a/mailcow/src/mailcow-dockerized/data/conf/rspamd/override.d/worker-normal.inc
+++ b/mailcow/src/mailcow-dockerized/data/conf/rspamd/override.d/worker-normal.inc
@@ -1,4 +1,4 @@
 bind_socket = "*:11333";
-task_timeout = 12s;
+task_timeout = 25s;
 count = 1;
 .include(try=true; priority=30) "$CONFDIR/override.d/worker-normal.custom.inc"