/* ************************************************************************
*  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;
	}
}