masqmail
view src/accept.c @ 378:5781ba87df95
Removed ident. This had been discussed on the mailing list in Oct 2011.
Ident is hardly useful in typical setups for masqmail. Probably Oliver
had used it in his setup; that would make sense. Now, I know of nobody
who needs it.
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Sat, 14 Jan 2012 21:36:58 +0100 |
parents | 9bc3e47b0222 |
children | aa40710f09fe |
line source
1 /*
2 ** MasqMail
3 ** Copyright (C) 1999-2001 Oliver Kurth
4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
21 #include "masqmail.h"
22 #include "readsock.h"
24 /* must match PROT_* in masqmail.h */
25 gchar *prot_names[] = {
26 "local",
27 "SMTP",
28 "ESMTP",
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 /*
46 ** accept message from anywhere.
47 ** A message from local is indicated by msg->recieved_host == NULL
48 **
49 ** The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
50 ** in To/Cc/Bcc headers are added to the recipient list.
51 */
53 accept_error
54 accept_message_stream(FILE *in, message *msg, guint flags)
55 {
56 gchar *line, *line1;
57 int line_size = MAX_DATALINE;
58 gboolean in_headers = TRUE;
59 header *hdr = NULL;
60 gint line_cnt = 0, data_size = 0;
62 line = g_malloc(line_size);
63 line[0] = '\0';
65 while (TRUE) {
66 int len = read_sockline1(in, &line, &line_size, 5 * 60, READSOCKL_CVT_CRLF);
68 line1 = line;
70 if ((line[0] == '.') && (!(flags & ACC_DOT_IGNORE))) {
71 if (line[1] == '\n') {
72 g_free(line);
73 break;
74 }
75 line1++;
76 }
78 if ((len == -1) && (flags & (ACC_DOT_IGNORE | ACC_NODOT_RELAX))) {
79 /* we got an EOF, and the last line was not terminated by a CR */
80 gint len1 = strlen(line1);
81 if (len1 > 0) { /* == 0 is 'normal' (EOF after a CR) */
82 if (line1[len1 - 1] != '\n') { /* some mail clients allow unterminated lines */
83 line1[len1] = '\n';
84 line1[len1 + 1] = '\0';
85 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
86 data_size += strlen(line1);
87 line_cnt++;
88 }
89 }
90 break;
92 } else if (len == -1) {
93 g_free(line);
94 return AERR_EOF;
96 } else if (len == -2) {
97 /* should not happen any more */
98 g_free(line);
99 return AERR_OVERFLOW;
101 } else if (len == -3) {
102 g_free(line);
103 return AERR_TIMEOUT;
105 } else if (len <= 0) {
106 /* does not happen */
107 g_free(line);
108 DEBUG(5) debugf("read_sockline returned %d\n", len);
109 return AERR_UNKNOWN;
111 }
113 if (in_headers) {
115 /* some pop servers send the 'From ' line, skip it: */
116 if (!msg->hdr_list && strncmp(line1, "From ", 5) == 0) {
117 continue;
118 }
120 if (line1[0] == ' ' || line1[0] == '\t') {
121 /* continuation of 'folded' header: */
122 if (hdr) {
123 char *cp;
124 cp = g_strconcat(hdr->header, line1, NULL);
125 hdr->value = cp + (hdr->value - hdr->header);
126 free(hdr->header);
127 hdr->header = cp;
128 }
130 } else if (line1[0] == '\n') {
131 /* an empty line marks end of headers */
132 in_headers = FALSE;
133 } else {
134 /* in all other cases we expect another header */
135 if ((hdr = get_header(line1))) {
136 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
137 } else {
138 /*
139 ** if get_header() returns NULL,
140 ** no header was recognized,
141 ** so this seems to be the first
142 ** data line of a broken mailer
143 ** which does not send an empty
144 ** line after the headers
145 */
146 in_headers = FALSE;
147 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
148 }
149 }
150 } else {
151 /* message body */
152 msg->data_list = g_list_prepend(msg->data_list, g_strdup(line1));
153 data_size += strlen(line1);
154 line_cnt++;
155 }
157 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
158 DEBUG(4) debugf("accept_message_stream(): "
159 "received %d bytes (conf.max_msg_size=%d)\n",
160 data_size, conf.max_msg_size);
161 return AERR_SIZE;
162 }
164 }
166 DEBUG(4) debugf("received %d lines of data (%d bytes)\n", line_cnt, data_size);
168 if (!msg->data_list) {
169 /* make sure data list is not NULL: */
170 msg->data_list = g_list_append(NULL, g_strdup(""));
171 }
172 msg->data_list = g_list_reverse(msg->data_list);
174 /* we get here after we succesfully received the mail data */
176 msg->data_size = data_size;
177 msg->received_time = time(NULL);
179 return AERR_OK;
180 }
182 accept_error
183 accept_message_prepare(message *msg, guint flags)
184 {
185 struct passwd *passwd = NULL;
186 time_t rec_time = time(NULL);
188 DEBUG(5) debugf("accept_message_prepare()\n");
190 /* create unique message id */
191 msg->uid = g_malloc(14);
192 string_base62(msg->uid, rec_time, 6);
193 msg->uid[6] = '-';
194 string_base62(msg->uid + 7, getpid(), 3);
195 msg->uid[10] = '-';
196 string_base62(msg->uid + 11, msg->transfer_id, 2);
197 msg->uid[13] = '\0';
199 /* if local, get password entry and set return path if missing */
200 if (!msg->received_host) {
201 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
202 msg->ident = g_strdup(passwd->pw_name);
203 if (!msg->return_path) {
204 gchar *path = g_strdup_printf("<%s@%s>", passwd->pw_name, conf.host_name);
205 DEBUG(3) debugf("setting return_path for local accept: %s\n", path);
206 msg->return_path = create_address(path, TRUE);
207 g_free(path);
208 }
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_to_or_cc = FALSE;
218 GList *hdr_node, *hdr_node_next;
219 header *hdr;
221 for (hdr_node = g_list_first(msg->hdr_list);
222 hdr_node;
223 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 has_to_or_cc = TRUE;
243 /* fall through */
244 case HEAD_BCC:
245 if (flags & ACC_RCPT_FROM_HEAD) {
246 /* -t option (see comment above) */
247 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
248 if (hdr->value) {
249 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
250 }
251 }
252 if (hdr->id == HEAD_BCC) {
253 DEBUG(3) debugf("removing 'Bcc' header\n");
254 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
255 g_list_free_1(hdr_node);
256 destroy_header(hdr);
257 }
258 break;
259 case HEAD_ENVELOPE_TO:
260 if (flags & ACC_SAVE_ENVELOPE_TO) {
261 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
262 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
263 "X-Orig-Envelope-To: %s", hdr->value));
264 }
265 DEBUG(3) debugf("removing 'Envelope-To' header\n");
266 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
267 g_list_free_1(hdr_node);
268 destroy_header(hdr);
269 break;
270 case HEAD_RETURN_PATH:
271 if (flags & ACC_MAIL_FROM_HEAD) {
272 /* usually POP3 accept */
273 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
274 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
275 }
276 DEBUG(3) debugf("removing 'Return-Path' header\n");
277 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
278 g_list_free_1(hdr_node);
279 destroy_header(hdr);
280 break;
281 default:
282 break; /* make compiler happy */
283 }
284 }
286 if (!msg->return_path) {
287 /*
288 ** TODO: do we still need this as we don't fetch
289 ** mail anymore?
290 ** This can happen for pop3 accept only and if no
291 ** Return-Path: header was given
292 */
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 DEBUG(5) debugf("adding 'Received:' header (5)\n");
377 hdr = create_header(HEAD_RECEIVED,
378 "Received: from %s\n\tby %s with %s (%s %s)\n\tid %s%s; %s\n",
379 msg->received_host,
380 conf.host_name, prot_names[msg->received_prot], PACKAGE,
381 VERSION, msg->uid, for_string ? for_string : "",
382 rec_timestamp());
383 }
384 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
386 if (for_string)
387 g_free(for_string);
389 return AERR_OK;
390 }
392 accept_error
393 accept_message(FILE *in, message *msg, guint flags)
394 {
395 accept_error err;
397 err = accept_message_stream(in, msg, flags);
398 if (err == AERR_OK) {
399 err = accept_message_prepare(msg, flags);
400 }
402 return err;
403 }