masqmail

view src/deliver.c @ 281:ea5f86e0a81c

modes are now enforced exclusive Other MTAs (exim, postfix) are more relaxing, but as combinations of exclusive modes are senseless we behave more obvious if we fail early. This makes understanding the behavior easier too.
author markus schnalke <meillo@marmaro.de>
date Tue, 07 Dec 2010 14:04:56 -0300
parents 5f9f3a65032e
children f10a56dc7481
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 return a new copy of the list */
239 void
240 msg_rcptlist_local(GList * rcpt_list, GList ** p_local_list, GList ** p_nonlocal_list)
241 {
242 GList *rcpt_node;
244 foreach(rcpt_list, rcpt_node) {
245 address *rcpt = (address *) (rcpt_node->data);
246 GList *dom_node;
248 DEBUG(5) debugf("checking address %s\n", rcpt->address);
250 /* search for local host list: */
251 foreach(conf.local_hosts, dom_node) {
252 if (strcasecmp(dom_node->data, rcpt->domain) == 0) {
253 *p_local_list = g_list_append(*p_local_list, rcpt);
254 DEBUG(5) debugf("<%s@%s> is local\n", rcpt->local_part, rcpt->domain);
255 break;
256 } else {
257 *p_nonlocal_list = g_list_append(*p_nonlocal_list, rcpt);
258 }
259 }
260 }
261 }
263 gboolean
264 deliver_msglist_host_pipe(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
265 {
266 gboolean ok = TRUE;
267 GList *msgout_node;
269 DEBUG(5) debugf("deliver_msglist_host_pipe entered\n");
271 if (route->pipe == NULL) {
272 logwrite(LOG_ALERT, "no pipe command given for route (protocol is pipe!)\n");
273 return FALSE;
274 }
276 foreach(msgout_list, msgout_node) {
277 msg_out *msgout = (msg_out *) (msgout_node->data);
278 gboolean flag, ok_fail = FALSE;
279 message *msg = msgout->msg;
280 GList *rcpt_node, *rcpt_list = msgout->rcpt_list;
282 DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
284 flag = (msg->data_list == NULL);
285 if (flag && !spool_read_data(msg)) {
286 logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid);
287 continue;
288 }
290 ok = FALSE;
291 foreach(rcpt_list, rcpt_node) {
292 address *rcpt = (address *) (rcpt_node->data);
293 gchar *cmd = g_malloc(256);
294 GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
296 DEBUG(1) debugf("attempting to deliver %s to %s@%s with pipe\n", msg->uid, rcpt->local_part, rcpt->domain);
298 if (!expand(var_table, route->pipe, cmd, 256)) {
299 logwrite(LOG_ALERT, "could not expand string %s\n", route->pipe);
300 } else {
302 if (pipe_out(msg, msg->hdr_list, rcpt, cmd, (route->pipe_fromline ? MSGSTR_FROMLINE : 0)
303 | (route->pipe_fromhack ? MSGSTR_FROMHACK : 0))) {
304 logwrite(LOG_NOTICE, "%s => %s@%s with pipe (cmd = '%s')\n",
305 msg->uid, rcpt->local_part, rcpt->domain, cmd);
306 addr_mark_delivered(rcpt);
307 ok = TRUE;
308 } else {
309 logwrite(LOG_ALERT, "pipe_out '%s' failed\n", route->pipe);
311 if (route->connect_error_fail) {
312 addr_mark_failed(rcpt);
313 } else {
314 addr_mark_defered(rcpt);
315 }
316 }
317 }
319 destroy_table(var_table);
320 }
321 ok_fail = delivery_failures(msg, rcpt_list, "%s", strerror(errno));
323 if (flag) {
324 msg_free_data(msg);
325 }
326 if (ok || ok_fail) {
327 deliver_finish(msgout);
328 }
329 }
331 return ok;
332 }
334 /* deliver list of messages to one host and finishes them if the message was
335 delivered to at least one rcpt.
336 Returns TRUE if at least one msg was delivered to at least one rcpt.
337 */
338 gboolean
339 deliver_msglist_host_smtp(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
340 {
341 gboolean ok = FALSE;
342 GList *msgout_node;
343 smtp_base *psb;
344 gint port = 25;
346 /* paranoid check: */
347 if (!msgout_list) {
348 logwrite(LOG_ALERT, "Ooops: empty list of messages in deliver_msglist_host()\n");
349 return FALSE;
350 }
352 if (!host) {
353 /* XXX: what if mail_host isn't set? Is this possible? */
354 host = route->mail_host->address;
355 port = route->mail_host->port;
356 }
358 if (route->wrapper) {
359 psb = smtp_out_open_child(route->wrapper);
360 } else {
361 psb = smtp_out_open(host, port, res_list);
362 }
364 if (!psb) {
365 /* smtp_out_open() failed */
366 foreach(msgout_list, msgout_node) {
367 msg_out *msgout = (msg_out *) (msgout_node->data);
368 GList *rcpt_node;
370 for (rcpt_node = g_list_first(msgout->rcpt_list);
371 rcpt_node;
372 rcpt_node = g_list_next(rcpt_node)) {
373 address *rcpt = (address *) (rcpt_node->data);
374 gboolean ret = FALSE;
376 addr_unmark_delivered(rcpt);
377 if (route->connect_error_fail) {
378 addr_mark_failed(rcpt);
379 } else {
380 addr_mark_defered(rcpt);
381 }
382 if (route->wrapper) {
383 ret = delivery_failures(msgout->msg, msgout->rcpt_list, "could not open wrapper:\n\t%s", strerror(errno));
384 } else {
385 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));
386 }
387 if (ret) {
388 deliver_finish(msgout);
389 }
390 }
391 }
392 return ok;
393 }
396 if (route->wrapper) {
397 /* it seems as if the remote_host is only set for logging
398 /* XXX: this could probably be moved into smtp_out_open_child() */
399 psb->remote_host = host;
400 }
402 set_heloname(psb, route->helo_name ? route->helo_name : conf.host_name, route->do_correct_helo);
404 #ifdef ENABLE_AUTH
405 if ((route->auth_name) && (route->auth_login) && (route->auth_secret)) {
406 set_auth(psb, route->auth_name, route->auth_login, route->auth_secret);
407 }
408 #endif
409 if (!smtp_out_init(psb, route->instant_helo)) {
410 /* smtp_out_init() failed */
411 if ((psb->error==smtp_fail) || (psb->error==smtp_trylater) || (psb->error==smtp_syntax)) {
412 smtp_out_quit(psb);
414 foreach(msgout_list, msgout_node) {
415 msg_out *msgout = (msg_out *) (msgout_node->data);
416 smtp_out_mark_rcpts(psb, msgout->rcpt_list);
418 if (delivery_failures(msgout->msg, msgout->rcpt_list, "while connected with %s, the server replied\n\t%s", host, psb->buffer)) {
419 deliver_finish(msgout);
420 }
421 }
422 }
423 destroy_smtpbase(psb);
424 return ok;
425 }
427 if (!route->do_pipelining) {
428 psb->use_pipelining = FALSE;
429 }
431 foreach(msgout_list, msgout_node) {
432 msg_out *msgout = (msg_out *) (msgout_node->data);
433 gboolean flag, ok_msg = FALSE, ok_fail = FALSE;
434 message *msg = msgout->msg;
436 /* we may have to read the data at this point and remember if we did */
437 flag = (msg->data_list == NULL);
438 if (flag && !spool_read_data(msg)) {
439 logwrite(LOG_ALERT, "could not open data spool file %s\n", msg->uid);
440 break;
441 }
443 smtp_out_msg(psb, msg, msgout->return_path, msgout->rcpt_list, msgout->hdr_list);
445 ok_fail = delivery_failures(msg, msgout->rcpt_list, "while connected with %s, the server replied\n\t%s", host, psb->buffer);
447 if ((psb->error == smtp_eof) || (psb->error == smtp_timeout)) {
448 /* connection lost */
449 break;
450 } else if (psb->error != smtp_ok) {
451 if (g_list_next(msgout_node) && !smtp_out_rset(psb)) {
452 break;
453 }
454 }
455 ok_msg = (psb->error == smtp_ok);
457 if (flag) {
458 msg_free_data(msg);
459 }
460 if (ok_msg) {
461 ok = TRUE;
462 }
463 if (ok_msg || ok_fail) {
464 deliver_finish(msgout);
465 }
466 }
467 if (psb->error == smtp_ok || (psb->error == smtp_fail)
468 || (psb->error == smtp_trylater) || (psb->error == smtp_syntax)) {
469 smtp_out_quit(psb);
470 }
471 destroy_smtpbase(psb);
472 return ok;
473 }
475 gboolean
476 deliver_msglist_host(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
477 {
478 DEBUG(5) debugf("protocol = %s\n", route->protocol);
480 if (strcmp(route->protocol, "pipe") == 0) {
481 return deliver_msglist_host_pipe(route, msgout_list, host, res_list);
482 } else {
483 return deliver_msglist_host_smtp(route, msgout_list, host, res_list);
484 }
485 }
487 /*
488 delivers messages in msgout_list using route
489 */
490 gboolean
491 deliver_route_msgout_list(connect_route * route, GList * msgout_list)
492 {
493 gboolean ok = FALSE;
494 GList *mo_ph_list;
495 GList *mo_ph_node;
497 DEBUG(5) debugf("deliver_route_msgout_list entered, route->name = %s\n", route->name);
499 if (route->mail_host) {
500 /* this is easy... deliver everything to a smart host for relay */
501 return deliver_msglist_host(route, msgout_list, NULL, route->resolve_list);
502 }
504 /* this is not easy... */
506 mo_ph_list = route_msgout_list(route, msgout_list);
507 /* okay, now we have ordered our messages by the hosts. */
508 if (!mo_ph_list) {
509 return FALSE;
510 }
512 /* TODO: It would be nice to be able to fork for each host.
513 We cannot do that yet because of complications with finishing the
514 messages. Threads could be a solution because they use the same
515 memory. But we are not thread safe yet...
516 */
517 foreach(mo_ph_list, mo_ph_node) {
518 msgout_perhost *mo_ph = (msgout_perhost *) (mo_ph_node->data);
519 if (deliver_msglist_host(route, mo_ph->msgout_list, mo_ph->host, route->resolve_list)) {
520 ok = TRUE;
521 }
522 destroy_msgout_perhost(mo_ph);
523 }
524 g_list_free(mo_ph_list);
525 return ok;
526 }
528 /*
529 calls route_prepare_msg()
530 delivers messages in msg_list using route by calling deliver_route_msgout_list()
531 */
532 gboolean
533 deliver_route_msg_list(connect_route * route, GList * msgout_list)
534 {
535 GList *msgout_list_deliver = NULL;
536 GList *msgout_node;
537 gboolean ok = TRUE;
539 DEBUG(6) debugf("deliver_route_msg_list()\n");
541 foreach(msgout_list, msgout_node) {
542 msg_out *msgout = (msg_out *) (msgout_node->data);
543 msg_out *msgout_cloned = clone_msg_out(msgout);
544 GList *rcpt_list_non_delivered = NULL;
545 GList *rcpt_node;
547 /* we have to delete already delivered rcpt's because a
548 previous route may have delivered to it */
549 foreach(msgout_cloned->rcpt_list, rcpt_node) {
550 address *rcpt = (address *) (rcpt_node->data);
551 /* failed addresses already have been bounced;
552 there should be a better way to handle those. */
553 if (!addr_is_delivered(rcpt) && !addr_is_failed(rcpt)
554 && !(rcpt->flags & ADDR_FLAG_LAST_ROUTE)) {
555 rcpt_list_non_delivered = g_list_append(rcpt_list_non_delivered, rcpt);
556 }
557 }
558 g_list_free(msgout_cloned->rcpt_list);
559 msgout_cloned->rcpt_list = rcpt_list_non_delivered;
561 if (!msgout_cloned->rcpt_list) {
562 destroy_msg_out(msgout_cloned);
563 continue;
564 }
566 if (!route_is_allowed_mail_local(route, msgout->msg->return_path)
567 || !route_is_allowed_return_path(route, msgout->msg-> return_path)) {
568 destroy_msg_out(msgout_cloned);
569 continue;
570 }
572 GList *rcpt_list_allowed = NULL, *rcpt_list_notallowed = NULL;
573 msg_rcptlist_route(route, msgout_cloned->rcpt_list, &rcpt_list_allowed, &rcpt_list_notallowed);
575 if (!rcpt_list_allowed) {
576 destroy_msg_out(msgout_cloned);
577 continue;
578 }
579 logwrite(LOG_NOTICE, "%s using '%s'\n", msgout->msg->uid, route->name);
581 g_list_free(msgout_cloned->rcpt_list);
582 msgout_cloned->rcpt_list = rcpt_list_allowed;
584 if (route->last_route) {
585 GList *rcpt_node;
586 foreach(msgout_cloned->rcpt_list, rcpt_node) {
587 address *rcpt = (address *) (rcpt_node->data);
588 rcpt->flags |= ADDR_FLAG_LAST_ROUTE;
589 }
590 }
592 route_prepare_msgout(route, msgout_cloned);
593 msgout_list_deliver = g_list_append(msgout_list_deliver, msgout_cloned);
594 }
596 if (msgout_list_deliver) {
597 if (deliver_route_msgout_list(route, msgout_list_deliver)) {
598 ok = TRUE;
599 }
600 destroy_msg_out_list(msgout_list_deliver);
601 }
602 return ok;
603 }
605 /* copy pointers of delivered addresses to the msg's non_rcpt_list,
606 to make sure that they will not be delivered again.
607 */
608 void
609 update_non_rcpt_list(msg_out * msgout)
610 {
611 GList *rcpt_node;
612 message *msg = msgout->msg;
614 foreach(msgout->rcpt_list, rcpt_node) {
615 address *rcpt = (address *) (rcpt_node->data);
616 if (addr_is_delivered(rcpt) || addr_is_failed(rcpt)) {
617 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, rcpt);
618 }
619 }
620 }
622 /* after delivery attempts, we check if there are any rcpt addresses left in
623 the message. If all addresses have been completed, the spool files will be
624 deleted, otherwise the header spool will be written back. We never changed
625 the data spool, so there is no need to write that back.
627 returns TRUE if all went well.
628 */
629 gboolean
630 deliver_finish(msg_out * msgout)
631 {
632 GList *rcpt_node;
633 message *msg = msgout->msg;
634 gboolean finished = TRUE;
636 update_non_rcpt_list(msgout);
638 /* we NEVER made copies of the addresses, flags affecting addresses
639 were always set on the original address structs */
640 foreach(msg->rcpt_list, rcpt_node) {
641 address *rcpt = (address *) (rcpt_node->data);
642 if (!addr_is_finished_children(rcpt)) {
643 finished = FALSE;
644 } else {
645 /* if ALL children have been delivered, mark parent as
646 delivered. if there is one or more not delivered,
647 it must have failed, we mark the parent as failed
648 as well.
649 */
650 if (addr_is_delivered_children(rcpt)) {
651 addr_mark_delivered(rcpt);
652 } else {
653 addr_mark_failed(rcpt);
654 }
655 }
656 }
658 if (finished) {
659 if (spool_delete_all(msg)) {
660 logwrite(LOG_NOTICE, "%s completed.\n", msg->uid);
661 return TRUE;
662 }
663 return FALSE;
664 }
666 /* one not delivered address was found */
667 if (!spool_write(msg, FALSE)) {
668 logwrite(LOG_ALERT, "could not write back spool header for %s\n", msg->uid);
669 return FALSE;
670 }
672 DEBUG(2) debugf("spool header for %s written back.\n", msg->uid);
673 return TRUE;
674 }
676 gboolean
677 deliver_finish_list(GList * msgout_list)
678 {
679 gboolean ok = TRUE;
680 GList *msgout_node;
681 foreach(msgout_list, msgout_node) {
682 msg_out *msgout = (msg_out *) (msgout_node->data);
683 if (!deliver_finish(msgout)) {
684 ok = FALSE;
685 }
686 }
687 return ok;
688 }
690 gboolean
691 deliver_msgout_list_online(GList * msgout_list)
692 {
693 GList *rf_list = NULL;
694 gchar *connect_name = NULL;
695 gboolean ok = FALSE;
697 connect_name = detect_online();
698 if (!connect_name) {
699 return FALSE;
700 }
702 /* we are online! */
703 logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
705 rf_list = (GList *) table_find(conf.connect_routes, connect_name);
706 if (!rf_list) {
707 logwrite(LOG_ALERT, "route list with name '%s' not found.\n", connect_name);
708 return FALSE;
709 }
711 GList *route_list = read_route_list(rf_list, FALSE);
712 if (!route_list) {
713 logwrite(LOG_ALERT, "could not read route list '%s'\n", connect_name);
714 return FALSE;
715 }
717 GList *route_node;
718 foreach(route_list, route_node) {
719 connect_route *route = (connect_route *) (route_node->data);
720 /* TODO: ok gets overwritten */
721 ok = deliver_route_msg_list(route, msgout_list);
722 }
723 destroy_route_list(route_list);
724 return ok;
725 }
727 gboolean
728 deliver_msg_list(GList * msg_list, guint flags)
729 {
730 GList *msgout_list = create_msg_out_list(msg_list);
731 GList *local_msgout_list = NULL;
732 GList *localnet_msgout_list = NULL;
733 GList *other_msgout_list = NULL;
734 GList *msgout_node;
735 GList *alias_table = NULL;
736 gboolean ok = TRUE;
738 if (conf.alias_file) {
739 alias_table = table_read(conf.alias_file, ':');
740 }
742 /* sort messages for different deliveries */
743 foreach(msgout_list, msgout_node) {
744 msg_out *msgout = (msg_out *) (msgout_node->data);
745 GList *rcpt_list;
746 GList *local_rcpt_list = NULL;
747 GList *localnet_rcpt_list = NULL;
748 GList *other_rcpt_list = NULL;
750 if (!spool_lock(msgout->msg->uid)) {
751 DEBUG(5) debugf("spool_lock(%s) failed.\n", msgout->msg->uid);
752 continue;
753 }
754 DEBUG(5) debugf("spool_lock(%s)\n", msgout->msg->uid);
756 rcpt_list = g_list_copy(msgout->msg->rcpt_list);
757 if (conf.log_user) {
758 address *addr = create_address_qualified(conf.log_user, TRUE, conf.host_name);
759 if (addr) {
760 rcpt_list = g_list_prepend(rcpt_list, addr);
761 } else {
762 logwrite(LOG_ALERT, "invalid log_user address `%s', ignoring\n", conf.log_user);
763 }
764 }
765 if (alias_table) {
766 GList *aliased_rcpt_list;
767 aliased_rcpt_list = alias_expand(alias_table, rcpt_list, msgout->msg->non_rcpt_list);
768 g_list_free(rcpt_list);
769 rcpt_list = aliased_rcpt_list;
770 }
772 split_rcpts(rcpt_list, conf.local_nets, &local_rcpt_list, &localnet_rcpt_list, &other_rcpt_list);
773 g_list_free(rcpt_list);
775 /* local recipients */
776 if ((flags & DLVR_LOCAL) && local_rcpt_list) {
777 msg_out *local_msgout = clone_msg_out(msgout);
778 local_msgout->rcpt_list = local_rcpt_list;
779 local_msgout_list = g_list_append(local_msgout_list, local_msgout);
780 }
782 /* local net recipients */
783 if ((flags & DLVR_LAN) && localnet_rcpt_list) {
784 msg_out *localnet_msgout = clone_msg_out(msgout);
785 localnet_msgout->rcpt_list = localnet_rcpt_list;
786 localnet_msgout_list = g_list_append(localnet_msgout_list, localnet_msgout);
787 }
789 /* remote recipients (the rest), requires online delivery */
790 if ((flags & DLVR_ONLINE) && other_rcpt_list) {
791 msg_out *other_msgout = clone_msg_out(msgout);
792 other_msgout->rcpt_list = other_rcpt_list;
793 other_msgout_list = g_list_append(other_msgout_list, other_msgout);
794 }
795 }
797 if (alias_table) {
798 destroy_table(alias_table);
799 }
801 /* actual delivery */
803 if (local_msgout_list) {
804 DEBUG(5) debugf("local_msgout_list\n");
805 foreach(local_msgout_list, msgout_node) {
806 msg_out *msgout = (msg_out *) (msgout_node->data);
807 if (!deliver_local(msgout)) {
808 ok = FALSE;
809 }
810 }
811 destroy_msg_out_list(local_msgout_list);
812 }
814 if (localnet_msgout_list) {
815 GList *route_list = NULL;
816 GList *route_node;
818 DEBUG(5) debugf("localnet_msgout_list\n");
819 if (conf.local_net_routes) {
820 route_list = read_route_list(conf.local_net_routes, TRUE);
821 } else {
822 route_list = g_list_append(NULL, create_local_route());
823 }
825 foreach(route_list, route_node) {
826 connect_route *route = (connect_route *) (route_node->data);
827 if (!deliver_route_msg_list(route, localnet_msgout_list)) {
828 ok = FALSE;
829 }
830 }
831 destroy_msg_out_list(localnet_msgout_list);
832 destroy_route_list(route_list);
833 }
835 if (other_msgout_list) {
836 DEBUG(5) debugf("other_msgout_list\n");
837 if (!deliver_msgout_list_online(other_msgout_list)) {
838 ok = FALSE;
839 }
840 destroy_msg_out_list(other_msgout_list);
841 }
843 foreach(msgout_list, msgout_node) {
844 msg_out *msgout = (msg_out *) (msgout_node->data);
845 if (spool_unlock(msgout->msg->uid)) {
846 DEBUG(5) debugf("spool_unlock(%s)\n", msgout->msg->uid);
847 } else {
848 DEBUG(5) debugf("spool_unlock(%s) failed.\n", msgout->msg->uid);
849 }
851 }
853 destroy_msg_out_list(msgout_list);
855 return ok;
856 }
858 /* This function searches in the list of rcpt addresses
859 for local and 'local net' addresses. Remote addresses
860 which are reachable only when online are treated specially
861 in another function.
863 deliver() is called when a message has just been received and should
864 be delivered immediately.
865 */
866 gboolean
867 deliver(message * msg)
868 {
869 gboolean ok;
870 GList *msg_list = g_list_append(NULL, msg);
872 ok = deliver_msg_list(msg_list, DLVR_ALL);
873 g_list_free(msg_list);
875 return ok;
876 }