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