masqmail-0.2

view src/accept.c @ 179:ec3fe72a3e99

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:52:17 +0200
parents 087e99c7702a
children
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 "masqmail.h"
21 #include "readsock.h"
23 gchar *prot_names[] = {
24 "local",
25 "bsmtp",
26 "smtp",
27 "esmtp",
28 "pop3",
29 "apop",
30 "(unknown)" /* should not happen, but better than crashing. */
31 };
33 static gchar*
34 string_base62(gchar * res, guint value, gchar len)
35 {
36 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
37 gchar *p = res + len;
38 *p = '\0';
39 while (p > res) {
40 *(--p) = base62_chars[value % 62];
41 value /= 62;
42 }
43 return res;
44 }
46 static gint
47 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
48 {
49 address *addr1 = (address *) a;
50 address *addr2 = (address *) b;
51 int ret;
53 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0)
54 return strcmp(addr1->local_part, addr2->local_part);
55 else
56 return ret;
57 }
59 /* accept message from anywhere.
60 A locally originating message is indicated by msg->recieved_host == NULL
62 The -t option:
63 The ACC_RCPT_FROM_HEAD flag adds the recipients found in To/Cc/Bcc
64 headers to the recipients list.
65 The ACC_DEL_RCPTS flag makes only sense if the ACC_RCPT_FROM_HEAD
66 flag is given too. With ACC_DEL_RCPTS the recipients given on the
67 command line are removed from the ones given in headers.
68 */
70 accept_error
71 accept_message_stream(FILE * in, message * msg, guint flags)
72 {
73 gchar *line, *line1;
74 int line_size = MAX_DATALINE;
75 gboolean in_headers = TRUE;
76 header *hdr = NULL;
77 gint line_cnt = 0, data_size = 0;
79 line = g_malloc(line_size);
80 line[0] = '\0';
82 while (TRUE) {
83 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
85 line1 = line;
87 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
88 if (line[1] == '\n') {
89 g_free(line);
90 break;
91 }
92 line1++;
93 }
95 if (len <= 0) {
96 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
97 /* we got an EOF, and the last line was not terminated by a CR */
98 gint len1 = strlen(line1);
99 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
100 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
101 line1[len1] = '\n';
102 line1[len1 + 1] = '\0';
103 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
104 data_size += strlen(line1);
105 line_cnt++;
106 }
107 }
108 break;
109 } else {
110 g_free(line);
111 if (len == -1) {
112 return AERR_EOF;
113 } else if (len == -2) {
114 /* should not happen any more */
115 return AERR_OVERFLOW;
116 } else if (len == -3) {
117 return AERR_TIMEOUT;
118 } else {
119 /* does not happen */
120 DEBUG(5) debugf("read_sockline returned %d\n", len);
121 return AERR_UNKNOWN;
122 }
123 }
124 } else {
125 if (in_headers) {
127 /* some pop servers send the 'From ' line, skip it: */
128 if (msg->hdr_list == NULL)
129 if (strncmp(line1, "From ", 5) == 0)
130 continue;
132 if (line1[0] == ' ' || line1[0] == '\t') {
133 /* continuation of 'folded' header: */
134 if (hdr) {
135 char* cp;
136 cp = g_strconcat(hdr->header, line1, NULL);
137 hdr->value = cp + (hdr->value - hdr->header);
138 free(hdr->header);
139 hdr->header = cp;
140 }
142 } else if (line1[0] == '\n') {
143 /* an empty line marks end of headers */
144 in_headers = FALSE;
145 } else {
146 /* in all other cases we expect another header */
147 if ((hdr = get_header(line1)))
148 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
149 else {
150 /* if get_header() returns NULL, no header was recognized,
151 so this seems to be the first data line of a broken mailer
152 which does not send an empty line after the headers */
153 in_headers = FALSE;
154 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
155 }
156 }
157 } else {
158 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
159 data_size += strlen(line1);
160 line_cnt++;
161 }
162 }
163 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
164 DEBUG(4) debugf("accept_message_stream(): "
165 "received %d bytes (conf.max_msg_size=%d)\n",
166 data_size, conf.max_msg_size);
167 return AERR_SIZE;
168 }
170 }
172 if (msg->data_list != NULL)
173 msg->data_list = g_list_reverse(msg->data_list);
174 else
175 /* make sure data list is not NULL: */
176 msg->data_list = g_list_append(NULL, g_strdup(""));
178 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
179 /* we get here after we succesfully received the mail data */
181 msg->data_size = data_size;
182 msg->received_time = time(NULL);
184 return AERR_OK;
185 }
187 accept_error
188 accept_message_prepare(message * msg, guint flags)
189 {
190 struct passwd *passwd = NULL;
191 GList *non_rcpt_list = NULL;
192 time_t rec_time = time(NULL);
194 DEBUG(5) debugf("accept_message_prepare()\n");
196 /* create unique message id */
197 msg->uid = g_malloc(14);
199 string_base62(msg->uid, rec_time, 6);
200 msg->uid[6] = '-';
201 string_base62(&(msg->uid[7]), getpid(), 3);
202 msg->uid[10] = '-';
203 string_base62(&(msg->uid[11]), msg->transfer_id, 2);
204 msg->uid[13] = 0;
206 /* if local, get password entry */
207 if (msg->received_host == NULL) {
208 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
209 msg->ident = g_strdup(passwd->pw_name);
210 }
212 /* set return path if local */
213 if (msg->return_path == NULL && msg->received_host == NULL) {
214 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
215 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
216 msg->return_path = create_address(path, TRUE);
217 g_free(path);
218 }
220 /* -t option (see comment above) */
221 if (flags & ACC_DEL_RCPTS) {
222 non_rcpt_list = msg->rcpt_list;
223 msg->rcpt_list = NULL;
224 }
226 /* scan headers */
227 {
228 gboolean has_id = FALSE;
229 gboolean has_date = FALSE;
230 gboolean has_sender = FALSE;
231 gboolean has_from = FALSE;
232 gboolean has_to_or_cc = FALSE;
233 GList *hdr_node, *hdr_node_next;
234 header *hdr;
236 for (hdr_node = g_list_first(msg->hdr_list);
237 hdr_node != NULL; hdr_node = hdr_node_next) {
238 hdr_node_next = g_list_next(hdr_node);
239 hdr = ((header *) (hdr_node->data));
240 DEBUG(5) debugf("scanning headers: %s", hdr->header);
241 switch (hdr->id) {
242 case HEAD_MESSAGE_ID:
243 has_id = TRUE;
244 break;
245 case HEAD_DATE:
246 has_date = TRUE;
247 break;
248 case HEAD_FROM:
249 has_from = TRUE;
250 break;
251 case HEAD_SENDER:
252 has_sender = TRUE;
253 break;
254 case HEAD_TO:
255 case HEAD_CC:
256 has_to_or_cc = TRUE;
257 /* fall through */
258 case HEAD_BCC:
259 if (flags & ACC_RCPT_FROM_HEAD) {
260 /* -t option (see comment above) */
261 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
262 if (hdr->value) {
263 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
264 }
265 }
266 if (hdr->id == HEAD_BCC) {
267 DEBUG(3) debugf("removing 'Bcc' header\n");
268 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
269 g_list_free_1(hdr_node);
270 destroy_header(hdr);
271 }
272 break;
273 case HEAD_ENVELOPE_TO:
274 if (flags & ACC_SAVE_ENVELOPE_TO) {
275 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
276 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
277 "X-Orig-Envelope-to: %s", hdr->value));
278 }
279 DEBUG(3) debugf("removing 'Envelope-To' header\n");
280 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
281 g_list_free_1(hdr_node);
282 destroy_header(hdr);
283 break;
284 case HEAD_RETURN_PATH:
285 if (flags & ACC_MAIL_FROM_HEAD) {
286 /* usually POP3 accept */
287 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
288 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
289 }
290 DEBUG(3) debugf("removing 'Return-Path' header\n");
291 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
292 g_list_free_1(hdr_node);
293 destroy_header(hdr);
294 break;
295 default:
296 break; /* make compiler happy */
297 }
298 }
300 if (msg->return_path == NULL) {
301 /* this can happen for pop3 accept only and if no Return-path: header was given */
302 GList *hdr_list;
303 header *hdr;
305 DEBUG(3) debugf("return_path == NULL\n");
307 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
308 if (!hdr_list)
309 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
310 if (hdr_list) {
311 gchar *addr;
312 hdr = (header *) (g_list_first(hdr_list)->data);
314 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
316 addr = g_strdup(hdr->value);
317 g_strchomp(addr);
319 if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) {
320 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
321 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
322 "X-Warning: return path set from %s address\n",
323 hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
324 }
325 g_free(addr);
326 }
327 if (msg->return_path == NULL) { /* no Sender: or From: or create_address_qualified failed */
328 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
329 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
330 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
331 "X-Warning: real return path is unknown\n"));
332 }
333 }
335 if (flags & ACC_DEL_RCPTS) {
336 /* remove the recipients given on the command line from the ones given in headers
337 -t option (see comment above) */
338 GList *rcpt_node;
339 foreach(non_rcpt_list, rcpt_node) {
340 address *rcpt = (address *) (rcpt_node->data);
341 GList *node;
342 if ((node = g_list_find_custom(msg->rcpt_list, rcpt, _g_list_addr_isequal))) {
343 DEBUG(3) debugf("removing rcpt address %s\n", addr_string(node->data));
344 msg->rcpt_list = g_list_remove_link(msg->rcpt_list, node);
345 destroy_address((address *) (node->data));
346 g_list_free_1(node);
347 }
348 }
349 }
351 /* here we should have our recipients, fail if not: */
352 if (msg->rcpt_list == NULL) {
353 logwrite(LOG_WARNING, "no recipients found in message\n");
354 return AERR_NORCPT;
355 }
357 if (!(has_sender || has_from)) {
358 DEBUG(3) debugf("adding 'From' header\n");
359 msg->hdr_list = g_list_append(msg->hdr_list,
360 msg->full_sender_name
361 ?
362 create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name,
363 msg->return_path->local_part, msg->return_path->domain)
364 :
365 create_header(HEAD_FROM, "From: <%s@%s>\n",
366 msg->return_path->local_part, msg->return_path->domain)
367 );
368 }
369 if (!has_to_or_cc) {
370 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
371 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
372 }
373 if (!has_date) {
374 DEBUG(3) debugf("adding 'Date:' header\n");
375 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
376 }
377 if (!has_id) {
378 DEBUG(3) debugf("adding 'Message-ID:' header\n");
379 msg->hdr_list = g_list_append(msg->hdr_list,
380 create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
381 }
382 }
384 /* Received header: */
385 /* At this point because we have to know the rcpts for the 'for' part */
386 gchar *for_string = NULL;
387 header *hdr = NULL;
389 DEBUG(3) debugf("adding 'Received:' header\n");
391 if (g_list_length(msg->rcpt_list) == 1) {
392 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
393 for_string = g_strdup_printf(" for %s", addr_string(addr));
394 }
396 if (msg->received_host == NULL) {
397 /* received locally */
398 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
399 passwd->pw_name, conf.host_name, prot_names[msg->received_prot],
400 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
401 } else {
402 /* received from remote */
403 #ifdef ENABLE_IDENT
404 DEBUG(5) debugf("adding 'Received:' header (5)\n");
405 hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n",
406 msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name,
407 prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "",
408 rec_timestamp());
409 #else
410 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
411 msg->received_host, conf.host_name, prot_names[msg->received_prot],
412 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
413 #endif
414 }
415 header_fold(hdr);
416 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
418 if (for_string)
419 g_free(for_string);
421 /* write message to spool: */
422 /* accept is no longer responsible for this
423 if (!spool_write(msg, TRUE))
424 return AERR_NOSPOOL;
425 */
426 return AERR_OK;
427 }
429 accept_error
430 accept_message(FILE * in, message * msg, guint flags)
431 {
432 accept_error err;
434 err = accept_message_stream(in, msg, flags);
435 if (err == AERR_OK)
436 err = accept_message_prepare(msg, flags);
438 return err;
439 }