/*
* Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
* Copyright 1994-2000,2002-2003 by Matti Aarnio -- MIME processings,
* etc.
*/
#define DefCharset "ISO-8859-1"
#include "hostenv.h"
#include <ctype.h>
#include <pwd.h>
#include "zmsignal.h"
#include <sysexits.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include "ta.h"
#include "mail.h"
#include "zmalloc.h"
#include "zsyslog.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include "libz.h"
#include "libc.h"
#include "shmmib.h"
#ifdef HAVE_SYS_WAIT_H /* POSIX.1 compatible */
# include <sys/wait.h>
#else /* Not POSIX.1 compatible, lets fake it.. */
extern int wait();
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(s) (((s) >> 8) & 0377)
#endif
#ifndef WSIGNALSTATUS
# define WSIGNALSTATUS(s) ((s) & 0177)
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif /* !SEEK_SET */
/* as in: SKIPWHILE(isascii,cp) */
#define SKIPSPACE(Y) while (*Y == ' ' || *Y == '\t' || *Y == '\n') ++Y
#define SKIPTEXT(Y) while (*Y && *Y != ' ' && *Y != '\t' && *Y != '\n') ++Y
#define FROM_ "From "
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif /* MAXHOSTNAMELEN */
char uucpname[MAXHOSTNAMELEN+1];
const char *progname;
int readalready = 0; /* does buffer contain valid message data? */
int mimeqpnarrow = 0; /* Can't send TAB thru without MIME-QP */
FILE *logfp = NULL;
int maxwidth = 0;
int can_8bit = 0; /* Can do 8-bit stuff! */
int decode_qp = 0;
int keep_header8 = 0; /* Don't do "MIME-2" to the headers */
int D_alloc = 0; /* Memory debugging */
const char *defcharset;
extern RETSIGTYPE sigpipe();
int gotsigpipe = 0;
RETSIGTYPE
sigpipe(sig)
int sig;
{
gotsigpipe = 1;
SIGNAL_HANDLE(SIGPIPE, sigpipe);
}
struct maildesc {
char *name;
long flags;
char *command;
#define MD_ARGVMAX 20
char *argv[MD_ARGVMAX];
};
extern RETSIGTYPE wantout();
#ifndef MALLOC_TRACE
extern univptr_t emalloc();
extern univptr_t erealloc();
#endif
extern char *optarg;
extern int optind;
extern int getmyuucpname();
extern struct maildesc *readsmcf __((char *file, char *mailer));
extern void prversion();
extern void process __((struct ctldesc *dp, struct maildesc *mp, FILE *verboselog));
extern void deliver __((struct ctldesc *dp, struct maildesc *mp, struct rcpt *startrp, struct rcpt *endrp, FILE *verboselog));
extern int appendlet __((struct ctldesc *dp, struct maildesc *mp, FILE *fp, FILE *verboselog, int convertmode));
extern int writebuf __((struct maildesc *mp, FILE *fp, const char *buf, int len));
extern time_t time();
#ifndef strchr
extern char *strchr(), *strrchr();
#endif
static int zsfsetfd(fp, fd)
Sfio_t *fp;
int fd;
{
/* This is *NOT* the SFIO's sfsetfd() -- we do no sfsync() at any point.. */
fp->file = fd;
return fd;
}
static void
decodeXtext(mfp,xtext)
FILE *mfp;
const char *xtext;
{
for (;*xtext;++xtext) {
if (*xtext == '+') {
int c = '?';
sscanf(xtext+1,"%02X",&c);
fputc(c, mfp);
if (*xtext) ++xtext;
if (*xtext) ++xtext;
} else
fputc(*xtext, mfp);
}
}
static void MIBcountCleanup __((void))
{
MIBMtaEntry->tasmcm.TaProcCountG -= 1;
}
static void SM_MIB_diag(rc)
const int rc;
{
switch (rc) {
case EX_OK:
/* OK */
MIBMtaEntry->tasmcm.TaRcptsOk ++;
break;
case EX_TEMPFAIL:
case EX_IOERR:
case EX_OSERR:
case EX_CANTCREAT:
case EX_SOFTWARE:
case EX_DEFERALL:
/* DEFER */
MIBMtaEntry->tasmcm.TaRcptsRetry ++;
break;
case EX_NOPERM:
case EX_PROTOCOL:
case EX_USAGE:
case EX_NOUSER:
case EX_NOHOST:
case EX_UNAVAILABLE:
default:
/* FAIL */
MIBMtaEntry->tasmcm.TaRcptsFail ++;
break;
}
}
extern int check_7bit_cleanness __((struct ctldesc *dp));
extern int writemimeline __(( struct maildesc *mp, FILE *fp, const char *buf, int len, int convertmode));
#define MO_FFROMFLAG 0x00001
#define MO_RFROMFLAG 0x00002
#define MO_NORESETUID 0x00004
#define MO_STRIPQUOTES 0x00008
#define MO_MANYUSERS 0x00010
#define MO_RETURNPATH 0x00020
#define MO_UNIXFROM 0x00040
#define MO_HIDDENDOT 0x00080 /* SMTP dot-duplication */
#define MO_ESCAPEFROM 0x00100
#define MO_STRIPHIBIT 0x00200
#define MO_REMOTEFROM 0x00400
#define MO_CRLF 0x00800
#define MO_BSMTP 0x01000 /* BSMTP-wrapping -- with HIDDENDOT.. */
#define MO_BESMTP 0x02000 /* Extended BSMTP -- SIZE+8BITMIME */
#define MO_BEDSMTP 0x04000 /* EBSMTP + DSN */
#define MO_BEBSMTP 0x04000 /* ESMTP + DELIVERBY */
#define MO_WANTSDATE 0x08000 /* Wants "Date:" -header */
#define MO_WANTSFROM 0x10000 /* Wants "From:" -header */
#define MO_BSMTPHELO 0x20000 /* Add HELO/EHLO to the BSMTP */
#define MO_XENVELOPES 0x40000 /* Write various X-Envelope-*: headers to mesage */
#define MO_XORCPT 0x80000 /* Write X-Orcpt : header to message */
struct exmapinfo {
int origstatus;
const char *statusmsg;
int newstatus;
const char *dsnstatus;
const char *dsndiags;
};
struct exmapinfo exmap[] = {
{ EX_USAGE, "command line usage error", EX_TEMPFAIL, "5.3.0", "x-local; 500 (Command line usage error)" },
{ EX_DATAERR, "data format error", EX_DATAERR, "5.3.0", "x-local; 500 (Data format error)" },
{ EX_NOINPUT, "cannot open input", EX_TEMPFAIL, "5.3.0", "x-local; 530 (Cannot open input)" },
{ EX_NOUSER, "addressee unknown", EX_NOUSER, "5.1.1", "x-local; 521 (No such target user)" },
{ EX_NOHOST, "host name unknown", EX_NOHOST, "5.3.0", "x-local; 500 (Target host unknown)" },
{ EX_UNAVAILABLE, "service unavailable", EX_UNAVAILABLE, "5.3.0", "x-local; 500 (Service unavailable)" },
{ EX_SOFTWARE, "internal software error", EX_TEMPFAIL, "5.3.0", "x-local; 500 (Internal software error)" },
{ EX_OSERR, "system error", EX_TEMPFAIL, "5.3.0", "x-local; 500 (System error)" },
{ EX_OSFILE, "critical OS file missing", EX_TEMPFAIL, "5.3.0", "x-local; 500 (Critical OS file missing)" },
{ EX_CANTCREAT, "can't create output file", EX_TEMPFAIL, "5.2.1", "x-local; 500 (Can't create output file)" },
{ EX_IOERR, "input/output error", EX_TEMPFAIL, "5.2.2", "x-local; 500 (Input/Output error)" },
{ EX_TEMPFAIL, "temporary failure", EX_TEMPFAIL, "5.3.0", "x-local; 500 (Temporary failure)" },
{ EX_PROTOCOL, "remote error in protocol", EX_TEMPFAIL, "5.3.0", "x-local; 500 (Remote error in protocol)" },
{ EX_NOPERM, "permission denied", EX_NOPERM, "5.2.0", "x-local; 520 (Permission denied)" },
{ 0, NULL, EX_TEMPFAIL, NULL, NULL }
};
char myhostname[MAXHOSTNAMELEN+1];
#ifdef lint
#undef putc
#define putc fputc
#endif /* lint */
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif /* MAXPATHLEN */
int
main(argc, argv)
int argc;
char *argv[];
{
char file[MAXPATHLEN+1];
char *channel, *host = NULL, *mailer, *cf;
struct ctldesc *dp;
int errflg, c;
struct maildesc *mp;
RETSIGTYPE (*oldsig)();
FILE *verboselog = NULL;
SIGNAL_HANDLESAVE(SIGINT, SIG_IGN, oldsig);
if (oldsig != SIG_IGN)
SIGNAL_HANDLE(SIGINT, wantout);
SIGNAL_HANDLESAVE(SIGTERM, SIG_IGN, oldsig);
if (oldsig != SIG_IGN)
SIGNAL_HANDLE(SIGTERM, wantout);
SIGNAL_HANDLESAVE(SIGQUIT, SIG_IGN, oldsig);
if (oldsig != SIG_IGN)
SIGNAL_HANDLE(SIGQUIT, wantout);
SIGNAL_HANDLESAVE(SIGHUP, SIG_IGN, oldsig);
if (oldsig != SIG_IGN)
SIGNAL_HANDLE(SIGHUP, wantout);
SIGNAL_HANDLE(SIGPIPE, sigpipe);
#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) && defined(LC_ALL)
setlocale(LC_ALL, "C");
#endif
if (getenv("ZCONFIG")) readzenv(getenv("ZCONFIG"));
Z_SHM_MIB_Attach(1); /* we don't care if it succeeds or fails.. */
MIBMtaEntry->tasmcm.TaProcessStarts += 1;
MIBMtaEntry->tasmcm.TaProcCountG += 1;
atexit(MIBcountCleanup);
if ((progname = strrchr(argv[0], '/')) == NULL)
progname = argv[0];
else
++progname;
errflg = 0;
host = channel = NULL;
myhostname[0] = 0;
cf = NULL;
while (1) {
c = getopt(argc, argv, "f:c:h:HQvVw:8");
if (c == EOF)
break;
switch (c) {
case 'f':
cf = optarg;
break;
case 'c': /* remote hostname */
channel = optarg;
break;
case 'h': /* remote hostname */
host = strdup(optarg);
break;
case 'Q':
mimeqpnarrow = 1;
break;
case 'V':
prversion("sm");
exit(0);
break;
case 'v':
verboselog = stdout;
break;
case '8':
can_8bit = decode_qp = 1;
break;
case 'H':
keep_header8 = 1;
break;
case 'w':
maxwidth = atoi(optarg);
if (maxwidth < 0)
maxwidth = 0;
default:
++errflg;
break;
}
}
if (errflg || optind != argc - 1 || host == channel) {
fprintf(stderr,
"Usage: %s [-V][-v][-H][-8 | -Q][-f cfgfile][-w maxwidth][-c channel | -h host] mailer\n", argv[0]);
exit(EX_USAGE);
}
mailer = argv[optind];
/* We need this later on .. */
zopenlog("sm", LOG_PID, LOG_MAIL);
defcharset = getzenv("DEFCHARSET");
if (!defcharset)
defcharset = DefCharset;
if ((mp = readsmcf(cf, mailer)) == NULL)
exit(EX_OSFILE);
if (mp->flags & MO_REMOTEFROM)
getmyuucpname(uucpname, sizeof uucpname); /*XX*/
while (!getout) {
char *s;
/* Input:
spool/file/name [ \t host.info ] \n
*/
printf("#hungry\n");
fflush(stdout);
if (fgets(file, sizeof file, stdin) == NULL)
break;
if (strchr(file, '\n') == NULL) break; /* No ending '\n' ! Must
have been partial input! */
if (strcmp(file, "#idle\n") == 0) {
MIBMtaEntry->tasmcm.TaIdleStates += 1;
continue; /* Ah well, we can stay idle.. */
}
if (emptyline(file, sizeof file))
break;
MIBMtaEntry->tasmcm.TaMessages += 1;
s = strchr(file,'\t');
if (s != NULL) {
if (host) free(host);
host = strdup(s+1);
*s = 0;
}
ctlsticky(NULL, NULL, NULL); /* reset */
dp = ctlopen(file, channel, host, &getout, ctlsticky, NULL);
if (dp == NULL) {
printf("#resync %s\n",file);
fflush(stdout);
continue;
}
if (verboselog != stdout && verboselog != NULL) {
fclose(verboselog);
verboselog = NULL;
}
if (verboselog != stdout && dp->verbose) {
verboselog = fopen(dp->verbose,"a");
if (verboselog) setbuf(verboselog,NULL);
}
process(dp, mp, verboselog);
ctlclose(dp);
}
if (verboselog != NULL)
fclose(verboselog);
if (logfp != NULL)
fclose(logfp);
exit(EX_OK);
/* NOTREACHED */
return 0;
}
void
process(dp, mp, verboselog)
struct ctldesc *dp;
struct maildesc *mp;
FILE *verboselog;
{
struct rcpt *rp, *rphead;
readalready = 0; /* ignore any previous message data cache */
if (mp->flags & MO_MANYUSERS) {
for (rp = rphead = dp->recipients; rp != NULL; rp = rp->next) {
if (rp->next == NULL
|| rp->addr->link != rp->next->addr->link
|| rp->newmsgheader != rp->next->newmsgheader) {
deliver(dp, mp, rphead, rp->next, verboselog);
rphead = rp->next;
}
}
} else {
for (rp = dp->recipients; rp != NULL; rp = rp->next) {
deliver(dp, mp, rp, rp->next, verboselog);
}
}
}
/*
* deliver - deliver the letter in to user's mail box. Return
* errors and requests for further processing in the structure
*/
void
deliver(dp, mp, startrp, endrp, verboselog)
struct ctldesc *dp;
struct maildesc *mp;
struct rcpt *startrp, *endrp;
FILE *verboselog;
{
struct rcpt *rp = NULL;
struct exmapinfo *exp;
const char *exs, *exd;
int i, j, pid = 0, in[2], out[2], ii = 0;
unsigned int avsize;
FILE *tafp = NULL, *errfp = NULL;
char *cp = NULL, buf[BUFSIZ], buf2[BUFSIZ];
char *ws = NULL, *we = NULL;
const char *ds, **av, *s;
int status;
int content_kind, conversion_prohibited, ascii_clean = 0;
time_t now;
char *timestring;
CONVERTMODE convertmode = _CONVERT_NONE;
char *lineendseq = "\n";
MIBMtaEntry->tasmcm.TaDeliveryStarts += 1;
now = time((time_t *)0);
timestring = ctime(&now);
*(timestring+strlen(timestring)-1) = '\0';
if (lseek(dp->msgfd, (off_t)(dp->msgbodyoffset), SEEK_SET) < 0L)
warning("Cannot seek to message body! (%m)", (char *)NULL);
i = 0;
avsize = 5;
av = (const char **)emalloc(sizeof av[0] * avsize);
av[i++] = mp->argv[0];
if (mp->flags & MO_FFROMFLAG) {
av[i++] = "-f";
if (strcmp(startrp->addr->link->channel,"error")==0)
av[i++] = "<>";
else
av[i++] = startrp->addr->link->user;
} else if (mp->flags & MO_RFROMFLAG) {
av[i++] = "-r";
if (strcmp(startrp->addr->link->channel,"error")==0)
av[i++] = "<>";
else
av[i++] = startrp->addr->link->user;
}
for (j = 0; mp->argv[j] != NULL; ++j) {
while (i+2 >= avsize) {
avsize *= 2;
av = (const char **)erealloc((char *)av,
sizeof av[0] * avsize);
}
if (strchr(mp->argv[j], '$') == NULL) {
if (j > 0)
av[i++] = mp->argv[j];
continue;
}
rp = startrp;
do {
/* Even argv[0] MAY have $-expansions.. */
ii = i; if (j == 0) ii = 0;
while (i+2 >= avsize) {
avsize <<= 1;
av = (const char **)erealloc((char *)av,
sizeof av[0] * avsize);
}
ws = buf;
we = buf + sizeof(buf);
for (cp = mp->argv[j]; *cp != '\0'; ++cp) {
if (*cp == '$') {
switch (*++cp) {
case 'g':
ds = rp->addr->link->user;
break;
case 'h':
ds = rp->addr->host;
break;
case 'u':
ds = rp->addr->user;
rp = rp->next;
break;
case 'U':
strncpy(buf2, rp->addr->user, sizeof(buf2));
buf2[sizeof(buf2)-1] = 0;
strlower(buf2);
ds = buf2;
rp = rp->next;
break;
case '{':
s = ++cp;
while (*cp != 0 && *cp != '}') ++cp;
if (*cp != 0) {
*cp = 0;
ds = getzenv(s);
*cp = '}';
} else {
ds = getzenv(s);
}
break;
default:
ds = NULL;
break;
}
if (ds == NULL || *ds == '\0') {
char msg[BUFSIZ];
sprintf(msg,
"Null value for $%c (%%s) (msgfile: %s)!",
*cp, dp->msgfile);
warning(msg, mp->name);
} else {
int len = strlen(ds);
if (ws + len >= we)
break; /* D'uh :-( */
memcpy(ws, ds, len+1);
ws += len;
}
} else
if (ws < we)
*ws++ = *cp;
}
if (ws < we)
*ws = '\0';
else
we[-1] = '\0'; /* Trunk in all cases */
/* not worth freeing this stuff */
av[ii] = strdup(buf);
if (j > 0)
++i;
} while (rp != startrp && rp != endrp);
/* End of: "do {" */
}
/* End of: "for (j = ...) {" */
av[i] = NULL;
gotsigpipe = 0;
/* now we can fork off and run the command... */
if (pipe(out) < 0) {
for (rp = startrp; rp != endrp; rp = rp->next) {
notaryreport(rp->addr->user,"failed",
"5.3.0 (Out of system resources, pipe creation failed)",
"x-local; 500 (pipe creation error, out of system resources ?)");
diagnostic(verboselog, rp, EX_OSERR, 0,
"cannot create pipe from \"%s\"",
mp->command);
SM_MIB_diag(EX_OSERR);
}
return;
}
if (pipe(in) < 0) {
for (rp = startrp; rp != endrp; rp = rp->next) {
notaryreport(rp->addr->user,"failed",
"5.3.0 (Out of system resources, pipe creation failed)",
"x-local; 500 (pipe creation error, out of system resources ?)");
diagnostic(verboselog, rp, EX_OSERR, 0,
"cannot create pipe to \"%s\"",
mp->command);
SM_MIB_diag(EX_OSERR);
}
return;
}
if (verboselog) {
const char **p = av;
fprintf(verboselog,"To run UID=%d GID=%d ARGV[] =",
(int)getuid(), (int)getgid());
for ( ;*p != NULL; ++p) {
fprintf(verboselog," '%s'", *p);
}
fprintf(verboselog,"\n");
fflush(verboselog);
}
if ((pid = fork()) == 0) { /* child, run the command */
if (!(mp->flags & MO_NORESETUID)) {
/* struct passwd *pw = getpwuid(); */
setuid(getuid());
}
if (in[1] > 0) {
/* its stdout and stderr is the pipe, its stdin is our tafp */
if (out[0] > 0) dup2(out[0], 0);
if (in[1] > 1) dup2(in[1], 1);
dup2(1, 2);
if (in[0] > 2) close(in[0]);
if (in[1] > 2) close(in[1]);
if (out[0] > 2) close(out[0]);
if (out[1] > 2) close(out[1]);
} else {
close(in[0]);
close(out[1]);
/* its stdout and stderr is the pipe, its stdin is our tafp */
close(0);
close(1);
close(2);
dup2(out[0], 0);
close(out[0]);
dup2(in[1], 1);
dup2(in[1], 2);
close(in[1]);
}
execv(mp->command, (char**) av);
_exit(254);
} else if (pid < 0) { /* couldn't fork, complain */
for (rp = startrp; rp != endrp; rp = rp->next) {
notaryreport(rp->addr->user,"failed",
"5.3.0 (Out of system resources, fork failed)",
"x-local; 500 (fork failure, out of system resources ?)");
diagnostic(verboselog, rp, EX_OSERR, 0, "cannot fork");
SM_MIB_diag(EX_OSERR);
}
return;
}
close(out[0]); /* child ends.. */
close(in[1]);
tafp = fdopen(out[1], "w"); /* parent ends .. */
errfp = fdopen(in[0], "r");
/* read any messages from its stdout/err on in[0] */
if (verboselog) {
fprintf(verboselog,"%s\n\t", mp->command);
for (i = 0; av[i] != NULL; ++i)
fprintf(verboselog,"%s ", av[i]);
fprintf(verboselog,"\n");
}
free((char *)av);
/* ... having forked and set up the pipe, we quickly continue */
/* BSMTP et.al. envelope formation here! */
if (mp->flags & MO_BSMTP) {
if (mp->flags & MO_BSMTPHELO) {
if (mp->flags & MO_BESMTP)
fprintf(tafp,"EHLO %s",myhostname);
else
fprintf(tafp,"HELO %s",myhostname);
if (mp->flags & MO_CRLF) putc('\r',tafp);
putc('\n',tafp);
}
if (strcmp(startrp->addr->link->channel,"error")==0)
fprintf(tafp,"MAIL From:<>");
else
fprintf(tafp,"MAIL From:<%s>", startrp->addr->link->user);
if (mp->flags & MO_BESMTP) {
fprintf(tafp," SIZE=%ld",startrp->desc->msgsizeestimate);
if (can_8bit)
fprintf(tafp," BODY=8BITMIME");
}
if (mp->flags & MO_BEDSMTP) {
if (startrp->desc->envid != NULL)
fprintf(tafp," ENVID=%s",startrp->desc->envid);
if (startrp->desc->dsnretmode != NULL)
fprintf(tafp, " RET=%s", startrp->desc->dsnretmode);
}
if (mp->flags & MO_CRLF) lineendseq = "\r\n";
fputs(lineendseq, tafp);
for (rp = startrp; rp != endrp; rp = rp->next) {
fprintf(tafp,"RCPT TO:<%s>",rp->addr->user);
/* if (mp->flags & MO_BESMTP) { } */
if (mp->flags & MO_BEDSMTP) {
if (rp->notifyflgs) {
char *s = "";
fprintf(tafp," NOTIFY=");
if (rp->notifyflgs & _DSN_NOTIFY_NEVER) {
fprintf(tafp,"NEVER");
}
if (rp->notifyflgs & _DSN_NOTIFY_SUCCESS) {
fprintf(tafp,"SUCCESS");
s = ",";
}
if (rp->notifyflgs & _DSN_NOTIFY_FAILURE) {
fprintf(tafp,"%sFAILURE",s);
s = ",";
}
if (rp->notifyflgs & _DSN_NOTIFY_DELAY) {
fprintf(tafp,"%sDELAY",s);
}
}
if (rp->orcpt)
fprintf(tafp," ORCPT=%s",rp->orcpt);
}
if (mp->flags & MO_BEBSMTP) {
if (rp->deliverby) {
fprintf(tafp," BY=%ld;", (long)(rp->deliverby - now));
if (rp->deliverbyflgs & _DELIVERBY_R) fputc('R',tafp);
if (rp->deliverbyflgs & _DELIVERBY_N) fputc('N',tafp);
if (rp->deliverbyflgs & _DELIVERBY_T) fputc('T',tafp);
}
}
fputs(lineendseq, tafp);
}
fprintf(tafp,"DATA");
fputs(lineendseq, tafp);
}
/* Now continue with inside stuff -- well, normal UUCP stuff */
if (mp->flags & (MO_UNIXFROM|MO_REMOTEFROM)) {
const char *uu = startrp->addr->link->user;
if (strcmp(startrp->addr->link->channel,"error")==0)
uu = "<>";
fprintf(tafp, "%s%s %s", FROM_, uu, timestring);
if (mp->flags & MO_REMOTEFROM)
fprintf(tafp, " remote from %s", uucpname);
if (verboselog) {
fprintf(verboselog, "%s%s %s", FROM_, uu, timestring);
if (mp->flags & MO_REMOTEFROM)
fprintf(verboselog, " remote from %s", uucpname);
putc('\n',verboselog);
}
putc('\n', tafp);
}
conversion_prohibited = check_conv_prohibit(startrp);
/* Content-Transfer-Encoding: 8BIT ? */
content_kind = cte_check(startrp);
/* If the header says '8BIT' and ISO-8859-* something,
but body is plain 7-bit, turn it to '7BIT', and US-ASCII */
ascii_clean = check_7bit_cleanness(dp);
if (!conversion_prohibited && ascii_clean && content_kind == 8) {
if (downgrade_charset(startrp, verboselog))
content_kind = 7;
}
convertmode = _CONVERT_NONE;
if (!conversion_prohibited) {
switch (content_kind) {
case 0: /* No MIME headers defined */
if (!can_8bit && !ascii_clean) {
convertmode = _CONVERT_UNKNOWN;
downgrade_headers(startrp, convertmode, verboselog);
}
break;
case 2: /* MIME, but no C-T-E: ? */
case 1: /* MIME BASE64 ? some MIME anyway.. */
case 7: /* 7BIT */
convertmode = _CONVERT_NONE;
break;
case 8: /* 8BIT */
if (!can_8bit && !ascii_clean) {
convertmode = _CONVERT_QP;
if (!downgrade_headers(startrp, convertmode, verboselog))
convertmode = _CONVERT_NONE;
}
break;
case 9: /* QUOTED-PRINTABLE */
if (decode_qp) {
/* Force(d) to decode Q-P while transfer.. */
convertmode = _CONVERT_8BIT;
/* UPGRADE TO 8BIT ! */
if (!qp_to_8bit(startrp))
convertmode = _CONVERT_NONE;
content_kind = 10;
ascii_clean = 0;
}
break;
default: /* ?? should not happen.. */
break;
}
if (!keep_header8 && headers_need_mime2(startrp)) {
headers_to_mime2(startrp,defcharset,verboselog);
}
}
/* Snub the "Return-Path:" header, if it exists.. */
if (1) {
char **hdrs;
do {
hdrs = has_header(startrp,"Return-Path:");
if (hdrs) delete_header(startrp, hdrs);
} while (hdrs);
}
/* Snub stuff that we add later below.. */
if (mp->flags & MO_XENVELOPES) {
char **hdrs;
do {
hdrs = has_header(startrp,"X-Envelope-To:");
if (hdrs) delete_header(startrp, hdrs);
} while (hdrs);
do {
hdrs = has_header(startrp,"X-Envid:");
if (hdrs) delete_header(rp,hdrs);
} while (hdrs);
do {
hdrs = has_header(startrp,"Envelope-Id:");
if (hdrs) delete_header(rp,hdrs);
} while (hdrs);
}
/* PERT-NB X-Orcpt if O option */
if (mp->flags & (MO_XORCPT|MO_XENVELOPES)) {
char **hdrs;
do {
hdrs = has_header(startrp,"X-Orcpt:");
if (hdrs) delete_header(startrp, hdrs);
} while (hdrs);
do { /* RFC 2298 section 2.3 */
hdrs = has_header(startrp,"Original-Recipient:");
if (hdrs) delete_header(startrp, hdrs);
} while (hdrs);
}
/* PERT-NB END */
/* Write headers: */
header_received_for_clause(startrp, 0, verboselog);
fwriteheaders(startrp, tafp, lineendseq, convertmode, maxwidth, NULL);
if (verboselog)
fwriteheaders(startrp, verboselog, "\n", convertmode, maxwidth, NULL);
/*
* NOTE: Following header writers make sense only for SINGLE
* RECIPIENT DELIVERIES!
*
*/
if (mp->flags & MO_XENVELOPES) {
if (dp->envid) {
fprintf(tafp, "Envelope-Id: ");
decodeXtext(tafp, dp->envid);
fputs(lineendseq, tafp);
if (verboselog) {
fprintf(verboselog, "Envelope-Id: ");
decodeXtext(verboselog, dp->envid);
fputs("\n", verboselog);
}
}
}
if (mp->flags & (MO_XORCPT|MO_XENVELOPES)) {
/* Put out a sequence of X-Envelope-To: and
Original-Recipient: headers -- in order.. */
for (rp = startrp; rp != endrp; rp = rp->next) {
const char *uu = rp->addr->user;
if (strcmp(rp->addr->link->channel,"error")==0)
uu = "";
fprintf(tafp, "X-Envelope-To: <%s> (uid %s)%s",
uu, rp->addr->misc, lineendseq);
if (rp->orcpt) {
/* RFC 2298: section 2.3 */
fprintf(tafp, "Original-Recipient: ");
decodeXtext(tafp, rp->orcpt);
fputs(lineendseq, tafp);
}
if (verboselog) {
fprintf(verboselog, "X-Envelope-To: <%s> (uid %s)\n",
uu, rp->addr->misc);
if (rp->orcpt) {
/* RFC 2298: section 2.3 */
fprintf(verboselog, "Original-Recipient: ");
decodeXtext(verboselog, rp->orcpt);
fputs("\n", verboselog);
}
}
}
}
if (mp->flags & MO_RETURNPATH) {
const char *uu = startrp->addr->link->user;
if (strcmp(startrp->addr->link->channel,"error")==0)
uu = "";
fprintf(tafp, "Return-Path: <%s>%s", uu, lineendseq);
}
/* Header-Body separator: */
fputs(lineendseq, tafp);
if (verboselog) fprintf(verboselog, "\n");
/* Append message body itself */
i = appendlet(dp, mp, tafp, verboselog, convertmode);
if (i != EX_OK && !gotsigpipe) {
for (rp = startrp; rp != endrp; rp = rp->next) {
notaryreport(rp->addr->user,"failed",
/* Could indicate: 4.3.1 - mail system full ?? */
"5.3.0 (Write to target failed for some reason)",
"x-local; 500 (Write to target failed for some reason)");
diagnostic(verboselog, rp, i, 0, "write error");
SM_MIB_diag(i);
}
/* just to make sure nothing will get delivered */
kill(pid, SIGTERM);
sleep(1);
kill(pid, SIGKILL);
wait(NULL);
fclose(tafp); /* FP/pipe cleanups */
fclose(errfp);
return;
}
if (mp->flags & MO_BSMTP) {
fputs(lineendseq, tafp);
}
fclose(tafp);
close(out[1]); /* paranoia */
if (fgets(buf, sizeof buf, errfp) == NULL)
buf[0] = '\0';
else if ((cp = strchr(buf, '\n')) != NULL)
*cp = '\0';
fclose(errfp);
close(in[0]); /* more paranoia */
cp = buf + strlen(buf);
exd = exs = NULL;
pid = wait(&status);
if (WSIGNALSTATUS(status) != 0) {
if (cp != buf)
*cp++ = ' ';
sprintf(cp, "[signal %d", WSIGNALSTATUS(status));
if (status&0200)
strcat(cp, " (Core dumped)");
strcat(cp, "]");
i = EX_TEMPFAIL;
exd = "x-local; 500 (failed on signal)";
exs = "5.3.0";
} else if (WEXITSTATUS(status) == 0
#if EX_OK != 0
|| WEXITSTATUS(status) == EX_OK
#endif
) {
i = EX_OK;
} else {
i = WEXITSTATUS(status);
s = NULL;
for (exp = & exmap[0]; exp->origstatus != 0; ++exp)
if (exp->origstatus == i) {
s = exp->statusmsg;
i = exp->newstatus;
exs = exp->dsnstatus;
exd = exp->dsndiags;
break;
}
sprintf(cp, "[exit status %d/%d", WEXITSTATUS(status), i);
if (s)
sprintf(cp+strlen(cp), " (%s)", s);
/* sprintf(cp+strlen(cp), " of command: %s", mp->command); */
strcat(cp, "]");
}
for (rp = startrp; rp != endrp; rp = rp->next) {
if (i == EX_OK)
notaryreport(rp->addr->user, "relayed",
"2.5.0", "smtp;250 (Delivered)");
else
notaryreport(rp->addr->user, "failed", exs, exd);
diagnostic(verboselog, rp, i, 0, "%s", buf);
SM_MIB_diag(i);
}
/* XX: still need to deal with MO_STRIPQUOTES */
}
/*
* appendlet - append letter to file pointed at by fd
*/
int
appendlet(dp, mp, fp, verboselog, convertmode)
struct ctldesc *dp;
struct maildesc *mp;
FILE *fp;
FILE *verboselog;
int convertmode;
{
/* `convertmode' controls the behaviour of the message conversion:
_CONVERT_NONE (0): send as is
_CONVERT_QP (1): Convert 8-bit chars to QUOTED-PRINTABLE
_CONVERT_8BIT (2): Convert QP-encoded chars to 8-bit
_CONVERT_UNKNOWN (3): Turn message to charset=UNKNOWN-8BIT, Q-P..
*/
register int i;
int lastch, rc;
register int bufferfull;
int mfd = dp->msgfd;
writebuf(mp, fp, (char *)NULL, 0); /* magic initialization */
lastch = -1;
if (convertmode == _CONVERT_NONE) {
if (ta_use_mmap <= 0) {
/* can we use cache of message body data */
if (readalready > 0) {
lastch = dp->let_buffer[readalready-1];
if (writebuf(mp, fp, dp->let_buffer, readalready) != readalready)
return EX_IOERR;
if (lastch != '\n')
if (writebuf(mp, fp, "\n", 1) != 1)
return EX_IOERR;
return EX_OK;
}
bufferfull = 0;
readalready = 0;
lastch = -256;
lseek(mfd, dp->msgbodyoffset, SEEK_SET);
for (;;) {
i = read(mfd, (void*)(dp->let_buffer), dp->let_buffer_size);
if (i == 0) break; /* EOF */
if (i < 0 && (errno == EINTR || errno == EAGAIN))
continue;
if (i < 0)
return EX_IOERR;
lastch = dp->let_buffer[i-1];
if (writebuf(mp, fp, dp->let_buffer, i) != i)
return EX_IOERR;
readalready = i;
bufferfull++;
}
if (lastch != '\n')
if (writebuf(mp, fp, "\n", 1) != 1)
return EX_IOERR;
if (bufferfull > 1) /* not all in memory, need to reread */
readalready = 0;
} else { /* mmap()ed message buffer */
const char *p = dp->let_buffer + dp->msgbodyoffset;
i = dp->let_end - p;
if (i < 0)
return EX_IOERR;
lastch = dp->let_end[-1];
if (writebuf(mp, fp, p, i) != i)
return EX_IOERR;
if (lastch != '\n')
if (writebuf(mp, fp, "\n", 1) != 1)
return EX_IOERR;
}
rc = EX_OK;
} else {
/* convertmode something else, than _CONVERT_NONE */
/* Various esoteric conversion modes..
We are better to feed writemimeline() with LINES
instead of blocks of data.. */
writemimeline(mp, fp, (char *)NULL, 0, 0); /* reset */
/*
if(verboselog) fprintf(verboselog,
"sm: Convert mode: %d, fd=%d, fdoffset=%d, bodyoffset=%d\n",
convertmode, mfd, (int)lseek(mfd, (off_t)0, SEEK_CUR),
dp->msgbodyoffset);
*/
/* we are assuming to be positioned properly
at the start of the message body */
lastch = -1;
i = 0;
if (ta_use_mmap <= 0) {
/* Locally read, line by line */
char sfio_buf[16*1024];
Sfio_t *mfp = NULL;
mfp = sfnew(NULL, sfio_buf, sizeof(sfio_buf), mfd, SF_READ);
sfseek(mfp, dp->msgbodyoffset, SEEK_SET);
readalready = 0;
for (;;) {
i = csfgets((void*)(dp->let_buffer), dp->let_buffer_size, mfp);
if (i == EOF)
break;
lastch = dp->let_buffer[i-1];
/* It MAY be malformed -- if it has a BUFSIZ length
line in it, IT CAN'T BE MIME :-/ */
/* Ok, write the line */
if (writemimeline(mp, fp, dp->let_buffer, i, convertmode) != i)
return EX_IOERR;
}
if (mfp) {
if (i == EOF && !sfeof(mfp) && !sferror(mfp)) {
rc = EX_IOERR;
}
zsfsetfd(mfp, -1);
sfclose(mfp);
}
rc = EX_OK;
} else {
/* MMAP()ED buffer */
const char *s = dp->let_buffer + dp->msgbodyoffset;
for (;;) {
const char *s2 = s;
i = 0;
if (s >= dp->let_end) break; /* "EOF" */
while (s2 < dp->let_end && *s2 != '\n')
++s2, ++i;
if ((lastch = *s2) == '\n')
++s2, ++i;
/* Ok, write the line */
if (writemimeline(mp, fp, s, i, convertmode) != i)
return EX_IOERR;
s = s2;
}
}
rc = EX_OK;
}
/* we must make sure the last thing we transmit is a CRLF sequence */
if (lastch != '\n')
writebuf(mp, fp, "\n", 1);
/* Failure in that last CRLF writing does not
affect our return code -- should it ?? */
return rc;
}
/*
* Writebuf() is like write(), except all '\n' are converted to "\r\n"
* (CRLF), and the sequence "\n.\n" is converted to "\r\n..\r\n".
*/
int
writebuf(mp, fp, buf, len)
struct maildesc *mp;
FILE *fp;
const char *buf;
int len;
{
register const char *cp;
register int n;
int tlen;
register char expect;
static char save = '\0';
static char frombuf[8];
static char *fromp;
if (buf == NULL) { /* magic initialization */
save = '.';
frombuf[0] = 0;
fromp = frombuf;
return 0;
}
expect = save;
for (cp = buf, n = len, tlen = 0; n > 0; --n, ++cp) {
int c = (*cp) & 0xFF;
if (mp->flags & MO_STRIPHIBIT)
c &= 0x7F;
++tlen;
if (c == '\n') {
frombuf[0] = 0;
fromp = frombuf;
if (expect == '\n' && (mp->flags & MO_HIDDENDOT))
/* "\n.\n" sequence */
if (putc('.', fp) == EOF) { tlen = -1; break; }
if (mp->flags & MO_CRLF)
if (putc('\r', fp) == EOF) { tlen = -1; break; }
if (putc(c,fp) == EOF) { tlen = -1; break; }
expect = '.';
} else if (expect != '\0') {
if (expect == '.') {
if ((mp->flags & MO_ESCAPEFROM) && c == 'F')
expect = 'F';
else if (c == '.' && (mp->flags & MO_HIDDENDOT)) {
if (putc('.', fp) == EOF || putc('.', fp) == EOF)
{ tlen = -1; break; }
expect = '\0';
continue;
} else {
if (putc(c, fp) == EOF)
{ tlen = -1; break; }
expect = '\0';
continue;
}
}
if (c == expect) {
*fromp++ = c;
*fromp = 0;
switch (expect) {
case 'F': expect = 'r'; break;
case 'r': expect = 'o'; break;
case 'o': expect = 'm'; break;
case 'm': expect = ' '; break;
case ' ':
/* Write the separator, and the word.. */
if (fwrite(">From ", 6, 1, fp) == 0)
{ tlen = -1; break; }
/* anticipate future instances */
expect = '\0';
break;
}
} else {
expect = '\0';
fromp = frombuf;
while (*fromp) {
if (putc(*fromp,fp) == EOF)
{ tlen = -1; break; }
++fromp;
}
frombuf[0] = 0;
if (putc(c,fp) == EOF)
{ tlen = -1; break; }
}
} else {
/* expect == 0 */
if (putc(c,fp) == EOF)
{ tlen = -1; break; }
}
}
save = expect;
return tlen;
}
int
writemimeline(mp, fp, buf, len, convertmode)
struct maildesc *mp;
FILE *fp;
const char *buf;
int len, convertmode;
{
/* `convertmode' controls the behaviour of the message conversion:
_CONVERT_NONE (0): send as is
_CONVERT_QP (1): Convert 8-bit chars to QUOTED-PRINTABLE
_CONVERT_8BIT (2): Convert QP-encoded chars to 8-bit
_CONVERT_UNKNOWN (3): Turn message to charset=UNKNOWN-8BIT, Q-P..
*/
register const char *cp;
register int n;
static int column;
register int qp_conv;
register int qp_chrs = 0;
if (buf == NULL) {
column = -1;
return 0;
}
qp_conv = (convertmode == _CONVERT_QP ||
convertmode == _CONVERT_UNKNOWN);
for (cp = buf, n = len; n > 0; --n, ++cp) {
int c = (*cp) & 0xFF;
++column;
if (qp_conv) {
/* ENCODE to QUOTED-PRINTABLE ... */
if (column > 70 && c != '\n') {
putc('=',fp);
if (mp->flags & MO_CRLF)
putc('\r', fp);
putc('\n',fp);
column = 0;
}
if (column == 0 && (mp->flags & MO_HIDDENDOT) && c == '.') {
/* Duplicate the line initial dot.. */
if (putc(c,fp)==EOF) return EOF;
} else if (column == 0 && (mp->flags & MO_ESCAPEFROM) &&
c == 'F' && n >= 4 && strncmp(cp,"From",4)==0) {
/* We Q-P encode the leading 'F'.. */
if (fputs("=46",fp) != 3) return EOF;
column += 2;
} else if ((n < 3 || mimeqpnarrow) && c != '\n' &&
(c <= 32 || c > 126 || c == '=')) {
/* Downgrade it by translating it to Quoted-Printable.. */
/* Translate also trailing spaces/TABs */
if (fprintf(fp,"=%02X",c) != 3) return EOF;
column += 2;
} else if (c != '\n' && c != '\t' &&
(c < 32 || c > 126 || c == '=')) {
/* Downgrade it by translating it to Quoted-Printable.. */
/* SPACE and TAB are left untranslated */
if (fprintf(fp,"=%02X",c) != 3) return EOF;
column += 2;
buf = cp;
} else if (c == '\n') { /* This is most likely the LAST char */
if (mp->flags & MO_CRLF)
if (putc('\r', fp) == EOF) return EOF;
if (putc(c,fp) == EOF) return EOF;
column = -1;
} else {
if (putc(c, fp) == EOF) return EOF;
}
} else if (convertmode == _CONVERT_8BIT) {
/* DECODE from QUOTED-PRINTABLE text.. */
static int qp_val = 0;
if (!qp_chrs && c == '=') { /* Q-P -prefix */
qp_chrs = 2;
qp_val = 0;
continue;
} else if (qp_chrs) {
--column;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
break; /* Done with it, it was soft end-of-line */
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f')) {
/* A HEX digit ? QP char coming up ? */
if (c >= 'a') c -= ('a' - 'A');
if (c >= 'A') c -= ('A' - '9' -1);
qp_val <<= 4;
qp_val |= (c & 0x0F);
if (--qp_chrs)
continue; /* Not yet last.. */
else
c = qp_val; /* The second (=last) hex digit */
} else
qp_chrs = 0; /* While in this mode, NOT QP-hex-digit! */
} /* Ok, decoded possible Q-P chars. Now normal processing.. */
if (column == 0 && c == '.' && (mp->flags & MO_HIDDENDOT)) {
if (putc(c,fp)==EOF) return EOF;
} else if (column == 0 && (mp->flags & MO_ESCAPEFROM) &&
c == 'F' && strncmp(cp+1,"rom",3)==0) {
if (putc('>',fp)==EOF) return EOF;
++column;
} else if (c == '\n') {
if (mp->flags & MO_CRLF)
if (putc('\r',fp)==EOF) return EOF;
column = -1;
}
/* And output the char.. */
if (putc(c,fp)==EOF) return EOF;
} else
abort(); /* WOO! We should not be called for '_CONVERT_NONE'! */
}
if (feof(fp) || ferror(fp)) return EOF;
return len;
}
struct maildesc *
readsmcf(file, mailer)
char *file, *mailer;
{
char *entry, buf[BUFSIZ];
unsigned char *cp;
FILE *fp;
int i;
static struct maildesc m;
if (file == NULL) {
const char *mailshare = getzenv("MAILSHARE");
if (mailshare == NULL)
mailshare = MAILSHARE;
sprintf(buf, "%s/%s.conf", mailshare, progname);
file = buf;
}
if ((fp = fopen(file, "r")) == NULL) {
fprintf(stderr, "%s: cannot open ", progname);
perror(file);
exit(EX_OSFILE);
}
entry = NULL;
cp = NULL;
while (fgets(buf, sizeof buf, fp) != NULL) {
if (buf[0] == '#' || buf[0] == '\n')
continue;
if ((cp = emalloc(strlen(buf)+1)) == NULL) {
fprintf(stderr, "%s: Out of Virtual Memory!\n",
progname);
exit(EX_OSERR);
}
entry = (char*)cp;
strcpy(entry, buf);
SKIPTEXT(cp);
if (isascii(*cp) && isspace(*cp)) {
if (*cp == '\n') {
fprintf(stderr, "%s: %s: bad entry: %s",
progname, file, entry);
} else
*cp = '\0';
} else {
fprintf(stderr, "%s: %s: bad entry: %s",
progname, file, entry);
}
if (strcmp(entry, mailer) == 0)
break;
free(entry);
entry = NULL;
}
fclose(fp);
if (entry == NULL)
return NULL;
m.name = entry;
m.flags = MO_UNIXFROM;
++cp;
SKIPSPACE(cp);
/* process mailer option flags */
for (;*cp && *cp != ' ' && *cp != '\t' && *cp != '\n'; ++cp) {
int no = 0;
switch (*cp) {
case '7': m.flags |= MO_STRIPHIBIT; break;
case '8': can_8bit = 1; break;
case '9': decode_qp = 1; break;
case 'A': no=*cp; break; /* arpanet-compatibility */
case 'b': m.flags |= (MO_BSMTP|MO_HIDDENDOT); break;
case 'B': if (m.flags & MO_BESMTP) /* -BB */
m.flags |= MO_BEDSMTP;
else
m.flags |= MO_BESMTP|MO_BSMTP|MO_HIDDENDOT;
break;
case 'C': no=*cp; break; /* canonicalize remote hostnames */
case 'D': /* this mailer wants a Date: line */
m.flags |= MO_WANTSDATE; break;
case 'e': m.flags |= MO_XENVELOPES; break;
/* PERT-NB O option for X-Orcpt */
case 'O': m.flags |= MO_XORCPT; break;
case 'E': m.flags |= MO_ESCAPEFROM; break;
case 'f': m.flags |= MO_FFROMFLAG; break;
case 'F': /* this mailer wants a From: line */
m.flags |= MO_WANTSFROM; break;
case 'h': no=*cp; break; /* preserve upper case in host names */
case 'H': m.flags |= MO_BSMTPHELO; break;
case 'I': no=*cp; break; /* talking to a clone of I */
case 'l': no=*cp; break; /* this is a local mailer */
case 'L': no=*cp; break; /* limit line length */
case 'm': m.flags |= MO_MANYUSERS; break;
case 'M': no=*cp; break; /* this mailer wants a Message-Id: line */
case 'n': m.flags &= ~MO_UNIXFROM; break;
case 'p': no=*cp; break; /* use SMTP return path */
case 'P': m.flags |= MO_RETURNPATH; break;
case 'r': m.flags |= MO_RFROMFLAG; break;
case 'R': m.flags |= MO_CRLF; break;
case 's': m.flags |= MO_STRIPQUOTES; break;
case 'S': m.flags |= MO_NORESETUID; break;
case 'u': no=*cp; break; /* preserve upper case in user names */
case 'U': m.flags |= MO_REMOTEFROM; break;
case 'x': no=*cp; break; /* this mailer wants a Full-Name: line */
case 'X': m.flags |= MO_HIDDENDOT; break;
case '-': break; /* ignore */
default:
fprintf(stderr,
"%s: unknown sendmail mailer option '%c'\n",
progname, *cp);
break;
}
if (no) {
fprintf(stderr,
"%s: the '%c' sendmail mailer option does not make sense in this environment\n",
progname,no);
}
}
SKIPSPACE(cp);
m.command = (char*) cp;
SKIPTEXT(cp);
if ((char*)cp == m.command) {
fprintf(stderr,"%s: bad entry for %s\n",progname, m.name);
return NULL;
}
*cp++ = '\0';
if (*m.command != '/') {
char *nmc;
const char *mailbin = getzenv("MAILBIN");
if (mailbin == NULL)
mailbin = MAILBIN;
nmc = emalloc(strlen(mailbin)+1+strlen(m.command)+1);
sprintf(nmc, "%s/%s", mailbin, m.command);
m.command = nmc;
}
SKIPSPACE(cp);
i = 0;
while (isascii(*cp) && !isspace(*cp) && i < MD_ARGVMAX) {
if (*cp == '\0')
break;
m.argv[i++] = (char*) cp;
SKIPTEXT(cp);
if (*cp) {
*cp++ = '\0';
SKIPSPACE(cp);
}
}
if (i == 0) {
fprintf(stderr,
"%s: bad command for %s\n", progname, m.name);
return NULL;
}
m.argv[i] = NULL;
return &m;
}
/* When data is clean 7-BIT, return 1.. (zero == non-clean) */
int
check_7bit_cleanness(dp)
struct ctldesc *dp;
{
if (ta_use_mmap > 0) {
/* With MMAP()ed spool file it is sweet and simple.. */
register const char *s = dp->let_buffer + dp->msgbodyoffset;
while (s < dp->let_end)
if (128 & *s) {
/*
if (verboselog)
fprintf(verboselog,
"check_7bit_cleanness() non-clean byte on offset %d, str=\"%-8s\"\n",
s-(dp->let_buffer + dp->msgbodyoffset), s);
*/
return 0; /* Not clean ! */
}
else ++s;
} else {
/* Without MMAP() we work... */
register int i;
register int bufferfull;
int lastwasnl;
int mfd = dp->msgfd;
/* can we use cache of message body data */
if (readalready != 0) {
for (i=0; i<readalready; ++i)
if (128 & (dp->let_buffer[i]))
return 0; /* Not clean ! */
}
/* we are assumed to be positioned properly at start of message body */
bufferfull = 0;
for (;;) {
i = read(mfd, (void*)(dp->let_buffer), dp->let_buffer_size);
if (i == 0) break; /* EOF */
if (i < 0) {
/* ERROR ?!?!? */
if (errno == EINTR || errno == EAGAIN)
continue;
readalready = 0;
lseek(mfd, dp->msgbodyoffset, SEEK_SET);
return 0;
}
lastwasnl = (dp->let_buffer[i-1] == '\n');
readalready = i;
bufferfull++;
for (i=0; i < readalready; ++i)
if (128 & (dp->let_buffer[i])) {
lseek(mfd, dp->msgbodyoffset, SEEK_SET);
/* We probably have not read everything of the file! */
readalready = 0;
return 0; /* Not clean ! */
}
}
/* Got to EOF, and still it is clean 7-BIT! */
lseek(mfd, dp->msgbodyoffset, SEEK_SET);
if (bufferfull > 1) /* not all in memory, need to reread */
readalready = 0;
}
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1