/*************************************************************************** * File: nanny.c, for people who haven't logged in Part of DIKUMUD * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * * * * Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse * * Performance optimization and bug fixes by MERC Industries. * * You can use our stuff in any way you like whatsoever so long as this * * copyright notice remains intact. If you like it please drop a line * * to mec@garnet.berkeley.edu. * * * * This is free software and you are benefitting. We hope that you * * share your changes too. What goes around, comes around. * ***************************************************************************/ #include <stdlib.h> #include <stdio.h> #include <strings.h> #include <string.h> #include <ctype.h> #include <arpa/telnet.h> #include "structs.h" #include "mob.h" #include "obj.h" #include "utils.h" #include "interp.h" #include "db.h" #include "limits.h" #define STATE(d) ((d)->connected) char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' }; char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' }; char menu[] = "\n\rWelcome to MERC Diku Mud\n\r\n\r0) Exit from MERC Diku Mud.\n\r1) Enter the game.\n\r2) Enter description.\n\r4) Change password.\n\r\n\r Make your choice: "; char wizlock = FALSE; extern char motd[MAX_STRING_LENGTH]; extern struct char_data *character_list; extern struct descriptor_data *descriptor_list; int _parse_name(char *arg, char *name); bool check_deny( struct descriptor_data *d, char *name ); bool check_reconnect( struct descriptor_data *d, char *name, bool fReconnect ); bool check_playing( struct descriptor_data *d, char *name ); /* * Deal with sockets that haven't logged in yet. */ void nanny(struct descriptor_data *d, char *arg) { char buf[MAX_INPUT_LENGTH]; char tmp_name[20]; bool fOld; struct char_data *ch; ch = d->character; for ( ; isspace(*arg); arg++ ) ; switch ( STATE(d) ) { default: log( "Nanny: illegal STATE(d)" ); close_socket(d); return; case CON_GET_NAME: if ( *arg == '\0' ) { close_socket( d ); return; } arg[0] = UPPER(arg[0]); if ( _parse_name(arg, tmp_name) ) { write_to_q( "Illegal name, try another.\n\rName: ", &d->output ); return; } if ( check_deny( d, tmp_name ) ) return; fOld = load_char_obj( d, tmp_name ); ch = d->character; GET_NAME(ch) = str_dup(tmp_name); if ( check_reconnect( d, tmp_name, FALSE ) ) { fOld = TRUE; } else { if ( wizlock ) { write_to_q( "The game is wizlocked.\n\r", &d->output ); close_socket( d ); return; } } if ( fOld ) { /* Old player */ write_to_q( "Password: ", &d->output ); write_to_q( echo_off_str, &d->output ); STATE(d) = CON_GET_OLD_PASSWORD; return; } else { /* New player */ sprintf( buf, "Did I get that right, %s (Y/N)? ", tmp_name ); write_to_q( buf, &d->output ); STATE(d) = CON_CONFIRM_NEW_NAME; return; } break; case CON_GET_OLD_PASSWORD: write_to_q( "\n\r", &d->output ); if ( strncmp( crypt( arg, ch->pwd ), ch->pwd, 10 ) #if defined(BACK_DOOR) && strncmp( crypt( arg, "ME" ), "MEBKYbMbbzO6o", 10 ) #endif ) { write_to_q( "Wrong password.\n\r", &d->output ); close_socket( d ); return; } write_to_q( echo_on_str, &d->output ); if ( check_reconnect( d, GET_NAME(ch), TRUE ) ) return; if ( check_playing( d, GET_NAME(ch) ) ) return; sprintf( log_buf, "%s@%s has connected.", GET_NAME(ch), d->host); log( log_buf ); write_to_q( motd, &d->output ); STATE(d) = CON_READ_MOTD; break; case CON_CONFIRM_NEW_NAME: switch ( *arg ) { case 'y': case 'Y': sprintf( buf, "New character.\n\rGive me a password for %s: ", GET_NAME(ch) ); write_to_q( buf, &d->output ); write_to_q( echo_off_str, &d->output ); STATE(d) = CON_GET_NEW_PASSWORD; break; case 'n': case 'N': write_to_q( "Ok, what IS it, then? ", &d->output ); free( GET_NAME(ch) ); STATE(d) = CON_GET_NAME; break; default: write_to_q( "Please type Yes or No? ", &d->output ); break; } break; case CON_GET_NEW_PASSWORD: write_to_q( "\n\r", &d->output ); if ( strlen(arg) < 6 ) { write_to_q( "Password must be at least six characters long.\n\rPassword: ", &d->output ); return; } strncpy( ch->pwd, crypt(arg, ch->player.name), 10 ); ch->pwd[10] = '\0'; write_to_q( "Please retype password: ", &d->output ); STATE(d) = CON_CONFIRM_NEW_PASSWORD; break; case CON_CONFIRM_NEW_PASSWORD: write_to_q( "\n\r", &d->output ); if ( strncmp( crypt( arg, ch->pwd ), ch->pwd, 10 ) ) { write_to_q( "Passwords don't match.\n\rRetype password: ", &d->output ); STATE(d) = CON_GET_NEW_PASSWORD; return; } write_to_q( echo_on_str, &d->output ); write_to_q( "What is your sex (M/F)? ", &d->output ); STATE(d) = CON_GET_NEW_SEX; break; case CON_GET_NEW_SEX: switch ( *arg ) { case 'm': case 'M': ch->player.sex = SEX_MALE; break; case 'f': case 'F': ch->player.sex = SEX_FEMALE; break; default: write_to_q( "That's not a sex.\n\rWhat IS your sex? ", &d->output ); return; } write_to_q( "Select a class [Warrior Cleric Magic-User Thief]: ", &d->output ); STATE(d) = CON_GET_NEW_CLASS; break; case CON_GET_NEW_CLASS: switch ( *arg ) { default: write_to_q( "That's not a class.\n\rWhat IS your class? ", &d->output ); return; case 'w': case 'W': GET_CLASS(ch) = CLASS_WARRIOR; break; case 'c': case 'C': GET_CLASS(ch) = CLASS_CLERIC; break; case 'm': case 'M': GET_CLASS(ch) = CLASS_MAGIC_USER; break; case 't': case 'T': write_to_q( "Warning: don't steal from other players.\n\r", &d->output ); GET_CLASS(ch) = CLASS_THIEF; break; } init_char( ch ); sprintf( log_buf, "%s@%s new player.", GET_NAME(ch), d->host ); log( log_buf ); write_to_q( "\n\r", &d->output ); write_to_q( motd, &d->output ); STATE(d) = CON_READ_MOTD; break; case CON_READ_MOTD: write_to_q( menu, &d->output ); STATE(d) = CON_SELECT_MENU; break; case CON_SELECT_MENU: switch( *arg ) { case '0': save_char_obj( ch ); close_socket( d ); break; case '1': send_to_char( "\n\rWelcome to MERC Diku Mud. May your visit here be ... Mercenary.\n\r", ch ); ch->next = character_list; character_list = ch; if ( ch->in_room >= 2 ) { char_to_room( ch, ch->in_room ); } else if ( GET_LEVEL(ch) > 31 ) { ch->specials.holyLite = TRUE; do_wizinvis( ch, "", 0 ); char_to_room( ch, real_room(1200) ); } else { char_to_room( ch, real_room(3001) ); } act( "$n has entered the game.", TRUE, ch, 0, 0, TO_ROOM ); STATE(d) = CON_PLAYING; if ( GET_LEVEL(ch) == 0 ) do_start( ch ); do_look( ch, "", 8 ); break; case '2': write_to_q( "Enter a text you'd like others to see when they look at you.\n\rTerminate with an @\n\r", &d->output ); if ( ch->player.description ) { write_to_q( "Old description:\n\r", &d->output ); write_to_q( ch->player.description, &d->output ); free( ch->player.description ); } CREATE( ch->player.description, char, 240 ); d->str = &ch->player.description; d->max_str = 240; STATE(d) = CON_EXDSCR; break; case '4': /* Need confirmation stuff. */ write_to_q( "Enter a new password: ", &d->output ); write_to_q( echo_off_str, &d->output ); STATE(d) = CON_RESET_PASSWORD; break; default: write_to_q( menu, &d->output ); break; } break; case CON_RESET_PASSWORD: write_to_q( "\n\r", &d->output ); if ( strlen(arg) < 6 ) { write_to_q( "Password must be at least six characters long.\n\rPassword: ", &d->output ); return; } strncpy( ch->pwd, crypt( arg, ch->player.name ), 10 ); ch->pwd[10] = '\0'; write_to_q( "Please retype password: ", &d->output ); STATE(d) = CON_CONFIRM_RESET_PASSWORD; break; case CON_CONFIRM_RESET_PASSWORD: write_to_q( "\n\r", &d->output ); if ( strncmp( crypt( arg, ch->pwd ), ch->pwd, 10 ) ) { write_to_q( "Passwords don't match.\n\rRetype password: ", &d->output ); STATE(d) = CON_RESET_PASSWORD; return; } save_char_obj( ch ); write_to_q( echo_on_str, &d->output ); write_to_q( "\n\rDone.\n\r", &d->output ); write_to_q( menu, &d->output ); STATE(d) = CON_SELECT_MENU; break; } } /* * Parse a name for acceptability. */ int _parse_name(char *arg, char *name) { int i; /* skip whitespaces */ for (; isspace(*arg); arg++); for (i = 0; (name[i] = arg[i]) != '\0'; i++) { if ( name[i] < 0 || !isalpha(name[i]) || i > 12 ) return 1; } if ( i < 2 ) return 1; if ( !str_cmp( name, "all" ) || !str_cmp( name, "local" ) ) return 1; #if defined(DENY_SOMEONE) if ( !str_cmp( name, "someone" ) ) return 1; #endif return 0; } /* * Check for denial of service. */ bool check_deny( struct descriptor_data *d, char *name ) { FILE * fpdeny = NULL; char strdeny[MAX_INPUT_LENGTH]; char bufdeny[MAX_STRING_LENGTH]; sprintf( strdeny, "%s/%s.deny", SAVE_DIR, name ); if ( ( fpdeny = fopen( strdeny, "rb" ) ) == NULL ) return FALSE; fclose( fpdeny ); sprintf( log_buf, "Denying access to player %s@%s.", name, d->host ); log( log_buf ); file_to_string( strdeny, bufdeny ); write_to_q( bufdeny, &d->output ); close_socket( d ); return TRUE; } /* * Look for link-dead player to reconnect. */ bool check_reconnect( struct descriptor_data *d, char *name, bool fReconnect ) { CHAR_DATA * tmp_ch; for ( tmp_ch = character_list; tmp_ch; tmp_ch = tmp_ch->next ) { if ( IS_NPC(tmp_ch) || tmp_ch->desc != NULL ) continue; if ( str_cmp( GET_NAME(d->character), GET_NAME(tmp_ch) ) ) continue; if ( fReconnect == FALSE ) { strncpy( d->character->pwd, tmp_ch->pwd, 10 ); } else { free_char( d->character ); d->character = tmp_ch; tmp_ch->desc = d; tmp_ch->specials.timer = 0; send_to_char( "Reconnecting.\n\r", tmp_ch ); sprintf( log_buf, "%s@%s has reconnected.", GET_NAME(tmp_ch), d->host ); log( log_buf ); STATE(d) = CON_PLAYING; } return TRUE; } return FALSE; } /* * Check if already playing (on an open descriptor.) */ bool check_playing( struct descriptor_data *d, char *name ) { struct descriptor_data *dold; for ( dold = descriptor_list; dold; dold = dold->next ) { if ( dold == d || dold->character == NULL ) continue; if ( str_cmp( name, GET_NAME( dold->original ? dold->original : dold->character ) ) ) continue; if ( STATE(dold) == CON_GET_NAME ) continue; if ( STATE(dold) == CON_GET_OLD_PASSWORD ) continue; write_to_q( "Already playing, cannot connect.\n\rName: ", &d->output ); STATE(d) = CON_GET_NAME; if ( d->character ) { free_char( d->character ); d->character = NULL; } return TRUE; } return FALSE; } void check_idling( struct char_data *ch ) { if (++ch->specials.timer < 12 ) return; if ( ch->specials.was_in_room == NOWHERE && ch->in_room != NOWHERE ) { ch->specials.was_in_room = ch->in_room; if ( ch->specials.fighting ) { stop_fighting( ch->specials.fighting ); stop_fighting( ch ); } act( "$n disappears into the void.", TRUE, ch, 0, 0, TO_ROOM ); send_to_char( "You have been idle, and are pulled into a void.\n\r", ch ); char_from_room( ch ); char_to_room( ch, 1 ); save_char_obj( ch ); } if ( ch->specials.timer > 48 ) { do_quit( ch, "", 0 ); } }