xref: /freebsd/contrib/dma/mail.c (revision e56bad4a94aff7e3e43200568b211de872a52aea)
1a9e8641dSBaptiste Daroussin /*
2*e56bad4aSBaptiste Daroussin  * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>.
3a9e8641dSBaptiste Daroussin  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
4a9e8641dSBaptiste Daroussin  *
5a9e8641dSBaptiste Daroussin  * This code is derived from software contributed to The DragonFly Project
6*e56bad4aSBaptiste Daroussin  * by Simon Schubert <2@0x2c.org>.
7a9e8641dSBaptiste Daroussin  *
8a9e8641dSBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
9a9e8641dSBaptiste Daroussin  * modification, are permitted provided that the following conditions
10a9e8641dSBaptiste Daroussin  * are met:
11a9e8641dSBaptiste Daroussin  *
12a9e8641dSBaptiste Daroussin  * 1. Redistributions of source code must retain the above copyright
13a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer.
14a9e8641dSBaptiste Daroussin  * 2. Redistributions in binary form must reproduce the above copyright
15a9e8641dSBaptiste Daroussin  *    notice, this list of conditions and the following disclaimer in
16a9e8641dSBaptiste Daroussin  *    the documentation and/or other materials provided with the
17a9e8641dSBaptiste Daroussin  *    distribution.
18a9e8641dSBaptiste Daroussin  * 3. Neither the name of The DragonFly Project nor the names of its
19a9e8641dSBaptiste Daroussin  *    contributors may be used to endorse or promote products derived
20a9e8641dSBaptiste Daroussin  *    from this software without specific, prior written permission.
21a9e8641dSBaptiste Daroussin  *
22a9e8641dSBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23a9e8641dSBaptiste Daroussin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24a9e8641dSBaptiste Daroussin  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25a9e8641dSBaptiste Daroussin  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26a9e8641dSBaptiste Daroussin  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27a9e8641dSBaptiste Daroussin  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28a9e8641dSBaptiste Daroussin  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29a9e8641dSBaptiste Daroussin  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30a9e8641dSBaptiste Daroussin  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31a9e8641dSBaptiste Daroussin  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32a9e8641dSBaptiste Daroussin  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33a9e8641dSBaptiste Daroussin  * SUCH DAMAGE.
34a9e8641dSBaptiste Daroussin  */
35a9e8641dSBaptiste Daroussin 
36a9e8641dSBaptiste Daroussin #include <errno.h>
37a9e8641dSBaptiste Daroussin #include <inttypes.h>
38a9e8641dSBaptiste Daroussin #include <signal.h>
39a9e8641dSBaptiste Daroussin #include <syslog.h>
40a9e8641dSBaptiste Daroussin #include <unistd.h>
41a9e8641dSBaptiste Daroussin 
42a9e8641dSBaptiste Daroussin #include "dma.h"
43a9e8641dSBaptiste Daroussin 
44a9e8641dSBaptiste Daroussin void
45a9e8641dSBaptiste Daroussin bounce(struct qitem *it, const char *reason)
46a9e8641dSBaptiste Daroussin {
47a9e8641dSBaptiste Daroussin 	struct queue bounceq;
48a9e8641dSBaptiste Daroussin 	char line[1000];
49a9e8641dSBaptiste Daroussin 	size_t pos;
50a9e8641dSBaptiste Daroussin 	int error;
51a9e8641dSBaptiste Daroussin 
52a9e8641dSBaptiste Daroussin 	/* Don't bounce bounced mails */
53a9e8641dSBaptiste Daroussin 	if (it->sender[0] == 0) {
54a9e8641dSBaptiste Daroussin 		syslog(LOG_INFO, "can not bounce a bounce message, discarding");
55*e56bad4aSBaptiste Daroussin 		exit(EX_SOFTWARE);
56a9e8641dSBaptiste Daroussin 	}
57a9e8641dSBaptiste Daroussin 
58a9e8641dSBaptiste Daroussin 	bzero(&bounceq, sizeof(bounceq));
59a9e8641dSBaptiste Daroussin 	LIST_INIT(&bounceq.queue);
60a9e8641dSBaptiste Daroussin 	bounceq.sender = "";
61a9e8641dSBaptiste Daroussin 	if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0)
62a9e8641dSBaptiste Daroussin 		goto fail;
63a9e8641dSBaptiste Daroussin 
64a9e8641dSBaptiste Daroussin 	if (newspoolf(&bounceq) != 0)
65a9e8641dSBaptiste Daroussin 		goto fail;
66a9e8641dSBaptiste Daroussin 
67a9e8641dSBaptiste Daroussin 	syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
68a9e8641dSBaptiste Daroussin 	setlogident("%s", bounceq.id);
69a9e8641dSBaptiste Daroussin 
70a9e8641dSBaptiste Daroussin 	error = fprintf(bounceq.mailf,
71a9e8641dSBaptiste Daroussin 		"Received: from MAILER-DAEMON\n"
72a9e8641dSBaptiste Daroussin 		"\tid %s\n"
73a9e8641dSBaptiste Daroussin 		"\tby %s (%s);\n"
74a9e8641dSBaptiste Daroussin 		"\t%s\n"
75a9e8641dSBaptiste Daroussin 		"X-Original-To: <%s>\n"
76a9e8641dSBaptiste Daroussin 		"From: MAILER-DAEMON <>\n"
77a9e8641dSBaptiste Daroussin 		"To: %s\n"
78a9e8641dSBaptiste Daroussin 		"Subject: Mail delivery failed\n"
79a9e8641dSBaptiste Daroussin 		"Message-Id: <%s@%s>\n"
80a9e8641dSBaptiste Daroussin 		"Date: %s\n"
81a9e8641dSBaptiste Daroussin 		"\n"
82a9e8641dSBaptiste Daroussin 		"This is the %s at %s.\n"
83a9e8641dSBaptiste Daroussin 		"\n"
84a9e8641dSBaptiste Daroussin 		"There was an error delivering your mail to <%s>.\n"
85a9e8641dSBaptiste Daroussin 		"\n"
86a9e8641dSBaptiste Daroussin 		"%s\n"
87a9e8641dSBaptiste Daroussin 		"\n"
88a9e8641dSBaptiste Daroussin 		"%s\n"
89a9e8641dSBaptiste Daroussin 		"\n",
90a9e8641dSBaptiste Daroussin 		bounceq.id,
91a9e8641dSBaptiste Daroussin 		hostname(), VERSION,
92a9e8641dSBaptiste Daroussin 		rfc822date(),
93a9e8641dSBaptiste Daroussin 		it->addr,
94a9e8641dSBaptiste Daroussin 		it->sender,
95a9e8641dSBaptiste Daroussin 		bounceq.id, hostname(),
96a9e8641dSBaptiste Daroussin 		rfc822date(),
97a9e8641dSBaptiste Daroussin 		VERSION, hostname(),
98a9e8641dSBaptiste Daroussin 		it->addr,
99a9e8641dSBaptiste Daroussin 		reason,
100a9e8641dSBaptiste Daroussin 		config.features & FULLBOUNCE ?
101a9e8641dSBaptiste Daroussin 		    "Original message follows." :
102a9e8641dSBaptiste Daroussin 		    "Message headers follow.");
103a9e8641dSBaptiste Daroussin 	if (error < 0)
104a9e8641dSBaptiste Daroussin 		goto fail;
105a9e8641dSBaptiste Daroussin 
106a9e8641dSBaptiste Daroussin 	if (fseek(it->mailf, 0, SEEK_SET) != 0)
107a9e8641dSBaptiste Daroussin 		goto fail;
108a9e8641dSBaptiste Daroussin 	if (config.features & FULLBOUNCE) {
109a9e8641dSBaptiste Daroussin 		while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
110a9e8641dSBaptiste Daroussin 			if (fwrite(line, 1, pos, bounceq.mailf) != pos)
111a9e8641dSBaptiste Daroussin 				goto fail;
112a9e8641dSBaptiste Daroussin 		}
113a9e8641dSBaptiste Daroussin 	} else {
114a9e8641dSBaptiste Daroussin 		while (!feof(it->mailf)) {
115a9e8641dSBaptiste Daroussin 			if (fgets(line, sizeof(line), it->mailf) == NULL)
116a9e8641dSBaptiste Daroussin 				break;
117a9e8641dSBaptiste Daroussin 			if (line[0] == '\n')
118a9e8641dSBaptiste Daroussin 				break;
119a9e8641dSBaptiste Daroussin 			if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1)
120a9e8641dSBaptiste Daroussin 				goto fail;
121a9e8641dSBaptiste Daroussin 		}
122a9e8641dSBaptiste Daroussin 	}
123a9e8641dSBaptiste Daroussin 
124a9e8641dSBaptiste Daroussin 	if (linkspool(&bounceq) != 0)
125a9e8641dSBaptiste Daroussin 		goto fail;
126a9e8641dSBaptiste Daroussin 	/* bounce is safe */
127a9e8641dSBaptiste Daroussin 
128a9e8641dSBaptiste Daroussin 	delqueue(it);
129a9e8641dSBaptiste Daroussin 
130a9e8641dSBaptiste Daroussin 	run_queue(&bounceq);
131a9e8641dSBaptiste Daroussin 	/* NOTREACHED */
132a9e8641dSBaptiste Daroussin 
133a9e8641dSBaptiste Daroussin fail:
134a9e8641dSBaptiste Daroussin 	syslog(LOG_CRIT, "error creating bounce: %m");
135a9e8641dSBaptiste Daroussin 	delqueue(it);
136*e56bad4aSBaptiste Daroussin 	exit(EX_IOERR);
137a9e8641dSBaptiste Daroussin }
138a9e8641dSBaptiste Daroussin 
139a9e8641dSBaptiste Daroussin struct parse_state {
140a9e8641dSBaptiste Daroussin 	char addr[1000];
141a9e8641dSBaptiste Daroussin 	int pos;
142a9e8641dSBaptiste Daroussin 
143a9e8641dSBaptiste Daroussin 	enum {
144a9e8641dSBaptiste Daroussin 		NONE = 0,
145a9e8641dSBaptiste Daroussin 		START,
146a9e8641dSBaptiste Daroussin 		MAIN,
147a9e8641dSBaptiste Daroussin 		EOL,
148a9e8641dSBaptiste Daroussin 		QUIT
149a9e8641dSBaptiste Daroussin 	} state;
150a9e8641dSBaptiste Daroussin 	int comment;
151a9e8641dSBaptiste Daroussin 	int quote;
152a9e8641dSBaptiste Daroussin 	int brackets;
153a9e8641dSBaptiste Daroussin 	int esc;
154a9e8641dSBaptiste Daroussin };
155a9e8641dSBaptiste Daroussin 
156a9e8641dSBaptiste Daroussin /*
157a9e8641dSBaptiste Daroussin  * Simplified RFC2822 header/address parsing.
158a9e8641dSBaptiste Daroussin  * We copy escapes and quoted strings directly, since
159a9e8641dSBaptiste Daroussin  * we have to pass them like this to the mail server anyways.
160a9e8641dSBaptiste Daroussin  * XXX local addresses will need treatment
161a9e8641dSBaptiste Daroussin  */
162a9e8641dSBaptiste Daroussin static int
163a9e8641dSBaptiste Daroussin parse_addrs(struct parse_state *ps, char *s, struct queue *queue)
164a9e8641dSBaptiste Daroussin {
165a9e8641dSBaptiste Daroussin 	char *addr;
166a9e8641dSBaptiste Daroussin 
167a9e8641dSBaptiste Daroussin again:
168a9e8641dSBaptiste Daroussin 	switch (ps->state) {
169a9e8641dSBaptiste Daroussin 	case NONE:
170a9e8641dSBaptiste Daroussin 		return (-1);
171a9e8641dSBaptiste Daroussin 
172a9e8641dSBaptiste Daroussin 	case START:
173a9e8641dSBaptiste Daroussin 		/* init our data */
174a9e8641dSBaptiste Daroussin 		bzero(ps, sizeof(*ps));
175a9e8641dSBaptiste Daroussin 
176a9e8641dSBaptiste Daroussin 		/* skip over header name */
177a9e8641dSBaptiste Daroussin 		while (*s != ':')
178a9e8641dSBaptiste Daroussin 			s++;
179a9e8641dSBaptiste Daroussin 		s++;
180a9e8641dSBaptiste Daroussin 		ps->state = MAIN;
181a9e8641dSBaptiste Daroussin 		break;
182a9e8641dSBaptiste Daroussin 
183a9e8641dSBaptiste Daroussin 	case MAIN:
184a9e8641dSBaptiste Daroussin 		/* all fine */
185a9e8641dSBaptiste Daroussin 		break;
186a9e8641dSBaptiste Daroussin 
187a9e8641dSBaptiste Daroussin 	case EOL:
188a9e8641dSBaptiste Daroussin 		switch (*s) {
189a9e8641dSBaptiste Daroussin 		case ' ':
190a9e8641dSBaptiste Daroussin 		case '\t':
191a9e8641dSBaptiste Daroussin 			s++;
192a9e8641dSBaptiste Daroussin 			/* continue */
193a9e8641dSBaptiste Daroussin 			break;
194a9e8641dSBaptiste Daroussin 
195a9e8641dSBaptiste Daroussin 		default:
196a9e8641dSBaptiste Daroussin 			ps->state = QUIT;
197a9e8641dSBaptiste Daroussin 			if (ps->pos != 0)
198a9e8641dSBaptiste Daroussin 				goto newaddr;
199a9e8641dSBaptiste Daroussin 			return (0);
200a9e8641dSBaptiste Daroussin 		}
201a9e8641dSBaptiste Daroussin 
202a9e8641dSBaptiste Daroussin 	case QUIT:
203a9e8641dSBaptiste Daroussin 		return (0);
204a9e8641dSBaptiste Daroussin 	}
205a9e8641dSBaptiste Daroussin 
206a9e8641dSBaptiste Daroussin 	for (; *s != 0; s++) {
207a9e8641dSBaptiste Daroussin 		if (ps->esc) {
208a9e8641dSBaptiste Daroussin 			ps->esc = 0;
209a9e8641dSBaptiste Daroussin 
210a9e8641dSBaptiste Daroussin 			switch (*s) {
211a9e8641dSBaptiste Daroussin 			case '\r':
212a9e8641dSBaptiste Daroussin 			case '\n':
213a9e8641dSBaptiste Daroussin 				goto err;
214a9e8641dSBaptiste Daroussin 
215a9e8641dSBaptiste Daroussin 			default:
216a9e8641dSBaptiste Daroussin 				goto copy;
217a9e8641dSBaptiste Daroussin 			}
218a9e8641dSBaptiste Daroussin 		}
219a9e8641dSBaptiste Daroussin 
220a9e8641dSBaptiste Daroussin 		if (ps->quote) {
221a9e8641dSBaptiste Daroussin 			switch (*s) {
222a9e8641dSBaptiste Daroussin 			case '"':
223a9e8641dSBaptiste Daroussin 				ps->quote = 0;
224a9e8641dSBaptiste Daroussin 				goto copy;
225a9e8641dSBaptiste Daroussin 
226a9e8641dSBaptiste Daroussin 			case '\\':
227a9e8641dSBaptiste Daroussin 				ps->esc = 1;
228a9e8641dSBaptiste Daroussin 				goto copy;
229a9e8641dSBaptiste Daroussin 
230a9e8641dSBaptiste Daroussin 			case '\r':
231a9e8641dSBaptiste Daroussin 			case '\n':
232a9e8641dSBaptiste Daroussin 				goto eol;
233a9e8641dSBaptiste Daroussin 
234a9e8641dSBaptiste Daroussin 			default:
235a9e8641dSBaptiste Daroussin 				goto copy;
236a9e8641dSBaptiste Daroussin 			}
237a9e8641dSBaptiste Daroussin 		}
238a9e8641dSBaptiste Daroussin 
239a9e8641dSBaptiste Daroussin 		switch (*s) {
240a9e8641dSBaptiste Daroussin 		case '(':
241a9e8641dSBaptiste Daroussin 			ps->comment++;
242a9e8641dSBaptiste Daroussin 			break;
243a9e8641dSBaptiste Daroussin 
244a9e8641dSBaptiste Daroussin 		case ')':
245a9e8641dSBaptiste Daroussin 			if (ps->comment)
246a9e8641dSBaptiste Daroussin 				ps->comment--;
247a9e8641dSBaptiste Daroussin 			else
248a9e8641dSBaptiste Daroussin 				goto err;
249a9e8641dSBaptiste Daroussin 			goto skip;
250a9e8641dSBaptiste Daroussin 
251a9e8641dSBaptiste Daroussin 		case '"':
252a9e8641dSBaptiste Daroussin 			ps->quote = 1;
253a9e8641dSBaptiste Daroussin 			goto copy;
254a9e8641dSBaptiste Daroussin 
255a9e8641dSBaptiste Daroussin 		case '\\':
256a9e8641dSBaptiste Daroussin 			ps->esc = 1;
257a9e8641dSBaptiste Daroussin 			goto copy;
258a9e8641dSBaptiste Daroussin 
259a9e8641dSBaptiste Daroussin 		case '\r':
260a9e8641dSBaptiste Daroussin 		case '\n':
261a9e8641dSBaptiste Daroussin 			goto eol;
262a9e8641dSBaptiste Daroussin 		}
263a9e8641dSBaptiste Daroussin 
264a9e8641dSBaptiste Daroussin 		if (ps->comment)
265a9e8641dSBaptiste Daroussin 			goto skip;
266a9e8641dSBaptiste Daroussin 
267a9e8641dSBaptiste Daroussin 		switch (*s) {
268a9e8641dSBaptiste Daroussin 		case ' ':
269a9e8641dSBaptiste Daroussin 		case '\t':
270a9e8641dSBaptiste Daroussin 			/* ignore whitespace */
271a9e8641dSBaptiste Daroussin 			goto skip;
272a9e8641dSBaptiste Daroussin 
273a9e8641dSBaptiste Daroussin 		case '<':
274a9e8641dSBaptiste Daroussin 			/* this is the real address now */
275a9e8641dSBaptiste Daroussin 			ps->brackets = 1;
276a9e8641dSBaptiste Daroussin 			ps->pos = 0;
277a9e8641dSBaptiste Daroussin 			goto skip;
278a9e8641dSBaptiste Daroussin 
279a9e8641dSBaptiste Daroussin 		case '>':
280a9e8641dSBaptiste Daroussin 			if (!ps->brackets)
281a9e8641dSBaptiste Daroussin 				goto err;
282a9e8641dSBaptiste Daroussin 			ps->brackets = 0;
283a9e8641dSBaptiste Daroussin 
284a9e8641dSBaptiste Daroussin 			s++;
285a9e8641dSBaptiste Daroussin 			goto newaddr;
286a9e8641dSBaptiste Daroussin 
287a9e8641dSBaptiste Daroussin 		case ':':
288a9e8641dSBaptiste Daroussin 			/* group - ignore */
289a9e8641dSBaptiste Daroussin 			ps->pos = 0;
290a9e8641dSBaptiste Daroussin 			goto skip;
291a9e8641dSBaptiste Daroussin 
292a9e8641dSBaptiste Daroussin 		case ',':
293a9e8641dSBaptiste Daroussin 		case ';':
294a9e8641dSBaptiste Daroussin 			/*
295a9e8641dSBaptiste Daroussin 			 * Next address, copy previous one.
296a9e8641dSBaptiste Daroussin 			 * However, we might be directly after
297a9e8641dSBaptiste Daroussin 			 * a <address>, or have two consecutive
298a9e8641dSBaptiste Daroussin 			 * commas.
299a9e8641dSBaptiste Daroussin 			 * Skip the comma unless there is
300a9e8641dSBaptiste Daroussin 			 * really something to copy.
301a9e8641dSBaptiste Daroussin 			 */
302a9e8641dSBaptiste Daroussin 			if (ps->pos == 0)
303a9e8641dSBaptiste Daroussin 				goto skip;
304a9e8641dSBaptiste Daroussin 			s++;
305a9e8641dSBaptiste Daroussin 			goto newaddr;
306a9e8641dSBaptiste Daroussin 
307a9e8641dSBaptiste Daroussin 		default:
308a9e8641dSBaptiste Daroussin 			goto copy;
309a9e8641dSBaptiste Daroussin 		}
310a9e8641dSBaptiste Daroussin 
311a9e8641dSBaptiste Daroussin copy:
312a9e8641dSBaptiste Daroussin 		if (ps->comment)
313a9e8641dSBaptiste Daroussin 			goto skip;
314a9e8641dSBaptiste Daroussin 
315a9e8641dSBaptiste Daroussin 		if (ps->pos + 1 == sizeof(ps->addr))
316a9e8641dSBaptiste Daroussin 			goto err;
317a9e8641dSBaptiste Daroussin 		ps->addr[ps->pos++] = *s;
318a9e8641dSBaptiste Daroussin 
319a9e8641dSBaptiste Daroussin skip:
320a9e8641dSBaptiste Daroussin 		;
321a9e8641dSBaptiste Daroussin 	}
322a9e8641dSBaptiste Daroussin 
323a9e8641dSBaptiste Daroussin eol:
324a9e8641dSBaptiste Daroussin 	ps->state = EOL;
325a9e8641dSBaptiste Daroussin 	return (0);
326a9e8641dSBaptiste Daroussin 
327a9e8641dSBaptiste Daroussin err:
328a9e8641dSBaptiste Daroussin 	ps->state = QUIT;
329a9e8641dSBaptiste Daroussin 	return (-1);
330a9e8641dSBaptiste Daroussin 
331a9e8641dSBaptiste Daroussin newaddr:
332a9e8641dSBaptiste Daroussin 	ps->addr[ps->pos] = 0;
333a9e8641dSBaptiste Daroussin 	ps->pos = 0;
334a9e8641dSBaptiste Daroussin 	addr = strdup(ps->addr);
335a9e8641dSBaptiste Daroussin 	if (addr == NULL)
336*e56bad4aSBaptiste Daroussin 		errlog(EX_SOFTWARE, NULL);
337a9e8641dSBaptiste Daroussin 
338a9e8641dSBaptiste Daroussin 	if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
339*e56bad4aSBaptiste Daroussin 		errlogx(EX_DATAERR, "invalid recipient `%s'", addr);
340a9e8641dSBaptiste Daroussin 
341a9e8641dSBaptiste Daroussin 	goto again;
342a9e8641dSBaptiste Daroussin }
343a9e8641dSBaptiste Daroussin 
344a9e8641dSBaptiste Daroussin int
345a9e8641dSBaptiste Daroussin readmail(struct queue *queue, int nodot, int recp_from_header)
346a9e8641dSBaptiste Daroussin {
347a9e8641dSBaptiste Daroussin 	struct parse_state parse_state;
348a9e8641dSBaptiste Daroussin 	char line[1000];	/* by RFC2822 */
349a9e8641dSBaptiste Daroussin 	size_t linelen;
350a9e8641dSBaptiste Daroussin 	size_t error;
351a9e8641dSBaptiste Daroussin 	int had_headers = 0;
352a9e8641dSBaptiste Daroussin 	int had_from = 0;
353a9e8641dSBaptiste Daroussin 	int had_messagid = 0;
354a9e8641dSBaptiste Daroussin 	int had_date = 0;
355a9e8641dSBaptiste Daroussin 	int had_last_line = 0;
356a9e8641dSBaptiste Daroussin 	int nocopy = 0;
357a9e8641dSBaptiste Daroussin 
358a9e8641dSBaptiste Daroussin 	parse_state.state = NONE;
359a9e8641dSBaptiste Daroussin 
360a9e8641dSBaptiste Daroussin 	error = fprintf(queue->mailf,
361a9e8641dSBaptiste Daroussin 		"Received: from %s (uid %d)\n"
362a9e8641dSBaptiste Daroussin 		"\t(envelope-from %s)\n"
363a9e8641dSBaptiste Daroussin 		"\tid %s\n"
364a9e8641dSBaptiste Daroussin 		"\tby %s (%s);\n"
365a9e8641dSBaptiste Daroussin 		"\t%s\n",
366a9e8641dSBaptiste Daroussin 		username, useruid,
367a9e8641dSBaptiste Daroussin 		queue->sender,
368a9e8641dSBaptiste Daroussin 		queue->id,
369a9e8641dSBaptiste Daroussin 		hostname(), VERSION,
370a9e8641dSBaptiste Daroussin 		rfc822date());
371a9e8641dSBaptiste Daroussin 	if ((ssize_t)error < 0)
372a9e8641dSBaptiste Daroussin 		return (-1);
373a9e8641dSBaptiste Daroussin 
374a9e8641dSBaptiste Daroussin 	while (!feof(stdin)) {
375a9e8641dSBaptiste Daroussin 		if (fgets(line, sizeof(line) - 1, stdin) == NULL)
376a9e8641dSBaptiste Daroussin 			break;
377a9e8641dSBaptiste Daroussin 		if (had_last_line)
378*e56bad4aSBaptiste Daroussin 			errlogx(EX_DATAERR, "bad mail input format:"
379*e56bad4aSBaptiste Daroussin 				" from %s (uid %d) (envelope-from %s)",
380*e56bad4aSBaptiste Daroussin 				username, useruid, queue->sender);
381a9e8641dSBaptiste Daroussin 		linelen = strlen(line);
382a9e8641dSBaptiste Daroussin 		if (linelen == 0 || line[linelen - 1] != '\n') {
383a9e8641dSBaptiste Daroussin 			/*
384a9e8641dSBaptiste Daroussin 			 * This line did not end with a newline character.
385a9e8641dSBaptiste Daroussin 			 * If we fix it, it better be the last line of
386a9e8641dSBaptiste Daroussin 			 * the file.
387a9e8641dSBaptiste Daroussin 			 */
388a9e8641dSBaptiste Daroussin 			line[linelen] = '\n';
389a9e8641dSBaptiste Daroussin 			line[linelen + 1] = 0;
390a9e8641dSBaptiste Daroussin 			had_last_line = 1;
391a9e8641dSBaptiste Daroussin 		}
392a9e8641dSBaptiste Daroussin 		if (!had_headers) {
393a9e8641dSBaptiste Daroussin 			/*
394a9e8641dSBaptiste Daroussin 			 * Unless this is a continuation, switch of
395a9e8641dSBaptiste Daroussin 			 * the Bcc: nocopy flag.
396a9e8641dSBaptiste Daroussin 			 */
397a9e8641dSBaptiste Daroussin 			if (!(line[0] == ' ' || line[0] == '\t'))
398a9e8641dSBaptiste Daroussin 				nocopy = 0;
399a9e8641dSBaptiste Daroussin 
400a9e8641dSBaptiste Daroussin 			if (strprefixcmp(line, "Date:") == 0)
401a9e8641dSBaptiste Daroussin 				had_date = 1;
402a9e8641dSBaptiste Daroussin 			else if (strprefixcmp(line, "Message-Id:") == 0)
403a9e8641dSBaptiste Daroussin 				had_messagid = 1;
404a9e8641dSBaptiste Daroussin 			else if (strprefixcmp(line, "From:") == 0)
405a9e8641dSBaptiste Daroussin 				had_from = 1;
406a9e8641dSBaptiste Daroussin 			else if (strprefixcmp(line, "Bcc:") == 0)
407a9e8641dSBaptiste Daroussin 				nocopy = 1;
408a9e8641dSBaptiste Daroussin 
409a9e8641dSBaptiste Daroussin 			if (parse_state.state != NONE) {
410a9e8641dSBaptiste Daroussin 				if (parse_addrs(&parse_state, line, queue) < 0) {
411*e56bad4aSBaptiste Daroussin 					errlogx(EX_DATAERR, "invalid address in header\n");
412a9e8641dSBaptiste Daroussin 					/* NOTREACHED */
413a9e8641dSBaptiste Daroussin 				}
414a9e8641dSBaptiste Daroussin 			}
415a9e8641dSBaptiste Daroussin 
416a9e8641dSBaptiste Daroussin 			if (recp_from_header && (
417a9e8641dSBaptiste Daroussin 					strprefixcmp(line, "To:") == 0 ||
418a9e8641dSBaptiste Daroussin 					strprefixcmp(line, "Cc:") == 0 ||
419a9e8641dSBaptiste Daroussin 					strprefixcmp(line, "Bcc:") == 0)) {
420a9e8641dSBaptiste Daroussin 				parse_state.state = START;
421a9e8641dSBaptiste Daroussin 				if (parse_addrs(&parse_state, line, queue) < 0) {
422*e56bad4aSBaptiste Daroussin 					errlogx(EX_DATAERR, "invalid address in header\n");
423a9e8641dSBaptiste Daroussin 					/* NOTREACHED */
424a9e8641dSBaptiste Daroussin 				}
425a9e8641dSBaptiste Daroussin 			}
426a9e8641dSBaptiste Daroussin 		}
427a9e8641dSBaptiste Daroussin 
428a9e8641dSBaptiste Daroussin 		if (strcmp(line, "\n") == 0 && !had_headers) {
429a9e8641dSBaptiste Daroussin 			had_headers = 1;
430a9e8641dSBaptiste Daroussin 			while (!had_date || !had_messagid || !had_from) {
431a9e8641dSBaptiste Daroussin 				if (!had_date) {
432a9e8641dSBaptiste Daroussin 					had_date = 1;
433a9e8641dSBaptiste Daroussin 					snprintf(line, sizeof(line), "Date: %s\n", rfc822date());
434a9e8641dSBaptiste Daroussin 				} else if (!had_messagid) {
435a9e8641dSBaptiste Daroussin 					/* XXX msgid, assign earlier and log? */
436a9e8641dSBaptiste Daroussin 					had_messagid = 1;
437a9e8641dSBaptiste Daroussin 					snprintf(line, sizeof(line), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
438a9e8641dSBaptiste Daroussin 						 (uintmax_t)time(NULL),
439a9e8641dSBaptiste Daroussin 						 queue->id,
440a9e8641dSBaptiste Daroussin 						 (uintmax_t)random(),
441a9e8641dSBaptiste Daroussin 						 hostname());
442a9e8641dSBaptiste Daroussin 				} else if (!had_from) {
443a9e8641dSBaptiste Daroussin 					had_from = 1;
444a9e8641dSBaptiste Daroussin 					snprintf(line, sizeof(line), "From: <%s>\n", queue->sender);
445a9e8641dSBaptiste Daroussin 				}
446a9e8641dSBaptiste Daroussin 				if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
447a9e8641dSBaptiste Daroussin 					return (-1);
448a9e8641dSBaptiste Daroussin 			}
449a9e8641dSBaptiste Daroussin 			strcpy(line, "\n");
450a9e8641dSBaptiste Daroussin 		}
451a9e8641dSBaptiste Daroussin 		if (!nodot && linelen == 2 && line[0] == '.')
452a9e8641dSBaptiste Daroussin 			break;
453a9e8641dSBaptiste Daroussin 		if (!nocopy) {
454a9e8641dSBaptiste Daroussin 			if (fwrite(line, strlen(line), 1, queue->mailf) != 1)
455a9e8641dSBaptiste Daroussin 				return (-1);
456a9e8641dSBaptiste Daroussin 		}
457a9e8641dSBaptiste Daroussin 	}
458a9e8641dSBaptiste Daroussin 
459a9e8641dSBaptiste Daroussin 	return (0);
460a9e8641dSBaptiste Daroussin }
461