/*****************************************************************************
 *
 * File ..................: mbcico/mbcico.c
 * Purpose ...............: Fidonet mailer
 * Last modification date : 15-Nov-1999
 *
 *****************************************************************************
 * Copyright (C) 1997-1999
 *   
 * Michiel Broek		FIDO:		2:2801/16
 * Beekmansbos 10		Internet:	mbroek@ux123.pttnwb.nl
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../include/libs.h"
#include "../include/structs.h"
#include "../include/records.h"
#include "../common/common.h"
#include "../dbase/dbcfg.h"
#include "../dbase/dbnode.h"
#include "../dbase/dbftn.h"
#include "config.h"
#include "answer.h"
#include "portsel.h"
#include "call.h"
#include "callall.h"
#include "lutil.h"
#include "mbcico.h"
#include "ftn.h"
#include "session.h"



int		master = 0;
int		forcedcalls = 0;
char		*forcedphone = NULL;
char		*forcedline = NULL;
char		*inetaddr = NULL;
time_t		t_start;
time_t		t_end;
time_t		c_start;
time_t		c_end;
int		online = 0;
unsigned long	sentbytes = 0;
unsigned long	rcvdbytes = 0;
int		tcp_mode = TCPMODE_NONE;
#ifdef NOISEDEBUG
int		junklevel=0;
#endif


extern char	*version;
extern char	*reldate;
extern char	*copyright;
extern char	*myname;
char		*name;
char		*location;
char		*sysop;
char		*outbound;
char		*inbound;
char		*norminbound;
char		*protinbound;
char		*listinbound;
char		*uxoutbound;
char		*phone;
char		*flags;
extern int	gotfiles;
extern int	mypid;


void usage(void)
{
	fprintf(stderr,"ifcico; (c) Eugene G. Crosser, 1993-1997\n");
	fprintf(stderr,"mbcico ver. %s %s; (c) %s\n\n", version, reldate, copyright);
#ifdef NOISEDEBUG
	fprintf(stderr,"-r<role> -j<num> -a<inetaddr> <node> ...\n");
	fprintf(stderr,"-j<num>		damage every <num> byte	[%d]\n",
								junklevel);
#else
	fprintf(stderr,"-r<role> -a<inetaddr> <node> ...\n");
#endif
	fprintf(stderr,"-r 0|1		1 - master, 0 - slave	[0]\n");
	fprintf(stderr,"-n<phone>	forced phone number\n");
	fprintf(stderr,"-l<ttydevice>	forced tty device\n");
	fprintf(stderr,"-t<tcpmode>	must be one of ifc|itn|ibn, forces TCP/IP\n");
	fprintf(stderr,"-a<inetaddr>	supply internet hostname if not in nodelist\n");
	fprintf(stderr,"  <node>	should be in domain form, e.g. f11.n22.z3\n");
	fprintf(stderr,"		(this implies master mode)\n");
	fprintf(stderr,"\n or: %s tsync|yoohoo|**EMSI_INQC816|ibn\n",myname);
	fprintf(stderr,"		(this implies slave mode)\n");
}



void die(int onsig)
{
	int	total = 0;

	signal(onsig, SIG_IGN);

	if (onsig) {
		if (onsig <= NSIG)
			WriteError("$Terminated on signal %d (%s)", onsig, SigName[onsig]);
		else
			Syslog('+', "Terminated with error %d", onsig);
	}

	if (sentbytes || rcvdbytes) {
		total = (int)(c_end - c_start);
		if (!total)
			total = 1;
		Syslog('+', "Sent %lu bytes, received %lu bytes, avg %d cps", 
			    sentbytes, rcvdbytes, (sentbytes + rcvdbytes) / total);
	}

	if (online)
		Syslog('+', "Connected %s", str_time(online));

	if (gotfiles)
		CreateSema((char *)"mailin");

	time(&t_end);
	Syslog(' ', "MBCICO finished in %s", t_elapsed(t_start, t_end));
	ExitClient(onsig);
}



int main(int argc, char *argv[])
{
	int		i, c, uid;
	fa_list		*callist = NULL, **tmpl;
	faddr		*tmp;
	int		rc, maxrc, callno = 0, succno = 0;
	char		*answermode = NULL, *p;
	pid_t		child, waitret;
	int		status;
	struct passwd	*pw;
	char		temp[81];

	/*
	 * The next trick is to supply a fake environment variable
	 * MBSE_ROOT in case we are started from inetd or mgetty,
	 * this will setup the variable so InitConfig() will work.
	 * The /etc/passwd must point to the correct homedirectory.
	 */
	pw = getpwuid(getuid());
	if (getenv("MBSE_ROOT") == NULL) {
		p = xstrcpy((char *)"MBSE_ROOT=");
		p = xstrcat(p, pw->pw_dir);
		putenv(p);
	}

	if (argc < 2) {
		usage();
		exit(101);
	}

	memset(&history, 0, sizeof(history));
	InitConfig();
	InitNode();
	InitFidonet();
	TermInit(1);
	time(&t_start);
	time(&c_start);
	time(&c_end);

	InitClient(pw->pw_name, (char *)"mbcico", CFG.location, CFG.logfile, CFG.cico_loglevel);
	Syslog(' ', " ");
	Syslog(' ', "MBCICO v%s/%s", version, reldate);

	/*
	 * Catch all signals we can, and handle the rest.
	 */
	for (i = 0; i < NSIG; i++) {
		if ((i == SIGINT) || (i == SIGBUS) ||
		    (i == SIGFPE) || (i == SIGSEGV)) {
			signal(i, (void (*))die);
		} else
			signal(i, SIG_DFL);
	}

	p = xstrcpy((char *)"Cmd: mbcico");
	for (i = 1; i < argc; i++) {
		p = xstrcat(p, (char *)" ");
		p = xstrcat(p, argv[i]);
	}
	Syslog(' ', p);

	setmyname(argv[0]);

#ifdef NOISEDEBUG
	while ((c=getopt(argc,argv,"j:r:n:l:t:a:I:h")) != -1)
#else
	while ((c=getopt(argc,argv,"r:n:l:t:a:I:h")) != -1)
#endif
		switch (c) {
#ifdef NOISEDEBUG
			case 'j':	junklevel = atoi(optarg); 
					break;
#endif
			case 'r':	master = atoi(optarg);
					if ((master != 0) && (master != 1)) {
						usage();
						die(101);
					}
					break;

			case 'l':	forcedline = optarg; 
					break;

			case 't':	p = xstrcpy(optarg);
					if (strncmp(p, "ifc", 3) == 0)
						tcp_mode = TCPMODE_IFC;
					else if (strncmp(p, "itn", 3) == 0)
						tcp_mode = TCPMODE_ITN;
					else if (strncmp(p, "ibn", 3) == 0)
						tcp_mode = TCPMODE_IBN;
					else {
						usage();
						die(101);
					}
					break;

			case 'a':	inetaddr = optarg; 
					break;

			case 'n':	forcedphone = optarg; 
					break;

			default:	usage(); 
					die(101);
		}

	/*
	 * Load rest of the configuration
	 */
	name		= xstrcpy(CFG.bbs_name);
	location	= xstrcpy(CFG.location);
	sysop		= xstrcpy(CFG.sysop_name);
	norminbound	= xstrcpy(CFG.inbound);
	inbound		= xstrcpy(CFG.inbound);
	protinbound	= xstrcpy(CFG.pinbound);
	listinbound	= xstrcpy(CFG.inbound);
	outbound	= xstrcpy(CFG.outbound);
	uxoutbound	= xstrcpy(CFG.uxpath);
	phone		= xstrcpy(CFG.Phone);
	flags		= xstrcpy(CFG.Flags);

	tmpl = &callist;

	while (argv[optind]) {
		for (p = argv[optind]; (*p) && (*p == '*'); p++);

		if (strncasecmp(p, "EMSI_NAKEEC3", 12) == 0) {

			Syslog('+', "Detected IEMSI client, starting BBS");
			sprintf(temp, "%s/bin/mbsebbs", getenv("MBSE_ROOT"));
			socket_shutdown(mypid);

			if (execl(temp, "mbsebbs", (char *)NULL) == -1)
				perror("FATAL: Error loading BBS!");

			/*
			 * If this happens, nothing is logged!
			 */
			printf("\n\nFATAL: Loading of the BBS failed!\n\n");
			sleep(3);
			exit(100);
		}

		if ((strcasecmp(argv[optind],"tsync") == 0) ||
		    (strcasecmp(argv[optind],"yoohoo") == 0) ||
		    (strcasecmp(argv[optind],"ibn") == 0) ||
		    (strncasecmp(p,"EMSI_",5) == 0)) {
			master = 0;
			answermode = argv[optind];
			Syslog('-', "Inbound \"%s\" mode", S(answermode));
		} else {
			Syslog('-', "Callist entry \"%s\"", argv[optind]);
			if ((tmp = parsefaddr(argv[optind]))) {
				*tmpl = (fa_list *)xmalloc(sizeof(fa_list));
				(*tmpl)->next = NULL;
				(*tmpl)->addr = tmp;
				tmpl = &((*tmpl)->next);
			} else
				WriteError("Unrecognizable address \"%s\"", argv[optind]);
		}
		optind++;
	}

	if (callist) {
		master = 1;
		forcedcalls = 1;
	}

	/*
	   The following witchkraft about uid-s is necessary to make
	   access() work right.  Unforunately, access() checks the real
	   uid, if mbcico is invoked with supervisor real uid (as when
	   called by uugetty) it returns X_OK for the magic files that
	   even do not have `x' bit set.  Therefore, `reference' magic
	   file requests are taken for `execute' requests (and the
	   actual execution natually fails).  Here we set real uid equal
	   to effective.  If real uid is not zero, all these fails, but
	   in this case it is not necessary anyway.
	*/

	uid=geteuid();
	seteuid(0);
	setuid(uid);
	seteuid(uid);

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

	p = xstrcpy(inbound);
	p = xstrcat(p,(char *)"/tmp/fooinb");
	mkdirs(p);
	free(p);

	maxrc=0;
	if (master) {
		if ((child = fork())) {

			if (child == -1) {
				WriteError("$fork() error");
				die(101);
			}

			while (((waitret = wait(&status)) != -1) && (waitret != child)) {
				WriteError("wait return %d, status %d,%d",
					waitret, status >> 8, status & 0xff);
			}

			/*
			 *  Note: exit(), not die() in next statement. The
			 *  child will call die() and shutdown mbsed.
			 */
			if (status&0xff) 
				kill(getpid(),status&0xff);
			else 
				exit((status>>8)+100);
		}

		if (callist == NULL) 
			callist = callall();

		for (tmpl = &callist; *tmpl; tmpl = &((*tmpl)->next)) {
			callno++;
			rc = call((*tmpl)->addr);
			Syslog('+', "Call to %s %s (rc=%d)",
				ascfnode((*tmpl)->addr,0x1f),
				rc?"failed":"successful",rc);
			if (rc > maxrc) 
				maxrc=rc;
			if (rc == 0) {
				succno++;
				break;
			}
		}
		if (callist == NULL) 
			if (IsSema((char *)"scanout"))
				RemoveSema((char *)"scanout");
	} else {
		/* slave */
		if (!answermode && tcp_mode == TCPMODE_IBN)
			answermode = xstrcpy((char *)"ibn");
		maxrc = answer(answermode);
		callno = 1;
		succno = (maxrc == 0);
	}

	if (callno)
		Syslog('+', "%d of %d calls, maxrc=%d",succno,callno,maxrc);

	if (maxrc)
		die(maxrc+100);
	else
		die(0);
	return 0;
}


