Pārlūkot izejas kodu

[Web] Important fixes for quarantaine; other minor changes

andre.peters 7 gadi atpakaļ
vecāks
revīzija
003e6ef5cd

+ 3 - 3
data/conf/rspamd/dynmaps/settings.php

@@ -47,7 +47,7 @@ function ucl_rcpts($object, $type) {
       $local = parse_email($row['address'])['local'];
       $local = parse_email($row['address'])['local'];
       $domain = parse_email($row['address'])['domain'];
       $domain = parse_email($row['address'])['domain'];
       if (!empty($local) && !empty($domain)) {
       if (!empty($local) && !empty($domain)) {
-        $rcpt[] = '/' . $local . '\+.*' . $domain . '/i';
+        $rcpt[] = '/' . $local . '[+].*' . $domain . '/i';
       }
       }
       $rcpt[] = $row['address'];
       $rcpt[] = $row['address'];
     }
     }
@@ -65,7 +65,7 @@ function ucl_rcpts($object, $type) {
         $local = parse_email($row['alias'])['local'];
         $local = parse_email($row['alias'])['local'];
         $domain = parse_email($row['alias'])['domain'];
         $domain = parse_email($row['alias'])['domain'];
         if (!empty($local) && !empty($domain)) {
         if (!empty($local) && !empty($domain)) {
-          $rcpt[] = '/' . $local . '\+.*' . $domain . '/i';
+          $rcpt[] = '/' . $local . '[+].*' . $domain . '/i';
         }
         }
       $rcpt[] = $row['alias'];
       $rcpt[] = $row['alias'];
       }
       }
@@ -74,7 +74,7 @@ function ucl_rcpts($object, $type) {
     $local = parse_email($row['object'])['local'];
     $local = parse_email($row['object'])['local'];
     $domain = parse_email($row['object'])['domain'];
     $domain = parse_email($row['object'])['domain'];
     if (!empty($local) && !empty($domain)) {
     if (!empty($local) && !empty($domain)) {
-      $rcpt[] = '/' . $local . '\+.*' . $domain . '/i';
+      $rcpt[] = '/' . $local . '[+].*' . $domain . '/i';
     }
     }
     $rcpt[] = $object;
     $rcpt[] = $object;
   }
   }

+ 96 - 27
data/conf/rspamd/meta_exporter/pipe.php

@@ -77,36 +77,103 @@ catch (RedisException $e) {
   exit;
   exit;
 }
 }
 
 
-$filtered_rcpts = array();
+$rcpt_final_mailboxes = array();
+
+// Loop through all rcpts
 foreach (json_decode($rcpts, true) as $rcpt) {
 foreach (json_decode($rcpts, true) as $rcpt) {
-  $parsed_mail = parse_email($rcpt);
-  if (in_array($parsed_mail['domain'], $exclude_domains)) {
-    error_log(sprintf("Skipped domain %s", $parsed_mail['domain']));
+  // Break rcpt into local part and domain part
+  $parsed_rcpt = parse_email($rcpt);
+  
+  // Skip if not a mailcow handled domain
+  try {
+    if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
+      continue;
+    }
+  }
+  catch (RedisException $e) {
+    error_log($e);
+    http_response_code(504);
+    exit;
+  }
+
+  // Skip if domain is excluded
+  if (in_array($parsed_rcpt['domain'], $exclude_domains)) {
+    error_log(sprintf("Skipped domain %s", $parsed_rcpt['domain']));
     continue;
     continue;
   }
   }
+
+  // Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
+  //
+  //             rcpt
+  //              |
+  // mailbox <-- goto ---> alias1, alias2, mailbox2
+  //                          |       |
+  //                      mailbox3    |
+  //                                  |
+  //                               alias3 ---> mailbox4
+  //
   try {
   try {
-    $stmt = $pdo->prepare("SELECT `goto` FROM `alias`
-      WHERE
-      (
-        `address` = :rcpt
-        OR
-        `address` IN (
-          SELECT username FROM mailbox, alias_domain
-            WHERE (alias_domain.alias_domain = :domain_part
-              AND mailbox.username = CONCAT(:local_part, '@', alias_domain.target_domain)
-              AND mailbox.active = '1'
-              AND alias_domain.active='1')
-        )
-      )
-      AND `active`= '1';");
+    $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
     $stmt->execute(array(
     $stmt->execute(array(
-      ':rcpt' => $rcpt,
-      ':local_part' => $parsed_mail['local'],
-      ':domain_part' => $parsed_mail['domain']
+      ':rcpt' => $rcpt
     ));
     ));
     $gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
     $gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
-    if (!empty($gotos)) {
-      $filtered_rcpts  = array_unique(array_merge($filtered_rcpts, explode(',', $gotos)));
+    if (empty($gotos)) {
+      $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
+      $stmt->execute(array(
+        ':rcpt' => '@' . $parsed_rcpt['domain']
+      ));
+      $gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
+    }
+    $gotos_array = explode(',', $gotos);
+
+    $loop_c = 0;
+
+    while (count($gotos_array) != 0 && $loop_c <= 20) {
+
+      // Loop through all found gotos
+      foreach ($gotos_array as $index => &$goto) {
+        error_log("quarantaine pipe: query " . $goto . " as username from mailbox");
+        $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND `active`= '1';");
+        $stmt->execute(array(':goto' => $goto));
+        $username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
+        if (!empty($username)) {
+          error_log("quarantaine pipe: mailbox found: " . $username);
+          // Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
+          if (!in_array($username, $rcpt_final_mailboxes)) {
+            $rcpt_final_mailboxes[] = $username;
+          }
+        }
+        else {
+          $parsed_goto = parse_email($goto);
+          if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
+            error_log($goto . " is not a mailcow handled mailbox or alias address");
+          }
+          else {
+            $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
+            $stmt->execute(array(':goto' => $goto));
+            $goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
+            error_log("quarantaine pipe: goto address " . $goto . " is a alias branch for " . $goto_branch);
+            $goto_branch_array = explode(',', $goto_branch);
+          }
+        }
+        // goto item was processed, unset
+        unset($gotos_array[$index]);
+      }
+
+      // Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
+      if (!empty($goto_branch_array)) {
+        $gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
+        unset($goto_branch_array);
+      }
+
+      // Reindex array
+      $gotos_array = array_values($gotos_array);
+
+      // Force exit if loop cannot be solved
+      // Postfix does not allow for alias loops, so this should never happen.
+      $loop_c++;
+      error_log("quarantaine pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array));
     }
     }
   }
   }
   catch (PDOException $e) {
   catch (PDOException $e) {
@@ -115,8 +182,9 @@ foreach (json_decode($rcpts, true) as $rcpt) {
     exit;
     exit;
   }
   }
 }
 }
-foreach ($filtered_rcpts as $rcpt) {
-  
+
+foreach ($rcpt_final_mailboxes as $rcpt) {
+  error_log("quarantaine pipe: processing quarantaine message for rcpt " . $rcpt);
   try {
   try {
     $stmt = $pdo->prepare("INSERT INTO `quarantaine` (`qid`, `score`, `sender`, `rcpt`, `symbols`, `user`, `ip`, `msg`, `action`)
     $stmt = $pdo->prepare("INSERT INTO `quarantaine` (`qid`, `score`, `sender`, `rcpt`, `symbols`, `user`, `ip`, `msg`, `action`)
       VALUES (:qid, :score, :sender, :rcpt, :symbols, :user, :ip, :msg, :action)");
       VALUES (:qid, :score, :sender, :rcpt, :symbols, :user, :ip, :msg, :action)");
@@ -131,18 +199,19 @@ foreach ($filtered_rcpts as $rcpt) {
       ':msg' => $raw_data,
       ':msg' => $raw_data,
       ':action' => $action
       ':action' => $action
     ));
     ));
-    $stmt = $pdo->prepare('DELETE FROM `quarantaine` WHERE `id` NOT IN ( 
+    $stmt = $pdo->prepare('DELETE FROM `quarantaine` WHERE `rcpt` = :rcpt AND `id` NOT IN (
       SELECT `id`
       SELECT `id`
       FROM (
       FROM (
         SELECT `id`
         SELECT `id`
         FROM `quarantaine`
         FROM `quarantaine`
-        WHERE `rcpt` = :rcpt
+        WHERE `rcpt` = :rcpt2
         ORDER BY id DESC
         ORDER BY id DESC
         LIMIT :retention_size
         LIMIT :retention_size
       ) x 
       ) x 
     );');
     );');
     $stmt->execute(array(
     $stmt->execute(array(
       ':rcpt' => $rcpt,
       ':rcpt' => $rcpt,
+      ':rcpt2' => $rcpt,
       ':retention_size' => $retention_size
       ':retention_size' => $retention_size
     ));
     ));
   }
   }

+ 1 - 1
docker-compose.yml

@@ -91,7 +91,7 @@ services:
             - rspamd
             - rspamd
 
 
     php-fpm-mailcow:
     php-fpm-mailcow:
-      image: mailcow/phpfpm:1.7
+      image: mailcow/phpfpm:1.8
       build: ./data/Dockerfiles/phpfpm
       build: ./data/Dockerfiles/phpfpm
       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: