untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
/*
    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);
}