masqmail

view src/accept.c @ 281:ea5f86e0a81c

modes are now enforced exclusive Other MTAs (exim, postfix) are more relaxing, but as combinations of exclusive modes are senseless we behave more obvious if we fail early. This makes understanding the behavior easier too.
author markus schnalke <meillo@marmaro.de>
date Tue, 07 Dec 2010 14:04:56 -0300
parents 1405012509e3
children bb3005ce0837
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 "(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);
195 string_base62(msg->uid, rec_time, 6);
196 msg->uid[6] = '-';
197 string_base62(&(msg->uid[7]), getpid(), 3);
198 msg->uid[10] = '-';
199 string_base62(&(msg->uid[11]), msg->transfer_id, 2);
200 msg->uid[13] = 0;
202 /* if local, get password entry */
203 if (msg->received_host == NULL) {
204 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
205 msg->ident = g_strdup(passwd->pw_name);
206 }
208 /* set return path if local */
209 if (msg->return_path == NULL && msg->received_host == NULL) {
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 }
216 /* scan headers */
217 {
218 gboolean has_id = FALSE;
219 gboolean has_date = FALSE;
220 gboolean has_sender = FALSE;
221 gboolean has_from = FALSE;
222 gboolean has_to_or_cc = FALSE;
223 GList *hdr_node, *hdr_node_next;
224 header *hdr;
226 for (hdr_node = g_list_first(msg->hdr_list);
227 hdr_node != NULL; hdr_node = hdr_node_next) {
228 hdr_node_next = g_list_next(hdr_node);
229 hdr = ((header *) (hdr_node->data));
230 DEBUG(5) debugf("scanning headers: %s", hdr->header);
231 switch (hdr->id) {
232 case HEAD_MESSAGE_ID:
233 has_id = TRUE;
234 break;
235 case HEAD_DATE:
236 has_date = TRUE;
237 break;
238 case HEAD_FROM:
239 has_from = TRUE;
240 break;
241 case HEAD_SENDER:
242 has_sender = TRUE;
243 break;
244 case HEAD_TO:
245 case HEAD_CC:
246 has_to_or_cc = TRUE;
247 /* fall through */
248 case HEAD_BCC:
249 if (flags & ACC_RCPT_FROM_HEAD) {
250 /* -t option (see comment above) */
251 DEBUG(5) debugf("hdr->value = %s\n", hdr->value);
252 if (hdr->value) {
253 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
254 }
255 }
256 if (hdr->id == HEAD_BCC) {
257 DEBUG(3) debugf("removing 'Bcc' header\n");
258 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
259 g_list_free_1(hdr_node);
260 destroy_header(hdr);
261 }
262 break;
263 case HEAD_ENVELOPE_TO:
264 if (flags & ACC_SAVE_ENVELOPE_TO) {
265 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
266 msg->hdr_list = g_list_prepend(msg->hdr_list, create_header(HEAD_UNKNOWN,
267 "X-Orig-Envelope-To: %s", hdr->value));
268 }
269 DEBUG(3) debugf("removing 'Envelope-To' header\n");
270 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
271 g_list_free_1(hdr_node);
272 destroy_header(hdr);
273 break;
274 case HEAD_RETURN_PATH:
275 if (flags & ACC_MAIL_FROM_HEAD) {
276 /* usually POP3 accept */
277 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
278 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
279 }
280 DEBUG(3) debugf("removing 'Return-Path' header\n");
281 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
282 g_list_free_1(hdr_node);
283 destroy_header(hdr);
284 break;
285 default:
286 break; /* make compiler happy */
287 }
288 }
290 if (msg->return_path == NULL) {
291 /* this can happen for pop3 accept only and if no Return-path: header was given */
292 GList *hdr_list;
293 header *hdr;
295 DEBUG(3) debugf("return_path == NULL\n");
297 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
298 if (!hdr_list)
299 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
300 if (hdr_list) {
301 gchar *addr;
302 hdr = (header *) (g_list_first(hdr_list)->data);
304 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
306 addr = g_strdup(hdr->value);
307 g_strchomp(addr);
309 if ((msg->return_path = create_address_qualified(addr, FALSE, msg->received_host)) != NULL) {
310 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
311 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
312 "X-Warning: return path set from %s address\n",
313 hdr->id == HEAD_SENDER ? "Sender:" : "From:"));
314 }
315 g_free(addr);
316 }
317 if (msg->return_path == NULL) { /* no Sender: or From: or create_address_qualified failed */
318 msg->return_path = create_address_qualified("postmaster", TRUE, conf.host_name);
319 DEBUG(3) debugf("setting return_path to %s\n", addr_string(msg->return_path));
320 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_UNKNOWN,
321 "X-Warning: real return path is unknown\n"));
322 }
323 }
325 /* here we should have our recipients, fail if not: */
326 if (!msg->rcpt_list) {
327 logwrite(LOG_WARNING, "no recipients found in message\n");
328 return AERR_NORCPT;
329 }
331 if (!(has_sender || has_from)) {
332 DEBUG(3) debugf("adding 'From' header\n");
333 msg->hdr_list = g_list_append(msg->hdr_list,
334 msg->full_sender_name
335 ?
336 create_header(HEAD_FROM, "From: \"%s\" <%s@%s>\n", msg->full_sender_name,
337 msg->return_path->local_part, msg->return_path->domain)
338 :
339 create_header(HEAD_FROM, "From: <%s@%s>\n",
340 msg->return_path->local_part, msg->return_path->domain)
341 );
342 }
343 if (!has_to_or_cc) {
344 DEBUG(3) debugf("no To: or Cc: header, hence adding `To: undisclosed recipients:;'\n");
345 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_TO, "To: undisclosed-recipients:;\n"));
346 }
347 if (!has_date) {
348 DEBUG(3) debugf("adding 'Date:' header\n");
349 msg->hdr_list = g_list_append(msg->hdr_list, create_header(HEAD_DATE, "Date: %s\n", rec_timestamp()));
350 }
351 if (!has_id) {
352 DEBUG(3) debugf("adding 'Message-ID:' header\n");
353 msg->hdr_list = g_list_append(msg->hdr_list,
354 create_header(HEAD_MESSAGE_ID, "Message-ID: <%s@%s>\n", msg->uid, conf.host_name));
355 }
356 }
358 /* Received header: */
359 /* At this point because we have to know the rcpts for the 'for' part */
360 gchar *for_string = NULL;
361 header *hdr = NULL;
363 DEBUG(3) debugf("adding 'Received:' header\n");
365 if (g_list_length(msg->rcpt_list) == 1) {
366 address *addr = (address *) (g_list_first(msg->rcpt_list)->data);
367 for_string = g_strdup_printf(" for %s", addr_string(addr));
368 }
370 if (msg->received_host == NULL) {
371 /* received locally */
372 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
373 passwd->pw_name, conf.host_name, prot_names[msg->received_prot],
374 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
375 } else {
376 /* received from remote */
377 #ifdef ENABLE_IDENT
378 DEBUG(5) debugf("adding 'Received:' header (5)\n");
379 hdr = create_header(HEAD_RECEIVED, "Received: from %s (ident=%s) by %s with %s (%s %s) id %s%s; %s\n",
380 msg->received_host, msg->ident ? msg->ident : "unknown", conf.host_name,
381 prot_names[msg->received_prot], PACKAGE, VERSION, msg->uid, for_string ? for_string : "",
382 rec_timestamp());
383 #else
384 hdr = create_header(HEAD_RECEIVED, "Received: from %s by %s with %s (%s %s) id %s%s; %s\n",
385 msg->received_host, conf.host_name, prot_names[msg->received_prot],
386 PACKAGE, VERSION, msg->uid, for_string ? for_string : "", rec_timestamp());
387 #endif
388 }
389 header_fold(hdr);
390 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
392 if (for_string)
393 g_free(for_string);
395 return AERR_OK;
396 }
398 accept_error
399 accept_message(FILE * in, message * msg, guint flags)
400 {
401 accept_error err;
403 err = accept_message_stream(in, msg, flags);
404 if (err == AERR_OK) {
405 err = accept_message_prepare(msg, flags);
406 }
408 return err;
409 }