btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * netcommon.c 
 */

/*
 * $Id: netcommon.c,v 1.7 2005/08/08 10:30:11 murrayma Exp $ 
 */

/*
 * This file contains routines used by the networking code that do not
 * depend on the implementation of the networking code.  The network-specific
 * portions of the descriptor data structure are not used.
 */

#include "copyright.h"
#include "config.h"

#include <time.h>

#include "db.h"
#include "mudconf.h"
#include "file_c.h"
#include "interface.h"
#include "command.h"
#include "externs.h"
#include "alloc.h"
#include "attrs.h"
#include "mguests.h"
#include "ansi.h"
#include "mail.h"
#include "powers.h"
#include "alloc.h"
#include "config.h"
#include "p.comsys.h"

#ifdef DEBUG_NETCOMMON
#ifndef DEBUG
#define DEBUG
#endif
#endif
#include "debug.h"



extern int process_output(DESC * d);
extern void handle_prog(DESC *, char *);
extern void fcache_dump_conn(DESC *, int);
extern void do_comconnect(dbref, DESC *);
extern void do_comdisconnect(dbref);
extern void set_lastsite(DESC *, char *);


/*
 * ---------------------------------------------------------------------------
 * * make_portlist: Make a list of ports for PORTS().
 */

void make_portlist(dbref player, dbref target, char *buff, char **bufc) {
    DESC *d;
    int i = 0;

    DESC_ITER_CONN(d) {
        if (d->player == target) {
            safe_str(tprintf("%d ", d->descriptor), buff, bufc);
            i = 1;
        }
    }
    if (i) {
        (*bufc)--;
    }
    **bufc = '\0';
}

/*
 * ---------------------------------------------------------------------------
 * * timeval_sub: return difference between two times as a timeval
 */

struct timeval timeval_sub(now, then)
    struct timeval now, then;

{
    now.tv_sec -= then.tv_sec;
    now.tv_usec -= then.tv_usec;
    if (now.tv_usec < 0) {
        now.tv_usec += 1000000;
        now.tv_sec--;
    }
    return now;
}

/*
 * ---------------------------------------------------------------------------
 * * msec_diff: return difference between two times in msec
 */

int msec_diff(struct timeval now, struct timeval then) {
    return ((now.tv_sec - then.tv_sec) * 1000 + (now.tv_usec -
                then.tv_usec) / 1000);
}

/*
 * ---------------------------------------------------------------------------
 * * msec_add: add milliseconds to a timeval
 */

struct timeval msec_add(struct timeval t, int x) {
    t.tv_sec += x / 1000;
    t.tv_usec += (x % 1000) * 1000;
    if (t.tv_usec >= 1000000) {
        t.tv_sec += t.tv_usec / 1000000;
        t.tv_usec = t.tv_usec % 1000000;
    }
    return t;
}

/*
 * ---------------------------------------------------------------------------
 * * update_quotas: Update timeslice quotas
 */

struct timeval update_quotas(struct timeval last, struct timeval current) {
    int nslices;
    DESC *d;

    nslices = msec_diff(current, last) / (mudconf.timeslice > 0 ? mudconf.timeslice : 1);

    if (nslices > 0) {
        DESC_ITER_ALL(d) {
            d->quota += mudconf.cmd_quota_incr * nslices;
            if (d->quota > mudconf.cmd_quota_max)
                d->quota = mudconf.cmd_quota_max;
        }
    }
    return msec_add(last, nslices * mudconf.timeslice);
}

/* raw_notify_html() -- raw_notify() without the newline */
void raw_notify_html(dbref player, const char *msg) {
    DESC *d;

    if (!msg || !*msg)
        return;
    if (mudstate.inpipe && (player == mudstate.poutobj)) {
        safe_str((char *) msg, mudstate.poutnew, &mudstate.poutbufc);
        return;
    }
    if (!Connected(player))
        return;

    DESC_ITER_PLAYER(player, d) {
        queue_string(d, msg);
    }
}

#ifdef TCP_CORK // Linux 2.4, 2.6
/* choke_player: cork the player's sockets, must have a matching release_socket */
void choke_player(dbref player) {
    DESC *d;
    int eins = 1, null = 0; 

    DESC_ITER_PLAYER(player, d) {
        if(d->chokes == 0) {
            if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_CORK, &eins, sizeof(eins))<0) {
                // XXX: currently we ignore the error, because if its not supported,
                // then we'd spam the logs; other errors will be caught in the event loop.
            } 
        } 
        d->chokes++;
    }
}

void release_player(dbref player) {
    DESC *d;
    int eins = 1, null = 0;

    DESC_ITER_PLAYER(player, d) {
        d->chokes--;
        if(d->chokes == 0) {
            if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_CORK, &null, sizeof(null))<0) {
                // XXX: current we ignore any error, ebcause if its not supported,
                // then we'd spam the logs; other errors will be caught in the event loop.
            } 
        }
        if(d->chokes < 0) d->chokes = 0;
    }
}
#else
#ifdef TCP_NOPUSH // *BSD, Mac OSX
/* choke_player: cork the player's sockets, must have a matching release_socket */
void choke_player(dbref player) {
    DESC *d;
    int eins = 1, null = 0; 

    DESC_ITER_PLAYER(player, d) {
       if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_NOPUSH, &eins, sizeof(eins))<0) {
            log_perror("NET", "FAIL", "choke_player", "setsockopt");
        } 
    }
}

void release_player(dbref player) {
    DESC *d;
    int eins = 1, null = 0;

    DESC_ITER_PLAYER(player, d) {
       if(setsockopt(d->descriptor, IPPROTO_TCP, TCP_NOPUSH, &null, sizeof(null))<0) {
            log_perror("NET", "FAIL", "release_player", "setsockopt");
        } 
    }
}
#else /* no OS support for network block coalescing. */
void choke_player(dbref player) {
    // Do nothing!
}
void release_player(dbref player) {
    // Do nothing!
}
#endif
#endif




/* raw_notify_raw: write a message to a player without the newline */

void raw_notify_raw(dbref player, const char *msg, char *append) {
    DESC *d;

    if (!msg || !*msg)
        return;

    if (mudstate.inpipe && (player == mudstate.poutobj)) {
        safe_str((char *) msg, mudstate.poutnew, &mudstate.poutbufc);
        if (append != NULL)
            safe_str(append, mudstate.poutnew, &mudstate.poutbufc);
        return;
    }

    if (!Connected(player))
        return;

    DESC_ITER_PLAYER(player, d) {
        queue_string(d, msg);
        if (append != NULL)
            queue_write(d, append, strlen(append));
    }
}

/* raw_notify: write a message to a player */
void raw_notify(dbref player, const char *msg) {
    raw_notify_raw(player, msg, "\r\n");
}

void notify_printf(dbref player, const char *format, ...) {
    DESC *d;
    char buffer[LBUF_SIZE];
    va_list ap;

    va_start(ap, format);

    vsnprintf(buffer, LBUF_SIZE, format, ap);
    va_end(ap);

    strncat(buffer, "\r\n", LBUF_SIZE);

    DESC_ITER_PLAYER(player, d) {
        queue_string(d, buffer);
    }
}


void raw_notify_newline(dbref player) {
    DESC *d;

    if (mudstate.inpipe && (player == mudstate.poutobj)) {
        safe_str("\r\n", mudstate.poutnew, &mudstate.poutbufc);
        return;
    }
    if (!Connected(player))
        return;

    DESC_ITER_PLAYER(player, d) {
        queue_write(d, "\r\n", 2);
    }
}

#ifdef HUDINFO_SUPPORT
void hudinfo_notify(DESC *d, const char *msgclass, const char *msgtype, 
        const char *msg) {
    char buf[LBUF_SIZE];

    if (!msgclass || !msgtype) {
        queue_string(d, msg);
        queue_write(d, "\r\n", 2);
        return;
    }

    snprintf(buf, LBUF_SIZE, "#HUD:%s:%s:%s# %s\r\n",
            d->hudkey[0] ? d->hudkey : "???", msgclass, msgtype, msg);
    buf[LBUF_SIZE-1] = '\0';
    queue_string(d, buf);
}
#endif

/*
 * ---------------------------------------------------------------------------
 * * raw_broadcast: Send message to players who have indicated flags
 */

void raw_broadcast(int inflags, char *template, ...) {
    char *buff;
    DESC *d;
    va_list ap;

    va_start(ap, template);
    if (!template || !*template)
        return;

    buff = alloc_lbuf("raw_broadcast");
    vsprintf(buff, template, ap);

    DESC_ITER_CONN(d) {
        if ((Flags(d->player) & inflags) == inflags) {
            queue_string(d, buff);
            queue_write(d, "\r\n", 2);
            // process_output(d);
        }
    }
    flush_sockets();
    free_lbuf(buff);
    va_end(ap);
}

/*
 * ---------------------------------------------------------------------------
 * * clearstrings: clear out prefix and suffix strings
 */

void clearstrings(DESC *d) {
    if (d->output_prefix) {
        free_lbuf(d->output_prefix);
        d->output_prefix = NULL;
    }
    if (d->output_suffix) {
        free_lbuf(d->output_suffix);
        d->output_suffix = NULL;
    }
}

/*
 * ---------------------------------------------------------------------------
 * * queue_write: Add text to the output queue for the indicated descriptor.
 */

void queue_write(DESC *d, char *b, int n) {
    int retval;
    if (n <= 0)
        return;

    bufferevent_write(d->sock_buff, b, n);
    d->output_tot += n;
    return;
}

void queue_string(DESC *d, const char *s) {
    char *new;

    if (!Ansi(d->player) && index(s, ESC_CHAR))
        new = strip_ansi(s);
    else if (NoBleed(d->player))
        new = normal_to_white(s);
    else
        new = (char *) s;
    queue_write(d, new, strlen(new));
}

void freeqs(DESC *d) {
    CBLK *cb, *cnext;

    cb = d->input_head;
    while (cb) {
        cnext = (CBLK *) cb->hdr.nxt;
        free_lbuf(cb);
        cb = cnext;
    }

    d->input_head = NULL;
    d->input_tail = NULL;

    if (d->raw_input)
        free_lbuf(d->raw_input);
    d->raw_input = NULL;
    d->raw_input_at = NULL;
}

int desc_cmp(void *vleft, void *vright, void *token) {
    dbref left = (dbref)vleft;
    dbref right = (dbref)vright;

    return (left-right);
}


/*
 * ---------------------------------------------------------------------------
 * * desc_addhash: Add a net descriptor to its player hash list.
 */

void desc_addhash(DESC *d) {
    dbref player;
    DESC *hdesc;

    player = d->player;

    hdesc = (DESC *)rb_find(mudstate.desctree, (void *)d->player);
    dprintk("Adding descriptor %p(%d) to list root at %p (%d).", 
            d, d->player, hdesc, (hdesc?hdesc->player:-1));
            
    if (hdesc == NULL) {
        d->hashnext = NULL;
    } else {
        d->hashnext = hdesc;
    }
    rb_insert(mudstate.desctree, (void *)d->player, d);
}

/*
 * ---------------------------------------------------------------------------
 * * desc_delhash: Remove a net descriptor from its player hash list.
 */

static void desc_delhash(DESC *d) {
    DESC *hdesc, *last;
    dbref player;
    char buffer[4096];

    player = d->player;
    last = NULL;
    hdesc = (DESC *) rb_find(mudstate.desctree, (void *)d->player);

    dprintk("removing descriptor %p(%d) from list root %p(%d).", d, d->player,
            hdesc, hdesc?hdesc->player:-1);

    if(!hdesc) {
        snprintf(buffer, 4096, "desc_delhash: unable to find player(%d)'s descriptors from hashtable.\n", 
                d->player);
        log_text(buffer);
        return;
    }
    dprintk("hdesc: %p, d: %p, hdesc->hashnext: %p, d->hashnext: %p", hdesc, d,
            hdesc->hashnext, d->hashnext);

    if(hdesc == d && hdesc->hashnext) {
        dprintk("updating %d to use hashroot %p", d->player, hdesc->hashnext);
        rb_insert(mudstate.desctree, (void *)d->player, hdesc->hashnext);
        return;
    } else if(hdesc == d) {
        dprintk("removing %d table", d->player);
        rb_delete(mudstate.desctree, (void *)d->player);
        return;
    }
     
    while (hdesc->hashnext != NULL) {
        if (hdesc->hashnext == d) {
            hdesc->hashnext = hdesc->hashnext->hashnext;
            break;
        }
        hdesc = hdesc->hashnext;
    }
    if(!hdesc) {
        snprintf(buffer, 4096, "Unable to find descriptor in player(%d)'s descriptor list.\n", d->player);
        log_text(buffer);
    }
    d->hashnext = NULL;
    return;
}

extern int fcache_conn_c;

void welcome_user(DESC *d) {
    if (d->host_info & H_REGISTRATION)
        fcache_dump(d, FC_CONN_REG);
    else {
        if (fcache_conn_c) {
            fcache_dump_conn(d, rand() % fcache_conn_c);
            return;
        }
        fcache_dump(d, FC_CONN);
    }
}

void save_command(DESC *d, CBLK *command) {

    command->hdr.nxt = NULL;
    if (d->input_tail == NULL)
        d->input_head = command;
    else
        d->input_tail->hdr.nxt = command;
    d->input_tail = command;
}

static void set_userstring(char **userstring, const char *command) {
    while (*command && isascii(*command) && isspace(*command))
        command++;
    if (!*command) {
        if (*userstring != NULL) {
            free_lbuf(*userstring);
            *userstring = NULL;
        }
    } else {
        if (*userstring == NULL) {
            *userstring = alloc_lbuf("set_userstring");
        }
        StringCopy(*userstring, command);
    }
}

static void parse_connect(const char *msg, char *command, char *user, char *pass) {
    char *p;

    if (strlen(msg) > MBUF_SIZE) {
        *command = '\0';
        *user = '\0';
        *pass = '\0';
        return;
    }
    while (*msg && isascii(*msg) && isspace(*msg))
        msg++;
    p = command;
    while (*msg && isascii(*msg) && !isspace(*msg))
        *p++ = *msg++;
    *p = '\0';
    while (*msg && isascii(*msg) && isspace(*msg))
        msg++;
    p = user;
    if (mudconf.name_spaces && (*msg == '\"')) {
        for (; *msg && (*msg == '\"' || isspace(*msg)); msg++);
        while (*msg && *msg != '\"') {
            while (*msg && !isspace(*msg) && (*msg != '\"'))
                *p++ = *msg++;
            if (*msg == '\"')
                break;
            while (*msg && isspace(*msg))
                msg++;
            if (*msg && (*msg != '\"'))
                *p++ = ' ';
        }
        for (; *msg && *msg == '\"'; msg++);
    } else
        while (*msg && isascii(*msg) && !isspace(*msg))
            *p++ = *msg++;
    *p = '\0';
    while (*msg && isascii(*msg) && isspace(*msg))
        msg++;
    p = pass;
    while (*msg && isascii(*msg) && !isspace(*msg))
        *p++ = *msg++;
    *p = '\0';
}

static const char *time_format_1(time_t dt) {
    register struct tm *delta;
    static char buf[64];

    if (dt < 0)
        dt = 0;

    delta = gmtime(&dt);
    if (delta->tm_yday > 0) {
        sprintf(buf, "%dd %02d:%02d", delta->tm_yday, delta->tm_hour,
                delta->tm_min);
    } else {
        sprintf(buf, "%02d:%02d", delta->tm_hour, delta->tm_min);
    }
    return buf;
}

static const char *time_format_2(time_t dt) {
    register struct tm *delta;
    static char buf[64];

    if (dt < 0)
        dt = 0;

    delta = gmtime(&dt);
    if (delta->tm_yday > 0) {
        sprintf(buf, "%dd", delta->tm_yday);
    } else if (delta->tm_hour > 0) {
        sprintf(buf, "%dh", delta->tm_hour);
    } else if (delta->tm_min > 0) {
        sprintf(buf, "%dm", delta->tm_min);
    } else {
        sprintf(buf, "%ds", delta->tm_sec);
    }
    return buf;
}

static void announce_connect(dbref player, DESC *d) {
    dbref loc, aowner, temp;
    dbref zone, obj;

    int aflags, num, key, count;
    char *buf, *time_str;
    DESC *dtemp;

    desc_addhash(d);

    choke_player(player);

    count = 0;
    DESC_ITER_CONN(dtemp)
        count++;

    if (mudstate.record_players < count)
        mudstate.record_players = count;

    buf = atr_pget(player, A_TIMEOUT, &aowner, &aflags);
    if (buf) {
        d->timeout = atoi(buf);
        if (d->timeout <= 0)
            d->timeout = mudconf.idle_timeout;
    }
    free_lbuf(buf);

    loc = Location(player);
    s_Connected(player);

    if (d->flags & DS_PUEBLOCLIENT) {
        s_Html(player);
    }

    raw_notify(player, tprintf("\n%sMOTD:%s %s\n", ANSI_HILITE,
                ANSI_NORMAL, mudconf.motd_msg));
    if (Wizard(player)) {
        raw_notify(player, tprintf("%sWIZMOTD:%s %s\n", ANSI_HILITE,
                    ANSI_NORMAL, mudconf.wizmotd_msg));
        if (!(mudconf.control_flags & CF_LOGIN)) {
            raw_notify(player, "*** Logins are disabled.");
        }
    }
    buf = atr_get(player, A_LPAGE, &aowner, &aflags);
    if (buf && *buf) {
        raw_notify(player,
                "Your PAGE LOCK is set.  You may be unable to receive some pages.");
    }
    num = 0;
    DESC_ITER_PLAYER(player, dtemp) num++;

    /*
     * Reset vacation flag 
     */
    s_Flags2(player, Flags2(player) & ~VACATION);

    if (num < 2) {
        sprintf(buf, "%s has connected.", Name(player));

        if (mudconf.have_comsys)
            do_comconnect(player, d);

        if (Dark(player)) {
            raw_broadcast(MONITOR, (char *) "GAME: %s has DARK-connected.",
                    Name(player), 0, 0, 0, 0, 0);
        } else {
            raw_broadcast(MONITOR, (char *) "GAME: %s has connected.",
                    Name(player), 0, 0, 0, 0, 0);
        }
    } else {
        sprintf(buf, "%s has reconnected.", Name(player));
        raw_broadcast(MONITOR, (char *) "GAME: %s has reconnected.",
                Name(player), 0, 0, 0, 0, 0);
    }

    key = MSG_INV;
    if ((loc != NOTHING) && !(Dark(player) && Wizard(player)))
        key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST);

    temp = mudstate.curr_enactor;
    mudstate.curr_enactor = player;
    notify_checked(player, player, buf, key);
    free_lbuf(buf);
    if (Suspect(player)) {
        send_channel("Suspect", "%s has connected.", Name(player));

    }
    if (d->host_info & H_SUSPECT)
        send_channel("Suspect", "[Suspect site: %s] %s has connected.", d->addr,
                    Name(player));
    buf = atr_pget(player, A_ACONNECT, &aowner, &aflags);
    if (buf)
        wait_que(player, player, 0, NOTHING, 0, buf, (char **) NULL, 0,
                NULL);
    free_lbuf(buf);
    if (mudconf.master_room != NOTHING) {
        buf = atr_pget(mudconf.master_room, A_ACONNECT, &aowner, &aflags);
        if (buf)
            wait_que(mudconf.master_room, player, 0, NOTHING, 0, buf,
                    (char **) NULL, 0, NULL);
        free_lbuf(buf);
        DOLIST(obj, Contents(mudconf.master_room)) {
            buf = atr_pget(obj, A_ACONNECT, &aowner, &aflags);
            if (buf) {
                wait_que(obj, player, 0, NOTHING, 0, buf, (char **) NULL,
                        0, NULL);
            }
            free_lbuf(buf);
        }
    }
    /*
     * do the zone of the player's location's possible aconnect 
     */
    if (mudconf.have_zones && ((zone = Zone(loc)) != NOTHING)) {
        switch (Typeof(zone)) {
            case TYPE_THING:
                buf = atr_pget(zone, A_ACONNECT, &aowner, &aflags);
                if (buf) {
                    wait_que(zone, player, 0, NOTHING, 0, buf, (char **) NULL,
                            0, NULL);
                }
                free_lbuf(buf);
                break;
            case TYPE_ROOM:
                /*
                 * check every object in the room for a connect * * * 
                 * action 
                 */
                DOLIST(obj, Contents(zone)) {
                    buf = atr_pget(obj, A_ACONNECT, &aowner, &aflags);
                    if (buf) {
                        wait_que(obj, player, 0, NOTHING, 0, buf,
                                (char **) NULL, 0, NULL);
                    }
                    free_lbuf(buf);
                }
                break;
            default:
                log_text(tprintf
                        ("Invalid zone #%d for %s(#%d) has bad type %d", zone,
                         Name(player), player, Typeof(zone)));
        }
    }
    time_str = ctime(&mudstate.now);
    time_str[strlen(time_str) - 1] = '\0';
    record_login(player, 1, time_str, d->addr, d->username);
    look_in(player, Location(player),
            (LK_SHOWEXIT | LK_OBEYTERSE | LK_SHOWVRML));
    mudstate.curr_enactor = temp;
    release_player(player);
}

void announce_disconnect(dbref player, DESC *d, const char *reason) {
    dbref loc, aowner, temp, zone, obj;
    int num, aflags, key;
    char *buf, *atr_temp;
    DESC *dtemp;
    char *argv[1];


    if (Suspect(player)) {
        send_channel("Suspect", "%s has disconnected.", Name(player));
    }
    if (d->host_info & H_SUSPECT) {
        send_channel("Suspect", "[Suspect site: %s] %s has disconnected.", 
                d->addr, Name(d->player));
    }
    loc = Location(player);
    num = 0;
    DESC_ITER_PLAYER(player, dtemp) num++;

    temp = mudstate.curr_enactor;
    mudstate.curr_enactor = player;

    if (num < 2) {
        buf = alloc_mbuf("announce_disconnect.only");

        sprintf(buf, "%s has disconnected.", Name(player));
        key = MSG_INV;
        if ((loc != NOTHING) && !(Dark(player) && Wizard(player)))
            key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST);
        notify_checked(player, player, buf, key);
        free_mbuf(buf);

        if (mudconf.have_comsys)
            do_comdisconnect(player);

        if (mudconf.have_mailer)
            do_mail_purge(player);

        raw_broadcast(MONITOR, (char *) "GAME: %s has disconnected.",
                Name(player), 0, 0, 0, 0, 0);

        if (Guest(player) && mudconf.have_comsys)
            toast_player(player);

        argv[0] = (char *) reason;
        c_Connected(player);

        atr_temp = atr_pget(player, A_ADISCONNECT, &aowner, &aflags);
        if (atr_temp && *atr_temp)
            wait_que(player, player, 0, NOTHING, 0, atr_temp, argv, 1,
                    NULL);
        free_lbuf(atr_temp);
        if (mudconf.master_room != NOTHING) {
            atr_temp =
                atr_pget(mudconf.master_room, A_ADISCONNECT, &aowner,
                        &aflags);
            if (atr_temp)
                wait_que(mudconf.master_room, player, 0, NOTHING, 0,
                        atr_temp, (char **) NULL, 0, NULL);
            free_lbuf(atr_temp);
            DOLIST(obj, Contents(mudconf.master_room)) {
                atr_temp = atr_pget(obj, A_ADISCONNECT, &aowner, &aflags);
                if (atr_temp) {
                    wait_que(obj, player, 0, NOTHING, 0, atr_temp,
                            (char **) NULL, 0, NULL);
                }
                free_lbuf(atr_temp);
            }
        }
        /*
         * do the zone of the player's location's possible * * *
         * adisconnect 
         */
        if (mudconf.have_zones && ((zone = Zone(loc)) != NOTHING)) {
            switch (Typeof(zone)) {
                case TYPE_THING:
                    atr_temp = atr_pget(zone, A_ADISCONNECT, &aowner, &aflags);
                    if (atr_temp) {
                        wait_que(zone, player, 0, NOTHING, 0, atr_temp,
                                (char **) NULL, 0, NULL);
                    }
                    free_lbuf(atr_temp);
                    break;
                case TYPE_ROOM:
                    /*
                     * check every object in the room for a * * * 
                     * connect action 
                     */
                    DOLIST(obj, Contents(zone)) {
                        atr_temp =
                            atr_pget(obj, A_ADISCONNECT, &aowner, &aflags);
                        if (atr_temp) {
                            wait_que(obj, player, 0, NOTHING, 0, atr_temp,
                                    (char **) NULL, 0, NULL);
                        }
                        free_lbuf(atr_temp);
                    }
                    break;
                default:
                    log_text(tprintf
                            ("Invalid zone #%d for %s(#%d) has bad type %d", zone,
                             Name(player), player, Typeof(zone)));
            }
        }
        if (d->flags & DS_AUTODARK) {
            s_Flags(d->player, Flags(d->player) & ~DARK);
            d->flags &= ~DS_AUTODARK;
        }

        if (Guest(player))
            s_Flags(player, Flags(player) | DARK);
    } else {
        buf = alloc_mbuf("announce_disconnect.partial");
        sprintf(buf, "%s has partially disconnected.", Name(player));
        key = MSG_INV;
        if ((loc != NOTHING) && !(Dark(player) && Wizard(player)))
            key |= (MSG_NBR | MSG_NBR_EXITS | MSG_LOC | MSG_FWDLIST);
        notify_checked(player, player, buf, key);
        raw_broadcast(MONITOR,
                (char *) "GAME: %s has partially disconnected.", Name(player),
                0, 0, 0, 0, 0);
        free_mbuf(buf);
    }

    mudstate.curr_enactor = temp;
    release_player(player);
    desc_delhash(d);
}

int boot_off(dbref player, char *message) {
    DESC *d, *dnext;
    int count;

    count = 0;
    DESC_SAFEITER_PLAYER(player, d, dnext) {
        if (message && *message) {
            queue_string(d, message);
            queue_string(d, "\r\n");
        }
        shutdownsock(d, R_BOOT);
        count++;
    }
    return count;
}

int boot_by_port(int port, int no_god, char *message) {
    DESC *d, *dnext;
    int count;

    count = 0;
    DESC_SAFEITER_ALL(d, dnext) {
        if ((d->descriptor == port) && (!no_god || !God(d->player))) {
            if (message && *message) {
                queue_string(d, message);
                queue_string(d, "\r\n");
            }
            shutdownsock(d, R_BOOT);
            count++;
        }
    }
    return count;
}

/*
 * ---------------------------------------------------------------------------
 * * desc_reload: Reload parts of net descriptor that are based on db info.
 */

void desc_reload(dbref player) {
    DESC *d;
    char *buf;
    dbref aowner;
    FLAG aflags;

    DESC_ITER_PLAYER(player, d) {
        buf = atr_pget(player, A_TIMEOUT, &aowner, &aflags);
        if (buf) {
            d->timeout = atoi(buf);
            if (d->timeout <= 0)
                d->timeout = mudconf.idle_timeout;
        }
        free_lbuf(buf);
    }
}

/*
 * ---------------------------------------------------------------------------
 * * fetch_idle, fetch_connect: Return smallest idle time/largest connec time
 * * for a player (or -1 if not logged in)
 */

int fetch_idle(dbref target) {
    DESC *d;
    int result, idletime;

    result = -1;
    DESC_ITER_PLAYER(target, d) {
        idletime = (mudstate.now - d->last_time);
        if ((result == -1) || (idletime < result))
            result = idletime;
    }
    return result;
}

int fetch_connect(dbref target) {
    DESC *d;
    int result, conntime;

    result = -1;
    DESC_ITER_PLAYER(target, d) {
        conntime = (mudstate.now - d->connected_at);
        if (conntime > result)
            result = conntime;
    }
    return result;
}

void check_idle(void) {
    DESC *d, *dnext;
    time_t idletime;

    DESC_SAFEITER_ALL(d, dnext) {
        if (d->flags & DS_CONNECTED) {
            idletime = mudstate.now - d->last_time;
            if ((idletime > d->timeout) && !Can_Idle(d->player)) {
                queue_string(d, "*** Inactivity Timeout ***\r\n");
                shutdownsock(d, R_TIMEOUT);
            } else if (mudconf.idle_wiz_dark &&
                    (idletime > mudconf.idle_timeout) && Can_Idle(d->player) &&
                    !Dark(d->player)) {
                s_Flags(d->player, Flags(d->player) | DARK);
                d->flags |= DS_AUTODARK;
            }
        } else {
            idletime = mudstate.now - d->connected_at;
            if (idletime > mudconf.conn_timeout) {
                queue_string(d, "*** Login Timeout ***\r\n");
                shutdownsock(d, R_TIMEOUT);
            }
        }
    }
}

void check_events(void) {
    struct tm *ltime;
    dbref thing, parent;
    int lev;

    ltime = localtime(&mudstate.now);
    if ((ltime->tm_hour == mudconf.events_daily_hour)
            && !(mudstate.events_flag & ET_DAILY)) {
        mudstate.events_flag = mudstate.events_flag | ET_DAILY;
        DO_WHOLE_DB(thing) {
            if (Going(thing))
                continue;

            ITER_PARENTS(thing, parent, lev) {
                if (Flags2(thing) & HAS_DAILY) {
                    did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_DAILY,
                            (char **) NULL, 0);

                    break;
                }
            }
        }
    }
    if (ltime->tm_hour != mudstate.events_lasthour) {
        if (mudstate.events_lasthour >= 0) {
            /* Run hourly maintenance */
            DO_WHOLE_DB(thing) {
                if (Going(thing))
                    continue;

                ITER_PARENTS(thing, parent, lev) {
                    if (Flags2(thing) & HAS_HOURLY) {
                        did_it(Owner(thing), thing, 0, NULL, 0, NULL,
                                A_HOURLY, (char **) NULL, 0);

                        break;
                    }
                }
            }

        }
        mudstate.events_lasthour = ltime->tm_hour;
    }
    if (ltime->tm_hour == 23) {	/*
                                 * Nightly resetting 
                                 */
        mudstate.events_flag = 0;
    }
}
static char *trimmed_name(dbref player) {
    static char cbuff[18];

    if (strlen(Name(player)) <= 16)
        return Name(player);
    StringCopyTrunc(cbuff, Name(player), 16);
    cbuff[16] = '\0';
    return cbuff;
}

static char *trimmed_site( char *name) {
    static char buff[MBUF_SIZE];

    if ((strlen(name) <= mudconf.site_chars) || (mudconf.site_chars == 0))
        return name;
    StringCopyTrunc(buff, name, mudconf.site_chars);
    buff[mudconf.site_chars + 1] = '\0';
    return buff;
}

static void dump_users(DESC *e, char *match, int key) {
    DESC *d;
    int count, rcount;
    char *buf, *fp, *sp, flist[4], slist[4];
    dbref room_it;

    while (match && *match && isspace(*match))
        match++;
    if (!match || !*match)
        match = NULL;

    if (e->flags & DS_PUEBLOCLIENT)
        queue_string(e, "<pre>");

    buf = alloc_mbuf("dump_users");
    if (key == CMD_SESSION) {
        queue_string(e, "                               ");
        queue_string(e,
                "     Characters Input----  Characters Output---\r\n");
    }
    queue_string(e, "Player Name        On For Idle ");
    if (key == CMD_SESSION) {
        queue_string(e,
                "Port Pend  Lost     Total  Pend  Lost     Total\r\n");
    } else if ((e->flags & DS_CONNECTED) && (Wizard_Who(e->player)) &&
            (key == CMD_WHO)) {
        queue_string(e, "  Room    Cmds   Host\r\n");
    } else {
        if (Wizard_Who(e->player))
            queue_string(e, "  ");
        else
            queue_string(e, " ");
        queue_string(e, mudstate.doing_hdr);
        queue_string(e, "\r\n");
    }
    count = 0;
    rcount = 0;
    DESC_ITER_CONN(d) {
        if (!Hidden(d->player) ||
                (e->flags & DS_CONNECTED) & Wizard_Who(e->player)) {
            count++;
            if (match && !(string_prefix(Name(d->player), match)))
                continue;
#if 0
            if ((!((Wizard_Who(e->player)) && (e->flags & DS_CONNECTED)) &&
                        (d->player != e->player)))
                if (In_Character(Location(d->player)) &&
                        In_Character(Location(Location(d->player))))
                    continue;
#endif
            rcount++;
            if ((key == CMD_SESSION) && !(Wizard_Who(e->player) &&
                        (e->flags & DS_CONNECTED)) && (d->player != e->player))
                continue;

            /*
             * Get choice flags for wizards 
             */

            fp = flist;
            sp = slist;
            if ((e->flags & DS_CONNECTED) && Wizard_Who(e->player)) {
                if (Hidden(d->player)) {
                    if (d->flags & DS_AUTODARK)
                        *fp++ = 'd';
                    else if (Dark(d->player))
                        *fp++ = 'D';
                }
                if (!Findable(d->player)) {
                    *fp++ = 'U';
                } else {
                    room_it = where_room(d->player);
                    if (Good_obj(room_it)) {
                        if (Hideout(room_it))
                            *fp++ = 'u';
                    } else {
                        *fp++ = 'u';
                    }
                }

                if (Suspect(d->player))
                    *fp++ = '+';
                if (d->host_info & H_FORBIDDEN)
                    *sp++ = 'F';
                if (d->host_info & H_REGISTRATION)
                    *sp++ = 'R';
                if (d->host_info & H_SUSPECT)
                    *sp++ = '+';
            }
            *fp = '\0';
            *sp = '\0';

            if ((e->flags & DS_CONNECTED) && Wizard_Who(e->player) &&
                    (key == CMD_WHO)) {
                sprintf(buf, "%-16s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n",
                        trimmed_name(d->player),
                        time_format_1(mudstate.now - d->connected_at),
                        time_format_2(mudstate.now - d->last_time), flist,
                        Location(d->player), d->command_count, slist,
                        trimmed_site(((d->username[0] !=
                                    '\0') ? tprintf("%s@%s", d->username,
                                        d->addr) : d->addr)));
            } else if (key == CMD_SESSION) {
                sprintf(buf, "%-16s%9s %4s%5d%5d%6d%10d%6d%6d%10d\r\n",
                        trimmed_name(d->player),
                        time_format_1(mudstate.now - d->connected_at),
                        time_format_2((mudstate.now - d->last_time) >
                            HIDDEN_IDLESECS ? (mudstate.now -
                                d->last_time) : 0), d->descriptor,
                        d->input_size, d->input_lost, d->input_tot,
                        d->output_size, d->output_lost, d->output_tot);
            } else if (Wizard_Who(e->player)) {
                sprintf(buf, "%-16s%9s %4s%-3s%s\r\n",
                        trimmed_name(d->player),
                        time_format_1(mudstate.now - d->connected_at),
                        time_format_2((mudstate.now - d->last_time) >
                            HIDDEN_IDLESECS ? (mudstate.now -
                                d->last_time) : 0), flist, d->doing);
            } else {
                sprintf(buf, "%-16s%9s %4s  %s\r\n",
                        trimmed_name(d->player),
                        time_format_1(mudstate.now - d->connected_at),
                        time_format_2((mudstate.now - d->last_time) >
                            HIDDEN_IDLESECS ? (mudstate.now -
                                d->last_time) : 0), d->doing);
            }
            queue_string(e, buf);
        }
    }
    count = rcount;		/* previous mode was .. disgusting. */
    /*
     * sometimes I like the ternary operator.... 
     */

    sprintf(buf, "%d Player%slogged in, %d record, %s maximum.\r\n", count,
            (count == 1) ? " " : "s ", mudstate.record_players,
            (mudconf.max_players == -1) ? "no" : tprintf("%d",
                                                         mudconf.max_players));
    queue_string(e, buf);

    if (e->flags & DS_PUEBLOCLIENT)
        queue_string(e, "</pre>");

    free_mbuf(buf);
}

/*
 * ---------------------------------------------------------------------------
 * * do_doing: Set the doing string that appears in the WHO report.
 * * Idea from R'nice@TinyTIM.
 */

void do_doing(dbref player, dbref cause, int key, char *arg) {
    DESC *d;
    char *c, *e;
    int foundany, over;

    if (key == DOING_MESSAGE) {
        foundany = 0;
        over = 0;
        DESC_ITER_PLAYER(player, d) {
            c = d->doing;

            over =
                safe_copy_str(arg, d->doing, &c,
                        DOINGLEN - 2 - strlen(ANSI_NORMAL));
            /* See if there's <esc>[<numbers> as the last remaining stuff */
            if (over) {
                e = c;
                c--;
                if (isdigit(*c)) {
                    while (isdigit(*c) && c > e)
                        c--;
                    if (*c == '[') {
                        c--;
                        if (c > e && *c == '\033') {
                            *c = 0;
                            e = c;
                        }
                    }
                }
                StringCopy(e, ANSI_NORMAL);
            } else
                StringCopy(c, ANSI_NORMAL);
            foundany = 1;
        }
        if (foundany) {
            if (over) {
                notify_printf(player, "Warning: %d characters lost.",
                            over);
            }
            if (!Quiet(player))
                notify(player, "Set.");
        } else {
            notify(player, "Not connected.");
        }
    } else if (key == DOING_HEADER) {
        if (!(Can_Poll(player))) {
            notify(player, "Permission denied.");
            return;
        }
        if (!arg || !*arg) {
            StringCopy(mudstate.doing_hdr, "Doing");
            over = 0;
        } else {
            c = mudstate.doing_hdr;
            over =
                safe_copy_str(arg, mudstate.doing_hdr, &c, DOINGLEN - 1);
            *c = '\0';
        }
        if (over) {
            notify(player, tprintf("Warning: %d characters lost.", over));
        }
        if (!Quiet(player))
            notify(player, "Set.");
    } else {
        notify(player, tprintf("Poll: %s", mudstate.doing_hdr));
    }
}
/* *INDENT-OFF* */

NAMETAB logout_cmdtable[] = {
    {(char *)"DOING",	5,	CA_PUBLIC,	CMD_DOING},
    {(char *)"LOGOUT",	6,	CA_PUBLIC,	CMD_LOGOUT},
    {(char *)"OUTPUTPREFIX",12,	CA_PUBLIC,	CMD_PREFIX|CMD_NOxFIX},
    {(char *)"OUTPUTSUFFIX",12,	CA_PUBLIC,	CMD_SUFFIX|CMD_NOxFIX},
    {(char *)"QUIT",	4,	CA_PUBLIC,	CMD_QUIT},
    {(char *)"SESSION",	7,	CA_PUBLIC,	CMD_SESSION},
    {(char *)"WHO",		3,	CA_PUBLIC,	CMD_WHO},
    {(char *)"PUEBLOCLIENT", 12,	CA_PUBLIC,      CMD_PUEBLOCLIENT},
    {NULL,			0,	0,		0}};

/* *INDENT-ON* */

void init_logout_cmdtab(void) {
    NAMETAB *cp;

    /*
     * Make the htab bigger than the number of entries so that we find
     * things on the first check.  Remember that the admin can add
     * aliases. 
     */

    hashinit(&mudstate.logout_cmd_htab, 3 * HASH_FACTOR);
    for (cp = logout_cmdtable; cp->flag; cp++)
        hashadd(cp->name, (int *) cp, &mudstate.logout_cmd_htab);
}

static void failconn(const char *logcode, const char *logtype, const char *logreason, 
        DESC *d, int disconnect_reason, dbref player, int filecache, char *motd_msg, 
        char *command, char *user, char *password, char *cmdsave) {
    char *buff;

    STARTLOG(LOG_LOGIN | LOG_SECURITY, logcode, "RJCT") {
        buff = alloc_mbuf("failconn.LOG");
        sprintf(buff, "[%d/%s] %s rejected to ", d->descriptor, d->addr,
                logtype);
        log_text(buff);
        free_mbuf(buff);
        if (player != NOTHING)
            log_name(player);
        else
            log_text(user);
        log_text((char *) " (");
        log_text((char *) logreason);
        log_text((char *) ")");
        ENDLOG;
    } fcache_dump(d, filecache);

    if (*motd_msg) {
        queue_string(d, motd_msg);
        queue_write(d, "\r\n", 2);
    }
    free_lbuf(command);
    free_lbuf(user);
    free_lbuf(password);
    shutdownsock(d, disconnect_reason);
    mudstate.debug_cmd = cmdsave;
    return;
}

static const char *connect_fail =
"Either that player does not exist, or has a different password.\r\n";
static const char *create_fail =
"Either there is already a player with that name, or that name is illegal.\r\n";

static int check_connect(DESC *d, char *msg) {
    char *command, *user, *password, *buff, *cmdsave;
    dbref player, aowner;
    int aflags, nplayers;
    DESC *d2;
    char *p;

    cmdsave = mudstate.debug_cmd;
    mudstate.debug_cmd = (char *) "< check_connect >";

    /*
     * Hide the password length from SESSION 
     */

    d->input_tot -= (strlen(msg) + 1);

    /*
     * Crack the command apart 
     */

    command = alloc_lbuf("check_conn.cmd");
    user = alloc_lbuf("check_conn.user");
    password = alloc_lbuf("check_conn.pass");
    parse_connect(msg, command, user, password);

    if (!strncmp(command, "co", 2) || !strncmp(command, "cd", 2)) {
        if ((string_prefix(user, mudconf.guest_prefix)) &&
                (mudconf.guest_char != NOTHING) &&
                (mudconf.control_flags & CF_LOGIN)) {
            if ((p = make_guest(d)) == NULL) {
                queue_string(d,
                        "All guests are tied up, please try again later.\n");
                free_lbuf(command);
                free_lbuf(user);
                free_lbuf(password);
                return 0;
            }
            StringCopy(user, p);
            StringCopy(password, mudconf.guest_prefix);
        }
        /*
         * See if this connection would exceed the max #players 
         */

        if (mudconf.max_players < 0) {
            nplayers = mudconf.max_players - 1;
        } else {
            nplayers = 0;
            DESC_ITER_CONN(d2)
                nplayers++;
        }

        player = connect_player(user, password, d->addr, d->username);
        if (player == NOTHING) {

            /*
             * Not a player, or wrong password 
             */

            queue_string(d, connect_fail);
            STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD") {
                buff = alloc_lbuf("check_conn.LOG.bad");
                user[3800] = '\0';
                sprintf(buff, "[%d/%s] Failed connect to '%s'",
                        d->descriptor, d->addr, user);
                log_text(buff);
                free_lbuf(buff);
                ENDLOG;
            }
            if (--(d->retries_left) <= 0) {
                free_lbuf(command);
                free_lbuf(user);
                free_lbuf(password);
                shutdownsock(d, R_BADLOGIN);
                mudstate.debug_cmd = cmdsave;
                return 0;
            }
        } else if (((mudconf.control_flags & CF_LOGIN) &&
                    (nplayers < mudconf.max_players)) || WizRoy(player) ||
                God(player)) {

            if (!strncmp(command, "cd", 2) && (Wizard(player) ||
                        God(player)))
                s_Flags(player, Flags(player) | DARK);

            /*
             * Logins are enabled, or wiz or god 
             */

            STARTLOG(LOG_LOGIN, "CON", "LOGIN") {
                buff = alloc_mbuf("check_conn.LOG.login");
                sprintf(buff, "[%d/%s] Connected to ", d->descriptor,
                        d->addr);
                log_text(buff);
                log_name_and_loc(player);
                free_mbuf(buff);
                ENDLOG;
            }
            d->flags |= DS_CONNECTED;

            d->connected_at = time(0);
            d->player = player;
            set_lastsite(d, NULL);

            /* Check to see if the player is currently running
             * an @program. If so, drop the new descriptor into
             * it.
             */

            DESC_ITER_PLAYER(player, d2) {
                if (d2->program_data != NULL) {
                    d->program_data = d2->program_data;
                    break;
                }
            }

            /*
             * Give the player the MOTD file and the settable * * 
             * 
             * * MOTD * message(s). Use raw notifies so the
             * player * * * doesn't * try to match on the text. 
             */

            if (Guest(player)) {
                fcache_dump(d, FC_CONN_GUEST);
            } else {
                buff = atr_get(player, A_LAST, &aowner, &aflags);
                if ((buff == NULL) || (*buff == '\0'))
                    fcache_dump(d, FC_CREA_NEW);
                else
                    fcache_dump(d, FC_MOTD);
                if (Wizard(player))
                    fcache_dump(d, FC_WIZMOTD);
                free_lbuf(buff);
            }
            announce_connect(player, d);

            /* If stuck in an @prog, show the prompt */

            if (d->program_data != NULL)
                queue_string(d, ">\377\371");

        } else if (!(mudconf.control_flags & CF_LOGIN)) {
            failconn("CON", "Connect", "Logins Disabled", d, R_GAMEDOWN,
                    player, FC_CONN_DOWN, mudconf.downmotd_msg, command, user,
                    password, cmdsave);
            return 0;
        } else {
            failconn("CON", "Connect", "Game Full", d, R_GAMEFULL, player,
                    FC_CONN_FULL, mudconf.fullmotd_msg, command, user,
                    password, cmdsave);
            return 0;
        }
    } else if (!strncmp(command, "cr", 2)) {

        /*
         * Enforce game down 
         */

        if (!(mudconf.control_flags & CF_LOGIN)) {
            failconn("CRE", "Create", "Logins Disabled", d, R_GAMEDOWN,
                    NOTHING, FC_CONN_DOWN, mudconf.downmotd_msg, command, user,
                    password, cmdsave);
            return 0;
        }
        /*
         * Enforce max #players 
         */

        if (mudconf.max_players < 0) {
            nplayers = mudconf.max_players;
        } else {
            nplayers = 0;
            DESC_ITER_CONN(d2)
                nplayers++;
        }
        if (nplayers > mudconf.max_players) {

            /*
             * Too many players on, reject the attempt 
             */

            failconn("CRE", "Create", "Game Full", d, R_GAMEFULL, NOTHING,
                    FC_CONN_FULL, mudconf.fullmotd_msg, command, user,
                    password, cmdsave);
            return 0;
        }
        if (d->host_info & H_REGISTRATION) {
            fcache_dump(d, FC_CREA_REG);
        } else {
            player = create_player(user, password, NOTHING, 0, 0);
            if (player == NOTHING) {
                queue_string(d, create_fail);
                STARTLOG(LOG_SECURITY | LOG_PCREATES, "CON", "BAD") {
                    buff = alloc_mbuf("check_conn.LOG.badcrea");
                    sprintf(buff, "[%d/%s] Create of '%s' failed",
                            d->descriptor, d->addr, user);
                    log_text(buff);
                    free_mbuf(buff);
                    ENDLOG;
                }
            } else {
                STARTLOG(LOG_LOGIN | LOG_PCREATES, "CON", "CREA") {
                    buff = alloc_mbuf("check_conn.LOG.create");
                    sprintf(buff, "[%d/%s] Created ", d->descriptor,
                            d->addr);
                    log_text(buff);
                    log_name(player);
                    free_mbuf(buff);
                    ENDLOG;
                }
                move_object(player, mudconf.start_room);

                d->flags |= DS_CONNECTED;
                d->connected_at = time(0);
                d->player = player;
                set_lastsite(d, NULL);
                fcache_dump(d, FC_CREA_NEW);
                announce_connect(player, d);
            }
        }
    } else {
        welcome_user(d);
        STARTLOG(LOG_LOGIN | LOG_SECURITY, "CON", "BAD") {
            buff = alloc_mbuf("check_conn.LOG.bad");
            msg[150] = '\0';
            sprintf(buff, "[%d/%s] Failed connect: '%s'", d->descriptor,
                    d->addr, msg);
            log_text(buff);
            free_mbuf(buff);
            ENDLOG;
        }
    }
    free_lbuf(command);
    free_lbuf(user);
    free_lbuf(password);

    mudstate.debug_cmd = cmdsave;
    return 1;
}

int do_command(DESC *d, char *command, int first) {
    char *arg, *cmdsave;
    NAMETAB *cp;

    cmdsave = mudstate.debug_cmd;
    mudstate.debug_cmd = (char *) "< do_command >";
    d->last_time = mudstate.now;

    /*
     * Split off the command from the arguments 
     */

    arg = command;
    while (*arg && !isspace(*arg))
        arg++;
    if (*arg)
        *arg++ = '\0';

#ifdef HUDINFO_SUPPORT
    /* We check for hudinfo before anything else.  This is a fairly dirty
     * hack, and slows down the common case by a strcmp (which is fast on
     * modern processors and libraries) but has many advantages: hudinfo
     * only outputs to the *connection* (rather than the player) that issued
     * the command, and always knows which hud session key to use. I think
     * the payoff is justified, in this case.
     */

    if (mudconf.hudinfo_enabled > 0 && d->flags & DS_CONNECTED && !strcmp(command, "hudinfo")) {
        d->command_count++;
        mudstate.curr_player = d->player;
        mudstate.curr_enactor = d->player;
        mudstate.debug_cmd = "hudinfo";
        do_hudinfo(d, arg);
        mudstate.debug_cmd = cmdsave;
        return 1;
    }
#endif

    /*
     * Look up the command.  If we don't find it, turn it over to the normal
     * logged-in command processor or to create/connect
     */

    if (!(d->flags & DS_CONNECTED)) {
        cp = (NAMETAB *) hashfind(command, &mudstate.logout_cmd_htab);
    } else
        cp = NULL;

    if (cp == NULL) {
        if (*arg)
            *--arg = ' ';	/*
            * restore nullified space 
            */
        if (d->flags & DS_CONNECTED) {
            d->command_count++;
            choke_player(d->player);
            if (d->output_prefix) {
                queue_string(d, d->output_prefix);
                queue_write(d, "\r\n", 2);
            }
            mudstate.curr_player = d->player;
            mudstate.curr_enactor = d->player;
            process_command(d->player, d->player, 1, command,
                    (char **) NULL, 0);
            if (d->output_suffix) {
                queue_string(d, d->output_suffix);
                queue_write(d, "\r\n", 2);
            }
            release_player(d->player);
            mudstate.debug_cmd = cmdsave;
            return 1;
        } else {
            mudstate.debug_cmd = cmdsave;
            return (check_connect(d, command));
        }
    }
    /*
     * The command was in the logged-out command table.  Perform prefix and
     * suffix processing, and invoke the command handler.
     */

    d->command_count++;
    if (!(cp->flag & CMD_NOxFIX)) {
        if (d->output_prefix) {
            queue_string(d, d->output_prefix);
            queue_write(d, "\r\n", 2);
        }
    }
    if ((!check_access(d->player, cp->perm)) || ((cp->perm & CA_PLAYER) &&
                !(d->flags & DS_CONNECTED))) {
        queue_string(d, "Permission denied.\r\n");
    } else {
        mudstate.debug_cmd = cp->name;
        switch (cp->flag & CMD_MASK) {
            case CMD_QUIT:
                shutdownsock(d, R_QUIT);
                mudstate.debug_cmd = cmdsave;
                return 0;
            case CMD_LOGOUT:
                shutdownsock(d, R_LOGOUT);
                break;
            case CMD_WHO:
                if (d->player || mudconf.allow_unloggedwho) {
                    dump_users(d, arg, CMD_WHO);
                } else {
                    queue_string(d,
                            "This MUX does not allow WHO at the login screen.\r\nPlease login or create a character first.\r\n");
                }
                break;
            case CMD_DOING:
                if (d->player || mudconf.allow_unloggedwho) {
                    dump_users(d, arg, CMD_DOING);
                } else {
                    queue_string(d,
                            "This MUX does not allow DOING at the login screen.\r\nPlease login or create a character first.\r\n");
                }
                break;
            case CMD_SESSION:
                if (d->player || mudconf.allow_unloggedwho) {
                    dump_users(d, arg, CMD_SESSION);
                } else {
                    queue_string(d,
                            "This MUX does not allow SESSION at the login screen.\r\nPlease login or create a character first.\r\n");
                }
                break;
            case CMD_PREFIX:
                set_userstring(&d->output_prefix, arg);
                break;
            case CMD_SUFFIX:
                set_userstring(&d->output_suffix, arg);
                break;
            case CMD_PUEBLOCLIENT:
                /* Set the descriptor's flag */
                d->flags |= DS_PUEBLOCLIENT;
                /* If we're already connected, set the player's flag */
                if (d->player) {
                    s_Html(d->player);
                }
                queue_string(d, mudconf.pueblo_msg);
                queue_string(d, "\r\n");
                break;
            default:
                STARTLOG(LOG_BUGS, "BUG", "PARSE") {
                    arg = alloc_lbuf("do_command.LOG");
                    sprintf(arg, "Prefix command with no handler: '%s'",
                            command);
                    log_text(arg);
                    free_lbuf(arg);
                    ENDLOG;
                }
        }
    }
    if (!(cp->flag & CMD_NOxFIX)) {
        if (d->output_prefix) {
            queue_string(d, d->output_suffix);
            queue_write(d, "\r\n", 2);
        }
    }
    mudstate.debug_cmd = cmdsave;
    return 1;
}

void logged_out(dbref player, dbref cause, int key, char *arg) {
    DESC *d;
    int idletime;

    if (player != cause)
        return;
    DESC_ITER_PLAYER(player, d) {
        idletime = (mudstate.now - d->last_time);

        switch (key) {
            case CMD_QUIT:
                if (idletime == 0) {
                    shutdownsock(d, R_QUIT);
                    return;
                }
                break;
            case CMD_LOGOUT:
                if (idletime == 0) {
                    shutdownsock(d, R_LOGOUT);
                    return;
                }
                break;
            case CMD_WHO:
                if (d->player || mudconf.allow_unloggedwho) {
                    if (idletime == 0) {
                        dump_users(d, arg, CMD_WHO);
                        return;
                    }
                } else {
                    queue_string(d,
                            "This MUX does not allow WHO at the login screen.\r\nPlease login or create a character first.\r\n");
                }
                break;
            case CMD_DOING:
                if (d->player || mudconf.allow_unloggedwho) {
                    if (idletime == 0) {
                        dump_users(d, arg, CMD_DOING);
                        return;
                    }
                } else {
                    queue_string(d,
                            "This MUX does not allow DOING at the login screen.\r\nPlease login or create a character first.\r\n");
                }
                break;
            case CMD_SESSION:
                if (d->player || mudconf.allow_unloggedwho) {
                    if (idletime == 0) {
                        dump_users(d, arg, CMD_SESSION);
                        return;
                    }
                } else {
                    queue_string(d,
                            "This MUX does not allow SESSION at the login screen.\r\nPlease login or create a character first.\r\n");
                }
                break;
            case CMD_PREFIX:
                if (idletime == 0) {
                    set_userstring(&d->output_prefix, arg);
                    return;
                }
                break;
            case CMD_SUFFIX:
                if (idletime == 0) {
                    set_userstring(&d->output_suffix, arg);
                    return;
                }
                break;
            case CMD_PUEBLOCLIENT:
                /* Set the descriptor's flag */
                d->flags |= DS_PUEBLOCLIENT;
                /* If we're already connected, set the player's flag */
                if (d->player) {
                    s_Html(d->player);
                }
                queue_string(d, mudconf.pueblo_msg);
                queue_string(d, "\r\n");
                break;
        }
    }
}

void process_commands(void) {
    int nprocessed;
    DESC *d, *dnext;
    CBLK *t;
    char *cmdsave;

    cmdsave = mudstate.debug_cmd;
    mudstate.debug_cmd = (char *) "process_commands";

    do {
        nprocessed = 0;
        DESC_SAFEITER_ALL(d, dnext) {
            if (d->quota > 0 && (t = d->input_head)) {
                if (d->player <= 0 || !Staff(d->player))
                    d->quota--;
                nprocessed++;
                d->input_head = (CBLK *) t->hdr.nxt;
                if (!d->input_head)
                    d->input_tail = NULL;
                d->input_size -= (strlen(t->cmd) + 1);
                if (d->program_data != NULL)
                    handle_prog(d, t->cmd);
                else
                    do_command(d, t->cmd, 1);
                free_lbuf(t);
            }
        }
    } while (nprocessed > 0);
    mudstate.debug_cmd = cmdsave;
}

/*
 * ---------------------------------------------------------------------------
 * * site_check: Check for site flags in a site list.
 */

int site_check(struct in_addr host, SITE *site_list) {
    SITE *this;

    for (this = site_list; this; this = this->next) {
        if ((host.s_addr & this->mask.s_addr) == this->address.s_addr)
            return this->flag;
    }
    return 0;
}

/*
 * --------------------------------------------------------------------------
 * * list_sites: Display information in a site list
 */

#define	S_SUSPECT	1
#define	S_ACCESS	2

static const char *stat_string(int strtype, int flag) {
    const char *str;

    switch (strtype) {
        case S_SUSPECT:
            if (flag)
                str = "Suspected";
            else
                str = "Trusted";
            break;
        case S_ACCESS:
            switch (flag) {
                case H_FORBIDDEN:
                    str = "Forbidden";
                    break;
                case H_REGISTRATION:
                    str = "Registration";
                    break;
                case 0:
                    str = "Unrestricted";
                    break;
                default:
                    str = "Strange";
            }
            break;
        default:
            str = "Strange";
    }
    return str;
}

static void list_sites(dbref player, SITE *site_list, const char *header_txt, int stat_type) {
    char *buff, *buff1, *str;
    SITE *this;

    buff = alloc_mbuf("list_sites.buff");
    buff1 = alloc_sbuf("list_sites.addr");
    sprintf(buff, "----- %s -----", header_txt);
    notify(player, buff);
    notify(player, "Address              Mask                 Status");
    for (this = site_list; this; this = this->next) {
        str = (char *) stat_string(stat_type, this->flag);
        StringCopy(buff1, inet_ntoa(this->mask));
        sprintf(buff, "%-20s %-20s %s", inet_ntoa(this->address), buff1,
                str);
        notify(player, buff);
    }
    free_mbuf(buff);
    free_sbuf(buff1);
}

/*
 * ---------------------------------------------------------------------------
 * * list_siteinfo: List information about specially-marked sites.
 */

void list_siteinfo(dbref player) {
    list_sites(player, mudstate.access_list, "Site Access", S_ACCESS);
    list_sites(player, mudstate.suspect_list, "Suspected Sites",
            S_SUSPECT);
}

/*
 * ---------------------------------------------------------------------------
 * * make_ulist: Make a list of connected user numbers for the LWHO function.
 */

void make_ulist(dbref player, char *buff, char **bufc) {
    DESC *d;
    char *cp;

    cp = *bufc;
    DESC_ITER_CONN(d) {
        if (!WizRoy(player) && Hidden(d->player))
            continue;
        if (cp != *bufc)
            safe_chr(' ', buff, bufc);
        safe_chr('#', buff, bufc);
        safe_str(tprintf("%d", d->player), buff, bufc);
    }
}

/*
 * ---------------------------------------------------------------------------
 * * find_connected_name: Resolve a playername from the list of connected
 * * players using prefix matching.  We only return a match if the prefix
 * * was unique.
 */

dbref find_connected_name(dbref player, char *name) {
    DESC *d;
    dbref found;

    found = NOTHING;
    DESC_ITER_CONN(d) {
        if (Good_obj(player) && !Wizard(player) && Hidden(d->player))
            continue;
        if (!string_prefix(Name(d->player), name))
            continue;
        if ((found != NOTHING) && (found != d->player))
            return NOTHING;
        found = d->player;
    }
    return found;
}