/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
/* configure all options BEFORE including system stuff. */
#include "config.h"
#include "mud.h"
#include "vars.h"
#include "sbuf.h"
#include "trans.h"
#include "xact.h"
/* xmit.c */
extern int xc_sbread (Sbuf * sb);
static int xact_grtserver (SOCKET fd);
static int xact_connectto (char *mud);
static int xact_sendobj (char *nam, tr_prog * prog, int flag);
static int xact_recobj(char *name, tr_prog *p);
static int xact_linkto(char *s, tr_prog *p);
static int xact_addto(char *s, tr_prog *p);
static int xact_end(char *s, tr_prog *p);
static int xact_abort(char *s, tr_prog *p);
typedef struct {
char *name;
int (*handler) ();
} xact_cmd;
static xact_cmd *xact_cmdmatch (char *s);
xact_cmd xact_cmd_tab[] = {
{"OBJECT", xact_recobj},
{"LINKTO", xact_linkto},
{"ADDTO", xact_addto},
{"ENDTRANS", xact_end},
{"ABORT", xact_abort},
{(char *) 0, (int (*)()) 0}
};
/* handle an incoming connection from a remote server. */
void xact_in (SOCKET 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);
(void) 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);
(void) 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);
}
}
(void) 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.
*/
int xact_sendplayer (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 */
/* The MUD part is the bit after the LAST @. We allow multiple @s */
for (q = dest, mud = (char *) 0; *q; q++) {
if (*q == '@')
mud = q;
}
if (!*mud) {
sbuf_freestatic (&suf);
return (-1);
}
strncpy (dst, dest, mud - dest);
dst[mud - dest] = '\0';
mud++;
/* 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, sizeof (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, sizeof (nxtu));
}
}
if ((in =
ut_getatt (p, 0, typ_list, var_wearing, (char *) 0)) != (char *) 0) {
in = lstnext (in, nxtu, sizeof (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 wearing a local object.\n", (char *) 0);
goto abort;
}
if (xact_sendobj (nxtu, prog, 0))
goto abort;
in = lstnext (in, nxtu, sizeof (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;
}
#ifdef COMBAT
if ((in = ut_getatt (p, 0, typ_obj, var_weapon, (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 weapon.\n", (char *) 0);
goto abort;
}
if (xact_sendobj (in, prog, 0))
goto abort;
}
#endif
/* 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);
(void) xc_flush ();
}
/* Get response. */
if (!xc_sbread (&suf) || strcmp (sbuf_buf (&suf), "OK")) {
log_printf ("Failed transaction ", sbuf_buf (&suf), "\n", (char *) 0);
goto fail;
}
#ifdef XACT_DEBUG
log_printf ("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);
(void) 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 */
int xact_addlink (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);
/* Mud part follows LAST @. we allow multiple @'s */
for (q = to, mud = (char *) 0; *q; q++) {
if (*q == '@')
mud = q;
}
if (mud == (char *) 0 || !*mud) {
sbuf_freestatic (&suf);
return (-1);
}
strncpy (dst, to, mud - to);
dst[mud - to] = '\0';
mud++;
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")) {
log_printf ("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 */
static int xact_connectto (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.
*/
static int xact_sendobj (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 < (int) 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 */
void xact_tinylink (char *who)
{
char linkmsg[512];
snprintf (linkmsg, sizeof (linkmsg),
"#### Please reconnect to %s@%s (%s) port %s ####", xc_getmudname (),
xc_getips (), xc_gethostname (), xc_getport ());
say (who, linkmsg, "\n", (char *) 0);
(void) ut_set (who, who, typ_str, var_linkmsg, linkmsg);
}
/* Match a string against the command table. */
static xact_cmd *xact_cmdmatch (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);
}
/*
Handles introduing ourselves, finding out who the guy on the other end of
the fd is, and ensuring it's OK.
*/
static int xact_grtserver (SOCKET 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)) {
log_printf ("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) {
log_printf ("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)) {
log_printf ("Unknown remote MUD '", name, "'\n", (char *) 0);
sbuf_freestatic (&s);
return (1);
}
if (check_rempwd (pwd)) {
log_printf ("Remote MUD '", name, "' with bad password '",
pwd, "'\n", (char *) 0);
sbuf_freestatic (&s);
return (1);
}
/* Send out our initial string */
(void) 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);
}