masqmail-0.2

view src/accept.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 7c1635972aa7
children f4719cffc48c
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 "masqmail.h"
20 #include "readsock.h"
22 gchar *prot_names[] = {
23 "local",
24 "bsmtp",
25 "smtp",
26 "esmtp",
27 "pop3",
28 "apop",
29 "(unknown)" /* should not happen, but better than crashing. */
30 };
32 static gchar*
33 string_base62(gchar * res, guint value, gchar len)
34 {
35 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
36 gchar *p = res + len;
37 *p = '\0';
38 while (p > res) {
39 *(--p) = base62_chars[value % 62];
40 value /= 62;
41 }
42 return res;
43 }
45 static gint
46 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
47 {
48 address *addr1 = (address *) a;
49 address *addr2 = (address *) b;
50 int ret;
52 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0)
53 return strcmp(addr1->local_part, addr2->local_part);
54 else
55 return ret;
56 }
58 /* accept message from anywhere.
59 A locally originating message is indicated by msg->recieved_host == NULL
61 If the flags ACC_DEL_RCPTS is set, recipients in the msg->rcpt_list is
62 copied and items occuring in it will be removed from the newly constructed
63 (from To/Cc/Bcc headers if ACC_RCPT_TO is set) rcpt_list.
64 */
66 accept_error
67 accept_message_stream(FILE * in, message * msg, guint flags)
68 {
69 gchar *line, *line1;
70 int line_size = MAX_DATALINE;
71 gboolean in_headers = TRUE;
72 header *hdr = NULL;
73 gint line_cnt = 0, data_size = 0;
75 line = g_malloc(line_size);
76 line[0] = '\0';
78 while (TRUE) {
79 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
81 line1 = line;
83 if ((line[0] == '.') && (!(flags & ACC_NODOT_TERM))) {
84 if (line[1] == '\n') {
85 g_free(line);
86 break;
87 }
88 line1++;
89 }
91 if (len <= 0) {
92 if ((len == -1) && ((flags & ACC_NODOT_TERM) || (flags & ACC_NODOT_RELAX))) {
93 /* we got an EOF, and the last line was not terminated by a CR */
94 gint len1 = strlen(line1);
95 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
96 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
97 line1[len1] = '\n';
98 line1[len1 + 1] = '\0';
99 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
100 data_size += strlen(line1);
101 line_cnt++;
102 }
103 }
104 break;
105 } else {
106 g_free(line);
107 if (len == -1) {
108 return AERR_EOF;
109 } else if (len == -2) {
110 /* should not happen any more */
111 return AERR_OVERFLOW;
112 } else if (len == -3) {
113 return AERR_TIMEOUT;
114 } else {
115 /* does not happen */
116 DEBUG(5) debugf("read_sockline returned %d\n", len);
117 return AERR_UNKNOWN;
118 }
119 }
120 } else {
121 if (in_headers) {
123 /* some pop servers send the 'From ' line, skip it: */
124 if (msg->hdr_list == NULL)
125 if (strncmp(line1, "From ", 5) == 0)
126 continue;
128 if (line1[0] == ' ' || line1[0] == '\t') {
129 /* continuation of 'folded' header: */
130 if (hdr) {
131 hdr->header = g_strconcat(hdr->header, line1, NULL);
132 }
134 } else if (line1[0] == '\n') {
135 /* an empty line marks end of headers */
136 in_headers = FALSE;
137 } else {
138 /* in all other cases we expect another header */
139 if ((hdr = get_header(line1)))
140 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
141 else {
142 /* if get_header() returns NULL, no header was recognized,
143 so this seems to be the first data line of a broken mailer
144 which does not send an empty line after the headers */
145 in_headers = FALSE;
146 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
147 }
148 }
149 } else {
150 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
151 data_size += strlen(line1);
152 line_cnt++;
153 }
154 }
155 }
157 if (msg->data_list != NULL)
158 msg->data_list = g_list_reverse(msg->data_list);
159 else
160 /* make sure data list is not NULL: */
161 msg->data_list = g_list_append(NULL, g_strdup(""));
163 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
164 /* we get here after we succesfully received the mail data */
166 msg->data_size = data_size;
167 msg->received_time = time(NULL);
169 return AERR_OK;
170 }
172 accept_error
173 accept_message_prepare(message * msg, guint flags)
174 {
175 struct passwd *passwd = NULL;
176 GList *non_rcpt_list = NULL;
177 time_t rec_time = time(NULL);
179 DEBUG(5) debugf("accept_message_prepare()\n");
181 /* create unique message id */
182 msg->uid = g_malloc(14);
184 string_base62(msg->uid, rec_time, 6);
185 msg->uid[6] = '-';
186 string_base62(&(msg->uid[7]), getpid(), 3);
187 msg->uid[10] = '-';
188 string_base62(&(msg->uid[11]), msg->transfer_id, 2);
189 msg->uid[13] = 0;
191 /* if local, get password entry */
192 if (msg->received_host == NULL) {
193 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
194 msg->ident = g_strdup(passwd->pw_name);
195 }
197 /* set return path if local */
198 if (msg->return_path == NULL && msg->received_host == NULL) {
199 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
200 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
201 msg->return_path = create_address(path, TRUE);
202 g_free(path);
203 }
205 /* -t option */
206 if (flags & ACC_DEL_RCPTS) {
207 non_rcpt_list = msg->rcpt_list;
208 msg->rcpt_list = NULL;
209 }
211 /* scan headers */
212 {
213 gboolean has_id = FALSE;
214 gboolean has_date = FALSE;
215 gboolean has_sender = FALSE;
216 gboolean has_from = FALSE;
217 gboolean has_rcpt = FALSE;
218 gboolean has_to_or_cc = FALSE;
219 GList *hdr_node, *hdr_node_next;
220 header *hdr;
222 for (hdr_node = g_list_first(msg->hdr_list);
223 hdr_node != NULL; hdr_node = hdr_node_next) {
224 hdr_node_next = g_list_next(hdr_node);
225 hdr = ((header *) (hdr_node->data));
226 DEBUG(5) debugf("scanning headers: %s", hdr->header);
227 switch (hdr->id) {
228 case HEAD_MESSAGE_ID:
229 has_id = TRUE;
230 break;
231 case HEAD_DATE:
232 has_date = TRUE;
233 break;
234 case HEAD_FROM:
235 has_from = TRUE;
236 break;
237 case HEAD_SENDER:
238 has_sender = TRUE;
239 break;
240 case HEAD_TO:
241 case HEAD_CC:
242 case HEAD_BCC:
243 has_rcpt = TRUE;
244 if (flags & ACC_RCPT_FROM_HEAD) {
245 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
246 if (hdr->value) {
247 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
248 }
249 }
250 if ((flags & ACC_DEL_BCC) && (hdr->id == HEAD_BCC)) {
251 DEBUG(3) debugf("removing 'Bcc' header\n");
252 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
253 g_list_free_1(hdr_node);
254 destroy_header(hdr);
255 } else
256 has_to_or_cc = TRUE;
257 break;
258 case HEAD_ENVELOPE_TO:
259 if (flags & ACC_SAVE_ENVELOPE_TO) {
260 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
261 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
262 "X-Orig-Envelope-to: %s", hdr->value));
263 }
264 DEBUG(3) debugf("removing 'Envelope-To' header\n");
265 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
266 g_list_free_1(hdr_node);
267 destroy_header(hdr);
268 break;
269 case HEAD_RETURN_PATH:
270 if (flags & ACC_MAIL_FROM_HEAD) {
271 /* usually POP3 accept */
272 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
273 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
274 }
275 DEBUG(3) debugf("removing 'Return-Path' header\n");
276 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
277 g_list_free_1(hdr_node);
278 destroy_header(hdr);
279 break;
280 default:
281 break; /* make compiler happy */
282 }
283 }
285 if (msg->return_path == NULL) {
286 /* this can happen for pop3 accept only and if no Return-path: header was given */
287 GList *hdr_list;
288 header *hdr;
290 DEBUG(3) debugf("return_path == NULL\n");
292 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
293 if (!hdr_list)
294 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
295 if (hdr_list) {
296 gchar *addr;
297 hdr = (header *) (g_list_first(hdr_list)->data);
299 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
301 addr = g_strdup(hdr->value);
302 g_strchomp(addr);
304 if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) {
305 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
306 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
307 "X-Warning: return path set from %s address\n",
308 hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
309 }
310 g_free(addr);
311 }
312 if (msg->return_path == NULL) { /* no Sender: or From: or create_address_qualified failed */
313 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
314 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
315 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
316 "X-Warning: real return path is unknown\n"));
317 }
318 }
320 if (flags & ACC_DEL_RCPTS) {
321 GList *rcpt_node;
322 foreach(non_rcpt_list, rcpt_node) {
323 address *rcpt = (address *) (rcpt_node->data);
324 GList *node;
325 if ((node = g_list_find_custom(msg->rcpt_list, rcpt, _g_list_addr_isequal))) {
326 DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data));
327 msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node);
328 destroy_address((address *) (node->data));
329 g_list_free_1(node);
330 }
331 }
332 }
334 /* here we should have our recipients, fail if not: */
335 if (msg->rcpt_list == NULL) {
336 logwrite(LOG_WARNING, "no recipients found in message\n");
337 return AERR_NORCPT;
338 }
340 if (!(has_sender || has_from)) {
341 DEBUG(3) debugf("adding 'From' header\n");
342 msg->hdr_list = g_list_append(msg->hdr_list,
343 msg->full_sender_name
344 ?
345 create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name,
346 msg->return_path->local_part, msg->return_path->domain)
347 :
348 create_header(HEAD_FROM, "From: <%s@%s>\n",
349 msg->return_path->local_part, msg->return_path->domain)
350 );
351 }
352 if ((flags & ACC_HEAD_FROM_RCPT) && !has_rcpt) {
353 GList *node;
354 DEBUG(3) debugf("adding 'To' header(s)\n");
355 for (node = g_list_first(msg->rcpt_list); node; node = g_list_next(node)) {
356 msg->hdr_list = g_list_append(msg->hdr_list,
357 create_header(HEAD_TO, "To: %s\n", addr_string(msg-> return_path)));
358 }
359 }
360 if ((flags & ACC_DEL_BCC) && !has_to_or_cc) {
361 /* Bcc headers have been removed, and there are no remaining rcpt headers */
362 DEBUG(3) debugf("adding empty 'Bcc:' header\n");
363 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_BCC, "Bcc:\n"));
364 }
365 if (!has_date) {
366 DEBUG(3) debugf("adding 'Date:' header\n");
367 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
368 }
369 if (!has_id) {
370 DEBUG(3) debugf("adding 'Message-ID:' header\n");
371 msg->hdr_list = g_list_append(msg->hdr_list,
372 create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
373 }
374 }
376 /* Received header: */
377 /* At this point because we have to know the rcpts for the 'for' part */
378 if (!(flags & ACC_NO_RECVD_HDR)) {
379 gchar *for_string = NULL;
380 header *hdr = NULL;
382 DEBUG(3) debugf("adding 'Received:' header\n");
384 if (g_list_length(msg->rcpt_list) == 1) {
385 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
386 for_string = g_strdup_printf(" for %s", addr_string(addr));
387 }
389 if (msg->received_host == NULL) {
390 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
391 passwd->pw_name, conf.host_name, prot_names[msg->received_prot],
392 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
393 } else {
394 #ifdef ENABLE_IDENT
395 DEBUG(5) debugf("adding 'Received:' header (5)\n");
396 hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n",
397 msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name,
398 prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "",
399 rec_timestamp());
400 #else
401 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
402 msg->received_host, conf.host_name, prot_names[msg->received_prot],
403 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
404 #endif
405 }
406 header_fold(hdr);
407 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
409 if (for_string)
410 g_free(for_string);
411 }
413 /* write message to spool: */
414 /* accept is no longer responsible for this
415 if (!spool_write(msg, TRUE))
416 return AERR_NOSPOOL;
417 */
418 return AERR_OK;
419 }
421 accept_error
422 accept_message(FILE * in, message * msg, guint flags)
423 {
424 accept_error err;
426 err = accept_message_stream(in, msg, flags);
427 if (err == AERR_OK)
428 err = accept_message_prepare(msg, flags);
430 return err;
431 }