/* ### Modified by P.Saratxaga on 9 Aug 1995 ###
 * - added ALLOW_CONTROL to allow Control: lines to be gated
 * - added a line so if fmsg->to->name is greater than MAXNAME (35),
 *   and "@" or "%" or "!" is in it; then UUCP is used instead. So we can
 *   send messages to old style fido->email gates (that is, using another 
 *   soft than ifmail ;-) ) with long adresses.
 * - REFERENCES_MSC96 added from Marc Schaeffer
 * - added AREAS_HACKING from Marc Schaeffer
 * - added LEVEL stuff
 */
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sysexits.h>
#ifdef HAS_SYSLOG
#include <syslog.h>
#endif
#include "getopt.h"
#include "lutil.h"
#include "xutil.h"
#include "ftn.h"
#include "rfcaddr.h"
#include "falists.h"
#include "rfcmsg.h"
#include "ftnmsg.h"
#include "areas.h"
#include "config.h"
#include "version.h"
#include "trap.h"
#include "hash.h"
#include "needed.h"
#include "nodecheck.h"
#include "charset.h"
#ifdef REFERENCES_MSC96
#include "ref_interface.h"
ref_private_t *ref_dbase;
#endif
#ifdef GATEBAU_MSGID
extern unsigned long crcgatebau(char*);
extern char *rfcmsgid(char*,faddr*,int);
extern int ftnmsgid(char*,char**,unsigned long*,char*);
#endif

#ifndef LEVEL
#define LEVEL   1
#endif
#ifndef FAKEDIR
#define FAKEDIR "/tmp/ifmail/"
#endif

extern int newsmode;
extern char *configname;
extern char passwd[];

extern int putmessage(rfcmsg *,ftnmsg *,FILE *,faddr*,char,fa_list **,int,int);
extern void closepkt(void);
extern char *bgets(char *,int,FILE *);
#ifdef AREAS_HACKING
extern void readareas(char *, char *);
#else
extern void readareas(char *);
#endif
extern void try_attach(char *,int,faddr *,char);
extern int flag_on(char *,char *);
extern unsigned INT16 crc(char *);

int usetmp=0;
faddr *bestaka;
extern int fakeoutbound;
int rfclevel=LEVEL;
int nosplitmode=0;
int pgpsigned;

void usage(void)
{
	confusage("-N -o<flavors> -n -r<addr> -g<grade> <recip> ...");
	fprintf(stderr,_("-N\t\tput packets to %s directory\n"),FAKEDIR);
	fprintf(stderr,_("-o<flavors>\tforce `out' mode for these flavors\n"));
	fprintf(stderr,_("-o+\t\tforce `out' mode for all flavors\n"));
	fprintf(stderr,_("-o-\t\treset `out' mode for all flavors\n"));
	fprintf(stderr,_("-n\t\tset news mode\n"));
	fprintf(stderr,_("-s\t\trun in secure mode (check nodelist)\n"));
	fprintf(stderr,_("-r<addr>\taddress to route packet\n"));
	fprintf(stderr,_("-g<grade>\t[ n | c | h ] \"flavor\" of packet\n"));
#ifdef DIRTY_CHRS
	fprintf(stderr,_("-c<charset>\tforce the given charset\n"));
#endif
	fprintf(stderr,_("-l<level>\tforce the given level (default=%d)\n"),LEVEL);
	fprintf(stderr,_("-b\t\tdon't split the messages\n"));
	fprintf(stderr,_("<recip>\t\tlist of receipient addresses\n"));
}

int main(argc,argv)
int argc;
char *argv[];
{
	int c;
	char *p;
	char buf[BUFSIZ];
	FILE *fp;
	char *routec=NULL;
	faddr *route = NULL;
	fa_list **envrecip, *envrecip_start = NULL;
	int envrecip_count=0;
	area_list *area = NULL, *area_start = NULL;
	int area_count=0;
	int msg_in=0,msg_out=0;
	rfcmsg *msg=NULL,*tmsg;
	ftnmsg *fmsg=NULL;
	faddr *taddr;
	char cflavor='\0',flavor;
	fa_list *sbl = NULL;
	unsigned long svmsgid,svreply;
	char *outmode=NULL;
	int secure=0;
	int incode, outcode;
#ifdef DIRTY_CHRS
	int dirtyoutcode=CHRS_NOTSET;
#endif

#if defined(HAS_SYSLOG) && defined(MAILLOG)
	logfacility=MAILLOG;
#endif

	setmyname(argv[0]);
	catch(myname);

#ifdef DIRTY_CHRS
	while ((c=getopt(argc,argv,"g:r:x:I:c:l:bnNhos")) != -1)
#else
	while ((c=getopt(argc,argv,"g:r:x:I:l:bnNhos")) != -1)
#endif
	if (confopt(c,optarg)) switch (c)
	{
		case 'N':	fakeoutbound=1; break;
		case 'o':	outmode=optarg; break;
		case 'g':	if (optarg && ((*optarg == 'n') || 
				   (*optarg == 'c') || (*optarg == 'h')))
				{
					cflavor=*optarg;
				}
				else 
				{
					usage(); 
					exit(EX_USAGE);
				}
				break;
		case 'r':	routec=optarg; break;
		case 'n':	newsmode=1; break;
		case 's':	secure=1; break;
#ifdef DIRTY_CHRS
		case 'c':	dirtyoutcode=readchrs(optarg);
				if (dirtyoutcode == CHRS_NOTSET)
					dirtyoutcode=getcode(optarg);
				break;
#endif
		case 'l':	rfclevel=atoi(optarg); break;
		case 'b':	nosplitmode=1; break;
		default:	usage(); exit(EX_USAGE);
	}

	if (cflavor == 'n') cflavor='o';

	if (readconfig())
	{
		fprintf(stderr,"Error getting configuration, aborting\n");
		exit(EX_DATAERR);
	}

	if (outmode)
	{
		if (*outmode == '-')
			nonpacked="";
		else if (*outmode == '+')
			nonpacked="och";
		else
			nonpacked=outmode;
	}
	if (nonpacked == NULL) nonpacked="";
	for (p=nonpacked;*p;p++)
	if ((*p == 'f') || (*p == 'n')) *p='o';
	/* constant values will not be modified anyway */

	if ((routec) && ((route=parsefaddr(routec)) == NULL))
		logerr("unparsable route address \"%s\" (%s)",
			S(routec),addrerrstr(addrerror));

	if ((p=strrchr(argv[0],'/'))) p++;
	else p=argv[0];
	if (!strcmp(p,"ifnews")) newsmode=1;

	if (newsmode)
	{
#ifdef REFERENCES_MSC96
		if (refdbm)
			ref_dbase = ref_init(refdbm);
#endif
#ifdef AREAS_HACKING
		if (routec) {
			readareas(areafile, routec);
		}
		else {
			readareas(areafile, getenv("NEWSSITE"));
		}
#else
		readareas(areafile);
#endif
#if defined(HAS_SYSLOG) && defined(NEWSLOG)
		logfacility=NEWSLOG;
#endif
	}
	else
	{
		if (strchr(nonpacked,'m'))
			nonpacked="och";
	}

	envrecip=&envrecip_start;
	while (argv[optind])
	if ((taddr=parsefaddr(argv[optind++]))) 
	{
		if ((!secure) || ((secure) && (chknlent(taddr))))
		{
			(*envrecip)=(fa_list*)xmalloc(sizeof(fa_list));
			(*envrecip)->next=NULL;
			(*envrecip)->addr=taddr;
			envrecip=&((*envrecip)->next);
			envrecip_count++;
		}
		else logerr("unexistent recipient: \"%s\" not in nodelist",
			argv[optind-1]);
	}
	else logerr("unparsable recipient \"%s\" (%s), ignored",
		argv[optind-1],
		addrerrstr(addrerror));

	if ((!newsmode) && (!envrecip_count))
	{
		logerr("No valid receipients specified, aborting");
		exit(EX_NOUSER);
	}

	if (!route && newsmode && envrecip_count)
		route=envrecip_start->addr;

	if (!route)
		if ((routec=getenv("NEWSSITE")))
			route=parsefaddr(routec);

	if (!route)
	{
		logerr("Routing address not specified, aborting");
		exit(EX_USAGE);
	}

	if (fakeoutbound)
	{
		mkdir(FAKEDIR,0777);
		loginf("packets will go to %s",FAKEDIR);
	}

	bestaka=bestaka_s(route);

	for(envrecip=&envrecip_start;*envrecip;envrecip=&((*envrecip)->next))
		loginf("envrecip: %s",ascfnode((*envrecip)->addr,0x7f));
	loginf("route: %s",ascfnode(route,0x1f));

	umask(066); /* packets may contain confidential information */

	while (!feof(stdin))
	{
		usetmp=0;
		tidyrfc(msg);
		msg=parsrfc(stdin);

	incode = outcode = CHRS_NOTSET;
	pgpsigned = 0;

        p=hdr("Content-Type",msg);
        if (p) incode=readcharset(p);
        if (incode == CHRS_NOTSET)
        {
                p=hdr("X-FTN-CHRS",msg);
                if (p == NULL) p=hdr("X-FTN-CHARSET",msg);
		if (p == NULL) p=hdr("X-FTN-CODEPAGE",msg);
                if (p) incode=readchrs(p);
#ifdef JE
		if (!newsmode)
#else
		if (1)
#endif
		{
			if ((p=hdr("Message-ID",msg)) && 
			    (chkftnmsgid(p)))
				incode=defaultftnchar;
			else incode=defaultrfcchar;
		}
        }
	if ((p=hdr("Content-Type",msg)) && 
		((strcasestr(p,"multipart/signed")) ||
		 (strcasestr(p,"application/pgp"))))
		{ pgpsigned=1; outcode=incode; }
#ifndef TOGATE
	else if ((p=hdr("X-FTN-ORIGCHRS",msg))) outcode=readchrs(p);
#endif
#ifdef DIRTY_CHRS
	else if (dirtyoutcode != CHRS_NOTSET)
		outcode=dirtyoutcode;
#endif
#ifdef JE
	else if (!newsmode) outcode=getoutcode(incode);
#else
	else outcode=getoutcode(incode);
#endif 

		flavor=cflavor;

		if (newsmode)
		{
			if (!flavor) flavor='o';
			tidy_arealist(area_start);
			area_start=areas(hdr("Newsgroups",msg),1);
			area_count=0;
			for(area=area_start;area;area=area->next)
			{
				area_count++;
				debug(9,"area: %s",S(area->name));
			}
			tidy_falist(&sbl);
			for (tmsg=msg;tmsg;tmsg=tmsg->next)
				if (strcasecmp(tmsg->key,"X-FTN-SEEN-BY") == 0)
					fill_list(&sbl,tmsg->val,NULL);
		}
		else
		{
			if ((p=hdr("X-FTN-FLAGS",msg)))
			{
				if (!flavor)
				{
					if (flag_on("CRS",p)) flavor='c';
					else if (flag_on("HLD",p)) flavor='h';
					else flavor='o';
				}
				if (flag_on("ATT",p))
					try_attach(hdr("Subject",msg),
						0,route,flavor);
				if (flag_on("TFS",p))
					try_attach(hdr("Subject",msg),
						1,route,flavor);
				if (flag_on("KFS",p))
					try_attach(hdr("Subject",msg),
						2,route,flavor);
			}
			if ((!flavor) && ((p=hdr("Priority",msg)) ||
			         (p=hdr("Precedence",msg)) ||
			         (p=hdr("X-Class",msg))))
			{
				while (isspace(*p)) p++;
				if ((strncasecmp(p,"fast",4) == 0) ||
				    (strncasecmp(p,"high",4) == 0) ||
				    (strncasecmp(p,"crash",5) == 0) ||
				    (strncasecmp(p,"airmail",5) == 0) ||
				    (strncasecmp(p,"special-delivery",5) == 0) ||
				    (strncasecmp(p,"first-class",5) == 0))
					flavor='c';
				else if ((strncasecmp(p,"slow",4) == 0) ||
				         (strncasecmp(p,"low",3) == 0) ||
				         (strncasecmp(p,"hold",4) == 0) ||
				         (strncasecmp(p,"news",4) == 0) ||
				         (strncasecmp(p,"bulk",4) == 0) ||
				         (strncasecmp(p,"junk",4) == 0))
					flavor='h';
			}
			if (!flavor) flavor='o';
		}

		if (((!newsmode) && (envrecip_count > 1)) ||
		    ((newsmode) && (area_count > 1)))
		{
			if ((fp=tmpfile()) == NULL)
			{
				logerr("$Cannot open temporary file");
				exit(EX_OSERR);
			}
			while(bgets(buf,sizeof(buf)-1,stdin))
				fputs(buf,fp);
			rewind(fp);
			usetmp=1;
		}
		else
		{
			fp=stdin;
			usetmp=0;
		}

		if (newsmode && ((area_count < 1) ||
		    (maxgroups && (group_count > maxgroups)) ||
#ifndef ALLOW_CONTROL
		    (hdr("Control",msg) && (rfclevel < 5)) ||
#endif
		     (in_list(route,&sbl))))
		{
			debug(9,"skipping news message");
			while(bgets(buf,sizeof(buf)-1,fp));
		}
		else
		{
			tidy_ftnmsg(fmsg);
			if ((fmsg=mkftnhdr(msg,incode,outcode)) == NULL)
			{
				logerr("Unable to create FTN headers from RFC ones, aborting");
				exit(EX_UNAVAILABLE);
			}

			if (newsmode)
			{
				fill_list(&sbl,ascfnode(bestaka,0x06),NULL);
				fill_list(&sbl,ascfnode(route,0x06),NULL);
				fmsg->to->zone=route->zone;
				fmsg->to->net=route->net;
				fmsg->to->node=route->node;
				fmsg->to->point=route->point;
			}

			if (!newsmode)
			for(envrecip=&envrecip_start;*envrecip;envrecip=&((*envrecip)->next))
			{
				fmsg->to=(*envrecip)->addr;
				if ((!fmsg->to->name) || 
				    ((strlen(fmsg->to->name) > MAXNAME)
				    && ((strstr(fmsg->to->name,"@"))
				    ||  (strstr(fmsg->to->name,"%")) 
				    ||  (strstr(fmsg->to->name,"!"))))) 
					fmsg->to->name="UUCP";
				if (putmessage(msg,fmsg,fp,route,flavor,&sbl,incode,outcode))
				{
					logerr("Unable to put netmail message into the packet, aborting");
					exit(EX_CANTCREAT);
				}
				if (usetmp) rewind(fp);
				fmsg->to=NULL;
				msg_out++;
			}
			else
			for(area=area_start;area;area=area->next)
			{
				fmsg->area=area->name;
				svmsgid=fmsg->msgid_n;
				svreply=fmsg->reply_n;
				if ((p=hdr("Message-ID",msg))) {
					ftnmsgid(p,&fmsg->msgid_a,
						&fmsg->msgid_n,fmsg->area);
#ifdef GATEBAU_MSGID
					if (areagatebau(fmsg->area)) {
						p=xstrcpy(fmsg->msgid_a);
						p=xstrcat(p,(fmsg->area));
						fmsg->msgid_n = crcgatebau(p);
					} else {
#endif
						hash_update_s(&fmsg->msgid_n,
							fmsg->area);
#ifdef REFERENCES_MSC96
						if ((refdbm) && (newsmode) &&
						    (!chkftnmsgid(p)))
			 				ref_store_msgid(ref_dbase,fmsg,p);
#endif
#ifdef GATEBAU_MSGID
					}
#endif
				}
				if ((p=hdr("References",msg)))
				{
					p=strrchr(p,' ');
					ftnmsgid(p,&fmsg->reply_a,
						&fmsg->reply_n,fmsg->area);
				    if (!chkftnmsgid(p)) {
#ifdef GATEBAU_MSGID
					if (areagatebau(fmsg->area)) {
						p=xstrcpy(fmsg->reply_a);
						p=xstrcat(p,(fmsg->area));
						fmsg->reply_n = crcgatebau(p);
					} else {
#endif
						hash_update_s(&fmsg->reply_n,
							fmsg->area);
#ifdef GATEBAU_MSGID
					}
#endif
				    }
				}
				else if ((p=hdr("In-Reply-To",msg)))
				{
					ftnmsgid(p,&fmsg->reply_a,
						&fmsg->reply_n,fmsg->area);
				    if (!chkftnmsgid(p)) {
#ifdef GATEBAU_MSGID
					if (areagatebau(fmsg->area)) {
						p=xstrcpy(fmsg->reply_a);
						p=xstrcat(p,(fmsg->area));
						fmsg->reply_n = crcgatebau(p);
					} else {
#endif
						hash_update_s(&fmsg->reply_n,
							fmsg->area);
#ifdef GATEBAU_MSGID
					}
#endif
				    }
				}

#ifdef JE
				areacharset(fmsg->area, &incode, &outcode);
				if ((incode==CHRS_NOTSET) && (hdr("Message-ID",msg)))
				{
					if (chkftnmsgid(hdr("Message-ID",msg)))
						incode=defaultftnchar;
					else
						incode=defaultrfcchar;
				}
				if (pgpsigned) outcode=incode;
				else if (outcode==CHRS_NOTSET)
					outcode=getoutcode(incode);
#endif
				if (putmessage(msg,fmsg,fp,route,flavor,&sbl,incode,outcode))
				{
					logerr("Unable to put echo message into the packet, aborting");
					exit(EX_SOFTWARE);
				}
				if (usetmp) rewind(fp);
				fmsg->area=NULL;
				fmsg->msgid_n=svmsgid;
				fmsg->reply_n=svreply;
				msg_out++;
			}
			msg_in++;
		}
		if (usetmp) fclose(fp);
	}

	closepkt();

	loginf("end input %d, output %d messages",msg_in,msg_out);

#ifdef REFERENCES_MSC96
	if ((refdbm) && (newsmode))
		ref_deinit(ref_dbase);
#endif
	return EX_OK;
}
