
/*
 *  SENDMAIL / RMAIL
 *
 *  (C) Copyright 1989-1990 by Matthew Dillon,  All Rights Reserved.
 *
 *  SENDMAIL <file -f from -t to -s subject -c cc -b bcc -r
 *  RMAIL user
 *
 *  Example:	Sendmail <datafile -froot
 *
 *  From: line is automatically added but can be overriden by a From:
 *  header in the file.  stdin is made up of a list of headers, a blank
 *  line, and then data until EOF.
 *
 *  the -r option tells sendmail that this is incomming mail.
 *  If av[0] begins with an 'r' for RMail instead of an 's' for
 *  Sendmail, then the rmail argument format is used (rmail user),
 *  as well as forcing -r.
 */

#include <exec/types.h>
#include <exec/lists.h>
#include <proto/all.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <log.h>
#include <config.h>
#include <time.h>
#include <pwd.h>
#include <string.h>
#include "/version.h"

#define RCVR_UUCP	1
#define RCVR_SENDMAIL	2

IDENT(".03");

typedef struct List	LIST;
typedef struct Node	NODE;

char	*UserName;
char	*NodeName;
char	*DomainName;
char	*TimeZoneName;	/*  All caps, ex: PST			 */
char	*DefaultNode;	/*  for addr formats we don't understand */
LIST	RecvList;	/*  Received:	    */
LIST	FromList;	/*  last one rules  */
LIST	ToList; 	/*  To: 	    */
LIST	CcList; 	/*  Cc: 	    */
LIST	BccList;	/*  Bcc:	    */
LIST	XccList;	/*  list of actual mail to be sent  */
LIST	SubjList;	/*  Subject:	    */
LIST	HdrList;	/*  other headers not specifically parsed */
char	ScrBuf[1024];
char	ScrBuf2[1024];
char	TempFileBuf[256];
int	Seq;		/*  UUCP sequence no	*/
char	ROpt;		/*  Receive-Mail flag	*/
static char OrigFromLine[512];
static char FromLine[512];  /*	'From ' line, if ROpt   */

char	*TmpFileName();
char	*atime();
void	PostPend();
void	MakeToFixNode();
void	MakeToAddrNode();
void	ToFixup();
void	FromFixup();
void	DumpHeaderInfo();
void	DumpHeader();
void	DumpCombineHeader();
void	IntegrateHeader();
void	Usage();
FILE	*SendMailTo();
FILE	*OneMailTo();
FILE	*OneMailToUUCP();
FILE	*OneMailToUser();
FILE	*OneMailToFile();
FILE	*OneMailToPipe();
NODE	*FindHeader();
NODE	*MakeNode();

CXBRK()
{
    return(0);
}

void
Usage()
{
    printf(
	"Sendmail -f user [-t address -c address -b address -s subject -r]\n"
	"RMail user\n"
    );
}

void
main(ac, av)
char *av[];
{
    short isRMail = 0;
    short isSendMail = 1;
    FILE *fi;

    NewList(&RecvList);
    NewList(&FromList);
    NewList(&ToList);
    NewList(&CcList);
    NewList(&BccList);
    NewList(&XccList);
    NewList(&SubjList);
    NewList(&HdrList);

    UserName = FindConfig("UserName");
    if (UserName == NULL) {
	puts("Sendmail: UserName not in UULIB:Config!");
	exit(1);
    }
    NodeName = FindConfig("NodeName");
    if (NodeName == NULL) {
	puts("Sendmail: NodeName not in UULIB:Config!");
	exit(1);
    }
    DomainName = FindConfig("DomainName");
    if (DomainName == NULL) {
	puts("Sendmail: DomainName not in UULIB:Config! using .UUCP");
	DomainName = ".UUCP";
    }
    DefaultNode = FindConfig("DefaultNode");
    TimeZoneName = FindConfig("TimeZone");

    LoadAliases();

    {
	char *ptr = av[0];
	if (*ptr == 'r' || *ptr == 'R') {
	    isRMail = 1;
	    isSendMail = 0;
	}
    }

    if (isRMail) {
	char *addr = (ac == 2) ? av[1] : "Mailer-Daemon";

	MakeNode(&BccList, addr);
	UserName = "postmaster";    /*  XXX  */
	ROpt = 1;		    /*	no header processing */
    }
    if (isSendMail) {
	short i;
	char *arg;
	for (i = 1; i < ac; ++i) {
	    arg = av[i];
	    if (*arg != '-')
		Usage();
	    switch(arg[1]) {
	    case 'f':
		UserName = av[i+1];
		sprintf(ScrBuf, "%s@%s%s", av[i+1], NodeName, DomainName);
		MakeNode(&FromList, ScrBuf);
		++i;
		break;
	    case 't':
		MakeNode(&ToList, av[++i]);
		break;
	    case 'c':
		MakeNode(&CcList, av[++i]);
		break;
	    case 'b':
		MakeNode(&BccList, av[++i]);
		break;
	    case 's':
		MakeNode(&SubjList, av[++i]);
		break;
	    case 'r':
		++ROpt;
		break;
	    default:
		Usage();
	    }
	}
    }

    /*
     *	Read headers from input file.  Headers are not necessarily
     *	contained on a single line.  Maximum 4096 chars per header.
     */

    if (ROpt) {
	fgets(ScrBuf, sizeof(ScrBuf), stdin);
	strcpy(OrigFromLine, ScrBuf);
	if (strncmp(ScrBuf, "From ", 5) != 0) {
	    ulog(-1, "Receive mail, expected 'From ', got %s", ScrBuf);
	}
	strcpy(FromLine, "From ");
	PostPend(ScrBuf + 5, 1);

	while (fgets(ScrBuf, sizeof(ScrBuf), stdin) && strncmp(ScrBuf, ">From ", 6) == 0) {
	    strcpy(OrigFromLine, ScrBuf + 1);
	    PostPend(ScrBuf + 6, 1);
	}
	strcpy(ScrBuf2, OrigFromLine + 5);
	PostPend(ScrBuf2, 0);
    } else {
	ScrBuf[0] = '\n';
	fgets(ScrBuf, sizeof(ScrBuf), stdin);
    }
    {
	static char Hdr[4096];
	short i = 0;		/*  index into Hdr  */

	while (ScrBuf[0] != '\n') {
	    char *ptr = ScrBuf;
	    while (*ptr && *ptr != ' ' && *ptr != 9 && *ptr != ':')
		++ptr;
	    if (*ptr == ':') {  /*  found new header */
		if (i)          /*  Dump old header  */
		    IntegrateHeader(Hdr, i);
		strcpy(Hdr, ScrBuf);
		i = strlen(Hdr);
	    } else {		/*  append to existing header	*/
		if (i == 0)
		    puts("Expected a Header!");
		strcpy(Hdr + i, ScrBuf);
		i = i + strlen(Hdr + i);
	    }

	    if (fgets(ScrBuf, sizeof(ScrBuf), stdin) == NULL)
		ScrBuf[0] = '\n';
	}
	if (i)
	    IntegrateHeader(Hdr, i);

	if (ScrBuf[0] != '\n') {
	    puts("sendmail: no mail");
	    exit(1);
	}
    }

    /*
     *	Parse & fixup each To:, Cc:, and Bcc: field.
     *
     *	From:	we add the personal info arg from the password file
     *	To:	we expand any aliases
     */

    if (ROpt == 0)
	FromFixup(&FromList);
    if (ROpt) {
	ToFixup(&BccList);
    } else {
	ToFixup(&ToList);
	ToFixup(&CcList);
	ToFixup(&BccList);
    }

    /*
     *	If no Subject: field add a dummy one
     */

    if (EmptyList(&SubjList))
	MakeNode(&SubjList, "");

    if (EmptyList(&FromList)) {
	sprintf(ScrBuf, "%s@%s%s", UserName, NodeName, DomainName);
	MakeNode(&FromList, ScrBuf);
    }

    fi = SendMailTo(&XccList, stdin);
    if (fi && fi != stdin)
	fclose(fi);
    if (TempFileBuf[0])
	remove(TempFileBuf);
    UnLockFiles();
}

/*
 *  Strips string and creates named node which is appended to the
 *  given list.
 */

NODE *
MakeNode(list, str)
LIST *list;
char *str;
{
    NODE *node;
    char *ptr;

    while (*str == ' ' || *str == 9)
	++str;
    for (ptr = str + strlen(str); ptr >= str && (*ptr == ' ' || *ptr == 9); --ptr);
    ++ptr;
    *ptr = 0;
    node = malloc(sizeof(NODE) + strlen(str) + 1);
    node->ln_Name = (char *)(node + 1);
    strcpy(node->ln_Name, str);
    AddTail(list, node);
    return(node);
}

/*
 *
 */

void
MakeToFixNode(list, str, send)
LIST *list;
char *str;
char *send;
{
    char *ptr;
    short len;
    short c;
    void fixCallBack();

    while (str < send && (*str == ' ' || *str == 9))
	++str;
    for (ptr = send - 1; ptr >= str && (*ptr == ' ' || *ptr == 9); --ptr);
    ++ptr;

    len = ptr - str;
    if (len < 0)
	return;

    /*
     *	str[0..len-1]
     */

    c = str[len];
    str[len] = 0;

    if (ROpt) {     /*  disallow remote asking for special options */
	ulog(-1, "Received mail for %s", str);
	if (str[0] == '>' || str[0] == '<' || str[0] == '|' || str[0] == ':' || str[0] == '/') {
	    ulog(-1, "SendMail, bad user %s", str);
	    return;
	}
    }

    {
	NODE *node = malloc(sizeof(NODE) + strlen(str) + 1);

	node->ln_Name = (char *)(node + 1);
	strcpy(node->ln_Name, str);
	AddTail(list, node);
    }

    UserAliasList(str, fixCallBack);    /*  from lib/alias.c    */
    str[len] = c;
}

void
fixCallBack(user)
char *user;
{
    NODE    *node;

    if (user[0] == '<') {
	FILE *fi = fopen(user + 1, "r");
	char *buf = malloc(256);

	if (fi == NULL) {
	    ulog(-1, "Unable to open < file %s", user + 1);
	    return;
	}
	while (fgets(buf, 256, fi)) {
	    short i = 0;
	    short j;
	    while (buf[i] == ' ' || buf[i] == 9)
		++i;
	    if (buf[i] == 0 || buf[i] == '\n')
		continue;
	    for (j = i; buf[j] && buf[j] != '\n' && buf[j] != ' ' && buf[j] != 9; ++j);
	    buf[j] = 0;
	    UserAliasList(buf, fixCallBack);
	}
	fclose(fi);
	free(buf);
	return;
    }

    ulog(-1, "Sendmail, Sending mail to %s", user);

    if (user[0] == '\\')
	++user;

    node = malloc(sizeof(NODE) + strlen(user) + 1);
    node->ln_Name = (char *)(node + 1);
    strcpy(node->ln_Name, user);
    AddTail(&XccList, node);
}


/*
 *  Integrates a header
 */

void
IntegrateHeader(hdr, len)
char *hdr;
short len;
{
    if (hdr[len-1] == '\n')     /*  strip trailing newline  */
	hdr[len-1] = 0;

    if (strncmp(hdr, "From:", 5) == 0) {
	MakeNode(&FromList, hdr + 5);
	return;
    }
    if (strncmp(hdr, "To:", 3) == 0) {
	MakeNode(&ToList, hdr + 3);
	return;
    }
    if (strncmp(hdr, "Cc:", 3) == 0) {
	MakeNode(&CcList, hdr + 3);
	return;
    }
    if (strncmp(hdr, "Bcc:", 4) == 0) {
	MakeNode(&BccList, hdr + 4);
	return;
    }
    if (strncmp(hdr, "Subject:", 8) == 0) {
	MakeNode(&SubjList, hdr + 8);
	return;
    }
    if (strncmp(hdr, "Received:", 9) == 0) {
	MakeNode(&RecvList, hdr + 9);
	return;
    }
    MakeNode(&HdrList, hdr);
}

/*
 *  Adds (personal info) to FromList based on passwd entry or, if
 *  that is not available, from the Config entry 'RealName'.
 */

void
FromFixup(list)
LIST *list;
{
    char *wholeName = GetConfig("RealName", "Who Knows");
    NODE *node;
    NODE *nn;
    LIST tmpList;

    NewList(&tmpList);

    while (node = RemHead(list)) {
	/*
	 *  FIXME.  Fix getpwnam() and use pw_gecos entry.
	 */
	nn = malloc(sizeof(NODE) + strlen(node->ln_Name) + strlen(wholeName) + 16);
	nn->ln_Name = (char *)(nn + 1);
	sprintf(nn->ln_Name, "%s (%s)", node->ln_Name, wholeName);
	free(node);
	AddTail(&tmpList, nn);
    }
    while (node = RemHead(&tmpList))
	AddTail(list, node);
}

/*
 *  Converts an unparsed list of names into a list of single address
 *  fields, removing any personal idents from the entries.  These will
 *  be recombined after processing when the data file is written out.
 *
 *  Also expands sendmail aliases (UULIB:Aliases) (HACK)
 */

void
ToFixup(list)
LIST *list;
{
    NODE *node;
    LIST tmpList;

    NewList(&tmpList);

    while (node = RemHead(list)) {
	char *str = node->ln_Name;
	char *ptr;

	while (*str) {      /*  breakup fields by newline or comma */
	    for (ptr = str; *ptr && *ptr != '\n' && *ptr != ','; ++ptr);
	    MakeToAddrNode(&tmpList, str);
	    str = ptr;
	    while (*str == '\n' || *str == ',' || *str == ' ' || *str == 9)
		++str;
	}
	free(node);
    }
    while (node = RemHead(&tmpList))
	AddTail(list, node);
}

/*
 *  Extracts a single name / address (comma or newline delimited)
 *  field and creates a new node.
 */

void
MakeToAddrNode(list, str)
LIST *list;
char *str;
{
    char *p1, *p2;
    short ns = 0;	/*  non-whitespace encountered */

    for (p1 = str; *p1 && *p1 != ',' && *p1 != '\n'; ++p1) {
	if (*p1 == '(') {   /*  addr (name) OR (name) addr */
	    if (ns) {       /*  addr (name) */
		MakeToFixNode(list, str, p1);
	    } else {
		while (*p1 && *p1 != ',' && *p1 != '\n' && *p1 != ')')
		    ++p1;
		if (*p1 == ')') {
		    for (p2 = p1 + 1; *p2 && *p2 != ',' && *p2 != '\n'; ++p2);
		    MakeToFixNode(list, p1 + 1, p2);
		}
	    }
	    return;
	}

	if (*p1 == '<') {   /*  <addr>  */
	    for (p2 = p1 + 1; *p2 && *p2 != ',' && *p2 != '\n' && *p2 != '>'; ++p2);
	    if (*p2 == '>')
		MakeToFixNode(list, p1 + 1, p2);
	    return;
	}
	if (*p1 != ' ' && *p1 != 9)
	    ns = 1;
    }
    MakeToFixNode(list, str, p1);
}


/*
 *  Send mail to <recipeant>
 *
 *	-Local mail
 *	-Machine path (UUCP)
 */

FILE *
SendMailTo(list, fi)
LIST *list;
FILE *fi;
{
    NODE *node;

    for (node = list->lh_Head; node != (NODE *)&list->lh_Tail; node = node->ln_Succ)
	fi = OneMailTo(node->ln_Name, fi);
    return(fi);
}

FILE *
OneMailTo(toaddr, rfi)
char *toaddr;
FILE *rfi;
{
    short i;
    char c;
    static char Buf[256];
    static char typeBuf[16];
    static char classBuf[16];
    static char addrBuf[128];

    if (toaddr[0] == '|')       /*  pipe through command    */
	return(OneMailToPipe(toaddr + 1, rfi));
    if (toaddr[0] == '>')       /*  copy to file            */
	return(OneMailToFile(toaddr + 1, rfi));

    for (i = 0; c = toaddr[i]; ++i) {
	if (c == '!' || c == '%' || c == '@' || c == ':')
	    break;
    }
    if (c == 0)                 /*  local name  */
	return(OneMailToUser(toaddr, rfi));

    /*
     *	Non-Local mail
     */

    i = ParseAddress(toaddr, Buf, strlen(toaddr));
    if (DomainLookup(Buf, strlen(Buf), typeBuf, classBuf, addrBuf)) {
	printf("type %s class %s addr %s\n", typeBuf, classBuf, addrBuf);
	printf("buf %s\n", Buf);

	/*
	 *  Note distinction between mail destination and mail forwarder.
	 *  mail destination removes the first machine from the rmail line
	 *  mail forwarder does NOT
	 */

	if (strcmpi(classBuf, "UU") == 0) {
	    if (strcmpi(typeBuf, "MD") == 0)
		return(OneMailToUUCP(addrBuf, Buf + i + 1, rfi));
	    else
		return(OneMailToUUCP(addrBuf, Buf, rfi));
	} else {
	    ulog(-1, "Unsupported domain class: %s", classBuf);
	    printf("Unsupported domain class: %s\n", classBuf);
	}
	return(rfi);
    } else {
	ulog(-1, "Could not find domain for %s, no mail sent", Buf);
	printf("Unable to send mail to %s\n", Buf);
	return(rfi);
    }
}


FILE *
OneMailToPipe(toaddr, rfi)
char *toaddr;
FILE *rfi;
{
    FILE *fi;
    char *ptr;
    static long pos;

    if (TempFileBuf[0] == 0) {
	strcpy(TempFileBuf, TmpFileName("T:pipe"));
	fi = fopen(TempFileBuf, "w");
	if (fi == NULL) {
	    ulog(-1, "Unable to open temp file %s for command: %s", TempFileBuf, toaddr);
	    return(rfi);
	}
	DumpHeaderInfo(fi, RCVR_SENDMAIL, 0, 1);
	pos = ftell(fi);
	while (fgets(ScrBuf, sizeof(ScrBuf), rfi))
	    fputs(ScrBuf, fi);
	fclose(fi);
    }
    strcpy(ScrBuf, toaddr);

    ptr = toaddr;
    if (strncmpi(toaddr, "run", 3) == 0) {
	ptr += 3;
	while (*ptr == ' ' || *ptr == 9)
	    ++ptr;
    }
    while (*ptr && *ptr != ' ' && *ptr != 9)
	++ptr;

    if (*ptr == 0)
	strcat(ScrBuf, " ");
    sprintf(ScrBuf + (ptr - toaddr + 1), "<%s %s", TempFileBuf, ptr);
    if (Execute(ScrBuf, NULL, NULL) == 0)
	ulog(-1, "Couldn't execute %s", ScrBuf);

    fi = fopen(TempFileBuf, "r");
    if (fi) {
	if (rfi != stdin)
	    fclose(rfi);
	rfi = fi;
	fseek(rfi, pos, 0);
    } else {
	ulog(-1, "Couldn't reopen temp '%s', mail failed!", TempFileBuf);
	rfi = stdin;
    }
    return(rfi);
}

FILE *
OneMailToUser(toaddr, rfi)
char *toaddr;
FILE *rfi;
{
    sprintf(ScrBuf, "UUMAIL:%s", toaddr);
    return(OneMailToFile(ScrBuf, rfi));
}

FILE *
OneMailToFile(tofile, rfi)
char *tofile;
FILE *rfi;
{
    FILE *fo;
    static char DataFile[128];
    long pos;

    strcpy(DataFile, tofile);

    LockFile(DataFile);
    fo = fopen(DataFile, "a");
    if (fo == NULL) {
	strcpy(DataFile, "T:MailOverflow");
	fo = fopen(DataFile, "a");
	if (fo)
	    ulog(-1, "Could not append to %s, appending to %s", tofile, DataFile);
	else
	    ulog(-1, "Can't append to anywhere! (%s)", tofile);
    }
    if (fo) {
	DumpHeaderInfo(fo, RCVR_SENDMAIL, 0, 1);
	pos = ftell(fo);
	if (ROpt) {
	    if (fgets(ScrBuf, sizeof(ScrBuf), rfi))
		fputs(ScrBuf, fo);
	}
	while (fgets(ScrBuf, sizeof(ScrBuf), rfi)) {
	    if (ScrBuf[0] == 'F' && strncmp(ScrBuf, "From ", 5) == 0)
		strins(ScrBuf, " ");
	    fputs(ScrBuf, fo);
	}
	fclose(fo);
	if (rfi != stdin)
	    fclose(rfi);
	rfi = fopen(DataFile, "r");
	fseek(rfi, pos, 0);
    }
    return(rfi);
}

FILE *
OneMailToUUCP(toaddr, skipaddr, rfi)
char *toaddr;
char *skipaddr;
FILE *rfi;
{
    static char ExecFile[128];
    static char XExecFile[128];
    static char CommandFile[128];
    static char DataFile[128];
    static char DestNode[256];
    /*static char ToAddr[256];*/
    FILE *fi;
    int seq;
    long pos;
    short i;
    short ai;

    /*
     *	If the destination node
     */

    strcpy(DestNode, toaddr);

    ai = -1;
    for (i = 0; DestNode[i]; ++i) {
	/*
	 *  Remember index for additional paths
	 */

	if (DestNode[i] == '!') {
	    DestNode[i] = 0;
	    ai = i + 1;
	    break;
	}

	/*
	 *  Cut off machine name at 7 chars and ignore any domain
	 *  info.
	 */

	if (i == 7 || DestNode[i] == '.')
	    DestNode[i] = 0;
    }

    Seq = seq = GetSequence(4);

#define FOFF	8

    sprintf(ExecFile,   "UUSPOOL:D.%sX%04d", DestNode, seq++);
    sprintf(XExecFile,  "UUSPOOL:X.%sX%04d", DestNode, seq++);
    sprintf(CommandFile,"UUSPOOL:C.%sA%04d", DestNode, seq++);
    sprintf(DataFile,   "UUSPOOL:D.%sB%04d", DestNode, seq);

    LockFile(ExecFile);
    LockFile(XExecFile);
    LockFile(CommandFile);
    LockFile(DataFile);

    /*
     *	Note, cannot run uuxqt from sendmail as uuxqt may call
     *	sendmail!
     */

    if (strncmp(DestNode, NodeName, 7) == 0)
	fi = fopen(XExecFile, "w");
    else
	fi = fopen(ExecFile, "w");

    if (fi == NULL)
	goto fail;

    fprintf(fi, "U %s\n", UserName);
    fprintf(fi, "F %s\n", DataFile + FOFF);
    fprintf(fi, "I %s\n", DataFile + FOFF);
    if (ai >= 0)
	fprintf(fi, "C rmail %s!%s\n", DestNode + ai, skipaddr);
    else
	fprintf(fi, "C rmail %s\n", skipaddr);
    fclose(fi);

    if (strncmp(DestNode, NodeName, 7)) {
	fi = fopen(CommandFile, "w");
	if (fi == NULL)
	    goto fail;

	fprintf(fi, "S %s %s %s - %s 0666\n", DataFile + FOFF, DataFile + FOFF, UserName, DataFile + FOFF);
	fprintf(fi, "S %s %s %s - %s 0666\n", ExecFile + FOFF, XExecFile + FOFF, UserName, ExecFile + FOFF);
	fclose(fi);
    }

    fi = fopen(DataFile, "w");
    if (fi == NULL)
	goto fail;

    DumpHeaderInfo(fi, RCVR_UUCP, 1, 0);
    pos = ftell(fi);

    if (ROpt) {
	if (fgets(ScrBuf, sizeof(ScrBuf), rfi))
	    fputs(ScrBuf, fi);
    }
    while (fgets(ScrBuf, sizeof(ScrBuf), rfi)) {
	if (ScrBuf[0] == 'F' && strncmp(ScrBuf, "From ", 5) == 0)
	    strins(ScrBuf, " ");
	fputs(ScrBuf, fi);
    }
    fclose(fi);
    if (rfi != stdin)
	fclose(rfi);
    fi = fopen(DataFile, "r");
    fseek(fi, pos, 0);
    return(fi);
fail:
    puts("Fail");
    return(rfi);
}

void
DumpHeaderInfo(fi, rcvr, resend, local)
FILE *fi;
{
    char *source;
    time_t t;

    time(&t);

    /*
     *	Write header info
     */

    if (rcvr == RCVR_UUCP)
	source = "AmigaUUCP";
    else
	source = "Sendmail";

    if (ROpt) {
	if (resend)
	    fprintf(fi, "%s %s remote from %s\n", FromLine, atime(&t), NodeName);
	else
	    fprintf(fi, "%s %s\n", FromLine, atime(&t));
    } else {
	if (local)
	    fprintf(fi, "From %s %s\n", UserName, atime(&t));
	else
	    fprintf(fi, "From %s %s remote from %s\n", UserName, atime(&t), NodeName);
    }
    fprintf(fi, "Received: by %s%s (0.44/0.44)\n\tid AA%05d; %s\n",
	NodeName, DomainName, Seq, atime(&t)
    );
    DumpHeader(fi, "Received:", &RecvList);
    if (ROpt == 0) {
	time_t t2 = t + 3600 * GetHourOffset(TimeZoneName);
	struct tm *ut;

	if (FindHeader("Date:") == NULL)
	    fprintf(fi, "Date: %s\n", atime(&t));
	ut = localtime(&t2);
	fprintf(fi, "Message-Id: <%02d%02d%02d%02d%02d.AA%05d@%s%s>\n",
	    ut->tm_year % 100, ut->tm_mon + 1, ut->tm_mday, ut->tm_hour, ut->tm_min,
	    Seq, NodeName, DomainName
	);
    }

    /*
     *	From:, To:, Cc:, Subject: (Bcc: never written to header),
     *	and any other header fields
     */

    DumpHeader(fi, NULL, &HdrList);
    DumpHeader(fi, "From:", &FromList);
    DumpCombineHeader(fi, "To:", &ToList);
    if (!EmptyList(&CcList))
	DumpCombineHeader(fi, "Cc:", &CcList);
    DumpHeader(fi, "Subject:", &SubjList);

    fprintf(fi, "\n");
}

void
DumpHeader(fi, field, list)
FILE *fi;
char *field;
LIST *list;
{
    NODE *node;

    for (node = list->lh_Head; node != (NODE *)&list->lh_Tail; node = node->ln_Succ) {
	if (field)
	    fprintf(fi, "%s %s\n", field, node->ln_Name);
	else
	    fprintf(fi, "%s\n", node->ln_Name);
    }
}

NODE *
FindHeader(field)
char *field;
{
    NODE *node;
    short len = strlen(field);

    for (node = HdrList.lh_Head; node != (NODE *)&HdrList.lh_Tail; node = node->ln_Succ) {
	if (strncmp(node->ln_Name, field, len) == 0)
	    return(node);
    }
    return(NULL);
}

void
DumpCombineHeader(fi, field, list)
FILE *fi;
char *field;
LIST *list;
{
    NODE *node;
    short ci = 0;

    fprintf(fi, "%s ", field);
    for (node = list->lh_Head; node != (NODE *)&list->lh_Tail; node = node->ln_Succ) {
	if (ci && ci + strlen(node->ln_Name) > 70) {
	    fprintf(fi, ",\n\t");
	    ci = 0;
	}
	if (ci)
	    fprintf(fi, ", ");
	fprintf(fi, "%s", node->ln_Name);
	ci += strlen(node->ln_Name) + 2;
    }
    fprintf(fi, "\n");
}

char *
atime(pt)
time_t *pt;
{
    static char buf[40];
    static char *mo[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
			    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
			};
    static char *dow[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    struct tm *ut = localtime(pt);

    sprintf(buf, "%s, %d %s %02d %02d:%02d:%02d %s",
	dow[ut->tm_wday], ut->tm_mday, mo[ut->tm_mon],
	ut->tm_year % 100, ut->tm_hour, ut->tm_min, ut->tm_sec,
	TimeZoneName
    );
    return(buf);
}

GetHourOffset(tz)
char *tz;
{
    short i;

    static struct {
	char *Name;
	short Hours;
    } TZAry[] = {
	"GMT",  0,
	"UT",   0,
	"PST",  8,
	"MST",  7,
	"CST",  6,
	"EST",  5,
	"AST",  4,
	"PDT",  7,
	"MDT",  6,
	"CDT",  5,
	"EDT",  4,
	"ADT",  3,
	NULL, 0
    };
    for (i = 0; TZAry[i].Name; ++i) {
	if (strncmp(tz, TZAry[i].Name, 3) == 0)
	    return((int)TZAry[i].Hours);
    }
    ulog(-1, "Unknown Timezone: %s", tz);
    printf("Unknown Timezone: %s", tz);
    return(6);
}

void
PostPend(str, frend)
char *str;
{
    char *ptr;
    if (frend) {
	ptr = str + strlen(str);
	while (ptr > str && *ptr != ' ' && *ptr != 9) {
	    if (*ptr == '\n')
		*ptr = 0;
	    --ptr;
	}
	str = ptr + 1;
    }
    for (ptr = str; *ptr && *ptr != ' ' && *ptr != 9 && *ptr != '\n'; ++ptr);
    if (frend)
	strcpy(ptr, "!");
    else
	strcpy(ptr, "");
    for (ptr = FromLine + 5; *ptr && *ptr != ' ' && *ptr != 9; ++ptr);
    strins(ptr, str);
}

EmptyList(list)
LIST *list;
{
    if (list->lh_Head == (NODE *)&list->lh_Tail)
	return(1);
    return(0);
}

