Mercurial > masqmail
view src/deliver.c @ 283:853b85616c98
improved man/masqmail.8 heavily
In respect to the modes, -bm, -q, and -qo.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Tue, 07 Dec 2010 14:19:04 -0300 |
parents | 72e377210d5e |
children | f10a56dc7481 |
line wrap: on
line source
/* MasqMail Copyright (C) 1999-2002 Oliver Kurth Copyright (C) 2008, 2010 markus schnalke <meillo@marmaro.de> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <fnmatch.h> #include <sysexits.h> #include <netdb.h> #include "masqmail.h" #include "smtp_out.h" /* collect failed/defered rcpts for failure/warning messages */ /* returns TRUE if either there are no failures or a failure message has been successfully sent */ gboolean delivery_failures(message * msg, GList * rcpt_list, gchar * err_fmt, ...) { gboolean ok_fail = TRUE, ok_warn = TRUE; time_t now = time(NULL); GList *failed_list = NULL, *defered_list = NULL, *rcpt_node; va_list args; va_start(args, err_fmt); foreach(rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); if (addr_is_defered(rcpt)) { if ((now - msg->received_time) >= conf.max_defer_time) { addr_mark_failed(rcpt); } else { defered_list = g_list_prepend(defered_list, rcpt); } } if (addr_is_failed(rcpt)) { failed_list = g_list_prepend(failed_list, rcpt); } } if (failed_list) { ok_fail = fail_msg(msg, conf.errmsg_file, failed_list, err_fmt, args); g_list_free(failed_list); } if (defered_list) { ok_warn = warn_msg(msg, conf.warnmsg_file, defered_list, err_fmt, args); g_list_free(defered_list); } va_end(args); return ok_fail && ok_warn; } static gint _g_list_strcasecmp(gconstpointer a, gconstpointer b) { return (gint) strcasecmp(a, b); } gboolean deliver_local_mbox(message* msg, GList* hdr_list, address* rcpt, address* env_addr) { DEBUG(1) debugf("attempting to deliver %s with mbox\n", msg->uid); if (append_file(msg, hdr_list, rcpt->local_part)) { if (env_addr != rcpt) { 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); } else { logwrite(LOG_NOTICE, "%s => <%s@%s> with mbox\n", msg->uid, rcpt->local_part, rcpt->domain); } addr_mark_delivered(rcpt); return TRUE; } /* prevents 'Resource temporarily unavailable (11)' */ if (errno != EAGAIN) { addr_mark_failed(rcpt); } else { addr_mark_defered(rcpt); } return FALSE; } gboolean deliver_local_pipe(message* msg, GList* hdr_list, address* rcpt, address* env_addr) { guint flags; DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid); flags = (conf.pipe_fromline) ? MSGSTR_FROMLINE : 0; flags |= (conf.pipe_fromhack) ? MSGSTR_FROMHACK : 0; if (pipe_out(msg, hdr_list, rcpt, &(rcpt->local_part[1]), flags)) { logwrite(LOG_NOTICE, "%s => %s <%s@%s> with pipe\n", msg->uid, rcpt->local_part, env_addr->local_part, env_addr->domain); addr_mark_delivered(rcpt); return TRUE; } if ((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)) { addr_mark_failed(rcpt); } else { addr_mark_defered(rcpt); /* has no effect yet, except that mail remains in spool */ } return FALSE; } gboolean deliver_local_mda(message* msg, GList* hdr_list, address* rcpt, address* env_addr) { gboolean ok = FALSE; gchar *cmd = g_malloc(256); GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt); guint flags; DEBUG(1) debugf("attempting to deliver %s with mda\n", msg->uid); if (!expand(var_table, conf.mda, cmd, 256)) { logwrite(LOG_ALERT, "could not expand string %s\n", conf.mda); destroy_table(var_table); return FALSE; } flags = (conf.mda_fromline) ? MSGSTR_FROMLINE : 0; flags |= (conf.mda_fromhack) ? MSGSTR_FROMHACK : 0; if (pipe_out(msg, hdr_list, rcpt, cmd, flags)) { logwrite(LOG_NOTICE, "%s => %s@%s with mda (cmd = '%s')\n", msg->uid, rcpt->local_part, rcpt->domain, cmd); addr_mark_delivered(rcpt); ok = TRUE; } else if ((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)) { addr_mark_failed(rcpt); } else { addr_mark_defered(rcpt); /* has no effect yet, except that mail remains in spool */ } destroy_table(var_table); return ok; } gboolean deliver_local(msg_out * msgout) { message *msg = msgout->msg; GList *rcpt_list = msgout->rcpt_list; GList *rcpt_node; gboolean ok = FALSE, flag = FALSE, ok_fail = FALSE; DEBUG(5) debugf("deliver_local entered\n"); flag = (msg->data_list == NULL); if (flag && !spool_read_data(msg)) { logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid); return FALSE; } for (rcpt_node = g_list_first(rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) { GList *hdr_list; address *rcpt = (address *) (rcpt_node->data); address *env_addr = addr_find_ancestor(rcpt); address *ret_path = msg->return_path; header *retpath_hdr, *envto_hdr; /* we need a private copy of the hdr list because we add headers here that belong to the rcpt only. g_list_copy copies only the nodes, so it is safe to g_list_free it */ hdr_list = g_list_copy(msg->hdr_list); retpath_hdr = create_header(HEAD_ENVELOPE_TO, "Envelope-to: %s\n", addr_string(env_addr)); envto_hdr = create_header(HEAD_RETURN_PATH, "Return-path: %s\n", addr_string(ret_path)); hdr_list = g_list_prepend(hdr_list, envto_hdr); hdr_list = g_list_prepend(hdr_list, retpath_hdr); if (rcpt->local_part[0] == '|') { /* probably for expanded aliases, but why not done like with the mda? //meillo 2010-12-06 */ if (deliver_local_pipe(msg, hdr_list, rcpt, env_addr)) { ok = TRUE; } } else { /* figure out which mailbox type should be used for this user */ gchar *user = rcpt->local_part; gchar *mbox_type = conf.mbox_default; if (g_list_find_custom (conf.mbox_users, user, _g_list_strcasecmp)) { mbox_type = "mbox"; } else if (g_list_find_custom (conf.mda_users, user, _g_list_strcasecmp)) { mbox_type = "mda"; } if (strcmp(mbox_type, "mbox") == 0) { if (deliver_local_mbox(msg, hdr_list, rcpt, env_addr)) { ok = TRUE; } } else if (strcmp(mbox_type, "mda") == 0) { if (conf.mda) { if (deliver_local_mda(msg, hdr_list, rcpt, env_addr)) { ok = TRUE; } } else { logwrite(LOG_ALERT, "mbox type is mda, but no mda command given in configuration\n"); } } else { logwrite(LOG_ALERT, "unknown mbox type '%s'\n", mbox_type); } } destroy_header(retpath_hdr); destroy_header(envto_hdr); g_list_free(hdr_list); } ok_fail = delivery_failures(msg, rcpt_list, "%s (%d)", ext_strerror(errno), errno); if (flag) { msg_free_data(msg); } if (ok || ok_fail) { deliver_finish(msgout); } return ok; } /* make a list of rcpt's of a message that are local return a new copy of the list */ void msg_rcptlist_local(GList * rcpt_list, GList ** p_local_list, GList ** p_nonlocal_list) { GList *rcpt_node; foreach(rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); GList *dom_node; DEBUG(5) debugf("checking address %s\n", rcpt->address); /* search for local host list: */ foreach(conf.local_hosts, dom_node) { if (strcasecmp(dom_node->data, rcpt->domain) == 0) { *p_local_list = g_list_append(*p_local_list, rcpt); DEBUG(5) debugf("<%s@%s> is local\n", rcpt->local_part, rcpt->domain); break; } else { *p_nonlocal_list = g_list_append(*p_nonlocal_list, rcpt); } } } } gboolean deliver_msglist_host_pipe(connect_route * route, GList * msgout_list, gchar * host, GList * res_list) { gboolean ok = TRUE; GList *msgout_node; DEBUG(5) debugf("deliver_msglist_host_pipe entered\n"); if (route->pipe == NULL) { logwrite(LOG_ALERT, "no pipe command given for route (protocol is pipe!)\n"); return FALSE; } foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); gboolean flag, ok_fail = FALSE; message *msg = msgout->msg; GList *rcpt_node, *rcpt_list = msgout->rcpt_list; DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid); flag = (msg->data_list == NULL); if (flag && !spool_read_data(msg)) { logwrite(LOG_ALERT, "could not open data spool file for %s\n", msg->uid); continue; } ok = FALSE; foreach(rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); gchar *cmd = g_malloc(256); GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt); DEBUG(1) debugf("attempting to deliver %s to %s@%s with pipe\n", msg->uid, rcpt->local_part, rcpt->domain); if (!expand(var_table, route->pipe, cmd, 256)) { logwrite(LOG_ALERT, "could not expand string %s\n", route->pipe); } else { if (pipe_out(msg, msg->hdr_list, rcpt, cmd, (route->pipe_fromline ? MSGSTR_FROMLINE : 0) | (route->pipe_fromhack ? MSGSTR_FROMHACK : 0))) { logwrite(LOG_NOTICE, "%s => %s@%s with pipe (cmd = '%s')\n", msg->uid, rcpt->local_part, rcpt->domain, cmd); addr_mark_delivered(rcpt); ok = TRUE; } else { logwrite(LOG_ALERT, "pipe_out '%s' failed\n", route->pipe); if (route->connect_error_fail) { addr_mark_failed(rcpt); } else { addr_mark_defered(rcpt); } } } destroy_table(var_table); } ok_fail = delivery_failures(msg, rcpt_list, "%s", strerror(errno)); if (flag) { msg_free_data(msg); } if (ok || ok_fail) { deliver_finish(msgout); } } return ok; } /* deliver list of messages to one host and finishes them if the message was delivered to at least one rcpt. Returns TRUE if at least one msg was delivered to at least one rcpt. */ gboolean deliver_msglist_host_smtp(connect_route * route, GList * msgout_list, gchar * host, GList * res_list) { gboolean ok = FALSE; GList *msgout_node; smtp_base *psb; gint port = 25; /* paranoid check: */ if (!msgout_list) { logwrite(LOG_ALERT, "Ooops: empty list of messages in deliver_msglist_host()\n"); return FALSE; } if (!host) { /* XXX: what if mail_host isn't set? Is this possible? */ host = route->mail_host->address; port = route->mail_host->port; } if (route->wrapper) { psb = smtp_out_open_child(route->wrapper); } else { psb = smtp_out_open(host, port, res_list); } if (!psb) { /* smtp_out_open() failed */ foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); GList *rcpt_node; for (rcpt_node = g_list_first(msgout->rcpt_list); rcpt_node; rcpt_node = g_list_next(rcpt_node)) { address *rcpt = (address *) (rcpt_node->data); gboolean ret = FALSE; addr_unmark_delivered(rcpt); if (route->connect_error_fail) { addr_mark_failed(rcpt); } else { addr_mark_defered(rcpt); } if (route->wrapper) { ret = delivery_failures(msgout->msg, msgout->rcpt_list, "could not open wrapper:\n\t%s", strerror(errno)); } else { 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)); } if (ret) { deliver_finish(msgout); } } } return ok; } if (route->wrapper) { /* it seems as if the remote_host is only set for logging /* XXX: this could probably be moved into smtp_out_open_child() */ psb->remote_host = host; } set_heloname(psb, route->helo_name ? route->helo_name : conf.host_name, route->do_correct_helo); #ifdef ENABLE_AUTH if ((route->auth_name) && (route->auth_login) && (route->auth_secret)) { set_auth(psb, route->auth_name, route->auth_login, route->auth_secret); } #endif if (!smtp_out_init(psb, route->instant_helo)) { /* smtp_out_init() failed */ if ((psb->error==smtp_fail) || (psb->error==smtp_trylater) || (psb->error==smtp_syntax)) { smtp_out_quit(psb); foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); smtp_out_mark_rcpts(psb, msgout->rcpt_list); if (delivery_failures(msgout->msg, msgout->rcpt_list, "while connected with %s, the server replied\n\t%s", host, psb->buffer)) { deliver_finish(msgout); } } } destroy_smtpbase(psb); return ok; } if (!route->do_pipelining) { psb->use_pipelining = FALSE; } foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); gboolean flag, ok_msg = FALSE, ok_fail = FALSE; message *msg = msgout->msg; /* we may have to read the data at this point and remember if we did */ flag = (msg->data_list == NULL); if (flag && !spool_read_data(msg)) { logwrite(LOG_ALERT, "could not open data spool file %s\n", msg->uid); break; } smtp_out_msg(psb, msg, msgout->return_path, msgout->rcpt_list, msgout->hdr_list); ok_fail = delivery_failures(msg, msgout->rcpt_list, "while connected with %s, the server replied\n\t%s", host, psb->buffer); if ((psb->error == smtp_eof) || (psb->error == smtp_timeout)) { /* connection lost */ break; } else if (psb->error != smtp_ok) { if (g_list_next(msgout_node) && !smtp_out_rset(psb)) { break; } } ok_msg = (psb->error == smtp_ok); if (flag) { msg_free_data(msg); } if (ok_msg) { ok = TRUE; } if (ok_msg || ok_fail) { deliver_finish(msgout); } } if (psb->error == smtp_ok || (psb->error == smtp_fail) || (psb->error == smtp_trylater) || (psb->error == smtp_syntax)) { smtp_out_quit(psb); } destroy_smtpbase(psb); return ok; } gboolean deliver_msglist_host(connect_route * route, GList * msgout_list, gchar * host, GList * res_list) { DEBUG(5) debugf("protocol = %s\n", route->protocol); if (strcmp(route->protocol, "pipe") == 0) { return deliver_msglist_host_pipe(route, msgout_list, host, res_list); } else { return deliver_msglist_host_smtp(route, msgout_list, host, res_list); } } /* delivers messages in msgout_list using route */ gboolean deliver_route_msgout_list(connect_route * route, GList * msgout_list) { gboolean ok = FALSE; GList *mo_ph_list; GList *mo_ph_node; DEBUG(5) debugf("deliver_route_msgout_list entered, route->name = %s\n", route->name); if (route->mail_host) { /* this is easy... deliver everything to a smart host for relay */ return deliver_msglist_host(route, msgout_list, NULL, route->resolve_list); } /* this is not easy... */ mo_ph_list = route_msgout_list(route, msgout_list); /* okay, now we have ordered our messages by the hosts. */ if (!mo_ph_list) { return FALSE; } /* TODO: It would be nice to be able to fork for each host. We cannot do that yet because of complications with finishing the messages. Threads could be a solution because they use the same memory. But we are not thread safe yet... */ foreach(mo_ph_list, mo_ph_node) { msgout_perhost *mo_ph = (msgout_perhost *) (mo_ph_node->data); if (deliver_msglist_host(route, mo_ph->msgout_list, mo_ph->host, route->resolve_list)) { ok = TRUE; } destroy_msgout_perhost(mo_ph); } g_list_free(mo_ph_list); return ok; } /* calls route_prepare_msg() delivers messages in msg_list using route by calling deliver_route_msgout_list() */ gboolean deliver_route_msg_list(connect_route * route, GList * msgout_list) { GList *msgout_list_deliver = NULL; GList *msgout_node; gboolean ok = TRUE; DEBUG(6) debugf("deliver_route_msg_list()\n"); foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); msg_out *msgout_cloned = clone_msg_out(msgout); GList *rcpt_list_non_delivered = NULL; GList *rcpt_node; /* we have to delete already delivered rcpt's because a previous route may have delivered to it */ foreach(msgout_cloned->rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); /* failed addresses already have been bounced; there should be a better way to handle those. */ if (!addr_is_delivered(rcpt) && !addr_is_failed(rcpt) && !(rcpt->flags & ADDR_FLAG_LAST_ROUTE)) { rcpt_list_non_delivered = g_list_append(rcpt_list_non_delivered, rcpt); } } g_list_free(msgout_cloned->rcpt_list); msgout_cloned->rcpt_list = rcpt_list_non_delivered; if (!msgout_cloned->rcpt_list) { destroy_msg_out(msgout_cloned); continue; } if (!route_is_allowed_mail_local(route, msgout->msg->return_path) || !route_is_allowed_return_path(route, msgout->msg-> return_path)) { destroy_msg_out(msgout_cloned); continue; } GList *rcpt_list_allowed = NULL, *rcpt_list_notallowed = NULL; msg_rcptlist_route(route, msgout_cloned->rcpt_list, &rcpt_list_allowed, &rcpt_list_notallowed); if (!rcpt_list_allowed) { destroy_msg_out(msgout_cloned); continue; } logwrite(LOG_NOTICE, "%s using '%s'\n", msgout->msg->uid, route->name); g_list_free(msgout_cloned->rcpt_list); msgout_cloned->rcpt_list = rcpt_list_allowed; if (route->last_route) { GList *rcpt_node; foreach(msgout_cloned->rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); rcpt->flags |= ADDR_FLAG_LAST_ROUTE; } } route_prepare_msgout(route, msgout_cloned); msgout_list_deliver = g_list_append(msgout_list_deliver, msgout_cloned); } if (msgout_list_deliver) { if (deliver_route_msgout_list(route, msgout_list_deliver)) { ok = TRUE; } destroy_msg_out_list(msgout_list_deliver); } return ok; } /* copy pointers of delivered addresses to the msg's non_rcpt_list, to make sure that they will not be delivered again. */ void update_non_rcpt_list(msg_out * msgout) { GList *rcpt_node; message *msg = msgout->msg; foreach(msgout->rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); if (addr_is_delivered(rcpt) || addr_is_failed(rcpt)) { msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, rcpt); } } } /* after delivery attempts, we check if there are any rcpt addresses left in the message. If all addresses have been completed, the spool files will be deleted, otherwise the header spool will be written back. We never changed the data spool, so there is no need to write that back. returns TRUE if all went well. */ gboolean deliver_finish(msg_out * msgout) { GList *rcpt_node; message *msg = msgout->msg; gboolean finished = TRUE; update_non_rcpt_list(msgout); /* we NEVER made copies of the addresses, flags affecting addresses were always set on the original address structs */ foreach(msg->rcpt_list, rcpt_node) { address *rcpt = (address *) (rcpt_node->data); if (!addr_is_finished_children(rcpt)) { finished = FALSE; } else { /* if ALL children have been delivered, mark parent as delivered. if there is one or more not delivered, it must have failed, we mark the parent as failed as well. */ if (addr_is_delivered_children(rcpt)) { addr_mark_delivered(rcpt); } else { addr_mark_failed(rcpt); } } } if (finished) { if (spool_delete_all(msg)) { logwrite(LOG_NOTICE, "%s completed.\n", msg->uid); return TRUE; } return FALSE; } /* one not delivered address was found */ if (!spool_write(msg, FALSE)) { logwrite(LOG_ALERT, "could not write back spool header for %s\n", msg->uid); return FALSE; } DEBUG(2) debugf("spool header for %s written back.\n", msg->uid); return TRUE; } gboolean deliver_finish_list(GList * msgout_list) { gboolean ok = TRUE; GList *msgout_node; foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); if (!deliver_finish(msgout)) { ok = FALSE; } } return ok; } gboolean deliver_msgout_list_online(GList * msgout_list) { GList *rf_list = NULL; gchar *connect_name = NULL; gboolean ok = FALSE; connect_name = detect_online(); if (!connect_name) { return FALSE; } /* we are online! */ logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name); rf_list = (GList *) table_find(conf.connect_routes, connect_name); if (!rf_list) { logwrite(LOG_ALERT, "route list with name '%s' not found.\n", connect_name); return FALSE; } GList *route_list = read_route_list(rf_list, FALSE); if (!route_list) { logwrite(LOG_ALERT, "could not read route list '%s'\n", connect_name); return FALSE; } GList *route_node; foreach(route_list, route_node) { connect_route *route = (connect_route *) (route_node->data); /* TODO: ok gets overwritten */ ok = deliver_route_msg_list(route, msgout_list); } destroy_route_list(route_list); return ok; } gboolean deliver_msg_list(GList * msg_list, guint flags) { GList *msgout_list = create_msg_out_list(msg_list); GList *local_msgout_list = NULL; GList *localnet_msgout_list = NULL; GList *other_msgout_list = NULL; GList *msgout_node; GList *alias_table = NULL; gboolean ok = TRUE; if (conf.alias_file) { alias_table = table_read(conf.alias_file, ':'); } /* sort messages for different deliveries */ foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); GList *rcpt_list; GList *local_rcpt_list = NULL; GList *localnet_rcpt_list = NULL; GList *other_rcpt_list = NULL; if (!spool_lock(msgout->msg->uid)) { DEBUG(5) debugf("spool_lock(%s) failed.\n", msgout->msg->uid); continue; } DEBUG(5) debugf("spool_lock(%s)\n", msgout->msg->uid); rcpt_list = g_list_copy(msgout->msg->rcpt_list); if (conf.log_user) { address *addr = create_address_qualified(conf.log_user, TRUE, conf.host_name); if (addr) { rcpt_list = g_list_prepend(rcpt_list, addr); } else { logwrite(LOG_ALERT, "invalid log_user address `%s', ignoring\n", conf.log_user); } } if (alias_table) { GList *aliased_rcpt_list; aliased_rcpt_list = alias_expand(alias_table, rcpt_list, msgout->msg->non_rcpt_list); g_list_free(rcpt_list); rcpt_list = aliased_rcpt_list; } split_rcpts(rcpt_list, conf.local_nets, &local_rcpt_list, &localnet_rcpt_list, &other_rcpt_list); g_list_free(rcpt_list); /* local recipients */ if ((flags & DLVR_LOCAL) && local_rcpt_list) { msg_out *local_msgout = clone_msg_out(msgout); local_msgout->rcpt_list = local_rcpt_list; local_msgout_list = g_list_append(local_msgout_list, local_msgout); } /* local net recipients */ if ((flags & DLVR_LAN) && localnet_rcpt_list) { msg_out *localnet_msgout = clone_msg_out(msgout); localnet_msgout->rcpt_list = localnet_rcpt_list; localnet_msgout_list = g_list_append(localnet_msgout_list, localnet_msgout); } /* remote recipients (the rest), requires online delivery */ if ((flags & DLVR_ONLINE) && other_rcpt_list) { msg_out *other_msgout = clone_msg_out(msgout); other_msgout->rcpt_list = other_rcpt_list; other_msgout_list = g_list_append(other_msgout_list, other_msgout); } } if (alias_table) { destroy_table(alias_table); } /* actual delivery */ if (local_msgout_list) { DEBUG(5) debugf("local_msgout_list\n"); foreach(local_msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); if (!deliver_local(msgout)) { ok = FALSE; } } destroy_msg_out_list(local_msgout_list); } if (localnet_msgout_list) { GList *route_list = NULL; GList *route_node; DEBUG(5) debugf("localnet_msgout_list\n"); if (conf.local_net_routes) { route_list = read_route_list(conf.local_net_routes, TRUE); } else { route_list = g_list_append(NULL, create_local_route()); } foreach(route_list, route_node) { connect_route *route = (connect_route *) (route_node->data); if (!deliver_route_msg_list(route, localnet_msgout_list)) { ok = FALSE; } } destroy_msg_out_list(localnet_msgout_list); destroy_route_list(route_list); } if (other_msgout_list) { DEBUG(5) debugf("other_msgout_list\n"); if (!deliver_msgout_list_online(other_msgout_list)) { ok = FALSE; } destroy_msg_out_list(other_msgout_list); } foreach(msgout_list, msgout_node) { msg_out *msgout = (msg_out *) (msgout_node->data); if (spool_unlock(msgout->msg->uid)) { DEBUG(5) debugf("spool_unlock(%s)\n", msgout->msg->uid); } else { DEBUG(5) debugf("spool_unlock(%s) failed.\n", msgout->msg->uid); } } destroy_msg_out_list(msgout_list); return ok; } /* This function searches in the list of rcpt addresses for local and 'local net' addresses. Remote addresses which are reachable only when online are treated specially in another function. deliver() is called when a message has just been received and should be delivered immediately. */ gboolean deliver(message * msg) { gboolean ok; GList *msg_list = g_list_append(NULL, msg); ok = deliver_msg_list(msg_list, DLVR_ALL); g_list_free(msg_list); return ok; }