//***************************************************************************** // // account_handler.c // // The login and creation of accounts, and handles all of the account procedures // load loading and deleting characters. If you are looking to change the char // creation menu, you will want char_gen.c, not account_handler.c // //***************************************************************************** #include "mud.h" #include "utils.h" #include "socket.h" #include "account.h" #include "character.h" #include "room.h" #include "inform.h" #include "save.h" #include "char_gen.h" #include "hooks.h" // mccp support const unsigned char do_echo [] = { IAC, WONT, TELOPT_ECHO, '\0' }; const unsigned char dont_echo [] = { IAC, WILL, TELOPT_ECHO, '\0' }; // account input handlers void account_ask_name (SOCKET_DATA *sock, char *arg); void account_ask_password (SOCKET_DATA *sock, char *arg); void account_new_password (SOCKET_DATA *sock, char *arg); void account_verify_password(SOCKET_DATA *sock, char *arg); void account_handle_menu (SOCKET_DATA *sock, char *arg); void account_new_char (SOCKET_DATA *sock, char *arg); void account_change_password(SOCKET_DATA *sock, char *arg); // and prompts void account_menu (SOCKET_DATA *sock); // // the head honcho. This is the first handler that a socket enters once it has // connected to the game. You should use this function to get your login // procedure rolling. void handle_new_connections(SOCKET_DATA *sock, char *arg) { account_ask_name(sock, arg); } //***************************************************************************** // implementation of account input handlers //***************************************************************************** // // returns TRUE if the account has a valid name, and FALSE if it does not. // alphanumeric characters and digits are allows. Min length 4, max length 12. // first character must be a letter bool check_account_name(const char *name) { int i, len = strlen(name); if(len < 3 || len > 12) return FALSE; if(!isalpha(*name)) return FALSE; for(i = 1; name[i] != '\0'; i++) if(!isdigit(name[i]) && !isalpha(name[i])) return FALSE; return TRUE; } // // Asks for an account name. If this is a new account, then proceed to ask and // verify the password. Otherwise, ask for an account password and make sure it // matches the actual password. void account_ask_name(SOCKET_DATA *sock, char *arg) { // make sure we're not still resolving the DNS if (socketGetDNSLookupStatus(sock) != TSTATE_DONE) { text_to_buffer(sock, "Making a dns lookup, please have patience.\n\r" "What is your account name? "); return; } // check for a legal account name if (!check_account_name(arg)) { text_to_buffer(sock, "Sorry, that's not a legal account name. Your account name must only consist of\r\n" "characters and numbers, and it must be between 4 and 12 characters long. The\r\n" "first character MUST be a letter. Please pick another.\r\n" "What is your name? "); return; } // all is good. See if the account exists already else { ACCOUNT_DATA *acct = NULL; log_string("Account '%s' is trying to connect.", arg); // check for new account if ( (acct = get_account(arg)) == NULL) { // check for lockdown if(*mudsettingGetString("lockdown") && !is_keyword(mudsettingGetString("lockdown"), DFLT_USER_GROUP, FALSE)) { text_to_socket(sock, "Sorry, creating new accounts is not allowed at the moment.\r\n"); close_socket(sock, FALSE); return; } // make sure someone else is not creating an account with this name if(account_creating(arg)) { text_to_socket(sock, "Someone is already creating an account with " "that name.\r\nTry again: "); return; } // make a new account and give it a name acct = newAccount(); accountSetName(acct, arg); // prepare for next step text_to_buffer(sock, "Please enter a new password: "); socketReplaceInputHandler(sock, account_new_password, NULL); } // old account else { // prepare for next step text_to_buffer(sock, "What is your password? "); socketReplaceInputHandler(sock, account_ask_password, NULL); } // make sure the password entry is not displayed on screen text_to_buffer(sock, (char *)dont_echo); // attach the account to the socket socketSetAccount(sock, acct); } } // // Ask for a password, and make sure it matches the account's password. If it // does not, then dc the socket void account_ask_password (SOCKET_DATA *sock, char *arg) { // make sure we can start seeing what we type, again text_to_buffer(sock, (char *) do_echo); // Passwords match. Go to the account menu if (compares(crypt(arg, accountGetName(socketGetAccount(sock))), accountGetPassword(socketGetAccount(sock)))) { socketReplaceInputHandler(sock, account_handle_menu, account_menu); } else { text_to_socket(sock, "\007\007Bad password! Disconnecting.\r\n"); close_socket(sock, FALSE); } } // // Ask and verify a new password during account creation void account_new_password (SOCKET_DATA *sock, char *arg) { // make sure the password is an acceptable length if (strlen(arg) < 5 || strlen(arg) > 12) { text_to_buffer(sock, "\r\nBetween 5 and 12 chars please!\n\r" "Please enter a new password: "); return; } // encrypt and set the password accountSetPassword(socketGetAccount(sock), crypt(arg, accountGetName(socketGetAccount(sock)))); // move onto the next step text_to_buffer(sock, "\r\nPlease verify the password: "); socketReplaceInputHandler(sock, account_verify_password, NULL); } // // Verify the password during account creation. If there is a password mismatch, // then restart the password process. void account_verify_password(SOCKET_DATA *sock, char *arg) { if (compares(crypt(arg, accountGetName(socketGetAccount(sock))), accountGetPassword(socketGetAccount(sock)))) { text_to_buffer(sock, (char *)do_echo); // account created. Register it, and plop it into the account menu. If it // is already created, we're just editing the password. So save changes. if(!account_exists(accountGetName(socketGetAccount(sock)))) register_account(socketGetAccount(sock)); else save_account(socketGetAccount(sock)); socketReplaceInputHandler(sock, account_handle_menu, account_menu); } else { accountSetPassword(socketGetAccount(sock), NULL); text_to_buffer(sock, "\r\nPassword mismatch!\n\r" "Please enter a new password: "); socketReplaceInputHandler(sock, account_new_password, NULL); } } // // Load up an existing character attached to the account. Enter the game void account_load_char(SOCKET_DATA *sock, int ch_num) { ACCOUNT_DATA *acct = socketGetAccount(sock); char *ch_name = NULL; if((ch_name = listGet(accountGetChars(acct), ch_num)) == NULL) text_to_buffer(sock, "Invalid choice!\r\n"); else { CHAR_DATA *ch = check_reconnect(ch_name); // this character is already in-game. Disconnect them if(ch != NULL) { // attach the character socketSetChar(sock, ch); charSetSocket(ch, sock); log_string("%s has reconnected.", charGetName(ch)); // and let him enter the game. Replace the load character input handler // with the one for playing the game. socketPushInputHandler(sock, handle_cmd_input, show_prompt); text_to_buffer(sock, "You take over a body already in use.\n\r"); } // hmmm... our pfile is missing!! else if ((ch = get_player(ch_name)) == NULL) text_to_socket(sock, "ERROR: Your pfile is missing!\n\r"); // everything is OK else { // attach the new player socketSetChar(sock, ch); charSetSocket(ch, sock); // make sure the mud isn't locked to this person if(*mudsettingGetString("lockdown") && !bitIsSet(charGetUserGroups(ch), mudsettingGetString("lockdown"))) { send_to_char(ch, "You are currently locked out of the mud.\r\n"); unreference_player(ch); socketSetChar(sock, NULL); return; } // try putting the character into the game. Pop the input handler // if we cannot if(try_enter_game(ch)) { log_string("%s has entered the game.", charGetName(ch)); // we're no longer in the creation process... attach the game // input handler socketPushInputHandler(sock, handle_cmd_input, show_prompt); text_to_buffer(sock, bufferString(motd)); look_at_room(ch, charGetRoom(ch)); // run entrance hooks hookRun("enter", ch, charGetRoom(ch), NULL); } else { text_to_buffer(sock, "There was a problem entering the game. Try again later!\r\n"); // do not extract, just delete. We failed to enter // the game, so there is no need to extract from the game. unreference_player(socketGetChar(sock)); socketSetChar(sock, NULL); } } } } // // Change the password on an already-existant account void account_change_password(SOCKET_DATA *sock, char *arg) { if (compares(crypt(arg, accountGetName(socketGetAccount(sock))), accountGetPassword(socketGetAccount(sock)))) { text_to_buffer(sock, "\r\nPlease enter a new password: "); socketReplaceInputHandler(sock, account_new_password, NULL); } else { text_to_buffer(sock, "\r\nIncorrect password!\r\n"); socketReplaceInputHandler(sock, account_handle_menu, account_menu); } } // // handle all of the options in the account main menu void account_handle_menu(SOCKET_DATA *sock, char *arg) { // are we trying to load up a character? if(isdigit(*arg)) account_load_char(sock, atoi(arg)); // we're doing a menu choice else switch(toupper(*arg)) { case 'Q': // quit the menu text_to_buffer(sock, "Come back soon!\r\n"); save_account(socketGetAccount(sock)); socketPopInputHandler(sock); break; case 'P': // change password text_to_buffer(sock, (char *)dont_echo); text_to_buffer(sock, "Enter old password: "); socketReplaceInputHandler(sock, account_change_password, NULL); break; case 'N': // make a new character if(*mudsettingGetString("lockdown") && !is_keyword(mudsettingGetString("lockdown"), DFLT_USER_GROUP, FALSE)) { text_to_buffer(sock, "The MUD is currently locked to new players.\r\n"); return; } start_char_gen(sock); break; default: text_to_buffer(sock, "Invalid choice!\r\n"); break; } } // // Display a list of all the account's characters to the account. void display_account_chars(SOCKET_DATA *sock) { static char fmt[100]; int print_room, num_cols = 3, i = 0; char *str = NULL; LIST_ITERATOR *ch_i =newListIterator(accountGetChars(socketGetAccount(sock))); print_room = (80 - 10*num_cols)/num_cols; sprintf(fmt, " {c%%2d{g) %%-%ds%%s", print_room); text_to_buffer(sock, "\r\n{wPlay a Character:\r\n"); ITERATE_LIST(str, ch_i) { send_to_socket(sock, fmt, i, str, (i % num_cols == (num_cols - 1) ? "\r\n" : " ")); i++; } deleteListIterator(ch_i); if(i % num_cols != 0) send_to_socket(sock, "\r\n"); } void account_menu(SOCKET_DATA *sock) { // if we have some characters created, display them if(listSize(accountGetChars(socketGetAccount(sock))) > 0) display_account_chars(sock); // additional options? delete? who's online? text_to_buffer(sock, "\r\n{wAdditional Options:\r\n" " {g[{cP{g]assword change\r\n" " {g[{cN{g]ew character\r\n" "\r\n" "Enter choice, or Q to quit:{n "); }