/* Internet TTY "link" (keyboard chat) server
 * Copyright 1991 Phil Karn, KA9Q
 */
#include "global.h"
#include "commands.h"
#ifndef MSDOS
#include <time.h>
#endif
#include "mbuf.h"
#include "socket.h"
#include "telnet.h"
#include "mailbox.h"
#include "x.h"
#include "stats.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: ttylink.c,v 1.23 2001/05/06 16:32:58 brian Exp $";
#endif

#ifdef ALLSERV
static int Sttylink = -1;	/* Protoype socket for service */
int Attended = TRUE;            /* default to attended mode */
static int Screening;           /* default to non-screened mode */
#ifdef PATCURSES
extern void clrchatline (struct session * sp);
#endif
static char Tnbanner[] = "Welcome to TTY-Link at %s - The system is %s.\n";
static char defaultDeny[] = "Sorry, but the SYSOP is unable to join you at this time...";
extern const char SysopBusy[];
extern char *Motd;

int ScreenedCall;
char *ScreenedCaller;
static int SCREENretries = 60;
#ifdef SOUNDS
static char *SCREENsound;
static char *CONNECTsound;
#endif
static char *DENYstr;

int CHAToneshotBypass = 0;



int
ttylstart(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
struct sockaddr_in lsocket;
int s, type, i;
char addr[MAXSOCKSIZE];
int len = MAXSOCKSIZE;
time_t nowtime;

	if (Sttylink != -1)
		return 0;

	ksignal (Curproc, 0);	/* Don't keep the parser waiting */
	chname (Curproc, "TTYlink listener");
	server_disconnect_io ();
	
	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if (argc < 2)
		lsocket.sin_port = IPPORT_TTYLINK;
	else
		lsocket.sin_port = (int16) atoi (argv[1]);

	Sttylink = socket (AF_INET, SOCK_STREAM, 0);
	(void) bind (Sttylink, (char *)&lsocket, sizeof (lsocket));
	(void) listen (Sttylink, 1);
	for ( ; ; )	{
		if ((s = accept (Sttylink, NULLCHAR, (int *)NULL)) == -1)
			break;	/* Service is shutting down */
		
		if (!Attended)	{
busy:			usprintf (s, SysopBusy);
toclose:			(void) shutdown (s, 1);
			close_s (s);
		} else {
			if (Screening && !CHAToneshotBypass)	{
				while (ScreenedCall == -1)
					kpause (1000);
				usprintf (s, "Paging SYSOP, stand by....\n");
				usflush (s);
				ScreenedCall = -1;
#ifdef SOUNDS
				if (SCREENsound)
					(void) playsound (SCREENsound);
				else
#endif
					tcmdprintf ("\007");
				(void) time (&nowtime);		/* current time */
				if (getpeername (s, addr, &len) != -1)
					ScreenedCaller = strdup (psocket (addr));
				else
					ScreenedCaller = strdup ("unknown");
#ifdef XSERVER
				xnotify (X_SCREEN);
#endif
				tcmdprintf ("*** Incoming chat request from %s on %s\n",
					ScreenedCaller, ctime (&nowtime));
				for (i = 0; i < SCREENretries; i++)	{
					if (ScreenedCall != -1)
						break;
					kpause (1000);
				}
				free (ScreenedCaller);
				ScreenedCaller = NULLCHAR;
#ifdef XSERVER
				xnotify (X_SCREEN);
#endif
				switch (ScreenedCall)	{
					case 0:		/* denied */
						usprintf (s, "%s\n", (DENYstr && *DENYstr) ? DENYstr : defaultDeny);
						goto toclose;
					case -1:	/* unanswered */
						ScreenedCall = 0;
#ifdef XSERVER
						xnotify (X_SCREEN);
#endif
						goto busy;
					case 1:		/* accepted */
						usprintf (s, "Call accepted by SYSOP!\n");
						break;
					default:
						break;
				}
			}
			CHAToneshotBypass = 0;
			type = TELNET;
			if (newproc ("chat", 2048, ttylhandle, s, (void *)&type, NULL, 0) == NULLPROC)	{
				(void) shutdown (s, 1);
				close_s (s);
			}
		}
	}
	return 0;
}


/* This function handles all incoming "chat" sessions, be they TCP,
 * NET/ROM or AX.25
 */
void
ttylhandle (s, t, p)
int s;
void *t;
void *p OPTIONAL;
{
int type, theindex;
struct session *sp;
char addr[MAXSOCKSIZE];
int len = MAXSOCKSIZE;
struct telnet tn;
time_t nowtime;
int validaddr;

	type = * (int *)t;
	(void) sockmode (s, SOCK_ASCII);
	(void) sockowner (s, Curproc);	/* We own it now */
	log (s, "open %s", Sestypes[type]);

	/* Allocate a session descriptor */
	if ((sp = newsession (NULLCHAR, type, 1)) == NULLSESSION)	{
		tputs (TooManySessions);
		close_s (s);
		return;
	}
#ifdef STATS_USE
	STATS_adduse (1);
	MiscUsers++;
#endif
	theindex = sp - Sessions;
#ifdef PATCURSES
	if (sp->split)	/* clear to paint chatline */
		clrchatline(sp);
#endif

	/* Initialize a Telnet protocol descriptor */
	memset ((char *)&tn, 0, sizeof (tn));
	tn.session = sp;	/* Upward pointer */
	sp->cb.telnet = &tn;	/* Downward pointer */
	sp->s = s;
	sp->proc = Curproc;

	(void) time (&nowtime);		/* current time */

	validaddr = getpeername (s, addr, &len);
#ifdef SOUNDS
	if (CONNECTsound)
		(void) playsound (CONNECTsound);
	else
#endif
		tputc ('\007');

	tprintf ("Incoming %s session %u from %s on %s",
		Sestypes[type], theindex, (validaddr != -1) ? psocket(addr) : "unknown host", ctime (&nowtime));

	usprintf (s, Tnbanner, Hostname, Attended ? "attended" : "unattended");
	if (Motd != NULLCHAR)
		usprintf (s, "%s", Motd);

	tnrecv (&tn);
#ifdef STATS_USE
	MiscUsers--;
#endif
}


/* Shut down Ttylink server */
int
ttyl0 (argc, argv, p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
	return (deleteserver (&Sttylink));
}


static int doretries (int argc,char *argv[],void *p);
static int doattended (int argc,char *argv[],void *p);
static int doscreening (int argc,char *argv[],void *p);
static int doaccept (int argc,char *argv[],void *p);
static int dodeny (int argc,char *argv[],void *p);
static int dodenystr (int argc,char *argv[],void *p);
#ifdef SOUNDS
static int doscreensound (int argc,char *argv[],void *p);
static int doconnectsound (int argc,char *argv[],void *p);
#endif

/* chat subcommand table */
static struct cmds CHATtab[] = {
	{ "accept",		doaccept,		0, 0, NULLCHAR },
	{ "attended",		doattended,		0, 0, NULLCHAR },
#ifdef SOUNDS
	{ "connectsound",	doconnectsound,		0, 0, NULLCHAR },
#endif
	{ "deny",		dodeny,			0, 0, NULLCHAR },
	{ "denystr",		dodenystr,		0, 0, NULLCHAR },
	{ "retries",		doretries,		0, 0, NULLCHAR },
	{ "screening",		doscreening,		0, 0, NULLCHAR },
#ifdef SOUNDS
	{ "screensound",	doscreensound,		0, 0, NULLCHAR },
#endif
#if defined(MAILBOX) && defined(GATECMDS)
	{ "with",		dombchat,		0, 2, "chat with <bbsuser>" },
#endif
	{ NULLCHAR,		NULL,			0, 0, NULLCHAR }
};


int
chatcmd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(CHATtab,argc,argv,p);
}


/* if unattended mode is set - restrict ax25, telnet and maybe other sessions */
static int
doattended(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setbool(&Attended,"Incoming Chat Attended",argc,argv);
}


/* if attended mode is set, do we screen the incoming calls? */
static int
doscreening(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setbool(&Screening,"Incoming Chat Screening",argc,argv);
}


/* if attended mode is set, do we screen the incoming calls? */
static int
doretries(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return setint(&SCREENretries,"Time for Screened Callers to wait (in secs)",argc,argv);
}


/* to accept the waiting incoming call */
static int
doaccept(argc,argv,p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
	ScreenedCall = 1;
	return 0;
}


/* to deny the waiting incoming call */
static int
dodeny(argc,argv,p)
int argc OPTIONAL;
char *argv[] OPTIONAL;
void *p OPTIONAL;
{
	ScreenedCall = 0;
	return 0;
}


static int
dodenystr(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	if(argc < 2)	{
		if (DENYstr)
			tprintf ("%s\n", DENYstr);
	} else {
		if (DENYstr)
			free (DENYstr);
		DENYstr = strdup (argv[1]);
	}
	return 0;
}


#ifdef SOUNDS
static int
doscreensound(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (setsoundstr (argc, argv, &SCREENsound));
}


static int
doconnectsound(argc,argv,p)
int argc;
char *argv[];
void *p OPTIONAL;
{
	return (setsoundstr (argc, argv, &CONNECTsound));
}
#endif /* SOUNDS */

#endif /*ALLSERV*/
