smtp-server-cmd-data.c revision 8794ac44a0f53a38f99c743c6c7244aff8aaea4a
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch/* DATA/BDAT/B... commands */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschbool smtp_server_connection_data_check_state(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (conn->state.data_chunks > 0 && conn->state.data_failed) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch // FIXME: should it even reply anything? RFC is unclear.
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_fail(command, 503, "5.5.0",
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Previous data chunk failed, issue RSET first");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* check valid MAIL */
eda1c4f68f460fe9057a3a7e90249b9fa96e687eStephan Bosch /* Can only decide whether we have valid recipients once there are no
eda1c4f68f460fe9057a3a7e90249b9fa96e687eStephan Bosch pending RCPT commands */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* special handling for LMTP */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (conn->set.protocol == SMTP_PROTOCOL_LMTP) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* check valid RCPT (at least one) */
237ff492dad72389ac4b8f5f44ec334640351b42Stephan Bosch !smtp_server_transaction_has_rcpt(conn->state.trans)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (data_cmd->chunk_size > 0 && data_cmd->chunk_last) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* RFC 2033, Section 4.3:
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch If there were no previously successful RCPT
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch commands in the mail transaction, then the
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch BDAT LAST command returns zero replies.
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* RFC 2033, Section 4.2:
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch The additional restriction is that when there
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch have been no successful RCPT commands in the
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch mail transaction, the DATA command MUST fail
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch with a 503 reply code.
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* check valid RCPT (at least one) */
eda1c4f68f460fe9057a3a7e90249b9fa96e687eStephan Bosch !smtp_server_transaction_has_rcpt(conn->state.trans)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_destroy(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch !smtp_server_command_replied_success(command)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* clean up */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_replied(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_completed(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* reset state */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_chunk_replied(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(smtp_server_command_is_replied(command));
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!smtp_server_command_replied_success(command))
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschcmd_data_chunk_finish(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* re-check transaction state (for BDAT/B... command) */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!smtp_server_connection_data_check_state(cmd))
836ed7eb3d79eb41b28d80d4f59099e54b7cb20aJosef 'Jeff' Sipek "Added %"PRIuUOFF_T" octets", data_cmd->chunk_size);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_input_error(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch unsigned int stream_errno = conn->state.data_input->stream_errno;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!smtp_server_command_is_replied(command)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (stream_errno != EPIPE && stream_errno != ECONNRESET) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Connection lost during data transfer: "
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "read(%s) failed: %s",
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_close(&conn, "Read failure");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Connection lost during data transfer: "
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Remote disconnected");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Remote closed connection unexpectedly");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic int cmd_data_handle_input(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const struct smtp_server_callbacks *callbacks = conn->callbacks;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* continue reading from client */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch ret = callbacks->conn_cmd_data_continue(conn->context,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!i_stream_have_bytes_left(conn->state.data_input)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "End of data");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch !i_stream_have_bytes_left(data_cmd->chunk_input)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "End of chunk");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Not all client data read");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_timeout_stop(cmd->conn);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_timeout_start(cmd->conn);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (conn->state.data_input->stream_errno != 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* command is waiting for external event or it failed */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(smtp_server_command_is_replied(command));
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_input(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_timeout_reset(cmd->conn);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_next(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const struct smtp_server_callbacks *callbacks = conn->callbacks;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* this command is next to send a reply */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(conn->state.pending_mail_cmds == 0 &&
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Command is next to be replied");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* check whether we have had successful mail and rcpt commands */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!smtp_server_connection_data_check_state(cmd))
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* LMTP 'DATA' and 'BDAT LAST' commands need to send one reply
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch per recipient
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (data_cmd->chunk_last && conn->set.protocol == SMTP_PROTOCOL_LMTP) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_DATA);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* chain data streams in the correct order */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "Seen the last chunk");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_stream_chain_append_eof(conn->state.data_chain);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *cmd_temp = command;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch "First chunk");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if ((ret=callbacks->conn_cmd_data_begin(conn->context,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch cmd, conn->state.trans, conn->state.data_input)) < 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(smtp_server_command_is_replied(cmd_temp));
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* command failed */
090abb1975e23dbaebf4b07ba6003a7e54097341Stephan Bosch if (smtp_server_command_is_replied(command)) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* using input from client connection;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch capture I/O event */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_command_input_capture(cmd, cmd_data_input);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_start_input(struct smtp_server_cmd_ctx *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct cmd_data_context *data_cmd = command->data;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch/* DATA command */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschstatic void cmd_data_start(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* called when all previous commands were finished */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(conn->state.pending_mail_cmds == 0 &&
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* check whether we have had successful mail and rcpt commands */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!smtp_server_connection_data_check_state(cmd))
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* don't allow classic DATA when CHUNKING sequence was started before */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_DATA);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* confirm initial success to client */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch smtp_server_connection_reply_immediate(conn, 354, "OK");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* start reading message data from client */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch dot_input = smtp_command_parse_data_with_dot(conn->smtp_parser);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_cmd_data(struct smtp_server_cmd_ctx *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* data = "DATA" CRLF */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch data_cmd = p_new(cmd->pool, struct cmd_data_context, 1);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch/* BDAT/B... commands */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_connection_data_chunk_init(struct smtp_server_cmd_ctx *cmd)
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch data_cmd = p_new(cmd->pool, struct cmd_data_context, 1);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch data_cmd->chunk_first = (conn->state.data_chunks++ == 0);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch command->hook_replied = cmd_data_chunk_replied;
8794ac44a0f53a38f99c743c6c7244aff8aaea4aStephan Bosch if (!conn->state.data_failed && conn->state.data_chain == NULL) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_assert(conn->state.data_chain_input == NULL);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch i_stream_create_chain(&conn->state.data_chain);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschint smtp_server_connection_data_chunk_add(struct smtp_server_cmd_ctx *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct istream *chunk, uoff_t chunk_size, bool chunk_last,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_command *command = cmd->cmd;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (!smtp_server_connection_data_check_state(cmd))
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch input = (data_cmd->chunk_first ? conn->state.data_chain_input : NULL);
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch/* BDAT command */
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Boschvoid smtp_server_cmd_bdat(struct smtp_server_cmd_ctx *cmd,
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch struct smtp_server_connection *conn = cmd->conn;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch const char *const *argv;
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if ((conn->set.capabilities & SMTP_CAPABILITY_CHUNKING) == 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* bdat-cmd = "BDAT" SP chunk-size [ SP end-marker ] CR LF
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch chunk-size = 1*DIGIT
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch end-marker = "LAST"
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch if (argv[0] == NULL || str_to_uoff(argv[0], &size) < 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch 501, "5.5.4", "Invalid chunk size parameter");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch } else if (strcasecmp(argv[1], "LAST") != 0) {
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch 501, "5.5.4", "Invalid end marker parameter");
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch /* read/skip data even in case of error, as long as size is
56dd928c164ec5c0d1158a1760154b58c5f1f6e7Stephan Bosch input = smtp_command_parse_data_with_size(conn->smtp_parser,