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