mux/src/netcommon.cpp

Go to the documentation of this file.
00001 // netcommon.cpp
00002 //
00003 // $Id: netcommon.cpp,v 1.62 2006/01/07 18:17:49 sdennis Exp $
00004 //
00005 // This file contains routines used by the networking code that do not
00006 // depend on the implementation of the networking code.  The network-specific
00007 // portions of the descriptor data structure are not used.
00008 //
00009 
00010 #include "copyright.h"
00011 #include "autoconf.h"
00012 #include "config.h"
00013 #include "externs.h"
00014 
00015 #include <time.h>
00016 
00017 #include "ansi.h"
00018 #include "attrs.h"
00019 #include "command.h"
00020 #include "comsys.h"
00021 #include "file_c.h"
00022 #include "functions.h"
00023 #include "mguests.h"
00024 #include "powers.h"
00025 #include "svdreport.h"
00026 #ifdef REALITY_LVLS
00027 #include "levels.h"
00028 #endif /* REALITY_LVLS */
00029 
00030 
00031 /* ---------------------------------------------------------------------------
00032  * make_portlist: Make a list of ports for PORTS().
00033  */
00034 
00035 void make_portlist(dbref player, dbref target, char *buff, char **bufc)
00036 {
00037     UNUSED_PARAMETER(player);
00038 
00039     ITL itl;
00040     ItemToList_Init(&itl, buff, bufc);
00041 
00042     DESC *d;
00043     DESC_ITER_CONN(d)
00044     {
00045         if (  d->player == target
00046            && !ItemToList_AddInteger(&itl, d->descriptor))
00047         {
00048             break;
00049         }
00050     }
00051     ItemToList_Final(&itl);
00052 }
00053 
00054 // ---------------------------------------------------------------------------
00055 // make_port_ulist: Make a list of connected user numbers for the LPORTS function.
00056 // ---------------------------------------------------------------------------
00057 
00058 void make_port_ulist(dbref player, char *buff, char **bufc)
00059 {
00060     DESC *d;
00061     ITL itl;
00062     char *tmp = alloc_sbuf("make_port_ulist");
00063     ItemToList_Init(&itl, buff, bufc, '#');
00064     DESC_ITER_CONN(d)
00065     {
00066         if (  !See_Hidden(player)
00067            && Hidden(d->player))
00068         {
00069             continue;
00070         }
00071 
00072         // printf format: printf("%d:%d", d->player, d->descriptor);
00073         //
00074         char *p = tmp;
00075         p += mux_ltoa(d->player, p);
00076         *p++ = ':';
00077         p += mux_ltoa(d->descriptor, p);
00078 
00079         size_t n = p - tmp;
00080         if (!ItemToList_AddStringLEN(&itl, n, tmp))
00081         {
00082             break;
00083         }
00084     }
00085     ItemToList_Final(&itl);
00086     free_sbuf(tmp);
00087 }
00088 
00089 /* ---------------------------------------------------------------------------
00090  * update_quotas: Update timeslice quotas
00091  */
00092 
00093 void update_quotas(CLinearTimeAbsolute& ltaLast, const CLinearTimeAbsolute& ltaCurrent)
00094 {
00095     if (ltaCurrent < ltaLast)
00096     {
00097         ltaLast = ltaCurrent;
00098         return;
00099     }
00100 
00101     CLinearTimeDelta ltdDiff = ltaCurrent - ltaLast;
00102     if (ltdDiff < mudconf.timeslice)
00103     {
00104         return;
00105     }
00106 
00107     int nSlices = ltdDiff / mudconf.timeslice;
00108     int nExtraQuota = mudconf.cmd_quota_incr * nSlices;
00109 
00110     if (nExtraQuota > 0)
00111     {
00112         DESC *d;
00113         DESC_ITER_ALL(d)
00114         {
00115             d->quota += nExtraQuota;
00116             if (d->quota > mudconf.cmd_quota_max)
00117             {
00118                 d->quota = mudconf.cmd_quota_max;
00119             }
00120         }
00121     }
00122     ltaLast += mudconf.timeslice * nSlices;
00123 }
00124 
00125 /* raw_notify_html() -- raw_notify() without the newline */
00126 void raw_notify_html(dbref player, const char *msg)
00127 {
00128     if (!msg || !*msg)
00129     {
00130         return;
00131     }
00132 
00133     if (  mudstate.inpipe
00134        && player == mudstate.poutobj)
00135     {
00136         safe_str(msg, mudstate.poutnew, &mudstate.poutbufc);
00137         return;
00138     }
00139     if (  !Connected(player)
00140        || !Html(player))
00141     {
00142         return;
00143     }
00144 
00145     DESC *d;
00146     DESC_ITER_PLAYER(player, d)
00147     {
00148         queue_string(d, msg);
00149     }
00150 }
00151 
00152 /* ---------------------------------------------------------------------------
00153  * raw_notify: write a message to a player
00154  */
00155 
00156 void raw_notify(dbref player, const char *msg)
00157 {
00158     DESC *d;
00159 
00160     if (!msg || !*msg)
00161     {
00162         return;
00163     }
00164 
00165     if (  mudstate.inpipe
00166        && player == mudstate.poutobj)
00167     {
00168         safe_str(msg, mudstate.poutnew, &mudstate.poutbufc);
00169         safe_str("\r\n", mudstate.poutnew, &mudstate.poutbufc);
00170         return;
00171     }
00172 
00173     if (!Connected(player))
00174     {
00175         return;
00176     }
00177 
00178     DESC_ITER_PLAYER(player, d)
00179     {
00180         queue_string(d, msg);
00181         queue_write_LEN(d, "\r\n", 2);
00182     }
00183 }
00184 
00185 void raw_notify_newline(dbref player)
00186 {
00187     if (  mudstate.inpipe
00188        && player == mudstate.poutobj)
00189     {
00190         safe_str("\r\n", mudstate.poutnew, &mudstate.poutbufc);
00191         return;
00192     }
00193     if (!Connected(player))
00194     {
00195         return;
00196     }
00197 
00198     DESC *d;
00199     DESC_ITER_PLAYER(player, d)
00200     {
00201         queue_write_LEN(d, "\r\n", 2);
00202     }
00203 }
00204 
00205 /* ---------------------------------------------------------------------------
00206  * raw_broadcast: Send message to players who have indicated flags
00207  */
00208 
00209 void DCL_CDECL raw_broadcast(int inflags, char *fmt, ...)
00210 {
00211     if (!fmt || !*fmt)
00212     {
00213         return;
00214     }
00215 
00216     char buff[LBUF_SIZE];
00217 
00218     va_list ap;
00219     va_start(ap, fmt);
00220     mux_vsnprintf(buff, LBUF_SIZE, fmt, ap);
00221     va_end(ap);
00222 
00223     DESC *d;
00224     DESC_ITER_CONN(d)
00225     {
00226         if ((Flags(d->player) & inflags) == inflags)
00227         {
00228             queue_string(d, buff);
00229             queue_write_LEN(d, "\r\n", 2);
00230             process_output(d, false);
00231         }
00232     }
00233 }
00234 
00235 /* ---------------------------------------------------------------------------
00236  * clearstrings: clear out prefix and suffix strings
00237  */
00238 
00239 void clearstrings(DESC *d)
00240 {
00241     if (d->output_prefix)
00242     {
00243         free_lbuf(d->output_prefix);
00244         d->output_prefix = NULL;
00245     }
00246     if (d->output_suffix)
00247     {
00248         free_lbuf(d->output_suffix);
00249         d->output_suffix = NULL;
00250     }
00251 }
00252 
00253 static void add_to_output_queue(DESC *d, const char *b, int n)
00254 {
00255     TBLOCK *tp;
00256     int left;
00257 
00258     // Allocate an output buffer if needed.
00259     //
00260     if (d->output_head == NULL)
00261     {
00262         tp = (TBLOCK *)MEMALLOC(OUTPUT_BLOCK_SIZE);
00263         ISOUTOFMEMORY(tp);
00264         tp->hdr.nxt = NULL;
00265         tp->hdr.start = tp->data;
00266         tp->hdr.end = tp->data;
00267         tp->hdr.nchars = 0;
00268         d->output_head = tp;
00269         d->output_tail = tp;
00270     }
00271     else
00272     {
00273         tp = d->output_tail;
00274     }
00275 
00276     // Now tp points to the last buffer in the chain.
00277     //
00278     do
00279     {
00280         // See if there is enough space in the buffer to hold the
00281         // string.  If so, copy it and update the pointers..
00282         //
00283         left = OUTPUT_BLOCK_SIZE - (tp->hdr.end - (char *)tp + 1);
00284         if (n <= left)
00285         {
00286             memcpy(tp->hdr.end, b, n);
00287             tp->hdr.end += n;
00288             tp->hdr.nchars += n;
00289             n = 0;
00290         }
00291         else
00292         {
00293             // It didn't fit.  Copy what will fit and then allocate
00294             // another buffer and retry.
00295             //
00296             if (left > 0)
00297             {
00298                 memcpy(tp->hdr.end, b, left);
00299                 tp->hdr.end += left;
00300                 tp->hdr.nchars += left;
00301                 b += left;
00302                 n -= left;
00303             }
00304             tp = (TBLOCK *)MEMALLOC(OUTPUT_BLOCK_SIZE);
00305             ISOUTOFMEMORY(tp);
00306             tp->hdr.nxt = NULL;
00307             tp->hdr.start = tp->data;
00308             tp->hdr.end = tp->data;
00309             tp->hdr.nchars = 0;
00310             d->output_tail->hdr.nxt = tp;
00311             d->output_tail = tp;
00312         }
00313     } while (n > 0);
00314 }
00315 
00316 /* ---------------------------------------------------------------------------
00317  * queue_write: Add text to the output queue for the indicated descriptor.
00318  */
00319 
00320 void queue_write_LEN(DESC *d, const char *b, int n)
00321 {
00322     if (n <= 0)
00323     {
00324         return;
00325     }
00326 
00327     if (d->output_size + n > mudconf.output_limit)
00328     {
00329         process_output(d, false);
00330     }
00331 
00332     int left = mudconf.output_limit - d->output_size - n;
00333     if (left < 0)
00334     {
00335         TBLOCK *tp = d->output_head;
00336         if (tp == NULL)
00337         {
00338             STARTLOG(LOG_PROBLEMS, "QUE", "WRITE");
00339             log_text("Flushing when output_head is null!");
00340             ENDLOG;
00341         }
00342         else
00343         {
00344             STARTLOG(LOG_NET, "NET", "WRITE");
00345             char *buf = alloc_lbuf("queue_write.LOG");
00346             sprintf(buf, "[%u/%s] Output buffer overflow, %d chars discarded by ", d->descriptor, d->addr, tp->hdr.nchars);
00347             log_text(buf);
00348             free_lbuf(buf);
00349             if (d->flags & DS_CONNECTED)
00350             {
00351                 log_name(d->player);
00352             }
00353             ENDLOG;
00354             d->output_size -= tp->hdr.nchars;
00355             d->output_head = tp->hdr.nxt;
00356             d->output_lost += tp->hdr.nchars;
00357             if (d->output_head == NULL)
00358             {
00359                 d->output_tail = NULL;
00360             }
00361             MEMFREE(tp);
00362             tp = NULL;
00363         }
00364     }
00365 
00366     add_to_output_queue(d, b, n);
00367     d->output_size += n;
00368     d->output_tot += n;
00369 
00370 #ifdef WIN32
00371     if (  platform == VER_PLATFORM_WIN32_NT
00372        && !d->bWritePending
00373        && !d->bConnectionDropped)
00374     {
00375         d->bCallProcessOutputLater = true;
00376     }
00377 #endif
00378 }
00379 
00380 void queue_write(DESC *d, const char *b)
00381 {
00382     queue_write_LEN(d, b, strlen(b));
00383 }
00384 
00385 static const char *encode_iac(const char *szString)
00386 {
00387     static char Buffer[2*LBUF_SIZE];
00388     char *pBuffer = Buffer;
00389 
00390     const char *pString = szString;
00391     if (pString)
00392     {
00393         while (*pString)
00394         {
00395             const char *p = strchr(pString, NVT_IAC);
00396             if (!p)
00397             {
00398                 // NVT_IAC does not appear in the buffer. This is by far the most-common case.
00399                 //
00400                 if (pString == szString)
00401                 {
00402                     // Avoid copying to the static buffer, and just return the original buffer.
00403                     //
00404                     return szString;
00405                 }
00406                 else
00407                 {
00408                     strcpy(pBuffer, pString);
00409                     return Buffer;
00410                 }
00411             }
00412             else
00413             {
00414                 // Copy up to and including the IAC.
00415                 //
00416                 size_t n = p - pString + 1;
00417                 memcpy(pBuffer, pString, n);
00418                 pBuffer += n;
00419                 pString += n;
00420 
00421                 // Add another IAC.
00422                 //
00423                 *pBuffer++ = NVT_IAC;
00424             }
00425         }
00426     }
00427     *pBuffer = '\0';
00428     return Buffer;
00429 }
00430 
00431 void queue_string(DESC *d, const char *s)
00432 {
00433     const char *p = s;
00434 
00435     if (d->flags & DS_CONNECTED)
00436     {
00437         if (  !Ansi(d->player)
00438            && strchr(s, ESC_CHAR))
00439         {
00440             p = strip_ansi(p);
00441         }
00442         else if (NoBleed(d->player))
00443         {
00444             p = normal_to_white(p);
00445         }
00446 
00447         if (NoAccents(d->player))
00448         {
00449             p = strip_accents(p);
00450         }
00451     }
00452     else
00453     {
00454         if (strchr(s, ESC_CHAR))
00455         {
00456             p = strip_ansi(p);
00457         }
00458         p = strip_accents(p);
00459     }
00460     p = encode_iac(p);
00461     queue_write(d, p);
00462 }
00463 
00464 void freeqs(DESC *d)
00465 {
00466     TBLOCK *tb, *tnext;
00467     CBLK *cb, *cnext;
00468 
00469     tb = d->output_head;
00470     while (tb)
00471     {
00472         tnext = tb->hdr.nxt;
00473         MEMFREE(tb);
00474         tb = tnext;
00475     }
00476     d->output_head = NULL;
00477     d->output_tail = NULL;
00478 
00479     cb = d->input_head;
00480     while (cb)
00481     {
00482         cnext = (CBLK *) cb->hdr.nxt;
00483         free_lbuf(cb);
00484         cb = cnext;
00485     }
00486 
00487     d->input_head = NULL;
00488     d->input_tail = NULL;
00489 
00490     if (d->raw_input)
00491     {
00492         free_lbuf(d->raw_input);
00493     }
00494     d->raw_input = NULL;
00495 
00496     d->raw_input_at = NULL;
00497     d->nOption = 0;
00498     d->raw_input_state    = NVT_IS_NORMAL;
00499     d->nvt_sga_him_state  = OPTION_NO;
00500     d->nvt_sga_us_state   = OPTION_NO;
00501     d->nvt_eor_him_state  = OPTION_NO;
00502     d->nvt_eor_us_state   = OPTION_NO;
00503     d->nvt_naws_him_state = OPTION_NO;
00504     d->nvt_naws_us_state  = OPTION_NO;
00505     d->height = 24;
00506     d->width = 78;
00507 }
00508 
00509 /* ---------------------------------------------------------------------------
00510  * desc_addhash: Add a net descriptor to its player hash list.
00511  */
00512 
00513 void desc_addhash(DESC *d)
00514 {
00515     dbref player = d->player;
00516     DESC *hdesc = (DESC *)hashfindLEN(&player, sizeof(player), &mudstate.desc_htab);
00517     if (hdesc == NULL)
00518     {
00519         d->hashnext = NULL;
00520         hashaddLEN(&player, sizeof(player), d, &mudstate.desc_htab);
00521     }
00522     else
00523     {
00524         d->hashnext = hdesc;
00525         hashreplLEN(&player, sizeof(player), d, &mudstate.desc_htab);
00526     }
00527 }
00528 
00529 /* ---------------------------------------------------------------------------
00530  * desc_delhash: Remove a net descriptor from its player hash list.
00531  */
00532 
00533 static void desc_delhash(DESC *d)
00534 {
00535     dbref player = d->player;
00536     DESC *last = NULL;
00537     DESC *hdesc = (DESC *)hashfindLEN(&player, sizeof(player), &mudstate.desc_htab);
00538     while (hdesc != NULL)
00539     {
00540         if (d == hdesc)
00541         {
00542             if (last == NULL)
00543             {
00544                 if (d->hashnext == NULL)
00545                 {
00546                     hashdeleteLEN(&player, sizeof(player), &mudstate.desc_htab);
00547                 }
00548                 else
00549                 {
00550                     hashreplLEN(&player, sizeof(player), d->hashnext, &mudstate.desc_htab);
00551                 }
00552             }
00553             else
00554             {
00555                 last->hashnext = d->hashnext;
00556             }
00557             break;
00558         }
00559         last = hdesc;
00560         hdesc = hdesc->hashnext;
00561     }
00562     d->hashnext = NULL;
00563 }
00564 
00565 void welcome_user(DESC *d)
00566 {
00567     if (d->host_info & H_REGISTRATION)
00568     {
00569         fcache_dump(d, FC_CONN_REG);
00570     }
00571     else
00572     {
00573         fcache_dump(d, FC_CONN);
00574     }
00575 }
00576 
00577 void save_command(DESC *d, CBLK *command)
00578 {
00579     command->hdr.nxt = NULL;
00580     if (d->input_tail == NULL)
00581     {
00582         d->input_head = command;
00583 
00584         // We have added our first command to an empty list. Go process it later.
00585         //
00586         scheduler.DeferImmediateTask(PRIORITY_SYSTEM, Task_ProcessCommand, d, 0);
00587     }
00588     else
00589     {
00590         d->input_tail->hdr.nxt = command;
00591     }
00592     d->input_tail = command;
00593 }
00594 
00595 static void set_userstring(char **userstring, const char *command)
00596 {
00597     while (mux_isspace(*command))
00598     {
00599         command++;
00600     }
00601 
00602     if (!*command)
00603     {
00604         if (*userstring != NULL)
00605         {
00606             free_lbuf(*userstring);
00607             *userstring = NULL;
00608         }
00609     }
00610     else
00611     {
00612         if (*userstring == NULL)
00613         {
00614             *userstring = alloc_lbuf("set_userstring");
00615         }
00616         strcpy(*userstring, command);
00617     }
00618 }
00619 
00620 static void parse_connect(const char *msg, char *command, char *user, char *pass)
00621 {
00622     if (strlen(msg) > MBUF_SIZE)
00623     {
00624         *command = '\0';
00625         *user = '\0';
00626         *pass = '\0';
00627         return;
00628     }
00629     while (mux_isspace(*msg))
00630     {
00631         msg++;
00632     }
00633     char *p = command;
00634     while (  *msg
00635           && !mux_isspace(*msg))
00636     {
00637         *p++ = *msg++;
00638     }
00639     *p = '\0';
00640     while (mux_isspace(*msg))
00641     {
00642         msg++;
00643     }
00644     p = user;
00645     if (  mudconf.name_spaces
00646        && *msg == '\"')
00647     {
00648         for (; *msg && (*msg == '\"' || mux_isspace(*msg)); msg++)
00649         {
00650             // Nothing.
00651         }
00652         while (  *msg
00653               && *msg != '\"')
00654         {
00655             while (  *msg
00656                   && !mux_isspace(*msg)
00657                   && *msg != '\"')
00658             {
00659                 *p++ = *msg++;
00660             }
00661 
00662             if (*msg == '\"')
00663             {
00664                 break;
00665             }
00666 
00667             while (mux_isspace(*msg))
00668             {
00669                 msg++;
00670             }
00671 
00672             if (  *msg
00673                && *msg != '\"')
00674             {
00675                 *p++ = ' ';
00676             }
00677         }
00678         while (  *msg
00679               && *msg == '\"')
00680         {
00681              msg++;
00682         }
00683     }
00684     else
00685     {
00686         while (  *msg
00687               && !mux_isspace(*msg))
00688         {
00689             *p++ = *msg++;
00690         }
00691     }
00692     *p = '\0';
00693     while (mux_isspace(*msg))
00694     {
00695         msg++;
00696     }
00697     p = pass;
00698     while (  *msg
00699           && !mux_isspace(*msg))
00700     {
00701         *p++ = *msg++;
00702     }
00703     *p = '\0';
00704 }
00705 
00706 static void announce_connect(dbref player, DESC *d)
00707 {
00708     desc_addhash(d);
00709 
00710     DESC *dtemp;
00711     int count = 0;
00712     DESC_ITER_CONN(dtemp)
00713     {
00714         count++;
00715     }
00716 
00717     if (mudstate.record_players < count)
00718     {
00719         mudstate.record_players = count;
00720     }
00721 
00722     char *buf = alloc_lbuf("announce_connect");
00723     dbref aowner;
00724     int aflags;
00725     size_t nLen;
00726     atr_pget_str_LEN(buf, player, A_TIMEOUT, &aowner, &aflags, &nLen);
00727     if (nLen)
00728     {
00729         d->timeout = mux_atol(buf);
00730         if (d->timeout <= 0)
00731         {
00732             d->timeout = mudconf.idle_timeout;
00733         }
00734     }
00735 
00736     dbref loc = Location(player);
00737     s_Connected(player);
00738 
00739     if (d->flags & DS_PUEBLOCLIENT)
00740     {
00741         s_Html(player);
00742     }
00743 
00744     raw_notify( player, tprintf("\n%sMOTD:%s %s\n", ANSI_HILITE,
00745                 ANSI_NORMAL, mudconf.motd_msg));
00746 
00747     if (Wizard(player))
00748     {
00749         raw_notify(player, tprintf("%sWIZMOTD:%s %s\n", ANSI_HILITE,
00750             ANSI_NORMAL, mudconf.wizmotd_msg));
00751 
00752         if (!(mudconf.control_flags & CF_LOGIN))
00753         {
00754             raw_notify(player, "*** Logins are disabled.");
00755         }
00756     }
00757     atr_get_str_LEN(buf, player, A_LPAGE, &aowner, &aflags, &nLen);
00758     if (nLen)
00759     {
00760         raw_notify(player, "Your PAGE LOCK is set.  You may be unable to receive some pages.");
00761     }
00762     int num = 0;
00763     DESC_ITER_PLAYER(player, dtemp)
00764     {
00765         num++;
00766     }
00767 
00768     // Reset vacation flag.
00769     //
00770     s_Flags(player, FLAG_WORD2, Flags2(player) & ~VACATION);
00771 
00772     char *pRoomAnnounceFmt;
00773     char *pMonitorAnnounceFmt;
00774     if (num < 2)
00775     {
00776         pRoomAnnounceFmt = "%s has connected.";
00777         if (mudconf.have_comsys)
00778         {
00779             do_comconnect(player);
00780         }
00781         if (  Hidden(player)
00782            && Can_Hide(player))
00783         {
00784             pMonitorAnnounceFmt = "GAME: %s has DARK-connected.";
00785         }
00786         else
00787         {
00788             pMonitorAnnounceFmt = "GAME: %s has connected.";
00789         }
00790         if (  Suspect(player)
00791            || (d->host_info & H_SUSPECT))
00792         {
00793             raw_broadcast(WIZARD, "[Suspect] %s has connected.", Moniker(player));
00794         }
00795     }
00796     else
00797     {
00798         pRoomAnnounceFmt = "%s has reconnected.";
00799         pMonitorAnnounceFmt = "GAME: %s has reconnected.";
00800         if (  Suspect(player)
00801            || (d->host_info & H_SUSPECT))
00802         {
00803             raw_broadcast(WIZARD, "[Suspect] %s has reconnected.", Moniker(player));
00804         }
00805     }
00806     sprintf(buf, pRoomAnnounceFmt, Moniker(player));
00807     raw_broadcast(MONITOR, pMonitorAnnounceFmt, Moniker(player));
00808 
00809     int key = MSG_INV;
00810     if (  loc != NOTHING
00811        && !(  Hidden(player)
00812            && Can_Hide(player)))
00813     {
00814         key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST);
00815     }
00816 
00817     dbref temp = mudstate.curr_enactor;
00818     mudstate.curr_enactor = player;
00819 #ifdef REALITY_LVLS
00820     if(loc == NOTHING)
00821         notify_check(player, player, buf, key);
00822     else
00823         notify_except_rlevel(loc, player, player, buf, 0);
00824 #else
00825     notify_check(player, player, buf, key);
00826 #endif /* REALITY_LVLS */
00827     atr_pget_str_LEN(buf, player, A_ACONNECT, &aowner, &aflags, &nLen);
00828     CLinearTimeAbsolute lta;
00829     dbref zone, obj;
00830     if (nLen)
00831     {
00832         wait_que(player, player, player, false, lta, NOTHING, 0, buf,
00833             (char **)NULL, 0, NULL);
00834     }
00835     if (mudconf.master_room != NOTHING)
00836     {
00837         atr_pget_str_LEN(buf, mudconf.master_room, A_ACONNECT, &aowner,
00838             &aflags, &nLen);
00839         if (nLen)
00840         {
00841             wait_que(mudconf.master_room, player, player, false, lta,
00842                 NOTHING, 0, buf, (char **)NULL, 0, NULL);
00843         }
00844         DOLIST(obj, Contents(mudconf.master_room))
00845         {
00846             atr_pget_str_LEN(buf, obj, A_ACONNECT, &aowner, &aflags, &nLen);
00847             if (nLen)
00848             {
00849                 wait_que(obj, player, player, false, lta, NOTHING, 0, buf,
00850                     (char **)NULL, 0, NULL);
00851             }
00852         }
00853     }
00854 
00855     // Do the zone of the player's location's possible aconnect.
00856     //
00857     if (  mudconf.have_zones
00858        && Good_obj(zone = Zone(loc)))
00859     {
00860         switch (Typeof(zone))
00861         {
00862         case TYPE_THING:
00863 
00864             atr_pget_str_LEN(buf, zone, A_ACONNECT, &aowner, &aflags, &nLen);
00865             if (nLen)
00866             {
00867                 wait_que(zone, player, player, false, lta, NOTHING, 0, buf,
00868                     (char **)NULL, 0, NULL);
00869             }
00870             break;
00871 
00872         case TYPE_ROOM:
00873 
00874             // check every object in the room for a connect action.
00875             //
00876             DOLIST(obj, Contents(zone))
00877             {
00878                 atr_pget_str_LEN(buf, obj, A_ACONNECT, &aowner, &aflags,
00879                     &nLen);
00880                 if (nLen)
00881                 {
00882                     wait_que(obj, player, player, false, lta, NOTHING, 0,
00883                         buf, (char **)NULL, 0, NULL);
00884                 }
00885             }
00886             break;
00887 
00888         default:
00889 
00890             log_text(tprintf("Invalid zone #%d for %s(#%d) has bad type %d",
00891                 zone, Name(player), player, Typeof(zone)));
00892         }
00893     }
00894     free_lbuf(buf);
00895     CLinearTimeAbsolute ltaNow;
00896     ltaNow.GetLocal();
00897     char *time_str = ltaNow.ReturnDateString(7);
00898 
00899     record_login(player, true, time_str, d->addr, d->username,
00900         inet_ntoa((d->address).sin_addr));
00901     if (mudconf.have_mailer)
00902     {
00903         check_mail(player, 0, false);
00904     }
00905     look_in(player, Location(player), (LK_SHOWEXIT|LK_OBEYTERSE|LK_SHOWVRML));
00906     mudstate.curr_enactor = temp;
00907     if (Guest(player))
00908     {
00909         db[player].fs.word[FLAG_WORD1] &= ~DARK;
00910     }
00911 }
00912 
00913 void announce_disconnect(dbref player, DESC *d, const char *reason)
00914 {
00915     int num = 0, key;
00916     DESC *dtemp;
00917     DESC_ITER_PLAYER(player, dtemp)
00918     {
00919         num++;
00920     }
00921 
00922     dbref temp = mudstate.curr_enactor;
00923     mudstate.curr_enactor = player;
00924     dbref loc = Location(player);
00925 
00926     if (num < 2)
00927     {
00928         if (  Suspect(player)
00929            || (d->host_info & H_SUSPECT))
00930         {
00931             raw_broadcast(WIZARD, "[Suspect] %s has disconnected.", Moniker(player));
00932         }
00933         char *buf = alloc_lbuf("announce_disconnect.only");
00934 
00935         sprintf(buf, "%s has disconnected.", Moniker(player));
00936         key = MSG_INV;
00937         if (  loc != NOTHING
00938            && !(  Hidden(player)
00939                && Can_Hide(player)))
00940         {
00941             key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST);
00942         }
00943 #ifdef REALITY_LVLS
00944         if(loc == NOTHING)
00945             notify_check(player, player, buf, key);
00946         else
00947             notify_except_rlevel(loc, player, player, buf, 0);
00948 #else
00949         notify_check(player, player, buf, key);
00950 #endif /* REALITY_LVLS */
00951 
00952         if (mudconf.have_mailer)
00953         {
00954             do_mail_purge(player);
00955         }
00956 
00957         raw_broadcast(MONITOR, "GAME: %s has disconnected. <%s>", Moniker(player), reason);
00958 
00959         c_Connected(player);
00960 
00961         if (mudconf.have_comsys)
00962         {
00963             do_comdisconnect(player);
00964         }
00965 
00966         dbref aowner, zone, obj;
00967         int aflags;
00968         size_t nLen;
00969         char *argv[1];
00970         argv[0] = (char *)reason;
00971         CLinearTimeAbsolute lta;
00972         atr_pget_str_LEN(buf, player, A_ADISCONNECT, &aowner, &aflags, &nLen);
00973         if (nLen)
00974         {
00975             wait_que(player, player, player, false, lta, NOTHING, 0, buf,
00976                 argv, 1, NULL);
00977         }
00978         if (mudconf.master_room != NOTHING)
00979         {
00980             atr_pget_str_LEN(buf, mudconf.master_room, A_ADISCONNECT, &aowner,
00981                 &aflags, &nLen);
00982             if (nLen)
00983             {
00984                 wait_que(mudconf.master_room, player, player, false, lta,
00985                     NOTHING, 0, buf, (char **)NULL, 0, NULL);
00986             }
00987             DOLIST(obj, Contents(mudconf.master_room))
00988             {
00989                 atr_pget_str_LEN(buf, obj, A_ADISCONNECT, &aowner, &aflags,
00990                     &nLen);
00991                 if (nLen)
00992                 {
00993                     wait_que(obj, player, player, false, lta, NOTHING, 0,
00994                         buf, (char **)NULL, 0, NULL);
00995                 }
00996             }
00997         }
00998 
00999         // Do the zone of the player's location's possible adisconnect.
01000         //
01001         if (mudconf.have_zones && Good_obj(zone = Zone(loc)))
01002         {
01003             switch (Typeof(zone))
01004             {
01005             case TYPE_THING:
01006 
01007                 atr_pget_str_LEN(buf, zone, A_ADISCONNECT, &aowner, &aflags,
01008                     &nLen);
01009                 if (nLen)
01010                 {
01011                     wait_que(zone, player, player, false, lta, NOTHING, 0,
01012                         buf, (char **)NULL, 0, NULL);
01013                 }
01014                 break;
01015 
01016             case TYPE_ROOM:
01017 
01018                 // check every object in the room for a connect action.
01019                 //
01020                 DOLIST(obj, Contents(zone))
01021                 {
01022                     atr_pget_str_LEN(buf, obj, A_ADISCONNECT, &aowner, &aflags,
01023                         &nLen);
01024                     if (nLen)
01025                     {
01026                         wait_que(obj, player, player, false, lta, NOTHING,
01027                             0, buf, (char **)NULL, 0, NULL);
01028                     }
01029                 }
01030                 break;
01031 
01032             default:
01033                 log_text(tprintf("Invalid zone #%d for %s(#%d) has bad type %d",
01034                     zone, Name(player), player, Typeof(zone)));
01035             }
01036         }
01037         free_lbuf(buf);
01038         if (d->flags & DS_AUTODARK)
01039         {
01040             d->flags &= ~DS_AUTODARK;
01041             db[player].fs.word[FLAG_WORD1] &= ~DARK;
01042         }
01043 
01044         if (Guest(player))
01045         {
01046             db[player].fs.word[FLAG_WORD1] |= DARK;
01047             halt_que(NOTHING, player);
01048         }
01049     }
01050     else
01051     {
01052         if (  Suspect(player)
01053            || (d->host_info & H_SUSPECT))
01054         {
01055             raw_broadcast(WIZARD, "[Suspect] %s has partially disconnected.", Moniker(player));
01056         }
01057         char *mbuf = alloc_mbuf("announce_disconnect.partial");
01058         sprintf(mbuf, "%s has partially disconnected.", Moniker(player));
01059         key = MSG_INV;
01060         if (  loc != NOTHING
01061            && !(  Hidden(player)
01062                && Can_Hide(player)))
01063         {
01064             key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST);
01065         }
01066 #ifdef REALITY_LVLS
01067         if(loc == NOTHING)
01068             notify_check(player, player, mbuf, key);
01069         else
01070             notify_except_rlevel(loc, player, player, mbuf, 0);
01071 #else
01072         notify_check(player, player, mbuf, key);
01073 #endif /* REALITY_LVLS */
01074         raw_broadcast(MONITOR, "GAME: %s has partially disconnected.",
01075             Moniker(player));
01076         free_mbuf(mbuf);
01077     }
01078 
01079     mudstate.curr_enactor = temp;
01080     desc_delhash(d);
01081 
01082     local_disconnect(player, num);
01083 }
01084 
01085 int boot_off(dbref player, const char *message)
01086 {
01087     DESC *d, *dnext;
01088     int count = 0;
01089     DESC_SAFEITER_PLAYER(player, d, dnext)
01090     {
01091         if (message && *message)
01092         {
01093             queue_string(d, message);
01094             queue_write_LEN(d, "\r\n", 2);
01095         }
01096         shutdownsock(d, R_BOOT);
01097         count++;
01098     }
01099     return count;
01100 }
01101 
01102 int boot_by_port(SOCKET port, bool bGod, const char *message)
01103 {
01104     DESC *d, *dnext;
01105     int count = 0;
01106     DESC_SAFEITER_ALL(d, dnext)
01107     {
01108         if (  d->descriptor == port
01109            && (  bGod
01110               || !(d->flags & DS_CONNECTED)
01111               || !God(d->player)))
01112         {
01113             if (  message
01114                && *message)
01115             {
01116                 queue_string(d, message);
01117                 queue_write_LEN(d, "\r\n", 2);
01118             }
01119             shutdownsock(d, R_BOOT);
01120             count++;
01121         }
01122     }
01123     return count;
01124 }
01125 
01126 /* ---------------------------------------------------------------------------
01127  * desc_reload: Reload parts of net descriptor that are based on db info.
01128  */
01129 
01130 void desc_reload(dbref player)
01131 {
01132     DESC *d;
01133     char *buf;
01134     dbref aowner;
01135     FLAG aflags;
01136 
01137     DESC_ITER_PLAYER(player, d)
01138     {
01139         buf = atr_pget(player, A_TIMEOUT, &aowner, &aflags);
01140         if (buf)
01141         {
01142             d->timeout = mux_atol(buf);
01143             if (d->timeout <= 0)
01144             {
01145                 d->timeout = mudconf.idle_timeout;
01146             }
01147         }
01148         free_lbuf(buf);
01149     }
01150 }
01151 
01152 // ---------------------------------------------------------------------------
01153 // fetch_session: Return number of sessions (or 0 if not logged in).
01154 //
01155 int fetch_session(dbref target)
01156 {
01157     DESC *d;
01158     int nCount = 0;
01159     DESC_ITER_PLAYER(target, d)
01160     {
01161         nCount++;
01162     }
01163     return nCount;
01164 }
01165 
01166 static DESC *find_least_idle(dbref target)
01167 {
01168     CLinearTimeAbsolute ltaNewestLastTime;
01169 
01170     DESC *d;
01171     DESC *dLeastIdle = NULL;
01172     DESC_ITER_PLAYER(target, d)
01173     {
01174         if (  NULL == dLeastIdle
01175            || ltaNewestLastTime < d->last_time)
01176         {
01177             dLeastIdle = d;
01178             ltaNewestLastTime = d->last_time;
01179         }
01180     }
01181     return dLeastIdle;
01182 }
01183 
01184 int fetch_height(dbref target)
01185 {
01186     DESC *d = find_least_idle(target);
01187     if (NULL != d)
01188     {
01189         return d->height;
01190     }
01191     return 24;
01192 }
01193 
01194 int fetch_width(dbref target)
01195 {
01196     DESC *d = find_least_idle(target);
01197     if (NULL != d)
01198     {
01199         return d->width;
01200     }
01201     return 78;
01202 }
01203 
01204 // ---------------------------------------------------------------------------
01205 // fetch_idle: Return smallest idle time for a player (or -1 if not logged in).
01206 //
01207 int fetch_idle(dbref target)
01208 {
01209     CLinearTimeAbsolute ltaNow;
01210     ltaNow.GetUTC();
01211 
01212     DESC *d = find_least_idle(target);
01213     if (NULL != d)
01214     {
01215         CLinearTimeDelta ltdResult;
01216         ltdResult = ltaNow - d->last_time;
01217         return ltdResult.ReturnSeconds();
01218     }
01219     else
01220     {
01221         return -1;
01222     }
01223 }
01224 
01225 // ---------------------------------------------------------------------------
01226 // find_oldest: Return descriptor with the oldeset connected_at (or NULL if
01227 // not logged in).
01228 //
01229 void find_oldest(dbref target, DESC *dOldest[2])
01230 {
01231     dOldest[0] = NULL;
01232     dOldest[1] = NULL;
01233 
01234     DESC *d;
01235     bool bFound = false;
01236     DESC_ITER_PLAYER(target, d)
01237     {
01238         if (  !bFound
01239            || d->connected_at < dOldest[0]->connected_at)
01240         {
01241             bFound = true;
01242             dOldest[1] = dOldest[0];
01243             dOldest[0] = d;
01244         }
01245     }
01246 }
01247 
01248 // ---------------------------------------------------------------------------
01249 // fetch_connect: Return largest connect time for a player (or -1 if not
01250 // logged in).
01251 //
01252 int fetch_connect(dbref target)
01253 {
01254     DESC *dOldest[2];
01255     find_oldest(target, dOldest);
01256     if (dOldest[0])
01257     {
01258         CLinearTimeAbsolute ltaNow;
01259         CLinearTimeDelta ltdOldest;
01260 
01261         ltaNow.GetUTC();
01262         ltdOldest = ltaNow - dOldest[0]->connected_at;
01263         return ltdOldest.ReturnSeconds();
01264     }
01265     else
01266     {
01267         return -1;
01268     }
01269 }
01270 
01271 // A NOTE about AUTODARK: It only works for wizard players. Wizard players
01272 // are automatically set DARK if they are not already set DARK and they have
01273 // no session which is unidle.
01274 //
01275 // The AUTODARK state is cleared when at least one session becomes unidle.
01276 // The AUTODARK state is also cleared when the last idle session is
01277 // disconnected from the server (session shutdown or @shutdown).
01278 //
01279 void check_idle(void)
01280 {
01281     DESC *d, *dnext;
01282 
01283     CLinearTimeAbsolute ltaNow;
01284     ltaNow.GetUTC();
01285 
01286     DESC_SAFEITER_ALL(d, dnext)
01287     {
01288         if (  (d->flags & DS_CONNECTED)
01289            && KeepAlive(d->player))
01290         {
01291             // Send a Telnet NOP code - creates traffic to keep NAT routers
01292             // happy.  Hopefully this only runs once a minute.
01293             //
01294             const char aNOP[2] = { NVT_IAC, NVT_NOP };
01295             queue_write_LEN(d, aNOP, sizeof(aNOP));
01296         }
01297         if (d->flags & DS_AUTODARK)
01298         {
01299             continue;
01300         }
01301         if (d->flags & DS_CONNECTED)
01302         {
01303             if (mudconf.idle_timeout <= 0)
01304             {
01305                 // Idle timeout checking on connected players is effectively disabled.
01306                 // PennMUSH uses idle_timeout == 0. Rhost uses idel_timeout == -1.
01307                 // We will be disabled for either setting.
01308                 //
01309                 continue;
01310             }
01311 
01312             CLinearTimeDelta ltdIdle = ltaNow - d->last_time;
01313             if (Can_Idle(d->player))
01314             {
01315                 if (  mudconf.idle_wiz_dark
01316                    && (Flags(d->player) & (WIZARD|DARK)) == WIZARD
01317                    && ltdIdle.ReturnSeconds() > mudconf.idle_timeout)
01318                 {
01319                     // Make sure this Wizard player does not have some other
01320                     // active session.
01321                     //
01322                     DESC *d1;
01323                     bool bFound = false;
01324                     DESC_ITER_PLAYER(d->player, d1)
01325                     {
01326                         if (d1 != d)
01327                         {
01328                             CLinearTimeDelta ltd = ltaNow - d1->last_time;
01329                             if (ltd.ReturnSeconds() <= mudconf.idle_timeout)
01330                             {
01331                                  bFound = true;
01332                                  break;
01333                             }
01334                         }
01335                     }
01336                     if (!bFound)
01337                     {
01338                         db[d->player].fs.word[FLAG_WORD1] |= DARK;
01339                         DESC_ITER_PLAYER(d->player, d1)
01340                         {
01341                             d1->flags |= DS_AUTODARK;
01342                         }
01343                     }
01344                 }
01345             }
01346             else if (ltdIdle.ReturnSeconds() > d->timeout)
01347             {
01348                 queue_write(d, "*** Inactivity Timeout ***\r\n");
01349                 shutdownsock(d, R_TIMEOUT);
01350             }
01351         }
01352         else if (0 < mudconf.conn_timeout)
01353         {
01354             CLinearTimeDelta ltdIdle = ltaNow - d->connected_at;
01355             if (ltdIdle.ReturnSeconds() > mudconf.conn_timeout)
01356             {
01357                 queue_write(d, "*** Login Timeout ***\r\n");
01358                 shutdownsock(d, R_TIMEOUT);
01359             }
01360         }
01361     }
01362 }
01363 
01364 void check_events(void)
01365 {
01366     dbref thing, parent;
01367     int lev;
01368 
01369     CLinearTimeAbsolute ltaNow;
01370     ltaNow.GetLocal();
01371 
01372     FIELDEDTIME ft;
01373     if (!ltaNow.ReturnFields(&ft))
01374     {
01375         return;
01376     }
01377 
01378     // Resetting every midnight.
01379     //
01380     static int iLastHourChecked = 25;
01381     if (  iLastHourChecked == 23
01382        && ft.iHour < iLastHourChecked)
01383     {
01384         mudstate.events_flag &= ~ET_DAILY;
01385     }
01386     iLastHourChecked = ft.iHour;
01387 
01388     if (  ft.iHour == mudconf.events_daily_hour
01389        && !(mudstate.events_flag & ET_DAILY))
01390     {
01391         mudstate.events_flag |= ET_DAILY;
01392         DO_WHOLE_DB(thing)
01393         {
01394             if (Going(thing))
01395             {
01396                 continue;
01397             }
01398 
01399             ITER_PARENTS(thing, parent, lev)
01400             {
01401                 if (Flags2(thing) & HAS_DAILY)
01402                 {
01403                     did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_DAILY, (char **)NULL, 0);
01404                     break;
01405                 }
01406             }
01407         }
01408     }
01409 
01410 }
01411 
01412 #define MAX_TRIMMED_NAME_LENGTH 16
01413 static const char *trimmed_name(dbref player, int *pvw)
01414 {
01415     static char cbuff[MBUF_SIZE];
01416 
01417     ANSI_TruncateToField(
01418         Moniker(player),
01419         sizeof(cbuff),
01420         cbuff,
01421         MAX_TRIMMED_NAME_LENGTH,
01422         pvw,
01423         ANSI_ENDGOAL_NORMAL
01424     );
01425     return cbuff;
01426 }
01427 
01428 static char *trimmed_site(char *szName)
01429 {
01430     static char buff[MBUF_SIZE];
01431 
01432     unsigned int nLen = strlen(szName);
01433     if (  mudconf.site_chars <= 0
01434        || nLen <= mudconf.site_chars)
01435     {
01436         return szName;
01437     }
01438     nLen = mudconf.site_chars;
01439     if (nLen > sizeof(buff)-1)
01440     {
01441         nLen = sizeof(buff)-1;
01442     }
01443     memcpy(buff, szName, nLen);
01444     buff[nLen] = '\0';
01445     return buff;
01446 }
01447 
01448 static void dump_users(DESC *e, char *match, int key)
01449 {
01450     DESC *d;
01451     int count;
01452     char *buf, *fp, *sp, flist[4], slist[4];
01453     dbref room_it;
01454 
01455     if (match)
01456     {
01457         while (mux_isspace(*match))
01458         {
01459             match++;
01460         }
01461 
01462         if (!*match)
01463         {
01464             match = NULL;
01465         }
01466     }
01467 
01468     if (  (e->flags & (DS_PUEBLOCLIENT|DS_CONNECTED))
01469        && Html(e->player))
01470     {
01471         queue_write(e, "<pre>");
01472     }
01473 
01474     buf = alloc_mbuf("dump_users");
01475     if (key == CMD_SESSION)
01476     {
01477         queue_write(e, "                               ");
01478         queue_write(e, "     Characters Input----  Characters Output---\r\n");
01479     }
01480     queue_write(e, "Player Name        On For Idle ");
01481     if (key == CMD_SESSION)
01482     {
01483         queue_write(e, "Port Pend  Lost     Total  Pend  Lost     Total\r\n");
01484     }
01485     else if (  (e->flags & DS_CONNECTED)
01486             && Wizard_Who(e->player)
01487             && key == CMD_WHO)
01488     {
01489         queue_write(e, "  Room    Cmds   Host\r\n");
01490     }
01491     else
01492     {
01493         if (  Wizard_Who(e->player)
01494            || See_Hidden(e->player))
01495         {
01496             queue_write(e, "  ");
01497         }
01498         else
01499         {
01500             queue_write(e, " ");
01501         }
01502         queue_string(e, mudstate.doing_hdr);
01503         queue_write_LEN(e, "\r\n", 2);
01504     }
01505     count = 0;
01506 
01507     CLinearTimeAbsolute ltaNow;
01508     ltaNow.GetUTC();
01509 
01510     DESC_ITER_ALL(d)
01511     {
01512         if (!(  (  (e->flags & DS_CONNECTED)
01513                 && SiteMon(e->player))
01514              || (d->flags & DS_CONNECTED)))
01515         {
01516             continue;
01517         }
01518         if (  !(d->flags & DS_CONNECTED)
01519            || !Hidden(d->player)
01520            || (  (e->flags & DS_CONNECTED)
01521               && (  Wizard_Who(e->player)
01522                  || See_Hidden(e->player))))
01523         {
01524             count++;
01525             if (  match
01526                && (  !(d->flags & DS_CONNECTED)
01527                   || string_prefix(Name(d->player), match) == 0))
01528             {
01529                 continue;
01530             }
01531             if (  key == CMD_SESSION
01532                && (  !(e->flags & DS_CONNECTED)
01533                   || !Wizard_Who(e->player))
01534                && (  !(e->flags & DS_CONNECTED)
01535                   || !(d->flags & DS_CONNECTED)
01536                   || d->player != e->player))
01537             {
01538                 continue;
01539             }
01540 
01541             // Get choice flags for wizards.
01542             //
01543             fp = flist;
01544             sp = slist;
01545             if (  (e->flags & DS_CONNECTED)
01546                && Wizard_Who(e->player))
01547             {
01548                 if (  (d->flags & DS_CONNECTED)
01549                    && Hidden(d->player))
01550                 {
01551                     if (d->flags & DS_AUTODARK)
01552                     {
01553                         *fp++ = 'd';
01554                     }
01555                     else
01556                     {
01557                         *fp++ = 'D';
01558                     }
01559                 }
01560                 if (d->flags & DS_CONNECTED)
01561                 {
01562                     if (Hideout(d->player))
01563                     {
01564                         *fp++ = 'U';
01565                     }
01566                     else
01567                     {
01568                         room_it = where_room(d->player);
01569                         if (Good_obj(room_it))
01570                         {
01571                             if (Hideout(room_it))
01572                             {
01573                                 *fp++ = 'u';
01574                             }
01575                         }
01576                         else
01577                         {
01578                             *fp++ = 'u';
01579                         }
01580                     }
01581 
01582                     if (Suspect(d->player))
01583                     {
01584                         *fp++ = '+';
01585                     }
01586                 }
01587                 if (d->host_info & H_FORBIDDEN)
01588                 {
01589                     *sp++ = 'F';
01590                 }
01591                 if (d->host_info & H_REGISTRATION)
01592                 {
01593                     *sp++ = 'R';
01594                 }
01595                 if (d->host_info & H_SUSPECT)
01596                 {
01597                     *sp++ = '+';
01598                 }
01599                 if (d->host_info & H_GUEST)
01600                 {
01601                     *sp++ = 'G';
01602                 }
01603             }
01604             else if (  (e->flags & DS_CONNECTED)
01605                     && (d->flags & DS_CONNECTED)
01606                     && See_Hidden(e->player)
01607                     && Hidden(d->player))
01608             {
01609                 if (d->flags & DS_AUTODARK)
01610                 {
01611                     *fp++ = 'd';
01612                 }
01613                 else
01614                 {
01615                     *fp++ = 'D';
01616                 }
01617             }
01618             *fp = '\0';
01619             *sp = '\0';
01620 
01621             CLinearTimeDelta ltdConnected = ltaNow - d->connected_at;
01622             CLinearTimeDelta ltdLastTime  = ltaNow - d->last_time;
01623 
01624             const char *pNameField = "<Unconnected>";
01625             int vwNameField = strlen(pNameField);
01626             if (d->flags & DS_CONNECTED)
01627             {
01628                 pNameField = trimmed_name(d->player, &vwNameField);
01629             }
01630 
01631             // How many spaces between the name field and the 'On For' field.
01632             //
01633             size_t nFill;
01634             if (13 <= vwNameField)
01635             {
01636                 nFill = 1;
01637             }
01638             else
01639             {
01640                 nFill = 14-vwNameField;
01641             }
01642             char aFill[15];
01643             memset(aFill, ' ', nFill);
01644             aFill[nFill] = '\0';
01645 
01646             // The width size allocated to the 'On For' field.
01647             //
01648             size_t nOnFor = 25 - nFill - vwNameField;
01649 
01650             const char *pTimeStamp1 = time_format_1(ltdConnected.ReturnSeconds(), nOnFor);
01651             const char *pTimeStamp2 = time_format_2(ltdLastTime.ReturnSeconds());
01652 
01653             if (  (e->flags & DS_CONNECTED)
01654                && Wizard_Who(e->player)
01655                && key == CMD_WHO)
01656             {
01657                 sprintf(buf, "%s%s%s %4s%-3s#%-6d%5d%3s%s\r\n",
01658                     pNameField, aFill,
01659                     pTimeStamp1,
01660                     pTimeStamp2,
01661                     flist,
01662                     ((d->flags & DS_CONNECTED) ? Location(d->player) : -1),
01663                     d->command_count,
01664                     slist,
01665                     trimmed_site(((d->username[0] != '\0') ? tprintf("%s@%s", d->username, d->addr) : d->addr)));
01666             }
01667             else if (key == CMD_SESSION)
01668             {
01669                 sprintf(buf, "%s%s%s %4s%5u%5d%6d%10d%6d%6d%10d\r\n",
01670                     pNameField, aFill,
01671                     pTimeStamp1,
01672                     pTimeStamp2,
01673                     d->descriptor,
01674                     d->input_size, d->input_lost,
01675                     d->input_tot,
01676                     d->output_size, d->output_lost,
01677                     d->output_tot);
01678             }
01679             else if (  Wizard_Who(e->player)
01680                     || See_Hidden(e->player))
01681             {
01682                 sprintf(buf, "%s%s%s %4s%-3s%s\r\n",
01683                     pNameField, aFill,
01684                     pTimeStamp1,
01685                     pTimeStamp2,
01686                     flist,
01687                     d->doing);
01688             }
01689             else
01690             {
01691                 sprintf(buf, "%s%s%s %4s  %s\r\n",
01692                     pNameField, aFill,
01693                     pTimeStamp1,
01694                     pTimeStamp2,
01695                     d->doing);
01696             }
01697             queue_string(e, buf);
01698         }
01699     }
01700 
01701     // Sometimes I like the ternary operator.
01702     //
01703     sprintf(buf, "%d Player%slogged in, %d record, %s maximum.\r\n", count,
01704         (count == 1) ? " " : "s ", mudstate.record_players,
01705         (mudconf.max_players == -1) ? "no" : mux_ltoa_t(mudconf.max_players));
01706     queue_write(e, buf);
01707 
01708     if (  (e->flags & (DS_PUEBLOCLIENT|DS_CONNECTED))
01709        && Html(e->player))
01710     {
01711         queue_write(e, "</pre>");
01712     }
01713     free_mbuf(buf);
01714 }
01715 
01716 #ifdef WOD_REALMS
01717 #define INFO_VERSION "1.1"
01718 #else // WOD_REALMS
01719 #define INFO_VERSION "1"
01720 #endif // WOD_REALMS
01721 
01722 static void dump_info(DESC *arg_desc)
01723 {
01724     queue_write(arg_desc, "### Begin INFO " INFO_VERSION "\r\n");
01725 
01726     queue_string(arg_desc, tprintf("Name: %s\r\n", mudconf.mud_name));
01727 
01728     char *temp = mudstate.start_time.ReturnDateString();
01729     queue_write(arg_desc, tprintf("Uptime: %s\r\n", temp));
01730 
01731     DESC *d;
01732     int count = 0;
01733     DESC_ITER_CONN(d)
01734     {
01735         if (!Good_obj(d->player))
01736         {
01737             continue;
01738         }
01739         if (  !Hidden(d->player)
01740            || (  (arg_desc->flags & DS_CONNECTED)
01741               && See_Hidden(arg_desc->player)))
01742         {
01743             count++;
01744         }
01745     }
01746     queue_write(arg_desc, tprintf("Connected: %d\r\n", count));
01747     queue_write(arg_desc, tprintf("Size: %d\r\n", mudstate.db_top));
01748     queue_write(arg_desc, tprintf("Version: %s\r\n", mudstate.short_ver));
01749 #ifdef WOD_REALMS
01750     queue_write(arg_desc, tprintf("Patches: WOD_REALMS\r\n"));
01751 #endif // WOD_REALMS
01752     queue_write(arg_desc, "### End INFO\r\n");
01753 }
01754 
01755 static char *MakeCanonicalDoing(char *pDoing, int *pnValidDoing, bool *pbValidDoing)
01756 {
01757     *pnValidDoing = 0;
01758     *pbValidDoing = false;
01759 
01760     if (!pDoing)
01761     {
01762         return NULL;
01763     }
01764 
01765     // First, remove all '\r\n\t' from the string.
01766     //
01767     char *Buffer = RemoveSetOfCharacters(pDoing, "\r\n\t");
01768 
01769     // Optimize/terminate any ANSI in the string.
01770     //
01771     int nVisualWidth;
01772     static char szFittedDoing[SIZEOF_DOING_STRING];
01773     *pnValidDoing = ANSI_TruncateToField
01774                     ( Buffer,
01775                       SIZEOF_DOING_STRING,
01776                       szFittedDoing,
01777                       WIDTHOF_DOING_STRING,
01778                       &nVisualWidth,
01779                       ANSI_ENDGOAL_NORMAL
01780                     );
01781     *pbValidDoing = true;
01782     return szFittedDoing;
01783 }
01784 
01785 // ---------------------------------------------------------------------------
01786 // do_doing: Set the doing string that appears in the WHO report.
01787 // Idea from R'nice@TinyTIM.
01788 //
01789 void do_doing(dbref executor, dbref caller, dbref enactor, int key, char *arg)
01790 {
01791     UNUSED_PARAMETER(caller);
01792     UNUSED_PARAMETER(enactor);
01793 
01794     // Make sure there can be no embedded newlines from %r
01795     //
01796     static char *Empty = "";
01797     char *szValidDoing = Empty;
01798     bool bValidDoing;
01799     int nValidDoing = 0;
01800     if (arg)
01801     {
01802         szValidDoing = MakeCanonicalDoing(arg, &nValidDoing, &bValidDoing);
01803         if (!bValidDoing)
01804         {
01805             szValidDoing = Empty;
01806             nValidDoing = 0;
01807         }
01808     }
01809 
01810     bool bQuiet = ((key & DOING_QUIET) == DOING_QUIET);
01811     key &= DOING_MASK;
01812     if (key == DOING_MESSAGE)
01813     {
01814         DESC *d;
01815         bool bFound = false;
01816         DESC_ITER_PLAYER(executor, d)
01817         {
01818             memcpy(d->doing, szValidDoing, nValidDoing+1);
01819             bFound = true;
01820         }
01821         if (bFound)
01822         {
01823             if (  !bQuiet
01824                && !Quiet(executor))
01825             {
01826                 notify(executor, "Set.");
01827             }
01828         }
01829         else
01830         {
01831             notify(executor, "Not connected.");
01832         }
01833     }
01834     else if (key == DOING_UNIQUE)
01835     {
01836         DESC *d;
01837         DESC *dMax = NULL;
01838         CLinearTimeAbsolute ltaMax;
01839         DESC_ITER_PLAYER(executor, d)
01840         {
01841             if (  !dMax
01842                && ltaMax < d->last_time)
01843             {
01844                 ltaMax = d->last_time;
01845                 dMax = d;
01846             }
01847         }
01848         if (dMax)
01849         {
01850             memcpy(dMax->doing, szValidDoing, nValidDoing+1);
01851             if (  !bQuiet
01852                && !Quiet(executor))
01853             {
01854                 notify(executor, "Set.");
01855             }
01856         }
01857         else
01858         {
01859             notify(executor, "Not connected.");
01860         }
01861     }
01862     else if (key == DOING_HEADER)
01863     {
01864         if (!Can_Poll(executor))
01865         {
01866             notify(executor, NOPERM_MESSAGE);
01867             return;
01868         }
01869         if (nValidDoing == 0)
01870         {
01871             strcpy(mudstate.doing_hdr, "Doing");
01872         }
01873         else
01874         {
01875             memcpy(mudstate.doing_hdr, szValidDoing, nValidDoing+1);
01876         }
01877         if (  !bQuiet
01878            && !Quiet(executor))
01879         {
01880             notify(executor, "Set.");
01881         }
01882     }
01883     else // if (key == DOING_POLL)
01884     {
01885         notify(executor, tprintf("Poll: %s", mudstate.doing_hdr));
01886     }
01887 }
01888 
01889 NAMETAB logout_cmdtable[] =
01890 {
01891     {(char *)"DOING",         5,  CA_PUBLIC,  CMD_DOING},
01892     {(char *)"LOGOUT",        6,  CA_PUBLIC,  CMD_LOGOUT},
01893     {(char *)"OUTPUTPREFIX", 12,  CA_PUBLIC,  CMD_PREFIX|CMD_NOxFIX},
01894     {(char *)"OUTPUTSUFFIX", 12,  CA_PUBLIC,  CMD_SUFFIX|CMD_NOxFIX},
01895     {(char *)"QUIT",          4,  CA_PUBLIC,  CMD_QUIT},
01896     {(char *)"SESSION",       7,  CA_PUBLIC,  CMD_SESSION},
01897     {(char *)"WHO",           3,  CA_PUBLIC,  CMD_WHO},
01898     {(char *)"PUEBLOCLIENT", 12,  CA_PUBLIC,  CMD_PUEBLOCLIENT},
01899     {(char *)"INFO",          4,  CA_PUBLIC,  CMD_INFO},
01900     {NULL,                    0,          0,         0}
01901 };
01902 
01903 void init_logout_cmdtab(void)
01904 {
01905     NAMETAB *cp;
01906 
01907     // Make the htab bigger than the number of entries so that we find things
01908     // on the first check.  Remember that the admin can add aliases.
01909     //
01910     for (cp = logout_cmdtable; cp->flag; cp++)
01911     {
01912         hashaddLEN(cp->name, strlen(cp->name), cp, &mudstate.logout_cmd_htab);
01913     }
01914 }
01915 
01916 static void failconn(const char *logcode, const char *logtype, const char *logreason,
01917                      DESC *d, int disconnect_reason,
01918                      dbref player, int filecache, char *motd_msg, char *command,
01919                      char *user, char *password, char *cmdsave)
01920 {
01921     STARTLOG(LOG_LOGIN | LOG_SECURITY, logcode, "RJCT");
01922     char *buff = alloc_mbuf("failconn.LOG");
01923     sprintf(buff, "[%u/%s] %s rejected to ", d->descriptor, d->addr, logtype);
01924     log_text(buff);
01925     free_mbuf(buff);
01926     if (player != NOTHING)
01927     {
01928         log_name(player);
01929     }
01930     else
01931     {
01932         log_text(user);
01933     }
01934     log_text(" (");
01935     log_text(logreason);
01936     log_text(")");
01937     ENDLOG;
01938     fcache_dump(d, filecache);
01939     if (*motd_msg)
01940     {
01941         queue_string(d, motd_msg);
01942         queue_write_LEN(d, "\r\n", 2);
01943     }
01944     free_lbuf(command);
01945     free_lbuf(user);
01946     free_lbuf(password);
01947     shutdownsock(d, disconnect_reason);
01948     mudstate.debug_cmd = cmdsave;
01949     return;
01950 }
01951 
01952 static const char *connect_fail = "Either that player does not exist, or has a different password.\r\n";
01953 
01954 static bool check_connect(DESC *d, char *msg)
01955 {
01956     char *buff;
01957     dbref player, aowner;
01958     int aflags, nplayers;
01959     DESC *d2;
01960     const char *p;
01961     bool isGuest = false;
01962 
01963     char *cmdsave = mudstate.debug_cmd;
01964     mudstate.debug_cmd = (char *)"< check_connect >";
01965 
01966     // Hide the password length from SESSION.
01967     //
01968     d->input_tot -= (strlen(msg) + 1);
01969 
01970     // Crack the command apart.
01971     //
01972     char *command = alloc_lbuf("check_conn.cmd");
01973     char *user = alloc_lbuf("check_conn.user");
01974     char *password = alloc_lbuf("check_conn.pass");
01975     parse_connect(msg, command, user, password);
01976 
01977     // At this point, command, user, and password are all less than
01978     // MBUF_SIZE.
01979     //
01980     if (  strncmp(command, "co", 2) == 0
01981        || strncmp(command, "cd", 2) == 0)
01982     {
01983         if (string_prefix(user, mudconf.guest_prefix))
01984         {
01985             if (  (d->host_info & H_GUEST)
01986                || (   !mudconf.allow_guest_from_registered_site
01987                   && (d->host_info & H_REGISTRATION)))
01988             {
01989                 // Someone from an IP with guest restrictions is
01990                 // trying to use a guest account. Give them the blurb
01991                 // most likely to have instructions about requesting a
01992                 // character by other means and then fail this
01993                 // connection.
01994                 //
01995                 // The guest 'power' is handled separately further
01996                 // down.
01997                 //
01998                 failconn("CONN", "Connect", "Guest Site Forbidden", d,
01999                     R_GAMEDOWN, NOTHING, FC_CONN_REG, mudconf.downmotd_msg,
02000                     command, user, password, cmdsave);
02001                 return false;
02002             }
02003             if (  mudconf.guest_char != NOTHING
02004                && (mudconf.control_flags & CF_LOGIN))
02005             {
02006                 if (!(mudconf.control_flags & CF_GUEST))
02007                 {
02008                     queue_write(d, "Guest logins are disabled.\r\n");
02009                     free_lbuf(command);
02010                     free_lbuf(user);
02011                     free_lbuf(password);
02012                     return false;
02013                 }
02014 
02015                 if ((p = Guest.Create(d)) == NULL)
02016                 {
02017                     queue_write(d, "All guests are tied up, please try again later.\r\n");
02018                     free_lbuf(command);
02019                     free_lbuf(user);
02020                     free_lbuf(password);
02021                     return false;
02022                 }
02023                 strcpy(user, p);
02024                 strcpy(password, GUEST_PASSWORD);
02025                 isGuest = true;
02026             }
02027         }
02028 
02029         // See if this connection would exceed the max #players.
02030         //
02031         if (mudconf.max_players < 0)
02032         {
02033             nplayers = mudconf.max_players - 1;
02034         }
02035         else
02036         {
02037             nplayers = 0;
02038             DESC_ITER_CONN(d2)
02039             {
02040                 nplayers++;
02041             }
02042         }
02043 
02044         player = connect_player(user, password, d->addr, d->username, inet_ntoa((d->address).sin_addr));
02045         if (  player == NOTHING
02046            || (!isGuest && Guest.CheckGuest(player)))
02047         {
02048             // Not a player, or wrong password.
02049             //
02050             queue_write(d, connect_fail);
02051             STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD");
02052             buff = alloc_lbuf("check_conn.LOG.bad");
02053             sprintf(buff, "[%u/%s] Failed connect to '%s'", d->descriptor, d->addr, user);
02054             log_text(buff);
02055             free_lbuf(buff);
02056             ENDLOG;
02057             if (--(d->retries_left) <= 0)
02058             {
02059                 free_lbuf(command);
02060                 free_lbuf(user);
02061                 free_lbuf(password);
02062                 shutdownsock(d, R_BADLOGIN);
02063                 mudstate.debug_cmd = cmdsave;
02064                 return false;
02065             }
02066         }
02067         else if (  (  (mudconf.control_flags & CF_LOGIN)
02068                    && (nplayers < mudconf.max_players))
02069                 || WizRoy(player)
02070                 || God(player))
02071         {
02072             if (  strncmp(command, "cd", 2) == 0
02073                && (  Wizard(player)
02074                   || God(player)))
02075             {
02076                 db[player].fs.word[FLAG_WORD1] |= DARK;
02077             }
02078 
02079             // Make sure we don't have a guest from an unwanted host.
02080             // The majority of these are handled above.
02081             //
02082             // The following code handles the case where a staffer
02083             // (#1-only by default) has specifically given the guest 'power'
02084             // to an existing player.
02085             //
02086             // In this case, the player -already- has an account complete
02087             // with password. We still fail the connection to -this- player
02088             // but if the site isn't register_sited, this player can simply
02089             // auto-create another player. So, the procedure is not much
02090             // different from @newpassword'ing them. Oh well. We are just
02091             // following orders. ;)
02092             //
02093             if (  Guest(player)
02094                && (  (d->host_info & H_GUEST)
02095                   || (   !mudconf.allow_guest_from_registered_site
02096                      && (d->host_info & H_REGISTRATION))))
02097             {
02098                 failconn("CON", "Connect", "Guest Site Forbidden", d,
02099                     R_GAMEDOWN, player, FC_CONN_SITE,
02100                     mudconf.downmotd_msg, command, user, password,
02101                     cmdsave);
02102                 return false;
02103             }
02104 
02105             // Logins are enabled, or wiz or god.
02106             //
02107             STARTLOG(LOG_LOGIN, "CON", "LOGIN");
02108             buff = alloc_mbuf("check_conn.LOG.login");
02109             sprintf(buff, "[%u/%s] Connected to ", d->descriptor, d->addr);
02110             log_text(buff);
02111             log_name_and_loc(player);
02112             free_mbuf(buff);
02113             ENDLOG;
02114             d->flags |= DS_CONNECTED;
02115             d->connected_at.GetUTC();
02116             d->player = player;
02117 
02118             // Check to see if the player is currently running an
02119             // @program. If so, drop the new descriptor into it.
02120             //
02121             DESC_ITER_PLAYER(player, d2)
02122             {
02123                 if (d2->program_data != NULL)
02124                 {
02125                     d->program_data = d2->program_data;
02126                     break;
02127                 }
02128             }
02129 
02130             // Give the player the MOTD file and the settable MOTD
02131             // message(s). Use raw notifies so the player doesn't try
02132             // to match on the text.
02133             //
02134             if (Guest(player))
02135             {
02136                 fcache_dump(d, FC_CONN_GUEST);
02137             }
02138             else
02139             {
02140                 buff = atr_get(player, A_LAST, &aowner, &aflags);
02141                 if (*buff == '\0')
02142                     fcache_dump(d, FC_CREA_NEW);
02143                 else
02144                     fcache_dump(d, FC_MOTD);
02145                 if (Wizard(player))
02146                     fcache_dump(d, FC_WIZMOTD);
02147                 free_lbuf(buff);
02148             }
02149             announce_connect(player, d);
02150 
02151             DESC* dtemp;
02152             int num_con = 0;
02153             DESC_ITER_PLAYER(player, dtemp)
02154             {
02155                 num_con++;
02156             }
02157             local_connect(player, 0, num_con);
02158 
02159             // If stuck in an @prog, show the prompt.
02160             //
02161             if (d->program_data != NULL)
02162             {
02163                 queue_write_LEN(d, ">\377\371", 3);
02164             }
02165 
02166         }
02167         else if (!(mudconf.control_flags & CF_LOGIN))
02168         {
02169             failconn("CON", "Connect", "Logins Disabled", d, R_GAMEDOWN, player, FC_CONN_DOWN,
02170                 mudconf.downmotd_msg, command, user, password, cmdsave);
02171             return false;
02172         }
02173         else
02174         {
02175             failconn("CON", "Connect", "Game Full", d, R_GAMEFULL, player, FC_CONN_FULL,
02176                 mudconf.fullmotd_msg, command, user, password, cmdsave);
02177             return false;
02178         }
02179     }
02180     else if (strncmp(command, "cr", 2) == 0)
02181     {
02182         // Enforce game down.
02183         //
02184         if (!(mudconf.control_flags & CF_LOGIN))
02185         {
02186             failconn("CRE", "Create", "Logins Disabled", d, R_GAMEDOWN, NOTHING, FC_CONN_DOWN,
02187                 mudconf.downmotd_msg, command, user, password, cmdsave);
02188             return false;
02189         }
02190 
02191         // Enforce max #players.
02192         //
02193         if (mudconf.max_players < 0)
02194         {
02195             nplayers = mudconf.max_players;
02196         }
02197         else
02198         {
02199             nplayers = 0;
02200             DESC_ITER_CONN(d2)
02201             {
02202                 nplayers++;
02203             }
02204         }
02205         if (nplayers > mudconf.max_players)
02206         {
02207             // Too many players on, reject the attempt.
02208             //
02209             failconn("CRE", "Create", "Game Full", d,
02210                 R_GAMEFULL, NOTHING, FC_CONN_FULL,
02211                 mudconf.fullmotd_msg, command, user, password,
02212                 cmdsave);
02213             return false;
02214         }
02215         if (d->host_info & H_REGISTRATION)
02216         {
02217             fcache_dump(d, FC_CREA_REG);
02218         }
02219         else
02220         {
02221             const char *pmsg;
02222             player = create_player(user, password, NOTHING, false, &pmsg);
02223             if (player == NOTHING)
02224             {
02225                 queue_write(d, pmsg);
02226                 queue_write(d, "\r\n");
02227                 STARTLOG(LOG_SECURITY | LOG_PCREATES, "CON", "BAD");
02228                 buff = alloc_lbuf("check_conn.LOG.badcrea");
02229                 sprintf(buff, "[%u/%s] Create of '%s' failed", d->descriptor, d->addr, user);
02230                 log_text(buff);
02231                 free_lbuf(buff);
02232                 ENDLOG;
02233             }
02234             else
02235             {
02236                 AddToPublicChannel(player);
02237                 STARTLOG(LOG_LOGIN | LOG_PCREATES, "CON", "CREA");
02238                 buff = alloc_mbuf("check_conn.LOG.create");
02239                 sprintf(buff, "[%u/%s] Created ", d->descriptor, d->addr);
02240                 log_text(buff);
02241                 log_name(player);
02242                 free_mbuf(buff);
02243                 ENDLOG;
02244                 move_object(player, mudconf.start_room);
02245                 d->flags |= DS_CONNECTED;
02246                 d->connected_at.GetUTC();
02247                 d->player = player;
02248                 fcache_dump(d, FC_CREA_NEW);
02249                 announce_connect(player, d);
02250 
02251                 // Since it is on the create call, assume connection count
02252                 // is 0 and indicate the connect is a new character.
02253                 //
02254                 local_connect(player, 1, 0);
02255             }
02256         }
02257     }
02258     else
02259     {
02260         welcome_user(d);
02261         STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD");
02262         buff = alloc_mbuf("check_conn.LOG.bad");
02263         msg[150] = '\0';
02264         sprintf(buff, "[%u/%s] Failed connect: '%s'", d->descriptor, d->addr, msg);
02265         log_text(buff);
02266         free_mbuf(buff);
02267         ENDLOG;
02268     }
02269     free_lbuf(command);
02270     free_lbuf(user);
02271     free_lbuf(password);
02272 
02273     mudstate.debug_cmd = cmdsave;
02274     return true;
02275 }
02276 
02277 static void do_logged_out_internal(DESC *d, int key, char *arg)
02278 {
02279     switch (key)
02280     {
02281     case CMD_QUIT:
02282 
02283         shutdownsock(d, R_QUIT);
02284         break;
02285 
02286     case CMD_LOGOUT:
02287 
02288         shutdownsock(d, R_LOGOUT);
02289         break;
02290 
02291     case CMD_WHO:
02292     case CMD_DOING:
02293     case CMD_SESSION:
02294 
02295         dump_users(d, arg, key);
02296         break;
02297 
02298     case CMD_PREFIX:
02299 
02300         set_userstring(&d->output_prefix, arg);
02301         break;
02302 
02303     case CMD_SUFFIX:
02304 
02305         set_userstring(&d->output_suffix, arg);
02306         break;
02307 
02308     case CMD_INFO:
02309 
02310         dump_info(d);
02311         break;
02312 
02313     case CMD_PUEBLOCLIENT:
02314 
02315         // Set the descriptor's flag.
02316         //
02317         d->flags |= DS_PUEBLOCLIENT;
02318 
02319         queue_string(d, mudconf.pueblo_msg);
02320         queue_write_LEN(d, "\r\n", 2);
02321         break;
02322 
02323     default:
02324 
02325         {
02326             char buf[LBUF_SIZE * 2];
02327             STARTLOG(LOG_BUGS, "BUG", "PARSE");
02328             sprintf(buf, "Logged-out command with no handler: '%s'", mudstate.debug_cmd);
02329             log_text(buf);
02330             ENDLOG;
02331         }
02332     }
02333 }
02334 
02335 void do_command(DESC *d, char *command)
02336 {
02337     char *cmdsave = mudstate.debug_cmd;
02338     mudstate.debug_cmd = (char *)"< do_command >";
02339 
02340     if (d->flags & DS_CONNECTED)
02341     {
02342         // Normal logged-in command processing.
02343         //
02344         d->command_count++;
02345         if (d->output_prefix)
02346         {
02347             queue_string(d, d->output_prefix);
02348             queue_write_LEN(d, "\r\n", 2);
02349         }
02350         mudstate.curr_executor = d->player;
02351         mudstate.curr_enactor = d->player;
02352         for (int i = 0; i < MAX_GLOBAL_REGS; i++)
02353         {
02354             mudstate.global_regs[i][0] = '\0';
02355             mudstate.glob_reg_len[i] = 0;
02356         }
02357 
02358         CLinearTimeAbsolute ltaBegin;
02359         ltaBegin.GetUTC();
02360         MuxAlarm.Set(mudconf.max_cmdsecs);
02361 
02362         char *log_cmdbuf = process_command(d->player, d->player, d->player,
02363             true, command, (char **)NULL, 0);
02364 
02365         CLinearTimeAbsolute ltaEnd;
02366         ltaEnd.GetUTC();
02367         if (MuxAlarm.bAlarmed)
02368         {
02369             notify(d->player, "GAME: Expensive activity abbreviated.");
02370             halt_que(d->player, NOTHING);
02371             s_Flags(d->player, FLAG_WORD1, Flags(d->player) | HALT);
02372         }
02373         MuxAlarm.Clear();
02374 
02375         CLinearTimeDelta ltd = ltaEnd - ltaBegin;
02376         if (ltd > mudconf.rpt_cmdsecs)
02377         {
02378             STARTLOG(LOG_PROBLEMS, "CMD", "CPU");
02379             log_name_and_loc(d->player);
02380             char *logbuf = alloc_lbuf("do_command.LOG.cpu");
02381             sprintf(logbuf, " queued command taking %s secs: ",
02382                 ltd.ReturnSecondsString(4));
02383             log_text(logbuf);
02384             free_lbuf(logbuf);
02385             log_text(log_cmdbuf);
02386             ENDLOG;
02387         }
02388 
02389         mudstate.curr_cmd = (char *) "";
02390         if (d->output_suffix)
02391         {
02392             queue_string(d, d->output_suffix);
02393             queue_write_LEN(d, "\r\n", 2);
02394         }
02395         mudstate.debug_cmd = cmdsave;
02396         return;
02397     }
02398 
02399     // Login screen (logged-out) command processing.
02400     //
02401 
02402     // Split off the command from the arguments.
02403     //
02404     char *arg = command;
02405     while (*arg && !mux_isspace(*arg))
02406     {
02407         arg++;
02408     }
02409 
02410     if (*arg)
02411     {
02412         *arg++ = '\0';
02413     }
02414 
02415     // Look up the command in the logged-out command table.
02416     //
02417     NAMETAB *cp = (NAMETAB *)hashfindLEN(command, strlen(command), &mudstate.logout_cmd_htab);
02418     if (cp == NULL)
02419     {
02420         // Not in the logged-out command table, so maybe a connect attempt.
02421         //
02422         if (*arg)
02423         {
02424             // Restore nullified space
02425             //
02426             *--arg = ' ';
02427         }
02428         mudstate.curr_executor = NOTHING;
02429         mudstate.curr_enactor = NOTHING;
02430         mudstate.debug_cmd = cmdsave;
02431         check_connect(d, command);
02432         return;
02433     }
02434 
02435     // The command was in the logged-out command table. Perform
02436     // prefix and suffix processing, and invoke the command
02437     // handler.
02438     //
02439     d->command_count++;
02440     if (!(cp->flag & CMD_NOxFIX))
02441     {
02442         if (d->output_prefix)
02443         {
02444             queue_string(d, d->output_prefix);
02445             queue_write_LEN(d, "\r\n", 2);
02446         }
02447     }
02448     if (cp->perm != CA_PUBLIC)
02449     {
02450         queue_write(d, "Permission denied.\r\n");
02451     }
02452     else
02453     {
02454         mudstate.debug_cmd = cp->name;
02455         do_logged_out_internal(d, cp->flag & CMD_MASK, arg);
02456     }
02457     // QUIT or LOGOUT will close the connection and cause the
02458     // descriptor to be freed!
02459     //
02460     if (  ((cp->flag & CMD_MASK) != CMD_QUIT)
02461        && ((cp->flag & CMD_MASK) != CMD_LOGOUT)
02462        && !(cp->flag & CMD_NOxFIX))
02463     {
02464         if (d->output_suffix)
02465         {
02466             queue_string(d, d->output_suffix);
02467             queue_write_LEN(d, "\r\n", 2);
02468         }
02469     }
02470     mudstate.debug_cmd = cmdsave;
02471 }
02472 
02473 void logged_out1(dbref executor, dbref caller, dbref enactor, int key, char *arg)
02474 {
02475     UNUSED_PARAMETER(caller);
02476     UNUSED_PARAMETER(enactor);
02477 
02478     // PUEBLOCLIENT affects all the player's connections.
02479     //
02480     if (key == CMD_PUEBLOCLIENT)
02481     {
02482         DESC *d;
02483         DESC_ITER_PLAYER(executor, d)
02484         {
02485             do_logged_out_internal(d, key, arg);
02486         }
02487         // Set the player's flag.
02488         //
02489         s_Html(executor);
02490         return;
02491     }
02492 
02493     // Other logged-out commands affect only the player's most recently
02494     // used connection.
02495     //
02496     DESC *d;
02497     DESC *dLatest = NULL;
02498     DESC_ITER_PLAYER(executor, d)
02499     {
02500         if (  dLatest == NULL
02501            || dLatest->last_time < d->last_time)
02502         {
02503             dLatest = d;
02504         }
02505     }
02506     if (dLatest != NULL)
02507     {
02508         do_logged_out_internal(dLatest, key, arg);
02509     }
02510 }
02511 
02512 void logged_out0(dbref executor, dbref caller, dbref enactor, int key)
02513 {
02514     logged_out1(executor, caller, enactor, key, "");
02515 }
02516 
02517 void Task_ProcessCommand(void *arg_voidptr, int arg_iInteger)
02518 {
02519     UNUSED_PARAMETER(arg_iInteger);
02520 
02521     DESC *d = (DESC *)arg_voidptr;
02522     if (d)
02523     {
02524         CBLK *t = d->input_head;
02525         if (t)
02526         {
02527             if (d->quota > 0)
02528             {
02529                 d->quota--;
02530                 d->input_head = (CBLK *) t->hdr.nxt;
02531                 if (d->input_head)
02532                 {
02533                     // There are still commands to process, so schedule another looksee.
02534                     //
02535                     scheduler.DeferImmediateTask(PRIORITY_SYSTEM, Task_ProcessCommand, d, 0);
02536                 }
02537                 else
02538                 {
02539                     d->input_tail = NULL;
02540                 }
02541                 d->input_size -= strlen(t->cmd);
02542                 d->last_time.GetUTC();
02543                 if (d->program_data != NULL)
02544                 {
02545                     handle_prog(d, t->cmd);
02546                 }
02547                 else
02548                 {
02549                     do_command(d, t->cmd);
02550                 }
02551                 free_lbuf(t);
02552             }
02553             else
02554             {
02555                 // Don't bother looking for more quota until at least this much time has past.
02556                 //
02557                 CLinearTimeAbsolute lsaWhen;
02558                 lsaWhen.GetUTC();
02559 
02560                 scheduler.DeferTask(lsaWhen + mudconf.timeslice, PRIORITY_SYSTEM, Task_ProcessCommand, d, 0);
02561             }
02562         }
02563     }
02564 }
02565 
02566 /* ---------------------------------------------------------------------------
02567  * site_check: Check for site flags in a site list.
02568  */
02569 
02570 int site_check(struct in_addr host, SITE *site_list)
02571 {
02572     SITE *this0;
02573 
02574     for (this0 = site_list; this0; this0 = this0->next)
02575     {
02576         if ((host.s_addr & this0->mask.s_addr) == this0->address.s_addr)
02577         {
02578             return this0->flag;
02579         }
02580     }
02581     return 0;
02582 }
02583 
02584 /* --------------------------------------------------------------------------
02585  * list_sites: Display information in a site list
02586  */
02587 
02588 #define S_SUSPECT   1
02589 #define S_ACCESS    2
02590 
02591 static const char *stat_string(int strtype, int flag)
02592 {
02593     const char *str;
02594 
02595     switch (strtype)
02596     {
02597     case S_SUSPECT:
02598         if (flag)
02599         {
02600             str = "Suspected";
02601         }
02602         else
02603         {
02604             str = "Trusted";
02605         }
02606         break;
02607 
02608     case S_ACCESS:
02609         switch (flag)
02610         {
02611         case H_FORBIDDEN:
02612             str = "Forbidden";
02613             break;
02614 
02615         case H_REGISTRATION:
02616             str = "Registration";
02617             break;
02618 
02619         case H_GUEST:
02620             str = "NoGuest";
02621             break;
02622 
02623         case H_NOSITEMON:
02624             str = "NoSiteMon";
02625             break;
02626 
02627         case 0:
02628             str = "Unrestricted";
02629             break;
02630 
02631         default:
02632             str = "Strange";
02633             break;
02634         }
02635         break;
02636 
02637     default:
02638         str = "Strange";
02639         break;
02640     }
02641     return str;
02642 }
02643 
02644 static void list_sites(dbref player, SITE *site_list, const char *header_txt, int stat_type)
02645 {
02646     char *buff, *buff1, *str;
02647     SITE *this0;
02648 
02649     buff = alloc_mbuf("list_sites.buff");
02650     buff1 = alloc_sbuf("list_sites.addr");
02651     sprintf(buff, "----- %s -----", header_txt);
02652     notify(player, buff);
02653     notify(player, "Address              Mask                 Status");
02654     for (this0 = site_list; this0; this0 = this0->next)
02655     {
02656         str = (char *)stat_string(stat_type, this0->flag);
02657         strcpy(buff1, inet_ntoa(this0->mask));
02658         sprintf(buff, "%-20s %-20s %s", inet_ntoa(this0->address), buff1,
02659             str);
02660         notify(player, buff);
02661     }
02662     free_mbuf(buff);
02663     free_sbuf(buff1);
02664 }
02665 
02666 /* ---------------------------------------------------------------------------
02667  * list_siteinfo: List information about specially-marked sites.
02668  */
02669 
02670 void list_siteinfo(dbref player)
02671 {
02672     list_sites(player, mudstate.access_list, "Site Access", S_ACCESS);
02673     list_sites(player, mudstate.suspect_list, "Suspected Sites", S_SUSPECT);
02674 }
02675 
02676 /* ---------------------------------------------------------------------------
02677  * make_ulist: Make a list of connected user numbers for the LWHO function.
02678  */
02679 
02680 void make_ulist(dbref player, char *buff, char **bufc, bool bPorts)
02681 {
02682     DESC *d;
02683     if (bPorts)
02684     {
02685         make_port_ulist(player, buff, bufc);
02686     }
02687     else
02688     {
02689         ITL pContext;
02690         ItemToList_Init(&pContext, buff, bufc, '#');
02691         DESC_ITER_CONN(d)
02692         {
02693             if (  !See_Hidden(player)
02694                && Hidden(d->player))
02695             {
02696                 continue;
02697             }
02698             if (!ItemToList_AddInteger(&pContext, d->player))
02699             {
02700                 break;
02701             }
02702         }
02703         ItemToList_Final(&pContext);
02704     }
02705 }
02706 
02707 /* ---------------------------------------------------------------------------
02708  * find_connected_name: Resolve a playername from the list of connected
02709  * players using prefix matching.  We only return a match if the prefix
02710  * was unique.
02711  */
02712 
02713 dbref find_connected_name(dbref player, char *name)
02714 {
02715     DESC *d;
02716     dbref found = NOTHING;
02717     DESC_ITER_CONN(d)
02718     {
02719         if (  Good_obj(player)
02720            && !See_Hidden(player)
02721            && Hidden(d->player))
02722         {
02723             continue;
02724         }
02725         if (!string_prefix(Name(d->player), name))
02726         {
02727             continue;
02728         }
02729         if (  found != NOTHING
02730            && found != d->player)
02731         {
02732             return NOTHING;
02733         }
02734         found = d->player;
02735     }
02736     return found;
02737 }
02738 
02739 FUNCTION(fun_doing)
02740 {
02741     UNUSED_PARAMETER(caller);
02742     UNUSED_PARAMETER(enactor);
02743     UNUSED_PARAMETER(nfargs);
02744     UNUSED_PARAMETER(cargs);
02745     UNUSED_PARAMETER(ncargs);
02746 
02747     if (is_rational(fargs[0]))
02748     {
02749         SOCKET s = mux_atol(fargs[0]);
02750         bool bFound = false;
02751         DESC *d;
02752         DESC_ITER_CONN(d)
02753         {
02754             if (d->descriptor == s)
02755             {
02756                 bFound = true;
02757                 break;
02758             }
02759         }
02760         if (  bFound
02761            && (  d->player == executor
02762               || Wizard_Who(executor)))
02763         {
02764             safe_str(d->doing, buff, bufc);
02765         }
02766         else
02767         {
02768             safe_nothing(buff, bufc);
02769         }
02770     }
02771     else
02772     {
02773         dbref victim = lookup_player(executor, fargs[0], true);
02774         if (victim == NOTHING)
02775         {
02776             safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc);
02777             return;
02778         }
02779 
02780         if (  Wizard_Who(executor)
02781            || !Hidden(victim))
02782         {
02783             DESC *d;
02784             DESC_ITER_CONN(d)
02785             {
02786                 if (d->player == victim)
02787                 {
02788                     safe_str(d->doing, buff, bufc);
02789                     return;
02790                 }
02791             }
02792         }
02793         safe_str("#-1 NOT A CONNECTED PLAYER", buff, bufc);
02794     }
02795 }
02796 
02797 // ---------------------------------------------------------------------------
02798 // fun_host: Return hostname of player or port descriptor.
02799 // ---------------------------------------------------------------------------
02800 FUNCTION(fun_host)
02801 {
02802     UNUSED_PARAMETER(caller);
02803     UNUSED_PARAMETER(enactor);
02804     UNUSED_PARAMETER(nfargs);
02805     UNUSED_PARAMETER(cargs);
02806     UNUSED_PARAMETER(ncargs);
02807 
02808     if (!Wizard_Who(executor))
02809     {
02810         safe_noperm(buff, bufc);
02811         return;
02812     }
02813 
02814     bool isPort = is_rational(fargs[0]);
02815     bool bFound = false;
02816     DESC *d;
02817     if (isPort)
02818     {
02819         SOCKET s = mux_atol(fargs[0]);
02820         DESC_ITER_CONN(d)
02821         {
02822             if (d->descriptor == s)
02823             {
02824                 bFound = true;
02825                 break;
02826             }
02827         }
02828     }
02829     else
02830     {
02831         dbref victim = lookup_player(executor, fargs[0], true);
02832         if (victim == NOTHING)
02833         {
02834             safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc);
02835             return;
02836         }
02837         DESC_ITER_CONN(d)
02838         {
02839             if (d->player == victim)
02840             {
02841                 bFound = true;
02842                 break;
02843             }
02844         }
02845     }
02846     if (bFound)
02847     {
02848         char *hostname = ((d->username[0] != '\0') ?
02849             tprintf("%s@%s", d->username, d->addr) : d->addr);
02850         safe_str(hostname, buff, bufc);
02851         return;
02852     }
02853     if (isPort)
02854     {
02855         safe_str("#-1 NOT AN ACTIVE PORT", buff, bufc);
02856     }
02857     else
02858     {
02859         safe_str("#-1 NOT A CONNECTED PLAYER", buff, bufc);
02860     }
02861 }
02862 
02863 FUNCTION(fun_poll)
02864 {
02865     UNUSED_PARAMETER(executor);
02866     UNUSED_PARAMETER(caller);
02867     UNUSED_PARAMETER(enactor);
02868     UNUSED_PARAMETER(fargs);
02869     UNUSED_PARAMETER(nfargs);
02870     UNUSED_PARAMETER(cargs);
02871     UNUSED_PARAMETER(ncargs);
02872 
02873     safe_str(mudstate.doing_hdr, buff, bufc);
02874 }
02875 
02876 FUNCTION(fun_motd)
02877 {
02878     UNUSED_PARAMETER(executor);
02879     UNUSED_PARAMETER(caller);
02880     UNUSED_PARAMETER(enactor);
02881     UNUSED_PARAMETER(fargs);
02882     UNUSED_PARAMETER(nfargs);
02883     UNUSED_PARAMETER(cargs);
02884     UNUSED_PARAMETER(ncargs);
02885 
02886     safe_str(mudconf.motd_msg, buff, bufc);
02887 }
02888 
02889 // fetch_cmds - Retrieve Player's number of commands entered.
02890 //
02891 int fetch_cmds(dbref target)
02892 {
02893     int sum = 0;
02894     bool bFound = false;
02895 
02896     DESC *d;
02897     DESC_ITER_PLAYER(target, d)
02898     {
02899         sum += d->command_count;
02900         bFound = true;
02901     }
02902 
02903     if (bFound)
02904     {
02905         return sum;
02906     }
02907     else
02908     {
02909         return -1;
02910     }
02911 }
02912 
02913 static void ParseConnectionInfoString(char *pConnInfo, char *pFields[5])
02914 {
02915     MUX_STRTOK_STATE tts;
02916     mux_strtok_src(&tts, pConnInfo);
02917     mux_strtok_ctl(&tts, " ");
02918     for (int i = 0; i < 5; i++)
02919     {
02920         pFields[i] = mux_strtok_parse(&tts);
02921     }
02922 }
02923 
02924 void fetch_ConnectionInfoFields(dbref target, long anFields[4])
02925 {
02926     dbref aowner;
02927     int   aflags;
02928     char *pConnInfo = atr_get(target, A_CONNINFO, &aowner, &aflags);
02929     char *aFields[5];
02930     ParseConnectionInfoString(pConnInfo, aFields);
02931 
02932     for (int i = 0; i < 4; i++)
02933     {
02934         long result;
02935         if (  !aFields[i]
02936            || (result = mux_atol(aFields[i])) < 0)
02937         {
02938             result = 0;
02939         }
02940         anFields[i] = result;
02941     }
02942     free_lbuf(pConnInfo);
02943 }
02944 
02945 void put_ConnectionInfoFields
02946 (
02947     dbref target,
02948     long anFields[4],
02949     CLinearTimeAbsolute &ltaLogout
02950 )
02951 {
02952     char *pConnInfo = alloc_lbuf("put_CIF");
02953     char *p = pConnInfo;
02954     for (int i = 0; i < 4; i++)
02955     {
02956         p += mux_ltoa(anFields[i], p);
02957         *p++ = ' ';
02958     }
02959     p += mux_i64toa(ltaLogout.ReturnSeconds(), p);
02960     *p++ = 0;
02961 
02962     atr_add_raw_LEN(target, A_CONNINFO, pConnInfo, p - pConnInfo);
02963     free_lbuf(pConnInfo);
02964 }
02965 
02966 long fetch_ConnectionInfoField(dbref target, int iField)
02967 {
02968     dbref aowner;
02969     int   aflags;
02970     char *pConnInfo = atr_get(target, A_CONNINFO, &aowner, &aflags);
02971     char *aFields[5];
02972     ParseConnectionInfoString(pConnInfo, aFields);
02973 
02974     long result;
02975     if (  !aFields[iField]
02976        || (result = mux_atol(aFields[iField])) < 0)
02977     {
02978         result = 0;
02979     }
02980     free_lbuf(pConnInfo);
02981     return result;
02982 }
02983 
02984 #define CIF_LOGOUTTIME     4
02985 
02986 CLinearTimeAbsolute fetch_logouttime(dbref target)
02987 {
02988     dbref aowner;
02989     int   aflags;
02990     char *pConnInfo = atr_get(target, A_CONNINFO, &aowner, &aflags);
02991     char *aFields[5];
02992     ParseConnectionInfoString(pConnInfo, aFields);
02993 
02994     CLinearTimeAbsolute lta;
02995     if (aFields[CIF_LOGOUTTIME])
02996     {
02997         lta.SetSecondsString(aFields[CIF_LOGOUTTIME]);
02998     }
02999     else
03000     {
03001         lta.SetSeconds(0);
03002     }
03003     free_lbuf(pConnInfo);
03004     return lta;
03005 }

Generated on Mon May 28 04:40:10 2007 for MUX by  doxygen 1.4.7