masqmail

view src/route.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents b27f66555ba8
children 5593964ec779
line source
1 /*
2 ** MasqMail
3 ** Copyright (C) 1999-2001 Oliver Kurth
4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
21 #include <fnmatch.h>
23 #include "masqmail.h"
25 msgout_perhost*
26 create_msgout_perhost(gchar *host)
27 {
28 msgout_perhost *mo_ph = g_malloc(sizeof(msgout_perhost));
29 if (mo_ph) {
30 mo_ph->host = g_strdup(host);
31 mo_ph->msgout_list = NULL;
32 }
33 return mo_ph;
34 }
36 void
37 destroy_msgout_perhost(msgout_perhost *mo_ph)
38 {
39 GList *mo_node;
41 foreach(mo_ph->msgout_list, mo_node) {
42 msg_out *mo = (msg_out *) (mo_node->data);
43 /* the rcpt_list is owned by the msgout's, but not the rcpt's themselves */
44 g_list_free(mo->rcpt_list);
45 g_free(mo);
46 }
47 g_list_free(mo_ph->msgout_list);
48 g_free(mo_ph);
49 }
51 void
52 rewrite_headers(msg_out *msgout, connect_route *route)
53 {
54 /*
55 ** if set_h_from_domain is set, replace domain in all
56 ** From: headers.
57 */
58 msgout->hdr_list = g_list_copy(msgout->msg->hdr_list);
60 /* map from addresses */
61 if (route->map_h_from_addresses != NULL) {
62 GList *hdr_node;
63 foreach(msgout->hdr_list, hdr_node) {
64 header *hdr = (header *) (hdr_node->data);
65 if (hdr->id == HEAD_FROM) {
66 header *new_hdr = copy_header(hdr);
67 if (map_address_header(new_hdr, route->map_h_from_addresses)) {
68 hdr_node->data = new_hdr;
69 /* we need this list only to carefully free the extra headers: */
70 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
71 } else
72 g_free(new_hdr);
73 }
74 }
75 } else {
76 /* replace from domain */
77 if (route->set_h_from_domain != NULL) {
78 GList *hdr_node;
80 foreach(msgout->hdr_list, hdr_node) {
81 header *hdr = (header *) (hdr_node->data);
82 if (hdr->id == HEAD_FROM) {
83 header *new_hdr = copy_header(hdr);
85 DEBUG(5) debugf("setting From: domain to %s\n", route->set_h_from_domain);
86 if (set_address_header_domain(new_hdr, route->set_h_from_domain)) {
87 hdr_node->data = new_hdr;
88 /* we need this list only to carefully free the extra headers: */
89 DEBUG(6) debugf("header = %s\n", new_hdr->header);
90 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
91 } else {
92 logwrite(LOG_ALERT, "error in set_address_header_domain(%s, %s)\n",
93 new_hdr->value, route->set_h_from_domain);
94 }
95 }
96 }
97 }
98 }
100 /* map reply-to addresses */
101 if (route->map_h_reply_to_addresses != NULL) {
102 GList *hdr_node;
103 foreach(msgout->hdr_list, hdr_node) {
104 header *hdr = (header *) (hdr_node->data);
105 if (hdr->id == HEAD_REPLY_TO) {
106 header *new_hdr = copy_header(hdr);
107 if (map_address_header
108 (new_hdr, route->map_h_reply_to_addresses)) {
109 hdr_node->data = new_hdr;
110 /* we need this list only to carefully free the extra headers: */
111 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
112 } else
113 g_free(new_hdr);
114 }
115 }
116 } else {
117 /* replace Reply-to domain */
118 if (route->set_h_reply_to_domain != NULL) {
119 GList *hdr_node;
121 foreach(msgout->hdr_list, hdr_node) {
122 header *hdr = (header *) (hdr_node->data);
123 if (hdr->id == HEAD_REPLY_TO) {
124 header *new_hdr = copy_header(hdr);
126 set_address_header_domain(new_hdr, route-> set_h_reply_to_domain);
127 hdr_node->data = new_hdr;
128 /* we need this list only to carefully free the extra headers: */
129 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
130 }
131 }
132 }
133 }
135 /* map Mail-Followup-To addresses */
136 if (route->map_h_mail_followup_to_addresses != NULL) {
137 GList *hdr_node;
138 foreach(msgout->hdr_list, hdr_node) {
139 header *hdr = (header *) (hdr_node->data);
140 if (strncasecmp(hdr->header, "Mail-Followup-To", 16) == 0) {
141 header *new_hdr = copy_header(hdr);
142 if (map_address_header(new_hdr, route->map_h_mail_followup_to_addresses)) {
143 hdr_node->data = new_hdr;
144 /* we need this list only to carefully free the extra headers: */
145 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
146 } else
147 g_free(new_hdr);
148 }
149 }
150 }
152 /* set Sender: domain to return_path->domain */
153 if (route->expand_h_sender_domain) {
154 GList *hdr_node;
156 foreach(msgout->hdr_list, hdr_node) {
157 header *hdr = (header *) (hdr_node->data);
158 if (hdr->id == HEAD_SENDER) {
159 header *new_hdr = copy_header(hdr);
161 set_address_header_domain(new_hdr, msgout->return_path->domain);
162 hdr_node->data = new_hdr;
163 /* we need this list only to carefully free the extra headers: */
164 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
165 }
166 }
167 }
169 /* set Sender: domain to return_path->domain */
170 if (route->expand_h_sender_address) {
171 GList *hdr_node;
173 foreach(msgout->hdr_list, hdr_node) {
174 header *hdr = (header *) (hdr_node->data);
175 if (hdr->id == HEAD_SENDER) {
176 header *new_hdr;
178 new_hdr = create_header(HEAD_SENDER, "Sender: %s@%s\n",
179 msgout->return_path->local_part, msgout->return_path->domain);
180 hdr_node->data = new_hdr;
181 /* we need this list only to carefully free the extra headers: */
182 msgout->xtra_hdr_list = g_list_append(msgout->xtra_hdr_list, new_hdr);
183 }
184 }
185 }
187 if (msgout->xtra_hdr_list == NULL) {
188 /* nothing was changed */
189 g_list_free(msgout->hdr_list);
190 msgout->hdr_list = NULL;
191 }
192 DEBUG(5) debugf("rewrite_headers() returning\n");
193 }
195 /*
196 ** Split a recipient list into the three groups:
197 ** - local recipients
198 ** - those maching the patterns
199 ** - those not matching the patterns
200 ** If patterns is NULL: only splitting between local and others is done.
201 */
202 void
203 split_rcpts(GList *rcpt_list, GList *patterns, GList **rl_local,
204 GList **rl_matching, GList **rl_others)
205 {
206 GList *rcpt_node;
207 GList *pat_node = NULL;
208 address *rcpt = NULL;
210 if (rcpt_list == NULL)
211 return;
213 foreach(rcpt_list, rcpt_node) {
214 rcpt = (address *) (rcpt_node->data);
215 pat_node = NULL;
217 if (addr_is_local(rcpt)) {
218 if (rl_local)
219 *rl_local = g_list_append(*rl_local, rcpt);
220 } else {
221 /*
222 ** if patterns is NULL, pat_node will be NULL,
223 ** hence all non-locals are put to others
224 */
225 foreach(patterns, pat_node) {
226 address *pat = (address *) (pat_node->data);
227 if (fnmatch(pat->domain, rcpt->domain, FNM_CASEFOLD)==0 && fnmatch(pat->local_part, rcpt->local_part, 0)==0) { /* TODO: match local_part caseless? */
228 break;
229 }
230 }
231 if (pat_node) {
232 if (rl_matching)
233 *rl_matching = g_list_append(*rl_matching, rcpt);
234 } else {
235 if (rl_others)
236 *rl_others = g_list_append(*rl_others, rcpt);
237 }
238 }
239 }
240 }
242 /*
243 ** Return a new list of the local rcpts in the rcpt_list
244 ** TODO: This function is almost exactly the same as remote_rcpts(). Merge?
245 */
246 GList*
247 local_rcpts(GList *rcpt_list)
248 {
249 GList *rcpt_node;
250 GList *local_rcpts = NULL;
251 address *rcpt = NULL;
253 if (!rcpt_list) {
254 return NULL;
255 }
256 foreach(rcpt_list, rcpt_node) {
257 rcpt = (address *) (rcpt_node->data);
258 if (addr_is_local(rcpt)) {
259 local_rcpts = g_list_append(local_rcpts, rcpt);
260 }
261 }
262 return local_rcpts;
263 }
265 /*
266 ** Return a new list of non-local rcpts in the rcpt_list
267 ** TODO: This function is almost exactly the same as local_rcpts(). Merge?
268 */
269 GList*
270 remote_rcpts(GList *rcpt_list)
271 {
272 GList *rcpt_node;
273 GList *remote_rcpts = NULL;
274 address *rcpt = NULL;
276 if (!rcpt_list) {
277 return NULL;
278 }
279 foreach(rcpt_list, rcpt_node) {
280 rcpt = (address *) (rcpt_node->data);
281 if (!addr_is_local(rcpt)) {
282 remote_rcpts = g_list_append(remote_rcpts, rcpt);
283 }
284 }
285 return remote_rcpts;
286 }
288 static gint
289 _g_list_addrcmp(gconstpointer pattern, gconstpointer addr)
290 {
291 int res;
292 address *patternaddr = (address*) pattern;
293 address *stringaddr = (address*) addr;
295 DEBUG(6) debugf("_g_list_addrcmp: pattern `%s' `%s' on string `%s' `%s'\n",
296 patternaddr->local_part, patternaddr->domain,
297 stringaddr->local_part, stringaddr->domain);
298 /* TODO: check if we should match here dependent on caseless_matching */
299 res = fnmatch(patternaddr->local_part, stringaddr->local_part, 0);
300 if (res != 0) {
301 DEBUG(6) debugf("_g_list_addrcmp: ... failed on local_part\n");
302 return res;
303 }
304 res = fnmatch(patternaddr->domain, stringaddr->domain, FNM_CASEFOLD);
305 DEBUG(6) debugf("_g_list_addrcmp: ... %s\n", (res==0) ? "matched" : "failed on domain");
306 return res;
307 }
309 gboolean
310 route_sender_is_allowed(connect_route *route, address *ret_path)
311 {
312 if (route->denied_senders && g_list_find_custom(route->denied_senders, ret_path, _g_list_addrcmp)) {
313 return FALSE;
314 }
315 if (route->allowed_senders) {
316 if (g_list_find_custom(route->allowed_senders, ret_path, _g_list_addrcmp)) {
317 return TRUE;
318 } else {
319 return FALSE;
320 }
321 }
322 return TRUE;
323 }
325 /*
326 ** Make lists of matching/not matching rcpts.
327 ** Local domains are NOT regared here, these should be sorted out previously
328 */
329 void
330 route_split_rcpts(connect_route *route, GList *rcpt_list, GList **p_rcpt_list, GList **p_non_rcpt_list)
331 {
332 GList *tmp_list = NULL;
333 /* sort out those domains that can be sent over this connection: */
334 if (route->allowed_recipients) {
335 DEBUG(5) debugf("testing for route->allowed_recipients\n");
336 split_rcpts(rcpt_list, route->allowed_recipients, NULL, &tmp_list, p_non_rcpt_list);
337 } else {
338 DEBUG(5) debugf("route->allowed_recipients == NULL\n");
339 tmp_list = g_list_copy(rcpt_list);
340 }
342 /* sort out those domains that cannot be sent over this connection: */
343 split_rcpts(tmp_list, route->denied_recipients, NULL, p_non_rcpt_list, p_rcpt_list);
344 g_list_free(tmp_list);
345 }
347 msg_out*
348 route_prepare_msgout(connect_route *route, msg_out *msgout)
349 {
350 message *msg = msgout->msg;
351 GList *rcpt_list = msgout->rcpt_list;
353 if (rcpt_list != NULL) {
354 /* found a few */
355 DEBUG(5) {
356 GList *node;
357 debugf("rcpts for routed delivery, route = %s, id = %s\n", route->name, msg->uid);
358 foreach(rcpt_list, node) {
359 address *rcpt = (address *) (node->data);
360 debugf(" rcpt for routed delivery: <%s@%s>\n",
361 rcpt->local_part, rcpt->domain);
362 }
363 }
365 /*
366 ** rewrite return path if there is a table, use that
367 ** if an address is found and if it has a domain, use that
368 */
369 if (route->map_return_path_addresses) {
370 address *ret_path = NULL;
371 DEBUG(5) debugf("looking up %s in map_return_path_addresses\n", msg->return_path->local_part);
372 ret_path = (address *) table_find_fnmatch(route->map_return_path_addresses, msg->return_path->local_part);
373 if (ret_path) {
374 DEBUG(5) debugf("found <%s@%s>\n", ret_path->local_part, ret_path->domain);
375 if (ret_path->domain == NULL)
376 ret_path->domain = route->set_return_path_domain
377 ? route->set_return_path_domain
378 : msg->return_path->domain;
379 msgout->return_path = copy_address(ret_path);
380 }
381 }
382 if (msgout->return_path == NULL) {
383 DEBUG(5) debugf("setting return path to %s\n", route->set_return_path_domain);
384 msgout->return_path = copy_modify_address(msg->return_path, NULL, route->set_return_path_domain);
385 }
386 rewrite_headers(msgout, route);
388 return msgout;
389 }
390 return NULL;
391 }
393 /*
394 ** put msgout's is msgout_list into bins (msgout_perhost structs) for each
395 ** host. Used if there is no mail_host.
396 ** route param is not used, we leave it here because that may change.
397 */
398 GList*
399 route_msgout_list(connect_route *route, GList *msgout_list)
400 {
401 GList *mo_ph_list = NULL;
402 GList *msgout_node;
404 foreach(msgout_list, msgout_node) {
405 msg_out *msgout = (msg_out *) (msgout_node->data);
406 msg_out *msgout_new;
407 GList *rcpt_list = msgout->rcpt_list;
408 GList *rcpt_node;
410 foreach(rcpt_list, rcpt_node) {
411 address *rcpt = rcpt_node->data;
412 msgout_perhost *mo_ph = NULL;
413 GList *mo_ph_node = NULL;
415 /* search host in mo_ph_list */
416 foreach(mo_ph_list, mo_ph_node) {
417 mo_ph = (msgout_perhost *) (mo_ph_node->data);
418 if (strcasecmp(mo_ph->host, rcpt->domain) == 0)
419 break;
420 }
421 if (mo_ph_node != NULL) {
422 /* there is already a rcpt for this host */
423 msg_out *msgout_last = (msg_out *) ((g_list_last(mo_ph->msgout_list))->data);
424 if (msgout_last->msg == msgout->msg) {
425 /*
426 ** if it is also the same message,
427 ** it must be the last one
428 ** appended to mo_ph->msgout_list
429 ** (since outer loop goes through
430 ** msgout_list)
431 */
432 msgout_last->rcpt_list = g_list_append(msgout_last->rcpt_list, rcpt);
433 } else {
434 /* if not, we append a new msgout */
435 /* make a copy of msgout */
436 msgout_new = create_msg_out(msgout->msg);
437 msgout_new->return_path = msgout->return_path;
438 msgout_new->hdr_list = msgout->hdr_list;
440 /* append our rcpt to it */
441 /* It is the 1st rcpt for this msg to this host, therefore we safely give NULL */
442 msgout_new->rcpt_list = g_list_append(NULL, rcpt);
443 mo_ph->msgout_list = g_list_append(mo_ph->msgout_list, msgout_new);
444 }
445 } else {
446 /* this rcpt to goes to another host */
447 mo_ph = create_msgout_perhost(rcpt->domain);
448 mo_ph_list = g_list_append(mo_ph_list, mo_ph);
450 /* make a copy of msgout */
451 msgout_new = create_msg_out(msgout->msg);
452 msgout_new->return_path = msgout->return_path;
453 msgout_new->hdr_list = msgout->hdr_list;
455 /* append our rcpt to it */
456 /* It is the 1st rcpt for this msg to this host, therefore we safely give NULL */
457 msgout_new->rcpt_list = g_list_append(NULL, rcpt);
458 mo_ph->msgout_list = g_list_append(mo_ph->msgout_list, msgout_new);
459 } /* if mo_ph != NULL */
460 } /* foreach(rcpt_list, ... */
461 } /* foreach(msgout_list, ... */
463 return mo_ph_list;
464 }