bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "lib.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "istream.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "istream-failure-at.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "istream-sized.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "istream-dot.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "smtp-parser.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include "smtp-command-parser.h"
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#include <ctype.h>
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch#define SMTP_COMMAND_PARSER_MAX_COMMAND_LENGTH 32
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschenum smtp_command_parser_state {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_INIT = 0,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_SKIP_LINE,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_SP,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_PARAMETERS,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_CR,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_LF,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_ERROR,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch};
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstruct smtp_command_parser_state_data {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch enum smtp_command_parser_state state;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch char *cmd_name;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch char *cmd_params;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch uoff_t poff;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch};
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstruct smtp_command_parser {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct istream *input;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct smtp_command_limits limits;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const unsigned char *cur, *end;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct istream *data;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct smtp_command_parser_state_data state;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch enum smtp_command_parse_error error_code;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch char *error;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch bool auth_response:1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch};
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic inline void ATTR_FORMAT(3, 4)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parser_error(struct smtp_command_parser *parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch enum smtp_command_parse_error code,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const char *format, ...)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch va_list args;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser->error);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->error_code = code;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch va_start(args, format);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->error = i_strdup_vprintf(format, args);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch va_end(args);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstruct smtp_command_parser *
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parser_init(struct istream *input,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const struct smtp_command_limits *limits)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct smtp_command_parser *parser;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser = i_new(struct smtp_command_parser, 1);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->input = input;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_ref(input);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (limits != NULL)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->limits = *limits;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->limits.max_parameters_size == 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->limits.max_parameters_size =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_DEFAULT_MAX_PARAMETERS_SIZE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->limits.max_auth_size == 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->limits.max_auth_size =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_DEFAULT_MAX_AUTH_SIZE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->limits.max_data_size == 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->limits.max_data_size =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_DEFAULT_MAX_DATA_SIZE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return parser;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschvoid smtp_command_parser_deinit(struct smtp_command_parser **_parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct smtp_command_parser *parser = *_parser;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_unref(&parser->data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser->state.cmd_name);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser->state.cmd_params);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser->error);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_unref(&parser->input);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *_parser = NULL;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic void
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parser_restart(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser->state.cmd_name);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free(parser->state.cmd_params);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_zero(&parser->state);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschvoid smtp_command_parser_set_stream(struct smtp_command_parser *parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct istream *input)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_unref(&parser->input);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (input != NULL) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->input = input;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_ref(parser->input);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic inline const char *_chr_sanitize(unsigned char c)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (c >= 0x20 && c < 0x7F)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return t_strdup_printf("`%c'", c);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (c == 0x0a)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return "<LF>";
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (c == 0x0d)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return "<CR>";
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return t_strdup_printf("<0x%02x>", c);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic int smtp_command_parse_identifier(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const unsigned char *p;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* The commands themselves are alphabetic characters.
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch p = parser->cur + parser->state.poff;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(p <= parser->end);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch while (p < parser->end && i_isalpha(*p))
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch p++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((p - parser->cur) > SMTP_COMMAND_PARSER_MAX_COMMAND_LENGTH) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Command name is too long");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.poff = p - parser->cur;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (p == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.cmd_name = str_ucase(i_strdup_until(parser->cur, p));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur = p;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.poff = 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic int smtp_command_parse_parameters(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const unsigned char *p, *mp;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch uoff_t max_size = (parser->auth_response ?
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->limits.max_auth_size :
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->limits.max_parameters_size);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* We assume parameters to match textstr
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch => HT, SP, Printable US-ASCII
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch p = parser->cur + parser->state.poff;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch while (p < parser->end && smtp_char_is_textstr(*p))
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch p++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (max_size > 0 && (uoff_t)(p - parser->cur) > max_size) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "%s line is too long",
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch (parser->auth_response ?
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "AUTH response" : "Command"));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.poff = p - parser->cur;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (p == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* In the interest of improved interoperability, SMTP receivers SHOULD
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch tolerate trailing white space before the terminating <CRLF>.
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch WSP = SP / HTAB ; white space
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch --> Trim the end of the buffer
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch mp = p;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (mp > parser->cur) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch while (mp > parser->cur && (*(mp-1) == ' ' || *(mp-1) == '\t'))
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch mp--;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (!parser->auth_response && mp > parser->cur && *parser->cur == ' ') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Duplicate space after command name");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.cmd_params = i_strdup_until(parser->cur, mp);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur = p;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.poff = 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic int
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parse_line(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch int ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* RFC 5321, Section 4.1.1:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP commands are character strings terminated by <CRLF>. The
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch commands themselves are alphabetic characters terminated by <SP> if
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parameters follow and <CRLF> otherwise. (In the interest of improved
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch interoperability, SMTP receivers SHOULD tolerate trailing white space
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch before the terminating <CRLF>.)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch for (;;) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch switch (parser->state.state) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_INIT:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_restart(parser);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->auth_response) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* parse AUTH response as bare parameters */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_PARAMETERS;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_COMMAND;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->cur == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->auth_response)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch break;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* fall through */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_COMMAND:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((ret=smtp_command_parse_identifier(parser)) <= 0)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_SP;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->cur == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* fall through */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_SP:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (*parser->cur == '\r') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_CR;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch break;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else if (*parser->cur == '\n') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_LF;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch break;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else if (*parser->cur != ' ') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Unexpected character %s in command name",
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch _chr_sanitize(*parser->cur));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state =
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_STATE_PARAMETERS;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->cur >= parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* fall through */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_PARAMETERS:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((ret=smtp_command_parse_parameters(parser)) <= 0)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_CR;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->cur == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* fall through */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_CR:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (*parser->cur == '\r') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else if (*parser->cur != '\n') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Unexpected character %s in %s",
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch _chr_sanitize(*parser->cur),
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch (parser->auth_response ?
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "AUTH response" :
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "command parameters"));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_LF;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->cur == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* fall through */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_LF:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (*parser->cur != '\n') {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Expected LF after CR at end of %s, "
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "but found %s",
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch (parser->auth_response ?
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "AUTH response" : "command"),
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch _chr_sanitize(*parser->cur));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_INIT;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case SMTP_COMMAND_PARSE_STATE_ERROR:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* skip until end of line */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch while (parser->cur < parser->end &&
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *parser->cur != '\n')
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->cur == parser->end)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur++;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_INIT;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch break;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch default:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_unreached();
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_unreached();
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic int smtp_command_parse(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const unsigned char *begin;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch size_t size, old_bytes = 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch int ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch while ((ret = i_stream_read_data(parser->input, &begin, &size,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch old_bytes)) > 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->cur = begin;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->end = parser->cur + size;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
0ac0c93af1bb5c2aabf953bfebbd7df39665e080Stephan Bosch ret = smtp_command_parse_line(parser);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_skip(parser->input, parser->cur - begin);
0ac0c93af1bb5c2aabf953bfebbd7df39665e080Stephan Bosch if (ret != 0)
0ac0c93af1bb5c2aabf953bfebbd7df39665e080Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch old_bytes = i_stream_get_data_size(parser->input);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret == -2) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* should not really happen */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "%s line is too long",
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch (parser->auth_response ?
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "AUTH response" : "Command"));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret < 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(parser->input->eof);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->input->stream_errno == 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT)
4e4b3ae48b9ce1c397189c263cbd42df1b8c1d87Timo Sirainen ret = -2;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BROKEN_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Premature end of input");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BROKEN_STREAM,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Stream error: %s",
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_get_error(parser->input));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschbool smtp_command_parser_pending_data(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->data == NULL)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return FALSE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return i_stream_have_bytes_left(parser->data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstatic int
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parse_finish_data(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const unsigned char *data;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch size_t size;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch int ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->error = NULL;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->data == NULL)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->data->eof) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_unref(&parser->data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch while ((ret = i_stream_read_data(parser->data, &data, &size, 0)) > 0)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_skip(parser->data, size);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret == 0 || parser->data->stream_errno != 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch switch (parser->data->stream_errno) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case 0:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 0;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case EIO:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BROKEN_COMMAND,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Invalid command data");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch break;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch case EMSGSIZE:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_DATA_TOO_LARGE,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Command data too large");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch break;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch default:
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch smtp_command_parser_error(parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch SMTP_COMMAND_PARSE_ERROR_BROKEN_STREAM,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Stream error while skipping command data: "
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "%s", i_stream_get_error(parser->data));
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return -1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_unref(&parser->data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschint smtp_command_parse_next(struct smtp_command_parser *parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const char **cmd_name_r, const char **cmd_params_r,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch enum smtp_command_parse_error *error_code_r,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const char **error_r)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch int ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(!parser->auth_response ||
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT ||
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state == SMTP_COMMAND_PARSE_STATE_ERROR);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->auth_response = FALSE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_code_r = parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_r = NULL;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free_and_null(parser->error);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* make sure we finished streaming payload from previous command
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch before we continue. */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((ret = smtp_command_parse_finish_data(parser)) <= 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret < 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_code_r = parser->error_code;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_r = parser->error;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((ret=smtp_command_parse(parser)) <= 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret < 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_code_r = parser->error_code;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_r = parser->error;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *cmd_name_r = parser->state.cmd_name;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *cmd_params_r = (parser->state.cmd_params == NULL ?
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "" : parser->state.cmd_params);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstruct istream *
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parse_data_with_size(struct smtp_command_parser *parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch uoff_t size)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(parser->data == NULL);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (size > parser->limits.max_data_size) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* not supposed to happen; command should check size */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->data = i_stream_create_error_str(EMSGSIZE,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Command data size exceeds maximum");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch // FIXME: make exact_size stream type
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->data = i_stream_create_min_sized(
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_create_limit(parser->input, size), size);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_ref(parser->data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return parser->data;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschstruct istream *
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschsmtp_command_parse_data_with_dot(struct smtp_command_parser *parser)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch struct istream *data;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(parser->data == NULL);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch data = i_stream_create_dot(parser->input, TRUE);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (parser->limits.max_data_size != (uoff_t)-1) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->data = i_stream_create_failure_at(
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch data, parser->limits.max_data_size, EMSGSIZE,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch "Command data size exceeds maximum");
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_unref(&data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch } else {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->data = data;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_stream_ref(parser->data);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return parser->data;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Boschint smtp_command_parse_auth_response(struct smtp_command_parser *parser,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const char **line_r,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch enum smtp_command_parse_error *error_code_r,
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch const char **error_r)
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch{
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch int ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(parser->auth_response ||
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT ||
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state == SMTP_COMMAND_PARSE_STATE_ERROR);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->auth_response = TRUE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_code_r = parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_r = NULL;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_free_and_null(parser->error);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch /* make sure we finished streaming payload from previous command
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch before we continue. */
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((ret = smtp_command_parse_finish_data(parser)) <= 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret < 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_code_r = parser->error_code;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_r = parser->error;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if ((ret=smtp_command_parse(parser)) <= 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch if (ret < 0) {
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_code_r = parser->error_code;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *error_r = parser->error;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return ret;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch }
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch i_assert(parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT);
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch *line_r = parser->state.cmd_params;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch parser->auth_response = FALSE;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch return 1;
8141e652481ff9db3bce36fdc1fe04c75a3ba7e3Stephan Bosch}