/* Miscellaneous Internet servers: discard, echo and remote
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include <time.h>
#ifdef MSDOS
#include <dos.h>
#endif
#include "global.h"
#include "config.h"
#include "netuser.h"
#include "mbuf.h"
#include "socket.h"
#include "proc.h"
#include "remote.h"
#include "smtp.h"
#include "tcp.h"
#include "nr4.h"
#include "commands.h"
#include "hardware.h"
#include "mailbox.h"

char *Rempass = "";	/* Remote access password */
extern char *Quotes, *MMotd;

static int chkrpass __ARGS((struct mbuf *bp));
static void discserv __ARGS((int s,void *unused,void *p));
static void echoserv __ARGS((int s,void *unused,void *p));
static void quoteserv __ARGS((int s,void *unused,void *p));
static void timeserv __ARGS((int s,void *unused,void *p));
static void daytimeserv __ARGS((int s,void *unused,void *p));
char *getquote __ARGS((void));

static int Rem = -1;

int
deleteserver (mysocket)
int *mysocket;
{
	if (*mysocket != -1)	{
		close_s(*mysocket);
		*mysocket = -1;
	} else
		tprintf ("The server is not active - nothing to stop!\n");
	return 0;
}


/* Start up a TCP server */
int
installserver(argc,argv,mysocket,listener,defport,procname,proc,size,parm)
int argc;
char *argv[];
int *mysocket, defport;
char *listener, *procname;
void (*proc) __ARGS((int,void *,void *));
int size;
void *parm;
{
struct sockaddr_in lsocket;
int s;

	if(*mysocket != -1){
		return 0;
	}
	psignal(Curproc,0); 	/* Don't keep the parser waiting */
	chname(Curproc,listener);

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = defport;
	else
		lsocket.sin_port = atoi(argv[1]);
	*mysocket = socket(AF_INET,SOCK_STREAM,0);
	bind(*mysocket,(char *)&lsocket,sizeof(lsocket));
	listen(*mysocket,1);
	for(;;){
		if((s = accept(*mysocket,NULLCHAR,(int *)NULL)) == -1)
			break;	/* Service is shutting down */

        /* Low mem check now done in tcpin.c - WG7J */
        /* Spawn a server */
        newproc(procname,size,proc,s,parm,NULL,0);
	}
	return 0;
}


#ifdef ALLSERV
static int Sdisc = -1;
static int Stime = -1;
static int Squote = -1;
static int Sdaytime = -1;
static int Secho = -1;
static int Srlogin = -1;
#ifdef TUTOR
int Stutor = -1;
int Sinfo = -1;
int Snews = -1;
#endif

#define DIFFTIME 2208988800

#define T_TIME 		0
#define T_DAYTIME	1
#define	T_DISCARD	2
#define T_ECHO		3
#define T_QUOTE		4

static void
handleserver(s,name,type)
int s, type;
char *name;
{
struct mbuf *bp;
time_t currtime;
char buf[20];

	sockowner(s,Curproc);
	sprintf (buf, "open %s", name);
	log(s,buf);
	switch (type)		{
		case T_DISCARD:		while(recv_mbuf(s,&bp,0,NULLCHAR,NULL) > 0)
						free_p(bp);
					break;
		case T_QUOTE:		{
					char buf2[512], *cp;
					cp = getquote();
					currtime = time (&currtime);
					sprintf (buf2,"\nQuote-of-the-Day at '%s' on %s\n", Hostname, ctime(&currtime));
					bp = qdata (buf2, strlen(buf2));
					send_mbuf (s, bp,0,NULLCHAR, 0);
					bp = qdata (cp, strlen(cp));
					free (cp);
					}
					goto common;
		case T_TIME:
		case T_DAYTIME:		currtime = time (&currtime);
					if (type == T_DAYTIME)	/* daytime protocol */
						bp = qdata (ctime(&currtime), 26);
					else	{
						/*
						 * Change 1970 start time to 1900 start time,
						 * and put it in network order
						 */
						put32(buf,time((time_t *)0)+DIFFTIME);

						/* enqueue for transmission */
						bp = qdata(buf,sizeof(int32));
					}
common:					if (send_mbuf(s,bp,0,NULLCHAR,0) == -1)		{
						sprintf (buf, "%s failed", name);
						log(s,buf);
					}	
					break;
		case T_ECHO:		while(recv_mbuf(s,&bp,0,NULLCHAR,NULL) > 0)
						send_mbuf(s,bp,0,NULLCHAR,0);

		}
	sprintf (buf, "close %s", name);
	log(s,buf);
	close_s(s);
}


/* Start up TCP discard server */
int
dis1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Sdisc, "Discard listener", IPPORT_DISCARD,
		"Discard server", discserv, 576, NULL));
}


static void
discserv(s,unused,p)
int s;
void *unused;
void *p;
{
	handleserver (s, "discard", T_DISCARD);
}

/* Stop discard server */
int
dis0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Sdisc));
}

/* Start up TCP time server */
int
time1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Stime, "Time listener", IPPORT_TIME,
		"Time server", timeserv, 512, NULL));
}

static void
timeserv(s,unused,p)
int s;
void *unused;
void *p;
{
	sockmode(s, SOCK_BINARY);
	handleserver (s, "time", T_TIME);
}

/* Stop time server */
int
time0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Stime));
}

/* Start up TCP daytime server */
int
daytime1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Sdaytime, "Daytime listener", IPPORT_DAYTIME,
		"Daytime server", daytimeserv, 576, NULL));
}

static void
daytimeserv(s,unused,p)
int s;
void *unused;
void *p;
{
	handleserver (s, "daytime", T_DAYTIME);
}

/* Stop time server */
int
daytime0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Sdaytime));
}

/* Start up TCP echo server */
int
echo1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Secho, "Echo listener", IPPORT_ECHO,
		"Echo server", echoserv, 2048, NULL));
}
static void
echoserv(s,unused,p)
int s;
void *unused;
void *p;
{
	handleserver (s, "echo", T_ECHO);
}

/* stop echo server */
int
echo0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Secho));
}

#endif /*ALLSERV*/

extern void where_outta_here __ARGS((int resetme));

/* Start remote exit/reboot server */
int
rem1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct sockaddr_in lsocket,fsock;
int i;
int command;
struct mbuf *bp;
int32 addr;
char temp[20];

	if(Rem != -1)
		return 0;
	psignal(Curproc,0);
	chname(Curproc,"Remote listener");
	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = IPPORT_REMOTE;
	else
		lsocket.sin_port = atoi(argv[1]);
	
	Rem = socket(AF_INET,SOCK_DGRAM,0);
	bind(Rem,(char *)&lsocket,sizeof(lsocket));
	for(;;){
		i = sizeof(fsock);
		if(recv_mbuf(Rem,&bp,0,(char *)&fsock,&i) == -1)
			break;
		command = PULLCHAR(&bp);

		switch(command){
#ifdef	MSDOS	/* Only present on PCs running MSDOS */
		case SYS_RESET:
			i = chkrpass(bp);
			log(Rem,"Remote reset %s %s",
			 psocket((struct sockaddr *)&fsock),
			 i == 0 ? "PASSWORD FAIL" : "" );
			if(i != 0){
                /*Indicate a hardware reset*/
                where_outta_here(1);
			}
			break;
#endif
		case SYS_EXIT:
			i = chkrpass(bp);
			log(Rem,"Remote exit %s %s",
			 psocket((struct sockaddr *)&fsock),
			 i == 0 ? "PASSWORD FAIL" : "" );
			if(i != 0){
                /*No reset*/
                where_outta_here(0);
			}
			break;
		case KICK_ME:
			if(len_p(bp) >= sizeof(int32))
				addr = pull32(&bp);
			else
				addr = fsock.sin_addr.s_addr;

			/* get round strange printf bug */
			strcpy(temp, inet_ntoa(addr));

			log(Rem,"Remote kick by %s for host %s",
			 psocket((struct sockaddr *)&fsock), temp);
#ifdef NETROM
			donodetick();		/* g3rra's idea!..hmmm */
#endif /* NETROM */
			kick(addr);
			smtptick((void *)addr);
			break;
		}
		free_p(bp);
	}
	close_s(Rem);
	Rem = -1;
	return 0;
}

/* Check remote password */
static int
chkrpass(bp)
struct mbuf *bp;
{
	char *lbuf;
	int16 len;
	int rval = 0;

	len = len_p(bp);
	if(strlen(Rempass) != len)
		return rval;
	lbuf = mallocw(len);
	pullup(&bp,lbuf,len);
	if(strncmp(Rempass,lbuf,len) == 0)
		rval = 1;
	free(lbuf);
	return rval;
}

int
rem0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Rem);
	return 0;
}


/* Start up TCP quote server */
int
quote1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Squote, "Quote listener", IPPORT_QUOTE,
		"Quote server", quoteserv, 1536, NULL));
}

static void
quoteserv(s,unused,p)
int s;
void *unused;
void *p;
{
	handleserver (s, "quote", T_QUOTE);
}

/* Stop quote server */
int
quote0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Squote));
}


char *
getquote()
{
char buf2[512];
char searchstr[5];
char buf3[4096];
char *cptr;
FILE *fp;
struct date dt;

	if(Squote == -1)
		return (NULLCHAR);
	if((fp = fopen(Quotes,READ_TEXT)) == NULLFILE)	{
		if (MMotd != NULLCHAR)
			return (strdup(MMotd));
		else
			return (NULLCHAR);
	}
	getdate (&dt);
	sprintf (searchstr, "[%-d]", dt.da_day);
	while(fgets(buf2,512,fp) != NULLCHAR) 	{
		if (!strncmp (searchstr, buf2, strlen(searchstr)))
			break;
	}
	buf3[0] = 0;
	if ((cptr = strchr (buf2, '%')) != NULLCHAR)	{
		rip (buf2);
		fclose (fp);
		if ((fp = fopen (++cptr,READ_TEXT)) == NULLFILE)	{
			printf ("Couldn't open '%s'\n", &buf2[1]);
			return (NULLCHAR);
		}
	}
	while(fgets(buf2,512,fp) != NULLCHAR)	{
		if (*buf2 == '[')
			break;
		strcat (buf3, buf2);
	}
	fclose (fp);
	return (strdup (buf3));
}


/* Execute user quote command */
int
doquote(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char buf[32], buf2[32], *cptr;
int i = 0;

	free (argv[0]);
	argv[0] = strdup ("telnet");
	if (argv[2])
		free (argv[2]);
	argv[2] = strdup ("17");
	if (!argv[1])
		argv[1] = strdup (Hostname);
	return dotelnet(3,argv,p);
}


#if 0
#ifdef RLOGINSERV
static void
rloginserver(s,o,p)
int s;
void *o;
void *p;
{
	mbx_incom (s, (void *)RLOGIN_LINK, p);
}
#endif
#endif


#ifdef TUTOR
extern void tutorserv __ARGS((char *, struct mbx *, int, int, int));
static char *tut[] = {"tutor", "info", "news"};

static void
tutorserver(s,mode,p)
int s;
void *mode;
void *p;
{
int outsave, insave;

	sockmode(s,SOCK_ASCII);
	sockowner(s,Curproc);
	log (s, "open %s server", tut[(int) mode]);
	outsave = Curproc->output;
	Curproc->output = s;
	insave = Curproc->input;
	Curproc->input = s;
	tutorserv ("NoName", NULLMBX, (int) mode, 0, 1);
	Curproc->input = insave;
	Curproc->output = outsave;
	log(s,"close %s server", tut[(int) mode]);
	close_s(s);
}

#ifdef RLOGINSERV
/* Start up RLOGIN server */
int
rlogin1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
#if 0
	return (installserver (argc, argv, &Srlogin, "RLogin listener", IPPORT_RLOGIN,
		"RLogin server", rloginserver, 2048, (void *) 0));
#else
	return (installserver (argc, argv, &Srlogin, "RLogin listener", IPPORT_RLOGIN,
		"RLogin server", mbx_incom, 2048, (void *) RLOGIN_LINK));
#endif
}

/* Stop RLOGIN server */
int
rlogin0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Srlogin));
}
#endif

/* Start up TCP tutorial server */
int
tutor1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Stutor, "Tutor listener", IPPORT_TUTOR,
		"Tutor server", tutorserver, 2048, (void *) 0));
}

/* Stop tutorial server */
int
tutor0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Stutor));
}

/* Start up TCP tutorial server */
int
info1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Sinfo, "Info listener", IPPORT_INFO,
		"Info server", tutorserver, 2048, (void *) 1));
}

/* Stop tutorial server */
int
info0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Sinfo));
}

/* Start up TCP news server */
int
news1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Snews, "News listener", IPPORT_NEWS,
		"News server", tutorserver, 2048, (void *) 2));
}

/* Stop news server */
int
news0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Snews));
}

char WhichTutor = 0;

/* Execute user tutor command */
int
dotutor(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char buf[6];
int i = WhichTutor;

	WhichTutor = 0;
	free (argv[0]);
	argv[0] = strdup ("telnet");
	if (argv[2])
		free (argv[2]);
	sprintf (buf, "%-d", IPPORT_TUTOR - i);
	argv[2] = strdup (buf);
	if (!argv[1])
		argv[1] = strdup (Hostname);
	return dotelnet(3,argv,p);
}


/* Execute user info command */
int
doinfo(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	WhichTutor = 1;
	return (dotutor(argc, argv, p));
}


/* Execute user info command */
int
donews(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	WhichTutor = 2;
	return (dotutor(argc, argv, p));
}


#endif
