masqmail-0.2

annotate src/route.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-2001 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 <fnmatch.h>
meillo@0 21
meillo@0 22 msgout_perhost *create_msgout_perhost(gchar *host)
meillo@0 23 {
meillo@0 24 msgout_perhost *mo_ph = g_malloc(sizeof(msgout_perhost));
meillo@0 25 if(mo_ph){
meillo@0 26 mo_ph->host = g_strdup(host);
meillo@0 27 mo_ph->msgout_list = NULL;
meillo@0 28 }
meillo@0 29 return mo_ph;
meillo@0 30 }
meillo@0 31
meillo@0 32 void destroy_msgout_perhost(msgout_perhost *mo_ph)
meillo@0 33 {
meillo@0 34 GList *mo_node;
meillo@0 35
meillo@0 36 foreach(mo_ph->msgout_list, mo_node){
meillo@0 37 msg_out *mo = (msg_out *)(mo_node->data);
meillo@0 38 /* the rcpt_list is owned by the msgout's,
meillo@0 39 but not the rcpt's themselves */
meillo@0 40 g_list_free(mo->rcpt_list);
meillo@0 41 g_free(mo);
meillo@0 42 }
meillo@0 43 g_list_free(mo_ph->msgout_list);
meillo@0 44 g_free(mo_ph);
meillo@0 45 }
meillo@0 46
meillo@0 47 void rewrite_headers(msg_out *msgout, connect_route *route)
meillo@0 48 {
meillo@0 49 /* if set_h_from_domain is set, replace domain in all
meillo@0 50 From: headers.
meillo@0 51 */
meillo@0 52 msgout->hdr_list = g_list_copy(msgout->msg->hdr_list);
meillo@0 53
meillo@0 54 /* map from addresses */
meillo@0 55 if(route->map_h_from_addresses != NULL){
meillo@0 56 GList *hdr_node;
meillo@0 57 foreach(msgout->hdr_list, hdr_node){
meillo@0 58 header *hdr = (header *)(hdr_node->data);
meillo@0 59 if(hdr->id == HEAD_FROM){
meillo@0 60 header *new_hdr = copy_header(hdr);
meillo@0 61 if(map_address_header(new_hdr, route->map_h_from_addresses)){
meillo@0 62 hdr_node->data = new_hdr;
meillo@0 63 /* we need this list only to carefully free the extra headers: */
meillo@0 64 msgout->xtra_hdr_list =
meillo@0 65 g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 66 }else
meillo@0 67 g_free(new_hdr);
meillo@0 68 }
meillo@0 69 }
meillo@0 70 }else{
meillo@0 71 /* replace from domain */
meillo@0 72 if(route->set_h_from_domain != NULL){
meillo@0 73 GList *hdr_node;
meillo@0 74
meillo@0 75 foreach(msgout->hdr_list, hdr_node){
meillo@0 76 header *hdr = (header *)(hdr_node->data);
meillo@0 77 if(hdr->id == HEAD_FROM){
meillo@0 78 header *new_hdr = copy_header(hdr);
meillo@0 79
meillo@0 80 DEBUG(5) debugf("setting From: domain to %s\n",
meillo@0 81 route->set_h_from_domain);
meillo@0 82 if(set_address_header_domain(new_hdr, route->set_h_from_domain)){
meillo@0 83 hdr_node->data = new_hdr;
meillo@0 84 /* we need this list only to carefully free the extra headers: */
meillo@0 85 DEBUG(6) debugf("header = %s\n",
meillo@0 86 new_hdr->header);
meillo@0 87 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 88 }else{
meillo@0 89 logwrite(LOG_ALERT, "error in set_address_header_domain(%s, %s)\n",
meillo@0 90 new_hdr->value, route->set_h_from_domain);
meillo@0 91 }
meillo@0 92 }
meillo@0 93 }
meillo@0 94 }
meillo@0 95 }
meillo@0 96
meillo@0 97 /* map reply-to addresses */
meillo@0 98 if(route->map_h_reply_to_addresses != NULL){
meillo@0 99 GList *hdr_node;
meillo@0 100 foreach(msgout->hdr_list, hdr_node){
meillo@0 101 header *hdr = (header *)(hdr_node->data);
meillo@0 102 if(hdr->id == HEAD_REPLY_TO){
meillo@0 103 header *new_hdr = copy_header(hdr);
meillo@0 104 if(map_address_header(new_hdr, route->map_h_reply_to_addresses)){
meillo@0 105 hdr_node->data = new_hdr;
meillo@0 106 /* we need this list only to carefully free the extra headers: */
meillo@0 107 msgout->xtra_hdr_list =
meillo@0 108 g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 109 }else
meillo@0 110 g_free(new_hdr);
meillo@0 111 }
meillo@0 112 }
meillo@0 113 }else{
meillo@0 114 /* replace Reply-to domain */
meillo@0 115 if(route->set_h_reply_to_domain != NULL){
meillo@0 116 GList *hdr_node;
meillo@0 117
meillo@0 118 foreach(msgout->hdr_list, hdr_node){
meillo@0 119 header *hdr = (header *)(hdr_node->data);
meillo@0 120 if(hdr->id == HEAD_REPLY_TO){
meillo@0 121 header *new_hdr = copy_header(hdr);
meillo@0 122
meillo@0 123 set_address_header_domain(new_hdr, route->set_h_reply_to_domain);
meillo@0 124 hdr_node->data = new_hdr;
meillo@0 125 /* we need this list only to carefully free the extra headers: */
meillo@0 126 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 127 }
meillo@0 128 }
meillo@0 129 }
meillo@0 130 }
meillo@0 131
meillo@0 132 /* map Mail-Followup-To addresses */
meillo@0 133 if(route->map_h_mail_followup_to_addresses != NULL){
meillo@0 134 GList *hdr_node;
meillo@0 135 foreach(msgout->hdr_list, hdr_node){
meillo@0 136 header *hdr = (header *)(hdr_node->data);
meillo@0 137 if(strncasecmp(hdr->header, "Mail-Followup-To", 16) == 0){
meillo@0 138 header *new_hdr = copy_header(hdr);
meillo@0 139 if(map_address_header(new_hdr, route->map_h_mail_followup_to_addresses)){
meillo@0 140 hdr_node->data = new_hdr;
meillo@0 141 /* we need this list only to carefully free the extra headers: */
meillo@0 142 msgout->xtra_hdr_list =
meillo@0 143 g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 144 }else
meillo@0 145 g_free(new_hdr);
meillo@0 146 }
meillo@0 147 }
meillo@0 148 }
meillo@0 149
meillo@0 150 /* set Sender: domain to return_path->domain */
meillo@0 151 if(route->expand_h_sender_domain){
meillo@0 152 GList *hdr_node;
meillo@0 153
meillo@0 154 foreach(msgout->hdr_list, hdr_node){
meillo@0 155 header *hdr = (header *)(hdr_node->data);
meillo@0 156 if(hdr->id == HEAD_SENDER){
meillo@0 157 header *new_hdr = copy_header(hdr);
meillo@0 158
meillo@0 159 set_address_header_domain(new_hdr, msgout->return_path->domain);
meillo@0 160 hdr_node->data = new_hdr;
meillo@0 161 /* we need this list only to carefully free the extra headers: */
meillo@0 162 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 163 }
meillo@0 164 }
meillo@0 165 }
meillo@0 166
meillo@0 167 /* set Sender: domain to return_path->domain */
meillo@0 168 if(route->expand_h_sender_address){
meillo@0 169 GList *hdr_node;
meillo@0 170
meillo@0 171 foreach(msgout->hdr_list, hdr_node){
meillo@0 172 header *hdr = (header *)(hdr_node->data);
meillo@0 173 if(hdr->id == HEAD_SENDER){
meillo@0 174 header *new_hdr;
meillo@0 175
meillo@0 176 new_hdr =
meillo@0 177 create_header(HEAD_SENDER, "Sender: %s@%s\n",
meillo@0 178 msgout->return_path->local_part, msgout->return_path->domain);
meillo@0 179 hdr_node->data = new_hdr;
meillo@0 180 /* we need this list only to carefully free the extra headers: */
meillo@0 181 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
meillo@0 182 }
meillo@0 183 }
meillo@0 184 }
meillo@0 185
meillo@0 186 if(msgout->xtra_hdr_list == NULL){
meillo@0 187 /* nothing was changed */
meillo@0 188 g_list_free(msgout->hdr_list);
meillo@0 189 msgout->hdr_list = NULL;
meillo@0 190 }
meillo@0 191 DEBUG(5) debugf("rewrite_headers() returning\n");
meillo@0 192 }
meillo@0 193
meillo@0 194 void rcptlist_with_one_of_hostlist(GList *rcpt_list, GList *host_list,
meillo@0 195 GList **p_rcpt_list, GList **p_non_rcpt_list)
meillo@0 196 {
meillo@0 197 GList *rcpt_node;
meillo@0 198
meillo@0 199 if(rcpt_list == NULL)
meillo@0 200 return;
meillo@0 201
meillo@0 202 foreach(rcpt_list, rcpt_node){
meillo@0 203 address *rcpt = (address *)(rcpt_node->data);
meillo@0 204 GList *host_node = NULL;
meillo@0 205
meillo@0 206 foreach(host_list, host_node){
meillo@0 207 gchar *host = (gchar *)(host_node->data);
meillo@0 208 if(fnmatch(host, rcpt->domain, FNM_CASEFOLD) == 0)
meillo@0 209 break;
meillo@0 210 }
meillo@0 211 if(host_node){
meillo@0 212 if(p_rcpt_list)
meillo@0 213 *p_rcpt_list = g_list_append(*p_rcpt_list, rcpt);
meillo@0 214 }else{
meillo@0 215 if(p_non_rcpt_list)
meillo@0 216 *p_non_rcpt_list = g_list_append(*p_non_rcpt_list, rcpt);
meillo@0 217 }
meillo@0 218
meillo@0 219 }
meillo@0 220 }
meillo@0 221
meillo@0 222 void rcptlist_with_addr_is_local(GList *rcpt_list,
meillo@0 223 GList **p_rcpt_list, GList **p_non_rcpt_list)
meillo@0 224 {
meillo@0 225 GList *rcpt_node;
meillo@0 226
meillo@0 227 if(rcpt_list == NULL)
meillo@0 228 return;
meillo@0 229
meillo@0 230 foreach(rcpt_list, rcpt_node){
meillo@0 231 address *rcpt = (address *)(rcpt_node->data);
meillo@0 232 if(addr_is_local(rcpt)){
meillo@0 233 if(p_rcpt_list)
meillo@0 234 *p_rcpt_list = g_list_append(*p_rcpt_list, rcpt);
meillo@0 235 }else{
meillo@0 236 if(p_non_rcpt_list)
meillo@0 237 *p_non_rcpt_list = g_list_append(*p_non_rcpt_list, rcpt);
meillo@0 238 }
meillo@0 239
meillo@0 240 }
meillo@0 241 }
meillo@0 242
meillo@0 243 static gint _g_list_addrcmp(gconstpointer a, gconstpointer b)
meillo@0 244 {
meillo@0 245 return addr_match((address *)a, (address *)b);
meillo@0 246 }
meillo@0 247
meillo@0 248 gboolean route_is_allowed_return_path(connect_route *route, address *ret_path)
meillo@0 249 {
meillo@0 250 if(route->not_allowed_return_paths != NULL){
meillo@0 251 if(g_list_find_custom(route->not_allowed_return_paths, ret_path,
meillo@0 252 _g_list_addrcmp) != NULL){
meillo@0 253 return FALSE;
meillo@0 254 }
meillo@0 255 }
meillo@0 256 if(route->allowed_return_paths != NULL){
meillo@0 257 if(g_list_find_custom(route->allowed_return_paths, ret_path,
meillo@0 258 _g_list_addrcmp) != NULL){
meillo@0 259 return TRUE;
meillo@0 260 }else{
meillo@0 261 return FALSE;
meillo@0 262 }
meillo@0 263 }
meillo@0 264 return TRUE;
meillo@0 265 }
meillo@0 266
meillo@0 267 static gint _g_list_strcmp(gconstpointer a, gconstpointer b)
meillo@0 268 {
meillo@0 269 return (gint)strcmp(a, b);
meillo@0 270 }
meillo@0 271
meillo@0 272 gboolean route_is_allowed_mail_local(connect_route *route, address *ret_path)
meillo@0 273 {
meillo@0 274 gchar *loc_part = ret_path->local_part;
meillo@0 275
meillo@0 276 if(route->not_allowed_mail_locals != NULL){
meillo@0 277 if(g_list_find_custom(route->not_allowed_mail_locals, loc_part,
meillo@0 278 _g_list_strcmp) != NULL)
meillo@0 279 return FALSE;
meillo@0 280 }
meillo@0 281 if(route->allowed_mail_locals != NULL){
meillo@0 282 if(g_list_find_custom(route->allowed_mail_locals, loc_part,
meillo@0 283 _g_list_strcmp) != NULL)
meillo@0 284 return TRUE;
meillo@0 285 else
meillo@0 286 return FALSE;
meillo@0 287 }
meillo@0 288 return TRUE;
meillo@0 289 }
meillo@0 290
meillo@0 291 /*
meillo@0 292 Make lists of matching/not matching rcpts.
meillo@0 293 Local domains are NOT regared here, these should be sorted out previously
meillo@0 294 */
meillo@0 295 void msg_rcptlist_route(connect_route *route, GList *rcpt_list,
meillo@0 296 GList **p_rcpt_list, GList **p_non_rcpt_list)
meillo@0 297 {
meillo@0 298 GList *tmp_list = NULL;
meillo@0 299 /* sort out those domains that can be sent over this connection: */
meillo@0 300 if(route->allowed_rcpt_domains){
meillo@0 301 DEBUG(5) debugf("testing for route->allowed_rcpt_domains\n");
meillo@0 302 rcptlist_with_one_of_hostlist(rcpt_list, route->allowed_rcpt_domains, &tmp_list, p_non_rcpt_list);
meillo@0 303 }else{
meillo@0 304 DEBUG(5) debugf("route->allowed_rcpt_domains == NULL\n");
meillo@0 305 tmp_list = g_list_copy(rcpt_list);
meillo@0 306 }
meillo@0 307
meillo@0 308 /* sort out those domains that cannot be sent over this connection: */
meillo@0 309 rcptlist_with_one_of_hostlist(tmp_list, route->not_allowed_rcpt_domains, p_non_rcpt_list, p_rcpt_list);
meillo@0 310 g_list_free(tmp_list);
meillo@0 311 }
meillo@0 312
meillo@0 313 msg_out *route_prepare_msgout(connect_route *route, msg_out *msgout)
meillo@0 314 {
meillo@0 315 message *msg = msgout->msg;
meillo@0 316 GList *rcpt_list = msgout->rcpt_list;
meillo@0 317
meillo@0 318 if(rcpt_list != NULL){
meillo@0 319 /* found a few */
meillo@0 320 DEBUG(5){
meillo@0 321 GList *node;
meillo@0 322 debugf("rcpts for routed delivery, route = %s, id = %s\n", route->name, msg->uid);
meillo@0 323 foreach(rcpt_list, node){
meillo@0 324 address *rcpt = (address *)(node->data);
meillo@0 325 debugf("rcpt for routed delivery: <%s@%s>\n",
meillo@0 326 rcpt->local_part, rcpt->domain);
meillo@0 327 }
meillo@0 328 }
meillo@0 329
meillo@0 330 /* rewrite return path
meillo@0 331 if there is a table, use that
meillo@0 332 if an address is found and if it has a domain, use that
meillo@0 333 */
meillo@0 334 if(route->map_return_path_addresses){
meillo@0 335 address *ret_path = NULL;
meillo@0 336 DEBUG(5) debugf("looking up %s in map_return_path_addresses\n",
meillo@0 337 msg->return_path->local_part);
meillo@0 338 ret_path =
meillo@0 339 (address *)table_find_fnmatch(route->map_return_path_addresses,
meillo@0 340 msg->return_path->local_part);
meillo@0 341 if(ret_path){
meillo@0 342 DEBUG(5) debugf("found <%s@%s>\n",
meillo@0 343 ret_path->local_part, ret_path->domain);
meillo@0 344 if(ret_path->domain == NULL)
meillo@0 345 ret_path->domain =
meillo@0 346 route->set_return_path_domain ?
meillo@0 347 route->set_return_path_domain : msg->return_path->domain;
meillo@0 348 msgout->return_path = copy_address(ret_path);
meillo@0 349 }
meillo@0 350 }
meillo@0 351 if(msgout->return_path == NULL){
meillo@0 352 DEBUG(5) debugf("setting return path to %s\n",
meillo@0 353 route->set_return_path_domain);
meillo@0 354 msgout->return_path =
meillo@0 355 copy_modify_address(msg->return_path,
meillo@0 356 NULL, route->set_return_path_domain);
meillo@0 357 }
meillo@0 358 rewrite_headers(msgout, route);
meillo@0 359
meillo@0 360 return msgout;
meillo@0 361 }
meillo@0 362 return NULL;
meillo@0 363 }
meillo@0 364
meillo@0 365 /* put msgout's is msgout_list into bins (msgout_perhost structs) for each
meillo@0 366 host. Used if there is no mail_host.
meillo@0 367 route param is not used, we leave it here because that may change.
meillo@0 368 */
meillo@0 369
meillo@0 370 GList *route_msgout_list(connect_route *route, GList *msgout_list)
meillo@0 371 {
meillo@0 372 GList *mo_ph_list = NULL;
meillo@0 373 GList *msgout_node;
meillo@0 374
meillo@0 375 foreach(msgout_list, msgout_node){
meillo@0 376 msg_out *msgout = (msg_out *)(msgout_node->data);
meillo@0 377 msg_out *msgout_new;
meillo@0 378 GList *rcpt_list = msgout->rcpt_list;
meillo@0 379 GList *rcpt_node;
meillo@0 380
meillo@0 381 foreach(rcpt_list, rcpt_node){
meillo@0 382 address *rcpt = rcpt_node->data;
meillo@0 383 msgout_perhost *mo_ph = NULL;
meillo@0 384 GList *mo_ph_node = NULL;
meillo@0 385
meillo@0 386 /* search host in mo_ph_list */
meillo@0 387 foreach(mo_ph_list, mo_ph_node){
meillo@0 388 mo_ph = (msgout_perhost *)(mo_ph_node->data);
meillo@0 389 if(strcasecmp(mo_ph->host, rcpt->domain) == 0)
meillo@0 390 break;
meillo@0 391 }
meillo@0 392 if(mo_ph_node != NULL){
meillo@0 393 /* there is already a rcpt for this host */
meillo@0 394 msg_out *msgout_last =
meillo@0 395 (msg_out *)((g_list_last(mo_ph->msgout_list))->data);
meillo@0 396 if(msgout_last->msg == msgout->msg){
meillo@0 397 /* if it is also the same message, it must be the last one
meillo@0 398 appended to mo_ph->msgout_list (since outer loop goes through
meillo@0 399 msgout_list) */
meillo@0 400 msgout_last->rcpt_list =
meillo@0 401 g_list_append(msgout_last->rcpt_list, rcpt);
meillo@0 402 }else{
meillo@0 403 /* if not, we append a new msgout */
meillo@0 404 /* make a copy of msgout */
meillo@0 405 msgout_new = create_msg_out(msgout->msg);
meillo@0 406 msgout_new->return_path = msgout->return_path;
meillo@0 407 msgout_new->hdr_list = msgout->hdr_list;
meillo@0 408
meillo@0 409 /* append our rcpt to it */
meillo@0 410 /* It is the 1st rcpt for this msg to this host,
meillo@0 411 therefore we safely give NULL */
meillo@0 412 msgout_new->rcpt_list = g_list_append(NULL, rcpt);
meillo@0 413 mo_ph->msgout_list =
meillo@0 414 g_list_append(mo_ph->msgout_list, msgout_new);
meillo@0 415 }
meillo@0 416 }else{
meillo@0 417 /* this rcpt to goes to another host */
meillo@0 418 mo_ph = create_msgout_perhost(rcpt->domain);
meillo@0 419 mo_ph_list = g_list_append(mo_ph_list, mo_ph);
meillo@0 420
meillo@0 421 /* make a copy of msgout */
meillo@0 422 msgout_new = create_msg_out(msgout->msg);
meillo@0 423 msgout_new->return_path = msgout->return_path;
meillo@0 424 msgout_new->hdr_list = msgout->hdr_list;
meillo@0 425
meillo@0 426 /* append our rcpt to it */
meillo@0 427 /* It is the 1st rcpt for this msg to this host,
meillo@0 428 therefore we safely give NULL */
meillo@0 429 msgout_new->rcpt_list = g_list_append(NULL, rcpt);
meillo@0 430 mo_ph->msgout_list = g_list_append(mo_ph->msgout_list, msgout_new);
meillo@0 431 }/* if mo_ph != NULL */
meillo@0 432 }/* foreach(rcpt_list, ... */
meillo@0 433 }/* foreach(msgout_list, ... */
meillo@0 434
meillo@0 435 return mo_ph_list;
meillo@0 436 }