/* ### Modified by P.Saratxaga on 7 Aug 1995 ###
 * - Newsgroups: line is now gated if there is more than one newsgroup
 * - Added support for X-FTN-To and X-FTN-From generated by other gates
 * - modified removemime to kludgerise lines if charset != us-ascii
 * - creation of ^aACUPDATE kludges from Supersedes: and cancel's
 * - added charset support (see Changelog for details)
 * - added recognition of X-Fsc-
 * - added support to dequote MIME quoted printable messages. Code by T.Tanaka
 * - added removemsgid and removeinreply
 * - added rfclevel stuff.
 * - added DEFAULT_ORIGIN from an idea of "Marco d'Itri" <md@linux.it>
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <sys/param.h>
#include "xutil.h"
#include "lutil.h"
#include "ftn.h"
#include "rfcaddr.h"
#include "ftnmsg.h"
#include "rfcmsg.h"
#include "config.h"
#include "bwrite.h"
#include "falists.h"
#include "version.h"
#include "hash.h"
#include "needed.h"
#include "mkftnhdr.h"
#ifdef JE
#include "areas.h"
#endif /* JE */
#include "charset.h"
#include "charconv.h"
#include "mime.h"
#ifdef REFERENCES_MSC96
#include "ref_interface.h"
ref_private_t *ref_dbase;
#endif

#define MAXHDRSIZE 2048
#define MAXSEEN 70
#define MAXPATH 73

#ifndef DEFAULT_ORIGIN
#define DEFAULT_ORIGIN "Unknown"
#endif

extern time_t now;
extern int newsmode;
extern char *replyaddr;
extern faddr *bestaka;
extern int rfclevel;
extern int nosplitmode;
extern int pgpsigned;

#ifdef HAS_NDBM_H
extern char *idlookup(char *);
#endif
extern char *bgets(char *,int,FILE *);
extern long sequencer(void);
extern unsigned INT32 crc32(char*,int);
extern char *strip_flags(char *);
extern void putbody(int,rfcmsg*,FILE*,FILE*,int,int,int,int,int,int*);

static int removemime;
static int removeorg;
static int removemsgid;
static int removeref;
static int removeinreply;
static int removesupersedes;
static int removeapproved;
static int removefrom;
static int removereplyto;
static int removereturnto;
static int ftnorigin;

static int isftnpath(char*);

int needputrfc(rfcmsg*);
int needputrfc(msg)
rfcmsg *msg;
{
	faddr *ta;

	/* 0-junk, 1-kludge, 2-pass */

	if (!strcasecmp(msg->key,"X-UUCP-From")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"X-Body-Start")) return (rfclevel-2);
	if (!strncasecmp(msg->key,".",1)) return 0;
	if (!strncasecmp(msg->key,"X-FTN-",6)) return 0;
	if (!strncasecmp(msg->key,"X-Fsc-",6)) return 0;
	if (!strncasecmp(msg->key,"X-ZC-",5)) return 0;
	if (!strcasecmp(msg->key,"X-Gateway")) return 0;
	if (!strcasecmp(msg->key,"Path")) return isftnpath(msg->val)?rfclevel-2:rfclevel-1;
	if (!strcasecmp(msg->key,"Newsgroups"))
        {
		if ((hdr("X-Origin-Newsgroups",msg)))
			return 0;
                else if ((!newsmode) || (newsmode && strstr(msg->val,",")))
			return rfclevel;
                else return (rfclevel-2);
        }
	if (!strcasecmp(msg->key,"X-Origin-Newsgroups"))
	{
		if ((!newsmode) || (newsmode && strstr(msg->val,",")))
			return rfclevel;
		else return (rfclevel-2);
	}
	if (!strcasecmp(msg->key,"Control"))
	{
#ifdef ALLOW_CONTROL
		if (strstr(msg->val,"cancel")) return rfclevel;
		 else return (rfclevel-4);
#else
		if (strstr(msg->val,"cancel")) return (rfclevel-3);
 		else return (rfclevel-4);
#endif
	}
#ifdef ALLOW_RETURNPATH
	if (!strcasecmp(msg->key,"Return-Path")) return rfclevel;
#else
	if (!strcasecmp(msg->key,"Return-Path")) return (rfclevel-1);
#endif
	if (!strcasecmp(msg->key,"Xref")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"Approved"))
		return removeapproved?(rfclevel-2):(rfclevel+1);
	if (!strcasecmp(msg->key,"X-URL")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"Return-Receipt-To")) 
		return removereturnto?(rfclevel-1):rfclevel;
	if (!strcasecmp(msg->key,"Notice-Requested-Upon-Delivery-To"))
		return (rfclevel-2);
	if (!strcasecmp(msg->key,"Received"))
	{
		if (newsmode) return (rfclevel-2);
		else return ftnorigin?(rfclevel-1):rfclevel;
	}
	if (!strcasecmp(msg->key,"From"))
	{
		if ((ta=parsefaddr(msg->val)))
		{
			tidy_faddr(ta);
			return (rfclevel-2);
		}
		else return removefrom?(rfclevel-1):(rfclevel+3);
	}
	if (!strcasecmp(msg->key,"To"))
	{
		if (newsmode) return (rfclevel-2);
		if ((ta=parsefaddr(msg->val)))
		{
			tidy_faddr(ta);
			return (rfclevel-2);
		}
		else return (rfclevel+3);
	}
	if (!strcasecmp(msg->key,"Cc")) return (rfclevel+1);
	if (!strcasecmp(msg->key,"Bcc")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"Reply-To"))
	{
		if ((ta=parsefaddr(msg->val)))
		{
			tidy_faddr(ta);
			return (rfclevel-2);
		}
		else return removereplyto?(rfclevel-1):(rfclevel+3);
	}
	if (!strcasecmp(msg->key,"Lines")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"Date")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"Subject")) 
	{
		if ((msg->val) && (strlen(msg->val) > MAXSUBJ)) return (rfclevel+1);
		else return (rfclevel-1);
	}
	if (!strcasecmp(msg->key,"Organization")) return removeorg?(rfclevel-1):rfclevel; 
	if (!strcasecmp(msg->key,"Comment-To")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"X-Comment-To")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"X-Apparently-To")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"Apparently-To")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"X-Fidonet-Comment-To")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"Keywords")) return (rfclevel+1);
	if (!strcasecmp(msg->key,"Summary")) return (rfclevel+1);
	if (!strcasecmp(msg->key,"MIME-Version")) return removemime?(rfclevel-1):rfclevel;
	if (!strcasecmp(msg->key,"Content-Type")) return removemime?(rfclevel-1):rfclevel;
	if (!strcasecmp(msg->key,"Content-Length")) return removemime?(rfclevel-1):rfclevel;
	if (!strcasecmp(msg->key,"Content-Transfer-Encoding")) return removemime?(rfclevel-1):rfclevel;
	if (!strcasecmp(msg->key,"Content-Name")) return (rfclevel+1);
	if (!strcasecmp(msg->key,"Content-Description")) return (rfclevel+1);
	if (!strcasecmp(msg->key,"Message-ID")) return removemsgid?(rfclevel-2):rfclevel;
	if (!strcasecmp(msg->key,"References")) return removeref?(rfclevel-2):rfclevel;
	if (!strcasecmp(msg->key,"In-Reply-To")) return removeinreply?(rfclevel-2):rfclevel;
	if (!strcasecmp(msg->key,"Supersedes")) return removesupersedes?(rfclevel-1):rfclevel;
	if (!strcasecmp(msg->key,"Distribution")) return ftnorigin?(rfclevel-2):(rfclevel-1);
	if (!strcasecmp(msg->key,"X-Newsreader")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"X-Mailer")) return (rfclevel-1);
	if (!strcasecmp(msg->key,"User-Agent")) return (rfclevel-1);
	if (!strncasecmp(msg->key,"NNTP-",5)) return (rfclevel-1);
	if (!strncasecmp(msg->key,"X-Trace",7)) return (rfclevel-1);
	if (!strncasecmp(msg->key,"X-Complaints",12)) return (rfclevel-1);
	if (!strncasecmp(msg->key,"X-MSMail",9)) return 0;
	if (!strncasecmp(msg->key,"X-MimeOLE",9)) return 0;
	if (!strncasecmp(msg->key,"X-MIME-Autoconverted",20)) return 0;
	if (!strcasecmp(msg->key,"X-Origin-Date")) return (rfclevel-2);
	if (!strncasecmp(msg->key,"X-PGP-",6)) return (rfclevel-4);
	if (!strncasecmp(msg->key,"Resent-",7)) return (rfclevel-2);
	if (!strcasecmp(msg->key,"X-Mailing-List")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"X-Loop")) return (rfclevel-2);
	if (!strcasecmp(msg->key,"Precedence")) return (rfclevel-2);
	/*if (!strcasecmp(msg->key,"")) return ;*/
	return rfclevel;
}

/* in real life, this never happens...  I probably should drop it... */
int isftnpath(path)
char *path;
{
	char *p,*q,*r;
	faddr *fa;
	int nextftn=1,prevftn=1;

	debug(3,"checking path \"%s\" for being clear ftn",S(path));
	p=xstrcpy(path);
	if (p == NULL) return 0;
	for (q=p,r=strchr(q,'!');*q;q=r,r=strchr(q,'!'))
	{
		if (r == NULL) r=q+strlen(q);
		else *r++='\0';
		while ((*q) && (isspace(*q))) q++;
		prevftn=nextftn;
		if ((fa=parsefaddr(q)))
		{
			debug(3,"\"%s\" is ftn address",q);
			tidy_faddr(fa);
		}
		else
		{
			debug(3,"\"%s\" is not ftn address",q);
			nextftn=0;
		}
	}
	free(p);
	debug(3,"this is%s clear ftn path",prevftn?"":" not");
	return prevftn;
}

static char *months[] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
};
static char *weekday[] = {
"Sun","Mon","Tue","Wed","Thu","Fri","Sat"
};

char *viadate(void);
char *viadate(void)
{
	static char buf[64];
	time_t t;
	struct tm *ptm;

	time(&t);
	ptm=localtime(&t);
	sprintf(buf,"%s %s %d %d at %02d:%02d",
		weekday[ptm->tm_wday],months[ptm->tm_mon],
		ptm->tm_mday,ptm->tm_year+1900,ptm->tm_hour,ptm->tm_min);
	return buf;
}

int putmessage(msg,fmsg,fp,route,flavor,sbl,incode,outcode)
rfcmsg *msg;
ftnmsg *fmsg;
FILE *fp;
faddr *route;
char flavor;
fa_list **sbl;
int incode, outcode;
{
	char buf[BUFSIZ],*p,*q,newsubj[4 * (MAXSUBJ+1)],*oldsubj;
	rfcmsg *tmp;
	int rfcheaders;
	int needsplit,hdrsize,datasize,splitpart,forbidsplit;
	int sot_kludge, eot_kludge;
	int qp_or_base64; /* 0=plain text, 1=quoted-printable, 2=base64 */
#ifdef FSCHTML
	int html_message;
#endif
	int tinyorigin=0;
	fa_list *tmpl;
	fa_list *ptl=NULL;
	faddr *ta;
	int seenlen;
	int oldnet;
	int i;
	char *acup_a=NULL;
	unsigned long acup_n=0;
	char sbe[16];
	FILE *pkt;
#ifdef JE
	time_t t;
	struct tm *ptm;
#endif
#ifdef FSC_0047
	struct tm *ptmsplit;
	int splitparttotal;
#endif
	static char hostname[MAXHOSTNAMELEN];


	debug(3,"putmessage from %s",ascfnode(fmsg->from,0x7f));
	debug(3,"putmessage   to %s",ascfnode(fmsg->to,0x7f));
	debug(3,"putmessage subj %s",S(fmsg->subj));
	debug(3,"putmessage flags %04x",fmsg->flags);
	debug(3,"putmessage msgid %s %lx",S(fmsg->msgid_a),fmsg->msgid_n);
	debug(3,"putmessage reply %s %lx",S(fmsg->reply_a),fmsg->reply_n);
	debug(3,"putmessage date %s",ftndate(fmsg->date));
	if (newsmode) debug(3,"putmessage area %s",S(fmsg->area));

	removemime=0;
	removeorg=0;
	removemsgid=0;
	removeref=0;
	removeinreply=0;
	removesupersedes=0;
	removeapproved=0;
	removefrom=1;
	removereplyto=1;
	removereturnto=1;
	ftnorigin=fmsg->ftnorigin;
	sot_kludge=0;
	eot_kludge=0;
	qp_or_base64=0;
#ifdef FSCHTML
	html_message=0;
#endif

	if ((hdr("X-PGP-Signed",msg)))
		pgpsigned=1;
	q=hdr("Content-Transfer-Encoding",msg);
	if (q) while (*q && isspace(*q)) q++;
	if (!(q)) q="8bit"; 
	if ((p=hdr("Content-Type",msg)))
	{
		while (*p && isspace(*p)) p++;

		/* turn the quoted-printable decode mode on; remember FTN is
			virtually 8-bit clean */
		if ((strncasecmp(p, "text/plain", 10) == 0) &&
		    (strncasecmp(q, "quoted-printable", 16) == 0))
			qp_or_base64 = 1;
		/* turn the base64 decode mode on */
		else if ((strncasecmp(p, "text/plain", 10) == 0) &&
			 (strncasecmp(q, "base64", 6) == 0))
			qp_or_base64 = 2;
	/* This is already done in ifmail.c */
	/*	else if ((strncasecmp(p, "application/pgp", 15) == 0))
			pgpsigned=1; */

#ifdef FSCHTML
		/* text/html support from FSC-HTML 001 proposal of 
			Odinn Sorensen (2:236/77) */ 
		if (strncasecmp(p, "text/html", 9) == 0)
			html_message = 1;
		for (tmp=msg;tmp;tmp=tmp->next)
		if (((strcasecmp(tmp->key,"X-FTN-KLUDGE") == 0) &&
		     (strcasecmp(tmp->val,"FSCHTML") == 0)) ||
		    (strcasecmp(tmp->key,"X-FTN-HTML") == 0))
			html_message = 0;
#endif

		if ((readcharset(p) != CHRS_NOTSET ) &&
		    ((q == NULL) ||
		     (strncasecmp(q,"7bit",4) == 0) ||
	             ((!pgpsigned) && (qp_or_base64==1)) ||
		     ((!pgpsigned) && (qp_or_base64==2)) ||
		     (strncasecmp(q,"8bit",4) == 0)))
			removemime=1; /* no need in MIME headers */
	/* some old MUA puts "text" instead of "text/plain; charset=..." */
		else if ((strcasecmp(p,"text\n") == 0))
			removemime=1;

	}

	if ((p=hdr("Message-ID",msg)))
	{
		if ((rfclevel < 1) && (ftnmsgidstyle) && (q=strchr(p,'@')))
		{
			q++;
			if (strncasecmp(q,ftnmsgidstyle,strlen(ftnmsgidstyle))==0)
				removemsgid=1;
				removeref=1;
		}
		if (!removemsgid)
			removemsgid=chkftnmsgid(p);
	}
		
	if ((!removeref) && (newsmode) && (p=hdr("References",msg)))
	{
		p=xstrcpy(p);
		q=strtok(p," \t\n");
		if ((q) && (strtok(NULL," \t\n") == NULL))
			removeref=chkftnmsgid(q);	
		free(p);
	}

	if ((p=hdr("In-Reply-To",msg)) && (!newsmode))
	{
		p=xstrcpy(p);
		q=strtok(p," \t\n");
		if ((q) && (strtok(NULL," \t\n") == NULL))
			removeinreply=chkftnmsgid(q);
		free(p);
	}

	if ((p=hdr("Supersedes",msg)))
		removesupersedes=chkftnmsgid(p);

	if ((newsmode) && (p=hdr("Approved",msg)))
	{
		int modtype;
		char *newsgroup,*distribution,*moderator;

		while(*p && isspace(*p)) p++;
		if ((q=strchr(p,'\n'))) *q='\0';
		/* ngdist(area,newsgroup,distribution,moderator,modtype) */
		ngdist(fmsg->area,&newsgroup,&distribution,&moderator,&modtype);
		if ((modtype > 1) && (strcasestr(moderator,p)))
			removeapproved=1;
		if (q) *q='\n';
	}

	if ((p=hdr("From",msg)))
	{
		if (rfclevel > -2) removefrom=0;
		if ((rfclevel < 1) && !(removefrom))
		{
			if ((!gethostname(hostname,sizeof(hostname)-1)) &&
			    (strcasestr(p,hostname))) removefrom=1;
			else if ((localdomain) && (q=strchr(p,'@')))
			{
                		q++;
                		if (strcasestr(q,localdomain))
						removefrom=1;
			}
		}
	}

	if ((p=hdr("Reply-To",msg)))
	{
		if (rfclevel > -2) removereplyto=0;
		if ((rfclevel < 1) && !(removereplyto))
		{
			if ((!gethostname(hostname,sizeof(hostname)-1)) &&
			    (strcasestr(p,hostname))) removereplyto=1;
			else if ((localdomain) && (q=strchr(p,'@')))
			{
				q++;
				if (strcasestr(q,localdomain))
					removereplyto=1;
			}
		}
		if ((!removereplyto) && (q=hdr("From",msg)))
		{
			char *r;
			r=xstrcpy(p); p=r;
			while(*p && isspace(*p)) p++;
			if (p[strlen(p)-1] == '\n')
				p[strlen(p)-1]='\0';
			if (strcasestr(q,p))
				removereplyto=1;
			free(r);
		}
	}

	if ((p=hdr("Return-Receipt-To",msg)))
	{
		if (rfclevel > -1) removereturnto=0;
		if ((rfclevel < 1) && !(removereturnto))
		{
			if ((!gethostname(hostname,sizeof(hostname)-1)) &&
			    (strcasestr(p,hostname))) removereturnto=1;
			else if ((localdomain) && (q=strchr(p,'@')))
			{
				q++;
				if (strcasestr(q,localdomain))
					removereturnto=1;
			}
		}
		if ((!removereturnto) && (q=hdr("From",msg)))
		{
			char *r;
			r=xstrcpy(p); p=r;
			while(*p && isspace(*p)) p++;
			if (p[strlen(p)-1] == '\n') p[strlen(p)-1]='\0';
			if (strcasestr(q,p)) removereturnto=1;
			free(r);
		}
	}

	p=ascfnode(fmsg->from,0x1f);
	i=79-11-3-strlen(p);
	if (ftnorigin && fmsg->origin && (strlen(fmsg->origin) > i))
	{
		/* This is a kludge...  I don't like it too much.  But well,
		   if this is a message of FTN origin, the original origin (:)
		   line MUST have been short enough to fit in 79 chars...
		   So we give it a try.  Probably it would be better to keep
		   the information about the address format from the origin
		   line in a special X-FTN-... header, but this seems even
		   less elegant.  Any _good_ ideas, anyone? */

		/* OK, I am keeping this, though if should never be used
		   al long as X-FTN-Origin is used now */

		p=ascfnode(fmsg->from,0x0f);
		i=79-11-3-strlen(p);
		tinyorigin=1;
	}
	if (fmsg->origin)
	{
		if (strlen(fmsg->origin) > i)
			fmsg->origin[i]='\0';
		else
			removeorg=1;
	}
	forbidsplit=(nosplitmode || ftnorigin || (hdr("X-FTN-Split",msg)));
	needsplit=0;
#ifdef FSC_0047
	time(&t);
	ptmsplit=localtime(&t);
#endif
	splitpart=0;
	hdrsize=20;
	hdrsize+=(fmsg->subj)?strlen(fmsg->subj):0;
	hdrsize+=(fmsg->from->name)?strlen(fmsg->from->name):0;
	hdrsize+=(fmsg->to->name)?strlen(fmsg->to->name):0;
	do
	{
		datasize=0;

		if (splitpart)
		{
#ifdef GATEBAU_MSGID
		if (areagatebau(fmsg->area))	
			sprintf(newsubj,"%02d: ",splitpart+1);
		else
#endif
			sprintf(newsubj,"[part %d] ",splitpart+1);
			strncat(newsubj,fmsg->subj,MAXSUBJ-strlen(newsubj));
		}
		else
		{
			strncpy(newsubj,fmsg->subj,MAXSUBJ);
		}
		strcpy(newsubj, hdrnconv(newsubj, incode, outcode, MAXSUBJ));
		newsubj[MAXSUBJ]='\0';

		if (splitpart)
		{
#ifdef GATEBAU_MSGID
		if (areagatebau(fmsg->area))
			fmsg->msgid_n += splitpart;
		else
#endif
			hash_update_n(&fmsg->msgid_n,splitpart);
		}

		oldsubj=fmsg->subj;
		fmsg->subj=newsubj;
		if ((pkt=ftnmsghdr(fmsg,NULL,route,flavor)) == NULL)
		{
			return 1;
		}
		fmsg->subj=oldsubj;

#ifdef FSC_0047
		if (splitpart)
		{
/* For this we need to know, BEFORE writting the message, how many split
 * parts will be needed. It is not possible with the method actually used
 * here, maybe a day...
 */

/* Field 3 ("123" in the exemple) is intended to be the number of the message
 * in an *.MSG message base :)
 * as there is nothing similar with ifmail I put the number of day (1-365)
 */

/* How to retrieve this number ??? (I put a fake 24 here, for testing, but it
 * must contain the total number of splitted messages)
 */
                        splitparttotal=24;

/* The kludge line itself */
/*                    1         2         3         4         5
 *          0123456789012345678901234567890123456789012345678901234
 *  @SPLIT: 11 Jun 96 05:49:33 @293/2219    123   10/24 +++++++++++
 */

			fprintf(pkt,"\1SPLIT: %02d %s %02d %02d:%02d:%02d @%-11s %-5d %02d/%02d +++++++++++\r",
				ptmsplit->tm_mday,months[ptmsplit->tm_mon],
				ptmsplit->tm_year%100,ptmsplit->tm_hour,
				ptmsplit->tm_min,ptmsplit->tm_sec,
				ascfnode(bestaka,0x06),ptmsplit->tm_yday,
				splitpart,splitparttotal);
		}
#endif

		if ((p=hdr("X-FTN-REPLYADDR",msg)))
		{
			hdrsize += 10+strlen(p);
			fprintf(pkt,"\1REPLYADDR:");
			kwrite(p,pkt);
		}
		else if ((replyaddr) && ((!removereplyto) || (!removefrom)))
		{
			hdrsize += 10+strlen(replyaddr);
			fprintf(pkt,"\1REPLYADDR: ");
			kwrite(replyaddr,pkt);
		}

		if ((p=hdr("X-FTN-REPLYTO",msg)))
		{
			hdrsize += 8+strlen(p);
			fprintf(pkt,"\1REPLYTO:");
			kwrite(p,pkt);
		}
		else if ((replyaddr) && ((!removereplyto) || (!removefrom)))
		{
			hdrsize += 15;
			if (magicname)
				fprintf(pkt,"\1REPLYTO: %s %s\r",
					ascfnode(bestaka,0x1f),
					magicname);
			else
				fprintf(pkt,"\1REPLYTO: %s UUCP\r",
					ascfnode(bestaka,0x1f));
		}
		else if ((p=hdr("Reply-To",msg)))
		{
			if ((ta=parsefaddr(p)))
			{
                		if ((q=hdr("From",msg)))
                		{
                		        if (!strcasestr(q,p))
						fprintf(pkt,"\1REPLYTO: %s %s\r",
							ascfnode(ta,0x1f),
							ta->name);
						tidy_faddr(ta);
				}
			}
		}

		if ((p=strip_flags(hdr("X-FTN-FLAGS",msg))))
		{
			hdrsize += 15;
			fprintf(pkt,"\1FLAGS:%s\r",p);
			free(p);
		}

#ifndef ADD_PID
		if (rfclevel==1)
#endif
		if (!hdr("X-FTN-PID", msg)) { 
			p=hdr("User-Agent", msg);
			if (p==NULL) p=hdr("X-Newsreader", msg);
			if (p==NULL) p=hdr("X-Mailer", msg);
			if (p)
			{
				hdrsize += 4 + strlen(p);
				fprintf(pkt, "\1PID:");
				kwrite(p, pkt);
			}
		}

		hdrsize += 15;
		writechrs(outcode,pkt,1);

#ifdef FSCHTML
		if (html_message)
		{
			hdrsize += 9;
			fprintf(pkt, "\1HTML: 5\r");
		}
#endif

#ifdef ALLOW_CONTROL
		if ((!hdr("X-FTN-ACUPDATE",msg)) && (p=hdr("Control",msg)))
		{
			if (strstr(p,"cancel"))
			{
				ftnmsgid(p,&acup_a,&acup_n,fmsg->area);
				if (acup_a) {
#ifdef GATEBAU_MSGID
				if (!areagatebau(fmsg->area))
				{
					hash_update_n(&acup_n,fmsg->area);
				}
#else
				hash_update_n(&acup_n,fmsg->area);
#endif
				hdrsize += 26 + strlen(acup_a);
				fprintf(pkt,"\1ACUPDATE: DELETE %s %08lx\r",
								acup_a,acup_n);
				}
			}
		}
#endif

		if ((!hdr("X-FTN-ACUPDATE",msg)) && (p=hdr("Supersedes",msg)))
		{
       		        ftnmsgid(p,&acup_a,&acup_n,fmsg->area);
			if (acup_a) {
#ifdef GATEBAU_MSGID
			if (!areagatebau(fmsg->area))
				hash_update_n(&acup_n,fmsg->area);
#else
			hash_update_n(&acup_n,fmsg->area);
#endif
			hdrsize += 26 + strlen(acup_a);
			fprintf(pkt,"\1ACUPDATE: MODIFY %s %08lx\r",
							acup_a,acup_n);
			}
	        }

#ifdef FSC_0070
		/* FSC-0070 */
		if((p = hdr("Message-ID", msg))
		 && !(hdr("X-FTN-RFCID", msg))) {
			q = strdup(p);
			fprintf(pkt,"\1RFCID:");
			if ((l = strrchr(q, '<')) && (r = strchr(q, '>'))
			 && (l < r)) {
				*l++ = ' ';
				while(*l && isspace(*l))
					l++;
				l--; /* leading ' ' */
				*r-- = '\0';
				while(*r && isspace(*r))
					*r-- = '\0';
			}
			else
				l = q;
			kwrite(l, pkt);
			hdrsize += 6 + strlen(l);
			free(q);
		}
#endif /* FSC_0070 */

#ifdef KEEP_TEARLINES_CLEAN
		if (!(hdr("X-FTN-Tearline", msg))
		 && !(hdr("X-FTN-TID", msg)))
		{
			sprintf(buf, " ifmail %s", version);
			hdrsize += 4 + strlen(buf);
			fprintf(pkt, "\1TID:");
			kwrite(buf, pkt);
		}
#endif /* KEEP_TEARLINES_CLEAN */

		if ((splitpart == 0) || (hdrsize < MAXHDRSIZE))
		{
			for (tmp=msg;tmp;tmp=tmp->next) 
			if ((!strncmp(tmp->key,"X-Fsc-",6)) ||
			    (!strncmp(tmp->key,"X-FTN-",6) &&
			    strcasecmp(tmp->key,"X-FTN-Tearline") &&
			    strcasecmp(tmp->key,"X-FTN-Origin") &&
			    strcasecmp(tmp->key,"X-FTN-Sender") &&
			    strcasecmp(tmp->key,"X-FTN-Split") &&
			    strcasecmp(tmp->key,"X-FTN-FLAGS") &&
			    strcasecmp(tmp->key,"X-FTN-AREA") &&
			    strcasecmp(tmp->key,"X-FTN-MSGID") &&
			    strcasecmp(tmp->key,"X-FTN-REPLY") &&
			    strcasecmp(tmp->key,"X-FTN-SEEN-BY") &&
			    strcasecmp(tmp->key,"X-FTN-PATH") &&
			    strcasecmp(tmp->key,"X-FTN-REPLYADDR") &&
			    strcasecmp(tmp->key,"X-FTN-REPLYTO") &&
			    strcasecmp(tmp->key,"X-FTN-To") &&
			    strcasecmp(tmp->key,"X-FTN-From") &&
			    strcasecmp(tmp->key,"X-FTN-CHARSET") &&
		            strcasecmp(tmp->key,"X-FTN-CHRS") &&
			    strcasecmp(tmp->key,"X-FTN-CODEPAGE") &&
			    strcasecmp(tmp->key,"X-FTN-ORIGCHRS") &&
   			    strcasecmp(tmp->key,"X-FTN-SOT") &&
			    strcasecmp(tmp->key,"X-FTN-EOT") &&
			    strcasecmp(tmp->key,"X-FTN-Via")))
			if (strcasecmp(tmp->key,"X-FTN-KLUDGE") == 0)
			{
				if (!strcasecmp(tmp->val," SOT:\n"))
					sot_kludge=1;
				else if (!strcasecmp(tmp->val," EOT:\n"))
					eot_kludge=1;
				else
				{
					hdrsize += strlen(tmp->val);
					fprintf(pkt,"\1");
		/* we should have restored the original string here... */
					kwrite((tmp->val)+1,pkt);
				}
			}
			else
			{
				hdrsize += strlen(tmp->key)+strlen(tmp->val);
				fprintf(pkt,"\1%s:",tmp->key+6);
				kwrite(tmp->val,pkt);
			}
			/* ZConnect are X-ZC-*: in usenet, \1ZC-*: in FTN */
                        for (tmp=msg;tmp;tmp=tmp->next)
                        if ((!strncmp(tmp->key,"X-ZC-",5)))
			{
				hdrsize += strlen(tmp->key)+strlen(tmp->val);
				fprintf(pkt,"\1%s:",tmp->key+2);
				kwrite(tmp->val,pkt);
			}
			/* mondo.org gateway uses ".MSGID: ..." in usenet */
			for (tmp=msg;tmp;tmp=tmp->next)
			if ((!strncmp(tmp->key,".",1)) &&
			    (strcasecmp(tmp->key,".MSGID")))
			{
				hdrsize += strlen(tmp->key)+strlen(tmp->val);
				fprintf(pkt,"\1%s:",tmp->key+1);
				kwrite(tmp->val,pkt);
			}
#ifdef GATEBAU_MSGID
			if ((!ftnorigin) && (rfclevel > 0) &&
			    (areagatebau(fmsg->area)))
			{
				hdrsize += 12; 
				fprintf(pkt,"\1X-GATEWAY:");
#ifdef XGATEWAY_STRICT
				if ((p=hdr("X-Gateway",msg)))
				{
					hdrsize += strlen(p);
					if (q=strchr(p,'\n')) *q='\0';
					fprintf(pkt," %s,",p);
					if (q) *q='\n';
				}
#endif
				hdrsize += 24+strlen(myfqdn)+strlen(version); 
				fprintf(pkt," RFC1036/822 %s [ifmail %s]\r",
					myfqdn,version);
			}
#endif


			rfcheaders=0;
			for (tmp=msg;tmp;tmp=tmp->next)
#ifdef KEEP_MSGID_ON_SPLIT
			if ((needputrfc(tmp) == 1))
#else
			if ((needputrfc(tmp) == 1) &&
			    ((strcasecmp(tmp->key,"Message-ID")) ||
			     (splitpart == 0)))
#endif
			{
			    if (strcasestr("X-Origin-Newsgroups",tmp->key)) {
				hdrsize += 10+strlen(tmp->val);
				fprintf(pkt,"\1RFC-Newsgroups:");
			    } else {
				hdrsize += strlen(tmp->key)+strlen(tmp->val);
				fprintf(pkt,"\1RFC-%s:",tmp->key);
			    }
				kwrite(hdrconv(tmp->val, incode, outcode),pkt);
			}
			for (tmp=msg;tmp;tmp=tmp->next)
#ifdef KEEP_MSGID_ON_SPLIT
			if ((needputrfc(tmp) > 1))
#else
			if ((needputrfc(tmp) > 1) &&
			    ((strcasecmp(tmp->key,"Message-ID")) ||
			     (splitpart == 0)))
#endif
			{
				rfcheaders++;
			    if (strcasestr("X-Origin-Newsgroups",tmp->key)) {
				hdrsize += 10+strlen(tmp->val);
				fprintf(pkt,"Newsgroups:");
			    } else {
				hdrsize += strlen(tmp->key)+strlen(tmp->val);
				fprintf(pkt,"%s:",tmp->key);
			   }
				cwrite(hdrconv(tmp->val, incode, outcode),pkt);
			}
			if (rfcheaders) cwrite("\n",pkt);
			if ((hdr("X-FTN-SOT",msg)) || (sot_kludge))
				fprintf(pkt,"\1SOT:\r");
			if ((splitpart == 0) && (hdr("X-PGP-Signed",msg)))
				fprintf(pkt,PGP_SIGNED_BEGIN"\r");
		}

		if (replyaddr)
		{
			free(replyaddr);
			replyaddr=NULL;
		}
/* */
		if (needsplit)
		{
#ifndef FSC_0047
			fprintf(pkt," * Continuation %d of a split message *\r\r",
				splitpart);
#endif
			needsplit=0;
		}
		else if ((p=hdr("X-Body-Start",msg))) 
		{
			datasize += strlen(p);
			if (qp_or_base64==1)
				cwrite(strkconv(qp_decode(p), incode, outcode), pkt);
			else if (qp_or_base64==2)
				cwrite(strkconv(b64_decode(p), incode, outcode), pkt);
			else
				cwrite(strkconv(p, incode, outcode), pkt);
		}
		while (!(needsplit=(!forbidsplit) &&
				(((splitpart &&
				   (datasize > maxmsize)) ||
				  (!splitpart &&
				   ((datasize+hdrsize) > maxmsize))))
							) &&
			(bgets(buf,sizeof(buf)-1,fp)))
		{
			debug(19,"putmessage body %s",buf);
			datasize += strlen(buf);
			if (qp_or_base64==1)
				cwrite(strkconv(qp_decode(buf), incode, outcode), pkt);
			else if (qp_or_base64==2)
				cwrite(strkconv(b64_decode(buf), incode, outcode), pkt);
			else
				cwrite(strkconv(buf, incode, outcode), pkt);
		}
		if (needsplit)
		{
#ifndef FSC_0047
			fprintf(pkt,"\r * Message split, to be continued *\r");
#endif
			splitpart++;
		}
		else if ((p=hdr("X-PGP-Signed",msg)))
		{
			fprintf(pkt,PGP_SIG_BEGIN"\r");
			if ((q=hdr("X-PGP-Version",msg)))
			{
				fprintf(pkt,"Version:");
				cwrite(q,pkt);
			}
			if ((q=hdr("X-PGP-Charset",msg)))
			{
				fprintf(pkt,"Charset:");
				cwrite(q,pkt);
			}
			if ((q=hdr("X-PGP-Comment",msg)))
			{
				fprintf(pkt,"Comment:");
				cwrite(q,pkt);
			}
			fprintf(pkt,"\r");
			p=xstrcpy(p);
			q=strtok(p," \t\n");
			fprintf(pkt,"%s\r",q);
			while ((q=(strtok(NULL," \t\n"))))
				fprintf(pkt,"%s\r",q);
			fprintf(pkt,PGP_SIG_END"\r");
		}
/* */

		/* putbody(0,msg,fp,pkt,forbidsplit,hdrsize,incode,outcode,qp_or_base64); */

		if ((p=hdr("X-FTN-EOT",msg)) || (eot_kludge))
			fprintf(pkt,"\1EOT:\r");

		if ((p=hdr("X-FTN-Tearline",msg)))
		{
			fprintf(pkt,"---");
			if (strcasecmp(p," (none)\n") == 0)
				cwrite("\n",pkt);
			else
				cwrite(p,pkt);
		}
		else if (newsmode)
#ifdef KEEP_TEARLINES_CLEAN
			fprintf(pkt,"---\r");
#else
#ifdef REPLACE_TEARLINES_WITH
			fprintf(pkt,"--- "/**/REPLACE_TEARLINES_WITH/**/"\r");
#else
			fprintf(pkt,"--- ifmail v.%s\r",version);
#endif /* REPLACE_TEARLINES_WITH */
#endif /* KEEP_TEARLINES_CLEAN */

		if ((p=hdr("X-FTN-Origin",msg)))
		{
			if (*(q=p+strlen(p)-1) == '\n') *q='\0';
			fprintf(pkt," * Origin:");
			cwrite(hdrconv(p, incode, outcode),pkt);
			if (!newsmode) fprintf(pkt,"\r");
		}
		else if (newsmode)
		{
			fprintf(pkt," * Origin: "); /* strlen=11 */
			if (fmsg->origin)
				cwrite(hdrconv(fmsg->origin, incode, outcode), pkt);
			else
				cwrite(DEFAULT_ORIGIN,pkt);
			fprintf(pkt," (%s)",
				ascfnode(fmsg->from,tinyorigin?0x0f:0x1f));
		}

		if (newsmode)
		{
			for (tmpl=whoami;tmpl;tmpl=tmpl->next)
			if ((tmpl->addr->point == 0) &&
			    ((bestaka->domain == NULL) ||
			     (tmpl->addr->domain == NULL) ||
			     (strcasecmp(bestaka->domain,
					tmpl->addr->domain) == 0)) &&
			    (bestaka->zone == tmpl->addr->zone))
				fill_list(sbl,ascfnode(tmpl->addr,0x06),NULL);
#ifdef HAS_NDBM_H
			if ((p=hdr("Message-ID",msg)))
			{
				while (isspace(*p)) p++;
				q=xstrcpy(p);
				if (*(p=q+strlen(q)-1) == '\n') *(p--)='\0';
				while (isspace(*p)) *(p--)='\0';
				fill_list(sbl,idlookup(q),NULL);
				free(q);
			}
#endif
			sort_list(sbl);
			seenlen=MAXSEEN+1;
			/* ensure it will not match for the first entry */
			oldnet=(*sbl)->addr->net-1;
			for (tmpl=*sbl;tmpl;tmpl=tmpl->next)
			{
				if (tmpl->addr->net == oldnet)
					sprintf(sbe," %u",tmpl->addr->node);
				else
					sprintf(sbe," %u/%u",tmpl->addr->net,
							tmpl->addr->node);
				oldnet=tmpl->addr->net;
				seenlen+=strlen(sbe);
				if (seenlen > MAXSEEN)
				{
					seenlen=0;
					fprintf(pkt,"\rSEEN-BY:");
					sprintf(sbe," %u/%u",tmpl->addr->net,
							tmpl->addr->node);
					seenlen=strlen(sbe);
				}
				fprintf(pkt,"%s",sbe);
			}

			for (tmp=msg;tmp;tmp=tmp->next) 
			if (!strcasecmp(tmp->key,"X-FTN-PATH"))
			{
				fill_path(&ptl,tmp->val);
			}
			sprintf(sbe,"%u/%u",bestaka->net,
				bestaka->node);
			fill_path(&ptl,sbe);
			uniq_list(&ptl);
			seenlen=MAXPATH+1;
			/* ensure it will not match for the first entry */
			oldnet=ptl->addr->net-1;
			for (tmpl=ptl;tmpl;tmpl=tmpl->next)
			{
				if (tmpl->addr->net == oldnet)
					sprintf(sbe," %u",tmpl->addr->node);
				else
					sprintf(sbe," %u/%u",tmpl->addr->net,
							tmpl->addr->node);
				oldnet=tmpl->addr->net;
				seenlen+=strlen(sbe);
				if (seenlen > MAXPATH)
				{
					seenlen=0;
					fprintf(pkt,"\r\1PATH:");
					sprintf(sbe," %u/%u",tmpl->addr->net,
							tmpl->addr->node);
					seenlen=strlen(sbe);
				}
				fprintf(pkt,"%s",sbe);
			}
			fprintf(pkt,"\r");
		}
		else /* mail mode */
		{
			for (tmp=msg;tmp;tmp=tmp->next) 
			if (!strcasecmp(tmp->key,"X-FTN-Via"))
			{
			datasize += strlen(tmp->key)+strlen(tmp->val);
				fprintf(pkt,"\1Via");
				kwrite(tmp->val,pkt);
			}
#ifdef JE
			/* ### Modified by T.Tanaka on 26 Mar 1995 */
			time(&t);
			ptm=gmtime(&t);
/* @Via 6:730/18@os2net @19950304.052627 FrontDoor 2.12.SW */
/*			fprintf(pkt, "\1Via %s @%04d%02d%02d.%02d%02d%02d.%s ifmail %s\r",
				ascfnode(bestaka,0x1f),
				ptm->tm_year + 1900,
				ptm->tm_mon + 1,
				ptm->tm_mday,
				ptm->tm_hour,
				ptm->tm_min,
				ptm->tm_sec,
				"UTC", version);
*/
/* @Via Squish/386 1.11 6:600/500, Fri Mar 24 1995 at 09:32 UTC */
/*			fprintf(pkt, "\1Via ifmail %s %s, %s %s %d %d at %02d:%02d %s\r",
				version, ascfnode(bestaka,0x1f),
				weekday[ptm->tm_wday], months[ptm->tm_mon],
				ptm->tm_mday, ptm->tm_year + 1900,
				ptm->tm_hour, ptm->tm_min, "UTC");
*/
#endif
/* @Via ifmail 2:293/2219@fidonet, Wed Jan 3 1996 at 07:49 (2.8c) */
			fprintf(pkt,"\1Via ifmail %s, %s (%s)\r",
				ascfnode(bestaka,0x1f),
				viadate(),version);
		}
		awrite("",pkt); /* trailing zero byte */
		if (ferror(pkt))
		{
			logerr("$error writing to ftn packet");
			return 1;
		}
		tidy_falist(&ptl);
	}
	while (needsplit);

	debug(3,"putmessage exiting...");
	return 0;
}

