masqmail-0.2

annotate src/deliver.c @ 171:349518b940db

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