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