/*
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);
}