/*
* Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
* This will be free software, but only when it is finished.
*/
/*
* Some modifications by
* Matti Aarnio <mea@nic.funet.fi> (copyright) 1992-2003
*/
/*
* This file contains most of the RFC822-specific message manipulation.
*/
#include "mailer.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/file.h>
#include <errno.h>
#include "mail.h"
#include "libz.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "zsyslog.h"
#ifdef HAVE_SYS_UN_H
#include <sys/socket.h>
#include <sys/un.h>
#endif
#include "prototypes.h"
#include "libsh.h"
#include "zmsignal.h"
#include "shmmib.h"
#ifndef _IOFBF
#define _IOFBF 0
#endif /* !_IOFBF */
#ifndef _IOLBF
#define _IOLBF 0200
#endif /* !_IOFBF */
#ifdef HAVE_LOCKF
#ifdef F_OK
#undef F_OK
#endif /* F_OK */
#ifdef X_OK
#undef X_OK
#endif /* X_OK */
#ifdef W_OK
#undef W_OK
#endif /* W_OK */
#ifdef R_OK
#undef R_OK
#endif /* R_OK */
#endif /* HAVE_LOCKF */
static void reject __((struct envelope *e, const char *msgfile));
static const char * prctladdr __((conscell *info, FILE *fp, int cfflag, const char *comment));
static void prdsndata __((conscell *info, FILE *fp, const char *comment));
static conscell *find_errto __((conscell *list));
#define dprintf if (D_sequencer) printf
#define QCHANNEL(x) (x)
#define QHOST(x) cdr(x)
#define QUSER(x) cddr(x)
#define QATTRIBUTES(x) cdr(cddr(x))
conscell *
makequad()
{
conscell *l;
GCVARS1;
l = NULL;
GCPRO1(l);
l = conststring(NULL, 0);
cdr(l) = conststring(NULL, 0);
cddr(l) = conststring(NULL, 0);
cdr(cddr(l)) = conststring(NULL, 0);
UNGCPRO1;
return l;
}
static int
iserrmessage(e)
struct envelope *e;
{
return (e && e->e_from_trusted &&
(QCHANNEL(e->e_from_trusted)->cstring == NULL ||
CISTREQ(QCHANNEL(e->e_from_trusted)->cstring, "error")));
}
static int zunlink __((const char *));
static int zunlink(path)
const char *path;
{
int rc;
while ((rc = unlink(path)) < 0 && (errno == EBUSY || errno == EINTR))
;
return rc;
}
/*
* Apply RFC822 parsing and processing to the message in the file in argv[1].
*/
int
run_rfc822(argc, argv)
int argc;
const char *argv[];
{
struct envelope *e; /* TWO GC PROTECTABLE ITEMS! */
const char *file;
char buf[8196]; /* FILE* buffer, will be released at fclose() */
int status = PERR_OK, errflg;
memtypes oval;
GCVARS2;
errflg = 0;
#if 0
{
int c;
zoptind = 1;
while ((c = zgetopt(argc, argv, "")) != EOF) {
switch (c) {
default:
++errflg;
break;
}
}
}
if (errflg || zoptind != (argc - 1))
#endif
if (argc != 2)
{
fprintf(stderr, "Usage: %s messagefile\n", argv[0]);
return PERR_USAGE;
}
/* file = argv[zoptind]; */
file = argv[1];
#ifdef XMEM
mal_contents(stdout);
#endif /* XMEM */
oval = stickymem;
stickymem = MEM_TEMP; /* per-message space */
e = (struct envelope *)tmalloc(sizeof (struct envelope));
/* XXX: If this tmalloc() fails, we crash! */
memset(e, 0, sizeof(*e)); /* Lots of pointers, etc here! */
GCPRO2(e->e_from_trusted, e->e_from_resolved);
if ((e->e_fp = fopen(file, "r")) == NULL) {
fprintf(stderr, "router: cannot open %s\n", file);
status = PERR_BADOPEN;
} else {
/* XX: DEBUG STUFF! */
if (FILENO(e->e_fp) < 3) {
fprintf(stderr,"RFC822: While opening mail-file '%s', got fd=%d AARGH! (fds 0..2 should never close on us!)",
file, FILENO(e->e_fp));
fflush(stderr);
/* abort(); */
}
status = PERR_OK;
setvbuf(e->e_fp, buf, _IOFBF, sizeof buf);
e->e_file = file;
status = makeLetter(e);
}
if (status == PERR_OK)
/* passing the file is redundant but nice for backtraces */
status = sequencer(e, file);
switch (status) {
case PERR_OK: /* 0 */
break;
case PERR_BADOPEN:
break;
case PERR_BADCONTINUATION: /* fatal */
squirrel(e, ":badenvelope", "continuation line prior to header");
break;
case PERR_BADSUBMIT: /* fatal */
squirrel(e, ":badsubmit", "unrecognized envelope information");
break;
case PERR_LOOP: /* fatal */
reject(e, "loopexceeded");
squirrel(e, "_looped", "loop count exceeded");
break;
case PERR_ENVELOPE: /* fatal */
reject(e, "envelope");
break;
case PERR_DEFERRED:
if (deferuid)
defer(e, "deferuid");
else
defer(e, "deferred");
break;
case PERR_HEADER: /* fatal */
reject(e, "header");
break;
case PERR_NORECIPIENTS: /* fatal */
reject(e, "norecipients");
break;
case PERR_NOSENDER:
squirrel(e, "nosender", "really truly Unknown Sender");
break;
case PERR_CTRLFILE:
defer(e, "ctrlfile");
break;
default:
abort(); /* Impossible processing status */
}
if (D_final > 0)
dumpInfo(e);
/* This will always make some noise in successfull case,
except that it is SILENT code... */
if (status != PERR_CTRLFILE && status != PERR_DEFERRED && !savefile)
(void) zunlink(file); /* SILENT! */
if (e->e_fp)
fclose(e->e_fp);
tfree(MEM_TEMP);
stickymem = oval;
#ifdef XMEM
mal_contents(stdout);
#endif /* XMEM */
UNGCPRO2;
return status;
}
/*
* Read, store, and parse the message control information (envelope + header).
*/
/* Live code, 'octothorp' eliminated (obsolete thing..) */
int
makeLetter(e)
register struct envelope *e;
{
register int i;
register char *cp;
struct header *h;
int n, inheader;
struct header *ph, *nh;
e->e_eHeaders = 0;
e->e_headers = 0;
e->e_hdrOffset = 0;
e->e_msgOffset = 0;
e->e_nowtime = now = time(NULL);
if (efstat(FILENO(e->e_fp), &(e->e_statbuf)) < 0) {
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
e->e_statbuf.st_blksize = 0;
#endif /* !HAVE_STRUCT_STAT_ST_BLKSIZE */
e->e_statbuf.st_mtime = e->e_nowtime;
}
e->e_localtime = *(localtime(&(e->e_statbuf.st_mtime)));
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
initzline((int)e->e_statbuf.st_blksize);
#else /* !HAVE_STRUCT_STAT_ST_BLKSIZE */
initzline(4096);
#endif /* !HAVE_STRUCT_STAT_ST_BLKSIZE */
taspoolid(e->e_spoolid, e->e_statbuf.st_mtime, (long)e->e_statbuf.st_ino);
inheader = 0; /* 0: envelope, 1: RFC-822 */
/* Line of length 1 is LIKELY just "\n" */
while ((n = zgetline(e->e_fp)) > 0) {
/* We do kludgy processing things in case the input
does have a CRLF at the end of the line.. */
if (n == 1 && zlinebuf[0] == '\n') {
/* Separator in between RFC 822 headers, and body */
break;
}
if (n > 1 &&
zlinebuf[n-2] == '\r' &&
zlinebuf[n-1] == '\n') {
--n;
zlinebuf[n-1] = '\n';
if (n <= 1) break; /* Void line */
}
/* Ok, now we can proceed with the original agenda.. */
i = hdr_status(zlinebuf, zlinebuf, n, 0);
if (i > 0) { /* a real message header */
/* record start of headers for posterity */
if (!inheader)
e->e_hdrOffset = zlineoffset(e->e_fp)-n;
/* cons up a new one at top of header list */
h = makeHeader(spt_headers, zlinebuf, i);
h->h_next = e->e_headers;
e->e_headers = h;
h->h_lines = makeToken(zlinebuf+i+1, n-i-2);
h->h_lines->t_type = Line;
++inheader;
} else if (i == 0) { /* a continuation line */
if (inheader && zlinebuf[0] == ':') {
optsave(FYI_ILLHEADER, e);
/* cons up a new one at top of header list */
h = makeHeader(spt_headers,"X-Null-Field",12);
h->h_next = e->e_headers;
e->e_headers = h;
h->h_lines = makeToken(zlinebuf+i+1, n-i-2);
h->h_lines->t_type = Line;
} else if (inheader && n > 1) {
/* append to the header we just saw */
token822 *t;
if (e->e_headers == NULL) {
/* Wow, continuation without previous header! */
/* It must be body.. */
repos_zgetline(e->e_fp,
zlineoffset(e->e_fp) - n);
break;
}
t = e->e_headers->h_lines;
while (t->t_next)
t = t->t_next;
t->t_next = makeToken(zlinebuf, n-1);
t->t_next->t_type = Line;
} else {
return PERR_BADCONTINUATION;
}
} else if (!inheader /* envelope information */
&& (*(cp=zlinebuf-i) == ' ' || *cp == '\t'
|| *cp == '\n')) {
HeaderSemantics osem;
/* cons up a new one at top of envelope header list */
h = makeHeader(spt_eheaders, zlinebuf, -i);
h->h_next = e->e_eHeaders;
e->e_eHeaders = h;
h->h_lines = makeToken(zlinebuf-i+1, n > 1-i ? n+i-2: 0);
h->h_lines->t_type = Line;
switch (h->h_descriptor->class) {
case normal:
case Resent:
/* error */
fprintf(stderr,
"%s: unknown envelope header (class %d): ",
progname, h->h_descriptor->class);
fwrite((char *)zlinebuf, sizeof (char), -i, stderr);
putc('\n', stderr);
h->h_contents = hdr_scanparse(e, h, 0, 0);
h->h_stamp = hdr_type(h);
return PERR_BADSUBMIT;
/* break; */
case eFrom: /* pity to break the elegance */
osem = h->h_descriptor->semantics;
h->h_descriptor->semantics = Mailbox;
h->h_contents = hdr_scanparse(e, h, 0, 0);
h->h_stamp = hdr_type(h);
h->h_descriptor->semantics = osem;
break;
case eEnvEnd: /* more elegance breaks */
inheader = 1;
/* record start of headers for posterity */
e->e_hdrOffset = zlineoffset(e->e_fp);
e->e_eHeaders = h->h_next;
break;
default: /* a real envelope header */
h->h_contents = hdr_scanparse(e, h, 0, 0);
h->h_stamp = hdr_type(h);
break;
}
} else {
/* Expected RFC-822 header, and got something else.
We play as if it was the end of the headers, and
start of the body. Keep the first faulty input
around by reseeking into its start. */
repos_zgetline(e->e_fp, zlineoffset(e->e_fp) - n);
break;
}
}
/* reverse the list of headers so we keep them in the original order */
for (ph = NULL, h = e->e_eHeaders; h != NULL; h = nh) {
nh = h->h_next;
h->h_next = ph;
if (h->h_descriptor->semantics == nilHeaderSemantics
|| !hdr_nilp(h))
ph = h;
}
e->e_eHeaders = ph;
/* parse the message headers -- we already took care of envelope info */
for (ph = NULL, h = e->e_headers; h != NULL; h = nh) {
nh = h->h_next;
h->h_next = ph;
if (h->h_descriptor) {
h->h_contents = hdr_scanparse(e, h, 0, 0);
h->h_stamp = hdr_type(h);
if (!hdr_nilp(h)) /* excise null-valued headers */
ph = h;
} else
ph = h;
}
e->e_headers = ph;
/* record the start of the message body for posterity */
e->e_msgOffset = zlineoffset(e->e_fp);
return PERR_OK;
}
void
dumpInfo(e)
struct envelope *e;
{
printf("Message header starts at byte %ld.\n", e->e_hdrOffset);
printf("Message body starts at byte %ld.\n", e->e_msgOffset);
printf("ENVELOPE:\n");
dumpHeaders(e->e_eHeaders);
printf("HEADERS:\n");
dumpHeaders(e->e_headers);
}
void
dumpHeaders(h)
struct header *h;
{
for (; h != NULL; h = h->h_next)
dumpHeader(h);
}
void
dumpHeader(h)
struct header *h;
{
token822 *t;
struct address *a;
struct addr *p;
if (h->h_stamp == BadHeader)
hdr_errprint(NULL, h, stdout, "header");
printf("%s\n", h->h_pname);
if (h->h_descriptor == NULL)
return;
switch (h->h_descriptor->semantics) {
case DateTime:
printf("\tUNIX time %ld, local time %s",
(long)h->h_contents.d, ctime(&(h->h_contents.d)));
break;
case nilHeaderSemantics:
for (t = h->h_lines; t != NULL; t = t->t_next)
printf("\t%s\n", formatToken(t));
for (t = h->h_contents.t; t != NULL; t = t->t_next)
printf("\t%s\n", formatToken(t));
break;
case Received:
if (h->h_contents.r == NULL)
break;
if ((a = h->h_contents.r->r_from) != NULL) {
printf("\tFrom:\n");
for (p = a->a_tokens; p != NULL; p=p->p_next) {
printf("\t%s:\n", formatAddr(p->p_type));
for (t = p->p_tokens; t != NULL; t = t->t_next)
printf("\t\t%s\n", formatToken(t));
}
}
if ((a = h->h_contents.r->r_by) != NULL) {
printf("\tBy:\n");
for (p = a->a_tokens; p != NULL; p=p->p_next) {
printf("\t%s:\n", formatAddr(p->p_type));
for (t = p->p_tokens; t != NULL; t = t->t_next)
printf("\t\t%s\n", formatToken(t));
}
}
if ((t = h->h_contents.r->r_via) != NULL)
printf("\tVia: %s\n", formatToken(t));
for (t = h->h_contents.r->r_with; t != NULL; t = t->t_next)
printf("\tWith: %s\n", formatToken(t));
if ((t = h->h_contents.r->r_convert) != NULL)
printf("\tConvert: %s\n", formatToken(t));
printf("\tUNIX time %ld, local time %s",
(long) h->h_contents.r->r_time,
ctime(&(h->h_contents.r->r_time)));
break;
default:
for (a = h->h_contents.a; a != NULL; a = a->a_next) {
for (p = a->a_tokens; p != NULL; p=p->p_next) {
printf("\t%s:\n", formatAddr(p->p_type));
for (t = p->p_tokens; t != NULL; t = t->t_next)
printf("\t\t%s\n", formatToken(t));
}
printf("--- end of address ---\n");
}
break;
}
}
/*
* The following two variables are set so header address rewriting functions
* can find out whether they are dealing with a sender or recipient address.
* The variables are accessed through C coded config file functions.
*/
int isSenderAddr;
int isRecpntAddr;
/*
* Routing can sometimes want to have different view of the world when
* the message is _internally_ tagged with an 'errormsg' envelope header.
*/
int isErrorMsg;
int isErrChannel;
#define FindEnvelope(X) \
for (h = e->e_eHeaders; h != NULL; h = h->h_next) \
if (h->h_descriptor->class == X) \
break;
#define FindEnvelopeLast(X) \
{ struct header *__h_1 = NULL; \
for (h = e->e_eHeaders; h != NULL; h = h->h_next) \
if (h->h_descriptor->class == X) \
__h_1 = h; \
h = __h_1; \
}
#define RESENT_SIZE (sizeof "resent-" - 1)
#define FindHeader(X,RESENT) \
for (h = e->e_headers; h != NULL; h = h->h_next) \
if ((RESENT ? (h->h_descriptor->class == Resent) : \
(h->h_descriptor->class != Resent) ) \
&& h->h_descriptor->hdr_name != NULL \
&& STREQ(h->h_descriptor->hdr_name+(RESENT?RESENT_SIZE:0), X))\
break;
/* we insert the new header just before this point */
#define InsertHeader(IH, EXPR) \
{ for (ph = e->e_headers; ph != NULL; ph = ph->h_next) \
if (ph->h_next == IH) \
break; \
nh = EXPR; \
if (ph) { \
nh->h_next = IH; \
ph->h_next = nh; \
} else { \
nh->h_next = e->e_headers; \
e->e_headers = nh; \
} }
/* Make it possible to do real override of Errors-To: with $(listaddress ...) */
char *errors_to = NULL;
/* Save the message in a directory for the postmaster to view it later */
void
squirrel(e, keyw, text)
struct envelope *e;
const char *keyw, *text;
{
char *path;
#ifndef USE_ALLOCA
path = (char*)emalloc(5+sizeof(POSTMANDIR)+10+strlen(keyw));
#else
path = (char*)alloca(5+sizeof(POSTMANDIR)+10+strlen(keyw));
#endif
sprintf(path, "../%s/%d.%s",
POSTMANDIR, (int)e->e_statbuf.st_ino, keyw);
/* XX: We make a copy by link()ing file to two dirs, some
systems (Andrew-FS in mind) can't do it! Copying is
ok in their case.. */
/* If linking to /postman/ fails, link it to router dir with
two dots at the begin of the name + keyword at the end */
if (elink(e->e_file, path) < 0) {
sprintf(path, "..%d.%s", (int)e->e_statbuf.st_ino, keyw);
elink(e->e_file, path);
}
/* The successfull squirrel MUST NOT delete the original message! */
fprintf(stderr, "squirrel: %d.%s saved for inspection: %s\n",
(int) e->e_statbuf.st_ino, keyw, text);
zsyslog((LOG_ERR, "%d.%s saved for inspection: %s\n",
(int) e->e_statbuf.st_ino, keyw, text));
#ifndef USE_ALLOCA
free(path);
#endif
}
struct header *
erraddress(e)
struct envelope *e;
{
register struct header *h;
struct header *best;
int minval;
char **cpp;
const char *cp;
best = NULL;
minval = 10000;
for (h = e->e_headers; h != NULL; h = h->h_next) {
if ((e->e_resent != 0 && h->h_descriptor->class != Resent)
|| h->h_descriptor->hdr_name == NULL
|| h->h_stamp == BadHeader)
continue;
cp = h->h_descriptor->hdr_name + e->e_resent;
/* char *err_prio_list[] = { "sender", "errors-to", 0 }; */
for (cpp = err_prio_list; *cpp != NULL; ++cpp) {
if (CISTREQ(*cpp, cp)
&& (cpp - err_prio_list) < minval) {
best = h;
minval = cpp - err_prio_list;
}
}
}
if (best == NULL) {
FindEnvelope(eFrom); /* should never be NULL */
/* but might still be problematic */
if (h && h->h_stamp == BadHeader)
FindHeader("from",e->e_resent);
} else
h = best;
if (h && h->h_stamp == BadHeader)
h = NULL;
if (h == NULL) {
/* everything else failed, so use the owner of the file */
if (!e->e_trusted)
/*ROUTER*/ h = mkSender(e, uidpwnam(e->e_statbuf.st_uid), 1);
#if 0
else
/*ROUTER*/ h = mkSender(e, POSTMASTER, 1);
#endif
}
return h;
}
#if 0 /* DEAD CODE */
/* Pick recipient address from the input line.
EXTREMELY Simple minded parsing.. */
static void pick_env_addr __((char *buf, FILE *mfp));
static void pick_env_addr(buf,mfp)
char *buf;
FILE *mfp;
{
char *s = buf;
while (*s != 0 && *s != ' ' && *s != '\t' && *s != ':') ++s;
if (*s != ':') return; /* BAD INPUT! */
buf = ++s; /* We have skipped the initial header.. */
s = strchr(buf,'\n');
if (s) *s = 0;
s = strchr(buf,'<');
if (s) {
/* Cc: The Postoffice managers <postoffice> */
buf = ++s;
s = strrchr(buf,'>');
if (s) *s = 0;
else return; /* No trailing '>' ? BAD BAD! */
fprintf(mfp,"to <%s>\n",buf);
} else {
/* Cc: some-address */
fprintf(mfp,"to <%s>\n",buf);
}
}
#endif /* DEAD CODE */
static int nullhost __((conscell *cs));
static int
nullhost(cs)
conscell *cs;
{
const char *s = cs->cstring;
return ( !s || *s == '\0' || STREQ(s, "-") );
/* actually we should also check for localhostness, but lets not
get carried away... */
}
/* Send the message back to originator with user-friendly chastizing errors */
static void
reject(e, msgfile)
struct envelope *e;
const char *msgfile;
{
register struct header *h;
char *path, lastCh, nextToLastCh;
const char *c_cp;
int n;
FILE *mfp, *fp;
char buf[BUFSIZ], vbuf[BUFSIZ];
fprintf(stderr, "Rejecting because of %s\n", msgfile);
h = erraddress(e);
if (h == NULL) {
squirrel(e, "noerraddr", "No one to return an error to!");
return;
}
/*
* turn all (possibly more than one) the addresses in whatever h
* is pointing at, into recipient addresses.
*/
#ifndef USE_ALLOCA
path = (char*)emalloc(3+strlen(mailshare)+strlen(FORMSDIR)+strlen(msgfile));
#else
path = (char*)alloca(3+strlen(mailshare)+strlen(FORMSDIR)+strlen(msgfile));
#endif
sprintf(path, "%s/%s/%s", mailshare, FORMSDIR, msgfile);
fp = fopen(path, "r");
if (fp == NULL) {
perror(path);
squirrel(e, "norejform", "Couldn't open reject form file");
#ifndef USE_ALLOCA
free(path);
#endif
return;
}
#ifndef USE_ALLOCA
free(path);
#endif
setvbuf(fp, vbuf, _IOFBF, sizeof vbuf);
fseek(fp,(off_t)0,0);
runastrusteduser();
mfp = mail_open(MSG_RFC822);
runasrootuser();
if (mfp == NULL) {
squirrel(e, "mailcreatefail", "Couldn't open reject message file");
fclose(fp);
return;
}
/* Mark this as an error message, but don't add the EXPLICITE
envelope and RFC-822 header separator ("env-end") ! */
fputs("channel error\n", mfp);
fputs("errormsg\n", mfp);
/* fputs("env-end\n", mfp); */
/* who is it from? */
c_cp = h->h_pname;
h->h_pname = "To";
hdr_print(h, mfp);
h->h_pname = c_cp;
lastCh = nextToLastCh = '\0';
while (fgets(buf,sizeof(buf),fp)) {
int rc;
n = strlen(buf);
if (strncmp("HDR",buf,3)==0 ||
strncmp("ADR",buf,3)==0 ||
strncmp("SUB",buf,3)==0) {
rc = fputs(buf+4,mfp);
n -= 4;
} else
rc = fputs(buf,mfp);
if (n > 1) {
nextToLastCh = buf[n-2];
lastCh = buf[n-1];
} else {
nextToLastCh = lastCh;
lastCh = buf[0];
}
}
fclose(fp);
if (lastCh == '\n') {
if (nextToLastCh != '\n')
putc('\n', mfp);
} else {
putc('\n', mfp);
putc('\n', mfp);
}
/* print the headers that had errors in them, with annotations */
for (h = e->e_eHeaders; h != NULL; h = h->h_next)
if (h->h_stamp == BadHeader) {
hdr_errprint(e, h, mfp, "envelope");
putc('\n', mfp);
}
for (h = e->e_headers; h != NULL; h = h->h_next)
if (h->h_stamp == BadHeader) {
hdr_errprint(e, h, mfp, "header");
putc('\n', mfp);
}
fprintf(mfp, "\nThe entire original message file follows.\n");
fprintf(mfp, "\n-----------------------------------------\n");
rewind(e->e_fp);
while ((n = fread(buf, 1, sizeof buf, e->e_fp)) > 0) {
if (fwrite(buf, 1, n, mfp) != n) {
squirrel(e, "mailcreafail", "Couldn't include rejected input message");
mail_abort(mfp);
return;
}
}
if (mail_close(mfp) < 0) {
squirrel(e, "mailcreafail", "Couldn't close reject message file");
mail_abort(mfp);
}
}
/* Save a message file away because something went wrong during routing */
void
defer(e, why)
struct envelope *e;
const char *why;
{
int i;
char *path, *s;
struct stat stbuf;
if (e->e_fp && files_gid >= 0) {
fchown(FILENO(e->e_fp), -1, files_gid);
fchmod(FILENO(e->e_fp),
(0440|e->e_statbuf.st_mode) & 0660);
fstat(fileno(e->e_fp),&stbuf);
}
if (stbuf.st_ino > 0) {
#ifdef USE_ALLOCA
path = (char*)alloca(5+sizeof(DEFERREDDIR)+6+strlen(why));
#else
path = (char*)emalloc(5+sizeof(DEFERREDDIR)+6+strlen(why));
#endif
sprintf(path,"../%s/%d.%s", DEFERREDDIR, (int)stbuf.st_ino, why);
} else {
#ifdef USE_ALLOCA
path = (char*)alloca(5+sizeof(DEFERREDDIR)+strlen(e->e_file)+2+strlen(why));
#else
path = (char*)emalloc(5+sizeof(DEFERREDDIR)+strlen(e->e_file)+2+strlen(why));
#endif
sprintf(path, "../%s/%s.%s", DEFERREDDIR, e->e_file, why);
}
s = path;
while ((s = strchr(s, ' '))) /* remap blanks to '-':es.. */
*s = '-';
#ifndef HAVE_RENAME
zunlink(path);
#endif
/* try renameing every few seconds for a minute */
for (i = 0; erename(e->e_file, path) < 0 && i < 10 && errno != ENOENT;++i)
sleep(6);
if (i >= 10) {
zsyslog((LOG_ALERT, "cannot defer %s (%m)", e->e_file));
fprintf(stderr, "cannot defer %s\n", e->e_file);
/* XX: make sure file is not zunlink()ed in main() */
} else {
zsyslog((LOG_NOTICE, "%s; %sing deferred", e->e_file,
(deferit || deferuid) ? "schedul" : "rout"));
}
#ifndef USE_ALLOCA
free(path);
#endif
}
/*
* Construct a header line that contains a given sender name.
*/
struct header *
mkSender(e, name, flag)
struct envelope *e;
const char *name;
int flag;
{
register struct header *sh, *h;
struct addr *pp, *qp, **ppp;
conscell *l;
int didbracket;
GCVARS1;
l = NULL;
GCPRO1(l);
sh = (struct header *)tmalloc(sizeof (struct header));
sh->h_pname = e->e_resent ? "Resent-From" : "From";
sh->h_descriptor = senderDesc();
sh->h_stamp = newHeader;
sh->h_lines = 0;
sh->h_next = 0;
sh->h_contents.a = (struct address *)tmalloc(sizeof (struct address));
sh->h_contents.a->a_tokens = NULL;
sh->h_contents.a->a_next = NULL;
sh->h_contents.a->a_stamp = newAddress;
sh->h_contents.a->a_dsn = NULL;
ppp = &sh->h_contents.a->a_tokens;
didbracket = 0;
FindEnvelope(eFullname);
if (h && h->h_contents.a) {
sh->h_contents.a->a_tokens = h->h_contents.a->a_tokens;
/* 'Full Name' */
for (pp = h->h_contents.a->a_tokens;
pp != NULL; pp = pp->p_next) {
qp = (struct addr *)tmalloc(sizeof (struct addr));
*qp = *pp;
*ppp = qp;
ppp = &qp->p_next;
if (pp->p_next == NULL)
break;
}
} else {
spkey_t spk = symbol_lookup_db(name, spt_fullnamemap->symbols);
struct spblk *spl = NULL;
if (spk)
spl = sp_lookup(spk, spt_fullnamemap);
if (spk && spl && spl->data) {
*ppp = (struct addr *)tmalloc(sizeof (struct addr));
pp = *ppp;
ppp = &pp->p_next;
pp->p_tokens = makeToken((char *)spl->data,
strlen((char *)spl->data));
pp->p_tokens->t_type = Word;
pp->p_type = aPhrase;
}
}
if (sh->h_contents.a->a_tokens) { /* 'Full Name <' */
*ppp = (struct addr *)tmalloc(sizeof (struct addr));
pp = *ppp;
ppp = &pp->p_next;
pp->p_tokens = makeToken("<", 1);
didbracket = 1;
pp->p_tokens->t_type = Special;
pp->p_type = aSpecial;
}
FindEnvelope(ePrettyLogin);
if (h && h->h_contents.a == NULL)
h = NULL;
if (h && !flag && !e->e_trusted) {
/* make sure the pretty login is valid. see thesender() */
/*ROUTER*/ l = router(h->h_contents.a, e->e_statbuf.st_uid, "sender", NULL);
if (l) {
l = pickaddress(l);
flag = (QUSER(l) == NULL || !nullhost(QHOST(l)));
if (!flag)
flag = !CISTREQ(QUSER(l)->cstring, name);
} else
flag = 1;
} else
l = NULL;
if (h && (!flag || e->e_trusted)) {
/* 'Full Name <Pretty.Login' */
if (l) {
if (e->e_trusted)
e->e_from_trusted = l;
else
e->e_from_resolved = l;
}
for (pp = h->h_contents.a->a_tokens;
pp != NULL; pp = pp->p_next) {
qp = (struct addr *)tmalloc(sizeof (struct addr));
*qp = *pp;
*ppp = qp;
ppp = &qp->p_next;
if (pp->p_next == NULL)
break;
}
} else {
/* 'Full Name <login' */
*ppp = (struct addr *)tmalloc(sizeof (struct addr));
pp = *ppp;
ppp = &pp->p_next;
pp->p_tokens = makeToken(name, strlen(name));
pp->p_tokens->t_type = Atom;
pp->p_type = anAddress;
l = NULL;
}
if (didbracket) {
/* 'Full Name <Pretty.Login>' */
*ppp = (struct addr *)tmalloc(sizeof (struct addr));
pp = *ppp;
ppp = &pp->p_next;
pp->p_tokens = makeToken(">", 1);
pp->p_tokens->t_type = Special;
pp->p_type = aSpecial;
}
*ppp = NULL;
if (l == NULL && !e->e_trusted) {
/*ROUTER*/ l = router(sh->h_contents.a, e->e_statbuf.st_uid, "sender", NULL);
if (l)
e->e_from_trusted = pickaddress(l);
}
#ifdef notdef
/* X: sprintf(buf, "%s <%s>", fullname, name); more or less */
sh->h_lines = makeToken(name, strlen((char *)name));
sh->h_lines->t_type = Line;
sh->h_contents = hdr_scanparse(e, sh, 0, 0);
#endif
/* X: optimize: save this in a hashtable somewhere */
UNGCPRO1;
return sh;
}
/*
* Construct a trace header line.
*/
static const struct headerinfo traceDesc =
{ "received", Received, nilUserType, normal };
struct header *
mkTrace(e, rcvdhdr)
struct envelope *e;
struct header *rcvdhdr;
{
register struct header *h, *th;
struct addr *na;
struct address *ap;
th = (struct header *)tmalloc(sizeof (struct header));
th->h_pname = "Received";
th->h_descriptor = (struct headerinfo *)&traceDesc;
th->h_stamp = newHeader;
th->h_next = 0;
th->h_contents.r = (struct received *)tmalloc(sizeof (struct received));
th->h_lines = NULL;
h = rcvdhdr; /* FindEnvelopeLast(eRcvdFrom); */
/* from */
if (h)
th->h_contents.r->r_from = h->h_contents.a;
else
th->h_contents.r->r_from = NULL;
/* by */
if (myhostname) {
na = (struct addr *)tmalloc(sizeof (struct addr));
/* prepend now -- reverse later */
na->p_tokens = makeToken(myhostname, strlen(myhostname));
na->p_next = NULL;
na->p_type = anAddress;
ap = (struct address *)tmalloc(sizeof (struct address));
ap->a_tokens = na;
ap->a_next = NULL;
/* we don't need to fill in the other structure elements */
/* but, just in case... */
ap->a_stamp = newAddress;
ap->a_dsn = NULL;
th->h_contents.r->r_by = ap;
} else
th->h_contents.r->r_by = NULL;
/* via */
FindEnvelope(eVia);
if (h && h->h_contents.a && h->h_contents.a->a_tokens) {
th->h_contents.r->r_via = h->h_contents.a->a_tokens->p_tokens;
} else
th->h_contents.r->r_via = NULL;
/* with */
FindEnvelope(eWith);
if (h && h->h_contents.a && h->h_contents.a->a_tokens) {
th->h_contents.r->r_with = h->h_contents.a->a_tokens->p_tokens;
} else
th->h_contents.r->r_with = NULL;
/* id */
na = (struct addr *)tmalloc(sizeof (struct addr));
na->p_tokens = makeToken(e->e_spoolid, strlen(e->e_spoolid));
na->p_next = NULL;
na->p_type = anAddress; /* really a spool-id */
ap = (struct address *)tmalloc(sizeof (struct address));
ap->a_tokens = na;
ap->a_next = NULL;
ap->a_dsn = NULL;
ap->a_stamp = newAddress;
th->h_contents.r->r_id = ap;
/* for */
th->h_contents.r->r_for = NULL;
/* time */
th->h_contents.r->r_time = e->e_statbuf.st_mtime;
/* looks like:sprintf(buf, "from %s by %s via %s with %s id %s; %s"); */
th->h_contents.r->r_convert = NULL;
return th;
}
struct rwmatrix {
struct rwmatrix *next;
struct rwmatrix *down;
conscell *info; /* rewrite, sender, or recipient list info */
conscell *errto;
union {
int number; /* XOR address id number for recipients */
struct header *h; /* for rewritings */
} urw;
};
static struct rwmatrix * rwalloc __((struct rwmatrix **));
static struct rwmatrix *
rwalloc(rwpp)
struct rwmatrix **rwpp;
{
struct rwmatrix *p;
p = (struct rwmatrix *)tmalloc(sizeof (struct rwmatrix));
p->down = NULL;
p->errto = NULL;
p->urw.number = 0; /* unused but why not */
if (*rwpp)
p->next = *rwpp;
else
p->next = NULL;
*rwpp = p;
return p;
}
conscell *
pickaddress(l)
conscell *l;
{
conscell *la, *lx, *p;
/*
* Given an AND-XOR tree returned from the router, pick one address
* quad to become *the* resolved address.
*/
for (p = NULL, la = car(l); la != NULL && LIST(la) ; la = cdr(la)) {
/* AND addresses; i.e. exploded address list or one address */
for (lx = car(la); lx != NULL && LIST(lx) ; lx = cdr(lx)) {
if (STRING(cdar(lx)) && nullhost(cdar(lx))) {
p = lx;
break;
}
}
}
if (p == NULL)
p = caar(l);
if (!LIST(p))
return NULL;
return car(p);
}
int
thesender(e, a)
struct envelope *e;
struct address *a;
{
conscell *l; /* Var life ends after return.. no GC protection */
/*ROUTER*/ l = router(a, e->e_statbuf.st_uid, "sender", NULL);
if (l == NULL)
return 0;
e->e_from_resolved = pickaddress(l);
if (QUSER(e->e_from_resolved)->cstring == NULL ||
!nullhost(QHOST(e->e_from_resolved)))
return 0;
/*
* We assume here, that local mailbox id's correspond to user
* login names. If this is not the case, trp->user needs to be
* canonicalized at this point... the problem is that the algorithm
* must be the same here and in the local mail delivery program
* which is outside the router. Maybe that should be a C library
* routine, but for now, ignore such complications. This check is
* only done if the message file wasn't created by a trusted user,
* so in practise this is less of a problem than it might appear to be.
*/
return CISTREQ(QUSER(e->e_from_resolved)->cstring,
uidpwnam(e->e_statbuf.st_uid));
}
extern conscell *rwmappend __((conscell *, conscell *, conscell *));
conscell *rwmappend(rwmroot,info,errtop)
conscell *rwmroot, *info, *errtop;
{
conscell *p = NULL;
GCVARS4;
GCPRO4(p, rwmroot, info, errtop);
if (info) {
p = ncons(info);
cdr(p) = rwmroot;
rwmroot = p;
}
if (errtop) {
p = ncons(info);
cdr(p) = rwmroot;
rwmroot = p;
}
UNGCPRO4;
return rwmroot;
}
/*
* The sequencer takes care of doing the right things with the right headers
* in the right order. It implements the semantics of message processing.
*/
int ReceivedCount = 0;
static int schedulersubdirhash = -1;
int
sequencer(e, file)
struct envelope *e;
const char *file;
{
struct header *h, *ph, *nh = NULL, *oh, *msgidh, **hp;
struct header *rcvdhdr;
struct address *a, *ap;
struct addr *p = NULL;
char *ofpname, *path, *qpath;
const char *pfile;
conscell *l, *routed_addresses, *sender, *to;
conscell *rwmchain;
struct rwmatrix *rwhead, *nsp, *rwp = NULL, *rcp = NULL;
token822 *t = NULL;
int idnumber = 0, nxor = 0, i, slen;
int def_uid, header_error, perr;
FILE *ofp, *vfp;
int ofperrors = 0;
char vbuf[2048], verbosefile[1024];
const char *envid;
const char *notaryret;
int onrcpts = 0;
int inrcpts = 0;
const char *fromaddr = "?from?";
const char *msgidstr = "?msgid?";
const char *smtprelay = NULL;
const char *senderstr;
char subdirhash[8];
struct notary *DSN;
time_t start_now;
struct stat stbuf;
long infilesize_kb, taskfilesize_kb;
GCVARS5;
time(&start_now);
if (schedulersubdirhash < 0) {
const char *s = getzenv("SCHEDULERDIRHASH");
if (s && ((s[0] == '1' || s[0] == '2') && s[1] == 0))
schedulersubdirhash = s[0] - '0';
else
schedulersubdirhash = 0;
}
pfile = file;
if ('A' <= *pfile && *pfile <= 'Z') {
++pfile;
if (*pfile == '/') ++pfile;
if ('A' <= *pfile && *pfile <= 'Z') ++pfile;
if (*pfile == '/') ++pfile;
}
if (schedulersubdirhash) {
long ino = e->e_statbuf.st_ino;
if (schedulersubdirhash > 1) {
int h1 = (ino / 26) % 26;
int h2 = ino % 26;
sprintf(subdirhash, "%c/%c/", h1 + 'A', h2 + 'A');
} else {
int h2 = ino % 26;
sprintf(subdirhash, "%c/", h2 + 'A');
}
} else
*subdirhash = 0;
deferuid = 0;
errors_to = NULL; /* to be gotten rid off.. */
if (e == NULL) {
/* No envelope ??? */
return PERR_OK;
}
dprintf("Sender authentication\n");
e->e_trusted = isgoodguy(e->e_statbuf.st_uid);
dprintf("Sender is%s trusted\n", e->e_trusted ? "" : "n't");
if (deferuid)
return PERR_DEFERRED;
FindEnvelopeLast(eRcvdFrom);
rcvdhdr = h;
if (e->e_trusted) {
if (rcvdhdr)
smtprelay = rcvdhdr->h_lines->t_pname;
else {
const char *s = uidpwnam(e->e_statbuf.st_uid);
char *sr = strnsave("",strlen(s)+20);
sprintf(sr, "%s@localhost", s);
smtprelay = sr;
}
} else {
const char *s, *ps;
char *ts;
int totlen;
if (rcvdhdr) {
/* Ok, we don't trust it! In fact we might overrule the data
that the message writer coded in... */
dprintf("Message has 'rcvdfrom' envelope header, but we don't trust it!\n");
s = uidpwnam(e->e_statbuf.st_uid);
ps = "";
totlen = 10 + strlen(s) + 60;
if (rcvdhdr) {
ps = rcvdhdr->h_lines->t_pname;
totlen += strlen(ps) + 10;
}
/* ts = strnsave("", totlen); */
ts = tmalloc(totlen); /* Alloc the space */
if (rcvdhdr) {
sprintf(ts, "rcvdfrom STDIN (from localhost user: '%s' uid#%ld fake: %s)",
s, (long) e->e_statbuf.st_uid, rcvdhdr->h_lines->t_pname);
} else
sprintf(ts, "rcvdfrom STDIN (from localhost user: '%s' uid#%ld)",
s, (long) e->e_statbuf.st_uid);
} else {
/* No "rcvdfrom" envelope header */
s = uidpwnam(e->e_statbuf.st_uid);
totlen = 60 + strlen(s);
ts = tmalloc(totlen);
sprintf(ts, "rcvdfrom STDIN (from localhost user: '%s', uid#%ld)",
s, (long) e->e_statbuf.st_uid);
}
rcvdhdr = makeHeader(spt_eheaders, ts, 8);
rcvdhdr->h_next = e->e_eHeaders;
e->e_eHeaders = rcvdhdr;
rcvdhdr->h_lines = makeToken(ts+9, strlen(ts+9));
rcvdhdr->h_lines->t_type = Line;
rcvdhdr->h_contents = hdr_scanparse(e, rcvdhdr, 0, 0);
rcvdhdr->h_stamp = hdr_type(rcvdhdr);
smtprelay = ts+9;
}
if (deferuid)
return PERR_DEFERRED;
dprintf("Parse envelope and message header\n");
e->e_messageid = NULL;
perr = 0;
if (myhostname) { /* appease honeyman et al groupies */
dprintf("Stamp it with a trace header\n");
h = mkTrace(e, rcvdhdr);
if (h) {
h->h_next = e->e_headers;
e->e_headers = h;
}
}
/* gross loop checking */
ReceivedCount = 0;
for (h = e->e_headers, i = 0; h != NULL; h = h->h_next) {
if (h->h_descriptor->semantics == Received
|| (h->h_descriptor->semantics == nilHeaderSemantics
&& CISTREQ(h->h_pname, "Received"))) {
if (++ReceivedCount >= maxReceived) {
if (ReceivedCount == maxReceived)
fprintf(stderr,
"%s: looping message, rejected!\n",
progname);
perr = PERR_LOOP;
}
}
}
if (deferuid)
return PERR_DEFERRED;
dprintf("Determine if message is a Resent-* type thing\n");
e->e_resent = 0;
for (h = e->e_headers; h != NULL; h = h->h_next)
if (h->h_descriptor->class == Resent) {
e->e_resent = 1;
break;
}
dprintf("It is%s...\n", e->e_resent ? "" : "n't");
dprintf("Generate an error message if an error occurred in parsing\n");
for (h = e->e_eHeaders; h != NULL; h = h->h_next)
if (h->h_stamp == BadHeader)
break;
if (h && h->h_stamp == BadHeader) {
/*
* There's an error in the envelope; this implies a system
* problem that must be brought to the attention of the
* postmaster ASAP. Hopefully the postmaster will resubmit
* the message when the problem is fixed, so we don't tell
* the originator that anything went wrong.
*/
/* We give precedence to loop-trap error */
if (perr == 0)
perr = PERR_ENVELOPE;
}
e->e_from_trusted = makequad();
e->e_from_resolved = makequad();
if (myhostname) { /* we care about message id's */
dprintf("Make sure Message-Id exists, for loop control\n");
/* it may be message-id, or resent-message-id */
FindHeader("message-id",e->e_resent);
if (h == NULL) /* Ok, wasn't, how about resent-message-id ? */
FindHeader("message-id",!e->e_resent);
if (h == NULL) {
/* the time used must be the same as for trace header */
InsertHeader(h, mkMessageId(e, e->e_nowtime));
/* or: e->e_statbuf.st_mtime */
dprintf("We added one\n");
msgidh = nh;
} else
msgidh = h;
if (msgidh->h_contents.a && msgidh->h_contents.a->a_tokens)
e->e_messageid = saveAddress(msgidh->h_contents.a->a_tokens);
}
if (perr)
return perr;
/* put pertinent message information in the zsh environment */
setenvinfo(e);
FindEnvelope(eEnvid);
envid = NULL;
if (h && h->h_contents.a && h->h_contents.a->a_pname) {
envid = h->h_contents.a->a_pname;
}
FindEnvelope(eNotaryRet);
notaryret = NULL;
if (h && h->h_contents.a && h->h_contents.a->a_pname) {
notaryret = h->h_contents.a->a_pname;
}
rwmchain = l = routed_addresses = sender = to = NULL;
GCPRO5(l, routed_addresses, sender, to, rwmchain);
FindEnvelope(eFrom);
if (h == NULL && e->e_trusted) {
/* Perhaps 'channel error' ??? */
FindEnvelope(eChannel);
if (h) {
dprintf("A channel was specified\n");
if ((ap = h->h_contents.a) != NULL
&& (p = ap->a_tokens) != NULL
&& p->p_tokens != NULL) {
t = p->p_tokens;
slen = TOKENLEN(t);
l = cdr(QCHANNEL(e->e_from_trusted));
QCHANNEL(e->e_from_trusted) =
newstring(dupnstr(t->t_pname, slen), slen);
cdr(QCHANNEL(e->e_from_trusted)) = l;
} else {
/*
* No origination channel, or channel
* has no name, or ...
*/
optsave(FYI_NOCHANNEL, e);
}
}
}
if (deferuid) {
UNGCPRO5;
return PERR_DEFERRED;
}
if (h == NULL) {
dprintf("A sender was NOT specified in the envelope\n");
for (h = e->e_headers; h != NULL; h = h->h_next)
if (h->h_descriptor->user_type == Sender
&& (e->e_resent == 0
|| h->h_descriptor->class == Resent))
break;
if (h && e->e_trusted) {
dprintf("Use the Sender: or From: field from header\n");
h = copySender(e);
} else {
dprintf("Generate a sender based on owner of file\n");
/*ROUTER*/ h = mkSender(e, uidpwnam(e->e_statbuf.st_uid), 0);
}
if (h == NULL)
/*ROUTER*/ h = mkSender(e, POSTMASTER, 1);
/* assert h != NULL */
h->h_next = e->e_eHeaders;
e->e_eHeaders = h;
} else if (!e->e_trusted
|| ((ap = h->h_contents.a) != NULL &&
ap->a_tokens != NULL &&
ap->a_tokens->p_next == NULL &&
ap->a_tokens->p_type == anAddress &&
(t = ap->a_tokens->p_tokens) != NULL &&
t->t_next == NULL)) {
dprintf("A sender was specified in the envelope\n");
if (!e->e_trusted) {
dprintf("Replace the sender based on owner of file\n");
/*ROUTER*/ h = mkSender(e, uidpwnam(e->e_statbuf.st_uid), 0);
} else {
dprintf("Provide a full name for the originator\n");
/* ensure there is a fullnamemap entry */
login_to_uid(t->t_pname);
/*ROUTER*/ h = mkSender(e, t->t_pname, 0);
}
h->h_next = e->e_eHeaders;
e->e_eHeaders = h;
/*
* No need to delete other header since there can only
* be one sender in the message envelope and in other
* places in the code we will use the first one found.
*/
}
for (h = e->e_headers; h != NULL; h = h->h_next)
if (h->h_stamp == BadHeader)
break;
if (h && h->h_stamp == BadHeader) {
/*
* There's an error in the message header; we save the message
* for the future amusement of the postmaster, and also send
* back a nasty note to the originator.
*/
optsave(FYI_BADHEADER, e);
/* continue on with address rewriting - emit warning there */
/* return e; */
header_error = 1;
} else
header_error = 0;
dprintf("Originating channel determination\n");
def_uid = nobody;
isErrorMsg = 0;
isErrChannel = 0;
if (e->e_trusted) {
/* The sender uid is known */
/* Is it perhaps an ERROR MESSAGE ? */
FindEnvelope(eErrorMsg);
if (h) isErrorMsg = 1;
/* Does it have CHANNEL ? Is it "ERROR" ? */
FindEnvelope(eChannel);
if (h) {
dprintf("A channel was specified\n");
if ((ap = h->h_contents.a) != NULL
&& (p = ap->a_tokens) != NULL
&& p->p_tokens != NULL) {
t = p->p_tokens;
slen = TOKENLEN(t);
l = cdr(QCHANNEL(e->e_from_trusted));
QCHANNEL(e->e_from_trusted) =
newstring(dupnstr(t->t_pname, slen), slen);
cdr(QCHANNEL(e->e_from_trusted)) = l;
}
if (QCHANNEL(e->e_from_trusted)->cstring == NULL) {
/*
* the mailer is supposed to know about
* all valid channel identifiers. Gripe.
*/
optsave(FYI_NOCHANNEL, e);
}
if (iserrmessage(e))
isErrChannel = 1;
}
h = rcvdhdr; /* FindEnvelopeLast(eRcvdFrom); */
if (h && h->h_contents.a && h->h_contents.a->a_pname) {
/* a previous host was specified */
slen = strlen(h->h_contents.a->a_pname);
l = cdr(QHOST(e->e_from_trusted));
QHOST(e->e_from_trusted) =
newstring(dupnstr(h->h_contents.a->a_pname, slen),slen);
cdr(QHOST(e->e_from_trusted)) = l;
}
FindEnvelope(eUser);
if (h && h->h_contents.a && h->h_contents.a->a_pname) {
/* a previous user was specified */
slen = strlen(h->h_contents.a->a_pname);
l = cdr(QHOST(e->e_from_trusted));
QHOST(e->e_from_trusted) =
newstring(dupnstr(h->h_contents.a->a_pname, slen),slen);
cdr(QHOST(e->e_from_trusted)) = l;
}
if (QCHANNEL(e->e_from_trusted)->cstring == NULL
|| QHOST(e->e_from_trusted)->cstring == NULL
|| QUSER(e->e_from_trusted)->cstring == NULL) {
FindEnvelope(eFrom);
/* X: assert h != NULL */
if (h == NULL || h->h_stamp == BadHeader) {
/* XX: perhaps should be other way around? */
FindHeader("sender",e->e_resent);
if (h == NULL || h->h_stamp == BadHeader)
FindHeader("from",e->e_resent);
if (h == NULL || h->h_stamp == BadHeader)
FindHeader("sender",!e->e_resent);
if (h == NULL || h->h_stamp == BadHeader)
FindHeader("from",!e->e_resent);
if (h == NULL || h->h_stamp == BadHeader)
/* BAD input, must try to do something.. */
/*ROUTER*/ h = mkSender(e,uidpwnam(e->e_statbuf.st_uid),0);
}
if (h == NULL)
abort(); /* Failed to make sender header */
if (isErrChannel) {
char *ss = (char*)tmalloc(10);
strcpy(ss, "from <>");
h = makeHeader(spt_eheaders, ss, 4);
h->h_lines = makeToken(ss+5,2);
h->h_lines->t_type = Line;
h->h_contents = hdr_scanparse(e, h, 0, 0);
/* And now we bend the result beyond any
recognition.. */
h->h_contents.a->a_tokens->p_type = anAddress;
h->h_contents.a->a_tokens->p_tokens->t_len = 2;
h->h_contents.a->a_tokens->p_tokens->t_type = Atom;
h->h_contents.a->a_tokens->p_tokens->t_next = NULL;
h->h_stamp = hdr_type(h);
}
/* This conscell lifetime is limited, no GC protect. */
/*ROUTER*/ l = router(h->h_contents.a,
e->e_statbuf.st_uid, "sender", NULL);
if (l == NULL) {
/* From: <>, and no envelope 'from' .. */
/*ROUTER*/ h = mkSender(e,uidpwnam(e->e_statbuf.st_uid),0);
if (h == NULL)
abort(); /* Can't make Sender header ?? */
/*ROUTER*/ l = router(h->h_contents.a,
e->e_statbuf.st_uid, "sender", NULL);
}
if (l) {
/*
* In case the router returns several addresses,
* we pick one at random to use for sender priv
* determination.
*/
e->e_from_resolved = pickaddress(l);
}
}
if (nullhost(QHOST(e->e_from_resolved)) &&
nullhost(QHOST(e->e_from_trusted))) {
/* local user */
FindEnvelope(eExternal);
if (h || (e->e_statbuf.st_mode & 022)) {
optsave(FYI_BREAKIN, e);
} else if (QUSER(e->e_from_resolved)->cstring)
def_uid =
login_to_uid(QUSER(e->e_from_resolved)->cstring);
else if (QUSER(e->e_from_trusted)->cstring)
def_uid =
login_to_uid(QUSER(e->e_from_trusted)->cstring);
}
} else {
dprintf("We know sender is local and one of the peons\n");
def_uid = e->e_statbuf.st_uid;
FindHeader("from",e->e_resent);
nh = h;
FindHeader("sender",e->e_resent);
if (h && !nh) {
dprintf("A Sender w/o a From is bad; fixed\n");
h->h_descriptor = senderDesc();
set_pname(e, h, "From");
nh = h;
h = NULL;
}
if (h && h->h_contents.a) {
/* a Sender: was given */
/*ROUTER*/ if (!thesender(e, h->h_contents.a)) {
/* but it is fake, so correct it */
dprintf("The Sender: is not the sender\n");
set_pname(e, h, "Fake-Sender");
} else /* it is correct and we don't care about From: */
h = NULL;
} else if (nh && nh->h_contents.a) {
/* only a From: was given */
/*ROUTER*/ if (!thesender(e, nh->h_contents.a)) {
/* but it is fake, so add a Sender: */
dprintf("The From: is not the sender\n");
if (h) {
/* use our empty Sender: */
/*ROUTER*/ ph = mkSender(e,uidpwnam(e->e_statbuf.st_uid),0);
h->h_contents.a = ph->h_contents.a;
h = NULL;
} else
h = nh;
} else
h = NULL;
}
if (h) {
/*ROUTER*/ InsertHeader(h,mkSender(e, uidpwnam(e->e_statbuf.st_uid),0));
set_pname(e, nh, "Sender");
}
}
if (QCHANNEL(e->e_from_trusted)->cstring == NULL) {
l = cdr(QCHANNEL(e->e_from_trusted));
QCHANNEL(e->e_from_trusted) = copycell(QCHANNEL(e->e_from_resolved));
cdr(QCHANNEL(e->e_from_trusted)) = l;
}
if (QHOST(e->e_from_trusted)->cstring == NULL) {
l = cdr(QHOST(e->e_from_trusted));
QHOST(e->e_from_trusted) = copycell(QHOST(e->e_from_resolved));
cdr(QHOST(e->e_from_trusted)) = l;
}
if (QUSER(e->e_from_trusted)->cstring == NULL) {
l = cdr(QUSER(e->e_from_trusted));
QUSER(e->e_from_trusted) = copycell(QUSER(e->e_from_resolved));
cdr(QUSER(e->e_from_trusted)) = l;
}
if (QATTRIBUTES(e->e_from_trusted)->cstring == NULL) {
l = cdr(QATTRIBUTES(e->e_from_trusted));
QATTRIBUTES(e->e_from_trusted) = copycell(QATTRIBUTES(e->e_from_resolved));
cdr(QATTRIBUTES(e->e_from_trusted)) = l;
}
if (deferuid) {
UNGCPRO5;
return PERR_DEFERRED;
}
dprintf("Recipient determination\n");
FindEnvelope(eTo);
if (h == NULL) {
dprintf("No recipient(s) specified in the envelope\n");
if (header_error) {
dprintf("Due to header error, we ignore message\n");
UNGCPRO5;
return PERR_HEADER;
}
ph = e->e_eHeaders;
dprintf("Add all To:,Cc:,Bcc: to envelope recipients\n");
oh = NULL;
for (h = e->e_headers; h != NULL; oh = h, h = h->h_next) {
if (h->h_descriptor->user_type != Recipient
|| (e->e_resent != 0
&& h->h_descriptor->class != Resent))
continue;
for(a = h->h_contents.a; a!=NULL; a = a->a_next)
for (p=a->a_tokens;p!=NULL; p=p->p_next)
if (p->p_type == anAddress)
goto ok;
continue;
ok:
nh = copyRecipient(h);
nh->h_next = e->e_eHeaders;
e->e_eHeaders = nh;
}
dprintf("Are we supposed to be psychic?\n");
if (ph == e->e_eHeaders) {
UNGCPRO5;
return PERR_NORECIPIENTS;
}
}
dprintf("Nuke Bcc/Return-Path/X-Orcpt/X-Envid headers, if any\n");
hp = & e->e_headers;
while (*hp) {
h = *hp;
if (h->h_descriptor->hdr_name != NULL &&
((h->h_descriptor->user_type == killUserType)
|| (h->h_descriptor->class == normal &&
(CISTREQ(h->h_descriptor->hdr_name,"bcc")))
|| (h->h_descriptor->class == Resent &&
(CISTREQ(h->h_descriptor->hdr_name,"resent-bcc"))))) {
/* Skip this one */
*hp = h->h_next;
} else {
/* No header dropping here */
hp = & h->h_next;
}
}
/*
* Log the message after we find the envelope From address, otherwise
* log entries might have empty sender fields.
*/
if (e->e_messageid)
logmessage(e);
if (header_error) {
/* only send back if local originator, until we fix gweansm */
if (def_uid != nobody) {
reject(e, "headerwarn");
printf("def_uid = %d\n", def_uid);
}
dprintf("Emit warning headers\n");
for (ph = NULL, h = e->e_headers; h != NULL; ph=h,h=h->h_next) {
if (h->h_stamp == BadHeader && do_hdr_warning) {
if (ph == NULL)
e->e_headers = hdr_warning(h);
else
ph->h_next = hdr_warning(h);
}
}
}
dprintf("Make sure a From: line exists...");
FindHeader("from",e->e_resent);
if (h == NULL) {
FindEnvelope(eFrom);
oh = h;
if (h == NULL) { /* panic... can't happen... */
dprintf(" none available!\n");
optsave(FYI_NOSENDER, e);
} else {
if (isErrChannel) {
char *ss = (char*)tmalloc(10);
strcpy(ss, "From: <>");
nh = makeHeader(spt_headers, ss, 4);
nh->h_lines = makeToken(ss+5,3);
nh->h_lines->t_type = Line;
nh->h_contents = hdr_scanparse(e, nh, 0, 0);
nh->h_stamp = hdr_type(nh);
FindHeader("to",e->e_resent);
if (h == NULL) {
for (h = e->e_headers; h != NULL; h = h->h_next)
if (CISTREQ(h->h_pname, "subject"))
break;
}
InsertHeader(h, nh);
dprintf(" nope (added)!\n");
} else {
/* We make a copy of eFrom envelope header
to be our new From: header */
nh = makeHeader(spt_headers, "From", 4);
nh->h_lines = oh->h_lines;
nh->h_contents = oh->h_contents;
nh->h_stamp = hdr_type(nh);
FindHeader("to",e->e_resent);
if (h == NULL) {
for (h = e->e_headers; h != NULL; h = h->h_next)
if (CISTREQ(h->h_pname, "subject"))
break;
}
InsertHeader(h, nh);
dprintf(" nope (added)!\n");
}
}
} else
dprintf(" yup!\n");
/* Any 'To:' in the headers ? */
FindHeader("to",e->e_resent);
if (h == NULL)
FindHeader("to",!e->e_resent);
if (h == NULL) {
#if 1
dprintf("No 'To:' -header, creating our own\n");
InsertHeader(h, mkToHeader(e,"unlisted-recipients:; (no To-header on input)"));
#else
dprintf("Insert the To: header lines\n");
for (h = e->e_headers; h != NULL; h = h->h_next)
if (CISTREQ(h->h_pname, "subject"))
break;
if (h == NULL) {
FindHeader("from",e->e_resent);
if (h)
h = h->h_next;
}
ph = h;
for (h = e->e_headers; h != NULL; h = h->h_next)
if (h->h_next == ph)
break;
ph = h; /* assert ph != NULL */
for (h = e->e_eHeaders; h != NULL; h = h->h_next) {
if (h->h_descriptor->class == eTo) {
nh = (struct header *)tmalloc(sizeof (struct header));
*nh = *h;
set_pname(e, nh, "To");
nh->h_next = ph->h_next;
ph->h_next = nh;
/* so we add them in reverse order... */
}
}
#endif
}
#ifdef notdef
rewrite all addresses in the message according to the
incoming-rewriting rules for the originating channel.
#endif
dprintf("Route recipient addresses\n");
if (errors_to) free(errors_to);
errors_to = NULL;
senderstr = NULL;
if (e->e_from_trusted) {
if (isErrChannel) {
senderstr = "<>"; /* From "BOX" -- from an error channel! */
} else {
senderstr = QUSER(e->e_from_trusted)->cstring;
}
}
DSN = NULL;
for (h = e->e_eHeaders; h != NULL; h = h->h_next) {
if (h->h_descriptor->class != eTo &&
h->h_descriptor->class != eToDSN) {
DSN = NULL;
continue;
}
if (D_sequencer) dumpHeader(h);
if (h->h_descriptor->class == eToDSN) {
DSN = NULL;
if (h->h_contents.a == NULL ||
h->h_contents.a->a_pname == NULL ||
h->h_contents.a->a_pname[0] == 0) {
/* HUH! How come ? "todsn" w/o data ?? */
continue;
}
DSN = (struct notary *)tmalloc(sizeof(struct notary));
DSN->envid = envid;
DSN->ret = notaryret;
DSN->dsn = h->h_contents.a->a_pname;
continue;
}
if (DSN) {
h->h_contents.a->a_dsn = DSN;
DSN = NULL;
}
for (a = h->h_contents.a; a != NULL; a = a->a_next) {
++inrcpts;
/*ROUTER*/ l = router(a, def_uid, "recipient", senderstr);
if (l == NULL)
continue;
#if 0
if (!LIST(l) || !car(l) || !LIST(car(l))) {
FIXME: FIXME: react on badly structured result!
}
#endif
if (routed_addresses == NULL) {
routed_addresses = ncons(car(l));
} else {
cdr(s_last(car(l))) = car(routed_addresses);
car(routed_addresses) = car(l);
}
/* freecell(l) */
l = NULL;
}
if (deferuid) {
UNGCPRO5;
return PERR_DEFERRED;
}
}
dprintf("Crossbar to be applied to all (sender,routed-address) pairs\n");
sender = ncons(e->e_from_trusted);
#if 0
if (LIST(sender) && LIST(car(sender)))
sender = caar(sender);
#endif
if (routed_addresses == NULL) /* they were probably all deferred */ {
printf("No routed addresses -> deferred\n");
UNGCPRO5;
return PERR_DEFERRED;
}
rwhead = NULL;
for (idnumber = 0, l = car(routed_addresses); l != NULL ; l = cdr(l)) {
/* first level is ANDs */
++idnumber, nxor = 0;
for (to = car(l); to != NULL; to = cdr(to)) {
conscell *x, *tmp, *errto, *gg;
conscell *sender1, *z;
GCVARS6;
/* secondlevel is XORs */
if (D_sequencer) {
printf("crossbar sender: ");
s_grind(sender, stdout);
putchar('\n');
printf("crossbar to: ");
s_grind(to, stdout);
putchar('\n');
}
if ((x = crossbar(sender, to)) == NULL)
continue;
/*
* We expect to see something like
* (rewrite (fc fh fu) (tc th tu)) or
* ((address-rewrite header-rewrite) (fc fh fu) (tc th tu))
* back from the crossbar function.
*/
errto = gg = sender1 = tmp = z = NULL;
GCPRO6(x, errto, gg, sender1, tmp, z);
if (D_sequencer) {
printf("crossbar returns: ");
s_grind(x, stdout);
putchar('\n');
}
++nxor;
tmp = copycell(car(x));
cdr(tmp) = NULL;
sender1 = cdar(x);
gg = cdr(cddr(car(sender1))); /* recipient attrib! */
errto = find_errto(gg); /* Possible 'ERR' value */
if (!errto)
errto = cddar(sender1); /* Sender 'user' */
/* Rewriter level: */
for (rwp = rwhead; rwp != NULL; rwp = rwp->next)
if (s_equal(rwp->info, tmp)) {
break;
}
if (rwp == NULL) {
rwp = rwalloc(&rwhead);
rwp->info = tmp; /* rewritings */
rwp->errto = NULL;
rwmchain = rwmappend(rwmchain,tmp,NULL);
}
/* else the 'tmp' leaks for a moment ? */
tmp = cdr(sender1); /* recipient */
cdr(sender1) = NULL; /* detach from sender */
/* Sender (and errto) level: */
for (nsp = rwp->down; nsp != NULL; nsp = nsp->next)
if (s_equal(nsp->info, sender1) &&
s_equal1(nsp->errto,errto))
break;
if (nsp == NULL) {
nsp = rwalloc(&rwp->down);
nsp->info = sender1; /* new sender */
nsp->errto = errto;
rwmchain = rwmappend(rwmchain,sender1,errto);
}
#if 0
else {
printf("==comparing: ");
s_grind(nsp->info, stdout);
printf("\n\tand: ");
s_grind(cdar(x), stdout);
printf("\n");
fflush(stdout);
}
#endif
rcp = rwalloc(&nsp->down);
rcp->info = tmp; /* new recipient */
rcp->urw.number = idnumber;
rcp->errto = errto;
rwmchain = rwmappend(rwmchain,tmp,errto);
#if 0
for (rwp = rwhead; rwp != NULL; rwp = rwp->next) {
printf("**");
s_grind(rwp->info, stdout);
printf("\n");
fflush(stdout);
for (nsp = rwp->down; nsp != NULL; nsp = nsp->next) {
struct rwmatrix *rp;
printf("**\t");
s_grind(nsp->info, stdout);
printf("\n");
fflush(stdout);
for (rp = nsp->down; rp != NULL;
rp = rp->next) {
printf("**\t\t");
s_grind(rp->info, stdout);
printf("\n");
fflush(stdout);
}
}
}
#endif
UNGCPRO6;
}
if (nxor <= 1) {
--idnumber;
if (nxor == 1)
rcp->urw.number = 0; /* 0 means no XOR addrs */
}
}
if (deferuid) {
UNGCPRO5;
return PERR_DEFERRED;
}
isSenderAddr = isRecpntAddr = 0;
dprintf("Make sure Date, From, To, are in the header\n");
FindHeader("date",e->e_resent);
if (h == NULL)
InsertHeader(h, mkDate(e->e_resent, e->e_statbuf.st_mtime));
dprintf("Rewrite message headers\n");
for (rwp = rwhead; rwp != NULL; rwp = rwp->next) {
hp = &rwp->urw.h;
for (h = e->e_headers; h != NULL; h = h->h_next) {
isSenderAddr = h->h_descriptor->user_type == Sender;
isRecpntAddr = h->h_descriptor->user_type == Recipient;
if (isSenderAddr || isRecpntAddr) {
if (!STRING(rwp->info)) /* just addresses */
continue;
*hp = hdr_rewrite(rwp->info->string, h);
hp = &(*hp)->h_next;
}
}
*hp = NULL;
}
if (deferuid) {
UNGCPRO5;
return PERR_DEFERRED;
}
if (rwhead == NULL) {
UNGCPRO5;
return PERR_NORECIPIENTS;
}
dprintf("Emit specification to the transport system\n");
#ifdef USE_ALLOCA
ofpname = (char *)alloca(30);
#else
ofpname = (char *)emalloc(30);
#endif
/* Guaranteed unique within this machine */
sprintf(ofpname,".%ld.%d", (long)time(NULL), (int)getpid());
ofp = fopen(ofpname, "w+");
if (ofp == NULL) {
#ifndef USE_ALLOCA
free(ofpname);
#endif
printf("Creation of control file failed\n");
UNGCPRO5;
return PERR_CTRLFILE;
}
setvbuf(ofp, vbuf, _IOFBF, sizeof vbuf);
if (files_gid >= 0) {
fchown(FILENO(ofp), e->e_statbuf.st_uid, files_gid);
fchmod(FILENO(ofp), 0460);
}
fprintf(ofp, "%c%c0x%08lx\n",
_CF_FORMAT, _CFTAG_NORMAL, (long)_CF_FORMAT_KNOWN_SET);
FindEnvelope(eVerbose);
if (h && h->h_contents.a && h->h_contents.a->a_tokens) {
if (h->h_contents.a->a_tokens->p_tokens &&
h->h_contents.a->a_tokens->p_tokens->t_type == String)
h->h_contents.a->a_tokens->p_tokens->t_type = Atom;
printToken(verbosefile, verbosefile + sizeof verbosefile,
h->h_contents.a->a_tokens->p_tokens,
(token822 *)NULL, 0);
/*
* We have to be careful how we open this file, since one
* might imagine someone trying to append to /etc/passwd
* using this stuff. The only safe way is to open it
* with the permissions of the owner of the message file.
*/
SETEUID(e->e_statbuf.st_uid);
vfp = fopen(verbosefile, "a");
if (vfp) {
fseek(vfp, (off_t)0, 2);
setvbuf(vfp, NULL, _IOLBF, 0);
fprintf(vfp, "router processed message %s\n", file);
fprintf(ofp, "%c%c%s\n", _CF_VERBOSE, _CFTAG_NORMAL,
verbosefile);
}
SETEUID(0);
} else
vfp = NULL;
fprintf(ofp, "%c%c%s\n",
_CF_MESSAGEID, _CFTAG_NORMAL, pfile);
fprintf(ofp, "%c%c%d\n",
_CF_BODYOFFSET, _CFTAG_NORMAL, (int)(e->e_msgOffset));
if (e->e_messageid) {
fprintf(ofp, "%c%c%s\n",
_CF_LOGIDENT, _CFTAG_NORMAL, e->e_messageid);
msgidstr = e->e_messageid;
}
/* else { we don't want to log anything } */
if (vfp) {
if (e->e_messageid)
fprintf(vfp, "%c%c%s\n",
_CF_LOGIDENT, _CFTAG_NORMAL, e->e_messageid);
/* else { we don't want to log anything } */
}
/*
* If this message came from an error channel, then
* do NOT produce an error message if something goes
* wrong. It can quickly lead to Bad Things happening
* to your disk space.
*/
if (!isErrChannel) {
#if 1
if (errors_to) { /* [mea@utu.fi] Stupid, but workable.. */
putc(_CF_ERRORADDR, ofp);
putc(_CFTAG_NORMAL, ofp);
fprintf(ofp,"%s\n",errors_to);
if (vfp) {
putc(_CF_ERRORADDR, vfp);
putc(_CFTAG_NORMAL, vfp);
fprintf(vfp,"%s\n",errors_to);
}
free(errors_to);
errors_to = NULL;
} else
#endif
if ((h = erraddress(e)) != NULL) {
putc(_CF_ERRORADDR, ofp);
putc(_CFTAG_NORMAL, ofp);
for (ap = h->h_contents.a;
ap != NULL; ap = ap->a_next) {
if (ap != h->h_contents.a)
putc(' ', ofp);
printAddress(ofp, ap->a_tokens, 0);
if (ap->a_next)
putc(',', ofp);
}
putc('\n', ofp);
if (vfp) {
putc(_CF_ERRORADDR, vfp);
putc(_CFTAG_NORMAL, vfp);
for (ap = h->h_contents.a;
ap != NULL; ap = ap->a_next) {
if (ap != h->h_contents.a)
putc(' ', vfp);
printAddress(vfp, ap->a_tokens, 0);
if (ap->a_next)
putc(',', vfp);
}
putc('\n', vfp);
}
}
}
/*
* If this message might obsolete another we need to tell
* the scheduler.
*/
for (h = e->e_headers; h != NULL; h = h->h_next) {
ofperrors |= ferror(ofp);
if (ofperrors) break; /* Sigh.. */
if (h->h_descriptor->hdr_name == NULL
|| !CISTREQ(h->h_descriptor->hdr_name, "obsoletes"))
continue;
for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next) {
putc(_CF_OBSOLETES, ofp);
putc(_CFTAG_NORMAL, ofp);
printAddress(ofp, ap->a_tokens, 0);
putc('\n', ofp);
}
}
/*
* If this message might trigger scheduler to give target (smtp),
* we must tell the scheduler.
*/
for (h = e->e_headers; h != NULL; h = h->h_next) {
ofperrors |= ferror(ofp);
if (ofperrors) break; /* Sigh.. */
if (h->h_descriptor->hdr_name == NULL
|| !CISTREQ(h->h_descriptor->hdr_name, "turnme"))
continue;
for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next) {
putc(_CF_TURNME, ofp);
putc(_CFTAG_NORMAL, ofp);
printAddress(ofp, ap->a_tokens, 0);
putc('\n', ofp);
}
}
for (rwp = rwhead; rwp != NULL; rwp = rwp->next) {
ofperrors |= ferror(ofp);
if (ofperrors) break; /* Sigh.. */
for (nsp = rwp->down; nsp != NULL; nsp = nsp->next) {
ofperrors |= ferror(ofp);
if (ofperrors) break; /* Sigh.. */
if (!isErrChannel && nsp->errto &&
nsp->errto->string) {
/* print envelope sender address */
putc(_CF_ERRORADDR, ofp);
putc(_CFTAG_NORMAL, ofp);
fprintf(ofp,"%s\n",nsp->errto->string);
if (vfp) {
putc(_CF_ERRORADDR, vfp);
putc(_CFTAG_NORMAL, vfp);
fprintf(vfp,"%s\n",nsp->errto->string);
}
}
fprintf(ofp, "%c%c", _CF_SENDER, _CFTAG_NORMAL);
fromaddr = prctladdr(nsp->info, ofp, _CF_SENDER, "sender");
if (!fromaddr) {
UNGCPRO5;
goto bad_addrdata;
}
putc('\n', ofp);
if (vfp) {
fprintf(vfp, "%c%c",
_CF_SENDER, _CFTAG_NORMAL);
prctladdr(nsp->info, vfp,
_CF_SENDER, "sender");
putc('\n', vfp);
}
/* print recipient addresses */
for (rcp = nsp->down; rcp != NULL; rcp = rcp->next) {
ofperrors |= ferror(ofp);
if (ofperrors) break; /* Sigh.. */
if (rcp->urw.number > 0)
putc(_CF_XORECIPIENT, ofp);
else
putc(_CF_RECIPIENT, ofp);
putc(_CFTAG_NORMAL, ofp);
fprintf(ofp,"%*s",(_CFTAG_RCPTPIDSIZE +
_CFTAG_RCPTDELAYSIZE),"");
if (rcp->urw.number > 0)
fprintf(ofp, "%d ", rcp->urw.number);
if (! prctladdr(rcp->info, ofp,
_CF_RECIPIENT, "recipient")) {
UNGCPRO5;
goto bad_addrdata;
}
putc('\n', ofp);
++onrcpts;
/* DSN data output ! */
prdsndata(rcp->info, ofp, "recipient");
if (vfp) {
if (rcp->urw.number > 0)
putc(_CF_XORECIPIENT, vfp);
else
putc(_CF_RECIPIENT, vfp);
putc(_CFTAG_NORMAL, vfp);
fprintf(vfp,"%*s",(_CFTAG_RCPTPIDSIZE +
_CFTAG_RCPTDELAYSIZE),"");
if (rcp->urw.number > 0)
fprintf(vfp, "%d ", rcp->urw.number);
prctladdr(rcp->info, vfp,
_CF_RECIPIENT, "recipient");
putc('\n', vfp);
/* DSN data output ! */
prdsndata(rcp->info, vfp, "recipient");
}
}
}
/* print header */
putc(_CF_MSGHEADERS, ofp);
putc('\n', ofp);
if (vfp)
fprintf(vfp, "headers rewritten using '%s' function:\n",
rwp->info->string);
/* print the header, replacing all To:, Cc:, fields with
the corresponding fields as stored with the rewrite set. */
nh = rwp->urw.h;
for (h = e->e_headers; h != NULL; h = h->h_next) {
ofperrors |= ferror(ofp);
if (ofperrors) break; /* Sigh.. */
if (nh
&& (h->h_descriptor->user_type == Sender
|| h->h_descriptor->user_type == Recipient)) {
hdr_print(nh, ofp);
if (vfp)
hdr_print(nh, vfp);
nh = nh->h_next;
} else {
hdr_print(h, ofp);
if (vfp)
hdr_print(h, vfp);
}
}
putc('\n', ofp);
if (vfp)
putc('\n', vfp);
}
UNGCPRO5;
if (e->e_fp && files_gid >= 0) {
fchown(FILENO(e->e_fp), -1, files_gid);
fchmod(FILENO(e->e_fp),
(0440|e->e_statbuf.st_mode) & 0660);
}
/* all is nirvana -- link the input file to somewhere safe */
#ifdef USE_ALLOCA
qpath = (char*)alloca(12+strlen(QUEUEDIR)+strlen(pfile));
#else
qpath = (char*)emalloc(12+strlen(QUEUEDIR)+strlen(pfile));
#endif
sprintf(qpath, "../%s/%s%s", QUEUEDIR, subdirhash, pfile);
fflush(ofp);
#ifdef HAVE_FSYNC
while (fsync(FILENO(ofp)) < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
break;
}
#endif
ofperrors |= ferror(ofp);
taskfilesize_kb = 0;
if (!ofperrors) {
memset(&stbuf, 0, sizeof(stbuf));
fstat(FILENO(ofp), &stbuf); /* will always succeed.. */
if (stbuf.st_blksize == 0) stbuf.st_blksize = 1024;
taskfilesize_kb = (stbuf.st_size + stbuf.st_blksize -1) / 1024;
}
infilesize_kb = 0;
if (!ofperrors) {
memset(&stbuf, 0, sizeof(stbuf));
lstat(file, &stbuf); /* will always succeed.. */
if (stbuf.st_blksize == 0) stbuf.st_blksize = 1024;
infilesize_kb = (stbuf.st_size + stbuf.st_blksize -1) / 1024;
}
if ((fclose(ofp) != 0) || ofperrors ||
(erename(file, qpath) != 0)) { /* ORIGINAL FILE PATH! */
zunlink(qpath);
zunlink(ofpname);
#ifndef USE_ALLOCA
free(ofpname);
free(qpath);
#endif
return PERR_CTRLFILE;
}
#ifndef USE_ALLOCA
path = (char*)emalloc(14+strlen(TRANSPORTDIR)+strlen(pfile));
#else
/* This actually reallocs more space from stack, but then it
is just stack space and will disappear.. */
path = (char*)alloca(14+strlen(TRANSPORTDIR)+strlen(pfile));
#endif
sprintf(path, "../%s/%s%s", TRANSPORTDIR, subdirhash, pfile);
#ifndef HAVE_RENAME
zunlink(path); /* Should actually always fail.. */
#endif
if (erename(ofpname, path) < 0) {
zunlink(qpath);
zunlink(path);
#ifndef USE_ALLOCA
free(ofpname);
free(qpath);
free(path);
#endif
return PERR_CTRLFILE;
}
if (vfp) {
fprintf(vfp, "router done processing; F='%s' TF='%s'\n", file, path);
fflush(vfp);
#ifdef HAVE_FSYNC
while (fsync(FILENO(vfp)) < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
break;
}
#endif
fclose(vfp);
}
rtsyslog(e->e_spoolid, e->e_statbuf.st_mtime,
fromaddr, smtprelay, (int) e->e_statbuf.st_size,
onrcpts, msgidstr, start_now);
MIBMtaEntry->rt.TransmittedMessages += 1;
MIBMtaEntry->rt.ReceivedRecipients += inrcpts;
MIBMtaEntry->rt.TransmittedRecipients += onrcpts;
MIBMtaEntry->rt.TransmittedVolume += infilesize_kb;
MIBMtaEntry->rt.TransmittedVolume2 += taskfilesize_kb;
MIBMtaEntry->rt.StoredMessages -= 1;
MIBMtaEntry->rt.StoredVolume -= infilesize_kb;
#ifdef AF_UNIX
do {
const char *schedulernotify;
char buf[1000];
int notifysocket;
struct sockaddr_un sad;
#ifndef MSG_NOSIGNAL
RETSIGTYPE (*oldsig)__((int));
#endif
schedulernotify = getzenv("SCHEDULERNOTIFY");
if (!schedulernotify) break;
memset(&sad, 0, sizeof(sad));
sad.sun_family = AF_UNIX;
strncpy(sad.sun_path, schedulernotify, sizeof(sad.sun_path));
sad.sun_path[ sizeof(sad.sun_path)-1 ] = 0;
notifysocket = socket(PF_UNIX, SOCK_DGRAM, 0);
if (notifysocket < 0) {
perror("notifysocket: socket(PF_UNIX)");
break;
}
fd_nonblockingmode(notifysocket);
sprintf(buf, "NEW %s%.300s", subdirhash, pfile);
/* We are running with SIGPIPE ignored.. */
sendto(notifysocket, buf, strlen(buf),
#ifdef MSG_NOSIGNAL
MSG_NOSIGNAL|
#endif
MSG_DONTWAIT ,
(struct sockaddr *)&sad, sizeof(sad));
close(notifysocket);
} while (0);
#endif /* AF_UNIX */
#ifndef USE_ALLOCA
free(ofpname);
free(qpath);
free(path);
#endif
/* we did it! */
return PERR_OK;
bad_addrdata:;
/* Bad data in routing results, can't do it now, must defer! */
fclose(ofp);
zunlink(ofpname); /* created file is thrown away.. */
#ifndef USE_ALLOCA
free(ofpname);
#endif
return PERR_CTRLFILE;
}
static const char *
prctladdr(info, fp, cfflag, comment)
conscell *info; /* No conscell allocs down here */
FILE *fp;
int cfflag;
const char *comment;
{
int i = 0;
register conscell *l, *x;
const char *user = "?user?";
const char *channel = NULL;
const char *privilege = NULL, *p;
/* We print the quad of channel/host/user/privilege information
with this routine, and we return pointer to the user info.
For an "error" channel we return an empty string for the user. */
for (l = car(info); l != NULL; l = cdr(l)) {
++i;
if (STRING(l)) {
if (cdr(l) == NULL /* No next == this is the last */
&& (x = v_find(l->string)) != NULL /* can find ? */
&& (x = cdr(x)) != NULL /* found, ... */
&& LIST(x)) { /* ... and it is a list */
/* Find the 'privilege' item */
for (x = car(x); x != NULL; x = cddr(x)) {
if (STRING(x) &&
STREQ((char *)x->string, "privilege")) {
x = cdr(x);
break;
}
}
/* if x == NULL, no privilege was specified */
} else
x = l;
if (x) {
if (x->string == NULL || *x->string == '\0')
putc('-', fp);
else {
char *s = x->string;
int quote = 0;
/* Do same scanner as skip821address() will do with
our result. Does it arrive to the end-of-string ?
Does it do it with 'quote' state CLEAR ? */
for ( ; *s; ++s) {
int c = *s;
if (c == '\\') {
++s;
if (*s == 0) break;
}
if (c == quote) /* 'c' is non-zero here */
quote = 0;
else if (c == '"')
quote = c;
else if (!quote && (c == ' ' || c == '\t'))
break;
}
if (*s || quote) {
/* Didn't arrive to the end-of-string with quote clear! */
putc('"',fp);
for (s = x->string; *s; ++s) {
if (*s == '"' || *s == '\\')
putc('\\',fp);
putc(*s,fp);
}
putc('"',fp);
} else
/* It is clean string not needing extra quotes */
fprintf(fp, "%s", x->string);
}
if (i == 4 && x->string)
privilege = x->string;
if (i == 3 && x->string)
user = x->string;
if (i == 1 && x->string)
channel = x->string;
}
if (cdr(l))
putc(' ', fp);
} else if (channel && !STREQ(channel,"error"))
fprintf(stderr, "Malformed %s\n", comment);
}
if ((cfflag == _CF_SENDER) && channel && STREQ(channel,"error"))
user = ""; /* error channel source address -> no user! */
if (!privilege) {
fprintf(stderr, "Malformed (missing) %s privilege data!\n", comment);
return NULL;
}
p = privilege;
if (*p == '-') ++p;
while ('0' <= *p && *p <= '9') ++p;
if (*p) {
fprintf(stderr, "Malformed ('%s') %s privilege data!\n",
privilege, comment);
return NULL;
}
return user;
}
static void
prdsndata(info, fp, comment)
conscell *info;
FILE *fp;
const char *comment;
{
int i = 0;
register conscell *l, *x = NULL; /* No allocs down here */
const char *DSN = NULL;
const char *ENV = NULL;
const char *RET = NULL;
for (l = car(info); l != NULL; l = cdr(l)) {
++i;
if (STRING(l)) {
if (cdr(l) == NULL
&& (x = v_find(l->cstring)) != NULL
&& (x = cdr(x)) != NULL
&& LIST(x)) {
for (x = car(x); x != NULL; x = cddr(x)) {
if (STRING(x) && cdr(x) && STRING(cdr(x))) {
if (memcmp(x->cstring,"DSN",4) == 0) {
DSN = cdr(x)->cstring;
} else if (memcmp(x->cstring,"DSNr",5) == 0) {
RET = cdr(x)->cstring;
} else if (memcmp(x->cstring,"DSNe",5) == 0) {
ENV = cdr(x)->cstring;
}
}
}
/* if x == NULL, no DSN was specified */
}
} else
fprintf(stderr, "Malformed DSN %s\n", comment);
}
if (DSN && *DSN)
fprintf(fp, "%c%c%s\n", _CF_RCPTNOTARY, _CFTAG_NORMAL, DSN);
if (RET && *RET)
fprintf(fp, "%c%c%s\n", _CF_DSNRETMODE, _CFTAG_NORMAL, RET);
if (ENV && *ENV)
fprintf(fp, "%c%c%s\n", _CF_DSNENVID, _CFTAG_NORMAL, ENV);
}
static conscell *
find_errto(info)
conscell *info; /* No allocs under here */
{
register conscell *x = NULL;
if (STRING(info))
info = v_find(info->string);
if (!info) return NULL;
for (x = cadr(info); x != NULL; x = cddr(x)) {
if (!STRING(x))
return NULL; /* error in data */
if (STREQ((char *)x->string,"ERR")) {
x = cdr(x);
break;
}
}
return x;
}
syntax highlighted by Code2HTML, v. 0.9.1