masqmail

view src/accept.c @ 323:29de6a1c4538

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:47:27 +0200
parents d5ce2ba71e7b
children 9d49dffc3070
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 /* must match PROT_* in masqmail.h */
24 gchar *prot_names[] = {
25 "local",
26 "SMTP",
27 "ESMTP",
28 "(unknown)" /* should not happen, but better than crashing. */
29 };
31 static gchar*
32 string_base62(gchar * res, guint value, gchar len)
33 {
34 static gchar base62_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
35 gchar *p = res + len;
36 *p = '\0';
37 while (p > res) {
38 *(--p) = base62_chars[value % 62];
39 value /= 62;
40 }
41 return res;
42 }
44 static gint
45 _g_list_addr_isequal(gconstpointer a, gconstpointer b)
46 {
47 address *addr1 = (address *) a;
48 address *addr2 = (address *) b;
49 int ret;
51 if ((ret = strcasecmp(addr1->domain, addr2->domain)) == 0) {
52 return strcmp(addr1->local_part, addr2->local_part);
53 }
54 return ret;
55 }
57 /* accept message from anywhere.
58 A message from local is indicated by msg->recieved_host == NULL
60 The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
61 in To/Cc/Bcc headers are added to the recipient list.
62 */
64 accept_error
65 accept_message_stream(FILE * in, message * msg, guint flags)
66 {
67 gchar *line, *line1;
68 int line_size = MAX_DATALINE;
69 gboolean in_headers = TRUE;
70 header *hdr = NULL;
71 gint line_cnt = 0, data_size = 0;
73 line = g_malloc(line_size);
74 line[0] = '\0';
76 while (TRUE) {
77 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
79 line1 = line;
81 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
82 if (line[1] == '\n') {
83 g_free(line);
84 break;
85 }
86 line1++;
87 }
89 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
90 /* we got an EOF, and the last line was not terminated by a CR */
91 gint len1 = strlen(line1);
92 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
93 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
94 line1[len1] = '\n';
95 line1[len1 + 1] = '\0';
96 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
97 data_size += strlen(line1);
98 line_cnt++;
99 }
100 }
101 break;
103 } else if (len == -1) {
104 g_free(line);
105 return AERR_EOF;
107 } else if (len == -2) {
108 /* should not happen any more */
109 g_free(line);
110 return AERR_OVERFLOW;
112 } else if (len == -3) {
113 g_free(line);
114 return AERR_TIMEOUT;
116 } else if (len <= 0) {
117 /* does not happen */
118 g_free(line);
119 DEBUG(5) debugf("read_sockline returned %d\n", len);
120 return AERR_UNKNOWN;
122 }
124 if (in_headers) {
126 /* some pop servers send the 'From ' line, skip it: */
127 if (!msg->hdr_list && strncmp(line1, "From ", 5) == 0) {
128 continue;
129 }
131 if (line1[0] == ' ' || line1[0] == '\t') {
132 /* continuation of 'folded' header: */
133 if (hdr) {
134 char* cp;
135 cp = g_strconcat(hdr->header, line1, NULL);
136 hdr->value = cp + (hdr->value - hdr->header);
137 free(hdr->header);
138 hdr->header = cp;
139 }
141 } else if (line1[0] == '\n') {
142 /* an empty line marks end of headers */
143 in_headers = FALSE;
144 } else {
145 /* in all other cases we expect another header */
146 if ((hdr = get_header(line1))) {
147 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
148 } else {
149 /* if get_header() returns NULL, no header was recognized,
150 so this seems to be the first data line of a broken mailer
151 which does not send an empty line after the headers */
152 in_headers = FALSE;
153 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
154 }
155 }
156 } else {
157 /* message body */
158 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
159 data_size += strlen(line1);
160 line_cnt++;
161 }
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 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
174 if (!msg->data_list) {
175 /* make sure data list is not NULL: */
176 msg->data_list = g_list_append(NULL, g_strdup(""));
177 }
178 msg->data_list = g_list_reverse(msg->data_list);
180 /* we get here after we succesfully received the mail data */
182 msg->data_size = data_size;
183 msg->received_time = time(NULL);
185 return AERR_OK;
186 }
188 accept_error
189 accept_message_prepare(message * msg, guint flags)
190 {
191 struct passwd *passwd = 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);
198 string_base62(msg->uid, rec_time, 6);
199 msg->uid[6] = '-';
200 string_base62(msg->uid + 7, getpid(), 3);
201 msg->uid[10] = '-';
202 string_base62(msg->uid + 11, msg->transfer_id, 2);
203 msg->uid[13] = '\0';
205 /* if local, get password entry and set return path if missing */
206 if (!msg->received_host) {
207 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
208 msg->ident = g_strdup(passwd->pw_name);
209 if (!msg->return_path) {
210 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
211 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
212 msg->return_path = create_address(path, TRUE);
213 g_free(path);
214 }
215 }
217 /* scan headers */
218 {
219 gboolean has_id = FALSE;
220 gboolean has_date = FALSE;
221 gboolean has_sender = FALSE;
222 gboolean has_from = FALSE;
223 gboolean has_to_or_cc = FALSE;
224 GList *hdr_node, *hdr_node_next;
225 header *hdr;
227 for (hdr_node = g_list_first(msg->hdr_list);
228 hdr_node;
229 hdr_node = hdr_node_next) {
230 hdr_node_next = g_list_next(hdr_node);
231 hdr = ((header *) (hdr_node->data));
232 DEBUG(5) debugf("scanning headers: %s", hdr->header);
233 switch (hdr->id) {
234 case HEAD_MESSAGE_ID:
235 has_id = TRUE;
236 break;
237 case HEAD_DATE:
238 has_date = TRUE;
239 break;
240 case HEAD_FROM:
241 has_from = TRUE;
242 break;
243 case HEAD_SENDER:
244 has_sender = TRUE;
245 break;
246 case HEAD_TO:
247 case HEAD_CC:
248 has_to_or_cc = TRUE;
249 /* fall through */
250 case HEAD_BCC:
251 if (flags & ACC_RCPT_FROM_HEAD) {
252 /* -t option (see comment above) */
253 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
254 if (hdr->value) {
255 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
256 }
257 }
258 if (hdr->id == HEAD_BCC) {
259 DEBUG(3) debugf("removing 'Bcc' header\n");
260 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
261 g_list_free_1(hdr_node);
262 destroy_header(hdr);
263 }
264 break;
265 case HEAD_ENVELOPE_TO:
266 if (flags & ACC_SAVE_ENVELOPE_TO) {
267 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
268 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
269 "X-Orig-Envelope-To: %s", hdr->value));
270 }
271 DEBUG(3) debugf("removing 'Envelope-To' header\n");
272 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
273 g_list_free_1(hdr_node);
274 destroy_header(hdr);
275 break;
276 case HEAD_RETURN_PATH:
277 if (flags & ACC_MAIL_FROM_HEAD) {
278 /* usually POP3 accept */
279 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
280 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
281 }
282 DEBUG(3) debugf("removing 'Return-Path' header\n");
283 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
284 g_list_free_1(hdr_node);
285 destroy_header(hdr);
286 break;
287 default:
288 break; /* make compiler happy */
289 }
290 }
292 if (!msg->return_path) {
293 /* TODO: do we still need this as we don't fetch
294 mail anymore? */
295 /* this can happen for pop3 accept only and if no
296 Return-Path: header was given */
297 GList *hdr_list;
298 header *hdr;
300 DEBUG(3) debugf("return_path == NULL\n");
302 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
303 if (!hdr_list) {
304 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
305 }
306 if (hdr_list) {
307 gchar *addr;
308 hdr = (header *) (g_list_first(hdr_list)->data);
310 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
312 addr = g_strdup(hdr->value);
313 g_strchomp(addr);
315 msg->return_path = create_address_qualified(addr, FALSE, msg->received_host);
316 if (msg->return_path) {
317 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
318 msg->hdr_list = g_list_append( msg->hdr_list, create_header(HEAD_UNKNOWN, "X-Warning: return path set from %s address\n", hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
319 }
320 g_free(addr);
321 }
322 if (!msg->return_path) {
323 /* no Sender: or From: or
324 create_address_qualified failed */
325 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
326 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
327 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN, "X-Warning: real return path is unknown\n"));
328 }
329 }
331 /* here we should have our recipients, fail if not: */
332 if (!msg->rcpt_list) {
333 logwrite(LOG_WARNING, "no recipients found in message\n");
334 return AERR_NORCPT;
335 }
337 if (!has_sender && !has_from) {
338 DEBUG(3) debugf("adding 'From:' header\n");
339 if (msg->full_sender_name) {
340 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name, msg->return_path->local_part, msg->return_path->domain));
341 } else {
342 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_FROM, "From: <%s@%s>\n", msg->return_path->local_part, msg->return_path->domain));
343 }
344 }
345 if (!has_to_or_cc) {
346 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
347 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
348 }
349 if (!has_date) {
350 DEBUG(3) debugf("adding 'Date:' header\n");
351 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
352 }
353 if (!has_id) {
354 DEBUG(3) debugf("adding 'Message-ID:' header\n");
355 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
356 }
357 }
359 /* Received header: */
360 /* At this point because we have to know the rcpts for the 'for' part */
361 /* The `for' part will only be used if exactly one rcpt is present. */
362 gchar *for_string = NULL;
363 header *hdr = NULL;
365 DEBUG(3) debugf("adding 'Received:' header\n");
367 if (g_list_length(msg->rcpt_list) == 1) {
368 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
369 for_string = g_strdup_printf("\n\tfor %s", addr_string(addr));
370 }
372 if (!msg->received_host) {
373 /* received locally */
374 hdr = create_header(HEAD_RECEIVED,
375 "Received: by %s (%s %s, from userid %d)\n\tid %s%s; %s\n",
376 conf.host_name, PACKAGE, VERSION, geteuid(),
377 msg->uid, for_string ? for_string : "", rec_timestamp());
378 } else {
379 /* received from remote */
380 #ifdef ENABLE_IDENT
381 DEBUG(5) debugf("adding 'Received:' header (5)\n");
382 hdr = create_header(HEAD_RECEIVED,
383 "Received: from %s (ident=%s)\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
384 msg->received_host, msg->ident ? msg->ident : "unknown",
385 conf.host_name, prot_names[msg->received_prot], PACKAGE,
386 VERSION, msg->uid, for_string ? for_string : "",
387 rec_timestamp());
388 #else
389 hdr = create_header(HEAD_RECEIVED,
390 "Received: from %s\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
391 msg->received_host, conf.host_name,
392 prot_names[msg->received_prot], PACKAGE, VERSION,
393 msg->uid, for_string ? for_string : "", rec_timestamp());
394 #endif
395 }
396 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
398 if (for_string)
399 g_free(for_string);
401 return AERR_OK;
402 }
404 accept_error
405 accept_message(FILE * in, message * msg, guint flags)
406 {
407 accept_error err;
409 err = accept_message_stream(in, msg, flags);
410 if (err == AERR_OK) {
411 err = accept_message_prepare(msg, flags);
412 }
414 return err;
415 }