/* 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 "copyright.h" #include <varargs.h> #include <stdio.h> #include <string.h> #ifdef WANT_ANSI #ifdef __STDC__ #include <stdlib.h> #include <unistd.h> #endif #endif #include "db.h" #include "mudconf.h" #include "interface.h" #include "command.h" #include "externs.h" #include "rwho_clilib.h" /* --------------------------------------------------------------------------- * timeval_sub: return difference between two times as a timeval */ 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; } /* --------------------------------------------------------------------------- * 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; 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 */ void raw_notify(dbref player, const char *msg) { DESC *d; if (!msg || !*msg) return; if (!(Flags(player) & PLAYER_CONNECT)) 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 NEVER void raw_broadcast (int inflags, char *template, int a1, int a2, int a3, int a4, int a5, int a6) { char *buff; DESC *d; if (!template || !*template) return; buff=alloc_lbuf("raw_broadcast"); sprintf(buff, template, a1, a2, a3, a4, a5, a6); 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); } #else void raw_broadcast (va_alist) va_dcl { char *buff; DESC *d; int inflags; char *template; va_list ap; va_start(ap); inflags = va_arg(ap, int); template = va_arg(ap, char *); 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); } #endif /* --------------------------------------------------------------------------- * 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, 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); 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(DESC *d, const char *s) { if (s) queue_write(d, s, strlen(s)); } void freeqs(DESC *d) { TBLOCK *tb, *tnext; CBLOCK *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 = (CBLOCK *)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 (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 (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(DESC *d) { if (d->host_info & H_REGISTRATION) fcache_dump(d, mudstate.creg_fcache); else fcache_dump(d, mudstate.conn_fcache); } int fcache_read (FBLOCK **cp, char *filename) { int n, nmax, fd, tchars; char *bufp; FBLOCK *fp, *tfp; /* Free a prior buffer chain */ fp = *cp; while (fp != NULL) { tfp = fp->hdr.nxt; free_mbuf(fp); fp = tfp; } *cp = NULL; /* Read the text file into a new chain */ close(mudstate.reserved_fileid); if ((fd = open(filename, O_RDONLY, 0)) == -1) { /* Failure: log the event */ STARTLOG(LOG_PROBLEMS,"FIL","OPEN") bufp = alloc_mbuf("fcache_read.LOG"); sprintf(bufp, "Couldn't open file '%s'.", filename); log_text(bufp); free_mbuf(bufp); ENDLOG mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); return -1; } /* Success. Allocate and initialize the first buffer */ fp = (FBLOCK *)alloc_mbuf("fcache_read.first"); fp->hdr.nxt = NULL; fp->hdr.nchars = 0; *cp = fp; tfp = NULL; tchars = 0; /* Read in the first chunk of the file */ nmax = MBUF_SIZE - sizeof(FBLKHDR); bufp = fp->data; n = read(fd, bufp, nmax); #ifdef VMS if ((n < nmax) && (n > 0)) { bufp[n] = bufp[n-1]; bufp[n-1] = '\r'; bufp[++n] = 0; } #endif VMS while (n > 0) { /* If we didn't read in all we wanted, update the pointers and * try to fill the current buffer */ fp->hdr.nchars += n; tchars += n; if (fp->hdr.nchars < (MBUF_SIZE - sizeof(FBLKHDR))) { nmax -= n; bufp += n; } else { /* We filled the current buffer. Go get a new one. */ tfp = fp; fp = (FBLOCK *)alloc_mbuf("fcache_read.next"); fp->hdr.nxt = NULL; fp->hdr.nchars = 0; tfp->hdr.nxt = fp; nmax = MBUF_SIZE - sizeof(FBLKHDR); bufp = fp->data; } /* Read in the next chunk of the file */ n = read(fd, bufp, nmax); #ifdef VMS if ((n < nmax) && (n > 0)) { bufp[n] = bufp[n-1]; bufp[n-1] = '\r'; bufp[++n] = 0; } #endif VMS } close(fd); mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); if (fp->hdr.nchars == 0) { free_mbuf(fp); if (tfp == NULL) *cp = NULL; else tfp->hdr.nxt = NULL; } return tchars; } void fcache_dump (DESC *d, FBLOCK *fp) { while(fp != NULL) { queue_write(d, fp->data, fp->hdr.nchars); fp = fp->hdr.nxt; } } void fcache_send (dbref player, FBLOCK *fp) { DESC *d; DESC_ITER_PLAYER(player, d) { fcache_dump(d, fp); } } void fcache_load (dbref player) { int guest, conn, creg, regf, motd, wmotd, quit, down, site, crea, full; char *buf; guest = fcache_read(&mudstate.guest_fcache, mudconf.guest_file); conn = fcache_read(&mudstate.conn_fcache, mudconf.conn_file); creg = fcache_read(&mudstate.creg_fcache, mudconf.creg_file); regf = fcache_read(&mudstate.regf_fcache, mudconf.regf_file); motd = fcache_read(&mudstate.motd_fcache, mudconf.motd_file); wmotd = fcache_read(&mudstate.wizmotd_fcache, mudconf.wizmotd_file); quit = fcache_read(&mudstate.quit_fcache, mudconf.quit_file); down = fcache_read(&mudstate.down_fcache, mudconf.down_file); site = fcache_read(&mudstate.site_fcache, mudconf.site_file); crea = fcache_read(&mudstate.crea_fcache, mudconf.crea_file); full = fcache_read(&mudstate.full_fcache, mudconf.full_file); if ((player != NOTHING) && !Quiet(player)) { buf = alloc_lbuf("fcache_load"); sprintf(buf, "File sizes: Guest...%d Connect...%d Conn/Reg...%d Crea/Reg...%d Motd...%d Wizmotd...%d Quit...%d Down...%d Full...%d Conn/Badsite...%d Newuser...%d", guest, conn, creg, regf, motd, wmotd, quit, down, full, site, crea); notify(player, buf); free_lbuf(buf); } } void fcache_init () { mudstate.guest_fcache = NULL; mudstate.conn_fcache = NULL; mudstate.creg_fcache = NULL; mudstate.regf_fcache = NULL; mudstate.motd_fcache = NULL; mudstate.wizmotd_fcache = NULL; mudstate.quit_fcache = NULL; mudstate.down_fcache = NULL; mudstate.site_fcache = NULL; mudstate.crea_fcache = NULL; mudstate.full_fcache = NULL; fcache_load(NOTHING); } void save_command(DESC *d, const char *command) { CBLOCK *cp; cp = (CBLOCK *)alloc_lbuf("save_command"); cp->hdr.nxt = NULL; strcpy(cp->cmd, command); if (d->input_tail == NULL) d->input_head = cp; else d->input_tail->hdr.nxt = cp; d->input_tail = cp; } 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"); strcpy(*userstring, command); } } static void parse_connect(const char *msg, char *command, char *user, char *pass) { char *p; 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; 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; int aflags, num; char *buf, *atr_temp, *time_str; time_t tt; DESC *dtemp; desc_addhash(d); if (atr_temp = atr_pget(player, A_TIMEOUT, &aowner, &aflags)) { d->timeout = atoi(atr_temp); if (d->timeout <= 0) d->timeout = mudconf.idle_timeout; } free_lbuf(atr_temp); loc = Location(player); s_Flags(player, Flags(player) | PLAYER_CONNECT); raw_notify(player, mudconf.motd_msg); if (Wizard(player)) raw_notify(player, mudconf.wizmotd_msg); buf=alloc_mbuf("announce_connect"); #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)); notify_except(player, player, player, buf, 1); /* Thanks, Micro. */ if ((loc != NOTHING) && !(Dark(player) && Wizard(player))) notify_except(loc, player, player, buf, 1); free_mbuf(buf); if (Suspect(player)) { raw_broadcast(WIZARD, (char *)"[Suspect] %s has connected.", (int)Name(player), 0, 0, 0, 0, 0); } if (d->host_info & H_SUSPECT) { raw_broadcast(WIZARD, (char *)"[Suspect site: %s] %s has connected.", (int)(d->addr), (int)Name(player), 0, 0, 0, 0); } if (atr_temp = atr_pget(player, A_ACONNECT, &aowner, &aflags)) wait_que(player, player, RU_ARG1_COPY, 0, NOTHING, atr_temp, (char **)NULL, 0); free_lbuf(atr_temp); time(&tt); time_str = ctime(&tt); time_str[strlen(time_str) - 1] = '\0'; record_login(player, 1, time_str, d->addr); look_in (player, Location(player), 1); } void announce_disconnect(dbref player, DESC *d, const char *reason) { dbref loc, aowner; int num, aflags; char *buf, *atr_temp; DESC *dtemp; char *argv[1]; if (Suspect(player)) { raw_broadcast(WIZARD, (char *)"[Suspect] %s has disconnected.", (int)Name(player), 0, 0, 0, 0, 0); } if (d->host_info & H_SUSPECT) { raw_broadcast(WIZARD, (char *)"[Suspect site: %s] %s has disconnected.", (int)d->addr, (int)Name(d->player), 0, 0, 0, 0); } loc = Location(player); num = 0; DESC_ITER_PLAYER(player, dtemp) num++; 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)); if ((loc != NOTHING) && !(Dark(player) && Wizard(player))) notify_except(loc, player, player, buf, 1); notify_except(player, player, player, buf, 1); free_mbuf(buf); argv[0] = (char *)reason; s_Flags(player, Flags(player) & ~PLAYER_CONNECT); atr_temp = atr_pget(player, A_ADISCONNECT, &aowner, &aflags); if (atr_temp && *atr_temp) wait_que(player, player, RU_ARG1_COPY, 0, NOTHING, atr_temp, argv, 1); 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)); if ((loc != NOTHING) && !(Dark(player) && Wizard(player))) notify_except(loc, player, player, buf, 1); notify_except(player, player, player, buf, 1); free_mbuf(buf); } 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; } void check_idle() { DESC *d, *dnext; time_t now, idletime; time(&now); DESC_SAFEITER_ALL(d,dnext) { if (d->flags & DS_CONNECTED) { idletime = 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 = now - d->connected_at; if (idletime > mudconf.conn_timeout) { queue_string(d, "*** Login Timeout ***\r\n"); shutdownsock(d, R_TIMEOUT); } } } } static void dump_users(DESC *e, char *match, int key) { DESC *d; int count; time_t now; char *buf, *fp, *sp, flist[4], slist[4]; while (match && *match && isspace(*match)) match++; if (!match || !*match) match = NULL; buf=alloc_mbuf("dump_users"); time(&now); 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 (!Dark(d->player) || Wizard(e->player)) { count++; if (match && !(string_prefix(Name(d->player), match))) continue; if ((key == CMD_SESSION) && !Wizard(e->player) && (d->player != e->player)) continue; /* Get choice flags for wizards */ fp = flist; sp = slist; if ((e->flags & DS_CONNECTED) && Wizard(e->player)) { if (Dark(d->player)) { if (d->flags & DS_AUTODARK) *fp++ = 'd'; else *fp++ = 'D'; } if (Unfindable(d->player)) *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(e->player) && (key == CMD_WHO)) { sprintf(buf, "%-16s%9s %4s%-3s#%-6d%5d%3s%s\r\n", Name(d->player), time_format_1(now - d->connected_at), time_format_2(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", Name(d->player), time_format_1(now - d->connected_at), time_format_2(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", Name(d->player), time_format_1(now - d->connected_at), time_format_2(now - d->last_time), flist, d->doing); } else { sprintf(buf, "%-16s%9s %4s %s\r\n", Name(d->player), time_format_1(now - d->connected_at), time_format_2(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); 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; 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, 40); *c = '\0'; 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, 40); *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)); } } 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}, {NULL, 0, 0, 0}}; void 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 (const char *logcode, const char *logtype, const char *logreason, DESC *d, int disconnect_reason, dbref player, FBLOCK *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_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(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 '%s'", d->descriptor, d->addr, user); log_text(buff); 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)) { /* 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, mudstate.guest_fcache); } else { buff = atr_get(player, A_LAST, &aowner, &aflags); if ((buff == NULL) || (*buff == '\0')) fcache_dump(d, mudstate.crea_fcache); else fcache_dump(d, mudstate.motd_fcache); if (Wizard(player)) fcache_dump(d, mudstate.wizmotd_fcache); free_lbuf(buff); } announce_connect(player, d); } else if (!(mudconf.control_flags & CF_LOGIN)) { failconn("CON", "Connect", "Logins Disabled", d, R_GAMEDOWN, player, mudstate.down_fcache, mudconf.downmotd_msg, command, user, password, cmdsave); return 0; } else { failconn("CON", "Connect", "Game Full", d, R_GAMEFULL, player, mudstate.full_fcache, 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, mudstate.down_fcache, 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, mudstate.full_fcache, mudconf.fullmotd_msg, command, user, password, cmdsave); return 0; } if (d->host_info & H_REGISTRATION) { fcache_dump(d, mudstate.regf_fcache); } 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 '%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; fcache_dump(d, mudstate.crea_fcache); announce_connect(player, d); } } } else { welcome_user(d); } free_mbuf(command); free_mbuf(user); free_mbuf(password); mudstate.debug_cmd = cmdsave; return 1; } int do_command(DESC *d, char *command) { char *arg, *cmdsave; NAMETAB *cp; 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; 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); } 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; 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 process_commands() { int nprocessed; DESC *d, *dnext; CBLOCK *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 = (CBLOCK *)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 (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; } /* -------------------------------------------------------------------------- * site_list: Display information in a site list */ const char *access_statstrings (int flag) { const char *str; switch (flag) { case H_FORBIDDEN: str = "Forbidden"; break; case H_REGISTRATION: str = "Registration"; break; case 0: str = "Unrestricted"; break; default: str = "Strange"; } return str; } const char *suspect_statstrings (int flag) { const char *str; if (flag) str = "Suspected"; else str = "Trusted"; return str; } void site_list (dbref player, SITE *site_list, const char *header_txt, const char *stat_str()) { char *buff, *buff1, *str; SITE *this; buff = alloc_mbuf("site_list.buff"); buff1 = alloc_sbuf("site_list.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_str(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 (dbref player) { site_list(player, mudstate.access_list, "Site Access", access_statstrings); site_list(player, mudstate.suspect_list, "Suspected Sites", suspect_statstrings); } /* --------------------------------------------------------------------------- * make_ulist: Make a list of connected user numbers for the LWHO function. */ void make_ulist (dbref player, char *buff) { char *cp; DESC *d; cp = buff; DESC_ITER_CONN(d) { if (!Wizard(player) && Dark(d->player)) continue; if (cp != buff) safe_chr(' ', buff, &cp); safe_chr('#', buff, &cp); safe_str(tprintf("%d", d->player), buff, &cp); } *cp = '\0'; } /* --------------------------------------------------------------------------- * 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) && Dark(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 rwho_update(void) { 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) && !Dark(d->player)) { sprintf(buf, "%d@%s", d->player, mudconf.mud_name); rwhocli_userlogin(buf, Name(d->player), d->connected_at); } } free_mbuf(buf); } #endif