Coverage Report

Created: 2023-10-10 06:32

/src/dovecot/src/lib-smtp/smtp-server-reply.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "array.h"
6
#include "istream.h"
7
#include "ostream.h"
8
#include "smtp-address.h"
9
#include "smtp-reply.h"
10
11
#include "smtp-server-private.h"
12
13
/*
14
 * Reply
15
 */
16
17
static void smtp_server_reply_destroy(struct smtp_server_reply *reply)
18
127k
{
19
127k
  if (reply->command == NULL)
20
0
    return;
21
22
127k
  if (reply->event != NULL) {
23
124k
    e_debug(reply->event, "Destroy");
24
124k
    event_unref(&reply->event);
25
124k
  }
26
27
127k
  if (reply->content == NULL)
28
0
    return;
29
127k
  str_free(&reply->content->text);
30
127k
}
31
32
static void smtp_server_reply_clear(struct smtp_server_reply *reply)
33
1.13k
{
34
1.13k
  smtp_server_reply_destroy(reply);
35
1.13k
  if (reply->submitted) {
36
1.13k
    i_assert(reply->command->replies_submitted > 0);
37
1.13k
    reply->command->replies_submitted--;
38
1.13k
  }
39
1.13k
  reply->submitted = FALSE;
40
1.13k
  reply->forwarded = FALSE;
41
1.13k
}
42
43
static void smtp_server_reply_update_event(struct smtp_server_reply *reply)
44
124k
{
45
124k
  struct smtp_server_command *command = reply->command;
46
47
124k
  event_add_int(reply->event, "index", reply->index);
48
124k
  event_add_int(reply->event, "status", reply->content->status);
49
50
124k
  if (command->replies_expected > 1) {
51
0
    event_set_append_log_prefix(reply->event,
52
0
      t_strdup_printf("%u reply [%u/%u]: ",
53
0
          reply->content->status,
54
0
          reply->index+1,
55
0
          command->replies_expected));
56
124k
  } else {
57
124k
    event_set_append_log_prefix(reply->event,
58
124k
      t_strdup_printf("%u reply: ",
59
124k
          reply->content->status));
60
124k
  }
61
124k
}
62
63
static struct smtp_server_reply *
64
smtp_server_reply_alloc(struct smtp_server_command *cmd, unsigned int index)
65
124k
{
66
124k
  struct smtp_server_reply *reply;
67
124k
  pool_t pool = cmd->context.pool;
68
69
124k
  if (array_is_created(&cmd->replies)) {
70
1.13k
    reply = array_idx_modifiable(&cmd->replies, index);
71
    /* get rid of any existing reply */
72
1.13k
    i_assert(!reply->sent);
73
1.13k
    smtp_server_reply_clear(reply);
74
123k
  } else {
75
123k
    p_array_init(&cmd->replies, pool, cmd->replies_expected);
76
123k
    array_idx_clear(&cmd->replies, cmd->replies_expected - 1);
77
123k
    reply = array_idx_modifiable(&cmd->replies, index);
78
123k
  }
79
124k
  reply->event = event_create(cmd->context.event);
80
81
124k
  return reply;
82
124k
}
83
84
static void
85
smtp_server_reply_update_prefix(struct smtp_server_reply *reply,
86
        unsigned int status, const char *enh_code)
87
124k
{
88
124k
  pool_t pool = reply->command->context.pool;
89
124k
  string_t *textbuf, *new_text;
90
124k
  const char *new_prefix, *text, *p;
91
124k
  size_t text_len, prefix_len, line_len;
92
93
124k
  if (enh_code == NULL || *enh_code == '\0') {
94
8.38k
    new_prefix = p_strdup_printf(pool, "%03u-", status);
95
115k
  } else {
96
115k
    new_prefix = p_strdup_printf(pool, "%03u-%s ",
97
115k
               status, enh_code);
98
115k
  }
99
100
124k
  i_assert(reply->content != NULL);
101
124k
  textbuf = reply->content->text;
102
103
124k
  if (textbuf == NULL || str_len(textbuf) == 0) {
104
124k
    reply->content->status_prefix = new_prefix;
105
124k
    return;
106
124k
  }
107
0
  new_text = str_new(default_pool, 256);
108
109
0
  prefix_len = strlen(reply->content->status_prefix);
110
0
  text = str_c(textbuf);
111
0
  text_len = str_len(textbuf);
112
113
0
  i_assert(text_len > prefix_len);
114
0
  text_len -= prefix_len;
115
0
  text += prefix_len;
116
117
0
  for (;;) {
118
0
    reply->content->last_line = str_len(new_text);
119
120
0
    p = strchr(text, '\n');
121
0
    i_assert(p != NULL && p > text && *(p-1) == '\r');
122
0
    p++;
123
124
0
    str_append(new_text, new_prefix);
125
0
    str_append_data(new_text, text, p - text);
126
127
0
    line_len = (size_t)(p - text);
128
0
    i_assert(text_len >= line_len);
129
0
    text_len -= line_len;
130
0
    text = p;
131
132
0
    if (text_len <= prefix_len)
133
0
      break;
134
135
0
    text_len -= prefix_len;
136
0
    text += prefix_len;
137
0
  }
138
139
0
  str_free(&textbuf);
140
0
  reply->content->text = new_text;
141
0
  reply->content->status_prefix = new_prefix;
142
0
}
143
144
void smtp_server_reply_set_status(struct smtp_server_reply *reply,
145
          unsigned int status, const char *enh_code)
146
124k
{
147
124k
  pool_t pool = reply->command->context.pool;
148
149
  /* RFC 5321, Section 4.2:
150
151
     In the absence of extensions negotiated with the client, SMTP servers
152
     MUST NOT send reply codes whose first digits are other than 2, 3, 4,
153
     or 5.  Clients that receive such out-of-range codes SHOULD normally
154
     treat them as fatal errors and terminate the mail transaction.
155
   */
156
124k
  i_assert(status >= 200 && status < 560);
157
158
  /* RFC 2034, Section 4:
159
160
     All status codes returned by the server must agree with the primary
161
     response code, that is, a 2xx response must incorporate a 2.X.X code,
162
     a 4xx response must incorporate a 4.X.X code, and a 5xx response must
163
     incorporate a 5.X.X code.
164
   */
165
124k
  i_assert(enh_code == NULL || *enh_code == '\0' ||
166
124k
    ((unsigned int)(enh_code[0] - '0') == (status / 100)
167
124k
      && enh_code[1] == '.'));
168
169
124k
  if (reply->content->status == status &&
170
124k
      null_strcmp(reply->content->enhanced_code, enh_code) == 0)
171
0
    return;
172
173
124k
  smtp_server_reply_update_prefix(reply, status, enh_code);
174
124k
  reply->content->status = status;
175
124k
  reply->content->enhanced_code = p_strdup(pool, enh_code);
176
124k
}
177
178
unsigned int smtp_server_reply_get_status(struct smtp_server_reply *reply,
179
            const char **enh_code_r)
180
0
{
181
0
  if (enh_code_r != NULL)
182
0
    *enh_code_r = reply->content->enhanced_code;
183
0
  return reply->content->status;
184
0
}
185
186
struct smtp_server_reply *
187
smtp_server_reply_create_index(struct smtp_server_command *cmd,
188
             unsigned int index, unsigned int status,
189
             const char *enh_code)
190
124k
{
191
124k
  struct smtp_server_reply *reply;
192
124k
  pool_t pool = cmd->context.pool;
193
194
124k
  i_assert(cmd->replies_expected > 0);
195
124k
  i_assert(index < cmd->replies_expected);
196
197
124k
  reply = smtp_server_reply_alloc(cmd, index);
198
124k
  reply->index = index;
199
124k
  reply->command = cmd;
200
201
124k
  if (reply->content == NULL)
202
123k
    reply->content = p_new(pool, struct smtp_server_reply_content, 1);
203
124k
  smtp_server_reply_set_status(reply, status, enh_code);
204
124k
  reply->content->text = str_new(default_pool, 256);
205
206
124k
  smtp_server_reply_update_event(reply);
207
208
124k
  return reply;
209
124k
}
210
211
struct smtp_server_reply *
212
smtp_server_reply_create(struct smtp_server_command *cmd,
213
       unsigned int status, const char *enh_code)
214
7.90k
{
215
7.90k
  return smtp_server_reply_create_index(cmd, 0, status, enh_code);
216
7.90k
}
217
218
struct smtp_server_reply *
219
smtp_server_reply_create_forward(struct smtp_server_command *cmd,
220
         unsigned int index,
221
         const struct smtp_reply *from)
222
0
{
223
0
  struct smtp_server_reply *reply;
224
0
  string_t *textbuf;
225
0
  char *text;
226
0
  size_t last_line, i;
227
228
0
  reply = smtp_server_reply_create_index(cmd, index,
229
0
    from->status, smtp_reply_get_enh_code(from));
230
0
  smtp_reply_write(reply->content->text, from);
231
232
0
  i_assert(reply->content != NULL);
233
0
  textbuf = reply->content->text;
234
0
  text = str_c_modifiable(textbuf);
235
236
  /* Find the last line */
237
0
  reply->content->last_line = last_line = 0;
238
0
  for (i = 0; i < str_len(textbuf); i++) {
239
0
    if (text[i] == '\n') {
240
0
      reply->content->last_line = last_line;
241
0
      last_line = i + 1;
242
0
    }
243
0
  }
244
245
  /* Make this reply suitable for further amendment with
246
     smtp_server_reply_add_text() */
247
0
  if ((reply->content->last_line + 3) < str_len(textbuf)) {
248
0
    i_assert(text[reply->content->last_line + 3] == ' ');
249
0
    text[reply->content->last_line + 3] = '-';
250
0
  } else {
251
0
    str_append_c(textbuf, '-');
252
0
  }
253
254
0
  reply->forwarded = TRUE;
255
256
0
  return reply;
257
0
}
258
259
void smtp_server_reply_free(struct smtp_server_command *cmd)
260
127k
{
261
127k
  unsigned int i;
262
263
127k
  if (!array_is_created(&cmd->replies))
264
1.26k
    return;
265
266
252k
  for (i = 0; i < cmd->replies_expected; i++) {
267
126k
    struct smtp_server_reply *reply =
268
126k
      array_idx_modifiable(&cmd->replies, i);
269
126k
    smtp_server_reply_destroy(reply);
270
126k
  }
271
126k
}
272
273
void smtp_server_reply_add_text(struct smtp_server_reply *reply,
274
        const char *text)
275
116k
{
276
116k
  string_t *textbuf = reply->content->text;
277
278
116k
  i_assert(!reply->submitted);
279
280
116k
  if (*text == '\0')
281
0
    return;
282
283
116k
  do {
284
116k
    const char *p;
285
286
116k
    reply->content->last_line = str_len(textbuf);
287
288
116k
    p = strchr(text, '\n');
289
116k
    str_append(textbuf, reply->content->status_prefix);
290
116k
    if (p == NULL) {
291
116k
      str_append(textbuf, text);
292
116k
      text = NULL;
293
116k
    } else {
294
0
      if (p > text && *(p-1) == '\r')
295
0
        str_append_data(textbuf, text, p - text - 1);
296
0
      else
297
0
        str_append_data(textbuf, text, p - text);
298
0
      text = p + 1;
299
0
    }
300
116k
    str_append(textbuf, "\r\n");
301
116k
  } while (text != NULL && *text != '\0');
302
116k
}
303
304
static size_t smtp_server_reply_get_path_len(struct smtp_server_reply *reply)
305
0
{
306
0
  size_t prefix_len = strlen(reply->content->status_prefix);
307
0
  size_t text_len = str_len(reply->content->text), line_len, path_len;
308
0
  const char *text = str_c(reply->content->text);
309
0
  const char *text_end = text + text_len, *line_end;
310
311
0
  i_assert(prefix_len <= text_len);
312
313
0
  line_end = strchr(text, '\r');
314
0
  if (line_end == NULL) {
315
0
    line_end = text_end;
316
0
    line_len = text_len;
317
0
  } else {
318
0
    i_assert(line_end + 1 < text_end);
319
0
    i_assert(*(line_end + 1) == '\n');
320
0
    line_len = line_end - text;
321
0
  }
322
323
0
  if (prefix_len == line_len || text[prefix_len] != '<') {
324
0
    path_len = 0;
325
0
  } else {
326
0
    const char *path_begin = &text[prefix_len], *path_end;
327
328
0
    path_end = strchr(path_begin, '>');
329
0
    if (path_end == NULL || path_end > line_end)
330
0
      path_len = 0;
331
0
    else {
332
0
      i_assert(path_end < line_end);
333
0
      path_end++;
334
0
      path_len = path_end - path_begin;
335
0
      if (path_end < line_end && *path_end != ' ')
336
0
        path_len = 0;
337
0
    }
338
0
  }
339
340
0
  i_assert(prefix_len + path_len <= text_len);
341
0
  return path_len;
342
0
}
343
344
void smtp_server_reply_prepend_text(struct smtp_server_reply *reply,
345
            const char *text_prefix)
346
0
{
347
0
  const char *text = str_c(reply->content->text);
348
0
  size_t tlen = str_len(reply->content->text), offset;
349
350
0
  i_assert(!reply->sent);
351
0
  i_assert(reply->content != NULL);
352
0
  i_assert(reply->content->text != NULL);
353
354
0
  offset = strlen(reply->content->status_prefix) +
355
0
    smtp_server_reply_get_path_len(reply);
356
0
  i_assert(offset < tlen);
357
0
  if (text[offset] == ' ')
358
0
    offset++;
359
360
0
  str_insert(reply->content->text, offset, text_prefix);
361
362
0
  if (reply->content->last_line > 0)
363
0
    reply->content->last_line += strlen(text_prefix);
364
0
}
365
366
void smtp_server_reply_replace_path(struct smtp_server_reply *reply,
367
            struct smtp_address *path, bool add)
368
0
{
369
0
  size_t prefix_len, path_len;
370
0
  const char *path_text;
371
372
0
  i_assert(!reply->sent);
373
0
  i_assert(reply->content != NULL);
374
0
  i_assert(reply->content->text != NULL);
375
376
0
  prefix_len = strlen(reply->content->status_prefix);
377
0
  path_len = smtp_server_reply_get_path_len(reply);
378
379
0
  if (path_len > 0) {
380
0
    path_text = smtp_address_encode_path(path);
381
0
    str_replace(reply->content->text, prefix_len, path_len,
382
0
          path_text);
383
0
    if (reply->content->last_line > 0) {
384
0
      i_assert(reply->content->last_line > path_len);
385
0
      reply->content->last_line -= path_len;
386
0
      reply->content->last_line += strlen(path_text);
387
0
    }
388
0
  } else if (add) {
389
0
    path_text = t_strdup_printf(
390
0
      "<%s> ", smtp_address_encode(path));
391
0
    str_insert(reply->content->text, prefix_len, path_text);
392
0
    if (reply->content->last_line > 0)
393
0
      reply->content->last_line += strlen(path_text);
394
0
  }
395
0
}
396
397
void smtp_server_reply_submit(struct smtp_server_reply *reply)
398
124k
{
399
124k
  i_assert(!reply->submitted);
400
124k
  i_assert(reply->content != NULL);
401
124k
  i_assert(str_len(reply->content->text) >= 5);
402
124k
  e_debug(reply->event, "Submitted");
403
404
124k
  reply->command->replies_submitted++;
405
124k
  reply->submitted = TRUE;
406
124k
  smtp_server_command_submit_reply(reply->command);
407
124k
}
408
409
void smtp_server_reply_submit_duplicate(struct smtp_server_cmd_ctx *_cmd,
410
          unsigned int index,
411
          unsigned int from_index)
412
0
{
413
0
  struct smtp_server_command *cmd = _cmd->cmd;
414
0
  struct smtp_server_reply *reply, *from_reply;
415
416
0
  i_assert(cmd->replies_expected > 0);
417
0
  i_assert(index < cmd->replies_expected);
418
0
  i_assert(from_index < cmd->replies_expected);
419
0
  i_assert(array_is_created(&cmd->replies));
420
421
0
  from_reply = array_idx_modifiable(&cmd->replies, from_index);
422
0
  i_assert(from_reply->content != NULL);
423
0
  i_assert(from_reply->submitted);
424
425
0
  reply = smtp_server_reply_alloc(cmd, index);
426
0
  reply->index = index;
427
0
  reply->command = cmd;
428
0
  reply->content = from_reply->content;
429
0
  smtp_server_reply_update_event(reply);
430
431
0
  smtp_server_reply_submit(reply);
432
0
}
433
434
void smtp_server_reply_indexv(struct smtp_server_cmd_ctx *_cmd,
435
            unsigned int index, unsigned int status,
436
            const char *enh_code,
437
            const char *fmt, va_list args)
438
114k
{
439
114k
  struct smtp_server_command *cmd = _cmd->cmd;
440
114k
  struct smtp_server_reply *reply;
441
442
114k
  reply = smtp_server_reply_create_index(cmd, index, status, enh_code);
443
114k
  smtp_server_reply_add_text(reply, t_strdup_vprintf(fmt, args));
444
114k
  smtp_server_reply_submit(reply);
445
114k
}
446
447
void smtp_server_reply(struct smtp_server_cmd_ctx *_cmd, unsigned int status,
448
           const char *enh_code, const char *fmt, ...)
449
55.4k
{
450
55.4k
  struct smtp_server_command *cmd = _cmd->cmd;
451
55.4k
  va_list args;
452
453
55.4k
  i_assert(cmd->replies_expected <= 1);
454
455
55.4k
  va_start(args, fmt);
456
55.4k
  smtp_server_reply_indexv(_cmd, 0, status, enh_code, fmt, args);
457
55.4k
  va_end(args);
458
55.4k
}
459
460
void smtp_server_reply_index(struct smtp_server_cmd_ctx *_cmd,
461
           unsigned int index, unsigned int status,
462
           const char *enh_code, const char *fmt, ...)
463
0
{
464
0
  va_list args;
465
466
0
  va_start(args, fmt);
467
0
  smtp_server_reply_indexv(_cmd, index, status, enh_code, fmt, args);
468
0
  va_end(args);
469
0
}
470
471
void smtp_server_reply_index_forward(struct smtp_server_cmd_ctx *cmd,
472
             unsigned int index,
473
             const struct smtp_reply *from)
474
0
{
475
0
  smtp_server_reply_submit(
476
0
    smtp_server_reply_create_forward(cmd->cmd, index, from));
477
0
}
478
479
void smtp_server_reply_forward(struct smtp_server_cmd_ctx *_cmd,
480
             const struct smtp_reply *from)
481
0
{
482
0
  struct smtp_server_command *cmd = _cmd->cmd;
483
484
0
  i_assert(cmd->replies_expected <= 1);
485
486
0
  smtp_server_reply_submit(
487
0
    smtp_server_reply_create_forward(cmd, 0, from));
488
0
}
489
490
static void ATTR_FORMAT(4, 0)
491
smtp_server_reply_allv(struct smtp_server_cmd_ctx *_cmd,
492
           unsigned int status, const char *enh_code,
493
           const char *fmt, va_list args)
494
1.82k
{
495
1.82k
  struct smtp_server_command *cmd = _cmd->cmd;
496
1.82k
  struct smtp_server_reply *reply;
497
1.82k
  const char *text;
498
1.82k
  unsigned int first, i = 0;
499
500
  /* find the first unsent reply */
501
1.82k
  if (array_is_created(&cmd->replies)) {
502
0
    for (; i < cmd->replies_expected; i++) {
503
0
      struct smtp_server_reply *reply =
504
0
        array_idx_modifiable(&cmd->replies, i);
505
0
      if (!reply->sent)
506
0
        break;
507
0
    }
508
0
    i_assert (i < cmd->replies_expected);
509
0
  }
510
1.82k
  first = i++;
511
512
  /* compose the reply text */
513
1.82k
  text = t_strdup_vprintf(fmt, args);
514
515
  /* submit the first remaining reply */
516
1.82k
  reply = smtp_server_reply_create_index(cmd, first, status, enh_code);
517
1.82k
  smtp_server_reply_add_text(reply, text);
518
1.82k
  smtp_server_reply_submit(reply);
519
520
  /* duplicate the rest from it */
521
1.82k
  for (; i < cmd->replies_expected; i++)
522
0
    smtp_server_reply_submit_duplicate(_cmd, i, first);
523
1.82k
}
524
525
void smtp_server_reply_all(struct smtp_server_cmd_ctx *_cmd,
526
         unsigned int status, const char *enh_code,
527
         const char *fmt, ...)
528
1.82k
{
529
1.82k
  va_list args;
530
531
1.82k
  va_start(args, fmt);
532
1.82k
  smtp_server_reply_allv(_cmd, status, enh_code, fmt, args);
533
1.82k
  va_end(args);
534
1.82k
}
535
536
void smtp_server_reply_early(struct smtp_server_cmd_ctx *_cmd,
537
           unsigned int status, const char *enh_code,
538
           const char *fmt, ...)
539
0
{
540
0
  va_list args;
541
542
0
  _cmd->cmd->reply_early = TRUE;
543
544
0
  va_start(args, fmt);
545
0
  smtp_server_reply_allv(_cmd, status, enh_code, fmt, args);
546
0
  va_end(args);
547
0
}
548
549
void smtp_server_reply_quit(struct smtp_server_cmd_ctx *_cmd)
550
79
{
551
79
  struct smtp_server_command *cmd = _cmd->cmd;
552
79
  struct smtp_server_reply *reply;
553
554
79
  reply = smtp_server_reply_create(cmd, 221, "2.0.0");
555
79
  smtp_server_reply_add_text(reply, "Bye");
556
79
  smtp_server_reply_submit(reply);
557
79
}
558
559
static void
560
smtp_server_reply_write_one_line(const struct smtp_server_reply *reply,
561
         string_t *str, bool skip_status)
562
74.4k
{
563
74.4k
  string_t *textbuf;
564
74.4k
  const char *text, *p;
565
74.4k
  size_t text_len, prefix_len, line_len;
566
567
74.4k
  i_assert(reply->content != NULL);
568
74.4k
  textbuf = reply->content->text;
569
74.4k
  i_assert(str_len(textbuf) > 0);
570
571
74.4k
  prefix_len = strlen(reply->content->status_prefix);
572
74.4k
  text = str_c(textbuf);
573
74.4k
  text_len = str_len(textbuf);
574
575
74.4k
  if (skip_status) {
576
74.4k
    i_assert(text_len > prefix_len);
577
74.4k
    text_len -= prefix_len;
578
74.4k
    text += prefix_len;
579
74.4k
  }
580
581
74.4k
  for (;;) {
582
74.4k
    p = strchr(text, '\n');
583
74.4k
    i_assert(p != NULL && p > text && *(p-1) == '\r');
584
74.4k
    str_append_data(str, text, p - text - 1);
585
74.4k
    line_len = (size_t)(p - text) + 1;
586
74.4k
    i_assert(text_len >= line_len);
587
74.4k
    text_len -= line_len;
588
74.4k
    text = p + 1;
589
590
74.4k
    if (text_len <= prefix_len)
591
74.4k
      break;
592
593
0
    text_len -= prefix_len;
594
0
    text += prefix_len;
595
0
    str_append_c(str, ' ');
596
0
  }
597
74.4k
}
598
599
const char *
600
smtp_server_reply_get_one_line(const struct smtp_server_reply *reply)
601
0
{
602
0
  string_t *str = t_str_new(256);
603
604
0
  smtp_server_reply_write_one_line(reply, str, FALSE);
605
0
  return str_c(str);
606
0
}
607
608
const char *
609
smtp_server_reply_get_message(const struct smtp_server_reply *reply)
610
74.4k
{
611
74.4k
  string_t *str = t_str_new(256);
612
613
74.4k
  smtp_server_reply_write_one_line(reply, str, TRUE);
614
74.4k
  return str_c(str);
615
74.4k
}
616
617
static int smtp_server_reply_send_real(struct smtp_server_reply *reply)
618
119k
{
619
119k
  struct smtp_server_command *cmd = reply->command;
620
119k
  struct smtp_server_connection *conn = cmd->context.conn;
621
119k
  struct ostream *output = conn->conn.output;
622
119k
  string_t *textbuf;
623
119k
  char *text;
624
119k
  int ret = 0;
625
626
119k
  i_assert(reply->content != NULL);
627
119k
  textbuf = reply->content->text;
628
119k
  i_assert(str_len(textbuf) > 0);
629
630
  /* substitute '-' with ' ' in last line */
631
119k
  text = str_c_modifiable(textbuf);
632
119k
  text = text + reply->content->last_line + 3;
633
119k
  if (text[0] != ' ') {
634
119k
    i_assert(text[0] == '-');
635
119k
    text[0] = ' ';
636
119k
  }
637
638
119k
  if (o_stream_send(output, str_data(textbuf), str_len(textbuf)) < 0) {
639
0
    e_debug(reply->event, "Send failed: %s",
640
0
      o_stream_get_disconnect_reason(output));
641
0
    smtp_server_connection_handle_output_error(conn);
642
0
    return -1;
643
0
  }
644
645
119k
  e_debug(reply->event, "Sent: %s",
646
119k
    smtp_server_reply_get_one_line(reply));
647
119k
  return ret;
648
119k
}
649
650
int smtp_server_reply_send(struct smtp_server_reply *reply)
651
119k
{
652
119k
  int ret;
653
654
119k
  if (reply->sent)
655
0
    return 0;
656
657
119k
  T_BEGIN {
658
119k
    ret = smtp_server_reply_send_real(reply);
659
119k
  } T_END;
660
661
119k
  reply->sent = TRUE;
662
119k
  return ret;
663
119k
}
664
665
bool smtp_server_reply_is_success(const struct smtp_server_reply *reply)
666
229k
{
667
229k
  i_assert(reply->content != NULL);
668
229k
  return (reply->content->status / 100 == 2);
669
229k
}
670
671
void smtp_server_reply_add_to_event(const struct smtp_server_reply *reply,
672
            struct event_passthrough *e)
673
124k
{
674
124k
  i_assert(reply->content != NULL);
675
124k
  e->add_int("status_code", reply->content->status);
676
124k
  if (reply->content->enhanced_code != NULL &&
677
124k
      reply->content->enhanced_code[0] != '\0')
678
116k
    e->add_str("enhanced_code", reply->content->enhanced_code);
679
124k
  if (!smtp_server_reply_is_success(reply))
680
74.4k
    e->add_str("error", smtp_server_reply_get_message(reply));
681
124k
}
682
683
/*
684
 * EHLO reply
685
 */
686
687
struct smtp_server_reply *
688
smtp_server_reply_create_ehlo(struct smtp_server_command *cmd)
689
7.82k
{
690
7.82k
  struct smtp_server_connection *conn = cmd->context.conn;
691
7.82k
  struct smtp_server_reply *reply;
692
7.82k
  string_t *textbuf;
693
694
7.82k
  reply = smtp_server_reply_create(cmd, 250, "");
695
7.82k
  textbuf = reply->content->text;
696
7.82k
  str_append(textbuf, reply->content->status_prefix);
697
7.82k
  str_append(textbuf, conn->set.hostname);
698
7.82k
  str_append(textbuf, "\r\n");
699
700
7.82k
  return reply;
701
7.82k
}
702
703
void smtp_server_reply_ehlo_add(struct smtp_server_reply *reply,
704
        const char *keyword)
705
34.7k
{
706
34.7k
  string_t *textbuf;
707
708
34.7k
  i_assert(!reply->submitted);
709
34.7k
  i_assert(reply->content != NULL);
710
34.7k
  textbuf = reply->content->text;
711
712
34.7k
  reply->content->last_line = str_len(textbuf);
713
34.7k
  str_append(textbuf, reply->content->status_prefix);
714
34.7k
  str_append(textbuf, keyword);
715
34.7k
  str_append(textbuf, "\r\n");
716
34.7k
}
717
718
void smtp_server_reply_ehlo_add_param(struct smtp_server_reply *reply,
719
              const char *keyword,
720
              const char *param_fmt, ...)
721
0
{
722
0
  va_list args;
723
0
  string_t *textbuf;
724
725
0
  i_assert(!reply->submitted);
726
0
  i_assert(reply->content != NULL);
727
0
  textbuf = reply->content->text;
728
729
0
  reply->content->last_line = str_len(textbuf);
730
0
  str_append(textbuf, reply->content->status_prefix);
731
0
  str_append(textbuf, keyword);
732
0
  if (*param_fmt != '\0') {
733
0
    va_start(args, param_fmt);
734
0
    str_append_c(textbuf, ' ');
735
0
    str_vprintfa(textbuf, param_fmt, args);
736
0
    va_end(args);
737
0
  }
738
0
  str_append(textbuf, "\r\n");
739
0
}
740
741
void smtp_server_reply_ehlo_add_params(struct smtp_server_reply *reply,
742
               const char *keyword,
743
               const char *const *params)
744
0
{
745
0
  string_t *textbuf;
746
747
0
  i_assert(!reply->submitted);
748
0
  i_assert(reply->content != NULL);
749
0
  textbuf = reply->content->text;
750
751
0
  reply->content->last_line = str_len(textbuf);
752
0
  str_append(textbuf, reply->content->status_prefix);
753
0
  str_append(textbuf, keyword);
754
0
  if (params != NULL) {
755
0
    while (*params != NULL) {
756
0
      str_append_c(textbuf, ' ');
757
0
      str_append(textbuf, *params);
758
0
      params++;
759
0
    }
760
0
  }
761
0
  str_append(textbuf, "\r\n");
762
0
}
763
764
void smtp_server_reply_ehlo_add_8bitmime(struct smtp_server_reply *reply)
765
6.95k
{
766
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
767
6.95k
  struct smtp_server_connection *conn = cmd->conn;
768
6.95k
  enum smtp_capability caps = conn->set.capabilities;
769
770
6.95k
  if ((caps & SMTP_CAPABILITY_8BITMIME) == 0)
771
0
    return;
772
6.95k
  smtp_server_reply_ehlo_add(reply, "8BITMIME");
773
6.95k
}
774
775
void smtp_server_reply_ehlo_add_binarymime(struct smtp_server_reply *reply)
776
6.95k
{
777
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
778
6.95k
  struct smtp_server_connection *conn = cmd->conn;
779
6.95k
  enum smtp_capability caps = conn->set.capabilities;
780
781
6.95k
  if ((caps & SMTP_CAPABILITY_BINARYMIME) == 0 ||
782
6.95k
      (caps & SMTP_CAPABILITY_CHUNKING) == 0)
783
6.95k
    return;
784
0
  smtp_server_reply_ehlo_add(reply, "BINARYMIME");
785
0
}
786
787
void smtp_server_reply_ehlo_add_chunking(struct smtp_server_reply *reply)
788
6.95k
{
789
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
790
6.95k
  struct smtp_server_connection *conn = cmd->conn;
791
6.95k
  enum smtp_capability caps = conn->set.capabilities;
792
793
6.95k
  if ((caps & SMTP_CAPABILITY_CHUNKING) == 0)
794
0
    return;
795
6.95k
  smtp_server_reply_ehlo_add(reply, "CHUNKING");
796
6.95k
}
797
798
void smtp_server_reply_ehlo_add_dsn(struct smtp_server_reply *reply)
799
6.95k
{
800
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
801
6.95k
  struct smtp_server_connection *conn = cmd->conn;
802
6.95k
  enum smtp_capability caps = conn->set.capabilities;
803
804
6.95k
  if ((caps & SMTP_CAPABILITY_DSN) == 0)
805
6.95k
    return;
806
0
  smtp_server_reply_ehlo_add(reply, "DSN");
807
0
}
808
809
void smtp_server_reply_ehlo_add_enhancedstatuscodes(
810
  struct smtp_server_reply *reply)
811
6.95k
{
812
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
813
6.95k
  struct smtp_server_connection *conn = cmd->conn;
814
6.95k
  enum smtp_capability caps = conn->set.capabilities;
815
816
6.95k
  if ((caps & SMTP_CAPABILITY_ENHANCEDSTATUSCODES) == 0)
817
0
    return;
818
6.95k
  smtp_server_reply_ehlo_add(reply, "ENHANCEDSTATUSCODES");
819
6.95k
}
820
821
void smtp_server_reply_ehlo_add_pipelining(struct smtp_server_reply *reply)
822
6.95k
{
823
6.95k
  smtp_server_reply_ehlo_add(reply, "PIPELINING");
824
6.95k
}
825
826
void smtp_server_reply_ehlo_add_size(struct smtp_server_reply *reply)
827
6.95k
{
828
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
829
6.95k
  struct smtp_server_connection *conn = cmd->conn;
830
6.95k
  enum smtp_capability caps = conn->set.capabilities;
831
6.95k
  uoff_t cap_size = conn->set.max_message_size;
832
833
6.95k
  if ((caps & SMTP_CAPABILITY_SIZE) == 0)
834
0
    return;
835
836
6.95k
  if (cap_size > 0 && cap_size != UOFF_T_MAX) {
837
0
    smtp_server_reply_ehlo_add_param(reply,
838
0
      "SIZE", "%"PRIuUOFF_T, cap_size);
839
6.95k
  } else {
840
6.95k
    smtp_server_reply_ehlo_add(reply, "SIZE");
841
6.95k
  }
842
6.95k
}
843
844
void smtp_server_reply_ehlo_add_starttls(struct smtp_server_reply *reply)
845
6.95k
{
846
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
847
6.95k
  struct smtp_server_connection *conn = cmd->conn;
848
6.95k
  enum smtp_capability caps = conn->set.capabilities;
849
850
6.95k
  if ((caps & SMTP_CAPABILITY_STARTTLS) == 0)
851
6.95k
    return;
852
0
  smtp_server_reply_ehlo_add(reply, "STARTTLS");
853
0
}
854
855
void smtp_server_reply_ehlo_add_vrfy(struct smtp_server_reply *reply)
856
6.95k
{
857
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
858
6.95k
  struct smtp_server_connection *conn = cmd->conn;
859
6.95k
  enum smtp_capability caps = conn->set.capabilities;
860
861
6.95k
  if ((caps & SMTP_CAPABILITY_VRFY) == 0)
862
6.95k
    return;
863
0
  smtp_server_reply_ehlo_add(reply, "VRFY");
864
0
}
865
866
void smtp_server_reply_ehlo_add_xclient(struct smtp_server_reply *reply)
867
6.95k
{
868
6.95k
  static const char *base_fields =
869
6.95k
    "ADDR PORT PROTO HELO LOGIN SESSION CLIENT-TRANSPORT TTL TIMEOUT";
870
6.95k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
871
6.95k
  struct smtp_server_connection *conn = cmd->conn;
872
873
6.95k
  if (!smtp_server_connection_is_trusted(conn))
874
6.95k
    return;
875
0
  if (conn->set.xclient_extensions == NULL ||
876
0
      *conn->set.xclient_extensions == NULL) {
877
0
    smtp_server_reply_ehlo_add_param(reply, "XCLIENT", "%s",
878
0
      base_fields);
879
0
    return;
880
0
  }
881
882
0
  smtp_server_reply_ehlo_add_param(reply, "XCLIENT", "%s",
883
0
    t_strconcat(base_fields, " ",
884
0
      t_strarray_join(conn->set.xclient_extensions, " "),
885
0
      NULL));
886
0
}