/* * netcommon.c */ /* * $Id: netcommon.c,v 1.7 2005/08/08 10:30:11 murrayma Exp $ */ /* * This file contains routines used by the networking code that do not * depend on the implementation of the networking code. The network-specific * portions of the descriptor data structure are not used. */ #include "copyright.h" #include "config.h" #include <time.h> #include "db.h" #include "mudconf.h" #include "file_c.h" #include "interface.h" #include "command.h" #include "externs.h" #include "alloc.h" #include "attrs.h" #include "mguests.h" #include "ansi.h" #include "mail.h" #include "powers.h" #include "alloc.h" #include "config.h" #include "p.comsys.h" #ifdef DEBUG_NETCOMMON #ifndef DEBUG #define DEBUG #endif #endif #include "debug.h" extern int process_output(DESC * d); extern void handle_prog(DESC *, char *); extern void fcache_dump_conn(DESC *, int); extern void do_comconnect(dbref, DESC *); extern void do_comdisconnect(dbref); extern void set_lastsite(DESC *, char *); /* * --------------------------------------------------------------------------- * * make_portlist: Make a list of ports for PORTS(). */ void make_portlist(dbref player, dbref target, char *buff, char **bufc) { DESC *d; int i = 0; DESC_ITER_CONN(d) { if (d->player == target) { safe_str(tprintf("%d ", d->descriptor), buff, bufc); i = 1; } } if (i) { (*bufc)--; } **bufc = '\0'; } /* * --------------------------------------------------------------------------- * * timeval_sub: return difference between two times as a timeval */ struct timeval timeval_sub(now, then) struct timeval now, 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; } /* * --------------------------------------------------------------------------- * * msec_diff: return difference between two times in msec */ int msec_diff(struct timeval now, struct timeval then) { return ((now.tv_sec - then.tv_sec) * 1000 + (now.tv_usec - then.tv_usec) / 1000); } /* * --------------------------------------------------------------------------- * * msec_add: add milliseconds to a timeval */ 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; } /* * --------------------------------------------------------------------------- * * update_quotas: Update timeslice quotas */ struct timeval update_quotas(struct timeval last, struct timeval current) { int nslices; DESC *d; nslices = msec_diff(current, last) / (mudconf.timeslice > 0 ? mudconf.timeslice : 1); if (nslices > 0) { DESC_ITER_ALL(d) { d->quota += mudconf.cmd_quota_incr * nslices; if (d->quota > mudconf.cmd_quota_max) d->quota = mudconf.cmd_quota_max; } } return msec_add(last, nslices * mudconf.timeslice); } /* raw_notify_html() -- raw_notify() without the newline */ void raw_notify_html(dbref player, const char *msg) { DESC *d; if (!msg || !*msg) return; if (mudstate.inpipe && (player == mudstate.poutobj)) { safe_str((char *) msg, mudstate.poutnew, &mudstate.poutbufc); return; } if (!Connected(player)) return; DESC_ITER_PLAYER(player, d) { queue_string(d, msg); } } #ifdef TCP_CORK // Linux 2.4, 2.6 /* choke_player: cork the player's sockets, must have a matching release_socket */ void choke_player(dbref player) { DESC *d; int eins = 1, null = 0; DESC_ITER_PLAYER(player, d) { if(d->chokes == 0) { if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_CORK, &eins, sizeof(eins))<0) { // XXX: currently we ignore the error, because if its not supported, // then we'd spam the logs; other errors will be caught in the event loop. } } d->chokes++; } } void release_player(dbref player) { DESC *d; int eins = 1, null = 0; DESC_ITER_PLAYER(player, d) { d->chokes--; if(d->chokes == 0) { if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_CORK, &null, sizeof(null))<0) { // XXX: current we ignore any error, ebcause if its not supported, // then we'd spam the logs; other errors will be caught in the event loop. } } if(d->chokes < 0) d->chokes = 0; } } #else #ifdef TCP_NOPUSH // *BSD, Mac OSX /* choke_player: cork the player's sockets, must have a matching release_socket */ void choke_player(dbref player) { DESC *d; int eins = 1, null = 0; DESC_ITER_PLAYER(player, d) { if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_NOPUSH, &eins, sizeof(eins))<0) { log_perror("NET", "FAIL", "choke_player", "setsockopt"); } } } void release_player(dbref player) { DESC *d; int eins = 1, null = 0; DESC_ITER_PLAYER(player, d) { if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_NOPUSH, &null, sizeof(null))<0) { log_perror("NET", "FAIL", "release_player", "setsockopt"); } } } #else /* no OS support for network block coalescing. */ void choke_player(dbref player) { // Do nothing! } void release_player(dbref player) { // Do nothing! } #endif #endif /* raw_notify_raw: write a message to a player without the newline */ void raw_notify_raw(dbref player, const char *msg, char *append) { DESC *d; if (!msg || !*msg) return; if (mudstate.inpipe && (player == mudstate.poutobj)) { safe_str((char *) msg, mudstate.poutnew, &mudstate.poutbufc); if (append != NULL) safe_str(append, mudstate.poutnew, &mudstate.poutbufc); return; } if (!Connected(player)) return; DESC_ITER_PLAYER(player, d) { queue_string(d, msg); if (append != NULL) queue_write(d, append, strlen(append)); } } /* raw_notify: write a message to a player */ void raw_notify(dbref player, const char *msg) { raw_notify_raw(player, msg, "\r\n"); } void notify_printf(dbref player, const char *format, ...) { DESC *d; char buffer[LBUF_SIZE]; va_list ap; va_start(ap, format); vsnprintf(buffer, LBUF_SIZE, format, ap); va_end(ap); strncat(buffer, "\r\n", LBUF_SIZE); DESC_ITER_PLAYER(player, d) { queue_string(d, buffer); } } void raw_notify_newline(dbref player) { DESC *d; if (mudstate.inpipe && (player == mudstate.poutobj)) { safe_str("\r\n", mudstate.poutnew, &mudstate.poutbufc); return; } if (!Connected(player)) return; DESC_ITER_PLAYER(player, d) { queue_write(d, "\r\n", 2); } } #ifdef HUDINFO_SUPPORT void hudinfo_notify(DESC *d, const char *msgclass, const char *msgtype, const char *msg) { char buf[LBUF_SIZE]; if (!msgclass || !msgtype) { queue_string(d, msg); queue_write(d, "\r\n", 2); return; } snprintf(buf, LBUF_SIZE, "#HUD:%s:%s:%s# %s\r\n", d->hudkey[0] ? d->hudkey : "???", msgclass, msgtype, msg); buf[LBUF_SIZE-1] = '\0'; queue_string(d, buf); } #endif /* * --------------------------------------------------------------------------- * * raw_broadcast: Send message to players who have indicated flags */ void raw_broadcast(int inflags, char *template, ...) { char *buff; DESC *d; va_list ap; va_start(ap, template); if (!template || !*template) return; buff = alloc_lbuf("raw_broadcast"); vsprintf(buff, template, ap); DESC_ITER_CONN(d) { if ((Flags(d->player) & inflags) == inflags) { queue_string(d, buff); queue_write(d, "\r\n", 2); // process_output(d); } } flush_sockets(); free_lbuf(buff); va_end(ap); } /* * --------------------------------------------------------------------------- * * clearstrings: clear out prefix and suffix strings */ void clearstrings(DESC *d) { if (d->output_prefix) { free_lbuf(d->output_prefix); d->output_prefix = NULL; } if (d->output_suffix) { free_lbuf(d->output_suffix); d->output_suffix = NULL; } } /* * --------------------------------------------------------------------------- * * queue_write: Add text to the output queue for the indicated descriptor. */ void queue_write(DESC *d, char *b, int n) { int retval; if (n <= 0) return; bufferevent_write(d->sock_buff, b, n); d->output_tot += n; return; } void queue_string(DESC *d, const char *s) { char *new; if (!Ansi(d->player) && index(s, ESC_CHAR)) new = strip_ansi(s); else if (NoBleed(d->player)) new = normal_to_white(s); else new = (char *) s; queue_write(d, new, strlen(new)); } void freeqs(DESC *d) { CBLK *cb, *cnext; cb = d->input_head; while (cb) { cnext = (CBLK *) cb->hdr.nxt; free_lbuf(cb); cb = cnext; } d->input_head = NULL; d->input_tail = NULL; if (d->raw_input) free_lbuf(d->raw_input); d->raw_input = NULL; d->raw_input_at = NULL; } int desc_cmp(void *vleft, void *vright, void *token) { dbref left = (dbref)vleft; dbref right = (dbref)vright; return (left-right); } /* * --------------------------------------------------------------------------- * * desc_addhash: Add a net descriptor to its player hash list. */ void desc_addhash(DESC *d) { dbref player; DESC *hdesc; player = d->player; hdesc = (DESC *)rb_find(mudstate.desctree, (void *)d->player); dprintk("Adding descriptor %p(%d) to list root at %p (%d).", d, d->player, hdesc, (hdesc?hdesc->player:-1)); if (hdesc == NULL) { d->hashnext = NULL; } else { d->hashnext = hdesc; } rb_insert(mudstate.desctree, (void *)d->player, d); } /* * --------------------------------------------------------------------------- * * desc_delhash: Remove a net descriptor from its player hash list. */ static void desc_delhash(DESC *d) { DESC *hdesc, *last; dbref player; char buffer[4096]; player = d->player; last = NULL; hdesc = (DESC *) rb_find(mudstate.desctree, (void *)d->player); dprintk("removing descriptor %p(%d) from list root %p(%d).", d, d->player, hdesc, hdesc?hdesc->player:-1); if(!hdesc) { snprintf(buffer, 4096, "desc_delhash: unable to find player(%d)'s descriptors from hashtable.\n", d->player); log_text(buffer); return; } dprintk("hdesc: %p, d: %p, hdesc->hashnext: %p, d->hashnext: %p", hdesc, d, hdesc->hashnext, d->hashnext); if(hdesc == d && hdesc->hashnext) { dprintk("updating %d to use hashroot %p", d->player, hdesc->hashnext); rb_insert(mudstate.desctree, (void *)d->player, hdesc->hashnext); return; } else if(hdesc == d) { dprintk("removing %d table", d->player); rb_delete(mudstate.desctree, (void *)d->player); return; } while (hdesc->hashnext != NULL) { if (hdesc->hashnext == d) { hdesc->hashnext = hdesc->hashnext->hashnext; break; } hdesc = hdesc->hashnext; } if(!hdesc) { snprintf(buffer, 4096, "Unable to find descriptor in player(%d)'s descriptor list.\n", d->player); log_text(buffer); } d->hashnext = NULL; return; } extern int fcache_conn_c; void welcome_user(DESC *d) { if (d->host_info & H_REGISTRATION) fcache_dump(d, FC_CONN_REG); else { if (fcache_conn_c) { fcache_dump_conn(d, rand() % fcache_conn_c); return; } fcache_dump(d, FC_CONN); } } void save_command(DESC *d, CBLK *command) { command->hdr.nxt = NULL; if (d->input_tail == NULL) d->input_head = command; else d->input_tail->hdr.nxt = command; d->input_tail = command; } static void set_userstring(char **userstring, const char *command) { while (*command && isascii(*command) && isspace(*command)) command++; if (!*command) { if (*userstring != NULL) { free_lbuf(*userstring); *userstring = NULL; } } else { if (*userstring == NULL) { *userstring = alloc_lbuf("set_userstring"); } StringCopy(*userstring, command); } } static void parse_connect(const char *msg, char *command, char *user, char *pass) { char *p; if (strlen(msg) > MBUF_SIZE) { *command = '\0'; *user = '\0'; *pass = '\0'; return; } while (*msg && isascii(*msg) && isspace(*msg)) msg++; p = command; while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && isspace(*msg)) msg++; p = user; if (mudconf.name_spaces && (*msg == '\"')) { for (; *msg && (*msg == '\"' || isspace(*msg)); msg++); while (*msg && *msg != '\"') { while (*msg && !isspace(*msg) && (*msg != '\"')) *p++ = *msg++; if (*msg == '\"') break; while (*msg && isspace(*msg)) msg++; if (*msg && (*msg != '\"')) *p++ = ' '; } for (; *msg && *msg == '\"'; msg++); } else while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && isspace(*msg)) msg++; p = pass; while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; } static const char *time_format_1(time_t dt) { register struct tm *delta; static char buf[64]; if (dt < 0) dt = 0; delta = gmtime(&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(time_t dt) { register struct tm *delta; static char buf[64]; if (dt < 0) dt = 0; delta = gmtime(&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, DESC *d) { dbref loc, aowner, temp; dbref zone, obj; int aflags, num, key, count; char *buf, *time_str; DESC *dtemp; desc_addhash(d); choke_player(player); count = 0; DESC_ITER_CONN(dtemp) count++; if (mudstate.record_players < count) mudstate.record_players = count; buf = atr_pget(player, A_TIMEOUT, &aowner, &aflags); if (buf) { d->timeout = atoi(buf); if (d->timeout <= 0) d->timeout = mudconf.idle_timeout; } free_lbuf(buf); loc = Location(player); s_Connected(player); if (d->flags & DS_PUEBLOCLIENT) { s_Html(player); } raw_notify(player, tprintf("\n%sMOTD:%s %s\n", ANSI_HILITE, ANSI_NORMAL, mudconf.motd_msg)); if (Wizard(player)) { raw_notify(player, tprintf("%sWIZMOTD:%s %s\n", ANSI_HILITE, ANSI_NORMAL, mudconf.wizmotd_msg)); if (!(mudconf.control_flags & CF_LOGIN)) { raw_notify(player, "*** Logins are disabled."); } } buf = atr_get(player, A_LPAGE, &aowner, &aflags); if (buf && *buf) { raw_notify(player, "Your PAGE LOCK is set. You may be unable to receive some pages."); } num = 0; DESC_ITER_PLAYER(player, dtemp) num++; /* * Reset vacation flag */ s_Flags2(player, Flags2(player) & ~VACATION); if (num < 2) { sprintf(buf, "%s has connected.", Name(player)); if (mudconf.have_comsys) do_comconnect(player, d); if (Dark(player)) { raw_broadcast(MONITOR, (char *) "GAME: %s has DARK-connected.", Name(player), 0, 0, 0, 0, 0); } else { raw_broadcast(MONITOR, (char *) "GAME: %s has connected.", Name(player), 0, 0, 0, 0, 0); } } else { sprintf(buf, "%s has reconnected.", Name(player)); raw_broadcast(MONITOR, (char *) "GAME: %s has reconnected.", Name(player), 0, 0, 0, 0, 0); } key = MSG_INV; if ((loc != NOTHING) && !(Dark(player) && Wizard(player))) key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST); temp = mudstate.curr_enactor; mudstate.curr_enactor = player; notify_checked(player, player, buf, key); free_lbuf(buf); if (Suspect(player)) { send_channel("Suspect", "%s has connected.", Name(player)); } if (d->host_info & H_SUSPECT) send_channel("Suspect", "[Suspect site: %s] %s has connected.", d->addr, Name(player)); buf = atr_pget(player, A_ACONNECT, &aowner, &aflags); if (buf) wait_que(player, player, 0, NOTHING, 0, buf, (char **) NULL, 0, NULL); free_lbuf(buf); if (mudconf.master_room != NOTHING) { buf = atr_pget(mudconf.master_room, A_ACONNECT, &aowner, &aflags); if (buf) wait_que(mudconf.master_room, player, 0, NOTHING, 0, buf, (char **) NULL, 0, NULL); free_lbuf(buf); DOLIST(obj, Contents(mudconf.master_room)) { buf = atr_pget(obj, A_ACONNECT, &aowner, &aflags); if (buf) { wait_que(obj, player, 0, NOTHING, 0, buf, (char **) NULL, 0, NULL); } free_lbuf(buf); } } /* * do the zone of the player's location's possible aconnect */ if (mudconf.have_zones && ((zone = Zone(loc)) != NOTHING)) { switch (Typeof(zone)) { case TYPE_THING: buf = atr_pget(zone, A_ACONNECT, &aowner, &aflags); if (buf) { wait_que(zone, player, 0, NOTHING, 0, buf, (char **) NULL, 0, NULL); } free_lbuf(buf); break; case TYPE_ROOM: /* * check every object in the room for a connect * * * * action */ DOLIST(obj, Contents(zone)) { buf = atr_pget(obj, A_ACONNECT, &aowner, &aflags); if (buf) { wait_que(obj, player, 0, NOTHING, 0, buf, (char **) NULL, 0, NULL); } free_lbuf(buf); } break; default: log_text(tprintf ("Invalid zone #%d for %s(#%d) has bad type %d", zone, Name(player), player, Typeof(zone))); } } time_str = ctime(&mudstate.now); time_str[strlen(time_str) - 1] = '\0'; record_login(player, 1, time_str, d->addr, d->username); look_in(player, Location(player), (LK_SHOWEXIT | LK_OBEYTERSE | LK_SHOWVRML)); mudstate.curr_enactor = temp; release_player(player); } void announce_disconnect(dbref player, DESC *d, const char *reason) { dbref loc, aowner, temp, zone, obj; int num, aflags, key; char *buf, *atr_temp; DESC *dtemp; char *argv[1]; if (Suspect(player)) { send_channel("Suspect", "%s has disconnected.", Name(player)); } if (d->host_info & H_SUSPECT) { send_channel("Suspect", "[Suspect site: %s] %s has disconnected.", d->addr, Name(d->player)); } loc = Location(player); num = 0; DESC_ITER_PLAYER(player, dtemp) num++; temp = mudstate.curr_enactor; mudstate.curr_enactor = player; if (num < 2) { buf = alloc_mbuf("announce_disconnect.only"); sprintf(buf, "%s has disconnected.", Name(player)); key = MSG_INV; if ((loc != NOTHING) && !(Dark(player) && Wizard(player))) key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST); notify_checked(player, player, buf, key); free_mbuf(buf); if (mudconf.have_comsys) do_comdisconnect(player); if (mudconf.have_mailer) do_mail_purge(player); raw_broadcast(MONITOR, (char *) "GAME: %s has disconnected.", Name(player), 0, 0, 0, 0, 0); if (Guest(player) && mudconf.have_comsys) toast_player(player); argv[0] = (char *) reason; c_Connected(player); atr_temp = atr_pget(player, A_ADISCONNECT, &aowner, &aflags); if (atr_temp && *atr_temp) wait_que(player, player, 0, NOTHING, 0, atr_temp, argv, 1, NULL); free_lbuf(atr_temp); if (mudconf.master_room != NOTHING) { atr_temp = atr_pget(mudconf.master_room, A_ADISCONNECT, &aowner, &aflags); if (atr_temp) wait_que(mudconf.master_room, player, 0, NOTHING, 0, atr_temp, (char **) NULL, 0, NULL); free_lbuf(atr_temp); DOLIST(obj, Contents(mudconf.master_room)) { atr_temp = atr_pget(obj, A_ADISCONNECT, &aowner, &aflags); if (atr_temp) { wait_que(obj, player, 0, NOTHING, 0, atr_temp, (char **) NULL, 0, NULL); } free_lbuf(atr_temp); } } /* * do the zone of the player's location's possible * * * * adisconnect */ if (mudconf.have_zones && ((zone = Zone(loc)) != NOTHING)) { switch (Typeof(zone)) { case TYPE_THING: atr_temp = atr_pget(zone, A_ADISCONNECT, &aowner, &aflags); if (atr_temp) { wait_que(zone, player, 0, NOTHING, 0, atr_temp, (char **) NULL, 0, NULL); } free_lbuf(atr_temp); break; case TYPE_ROOM: /* * check every object in the room for a * * * * connect action */ DOLIST(obj, Contents(zone)) { atr_temp = atr_pget(obj, A_ADISCONNECT, &aowner, &aflags); if (atr_temp) { wait_que(obj, player, 0, NOTHING, 0, atr_temp, (char **) NULL, 0, NULL); } free_lbuf(atr_temp); } break; default: log_text(tprintf ("Invalid zone #%d for %s(#%d) has bad type %d", zone, Name(player), player, Typeof(zone))); } } if (d->flags & DS_AUTODARK) { s_Flags(d->player, Flags(d->player) & ~DARK); d->flags &= ~DS_AUTODARK; } if (Guest(player)) s_Flags(player, Flags(player) | DARK); } else { buf = alloc_mbuf("announce_disconnect.partial"); sprintf(buf, "%s has partially disconnected.", Name(player)); key = MSG_INV; if ((loc != NOTHING) && !(Dark(player) && Wizard(player))) key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST); notify_checked(player, player, buf, key); raw_broadcast(MONITOR, (char *) "GAME: %s has partially disconnected.", Name(player), 0, 0, 0, 0, 0); free_mbuf(buf); } mudstate.curr_enactor = temp; release_player(player); desc_delhash(d); } int boot_off(dbref player, char *message) { DESC *d, *dnext; int count; count = 0; DESC_SAFEITER_PLAYER(player, d, dnext) { if (message && *message) { queue_string(d, message); queue_string(d, "\r\n"); } shutdownsock(d, R_BOOT); count++; } return count; } int boot_by_port(int port, int no_god, char *message) { DESC *d, *dnext; int count; count = 0; DESC_SAFEITER_ALL(d, dnext) { if ((d->descriptor == port) && (!no_god || !God(d->player))) { if (message && *message) { queue_string(d, message); queue_string(d, "\r\n"); } shutdownsock(d, R_BOOT); count++; } } return count; } /* * --------------------------------------------------------------------------- * * desc_reload: Reload parts of net descriptor that are based on db info. */ void desc_reload(dbref player) { DESC *d; char *buf; dbref aowner; FLAG aflags; DESC_ITER_PLAYER(player, d) { buf = atr_pget(player, A_TIMEOUT, &aowner, &aflags); if (buf) { d->timeout = atoi(buf); if (d->timeout <= 0) d->timeout = mudconf.idle_timeout; } free_lbuf(buf); } } /* * --------------------------------------------------------------------------- * * fetch_idle, fetch_connect: Return smallest idle time/largest connec time * * for a player (or -1 if not logged in) */ int fetch_idle(dbref target) { DESC *d; int result, idletime; result = -1; DESC_ITER_PLAYER(target, d) { idletime = (mudstate.now - d->last_time); if ((result == -1) || (idletime < result)) result = idletime; } return result; } int fetch_connect(dbref target) { DESC *d; int result, conntime; result = -1; DESC_ITER_PLAYER(target, d) { conntime = (mudstate.now - d->connected_at); if (conntime > result) result = conntime; } return result; } void check_idle(void) { DESC *d, *dnext; time_t idletime; DESC_SAFEITER_ALL(d, dnext) { if (d->flags & DS_CONNECTED) { idletime = mudstate.now - d->last_time; if ((idletime > d->timeout) && !Can_Idle(d->player)) { queue_string(d, "*** Inactivity Timeout ***\r\n"); shutdownsock(d, R_TIMEOUT); } else if (mudconf.idle_wiz_dark && (idletime > mudconf.idle_timeout) && Can_Idle(d->player) && !Dark(d->player)) { s_Flags(d->player, Flags(d->player) | DARK); d->flags |= DS_AUTODARK; } } else { idletime = mudstate.now - d->connected_at; if (idletime > mudconf.conn_timeout) { queue_string(d, "*** Login Timeout ***\r\n"); shutdownsock(d, R_TIMEOUT); } } } } void check_events(void) { struct tm *ltime; dbref thing, parent; int lev; ltime = localtime(&mudstate.now); if ((ltime->tm_hour == mudconf.events_daily_hour) && !(mudstate.events_flag & ET_DAILY)) { mudstate.events_flag = mudstate.events_flag | ET_DAILY; DO_WHOLE_DB(thing) { if (Going(thing)) continue; ITER_PARENTS(thing, parent, lev) { if (Flags2(thing) & HAS_DAILY) { did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_DAILY, (char **) NULL, 0); break; } } } } if (ltime->tm_hour != mudstate.events_lasthour) { if (mudstate.events_lasthour >= 0) { /* Run hourly maintenance */ DO_WHOLE_DB(thing) { if (Going(thing)) continue; ITER_PARENTS(thing, parent, lev) { if (Flags2(thing) & HAS_HOURLY) { did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_HOURLY, (char **) NULL, 0); break; } } } } mudstate.events_lasthour = ltime->tm_hour; } if (ltime->tm_hour == 23) { /* * Nightly resetting */ mudstate.events_flag = 0; } } static char *trimmed_name(dbref player) { static char cbuff[18]; if (strlen(Name(player)) <= 16) return Name(player); StringCopyTrunc(cbuff, Name(player), 16); cbuff[16] = '\0'; return cbuff; } static char *trimmed_site( char *name) { static char buff[MBUF_SIZE]; if ((strlen(name) <= mudconf.site_chars) || (mudconf.site_chars == 0)) return name; StringCopyTrunc(buff, name, mudconf.site_chars); buff[mudconf.site_chars + 1] = '\0'; return buff; } static void dump_users(DESC *e, char *match, int key) { DESC *d; int count, rcount; char *buf, *fp, *sp, flist[4], slist[4]; dbref room_it; while (match && *match && isspace(*match)) match++; if (!match || !*match) match = NULL; if (e->flags & DS_PUEBLOCLIENT) queue_string(e, "<pre>"); buf = alloc_mbuf("dump_users"); if (key == CMD_SESSION) { queue_string(e, " "); queue_string(e, " Characters Input---- Characters Output---\r\n"); } queue_string(e, "Player Name On For Idle "); if (key == CMD_SESSION) { queue_string(e, "Port Pend Lost Total Pend Lost Total\r\n"); } else if ((e->flags & DS_CONNECTED) && (Wizard_Who(e->player)) && (key == CMD_WHO)) { queue_string(e, " Room Cmds Host\r\n"); } else { if (Wizard_Who(e->player)) queue_string(e, " "); else queue_string(e, " "); queue_string(e, mudstate.doing_hdr); queue_string(e, "\r\n"); } count = 0; rcount = 0; DESC_ITER_CONN(d) { if (!Hidden(d->player) || (e->flags & DS_CONNECTED) & Wizard_Who(e->player)) { count++; if (match && !(string_prefix(Name(d->player), match))) continue; #if 0 if ((!((Wizard_Who(e->player)) && (e->flags & DS_CONNECTED)) && (d->player != e->player))) if (In_Character(Location(d->player)) && In_Character(Location(Location(d->player)))) continue; #endif rcount++; if ((key == CMD_SESSION) && !(Wizard_Who(e->player) && (e->flags & DS_CONNECTED)) && (d->player != e->player)) continue; /* * Get choice flags for wizards */ fp = flist; sp = slist; if ((e->flags & DS_CONNECTED) && Wizard_Who(e->player)) { if (Hidden(d->player)) { if (d->flags & DS_AUTODARK) *fp++ = 'd'; else if (Dark(d->player)) *fp++ = 'D'; } if (!Findable(d->player)) { *fp++ = 'U'; } else { room_it = where_room(d->player); if (Good_obj(room_it)) { if (Hideout(room_it)) *fp++ = 'u'; } else { *fp++ = 'u'; } } if (Suspect(d->player)) *fp++ = '+'; if (d->host_info & H_FORBIDDEN) *sp++ = 'F'; if (d->host_info & H_REGISTRATION) *sp++ = 'R'; if (d->host_info & H_SUSPECT) *sp++ = '+'; } *fp = '\0'; *sp = '\0'; if ((e->flags & DS_CONNECTED) && Wizard_Who(e->player) && (key == CMD_WHO)) { sprintf(buf, "%-16s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n", trimmed_name(d->player), time_format_1(mudstate.now - d->connected_at), time_format_2(mudstate.now - d->last_time), flist, Location(d->player), d->command_count, slist, trimmed_site(((d->username[0] != '\0') ? tprintf("%s@%s", d->username, d->addr) : d->addr))); } else if (key == CMD_SESSION) { sprintf(buf, "%-16s%9s %4s%5d%5d%6d%10d%6d%6d%10d\r\n", trimmed_name(d->player), time_format_1(mudstate.now - d->connected_at), time_format_2((mudstate.now - d->last_time) > HIDDEN_IDLESECS ? (mudstate.now - d->last_time) : 0), d->descriptor, d->input_size, d->input_lost, d->input_tot, d->output_size, d->output_lost, d->output_tot); } else if (Wizard_Who(e->player)) { sprintf(buf, "%-16s%9s %4s%-3s%s\r\n", trimmed_name(d->player), time_format_1(mudstate.now - d->connected_at), time_format_2((mudstate.now - d->last_time) > HIDDEN_IDLESECS ? (mudstate.now - d->last_time) : 0), flist, d->doing); } else { sprintf(buf, "%-16s%9s %4s %s\r\n", trimmed_name(d->player), time_format_1(mudstate.now - d->connected_at), time_format_2((mudstate.now - d->last_time) > HIDDEN_IDLESECS ? (mudstate.now - d->last_time) : 0), d->doing); } queue_string(e, buf); } } count = rcount; /* previous mode was .. disgusting. */ /* * sometimes I like the ternary operator.... */ sprintf(buf, "%d Player%slogged in, %d record, %s maximum.\r\n", count, (count == 1) ? " " : "s ", mudstate.record_players, (mudconf.max_players == -1) ? "no" : tprintf("%d", mudconf.max_players)); queue_string(e, buf); if (e->flags & DS_PUEBLOCLIENT) queue_string(e, "</pre>"); free_mbuf(buf); } /* * --------------------------------------------------------------------------- * * do_doing: Set the doing string that appears in the WHO report. * * Idea from R'nice@TinyTIM. */ void do_doing(dbref player, dbref cause, int key, char *arg) { DESC *d; char *c, *e; int foundany, over; if (key == DOING_MESSAGE) { foundany = 0; over = 0; DESC_ITER_PLAYER(player, d) { c = d->doing; over = safe_copy_str(arg, d->doing, &c, DOINGLEN - 2 - strlen(ANSI_NORMAL)); /* See if there's <esc>[<numbers> as the last remaining stuff */ if (over) { e = c; c--; if (isdigit(*c)) { while (isdigit(*c) && c > e) c--; if (*c == '[') { c--; if (c > e && *c == '\033') { *c = 0; e = c; } } } StringCopy(e, ANSI_NORMAL); } else StringCopy(c, ANSI_NORMAL); foundany = 1; } if (foundany) { if (over) { notify_printf(player, "Warning: %d characters lost.", over); } if (!Quiet(player)) notify(player, "Set."); } else { notify(player, "Not connected."); } } else if (key == DOING_HEADER) { if (!(Can_Poll(player))) { notify(player, "Permission denied."); return; } if (!arg || !*arg) { StringCopy(mudstate.doing_hdr, "Doing"); over = 0; } else { c = mudstate.doing_hdr; over = safe_copy_str(arg, mudstate.doing_hdr, &c, DOINGLEN - 1); *c = '\0'; } if (over) { notify(player, tprintf("Warning: %d characters lost.", over)); } if (!Quiet(player)) notify(player, "Set."); } else { notify(player, tprintf("Poll: %s", mudstate.doing_hdr)); } } /* *INDENT-OFF* */ NAMETAB logout_cmdtable[] = { {(char *)"DOING", 5, CA_PUBLIC, CMD_DOING}, {(char *)"LOGOUT", 6, CA_PUBLIC, CMD_LOGOUT}, {(char *)"OUTPUTPREFIX",12, CA_PUBLIC, CMD_PREFIX|CMD_NOxFIX}, {(char *)"OUTPUTSUFFIX",12, CA_PUBLIC, CMD_SUFFIX|CMD_NOxFIX}, {(char *)"QUIT", 4, CA_PUBLIC, CMD_QUIT}, {(char *)"SESSION", 7, CA_PUBLIC, CMD_SESSION}, {(char *)"WHO", 3, CA_PUBLIC, CMD_WHO}, {(char *)"PUEBLOCLIENT", 12, CA_PUBLIC, CMD_PUEBLOCLIENT}, {NULL, 0, 0, 0}}; /* *INDENT-ON* */ void init_logout_cmdtab(void) { NAMETAB *cp; /* * Make the htab bigger than the number of entries so that we find * things on the first check. Remember that the admin can add * aliases. */ hashinit(&mudstate.logout_cmd_htab, 3 * HASH_FACTOR); for (cp = logout_cmdtable; cp->flag; cp++) hashadd(cp->name, (int *) cp, &mudstate.logout_cmd_htab); } static void failconn(const char *logcode, const char *logtype, const char *logreason, DESC *d, int disconnect_reason, dbref player, int filecache, char *motd_msg, char *command, char *user, char *password, char *cmdsave) { char *buff; STARTLOG(LOG_LOGIN | LOG_SECURITY, logcode, "RJCT") { buff = alloc_mbuf("failconn.LOG"); sprintf(buff, "[%d/%s] %s rejected to ", d->descriptor, d->addr, logtype); log_text(buff); free_mbuf(buff); if (player != NOTHING) log_name(player); else log_text(user); log_text((char *) " ("); log_text((char *) logreason); log_text((char *) ")"); ENDLOG; } fcache_dump(d, filecache); if (*motd_msg) { queue_string(d, motd_msg); queue_write(d, "\r\n", 2); } free_lbuf(command); free_lbuf(user); free_lbuf(password); shutdownsock(d, disconnect_reason); mudstate.debug_cmd = cmdsave; return; } static const char *connect_fail = "Either that player does not exist, or has a different password.\r\n"; static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\r\n"; static int check_connect(DESC *d, char *msg) { char *command, *user, *password, *buff, *cmdsave; dbref player, aowner; int aflags, nplayers; DESC *d2; char *p; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *) "< check_connect >"; /* * Hide the password length from SESSION */ d->input_tot -= (strlen(msg) + 1); /* * Crack the command apart */ command = alloc_lbuf("check_conn.cmd"); user = alloc_lbuf("check_conn.user"); password = alloc_lbuf("check_conn.pass"); parse_connect(msg, command, user, password); if (!strncmp(command, "co", 2) || !strncmp(command, "cd", 2)) { if ((string_prefix(user, mudconf.guest_prefix)) && (mudconf.guest_char != NOTHING) && (mudconf.control_flags & CF_LOGIN)) { if ((p = make_guest(d)) == NULL) { queue_string(d, "All guests are tied up, please try again later.\n"); free_lbuf(command); free_lbuf(user); free_lbuf(password); return 0; } StringCopy(user, p); StringCopy(password, mudconf.guest_prefix); } /* * See if this connection would exceed the max #players */ if (mudconf.max_players < 0) { nplayers = mudconf.max_players - 1; } else { nplayers = 0; DESC_ITER_CONN(d2) nplayers++; } player = connect_player(user, password, d->addr, d->username); if (player == NOTHING) { /* * Not a player, or wrong password */ queue_string(d, connect_fail); STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD") { buff = alloc_lbuf("check_conn.LOG.bad"); user[3800] = '\0'; sprintf(buff, "[%d/%s] Failed connect to '%s'", d->descriptor, d->addr, user); log_text(buff); free_lbuf(buff); ENDLOG; } if (--(d->retries_left) <= 0) { free_lbuf(command); free_lbuf(user); free_lbuf(password); shutdownsock(d, R_BADLOGIN); mudstate.debug_cmd = cmdsave; return 0; } } else if (((mudconf.control_flags & CF_LOGIN) && (nplayers < mudconf.max_players)) || WizRoy(player) || God(player)) { if (!strncmp(command, "cd", 2) && (Wizard(player) || God(player))) s_Flags(player, Flags(player) | DARK); /* * Logins are enabled, or wiz or god */ STARTLOG(LOG_LOGIN, "CON", "LOGIN") { buff = alloc_mbuf("check_conn.LOG.login"); sprintf(buff, "[%d/%s] Connected to ", d->descriptor, d->addr); log_text(buff); log_name_and_loc(player); free_mbuf(buff); ENDLOG; } d->flags |= DS_CONNECTED; d->connected_at = time(0); d->player = player; set_lastsite(d, NULL); /* Check to see if the player is currently running * an @program. If so, drop the new descriptor into * it. */ DESC_ITER_PLAYER(player, d2) { if (d2->program_data != NULL) { d->program_data = d2->program_data; break; } } /* * Give the player the MOTD file and the settable * * * * * MOTD * message(s). Use raw notifies so the * player * * * doesn't * try to match on the text. */ if (Guest(player)) { fcache_dump(d, FC_CONN_GUEST); } else { buff = atr_get(player, A_LAST, &aowner, &aflags); if ((buff == NULL) || (*buff == '\0')) fcache_dump(d, FC_CREA_NEW); else fcache_dump(d, FC_MOTD); if (Wizard(player)) fcache_dump(d, FC_WIZMOTD); free_lbuf(buff); } announce_connect(player, d); /* If stuck in an @prog, show the prompt */ if (d->program_data != NULL) queue_string(d, ">\377\371"); } else if (!(mudconf.control_flags & CF_LOGIN)) { failconn("CON", "Connect", "Logins Disabled", d, R_GAMEDOWN, player, FC_CONN_DOWN, mudconf.downmotd_msg, command, user, password, cmdsave); return 0; } else { failconn("CON", "Connect", "Game Full", d, R_GAMEFULL, player, FC_CONN_FULL, mudconf.fullmotd_msg, command, user, password, cmdsave); return 0; } } else if (!strncmp(command, "cr", 2)) { /* * Enforce game down */ if (!(mudconf.control_flags & CF_LOGIN)) { failconn("CRE", "Create", "Logins Disabled", d, R_GAMEDOWN, NOTHING, FC_CONN_DOWN, mudconf.downmotd_msg, command, user, password, cmdsave); return 0; } /* * Enforce max #players */ if (mudconf.max_players < 0) { nplayers = mudconf.max_players; } else { nplayers = 0; DESC_ITER_CONN(d2) nplayers++; } if (nplayers > mudconf.max_players) { /* * Too many players on, reject the attempt */ failconn("CRE", "Create", "Game Full", d, R_GAMEFULL, NOTHING, FC_CONN_FULL, mudconf.fullmotd_msg, command, user, password, cmdsave); return 0; } if (d->host_info & H_REGISTRATION) { fcache_dump(d, FC_CREA_REG); } else { player = create_player(user, password, NOTHING, 0, 0); if (player == NOTHING) { queue_string(d, create_fail); STARTLOG(LOG_SECURITY | LOG_PCREATES, "CON", "BAD") { buff = alloc_mbuf("check_conn.LOG.badcrea"); sprintf(buff, "[%d/%s] Create of '%s' failed", d->descriptor, d->addr, user); log_text(buff); free_mbuf(buff); ENDLOG; } } else { STARTLOG(LOG_LOGIN | LOG_PCREATES, "CON", "CREA") { buff = alloc_mbuf("check_conn.LOG.create"); sprintf(buff, "[%d/%s] Created ", d->descriptor, d->addr); log_text(buff); log_name(player); free_mbuf(buff); ENDLOG; } move_object(player, mudconf.start_room); d->flags |= DS_CONNECTED; d->connected_at = time(0); d->player = player; set_lastsite(d, NULL); fcache_dump(d, FC_CREA_NEW); announce_connect(player, d); } } } else { welcome_user(d); STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD") { buff = alloc_mbuf("check_conn.LOG.bad"); msg[150] = '\0'; sprintf(buff, "[%d/%s] Failed connect: '%s'", d->descriptor, d->addr, msg); log_text(buff); free_mbuf(buff); ENDLOG; } } free_lbuf(command); free_lbuf(user); free_lbuf(password); mudstate.debug_cmd = cmdsave; return 1; } int do_command(DESC *d, char *command, int first) { char *arg, *cmdsave; NAMETAB *cp; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *) "< do_command >"; d->last_time = mudstate.now; /* * Split off the command from the arguments */ arg = command; while (*arg && !isspace(*arg)) arg++; if (*arg) *arg++ = '\0'; #ifdef HUDINFO_SUPPORT /* We check for hudinfo before anything else. This is a fairly dirty * hack, and slows down the common case by a strcmp (which is fast on * modern processors and libraries) but has many advantages: hudinfo * only outputs to the *connection* (rather than the player) that issued * the command, and always knows which hud session key to use. I think * the payoff is justified, in this case. */ if (mudconf.hudinfo_enabled > 0 && d->flags & DS_CONNECTED && !strcmp(command, "hudinfo")) { d->command_count++; mudstate.curr_player = d->player; mudstate.curr_enactor = d->player; mudstate.debug_cmd = "hudinfo"; do_hudinfo(d, arg); mudstate.debug_cmd = cmdsave; return 1; } #endif /* * Look up the command. If we don't find it, turn it over to the normal * logged-in command processor or to create/connect */ if (!(d->flags & DS_CONNECTED)) { cp = (NAMETAB *) hashfind(command, &mudstate.logout_cmd_htab); } else cp = NULL; if (cp == NULL) { if (*arg) *--arg = ' '; /* * restore nullified space */ if (d->flags & DS_CONNECTED) { d->command_count++; choke_player(d->player); if (d->output_prefix) { queue_string(d, d->output_prefix); queue_write(d, "\r\n", 2); } mudstate.curr_player = d->player; mudstate.curr_enactor = d->player; process_command(d->player, d->player, 1, command, (char **) NULL, 0); if (d->output_suffix) { queue_string(d, d->output_suffix); queue_write(d, "\r\n", 2); } release_player(d->player); mudstate.debug_cmd = cmdsave; return 1; } else { mudstate.debug_cmd = cmdsave; return (check_connect(d, command)); } } /* * The command was in the logged-out command table. Perform prefix and * suffix processing, and invoke the command handler. */ d->command_count++; if (!(cp->flag & CMD_NOxFIX)) { if (d->output_prefix) { queue_string(d, d->output_prefix); queue_write(d, "\r\n", 2); } } if ((!check_access(d->player, cp->perm)) || ((cp->perm & CA_PLAYER) && !(d->flags & DS_CONNECTED))) { queue_string(d, "Permission denied.\r\n"); } else { mudstate.debug_cmd = cp->name; switch (cp->flag & CMD_MASK) { case CMD_QUIT: shutdownsock(d, R_QUIT); mudstate.debug_cmd = cmdsave; return 0; case CMD_LOGOUT: shutdownsock(d, R_LOGOUT); break; case CMD_WHO: if (d->player || mudconf.allow_unloggedwho) { dump_users(d, arg, CMD_WHO); } else { queue_string(d, "This MUX does not allow WHO at the login screen.\r\nPlease login or create a character first.\r\n"); } break; case CMD_DOING: if (d->player || mudconf.allow_unloggedwho) { dump_users(d, arg, CMD_DOING); } else { queue_string(d, "This MUX does not allow DOING at the login screen.\r\nPlease login or create a character first.\r\n"); } break; case CMD_SESSION: if (d->player || mudconf.allow_unloggedwho) { dump_users(d, arg, CMD_SESSION); } else { queue_string(d, "This MUX does not allow SESSION at the login screen.\r\nPlease login or create a character first.\r\n"); } break; case CMD_PREFIX: set_userstring(&d->output_prefix, arg); break; case CMD_SUFFIX: set_userstring(&d->output_suffix, arg); break; case CMD_PUEBLOCLIENT: /* Set the descriptor's flag */ d->flags |= DS_PUEBLOCLIENT; /* If we're already connected, set the player's flag */ if (d->player) { s_Html(d->player); } queue_string(d, mudconf.pueblo_msg); queue_string(d, "\r\n"); break; default: STARTLOG(LOG_BUGS, "BUG", "PARSE") { arg = alloc_lbuf("do_command.LOG"); sprintf(arg, "Prefix command with no handler: '%s'", command); log_text(arg); free_lbuf(arg); ENDLOG; } } } if (!(cp->flag & CMD_NOxFIX)) { if (d->output_prefix) { queue_string(d, d->output_suffix); queue_write(d, "\r\n", 2); } } mudstate.debug_cmd = cmdsave; return 1; } void logged_out(dbref player, dbref cause, int key, char *arg) { DESC *d; int idletime; if (player != cause) return; DESC_ITER_PLAYER(player, d) { idletime = (mudstate.now - d->last_time); switch (key) { case CMD_QUIT: if (idletime == 0) { shutdownsock(d, R_QUIT); return; } break; case CMD_LOGOUT: if (idletime == 0) { shutdownsock(d, R_LOGOUT); return; } break; case CMD_WHO: if (d->player || mudconf.allow_unloggedwho) { if (idletime == 0) { dump_users(d, arg, CMD_WHO); return; } } else { queue_string(d, "This MUX does not allow WHO at the login screen.\r\nPlease login or create a character first.\r\n"); } break; case CMD_DOING: if (d->player || mudconf.allow_unloggedwho) { if (idletime == 0) { dump_users(d, arg, CMD_DOING); return; } } else { queue_string(d, "This MUX does not allow DOING at the login screen.\r\nPlease login or create a character first.\r\n"); } break; case CMD_SESSION: if (d->player || mudconf.allow_unloggedwho) { if (idletime == 0) { dump_users(d, arg, CMD_SESSION); return; } } else { queue_string(d, "This MUX does not allow SESSION at the login screen.\r\nPlease login or create a character first.\r\n"); } break; case CMD_PREFIX: if (idletime == 0) { set_userstring(&d->output_prefix, arg); return; } break; case CMD_SUFFIX: if (idletime == 0) { set_userstring(&d->output_suffix, arg); return; } break; case CMD_PUEBLOCLIENT: /* Set the descriptor's flag */ d->flags |= DS_PUEBLOCLIENT; /* If we're already connected, set the player's flag */ if (d->player) { s_Html(d->player); } queue_string(d, mudconf.pueblo_msg); queue_string(d, "\r\n"); break; } } } void process_commands(void) { int nprocessed; DESC *d, *dnext; CBLK *t; char *cmdsave; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *) "process_commands"; do { nprocessed = 0; DESC_SAFEITER_ALL(d, dnext) { if (d->quota > 0 && (t = d->input_head)) { if (d->player <= 0 || !Staff(d->player)) d->quota--; nprocessed++; d->input_head = (CBLK *) t->hdr.nxt; if (!d->input_head) d->input_tail = NULL; d->input_size -= (strlen(t->cmd) + 1); if (d->program_data != NULL) handle_prog(d, t->cmd); else do_command(d, t->cmd, 1); free_lbuf(t); } } } while (nprocessed > 0); mudstate.debug_cmd = cmdsave; } /* * --------------------------------------------------------------------------- * * site_check: Check for site flags in a site list. */ int site_check(struct in_addr host, SITE *site_list) { SITE *this; for (this = site_list; this; this = this->next) { if ((host.s_addr & this->mask.s_addr) == this->address.s_addr) return this->flag; } return 0; } /* * -------------------------------------------------------------------------- * * list_sites: Display information in a site list */ #define S_SUSPECT 1 #define S_ACCESS 2 static const char *stat_string(int strtype, int flag) { const char *str; switch (strtype) { case S_SUSPECT: if (flag) str = "Suspected"; else str = "Trusted"; break; case S_ACCESS: switch (flag) { case H_FORBIDDEN: str = "Forbidden"; break; case H_REGISTRATION: str = "Registration"; break; case 0: str = "Unrestricted"; break; default: str = "Strange"; } break; default: str = "Strange"; } return str; } static void list_sites(dbref player, SITE *site_list, const char *header_txt, int stat_type) { char *buff, *buff1, *str; SITE *this; buff = alloc_mbuf("list_sites.buff"); buff1 = alloc_sbuf("list_sites.addr"); sprintf(buff, "----- %s -----", header_txt); notify(player, buff); notify(player, "Address Mask Status"); for (this = site_list; this; this = this->next) { str = (char *) stat_string(stat_type, this->flag); StringCopy(buff1, inet_ntoa(this->mask)); sprintf(buff, "%-20s %-20s %s", inet_ntoa(this->address), buff1, str); notify(player, buff); } free_mbuf(buff); free_sbuf(buff1); } /* * --------------------------------------------------------------------------- * * list_siteinfo: List information about specially-marked sites. */ void list_siteinfo(dbref player) { list_sites(player, mudstate.access_list, "Site Access", S_ACCESS); list_sites(player, mudstate.suspect_list, "Suspected Sites", S_SUSPECT); } /* * --------------------------------------------------------------------------- * * make_ulist: Make a list of connected user numbers for the LWHO function. */ void make_ulist(dbref player, char *buff, char **bufc) { DESC *d; char *cp; cp = *bufc; DESC_ITER_CONN(d) { if (!WizRoy(player) && Hidden(d->player)) continue; if (cp != *bufc) safe_chr(' ', buff, bufc); safe_chr('#', buff, bufc); safe_str(tprintf("%d", d->player), buff, bufc); } } /* * --------------------------------------------------------------------------- * * find_connected_name: Resolve a playername from the list of connected * * players using prefix matching. We only return a match if the prefix * * was unique. */ dbref find_connected_name(dbref player, char *name) { DESC *d; dbref found; found = NOTHING; DESC_ITER_CONN(d) { if (Good_obj(player) && !Wizard(player) && Hidden(d->player)) continue; if (!string_prefix(Name(d->player), name)) continue; if ((found != NOTHING) && (found != d->player)) return NOTHING; found = d->player; } return found; }