Coverage Report

Created: 2025-11-03 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-smtp/smtp-command-parser.c
Line
Count
Source
1
/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "buffer.h"
5
#include "unichar.h"
6
#include "safe-memset.h"
7
#include "istream.h"
8
#include "istream-failure-at.h"
9
#include "istream-sized.h"
10
#include "istream-dot.h"
11
12
#include "smtp-parser.h"
13
#include "smtp-command-parser.h"
14
15
#include <ctype.h>
16
17
118k
#define SMTP_COMMAND_PARSER_MAX_COMMAND_LENGTH 32
18
19
enum smtp_command_parser_state {
20
  SMTP_COMMAND_PARSE_STATE_INIT = 0,
21
  SMTP_COMMAND_PARSE_STATE_SKIP_LINE,
22
  SMTP_COMMAND_PARSE_STATE_COMMAND,
23
  SMTP_COMMAND_PARSE_STATE_SP,
24
  SMTP_COMMAND_PARSE_STATE_PARAMETERS,
25
  SMTP_COMMAND_PARSE_STATE_CR,
26
  SMTP_COMMAND_PARSE_STATE_LF,
27
  SMTP_COMMAND_PARSE_STATE_ERROR,
28
};
29
30
struct smtp_command_parser_state_data {
31
  enum smtp_command_parser_state state;
32
33
  char *cmd_name;
34
  char *cmd_params;
35
36
  size_t poff;
37
};
38
39
struct smtp_command_parser {
40
  struct istream *input;
41
42
  struct smtp_command_limits limits;
43
44
  const unsigned char *cur, *end;
45
  buffer_t *line_buffer;
46
  struct istream *data;
47
48
  struct smtp_command_parser_state_data state;
49
50
  enum smtp_command_parse_error error_code;
51
  char *error;
52
53
  bool auth_response:1;
54
  bool auth_response_buffered:1;
55
};
56
57
static inline void ATTR_FORMAT(3, 4)
58
smtp_command_parser_error(struct smtp_command_parser *parser,
59
        enum smtp_command_parse_error code,
60
        const char *format, ...)
61
21.4k
{
62
21.4k
  va_list args;
63
64
21.4k
  parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR;
65
66
21.4k
  i_free(parser->error);
67
21.4k
  parser->error_code = code;
68
69
21.4k
  va_start(args, format);
70
21.4k
  parser->error = i_strdup_vprintf(format, args);
71
21.4k
  va_end(args);
72
21.4k
}
73
74
struct smtp_command_parser *
75
smtp_command_parser_init(struct istream *input,
76
       const struct smtp_command_limits *limits)
77
6.18k
{
78
6.18k
  struct smtp_command_parser *parser;
79
80
6.18k
  parser = i_new(struct smtp_command_parser, 1);
81
6.18k
  parser->input = input;
82
6.18k
  i_stream_ref(input);
83
84
6.18k
  if (limits != NULL)
85
6.18k
    parser->limits = *limits;
86
6.18k
  if (parser->limits.max_parameters_size == 0) {
87
6.18k
    parser->limits.max_parameters_size =
88
6.18k
      SMTP_COMMAND_DEFAULT_MAX_PARAMETERS_SIZE;
89
6.18k
  }
90
6.18k
  if (parser->limits.max_auth_size == 0) {
91
6.18k
    parser->limits.max_auth_size =
92
6.18k
      SMTP_COMMAND_DEFAULT_MAX_AUTH_SIZE;
93
6.18k
  }
94
6.18k
  if (parser->limits.max_data_size == 0) {
95
6.18k
    parser->limits.max_data_size =
96
6.18k
      SMTP_COMMAND_DEFAULT_MAX_DATA_SIZE;
97
6.18k
  }
98
99
6.18k
  return parser;
100
6.18k
}
101
102
void smtp_command_parser_clear(struct smtp_command_parser *parser)
103
125k
{
104
125k
  if (parser->auth_response_buffered) {
105
0
    if (parser->line_buffer != NULL)
106
0
      buffer_clear_safe(parser->line_buffer);
107
0
    if (parser->state.cmd_params != NULL) {
108
0
      safe_memset(parser->state.cmd_params, 0,
109
0
            strlen(parser->state.cmd_params));
110
0
    }
111
0
  }
112
125k
  parser->auth_response_buffered = FALSE;
113
125k
}
114
115
void smtp_command_parser_deinit(struct smtp_command_parser **_parser)
116
6.18k
{
117
6.18k
  struct smtp_command_parser *parser = *_parser;
118
119
6.18k
  smtp_command_parser_clear(parser);
120
121
6.18k
  i_stream_unref(&parser->data);
122
6.18k
  buffer_free(&parser->line_buffer);
123
6.18k
  i_free(parser->state.cmd_name);
124
6.18k
  i_free(parser->state.cmd_params);
125
6.18k
  i_free(parser->error);
126
6.18k
  i_stream_unref(&parser->input);
127
6.18k
  i_free(parser);
128
6.18k
  *_parser = NULL;
129
6.18k
}
130
131
static void smtp_command_parser_restart(struct smtp_command_parser *parser)
132
118k
{
133
118k
  smtp_command_parser_clear(parser);
134
135
118k
  buffer_free(&parser->line_buffer);
136
118k
  i_free(parser->state.cmd_name);
137
118k
  i_free(parser->state.cmd_params);
138
139
118k
  i_zero(&parser->state);
140
118k
}
141
142
void smtp_command_parser_set_stream(struct smtp_command_parser *parser,
143
            struct istream *input)
144
0
{
145
0
  i_stream_unref(&parser->input);
146
0
  if (input != NULL) {
147
0
    parser->input = input;
148
0
    i_stream_ref(parser->input);
149
0
  }
150
0
}
151
152
static inline const char *_chr_sanitize(unsigned char c)
153
11.2k
{
154
11.2k
  if (c >= 0x20 && c < 0x7F)
155
4.26k
    return t_strdup_printf("`%c'", c);
156
6.99k
  if (c == 0x0a)
157
0
    return "<LF>";
158
6.99k
  if (c == 0x0d)
159
538
    return "<CR>";
160
6.45k
  return t_strdup_printf("<0x%02x>", c);
161
6.99k
}
162
163
static int smtp_command_parse_identifier(struct smtp_command_parser *parser)
164
118k
{
165
118k
  const unsigned char *p;
166
167
  /* The commands themselves are alphabetic characters.
168
   */
169
118k
  p = parser->cur + parser->state.poff;
170
118k
  i_assert(p <= parser->end);
171
916k
  while (p < parser->end && i_isalpha(*p))
172
798k
    p++;
173
118k
  if ((p - parser->cur) > SMTP_COMMAND_PARSER_MAX_COMMAND_LENGTH) {
174
734
    smtp_command_parser_error(
175
734
      parser, SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
176
734
      "Command name is too long");
177
734
    return -1;
178
734
  }
179
118k
  parser->state.poff = p - parser->cur;
180
118k
  if (p == parser->end)
181
218
    return 0;
182
117k
  parser->state.cmd_name = i_strdup_until(parser->cur, p);
183
117k
  parser->cur = p;
184
117k
  parser->state.poff = 0;
185
117k
  return 1;
186
118k
}
187
188
static int smtp_command_parse_parameters(struct smtp_command_parser *parser)
189
73.4k
{
190
73.4k
  const unsigned char *p, *mp;
191
73.4k
  size_t max_size = (parser->auth_response ?
192
0
         parser->limits.max_auth_size :
193
73.4k
         parser->limits.max_parameters_size);
194
73.4k
  size_t buf_size = (parser->line_buffer == NULL ?
195
73.4k
         0 : parser->line_buffer->used);
196
73.4k
  int nch = 1;
197
198
73.4k
  i_assert(max_size == 0 || buf_size <= max_size);
199
73.4k
  if (max_size > 0 && buf_size == max_size) {
200
0
    smtp_command_parser_error(
201
0
      parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
202
0
      "%s line is too long",
203
0
      (parser->auth_response ? "AUTH response" : "Command"));
204
0
    return -1;
205
0
  }
206
207
  /* We assume parameters to match textstr (HT, SP, Printable US-ASCII).
208
     For command parameters, we also accept valid UTF-8 characters.
209
   */
210
73.4k
  p = parser->cur + parser->state.poff;
211
6.82M
  while (p < parser->end) {
212
6.82M
    unichar_t ch;
213
214
6.82M
    if (parser->auth_response) {
215
0
      if ((*p & 0x80) != 0x00)
216
0
        break;
217
0
      ch = *p;
218
6.82M
    } else {
219
6.82M
      nch = uni_utf8_get_char_n(p, (size_t)(parser->end - p),
220
6.82M
              &ch);
221
6.82M
    }
222
6.82M
    if (nch == 0)
223
115
      break;
224
6.82M
    if (nch < 0) {
225
3.35k
      smtp_command_parser_error(
226
3.35k
        parser, SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
227
3.35k
        "Invalid UTF-8 character in command parameters");
228
3.35k
      return -1;
229
3.35k
    }
230
6.82M
    if (nch == 1 && !smtp_char_is_textstr((unsigned char)ch))
231
68.9k
      break;
232
6.75M
    p += nch;
233
6.75M
  }
234
70.0k
  if (max_size > 0 && (size_t)(p - parser->cur) > (max_size - buf_size)) {
235
358
    smtp_command_parser_error(
236
358
      parser, SMTP_COMMAND_PARSE_ERROR_LINE_TOO_LONG,
237
358
      "%s line is too long",
238
358
      (parser->auth_response ? "AUTH response" : "Command"));
239
358
    return -1;
240
358
  }
241
69.7k
  parser->state.poff = p - parser->cur;
242
69.7k
  if (p == parser->end || nch == 0) {
243
    /* Parsed up to end of what is currently buffered in the input
244
       stream. */
245
1.10k
    unsigned int ch_size = (p == parser->end ?
246
993
          0 : uni_utf8_char_bytes(*p));
247
1.10k
    size_t max_input = i_stream_get_max_buffer_size(parser->input);
248
249
    /* Move parsed data to parser's line buffer if the input stream
250
       buffer is full. This can happen when the parser's limits
251
       exceed the input stream max buffer size. */
252
1.10k
    if ((parser->state.poff + ch_size) >= max_input) {
253
0
      if (parser->line_buffer == NULL) {
254
0
        buf_size = (max_input < SIZE_MAX / 2 ?
255
0
              max_input * 2 : SIZE_MAX);
256
0
        buf_size = I_MAX(buf_size, 2048);
257
0
        buf_size = I_MIN(buf_size, max_size);
258
259
0
        parser->line_buffer = buffer_create_dynamic(
260
0
          default_pool, buf_size);
261
0
      }
262
0
      if (parser->auth_response)
263
0
        parser->auth_response_buffered = TRUE;
264
0
      buffer_append(parser->line_buffer, parser->cur,
265
0
              (p - parser->cur));
266
267
0
      parser->cur = p;
268
0
      parser->state.poff = 0;
269
0
    }
270
1.10k
    return 0;
271
1.10k
  }
272
273
  /* In the interest of improved interoperability, SMTP receivers SHOULD
274
     tolerate trailing white space before the terminating <CRLF>.
275
276
     WSP =  SP / HTAB ; white space
277
278
     --> Trim the end of the buffer
279
   */
280
68.6k
  mp = p;
281
68.6k
  if (mp > parser->cur) {
282
146k
    while (mp > parser->cur && (*(mp-1) == ' ' || *(mp-1) == '\t'))
283
79.3k
      mp--;
284
67.5k
  }
285
286
68.6k
  if (!parser->auth_response && mp > parser->cur && *parser->cur == ' ') {
287
435
    smtp_command_parser_error(
288
435
      parser, SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
289
435
      "Duplicate space after command name");
290
435
    return -1;
291
435
  }
292
293
68.1k
  if (parser->auth_response)
294
0
    parser->auth_response_buffered = TRUE;
295
68.1k
  if (parser->line_buffer == NULL) {
296
    /* Buffered only in input stream */
297
68.1k
    parser->state.cmd_params = i_strdup_until(parser->cur, mp);
298
68.1k
  } else {
299
    /* Buffered also in the parser */
300
0
    buffer_append(parser->line_buffer, parser->cur,
301
0
            (mp - parser->cur));
302
0
    parser->state.cmd_params =
303
0
      buffer_free_without_data(&parser->line_buffer);
304
0
  }
305
68.1k
  parser->cur = p;
306
68.1k
  parser->state.poff = 0;
307
68.1k
  return 1;
308
68.6k
}
309
310
static int smtp_command_parse_line(struct smtp_command_parser *parser)
311
124k
{
312
124k
  int ret;
313
314
  /* RFC 5321, Section 4.1.1:
315
316
     SMTP commands are character strings terminated by <CRLF>. The
317
     commands themselves are alphabetic characters terminated by <SP> if
318
     parameters follow and <CRLF> otherwise. (In the interest of improved
319
     interoperability, SMTP receivers SHOULD tolerate trailing white space
320
     before the terminating <CRLF>.)
321
   */
322
176k
  for (;;) {
323
176k
    switch (parser->state.state) {
324
118k
    case SMTP_COMMAND_PARSE_STATE_INIT:
325
118k
      smtp_command_parser_restart(parser);
326
118k
      if (parser->auth_response) {
327
        /* Parse AUTH response as bare parameters */
328
0
        parser->state.state =
329
0
          SMTP_COMMAND_PARSE_STATE_PARAMETERS;
330
118k
      } else {
331
118k
        parser->state.state =
332
118k
          SMTP_COMMAND_PARSE_STATE_COMMAND;
333
118k
      }
334
118k
      if (parser->cur == parser->end)
335
97
        return 0;
336
118k
      if (parser->auth_response)
337
0
        break;
338
      /* fall through */
339
118k
    case SMTP_COMMAND_PARSE_STATE_COMMAND:
340
118k
      ret = smtp_command_parse_identifier(parser);
341
118k
      if (ret <= 0)
342
952
        return ret;
343
117k
      parser->state.state = SMTP_COMMAND_PARSE_STATE_SP;
344
117k
      if (parser->cur == parser->end)
345
0
        return 0;
346
      /* fall through */
347
117k
    case SMTP_COMMAND_PARSE_STATE_SP:
348
117k
      if (*parser->cur == '\r') {
349
1.73k
        parser->state.state =
350
1.73k
          SMTP_COMMAND_PARSE_STATE_CR;
351
1.73k
        break;
352
116k
      } else if (*parser->cur == '\n') {
353
35.6k
        parser->state.state =
354
35.6k
          SMTP_COMMAND_PARSE_STATE_LF;
355
35.6k
        break;
356
80.5k
      } else if (*parser->cur != ' ') {
357
7.98k
        smtp_command_parser_error(parser,
358
7.98k
          SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
359
7.98k
          "Unexpected character %s in command name",
360
7.98k
          _chr_sanitize(*parser->cur));
361
7.98k
        return -1;
362
7.98k
      }
363
72.5k
      parser->cur++;
364
72.5k
      parser->state.state =
365
72.5k
        SMTP_COMMAND_PARSE_STATE_PARAMETERS;
366
72.5k
      if (parser->cur >= parser->end)
367
47
        return 0;
368
      /* fall through */
369
73.4k
    case SMTP_COMMAND_PARSE_STATE_PARAMETERS:
370
73.4k
      ret = smtp_command_parse_parameters(parser);
371
73.4k
      if (ret <= 0)
372
5.25k
        return ret;
373
68.1k
      parser->state.state = SMTP_COMMAND_PARSE_STATE_CR;
374
68.1k
      if (parser->cur == parser->end)
375
0
        return 0;
376
      /* fall through */
377
69.9k
    case SMTP_COMMAND_PARSE_STATE_CR:
378
69.9k
      if (*parser->cur == '\r') {
379
1.89k
        parser->cur++;
380
68.0k
      } else if (*parser->cur != '\n') {
381
1.60k
        smtp_command_parser_error(parser,
382
1.60k
          SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
383
1.60k
          "Unexpected character %s in %s",
384
1.60k
          _chr_sanitize(*parser->cur),
385
1.60k
          (parser->auth_response ?
386
0
           "AUTH response" :
387
1.60k
           "command parameters"));
388
1.60k
        return -1;
389
1.60k
      }
390
68.2k
      parser->state.state = SMTP_COMMAND_PARSE_STATE_LF;
391
68.2k
      if (parser->cur == parser->end)
392
47
        return 0;
393
      /* fall through */
394
103k
    case SMTP_COMMAND_PARSE_STATE_LF:
395
103k
      if (*parser->cur != '\n') {
396
1.66k
        smtp_command_parser_error(parser,
397
1.66k
          SMTP_COMMAND_PARSE_ERROR_BAD_COMMAND,
398
1.66k
          "Expected LF after CR at end of %s, "
399
1.66k
          "but found %s",
400
1.66k
          (parser->auth_response ?
401
1.66k
           "AUTH response" : "command"),
402
1.66k
          _chr_sanitize(*parser->cur));
403
1.66k
        return -1;
404
1.66k
      }
405
102k
      parser->cur++;
406
102k
      parser->state.state = SMTP_COMMAND_PARSE_STATE_INIT;
407
102k
      return 1;
408
19.6k
    case SMTP_COMMAND_PARSE_STATE_ERROR:
409
      /* Skip until end of line */
410
33.0M
      while (parser->cur < parser->end &&
411
33.0M
             *parser->cur != '\n')
412
33.0M
        parser->cur++;
413
19.6k
      if (parser->cur == parser->end)
414
4.22k
        return 0;
415
15.4k
      parser->cur++;
416
15.4k
      parser->state.state = SMTP_COMMAND_PARSE_STATE_INIT;
417
15.4k
      break;
418
0
    default:
419
0
      i_unreached();
420
176k
    }
421
176k
  }
422
423
124k
  i_unreached();
424
124k
}
425
426
static int smtp_command_parse(struct smtp_command_parser *parser)
427
123k
{
428
123k
  const unsigned char *begin;
429
123k
  size_t size, old_bytes = 0;
430
123k
  int ret;
431
432
129k
  while ((ret = i_stream_read_data(parser->input, &begin, &size,
433
129k
           old_bytes)) > 0) {
434
124k
    parser->cur = begin;
435
124k
    parser->end = parser->cur + size;
436
437
124k
    ret = smtp_command_parse_line(parser);
438
124k
    i_stream_skip(parser->input, parser->cur - begin);
439
124k
    if (ret != 0)
440
118k
      return ret;
441
5.73k
    old_bytes = i_stream_get_data_size(parser->input);
442
5.73k
  }
443
123k
  i_assert(ret != -2);
444
445
5.27k
  if (ret < 0) {
446
5.13k
    i_assert(parser->input->eof);
447
5.13k
    if (parser->input->stream_errno == 0) {
448
5.13k
      if (parser->state.state ==
449
5.13k
          SMTP_COMMAND_PARSE_STATE_INIT)
450
4.03k
        ret = -2;
451
5.13k
      smtp_command_parser_error(
452
5.13k
        parser, SMTP_COMMAND_PARSE_ERROR_BROKEN_COMMAND,
453
5.13k
        "Premature end of input");
454
5.13k
    } else {
455
0
      smtp_command_parser_error(
456
0
        parser, SMTP_COMMAND_PARSE_ERROR_BROKEN_STREAM,
457
0
        "%s", i_stream_get_disconnect_reason(parser->input));
458
0
    }
459
5.13k
  }
460
5.27k
  return ret;
461
5.27k
}
462
463
bool smtp_command_parser_pending_data(struct smtp_command_parser *parser)
464
121k
{
465
121k
  if (parser->data == NULL)
466
106k
    return FALSE;
467
14.5k
  return i_stream_have_bytes_left(parser->data);
468
121k
}
469
470
static int smtp_command_parse_finish_data(struct smtp_command_parser *parser)
471
123k
{
472
123k
  const unsigned char *data;
473
123k
  size_t size;
474
123k
  int ret;
475
476
123k
  parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE;
477
123k
  parser->error = NULL;
478
479
123k
  if (parser->data == NULL)
480
109k
    return 1;
481
14.0k
  if (parser->data->eof) {
482
10.4k
    i_stream_unref(&parser->data);
483
10.4k
    return 1;
484
10.4k
  }
485
486
6.30k
  while ((ret = i_stream_read_data(parser->data, &data, &size, 0)) > 0)
487
2.77k
    i_stream_skip(parser->data, size);
488
3.53k
  if (ret == 0 || parser->data->stream_errno != 0) {
489
260
    switch (parser->data->stream_errno) {
490
55
    case 0:
491
55
      return 0;
492
0
    case EMSGSIZE:
493
0
      smtp_command_parser_error(
494
0
        parser, SMTP_COMMAND_PARSE_ERROR_DATA_TOO_LARGE,
495
0
        "Command data too large");
496
0
      break;
497
205
    default:
498
205
      smtp_command_parser_error(
499
205
        parser, SMTP_COMMAND_PARSE_ERROR_BROKEN_STREAM,
500
205
        "%s", i_stream_get_disconnect_reason(parser->data));
501
260
    }
502
205
    return -1;
503
260
  }
504
3.27k
  i_stream_unref(&parser->data);
505
3.27k
  return 1;
506
3.53k
}
507
508
int smtp_command_parse_next(struct smtp_command_parser *parser,
509
          bool only_finish_previous,
510
          const char **cmd_name_r, const char **cmd_params_r,
511
          enum smtp_command_parse_error *error_code_r,
512
          const char **error_r)
513
123k
{
514
123k
  int ret;
515
516
123k
  i_assert(!parser->auth_response ||
517
123k
     parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT ||
518
123k
     parser->state.state == SMTP_COMMAND_PARSE_STATE_ERROR);
519
123k
  parser->auth_response = FALSE;
520
521
123k
  *cmd_name_r = NULL;
522
123k
  *cmd_params_r = NULL;
523
123k
  *error_code_r = parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE;
524
123k
  *error_r = NULL;
525
526
123k
  i_free_and_null(parser->error);
527
528
  /* Make sure we finished streaming payload from previous command
529
     before we continue. */
530
123k
  ret = smtp_command_parse_finish_data(parser);
531
123k
  if (ret <= 0) {
532
260
    if (ret < 0) {
533
205
      *error_code_r = parser->error_code;
534
205
      *error_r = parser->error;
535
205
    }
536
260
    return ret;
537
260
  }
538
539
123k
  if (only_finish_previous)
540
0
    return 1;
541
542
123k
  ret = smtp_command_parse(parser);
543
123k
  if (ret <= 0) {
544
21.4k
    if (ret < 0) {
545
21.2k
      *error_code_r = parser->error_code;
546
21.2k
      *error_r = parser->error;
547
21.2k
      parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR;
548
21.2k
    }
549
21.4k
    return ret;
550
21.4k
  }
551
552
123k
  i_assert(parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT);
553
102k
  *cmd_name_r = parser->state.cmd_name;
554
102k
  *cmd_params_r = (parser->state.cmd_params == NULL ?
555
66.4k
       "" : parser->state.cmd_params);
556
102k
  return 1;
557
102k
}
558
559
struct istream *
560
smtp_command_parse_data_with_size(struct smtp_command_parser *parser,
561
          uoff_t size)
562
13.0k
{
563
13.0k
  i_assert(parser->data == NULL);
564
13.0k
  if (size > parser->limits.max_data_size) {
565
    /* Not supposed to happen; command should check size */
566
972
    parser->data = i_stream_create_error_str(EMSGSIZE,
567
972
      "Command data size exceeds maximum "
568
972
      "(%"PRIuUOFF_T" > %"PRIuUOFF_T")",
569
972
      size, parser->limits.max_data_size);
570
12.1k
  } else {
571
    // FIXME: Make exact_size stream type
572
12.1k
    struct istream *limit_input =
573
12.1k
      i_stream_create_limit(parser->input, size);
574
12.1k
    parser->data = i_stream_create_min_sized(limit_input, size);
575
12.1k
    i_stream_unref(&limit_input);
576
12.1k
  }
577
13.0k
  i_stream_ref(parser->data);
578
13.0k
  return parser->data;
579
13.0k
}
580
581
struct istream *
582
smtp_command_parse_data_with_dot(struct smtp_command_parser *parser)
583
1.46k
{
584
1.46k
  struct istream *data;
585
1.46k
  i_assert(parser->data == NULL);
586
587
1.46k
  data = i_stream_create_dot(parser->input, ISTREAM_DOT_NO_TRIM |
588
1.46k
              ISTREAM_DOT_STRICT_EOT);
589
1.46k
  if (parser->limits.max_data_size != UOFF_T_MAX) {
590
1.46k
    parser->data = i_stream_create_failure_at(
591
1.46k
      data, parser->limits.max_data_size, EMSGSIZE,
592
1.46k
      t_strdup_printf("Command data size exceeds maximum "
593
1.46k
          "(> %"PRIuUOFF_T")",
594
1.46k
          parser->limits.max_data_size));
595
1.46k
    i_stream_unref(&data);
596
1.46k
  } else {
597
0
    parser->data = data;
598
0
  }
599
1.46k
  i_stream_ref(parser->data);
600
1.46k
  return parser->data;
601
1.46k
}
602
603
int smtp_command_parse_auth_response(
604
  struct smtp_command_parser *parser, const char **line_r,
605
  enum smtp_command_parse_error *error_code_r, const char **error_r)
606
0
{
607
0
  int ret;
608
609
0
  i_assert(parser->auth_response ||
610
0
     parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT ||
611
0
     parser->state.state == SMTP_COMMAND_PARSE_STATE_ERROR);
612
0
  parser->auth_response = TRUE;
613
614
0
  *error_code_r = parser->error_code = SMTP_COMMAND_PARSE_ERROR_NONE;
615
0
  *error_r = NULL;
616
617
0
  i_free_and_null(parser->error);
618
619
  /* Make sure we finished streaming payload from previous command
620
     before we continue. */
621
0
  ret = smtp_command_parse_finish_data(parser);
622
0
  if (ret <= 0) {
623
0
    if (ret < 0) {
624
0
      *error_code_r = parser->error_code;
625
0
      *error_r = parser->error;
626
0
    }
627
0
    return ret;
628
0
  }
629
630
0
  ret = smtp_command_parse(parser);
631
0
  if (ret <= 0) {
632
0
    if (ret < 0) {
633
0
      *error_code_r = parser->error_code;
634
0
      *error_r = parser->error;
635
0
      parser->state.state = SMTP_COMMAND_PARSE_STATE_ERROR;
636
0
    }
637
0
    return ret;
638
0
  }
639
640
0
  i_assert(parser->state.state == SMTP_COMMAND_PARSE_STATE_INIT);
641
0
  *line_r = parser->state.cmd_params;
642
0
  parser->auth_response = FALSE;
643
0
  return 1;
644
0
}