/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* utils.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

/* AIX requires this to be the first thing in the file. */
#ifdef __GNUC__
#define alloca	__builtin_alloca
#else	/* not __GNUC__ */
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#else	/* not HAVE_ALLOCA_H */
#ifdef _AIX
 #pragma alloca
#endif	/* not _AIX */
#endif	/* not HAVE_ALLOCA_H */
#endif 	/* not __GNUC__ */

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif				/* HAVE_STRING_H */
#include <ctype.h>
#ifdef TM_IN_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif				/* TM_IN_SYS_TIME */
#include <sys/stat.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif				/* HAVE_MALLOC_H */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif				/* HAVE_STDLIB_H */

#include "conf.h"
#include "teeny.h"
#include "commands.h"
#include "ptable.h"
#include "sha/sha_wrap.h"
#include "externs.h"

/* Some generic utility functions for TeenyMUD */

int create_player(name, pwd, code)
    char *name, *pwd;
    int code;
{
  int player, ret = 0;
  int flags[FLAGS_LEN];

  /* No guests can be created from the greet screen. */
  if ((code == -1) 
      && (!ok_player_name(name) || (strncasecmp(name, "guest", 5) == 0)
	  || (pwd == (char *)NULL)))
    return(-1);

  /* Create a player in object number STARTING_LOC */
  player = create_obj(TYP_PLAYER);
  ret = set_str_elt(player, NAME, name);
  if ((ret != -1) && (pwd != (char *)NULL) && pwd[0])
    ret = attr_add(player, PASSWORD, cryptpwd(pwd), PASSWORD_FLGS);
  if (code == -1) {
    if (ret != -1) {
      list_add(player, mudconf.starting_loc, CONTENTS);
      ret = set_int_elt(player, LOC, mudconf.starting_loc);
    }
  } else {
    if (ret != -1) {
      list_add(player, code, CONTENTS);
      ret = set_int_elt(player, LOC, code);
    }
  }
  if (ret != -1)
    ret = set_int_elt(player, HOME, mudconf.starting_loc);
  if (ret != -1)
    ret = set_int_elt(player, OWNER, player);
  if (ret != -1)
    ret = get_flags_elt(player, FLAGS, flags);
  if (ret != -1) {
    flags[0] |= (mudconf.newp_flags)[0];
    flags[1] |= (mudconf.newp_flags)[1];
    ret = set_flags_elt(player, FLAGS, flags);
  }
  if (ret != -1)
    ret = set_int_elt(player, QUOTA, mudconf.starting_quota);
  if (ret != -1)
    ret = set_int_elt(player, PENNIES, mudconf.starting_money);

  if (ret == -1) {
    destroy_obj(player);
    return (-1);
  }
  return (player);
}

int connect_player(name, pwd)
    char *name, *pwd;
{
  char *realpwd;
  int player, aflags;

  /*
   * Special goo for multiple guest character support.
   */
  if(strcasecmp(name, "guest") == 0) {	/* They're connecting to guest. */
    player = match_player(name);
    if(player == -1) {		/* No "guest", look for "guestN". */
      int gn;
      char sbuf[10];

      for(gn = 1; gn < 256; gn++) {
	snprintf(sbuf, sizeof(sbuf), "guest%d", gn);

	player = match_player(sbuf);
	if(player == -1)
	  return(-1);
	
	/* Is it already connected? */
	if(isALIVE(player))
	  continue;

	/* Otherwise, we found the right one. */
	break;
      }
    }
  } else {
    char *sptr;

    player = match_player(name);
    if (player == -1) {
      if((name[0] == '#') && isdigit(name[1])) {
        player = (int)strtol(&name[1], &sptr, 10);
	if((sptr != &name[1]) && exists_object(player) && isPLAYER(player))
	  return(player);
      }
      return (-1);
    }
  }

  /* Check the password */
  if (attr_get(player, PASSWORD, &realpwd, &aflags) == -1) {
    logfile(LOG_ERROR,
	    "connect_player: bad attribute reference on object #%d\n", player);
    return (-1);
  }

  if (((realpwd == (char *)NULL) || (realpwd[0] == '\0'))
      && (pwd == (char *)NULL))
    return (player);
  if ((realpwd != (char *)NULL) && (realpwd[0] != '\0')
      && (pwd != (char *)NULL) && comp_password(realpwd, pwd))
    return (player);
  return (-1);
}

void announce_connect(player, user, host)
    int player;
    char *user, *host;
{
  int loc;
  char *name;
  char buf[256];
  int wflags[FLAGS_LEN];

  if (get_int_elt(player, LOC, &loc) != -1) {
    if (get_str_elt(player, NAME, &name) != -1) {
      if (!isDARK(player) && !isDARK(loc)) {
	snprintf(buf, sizeof(buf), "%s has %s.", name, isALIVE(player) ?
		 "reconnected" : "connected");
	notify_all(player, player, player, buf, NOT_NOPUPPET);
      }

      if(user != (char *)NULL) {
        snprintf(buf, sizeof(buf), "[ %s has connected from %s@%s. ]", name,
		user, host);
      } else {
        snprintf(buf, sizeof(buf), "[ %s has connected from %s. ]", name, host);
      }

      wflags[0] = RETENTIVE;
      wflags[1] = 0;
      tcp_wall(wflags, buf, -1);
    }
  }
}

void announce_disconnect(player)
    int player;
{
  int loc;
  char *name;
  char buf[80];
  int wflags[FLAGS_LEN];

  if (get_int_elt(player, LOC, &loc) != -1) {
    if (get_str_elt(player, NAME, &name) != -1) {
      if (!isDARK(player) && !isDARK(loc)) {
	snprintf(buf, sizeof(buf), "%s has %s.", name, isALIVE(player) ?
		 "partially disconnected" : "disconnected");
	notify_all(player, player, player, buf, NOT_NOPUPPET);
      }
      snprintf(buf, sizeof(buf), "[ %s has disconnected. ]", name);

      wflags[0] = RETENTIVE;
      wflags[1] = 0;
      tcp_wall(wflags, buf, -1);
    }
  }
  if (isGUEST(player) && !isALIVE(player)) {
    /* send "GUEST" players to their home */
    do_home(player, player, 0);
  }
}

/* Hack name/pwd pair up. */
int parse_name_pwd(str, name, pwd)
    char *str, **name, **pwd;
{
  static char nbuf[MAXPNAMELEN + 1], pbuf[MAXPWDLEN + 1];
  char *p;

  if (str == (char *)NULL)
    return(-1);

  while (*str && isspace(*str))
    str++;
  if (*str == '\0')
    return (-1);

  if (*str != '\"') {		/* the name isn't quoted, fuck */
    /* assume the first word is name, all other is pword */
    for (p = nbuf; *str && !isspace(*str) && (p - nbuf) < sizeof(nbuf);
	 *p++ = *str++);
    *p = '\0';

    while (isspace(*str) && *str)
      str++;
    if (*str == '\0')
      *pwd = (char *) NULL;
    else {
      for (p = pbuf; *str && (p - pbuf) < sizeof(pbuf); *p++ = *str++);
      *p = '\0';
      *pwd = pbuf;		/* Assume no trailing whitespace. */
    }
    *name = nbuf;
    return (0);
  } else {			/* quoted name! YAY! */
    while (*str && *str == '\"')
      str++;
    while (*str && isspace(*str))
      str++;
    p = nbuf;
    *name = nbuf;
    while (*str && *str != '\"' && (p - nbuf) < sizeof(nbuf)) {
      if (!isspace(*str)) {
	*p++ = *str++;
	continue;
      }
      for (; *str && isspace(*str); str++);
      if (*str && (*str != '\"'))
	*p++ = ' ';
    }
    *p = '\0';

    while (*str && (*str == '\"' || isspace(*str)))
      str++;
    if (*str == '\0')
      *pwd = (char *) NULL;
    else {
      for (p = pbuf; *str && (p - pbuf) < sizeof(pbuf); *p++ = *str++);
      *p = '\0';
      *pwd = pbuf;
    }
    return (0);
  }
}

void parse_type(str, ret)
    char *str;
    int *ret;
{
  switch(str[0]) {
  case 'p':
  case 'P':
    *ret = TYP_PLAYER;
    break;
  case 'r':
  case 'R':
    *ret = TYP_ROOM;
    break;
  case 't':
  case 'T':
    *ret = TYP_THING;
    break;
  case 'e':
  case 'E':
    *ret = TYP_EXIT;
    break;
  default:
    *ret = -1;
  }
}

int ok_attr_name(name)
    char *name;
{
  return (name && *name && (strlen(name) < MAXATTRNAME) &&
	  !strchr(name, '*') && !strchr(name, '?') &&
	  !strchr(name, '\\') && !strchr(name, '[') &&
	  !strchr(name, ']'));
}

int ok_attr_value(data)
    char *data;
{
  register int i;

  for (i = 0; data[i] && (i < 4097); i++) {
    if (!isprint(data[i]) || (data[i] == '\r') || (data[i] == '\n'))
      return (0);
  }
  return (i < 4097);
}

int ok_name(name)
    char *name;
{
  register int i;

  if (!name || !*name || (name[0] == '#') || (name[0] == '!'))
    return (0);
  for (i = 0; name[i]; i++) {
    if ((name[i] == '=') || (name[i] == '&') || (name[i] == '|')
	|| (name[i] == '*') || (name[i] == '?') || (name[i] == '\\')
	|| (name[i] == '[') || (name[i] == ']') || (name[i] == ':')
	|| (name[i] == '<') || (name[i] == '>') || (name[i] == '&')
	|| (name[i] == '\"') || !isprint(name[i]))
      return (0);
  }

  /* Hard coded bad object names. */
  return (strcmp(name, "A") && strcmp(name, "An") && strcmp(name, "The")
	  && strcmp(name, "You") && strcmp(name, "Your")
          && strcmp(name, "Going") && strcmp(name, "Huh?")
	  && strcmp(name, "me") && strcmp(name, "home")
	  && strcmp(name, "here"));
}

int ok_player_name(name)
    char *name;
{
  /* Do the obvious checkeds, first. */
  if (!ok_name(name) || (strlen(name) > MAXPNAMELEN) ||
      (match_player(name) != -1))
    return (0);
  else {
    int index;

    /* Check for configured bad player names. */
    for(index = 0; index < 64; index++) {
      if(mudconf.bad_pnames[index] != (char *)NULL) {
        if(strcmp(mudconf.bad_pnames[index], name) == 0)
	  return(0);
      } else
        break;
    }
    return (1);
  }
}

int ok_exit_name(name)
    char *name;
{
  char *p, *q, *buf;

  if (!ok_name(name))
    return (0);

  buf = (char *) alloca(strlen(name) + 1);
  if(buf == (char *)NULL)
    panic("ok_exit_name(): stack allocation failed\n");
  strcpy(buf, name);

  /* we have to check each sub string... */
  q = buf;
  while (q && *q) {
    for (p = q; *p && *p != ';'; p++);
    if (*p)
      *p++ = '\0';
    if (!ok_name(q))
      return (0);
    q = p;
  }
  return (1);
}

void reward_money(player, amount)
    int player, amount;
{
  int pennies, owner;

  if(!mudconf.enable_money)
    return;

  if (get_int_elt(player, OWNER, &owner) == -1) {
    logfile(LOG_ERROR, "reward_money: bad owner reference on #%d\n", player);
    return;
  }
  if (isWIZARD(owner))
    return;

  if (get_int_elt(owner, PENNIES, &pennies) == -1) {
    logfile(LOG_ERROR, "reward_money: bad pennies reference on #%d\n", owner);
    return;
  }
  if (set_int_elt(owner, PENNIES, pennies + amount) == -1) {
    logfile(LOG_ERROR, "reward_money: bad pennies reference on #%d\n", owner);
    return;
  }
}

int can_afford(player, cause, amount, quiet)
    int player, cause, amount, quiet;
{
  int pennies, owner;

  if(!mudconf.enable_money)
    return(1);

  if (get_int_elt(player, OWNER, &owner) == -1) {
    logfile(LOG_ERROR, "can_afford: bad owner reference on #%d\n", player);
    return (0);
  }
  if (isWIZARD(owner))
    return (1);

  if (get_int_elt(owner, PENNIES, &pennies) == -1) {
    logfile(LOG_ERROR, "can_afford: bad pennies reference on #%d\n", owner);
    return (0);
  }
  if (pennies > amount) {
    if (set_int_elt(owner, PENNIES, pennies - amount) == -1) {
      logfile(LOG_ERROR, "can_afford: bad pennies reference on #%d\n", owner);
      return (0);
    }
    return (1);
  }
  if(!quiet)
    notify_player(player, cause, player, "You don't have enough pennies.",
    		  NOT_QUIET);
  return (0);
}

void check_paycheck(player)
    int player;
{
  time_t last;
  int pennies;

  if(!mudconf.enable_money)
    return;

  if (isWIZARD(player) ||
      (get_int_elt(player, TIMESTAMP, (int *) &last) == -1) ||
      (get_int_elt(player, PENNIES, &pennies) == -1))
    return;
  if (last <= 0)
    return;
  if ((pennies < mudconf.max_pennies) &&
      ((localtime(&mudstat.now))->tm_yday > (localtime(&last))->tm_yday)) {
    if (set_int_elt(player, PENNIES, pennies + mudconf.daily_paycheck) == -1)
      return;
  }
}

void check_last(player)
    int player;
{
  char *host;
  time_t last;
  int aflags;
  char buf[MEDBUFFSIZ];

  if ((get_int_elt(player, TIMESTAMP, (int *) &last) == -1)
      || (attr_get(player, SITE, &host, &aflags) == -1))
    return;
  if (last <= 0)
    return;

  strftime(buf, MEDBUFFSIZ, "Last connection: %a %b %e %H:%M:%S %Z %Y from ",
  	   localtime(&last));
  strcat(buf, (host == NULL) ? "???" : host);
  notify_player(player, player, player, buf, 0);
}

/* spew a file to a player, following special permissions. */
void dump_file(player, cause, thing, fname)
    int player, cause, thing;
    char *fname;
{
  FILE *fp;
  char fbuf[128], mbuf[BUFFSIZ];
#ifndef __STDC__
  char *strstr();
#endif

  /* If there is a leading slash, object must be set FILE_OK. */
  if(fname[0] == '/') {
    if(!isFILE_OK(thing)) {
      snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname);
      notify_player(player, cause, thing, mbuf, 0);
      return;
    }
    snprintf(mbuf, sizeof(mbuf), "%s/%s", mudconf.files_path, fname);
  } else
    snprintf(mbuf, sizeof(mbuf), "%s/#%d/%s", mudconf.files_path, thing, fname);

  /* If it has a "../" in it, fail. */
  if(strstr(fname, "../") != (char *)NULL) {
    snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname);
    notify_player(player, cause, thing, mbuf, 0);
    return;
  }

  fp = fopen(mbuf, "r");
  if(fp == (FILE *)NULL) {
    snprintf(mbuf, sizeof(mbuf), "%s: Can't open file.", fname);
    notify_player(player, cause, thing, mbuf, 0);
    return;
  }
  while(!feof(fp) && (fgets(fbuf, sizeof(fbuf), fp) != (char *)NULL)) {
    remove_newline(fbuf);
    notify_player(player, cause, thing, fbuf, 0);
  }
  fclose(fp);
}

/* spew a shell command at the player, following special permissions. */
void dump_cmdoutput(player, cause, thing, fname)
    int player, cause, thing;
    char *fname;
{
  FILE *fp;
  char fbuf[128], mbuf[BUFFSIZ];
#ifndef __STDC__
  char *strstr();
#endif


  /* If there is a leading slash, object must be set FILE_OK. */
  if(fname[0] == '/') {
    if(!isFILE_OK(thing)) {
      snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname);
      notify_player(player, cause, thing, mbuf, 0);
      return;
    }
    snprintf(mbuf, sizeof(mbuf), "%s/%s", mudconf.files_path, fname);
  } else
    snprintf(mbuf, sizeof(mbuf), "%s/#%d/%s", mudconf.files_path, thing, fname);

  /* If it has a "../" in it, fail. */
  if(strstr(fname, "../") != (char *)NULL) {
    snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname);
    notify_player(player, cause, thing, mbuf, 0);
    return;
  }

  fp = popen(mbuf, "r");
  if(fp == (FILE *)NULL) {
    snprintf(mbuf, sizeof(mbuf), "%s: Can not execute.", fname);
    notify_player(player, cause, thing, mbuf, 0);
    return;
  }
  while(!feof(fp) && (fgets(fbuf, sizeof(fbuf), fp) != (char *)NULL)) {
    remove_newline(fbuf);
    notify_player(player, cause, thing, fbuf, 0);
  }
  pclose(fp);
}

int legal_roomloc_check(source, test, check_depth)
    int source, test;
    int *check_depth;
{
  int loc;

  (*check_depth)++;

  if (source == test)
    return (0);
  if (!exists_object(test) || !isROOM(test))
    return (0);
  if (test == mudconf.root_location)
    return (1);
  if ((*check_depth) > mudconf.room_depth)
    return (0);
  if (get_int_elt(test, LOC, &loc) == -1)
    return (0);
  return (legal_roomloc_check(source, loc, check_depth));
}

int legal_thingloc_check(obj, dest)
    int obj, dest;
{
  int loc, depth;

  depth = 0;

  if (isROOM(obj) || isEXIT(obj))
    return(1);
  if (obj == dest)
    return(0);

  loc = dest;
  while(!isROOM(loc) && (depth < mudconf.room_depth)) {
    if ((loc == obj) || ((depth > 0) && (loc == dest)))
      return(0);

    if (get_int_elt(loc, LOC, &loc) == -1)
      return(0);

    depth++;
  }

  if(isROOM(loc))
    return(1);
  return(0);
}

int legal_parent_check(obj, test, check_depth)
    int obj, test;
    int *check_depth;
{
  int parent;

  (*check_depth)++;

  if (test == -1)
    return (1);
  if ((obj == test) || !exists_object(test))
    return (0);
  if ((*check_depth) > mudconf.parent_depth)
    return (0);
  if (get_int_elt(test, PARENT, &parent) == -1)
    return (0);
  return (legal_parent_check(obj, parent, check_depth));
}

int legal_recursive_exit(exit, test, check_depth)
    int exit, test, *check_depth;
{
  int *dests;
  register int i;

  (*check_depth)++;

  if ((test == -1) || (test == -3) || (exists_object(test) && !isEXIT(test)))
    return (1);
  if ((exit == test) || !exists_object(test))
    return (0);
  if ((*check_depth) > mudconf.exit_depth)
    return (0);
  if (get_array_elt(test, DESTS, &dests) == -1)
    return (0);

  if (dests != (int *)NULL) {
    for(i = 1; i <= dests[0]; i++) {
      if(dests[i] == exit) {
        return(0);
      }
      if (Typeof(dests[i]) == TYP_EXIT) {
        if (!legal_recursive_exit(exit, dests[i], check_depth))
	  return(0);
      }
    }
  }
  return(1);
}

/*
 * HTML support code.
 *
 * html_anchor_exit() takes an exit name and returns an anchor for it.
 *
 * html_anchor_contents() takes an element from the contents list and
 * returns an anchor for it.
 *
 * html_anchor_location() checks the player's location for a URL, and sends
 * it to them.
 *
 * html_desc() checks for an Htdesc, and sends it.
 */
char *html_anchor_exit(name)
    char *name;
{
  static char ret[2048];
  char *sname, *ptr;

  for(ptr = name; *ptr && (*ptr != ';'); ptr++);
  if(((*ptr == '\0') || (*ptr == ';')) && ((ptr - name) > 0)) {
    sname = (char *)alloca((ptr - name)+1);
    if(name == (char *)NULL) {
      panic("html_anchor_exit: stack allocation failed!");
    }
    strncpy(sname, name, (ptr - name));
    sname[(ptr - name)] = '\0';
  
    snprintf(ret, sizeof(ret), "<a xch_cmd=\"%s\" xch_hint=\"Go %s\">%s</a>",
	     sname, sname, sname);
    return(ret);
  } else
    return(name);
}

char *html_anchor_contents(sname, name)
    char *sname, *name;
{
  static char ret[2048];

  snprintf(ret, sizeof(ret),
	   "<a xch_cmd=\"look %s\" xch_hint=\"Look at %s\">%s</a>", sname,
	   sname, name);
  return(ret);
}

void html_anchor_location(player, cause)
    int player, cause;
{
  int loc, aflags, source;
  char *attr, *buf;

  if((get_int_elt(player, LOC, &loc) == -1) || !exists_object(loc))
    return;

  if((attr_get_parent(loc, VRML_URL, &attr, &aflags, &source) == 0)
     && (attr != (char *)NULL)) {
    buf = (char *)alloca(strlen(attr)+30);
    if(buf == (char *)NULL) {
      panic("html_anchor_location: stack allocation failed!");
    }

    strcpy(buf, "<img xch_graph=load href=\"<");
    strcat(buf, attr);
    strcat(buf, "\">");

    notify_player(player, cause, player, buf, NOT_RAW);
  }
}

void html_desc(player, cause, thing)
    int player, cause, thing;
{
  int aflags, source;
  char *attr;

  if((attr_get_parent(thing, HTDESC, &attr, &aflags, &source) == 0)
     && (attr != (char *)NULL)) {
    notify_player(player, cause, player, attr, NOT_RAW);
  }
}

#ifdef UNIX_CRYPT

/*
 * Extra GOO to invisibly support both crypt(3) and SHA passwords, for
 * old databases.
 */

int comp_password(encrypt, string)
    char *encrypt, *string;
{
  /* If the encrypted string is 13 characters long, assume it's crypt(3). */
  if(strlen(encrypt) == 13) {
    char salt[3];

    salt[0] = encrypt[0];
    salt[1] = encrypt[1];
    salt[2] = '\0';
    return(strcmp((char *)crypt(string, salt), encrypt) == 0);
  }
  return(strcmp(sha_crypt(string), encrypt) == 0);
}
#endif			/* UNIX_CRYPT */