/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
/*
	Copyright (C) 1991, Marcus J. Ranum. All rights reserved.

	VMS port by Andrew Molitor.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/xmit_bsd.c,v 1.1 91/06/17 14:37:48 mjr Exp $";
#endif

/* configure all options BEFORE including system stuff. */
#include	"config.h"


#include	<stdio.h>

#include	"multinet_root:[multinet.include.sys]types.h"
#include	"multinet_root:[multinet.include]netdb.h"
#include	"multinet_root:[multinet.include.sys]socket.h"
#include	"multinet_root:[multinet.include.netinet]in.h"
#include	"multinet_root:[multinet.include.vms]inetiodef.h"


#include	<ctype.h>
#include	<varargs.h>
#include	<ssdef.h>
#include	<libdtdef.h>

#include	"mud.h"
#include	"xact.h"
#include	"sbuf.h"

/*
	We use event flags 62 and 63 here -- one on a timeout timer, one to
synchronize actual network events. We never have more than one event
outstanding here.

	Flags 32, 33, and 34 are used by net_vms.c

	We use timer reqidt 1, for the hell of it. Since we're cancelling this
a lot, you'd best not go using that ID for anything else, eh?

*/
/*
#define	XMITVMS_DEBUG
*/

/* don't bother being too big - this is a TCP */
#define	XMBUFSIZ	1024

/* default timeout for I/O or connections */
#define	DEFAULT_TIMEOUT	2


/* mud database entry */
typedef	struct	mudent {
	int			flg;
	char			*name;
	char			*plyport;
	char			*host;
	char			*symhost;
	char			*rempw;
	char			*locpw;
	struct sockaddr_in	addr;
	int			timot;
	struct	mudent		*next;
} Mudent;


/* buffered connection */
typedef	struct	{
	int		fd;
	short int	iosb[4];
	char		ibuf[XMBUFSIZ];
	char		*ibp;
	int		ibc;
	char		obuf[XMBUFSIZ];
	char		*obp;
	Mudent		*curmp;
} Cnxt;


static	Mudent	*getmudent();
static	Mudent	*mud_list;
static	Cnxt	xbuf;
static	Mudent	*lastmud = (Mudent *)0;

/* Look up the remote MUD and set the context up right */

set_remmud(nam)
char	*nam;
{
	Mudent	*mp;

	if((mp = getmudent(nam)) == (Mudent *)0)
		return(1);

	lastmud = xbuf.curmp = mp;
	return(0);
}



xc_open(mud)
char	*mud;
{
	Mudent	*mp;
	long	timo[2];
	int	op;

	if((mp = getmudent(mud)) == (Mudent *)0) {
		logf("unknown mud ",mud,"\n",(char *)0);
		return(-1);
	}

	if((xbuf.fd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
		perror("socket");
		return(-1);
	}

	/* Get a connect to the specified MUD. Time out reasonably fast */
	/* Return -1 on error, 0 on success */

	/* Set up a timer */

	op = LIB$K_DELTA_SECONDS;
	lib$cvt_to_internal_time(&op,&(mp->timot),timo);
	sys$setimr(62L,timo,0,1,0);

	/* Queue up the connect request */

	sys$qio(63L,xbuf.fd,IO$_CONNECT,&(xbuf.iosb),0,0,
		&(mp->addr),sizeof(struct sockaddr_in),0,0,0,0);

	/* Wait around for something to happen */

	sys$wflor(32L,0xC0000000L); /* Top two events flags, 62, 63 */
	sys$cantim(1,0);	/* Kill timer now, just in case */

	/* *what* happened? */

	if(xbuf.iosb[0] != SS$_NORMAL){
		/* connect didn't work out. Bag. */

		sys$cancel(xbuf.fd);
		return(-1);
	}

	/* Connect succeeded, cool. */

#ifdef	XMITVMS_DEBUG
	(void)printf("<<<connected>>>\n");
#endif
	logf("connection to ",mud,"\n",(char *)0);
	xbuf.ibp = xbuf.ibuf;
	xbuf.ibc = 0;
	xbuf.obp = xbuf.obuf;
	*xbuf.ibp = *xbuf.obp = '\0';
	lastmud = xbuf.curmp = mp;
	return(0);
}




xc_initfd(fd)
int	fd;
{
	xbuf.fd = fd;
	xbuf.ibp = xbuf.ibuf;
	xbuf.ibc = 0;
	xbuf.obp = xbuf.obuf;
	*xbuf.ibp = *xbuf.obp = '\0';
	return(0);
}




/* Send out an OIF Level 2 greet string to the current mud */
xmit_greet()
{
	/* Firewall. This should NEVER happen. */
	if(xbuf.curmp == (Mudent *)0)
		return(-1);

	if(xc_write("UnterMUD ",mud_getname()," ",version," "
			,(xbuf.curmp)->locpw,"\n",(char *)0))
		return(-1);
	if(xc_flush())
		return(-1);

	return(0);

}




/* Return 0 if pwd is OK */
check_rempwd(pwd)
char	*pwd;
{
	/* NEVER happen. */
	if(xbuf.curmp == (Mudent *)0)
		return(1);

	return(strcmp(pwd,(xbuf.curmp)->rempw));
}




/* These grub data out of the LAST Mud contacted.  */
char *
xc_getips()
{
	extern	char	*inet_ntoa();

	if(lastmud == (Mudent *)0)
		return("");
	return( inet_ntoa(lastmud->addr.sin_addr) );
}




char *
xc_getmudname()
{
	if(lastmud == (Mudent *)0){
		return("");
	}
	return(lastmud->name);
}




char *
xc_gethostname()
{
	if(lastmud == (Mudent *)0)
		return("");
	return(lastmud->symhost);
}




char *
xc_getport()
{
	if(lastmud == (Mudent *)0)
		return("");
	return(lastmud->plyport);
}




static Mudent	*
getmudent(mud)
char	*mud;
{
	Mudent	*mp = mud_list;

	while(mp != (Mudent *)0) {
		if(!strcmp(mud,mp->name))
			return(mp);
		mp = mp->next;
	}
	return((Mudent *)0);
}




/*
define a MUD entry. One semi-sensible thing done here is to
pre-resolve the addresses and just stash those. this way
we shouldn't have to hit our resolver a whole lot once the
MUD is up, if at all. also accept "dot" notation.
*/
xmit_def_mudent(name,host,symhost,rempw,locpw,port,plyport,timot)
char	*name;
char	*host;
char	*symhost;
char	*rempw;
char	*locpw;
char	*port;
char	*plyport;
char	*timot;
{
	Mudent	*np;
#ifndef	NO_HUGE_RESOLVER_CODE
	struct	hostent	*hp;
#endif
	struct	sockaddr_in	tmpa;
	char	*p;

	p = host;
	while(*p != '\0' && (*p == '.' || isdigit(*p)))
		p++;

	if(*p != '\0') {
#ifndef	NO_HUGE_RESOLVER_CODE
		if((hp = gethostbyname(host)) == (struct hostent *)0) {
			logf("unknown host ",host,"\n",(char *)0);
			return(1);
		}
		(void)bcopy(hp->h_addr,(char *)&tmpa.sin_addr,hp->h_length);
#else
		logf("must use 'dot' notation to define host ",host,"\n",(char *)0);
		return(1);
#endif
	} else {
		unsigned long	f;

		if((f = inet_addr(host)) == -1L)
			return(1);
		(void)bcopy((char *)&f,(char *)&tmpa.sin_addr,sizeof(f));
	}

	if((np = (Mudent *)malloc(sizeof(Mudent))) == (Mudent *)0) {
		logf("cannot allocate remote mud map\n",(char *)0);
		return(1);
	}

	(void)bcopy((char *)&tmpa.sin_addr,&(np->addr.sin_addr),sizeof(tmpa.sin_addr));
	np->addr.sin_port = htons(atoi(port));
	np->addr.sin_family = AF_INET;

	if(timot != (char *)0)
		np->timot = atoi(timot);
	else
		np->timot = DEFAULT_TIMEOUT;


	np->name = malloc((unsigned)(strlen(name) + 1));
	np->rempw = malloc((unsigned)(strlen(rempw) + 1));
	np->locpw = malloc((unsigned)(strlen(locpw) + 1));
	np->plyport = mallo] != SS$_NORMAL){
				/* read didn't work out. Bag. */
#ifdef XMITVMS_DEBUG
				printf("Read failed. iosb[0]=%d, iosb[1]=%d\n",
					xbuf.iosb[0],xbuf.iosb[1]);
				printf("Buffer contains: %s\n",xbuf.ibuf);
				socket_perror("hep!");
#endif

				sys$cancel(xbuf.fd);
				return(0);
			}
#ifdef XMITVMS_DEBUG
			printf("Read succeeded, read %d chars\n",xbuf.iosb[1]);
#endif
			xbuf.ibc = xbuf.iosb[1];
			if(xbuf.ibc <= 0)
				return(0);
			xbuf.ibp = xbuf.ibuf;
		}

		/* end of line? */
		if(*xbuf.ibp == '\n') {
			sbuf_put('\0',sb);
			xbuf.ibp++;
			xbuf.ibc--;
			return(1);
		}

		/* put. */
		sbuf_put(*xbuf.ibp,sb);
		xbuf.ibp++;
		xbuf.ibc--;
	}
}




xc_flush()
{
	long	timo[2];
	int	op,sec;

	op = LIB$K_DELTA_SECONDS;

	if(xbuf.curmp != (Mudent *)0)
		sec = xbuf.curmp->timot;
	else
		sec = DEFAULT_TIMEOUT;

	/* Start the timer */
	lib$cvt_to_internal_time(&op,&sec,timo);
	sys$setimr(62L,timo,0,1,0);

	/* Queue up the write request */

	sys$qio(63L,xbuf.fd,IO$_WRITEVBLK,&(xbuf.iosb),0,0,
		xbuf.obuf,xbuf.obp - xbuf.obuf,0,0,0,0);

	/* Wait around for something to happen */

	sys$wflor(32L,0xC0000000L); /* Top two events flags, 62, 63 */
	sys$cantim(1,0);	/* Kill timer now, just in case */

	/* *what* happened? */

	if(xbuf.iosb[0] != SS$_NORMAL){
		/* write didn't work out. Bag. */

		sys$cancel(xbuf.fd);
		return(1);
	} else {
		xbuf.obp = xbuf.obuf;
	}
	return(0);
}




/* VARARGS */
xc_write(va_alist)
va_dcl
{
	char	*s;
	va_list	ap;

#ifdef XMITVMS_DEBUG
	printf("xc_write: ");
#endif
	va_start(ap);
	while((s = va_arg(ap,char *)) != (char *)0) {
#ifdef XMITVMS_DEBUG
		printf("%s ",s);
#endif
		while(*s) {
			if((xbuf.obp - xbuf.obuf) >= sizeof(xbuf.obuf))
				if(xc_flush())
					return(1);

			*xbuf.obp++ = *s++;
		}
	}
	va_end(ap);
#ifdef XMITVMS_DEBUG
	printf("\n");
#endif
	return(0);
}

/* Maintain a list of MUD-names we've heard about. */
static Mudent	*known_muds = 0;
static int	restrict_known = 0;	/* Don't restrict to just known
					   MUDs. */

static Mudent *
isknownmud(mud)
char	*mud;
{
	Mudent	*temp, *pos;

	temp = getmudent(mud);
	if (temp != NULL)
		return temp;
	pos = known_muds;
	while (pos != NULL) {
		if (!strcmp(pos->name, mud))
			return pos;
		pos = pos->next;
	}
	return (Mudent *) 0;
}

int
xmit_def_knownmud(mud)
char	*mud;
{
	Mudent	*mp;

	restrict_known = 1;

	mp = isknownmud(mud);
	if (mp != 0)
		return 0;
	
	mp = (Mudent *) malloc(sizeof(Mudent));
	if (mp == 0) {
		logf("couldn't alloc memory for knownmud mudent\n", (char *)0);
		return 1;
	}
	mp->name = malloc((unsigned)(strlen(mud) + 1));
	if (mp->name == 0) {
		free(mp);
		logf("couldn't alloc memory for knownmud name\n", (char *)0);
		return 1;
	}
	(void)strcpy(mp->name, mud);
	mp->next = known_muds;
	known_muds = mp;

	return 0;
}

/* A given MUD name is okay if:
	- we don't have MUD security enabled (we have specified no defknownmuds)
	- it's a MUD we connect to
	- it's a MUD listed in a _mudconfig defknownmud
or	- it's us.
*/
int
xmit_okay_remote(name)
char	*name;
{
	if (!restrict_known)
		return 1;
	if (isknownmud(name) != 0)
		return 1;
	if (!strcmp(name, mud_getname()))
		return 1;
	return 0;
}
(sb)
Sbuf	*sb;
{
	unsigned long eflags;

	if(xbuf.fd == -1 || sb == (Sbuf *)0)
		return(0);

	sbuf_reset(sb);
	while(1) {

		/* buffer empty ? fill. */
		if(xbuf.ibc <= 0) {
			int	op,sec;
			long	timo[2];

			op = LIB$K_DELTA_SECONDS;

			if(xbuf.curmp != (Mudent *)0)
				sec = xbuf.curmp->timot;
			else
				sec = DEFAULT_TIMEOUT;

			/* Start the timer */
			lib$cvt_to_internal_time(&op,&sec,timo);
			sys$setimr(62L,timo,0,1,0);

			/* Qumud/CHANGES1004000006040000120000000446505103144527007762 037777777777 1 0 V1.13	- minor nits. fixed rwho output spew. :)

V1.12	- added patch to correctly sync and shut down Iobs when someone
	disconnects. 'build dropto' command removed. 'build exit' demands
	a destination. 'build exit' also understands 'here' as dest. 'set'
	degenerates to a mass of special cases. rooms can also teleport
	players in them home. random exit matching fixed. fix so things
	like (T|T)&F work. eats whitespace before a command. added support
	for 'local' flag. rewrote attribute expansion code. fix buffer
	overrun in xact code.

V1.11	- cleaned up some error messages and diagnostics. added WHO
	list sorting code.

# numbering scheme changes
V1.10	- added patch to prevent null attribute names. patched whisper
	to only match players. patched matching some more. patched log
	formatting. added automatic disconnect for players at login
	prompt for more than 80 seconds. fixed teeny weeny buffer
	overrun that rarely occurred in booleans. added latent support
	for public/private attributes in vars.c (support not added to
	vlookup()). added '$#' environment variable, which gives th