[Web] Important fixes for quarantaine; other minor changes
This commit is contained in:
		| @@ -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; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -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); |   // Break rcpt into local part and domain part | ||||||
|   if (in_array($parsed_mail['domain'], $exclude_domains)) { |   $parsed_rcpt = parse_email($rcpt); | ||||||
|     error_log(sprintf("Skipped domain %s", $parsed_mail['domain'])); |    | ||||||
|  |   // Skip if not a mailcow handled domain | ||||||
|  |   try { | ||||||
|  |     if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) { | ||||||
|       continue; |       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; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // 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` |     $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'"); | ||||||
|       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->execute(array( |     $stmt->execute(array( | ||||||
|       ':rcpt' => $rcpt, |       ':rcpt' => $rcpt | ||||||
|       ':local_part' => $parsed_mail['local'], |  | ||||||
|       ':domain_part' => $parsed_mail['domain'] |  | ||||||
|     )); |     )); | ||||||
|     $gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto']; |     $gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto']; | ||||||
|     if (!empty($gotos)) { |     if (empty($gotos)) { | ||||||
|       $filtered_rcpts  = array_unique(array_merge($filtered_rcpts, explode(',', $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 | ||||||
|     )); |     )); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -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: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user