masqmail

annotate src/deliver.c @ 75:257a9e6d1a8e

fixed correct processing of mails with data lines longer 4096 chars Mail messages with lines longer than 4096 chars were already read correctly, i.e. the spool files were correct. This commit fixes the reading of spool files with long lines. The old behavior was that the message body was truncated right before the first line longer 4096 chars. The number comes from MAX_DATALINE.
author meillo@marmaro.de
date Wed, 16 Jun 2010 19:06:34 +0200
parents 6c59dedd06be
children 3b344bf57162
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2002 Oliver Kurth
meillo@19 3 Copyright 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@10 356 if (smtp_out_init(psb)) {
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@10 696 if (!spool_lock(msgout->msg->uid))
meillo@10 697 continue;
meillo@0 698
meillo@10 699 rcpt_list = g_list_copy(msgout->msg->rcpt_list);
meillo@10 700 if (conf.log_user) {
meillo@10 701 address *addr = create_address_qualified(conf.log_user, TRUE, conf.host_name);
meillo@10 702 if (addr)
meillo@10 703 rcpt_list = g_list_prepend(rcpt_list, addr);
meillo@10 704 }
meillo@10 705 if (alias_table) {
meillo@10 706 GList *aliased_rcpt_list;
meillo@10 707 aliased_rcpt_list = alias_expand(alias_table, rcpt_list, msgout->msg->non_rcpt_list);
meillo@10 708 g_list_free(rcpt_list);
meillo@10 709 rcpt_list = aliased_rcpt_list;
meillo@10 710 }
meillo@0 711
meillo@10 712 /* local recipients */
meillo@10 713 other_rcpt_list = NULL;
meillo@10 714 rcptlist_with_addr_is_local(rcpt_list, &local_rcpt_list, &other_rcpt_list);
meillo@0 715
meillo@10 716 if (flags & DLVR_LOCAL) {
meillo@10 717 if (local_rcpt_list != NULL) {
meillo@10 718 msg_out *local_msgout = clone_msg_out(msgout);
meillo@10 719 local_msgout->rcpt_list = local_rcpt_list;
meillo@10 720 local_msgout_list = g_list_append(local_msgout_list, local_msgout);
meillo@10 721 }
meillo@10 722 }
meillo@0 723
meillo@10 724 g_list_free(rcpt_list);
meillo@0 725
meillo@10 726 /* local net recipients */
meillo@10 727 rcpt_list = other_rcpt_list;
meillo@10 728 other_rcpt_list = NULL;
meillo@10 729 rcptlist_with_one_of_hostlist(rcpt_list, conf.local_nets, &localnet_rcpt_list, &other_rcpt_list);
meillo@0 730
meillo@10 731 if (flags & DLVR_LAN) {
meillo@10 732 if (localnet_rcpt_list != NULL) {
meillo@10 733 msg_out *localnet_msgout = clone_msg_out(msgout);
meillo@10 734 localnet_msgout->rcpt_list = localnet_rcpt_list;
meillo@10 735 localnet_msgout_list = g_list_append(localnet_msgout_list, localnet_msgout);
meillo@10 736 }
meillo@10 737 }
meillo@0 738
meillo@10 739 if (flags & DLVR_ONLINE) {
meillo@10 740 /* the rest, this is online delivery */
meillo@10 741 if (other_rcpt_list != NULL) {
meillo@10 742 msg_out *other_msgout = clone_msg_out(msgout);
meillo@10 743 other_msgout->rcpt_list = other_rcpt_list;
meillo@10 744 other_msgout_list = g_list_append(other_msgout_list, other_msgout);
meillo@10 745 }
meillo@10 746 }
meillo@10 747 }
meillo@0 748
meillo@10 749 if (alias_table)
meillo@10 750 destroy_table(alias_table);
meillo@0 751
meillo@10 752 /* actual delivery */
meillo@10 753 if (local_msgout_list != NULL) {
meillo@10 754 foreach(local_msgout_list, msgout_node) {
meillo@10 755 msg_out *msgout = (msg_out *) (msgout_node->data);
meillo@10 756 if (!deliver_local(msgout))
meillo@10 757 ok = FALSE;
meillo@10 758 }
meillo@10 759 destroy_msg_out_list(local_msgout_list);
meillo@10 760 }
meillo@0 761
meillo@10 762 if (localnet_msgout_list != NULL) {
meillo@10 763 GList *route_list = NULL;
meillo@10 764 GList *route_node;
meillo@0 765
meillo@10 766 if (conf.local_net_routes)
meillo@10 767 route_list = read_route_list(conf.local_net_routes, TRUE);
meillo@10 768 else
meillo@10 769 route_list = g_list_append(NULL, create_local_route());
meillo@0 770
meillo@10 771 foreach(route_list, route_node) {
meillo@10 772 connect_route *route = (connect_route *) (route_node->data);
meillo@10 773 if (!deliver_route_msg_list(route, localnet_msgout_list))
meillo@10 774 ok = FALSE;
meillo@10 775 }
meillo@10 776 destroy_msg_out_list(localnet_msgout_list);
meillo@10 777 destroy_route_list(route_list);
meillo@10 778 }
meillo@10 779
meillo@10 780 if (other_msgout_list != NULL) {
meillo@10 781 if (!deliver_msgout_list_online(other_msgout_list))
meillo@10 782 ok = FALSE;
meillo@10 783 destroy_msg_out_list(other_msgout_list);
meillo@10 784 }
meillo@10 785
meillo@10 786 foreach(msgout_list, msgout_node) {
meillo@10 787 msg_out *msgout = (msg_out *) (msgout_node->data);
meillo@10 788 spool_unlock(msgout->msg->uid);
meillo@10 789 }
meillo@10 790
meillo@10 791 destroy_msg_out_list(msgout_list);
meillo@10 792
meillo@10 793 return ok;
meillo@0 794 }
meillo@0 795
meillo@0 796 /* This function searches in the list of rcpt addresses
meillo@0 797 for local and 'local net' addresses. Remote addresses
meillo@0 798 which are reachable only when online are treated specially
meillo@0 799 in another function.
meillo@0 800
meillo@0 801 deliver() is called when a message has just been received and should
meillo@0 802 be delivered immediately.
meillo@0 803 */
meillo@10 804 gboolean
meillo@10 805 deliver(message * msg)
meillo@0 806 {
meillo@10 807 gboolean ok;
meillo@10 808 GList *msg_list = g_list_append(NULL, msg);
meillo@0 809
meillo@10 810 ok = deliver_msg_list(msg_list, DLVR_ALL);
meillo@10 811 g_list_free(msg_list);
meillo@0 812
meillo@10 813 return ok;
meillo@0 814 }