Player email registration code

From: Brian Gray (mandy@tmarts.com)
Date: 03/26/96


  Ok guys, here's the relevent section of the nanny() function for 
automatic player registration.  It's a two-hour hack, and I know there 
has to be a memory leak in there, along with a potential reference to an 
invalid memory address (occurs during attachment).  If anyone can find 
the bugs, I'd be most appreciative.  Integration of the code into Circle 
is left as an excercise for the reader.

------------------ Begin Code --------------------------

in config.c:
char *register_alert =
"                    *** New character registration ***\n\r"
"\r\n"
"Every character on The Multiplex must be registered with a valid, current\r\n"
"e-mail address.  If you have already registered a character, you may\r\n"
"attach this one to it and it will assume the other's e-mail address.  If\r\n"
"this is your first Multiplex character, please enter your full internet\r\n"
"e-mail address in the form userid@host below.  You will be sent a\r\n"
"message with your initial password that may be used to enter the game\r\n"
"the first time.  After that, we encourage you to change it.  If you\r\n"
"experience any difficulties with the registration process, please send an\r\n"
"e-mail to mandy@tmarts.com and we'll do our best to remedy the 
situation.\r\n"
"\r\n"
"Please enter your e-mail address or (a)ttach: ";

char *register_subject = "\"Multiplex character registration\"";

char *register_body =
"Greetings from The Multiplex!\n"
"\n"
" This message is to confirm that the person who owns this e-mail account is\n"
"indeed the same person who is applying to register a character on a multi-\n"
"user internet game entitled The Multiplex.  If you have never heard of the\n"
"game or did not recently attempt to create a new character, please let us\n"
"know so you won't receive any more erronous messages.\n"
"\n"
" If you are the person who is registering the character, your initial pass-\n"
"word follows.  You will only have to do this once, as any future characters\n"
"may be attached to this account.\n"
"\n"
"Thank you for playing The Multiplex.  Your password is: ";

in interpreter.c:
void nanny(struct descriptor_data * d, char *arg) {
  FILE *outfile;
  char buf[1024], filename[128];
  int player_i, load_result, i, rnum, code, written=0;
  char tmp_name[MAX_INPUT_LENGTH], *host;
  struct char_file_u tmp_store;
  struct char_data *tmp_ch;
  struct obj_data *obj;
  struct descriptor_data *k, *next;
  extern struct descriptor_data *descriptor_list;
  extern sh_int r_mortal_start_room;
  extern sh_int r_immort_start_room;
  extern sh_int r_frozen_start_room;
  extern int newbiekit[OBJ_IN_NEWBIEKIT];

  extern const char *class_menu;
  extern int max_bad_pws;
  sh_int load_room;

  int load_char(char *name, struct char_file_u * char_element);
  int parse_class(char arg);
  void obj_to_char(struct obj_data * object, struct char_data * ch);

  skip_spaces(&arg);

  switch (STATE(d)) {
  case CON_GET_NAME:            /* wait for input of name */
    if (d->character == NULL) {
      CREATE(d->character, struct char_data, 1);
      clear_char(d->character);
      CREATE(d->character->player_specials, struct player_special_data, 1);
      CREATE(d->character->prefs, struct char_prefs, 1);
      d->character->desc = d;
    }
    if (!*arg)
      close_socket(d);
    else {
      if ((_parse_name(arg, tmp_name)) || strlen(tmp_name) < 2 ||
          strlen(tmp_name) > MAX_NAME_LENGTH ||
          fill_word(strcpy(buf, tmp_name)) || reserved_word(buf)) {
        SEND_TO_Q("Invalid name, please try another.\r\n"
                  "Name: ", d);
        return;
      }
      if ((player_i = load_char(tmp_name, &tmp_store)) > -1) {
        store_to_char(&tmp_store, d->character);
        GET_PFILEPOS(d->character) = player_i;

        if (PLR_FLAGGED(d->character, PLR_DELETED)) {
          free_char(d->character);
          CREATE(d->character, struct char_data, 1);
          clear_char(d->character);
          CREATE(d->character->player_specials, struct player_special_data, 1);
          CREATE(d->character->prefs, struct char_prefs, 1);
          d->character->desc = d;
          CREATE(d->character->player.name, char, strlen(tmp_name) + 1);
          strcpy(d->character->player.name, CAP(tmp_name));
          GET_PFILEPOS(d->character) = player_i;
          sprintf(buf, "Did I get that right, %s (Y/N)? ", tmp_name);
          SEND_TO_Q(buf, d);
          STATE(d) = CON_NAME_CNFRM;
        } else {
          /* undo it just in case they are set */
          REMOVE_BIT(PLR_FLAGS(d->character),
                     PLR_WRITING | PLR_MAILING | PLR_CRYO);

          SEND_TO_Q("Password: ", d);
          echo_off(d);

          STATE(d) = CON_PASSWORD;
        }
      } else {
        /* player unknown -- make new character */

        if (!Valid_Name(tmp_name)) {
          SEND_TO_Q("Invalid name, please try another.\r\n", d);
          SEND_TO_Q("Name: ", d);
          return;
        }
        CREATE(d->character->player.name, char, strlen(tmp_name) + 1);
        strcpy(d->character->player.name, CAP(tmp_name));

        sprintf(buf, "Did I get that right, %s (Y/N)? ", tmp_name);
        SEND_TO_Q(buf, d);
        STATE(d) = CON_NAME_CNFRM;
      }
    }
    break;
  case CON_NAME_CNFRM:          /* wait for conf. of new name    */
    if (UPPER(*arg) == 'Y') {
      if (isbanned(d->host) >= BAN_NEW) {
        sprintf(buf, "Request for new char %s denied from [%s] (siteban)",
                GET_NAME(d->character), d->host);
        mudlog(buf, NRM, LVL_WRIT, TRUE);
        SEND_TO_Q("Sorry, new characters are not allowed from your 
site!\r\n", d
);
        STATE(d) = CON_CLOSE;
        return;
      }
      if (restrict) {
        SEND_TO_Q("Sorry, new players can't be created at the moment.\r\n", d);
        sprintf(buf, "Request for new char %s denied from %s (wizlock)",
                GET_NAME(d->character), d->host);
        mudlog(buf, NRM, LVL_WRIT, TRUE);
        STATE(d) = CON_CLOSE;
        return;
      }
      *GET_PASSWD(d->character)  = '\0';
      SEND_TO_Q("What is your sex (M/F)? ", d);
      STATE(d) = CON_QSEX;

/*  Taken out for registration
      sprintf(buf, "Give me a password for %s: ", GET_NAME(d->character));
      SEND_TO_Q(buf, d);
      echo_off(d);
      STATE(d) = CON_NEWPASSWD;
*/

    } else if (*arg == 'n' || *arg == 'N') {
      SEND_TO_Q("Okay, what IS it, then? ", d);
      free(d->character->player.name);
      d->character->player.name = NULL;
      STATE(d) = CON_GET_NAME;
    } else {
      SEND_TO_Q("Please type Yes or No: ", d);
    }
    break;

  case CON_QCLASS:
    load_result = parse_class(*arg);
    if (load_result == CLASS_UNDEFINED) {
      SEND_TO_Q("\r\nThat's not a class.\r\nClass: ", d);
      return;
    } else {
      GET_CLASS(d->character) = load_result;
    }
    if (GET_PFILEPOS(d->character) < 0) {
      GET_PFILEPOS(d->character) = create_entry(GET_NAME(d->character));
    }
    init_char(d->character);
    SEND_TO_Q(register_alert, d);
    STATE(d) = CON_REGISTER;
    sprintf(buf, "%s [%s] new player.", GET_NAME(d->character), d->host);
    mudlog(buf, NRM, LVL_CELEB, TRUE);
    break;

/* Player Registration Code -- BG Jan 1996 */

  case CON_REGISTER:           /* Enter e-mail address          */
    if (!*arg) close_socket(d);

/* He wants to attach to another player */
    else if (!strcmp(arg, "a")) {
      SEND_TO_Q("Name of character to whom to attach: ", d);
      STATE(d) = CON_ATTACH;
      return;
    } else {

/* Does the address include a '@'? */
      if ((host = strchr(arg, '@'))) host++;
      else {
        SEND_TO_Q("Address should be in the form userid@host.\r\n", d);
        SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
        return;
      }

/* Does the given hostname exist? */
      if (!(gethostbyname(host))) {
        SEND_TO_Q("Invalid internet host.\r\n", d);
        SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
        return;
      }

/* Are they trying to register as root?  Specifically we don't want to get
   Rett pissed because he's receiving this damn mail message all the 
time. */
      if (strstr(arg, "root")) {
        SEND_TO_Q("You cannot register as root.\r\n", d);
        SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
        return;
      }

/* No real email address should be longer than 80 characters.  If it is, 
sucks
   to be you. */
      if (strlen(arg) > EMAIL_LENGTH) {
        SEND_TO_Q("Address too long.\r\n", d);
        SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
        return;
      }

/* If we made it here, go ahead and register the player */
      CREATE(d->character->email, char, strlen(arg) + 1);
      strncpy(d->character->email, arg, EMAIL_LENGTH);
      *(GET_EMAIL(d->character) + EMAIL_LENGTH) = '\0';

/* Make sure the password is at least 4 characters */
      do {code = rand();} while (code < 1000);

/* ...and apply it */
      sprintf(buf, "%i", code);
      strncpy(GET_PASSWD(d->character), CRYPT(buf, 
GET_NAME(d->character)), MAX_
PWD_LENGTH);
      *(GET_PASSWD(d->character) + MAX_PWD_LENGTH) = '\0';

/* Assemble file for sending */
      sprintf(buf, "%s%i", register_body, code);
      sprintf(filename, "%s_reg", d->character->player.name);
      if (!(outfile = fopen(filename, "w"))) {
        log("Error opening registration file.");
        STATE(d) = CON_CLOSE;
        return;
      }
      if (fputs(buf, outfile) < 0) {
        log("Error writing registration file.");
        STATE(d) = CON_CLOSE;
        return;
      }
      if (fclose(outfile)) {
        log("Error closing registration file.");
        STATE(d) = CON_CLOSE;
        return;
      } else written = 1;

/* The code can get ahead of the disk drive here, so wait til it's ready */
      while (!written) {}

/* Send the mail */
      sprintf(buf, "elm -s %s %s < %s", register_subject, arg, filename);
      system(buf);

/* Remove the tempfile */
      sprintf(buf, "rm %s", filename);
      system(buf);

/* Tell him he's set and boot him */
      sprintf(buf, "\r\nYou are now registered to: %s\r\n\r\n", 
d->character->em
ail);
      SEND_TO_Q(buf, d);
      SEND_TO_Q("You will be receiving your initial password 
momentarily\r\n", d
);
      SEND_TO_Q("via e-mail.  It should be received almost 
instantaneously and c
an\r\n", d);
      SEND_TO_Q("immediately be used to log in your character.  See you 
soon.\r\
n\r\n", d);
      save_char(d->character, NOWHERE);
      STATE(d) = CON_CLOSE;
    }
    break;

  case CON_ATTACH:              /* Use e-mail address of another char */
    if (!*arg) close_socket(d);

/* Does the player exist? */
    else if (load_char(arg, &tmp_store) > -1) {

/* Is he deleted? */
      if (tmp_store.char_specials_saved.act & PLR_DELETED) {
        SEND_TO_Q("No player by that name.\r\n", d);
        SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
        STATE(d) = CON_REGISTER;
        return;
      } else {

/* We have to remember the name of the attachee, so for the time being stuff
   it into the email slot in the attacher and go on to the password */
        CREATE(d->character->email, char, strlen(arg) + 1);
        strncpy(d->character->email, arg, EMAIL_LENGTH);
        SEND_TO_Q("Password: ", d);
        echo_off(d);
        STATE(d) = CON_ATTCH_PWD;
        return;
      }
    } else {

/* Player doesn't exist. */
      SEND_TO_Q("No player by that name.\r\n", d);
      SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
      STATE(d) = CON_REGISTER;
      return;
    }
    break;

  case CON_ATTCH_PWD:           /* Enter other char's passwd     */
    echo_on(d);
    if (!*arg) close_socket(d);
    else {

/* Get the attachee's name from the email slot of the attacher and load 
him */
      load_char(d->character->email, &tmp_store);

/* Compare the given password to the attachee's password */
      if (strncmp(CRYPT(arg, tmp_store.name), tmp_store.pwd, 
MAX_PWD_LENGTH)) {
        SEND_TO_Q("Wrong Password.\r\n", d);
        SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
        STATE(d) = CON_REGISTER;
      } else {

/* You can only attach to a registered player */
        if (strlen(tmp_store.email) < 4) {
          SEND_TO_Q("Sorry, that player has no e-mail address on 
record.\r\n", d
);
          SEND_TO_Q("Enter e-mail address or (a)ttach: ", d);
          STATE(d) = CON_REGISTER;
          return;
        }

/* If we're here, go ahead and attach the poor sap */
        CREATE(d->character->email, char, strlen(arg) + 1);
        strncpy(d->character->email, tmp_store.email, EMAIL_LENGTH);
        *(GET_EMAIL(d->character) + EMAIL_LENGTH) = '\0';
        strncpy(GET_PASSWD(d->character), CRYPT(arg, 
GET_NAME(d->character)), MA
X_PWD_LENGTH);
        *(GET_PASSWD(d->character) + MAX_PWD_LENGTH) = '\0';
        sprintf(buf, "You are now attached to: %s.\r\n", 
d->character->email);
        SEND_TO_Q(buf, d);
        save_char(d->character, NOWHERE);

/* ...and set him playing */
        if (GET_LEVEL(d->character) >= LVL_CELEB)
          SEND_TO_Q(imotd, d);
        else
          SEND_TO_Q(motd, d);

        sprintf(buf, "%s [%s] has connected.", 
GET_NAME(d->character),d->host);
        mudlog(buf, BRF, MAX(LVL_CELEB, GET_INVIS_LEV(d->character)), TRUE);

        if (load_result) {
          sprintf(buf, "\r\n\r\n\007\007\007"
                "%s%d LOGIN FAILURE%s SINCE LAST SUCCESSFUL LOGIN.%s\r\n",
                CCRED(d->character, C_SPR), load_result,
                (load_result > 1) ? "S" : "", CCNRM(d->character, C_SPR));
          SEND_TO_Q(buf, d);
        }
        SEND_TO_Q("\r\n\n*** PRESS RETURN: ", d);
        STATE(d) = CON_RMOTD;
        return;
      }
    }
    break;

/* End Player Registration Code */

  case CON_MENU:                /* get selection from main menu  */
    switch (*arg) {
    case '0':
      close_socket(d);
      break;

    case '1' to '5': [...]

    case '6':
      SEND_TO_Q("\r\nEnter your e-mail address or (a)ttach: ", d);
      STATE(d) = CON_REGISTER;
      break;

------------------- End Code ---------------------------

 -- Mandy, Coding Director of The Multiplex
 -- pccs.cs.pomona.edu 4747
 -- http://www.tmarts.com/Multiplex



This archive was generated by hypermail 2b30 : 12/07/00 PST