feat: add rule forwarding action
This commit is contained in:
@@ -3,9 +3,12 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const forwardingHeaderName = "X-LanQin-Forwarded-By"
|
||||
@@ -69,6 +72,62 @@ func (a *App) processInboundForwarding(ctx context.Context, messageID, mailboxID
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) processRuleForwarding(ctx context.Context, messageID, mailboxID string, action MailRuleAction) error {
|
||||
var userID, mailboxAddress string
|
||||
if err := a.db.QueryRowContext(ctx, `SELECT user_id,address FROM mailboxes WHERE id=? AND status='active'`, mailboxID).Scan(&userID, &mailboxAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
targets, err := a.cleanForwardingTargets(ctx, userID, splitRuleForwardTargets(action.Value))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self := normalizeEmail(mailboxAddress)
|
||||
filteredTargets := make([]string, 0, len(targets))
|
||||
for _, target := range targets {
|
||||
if normalizeEmail(target) == self {
|
||||
continue
|
||||
}
|
||||
filteredTargets = append(filteredTargets, target)
|
||||
}
|
||||
targets = dedupeEmails(filteredTargets)
|
||||
if len(targets) == 0 {
|
||||
return nil
|
||||
}
|
||||
raw, err := a.forwardingRawMessage(ctx, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasForwardingHeader(raw) {
|
||||
a.log.Warn("skip rule forwarding message that already has LanQin forwarding header", "message", messageID, "mailbox", mailboxID)
|
||||
return nil
|
||||
}
|
||||
forwarded := addForwardingHeaders(raw, mailboxAddress, a.cfg.PublicHostname)
|
||||
var rfcMessageID string
|
||||
_ = a.db.QueryRowContext(ctx, `SELECT message_id FROM messages WHERE id=?`, messageID).Scan(&rfcMessageID)
|
||||
if strings.TrimSpace(rfcMessageID) == "" {
|
||||
rfcMessageID = messageID
|
||||
}
|
||||
queueID, err := a.enqueueSend(ctx, sendQueueInput{
|
||||
UserID: userID,
|
||||
MailboxID: mailboxID,
|
||||
SentMessageID: messageID,
|
||||
MessageID: ruleForwardQueueMessageID(rfcMessageID, targets),
|
||||
Source: sendSourceRuleForwarding,
|
||||
MailFrom: mailboxAddress,
|
||||
HeaderFrom: mailboxAddress,
|
||||
Recipients: targets,
|
||||
MIMEBytes: forwarded,
|
||||
Now: a.now().UTC(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if queueID == "" {
|
||||
a.log.Warn("rule forwarding target configured but SMTP sending is not configured", "message", messageID, "mailbox", mailboxID, "targets", strings.Join(targets, ","))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) inboundForwardingTargets(ctx context.Context, mailboxID string) (targetEmails []string, userID, mailboxAddress string, err error) {
|
||||
var mailboxTarget, mailboxTargetsJSON, accountTarget, accountTargetsJSON string
|
||||
err = a.db.QueryRowContext(ctx, `SELECT mb.user_id,mb.address,COALESCE(mfs.target_email,''),COALESCE(mfs.target_emails,'[]'),COALESCE(afs.target_email,''),COALESCE(afs.target_emails,'[]')
|
||||
@@ -130,6 +189,21 @@ func (a *App) forwardingRawMessage(ctx context.Context, messageID string) ([]byt
|
||||
})
|
||||
}
|
||||
|
||||
func splitRuleForwardTargets(value string) []string {
|
||||
return strings.FieldsFunc(value, func(r rune) bool {
|
||||
return unicode.IsSpace(r) || r == ',' || r == ',' || r == ';' || r == ';'
|
||||
})
|
||||
}
|
||||
|
||||
func ruleForwardQueueMessageID(messageID string, targets []string) string {
|
||||
base := strings.TrimSpace(messageID)
|
||||
if base == "" {
|
||||
base = newID("ruleforward")
|
||||
}
|
||||
sum := sha256.Sum256([]byte(strings.Join(dedupeEmails(targets), ",")))
|
||||
return base + "#rule-forward-" + hex.EncodeToString(sum[:])[:12]
|
||||
}
|
||||
|
||||
func hasForwardingHeader(raw []byte) bool {
|
||||
header := raw
|
||||
if idx := bytes.Index(raw, []byte("\r\n\r\n")); idx >= 0 {
|
||||
|
||||
Reference in New Issue
Block a user