/
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/trans.c,v 1.1 91/07/04 17:32:31 mjr Rel $";
#endif

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


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


/*
	Generic facilities for bundling DB operations into 'programs' and
executing entire 'programs' 'atomically'

	Each type of transaction has a single entry point, which should accept
a flag, one of TR_VALIDATE, TR_EXEC, TR_ABORT. Extend at will. The 4 types
provided here are for server to server OIF transactions.

	Transaction handlers should return a success value. We ignore this in
everything except the validation phase.
*/




/* Mashes an object into local spec. For now, just dewiz everything.
   Turn off dark flags too.
*/
tr_vetobj(obj)
Obj *obj;
{
	(void)objunsetattr(obj,var_wiz);
	(void)objunsetattr(obj,var_isdark);
	return(0);
}




tr_prog
*tr_newprog()
{
	tr_prog	*new;

	if((new = (tr_prog *) malloc(sizeof(tr_prog))) == (tr_prog *)0){
		return((tr_prog *)0);
	}
	new->first = new->last = (tr_op *) 0;
	return(new);
}




/* add an operation to an existing program. */
void
tr_addop(prog,op)
tr_prog	*prog;
tr_op	*op;
{
	op->next = (tr_op *)0;
	if(prog->first == (tr_op *)0){
		prog->first = prog->last = op;
		return;
	}
	(prog->last)->next = op;
	prog->last = op;
}




/*
Validate a program. This is NON destructive. It does nothing but attempt
to guarantee that the program will succeed when executed.
*/
tr_validate(prog)
tr_prog	*prog;
{
	tr_op	*current;
	tr_op	*next;

	for(current = prog->first; current != (tr_op *)0; current = next){
		next = current->next;
		if((current->handler)(&(current->parm),TR_VALIDATE) == -1){
			return(-1);
		}
	}
	return(0);
}




/* Execute a program. This tidies everything up, too. */
tr_exec(prog)
tr_prog	*prog;
{
	tr_op	*current;
	tr_op	*next;

	for(current = prog->first; current != (tr_op *)0; current = next){
		next = current->next;
		(void) (current->handler)(&(current->parm),TR_EXEC);
		free((mall_t)current);
	}
	return(0);
}




/* Abort a program. This tidies everything up, too. */
tr_abort(prog)
tr_prog	*prog;
{
	tr_op	*current;
	tr_op	*next;

	for(current = prog->first; current != (tr_op *)0; current = next){
		next = current->next;
		(void)(current->handler)(&(current->parm),TR_ABORT);
		free((mall_t)current);
	}
	return(0);
}




/*
Basic routines for implementing each transaction type. These routines
are responsible for tidying up after themselves when given a TR_EXEC or a
TR_ABORT. They should not *touch* anything if given TR_VALIDATE. The present
implementation ignores return values in all but the VALIDATE case, but
success/failure should nonetheless be returned. The parameter and tr_op
structs are free's higher up, but any data specific to the operation gets
freed up here.

	Return -1 on failure, 0 on success.
*/


/* For putting a new object into the DB */
tr_newobj(data,flag)
tr_opdata	*data;
int		flag;
{
	int	ret = 0;
	char	*nam = (data->object).name;
	Obj	*obj = (data->object).obj;


	switch(flag){
	case TR_VALIDATE:
		if(obj == (Obj *)0 || nam == (char *)0)
			return(-1);
		if(!ut_isgoodid(nam))
			return(-1);

		/* We vet objects at this phase */

		if(tr_vetobj(obj))
			return(-1);

		while(*nam != '@' && *nam)
			nam++;
		if(!*nam)
			return(-1);
		nam++;
		if (!xmit_okay_remote(nam)) {
			plogf("Unknown origin MUD %s -- object transfer denied\n", nam);
			return -1;
		}

		/* Not much more validation to do. The other ways to fail */
		/* Are all file I/O stuff. We sorta hope that they'll work*/
		return(0);



	case TR_EXEC:
		/* If we have one of these already, kill it */
		if(cache_check(nam)){
			(void)cache_del(nam,0);
		}
		ret = cache_put(obj,nam);
		break;



	case TR_ABORT:
		/* OK, just nuke the Obj, and return. */
		objfree(obj);
		break;

	default:
		fatal("invalid transact op!",(char *)0);
	}


	/* In exec and abort cases , do this */
	if(nam != (char *)0)
		free((mall_t)nam);
	return(ret == 0 ? 0 : -1);
}




/* For removing an object from the DB.  */
tr_delobj(data,flag)
tr_opdata	*data;
int		flag;
{
	int ret = 0;

	switch(flag){
	case TR_VALIDATE:
		if(data->name == (char *)0)
			return(-1);
		if(!cache_check(data->name))
			return(-1);

		/* Again, not much more validation we can do. Either */
		/* the unlink() will work, or it won't. *shrug*. */
		return(0);



	case TR_EXEC:
		ret = cache_del(data->name,0);
		break;



	case TR_ABORT:
		/* Nothing to do in this case */
		break;
	}

	/* In exec and abort cases */
	free((mall_t)data->name);
	return(ret == 0 ? 0 : -1);
}




/* Zeros the location and contents of an object. put it in cold storage. */
tr_zeroobj(data,flag)
tr_opdata	*data;
int		flag;
{
	int	ret = 0;
	Obj	*o;

	switch(flag){
	case TR_VALIDATE:
		if(data->name == (char *)0)
			return(-1);
		if(!cache_check(data->name))
			return(-1);

		/* Again, not much more validation we can do. */
		return(0);



	case TR_EXEC:
		/* Nuke the location and contents list */
		if((o = cache_get(data->name)) == (Obj *)0)
			break;

		/* Nothing we CAN do in event of failure, so hope */
		(void)objunsetattr(o,var_cont);
		(void)objunsetattr(o,var_loc);
		(void)cache_put(o,data->name);
		break;



	case TR_ABORT:
		/* Nothing to do in this case */
		break;
	}


	/* In exec and abort cases */
	free((mall_t)data->name);                         
	return(ret == 0 ? 0 : -1);
}




/*
For inserting a player into a dest. The validate checks to see that the
DESTINATION exists. It does NOT check to see that the player does, because it
probably does not. The execute phase will fail if the player is not
successfully oif'd in by this time.
*/

tr_insply(data,flag)
tr_opdata	*data;
int		flag;
{
	char	*room = (data->insdel).there;
	char	*who = (data->insdel).this;

	switch(flag){
	case TR_VALIDATE:
		if(who == (char *)0 || room == (char *)0)
			return(-1);
		if(!cache_check(room))
			return(-1);

		/* Check for room-hood */
		if(!ut_flagged(room,var_isroom))
			return(-1);
		return(0);



	case TR_EXEC:
		if(ut_listadd(who,room,var_ply,who))
			return(-1);
		if(ut_set(who,who,typ_obj,var_loc,room))
			return(-1);
		break;



	case TR_ABORT:
		/* Don't do a thing */
		break;
	}


	/* In exec and abort cases: */
	free((mall_t) who);
	free((mall_t) room);
	return(0);
}




/*
For removing a player from a place. We just check that the place exists
'cause we aren't messing with the player object at this point.
*/
tr_remply(data,flag)
tr_opdata	*data;
int		flag;
{
	char *this = (data->insdel).this;
	char *there = (data->insdel).there;

	switch(flag){
	case TR_VALIDATE:
		if(this == (char *)0 || there == (char *)0)
			return(-1);
		if(!cache_check(there))
			return(-1);
		return(0);



	case TR_EXEC:
		if(ut_listdel(this,there,var_ply,this))
			return(-1);
		break;



	case TR_ABORT:
		/* Don't do anything */
		break;
	}


	/* In exec and abort cases */
	free((mall_t) this);
	free((mall_t) there);
	return(0);
}


/*
	A special thingy to see if a remote link can be made
*/
static	int
remote_linkok(who,to,from)
char *who;
char *to;
char *from;
{
	char	*lockp;

	lockp = ut_getatt(to,0,typ_bool,var_link,(char *)0);
	if(lockp == (char *)0)
		return(0);
	return(bool_eval(who,(char *)0,lockp,0));
}



/* Adding a link. Only the validate phase is substantive. */
tr_addlink(data,flag)
tr_opdata *data;
int flag;
{
	char *to = (data->linkchk).to;
	char *from = (data->linkchk).from;
	char *who = (data->linkchk).who;

	switch(flag){
	case TR_VALIDATE:
		/* Check the specified object for roomhood and linkokness */
		if(!ut_flagged(to,var_isroom))
			return(-1);
		if(!remote_linkok(who,to,from))
			return(-1);
		return(0);



	case TR_EXEC: /* Fallthru to cleanup */
	case TR_ABORT: /* Fallthru to cleanup */
		break;  /* VooDoo to shut the compiler up */
	}
	free((mall_t)to);
	free((mall_t)from);
	free((mall_t)who);
	return(0);
}