/* $Id: copymsg.c,v 1.9 2004/12/24 19:05:59 dm Exp $ */ /* * * Copyright (C) 2004 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "local.h" #include enum { maxbuf = 1024 * 1024 }; const char *opt_tmplate = "/tmp/msg.XXXXXXXXXX"; const char *opt_from; const char *opt_recip; char *msg_ufline; char *msg_rpline; char *msg_dtline; static const char delto[] = "Delivered-To: "; void initfile (char **path, int *wfd, int *rfd, struct stat *sbp) { mode_t m = umask (077); *path = xmalloc (strlen (opt_tmplate) + 1); for (;;) { strcpy (*path, opt_tmplate); *wfd = mkstemp (*path); if (*wfd < 0) { perror (*path); exit (EX_OSERR); } *rfd = open (*path, O_RDONLY); if (*rfd >= 0) { struct stat sb1, sb2; if (fstat (*wfd, &sb1) || fstat (*rfd, &sb2)) { perror (*path); exit (EX_OSERR); } if (sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino) { if (sbp) *sbp = sb1; umask (m); return; } } fprintf (stderr, "%s: file %s changed while opening\n", progname, *path); close (*rfd); close (*wfd); } } void set_msgvars (struct lnbuf *bufp, FILE *in) { time_t tm = time (NULL); char *date = ctime (&tm); const char *from; int size, res; if (bufp) { if (readln (bufp, in, maxbuf) != LNBUF_OK) exit (EX_DATAERR); if (!strncmp (bufp->buf, "From ", 5)) { if (!opt_from) { char *f1 = bufp->buf + 5, *f2; for (f2 = f1; *f2 && !isspace (*f2); f2++) ; if (f2 > f1) { *f2 = '\0'; opt_from = strcpy (xmalloc (f2 - f1 + 1), f1); } } bufp->size = 0; if (readln (bufp, in, maxbuf) != LNBUF_OK) exit (EX_DATAERR); } if (!strncasecmp (bufp->buf, "Return-Path:", 12)) { if (!opt_from) { char *f1 = bufp->buf + 12, *f2 = bufp->buf + bufp->size; while (isspace (*f1)) f1++; if (*f1++ == '<') { while (f2 > f1 && *--f2 != '>') ; if (f2 > f1) { *f2 = '\0'; opt_from = strcpy (xmalloc (f2 - f1 + 1), f1); bufp->size = 0; } } } else bufp->size = 0; } } from = opt_from; size = strlen (date); if (size > 0 && date[size - 1] == '\n') date[size - 1] = '\0'; if (!from || !*from || !strcmp (opt_from, "@")) from = "MAILER-DAEMON"; size = 8 + strlen (from) + strlen (date); msg_ufline = xmalloc (size); res = sprintf (msg_ufline, "From %s %s", from, date); assert (res + 1 == size); if (!strcmp (from, "MAILER-DAEMON")) from = ""; msg_rpline = xmalloc (sizeof ("Return-Path: <>") + strlen (from)); sprintf (msg_rpline, "Return-Path: <%s>", from); if (opt_recip) { msg_dtline = xmalloc (sizeof (delto) + strlen (opt_recip)); sprintf (msg_dtline, "%s%s", delto, opt_recip); } } int nocopymsg (FILE *in) { struct lnbuf buf; long skip; off_t pos = lseek (fileno (in), 0, SEEK_CUR); if (pos == -1) return -1; bzero (&buf, sizeof (buf)); set_msgvars (&buf, in); skip = ftell (in); if (skip < 0) { perror ("ftell"); exit (EX_OSERR); } assert (buf.size <= (unsigned long) skip); if (lseek (fileno (in), skip - buf.size, SEEK_SET) == -1) { perror ("re-lseek"); exit (EX_OSERR); } free (buf.buf); return 0; } void copymsg (int outfd, FILE *in) { struct lnbuf buf; FILE *out = fdopen (dup (outfd), "w"); int in_headers = 1, rl_res = 1, last_size = 1; if (!out) { perror ("fdopen"); exit (EX_OSERR); } bzero (&buf, sizeof (buf)); set_msgvars (&buf, in); if (msg_dtline && fprintf (out, "%s\n", msg_dtline) < 0) { perror ("write"); exit (EX_OSERR); } while (rl_res) { #if 0 if (rl_res != LNBUF_TOOBIG && buf.size >= 5 && !strncmp (buf.buf, "From ", 5)) fprintf (out, ">"); #endif if (buf.size && fwrite (buf.buf, 1, buf.size, out) != buf.size) { perror ("write"); exit (EX_OSERR); } switch (rl_res) { case LNBUF_EOFNL: fprintf (out, "\n"); break; case LNBUF_NOMEM: fprintf (stderr, "out of memory reading header line\n"); exit (EX_OSERR); break; case LNBUF_TOOBIG: if (in_headers) { fprintf (stderr, "line too long in header\n"); exit (EX_DATAERR); } break; case LNBUF_IOERR: fprintf (stderr, "Error reading message\n"); exit (EX_OSERR); break; } if (in_headers) { if (buf.size == 1) in_headers = 0; else if (opt_recip && !strncasecmp (delto, buf.buf, sizeof (delto) - 1)) { char *ap = buf.buf + sizeof (delto) - 1; char *addr = strnnsep (&ap, " \t\r\n"); if (addr && !strcmp (addr, opt_recip)) { fprintf (stderr, "message already delivered to %s; possible loop\n", opt_recip); exit (EX_SOFTWARE); } } } last_size = buf.size; rl_res = readln (&buf, in, maxbuf); } #if 0 if (last_size != 1) fprintf (out, "\n"); #endif fclose (out); free (buf.buf); }