[SMTP] Add reliable email delivery with retry and database-backed queue (#7240)

* [SMTP] Add reliable email delivery with retry and database-backed queue

Rework the SMTP client from fire-and-forget sending into a persistent
queue with exponential backoff and per-message retry budget, backed by
the existing database rows as the durable source of truth.

- Keep pending emails in an in-memory queue with attempts/backoff
  tracking; enqueue without deleting database rows, and only remove the
  row once delivery is confirmed via the mailDelivered signal.
- Add exponential backoff (doubling, capped at 30 min) between delivery
  attempts, bounded by new smtp/maxretries and smtp/retrydelay settings.
- Track connection-layer failures (connect/auth/encrypt) with their own
  backoff; route failures to recordFailed/recordConnectionFailure so
  undeliverable mail eventually drops from the queue instead of
  retrying forever.
- Treat rejected recipients/senders as permanent failures.
- Rebuild the SMTP buffer from the authoritative queue before each send
  via a new QxtSmtp::reset() so reconnects never duplicate deliveries.
- Start the idle timeout after authentication, not on TCP connect, so
  slow SSL/STARTTLS handshakes are not cut short.

* [SMTP] Fix retry accounting, backoff reset, and token dedupe

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
This commit is contained in:
BruebachL 2026-10-11 20:59:32 +02:00 • committed by GitHub
parent 1a4d89ff8f
commit 8968709642
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 655 additions and 133 deletions

View file

@ -21,6 +21,7 @@
#define SERVATRICE_H
#include "metrics_registry.h"
#include "smtpclient.h"
#include <QByteArray>
#include <QDateTime>
@ -181,6 +182,7 @@ private:
QMap<QString, bool> serverRequiredFeatureList;
QString officialWarnings;
Servatrice_DatabaseInterface *servatriceDatabaseInterface;
bool cleanupDatabaseForEmail(const QString &userName, EmailType type, const QString &token, qint64 rowId);
int serverId;
int uptime;
QMutex txBytesMutex, rxBytesMutex;
@ -231,6 +233,12 @@ public slots:
void scheduleShutdown(const QString &reason, int minutes);
void updateLoginMessage();
void setRequiredFeatures(const QString &featureList);
void onEmailDelivered(const QString &userName, EmailType type, const QString &token, qint64 rowId);
void onEmailPermanentlyFailed(const QString &userName,
EmailType type,
FailureReason reason,
const QString &token,
qint64 rowId);
public:
explicit Servatrice(QObject *parent = nullptr);