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