masqmail-0.2

view src/route.c @ 75:257a9e6d1a8e

fixed correct processing of mails with data lines longer 4096 chars Mail messages with lines longer than 4096 chars were already read correctly, i.e. the spool files were correct. This commit fixes the reading of spool files with long lines. The old behavior was that the message body was truncated right before the first line longer 4096 chars. The number comes from MAX_DATALINE.
author meillo@marmaro.de
date Wed, 16 Jun 2010 19:06:34 +0200
parents 26e34ae9a3e3
children a80ebfa16cd5
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", 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 }