/* * Plists.c */ #include <stdlib.h> #include <ctype.h> #include <time.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <memory.h> #include <signal.h> #include <setjmp.h> #include "fix.h" #include "config.h" #include "player.h" #include "compaction.c" /* externs */ extern player *find_player_absolute_quiet(char *); extern char *gstring(player *); extern void do_inform(player *, char *); extern void do_prompt(player *, char *); extern void quit(player *, char *); extern void finish_edit(player *); extern int restore_player_title(player *, char *, char *); extern void decompress_list(saved_player *); #if !defined(linux) extern char *crypt(char *, char *); #endif /* LINUX */ extern void player_flags(player *), player_flags_verbose(player *, char *); extern file load_file(); extern char *end_string(), *convert_time(), *retrieve_room_data(), *retrieve_list_data(), *bit_string(), *retrieve_mail_data(); extern room *create_room(), *convert_room(); extern void password_mode_on(), password_mode_off(), trans_to(); extern void construct_room_save(), construct_list_save(), construct_mail_save(); extern void decompress_room(room *); extern void log(char *, char *); extern void handle_error(char *); extern void free_room_data(saved_player *); extern void tell_player(player *, char *); extern void tell_room(room *, char *); extern void swho(player *, char *); extern void pager(player *, char *, int); extern int possible_move(player *, room *, int); extern file full_msg; extern player *p_sess; /* interns */ void error_on_load(); int bad_player_load = 0; char player_loading[MAX_NAME + 2]; jmp_buf jmp_env; file motd_msg, connect_msg, newban_msg, banned_msg, nonewbies_msg, newbie_msg, newpage1_msg, newpage2_msg, disclaimer_msg, splat_msg; saved_player **saved_hash[26]; int update[26]; void save_player(), newbie_check_name(), link_to_program(); int restore_player(); saved_player **birthday_list = 0; /* update functions .. a complete database traversal */ /* crypts the password */ char *update_password(char *oldpass) { char key[9]; strncpy(key, oldpass, 8); return crypt(key, "SP"); } void players_update_function(player * p) { char em[MAX_EMAIL]; if (p->email[0] == ' ') strcpy(em, " <VALIDATED AS SET>"); else strcpy(em, p->email); if (p->residency & ADMIN) printf("%-18s -- %-40s >(ADMIN)\n", p->name, em); else if (p->residency & SU) printf("%-18s -- %-40s >(SU)\n", p->name, em); else if (p->residency & PSU) printf("%-18s -- %-40s >(PSEUDO)\n", p->name, em); else printf("%-18s -- %s\n", p->name, em); } void flags_update_function(player * p) { printf("%-18s -- %-40s\n", p->name, bit_string(p->residency)); } void rooms_update_function(player * p) { room *r; saved_player *sp; sp = p->saved; r = sp->rooms; while (r) { if (r->flags & OPEN) { decompress_room(r); printf("-=> %s.%s (%s)\n", r->owner->lower_name, r->id, r->name); if (r->exits.where) printf(r->exits.where); } r = r->next; } } void do_update(int rooms) { player *p; saved_player *scan, **hash; int i, j, fd; fd = open("/dev/null", O_WRONLY); p = (player *) MALLOC(sizeof(player)); for (j = 0; j < 26; j++) { /* printf("Updating %c\n",j+'a'); */ hash = saved_hash[j]; for (i = 0; i < HASH_SIZE; i++, hash++) { for (scan = *hash; scan; scan = scan->next) { if (scan->residency != STANDARD_ROOMS && scan->residency != SYSTEM_ROOM &&(scan->residency != BANISHED) && (scan->residency != BANISHD)) { memset((char *) p, 0, sizeof(player)); p->fd = fd; p->script = 0; p->location = (room *) - 1; restore_player(p, scan->lower_name); if (rooms) rooms_update_function(p); else if (sys_flags & UPDATE) players_update_function(p); else flags_update_function(p); save_player(p); } } } } close(fd); } /* return the top player in a hash list */ saved_player *find_top_player(char c, int h) { if ((c < 0) || (c > 25)) return 0; if ((h < 0) || (h > HASH_SIZE)) return 0; return (*(saved_hash[c] + h)); } /* birthdays !!! */ void do_birthdays() { player *p; saved_player *scan, **hash, **list; int i, j, fd; time_t t; struct tm *date, *bday; char *oldstack; fd = open("/dev/null", O_WRONLY); oldstack = stack; align(stack); list = (saved_player **) stack; t = time(0); date = localtime(&t); p = (player *) MALLOC(sizeof(player)); for (j = 0; j < 26; j++) { hash = saved_hash[j]; for (i = 0; i < HASH_SIZE; i++, hash++) for (scan = *hash; scan; scan = scan->next) if (scan->residency != STANDARD_ROOMS && scan->residency != SYSTEM_ROOM && (scan->residency != BANISHED) && (!(scan->residency & BANISHD))) { memset((char *) p, 0, sizeof(player *)); restore_player(p, scan->lower_name); bday = localtime((time_t *)&(p->birthday)); if ((bday->tm_mon == date->tm_mon) && (bday->tm_mday == date->tm_mday)) { *((saved_player **) stack) = scan; stack += sizeof(saved_player *); p->age++; save_player(p); } } } *((saved_player **) stack) = 0; stack += sizeof(saved_player *); if (birthday_list) FREE(birthday_list); i = (int) stack - (int) list; if (i > 4) { birthday_list = (saved_player **) MALLOC(i); memcpy(birthday_list, list, i); } else birthday_list = 0; close(fd); } /* saved player stuff */ /* see if a saved player exists (given lower case name) */ saved_player *find_saved_player(char *name) { saved_player **hash, *list; int sum = 0,h; char *c; if (!isalpha(*name)) return 0; hash = saved_hash[((int) (tolower(*name)) - (int) 'a')]; for (c = name; *c; c++) { if (isalpha(*c)) sum += (int) (tolower(*c)) - 'a'; else return 0; } list = *(hash + (sum % HASH_SIZE)); for (; list; list = list->next) if (!strcmp(name, list->lower_name)) return list; return 0; } /* hard load and save stuff (ie to disk and back) */ /* extract one player */ void extract_player(char *where, int length) { int len, sum; char *oldstack, *c; saved_player *old, *sp, **hash; oldstack = stack; where = get_int(&len, where); where = get_string(oldstack, where); stack = end_string(oldstack); old = find_saved_player(oldstack); sp = old; if (!old) { sp = (saved_player *) MALLOC(sizeof(saved_player)); memset((char *) sp, 0, sizeof(saved_player)); strncpy(sp->lower_name, oldstack, MAX_NAME); strncpy(player_loading, sp->lower_name, MAX_NAME); sp->rooms = 0; sp->mail_sent = 0; sp->mail_received = 0; sp->list_top = 0; hash = saved_hash[((int) sp->lower_name[0] - (int) 'a')]; for (sum = 0, c = sp->lower_name; *c; c++) sum += (int) (*c) - 'a'; hash = (hash + (sum % HASH_SIZE)); sp->next = *hash; *hash = sp; } where = get_int(&sp->last_on, where); where = get_int(&sp->saved_flags, where); where = get_int(&sp->residency, where); if (sp->residency == BANISHED) sp->residency = BANISHD; if (sp->residency == BANISHD) { sp->last_host[0] = 0; sp->data.where = 0; sp->data.length = 0; stack = oldstack; return; } /* * This bit controls when idle players get wiped. After summer hols, this * bit MUST be uncommented */ if ( ((time(0)-(sp->last_on)) > PLAYER_TIMEOUT) && !(sp->residency&NO_TIMEOUT)) { log("timeouts", sp->lower_name); remove_player_file(sp->lower_name); stack=oldstack; return; } /* PUT ANYTHING TO CHANGE RESIDENCY OR OTHER FLAGS HERE */ where = get_string(sp->last_host, where); where = get_int(&sp->data.length, where); sp->data.where = (char *) MALLOC(sp->data.length); memcpy(sp->data.where, where, sp->data.length); where += sp->data.length; where = retrieve_room_data(sp, where); where = retrieve_list_data(sp, where); where = retrieve_mail_data(sp, where); stack = oldstack; } /* hard load in on player file */ void hard_load_one_file(char c) { char *oldstack, *where, *scan; int fd, length, len2, i, fromjmp; oldstack = stack; if (sys_flags & VERBOSE) { sprintf(oldstack, "Loading player file '%c'.", c); stack = end_string(oldstack); log("boot", oldstack); stack = oldstack; } #ifdef PC sprintf(oldstack, "files\\players\\%c", c); fd = open(oldstack, O_RDONLY | O_BINARY); #else sprintf(oldstack, "files/players/%c", c); fd = open(oldstack, O_RDONLY | O_NDELAY); #endif if (fd < 0) { sprintf(oldstack, "Failed to load player file '%c'", c); stack = end_string(oldstack); log("error", oldstack); } else { length = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); if (length) { where = (char *) MALLOC(length); if (read(fd, where, length) < 0) handle_error("Can't read player file."); for (i = 0, scan = where; i < length;) { get_int(&len2, scan); fromjmp = setjmp(jmp_env); if (!fromjmp && !bad_player_load) { extract_player(scan, len2); } else { sprintf(oldstack, "Bad Player \'%s\' deleted on load.", player_loading); stack = end_string(oldstack); log("boot", oldstack); stack = oldstack; remove_player_file(player_loading); bad_player_load = 0; } i += len2; scan += len2; } FREE(where); } close(fd); } stack = oldstack; } /* load in all the player files */ void hard_load_files() { char c; int i, hash_length; char *oldstack; #if defined(hpux) | defined(linux) struct sigaction sa; sa.sa_handler = error_on_load; sa.sa_mask = 0; sa.sa_flags = 0; sigaction(SIGSEGV, &sa, 0); sigaction(SIGSEGV, &sa, 0); #else /* hpux | linux */ signal(SIGSEGV, error_on_load); signal(SIGBUS, error_on_load); #endif /* hpux | linux */ oldstack = stack; hash_length = HASH_SIZE * sizeof(saved_player *); for (i = 0; i < 26; i++) { saved_hash[i] = (saved_player **) MALLOC(hash_length); memset((void *) saved_hash[i], 0, hash_length); } for (c = 'a'; c <= 'z'; c++) hard_load_one_file(c); } /* write one player file out */ void write_to_file(saved_player * sp) { char *oldstack; int length; oldstack = stack; if (sys_flags & VERBOSE && sys_flags & PANIC) { sprintf(oldstack, "Attempting to write player '%s'.", sp->lower_name); stack = end_string(oldstack); log("sync", oldstack); stack = oldstack; } stack += 4; stack = store_string(stack, sp->lower_name); stack = store_int(stack, sp->last_on); stack = store_int(stack, sp->saved_flags | COMPRESSED_LIST); stack = store_int(stack, sp->residency); if ((sp->residency != BANISHED) ) { stack = store_string(stack, sp->last_host); stack = store_int(stack, sp->data.length); memcpy(stack, sp->data.where, sp->data.length); stack += sp->data.length; construct_room_save(sp); construct_list_save(sp); construct_mail_save(sp); } length = (int) stack - (int) oldstack; (void) store_int(oldstack, length); } /* sync player files corresponding to one letter */ void sync_to_file(char c, int background) { saved_player *scan, **hash; char *oldstack; int fd, i, length; if (background && fork()) return; oldstack = stack; if (sys_flags & VERBOSE) { sprintf(oldstack, "Syncing File '%c'.", c); stack = end_string(oldstack); log("sync", oldstack); stack = oldstack; } hash = saved_hash[((int) c - (int) 'a')]; for (i = 0; i < HASH_SIZE; i++, hash++) for (scan = *hash; scan; scan = scan->next) if (scan->residency != STANDARD_ROOMS && scan->residency != SYSTEM_ROOM && (!(scan->residency & NO_SYNC) || scan->residency == BANISHED)) write_to_file(scan); length = (int) stack - (int) oldstack; /* test that you can write out a file ok */ strcpy(stack, "files/players/backup_write"); fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) handle_error("Primary open failed (player back)"); if (write(fd, oldstack, length) < 0) handle_error("Primary write failed " "(playerback)"); close(fd); #ifdef PC sprintf(stack, "files\\players\\%c", c); fd = open(stack, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY); #else sprintf(stack, "files/players/%c", c); fd = open(stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR); #endif if (fd < 0) handle_error("Failed to open player file."); if (write(fd, oldstack, length) < 0) handle_error("Failed to write player file."); close(fd); update[(int) c - (int) 'a'] = 0; stack = oldstack; if (background) exit(0); } /* sync everything to disk */ void sync_all() { char c, *oldstack; oldstack = stack; for (c = 'a'; c <= 'z'; c++) sync_to_file(c, 0); log("sync", "Full Sync Completed."); } /* fork and sync the playerfiles */ void fork_the_thing_and_sync_the_playerfiles(void) { int fl; char c, *oldstack; fl = fork(); if (fl == -1) { log("error", "Forked up!"); return; } if (fl > 0) return; sync_all(); exit(0); } /* flicks on the update flag for a particular player hash */ void set_update(char c) { update[(int) c - (int) 'a'] = 1; } /* removes an entry from the saved player lists */ int remove_player_file(char *name) { saved_player *previous = 0, **hash, *list; char *c; int sum = 0; if (!isalpha(*name)) { log("error", "Tried to remove non-player from save files."); return 0; } strcpy(stack, name); lower_case(stack); hash = saved_hash[((int) (*stack) - (int) 'a')]; for (c = stack; *c; c++) { if (isalpha(*c)) sum += (int) (*c) - 'a'; else { log("error", "Remove bad name from save files"); return 0; } } hash += (sum % HASH_SIZE); list = *hash; for (; list; previous = list, list = list->next) { if (!strcmp(stack, list->lower_name)) { if (previous) previous->next = list->next; else *hash = list->next; if (list->data.where) FREE(list->data.where); if (list->mail_received) FREE(list->mail_received); free_room_data(list); FREE((void *) list); set_update(*stack); return 1; } } return 0; } /* remove an entire hash of players */ void remove_entire_list(char c) { saved_player **hash, *sp, *next; int i; if (!isalpha(c)) return; hash = saved_hash[((int) (c) - (int) 'a')]; for (i = 0; i < HASH_SIZE; i++, hash++) { sp = *hash; while (sp) { next = sp->next; if (sp->data.where) FREE(sp->data.where); free_room_data(sp); FREE((void *) sp); sp = next; } *hash = 0; } set_update(c); } /* routines to save a player to the save files */ /* makes the save data onto the stack */ file construct_save_data(player * p) { file d; d.where = stack; stack = store_string(stack, p->name); stack = store_string(stack, p->prompt); stack = store_string(stack, p->converse_prompt); stack = store_string(stack, p->email); if (p->password[0] == -1) p->password[0] = 0; stack = store_string(stack, p->password); stack = store_string(stack, p->title); stack = store_string(stack, p->plan); stack = store_string(stack, p->description); stack = store_string(stack, p->enter_msg); stack = store_string(stack, p->pretitle); stack = store_string(stack, p->ignore_msg); stack = store_string(stack, p->room_connect); stack = store_int(stack, p->term_width); stack = store_int(stack, p->word_wrap); stack = store_int(stack, p->max_rooms); stack = store_int(stack, p->max_exits); stack = store_int(stack, p->max_autos); stack = store_int(stack, p->max_list); stack = store_int(stack, p->max_mail); stack = store_int(stack, p->gender); stack = store_int(stack, p->no_shout); stack = store_int(stack, p->total_login); stack = store_int(stack, p->term); stack = store_int(stack, p->birthday); stack = store_int(stack, p->age); /* Here goes with adding to the playerfiles */ stack = store_int(stack, p->jetlag); stack = store_int(stack, p->sneezed); d.length = (int) stack - (int) d.where; stack = d.where; return d; } /* the routine that sets everything up for the save */ void save_player(player * p) { saved_player *old, **hash, *sp; int sum; file data; char *c, *oldstack; int verb = 1; oldstack = stack; if (!(p->location) || !(p->name[0]) || p->residency == NON_RESIDENT) return; if (sys_flags & PANIC) { c = stack; sprintf(c, "Attempting to save player %s.", p->name); stack = end_string(c); log("boot", c); stack = c; } if (!(isalpha(p->lower_name[0]))) { log("error", "Tried to save non-player."); return; } if (p->residency & SYSTEM_ROOM) verb = 0; if (verb) { if (!(p->password[0] && p->password[0] != -1)) { tell_player(p, " Tried to save character but failed ...\n" " Your character will not save until you set a password.\n" " Simply type 'password' whilst in command mode to set one.\n"); p->residency |= NO_SYNC; tell_player(p, " NOT saved.\n"); stack = oldstack; return; } if (p->email[0] == 2) { tell_player(p, " Tried to save character but failed ...\n" " Your character will not save until you set an email address.\n" " To set this just type 'email <whatever>', where <whatever> is your\n" " email address.\n" " If you do not have an email, please speak to one of the superusers.\n"); p->residency |= NO_SYNC; tell_player(p, "NOT saved.\n"); stack = oldstack; return; } } p->residency &= ~NO_SYNC; p->saved_residency = p->residency; old = p->saved; sp = old; if (!old) { sp = (saved_player *) MALLOC(sizeof(saved_player)); memset((char *) sp, 0, sizeof(saved_player)); strncpy(sp->lower_name, p->lower_name, MAX_NAME); sp->rooms = 0; sp->mail_sent = 0; sp->mail_received = 0; sp->list_top = 0; hash = saved_hash[((int) p->lower_name[0] - (int) 'a')]; for (sum = 0, c = p->lower_name; *c; c++) { if (isalpha(*c)) sum += (int) (*c) - 'a'; else { tell_player(p, " Eeek, trying to save bad player name !!\n"); FREE(sp); return; } } hash = (hash + (sum % HASH_SIZE)); sp->next = *hash; *hash = sp; p->saved = sp; sp->saved_flags = p->saved_flags; create_room(p); } data = construct_save_data(p); if (!data.length) { log("error", "Bad construct save."); return; } if (old && sp->data.where) FREE((void *) sp->data.where); sp->data.where = (char *) MALLOC(data.length); sp->data.length = data.length; memcpy(sp->data.where, data.where, data.length); sp->residency = p->saved_residency; sp->saved_flags = p->saved_flags; strncpy(sp->last_host, p->inet_addr, MAX_INET_ADDR); set_update(*(sp->lower_name)); p->saved = sp; sp->last_on = time(0); if (verb) tell_player(p, " Character Saved ...\n"); } /* the routine that sets everything up for the save */ void create_banish_file(char *name) { saved_player **hash, *sp, *scan; int sum; char *c; sp = (saved_player *) MALLOC(sizeof(saved_player)); memset((char *) sp, 0, sizeof(saved_player)); strcpy(stack, name); lower_case(stack); strncpy(sp->lower_name, stack, MAX_NAME - 2); sp->rooms = 0; sp->mail_sent = 0; sp->mail_received = 0; hash = saved_hash[((int) name[0] - (int) 'a')]; for (sum = 0, c = name; *c; c++) if (isalpha(*c)) sum += (int) (*c) - 'a'; else { log("error", "Tried to banish bad player"); FREE(sp); return; } hash = (hash + (sum % HASH_SIZE)); scan = *hash; while (scan) { hash = &(scan->next); scan = scan->next; } *hash = sp; sp->residency = BANISHD; sp->saved_flags = 0; sp->last_host[0] = 0; sp->last_on = time(0); sp->next = 0; set_update(tolower(*(sp->lower_name))); } /* load from a saved player into a current player */ /* actually do load */ int load_player(player * p) { saved_player *sp; char *r; lower_case(p->lower_name); sp = find_saved_player(p->lower_name); p->saved = sp; if (!sp) return 0; p->residency = sp->residency; p->saved_residency = p->residency; p->saved_flags = sp->saved_flags; if (sp->residency == BANISHED || sp->residency == STANDARD_ROOMS || sp->residency == SYSTEM_ROOM || sp->residency == BANISHD) return 1; r = sp->data.where; r = get_string(p->name, r); r = get_string(p->prompt, r); r = get_string(p->converse_prompt, r); r = get_string(p->email, r); r = get_string(p->password, r); r = get_string(p->title, r); r = get_string(p->plan, r); r = get_string(p->description, r); r = get_string(p->enter_msg, r); r = get_string(p->pretitle, r); r = get_string(p->ignore_msg, r); r = get_string(p->room_connect, r); r = get_int(&p->term_width, r); r = get_int(&p->word_wrap, r); r = get_int(&p->max_rooms, r); r = get_int(&p->max_exits, r); r = get_int(&p->max_autos, r); r = get_int(&p->max_list, r); r = get_int(&p->max_mail, r); r = get_int(&p->gender, r); r = get_int(&p->no_shout, r); r = get_int(&p->total_login, r); r = get_int(&p->term, r); r = get_int(&p->birthday, r); r = get_int(&p->age, r); r = get_int(&p->jetlag, r); r = get_int(&p->sneezed, r); if (((p->term_width) >> 1) <= (p->word_wrap)) p->word_wrap = (p->term_width) >> 1; decompress_list(sp); p->saved_flags = sp->saved_flags; return 1; } /* load and do linking */ int restore_player(player * p, char *name) { return restore_player_title(p, name, 0); } int restore_player_title(player * p, char *name, char *title) { int did_load; int found_lower; char *n; strncpy(p->name, name, MAX_NAME - 2); strncpy(p->lower_name, name, MAX_NAME - 2); lower_case(p->lower_name); if (!strcmp(p->name, p->lower_name)) p->name[0] = toupper(p->name[0]); found_lower = 0; n = p->name; while (*n) { if (*n >= 'a' && *n <= 'z') { found_lower = 1; } *n++; } if (!found_lower) { n = p->name; *n++; while (*n) { *n = *n - ('A' - 'a'); *n++; } } /* Set up a default player structure, methinks */ strncpy(p->prompt, "->", MAX_PROMPT); strncpy(p->converse_prompt, "(Converse) ->", MAX_PROMPT); strcpy(p->enter_msg, "enters in a standard kind of way."); p->term_width = 79; p->column = 0; p->word_wrap = 10; p->total_login = 0; p->gender = VOID_GENDER; p->no_shout = 180; p->saved_flags = PRIVATE_EMAIL | TAG_ECHO | MAIL_INFORM | IAC_GA_ON | NOEPREFIX; strncpy(p->title, "the newbie, so treat me nicely.", MAX_TITLE); strncpy(p->description, "Isn't it time I wrote my own description ?", MAX_DESC); strncpy(p->plan, "I must write myself a proper plan sometime ...", MAX_PLAN); p->max_rooms = 2; p->max_exits = 5; p->max_autos = 5; p->max_list = 25; p->max_mail = 10; p->birthday = 0; p->age = 0; p->jail_timeout = 0; p->script = 0; strcpy(p->script_file, "dummy.log"); p->assisted_by[0] = 0; p->residency = 0; strcpy(p->ignore_msg, ""); p->jetlag = 0; did_load = load_player(p); strcpy(p->assisted_by, ""); if (title && *title) { strncpy(p->title, title, MAX_TITLE); p->title[MAX_TITLE] = 0; } if ((p->saved_flags & IAC_GA_ON) && (!(p->flags & EOR_ON))) p->flags |= IAC_GA_DO; else p->flags &= ~IAC_GA_DO; if (p->residency == 0 && did_load == 1) p->residency = SYSTEM_ROOM; if (p->saved_flags & SAVEDFROGGED) p->flags |= FROGGED; /* got to have someone here I'm afraid.. */ if (!strcmp("admin", p->lower_name)) { p->residency = HCADMIN_INIT; } if (p->residency & PSU) p->no_shout = 0; /* integrity .. sigh */ p->saved_residency = p->residency; /* * if (p->max_rooms>50 || p->max_rooms<0) p->max_rooms=3; if * (p->max_exits>50 || p->max_exits<0) p->max_exits=10; if (p->max_autos>50 * || p->max_autos<0) p->max_autos=10; if (p->max_list>100 || * p->max_list<0) p->max_list=20; if (p->max_mail>100 || p->max_mail<0) * p->max_mail=10; if (p->term_width>200 || p->term_width<0) * p->term_width=79; */ if ((p->word_wrap) > ((p->term_width) >> 1) || p->word_wrap < 0) p->word_wrap = (p->term_width) >> 1; if (p->term > 9) p->term = 0; return did_load; } /* current player stuff */ /* create an abstract player into the void hash list */ player *create_player() { player *p; p = (player *) MALLOC(sizeof(player)); memset((char *) p, 0, sizeof(player)); if (flatlist_start) flatlist_start->flat_previous = p; p->flat_next = flatlist_start; flatlist_start = p; p->hash_next = hashlist[0]; hashlist[0] = p; p->hash_top = 0; p->timer_fn = 0; p->timer_count = -1; p->edit_info = 0; p->logged_in = 0; return p; } /* unlink p from all the lists */ void punlink(player * p) { player *previous, *scan; /* reset the session p */ p_sess = 0; /* first remove from the hash list */ scan = hashlist[p->hash_top]; previous = 0; while (scan && scan != p) { previous = scan; scan = scan->hash_next; } if (!scan) log("error", "Bad hash list"); else if (!previous) hashlist[p->hash_top] = p->hash_next; else previous->hash_next = p->hash_next; /* then remove from the flat list */ if (p->flat_previous) p->flat_previous->flat_next = p->flat_next; else flatlist_start = p->flat_next; if (p->flat_next) p->flat_next->flat_previous = p->flat_previous; /* finally remove from the location list */ if (p->location) { previous = 0; scan = p->location->players_top; while (scan && scan != p) { previous = scan; scan = scan->room_next; } if (!scan) log("error", "Bad Location list"); else if (!previous) p->location->players_top = p->room_next; else previous->room_next = p->room_next; } } /* remove a player from the current hash lists */ void destroy_player(player * p) { #ifndef PC if ((p->fd) > 0) { shutdown(p->fd, 0); close(p->fd); } #endif if (p->name[0] && p->location) current_players--; punlink(p); if (p->edit_info) finish_edit(p); FREE(p); #ifdef PC if (p == input_player) { input_player = flatlist_start; if (!input_player) sys_flags |= SHUTDOWN; } #endif } /* get person to agree to disclaimer */ void agree_disclaimer(player * p, char *str) { p->input_to_fn = 0; if (!strcasecmp(str, "continue")) { p->saved_flags |= AGREED_DISCLAIMER; if (p->saved) p->saved->saved_flags |= AGREED_DISCLAIMER; link_to_program(p); return; } tell_player(p, "\n Disconnecting from program.\n\n"); quit(p, ""); } /* links a person into the program properly */ void link_to_program(player * p) { char *oldstack; saved_player *sp; player *search, *previous, *scan; room *r, *rm; int hash; time_t t; struct tm *log_time; oldstack = stack; search = hashlist[((int) (p->lower_name[0])) - (int) 'a' + 1]; for (; search; search = search->hash_next) { if (!strcmp(p->lower_name, search->lower_name)) { if (p->residency == NON_RESIDENT) { tell_player(p, "\n Sorry there is already someone on the " "program with that name.\n Please try again, " "but use a different name.\n\n"); quit(p, ""); return; } else { tell_player(p, "\n You were already on the program !!\n\n" " Closing other connection.\n\n"); p->total_login = search->total_login; search->flags |= RECONNECTION; p->flags |= RECONNECTION; if (search->location) { previous = 0; scan = search->location->players_top; while (scan && scan != search) { previous = scan; scan = scan->room_next; } if (!scan) log("error", "Bad Location list"); else if (!previous) search->location->players_top = search->room_next; else previous->room_next = search->room_next; } search->location = 0; quit(search, 0); } } } /* do the disclaimer biz */ if (!(p->saved_flags & AGREED_DISCLAIMER)) { if (!(p->saved && p->password[0] == 0)) { tell_player(p, disclaimer_msg.where); do_prompt(p, "Enter 'continue' or 'end':"); p->input_to_fn = agree_disclaimer; return; } } /* remove player from non name hash list */ previous = 0; scan = hashlist[0]; while (scan && scan != p) { previous = scan; scan = scan->hash_next; } if (!scan) log("error", "Bad non-name hash list"); else if (!previous) hashlist[0] = p->hash_next; else previous->hash_next = p->hash_next; /* now place into named hashed lists */ hash = (int) (p->lower_name[0]) - (int) 'a' + 1; p->hash_next = hashlist[hash]; hashlist[hash] = p; p->hash_top = hash; if (p->flags & SITE_LOG) { sprintf(oldstack,"%s - %s",p->name,p->inet_addr); stack=end_string(oldstack); log("site", oldstack); } t = time(0); log_time = localtime(&t); p->flags |= PROMPT; p->timer_fn = 0; p->timer_count = -1; p->saved_flags &= ~NEW_SITE; p->mode = NONE; if (p->residency != NON_RESIDENT) { p->logged_in = 1; sp = p->saved; tell_player(p, motd_msg.where); if (p->saved_flags & CONVERSE) p->mode |= CONV; } else { tell_player(p, newbie_msg.where); tell_player(p, motd_msg.where); } current_players++; p->on_since = time(0); logins++; p->shout_index = 50; if (p->residency != NON_RESIDENT) player_flags(p); if (p->saved_flags & SAVEDJAIL) { p->jail_timeout = -1; trans_to(p, "system.prison"); } else if (p->saved_flags & TRANS_TO_HOME || *p->room_connect) { sp = p->saved; if (!sp) tell_player(p, " Double Eeek (room_connect)!\n"); else { if (p->saved_flags & TRANS_TO_HOME) { for (r = sp->rooms; r; r = r->next) if (r->flags & HOME_ROOM) { sprintf(oldstack, "%s.%s", r->owner->lower_name, r->id); stack = end_string(oldstack); trans_to(p, oldstack); break; } } else { rm = convert_room(p, p->room_connect); if (rm && rm->flags & CONFERENCE && possible_move(p, rm, 1)) trans_to(p, p->room_connect); } if (!(p->location)) tell_player(p, " -=> Tried to connect you to a room, but failed" " !!\n\n"); } } if (!(p->location)) { trans_to(p, ENTRANCE_ROOM); } if (p->flags & RECONNECTION) { do_inform(p, "[%s reconnects] %s"); sprintf(oldstack, " %s's image shimmers for a moment as %s re-connects.\n", p->name, gstring(p)); stack = end_string(oldstack); tell_room(p->location, oldstack); p->flags &= ~RECONNECTION; } else { if (p->gender==PLURAL) do_inform(p, "[%s have connected] %s"); else do_inform(p, "[%s has connected] %s"); if (p->gender==PLURAL) sprintf(oldstack, " %s appear as a wobbly image that soon solidifies.\n", p->name); else sprintf(oldstack, " %s appears as a wobbly image that soon solidifies.\n", p->name); stack = end_string(oldstack); tell_room(p->location, oldstack); } if (p->saved) { decompress_list(p->saved); p->saved_flags = p->saved->saved_flags; } stack = oldstack; } /* get new gender */ void enter_gender(player * p, char *str) { switch (tolower(*str)) { case 'm': p->gender = MALE; tell_player(p, " Gender set to Male.\n"); break; case 'f': p->gender = FEMALE; tell_player(p, " Gender set to Female.\n"); break; case 'p': p->gender = PLURAL; strncpy(p->title, "the newbies, so treat us nicely.", MAX_TITLE); strncpy(p->description, "Isn't it time we wrote our own descriptions ?", MAX_DESC); strncpy(p->plan, "We must write ourselves a proper plan sometime ...", MAX_PLAN); strcpy(p->enter_msg, "enter in a standard kind of way."); tell_player(p, " Gender set to Plural.\n"); break; case 'n': p->gender = OTHER; tell_player(p, " Gender set to well, erm, something.\n"); break; default: tell_player(p, " No gender set.\n"); break; } p->input_to_fn = 0; link_to_program(p); } /* time out */ void login_timeout(player * p) { tell_player(p, "\n\n Connection Timed Out ...\n\n"); quit(p, 0); } /* newbie stuff */ void newbie_get_gender(player * p, char *str) { tell_player(p, "\n\n The program requires that you enter your gender.\n" " This is used solely for the purposes of correct english and grammer.\n" " If you object to this, then simply type 'n' for not applicable.\n\n"); do_prompt(p, "Enter (M)ale, (F)emale, (P)lural, or (N)ot applicable:\n"); p->input_to_fn = enter_gender; } void got_new_name(player * p, char *str) { char *oldstack, *cpy; int length = 0; oldstack = stack; for (cpy = str; *cpy; cpy++) if (isalpha(*cpy)) { *stack++ = *cpy; length++; } *stack++ = 0; length++; if (length > (MAX_NAME - 2)) { tell_player(p, " Sorry, that name is too long, please enter something " "shorter.\n\n"); do_prompt(p, "Please enter a name:"); p->input_to_fn = got_new_name; if (sys_flags & VERBOSE) { cpy = stack; sprintf(cpy, "Name too long : %s\n", str); stack = end_string(cpy); log("connection", cpy); stack = cpy; } stack = oldstack; return; } if (length < 3) { tell_player(p, " Thats a bit short, try something longer.\n\n"); do_prompt(p, "Please enter different name:"); p->input_to_fn = got_new_name; stack = oldstack; return; } if (restore_player_title(p, oldstack, 0)) { tell_player(p, " Sorry, there is already someone who uses the " "program with that name.\n\n"); do_prompt(p, "Please enter different name:"); p->input_to_fn = got_new_name; stack = oldstack; return; } newbie_get_gender(p, str); stack = oldstack; } void newbie_got_name_answer(player * p, char *str) { switch (tolower(*str)) { case 'y': newbie_get_gender(p, str); break; case 'n': tell_player(p, "\n\n Ok, then, please enter a new name ...\n\n"); do_prompt(p, "Enter a name:"); p->input_to_fn = got_new_name; break; default: tell_player(p, " Please answer with Y or N.\n"); newbie_check_name(p, str); break; } } void newbie_check_name(player * p, char *str) { char *oldstack; oldstack = stack; sprintf(stack, "\n\n You entered the name '%s' when you first logged in.\n" " Is this the name that you wish to be known as on the program ?\n\n", p->name); stack = end_string(stack); tell_player(p, oldstack); do_prompt(p, "Answer Y or N:"); p->input_to_fn = newbie_got_name_answer; stack = oldstack; } void newbie_start(player * p, char *str) { tell_player(p, newpage2_msg.where); do_prompt(p, "Hit return to continue:"); p->input_to_fn = newbie_check_name; } /* test password */ int check_password(char *password, char *entered, player * p) { char key[9]; strncpy(key, entered, 8); return (!strncmp(crypt(key, p->lower_name), password, 11)); } void got_password(player * p, char *str) { char *oldstack; oldstack = stack; p->input_to_fn = 0; password_mode_off(p); if (!check_password(p->password, str, p)) { tell_player(p, "\n\n Hey !! that ain't right !\n" " Wrong password .... closing connection.\n\n"); sprintf(stack, "Password fail: %s - %s", p->inet_addr, p->name); stack = end_string(stack); if (p->residency & SU) { log("sufailpass", oldstack); } else { log("connection", oldstack); } stack = oldstack; p->flags |= NO_SAVE_LAST_ON; quit(p, ""); return; } if (p->gender < 0) { p->input_to_fn = enter_gender; tell_player(p, "\n You have no gender set.\n"); do_prompt(p, "Please choose M(ale), F(female) or N(ot applicable):"); return; } stack = oldstack; link_to_program(p); } /* check for soft splats */ int site_soft_splat(player * p) { int no1 = 0, no2 = 0, no3 = 0, no4 = 0, out; out = time(0); sscanf(p->num_addr, "%d.%d.%d.%d", &no1, &no2, &no3, &no4); if (out <= soft_timeout && no1 == soft_splat1 && no2 == soft_splat2) return 1; else return 0; } /* calls here when the player has entered their name */ void got_name(player * p, char *str) { int t; char *oldstack, *cpy, *space; int length = 0, isspace = 0, nologin; player *search; oldstack = stack; for (cpy = str; *cpy && *cpy != ' '; cpy++) if (isalpha(*cpy)) { *stack++ = *cpy; length++; } if (*cpy == ' ') isspace = 1; *stack++ = 0; space = stack; length++; if (length > (MAX_NAME - 2)) { tell_player(p, " Sorry, that name is too long, please enter something " "shorter.\n\n"); do_prompt(p, "Please enter a name:"); p->input_to_fn = got_name; if (sys_flags & VERBOSE) { cpy = stack; sprintf(cpy, "Name too long : %s\n", str); stack = end_string(cpy); log("connection", cpy); stack = cpy; } stack = oldstack; return; } if (length < 3) { tell_player(p, " Thats a bit short, try something longer.\n\n"); do_prompt(p, "Please enter a name:"); p->input_to_fn = got_name; stack = oldstack; return; } if (!strcasecmp("who", oldstack)) { swho(p, 0); p->input_to_fn = got_name; do_prompt(p, "\nPlease enter a name:"); stack = oldstack; return; } if (!strcasecmp("quit", oldstack)) { quit(p, ""); stack = oldstack; return; } if (isspace) while (*++cpy == ' '); if (restore_player_title(p, oldstack, isspace ? cpy : 0)) { if (p->residency & BANISHD) { tell_player(p, banned_msg.where); quit(p, ""); stack = oldstack; return; } if (p->residency == SYSTEM_ROOM) { tell_player(p, "\n Sorry but that name is reserved.\n" " Choose a different name ...\n\n"); do_prompt(p, "Please enter a name:"); p->input_to_fn = got_name; stack = oldstack; return; } t = time(0); if (p->sneezed > t) { nologin = p->sneezed - t; stack = oldstack; sprintf(stack, "\n Sorry, you have been prevented from logging on for " "another %d seconds (and counting ...)\n\n", nologin); stack = end_string(stack); tell_player(p, oldstack); quit(p, ""); stack = oldstack; return; } else p->sneezed = 0; /* login limits checks here */ if (!(p->residency & (LOWER_ADMIN | ADMIN))) { if (current_players >= max_players) { tell_player(p, full_msg.where); quit(p, 0); } } sprintf(oldstack, "\n Password needed for '%s'\n\n", p->name); stack = end_string(oldstack); tell_player(p, oldstack); stack = oldstack; if (p->password[0]) { password_mode_on(p); do_prompt(p, "Please enter your password:"); p->input_to_fn = got_password; p->timer_count = 60; p->timer_fn = login_timeout; stack = oldstack; return; } stack = oldstack; link_to_program(p); tell_player(p, "\n You have no password !!!\n" " Please set one as soon as possible with the 'password' command.\n" " If you don't your character will not save\n"); p->input_to_fn = 0; return; } p->input_to_fn = 0; if (p->flags & CLOSED_TO_NEWBIES) { tell_player(p, newban_msg.where); quit(p, ""); stack = oldstack; return; } if (site_soft_splat(p)) { tell_player(p, newban_msg.where); quit(p, ""); stack = oldstack; return; } if (sys_flags & CLOSED_TO_NEWBIES) { tell_player(p, nonewbies_msg.where); quit(p, ""); stack = oldstack; return; } search = hashlist[((int) (p->lower_name[0])) - (int) 'a' + 1]; for (; search; search = search->hash_next) if (!strcmp(p->lower_name, search->lower_name)) { tell_player(p, "\n Sorry there is already someone on the program " "with that name.\nPlease try again, but use a " "different name.\n\n"); quit(p, ""); return; } tell_player(p, newpage1_msg.where); do_prompt(p, "Hit return to continue:"); p->input_to_fn = newbie_start; p->timer_count = 1800; stack = oldstack; } /* a new player has connected */ void connect_to_prog(player * p) { player *cp; cp = current_player; current_player = p; tell_player(p, "\377\373\031"); /* send will EOR */ tell_player(p, connect_msg.where); do_prompt(p, "Please enter your name:"); p->input_to_fn = got_name; p->timer_count = 60; p->timer_fn = login_timeout; current_player = cp; } /* tell player motd */ void motd(player * p, char *str) { if (!p->residency) tell_player(p, newbie_msg.where); tell_player(p, motd_msg.where); } /* init everything needed for the plist file */ void init_plist() { char *oldstack; int i; oldstack = stack; #ifdef PC newban_msg = load_file("files\\newban.msg"); nonewbies_msg = load_file("files\\nonew.msg"); connect_msg = load_file("files\\connect.msg"); motd_msg = load_file("files\\motd.msg"); banned_msg = load_file("files\\banned.msg"); newbie_msg = load_file("files\\newbie.msg"); newpage1_msg = load_file("files\\newpage1.msg"); newpage2_msg = load_file("files\\newpage2.msg"); #else newban_msg = load_file("files/newban.msg"); nonewbies_msg = load_file("files/nonew.msg"); connect_msg = load_file("files/connect.msg"); motd_msg = load_file("files/motd.msg"); banned_msg = load_file("files/banned.msg"); newbie_msg = load_file("files/newbie.msg"); newpage1_msg = load_file("files/newpage1.msg"); newpage2_msg = load_file("files/newpage2.msg"); disclaimer_msg = load_file("files/disclaimer.msg"); splat_msg = load_file("files/splat.msg"); #endif hard_load_files(); for (i = 0; i < 26; i++) update[i] = 0; stack = oldstack; } /* various associated routines */ /* find one player given a (partial) name */ /* little subroutinette */ int match_player(char *str1, char *str2) { for (; *str2; str1++, str2++) { if (*str1 != *str2 && *str2 != ' ') { if (!(*str1) && *str2 == '.' && !(*(str2 + 1))) return 1; return 0; } } if (*str1) return -1; return 1; } /* command to view res files */ void view_saved_lists(player * p, char *str) { saved_player *scan, **hash; int i, j; char *oldstack; oldstack = stack; if (!*str || !isalpha(*str)) { tell_player(p, " Argument is a letter.\n"); return; } strcpy(stack, "[HASH] [NAME] 12345678901234567890123456789012\n"); stack = strchr(stack, 0); hash = saved_hash[((int) (tolower(*str)) - (int) 'a')]; for (i = 0; i < HASH_SIZE; i++, hash++) for (scan = *hash; scan; scan = scan->next) { sprintf(stack, "[%d]", i); j = strlen(stack); stack = strchr(stack, 0); for (j = 7 - j; j; j--) *stack++ = ' '; strcpy(stack, scan->lower_name); j = strlen(stack); stack = strchr(stack, 0); for (j = 21 - j; j; j--) *stack++ = ' '; switch (scan->residency) { case STANDARD_ROOMS: strcpy(stack, "Standard room file."); break; case BANISHD: strcpy(stack, "BANISHED (Name Only)"); break; default: if (scan->residency & BANISHD) { strcpy(stack, "BANISHED"); } else { strcpy(stack, bit_string(scan->residency)); } break; } stack = strchr(stack, 0); *stack++ = '\n'; } *stack++ = 0; pager(p, oldstack, 1); stack = oldstack; } /* external routine to check updates */ void check_updates(player * p, char *str) { char *oldstack; int i; oldstack = stack; strcpy(stack, "abcdefghijklmnopqrstuvwxyz\n"); stack = strchr(stack, 0); for (i = 0; i < 26; i++) if (update[i]) *stack++ = '*'; else *stack++ = ' '; *stack++ = '\n'; *stack++ = 0; tell_player(p, oldstack); stack = oldstack; } void player_flags(player * p) { char *oldstack, *str; oldstack = stack; str = stack; if (p->residency == NON_RESIDENT) { log("error", "You've sponged Chris. Tried to player_flags a non-resi"); return; } if (!p->saved) tell_player(p, " Eeeeeeek ! No saved bits !\n"); else { sprintf(oldstack, "\n --\n Last logged in %s from %s.\n", convert_time(p->saved->last_on), p->saved->last_host); stack = strchr(stack, 0); strcpy(stack, " You are "); stack = strchr(stack, 0); if (p->saved_flags & HIDING) { strcpy(stack, "in hiding, "); stack = strchr(stack, 0); } if (p->saved_flags & BLOCK_SHOUT) { strcpy(stack, "ignoring shouts, "); stack = strchr(stack, 0); } if (p->saved_flags & BLOCK_TELLS) { strcpy(stack, "ignoring tells, "); stack = strchr(stack, 0); } if (p->saved_flags & CONVERSE) { strcpy(stack, "in converse mode, "); stack = strchr(stack, 0); } if (p->saved_flags & NOPREFIX) { strcpy(stack, "ignoring prefixes, "); stack = strchr(stack, 0); } if (str = strrchr(oldstack, ',')) { *str++ = '.'; *str++ = '\n'; *str++ = 0; stack = strchr(stack, 0); *stack++; tell_player(p, oldstack); } } if (p->saved_flags & NEW_MAIL) { command_type |= HIGHLIGHT; tell_player(p, " You have unread mail\n"); p->saved_flags &= ~NEW_MAIL; p->saved->saved_flags &= ~NEW_MAIL; command_type &= ~HIGHLIGHT; } if (p->residency & SU && sys_flags & CLOSED_TO_NEWBIES) { command_type |= HIGHLIGHT; tell_player(p, " Summink is closed to newbies\n"); command_type &= ~HIGHLIGHT; } tell_player(p, " --\n"); stack = oldstack; } void player_flags_verbose(player * p, char *str) { char *oldstack, *wibble, *argh; player *p2; oldstack = stack; if (*str && (p->residency & SU || p->residency & ADMIN)) { p2 = find_player_absolute_quiet(str); if (!p2) { tell_player(p, " No-one on of that name.\n"); return; } } else p2 = p; if (p2->residency == NON_RESIDENT) { tell_player(p, " You aren't a resident, so your character won't be " "saved when you log off.\n"); return; } strcpy(stack, "\n --\n"); stack = strchr(stack, 0); argh = stack; strcpy(stack, " You are "); stack = strchr(stack, 0); if (p2->saved_flags & HIDING) { strcpy(stack, "in hiding, "); stack = strchr(stack, 0); } if (p2->saved_flags & BLOCK_SHOUT) { strcpy(stack, "ignoring shouts, "); stack = strchr(stack, 0); } if (p2->saved_flags & BLOCK_TELLS) { strcpy(stack, "ignoring tells, "); stack = strchr(stack, 0); } if (p2->saved_flags & CONVERSE) { strcpy(stack, "in converse mode, "); stack = strchr(stack, 0); } if (p2->saved_flags & NOPREFIX) { strcpy(stack, "ignoring prefixes, "); stack = strchr(stack, 0); } if (wibble = strrchr(oldstack, ',')) { *wibble++ = '.'; *wibble++ = '\n'; *wibble = 0; } else stack = argh; if (p2->saved_flags & PRIVATE_EMAIL) strcpy(stack, " Your email is private.\n"); else strcpy(stack, " Your email is public for all to see.\n"); stack = strchr(stack, 0); if (p2->saved_flags & TRANS_TO_HOME) { strcpy(stack, " You will be taken to your home when you log in.\n"); stack = strchr(stack, 0); } else if (*p2->room_connect) { sprintf(stack, " You will try to connect to room '%s' when you log" " in\n", p->room_connect); stack = strchr(stack, 0); } if (p2->saved_flags & NO_ANONYMOUS) strcpy(stack, " You won't receive anonymous mail.\n"); else strcpy(stack, " You are currently able to receive anonymous mail.\n"); stack = strchr(stack, 0); if (p2->saved_flags & IAC_GA_ON) strcpy(stack, " Iacga prompting is turned on.\n"); else strcpy(stack, " Iacga prompting is turned off.\n"); stack = strchr(stack, 0); if (p2->saved_flags & NO_PAGER) strcpy(stack, " You are not recieving paged output.\n"); else strcpy(stack, " You are recieving paged output.\n"); stack = strchr(stack, 0); if (p2->flags & BLOCK_SU && p->residency & PSU) { strcpy(stack, " You are ignoring sus.\n"); stack = strchr(stack, 0); } /* will they be notified when people enter their rooms (ie have they room notify set) */ if (p2->saved_flags & ROOM_ENTER) strcpy(stack, " You will be informed when someone enters one of " "your rooms.\n"); else strcpy(stack, " You will not be informed when someone enters " "one of your rooms.\n"); stack = strchr(stack, 0); strcpy(stack, " --\n"); stack = end_string(stack); tell_player(p, oldstack); stack = oldstack; } /* Catch SEGV's and BUS's on load of players, hopefully... */ void error_on_load() { bad_player_load = 1; longjmp(jmp_env, 0); longjmp(jmp_env, 0); }