[Web] Important fixes for quarantaine; other minor changes
parent
7701660119
commit
003e6ef5cd
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue