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