fbmuck-6.01/contrib/jresolver/
fbmuck-6.01/contrib/jresolver/org/
fbmuck-6.01/contrib/jresolver/org/fuzzball/
fbmuck-6.01/docs/devel/
fbmuck-6.01/game/
fbmuck-6.01/game/logs/
fbmuck-6.01/game/muf/
fbmuck-6.01/scripts/
fbmuck-6.01/src_docs/
/* $Header: /cvsroot/fbmuck/fbmuck/src/interface.c,v 1.90 2003/09/13 08:33:23 revar Exp $ */

/* Copyright 1992-2001 by Fuzzball Software */
/* Consider this code protected under the GNU public license, with explicit
 * permission to distribute when linked against openSSL. */

#include "copyright.h"
#include "config.h"
#include "match.h"
#include "mpi.h"

#include <sys/types.h>

#ifndef WIN32
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#endif

#include <fcntl.h>
#if defined (HAVE_ERRNO_H)
# include <errno.h>
#else
#if defined (HAVE_SYS_ERRNO_H)
# include <sys/errno.h>
#else
  extern int errno;
#endif
#endif
#include <ctype.h>

#ifndef WIN32
# define NEED_SOCKLEN_T
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <arpa/inet.h>
#else
  typedef int socklen_t;
#endif

#ifdef AIX
# include <sys/select.h>
#endif

#ifdef USE_IPV6
# ifdef HAVE_NETINET6_IN6_H
#  include <netinet6/in6.h>
# elif defined(HAVE_LINUX_IN6_H)
#  include <linux/in6.h>
# elif defined(HAVE_IN6_H)
#  include <in6.h>
# endif
#endif

#ifdef USE_SSL
# if defined (HAVE_OPENSSL_SSL_H)
#  include <openssl/ssl.h>
# elif defined (HAVE_SSL_SSL_H)
#  include <ssl/ssl.h>
# elif defined (HAVE_SSL_H)
#  include <ssl.h>
# endif
#endif

#include "db.h"
#include "interface.h"
#include "params.h"
#include "tune.h"
#include "props.h"
#include "mcp.h"
#include "mufevent.h"
#include "externs.h"
#include "interp.h"

#ifdef __APPLE__
    typedef unsigned int socklen_t;
#endif

int shutdown_flag = 0;
int restart_flag = 0;

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 const char *flushed_message = "<Output Flushed>\r\n";
static const char *shutdown_message = "\r\nGoing down - Bye\r\n";

int resolver_sock[2];

struct text_block {
	int nchars;
	struct text_block *nxt;
	char *start;
	char *buf;
};

struct text_queue {
	int lines;
	struct text_block *head;
	struct text_block **tail;
};

struct descriptor_data {
	int descriptor;
	int connected;
	int con_number;
	int booted;
#ifdef USE_SSL
	SSL *ssl_session;
#endif
	dbref player;
	char *output_prefix;
	char *output_suffix;
	int output_size;
	struct text_queue output;
	struct text_queue input;
	char *raw_input;
	char *raw_input_at;
	int inIAC;
	long last_time;
	long connected_at;
	const char *hostname;
	const char *username;
	int quota;
	struct descriptor_data *next;
	struct descriptor_data **prev;
	McpFrame mcpframe;
};

struct descriptor_data *descriptor_list = NULL;

#define MAX_LISTEN_SOCKS 16

static int numsocks = 0;
static int listener_port[MAX_LISTEN_SOCKS];
static int sock[MAX_LISTEN_SOCKS];
#ifdef USE_SSL
static int ssl_numsocks = 0;
static int ssl_listener_port[MAX_LISTEN_SOCKS];
static int ssl_sock[MAX_LISTEN_SOCKS];
SSL_CTX *ssl_ctx;
#endif

static int ndescriptors = 0;
extern void fork_and_dump(void);

extern int rwhocli_setup(const char *server, const char *serverpw, const char *myname,

						 const char *comment);
extern int rwhocli_shutdown(void);
extern int rwhocli_pingalive(void);
extern int rwhocli_userlogin(const char *uid, const char *name, time_t tim);
extern int rwhocli_userlogout(const char *uid);

void process_commands(void);
void shovechars();
void shutdownsock(struct descriptor_data *d);
struct descriptor_data *initializesock(int s, const char *hostname);
void make_nonblocking(int s);
void freeqs(struct descriptor_data *d);
void welcome_user(struct descriptor_data *d);
void check_connect(struct descriptor_data *d, const char *msg);
void close_sockets(const char *msg);
int boot_off(dbref player);
void boot_player_off(dbref player);

#ifdef USE_IPV6
const char *addrout(int, struct in6_addr *, unsigned short);
#else
const char *addrout(int, long, unsigned short);
#endif /* USE_IPV6 */
void dump_users(struct descriptor_data *d, char *user);
struct descriptor_data *new_connection(int port, int sock);
void parse_connect(const char *msg, char *command, char *user, char *pass);
void set_userstring(char **userstring, const char *command);
int do_command(struct descriptor_data *d, char *command);
int is_interface_command(const char* cmd);
char *strsave(const char *s);
int make_socket(int);
int queue_string(struct descriptor_data *, const char *);
int queue_write(struct descriptor_data *, const char *, int);
int process_output(struct descriptor_data *d);
int process_input(struct descriptor_data *d);
void announce_connect(int, dbref);
void announce_disconnect(struct descriptor_data *);
char *time_format_1(long);
char *time_format_2(long);
void    init_descriptor_lookup();
void    init_descr_count_lookup();
void    remember_descriptor(struct descriptor_data *);
void    remember_player_descr(dbref player, int);
void    update_desc_count_table();
int*    get_player_descrs(dbref player, int* count);
void    forget_player_descr(dbref player, int);
void    forget_descriptor(struct descriptor_data *);
struct descriptor_data* descrdata_by_descr(int i);
struct descriptor_data* lookup_descriptor(int);
int online(dbref player);
int online_init(void);
dbref online_next(int *ptr);
long max_open_files(void);
#ifdef MUD_ID
void do_setuid(char *user);
#endif /* MUD_ID */
#ifdef MUD_GID
void do_setgid(char *group);
#endif /* MUD_GID */

#ifdef SPAWN_HOST_RESOLVER
void kill_resolver(void);
#endif

#ifdef USE_SSL
ssize_t socket_read(struct descriptor_data *d, void *buf, size_t count);
ssize_t socket_write(struct descriptor_data *d, const void *buf, size_t count);
#elif WIN32
#define socket_write(d, buf, count) send(d->descriptor, buf, count,0)
#define socket_read(d, buf, count) recv(d->descriptor, buf, count,0)
#else
#define socket_write(d, buf, count) write(d->descriptor, buf, count)
#define socket_read(d, buf, count) read(d->descriptor, buf, count)
#endif
 

void spawn_resolver(void);
void resolve_hostnames(void);

#define MALLOC(result, type, number) do {   \
                                       if (!((result) = (type *) malloc ((number) * sizeof (type)))) \
                                       panic("Out of memory");                             \
                                     } while (0)

#define FREE(x) (free((void *) x))

#ifndef BOOLEXP_DEBUGGING

extern FILE *input_file;
extern FILE *delta_infile;
extern FILE *delta_outfile;

short db_conversion_flag = 0;
short db_decompression_flag = 0;
short wizonly_mode = 0;
pid_t global_resolver_pid=0;
#ifndef DISKBASE
pid_t global_dumper_pid=0;
#endif
short global_dumpdone=0;


time_t sel_prof_start_time;
long sel_prof_idle_sec;
long sel_prof_idle_usec;
unsigned long sel_prof_idle_use;


void
show_program_usage(char *prog)
{
	fprintf(stderr, "Usage: %s [<options>] [infile [outfile [portnum [portnum ...]]]]\n", prog);
	fprintf(stderr, "    Arguments:\n");
	fprintf(stderr, "        infile           db file loaded at startup.  optional with -dbin.\n");
	fprintf(stderr, "        outfile          output db file to save to.  optional with -dbout.\n");
	fprintf(stderr, "        portnum          port num to listen for conns on. (16 ports max)\n");
	fprintf(stderr, "    Options:\n");
	fprintf(stderr, "        -dbin INFILE     use INFILE as the database to load at startup.\n");
	fprintf(stderr, "        -dbout OUTFILE   use OUTFILE as the output database to save to.\n");
	fprintf(stderr, "        -port NUMBER     sets the port number to listen for connections on.\n");
#ifdef USE_SSL
	fprintf(stderr, "        -sport NUMBER    sets the port number for secure connections\n");
#else
	fprintf(stderr, "        -sport NUMBER    Ignored.  SSL support isn't compiled in.\n");
#endif
	fprintf(stderr, "        -gamedir PATH    changes directory to PATH before starting up.\n");
	fprintf(stderr, "        -convert         load the db, then save and quit.\n");
	fprintf(stderr, "        -decompress      when saving db, save in uncompressed format.\n");
	fprintf(stderr, "        -nosanity        don't do db sanity checks at startup time.\n");
	fprintf(stderr, "        -insanity        load db, then enter the interactive sanity editor.\n");
	fprintf(stderr, "        -sanfix          attempt to auto-fix a corrupt db after loading.\n");
	fprintf(stderr, "        -wizonly         only allow wizards to login.\n");
	fprintf(stderr, "        -godpasswd PASS  reset God(#1)'s password to PASS.  Implies -convert\n");
	fprintf(stderr, "        -version         display this server's version.\n");
	fprintf(stderr, "        -help            display this message.\n");
	exit(1);
}


extern int sanity_violated;

int
main(int argc, char **argv)
{
	FILE *ffd;
	char *infile_name;
	char *outfile_name;
	char *num_one_new_passwd = NULL;
	int i, nomore_options;
	int sanity_skip;
	int sanity_interactive;
	int sanity_autofix;
	int val;
#ifdef WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
#endif

	listener_port[0] = TINYPORT;

    init_descriptor_lookup();
    init_descr_count_lookup();

	nomore_options = 0;
	sanity_skip = 0;
	sanity_interactive = 0;
	sanity_autofix = 0;
	infile_name = NULL;
	outfile_name = NULL;

	for (i = 1; i < argc; i++) {
		if (!nomore_options && argv[i][0] == '-') {
			if (!strcmp(argv[i], "-convert")) {
				db_conversion_flag = 1;
			} else if (!strcmp(argv[i], "-decompress")) {
				db_decompression_flag = 1;
			} else if (!strcmp(argv[i], "-nosanity")) {
				sanity_skip = 1;
			} else if (!strcmp(argv[i], "-insanity")) {
				sanity_interactive = 1;
			} else if (!strcmp(argv[i], "-wizonly")) {
				wizonly_mode = 1;
			} else if (!strcmp(argv[i], "-sanfix")) {
				sanity_autofix = 1;
			} else if (!strcmp(argv[i], "-version")) {
				printf("%s\n", VERSION);
				exit(0);
			} else if (!strcmp(argv[i], "-dbin")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
				infile_name = argv[++i];

			} else if (!strcmp(argv[i], "-dbout")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
				outfile_name = argv[++i];

			} else if (!strcmp(argv[i], "-godpasswd")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
				num_one_new_passwd = argv[++i];
				if (!ok_password(num_one_new_passwd)) {
					fprintf(stderr, "Bad -godpasswd password.\n");
					exit(1);
				}
				db_conversion_flag = 1;

			} else if (!strcmp(argv[i], "-port")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
#ifdef USE_SSL
				if ( (ssl_numsocks + numsocks) < MAX_LISTEN_SOCKS)
#else
				if (numsocks < MAX_LISTEN_SOCKS)
#endif
					listener_port[numsocks++] = atoi(argv[++i]);
#ifdef USE_SSL
			} else if (!strcmp(argv[i], "-sport")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
				if ( (ssl_numsocks + numsocks) < MAX_LISTEN_SOCKS)
					ssl_listener_port[ssl_numsocks++] = atoi(argv[++i]);
#else
			} else if (!strcmp(argv[i], "-sport")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
				i++;
				fprintf(stderr, "SSL unsupported.  Ignoring -sport.\n");
#endif
			} else if (!strcmp(argv[i], "-gamedir")) {
				if (i + 1 >= argc) {
					show_program_usage(*argv);
				}
				if (chdir(argv[++i])) {
					perror("cd to gamedir");
					exit(4);
				}

			} else if (!strcmp(argv[i], "--")) {
				nomore_options = 1;
			} else {
				show_program_usage(*argv);
			}
		} else {
			if (!infile_name) {
				infile_name = argv[i];
			} else if (!outfile_name) {
				outfile_name = argv[i];
			} else {
				val = atoi(argv[i]);
				if (val < 1 || val > 65535) {
					show_program_usage(*argv);
				}
#ifdef USE_SSL
				if ( (ssl_numsocks + numsocks) < MAX_LISTEN_SOCKS)
#else
				if (numsocks < MAX_LISTEN_SOCKS)
#endif
					listener_port[numsocks++] = val;
			}
		}
	}
	if (numsocks < 1) {
		numsocks = 1;
	}
	if (!infile_name || !outfile_name) {
		show_program_usage(*argv);
	}
#ifdef DISKBASE
	if (!strcmp(infile_name, outfile_name)) {
		fprintf(stderr, "Output file must be different from the input file.");
		exit(3);
	}
#endif

	if (!sanity_interactive) {

                log_status("INIT: TinyMUCK %s starting.\n", "version");

#ifdef DETACH
		/* Go into the background unless requested not to */
		if (!sanity_interactive && !db_conversion_flag) {
			fclose(stdin);
			fclose(stdout);
			fclose(stderr);
			if (fork() != 0)
				_exit(0);
		}
#endif

		/* save the PID for future use */
		if ((ffd = fopen(PID_FILE, "w")) != NULL) {
			fprintf(ffd, "%d\n", getpid());
			fclose(ffd);
		}

#ifdef DETACH
		if (!sanity_interactive && !db_conversion_flag) {
			/* Detach from the TTY, log whatever output we have... */
			freopen(LOG_ERR_FILE, "a", stderr);
			setbuf(stderr, NULL);
			freopen(LOG_FILE, "a", stdout);
			setbuf(stdout, NULL);

			/* Disassociate from Process Group */
# ifdef _POSIX_SOURCE
			setsid();
# else
#  ifdef SYSV
			setpgrp();			/* The SysV way */
#  else
			setpgid(0, getpid());	/* The POSIX way. */
#  endif						/* SYSV */

#  ifdef  TIOCNOTTY				/* we can force this, POSIX / BSD */
			{
				int fd;
				if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
					ioctl(fd, TIOCNOTTY, (char *) 0);	/* lose controll TTY */
					close(fd);
				}
			}
#  endif						/* TIOCNOTTY */
# endif							/* !_POSIX_SOURCE */
		}
#endif							/* DETACH */

#ifdef SPAWN_HOST_RESOLVER
		if (!db_conversion_flag) {
			spawn_resolver();
		}
#endif

	}

/*
 * You have to change gid first, since setgid() relies on root permissions
 * if you don't call initgroups() -- and since initgroups() isn't standard,
 * I'd rather assume the user knows what he's doing.
*/

#ifdef MUD_GID
	if (!sanity_interactive) {
		do_setgid(MUD_GID);
	}
#endif							/* MUD_GID */
#ifdef MUD_ID
	if (!sanity_interactive) {
		do_setuid(MUD_ID);
	}
#endif							/* MUD_ID */

	/* Initialize MCP and some packages. */
	mcp_initialize();
	gui_initialize();

    sel_prof_start_time = time(NULL); /* Set useful starting time */
    sel_prof_idle_sec = 0;
    sel_prof_idle_usec = 0;
    sel_prof_idle_use = 0;

	if (init_game(infile_name, outfile_name) < 0) {
		fprintf(stderr, "Couldn't load %s!\n", infile_name);
		exit(2);
	}

	if (num_one_new_passwd != NULL) {
		set_password(GOD, num_one_new_passwd);
	}

	if (!sanity_interactive && !db_conversion_flag) {
		set_signals();

		if (!sanity_skip) {
			sanity(AMBIGUOUS);
			if (sanity_violated) {
				wizonly_mode = 1;
				if (sanity_autofix) {
					sanfix(AMBIGUOUS);
				}
			}
		}


#ifdef WIN32
	wVersionRequested = MAKEWORD( 2, 0 );
 
	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		perror("Unable to start socket layer");
		return 1;
	}
 
	if ( LOBYTE( wsaData.wVersion ) != 2 ||
			HIBYTE( wsaData.wVersion ) != 0 ) {
		perror("Winsock 2.0 or later is required to run this application.");
		WSACleanup( );
		return 1; 
	}

	set_console(); /* Setup the console to handle CTRL+C */
#endif

		/* go do it */
		shovechars();

		if (restart_flag) {
			close_sockets("\r\nServer restarting.  Try logging back on in a few minutes.\r\n");
		} else {
			close_sockets("\r\nServer shutting down normally.\r\n");
		}

		do_dequeue(-1, (dbref) 1, "all");

#ifdef WIN32
		WSACleanup();
#endif

		if (tp_rwho) {
			rwhocli_shutdown();
		}
	}

	if (sanity_interactive) {
		san_main();
	} else {
		dump_database();
		tune_save_parmsfile();

#ifdef SPAWN_HOST_RESOLVER
		kill_resolver();
#endif

#ifdef MALLOC_PROFILING
		db_free();
		free_old_macros();
		purge_all_free_frames();
		purge_timenode_free_pool();
		purge_for_pool();
		purge_for_pool(); /* have to do this a second time to purge all */
		purge_try_pool();
		purge_try_pool(); /* have to do this a second time to purge all */
		purge_mfns();
		cleanup_game();
		tune_freeparms();
		free_compress_dictionary();
#endif

#ifdef DISKBASE
		fclose(input_file);
#endif
#ifdef DELTADUMPS
		fclose(delta_infile);
		fclose(delta_outfile);
		(void) unlink(DELTAFILE_NAME);
#endif

#ifdef MALLOC_PROFILING
		CrT_summarize_to_file("malloc_log", "Shutdown");
#endif

		if (restart_flag) {
#ifndef WIN32
			char **argslist;
			char numbuf[32];
			int argcnt = numsocks + 2;
			int argnum = 1;

#ifdef USE_SSL
			argcnt += ssl_numsocks;
#endif

			argslist = (char**)calloc(argcnt, sizeof(char*));

			for (i = 0; i < numsocks; i++) {
				snprintf(numbuf, sizeof(numbuf), "%d", listener_port[i]);
				argslist[argnum] = (char*)malloc(strlen(numbuf)+1);
				strcpy(argslist[argnum++], numbuf);
			}

#ifdef USE_SSL
			for (i = 0; i < ssl_numsocks; i++) {
				snprintf(numbuf, sizeof(numbuf), "-sport %d", ssl_listener_port[i]);
				argslist[argnum] = (char*)malloc(strlen(numbuf)+1);
				strcpy(argslist[argnum++], numbuf);
			}
#endif

			if (!fork()) {
				argslist[0] = "./restart";
				execv(argslist[0], argslist);

				argslist[0] = "restart";
				execv(argslist[0], argslist);

				fprintf(stderr, "Could not find restart script!\n");
			}
#else	/* WIN32 */
			char* argbuf[1];
			argbuf[0] = NULL;
			execv("restart", argbuf);
#endif	/* WIN32 */
		}
	}

	exit(0);
	return 0;
}

#endif							/* BOOLEXP_DEBUGGING */


int
queue_ansi(struct descriptor_data *d, const char *msg)
{
	char buf[BUFFER_LEN + 8];

	if (d->connected) {
		if (FLAGS(d->player) & CHOWN_OK) {
			strip_bad_ansi(buf, msg);
		} else {
			strip_ansi(buf, msg);
		}
	} else {
		strip_ansi(buf, msg);
	}
	mcp_frame_output_inband(&d->mcpframe, buf);
	return strlen(buf);
	/* return queue_string(d, buf); */
}


int notify_nolisten_level = 0;

int
notify_nolisten(dbref player, const char *msg, int isprivate)
{
	int retval = 0;
	char buf[BUFFER_LEN + 2];
	char buf2[BUFFER_LEN + 2];
	int firstpass = 1;
	char *ptr1;
	const char *ptr2;
	dbref ref;
    int di;
    int* darr;
    int dcount;

#ifdef COMPRESS
	extern const char *uncompress(const char *);

	msg = uncompress(msg);
#endif							/* COMPRESS */

#if defined(ANONYMITY)
	msg = unmangle(player, msg);
#endif

	ptr2 = msg;
	while (ptr2 && *ptr2) {
		ptr1 = buf;
		while (ptr2 && *ptr2 && *ptr2 != '\r')
			*(ptr1++) = *(ptr2++);
		*(ptr1++) = '\r';
		*(ptr1++) = '\n';
		*(ptr1++) = '\0';
		if (*ptr2 == '\r')
			ptr2++;

		darr = get_player_descrs(player, &dcount);
        for (di = 0; di < dcount; di++) {
            queue_ansi(descrdata_by_descr(darr[di]), buf);
            if (firstpass) retval++;
        }

		if (tp_zombies) {
			if ((Typeof(player) == TYPE_THING) && (FLAGS(player) & ZOMBIE) &&
				!(FLAGS(OWNER(player)) & ZOMBIE) &&
				(!(FLAGS(player) & DARK) || Wizard(OWNER(player)))) {
				ref = getloc(player);
				if (Wizard(OWNER(player)) || ref == NOTHING ||
					Typeof(ref) != TYPE_ROOM || !(FLAGS(ref) & ZOMBIE)) {
					if (isprivate || getloc(player) != getloc(OWNER(player))) {
						char pbuf[BUFFER_LEN];
						const char *prefix;
						char ch = *match_args;

						*match_args = '\0';

						if (notify_nolisten_level <= 0)
						{
							notify_nolisten_level++;

							prefix = do_parse_prop(-1, player, player, MESGPROP_PECHO,
												"(@Pecho)", pbuf, MPI_ISPRIVATE);

							notify_nolisten_level--;
						}
						else
							prefix = 0;

						*match_args = ch;

						if (!prefix || !*prefix) {
							prefix = NAME(player);
							snprintf(buf2, sizeof(buf2), "%s> %.*s", prefix,
									(int)(BUFFER_LEN - (strlen(prefix) + 3)), buf);
						} else {
							snprintf(buf2, sizeof(buf2), "%s %.*s", prefix,
									(int)(BUFFER_LEN - (strlen(prefix) + 2)), buf);
						}

						darr = get_player_descrs(OWNER(player), &dcount);
                        for (di = 0; di < dcount; di++) {
                            queue_ansi(descrdata_by_descr(darr[di]), buf2);
                            if (firstpass) retval++;
                        }
					}
				}
			}
		}
		firstpass = 0;
	}

	return retval;
}

int
notify_filtered(dbref from, dbref player, const char *msg, int isprivate)
{
	if ((msg == 0) || ignore_is_ignoring(player, from))
		return 0;
	return notify_nolisten(player, msg, isprivate);
}

int
notify_from_echo(dbref from, dbref player, const char *msg, int isprivate)
{
	const char *ptr;

#ifdef COMPRESS
	extern const char *uncompress(const char *);

	ptr = uncompress(msg);
#else
	ptr = msg;
#endif							/* COMPRESS */

#ifdef ANONYMITY
	ptr = unmangle(player, ptr);
#endif

	if (tp_listeners) {
		if (tp_listeners_obj || Typeof(player) == TYPE_ROOM) {
			listenqueue(-1, from, getloc(from), player, player, NOTHING,
						"_listen", ptr, tp_listen_mlev, 1, 0);
			listenqueue(-1, from, getloc(from), player, player, NOTHING,
						"~listen", ptr, tp_listen_mlev, 1, 1);
			listenqueue(-1, from, getloc(from), player, player, NOTHING,
						"~olisten", ptr, tp_listen_mlev, 0, 1);
		}
	}

	if (Typeof(player) == TYPE_THING && (FLAGS(player) & VEHICLE) &&
		(!(FLAGS(player) & DARK) || Wizard(OWNER(player)))
			) {
		dbref ref;

		ref = getloc(player);
		if (Wizard(OWNER(player)) || ref == NOTHING ||
			Typeof(ref) != TYPE_ROOM || !(FLAGS(ref) & VEHICLE)
				) {
			if (!isprivate && getloc(from) == getloc(player)) {
				char buf[BUFFER_LEN];
				char pbuf[BUFFER_LEN];
				const char *prefix;
				char ch = *match_args;

				*match_args = '\0';
				prefix = do_parse_prop(-1, from, player, MESGPROP_OECHO,
										"(@Oecho)", pbuf, MPI_ISPRIVATE);
				*match_args = ch;

				if (!prefix || !*prefix)
					prefix = "Outside>";
				snprintf(buf, sizeof(buf), "%s %.*s", prefix, (int)(BUFFER_LEN - (strlen(prefix) + 2)), msg);
				ref = DBFETCH(player)->contents;
				while (ref != NOTHING) {
					notify_filtered(from, ref, buf, isprivate);
					ref = DBFETCH(ref)->next;
				}
			}
		}
	}

	return notify_filtered(from, player, msg, isprivate);
}

int
notify_from(dbref from, dbref player, const char *msg)
{
	return notify_from_echo(from, player, msg, 1);
}

int
notify(dbref player, const char *msg)
{
	return notify_from_echo(player, player, msg, 1);
}


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

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

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;
}

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

	nslices = msec_diff(current, last) / tp_command_time_msec;

	if (nslices > 0) {
		for (d = descriptor_list; d; d = d->next) {
			if (d->connected) {
				cmds_per_time = ((FLAGS(d->player) & INTERACTIVE)
								 ? (tp_commands_per_time * 8) : tp_commands_per_time);
			} else {
				cmds_per_time = tp_commands_per_time;
			}
			d->quota += cmds_per_time * nslices;
			if (d->quota > tp_command_burst_size)
				d->quota = tp_command_burst_size;
		}
	}
	return msec_add(last, nslices * tp_command_time_msec);
}

/*
 * long max_open_files()
 *
 * This returns the max number of files you may have open
 * as a long, and if it can use setrlimit() to increase it,
 * it will do so.
 *
 * Becuse there is no way to just "know" if get/setrlimit is
 * around, since its defs are in <sys/resource.h>, you need to
 * define USE_RLIMIT in config.h to attempt it.
 *
 * Otherwise it trys to use sysconf() (POSIX.1) or getdtablesize()
 * to get what is avalible to you.
 */
#ifdef HAVE_RESOURCE_H
# include <sys/resource.h>
#endif

#if defined(RLIMIT_NOFILE) || defined(RLIMIT_OFILE)
# define USE_RLIMIT
#endif

long
max_open_files(void)
{
#if defined(_SC_OPEN_MAX) && !defined(USE_RLIMIT)	/* Use POSIX.1 method, sysconf() */
/*
 * POSIX.1 code.
 */
	return sysconf(_SC_OPEN_MAX);
#else							/* !POSIX */
# if defined(USE_RLIMIT) && (defined(RLIMIT_NOFILE) || defined(RLIMIT_OFILE))
#  ifndef RLIMIT_NOFILE
#   define RLIMIT_NOFILE RLIMIT_OFILE	/* We Be BSD! */
#  endif						/* !RLIMIT_NOFILE */
/*
 * get/setrlimit() code.
 */
	struct rlimit file_limit;

	getrlimit(RLIMIT_NOFILE, &file_limit);	/* Whats the limit? */

	if (file_limit.rlim_cur < file_limit.rlim_max) {	/* if not at max... */
		file_limit.rlim_cur = file_limit.rlim_max;	/* ...set to max. */
		setrlimit(RLIMIT_NOFILE, &file_limit);

		getrlimit(RLIMIT_NOFILE, &file_limit);	/* See what we got. */
	}

	return (long) file_limit.rlim_cur;

# elif WIN32					/* !RLIMIT && WIN32 */
	return FD_SETSIZE;
# else							/* !RLIMIT && !WIN32 */
/*
 * Don't know what else to do, try getdtablesize().
 * email other bright ideas to me. :) (whitefire)
 */
	return (long) getdtablesize();
# endif							/* !RLIMIT */
#endif							/* !POSIX */
}

int
queue_immediate(struct descriptor_data *d, const char *msg)
{
	char buf[BUFFER_LEN + 8];
	int quote_len = 0;

	if (d->connected) {
		if (FLAGS(d->player) & CHOWN_OK) {
			strip_bad_ansi(buf, msg);
		} else {
			strip_ansi(buf, msg);
		}
	} else {
		strip_ansi(buf, msg);
	}

	if (d->mcpframe.enabled && !(strncmp(buf, MCP_MESG_PREFIX, 3) && strncmp(buf, MCP_QUOTE_PREFIX, 3)))
	{
		quote_len = strlen(MCP_QUOTE_PREFIX);
		socket_write(d, MCP_QUOTE_PREFIX, quote_len);
	}

	return socket_write(d, buf, strlen(buf)) + quote_len;
}

void
goodbye_user(struct descriptor_data *d)
{
	queue_immediate(d, "\r\n");
	queue_immediate(d, tp_leave_mesg);
	queue_immediate(d, "\r\n\r\n");
}

void
idleboot_user(struct descriptor_data *d)
{
	queue_immediate(d, "\r\n");
	queue_immediate(d, tp_idle_mesg);
	queue_immediate(d, "\r\n\r\n");
	d->booted = 1;
}

#ifdef USE_SSL
int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata)
{
	const char *pw = (const char*)userdata;
	int pwlen = strlen(pw);
	strncpy(buf, pw, size);
	return ((pwlen > size)? size : pwlen);
}
#endif

static int con_players_max = 0;	/* one of Cynbe's good ideas. */
static int con_players_curr = 0;	/* for playermax checks. */
extern void purge_free_frames(void);

void
shovechars()
{
	fd_set input_set, output_set;
	time_t now;
	long tmptq;
	struct timeval last_slice, current_time;
	struct timeval next_slice;
	struct timeval timeout, slice_timeout;
	int maxd, cnt;
	struct descriptor_data *d, *dnext;
	struct descriptor_data *newd;
	struct timeval sel_in, sel_out;
	int avail_descriptors;
	int i;

#ifdef USE_SSL
	int ssl_status_ok = 1;
#endif

	for (i = 0; i < numsocks; i++) {
		sock[i] = make_socket(listener_port[i]);
		maxd = sock[i] + 1;
	}

#ifdef USE_SSL
	SSL_load_error_strings ();
 	OpenSSL_add_ssl_algorithms (); 
	ssl_ctx = SSL_CTX_new (SSLv23_server_method ());
 
	if (!SSL_CTX_use_certificate_file (ssl_ctx, SSL_CERT_FILE, SSL_FILETYPE_PEM)) {
		log_status("Could not load certificate file %s\n", SSL_CERT_FILE);
		ssl_status_ok = 0;
	}
	if (ssl_status_ok) {
		SSL_CTX_set_default_passwd_cb(ssl_ctx, pem_passwd_cb);
		SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void*)tp_ssl_keyfile_passwd);

		if (!SSL_CTX_use_PrivateKey_file (ssl_ctx, SSL_KEY_FILE, SSL_FILETYPE_PEM)) {
			log_status("Could not load private key file %s\n", SSL_KEY_FILE);
			ssl_status_ok = 0;
		}
	}
	if (ssl_status_ok) {
		if (!SSL_CTX_check_private_key (ssl_ctx)) {
			log_status("Private key does not check out and appears to be invalid.\n");
			ssl_status_ok = 0;
		}
	}
 
	if (ssl_status_ok) {
		for (i = 0; i < ssl_numsocks; i++) {
			ssl_sock[i] = make_socket(ssl_listener_port[i]);
			maxd = ssl_sock[i] + 1;
		}
	} else {
		ssl_numsocks = 0;
	}
#endif
	gettimeofday(&last_slice, (struct timezone *) 0);

	avail_descriptors = max_open_files() - 5;

	while (shutdown_flag == 0) {
		gettimeofday(&current_time, (struct timezone *) 0);
		last_slice = update_quotas(last_slice, current_time);

		next_muckevent();
		process_commands();
		muf_event_process();
#ifdef WIN32
		check_console(); /* Handle possible CTRL+C */
#endif

		for (d = descriptor_list; d; d = dnext) {
			dnext = d->next;
			if (d->booted) {
				process_output(d);
				if (d->booted == 2) {
					goodbye_user(d);
				}
				d->booted = 0;
				process_output(d);
				shutdownsock(d);
			}
		}
		if (global_dumpdone != 0) {
			if (tp_dumpdone_warning) {
				wall_and_flush(tp_dumpdone_mesg);
			}
			global_dumpdone = 0;
		}
		purge_free_frames();
		untouchprops_incremental(1);

		if (shutdown_flag)
			break;
		timeout.tv_sec = 10;
		timeout.tv_usec = 0;
		next_slice = msec_add(last_slice, tp_command_time_msec);
		slice_timeout = timeval_sub(next_slice, current_time);

		FD_ZERO(&input_set);
		FD_ZERO(&output_set);
		if (ndescriptors < avail_descriptors) {
			for (i = 0; i < numsocks; i++) {
				FD_SET(sock[i], &input_set);
			}
#ifdef USE_SSL
			for (i = 0; i < ssl_numsocks; i++) {
				FD_SET(ssl_sock[i], &input_set);
			}
#endif
		}
		for (d = descriptor_list; d; d = d->next) {
			if (d->input.lines > 100)
				timeout = slice_timeout;
			else
				FD_SET(d->descriptor, &input_set);
			if (d->output.head)
				FD_SET(d->descriptor, &output_set);
#ifdef USE_SSL
			if (d->ssl_session) {
			/* SSL may want to write even if the output queue is empty */
				if ( ! SSL_is_init_finished(d->ssl_session) ) {
					/* log_status("SSL : Init not finished.\n", "version"); */
					FD_CLR(d->descriptor, &output_set);
					FD_SET(d->descriptor, &input_set);
				} 
				if ( SSL_want_write(d->ssl_session) ) {
					/* log_status("SSL : Need write.\n", "version"); */
					FD_SET(d->descriptor, &output_set);
				}
			}
#endif
		}
#ifdef SPAWN_HOST_RESOLVER
		FD_SET(resolver_sock[1], &input_set);
#endif

		tmptq = next_muckevent_time();
		if ((tmptq >= 0L) && (timeout.tv_sec > tmptq)) {
			timeout.tv_sec = tmptq + (tp_pause_min / 1000);
			timeout.tv_usec = (tp_pause_min % 1000) * 1000L;
		}
		gettimeofday(&sel_in,NULL);
#ifndef WIN32
		if (select(maxd, &input_set, &output_set, (fd_set *) 0, &timeout) < 0) {
			if (errno != EINTR) {
				perror("select");
				return;
			}
#else
		if (select(maxd, &input_set, &output_set, (fd_set *) 0, &timeout) == SOCKET_ERROR) {
			if (WSAGetLastError() != WSAEINTR) {
				perror("select");
				return;
			}
#endif
		} else {
			gettimeofday(&sel_out,NULL);
			if (sel_out.tv_usec < sel_in.tv_usec) {
				sel_out.tv_usec += 1000000;
				sel_out.tv_sec -= 1;
			}
			sel_out.tv_usec -= sel_in.tv_usec;
			sel_out.tv_sec -= sel_in.tv_sec;
			sel_prof_idle_sec += sel_out.tv_sec;
			sel_prof_idle_usec += sel_out.tv_usec;
			if (sel_prof_idle_usec >= 1000000) {
				sel_prof_idle_usec -= 1000000;
				sel_prof_idle_sec += 1;
			}
			sel_prof_idle_use++;
			(void) time(&now);
			for (i = 0; i < numsocks; i++) {
				if (FD_ISSET(sock[i], &input_set)) {
					if (!(newd = new_connection(listener_port[i], sock[i]))) {
#ifndef WIN32
						if (errno && errno != EINTR && errno != EMFILE && errno != ENFILE
							/*
							*  && errno != ETIMEDOUT
							*  && errno != ECONNRESET
							*  && errno != ENOTCONN
							*  && errno != EPIPE
							*  && errno != ECONNREFUSED
							*#ifdef EPROTO
							*  && errno != EPROTO
							*#endif
							*/
								) {
							perror("new_connection");
							/* return; */
						}
#else	/* WIN32 */
						if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != EMFILE) {
							perror("new_connection");
							/* return; */
						}
#endif	/* WIN32 */
					} else {
						if (newd->descriptor >= maxd)
							maxd = newd->descriptor + 1;
					}
				}
			}
#ifdef USE_SSL
			for (i = 0; i < ssl_numsocks; i++) {
				if (FD_ISSET(ssl_sock[i], &input_set)) {
					if (!(newd = new_connection(ssl_listener_port[i], ssl_sock[i]))) {
#ifndef WIN32
						if (errno 
							&& errno != EINTR 
							&& errno != EMFILE
							&& errno != ENFILE
							) {
							perror("new_connection");
							/* return; */
						}
#else
						if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != EMFILE) {
							perror("new_connection");
							/* return; */
						}
#endif
					} else {
						if (newd->descriptor >= maxd)
							maxd = newd->descriptor + 1;
						newd->ssl_session  = SSL_new (ssl_ctx);
						SSL_set_fd(newd->ssl_session, newd->descriptor);
						cnt = SSL_accept(newd->ssl_session);
						/* log_status("SSL accept1: %i\n", cnt ); */
					}
				}
			}
#endif
#ifdef SPAWN_HOST_RESOLVER
			if (FD_ISSET(resolver_sock[1], &input_set)) {
				resolve_hostnames();
			}
#endif
			for (cnt = 0, d = descriptor_list; d; d = dnext) {
				dnext = d->next;
				if (FD_ISSET(d->descriptor, &input_set)) {
					if (!process_input(d)) {
						d->booted = 1;
					}
				}
				if (FD_ISSET(d->descriptor, &output_set)) {
					if (!process_output(d)) {
						d->booted = 1;
					}
				}
				if (d->connected) {
					cnt++;
					if (tp_idleboot && ((now - d->last_time) > tp_maxidle) &&
						!Wizard(d->player)) {
						idleboot_user(d);
					}
				} else {
					if ((now - d->connected_at) > 300) {
						d->booted = 1;
					}
				}
			}
			if (cnt > con_players_max) {
				add_property((dbref) 0, "_sys/max_connects", NULL, cnt);
				con_players_max = cnt;
			}
			con_players_curr = cnt;
		}
	}
	(void) time(&now);
	add_property((dbref) 0, "_sys/lastdumptime", NULL, (int) now);
	add_property((dbref) 0, "_sys/shutdowntime", NULL, (int) now);
}


void
wall_and_flush(const char *msg)
{
	struct descriptor_data *d, *dnext;
	char buf[BUFFER_LEN + 2];

#ifdef COMPRESS
	extern const char *uncompress(const char *);

	msg = uncompress(msg);
#endif							/* COMPRESS */

	if (!msg || !*msg)
		return;
	strcpy(buf, msg);
	strcatn(buf, sizeof(buf), "\r\n");

	for (d = descriptor_list; d; d = dnext) {
		dnext = d->next;
		queue_ansi(d, buf);
		/* queue_write(d, "\r\n", 2); */
		if (!process_output(d)) {
			d->booted = 1;
		}
	}
}


void
flush_user_output(dbref player)
{
    int di;
    int* darr;
    int dcount;
	struct descriptor_data *d;

	darr = get_player_descrs(OWNER(player), &dcount);
    for (di = 0; di < dcount; di++) {
        d = descrdata_by_descr(darr[di]);
        if (d && !process_output(d)) {
            d->booted = 1;
        }
    }
}


void
wall_wizards(const char *msg)
{
	struct descriptor_data *d, *dnext;
	char buf[BUFFER_LEN + 2];

#ifdef COMPRESS
	extern const char *uncompress(const char *);

	msg = uncompress(msg);
#endif							/* COMPRESS */

	strcpy(buf, msg);
	strcatn(buf, sizeof(buf), "\r\n");

	for (d = descriptor_list; d; d = dnext) {
		dnext = d->next;
		if (d->connected && Wizard(d->player)) {
			queue_ansi(d, buf);
			if (!process_output(d)) {
				d->booted = 1;
			}
		}
	}
}


struct descriptor_data *
new_connection(int port, int sock)
{
	int newsock;

#ifdef USE_IPV6
	struct sockaddr_in6 addr;
#else
	struct sockaddr_in addr;
#endif
	socklen_t addr_len;
	char hostname[128];

	addr_len = (socklen_t)sizeof(addr);
	newsock = accept(sock, (struct sockaddr *) &addr, &addr_len);
	if (newsock < 0) {
		return 0;
	} else {
#ifdef F_SETFD
		fcntl(newsock, F_SETFD, 1);
#endif
#ifdef USE_IPV6
		strcpy(hostname, addrout(port, &(addr.sin6_addr), addr.sin6_port));
		log_status("ACCEPT: %s(%d) on descriptor %d\n", hostname,
				   ntohs(addr.sin6_port), newsock);
#else
		strcpy(hostname, addrout(port, addr.sin_addr.s_addr, addr.sin_port));
		log_status("ACCEPT: %s(%d) on descriptor %d\n", hostname,
				   ntohs(addr.sin_port), newsock);
#endif
		log_status("CONCOUNT: There are now %d open connections.\n", ++ndescriptors);
		return initializesock(newsock, hostname);
	}
}


#ifdef SPAWN_HOST_RESOLVER

void
kill_resolver(void)
{
	int i;
	pid_t p;

	write(resolver_sock[1], "QUIT\n", 5);
	p = wait(&i);
}



static time_t resolver_spawn_time = 0;

void
spawn_resolver()
{
	if (time(NULL) - resolver_spawn_time < 5) {
		return;
	}
	resolver_spawn_time = time(NULL);

	socketpair(AF_UNIX, SOCK_STREAM, 0, resolver_sock);
	make_nonblocking(resolver_sock[1]);
	if ((global_resolver_pid=fork())==0) {
		close(0);
		close(1);
		dup(resolver_sock[0]);
		dup(resolver_sock[0]);
#ifdef BINDIR
		{
			char resolverpath[BUFFER_LEN];
			snprintf(resolverpath, sizeof(resolverpath), "%s/fb-resolver", BINDIR);
			execl(resolverpath, "fb-resolver", NULL);
			snprintf(resolverpath, sizeof(resolverpath), "%s/resolver", BINDIR);
			execl(resolverpath, "resolver", NULL);
		}
#endif
		execl("/usr/local/bin/fb-resolver", "resolver", NULL);
		execl("/usr/local/bin/resolver", "resolver", NULL);
		execl("/usr/bin/fb-resolver", "resolver", NULL);
		execl("/usr/bin/resolver", "resolver", NULL);
		execl("./fb-resolver", "resolver", NULL);
		execl("./resolver", "resolver", NULL);
#if 0
		execl("@bindir@/resolver", "resolver", NULL);
		execl("./bin/resolver", "resolver", NULL);
		execl("/usr/lib/fbmuck/resolver", "resolver", NULL);
		execl("/usr/local/fbmuck/bin/resolver", "resolver", NULL);
		execl("/usr/local/bin/resolver", "resolver", NULL);
		execl("../src/resolver", "resolver", NULL);
		execl("./resolver", "resolver", NULL);
		execl("resolver", "resolver", NULL);
#endif
		log_status("%s","Unable to spawn host resolver!");
		_exit(1);
	}
}


void
resolve_hostnames()
{
	char buf[BUFFER_LEN];
	char *ptr, *ptr2, *ptr3, *hostip, *port, *hostname, *username, *tempptr;
	struct descriptor_data *d;
	int got, dc;

	got = read(resolver_sock[1], buf, sizeof(buf));
	if (got < 0)
		return;
	if (got == sizeof(buf)) {
		got--;
		while (got > 0 && buf[got] != '\n')
			buf[got--] = '\0';
	}
	ptr = buf;
	dc = 0;
	do {
		for (ptr2 = ptr; *ptr && *ptr != '\n' && dc < got; ptr++, dc++) ;
		if (*ptr) {
			*ptr++ = '\0';
			dc++;
		}
		if (*ptr2) {
#ifdef USE_IPV6
			ptr3 = index(ptr2, '|');
#else
			ptr3 = index(ptr2, ':');
#endif
			if (!ptr3)
				return;
			hostip = ptr2;
			port = index(ptr2, '(');
			if (!port)
				return;
			tempptr = index(port, ')');
			if (!tempptr)
				return;
			*tempptr = '\0';
			hostname = ptr3;
			username = index(ptr3, '(');
			if (!username)
				return;
			tempptr = index(username, ')');
			if (!tempptr)
				return;
			*tempptr = '\0';
			if (*port && *hostname && *username) {
				*port++ = '\0';
				*hostname++ = '\0';
				*username++ = '\0';
				for (d = descriptor_list; d; d = d->next) {
					if (!strcmp(d->hostname, hostip) && !strcmp(d->username, port)) {
						FREE(d->hostname);
						FREE(d->username);
						d->hostname = strsave(hostname);
						d->username = strsave(username);
					}
				}
			}
		}
	} while (dc < got && *ptr);
}

#endif


/*  addrout -- Translate address 'a' from int to text.		*/

const char *
#ifdef USE_IPV6
addrout(int lport, struct in6_addr *a, unsigned short prt)
#else
addrout(int lport, long a, unsigned short prt)
#endif
{
	static char buf[128];

#ifdef USE_IPV6
	char ip6addr[128];

	struct in6_addr addr;
	memcpy(&addr.s6_addr, a, sizeof(struct in6_addr));
#else
	struct in_addr addr;

	addr.s_addr = a;
#endif

	prt = ntohs(prt);

#ifndef SPAWN_HOST_RESOLVER
	if (tp_hostnames) {
		/* One day the nameserver Qwest uses decided to start */
		/* doing halfminute lags, locking up the entire muck  */
		/* that long on every connect.  This is intended to   */
		/* prevent that, reduces average lag due to nameserver */
		/* to 1 sec/call, simply by not calling nameserver if */
		/* it's in a slow mood *grin*. If the nameserver lags */
		/* consistently, a hostname cache ala OJ's tinymuck2.3 */
		/* would make more sense:                             */
		static secs_lost = 0;

		if (secs_lost) {
			secs_lost--;
		} else {
			time_t gethost_start = time(NULL);

#ifdef USE_IPV6
			struct hostent *he = gethostbyaddr(((char *) &addr),
											   sizeof(addr), AF_INET6);
#else
			struct hostent *he = gethostbyaddr(((char *) &addr),
											   sizeof(addr), AF_INET);
#endif
			time_t gethost_stop = time(NULL);
			time_t lag = gethost_stop - gethost_start;

			if (lag > 10) {
				secs_lost = lag;

#if MIN_SECS_TO_LOG
				if (lag >= CFG_MIN_SECS_TO_LOG) {
					log_status("GETHOSTBYNAME-RAN: secs %3d\n", lag);
				}
#endif

			}
			if (he) {
				snprintf(buf, sizeof(buf), "%s(%u)", he->h_name, prt);
				return buf;
			}
		}
	}
#endif							/* SPAWN_HOST_RESOLVER */

#ifdef USE_IPV6
	inet_ntop(AF_INET6, a, ip6addr, 128);
#ifdef SPAWN_HOST_RESOLVER
	snprintf(buf, sizeof(buf), "%s(%u)%u\n", ip6addr, prt, lport);
	if (tp_hostnames) {
		write(resolver_sock[1], buf, strlen(buf));
	}
#endif
	snprintf(buf, sizeof(buf), "%s(%u)\n", ip6addr, prt);

#else
	a = ntohl(a);

#ifdef SPAWN_HOST_RESOLVER
	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld(%u)%u\n",
			(a >> 24) & 0xff,
			(a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff, prt, lport);
	if (tp_hostnames) {
		write(resolver_sock[1], buf, strlen(buf));
	}
#endif

	snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld(%u)",
			(a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff, prt);
#endif
	return buf;
}


void
clearstrings(struct descriptor_data *d)
{
	if (d->output_prefix) {
		FREE(d->output_prefix);
		d->output_prefix = 0;
	}
	if (d->output_suffix) {
		FREE(d->output_suffix);
		d->output_suffix = 0;
	}
}

void
shutdownsock(struct descriptor_data *d)
{
	if (d->connected) {
		log_status("DISCONNECT: descriptor %d player %s(%d) from %s(%s)\n",
				   d->descriptor, NAME(d->player), d->player, d->hostname, d->username);
		announce_disconnect(d);
	} else {
		log_status("DISCONNECT: descriptor %d from %s(%s) never connected.\n",
				   d->descriptor, d->hostname, d->username);
	}
	clearstrings(d);
	shutdown(d->descriptor, 2);
	close(d->descriptor);
    forget_descriptor(d);
	freeqs(d);
	*d->prev = d->next;
	if (d->next)
		d->next->prev = d->prev;
	if (d->hostname)
		free((void *) d->hostname);
	if (d->username)
		free((void *) d->username);
	mcp_frame_clear(&d->mcpframe);
	FREE(d);
	ndescriptors--;
	log_status("CONCOUNT: There are now %d open connections.\n", ndescriptors);
}

void
SendText(McpFrame * mfr, const char *text)
{
	queue_string((struct descriptor_data *) mfr->descriptor, text);
}

void
FlushText(McpFrame * mfr)
{
	struct descriptor_data *d = (struct descriptor_data *)mfr->descriptor;
	if (d && !process_output(d)) {
		d->booted = 1;
	}
}

int
mcpframe_to_descr(McpFrame * ptr)
{
	return ((struct descriptor_data *) ptr->descriptor)->descriptor;
}

int
mcpframe_to_user(McpFrame * ptr)
{
	return ((struct descriptor_data *) ptr->descriptor)->player;
}

struct descriptor_data *
initializesock(int s, const char *hostname)
{
	struct descriptor_data *d;
	char buf[128], *ptr;

	MALLOC(d, struct descriptor_data, 1);

	d->descriptor = s;
#ifdef USE_SSL
	d->ssl_session = NULL;
#endif
	d->connected = 0;
	d->booted = 0;
	d->player = -1;
	d->con_number = 0;
	d->connected_at = time(NULL);
	make_nonblocking(s);
	d->output_prefix = 0;
	d->output_suffix = 0;
	d->output_size = 0;
	d->output.lines = 0;
	d->output.head = 0;
	d->output.tail = &d->output.head;
	d->input.lines = 0;
	d->input.head = 0;
	d->input.tail = &d->input.head;
	d->raw_input = 0;
	d->raw_input_at = 0;
	d->inIAC = 0;
	d->quota = tp_command_burst_size;
	d->last_time = d->connected_at;
	mcp_frame_init(&d->mcpframe, d);
	strcpy(buf, hostname);
	ptr = index(buf, ')');
	if (ptr)
		*ptr = '\0';
	ptr = index(buf, '(');
	*ptr++ = '\0';
	d->hostname = alloc_string(buf);
	d->username = alloc_string(ptr);
	if (descriptor_list)
		descriptor_list->prev = &d->next;
	d->next = descriptor_list;
	d->prev = &descriptor_list;
	descriptor_list = d;
    remember_descriptor(d);

	mcp_negotiation_start(&d->mcpframe);
	welcome_user(d);
	return d;
}

int
make_socket(int port)
{
	int s;
	int opt;

#ifdef USE_IPV6
	struct sockaddr_in6 server;

	s = socket(AF_INET6, SOCK_STREAM, 0);
#else
	struct sockaddr_in server;

	s = socket(AF_INET, SOCK_STREAM, 0);
#endif

	if (s < 0) {
		perror("creating stream socket");
		exit(3);
	}
	opt = 1;
	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) {
		perror("setsockopt");
		exit(1);
	}
#ifdef USE_IPV6
	server.sin6_family = AF_INET6;
	memset(server.sin6_addr.s6_addr, 0, 16);
	server.sin6_port = htons(port);
#else
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(port);
#endif

	if (bind(s, (struct sockaddr *) &server, sizeof(server))) {
		perror("binding stream socket");
		close(s);
		exit(4);
	}
	listen(s, 5);
	return s;
}

struct text_block *
make_text_block(const char *s, int n)
{
	struct text_block *p;

	MALLOC(p, struct text_block, 1);
	MALLOC(p->buf, char, n);

	bcopy(s, p->buf, n);
	p->nchars = n;
	p->start = p->buf;
	p->nxt = 0;
	return p;
}

void
free_text_block(struct text_block *t)
{
	FREE(t->buf);
	FREE((char *) t);
}

void
add_to_queue(struct text_queue *q, const char *b, int n)
{
	struct text_block *p;

	if (n == 0)
		return;

	p = make_text_block(b, n);
	p->nxt = 0;
	*q->tail = p;
	q->tail = &p->nxt;
	q->lines++;
}

int
flush_queue(struct text_queue *q, int n)
{
	struct text_block *p;
	int really_flushed = 0;

	n += strlen(flushed_message);

	while (n > 0 && (p = q->head)) {
		n -= p->nchars;
		really_flushed += p->nchars;
		q->head = p->nxt;
		q->lines--;
		free_text_block(p);
	}
	p = make_text_block(flushed_message, strlen(flushed_message));
	p->nxt = q->head;
	q->head = p;
	q->lines++;
	if (!p->nxt)
		q->tail = &p->nxt;
	really_flushed -= p->nchars;
	return really_flushed;
}

int
queue_write(struct descriptor_data *d, const char *b, int n)
{
	int space;

	space = tp_max_output - d->output_size - n;
	if (space < 0)
		d->output_size -= flush_queue(&d->output, -space);
	add_to_queue(&d->output, b, n);
	d->output_size += n;
	return n;
}

int
queue_string(struct descriptor_data *d, const char *s)
{
	return queue_write(d, s, strlen(s));
}

int
process_output(struct descriptor_data *d)
{
	struct text_block **qp, *cur;
	int cnt;

	/* drastic, but this may give us crash test data */
	if (!d || !d->descriptor) {
		fprintf(stderr, "process_output: bad descriptor or connect struct!\n");
		abort();
	}

	if (d->output.lines == 0) {
		return 1;
	}

	for (qp = &d->output.head; (cur = *qp);) {
		cnt = socket_write(d, cur->start, cur->nchars);
#ifndef WIN32
		if (cnt < 0) {
			if (errno == EWOULDBLOCK)
				return 1;
			return 0;
		}
#else
		if (cnt < 0 || cnt == SOCKET_ERROR) {
			if (WSAGetLastError() == WSAEWOULDBLOCK)
				return 1;
			return 0;
		}
#endif
		d->output_size -= cnt;
		if (cnt == cur->nchars) {
			d->output.lines--;
			if (!cur->nxt) {
				d->output.tail = qp;
				d->output.lines = 0;
			}
			*qp = cur->nxt;
			free_text_block(cur);
			continue;			/* do not adv ptr */
		}
		cur->nchars -= cnt;
		cur->start += cnt;
		break;
	}
	return 1;
}

void
make_nonblocking(int s)
{
#ifndef WIN32
# if !defined(O_NONBLOCK) || defined(ULTRIX)	/* POSIX ME HARDER */
#  ifdef FNDELAY					/* SUN OS */
#   define O_NONBLOCK FNDELAY
#  else
#   ifdef O_NDELAY				/* SyseVil */
#    define O_NONBLOCK O_NDELAY
#   endif						/* O_NDELAY */
#  endif							/* FNDELAY */
# endif

	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) {
		perror("make_nonblocking: fcntl");
		panic("O_NONBLOCK fcntl failed");
	}
#else /* WIN32 */
	unsigned long O_NONBLOCK = 1;
	if (ioctlsocket(s, FIONBIO, &O_NONBLOCK) == SOCKET_ERROR) {
		perror("make_nonblocking: ioctlsocket");
		panic("O_NONBLOCK ioctlsocket failed");
	}
#endif /* WIN32 */
}

void
freeqs(struct descriptor_data *d)
{
	struct text_block *cur, *next;

	cur = d->output.head;
	while (cur) {
		next = cur->nxt;
		free_text_block(cur);
		cur = next;
	}
	d->output.lines = 0;
	d->output.head = 0;
	d->output.tail = &d->output.head;

	cur = d->input.head;
	while (cur) {
		next = cur->nxt;
		free_text_block(cur);
		cur = next;
	}
	d->input.lines = 0;
	d->input.head = 0;
	d->input.tail = &d->input.head;

	if (d->raw_input)
		FREE(d->raw_input);
	d->raw_input = 0;
	d->raw_input_at = 0;
}

char *
strsave(const char *s)
{
	char *p;

	MALLOC(p, char, strlen(s) + 1);

	if (p)
		strcpy(p, s);
	return p;
}

void
save_command(struct descriptor_data *d, const char *command)
{
	add_to_queue(&d->input, command, strlen(command) + 1);
}

int
process_input(struct descriptor_data *d)
{
	char buf[MAX_COMMAND_LEN * 2];
	int got;
	char *p, *pend, *q, *qend;

	got = socket_read(d, buf, sizeof buf);
#ifndef WIN32
# ifdef USE_SSL
	if ( (got <= 0) && errno != EWOULDBLOCK ) 
# else
	if (got <= 0)
# endif
#else
# ifdef USE_SSL
	if ( (got <= 0 || got == SOCKET_ERROR) && WSAGetLastError() != EWOULDBLOCK ) 
# else
	if (got <= 0 || got == SOCKET_ERROR)
# endif
#endif
	{
		/* perror("socket_read"); */
		return 0;
	}
	if (!d->raw_input) {
		MALLOC(d->raw_input, char, MAX_COMMAND_LEN);

		d->raw_input_at = d->raw_input;
	}
	p = d->raw_input_at;
	pend = d->raw_input + MAX_COMMAND_LEN - 1;
	for (q = buf, qend = buf + got; q < qend; q++) {
		if (*q == '\n') {
			d->last_time = time(NULL);
			*p = '\0';
			if (p >= d->raw_input)
				save_command(d, d->raw_input);
			p = d->raw_input;
		} else if (d->inIAC == 1) {
			switch (*q) {
				case '\361': /* NOP */
					d->inIAC = 0;
					break;
				case '\363': /* Break */
				case '\364': /* Interrupt Process */
					save_command(d, BREAK_COMMAND);
					d->inIAC = 0;
					break;
				case '\365': /* Abort Output */
					/* could be handy, but for now leave unimplemented */
					d->inIAC = 0;
					break;
				case '\366': /* AYT */
					{
						char sendbuf[] = "[Yes]\r\n";
						socket_write(d, sendbuf, strlen(sendbuf));
						d->inIAC = 0;
						break;
					}
				case '\367': /* Erase character */
					if (p > d->raw_input)
						p--;
					d->inIAC = 0;
					break;
				case '\370': /* Erase line */
					p = d->raw_input;
					d->inIAC = 0;
					break;
				case '\372': /* Go Ahead */
					/* treat as a NOP (?) */
					d->inIAC = 0;
					break;
				case '\373': /* WILL (option offer) */
					d->inIAC = 2;
					break;
				case '\374': /* WONT (option offer) */
					d->inIAC = 4;
					break;
				case '\375': /* DO (option request) */
				case '\376': /* DONT (option request) */
					d->inIAC = 3;
					break;
				case '\377': /* IAC a second time */
#if 0
					/* If we were 8 bit clean, we'd pass this along */
					*p++ = *q;
#endif
					d->inIAC = 0;
					break;
				default:
					/* just ignore */
					d->inIAC = 0;
					break;
			}
		} else if (d->inIAC == 2) {
			/* We don't negotiate: send back DONT option */
			char sendbuf[4];
			sendbuf[0] = '\377';
			sendbuf[1] = '\376';
			sendbuf[2] = *q;
			sendbuf[3] = '\0';
			socket_write(d, sendbuf, 3);
			d->inIAC = 0;
		} else if (d->inIAC == 3) {
			/* We don't negotiate: send back WONT option */
			char sendbuf[4];
			sendbuf[0] = '\377';
			sendbuf[1] = '\374';
			sendbuf[2] = *q;
			sendbuf[3] = '\0';
			socket_write(d, sendbuf, 3);
			d->inIAC = 0;
		} else if (d->inIAC == 4) {
			/* Ignore WONT option. */
			d->inIAC = 0;
		} else if (*q == '\377') {
			/* Got TELNET IAC, store for next byte */	
			d->inIAC = 1;
		} else if (p < pend && isascii(*q)) {
			if (isprint(*q)) {
				*p++ = *q;
			} else if (*q == '\t') {
				*p++ = ' ';
			} else if (*q == 8 || *q == 127) {
				/* if BS or DEL, delete last character */
				if (p > d->raw_input)
					p--;
			}
			d->inIAC = 0;
		}
	}
	if (p > d->raw_input) {
		d->raw_input_at = p;
	} else {
		FREE(d->raw_input);
		d->raw_input = 0;
		d->raw_input_at = 0;
	}
	return 1;
}

void
set_userstring(char **userstring, const char *command)
{
	if (*userstring) {
		FREE(*userstring);
		*userstring = 0;
	}
	while (*command && isascii(*command) && isspace(*command))
		command++;
	if (*command)
		*userstring = strsave(command);
}

void
process_commands(void)
{
	int nprocessed;
	struct descriptor_data *d, *dnext;
	struct text_block *t;

	do {
		nprocessed = 0;
		for (d = descriptor_list; d; d = dnext) {
			dnext = d->next;
			if (d->quota > 0 && (t = d->input.head)) {
				if (d->connected && PLAYER_BLOCK(d->player) && !is_interface_command(t->start)) {
					char *tmp = t->start;
					if (!strncmp(tmp, "#$\"", 3)) {
						/* Un-escape MCP escaped lines */
						tmp += 3;
					}
					/* WORK: send player's foreground/preempt programs an exclusive READ mufevent */
					if (!read_event_notify(d->descriptor, d->player, tmp) && !*tmp) {
						/* Didn't send blank line.  Eat it.  */
						nprocessed++;
						d->input.head = t->nxt;
						d->input.lines--;
						if (!d->input.head) {
							d->input.tail = &d->input.head;
							d->input.lines = 0;
						}
						free_text_block(t);
					}
				} else {
					if (strncmp(t->start, "#$#", 3)) {
						/* Not an MCP mesg, so count this against quota. */
						d->quota--;
					}
					nprocessed++;
					if (!do_command(d, t->start)) {
						d->booted = 2;
						/* Disconnect player next pass through main event loop. */
					}
					d->input.head = t->nxt;
					d->input.lines--;
					if (!d->input.head) {
						d->input.tail = &d->input.head;
						d->input.lines = 0;
					}
					free_text_block(t);
				}
			}
		}
	} while (nprocessed > 0);
}

int
is_interface_command(const char* cmd)
{
	const char* tmp = cmd;
	if (!strncmp(tmp, "#$\"", 3)) {
		/* dequote MCP quoting. */
		tmp += 3;
	}
	if (!strncmp(cmd, "#$#", 3)) /* MCP mesg. */
		return 1;
	if (!string_compare(tmp, BREAK_COMMAND))
		return 1;
	if (!strcmp(tmp, QUIT_COMMAND))
		return 1;
	if (!strncmp(tmp, WHO_COMMAND, strlen(WHO_COMMAND)))
		return 1;
	if (!strncmp(tmp, PREFIX_COMMAND, strlen(PREFIX_COMMAND)))
		return 1;
	if (!strncmp(tmp, SUFFIX_COMMAND, strlen(SUFFIX_COMMAND)))
		return 1;
	return 0;
}

int
do_command(struct descriptor_data *d, char *command)
{
	char buf[BUFFER_LEN];
	char cmdbuf[BUFFER_LEN];

	if (!mcp_frame_process_input(&d->mcpframe, command, cmdbuf, sizeof(cmdbuf))) {
		d->quota++;
		return 1;
	}
	command = cmdbuf;
	if (d->connected)
		ts_lastuseobject(d->player);

	if (!string_compare(command, BREAK_COMMAND)) {
	        if (!d->connected)
		        return 0;
		if (dequeue_prog(d->player, 2)) {
			if (d->output_prefix) {
				queue_ansi(d, d->output_prefix);
				queue_write(d, "\r\n", 2);
			}
			queue_ansi(d, "Foreground program aborted.\r\n");
			if ((FLAGS(d->player) & INTERACTIVE))
				if ((FLAGS(d->player) & READMODE))
					process_command(d->descriptor, d->player, command);
			if (d->output_suffix) {
				queue_ansi(d, d->output_suffix);
				queue_write(d, "\r\n", 2);
			}
		}
		PLAYER_SET_BLOCK(d->player, 0);
		return 1;
	} else if (!strcmp(command, QUIT_COMMAND)) {
		return 0;
	} else if ((!strncmp(command, WHO_COMMAND, sizeof(WHO_COMMAND) - 1)) ||
                   (*command == OVERIDE_TOKEN &&
                    (!strncmp(command+1, WHO_COMMAND, sizeof(WHO_COMMAND) - 1))
                   )) {
		if (d->output_prefix) {
			queue_ansi(d, d->output_prefix);
			queue_write(d, "\r\n", 2);
		}
		strcpy(buf, "@");
		strcatn(buf, sizeof(buf), WHO_COMMAND);
		strcatn(buf, sizeof(buf), " ");
		strcatn(buf, sizeof(buf), command + sizeof(WHO_COMMAND) - 1);
		if (!d->connected || (FLAGS(d->player) & INTERACTIVE)) {
			if (tp_secure_who) {
				queue_ansi(d, "Sorry, WHO is unavailable at this point.\r\n");
			} else {
				dump_users(d, command + sizeof(WHO_COMMAND) - 1);
			}
		} else {
			if ((!(TrueWizard(OWNER(d->player)) &&
                              (*command == OVERIDE_TOKEN))) &&
                            can_move(d->descriptor, d->player, buf, 2)) {
				do_move(d->descriptor, d->player, buf, 2);
			} else {
				dump_users(d, command + sizeof(WHO_COMMAND) - 
                                           ((*command == OVERIDE_TOKEN)?0:1));
			}
		}
		if (d->output_suffix) {
			queue_ansi(d, d->output_suffix);
			queue_write(d, "\r\n", 2);
		}
	} else if (!strncmp(command, PREFIX_COMMAND, sizeof(PREFIX_COMMAND) - 1)) {
		set_userstring(&d->output_prefix, command + sizeof(PREFIX_COMMAND) - 1);
	} else if (!strncmp(command, SUFFIX_COMMAND, sizeof(SUFFIX_COMMAND) - 1)) {
		set_userstring(&d->output_suffix, command + sizeof(SUFFIX_COMMAND) - 1);
	} else {
		if (d->connected) {
			if (d->output_prefix) {
				queue_ansi(d, d->output_prefix);
				queue_write(d, "\r\n", 2);
			}
			process_command(d->descriptor, d->player, command);
			if (d->output_suffix) {
				queue_ansi(d, d->output_suffix);
				queue_write(d, "\r\n", 2);
			}
		} else {
			check_connect(d, command);
		}
	}
	return 1;
}

void
interact_warn(dbref player)
{
	if (FLAGS(player) & INTERACTIVE) {
		char buf[BUFFER_LEN];

		snprintf(buf, sizeof(buf), "***  %s  ***",
				(FLAGS(player) & READMODE) ?
				"You are currently using a program.  Use \"@Q\" to return to a more reasonable state of control."
				: (PLAYER_INSERT_MODE(player) ?
				   "You are currently inserting MUF program text.  Use \".\" to return to the editor, then \"quit\" if you wish to return to your regularly scheduled Muck universe."
				   : "You are currently using the MUF program editor."));
		notify(player, buf);
	}
}

void
check_connect(struct descriptor_data *d, const char *msg)
{
	char command[MAX_COMMAND_LEN];
	char user[MAX_COMMAND_LEN];
	char password[MAX_COMMAND_LEN];
	dbref player;

	parse_connect(msg, command, user, password);

	if (!strncmp(command, "co", 2)) {
		player = connect_player(user, password);
		if (player == NOTHING) {
			queue_ansi(d, connect_fail);
			log_status("FAILED CONNECT %s on descriptor %d\n", user, d->descriptor);
		} else {
			if ((wizonly_mode ||
				 (tp_playermax && con_players_curr >= tp_playermax_limit)) &&
				!TrueWizard(player)) {
				if (wizonly_mode) {
					queue_ansi(d, "Sorry, but the game is in maintenance mode currently, and only wizards are allowed to connect.  Try again later.");
				} else {
					queue_ansi(d, tp_playermax_bootmesg);
				}
				queue_string(d, "\r\n");
				d->booted = 1;
			} else {
				log_status("CONNECTED: %s(%d) on descriptor %d\n",
						   NAME(player), player, d->descriptor);
				d->connected = 1;
				d->connected_at = time(NULL);
				d->player = player;
				update_desc_count_table();
				remember_player_descr(player, d->descriptor);
				/* cks: someone has to initialize this somewhere. */
				PLAYER_SET_BLOCK(d->player, 0);
				spit_file(player, MOTD_FILE);
				announce_connect(d->descriptor, player);
				interact_warn(player);
				if (sanity_violated && Wizard(player)) {
					notify(player,
						   "#########################################################################");
					notify(player,
						   "## WARNING!  The DB appears to be corrupt!  Please repair immediately! ##");
					notify(player,
						   "#########################################################################");
				}
				con_players_curr++;
			}
		}
	} else if (!strncmp(command, "cr", 2)) {
		if (!tp_registration) {
			if (wizonly_mode || (tp_playermax && con_players_curr >= tp_playermax_limit)) {
				if (wizonly_mode) {
					queue_ansi(d, "Sorry, but the game is in maintenance mode currently, and only wizards are allowed to connect.  Try again later.");
				} else {
					queue_ansi(d, tp_playermax_bootmesg);
				}
				queue_string(d, "\r\n");
				d->booted = 1;
			} else {
				player = create_player(user, password);
				if (player == NOTHING) {
					queue_ansi(d, create_fail);
					log_status("FAILED CREATE %s on descriptor %d\n", user, d->descriptor);
				} else {
					log_status("CREATED %s(%d) on descriptor %d\n",
							   NAME(player), player, d->descriptor);
					d->connected = 1;
					d->connected_at = time(NULL);
					d->player = player;
					update_desc_count_table();
					remember_player_descr(player, d->descriptor);
					/* cks: someone has to initialize this somewhere. */
					PLAYER_SET_BLOCK(d->player, 0);
					spit_file(player, MOTD_FILE);
					announce_connect(d->descriptor, player);
					con_players_curr++;
				}
			}
		} else {
			queue_ansi(d, tp_register_mesg);
			queue_string(d, "\r\n");
			log_status("FAILED CREATE %s on descriptor %d\n", user, d->descriptor);
		}
	} else if (!*command) {
		/* do nothing */
	} else {
		welcome_user(d);
	}
}

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

	while (*msg && isascii(*msg) && isspace(*msg))
		msg++;
	p = command;
	while (*msg && isascii(*msg) && !isspace(*msg))
		*p++ = *msg++;
	*p = '\0';
	while (*msg && isascii(*msg) && isspace(*msg))
		msg++;
	p = user;
	while (*msg && isascii(*msg) && !isspace(*msg))
		*p++ = *msg++;
	*p = '\0';
	while (*msg && isascii(*msg) && isspace(*msg))
		msg++;
	p = pass;
	while (*msg && isascii(*msg) && !isspace(*msg))
		*p++ = *msg++;
	*p = '\0';
}


int
boot_off(dbref player)
{
    int* darr;
    int dcount;
	struct descriptor_data *last = NULL;

	darr = get_player_descrs(player, &dcount);
	if (darr) {
        last = descrdata_by_descr(darr[0]);
	}

	if (last) {
		process_output(last);
		last->booted = 1;
		/* shutdownsock(last); */
		return 1;
	}
	return 0;
}

void
boot_player_off(dbref player)
{
    int di;
    int* darr;
    int dcount;
    struct descriptor_data *d;
 
	darr = get_player_descrs(player, &dcount);
    for (di = 0; di < dcount; di++) {
        d = descrdata_by_descr(darr[di]);
        if (d) {
            d->booted = 1;
        }
    }
}


void
close_sockets(const char *msg)
{
	struct descriptor_data *d, *dnext;
	int i;

	for (d = descriptor_list; d; d = dnext) {
		dnext = d->next;
		if (d->connected) {
			forget_player_descr(d->player, d->descriptor);
		}
		socket_write(d, msg, strlen(msg));
		socket_write(d, shutdown_message, strlen(shutdown_message));
		clearstrings(d);
		if (shutdown(d->descriptor, 2) < 0)
			perror("shutdown");
		close(d->descriptor);
		freeqs(d);
		*d->prev = d->next;
		if (d->next)
			d->next->prev = d->prev;
		if (d->hostname)
			free((void *) d->hostname);
		if (d->username)
			free((void *) d->username);
		mcp_frame_clear(&d->mcpframe);
		FREE(d);
		ndescriptors--;
	}
	update_desc_count_table();
	for (i = 0; i < numsocks; i++) {
		close(sock[i]);
	}
#ifdef USE_SSL
	for (i = 0; i < ssl_numsocks; i++) {
		close(ssl_sock[i]);
	}
#endif
}


void
do_armageddon(dbref player, const char *msg)
{
	char buf[BUFFER_LEN];

	if (!Wizard(player)) {
		notify(player, "Sorry, but you don't look like the god of War to me.");
		log_status("ILLEGAL ARMAGEDDON: tried by %s\n", unparse_object(player, player));
		return;
	}
	snprintf(buf, sizeof(buf), "\r\nImmediate shutdown initiated by %s.\r\n", NAME(player));
	if (msg || *msg)
		strcatn(buf, sizeof(buf), msg);
	log_status("ARMAGEDDON initiated by %s(%d): %s\n", NAME(player), player, msg);
	fprintf(stderr, "ARMAGEDDON initiated by %s(%d): %s\n", NAME(player), player, msg);
	close_sockets(buf);

#ifdef SPAWN_HOST_RESOLVER
	kill_resolver();
#endif

	exit(1);
}


void
emergency_shutdown(void)
{
	close_sockets("\r\nEmergency shutdown due to server crash.");

#ifdef SPAWN_HOST_RESOLVER
	kill_resolver();
#endif

}


void
dump_users(struct descriptor_data *e, char *user)
{
	struct descriptor_data *d;
	int wizard, players;
	time_t now;
	char buf[2048];
	char pbuf[64];

#ifdef COMPRESS
	extern const char *uncompress(const char *);
#endif

/* #ifdef GOD_PRIV */
/* -- Wizard should always override tp_who_doing JES
	if (tp_who_doing) {
		wizard = e->connected && God(e->player);
	} else {
		wizard = e->connected && Wizard(e->player);
	}
*/
/* #else */
	wizard = e->connected && Wizard(e->player) && !tp_who_doing;
/* #endif */

	while (*user && (isspace(*user) || *user == '*')) {
		if (tp_who_doing && *user == '*' && e->connected && Wizard(e->player))
			wizard = 1;
		user++;
	}

	if (wizard)
		/* S/he is connected and not quelled. Okay; log it. */
		log_command("WIZ: %s(%d) in %s(%d):  %s\n", NAME(e->player),
					(int) e->player, NAME(DBFETCH(e->player)->location),
					(int) DBFETCH(e->player)->location, "WHO");

	if (!*user)
		user = NULL;

	(void) time(&now);
	if (wizard) {
		queue_ansi(e, "Player Name                Location     On For Idle   Host\r\n");
	} else {
		if (tp_who_doing) {
			queue_ansi(e, "Player Name           On For Idle   Doing...\r\n");
		} else {
			queue_ansi(e, "Player Name           On For Idle\r\n");
		}
	}

	d = descriptor_list;
	players = 0;
	while (d) {
		if (d->connected &&
			(!tp_who_hides_dark ||
			 (wizard || !(FLAGS(d->player) & DARK))) &&
			++players && (!user || string_prefix(NAME(d->player), user))
				) {
			if (wizard) {
				/* don't print flags, to save space */
				snprintf(pbuf, sizeof(pbuf), "%.*s(#%d)", PLAYER_NAME_LIMIT + 1,
						NAME(d->player), (int) d->player);
#ifdef GOD_PRIV
				if (!God(e->player))
#ifdef USE_SSL
					snprintf(buf, sizeof(buf), "%-*s [%6d] %10s %4s%c%c %s\r\n",
#else
					snprintf(buf, sizeof(buf), "%-*s [%6d] %10s%4s%c  %s\r\n",
#endif
							PLAYER_NAME_LIMIT + 10, pbuf,
							(int) DBFETCH(d->player)->location,
							time_format_1(now - d->connected_at),
							time_format_2(now - d->last_time),
							((FLAGS(d->player) & INTERACTIVE) ? '*' : ' '),
#ifdef USE_SSL	
							(d->ssl_session ? '@' : ' '),
#endif
							d->hostname);
				else
#endif
#ifdef USE_SSL
					snprintf(buf, sizeof(buf), "%-*s [%6d] %10s %4s%c%c %s(%s)\r\n",
#else
					snprintf(buf, sizeof(buf), "%-*s [%6d] %10s %4s%c  %s(%s)\r\n",
#endif
							PLAYER_NAME_LIMIT + 10, pbuf,
							(int) DBFETCH(d->player)->location,
							time_format_1(now - d->connected_at),
							time_format_2(now - d->last_time),
							((FLAGS(d->player) & INTERACTIVE) ? '*' : ' '),
#ifdef USE_SSL
							(d->ssl_session ? '@' : ' '),
#endif
							d->hostname, d->username);
			} else {
				if (tp_who_doing) {
					/* Modified to take into account PLAYER_NAME_LIMIT changes */
#ifdef USE_SSL
					snprintf(buf, sizeof(buf), "%-*s %10s %4s%c%c %*s\r\n",
#else
					snprintf(buf, sizeof(buf), "%-*s %10s %4s%c  %*s\r\n",
#endif
							PLAYER_NAME_LIMIT + 1,
							NAME(d->player),
							time_format_1(now - d->connected_at),
							time_format_2(now - d->last_time),
							((FLAGS(d->player) & INTERACTIVE) ? '*' : ' '),
#ifdef USE_SSL
							(d->ssl_session ? '@' : ' '),
#endif		
							(int) (44 - (PLAYER_NAME_LIMIT - 16)),
							GETDOING(d->player) ?
#ifdef COMPRESS
							uncompress(GETDOING(d->player))
#else
							GETDOING(d->player)
#endif
							: "");
				} else {
#ifdef USE_SSL
					snprintf(buf, sizeof(buf), "%-*s %10s %4s%c%c\r\n",
#else
					snprintf(buf, sizeof(buf), "%-*s %10s %4s%c\r\n",
#endif
							(int)(PLAYER_NAME_LIMIT + 1),
							NAME(d->player),
							time_format_1(now - d->connected_at),
							time_format_2(now - d->last_time),
							((FLAGS(d->player) & INTERACTIVE) ? '*' : ' ')
#ifdef USE_SSL
							,(d->ssl_session ? '@' : ' ')
#endif
							);
				}
			}
			queue_ansi(e, buf);
		}
		d = d->next;
	}
	if (players > con_players_max)
		con_players_max = players;
	snprintf(buf, sizeof(buf), "%d player%s %s connected.  (Max was %d)\r\n", players,
			(players == 1) ? "" : "s", (players == 1) ? "is" : "are", con_players_max);
	queue_ansi(e, buf);
}

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

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

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

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


void
announce_puppets(dbref player, const char *msg, const char *prop)
{
	dbref what, where;
	const char *ptr, *msg2;
	char buf[BUFFER_LEN];

	for (what = 0; what < db_top; what++) {
		if (Typeof(what) == TYPE_THING && (FLAGS(what) & ZOMBIE)) {
			if (OWNER(what) == player) {
				where = getloc(what);
				if ((!Dark(where)) && (!Dark(player)) && (!Dark(what))) {
					msg2 = msg;
					if ((ptr = (char *) get_property_class(what, prop)) && *ptr)
						msg2 = ptr;
					snprintf(buf, sizeof(buf), "%.512s %.3000s", PNAME(what), msg2);
					notify_except(DBFETCH(where)->contents, what, buf, what);
				}
			}
		}
	}
}

void
announce_connect(int descr, dbref player)
{
	dbref loc;
	char buf[BUFFER_LEN];
	struct match_data md;
	dbref exit;
	time_t tt;

	if ((loc = getloc(player)) == NOTHING)
		return;

	if (tp_rwho) {
		time(&tt);
		snprintf(buf, sizeof(buf), "%d@%s", player, tp_muckname);
		rwhocli_userlogin(buf, NAME(player), tt);
	}

	if ((!Dark(player)) && (!Dark(loc))) {
		snprintf(buf, sizeof(buf), "%s has connected.", PNAME(player));
		notify_except(DBFETCH(loc)->contents, player, buf, player);
	}

	exit = NOTHING;
	if (online(player) == 1) {
		init_match(descr, player, "connect", TYPE_EXIT, &md);	/* match for connect */
		md.match_level = 1;
		match_all_exits(&md);
		exit = match_result(&md);
		if (exit == AMBIGUOUS)
			exit = NOTHING;
	}

	if (exit == NOTHING || !(FLAGS(exit) & STICKY)) {
		if (can_move(descr, player, tp_autolook_cmd, 1)) {
			do_move(descr, player, tp_autolook_cmd, 1);
		} else {
			do_look_around(descr, player);
		}
	}


	/*
	 * See if there's a connect action.  If so, and the player is the first to
	 * connect, send the player through it.  If the connect action is set
	 * sticky, then suppress the normal look-around.
	 */

	if (exit != NOTHING)
		do_move(descr, player, "connect", 1);

	if (online(player) == 1) {
		announce_puppets(player, "wakes up.", "_/pcon");
	}

	/* queue up all _connect programs referred to by properties */
	envpropqueue(descr, player, getloc(player), NOTHING, player, NOTHING,
				 "_connect", "Connect", 1, 1);
	envpropqueue(descr, player, getloc(player), NOTHING, player, NOTHING,
				 "_oconnect", "Oconnect", 1, 0);

	ts_useobject(player);
	return;
}

void
announce_disconnect(struct descriptor_data *d)
{
	dbref player = d->player;
	dbref loc;
	char buf[BUFFER_LEN];
	int dcount;

	if ((loc = getloc(player)) == NOTHING)
		return;

	if (tp_rwho) {
		snprintf(buf, sizeof(buf), "%d@%s", player, tp_muckname);
		rwhocli_userlogout(buf);
	}

	get_player_descrs(d->player, &dcount);
	if (dcount < 2 && dequeue_prog(player, 2))
		notify(player, "Foreground program aborted.");

	if ((!Dark(player)) && (!Dark(loc))) {
		snprintf(buf, sizeof(buf), "%s has disconnected.", PNAME(player));
		notify_except(DBFETCH(loc)->contents, player, buf, player);
	}

	/* trigger local disconnect action */
	if (online(player) == 1) {
		if (can_move(d->descriptor, player, "disconnect", 1)) {
			do_move(d->descriptor, player, "disconnect", 1);
		}
		announce_puppets(player, "falls asleep.", "_/pdcon");
	}
	gui_dlog_closeall_descr(d->descriptor);

	d->connected = 0;
	d->player = NOTHING;

    forget_player_descr(player, d->descriptor);
    update_desc_count_table();

	/* queue up all _connect programs referred to by properties */
	envpropqueue(d->descriptor, player, getloc(player), NOTHING, player, NOTHING,
				 "_disconnect", "Disconnect", 1, 1);
	envpropqueue(d->descriptor, player, getloc(player), NOTHING, player, NOTHING,
				 "_odisconnect", "Odisconnect", 1, 0);

	ts_lastuseobject(player);
	DBDIRTY(player);
}

#ifdef MUD_ID
#include <pwd.h>
void
do_setuid(char *name)
{
	struct passwd *pw;

	if ((pw = getpwnam(name)) == NULL) {
		log_status("can't get pwent for %s\n", name);
		exit(1);
	}
	if (setuid(pw->pw_uid) == -1) {
		log_status("can't setuid(%d): ", pw->pw_uid);
		perror("setuid");
		exit(1);
	}
}
#endif							/* MUD_ID */


#ifdef MUD_GID
#include <grp.h>
void
do_setgid(char *name)
{
	struct group *gr;

	if ((gr = getgrnam(name)) == NULL) {
		log_status("can't get grent for group %s\n", name);
		exit(1);
	}
	if (setgid(gr->gr_gid) == -1) {
		log_status("can't setgid(%d): ",gr->gr_gid);
		perror("setgid");
		exit(1);
	}
}
#endif							/* MUD_GID */


/***** O(1) Connection Optimizations *****/
struct descriptor_data *descr_count_table[FD_SETSIZE];
int current_descr_count = 0;

void
init_descr_count_lookup()
{
	int i;
	for (i = 0; i < FD_SETSIZE; i++) {
		descr_count_table[i] = NULL;
	}
}

void
update_desc_count_table()
{
	int c;
	struct descriptor_data *d;

	current_descr_count = 0;
	for (c = 0, d = descriptor_list; d; d = d->next)
	{
		if (d->connected)
		{
			d->con_number = c + 1;
			descr_count_table[c++] = d;
			current_descr_count++;
		}
	}
}

struct descriptor_data *
descrdata_by_count(int c)
{
	c--;
	if (c >= current_descr_count || c < 0) {
		return NULL;
	}
	return descr_count_table[c];
}

struct descriptor_data *descr_lookup_table[FD_SETSIZE];

#ifdef WIN32
int descr_hash_table[FD_SETSIZE];

int sethash_descr(int d) {
   for (int i = 0; i < FD_SETSIZE; i++) {
      if (descr_hash_table[i] == -1) { descr_hash_table[i] = d; return i; }
   }
   fprintf(stderr,"descr hash table full!", NULL); /* Should NEVER happen */
   return -1;
}

int gethash_descr(int d) {
   for (int i = 0; i < FD_SETSIZE; i++) {
      if (descr_hash_table[i] == d) return i;
   }
   fprintf(stderr,"descr hash value missing!", NULL); /* Should NEVER happen */
   return -1;
 
}

void unsethash_descr(int d) {
   for (int i = 0; i < FD_SETSIZE; i++) {
      if (descr_hash_table[i] == d) {
         descr_hash_table[i] = -1;
         return;
      }
   }
   fprintf(stderr,"descr hash value missing!", NULL); /* Should NEVER happen */
}
#endif

void
init_descriptor_lookup()
{
	int i;
#ifdef WIN32
	for (i = 0; i < FD_SETSIZE; i++) {
		descr_hash_table[i] = -1;
	}
#endif
	for (i = 0; i < FD_SETSIZE; i++) {
		descr_lookup_table[i] = NULL;
	}
}


int
index_descr(int index)
{
    if((index < 0) || (index >= FD_SETSIZE))
		return -1;
	if(descr_lookup_table[index] == NULL)
		return -1;
	return descr_lookup_table[index]->descriptor;
}


int*
get_player_descrs(dbref player, int* count)
{
	int* darr;

	if (Typeof(player) == TYPE_PLAYER) {
		*count = PLAYER_DESCRCOUNT(player);
	    darr = PLAYER_DESCRS(player);
		if (!darr) {
			*count = 0;
		}
		return darr;
	} else {
		*count = 0;
		return NULL;
	}
}

void
remember_player_descr(dbref player, int descr)
{
	int  count = 0;
	int* arr   = NULL;

	if (Typeof(player) != TYPE_PLAYER)
		return;

	count = PLAYER_DESCRCOUNT(player);
	arr = PLAYER_DESCRS(player);

	if (!arr) {
		arr = (int*)malloc(sizeof(int));
		arr[0] = descr;
		count = 1;
	} else {
		arr = (int*)realloc(arr,sizeof(int) * (count+1));
		arr[count] = descr;
		count++;
	}
	PLAYER_SET_DESCRCOUNT(player, count);
	PLAYER_SET_DESCRS(player, arr);
}

void
forget_player_descr(dbref player, int descr)
{
	int  count = 0;
	int* arr   = NULL;

	if (Typeof(player) != TYPE_PLAYER)
		return;

	count = PLAYER_DESCRCOUNT(player);
	arr = PLAYER_DESCRS(player);

	if (!arr) {
		count = 0;
	} else if (count > 1) {
		int src, dest;
		for (src = dest = 0; src < count; src++) {
			if (arr[src] != descr) {
				if (src != dest) {
					arr[dest] = arr[src];
				}
				dest++;
			}
		}
		if (dest != count) {
			count = dest;
			arr = (int*)realloc(arr,sizeof(int) * count);
		}
	} else {
		free((void*)arr);
		arr = NULL;
		count = 0;
	}
	PLAYER_SET_DESCRCOUNT(player, count);
	PLAYER_SET_DESCRS(player, arr);
}

void
remember_descriptor(struct descriptor_data *d)
{
	if (d) {
#ifdef WIN32
		descr_lookup_table[sethash_descr(d->descriptor)] = d;
#else
		descr_lookup_table[d->descriptor] = d;
#endif
	}
}

void
forget_descriptor(struct descriptor_data *d)
{
	if (d) {
#ifdef WIN32
		unsethash_descr(d->descriptor);
#else
		descr_lookup_table[d->descriptor] = NULL;
#endif
	}
}

struct descriptor_data *
lookup_descriptor(int c)
{
#ifdef WIN32
	if ( c < 0 ) return NULL;
	return descr_lookup_table[gethash_descr(c)];
#else 
	if (c >= FD_SETSIZE || c < 0) {
		return NULL;
	}
	return descr_lookup_table[c];
#endif
}

struct descriptor_data *
descrdata_by_descr(int i)
{
	return lookup_descriptor(i);
}


/*** JME ***/
int
online(dbref player)
{
	return PLAYER_DESCRCOUNT(player);
}

int
pcount(void)
{
    return current_descr_count;
}

int
pidle(int c)
{
	struct descriptor_data *d;
	time_t now;

	d = descrdata_by_count(c);

	(void) time(&now);
	if (d) {
		return (now - d->last_time);
	}

	return -1;
}

int
pdescridle(int c)
{
	struct descriptor_data *d;
	time_t now;

	d = descrdata_by_descr(c);

	(void) time(&now);
	if (d) {
		return (now - d->last_time);
	}

	return -1;
}

dbref
pdbref(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_count(c);

	if (d) {
		return (d->player);
	}

	return NOTHING;
}

dbref
pdescrdbref(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_descr(c);

	if (d) {
		return (d->player);
	}

	return NOTHING;
}

int
pontime(int c)
{
	struct descriptor_data *d;
	time_t now;

	d = descrdata_by_count(c);

	(void) time(&now);
	if (d) {
		return (now - d->connected_at);
	}

	return -1;
}

int
pdescrontime(int c)
{
	struct descriptor_data *d;
	time_t now;

	d = descrdata_by_descr(c);

	(void) time(&now);
	if (d) {
		return (now - d->connected_at);
	}
	return -1;
}

char *
phost(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_count(c);

	if (d) {
		return ((char *) d->hostname);
	}

	return (char *) NULL;
}

char *
pdescrhost(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_descr(c);

	if (d) {
		return ((char *) d->hostname);
	}

	return (char *) NULL;
}

char *
puser(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_count(c);

	if (d) {
		return ((char *) d->username);
	}

	return (char *) NULL;
}

char *
pdescruser(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_descr(c);

	if (d) {
		return ((char *) d->username);
	}

	return (char *) NULL;
}

/*** Foxen ***/
int
least_idle_player_descr(dbref who)
{
	struct descriptor_data *d;
	struct descriptor_data *best_d = NULL;
	int dcount, di;
	int* darr;
	long best_time = 0;

	darr = get_player_descrs(who, &dcount);
	for (di = 0; di < dcount; di++) {
		d = descrdata_by_descr(darr[di]);
		if (d && (!best_time || d->last_time > best_time)) {
			best_d = d;
			best_time = d->last_time;
		}
	}
	if (best_d) {
		return best_d->con_number;
	}
	return 0;
}


int
most_idle_player_descr(dbref who)
{
	struct descriptor_data *d;
	struct descriptor_data *best_d = NULL;
	int dcount, di;
	int* darr;
	long best_time = 0;

	darr = get_player_descrs(who, &dcount);
	for (di = 0; di < dcount; di++) {
		d = descrdata_by_descr(darr[di]);
		if (d && (!best_time || d->last_time < best_time)) {
			best_d = d;
			best_time = d->last_time;
		}
	}
	if (best_d) {
		return best_d->con_number;
	}
	return 0;
}


void
pboot(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_count(c);

	if (d) {
		process_output(d);
		d->booted = 1;
		/* shutdownsock(d); */
	}
}

int 
pdescrboot(int c)
{
    struct descriptor_data *d;

    d = descrdata_by_descr(c);

    if (d) {
		process_output(d);
		d->booted = 1;
		/* shutdownsock(d); */
		return 1;
    }
	return 0;
}


void
pnotify(int c, char *outstr)
{
	struct descriptor_data *d;

	d = descrdata_by_count(c);

	if (d) {
		queue_ansi(d, outstr);
		queue_write(d, "\r\n", 2);
	}
}


int
pdescrnotify(int c, char *outstr)
{
	struct descriptor_data *d;

	d = descrdata_by_descr(c);

	if (d) {
		queue_ansi(d, outstr);
		queue_write(d, "\r\n", 2);
		return 1;
	}
	return 0;
}


int
pdescr(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_count(c);

	if (d) {
		return (d->descriptor);
	}

	return -1;
}


int 
pdescrcount(void)
{
    return current_descr_count;
}


int 
pfirstdescr(void)
{
    struct descriptor_data *d;

	d = descrdata_by_count(1);
    if (d) {
		return d->descriptor;
	}

	return 0;
}


int 
plastdescr(void)
{
    struct descriptor_data *d;

	d = descrdata_by_count(current_descr_count);
	if (d) {
		return d->descriptor;
	}
	return 0;
}


int
pnextdescr(int c)
{
	struct descriptor_data *d;

    d = descrdata_by_descr(c);
	if (d) {
		d = d->next;
	}
	while (d && (!d->connected))
		d = d->next;
	if (d) {
		return (d->descriptor);
	}
	return (0);
}


int
pdescrcon(int c)
{
	struct descriptor_data *d;

    d = descrdata_by_descr(c);
	if (d) {
		return d->con_number;
	} else {
		return 0;
	}
}


int
pset_user(int c, dbref who)
{
	struct descriptor_data *d;
	static int setuser_depth = 0;

	if (++setuser_depth > 8) {
		/* Prevent infinite loops */
		setuser_depth--;
		return 0;
	}

    d = descrdata_by_descr(c);
	if (d && d->connected) {
		announce_disconnect(d);
		if (who != NOTHING) {
			d->player = who;
			d->connected = 1;
			update_desc_count_table();
            remember_player_descr(who, d->descriptor);
			announce_connect(d->descriptor, who);
		}
		setuser_depth--;
		return 1;
	}
	setuser_depth--;
	return 0;
}


int
dbref_first_descr(dbref c)
{
	int dcount;
	int* darr;

	darr = get_player_descrs(c, &dcount);
	if (dcount > 0) {
		return darr[dcount - 1];
	} else {
		return -1;
	}
}


McpFrame *
descr_mcpframe(int c)
{
	struct descriptor_data *d;

    d = descrdata_by_descr(c);
	if (d) {
		return &d->mcpframe;
	}
	return NULL;
}


int
pdescrflush(int c)
{
	struct descriptor_data *d;
	int i = 0;

	if (c != -1) {
		d = descrdata_by_descr(c);
		if (d) {
			if (!process_output(d)) {
				d->booted = 1;
			}
			i++;
		}
	} else {
		for (d = descriptor_list; d; d = d->next) {
			if (!process_output(d)) {
				d->booted = 1;
			}
			i++;
		}
	}
	return i;
}

int
pdescrsecure(int c)
{
#ifdef USE_SSL
	struct descriptor_data *d;

	d = descrdata_by_descr(c);

	if (d && d->ssl_session)
		return 1;
	else
		return 0;
#else
	return 0;
#endif
}

int
pdescrbufsize(int c)
{
	struct descriptor_data *d;

	d = descrdata_by_descr(c);

	if (d) {
		return (tp_max_output - d->output_size);
	}

	return -1;
}

dbref
partial_pmatch(const char *name)
{
	struct descriptor_data *d;
	dbref last = NOTHING;

	d = descriptor_list;
	while (d) {
		if (d->connected && (last != d->player) && string_prefix(NAME(d->player), name)) {
			if (last != NOTHING) {
				last = AMBIGUOUS;
				break;
			}
			last = d->player;
		}
		d = d->next;
	}
	return (last);
}


void
update_rwho(void)
{
	struct descriptor_data *d;
	char buf[BUFFER_LEN];

	rwhocli_pingalive();
	d = descriptor_list;
	while (d) {
		if (d->connected) {
			snprintf(buf, sizeof(buf), "%d@%s", d->player, tp_muckname);
			rwhocli_userlogin(buf, NAME(d->player), d->connected_at);
		}
		d = d->next;
	}
}


void
welcome_user(struct descriptor_data *d)
{
	FILE *f;
	char *ptr;
	char buf[BUFFER_LEN];

	if ((f = fopen(WELC_FILE, "r")) == NULL) {
		queue_ansi(d, DEFAULT_WELCOME_MESSAGE);
		perror("spit_file: welcome.txt");
	} else {
		while (fgets(buf, sizeof(buf) - 3, f)) {
			ptr = index(buf, '\n');
			if (ptr && ptr > buf && *(ptr - 1) != '\r') {
				*ptr++ = '\r';
				*ptr++ = '\n';
				*ptr++ = '\0';
			}
			queue_ansi(d, buf);
		}
		fclose(f);
	}
	if (wizonly_mode) {
		queue_ansi(d, "## The game is currently in maintenance mode, and only wizards will be able to connect.\r\n");
	} else if (tp_playermax && con_players_curr >= tp_playermax_limit) {
		if (tp_playermax_warnmesg && *tp_playermax_warnmesg) {
			queue_ansi(d, tp_playermax_warnmesg);
			queue_string(d, "\r\n");
		}
	}
}

void
dump_status(void)
{
	struct descriptor_data *d;
	time_t now;
	char buf[BUFFER_LEN];

	(void) time(&now);
	log_status("STATUS REPORT:\n");
	for (d = descriptor_list; d; d = d->next) {
		if (d->connected) {
			snprintf(buf, sizeof(buf), "PLAYING descriptor %d player %s(%d) from host %s(%s), %s.\n",
					d->descriptor, NAME(d->player), d->player, d->hostname, d->username,
					(d->last_time) ? "idle %d seconds" : "never used");
		} else {
			snprintf(buf, sizeof(buf), "CONNECTING descriptor %d from host %s(%s), %s.\n",
					d->descriptor, d->hostname, d->username,
					(d->last_time) ? "idle %d seconds" : "never used");
		}
		log_status(buf, now - d->last_time);
	}
}

#ifdef USE_SSL
ssize_t socket_read(struct descriptor_data *d, void *buf, size_t count) {
	int i;
 
	if (! d->ssl_session) {
		return read(d->descriptor, buf, count);
	} else {
		i = SSL_read(d->ssl_session, buf, count);
		if ( i < 0 ) {
			i = SSL_get_error(d->ssl_session, i);
			if ( (i == SSL_ERROR_WANT_READ) || (i == SSL_ERROR_WANT_WRITE) ) {
				/* log_status("SSL read: Return wouldblock.\n", "version"); */
#ifndef WIN32
 				errno = EWOULDBLOCK;
#else
				WSASetLastError(WSAEWOULDBLOCK);
#endif
				return -1;
			} else {
				/* log_status("SSL read: Return EBADF.\n", "version"); */
#ifndef WIN32
				errno = EBADF;
#endif
				return -1;
			}
		}
		return i;
	}
}
 
ssize_t socket_write(struct descriptor_data *d, const void *buf, size_t count) {
	int i;

	if (! d->ssl_session) {
		return write(d->descriptor, buf, count);
	} else {
		i = SSL_write(d->ssl_session, buf, count);
		if ( i < 0 ) {
			i = SSL_get_error(d->ssl_session, i);
			if ( (i == SSL_ERROR_WANT_READ) || (i == SSL_ERROR_WANT_WRITE) ) {
				/* log_status("SSL write: Return wouldblock.\n", "version"); */
#ifndef WIN32
 				errno = EWOULDBLOCK;
#else
				WSASetLastError(WSAEWOULDBLOCK);
#endif
				return -1;
			} else { 
				/* log_status("SSL write: Return EBADF.\n", "version"); */
#ifndef WIN32
				errno = EBADF;
#endif
				return -1;
			}
		}
		return i;
	}
}
#endif

/* Ignore support -- Could do with moving into its own file */

static int ignore_is_ignoring_sub(dbref Player, dbref Who)
{
	int Top, Bottom;
	dbref* List;

	if (!tp_ignore_support)
		return 0;

	if ((Player < 0) || (Player >= db_top) || (Typeof(Player) == TYPE_GARBAGE))
		return 0;

	if ((Who < 0) || (Who >= db_top) || (Typeof(Who) == TYPE_GARBAGE))
		return 0;

	Player	= OWNER(Player);
	Who		= OWNER(Who);

	/* You can't ignore yourself, or an unquelled wizard, */
	/* and unquelled wizards can ignore no one. */
	if ((Player == Who) || (Wizard(Player)) || (Wizard(Who))) 
		return 0;

	if (PLAYER_IGNORE_LAST(Player) == AMBIGUOUS)
		return 0;

	/* Ignore the last player ignored without bothering to look them up */
	if (PLAYER_IGNORE_LAST(Player) == Who)
		return 1;

	if ((PLAYER_IGNORE_CACHE(Player) == NULL) && !ignore_prime_cache(Player))
		return 0;

	Top		= 0;
	Bottom	= PLAYER_IGNORE_COUNT(Player);
	List	= PLAYER_IGNORE_CACHE(Player);

	while(Top < Bottom)
	{
		int Middle = Top + (Bottom - Top) / 2;

		if (List[Middle] == Who)
			break;

		if (List[Middle] < Who)
			Top		= Middle + 1;
		else
			Bottom	= Middle;
	}

	if (Top >= Bottom)
		return 0;

	PLAYER_SET_IGNORE_LAST(Player, Who);
	
	return 1;
}

int ignore_is_ignoring(dbref Player, dbref Who)
{
	return ignore_is_ignoring_sub(Player, Who) || (tp_ignore_bidirectional && ignore_is_ignoring_sub(Who, Player));
}

static int ignore_dbref_compare(const void* Lhs, const void* Rhs)
{
	return *(dbref*)Lhs - *(dbref*)Rhs;
}

int ignore_prime_cache(dbref Player)
{
	const char* Txt = 0;
	const char* Ptr = 0;
	dbref* List = 0;
	int Count = 0;
	int i;

	if (!tp_ignore_support)
		return 0;

	if ((Player < 0) || (Player >= db_top) || (Typeof(Player) != TYPE_PLAYER))
		return 0;

	if ((Txt = get_uncompress(get_property_class(Player, IGNORE_PROP))) == NULL)
	{
		PLAYER_SET_IGNORE_LAST(Player, AMBIGUOUS);
		return 0;
	}

	while(*Txt && isspace(*Txt))
		Txt++;

	if (*Txt == '\0')
	{
		PLAYER_SET_IGNORE_LAST(Player, AMBIGUOUS);
		return 0;
	}

	for(Ptr = Txt; *Ptr; )
	{
		Count++;

		if (*Ptr == '#')
			Ptr++;

		while(*Ptr && !isspace(*Ptr))
			Ptr++;

		while(*Ptr && isspace(*Ptr))
			Ptr++;
	}

	if ((List = (dbref*)malloc(sizeof(dbref) * Count)) == 0)
		return 0;

	for(Ptr = Txt, i = 0; *Ptr; )
	{
		if (*Ptr == '#')
			Ptr++;

		if (isdigit(*Ptr))
			List[i++] = atoi(Ptr);
		else
			List[i++] = NOTHING;

		while(*Ptr && !isspace(*Ptr))
			Ptr++;

		while(*Ptr && isspace(*Ptr))
			Ptr++;
	}

	qsort(List, Count, sizeof(dbref), ignore_dbref_compare);

	PLAYER_SET_IGNORE_CACHE(Player, List);
	PLAYER_SET_IGNORE_COUNT(Player, Count);

	return 1;
}

void ignore_flush_cache(dbref Player)
{
	if ((Player < 0) || (Player >= db_top) || (Typeof(Player) != TYPE_PLAYER))
		return;

	if (PLAYER_IGNORE_CACHE(Player))
	{
		free(PLAYER_IGNORE_CACHE(Player));
		PLAYER_SET_IGNORE_CACHE(Player, NULL);
		PLAYER_SET_IGNORE_COUNT(Player, 0);
	}

	PLAYER_SET_IGNORE_LAST(Player, NOTHING);
}

void ignore_flush_all_cache()
{
	int i;

	/* Don't touch the database if it's not been loaded yet... */
	if (db == 0)
		return;
	
	for(i = 0; i < db_top; i++)
	{
		if (Typeof(i) == TYPE_PLAYER)
		{
			if (PLAYER_IGNORE_CACHE(i))
			{
				free(PLAYER_IGNORE_CACHE(i));
				PLAYER_SET_IGNORE_CACHE(i, NULL);
				PLAYER_SET_IGNORE_COUNT(i, 0);
			}

			PLAYER_SET_IGNORE_LAST(i, NOTHING);
		}
	}
}

void ignore_add_player(dbref Player, dbref Who)
{
	if (!tp_ignore_support)
		return;

	if ((Player < 0) || (Player >= db_top) || (Typeof(Player) == TYPE_GARBAGE))
		return;

	if ((Who < 0) || (Who >= db_top) || (Typeof(Who) == TYPE_GARBAGE))
		return;

	reflist_add(OWNER(Player), IGNORE_PROP, OWNER(Who));

	ignore_flush_cache(OWNER(Player));
}

void ignore_remove_player(dbref Player, dbref Who)
{
	if (!tp_ignore_support)
		return;

	if ((Player < 0) || (Player >= db_top) || (Typeof(Player) == TYPE_GARBAGE))
		return;

	if ((Who < 0) || (Who >= db_top) || (Typeof(Who) == TYPE_GARBAGE))
		return;

	reflist_del(OWNER(Player), IGNORE_PROP, OWNER(Who));

	ignore_flush_cache(OWNER(Player));
}

void ignore_remove_from_all_players(dbref Player)
{
	int i;

	if (!tp_ignore_support)
		return;

	for(i = 0; i < db_top; i++)
		if (Typeof(i) == TYPE_PLAYER)
			reflist_del(i, IGNORE_PROP, Player);

	ignore_flush_all_cache();
}