/* netcommon.c - Network utility routines */ /* 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 "autoconf.h" #include "copyright.h" #ifndef lint static char *RCSid = "$Id: netcommon.c,v 1.13 1995/03/29 23:42:55 ambar Exp $"; USE(RCSid); #endif #include "interface.h" #include "file_c.h" #include "command.h" #include "rwho_clilib.h" #include "attrs.h" /* Logged out command table definitions */ #define CMD_QUIT 1 #define CMD_WHO 2 #define CMD_DOING 3 #define CMD_RWHO 4 #define CMD_PREFIX 5 #define CMD_SUFFIX 6 #define CMD_LOGOUT 7 #define CMD_SESSION 8 #define CMD_PUEBLOCLIENT 9 #define CMD_MASK 0xff #define CMD_NOxFIX 0x100 extern int FDECL(process_output, (DESC * d)); #ifdef LOCAL_RWHO_SERVER void FDECL(dump_rusers, (DESC * call_by)); #endif /* --------------------------------------------------------------------------- * 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(now, then) struct timeval now, 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(t, x) 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(last, current) struct timeval last, current; { int nslices; DESC *d; nslices = msec_diff(current, last) / mudconf.timeslice; 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: write a message to a player */ #ifdef PUEBLO_SUPPORT void raw_notify_html(player, msg) dbref player; const char *msg; { DESC *d; if (!msg || !*msg) return; if (!Connected(player)) return; DESC_ITER_PLAYER(player, d) { queue_string(d, msg); } } #endif /* PUEBLO_SUPPORT */ /* --------------------------------------------------------------------------- * raw_notify: write a message to a player */ void raw_notify(player, msg) dbref player; const char *msg; { DESC *d; if (!msg || !*msg) return; if (!Connected(player)) return; DESC_ITER_PLAYER(player, d) { queue_string(d, msg); queue_write(d, "\r\n", 2); } } /* --------------------------------------------------------------------------- * raw_broadcast: Send message to players who have indicated flags */ #ifdef NEED_VSPRINTF_DCL extern char *FDECL(vsprintf, (char *, char *, va_list)); #endif #if defined(__STDC__) && defined(STDC_HEADERS) void raw_broadcast(int inflags, char *template,...) #else void raw_broadcast(va_alist) va_dcl #endif { char *buff; DESC *d; va_list ap; #if defined(__STDC__) && defined(STDC_HEADERS) va_start(ap, template); #else int inflags; char *template; va_start(ap); inflags = va_arg(ap, int); template = va_arg(ap, char *); #endif 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); } } free_lbuf(buff); va_end(ap); } /* --------------------------------------------------------------------------- * clearstrings: clear out prefix and suffix strings */ void clearstrings(d) 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(d, b, n) DESC *d; const char *b; int n; { int left; char *buf; TBLOCK *tp; if (n <= 0) return; if (d->output_size + n > mudconf.output_limit) process_output(d); left = mudconf.output_limit - d->output_size - n; if (left < 0) { tp = d->output_head; if (tp == NULL) { STARTLOG(LOG_PROBLEMS, "QUE", "WRITE") log_text((char *) "Flushing when output_head is null!"); ENDLOG } else { STARTLOG(LOG_NET, "NET", "WRITE") buf = alloc_lbuf("queue_write.LOG"); sprintf(buf, "[%d/%s] Output buffer overflow, %d chars discarded by ", d->descriptor, d->addr, tp->hdr.nchars); log_text(buf); free_lbuf(buf); log_name(d->player); ENDLOG d->output_size -= tp->hdr.nchars; d->output_head = tp->hdr.nxt; d->output_lost += tp->hdr.nchars; if (d->output_head == NULL) d->output_tail = NULL; free_lbuf(tp); } } /* Allocate an output buffer if needed */ if (d->output_head == NULL) { tp = (TBLOCK *) alloc_lbuf("queue_write.new"); tp->hdr.nxt = NULL; tp->hdr.start = tp->data; tp->hdr.end = tp->data; tp->hdr.nchars = 0; d->output_head = tp; d->output_tail = tp; } else { tp = d->output_tail; } /* Now tp points to the last buffer in the chain */ d->output_size += n; d->output_tot += n; do { /* See if there is enough space in the buffer to hold the * string. If so, copy it and update the pointers.. */ left = LBUF_SIZE - (tp->hdr.end - (char *) tp + 1); if (n <= left) { strncpy(tp->hdr.end, b, n); tp->hdr.end += n; tp->hdr.nchars += n; n = 0; } else { /* It didn't fit. Copy what will fit and then allocate * another buffer and retry. */ if (left > 0) { strncpy(tp->hdr.end, b, left); tp->hdr.end += left; tp->hdr.nchars += left; b += left; n -= left; } tp = (TBLOCK *) alloc_lbuf("queue_write.extend"); tp->hdr.nxt = NULL; tp->hdr.start = tp->data; tp->hdr.end = tp->data; tp->hdr.nchars = 0; d->output_tail->hdr.nxt = tp; d->output_tail = tp; } } while (n > 0); } void queue_string(d, s) DESC *d; const char *s; { if (s) queue_write(d, s, strlen(s)); } void freeqs(d) DESC *d; { TBLOCK *tb, *tnext; CBLK *cb, *cnext; tb = d->output_head; while (tb) { tnext = tb->hdr.nxt; free_lbuf(tb); tb = tnext; } d->output_head = NULL; d->output_tail = NULL; 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; } /* --------------------------------------------------------------------------- * desc_addhash: Add a net descriptor to its player hash list. */ static void desc_addhash(d) DESC *d; { dbref player; DESC *hdesc; player = d->player; hdesc = (DESC *) nhashfind((int) player, &mudstate.desc_htab); if (hdesc == NULL) { d->hashnext = NULL; nhashadd((int) player, (int *) d, &mudstate.desc_htab); } else { d->hashnext = hdesc; nhashrepl((int) player, (int *) d, &mudstate.desc_htab); } } /* --------------------------------------------------------------------------- * desc_delhash: Remove a net descriptor from its player hash list. */ static void desc_delhash(d) DESC *d; { DESC *hdesc, *last; dbref player; player = d->player; last = NULL; hdesc = (DESC *) nhashfind((int) player, &mudstate.desc_htab); while (hdesc != NULL) { if (d == hdesc) { if (last == NULL) { if (d->hashnext == NULL) { nhashdelete((int) player, &mudstate.desc_htab); } else { nhashrepl((int) player, (int *) d->hashnext, &mudstate.desc_htab); } } else { last->hashnext = d->hashnext; } break; } last = hdesc; hdesc = hdesc->hashnext; } d->hashnext = NULL; } void welcome_user(d) DESC *d; { #ifdef PUEBLO_SUPPORT queue_string(d, PUEBLO_SUPPORT_MSG); #endif /* PUEBLO_SUPPORT */ if (d->host_info & H_REGISTRATION) fcache_dump(d, FC_CONN_REG); else fcache_dump(d, FC_CONN); } void save_command(d, 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(userstring, command) 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"); strcpy(*userstring, command); } } static void parse_connect(msg, command, user, pass) const char *msg; char *command, *user, *pass; { char *p, *buff, *bufp, *cp; while (*msg && isascii(*msg) && isspace(*msg)) msg++; /* skip leading spaces */ p = bufp = buff = strsave(msg); /* give us a copy we can mess with */ if (!buff) { strcpy(command, "malloc failed"); strcpy(user, "malloc failed"); strcpy(pass, "malloc failed"); return; } cp = command; while (*bufp && isascii(*bufp) && !isspace(*bufp)) bufp++; if (*bufp) *bufp++ = '\0'; /* set the null and skip it */ safe_mb_str(buff, command, &cp); while (*bufp && isascii(*bufp) && isspace(*bufp)) /* skip spaces */ bufp++; cp = user; p = bufp; if (mudconf.name_spaces && (*bufp == '\"')) { for (; *bufp && (*bufp == '\"' || isspace(*bufp)); bufp++); while (*bufp && *bufp != '\"') { while (*bufp && !isspace(*bufp) && (*bufp != '\"')) { safe_mb_chr(*bufp, user, &cp); bufp++; } if (*bufp == '\"') break; while (*bufp && isspace(*bufp)) bufp++; if (*bufp && (*bufp != '\"')) { safe_mb_chr(' ', user, &cp); } } for (; *bufp && *bufp == '\"'; bufp++) ; if (*bufp) *bufp++ = '\0'; } else { while (*bufp && isascii(*bufp) && !isspace(*bufp)) bufp++; if (*bufp) *bufp++ = '\0'; safe_mb_str(p, user, &cp); } while (*bufp && isascii(*bufp) && isspace(*bufp)) bufp++; cp = pass; p = bufp; while (*bufp && isascii(*bufp) && !isspace(*bufp)) bufp++; if (*bufp) *bufp++ = '\0'; safe_mb_str(p, pass, &cp); XFREE(buff, "parse_connect"); } static const char * time_format_1(dt) 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(dt) 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(player, d) dbref player; DESC *d; { dbref loc, aowner, temp; int aflags, num, key; char *buf, *time_str; DESC *dtemp; desc_addhash(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); loc = Location(player); s_Connected(player); #ifdef PUEBLO_SUPPORT if (d->flags & DS_PUEBLOCLIENT) { s_Html(player); } #endif raw_notify(player, mudconf.motd_msg); if (Wizard(player)) { raw_notify(player, 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."); } #ifdef RWHO_IN_USE if (mudconf.rwho_transmit && (mudconf.control_flags & CF_RWHO_XMIT)) { sprintf(buf, "%d@%s", player, mudconf.mud_name); rwhocli_userlogin(buf, Name(player), time((time_t *) 0)); } #endif num = 0; DESC_ITER_PLAYER(player, dtemp) num++; if (num < 2) sprintf(buf, "%s has connected.", Name(player)); else sprintf(buf, "%s has reconnected.", Name(player)); 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_check(player, player, buf, key); free_lbuf(buf); if (Suspect(player)) { raw_broadcast(WIZARD, (char *) "[Suspect] %s has connected.", (char *) Name(player), 0, 0, 0, 0, 0); } if (d->host_info & H_SUSPECT) { raw_broadcast(WIZARD, (char *) "[Suspect site: %s] %s has connected.", (char *) (d->addr), (char *) Name(player), 0, 0, 0, 0); } buf = atr_pget(player, A_ACONNECT, &aowner, &aflags); if (buf && *buf) wait_que(player, player, 0, NOTHING, buf, (char **) NULL, 0, NULL); free_lbuf(buf); if (mudconf.use_global_aconn) { DOLIST(temp, Contents(mudconf.master_room)) { if (could_doit(player, temp, A_LUSE)) { /* respect uselock */ buf = atr_get(temp, A_ACONNECT, &aowner, &aflags); if (buf && *buf) wait_que(temp, player, 0, NOTHING, buf, (char **) NULL, 0, NULL); free_lbuf(buf); } } } time_str = ctime(&mudstate.now); time_str[strlen(time_str) - 1] = '\0'; record_login(player, 1, time_str, d->addr); #ifdef PUEBLO_SUPPORT look_in(player, Location(player), (LK_SHOWEXIT | LK_OBEYTERSE | LK_SHOWVRML)); #else look_in(player, Location(player), (LK_SHOWEXIT | LK_OBEYTERSE )); #endif /* PUEBLO_SUPPORT */ mudstate.curr_enactor = temp; } void announce_disconnect(player, d, reason) dbref player; DESC *d; const char *reason; { dbref loc, aowner, temp; int num, aflags, key; char *buf, *atr_temp; DESC *dtemp; char *argv[1]; if (Suspect(player)) { raw_broadcast(WIZARD, (char *) "[Suspect] %s has disconnected.", (char *) Name(player), 0, 0, 0, 0, 0); } if (d->host_info & H_SUSPECT) { raw_broadcast(WIZARD, (char *) "[Suspect site: %s] %s has disconnected.", (char *) d->addr, (char *) Name(d->player), 0, 0, 0, 0); } 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"); #ifdef RWHO_IN_USE if (mudconf.rwho_transmit && (mudconf.control_flags & CF_RWHO_XMIT)) { sprintf(buf, "%d@%s", player, mudconf.mud_name); rwhocli_userlogout(buf); } #endif 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_check(player, player, buf, key); free_mbuf(buf); argv[0] = (char *) reason; c_Connected(player); #ifdef PUEBLO_SUPPORT c_Html(player); #endif /* PUEBLO_SUPPORT */ atr_temp = atr_pget(player, A_ADISCONNECT, &aowner, &aflags); if (atr_temp && *atr_temp) wait_que(player, player, 0, NOTHING, atr_temp, argv, 1, NULL); free_lbuf(atr_temp); if (mudconf.use_global_aconn) { DOLIST(temp, Contents(mudconf.master_room)) { if (could_doit(player, temp, A_LUSE)) { /* respect uselock */ atr_temp = atr_get(temp, A_ADISCONNECT, &aowner, &aflags); if (atr_temp && *atr_temp) wait_que(temp, player, 0, NOTHING, atr_temp, argv, 1, NULL); free_lbuf(atr_temp); } } } if (d->flags & DS_AUTODARK) { s_Flags(d->player, Flags(d->player) & ~DARK); d->flags &= ~DS_AUTODARK; } } 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_check(player, player, buf, key); free_mbuf(buf); } mudstate.curr_enactor = temp; desc_delhash(d); } int boot_off(player, message) 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(port, no_god, message) int port, 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(player) 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(target) 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(target) 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 NDECL(check_idle) { 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) { queue_string(d, "*** Inactivity Timeout ***\r\n"); shutdownsock(d, R_TIMEOUT); } else if (mudconf.idle_wiz_dark && (idletime > mudconf.idle_timeout) && Wizard(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); } } } } static char * trimmed_name(player) dbref player; { static char cbuff[18]; if (strlen(Name(player)) <= 16) return Name(player); strncpy(cbuff, Name(player), 16); cbuff[16] = '\0'; return cbuff; } static void dump_users(e, match, key) DESC *e; char *match; int key; { DESC *d; int count; char *buf, *fp, *sp, flist[4], slist[4]; dbref room_it; while (match && *match && isspace(*match)) match++; if (!match || !*match) match = NULL; #ifdef PUEBLO_SUPPORT if (e->flags & DS_PUEBLOCLIENT) queue_string(e, "<pre>"); #endif /* PUEBLO_SUPPORT */ 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(e->player) && (key == CMD_WHO)) { queue_string(e, " Room Cmds Host\r\n"); } else { if (Wizard(e->player)) queue_string(e, " "); else queue_string(e, " "); queue_string(e, mudstate.doing_hdr); queue_string(e, "\r\n"); } count = 0; DESC_ITER_CONN(d) { if (!Hidden(d->player) || ((e->flags & DS_CONNECTED) && Wizard(e->player))) { count++; if (match && !(string_prefix(Name(d->player), match))) continue; if ((key == CMD_SESSION) && !(Wizard(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(e->player)) { if (Hidden(d->player)) { if (d->flags & DS_AUTODARK) *fp++ = 'd'; else *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++ = '+'; if (d->host_info & H_GUEST) *sp++ = 'G'; } *fp = '\0'; *sp = '\0'; if ((e->flags & DS_CONNECTED) && Wizard(e->player) && (key == CMD_WHO)) { sprintf(buf, "%-16s%9s %4s%-3s#%-6d%5d%3s%s\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, 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), d->descriptor, d->input_size, d->input_lost, d->input_tot, d->output_size, d->output_lost, d->output_tot); } else if (Wizard(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), 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), d->doing); } queue_string(e, buf); } } /* sometimes I like the ternary operator.... */ sprintf(buf, "%d Player%slogged in.\r\n", count, (count == 1) ? " " : "s "); queue_string(e, buf); #ifdef PUEBLO_SUPPORT if (e->flags & DS_PUEBLOCLIENT) queue_string(e, "</pre>"); #endif free_mbuf(buf); } /* --------------------------------------------------------------------------- * do_mudwho: Special WHO for Howard's intermud paging/who server. * Original code by Howard. */ void do_mudwho(player, cause, key, name, mud) dbref player, cause; int key; char *name, *mud; { DESC *d; int players; players = 0; notify(player, tprintf("@inwho %s@%s=Player On For Idle %s", name, mud, mudstate.doing_hdr)); DESC_ITER_CONN(d) { if (!Hidden(d->player)) { notify(player, tprintf("@inwho %s@%s=%-16.16s %10s %4s %s", name, mud, trimmed_name(d->player), time_format_1(mudstate.now - d->connected_at), time_format_2(mudstate.now - d->last_time), d->doing ? d->doing : "")); players++; } } notify(player, tprintf("@inwho %s@%s=%d player%s %s connected.", name, mud, players, (players == 1) ? "" : "s", (players == 1) ? "is" : "are")); } /* --------------------------------------------------------------------------- * do_doing: Set the doing string that appears in the WHO report. * Idea from R'nice@TinyTIM. */ void do_doing(player, cause, key, arg) dbref player, cause; int key; char *arg; { DESC *d; char *c, *p; int foundany, over; if (key == DOING_MESSAGE) { foundany = 0; over = 0; for (p = arg; *p; p++) if ((*p == '\t') || (*p == '\r') || (*p == '\n')) *p = ' '; DESC_ITER_PLAYER(player, d) { c = d->doing; over = safe_copy_str(arg, d->doing, &c, DOING_LEN-1); foundany = 1; } if (foundany) { if (over) { notify(player, tprintf("Warning: %d characters lost.", over)); } if (!Quiet(player)) notify(player, "Set."); } else { notify(player, "Not connected."); } } else if (key == DOING_HEADER) { if (!arg || !*arg) { strcpy(mudstate.doing_hdr, "Doing"); over = 0; } else { c = mudstate.doing_hdr; over = safe_copy_str(arg, mudstate.doing_hdr, &c, DOING_LEN-1); } if (over) { notify(player, tprintf("Warning: %d characters lost.", over + 1)); } if (!Quiet(player)) notify(player, "Set."); } else { notify(player, tprintf("Poll: %s", mudstate.doing_hdr)); } } 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}, #ifdef LOCAL_RWHO_SERVER {(char *) "RWHO", 4, CA_PUBLIC, CMD_RWHO}, #endif {(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}}; void NDECL(init_logout_cmdtab) { 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, 19); for (cp = logout_cmdtable; cp->flag; cp++) hashadd(cp->name, (int *) cp, &mudstate.logout_cmd_htab); } static void failconn(logcode, logtype, logreason, d, disconnect_reason, player, filecache, motd_msg, command, user, password, cmdsave) const char *logcode, *logtype, *logreason; char *motd_msg, *command, *user, *password, *cmdsave; DESC *d; int disconnect_reason, filecache; dbref player; { 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_mbuf(command); free_mbuf(user); free_mbuf(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(d, msg) DESC *d; const char *msg; { char *command, *user, *password, *buff, *cmdsave; dbref player, aowner; int aflags, nplayers; DESC *d2; 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_mbuf("check_conn.cmd"); user = alloc_mbuf("check_conn.user"); password = alloc_mbuf("check_conn.pass"); parse_connect(msg, command, user, password); if (!strncmp(command, "co", 2)) { /* 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); if (player == NOTHING) { /* Not a player, or wrong password */ queue_string(d, connect_fail); STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD") buff = alloc_mbuf("check_conn.LOG.bad"); sprintf(buff, "[%d/%s] Failed connect to '", d->descriptor, d->addr); log_text(buff); log_text(user); log_text("'"); free_mbuf(buff); ENDLOG if (--(d->retries_left) <= 0) { free_mbuf(command); free_mbuf(user); free_mbuf(password); shutdownsock(d, R_BADLOGIN); mudstate.debug_cmd = cmdsave; return 0; } } else if (((mudconf.control_flags & CF_LOGIN) && (nplayers < mudconf.max_players)) || Wizard(player) || God(player)) { /* First make sure we don't have a guest from a bad host. */ if (Guest(player) && (d->host_info & H_GUEST)) { failconn("CON", "Connect", "Guest Site Forbidden", d, R_GAMEDOWN, player, FC_CONN_SITE, mudconf.downmotd_msg, command, user, password, cmdsave); return 0; } /* 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; /* 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); } 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); 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 '", d->descriptor, d->addr); log_text(buff); log_text(user); log_text("' failed"); 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; fcache_dump(d, FC_CREA_NEW); announce_connect(player, d); } } } else { welcome_user(d); } free_mbuf(command); free_mbuf(user); free_mbuf(password); mudstate.debug_cmd = cmdsave; return 1; } static int do_command(d, command) DESC *d; char *command; { char *arg, *cmdsave, *logbuf, *log_cmdbuf; NAMETAB *cp; long begin_time, used_time; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *) "< do_command >"; /* Split off the command from the arguments */ arg = command; while (*arg && !isspace(*arg)) arg++; if (*arg) *arg++ = '\0'; /* Look up the command. If we don't find it, turn it over to the * normal logged-in command processor or to create/connect */ cp = (NAMETAB *) hashfind(command, &mudstate.logout_cmd_htab); if (cp == NULL) { if (*arg) *--arg = ' '; /* restore nullified space */ if (d->flags & DS_CONNECTED) { d->command_count++; 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; #ifndef NO_LAG_CHECK begin_time = time(NULL); #endif /* NO_LAG_CHECK */ log_cmdbuf = process_command(d->player, d->player, 1, command, (char **) NULL, 0); #ifndef NO_LAG_CHECK used_time = time(NULL) - begin_time; if (used_time >= mudconf.max_cmdsecs) { STARTLOG(LOG_PROBLEMS, "CMD", "CPU") log_name_and_loc(d->player); logbuf = alloc_lbuf("do_command.LOG.cpu"); sprintf(logbuf, " entered command taking %d secs: ", used_time); log_text(logbuf); free_lbuf(logbuf); log_text(log_cmdbuf); ENDLOG } #endif /* NO_LAG_CHECK */ mudstate.curr_cmd = (char *) ""; if (d->output_suffix) { queue_string(d, d->output_suffix); queue_write(d, "\r\n", 2); } 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: dump_users(d, arg, CMD_WHO); break; case CMD_DOING: dump_users(d, arg, CMD_DOING); break; case CMD_SESSION: dump_users(d, arg, CMD_SESSION); break; #ifdef LOCAL_RWHO_SERVER case CMD_RWHO: dump_rusers(d); break; #endif case CMD_PREFIX: set_userstring(&d->output_prefix, arg); break; case CMD_SUFFIX: set_userstring(&d->output_suffix, arg); break; case CMD_PUEBLOCLIENT: #ifdef PUEBLO_SUPPORT /* Set the descriptor's flag */ d->flags |= DS_PUEBLOCLIENT; /* If connected, set the player's flag */ if (d->player) { s_Html(d->player); } queue_string(d, mudconf.pueblo_msg); queue_string(d, "\r\n"); fcache_dump(d, FC_CONN_HTML); STARTLOG(LOG_LOGIN, "CON", "HTML") arg = alloc_mbuf("do_command.LOG.con_html"); sprintf(arg, "[%d/%s] PuebloClient enabled.", d->descriptor, d->addr); log_text(arg); free_mbuf(arg); ENDLOG #else queue_string(d, "Sorry. This MUSH does not have Pueblo support enabled.\r\n"); #endif /* PUEBLO_SUPPORT */ 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 NDECL(process_commands) { 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)) { 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); do_command(d, t->cmd); free_lbuf(t); } } } while (nprocessed > 0); mudstate.debug_cmd = cmdsave; } /* --------------------------------------------------------------------------- * site_check: Check for site flags in a site list. */ int site_check(host, site_list) 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(strtype, flag) int strtype, 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 H_GUEST: str = "NoGuest"; break; case 0: str = "Unrestricted"; break; default: str = "Strange"; } break; default: str = "Strange"; } return str; } static void list_sites(player, site_list, header_txt, stat_type) 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); strcpy(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(player) 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(player, buff) dbref player; char *buff; { char *cp, nbuf[20]; DESC *d; cp = buff; DESC_ITER_CONN(d) { if (!Wizard(player) && Hidden(d->player)) continue; if (cp != buff) { safe_chr(' ', buff, &cp); } safe_chr('#', buff, &cp); ltos(nbuf, d->player); safe_str(nbuf, buff, &cp); } *cp = '\0'; } /* --------------------------------------------------------------------------- * make_portlist: Make a list of ports for PORTS(). */ void make_portlist(player, target, buff) dbref player; dbref target; char *buff; { DESC *d; *buff = '\0'; DESC_ITER_CONN(d) { if (d->player == target) { if (*buff) sprintf(buff, "%s %d", buff, d->descriptor); else ltos(buff, d->descriptor); } } } /* --------------------------------------------------------------------------- * 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(player, 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; } /* --------------------------------------------------------------------------- * rwho_update: Send RWHO info to the remote RWHO server. */ #ifdef RWHO_IN_USE void NDECL(rwho_update) { DESC *d; char *buf; if (!(mudconf.rwho_transmit && (mudconf.control_flags & CF_RWHO_XMIT))) return; buf = alloc_mbuf("rwho_update"); rwhocli_pingalive(); DESC_ITER_ALL(d) { if ((d->flags & DS_CONNECTED) && !Hidden(d->player)) { sprintf(buf, "%d@%s", d->player, mudconf.mud_name); rwhocli_userlogin(buf, Name(d->player), d->connected_at); } } free_mbuf(buf); } #endif