masqmail

view src/route.c @ 222:8cddc65765bd

added support for STARTTLS wrappers added the route config option `instant_helo' which causes masqmail, as SMTP client, not to wait for the server's 220 greeting. Instead if says EHLO right at once. You'll need this for STARTTLS wrappers that usually eat the greeting line.
author meillo@marmaro.de
date Fri, 23 Jul 2010 10:57:53 +0200
parents f671821d8222
children 5f9f3a65032e
line source
1 /* MasqMail
2 Copyright (C) 1999-2001 Oliver Kurth
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.
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.
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 */
19 #include <fnmatch.h>
21 #include "masqmail.h"
23 msgout_perhost*
24 create_msgout_perhost(gchar * host)
25 {
26 msgout_perhost *mo_ph = g_malloc(sizeof(msgout_perhost));
27 if (mo_ph) {
28 mo_ph->host = g_strdup(host);
29 mo_ph->msgout_list = NULL;
30 }
31 return mo_ph;
32 }
34 void
35 destroy_msgout_perhost(msgout_perhost * mo_ph)
36 {
37 GList *mo_node;
39 foreach(mo_ph->msgout_list, mo_node) {
40 msg_out *mo = (msg_out *) (mo_node->data);
41 /* the rcpt_list is owned by the msgout's, but not the rcpt's themselves */
42 g_list_free(mo->rcpt_list);
43 g_free(mo);
44 }
45 g_list_free(mo_ph->msgout_list);
46 g_free(mo_ph);
47 }
49 void
50 rewrite_headers(msg_out * msgout, connect_route * route)
51 {
52 /* if set_h_from_domain is set, replace domain in all
53 From: headers.
54 */
55 msgout->hdr_list = g_list_copy(msgout->msg->hdr_list);
57 /* map from addresses */
58 if (route->map_h_from_addresses != NULL) {
59 GList *hdr_node;
60 foreach(msgout->hdr_list, hdr_node) {
61 header *hdr = (header *) (hdr_node->data);
62 if (hdr->id == HEAD_FROM) {
63 header *new_hdr = copy_header(hdr);
64 if (map_address_header(new_hdr, route->map_h_from_addresses)) {
65 hdr_node->data = new_hdr;
66 /* we need this list only to carefully free the extra headers: */
67 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
68 } else
69 g_free(new_hdr);
70 }
71 }
72 } else {
73 /* replace from domain */
74 if (route->set_h_from_domain != NULL) {
75 GList *hdr_node;
77 foreach(msgout->hdr_list, hdr_node) {
78 header *hdr = (header *) (hdr_node->data);
79 if (hdr->id == HEAD_FROM) {
80 header *new_hdr = copy_header(hdr);
82 DEBUG(5) debugf("setting From: domain to %s\n", route->set_h_from_domain);
83 if (set_address_header_domain(new_hdr, route->set_h_from_domain)) {
84 hdr_node->data = new_hdr;
85 /* we need this list only to carefully free the extra headers: */
86 DEBUG(6) debugf("header = %s\n", 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 }
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
105 (new_hdr, route->map_h_reply_to_addresses)) {
106 hdr_node->data = new_hdr;
107 /* we need this list only to carefully free the extra headers: */
108 msgout->xtra_hdr_list = 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;
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);
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 }
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 = g_list_append(msgout->xtra_hdr_list, new_hdr);
143 } else
144 g_free(new_hdr);
145 }
146 }
147 }
149 /* set Sender: domain to return_path->domain */
150 if (route->expand_h_sender_domain) {
151 GList *hdr_node;
153 foreach(msgout->hdr_list, hdr_node) {
154 header *hdr = (header *) (hdr_node->data);
155 if (hdr->id == HEAD_SENDER) {
156 header *new_hdr = copy_header(hdr);
158 set_address_header_domain(new_hdr, msgout->return_path->domain);
159 hdr_node->data = new_hdr;
160 /* we need this list only to carefully free the extra headers: */
161 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
162 }
163 }
164 }
166 /* set Sender: domain to return_path->domain */
167 if (route->expand_h_sender_address) {
168 GList *hdr_node;
170 foreach(msgout->hdr_list, hdr_node) {
171 header *hdr = (header *) (hdr_node->data);
172 if (hdr->id == HEAD_SENDER) {
173 header *new_hdr;
175 new_hdr = create_header(HEAD_SENDER, "Sender: %s@%s\n",
176 msgout->return_path->local_part, msgout->return_path->domain);
177 hdr_node->data = new_hdr;
178 /* we need this list only to carefully free the extra headers: */
179 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
180 }
181 }
182 }
184 if (msgout->xtra_hdr_list == NULL) {
185 /* nothing was changed */
186 g_list_free(msgout->hdr_list);
187 msgout->hdr_list = NULL;
188 }
189 DEBUG(5) debugf("rewrite_headers() returning\n");
190 }
192 void
193 rcptlist_with_one_of_hostlist(GList * rcpt_list, GList * host_list, GList ** p_rcpt_list, GList ** p_non_rcpt_list)
194 {
195 GList *rcpt_node;
197 if (rcpt_list == NULL)
198 return;
200 foreach(rcpt_list, rcpt_node) {
201 address *rcpt = (address *) (rcpt_node->data);
202 GList *host_node = NULL;
204 foreach(host_list, host_node) {
205 gchar *host = (gchar *) (host_node->data);
206 if (fnmatch(host, rcpt->domain, FNM_CASEFOLD) == 0)
207 break;
208 }
209 if (host_node) {
210 if (p_rcpt_list)
211 *p_rcpt_list = g_list_append(*p_rcpt_list, rcpt);
212 } else {
213 if (p_non_rcpt_list)
214 *p_non_rcpt_list = g_list_append(*p_non_rcpt_list, rcpt);
215 }
217 }
218 }
220 void
221 rcptlist_with_addr_is_local(GList * rcpt_list, GList ** p_rcpt_list, GList ** p_non_rcpt_list)
222 {
223 GList *rcpt_node;
225 if (rcpt_list == NULL)
226 return;
228 foreach(rcpt_list, rcpt_node) {
229 address *rcpt = (address *) (rcpt_node->data);
230 if (addr_is_local(rcpt)) {
231 if (p_rcpt_list)
232 *p_rcpt_list = g_list_append(*p_rcpt_list, rcpt);
233 } else {
234 if (p_non_rcpt_list)
235 *p_non_rcpt_list = g_list_append(*p_non_rcpt_list, rcpt);
236 }
238 }
239 }
241 static gint
242 _g_list_addrcmp(gconstpointer a, gconstpointer b)
243 {
244 return addr_match((address *) a, (address *) b);
245 }
247 gboolean
248 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, _g_list_addrcmp) != NULL) {
252 return FALSE;
253 }
254 }
255 if (route->allowed_return_paths != NULL) {
256 if (g_list_find_custom(route->allowed_return_paths, ret_path, _g_list_addrcmp) != NULL) {
257 return TRUE;
258 } else {
259 return FALSE;
260 }
261 }
262 return TRUE;
263 }
265 static gint
266 _g_list_strcmp(gconstpointer a, gconstpointer b)
267 {
268 return (gint) strcmp(a, b);
269 }
271 gboolean
272 route_is_allowed_mail_local(connect_route * route, address * ret_path)
273 {
274 gchar *loc_part = ret_path->local_part;
276 if (route->not_allowed_mail_locals != NULL) {
277 if (g_list_find_custom(route->not_allowed_mail_locals, loc_part, _g_list_strcmp) != NULL)
278 return FALSE;
279 }
280 if (route->allowed_mail_locals != NULL) {
281 if (g_list_find_custom(route->allowed_mail_locals, loc_part, _g_list_strcmp) != NULL)
282 return TRUE;
283 else
284 return FALSE;
285 }
286 return TRUE;
287 }
289 /*
290 Make lists of matching/not matching rcpts.
291 Local domains are NOT regared here, these should be sorted out previously
292 */
293 void
294 msg_rcptlist_route(connect_route * route, GList * rcpt_list, GList ** p_rcpt_list, GList ** p_non_rcpt_list)
295 {
296 GList *tmp_list = NULL;
297 /* sort out those domains that can be sent over this connection: */
298 if (route->allowed_rcpt_domains) {
299 DEBUG(5) debugf("testing for route->allowed_rcpt_domains\n");
300 rcptlist_with_one_of_hostlist(rcpt_list, route->allowed_rcpt_domains, &tmp_list, p_non_rcpt_list);
301 } else {
302 DEBUG(5) debugf("route->allowed_rcpt_domains == NULL\n");
303 tmp_list = g_list_copy(rcpt_list);
304 }
306 /* sort out those domains that cannot be sent over this connection: */
307 rcptlist_with_one_of_hostlist(tmp_list, route->not_allowed_rcpt_domains, p_non_rcpt_list, p_rcpt_list);
308 g_list_free(tmp_list);
309 }
311 msg_out*
312 route_prepare_msgout(connect_route * route, msg_out * msgout)
313 {
314 message *msg = msgout->msg;
315 GList *rcpt_list = msgout->rcpt_list;
317 if (rcpt_list != NULL) {
318 /* found a few */
319 DEBUG(5) {
320 GList *node;
321 debugf("rcpts for routed delivery, route = %s, id = %s\n", route->name, msg->uid);
322 foreach(rcpt_list, node) {
323 address *rcpt = (address *) (node->data);
324 debugf(" rcpt for routed delivery: <%s@%s>\n",
325 rcpt->local_part, rcpt->domain);
326 }
327 }
329 /* rewrite return path if there is a table, use that
330 if an address is found and if it has a domain, use that
331 */
332 if (route->map_return_path_addresses) {
333 address *ret_path = NULL;
334 DEBUG(5) debugf("looking up %s in map_return_path_addresses\n", msg->return_path->local_part);
335 ret_path = (address *) table_find_fnmatch(route->map_return_path_addresses, msg->return_path->local_part);
336 if (ret_path) {
337 DEBUG(5) debugf("found <%s@%s>\n", ret_path->local_part, ret_path->domain);
338 if (ret_path->domain == NULL)
339 ret_path->domain = route->set_return_path_domain
340 ? route->set_return_path_domain
341 : msg->return_path->domain;
342 msgout->return_path = copy_address(ret_path);
343 }
344 }
345 if (msgout->return_path == NULL) {
346 DEBUG(5) debugf("setting return path to %s\n", route->set_return_path_domain);
347 msgout->return_path = copy_modify_address(msg->return_path, NULL, route->set_return_path_domain);
348 }
349 rewrite_headers(msgout, route);
351 return msgout;
352 }
353 return NULL;
354 }
356 /* put msgout's is msgout_list into bins (msgout_perhost structs) for each
357 host. Used if there is no mail_host.
358 route param is not used, we leave it here because that may change.
359 */
361 GList*
362 route_msgout_list(connect_route * route, GList * msgout_list)
363 {
364 GList *mo_ph_list = NULL;
365 GList *msgout_node;
367 foreach(msgout_list, msgout_node) {
368 msg_out *msgout = (msg_out *) (msgout_node->data);
369 msg_out *msgout_new;
370 GList *rcpt_list = msgout->rcpt_list;
371 GList *rcpt_node;
373 foreach(rcpt_list, rcpt_node) {
374 address *rcpt = rcpt_node->data;
375 msgout_perhost *mo_ph = NULL;
376 GList *mo_ph_node = NULL;
378 /* search host in mo_ph_list */
379 foreach(mo_ph_list, mo_ph_node) {
380 mo_ph = (msgout_perhost *) (mo_ph_node->data);
381 if (strcasecmp(mo_ph->host, rcpt->domain) == 0)
382 break;
383 }
384 if (mo_ph_node != NULL) {
385 /* there is already a rcpt for this host */
386 msg_out *msgout_last = (msg_out *) ((g_list_last(mo_ph->msgout_list))->data);
387 if (msgout_last->msg == msgout->msg) {
388 /* if it is also the same message, it must be the last one appended
389 to mo_ph->msgout_list (since outer loop goes through msgout_list) */
390 msgout_last->rcpt_list = g_list_append(msgout_last->rcpt_list, rcpt);
391 } else {
392 /* if not, we append a new msgout */
393 /* make a copy of msgout */
394 msgout_new = create_msg_out(msgout->msg);
395 msgout_new->return_path = msgout->return_path;
396 msgout_new->hdr_list = msgout->hdr_list;
398 /* append our rcpt to it */
399 /* It is the 1st rcpt for this msg to this host, therefore we safely give NULL */
400 msgout_new->rcpt_list = g_list_append(NULL, rcpt);
401 mo_ph->msgout_list = g_list_append(mo_ph->msgout_list, msgout_new);
402 }
403 } else {
404 /* this rcpt to goes to another host */
405 mo_ph = create_msgout_perhost(rcpt->domain);
406 mo_ph_list = g_list_append(mo_ph_list, mo_ph);
408 /* make a copy of msgout */
409 msgout_new = create_msg_out(msgout->msg);
410 msgout_new->return_path = msgout->return_path;
411 msgout_new->hdr_list = msgout->hdr_list;
413 /* append our rcpt to it */
414 /* It is the 1st rcpt for this msg to this host, therefore we safely give NULL */
415 msgout_new->rcpt_list = g_list_append(NULL, rcpt);
416 mo_ph->msgout_list = g_list_append(mo_ph->msgout_list, msgout_new);
417 } /* if mo_ph != NULL */
418 } /* foreach(rcpt_list, ... */
419 } /* foreach(msgout_list, ... */
421 return mo_ph_list;
422 }