Coverage Report

Created: 2026-10-17 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-smtp/smtp-server-reply.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level 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
120k
{
19
120k
  if (reply->command == NULL)
20
0
    return;
21
22
120k
  if (reply->event != NULL) {
23
117k
    e_debug(reply->event, "Destroy");
24
117k
    event_unref(&reply->event);
25
117k
  }
26
27
120k
  if (reply->content == NULL)
28
0
    return;
29
120k
  str_free(&reply->content->text);
30
120k
}
31
32
static void smtp_server_reply_clear(struct smtp_server_reply *reply)
33
1.18k
{
34
1.18k
  smtp_server_reply_destroy(reply);
35
1.18k
  if (reply->submitted) {
36
1.18k
    i_assert(reply->command->replies_submitted > 0);
37
1.18k
    reply->command->replies_submitted--;
38
1.18k
  }
39
1.18k
  reply->submitted = FALSE;
40
1.18k
  reply->forwarded = FALSE;
41
1.18k
}
42
43
static void smtp_server_reply_update_event(struct smtp_server_reply *reply)
44
117k
{
45
117k
  struct smtp_server_command *command = reply->command;
46
47
117k
  event_add_int(reply->event, "index", reply->index);
48
117k
  event_add_int(reply->event, "status", reply->content->status);
49
50
117k
  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
117k
  } else {
57
117k
    event_set_append_log_prefix(reply->event,
58
117k
      t_strdup_printf("%u reply: ",
59
117k
          reply->content->status));
60
117k
  }
61
117k
}
62
63
static struct smtp_server_reply *
64
smtp_server_reply_alloc(struct smtp_server_command *cmd, unsigned int index)
65
117k
{
66
117k
  struct smtp_server_reply *reply;
67
117k
  pool_t pool = cmd->context.pool;
68
69
117k
  if (array_is_created(&cmd->replies)) {
70
1.18k
    reply = array_idx_modifiable(&cmd->replies, index);
71
    /* get rid of any existing reply */
72
1.18k
    i_assert(!reply->sent);
73
1.18k
    smtp_server_reply_clear(reply);
74
116k
  } else {
75
116k
    p_array_init(&cmd->replies, pool, cmd->replies_expected);
76
116k
    array_idx_clear(&cmd->replies, cmd->replies_expected - 1);
77
116k
    reply = array_idx_modifiable(&cmd->replies, index);
78
116k
  }
79
117k
  reply->event = event_create(cmd->context.event);
80
81
117k
  return reply;
82
117k
}
83
84
static void
85
smtp_server_reply_update_prefix(struct smtp_server_reply *reply,
86
        unsigned int status, const char *enh_code)
87
117k
{
88
117k
  pool_t pool = reply->command->context.pool;
89
117k
  string_t *textbuf, *new_text;
90
117k
  const char *new_prefix, *text, *p;
91
117k
  size_t text_len, prefix_len, line_len;
92
93
117k
  if (enh_code == NULL || *enh_code == '\0') {
94
8.11k
    new_prefix = p_strdup_printf(pool, "%03u-", status);
95
109k
  } else {
96
109k
    new_prefix = p_strdup_printf(pool, "%03u-%s ",
97
109k
               status, enh_code);
98
109k
  }
99
100
117k
  i_assert(reply->content != NULL);
101
117k
  textbuf = reply->content->text;
102
103
117k
  if (textbuf == NULL || str_len(textbuf) == 0) {
104
117k
    reply->content->status_prefix = new_prefix;
105
117k
    return;
106
117k
  }
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
117k
{
147
117k
  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
117k
  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
117k
  i_assert(enh_code == NULL || *enh_code == '\0' ||
166
117k
    ((unsigned int)(enh_code[0] - '0') == (status / 100)
167
117k
      && enh_code[1] == '.'));
168
169
117k
  if (reply->content->status == status &&
170
0
      null_strcmp(reply->content->enhanced_code, enh_code) == 0)
171
0
    return;
172
173
117k
  smtp_server_reply_update_prefix(reply, status, enh_code);
174
117k
  reply->content->status = status;
175
117k
  reply->content->enhanced_code = p_strdup(pool, enh_code);
176
117k
}
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
117k
{
191
117k
  struct smtp_server_reply *reply;
192
117k
  pool_t pool = cmd->context.pool;
193
194
117k
  i_assert(cmd->replies_expected > 0);
195
117k
  i_assert(index < cmd->replies_expected);
196
197
117k
  reply = smtp_server_reply_alloc(cmd, index);
198
117k
  reply->index = index;
199
117k
  reply->command = cmd;
200
201
117k
  if (reply->content == NULL)
202
116k
    reply->content = p_new(pool, struct smtp_server_reply_content, 1);
203
117k
  smtp_server_reply_set_status(reply, status, enh_code);
204
117k
  reply->content->text = str_new(default_pool, 256);
205
206
117k
  smtp_server_reply_update_event(reply);
207
208
117k
  return reply;
209
117k
}
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.70k
{
215
7.70k
  return smtp_server_reply_create_index(cmd, 0, status, enh_code);
216
7.70k
}
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
120k
{
261
120k
  unsigned int i;
262
263
120k
  if (!array_is_created(&cmd->replies))
264
1.29k
    return;
265
266
239k
  for (i = 0; i < cmd->replies_expected; i++) {
267
119k
    struct smtp_server_reply *reply =
268
119k
      array_idx_modifiable(&cmd->replies, i);
269
119k
    smtp_server_reply_destroy(reply);
270
119k
  }
271
119k
}
272
273
void smtp_server_reply_add_text(struct smtp_server_reply *reply,
274
        const char *text)
275
110k
{
276
110k
  string_t *textbuf = reply->content->text;
277
278
110k
  i_assert(!reply->submitted);
279
280
110k
  if (*text == '\0')
281
0
    return;
282
283
110k
  do {
284
110k
    const char *p;
285
286
110k
    reply->content->last_line = str_len(textbuf);
287
288
110k
    p = strchr(text, '\n');
289
110k
    str_append(textbuf, reply->content->status_prefix);
290
110k
    if (p == NULL) {
291
110k
      str_append(textbuf, text);
292
110k
      text = NULL;
293
110k
    } 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
110k
    str_append(textbuf, "\r\n");
301
110k
  } while (text != NULL && *text != '\0');
302
110k
}
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
117k
{
399
117k
  i_assert(!reply->submitted);
400
117k
  i_assert(reply->content != NULL);
401
117k
  i_assert(str_len(reply->content->text) >= 5);
402
117k
  e_debug(reply->event, "Submitted");
403
404
117k
  reply->command->replies_submitted++;
405
117k
  reply->submitted = TRUE;
406
117k
  smtp_server_command_submit_reply(reply->command);
407
117k
}
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
108k
{
439
108k
  struct smtp_server_command *cmd = _cmd->cmd;
440
108k
  struct smtp_server_reply *reply;
441
442
108k
  reply = smtp_server_reply_create_index(cmd, index, status, enh_code);
443
108k
  smtp_server_reply_add_text(reply, t_strdup_vprintf(fmt, args));
444
108k
  smtp_server_reply_submit(reply);
445
108k
}
446
447
void smtp_server_reply(struct smtp_server_cmd_ctx *_cmd, unsigned int status,
448
           const char *enh_code, const char *fmt, ...)
449
52.5k
{
450
52.5k
  struct smtp_server_command *cmd = _cmd->cmd;
451
52.5k
  va_list args;
452
453
52.5k
  i_assert(cmd->replies_expected <= 1);
454
455
52.5k
  va_start(args, fmt);
456
52.5k
  smtp_server_reply_indexv(_cmd, 0, status, enh_code, fmt, args);
457
52.5k
  va_end(args);
458
52.5k
}
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.22k
{
495
1.22k
  struct smtp_server_command *cmd = _cmd->cmd;
496
1.22k
  struct smtp_server_reply *reply;
497
1.22k
  const char *text;
498
1.22k
  unsigned int first, i = 0;
499
500
  /* find the first unsent reply */
501
1.22k
  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.22k
  first = i++;
511
512
  /* compose the reply text */
513
1.22k
  text = t_strdup_vprintf(fmt, args);
514
515
  /* submit the first remaining reply */
516
1.22k
  reply = smtp_server_reply_create_index(cmd, first, status, enh_code);
517
1.22k
  smtp_server_reply_add_text(reply, text);
518
1.22k
  smtp_server_reply_submit(reply);
519
520
  /* duplicate the rest from it */
521
1.22k
  for (; i < cmd->replies_expected; i++)
522
0
    smtp_server_reply_submit_duplicate(_cmd, i, first);
523
1.22k
}
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.22k
{
529
1.22k
  va_list args;
530
531
1.22k
  va_start(args, fmt);
532
1.22k
  smtp_server_reply_allv(_cmd, status, enh_code, fmt, args);
533
1.22k
  va_end(args);
534
1.22k
}
535
536
void smtp_server_reply_quit(struct smtp_server_cmd_ctx *_cmd)
537
82
{
538
82
  struct smtp_server_command *cmd = _cmd->cmd;
539
82
  struct smtp_server_reply *reply;
540
541
82
  reply = smtp_server_reply_create(cmd, 221, "2.0.0");
542
82
  smtp_server_reply_add_text(reply, "Bye");
543
82
  smtp_server_reply_submit(reply);
544
82
}
545
546
static void
547
smtp_server_reply_write_one_line(const struct smtp_server_reply *reply,
548
         string_t *str, bool skip_status)
549
71.4k
{
550
71.4k
  string_t *textbuf;
551
71.4k
  const char *text, *p;
552
71.4k
  size_t text_len, prefix_len, line_len;
553
554
71.4k
  i_assert(reply->content != NULL);
555
71.4k
  textbuf = reply->content->text;
556
71.4k
  i_assert(str_len(textbuf) > 0);
557
558
71.4k
  prefix_len = strlen(reply->content->status_prefix);
559
71.4k
  text = str_c(textbuf);
560
71.4k
  text_len = str_len(textbuf);
561
562
71.4k
  if (skip_status) {
563
71.4k
    i_assert(text_len > prefix_len);
564
71.4k
    text_len -= prefix_len;
565
71.4k
    text += prefix_len;
566
71.4k
  }
567
568
71.4k
  for (;;) {
569
71.4k
    p = strchr(text, '\n');
570
71.4k
    i_assert(p != NULL && p > text && *(p - 1) == '\r');
571
71.4k
    str_append_data(str, text, p - text - 1);
572
71.4k
    line_len = (size_t)(p - text) + 1;
573
71.4k
    i_assert(text_len >= line_len);
574
71.4k
    text_len -= line_len;
575
71.4k
    text = p + 1;
576
577
71.4k
    if (text_len <= prefix_len)
578
71.4k
      break;
579
580
0
    text_len -= prefix_len;
581
0
    text += prefix_len;
582
0
    str_append_c(str, ' ');
583
0
  }
584
585
71.4k
  if (!skip_status) {
586
0
    i_assert(str_len(str) > 3);
587
0
    str_replace(str, 3, 1, " ");
588
0
  }
589
71.4k
}
590
591
const char *
592
smtp_server_reply_get_one_line(const struct smtp_server_reply *reply)
593
0
{
594
0
  string_t *str = t_str_new(256);
595
596
0
  smtp_server_reply_write_one_line(reply, str, FALSE);
597
0
  return str_c(str);
598
0
}
599
600
const char *
601
smtp_server_reply_get_message(const struct smtp_server_reply *reply)
602
71.4k
{
603
71.4k
  string_t *str = t_str_new(256);
604
605
71.4k
  smtp_server_reply_write_one_line(reply, str, TRUE);
606
71.4k
  return str_c(str);
607
71.4k
}
608
609
static int smtp_server_reply_send_real(struct smtp_server_reply *reply)
610
113k
{
611
113k
  struct smtp_server_command *cmd = reply->command;
612
113k
  struct smtp_server_connection *conn = cmd->context.conn;
613
113k
  struct ostream *output = conn->conn.output;
614
113k
  string_t *textbuf;
615
113k
  char *text;
616
113k
  int ret = 0;
617
618
113k
  i_assert(reply->content != NULL);
619
113k
  textbuf = reply->content->text;
620
113k
  i_assert(str_len(textbuf) > 0);
621
622
  /* substitute '-' with ' ' in last line */
623
113k
  text = str_c_modifiable(textbuf);
624
113k
  text = text + reply->content->last_line + 3;
625
113k
  if (text[0] != ' ') {
626
113k
    i_assert(text[0] == '-');
627
113k
    text[0] = ' ';
628
113k
  }
629
630
113k
  if (o_stream_send(output, str_data(textbuf), str_len(textbuf)) < 0) {
631
0
    e_debug(reply->event, "Send failed: %s",
632
0
      o_stream_get_disconnect_reason(output));
633
0
    smtp_server_connection_handle_output_error(conn);
634
0
    return -1;
635
0
  }
636
637
113k
  e_debug(reply->event, "Sent: %s",
638
113k
    smtp_server_reply_get_one_line(reply));
639
113k
  return ret;
640
113k
}
641
642
int smtp_server_reply_send(struct smtp_server_reply *reply)
643
113k
{
644
113k
  int ret;
645
646
113k
  if (reply->sent)
647
0
    return 0;
648
649
113k
  T_BEGIN {
650
113k
    ret = smtp_server_reply_send_real(reply);
651
113k
  } T_END;
652
653
113k
  reply->sent = TRUE;
654
113k
  return ret;
655
113k
}
656
657
bool smtp_server_reply_is_success(const struct smtp_server_reply *reply)
658
211k
{
659
211k
  i_assert(reply->content != NULL);
660
211k
  return (reply->content->status / 100 == 2);
661
211k
}
662
663
void smtp_server_reply_add_to_event(const struct smtp_server_reply *reply,
664
            struct event_passthrough *e)
665
117k
{
666
117k
  i_assert(reply->content != NULL);
667
117k
  e->add_int("status_code", reply->content->status);
668
117k
  if (reply->content->enhanced_code != NULL &&
669
117k
      reply->content->enhanced_code[0] != '\0')
670
109k
    e->add_str("enhanced_code", reply->content->enhanced_code);
671
117k
  if (!smtp_server_reply_is_success(reply))
672
71.4k
    e->add_str("error", smtp_server_reply_get_message(reply));
673
117k
}
674
675
/*
676
 * EHLO reply
677
 */
678
679
struct smtp_server_reply *
680
smtp_server_reply_create_ehlo(struct smtp_server_command *cmd)
681
7.61k
{
682
7.61k
  struct smtp_server_connection *conn = cmd->context.conn;
683
7.61k
  struct smtp_server_reply *reply;
684
7.61k
  string_t *textbuf;
685
686
7.61k
  reply = smtp_server_reply_create(cmd, 250, "");
687
7.61k
  textbuf = reply->content->text;
688
7.61k
  str_append(textbuf, reply->content->status_prefix);
689
7.61k
  str_append(textbuf, conn->set.hostname);
690
7.61k
  str_append(textbuf, "\r\n");
691
692
7.61k
  return reply;
693
7.61k
}
694
695
void smtp_server_reply_ehlo_add(struct smtp_server_reply *reply,
696
        const char *keyword)
697
35.6k
{
698
35.6k
  string_t *textbuf;
699
700
35.6k
  i_assert(!reply->submitted);
701
35.6k
  i_assert(reply->content != NULL);
702
35.6k
  textbuf = reply->content->text;
703
704
35.6k
  reply->content->last_line = str_len(textbuf);
705
35.6k
  str_append(textbuf, reply->content->status_prefix);
706
35.6k
  str_append(textbuf, keyword);
707
35.6k
  str_append(textbuf, "\r\n");
708
35.6k
}
709
710
void smtp_server_reply_ehlo_add_param(struct smtp_server_reply *reply,
711
              const char *keyword,
712
              const char *param_fmt, ...)
713
0
{
714
0
  va_list args;
715
0
  string_t *textbuf;
716
717
0
  i_assert(!reply->submitted);
718
0
  i_assert(reply->content != NULL);
719
0
  textbuf = reply->content->text;
720
721
0
  reply->content->last_line = str_len(textbuf);
722
0
  str_append(textbuf, reply->content->status_prefix);
723
0
  str_append(textbuf, keyword);
724
0
  if (*param_fmt != '\0') {
725
0
    va_start(args, param_fmt);
726
0
    str_append_c(textbuf, ' ');
727
0
    str_vprintfa(textbuf, param_fmt, args);
728
0
    va_end(args);
729
0
  }
730
0
  str_append(textbuf, "\r\n");
731
0
}
732
733
void smtp_server_reply_ehlo_add_params(struct smtp_server_reply *reply,
734
               const char *keyword,
735
               const char *const *params)
736
0
{
737
0
  string_t *textbuf;
738
739
0
  i_assert(!reply->submitted);
740
0
  i_assert(reply->content != NULL);
741
0
  textbuf = reply->content->text;
742
743
0
  reply->content->last_line = str_len(textbuf);
744
0
  str_append(textbuf, reply->content->status_prefix);
745
0
  str_append(textbuf, keyword);
746
0
  if (params != NULL) {
747
0
    while (*params != NULL) {
748
0
      str_append_c(textbuf, ' ');
749
0
      str_append(textbuf, *params);
750
0
      params++;
751
0
    }
752
0
  }
753
0
  str_append(textbuf, "\r\n");
754
0
}
755
756
void smtp_server_reply_ehlo_add_8bitmime(struct smtp_server_reply *reply)
757
7.13k
{
758
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
759
7.13k
  struct smtp_server_connection *conn = cmd->conn;
760
7.13k
  enum smtp_capability caps = conn->set.capabilities;
761
762
7.13k
  if ((caps & SMTP_CAPABILITY_8BITMIME) == 0)
763
0
    return;
764
7.13k
  smtp_server_reply_ehlo_add(reply, "8BITMIME");
765
7.13k
}
766
767
void smtp_server_reply_ehlo_add_binarymime(struct smtp_server_reply *reply)
768
7.13k
{
769
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
770
7.13k
  struct smtp_server_connection *conn = cmd->conn;
771
7.13k
  enum smtp_capability caps = conn->set.capabilities;
772
773
7.13k
  if ((caps & SMTP_CAPABILITY_BINARYMIME) == 0 ||
774
0
      (caps & SMTP_CAPABILITY_CHUNKING) == 0)
775
7.13k
    return;
776
0
  smtp_server_reply_ehlo_add(reply, "BINARYMIME");
777
0
}
778
779
void smtp_server_reply_ehlo_add_chunking(struct smtp_server_reply *reply)
780
7.13k
{
781
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
782
7.13k
  struct smtp_server_connection *conn = cmd->conn;
783
7.13k
  enum smtp_capability caps = conn->set.capabilities;
784
785
7.13k
  if ((caps & SMTP_CAPABILITY_CHUNKING) == 0)
786
0
    return;
787
7.13k
  smtp_server_reply_ehlo_add(reply, "CHUNKING");
788
7.13k
}
789
790
void smtp_server_reply_ehlo_add_dsn(struct smtp_server_reply *reply)
791
7.13k
{
792
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
793
7.13k
  struct smtp_server_connection *conn = cmd->conn;
794
7.13k
  enum smtp_capability caps = conn->set.capabilities;
795
796
7.13k
  if ((caps & SMTP_CAPABILITY_DSN) == 0)
797
7.13k
    return;
798
0
  smtp_server_reply_ehlo_add(reply, "DSN");
799
0
}
800
801
void smtp_server_reply_ehlo_add_enhancedstatuscodes(
802
  struct smtp_server_reply *reply)
803
7.13k
{
804
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
805
7.13k
  struct smtp_server_connection *conn = cmd->conn;
806
7.13k
  enum smtp_capability caps = conn->set.capabilities;
807
808
7.13k
  if ((caps & SMTP_CAPABILITY_ENHANCEDSTATUSCODES) == 0)
809
0
    return;
810
7.13k
  smtp_server_reply_ehlo_add(reply, "ENHANCEDSTATUSCODES");
811
7.13k
}
812
813
void smtp_server_reply_ehlo_add_pipelining(struct smtp_server_reply *reply)
814
7.13k
{
815
7.13k
  smtp_server_reply_ehlo_add(reply, "PIPELINING");
816
7.13k
}
817
818
void smtp_server_reply_ehlo_add_size(struct smtp_server_reply *reply)
819
7.13k
{
820
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
821
7.13k
  struct smtp_server_connection *conn = cmd->conn;
822
7.13k
  enum smtp_capability caps = conn->set.capabilities;
823
7.13k
  uoff_t cap_size = conn->set.max_message_size;
824
825
7.13k
  if ((caps & SMTP_CAPABILITY_SIZE) == 0)
826
0
    return;
827
828
7.13k
  if (cap_size > 0 && cap_size != UOFF_T_MAX) {
829
0
    smtp_server_reply_ehlo_add_param(reply,
830
0
      "SIZE", "%"PRIuUOFF_T, cap_size);
831
7.13k
  } else {
832
7.13k
    smtp_server_reply_ehlo_add(reply, "SIZE");
833
7.13k
  }
834
7.13k
}
835
836
#ifdef EXPERIMENTAL_MAIL_UTF8
837
void smtp_server_reply_ehlo_add_smtputf8(struct smtp_server_reply *reply)
838
{
839
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
840
  struct smtp_server_connection *conn = cmd->conn;
841
  enum smtp_capability caps = conn->set.capabilities;
842
843
  if ((caps & SMTP_CAPABILITY_SMTPUTF8) == 0)
844
    return;
845
  smtp_server_reply_ehlo_add(reply, "SMTPUTF8");
846
}
847
#endif
848
849
void smtp_server_reply_ehlo_add_starttls(struct smtp_server_reply *reply)
850
7.13k
{
851
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
852
7.13k
  struct smtp_server_connection *conn = cmd->conn;
853
7.13k
  enum smtp_capability caps = conn->set.capabilities;
854
855
7.13k
  if ((caps & SMTP_CAPABILITY_STARTTLS) == 0)
856
7.13k
    return;
857
0
  smtp_server_reply_ehlo_add(reply, "STARTTLS");
858
0
}
859
860
void smtp_server_reply_ehlo_add_vrfy(struct smtp_server_reply *reply)
861
7.13k
{
862
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
863
7.13k
  struct smtp_server_connection *conn = cmd->conn;
864
7.13k
  enum smtp_capability caps = conn->set.capabilities;
865
866
7.13k
  if ((caps & SMTP_CAPABILITY_VRFY) == 0)
867
7.13k
    return;
868
0
  smtp_server_reply_ehlo_add(reply, "VRFY");
869
0
}
870
871
void smtp_server_reply_ehlo_add_xclient(struct smtp_server_reply *reply)
872
7.13k
{
873
7.13k
  static const char *base_fields =
874
7.13k
    "ADDR PORT PROTO HELO LOGIN SESSION CLIENT-TRANSPORT TTL TIMEOUT "
875
7.13k
    "DESTNAME DESTADDR DESTPORT";
876
7.13k
  struct smtp_server_cmd_ctx *cmd = &reply->command->context;
877
7.13k
  struct smtp_server_connection *conn = cmd->conn;
878
879
7.13k
  if (!smtp_server_connection_is_trusted(conn))
880
7.13k
    return;
881
0
  if (conn->set.xclient_extensions == NULL ||
882
0
      *conn->set.xclient_extensions == NULL) {
883
0
    smtp_server_reply_ehlo_add_param(reply, "XCLIENT", "%s",
884
0
      base_fields);
885
0
    return;
886
0
  }
887
888
0
  smtp_server_reply_ehlo_add_param(reply, "XCLIENT", "%s",
889
0
    t_strconcat(base_fields, " ",
890
0
      t_strarray_join(conn->set.xclient_extensions, " "),
891
      NULL));
892
0
}