smtp-server-reply.c revision 20b78f502ff73c8c081a0af138ea403418d7d899
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "lib.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "str.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "array.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "istream.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "ostream.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "smtp-reply.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "smtp-server-private.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch/*
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch * Logging
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic inline void ATTR_FORMAT(2, 3)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschsmtp_server_reply_debug(struct smtp_server_reply *reply,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch const char *format, ...)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct smtp_server_command *command = reply->command;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct smtp_server_connection *conn = command->context.conn;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch const struct smtp_server_settings *set = &conn->set;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_list args;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (set->debug) {
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen va_start(args, format);
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen if (command->replies_expected > 1) {
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen i_debug("%s-server: conn %s: "
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen "command %s; %u reply [%u/%u]: %s",
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen smtp_protocol_name(set->protocol),
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen smtp_server_connection_label(conn),
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_command_label(command),
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->status, reply->index+1, command->replies_expected,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch t_strdup_vprintf(format, args));
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else {
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen i_debug("%s-server: conn %s: "
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen "command %s; %u reply: %s",
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen smtp_protocol_name(set->protocol),
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_connection_label(conn),
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_command_label(command),
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->status, t_strdup_vprintf(format, args));
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_end(args);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen/*
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch * Reply
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void smtp_server_reply_clear(struct smtp_server_reply *reply)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
597db47e06df5ac9dbf80c336383b921fec9d373Timo Sirainen if (reply->command == NULL)
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen return;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_debug(reply, "Destroy");
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen if (reply->text != NULL)
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen str_free(&reply->text);
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen}
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainenstruct smtp_server_reply *
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschsmtp_server_reply_create_index(struct smtp_server_command *cmd,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch unsigned int index, unsigned int status,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch const char *enh_code)
d295cff1b640240cd198b9c8e963c9116ab95510Timo Sirainen{
d295cff1b640240cd198b9c8e963c9116ab95510Timo Sirainen struct smtp_server_reply *reply;
935a2434cd674e9cd17eabb296f84d1d891a23aeTimo Sirainen pool_t pool = cmd->context.pool;
935a2434cd674e9cd17eabb296f84d1d891a23aeTimo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(cmd->replies_expected > 0);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(index < cmd->replies_expected);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen /* RFC 5321, Section 4.2:
9a5980c7bb836f69a63082f4699c30596ea4ee74Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen In the absence of extensions negotiated with the client, SMTP servers
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen MUST NOT send reply codes whose first digits are other than 2, 3, 4,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen or 5. Clients that receive such out-of-range codes SHOULD normally
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen treat them as fatal errors and terminate the mail transaction.
9a5980c7bb836f69a63082f4699c30596ea4ee74Timo Sirainen */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen i_assert(status >= 200 && status < 560);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* RFC 2034, Section 4:
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch All status codes returned by the server must agree with the primary
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch response code, that is, a 2xx response must incorporate a 2.X.X code,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch a 4xx response must incorporate a 4.X.X code, and a 5xx response must
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch incorporate a 5.X.X code.
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch */
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen i_assert(enh_code == NULL || *enh_code == '\0' ||
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch ((unsigned int)(enh_code[0] - '0') == (status / 100)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch && enh_code[1] == '.'));
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (array_is_created(&cmd->replies)) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply = array_idx_modifiable(&cmd->replies, index);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* get rid of any existing reply */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(!reply->sent);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_clear(reply);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch p_array_init(&cmd->replies, pool, cmd->replies_expected);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch array_idx_clear(&cmd->replies, cmd->replies_expected - 1);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply = array_idx_modifiable(&cmd->replies, index);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->index = index;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->command = cmd;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->status = status;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (enh_code == NULL || *enh_code == '\0') {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->status_prefix =
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch p_strdup_printf(pool, "%03u-", status);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else {
5d3e31cae626840561c5313e97eef0272be26ae1Timo Sirainen reply->status_prefix =
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch p_strdup_printf(pool, "%03u-%s ", status, enh_code);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->text = str_new(default_pool, 256);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return reply;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct smtp_server_reply *
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainensmtp_server_reply_create(struct smtp_server_command *cmd,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch unsigned int status, const char *enh_code)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return smtp_server_reply_create_index(cmd, 0, status, enh_code);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct smtp_server_reply *
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschsmtp_server_reply_create_forward(struct smtp_server_command *cmd,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch unsigned int index, const struct smtp_reply *from)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct smtp_server_reply *reply;
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen reply = smtp_server_reply_create_index(cmd, index,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch from->status, smtp_reply_get_enh_code(from));
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen smtp_reply_write(reply->text, from);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return reply;
1a3254b83ba00315cfc47d3c6e99e837914594cfTimo Sirainen}
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainenvoid smtp_server_reply_free(struct smtp_server_command *cmd)
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen{
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen unsigned int i;
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen if (!array_is_created(&cmd->replies))
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen return;
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen for (i = 0; i < cmd->replies_expected; i++) {
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen struct smtp_server_reply *reply =
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen array_idx_modifiable(&cmd->replies, i);
d730192e34fbedbc590a5abc7351e5af5e120c5fTimo Sirainen smtp_server_reply_clear(reply);
d730192e34fbedbc590a5abc7351e5af5e120c5fTimo Sirainen }
d730192e34fbedbc590a5abc7351e5af5e120c5fTimo Sirainen}
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainenvoid smtp_server_reply_add_text(struct smtp_server_reply *reply,
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen const char *text)
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen{
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen i_assert(!reply->submitted);
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen if (*text == '\0')
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen return;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen do {
1a3254b83ba00315cfc47d3c6e99e837914594cfTimo Sirainen const char *p;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply->last_line = str_len(reply->text);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
3ab672903a7ed98263b89180261079870c964831Timo Sirainen p = strchr(text, '\n');
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch str_append(reply->text, reply->status_prefix);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen if (p == NULL) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch str_append(reply->text, text);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen text = NULL;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (p > text && *(p-1) == '\r')
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch str_append_n(reply->text, text, p - text - 1);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch else
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch str_append_n(reply->text, text, p - text);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch text = p + 1;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch str_append(reply->text, "\r\n");
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } while (text != NULL && *text != '\0');
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschvoid smtp_server_reply_submit(struct smtp_server_reply *reply)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(!reply->submitted);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(str_len(reply->text) >= 5);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_debug(reply, "Submitted");
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen reply->command->replies_submitted++;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen reply->submitted = TRUE;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen smtp_server_command_submit_reply(reply->command);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschvoid smtp_server_reply_indexv(struct smtp_server_cmd_ctx *_cmd,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch unsigned int index, unsigned int status, const char *enh_code,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen const char *fmt, va_list args)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen{
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct smtp_server_command *cmd = _cmd->cmd;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct smtp_server_reply *reply;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen reply = smtp_server_reply_create_index(cmd, index, status, enh_code);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen smtp_server_reply_add_text(reply, t_strdup_vprintf(fmt, args));
3ab672903a7ed98263b89180261079870c964831Timo Sirainen smtp_server_reply_submit(reply);
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen}
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainenvoid smtp_server_reply(struct smtp_server_cmd_ctx *_cmd,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen unsigned int status, const char *enh_code, const char *fmt, ...)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen{
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct smtp_server_command *cmd = _cmd->cmd;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen va_list args;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen i_assert(cmd->replies_expected <= 1);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen va_start(args, fmt);
c67f6d09c1aa9d8bfdce4167da8ac009fd2917ceTimo Sirainen smtp_server_reply_indexv(_cmd, 0, status, enh_code, fmt, args);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_end(args);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen}
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainenvoid smtp_server_reply_index(struct smtp_server_cmd_ctx *_cmd,
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen unsigned int index, unsigned int status, const char *enh_code,
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen const char *fmt, ...)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_list args;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen va_start(args, fmt);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_indexv(_cmd, index, status, enh_code, fmt, args);
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen va_end(args);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainenvoid smtp_server_reply_index_forward(struct smtp_server_cmd_ctx *cmd,
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen unsigned int index, const struct smtp_reply *from)
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen{
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen smtp_server_reply_submit(
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen smtp_server_reply_create_forward(cmd->cmd, index, from));
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen}
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainenvoid smtp_server_reply_forward(struct smtp_server_cmd_ctx *_cmd,
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen const struct smtp_reply *from)
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen{
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen struct smtp_server_command *cmd = _cmd->cmd;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(cmd->replies_expected <= 1);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_submit(
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen smtp_server_reply_create_forward(cmd, 0, from));
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void ATTR_FORMAT(4, 0)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschsmtp_server_reply_allv(struct smtp_server_cmd_ctx *_cmd,
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen unsigned int status, const char *enh_code,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch const char *fmt, va_list args)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct smtp_server_command *cmd = _cmd->cmd;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen struct smtp_server_reply *reply;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen const char *text;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen unsigned int i = 0;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* find the first unsent reply */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen if (array_is_created(&cmd->replies)) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch for (; i < cmd->replies_expected; i++) {
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen struct smtp_server_reply *reply =
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen array_idx_modifiable(&cmd->replies, i);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen if (!reply->sent)
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen break;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen }
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen i_assert (i < cmd->replies_expected);
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen }
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* compose the reply text */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch text = t_strdup_vprintf(fmt, args);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* submit remaining replies */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen for (; i < cmd->replies_expected; i++) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply = smtp_server_reply_create_index(cmd,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i, status, enh_code);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_add_text(reply, text);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_submit(reply);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen }
3ab672903a7ed98263b89180261079870c964831Timo Sirainen}
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainenvoid smtp_server_reply_all(struct smtp_server_cmd_ctx *_cmd,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen unsigned int status, const char *enh_code,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen const char *fmt, ...)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen{
3ab672903a7ed98263b89180261079870c964831Timo Sirainen va_list args;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen va_start(args, fmt);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen smtp_server_reply_allv(_cmd, status, enh_code, fmt, args);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen va_end(args);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen}
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainenvoid smtp_server_reply_early(struct smtp_server_cmd_ctx *_cmd,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen unsigned int status, const char *enh_code,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen const char *fmt, ...)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_list args;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch _cmd->cmd->reply_early = TRUE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_start(args, fmt);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_allv(_cmd, status, enh_code, fmt, args);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch va_end(args);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschvoid smtp_server_reply_quit(struct smtp_server_cmd_ctx *_cmd)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct smtp_server_command *cmd = _cmd->cmd;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct smtp_server_reply *reply;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch reply = smtp_server_reply_create(cmd, 221, "2.0.0");
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_add_text(reply, "Bye");
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch smtp_server_reply_submit(reply);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschconst char *smtp_server_reply_get_one_line(struct smtp_server_reply *reply)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
string_t *str;
const char *text, *p;
size_t text_len, prefix_len, line_len;
i_assert(str_len(reply->text) > 0);
prefix_len = strlen(reply->status_prefix);
str = t_str_new(256);
text = str_c(reply->text);
text_len = str_len(reply->text);
for (;;) {
p = strchr(text, '\n');
i_assert(p != NULL && p > text && *(p-1) == '\r');
str_append_n(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, ' ');
}
return str_c(str);
}
static int smtp_server_reply_send_real(struct smtp_server_reply *reply,
const char **error_r)
{
struct smtp_server_command *cmd = reply->command;
struct smtp_server_connection *conn = cmd->context.conn;
const struct smtp_server_settings *set = &conn->set;
struct ostream *output = conn->conn.output;
char *text;
int ret = 0;
*error_r = NULL;
i_assert(str_len(reply->text) > 0);
/* substitute '-' with ' ' in last line */
text = str_c_modifiable(reply->text);
text = text + reply->last_line + 3;
if (text[0] != ' ') {
i_assert(text[0] == '-');
text[0] = ' ';
}
if (o_stream_send(output,
str_data(reply->text), str_len(reply->text)) < 0) {
if (errno != EPIPE && errno != ECONNRESET) {
*error_r = t_strdup_printf("write(%s) failed: %s",
o_stream_get_name(output),
o_stream_get_error(output));
}
ret = -1;
}
if (set->debug) {
smtp_server_reply_debug(reply, "Sent: %s",
smtp_server_reply_get_one_line(reply));
}
return ret;
}
int smtp_server_reply_send(struct smtp_server_reply *reply,
const char **error_r)
{
int ret;
if (reply->sent)
return 0;
T_BEGIN {
ret = smtp_server_reply_send_real(reply, error_r);
} T_END;
reply->sent = TRUE;
return ret;
}
/*
* 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;
reply = smtp_server_reply_create(cmd, 250, "");
str_append(reply->text, reply->status_prefix);
str_append(reply->text, conn->set.hostname);
str_append(reply->text, "\r\n");
return reply;
}
void smtp_server_reply_ehlo_add(struct smtp_server_reply *reply,
const char *keyword)
{
i_assert(!reply->submitted);
reply->last_line = str_len(reply->text);
str_append(reply->text, reply->status_prefix);
str_append(reply->text, keyword);
str_append(reply->text, "\r\n");
}
void smtp_server_reply_ehlo_add_param(struct smtp_server_reply *reply,
const char *keyword, const char *param_fmt, ...)
{
va_list args;
i_assert(!reply->submitted);
reply->last_line = str_len(reply->text);
str_append(reply->text, reply->status_prefix);
str_append(reply->text, keyword);
if (*param_fmt != '\0') {
va_start(args, param_fmt);
str_append_c(reply->text, ' ');
str_vprintfa(reply->text, param_fmt, args);
va_end(args);
}
str_append(reply->text, "\r\n");
}
void smtp_server_reply_ehlo_add_xclient(struct smtp_server_reply *reply)
{
static const char *base_fields =
"ADDR PORT PROTO HELO LOGIN 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));
}