masqmail

view src/accept.c @ 421:f37384470855

Changed lockdir to /var/lock/masqmail; Create lockdir and piddir on startup. Moved the lockdir out of the spool dir. (When /var/lock is a ramdisk we do well to have the lock files there.) Added the new configure option --with-lockdir to change that location. Nontheless, if we run_as_user, then lock files are always stored in the spool dir directly. Instead of installing the lockdir and piddir at installation time, we create them on startup time now if they are missing. This is necessary if lockdir or piddir are a tmpfs.
author markus schnalke <meillo@marmaro.de>
date Wed, 30 May 2012 09:38:38 +0200
parents aa40710f09fe
children
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[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
36 "abcdefghijklmnopqrstuvwxyz";
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 /*
47 ** accept message from anywhere.
48 ** A message from local is indicated by msg->recieved_host == NULL
49 **
50 ** The -t option: With the ACC_RCPT_FROM_HEAD flag the addrs found found
51 ** in To/Cc/Bcc headers are added to the recipient list.
52 */
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';
65 while (1) {
66 int len = read_sockline1(in, &line, &line_size, 5 * 60,
67 READSOCKL_CVT_CRLF);
68 line1 = line;
70 if ((*line == '.') && (!(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 /* at EOF but last line was not terminated by CR */
80 /* some MUAs allow unterminated lines */
81 gint len1 = strlen(line1);
82 if (len1 > 0 && line1[len1-1] != '\n') {
83 line1[len1] = '\n';
84 line1[len1+1] = '\0';
85 msg->data_list = g_list_prepend(msg->data_list,
86 g_strdup(line1));
87 data_size += strlen(line1);
88 line_cnt++;
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) {
114 /* some pop servers send the 'From ' line, skip it: */
115 if (!msg->hdr_list && strncmp(line1, "From ", 5)==0) {
116 continue;
117 }
119 if (*line1 == ' ' || *line1 == '\t') {
120 /* continuation of 'folded' header: */
121 if (hdr) {
122 char *cp;
123 cp = g_strconcat(hdr->header, line1,
124 NULL);
125 hdr->value = cp + (hdr->value -
126 hdr->header);
127 free(hdr->header);
128 hdr->header = cp;
129 }
130 } else if (*line1 == '\n') {
131 /* an empty line marks end of headers */
132 in_headers = FALSE;
134 } else if ((hdr = get_header(line1))) {
135 /* another header */
136 msg->hdr_list = g_list_append(msg->hdr_list, hdr);
137 } else {
138 /*
139 ** Should be another header but none was
140 ** recognized, so this seems to be the first
141 ** data line of a broken mailer which does
142 ** not add an empty line after the headers.
143 */
144 in_headers = FALSE;
145 msg->data_list = g_list_prepend(msg->data_list,
146 g_strdup(line1));
147 }
148 } else {
149 /* message body */
150 msg->data_list = g_list_prepend(msg->data_list,
151 g_strdup(line1));
152 data_size += strlen(line1);
153 line_cnt++;
154 }
156 if (conf.max_msg_size && (data_size > conf.max_msg_size)) {
157 DEBUG(4) debugf("accept_message_stream(): "
158 "received %d bytes (conf.max_msg_size=%d)\n",
159 data_size, conf.max_msg_size);
160 return AERR_SIZE;
161 }
162 }
163 DEBUG(4) debugf("received %d lines of data (%d bytes)\n",
164 line_cnt, data_size);
166 if (!msg->data_list) {
167 /* make sure data list is not NULL */
168 msg->data_list = g_list_append(NULL, g_strdup(""));
169 }
170 msg->data_list = g_list_reverse(msg->data_list);
172 /* we have succesfully received the mail data */
174 msg->data_size = data_size;
175 msg->received_time = time(NULL);
177 return AERR_OK;
178 }
180 static void
181 ensure_return_path(message *msg)
182 {
183 GList *hdr_list;
184 header *hdr;
185 gchar *addr;
187 if (msg->return_path) {
188 return;
189 }
191 DEBUG(3) debugf("return_path == NULL\n");
193 hdr_list = find_header(msg->hdr_list, HEAD_SENDER, NULL);
194 if (!hdr_list) {
195 hdr_list = find_header(msg->hdr_list, HEAD_FROM, NULL);
196 }
197 if (hdr_list) {
198 hdr = (header *) (g_list_first(hdr_list)->data);
200 DEBUG(5) debugf("hdr->value = '%s'\n", hdr->value);
202 addr = g_strstrip(g_strdup(hdr->value));
203 msg->return_path = create_address_qualified(addr,
204 FALSE, msg->received_host);
205 if (msg->return_path) {
206 DEBUG(3) debugf("setting return_path to %s\n",
207 addr_string(msg->return_path));
208 msg->hdr_list = g_list_append(msg->hdr_list,
209 create_header(HEAD_UNKNOWN,
210 "X-Warning: return path set from %s "
211 "address\n",
212 (hdr->id == HEAD_SENDER) ?
213 "Sender:" : "From:"));
214 }
215 g_free(addr);
216 }
217 if (!msg->return_path) {
218 /* no Sender: or From: or create_address_qualified failed */
219 msg->return_path = create_address_qualified("postmaster",
220 TRUE, conf.host_name);
221 DEBUG(3) debugf("setting return_path to %s\n",
222 addr_string(msg->return_path));
223 msg->hdr_list = g_list_append(msg->hdr_list,
224 create_header(HEAD_UNKNOWN,
225 "X-Warning: real return path is unknown\n"));
226 }
227 }
229 static accept_error
230 scan_headers(message *msg, guint flags)
231 {
232 gboolean has_id = FALSE;
233 gboolean has_date = FALSE;
234 gboolean has_sender = FALSE;
235 gboolean has_from = FALSE;
236 gboolean has_to_or_cc = FALSE;
237 GList *hdr_node, *hdr_node_next;
238 header *hdr;
240 for (hdr_node = g_list_first(msg->hdr_list); hdr_node;
241 hdr_node = hdr_node_next) {
242 hdr_node_next = g_list_next(hdr_node);
243 hdr = ((header *) (hdr_node->data));
244 DEBUG(5) debugf("scanning headers: %s", hdr->header);
245 switch (hdr->id) {
246 case HEAD_MESSAGE_ID:
247 has_id = TRUE;
248 break;
249 case HEAD_DATE:
250 has_date = TRUE;
251 break;
252 case HEAD_FROM:
253 has_from = TRUE;
254 break;
255 case HEAD_SENDER:
256 has_sender = TRUE;
257 break;
258 case HEAD_TO:
259 case HEAD_CC:
260 has_to_or_cc = TRUE;
261 /* fall through */
262 case HEAD_BCC:
263 if (flags & ACC_RCPT_FROM_HEAD) {
264 /* -t option (see comment above) */
265 DEBUG(5) debugf("hdr->value = %s\n",
266 hdr->value);
267 if (hdr->value) {
268 msg->rcpt_list = addr_list_append_rfc822(msg->rcpt_list, hdr->value, conf.host_name);
269 }
270 }
271 if (hdr->id == HEAD_BCC) {
272 DEBUG(3) debugf("removing 'Bcc' header\n");
273 msg->hdr_list = g_list_remove_link(msg->hdr_list, hdr_node);
274 g_list_free_1(hdr_node);
275 destroy_header(hdr);
276 }
277 break;
278 case HEAD_ENVELOPE_TO:
279 if (flags & ACC_SAVE_ENVELOPE_TO) {
280 DEBUG(3) debugf("creating 'X-Orig-Envelope-To' header\n");
281 msg->hdr_list = g_list_prepend(msg->hdr_list,
282 create_header(HEAD_UNKNOWN,
283 "X-Orig-Envelope-To: %s",
284 hdr->value));
285 }
286 DEBUG(3) debugf("removing 'Envelope-To' header\n");
287 msg->hdr_list = g_list_remove_link(msg->hdr_list,
288 hdr_node);
289 g_list_free_1(hdr_node);
290 destroy_header(hdr);
291 break;
292 case HEAD_RETURN_PATH:
293 if (flags & ACC_MAIL_FROM_HEAD) {
294 /* usually POP3 accept */
295 msg->return_path = create_address_qualified(hdr->value, TRUE, msg->received_host);
296 DEBUG(3) debugf("setting return_path to %s\n",
297 addr_string(msg->return_path));
298 }
299 DEBUG(3) debugf("removing 'Return-Path' header\n");
300 msg->hdr_list = g_list_remove_link(msg->hdr_list,
301 hdr_node);
302 g_list_free_1(hdr_node);
303 destroy_header(hdr);
304 break;
305 default:
306 break; /* make compiler happy */
307 }
308 }
310 /*
311 ** TODO: do we still need this as we don't fetch
312 ** mail anymore?
313 ** This can happen for pop3 accept only and if no
314 ** Return-Path: header was given
315 */
316 ensure_return_path(msg);
318 /* here we should have our recipients, fail if not: */
319 if (!msg->rcpt_list) {
320 logwrite(LOG_WARNING, "no recipients found in message\n");
321 return AERR_NORCPT;
322 }
324 if (!has_sender && !has_from) {
325 DEBUG(3) debugf("adding 'From:' header\n");
326 if (msg->full_sender_name) {
327 msg->hdr_list = g_list_append(msg->hdr_list,
328 create_header(HEAD_FROM,
329 "From: \"%s\" <%s@%s>\n",
330 msg->full_sender_name,
331 msg->return_path->local_part,
332 msg->return_path->domain));
333 } else {
334 msg->hdr_list = g_list_append(msg->hdr_list,
335 create_header(HEAD_FROM,
336 "From: <%s@%s>\n",
337 msg->return_path->local_part,
338 msg->return_path->domain));
339 }
340 }
341 if (!has_to_or_cc) {
342 DEBUG(3) debugf("no To: or Cc: header, hence adding "
343 "`To: undisclosed recipients:;'\n");
344 msg->hdr_list = g_list_append(msg->hdr_list,
345 create_header(HEAD_TO,
346 "To: undisclosed-recipients:;\n"));
347 }
348 if (!has_date) {
349 DEBUG(3) debugf("adding 'Date:' header\n");
350 msg->hdr_list = g_list_append(msg->hdr_list,
351 create_header(HEAD_DATE, "Date: %s\n",
352 rec_timestamp()));
353 }
354 if (!has_id) {
355 DEBUG(3) debugf("adding 'Message-ID:' header\n");
356 msg->hdr_list = g_list_append(msg->hdr_list,
357 create_header(HEAD_MESSAGE_ID,
358 "Message-ID: <%s@%s>\n",
359 msg->uid, conf.host_name));
360 }
362 return AERR_OK;
363 }
365 static void
366 add_received_hdr(message *msg)
367 {
368 gchar *for_string = NULL;
369 header *hdr = NULL;
370 address *addr;
372 DEBUG(3) debugf("adding 'Received:' header\n");
373 if (g_list_length(msg->rcpt_list) == 1) {
374 /* The `for' part only if exactly one rcpt is present */
375 addr = (address *) (g_list_first(msg->rcpt_list)->data);
376 for_string = g_strdup_printf("\n\tfor %s", addr_string(addr));
377 }
378 if (!msg->received_host) {
379 /* received locally */
380 hdr = create_header(HEAD_RECEIVED,
381 "Received: by %s (%s %s, from userid %d)\n"
382 "\tid %s%s; %s\n",
383 conf.host_name, PACKAGE, VERSION, geteuid(),
384 msg->uid,
385 for_string ? for_string : "", rec_timestamp());
386 } else {
387 /* received from remote */
388 DEBUG(5) debugf("adding 'Received:' header (5)\n");
389 hdr = create_header(HEAD_RECEIVED,
390 "Received: from %s\n"
391 "\tby %s with %s (%s %s)\n"
392 "\tid %s%s; %s\n",
393 msg->received_host, conf.host_name,
394 prot_names[msg->received_prot], PACKAGE,
395 VERSION, msg->uid,
396 for_string ? for_string : "", rec_timestamp());
397 }
398 msg->hdr_list = g_list_prepend(msg->hdr_list, hdr);
399 if (for_string) {
400 g_free(for_string);
401 }
402 }
404 accept_error
405 accept_message_prepare(message *msg, guint flags)
406 {
407 DEBUG(5) debugf("accept_message_prepare()\n");
409 /* generate unique message id */
410 msg->uid = g_malloc(14);
411 string_base62(msg->uid, time(NULL), 6);
412 msg->uid[6] = '-';
413 string_base62(msg->uid + 7, getpid(), 3);
414 msg->uid[10] = '-';
415 string_base62(msg->uid + 11, msg->transfer_id, 2);
416 msg->uid[13] = '\0';
418 /* if local, get password entry and set return path if missing */
419 if (!msg->received_host) {
420 struct passwd *passwd = NULL;
422 passwd = g_memdup(getpwuid(geteuid()), sizeof(struct passwd));
423 msg->ident = g_strdup(passwd->pw_name);
424 if (!msg->return_path) {
425 gchar *path = g_strdup_printf("<%s@%s>",
426 passwd->pw_name, conf.host_name);
427 DEBUG(3) debugf("setting return_path for local "
428 "accept: %s\n", path);
429 msg->return_path = create_address(path, TRUE);
430 g_free(path);
431 }
432 }
434 if (scan_headers(msg, flags) == AERR_NORCPT) {
435 return AERR_NORCPT;
436 }
438 /* after the hdrs are scanned because we need to know the rcpts */
439 add_received_hdr(msg);
441 return AERR_OK;
442 }
444 accept_error
445 accept_message(FILE *in, message *msg, guint flags)
446 {
447 accept_error err;
449 err = accept_message_stream(in, msg, flags);
450 if (err == AERR_OK) {
451 err = accept_message_prepare(msg, flags);
452 }
454 return err;
455 }