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