--- /dev/null
+/**
+ * \file console.c
+ *
+ * \brief Alternative main function for PennMUSH which runs on the console
+ * instead of over the network.
+ */
+
+#include "copyrite.h"
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef I_SYS_TYPES
+#include <sys/types.h>
+#endif
+#ifdef WIN32
+#define FD_SETSIZE 256
+#include <windows.h>
+#include <winsock.h>
+#include <io.h>
+#define EINTR WSAEINTR
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define MAXHOSTNAMELEN 32
+#define LC_MESSAGES 6
+#else /* !WIN32 */
+#ifdef I_SYS_FILE
+#include <sys/file.h>
+#endif
+#ifdef I_SYS_TIME
+#include <sys/time.h>
+#endif
+#include <sys/ioctl.h>
+#include <errno.h>
+#ifdef I_SYS_SOCKET
+#include <sys/socket.h>
+#endif
+#ifdef I_NETINET_IN
+#include <netinet/in.h>
+#endif
+#ifdef I_NETDB
+#include <netdb.h>
+#endif
+#ifdef I_SYS_PARAM
+#include <sys/param.h>
+#endif
+#ifdef I_SYS_STAT
+#include <sys/stat.h>
+#endif
+#endif /* !WIN32 */
+#include <time.h>
+#ifdef I_SYS_WAIT
+#include <sys/wait.h>
+#endif
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef I_SYS_SELECT
+#include <sys/select.h>
+#endif
+#ifdef I_UNISTD
+#include <unistd.h>
+#endif
+#ifdef HAS_GETRLIMIT
+#include <sys/resource.h>
+#endif
+#include <limits.h>
+#ifdef I_FLOATINGPOINT
+#include <floatingpoint.h>
+#endif
+#include <locale.h>
+#ifdef __APPLE__
+#define LC_MESSAGES 6
+#define AUTORESTART
+#endif
+#include <setjmp.h>
+
+#include "conf.h"
+
+#include "externs.h"
+#include "chunk.h"
+#include "mushdb.h"
+#include "dbdefs.h"
+#include "flags.h"
+#include "lock.h"
+#include "help.h"
+#include "match.h"
+#include "ansi.h"
+#include "pueblo.h"
+#include "parse.h"
+#include "access.h"
+#include "command.h"
+#include "version.h"
+#include "patches.h"
+#include "mysocket.h"
+#include "ident.h"
+#include "strtree.h"
+#include "log.h"
+#include "pcre.h"
+#include "mymalloc.h"
+#include "extmail.h"
+#include "attrib.h"
+#include "game.h"
+#include "dbio.h"
+#include "confmagic.h"
+#ifdef HAS_WAITPID
+/** What does wait*() return? */
+#define WAIT_TYPE int
+#else
+#ifdef UNION_WAIT
+#define WAIT_TYPE union wait
+#else
+#define WAIT_TYPE int
+#endif
+#endif
+
+
+/* BSD 4.2 and maybe some others need these defined */
+#ifndef FD_ZERO
+/** An fd_set is 4 bytes */
+#define fd_set int
+/** Clear an fd_set */
+#define FD_ZERO(p) (*p = 0)
+/** Set a bit in an fd_set */
+#define FD_SET(n,p) (*p |= (1<<(n)))
+/** Clear a bit in an fd_set */
+#define FD_CLR(n,p) (*p &= ~(1<<(n)))
+/** Check a bit in an fd_set */
+#define FD_ISSET(n,p) (*p & (1<<(n)))
+#endif /* defines for BSD 4.2 */
+
+#ifdef HAS_GETRUSAGE
+void rusage_stats(void);
+#endif
+int que_next(void); /* from cque.c */
+
+void dispatch(void); /* from timer.c */
+dbref email_register_player(const char *name, const char *email, const char *host, const char *ip); /* from player.c */
+
+static int extrafd;
+int shutdown_flag = 0; /**< Is it time to shut down? */
+#ifdef CHAT_SYSTEM
+void chat_player_announce(dbref player, char *msg, int ungag);
+#endif /* CHAT_SYSTEM */
+
+static int login_number = 0;
+static int under_limit = 1;
+
+char cf_motd_msg[BUFFER_LEN]; /**< The message of the day */
+char cf_downmotd_msg[BUFFER_LEN]; /**< The down message */
+char cf_fullmotd_msg[BUFFER_LEN]; /**< The 'mush full' message */
+static char poll_msg[DOING_LEN];
+char confname[BUFFER_LEN]; /**< Name of the config file */
+char errlog[BUFFER_LEN]; /**< Name of the error log file */
+
+/* Default Connection flags for certain clients
+ */
+static CLIENT_DEFAULTS client_maps[] = {
+ {"TINYFUGUE", CONN_PROMPT},
+ {NULL, -1}
+};
+
+
+/** Is this descriptor connected to a telnet-compatible terminal? */
+#define TELNET_ABLE(d) ((d)->conn_flags & (CONN_TELNET | CONN_TELNET_QUERY))
+
+
+/* When the mush gets a new connection, it tries sending a telnet
+ * option negotiation code for setting client-side line-editing mode
+ * to it. If it gets a reply, a flag in the descriptor struct is
+ * turned on indicated telnet-awareness.
+ *
+ * If the reply indicates that the client supports linemode, further
+ * instructions as to what linemode options are to be used is sent.
+ * Those options: Client-side line editing, and expanding literal
+ * client-side-entered tabs into spaces.
+ *
+ * Option negotation requests sent by the client are processed,
+ * with the only one we confirm rather than refuse outright being
+ * suppress-go-ahead, since a number of telnet clients try it.
+ *
+ * The character 255 is the telnet option escape character, so when it
+ * is sent to a telnet-aware client by itself (Since it's also often y-umlaut)
+ * it must be doubled to escape it for the client. This is done automatically,
+ * and is the original purpose of adding telnet option support.
+ */
+
+/* Telnet codes */
+#define IAC 255 /**< interpret as command: */
+#define GOAHEAD 249 /**< Go Ahead command */
+#define NOP 241 /**< no operation */
+#define AYT 246 /**< are you there? */
+#define DONT 254 /**< you are not to use option */
+#define DO 253 /**< please, you use option */
+#define WONT 252 /**< I won't use option */
+#define WILL 251 /**< I will use option */
+#define SB 250 /**< interpret as subnegotiation */
+#define SE 240 /**< end sub negotiation */
+#define TN_SGA 3 /**< Suppress go-ahead */
+#define TN_LINEMODE 34 /**< Line mode */
+#define TN_NAWS 31 /**< Negotiate About Window Size */
+#define TN_TTYPE 24 /**< Ask for termial type information */
+static void test_telnet(DESC *d);
+static void setup_telnet(DESC *d);
+static int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend);
+static const char *empabb(dbref);
+static int do_su_exit(DESC *d);
+
+static const char *create_fail =
+ "Either there is already a player with that name, or that name is illegal.";
+static const char *password_fail = "The password is invalid (or missing).";
+static const char *register_fail =
+ "Unable to register that player with that email address.";
+static const char *register_success =
+ "Registration successful! You will receive your password by email.";
+static const char *shutdown_message = "Going down - Bye";
+/** Where we save the descriptor info across reboots. */
+#define REBOOTFILE "reboot.db"
+
+#if 0
+/* For translation */
+static void dummy_msgs(void);
+static void
+dummy_msgs()
+{
+ char *temp;
+ temp = T("Either that player does not exist, or has a different password.");
+ temp =
+ T
+ ("Either there is already a player with that name, or that name is illegal.");
+ temp = T("The password is invalid (or missing).");
+ temp = T("Unable to register that player with that email address.");
+ temp = T("Registration successful! You will receive your password by email.");
+ temp = T("Going down - Bye");
+ temp = T("GAME: SSL connections must be dropped, sorry.");
+}
+
+#endif
+
+DESC *descriptor_list = NULL; /**< The linked list of descriptors */
+
+#ifdef WIN32
+static WSADATA wsadata;
+#endif
+int restarting = 0; /**< Are we restarting the server after a reboot? */
+int ndescriptors = 0;
+
+extern const unsigned char *tables;
+
+sig_atomic_t signal_shutdown_flag = 0; /**< Have we caught a shutdown signal? */
+sig_atomic_t signal_dump_flag = 0; /**< Have we caught a dump signal? */
+
+#ifdef HAS_GETRLIMIT
+static void init_rlimit(void);
+#endif
+#ifndef BOOLEXP_DEBUGGING
+#ifdef WIN32SERVICES
+void shutdown_checkpoint(void);
+void mainthread(int argc, char **argv);
+#else
+int main(int argc, char **argv);
+#endif
+#endif
+void set_signals(void);
+static struct timeval *timeval_sub(struct timeval *now, struct timeval *then);
+#ifdef WIN32
+/** Windows doesn't have gettimeofday(), so we implement it here */
+#define our_gettimeofday(now) win_gettimeofday((now))
+static void win_gettimeofday(struct timeval *now);
+#else
+/** A wrapper for gettimeofday() in case the system doesn't have it */
+#define our_gettimeofday(now) gettimeofday((now), (struct timezone *)NULL)
+#endif
+static long int msec_diff(struct timeval *now, struct timeval *then);
+static struct timeval *msec_add(struct timeval *t, int x);
+static void update_quotas(struct timeval *last, struct timeval *current);
+
+static void shovechars(void);
+
+static void clearstrings(DESC *d);
+
+/** A block of cached text. */
+typedef struct fblock {
+ unsigned char *buff; /**< Pointer to the block as a string */
+ size_t len; /**< Length of buff */
+} FBLOCK;
+
+/** The complete collection of cached text files. */
+struct fcache_entries {
+ FBLOCK connect_fcache[2]; /**< connect.txt and connect.html */
+ FBLOCK motd_fcache[2]; /**< motd.txt and motd.html */
+ FBLOCK newuser_fcache[2]; /**< newuser.txt and newuser.html */
+ FBLOCK register_fcache[2]; /**< register.txt and register.html */
+ FBLOCK quit_fcache[2]; /**< quit.txt and quit.html */
+ FBLOCK down_fcache[2]; /**< down.txt and down.html */
+ FBLOCK full_fcache[2]; /**< full.txt and full.html */
+ FBLOCK guest_fcache[2]; /**< guest.txt and guest.html */
+};
+
+void feed_snoop(DESC *, const char *, char );
+char is_snooped(DESC *);
+char set_snoop(dbref, DESC *);
+void clr_snoop(dbref, DESC *);
+void announce_connect(dbref player, int isnew, int num);
+void announce_disconnect(dbref player);
+void add_to_exit_path(DESC *d, dbref player);
+
+static struct fcache_entries fcache;
+static void fcache_dump(DESC *d, FBLOCK fp[2], const unsigned char *prefix);
+static int fcache_read(FBLOCK *cp, const char *filename);
+static void logout_sock(DESC *d);
+static void shutdownsock(DESC *d);
+static DESC *initializesock(int s, char *addr, char *ip, int use_ssl);
+int process_output(DESC *d);
+/* Notify.c */
+extern void free_text_block(struct text_block *t);
+extern void add_to_queue(struct text_queue *q, const unsigned char *b, int n);
+extern int queue_write(DESC *d, const unsigned char *b, int n);
+extern int queue_eol(DESC *d);
+extern int queue_newwrite(DESC *d, const unsigned char *b, int n);
+extern int queue_string(DESC *d, const char *s);
+extern int queue_string_eol(DESC *d, const char *s);
+extern void freeqs(DESC *d);
+static void welcome_user(DESC *d);
+static void dump_info(DESC *call_by);
+static void save_command(DESC *d, const unsigned char *command);
+static int process_input(DESC *d, int output_ready);
+static void process_input_helper(DESC *d, char *tbuf1, int got);
+static void set_userstring(unsigned char **userstring, const char *command);
+static void process_commands(void);
+static void parse_puebloclient(DESC *d, char *command);
+static int dump_messages(DESC *d, dbref player, int new);
+static int check_connect(DESC *d, const char *msg);
+static void parse_connect(const char *msg, char *command, char *user,
+ char *pass);
+static void close_sockets(void);
+dbref find_player_by_desc(int port);
+static DESC *lookup_desc(dbref executor, const char *name);
+void NORETURN bailout(int sig);
+void WIN32_CDECL signal_shutdown(int sig);
+void WIN32_CDECL signal_dump(int sig);
+void reaper(int sig);
+extern Pid_t forked_dump_pid; /**< Process id of forking dump process */
+static void dump_users(DESC *call_by, char *match, int doing);
+static const char *time_format_1(long int dt);
+static const char *time_format_2(long int dt);
+
+void inactivity_check(void);
+void reopen_logs(void);
+void load_reboot_db(void);
+#ifdef HAS_GETRLIMIT
+static void
+init_rlimit(void)
+{
+ /* Unlimit file descriptors. */
+ /* Ultrix 4.4 and others may have getrlimit but may not be able to
+ * change number of file descriptors
+ */
+#ifdef RLIMIT_NOFILE
+ struct rlimit *rlp;
+
+ rlp = (struct rlimit *) malloc(sizeof(struct rlimit));
+ if (getrlimit(RLIMIT_NOFILE, rlp)) {
+ perror("init_rlimit: getrlimit()");
+ free(rlp);
+ return;
+ }
+ /* This check seems dumb, but apparently FreeBSD may return 0 for
+ * the max # of descriptors!
+ */
+ if (rlp->rlim_max > rlp->rlim_cur) {
+ rlp->rlim_cur = rlp->rlim_max;
+ if (setrlimit(RLIMIT_NOFILE, rlp))
+ perror("init_rlimit: setrlimit()");
+ }
+ free(rlp);
+#endif
+ return;
+}
+#endif /* HAS_GETRLIMIT */
+
+#ifndef BOOLEXP_DEBUGGING
+#ifdef WIN32SERVICES
+/* Under WIN32, MUSH is a "service", so we just start a thread here.
+ * The real "main" is in win32/services.c
+ */
+void
+mainthread(int argc, char **argv)
+#else
+/** The main function.
+ * \param argc number of arguments.
+ * \param argv vector of arguments.
+ * \return exit code.
+ */
+int
+main(int argc, char **argv)
+#endif /* WIN32SERVICES */
+{
+#ifdef AUTORESTART
+ FILE *id;
+#endif
+ FILE *newerr;
+
+ /* read the configuration file */
+ if (argc < 2) {
+ fprintf(stderr, "ERROR: Usage: %s /path/to/config_file\n", argv[0]);
+ exit(2);
+ }
+
+#ifdef WIN32
+ {
+ unsigned short wVersionRequested = MAKEWORD(1, 1);
+ int err;
+
+ /* Need to include library: wsock32.lib for Windows Sockets */
+ err = WSAStartup(wVersionRequested, &wsadata);
+ if (err) {
+ printf(T("Error %i on WSAStartup\n"), err);
+ exit(1);
+ }
+ }
+#endif /* WIN32 */
+
+#ifdef HAS_GETRLIMIT
+ init_rlimit(); /* unlimit file descriptors */
+#endif
+
+ /* These are FreeBSDisms to fix floating point exceptions */
+#ifdef HAS_FPSETROUND
+ fpsetround(FP_RN);
+#endif
+#ifdef HAS_FPSETMASK
+ fpsetmask(0L);
+#endif
+
+ time(&mudtime);
+
+ /* If we have setlocale, call it to set locale info
+ * from environment variables
+ */
+#ifdef HAS_SETLOCALE
+ {
+ char *loc;
+ if ((loc = setlocale(LC_CTYPE, "")) == NULL)
+ do_rawlog(LT_ERR, "Failed to set ctype locale from environment.");
+ else
+ do_rawlog(LT_ERR, "Setting ctype locale to %s", loc);
+ if ((loc = setlocale(LC_TIME, "")) == NULL)
+ do_rawlog(LT_ERR, "Failed to set time locale from environment.");
+ else
+ do_rawlog(LT_ERR, "Setting time locale to %s", loc);
+ if ((loc = setlocale(LC_MESSAGES, "")) == NULL)
+ do_rawlog(LT_ERR, "Failed to set messages locale from environment.");
+ else
+ do_rawlog(LT_ERR, "Setting messages locale to %s", loc);
+ if ((loc = setlocale(LC_COLLATE, "")) == NULL)
+ do_rawlog(LT_ERR, "Failed to set collate locale from environment.");
+ else
+ do_rawlog(LT_ERR, "Setting collate locale to %s", loc);
+ }
+#endif
+#ifdef HAS_TEXTDOMAIN
+ textdomain("pennmush");
+#endif
+#ifdef HAS_BINDTEXTDOMAIN
+ bindtextdomain("pennmush", "../po");
+#endif
+
+ /* Build the locale-dependant tables used by PCRE */
+ tables = pcre_maketables();
+
+/* this writes a file used by the restart script to check for active mush */
+#ifdef AUTORESTART
+ id = fopen("runid", "w");
+ fprintf(id, "%d", getpid());
+ fclose(id);
+#endif
+
+ strncpy(confname, argv[1], BUFFER_LEN - 1);
+ confname[BUFFER_LEN - 1] = '\0';
+ init_game_config(confname);
+
+ /* save a file descriptor */
+ reserve_fd();
+#ifndef WIN32
+ extrafd = open("/dev/null", O_RDWR);
+#endif
+
+ /* decide if we're in @shutdown/reboot */
+ restarting = 0;
+ newerr = fopen(REBOOTFILE, "r");
+ if (newerr) {
+ restarting = 1;
+ fclose(newerr);
+ }
+
+ init_qids();
+ if (init_game_dbs() < 0) {
+ do_rawlog(LT_ERR, T("ERROR: Couldn't load databases! Exiting."));
+ exit(2);
+ }
+
+ init_game_postdb(confname);
+
+ globals.database_loaded = 1;
+
+ set_signals();
+
+ /* go do it */
+#ifdef CSRI
+#ifdef CSRI_DEBUG
+ mal_verify(1);
+#endif
+#ifdef CSRI_TRACE
+ mal_leaktrace(1);
+#endif
+#endif
+ load_reboot_db();
+ shovechars();
+#ifdef CSRI
+#ifdef CSRI_DEBUG
+ mal_verify(1);
+#endif
+#endif
+
+ /* someone has told us to shut down */
+#ifdef WIN32SERVICES
+ /* Keep service manager happy */
+ shutdown_checkpoint();
+#endif
+
+ shutdown_queues();
+
+#ifdef WIN32SERVICES
+ /* Keep service manager happy */
+ shutdown_checkpoint();
+#endif
+
+ close_sockets();
+ sql_shutdown();
+
+#ifdef WIN32SERVICES
+ /* Keep service manager happy */
+ shutdown_checkpoint();
+#endif
+
+ dump_database();
+
+ local_shutdown();
+
+#ifdef RPMODE_SYS
+ rplog_shutdown();
+#endif
+
+ end_all_logs();
+
+#ifdef CSRI
+#ifdef CSRI_PROFILESIZES
+ mal_statsdump(stderr);
+#endif
+#ifdef CSRI_TRACE
+ mal_dumpleaktrace(stderr);
+#endif
+ fflush(stderr);
+#endif
+
+#ifdef WIN32SERVICES
+ /* Keep service manager happy */
+ shutdown_checkpoint();
+#endif
+
+#ifdef HAS_GETRUSAGE
+ rusage_stats();
+#endif /* HAS_RUSAGE */
+
+ do_rawlog(LT_ERR, T("MUSH shutdown completed."));
+
+#ifdef WIN32
+#ifdef WIN32SERVICES
+ shutdown_checkpoint();
+#endif
+ WSACleanup(); /* clean up */
+#else
+#ifdef __APPLE__
+ unlink("runid");
+#endif
+ exit(0);
+#endif
+}
+#endif /* BOOLEXP_DEBUGGING */
+
+/** Close and reopen the logfiles - called on SIGHUP */
+void
+reopen_logs(void)
+{
+ FILE *newerr;
+ /* close up the log files */
+ end_all_logs();
+ newerr = fopen(errlog, "a");
+ if (!newerr) {
+ fprintf(stderr,
+ T("Unable to open %s. Error output continues to stderr.\n"),
+ errlog);
+ } else {
+ if (!freopen(errlog, "a", stderr)) {
+ printf(T("Ack! Failed reopening stderr!"));
+ exit(1);
+ }
+ setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
+ fclose(newerr);
+ }
+ start_all_logs();
+}
+
+/** Install our default signal handlers. */
+void
+set_signals(void)
+{
+
+#ifndef WIN32
+ /* we don't care about SIGPIPE, we notice it in select() and write() */
+ ignore_signal(SIGPIPE);
+ install_sig_handler(SIGUSR2, signal_dump);
+ install_sig_handler(SIGQUIT, signal_shutdown);
+ install_sig_handler(SIGINT, signal_shutdown);
+ install_sig_handler(SIGTERM, bailout);
+#else
+ /* Win32 stuff:
+ * No support for SIGUSR2 or SIGINT.
+ * SIGTERM is never generated on NT-based Windows (according to MSDN)
+ * MSVC++ will let you get away with installing a handler anyway,
+ * but VS.NET will not. So if it's MSVC++, we give it a try.
+ */
+#if _MSC_VER < 1200
+ install_sig_handler(SIGTERM, bailout);
+#endif
+#endif
+
+#ifndef WIN32
+ install_sig_handler(SIGCHLD, reaper);
+#endif
+
+}
+
+#ifdef WIN32
+/** Get the time using Windows function call.
+ * Looks weird, but it works. :-P
+ * \param now address to store timeval data.
+ */
+static void
+win_gettimeofday(struct timeval *now)
+{
+
+ FILETIME win_time;
+
+ GetSystemTimeAsFileTime(&win_time);
+ /* dwLow is in 100-s nanoseconds, not microseconds */
+ now->tv_usec = win_time.dwLowDateTime % 10000000 / 10;
+
+ /* dwLow contains at most 429 least significant seconds, since 32 bits maxint is 4294967294 */
+ win_time.dwLowDateTime /= 10000000;
+
+ /* Make room for the seconds of dwLow in dwHigh */
+ /* 32 bits of 1 = 4294967295. 4294967295 / 429 = 10011578 */
+ win_time.dwHighDateTime %= 10011578;
+ win_time.dwHighDateTime *= 429;
+
+ /* And add them */
+ now->tv_sec = win_time.dwHighDateTime + win_time.dwLowDateTime;
+}
+
+#endif
+
+/** Return the difference between two timeval structs as a timeval struct.
+ * \param now pointer to the timeval to subtract from.
+ * \param then pointer to the timeval to subtract.
+ * \return pointer to a statically allocated timeval of the difference.
+ */
+static struct timeval *
+timeval_sub(struct timeval *now, struct timeval *then)
+{
+ static struct timeval mytime;
+ mytime.tv_sec = now->tv_sec;
+ mytime.tv_usec = now->tv_usec;
+
+ mytime.tv_sec -= then->tv_sec;
+ mytime.tv_usec -= then->tv_usec;
+ if (mytime.tv_usec < 0) {
+ mytime.tv_usec += 1000000;
+ mytime.tv_sec--;
+ }
+ return &mytime;
+}
+
+/** Return the difference between two timeval structs in milliseconds.
+ * \param now pointer to the timeval to subtract from.
+ * \param then pointer to the timeval to subtract.
+ * \return milliseconds of difference between them.
+ */
+static long int
+msec_diff(struct timeval *now, struct timeval *then)
+{
+ long int secs = now->tv_sec - then->tv_sec;
+ if (secs == 0)
+ return (now->tv_usec - then->tv_usec) / 1000;
+ else if (secs == 1)
+ return (now->tv_usec + (1000000 - then->tv_usec)) / 100;
+ else if (secs > 1)
+ return (secs * 1000) + ((now->tv_usec + (1000000 - then->tv_usec)) / 1000);
+ else
+ return 0;
+}
+
+/** Add a given number of milliseconds to a timeval.
+ * \param t pointer to a timeval struct.
+ * \param x number of milliseconds to add to t.
+ * \return address of static timeval struct representing the sum.
+ */
+static struct timeval *
+msec_add(struct timeval *t, int x)
+{
+ static struct timeval mytime;
+ mytime.tv_sec = t->tv_sec;
+ mytime.tv_usec = t->tv_usec;
+ mytime.tv_sec += x / 1000;
+ mytime.tv_usec += (x % 1000) * 1000;
+ if (mytime.tv_usec >= 1000000) {
+ mytime.tv_sec += mytime.tv_usec / 1000000;
+ mytime.tv_usec = mytime.tv_usec % 1000000;
+ }
+ return &mytime;
+}
+
+/** Update each descriptor's allowed rate of issuing commands.
+ * Players are rate-limited; they may only perform up to a certain
+ * number of commands per time slice. This function is run periodically
+ * to refresh each descriptor's available command quota based on how
+ * many slices have passed since it was last updated.
+ * \param last pointer to timeval struct of last time quota was updated.
+ * \param current pointer to timeval struct of current time.
+ */
+static void
+update_quotas(struct timeval *last, struct timeval *current)
+{
+ int nslices;
+ DESC *d;
+ nslices = (int) msec_diff(current, last) / COMMAND_TIME_MSEC;
+
+ if (nslices > 0) {
+ for (d = descriptor_list; d; d = d->next) {
+ d->quota += COMMANDS_PER_TIME * nslices;
+ if (d->quota > COMMAND_BURST_SIZE)
+ d->quota = COMMAND_BURST_SIZE;
+ }
+ }
+}
+
+static const char *empabb(dbref player) {
+ static char str[4];
+ ATTR *a;
+ /*
+ dbref start, end, last;
+ */
+ dbref start;
+
+ memset(str, '\0', 4);
+
+ if(!IsDivision(SDIV(player).object))
+ goto bad_empabb_value;
+ start = SDIV(player).object;
+
+ /*
+ for(last = end = start; GoodObject(end) && IsDivision(end) &&
+ !has_flag_by_name(end, "EMPIRE", TYPE_DIVISION) ; last = end, end = SDIV(end).object)
+ ;
+ if(!has_flag_by_name(end, "EMPIRE", TYPE_DIVISION)) {
+ if(end == NOTHING && IsDivision(last))
+ end = last;
+ else end = start;
+ }
+ */
+ /* K, end is the empire we're grabbing this off of */
+ a = atr_get(start, "ALIAS");
+ if(!a)
+ goto bad_empabb_value;
+ strncpy(str, atr_value(a), 3);
+ if(!str[0])
+ goto bad_empabb_value;
+ return str;
+
+
+bad_empabb_value:
+ strncpy(str, "---", 3);
+ return str;
+}
+
+
+static void
+shovechars()
+{
+ /* this is the main game loop */
+
+ fd_set input_set, output_set;
+ time_t now;
+ struct timeval last_slice, current_time, then;
+ struct timeval next_slice, *returned_time;
+ struct timeval timeout, slice_timeout;
+ int found;
+ int queue_timeout;
+ DESC *d, *dnext;
+ int input_ready, output_ready;
+
+ d = initializesock(0, "localhost", "127.0.0.1", 0);
+
+ our_gettimeofday(&last_slice);
+
+ /* done. print message to the log */
+ do_rawlog(LT_ERR, "RESTART FINISHED.");
+
+ our_gettimeofday(&then);
+
+ while (shutdown_flag == 0) {
+ our_gettimeofday(¤t_time);
+
+ update_quotas(&last_slice, ¤t_time);
+ last_slice.tv_sec = current_time.tv_sec;
+ last_slice.tv_usec = current_time.tv_usec;
+
+ if (msec_diff(¤t_time, &then) >= 1000) {
+ globals.on_second = 1;
+ then.tv_sec = current_time.tv_sec;
+ then.tv_usec = current_time.tv_usec;
+ }
+
+ process_commands();
+
+ if (signal_shutdown_flag) {
+ flag_broadcast(0, 0, T("GAME: Shutdown by external signal"));
+ do_rawlog(LT_ERR, T("SHUTDOWN by external signal"));
+#ifdef AUTORESTART
+ system("touch NORESTART");
+#endif
+ shutdown_flag = 1;
+ }
+
+ if (signal_dump_flag) {
+ globals.paranoid_dump = 0;
+ do_rawlog(LT_CHECK, "DUMP by external signal");
+ fork_and_dump(1);
+ signal_dump_flag = 0;
+ }
+
+ if (shutdown_flag)
+ break;
+
+ /* test for events */
+ dispatch();
+
+ /* any queued robot commands waiting? */
+ /* timeout.tv_sec used to be set to que_next(), the number of
+ * seconds before something on the queue needed to run, but this
+ * caused a problem with stuff that had to be triggered by alarm
+ * signal every second, so we're reduced to what's below:
+ */
+ queue_timeout = que_next();
+ timeout.tv_sec = queue_timeout ? 1 : 0;
+ timeout.tv_usec = 0;
+
+ returned_time = msec_add(&last_slice, COMMAND_TIME_MSEC);
+ next_slice.tv_sec = returned_time->tv_sec;
+ next_slice.tv_usec = returned_time->tv_usec;
+
+ returned_time = timeval_sub(&next_slice, ¤t_time);
+ slice_timeout.tv_sec = returned_time->tv_sec;
+ slice_timeout.tv_usec = returned_time->tv_usec;
+ /* Make sure slice_timeout cannot have a negative time. Better
+ safe than sorry. */
+ if (slice_timeout.tv_sec < 0)
+ slice_timeout.tv_sec = 0;
+ if (slice_timeout.tv_usec < 0)
+ slice_timeout.tv_usec = 0;
+
+ FD_ZERO(&input_set);
+ FD_ZERO(&output_set);
+ for (d = descriptor_list; d; d = d->next) {
+ if (d->input.head) {
+ timeout.tv_sec = slice_timeout.tv_sec;
+ timeout.tv_usec = slice_timeout.tv_usec;
+ } else {
+ if(d->descriptor == 0)
+ FD_SET(STDIN_FILENO, &input_set);
+ else
+ FD_SET(d->descriptor, &input_set);
+ }
+ if (d->output.head) {
+ if(d->descriptor == 0)
+ FD_SET(STDOUT_FILENO, &output_set);
+ else
+ FD_SET(d->descriptor, &output_set);
+ }
+ }
+
+ found = select(2, &input_set, &output_set, (fd_set *) 0, &timeout);
+ if (found < 0) {
+#ifdef WIN32
+ if (found == SOCKET_ERROR && WSAGetLastError() != WSAEINTR)
+#else
+ if (errno != EINTR)
+#endif
+ {
+ perror("select");
+ return;
+ }
+ } else {
+ /* if !found then time for robot commands */
+
+ if (!found) {
+ do_top(options.queue_chunk);
+ continue;
+ } else {
+ do_top(options.active_q_chunk);
+ }
+ now = mudtime;
+ for (d = descriptor_list; d; d = dnext) {
+ dnext = d->next;
+ if(d->descriptor == 0) {
+ input_ready = FD_ISSET(STDIN_FILENO, &input_set);
+ output_ready = FD_ISSET(STDOUT_FILENO, &output_set);
+ } else {
+ input_ready = FD_ISSET(d->descriptor, &input_set);
+ output_ready = FD_ISSET(d->descriptor, &output_set);
+ }
+ if (input_ready) {
+ if (!process_input(d, output_ready)) {
+ shutdownsock(d);
+ if(d->descriptor == 0)
+ return;
+ continue;
+ }
+ }
+ if (output_ready) {
+ if (!process_output(d)) {
+ shutdownsock(d);
+ if(d->descriptor == 0)
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+clearstrings(DESC *d)
+{
+ if (d->output_prefix) {
+ mush_free((Malloc_t) d->output_prefix, "userstring");
+ d->output_prefix = 0;
+ }
+ if (d->output_suffix) {
+ mush_free((Malloc_t) d->output_suffix, "userstring");
+ d->output_suffix = 0;
+ }
+}
+
+/* Display a cached text file. If a prefix line was given,
+ * display that line before the text file, but only if we've
+ * got a text file to display
+ */
+static void
+fcache_dump(DESC *d, FBLOCK fb[2], const unsigned char *prefix)
+{
+ /* If we've got nothing nice to say, don't say anything */
+ if (!fb[0].buff && !((d->conn_flags & CONN_HTML) && fb[1].buff))
+ return;
+ /* We've got something to say */
+ if (prefix) {
+ queue_newwrite(d, prefix, u_strlen(prefix));
+ queue_eol(d);
+ }
+ if (d->conn_flags & CONN_HTML) {
+ if (fb[1].buff)
+ queue_newwrite(d, fb[1].buff, fb[1].len);
+ else
+ queue_write(d, fb[0].buff, fb[0].len);
+ } else
+ queue_write(d, fb[0].buff, fb[0].len);
+}
+
+
+static int
+fcache_read(FBLOCK *fb, const char *filename)
+{
+ if (!fb || !filename)
+ return -1;
+
+ /* Free prior cache */
+ if (fb->buff) {
+ mush_free(fb->buff, "fcache_data");
+ }
+
+ fb->buff = NULL;
+ fb->len = 0;
+
+#ifdef WIN32
+ /* Win32 read code here */
+ {
+ HANDLE fh;
+ BY_HANDLE_FILE_INFORMATION sb;
+ DWORD r = 0;
+
+
+ if ((fh = CreateFile(filename, GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (!GetFileInformationByHandle(fh, &sb)) {
+ CloseHandle(fh);
+ return -1;
+ }
+
+ fb->len = sb.nFileSizeLow;
+
+ if (!(fb->buff = mush_malloc(sb.nFileSizeLow, "fcache_data"))) {
+ CloseHandle(fh);
+ return -1;
+ }
+
+ if (!ReadFile(fh, fb->buff, sb.nFileSizeLow, &r, NULL) || fb->len != r) {
+ CloseHandle(fh);
+ mush_free(fb->buff, "fcache_data");
+ fb->buff = NULL;
+ return -1;
+ }
+
+ CloseHandle(fh);
+
+ fb->len = sb.nFileSizeLow;
+ return (int) fb->len;
+ }
+#else
+ /* Posix read code here */
+ {
+ int fd, n;
+ struct stat sb;
+
+ release_fd();
+ if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+ do_log(LT_ERR, 0, 0, T("Couldn't open cached text file '%s'"), filename);
+ reserve_fd();
+ return -1;
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ do_log(LT_ERR, 0, 0, T("Couldn't get the size of text file '%s'"),
+ filename);
+ close(fd);
+ reserve_fd();
+ return -1;
+ }
+
+
+ if (!(fb->buff = mush_malloc(sb.st_size, "fcache_data"))) {
+ do_log(LT_ERR, 0, 0, T("Couldn't allocate %d bytes of memory for '%s'!"),
+ (int) sb.st_size, filename);
+ close(fd);
+ reserve_fd();
+ return -1;
+ }
+
+ if ((n = read(fd, fb->buff, sb.st_size)) != sb.st_size) {
+ do_log(LT_ERR, 0, 0, T("Couldn't read all of '%s'"), filename);
+ close(fd);
+ mush_free(fb->buff, "fcache_data");
+ fb->buff = NULL;
+ reserve_fd();
+ return -1;
+ }
+
+ close(fd);
+ reserve_fd();
+ fb->len = sb.st_size;
+ }
+#endif /* Posix read code */
+
+ return fb->len;
+}
+
+/** Load all of the cached text files.
+ * \param player the enactor.
+ */
+void
+fcache_load(dbref player)
+{
+ int conn, motd, new, reg, quit, down, full;
+ int guest;
+ int i;
+
+ for (i = 0; i < (SUPPORT_PUEBLO ? 2 : 1); i++) {
+ conn = fcache_read(&fcache.connect_fcache[i], options.connect_file[i]);
+ motd = fcache_read(&fcache.motd_fcache[i], options.motd_file[i]);
+ new = fcache_read(&fcache.newuser_fcache[i], options.newuser_file[i]);
+ reg = fcache_read(&fcache.register_fcache[i], options.register_file[i]);
+ quit = fcache_read(&fcache.quit_fcache[i], options.quit_file[i]);
+ down = fcache_read(&fcache.down_fcache[i], options.down_file[i]);
+ full = fcache_read(&fcache.full_fcache[i], options.full_file[i]);
+ guest = fcache_read(&fcache.guest_fcache[i], options.guest_file[i]);
+
+ if (player != NOTHING) {
+ notify_format(player,
+ T
+ ("%s sizes: NewUser...%d Connect...%d Guest...%d Motd...%d Quit...%d Register...%d Down...%d Full...%d"),
+ i ? "HTMLFile" : "File", new, conn, guest, motd, quit,
+ reg, down, full);
+ }
+ }
+
+}
+
+/** Initialize all of the cached text files (at startup).
+ */
+void
+fcache_init(void)
+{
+ fcache_load(NOTHING);
+}
+
+static void
+logout_sock(DESC *d)
+{
+ SU_PATH *path_entry;
+
+ int n;
+ char tbuf1[BUFFER_LEN];
+
+ if (d->connected) {
+ fcache_dump(d, fcache.quit_fcache, NULL);
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Logout by %s(#%d) <Connection not dropped>"),
+ d->descriptor, d->addr, d->ip, Name(d->player), d->player);
+ if(d->last_time > 0) {
+ d->idle_total += difftime(mudtime, d->last_time);
+ d->unidle_times++;
+ }
+ snprintf(tbuf1, BUFFER_LEN-1, "%ld %ld %d %d", (mudtime - d->connected_at),
+ d->idle_total, d->unidle_times, d->cmds);
+ tbuf1[strlen(tbuf1)+1] = '\0';
+ (void) atr_add(d->player, "LASTACTIVITY", tbuf1, GOD, NOTHING);
+ announce_disconnect(d->player);
+ do_mail_purge(d->player);
+ if (MAX_LOGINS) {
+ login_number--;
+ if (!under_limit && (login_number < MAX_LOGINS)) {
+ under_limit = 1;
+ do_log(LT_CONN, 0, 0,
+ T("Below maximum player limit of %d. Logins enabled."),
+ MAX_LOGINS);
+ }
+ }
+ } else {
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Logout, never connected. <Connection not dropped>"),
+ d->descriptor, d->addr, d->ip);
+ }
+ process_output(d); /* flush our old output */
+ /* pretend we have a new connection */
+ d->input_handler = do_command;
+ d->connected = 0;
+ d->output_prefix = 0;
+ d->output_suffix = 0;
+ d->output_size = 0;
+ d->output.head = 0;
+ d->player = 0;
+ d->output.tail = &d->output.head;
+ d->input.head = 0;
+ d->input.tail = &d->input.head;
+ d->raw_input = 0;
+ d->raw_input_at = 0;
+ d->quota = COMMAND_BURST_SIZE;
+ d->last_time = mudtime;
+ d->idle_total = 0;
+ d->unidle_times = 0;
+ d->cmds = 0;
+ d->hide = 0;
+ d->doing[0] = '\0';
+ d->mailp = NULL;
+ d->pinfo.object = NOTHING;
+ d->pinfo.atr = NULL;
+ d->pinfo.lock = 0;
+ d->pinfo.function = NULL;
+
+ while(d->su_exit_path) {
+ path_entry = d->su_exit_path;
+ d->su_exit_path = path_entry->next;
+ mush_free(path_entry, "SU_EXIT_PATH");
+ }
+ welcome_user(d);
+ for(n = 0; n < MAX_SNOOPS; n++)
+ d->snooper[n] = -1;
+}
+
+/** Disconnect a descriptor.
+ * This sends appropriate disconnection text, flushes output, and
+ * then closes the associated socket.
+ * \param d pointer to descriptor to disconnect.
+ */
+static void
+shutdownsock(DESC *d)
+{
+ char tbuf1[BUFFER_LEN];
+ int i;
+
+ if (d->connected) {
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Logout by %s(#%d)"),
+ d->descriptor, d->addr, d->ip, Name(d->player), d->player);
+ if (d->connected != 2) {
+ fcache_dump(d, fcache.quit_fcache, NULL);
+ /* Player was not allowed to log in from the connect screen */
+ if(d->last_time > 0) {
+ d->idle_total += difftime(mudtime, d->last_time);
+ d->unidle_times++;
+ }
+ snprintf(tbuf1, BUFFER_LEN-1, "%ld %ld %d %d", (mudtime - d->connected_at),
+ d->idle_total , d->unidle_times, d->cmds);
+ tbuf1[strlen(tbuf1)+1] = '\0';
+ (void) atr_add(d->player, "LASTACTIVITY", tbuf1, GOD, NOTHING);
+ announce_disconnect(d->player);
+ do_mail_purge(d->player);
+ }
+ if (MAX_LOGINS) {
+ login_number--;
+ if (!under_limit && (login_number < MAX_LOGINS)) {
+ under_limit = 1;
+ do_log(LT_CONN, 0, 0,
+ T("Below maximum player limit of %d. Logins enabled."),
+ MAX_LOGINS);
+ }
+ }
+ } else {
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Connection closed, never connected."),
+ d->descriptor, d->addr, d->ip);
+ }
+ process_output(d);
+ clearstrings(d);
+ if(d->descriptor != 0) {
+ shutdown(d->descriptor, 2);
+ closesocket(d->descriptor);
+ } else {
+ freeqs(d);
+ d->input_handler = do_command;
+ d->connected = 0;
+ d->connected_at = mudtime;
+ d->output_prefix = 0;
+ d->output_suffix = 0;
+ d->output_size = 0;
+ d->output.head = 0;
+ d->player = 0;
+ d->output.tail = &d->output.head;
+ d->input.head = 0;
+ d->input.tail = &d->input.head;
+ d->raw_input = 0;
+ d->raw_input_at = 0;
+ d->quota = COMMAND_BURST_SIZE;
+ d->last_time = mudtime;
+ d->idle_total = 0;
+ d->unidle_times = 0;
+ d->cmds = 0;
+ d->hide = 0;
+ d->doing[0] = '\0';
+ d->mailp = NULL;
+ strncpy(d->addr, "localhost", 100);
+ d->addr[99] = '\0';
+ strncpy(d->ip, "127.0.0.1", 100);
+ d->ip[99] = '\0';
+ d->conn_flags = CONN_DEFAULT;
+ d->input_chars = 0;
+ d->output_chars = 0;
+ d->ttype = mush_strdup("unknown", "terminal description");
+ d->checksum[0] = '\0';
+ d->su_exit_path = NULL;
+ d->pinfo.object = NOTHING;
+ d->pinfo.atr = NULL;
+ d->pinfo.lock = 0;
+ d->pinfo.function = NULL;
+ d->width = 78;
+ d->height = 24;
+ welcome_user(d);
+ for(i = 0; i < MAX_SNOOPS; i++)
+ d->snooper[i] = -1;
+ }
+
+ if(d->descriptor != 0) {
+ if (d->prev)
+ d->prev->next = d->next;
+ else /* d was the first one! */
+ descriptor_list = d->next;
+ if (d->next)
+ d->next->prev = d->prev;
+ }
+
+ if(d->descriptor != 0) {
+ freeqs(d);
+ mush_free(d->ttype, "terminal description");
+ mush_free((Malloc_t) d, "descriptor");
+ }
+
+ ndescriptors--;
+}
+
+/* ARGSUSED */
+static DESC *
+initializesock(int s, char *addr, char *ip, int use_ssl
+ __attribute__ ((__unused__)))
+{
+ DESC *d;
+ int n;
+
+ d = (DESC *) mush_malloc(sizeof(DESC), "descriptor");
+ if (!d)
+ mush_panic("Out of memory.");
+ d->descriptor = s;
+ d->input_handler = do_command;
+ d->connected = 0;
+ d->connected_at = mudtime;
+ make_nonblocking(s);
+ d->output_prefix = 0;
+ d->output_suffix = 0;
+ d->output_size = 0;
+ d->output.head = 0;
+ d->player = 0;
+ d->output.tail = &d->output.head;
+ d->input.head = 0;
+ d->input.tail = &d->input.head;
+ d->raw_input = 0;
+ d->raw_input_at = 0;
+ d->quota = COMMAND_BURST_SIZE;
+ d->last_time = mudtime;
+ d->idle_total = 0;
+ d->unidle_times = 0;
+ d->cmds = 0;
+ d->hide = 0;
+ d->doing[0] = '\0';
+ d->mailp = NULL;
+ strncpy(d->addr, addr, 100);
+ d->addr[99] = '\0';
+ strncpy(d->ip, ip, 100);
+ d->ip[99] = '\0';
+ d->conn_flags = CONN_DEFAULT;
+ d->input_chars = 0;
+ d->output_chars = 0;
+ d->ttype = mush_strdup("unknown", "terminal description");
+ d->checksum[0] = '\0';
+ d->su_exit_path = NULL;
+ d->pinfo.object = NOTHING;
+ d->pinfo.atr = NULL;
+ d->pinfo.lock = 0;
+ d->pinfo.function = NULL;
+
+ if (descriptor_list)
+ descriptor_list->prev = d;
+ d->next = descriptor_list;
+ d->prev = NULL;
+ descriptor_list = d;
+
+ d->width = 78;
+ d->height = 24;
+ test_telnet(d);
+ welcome_user(d);
+ for(n = 0; n < MAX_SNOOPS; n++)
+ d->snooper[n] = -1;
+ return d;
+}
+
+/** Flush pending output for a descriptor.
+ * This function actually sends the queued output over the descriptor's
+ * socket.
+ * \param d pointer to descriptor to send output to.
+ * \retval 1 successfully flushed at least some output.
+ * \retval 0 something failed, and the descriptor should probably be closed.
+ */
+int
+process_output(DESC *d)
+{
+ struct text_block **qp, *cur;
+ int cnt;
+
+ for (qp = &d->output.head; ((cur = *qp) != NULL);) {
+ if(d->descriptor == 0)
+ cnt = write(STDOUT_FILENO, cur->start, cur->nchars);
+ else
+ cnt = send(d->descriptor, cur->start, cur->nchars, 0);
+ if (cnt < 0) {
+#ifdef WIN32
+ if (cnt == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+#ifdef EAGAIN
+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
+#else
+ if (errno == EWOULDBLOCK)
+#endif
+#endif
+ return 1;
+ return 0;
+ }
+ d->output_size -= cnt;
+ d->output_chars += cnt;
+ if (cnt == cur->nchars) {
+ if (!cur->nxt)
+ d->output.tail = qp;
+ *qp = cur->nxt;
+#ifdef DEBUG
+ do_rawlog(LT_ERR, "free_text_block(0x%x) at 2.", cur);
+#endif /* DEBUG */
+ free_text_block(cur);
+ continue; /* do not adv ptr */
+ }
+ cur->nchars -= cnt;
+ cur->start += cnt;
+ break;
+ }
+ return 1;
+}
+
+
+static void
+welcome_user(DESC *d)
+{
+ if (SUPPORT_PUEBLO && !(d->conn_flags & CONN_HTML))
+ queue_newwrite(d, (unsigned char *) PUEBLO_HELLO, strlen(PUEBLO_HELLO));
+ fcache_dump(d, fcache.connect_fcache, NULL);
+}
+
+static void
+save_command(DESC *d, const unsigned char *command)
+{
+ add_to_queue(&d->input, command, u_strlen(command) + 1);
+}
+
+static void
+test_telnet(DESC *d)
+{
+ /* Use rfc 1184 to test telnet support, as it tries to set linemode
+ with client-side editing. Good for Broken Telnet Programs. */
+ if (d->descriptor != 0 && !TELNET_ABLE(d)) {
+ /* IAC DO LINEMODE */
+ unsigned char query[3] = "\xFF\xFD\x22";
+ queue_newwrite(d, query, 3);
+ d->conn_flags |= CONN_TELNET_QUERY;
+ process_output(d);
+ }
+}
+
+static void
+setup_telnet(DESC *d)
+{
+ /* Win2k telnet doesn't do local echo by default,
+ apparently. Unfortunately, there doesn't seem to be a telnet
+ option for local echo, just remote echo. */
+ d->conn_flags |= CONN_TELNET;
+ if (d->conn_flags & CONN_TELNET_QUERY) {
+ /* IAC DO NAWS IAC DO TERMINAL-TYPE */
+ unsigned char extra_options[6] = "\xFF\xFD\x1F" "\xFF\xFD\x18";
+ d->conn_flags &= ~CONN_TELNET_QUERY;
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Switching to Telnet mode."),
+ d->descriptor, d->addr, d->ip);
+ queue_newwrite(d, extra_options, 6);
+ process_output(d);
+ }
+}
+
+static int
+handle_telnet(DESC *d, unsigned char **q, unsigned char *qend)
+{
+ int i;
+
+ /* *(*q - q) == IAC at this point. */
+ switch (**q) {
+ case SB: /* Sub-option */
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ if (**q == TN_LINEMODE) {
+ if ((*q + 2) >= qend)
+ return -1;
+ *q += 2;
+ while (*q < qend && **q != SE)
+ (*q)++;
+ if (*q >= qend)
+ return -1;
+ } else if (**q == TN_NAWS) {
+ /* Learn what size window the client is using. */
+ union {
+ short s;
+ unsigned char bytes[2];
+ } raw;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ /* Width */
+ if (**q == IAC) {
+ raw.bytes[0] = IAC;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ } else
+ raw.bytes[0] = **q;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ if (**q == IAC) {
+ raw.bytes[1] = IAC;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ } else
+ raw.bytes[1] = **q;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+
+ d->width = ntohs(raw.s);
+
+ /* Height */
+ if (**q == IAC) {
+ raw.bytes[0] = IAC;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ } else
+ raw.bytes[0] = **q;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ if (**q == IAC) {
+ raw.bytes[1] = IAC;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ } else
+ raw.bytes[1] = **q;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ d->height = ntohs(raw.s);
+
+ /* IAC SE */
+ if (*q + 1 >= qend)
+ return -1;
+ (*q)++;
+ } else if (**q == TN_TTYPE) {
+ /* Read the terminal type: TERMINAL-TYPE IS blah IAC SE */
+ char tbuf[BUFFER_LEN], *bp = tbuf;
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ /* Skip IS */
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+
+ /* Read up to IAC SE */
+ while (1) {
+ if (*q >= qend)
+ return -1;
+ if (**q == IAC) {
+ if (*q + 1 >= qend)
+ return -1;
+ if (*(*q + 1) == IAC) {
+ safe_chr((char) IAC, tbuf, &bp);
+ (*q)++;
+ } else
+ break;
+ } else
+ safe_chr(**q, tbuf, &bp);
+ (*q)++;
+ }
+ while (*q < qend && **q != SE)
+ (*q)++;
+ *bp = '\0';
+ mush_free(d->ttype, "terminal description");
+ d->ttype = mush_strdup(tbuf, "terminal description");
+ /* We have the terminal type, now set any defaults if we find 'em */
+ for(i = 0 ; client_maps[i].terminal != NULL; i++)
+ if(!strcmp(client_maps[i].terminal, d->ttype)) {
+ d->conn_flags |= client_maps[i].flags;
+ break;
+ }
+ } else {
+ while (*q < qend && **q != SE)
+ (*q)++;
+ }
+ break;
+ case NOP: /* No-op */
+ if (*q >= qend)
+ return -1;
+#ifdef DEBUG_TELNET
+ fprintf(stderr, "Got IAC NOP\n");
+#endif
+ break;
+ case AYT: /* Are you there? */
+ if (*q >= qend)
+ return -1;
+ else {
+ static char ayt_reply[] = "\r\n*** AYT received, I'm here ***\r\n";
+ queue_newwrite(d, (unsigned char *) ayt_reply, strlen(ayt_reply));
+ process_output(d);
+ }
+ break;
+ case WILL: /* Client is willing to do something, or confirming */
+ setup_telnet(d);
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+
+ if (**q == TN_LINEMODE) {
+ /* Set up our preferred linemode options. */
+ /* IAC SB LINEMODE MODE (EDIT|SOFT_TAB) IAC SE */
+ unsigned char reply[7] = "\xFF\xFA\x22\x01\x09\xFF\xF0";
+ queue_newwrite(d, reply, 7);
+#ifdef DEBUG_TELNET
+ fprintf(stderr, "Setting linemode options.\n");
+#endif
+ } else if (**q == TN_TTYPE) {
+ /* Ask for terminal type id: IAC SB TERMINAL-TYPE SEND IAC SEC */
+ unsigned char reply[6] = "\xFF\xFA\x18\x01\xFF\xF0";
+ queue_newwrite(d, reply, 6);
+ } else if (**q == TN_SGA || **q == TN_NAWS) {
+ /* This is good to be at. */
+ } else { /* Refuse options we don't handle */
+ unsigned char reply[3];
+ reply[0] = IAC;
+ reply[1] = DONT;
+ reply[2] = **q;
+ queue_newwrite(d, reply, sizeof reply);
+ process_output(d);
+ }
+ break;
+ case DO: /* Client is asking us to do something */
+ setup_telnet(d);
+ if (*q >= qend)
+ return -1;
+ (*q)++;
+ if (**q == TN_LINEMODE) {
+ } else if (**q == TN_SGA) {
+ /* IAC WILL SGA IAC DO SGA */
+ unsigned char reply[6] = "\xFF\xFB\x03\xFF\xFD\x03";
+ queue_newwrite(d, reply, 6);
+ process_output(d);
+#ifdef DEBUG_TELNET
+ fprintf(stderr, "GOT IAC DO SGA, sending IAC WILL SGA IAG DO SGA\n");
+#endif
+ } else {
+ /* Stuff we won't do */
+ unsigned char reply[3];
+ reply[0] = IAC;
+ reply[1] = WONT;
+ reply[2] = (char) **q;
+ queue_newwrite(d, reply, sizeof reply);
+ process_output(d);
+ }
+ break;
+ case WONT: /* Client won't do something we want. */
+ case DONT: /* Client doesn't want us to do something */
+ setup_telnet(d);
+#ifdef DEBUG_TELNET
+ fprintf(stderr, "Got IAC %s 0x%x\n", **q == WONT ? "WONT" : "DONT",
+ *(*q + 1));
+#endif
+ if (*q + 1 >= qend)
+ return -1;
+ (*q)++;
+ break;
+ default: /* Also catches IAC IAC for a literal 255 */
+ return 0;
+ }
+ return 1;
+}
+
+static void
+process_input_helper(DESC *d, char *tbuf1, int got)
+{
+ unsigned char *p, *pend, *q, *qend;
+
+ if (!d->raw_input) {
+ d->raw_input =
+ (unsigned char *) mush_malloc(sizeof(char) * MAX_COMMAND_LEN,
+ "descriptor_raw_input");
+ if (!d->raw_input)
+ mush_panic("Out of memory");
+ d->raw_input_at = d->raw_input;
+ }
+ p = d->raw_input_at;
+ d->input_chars += got;
+ pend = d->raw_input + MAX_COMMAND_LEN - 1;
+ for (q = (unsigned char *) tbuf1, qend = (unsigned char *) tbuf1 + got;
+ q < qend; q++) {
+ if (*q == '\r') {
+ /* A broken client (read: WinXP telnet) might send only CR, and not CRLF
+ * so it's nice of us to try to handle this.
+ */
+ *p = '\0';
+ if (p > d->raw_input)
+ save_command(d, d->raw_input);
+ p = d->raw_input;
+ if (((q + 1) < qend) && (*(q + 1) == '\n'))
+ q++; /* For clients that work */
+ } else if (*q == '\n') {
+ *p = '\0';
+ if (p > d->raw_input)
+ save_command(d, d->raw_input);
+ p = d->raw_input;
+ } else if (*q == '\b') {
+ if (p > d->raw_input)
+ p--;
+ } else if ((unsigned char) *q == IAC) { /* Telnet option foo */
+ if (q >= qend)
+ break;
+ q++;
+ if (!TELNET_ABLE(d) || handle_telnet(d, &q, qend) == 0) {
+ if (p < pend && isprint(*q))
+ *p++ = *q;
+ }
+ } else if (p < pend && isprint(*q)) {
+ *p++ = *q;
+ }
+ }
+ if (p > d->raw_input) {
+ d->raw_input_at = p;
+ } else {
+ mush_free((Malloc_t) d->raw_input, "descriptor_raw_input");
+ d->raw_input = 0;
+ d->raw_input_at = 0;
+ }
+}
+
+/* ARGSUSED */
+static int
+process_input(DESC *d, int output_ready __attribute__ ((__unused__)))
+{
+ int got = 0;
+ char tbuf1[BUFFER_LEN];
+
+ errno = 0;
+
+ if(d->descriptor == 0)
+ got = read(STDIN_FILENO, tbuf1, sizeof tbuf1);
+ else
+ got = recv(d->descriptor, tbuf1, sizeof tbuf1, 0);
+
+ if (got <= 0) {
+ /* At this point, select() says there's data waiting to be read from
+ * the socket, but we shouldn't assume that read() will actually get it
+ * and blindly act like a got of -1 is a disconnect-worthy error.
+ */
+#ifdef EAGAIN
+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINTR))
+#else
+ if ((errno == EWOULDBLOCK) || (errno == EINTR))
+#endif
+ return 1;
+ else
+ return 0;
+ }
+
+ process_input_helper(d, tbuf1, got);
+
+ return 1;
+}
+
+static void
+set_userstring(unsigned char **userstring, const char *command)
+{
+ if (*userstring) {
+ mush_free((Malloc_t) *userstring, "userstring");
+ *userstring = NULL;
+ }
+ while (*command && isspace((unsigned char) *command))
+ command++;
+ if (*command)
+ *userstring = (unsigned char *) mush_strdup(command, "userstring");
+}
+
+static void
+process_commands(void)
+{
+ int nprocessed;
+ DESC *cdesc, *dnext;
+ struct text_block *t;
+ int retval = 1;
+
+ do {
+ nprocessed = 0;
+ for (cdesc = descriptor_list; cdesc;
+ cdesc = (nprocessed > 0 && retval > 0) ? cdesc->next : dnext) {
+ dnext = cdesc->next;
+ if (cdesc->quota > 0 && (t = cdesc->input.head)) {
+ cdesc->quota--;
+ nprocessed++;
+ start_cpu_timer();
+ feed_snoop(cdesc,(const char *) t->start, 0);
+ /* check AUNIDLE */
+ if(options.idle_time && ((mudtime - cdesc->last_time) > options.idle_time)) {
+ if(atr_get(cdesc->player, "AUNIDLE"))
+ queue_attribute_noparent(cdesc->player, "AUNIDLE", cdesc->player);
+ if(GoodObject(Division(cdesc->player)) && atr_get(Division(cdesc->player), "AUNIDLE"))
+ queue_attribute(Division(cdesc->player), "AUNIDLE", cdesc->player);
+ }
+ retval = cdesc->input_handler(cdesc, (char *) t->start);
+ reset_cpu_timer();
+ if(retval == -1 && do_su_exit(cdesc))
+ retval = 1;
+
+ if (retval == 0) {
+ shutdownsock(cdesc);
+ } else if (retval == -1) {
+ logout_sock(cdesc);
+ } else {
+ cdesc->input.head = t->nxt;
+ if (!cdesc->input.head)
+ cdesc->input.tail = &cdesc->input.head;
+ if (t) {
+#ifdef DEBUG
+ do_rawlog(LT_ERR, "free_text_block(0x%x) at 5.", t);
+#endif /* DEBUG */
+ free_text_block(t);
+ }
+ }
+ }
+ }
+ } while (nprocessed > 0);
+}
+
+/** Send a descriptor's output prefix */
+#define send_prefix(d) \
+ if (d->output_prefix) { \
+ queue_newwrite(d, d->output_prefix, u_strlen(d->output_prefix)); \
+ queue_eol(d); \
+ }
+
+/** Send a descriptor's output suffix */
+#define send_suffix(d) \
+ if (d->output_suffix) { \
+ queue_newwrite(d, d->output_suffix, u_strlen(d->output_suffix)); \
+ queue_eol(d); \
+ }
+
+int
+do_command(DESC *d, char *command)
+{
+ int j;
+
+ depth = 0;
+
+ (d->cmds)++;
+
+ if (!strcmp(command, IDLE_COMMAND))
+ return 1;
+ if(difftime(mudtime, d->last_time) >= 300) {
+ d->idle_total += difftime(mudtime, d->last_time);
+ d->unidle_times++;
+ }
+ d->last_time = mudtime;
+ if (!strcmp(command, QUIT_COMMAND)) {
+ return 0;
+ } else if (!strcmp(command, LOGOUT_COMMAND)) {
+ return -1;
+ } else if (!strcmp(command, INFO_COMMAND)) {
+ send_prefix(d);
+ dump_info(d);
+ send_suffix(d);
+ } else if (!strncmp(command, WHO_COMMAND, strlen(WHO_COMMAND))) {
+ send_prefix(d);
+ dump_users(d, command + strlen(WHO_COMMAND), 0);
+ send_suffix(d);
+ } else if (!strncmp(command, DOING_COMMAND, strlen(DOING_COMMAND))) {
+ send_prefix(d);
+ dump_users(d, command + strlen(DOING_COMMAND), 1);
+ send_suffix(d);
+ } else if (!strncmp(command, SESSION_COMMAND, strlen(SESSION_COMMAND))) {
+ send_prefix(d);
+ dump_users(d, command + strlen(SESSION_COMMAND), 2);
+ send_suffix(d);
+ } else if (!strncmp(command, PREFIX_COMMAND, strlen(PREFIX_COMMAND))) {
+ set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND));
+ } else if (!strncmp(command, SUFFIX_COMMAND, strlen(SUFFIX_COMMAND))) {
+ set_userstring(&d->output_suffix, command + strlen(SUFFIX_COMMAND));
+ } else if (!strncmp(command, "SCREENWIDTH", 11)) {
+ d->width = parse_integer(command + 11);
+ } else if (!strncmp(command, "SCREENHEIGHT", 12)) {
+ d->height = parse_integer(command + 12);
+ } else if (SUPPORT_PUEBLO
+ && !strncmp(command, PUEBLO_COMMAND, strlen(PUEBLO_COMMAND))) {
+ parse_puebloclient(d, command);
+ if (!(d->conn_flags & CONN_HTML)) {
+ queue_newwrite(d, (unsigned char *) PUEBLO_SEND, strlen(PUEBLO_SEND));
+ process_output(d);
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Switching to Pueblo mode."),
+ d->descriptor, d->addr, d->ip);
+ d->conn_flags |= CONN_HTML;
+ if (!d->connected)
+ welcome_user(d);
+ }
+ } else {
+ if (d->connected) {
+ send_prefix(d);
+ global_eval_context.cplr = d->player;
+ strcpy(global_eval_context.ccom, command);
+ strcpy(global_eval_context.ucom, "");
+
+ /* Clear %0-%9 and r(0) - r(9) */
+ for (j = 0; j < 10; j++)
+ global_eval_context.wenv[j] = (char *) NULL;
+ for (j = 0; j < NUMQ; j++)
+ global_eval_context.renv[j][0] = '\0';
+ global_eval_context.process_command_port = d->descriptor;
+
+ process_command(d->player, command, d->player, d->player, 1);
+ send_suffix(d);
+ strcpy(global_eval_context.ccom, "");
+ strcpy(global_eval_context.ucom, "");
+ global_eval_context.cplr = NOTHING;
+ } else {
+ if (!check_connect(d, command))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void
+parse_puebloclient(DESC *d, char *command)
+{
+ const char *p, *end;
+ if ((p = string_match(command, "md5="))) {
+ /* Skip md5=" */
+ p += 5;
+ if ((end = strchr(p, '"'))) {
+ if ((end > p) && ((end - p) <= PUEBLO_CHECKSUM_LEN)) {
+ /* Got it! */
+ strncpy(d->checksum, p, end - p);
+ d->checksum[end - p] = '\0';
+ }
+ }
+ }
+}
+
+static int
+dump_messages(DESC *d, dbref player, int isnew)
+{
+ int is_hidden;
+ int num = 0;
+ DESC *tmpd;
+
+ d->connected = 1;
+ d->connected_at = mudtime;
+ d->player = player;
+ d->doing[0] = '\0';
+
+ if (MAX_LOGINS) {
+ /* check for exceeding max player limit */
+ login_number++;
+ if (under_limit && (login_number > MAX_LOGINS)) {
+ under_limit = 0;
+ do_rawlog(LT_CONN,
+ T("Limit of %d players reached. Logins disabled.\n"),
+ MAX_LOGINS);
+ }
+ }
+ /* give players a message on connection */
+ if (!options.login_allow || !under_limit ||
+ (Guest(player) && !options.guest_allow)) {
+ if (!options.login_allow) {
+ fcache_dump(d, fcache.down_fcache, NULL);
+ if (cf_downmotd_msg && *cf_downmotd_msg)
+ raw_notify(player, cf_downmotd_msg);
+ } else if (MAX_LOGINS && !under_limit) {
+ fcache_dump(d, fcache.full_fcache, NULL);
+ if (cf_fullmotd_msg && *cf_fullmotd_msg)
+ raw_notify(player, cf_fullmotd_msg);
+ }
+ if (!Can_Login(player)) {
+ /* when the connection has been refused, we want to update the
+ * LASTFAILED info on the player
+ */
+ check_lastfailed(player, d->addr);
+ return 0;
+ }
+ }
+ d->mailp = find_exact_starting_point(player);
+
+ /* check to see if this is a reconnect and also set DARK status */
+ is_hidden = Can_Hide(player) && Dark(player);
+ DESC_ITER_CONN(tmpd) {
+ if (tmpd->player == player) {
+ num++;
+ if (is_hidden)
+ tmpd->hide = 1;
+ }
+ }
+ /* give permanent text messages */
+ if (isnew)
+ fcache_dump(d, fcache.newuser_fcache, NULL);
+ if (num == 1)
+ fcache_dump(d, fcache.motd_fcache, NULL);
+ if (Guest(player))
+ fcache_dump(d, fcache.guest_fcache, NULL);
+
+ if (ModTime(player))
+ notify_format(player, T("%ld failed connections since last login."),
+ ModTime(player));
+ ModTime(player) = (time_t) 0;
+ announce_connect(player, isnew, num); /* broadcast connect message */
+ check_last(player, d->addr, d->ip); /* set Last, Lastsite, give paycheck */
+ /* Check folder 0, not silently (i.e. Report lack of mail, too) */
+ queue_eol(d);
+ if (command_check_byname(player, "@MAIL"))
+ check_mail(player, 0, 0);
+ set_player_folder(player, 0);
+ do_look_around(player);
+ if (Haven(player))
+ notify(player, T("Your HAVEN flag is set. You cannot receive pages."));
+ local_connect(player, isnew, num);
+ return 1;
+}
+
+static int
+check_connect(DESC *d, const char *msg)
+{
+ char command[MAX_COMMAND_LEN];
+ char user[MAX_COMMAND_LEN];
+ char password[MAX_COMMAND_LEN];
+ char errbuf[BUFFER_LEN];
+ dbref player;
+
+ parse_connect(msg, command, user, password);
+
+ if (string_prefix("connect", command)) {
+ if ((player =
+ connect_player(user, password, d->addr, d->ip, errbuf)) == NOTHING) {
+ queue_string_eol(d, errbuf);
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Failed connect to '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ } else {
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Connected to %s(#%d) in %s(#%d)"),
+ d->descriptor, d->addr, d->ip, Name(player), player,
+ Name(Location(player)), Location(player));
+ /* Check if we're fake siting this guy.. */
+ if(has_flag_by_name(player, "WEIRDSITE", TYPE_PLAYER)) {
+ ATTR *a;
+ a = atr_get(player, "LASTSITE");
+ strncpy(d->addr, !a ? "localhost" : atr_value(a), 100);
+ }
+
+ if ((dump_messages(d, player, 0)) == 0) {
+ d->connected = 2;
+ return 0;
+ }
+ }
+
+ } else if (!strcasecmp(command, "cd")) {
+ if ((player =
+ connect_player(user, password, d->addr, d->ip, errbuf)) == NOTHING) {
+ queue_string_eol(d, errbuf);
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Failed connect to '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ } else {
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Connected dark to %s(#%d) in %s(#%d)"),
+ d->descriptor, d->addr, d->ip, Name(player), player,
+ Name(Location(player)), Location(player));
+ /* Set player dark */
+ d->connected = 1;
+ d->player = player;
+ set_flag(player, player, "DARK", 0, 0, 0);
+ if ((dump_messages(d, player, 0)) == 0) {
+ d->connected = 2;
+ return 0;
+ }
+ }
+
+ } else if (!strcasecmp(command, "cv")) {
+ if ((player =
+ connect_player(user, password, d->addr, d->ip, errbuf)) == NOTHING) {
+ queue_string_eol(d, errbuf);
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Failed connect to '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ } else {
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Connected to %s(#%d) in %s(#%d)"),
+ d->descriptor, d->addr, d->ip, Name(player), player,
+ Name(Location(player)), Location(player));
+ /* Set player !dark */
+ d->connected = 1;
+ d->player = player;
+ set_flag(player, player, "DARK", 1, 0, 0);
+ if ((dump_messages(d, player, 0)) == 0) {
+ d->connected = 2;
+ return 0;
+ }
+ }
+
+ } else if (!strcasecmp(command, "ch")) {
+ if ((player =
+ connect_player(user, password, d->addr, d->ip, errbuf)) == NOTHING) {
+ queue_string_eol(d, errbuf);
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Failed connect to '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ } else {
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Connected hidden to %s(#%d) in %s(#%d)"),
+ d->descriptor, d->addr, d->ip, Name(player), player,
+ Name(Location(player)), Location(player));
+ /* Set player hidden */
+ d->connected = 1;
+ d->player = player;
+ if (Can_Hide(player))
+ d->hide = 1;
+ if ((dump_messages(d, player, 0)) == 0) {
+ d->connected = 2;
+ return 0;
+ }
+ }
+
+ } else if (string_prefix("create", command)) {
+ if (!Site_Can_Create(d->addr) || !Site_Can_Create(d->ip)) {
+ fcache_dump(d, fcache.register_fcache, NULL);
+ if (!Deny_Silent_Site(d->addr, AMBIGUOUS)
+ && !Deny_Silent_Site(d->ip, AMBIGUOUS)) {
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Refused create for '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ }
+ return 0;
+ }
+ if (!options.login_allow || !options.create_allow) {
+ if (!options.login_allow)
+ fcache_dump(d, fcache.down_fcache, NULL);
+ else
+ fcache_dump(d, fcache.register_fcache, NULL);
+ do_rawlog(LT_CONN,
+ "REFUSED CREATION for %s from %s on descriptor %d.\n",
+ user, d->addr, d->descriptor);
+ return 0;
+ } else if (MAX_LOGINS && !under_limit) {
+ fcache_dump(d, fcache.full_fcache, NULL);
+ do_rawlog(LT_CONN,
+ "REFUSED CREATION for %s from %s on descriptor %d.\n",
+ user, d->addr, d->descriptor);
+ return 0;
+ }
+ player = create_player(user, password, d->addr, d->ip);
+ if (player == NOTHING) {
+ queue_string_eol(d, T(create_fail));
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Failed create for '%s' (bad name)."),
+ d->descriptor, d->addr, d->ip, user);
+ } else if (player == AMBIGUOUS) {
+ queue_string_eol(d, T(password_fail));
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Failed create for '%s' (bad password)."),
+ d->descriptor, d->addr, d->ip, user);
+ } else {
+ do_log(LT_CONN, 0, 0, "[%d/%s/%s] Created %s(#%d)",
+ d->descriptor, d->addr, d->ip, Name(player), player);
+ if ((dump_messages(d, player, 1)) == 0) {
+ d->connected = 2;
+ return 0;
+ }
+ } /* successful player creation */
+
+ } else if (string_prefix("register", command)) {
+ if (!Site_Can_Register(d->addr) || !Site_Can_Register(d->ip)) {
+ fcache_dump(d, fcache.register_fcache, NULL);
+ if (!Deny_Silent_Site(d->addr, AMBIGUOUS)
+ && !Deny_Silent_Site(d->ip, AMBIGUOUS)) {
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Refused registration (bad site) for '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ }
+ return 0;
+ }
+ if (!options.create_allow) {
+ fcache_dump(d, fcache.register_fcache, NULL);
+ do_rawlog(LT_CONN,
+ "Refused registration (creation disabled) for %s from %s on descriptor %d.\n",
+ user, d->addr, d->descriptor);
+ return 0;
+ }
+ if ((player = email_register_player(user, password, d->addr, d->ip)) ==
+ NOTHING) {
+ queue_string_eol(d, T(register_fail));
+ do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Failed registration for '%s'."),
+ d->descriptor, d->addr, d->ip, user);
+ } else {
+ queue_string_eol(d, T(register_success));
+ do_log(LT_CONN, 0, 0, "[%d/%s/%s] Registered %s(#%d) to %s",
+ d->descriptor, d->addr, d->ip, Name(player), player, password);
+ }
+ /* Whether it succeeds or fails, leave them connected */
+
+ } else {
+ /* invalid command, just repeat login screen */
+ welcome_user(d);
+ }
+ /* If they were in a program, get them back in it */
+ if(InProg(d->player)) {
+ queue_string(d, "Loading @Program onto Descriptor....\r\n");
+ prog_load_desc(d);
+ }
+
+ return 1;
+}
+
+static void
+parse_connect(const char *msg1, char *command, char *user, char *pass)
+{
+ unsigned char *p;
+ unsigned const char *msg = (unsigned const char *) msg1;
+
+ while (*msg && isspace(*msg))
+ msg++;
+ p = (unsigned char *) command;
+ while (*msg && isprint(*msg) && !isspace(*msg))
+ *p++ = *msg++;
+ *p = '\0';
+ while (*msg && isspace(*msg))
+ msg++;
+ p = (unsigned char *) user;
+
+ if (PLAYER_NAME_SPACES && *msg == '\"') {
+ for (; *msg && ((*msg == '\"') || isspace(*msg)); msg++) ;
+ while (*msg && (*msg != '\"')) {
+ while (*msg && !isspace(*msg) && (*msg != '\"'))
+ *p++ = *msg++;
+ if (*msg == '\"') {
+ msg++;
+ while (*msg && isspace(*msg))
+ msg++;
+ break;
+ }
+ while (*msg && isspace(*msg))
+ msg++;
+ if (*msg && (*msg != '\"'))
+ *p++ = ' ';
+ }
+ } else
+ while (*msg && isprint(*msg) && !isspace(*msg))
+ *p++ = *msg++;
+
+ *p = '\0';
+ while (*msg && isspace(*msg))
+ msg++;
+ p = (unsigned char *) pass;
+ while (*msg && isprint(*msg) && !isspace(*msg))
+ *p++ = *msg++;
+ *p = '\0';
+}
+
+static void
+close_sockets(void)
+{
+ DESC *d, *dnext;
+
+ for (d = descriptor_list; d; d = dnext) {
+ dnext = d->next;
+ if(d->descriptor == 0) {
+ write(STDOUT_FILENO, T(shutdown_message), strlen(T(shutdown_message)));
+ write(STDOUT_FILENO, "\r\n", 2);
+ } else {
+ send(d->descriptor, T(shutdown_message), strlen(T(shutdown_message)), 0);
+ send(d->descriptor, "\r\n", 2, 0);
+ if (shutdown(d->descriptor, 2) < 0)
+ perror("shutdown");
+ closesocket(d->descriptor);
+ }
+ }
+}
+
+/** Give everyone the boot.
+ */
+void
+emergency_shutdown(void)
+{
+ close_sockets();
+}
+
+/** Disconnect a descriptor.
+ * \param d pointer to descriptor to disconnect.
+ */
+void
+boot_desc(DESC *d)
+{
+ shutdownsock(d);
+}
+
+/** Given a player dbref, return the player's first connected descriptor.
+ * \param player dbref of player.
+ * \return pointer to player's first connected descriptor, or NULL.
+ */
+DESC *
+player_desc(dbref player)
+{
+ DESC *d;
+ for (d = descriptor_list; d; d = d->next) {
+ if (d->connected && (d->player == player)) {
+ return d;
+ }
+ }
+ return (DESC *) NULL;
+}
+
+/** Page a specified socket.
+ * \param player the enactor.
+ * \param pc string containing port number to send message to.
+ * \param message message to send.
+ */
+void
+do_page_port(dbref player, const char *pc, const char *message)
+{
+ int p, key;
+ DESC *d;
+ const char *gap;
+ char tbuf[BUFFER_LEN], *tbp = tbuf;
+ dbref target = NOTHING;
+
+ if (!Admin(player)) {
+ notify(player, T("Permission denied."));
+ return;
+ }
+ p = atoi(pc);
+ if (p <= 0) {
+ notify(player, T("That's not a port number."));
+ return;
+ }
+
+ if (!message || !*message) {
+ notify(player, T("What do you want to page with?"));
+ return;
+ }
+
+ gap = " ";
+ switch (*message) {
+ case SEMI_POSE_TOKEN:
+ gap = "";
+ case POSE_TOKEN:
+ key = 1;
+ break;
+ default:
+ key = 3;
+ break;
+ }
+
+ d = port_desc(p);
+ if (!d) {
+ notify(player, T("That port's not active."));
+ return;
+ }
+ if (d->connected)
+ target = d->player;
+ switch (key) {
+ case 1:
+ safe_format(tbuf, &tbp, T("From afar, %s%s%s"), Name(player), gap,
+ message + 1);
+ notify_format(player, T("Long distance to %s: %s%s%s"),
+ target != NOTHING ? Name(target) :
+ T("a connecting player"), Name(player), gap, message + 1);
+ break;
+ case 3:
+ safe_format(tbuf, &tbp, T("%s pages: %s"), Name(player), message);
+ notify_format(player, T("You paged %s with '%s'."),
+ target != NOTHING ? Name(target) :
+ T("a connecting player"), message);
+ break;
+ }
+ *tbp = '\0';
+ if (target != NOTHING)
+ page_return(player, target, "Idle", "IDLE", NULL);
+ if (Typeof(player) != TYPE_PLAYER && Nospoof(target))
+ queue_string_eol(d, tprintf("[#%d] %s", player, tbuf));
+ else
+ queue_string_eol(d, tbuf);
+}
+
+
+/** Return an inactive descriptor, as long as there's more than
+ * one descriptor connected. Used for boot/me.
+ * \param player player to find an inactive descriptor for.
+ * \return pointer to player's inactive descriptor, or NULL.
+ */
+DESC *
+inactive_desc(dbref player)
+{
+ DESC *d, *in = NULL;
+ time_t now;
+ int numd = 0;
+ now = mudtime;
+ DESC_ITER_CONN(d) {
+ if (d->player == player) {
+ numd++;
+ if (now - d->last_time > 60)
+ in = d;
+ }
+ }
+ if (numd > 1)
+ return in;
+ else
+ return (DESC *) NULL;
+}
+
+/** Given a port (a socket number), return the descriptor.
+ * \param port port (socket file descriptor number).
+ * \return pointer to descriptor associated with the port.
+ */
+DESC *
+port_desc(int port)
+{
+ DESC *d;
+ for (d = descriptor_list; (d); d = d->next) {
+ if (d->descriptor == port) {
+ return d;
+ }
+ }
+ return (DESC *) NULL;
+}
+
+/** Given a port, find the matching player dbref.
+ * \param port (socket file descriptor number).
+ * \return dbref of connected player using that port, or NOTHING.
+ */
+dbref
+find_player_by_desc(int port)
+{
+ DESC *d;
+ for (d = descriptor_list; (d); d = d->next) {
+ if (d->connected && (d->descriptor == port)) {
+ return d->player;
+ }
+ }
+
+ /* didn't find anything */
+ return NOTHING;
+}
+
+
+#ifndef WIN32
+/** Handler for SIGINT. Note that we've received it, and reinstall.
+ * \param sig signal caught.
+ */
+void
+signal_shutdown(int sig __attribute__ ((__unused__)))
+{
+ signal_shutdown_flag = 1;
+ reload_sig_handler(SIGINT, signal_shutdown);
+}
+
+/** Handler for SIGUSR2. Note that we've received it, and reinstall
+ * \param sig signal caught.
+ */
+void
+signal_dump(int sig __attribute__ ((__unused__)))
+{
+ signal_dump_flag = 1;
+ reload_sig_handler(SIGUSR2, signal_dump);
+}
+#endif
+
+/** A general handler to puke and die.
+ * \param sig signal caught.
+ */
+void
+bailout(int sig)
+{
+ mush_panicf(T("BAILOUT: caught signal %d"), sig);
+}
+
+#ifndef WIN32
+/** Reap child processes, notably info_slaves and forking dumps,
+ * when we receive a SIGCHLD signal. Don't fear this function. :)
+ * \param sig signal caught.
+ */
+void
+reaper(int sig __attribute__ ((__unused__)))
+{
+ WAIT_TYPE my_stat;
+ Pid_t pid;
+
+#ifdef HAS_WAITPID
+ while ((pid = waitpid(-1, &my_stat, WNOHANG)) > 0)
+#else
+ while ((pid = wait3(&my_stat, WNOHANG, 0)) > 0)
+#endif
+ {
+ if (forked_dump_pid > -1 && pid == forked_dump_pid) {
+ /* Most failures are handled by the forked mush already */
+ if (WIFSIGNALED(my_stat)) {
+ do_rawlog(LT_ERR, T("ERROR! forking dump exited with signal %d"),
+ WTERMSIG(my_stat));
+ } else if (WIFEXITED(my_stat) && WEXITSTATUS(my_stat) == 0) {
+ time(&globals.last_dump_time);
+ if (DUMP_NOFORK_COMPLETE && *DUMP_NOFORK_COMPLETE)
+ flag_broadcast(0, 0, "%s", DUMP_NOFORK_COMPLETE);
+
+ }
+ forked_dump_pid = -1;
+ }
+ }
+ reload_sig_handler(SIGCHLD, reaper);
+}
+#endif /* !WIN32 */
+
+
+static void
+dump_info(DESC *call_by)
+{
+ int count = 0;
+ DESC *d;
+ queue_string_eol(call_by, tprintf("### Begin INFO %s", INFO_VERSION));
+
+ /* Count connected players */
+ for (d = descriptor_list; d; d = d->next) {
+ if (d->connected) {
+ if (!GoodObject(d->player))
+ continue;
+ if (COUNT_ALL || !Hidden(d) || call_by->player == d->player )
+ count++;
+ }
+ }
+ queue_string_eol(call_by, tprintf("Name: %s", options.mud_name));
+ queue_string_eol(call_by,
+ tprintf("Uptime: %s", show_time(globals.first_start_time, 0)));
+ queue_string_eol(call_by, tprintf("Connected: %d", count));
+ queue_string_eol(call_by, tprintf("Size: %d", db_top));
+ queue_string_eol(call_by, tprintf("Version: CobraMUSH v%s [%s]", VERSION,
+ VBRANCH));
+#ifdef PATCHES
+ queue_string_eol(call_by, tprintf("Patches: %s", PATCHES));
+#endif
+ queue_string_eol(call_by, "### End INFO");
+}
+
+static void
+dump_users(DESC *call_by, char *match, int doing)
+ /* doing: 0 if normal WHO, 1 if DOING, 2 if SESSION */
+{
+ DESC *d;
+#ifdef COLOREDWHO
+ int tcount = 0;
+#endif
+ int count = 0;
+ time_t now;
+ char tbuf1[BUFFER_LEN];
+ char tbuf2[BUFFER_LEN];
+ int csite;
+
+ if (!GoodObject(call_by->player)) {
+ do_log(LT_ERR, 0, 0, T("Bogus caller #%d of dump_users"), call_by->player);
+ return;
+ }
+ while (*match && *match == ' ')
+ match++;
+ now = mudtime;
+
+ /* If an admin types "DOING" it gives him the normal player WHO,
+ * BUT flags are not shown. Privileged WHO does not show @doings.
+ */
+
+ if (SUPPORT_PUEBLO && (call_by->conn_flags & CONN_HTML)) {
+ queue_newwrite(call_by, (unsigned char *) "<img xch_mode=html>", 19);
+ queue_newwrite(call_by, (unsigned char *) "<PRE>", 5);
+ }
+
+ if ((doing == 1) || !call_by->player || !Priv_Who(call_by->player)) {
+ if (poll_msg[0] == '\0')
+ strcpy(poll_msg, "Doing");
+ if (ShowAnsi(call_by->player))
+ sprintf(tbuf2, "%s%-16s %4s %10s %6s %s%s\n", ANSI_HILITE,
+ T("Player Name"), T("Aff"), T("On For"), T("Idle"), poll_msg, ANSI_NORMAL);
+ else
+ sprintf(tbuf2, "%-16s %4s %10s %6s %s\n",
+ T("Player Name"), T("Aff"), T("On For"), T("Idle"), poll_msg);
+ queue_string(call_by, tbuf2);
+ } else if (doing == 2) {
+ sprintf(tbuf2, "%s%-16s %6s %9s %5s %5s Des Sent Recv Pend%s\n", ShowAnsi(call_by->player) ? ANSI_HILITE :
+ "", T("Player Name"), T("Loc #"), T("On For"), T("Idle"), T("Cmds"),
+ ShowAnsi(call_by->player) ? ANSI_NORMAL : "");
+ queue_string(call_by, tbuf2);
+ } else {
+ sprintf(tbuf2, "%s%-16s %6s %9s %5s %5s Des Host%s\n", ShowAnsi(call_by->player) ? ANSI_HILITE : "",
+ T("Player Name"), T("Loc #"), T("On For"), T("Idle"), T("Cmds"), ShowAnsi(call_by->player) ? ANSI_NORMAL : "");
+ queue_string(call_by, tbuf2);
+ }
+
+ for (d = descriptor_list; d; d = d->next) {
+ if (d->connected) {
+ if (!GoodObject(d->player))
+ continue;
+ if (COUNT_ALL || (!Hidden(d) || call_by->player == d->player
+ || (call_by->player && Priv_Who(call_by->player)))) {
+ count++;
+#ifdef COLOREDWHO
+ tcount++;
+#endif
+ }
+ if (match && !(string_prefix(Name(d->player), match)))
+ continue;
+ csite = CanSee(call_by->player, d->player);
+
+ if (call_by->connected && doing == 0 && call_by->player
+ && Priv_Who(call_by->player)) {
+ if (Hidden(d) && !csite)
+ continue;
+
+ sprintf(tbuf1, "%-16s %6s %9s %5s %4d %3d%c %s", tprintf("%s%s", Name(d->player), InProg(d->player) ? "(P)" : ""),
+ Can_Locate(call_by->player, d->player) ? unparse_dbref(Location(d->player)) : "#-1",
+ time_format_1(now - d->connected_at),
+ time_format_2(now - d->last_time), csite ? d->cmds : 0,
+ csite ? d->descriptor : 0,
+ ' ',
+ csite ? d->addr : "---");
+ tbuf1[78] = '\0';
+ if (Dark(d->player)) {
+ tbuf1[71] = '\0';
+ strcat(tbuf1, " (Dark)");
+ } else if (Hidden(d)) {
+ tbuf1[71] = '\0';
+ strcat(tbuf1, " (Hide)");
+ }
+ } else if (call_by->connected && doing == 2 && call_by->player
+ && Priv_Who(call_by->player)) {
+ sprintf(tbuf1, "%-16s %6s %9s %5s %5d %3d%c %5lu %7lu %5d",
+ tprintf("%s%s", Name(d->player), InProg(d->player) ? "(P)" : ""),
+ Can_Locate(call_by->player, d->player) ? unparse_dbref(Location(d->player)) : "#-1",
+ time_format_1(now - d->connected_at),
+ time_format_2(now - d->last_time), csite ? d->cmds : 0,
+ csite ? d->descriptor : 0,
+ ' ',
+
+ csite ? d->input_chars : 0, csite ? d->output_chars : 0,
+ csite ? d->output_size : 0);
+ } else {
+ if (!Hidden(d)
+ || call_by->player == d->player ||
+ (call_by->player && Priv_Who(call_by->player) && (doing))) {
+ sprintf(tbuf1, "%-16s %4s %10s %4s%c %s", tprintf("%s%s", Name(d->player), InProg(d->player) ? "(P)" : ""), empabb(d->player),
+ time_format_1(now - d->connected_at),
+ time_format_2(now - d->last_time),
+ (Dark(d->player) ? 'D' : (Hidden(d) ? 'H' : ' '))
+ , d->doing);
+ }
+ }
+
+ if (!Hidden(d) || (call_by->player && Priv_Who(call_by->player))) {
+#ifdef COLOREDWHO
+ if(ShowAnsiColor(call_by->player))
+ queue_string(call_by, tprintf("%s%s%s%s%s", ANSI_NORMAL, (tcount % 2 ? "" : ANSI_HILITE),
+ (tcount % 2 ? ANSI_CYAN : ANSI_WHITE),
+ tbuf1, ANSI_NORMAL));
+
+ else
+#endif
+ queue_string(call_by, tbuf1);
+ queue_newwrite(call_by, (unsigned char *) "\r\n", 2);
+ }
+ } else if (call_by->player && Priv_Who(call_by->player) && doing != 1
+ && (!match || !*match)) {
+#ifdef COLOREDWHO
+ tcount++;
+#endif
+ if (doing == 0) {
+ /* Privileged WHO for non-logged in connections */
+ sprintf(tbuf1, "%-16s %6s %9s %5s %4d %3d%c %s", T("Connecting..."),
+ "#-1",
+ time_format_1(now - d->connected_at),
+ time_format_2(now - d->last_time), d->cmds, d->descriptor,
+ ' ',
+
+ d->addr);
+ tbuf1[78] = '\0';
+ } else {
+ /* SESSION for non-logged in connections */
+ sprintf(tbuf1, "%-16s %5s %9s %5s %5d %3d%c %5lu %7lu %5d",
+ T("Connecting..."), "#-1",
+ time_format_1(now - d->connected_at),
+ time_format_2(now - d->last_time), d->cmds, d->descriptor,
+ ' ',
+ d->input_chars, d->output_chars, d->output_size);
+ }
+#ifdef COLOREDWHO
+ if(ShowAnsiColor(call_by->player))
+ queue_string(call_by, tprintf("%s%s%s%s%s", ANSI_NORMAL,tcount % 2 ? "" : ANSI_HILITE,
+ tcount % 2 ? ANSI_CYAN : ANSI_WHITE, tbuf1, ANSI_NORMAL));
+ else
+#endif
+ queue_string(call_by, tbuf1);
+ queue_newwrite(call_by, (unsigned char *) "\r\n", 2);
+ }
+ }
+ switch (count) {
+ case 0:
+ strcpy(tbuf1, T("There are no players connected."));
+ break;
+ case 1:
+ strcpy(tbuf1, T("There is 1 player connected."));
+ break;
+ default:
+ sprintf(tbuf1, T("There are %d players connected."), count);
+ break;
+ }
+
+#ifdef COLOREDWHO
+ if(ShowAnsiColor(call_by->player))
+ queue_string(call_by, tprintf("%s%s%s%s%s", ANSI_NORMAL, (tcount+1) % 2 ? "" : ANSI_HILITE ,
+ (tcount+1) % 2 ? ANSI_CYAN : ANSI_WHITE, tbuf1, ANSI_NORMAL));
+ else
+#endif
+ queue_string(call_by, tbuf1);
+ if (SUPPORT_PUEBLO && (call_by->conn_flags & CONN_HTML)) {
+ queue_newwrite(call_by, (unsigned char *) "\n</PRE>\n", 8);
+ queue_newwrite(call_by, (unsigned char *) "<img xch_mode=purehtml>", 23);
+ } else
+ queue_newwrite(call_by, (unsigned char *) "\r\n", 2);
+}
+
+static const char *
+time_format_1(long dt)
+{
+ register struct tm *delta;
+ time_t holder; /* A hack for 64bit SGI */
+ static char buf[64];
+ if (dt < 0)
+ dt = 0;
+ holder = (time_t) dt;
+ delta = gmtime(&holder);
+ 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(long dt)
+{
+ register struct tm *delta;
+ static char buf[64];
+ if (dt < 0)
+ dt = 0;
+
+ delta = gmtime((time_t *) & 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;
+}
+
+/* connection messages
+ * isnew: newly creaetd or not?
+ * num: how many times connected?
+ */
+void
+announce_connect(dbref player, int isnew, int num)
+{
+ dbref loc;
+ char tbuf1[BUFFER_LEN];
+ dbref zone;
+ dbref obj;
+ int j;
+
+ set_flag_internal(player, "CONNECTED");
+
+ if (isnew) {
+ /* A brand new player created. */
+ sprintf(tbuf1, T("%s created."), Name(player));
+ flag_broadcast(0, "MONITOR", "%s %s", T("GAME:"), tbuf1);
+ }
+
+ /* Redundant, but better for translators */
+ if (Dark(player)) {
+ sprintf(tbuf1, (num > 1) ? T("%s has DARK-reconnected.") :
+ T("%s has DARK-connected."), Name(player));
+ } else if (hidden(player)) {
+ sprintf(tbuf1, (num > 1) ? T("%s has HIDDEN-reconnected.") :
+ T("%s has HIDDEN-connected."), Name(player));
+ } else {
+ sprintf(tbuf1, (num > 1) ? T("%s has reconnected.") :
+ T("%s has connected."), Name(player));
+ }
+
+ /* send out messages */
+ if (!Dark(player))
+ flag_broadcast(0, "MONITOR", "%s %s", T("GAME:"), tbuf1);
+
+#ifdef CHAT_SYSTEM
+ if (ANNOUNCE_CONNECTS)
+ chat_player_announce(player, tbuf1, (num == 1));
+#endif /* CHAT_SYSTEM */
+
+ loc = Location(player);
+ if (!GoodObject(loc)) {
+ notify(player, T("You are nowhere!"));
+ return;
+ }
+ orator = player;
+
+ if (cf_motd_msg && *cf_motd_msg) {
+ raw_notify(player, cf_motd_msg);
+ }
+ raw_notify(player, " ");
+
+ if(ANNOUNCE_CONNECTS)
+ notify_except(Contents(player), player, tbuf1, 0);
+ /* added to allow player's inventory to hear a player connect */
+ if(ANNOUNCE_CONNECTS)
+ if(!Dark(player))
+ notify_except(Contents(loc), player, tbuf1, NA_INTER_PRESENCE);
+
+ /* clear the environment for possible actions */
+ for (j = 0; j < 10; j++)
+ global_eval_context.wnxt[j] = NULL;
+ for (j = 0; j < NUMQ; j++)
+ global_eval_context.rnxt[j] = NULL;
+
+ /* do the person's personal connect action */
+ (void) queue_attribute(player, "ACONNECT", player);
+ if (ROOM_CONNECTS) {
+ /* Do the room the player connected into */
+ if (IsRoom(loc) || IsThing(loc)) {
+ (void) queue_attribute(loc, "ACONNECT", player);
+ }
+ }
+ /* do the person's division */
+ if (GoodObject(SDIV(player).object))
+ (void) queue_attribute(SDIV(player).object, "ACONNECT", player);
+ /* do the zone of the player's location's possible aconnect */
+ if ((zone = Zone(loc)) != NOTHING) {
+ switch (Typeof(zone)) {
+ case TYPE_THING:
+ (void) queue_attribute(zone, "ACONNECT", player);
+ break;
+ case TYPE_ROOM:
+ /* check every object in the room for a connect action */
+ DOLIST(obj, Contents(zone)) {
+ (void) queue_attribute(obj, "ACONNECT", player);
+ }
+ break;
+ default:
+ do_log(LT_ERR, 0, 0,
+ T("Invalid zone #%d for %s(#%d) has bad type %d"), zone,
+ Name(player), player, Typeof(zone));
+ }
+ }
+ /* now try the master room */
+ DOLIST(obj, Contents(MASTER_ROOM)) {
+ (void) queue_attribute(obj, "ACONNECT", player);
+ }
+}
+
+void
+announce_disconnect(dbref player)
+{
+ dbref loc;
+ int num;
+ DESC *d;
+ char tbuf1[BUFFER_LEN];
+ dbref zone, obj;
+ int j;
+
+ loc = Location(player);
+ if (!GoodObject(loc))
+ return;
+
+ orator = player;
+
+ for (num = 0, d = descriptor_list; d; d = d->next)
+ if (d->connected && (d->player == player))
+ num++;
+ if (num < 2) {
+ sprintf(tbuf1, T("%s has disconnected."), Name(player));
+
+ if (ANNOUNCE_CONNECTS) {
+ if (!Dark(player))
+ notify_except(Contents(loc), player, tbuf1, NA_INTER_PRESENCE);
+ /* notify contents */
+ notify_except(Contents(player), player, tbuf1, 0);
+ }
+
+ /* clear the environment for possible actions */
+ for (j = 0; j < 10; j++)
+ global_eval_context.wnxt[j] = NULL;
+ for (j = 0; j < NUMQ; j++)
+ global_eval_context.rnxt[j] = NULL;
+
+ /* Setup all connect information as info to pass */
+ (void) queue_attribute(player, "ADISCONNECT", player);
+ /* do the person's division */
+ if (GoodObject(SDIV(player).object))
+ (void) queue_attribute(SDIV(player).object, "ACONNECT", player);
+ if (ROOM_CONNECTS)
+ if (IsRoom(loc) || IsThing(loc)) {
+ (void) queue_attribute(loc, "ADISCONNECT", player);
+ }
+ /* do the zone of the player's location's possible adisconnect */
+ if ((zone = Zone(loc)) != NOTHING) {
+ switch (Typeof(zone)) {
+ case TYPE_DIVISION:
+ case TYPE_THING:
+ (void) queue_attribute(zone, "ADISCONNECT", player);
+ break;
+ case TYPE_ROOM:
+ /* check every object in the room for a connect action */
+ DOLIST(obj, Contents(zone)) {
+ (void) queue_attribute(obj, "ADISCONNECT", player);
+ }
+ break;
+ default:
+ do_log(LT_ERR, 0, 0,
+ T("Invalid zone #%d for %s(#%d) has bad type %d"), zone,
+ Name(player), player, Typeof(zone));
+ }
+ }
+ /* now try the master room */
+ DOLIST(obj, Contents(MASTER_ROOM)) {
+ (void) queue_attribute(obj, "ADISCONNECT", player);
+ }
+ clear_flag_internal(player, "CONNECTED");
+ (void) atr_add(player, "LASTLOGOUT", show_time(mudtime, 0), GOD, NOTHING);
+ } else {
+ /* note: when you partially disconnect, ADISCONNECTS are not executed */
+ sprintf(tbuf1, T("%s has partially disconnected."), Name(player));
+
+ if (ANNOUNCE_CONNECTS) {
+ if (!Dark(player))
+ notify_except(Contents(loc), player, tbuf1, NA_INTER_PRESENCE);
+ /* notify contents */
+ notify_except(Contents(player), player, tbuf1, 0);
+ }
+ }
+
+#ifdef CHAT_SYSTEM
+ if (ANNOUNCE_CONNECTS)
+ chat_player_announce(player, tbuf1, 0);
+#endif /* CHAT_SYSTEM */
+
+ /* Monitor broadcasts */
+ /* Redundant, but better for translators */
+ if (Dark(player)) {
+ sprintf(tbuf1, (num < 2) ? T("%s has DARK-disconnected.") :
+ T("%s has partially DARK-disconnected."), Name(player));
+ } else if (hidden(player)) {
+ sprintf(tbuf1, (num < 2) ? T("%s has HIDDEN-disconnected.") :
+ T("%s has partially HIDDEN-disconnected."), Name(player));
+ } else {
+ sprintf(tbuf1, (num < 2) ? T("%s has disconnected.") :
+ T("%s has partially disconnected."), Name(player));
+ }
+ if (!Dark(player))
+ flag_broadcast(0, "MONITOR", "%s %s", T("GAME:"), tbuf1);
+ if(Guest(player)) { /* queue for destruction */
+ set_flag_internal(player, "GOING");
+ set_flag_internal(player, "GOING_TWICE");
+ }
+ local_disconnect(player, num);
+}
+
+/** Set an motd message.
+ * \verbatim
+ * This implements @motd.
+ * \endverbatim
+ * \param player the enactor.
+ * \param key type of MOTD to set.
+ * \param message text to set the motd to.
+ */
+void
+do_motd(dbref player, enum motd_type key, const char *message)
+{
+
+ if (!Site(player) && key != MOTD_LIST) {
+ notify(player,
+ T
+ ("You may get 15 minutes of fame and glory in life, but not right now."));
+ return;
+ }
+ switch (key) {
+ case MOTD_MOTD:
+ strcpy(cf_motd_msg, message);
+ notify(player, T("Motd set."));
+ break;
+ case MOTD_DOWN:
+ strcpy(cf_downmotd_msg, message);
+ notify(player, T("Down motd set."));
+ break;
+ case MOTD_FULL:
+ strcpy(cf_fullmotd_msg, message);
+ notify(player, T("Full motd set."));
+ break;
+ case MOTD_LIST:
+ notify_format(player, "MOTD: %s", cf_motd_msg);
+ if (Site(player)) {
+ notify_format(player, T("Down MOTD: %s"), cf_downmotd_msg);
+ notify_format(player, T("Full MOTD: %s"), cf_fullmotd_msg);
+ }
+ }
+}
+
+/** Set a DOING message.
+ * \verbatim
+ * This implements @doing.
+ * \endverbatim
+ * \param player the enactor.
+ * \param message the message to set.
+ */
+void
+do_doing(dbref player, const char *message)
+{
+ char buf[MAX_COMMAND_LEN];
+ DESC *d;
+ int i;
+
+ if (!Connected(player)) {
+ /* non-connected things have no need for a doing */
+ notify(player, T("Why would you want to do that?"));
+ return;
+ }
+ strncpy(buf, remove_markup(message, NULL), DOING_LEN - 1);
+
+ /* now smash undesirable characters and truncate */
+ for (i = 0; i < DOING_LEN; i++) {
+ if ((buf[i] == '\r') || (buf[i] == '\n') ||
+ (buf[i] == '\t') || (buf[i] == BEEP_CHAR))
+ buf[i] = ' ';
+ }
+ buf[DOING_LEN - 1] = '\0';
+
+ /* set it */
+ for (d = descriptor_list; d; d = d->next)
+ if (d->connected && (d->player == player))
+ strcpy(d->doing, buf);
+ if (strlen(message) >= DOING_LEN) {
+ notify_format(player,
+ T("Doing set. %zu characters lost."),
+ strlen(message) - (DOING_LEN - 1));
+ } else
+ notify(player, T("Doing set."));
+}
+
+/** Set a poll message (which replaces "Doing" in the DOING output).
+ * \verbatim
+ * This implements @poll.
+ * \endverbatim
+ * \param player the enactor.
+ * \param message the message to set.
+ */
+void
+do_poll(dbref player, const char *message)
+{
+ int i;
+
+ if (!Change_Poll(player)) {
+ notify(player, T("Who do you think you are, Gallup?"));
+ return;
+ }
+ strncpy(poll_msg, remove_markup(message, NULL), DOING_LEN - 1);
+ for (i = 0; i < DOING_LEN; i++) {
+ if ((poll_msg[i] == '\r') || (poll_msg[i] == '\n') ||
+ (poll_msg[i] == '\t') || (poll_msg[i] == BEEP_CHAR))
+ poll_msg[i] = ' ';
+ }
+ poll_msg[DOING_LEN - 1] = '\0';
+
+ if (strlen(message) >= DOING_LEN) {
+ poll_msg[DOING_LEN - 1] = 0;
+ notify_format(player,
+ T("Poll set. %zu characters lost."),
+ strlen(message) - (DOING_LEN - 1));
+ } else
+ notify(player, T("Poll set."));
+ do_log(LT_WIZ, player, NOTHING, T("Poll Set to '%s'."), poll_msg);
+}
+
+/** Match the partial name of a connected player.
+ * \param match string to match.
+ * \return dbref of a unique connected player whose name partial-matches,
+ * AMBIGUOUS, or NOTHING.
+ */
+dbref
+short_page(const char *match)
+{
+ DESC *d;
+ dbref who1 = NOTHING;
+ int count = 0;
+
+ for (d = descriptor_list; d; d = d->next) {
+ if (d->connected) {
+ if (match && !string_prefix(Name(d->player), match))
+ continue;
+ if (!strcasecmp(Name(d->player), match)) {
+ count = 1;
+ who1 = d->player;
+ break;
+ }
+ if (who1 == NOTHING || d->player != who1) {
+ who1 = d->player;
+ count++;
+ }
+ }
+ }
+
+ if (count > 1)
+ return AMBIGUOUS;
+ else if (count == 0)
+ return NOTHING;
+
+ return who1;
+}
+
+/** Match the partial name of a connected player the enactor can see.
+ * \param player the enactor
+ * \param match string to match.
+ * \return dbref of a unique connected player whose name partial-matches,
+ * AMBIGUOUS, or NOTHING.
+ */
+dbref
+visible_short_page(dbref player, const char *match)
+{
+ dbref target;
+ target = short_page(match);
+ if (Priv_Who(player) || !GoodObject(target))
+ return target;
+ if (Dark(target) || (hidden(target) && !nearby(player, target)))
+ return NOTHING;
+ return target;
+}
+
+/* LWHO() function - really belongs elsewhere but needs stuff declared here */
+
+/* ARGSUSED */
+FUNCTION(fun_lwho)
+{
+ DESC *d;
+ dbref victim;
+ int first, powered = (*called_as == 'L') && Priv_Who(executor);
+
+ first = 1;
+ if(nargs && args[0] && *args[0]) {
+ if(!powered) {
+ safe_str(T(e_perm), buff, bp);
+ return;
+ }
+
+ if( (victim = noisy_match_result(executor, args[0], NOTYPE, MAT_EVERYTHING)) == 0) {
+ safe_str(T(e_notvis), buff, bp);
+ return;
+ }
+ if(!controls(executor, victim)) {
+ safe_str(T(e_perm), buff, bp);
+ return;
+ }
+ if(!Priv_Who(victim))
+ powered = 0;
+ } else victim = executor;
+
+ DESC_ITER_CONN(d) {
+ if (!Hidden(d) || (powered && CanSee(victim,d->player))) {
+ if (first)
+ first = 0;
+ else
+ safe_chr(' ', buff, bp);
+ safe_dbref(d->player, buff, bp);
+ }
+ }
+}
+
+
+/** Look up a DESC by character name or file descriptor.
+ * \param executor the dbref of the object calling the function calling this.
+ * \param name the name or descriptor to look up.
+ * \retval a pointer to the proper DESC, or NULL
+ */
+static DESC *
+lookup_desc(dbref executor, const char *name)
+{
+ DESC *d;
+
+ /* Given a file descriptor. See-all only. */
+ if (is_strict_integer(name)) {
+ int fd = parse_integer(name);
+ DESC_ITER_CONN(d) {
+ if (d->descriptor == fd) {
+ if (Priv_Who(executor) || d->player == executor
+ || (Inherit_Powers(executor) && Priv_Who(Owner(executor))))
+ return d;
+ else
+ return NULL;
+ }
+ }
+ return NULL;
+ } else { /* Look up player name */
+ DESC *match = NULL;
+ dbref target = lookup_player(name);
+ if (target == NOTHING) {
+ target = match_result(executor, name, TYPE_PLAYER,
+ MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
+ }
+ if (!GoodObject(target) || !Connected(target))
+ return NULL;
+ else {
+ /* walk the descriptor list looking for a match of a dbref */
+ DESC_ITER_CONN(d) {
+ if ((d->player == target) &&
+ (!Hidden(d) || Priv_Who(executor) ||
+ (Inherit_Powers(executor) && Priv_Who(Owner(executor)))) &&
+ (!match || (d->last_time > match->last_time)))
+ match = d;
+ }
+ return match;
+ }
+ }
+}
+
+/** Return the conn time of the longest-connected connection of a player.
+ * This function treats hidden connectios as nonexistent.
+ * \param player dbref of player to get ip for.
+ * \return connection time of player as an INT, or -1 if not found or hidden.
+ */
+int
+most_conn_time(dbref player)
+{
+ DESC *d, *match = NULL;
+ DESC_ITER_CONN(d) {
+ if ((d->player == player) && !Hidden(d) && (!match ||
+ (d->connected_at >
+ match->connected_at)))
+ match = d;
+ }
+ if (match) {
+ double result = difftime(mudtime, match->connected_at);
+ return (int) result;
+ } else
+ return -1;
+}
+
+/** Return the conn time of the longest-connected connection of a player.
+ * This function does includes hidden people.
+ * \param player dbref of player to get ip for.
+ * \return connection time of player as an INT, or -1 if not found.
+ */
+int
+most_conn_time_priv(dbref player)
+{
+ DESC *d, *match = NULL;
+ DESC_ITER_CONN(d) {
+ if ((d->player == player) && (!match ||
+ (d->connected_at > match->connected_at)))
+ match = d;
+ }
+ if (match) {
+ double result = difftime(mudtime, match->connected_at);
+ return (int) result;
+ } else
+ return -1;
+}
+
+/** Return the idle time of the least-idle connection of a player.
+ * This function treats hidden connections as nonexistant.
+ * \param player dbref of player to get time for.
+ * \return idle time of player as an INT, or -1 if not found or hidden.
+ */
+int
+least_idle_time(dbref player)
+{
+ DESC *d, *match = NULL;
+ DESC_ITER_CONN(d) {
+ if ((d->player == player) && !Hidden(d) &&
+ (!match || (d->last_time > match->last_time)))
+ match = d;
+ }
+ if (match) {
+ double result = difftime(mudtime, match->last_time);
+ return (int) result;
+ } else
+ return -1;
+}
+
+/** Return the idle time of the least-idle connection of a player.
+ * This function performs no permission checking.
+ * \param player dbref of player to get time for.
+ * \return idle time of player as an INT, or -1 if not found.
+ */
+int
+least_idle_time_priv(dbref player)
+{
+ DESC *d, *match = NULL;
+ DESC_ITER_CONN(d) {
+ if ((d->player == player) && (!match || (d->last_time > match->last_time)))
+ match = d;
+ }
+ if (match) {
+ double result = difftime(mudtime, match->last_time);
+ return (int) result;
+ } else
+ return -1;
+}
+
+/** Return the ip address of the least-idle connection of a player.
+ * This function performs no permission checking, and returns the
+ * pointer from the descriptor itself (so don't destroy the result!)
+ * \param player dbref of player to get ip for.
+ * \return IP address of player as a string or NULL if not found.
+ */
+char *
+least_idle_ip(dbref player)
+{
+ DESC *d, *match = NULL;
+ DESC_ITER_CONN(d) {
+ if ((d->player == player) && (!match || (d->last_time > match->last_time)))
+ match = d;
+ }
+ return match ? (match->ip) : NULL;
+}
+
+/** Return the hostname of the least-idle connection of a player.
+ * This function performs no permission checking, and returns a static
+ * string.
+ * \param player dbref of player to get ip for.
+ * \return hostname of player as a string or NULL if not found.
+ */
+char *
+least_idle_hostname(dbref player)
+{
+ DESC *d, *match = NULL;
+ static char hostname[101];
+ char *p;
+
+ DESC_ITER_CONN(d) {
+ if ((d->player == player) && (!match || (d->last_time > match->last_time)))
+ match = d;
+ }
+ if (!match)
+ return NULL;
+ strcpy(hostname, match->addr);
+ if ((p = strchr(hostname, '@'))) {
+ p++;
+ return p;
+ } else
+ return hostname;
+}
+
+/* ZWHO() function - really belongs in eval.c but needs stuff declared here */
+/* ARGSUSED */
+FUNCTION(fun_zwho)
+{
+ DESC *d;
+ dbref zone, victim;
+ int first;
+ int powered = (strcmp(called_as, "ZMWHO") && Priv_Who(executor) || (Inherit_Powers(executor) && Priv_Who(Owner(executor))));
+ first = 1;
+
+ zone = match_thing(executor, args[0]);
+
+ if (nargs == 1) {
+ victim = executor;
+ } else if ((nargs == 2) && powered) {
+ if ((victim = match_thing(executor, args[1])) == 0) {
+ safe_str(T(e_match), buff, bp);
+ return;
+ }
+ } else {
+ safe_str(T(e_perm), buff, bp);
+ return;
+ }
+
+ if (!GoodObject(zone) || !(eval_lock(victim, zone, Zone_Lock) || CanModify(victim,zone))) {
+ safe_str(T(e_perm), buff, bp);
+ return;
+ }
+ if ((getlock(zone, Zone_Lock) == TRUE_BOOLEXP) ||
+ (IsPlayer(zone) && !(has_flag_by_name(zone, "SHARED", TYPE_PLAYER)))) {
+ safe_str(T("#-1 INVALID ZONE."), buff, bp);
+ return;
+ }
+
+ /* Use lowest privilege for victim */
+ if (!Priv_Who(victim))
+ powered = 0;
+
+ DESC_ITER_CONN(d) {
+ if (!Hidden(d) || powered) {
+ if (Zone(Location(d->player)) == zone) {
+ if (first) {
+ first = 0;
+ } else {
+ safe_chr(' ', buff, bp);
+ }
+ safe_dbref(d->player, buff, bp);
+ }
+ }
+ }
+}
+
+/* ARGSUSED */
+FUNCTION(fun_doing)
+{
+ /* Gets a player's @doing */
+ DESC *d = lookup_desc(executor, args[0]);
+ if (d)
+ safe_str(d->doing, buff, bp);
+ else
+ safe_str("#-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_hostname)
+{
+ /* Gets a player's hostname */
+ DESC *d = lookup_desc(executor, args[0]);
+ if (d && (d->player == executor || Site(executor)))
+ safe_str(d->addr, buff, bp);
+ else
+ safe_str("#-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_ipaddr)
+{
+ /* Gets a player's IP address */
+ DESC *d = lookup_desc(executor, args[0]);
+ if (d && (d->player == executor || Site(executor)))
+ safe_str(d->ip, buff, bp);
+ else
+ safe_str("#-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_cmds)
+{
+ /* Gets a player's IP address */
+ DESC *d = lookup_desc(executor, args[0]);
+ if (d && (d->player == executor || Site(executor)))
+ safe_integer(d->cmds, buff, bp);
+ else
+ safe_integer(-1, buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_sent)
+{
+ /* Gets a player's bytes sent */
+ DESC *d = lookup_desc(executor, args[0]);
+ if (d && (d->player == executor || Site(executor)))
+ safe_integer(d->input_chars, buff, bp);
+ else
+ safe_integer(-1, buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_recv)
+{
+ /* Gets a player's bytes received */
+ DESC *d = lookup_desc(executor, args[0]);
+ if (d && (d->player == executor || Site(executor)))
+ safe_integer(d->output_chars, buff, bp);
+ else
+ safe_integer(-1, buff, bp);
+}
+
+FUNCTION(fun_poll)
+{
+ /* Gets the current poll */
+ if (poll_msg[0] == '\0')
+ strcpy(poll_msg, "Doing");
+
+ safe_str(poll_msg, buff, bp);
+}
+
+FUNCTION(fun_pueblo)
+{
+ /* Return the status of the pueblo flag on the least idle descriptor we
+ * find that matches the player's dbref.
+ */
+ DESC *match = lookup_desc(executor, args[0]);
+ if (match)
+ safe_boolean(match->conn_flags & CONN_HTML, buff, bp);
+ else
+ safe_str(T("#-1 NOT CONNECTED"), buff, bp);
+}
+
+FUNCTION(fun_ssl)
+{
+ /* Return the status of the ssl flag on the least idle descriptor we
+ * find that matches the player's dbref.
+ */
+ safe_boolean(0, buff, bp);
+}
+
+FUNCTION(fun_width)
+{
+ DESC *match;
+ if (!*args[0])
+ safe_str(T("#-1 FUNCTION REQUIRES ONE ARGUMENT"), buff, bp);
+ else if ((match = lookup_desc(executor, args[0])))
+ safe_integer(match->width, buff, bp);
+ else
+ safe_str("78", buff, bp);
+}
+
+FUNCTION(fun_height)
+{
+ DESC *match;
+ if (!*args[0])
+ safe_str(T("#-1 FUNCTION REQUIRES ONE ARGUMENT"), buff, bp);
+ else if ((match = lookup_desc(executor, args[0])))
+ safe_integer(match->height, buff, bp);
+ else
+ safe_str("24", buff, bp);
+}
+
+FUNCTION(fun_terminfo)
+{
+ DESC *match;
+ if (!*args[0])
+ safe_str(T("#-1 FUNCTION REQUIRES ONE ARGUMENT"), buff, bp);
+ else if ((match = lookup_desc(executor, args[0]))) {
+ if (match->player == executor || Site(executor)) {
+ safe_str(match->ttype, buff, bp);
+ if (match->conn_flags & CONN_HTML)
+ safe_str(" pueblo", buff, bp);
+ if (match->conn_flags & CONN_TELNET)
+ safe_str(" telnet", buff, bp);
+ } else
+ safe_str(T(e_perm), buff, bp);
+ } else
+ safe_str(T("#-1 NOT CONNECTED"), buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_idlesecs)
+{
+ /* returns the number of seconds a player has been idle, using
+ * their least idle connection
+ */
+
+ DESC *match = lookup_desc(executor, args[0]);
+ if (match)
+ safe_number(difftime(mudtime, match->last_time), buff, bp);
+ else
+ safe_str("-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_idle_average) {
+ DESC *match = lookup_desc(executor, args[0]);
+ double idle_time;
+
+ if(match) {
+ idle_time = difftime(mudtime, match->last_time);
+ if(idle_time >= 300)
+ safe_number(((match->idle_total + idle_time) / (match->unidle_times+1)), buff, bp);
+ else if(match->unidle_times == 0)
+ safe_number(0, buff, bp);
+ else
+ safe_number((match->idle_total / match->unidle_times), buff, bp);
+
+ } else
+ safe_str("-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_idle_total) {
+ DESC *match = lookup_desc(executor, args[0]);
+ double idle_time;
+
+ if(match) {
+ idle_time = difftime(mudtime, match->last_time);
+ safe_number(idle_time >= 300 ? (match->idle_total+idle_time) : match->idle_total, buff, bp);
+ } else
+ safe_str("-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_idle_times) {
+ DESC *match = lookup_desc(executor, args[0]);
+
+ if(match) {
+ safe_number((difftime(mudtime, match->last_time) >= 300) ?
+ (match->unidle_times+1) : match->unidle_times, buff, bp);
+ } else
+ safe_str("-1", buff, bp);
+}
+
+
+/* ARGSUSED */
+FUNCTION(fun_conn)
+{
+ /* returns the number of seconds a player has been connected, using
+ * their longest-connected descriptor
+ */
+
+ DESC *match = lookup_desc(executor, args[0]);
+ if (match)
+ safe_number(difftime(mudtime, match->connected_at), buff, bp);
+ else
+ safe_str("-1", buff, bp);
+}
+
+/* ARGSUSED */
+FUNCTION(fun_lports)
+{
+ DESC *d;
+ int first = 1;
+
+ if (!Priv_Who(executor)
+ && !(Inherit_Powers(executor) && Priv_Who(Owner(executor)))) {
+ safe_str(T(e_perm), buff, bp);
+ return;
+ }
+
+ DESC_ITER_CONN(d) {
+ if (first)
+ first = 0;
+ else
+ safe_chr(' ', buff, bp);
+ safe_integer(d->descriptor, buff, bp);
+ }
+}
+
+/* ARGSUSED */
+FUNCTION(fun_ports)
+{
+ /* returns a list of the network descriptors that a player is
+ * connected to
+ */
+
+ dbref target;
+ DESC *d;
+ int first;
+
+ target = lookup_player(args[0]);
+ if (target == NOTHING) {
+ target = match_result(executor, args[0], TYPE_PLAYER,
+ MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
+ }
+ if (target != executor && !Priv_Who(executor)
+ && !(Inherit_Powers(executor) && Priv_Who(Owner(executor)))) {
+ /* This should probably be a safe_str */
+ notify(executor, T("Permission denied."));
+ return;
+ }
+ if (!GoodObject(target) || !Connected(target)) {
+ return;
+ }
+ /* Walk descriptor chain. */
+ first = 1;
+ DESC_ITER_CONN(d) {
+ if (d->player == target) {
+ if (first)
+ first = 0;
+ else
+ safe_chr(' ', buff, bp);
+ safe_integer(d->descriptor, buff, bp);
+ }
+ }
+}
+
+
+/** Hide or unhide a player.
+ * Although hiding is a per-descriptor state, this function sets all of
+ * a player's connected descriptors to be hidden.
+ * \param player dbref of player to hide.
+ * \param hide if 1, hide; if 0, unhide.
+ */
+void
+hide_player(dbref player, int hide)
+{
+ DESC *d;
+ if (!Connected(player))
+ return;
+ if (!Can_Hide(player)) {
+ notify(player, T("Permission denied."));
+ return;
+ }
+ /* change status on WHO */
+ if (Can_Hide(player)) {
+ DESC_ITER_CONN(d) {
+ if (d->player == player)
+ d->hide = hide;
+ }
+ }
+ if (hide)
+ notify(player, T("You no longer appear on the WHO list."));
+ else
+ notify(player, T("You now appear on the WHO list."));
+}
+
+/** Perform the periodic check of inactive descriptors, and
+ * disconnect them or autohide them as appropriate.
+ */
+void
+inactivity_check(void)
+{
+ DESC *d, *nextd;
+ ATTR *a;
+ char tbuf[BUFFER_LEN];
+ time_t now, idle, idle_for, unconnected_idle;
+ if (!INACTIVITY_LIMIT && !UNCONNECTED_LIMIT)
+ return;
+ now = mudtime;
+ idle = INACTIVITY_LIMIT ? INACTIVITY_LIMIT : INT_MAX;
+ unconnected_idle = UNCONNECTED_LIMIT ? UNCONNECTED_LIMIT : INT_MAX;
+ for (d = descriptor_list; d; d = nextd) {
+ nextd = d->next;
+ idle_for = now - d->last_time;
+ /* If they've been connected for 60 seconds without getting a telnet-option
+ back, the client probably doesn't understand them */
+ if ((d->conn_flags & CONN_TELNET_QUERY) && idle_for > 60)
+ d->conn_flags &= ~CONN_TELNET_QUERY;
+ if(d->connected && GoodObject(d->player) && ((a = atr_get(d->player, "IDLE_TIMEOUT"))!=NULL)) {
+ memset(tbuf, '\0', BUFFER_LEN);
+ strncpy(tbuf, atr_value(a), BUFFER_LEN-1);
+ idle = atoi(tbuf);
+ if(idle > 0)
+ goto after_idle_atr_check;
+ }
+ idle = INACTIVITY_LIMIT ? INACTIVITY_LIMIT : INT_MAX;
+after_idle_atr_check:
+ if ((d->connected) ? (idle_for > idle ) : (idle_for > unconnected_idle)) {
+
+ if (!d->connected)
+ shutdownsock(d);
+ else if (!Can_Idle(d->player)) {
+
+ queue_string(d, T("\n*** Inactivity timeout ***\n"));
+ do_log(LT_CONN, 0, 0,
+ T("[%d/%s/%s] Logout by %s(#%d) <Inactivity Timeout>"),
+ d->descriptor, d->addr, d->ip, Name(d->player), d->player);
+ boot_desc(d);
+ } else if (Unfind(d->player)) {
+
+ if ((Can_Hide(d->player)) && (!Hidden(d))) {
+ queue_string(d,
+ T
+ ("\n*** Inactivity limit reached. You are now HIDDEN. ***\n"));
+ d->hide = 1;
+ }
+ }
+ }
+ }
+}
+
+
+/** Given a player dbref, return the player's hidden status.
+ * \param player dbref of player to check.
+ * \retval 1 player is hidden.
+ * \retval 0 player is not hidden.
+ */
+int
+hidden(dbref player)
+{
+ DESC *d;
+ DESC_ITER_CONN(d) {
+ if (d->player == player) {
+ if (Hidden(d))
+ return 1;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
+
+
+/** Return the mailp of the player closest in db# to player,
+ * or NULL if there's nobody on-line.
+ * In the current mail system, mail is stored in a linked list, sorted
+ * by recipient, which makes the most common operations (listing and reading
+ * your mail) fast. When a player first connects, we store (on the
+ * mailp element of their descriptor) a pointer to the beginning of
+ * their part of the linked list. Rather than search the whole linked
+ * list to find this location, we look at the mailp's of all the other
+ * connected players, and find the mailp of the player whose dbref
+ * is closest to the connecting player, and start our search from that
+ * point. This scales up nicely - as a mushes get larger, the linked
+ * list gets larger, but the more people connected at once, the faster
+ * the search for a newly connecting player's first mail.
+ * \param player player whose db# we want to get near.
+ * \return pointer to first mail of connected player with db# closest to
+ * player.
+ */
+MAIL *
+desc_mail(dbref player)
+{
+ DESC *d;
+ int i;
+ int diff = db_top;
+ static MAIL *mp;
+ mp = NULL;
+ DESC_ITER_CONN(d) {
+ i = abs(d->player - player);
+ if (i == 0)
+ return d->mailp;
+ if ((i < diff) && d->mailp) {
+ diff = i;
+ mp = d->mailp;
+ }
+ }
+ return mp;
+}
+
+/** Set a player's mail position on all their descriptors.
+ * \param player player to set mail position for.
+ * \param mp pointer to first mail in their list.
+ */
+void
+desc_mail_set(dbref player, MAIL *mp)
+{
+ DESC *d;
+ DESC_ITER_CONN(d) {
+ if (d->player == player)
+ d->mailp = mp;
+ }
+}
+
+/** Clear mail positions on all descriptors. Called from do_mail_nuke().
+ */
+void
+desc_mail_clear(void)
+{
+ DESC *d;
+ DESC_ITER_CONN(d) {
+ d->mailp = NULL;
+ }
+}
+
+
+
+
+#ifdef SUN_OS
+/* SunOS's implementation of stdio breaks when you get a file descriptor
+ * greater than 128. Brain damage, brain damage, brain damage!
+ *
+ * Our objective, therefore, is not to fix stdio, but to work around it,
+ * so that performance degrades semi-gracefully when you are using a lot
+ * of file descriptors.
+ * Therefore, we'll save a file descriptor when we start up that is less
+ * than 128, so that if we get a file descriptor that is >= 128, we can
+ * use our own saved file descriptor instead. This is only one level of
+ * defense; if you have more than 128 fd's in use, and you try two fopen's
+ * before doing an fclose(), the second will fail.
+ */
+
+FILE *
+fopen(file, mode)
+ const char *file;
+ const char *mode;
+{
+/* FILE *f; */
+ int fd, rw, oflags = 0;
+/* char tbchar; */
+ rw = (mode[1] == '+') || (mode[1] && (mode[2] == '+'));
+ switch (*mode) {
+ case 'a':
+ oflags = O_CREAT | (rw ? O_RDWR : O_WRONLY);
+ break;
+ case 'r':
+ oflags = rw ? O_RDWR : O_RDONLY;
+ break;
+ case 'w':
+ oflags = O_TRUNC | O_CREAT | (rw ? O_RDWR : O_WRONLY);
+ break;
+ default:
+ return (NULL);
+ }
+/* SunOS fopen() doesn't use the 't' or 'b' flags. */
+
+
+ fd = open(file, oflags, 0666);
+ if (fd < 0)
+ return NULL;
+ /* My addition, to cope with SunOS brain damage! */
+ if (fd >= 128) {
+ close(fd);
+ if ((extrafd < 128) && (extrafd >= 0)) {
+ close(extrafd);
+ fd = open(file, oflags, 0666);
+ extrafd = -1;
+ } else {
+ return NULL;
+ }
+ }
+ /* End addition. */
+
+ return fdopen(fd, mode);
+}
+
+
+#undef fclose(x)
+int
+f_close(stream)
+ FILE *stream;
+{
+ int fd = fileno(stream);
+ /* if extrafd is bad, and the fd we're closing is good, recycle the
+ * fd into extrafd.
+ */
+ fclose(stream);
+ if (((extrafd < 0)) && (fd >= 0) && (fd < 128)) {
+ extrafd = open("/dev/null", O_RDWR);
+ if (extrafd >= 128) {
+ /* To our surprise, we didn't get a usable fd. */
+ close(extrafd);
+ extrafd = -1;
+ }
+ }
+ return 0;
+}
+
+#define fclose(x) f_close(x)
+#endif /* SUN_OS */
+
+/** Dump the descriptor list to our REBOOTFILE so we can restore it on reboot.
+ */
+void
+dump_reboot_db(void)
+{
+ FILE *f;
+ DESC *d;
+ SU_PATH *exit_path;
+ long flags = RDBF_SCREENSIZE | RDBF_TTYPE | RDBF_PUEBLO_CHECKSUM
+ | RDBF_SU_EXIT_PATH;
+ if (setjmp(db_err)) {
+ flag_broadcast(0, 0, T("GAME: Error writing reboot database!"));
+ exit(0);
+ } else {
+
+ f = fopen(REBOOTFILE, "w");
+ /* This shouldn't happen */
+ if (!f) {
+ flag_broadcast(0, 0, T("GAME: Error writing reboot database!"));
+ exit(0);
+ }
+ /* Write out the reboot db flags here */
+ fprintf(f, "V%ld\n", flags);
+ putref(f, 0);
+ putref(f, 0);
+ /* First, iterate through all descriptors to get to the end
+ * we do this so the descriptor_list isn't reversed on reboot
+ */
+ for (d = descriptor_list; d && d->next; d = d->next) ;
+ /* Second, we iterate backwards from the end of descriptor_list
+ * which is now in the d variable.
+ */
+ for (; d != NULL; d = d->prev) {
+ putref(f, d->descriptor);
+ putref(f, d->connected_at);
+ putref(f, d->hide);
+ putref(f, d->cmds);
+ putref(f, d->idle_total);
+ putref(f, d->unidle_times);
+ if (GoodObject(d->player))
+ putref(f, d->player);
+ else
+ putref(f, -1);
+ putref(f, d->last_time);
+ if (d->output_prefix)
+ putstring(f, (char *) d->output_prefix);
+ else
+ putstring(f, "__NONE__");
+ if (d->output_suffix)
+ putstring(f, (char *) d->output_suffix);
+ else
+ putstring(f, "__NONE__");
+ putstring(f, d->addr);
+ putstring(f, d->ip);
+ putstring(f, d->doing);
+ putref(f, d->conn_flags);
+ putref(f, d->width);
+ putref(f, d->height);
+ putstring(f, d->ttype);
+ putstring(f, d->checksum);
+ for(exit_path = d->su_exit_path; exit_path; exit_path = exit_path->next)
+ putref(f, exit_path->player);
+ putref(f, NOTHING);
+ } /* for loop */
+
+ putref(f, 0);
+ putstring(f, poll_msg);
+ putref(f, globals.first_start_time);
+ putref(f, globals.reboot_count);
+ fclose(f);
+ }
+}
+
+/** Load the descriptor list back from the REBOOTFILE on reboot.
+ */
+void
+load_reboot_db(void)
+{
+ FILE *f;
+ DESC *d = NULL;
+ DESC *closed = NULL, *nextclosed;
+ int val;
+ int n;
+ const char *temp;
+ char c;
+ long flags = 0;
+ char tbuf1[BUFFER_LEN];
+ SU_PATH *epnext, *epprev;
+ dbref exit_path;
+ f = fopen(REBOOTFILE, "r");
+ if (!f) {
+ restarting = 0;
+ return;
+ }
+ restarting = 1;
+ /* Get the first line and see if it's a set of reboot db flags.
+ * Those start with V<number>
+ * If not, assume we're using the original format, in which the
+ * sock appears first
+ * */
+ c = getc(f); /* Skip the V */
+ if (c == 'V') {
+ flags = getref(f);
+ } else {
+ ungetc(c, f);
+ }
+
+ val = getref(f);
+ val = getref(f);
+
+ while ((val = getref(f)) != 0) {
+ ndescriptors++;
+ d = (DESC *) mush_malloc(sizeof(DESC), "descriptor");
+ d->descriptor = val;
+ d->input_handler = do_command;
+ d->connected_at = getref(f);
+ d->hide = getref(f);
+ d->cmds = getref(f);
+ d->idle_total = getref(f);
+ d->unidle_times = getref(f);
+ d->player = getref(f);
+ d->last_time = getref(f);
+ d->pinfo.object = NOTHING;
+ d->pinfo.atr = NULL;
+ d->pinfo.lock = 0;
+ d->pinfo.function = NULL;
+ d->connected = GoodObject(d->player) ? 1 : 0;
+ /* setup snooper array */
+ for(n = 0; n < MAX_SNOOPS; n++)
+ d->snooper[n] = -1;
+
+ temp = getstring_noalloc(f);
+ d->output_prefix = NULL;
+ if (strcmp(temp, "__NONE__"))
+ set_userstring(&d->output_prefix, temp);
+ temp = getstring_noalloc(f);
+ d->output_suffix = NULL;
+ if (strcmp(temp, "__NONE__"))
+ set_userstring(&d->output_suffix, temp);
+ strcpy(d->addr, getstring_noalloc(f));
+ strcpy(d->ip, getstring_noalloc(f));
+ strcpy(d->doing, getstring_noalloc(f));
+ d->conn_flags = getref(f);
+ if (flags & RDBF_SCREENSIZE) {
+ d->width = getref(f);
+ d->height = getref(f);
+ } else {
+ d->width = 78;
+ d->width = 24;
+ }
+ if (flags & RDBF_TTYPE)
+ d->ttype = mush_strdup(getstring_noalloc(f), "terminal description");
+ else
+ d->ttype = mush_strdup("unknown", "terminal description");
+ if (flags & RDBF_PUEBLO_CHECKSUM)
+ strcpy(d->checksum, getstring_noalloc(f));
+ else
+ d->checksum[0] = '\0';
+ d->su_exit_path = NULL;
+ if (flags & RDBF_SU_EXIT_PATH) {
+ exit_path = getref(f);
+ while(GoodObject(exit_path)) {
+ add_to_exit_path(d, exit_path);
+ exit_path = getref(f);
+ }
+ epprev = NULL;
+ while(d->su_exit_path) {
+ epnext = d->su_exit_path->next;
+ d->su_exit_path->next = epprev;
+ epprev = d->su_exit_path;
+ d->su_exit_path = epnext;
+ }
+ d->su_exit_path = epprev;
+ }
+ d->input_chars = 0;
+ d->output_chars = 0;
+ d->output_size = 0;
+ d->output.head = 0;
+ d->output.tail = &d->output.head;
+ d->input.head = 0;
+ d->input.tail = &d->input.head;
+ d->raw_input = NULL;
+ d->raw_input_at = NULL;
+ d->quota = options.starting_quota;
+ d->mailp = NULL;
+ if (d->conn_flags & CONN_CLOSE_READY) {
+ /* This isn't really an open descriptor, we're just tracking
+ * it so we can announce the disconnect properly. Do so, but
+ * don't link it into the descriptor list. Instead, keep a
+ * separate list.
+ */
+ if (closed)
+ closed->prev = d;
+ d->next = closed;
+ d->prev = NULL;
+ closed = d;
+ } else {
+ if (descriptor_list)
+ descriptor_list->prev = d;
+ d->next = descriptor_list;
+ d->prev = NULL;
+ descriptor_list = d;
+ if (d->connected && d->player && GoodObject(d->player) &&
+ IsPlayer(d->player))
+ set_flag_internal(d->player, "CONNECTED");
+ else if ((!d->player || !GoodObject(d->player)) && d->connected) {
+ d->connected = 0;
+ d->player = 0;
+ }
+ /* If they were in a program, get them back into it */
+ if(d->connected && InProg(d->player))
+ prog_load_desc(d);
+ }
+ } /* while loop */
+
+ /* Now announce disconnects of everyone who's not really here */
+ while (closed) {
+ nextclosed = closed->next;
+ if(closed->last_time > 0) {
+ closed->idle_total += difftime(mudtime, closed->last_time);
+ closed->unidle_times++;
+ }
+
+ snprintf(tbuf1, BUFFER_LEN-1, "%ld %ld %d %d", (mudtime - closed->connected_at),
+ closed->idle_total , closed->unidle_times, closed->cmds);
+ tbuf1[strlen(tbuf1)+1] = '\0';
+ (void) atr_add(closed->player, "LASTACTIVITY", tbuf1, GOD, NOTHING);
+ announce_disconnect(closed->player);
+ mush_free(closed, "descriptor");
+ closed = nextclosed;
+ }
+
+ strcpy(poll_msg, getstring_noalloc(f));
+ globals.first_start_time = getref(f);
+ globals.reboot_count = getref(f) + 1;
+ DESC_ITER_CONN(d) {
+ d->mailp = find_exact_starting_point(d->player);
+ }
+
+ fclose(f);
+ remove(REBOOTFILE);
+ flag_broadcast(0, 0, T("GAME: Reboot finished."));
+}
+
+
+/* Syntax: @snoop[/list] [!]<descriptor>
+ */
+COMMAND(cmd_snoop) {
+ DESC *d;
+ int descn, on, n, sn;
+ char snooplist[MAX_SNOOPS][BUFFER_LEN];
+ char buf[BUFFER_LEN], *bp;
+
+ if(SW_ISSET(sw, SWITCH_LIST)) {
+ descn = atoi(arg_left);
+
+ bp = buf;
+
+ d = port_desc(descn);
+
+ if (LEVEL(player) <= 28) {
+ notify(player, MSG_HUH);
+ return;
+ }
+ /* make sure teh desc exists and they're connected (no spying on 'em at the connect screen!) */
+ if(!d || (d && !IsPlayer(d->player))) {
+ notify(player, "There is no one connected on that descriptor.");
+ return;
+ }
+
+ for(sn = 0, n = 0; n < MAX_SNOOPS; n++)
+ if(d->snooper[n] != -1) {
+ memset(snooplist[sn], '\0', BUFFER_LEN);
+ snprintf(snooplist[sn++], BUFFER_LEN-1, "%s", Name(d->snooper[n]));
+ }
+ *snooplist[sn] = '\0';
+
+ for(n = 0; n < sn; n++) {
+ if(n != 0)
+ safe_str(", ", buf, &bp);
+ if(n == (sn-1) && sn > 1)
+ safe_str("& ", buf, &bp);
+ safe_str(snooplist[n], buf, &bp);
+ }
+ *bp = '\0';
+ notify_format(player, "%s is being snooped on by: %s", Name(d->player), buf);
+
+ } else {
+ if(*arg_left== '!') {
+ on = 0;
+ descn = atoi(arg_left+1);
+ } else {
+ descn = atoi(arg_left);
+ on = 1;
+ }
+
+ d = port_desc(descn);
+ if (LEVEL(player) <= 28) {
+ notify(player, MSG_HUH);
+ return;
+ }
+ /* make sure teh desc exists and they're connected (no spying on 'em at the connect screen!) */
+ if(!d || (d && !IsPlayer(d->player))) {
+ notify(player, "There is no one connected on that descriptor.");
+ return;
+ }
+
+ if(on) {
+ if((d->player == player)) {
+ notify(player, "You can't snoop yourself.");
+ return;
+ }
+
+ switch(set_snoop(player,d)) {
+ case -1: /* Too Many */
+ notify(player, "Sorry, can't snoop at this time.");
+ return;
+ case -2: /* Already Snoopin on 'em */
+ notify(player, "You can only snoop one person at a time.");
+ return;
+ default:
+ notify_format(player, T("Snoop now set on %s(%d)"), Name(d->player), descn);
+ }
+ } else {
+ for(on = n = 0; n < MAX_SNOOPS; n++)
+ if(d->snooper[n] == player) {
+ d->snooper[n] = -1;
+ on = 1;
+ }
+ notify(player, on ? "Snoop deactivated." : "Snooping Error.");
+ }
+ }
+}
+
+
+void feed_snoop(DESC *d, const char *s, char dir) {
+ int n;
+ char snstr[BUFFER_LEN];
+
+
+ if(!d ||!d->connected)
+ return;
+ memset(snstr, '\0', BUFFER_LEN);
+ strncpy(snstr, remove_markup(s, NULL), BUFFER_LEN-1);
+ if(*s && !*snstr)
+ return;
+ for(n = 0; n < MAX_SNOOPS ; n++)
+ if(!IsPlayer(d->snooper[n]))
+ continue;
+ else if(GoodObject(d->snooper[n]) && IsPlayer(d->snooper[n])) {
+ if(dir == 1) /* player see's */
+ notify_format((dbref) d->snooper[n], T("%s%s<-%s %s"), object_header(d->snooper[n],d->player), ANSI_BLUE,
+ ANSI_NORMAL, snstr);
+ else /* player types */
+ notify_format((dbref) d->snooper[n], T("%s%s->%s %s%s"), object_header(d->snooper[n],d->player),
+ ANSI_BLUE, ANSI_RED, snstr, ANSI_NORMAL);
+ }
+
+}
+
+char is_snooped(DESC *d) {
+ int n;
+
+ for(n = 0; n < MAX_SNOOPS; n++)
+ if(IsPlayer(d->snooper[n]))
+ return 1;
+ return 0;
+}
+
+
+char set_snoop(dbref plyr, DESC *d) {
+ int n;
+ /* take first available spot */
+ for( n = 0; n < MAX_SNOOPS ; n++)
+ if(d->snooper[n] == -1)
+ break;
+ else if(d->snooper[n] == plyr)
+ return -2; /* they're already snooping */
+ if(n == MAX_SNOOPS-1) /* too many snoopers on player */
+ return -1;
+ d->snooper[n] = plyr;
+ return 1;
+}
+
+void clr_snoop(dbref plyr, DESC *d) {
+ int n;
+
+ for(n = 0; n < MAX_SNOOPS; n++)
+ if(d->snooper[n] == plyr)
+ d->snooper[n] = -1;
+}
+
+/* switch users */
+COMMAND(cmd_su) {
+ DESC *d, *match = NULL;
+ dbref target;
+ int num = 0, is_hidden;
+
+ /* Stage 1. Make sure arg_left exists */
+ if(arg_left && *arg_left) {
+ if(!strcasecmp(cmd->name, "@SD")) {
+ target = match_result(player, arg_left, TYPE_DIVISION, MAT_EVERYTHING);
+ if(!GoodObject(target) || Typeof(target) != TYPE_DIVISION) {
+ notify(player, "No such division.");
+ return;
+ }
+ /* Check to see if special conditions exist, where they can enter without a password */
+ if(controls(player, target) /* condition 1: they control it */
+ ) {
+ add_to_div_exit_path(player, Division(player));
+ Division(player) = target;
+ notify_format(player, "You have switched into Division: %s", object_header(player, Division(player)));
+ return;
+ }
+ /* get least idle desc */
+ DESC_ITER_CONN(d)
+ if ((d->player == player) && (!match || (d->last_time > match->last_time)))
+ match = d;
+ /* We're only entering using a password at this moment */
+ queue_newwrite(match, (unsigned char *) tprintf(T("Password: %c%c"),
+ IAC, GOAHEAD), 13);
+ match->input_handler = password_handler;
+ match->pinfo.object = target;
+ match->pinfo.function = &pw_div_connect;
+ match->pinfo.lock = 0x40;
+ } else {
+ target = lookup_player(arg_left);
+ if(target == NOTHING) {
+ notify(player, "No such player.");
+ return;
+ }
+ do_log(LT_WIZ, player, target, "** @SU ATTEMPT **");
+ /* get least idle desc */
+ DESC_ITER_CONN(d)
+ if ((d->player == player) && (!match || (d->last_time > match->last_time)))
+ match = d;
+ /* Step 2. Find if we can get in without a pass, if
+ * we need a pass. Throw them into password_handler() internal
+ * prompt
+ */
+ if(div_powover(player, target, "@SU")) {
+ do_log(LT_WIZ, player, target, "** @SU SUCCESS **");
+ /* Phase 3a. Put Guy in user */
+ add_to_exit_path(match, player);
+ announce_disconnect(player);
+ match->player = target;
+ match->mailp = find_exact_starting_point(target);
+ is_hidden = Can_Hide(target) && Dark(target);
+ DESC_ITER_CONN(d)
+ if(d->player == player) {
+ num++;
+ if(is_hidden)
+ d->hide = 1;
+ }
+
+ if(ModTime(target))
+ notify_format(target, T("%ld failed connections since last login."), ModTime(target));
+ announce_connect(target, 0, num);
+ check_last(target, match->addr, match->ip); /* set last, lastsite, give paycheck */
+ queue_eol(match);
+ if(command_check_byname(target, "@MAIL"))
+ check_mail(target, 0, 0);
+ set_player_folder(target, 0);
+ do_look_around(target);
+ if(Haven(target))
+ notify(player, T("Your HAVEN flag is set. You cannot receive pages."));
+ } else {
+ /* Part 3b. Put guy in password program */
+ queue_newwrite(match, (unsigned char *) tprintf(T("Password: %c%c"),
+ IAC, GOAHEAD), 13);
+ match->input_handler = password_handler;
+ match->pinfo.object = target;
+ match->pinfo.function = &pw_player_connect;
+ match->pinfo.lock = 0x40;
+ }
+ }
+ } else {
+ if(SW_ISSET(sw, SWITCH_LOGOUT)) {
+ /* @sd/logout - check to see if there is any division @sd'ing history.. And backtrack us */
+ ATTR *divrcd;
+ char tbuf[BUFFER_LEN], *p_buf[BUFFER_LEN / 2], *tbp, sep[2];
+ int cnt;
+ dbref div_obj;
+
+ divrcd = atr_get(player, "XYXX_DIVRCD");
+ if(divrcd == NULL) {
+ notify(player, "You have not switched into any divisions.");
+ return;
+ }
+
+ cnt = list2arr(p_buf, BUFFER_LEN / 2, safe_atr_value(divrcd), ' ');
+ if(cnt < 1) {
+ notify(player, "You have not switched into any divisions.");
+ return;
+ }
+
+ /* Set them into cnt-1 if its good */
+ div_obj = parse_number(p_buf[cnt-1]);
+ if(GoodObject(div_obj) && IsDivision(div_obj)) {
+ Division(player) = div_obj;
+ notify_format(player, "You haev been went back to your other division: %s", object_header(player, div_obj));
+ }
+
+ /* now chop off last one, and arr2list() */
+ if(cnt == 1) { /* clear the attribute */
+ atr_clr(player, "XYXX_DIVRCD", GOD);
+ } else {
+ memset(tbuf, '\0', BUFFER_LEN);
+ tbp = tbuf;
+ sep[0] = ' ';
+ sep[1] = '\0';
+ arr2list(p_buf, cnt-1, tbuf, &tbp, sep);
+ /* Add the attribute back */
+ (void) atr_add(player, "XYXX_DIVRCD", tbuf, GOD, NOTHING);
+ }
+ } else {
+ notify(player, "Must specify what player you wish to @su into.");
+ }
+ }
+}
+
+void add_to_exit_path(DESC *d, dbref player) {
+ SU_PATH *path_entry;
+
+ if(!d)
+ return;
+
+ path_entry = (SU_PATH *) mush_malloc(sizeof(SU_PATH), "SU_PATH_ENTRY");
+
+ path_entry->player = player;
+ if(d->su_exit_path)
+ path_entry->next = d->su_exit_path;
+ else
+ path_entry->next = NULL;
+ d->su_exit_path = path_entry;
+}
+
+/* If they're logged in.. Log 'em out through their su path */
+static int do_su_exit(DESC *d) {
+ DESC *c;
+ SU_PATH *path_entry, *mark_path;
+ int is_hidden, num = 0;
+
+ if(d->su_exit_path) {
+ path_entry = d->su_exit_path;
+ while(path_entry)
+ if(GoodObject(path_entry->player) && IsPlayer(path_entry->player))
+ break;
+ else { /* Guess the guy got nuked along the way.. free the spot */
+ mark_path = path_entry;
+ path_entry = path_entry->next;
+ mush_free(mark_path, "SU_PATH_ENTRY");
+ }
+ if(!path_entry)
+ return 0;
+ d->su_exit_path = path_entry;
+ /* Disconnect appearance */
+ announce_disconnect(d->player);
+ d->player = path_entry->player;
+ /* Clear path_entry spot */
+ d->su_exit_path = path_entry->next;
+ mush_free(path_entry, "SU_PATH_ENTRY");
+ d->mailp = find_exact_starting_point(d->player);
+ is_hidden = Can_Hide(d->player) && Dark(d->player);
+ DESC_ITER_CONN(c)
+ if(c->player == d->player) {
+ num++;
+ if(is_hidden)
+ c->hide = 1;
+ }
+ if(ModTime(d->player))
+ notify_format(d->player, T("%ld failed connections since last login."), ModTime(d->player));
+ announce_connect(d->player, 0, num);
+ check_last(d->player, d->addr, d->ip); /* set last, lastsite, give paycheck */
+ queue_eol(d);
+ if(command_check_byname(d->player, "@MAIL"))
+ check_mail(d->player, 0, 0);
+ set_player_folder(d->player, 0);
+ do_look_around(d->player);
+ if(Haven(d->player))
+ notify(d->player, T("Your HAVEN flag is set. You cannot receive pages."));
+ return 1;
+ } else return 0;
+}
+
+void
+close_ssl_connections(void)
+{
+}
+
+void
+kill_info_slave(void)
+{
+}
+