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 "istream-chain.h"
8 
9 #include "smtp-command-parser.h"
10 #include "smtp-server-private.h"
11 
12 /* DATA/BDAT/B... commands */
13 
14 struct cmd_data_context {
15 	struct istream *main_input;
16 	struct istream *chunk_input;
17 	uoff_t chunk_size;
18 
19 	bool chunking:1;
20 	bool client_input:1;
21 	bool chunk_first:1;
22 	bool chunk_last:1;
23 };
24 
25 static void
smtp_server_cmd_data_size_limit_exceeded(struct smtp_server_cmd_ctx * cmd)26 smtp_server_cmd_data_size_limit_exceeded(struct smtp_server_cmd_ctx *cmd)
27 {
28 	struct smtp_server_command *command = cmd->cmd;
29 
30 	smtp_server_command_fail(command, 552, "5.2.3",
31 				 "Message size exceeds administrative limit");
32 }
33 
smtp_server_cmd_data_check_size(struct smtp_server_cmd_ctx * cmd)34 bool smtp_server_cmd_data_check_size(struct smtp_server_cmd_ctx *cmd)
35 {
36 	struct smtp_server_connection *conn = cmd->conn;
37 	const struct smtp_server_settings *set = &conn->set;
38 
39 	i_assert(conn->state.state == SMTP_SERVER_STATE_DATA);
40 
41 	if (conn->state.data_input == NULL)
42 		return TRUE;
43 	if (set->max_message_size == 0)
44 		return TRUE;
45 	if (conn->state.data_input->v_offset <= set->max_message_size)
46 		return TRUE;
47 
48 	smtp_server_cmd_data_size_limit_exceeded(cmd);
49 	return FALSE;
50 }
51 
smtp_server_connection_data_check_state(struct smtp_server_cmd_ctx * cmd)52 bool smtp_server_connection_data_check_state(struct smtp_server_cmd_ctx *cmd)
53 {
54 	struct smtp_server_connection *conn = cmd->conn;
55 	struct smtp_server_command *command = cmd->cmd;
56 	struct cmd_data_context *data_cmd = command->data;
57 
58 	if (conn->state.data_chunks > 0 && conn->state.data_failed) {
59 		// FIXME: should it even reply anything? RFC is unclear.
60 		smtp_server_command_fail(command, 503, "5.5.0",
61 			"Previous data chunk failed, issue RSET first");
62 		return FALSE;
63 	}
64 
65 	/* check valid MAIL */
66 	if (conn->state.trans == NULL
67 		&& conn->state.pending_mail_cmds == 0) {
68 		smtp_server_command_fail(command,
69 			503, "5.5.0", "MAIL needed first");
70 		return FALSE;
71 	}
72 	if (conn->state.trans != NULL &&
73 	    (conn->state.trans->params.body.type ==
74 		SMTP_PARAM_MAIL_BODY_TYPE_BINARYMIME) &&
75 	    !data_cmd->chunking) {
76 		/* RFC 3030, Section 3:
77 		   BINARYMIME cannot be used with the DATA command. If a DATA
78 		   command is issued after a MAIL command containing the
79 		   body-value of "BINARYMIME", a 503 "Bad sequence of commands"
80 		   response MUST be sent. The resulting state from this error
81 		   condition is indeterminate and the transaction MUST be reset
82 		   with the RSET command. */
83 		smtp_server_command_fail(command,
84 			503, "5.5.0", "DATA cannot be used with BINARYMIME");
85 		return FALSE;
86 	}
87 
88 	/* Can only decide whether we have valid recipients once there are no
89 	   pending RCPT commands */
90 	if (conn->state.pending_rcpt_cmds > 0)
91 		return TRUE;
92 
93 	/* special handling for LMTP */
94 	if (conn->set.protocol == SMTP_PROTOCOL_LMTP) {
95 		/* check valid RCPT (at least one) */
96 		if (conn->state.trans == NULL ||
97 		    !smtp_server_transaction_has_rcpt(conn->state.trans)) {
98 			if (data_cmd->chunk_size > 0 && data_cmd->chunk_last) {
99 				/* RFC 2033, Section 4.3:
100 				   If there were no previously successful RCPT
101 				   commands in the mail transaction, then the
102 				   BDAT LAST command returns zero replies.
103 				 */
104 				smtp_server_command_abort(&command);
105 			} else {
106 				/* RFC 2033, Section 4.2:
107 				   The additional restriction is that when there
108 				   have been no successful RCPT commands in the
109 				   mail transaction, the DATA command MUST fail
110 				   with a 503 reply code.
111 				*/
112 				smtp_server_command_fail(command,
113 					503, "5.5.0", "No valid recipients");
114 			}
115 			return FALSE;
116 		}
117 
118 	} else {
119 		/* check valid RCPT (at least one) */
120 		if (conn->state.trans == NULL ||
121 			!smtp_server_transaction_has_rcpt(conn->state.trans)) {
122 			smtp_server_command_fail(command,
123 				554, "5.5.0", "No valid recipients");
124 			return FALSE;
125 		}
126 	}
127 	return TRUE;
128 }
129 
130 static void
cmd_data_destroy(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd)131 cmd_data_destroy(struct smtp_server_cmd_ctx *cmd,
132 		 struct cmd_data_context *data_cmd)
133 {
134 	struct smtp_server_connection *conn = cmd->conn;
135 	struct smtp_server_command *command = cmd->cmd;
136 
137 	i_assert(data_cmd != NULL);
138 
139 	if (data_cmd->main_input == conn->state.data_input &&
140 	    (data_cmd->chunk_last ||
141 	     !smtp_server_command_replied_success(command))) {
142 		/* clean up */
143 		i_stream_destroy(&conn->state.data_input);
144 		i_stream_destroy(&conn->state.data_chain_input);
145 		conn->state.data_chain = NULL;
146 	}
147 
148 	i_stream_unref(&data_cmd->chunk_input);
149 }
150 
151 static void
cmd_data_replied_one(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd ATTR_UNUSED)152 cmd_data_replied_one(struct smtp_server_cmd_ctx *cmd,
153 		     struct cmd_data_context *data_cmd ATTR_UNUSED)
154 {
155 	struct smtp_server_connection *conn = cmd->conn;
156 	struct smtp_server_transaction *trans = conn->state.trans;
157 	struct smtp_server_recipient *rcpt;
158 
159 	if (trans == NULL || !array_is_created(&trans->rcpt_to))
160 		return;
161 
162 	array_foreach_elem(&trans->rcpt_to, rcpt)
163 		smtp_server_recipient_data_replied(rcpt);
164 }
165 
166 static void
cmd_data_replied(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd ATTR_UNUSED)167 cmd_data_replied(struct smtp_server_cmd_ctx *cmd,
168 		 struct cmd_data_context *data_cmd ATTR_UNUSED)
169 {
170 	struct smtp_server_connection *conn = cmd->conn;
171 	struct smtp_server_command *command = cmd->cmd;
172 
173         i_assert(conn->state.pending_data_cmds > 0);
174         conn->state.pending_data_cmds--;
175 
176 	smtp_server_command_input_lock(cmd);
177 	if (!smtp_server_command_replied_success(command))
178 		smtp_server_command_input_unlock(cmd);
179 }
180 
181 static void
cmd_data_completed(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd)182 cmd_data_completed(struct smtp_server_cmd_ctx *cmd,
183 		   struct cmd_data_context *data_cmd)
184 {
185 	struct smtp_server_connection *conn = cmd->conn;
186 
187 	i_assert(data_cmd != NULL);
188 	i_stream_unref(&data_cmd->chunk_input);
189 
190 	i_assert(conn->state.trans != NULL);
191 	smtp_server_transaction_finished(conn->state.trans, cmd);
192 
193 	/* reset state */
194 	smtp_server_connection_reset_state(conn);
195 }
196 
197 static void
cmd_data_chunk_replied(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd)198 cmd_data_chunk_replied(struct smtp_server_cmd_ctx *cmd,
199 		       struct cmd_data_context *data_cmd)
200 {
201 	struct smtp_server_connection *conn = cmd->conn;
202 	struct smtp_server_command *command = cmd->cmd;
203 
204 	i_assert(data_cmd != NULL);
205 
206         i_assert(conn->state.pending_data_cmds > 0);
207         conn->state.pending_data_cmds--;
208 
209 	i_assert(smtp_server_command_is_replied(command));
210 	if (!smtp_server_command_replied_success(command) &&
211 	    conn->state.pending_data_cmds == 0)
212 		conn->state.data_failed = TRUE;
213 }
214 
215 static void
cmd_data_chunk_completed(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd ATTR_UNUSED)216 cmd_data_chunk_completed(struct smtp_server_cmd_ctx *cmd,
217 			 struct cmd_data_context *data_cmd ATTR_UNUSED)
218 {
219 	struct smtp_server_connection *conn = cmd->conn;
220 	struct smtp_server_command *command = cmd->cmd;
221 
222 	if (!smtp_server_command_replied_success(command))
223 		conn->state.data_failed = TRUE;
224 }
225 
226 static void
cmd_data_chunk_finish(struct smtp_server_cmd_ctx * cmd)227 cmd_data_chunk_finish(struct smtp_server_cmd_ctx *cmd)
228 {
229 	struct smtp_server_command *command = cmd->cmd;
230 	struct cmd_data_context *data_cmd = command->data;
231 
232 	smtp_server_command_input_lock(cmd);
233 	i_stream_unref(&data_cmd->chunk_input);
234 
235 	/* re-check transaction state (for BDAT/B... command) */
236 	if (!smtp_server_connection_data_check_state(cmd))
237 		return;
238 
239 	smtp_server_reply(cmd, 250, "2.0.0",
240 		"Added %"PRIuUOFF_T" octets", data_cmd->chunk_size);
241 }
242 
cmd_data_input_error(struct smtp_server_cmd_ctx * cmd)243 static void cmd_data_input_error(struct smtp_server_cmd_ctx *cmd)
244 {
245 	struct smtp_server_connection *conn = cmd->conn;
246 	struct smtp_server_command *command = cmd->cmd;
247 	struct cmd_data_context *data_cmd = command->data;
248 	struct istream *data_input = conn->state.data_input;
249 	const char *error;
250 
251 	conn->state.data_failed = TRUE;
252 
253 	if (!data_cmd->client_input) {
254 		if (!smtp_server_command_is_replied(command)) {
255 			smtp_server_command_fail(command,
256 				400, "4.0.0", "Failed to add data");
257 		}
258 		return;
259 	}
260 
261 	error = i_stream_get_disconnect_reason(data_input);
262 	e_debug(conn->event, "Connection lost during data transfer: %s", error);
263 	smtp_server_connection_close(&conn, error);
264 }
265 
cmd_data_do_handle_input(struct smtp_server_cmd_ctx * cmd)266 static int cmd_data_do_handle_input(struct smtp_server_cmd_ctx *cmd)
267 {
268 	struct smtp_server_connection *conn = cmd->conn;
269 	const struct smtp_server_callbacks *callbacks = conn->callbacks;
270 	struct smtp_server_command *command = cmd->cmd;
271 	struct cmd_data_context *data_cmd = command->data;
272 	int ret;
273 
274 	i_assert(data_cmd != NULL);
275 
276 	i_assert(callbacks != NULL &&
277 		 callbacks->conn_cmd_data_continue != NULL);
278 	ret = callbacks->conn_cmd_data_continue(conn->context,
279 		cmd, conn->state.trans);
280 	if (ret >= 0) {
281 		if (!smtp_server_cmd_data_check_size(cmd)) {
282 			return -1;
283 		} else if (!i_stream_have_bytes_left(conn->state.data_input)) {
284 			e_debug(cmd->event, "End of data");
285 			smtp_server_transaction_received(
286 				conn->state.trans,
287 				conn->state.data_input->v_offset);
288 			smtp_server_command_input_lock(cmd);
289 			smtp_server_connection_timeout_stop(conn);
290 		} else if (!data_cmd->chunk_last &&
291 			!i_stream_have_bytes_left(data_cmd->chunk_input)) {
292 			e_debug(cmd->event, "End of chunk");
293 			cmd_data_chunk_finish(cmd);
294 		} else if (i_stream_get_data_size(
295 			conn->state.data_input) > 0) {
296 			e_debug(cmd->event, "Not all client data read");
297 			smtp_server_connection_timeout_stop(cmd->conn);
298 		} else {
299 			smtp_server_connection_timeout_start(cmd->conn);
300 		}
301 	} else {
302 		if (conn->state.data_input->stream_errno != 0) {
303 			cmd_data_input_error(cmd);
304 			return -1;
305 		}
306 		/* command is waiting for external event or it failed */
307 		i_assert(smtp_server_command_is_replied(command));
308 	}
309 
310 	return 1;
311 }
312 
cmd_data_handle_input(struct smtp_server_cmd_ctx * cmd)313 static int cmd_data_handle_input(struct smtp_server_cmd_ctx *cmd)
314 {
315 	struct smtp_server_connection *conn = cmd->conn;
316 	struct smtp_server_command *command = cmd->cmd;
317 	int ret;
318 
319 	if (!smtp_server_cmd_data_check_size(cmd))
320 		return -1;
321 
322 	smtp_server_connection_ref(conn);
323 	smtp_server_command_ref(command);
324 
325 	/* continue reading from client */
326 	ret = cmd_data_do_handle_input(cmd);
327 
328 	smtp_server_command_unref(&command);
329 	smtp_server_connection_unref(&conn);
330 
331 	return ret;
332 }
333 
cmd_data_input(struct smtp_server_cmd_ctx * cmd)334 static void cmd_data_input(struct smtp_server_cmd_ctx *cmd)
335 {
336 	smtp_server_connection_timeout_reset(cmd->conn);
337 	(void)cmd_data_handle_input(cmd);
338 }
339 
340 static void
cmd_data_next(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd)341 cmd_data_next(struct smtp_server_cmd_ctx *cmd,
342 	      struct cmd_data_context *data_cmd)
343 {
344 	struct smtp_server_connection *conn = cmd->conn;
345 	struct smtp_server_transaction *trans = conn->state.trans;
346 	const struct smtp_server_callbacks *callbacks = conn->callbacks;
347 	struct smtp_server_command *command = cmd->cmd;
348 
349 	/* this command is next to send a reply */
350 
351 	i_assert(data_cmd != NULL);
352 	i_assert(trans != NULL);
353 
354 	/* DATA command stops the pipeline, so if it is next to reply, nothing
355 	   else can be pending. */
356 	i_assert(conn->state.pending_mail_cmds == 0 &&
357 		conn->state.pending_rcpt_cmds == 0);
358 
359 	e_debug(cmd->event, "Command is next to be replied");
360 
361 	smtp_server_transaction_data_command(trans, cmd);
362 
363 	/* check whether we have had successful mail and rcpt commands */
364 	if (!smtp_server_connection_data_check_state(cmd))
365 		return;
366 
367 	if (data_cmd->chunk_last) {
368 		/* LMTP 'DATA' and 'BDAT LAST' commands need to send more than
369 		   one reply per recipient */
370 		if (HAS_ALL_BITS(trans->flags,
371 				 SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT)) {
372 			smtp_server_command_set_reply_count(command,
373 				array_count(&trans->rcpt_to));
374 		}
375 	}
376 
377 	smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_DATA, NULL);
378 
379 	/* chain data streams in the correct order */
380 	if (conn->state.data_chain != NULL) {
381 		i_assert(data_cmd->chunk_input != NULL);
382 		i_stream_chain_append(conn->state.data_chain,
383 				      data_cmd->chunk_input);
384 		if (data_cmd->chunk_last) {
385 			e_debug(cmd->event, "Seen the last chunk");
386 			i_stream_chain_append_eof(conn->state.data_chain);
387 		}
388 	}
389 
390 	if (data_cmd->chunk_first) {
391 		struct smtp_server_command *cmd_temp = command;
392 
393 		e_debug(cmd->event, "First chunk");
394 
395 		smtp_server_command_ref(cmd_temp);
396 		i_assert(callbacks != NULL &&
397 			 callbacks->conn_cmd_data_begin != NULL);
398 		i_assert(conn->state.data_input != NULL);
399 		if (callbacks->conn_cmd_data_begin(conn->context,
400 			cmd, conn->state.trans, conn->state.data_input) < 0) {
401 			i_assert(smtp_server_command_is_replied(cmd_temp));
402 			/* command failed */
403 			smtp_server_command_unref(&cmd_temp);
404 			return;
405 		}
406 		if (!smtp_server_command_unref(&cmd_temp))
407 			return;
408 	}
409 
410 	if (smtp_server_command_is_replied(command)) {
411 		smtp_server_command_input_unlock(cmd);
412 	} else {
413 		if (data_cmd->client_input) {
414 			/* using input from client connection;
415 			   capture I/O event */
416 			smtp_server_connection_timeout_start(conn);
417 			smtp_server_command_input_capture(cmd, cmd_data_input);
418 		}
419 
420 		(void)cmd_data_handle_input(cmd);
421 	}
422 }
423 
424 static void
cmd_data_start_input(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd,struct istream * input)425 cmd_data_start_input(struct smtp_server_cmd_ctx *cmd,
426 		     struct cmd_data_context *data_cmd, struct istream *input)
427 {
428 	struct smtp_server_connection *conn = cmd->conn;
429 	struct smtp_server_command *command = cmd->cmd;
430 
431 	i_assert(data_cmd != NULL);
432 
433 	if (input != NULL) {
434 		i_assert(conn->state.data_input == NULL);
435 		conn->state.data_input = input;
436 		i_stream_ref(input);
437 	}
438 	data_cmd->main_input = conn->state.data_input;
439 
440 	if (data_cmd->client_input)
441 		smtp_server_command_input_lock(cmd);
442 
443 	if (data_cmd->chunk_last) {
444 		smtp_server_command_add_hook(
445 			command, SMTP_SERVER_COMMAND_HOOK_COMPLETED,
446 			cmd_data_completed, data_cmd);
447 	} else {
448 		smtp_server_command_add_hook(
449 			command, SMTP_SERVER_COMMAND_HOOK_COMPLETED,
450 			cmd_data_chunk_completed, data_cmd);
451 	}
452 
453 	if (conn->state.pending_mail_cmds == 0 &&
454 		conn->state.pending_rcpt_cmds == 0) {
455 		cmd_data_next(cmd, data_cmd);
456 	} else {
457 		smtp_server_command_add_hook(
458 			command, SMTP_SERVER_COMMAND_HOOK_NEXT,
459 			cmd_data_next, data_cmd);
460 	}
461 }
462 
463 /* DATA command */
464 
465 static void
cmd_data_start(struct smtp_server_cmd_ctx * cmd,struct cmd_data_context * data_cmd)466 cmd_data_start(struct smtp_server_cmd_ctx *cmd,
467 	       struct cmd_data_context *data_cmd)
468 {
469 	struct smtp_server_connection *conn = cmd->conn;
470 	struct smtp_server_transaction *trans = conn->state.trans;
471 	struct istream *dot_input;
472 
473 	/* called when all previous commands were finished */
474 	i_assert(conn->state.pending_mail_cmds == 0 &&
475 		conn->state.pending_rcpt_cmds == 0);
476 
477 	if (trans != NULL)
478 		smtp_server_transaction_data_command(trans, cmd);
479 
480 	/* check whether we have had successful mail and rcpt commands */
481 	if (!smtp_server_connection_data_check_state(cmd))
482 		return;
483 
484 	/* don't allow classic DATA when CHUNKING sequence was started before */
485 	if (conn->state.data_chunks > 0) {
486 		smtp_server_command_fail(cmd->cmd,
487 			503, "5.5.0", "Bad sequence of commands");
488 		return;
489 	}
490 
491 	smtp_server_connection_set_state(conn, SMTP_SERVER_STATE_DATA, NULL);
492 
493 	/* confirm initial success to client */
494 	smtp_server_connection_reply_immediate(conn, 354, "OK");
495 
496 	/* start reading message data from client */
497 	dot_input = smtp_command_parse_data_with_dot(conn->smtp_parser);
498 	cmd_data_start_input(cmd, data_cmd, dot_input);
499 	i_stream_unref(&dot_input);
500 }
501 
smtp_server_cmd_data(struct smtp_server_cmd_ctx * cmd,const char * params)502 void smtp_server_cmd_data(struct smtp_server_cmd_ctx *cmd,
503 			  const char *params)
504 {
505 	struct smtp_server_connection *conn = cmd->conn;
506 	struct smtp_server_command *command = cmd->cmd;
507 	struct cmd_data_context *data_cmd;
508 
509 	/* data = "DATA" CRLF */
510 	if (*params != '\0') {
511 		smtp_server_reply(cmd,
512 			501, "5.5.4", "Invalid parameters");
513 		return;
514 	}
515 
516 	smtp_server_command_input_lock(cmd);
517 
518 	data_cmd = p_new(cmd->pool, struct cmd_data_context, 1);
519 	data_cmd->chunk_first = TRUE;
520 	data_cmd->chunk_last = TRUE;
521 	data_cmd->client_input = TRUE;
522 	command->data = data_cmd;
523 
524 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_NEXT,
525 				     cmd_data_start, data_cmd);
526 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_REPLIED_ONE,
527 				     cmd_data_replied_one, data_cmd);
528 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_REPLIED,
529 				     cmd_data_replied, data_cmd);
530 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_DESTROY,
531 				     cmd_data_destroy, data_cmd);
532 
533 	conn->state.pending_data_cmds++;
534 }
535 
536 /* BDAT/B... commands */
537 
smtp_server_connection_data_chunk_init(struct smtp_server_cmd_ctx * cmd)538 void smtp_server_connection_data_chunk_init(struct smtp_server_cmd_ctx *cmd)
539 {
540 	struct smtp_server_connection *conn = cmd->conn;
541 	struct smtp_server_command *command = cmd->cmd;
542 	struct cmd_data_context *data_cmd;
543 
544 	data_cmd = p_new(cmd->pool, struct cmd_data_context, 1);
545 	data_cmd->chunking = TRUE;
546 	data_cmd->chunk_first = (conn->state.data_chunks++ == 0);
547 	command->data = data_cmd;
548 
549 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_REPLIED,
550 				     cmd_data_chunk_replied, data_cmd);
551 	smtp_server_command_add_hook(command, SMTP_SERVER_COMMAND_HOOK_DESTROY,
552 				     cmd_data_destroy, data_cmd);
553 
554 	conn->state.pending_data_cmds++;
555 
556 	if (!conn->state.data_failed && conn->state.data_chain == NULL) {
557 		i_assert(data_cmd->chunk_first);
558 		i_assert(conn->state.data_chain_input == NULL);
559 		conn->state.data_chain_input =
560 			i_stream_create_chain(&conn->state.data_chain);
561 	}
562 }
563 
smtp_server_connection_data_chunk_add(struct smtp_server_cmd_ctx * cmd,struct istream * chunk,uoff_t chunk_size,bool chunk_last,bool client_input)564 int smtp_server_connection_data_chunk_add(struct smtp_server_cmd_ctx *cmd,
565 	struct istream *chunk, uoff_t chunk_size, bool chunk_last,
566 	bool client_input)
567 {
568 	struct smtp_server_connection *conn = cmd->conn;
569 	struct smtp_server_transaction *trans = conn->state.trans;
570 	const struct smtp_server_settings *set = &conn->set;
571 	struct smtp_server_command *command = cmd->cmd;
572 	struct cmd_data_context *data_cmd = command->data;
573 	uoff_t new_size;
574 
575 	i_assert(data_cmd != NULL);
576 
577 	if (trans != NULL)
578 		smtp_server_transaction_data_command(trans, cmd);
579 
580 	if (!smtp_server_connection_data_check_state(cmd))
581 		return -1;
582 
583 	/* check message size increase early */
584 	new_size = conn->state.data_size + chunk_size;
585 	if (new_size < conn->state.data_size ||
586 	    (set->max_message_size > 0 && new_size > set->max_message_size)) {
587 		smtp_server_cmd_data_size_limit_exceeded(cmd);
588 		return -1;
589 	}
590 	conn->state.data_size = new_size;
591 
592 	if (chunk_last) {
593 		smtp_server_command_remove_hook(
594 			command, SMTP_SERVER_COMMAND_HOOK_REPLIED,
595 			cmd_data_chunk_replied);
596 		smtp_server_command_add_hook(
597 			command, SMTP_SERVER_COMMAND_HOOK_REPLIED,
598 			cmd_data_replied, data_cmd);
599 	}
600 
601 	data_cmd->chunk_input = chunk;
602 	data_cmd->chunk_size = chunk_size;
603 	data_cmd->chunk_last = chunk_last;
604 	data_cmd->client_input = client_input;
605 	i_stream_ref(chunk);
606 
607 	cmd_data_start_input(cmd, data_cmd, conn->state.data_chain_input);
608 	i_stream_unref(&conn->state.data_chain_input);
609 	return 0;
610 }
611 
612 /* BDAT command */
613 
smtp_server_cmd_bdat(struct smtp_server_cmd_ctx * cmd,const char * params)614 void smtp_server_cmd_bdat(struct smtp_server_cmd_ctx *cmd,
615 			  const char *params)
616 {
617 	struct smtp_server_connection *conn = cmd->conn;
618 	struct istream *input = NULL;
619 	uoff_t size = 0;
620 	const char *const *argv;
621 	bool chunk_last = FALSE;
622 	int ret = 1;
623 
624 	if ((conn->set.capabilities & SMTP_CAPABILITY_CHUNKING) == 0) {
625 		smtp_server_reply(cmd,
626 			502, "5.5.1", "Unsupported command");
627 		return;
628 	}
629 
630 	smtp_server_connection_data_chunk_init(cmd);
631 
632 	/* bdat-cmd   = "BDAT" SP chunk-size [ SP end-marker ] CR LF
633 	   chunk-size = 1*DIGIT
634 	   end-marker = "LAST"
635 	 */
636 	argv = t_strsplit(params, " ");
637 	if (argv[0] == NULL || str_to_uoff(argv[0], &size) < 0) {
638 		smtp_server_reply(cmd,
639 			501, "5.5.4", "Invalid chunk size parameter");
640 		size = 0;
641 		ret = -1;
642 	} else if (argv[1] != NULL) {
643 		if (argv[2] != NULL) {
644 			smtp_server_reply(cmd,
645 				501, "5.5.4", "Invalid parameters");
646 			ret = -1;
647 		} else if (strcasecmp(argv[1], "LAST") != 0) {
648 			smtp_server_reply(cmd,
649 				501, "5.5.4", "Invalid end marker parameter");
650 			ret = -1;
651 		} else {
652 			chunk_last = TRUE;
653 		}
654 	}
655 
656 	if (ret > 0 || (size > 0 && !conn->disconnected)) {
657 		/* Read/skip data even in case of error, as long as size is
658 		   known and connection is still usable. */
659 		input = smtp_command_parse_data_with_size(conn->smtp_parser,
660 							  size);
661 	}
662 
663 	if (ret < 0) {
664 		i_stream_unref(&input);
665 		return;
666 	}
667 
668 	(void)smtp_server_connection_data_chunk_add(cmd,
669 		input, size, chunk_last, TRUE);
670 	i_stream_unref(&input);
671 }
672