bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "lib.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "array.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "str.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "strfuncs.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "istream.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "smtp-parser.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include "smtp-reply-parser.h"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch#include <ctype.h>
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch/* From RFC 5321:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-line = *( Reply-code "-" [ textstring ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-code [ SP textstring ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-code = %x32-35 %x30-35 %x30-39
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Greeting = ( "220 " (Domain / address-literal)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch [ SP textstring ] CRLF ) /
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ( "220-" (Domain / address-literal)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch [ SP textstring ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *( "220-" [ textstring ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "220" [ SP textstring ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch / ( "250-" Domain [ SP ehlo-greet ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *( "250-" ehlo-line CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "250" SP ehlo-line CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; string of any characters other than CR or LF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-line = ehlo-keyword *( SP ehlo-param )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; additional syntax of ehlo-params depends on
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; ehlo-keyword
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-param = 1*(%d33-126)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; any CHAR excluding <SP> and all
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; control characters (US-ASCII 0-31 and 127
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; inclusive)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch From RFC 2034:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch status-code ::= class "." subject "." detail
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch class ::= "2" / "4" / "5"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch subject ::= 1*3digit
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch detail ::= 1*3digit
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschenum smtp_reply_parser_state {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_INIT = 0,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_CODE,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_SEP,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_TEXT,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_EHLO_SPACE,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_EHLO_GREET,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_CR,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_CRLF,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_LF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch};
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstruct smtp_reply_parser_state_data {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch enum smtp_reply_parser_state state;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch unsigned int line;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct smtp_reply *reply;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ARRAY_TYPE(const_string) reply_lines;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch size_t reply_size;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch bool last_line:1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch};
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstruct smtp_reply_parser {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct istream *input;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch size_t max_reply_size;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const unsigned char *begin, *cur, *end;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch string_t *strbuf;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct smtp_reply_parser_state_data state;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch pool_t reply_pool;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch char *error;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch bool enhanced_codes:1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch bool ehlo:1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch};
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic inline void ATTR_FORMAT(2, 3)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschsmtp_reply_parser_error(struct smtp_reply_parser *parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const char *format, ...)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch va_list args;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_free(parser->error);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch va_start(args, format);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->error = i_strdup_vprintf(format, args);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch va_end(args);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstruct smtp_reply_parser *
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschsmtp_reply_parser_init(struct istream *input, size_t max_reply_size)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct smtp_reply_parser *parser;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser = i_new(struct smtp_reply_parser, 1);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->max_reply_size =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch (max_reply_size > 0 ? max_reply_size : (size_t)-1);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->input = input;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_stream_ref(input);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->strbuf = str_new(default_pool, 128);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return parser;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschvoid smtp_reply_parser_deinit(struct smtp_reply_parser **_parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct smtp_reply_parser *parser = *_parser;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *_parser = NULL;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_free(&parser->strbuf);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch pool_unref(&parser->reply_pool);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_stream_unref(&parser->input);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_free(parser->error);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_free(parser);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschvoid smtp_reply_parser_set_stream(struct smtp_reply_parser *parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct istream *input)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_stream_unref(&parser->input);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (input != NULL) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->input = input;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_stream_ref(parser->input);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic void
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschsmtp_reply_parser_restart(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_truncate(parser->strbuf, 0);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch pool_unref(&parser->reply_pool);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_zero(&parser->state);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->reply_pool = pool_alloconly_create("smtp_reply", 256);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply = p_new(parser->reply_pool, struct smtp_reply, 1);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p_array_init(&parser->state.reply_lines, parser->reply_pool, 8);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic int smtp_reply_parse_code
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch(struct smtp_reply_parser *parser, unsigned int *code_r)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const unsigned char *first = parser->cur;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const unsigned char *p;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* Reply-code = %x32-35 %x30-35 %x30-39
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while (parser->cur < parser->end && i_isdigit(*parser->cur))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (str_len(parser->strbuf) + (parser->cur-first) > 3)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_append_n(parser->strbuf, first, parser->cur - first);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (str_len(parser->strbuf) != 3)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p = str_data(parser->strbuf);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (p[0] < '2' || p[0] > '5' || p[1] > '5')
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *code_r = (p[0] - '0')*100 + (p[1] - '0')*10 + (p[2] - '0');
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_truncate(parser->strbuf, 0);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic int smtp_reply_parse_textstring(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const unsigned char *first = parser->cur;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while (parser->cur < parser->end && smtp_char_is_textstr(*parser->cur))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (((parser->cur-first) + parser->state.reply_size +
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_len(parser->strbuf)) > parser->max_reply_size) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Reply exceeds size limit");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_append_n(parser->strbuf, first, parser->cur - first);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic int smtp_reply_parse_ehlo_domain(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const unsigned char *first = parser->cur;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* Domain [ SP ...
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while (parser->cur < parser->end && *parser->cur != ' ' &&
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_char_is_textstr(*parser->cur))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (((parser->cur-first) + parser->state.reply_size +
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_len(parser->strbuf)) > parser->max_reply_size) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Reply exceeds size limit");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_append_n(parser->strbuf, first, parser->cur - first);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic int smtp_reply_parse_ehlo_greet(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const unsigned char *first = parser->cur;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch * The greet is not supposed to be empty, but we don't really care
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (smtp_char_is_ehlo_greet(*parser->cur)) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch for (;;) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while (parser->cur < parser->end &&
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_char_is_textstr(*parser->cur))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (((parser->cur-first) + parser->state.reply_size +
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_len(parser->strbuf)) >
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->max_reply_size) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Reply exceeds size limit");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* sanitize bad characters */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_append_n(parser->strbuf,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch first, parser->cur - first);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (!smtp_char_is_ehlo_greet(*parser->cur))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_append_c(parser->strbuf, ' ');
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch first = parser->cur;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic inline const char *_chr_sanitize(unsigned char c)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (c >= 0x20 && c < 0x7F)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return t_strdup_printf("'%c'", c);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return t_strdup_printf("0x%02x", c);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic void
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschsmtp_reply_parse_enhanced_code(struct smtp_reply_parser *parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const char **pos)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const char *p = *pos;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch unsigned int digits, x, y, z;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch unsigned int prevx = parser->state.reply->enhanced_code.x,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch prevy = parser->state.reply->enhanced_code.y,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch prevz = parser->state.reply->enhanced_code.z;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (prevx == 9)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return; /* failed on earlier line */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->enhanced_code.x = 9;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->enhanced_code.y = 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->enhanced_code.z = 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* status-code ::= class "." subject "." detail
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch class ::= "2" / "4" / "5"
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch subject ::= 1*3digit
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch detail ::= 1*3digit
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* class */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (p[1] != '.' || (p[0] != '2' && p[0] != '4' && p[0] != '5'))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch x = p[0] - '0';
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p += 2;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* subject */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch digits = 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch y = 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while (*p != '\0' && i_isdigit(*p) && digits++ < 3) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch y = y*10 + (*p - '0');
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (digits == 0 || *p != '.')
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* detail */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch digits = 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch z = 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while (*p != '\0' && i_isdigit(*p) && digits++ < 3) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch z = z*10 + (*p - '0');
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (digits == 0 || (*p != ' ' && *p != '\r' && *p != '\n'))
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch p++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* code is syntactically valid; strip code from textstring */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *pos = p;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* check for match with status */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (x != parser->state.reply->status / 100) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* ignore code */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* check for code consistency */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->state.line > 0 &&
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch (prevx != x || prevy != y || prevz != z)) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* ignore code */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->enhanced_code.x = x;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->enhanced_code.y = y;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->enhanced_code.z = z;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic void smtp_reply_parser_finish_line(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const char *text = str_c(parser->strbuf);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->enhanced_codes && str_len(parser->strbuf) > 5) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parse_enhanced_code(parser, &text);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.line++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply_size += str_len(parser->strbuf);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch text = p_strdup(parser->reply_pool, text);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch array_append(&parser->state.reply_lines, &text, 1);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_truncate(parser->strbuf, 0);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic int smtp_reply_parse_more(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch unsigned int status;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch int ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /*
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-line = *( Reply-code "-" [ textstring ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-code [ SP textstring ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-code = %x32-35 %x30-35 %x30-39
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch / ( "250-" Domain [ SP ehlo-greet ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *( "250-" ehlo-line CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "250" SP ehlo-line CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch for (;;) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch switch (parser->state.state) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_INIT:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_restart(parser);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_CODE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* fall through */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* Reply-code */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_CODE:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret=smtp_reply_parse_code(parser, &status)) <= 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (ret < 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Invalid status code in reply");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->state.line == 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->status = status;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch } else if (status != parser->state.reply->status) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Inconsistent status codes in reply");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_SEP;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* fall through */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* "-" / SP / CRLF */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_SEP:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch switch (*parser->cur) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* "-" [ textstring ] CRLF */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case '-':
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.last_line = FALSE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_TEXT;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* SP [ textstring ] CRLF ; allow missing text */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case ' ':
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_TEXT;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.last_line = TRUE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* CRLF */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case '\r':
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case '\n':
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.last_line = TRUE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_CR;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch default:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Encountered unexpected %s after reply status code",
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch _chr_sanitize(*parser->cur));
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->state.state != SMTP_REPLY_PARSE_STATE_TEXT)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* fall through */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* textstring / (Domain [ SP ehlo-greet ]) */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_TEXT:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->ehlo &&
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->status == 250 &&
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.line == 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* handle first line of EHLO success response
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch differently because it can contain control
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch characters (WHY??!) */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret=smtp_reply_parse_ehlo_domain(parser)) <= 0)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_EHLO_SPACE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret=smtp_reply_parse_textstring(parser)) <= 0)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_CR;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* fall through */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* CR */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_CR:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (*parser->cur == '\r') {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_CRLF;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch } else {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_LF;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* fall through */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* CRLF / LF */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_CRLF:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_LF:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (*parser->cur != '\n') {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->state.state ==
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_CRLF) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Encountered stray CR in reply text");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch } else {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Encountered stray %s in reply text",
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch _chr_sanitize(*parser->cur));
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_finish_line(parser);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->state.last_line) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch SMTP_REPLY_PARSE_STATE_INIT;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_CODE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* SP ehlo-greet */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_EHLO_SPACE:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (*parser->cur != ' ') {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_CR;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur++;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch str_append_c(parser->strbuf, ' ');
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_EHLO_GREET;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* fall through */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /* ehlo-greet */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch case SMTP_REPLY_PARSE_STATE_EHLO_GREET:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret=smtp_reply_parse_ehlo_greet(parser)) <= 0)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_CR;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->cur == parser->end)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch break;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch default:
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_unreached();
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_unreached();
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschstatic int smtp_reply_parse(struct smtp_reply_parser *parser)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch size_t size;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch int ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch while ((ret = i_stream_read_more(parser->input,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch &parser->begin, &size)) > 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->cur = parser->begin;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->end = parser->cur + size;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret = smtp_reply_parse_more(parser)) < 0)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return -1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_stream_skip(parser->input, parser->cur - parser->begin);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (ret > 0)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_assert(ret != -2);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (ret < 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_assert(parser->input->eof);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->input->stream_errno == 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if (parser->state.state == SMTP_REPLY_PARSE_STATE_INIT)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 0;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Premature end of input");
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch } else {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch smtp_reply_parser_error(parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "Stream error: %s",
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_stream_get_error(parser->input));
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschint smtp_reply_parse_next(struct smtp_reply_parser *parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch bool enhanced_codes, struct smtp_reply **reply_r,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch const char **error_r)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch int ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_assert(parser->state.state == SMTP_REPLY_PARSE_STATE_INIT ||
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch (parser->enhanced_codes == enhanced_codes && !parser->ehlo));
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->enhanced_codes = enhanced_codes;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->ehlo = FALSE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_free_and_null(parser->error);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /*
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-line = *( Reply-code "-" [ textstring ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-code [ SP textstring ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Reply-code = %x32-35 %x30-35 %x30-39
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch textstring = 1*(%d09 / %d32-126) ; HT, SP, Printable US-ASCII
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch Greeting is not handled specially here.
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret=smtp_reply_parse(parser)) <= 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *error_r = parser->error;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_INIT;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->text_lines =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch array_idx(&parser->state.reply_lines, 0);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *reply_r = parser->state.reply;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Boschint smtp_reply_parse_ehlo(struct smtp_reply_parser *parser,
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch struct smtp_reply **reply_r, const char **error_r)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch{
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch int ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_assert(parser->state.state == SMTP_REPLY_PARSE_STATE_INIT ||
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch (!parser->enhanced_codes && parser->ehlo));
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->enhanced_codes = FALSE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->ehlo = TRUE;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch i_free_and_null(parser->error);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch /*
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch / ( "250-" Domain [ SP ehlo-greet ] CRLF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *( "250-" ehlo-line CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch "250" SP ehlo-line CRLF )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; string of any characters other than CR or LF
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-line = ehlo-keyword *( SP ehlo-param )
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; additional syntax of ehlo-params depends on
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; ehlo-keyword
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ehlo-param = 1*(%d33-126)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; any CHAR excluding <SP> and all
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; control characters (US-ASCII 0-31 and 127
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch ; inclusive)
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch */
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch if ((ret=smtp_reply_parse(parser)) <= 0) {
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *error_r = parser->error;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return ret;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch }
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.state = SMTP_REPLY_PARSE_STATE_INIT;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch parser->state.reply->text_lines =
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch array_idx(&parser->state.reply_lines, 0);
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch *reply_r = parser->state.reply;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch return 1;
a8433392e8a5ddfe8a125716f62b2a4dc8f1f01eStephan Bosch}