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