// /bin/adm/login.c -- Vincent's Hollow Project.
#define DEBUG

#include <version.h>
#include <files.h>
#include <daemons.h>
#include <login.h>

// Magic numbers/phrases.

#define MAX_NAME	 12	   
#define	ASK_NAME	"What is your name? "
#define	ASK_PASS	"What is your password, " + capitalize(name) + "? "
#define CRAP_NAME1 \
"\nSorry, but we can't accept your name in its current form.\n"+\
"Your name has to have at least 2 characters in it and no\n"+\
"more than " + MAX_NAME + " characters.\n\n"+\
"You may try again.\n\n"
#define CRAP_NAME2 \
"\nSorry, but we can't accept your name in its current form.\n"+\
"Your name can only have characters 'a' through 'z'.\n\n"+\
"You may try again.\n\n"

// Macros to make coding easier.

#define	REMOVE	signoff(); 
#define	REMOVE_NONEWS	call_out("signoff_cont",1); 
#define	NOWAIT	if(find_call_out("signoff")) remove_call_out("signoff")
#define	WAIT	call_out("signoff", 60)

// Prototypings.

static void   logon();
void		  get_name(string str);
void		  get_password(string pass);
int			  check_password(string pass);
void		  new_pass(string pass);
void		  new_pass2(string pass2);
void		  signoff();
void		  signoff_msg();

// Global var declarations.

private static string	name, temp, password;
private static object	player;

// Let the login start.

void
create()
{
#ifdef DEBUG
	write("login:  create\n");
#endif
	seteuid((string)master()->get_root_uid());
}

// Start of the login server

static void
logon()
{
#ifdef DEBUG
	write("login:  logon\n");
#endif
	if(!(KERBEROS_D->request_ticket()))
	{
		write("kerberos: ticket refused\n");
		REMOVE;
		return;
	}
	write(ASK_NAME);
	WAIT;
	input_to("get_name");
	return;
}

static void
get_name(string str) 
{
	int i, size;

	NOWAIT;
	if(!str || str == "")
	{
		REMOVE;
		return;
	}
	name = lower_case(str);

	/*
	 * Check for logging on from restricted sites.
	 */
	if (KERBEROS_D->check_restricted(query_ip_name()))
	{
		write("You are logging in from a restricted site.\n");
		if (!KERBEROS_D->check_restrict_ok(name))
		{
			cat(NEWS+"login.site_restrict");
			REMOVE;
			return;
		}
		write("You are o.k. to log in.\n");
	}

	/*
	 * Site is OK, next:
	 * Check name is acceptable.
	 */
	size = strlen(str);
	if(size > MAX_NAME || size < 2)
	{
		write(CRAP_NAME1);
		write(ASK_NAME);
		WAIT;
		input_to("get_name");
		return;
	}
	for(i = 0; i < size; i++)
	{
		if(name[i] < 'a' || name[i] > 'z')
		{
			write(CRAP_NAME2);
			write(ASK_NAME);
			WAIT;
			input_to("get_name");
			return;
		}
	}

	/*
	 * Site is allowed, name is acceptable:
	 * Check name is allowed to logon
	 */
	if(!KERBEROS_D->check_access(name))
	{
		write("\n");
		REMOVE;
		return;
	}

	/*
	 * Passed the access checks:
	 * Either get the password and check it,
	 * or get a new password.
	 */
	if(file_size(SAVE + name + ".o") < 0)
	{ 
		cat(NEWS + "login.newpass");
		write(ASK_PASS);
		WAIT;
		input_to("new_pass", 1);
		return;
	}
	write(ASK_PASS);
	WAIT;
	input_to("get_password", 1);
	return;
}

/*
 * Player already exists, get password
 */
static void
get_password(string pass) 
{
	object copy;

	NOWAIT;
	if(pass == "")
	{
		write("\n");
		REMOVE;
		return;
	}
	player=(object)KERBEROS_D->check_password_and_exec(name,pass);
	if(player)
	{
		if((string)player->query_real_name()!="")
		{
#ifdef DEBUG
			write(
				"exec to netdead >"+(string)player->query_real_name()+"<\n");
#endif
			master()->remove_netdead(name);
			player->restart_heart();
		}
		else
		{
#ifdef DEBUG
			write("execing to new player\n");
#endif
			master()->add_login_count();
			player->restore_player(name);
			player->setup_ob();
		}
		REMOVE_NONEWS;
		return;
	}
	write("\nkerberos error: failed login attempt\n");
	write("\n"+ASK_NAME);
	WAIT;
	input_to("get_name");
	return;
}


// Routines to get information from new players.
	
static void
new_pass(string pass) 
{
	NOWAIT;
	if(!pass || pass == "")
	{
		write("\n");
		REMOVE;
		return;
	}
	if(strlen(pass) < 6)
	{
		write("\n\nYour password must be at least 6 characters in length.\n");
		write("This is so that it is harder for others to guess it.\n");
		write("\nYou may try again.\n\n");
		write(ASK_PASS);
		WAIT;
		input_to("new_pass",1);
		return;
	}
	temp = pass;
	write("\n\nWe need to verify what you just typed in.  This is so that\n");
	write("if you made a mistake, you can correct it.\n");
	write("\nPlease re-enter the new password for " + capitalize(name) + ": ");
	WAIT;
	input_to("new_pass2", 1);
	return;
}

static void
new_pass2(string pass) 
{
	NOWAIT;
	write("\n");
	if(temp == pass)
	{
		pass = crypt(pass,0);
		player=(object)KERBEROS_D->new_pass_and_exec(name, pass);
		if(player)
			player->setup_ob();
		else
			write("kerberos: refused addition and exec\n");
		REMOVE_NONEWS;
		return;
	}
	else
	{
		write("\nVerification failed.  You better try to type in the " +
			"password\n");
		write("again.  We'll repeat this process one more time.\n\n");
		write(ASK_PASS);
		WAIT;
		input_to("new_pass", 1);
		return;
	}
}

// House cleaning functions.

void
signoff()
{
	cat(NEWS + "login.signoff");
	call_out("signoff_cont",1);
}

void
signoff_cont()
{
	destruct(this_object());
}

// This function, in theory, would never be used.  But, just in case.

void
clean_up()
{
	destruct(this_object());
}


// EOF