Skip to content

Commit 6474f3a

Browse files
committed
submission: Add automatic BCC support for relayed messages
Add per-user configurable BCC functionality to the submission service. When submission_bcc is set to a valid email address, all messages relayed through the submission service will have a copy sent to that address. New settings: - submission_bcc: BCC recipient address (empty = disabled) - submission_bcc_ignore_errors: If FALSE (default), the DATA command fails when the relay rejects the BCC recipient. If TRUE, delivery proceeds with a warning logged. The BCC recipient is added before sending DATA. In strict mode (submission_bcc_ignore_errors=no), the server waits for the BCC RCPT response and fails the transaction if rejected.
1 parent f5d58b8 commit 6474f3a

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

src/submission/submission-backend-relay.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "settings.h"
77
#include "mail-user.h"
88
#include "iostream-ssl.h"
9+
#include "smtp-address.h"
910
#include "smtp-client.h"
1011
#include "smtp-client-connection.h"
1112
#include "smtp-client-transaction.h"
@@ -655,6 +656,55 @@ struct relay_cmd_data_context {
655656
struct smtp_server_transaction *trans;
656657
};
657658

659+
static void
660+
relay_cmd_data_callback(const struct smtp_reply *relay_reply,
661+
struct relay_cmd_data_context *data_ctx);
662+
663+
struct relay_cmd_bcc_context {
664+
struct submission_backend_relay *backend;
665+
struct smtp_server_cmd_ctx *cmd;
666+
struct smtp_server_transaction *trans;
667+
struct relay_cmd_data_context *data_ctx;
668+
struct istream *data_input;
669+
bool ignore_errors;
670+
};
671+
672+
static void
673+
relay_cmd_bcc_rcpt_callback(const struct smtp_reply *relay_reply,
674+
struct relay_cmd_bcc_context *bcc_ctx)
675+
{
676+
struct submission_backend_relay *rbackend = bcc_ctx->backend;
677+
struct submission_backend *backend = &rbackend->backend;
678+
struct smtp_reply reply;
679+
680+
if (!backend_relay_handle_relay_reply(rbackend, bcc_ctx->cmd,
681+
relay_reply, &reply))
682+
return;
683+
684+
if (!smtp_reply_is_success(&reply)) {
685+
if (bcc_ctx->ignore_errors) {
686+
e_warning(backend->event,
687+
"BCC recipient rejected (ignored): %s",
688+
smtp_reply_log(&reply));
689+
/* best-effort: proceed with DATA despite BCC failure */
690+
smtp_client_transaction_send(
691+
rbackend->trans, bcc_ctx->data_input,
692+
relay_cmd_data_callback, bcc_ctx->data_ctx);
693+
} else {
694+
e_error(backend->event, "BCC recipient rejected: %s",
695+
smtp_reply_log(&reply));
696+
/* strict mode: fail the DATA command */
697+
smtp_server_reply(bcc_ctx->cmd, 550, "5.5.0",
698+
"BCC recipient rejected by relay");
699+
}
700+
return;
701+
}
702+
703+
/* BCC accepted: proceed with DATA */
704+
smtp_client_transaction_send(rbackend->trans, bcc_ctx->data_input,
705+
relay_cmd_data_callback, bcc_ctx->data_ctx);
706+
}
707+
658708
static void
659709
relay_cmd_data_rcpt_callback(const struct smtp_reply *relay_reply,
660710
struct submission_recipient *srcpt)
@@ -775,6 +825,7 @@ backend_relay_cmd_data(struct submission_backend *backend,
775825
{
776826
struct submission_backend_relay *rbackend =
777827
container_of(backend, struct submission_backend_relay, backend);
828+
struct client *client = backend->client;
778829
struct relay_cmd_data_context *data_ctx;
779830

780831
/* start relaying to relay server */
@@ -788,6 +839,47 @@ backend_relay_cmd_data(struct submission_backend *backend,
788839

789840
backend_relay_cmd_data_init_callbacks(rbackend, trans);
790841

842+
/* Add BCC recipient if configured */
843+
if (client->set->submission_bcc[0] != '\0') {
844+
struct smtp_address *bcc_addr;
845+
const char *error;
846+
847+
if (smtp_address_parse_path(
848+
trans->pool, client->set->submission_bcc,
849+
SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
850+
&bcc_addr, &error) < 0) {
851+
/* This shouldn't happen if settings validation passed,
852+
but handle it gracefully by skipping BCC */
853+
e_error(backend->event,
854+
"Invalid BCC address '%s': %s",
855+
client->set->submission_bcc, error);
856+
} else {
857+
struct relay_cmd_bcc_context *bcc_ctx;
858+
859+
bcc_ctx = p_new(trans->pool,
860+
struct relay_cmd_bcc_context, 1);
861+
bcc_ctx->backend = rbackend;
862+
bcc_ctx->cmd = cmd;
863+
bcc_ctx->trans = trans;
864+
bcc_ctx->data_ctx = data_ctx;
865+
bcc_ctx->data_input = data_input;
866+
bcc_ctx->ignore_errors =
867+
client->set->submission_bcc_ignore_errors;
868+
869+
smtp_client_transaction_add_pool_rcpt(
870+
rbackend->trans, trans->pool, bcc_addr, NULL,
871+
relay_cmd_bcc_rcpt_callback, bcc_ctx);
872+
873+
e_debug(backend->event, "Added BCC recipient: %s",
874+
smtp_address_encode(bcc_addr));
875+
876+
/* Defer send() until BCC RCPT callback - it will
877+
either proceed with DATA or fail the command
878+
depending on BCC result and strict/best-effort mode */
879+
return 0;
880+
}
881+
}
882+
791883
smtp_client_transaction_send(rbackend->trans, data_input,
792884
relay_cmd_data_callback, data_ctx);
793885
return 0;

src/submission/submission-settings.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "master-service-settings.h"
88
#include "service-settings.h"
99
#include "mail-storage-settings.h"
10+
#include "smtp-address.h"
1011
#include "submission-settings.h"
1112

1213
#include <unistd.h>
@@ -84,6 +85,9 @@ static const struct setting_define submission_setting_defines[] = {
8485
DEF(IN_PORT, submission_relay_port),
8586
DEF(BOOL, submission_relay_trusted),
8687

88+
DEF(STR, submission_bcc),
89+
DEF(BOOL, submission_bcc_ignore_errors),
90+
8791
DEF(STR, submission_relay_user),
8892
DEF(STR, submission_relay_master_user),
8993
DEF(STR, submission_relay_password),
@@ -127,6 +131,9 @@ static const struct submission_settings submission_default_settings = {
127131
.submission_relay_port = 25,
128132
.submission_relay_trusted = FALSE,
129133

134+
.submission_bcc = "",
135+
.submission_bcc_ignore_errors = FALSE,
136+
130137
.submission_relay_user = "",
131138
.submission_relay_master_user = "",
132139
.submission_relay_password = "",
@@ -234,6 +241,21 @@ submission_settings_verify(void *_set, pool_t pool ATTR_UNUSED, const char **err
234241
}
235242
if (*set->hostname == '\0')
236243
set->hostname = p_strdup(pool, my_hostdomain());
244+
245+
if (set->submission_bcc[0] != '\0') {
246+
struct smtp_address *bcc_addr;
247+
const char *error;
248+
249+
if (smtp_address_parse_path(
250+
pool, set->submission_bcc,
251+
SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
252+
&bcc_addr, &error) < 0) {
253+
*error_r = t_strdup_printf(
254+
"Invalid submission_bcc address '%s': %s",
255+
set->submission_bcc, error);
256+
return FALSE;
257+
}
258+
}
237259
#endif
238260
return TRUE;
239261
}

src/submission/submission-settings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ struct submission_settings {
3838
in_port_t submission_relay_port;
3939
bool submission_relay_trusted;
4040

41+
/* submission BCC: */
42+
const char *submission_bcc;
43+
bool submission_bcc_ignore_errors;
44+
4145
const char *submission_relay_user;
4246
const char *submission_relay_master_user;
4347
const char *submission_relay_password;

0 commit comments

Comments
 (0)