pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
/* player.c */

#include "copyrite.h"
#include "config.h"
#include <stdio.h>
#ifdef I_UNISTD
#include <unistd.h>
#endif
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif
#include <fcntl.h>

#include "conf.h"
#include "mushdb.h"
#include "intrface.h"
#include "externs.h"
#include "access.h"
#include "confmagic.h"


extern time_t mudtime;
extern int reserved;
extern void add_player _((dbref player, char *alias));

int password_check _((dbref player, const char *password));
dbref connect_player _((const char *name, const char *password, const char *host));
dbref create_player _((const char *name, const char *password, const char *host));
dbref email_register_player _((const char *name, const char *email, const char *host));
static dbref make_player _((const char *name, const char *password, const char *host));
void do_password _((dbref player, const char *old, const char *newobj));
void check_last _((dbref player, const char *host));
void check_lastfailed _((dbref player, const char *host));

int
password_check(player, password)
    dbref player;
    const char *password;
{
  ATTR *a;

  /* read the password and compare it */
  if ((a = atr_get_noparent(player, "XYXXY")) &&
      strcmp(uncompress(a->value), password) &&
      strcmp(mush_crypt(password), uncompress(a->value)))
    return 0;

#if (CRYPT_SYSTEM > 0)
  /* prevent direct entry of the raw encrypted password */
  if ((strlen(password) == 13) &&
      (password[0] == 'X') && (password[1] == 'X'))
    return 0;
#endif

  /* we're okay */
  return 1;
}

dbref
connect_player(name, password, host)
    const char *name;
    const char *password;
    const char *host;
{
  dbref player;
  dbref i;

  /* validate name */
  if ((player = lookup_player(name)) == NOTHING)
    return NOTHING;

  /* See if player is allowed to connect like this */
  if (Guest(player) && !Site_Can_Guest(host)) {
    do_log(LT_CONN, 0, 0, "Connection to %s (GUEST) not allowed from %s",
	   name, host);
    return NOTHING;
  } else if (!Guest(player) && !Site_Can_Connect(host)) {
    do_log(LT_CONN, 0, 0, "Connection to %s (Non-GUEST) not allowed from %s",
	   name, host);
    return NOTHING;
  }
  /* validate password */
  if (!Guest(player))
    if (!password_check(player, password)) {
#ifdef CREATION_TIMES
      /* Increment count of login failures */
      ModTime(player)++;
#endif
      check_lastfailed(player, host);
      return NOTHING;
    }
  /* If it's a Guest player, and already connected, search the
   * db for another Guest player to connect them to. */
  if (Guest(player) && Connected(player)) {
    /* Search db for an unconnected Guest */
    for (i = 0; i < db_top; i++) {
      if ((Typeof(i) == TYPE_PLAYER) && !Hasprivs(i) &&
	  Guest(i) && !Connected(i)) {
	player = i;
	break;
      }
    }
    if (i == db_top) {
      /* We failed to find an unconnected Guest */
      do_log(LT_CONN, 0, 0, "Multiple connection to Guest #%d", player);
    }
  }
  if (Suspect_Site(host)) {
    do_log(LT_CONN, 0, 0, "Connection from Suspect site. Setting %s(#%d) suspect.", Name(player), player);
    Toggles(player) |= PLAYER_SUSPECT;
  }
  return player;
}

/* This function returns NOTHING if the player name was bad, and
 * AMBIGUOUS if the player password was bad
 */
dbref
create_player(name, password, host)
    const char *name;
    const char *password;
    const char *host;
{
  if (!ok_player_name(name)) {
    do_log(LT_CONN, 0, 0, "Failed creation (bad name) from %s", host);
    return NOTHING;
  }
  if (!ok_password(password)) {
    do_log(LT_CONN, 0, 0, "Failed creation (bad password) from %s", host);
    return AMBIGUOUS;
  }
  /* else he doesn't already exist, create him */
  return make_player(name, password, host);
}

/* The HAS_SENDMAIL ifdef is kept here as a hint to metaconfig */
#ifdef MAILER
#undef HAS_SENDMAIL
#define HAS_SENDMAIL 1
#undef SENDMAIL
#define SENDMAIL MAILER
#endif

#ifdef HAS_SENDMAIL
dbref
email_register_player(name, email, host)
    const char *name;
    const char *email;
    const char *host;
{
  char *p;
  char passwd[BUFFER_LEN];
  static char elems[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  int i, len;
  dbref player;
  FILE *fp;

  if (!ok_player_name(name)) {
    do_log(LT_CONN, 0, 0, "Failed registration (bad name) from %s", host);
    return NOTHING;
  }
  /* Make sure that the email address is valid. A valid address must
   * contain either an @ or a !
   * Also, to prevent someone from using the MUSH to mailbomb another site,
   * let's make sure that the site to which the user wants the email
   * sent is also allowed to use the register command.
   * If there's an @, we check whatever's after the last @
   * (since @foo.bar:user@host is a valid email)
   * If not, we check whatever comes before the first !
   */
  if ((p = strrchr(email, '@'))) {
    p++;
    if (!Site_Can_Register(p)) {
      do_log(LT_CONN, 0, 0, "Failed registration (bad site in email: %s) from %s", email, host);
      return NOTHING;
    }
  } else if ((p = strchr(email, '!'))) {
    *p = '\0';
    if (!Site_Can_Register(email)) {
      *p = '!';
      do_log(LT_CONN, 0, 0, "Failed registration (bad site in email: %s) from %s", email, host);
      return NOTHING;
    } else
      *p = '!';
  } else {
    do_log(LT_CONN, 0, 0, "Failed registration (bad email: %s) from %s", email, host);
    return NOTHING;
  }


  /* Come up with a random password of length 7-12 chars */
  len = 7 + getrandom(6);
  for (i = 0; i < len; i++)
    passwd[i] = elems[getrandom(strlen(elems))];
  passwd[len] = '\0';

  /* If we've made it here, we can send the email and create the
   * character. Email first, since that's more likely to go bad.
   * Some security precautions we'll take:
   *  1) We'll use sendmail -t, so we don't pass user-given values to a shell.
   */

#ifndef WIN32
  close(reserved);
#endif
  if ((fp = popen(tprintf("%s -t", SENDMAIL), "w")) == NULL) {
    do_log(LT_CONN, 0, 0, "Failed registration of %s by %s: unable to open sendmail", name, email);
#ifndef WIN32
    reserved = open("/dev/null", O_RDWR);
#endif
    return NOTHING;
  }
  fprintf(fp, "Subject: [%s] Registration of %s\n", MUDNAME, name);
  fprintf(fp, "To: %s\n", email);
  fprintf(fp, "Precedence: junk\n");
  fprintf(fp, "\n");
  fprintf(fp, "This is an automated message.\n");
  fprintf(fp, "\n");
  fprintf(fp, "Your requested player, %s, has been created.\n", name);
  fprintf(fp, "The password is %s\n", passwd);
  fprintf(fp, "\n");
  fprintf(fp, "To access this character, connect to %s and type:\n", MUDNAME);
  fprintf(fp, "\tconnect %s %s\n", name, passwd);
  fprintf(fp, "\n");
  pclose(fp);
#ifndef WIN32
  reserved = open("/dev/null", O_RDWR);
#endif
  /* Ok, all's well, make a player */
  player = make_player(name, passwd, host);
  (void) atr_add(player, "REGISTERED_EMAIL", email, GOD, NOTHING);
  return player;
}
#else
dbref
email_register_player(name, email, host)
    const char *name;
    const char *email;
    const char *host;
{
  do_log(LT_CONN, 0, 0, "Failed registration (no sendmail) from %s", host);
  return NOTHING;
}
#endif

static dbref
make_player(name, password, host)
    const char *name;
    const char *password;
    const char *host;
{

  dbref player;
  char *s;
#ifdef QUOTA
  char temp[SBUF_LEN];
#endif

  /* else he doesn't already exist, create him */
  s = ctime(&mudtime);
  s[strlen(s) - 1] = '\0';
  if (s[8] == ' ')
    s[8] = '0';
  player = new_object();

  /* initialize everything */
  SET(Name(player), name);
  Location(player) = PLAYER_START;
  Home(player) = PLAYER_START;
  Owner(player) = player;
  db[player].parent = NOTHING;
  Flags(player) = TYPE_PLAYER;
  Flags(player) |= options.player_flags;
  Toggles(player) |= options.player_toggles;
  if (Suspect_Site(host))
    Toggles(player) |= PLAYER_SUSPECT;
#ifdef USE_WARNINGS
  set_initial_warnings(player);
#endif
#ifdef CREATION_TIMES
  /* Modtime tracks login failures */
  ModTime(player) = (time_t) 0;
#endif
  atr_add(player, "XYXXY", mush_crypt(password), GOD, NOTHING);
  giveto(player, START_BONUS);	/* starting bonus */
  (void) atr_add(player, "LAST", s, GOD, NOTHING);
  (void) atr_add(player, "LASTSITE", host, GOD, NOTHING);
  (void) atr_add(player, "LASTFAILED", " ", GOD, NOTHING);
#ifdef QUOTA
  sprintf(temp, "%d", START_QUOTA);
  (void) atr_add(player, "RQUOTA", temp, GOD, NOTHING);
#endif				/* QUOTA */
#ifdef FIXED_FLAG
#ifndef EMPTY_ATTRS
  (void) atr_add(player, "ICLOC", " ", GOD, AF_MDARK | AF_PRIVATE | AF_WIZARD | AF_NOCOPY);
#else
  (void) atr_add(player, "ICLOC", "", GOD, AF_MDARK | AF_PRIVATE | AF_WIZARD | AF_NOCOPY);
#endif
#endif
#ifdef USE_MAILER
  (void) atr_add(player, "MAILCURF", "0", GOD, AF_LOCKED | AF_NOPROG | AF_WIZARD);
  add_folder_name(player, 0, "inbox");
#endif
  /* link him to PLAYER_START */
  PUSH(player, db[PLAYER_START].contents);

  add_player(player, NULL);
  add_lock(player, Basic_Lock, parse_boolexp(player, "=me"));
  add_lock(player, Enter_Lock, parse_boolexp(player, "=me"));

#ifdef LOCAL_DATA
  local_data_create(player);
#endif

  return player;
}


void
do_password(player, old, newobj)
    dbref player;
    const char *old;
    const char *newobj;
{

  if (Guest(player)) {
    notify(player, "Guests may not change their passwords.");
    return;
  }
  if (!password_check(player, old)) {
    notify(player, "Sorry");
  } else if (!ok_password(newobj)) {
    notify(player, "Bad new password.");
  } else {
    atr_add(player, "XYXXY", mush_crypt(newobj), GOD, NOTHING);
    notify(player, "Password changed.");
  }
}

void
check_last(player, host)
    dbref player;
    const char *host;
{
  char *s;
  ATTR *a;
  ATTR *h;
  char last_time[MAX_COMMAND_LEN / 8];
  char last_place[MAX_COMMAND_LEN];

  s = ctime(&mudtime);
  s[strlen(s) - 1] = 0;
  /* compare to last connect see if player gets salary */
  a = atr_get_noparent(player, "LAST");
  if (a && (strncmp(uncompress(a->value), s, 10) != 0))
    giveto(player, PAY_CHECK);

  /* tell the player where he last connected from */
  h = atr_get_noparent(player, "LASTSITE");
  if (h && a) {
    strcpy(last_place, uncompress(h->value));
    strcpy(last_time, uncompress(a->value));
    notify(player, tprintf("Last connect was from %s on %s.",
			   last_place, last_time));
  }
  /* How about last failed connection */
  h = atr_get_noparent(player, "LASTFAILED");
  if (h && a) {
    strcpy(last_place, uncompress(h->value));
    if (strlen(last_place) > 2)
      notify(player, tprintf("Last FAILED connect was from %s.",
			     last_place));
  }
  /* if there is no Lastsite, then the player is newly created.
   * the extra variables are a kludge to work around some weird
   * behavior involving uncompress.
   */

  /* set the new attributes */
  (void) atr_add(player, "LAST", s, GOD, NOTHING);
  (void) atr_add(player, "LASTSITE", host, GOD, NOTHING);
  (void) atr_add(player, "LASTFAILED", " ", GOD, NOTHING);
}


/* This is called on a failed connection */
void
check_lastfailed(player, host)
    dbref player;
    const char *host;
{
  char *s;
  char last_place[MAX_COMMAND_LEN];

  s = ctime(&mudtime);
  s[strlen(s) - 1] = 0;
  sprintf(last_place, "%s on %s", host, s);
  (void) atr_add(player, "LASTFAILED", last_place, GOD, NOTHING);
}