/* bsd.c */ #include "os.h" #include "copyright.h" #include "config.h" #include "db.h" #include "interface.h" #include "externs.h" #include "globals.h" #ifdef MEM_CHECK #include "mem_check.h" #endif int shutdown_flag = 0; int login_allow = 1; char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN], cf_nologinmotd_msg[BUFFER_LEN]; #ifdef AT_DOING static char poll[39]; #endif static const char *connect_fail = "Either that player does not exist, or has a different password.\n"; #ifndef WCREAT static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\n"; #endif static const char *flushed_message = "<Output Flushed>\n"; static const char *shutdown_message = "Going down - Bye\n"; static const char *asterisk_line = "*****************************************************************"; struct text_block { int nchars; struct text_block *nxt; char *start; char *buf; }; struct text_queue { struct text_block *head; struct text_block **tail; }; struct descriptor_data { SOCKET descriptor; int connected; char addr[51]; dbref player; char *output_prefix; char *output_suffix; int output_size; struct text_queue output; struct text_queue input; char *raw_input; char *raw_input_at; long connected_at; long last_time; int quota; #ifdef AT_DOING char doing[40]; #endif struct sockaddr_in address; /* added 3/6/90 SCG */ struct descriptor_data *next; struct descriptor_data **prev; } *descriptor_list = 0; static SOCKET sock; static int ndescriptors = 0; char ccom[BUFFER_LEN]; dbref cplr; static void set_signals (void); static void shovechars (int port); static struct descriptor_data *new_connection(SOCKET oldsock); static void shutdownsock(struct descriptor_data *d); static int make_socket(int port); static int queue_write (struct descriptor_data *d, const char *b, int n); static int queue_string (struct descriptor_data *d, const char *s); static int process_output (struct descriptor_data *d); static void make_nonblocking (SOCKET s); static void freeqs (struct descriptor_data *d); static void welcome_user (struct descriptor_data *d); static void spew_message (struct descriptor_data *d, char *filename); static char *strsave (const char *s); static int process_input (struct descriptor_data *d); static void set_userstring (char **userstring, const char *command); static struct descriptor_data *initializesock(SOCKET s, struct sockaddr_in *a, const char *addr); static int do_command (struct descriptor_data *d, char *command); static int check_connect (struct descriptor_data *d, const char *msg); static void parse_connect (const char *msg, char *command, char *user, char *pass); static void close_sockets (void); #ifdef LOCKOUT static const char *addrout (struct in_addr); #endif static void bailout(int sig); static void dump_users (struct descriptor_data *call_by, char *match, int doing); static const char *time_format_1(long dt); static const char *time_format_2(long dt); static void announce_connect (dbref player); static void announce_disconnect (dbref player); static int forbidden_site (const char *hname); #ifdef RWHO_SEND #ifdef FULL_RWHO void dump_rusers (); #endif void rwho_update (); #endif #ifndef BOOLEXP_DEBUGGING int main (int argc, char **argv) { const char *def_db_in = DEF_DB_IN; const char *def_db_out = DEF_DB_OUT; #ifdef AUTORESTART FILE *id; #endif /* change default input database? */ if (argc > 1) { --argc; def_db_in = *++argv; } /* change default dump database? */ if (argc > 1) { --argc; def_db_out = *++argv; } /* this writes a file used by the restart script to check for active mush */ #ifdef AUTORESTART id = fopen ("runid", "wb"); fprintf (id, "%d", getpid ()); fclose (id); #endif OS_SRAND (time (NULL)); /* save a file descriptor */ if (init_game (def_db_in, def_db_out) < 0) { fprintf (stderr, "ERROR: Couldn't load %s! Exiting.\n", def_db_in); WIN32CLEANUP return (2); } WIN32STARTUP set_signals (); #ifdef RWHO_SEND rwhocli_setup (RWHOSERV, RWHOPASS, MUDNAME, VERSION); #endif /* go do it */ shovechars (argc > 1 ? atoi (*++argv) : TINYPORT); close_sockets (); dump_database (); close (sock); /* patch moving this line here fixes * @shutdown error */ WIN32CLEANUP return (0); } #endif /* BOOLEXP_DEBUGGING */ static void set_signals (void) { /* we don't care about SIGPIPE, we notice it in select() and write() */ #ifndef WIN32 signal (SIGPIPE, SIG_IGN); #endif /* standard termination signals */ signal (SIGINT, bailout); signal (SIGTERM, bailout); } void raw_notify (dbref player, const char *msg) { struct descriptor_data *d; if (!msg || *msg == '\0') return; if (!(db[player].flags & PLAYER_CONNECT) && login_allow) return; for (d = descriptor_list; d; d = d->next) { if (d->connected && d->player == player) { queue_string (d, msg); queue_write (d, "\n", 1); } } } #define DESC struct descriptor_data #define DESC_ITER_CONN(d) \ for(d = descriptor_list;(d);d=(d)->next) \ if((d)->connected) void raw_broadcast (int inflags, char *template, ...) { DESC *d; char tbuf1[BUFFER_LEN]; va_list args; if (!template || !*template) return; va_start (args, template); vsprintf (tbuf1, template, args); va_end (args); DESC_ITER_CONN (d) { if ((db[d->player].flags & inflags) == inflags) { queue_string (d, tbuf1); queue_write (d, "\n", 1); process_output (d); } } } static struct timeval timeval_sub (struct timeval now, struct timeval then) { now.tv_sec -= then.tv_sec; now.tv_usec -= then.tv_usec; if (now.tv_usec < 0) { now.tv_usec += 1000000; now.tv_sec--; } return now; } static long msec_diff (struct timeval now, struct timeval then) { return ((now.tv_sec - then.tv_sec) * 1000 + (now.tv_usec - then.tv_usec) / 1000); } static struct timeval msec_add (struct timeval t, int x) { t.tv_sec += x / 1000; t.tv_usec += (x % 1000) * 1000; if (t.tv_usec >= 1000000) { t.tv_sec += t.tv_usec / 1000000; t.tv_usec = t.tv_usec % 1000000; } return t; } struct timeval update_quotas (struct timeval last, struct timeval current) { int nslices; struct descriptor_data *d; nslices = (int) msec_diff (current, last) / COMMAND_TIME_MSEC; if (nslices > 0) { for (d = descriptor_list; d; d = d->next) { d->quota += COMMANDS_PER_TIME * nslices; if (d->quota > COMMAND_BURST_SIZE) d->quota = COMMAND_BURST_SIZE; } } return msec_add (last, nslices * COMMAND_TIME_MSEC); } void shovechars (int port) { fd_set input_set, output_set; time_t now; struct timeval last_slice, current_time; struct timeval next_slice; struct timeval timeout, slice_timeout; int maxd, found; struct descriptor_data *d, *dnext; struct descriptor_data *newd; int avail_descriptors; sock = make_socket (port); maxd = sock + 1; gettimeofday (&last_slice, (struct timezone *) 0); avail_descriptors = getdtablesize () - 4; while (shutdown_flag == 0) { gettimeofday (¤t_time, (struct timezone *) 0); last_slice = update_quotas (last_slice, current_time); #ifdef IDLE_TIMEOUT /*check idle timeout */ inactivity_check (); #endif process_commands (); if (shutdown_flag) break; /* test for events */ dispatch (); /* any queued robot commands waiting? */ timeout.tv_sec = test_top ()? 0 : 1000; timeout.tv_usec = 0; next_slice = msec_add (last_slice, COMMAND_TIME_MSEC); slice_timeout = timeval_sub (next_slice, current_time); FD_ZERO (&input_set); FD_ZERO (&output_set); if (ndescriptors < avail_descriptors) FD_SET (sock, &input_set); for (d = descriptor_list; d; d = d->next) { if (d->input.head) timeout = slice_timeout; else FD_SET (d->descriptor, &input_set); if (d->output.head) FD_SET (d->descriptor, &output_set); } if ((found = select (maxd, &input_set, &output_set, (fd_set *) 0, &timeout)) < 0) { if (GETERROR != EINTR) { perror ("select"); return; } } else { /* if !found then time for robot commands */ if (!found) { do_top (); do_top (); do_top (); continue; } now = time ((time_t *) 0); if (FD_ISSET (sock, &input_set)) { if (!(newd = new_connection (sock))) { if (GETERROR && GETERROR != EINTR && GETERROR != EMFILE && GETERROR != ENFILE) { perror ("new_connection"); return; } } else { #ifndef WIN32 if (newd->descriptor >= maxd) maxd = newd->descriptor + 1; #endif } } for (d = descriptor_list; d; d = dnext) { dnext = d->next; if (FD_ISSET (d->descriptor, &input_set)) { d->last_time = now; if (!process_input (d)) { shutdownsock (d); continue; } } if (FD_ISSET (d->descriptor, &output_set)) { if (!process_output (d)) { shutdownsock (d); } } } } } } static struct descriptor_data *new_connection (SOCKET oldsock) { SOCKET newsock; struct sockaddr_in addr; int addr_len; char tbuf1[BUFFER_LEN]; addr_len = sizeof (addr); newsock = accept (oldsock, (struct sockaddr *) &addr, &addr_len); if (newsock == INVALID_SOCKET) { return 0; #ifdef LOCKOUT } else if (forbidden_site (addrout (addr.sin_addr))) { fprintf (stderr, "REFUSED CONNECTION from %s(%d) on descriptor %d\n", addrout (addr.sin_addr), ntohs (addr.sin_port), newsock); shutdown (newsock, 2); close (newsock); #if !defined WIN32 errno = 0; #endif return 0; #endif /* LOCKOUT */ } else { time_t tt; strcpy (tbuf1, addrout (addr.sin_addr)); tt = time ((time_t *) 0); fprintf (stderr, "USER CONNECT: des: %d host %s time: %s", newsock, tbuf1, ctime (&tt)); return initializesock (newsock, &addr, tbuf1); } } static void clearstrings (struct descriptor_data *d) { if (d->output_prefix) { free ((void *) d->output_prefix); #ifdef MEM_CHECK del_check ("userstring"); #endif d->output_prefix = 0; } if (d->output_suffix) { free ((void *) d->output_suffix); #ifdef MEM_CHECK del_check ("userstring"); #endif d->output_suffix = 0; } } static void shutdownsock (struct descriptor_data *d) { if (d->connected) { spew_message (d, LEAVE_MSG_FILE); fprintf (stderr, "DISCONNECT descriptor %d player %s(%d)\n", d->descriptor, db[d->player].name, d->player); announce_disconnect (d->player); } else { fprintf (stderr, "DISCONNECT descriptor %d never connected\n", d->descriptor); } process_output (d); clearstrings (d); shutdown (d->descriptor, 2); close (d->descriptor); freeqs (d); *d->prev = d->next; if (d->next) d->next->prev = d->prev; free ((void *) d); #ifdef MEM_CHECK del_check ("descriptor"); #endif ndescriptors--; } static struct descriptor_data *initializesock (SOCKET s, struct sockaddr_in *a, const char *addr) { struct descriptor_data *d; ndescriptors++; d = (struct descriptor_data *) malloc (sizeof (struct descriptor_data)); if (!d) panic ("Out of memory."); #ifdef MEM_CHECK add_check ("descriptor"); #endif d->descriptor = s; d->connected = 0; make_nonblocking (s); d->output_prefix = 0; d->output_suffix = 0; d->output_size = 0; d->output.head = 0; d->player = 0; d->output.tail = &d->output.head; d->input.head = 0; d->input.tail = &d->input.head; d->raw_input = 0; d->raw_input_at = 0; d->quota = COMMAND_BURST_SIZE; d->last_time = 0; #ifdef AT_DOING d->doing[0] = '\0'; #endif strncpy (d->addr, addr, 50); d->address = *a; /* added 5/3/90 SCG */ if (descriptor_list) descriptor_list->prev = &d->next; d->next = descriptor_list; d->prev = &descriptor_list; descriptor_list = d; welcome_user (d); return d; } static int make_socket (int port) { SOCKET s; struct sockaddr_in server; int opt; s = socket (AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { perror ("creating stream socket"); WIN32CLEANUP exit (3); } opt = 1; if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof (opt)) == SOCKET_ERROR) { perror ("setsockopt"); WIN32CLEANUP exit (1); } server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons (port); if (bind (s, (struct sockaddr *) &server, sizeof (server)) == SOCKET_ERROR) { perror ("binding stream socket"); close (s); WIN32CLEANUP exit (4); } listen (s, 5); return s; } static struct text_block *make_text_block (const char *s, int n) { struct text_block *p; p = (struct text_block *) malloc (sizeof (struct text_block)); if (!p) panic ("Out of memory"); p->buf = (char *) malloc (sizeof (char) * n); if (!p->buf) panic ("Out of memory"); #ifdef MEM_CHECK add_check ("text_block"); add_check ("text_block_buff"); #endif bcopy (s, p->buf, n); p->nchars = n; p->start = p->buf; p->nxt = 0; return p; } static void free_text_block (struct text_block *t) { if (t) { if (t->buf) free ((void *) t->buf); free ((void *) t); } #ifdef MEM_CHECK del_check ("text_block"); del_check ("text_block_buff"); #endif } static void add_to_queue (struct text_queue *q, const char *b, int n) { struct text_block *p; if (n == 0) return; p = make_text_block (b, n); p->nxt = 0; *q->tail = p; q->tail = &p->nxt; } static int flush_queue (struct text_queue *q, int n) { struct text_block *p; int really_flushed = 0; n += strlen (flushed_message); while (n > 0 && (p = q->head)) { n -= p->nchars; really_flushed += p->nchars; q->head = p->nxt; #ifdef DEBUG fprintf (stderr, "free_text_block(0x%x) at 1.\n", p); #endif /* DEBUG */ free_text_block (p); } p = make_text_block (flushed_message, strlen (flushed_message)); p->nxt = q->head; q->head = p; if (!p->nxt) q->tail = &p->nxt; really_flushed -= p->nchars; return really_flushed; } static int queue_write (struct descriptor_data *d, const char *b, int n) { int space; space = MAX_OUTPUT - d->output_size - n; if (space < 0) d->output_size -= flush_queue (&d->output, -space); add_to_queue (&d->output, b, n); d->output_size += n; return n; } static int queue_string (struct descriptor_data *d, const char *s) { return queue_write (d, s, strlen (s)); } static int process_output (struct descriptor_data *d) { struct text_block **qp, *cur; int cnt; for (qp = &d->output.head; cur = *qp;) { cnt = send (d->descriptor, cur->start, cur->nchars, 0); if (cnt == SOCKET_ERROR) { if (errno == EWOULDBLOCK) return 1; return 0; } d->output_size -= cnt; if (cnt == cur->nchars) { if (!cur->nxt) d->output.tail = qp; *qp = cur->nxt; #ifdef DEBUG fprintf (stderr, "free_text_block(0x%x) at 2.\n", cur); #endif /* DEBUG */ free_text_block (cur); continue; /* do not adv ptr */ } cur->nchars -= cnt; cur->start += cnt; break; } return 1; } static void make_nonblocking (SOCKET s) { #ifdef WIN32 unsigned long flags = 1; if (ioctlsocket (s, FIONBIO, &flags)) { #else if (fcntl (s, F_SETFL, FNDELAY) == -1) { #endif perror ("make_nonblocking: fcntl"); panic ("FNDELAY fcntl failed"); } } static void freeqs (struct descriptor_data *d) { struct text_block *cur, *next; cur = d->output.head; while (cur) { next = cur->nxt; #ifdef DEBUG fprintf (stderr, "free_text_block(0x%x) at 3.\n", cur); #endif /* DEBUG */ free_text_block (cur); cur = next; } d->output.head = 0; d->output.tail = &d->output.head; cur = d->input.head; while (cur) { next = cur->nxt; #ifdef DEBUG fprintf (stderr, "free_text_block(0x%x) at 4.\n", cur); #endif /* DEBUG */ free_text_block (cur); cur = next; } d->input.head = 0; d->input.tail = &d->input.head; if (d->raw_input) { free ((void *) d->raw_input); #ifdef MEM_CHECK del_check ("descriptor_raw_input"); #endif } d->raw_input = 0; d->raw_input_at = 0; } static void welcome_user (struct descriptor_data *d) { spew_message (d, WELCOME_MSG_FILE); } static void spew_message (struct descriptor_data *d, char *filename) { int n; FILE* f; char buf[512]; if ((f = fopen (filename, "rb")) != NULL) { while ((n = fread (buf, 1, 512, f)) > 0) queue_write (d, buf, n); fclose (f); queue_write (d, "\n", 1); } } static char *strsave (const char *s) { char *p; p = (char *) malloc (sizeof (char) * (strlen (s) + 1)); if (!p) panic ("Out of memory"); #ifdef MEM_CHECK add_check ("userstring"); #endif if (p) strcpy (p, s); return p; } static void save_command (struct descriptor_data *d, const char *command) { add_to_queue (&d->input, command, strlen (command) + 1); } static int process_input (struct descriptor_data *d) { int got; char *p, *pend, *q, *qend; char tbuf1[BUFFER_LEN]; got = recv (d->descriptor, tbuf1, sizeof tbuf1, 0); if (got <= 0) return 0; if (!d->raw_input) { d->raw_input = (char *) malloc (sizeof (char) * MAX_COMMAND_LEN); if (!d->raw_input) panic ("Out of memory"); #ifdef MEM_CHECK add_check ("descriptor_raw_input"); #endif d->raw_input_at = d->raw_input; } p = d->raw_input_at; pend = d->raw_input + MAX_COMMAND_LEN - 1; for (q = tbuf1, qend = tbuf1 + got; q < qend; q++) { if (*q == '\n') { *p = '\0'; if (p > d->raw_input) save_command (d, d->raw_input); p = d->raw_input; } else if (p < pend && isascii ((int)*q) && isprint ((int)*q)) { *p++ = *q; } } if (p > d->raw_input) { d->raw_input_at = p; } else { free ((void *) d->raw_input); #ifdef MEM_CHECK del_check ("descriptor_raw_input"); #endif d->raw_input = 0; d->raw_input_at = 0; } return 1; } static void set_userstring (char **userstring, const char *command) { if (*userstring) { free ((void *) *userstring); #ifdef MEM_CHECK del_check ("userstring"); #endif *userstring = 0; } while (*command && isascii ((int)*command) && isspace ((int)*command)) command++; if (*command) *userstring = strsave (command); } void process_commands (void) { int nprocessed; struct descriptor_data *d, *dnext; struct text_block *t; do { nprocessed = 0; for (d = descriptor_list; d; d = dnext) { dnext = d->next; if (d->quota > 0 && (t = d->input.head)) { d->quota--; nprocessed++; if (!do_command (d, t->start)) { shutdownsock (d); } else { d->input.head = t->nxt; if (!d->input.head) d->input.tail = &d->input.head; if (t) { #ifdef DEBUG fprintf (stderr, "free_text_block(0x%x) at 5.\n", t); #endif /* DEBUG */ free_text_block (t); } } } } } while (nprocessed > 0); } static int do_command (struct descriptor_data *d, char *command) { depth = 0; if (!strcmp (command, QUIT_COMMAND)) { return 0; } else if (!strncmp (command, WHO_COMMAND, strlen (WHO_COMMAND))) { if (d->output_prefix) { queue_string (d, d->output_prefix); queue_write (d, "\n", 1); } dump_users (d, command + strlen (WHO_COMMAND), 0); if (d->output_suffix) { queue_string (d, d->output_suffix); queue_write (d, "\n", 1); } #ifdef AT_DOING } else if (!strncmp (command, DOING_COMMAND, strlen (DOING_COMMAND))) { if (d->output_prefix) { queue_string (d, d->output_prefix); queue_write (d, "\n", 1); } dump_users (d, command + strlen (DOING_COMMAND), 1); if (d->output_suffix) { queue_string (d, d->output_suffix); queue_write (d, "\n", 1); } #endif } else if (!strncmp (command, PREFIX_COMMAND, strlen (PREFIX_COMMAND))) { set_userstring (&d->output_prefix, command + strlen (PREFIX_COMMAND)); } else if (!strncmp (command, SUFFIX_COMMAND, strlen (SUFFIX_COMMAND))) { set_userstring (&d->output_suffix, command + strlen (SUFFIX_COMMAND)); #ifdef RWHO_SEND #ifdef FULL_RWHO } else if (!strcmp (command, RWHO_COMMAND)) { dump_rusers (d); #endif #endif } else { if (d->connected) { if (d->output_prefix) { queue_string (d, d->output_prefix); queue_write (d, "\n", 1); } cplr = d->player; strcpy (ccom, command); process_command (d->player, command, d->player); if (d->output_suffix) { queue_string (d, d->output_suffix); queue_write (d, "\n", 1); } } else { if (!check_connect (d, command)) return 0; } } return 1; } static int check_connect (struct descriptor_data *d, const char *msg) { char command[MAX_COMMAND_LEN]; char user[MAX_COMMAND_LEN]; char password[MAX_COMMAND_LEN]; dbref player; parse_connect (msg, command, user, password); if (!strncmp (command, "co", 2)) { player = connect_player (user, password); if (player == NOTHING) { queue_string (d, connect_fail); fprintf (stderr, "FAILED CONNECT %s on descriptor %d\n", user, d->descriptor); } else { fprintf (stderr, "CONNECTED %s(%d) on descriptor %d\n", db[player].name, player, d->descriptor); #ifdef AT_DOING d->doing[0] = '\0'; #endif d->connected = 1; d->connected_at = time ((time_t *) 0); d->player = player; /* give players a message on connection */ if (!login_allow) { spew_message (d, DISABLE_MSG_FILE); raw_notify (player, asterisk_line); if (*cf_nologinmotd_msg) raw_notify (player, cf_nologinmotd_msg); if (!Wizard (player)) { raw_notify (player, asterisk_line); return 0; } else raw_notify (player, asterisk_line); } spew_message (d, CONNECT_MSG_FILE); /* give wizards and royalty their message, too - d'mike 7/15/91 */ if (Hasprivs (player)) { spew_message (d, WIZARD_MSG_FILE); } /* set the Lastsite attribute */ atr_add (player, "LASTSITE", d->addr, GOD, NOTHING); announce_connect (player); #ifdef USE_MAILER check_mail (player, 0); #endif do_look_around (player); if (db[player].flags & HAVEN) { notify (player, "Your HAVEN flag is set. You cannot receive pages."); } } } else if (!strncmp (command, "cr", 2)) { #ifdef WCREAT spew_message (d, REGISTER_MSG_FILE); #else /* WCREAT */ player = create_player (user, password); if (player == NOTHING) { queue_string (d, create_fail); fprintf (stderr, "FAILED CREATE %s on descriptor %d\n", user, d->descriptor); } else { fprintf (stderr, "CREATED %s(%d) on descriptor %d\n", db[player].name, player, d->descriptor); #ifdef AT_DOING d->doing[0] = '\0'; #endif d->connected = 1; d->connected_at = time ((time_t *) 0); d->player = player; if (!login_allow) { spew_message (d, DISABLE_MSG_FILE); raw_notify (player, asterisk_line); if (*cf_nologinmotd_msg) raw_notify (player, cf_nologinmotd_msg); raw_notify (player, asterisk_line); return 0; } /* give new players a special message */ spew_message (d, NEW_CONNECT_MSG_FILE); /* set the Lastsite attribute */ atr_add (player, "LASTSITE", d->addr, GOD, NOTHING); announce_connect (player); do_look_around (player); } #endif /* WCREAT */ } else welcome_user (d); return 1; } static void parse_connect (const char *msg, char *command, char *user, char *pass) { char *p; while (*msg && isascii ((int)*msg) && isspace ((int)*msg)) msg++; p = command; while (*msg && isascii ((int)*msg) && !isspace ((int)*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii ((int)*msg) && isspace ((int)*msg)) msg++; p = user; while (*msg && isascii ((int)*msg) && !isspace ((int)*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii ((int)*msg) && isspace ((int)*msg)) msg++; p = pass; while (*msg && isascii ((int)*msg) && !isspace ((int)*msg)) *p++ = *msg++; *p = '\0'; } static void close_sockets (void) { struct descriptor_data *d, *dnext; #ifdef RWHO_SEND rwhocli_shutdown (); #endif for (d = descriptor_list; d; d = dnext) { dnext = d->next; send (d->descriptor, shutdown_message, strlen (shutdown_message), 0); if (shutdown (d->descriptor, 2) == SOCKET_ERROR) perror ("shutdown"); close (d->descriptor); } } void emergency_shutdown (void) { close_sockets (); } void boot_off (dbref player) { struct descriptor_data *d; for (d = descriptor_list; d; d = d->next) { if (d->connected && d->player == player) { shutdownsock (d); return; } } } static void bailout (int sig) { char tbuf1[BUFFER_LEN]; sprintf (tbuf1, "BAILOUT: caught signal %d", sig); panic (tbuf1); WIN32CLEANUP _exit (7); } static void dump_users (struct descriptor_data *call_by, char *match, int doing /* 0 if normal WHO, 1 if DOING */ ) { struct descriptor_data *d; int count = 0; time_t now; char tbuf1[BUFFER_LEN]; char tbuf2[BUFFER_LEN]; if (call_by->player < 0 || call_by->player >= db_top) { fprintf (stderr, "ERROR: Bogus caller of dump_users! (%d)\n", call_by->player); return; } while (*match && *match == ' ') match++; now = time ((time_t *) 0); /* If a wizard/royal types "DOING" it gives him the normal player WHO. */ /* Wizard/royal WHO does not show @doings. */ if ((doing) || !Hasprivs (call_by->player)) { #ifdef AT_DOING if (poll[0] == '\0') strcpy (poll, "Doing"); sprintf (tbuf2, "Player Name On For Idle %s\n", poll); queue_string (call_by, tbuf2); #else queue_string (call_by, "Player Name On For Idle\n"); #endif } else { queue_string (call_by, "Player Name Location On For Idle [Host]\n"); } for (d = descriptor_list; d; d = d->next) { if (d->connected) { if (d->player < 0 || d->player >= db_top) continue; if (!Dark (d->player) || Wizard (call_by->player)) ++count; if (match && !(string_prefix (db[d->player].name, match))) continue; if (call_by->connected && !(doing) && Hasprivs (call_by->player)) { sprintf (tbuf1, "%-16s [%6d] %9s %5s [%s]", db[d->player].name, getloc (d->player), time_format_1 (now - d->connected_at), time_format_2 (now - d->last_time), d->addr); if (Dark (d->player)) sprintf (tbuf1 + strlen (tbuf1), " (Dark)"); } else { if (!Dark (d->player) || Hasprivs (call_by->player) && (doing)) { #ifdef AT_DOING sprintf (tbuf1, "%-16s %10s %4s %s", #else sprintf (tbuf1, "%-16s %10s %4s", #endif db[d->player].name, time_format_1 (now - d->connected_at), time_format_2 (now - d->last_time) #ifdef AT_DOING , d->doing #endif ); } } if (!Dark (d->player) || Hasprivs (call_by->player)) { queue_string (call_by, tbuf1); queue_write (call_by, "\n", 1); } } } sprintf (tbuf1, "There are %d players connected.\n", count); queue_string (call_by, tbuf1); } static const char *time_format_1 (long dt) { register struct tm *delta; static char buf[64]; if (dt < 0) dt = 0; delta = gmtime ((time_t *) & dt); if (delta->tm_yday > 0) { sprintf (buf, "%dd %02d:%02d", delta->tm_yday, delta->tm_hour, delta->tm_min); } else { sprintf (buf, "%02d:%02d", delta->tm_hour, delta->tm_min); } return buf; } static const char *time_format_2 (long dt) { register struct tm *delta; static char buf[64]; if (dt < 0) dt = 0; delta = gmtime ((time_t *) & dt); if (delta->tm_yday > 0) { sprintf (buf, "%dd", delta->tm_yday); } else if (delta->tm_hour > 0) { sprintf (buf, "%dh", delta->tm_hour); } else if (delta->tm_min > 0) { sprintf (buf, "%dm", delta->tm_min); } else { sprintf (buf, "%ds", delta->tm_sec); } return buf; } static void announce_connect (dbref player) { dbref loc; ATTR *temp; char tbuf1[BUFFER_LEN]; db[player].flags |= PLAYER_CONNECT; if (db[player].flags & PLAYER_SUSPECT) raw_broadcast (WIZARD, "Broadcast: Suspect %s has connected.", db[player].name); if ((loc = getloc (player)) == NOTHING) { notify (player, "You are nowhere!"); return; } speaker = player; #ifdef RWHO_SEND sprintf (tbuf1, "%d@%s", player, MUDNAME); rwhocli_userlogin (tbuf1, db[player].name, time ((time_t *) 0)); #endif raw_notify (player, asterisk_line); if (*cf_motd_msg) raw_notify (player, cf_motd_msg); raw_notify (player, " "); if (Hasprivs (player) && *cf_wizmotd_msg) raw_notify (player, cf_wizmotd_msg); raw_notify (player, asterisk_line); sprintf (tbuf1, "%s has connected.", db[player].name); notify_except (db[player].contents, player, tbuf1); /* added to allow player's inventory to hear a player connect */ if (!Dark (player)) notify_except (db[loc].contents, player, tbuf1); temp = atr_get (player, "ACONNECT"); if (temp) { char *s = safe_uncompress (temp->value); parse_que (player, s, player); free (s); } } static void announce_disconnect (dbref player) { dbref loc; int num; ATTR *temp; struct descriptor_data *d; char tbuf1[BUFFER_LEN]; if ((loc = getloc (player)) == NOTHING) return; speaker = player; for (num = 0, d = descriptor_list; d; d = d->next) if (d->connected && (d->player == player)) num++; if (num < 2) { #ifdef RWHO_SEND sprintf (tbuf1, "%d@%s", player, MUDNAME); rwhocli_userlogout (tbuf1); #endif sprintf (tbuf1, "%s has disconnected.", db[player].name); if (!Dark (player)) notify_except (db[loc].contents, player, tbuf1); /* notify contents */ notify_except (db[player].contents, player, tbuf1); temp = atr_get (player, "ADISCONNECT"); if (temp) { char *s = safe_uncompress (temp->value); parse_que (player, s, player); free (s); } db[player].flags &= ~PLAYER_CONNECT; if (db[player].flags & PLAYER_SUSPECT) raw_broadcast (WIZARD, "Broadcast: Suspect %s has disconnected.", db[player].name); } } #ifdef AT_DOING void do_doing (dbref player, char *arg1, char *arg2) { const char *message; char buf[MAX_COMMAND_LEN]; struct descriptor_data *d; message = reconstruct_message (arg1, arg2); sprintf (buf, message); buf[39] = '\0'; for (d = descriptor_list; d; d = d->next) if (d->connected && (d->player == player)) strcpy (d->doing, buf); if (strlen (message) > 39) notify (player, tprintf ("Doing set. %d characters lost.", strlen (message) - 39)); else notify (player, "Doing set."); } /* this sets the message which replaces "Doing" */ void do_poll (dbref player, char *arg1, char *arg2) { const char *message; if (!Wizard (player)) { notify (player, "Who do you think you are, Gallup?"); return; } message = reconstruct_message (arg1, arg2); strncpy (poll, message, 39); if (strlen (message) > 39) notify (player, tprintf ("Poll set. %d characters lost.", strlen (message) - 39)); else notify (player, "Poll set."); } #endif /* AT_DOING */ #ifdef RWHO_SEND #ifdef FULL_RWHO void dump_rusers (struct descriptor_data *call_by) { struct sockaddr_in addr; struct hostent *hp; char *p; int fd; int red; char *srv = NULL; int portnum = RWHOPORT; char tbuf1[BUFFER_LEN]; p = srv = (char *) RWHOSERV; while (*p != '\0' && (*p == '.' || isdigit ((int)*p))) p++; if (*p != '\0') { if ((hp = gethostbyname (srv)) == (struct hostent *) 0) { fprintf (stderr, "ERROR: unknown host %s\n", srv); queue_string (call_by, "Error in connecting to the RWHO server.\n"); return; } (void) bcopy (hp->h_addr, (char *) &addr.sin_addr, hp->h_length); } else { unsigned long f; if ((f = inet_addr (srv)) == INADDR_NONE) { fprintf (stderr, "ERROR: unknown host %s\n", srv); queue_string (call_by, "Error in connecting to the RWHO server.\n"); return; } (void) bcopy ((char *) &f, (char *) &addr.sin_addr, sizeof (f)); } addr.sin_port = htons (portnum); addr.sin_family = AF_INET; if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { queue_string (call_by, "Socket error in connecting to rwhod. sorry.\n"); return; } if (connect (fd, &addr, sizeof (addr)) < 0) { queue_string (call_by, "Connect error in connecting to rwhod. sorry.\n"); return; } while ((red = recv (fd, tbuf1, sizeof (tbuf1), 0)) > 0) queue_write (call_by, tbuf1, red); close (fd); } #endif /*FULL_RWHO */ void rwho_update (void) { struct descriptor_data *d; char tbuf1[BUFFER_LEN]; rwhocli_pingalive (); for (d = descriptor_list; d; d = d->next) { if (d->connected && !Dark (d->player)) { sprintf (tbuf1, "%d@%s", d->player, MUDNAME); rwhocli_userlogin (tbuf1, db[d->player].name, d->connected_at); } } } #endif /* RWHO_SEND */ #ifdef LOCKOUT static int quick_wild (char *s, char *d) { switch (*s) { case '?': return (wild (s + 1, (*d) ? d + 1 : d, 0, 0)); case '*': return (wild (s + 1, d, 0, 0) || ((*d) ? wild (s, d + 1, 0, 0) : 0)); default: return ((UPCASE (*s) != UPCASE (*d)) ? 0 : ((*s) ? wild (s + 1, d + 1, 0, 0) : 1)); } } static int forbidden_site (const char *hname) { char buf[MAXHOSTNAMELEN], *newlin; FILE *fp; fp = fopen (LOCKOUT_FILE, "rb"); if (fp == NULL) return 0; while (!feof (fp)) { fgets (buf, MAXHOSTNAMELEN, fp); /* step on the newline */ if ((newlin = index (buf, '\n')) != NULL) *newlin = '\0'; if (!strcasecmp (hname, buf)) { fclose (fp); return 1; } } fclose (fp); return 0; } static const char *addrout (struct in_addr a) { struct hostent *he; he = gethostbyaddr ((const char*)&a, sizeof (a), AF_INET); if (he) { return he->h_name; } else { return inet_ntoa (a); } } #endif /* LOCKOUT */ dbref short_page (const char *match) { struct descriptor_data *d; dbref who1 = NOTHING; int count = 0; for (d = descriptor_list; d; d = d->next) { if (d->connected) { if (match && !string_prefix (db[d->player].name, match)) continue; if (!string_compare (db[d->player].name, match)) { count = 1; who1 = d->player; break; } who1 = d->player; count++; } } if (count > 1) return AMBIGUOUS; else if (count == 0) return NOTHING; return who1; } /* LWHO() function - really belongs in eval.c but needs stuff declared here */ /* this function only works for wizards and royalty */ void fun_lwho (char *buff, char *args[10], dbref privs, dbref dumm3) { struct descriptor_data *d; char tbuf1[BUFFER_LEN]; buff[0] = '\0'; if (Hasprivs (privs)) { for (d = descriptor_list; d; d = d->next) { if (d->connected) { if (!Dark (d->player) || Hasprivs (privs)) { if (buff[0] == '\0') sprintf (buff, "#%d", d->player); else { if ((strlen (buff) + 15) < BUFFER_LEN) { sprintf (tbuf1, "%s #%d", buff, d->player); strcpy (buff, tbuf1); } } } } } } else { notify (privs, "Permission denied."); strcpy (buff, "#-1"); } } #ifdef IDLE_TIMEOUT void inactivity_check (void) { DESC *d; register struct tm *idle; time_t now; int check, hrs, mns; check = hrs = mns = 0; now = time ((time_t *) 0); for (mns = INACTIVITY_LIMIT; mns > 60; mns -= 60, hrs++); DESC_ITER_CONN (d) { if (!Wizard (d->player)) { check = (now - d->last_time); idle = gmtime ((time_t *) & check); if ((idle->tm_hour > hrs) || ((idle->tm_hour == hrs) && (idle->tm_min > mns))) { notify (d->player, "\n*** Inactivity timeout ***\n"); boot_off (d->player); } } } } #endif