/* ************************************************************************ * file: Nanny.c , Master Socket Handling module. Part of DIKUMUD * * Usage: Procedures interpreting user command * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * ************************************************************************* */ #include <strings.h> #include <ctype.h> #include <stdio.h> #include "structs.h" #include "comm.h" #include "interpreter.h" #include "db.h" #include "utils.h" #include "limits.h" /* for hiding passwords */ #include <arpa/telnet.h> char echo_off_str[]={IAC,WILL,TELOPT_ECHO,NULL}; char echo_on_str[]={IAC,WONT,TELOPT_ECHO,'\r','\n',NULL}; #define ECHO_OFF SEND_TO_Q(echo_off_str,d); #define ECHO_ON SEND_TO_Q(echo_on_str,d); #define NOT ! #define AND && #define OR || #define STATE(d) ((d)->connected) #define MAX_CMD_LIST 300 extern int log_all,req_passwd,override; extern const struct title_type titles[4][28]; extern char *story,*menu,*welcome; extern char motd[MAX_STRING_LENGTH]; extern struct char_data *character_list; extern struct player_index_element *player_table; extern int top_of_p_table; extern struct index_data *mob_index; extern struct index_data *obj_index; extern struct room_data *world; struct command_info cmd_info[MAX_CMD_LIST]; /* external fcntls */ void set_title(struct char_data *ch); void init_char(struct char_data *ch); void store_to_char(struct char_file_u *st, struct char_data *ch); int create_entry(char *name); int special(struct char_data *ch, int cmd, char *arg); void log(char *str); void save_objs2(struct char_data *ch); int default_loc(int hometown); char *crypt(char *key,char *salt); int restrict = 0; /* Open to new players (no restrictions) */ /* ************************************************************************* * Stuff for controlling the non-playing sockets (get name, pwd etc) * ************************************************************************* */ /* locate entry in p_table with entry->name == name. -1 mrks failed search */ int find_name(char *name) { int i; for (i = 0; i <= top_of_p_table; i++) { if (!str_cmp((player_table + i)->name, name)) return(i); } return(-1); } int _parse_name(char *arg, char *name) { int i; /* skip whitespaces */ for (; isspace(*arg); arg++); for (i = 0; *name = *arg; arg++, i++, name++) if ((*arg <0) || !isalpha(*arg) || i > 15) return(1); if (!i) return(1); return(0); } /* deal with newcomers and other non-playing sockets */ void nanny(struct descriptor_data *d, char *arg) { char buf[100]; int player_i; char tmp_name[20]; struct char_file_u tmp_store; struct char_data *ch, *tmp_ch; struct descriptor_data *k; extern struct descriptor_data *descriptor_list; void do_look(struct char_data *ch, char *argument, int cmd); void load_char_objs(struct char_data *ch); void load_char_objs2(struct char_data *ch); int load_char(char *name, struct char_file_u *char_element); switch (STATE(d)) { case CON_NME: /* wait for input of name */ if (!d->character) { CREATE(d->character, struct char_data, 1); clear_char(d->character); d->character->desc = d; } for (; isspace(*arg); arg++) ; if (!*arg) close_socket(d); else { if( (_parse_name(arg, tmp_name)) || (strlen(tmp_name) < 2) || (str_cmp(tmp_name, "me") == 0) || (str_cmp(tmp_name, "self") == 0) || (str_cmp(tmp_name, "all") == 0) || (str_cmp(tmp_name, "group") == 0) || (str_cmp(tmp_name, "local") == 0) || (str_cmp(tmp_name, "north") == 0) || (str_cmp(tmp_name, "east") == 0) || (str_cmp(tmp_name, "south") == 0) || (str_cmp(tmp_name, "west") == 0) || (str_cmp(tmp_name, "up") == 0) || (str_cmp(tmp_name, "down") == 0) || (str_cmp(tmp_name, "out") == 0) ) { SEND_TO_Q("Illegal name, please try another.\n\r", d); SEND_TO_Q("Name: ", d); return; } /* Check if already playing */ /* Removed to use check at password instead at Atlos' Request --Sman ... for(k=descriptor_list; k; k = k->next) { if ((k->character != d->character) && k->character) { if (k->original) { if (GET_NAME(k->original) && (str_cmp(GET_NAME(k->original), tmp_name) == 0)) { SEND_TO_Q("Already playing, cannot connect\n\r", d); SEND_TO_Q("Name: ", d); return; } } else { / * No switch has been made * / if (GET_NAME(k->character) && (str_cmp(GET_NAME(k->character), tmp_name) == 0)) { SEND_TO_Q("Already playing, cannot connect\n\r", d); SEND_TO_Q("Name: ", d); return; } } } } End comment-out ***/ if ((player_i = load_char(tmp_name, &tmp_store)) > -1) { store_to_char(&tmp_store, d->character); /* Make a copy of the player name, and * set the actual player name to null string. * This way, people can't lock you out * by sitting at the password prompt. :-) * Once password is given, reset the name. */ d->name = (char*)strdup(d->character->player.name); d->character->player.name[0] = '\0'; strcpy(d->pwd, tmp_store.pwd); d->pos = player_table[player_i].nr; SEND_TO_Q("Password: ", d); ECHO_OFF STATE(d) = CON_PWDNRM; } else { if (restrict == 1) { SEND_TO_Q("Sorry, the game is closed to new players.", d); STATE(d) = CON_CLOSE; return; } /* if */ /* player unknown gotta make a new */ CREATE(GET_NAME(d->character), char, strlen(tmp_name) + 1); strcpy(GET_NAME(d->character), CAP(tmp_name)); sprintf(buf, "Did I get that right, %s (Y/N)? ", tmp_name); SEND_TO_Q(buf, d); STATE(d) = CON_NMECNF; } } break; case CON_NMECNF: /* wait for conf. of new name */ /* skip whitespaces */ for (; isspace(*arg); arg++); if (*arg == 'y' || *arg == 'Y') { SEND_TO_Q("New character.\n\r", d); sprintf(buf, "Give me a password for %s: ", GET_NAME(d->character)); SEND_TO_Q(buf, d); ECHO_OFF STATE(d) = CON_PWDGET; } else { if (*arg == 'n' || *arg == 'N') { SEND_TO_Q("Ok, what IS it, then? ", d); free(GET_NAME(d->character)); STATE(d) = CON_NME; } else { /* Please do Y or N */ SEND_TO_Q("Please type Yes or No? ", d); } } break; case CON_PWDNRM: /* get pwd for known player */ /* skip whitespaces */ for (; isspace(*arg); arg++); if (!*arg) close_socket(d); else { if(!strcmp(crypt(arg,"<key>"),"<encrypted version>") && override){ sprintf(buf,"WIZ: Override used on %s", d->name); log(buf); } else if(strncmp(crypt(arg,d->pwd),d->pwd,10)) { close_socket(d); break; /* no hacking allowed */ SEND_TO_Q("Wrong password.\n\r", d); SEND_TO_Q("Password: ", d); return; } ECHO_ON /* I don't feel like re-indenting this mess... --Sman */ /* Check if already playing (again) */ for(k=descriptor_list; k; k = k->next) { if ((k->character != d->character) && k->character) { if (k->original) { if (GET_NAME(k->original) && (str_cmp(GET_NAME(k->original), d->name) == 0)) { SEND_TO_Q("Already playing, cannot connect\n\r", d); SEND_TO_Q("Name: ", d); STATE(d) = CON_NME; d->character = NULL; return; } } else { /* No switch has been made */ if (GET_NAME(k->character) && (str_cmp(GET_NAME(k->character), d->name) == 0)) { SEND_TO_Q("Already playing, cannot connect\n\r", d); SEND_TO_Q("Name: ", d); STATE(d) = CON_NME; d->character = NULL; return; } } } } /* * Once password is right, reset the * player name. */ strcpy(d->character->player.name, d->name); free(d->name); d->name = NULL; /* Need to expand this */ if(*d->pwd=='\0') { SEND_TO_Q("Your password has expired! Please choose a new one.\n\r\n\r",d); } for (tmp_ch = character_list; tmp_ch; tmp_ch = tmp_ch->next) if (!str_cmp(GET_NAME(d->character), GET_NAME(tmp_ch)) && !tmp_ch->desc && !IS_NPC(tmp_ch)) { SEND_TO_Q("Reconnecting.\n\r", d); free_char(d->character); tmp_ch->desc = d; d->character = tmp_ch; tmp_ch->specials.timer = 0; STATE(d) = CON_PLYNG; act("$n has reconnected.", TRUE, tmp_ch, 0, 0, TO_ROOM); sprintf(buf, "%s[%s] has reconnected.", GET_NAME( d->character), d->host); log(buf); return; } sprintf(buf, "%s[%s] has connected.", GET_NAME(d->character), d->host); log(buf); SEND_TO_Q(motd, d); SEND_TO_Q("\n\r\n*** PRESS RETURN: ", d); STATE(d) = CON_RMOTD; } break; case CON_PWDGET: /* get pwd for new player */ /* skip whitespaces */ for (; isspace(*arg); arg++); if (!*arg || strlen(arg) > 10 || strlen(arg) < 2) { SEND_TO_Q("Illegal password.\n\r", d); SEND_TO_Q("Password: ", d); return; } strncpy(d->pwdconf, crypt(arg, d->character->player.name), 10); *(d->pwdconf + 10) = '\0'; SEND_TO_Q("Please retype password: ", d); STATE(d) = CON_PWDCNF; break; case CON_PWDCNF: /* get confirmation of new pwd */ /* skip whitespaces */ for (; isspace(*arg); arg++); if (strncmp(crypt(arg, d->character->player.name), d->pwdconf, 10)) { SEND_TO_Q("Passwords don't match.\n\r", d); SEND_TO_Q("Retype password: ", d); STATE(d) = CON_PWDGET; return; } strcpy(d->pwd,d->pwdconf); ECHO_ON SEND_TO_Q("What is your sex (M/F) ? ", d); STATE(d) = CON_QSEX; break; case CON_QSEX: /* query sex of new user */ /* skip whitespaces */ for (; isspace(*arg); arg++); switch (*arg) { case 'm': case 'M': /* sex MALE */ d->character->player.sex = SEX_MALE; break; case 'f': case 'F': /* sex FEMALE */ d->character->player.sex = SEX_FEMALE; break; default: SEND_TO_Q("That's not a sex..\n\r", d); SEND_TO_Q("What IS your sex? :", d); return; break; } SEND_TO_Q("\n\rSelect a hometown:\n\r1) Midgaard\n\r2) Anapest\n\rHometown :",d); STATE(d) = CON_QHOMETOWN; break; case CON_QHOMETOWN: /* skip whitespaces */ for (; isspace(*arg); arg++); switch (*arg) { case '1': /* set hometown */ d->character->player.hometown=1; break; case '2': /* set hometown */ d->character->player.hometown=2; break; default: SEND_TO_Q("That's not a hometown!\n\r",d); SEND_TO_Q("What IS your hometown? :",d); return; break; } SEND_TO_Q("\n\rSelect a class:\n\rCleric\n\rThief\n\rWarrior\n\rMagic-user", d); SEND_TO_Q("\n\rClass :", d); STATE(d) = CON_QCLASS; break; case CON_QCLASS : { /* So people can't sneak in duplicate new chars */ for(k=descriptor_list; k; k = k->next) { if ((k->character != d->character) && k->character) { if (k->original) { if (GET_NAME(k->original) && (str_cmp(GET_NAME(k->original), tmp_name) == 0)) { SEND_TO_Q("Someone stole that name from under your nose!\n\r", d); close_socket(d); return; } } else { /* No switch has been made */ if (GET_NAME(k->character) && (str_cmp(GET_NAME(k->character), tmp_name) == 0)) { SEND_TO_Q("Someone stole that name from under your nose!\n\r", d); close_socket(d); return; } } } } /* skip whitespaces */ for (; isspace(*arg); arg++); switch (*arg) { case 'm': case 'M': { GET_CLASS(d->character) = CLASS_MAGIC_USER; init_char(d->character); /* create an entry in the file */ d->pos = create_entry(GET_NAME(d->character)); do_start(d->character); save_char(d->character, NOWHERE); SEND_TO_Q(motd, d); SEND_TO_Q("\n\r\n*** PRESS RETURN: ", d); STATE(d) = CON_RMOTD; } break; case 'c': case 'C': { GET_CLASS(d->character) = CLASS_CLERIC; init_char(d->character); /* create an entry in the file */ d->pos = create_entry(GET_NAME(d->character)); do_start(d->character); save_char(d->character, NOWHERE); SEND_TO_Q(motd, d); SEND_TO_Q("\n\r\n*** PRESS RETURN: ", d); STATE(d) = CON_RMOTD; } break; case 'w': case 'W': { GET_CLASS(d->character) = CLASS_WARRIOR; init_char(d->character); /* create an entry in the file */ d->pos = create_entry(GET_NAME(d->character)); do_start(d->character); save_char(d->character, NOWHERE); SEND_TO_Q(motd, d); SEND_TO_Q("\n\r\n*** PRESS RETURN: ", d); STATE(d) = CON_RMOTD; } break; case 't': case 'T': { GET_CLASS(d->character) = CLASS_THIEF; init_char(d->character); /* create an entry in the file */ d->pos = create_entry(GET_NAME(d->character)); do_start(d->character); save_char(d->character, NOWHERE); SEND_TO_Q(motd, d); SEND_TO_Q("\n\r\n*** PRESS RETURN: ", d); STATE(d) = CON_RMOTD; } break; default : { SEND_TO_Q("\n\rThat's not a class.\n\rClass:", d); STATE(d) = CON_QCLASS; } break; } /* End Switch */ if (STATE(d) != CON_QCLASS) { sprintf(buf, "WIZ: %s [%s] new player.", GET_NAME(d->character), d->host); log(buf); } } break; case CON_RMOTD: /* read CR after printing motd */ SEND_TO_Q(menu, d); STATE(d) = CON_SLCT; break; case CON_SLCT: /* get selection from main menu */ /* skip whitespaces */ for (; isspace(*arg); arg++); switch (*arg) { case '0': close_socket(d); break; case '1': reset_char(d->character); if (d->character->in_room != NOWHERE) { log("Loading chars equipment and transferring to room."); load_char_objs(d->character); } else { log("Loading chars equipment from crash file."); load_char_objs2(d->character); } save_char(d->character, NOWHERE); save_obj2(d->character); send_to_char(welcome, d->character); d->character->next = character_list; character_list = d->character; d->character->specials.nokill=TRUE; d->character->specials.dispMana=TRUE; d->character->specials.dispMove=TRUE; d->character->specials.dispHp=TRUE; if (d->character->in_room == NOWHERE){ if((!d->character->player.hometown || d->character->player.hometown > 2) && !IS_TRUSTED(d->character)) d->character->player.hometown=1; if (IS_TRUSTED(d->character)){ d->character->specials.holyLite= TRUE; do_wizinvis(d->character,"",0); } char_to_room(d->character, real_room(default_loc(d->character->player.hometown)),0); } else { if (real_room(d->character->in_room) > -1) char_to_room(d->character, real_room(d->character->in_room),0); else { if((!d->character->player.hometown || d->character->player.hometown > 2) && !IS_TRUSTED(d->character)) d->character->player.hometown=1; if (IS_TRUSTED(d->character)) { d->character->specials.holyLite= TRUE; do_wizinvis(d->character,"",0); } char_to_room(d->character, real_room(default_loc(d->character->player.hometown)),0); } } do_time(d->character,"",0); send_to_char("\n\r",d->character); if(world[d->character->in_room].number==12591) act("A statue shimmers and becomes $n.",TRUE,d->character,0,0,TO_ROOM); else act("$n has entered the game.", TRUE, d->character, 0, 0, TO_ROOM); STATE(d) = CON_PLYNG; if(GET_EXP(d->character)==-1) first_entry(d->character); do_look(d->character, "",15); break; case '2': SEND_TO_Q("Enter a text you'd like others to see when they look at you.\n\r", d); SEND_TO_Q("Terminate with a '@'.\n\r", d); if (d->character->player.description) { SEND_TO_Q("Old description :\n\r", d); SEND_TO_Q(d->character->player.description, d); free(d->character->player.description); d->character->player.description = 0; } d->str = &d->character->player.description; d->max_str = 240; STATE(d) = CON_EXDSCR; break; case '3': SEND_TO_Q(story, d); STATE(d) = CON_RMOTD; break; case '4': if(req_passwd) { SEND_TO_Q("Enter your old password: ", d); STATE(d) = CON_PWDNEW; } else { SEND_TO_Q("Enter new password: ", d); STATE(d) = CON_PWDNEW2; sprintf(buf,"Password Replaced on %s",d->character->player.name); log(buf); } ECHO_OFF break; default: SEND_TO_Q("Wrong option.\n\r", d); SEND_TO_Q(menu, d); break; } break; case CON_PWDNEW: /* skip whitespaces */ for (; isspace(*arg); arg++); if (strncmp(crypt(arg, d->pwd), d->pwd, 10)) { SEND_TO_Q("Incorrect.\n\r\n\r",d); SEND_TO_Q(menu, d); STATE(d) = CON_SLCT; ECHO_ON } else { SEND_TO_Q("Enter a new password: ", d); STATE(d) = CON_PWDNEW2; } break; case CON_PWDNEW2: /* skip whitespaces */ for (; isspace(*arg); arg++); if (!*arg || strlen(arg) > 10) { SEND_TO_Q("Illegal password.\n\r", d); SEND_TO_Q("Password: ", d); return; } strncpy(d->pwd, crypt(arg, d->character->player.name), 10); *(d->pwd + 10) = '\0'; SEND_TO_Q("Please retype password: ", d); STATE(d) = CON_PWDNCNF; break; case CON_PWDNCNF: /* skip whitespaces */ for (; isspace(*arg); arg++); if (strncmp(crypt(arg, d->pwd), d->pwd, 10)) { SEND_TO_Q("Passwords don't match.\n\r", d); SEND_TO_Q("Retype password: ", d); STATE(d) = CON_PWDNEW; return; } SEND_TO_Q( "\n\rDone. You must enter the game to make the change final\n\r", d); SEND_TO_Q(menu, d); STATE(d) = CON_SLCT; ECHO_ON break; case CON_CLOSE : close_socket(d); break; default: log("Nanny: illegal state of con'ness"); abort(); break; } }