masqmail

view src/accept.c @ 304:d5ce2ba71e7b

manual formating of Received: hdrs; changed hdr for local receival Now the Received: headers are much friendlier to read. About folding: We must fold any line at 998 chars before transfer. We should fold the lines we produce at 78 chars. That is what RFC 2821 requests. We should think about it, somewhen. The header for locally (i.e. non-SMTP) received mail is changed to the format postfix uses. This matches RFC 2821 better. The `from' clause should contain a domain or IP, not a user name. Also, the `with' clause should contain a registered standard protocol name, which ``local'' is not.
author markus schnalke <meillo@marmaro.de>
date Thu, 09 Dec 2010 18:28:11 -0300
parents 3e3c280ca5b2
children 29de6a1c4538
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 hdr->header = g_strconcat(hdr->header, line1, NULL);
135 }
137 } else if (line1[0] == '\n') {
138 /* an empty line marks end of headers */
139 in_headers = FALSE;
140 } else {
141 /* in all other cases we expect another header */
142 if ((hdr = get_header(line1))) {
143 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
144 } else {
145 /* if get_header() returns NULL, no header was recognized,
146 so this seems to be the first data line of a broken mailer
147 which does not send an empty line after the headers */
148 in_headers = FALSE;
149 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
150 }
151 }
152 } else {
153 /* message body */
154 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
155 data_size += strlen(line1);
156 line_cnt++;
157 }
159 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
160 DEBUG(4) debugf("accept_message_stream(): "
161 "received %d bytes (conf.max_msg_size=%d)\n",
162 data_size, conf.max_msg_size);
163 return AERR_SIZE;
164 }
166 }
168 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
170 if (!msg->data_list) {
171 /* make sure data list is not NULL: */
172 msg->data_list = g_list_append(NULL, g_strdup(""));
173 }
174 msg->data_list = g_list_reverse(msg->data_list);
176 /* we get here after we succesfully received the mail data */
178 msg->data_size = data_size;
179 msg->received_time = time(NULL);
181 return AERR_OK;
182 }
184 accept_error
185 accept_message_prepare(message * msg, guint flags)
186 {
187 struct passwd *passwd = NULL;
188 time_t rec_time = time(NULL);
190 DEBUG(5) debugf("accept_message_prepare()\n");
192 /* create unique message id */
193 msg->uid = g_malloc(14);
194 string_base62(msg->uid, rec_time, 6);
195 msg->uid[6] = '-';
196 string_base62(msg->uid + 7, getpid(), 3);
197 msg->uid[10] = '-';
198 string_base62(msg->uid + 11, msg->transfer_id, 2);
199 msg->uid[13] = '\0';
201 /* if local, get password entry and set return path if missing */
202 if (!msg->received_host) {
203 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
204 msg->ident = g_strdup(passwd->pw_name);
205 if (!msg->return_path) {
206 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
207 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
208 msg->return_path = create_address(path, TRUE);
209 g_free(path);
210 }
211 }
213 /* scan headers */
214 {
215 gboolean has_id = FALSE;
216 gboolean has_date = FALSE;
217 gboolean has_sender = FALSE;
218 gboolean has_from = FALSE;
219 gboolean has_to_or_cc = FALSE;
220 GList *hdr_node, *hdr_node_next;
221 header *hdr;
223 for (hdr_node = g_list_first(msg->hdr_list);
224 hdr_node;
225 hdr_node = hdr_node_next) {
226 hdr_node_next = g_list_next(hdr_node);
227 hdr = ((header *) (hdr_node->data));
228 DEBUG(5) debugf("scanning headers: %s", hdr->header);
229 switch (hdr->id) {
230 case HEAD_MESSAGE_ID:
231 has_id = TRUE;
232 break;
233 case HEAD_DATE:
234 has_date = TRUE;
235 break;
236 case HEAD_FROM:
237 has_from = TRUE;
238 break;
239 case HEAD_SENDER:
240 has_sender = TRUE;
241 break;
242 case HEAD_TO:
243 case HEAD_CC:
244 has_to_or_cc = TRUE;
245 /* fall through */
246 case HEAD_BCC:
247 if (flags & ACC_RCPT_FROM_HEAD) {
248 /* -t option (see comment above) */
249 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
250 if (hdr->value) {
251 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
252 }
253 }
254 if (hdr->id == HEAD_BCC) {
255 DEBUG(3) debugf("removing 'Bcc' header\n");
256 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
257 g_list_free_1(hdr_node);
258 destroy_header(hdr);
259 }
260 break;
261 case HEAD_ENVELOPE_TO:
262 if (flags & ACC_SAVE_ENVELOPE_TO) {
263 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
264 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
265 "X-Orig-Envelope-To: %s", hdr->value));
266 }
267 DEBUG(3) debugf("removing 'Envelope-To' 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 break;
272 case HEAD_RETURN_PATH:
273 if (flags & ACC_MAIL_FROM_HEAD) {
274 /* usually POP3 accept */
275 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
276 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
277 }
278 DEBUG(3) debugf("removing 'Return-Path' header\n");
279 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
280 g_list_free_1(hdr_node);
281 destroy_header(hdr);
282 break;
283 default:
284 break; /* make compiler happy */
285 }
286 }
288 if (!msg->return_path) {
289 /* TODO: do we still need this as we don't fetch
290 mail anymore? */
291 /* this can happen for pop3 accept only and if no
292 Return-Path: header was given */
293 GList *hdr_list;
294 header *hdr;
296 DEBUG(3) debugf("return_path == NULL\n");
298 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
299 if (!hdr_list) {
300 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
301 }
302 if (hdr_list) {
303 gchar *addr;
304 hdr = (header *) (g_list_first(hdr_list)->data);
306 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
308 addr = g_strdup(hdr->value);
309 g_strchomp(addr);
311 msg->return_path = create_address_qualified(addr, FALSE, msg->received_host);
312 if (msg->return_path) {
313 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
314 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:"));
315 }
316 g_free(addr);
317 }
318 if (!msg->return_path) {
319 /* no Sender: or From: or
320 create_address_qualified failed */
321 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
322 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
323 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN, "X-Warning: real return path is unknown\n"));
324 }
325 }
327 /* here we should have our recipients, fail if not: */
328 if (!msg->rcpt_list) {
329 logwrite(LOG_WARNING, "no recipients found in message\n");
330 return AERR_NORCPT;
331 }
333 if (!has_sender && !has_from) {
334 DEBUG(3) debugf("adding 'From:' header\n");
335 if (msg->full_sender_name) {
336 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));
337 } else {
338 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));
339 }
340 }
341 if (!has_to_or_cc) {
342 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
343 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
344 }
345 if (!has_date) {
346 DEBUG(3) debugf("adding 'Date:' header\n");
347 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
348 }
349 if (!has_id) {
350 DEBUG(3) debugf("adding 'Message-ID:' header\n");
351 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
352 }
353 }
355 /* Received header: */
356 /* At this point because we have to know the rcpts for the 'for' part */
357 /* The `for' part will only be used if exactly one rcpt is present. */
358 gchar *for_string = NULL;
359 header *hdr = NULL;
361 DEBUG(3) debugf("adding 'Received:' header\n");
363 if (g_list_length(msg->rcpt_list) == 1) {
364 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
365 for_string = g_strdup_printf("\n\tfor %s", addr_string(addr));
366 }
368 if (!msg->received_host) {
369 /* received locally */
370 hdr = create_header(HEAD_RECEIVED,
371 "Received: by %s (%s %s, from userid %d)\n\tid %s%s; %s\n",
372 conf.host_name, PACKAGE, VERSION, geteuid(),
373 msg->uid, for_string ? for_string : "", rec_timestamp());
374 } else {
375 /* received from remote */
376 #ifdef ENABLE_IDENT
377 DEBUG(5) debugf("adding 'Received:' header (5)\n");
378 hdr = create_header(HEAD_RECEIVED,
379 "Received: from %s (ident=%s)\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
380 msg->received_host, msg->ident ? msg->ident : "unknown",
381 conf.host_name, prot_names[msg->received_prot], PACKAGE,
382 VERSION, msg->uid, for_string ? for_string : "",
383 rec_timestamp());
384 #else
385 hdr = create_header(HEAD_RECEIVED,
386 "Received: from %s\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
387 msg->received_host, conf.host_name,
388 prot_names[msg->received_prot], PACKAGE, VERSION,
389 msg->uid, for_string ? for_string : "", rec_timestamp());
390 #endif
391 }
392 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
394 if (for_string)
395 g_free(for_string);
397 return AERR_OK;
398 }
400 accept_error
401 accept_message(FILE * in, message * msg, guint flags)
402 {
403 accept_error err;
405 err = accept_message_stream(in, msg, flags);
406 if (err == AERR_OK) {
407 err = accept_message_prepare(msg, flags);
408 }
410 return err;
411 }