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