/* $Header: /cvsroot/fbmuck/fbmuck/src/player.c,v 1.11 2004/05/30 22:49:39 winged Exp $ */
#include "copyright.h"
#include "config.h"
#include "db.h"
#include "params.h"
#include "tune.h"
#include "interface.h"
#include "externs.h"
static hash_tab player_list[PLAYER_HASH_SIZE];
dbref
lookup_player(const char *name)
{
	hash_data *hd;
	if ((hd = find_hash(name, player_list, PLAYER_HASH_SIZE)) == NULL) {
		return NOTHING;
	} else {
		return (hd->dbval);
	}
}
int
check_password(dbref player, const char* password)
{
	char md5buf[64];
	const char *processed = password;
	const char *pword = PLAYER_PASSWORD(player);
	if (password == NULL) {
		MD5base64(md5buf, "", 0);
		processed = md5buf;
	} else {
		if (*password) {
			MD5base64(md5buf, password, strlen(password));
			processed = md5buf;
		}
	}
	if (!pword || !*pword)
		return 1;
	if (!strcmp(pword, processed))
		return 1;
	return 0;
}
void
set_password_raw(dbref player, const char* password)
{
	PLAYER_SET_PASSWORD(player, password);
	DBDIRTY(player);
}
void
set_password(dbref player, const char* password)
{
	char md5buf[64];
	const char *processed = password;
	if (*password) {
		MD5base64(md5buf, password, strlen(password));
		processed = md5buf;
	}
	
	if (PLAYER_PASSWORD(player))
		free((void *) PLAYER_PASSWORD(player));
	set_password_raw(player, alloc_string(processed));
}
dbref
connect_player(const char *name, const char *password)
{
	dbref player;
	if (*name == NUMBER_TOKEN && number(name + 1) && atoi(name + 1)) {
		player = (dbref) atoi(name + 1);
		if ((player < 0) || (player >= db_top) || (Typeof(player) != TYPE_PLAYER))
			player = NOTHING;
	} else {
		player = lookup_player(name);
	}
	if (player == NOTHING)
		return NOTHING;
	if (!check_password(player, password))
		return NOTHING;
	return player;
}
dbref
create_player(const char *name, const char *password)
{
	dbref player;
	if (!ok_player_name(name) || !ok_password(password))
		return NOTHING;
	/* else he doesn't already exist, create him */
	player = new_object();
	/* initialize everything */
	NAME(player) = alloc_string(name);
	DBFETCH(player)->location = tp_player_start;	/* home */
	FLAGS(player) = TYPE_PLAYER;
	OWNER(player) = player;
	ALLOC_PLAYER_SP(player);
	PLAYER_SET_HOME(player, tp_player_start);
	DBFETCH(player)->exits = NOTHING;
	SETVALUE(player, tp_start_pennies);
	set_password_raw(player, NULL);
	set_password(player, password);
	PLAYER_SET_CURR_PROG(player, NOTHING);
	PLAYER_SET_INSERT_MODE(player, 0);
	PLAYER_SET_IGNORE_CACHE(player, NULL);
	PLAYER_SET_IGNORE_COUNT(player, 0);
	PLAYER_SET_IGNORE_LAST(player, NOTHING);
	/* link him to tp_player_start */
	PUSH(player, DBFETCH(tp_player_start)->contents);
	add_player(player);
	DBDIRTY(player);
	DBDIRTY(tp_player_start);
	set_flags_from_tunestr(player, tp_pcreate_flags);
	return player;
}
void
do_password(dbref player, const char *old, const char *newobj)
{
	NOGUEST("@password",player);
	if (!PLAYER_PASSWORD(player) || !check_password(player, old)) {
		notify(player, "Sorry, old password did not match current password.");
	} else if (!ok_password(newobj)) {
		notify(player, "Bad new password (no spaces allowed).");
	} else {
		set_password(player, newobj);
		DBDIRTY(player);
		notify(player, "Password changed.");
	}
}
void
clear_players(void)
{
	kill_hash(player_list, PLAYER_HASH_SIZE, 0);
	return;
}
void
add_player(dbref who)
{
	hash_data hd;
	hd.dbval = who;
	if (add_hash(NAME(who), hd, player_list, PLAYER_HASH_SIZE) == NULL) {
		panic("Out of memory");
	} else {
		return;
	}
}
void
delete_player(dbref who)
{
	int result;
	char buf[BUFFER_LEN];
	char namebuf[BUFFER_LEN];
	int i, j;
	dbref found, ren;
	result = free_hash(NAME(who), player_list, PLAYER_HASH_SIZE);
	if (result) {
		wall_wizards
				("## WARNING: Playername hashtable is inconsistent.  Rebuilding it.  Don't panic.");
		clear_players();
		for (i = 0; i < db_top; i++) {
			if (Typeof(i) == TYPE_PLAYER) {
				found = lookup_player(NAME(i));
				if (found != NOTHING) {
					ren = (i == who) ? found : i;
					j = 0;
					do {
						snprintf(namebuf, sizeof(namebuf), "%s%d", NAME(ren), ++j);
					} while (lookup_player(namebuf) != NOTHING);
					snprintf(buf, sizeof(buf),
							"## Renaming %s(#%d) to %s to prevent name collision.",
							NAME(ren), ren, namebuf);
					wall_wizards(buf);
					log_status("SANITY NAME CHANGE: %s(#%d) to %s\n", NAME(ren), ren, namebuf);
					if (ren == found) {
						free_hash(NAME(ren), player_list, PLAYER_HASH_SIZE);
					}
					if (NAME(ren)) {
						free((void *) NAME(ren));
					}
					ts_modifyobject(ren);
					NAME(ren) = alloc_string(namebuf);
					add_player(ren);
				} else {
					add_player(i);
				}
			}
		}
		result = free_hash(NAME(who), player_list, PLAYER_HASH_SIZE);
		if (result) {
			wall_wizards
					("## WARNING: Playername hashtable still inconsistent.  Now you can panic.");
		}
	}
	return;
}