masqmail-0.2

annotate src/deliver.c @ 0:08114f7dcc23

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