masqmail

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