/* $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 <ctype.h>
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);
}
syntax highlighted by Code2HTML, v. 0.9.1