diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/submission/submission-backend.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/submission/submission-backend.c')
-rw-r--r-- | src/submission/submission-backend.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/src/submission/submission-backend.c b/src/submission/submission-backend.c new file mode 100644 index 0000000..889a24b --- /dev/null +++ b/src/submission/submission-backend.c @@ -0,0 +1,448 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "submission-common.h" +#include "llist.h" +#include "istream.h" +#include "istream-sized.h" + +#include "submission-recipient.h" +#include "submission-client.h" +#include "submission-commands.h" +#include "submission-backend.h" + +struct submission_backend_module_register +submission_backend_module_register = { 0 }; + +void submission_backend_init(struct submission_backend *backend, + pool_t pool, struct client *client, + const struct submission_backend_vfuncs *vfunc) +{ + backend->pool = pool; + backend->client = client; + backend->v = *vfunc; + + p_array_init(&backend->module_contexts, pool, 5); + + client->backends_count++; + DLLIST_PREPEND(&client->backends, backend); +} + +static void submission_backend_destroy(struct submission_backend *backend) +{ + struct client *client = backend->client; + + i_stream_unref(&backend->data_input); + + i_free(backend->fail_enh_code); + i_free(backend->fail_reason); + + DLLIST_REMOVE(&client->backends, backend); + backend->v.destroy(backend); + pool_unref(&backend->pool); +} + +void submission_backends_destroy_all(struct client *client) +{ + while (client->backends != NULL) + submission_backend_destroy(client->backends); + array_clear(&client->rcpt_backends); + client->state.backend = NULL; +} + +void submission_backend_start(struct submission_backend *backend) +{ + if (backend->started) + return; + if (backend->fail_reason != NULL) { + /* Don't restart until failure is reset at transaction end */ + return; + } + backend->started = TRUE; + backend->v.start(backend); +} + +void submission_backend_started(struct submission_backend *backend, + enum smtp_capability caps) +{ + struct client *client = backend->client; + + if (backend == client->backend_default) + client_default_backend_started(client, caps); + backend->ready = TRUE; + if (backend->v.ready != NULL) + backend->v.ready(backend, caps); +} + +static void +submission_backend_fail_rcpts(struct submission_backend *backend) +{ + struct client *client = backend->client; + struct submission_recipient *srcpt; + const char *enh_code = backend->fail_enh_code; + const char *reason = backend->fail_reason; + + i_assert(array_count(&client->rcpt_to) > 0); + + i_assert(reason != NULL); + if (enh_code == NULL) + enh_code = "4.0.0"; + + array_foreach_elem(&client->rcpt_to, srcpt) { + struct smtp_server_recipient *rcpt = srcpt->rcpt; + + if (srcpt->backend != backend) + continue; + if (rcpt->cmd == NULL) + continue; + + smtp_server_recipient_reply(rcpt, 451, enh_code, "%s", reason); + } +} + +static inline void +submission_backend_reply_failure(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd) +{ + const char *enh_code = backend->fail_enh_code; + const char *reason = backend->fail_reason; + + if (enh_code == NULL) + enh_code = "4.0.0"; + + i_assert(smtp_server_command_get_reply_count(cmd->cmd) == 1); + smtp_server_reply(cmd, 451, enh_code, "%s", reason); +} + +static inline bool +submission_backend_handle_failure(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd) +{ + if (backend->fail_reason == NULL) + return TRUE; + + /* already failed */ + submission_backend_reply_failure(backend, cmd); + return TRUE; +} + +void submission_backend_fail(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd, + const char *enh_code, const char *reason) +{ + struct client *client = backend->client; + bool failed_before = (backend->fail_reason != NULL); + + /* Can be called several times */ + + if (backend == client->backend_default) { + /* default backend: fail the whole client */ + client_destroy(&client, enh_code, reason); + return; + } + + /* Non-default backend for this transaction (maybe even for only + some of the approved recipients): fail only the affected + transaction and/or specific recipients. */ + + /* Remember the failure only once */ + if (!failed_before) { + backend->fail_enh_code = i_strdup(enh_code); + backend->fail_reason = i_strdup(reason); + } + if (cmd == NULL) { + /* Called outside command context: just remember the failure */ + } else if (smtp_server_command_get_reply_count(cmd->cmd) > 1) { + /* Fail DATA/BDAT/BURL command expecting several replies */ + submission_backend_fail_rcpts(backend); + } else { + /* Single command */ + submission_backend_reply_failure(backend, cmd); + } + + /* Call the fail vfunc only once */ + if (!failed_before && backend->v.fail != NULL) + backend->v.fail(backend, enh_code, reason); + backend->started = FALSE; + backend->ready = FALSE; +} + +void submission_backends_client_input_pre(struct client *client) +{ + struct submission_backend *backend; + + for (backend = client->backends; backend != NULL; + backend = backend->next) { + if (!backend->started) + continue; + if (backend->v.client_input_pre != NULL) + backend->v.client_input_pre(backend); + + } +} + +void submission_backends_client_input_post(struct client *client) +{ + struct submission_backend *backend; + + for (backend = client->backends; backend != NULL; + backend = backend->next) { + if (!backend->started) + continue; + if (backend->v.client_input_post != NULL) + backend->v.client_input_post(backend); + } +} + +uoff_t submission_backend_get_max_mail_size(struct submission_backend *backend) +{ + if (backend->v.get_max_mail_size != NULL) + return backend->v.get_max_mail_size(backend); + return UOFF_T_MAX; +} + +void submission_backend_trans_start(struct submission_backend *backend, + struct smtp_server_transaction *trans) +{ + submission_backend_start(backend); + + if (backend->trans_started) + return; + backend->trans_started = TRUE; + + if (backend->v.trans_start != NULL) { + backend->v.trans_start(backend, trans, + trans->mail_from, &trans->params); + } +} + +static void +submission_backend_trans_free(struct submission_backend *backend, + struct smtp_server_transaction *trans) +{ + i_stream_unref(&backend->data_input); + if (backend->v.trans_free != NULL) + backend->v.trans_free(backend, trans); + backend->trans_started = FALSE; + + i_free(backend->fail_enh_code); + i_free(backend->fail_reason); +} + +void submission_backends_trans_start(struct client *client, + struct smtp_server_transaction *trans) +{ + struct submission_backend *backend; + + i_assert(client->state.backend != NULL); + submission_backend_trans_start(client->state.backend, trans); + + array_foreach_elem(&client->pending_backends, backend) + submission_backend_trans_start(backend, trans); + array_clear(&client->pending_backends); +} + +void submission_backends_trans_free(struct client *client, + struct smtp_server_transaction *trans) +{ + struct submission_backend *backend; + + i_assert(client->state.backend != NULL || + array_count(&client->rcpt_backends) == 0); + + array_foreach_elem(&client->rcpt_backends, backend) + submission_backend_trans_free(backend, trans); + array_clear(&client->pending_backends); + array_clear(&client->rcpt_backends); + client->state.backend = NULL; +} + +int submission_backend_cmd_helo(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd, + struct smtp_server_cmd_helo *data) +{ + /* failure on default backend closes the client connection */ + i_assert(backend->fail_reason == NULL); + + if (!backend->started || backend->v.cmd_helo == NULL) { + /* default backend is not interested, respond right away */ + submission_helo_reply_submit(cmd, data); + return 1; + } + + return backend->v.cmd_helo(backend, cmd, data); +} + +void submission_backend_helo_reply_submit( + struct submission_backend *backend ATTR_UNUSED, + struct smtp_server_cmd_ctx *cmd, struct smtp_server_cmd_helo *data) +{ + submission_helo_reply_submit(cmd, data); +} + +int submission_backend_cmd_mail(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd, + struct smtp_server_cmd_mail *data) +{ + if (!submission_backend_handle_failure(backend, cmd)) + return -1; + + submission_backend_start(backend); + + if (backend->v.cmd_mail == NULL) { + /* mail backend is not interested, respond right away */ + return 1; + } + + return backend->v.cmd_mail(backend, cmd, data); +} + +static void +submission_backend_add_pending(struct submission_backend *backend) +{ + struct client *client = backend->client; + struct submission_backend *pending_backend; + + array_foreach_elem(&client->pending_backends, pending_backend) { + if (backend == pending_backend) + return; + } + + array_push_back(&client->pending_backends, &backend); +} + +int submission_backend_cmd_rcpt(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd, + struct submission_recipient *srcpt) +{ + struct smtp_server_transaction *trans; + + if (!submission_backend_handle_failure(backend, cmd)) + return -1; + + i_assert(backend->started); + + if (backend->v.cmd_rcpt == NULL) { + /* backend is not interested, respond right away */ + return 1; + } + + trans = smtp_server_connection_get_transaction(cmd->conn); + if (trans != NULL) + submission_backend_trans_start(srcpt->backend, trans); + else + submission_backend_add_pending(srcpt->backend); + + return backend->v.cmd_rcpt(backend, cmd, srcpt); +} + +int submission_backend_cmd_rset(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd) +{ + if (!submission_backend_handle_failure(backend, cmd)) + return -1; + + submission_backend_start(backend); + + if (backend->v.cmd_rset == NULL) { + /* backend is not interested, respond right away */ + return 1; + } + return backend->v.cmd_rset(backend, cmd); +} + +static int +submission_backend_cmd_data(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd, + struct smtp_server_transaction *trans) +{ + if (backend->fail_reason != NULL) { + submission_backend_fail_rcpts(backend); + return 0; + } + + i_assert(backend->started); + + return backend->v.cmd_data(backend, cmd, trans, + backend->data_input, backend->data_size); +} + +int submission_backends_cmd_data(struct client *client, + struct smtp_server_cmd_ctx *cmd, + struct smtp_server_transaction *trans, + struct istream *data_input, uoff_t data_size) +{ + struct submission_backend *backend; + int ret = 0; + + i_assert(array_count(&client->rcpt_backends) > 0); + + /* Make sure data input stream is at the beginning (plugins may have + messed with it. */ + i_stream_seek(data_input, 0); + + /* create the data_input streams first */ + array_foreach_elem(&client->rcpt_backends, backend) { + backend->data_input = + i_stream_create_sized(data_input, data_size); + backend->data_size = data_size; + } + + /* now that all the streams are created, start reading them + (reading them earlier could have caused the data_input parent's + offset to change) */ + array_foreach_elem(&client->rcpt_backends, backend) { + ret = submission_backend_cmd_data(backend, cmd, trans); + if (ret < 0) + break; + } + + return ret; +} + +int submission_backend_cmd_vrfy(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd, + const char *param) +{ + /* failure on default backend closes the client connection */ + i_assert(backend->fail_reason == NULL); + + submission_backend_start(backend); + + if (backend->v.cmd_vrfy == NULL) { + /* backend is not interested, respond right away */ + return 1; + } + return backend->v.cmd_vrfy(backend, cmd, param); +} + +int submission_backend_cmd_noop(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd) +{ + /* failure on default backend closes the client connection */ + i_assert(backend->fail_reason == NULL); + + submission_backend_start(backend); + + if (backend->v.cmd_noop == NULL) { + /* backend is not interested, respond right away */ + return 1; + } + return backend->v.cmd_noop(backend, cmd); +} + +int submission_backend_cmd_quit(struct submission_backend *backend, + struct smtp_server_cmd_ctx *cmd) +{ + /* failure on default backend closes the client connection */ + i_assert(backend->fail_reason == NULL); + + if (!backend->started) { + /* quit before backend even started */ + return 1; + } + if (backend->v.cmd_quit == NULL) { + /* backend is not interested, respond right away */ + return 1; + } + return backend->v.cmd_quit(backend, cmd); +} |