xref: /freebsd/contrib/dma/mail.c (revision 169487223143b1232ec4686b720b028af8d6d42b)
1a9e8641dSBaptiste Daroussin /*
2e56bad4aSBaptiste 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
6e56bad4aSBaptiste 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>
38*16948722SEd Maste #include <malloc_np.h>
39a9e8641dSBaptiste Daroussin #include <signal.h>
40fbe95b88SBaptiste Daroussin #include <strings.h>
41b86d1398SJung-uk Kim #include <string.h>
42a9e8641dSBaptiste Daroussin #include <syslog.h>
43a9e8641dSBaptiste Daroussin #include <unistd.h>
44a9e8641dSBaptiste Daroussin 
45a9e8641dSBaptiste Daroussin #include "dma.h"
46a9e8641dSBaptiste Daroussin 
471a0dde33SEd Maste #define MAX_LINE_RFC822	999 /* 998 characters plus \n */
48b0b2d05fSBaptiste Daroussin 
49a9e8641dSBaptiste Daroussin void
bounce(struct qitem * it,const char * reason)50a9e8641dSBaptiste Daroussin bounce(struct qitem *it, const char *reason)
51a9e8641dSBaptiste Daroussin {
52a9e8641dSBaptiste Daroussin 	struct queue bounceq;
53a9e8641dSBaptiste Daroussin 	char line[1000];
54a9e8641dSBaptiste Daroussin 	size_t pos;
55a9e8641dSBaptiste Daroussin 	int error;
56a9e8641dSBaptiste Daroussin 
57a9e8641dSBaptiste Daroussin 	/* Don't bounce bounced mails */
58a9e8641dSBaptiste Daroussin 	if (it->sender[0] == 0) {
59a9e8641dSBaptiste Daroussin 		syslog(LOG_INFO, "can not bounce a bounce message, discarding");
60e56bad4aSBaptiste Daroussin 		exit(EX_SOFTWARE);
61a9e8641dSBaptiste Daroussin 	}
62a9e8641dSBaptiste Daroussin 
63a9e8641dSBaptiste Daroussin 	bzero(&bounceq, sizeof(bounceq));
64a9e8641dSBaptiste Daroussin 	LIST_INIT(&bounceq.queue);
65a9e8641dSBaptiste Daroussin 	bounceq.sender = "";
66a9e8641dSBaptiste Daroussin 	if (add_recp(&bounceq, it->sender, EXPAND_WILDCARD) != 0)
67a9e8641dSBaptiste Daroussin 		goto fail;
68a9e8641dSBaptiste Daroussin 
69a9e8641dSBaptiste Daroussin 	if (newspoolf(&bounceq) != 0)
70a9e8641dSBaptiste Daroussin 		goto fail;
71a9e8641dSBaptiste Daroussin 
72a9e8641dSBaptiste Daroussin 	syslog(LOG_ERR, "delivery failed, bouncing as %s", bounceq.id);
73a9e8641dSBaptiste Daroussin 	setlogident("%s", bounceq.id);
74a9e8641dSBaptiste Daroussin 
75a9e8641dSBaptiste Daroussin 	error = fprintf(bounceq.mailf,
76a9e8641dSBaptiste Daroussin 		"Received: from MAILER-DAEMON\n"
77a9e8641dSBaptiste Daroussin 		"\tid %s\n"
78fbe95b88SBaptiste Daroussin 		"\tby %s (%s on %s);\n"
79a9e8641dSBaptiste Daroussin 		"\t%s\n"
80a9e8641dSBaptiste Daroussin 		"X-Original-To: <%s>\n"
81a9e8641dSBaptiste Daroussin 		"From: MAILER-DAEMON <>\n"
82a9e8641dSBaptiste Daroussin 		"To: %s\n"
83a9e8641dSBaptiste Daroussin 		"Subject: Mail delivery failed\n"
84a9e8641dSBaptiste Daroussin 		"Message-Id: <%s@%s>\n"
85a9e8641dSBaptiste Daroussin 		"Date: %s\n"
86a9e8641dSBaptiste Daroussin 		"\n"
87a9e8641dSBaptiste Daroussin 		"This is the %s at %s.\n"
88a9e8641dSBaptiste Daroussin 		"\n"
89a9e8641dSBaptiste Daroussin 		"There was an error delivering your mail to <%s>.\n"
90a9e8641dSBaptiste Daroussin 		"\n"
91a9e8641dSBaptiste Daroussin 		"%s\n"
92a9e8641dSBaptiste Daroussin 		"\n"
93a9e8641dSBaptiste Daroussin 		"%s\n"
94a9e8641dSBaptiste Daroussin 		"\n",
95a9e8641dSBaptiste Daroussin 		bounceq.id,
96fbe95b88SBaptiste Daroussin 		hostname(), VERSION, systemhostname(),
97a9e8641dSBaptiste Daroussin 		rfc822date(),
98a9e8641dSBaptiste Daroussin 		it->addr,
99a9e8641dSBaptiste Daroussin 		it->sender,
100a9e8641dSBaptiste Daroussin 		bounceq.id, hostname(),
101a9e8641dSBaptiste Daroussin 		rfc822date(),
102a9e8641dSBaptiste Daroussin 		VERSION, hostname(),
103a9e8641dSBaptiste Daroussin 		it->addr,
104a9e8641dSBaptiste Daroussin 		reason,
105a9e8641dSBaptiste Daroussin 		config.features & FULLBOUNCE ?
106a9e8641dSBaptiste Daroussin 		    "Original message follows." :
107a9e8641dSBaptiste Daroussin 		    "Message headers follow.");
108a9e8641dSBaptiste Daroussin 	if (error < 0)
109a9e8641dSBaptiste Daroussin 		goto fail;
110a9e8641dSBaptiste Daroussin 
111a9e8641dSBaptiste Daroussin 	if (fseek(it->mailf, 0, SEEK_SET) != 0)
112a9e8641dSBaptiste Daroussin 		goto fail;
113a9e8641dSBaptiste Daroussin 	if (config.features & FULLBOUNCE) {
114a9e8641dSBaptiste Daroussin 		while ((pos = fread(line, 1, sizeof(line), it->mailf)) > 0) {
115a9e8641dSBaptiste Daroussin 			if (fwrite(line, 1, pos, bounceq.mailf) != pos)
116a9e8641dSBaptiste Daroussin 				goto fail;
117a9e8641dSBaptiste Daroussin 		}
118a9e8641dSBaptiste Daroussin 	} else {
119a9e8641dSBaptiste Daroussin 		while (!feof(it->mailf)) {
120a9e8641dSBaptiste Daroussin 			if (fgets(line, sizeof(line), it->mailf) == NULL)
121a9e8641dSBaptiste Daroussin 				break;
122a9e8641dSBaptiste Daroussin 			if (line[0] == '\n')
123a9e8641dSBaptiste Daroussin 				break;
124a9e8641dSBaptiste Daroussin 			if (fwrite(line, strlen(line), 1, bounceq.mailf) != 1)
125a9e8641dSBaptiste Daroussin 				goto fail;
126a9e8641dSBaptiste Daroussin 		}
127a9e8641dSBaptiste Daroussin 	}
128a9e8641dSBaptiste Daroussin 
129a9e8641dSBaptiste Daroussin 	if (linkspool(&bounceq) != 0)
130a9e8641dSBaptiste Daroussin 		goto fail;
131a9e8641dSBaptiste Daroussin 	/* bounce is safe */
132a9e8641dSBaptiste Daroussin 
133a9e8641dSBaptiste Daroussin 	delqueue(it);
134a9e8641dSBaptiste Daroussin 
135a9e8641dSBaptiste Daroussin 	run_queue(&bounceq);
136a9e8641dSBaptiste Daroussin 	/* NOTREACHED */
137a9e8641dSBaptiste Daroussin 
138a9e8641dSBaptiste Daroussin fail:
139a9e8641dSBaptiste Daroussin 	syslog(LOG_CRIT, "error creating bounce: %m");
140a9e8641dSBaptiste Daroussin 	delqueue(it);
141e56bad4aSBaptiste Daroussin 	exit(EX_IOERR);
142a9e8641dSBaptiste Daroussin }
143a9e8641dSBaptiste Daroussin 
144a9e8641dSBaptiste Daroussin struct parse_state {
145a9e8641dSBaptiste Daroussin 	char addr[1000];
146a9e8641dSBaptiste Daroussin 	int pos;
147a9e8641dSBaptiste Daroussin 
148a9e8641dSBaptiste Daroussin 	enum {
149a9e8641dSBaptiste Daroussin 		NONE = 0,
150a9e8641dSBaptiste Daroussin 		START,
151a9e8641dSBaptiste Daroussin 		MAIN,
152a9e8641dSBaptiste Daroussin 		EOL,
153a9e8641dSBaptiste Daroussin 		QUIT
154a9e8641dSBaptiste Daroussin 	} state;
155a9e8641dSBaptiste Daroussin 	int comment;
156a9e8641dSBaptiste Daroussin 	int quote;
157a9e8641dSBaptiste Daroussin 	int brackets;
158a9e8641dSBaptiste Daroussin 	int esc;
159a9e8641dSBaptiste Daroussin };
160a9e8641dSBaptiste Daroussin 
161a9e8641dSBaptiste Daroussin /*
162a9e8641dSBaptiste Daroussin  * Simplified RFC2822 header/address parsing.
163a9e8641dSBaptiste Daroussin  * We copy escapes and quoted strings directly, since
164a9e8641dSBaptiste Daroussin  * we have to pass them like this to the mail server anyways.
165a9e8641dSBaptiste Daroussin  * XXX local addresses will need treatment
166a9e8641dSBaptiste Daroussin  */
167a9e8641dSBaptiste Daroussin static int
parse_addrs(struct parse_state * ps,char * s,struct queue * queue)168a9e8641dSBaptiste Daroussin parse_addrs(struct parse_state *ps, char *s, struct queue *queue)
169a9e8641dSBaptiste Daroussin {
170a9e8641dSBaptiste Daroussin 	char *addr;
171a9e8641dSBaptiste Daroussin 
172a9e8641dSBaptiste Daroussin again:
173a9e8641dSBaptiste Daroussin 	switch (ps->state) {
174a9e8641dSBaptiste Daroussin 	case NONE:
175a9e8641dSBaptiste Daroussin 		return (-1);
176a9e8641dSBaptiste Daroussin 
177a9e8641dSBaptiste Daroussin 	case START:
178a9e8641dSBaptiste Daroussin 		/* init our data */
179a9e8641dSBaptiste Daroussin 		bzero(ps, sizeof(*ps));
180a9e8641dSBaptiste Daroussin 
181a9e8641dSBaptiste Daroussin 		/* skip over header name */
182a9e8641dSBaptiste Daroussin 		while (*s != ':')
183a9e8641dSBaptiste Daroussin 			s++;
184a9e8641dSBaptiste Daroussin 		s++;
185a9e8641dSBaptiste Daroussin 		ps->state = MAIN;
186a9e8641dSBaptiste Daroussin 		break;
187a9e8641dSBaptiste Daroussin 
188a9e8641dSBaptiste Daroussin 	case MAIN:
189a9e8641dSBaptiste Daroussin 		/* all fine */
190a9e8641dSBaptiste Daroussin 		break;
191a9e8641dSBaptiste Daroussin 
192a9e8641dSBaptiste Daroussin 	case EOL:
193a9e8641dSBaptiste Daroussin 		switch (*s) {
194a9e8641dSBaptiste Daroussin 		case ' ':
195a9e8641dSBaptiste Daroussin 		case '\t':
196fbe95b88SBaptiste Daroussin 			ps->state = MAIN;
197a9e8641dSBaptiste Daroussin 			break;
198a9e8641dSBaptiste Daroussin 
199a9e8641dSBaptiste Daroussin 		default:
200a9e8641dSBaptiste Daroussin 			ps->state = QUIT;
201a9e8641dSBaptiste Daroussin 			if (ps->pos != 0)
202a9e8641dSBaptiste Daroussin 				goto newaddr;
203a9e8641dSBaptiste Daroussin 			return (0);
204a9e8641dSBaptiste Daroussin 		}
205fbe95b88SBaptiste Daroussin 		break;
206a9e8641dSBaptiste Daroussin 
207a9e8641dSBaptiste Daroussin 	case QUIT:
208a9e8641dSBaptiste Daroussin 		return (0);
209a9e8641dSBaptiste Daroussin 	}
210a9e8641dSBaptiste Daroussin 
211a9e8641dSBaptiste Daroussin 	for (; *s != 0; s++) {
212a9e8641dSBaptiste Daroussin 		if (ps->esc) {
213a9e8641dSBaptiste Daroussin 			ps->esc = 0;
214a9e8641dSBaptiste Daroussin 
215a9e8641dSBaptiste Daroussin 			switch (*s) {
216a9e8641dSBaptiste Daroussin 			case '\r':
217a9e8641dSBaptiste Daroussin 			case '\n':
218a9e8641dSBaptiste Daroussin 				goto err;
219a9e8641dSBaptiste Daroussin 
220a9e8641dSBaptiste Daroussin 			default:
221a9e8641dSBaptiste Daroussin 				goto copy;
222a9e8641dSBaptiste Daroussin 			}
223a9e8641dSBaptiste Daroussin 		}
224a9e8641dSBaptiste Daroussin 
225a9e8641dSBaptiste Daroussin 		if (ps->quote) {
226a9e8641dSBaptiste Daroussin 			switch (*s) {
227a9e8641dSBaptiste Daroussin 			case '"':
228a9e8641dSBaptiste Daroussin 				ps->quote = 0;
229a9e8641dSBaptiste Daroussin 				goto copy;
230a9e8641dSBaptiste Daroussin 
231a9e8641dSBaptiste Daroussin 			case '\\':
232a9e8641dSBaptiste Daroussin 				ps->esc = 1;
233a9e8641dSBaptiste Daroussin 				goto copy;
234a9e8641dSBaptiste Daroussin 
235a9e8641dSBaptiste Daroussin 			case '\r':
236a9e8641dSBaptiste Daroussin 			case '\n':
237a9e8641dSBaptiste Daroussin 				goto eol;
238a9e8641dSBaptiste Daroussin 
239a9e8641dSBaptiste Daroussin 			default:
240a9e8641dSBaptiste Daroussin 				goto copy;
241a9e8641dSBaptiste Daroussin 			}
242a9e8641dSBaptiste Daroussin 		}
243a9e8641dSBaptiste Daroussin 
244a9e8641dSBaptiste Daroussin 		switch (*s) {
245a9e8641dSBaptiste Daroussin 		case '(':
246a9e8641dSBaptiste Daroussin 			ps->comment++;
247a9e8641dSBaptiste Daroussin 			break;
248a9e8641dSBaptiste Daroussin 
249a9e8641dSBaptiste Daroussin 		case ')':
250a9e8641dSBaptiste Daroussin 			if (ps->comment)
251a9e8641dSBaptiste Daroussin 				ps->comment--;
252a9e8641dSBaptiste Daroussin 			else
253a9e8641dSBaptiste Daroussin 				goto err;
254a9e8641dSBaptiste Daroussin 			goto skip;
255a9e8641dSBaptiste Daroussin 
256a9e8641dSBaptiste Daroussin 		case '"':
257a9e8641dSBaptiste Daroussin 			ps->quote = 1;
258a9e8641dSBaptiste Daroussin 			goto copy;
259a9e8641dSBaptiste Daroussin 
260a9e8641dSBaptiste Daroussin 		case '\\':
261a9e8641dSBaptiste Daroussin 			ps->esc = 1;
262a9e8641dSBaptiste Daroussin 			goto copy;
263a9e8641dSBaptiste Daroussin 
264a9e8641dSBaptiste Daroussin 		case '\r':
265a9e8641dSBaptiste Daroussin 		case '\n':
266a9e8641dSBaptiste Daroussin 			goto eol;
267a9e8641dSBaptiste Daroussin 		}
268a9e8641dSBaptiste Daroussin 
269a9e8641dSBaptiste Daroussin 		if (ps->comment)
270a9e8641dSBaptiste Daroussin 			goto skip;
271a9e8641dSBaptiste Daroussin 
272a9e8641dSBaptiste Daroussin 		switch (*s) {
273a9e8641dSBaptiste Daroussin 		case ' ':
274a9e8641dSBaptiste Daroussin 		case '\t':
275a9e8641dSBaptiste Daroussin 			/* ignore whitespace */
276a9e8641dSBaptiste Daroussin 			goto skip;
277a9e8641dSBaptiste Daroussin 
278a9e8641dSBaptiste Daroussin 		case '<':
279a9e8641dSBaptiste Daroussin 			/* this is the real address now */
280a9e8641dSBaptiste Daroussin 			ps->brackets = 1;
281a9e8641dSBaptiste Daroussin 			ps->pos = 0;
282a9e8641dSBaptiste Daroussin 			goto skip;
283a9e8641dSBaptiste Daroussin 
284a9e8641dSBaptiste Daroussin 		case '>':
285a9e8641dSBaptiste Daroussin 			if (!ps->brackets)
286a9e8641dSBaptiste Daroussin 				goto err;
287a9e8641dSBaptiste Daroussin 			ps->brackets = 0;
288a9e8641dSBaptiste Daroussin 
289a9e8641dSBaptiste Daroussin 			s++;
290a9e8641dSBaptiste Daroussin 			goto newaddr;
291a9e8641dSBaptiste Daroussin 
292a9e8641dSBaptiste Daroussin 		case ':':
293a9e8641dSBaptiste Daroussin 			/* group - ignore */
294a9e8641dSBaptiste Daroussin 			ps->pos = 0;
295a9e8641dSBaptiste Daroussin 			goto skip;
296a9e8641dSBaptiste Daroussin 
297a9e8641dSBaptiste Daroussin 		case ',':
298a9e8641dSBaptiste Daroussin 		case ';':
299a9e8641dSBaptiste Daroussin 			/*
300a9e8641dSBaptiste Daroussin 			 * Next address, copy previous one.
301a9e8641dSBaptiste Daroussin 			 * However, we might be directly after
302a9e8641dSBaptiste Daroussin 			 * a <address>, or have two consecutive
303a9e8641dSBaptiste Daroussin 			 * commas.
304a9e8641dSBaptiste Daroussin 			 * Skip the comma unless there is
305a9e8641dSBaptiste Daroussin 			 * really something to copy.
306a9e8641dSBaptiste Daroussin 			 */
307a9e8641dSBaptiste Daroussin 			if (ps->pos == 0)
308a9e8641dSBaptiste Daroussin 				goto skip;
309a9e8641dSBaptiste Daroussin 			s++;
310a9e8641dSBaptiste Daroussin 			goto newaddr;
311a9e8641dSBaptiste Daroussin 
312a9e8641dSBaptiste Daroussin 		default:
313a9e8641dSBaptiste Daroussin 			goto copy;
314a9e8641dSBaptiste Daroussin 		}
315a9e8641dSBaptiste Daroussin 
316a9e8641dSBaptiste Daroussin copy:
317a9e8641dSBaptiste Daroussin 		if (ps->comment)
318a9e8641dSBaptiste Daroussin 			goto skip;
319a9e8641dSBaptiste Daroussin 
320a9e8641dSBaptiste Daroussin 		if (ps->pos + 1 == sizeof(ps->addr))
321a9e8641dSBaptiste Daroussin 			goto err;
322a9e8641dSBaptiste Daroussin 		ps->addr[ps->pos++] = *s;
323a9e8641dSBaptiste Daroussin 
324a9e8641dSBaptiste Daroussin skip:
325a9e8641dSBaptiste Daroussin 		;
326a9e8641dSBaptiste Daroussin 	}
327a9e8641dSBaptiste Daroussin 
328a9e8641dSBaptiste Daroussin eol:
329a9e8641dSBaptiste Daroussin 	ps->state = EOL;
330a9e8641dSBaptiste Daroussin 	return (0);
331a9e8641dSBaptiste Daroussin 
332a9e8641dSBaptiste Daroussin err:
333a9e8641dSBaptiste Daroussin 	ps->state = QUIT;
334a9e8641dSBaptiste Daroussin 	return (-1);
335a9e8641dSBaptiste Daroussin 
336a9e8641dSBaptiste Daroussin newaddr:
337a9e8641dSBaptiste Daroussin 	ps->addr[ps->pos] = 0;
338a9e8641dSBaptiste Daroussin 	ps->pos = 0;
339a9e8641dSBaptiste Daroussin 	addr = strdup(ps->addr);
340a9e8641dSBaptiste Daroussin 	if (addr == NULL)
341eaccd9b3SBaptiste Daroussin 		errlog(EX_SOFTWARE, "strdup");
342a9e8641dSBaptiste Daroussin 
343a9e8641dSBaptiste Daroussin 	if (add_recp(queue, addr, EXPAND_WILDCARD) != 0)
344e56bad4aSBaptiste Daroussin 		errlogx(EX_DATAERR, "invalid recipient `%s'", addr);
345a9e8641dSBaptiste Daroussin 
346a9e8641dSBaptiste Daroussin 	goto again;
347a9e8641dSBaptiste Daroussin }
348a9e8641dSBaptiste Daroussin 
349b0b2d05fSBaptiste Daroussin static int
writeline(struct queue * queue,const char * line,ssize_t linelen)350b0b2d05fSBaptiste Daroussin writeline(struct queue *queue, const char *line, ssize_t linelen)
351b0b2d05fSBaptiste Daroussin {
352b0b2d05fSBaptiste Daroussin 	ssize_t len;
353b0b2d05fSBaptiste Daroussin 
354b0b2d05fSBaptiste Daroussin 	while (linelen > 0) {
355b0b2d05fSBaptiste Daroussin 		len = linelen;
356b0b2d05fSBaptiste Daroussin 		if (linelen > MAX_LINE_RFC822) {
357b0b2d05fSBaptiste Daroussin 			len = MAX_LINE_RFC822 - 10;
358b0b2d05fSBaptiste Daroussin 		}
359b0b2d05fSBaptiste Daroussin 
360b0b2d05fSBaptiste Daroussin 		if (fwrite(line, len, 1, queue->mailf) != 1)
361b0b2d05fSBaptiste Daroussin 			return (-1);
362b0b2d05fSBaptiste Daroussin 
363b0b2d05fSBaptiste Daroussin 		if (linelen <= MAX_LINE_RFC822)
364b0b2d05fSBaptiste Daroussin 			break;
365b0b2d05fSBaptiste Daroussin 
366b0b2d05fSBaptiste Daroussin 		if (fwrite("\n", 1, 1, queue->mailf) != 1)
367b0b2d05fSBaptiste Daroussin 			return (-1);
368b0b2d05fSBaptiste Daroussin 
369b0b2d05fSBaptiste Daroussin 		line += MAX_LINE_RFC822 - 10;
370b0b2d05fSBaptiste Daroussin 		linelen = strlen(line);
371b0b2d05fSBaptiste Daroussin 	}
372b0b2d05fSBaptiste Daroussin 	return (0);
373b0b2d05fSBaptiste Daroussin }
374b0b2d05fSBaptiste Daroussin 
375a9e8641dSBaptiste Daroussin int
readmail(struct queue * queue,int nodot,int recp_from_header)376a9e8641dSBaptiste Daroussin readmail(struct queue *queue, int nodot, int recp_from_header)
377a9e8641dSBaptiste Daroussin {
378a9e8641dSBaptiste Daroussin 	struct parse_state parse_state;
379b0b2d05fSBaptiste Daroussin 	char *line = NULL;
380b0b2d05fSBaptiste Daroussin 	ssize_t linelen;
381b0b2d05fSBaptiste Daroussin 	size_t linecap = 0;
3821a0dde33SEd Maste 	char newline[MAX_LINE_RFC822 + 1];
383a9e8641dSBaptiste Daroussin 	size_t error;
384a9e8641dSBaptiste Daroussin 	int had_headers = 0;
385a9e8641dSBaptiste Daroussin 	int had_from = 0;
386a9e8641dSBaptiste Daroussin 	int had_messagid = 0;
387a9e8641dSBaptiste Daroussin 	int had_date = 0;
388fbe95b88SBaptiste Daroussin 	int had_first_line = 0;
389fbe95b88SBaptiste Daroussin 	int had_last_line = 0;
390a9e8641dSBaptiste Daroussin 	int nocopy = 0;
391b0b2d05fSBaptiste Daroussin 	int ret = -1;
392a9e8641dSBaptiste Daroussin 
393a9e8641dSBaptiste Daroussin 	parse_state.state = NONE;
394a9e8641dSBaptiste Daroussin 
395a9e8641dSBaptiste Daroussin 	error = fprintf(queue->mailf,
396a9e8641dSBaptiste Daroussin 		"Received: from %s (uid %d)\n"
397a9e8641dSBaptiste Daroussin 		"\t(envelope-from %s)\n"
398a9e8641dSBaptiste Daroussin 		"\tid %s\n"
399fbe95b88SBaptiste Daroussin 		"\tby %s (%s on %s);\n"
400a9e8641dSBaptiste Daroussin 		"\t%s\n",
401a9e8641dSBaptiste Daroussin 		username, useruid,
402a9e8641dSBaptiste Daroussin 		queue->sender,
403a9e8641dSBaptiste Daroussin 		queue->id,
404fbe95b88SBaptiste Daroussin 		hostname(), VERSION, systemhostname(),
405a9e8641dSBaptiste Daroussin 		rfc822date());
406a9e8641dSBaptiste Daroussin 	if ((ssize_t)error < 0)
407a9e8641dSBaptiste Daroussin 		return (-1);
408a9e8641dSBaptiste Daroussin 
409d21e71efSEd Maste 	while ((linelen = getline(&line, &linecap, stdin)) > 0) {
410b0b2d05fSBaptiste Daroussin 		newline[0] = '\0';
411fbe95b88SBaptiste Daroussin 		if (had_last_line)
412fbe95b88SBaptiste Daroussin 			errlogx(EX_DATAERR, "bad mail input format:"
413fbe95b88SBaptiste Daroussin 				" from %s (uid %d) (envelope-from %s)",
414fbe95b88SBaptiste Daroussin 				username, useruid, queue->sender);
415fbe95b88SBaptiste Daroussin 		linelen = strlen(line);
416fbe95b88SBaptiste Daroussin 		if (linelen == 0 || line[linelen - 1] != '\n') {
417fbe95b88SBaptiste Daroussin 			/*
418fbe95b88SBaptiste Daroussin 			 * This line did not end with a newline character.
419fbe95b88SBaptiste Daroussin 			 * If we fix it, it better be the last line of
420fbe95b88SBaptiste Daroussin 			 * the file.
421fbe95b88SBaptiste Daroussin 			 */
422*16948722SEd Maste 			if ((size_t)linelen + 1 > linecap) {
423*16948722SEd Maste 				line = realloc(line, linelen + 2);
424*16948722SEd Maste 				if (line == NULL)
425*16948722SEd Maste 					errlogx(EX_SOFTWARE, "realloc");
426*16948722SEd Maste 				linecap = malloc_usable_size(line);
427*16948722SEd Maste 			}
428*16948722SEd Maste 			line[linelen++] = '\n';
429*16948722SEd Maste 			line[linelen] = 0;
430fbe95b88SBaptiste Daroussin 			had_last_line = 1;
431fbe95b88SBaptiste Daroussin 		}
432fbe95b88SBaptiste Daroussin 		if (!had_first_line) {
433fbe95b88SBaptiste Daroussin 			/*
434fbe95b88SBaptiste Daroussin 			 * Ignore a leading RFC-976 From_ or >From_ line mistakenly
435fbe95b88SBaptiste Daroussin 			 * inserted by some programs.
436fbe95b88SBaptiste Daroussin 			 */
437fbe95b88SBaptiste Daroussin 			if (strprefixcmp(line, "From ") == 0 || strprefixcmp(line, ">From ") == 0)
438fbe95b88SBaptiste Daroussin 				continue;
439fbe95b88SBaptiste Daroussin 			had_first_line = 1;
440fbe95b88SBaptiste Daroussin 		}
441b0b2d05fSBaptiste Daroussin 		if (!had_headers) {
442b0b2d05fSBaptiste Daroussin 			if (linelen > MAX_LINE_RFC822) {
443b0b2d05fSBaptiste Daroussin 				/* XXX also split headers */
444e56bad4aSBaptiste Daroussin 				errlogx(EX_DATAERR, "bad mail input format:"
445e56bad4aSBaptiste Daroussin 				    " from %s (uid %d) (envelope-from %s)",
446e56bad4aSBaptiste Daroussin 				    username, useruid, queue->sender);
447b4b4b530SBaptiste Daroussin 			}
448a9e8641dSBaptiste Daroussin 			/*
449a9e8641dSBaptiste Daroussin 			 * Unless this is a continuation, switch of
450a9e8641dSBaptiste Daroussin 			 * the Bcc: nocopy flag.
451a9e8641dSBaptiste Daroussin 			 */
452a9e8641dSBaptiste Daroussin 			if (!(line[0] == ' ' || line[0] == '\t'))
453a9e8641dSBaptiste Daroussin 				nocopy = 0;
454a9e8641dSBaptiste Daroussin 
455a9e8641dSBaptiste Daroussin 			if (strprefixcmp(line, "Date:") == 0)
456a9e8641dSBaptiste Daroussin 				had_date = 1;
457a9e8641dSBaptiste Daroussin 			else if (strprefixcmp(line, "Message-Id:") == 0)
458a9e8641dSBaptiste Daroussin 				had_messagid = 1;
459a9e8641dSBaptiste Daroussin 			else if (strprefixcmp(line, "From:") == 0)
460a9e8641dSBaptiste Daroussin 				had_from = 1;
461a9e8641dSBaptiste Daroussin 			else if (strprefixcmp(line, "Bcc:") == 0)
462a9e8641dSBaptiste Daroussin 				nocopy = 1;
463a9e8641dSBaptiste Daroussin 
464a9e8641dSBaptiste Daroussin 			if (parse_state.state != NONE) {
465a9e8641dSBaptiste Daroussin 				if (parse_addrs(&parse_state, line, queue) < 0) {
466e56bad4aSBaptiste Daroussin 					errlogx(EX_DATAERR, "invalid address in header\n");
467a9e8641dSBaptiste Daroussin 					/* NOTREACHED */
468a9e8641dSBaptiste Daroussin 				}
469a9e8641dSBaptiste Daroussin 			}
470a9e8641dSBaptiste Daroussin 
471a9e8641dSBaptiste Daroussin 			if (recp_from_header && (
472a9e8641dSBaptiste Daroussin 					strprefixcmp(line, "To:") == 0 ||
473a9e8641dSBaptiste Daroussin 					strprefixcmp(line, "Cc:") == 0 ||
474a9e8641dSBaptiste Daroussin 					strprefixcmp(line, "Bcc:") == 0)) {
475a9e8641dSBaptiste Daroussin 				parse_state.state = START;
476a9e8641dSBaptiste Daroussin 				if (parse_addrs(&parse_state, line, queue) < 0) {
477e56bad4aSBaptiste Daroussin 					errlogx(EX_DATAERR, "invalid address in header\n");
478a9e8641dSBaptiste Daroussin 					/* NOTREACHED */
479a9e8641dSBaptiste Daroussin 				}
480a9e8641dSBaptiste Daroussin 			}
481a9e8641dSBaptiste Daroussin 		}
482a9e8641dSBaptiste Daroussin 
483b4b4b530SBaptiste Daroussin 		if (strcmp(line, "\n") == 0 && !had_headers) {
484a9e8641dSBaptiste Daroussin 			had_headers = 1;
485a9e8641dSBaptiste Daroussin 			while (!had_date || !had_messagid || !had_from) {
486a9e8641dSBaptiste Daroussin 				if (!had_date) {
487a9e8641dSBaptiste Daroussin 					had_date = 1;
488b0b2d05fSBaptiste Daroussin 					snprintf(newline, sizeof(newline), "Date: %s\n", rfc822date());
489a9e8641dSBaptiste Daroussin 				} else if (!had_messagid) {
490a9e8641dSBaptiste Daroussin 					/* XXX msgid, assign earlier and log? */
491a9e8641dSBaptiste Daroussin 					had_messagid = 1;
492b0b2d05fSBaptiste Daroussin 					snprintf(newline, sizeof(newline), "Message-Id: <%"PRIxMAX".%s.%"PRIxMAX"@%s>\n",
493a9e8641dSBaptiste Daroussin 						 (uintmax_t)time(NULL),
494a9e8641dSBaptiste Daroussin 						 queue->id,
495a9e8641dSBaptiste Daroussin 						 (uintmax_t)random(),
496a9e8641dSBaptiste Daroussin 						 hostname());
497a9e8641dSBaptiste Daroussin 				} else if (!had_from) {
498a9e8641dSBaptiste Daroussin 					had_from = 1;
499b0b2d05fSBaptiste Daroussin 					snprintf(newline, sizeof(newline), "From: <%s>\n", queue->sender);
500a9e8641dSBaptiste Daroussin 				}
501b0b2d05fSBaptiste Daroussin 				if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
502b0b2d05fSBaptiste Daroussin 					goto fail;
503a9e8641dSBaptiste Daroussin 			}
504b0b2d05fSBaptiste Daroussin 			strlcpy(newline, "\n", sizeof(newline));
505a9e8641dSBaptiste Daroussin 		}
506a9e8641dSBaptiste Daroussin 		if (!nodot && linelen == 2 && line[0] == '.')
507a9e8641dSBaptiste Daroussin 			break;
508a9e8641dSBaptiste Daroussin 		if (!nocopy) {
509b0b2d05fSBaptiste Daroussin 			if (newline[0]) {
510b0b2d05fSBaptiste Daroussin 				if (fwrite(newline, strlen(newline), 1, queue->mailf) != 1)
511b0b2d05fSBaptiste Daroussin 					goto fail;
512b0b2d05fSBaptiste Daroussin 			} else {
513b0b2d05fSBaptiste Daroussin 				if (writeline(queue, line, linelen) != 0)
514b0b2d05fSBaptiste Daroussin 					goto fail;
515b0b2d05fSBaptiste Daroussin 			}
516a9e8641dSBaptiste Daroussin 		}
517a9e8641dSBaptiste Daroussin 	}
518d21e71efSEd Maste 	if (ferror(stdin) == 0)
519b0b2d05fSBaptiste Daroussin 		ret = 0;
520b0b2d05fSBaptiste Daroussin fail:
521b0b2d05fSBaptiste Daroussin 	free(line);
522b0b2d05fSBaptiste Daroussin 	return (ret);
523a9e8641dSBaptiste Daroussin }
524