1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "buffer.h"
5 #include "array.h"
6 #include "str.h"
7 #include "str-sanitize.h"
8 #include "llist.h"
9 #include "istream.h"
10 #include "ostream.h"
11 #include "ostream-dot.h"
12 #include "smtp-common.h"
13 #include "smtp-syntax.h"
14 #include "smtp-params.h"
15 #include "smtp-client-private.h"
16 
17 static const char *
smtp_client_command_get_name(struct smtp_client_command * cmd)18 smtp_client_command_get_name(struct smtp_client_command *cmd)
19 {
20 	const unsigned char *p, *pend;
21 
22 	if (cmd->name != NULL)
23 		return cmd->name;
24 
25 	if (cmd->plug)
26 		return NULL;
27 	if (cmd->data == NULL || cmd->data->used == 0)
28 		return NULL;
29 
30 	p = cmd->data->data;
31 	pend = p + cmd->data->used;
32 	for (;p < pend; p++) {
33 		if (*p == ' ' || *p == '\r' || *p == '\n')
34 			break;
35 	}
36 	cmd->name = p_strdup(cmd->pool,
37 		t_str_ucase(t_strdup_until(cmd->data->data, p)));
38 	return cmd->name;
39 }
40 
41 static const char *
smtp_client_command_get_label(struct smtp_client_command * cmd)42 smtp_client_command_get_label(struct smtp_client_command *cmd)
43 {
44 	if (cmd->plug)
45 		return "[plug]";
46 	if (cmd->data == NULL || cmd->data->used == 0) {
47 		if (!cmd->has_stream)
48 			return "[empty]";
49 		return "[data]";
50 	}
51 	return smtp_client_command_get_name(cmd);
52 }
53 
54 static void
smtp_client_command_update_event(struct smtp_client_command * cmd)55 smtp_client_command_update_event(struct smtp_client_command *cmd)
56 {
57 	event_add_str(cmd->event, "cmd_name", smtp_client_command_get_name(cmd));
58 	event_set_append_log_prefix(
59 		cmd->event,
60 		t_strdup_printf("command %s: ",
61 			str_sanitize(smtp_client_command_get_label(cmd), 128)));
62 }
63 
64 static struct smtp_client_command *
smtp_client_command_create(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,smtp_client_command_callback_t * callback,void * context)65 smtp_client_command_create(struct smtp_client_connection *conn,
66 			   enum smtp_client_command_flags flags,
67 			   smtp_client_command_callback_t *callback,
68 			   void *context)
69 {
70 	struct smtp_client_command *cmd;
71 	pool_t pool;
72 
73 	pool = pool_alloconly_create("smtp client command", 2048);
74 	cmd = p_new(pool, struct smtp_client_command, 1);
75 	cmd->pool = pool;
76 	cmd->refcount = 1;
77 	cmd->conn = conn;
78 	cmd->flags = flags;
79 	cmd->replies_expected = 1;
80 	cmd->callback = callback;
81 	cmd->context = context;
82 	cmd->event = event_create(conn->event);
83 	smtp_client_command_update_event(cmd);
84 	return cmd;
85 }
86 
87 #undef smtp_client_command_new
88 struct smtp_client_command *
smtp_client_command_new(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,smtp_client_command_callback_t * callback,void * context)89 smtp_client_command_new(struct smtp_client_connection *conn,
90 			enum smtp_client_command_flags flags,
91 			smtp_client_command_callback_t *callback,
92 			void *context)
93 {
94 	i_assert(callback != NULL);
95 	return smtp_client_command_create(conn, flags, callback, context);
96 }
97 
98 struct smtp_client_command *
smtp_client_command_plug(struct smtp_client_connection * conn,struct smtp_client_command * after)99 smtp_client_command_plug(struct smtp_client_connection *conn,
100 			 struct smtp_client_command *after)
101 {
102 	struct smtp_client_command *cmd;
103 
104 	cmd = smtp_client_command_create(conn, 0, NULL, NULL);
105 	cmd->plug = TRUE;
106 	smtp_client_command_submit_after(cmd, after);
107 	return cmd;
108 }
109 
smtp_client_command_ref(struct smtp_client_command * cmd)110 void smtp_client_command_ref(struct smtp_client_command *cmd)
111 {
112 	cmd->refcount++;
113 }
114 
smtp_client_command_unref(struct smtp_client_command ** _cmd)115 bool smtp_client_command_unref(struct smtp_client_command **_cmd)
116 {
117 	struct smtp_client_command *cmd = *_cmd;
118 
119 	*_cmd = NULL;
120 
121 	if (cmd == NULL)
122 		return FALSE;
123 
124 	struct smtp_client_connection *conn = cmd->conn;
125 
126 	i_assert(cmd->refcount > 0);
127 	if (--cmd->refcount > 0)
128 		return TRUE;
129 
130 	e_debug(cmd->event, "Destroy (%u commands pending, %u commands queued)",
131 		conn->cmd_wait_list_count, conn->cmd_send_queue_count);
132 
133 	i_assert(cmd->state >= SMTP_CLIENT_COMMAND_STATE_FINISHED);
134 	i_assert(cmd != conn->cmd_streaming);
135 
136 	i_stream_unref(&cmd->stream);
137 	event_unref(&cmd->event);
138 	pool_unref(&cmd->pool);
139 
140 	return FALSE;
141 }
142 
smtp_client_command_name_equals(struct smtp_client_command * cmd,const char * name)143 bool smtp_client_command_name_equals(struct smtp_client_command *cmd,
144 				     const char *name)
145 {
146 	const unsigned char *data;
147 	size_t name_len, data_len;
148 
149 	if (cmd->data == NULL)
150 		return FALSE;
151 
152 	name_len = strlen(name);
153 	data = cmd->data->data;
154 	data_len = cmd->data->used;
155 
156 	if (data_len < name_len ||
157 		i_memcasecmp(data, name, name_len) != 0)
158 		return FALSE;
159 	return (data_len == name_len ||
160 		data[name_len] == ' ' || data[name_len] == '\r');
161 }
162 
smtp_client_command_lock(struct smtp_client_command * cmd)163 void smtp_client_command_lock(struct smtp_client_command *cmd)
164 {
165 	if (cmd->plug)
166 		return;
167 	cmd->locked = TRUE;
168 }
169 
smtp_client_command_unlock(struct smtp_client_command * cmd)170 void smtp_client_command_unlock(struct smtp_client_command *cmd)
171 {
172 	if (cmd->plug)
173 		return;
174 	if (cmd->locked) {
175 		cmd->locked = FALSE;
176 		if (!cmd->conn->corked)
177 			smtp_client_connection_trigger_output(cmd->conn);
178 	}
179 }
180 
smtp_client_command_abort(struct smtp_client_command ** _cmd)181 void smtp_client_command_abort(struct smtp_client_command **_cmd)
182 {
183 	struct smtp_client_command *cmd = *_cmd;
184 	struct smtp_client_connection *conn = cmd->conn;
185 	enum smtp_client_command_state state = cmd->state;
186 	bool disconnected =
187 		(conn->state == SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED);
188 	bool was_locked =
189 		(state >= SMTP_CLIENT_COMMAND_STATE_SUBMITTED) &&
190 		(cmd->locked || cmd->plug);
191 	bool was_sent =
192 		(!disconnected && state > SMTP_CLIENT_COMMAND_STATE_SUBMITTED &&
193 		 state < SMTP_CLIENT_COMMAND_STATE_FINISHED);
194 
195 	*_cmd = NULL;
196 
197 	smtp_client_command_drop_callback(cmd);
198 
199 	if ((!disconnected && !cmd->plug && cmd->aborting) ||
200 		state >= SMTP_CLIENT_COMMAND_STATE_FINISHED)
201 		return;
202 
203 	struct event_passthrough *e = event_create_passthrough(cmd->event);
204 	if (!cmd->event_finished) {
205 		struct smtp_reply failure;
206 
207 		smtp_reply_init(&failure,
208 				SMTP_CLIENT_COMMAND_ERROR_ABORTED,
209 				"Aborted");
210 		failure.enhanced_code = SMTP_REPLY_ENH_CODE(9, 0, 0);
211 
212 		e->set_name("smtp_client_command_finished");
213 		smtp_reply_add_to_event(&failure, e);
214 		cmd->event_finished = TRUE;
215 	}
216 	e_debug(e->event(), "Aborted%s",
217 		(was_sent ? " (already sent)" : ""));
218 
219 	if (!was_sent) {
220 		cmd->state = SMTP_CLIENT_COMMAND_STATE_ABORTED;
221 	} else {
222 		i_assert(state < SMTP_CLIENT_COMMAND_STATE_FINISHED);
223 		cmd->aborting = TRUE;
224 	}
225 	cmd->locked = FALSE;
226 
227 	i_assert(!cmd->plug || state <= SMTP_CLIENT_COMMAND_STATE_SUBMITTED);
228 
229 	switch (state) {
230 	case SMTP_CLIENT_COMMAND_STATE_NEW:
231 		if (cmd->delaying_failure) {
232 			DLLIST_REMOVE(&conn->cmd_fail_list, cmd);
233 			if (conn->cmd_fail_list == NULL)
234 				timeout_remove(&conn->to_cmd_fail);
235 		}
236 		break;
237 	case SMTP_CLIENT_COMMAND_STATE_SENDING:
238 		if (!disconnected) {
239 			/* it is being sent; cannot truly abort it now */
240 			break;
241 		}
242 		/* fall through */
243 	case SMTP_CLIENT_COMMAND_STATE_SUBMITTED:
244 		/* not yet sent */
245 		e_debug(cmd->event, "Removed from send queue");
246 		i_assert(conn->cmd_send_queue_count > 0);
247 		DLLIST2_REMOVE(&conn->cmd_send_queue_head,
248 			&conn->cmd_send_queue_tail, cmd);
249 		i_assert(conn->cmd_send_queue_count > 1 ||
250 			 (cmd->prev == NULL && cmd->next == NULL));
251 		conn->cmd_send_queue_count--;
252 		break;
253 	case SMTP_CLIENT_COMMAND_STATE_WAITING:
254 		if (!disconnected) {
255 			/* we're expecting a reply; cannot truly abort it now */
256 			break;
257 		}
258 		e_debug(cmd->event, "Removed from wait list");
259 		i_assert(conn->cmd_wait_list_count > 0);
260 		DLLIST2_REMOVE(&conn->cmd_wait_list_head,
261 			&conn->cmd_wait_list_tail, cmd);
262 		conn->cmd_wait_list_count--;
263 		break;
264 	default:
265 		i_unreached();
266 	}
267 
268 	if (cmd->abort_callback != NULL) {
269 		cmd->abort_callback(cmd->abort_context);
270 		cmd->abort_callback = NULL;
271 	}
272 
273 	if (disconnected || cmd->plug ||
274 		state <= SMTP_CLIENT_COMMAND_STATE_SUBMITTED) {
275 		/* can only destroy it when it is not pending */
276 		smtp_client_command_unref(&cmd);
277 	}
278 
279 	if (!disconnected && was_locked && !conn->corked)
280 		smtp_client_connection_trigger_output(conn);
281 }
282 
smtp_client_command_drop_callback(struct smtp_client_command * cmd)283 void smtp_client_command_drop_callback(struct smtp_client_command *cmd)
284 {
285 	cmd->callback = NULL;
286 	cmd->context = NULL;
287 }
288 
smtp_client_command_fail_reply(struct smtp_client_command ** _cmd,const struct smtp_reply * reply)289 void smtp_client_command_fail_reply(struct smtp_client_command **_cmd,
290 				    const struct smtp_reply *reply)
291 {
292 	struct smtp_client_command *cmd = *_cmd, *tmp_cmd;
293 	struct smtp_client_connection *conn = cmd->conn;
294 	enum smtp_client_command_state state = cmd->state;
295 	smtp_client_command_callback_t *callback = cmd->callback;
296 
297 	*_cmd = NULL;
298 
299 	if (state >= SMTP_CLIENT_COMMAND_STATE_FINISHED)
300 		return;
301 
302 	if (cmd->delay_failure) {
303 		i_assert(cmd->delayed_failure == NULL);
304 		i_assert(state < SMTP_CLIENT_COMMAND_STATE_SUBMITTED);
305 
306 		e_debug(cmd->event, "Fail (delay)");
307 
308 		cmd->delayed_failure = smtp_reply_clone(cmd->pool, reply);
309 		cmd->delaying_failure = TRUE;
310 		if (conn->to_cmd_fail == NULL) {
311 			conn->to_cmd_fail = timeout_add_short(0,
312 				smtp_client_commands_fail_delayed, conn);
313 		}
314 		DLLIST_PREPEND(&conn->cmd_fail_list, cmd);
315 		return;
316 	}
317 
318 	cmd->callback = NULL;
319 
320 	smtp_client_connection_ref(conn);
321 	smtp_client_command_ref(cmd);
322 
323 	if (!cmd->aborting) {
324 		cmd->failed = TRUE;
325 
326 		struct event_passthrough *e =
327 			event_create_passthrough(cmd->event);
328 		if (!cmd->event_finished) {
329 			e->set_name("smtp_client_command_finished");
330 			smtp_reply_add_to_event(reply, e);
331 			cmd->event_finished = TRUE;
332 		}
333 		e_debug(e->event(), "Failed: %s", smtp_reply_log(reply));
334 
335 		if (callback != NULL)
336 			(void)callback(reply, cmd->context);
337 	}
338 
339 	tmp_cmd = cmd;
340 	smtp_client_command_abort(&tmp_cmd);
341 
342 	smtp_client_command_unref(&cmd);
343 	smtp_client_connection_unref(&conn);
344 }
345 
smtp_client_command_fail(struct smtp_client_command ** _cmd,unsigned int status,const char * error)346 void smtp_client_command_fail(struct smtp_client_command **_cmd,
347 			      unsigned int status, const char *error)
348 {
349 	struct smtp_reply reply;
350 	const char *text_lines[] = {error, NULL};
351 
352 	i_zero(&reply);
353 	reply.status = status;
354 	reply.text_lines = text_lines;
355 	reply.enhanced_code.x = 9;
356 
357 	smtp_client_command_fail_reply(_cmd, &reply);
358 }
359 
360 static void
smtp_client_command_fail_delayed(struct smtp_client_command ** _cmd)361 smtp_client_command_fail_delayed(struct smtp_client_command **_cmd)
362 {
363 	struct smtp_client_command *cmd = *_cmd;
364 
365 	e_debug(cmd->event, "Fail delayed");
366 
367 	i_assert(!cmd->delay_failure);
368 	i_assert(cmd->state < SMTP_CLIENT_COMMAND_STATE_FINISHED);
369 	smtp_client_command_fail_reply(_cmd, cmd->delayed_failure);
370 }
371 
smtp_client_commands_list_abort(struct smtp_client_command * cmds_list,unsigned int cmds_list_count)372 void smtp_client_commands_list_abort(struct smtp_client_command *cmds_list,
373 				     unsigned int cmds_list_count)
374 {
375 	struct smtp_client_command *cmd;
376 	ARRAY(struct smtp_client_command *) cmds_arr;
377 	struct smtp_client_command **cmds;
378 	unsigned int count, i;
379 
380 	if (cmds_list == NULL)
381 		return;
382 	i_assert(cmds_list_count > 0);
383 
384 	/* copy the array and reference the commands to be robust against more
385 	   than one command disappearing from the list */
386 	t_array_init(&cmds_arr, cmds_list_count);
387 	for (cmd = cmds_list; cmd != NULL; cmd = cmd->next) {
388 		smtp_client_command_ref(cmd);
389 		array_push_back(&cmds_arr, &cmd);
390 	}
391 
392 	cmds = array_get_modifiable(&cmds_arr, &count);
393 	for (i = 0; i < count; i++) {
394 		cmd = cmds[i];
395 		/* fail the reply */
396 		smtp_client_command_abort(&cmds[i]);
397 		/* drop our reference */
398 		smtp_client_command_unref(&cmd);
399 	}
400 }
401 
smtp_client_commands_list_fail_reply(struct smtp_client_command * cmds_list,unsigned int cmds_list_count,const struct smtp_reply * reply)402 void smtp_client_commands_list_fail_reply(struct smtp_client_command *cmds_list,
403 					  unsigned int cmds_list_count,
404 					  const struct smtp_reply *reply)
405 {
406 	struct smtp_client_command *cmd;
407 	ARRAY(struct smtp_client_command *) cmds_arr;
408 	struct smtp_client_command **cmds;
409 	unsigned int count, i;
410 
411 	if (cmds_list == NULL)
412 		return;
413 	i_assert(cmds_list_count > 0);
414 
415 	/* copy the array and reference the commands to be robust against more
416 	   than one command disappearing from the list */
417 	t_array_init(&cmds_arr, cmds_list_count);
418 	for (cmd = cmds_list; cmd != NULL; cmd = cmd->next) {
419 		smtp_client_command_ref(cmd);
420 		array_push_back(&cmds_arr, &cmd);
421 	}
422 
423 	cmds = array_get_modifiable(&cmds_arr, &count);
424 	for (i = 0; i < count; i++) {
425 		cmd = cmds[i];
426 		/* fail the reply */
427 		smtp_client_command_fail_reply(&cmds[i], reply);
428 		/* drop our reference */
429 		smtp_client_command_unref(&cmd);
430 	}
431 }
432 
smtp_client_commands_abort_delayed(struct smtp_client_connection * conn)433 void smtp_client_commands_abort_delayed(struct smtp_client_connection *conn)
434 {
435 	struct smtp_client_command *cmd;
436 
437 	timeout_remove(&conn->to_cmd_fail);
438 
439 	cmd = conn->cmd_fail_list;
440 	conn->cmd_fail_list = NULL;
441 	while (cmd != NULL) {
442 		struct smtp_client_command *cmd_next = cmd->next;
443 
444 		cmd->delaying_failure = FALSE;
445 		smtp_client_command_abort(&cmd);
446 		cmd = cmd_next;
447 	}
448 }
449 
smtp_client_commands_fail_delayed(struct smtp_client_connection * conn)450 void smtp_client_commands_fail_delayed(struct smtp_client_connection *conn)
451 {
452 	struct smtp_client_command *cmd;
453 
454 	timeout_remove(&conn->to_cmd_fail);
455 
456 	cmd = conn->cmd_fail_list;
457 	conn->cmd_fail_list = NULL;
458 	while (cmd != NULL) {
459 		struct smtp_client_command *cmd_next = cmd->next;
460 
461 		cmd->delaying_failure = FALSE;
462 		smtp_client_command_fail_delayed(&cmd);
463 		cmd = cmd_next;
464 	}
465 }
466 
smtp_client_command_set_abort_callback(struct smtp_client_command * cmd,void (* callback)(void * context),void * context)467 void smtp_client_command_set_abort_callback(struct smtp_client_command *cmd,
468 					    void (*callback)(void *context),
469 					    void *context)
470 {
471 	cmd->abort_callback = callback;
472 	cmd->abort_context = context;
473 }
474 
smtp_client_command_set_sent_callback(struct smtp_client_command * cmd,void (* callback)(void * context),void * context)475 void smtp_client_command_set_sent_callback(struct smtp_client_command *cmd,
476 					   void (*callback)(void *context),
477 					   void *context)
478 {
479 	cmd->sent_callback = callback;
480 	cmd->sent_context = context;
481 }
482 
smtp_client_command_set_replies(struct smtp_client_command * cmd,unsigned int replies)483 void smtp_client_command_set_replies(struct smtp_client_command *cmd,
484 				     unsigned int replies)
485 {
486 	i_assert(cmd->replies_expected == 1 ||
487 		cmd->replies_expected == replies);
488 	i_assert(replies > 0);
489 	i_assert(cmd->replies_seen <= 1);
490 	cmd->replies_expected = replies;
491 }
492 
smtp_client_command_sent(struct smtp_client_command * cmd)493 static void smtp_client_command_sent(struct smtp_client_command *cmd)
494 {
495 	struct event_passthrough *e;
496 
497 	e = event_create_passthrough(cmd->event)->
498 		set_name("smtp_client_command_sent");
499 
500 	if (cmd->data == NULL)
501 		e_debug(e->event(), "Sent");
502 	else {
503 		i_assert(str_len(cmd->data) > 2);
504 		str_truncate(cmd->data, str_len(cmd->data)-2);
505 		e_debug(e->event(), "Sent: %s", str_c(cmd->data));
506 	}
507 
508 	if (smtp_client_command_name_equals(cmd, "QUIT"))
509 		cmd->conn->sent_quit = TRUE;
510 
511 	if (cmd->sent_callback != NULL) {
512 		cmd->sent_callback(cmd->sent_context);
513 		cmd->sent_callback = NULL;
514 	}
515 }
516 
517 static int
smtp_client_command_finish_dot_stream(struct smtp_client_command * cmd)518 smtp_client_command_finish_dot_stream(struct smtp_client_command *cmd)
519 {
520 	struct smtp_client_connection *conn = cmd->conn;
521 	int ret;
522 
523 	i_assert(cmd->stream_dot);
524 	i_assert(conn->dot_output != NULL);
525 
526 	/* this concludes the dot stream with CRLF.CRLF */
527 	if ((ret = o_stream_finish(conn->dot_output)) < 0) {
528 		o_stream_unref(&conn->dot_output);
529 		smtp_client_connection_handle_output_error(conn);
530 		return -1;
531 	}
532 	if (ret == 0)
533 		return 0;
534 	o_stream_unref(&conn->dot_output);
535 	return 1;
536 }
537 
smtp_client_command_payload_input(struct smtp_client_command * cmd)538 static void smtp_client_command_payload_input(struct smtp_client_command *cmd)
539 {
540 	struct smtp_client_connection *conn = cmd->conn;
541 
542 	io_remove(&conn->io_cmd_payload);
543 
544 	smtp_client_connection_trigger_output(conn);
545 }
546 
smtp_client_command_send_stream(struct smtp_client_command * cmd)547 static int smtp_client_command_send_stream(struct smtp_client_command *cmd)
548 {
549 	struct smtp_client_connection *conn = cmd->conn;
550 	struct istream *stream = cmd->stream;
551 	struct ostream *output = conn->conn.output;
552 	enum ostream_send_istream_result res;
553 	int ret;
554 
555 	io_remove(&conn->io_cmd_payload);
556 
557 	if (cmd->stream_finished) {
558 		if ((ret = smtp_client_command_finish_dot_stream(cmd)) <= 0)
559 			return ret;
560 		/* done sending payload */
561 		e_debug(cmd->event, "Finished sending payload");
562 		i_stream_unref(&cmd->stream);
563 		return 1;
564 	}
565 	if (cmd->stream_dot) {
566 		if (conn->dot_output == NULL)
567 			conn->dot_output = o_stream_create_dot(output, FALSE);
568 		output = conn->dot_output;
569 	}
570 
571 	/* we're sending the stream now */
572 	o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE);
573 	res = o_stream_send_istream(output, stream);
574 	o_stream_set_max_buffer_size(output, SIZE_MAX);
575 
576 	switch (res) {
577 	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
578 		i_assert(cmd->stream_size == 0 ||
579 			 stream->v_offset == cmd->stream_size);
580 		/* finished with the stream */
581 		e_debug(cmd->event, "Finished reading payload stream");
582 		cmd->stream_finished = TRUE;
583 		if (cmd->stream_dot) {
584 			ret = smtp_client_command_finish_dot_stream(cmd);
585 			if (ret <= 0)
586 				return ret;
587 		}
588 		/* done sending payload */
589 		e_debug(cmd->event, "Finished sending payload");
590 		i_stream_unref(&cmd->stream);
591 		return 1;
592 	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
593 		/* input is blocking (client needs to act; disable timeout) */
594 		conn->io_cmd_payload = io_add_istream(
595 			stream, smtp_client_command_payload_input, cmd);
596 		return 0;
597 	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
598 		e_debug(cmd->event, "Partially sent payload");
599 		i_assert(cmd->stream_size == 0 ||
600 			stream->v_offset < cmd->stream_size);
601 		return 0;
602 	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
603 
604 		/* the provided payload stream is broken;
605 		   fail this command separately */
606 		e_error(cmd->event, "read(%s) failed: %s",
607 			i_stream_get_name(stream), i_stream_get_error(stream));
608 		smtp_client_command_fail(
609 			&cmd, SMTP_CLIENT_COMMAND_ERROR_BROKEN_PAYLOAD,
610 			"Broken payload stream");
611 		/* we're in the middle of sending a command, so the connection
612 		   will also have to be aborted */
613 		o_stream_unref(&conn->dot_output);
614 		smtp_client_connection_fail(
615 			conn, SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST,
616 			"Broken payload stream");
617 		return -1;
618 	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
619 		/* normal connection failure */
620 		o_stream_unref(&conn->dot_output);
621 		smtp_client_connection_handle_output_error(conn);
622 		return -1;
623 	}
624 	i_unreached();
625 }
626 
smtp_client_command_send_line(struct smtp_client_command * cmd)627 static int smtp_client_command_send_line(struct smtp_client_command *cmd)
628 {
629 	struct smtp_client_connection *conn = cmd->conn;
630 	const char *data;
631 	size_t size;
632 	ssize_t sent;
633 
634 	if (cmd->data == NULL)
635 		return 1;
636 
637 	while (cmd->send_pos < cmd->data->used) {
638 		data = CONST_PTR_OFFSET(cmd->data->data, cmd->send_pos);
639 		size = cmd->data->used - cmd->send_pos;
640 
641 		sent = o_stream_send(conn->conn.output, data, size);
642 		if (sent <= 0) {
643 			if (sent < 0) {
644 				smtp_client_connection_handle_output_error(conn);
645 				return -1;
646 			}
647 			e_debug(cmd->event, "Blocked while sending");
648 			return 0;
649 		}
650 		cmd->send_pos += sent;
651 	}
652 
653 	i_assert(cmd->send_pos == cmd->data->used);
654 	return 1;
655 }
656 
657 static bool
smtp_client_command_pipeline_is_open(struct smtp_client_connection * conn)658 smtp_client_command_pipeline_is_open(struct smtp_client_connection *conn)
659 {
660 	struct smtp_client_command *cmd = conn->cmd_send_queue_head;
661 
662 	if (cmd == NULL)
663 		return TRUE;
664 
665 	if (cmd->plug) {
666 		e_debug(cmd->event, "Pipeline is plugged");
667 		return FALSE;
668 	}
669 
670 	if (conn->state < SMTP_CLIENT_CONNECTION_STATE_READY &&
671 	    (cmd->flags & SMTP_CLIENT_COMMAND_FLAG_PRELOGIN) == 0) {
672 		/* wait until we're fully connected */
673 		e_debug(cmd->event, "Connection not ready [state=%s]",
674 			smtp_client_connection_state_names[conn->state]);
675 		return FALSE;
676 	}
677 
678 	cmd = conn->cmd_wait_list_head;
679 	if (cmd != NULL &&
680 	    (conn->caps.standard & SMTP_CAPABILITY_PIPELINING) == 0) {
681 		/* cannot pipeline; wait for reply */
682 		e_debug(cmd->event, "Pipeline occupied");
683 		return FALSE;
684 	}
685 	while (cmd != NULL) {
686 		if ((conn->caps.standard & SMTP_CAPABILITY_PIPELINING) == 0 ||
687 		    (cmd->flags & SMTP_CLIENT_COMMAND_FLAG_PIPELINE) == 0 ||
688 		    cmd->locked) {
689 			/* cannot pipeline with previous command;
690 			   wait for reply */
691 			e_debug(cmd->event, "Pipeline blocked");
692 			return FALSE;
693 		}
694 		cmd = cmd->next;
695 	}
696 
697 	return TRUE;
698 }
699 
smtp_cient_command_wait(struct smtp_client_command * cmd)700 static void smtp_cient_command_wait(struct smtp_client_command *cmd)
701 {
702 	struct smtp_client_connection *conn = cmd->conn;
703 
704 	/* move command to wait list. */
705 	i_assert(conn->cmd_send_queue_count > 0);
706 	i_assert(conn->cmd_send_queue_count > 1 ||
707 		 (cmd->prev == NULL && cmd->next == NULL));
708 	DLLIST2_REMOVE(&conn->cmd_send_queue_head,
709 		       &conn->cmd_send_queue_tail, cmd);
710 	conn->cmd_send_queue_count--;
711 	DLLIST2_APPEND(&conn->cmd_wait_list_head,
712 		       &conn->cmd_wait_list_tail, cmd);
713 	conn->cmd_wait_list_count++;
714 }
715 
smtp_client_command_do_send_more(struct smtp_client_connection * conn)716 static int smtp_client_command_do_send_more(struct smtp_client_connection *conn)
717 {
718 	struct smtp_client_command *cmd;
719 	int ret;
720 
721 	if (conn->cmd_streaming != NULL) {
722 		cmd = conn->cmd_streaming;
723 		i_assert(cmd->stream != NULL);
724 	} else {
725 		/* check whether we can send anything */
726 		cmd = conn->cmd_send_queue_head;
727 		if (cmd == NULL)
728 			return 0;
729 		if (!smtp_client_command_pipeline_is_open(conn))
730 			return 0;
731 
732 		cmd->state = SMTP_CLIENT_COMMAND_STATE_SENDING;
733 		conn->sending_command = TRUE;
734 
735 		if ((ret = smtp_client_command_send_line(cmd)) <= 0)
736 			return ret;
737 
738 		/* command line sent. move command to wait list. */
739 		smtp_cient_command_wait(cmd);
740 		cmd->state = SMTP_CLIENT_COMMAND_STATE_WAITING;
741 	}
742 
743 	if (cmd->stream != NULL &&
744 	    (ret = smtp_client_command_send_stream(cmd)) <= 0) {
745 		if (ret < 0)
746 			return -1;
747 		e_debug(cmd->event, "Blocked while sending payload");
748 		if (conn->cmd_streaming != cmd) {
749 			i_assert(conn->cmd_streaming == NULL);
750 			conn->cmd_streaming = cmd;
751 			smtp_client_command_ref(cmd);
752 		}
753 		return 0;
754 	}
755 
756 	conn->sending_command = FALSE;
757 	if (conn->cmd_streaming != cmd ||
758 	    smtp_client_command_unref(&conn->cmd_streaming))
759 		smtp_client_command_sent(cmd);
760 	return 1;
761 }
762 
smtp_client_command_send_more(struct smtp_client_connection * conn)763 int smtp_client_command_send_more(struct smtp_client_connection *conn)
764 {
765 	int ret;
766 
767 	while ((ret = smtp_client_command_do_send_more(conn)) > 0);
768 	if (ret < 0)
769 		return -1;
770 
771 	smtp_client_connection_update_cmd_timeout(conn);
772 	return ret;
773 }
774 
775 static void
smtp_client_command_disconnected(struct smtp_client_connection * conn)776 smtp_client_command_disconnected(struct smtp_client_connection *conn)
777 {
778 	smtp_client_connection_fail(
779 		conn, SMTP_CLIENT_COMMAND_ERROR_CONNECTION_LOST,
780 		"Disconnected");
781 }
782 
783 static void
smtp_client_command_insert_prioritized(struct smtp_client_command * cmd,enum smtp_client_command_flags flag)784 smtp_client_command_insert_prioritized(struct smtp_client_command *cmd,
785 				       enum smtp_client_command_flags flag)
786 {
787 	struct smtp_client_connection *conn = cmd->conn;
788 	struct smtp_client_command *cmd_cur, *cmd_prev;
789 
790 	cmd_cur = conn->cmd_send_queue_head;
791 	if (cmd_cur == NULL || (cmd_cur->flags & flag) == 0) {
792 		DLLIST2_PREPEND(&conn->cmd_send_queue_head,
793 				&conn->cmd_send_queue_tail, cmd);
794 		conn->cmd_send_queue_count++;
795 	} else {
796 		cmd_prev = cmd_cur;
797 		cmd_cur = cmd_cur->next;
798 		while (cmd_cur != NULL && (cmd_cur->flags & flag) != 0) {
799 			cmd_prev = cmd_cur;
800 			cmd_cur = cmd_cur->next;
801 		}
802 		DLLIST2_INSERT_AFTER(&conn->cmd_send_queue_head,
803 				     &conn->cmd_send_queue_tail, cmd_prev, cmd);
804 		conn->cmd_send_queue_count++;
805 	}
806 }
807 
smtp_client_command_submit_after(struct smtp_client_command * cmd,struct smtp_client_command * after)808 void smtp_client_command_submit_after(struct smtp_client_command *cmd,
809 				      struct smtp_client_command *after)
810 {
811 	struct smtp_client_connection *conn = cmd->conn;
812 	struct event_passthrough *e;
813 
814 	i_assert(after == NULL || cmd->conn == after->conn);
815 
816 	smtp_client_command_update_event(cmd);
817 	e = event_create_passthrough(cmd->event)->
818 		set_name("smtp_client_command_started");
819 
820 	cmd->state = SMTP_CLIENT_COMMAND_STATE_SUBMITTED;
821 
822 	if (smtp_client_command_name_equals(cmd, "EHLO"))
823 		cmd->ehlo = TRUE;
824 
825 	if (conn->state == SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED) {
826 		/* Add commands to send queue for delayed failure reply
827 		   from ioloop */
828 		DLLIST2_APPEND(&conn->cmd_send_queue_head,
829 			&conn->cmd_send_queue_tail, cmd);
830 		conn->cmd_send_queue_count++;
831 		if (conn->to_commands == NULL) {
832 			conn->to_commands = timeout_add_short(
833 				0, smtp_client_command_disconnected, conn);
834 		}
835 		e_debug(e->event(), "Submitted, but disconnected");
836 		return;
837 	}
838 
839 	if (cmd->data != NULL)
840 		str_append(cmd->data, "\r\n");
841 
842 	if ((cmd->flags & SMTP_CLIENT_COMMAND_FLAG_PRELOGIN) != 0 &&
843 	    conn->state < SMTP_CLIENT_CONNECTION_STATE_READY) {
844 		/* pre-login commands get inserted before everything else */
845 		smtp_client_command_insert_prioritized(
846 			cmd, SMTP_CLIENT_COMMAND_FLAG_PRELOGIN);
847 		if (!conn->corked)
848 			smtp_client_connection_trigger_output(conn);
849 		e_debug(e->event(), "Submitted with priority");
850 		return;
851 	}
852 
853 	if (after != NULL) {
854 		if (after->state >= SMTP_CLIENT_COMMAND_STATE_WAITING) {
855 			/* not in the send queue anymore; just prepend */
856 			DLLIST2_PREPEND(&conn->cmd_send_queue_head,
857 					&conn->cmd_send_queue_tail, cmd);
858 			conn->cmd_send_queue_count++;
859 		} else {
860 			/* insert after indicated command */
861 			DLLIST2_INSERT_AFTER(&conn->cmd_send_queue_head,
862 					     &conn->cmd_send_queue_tail,
863 					     after, cmd);
864 			conn->cmd_send_queue_count++;
865 		}
866 	} else if ((cmd->flags & SMTP_CLIENT_COMMAND_FLAG_PRIORITY) != 0) {
867 		/* insert at beginning of queue for priority commands */
868 		smtp_client_command_insert_prioritized(
869 			cmd, SMTP_CLIENT_COMMAND_FLAG_PRIORITY);
870 	} else {
871 		/* just append at end of queue */
872 		DLLIST2_APPEND(&conn->cmd_send_queue_head,
873 			       &conn->cmd_send_queue_tail, cmd);
874 		conn->cmd_send_queue_count++;
875 	}
876 
877 	if (conn->state >= SMTP_CLIENT_CONNECTION_STATE_READY)
878 		smtp_client_connection_start_cmd_timeout(conn);
879 
880 	if (!conn->corked)
881 		smtp_client_connection_trigger_output(conn);
882 	e_debug(e->event(), "Submitted");
883 }
884 
smtp_client_command_submit(struct smtp_client_command * cmd)885 void smtp_client_command_submit(struct smtp_client_command *cmd)
886 {
887 	smtp_client_command_submit_after(cmd, NULL);
888 }
889 
smtp_client_command_set_flags(struct smtp_client_command * cmd,enum smtp_client_command_flags flags)890 void smtp_client_command_set_flags(struct smtp_client_command *cmd,
891 				   enum smtp_client_command_flags flags)
892 {
893 	cmd->flags = flags;
894 }
895 
smtp_client_command_write(struct smtp_client_command * cmd,const char * cmd_str)896 void smtp_client_command_write(struct smtp_client_command *cmd,
897 			       const char *cmd_str)
898 {
899 	unsigned int len = strlen(cmd_str);
900 
901 	i_assert(cmd->state < SMTP_CLIENT_COMMAND_STATE_SUBMITTED);
902 	if (cmd->data == NULL)
903 		cmd->data = str_new(cmd->pool, len + 2);
904 	str_append(cmd->data, cmd_str);
905 }
906 
smtp_client_command_printf(struct smtp_client_command * cmd,const char * cmd_fmt,...)907 void smtp_client_command_printf(struct smtp_client_command *cmd,
908 				const char *cmd_fmt, ...)
909 {
910 	va_list args;
911 
912 	va_start(args, cmd_fmt);
913 	smtp_client_command_vprintf(cmd, cmd_fmt, args);
914 	va_end(args);
915 }
916 
smtp_client_command_vprintf(struct smtp_client_command * cmd,const char * cmd_fmt,va_list args)917 void smtp_client_command_vprintf(struct smtp_client_command *cmd,
918 				 const char *cmd_fmt, va_list args)
919 {
920 	if (cmd->data == NULL)
921 		cmd->data = str_new(cmd->pool, 128);
922 	str_vprintfa(cmd->data, cmd_fmt, args);
923 }
924 
smtp_client_command_set_stream(struct smtp_client_command * cmd,struct istream * input,bool dot)925 void smtp_client_command_set_stream(struct smtp_client_command *cmd,
926 				    struct istream *input, bool dot)
927 {
928 	int ret;
929 
930 	cmd->stream = input;
931 	i_stream_ref(input);
932 
933 	if ((ret = i_stream_get_size(input, TRUE, &cmd->stream_size)) <= 0) {
934 		if (ret < 0) {
935 			e_error(cmd->event, "i_stream_get_size(%s) failed: %s",
936 				i_stream_get_name(input),
937 				i_stream_get_error(input));
938 		}
939 		/* size must be known if stream is to be sent in chunks */
940 		i_assert(dot);
941 		cmd->stream_size = 0;
942 	}
943 
944 	cmd->stream_dot = dot;
945 	cmd->has_stream = TRUE;
946 }
947 
smtp_client_command_input_reply(struct smtp_client_command * cmd,const struct smtp_reply * reply)948 int smtp_client_command_input_reply(struct smtp_client_command *cmd,
949 				    const struct smtp_reply *reply)
950 {
951 	struct smtp_client_connection *conn = cmd->conn;
952 	bool finished;
953 
954 	i_assert(cmd->replies_seen < cmd->replies_expected);
955 	finished = (++cmd->replies_seen == cmd->replies_expected);
956 
957 	/* Finish command event at final reply or first failure */
958 	struct event_passthrough *e = event_create_passthrough(cmd->event);
959 	if (!cmd->event_finished &&
960 	    (finished || !smtp_reply_is_success(reply))) {
961 		e->set_name("smtp_client_command_finished");
962 		smtp_reply_add_to_event(reply, e);
963 		cmd->event_finished = TRUE;
964 	}
965 	e_debug(e->event(), "Got reply (%u/%u): %s "
966 		"(%u commands pending, %u commands queued)",
967 		cmd->replies_seen, cmd->replies_expected,
968 		smtp_reply_log(reply), conn->cmd_wait_list_count,
969 		conn->cmd_send_queue_count);
970 
971 	if (finished) {
972 		i_assert(conn->cmd_wait_list_count > 0);
973 		DLLIST2_REMOVE(&conn->cmd_wait_list_head,
974 			&conn->cmd_wait_list_tail, cmd);
975 		conn->cmd_wait_list_count--;
976 		if (cmd->aborting)
977 			cmd->state = SMTP_CLIENT_COMMAND_STATE_ABORTED;
978 		else if (cmd->state != SMTP_CLIENT_COMMAND_STATE_ABORTED)
979 			cmd->state = SMTP_CLIENT_COMMAND_STATE_FINISHED;
980 
981 		smtp_client_connection_update_cmd_timeout(conn);
982 	}
983 
984 	if (!cmd->aborting && cmd->callback != NULL)
985 		cmd->callback(reply, cmd->context);
986 
987 	if (finished) {
988 		smtp_client_command_drop_callback(cmd);
989 		smtp_client_command_unref(&cmd);
990 		smtp_client_connection_trigger_output(conn);
991 	}
992 	return 1;
993 }
994 
995 enum smtp_client_command_state
smtp_client_command_get_state(struct smtp_client_command * cmd)996 smtp_client_command_get_state(struct smtp_client_command *cmd)
997 {
998 	return cmd->state;
999 }
1000 
1001 /*
1002  * Standard commands
1003  */
1004 
1005 /* NOTE: Pipelining is only enabled for certain commands:
1006 
1007    From RFC 2920, Section 3.1:
1008 
1009    Once the client SMTP has confirmed that support exists for the
1010    pipelining extension, the client SMTP may then elect to transmit
1011    groups of SMTP commands in batches without waiting for a response to
1012    each individual command. In particular, the commands RSET, MAIL FROM,
1013    SEND FROM, SOML FROM, SAML FROM, and RCPT TO can all appear anywhere
1014    in a pipelined command group.  The EHLO, DATA, VRFY, EXPN, TURN,
1015    QUIT, and NOOP commands can only appear as the last command in a
1016    group since their success or failure produces a change of state which
1017    the client SMTP must accommodate. (NOOP is included in this group so
1018    it can be used as a synchronization point.)
1019 
1020    Additional commands added by other SMTP extensions may only appear as
1021    the last command in a group unless otherwise specified by the
1022    extensions that define the commands.
1023  */
1024 
1025 /* NOOP */
1026 
1027 #undef smtp_client_command_noop_submit_after
1028 struct smtp_client_command *
smtp_client_command_noop_submit_after(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,struct smtp_client_command * after,smtp_client_command_callback_t * callback,void * context)1029 smtp_client_command_noop_submit_after(struct smtp_client_connection *conn,
1030 				      enum smtp_client_command_flags flags,
1031 				      struct smtp_client_command *after,
1032 				      smtp_client_command_callback_t *callback,
1033 				      void *context)
1034 {
1035 	struct smtp_client_command *cmd;
1036 
1037 	cmd = smtp_client_command_new(conn, flags, callback, context);
1038 	smtp_client_command_write(cmd, "NOOP");
1039 	smtp_client_command_submit_after(cmd, after);
1040 	return cmd;
1041 }
1042 
1043 #undef smtp_client_command_noop_submit
1044 struct smtp_client_command *
smtp_client_command_noop_submit(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,smtp_client_command_callback_t * callback,void * context)1045 smtp_client_command_noop_submit(struct smtp_client_connection *conn,
1046 				enum smtp_client_command_flags flags,
1047 				smtp_client_command_callback_t *callback,
1048 				void *context)
1049 {
1050 	return smtp_client_command_noop_submit_after(conn, flags, NULL,
1051 						     callback, context);
1052 }
1053 
1054 /* VRFY */
1055 
1056 #undef smtp_client_command_vrfy_submit_after
1057 struct smtp_client_command *
smtp_client_command_vrfy_submit_after(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,struct smtp_client_command * after,const char * param,smtp_client_command_callback_t * callback,void * context)1058 smtp_client_command_vrfy_submit_after(struct smtp_client_connection *conn,
1059 				      enum smtp_client_command_flags flags,
1060 				      struct smtp_client_command *after,
1061 				      const char *param,
1062 				      smtp_client_command_callback_t *callback,
1063 				      void *context)
1064 {
1065 	struct smtp_client_command *cmd;
1066 
1067 	cmd = smtp_client_command_new(conn, flags, callback, context);
1068 	smtp_client_command_write(cmd, "VRFY ");
1069 	smtp_string_write(cmd->data, param);
1070 	smtp_client_command_submit_after(cmd, after);
1071 	return cmd;
1072 }
1073 
1074 #undef smtp_client_command_vrfy_submit
1075 struct smtp_client_command *
smtp_client_command_vrfy_submit(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,const char * param,smtp_client_command_callback_t * callback,void * context)1076 smtp_client_command_vrfy_submit(struct smtp_client_connection *conn,
1077 				enum smtp_client_command_flags flags,
1078 				const char *param,
1079 				smtp_client_command_callback_t *callback,
1080 				void *context)
1081 {
1082 	return smtp_client_command_vrfy_submit_after(conn, flags, NULL, param,
1083 						     callback, context);
1084 }
1085 
1086 /* RSET */
1087 
1088 #undef smtp_client_command_rset_submit_after
1089 struct smtp_client_command *
smtp_client_command_rset_submit_after(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,struct smtp_client_command * after,smtp_client_command_callback_t * callback,void * context)1090 smtp_client_command_rset_submit_after(struct smtp_client_connection *conn,
1091 				      enum smtp_client_command_flags flags,
1092 				      struct smtp_client_command *after,
1093 				      smtp_client_command_callback_t *callback,
1094 				      void *context)
1095 {
1096 	struct smtp_client_command *cmd;
1097 
1098 	cmd = smtp_client_command_new(conn,
1099 				      flags | SMTP_CLIENT_COMMAND_FLAG_PIPELINE,
1100 				      callback, context);
1101 	smtp_client_command_write(cmd, "RSET");
1102 	smtp_client_command_submit_after(cmd, after);
1103 	return cmd;
1104 }
1105 
1106 #undef smtp_client_command_rset_submit
1107 struct smtp_client_command *
smtp_client_command_rset_submit(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,smtp_client_command_callback_t * callback,void * context)1108 smtp_client_command_rset_submit(struct smtp_client_connection *conn,
1109 				enum smtp_client_command_flags flags,
1110 				smtp_client_command_callback_t *callback,
1111 				void *context)
1112 {
1113 	return smtp_client_command_rset_submit_after(conn, flags, NULL,
1114 						     callback, context);
1115 }
1116 
1117 /* MAIL FROM: */
1118 
1119 #undef smtp_client_command_mail_submit
1120 struct smtp_client_command *
smtp_client_command_mail_submit(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,const struct smtp_address * from,const struct smtp_params_mail * params,smtp_client_command_callback_t * callback,void * context)1121 smtp_client_command_mail_submit(struct smtp_client_connection *conn,
1122 				enum smtp_client_command_flags flags,
1123 				const struct smtp_address *from,
1124 				const struct smtp_params_mail *params,
1125 				smtp_client_command_callback_t *callback,
1126 				void *context)
1127 {
1128 	struct smtp_client_command *cmd;
1129 
1130 	smtp_client_connection_send_xclient(conn);
1131 
1132 	flags |= SMTP_CLIENT_COMMAND_FLAG_PIPELINE;
1133 	cmd = smtp_client_command_new(conn, flags, callback, context);
1134 	if (!conn->set.mail_send_broken_path || !smtp_address_is_broken(from)) {
1135 		/* Compose MAIL command with normalized path. */
1136 		smtp_client_command_printf(cmd, "MAIL FROM:<%s>",
1137 					   smtp_address_encode(from));
1138 	} else {
1139 		/* Compose MAIL command with broken path (for proxy). */
1140 		smtp_client_command_printf(cmd, "MAIL FROM:<%s>",
1141 					   smtp_address_encode_raw(from));
1142 	}
1143 	if (params != NULL) {
1144 		size_t orig_len = str_len(cmd->data);
1145 		const char *const *extensions = NULL;
1146 
1147 		if (array_is_created(&conn->caps.mail_param_extensions)) {
1148 			extensions =
1149 				array_front(&conn->caps.mail_param_extensions);
1150 		}
1151 
1152 		str_append_c(cmd->data, ' ');
1153 		smtp_params_mail_write(cmd->data, conn->caps.standard,
1154 				       extensions, params);
1155 		if (str_len(cmd->data) == orig_len + 1)
1156 			str_truncate(cmd->data, orig_len);
1157 	}
1158 	smtp_client_command_submit(cmd);
1159 	return cmd;
1160 }
1161 
1162 /* RCPT TO: */
1163 
1164 #undef smtp_client_command_rcpt_submit_after
1165 struct smtp_client_command *
smtp_client_command_rcpt_submit_after(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,struct smtp_client_command * after,const struct smtp_address * to,const struct smtp_params_rcpt * params,smtp_client_command_callback_t * callback,void * context)1166 smtp_client_command_rcpt_submit_after(struct smtp_client_connection *conn,
1167 				      enum smtp_client_command_flags flags,
1168 				      struct smtp_client_command *after,
1169 				      const struct smtp_address *to,
1170 				      const struct smtp_params_rcpt *params,
1171 				      smtp_client_command_callback_t *callback,
1172 				      void *context)
1173 {
1174 	struct smtp_client_command *cmd;
1175 
1176 	flags |= SMTP_CLIENT_COMMAND_FLAG_PIPELINE;
1177 	cmd = smtp_client_command_new(conn, flags, callback, context);
1178 	smtp_client_command_printf(cmd, "RCPT TO:<%s>",
1179 				   smtp_address_encode(to));
1180 	if (params != NULL) {
1181 		size_t orig_len = str_len(cmd->data);
1182 		const char *const *extensions = NULL;
1183 
1184 		if (array_is_created(&conn->caps.rcpt_param_extensions)) {
1185 			extensions =
1186 				array_front(&conn->caps.rcpt_param_extensions);
1187 		}
1188 
1189 		str_append_c(cmd->data, ' ');
1190 		smtp_params_rcpt_write(cmd->data, conn->caps.standard,
1191 				       extensions, params);
1192 		if (str_len(cmd->data) == orig_len + 1)
1193 			str_truncate(cmd->data, orig_len);
1194 	}
1195 	smtp_client_command_submit_after(cmd, after);
1196 	return cmd;
1197 }
1198 
1199 #undef smtp_client_command_rcpt_submit
1200 struct smtp_client_command *
smtp_client_command_rcpt_submit(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,const struct smtp_address * from,const struct smtp_params_rcpt * params,smtp_client_command_callback_t * callback,void * context)1201 smtp_client_command_rcpt_submit(struct smtp_client_connection *conn,
1202 				enum smtp_client_command_flags flags,
1203 				const struct smtp_address *from,
1204 				const struct smtp_params_rcpt *params,
1205 				smtp_client_command_callback_t *callback,
1206 				void *context)
1207 {
1208 	return smtp_client_command_rcpt_submit_after(conn, flags, NULL, from,
1209 						     params, callback, context);
1210 }
1211 
1212 /* DATA or BDAT */
1213 
1214 struct _cmd_data_context {
1215 	struct smtp_client_connection *conn;
1216 	pool_t pool;
1217 
1218 	struct smtp_client_command *cmd_data, *cmd_first;
1219 	ARRAY(struct smtp_client_command *) cmds;
1220 
1221 	struct istream *data;
1222 	uoff_t data_offset, data_left;
1223 };
1224 
1225 static void
1226 _cmd_bdat_send_chunks(struct _cmd_data_context *ctx,
1227 	struct smtp_client_command *after);
1228 
_cmd_data_context_free(struct _cmd_data_context * ctx)1229 static void _cmd_data_context_free(struct _cmd_data_context *ctx)
1230 {
1231 	if (ctx->cmd_data != NULL) {
1232 		/* abort the main (possibly unsubmitted) data command */
1233 		smtp_client_command_set_abort_callback(ctx->cmd_data,
1234 			NULL, NULL);
1235 		ctx->cmd_data = NULL;
1236 	}
1237 	i_stream_unref(&ctx->data);
1238 }
1239 
_cmd_data_abort(struct _cmd_data_context * ctx)1240 static void _cmd_data_abort(struct _cmd_data_context *ctx)
1241 {
1242 	struct smtp_client_command **cmds;
1243 	unsigned int count, i;
1244 
1245 	/* drop all pending commands */
1246 	cmds = array_get_modifiable(&ctx->cmds, &count);
1247 	for (i = 0; i < count; i++) {
1248 		smtp_client_command_set_abort_callback(cmds[i], NULL, NULL);
1249 		smtp_client_command_abort(&cmds[i]);
1250 	}
1251 }
1252 
_cmd_data_abort_cb(void * context)1253 static void _cmd_data_abort_cb(void *context)
1254 {
1255 	struct _cmd_data_context *ctx = (struct _cmd_data_context *)context;
1256 
1257 	/* the main (possibly unsubmitted) data command got aborted */
1258 	_cmd_data_abort(ctx);
1259 	_cmd_data_context_free(ctx);
1260 }
1261 
1262 static void
_cmd_data_error(struct _cmd_data_context * ctx,const struct smtp_reply * reply)1263 _cmd_data_error(struct _cmd_data_context *ctx, const struct smtp_reply *reply)
1264 {
1265 	struct smtp_client_command *cmd = ctx->cmd_data;
1266 
1267 	if (cmd != NULL) {
1268 		/* fail the main (possibly unsubmitted) data command so that
1269 		   the caller gets notified */
1270 		smtp_client_command_fail_reply(&cmd, reply);
1271 	}
1272 }
1273 
1274 static void
_cmd_data_cb(const struct smtp_reply * reply,void * context)1275 _cmd_data_cb(const struct smtp_reply *reply, void *context)
1276 {
1277 	struct _cmd_data_context *ctx = (struct _cmd_data_context *)context;
1278 	struct smtp_client_command *const *cmds, *cmd;
1279 	unsigned int count;
1280 
1281 	/* got DATA reply; one command must be pending */
1282 	cmds = array_get(&ctx->cmds, &count);
1283 	i_assert(count > 0);
1284 
1285 	if (reply->status == 354) {
1286 		/* submit second stage: which is a command with only a stream */
1287 		cmd = ctx->cmd_data;
1288 		smtp_client_command_submit_after(cmd, cmds[0]);
1289 
1290 		/* nothing else to do, so drop the context already */
1291 		_cmd_data_context_free(ctx);
1292 	} else {
1293 		/* error */
1294 		_cmd_data_error(ctx, reply);
1295 	}
1296 }
1297 
1298 static void
_cmd_bdat_cb(const struct smtp_reply * reply,void * context)1299 _cmd_bdat_cb(const struct smtp_reply *reply, void *context)
1300 {
1301 	struct _cmd_data_context *ctx = (struct _cmd_data_context *)context;
1302 
1303 	/* got BDAT reply, so there must be ones pending */
1304 	i_assert(array_count(&ctx->cmds) > 0);
1305 
1306 	if ((reply->status / 100) != 2) {
1307 		/* error */
1308 		_cmd_data_error(ctx, reply);
1309 		return;
1310 	}
1311 
1312 	/* drop the command from the list */
1313 	array_pop_front(&ctx->cmds);
1314 
1315 	/* send more BDAT commands if necessary */
1316 	(void)_cmd_bdat_send_chunks(ctx, NULL);
1317 
1318 	if (array_count(&ctx->cmds) == 0) {
1319 		/* all of the BDAT commands finished already */
1320 		_cmd_data_context_free(ctx);
1321 	}
1322 }
1323 
_cmd_bdat_sent_cb(void * context)1324 static void _cmd_bdat_sent_cb(void *context)
1325 {
1326 	struct _cmd_data_context *ctx = (struct _cmd_data_context *)context;
1327 
1328 	/* send more BDAT commands if possible */
1329 	(void)_cmd_bdat_send_chunks(ctx, NULL);
1330 }
1331 
1332 static int
_cmd_bdat_read_data(struct _cmd_data_context * ctx,size_t * data_size_r)1333 _cmd_bdat_read_data(struct _cmd_data_context *ctx, size_t *data_size_r)
1334 {
1335 	int ret;
1336 
1337 	while ((ret = i_stream_read(ctx->data)) > 0);
1338 
1339 	if (ret < 0) {
1340 		if (ret != -2 && ctx->data->stream_errno != 0) {
1341 			e_error(ctx->cmd_data->event,
1342 				"Failed to read DATA stream: %s",
1343 				i_stream_get_error(ctx->data));
1344 			smtp_client_command_fail(
1345 				&ctx->cmd_data,
1346 				SMTP_CLIENT_COMMAND_ERROR_BROKEN_PAYLOAD,
1347 				"Broken payload stream");
1348 			return -1;
1349 		}
1350 	}
1351 
1352 	*data_size_r = i_stream_get_data_size(ctx->data);
1353 	return 0;
1354 }
1355 
1356 static void
_cmd_bdat_send_chunks(struct _cmd_data_context * ctx,struct smtp_client_command * after)1357 _cmd_bdat_send_chunks(struct _cmd_data_context *ctx,
1358 		      struct smtp_client_command *after)
1359 {
1360 	struct smtp_client_connection *conn = ctx->conn;
1361 	const struct smtp_client_settings *set = &conn->set;
1362 	struct smtp_client_command *const *cmds, *cmd, *cmd_prev;
1363 	unsigned int count;
1364 	struct istream *chunk;
1365 	size_t data_size, max_chunk_size;
1366 
1367 	if (smtp_client_command_get_state(ctx->cmd_data) >=
1368 		SMTP_CLIENT_COMMAND_STATE_SUBMITTED) {
1369 		/* finished or aborted */
1370 		return;
1371 	}
1372 
1373 	/* pipeline management: determine where to submit the next command */
1374 	cmds = array_get(&ctx->cmds, &count);
1375 	cmd_prev = NULL;
1376 	if (after != NULL) {
1377 		i_assert(count == 0);
1378 		cmd_prev = after;
1379 	} else if (count > 0) {
1380 		cmd_prev = cmds[count-1];
1381 		smtp_client_command_unlock(cmd_prev);
1382 	}
1383 
1384 	data_size = ctx->data_left;
1385 	if (data_size > 0) {
1386 		max_chunk_size = set->max_data_chunk_size;
1387 	} else {
1388 		if (ctx->data->v_offset < ctx->data_offset) {
1389 			/* previous BDAT command not completely sent */
1390 			return;
1391 		}
1392 		max_chunk_size = i_stream_get_max_buffer_size(ctx->data);
1393 		if (set->max_data_chunk_size < max_chunk_size)
1394 			max_chunk_size = set->max_data_chunk_size;
1395 		if (_cmd_bdat_read_data(ctx, &data_size) < 0)
1396 			return;
1397 	}
1398 
1399 	/* Keep sending more chunks until pipeline is filled to the limit */
1400 	cmd = NULL;
1401 	while (data_size > max_chunk_size ||
1402 	       (data_size == max_chunk_size && !ctx->data->eof)) {
1403 		enum smtp_client_command_flags flags = ctx->cmd_data->flags;
1404 		size_t size = (data_size > set->max_data_chunk_size ?
1405 			       set->max_data_chunk_size : data_size);
1406 		chunk = i_stream_create_range(ctx->data, ctx->data_offset,
1407 					      size);
1408 
1409 		flags |= SMTP_CLIENT_COMMAND_FLAG_PIPELINE;
1410 		cmd = smtp_client_command_new(conn, flags, _cmd_bdat_cb, ctx);
1411 		smtp_client_command_set_abort_callback(cmd, _cmd_data_abort_cb,
1412 						       ctx);
1413 		smtp_client_command_set_stream(cmd, chunk, FALSE);
1414 		i_stream_unref(&chunk);
1415 		smtp_client_command_printf(cmd, "BDAT %"PRIuUOFF_T,
1416 					   (uoff_t)size);
1417 		smtp_client_command_submit_after(cmd, cmd_prev);
1418 		array_push_back(&ctx->cmds, &cmd);
1419 
1420 		ctx->data_offset += size;
1421 		data_size -= size;
1422 
1423 		if (array_count(&ctx->cmds) >= set->max_data_chunk_pipeline) {
1424 			/* pipeline full */
1425 			if (ctx->data_left != 0) {
1426 				/* data stream size known:
1427 				   record where we left off */
1428 				ctx->data_left = data_size;
1429 			}
1430 			smtp_client_command_lock(cmd);
1431 			return;
1432 		}
1433 
1434 		cmd_prev = cmd;
1435 	}
1436 
1437 	if (ctx->data_left != 0) {
1438 		/* data stream size known:
1439 		record where we left off */
1440 		ctx->data_left = data_size;
1441 	} else if (!ctx->data->eof) {
1442 		/* more to read */
1443 		if (cmd != NULL) {
1444 			smtp_client_command_set_sent_callback(
1445 				cmd, _cmd_bdat_sent_cb, ctx);
1446 		}
1447 		return;
1448 	}
1449 
1450 	/* the last chunk, which may actually be empty */
1451 	chunk = i_stream_create_range(ctx->data, ctx->data_offset, data_size);
1452 
1453 	/* submit final command */
1454 	cmd = ctx->cmd_data;
1455 	smtp_client_command_set_stream(cmd, chunk, FALSE);
1456 	i_stream_unref(&chunk);
1457 	smtp_client_command_printf(cmd, "BDAT %zu LAST", data_size);
1458 	smtp_client_command_submit_after(cmd, cmd_prev);
1459 
1460 	if (array_count(&ctx->cmds) == 0) {
1461 		/* all of the previous BDAT commands got replies already */
1462 		_cmd_data_context_free(ctx);
1463 	}
1464 }
1465 
1466 #undef smtp_client_command_data_submit_after
1467 struct smtp_client_command *
smtp_client_command_data_submit_after(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,struct smtp_client_command * after,struct istream * data,smtp_client_command_callback_t * callback,void * context)1468 smtp_client_command_data_submit_after(struct smtp_client_connection *conn,
1469 				      enum smtp_client_command_flags flags,
1470 				      struct smtp_client_command *after,
1471 				      struct istream *data,
1472 				      smtp_client_command_callback_t *callback,
1473 				      void *context)
1474 {
1475 	const struct smtp_client_settings *set = &conn->set;
1476 	struct _cmd_data_context *ctx;
1477 	struct smtp_client_command *cmd, *cmd_data;
1478 
1479 	/* create the final command early for reference by the caller;
1480 	   it will not be submitted for now. The DATA command is handled in
1481 	   two stages (== command submissions), the BDAT command in one or more. */
1482 	cmd = cmd_data = smtp_client_command_create(conn, flags,
1483 						    callback, context);
1484 
1485 	/* protect against race conditions */
1486 	cmd_data->delay_failure = TRUE;
1487 
1488 	/* create context in the final command's pool */
1489 	ctx = p_new(cmd->pool, struct _cmd_data_context, 1);
1490 	ctx->conn = conn;
1491 	ctx->pool = cmd->pool;
1492 	ctx->cmd_data = cmd;
1493 
1494 	/* capture abort event with our context */
1495 	smtp_client_command_set_abort_callback(cmd, _cmd_data_abort_cb, ctx);
1496 
1497 	ctx->data = data;
1498 	i_stream_ref(data);
1499 
1500 	if ((conn->caps.standard & SMTP_CAPABILITY_CHUNKING) == 0) {
1501 		/* DATA */
1502 		p_array_init(&ctx->cmds, ctx->pool, 1);
1503 
1504 		/* Data stream is sent in one go in the second stage. Since the data
1505 		   is sent in a '<CRLF>.<CRLF>'-terminated stream, it size is not
1506 		   relevant here. */
1507 		smtp_client_command_set_stream(cmd, ctx->data, TRUE);
1508 
1509 		/* Submit the initial DATA command */
1510 		cmd = smtp_client_command_new(conn, flags, _cmd_data_cb, ctx);
1511 		smtp_client_command_set_abort_callback(cmd, _cmd_data_abort_cb,
1512 						       ctx);
1513 		smtp_client_command_write(cmd, "DATA");
1514 		smtp_client_command_submit_after(cmd, after);
1515 		array_push_back(&ctx->cmds, &cmd);
1516 	} else {
1517 		/* BDAT */
1518 		p_array_init(&ctx->cmds, ctx->pool,
1519 			conn->set.max_data_chunk_pipeline);
1520 
1521 		/* The data stream is sent in multiple chunks. Either the size of the
1522 		   data stream is known or it is not. These cases are handled a little
1523 		   differently. */
1524 		if (i_stream_get_size(data, TRUE, &ctx->data_left) > 0) {
1525 			/* size is known */
1526 			i_assert(ctx->data_left >= data->v_offset);
1527 			ctx->data_left -= data->v_offset;
1528 		} else {
1529 			/* size is unknown */
1530 			ctx->data_left = 0;
1531 
1532 			/* Make sure we can send chunks of sufficient size by
1533 			   making the data stream buffer size limit at least
1534 			   equally large. */
1535 			if (i_stream_get_max_buffer_size(ctx->data) <
1536 				set->max_data_chunk_size) {
1537 				i_stream_set_max_buffer_size(
1538 					ctx->data, set->max_data_chunk_size);
1539 			}
1540 		}
1541 
1542 		/* Send the first BDAT command(s) */
1543 		ctx->data_offset = data->v_offset;
1544 		_cmd_bdat_send_chunks(ctx, after);
1545 	}
1546 
1547 	cmd_data->delay_failure = FALSE;
1548 	return cmd_data;
1549 }
1550 
1551 #undef smtp_client_command_data_submit
1552 struct smtp_client_command *
smtp_client_command_data_submit(struct smtp_client_connection * conn,enum smtp_client_command_flags flags,struct istream * data,smtp_client_command_callback_t * callback,void * context)1553 smtp_client_command_data_submit(struct smtp_client_connection *conn,
1554 				enum smtp_client_command_flags flags,
1555 				struct istream *data,
1556 				smtp_client_command_callback_t *callback,
1557 				void *context)
1558 {
1559 	return smtp_client_command_data_submit_after(conn, flags, NULL, data,
1560 						     callback, context);
1561 }
1562