1 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "llist.h"
5 #include "ioloop.h"
6 #include "net.h"
7 #include "istream.h"
8 #include "istream-crlf.h"
9 #include "ostream.h"
10 #include "str.h"
11 #include "str-sanitize.h"
12 #include "dns-lookup.h"
13
14 #include "smtp-common.h"
15 #include "smtp-address.h"
16 #include "smtp-params.h"
17 #include "smtp-client-private.h"
18 #include "smtp-client-command.h"
19 #include "smtp-client-transaction.h"
20
21 #include <ctype.h>
22
23 const char *const smtp_client_transaction_state_names[] = {
24 "new",
25 "mail_from",
26 "rcpt_to",
27 "data",
28 "reset",
29 "finished",
30 "aborted"
31 };
32
33 static void
34 smtp_client_transaction_submit_more(struct smtp_client_transaction *trans);
35 static void
36 smtp_client_transaction_submit(struct smtp_client_transaction *trans,
37 bool start);
38
39 static void
40 smtp_client_transaction_try_complete(struct smtp_client_transaction *trans);
41
42 static void
43 smtp_client_transaction_send_data(struct smtp_client_transaction *trans);
44 static void
45 smtp_client_transaction_send_reset(struct smtp_client_transaction *trans);
46
47 /*
48 * Sender
49 */
50
51 static struct smtp_client_transaction_mail *
smtp_client_transaction_mail_new(struct smtp_client_transaction * trans,const struct smtp_address * mail_from,const struct smtp_params_mail * mail_params)52 smtp_client_transaction_mail_new(struct smtp_client_transaction *trans,
53 const struct smtp_address *mail_from,
54 const struct smtp_params_mail *mail_params)
55 {
56 struct smtp_client_transaction_mail *mail;
57 pool_t pool;
58
59 pool = pool_alloconly_create("smtp transaction mail", 512);
60 mail = p_new(pool, struct smtp_client_transaction_mail, 1);
61 mail->pool = pool;
62 mail->trans = trans;
63 mail->mail_from = smtp_address_clone(pool, mail_from);
64 smtp_params_mail_copy(pool, &mail->mail_params, mail_params);
65
66 DLLIST2_APPEND(&trans->mail_head, &trans->mail_tail, mail);
67 if (trans->mail_send == NULL)
68 trans->mail_send = mail;
69
70 return mail;
71 }
72
73 static void
smtp_client_transaction_mail_free(struct smtp_client_transaction_mail ** _mail)74 smtp_client_transaction_mail_free(struct smtp_client_transaction_mail **_mail)
75 {
76 struct smtp_client_transaction_mail *mail = *_mail;
77 struct smtp_client_transaction *trans = mail->trans;
78
79 *_mail = NULL;
80
81 if (mail->cmd_mail_from != NULL)
82 smtp_client_command_abort(&mail->cmd_mail_from);
83 DLLIST2_REMOVE(&trans->mail_head, &trans->mail_tail, mail);
84 pool_unref(&mail->pool);
85 }
86
87 static void
smtp_client_transaction_mail_replied(struct smtp_client_transaction_mail ** _mail,const struct smtp_reply * reply)88 smtp_client_transaction_mail_replied(
89 struct smtp_client_transaction_mail **_mail,
90 const struct smtp_reply *reply)
91 {
92 struct smtp_client_transaction_mail *mail = *_mail;
93 smtp_client_command_callback_t *mail_callback = mail->mail_callback;
94 void *context = mail->context;
95
96 mail->mail_callback = NULL;
97
98 /* call the callback */
99 if (mail_callback != NULL)
100 mail_callback(reply, context);
101
102 smtp_client_transaction_mail_free(_mail);
103 }
104
smtp_client_transaction_mail_abort(struct smtp_client_transaction_mail ** _mail)105 void smtp_client_transaction_mail_abort(
106 struct smtp_client_transaction_mail **_mail)
107 {
108 struct smtp_client_transaction_mail *mail = *_mail;
109 struct smtp_client_transaction *trans = mail->trans;
110
111 i_assert(trans->state <= SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM ||
112 trans->state == SMTP_CLIENT_TRANSACTION_STATE_ABORTED);
113
114 smtp_client_transaction_mail_free(_mail);
115 }
116
117 static void
smtp_client_transaction_mail_fail_reply(struct smtp_client_transaction_mail ** _mail,const struct smtp_reply * reply)118 smtp_client_transaction_mail_fail_reply(
119 struct smtp_client_transaction_mail **_mail,
120 const struct smtp_reply *reply)
121 {
122 struct smtp_client_transaction_mail *mail = *_mail;
123 smtp_client_command_callback_t *callback = mail->mail_callback;
124 void *context = mail->context;
125
126 mail->mail_callback = NULL;
127
128 if (callback != NULL)
129 callback(reply, context);
130
131 smtp_client_transaction_mail_free(_mail);
132 }
133
134 /*
135 * Recipient
136 */
137
138 static void
smtp_client_transaction_rcpt_update_event(struct smtp_client_transaction_rcpt * rcpt)139 smtp_client_transaction_rcpt_update_event(
140 struct smtp_client_transaction_rcpt *rcpt)
141 {
142 const char *to = smtp_address_encode(rcpt->rcpt_to);
143
144 event_set_append_log_prefix(rcpt->event,
145 t_strdup_printf("rcpt <%s>: ", str_sanitize(to, 128)));
146 event_add_str(rcpt->event, "rcpt_to", to);
147 smtp_params_rcpt_add_to_event(&rcpt->rcpt_params, rcpt->event);
148 }
149
150 static struct smtp_client_transaction_rcpt *
smtp_client_transaction_rcpt_new(struct smtp_client_transaction * trans,pool_t pool,const struct smtp_address * rcpt_to,const struct smtp_params_rcpt * rcpt_params)151 smtp_client_transaction_rcpt_new(
152 struct smtp_client_transaction *trans, pool_t pool,
153 const struct smtp_address *rcpt_to,
154 const struct smtp_params_rcpt *rcpt_params)
155 {
156 struct smtp_client_transaction_rcpt *rcpt;
157
158 pool_ref(pool);
159
160 rcpt = p_new(pool, struct smtp_client_transaction_rcpt, 1);
161 rcpt->pool = pool;
162 rcpt->trans = trans;
163 rcpt->rcpt_to = smtp_address_clone(pool, rcpt_to);
164 smtp_params_rcpt_copy(pool, &rcpt->rcpt_params, rcpt_params);
165
166 DLLIST2_APPEND(&trans->rcpts_queue_head, &trans->rcpts_queue_tail,
167 rcpt);
168 trans->rcpts_queue_count++;
169 rcpt->queued = TRUE;
170 if (trans->rcpts_send == NULL)
171 trans->rcpts_send = rcpt;
172
173 rcpt->event = event_create(trans->event);
174 smtp_client_transaction_rcpt_update_event(rcpt);
175
176 trans->rcpts_total++;
177
178 return rcpt;
179 }
180
181 static void
smtp_client_transaction_rcpt_free(struct smtp_client_transaction_rcpt ** _rcpt)182 smtp_client_transaction_rcpt_free(
183 struct smtp_client_transaction_rcpt **_rcpt)
184 {
185 struct smtp_client_transaction_rcpt *rcpt = *_rcpt;
186 struct smtp_client_transaction *trans = rcpt->trans;
187
188 *_rcpt = NULL;
189
190 if (trans->rcpts_send == rcpt)
191 trans->rcpts_send = rcpt->next;
192 if (trans->rcpts_data == rcpt)
193 trans->rcpts_data = rcpt->next;
194 if (rcpt->queued) {
195 DLLIST2_REMOVE(&trans->rcpts_queue_head,
196 &trans->rcpts_queue_tail, rcpt);
197 trans->rcpts_queue_count--;
198 } else {
199 DLLIST2_REMOVE(&trans->rcpts_head,
200 &trans->rcpts_tail, rcpt);
201 trans->rcpts_count--;
202 }
203
204 if (!rcpt->finished) {
205 struct smtp_reply failure;
206
207 trans->rcpts_aborted++;
208
209 smtp_reply_init(&failure,
210 SMTP_CLIENT_COMMAND_ERROR_ABORTED,
211 "Aborted");
212 failure.enhanced_code = SMTP_REPLY_ENH_CODE(9, 0, 0);
213
214 struct event_passthrough *e =
215 event_create_passthrough(rcpt->event)->
216 set_name("smtp_client_transaction_rcpt_finished");
217 smtp_reply_add_to_event(&failure, e);
218 e_debug(e->event(), "Aborted");
219 }
220
221 event_unref(&rcpt->event);
222
223 if (rcpt->queued || rcpt->external_pool) {
224 i_assert(rcpt->pool != NULL);
225 pool_unref(&rcpt->pool);
226 }
227 }
228
229 static void
smtp_client_transaction_rcpt_approved(struct smtp_client_transaction_rcpt ** _rcpt)230 smtp_client_transaction_rcpt_approved(
231 struct smtp_client_transaction_rcpt **_rcpt)
232 {
233 struct smtp_client_transaction_rcpt *prcpt = *_rcpt;
234 struct smtp_client_transaction *trans = prcpt->trans;
235 struct smtp_client_transaction_rcpt *rcpt;
236 pool_t pool;
237
238 i_assert(prcpt->queued);
239
240 if (prcpt->external_pool) {
241 /* allocated externally; just remove it from the queue */
242 prcpt->queued = FALSE;
243 if (trans->rcpts_send == prcpt)
244 trans->rcpts_send = prcpt->next;
245 DLLIST2_REMOVE(&trans->rcpts_queue_head,
246 &trans->rcpts_queue_tail, prcpt);
247 trans->rcpts_queue_count--;
248
249 rcpt = prcpt;
250 } else {
251 /* move to transaction pool */
252 pool = trans->pool;
253 rcpt = p_new(pool, struct smtp_client_transaction_rcpt, 1);
254 rcpt->trans = trans;
255 rcpt->rcpt_to = smtp_address_clone(pool, prcpt->rcpt_to);
256 smtp_params_rcpt_copy(pool, &rcpt->rcpt_params,
257 &prcpt->rcpt_params);
258 rcpt->data_callback = prcpt->data_callback;
259 rcpt->data_context = prcpt->data_context;
260
261 rcpt->event = prcpt->event;
262 event_ref(rcpt->event);
263
264 /* free the old object, thereby removing it from the queue */
265 smtp_client_transaction_rcpt_free(&prcpt);
266 }
267
268 /* recipient is approved */
269 DLLIST2_APPEND(&trans->rcpts_head, &trans->rcpts_tail, rcpt);
270 trans->rcpts_count++;
271 if (trans->rcpts_data == NULL)
272 trans->rcpts_data = trans->rcpts_head;
273
274 *_rcpt = rcpt;
275 }
276
277 static void
smtp_client_transaction_rcpt_denied(struct smtp_client_transaction_rcpt ** _rcpt,const struct smtp_reply * reply)278 smtp_client_transaction_rcpt_denied(
279 struct smtp_client_transaction_rcpt **_rcpt,
280 const struct smtp_reply *reply)
281 {
282 struct smtp_client_transaction_rcpt *prcpt = *_rcpt;
283 struct smtp_client_transaction *trans = prcpt->trans;
284
285 *_rcpt = NULL;
286
287 trans->rcpts_denied++;
288 trans->rcpts_failed++;
289
290 struct event_passthrough *e =
291 event_create_passthrough(prcpt->event)->
292 set_name("smtp_client_transaction_rcpt_finished");
293 smtp_reply_add_to_event(reply, e);
294 e_debug(e->event(), "Denied");
295
296 /* not pending anymore */
297 smtp_client_transaction_rcpt_free(&prcpt);
298 }
299
300 static void
smtp_client_transaction_rcpt_replied(struct smtp_client_transaction_rcpt ** _rcpt,const struct smtp_reply * reply)301 smtp_client_transaction_rcpt_replied(
302 struct smtp_client_transaction_rcpt **_rcpt,
303 const struct smtp_reply *reply)
304 {
305 struct smtp_client_transaction_rcpt *rcpt = *_rcpt;
306 bool success = smtp_reply_is_success(reply);
307 smtp_client_command_callback_t *rcpt_callback = rcpt->rcpt_callback;
308 void *context = rcpt->context;
309
310 rcpt->rcpt_callback = NULL;
311
312 if (rcpt->finished)
313 return;
314 rcpt->finished = !success;
315
316 if (success)
317 smtp_client_transaction_rcpt_approved(_rcpt);
318 else
319 smtp_client_transaction_rcpt_denied(_rcpt, reply);
320
321 /* call the callback */
322 if (rcpt_callback != NULL)
323 rcpt_callback(reply, context);
324 }
325
smtp_client_transaction_rcpt_abort(struct smtp_client_transaction_rcpt ** _rcpt)326 void smtp_client_transaction_rcpt_abort(
327 struct smtp_client_transaction_rcpt **_rcpt)
328 {
329 struct smtp_client_transaction_rcpt *rcpt = *_rcpt;
330 struct smtp_client_transaction *trans = rcpt->trans;
331
332 i_assert(rcpt->queued || rcpt->external_pool);
333
334 i_assert(trans->state <= SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO ||
335 trans->state == SMTP_CLIENT_TRANSACTION_STATE_ABORTED);
336
337 smtp_client_transaction_rcpt_free(_rcpt);
338 }
339
340 static void
smtp_client_transaction_rcpt_fail_reply(struct smtp_client_transaction_rcpt ** _rcpt,const struct smtp_reply * reply)341 smtp_client_transaction_rcpt_fail_reply(
342 struct smtp_client_transaction_rcpt **_rcpt,
343 const struct smtp_reply *reply)
344 {
345 struct smtp_client_transaction_rcpt *rcpt = *_rcpt;
346 struct smtp_client_transaction *trans = rcpt->trans;
347 smtp_client_command_callback_t *callback;
348 void *context;
349
350 if (rcpt->finished)
351 return;
352 rcpt->finished = TRUE;
353
354 trans->rcpts_failed++;
355
356 if (rcpt->queued) {
357 callback = rcpt->rcpt_callback;
358 context = rcpt->context;
359 } else {
360 callback = rcpt->data_callback;
361 context = rcpt->data_context;
362 }
363 rcpt->rcpt_callback = NULL;
364 rcpt->data_callback = NULL;
365
366 struct event_passthrough *e =
367 event_create_passthrough(rcpt->event)->
368 set_name("smtp_client_transaction_rcpt_finished");
369 smtp_reply_add_to_event(reply, e);
370 e_debug(e->event(), "Failed");
371
372 if (callback != NULL)
373 callback(reply, context);
374
375 smtp_client_transaction_rcpt_free(_rcpt);
376 }
377
378 static void
smtp_client_transaction_rcpt_finished(struct smtp_client_transaction_rcpt * rcpt,const struct smtp_reply * reply)379 smtp_client_transaction_rcpt_finished(struct smtp_client_transaction_rcpt *rcpt,
380 const struct smtp_reply *reply)
381 {
382 struct smtp_client_transaction *trans = rcpt->trans;
383
384 i_assert(!rcpt->finished);
385 rcpt->finished = TRUE;
386
387 if (smtp_reply_is_success(reply))
388 trans->rcpts_succeeded++;
389 else
390 trans->rcpts_failed++;
391
392 struct event_passthrough *e =
393 event_create_passthrough(rcpt->event)->
394 set_name("smtp_client_transaction_rcpt_finished");
395 smtp_reply_add_to_event(reply, e);
396 e_debug(e->event(), "Finished");
397
398 if (rcpt->data_callback != NULL)
399 rcpt->data_callback(reply, rcpt->data_context);
400 rcpt->data_callback = NULL;
401 }
402
403 #undef smtp_client_transaction_rcpt_set_data_callback
smtp_client_transaction_rcpt_set_data_callback(struct smtp_client_transaction_rcpt * rcpt,smtp_client_command_callback_t * callback,void * context)404 void smtp_client_transaction_rcpt_set_data_callback(
405 struct smtp_client_transaction_rcpt *rcpt,
406 smtp_client_command_callback_t *callback, void *context)
407 {
408 i_assert(!rcpt->finished);
409
410 rcpt->data_callback = callback;
411 rcpt->data_context = context;
412 }
413
414 /*
415 * Transaction
416 */
417
418 static void
smtp_client_transaction_update_event(struct smtp_client_transaction * trans)419 smtp_client_transaction_update_event(struct smtp_client_transaction *trans)
420 {
421 event_set_append_log_prefix(trans->event, "transaction: ");
422 }
423
424 static struct event_passthrough *
smtp_client_transaction_result_event(struct smtp_client_transaction * trans,const struct smtp_reply * reply)425 smtp_client_transaction_result_event(struct smtp_client_transaction *trans,
426 const struct smtp_reply *reply)
427 {
428 struct event_passthrough *e;
429 unsigned int rcpts_aborted = trans->rcpts_aborted +
430 trans->rcpts_queue_count;
431
432 e = event_create_passthrough(trans->event)->
433 set_name("smtp_client_transaction_finished")->
434 add_int("recipients", trans->rcpts_total)->
435 add_int("recipients_aborted", rcpts_aborted)->
436 add_int("recipients_denied", trans->rcpts_denied)->
437 add_int("recipients_failed", trans->rcpts_failed)->
438 add_int("recipients_succeeded", trans->rcpts_succeeded);
439
440 smtp_reply_add_to_event(reply, e);
441 if (trans->reset)
442 e->add_str("is_reset", "yes");
443 return e;
444 }
445
446 #undef smtp_client_transaction_create_empty
447 struct smtp_client_transaction *
smtp_client_transaction_create_empty(struct smtp_client_connection * conn,enum smtp_client_transaction_flags flags,smtp_client_transaction_callback_t * callback,void * context)448 smtp_client_transaction_create_empty(
449 struct smtp_client_connection *conn,
450 enum smtp_client_transaction_flags flags,
451 smtp_client_transaction_callback_t *callback, void *context)
452 {
453 struct smtp_client_transaction *trans;
454 pool_t pool;
455
456 if (conn->protocol == SMTP_PROTOCOL_LMTP)
457 flags |= SMTP_CLIENT_TRANSACTION_FLAG_REPLY_PER_RCPT;
458
459 pool = pool_alloconly_create("smtp transaction", 4096);
460 trans = p_new(pool, struct smtp_client_transaction, 1);
461 trans->refcount = 1;
462 trans->pool = pool;
463 trans->flags = flags;
464 trans->callback = callback;
465 trans->context = context;
466
467 trans->event = event_create(conn->event);
468 smtp_client_transaction_update_event(trans);
469
470 trans->conn = conn;
471 smtp_client_connection_ref(conn);
472
473 e_debug(trans->event, "Created");
474
475 return trans;
476 }
477
478 #undef smtp_client_transaction_create
479 struct smtp_client_transaction *
smtp_client_transaction_create(struct smtp_client_connection * conn,const struct smtp_address * mail_from,const struct smtp_params_mail * mail_params,enum smtp_client_transaction_flags flags,smtp_client_transaction_callback_t * callback,void * context)480 smtp_client_transaction_create(struct smtp_client_connection *conn,
481 const struct smtp_address *mail_from,
482 const struct smtp_params_mail *mail_params,
483 enum smtp_client_transaction_flags flags,
484 smtp_client_transaction_callback_t *callback, void *context)
485 {
486 struct smtp_client_transaction *trans;
487
488 trans = smtp_client_transaction_create_empty(conn, flags,
489 callback, context);
490 (void)smtp_client_transaction_mail_new(trans, mail_from, mail_params);
491 return trans;
492 }
493
494 static void
smtp_client_transaction_finish(struct smtp_client_transaction * trans,const struct smtp_reply * final_reply)495 smtp_client_transaction_finish(struct smtp_client_transaction *trans,
496 const struct smtp_reply *final_reply)
497 {
498 struct smtp_client_connection *conn = trans->conn;
499
500 if (trans->state >= SMTP_CLIENT_TRANSACTION_STATE_FINISHED)
501 return;
502
503 timeout_remove(&trans->to_finish);
504
505 struct event_passthrough *e =
506 smtp_client_transaction_result_event(trans, final_reply);
507 e_debug(e->event(), "Finished");
508
509 io_loop_time_refresh();
510 trans->times.finished = ioloop_timeval;
511
512 i_assert(trans->to_send == NULL);
513
514 trans->state = SMTP_CLIENT_TRANSACTION_STATE_FINISHED;
515 i_assert(trans->callback != NULL);
516 trans->callback(trans->context);
517
518 if (!trans->submitted_data)
519 smtp_client_connection_abort_transaction(conn, trans);
520
521 smtp_client_transaction_unref(&trans);
522 }
523
smtp_client_transaction_abort(struct smtp_client_transaction * trans)524 void smtp_client_transaction_abort(struct smtp_client_transaction *trans)
525 {
526 struct smtp_client_connection *conn = trans->conn;
527
528 if (trans->failing) {
529 e_debug(trans->event, "Abort (already failing)");
530 return;
531 }
532
533 e_debug(trans->event, "Abort");
534
535 /* clean up */
536 i_stream_unref(&trans->data_input);
537 timeout_remove(&trans->to_send);
538 timeout_remove(&trans->to_finish);
539
540 trans->cmd_last = NULL;
541
542 /* abort any pending commands */
543 while (trans->mail_head != NULL) {
544 struct smtp_client_transaction_mail *mail = trans->mail_head;
545
546 if (mail->cmd_mail_from != NULL)
547 smtp_client_command_abort(&mail->cmd_mail_from);
548 smtp_client_transaction_mail_free(&mail);
549 }
550 while (trans->rcpts_queue_count > 0) {
551 struct smtp_client_transaction_rcpt *rcpt =
552 trans->rcpts_queue_head;
553
554 if (rcpt->cmd_rcpt_to != NULL)
555 smtp_client_command_abort(&rcpt->cmd_rcpt_to);
556 smtp_client_transaction_rcpt_free(&rcpt);
557 }
558 if (trans->cmd_data != NULL)
559 smtp_client_command_abort(&trans->cmd_data);
560 if (trans->cmd_rset != NULL)
561 smtp_client_command_abort(&trans->cmd_rset);
562 if (trans->cmd_plug != NULL)
563 smtp_client_command_abort(&trans->cmd_plug);
564 trans->cmd_data = NULL;
565 trans->cmd_rset = NULL;
566 trans->cmd_plug = NULL;
567
568 smtp_client_connection_abort_transaction(conn, trans);
569
570 /* abort if not finished */
571 if (trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED) {
572 struct event_passthrough *e;
573
574 if (trans->failure != NULL) {
575 e = smtp_client_transaction_result_event(
576 trans, trans->failure);
577 e_debug(e->event(), "Failed");
578 } else {
579 struct smtp_reply failure;
580
581 smtp_reply_init(&failure,
582 SMTP_CLIENT_COMMAND_ERROR_ABORTED,
583 "Aborted");
584 failure.enhanced_code = SMTP_REPLY_ENH_CODE(9, 0, 0);
585
586 e = smtp_client_transaction_result_event(
587 trans, &failure);
588 e_debug(e->event(), "Aborted");
589 }
590
591 trans->state = SMTP_CLIENT_TRANSACTION_STATE_ABORTED;
592 i_assert(trans->callback != NULL);
593 trans->callback(trans->context);
594
595 smtp_client_transaction_unref(&trans);
596 }
597 }
598
smtp_client_transaction_ref(struct smtp_client_transaction * trans)599 void smtp_client_transaction_ref(struct smtp_client_transaction *trans)
600 {
601 trans->refcount++;
602 }
603
smtp_client_transaction_unref(struct smtp_client_transaction ** _trans)604 void smtp_client_transaction_unref(struct smtp_client_transaction **_trans)
605 {
606 struct smtp_client_transaction *trans = *_trans;
607 struct smtp_client_connection *conn;
608
609 *_trans = NULL;
610
611 if (trans == NULL)
612 return;
613 conn = trans->conn;
614
615 i_assert(trans->refcount > 0);
616 if (--trans->refcount > 0)
617 return;
618
619 e_debug(trans->event, "Destroy");
620
621 i_stream_unref(&trans->data_input);
622 smtp_client_transaction_abort(trans);
623
624 while (trans->rcpts_count > 0) {
625 struct smtp_client_transaction_rcpt *rcpt =
626 trans->rcpts_head;
627 smtp_client_transaction_rcpt_free(&rcpt);
628 }
629
630 i_assert(trans->state >= SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
631 event_unref(&trans->event);
632 pool_unref(&trans->pool);
633
634 smtp_client_connection_unref(&conn);
635 }
636
smtp_client_transaction_destroy(struct smtp_client_transaction ** _trans)637 void smtp_client_transaction_destroy(struct smtp_client_transaction **_trans)
638 {
639 struct smtp_client_transaction *trans = *_trans;
640 struct smtp_client_transaction_mail *mail;
641 struct smtp_client_transaction_rcpt *rcpt;
642
643 *_trans = NULL;
644
645 if (trans == NULL)
646 return;
647
648 smtp_client_transaction_ref(trans);
649 smtp_client_transaction_abort(trans);
650
651 /* Make sure this transaction doesn't produce any more callbacks.
652 We cannot fully abort (destroy) these commands, as this may be
653 called from a callback. */
654 for (mail = trans->mail_head; mail != NULL; mail = mail->next) {
655 if (mail->cmd_mail_from != NULL)
656 smtp_client_command_drop_callback(mail->cmd_mail_from);
657 }
658 for (rcpt = trans->rcpts_queue_head; rcpt != NULL; rcpt = rcpt->next) {
659 if (rcpt->cmd_rcpt_to != NULL)
660 smtp_client_command_drop_callback(rcpt->cmd_rcpt_to);
661 }
662 if (trans->cmd_data != NULL)
663 smtp_client_command_drop_callback(trans->cmd_data);
664 if (trans->cmd_rset != NULL)
665 smtp_client_command_drop_callback(trans->cmd_rset);
666 if (trans->cmd_plug != NULL)
667 smtp_client_command_abort(&trans->cmd_plug);
668
669 /* Free any approved recipients early */
670 while (trans->rcpts_count > 0) {
671 struct smtp_client_transaction_rcpt *rcpt =
672 trans->rcpts_head;
673 smtp_client_transaction_rcpt_free(&rcpt);
674 }
675
676 if (trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED) {
677 struct smtp_client_transaction *trans_tmp = trans;
678
679 trans->state = SMTP_CLIENT_TRANSACTION_STATE_ABORTED;
680 smtp_client_transaction_unref(&trans_tmp);
681 }
682
683 smtp_client_transaction_unref(&trans);
684 }
685
smtp_client_transaction_fail_reply(struct smtp_client_transaction * trans,const struct smtp_reply * reply)686 void smtp_client_transaction_fail_reply(struct smtp_client_transaction *trans,
687 const struct smtp_reply *reply)
688 {
689 struct smtp_client_transaction_rcpt *rcpt, *rcpt_next;
690
691 if (reply == NULL)
692 reply = trans->failure;
693 i_assert(reply != NULL);
694
695 trans->failing = TRUE;
696
697 e_debug(trans->event, "Returning failure: %s", smtp_reply_log(reply));
698
699 /* hold a reference to prevent early destruction in a callback */
700 smtp_client_transaction_ref(trans);
701
702 trans->cmd_last = NULL;
703
704 timeout_remove(&trans->to_send);
705
706 /* MAIL */
707 while (trans->mail_head != NULL) {
708 struct smtp_client_transaction_mail *mail = trans->mail_head;
709
710 if (mail->cmd_mail_from != NULL)
711 smtp_client_command_abort(&mail->cmd_mail_from);
712 smtp_client_transaction_mail_fail_reply(&mail, reply);
713 }
714
715 /* RCPT */
716 rcpt = trans->rcpts_queue_head;
717 while (rcpt != NULL) {
718 struct smtp_client_command *cmd = rcpt->cmd_rcpt_to;
719
720 rcpt_next = rcpt->next;
721
722 rcpt->cmd_rcpt_to = NULL;
723 if (cmd != NULL)
724 smtp_client_command_fail_reply(&cmd, reply);
725 else
726 smtp_client_transaction_rcpt_fail_reply(&rcpt, reply);
727
728 rcpt = rcpt_next;
729 }
730
731 /* DATA / RSET */
732 if (!trans->data_provided && !trans->reset) {
733 /* none of smtp_client_transaction_send() and
734 smtp_client_transaction_reset() was called so far
735 */
736 } else if (trans->cmd_data != NULL) {
737 /* the DATA command is still pending; handle the failure by
738 failing the DATA command. */
739 smtp_client_command_fail_reply(&trans->cmd_data, reply);
740 } else if (trans->cmd_rset != NULL) {
741 /* the RSET command is still pending; handle the failure by
742 failing the RSET command. */
743 smtp_client_command_fail_reply(&trans->cmd_rset, reply);
744 } else {
745 i_assert(!trans->reset);
746
747 /* the DATA command was not sent yet; call all DATA callbacks
748 for the recipients that were previously accepted. */
749 rcpt = trans->rcpts_data;
750 while (rcpt != NULL) {
751 rcpt_next = rcpt->next;
752 smtp_client_transaction_rcpt_fail_reply(&rcpt, reply);
753 rcpt = rcpt_next;
754 }
755 if (trans->data_callback != NULL)
756 trans->data_callback(reply, trans->data_context);
757 trans->data_callback = NULL;
758 }
759
760 /* plug */
761 if (trans->failure == NULL)
762 trans->failure = smtp_reply_clone(trans->pool, reply);
763 if (trans->cmd_plug != NULL)
764 smtp_client_command_abort(&trans->cmd_plug);
765 trans->cmd_plug = NULL;
766
767 trans->failing = FALSE;
768
769 if (trans->data_provided || trans->reset) {
770 /* abort the transaction only if smtp_client_transaction_send()
771 or smtp_client_transaction_reset() was called (and if it is
772 not aborted already) */
773 smtp_client_transaction_abort(trans);
774 }
775
776 /* drop reference held earlier in this function */
777 smtp_client_transaction_unref(&trans);
778 }
779
smtp_client_transaction_fail(struct smtp_client_transaction * trans,unsigned int status,const char * error)780 void smtp_client_transaction_fail(struct smtp_client_transaction *trans,
781 unsigned int status, const char *error)
782 {
783 struct smtp_reply reply;
784
785 smtp_reply_init(&reply, status, error);
786 smtp_client_transaction_fail_reply(trans, &reply);
787 }
788
smtp_client_transaction_set_event(struct smtp_client_transaction * trans,struct event * event)789 void smtp_client_transaction_set_event(struct smtp_client_transaction *trans,
790 struct event *event)
791 {
792 i_assert(trans->conn != NULL);
793 event_unref(&trans->event);
794 trans->event = event_create(event);
795 event_set_forced_debug(trans->event, trans->conn->set.debug);
796 smtp_client_transaction_update_event(trans);
797 }
798
799 static void
smtp_client_transaction_timeout(struct smtp_client_transaction * trans)800 smtp_client_transaction_timeout(struct smtp_client_transaction *trans)
801 {
802 struct smtp_reply reply;
803
804 smtp_reply_printf(&reply, 451,
805 "Remote server not answering "
806 "(transaction timed out while %s)",
807 smtp_client_transaction_get_state_destription(trans));
808 reply.enhanced_code = SMTP_REPLY_ENH_CODE(4, 4, 0);
809
810 smtp_client_transaction_fail_reply(trans, &reply);
811 }
812
smtp_client_transaction_set_timeout(struct smtp_client_transaction * trans,unsigned int timeout_msecs)813 void smtp_client_transaction_set_timeout(struct smtp_client_transaction *trans,
814 unsigned int timeout_msecs)
815 {
816 i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
817
818 trans->finish_timeout_msecs = timeout_msecs;
819
820 if (trans->data_input != NULL && timeout_msecs > 0) {
821 /* adjust timeout if it is already started */
822 timeout_remove(&trans->to_finish);
823 trans->to_finish = timeout_add(trans->finish_timeout_msecs,
824 smtp_client_transaction_timeout, trans);
825 }
826 }
827
828 static void
smtp_client_transaction_mail_cb(const struct smtp_reply * reply,struct smtp_client_transaction * trans)829 smtp_client_transaction_mail_cb(const struct smtp_reply *reply,
830 struct smtp_client_transaction *trans)
831 {
832 struct smtp_client_transaction_mail *mail = trans->mail_head;
833 bool success = smtp_reply_is_success(reply);
834
835 e_debug(trans->event, "Got MAIL reply: %s", smtp_reply_log(reply));
836
837 i_assert(mail != NULL);
838 i_assert(trans->conn != NULL);
839
840 if (success) {
841 if (trans->sender_accepted) {
842 smtp_client_transaction_fail(
843 trans, SMTP_CLIENT_COMMAND_ERROR_BAD_REPLY,
844 "Server accepted more than a single MAIL command.");
845 return;
846 }
847 trans->mail_failure = NULL;
848 trans->sender_accepted = TRUE;
849 }
850
851 /* plug command line pipeline if no RCPT commands are yet issued */
852 if (!trans->immediate && mail->next == NULL &&
853 mail->cmd_mail_from == trans->cmd_last) {
854 trans->cmd_plug = trans->cmd_last =
855 smtp_client_command_plug(trans->conn, trans->cmd_last);
856 }
857 mail->cmd_mail_from = NULL;
858
859 if (trans->rcpts_queue_count > 0)
860 trans->state = SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO;
861 else if (trans->reset)
862 trans->state = SMTP_CLIENT_TRANSACTION_STATE_RESET;
863
864 {
865 enum smtp_client_transaction_state state;
866 struct smtp_client_transaction *tmp_trans = trans;
867
868 smtp_client_transaction_ref(tmp_trans);
869
870 smtp_client_transaction_mail_replied(&mail, reply);
871
872 state = trans->state;
873 smtp_client_transaction_unref(&tmp_trans);
874 if (state >= SMTP_CLIENT_TRANSACTION_STATE_FINISHED)
875 return;
876 }
877
878 if (!trans->sender_accepted && trans->mail_head != NULL) {
879 /* Update transaction with next MAIL command candidate */
880 mail = trans->mail_head;
881 event_add_str(trans->event, "mail_from",
882 smtp_address_encode(mail->mail_from));
883 smtp_params_mail_add_to_event(&mail->mail_params,
884 trans->event);
885 }
886
887 if (!success && !trans->sender_accepted) {
888 if (trans->state > SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
889 smtp_client_transaction_fail_reply(trans, reply);
890 else if (trans->mail_failure == NULL)
891 trans->mail_failure = smtp_reply_clone(trans->pool, reply);
892 }
893 }
894
895 #undef smtp_client_transaction_add_mail
896 struct smtp_client_transaction_mail *
smtp_client_transaction_add_mail(struct smtp_client_transaction * trans,const struct smtp_address * mail_from,const struct smtp_params_mail * mail_params,smtp_client_command_callback_t * mail_callback,void * context)897 smtp_client_transaction_add_mail(struct smtp_client_transaction *trans,
898 const struct smtp_address *mail_from,
899 const struct smtp_params_mail *mail_params,
900 smtp_client_command_callback_t *mail_callback,
901 void *context)
902 {
903 struct smtp_client_transaction_mail *mail;
904
905 e_debug(trans->event, "Add MAIL command");
906
907 i_assert(!trans->data_provided);
908 i_assert(!trans->reset);
909
910 i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO);
911
912 mail = smtp_client_transaction_mail_new(trans, mail_from, mail_params);
913 mail->mail_callback = mail_callback;
914 mail->context = context;
915
916 smtp_client_transaction_submit(trans, FALSE);
917
918 return mail;
919 }
920
smtp_client_transaction_connection_ready(struct smtp_client_transaction * trans)921 static void smtp_client_transaction_connection_ready(
922 struct smtp_client_transaction *trans)
923 {
924 if (trans->state != SMTP_CLIENT_TRANSACTION_STATE_PENDING)
925 return;
926
927 e_debug(trans->event, "Connecton is ready for transaction");
928
929 trans->state = SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM;
930
931 smtp_client_transaction_submit_more(trans);
932 }
933
934 #undef smtp_client_transaction_start
smtp_client_transaction_start(struct smtp_client_transaction * trans,smtp_client_command_callback_t * mail_callback,void * context)935 void smtp_client_transaction_start(
936 struct smtp_client_transaction *trans,
937 smtp_client_command_callback_t *mail_callback, void *context)
938 {
939 struct smtp_client_connection *conn = trans->conn;
940 struct smtp_client_transaction_mail *mail = trans->mail_head;
941
942 i_assert(trans->state == SMTP_CLIENT_TRANSACTION_STATE_NEW);
943 i_assert(trans->conn != NULL);
944
945 i_assert(mail != NULL);
946 event_add_str(trans->event, "mail_from",
947 smtp_address_encode(mail->mail_from));
948 event_add_str(trans->event, "mail_from_raw",
949 smtp_address_encode_raw(mail->mail_from));
950 smtp_params_mail_add_to_event(&mail->mail_params,
951 trans->event);
952
953 struct event_passthrough *e =
954 event_create_passthrough(trans->event)->
955 set_name("smtp_client_transaction_started");
956 e_debug(e->event(), "Start");
957
958 io_loop_time_refresh();
959 trans->times.started = ioloop_timeval;
960
961 i_assert(mail->mail_callback == NULL);
962
963 mail->mail_callback = mail_callback;
964 mail->context = context;
965
966 trans->state = SMTP_CLIENT_TRANSACTION_STATE_PENDING;
967
968 smtp_client_connection_add_transaction(conn, trans);
969
970 if (trans->immediate &&
971 conn->state == SMTP_CLIENT_CONNECTION_STATE_READY) {
972 trans->state = SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM;
973
974 if (!trans->submitting)
975 smtp_client_transaction_submit_more(trans);
976 }
977 }
978
979 #undef smtp_client_transaction_start_empty
smtp_client_transaction_start_empty(struct smtp_client_transaction * trans,const struct smtp_address * mail_from,const struct smtp_params_mail * mail_params,smtp_client_command_callback_t * mail_callback,void * context)980 void smtp_client_transaction_start_empty(
981 struct smtp_client_transaction *trans,
982 const struct smtp_address *mail_from,
983 const struct smtp_params_mail *mail_params,
984 smtp_client_command_callback_t *mail_callback, void *context)
985 {
986 i_assert(trans->mail_head == NULL);
987
988 (void)smtp_client_transaction_mail_new(trans, mail_from, mail_params);
989
990 smtp_client_transaction_start(trans, mail_callback, context);
991 }
992
993 static void
smtp_client_transaction_rcpt_cb(const struct smtp_reply * reply,struct smtp_client_transaction_rcpt * rcpt)994 smtp_client_transaction_rcpt_cb(const struct smtp_reply *reply,
995 struct smtp_client_transaction_rcpt *rcpt)
996 {
997 struct smtp_client_transaction *trans = rcpt->trans;
998
999 i_assert(trans->conn != NULL);
1000
1001 e_debug(trans->event, "Got RCPT reply: %s", smtp_reply_log(reply));
1002
1003 /* plug command line pipeline if DATA command is not yet issued */
1004 if (!trans->immediate && !trans->reset &&
1005 rcpt->cmd_rcpt_to == trans->cmd_last && trans->cmd_data == NULL) {
1006 trans->cmd_plug = trans->cmd_last =
1007 smtp_client_command_plug(trans->conn, trans->cmd_last);
1008 }
1009 rcpt->cmd_rcpt_to = NULL;
1010
1011 {
1012 enum smtp_client_transaction_state state;
1013 struct smtp_client_transaction *tmp_trans = trans;
1014
1015 smtp_client_transaction_ref(tmp_trans);
1016
1017 smtp_client_transaction_rcpt_replied(&rcpt, reply);
1018
1019 state = trans->state;
1020 smtp_client_transaction_unref(&tmp_trans);
1021 if (state >= SMTP_CLIENT_TRANSACTION_STATE_FINISHED)
1022 return;
1023 }
1024
1025 smtp_client_transaction_try_complete(trans);
1026 }
1027
1028 #undef smtp_client_transaction_add_rcpt
1029 struct smtp_client_transaction_rcpt *
smtp_client_transaction_add_rcpt(struct smtp_client_transaction * trans,const struct smtp_address * rcpt_to,const struct smtp_params_rcpt * rcpt_params,smtp_client_command_callback_t * rcpt_callback,smtp_client_command_callback_t * data_callback,void * context)1030 smtp_client_transaction_add_rcpt(struct smtp_client_transaction *trans,
1031 const struct smtp_address *rcpt_to,
1032 const struct smtp_params_rcpt *rcpt_params,
1033 smtp_client_command_callback_t *rcpt_callback,
1034 smtp_client_command_callback_t *data_callback,
1035 void *context)
1036 {
1037 struct smtp_client_transaction_rcpt *rcpt;
1038 pool_t pool;
1039
1040 e_debug(trans->event, "Add recipient");
1041
1042 i_assert(!trans->data_provided);
1043 i_assert(!trans->reset);
1044
1045 i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
1046
1047 if (trans->mail_head == NULL &&
1048 trans->state == SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
1049 trans->state = SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO;
1050
1051 pool = pool_alloconly_create("smtp transaction rcpt", 512);
1052 rcpt = smtp_client_transaction_rcpt_new(trans, pool,
1053 rcpt_to, rcpt_params);
1054 pool_unref(&pool);
1055
1056 rcpt->rcpt_callback = rcpt_callback;
1057 rcpt->context = context;
1058
1059 rcpt->data_callback = data_callback;
1060 rcpt->data_context = context;
1061
1062 smtp_client_transaction_submit(trans, FALSE);
1063
1064 return rcpt;
1065 }
1066
1067 #undef smtp_client_transaction_add_pool_rcpt
1068 struct smtp_client_transaction_rcpt *
smtp_client_transaction_add_pool_rcpt(struct smtp_client_transaction * trans,pool_t pool,const struct smtp_address * rcpt_to,const struct smtp_params_rcpt * rcpt_params,smtp_client_command_callback_t * rcpt_callback,void * context)1069 smtp_client_transaction_add_pool_rcpt(
1070 struct smtp_client_transaction *trans, pool_t pool,
1071 const struct smtp_address *rcpt_to,
1072 const struct smtp_params_rcpt *rcpt_params,
1073 smtp_client_command_callback_t *rcpt_callback, void *context)
1074 {
1075 struct smtp_client_transaction_rcpt *rcpt;
1076
1077 e_debug(trans->event, "Add recipient (external pool)");
1078
1079 i_assert(!trans->data_provided);
1080 i_assert(!trans->reset);
1081
1082 i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
1083
1084 if (trans->mail_head == NULL &&
1085 trans->state == SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
1086 trans->state = SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO;
1087
1088 rcpt = smtp_client_transaction_rcpt_new(trans, pool,
1089 rcpt_to, rcpt_params);
1090 rcpt->rcpt_callback = rcpt_callback;
1091 rcpt->context = context;
1092 rcpt->external_pool = TRUE;
1093
1094 smtp_client_transaction_submit(trans, FALSE);
1095
1096 return rcpt;
1097 }
1098
1099 static void
smtp_client_transaction_data_cb(const struct smtp_reply * reply,struct smtp_client_transaction * trans)1100 smtp_client_transaction_data_cb(const struct smtp_reply *reply,
1101 struct smtp_client_transaction *trans)
1102 {
1103 bool reply_per_rcpt = HAS_ALL_BITS(
1104 trans->flags, SMTP_CLIENT_TRANSACTION_FLAG_REPLY_PER_RCPT);
1105
1106 i_assert(!trans->reset);
1107
1108 smtp_client_transaction_ref(trans);
1109
1110 if (trans->data_input != NULL) {
1111 event_add_int(trans->event, "data_sent",
1112 trans->data_input->v_offset);
1113 i_stream_unref(&trans->data_input);
1114 }
1115
1116 if (reply_per_rcpt &&
1117 trans->cmd_data != NULL && /* NULL when failed early */
1118 trans->rcpts_data == NULL && trans->rcpts_count > 0) {
1119 smtp_client_command_set_replies(trans->cmd_data,
1120 trans->rcpts_count);
1121 }
1122 while (trans->rcpts_data != NULL) {
1123 struct smtp_client_transaction_rcpt *rcpt = trans->rcpts_data;
1124
1125 trans->rcpts_data = trans->rcpts_data->next;
1126 smtp_client_transaction_rcpt_finished(rcpt, reply);
1127 if (HAS_ALL_BITS(trans->flags,
1128 SMTP_CLIENT_TRANSACTION_FLAG_REPLY_PER_RCPT))
1129 break;
1130 }
1131
1132 if (reply_per_rcpt && trans->rcpts_count > 1 &&
1133 !smtp_reply_is_success(reply) && trans->data_failure == NULL)
1134 trans->data_failure = smtp_reply_clone(trans->pool, reply);
1135 if (trans->rcpts_data != NULL) {
1136 smtp_client_transaction_unref(&trans);
1137 return;
1138 }
1139
1140 trans->cmd_data = NULL;
1141
1142 if (trans->data_callback != NULL)
1143 trans->data_callback(reply, trans->data_context);
1144 trans->data_callback = NULL;
1145
1146 /* finished */
1147 smtp_client_transaction_finish(
1148 trans, (trans->data_failure == NULL ? reply :
1149 trans->data_failure));
1150
1151 smtp_client_transaction_unref(&trans);
1152 }
1153
1154 static void
smtp_client_transaction_send_data(struct smtp_client_transaction * trans)1155 smtp_client_transaction_send_data(struct smtp_client_transaction *trans)
1156 {
1157 struct smtp_reply failure;
1158
1159 i_assert(!trans->reset);
1160 i_assert(trans->data_input != NULL);
1161
1162 e_debug(trans->event, "Sending data");
1163
1164 timeout_remove(&trans->to_send);
1165
1166 i_zero(&failure);
1167 if (trans->failure != NULL) {
1168 smtp_client_transaction_fail_reply(trans, trans->failure);
1169 failure = *trans->failure;
1170 i_assert(failure.status != 0);
1171 } else if ((trans->rcpts_count + trans->rcpts_queue_count) == 0) {
1172 e_debug(trans->event, "No valid recipients");
1173 if (trans->failure != NULL)
1174 failure = *trans->failure;
1175 else {
1176 smtp_reply_init(&failure, 554, "No valid recipients");
1177 failure.enhanced_code = SMTP_REPLY_ENH_CODE(5, 5, 0);
1178 }
1179 i_assert(failure.status != 0);
1180 } else {
1181 i_assert(trans->conn != NULL);
1182
1183 trans->cmd_data = smtp_client_command_data_submit_after(
1184 trans->conn, 0, trans->cmd_last, trans->data_input,
1185 smtp_client_transaction_data_cb, trans);
1186 trans->submitted_data = TRUE;
1187
1188 if (trans->cmd_last != NULL)
1189 smtp_client_command_unlock(trans->cmd_last);
1190
1191 smtp_client_transaction_try_complete(trans);
1192 }
1193
1194 if (trans->cmd_plug != NULL)
1195 smtp_client_command_abort(&trans->cmd_plug);
1196 trans->cmd_last = NULL;
1197
1198 if (failure.status != 0)
1199 smtp_client_transaction_finish(trans, &failure);
1200 }
1201
1202 #undef smtp_client_transaction_send
smtp_client_transaction_send(struct smtp_client_transaction * trans,struct istream * data_input,smtp_client_command_callback_t * data_callback,void * data_context)1203 void smtp_client_transaction_send(
1204 struct smtp_client_transaction *trans, struct istream *data_input,
1205 smtp_client_command_callback_t *data_callback, void *data_context)
1206 {
1207 i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
1208 i_assert(!trans->data_provided);
1209 i_assert(!trans->reset);
1210
1211 if (trans->rcpts_queue_count == 0)
1212 e_debug(trans->event, "Got all RCPT replies");
1213
1214 e_debug(trans->event, "Send");
1215
1216 trans->data_provided = TRUE;
1217
1218 i_assert(trans->data_input == NULL);
1219 trans->data_input = i_stream_create_crlf(data_input);
1220
1221 trans->data_callback = data_callback;
1222 trans->data_context = data_context;
1223
1224 if (trans->finish_timeout_msecs > 0) {
1225 i_assert(trans->to_finish == NULL);
1226 trans->to_finish = timeout_add(trans->finish_timeout_msecs,
1227 smtp_client_transaction_timeout, trans);
1228 }
1229
1230 smtp_client_transaction_submit(trans, TRUE);
1231 }
1232
1233 static void
smtp_client_transaction_rset_cb(const struct smtp_reply * reply,struct smtp_client_transaction * trans)1234 smtp_client_transaction_rset_cb(const struct smtp_reply *reply,
1235 struct smtp_client_transaction *trans)
1236 {
1237 smtp_client_transaction_ref(trans);
1238
1239 trans->cmd_rset = NULL;
1240
1241 if (trans->reset_callback != NULL)
1242 trans->reset_callback(reply, trans->reset_context);
1243 trans->reset_callback = NULL;
1244
1245 /* finished */
1246 smtp_client_transaction_finish(trans, reply);
1247
1248 smtp_client_transaction_unref(&trans);
1249 }
1250
1251 static void
smtp_client_transaction_send_reset(struct smtp_client_transaction * trans)1252 smtp_client_transaction_send_reset(struct smtp_client_transaction *trans)
1253 {
1254 struct smtp_reply failure;
1255
1256 i_assert(trans->reset);
1257
1258 e_debug(trans->event, "Sending reset");
1259
1260 timeout_remove(&trans->to_send);
1261
1262 i_zero(&failure);
1263 if (trans->failure != NULL) {
1264 smtp_client_transaction_fail_reply(trans, trans->failure);
1265 failure = *trans->failure;
1266 i_assert(failure.status != 0);
1267 } else {
1268 i_assert(trans->conn != NULL);
1269
1270 trans->cmd_rset = smtp_client_command_rset_submit_after(
1271 trans->conn, 0, trans->cmd_last,
1272 smtp_client_transaction_rset_cb, trans);
1273
1274 if (trans->cmd_last != NULL)
1275 smtp_client_command_unlock(trans->cmd_last);
1276
1277 smtp_client_transaction_try_complete(trans);
1278 }
1279
1280 if (trans->cmd_plug != NULL)
1281 smtp_client_command_abort(&trans->cmd_plug);
1282 trans->cmd_last = NULL;
1283
1284 if (failure.status != 0)
1285 smtp_client_transaction_finish(trans, &failure);
1286 }
1287
1288 #undef smtp_client_transaction_reset
smtp_client_transaction_reset(struct smtp_client_transaction * trans,smtp_client_command_callback_t * reset_callback,void * reset_context)1289 void smtp_client_transaction_reset(
1290 struct smtp_client_transaction *trans,
1291 smtp_client_command_callback_t *reset_callback, void *reset_context)
1292 {
1293 i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
1294 i_assert(!trans->data_provided);
1295 i_assert(!trans->reset);
1296
1297 e_debug(trans->event, "Reset");
1298
1299 trans->reset = TRUE;
1300
1301 trans->reset_callback = reset_callback;
1302 trans->reset_context = reset_context;
1303
1304 if (trans->finish_timeout_msecs > 0) {
1305 i_assert(trans->to_finish == NULL);
1306 trans->to_finish = timeout_add(trans->finish_timeout_msecs,
1307 smtp_client_transaction_timeout, trans);
1308 }
1309
1310 smtp_client_transaction_submit(trans, TRUE);
1311 }
1312
1313 static void
smtp_client_transaction_do_submit_more(struct smtp_client_transaction * trans)1314 smtp_client_transaction_do_submit_more(struct smtp_client_transaction *trans)
1315 {
1316 timeout_remove(&trans->to_send);
1317
1318 if (trans->immediate)
1319 trans->cmd_last = NULL;
1320
1321 /* Check whether we already failed */
1322 if (trans->failure == NULL &&
1323 trans->state > SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
1324 trans->failure = trans->mail_failure;
1325 if (trans->failure != NULL) {
1326 smtp_client_transaction_fail_reply(trans, trans->failure);
1327 return;
1328 }
1329
1330 i_assert(trans->conn != NULL);
1331
1332 /* Make sure transaction is started */
1333 if (trans->state == SMTP_CLIENT_TRANSACTION_STATE_NEW) {
1334 enum smtp_client_transaction_state state;
1335 struct smtp_client_transaction *tmp_trans = trans;
1336
1337 smtp_client_transaction_ref(tmp_trans);
1338 smtp_client_transaction_start(tmp_trans, NULL, NULL);
1339 state = trans->state;
1340 smtp_client_transaction_unref(&tmp_trans);
1341 if (state >= SMTP_CLIENT_TRANSACTION_STATE_FINISHED)
1342 return;
1343 }
1344
1345 if (trans->state <= SMTP_CLIENT_TRANSACTION_STATE_PENDING)
1346 return;
1347
1348 /* MAIL */
1349 if (trans->mail_send != NULL) {
1350 e_debug(trans->event, "Sending MAIL command");
1351
1352 i_assert(trans->state == SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM);
1353
1354 if (trans->cmd_last != NULL)
1355 smtp_client_command_unlock(trans->cmd_last);
1356
1357 while (trans->mail_send != NULL) {
1358 struct smtp_client_transaction_mail *mail =
1359 trans->mail_send;
1360
1361 trans->mail_send = trans->mail_send->next;
1362 mail->cmd_mail_from = trans->cmd_last =
1363 smtp_client_command_mail_submit(trans->conn, 0,
1364 mail->mail_from, &mail->mail_params,
1365 smtp_client_transaction_mail_cb, trans);
1366 }
1367
1368 if (!trans->immediate)
1369 smtp_client_command_lock(trans->cmd_last);
1370 }
1371
1372 /* RCPT */
1373 if (trans->rcpts_send != NULL) {
1374 e_debug(trans->event, "Sending recipients");
1375
1376 if (trans->cmd_last != NULL)
1377 smtp_client_command_unlock(trans->cmd_last);
1378
1379 while (trans->rcpts_send != NULL) {
1380 struct smtp_client_transaction_rcpt *rcpt =
1381 trans->rcpts_send;
1382
1383 trans->rcpts_send = trans->rcpts_send->next;
1384 rcpt->cmd_rcpt_to = trans->cmd_last =
1385 smtp_client_command_rcpt_submit_after(
1386 trans->conn, 0, trans->cmd_last,
1387 rcpt->rcpt_to, &rcpt->rcpt_params,
1388 smtp_client_transaction_rcpt_cb, rcpt);
1389 }
1390 if (!trans->immediate)
1391 smtp_client_command_lock(trans->cmd_last);
1392 }
1393
1394 if (trans->cmd_plug != NULL &&
1395 (trans->immediate || trans->cmd_last != trans->cmd_plug))
1396 smtp_client_command_abort(&trans->cmd_plug);
1397
1398 /* DATA / RSET */
1399 if (trans->reset) {
1400 smtp_client_transaction_send_reset(trans);
1401 } else if (trans->data_input != NULL) {
1402 smtp_client_transaction_send_data(trans);
1403 }
1404 }
1405
1406 static void
smtp_client_transaction_submit_more(struct smtp_client_transaction * trans)1407 smtp_client_transaction_submit_more(struct smtp_client_transaction *trans)
1408 {
1409 smtp_client_transaction_ref(trans);
1410 trans->submitting = TRUE;
1411 smtp_client_transaction_do_submit_more(trans);
1412 trans->submitting = FALSE;
1413 smtp_client_transaction_unref(&trans);
1414 }
1415
1416 static void
smtp_client_transaction_submit(struct smtp_client_transaction * trans,bool start)1417 smtp_client_transaction_submit(struct smtp_client_transaction *trans,
1418 bool start)
1419 {
1420 if (trans->failure == NULL && !start &&
1421 trans->state <= SMTP_CLIENT_TRANSACTION_STATE_PENDING) {
1422 /* Cannot submit commands at this time */
1423 return;
1424 }
1425
1426 if (trans->immediate) {
1427 /* Submit immediately if not failed already: avoid calling
1428 failure callbacks directly (which is the first thing
1429 smtp_client_transaction_submit_more() would do). */
1430 if (trans->failure == NULL &&
1431 trans->state > SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
1432 trans->failure = trans->mail_failure;
1433 if (trans->failure == NULL) {
1434 smtp_client_transaction_submit_more(trans);
1435 return;
1436 }
1437 }
1438
1439 if (trans->to_send != NULL) {
1440 /* Already scheduled command submission */
1441 return;
1442 }
1443
1444 trans->to_send = timeout_add_short(0,
1445 smtp_client_transaction_submit_more, trans);
1446 }
1447
1448 static void
smtp_client_transaction_try_complete(struct smtp_client_transaction * trans)1449 smtp_client_transaction_try_complete(struct smtp_client_transaction *trans)
1450 {
1451 i_assert(trans->conn != NULL);
1452
1453 if (trans->rcpts_queue_count > 0) {
1454 /* Not all RCPT replies have come in yet */
1455 e_debug(trans->event, "RCPT replies are still pending (%u/%u)",
1456 trans->rcpts_queue_count,
1457 (trans->rcpts_queue_count + trans->rcpts_count));
1458 return;
1459 }
1460 if (!trans->data_provided && !trans->reset) {
1461 /* Still waiting for application to issue either
1462 smtp_client_transaction_send() or
1463 smtp_client_transaction_reset() */
1464 e_debug(trans->event, "Transaction is not yet complete");
1465 return;
1466 }
1467
1468 if (trans->state == SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO) {
1469 /* Completed at this instance */
1470 e_debug(trans->event,
1471 "Got all RCPT replies and transaction is complete");
1472 }
1473
1474 if (trans->reset) {
1475 /* Entering reset state */
1476 trans->state = SMTP_CLIENT_TRANSACTION_STATE_RESET;
1477
1478 if (trans->cmd_rset == NULL)
1479 return;
1480 } else {
1481 /* Entering data state */
1482 trans->state = SMTP_CLIENT_TRANSACTION_STATE_DATA;
1483
1484 if (trans->rcpts_count == 0) {
1485 /* abort transaction if all recipients failed */
1486 smtp_client_transaction_abort(trans);
1487 return;
1488 }
1489
1490 if (trans->cmd_data == NULL)
1491 return;
1492
1493 if (HAS_ALL_BITS(trans->flags,
1494 SMTP_CLIENT_TRANSACTION_FLAG_REPLY_PER_RCPT)) {
1495 smtp_client_command_set_replies(trans->cmd_data,
1496 trans->rcpts_count);
1497 }
1498 }
1499
1500 /* Got replies for all recipients and submitted our last command;
1501 the next transaction can submit its commands now. */
1502 smtp_client_connection_next_transaction(trans->conn, trans);
1503 }
1504
smtp_client_transaction_set_immediate(struct smtp_client_transaction * trans,bool immediate)1505 void smtp_client_transaction_set_immediate(
1506 struct smtp_client_transaction *trans, bool immediate)
1507 {
1508 trans->immediate = immediate;
1509 }
1510
smtp_client_transaction_connection_result(struct smtp_client_transaction * trans,const struct smtp_reply * reply)1511 void smtp_client_transaction_connection_result(
1512 struct smtp_client_transaction *trans,
1513 const struct smtp_reply *reply)
1514 {
1515 if (!smtp_reply_is_success(reply)) {
1516 if (trans->state <= SMTP_CLIENT_TRANSACTION_STATE_PENDING) {
1517 e_debug(trans->event, "Failed to connect: %s",
1518 smtp_reply_log(reply));
1519 } else {
1520 e_debug(trans->event, "Connection lost: %s",
1521 smtp_reply_log(reply));
1522 }
1523 smtp_client_transaction_fail_reply(trans, reply);
1524 return;
1525 }
1526
1527 smtp_client_transaction_connection_ready(trans);
1528 }
1529
smtp_client_transaction_connection_destroyed(struct smtp_client_transaction * trans)1530 void smtp_client_transaction_connection_destroyed(
1531 struct smtp_client_transaction *trans)
1532 {
1533 i_assert(trans->failure != NULL);
1534 smtp_client_connection_unref(&trans->conn);
1535 }
1536
1537 const struct smtp_client_transaction_times *
smtp_client_transaction_get_times(struct smtp_client_transaction * trans)1538 smtp_client_transaction_get_times(struct smtp_client_transaction *trans)
1539 {
1540 return &trans->times;
1541 }
1542
1543 enum smtp_client_transaction_state
smtp_client_transaction_get_state(struct smtp_client_transaction * trans)1544 smtp_client_transaction_get_state(struct smtp_client_transaction *trans)
1545 {
1546 return trans->state;
1547 }
1548
1549 const char *
smtp_client_transaction_get_state_name(struct smtp_client_transaction * trans)1550 smtp_client_transaction_get_state_name(struct smtp_client_transaction *trans)
1551 {
1552 i_assert(trans->state >= SMTP_CLIENT_TRANSACTION_STATE_NEW &&
1553 trans->state <= SMTP_CLIENT_TRANSACTION_STATE_ABORTED);
1554 return smtp_client_transaction_state_names[trans->state];
1555 }
1556
1557 const char *
smtp_client_transaction_get_state_destription(struct smtp_client_transaction * trans)1558 smtp_client_transaction_get_state_destription(
1559 struct smtp_client_transaction *trans)
1560 {
1561 enum smtp_client_connection_state conn_state;
1562
1563 switch (trans->state) {
1564 case SMTP_CLIENT_TRANSACTION_STATE_NEW:
1565 break;
1566 case SMTP_CLIENT_TRANSACTION_STATE_PENDING:
1567 i_assert(trans->conn != NULL);
1568 conn_state = smtp_client_connection_get_state(trans->conn);
1569 switch (conn_state) {
1570 case SMTP_CLIENT_CONNECTION_STATE_CONNECTING:
1571 case SMTP_CLIENT_CONNECTION_STATE_HANDSHAKING:
1572 case SMTP_CLIENT_CONNECTION_STATE_AUTHENTICATING:
1573 return smtp_client_connection_state_names[conn_state];
1574 case SMTP_CLIENT_CONNECTION_STATE_TRANSACTION:
1575 return "waiting for connection";
1576 case SMTP_CLIENT_CONNECTION_STATE_DISCONNECTED:
1577 case SMTP_CLIENT_CONNECTION_STATE_READY:
1578 default:
1579 break;
1580 }
1581 break;
1582 case SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM:
1583 return "waiting for reply to MAIL FROM";
1584 case SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO:
1585 return "waiting for reply to RCPT TO";
1586 case SMTP_CLIENT_TRANSACTION_STATE_DATA:
1587 return "waiting for reply to DATA";
1588 case SMTP_CLIENT_TRANSACTION_STATE_RESET:
1589 return "waiting for reply to RESET";
1590 case SMTP_CLIENT_TRANSACTION_STATE_FINISHED:
1591 return "finished";
1592 case SMTP_CLIENT_TRANSACTION_STATE_ABORTED:
1593 return "aborted";
1594 }
1595 i_unreached();
1596 }
1597
smtp_client_transaction_switch_ioloop(struct smtp_client_transaction * trans)1598 void smtp_client_transaction_switch_ioloop(
1599 struct smtp_client_transaction *trans)
1600 {
1601 if (trans->to_send != NULL)
1602 trans->to_send = io_loop_move_timeout(&trans->to_send);
1603 if (trans->to_finish != NULL)
1604 trans->to_finish = io_loop_move_timeout(&trans->to_finish);
1605 }
1606