masqmail

annotate src/deliver.c @ 222:8cddc65765bd

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