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