masqmail

diff 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
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/deliver.c	Fri Sep 26 17:05:23 2008 +0200
     1.3 @@ -0,0 +1,849 @@
     1.4 +/*  MasqMail
     1.5 +    Copyright (C) 1999-2002 Oliver Kurth
     1.6 +
     1.7 +    This program is free software; you can redistribute it and/or modify
     1.8 +    it under the terms of the GNU General Public License as published by
     1.9 +    the Free Software Foundation; either version 2 of the License, or
    1.10 +    (at your option) any later version.
    1.11 +
    1.12 +    This program is distributed in the hope that it will be useful,
    1.13 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.15 +    GNU General Public License for more details.
    1.16 +
    1.17 +    You should have received a copy of the GNU General Public License
    1.18 +    along with this program; if not, write to the Free Software
    1.19 +    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    1.20 +*/
    1.21 +
    1.22 +#include "masqmail.h"
    1.23 +#include "smtp_out.h"
    1.24 +#include <fnmatch.h>
    1.25 +#include <sysexits.h>
    1.26 +#include <netdb.h>
    1.27 +
    1.28 +/* collect failed/defered rcpts for failure/warning messages */
    1.29 +/* returns TRUE if either there are no failures or a
    1.30 +   failure message has been successfully sent */
    1.31 +gboolean delivery_failures(message *msg, GList *rcpt_list, gchar *err_fmt, ...)
    1.32 +{
    1.33 +  gboolean ok_fail = TRUE, ok_warn = TRUE;
    1.34 +  time_t now = time(NULL);
    1.35 +
    1.36 +  GList *failed_list = NULL, *defered_list = NULL, *rcpt_node;
    1.37 +  va_list args;
    1.38 +  va_start(args, err_fmt);
    1.39 +
    1.40 +  foreach(rcpt_list, rcpt_node){
    1.41 +    address *rcpt = (address *)(rcpt_node->data);
    1.42 +    
    1.43 +    if(addr_is_defered(rcpt)){
    1.44 +      if((now - msg->received_time) >= conf.max_defer_time){
    1.45 +	addr_mark_failed(rcpt);
    1.46 +      }else
    1.47 +	defered_list = g_list_prepend(defered_list, rcpt);
    1.48 +    }
    1.49 +    if(addr_is_failed(rcpt))
    1.50 +      failed_list = g_list_prepend(failed_list, rcpt);
    1.51 +  }
    1.52 +  if(failed_list != NULL){
    1.53 +    ok_fail = fail_msg(msg, conf.errmsg_file, failed_list, err_fmt, args);
    1.54 +    g_list_free(failed_list);
    1.55 +  }
    1.56 +  if(defered_list != NULL){
    1.57 +    ok_warn = warn_msg(msg, conf.warnmsg_file, defered_list, err_fmt, args);
    1.58 +    g_list_free(defered_list);
    1.59 +  }
    1.60 +  va_end(args);
    1.61 +  return ok_fail && ok_warn;
    1.62 +}
    1.63 +
    1.64 +static gint _g_list_strcasecmp(gconstpointer a, gconstpointer b)
    1.65 +{
    1.66 +  return (gint)strcasecmp(a, b);
    1.67 +}
    1.68 +
    1.69 +gboolean deliver_local(msg_out *msgout)
    1.70 +{
    1.71 +  message *msg = msgout->msg;
    1.72 +  GList *rcpt_list = msgout->rcpt_list;
    1.73 +  GList *rcpt_node;
    1.74 +  gboolean ok = TRUE, flag = FALSE, ok_fail = FALSE;
    1.75 +
    1.76 +  DEBUG(5) debugf("deliver_local entered\n");
    1.77 +
    1.78 +  flag = (msg->data_list == NULL);
    1.79 +  if(flag){
    1.80 +    if(!(ok = spool_read_data(msg))){
    1.81 +      logwrite(LOG_ALERT, "could not open data spool file for %s\n",
    1.82 +	       msg->uid);
    1.83 +    }
    1.84 +  }
    1.85 +  if(!ok) return FALSE;
    1.86 +
    1.87 +  ok = FALSE;
    1.88 +  for(rcpt_node = g_list_first(rcpt_list);
    1.89 +      rcpt_node;
    1.90 +      rcpt_node = g_list_next(rcpt_node)){
    1.91 +    GList *hdr_list;
    1.92 +    address *rcpt = (address *)(rcpt_node->data);
    1.93 +    address *env_addr = addr_find_ancestor(rcpt);
    1.94 +    address *ret_path = msg->return_path;
    1.95 +    header *retpath_hdr, *envto_hdr;
    1.96 +
    1.97 +    /* we need a private copy of the hdr list because we add headers here
    1.98 +       that belong to the rcpt only.
    1.99 +       g_list_copy copies only the nodes, so it is safe to
   1.100 +       g_list_free it
   1.101 +    */
   1.102 +    hdr_list = g_list_copy(msg->hdr_list);
   1.103 +    retpath_hdr = create_header(HEAD_ENVELOPE_TO,
   1.104 +				"Envelope-to: %s\n", addr_string(env_addr));
   1.105 +    envto_hdr = create_header(HEAD_RETURN_PATH,
   1.106 +			      "Return-path: %s\n", addr_string(ret_path));
   1.107 +    
   1.108 +    hdr_list = g_list_prepend(hdr_list, envto_hdr);
   1.109 +    hdr_list = g_list_prepend(hdr_list, retpath_hdr);
   1.110 +
   1.111 +    if(rcpt->local_part[0] == '|'){
   1.112 +      DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
   1.113 +      if(pipe_out(msg, hdr_list, rcpt, &(rcpt->local_part[1]),
   1.114 +		  (conf.pipe_fromline ? MSGSTR_FROMLINE : 0) |
   1.115 +		  (conf.pipe_fromhack ? MSGSTR_FROMHACK : 0))){
   1.116 +	logwrite(LOG_NOTICE, "%s => %s <%s@%s> with pipe\n",
   1.117 +		 msg->uid, rcpt->local_part,
   1.118 +		 env_addr->local_part, env_addr->domain
   1.119 +		 );
   1.120 +	addr_mark_delivered(rcpt);
   1.121 +	ok = TRUE;
   1.122 +      }else{
   1.123 +	if((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)){
   1.124 +	  addr_mark_failed(rcpt);
   1.125 +	}else{
   1.126 +	  addr_mark_defered(rcpt); /* has no effect yet,
   1.127 +				      except that mail remains in spool */
   1.128 +	}
   1.129 +      }
   1.130 +    }else{
   1.131 +      /* figure out which mailbox type should be used for this user */
   1.132 +      gchar *user = rcpt->local_part;
   1.133 +      gchar *mbox_type = conf.mbox_default;
   1.134 +      
   1.135 +      if(g_list_find_custom(conf.mbox_users, user, _g_list_strcasecmp) != NULL)
   1.136 +	mbox_type = "mbox";
   1.137 +      else if(g_list_find_custom(conf.mda_users, user, _g_list_strcasecmp) != NULL)
   1.138 +	mbox_type = "mda";
   1.139 +      else if(g_list_find_custom(conf.maildir_users, user, _g_list_strcasecmp) != NULL)
   1.140 +	mbox_type = "maildir";
   1.141 +
   1.142 +      if(strcmp(mbox_type, "mbox") == 0){
   1.143 +	DEBUG(1) debugf("attempting to deliver %s with mbox\n", msg->uid);
   1.144 +	if(append_file(msg, hdr_list, rcpt->local_part)){
   1.145 +	  if(env_addr != rcpt){
   1.146 +	    logwrite(LOG_NOTICE, "%s => %s@%s <%s@%s> with mbox\n",
   1.147 +		     msg->uid, rcpt->local_part, rcpt->domain,
   1.148 +		     env_addr->local_part, env_addr->domain
   1.149 +		     );
   1.150 +	  }else{
   1.151 +	    logwrite(LOG_NOTICE, "%s => <%s@%s> with mbox\n",
   1.152 +		     msg->uid, rcpt->local_part, rcpt->domain);
   1.153 +	  }
   1.154 +	  addr_mark_delivered(rcpt);
   1.155 +	  ok = TRUE;
   1.156 +	}else{
   1.157 +	  if(errno != EAGAIN){ /* prevents 'Resource temporarily unavailable (11)' */
   1.158 +	    addr_mark_failed(rcpt);
   1.159 +	  }else{
   1.160 +	    addr_mark_defered(rcpt);
   1.161 +	  }
   1.162 +	}
   1.163 +
   1.164 +      }else if(strcmp(mbox_type, "mda") == 0){
   1.165 +	if(conf.mda){
   1.166 +	  gchar *cmd = g_malloc(256);
   1.167 +	  GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
   1.168 +	  
   1.169 +	  DEBUG(1) debugf("attempting to deliver %s with mda\n", msg->uid);
   1.170 +	  
   1.171 +	  if(expand(var_table, conf.mda, cmd, 256)){
   1.172 +	  
   1.173 +	    if(pipe_out(msg, hdr_list, rcpt, cmd,
   1.174 +			(conf.mda_fromline ? MSGSTR_FROMLINE : 0) |
   1.175 +			(conf.mda_fromhack ? MSGSTR_FROMHACK : 0))){
   1.176 +	      logwrite(LOG_NOTICE, "%s => %s@%s with mda (cmd = '%s')\n",
   1.177 +		       msg->uid, rcpt->local_part, rcpt->domain, cmd
   1.178 +		       );
   1.179 +	      addr_mark_delivered(rcpt);
   1.180 +	      ok = TRUE;
   1.181 +	    }else{
   1.182 +	      if((errno != (1024 + EX_TEMPFAIL)) && (errno != EAGAIN)){
   1.183 +		addr_mark_failed(rcpt);
   1.184 +	      }else{
   1.185 +		addr_mark_defered(rcpt); /* has no effect yet,
   1.186 +					    except that mail remains in spool */
   1.187 +	      }
   1.188 +	    }
   1.189 +	  }else
   1.190 +	    logwrite(LOG_ALERT, "could not expand string %s\n", conf.mda);
   1.191 +	  
   1.192 +	  destroy_table(var_table);
   1.193 +	}else
   1.194 +	  logwrite(LOG_ALERT, "mbox type is mda, but no mda command given in configuration\n");
   1.195 +
   1.196 +#ifdef ENABLE_MAILDIR
   1.197 +      }else if(strcmp(mbox_type, "maildir") == 0){
   1.198 +	DEBUG(1) debugf("attempting to deliver %s with maildir\n", msg->uid);
   1.199 +	if(maildir_out(msg, hdr_list, rcpt->local_part, 0)){
   1.200 +	  if(env_addr != rcpt){
   1.201 +	    logwrite(LOG_NOTICE, "%s => %s@%s <%s@%s> with local\n",
   1.202 +		     msg->uid, rcpt->local_part, rcpt->domain,
   1.203 +		     env_addr->local_part, env_addr->domain
   1.204 +		     );
   1.205 +	  }else{
   1.206 +	    logwrite(LOG_NOTICE, "%s => <%s@%s> with maildir\n",
   1.207 +		     msg->uid, rcpt->local_part, rcpt->domain);
   1.208 +	  }
   1.209 +	  addr_mark_delivered(rcpt);
   1.210 +	  ok = TRUE;
   1.211 +	}else
   1.212 +	  addr_mark_failed(rcpt);
   1.213 +#endif
   1.214 +      }else
   1.215 +	logwrite(LOG_ALERT, "unknown mbox type '%s'\n", mbox_type);
   1.216 +    }
   1.217 +
   1.218 +    destroy_header(retpath_hdr);
   1.219 +    destroy_header(envto_hdr);
   1.220 +
   1.221 +    g_list_free(hdr_list);
   1.222 +  }
   1.223 +  ok_fail = delivery_failures(msg, rcpt_list, "%s (%d)", ext_strerror(errno), errno);
   1.224 +
   1.225 +  if(flag) msg_free_data(msg);
   1.226 +  if(ok || ok_fail) deliver_finish(msgout);
   1.227 +
   1.228 +  return ok;
   1.229 +}
   1.230 +
   1.231 +/* make a list of rcpt's of a message that are local
   1.232 +   return a new copy of the list
   1.233 +*/
   1.234 +void msg_rcptlist_local(GList *rcpt_list, GList **p_local_list, GList **p_nonlocal_list)
   1.235 +{
   1.236 +  GList *rcpt_node;
   1.237 +
   1.238 +  foreach(rcpt_list, rcpt_node){
   1.239 +    address *rcpt = (address *)(rcpt_node->data);
   1.240 +    GList *dom_node;
   1.241 +
   1.242 +    DEBUG(5) debugf("checking address %s\n", rcpt->address);
   1.243 +
   1.244 +    /* search for local host list: */
   1.245 +    foreach(conf.local_hosts, dom_node){
   1.246 +      if(strcasecmp(dom_node->data, rcpt->domain) == 0){
   1.247 +	*p_local_list = g_list_append(*p_local_list, rcpt);
   1.248 +	DEBUG(5) debugf("<%s@%s> is local\n", rcpt->local_part, rcpt->domain);
   1.249 +	break;
   1.250 +      }else{
   1.251 +	*p_nonlocal_list = g_list_append(*p_nonlocal_list, rcpt);
   1.252 +      }
   1.253 +    }
   1.254 +  }
   1.255 +}
   1.256 +
   1.257 +gboolean deliver_msglist_host_pipe(connect_route *route, GList *msgout_list, gchar *host, GList *res_list)
   1.258 +{
   1.259 +  gboolean ok = TRUE;
   1.260 +  GList *msgout_node;
   1.261 +
   1.262 +  DEBUG(5) debugf("deliver_msglist_host_pipe entered\n");
   1.263 +
   1.264 +  if(route->pipe == NULL){
   1.265 +    logwrite(LOG_ALERT, "no pipe command given for route (protocol is pipe!)\n");
   1.266 +    return FALSE;
   1.267 +  }
   1.268 +
   1.269 +  foreach(msgout_list, msgout_node){
   1.270 +    msg_out *msgout = (msg_out *)(msgout_node->data);
   1.271 +    gboolean flag, ok_msg = TRUE, ok_fail = FALSE;
   1.272 +    message *msg = msgout->msg;
   1.273 +    GList *rcpt_node, *rcpt_list = msgout->rcpt_list;
   1.274 +
   1.275 +    DEBUG(1) debugf("attempting to deliver %s with pipe\n", msg->uid);
   1.276 +
   1.277 +    flag = (msg->data_list == NULL);
   1.278 +    if(flag){
   1.279 +      if(!(ok_msg = spool_read_data(msg))){
   1.280 +	logwrite(LOG_ALERT, "could not open data spool file for %s\n",
   1.281 +		 msg->uid);
   1.282 +      }
   1.283 +    }
   1.284 +    if(!ok_msg) continue;
   1.285 +
   1.286 +    ok = FALSE;
   1.287 +    foreach(rcpt_list, rcpt_node){
   1.288 +      address *rcpt = (address *)(rcpt_node->data);
   1.289 +      gchar *cmd = g_malloc(256);
   1.290 +      GList *var_table = var_table_rcpt(var_table_msg(NULL, msg), rcpt);
   1.291 +      
   1.292 +      DEBUG(1) debugf("attempting to deliver %s to %s@%s with pipe\n",
   1.293 +		      msg->uid, rcpt->local_part, rcpt->domain);
   1.294 +      
   1.295 +      if(expand(var_table, route->pipe, cmd, 256)){
   1.296 +	
   1.297 +	if(pipe_out(msg, msg->hdr_list, rcpt, cmd,
   1.298 +		    (route->pipe_fromline ? MSGSTR_FROMLINE : 0) |
   1.299 +		    (route->pipe_fromhack ? MSGSTR_FROMHACK : 0))){
   1.300 +	  logwrite(LOG_NOTICE, "%s => %s@%s with pipe (cmd = '%s')\n",
   1.301 +		   msg->uid, rcpt->local_part, rcpt->domain, cmd
   1.302 +		   );
   1.303 +	  addr_mark_delivered(rcpt);
   1.304 +	  ok = TRUE;
   1.305 +	}else{
   1.306 +	  logwrite(LOG_ALERT, "pipe_out '%s' failed\n", route->pipe);
   1.307 +
   1.308 +	  if(route->connect_error_fail){
   1.309 +	    addr_mark_failed(rcpt);
   1.310 +	  }else{
   1.311 +	    addr_mark_defered(rcpt);
   1.312 +	  }
   1.313 +	}
   1.314 +      }else
   1.315 +	logwrite(LOG_ALERT, "could not expand string %s\n", route->pipe);
   1.316 +      
   1.317 +      destroy_table(var_table);
   1.318 +    }
   1.319 +    ok_fail = delivery_failures(msg, rcpt_list, "%s", strerror(errno));
   1.320 +
   1.321 +    if(flag) msg_free_data(msg);
   1.322 +
   1.323 +    if(ok || ok_fail) deliver_finish(msgout);
   1.324 +  }
   1.325 +
   1.326 +  return ok;
   1.327 +}
   1.328 +
   1.329 +/* deliver list of messages to one host
   1.330 +   and finishes them if the message was delivered to at least one
   1.331 +   rcpt.
   1.332 +   Returns TRUE if at least one msg was delivered to at least one
   1.333 +   rcpt.
   1.334 +*/
   1.335 +
   1.336 +gboolean deliver_msglist_host_smtp(connect_route *route, GList *msgout_list, gchar *host, GList *res_list)
   1.337 +{
   1.338 +  gboolean ok = FALSE;
   1.339 +  GList *msgout_node;
   1.340 +  smtp_base *psb;
   1.341 +  gint port;
   1.342 +  
   1.343 +  /* paranoid check: */
   1.344 +  if(msgout_list == NULL){
   1.345 +    logwrite(LOG_ALERT,
   1.346 +	     "Ooops: empty list of messages in deliver_msglist_host()\n");
   1.347 +    return FALSE;
   1.348 +  }
   1.349 +
   1.350 +  if(host == NULL){
   1.351 +    host = route->mail_host->address;
   1.352 +    port = route->mail_host->port;
   1.353 +  }else
   1.354 +    port = conf.remote_port;
   1.355 +    
   1.356 +#ifdef ENABLE_POP3
   1.357 +  if(route->pop3_login){
   1.358 +    if(!(pop_before_smtp(route->pop3_login)))
   1.359 +      return FALSE;
   1.360 +  }
   1.361 +#endif
   1.362 +
   1.363 +  if((psb = (route->wrapper ?
   1.364 +	     smtp_out_open_child(route->wrapper) :
   1.365 +	     smtp_out_open(host, port, res_list)))){
   1.366 +
   1.367 +    if(route->wrapper) psb->remote_host = host;
   1.368 +
   1.369 +    set_heloname(psb,
   1.370 +		 route->helo_name ? route->helo_name : conf.host_name,
   1.371 +		 route->do_correct_helo);
   1.372 +
   1.373 +#ifdef ENABLE_AUTH
   1.374 +    if((route->auth_name) && (route->auth_login) && (route->auth_secret))
   1.375 +      set_auth(psb, route->auth_name, route->auth_login, route->auth_secret);
   1.376 +#endif
   1.377 +    if(smtp_out_init(psb)){
   1.378 +
   1.379 +      if(!route->do_pipelining) psb->use_pipelining = FALSE;
   1.380 +
   1.381 +      foreach(msgout_list, msgout_node){
   1.382 +	msg_out *msgout = (msg_out *)(msgout_node->data);
   1.383 +	gboolean flag, ok_msg = FALSE, ok_fail = FALSE;
   1.384 +	message *msg = msgout->msg;
   1.385 +
   1.386 +	/* we may have to read the data at this point
   1.387 +	   and remember if we did */
   1.388 +	flag = (msg->data_list == NULL);
   1.389 +	if(flag){
   1.390 +	  if(!spool_read_data(msg)){
   1.391 +	    logwrite(LOG_ALERT, "could not open data spool file %s\n",
   1.392 +		     msg->uid);
   1.393 +	    break;
   1.394 +	  }
   1.395 +	}
   1.396 +    
   1.397 +	smtp_out_msg(psb, msg,
   1.398 +		     msgout->return_path, msgout->rcpt_list, msgout->hdr_list);
   1.399 +
   1.400 +	ok_fail = delivery_failures(msg, msgout->rcpt_list, 
   1.401 +				    "while connected with %s, the server replied\n\t%s",
   1.402 +				    host, psb->buffer);
   1.403 +
   1.404 +	if((psb->error == smtp_eof) ||
   1.405 +	   (psb->error == smtp_timeout)){
   1.406 +	  /* connection lost */
   1.407 +	  break;
   1.408 +	}
   1.409 +	else if(psb->error != smtp_ok){
   1.410 +	  if(g_list_next(msgout_node) != NULL)
   1.411 +	    if(!smtp_out_rset(psb))
   1.412 +	      break;
   1.413 +	}
   1.414 +	ok_msg = (psb->error == smtp_ok);
   1.415 +
   1.416 +	if(flag) msg_free_data(msg);
   1.417 +	if(ok_msg) ok = TRUE;
   1.418 +	if(ok_msg || ok_fail){
   1.419 +	  deliver_finish(msgout);
   1.420 +	}
   1.421 +      }
   1.422 +      if(psb->error == smtp_ok ||
   1.423 +	 (psb->error == smtp_fail) ||
   1.424 +	 (psb->error == smtp_trylater) ||
   1.425 +	 (psb->error == smtp_syntax)){
   1.426 +
   1.427 +	smtp_out_quit(psb);
   1.428 +      }
   1.429 +    }else{
   1.430 +      /* smtp_out_init() failed */
   1.431 +      if((psb->error == smtp_fail) ||
   1.432 +	 (psb->error == smtp_trylater) ||
   1.433 +	 (psb->error == smtp_syntax)){
   1.434 +	smtp_out_quit(psb);
   1.435 +
   1.436 +	foreach(msgout_list, msgout_node){
   1.437 +	  msg_out *msgout = (msg_out *)(msgout_node->data);
   1.438 +	  smtp_out_mark_rcpts(psb, msgout->rcpt_list);
   1.439 +
   1.440 +	  if(delivery_failures(msgout->msg, msgout->rcpt_list, 
   1.441 +			       "while connected with %s, the server replied\n\t%s",
   1.442 +			       host, psb->buffer))
   1.443 +	    deliver_finish(msgout);
   1.444 +	}
   1.445 +      }
   1.446 +    }
   1.447 +    destroy_smtpbase(psb);
   1.448 +  }else{
   1.449 +    /* smtp_out_open() failed */
   1.450 +    foreach(msgout_list, msgout_node){
   1.451 +      msg_out *msgout = (msg_out *)(msgout_node->data);
   1.452 +      GList *rcpt_node;
   1.453 +
   1.454 +      for(rcpt_node = g_list_first(msgout->rcpt_list);
   1.455 +	  rcpt_node;
   1.456 +	  rcpt_node = g_list_next(rcpt_node)){
   1.457 +	address *rcpt = (address *)(rcpt_node->data);
   1.458 +
   1.459 +	addr_unmark_delivered(rcpt);
   1.460 +	if(route->connect_error_fail){
   1.461 +	  addr_mark_failed(rcpt);
   1.462 +	}else{
   1.463 +	  addr_mark_defered(rcpt);
   1.464 +	}
   1.465 +	if(route->wrapper ?
   1.466 +	   delivery_failures(msgout->msg, msgout->rcpt_list,
   1.467 +			     "could not open wrapper:\n\t%s",
   1.468 +			     strerror(errno)) :
   1.469 +	   delivery_failures(msgout->msg, msgout->rcpt_list, 
   1.470 +			     "could not open connection to %s:%d :\n\t%s",
   1.471 +			     host, port, h_errno != 0 ? hstrerror(h_errno) : strerror(errno)))
   1.472 +	  deliver_finish(msgout);
   1.473 +      }
   1.474 +    }
   1.475 +  }
   1.476 +  return ok;
   1.477 +}
   1.478 +
   1.479 +gboolean deliver_msglist_host(connect_route *route, GList *msgout_list, gchar *host, GList *res_list)
   1.480 +{
   1.481 +  DEBUG(5) debugf("protocol = %s\n", route->protocol);
   1.482 +
   1.483 +  if(strcmp(route->protocol, "pipe") == 0){
   1.484 +    return deliver_msglist_host_pipe(route, msgout_list, host, res_list);
   1.485 +  }else{
   1.486 +    return deliver_msglist_host_smtp(route, msgout_list, host, res_list);
   1.487 +  }
   1.488 +}
   1.489 +
   1.490 +/*
   1.491 +  delivers messages in msgout_list using route
   1.492 +*/
   1.493 +gboolean deliver_route_msgout_list(connect_route *route, GList *msgout_list)
   1.494 +{
   1.495 +  gboolean ok = FALSE;
   1.496 +
   1.497 +  DEBUG(5) debugf("deliver_route_msgout_list entered, route->name = %s\n",
   1.498 +		  route->name);
   1.499 +
   1.500 +  if(route->mail_host != NULL){
   1.501 +    /* this is easy... */
   1.502 +    if(deliver_msglist_host(route, msgout_list,
   1.503 +			    NULL, route->resolve_list))
   1.504 +      ok = TRUE;
   1.505 +      
   1.506 +  }else{
   1.507 +    /* this is not easy... */
   1.508 +    GList *mo_ph_list;
   1.509 +
   1.510 +    mo_ph_list = route_msgout_list(route, msgout_list);
   1.511 +    /* okay, now we have ordered our messages by the hosts. */
   1.512 +    if(mo_ph_list != NULL){
   1.513 +      GList *mo_ph_node;
   1.514 +      /* TODO: It would be nice to be able to fork for each host.
   1.515 +	 We cannot do that yet because of complications with finishing the
   1.516 +	 messages. Threads could be a solution because they use the same
   1.517 +	 memory. But we are not thread safe yet...
   1.518 +      */
   1.519 +      foreach(mo_ph_list, mo_ph_node){
   1.520 +	msgout_perhost *mo_ph = (msgout_perhost *)(mo_ph_node->data);
   1.521 +	if(deliver_msglist_host(route, mo_ph->msgout_list,
   1.522 +				mo_ph->host, route->resolve_list))
   1.523 +	  ok = TRUE;
   1.524 +
   1.525 +	destroy_msgout_perhost(mo_ph);
   1.526 +      }
   1.527 +      g_list_free(mo_ph_list);
   1.528 +    }
   1.529 +  }
   1.530 +  return ok;
   1.531 +}
   1.532 +
   1.533 +/*
   1.534 +  calls route_prepare_msg()
   1.535 +  delivers messages in msg_list using route
   1.536 +  by calling deliver_route_msgout_list()
   1.537 +*/
   1.538 +gboolean deliver_route_msg_list(connect_route *route, GList *msgout_list)
   1.539 +{
   1.540 +  GList *msgout_list_deliver = NULL;
   1.541 +  GList *msgout_node;
   1.542 +  gboolean ok = TRUE;
   1.543 +
   1.544 +  DEBUG(6) debugf("deliver_route_msg_list()\n");
   1.545 +
   1.546 +  foreach(msgout_list, msgout_node){
   1.547 +    msg_out *msgout = (msg_out *)(msgout_node->data);
   1.548 +    msg_out *msgout_cloned = clone_msg_out(msgout);
   1.549 +    GList *rcpt_list_non_delivered = NULL;
   1.550 +    GList *rcpt_node;
   1.551 +
   1.552 +    /* we have to delete already delivered rcpt's
   1.553 +       because a previous route may have delivered to it */
   1.554 +    foreach(msgout_cloned->rcpt_list, rcpt_node){
   1.555 +      address *rcpt = (address *)(rcpt_node->data);
   1.556 +      /* failed addresses already have been bounced
   1.557 +	 - there should be a better way to handle those.*/
   1.558 +      if(!addr_is_delivered(rcpt) && !addr_is_failed(rcpt) && !(rcpt->flags & ADDR_FLAG_LAST_ROUTE))
   1.559 +	rcpt_list_non_delivered = g_list_append(rcpt_list_non_delivered, rcpt);
   1.560 +    }
   1.561 +    g_list_free(msgout_cloned->rcpt_list);
   1.562 +    msgout_cloned->rcpt_list = rcpt_list_non_delivered;
   1.563 +
   1.564 +    if(msgout_cloned->rcpt_list){
   1.565 +      if(route_is_allowed_mail_local(route, msgout->msg->return_path) && 
   1.566 +	 route_is_allowed_return_path(route, msgout->msg->return_path)){
   1.567 +	GList *rcpt_list_allowed = NULL, *rcpt_list_notallowed = NULL;
   1.568 +	msg_rcptlist_route(route, msgout_cloned->rcpt_list,
   1.569 +			   &rcpt_list_allowed, &rcpt_list_notallowed);
   1.570 +	
   1.571 +	if(rcpt_list_allowed != NULL){
   1.572 +	  logwrite(LOG_NOTICE, "%s using '%s'\n", msgout->msg->uid, route->name);
   1.573 +
   1.574 +	  g_list_free(msgout_cloned->rcpt_list);
   1.575 +	  msgout_cloned->rcpt_list = rcpt_list_allowed;
   1.576 +	  
   1.577 +	  if(route->last_route){
   1.578 +	    GList *rcpt_node;
   1.579 +	    foreach(msgout_cloned->rcpt_list, rcpt_node){
   1.580 +	      address *rcpt = (address *)(rcpt_node->data);
   1.581 +	      rcpt->flags |= ADDR_FLAG_LAST_ROUTE;
   1.582 +	    }
   1.583 +	  }
   1.584 +
   1.585 +	  route_prepare_msgout(route, msgout_cloned);
   1.586 +	  msgout_list_deliver = g_list_append(msgout_list_deliver, msgout_cloned);
   1.587 +	}else
   1.588 +	  destroy_msg_out(msgout_cloned);
   1.589 +      }
   1.590 +      else
   1.591 +	destroy_msg_out(msgout_cloned);
   1.592 +    }else
   1.593 +      destroy_msg_out(msgout_cloned);
   1.594 +  }
   1.595 +
   1.596 +  if(msgout_list_deliver != NULL){
   1.597 +    if(deliver_route_msgout_list(route, msgout_list_deliver))
   1.598 +      ok = TRUE;
   1.599 +    destroy_msg_out_list(msgout_list_deliver);
   1.600 +  }
   1.601 +  return ok;
   1.602 +}
   1.603 +
   1.604 +/* copy pointers of delivered addresses to the msg's non_rcpt_list,
   1.605 +   to make sure that they will not be delivered again.
   1.606 +*/
   1.607 +void update_non_rcpt_list(msg_out *msgout)
   1.608 +{
   1.609 +  GList *rcpt_node;
   1.610 +  message *msg = msgout->msg;
   1.611 +
   1.612 +  foreach(msgout->rcpt_list, rcpt_node){
   1.613 +    address *rcpt = (address *)(rcpt_node->data);
   1.614 +    if(addr_is_delivered(rcpt) || addr_is_failed(rcpt))
   1.615 +      msg->non_rcpt_list = g_list_append(msg->non_rcpt_list, rcpt);
   1.616 +  }
   1.617 +}
   1.618 +
   1.619 +/* after delivery attempts, we check if there are any
   1.620 +   rcpt addresses left in the message.
   1.621 +   If all addresses have been completed, the spool files will
   1.622 +   be deleted, otherwise the header spool will be written back.
   1.623 +   We never changed the data spool, so there is no need to write that back.
   1.624 +
   1.625 +   returns TRUE if all went well.
   1.626 +*/
   1.627 +gboolean deliver_finish(msg_out *msgout)
   1.628 +{
   1.629 +  GList *rcpt_node;
   1.630 +  gboolean ok = FALSE;
   1.631 +  message *msg = msgout->msg;
   1.632 +  gboolean finished = TRUE;
   1.633 +
   1.634 +  update_non_rcpt_list(msgout);
   1.635 +
   1.636 +  /* we NEVER made copies of the addresses, flags affecting addresses
   1.637 +     were always set on the original address structs */
   1.638 +  foreach(msg->rcpt_list, rcpt_node){
   1.639 +    address *rcpt = (address *)(rcpt_node->data);
   1.640 +    if(!addr_is_finished_children(rcpt))
   1.641 +      finished = FALSE;
   1.642 +    else{
   1.643 +      /* if ALL children have been delivered,
   1.644 +	 mark parent as delivered.
   1.645 +	 if there is one or more not delivered,
   1.646 +	 it must have failed, we mark the parent as failed as well.
   1.647 +      */
   1.648 +      if(addr_is_delivered_children(rcpt)){
   1.649 +	addr_mark_delivered(rcpt);
   1.650 +      }else{
   1.651 +	addr_mark_failed(rcpt);
   1.652 +      }
   1.653 +    }
   1.654 +  }
   1.655 +  
   1.656 +  if(!finished){
   1.657 +    /* one not delivered address was found */
   1.658 +    if(spool_write(msg, FALSE)){
   1.659 +      ok = TRUE;
   1.660 +      DEBUG(2) debugf("spool header for %s written back.\n", msg->uid);
   1.661 +    }else
   1.662 +      logwrite(LOG_ALERT, "could not write back spool header for %s\n",
   1.663 +	       msg->uid);
   1.664 +  }else{
   1.665 +    ok = spool_delete_all(msg);
   1.666 +    if(ok)
   1.667 +      logwrite(LOG_NOTICE, "%s completed.\n", msg->uid);
   1.668 +  }
   1.669 +  return ok;
   1.670 +}
   1.671 +
   1.672 +gboolean deliver_finish_list(GList *msgout_list)
   1.673 +{
   1.674 +  gboolean ok = TRUE;
   1.675 +  GList *msgout_node;
   1.676 +  foreach(msgout_list, msgout_node){
   1.677 +    msg_out *msgout = (msg_out *)(msgout_node->data);
   1.678 +    if(!deliver_finish(msgout))
   1.679 +      ok = FALSE;
   1.680 +  }
   1.681 +  return ok;
   1.682 +}
   1.683 + 
   1.684 +gboolean deliver_msgout_list_online(GList *msgout_list)
   1.685 +{
   1.686 +  GList *rf_list = NULL;
   1.687 +  gchar *connect_name = detect_online();
   1.688 +  gboolean ok = FALSE;
   1.689 +
   1.690 +  if(connect_name != NULL){
   1.691 +    logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
   1.692 +    /* we are online! */
   1.693 +    rf_list = (GList *)table_find(conf.connect_routes, connect_name);
   1.694 +    if(rf_list != NULL){
   1.695 +      GList *route_list = read_route_list(rf_list, FALSE);
   1.696 +      if(route_list){
   1.697 +	GList *route_node;
   1.698 +	foreach(route_list, route_node){
   1.699 +	  connect_route *route = (connect_route *)(route_node->data);
   1.700 +	  ok = deliver_route_msg_list(route, msgout_list);
   1.701 +	}
   1.702 +	destroy_route_list(route_list);
   1.703 +      }
   1.704 +      else
   1.705 +	logwrite(LOG_ALERT,
   1.706 +		 "could not read route list '%s'\n", connect_name);
   1.707 +    }else{
   1.708 +      logwrite(LOG_ALERT, "route list with name '%s' not found.\n", connect_name);
   1.709 +    }
   1.710 +  }
   1.711 +  return ok;
   1.712 +}
   1.713 +
   1.714 +gboolean deliver_msg_list(GList *msg_list, guint flags){
   1.715 +  GList *msgout_list = create_msg_out_list(msg_list);
   1.716 +  GList *local_msgout_list = NULL, *localnet_msgout_list = NULL, *other_msgout_list = NULL;
   1.717 +  GList *msgout_node;
   1.718 +  GList *alias_table = NULL;
   1.719 +  gboolean ok = TRUE;
   1.720 +
   1.721 +  if(conf.alias_file){
   1.722 +    if(!(alias_table = table_read(conf.alias_file, ':')))
   1.723 +      return FALSE;
   1.724 +  }
   1.725 +      
   1.726 +  /* sort messages for different deliveries */
   1.727 +  foreach(msgout_list, msgout_node){
   1.728 +    msg_out *msgout = (msg_out *)(msgout_node->data);
   1.729 +    GList *rcpt_list;
   1.730 +    GList *local_rcpt_list = NULL;
   1.731 +    GList *localnet_rcpt_list = NULL;
   1.732 +    GList *other_rcpt_list;
   1.733 +
   1.734 +    if(!spool_lock(msgout->msg->uid)) continue;
   1.735 +
   1.736 +    rcpt_list = g_list_copy(msgout->msg->rcpt_list);
   1.737 +    if(conf.log_user){
   1.738 +      address *addr = create_address_qualified(conf.log_user, TRUE, conf.host_name);
   1.739 +      if(addr)
   1.740 +	rcpt_list = g_list_prepend(rcpt_list, addr);
   1.741 +    }
   1.742 +    if(alias_table){
   1.743 +      GList *aliased_rcpt_list;
   1.744 +      aliased_rcpt_list = alias_expand(alias_table, rcpt_list,
   1.745 +				       msgout->msg->non_rcpt_list);
   1.746 +      g_list_free(rcpt_list);
   1.747 +      rcpt_list = aliased_rcpt_list;
   1.748 +    }
   1.749 +
   1.750 +    /* local recipients */
   1.751 +    other_rcpt_list = NULL;
   1.752 +    rcptlist_with_addr_is_local(rcpt_list, &local_rcpt_list, &other_rcpt_list);
   1.753 +    
   1.754 +    if(flags & DLVR_LOCAL){
   1.755 +      if(local_rcpt_list != NULL){
   1.756 +	msg_out *local_msgout = clone_msg_out(msgout);
   1.757 +	local_msgout->rcpt_list = local_rcpt_list;
   1.758 +	local_msgout_list = g_list_append(local_msgout_list, local_msgout);
   1.759 +      }
   1.760 +    }
   1.761 +
   1.762 +    g_list_free(rcpt_list);
   1.763 +
   1.764 +    /* local net recipients */
   1.765 +    rcpt_list = other_rcpt_list;
   1.766 +    other_rcpt_list = NULL;
   1.767 +    rcptlist_with_one_of_hostlist(rcpt_list, conf.local_nets,
   1.768 +				  &localnet_rcpt_list, &other_rcpt_list);
   1.769 +
   1.770 +    if(flags & DLVR_LAN){
   1.771 +      if(localnet_rcpt_list != NULL){
   1.772 +	msg_out *localnet_msgout = clone_msg_out(msgout);
   1.773 +	localnet_msgout->rcpt_list = localnet_rcpt_list;
   1.774 +	localnet_msgout_list = g_list_append(localnet_msgout_list, localnet_msgout);
   1.775 +      }
   1.776 +    }
   1.777 +
   1.778 +    if(flags & DLVR_ONLINE){
   1.779 +      /* the rest, this is online delivery */
   1.780 +      if(other_rcpt_list != NULL){
   1.781 +	msg_out *other_msgout = clone_msg_out(msgout);
   1.782 +	other_msgout->rcpt_list = other_rcpt_list;
   1.783 +	other_msgout_list = g_list_append(other_msgout_list, other_msgout);
   1.784 +      }
   1.785 +    }
   1.786 +  }
   1.787 +
   1.788 +  if(alias_table)
   1.789 +    destroy_table(alias_table);
   1.790 +
   1.791 +  /* actual delivery */
   1.792 +  if(local_msgout_list != NULL){
   1.793 +    foreach(local_msgout_list, msgout_node){
   1.794 +      msg_out *msgout = (msg_out *)(msgout_node->data);
   1.795 +      if(!deliver_local(msgout)) ok = FALSE;
   1.796 +    }
   1.797 +    destroy_msg_out_list(local_msgout_list);
   1.798 +  }
   1.799 +
   1.800 +  if(localnet_msgout_list != NULL){
   1.801 +    GList *route_list = NULL;
   1.802 +    GList *route_node;
   1.803 +
   1.804 +    if(conf.local_net_routes)
   1.805 +      route_list = read_route_list(conf.local_net_routes, TRUE);
   1.806 +    else
   1.807 +      route_list = g_list_append(NULL, create_local_route());
   1.808 +
   1.809 +    foreach(route_list, route_node){
   1.810 +      connect_route *route = (connect_route *)(route_node->data);
   1.811 +      if(!deliver_route_msg_list(route, localnet_msgout_list)) ok = FALSE;
   1.812 +    }
   1.813 +    destroy_msg_out_list(localnet_msgout_list);
   1.814 +    destroy_route_list(route_list);
   1.815 +  }
   1.816 +
   1.817 +  if(other_msgout_list != NULL){
   1.818 +    if(!deliver_msgout_list_online(other_msgout_list)) ok = FALSE;
   1.819 +    destroy_msg_out_list(other_msgout_list);
   1.820 +  }
   1.821 +
   1.822 +  foreach(msgout_list, msgout_node){
   1.823 +    msg_out *msgout = (msg_out *)(msgout_node->data);
   1.824 +    spool_unlock(msgout->msg->uid);
   1.825 +  }
   1.826 +
   1.827 +  destroy_msg_out_list(msgout_list);
   1.828 +
   1.829 +  return ok;
   1.830 +}
   1.831 +
   1.832 +/* This function searches in the list of rcpt addresses
   1.833 +   for local and 'local net' addresses. Remote addresses
   1.834 +   which are reachable only when online are treated specially
   1.835 +   in another function.
   1.836 +
   1.837 +   deliver() is called when a message has just been received and should
   1.838 +   be delivered immediately.
   1.839 +*/
   1.840 +gboolean deliver(message *msg)
   1.841 +{
   1.842 +  gboolean ok;
   1.843 +
   1.844 +  GList *msg_list = g_list_append(NULL, msg);
   1.845 +
   1.846 +  ok = deliver_msg_list(msg_list, DLVR_ALL);
   1.847 +  
   1.848 +  g_list_free(msg_list);
   1.849 +
   1.850 +  return ok;
   1.851 +}
   1.852 +