masqmail

view src/deliver.c @ 319:d41fb3b9ed3e

refactoring in the small
author meillo@marmaro.de
date Thu, 28 Apr 2011 16:42:28 +0200
parents c98aa884d2cb
children 412385b57dc4
line source
1 /* MasqMail
2 Copyright (C) 1999-2002 Oliver Kurth
3 Copyright (C) 2008, 2010 markus schnalke <meillo@marmaro.de>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
20 #include <fnmatch.h>
21 #include <sysexits.h>
22 #include <netdb.h>
24 #include "masqmail.h"
25 #include "smtp_out.h"
27 /* collect failed/defered rcpts for failure/warning messages */
28 /* returns TRUE if either there are no failures or a failure message has been successfully sent */
29 gboolean
30 delivery_failures(message * msg, GList * rcpt_list, gchar * err_fmt, ...)
31 {
32 gboolean ok_fail = TRUE, ok_warn = TRUE;
33 time_t now = time(NULL);
35 GList *failed_list = NULL, *defered_list = NULL, *rcpt_node;
36 va_list args;
37 va_start(args, err_fmt);
39 foreach(rcpt_list, rcpt_node) {
40 address *rcpt = (address *) (rcpt_node->data);
42 if (addr_is_defered(rcpt)) {
43 if ((now - msg->received_time) >= conf.max_defer_time) {
44 addr_mark_failed(rcpt);
45 } else {
46 defered_list = g_list_prepend(defered_list, rcpt);
47 }
48 }
49 if (addr_is_failed(rcpt)) {
50 failed_list = g_list_prepend(failed_list, rcpt);
51 }
52 }
53 if (failed_list) {
54 ok_fail = fail_msg(msg, conf.errmsg_file, failed_list, err_fmt, args);
55 g_list_free(failed_list);
56 }
57 if (defered_list) {
58 ok_warn = warn_msg(msg, conf.warnmsg_file, defered_list, err_fmt, args);
59 g_list_free(defered_list);
60 }
61 va_end(args);
62 return ok_fail && ok_warn;
63 }
65 static gint
66 _g_list_strcasecmp(gconstpointer a, gconstpointer b)
67 {
68 return (gint) strcasecmp(a, b);
69 }
71 gboolean
72 deliver_local_mbox(message* msg, GList* hdr_list, address* rcpt, address* env_addr)
73 {
74 DEBUG(1) debugf("attempting to deliver %s with mbox\n", msg->uid);
75 if (append_file(msg, hdr_list, rcpt->local_part)) {
76 if (env_addr != rcpt) {
77 logwrite(LOG_NOTICE, "%s => %s@%s <%s@%s> with mbox\n", msg->uid, rcpt->local_part, rcpt->domain, env_addr->local_part, env_addr->domain);
78 } else {
79 logwrite(LOG_NOTICE, "%s => <%s@%s> with mbox\n", msg->uid, rcpt->local_part, rcpt->domain);
80 }
81 addr_mark_delivered(rcpt);
82 return TRUE;
83 }
85 /* prevents 'Resource temporarily unavailable (11)' */
86 if (errno != EAGAIN) {
87 addr_mark_failed(rcpt);
88 } else {
89 addr_mark_defered(rcpt);
90 }
91 return FALSE;
92 }
94 gboolean
95 deliver_local_pipe(message* msg, GList* hdr_list, address* rcpt, address* env_addr)
96 {
97 guint flags;
99 DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
101 flags = (conf.pipe_fromline) ? MSGSTR_FROMLINE : 0;
102 flags |= (conf.pipe_fromhack) ? MSGSTR_FROMHACK : 0;
103 if (pipe_out(msg, hdr_list, rcpt, &(rcpt->local_part[1]), flags)) {
104 logwrite(LOG_NOTICE, "%s => %s <%s@%s> with pipe\n",
105 msg->uid, rcpt->local_part, env_addr->local_part, env_addr->domain);
106 addr_mark_delivered(rcpt);
107 return TRUE;
108 }
110 if ((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)) {
111 addr_mark_failed(rcpt);
112 } else {
113 addr_mark_defered(rcpt);
114 /* has no effect yet, except that mail remains in spool */
115 }
116 return FALSE;
117 }
119 gboolean
120 deliver_local_mda(message* msg, GList* hdr_list, address* rcpt, address* env_addr)
121 {
122 gboolean ok = FALSE;
123 gchar *cmd = g_malloc(256);
124 GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
125 guint flags;
127 DEBUG(1) debugf("attempting to deliver %s with mda\n", msg->uid);
129 if (!expand(var_table, conf.mda, cmd, 256)) {
130 logwrite(LOG_ALERT, "could not expand string %s\n", conf.mda);
131 destroy_table(var_table);
132 return FALSE;
133 }
135 flags = (conf.mda_fromline) ? MSGSTR_FROMLINE : 0;
136 flags |= (conf.mda_fromhack) ? MSGSTR_FROMHACK : 0;
137 if (pipe_out(msg, hdr_list, rcpt, cmd, flags)) {
138 logwrite(LOG_NOTICE, "%s => %s@%s with mda (cmd = '%s')\n",
139 msg->uid, rcpt->local_part, rcpt->domain, cmd);
140 addr_mark_delivered(rcpt);
141 ok = TRUE;
142 } else if ((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)) {
143 addr_mark_failed(rcpt);
144 } else {
145 addr_mark_defered(rcpt);
146 /* has no effect yet, except that mail remains in spool */
147 }
149 destroy_table(var_table);
150 return ok;
151 }
153 gboolean
154 deliver_local(msg_out * msgout)
155 {
156 message *msg = msgout->msg;
157 GList *rcpt_list = msgout->rcpt_list;
158 GList *rcpt_node;
159 gboolean ok = FALSE, flag = FALSE, ok_fail = FALSE;
161 DEBUG(5) debugf("deliver_local entered\n");
163 flag = (msg->data_list == NULL);
164 if (flag && !spool_read_data(msg)) {
165 logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid);
166 return FALSE;
167 }
169 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
170 GList *hdr_list;
171 address *rcpt = (address *) (rcpt_node->data);
172 address *env_addr = addr_find_ancestor(rcpt);
173 address *ret_path = msg->return_path;
174 header *retpath_hdr, *envto_hdr;
176 /* we need a private copy of the hdr list because we add headers
177 here that belong to the rcpt only. g_list_copy copies only
178 the nodes, so it is safe to g_list_free it */
179 hdr_list = g_list_copy(msg->hdr_list);
180 retpath_hdr = create_header(HEAD_ENVELOPE_TO, "Envelope-to: %s\n", addr_string(env_addr));
181 envto_hdr = create_header(HEAD_RETURN_PATH, "Return-path: %s\n", addr_string(ret_path));
183 hdr_list = g_list_prepend(hdr_list, envto_hdr);
184 hdr_list = g_list_prepend(hdr_list, retpath_hdr);
186 if (rcpt->local_part[0] == '|') {
187 /* probably for expanded aliases, but why not done
188 like with the mda? //meillo 2010-12-06 */
189 if (deliver_local_pipe(msg, hdr_list, rcpt, env_addr)) {
190 ok = TRUE;
191 }
192 } else {
193 /* figure out which mailbox type should be used for this user */
194 gchar *user = rcpt->local_part;
195 gchar *mbox_type = conf.mbox_default;
197 if (g_list_find_custom (conf.mbox_users, user, _g_list_strcasecmp)) {
198 mbox_type = "mbox";
199 } else if (g_list_find_custom (conf.mda_users, user, _g_list_strcasecmp)) {
200 mbox_type = "mda";
201 }
203 if (strcmp(mbox_type, "mbox") == 0) {
204 if (deliver_local_mbox(msg, hdr_list, rcpt, env_addr)) {
205 ok = TRUE;
206 }
207 } else if (strcmp(mbox_type, "mda") == 0) {
208 if (conf.mda) {
209 if (deliver_local_mda(msg, hdr_list, rcpt, env_addr)) {
210 ok = TRUE;
211 }
212 } else {
213 logwrite(LOG_ALERT, "mbox type is mda, but no mda command given in configuration\n");
214 }
216 } else {
217 logwrite(LOG_ALERT, "unknown mbox type '%s'\n", mbox_type);
218 }
219 }
221 destroy_header(retpath_hdr);
222 destroy_header(envto_hdr);
224 g_list_free(hdr_list);
225 }
226 ok_fail = delivery_failures(msg, rcpt_list, "%s (%d)", ext_strerror(errno), errno);
228 if (flag) {
229 msg_free_data(msg);
230 }
231 if (ok || ok_fail) {
232 deliver_finish(msgout);
233 }
235 return ok;
236 }
238 /* make a list of rcpt's of a message that are local
239 return a new copy of the list */
240 void
241 msg_rcptlist_local(GList * rcpt_list, GList ** p_local_list, GList ** p_nonlocal_list)
242 {
243 GList *rcpt_node;
245 foreach(rcpt_list, rcpt_node) {
246 address *rcpt = (address *) (rcpt_node->data);
247 GList *dom_node;
249 DEBUG(5) debugf("checking address %s\n", rcpt->address);
251 /* search for local host list: */
252 foreach(conf.local_hosts, dom_node) {
253 if (strcasecmp(dom_node->data, rcpt->domain) == 0) {
254 *p_local_list = g_list_append(*p_local_list, rcpt);
255 DEBUG(5) debugf("<%s@%s> is local\n", rcpt->local_part, rcpt->domain);
256 break;
257 } else {
258 *p_nonlocal_list = g_list_append(*p_nonlocal_list, rcpt);
259 }
260 }
261 }
262 }
264 gboolean
265 deliver_msglist_host_pipe(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
266 {
267 gboolean ok = TRUE;
268 GList *msgout_node;
270 DEBUG(5) debugf("deliver_msglist_host_pipe entered\n");
272 foreach(msgout_list, msgout_node) {
273 msg_out *msgout = (msg_out *) (msgout_node->data);
274 gboolean flag, ok_fail = FALSE;
275 message *msg = msgout->msg;
276 GList *rcpt_node, *rcpt_list = msgout->rcpt_list;
278 DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
280 flag = (msg->data_list == NULL);
281 if (flag && !spool_read_data(msg)) {
282 logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid);
283 continue;
284 }
286 ok = FALSE;
287 foreach(rcpt_list, rcpt_node) {
288 address *rcpt = (address *) (rcpt_node->data);
289 gchar *cmd = g_malloc(256);
290 GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
292 DEBUG(1) debugf("attempting to deliver %s to %s@%s with pipe\n", msg->uid, rcpt->local_part, rcpt->domain);
294 if (!expand(var_table, route->pipe, cmd, 256)) {
295 logwrite(LOG_ALERT, "could not expand string %s\n", route->pipe);
296 } else {
298 if (pipe_out(msg, msg->hdr_list, rcpt, cmd, (route->pipe_fromline ? MSGSTR_FROMLINE : 0)
299 | (route->pipe_fromhack ? MSGSTR_FROMHACK : 0))) {
300 logwrite(LOG_NOTICE, "%s => %s@%s with pipe (cmd = '%s')\n",
301 msg->uid, rcpt->local_part, rcpt->domain, cmd);
302 addr_mark_delivered(rcpt);
303 ok = TRUE;
304 } else {
305 logwrite(LOG_ALERT, "pipe_out '%s' failed\n", route->pipe);
307 if (route->connect_error_fail) {
308 addr_mark_failed(rcpt);
309 } else {
310 addr_mark_defered(rcpt);
311 }
312 }
313 }
315 destroy_table(var_table);
316 }
317 ok_fail = delivery_failures(msg, rcpt_list, "%s", strerror(errno));
319 if (flag) {
320 msg_free_data(msg);
321 }
322 if (ok || ok_fail) {
323 deliver_finish(msgout);
324 }
325 }
327 return ok;
328 }
330 /* deliver list of messages to one host and finishes them if the message was
331 delivered to at least one rcpt.
332 Returns TRUE if at least one msg was delivered to at least one rcpt.
333 */
334 gboolean
335 deliver_msglist_host_smtp(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
336 {
337 gboolean ok = FALSE;
338 GList *msgout_node;
339 smtp_base *psb;
340 gint port = 25;
342 /* paranoid check: */
343 if (!msgout_list) {
344 logwrite(LOG_ALERT, "Ooops: empty list of messages in deliver_msglist_host()\n");
345 return FALSE;
346 }
348 if (!host) {
349 /* XXX: what if mail_host isn't set? Is this possible? */
350 host = route->mail_host->address;
351 port = route->mail_host->port;
352 }
354 if (route->wrapper) {
355 psb = smtp_out_open_child(route->wrapper);
356 } else {
357 psb = smtp_out_open(host, port, res_list);
358 }
360 if (!psb) {
361 /* smtp_out_open() failed */
362 foreach(msgout_list, msgout_node) {
363 msg_out *msgout = (msg_out *) (msgout_node->data);
364 GList *rcpt_node;
366 for (rcpt_node = g_list_first(msgout->rcpt_list);
367 rcpt_node;
368 rcpt_node = g_list_next(rcpt_node)) {
369 address *rcpt = (address *) (rcpt_node->data);
370 gboolean ret = FALSE;
372 addr_unmark_delivered(rcpt);
373 if (route->connect_error_fail) {
374 addr_mark_failed(rcpt);
375 } else {
376 addr_mark_defered(rcpt);
377 }
378 if (route->wrapper) {
379 ret = delivery_failures(msgout->msg, msgout->rcpt_list, "could not open wrapper:\n\t%s", strerror(errno));
380 } else {
381 ret = delivery_failures(msgout->msg, msgout->rcpt_list, "could not open connection to %s:%d :\n\t%s", host, port, h_errno != 0 ? hstrerror(h_errno) : strerror(errno));
382 }
383 if (ret) {
384 deliver_finish(msgout);
385 }
386 }
387 }
388 return ok;
389 }
392 if (route->wrapper) {
393 /* it seems as if the remote_host is only set for logging
394 /* XXX: this could probably be moved into smtp_out_open_child() */
395 psb->remote_host = host;
396 }
398 set_heloname(psb, route->helo_name ? route->helo_name : conf.host_name, route->do_correct_helo);
400 #ifdef ENABLE_AUTH
401 if ((route->auth_name) && (route->auth_login) && (route->auth_secret)) {
402 set_auth(psb, route->auth_name, route->auth_login, route->auth_secret);
403 }
404 #endif
405 if (!smtp_out_init(psb, route->instant_helo)) {
406 /* smtp_out_init() failed */
407 if ((psb->error==smtp_fail) || (psb->error==smtp_trylater) || (psb->error==smtp_syntax)) {
408 smtp_out_quit(psb);
410 foreach(msgout_list, msgout_node) {
411 msg_out *msgout = (msg_out *) (msgout_node->data);
412 smtp_out_mark_rcpts(psb, msgout->rcpt_list);
414 if (delivery_failures(msgout->msg, msgout->rcpt_list, "while connected with %s, the server replied\n\t%s", host, psb->buffer)) {
415 deliver_finish(msgout);
416 }
417 }
418 }
419 destroy_smtpbase(psb);
420 return ok;
421 }
423 if (!route->do_pipelining) {
424 psb->use_pipelining = FALSE;
425 }
427 foreach(msgout_list, msgout_node) {
428 msg_out *msgout = (msg_out *) (msgout_node->data);
429 gboolean flag, ok_msg = FALSE, ok_fail = FALSE;
430 message *msg = msgout->msg;
432 /* we may have to read the data at this point and remember if we did */
433 flag = (msg->data_list == NULL);
434 if (flag && !spool_read_data(msg)) {
435 logwrite(LOG_ALERT, "could not open data spool file %s\n", msg->uid);
436 break;
437 }
439 smtp_out_msg(psb, msg, msgout->return_path, msgout->rcpt_list, msgout->hdr_list);
441 ok_fail = delivery_failures(msg, msgout->rcpt_list, "while connected with %s, the server replied\n\t%s", host, psb->buffer);
443 if ((psb->error == smtp_eof) || (psb->error == smtp_timeout)) {
444 /* connection lost */
445 break;
446 } else if (psb->error != smtp_ok) {
447 if (g_list_next(msgout_node) && !smtp_out_rset(psb)) {
448 break;
449 }
450 }
451 ok_msg = (psb->error == smtp_ok);
453 if (flag) {
454 msg_free_data(msg);
455 }
456 if (ok_msg) {
457 ok = TRUE;
458 }
459 if (ok_msg || ok_fail) {
460 deliver_finish(msgout);
461 }
462 }
463 if (psb->error == smtp_ok || (psb->error == smtp_fail)
464 || (psb->error == smtp_trylater) || (psb->error == smtp_syntax)) {
465 smtp_out_quit(psb);
466 }
467 destroy_smtpbase(psb);
468 return ok;
469 }
471 gboolean
472 deliver_msglist_host(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
473 {
475 if (route->pipe) {
476 DEBUG(5) debugf("with pipe\n");
477 return deliver_msglist_host_pipe(route, msgout_list, host, res_list);
478 } else {
479 DEBUG(5) debugf("with smtp\n");
480 return deliver_msglist_host_smtp(route, msgout_list, host, res_list);
481 }
482 }
484 /*
485 delivers messages in msgout_list using route
486 */
487 gboolean
488 deliver_route_msgout_list(connect_route * route, GList * msgout_list)
489 {
490 gboolean ok = FALSE;
491 GList *mo_ph_list;
492 GList *mo_ph_node;
494 DEBUG(5) debugf("deliver_route_msgout_list entered, route->name = %s\n", route->name);
496 if (route->mail_host) {
497 /* this is easy... deliver everything to a smart host for relay */
498 return deliver_msglist_host(route, msgout_list, NULL, route->resolve_list);
499 }
501 /* this is not easy... */
503 mo_ph_list = route_msgout_list(route, msgout_list);
504 /* okay, now we have ordered our messages by the hosts. */
505 if (!mo_ph_list) {
506 return FALSE;
507 }
509 /* TODO: It would be nice to be able to fork for each host.
510 We cannot do that yet because of complications with finishing the
511 messages. Threads could be a solution because they use the same
512 memory. But we are not thread safe yet...
513 */
514 foreach(mo_ph_list, mo_ph_node) {
515 msgout_perhost *mo_ph = (msgout_perhost *) (mo_ph_node->data);
516 if (deliver_msglist_host(route, mo_ph->msgout_list, mo_ph->host, route->resolve_list)) {
517 ok = TRUE;
518 }
519 destroy_msgout_perhost(mo_ph);
520 }
521 g_list_free(mo_ph_list);
522 return ok;
523 }
525 /*
526 calls route_prepare_msg()
527 delivers messages in msg_list using route by calling deliver_route_msgout_list()
528 */
529 gboolean
530 deliver_route_msg_list(connect_route * route, GList * msgout_list)
531 {
532 GList *msgout_list_deliver = NULL;
533 GList *msgout_node;
534 gboolean ok = TRUE;
536 DEBUG(6) debugf("deliver_route_msg_list()\n");
538 foreach(msgout_list, msgout_node) {
539 msg_out *msgout = (msg_out *) (msgout_node->data);
540 msg_out *msgout_cloned = clone_msg_out(msgout);
541 GList *rcpt_list_non_delivered = NULL;
542 GList *rcpt_node;
544 /* we have to delete already delivered rcpt's because a
545 previous route may have delivered to it */
546 foreach(msgout_cloned->rcpt_list, rcpt_node) {
547 address *rcpt = (address *) (rcpt_node->data);
548 /* failed addresses already have been bounced;
549 there should be a better way to handle those. */
550 if (!addr_is_delivered(rcpt) && !addr_is_failed(rcpt)
551 && !(rcpt->flags & ADDR_FLAG_LAST_ROUTE)) {
552 rcpt_list_non_delivered = g_list_append(rcpt_list_non_delivered, rcpt);
553 }
554 }
555 g_list_free(msgout_cloned->rcpt_list);
556 msgout_cloned->rcpt_list = rcpt_list_non_delivered;
558 if (!msgout_cloned->rcpt_list) {
559 destroy_msg_out(msgout_cloned);
560 continue;
561 }
563 /* filter by allowed envelope sender */
564 if (!route_sender_is_allowed(route, msgout->msg->return_path)) {
565 destroy_msg_out(msgout_cloned);
566 continue;
567 }
569 /* filter by allowed envelope rcpts */
570 GList* rcpt_list_allowed = NULL;
571 GList* rcpt_list_notallowed = NULL;
572 route_split_rcpts(route, msgout_cloned->rcpt_list, &rcpt_list_allowed, &rcpt_list_notallowed);
573 if (!rcpt_list_allowed) {
574 destroy_msg_out(msgout_cloned);
575 continue;
576 }
578 logwrite(LOG_NOTICE, "%s using '%s'\n", msgout->msg->uid, route->name);
580 g_list_free(msgout_cloned->rcpt_list);
581 msgout_cloned->rcpt_list = rcpt_list_allowed;
583 if (route->last_route) {
584 GList *rcpt_node;
585 foreach(msgout_cloned->rcpt_list, rcpt_node) {
586 address *rcpt = (address *) (rcpt_node->data);
587 rcpt->flags |= ADDR_FLAG_LAST_ROUTE;
588 }
589 }
591 route_prepare_msgout(route, msgout_cloned);
592 msgout_list_deliver = g_list_append(msgout_list_deliver, msgout_cloned);
593 }
595 if (msgout_list_deliver) {
596 if (deliver_route_msgout_list(route, msgout_list_deliver)) {
597 ok = TRUE;
598 }
599 destroy_msg_out_list(msgout_list_deliver);
600 }
601 return ok;
602 }
604 /* copy pointers of delivered addresses to the msg's non_rcpt_list,
605 to make sure that they will not be delivered again.
606 */
607 void
608 update_non_rcpt_list(msg_out * msgout)
609 {
610 GList *rcpt_node;
611 message *msg = msgout->msg;
613 foreach(msgout->rcpt_list, rcpt_node) {
614 address *rcpt = (address *) (rcpt_node->data);
615 if (addr_is_delivered(rcpt) || addr_is_failed(rcpt)) {
616 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, rcpt);
617 }
618 }
619 }
621 /* after delivery attempts, we check if there are any rcpt addresses left in
622 the message. If all addresses have been completed, the spool files will be
623 deleted, otherwise the header spool will be written back. We never changed
624 the data spool, so there is no need to write that back.
626 returns TRUE if all went well.
627 */
628 gboolean
629 deliver_finish(msg_out * msgout)
630 {
631 GList *rcpt_node;
632 message *msg = msgout->msg;
633 gboolean finished = TRUE;
635 update_non_rcpt_list(msgout);
637 /* we NEVER made copies of the addresses, flags affecting addresses
638 were always set on the original address structs */
639 foreach(msg->rcpt_list, rcpt_node) {
640 address *rcpt = (address *) (rcpt_node->data);
641 if (!addr_is_finished_children(rcpt)) {
642 finished = FALSE;
643 } else {
644 /* if ALL children have been delivered, mark parent as
645 delivered. if there is one or more not delivered,
646 it must have failed, we mark the parent as failed
647 as well.
648 */
649 if (addr_is_delivered_children(rcpt)) {
650 addr_mark_delivered(rcpt);
651 } else {
652 addr_mark_failed(rcpt);
653 }
654 }
655 }
657 if (finished) {
658 if (spool_delete_all(msg)) {
659 logwrite(LOG_NOTICE, "%s completed.\n", msg->uid);
660 return TRUE;
661 }
662 return FALSE;
663 }
665 /* one not delivered address was found */
666 if (!spool_write(msg, FALSE)) {
667 logwrite(LOG_ALERT, "could not write back spool header for %s\n", msg->uid);
668 return FALSE;
669 }
671 DEBUG(2) debugf("spool header for %s written back.\n", msg->uid);
672 return TRUE;
673 }
675 gboolean
676 deliver_finish_list(GList * msgout_list)
677 {
678 gboolean ok = TRUE;
679 GList *msgout_node;
680 foreach(msgout_list, msgout_node) {
681 msg_out *msgout = (msg_out *) (msgout_node->data);
682 if (!deliver_finish(msgout)) {
683 ok = FALSE;
684 }
685 }
686 return ok;
687 }
689 gboolean
690 deliver_msgout_list_online(GList * msgout_list)
691 {
692 GList *rf_list = NULL;
693 gchar *connect_name = NULL;
694 gboolean ok = FALSE;
696 connect_name = online_query();
697 if (!connect_name) {
698 return FALSE;
699 }
701 /* we are online! */
702 logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
704 rf_list = (GList *) table_find(conf.connect_routes, connect_name);
705 if (!rf_list) {
706 logwrite(LOG_ALERT, "route list with name '%s' not found.\n", connect_name);
707 return FALSE;
708 }
710 GList *route_list = read_route_list(rf_list, FALSE);
711 if (!route_list) {
712 logwrite(LOG_ALERT, "could not read route list '%s'\n", connect_name);
713 return FALSE;
714 }
716 GList *route_node;
717 foreach(route_list, route_node) {
718 connect_route *route = (connect_route *) (route_node->data);
719 /* TODO: ok gets overwritten */
720 ok = deliver_route_msg_list(route, msgout_list);
721 }
722 destroy_route_list(route_list);
723 return ok;
724 }
726 gboolean
727 deliver_msg_list(GList * msg_list, guint flags)
728 {
729 GList *msgout_list = create_msg_out_list(msg_list);
730 GList *local_msgout_list = NULL;
731 GList *localnet_msgout_list = NULL;
732 GList *other_msgout_list = NULL;
733 GList *msgout_node;
734 GList *alias_table = NULL;
735 gboolean ok = TRUE;
737 if (conf.alias_file) {
738 alias_table = table_read(conf.alias_file, ':');
739 }
741 /* sort messages for different deliveries */
742 foreach(msgout_list, msgout_node) {
743 msg_out *msgout = (msg_out *) (msgout_node->data);
744 GList *rcpt_list;
745 GList *local_rcpt_list = NULL;
746 GList *localnet_rcpt_list = NULL;
747 GList *other_rcpt_list = NULL;
749 if (!spool_lock(msgout->msg->uid)) {
750 DEBUG(5) debugf("spool_lock(%s) failed.\n", msgout->msg->uid);
751 continue;
752 }
753 DEBUG(5) debugf("spool_lock(%s)\n", msgout->msg->uid);
755 rcpt_list = g_list_copy(msgout->msg->rcpt_list);
756 if (conf.log_user) {
757 address *addr = create_address_qualified(conf.log_user, TRUE, conf.host_name);
758 if (addr) {
759 rcpt_list = g_list_prepend(rcpt_list, addr);
760 } else {
761 logwrite(LOG_ALERT, "invalid log_user address `%s', ignoring\n", conf.log_user);
762 }
763 }
764 if (alias_table) {
765 GList *aliased_rcpt_list;
766 aliased_rcpt_list = alias_expand(alias_table, rcpt_list, msgout->msg->non_rcpt_list);
767 g_list_free(rcpt_list);
768 rcpt_list = aliased_rcpt_list;
769 }
771 split_rcpts(rcpt_list, conf.local_nets, &local_rcpt_list, &localnet_rcpt_list, &other_rcpt_list);
772 g_list_free(rcpt_list);
774 /* local recipients */
775 if ((flags & DLVR_LOCAL) && local_rcpt_list) {
776 msg_out *local_msgout = clone_msg_out(msgout);
777 local_msgout->rcpt_list = local_rcpt_list;
778 local_msgout_list = g_list_append(local_msgout_list, local_msgout);
779 }
781 /* local net recipients */
782 if ((flags & DLVR_LAN) && localnet_rcpt_list) {
783 msg_out *localnet_msgout = clone_msg_out(msgout);
784 localnet_msgout->rcpt_list = localnet_rcpt_list;
785 localnet_msgout_list = g_list_append(localnet_msgout_list, localnet_msgout);
786 }
788 /* remote recipients (the rest), requires online delivery */
789 if ((flags & DLVR_ONLINE) && other_rcpt_list) {
790 msg_out *other_msgout = clone_msg_out(msgout);
791 other_msgout->rcpt_list = other_rcpt_list;
792 other_msgout_list = g_list_append(other_msgout_list, other_msgout);
793 }
794 }
796 if (alias_table) {
797 destroy_table(alias_table);
798 }
800 /* actual delivery */
802 if (local_msgout_list) {
803 DEBUG(5) debugf("local_msgout_list\n");
804 foreach(local_msgout_list, msgout_node) {
805 msg_out *msgout = (msg_out *) (msgout_node->data);
806 if (!deliver_local(msgout)) {
807 ok = FALSE;
808 }
809 }
810 destroy_msg_out_list(local_msgout_list);
811 }
813 if (localnet_msgout_list) {
814 GList *route_list = NULL;
815 GList *route_node;
817 DEBUG(5) debugf("localnet_msgout_list\n");
818 if (conf.local_net_routes) {
819 route_list = read_route_list(conf.local_net_routes, TRUE);
820 } else {
821 route_list = g_list_append(NULL, create_local_route());
822 }
824 foreach(route_list, route_node) {
825 connect_route *route = (connect_route *) (route_node->data);
826 if (!deliver_route_msg_list(route, localnet_msgout_list)) {
827 ok = FALSE;
828 }
829 }
830 destroy_msg_out_list(localnet_msgout_list);
831 destroy_route_list(route_list);
832 }
834 if (other_msgout_list) {
835 DEBUG(5) debugf("other_msgout_list\n");
836 if (!deliver_msgout_list_online(other_msgout_list)) {
837 ok = FALSE;
838 }
839 destroy_msg_out_list(other_msgout_list);
840 }
842 foreach(msgout_list, msgout_node) {
843 msg_out *msgout = (msg_out *) (msgout_node->data);
844 if (spool_unlock(msgout->msg->uid)) {
845 DEBUG(5) debugf("spool_unlock(%s)\n", msgout->msg->uid);
846 } else {
847 DEBUG(5) debugf("spool_unlock(%s) failed.\n", msgout->msg->uid);
848 }
850 }
852 destroy_msg_out_list(msgout_list);
854 return ok;
855 }
857 /* This function searches in the list of rcpt addresses
858 for local and 'local net' addresses. Remote addresses
859 which are reachable only when online are treated specially
860 in another function.
862 deliver() is called when a message has just been received and should
863 be delivered immediately.
864 */
865 gboolean
866 deliver(message * msg)
867 {
868 gboolean ok;
869 GList *msg_list = g_list_append(NULL, msg);
871 ok = deliver_msg_list(msg_list, DLVR_ALL);
872 g_list_free(msg_list);
874 return ok;
875 }