// netcommon.cpp // // $Id: netcommon.cpp,v 1.51 2005/10/11 05:27:40 rmg 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 "autoconf.h" #include "config.h" #include "externs.h" #include <time.h> #include "ansi.h" #include "attrs.h" #include "command.h" #include "comsys.h" #include "file_c.h" #include "functions.h" #include "mguests.h" #include "powers.h" #include "svdreport.h" #ifdef REALITY_LVLS #include "levels.h" #endif /* REALITY_LVLS */ extern void handle_prog(DESC *, char *); #ifdef WIN32 extern HANDLE CompletionPort; // IOs are queued up on this port extern OVERLAPPED lpo_aborted; // special to indicate a player has finished TCP IOs extern OVERLAPPED lpo_aborted_final; // Actually free the descriptor. extern OVERLAPPED lpo_shutdown; // special to indicate a player should do a shutdown #endif /* --------------------------------------------------------------------------- * make_portlist: Make a list of ports for PORTS(). */ void make_portlist(dbref player, dbref target, char *buff, char **bufc) { ITL itl; ItemToList_Init(&itl, buff, bufc); DESC *d; DESC_ITER_CONN(d) { if ( d->player == target && !ItemToList_AddInteger(&itl, d->descriptor)) { break; } } ItemToList_Final(&itl); } // --------------------------------------------------------------------------- // make_port_ulist: Make a list of connected user numbers for the LPORTS function. // --------------------------------------------------------------------------- void make_port_ulist(dbref player, char *buff, char **bufc) { DESC *d; ITL itl; char *tmp = alloc_sbuf("make_port_ulist"); ItemToList_Init(&itl, buff, bufc, '#'); DESC_ITER_CONN(d) { if ( !See_Hidden(player) && Hidden(d->player)) { continue; } // printf format: printf("%d:%d", d->player, d->descriptor); // char *p = tmp; p += mux_ltoa(d->player, p); *p++ = ':'; p += mux_ltoa(d->descriptor, p); size_t n = p - tmp; if (!ItemToList_AddStringLEN(&itl, n, tmp)) { break; } } ItemToList_Final(&itl); free_sbuf(tmp); } /* --------------------------------------------------------------------------- * update_quotas: Update timeslice quotas */ void update_quotas(CLinearTimeAbsolute& ltaLast, const CLinearTimeAbsolute& ltaCurrent) { if (ltaCurrent < ltaLast) { ltaLast = ltaCurrent; return; } CLinearTimeDelta ltdDiff = ltaCurrent - ltaLast; if (ltdDiff < mudconf.timeslice) { return; } int nSlices = ltdDiff / mudconf.timeslice; int nExtraQuota = mudconf.cmd_quota_incr * nSlices; if (nExtraQuota > 0) { DESC *d; DESC_ITER_ALL(d) { d->quota += nExtraQuota; if (d->quota > mudconf.cmd_quota_max) { d->quota = mudconf.cmd_quota_max; } } } ltaLast += mudconf.timeslice * nSlices; } /* raw_notify_html() -- raw_notify() without the newline */ void raw_notify_html(dbref player, const char *msg) { if (!msg || !*msg) { return; } if ( mudstate.inpipe && player == mudstate.poutobj) { safe_str(msg, mudstate.poutnew, &mudstate.poutbufc); return; } if ( !Connected(player) || !Html(player)) { return; } DESC *d; DESC_ITER_PLAYER(player, d) { queue_string(d, msg); } } /* --------------------------------------------------------------------------- * raw_notify: write a message to a player */ void raw_notify(dbref player, const char *msg) { DESC *d; if (!msg || !*msg) { return; } if ( mudstate.inpipe && player == mudstate.poutobj) { safe_str(msg, mudstate.poutnew, &mudstate.poutbufc); safe_str("\r\n", mudstate.poutnew, &mudstate.poutbufc); return; } if (!Connected(player)) { return; } DESC_ITER_PLAYER(player, d) { queue_string(d, msg); queue_write_LEN(d, "\r\n", 2); } } void raw_notify_newline(dbref player) { if ( mudstate.inpipe && player == mudstate.poutobj) { safe_str("\r\n", mudstate.poutnew, &mudstate.poutbufc); return; } if (!Connected(player)) { return; } DESC *d; DESC_ITER_PLAYER(player, d) { queue_write_LEN(d, "\r\n", 2); } } /* --------------------------------------------------------------------------- * raw_broadcast: Send message to players who have indicated flags */ void DCL_CDECL raw_broadcast(int inflags, char *fmt, ...) { if (!fmt || !*fmt) { return; } char buff[LBUF_SIZE]; va_list ap; va_start(ap, fmt); mux_vsnprintf(buff, LBUF_SIZE, fmt, ap); va_end(ap); DESC *d; DESC_ITER_CONN(d) { if ((Flags(d->player) & inflags) == inflags) { queue_string(d, buff); queue_write_LEN(d, "\r\n", 2); process_output(d, false); } } } /* --------------------------------------------------------------------------- * 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; } } void add_to_output_queue(DESC *d, const char *b, int n) { TBLOCK *tp; int left; // Allocate an output buffer if needed. // if (d->output_head == NULL) { tp = (TBLOCK *)MEMALLOC(OUTPUT_BLOCK_SIZE); ISOUTOFMEMORY(tp); 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. // do { // See if there is enough space in the buffer to hold the // string. If so, copy it and update the pointers.. // left = OUTPUT_BLOCK_SIZE - (tp->hdr.end - (char *)tp + 1); if (n <= left) { memcpy(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) { memcpy(tp->hdr.end, b, left); tp->hdr.end += left; tp->hdr.nchars += left; b += left; n -= left; } tp = (TBLOCK *)MEMALLOC(OUTPUT_BLOCK_SIZE); ISOUTOFMEMORY(tp); 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); } /* --------------------------------------------------------------------------- * queue_write: Add text to the output queue for the indicated descriptor. */ void queue_write_LEN(DESC *d, const char *b, int n) { if (n <= 0) { return; } if (d->output_size + n > mudconf.output_limit) { process_output(d, false); } int left = mudconf.output_limit - d->output_size - n; if (left < 0) { TBLOCK *tp = d->output_head; if (tp == NULL) { STARTLOG(LOG_PROBLEMS, "QUE", "WRITE"); log_text("Flushing when output_head is null!"); ENDLOG; } else { STARTLOG(LOG_NET, "NET", "WRITE"); char *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); if (d->flags & DS_CONNECTED) { 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; } MEMFREE(tp); tp = NULL; } } add_to_output_queue(d, b, n); d->output_size += n; d->output_tot += n; #ifdef WIN32 if ( platform == VER_PLATFORM_WIN32_NT && !d->bWritePending && !d->bConnectionDropped) { d->bCallProcessOutputLater = true; } #endif } void queue_write(DESC *d, const char *b) { queue_write_LEN(d, b, strlen(b)); } void queue_string(DESC *d, const char *s) { const char *p = s; if (d->flags & DS_CONNECTED) { if ( !Ansi(d->player) && strchr(s, ESC_CHAR)) { p = strip_ansi(p); } else if (NoBleed(d->player)) { p = normal_to_white(p); } if (NoAccents(d->player)) { p = strip_accents(p); } } else { if (strchr(s, ESC_CHAR)) { p = strip_ansi(p); } p = strip_accents(p); } queue_write(d, p); } void freeqs(DESC *d) { TBLOCK *tb, *tnext; CBLK *cb, *cnext; tb = d->output_head; while (tb) { tnext = tb->hdr.nxt; MEMFREE(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. */ void desc_addhash(DESC *d) { dbref player = d->player; DESC *hdesc = (DESC *)hashfindLEN(&player, sizeof(player), &mudstate.desc_htab); if (hdesc == NULL) { d->hashnext = NULL; hashaddLEN(&player, sizeof(player), d, &mudstate.desc_htab); } else { d->hashnext = hdesc; hashreplLEN(&player, sizeof(player), d, &mudstate.desc_htab); } } /* --------------------------------------------------------------------------- * desc_delhash: Remove a net descriptor from its player hash list. */ static void desc_delhash(DESC *d) { dbref player = d->player; DESC *last = NULL; DESC *hdesc = (DESC *)hashfindLEN(&player, sizeof(player), &mudstate.desc_htab); while (hdesc != NULL) { if (d == hdesc) { if (last == NULL) { if (d->hashnext == NULL) { hashdeleteLEN(&player, sizeof(player), &mudstate.desc_htab); } else { hashreplLEN(&player, sizeof(player), 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, FC_CONN_REG); } else { 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; // We have added our first command to an empty list. Go process it later. // scheduler.DeferImmediateTask(PRIORITY_SYSTEM, Task_ProcessCommand, d, 0); } else { d->input_tail->hdr.nxt = command; } d->input_tail = command; } static void set_userstring(char **userstring, const char *command) { while (mux_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) { if (strlen(msg) > MBUF_SIZE) { *command = '\0'; *user = '\0'; *pass = '\0'; return; } while (mux_isspace(*msg)) { msg++; } char *p = command; while ( *msg && !mux_isspace(*msg)) { *p++ = *msg++; } *p = '\0'; while (mux_isspace(*msg)) { msg++; } p = user; if ( mudconf.name_spaces && *msg == '\"') { for (; *msg && (*msg == '\"' || mux_isspace(*msg)); msg++) { // Nothing. } while ( *msg && *msg != '\"') { while ( *msg && !mux_isspace(*msg) && *msg != '\"') { *p++ = *msg++; } if (*msg == '\"') { break; } while (mux_isspace(*msg)) { msg++; } if ( *msg && *msg != '\"') { *p++ = ' '; } } while ( *msg && *msg == '\"') { msg++; } } else { while ( *msg && !mux_isspace(*msg)) { *p++ = *msg++; } } *p = '\0'; while (mux_isspace(*msg)) { msg++; } p = pass; while ( *msg && !mux_isspace(*msg)) { *p++ = *msg++; } *p = '\0'; } static void announce_connect(dbref player, DESC *d) { desc_addhash(d); DESC *dtemp; int count = 0; DESC_ITER_CONN(dtemp) { count++; } if (mudstate.record_players < count) { mudstate.record_players = count; } char *buf = alloc_lbuf("announce_connect"); dbref aowner; int aflags; size_t nLen; atr_pget_str_LEN(buf, player, A_TIMEOUT, &aowner, &aflags, &nLen); if (nLen) { d->timeout = mux_atol(buf); if (d->timeout <= 0) { d->timeout = mudconf.idle_timeout; } } dbref 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."); } } atr_get_str_LEN(buf, player, A_LPAGE, &aowner, &aflags, &nLen); if (nLen) { raw_notify(player, "Your PAGE LOCK is set. You may be unable to receive some pages."); } int num = 0; DESC_ITER_PLAYER(player, dtemp) { num++; } // Reset vacation flag. // s_Flags(player, FLAG_WORD2, Flags2(player) & ~VACATION); char *pRoomAnnounceFmt; char *pMonitorAnnounceFmt; if (num < 2) { pRoomAnnounceFmt = "%s has connected."; if (mudconf.have_comsys) { do_comconnect(player); } if ( Hidden(player) && Can_Hide(player)) { pMonitorAnnounceFmt = "GAME: %s has DARK-connected."; } else { pMonitorAnnounceFmt = "GAME: %s has connected."; } if ( Suspect(player) || (d->host_info & H_SUSPECT)) { raw_broadcast(WIZARD, "[Suspect] %s has connected.", Moniker(player)); } } else { pRoomAnnounceFmt = "%s has reconnected."; pMonitorAnnounceFmt = "GAME: %s has reconnected."; if ( Suspect(player) || (d->host_info & H_SUSPECT)) { raw_broadcast(WIZARD, "[Suspect] %s has reconnected.", Moniker(player)); } } sprintf(buf, pRoomAnnounceFmt, Moniker(player)); raw_broadcast(MONITOR, pMonitorAnnounceFmt, Moniker(player)); int key = MSG_INV; if ( loc != NOTHING && !( Hidden(player) && Can_Hide(player))) { key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST); } dbref temp = mudstate.curr_enactor; mudstate.curr_enactor = player; #ifdef REALITY_LVLS if(loc == NOTHING) notify_check(player, player, buf, key); else notify_except_rlevel(loc, player, player, buf, 0); #else notify_check(player, player, buf, key); #endif /* REALITY_LVLS */ atr_pget_str_LEN(buf, player, A_ACONNECT, &aowner, &aflags, &nLen); CLinearTimeAbsolute lta; dbref zone, obj; if (nLen) { wait_que(player, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } if (mudconf.master_room != NOTHING) { atr_pget_str_LEN(buf, mudconf.master_room, A_ACONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(mudconf.master_room, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } DOLIST(obj, Contents(mudconf.master_room)) { atr_pget_str_LEN(buf, obj, A_ACONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(obj, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } } } // Do the zone of the player's location's possible aconnect. // if ( mudconf.have_zones && Good_obj(zone = Zone(loc))) { switch (Typeof(zone)) { case TYPE_THING: atr_pget_str_LEN(buf, zone, A_ACONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(zone, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } break; case TYPE_ROOM: // check every object in the room for a connect action. // DOLIST(obj, Contents(zone)) { atr_pget_str_LEN(buf, obj, A_ACONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(obj, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } } break; default: log_text(tprintf("Invalid zone #%d for %s(#%d) has bad type %d", zone, Name(player), player, Typeof(zone))); } } free_lbuf(buf); CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); char *time_str = ltaNow.ReturnDateString(7); record_login(player, true, time_str, d->addr, d->username, inet_ntoa((d->address).sin_addr)); if (mudconf.have_mailer) { check_mail(player, 0, false); } look_in(player, Location(player), (LK_SHOWEXIT|LK_OBEYTERSE|LK_SHOWVRML)); mudstate.curr_enactor = temp; if (Guest(player)) { db[player].fs.word[FLAG_WORD1] &= ~DARK; } } void announce_disconnect(dbref player, DESC *d, const char *reason) { int num = 0, key; DESC *dtemp; DESC_ITER_PLAYER(player, dtemp) { num++; } dbref temp = mudstate.curr_enactor; mudstate.curr_enactor = player; dbref loc = Location(player); if (num < 2) { if ( Suspect(player) || (d->host_info & H_SUSPECT)) { raw_broadcast(WIZARD, "[Suspect] %s has disconnected.", Moniker(player)); } char *buf = alloc_lbuf("announce_disconnect.only"); sprintf(buf, "%s has disconnected.", Moniker(player)); key = MSG_INV; if ( loc != NOTHING && !( Hidden(player) && Can_Hide(player))) { key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST); } #ifdef REALITY_LVLS if(loc == NOTHING) notify_check(player, player, buf, key); else notify_except_rlevel(loc, player, player, buf, 0); #else notify_check(player, player, buf, key); #endif /* REALITY_LVLS */ if (mudconf.have_mailer) { do_mail_purge(player); } raw_broadcast(MONITOR, "GAME: %s has disconnected. <%s>", Moniker(player), reason); c_Connected(player); if (mudconf.have_comsys) { do_comdisconnect(player); } dbref aowner, zone, obj; int aflags; size_t nLen; char *argv[1]; argv[0] = (char *)reason; CLinearTimeAbsolute lta; atr_pget_str_LEN(buf, player, A_ADISCONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(player, player, player, false, lta, NOTHING, 0, buf, argv, 1, NULL); } if (mudconf.master_room != NOTHING) { atr_pget_str_LEN(buf, mudconf.master_room, A_ADISCONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(mudconf.master_room, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } DOLIST(obj, Contents(mudconf.master_room)) { atr_pget_str_LEN(buf, obj, A_ADISCONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(obj, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } } } // Do the zone of the player's location's possible adisconnect. // if (mudconf.have_zones && Good_obj(zone = Zone(loc))) { switch (Typeof(zone)) { case TYPE_THING: atr_pget_str_LEN(buf, zone, A_ADISCONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(zone, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } break; case TYPE_ROOM: // check every object in the room for a connect action. // DOLIST(obj, Contents(zone)) { atr_pget_str_LEN(buf, obj, A_ADISCONNECT, &aowner, &aflags, &nLen); if (nLen) { wait_que(obj, player, player, false, lta, NOTHING, 0, buf, (char **)NULL, 0, NULL); } } break; default: log_text(tprintf("Invalid zone #%d for %s(#%d) has bad type %d", zone, Name(player), player, Typeof(zone))); } } free_lbuf(buf); if (d->flags & DS_AUTODARK) { d->flags &= ~DS_AUTODARK; // Don't clear the DARK flag on the player unless there are // no other sessions. // DESC *d1; int num = 0; DESC_ITER_PLAYER(player, d1) { num++; } if (num <= 1) { db[player].fs.word[FLAG_WORD1] &= ~DARK; } } if (Guest(player)) { db[player].fs.word[FLAG_WORD1] |= DARK; halt_que(NOTHING, player); } } else { if ( Suspect(player) || (d->host_info & H_SUSPECT)) { raw_broadcast(WIZARD, "[Suspect] %s has partially disconnected.", Moniker(player)); } char *mbuf = alloc_mbuf("announce_disconnect.partial"); sprintf(mbuf, "%s has partially disconnected.", Moniker(player)); key = MSG_INV; if ( loc != NOTHING && !( Hidden(player) && Can_Hide(player))) { key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST); } #ifdef REALITY_LVLS if(loc == NOTHING) notify_check(player, player, mbuf, key); else notify_except_rlevel(loc, player, player, mbuf, 0); #else notify_check(player, player, mbuf, key); #endif /* REALITY_LVLS */ raw_broadcast(MONITOR, "GAME: %s has partially disconnected.", Moniker(player)); free_mbuf(mbuf); } mudstate.curr_enactor = temp; desc_delhash(d); local_disconnect(player, num); } int boot_off(dbref player, const char *message) { DESC *d, *dnext; int count = 0; DESC_SAFEITER_PLAYER(player, d, dnext) { if (message && *message) { queue_string(d, message); queue_write_LEN(d, "\r\n", 2); } shutdownsock(d, R_BOOT); count++; } return count; } int boot_by_port(SOCKET port, bool bGod, const char *message) { DESC *d, *dnext; int count = 0; DESC_SAFEITER_ALL(d, dnext) { if ( d->descriptor == port && ( bGod || !(d->flags & DS_CONNECTED) || !God(d->player))) { if ( message && *message) { queue_string(d, message); queue_write_LEN(d, "\r\n", 2); } 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 = mux_atol(buf); if (d->timeout <= 0) { d->timeout = mudconf.idle_timeout; } } free_lbuf(buf); } } // --------------------------------------------------------------------------- // fetch_session: Return number of sessions (or 0 if not logged in). // int fetch_session(dbref target) { DESC *d; int nCount = 0; DESC_ITER_PLAYER(target, d) { nCount++; } return nCount; } // --------------------------------------------------------------------------- // fetch_idle: Return smallest idle time for a player (or -1 if not logged in). // int fetch_idle(dbref target) { CLinearTimeAbsolute ltaNow; CLinearTimeAbsolute ltaNewestLastTime; ltaNow.GetUTC(); DESC *d; bool bFound = false; DESC_ITER_PLAYER(target, d) { if ( !bFound || ltaNewestLastTime < d->last_time) { bFound = true; ltaNewestLastTime = d->last_time; } } if (bFound) { CLinearTimeDelta ltdResult; ltdResult = ltaNow - ltaNewestLastTime; return ltdResult.ReturnSeconds(); } else { return -1; } } // --------------------------------------------------------------------------- // find_oldest: Return descriptor with the oldeset connected_at (or NULL if // not logged in). // void find_oldest(dbref target, DESC *dOldest[2]) { dOldest[0] = NULL; dOldest[1] = NULL; DESC *d; bool bFound = false; DESC_ITER_PLAYER(target, d) { if ( !bFound || d->connected_at < dOldest[0]->connected_at) { bFound = true; dOldest[1] = dOldest[0]; dOldest[0] = d; } } } // --------------------------------------------------------------------------- // fetch_connect: Return largest connect time for a player (or -1 if not // logged in). // int fetch_connect(dbref target) { DESC *dOldest[2]; find_oldest(target, dOldest); if (dOldest[0]) { CLinearTimeAbsolute ltaNow; CLinearTimeDelta ltdOldest; ltaNow.GetUTC(); ltdOldest = ltaNow - dOldest[0]->connected_at; return ltdOldest.ReturnSeconds(); } else { return -1; } } // A NOTE about AUTODARK: It only works for wizard players. Wizard players // are automatically set DARK if they are not already set DARK and they have // no session which is unidle. // // The AUTODARK state is cleared when at least one session becomes unidle. // The AUTODARK state is also cleared when the last idle session is // disconnected from the server (session shutdown or @shutdown). // void check_idle(void) { DESC *d, *dnext; CLinearTimeAbsolute ltaNow; ltaNow.GetUTC(); DESC_SAFEITER_ALL(d, dnext) { if ( (d->flags & DS_CONNECTED) && KeepAlive(d->player)) { // Send a Telnet NOP code - creates traffic to keep NAT routers // happy. Hopefully this only runs once a minute. // queue_write_LEN(d, "\377\361", 2); } if (d->flags & DS_AUTODARK) { continue; } if (d->flags & DS_CONNECTED) { if (mudconf.idle_timeout <= 0) { // Idle timeout checking on connected players is effectively disabled. // PennMUSH uses idle_timeout == 0. Rhost uses idel_timeout == -1. // We will be disabled for either setting. // continue; } CLinearTimeDelta ltdIdle = ltaNow - d->last_time; if (Can_Idle(d->player)) { if ( mudconf.idle_wiz_dark && (Flags(d->player) & (WIZARD|DARK)) == WIZARD && ltdIdle.ReturnSeconds() > mudconf.idle_timeout) { // Make sure this Wizard player does not have some other // active session. // DESC *d1; bool bFound = false; DESC_ITER_PLAYER(d->player, d1) { if (d1 != d) { CLinearTimeDelta ltd = ltaNow - d1->last_time; if (ltd.ReturnSeconds() <= mudconf.idle_timeout) { bFound = true; break; } } } if (!bFound) { db[d->player].fs.word[FLAG_WORD1] |= DARK; DESC_ITER_PLAYER(d->player, d1) { d1->flags |= DS_AUTODARK; } } } } else if (ltdIdle.ReturnSeconds() > d->timeout) { queue_write(d, "*** Inactivity Timeout ***\r\n"); shutdownsock(d, R_TIMEOUT); } } else if (0 < mudconf.conn_timeout) { CLinearTimeDelta ltdIdle = ltaNow - d->connected_at; if (ltdIdle.ReturnSeconds() > mudconf.conn_timeout) { queue_write(d, "*** Login Timeout ***\r\n"); shutdownsock(d, R_TIMEOUT); } } } } void check_events(void) { dbref thing, parent; int lev; CLinearTimeAbsolute ltaNow; ltaNow.GetLocal(); FIELDEDTIME ft; if (!ltaNow.ReturnFields(&ft)) { return; } // Resetting every midnight. // static int iLastHourChecked = 25; if ( iLastHourChecked == 23 && ft.iHour < iLastHourChecked) { mudstate.events_flag &= ~ET_DAILY; } iLastHourChecked = ft.iHour; if ( ft.iHour == mudconf.events_daily_hour && !(mudstate.events_flag & ET_DAILY)) { 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; } } } } } #define MAX_TRIMMED_NAME_LENGTH 16 static const char *trimmed_name(dbref player, int *pvw) { static char cbuff[MBUF_SIZE]; ANSI_TruncateToField( Moniker(player), sizeof(cbuff), cbuff, MAX_TRIMMED_NAME_LENGTH, pvw, ANSI_ENDGOAL_NORMAL ); return cbuff; } static char *trimmed_site(char *szName) { static char buff[MBUF_SIZE]; unsigned int nLen = strlen(szName); if ( mudconf.site_chars <= 0 || nLen <= mudconf.site_chars) { return szName; } nLen = mudconf.site_chars; if (nLen > sizeof(buff)-1) { nLen = sizeof(buff)-1; } memcpy(buff, szName, nLen); buff[nLen] = '\0'; return buff; } static void dump_users(DESC *e, char *match, int key) { DESC *d; int count; char *buf, *fp, *sp, flist[4], slist[4]; dbref room_it; if (match) { while (mux_isspace(*match)) { match++; } if (!*match) { match = NULL; } } if ( (e->flags & (DS_PUEBLOCLIENT|DS_CONNECTED)) && Html(e->player)) { queue_write(e, "<pre>"); } buf = alloc_mbuf("dump_users"); if (key == CMD_SESSION) { queue_write(e, " "); queue_write(e, " Characters Input---- Characters Output---\r\n"); } queue_write(e, "Player Name On For Idle "); if (key == CMD_SESSION) { queue_write(e, "Port Pend Lost Total Pend Lost Total\r\n"); } else if ( (e->flags & DS_CONNECTED) && Wizard_Who(e->player) && key == CMD_WHO) { queue_write(e, " Room Cmds Host\r\n"); } else { if ( Wizard_Who(e->player) || See_Hidden(e->player)) { queue_write(e, " "); } else { queue_write(e, " "); } queue_string(e, mudstate.doing_hdr); queue_write_LEN(e, "\r\n", 2); } count = 0; CLinearTimeAbsolute ltaNow; ltaNow.GetUTC(); DESC_ITER_ALL(d) { if (!( ( (e->flags & DS_CONNECTED) && SiteMon(e->player)) || (d->flags & DS_CONNECTED))) { continue; } if ( !(d->flags & DS_CONNECTED) || !Hidden(d->player) || ( (e->flags & DS_CONNECTED) && ( Wizard_Who(e->player) || See_Hidden(e->player)))) { count++; if ( match && ( !(d->flags & DS_CONNECTED) || string_prefix(Name(d->player), match) == 0)) { continue; } if ( key == CMD_SESSION && ( !(e->flags & DS_CONNECTED) || !Wizard_Who(e->player)) && ( !(e->flags & DS_CONNECTED) || !(d->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 ( (d->flags & DS_CONNECTED) && Hidden(d->player)) { if (d->flags & DS_AUTODARK) { *fp++ = 'd'; } else { *fp++ = 'D'; } } if (d->flags & DS_CONNECTED) { if (Hideout(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'; } } else if ( (e->flags & DS_CONNECTED) && (d->flags & DS_CONNECTED) && See_Hidden(e->player) && Hidden(d->player)) { if (d->flags & DS_AUTODARK) { *fp++ = 'd'; } else { *fp++ = 'D'; } } *fp = '\0'; *sp = '\0'; CLinearTimeDelta ltdConnected = ltaNow - d->connected_at; CLinearTimeDelta ltdLastTime = ltaNow - d->last_time; const char *pNameField = "<Unconnected>"; int vwNameField = strlen(pNameField); if (d->flags & DS_CONNECTED) { pNameField = trimmed_name(d->player, &vwNameField); } // How many spaces between the name field and the 'On For' field. // size_t nFill; if (13 <= vwNameField) { nFill = 1; } else { nFill = 14-vwNameField; } char aFill[15]; memset(aFill, ' ', nFill); aFill[nFill] = '\0'; // The width size allocated to the 'On For' field. // size_t nOnFor = 25 - nFill - vwNameField; if ( (e->flags & DS_CONNECTED) && Wizard_Who(e->player) && key == CMD_WHO) { sprintf(buf, "%s%s%s %4s%-3s#%-6d%5d%3s%s\r\n", pNameField, aFill, time_format_1(ltdConnected.ReturnSeconds(), nOnFor), time_format_2(ltdLastTime.ReturnSeconds()), flist, ((d->flags & DS_CONNECTED) ? Location(d->player) : -1), 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, "%s%s%s %4s%5d%5d%6d%10d%6d%6d%10d\r\n", pNameField, aFill, time_format_1(ltdConnected.ReturnSeconds(), nOnFor), time_format_2(ltdLastTime.ReturnSeconds()), 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) || See_Hidden(e->player)) { sprintf(buf, "%s%s%s %4s%-3s%s\r\n", pNameField, aFill, time_format_1(ltdConnected.ReturnSeconds(), nOnFor), time_format_2(ltdLastTime.ReturnSeconds()), flist, d->doing); } else { sprintf(buf, "%s%s%s %4s %s\r\n", pNameField, aFill, time_format_1(ltdConnected.ReturnSeconds(), nOnFor), time_format_2(ltdLastTime.ReturnSeconds()), d->doing); } queue_string(e, buf); } } // 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" : mux_ltoa_t(mudconf.max_players)); queue_write(e, buf); if ( (e->flags & (DS_PUEBLOCLIENT|DS_CONNECTED)) && Html(e->player)) { queue_write(e, "</pre>"); } free_mbuf(buf); } #ifdef WOD_REALMS #define INFO_VERSION "1.1" #else // WOD_REALMS #define INFO_VERSION "1" #endif // WOD_REALMS static void dump_info(DESC *arg_desc) { queue_write(arg_desc, "### Begin INFO " INFO_VERSION "\r\n"); queue_string(arg_desc, tprintf("Name: %s\r\n", mudconf.mud_name)); char *temp = mudstate.start_time.ReturnDateString(); queue_write(arg_desc, tprintf("Uptime: %s\r\n", temp)); DESC *d; int count = 0; DESC_ITER_CONN(d) { if (!Good_obj(d->player)) { continue; } if ( !Hidden(d->player) || ( (arg_desc->flags & DS_CONNECTED) && See_Hidden(arg_desc->player))) { count++; } } queue_write(arg_desc, tprintf("Connected: %d\r\n", count)); queue_write(arg_desc, tprintf("Size: %d\r\n", mudstate.db_top)); queue_write(arg_desc, tprintf("Version: %s\r\n", mudstate.short_ver)); #ifdef WOD_REALMS queue_write(arg_desc, tprintf("Patches: WOD_REALMS\r\n")); #endif // WOD_REALMS queue_write(arg_desc, "### End INFO\r\n"); } char *MakeCanonicalDoing(char *pDoing, int *pnValidDoing, bool *pbValidDoing) { *pnValidDoing = 0; *pbValidDoing = false; if (!pDoing) { return NULL; } // First, remove all '\r\n\t' from the string. // char *Buffer = RemoveSetOfCharacters(pDoing, "\r\n\t"); // Optimize/terminate any ANSI in the string. // int nVisualWidth; static char szFittedDoing[SIZEOF_DOING_STRING]; *pnValidDoing = ANSI_TruncateToField ( Buffer, SIZEOF_DOING_STRING, szFittedDoing, WIDTHOF_DOING_STRING, &nVisualWidth, ANSI_ENDGOAL_NORMAL ); *pbValidDoing = true; return szFittedDoing; } // --------------------------------------------------------------------------- // do_doing: Set the doing string that appears in the WHO report. // Idea from R'nice@TinyTIM. // void do_doing(dbref executor, dbref caller, dbref enactor, int key, char *arg) { // Make sure there can be no embedded newlines from %r // static char *Empty = ""; char *szValidDoing = Empty; bool bValidDoing; int nValidDoing = 0; if (arg) { szValidDoing = MakeCanonicalDoing(arg, &nValidDoing, &bValidDoing); if (!bValidDoing) { szValidDoing = Empty; nValidDoing = 0; } } bool bQuiet = ((key & DOING_QUIET) == DOING_QUIET); key &= DOING_MASK; if (key == DOING_MESSAGE) { DESC *d; bool bFound = false; DESC_ITER_PLAYER(executor, d) { memcpy(d->doing, szValidDoing, nValidDoing+1); bFound = true; } if (bFound) { if ( !bQuiet && !Quiet(executor)) { notify(executor, "Set."); } } else { notify(executor, "Not connected."); } } else if (key == DOING_UNIQUE) { DESC *d; DESC *dMax = NULL; CLinearTimeAbsolute ltaMax; DESC_ITER_PLAYER(executor, d) { if ( !dMax && ltaMax < d->last_time) { ltaMax = d->last_time; dMax = d; } } if (dMax) { memcpy(dMax->doing, szValidDoing, nValidDoing+1); if ( !bQuiet && !Quiet(executor)) { notify(executor, "Set."); } } else { notify(executor, "Not connected."); } } else if (key == DOING_HEADER) { if (!Can_Poll(executor)) { notify(executor, NOPERM_MESSAGE); return; } if (nValidDoing == 0) { strcpy(mudstate.doing_hdr, "Doing"); } else { memcpy(mudstate.doing_hdr, szValidDoing, nValidDoing+1); } if ( !bQuiet && !Quiet(executor)) { notify(executor, "Set."); } } else // if (key == DOING_POLL) { notify(executor, 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}, {(char *)"SESSION", 7, CA_PUBLIC, CMD_SESSION}, {(char *)"WHO", 3, CA_PUBLIC, CMD_WHO}, {(char *)"PUEBLOCLIENT", 12, CA_PUBLIC, CMD_PUEBLOCLIENT}, {(char *)"INFO", 4, CA_PUBLIC, CMD_INFO}, {NULL, 0, 0, 0} }; 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. // for (cp = logout_cmdtable; cp->flag; cp++) { hashaddLEN(cp->name, strlen(cp->name), 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) { STARTLOG(LOG_LOGIN | LOG_SECURITY, logcode, "RJCT"); char *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(" ("); log_text(logreason); log_text(")"); ENDLOG; fcache_dump(d, filecache); if (*motd_msg) { queue_string(d, motd_msg); queue_write_LEN(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 bool check_connect(DESC *d, char *msg) { char *buff; dbref player, aowner; int aflags, nplayers; DESC *d2; const char *p; bool isGuest = false; char *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. // char *command = alloc_lbuf("check_conn.cmd"); char *user = alloc_lbuf("check_conn.user"); char *password = alloc_lbuf("check_conn.pass"); parse_connect(msg, command, user, password); // At this point, command, user, and password are all less than // MBUF_SIZE. // if ( strncmp(command, "co", 2) == 0 || strncmp(command, "cd", 2) == 0) { if (string_prefix(user, mudconf.guest_prefix)) { if ( (d->host_info & H_GUEST) || ( !mudconf.allow_guest_from_registered_site && (d->host_info & H_REGISTRATION))) { // Someone from an IP with guest restrictions is // trying to use a guest account. Give them the blurb // most likely to have instructions about requesting a // character by other means and then fail this // connection. // // The guest 'power' is handled separately further // down. // failconn("CONN", "Connect", "Guest Site Forbidden", d, R_GAMEDOWN, NOTHING, FC_CONN_REG, mudconf.downmotd_msg, command, user, password, cmdsave); return false; } if ( mudconf.guest_char != NOTHING && (mudconf.control_flags & CF_LOGIN)) { if (!(mudconf.control_flags & CF_GUEST)) { queue_write(d, "Guest logins are disabled.\r\n"); free_lbuf(command); free_lbuf(user); free_lbuf(password); return false; } if ((p = Guest.Create(d)) == NULL) { queue_write(d, "All guests are tied up, please try again later.\r\n"); free_lbuf(command); free_lbuf(user); free_lbuf(password); return false; } strcpy(user, p); strcpy(password, GUEST_PASSWORD); isGuest = true; } } // 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, inet_ntoa((d->address).sin_addr)); if ( player == NOTHING || (!isGuest && Guest.CheckGuest(player))) { // Not a player, or wrong password. // queue_write(d, connect_fail); STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD"); buff = alloc_lbuf("check_conn.LOG.bad"); 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 false; } } else if ( ( (mudconf.control_flags & CF_LOGIN) && (nplayers < mudconf.max_players)) || WizRoy(player) || God(player)) { if ( strncmp(command, "cd", 2) == 0 && ( Wizard(player) || God(player))) { db[player].fs.word[FLAG_WORD1] |= DARK; } // Make sure we don't have a guest from an unwanted host. // The majority of these are handled above. // // The following code handles the case where a staffer // (#1-only by default) has specifically given the guest 'power' // to an existing player. // // In this case, the player -already- has an account complete // with password. We still fail the connection to -this- player // but if the site isn't register_sited, this player can simply // auto-create another player. So, the procedure is not much // different from @newpassword'ing them. Oh well. We are just // following orders. ;) // if ( Guest(player) && ( (d->host_info & H_GUEST) || ( !mudconf.allow_guest_from_registered_site && (d->host_info & H_REGISTRATION)))) { failconn("CON", "Connect", "Guest Site Forbidden", d, R_GAMEDOWN, player, FC_CONN_SITE, mudconf.downmotd_msg, command, user, password, cmdsave); return false; } // 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.GetUTC(); d->player = player; // 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 == '\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); DESC* dtemp; int num_con = 0; DESC_ITER_PLAYER(player, dtemp) { num_con++; } local_connect(player, 0, num_con); // If stuck in an @prog, show the prompt. // if (d->program_data != NULL) { queue_write_LEN(d, ">\377\371", 3); } } 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 false; } else { failconn("CON", "Connect", "Game Full", d, R_GAMEFULL, player, FC_CONN_FULL, mudconf.fullmotd_msg, command, user, password, cmdsave); return false; } } else if (strncmp(command, "cr", 2) == 0) { // 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 false; } // 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 false; } if (d->host_info & H_REGISTRATION) { fcache_dump(d, FC_CREA_REG); } else { const char *pmsg; player = create_player(user, password, NOTHING, false, &pmsg); if (player == NOTHING) { queue_write(d, pmsg); queue_write(d, "\r\n"); STARTLOG(LOG_SECURITY | LOG_PCREATES, "CON", "BAD"); buff = alloc_lbuf("check_conn.LOG.badcrea"); sprintf(buff, "[%d/%s] Create of '%s' failed", d->descriptor, d->addr, user); log_text(buff); free_lbuf(buff); ENDLOG; } else { AddToPublicChannel(player); 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.GetUTC(); d->player = player; fcache_dump(d, FC_CREA_NEW); announce_connect(player, d); // Since it is on the create call, assume connection count // is 0 and indicate the connect is a new character. // local_connect(player, 1, 0); } } } 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 true; } static void do_logged_out_internal(DESC *d, int key, char *arg) { switch (key) { case CMD_QUIT: shutdownsock(d, R_QUIT); break; case CMD_LOGOUT: shutdownsock(d, R_LOGOUT); break; case CMD_WHO: case CMD_DOING: case CMD_SESSION: dump_users(d, arg, key); break; case CMD_PREFIX: set_userstring(&d->output_prefix, arg); break; case CMD_SUFFIX: set_userstring(&d->output_suffix, arg); break; case CMD_INFO: dump_info(d); break; case CMD_PUEBLOCLIENT: // Set the descriptor's flag. // d->flags |= DS_PUEBLOCLIENT; queue_string(d, mudconf.pueblo_msg); queue_write_LEN(d, "\r\n", 2); break; default: { char buf[LBUF_SIZE * 2]; STARTLOG(LOG_BUGS, "BUG", "PARSE"); sprintf(buf, "Logged-out command with no handler: '%s'", mudstate.debug_cmd); log_text(buf); ENDLOG; } } } void do_command(DESC *d, char *command) { char *cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *)"< do_command >"; if (d->flags & DS_CONNECTED) { // Normal logged-in command processing. // d->command_count++; if (d->output_prefix) { queue_string(d, d->output_prefix); queue_write_LEN(d, "\r\n", 2); } mudstate.curr_executor = d->player; mudstate.curr_enactor = d->player; for (int i = 0; i < MAX_GLOBAL_REGS; i++) { mudstate.global_regs[i][0] = '\0'; mudstate.glob_reg_len[i] = 0; } CLinearTimeAbsolute ltaBegin; ltaBegin.GetUTC(); MuxAlarm.Set(mudconf.max_cmdsecs); char *log_cmdbuf = process_command(d->player, d->player, d->player, true, command, (char **)NULL, 0); CLinearTimeAbsolute ltaEnd; ltaEnd.GetUTC(); if (MuxAlarm.bAlarmed) { notify(d->player, "GAME: Expensive activity abbreviated."); halt_que(d->player, NOTHING); s_Flags(d->player, FLAG_WORD1, Flags(d->player) | HALT); } MuxAlarm.Clear(); CLinearTimeDelta ltd = ltaEnd - ltaBegin; if (ltd > mudconf.rpt_cmdsecs) { STARTLOG(LOG_PROBLEMS, "CMD", "CPU"); log_name_and_loc(d->player); char *logbuf = alloc_lbuf("do_command.LOG.cpu"); sprintf(logbuf, " queued command taking %s secs: ", ltd.ReturnSecondsString(4)); log_text(logbuf); free_lbuf(logbuf); log_text(log_cmdbuf); ENDLOG; } mudstate.curr_cmd = (char *) ""; if (d->output_suffix) { queue_string(d, d->output_suffix); queue_write_LEN(d, "\r\n", 2); } mudstate.debug_cmd = cmdsave; return; } // Login screen (logged-out) command processing. // // Split off the command from the arguments. // char *arg = command; while (*arg && !mux_isspace(*arg)) { arg++; } if (*arg) { *arg++ = '\0'; } // Look up the command in the logged-out command table. // NAMETAB *cp = (NAMETAB *)hashfindLEN(command, strlen(command), &mudstate.logout_cmd_htab); if (cp == NULL) { // Not in the logged-out command table, so maybe a connect attempt. // if (*arg) { // Restore nullified space // *--arg = ' '; } mudstate.curr_executor = NOTHING; mudstate.curr_enactor = NOTHING; mudstate.debug_cmd = cmdsave; check_connect(d, command); return; } // 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_LEN(d, "\r\n", 2); } } if (cp->perm != CA_PUBLIC) { queue_write(d, "Permission denied.\r\n"); } else { mudstate.debug_cmd = cp->name; do_logged_out_internal(d, cp->flag & CMD_MASK, arg); } // QUIT or LOGOUT will close the connection and cause the // descriptor to be freed! // if ( ((cp->flag & CMD_MASK) != CMD_QUIT) && ((cp->flag & CMD_MASK) != CMD_LOGOUT) && !(cp->flag & CMD_NOxFIX)) { if (d->output_suffix) { queue_string(d, d->output_suffix); queue_write_LEN(d, "\r\n", 2); } } mudstate.debug_cmd = cmdsave; } void logged_out1(dbref executor, dbref caller, dbref enactor, int key, char *arg) { // PUEBLOCLIENT affects all the player's connections. // if (key == CMD_PUEBLOCLIENT) { DESC *d; DESC_ITER_PLAYER(executor, d) { do_logged_out_internal(d, key, arg); } // Set the player's flag. // s_Html(executor); return; } // Other logged-out commands affect only the player's most recently // used connection. // DESC *d; DESC *dLatest = NULL; DESC_ITER_PLAYER(executor, d) { if ( dLatest == NULL || dLatest->last_time < d->last_time) { dLatest = d; } } if (dLatest != NULL) { do_logged_out_internal(dLatest, key, arg); } } void logged_out0(dbref executor, dbref caller, dbref enactor, int key) { logged_out1(executor, caller, enactor, key, ""); } void Task_ProcessCommand(void *arg_voidptr, int arg_iInteger) { DESC *d = (DESC *)arg_voidptr; if (d) { CBLK *t = d->input_head; if (t) { if (d->quota > 0) { d->quota--; d->input_head = (CBLK *) t->hdr.nxt; if (d->input_head) { // There are still commands to process, so schedule another looksee. // scheduler.DeferImmediateTask(PRIORITY_SYSTEM, Task_ProcessCommand, d, 0); } else { d->input_tail = NULL; } d->input_size -= (strlen(t->cmd) + 1); d->last_time.GetUTC(); if (d->program_data != NULL) { handle_prog(d, t->cmd); } else { do_command(d, t->cmd); } free_lbuf(t); } else { // Don't bother looking for more quota until at least this much time has past. // CLinearTimeAbsolute lsaWhen; lsaWhen.GetUTC(); scheduler.DeferTask(lsaWhen + mudconf.timeslice, PRIORITY_SYSTEM, Task_ProcessCommand, d, 0); } } } } /* --------------------------------------------------------------------------- * site_check: Check for site flags in a site list. */ int site_check(struct in_addr host, SITE *site_list) { SITE *this0; for (this0 = site_list; this0; this0 = this0->next) { if ((host.s_addr & this0->mask.s_addr) == this0->address.s_addr) { return this0->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 H_GUEST: str = "NoGuest"; break; case H_NOSITEMON: str = "NoSiteMon"; break; case 0: str = "Unrestricted"; break; default: str = "Strange"; break; } break; default: str = "Strange"; break; } return str; } static void list_sites(dbref player, SITE *site_list, const char *header_txt, int stat_type) { char *buff, *buff1, *str; SITE *this0; 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 (this0 = site_list; this0; this0 = this0->next) { str = (char *)stat_string(stat_type, this0->flag); strcpy(buff1, inet_ntoa(this0->mask)); sprintf(buff, "%-20s %-20s %s", inet_ntoa(this0->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, bool bPorts) { DESC *d; if (bPorts) { make_port_ulist(player, buff, bufc); } else { ITL pContext; ItemToList_Init(&pContext, buff, bufc, '#'); DESC_ITER_CONN(d) { if ( !See_Hidden(player) && Hidden(d->player)) { continue; } if (!ItemToList_AddInteger(&pContext, d->player)) { break; } } ItemToList_Final(&pContext); } } /* --------------------------------------------------------------------------- * 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 = NOTHING; DESC_ITER_CONN(d) { if ( Good_obj(player) && !See_Hidden(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; } FUNCTION(fun_doing) { if (is_rational(fargs[0])) { SOCKET s = mux_atol(fargs[0]); bool bFound = false; DESC *d; DESC_ITER_CONN(d) { if (d->descriptor == s) { bFound = true; break; } } if ( bFound && ( d->player == executor || Wizard_Who(executor))) { safe_str(d->doing, buff, bufc); } else { safe_nothing(buff, bufc); } } else { dbref victim = lookup_player(executor, fargs[0], true); if (victim == NOTHING) { safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc); return; } if ( Wizard_Who(executor) || !Hidden(victim)) { DESC *d; DESC_ITER_CONN(d) { if (d->player == victim) { safe_str(d->doing, buff, bufc); return; } } } safe_str("#-1 NOT A CONNECTED PLAYER", buff, bufc); } } // --------------------------------------------------------------------------- // fun_host: Return hostname of player or port descriptor. // --------------------------------------------------------------------------- FUNCTION(fun_host) { if (!Wizard_Who(executor)) { safe_noperm(buff, bufc); return; } bool isPort = is_rational(fargs[0]); bool bFound = false; DESC *d; if (isPort) { SOCKET s = mux_atol(fargs[0]); DESC_ITER_CONN(d) { if (d->descriptor == s) { bFound = true; break; } } } else { dbref victim = lookup_player(executor, fargs[0], true); if (victim == NOTHING) { safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc); return; } DESC_ITER_CONN(d) { if (d->player == victim) { bFound = true; break; } } } if (bFound) { char *hostname = ((d->username[0] != '\0') ? tprintf("%s@%s", d->username, d->addr) : d->addr); safe_str(hostname, buff, bufc); return; } if (isPort) { safe_str("#-1 NOT AN ACTIVE PORT", buff, bufc); } else { safe_str("#-1 NOT A CONNECTED PLAYER", buff, bufc); } } FUNCTION(fun_poll) { safe_str(mudstate.doing_hdr, buff, bufc); } FUNCTION(fun_motd) { safe_str(mudconf.motd_msg, buff, bufc); } // fetch_cmds - Retrieve Player's number of commands entered. // int fetch_cmds(dbref target) { int sum = 0; bool bFound = false; DESC *d; DESC_ITER_PLAYER(target, d) { sum += d->command_count; bFound = true; } if (bFound) { return sum; } else { return -1; } } void ParseConnectionInfoString(char *pConnInfo, char *pFields[5]) { MUX_STRTOK_STATE tts; mux_strtok_src(&tts, pConnInfo); mux_strtok_ctl(&tts, " "); for (int i = 0; i < 5; i++) { pFields[i] = mux_strtok_parse(&tts); } } void fetch_ConnectionInfoFields(dbref target, long anFields[4]) { dbref aowner; int aflags; char *pConnInfo = atr_get(target, A_CONNINFO, &aowner, &aflags); char *aFields[5]; ParseConnectionInfoString(pConnInfo, aFields); for (int i = 0; i < 4; i++) { long result; if ( !aFields[i] || (result = mux_atol(aFields[i])) < 0) { result = 0; } anFields[i] = result; } free_lbuf(pConnInfo); } void put_ConnectionInfoFields ( dbref target, long anFields[4], CLinearTimeAbsolute <aLogout ) { char *pConnInfo = alloc_lbuf("put_CIF"); char *p = pConnInfo; for (int i = 0; i < 4; i++) { p += mux_ltoa(anFields[i], p); *p++ = ' '; } p += mux_i64toa(ltaLogout.ReturnSeconds(), p); *p++ = 0; atr_add_raw_LEN(target, A_CONNINFO, pConnInfo, p - pConnInfo); free_lbuf(pConnInfo); } long fetch_ConnectionInfoField(dbref target, int iField) { dbref aowner; int aflags; char *pConnInfo = atr_get(target, A_CONNINFO, &aowner, &aflags); char *aFields[5]; ParseConnectionInfoString(pConnInfo, aFields); long result; if ( !aFields[iField] || (result = mux_atol(aFields[iField])) < 0) { result = 0; } free_lbuf(pConnInfo); return result; } #define CIF_LOGOUTTIME 4 CLinearTimeAbsolute fetch_logouttime(dbref target) { dbref aowner; int aflags; char *pConnInfo = atr_get(target, A_CONNINFO, &aowner, &aflags); char *aFields[5]; ParseConnectionInfoString(pConnInfo, aFields); CLinearTimeAbsolute lta; if (aFields[CIF_LOGOUTTIME]) { lta.SetSecondsString(aFields[CIF_LOGOUTTIME]); } else { lta.SetSeconds(0); } free_lbuf(pConnInfo); return lta; }