masqmail
view src/deliver.c @ 187:bd7c52a36b0c
improved mservdetect in various ways
errors are handled better (no segfaults anymore)
copied the relevant part of interface.c into mservdetect.c
described how I think the mserver protocol works
author | meillo@marmaro.de |
---|---|
date | Thu, 15 Jul 2010 00:14:26 +0200 |
parents | 91f8ee6514f5 |
children | dc89737b27aa |
line source
1 /* MasqMail
2 Copyright (C) 1999-2002 Oliver Kurth
3 Copyright (C) 2008 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 if (addr_is_failed(rcpt))
49 failed_list = g_list_prepend(failed_list, rcpt);
50 }
51 if (failed_list != NULL) {
52 ok_fail = fail_msg(msg, conf.errmsg_file, failed_list, err_fmt, args);
53 g_list_free(failed_list);
54 }
55 if (defered_list != NULL) {
56 ok_warn = warn_msg(msg, conf.warnmsg_file, defered_list, err_fmt, args);
57 g_list_free(defered_list);
58 }
59 va_end(args);
60 return ok_fail && ok_warn;
61 }
63 static gint
64 _g_list_strcasecmp(gconstpointer a, gconstpointer b)
65 {
66 return (gint) strcasecmp(a, b);
67 }
69 gboolean
70 deliver_local(msg_out * msgout)
71 {
72 message *msg = msgout->msg;
73 GList *rcpt_list = msgout->rcpt_list;
74 GList *rcpt_node;
75 gboolean ok = TRUE, flag = FALSE, ok_fail = FALSE;
77 DEBUG(5) debugf("deliver_local entered\n");
79 flag = (msg->data_list == NULL);
80 if (flag) {
81 if (!(ok = spool_read_data(msg))) {
82 logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid);
83 }
84 }
85 if (!ok)
86 return FALSE;
88 ok = FALSE;
89 for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
90 GList *hdr_list;
91 address *rcpt = (address *) (rcpt_node->data);
92 address *env_addr = addr_find_ancestor(rcpt);
93 address *ret_path = msg->return_path;
94 header *retpath_hdr, *envto_hdr;
96 /* we need a private copy of the hdr list because we add headers here that belong to the rcpt only.
97 g_list_copy copies only the nodes, so it is safe to g_list_free it */
98 hdr_list = g_list_copy(msg->hdr_list);
99 retpath_hdr = create_header(HEAD_ENVELOPE_TO, "Envelope-to: %s\n", addr_string(env_addr));
100 envto_hdr = create_header(HEAD_RETURN_PATH, "Return-path: %s\n", addr_string(ret_path));
102 hdr_list = g_list_prepend(hdr_list, envto_hdr);
103 hdr_list = g_list_prepend(hdr_list, retpath_hdr);
105 if (rcpt->local_part[0] == '|') {
106 DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
107 if (pipe_out(msg, hdr_list, rcpt, &(rcpt->local_part[1]),
108 (conf.pipe_fromline ? MSGSTR_FROMLINE : 0)
109 | (conf.pipe_fromhack ? MSGSTR_FROMHACK : 0))) {
110 logwrite(LOG_NOTICE, "%s => %s <%s@%s> with pipe\n",
111 msg->uid, rcpt->local_part, env_addr->local_part, env_addr->domain);
112 addr_mark_delivered(rcpt);
113 ok = TRUE;
114 } else {
115 if ((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)) {
116 addr_mark_failed(rcpt);
117 } else {
118 addr_mark_defered(rcpt); /* has no effect yet, except that mail remains in spool */
119 }
120 }
121 } else {
122 /* figure out which mailbox type should be used for this user */
123 gchar *user = rcpt->local_part;
124 gchar *mbox_type = conf.mbox_default;
126 if (g_list_find_custom (conf.mbox_users, user, _g_list_strcasecmp) != NULL)
127 mbox_type = "mbox";
128 else if (g_list_find_custom (conf.mda_users, user, _g_list_strcasecmp) != NULL)
129 mbox_type = "mda";
130 else if (g_list_find_custom (conf.maildir_users, user, _g_list_strcasecmp) != NULL)
131 mbox_type = "maildir";
133 if (strcmp(mbox_type, "mbox") == 0) {
134 DEBUG(1) debugf("attempting to deliver %s with mbox\n", msg->uid);
135 if (append_file(msg, hdr_list, rcpt->local_part)) {
136 if (env_addr != rcpt) {
137 logwrite(LOG_NOTICE, "%s => %s@%s <%s@%s> with mbox\n",
138 msg->uid, rcpt->local_part, rcpt->domain,
139 env_addr->local_part, env_addr->domain);
140 } else {
141 logwrite(LOG_NOTICE, "%s => <%s@%s> with mbox\n",
142 msg->uid, rcpt->local_part, rcpt->domain);
143 }
144 addr_mark_delivered(rcpt);
145 ok = TRUE;
146 } else {
147 if (errno != EAGAIN) { /* prevents 'Resource temporarily unavailable (11)' */
148 addr_mark_failed(rcpt);
149 } else {
150 addr_mark_defered(rcpt);
151 }
152 }
154 } else if (strcmp(mbox_type, "mda") == 0) {
155 if (conf.mda) {
156 gchar *cmd = g_malloc(256);
157 GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
159 DEBUG(1) debugf("attempting to deliver %s with mda\n", msg->uid);
161 if (expand(var_table, conf.mda, cmd, 256)) {
163 if (pipe_out(msg, hdr_list, rcpt, cmd, (conf.mda_fromline ? MSGSTR_FROMLINE : 0)
164 | (conf.mda_fromhack ? MSGSTR_FROMHACK : 0))) {
165 logwrite(LOG_NOTICE, "%s => %s@%s with mda (cmd = '%s')\n",
166 msg->uid, rcpt->local_part, rcpt->domain, cmd);
167 addr_mark_delivered(rcpt);
168 ok = TRUE;
169 } else {
170 if ((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)) {
171 addr_mark_failed(rcpt);
172 } else {
173 addr_mark_defered(rcpt); /* has no effect yet, except that mail remains in spool */
174 }
175 }
176 } else
177 logwrite(LOG_ALERT, "could not expand string %s\n", conf.mda);
179 destroy_table(var_table);
180 } else
181 logwrite(LOG_ALERT, "mbox type is mda, but no mda command given in configuration\n");
183 #ifdef ENABLE_MAILDIR
184 } else if (strcmp(mbox_type, "maildir") == 0) {
185 DEBUG(1) debugf("attempting to deliver %s with maildir\n", msg->uid);
186 if (maildir_out(msg, hdr_list, rcpt->local_part, 0)) {
187 if (env_addr != rcpt) {
188 logwrite(LOG_NOTICE, "%s => %s@%s <%s@%s> with local\n", msg->uid,
189 rcpt->local_part, rcpt->domain, env_addr->local_part, env_addr->domain);
190 } else {
191 logwrite(LOG_NOTICE, "%s => <%s@%s> with maildir\n", msg->uid,
192 rcpt->local_part, rcpt->domain);
193 }
194 addr_mark_delivered(rcpt);
195 ok = TRUE;
196 } else
197 addr_mark_failed(rcpt);
198 #endif
199 } else
200 logwrite(LOG_ALERT, "unknown mbox type '%s'\n", mbox_type);
201 }
203 destroy_header(retpath_hdr);
204 destroy_header(envto_hdr);
206 g_list_free(hdr_list);
207 }
208 ok_fail = delivery_failures(msg, rcpt_list, "%s (%d)", ext_strerror(errno), errno);
210 if (flag)
211 msg_free_data(msg);
212 if (ok || ok_fail)
213 deliver_finish(msgout);
215 return ok;
216 }
218 /* make a list of rcpt's of a message that are local return a new copy of the list */
219 void
220 msg_rcptlist_local(GList * rcpt_list, GList ** p_local_list, GList ** p_nonlocal_list)
221 {
222 GList *rcpt_node;
224 foreach(rcpt_list, rcpt_node) {
225 address *rcpt = (address *) (rcpt_node->data);
226 GList *dom_node;
228 DEBUG(5) debugf("checking address %s\n", rcpt->address);
230 /* search for local host list: */
231 foreach(conf.local_hosts, dom_node) {
232 if (strcasecmp(dom_node->data, rcpt->domain) == 0) {
233 *p_local_list = g_list_append(*p_local_list, rcpt);
234 DEBUG(5) debugf("<%s@%s> is local\n", rcpt->local_part, rcpt->domain);
235 break;
236 } else {
237 *p_nonlocal_list = g_list_append(*p_nonlocal_list, rcpt);
238 }
239 }
240 }
241 }
243 gboolean
244 deliver_msglist_host_pipe(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
245 {
246 gboolean ok = TRUE;
247 GList *msgout_node;
249 DEBUG(5) debugf("deliver_msglist_host_pipe entered\n");
251 if (route->pipe == NULL) {
252 logwrite(LOG_ALERT, "no pipe command given for route (protocol is pipe!)\n");
253 return FALSE;
254 }
256 foreach(msgout_list, msgout_node) {
257 msg_out *msgout = (msg_out *) (msgout_node->data);
258 gboolean flag, ok_msg = TRUE, ok_fail = FALSE;
259 message *msg = msgout->msg;
260 GList *rcpt_node, *rcpt_list = msgout->rcpt_list;
262 DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
264 flag = (msg->data_list == NULL);
265 if (flag) {
266 if (!(ok_msg = spool_read_data(msg))) {
267 logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid);
268 }
269 }
270 if (!ok_msg)
271 continue;
273 ok = FALSE;
274 foreach(rcpt_list, rcpt_node) {
275 address *rcpt = (address *) (rcpt_node->data);
276 gchar *cmd = g_malloc(256);
277 GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
279 DEBUG(1) debugf("attempting to deliver %s to %s@%s with pipe\n", msg->uid, rcpt->local_part, rcpt->domain);
281 if (expand(var_table, route->pipe, cmd, 256)) {
283 if (pipe_out(msg, msg->hdr_list, rcpt, cmd, (route->pipe_fromline ? MSGSTR_FROMLINE : 0)
284 | (route->pipe_fromhack ? MSGSTR_FROMHACK : 0))) {
285 logwrite(LOG_NOTICE, "%s => %s@%s with pipe (cmd = '%s')\n",
286 msg->uid, rcpt->local_part, rcpt->domain, cmd);
287 addr_mark_delivered(rcpt);
288 ok = TRUE;
289 } else {
290 logwrite(LOG_ALERT, "pipe_out '%s' failed\n", route->pipe);
292 if (route->connect_error_fail) {
293 addr_mark_failed(rcpt);
294 } else {
295 addr_mark_defered(rcpt);
296 }
297 }
298 } else
299 logwrite(LOG_ALERT, "could not expand string %s\n", route->pipe);
301 destroy_table(var_table);
302 }
303 ok_fail = delivery_failures(msg, rcpt_list, "%s", strerror(errno));
305 if (flag)
306 msg_free_data(msg);
308 if (ok || ok_fail)
309 deliver_finish(msgout);
310 }
312 return ok;
313 }
315 /* deliver list of messages to one host and finishes them if the message was delivered to at least one rcpt.
316 Returns TRUE if at least one msg was delivered to at least one rcpt.
317 */
318 gboolean
319 deliver_msglist_host_smtp(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
320 {
321 gboolean ok = FALSE;
322 GList *msgout_node;
323 smtp_base *psb;
324 gint port = 25;
326 /* paranoid check: */
327 if (msgout_list == NULL) {
328 logwrite(LOG_ALERT, "Ooops: empty list of messages in deliver_msglist_host()\n");
329 return FALSE;
330 }
332 if (host == NULL) {
333 /* XXX: what if mail_host isn't set? Is this possible? */
334 host = route->mail_host->address;
335 port = route->mail_host->port;
336 }
338 #ifdef ENABLE_POP3
339 if (route->pop3_login) {
340 if (!(pop_before_smtp(route->pop3_login)))
341 return FALSE;
342 }
343 #endif
345 if ((psb = (route->wrapper ? smtp_out_open_child(route->wrapper) : smtp_out_open(host, port, res_list)))) {
347 if (route->wrapper) {
348 /* it seems as if the remote_host is only set for logging
349 /* XXX: this could probably be moved into smtp_out_open_child() */
350 psb->remote_host = host;
351 }
353 set_heloname(psb, route->helo_name ? route->helo_name : conf.host_name, route->do_correct_helo);
355 #ifdef ENABLE_AUTH
356 if ((route->auth_name) && (route->auth_login) && (route->auth_secret))
357 set_auth(psb, route->auth_name, route->auth_login, route->auth_secret);
358 #endif
359 if (smtp_out_init(psb)) {
361 if (!route->do_pipelining)
362 psb->use_pipelining = FALSE;
364 foreach(msgout_list, msgout_node) {
365 msg_out *msgout = (msg_out *) (msgout_node->data);
366 gboolean flag, ok_msg = FALSE, ok_fail = FALSE;
367 message *msg = msgout->msg;
369 /* we may have to read the data at this point and remember if we did */
370 flag = (msg->data_list == NULL);
371 if (flag) {
372 if (!spool_read_data(msg)) {
373 logwrite(LOG_ALERT, "could not open data spool file %s\n", msg->uid);
374 break;
375 }
376 }
378 smtp_out_msg(psb, msg, msgout->return_path, msgout->rcpt_list, msgout->hdr_list);
380 ok_fail = delivery_failures(msg, msgout->rcpt_list,
381 "while connected with %s, the server replied\n\t%s", host, psb->buffer);
383 if ((psb->error == smtp_eof)
384 || (psb->error == smtp_timeout)) {
385 /* connection lost */
386 break;
387 } else if (psb->error != smtp_ok) {
388 if (g_list_next(msgout_node) != NULL)
389 if (!smtp_out_rset(psb))
390 break;
391 }
392 ok_msg = (psb->error == smtp_ok);
394 if (flag)
395 msg_free_data(msg);
396 if (ok_msg)
397 ok = TRUE;
398 if (ok_msg || ok_fail) {
399 deliver_finish(msgout);
400 }
401 }
402 if (psb->error == smtp_ok || (psb->error == smtp_fail)
403 || (psb->error == smtp_trylater) || (psb->error == smtp_syntax)) {
404 smtp_out_quit(psb);
405 }
406 } else {
407 /* smtp_out_init() failed */
408 if ((psb->error == smtp_fail) || (psb->error == smtp_trylater) || (psb->error == smtp_syntax)) {
409 smtp_out_quit(psb);
411 foreach(msgout_list, msgout_node) {
412 msg_out *msgout = (msg_out *) (msgout_node->data);
413 smtp_out_mark_rcpts(psb, msgout->rcpt_list);
415 if (delivery_failures(msgout->msg, msgout->rcpt_list,
416 "while connected with %s, the server replied\n\t%s", host, psb->buffer))
417 deliver_finish(msgout);
418 }
419 }
420 }
421 destroy_smtpbase(psb);
422 } else {
423 /* smtp_out_open() failed */
424 foreach(msgout_list, msgout_node) {
425 msg_out *msgout = (msg_out *) (msgout_node->data);
426 GList *rcpt_node;
428 for (rcpt_node = g_list_first(msgout->rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) {
429 address *rcpt = (address *) (rcpt_node->data);
431 addr_unmark_delivered(rcpt);
432 if (route->connect_error_fail) {
433 addr_mark_failed(rcpt);
434 } else {
435 addr_mark_defered(rcpt);
436 }
437 if (route->wrapper
438 ? delivery_failures(msgout->msg, msgout->rcpt_list, "could not open wrapper:\n\t%s",
439 strerror(errno))
440 : delivery_failures(msgout->msg, msgout->rcpt_list, "could not open connection to %s:%d :\n\t%s",
441 host, port, h_errno != 0 ? hstrerror(h_errno) : strerror(errno)))
442 deliver_finish(msgout);
443 }
444 }
445 }
446 return ok;
447 }
449 gboolean
450 deliver_msglist_host(connect_route * route, GList * msgout_list, gchar * host, GList * res_list)
451 {
452 DEBUG(5) debugf("protocol = %s\n", route->protocol);
454 if (strcmp(route->protocol, "pipe") == 0) {
455 return deliver_msglist_host_pipe(route, msgout_list, host, res_list);
456 } else {
457 return deliver_msglist_host_smtp(route, msgout_list, host, res_list);
458 }
459 }
461 /*
462 delivers messages in msgout_list using route
463 */
464 gboolean
465 deliver_route_msgout_list(connect_route * route, GList * msgout_list)
466 {
467 gboolean ok = FALSE;
469 DEBUG(5) debugf("deliver_route_msgout_list entered, route->name = %s\n", route->name);
471 if (route->mail_host) {
472 /* this is easy... deliver everything to a smart host for relay */
473 if (deliver_msglist_host(route, msgout_list, NULL, route->resolve_list))
474 ok = TRUE;
476 } else {
477 /* this is not easy... */
478 GList *mo_ph_list;
480 mo_ph_list = route_msgout_list(route, msgout_list);
481 /* okay, now we have ordered our messages by the hosts. */
482 if (mo_ph_list != NULL) {
483 GList *mo_ph_node;
484 /* TODO: It would be nice to be able to fork for each host.
485 We cannot do that yet because of complications with finishing the
486 messages. Threads could be a solution because they use the same
487 memory. But we are not thread safe yet...
488 */
489 foreach(mo_ph_list, mo_ph_node) {
490 msgout_perhost *mo_ph = (msgout_perhost *) (mo_ph_node->data);
491 if (deliver_msglist_host (route, mo_ph->msgout_list, mo_ph->host, route->resolve_list))
492 ok = TRUE;
494 destroy_msgout_perhost(mo_ph);
495 }
496 g_list_free(mo_ph_list);
497 }
498 }
499 return ok;
500 }
502 /*
503 calls route_prepare_msg()
504 delivers messages in msg_list using route by calling deliver_route_msgout_list()
505 */
506 gboolean
507 deliver_route_msg_list(connect_route * route, GList * msgout_list)
508 {
509 GList *msgout_list_deliver = NULL;
510 GList *msgout_node;
511 gboolean ok = TRUE;
513 DEBUG(6) debugf("deliver_route_msg_list()\n");
515 foreach(msgout_list, msgout_node) {
516 msg_out *msgout = (msg_out *) (msgout_node->data);
517 msg_out *msgout_cloned = clone_msg_out(msgout);
518 GList *rcpt_list_non_delivered = NULL;
519 GList *rcpt_node;
521 /* we have to delete already delivered rcpt's because a previous route may have delivered to it */
522 foreach(msgout_cloned->rcpt_list, rcpt_node) {
523 address *rcpt = (address *) (rcpt_node->data);
524 /* failed addresses already have been bounced - there should be a better way to handle those. */
525 if (!addr_is_delivered(rcpt) && !addr_is_failed(rcpt)
526 && !(rcpt->flags & ADDR_FLAG_LAST_ROUTE))
527 rcpt_list_non_delivered = g_list_append(rcpt_list_non_delivered, rcpt);
528 }
529 g_list_free(msgout_cloned->rcpt_list);
530 msgout_cloned->rcpt_list = rcpt_list_non_delivered;
532 if (msgout_cloned->rcpt_list) {
533 if (route_is_allowed_mail_local(route, msgout->msg->return_path)
534 && route_is_allowed_return_path(route, msgout->msg-> return_path)) {
535 GList *rcpt_list_allowed = NULL, *rcpt_list_notallowed = NULL;
536 msg_rcptlist_route(route, msgout_cloned->rcpt_list, &rcpt_list_allowed, &rcpt_list_notallowed);
538 if (rcpt_list_allowed != NULL) {
539 logwrite(LOG_NOTICE, "%s using '%s'\n", msgout->msg->uid, route->name);
541 g_list_free(msgout_cloned->rcpt_list);
542 msgout_cloned->rcpt_list = rcpt_list_allowed;
544 if (route->last_route) {
545 GList *rcpt_node;
546 foreach(msgout_cloned->rcpt_list, rcpt_node) {
547 address *rcpt = (address *) (rcpt_node->data);
548 rcpt->flags |= ADDR_FLAG_LAST_ROUTE;
549 }
550 }
552 route_prepare_msgout(route, msgout_cloned);
553 msgout_list_deliver = g_list_append(msgout_list_deliver, msgout_cloned);
554 } else
555 destroy_msg_out(msgout_cloned);
556 } else
557 destroy_msg_out(msgout_cloned);
558 } else
559 destroy_msg_out(msgout_cloned);
560 }
562 if (msgout_list_deliver != NULL) {
563 if (deliver_route_msgout_list(route, msgout_list_deliver))
564 ok = TRUE;
565 destroy_msg_out_list(msgout_list_deliver);
566 }
567 return ok;
568 }
570 /* copy pointers of delivered addresses to the msg's non_rcpt_list,
571 to make sure that they will not be delivered again.
572 */
573 void
574 update_non_rcpt_list(msg_out * msgout)
575 {
576 GList *rcpt_node;
577 message *msg = msgout->msg;
579 foreach(msgout->rcpt_list, rcpt_node) {
580 address *rcpt = (address *) (rcpt_node->data);
581 if (addr_is_delivered(rcpt) || addr_is_failed(rcpt))
582 msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, rcpt);
583 }
584 }
586 /* after delivery attempts, we check if there are any rcpt addresses left in the message.
587 If all addresses have been completed, the spool files will be deleted,
588 otherwise the header spool will be written back.
589 We never changed the data spool, so there is no need to write that back.
591 returns TRUE if all went well.
592 */
593 gboolean
594 deliver_finish(msg_out * msgout)
595 {
596 GList *rcpt_node;
597 gboolean ok = FALSE;
598 message *msg = msgout->msg;
599 gboolean finished = TRUE;
601 update_non_rcpt_list(msgout);
603 /* we NEVER made copies of the addresses, flags affecting addresses
604 were always set on the original address structs */
605 foreach(msg->rcpt_list, rcpt_node) {
606 address *rcpt = (address *) (rcpt_node->data);
607 if (!addr_is_finished_children(rcpt))
608 finished = FALSE;
609 else {
610 /* if ALL children have been delivered, mark parent as delivered.
611 if there is one or more not delivered, it must have failed, we mark the parent as failed as well.
612 */
613 if (addr_is_delivered_children(rcpt)) {
614 addr_mark_delivered(rcpt);
615 } else {
616 addr_mark_failed(rcpt);
617 }
618 }
619 }
621 if (!finished) {
622 /* one not delivered address was found */
623 if (spool_write(msg, FALSE)) {
624 ok = TRUE;
625 DEBUG(2) debugf("spool header for %s written back.\n", msg->uid);
626 } else
627 logwrite(LOG_ALERT, "could not write back spool header for %s\n", msg->uid);
628 } else {
629 ok = spool_delete_all(msg);
630 if (ok)
631 logwrite(LOG_NOTICE, "%s completed.\n", msg->uid);
632 }
633 return ok;
634 }
636 gboolean
637 deliver_finish_list(GList * msgout_list)
638 {
639 gboolean ok = TRUE;
640 GList *msgout_node;
641 foreach(msgout_list, msgout_node) {
642 msg_out *msgout = (msg_out *) (msgout_node->data);
643 if (!deliver_finish(msgout))
644 ok = FALSE;
645 }
646 return ok;
647 }
649 gboolean
650 deliver_msgout_list_online(GList * msgout_list)
651 {
652 GList *rf_list = NULL;
653 gchar *connect_name = detect_online();
654 gboolean ok = FALSE;
656 if (connect_name != NULL) {
657 logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
658 /* we are online! */
659 rf_list = (GList *) table_find(conf.connect_routes, connect_name);
660 if (rf_list != NULL) {
661 GList *route_list = read_route_list(rf_list, FALSE);
662 if (route_list) {
663 GList *route_node;
664 foreach(route_list, route_node) {
665 connect_route *route = (connect_route *) (route_node->data);
666 ok = deliver_route_msg_list(route, msgout_list);
667 }
668 destroy_route_list(route_list);
669 } else
670 logwrite(LOG_ALERT, "could not read route list '%s'\n", connect_name);
671 } else {
672 logwrite(LOG_ALERT, "route list with name '%s' not found.\n", connect_name);
673 }
674 }
675 return ok;
676 }
678 gboolean
679 deliver_msg_list(GList * msg_list, guint flags)
680 {
681 GList *msgout_list = create_msg_out_list(msg_list);
682 GList *local_msgout_list = NULL, *localnet_msgout_list = NULL, *other_msgout_list = NULL;
683 GList *msgout_node;
684 GList *alias_table = NULL;
685 gboolean ok = TRUE;
687 if (conf.alias_file) {
688 alias_table = table_read(conf.alias_file, ':');
689 }
691 /* sort messages for different deliveries */
692 foreach(msgout_list, msgout_node) {
693 msg_out *msgout = (msg_out *) (msgout_node->data);
694 GList *rcpt_list;
695 GList *local_rcpt_list = NULL;
696 GList *localnet_rcpt_list = NULL;
697 GList *other_rcpt_list;
699 if (!spool_lock(msgout->msg->uid)) {
700 DEBUG(5) debugf("spool_lock(%s) failed.\n", msgout->msg->uid);
701 continue;
702 }
703 DEBUG(5) debugf("spool_lock(%s)\n", msgout->msg->uid);
705 rcpt_list = g_list_copy(msgout->msg->rcpt_list);
706 if (conf.log_user) {
707 address *addr = create_address_qualified(conf.log_user, TRUE, conf.host_name);
708 if (addr)
709 rcpt_list = g_list_prepend(rcpt_list, addr);
710 }
711 if (alias_table) {
712 GList *aliased_rcpt_list;
713 aliased_rcpt_list = alias_expand(alias_table, rcpt_list, msgout->msg->non_rcpt_list);
714 g_list_free(rcpt_list);
715 rcpt_list = aliased_rcpt_list;
716 }
718 /* local recipients */
719 other_rcpt_list = NULL;
720 rcptlist_with_addr_is_local(rcpt_list, &local_rcpt_list, &other_rcpt_list);
722 if (flags & DLVR_LOCAL) {
723 if (local_rcpt_list != NULL) {
724 msg_out *local_msgout = clone_msg_out(msgout);
725 local_msgout->rcpt_list = local_rcpt_list;
726 local_msgout_list = g_list_append(local_msgout_list, local_msgout);
727 }
728 }
730 g_list_free(rcpt_list);
732 /* local net recipients */
733 rcpt_list = other_rcpt_list;
734 other_rcpt_list = NULL;
735 rcptlist_with_one_of_hostlist(rcpt_list, conf.local_nets, &localnet_rcpt_list, &other_rcpt_list);
737 if (flags & DLVR_LAN) {
738 if (localnet_rcpt_list != NULL) {
739 msg_out *localnet_msgout = clone_msg_out(msgout);
740 localnet_msgout->rcpt_list = localnet_rcpt_list;
741 localnet_msgout_list = g_list_append(localnet_msgout_list, localnet_msgout);
742 }
743 }
745 if (flags & DLVR_ONLINE) {
746 /* the rest, this is online delivery */
747 if (other_rcpt_list != NULL) {
748 msg_out *other_msgout = clone_msg_out(msgout);
749 other_msgout->rcpt_list = other_rcpt_list;
750 other_msgout_list = g_list_append(other_msgout_list, other_msgout);
751 }
752 }
753 }
755 if (alias_table)
756 destroy_table(alias_table);
758 /* actual delivery */
759 if (local_msgout_list != NULL) {
760 DEBUG(5) debugf("local_msgout_list\n");
761 foreach(local_msgout_list, msgout_node) {
762 msg_out *msgout = (msg_out *) (msgout_node->data);
763 if (!deliver_local(msgout))
764 ok = FALSE;
765 }
766 destroy_msg_out_list(local_msgout_list);
767 }
769 if (localnet_msgout_list != NULL) {
770 GList *route_list = NULL;
771 GList *route_node;
773 DEBUG(5) debugf("localnet_msgout_list\n");
774 if (conf.local_net_routes)
775 route_list = read_route_list(conf.local_net_routes, TRUE);
776 else
777 route_list = g_list_append(NULL, create_local_route());
779 foreach(route_list, route_node) {
780 connect_route *route = (connect_route *) (route_node->data);
781 if (!deliver_route_msg_list(route, localnet_msgout_list))
782 ok = FALSE;
783 }
784 destroy_msg_out_list(localnet_msgout_list);
785 destroy_route_list(route_list);
786 }
788 if (other_msgout_list != NULL) {
789 DEBUG(5) debugf("other_msgout_list\n");
790 if (!deliver_msgout_list_online(other_msgout_list))
791 ok = FALSE;
792 destroy_msg_out_list(other_msgout_list);
793 }
795 foreach(msgout_list, msgout_node) {
796 msg_out *msgout = (msg_out *) (msgout_node->data);
797 if (spool_unlock(msgout->msg->uid)) {
798 DEBUG(5) debugf("spool_unlock(%s)\n", msgout->msg->uid);
799 } else {
800 DEBUG(5) debugf("spool_unlock(%s) failed.\n", msgout->msg->uid);
801 }
803 }
805 destroy_msg_out_list(msgout_list);
807 return ok;
808 }
810 /* This function searches in the list of rcpt addresses
811 for local and 'local net' addresses. Remote addresses
812 which are reachable only when online are treated specially
813 in another function.
815 deliver() is called when a message has just been received and should
816 be delivered immediately.
817 */
818 gboolean
819 deliver(message * msg)
820 {
821 gboolean ok;
822 GList *msg_list = g_list_append(NULL, msg);
824 ok = deliver_msg_list(msg_list, DLVR_ALL);
825 g_list_free(msg_list);
827 return ok;
828 }