/
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.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/xact.c,v 1.6 91/09/19 12:56:20 mjr Exp $";
#endif

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


#include	<stdio.h>
#include	<ctype.h>


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

static int	xact_recobj();
static int	xact_linkto();
static int	xact_addto();
static int	xact_end();
static int	xact_abort();


typedef struct {
	char	*name;
	int	(*handler)();
} xact_cmd;


xact_cmd xact_cmd_tab[] = {
	"OBJECT",	xact_recobj,
	"LINKTO",	xact_linkto,
	"ADDTO",	xact_addto,
	"ENDTRANS",	xact_end,
	"ABORT",	xact_abort,
	(char *)0,	(int (*)())0
};

static xact_cmd	*xact_cmdmatch();



/* handle an incoming connection from a remote server. */
void
xact_in(fd)
int	fd;
{
	Sbuf		suf;
	tr_prog		*prog;
	char		*cmdstr,*args;
	xact_cmd	*cmd;
	int		ret;
	int		errstat = 0;

	sbuf_initstatic(&suf);

	xc_initfd(fd);
	if(xact_grtserver(fd)) {
		xc_write("ERR I don't know you. Go away.\n",(char *)0);
		xc_flush();
		xc_close();
		plogf("hung up on remote server, bad introduction, on %d\n",
		      fd);
		sbuf_freestatic(&suf);
		return;
	}

	/* Wait around for stuff to arrive. */
	if((prog = tr_newprog()) == (tr_prog *)0) {
		xc_write("ERR Internal error.\n",(char *)0);
		xc_flush();
		xc_close();
		plogf("hung up on remote server, can't get tr_prog, on %d\n",
		     fd);
		sbuf_freestatic(&suf);
		return;
	}

	while(1) {
		if(xc_sbread(&suf) == 0) {
			xc_write("ERR Timeout.\n",(char *)0);
			errstat = 1;
			break;
		}
		cmdstr = sbuf_buf(&suf);

		/* Execute this command. It may ONLY read. */
		if((cmd = xact_cmdmatch(cmdstr)) == (xact_cmd *)0) {
			xc_write("ERR Unknown command ",cmdstr,".\n",(char *)0);
			errstat = 1;
			plogf("hung up remote server, bad command '%s', on %d\n",
			      cmdstr, fd);
			break;
		}

		/* Skip ahead to args */
		args = cmdstr;
		while(isspace(*args))
			args++;
		while(!isspace(*args) && *args)
			args++;
		while(isspace(*args))
			args++;

		ret = (cmd->handler)(args,prog);

		/* Failed command */
		if(ret == -1) {
			xc_write("ERR Failed command ",cmdstr,".\n",(char *)0);
			errstat = 1;
			plogf("hung up remote server, failed command '%s', on %d\n",
			      cmdstr, fd);
			break;
		}

		/* We're done. */
		if(ret == 1)
			break;
	}

	/* Check the program */
	if(errstat) {
		/* Error has been reported in more detail already */
		(void)tr_abort(prog);
	} else {
		if(tr_validate(prog)) {
			xc_write("ERR Bad transaction\n",(char *)0);
			(void)tr_abort(prog);
		} else {
			xc_write("OK\n",(char *)0);
			(void)tr_exec(prog);
		}
	}
	xc_flush();
	xc_close();
	plogf("hung up remote server on %d\n", fd);
	sbuf_freestatic(&suf);
}




/*
	Send a player object and its inventory over. This includes all
protocol, including starting and finishing the connect.

	Returns 0 on success. Fail at the slightest provocation.
*/
xact_sendplayer(p,src,dest)
char	*p;
char	*src;
char	*dest;
{
	tr_prog		*prog;
	tr_op		*newop;
	Sbuf		suf;
	char		*in;
	char		*q;
	char		*mud;
	char		nxtu[MAXOID];
	char		dst[MAXOID];


	if(p == (char *)0 || src == (char *)0 || dest == (char *)0)
		return(-1);

#ifdef XACT_DEBUG
	printf("sending %s from %s to %s\n",p,src,dest);
#endif

	if((prog = tr_newprog()) == (tr_prog *)0)
		return(-1);

	sbuf_initstatic(&suf);

	/* Copy the dest room number off into dst. Get to the MUD part */
	mud = dest;
	q = dst;
	while(*mud != '@' && *mud)
		*q++ = *mud++;

	if(!*mud) {
		sbuf_freestatic(&suf);
		return(-1);
	}
	mud++;
	*q = '\0';

	/* Sanity check the player too */
	for(q = p; *q != '@' && *q; )
		q++;

	if(!*q || ut_flagged(p,var_islocal)) {
		say(p,"You are not allowed to leave this MUD!\n",(char *)0);
		sbuf_freestatic(&suf);
		return(-1);
	}

	/* Connect and introduce ourselves. */
	if(xact_connectto(mud))
		goto fail;

	/* Send the objects along, watch for dropped connects */
	if((in = ut_getatt(p,0,typ_list,var_cont,(char *)0)) != (char *)0) {
		in = lstnext(in,nxtu);

     		while(in != (char *)0) {
			/* Check that this is a non-local object */
			for(q = nxtu; *q != '@' && *q;)
				q++;

			if(!*q || ut_flagged(nxtu,var_islocal)) {
				say(p,"You are carrying a local object.\n",
					(char *)0);
				goto abort;
			}

     			if(xact_sendobj(nxtu,prog,0))
				goto abort;
			in = lstnext(in,nxtu);
     		}
	}
	if((in = ut_getatt(p,0,typ_obj,var_using,(char *)0)) != (char *)0){

		/* Check that this is a non-local object */
		for(q = in; *q != '@' && *q;)
			q++;

		if(!*q || ut_flagged(in,var_islocal)) {
			say(p,"You are holding a local object.\n",(char *)0);
			goto abort;
		}
     		if(xact_sendobj(in,prog,0))
			goto abort;
	}

	/* Get the player out of here, and into there, at least tentatively */
	/* Build a 'delete player from this room', and be anal about malloc */
	if(xact_sendobj(p,prog,1))
		goto abort;

	if((newop = (tr_op *)malloc(sizeof(tr_op))) == (tr_op *)0)
		goto abort;

	(newop->parm).insdel.this = (char *)malloc((unsigned)(strlen(p) + 1));
	if((newop->parm).insdel.this == (char *)0) {
		free((mall_t)newop);
		goto abort;
	}

	(newop->parm).insdel.there = (char *)malloc((unsigned)(strlen(src) + 1));
	if((newop->parm).insdel.there == (char *)0) {
		free((mall_t)(newop->parm).insdel.this);
		free((mall_t)newop);
		goto abort;
	}
	strcpy((newop->parm).insdel.this,p);
	strcpy((newop->parm).insdel.there,src);
	newop->handler = tr_remply;
	tr_addop(prog,newop);

	xc_write("ADDTO ",p," ",dst,"\n",(char *)0);
	

	/* Check our program */
	if(tr_validate(prog)) {
		/* SHIT! No good. Back off. */
		goto abort;
	} else {
		/* Commit */
		xc_write("ENDTRANS\n",(char *)0);
		xc_flush();
	}

	/* Get response. */
	if(!xc_sbread(&suf) || strcmp(sbuf_buf(&suf),"OK")) {
		logf("Failed transaction ",sbuf_buf(&suf),"\n",(char *)0);
		goto fail;
	}

#ifdef	XACT_DEBUG
	logf("remote: ",sbuf_buf(&suf),"\n",(char *)0);
#endif
	/* Commit at our end. */
	tr_exec(prog);
	xc_close();
	plogf("Sent %s from %s to %s@%s\n", p, src, dst, mud);
	sbuf_freestatic(&suf);
	return(0);

abort:
	xc_write("ABORT\n",(char *)0);
	xc_flush();
	/* Fall thru to fail */


fail:
	/* Ofuk. */
	tr_abort(prog);
	xc_close();
	sbuf_freestatic(&suf);
	return(-1);
}




/* Do an OIF Level 2 transaction to verify that a link can be added */
xact_addlink(to,from,who)
char	*to;
char	*from;
char	*who;
{
	char *mud,*q;
	Sbuf suf;
	char dst[MAXOID];

	if(to == (char *)0 || from == (char *)0 || who == (char *)0)
		return(-1);

	/* Who to connect to. And destination room. */
	q = dst;
	mud = to;

	while(*mud != '@' && *mud)
		*q++ = *mud++;

	if(!*mud)
		return(-1);
	mud++;
	*q = '\0';

	if(xact_connectto(mud))
		return(-1);

	if(xc_write("LINKTO ",dst," ",from," ",who,"\nENDTRANS\n",(char *)0))
		goto fail;

	if(xc_flush())
		goto fail;

	/* Get the response */
	sbuf_initstatic(&suf);
	if(!xc_sbread(&suf) || strcmp(sbuf_buf(&suf),"OK")) {
		logf("Failed transaction ",sbuf_buf(&suf),"\n",(char *)0);
		goto fail;
	}
	xc_close();
	sbuf_freestatic(&suf);
	return(0);

fail:
	xc_close();
	sbuf_freestatic(&suf);
	return(-1);
}




/* Initiate and verify a conect to a remote MUD */
xact_connectto(mud)
char	*mud;
{
	Sbuf	suf;
	char	*s;
	char	*name;
	char	*pwd;
	static	char	up[] = "UnterMUD";

	if(xc_open(mud))
		return(-1);

	sbuf_initstatic(&suf);

	if(xmit_greet())
		goto fail;

	/* Get the response greet string back */
	if(!xc_sbread(&suf))
		goto fail;

	s = sbuf_buf(&suf);

	/* Check stuff. */
	if(strncmp(up,s,sizeof(up) - 1)) {
		sbuf_freestatic(&suf);
		return(-1);
	}

	s += sizeof(up);

	while(isspace(*s))
		s++;

	name = s;
	while(!isspace(*s) && *s)
		s++;

	if(!*s) {
		sbuf_freestatic(&suf);
		return(-1);
	}

	*s++ = '\0';
	while(isspace(*s))
		s++;
	while(!isspace(*s) && *s)
		s++;
	while(isspace(*s))
		s++;

	if(!*s) {
		sbuf_freestatic(&suf);
		return(-1);
	}
	pwd = s;

	while(!isspace(*s) && *s)
		s++;
	*s = '\0';

	/* Verify name and pwd */
	if(check_rempwd(pwd) || strcmp(mud,name)) {
		plogf("To server, bad remote name or pw: want %s got %s with pw %s.\n",
		     mud, name, pwd);
		sbuf_freestatic(&suf);
		return(-1);
	}

	sbuf_freestatic(&suf);
	return(0);

fail:
	xc_close();
	sbuf_freestatic(&suf);
	return(-1);
}




/*
Send a single object off to the remote MUD. We are connected at this point.
Handles building tr_ops, too. Flag == 0 to delete object, 1 to just zero it.
Return 0 on success.
*/
xact_sendobj(nam,prog,flag)
char	*nam;
tr_prog	*prog;
int	flag;
{
	Obj	*o;
	int	a;
	tr_op	*newop;

#ifdef	XACT_DEBUG
	printf("Sending object %s\n", nam);
#endif

	/* Lookup the object. */
	if((o = cache_get(nam)) == (Obj *)0)
		return(-1);

	/* Stuff the object out */
	if(xc_write("OBJECT ",nam,"\nobject\n",(char *)0))
		return(-1);

	for(a = 0; a < o->ocnt; a++)
		if(xc_write(o->oap[a],"\n",(char *)0))
			return(-1);

	if(xc_write("endobj\n",(char *)0))
		return(-1);

	/* Build a suitable tr_op here */
	newop = (tr_op *)malloc(sizeof(tr_op));
	if(newop == (tr_op *)0) {
		return(-1);
	}

	/* Delete it, or merely zero location+contents */
	if(flag)
		newop->handler = tr_zeroobj;
	else
		newop->handler = tr_delobj;

	(newop->parm).name = (char *)malloc((unsigned)(strlen(nam) + 1));
	if((newop->parm).name == (char *)0) {
		free((mall_t)newop);
		return(-1);
	}

	strcpy((newop->parm).name, nam);
	tr_addop(prog,newop);
	return(0);
}




/* Send a player a tinylink message */
xact_tinylink(who)
char	*who;
{
	char linkmsg[512];

	sprintf(linkmsg,"#### Please reconnect to %s@%s (%s) port %s ####",
		xc_getmudname(),xc_getips(),xc_gethostname(),xc_getport());
	say(who,linkmsg,"\n",(char *)0);
	ut_set(who,who,typ_str,var_linkmsg,linkmsg);
}




/* Match a string against the command table. */
static xact_cmd	*
xact_cmdmatch(s)
char	*s;
{
	char		*p;
	char		ch;
	xact_cmd	*cmd;

	while(isspace(*s))
		s++;

	p = s;

	while(!isspace(*s) && *s)
		s++;

	ch = *s;
	*s = '\0';

	for(cmd = xact_cmd_tab; cmd->name != (char *)0; cmd++) {
		if(!strcmp(p,cmd->name)) {
			*s = ch;
			return(cmd);
		}
	}

	*s = ch;
	return((xact_cmd *) 0);
}




/* Tries to connect us to the specified MUD. Returns 0 on success.  */
xact_connserver(mud)
char	*mud;
{
	if(xc_open(mud) == -1)
		return(-1);

	/* We're connected. Introduce ourselves. */
	xmit_greet();
	return(0);
}




/*
Handles introduing ourselves, finding out who the guy on the other end of
the fd is, and ensuring it's OK.
*/
xact_grtserver(fd)
int	fd;
{
	Sbuf	s;
	char	*p;
	char	*name;
	char	*pwd;
	static	char	up[] = "UnterMUD";

	sbuf_initstatic(&s);
	if(xc_sbread(&s) == 0) {
		sbuf_freestatic(&s);
		return(1);
	}

	/* s has the introduce string. Who is this bozo? */
	p = sbuf_buf(&s);

	/* Kinda stupid to check this, but wtf.. */
	if(strncmp(up,p,sizeof(up) - 1)) {
		logf("remote server with unset name\n", (char *) 0);
		sbuf_freestatic(&s);
		return(1);
	}

	/* Skip to the MUD name and terminate it */
	p += sizeof(up);

	while(isspace(*p))
		p++;
	name = p;

	while(!isspace(*p) && *p)
		p++;
	if(!*p) {
		logf("remote greeting of '", up,
		     "' and can't find password.\n", (char *)0);
		sbuf_freestatic(&s);
		return(1);
	}
	*p++ = '\0';

	/* Bag the version number and skip to the password */
	while(isspace(*p))
		p++;
	while(!isspace(*p) && *p)
		p++;
	while(isspace(*p))
		p++;
	pwd = p;

	/* Is this guy OK? */
	if(set_remmud(name)) {
		logf("Unknown remote MUD '", name, "'\n", (char *) 0);
		sbuf_freestatic(&s);
		return(1);
	}
	if(check_rempwd(pwd)) {
		logf("Remote MUD '", name, "' with bad password '",
		     pwd, "'\n", (char *) 0);
		sbuf_freestatic(&s);
		return(1);
	}
	
	/* Send out our initial string */
	xmit_greet();
	plogf("CONNECT remote server %s on %d\n", name, fd);
	sbuf_freestatic(&s);
	return(0);
}




/*
	Handlers for the various OIF level 2 commands. Return -1 for failure,
0 for success, and 1 if we want to conclude the transaction now.
*/
static	int
xact_recobj(name,p)
char	*name;
tr_prog	*p;
{
	tr_op		*newop;
	Sbuf		suf;
	char		*s;
	Obj		*newobj;

	/* Sanity check */
	if(*name == '\0')
		return(-1);

	for(s = name; *s != '@' && *s; )
		s++;

	if(*s != '@')
		return(-1);

	sbuf_initstatic(&suf);

	/* Snarf in the first line */
	if(!xc_sbread(&suf)) {
		sbuf_freestatic(&suf);
		return(-1);
	}

	s = sbuf_buf(&suf);
	if(strncmp(s,"object",6)) {
		sbuf_freestatic(&suf);
		return(-1);
	}

	/* OK. Grab the object attributes one by one */
	if((newobj = objnew()) == (Obj *)0) {
		sbuf_freestatic(&suf);
		return(-1);
	}

	while(1) {
		if(!xc_sbread(&suf)) {
			objfree(newobj);
			sbuf_freestatic(&suf);
			return(-1);
		}

		s = sbuf_buf(&suf);
		if(!strcmp(s,"endobj"))
			break;

		if(objstuffattr(newobj,s,sbuf_len(&suf))) {
			objfree(newobj);
			sbuf_freestatic(&suf);
			return(-1);
		}
	}

	/* Loop ONLY breaks to here if the object was gotten OK */
	/* Slap together a tr_op with it. */
	newop = (tr_op *)malloc(sizeof(tr_op));
	if(newop == (tr_op *)0) {
		objfree(newobj);
		sbuf_freestatic(&suf);
		return(-1);
	}

	newop->handler = tr_newobj;
	(newop->parm).object.name = (char *)malloc((unsigned)(strlen(name) + 1));
	if((newop->parm).object.name == (char *)0) {
		objfree(newobj);
		sbuf_freestatic(&suf);
		return(-1);
	}

	strcpy((newop->parm).object.name, name);
	(newop->parm).object.obj = newobj;
	tr_addop(p,newop);
	sbuf_freestatic(&suf);
	return(0);
}




static	int
xact_linkto(s,p)
char	*s;
tr_prog	*p;
{
	tr_op	*newop;
	char	*who;
	char	*from;
	char	*to;


	/* Pull out args. Yes, I should use enargv() here. I know. */
	to = s;
	while(!isspace(*s) && *s)
		s++;
	if(!*s)
		return(-1);
	*s++ = '\0';

	while(isspace(*s))
		s++;

	from = s;
	while(!isspace(*s) && *s)
		s++;

	if(!*s)
		return(-1);
	*s++ = '\0';
	while(isspace(*s))
		s++;

	if(!*s)
		return(-1);
	who = s;

	if((newop = (tr_op *)malloc(sizeof(tr_op))) == (tr_op *)0)
		return(-1);

	newop->handler = tr_addlink;

	(newop->parm).linkchk.who = (char *)malloc((unsigned)(strlen(who) + 1));
	if((newop->parm).linkchk.who == (char *)0) {
		free((mall_t)newop);
		return(-1);
	}

	(newop->parm).linkchk.from = (char *)malloc((unsigned)(strlen(from) + 1));
	if((newop->parm).linkchk.from == (char *)0) {
		free((mall_t)(newop->parm).linkchk.who);
		free((mall_t)newop);
		return(-1);
	}

	(newop->parm).linkchk.to = (char *)malloc((unsigned)(strlen(to) + 1));
	if((newop->parm).linkchk.to == (char *)0) {
		free((mall_t) (newop->parm).linkchk.who);
		free((mall_t) (newop->parm).linkchk.from);
		free((mall_t)newop);
		return(-1);
	}

	strcpy((newop->parm).linkchk.to, to);
	strcpy((newop->parm).linkchk.from, from);
	strcpy((newop->parm).linkchk.who, who);

	tr_addop(p,newop);

	return(0);
}




static	int
xact_addto(s,p)
char	*s;
tr_prog	*p;
{
	tr_op	*newop;
	char	*this;
	char	*there;

	/* Snarf out the arguments. */
	this = s;

	while(!isspace(*s) && *s)
		s++;
	if(*s)
		*s++ = '\0';

	while(isspace(*s))
		s++;
	there = s;

	while(!isspace(*s) && *s)
		s++;
	*s = '\0';

	if((newop = (tr_op *)malloc(sizeof(tr_op))) == (tr_op *)0)
		return(-1);

	newop->handler = tr_insply;

	(newop->parm).insdel.this = (char *)malloc((unsigned)(strlen(this) + 1));
	if((newop->parm).insdel.this == (char *)0) {
		free((mall_t)newop);
		return(-1);
	}

	(newop->parm).insdel.there = (char *)malloc((unsigned)(strlen(there) + 1));
	if((newop->parm).insdel.there == (char *)0) {
		free((mall_t)(newop->parm).insdel.this);
		free((mall_t)newop);
		return(-1);
	}

	strcpy((newop->parm).insdel.this,this);
	strcpy((newop->parm).insdel.there,there);

	tr_addop(p,newop);
	/* This is likely a player addto? */
	plogf("remote added %s to %s\n", this, there);
	return(0);
}




static	int
xact_end(s,p)
char	*s;
tr_prog	*p;
{
	return(1);
}




/* By definition, this always fails */
static	int
xact_abort(s,p)
char	*s;
tr_prog	*p;
{
	return(-1);
}