mux2.0/game/
mux2.0/game/data/
mux2.0/src/tools/
/*
 * player.c 
 */
/*
 * $Id: player.cpp,v 1.6 2000/09/18 18:26:24 sdennis Exp $ 
 */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"

#include "mudconf.h"
#include "db.h"
#include "interface.h"
#include "alloc.h"
#include "attrs.h"
#include "powers.h"
#include "comsys.h"
#include "functions.h"
#include "svdreport.h"

// The following is sometime useful if you don't have access to a crypt
// library. It does however make your flatfiles password incompatible with
// Unix flatfiles, but there are ways of getting around that as well.
//
#if 0
char *crypt(const char *inptr, const char *inkey)
{
    return (char *)inptr;
}
#endif


#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;
};


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

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

    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++;
        info->tot_good = Tiny_atol(grabto(&atrbuf, ';'));
        for (i = 0; i < NUM_GOOD; i++) {
            info->good[i].host = grabto(&atrbuf, ';');
            info->good[i].dtm = grabto(&atrbuf, ';');
        }
        info->new_bad = Tiny_atol(grabto(&atrbuf, ';'));
        info->tot_bad = Tiny_atol(grabto(&atrbuf, ';'));
        for (i = 0; i < NUM_BAD; i++) {
            info->bad[i].host = grabto(&atrbuf, ';');
            info->bad[i].dtm = grabto(&atrbuf, ';');
        }
    }
}

static void encrypt_logindata(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(dbref player, int isgood, char *ldate, char *lhost, char *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;

    target = atr_get(player, A_PASS, &aowner, &aflags);
    if (*target && strcmp(target, password) && strcmp(crypt(password, "XX"), 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. 
     */

    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;

    CLinearTimeAbsolute ltaNow;
    ltaNow.GetLocal();
    char *time_str = ltaNow.ReturnDateString();

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

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

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

dbref create_player(char *name, char *password, dbref creator, int isrobot, int 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, creator))
    {
        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());
    free_lbuf(pbuf);
    return player;
}

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

void do_password(dbref player, dbref cause, int key, char *oldpass, char *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, player))
    {
        // Do nothing, notification is handled by ok_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(dbref player, char *dtm_str, char *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(dbref player, dbref 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(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';
    _strlwr(temp);

    p = (int *)hashfindLEN(temp, strlen(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.
        //
        MEMFREE(p);
        p = (dbref *)MEMALLOC(sizeof(int));
        ISOUTOFMEMORY(p);

        *p = player;
        stat = hashreplLEN(temp, strlen(temp), p, &mudstate.player_htab);
        free_lbuf(temp);
    }
    else
    {
        p = (dbref *)MEMALLOC(sizeof(int));
        ISOUTOFMEMORY(p);

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

int delete_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';
    _strlwr(temp);

    p = (int *)hashfindLEN(temp, strlen(temp), &mudstate.player_htab);
    if (!p || (*p == NOTHING) || ((player != NOTHING) && (*p != player))) {
        free_lbuf(temp);
        return 0;
    }
    MEMFREE(p);
    hashdeleteLEN(temp, strlen(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 = Tiny_atol(name);
        if (!Good_obj(thing))
        {
            return NOTHING;
        }
        if (!(isPlayer(thing) || God(doer)))
        {
            thing = NOTHING;
        }
        return thing;
    }
    tp = temp = alloc_lbuf("lookup_player");
    safe_str(name, temp, &tp);
    *tp = '\0';
    _strlwr(temp);
    p = (int *)hashfindLEN(temp, strlen(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 NDECL(load_player_names)
{
    dbref i;
    DO_WHOLE_DB(i)
    {
        if (Typeof(i) == TYPE_PLAYER)
        {
            add_player_name(i, Name(i));
        }
    }
    char *alias = alloc_lbuf("load_player_names");
    DO_WHOLE_DB(i)
    {
        if (Typeof(i) == TYPE_PLAYER)
        {
            dbref aowner;
            int aflags;
            alias = atr_pget_str(alias, i, A_ALIAS, &aowner, &aflags);
            if (*alias)
            {
                add_player_name(i, alias);
            }
        }
    }
    free_lbuf(alias);
}

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

void badname_add(char *bad_name)
{
    // Make a new node and link it in at the top.
    //
    BADNAME *bp = (BADNAME *)MEMALLOC(sizeof(BADNAME));
    ISOUTOFMEMORY(bp);
    bp->name = StringClone(bad_name);
    bp->next = mudstate.badname_head;
    mudstate.badname_head = bp;
}

void badname_remove(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;
            MEMFREE(bp->name);
            MEMFREE(bp);
            return;
        }
    }
}

int badname_check(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(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);
}