/* player.c */ #include "autoconf.h" #include "copyright.h" #ifndef lint static char *RCSid = "$Id: player.c,v 1.8 1995/03/21 00:01:00 ambar Exp $"; USE(RCSid); #endif #include "interface.h" #include "attrs.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; }; #ifndef VMS extern char *FDECL(crypt, (const char *, const char *)); #else char * crypt(const char *inptr, const char *inkey) { return (char *) inptr; } #endif /* VMS */ extern time_t FDECL(time, (time_t *)); /* --------------------------------------------------------------------------- * decrypt_logindata, encrypt_logindata: Decode and encode login info. */ static void decrypt_logindata(atrbuf, info) 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 = atoi(grabto(&atrbuf, ';')); for (i = 0; i < NUM_GOOD; i++) { info->good[i].host = grabto(&atrbuf, ';'); info->good[i].dtm = grabto(&atrbuf, ';'); } info->new_bad = atoi(grabto(&atrbuf, ';')); info->tot_bad = atoi(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(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); strcpy(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) dbref player; int isgood; char *ldate, *lhost; { 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)); } 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++; 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(player, password) 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(name, password, host) char *name, *password, *host; { 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); 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) { if (Pennies(player) < mudconf.paylimit) { /* Don't heap coins on players who already have lots of money. */ 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) char *name, *password; dbref creator; int isrobot; { 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 */ 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(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); 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) && (isPlayer(p))) { free_lbuf(temp); if (p == player) { return 1; } else { return 0; } } /* It's an alias (or an incorrect entry). Clobber it */ stat = hashrepl(temp, (int *) player, &mudstate.player_htab); } else { stat = hashadd(temp, (int *) player, &mudstate.player_htab); } free_lbuf(temp); return ((stat < 0) ? 0 : 1); } 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); 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; } hashdelete(temp, &mudstate.player_htab); free_lbuf(temp); return 1; } dbref lookup_player(doer, name, check_who) dbref doer; char *name; int check_who; { dbref p; char *temp, *tp; if (!string_compare(name, "me")) return doer; if (*name == NUMBER_TOKEN) { name++; if (!is_number(name)) return NOTHING; p = atoi(name); if (!Good_obj(p)) return NOTHING; if (!(isPlayer(p) || God(doer))) p = NOTHING; return p; } tp = temp = alloc_lbuf("lookup_player"); safe_str(name, temp, &tp); for (tp = temp; *tp; tp++) *tp = ToLower(*tp); p = (int) hashfind(temp, &mudstate.player_htab); free_lbuf(temp); if (!p) { if (check_who) p = find_connected_name(doer, name); else p = NOTHING; } else if (!Good_obj(p)) { p = NOTHING; } return p; } void NDECL(load_player_names) { dbref i, j = 0, aowner; int aflags; char *alias; DO_WHOLE_DB(i) { if (isPlayer(i)) add_player_name(i, Name(i)); if (i % 20 == 0) cache_reset(); } alias = alloc_lbuf("load_player_names"); DO_WHOLE_DB(i) { if (isPlayer(i)) { alias = atr_pget_str(alias, i, A_ALIAS, &aowner, &aflags); if (*alias) add_player_name(i, alias); if (++j > 20) { cache_reset(0); j = 0; } } } 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 = strsave(bad_name); bp->next = mudstate.badname_head; mudstate.badname_head = bp; } 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); } /* Now display it */ notify(player, buff); free_lbuf(buff); }