diff options
Diffstat (limited to 'src/lib-smtp/smtp-server-reply.c')
-rw-r--r-- | src/lib-smtp/smtp-server-reply.c | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/src/lib-smtp/smtp-server-reply.c b/src/lib-smtp/smtp-server-reply.c new file mode 100644 index 0000000..60342db --- /dev/null +++ b/src/lib-smtp/smtp-server-reply.c @@ -0,0 +1,876 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "array.h" +#include "istream.h" +#include "ostream.h" +#include "smtp-address.h" +#include "smtp-reply.h" + +#include "smtp-server-private.h" + +/* + * Reply + */ + +static void smtp_server_reply_destroy(struct smtp_server_reply *reply) +{ + if (reply->command == NULL) + return; + + if (reply->event != NULL) { + e_debug(reply->event, "Destroy"); + event_unref(&reply->event); + } + + if (reply->content == NULL) + return; + str_free(&reply->content->text); +} + +static void smtp_server_reply_clear(struct smtp_server_reply *reply) +{ + smtp_server_reply_destroy(reply); + if (reply->submitted) { + i_assert(reply->command->replies_submitted > 0); + reply->command->replies_submitted--; + } + reply->submitted = FALSE; + reply->forwarded = FALSE; +} + +static void smtp_server_reply_update_event(struct smtp_server_reply *reply) +{ + struct smtp_server_command *command = reply->command; + + event_add_int(reply->event, "index", reply->index); + event_add_int(reply->event, "status", reply->content->status); + + if (command->replies_expected > 1) { + event_set_append_log_prefix(reply->event, + t_strdup_printf("%u reply [%u/%u]: ", + reply->content->status, + reply->index+1, + command->replies_expected)); + } else { + event_set_append_log_prefix(reply->event, + t_strdup_printf("%u reply: ", + reply->content->status)); + } +} + +static struct smtp_server_reply * +smtp_server_reply_alloc(struct smtp_server_command *cmd, unsigned int index) +{ + struct smtp_server_reply *reply; + pool_t pool = cmd->context.pool; + + if (array_is_created(&cmd->replies)) { + reply = array_idx_modifiable(&cmd->replies, index); + /* get rid of any existing reply */ + i_assert(!reply->sent); + smtp_server_reply_clear(reply); + } else { + p_array_init(&cmd->replies, pool, cmd->replies_expected); + array_idx_clear(&cmd->replies, cmd->replies_expected - 1); + reply = array_idx_modifiable(&cmd->replies, index); + } + reply->event = event_create(cmd->context.event); + + return reply; +} + +static void +smtp_server_reply_update_prefix(struct smtp_server_reply *reply, + unsigned int status, const char *enh_code) +{ + pool_t pool = reply->command->context.pool; + string_t *textbuf, *new_text; + const char *new_prefix, *text, *p; + size_t text_len, prefix_len, line_len; + + if (enh_code == NULL || *enh_code == '\0') { + new_prefix = p_strdup_printf(pool, "%03u-", status); + } else { + new_prefix = p_strdup_printf(pool, "%03u-%s ", + status, enh_code); + } + + i_assert(reply->content != NULL); + textbuf = reply->content->text; + + if (textbuf == NULL || str_len(textbuf) == 0) { + reply->content->status_prefix = new_prefix; + return; + } + new_text = str_new(default_pool, 256); + + prefix_len = strlen(reply->content->status_prefix); + text = str_c(textbuf); + text_len = str_len(textbuf); + + i_assert(text_len > prefix_len); + text_len -= prefix_len; + text += prefix_len; + + for (;;) { + reply->content->last_line = str_len(new_text); + + p = strchr(text, '\n'); + i_assert(p != NULL && p > text && *(p-1) == '\r'); + p++; + + str_append(new_text, new_prefix); + str_append_data(new_text, text, p - text); + + line_len = (size_t)(p - text); + i_assert(text_len >= line_len); + text_len -= line_len; + text = p; + + if (text_len <= prefix_len) + break; + + text_len -= prefix_len; + text += prefix_len; + } + + str_free(&textbuf); + reply->content->text = new_text; + reply->content->status_prefix = new_prefix; +} + +void smtp_server_reply_set_status(struct smtp_server_reply *reply, + unsigned int status, const char *enh_code) +{ + pool_t pool = reply->command->context.pool; + + /* RFC 5321, Section 4.2: + + In the absence of extensions negotiated with the client, SMTP servers + MUST NOT send reply codes whose first digits are other than 2, 3, 4, + or 5. Clients that receive such out-of-range codes SHOULD normally + treat them as fatal errors and terminate the mail transaction. + */ + i_assert(status >= 200 && status < 560); + + /* RFC 2034, Section 4: + + All status codes returned by the server must agree with the primary + response code, that is, a 2xx response must incorporate a 2.X.X code, + a 4xx response must incorporate a 4.X.X code, and a 5xx response must + incorporate a 5.X.X code. + */ + i_assert(enh_code == NULL || *enh_code == '\0' || + ((unsigned int)(enh_code[0] - '0') == (status / 100) + && enh_code[1] == '.')); + + if (reply->content->status == status && + null_strcmp(reply->content->enhanced_code, enh_code) == 0) + return; + + smtp_server_reply_update_prefix(reply, status, enh_code); + reply->content->status = status; + reply->content->enhanced_code = p_strdup(pool, enh_code); +} + +unsigned int smtp_server_reply_get_status(struct smtp_server_reply *reply, + const char **enh_code_r) +{ + if (enh_code_r != NULL) + *enh_code_r = reply->content->enhanced_code; + return reply->content->status; +} + +struct smtp_server_reply * +smtp_server_reply_create_index(struct smtp_server_command *cmd, + unsigned int index, unsigned int status, + const char *enh_code) +{ + struct smtp_server_reply *reply; + pool_t pool = cmd->context.pool; + + i_assert(cmd->replies_expected > 0); + i_assert(index < cmd->replies_expected); + + reply = smtp_server_reply_alloc(cmd, index); + reply->index = index; + reply->command = cmd; + + if (reply->content == NULL) + reply->content = p_new(pool, struct smtp_server_reply_content, 1); + smtp_server_reply_set_status(reply, status, enh_code); + reply->content->text = str_new(default_pool, 256); + + smtp_server_reply_update_event(reply); + + return reply; +} + +struct smtp_server_reply * +smtp_server_reply_create(struct smtp_server_command *cmd, + unsigned int status, const char *enh_code) +{ + return smtp_server_reply_create_index(cmd, 0, status, enh_code); +} + +struct smtp_server_reply * +smtp_server_reply_create_forward(struct smtp_server_command *cmd, + unsigned int index, const struct smtp_reply *from) +{ + struct smtp_server_reply *reply; + string_t *textbuf; + char *text; + size_t last_line, i; + + reply = smtp_server_reply_create_index(cmd, index, + from->status, smtp_reply_get_enh_code(from)); + smtp_reply_write(reply->content->text, from); + + i_assert(reply->content != NULL); + textbuf = reply->content->text; + text = str_c_modifiable(textbuf); + + /* Find the last line */ + reply->content->last_line = last_line = 0; + for (i = 0; i < str_len(textbuf); i++) { + if (text[i] == '\n') { + reply->content->last_line = last_line; + last_line = i + 1; + } + } + + /* Make this reply suitable for further amendment with + smtp_server_reply_add_text() */ + if ((reply->content->last_line + 3) < str_len(textbuf)) { + i_assert(text[reply->content->last_line + 3] == ' '); + text[reply->content->last_line + 3] = '-'; + } else { + str_append_c(textbuf, '-'); + } + + reply->forwarded = TRUE; + + return reply; +} + +void smtp_server_reply_free(struct smtp_server_command *cmd) +{ + unsigned int i; + + if (!array_is_created(&cmd->replies)) + return; + + for (i = 0; i < cmd->replies_expected; i++) { + struct smtp_server_reply *reply = + array_idx_modifiable(&cmd->replies, i); + smtp_server_reply_destroy(reply); + } +} + +void smtp_server_reply_add_text(struct smtp_server_reply *reply, + const char *text) +{ + string_t *textbuf = reply->content->text; + + i_assert(!reply->submitted); + + if (*text == '\0') + return; + + do { + const char *p; + + reply->content->last_line = str_len(textbuf); + + p = strchr(text, '\n'); + str_append(textbuf, reply->content->status_prefix); + if (p == NULL) { + str_append(textbuf, text); + text = NULL; + } else { + if (p > text && *(p-1) == '\r') + str_append_data(textbuf, text, p - text - 1); + else + str_append_data(textbuf, text, p - text); + text = p + 1; + } + str_append(textbuf, "\r\n"); + } while (text != NULL && *text != '\0'); +} + +static size_t +smtp_server_reply_get_path_len(struct smtp_server_reply *reply) +{ + size_t prefix_len = strlen(reply->content->status_prefix); + size_t text_len = str_len(reply->content->text), line_len, path_len; + const char *text = str_c(reply->content->text); + const char *text_end = text + text_len, *line_end; + + i_assert(prefix_len <= text_len); + + line_end = strchr(text, '\r'); + if (line_end == NULL) { + line_end = text_end; + line_len = text_len; + } else { + i_assert(line_end + 1 < text_end); + i_assert(*(line_end + 1) == '\n'); + line_len = line_end - text; + } + + if (prefix_len == line_len || text[prefix_len] != '<') { + path_len = 0; + } else { + const char *path_begin = &text[prefix_len], *path_end; + + path_end = strchr(path_begin, '>'); + if (path_end == NULL || path_end > line_end) + path_len = 0; + else { + i_assert(path_end < line_end); + path_end++; + path_len = path_end - path_begin; + if (path_end < line_end && *path_end != ' ') + path_len = 0; + } + } + + i_assert(prefix_len + path_len <= text_len); + return path_len; +} + +void smtp_server_reply_prepend_text(struct smtp_server_reply *reply, + const char *text_prefix) +{ + const char *text = str_c(reply->content->text); + size_t tlen = str_len(reply->content->text), offset; + + i_assert(!reply->sent); + i_assert(reply->content != NULL); + i_assert(reply->content->text != NULL); + + offset = strlen(reply->content->status_prefix) + + smtp_server_reply_get_path_len(reply); + i_assert(offset < tlen); + if (text[offset] == ' ') + offset++; + + str_insert(reply->content->text, offset, text_prefix); + + if (reply->content->last_line > 0) + reply->content->last_line += strlen(text_prefix); +} + +void smtp_server_reply_replace_path(struct smtp_server_reply *reply, + struct smtp_address *path, bool add) +{ + size_t prefix_len, path_len; + const char *path_text; + + i_assert(!reply->sent); + i_assert(reply->content != NULL); + i_assert(reply->content->text != NULL); + + prefix_len = strlen(reply->content->status_prefix); + path_len = smtp_server_reply_get_path_len(reply); + + if (path_len > 0) { + path_text = smtp_address_encode_path(path); + str_replace(reply->content->text, prefix_len, path_len, + path_text); + } else if (add) { + path_text = t_strdup_printf( + "<%s> ", smtp_address_encode(path)); + str_insert(reply->content->text, prefix_len, path_text); + } +} + +void smtp_server_reply_submit(struct smtp_server_reply *reply) +{ + i_assert(!reply->submitted); + i_assert(reply->content != NULL); + i_assert(str_len(reply->content->text) >= 5); + e_debug(reply->event, "Submitted"); + + reply->command->replies_submitted++; + reply->submitted = TRUE; + smtp_server_command_submit_reply(reply->command); +} + +void smtp_server_reply_submit_duplicate(struct smtp_server_cmd_ctx *_cmd, + unsigned int index, + unsigned int from_index) +{ + struct smtp_server_command *cmd = _cmd->cmd; + struct smtp_server_reply *reply, *from_reply; + + i_assert(cmd->replies_expected > 0); + i_assert(index < cmd->replies_expected); + i_assert(from_index < cmd->replies_expected); + i_assert(array_is_created(&cmd->replies)); + + from_reply = array_idx_modifiable(&cmd->replies, from_index); + i_assert(from_reply->content != NULL); + i_assert(from_reply->submitted); + + reply = smtp_server_reply_alloc(cmd, index); + reply->index = index; + reply->command = cmd; + reply->content = from_reply->content; + smtp_server_reply_update_event(reply); + + smtp_server_reply_submit(reply); +} + +void smtp_server_reply_indexv(struct smtp_server_cmd_ctx *_cmd, + unsigned int index, unsigned int status, const char *enh_code, + const char *fmt, va_list args) +{ + struct smtp_server_command *cmd = _cmd->cmd; + struct smtp_server_reply *reply; + + reply = smtp_server_reply_create_index(cmd, index, status, enh_code); + smtp_server_reply_add_text(reply, t_strdup_vprintf(fmt, args)); + smtp_server_reply_submit(reply); +} + +void smtp_server_reply(struct smtp_server_cmd_ctx *_cmd, + unsigned int status, const char *enh_code, const char *fmt, ...) +{ + struct smtp_server_command *cmd = _cmd->cmd; + va_list args; + + i_assert(cmd->replies_expected <= 1); + + va_start(args, fmt); + smtp_server_reply_indexv(_cmd, 0, status, enh_code, fmt, args); + va_end(args); +} + +void smtp_server_reply_index(struct smtp_server_cmd_ctx *_cmd, + unsigned int index, unsigned int status, const char *enh_code, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + smtp_server_reply_indexv(_cmd, index, status, enh_code, fmt, args); + va_end(args); +} + +void smtp_server_reply_index_forward(struct smtp_server_cmd_ctx *cmd, + unsigned int index, const struct smtp_reply *from) +{ + smtp_server_reply_submit( + smtp_server_reply_create_forward(cmd->cmd, index, from)); +} + +void smtp_server_reply_forward(struct smtp_server_cmd_ctx *_cmd, + const struct smtp_reply *from) +{ + struct smtp_server_command *cmd = _cmd->cmd; + + i_assert(cmd->replies_expected <= 1); + + smtp_server_reply_submit( + smtp_server_reply_create_forward(cmd, 0, from)); +} + +static void ATTR_FORMAT(4, 0) +smtp_server_reply_allv(struct smtp_server_cmd_ctx *_cmd, + unsigned int status, const char *enh_code, + const char *fmt, va_list args) +{ + struct smtp_server_command *cmd = _cmd->cmd; + struct smtp_server_reply *reply; + const char *text; + unsigned int first, i = 0; + + /* find the first unsent reply */ + if (array_is_created(&cmd->replies)) { + for (; i < cmd->replies_expected; i++) { + struct smtp_server_reply *reply = + array_idx_modifiable(&cmd->replies, i); + if (!reply->sent) + break; + } + i_assert (i < cmd->replies_expected); + } + first = i++; + + /* compose the reply text */ + text = t_strdup_vprintf(fmt, args); + + /* submit the first remaining reply */ + reply = smtp_server_reply_create_index(cmd, first, status, enh_code); + smtp_server_reply_add_text(reply, text); + smtp_server_reply_submit(reply); + + /* duplicate the rest from it */ + for (; i < cmd->replies_expected; i++) + smtp_server_reply_submit_duplicate(_cmd, i, first); +} + +void smtp_server_reply_all(struct smtp_server_cmd_ctx *_cmd, + unsigned int status, const char *enh_code, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + smtp_server_reply_allv(_cmd, status, enh_code, fmt, args); + va_end(args); +} + +void smtp_server_reply_early(struct smtp_server_cmd_ctx *_cmd, + unsigned int status, const char *enh_code, + const char *fmt, ...) +{ + va_list args; + + _cmd->cmd->reply_early = TRUE; + + va_start(args, fmt); + smtp_server_reply_allv(_cmd, status, enh_code, fmt, args); + va_end(args); +} + +void smtp_server_reply_quit(struct smtp_server_cmd_ctx *_cmd) +{ + struct smtp_server_command *cmd = _cmd->cmd; + struct smtp_server_reply *reply; + + reply = smtp_server_reply_create(cmd, 221, "2.0.0"); + smtp_server_reply_add_text(reply, "Bye"); + smtp_server_reply_submit(reply); +} + +static void +smtp_server_reply_write_one_line(const struct smtp_server_reply *reply, + string_t *str, bool skip_status) +{ + string_t *textbuf; + const char *text, *p; + size_t text_len, prefix_len, line_len; + + i_assert(reply->content != NULL); + textbuf = reply->content->text; + i_assert(str_len(textbuf) > 0); + + prefix_len = strlen(reply->content->status_prefix); + text = str_c(textbuf); + text_len = str_len(textbuf); + + if (skip_status) { + i_assert(text_len > prefix_len); + text_len -= prefix_len; + text += prefix_len; + } + + for (;;) { + p = strchr(text, '\n'); + i_assert(p != NULL && p > text && *(p-1) == '\r'); + str_append_data(str, text, p - text - 1); + line_len = (size_t)(p - text) + 1; + i_assert(text_len >= line_len); + text_len -= line_len; + text = p + 1; + + if (text_len <= prefix_len) + break; + + text_len -= prefix_len; + text += prefix_len; + str_append_c(str, ' '); + } +} + +const char * +smtp_server_reply_get_one_line(const struct smtp_server_reply *reply) +{ + string_t *str = t_str_new(256); + + smtp_server_reply_write_one_line(reply, str, FALSE); + return str_c(str); +} + +const char * +smtp_server_reply_get_message(const struct smtp_server_reply *reply) +{ + string_t *str = t_str_new(256); + + smtp_server_reply_write_one_line(reply, str, TRUE); + return str_c(str); +} + +static int smtp_server_reply_send_real(struct smtp_server_reply *reply) +{ + struct smtp_server_command *cmd = reply->command; + struct smtp_server_connection *conn = cmd->context.conn; + struct ostream *output = conn->conn.output; + string_t *textbuf; + char *text; + int ret = 0; + + i_assert(reply->content != NULL); + textbuf = reply->content->text; + i_assert(str_len(textbuf) > 0); + + /* substitute '-' with ' ' in last line */ + text = str_c_modifiable(textbuf); + text = text + reply->content->last_line + 3; + if (text[0] != ' ') { + i_assert(text[0] == '-'); + text[0] = ' '; + } + + if (o_stream_send(output, str_data(textbuf), str_len(textbuf)) < 0) { + e_debug(reply->event, "Send failed: %s", + o_stream_get_disconnect_reason(output)); + smtp_server_connection_handle_output_error(conn); + return -1; + } + + e_debug(reply->event, "Sent: %s", + smtp_server_reply_get_one_line(reply)); + return ret; +} + +int smtp_server_reply_send(struct smtp_server_reply *reply) +{ + int ret; + + if (reply->sent) + return 0; + + T_BEGIN { + ret = smtp_server_reply_send_real(reply); + } T_END; + + reply->sent = TRUE; + return ret; +} + +bool smtp_server_reply_is_success(const struct smtp_server_reply *reply) +{ + i_assert(reply->content != NULL); + return (reply->content->status / 100 == 2); +} + +void smtp_server_reply_add_to_event(const struct smtp_server_reply *reply, + struct event_passthrough *e) +{ + i_assert(reply->content != NULL); + e->add_int("status_code", reply->content->status); + if (reply->content->enhanced_code != NULL && + reply->content->enhanced_code[0] != '\0') + e->add_str("enhanced_code", reply->content->enhanced_code); + if (!smtp_server_reply_is_success(reply)) + e->add_str("error", smtp_server_reply_get_message(reply)); +} + +/* + * EHLO reply + */ + +struct smtp_server_reply * +smtp_server_reply_create_ehlo(struct smtp_server_command *cmd) +{ + struct smtp_server_connection *conn = cmd->context.conn; + struct smtp_server_reply *reply; + string_t *textbuf; + + reply = smtp_server_reply_create(cmd, 250, ""); + textbuf = reply->content->text; + str_append(textbuf, reply->content->status_prefix); + str_append(textbuf, conn->set.hostname); + str_append(textbuf, "\r\n"); + + return reply; +} + +void smtp_server_reply_ehlo_add(struct smtp_server_reply *reply, + const char *keyword) +{ + string_t *textbuf; + + i_assert(!reply->submitted); + i_assert(reply->content != NULL); + textbuf = reply->content->text; + + reply->content->last_line = str_len(textbuf); + str_append(textbuf, reply->content->status_prefix); + str_append(textbuf, keyword); + str_append(textbuf, "\r\n"); +} + +void smtp_server_reply_ehlo_add_param(struct smtp_server_reply *reply, + const char *keyword, const char *param_fmt, ...) +{ + va_list args; + string_t *textbuf; + + i_assert(!reply->submitted); + i_assert(reply->content != NULL); + textbuf = reply->content->text; + + reply->content->last_line = str_len(textbuf); + str_append(textbuf, reply->content->status_prefix); + str_append(textbuf, keyword); + if (*param_fmt != '\0') { + va_start(args, param_fmt); + str_append_c(textbuf, ' '); + str_vprintfa(textbuf, param_fmt, args); + va_end(args); + } + str_append(textbuf, "\r\n"); +} + +void smtp_server_reply_ehlo_add_params(struct smtp_server_reply *reply, + const char *keyword, + const char *const *params) +{ + string_t *textbuf; + + i_assert(!reply->submitted); + i_assert(reply->content != NULL); + textbuf = reply->content->text; + + reply->content->last_line = str_len(textbuf); + str_append(textbuf, reply->content->status_prefix); + str_append(textbuf, keyword); + if (params != NULL) { + while (*params != NULL) { + str_append_c(textbuf, ' '); + str_append(textbuf, *params); + params++; + } + } + str_append(textbuf, "\r\n"); +} + +void smtp_server_reply_ehlo_add_8bitmime(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_8BITMIME) == 0) + return; + smtp_server_reply_ehlo_add(reply, "8BITMIME"); +} + +void smtp_server_reply_ehlo_add_binarymime(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_BINARYMIME) == 0 || + (caps & SMTP_CAPABILITY_CHUNKING) == 0) + return; + smtp_server_reply_ehlo_add(reply, "BINARYMIME"); +} + +void smtp_server_reply_ehlo_add_chunking(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_CHUNKING) == 0) + return; + smtp_server_reply_ehlo_add(reply, "CHUNKING"); +} + +void smtp_server_reply_ehlo_add_dsn(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_DSN) == 0) + return; + smtp_server_reply_ehlo_add(reply, "DSN"); +} + +void smtp_server_reply_ehlo_add_enhancedstatuscodes( + struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_ENHANCEDSTATUSCODES) == 0) + return; + smtp_server_reply_ehlo_add(reply, "ENHANCEDSTATUSCODES"); +} + +void smtp_server_reply_ehlo_add_pipelining(struct smtp_server_reply *reply) +{ + smtp_server_reply_ehlo_add(reply, "PIPELINING"); +} + +void smtp_server_reply_ehlo_add_size(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + uoff_t cap_size = conn->set.max_message_size; + + if ((caps & SMTP_CAPABILITY_SIZE) == 0) + return; + + if (cap_size > 0 && cap_size != UOFF_T_MAX) { + smtp_server_reply_ehlo_add_param(reply, + "SIZE", "%"PRIuUOFF_T, cap_size); + } else { + smtp_server_reply_ehlo_add(reply, "SIZE"); + } +} + +void smtp_server_reply_ehlo_add_starttls(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_STARTTLS) == 0) + return; + smtp_server_reply_ehlo_add(reply, "STARTTLS"); +} + +void smtp_server_reply_ehlo_add_vrfy(struct smtp_server_reply *reply) +{ + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + enum smtp_capability caps = conn->set.capabilities; + + if ((caps & SMTP_CAPABILITY_VRFY) == 0) + return; + smtp_server_reply_ehlo_add(reply, "VRFY"); +} + +void smtp_server_reply_ehlo_add_xclient(struct smtp_server_reply *reply) +{ + static const char *base_fields = + "ADDR PORT PROTO HELO LOGIN SESSION TTL TIMEOUT"; + struct smtp_server_cmd_ctx *cmd = &reply->command->context; + struct smtp_server_connection *conn = cmd->conn; + + if (!smtp_server_connection_is_trusted(conn)) + return; + if (conn->set.xclient_extensions == NULL || + *conn->set.xclient_extensions == NULL) { + smtp_server_reply_ehlo_add_param(reply, "XCLIENT", "%s", + base_fields); + return; + } + + smtp_server_reply_ehlo_add_param(reply, "XCLIENT", "%s", + t_strconcat(base_fields, " ", + t_strarray_join(conn->set.xclient_extensions, " "), + NULL)); +} |