/* player.c */ #include "copyright.h" #ifdef WANT_ANSI #ifdef __STDC__ #include <stddef.h> #include <stdlib.h> #endif /* __STDC__ */ #endif /* WANT_ANSI */ #include "mudconf.h" #include "config.h" #include "db.h" #include "interface.h" #include "externs.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 *crypt(); #else char *crypt(char *inptr, char *inkey) { return inptr; } #endif VMS /* --------------------------------------------------------------------------- * 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 = 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 = atol(grabto(&atrbuf, ';')); info->tot_bad = 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); 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 (dbref player, int isgood, char *ldate, char *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++; } 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); atr_add_raw(player, A_LASTSITE, lhost); 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) { 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) { allowance = atr_pget(player, A_ALLOWANCE, &aowner, &aflags); if (*allowance == '\0') giveto(player, mudconf.paycheck); else giveto(player, atol(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(char *name, char *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(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)) { 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 (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'; 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 */ stat = hashrepl(temp, (int *)player, &mudstate.player_htab); free_lbuf(temp); return stat; } else { stat = hashadd(temp, (int *)player, &mudstate.player_htab); free_lbuf(temp); return (stat < 0) ? 0 : 1; } return 0; } 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'; 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 (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 (!((Typeof(p) == TYPE_PLAYER) || God(doer))) p = NOTHING; return p; } 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) p = find_connected_name(doer, name); else p = NOTHING; } else if (!Good_obj(p)) { p = NOTHING; } return p; } void load_player_names (void) { dbref i, aowner; int aflags; char *alias; DO_WHOLE_DB(i) { if (Typeof(i) == TYPE_PLAYER) add_player_name (i, Name(i)); } DO_WHOLE_DB(i) { if (Typeof(i) == TYPE_PLAYER) { alias = atr_pget(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) { 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; strcpy(bp->name, bad_name); } 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); }