btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * player.c 
 */

/*
 * $Id: player.c,v 1.7 2005/08/08 09:43:07 murrayma Exp $ 
 */

#include "copyright.h"
#include "config.h"

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "alloc.h"
#include "attrs.h"
#include "powers.h"
#include "command.h"

#define	NUM_GOOD	4	/*
				 * # of successful logins to save data for 
				 */
#define NUM_BAD		3	/*
				 * # of failed logins to save data for 
				 */

typedef struct hostdtm HOSTDTM;
struct hostdtm {
    char *host;
    char *dtm;
};

typedef struct logindata LDATA;
struct logindata {
    HOSTDTM good[NUM_GOOD];
    HOSTDTM bad[NUM_BAD];
    int tot_good;
    int tot_bad;
    int new_bad;
};

extern char *crypt(const char *, const char *);
extern time_t time(time_t *);

/*
 * ---------------------------------------------------------------------------
 * * decrypt_logindata, encrypt_logindata: Decode and encode login info.
 */

static void decrypt_logindata(atrbuf, info)
char *atrbuf;
LDATA *info;
{
    int i;
    char *tmpc;

    info->tot_good = 0;
    info->tot_bad = 0;
    info->new_bad = 0;
    for (i = 0; i < NUM_GOOD; i++) {
	info->good[i].host = NULL;
	info->good[i].dtm = NULL;
    }
    for (i = 0; i < NUM_BAD; i++) {
	info->bad[i].host = NULL;
	info->bad[i].dtm = NULL;
    }

    if (*atrbuf == '#') {
	atrbuf++;
	if (!(tmpc = grabto(&atrbuf, ';')))
	    return;
	info->tot_good = atoi(tmpc);
	for (i = 0; i < NUM_GOOD; i++) {
	    if (!(tmpc = grabto(&atrbuf, ';')))
		return;
	    info->good[i].host = tmpc;
	    if (!(tmpc = grabto(&atrbuf, ';')))
		return;
	    info->good[i].dtm = tmpc;
	}
	if (!(tmpc = grabto(&atrbuf, ';')))
	    return;
	info->new_bad = atoi(tmpc);
	if (!(tmpc = grabto(&atrbuf, ';')))
	    return;
	info->tot_bad = atoi(tmpc);
	for (i = 0; i < NUM_BAD; i++) {
	    if (!(tmpc = grabto(&atrbuf, ';')))
		return;
	    info->bad[i].host = tmpc;
	    if (!(tmpc = grabto(&atrbuf, ';')))
		return;
	    info->bad[i].dtm = tmpc;
	}
    }
}

static void encrypt_logindata(atrbuf, info)
char *atrbuf;
LDATA *info;
{
    char *bp, nullc;
    int i;

    /*
     * Make sure the SPRINTF call tracks NUM_GOOD and NUM_BAD for the * * 
     * 
     * *  * * number of host/dtm pairs of each type. 
     */

    nullc = '\0';
    for (i = 0; i < NUM_GOOD; i++) {
	if (!info->good[i].host)
	    info->good[i].host = &nullc;
	if (!info->good[i].dtm)
	    info->good[i].dtm = &nullc;
    }
    for (i = 0; i < NUM_BAD; i++) {
	if (!info->bad[i].host)
	    info->bad[i].host = &nullc;
	if (!info->bad[i].dtm)
	    info->bad[i].dtm = &nullc;
    }
    bp = alloc_lbuf("encrypt_logindata");
    sprintf(bp, "#%d;%s;%s;%s;%s;%s;%s;%s;%s;%d;%d;%s;%s;%s;%s;%s;%s;",
	info->tot_good, info->good[0].host, info->good[0].dtm,
	info->good[1].host, info->good[1].dtm, info->good[2].host,
	info->good[2].dtm, info->good[3].host, info->good[3].dtm,
	info->new_bad, info->tot_bad, info->bad[0].host, info->bad[0].dtm,
	info->bad[1].host, info->bad[1].dtm, info->bad[2].host,
	info->bad[2].dtm);
    StringCopy(atrbuf, bp);
    free_lbuf(bp);
}

/*
 * ---------------------------------------------------------------------------
 * * record_login: Record successful or failed login attempt.
 * * If successful, report last successful login and number of failures since
 * * last successful login.
 */

void record_login(player, isgood, ldate, lhost, lusername)
dbref player;
int isgood;
char *ldate, *lhost, *lusername;
{
    LDATA login_info;
    char *atrbuf;
    dbref aowner;
    int aflags, i;

    atrbuf = atr_get(player, A_LOGINDATA, &aowner, &aflags);
    decrypt_logindata(atrbuf, &login_info);
    if (isgood) {
	if (login_info.new_bad > 0) {
	    notify(player, "");
	    notify(player,
		tprintf
		("**** %d failed connect%s since your last successful connect. ****",
		    login_info.new_bad,
		    (login_info.new_bad == 1 ? "" : "s")));
	    notify(player,
		tprintf("Most recent attempt was from %s on %s.",
		    login_info.bad[0].host, login_info.bad[0].dtm));
	    notify(player, "");
	    login_info.new_bad = 0;
	}
	if (login_info.good[0].host && *login_info.good[0].host &&
	    login_info.good[0].dtm && *login_info.good[0].dtm) {
	    notify(player, tprintf("Last connect was from %s on %s.",
		    login_info.good[0].host, login_info.good[0].dtm));
	}
	if (mudconf.have_mailer)
	    check_mail(player, 0, 0);
	for (i = NUM_GOOD - 1; i > 0; i--) {
	    login_info.good[i].dtm = login_info.good[i - 1].dtm;
	    login_info.good[i].host = login_info.good[i - 1].host;
	}
	login_info.good[0].dtm = ldate;
	login_info.good[0].host = lhost;
	login_info.tot_good++;
	if (*lusername)
	    atr_add_raw(player, A_LASTSITE, tprintf("%s@%s", lusername,
		    lhost));
	else
	    atr_add_raw(player, A_LASTSITE, lhost);
    } else {
	for (i = NUM_BAD - 1; i > 0; i--) {
	    login_info.bad[i].dtm = login_info.bad[i - 1].dtm;
	    login_info.bad[i].host = login_info.bad[i - 1].host;
	}
	login_info.bad[0].dtm = ldate;
	login_info.bad[0].host = lhost;
	login_info.tot_bad++;
	login_info.new_bad++;
    }
    encrypt_logindata(atrbuf, &login_info);
    atr_add_raw(player, A_LOGINDATA, atrbuf);
    free_lbuf(atrbuf);
}

/*
 * ---------------------------------------------------------------------------
 * * check_pass: Test a password to see if it is correct.
 */

int check_pass(dbref player, const char *password) {
    dbref aowner;
    int aflags;
    char *target;
    char *hashed;

    target = atr_get(player, A_PASS, &aowner, &aflags);
    hashed = crypt(password, "XX");
    if (*target && strcmp(target, password) && strcmp(hashed, target)) {
        free_lbuf(target);
        return 0;
    }
    free_lbuf(target);

    /*
     * This is needed to prevent entering the raw encrypted password from
     * * * working.  Do it better if you like, but it's needed. 
     *
     * Not really, you should just not really allow unencrypted passwords.
     * -Hag
     */

    if ((strlen(password) == 13) && (password[0] == 'X') &&
            (password[1] == 'X'))
        return 0;

    return 1;
}

/*
 * ---------------------------------------------------------------------------
 * * connect_player: Try to connect to an existing player.
 */

dbref connect_player(char *name, char *password, char *host, char *username) {
    dbref player, aowner;
    int aflags;
    time_t tt;
    char *time_str, *player_last, *allowance;

    time(&tt);
    time_str = ctime(&tt);
    time_str[strlen(time_str) - 1] = '\0';

    if ((player = lookup_player(NOTHING, name, 0)) == NOTHING)
        return NOTHING;
    if (!check_pass(player, password)) {
        record_login(player, 0, time_str, host, username);
        return NOTHING;
    }
    time(&tt);
    time_str = ctime(&tt);
    time_str[strlen(time_str) - 1] = '\0';

    /*
     * compare to last connect see if player gets salary 
     */
    player_last = atr_get(player, A_LAST, &aowner, &aflags);
    if (strncmp(player_last, time_str, 10) != 0) {
        allowance = atr_pget(player, A_ALLOWANCE, &aowner, &aflags);
        if (*allowance == '\0')
            giveto(player, mudconf.paycheck);
        else
            giveto(player, atoi(allowance));
        free_lbuf(allowance);
    }
    atr_add_raw(player, A_LAST, time_str);
    free_lbuf(player_last);
    return player;
}

/*
 * ---------------------------------------------------------------------------
 * * create_player: Create a new player.
 */

dbref create_player(name, password, creator, isrobot, isguest)
char *name, *password;
dbref creator;
int isrobot, isguest;
{
    dbref player;
    char *pbuf;

    /*
     * Make sure the password is OK.  Name is checked in create_obj 
     */

    pbuf = trim_spaces(password);
    if (!ok_password(pbuf)) {
	free_lbuf(pbuf);
	return NOTHING;
    }
    /*
     * If so, go create him 
     */

    player = create_obj(creator, TYPE_PLAYER, name, isrobot);
    if (player == NOTHING) {
	free_lbuf(pbuf);
	return NOTHING;
    }
    /*
     * initialize everything 
     */
    if (isguest) {
	if (*mudconf.guests_channel)
	    do_addcom(player, player, 0, "g", mudconf.guests_channel);
    } else {
	if (*mudconf.public_channel)
	    do_addcom(player, player, 0, "pub", mudconf.public_channel);
    }

    s_Pass(player, crypt(pbuf, "XX"));
    s_Home(player, start_home());
    s_Fixed(player);
    free_lbuf(pbuf);
    return player;
}

/*
 * ---------------------------------------------------------------------------
 * * do_password: Change the password for a player
 */

void do_password(player, cause, key, oldpass, newpass)
dbref player, cause;
int key;
char *oldpass, *newpass;
{
    dbref aowner;
    int aflags;
    char *target;

    target = atr_get(player, A_PASS, &aowner, &aflags);
    if (!*target || !check_pass(player, oldpass)) {
	notify(player, "Sorry.");
    } else if (!ok_password(newpass)) {
	notify(player, "Bad new password.");
    } else {
	atr_add_raw(player, A_PASS, crypt(newpass, "XX"));
	notify(player, "Password changed.");
    }
    free_lbuf(target);
}

/*
 * ---------------------------------------------------------------------------
 * * do_last Display login history data.
 */

static void disp_from_on(player, dtm_str, host_str)
dbref player;
char *dtm_str, *host_str;
{
    if (dtm_str && *dtm_str && host_str && *host_str) {
	notify(player, tprintf("     From: %s   On: %s", dtm_str,
		host_str));
    }
}

void do_last(player, cause, key, who)
dbref player, cause;
int key;
char *who;
{
    dbref target, aowner;
    LDATA login_info;
    char *atrbuf;
    int i, aflags;

    if (!who || !*who) {
	target = Owner(player);
    } else if (!(string_compare(who, "me"))) {
	target = Owner(player);
    } else {
	target = lookup_player(player, who, 1);
    }

    if (target == NOTHING) {
	notify(player, "I couldn't find that player.");
    } else if (!Controls(player, target)) {
	notify(player, "Permission denied.");
    } else {
	atrbuf = atr_get(target, A_LOGINDATA, &aowner, &aflags);
	decrypt_logindata(atrbuf, &login_info);

	notify(player, tprintf("Total successful connects: %d",
		login_info.tot_good));
	for (i = 0; i < NUM_GOOD; i++) {
	    disp_from_on(player, login_info.good[i].host,
		login_info.good[i].dtm);
	}
	notify(player, tprintf("Total failed connects: %d",
		login_info.tot_bad));
	for (i = 0; i < NUM_BAD; i++) {
	    disp_from_on(player, login_info.bad[i].host,
		login_info.bad[i].dtm);
	}
	free_lbuf(atrbuf);
    }
}

/*
 * ---------------------------------------------------------------------------
 * * add_player_name, delete_player_name, lookup_player:
 * * Manage playername->dbref mapping
 */

int add_player_name(player, name)
dbref player;
char *name;
{
    int stat;
    dbref *p;
    char *temp, *tp;

    /*
     * Convert to all lowercase 
     */

    tp = temp = alloc_lbuf("add_player_name");
    safe_str(name, temp, &tp);
    *tp = '\0';
    for (tp = temp; *tp; tp++)
	*tp = ToLower(*tp);

    p = (int *) hashfind(temp, &mudstate.player_htab);
    if (p) {

	/*
	 * Entry found in the hashtable.  If a player, succeed if the
	 * * * numbers match (already correctly in the hash table),
	 * fail * * if they don't.  Fail if the name is a disallowed
	 * name * * (value AMBIGUOUS). 
	 */

	if (*p == AMBIGUOUS) {
	    free_lbuf(temp);
	    return 0;
	}
	if (Good_obj(*p) && (Typeof(*p) == TYPE_PLAYER)) {
	    free_lbuf(temp);
	    if (*p == player) {
		return 1;
	    } else {
		return 0;
	    }
	}
	/*
	 * It's an alias (or an incorrect entry).  Clobber it 
	 */
	free(p);
	p = (dbref *) malloc(sizeof(int));

	*p = player;
	stat = hashrepl(temp, p, &mudstate.player_htab);
	free_lbuf(temp);
    } else {
	p = (dbref *) malloc(sizeof(int));

	*p = player;
	stat = hashadd(temp, p, &mudstate.player_htab);
	free_lbuf(temp);
	stat = (stat < 0) ? 0 : 1;
    }
    return stat;
}

int delete_player_name(player, name)
dbref player;
char *name;
{
    dbref *p;
    char *temp, *tp;

    tp = temp = alloc_lbuf("delete_player_name");
    safe_str(name, temp, &tp);
    *tp = '\0';
    for (tp = temp; *tp; tp++)
	*tp = ToLower(*tp);

    p = (int *) hashfind(temp, &mudstate.player_htab);
    if (!p || (*p == NOTHING) || ((player != NOTHING) && (*p != player))) {
	free_lbuf(temp);
	return 0;
    }
    free(p);
    hashdelete(temp, &mudstate.player_htab);
    free_lbuf(temp);
    return 1;
}

dbref lookup_player(dbref doer, char *name, int check_who) {
    dbref *p, thing;
    char *temp, *tp;

    if (!string_compare(name, "me"))
        return doer;

    if (*name == NUMBER_TOKEN) {
        name++;
        if (!is_number(name))
            return NOTHING;
        thing = atoi(name);
        if (!Good_obj(thing))
            return NOTHING;
        if (!((Typeof(thing) == TYPE_PLAYER) || God(doer)))
            thing = NOTHING;
        return thing;
    }
    tp = temp = alloc_lbuf("lookup_player");
    safe_str(name, temp, &tp);
    *tp = '\0';
    for (tp = temp; *tp; tp++)
        *tp = ToLower(*tp);
    p = (int *) hashfind(temp, &mudstate.player_htab);
    free_lbuf(temp);
    if (!p) {
        if (check_who) {
            thing = find_connected_name(doer, name);
            if (Dark(thing))
                thing = NOTHING;
        } else
            thing = NOTHING;
    } else if (!Good_obj(*p)) {
        thing = NOTHING;
    } else
        thing = *p;

    return thing;
}

void load_player_names(void)
{
    dbref i, j, aowner;
    int aflags;
    char *alias;

    j = 0;
    DO_WHOLE_DB(i) {
	if (Typeof(i) == TYPE_PLAYER) {
	    add_player_name(i, Name(i));
#ifndef MEMORY_BASED
	    if (++j > 20) {
		cache_reset(0);
		j = 0;
	    }
#endif				/*
				 * MEMORY_BASED 
				 */
	}
    }
    alias = alloc_lbuf("load_player_names");
    j = 0;
    DO_WHOLE_DB(i) {
	if (Typeof(i) == TYPE_PLAYER) {
	    alias = atr_pget_str(alias, i, A_ALIAS, &aowner, &aflags);
	    if (*alias)
		add_player_name(i, alias);
#ifndef MEMORY_BASED
	    if (++j > 20) {
		cache_reset(0);
		j = 0;
	    }
#endif				/*
				 * MEMORY_BASED 
				 */
	}
    }
    free_lbuf(alias);
}

/*
 * ---------------------------------------------------------------------------
 * * badname_add, badname_check, badname_list: Add/look for/display bad names.
 */

void badname_add(bad_name)
char *bad_name;
{
    BADNAME *bp;

    /*
     * Make a new node and link it in at the top 
     */

    bp = (BADNAME *) XMALLOC(sizeof(BADNAME), "badname.struc");
    bp->name = XMALLOC(strlen(bad_name) + 1, "badname.name");
    bp->next = mudstate.badname_head;
    mudstate.badname_head = bp;
    StringCopy(bp->name, bad_name);
}

void badname_remove(bad_name)
char *bad_name;
{
    BADNAME *bp, *backp;

    /*
     * Look for an exact match on the bad name and remove if found 
     */

    backp = NULL;
    for (bp = mudstate.badname_head; bp; backp = bp, bp = bp->next) {
	if (!string_compare(bad_name, bp->name)) {
	    if (backp)
		backp->next = bp->next;
	    else
		mudstate.badname_head = bp->next;
	    XFREE(bp->name, "badname.name");
	    XFREE(bp, "badname.struc");
	    return;
	}
    }
}

int badname_check(bad_name)
char *bad_name;
{
    BADNAME *bp;

    /*
     * Walk the badname list, doing wildcard matching.  If we get a hit * 
     * 
     * *  * *  * * then return false.  If no matches in the list, return
     * true.  
     */

    for (bp = mudstate.badname_head; bp; bp = bp->next) {
	if (quick_wild(bp->name, bad_name))
	    return 0;
    }
    return 1;
}

void badname_list(player, prefix)
dbref player;
const char *prefix;
{
    BADNAME *bp;
    char *buff, *bufp;

    /*
     * Construct an lbuf with all the names separated by spaces 
     */

    buff = bufp = alloc_lbuf("badname_list");
    safe_str((char *) prefix, buff, &bufp);
    for (bp = mudstate.badname_head; bp; bp = bp->next) {
	safe_chr(' ', buff, &bufp);
	safe_str(bp->name, buff, &bufp);
    }
    *bufp = '\0';

    /*
     * Now display it 
     */

    notify(player, buff);
    free_lbuf(buff);
}