/* bsd.c */
#include "os.h"
#include "copyright.h"
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "globals.h"
#ifdef MEM_CHECK
#include "mem_check.h"
#endif
int shutdown_flag = 0;
int login_allow = 1;
char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN],
cf_nologinmotd_msg[BUFFER_LEN];
#ifdef AT_DOING
static char poll[39];
#endif
static const char *connect_fail =
"Either that player does not exist, or has a different password.\n";
#ifndef WCREAT
static const char *create_fail =
"Either there is already a player with that name, or that name is illegal.\n";
#endif
static const char *flushed_message = "<Output Flushed>\n";
static const char *shutdown_message = "Going down - Bye\n";
static const char *asterisk_line =
"*****************************************************************";
struct text_block {
int nchars;
struct text_block *nxt;
char *start;
char *buf;
};
struct text_queue {
struct text_block *head;
struct text_block **tail;
};
struct descriptor_data {
SOCKET descriptor;
int connected;
char addr[51];
dbref player;
char *output_prefix;
char *output_suffix;
int output_size;
struct text_queue output;
struct text_queue input;
char *raw_input;
char *raw_input_at;
long connected_at;
long last_time;
int quota;
#ifdef AT_DOING
char doing[40];
#endif
struct sockaddr_in address; /* added 3/6/90 SCG */
struct descriptor_data *next;
struct descriptor_data **prev;
} *descriptor_list = 0;
static SOCKET sock;
static int ndescriptors = 0;
char ccom[BUFFER_LEN];
dbref cplr;
static void set_signals (void);
static void shovechars (int port);
static struct descriptor_data *new_connection(SOCKET oldsock);
static void shutdownsock(struct descriptor_data *d);
static int make_socket(int port);
static int queue_write (struct descriptor_data *d, const char *b, int n);
static int queue_string (struct descriptor_data *d, const char *s);
static int process_output (struct descriptor_data *d);
static void make_nonblocking (SOCKET s);
static void freeqs (struct descriptor_data *d);
static void welcome_user (struct descriptor_data *d);
static void spew_message (struct descriptor_data *d, char *filename);
static char *strsave (const char *s);
static int process_input (struct descriptor_data *d);
static void set_userstring (char **userstring, const char *command);
static struct descriptor_data *initializesock(SOCKET s, struct sockaddr_in *a, const char *addr);
static int do_command (struct descriptor_data *d, char *command);
static int check_connect (struct descriptor_data *d, const char *msg);
static void parse_connect (const char *msg, char *command, char *user, char *pass);
static void close_sockets (void);
#ifdef LOCKOUT
static const char *addrout (struct in_addr);
#endif
static void bailout(int sig);
static void dump_users (struct descriptor_data *call_by, char *match, int doing);
static const char *time_format_1(long dt);
static const char *time_format_2(long dt);
static void announce_connect (dbref player);
static void announce_disconnect (dbref player);
static int forbidden_site (const char *hname);
#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers ();
#endif
void rwho_update ();
#endif
#ifndef BOOLEXP_DEBUGGING
int main (int argc, char **argv)
{
const char *def_db_in = DEF_DB_IN;
const char *def_db_out = DEF_DB_OUT;
#ifdef AUTORESTART
FILE *id;
#endif
/* change default input database? */
if (argc > 1) {
--argc;
def_db_in = *++argv;
}
/* change default dump database? */
if (argc > 1) {
--argc;
def_db_out = *++argv;
}
/* this writes a file used by the restart script to check for active mush */
#ifdef AUTORESTART
id = fopen ("runid", "wb");
fprintf (id, "%d", getpid ());
fclose (id);
#endif
OS_SRAND (time (NULL));
/* save a file descriptor */
if (init_game (def_db_in, def_db_out) < 0) {
fprintf (stderr, "ERROR: Couldn't load %s! Exiting.\n", def_db_in);
WIN32CLEANUP
return (2);
}
WIN32STARTUP
set_signals ();
#ifdef RWHO_SEND
rwhocli_setup (RWHOSERV, RWHOPASS, MUDNAME, VERSION);
#endif
/* go do it */
shovechars (argc > 1 ? atoi (*++argv) : TINYPORT);
close_sockets ();
dump_database ();
close (sock); /* patch moving this line here fixes
* @shutdown error */
WIN32CLEANUP
return (0);
}
#endif /* BOOLEXP_DEBUGGING */
static void set_signals (void)
{
/* we don't care about SIGPIPE, we notice it in select() and write() */
#ifndef WIN32
signal (SIGPIPE, SIG_IGN);
#endif
/* standard termination signals */
signal (SIGINT, bailout);
signal (SIGTERM, bailout);
}
void raw_notify (dbref player, const char *msg)
{
struct descriptor_data *d;
if (!msg || *msg == '\0')
return;
if (!(db[player].flags & PLAYER_CONNECT) && login_allow)
return;
for (d = descriptor_list; d; d = d->next) {
if (d->connected && d->player == player) {
queue_string (d, msg);
queue_write (d, "\n", 1);
}
}
}
#define DESC struct descriptor_data
#define DESC_ITER_CONN(d) \
for(d = descriptor_list;(d);d=(d)->next) \
if((d)->connected)
void raw_broadcast (int inflags, char *template, ...)
{
DESC *d;
char tbuf1[BUFFER_LEN];
va_list args;
if (!template || !*template)
return;
va_start (args, template);
vsprintf (tbuf1, template, args);
va_end (args);
DESC_ITER_CONN (d) {
if ((db[d->player].flags & inflags) == inflags) {
queue_string (d, tbuf1);
queue_write (d, "\n", 1);
process_output (d);
}
}
}
static struct timeval timeval_sub (struct timeval now, struct timeval then)
{
now.tv_sec -= then.tv_sec;
now.tv_usec -= then.tv_usec;
if (now.tv_usec < 0) {
now.tv_usec += 1000000;
now.tv_sec--;
}
return now;
}
static long msec_diff (struct timeval now, struct timeval then)
{
return ((now.tv_sec - then.tv_sec) * 1000
+ (now.tv_usec - then.tv_usec) / 1000);
}
static struct timeval msec_add (struct timeval t, int x)
{
t.tv_sec += x / 1000;
t.tv_usec += (x % 1000) * 1000;
if (t.tv_usec >= 1000000) {
t.tv_sec += t.tv_usec / 1000000;
t.tv_usec = t.tv_usec % 1000000;
}
return t;
}
struct timeval update_quotas (struct timeval last, struct timeval current)
{
int nslices;
struct descriptor_data *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;
}
}
return msec_add (last, nslices * COMMAND_TIME_MSEC);
}
void shovechars (int port)
{
fd_set input_set, output_set;
time_t now;
struct timeval last_slice, current_time;
struct timeval next_slice;
struct timeval timeout, slice_timeout;
int maxd, found;
struct descriptor_data *d, *dnext;
struct descriptor_data *newd;
int avail_descriptors;
sock = make_socket (port);
maxd = sock + 1;
gettimeofday (&last_slice, (struct timezone *) 0);
avail_descriptors = getdtablesize () - 4;
while (shutdown_flag == 0) {
gettimeofday (¤t_time, (struct timezone *) 0);
last_slice = update_quotas (last_slice, current_time);
#ifdef IDLE_TIMEOUT
/*check idle timeout */
inactivity_check ();
#endif
process_commands ();
if (shutdown_flag)
break;
/* test for events */
dispatch ();
/* any queued robot commands waiting? */
timeout.tv_sec = test_top ()? 0 : 1000;
timeout.tv_usec = 0;
next_slice = msec_add (last_slice, COMMAND_TIME_MSEC);
slice_timeout = timeval_sub (next_slice, current_time);
FD_ZERO (&input_set);
FD_ZERO (&output_set);
if (ndescriptors < avail_descriptors)
FD_SET (sock, &input_set);
for (d = descriptor_list; d; d = d->next) {
if (d->input.head)
timeout = slice_timeout;
else
FD_SET (d->descriptor, &input_set);
if (d->output.head)
FD_SET (d->descriptor, &output_set);
}
if ((found = select (maxd, &input_set, &output_set,
(fd_set *) 0, &timeout)) < 0) {
if (GETERROR != EINTR) {
perror ("select");
return;
}
} else {
/* if !found then time for robot commands */
if (!found) {
do_top ();
do_top ();
do_top ();
continue;
}
now = time ((time_t *) 0);
if (FD_ISSET (sock, &input_set)) {
if (!(newd = new_connection (sock))) {
if (GETERROR && GETERROR != EINTR && GETERROR != EMFILE && GETERROR != ENFILE) {
perror ("new_connection");
return;
}
} else {
#ifndef WIN32
if (newd->descriptor >= maxd)
maxd = newd->descriptor + 1;
#endif
}
}
for (d = descriptor_list; d; d = dnext) {
dnext = d->next;
if (FD_ISSET (d->descriptor, &input_set)) {
d->last_time = now;
if (!process_input (d)) {
shutdownsock (d);
continue;
}
}
if (FD_ISSET (d->descriptor, &output_set)) {
if (!process_output (d)) {
shutdownsock (d);
}
}
}
}
}
}
static struct descriptor_data *new_connection (SOCKET oldsock)
{
SOCKET newsock;
struct sockaddr_in addr;
int addr_len;
char tbuf1[BUFFER_LEN];
addr_len = sizeof (addr);
newsock = accept (oldsock, (struct sockaddr *) &addr, &addr_len);
if (newsock == INVALID_SOCKET) {
return 0;
#ifdef LOCKOUT
} else if (forbidden_site (addrout (addr.sin_addr))) {
fprintf (stderr, "REFUSED CONNECTION from %s(%d) on descriptor %d\n",
addrout (addr.sin_addr), ntohs (addr.sin_port), newsock);
shutdown (newsock, 2);
close (newsock);
#if !defined WIN32
errno = 0;
#endif
return 0;
#endif /* LOCKOUT */
} else {
time_t tt;
strcpy (tbuf1, addrout (addr.sin_addr));
tt = time ((time_t *) 0);
fprintf (stderr, "USER CONNECT: des: %d host %s time: %s", newsock, tbuf1,
ctime (&tt));
return initializesock (newsock, &addr, tbuf1);
}
}
static void clearstrings (struct descriptor_data *d)
{
if (d->output_prefix) {
free ((void *) d->output_prefix);
#ifdef MEM_CHECK
del_check ("userstring");
#endif
d->output_prefix = 0;
}
if (d->output_suffix) {
free ((void *) d->output_suffix);
#ifdef MEM_CHECK
del_check ("userstring");
#endif
d->output_suffix = 0;
}
}
static void shutdownsock (struct descriptor_data *d)
{
if (d->connected) {
spew_message (d, LEAVE_MSG_FILE);
fprintf (stderr, "DISCONNECT descriptor %d player %s(%d)\n",
d->descriptor, db[d->player].name, d->player);
announce_disconnect (d->player);
} else {
fprintf (stderr, "DISCONNECT descriptor %d never connected\n",
d->descriptor);
}
process_output (d);
clearstrings (d);
shutdown (d->descriptor, 2);
close (d->descriptor);
freeqs (d);
*d->prev = d->next;
if (d->next)
d->next->prev = d->prev;
free ((void *) d);
#ifdef MEM_CHECK
del_check ("descriptor");
#endif
ndescriptors--;
}
static struct descriptor_data *initializesock (SOCKET s, struct sockaddr_in *a,
const char *addr)
{
struct descriptor_data *d;
ndescriptors++;
d = (struct descriptor_data *) malloc (sizeof (struct descriptor_data));
if (!d)
panic ("Out of memory.");
#ifdef MEM_CHECK
add_check ("descriptor");
#endif
d->descriptor = s;
d->connected = 0;
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 = 0;
#ifdef AT_DOING
d->doing[0] = '\0';
#endif
strncpy (d->addr, addr, 50);
d->address = *a; /* added 5/3/90 SCG */
if (descriptor_list)
descriptor_list->prev = &d->next;
d->next = descriptor_list;
d->prev = &descriptor_list;
descriptor_list = d;
welcome_user (d);
return d;
}
static int make_socket (int port)
{
SOCKET s;
struct sockaddr_in server;
int opt;
s = socket (AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
perror ("creating stream socket");
WIN32CLEANUP
exit (3);
}
opt = 1;
if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
(char *) &opt, sizeof (opt)) == SOCKET_ERROR) {
perror ("setsockopt");
WIN32CLEANUP
exit (1);
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons (port);
if (bind (s, (struct sockaddr *) &server, sizeof (server)) == SOCKET_ERROR) {
perror ("binding stream socket");
close (s);
WIN32CLEANUP
exit (4);
}
listen (s, 5);
return s;
}
static struct text_block *make_text_block (const char *s, int n)
{
struct text_block *p;
p = (struct text_block *) malloc (sizeof (struct text_block));
if (!p)
panic ("Out of memory");
p->buf = (char *) malloc (sizeof (char) * n);
if (!p->buf)
panic ("Out of memory");
#ifdef MEM_CHECK
add_check ("text_block");
add_check ("text_block_buff");
#endif
bcopy (s, p->buf, n);
p->nchars = n;
p->start = p->buf;
p->nxt = 0;
return p;
}
static void free_text_block (struct text_block *t)
{
if (t) {
if (t->buf)
free ((void *) t->buf);
free ((void *) t);
}
#ifdef MEM_CHECK
del_check ("text_block");
del_check ("text_block_buff");
#endif
}
static void add_to_queue (struct text_queue *q, const char *b, int n)
{
struct text_block *p;
if (n == 0)
return;
p = make_text_block (b, n);
p->nxt = 0;
*q->tail = p;
q->tail = &p->nxt;
}
static int flush_queue (struct text_queue *q, int n)
{
struct text_block *p;
int really_flushed = 0;
n += strlen (flushed_message);
while (n > 0 && (p = q->head)) {
n -= p->nchars;
really_flushed += p->nchars;
q->head = p->nxt;
#ifdef DEBUG
fprintf (stderr, "free_text_block(0x%x) at 1.\n", p);
#endif /* DEBUG */
free_text_block (p);
}
p = make_text_block (flushed_message, strlen (flushed_message));
p->nxt = q->head;
q->head = p;
if (!p->nxt)
q->tail = &p->nxt;
really_flushed -= p->nchars;
return really_flushed;
}
static int queue_write (struct descriptor_data *d, const char *b, int n)
{
int space;
space = MAX_OUTPUT - d->output_size - n;
if (space < 0)
d->output_size -= flush_queue (&d->output, -space);
add_to_queue (&d->output, b, n);
d->output_size += n;
return n;
}
static int queue_string (struct descriptor_data *d, const char *s)
{
return queue_write (d, s, strlen (s));
}
static int process_output (struct descriptor_data *d)
{
struct text_block **qp, *cur;
int cnt;
for (qp = &d->output.head; cur = *qp;) {
cnt = send (d->descriptor, cur->start, cur->nchars, 0);
if (cnt == SOCKET_ERROR) {
if (errno == EWOULDBLOCK)
return 1;
return 0;
}
d->output_size -= cnt;
if (cnt == cur->nchars) {
if (!cur->nxt)
d->output.tail = qp;
*qp = cur->nxt;
#ifdef DEBUG
fprintf (stderr, "free_text_block(0x%x) at 2.\n", cur);
#endif /* DEBUG */
free_text_block (cur);
continue; /* do not adv ptr */
}
cur->nchars -= cnt;
cur->start += cnt;
break;
}
return 1;
}
static void make_nonblocking (SOCKET s)
{
#ifdef WIN32
unsigned long flags = 1;
if (ioctlsocket (s, FIONBIO, &flags)) {
#else
if (fcntl (s, F_SETFL, FNDELAY) == -1) {
#endif
perror ("make_nonblocking: fcntl");
panic ("FNDELAY fcntl failed");
}
}
static void freeqs (struct descriptor_data *d)
{
struct text_block *cur, *next;
cur = d->output.head;
while (cur) {
next = cur->nxt;
#ifdef DEBUG
fprintf (stderr, "free_text_block(0x%x) at 3.\n", cur);
#endif /* DEBUG */
free_text_block (cur);
cur = next;
}
d->output.head = 0;
d->output.tail = &d->output.head;
cur = d->input.head;
while (cur) {
next = cur->nxt;
#ifdef DEBUG
fprintf (stderr, "free_text_block(0x%x) at 4.\n", cur);
#endif /* DEBUG */
free_text_block (cur);
cur = next;
}
d->input.head = 0;
d->input.tail = &d->input.head;
if (d->raw_input) {
free ((void *) d->raw_input);
#ifdef MEM_CHECK
del_check ("descriptor_raw_input");
#endif
}
d->raw_input = 0;
d->raw_input_at = 0;
}
static void welcome_user (struct descriptor_data *d)
{
spew_message (d, WELCOME_MSG_FILE);
}
static void spew_message (struct descriptor_data *d, char *filename)
{
int n;
FILE* f;
char buf[512];
if ((f = fopen (filename, "rb")) != NULL) {
while ((n = fread (buf, 1, 512, f)) > 0)
queue_write (d, buf, n);
fclose (f);
queue_write (d, "\n", 1);
}
}
static char *strsave (const char *s)
{
char *p;
p = (char *) malloc (sizeof (char) * (strlen (s) + 1));
if (!p)
panic ("Out of memory");
#ifdef MEM_CHECK
add_check ("userstring");
#endif
if (p)
strcpy (p, s);
return p;
}
static void save_command (struct descriptor_data *d, const char *command)
{
add_to_queue (&d->input, command, strlen (command) + 1);
}
static int process_input (struct descriptor_data *d)
{
int got;
char *p, *pend, *q, *qend;
char tbuf1[BUFFER_LEN];
got = recv (d->descriptor, tbuf1, sizeof tbuf1, 0);
if (got <= 0)
return 0;
if (!d->raw_input) {
d->raw_input = (char *) malloc (sizeof (char) * MAX_COMMAND_LEN);
if (!d->raw_input)
panic ("Out of memory");
#ifdef MEM_CHECK
add_check ("descriptor_raw_input");
#endif
d->raw_input_at = d->raw_input;
}
p = d->raw_input_at;
pend = d->raw_input + MAX_COMMAND_LEN - 1;
for (q = tbuf1, qend = tbuf1 + got; q < qend; q++) {
if (*q == '\n') {
*p = '\0';
if (p > d->raw_input)
save_command (d, d->raw_input);
p = d->raw_input;
} else if (p < pend && isascii ((int)*q) && isprint ((int)*q)) {
*p++ = *q;
}
}
if (p > d->raw_input) {
d->raw_input_at = p;
} else {
free ((void *) d->raw_input);
#ifdef MEM_CHECK
del_check ("descriptor_raw_input");
#endif
d->raw_input = 0;
d->raw_input_at = 0;
}
return 1;
}
static void set_userstring (char **userstring, const char *command)
{
if (*userstring) {
free ((void *) *userstring);
#ifdef MEM_CHECK
del_check ("userstring");
#endif
*userstring = 0;
}
while (*command && isascii ((int)*command) && isspace ((int)*command))
command++;
if (*command)
*userstring = strsave (command);
}
void process_commands (void)
{
int nprocessed;
struct descriptor_data *d, *dnext;
struct text_block *t;
do {
nprocessed = 0;
for (d = descriptor_list; d; d = dnext) {
dnext = d->next;
if (d->quota > 0 && (t = d->input.head)) {
d->quota--;
nprocessed++;
if (!do_command (d, t->start)) {
shutdownsock (d);
} else {
d->input.head = t->nxt;
if (!d->input.head)
d->input.tail = &d->input.head;
if (t) {
#ifdef DEBUG
fprintf (stderr, "free_text_block(0x%x) at 5.\n", t);
#endif /* DEBUG */
free_text_block (t);
}
}
}
}
} while (nprocessed > 0);
}
static int do_command (struct descriptor_data *d, char *command)
{
depth = 0;
if (!strcmp (command, QUIT_COMMAND)) {
return 0;
} else if (!strncmp (command, WHO_COMMAND, strlen (WHO_COMMAND))) {
if (d->output_prefix) {
queue_string (d, d->output_prefix);
queue_write (d, "\n", 1);
}
dump_users (d, command + strlen (WHO_COMMAND), 0);
if (d->output_suffix) {
queue_string (d, d->output_suffix);
queue_write (d, "\n", 1);
}
#ifdef AT_DOING
} else if (!strncmp (command, DOING_COMMAND, strlen (DOING_COMMAND))) {
if (d->output_prefix) {
queue_string (d, d->output_prefix);
queue_write (d, "\n", 1);
}
dump_users (d, command + strlen (DOING_COMMAND), 1);
if (d->output_suffix) {
queue_string (d, d->output_suffix);
queue_write (d, "\n", 1);
}
#endif
} 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));
#ifdef RWHO_SEND
#ifdef FULL_RWHO
} else if (!strcmp (command, RWHO_COMMAND)) {
dump_rusers (d);
#endif
#endif
} else {
if (d->connected) {
if (d->output_prefix) {
queue_string (d, d->output_prefix);
queue_write (d, "\n", 1);
}
cplr = d->player;
strcpy (ccom, command);
process_command (d->player, command, d->player);
if (d->output_suffix) {
queue_string (d, d->output_suffix);
queue_write (d, "\n", 1);
}
} else {
if (!check_connect (d, command))
return 0;
}
}
return 1;
}
static int check_connect (struct descriptor_data *d, const char *msg)
{
char command[MAX_COMMAND_LEN];
char user[MAX_COMMAND_LEN];
char password[MAX_COMMAND_LEN];
dbref player;
parse_connect (msg, command, user, password);
if (!strncmp (command, "co", 2)) {
player = connect_player (user, password);
if (player == NOTHING) {
queue_string (d, connect_fail);
fprintf (stderr, "FAILED CONNECT %s on descriptor %d\n",
user, d->descriptor);
} else {
fprintf (stderr, "CONNECTED %s(%d) on descriptor %d\n",
db[player].name, player, d->descriptor);
#ifdef AT_DOING
d->doing[0] = '\0';
#endif
d->connected = 1;
d->connected_at = time ((time_t *) 0);
d->player = player;
/* give players a message on connection */
if (!login_allow) {
spew_message (d, DISABLE_MSG_FILE);
raw_notify (player, asterisk_line);
if (*cf_nologinmotd_msg)
raw_notify (player, cf_nologinmotd_msg);
if (!Wizard (player)) {
raw_notify (player, asterisk_line);
return 0;
} else
raw_notify (player, asterisk_line);
}
spew_message (d, CONNECT_MSG_FILE);
/* give wizards and royalty their message, too - d'mike 7/15/91 */
if (Hasprivs (player)) {
spew_message (d, WIZARD_MSG_FILE);
}
/* set the Lastsite attribute */
atr_add (player, "LASTSITE", d->addr, GOD, NOTHING);
announce_connect (player);
#ifdef USE_MAILER
check_mail (player, 0);
#endif
do_look_around (player);
if (db[player].flags & HAVEN) {
notify (player, "Your HAVEN flag is set. You cannot receive pages.");
}
}
} else if (!strncmp (command, "cr", 2)) {
#ifdef WCREAT
spew_message (d, REGISTER_MSG_FILE);
#else /* WCREAT */
player = create_player (user, password);
if (player == NOTHING) {
queue_string (d, create_fail);
fprintf (stderr, "FAILED CREATE %s on descriptor %d\n",
user, d->descriptor);
} else {
fprintf (stderr, "CREATED %s(%d) on descriptor %d\n",
db[player].name, player, d->descriptor);
#ifdef AT_DOING
d->doing[0] = '\0';
#endif
d->connected = 1;
d->connected_at = time ((time_t *) 0);
d->player = player;
if (!login_allow) {
spew_message (d, DISABLE_MSG_FILE);
raw_notify (player, asterisk_line);
if (*cf_nologinmotd_msg)
raw_notify (player, cf_nologinmotd_msg);
raw_notify (player, asterisk_line);
return 0;
}
/* give new players a special message */
spew_message (d, NEW_CONNECT_MSG_FILE);
/* set the Lastsite attribute */
atr_add (player, "LASTSITE", d->addr, GOD, NOTHING);
announce_connect (player);
do_look_around (player);
}
#endif /* WCREAT */
} else
welcome_user (d);
return 1;
}
static void parse_connect (const char *msg, char *command, char *user, char *pass)
{
char *p;
while (*msg && isascii ((int)*msg) && isspace ((int)*msg))
msg++;
p = command;
while (*msg && isascii ((int)*msg) && !isspace ((int)*msg))
*p++ = *msg++;
*p = '\0';
while (*msg && isascii ((int)*msg) && isspace ((int)*msg))
msg++;
p = user;
while (*msg && isascii ((int)*msg) && !isspace ((int)*msg))
*p++ = *msg++;
*p = '\0';
while (*msg && isascii ((int)*msg) && isspace ((int)*msg))
msg++;
p = pass;
while (*msg && isascii ((int)*msg) && !isspace ((int)*msg))
*p++ = *msg++;
*p = '\0';
}
static void close_sockets (void)
{
struct descriptor_data *d, *dnext;
#ifdef RWHO_SEND
rwhocli_shutdown ();
#endif
for (d = descriptor_list; d; d = dnext) {
dnext = d->next;
send (d->descriptor, shutdown_message, strlen (shutdown_message), 0);
if (shutdown (d->descriptor, 2) == SOCKET_ERROR)
perror ("shutdown");
close (d->descriptor);
}
}
void emergency_shutdown (void)
{
close_sockets ();
}
void boot_off (dbref player)
{
struct descriptor_data *d;
for (d = descriptor_list; d; d = d->next) {
if (d->connected && d->player == player) {
shutdownsock (d);
return;
}
}
}
static void bailout (int sig)
{
char tbuf1[BUFFER_LEN];
sprintf (tbuf1, "BAILOUT: caught signal %d", sig);
panic (tbuf1);
WIN32CLEANUP
_exit (7);
}
static void dump_users (struct descriptor_data *call_by, char *match, int doing
/* 0 if normal WHO, 1 if DOING */
)
{
struct descriptor_data *d;
int count = 0;
time_t now;
char tbuf1[BUFFER_LEN];
char tbuf2[BUFFER_LEN];
if (call_by->player < 0 || call_by->player >= db_top) {
fprintf (stderr, "ERROR: Bogus caller of dump_users! (%d)\n",
call_by->player);
return;
}
while (*match && *match == ' ')
match++;
now = time ((time_t *) 0);
/* If a wizard/royal types "DOING" it gives him the normal player WHO. */
/* Wizard/royal WHO does not show @doings. */
if ((doing) || !Hasprivs (call_by->player)) {
#ifdef AT_DOING
if (poll[0] == '\0')
strcpy (poll, "Doing");
sprintf (tbuf2, "Player Name On For Idle %s\n", poll);
queue_string (call_by, tbuf2);
#else
queue_string (call_by, "Player Name On For Idle\n");
#endif
} else {
queue_string (call_by,
"Player Name Location On For Idle [Host]\n");
}
for (d = descriptor_list; d; d = d->next) {
if (d->connected) {
if (d->player < 0 || d->player >= db_top)
continue;
if (!Dark (d->player) || Wizard (call_by->player))
++count;
if (match && !(string_prefix (db[d->player].name, match)))
continue;
if (call_by->connected && !(doing) && Hasprivs (call_by->player)) {
sprintf (tbuf1, "%-16s [%6d] %9s %5s [%s]",
db[d->player].name,
getloc (d->player),
time_format_1 (now - d->connected_at),
time_format_2 (now - d->last_time), d->addr);
if (Dark (d->player))
sprintf (tbuf1 + strlen (tbuf1), " (Dark)");
} else {
if (!Dark (d->player) || Hasprivs (call_by->player) && (doing)) {
#ifdef AT_DOING
sprintf (tbuf1, "%-16s %10s %4s %s",
#else
sprintf (tbuf1, "%-16s %10s %4s",
#endif
db[d->player].name,
time_format_1 (now - d->connected_at),
time_format_2 (now - d->last_time)
#ifdef AT_DOING
, d->doing
#endif
);
}
}
if (!Dark (d->player) || Hasprivs (call_by->player)) {
queue_string (call_by, tbuf1);
queue_write (call_by, "\n", 1);
}
}
}
sprintf (tbuf1, "There are %d players connected.\n", count);
queue_string (call_by, tbuf1);
}
static const char *time_format_1 (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 %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;
}
static void announce_connect (dbref player)
{
dbref loc;
ATTR *temp;
char tbuf1[BUFFER_LEN];
db[player].flags |= PLAYER_CONNECT;
if (db[player].flags & PLAYER_SUSPECT)
raw_broadcast (WIZARD, "Broadcast: Suspect %s has connected.",
db[player].name);
if ((loc = getloc (player)) == NOTHING) {
notify (player, "You are nowhere!");
return;
}
speaker = player;
#ifdef RWHO_SEND
sprintf (tbuf1, "%d@%s", player, MUDNAME);
rwhocli_userlogin (tbuf1, db[player].name, time ((time_t *) 0));
#endif
raw_notify (player, asterisk_line);
if (*cf_motd_msg)
raw_notify (player, cf_motd_msg);
raw_notify (player, " ");
if (Hasprivs (player) && *cf_wizmotd_msg)
raw_notify (player, cf_wizmotd_msg);
raw_notify (player, asterisk_line);
sprintf (tbuf1, "%s has connected.", db[player].name);
notify_except (db[player].contents, player, tbuf1);
/* added to allow player's inventory to hear a player connect */
if (!Dark (player))
notify_except (db[loc].contents, player, tbuf1);
temp = atr_get (player, "ACONNECT");
if (temp) {
char *s = safe_uncompress (temp->value);
parse_que (player, s, player);
free (s);
}
}
static void announce_disconnect (dbref player)
{
dbref loc;
int num;
ATTR *temp;
struct descriptor_data *d;
char tbuf1[BUFFER_LEN];
if ((loc = getloc (player)) == NOTHING)
return;
speaker = player;
for (num = 0, d = descriptor_list; d; d = d->next)
if (d->connected && (d->player == player))
num++;
if (num < 2) {
#ifdef RWHO_SEND
sprintf (tbuf1, "%d@%s", player, MUDNAME);
rwhocli_userlogout (tbuf1);
#endif
sprintf (tbuf1, "%s has disconnected.", db[player].name);
if (!Dark (player))
notify_except (db[loc].contents, player, tbuf1);
/* notify contents */
notify_except (db[player].contents, player, tbuf1);
temp = atr_get (player, "ADISCONNECT");
if (temp) {
char *s = safe_uncompress (temp->value);
parse_que (player, s, player);
free (s);
}
db[player].flags &= ~PLAYER_CONNECT;
if (db[player].flags & PLAYER_SUSPECT)
raw_broadcast (WIZARD, "Broadcast: Suspect %s has disconnected.",
db[player].name);
}
}
#ifdef AT_DOING
void do_doing (dbref player, char *arg1, char *arg2)
{
const char *message;
char buf[MAX_COMMAND_LEN];
struct descriptor_data *d;
message = reconstruct_message (arg1, arg2);
sprintf (buf, message);
buf[39] = '\0';
for (d = descriptor_list; d; d = d->next)
if (d->connected && (d->player == player))
strcpy (d->doing, buf);
if (strlen (message) > 39)
notify (player,
tprintf ("Doing set. %d characters lost.", strlen (message) - 39));
else
notify (player, "Doing set.");
}
/* this sets the message which replaces "Doing" */
void do_poll (dbref player, char *arg1, char *arg2)
{
const char *message;
if (!Wizard (player)) {
notify (player, "Who do you think you are, Gallup?");
return;
}
message = reconstruct_message (arg1, arg2);
strncpy (poll, message, 39);
if (strlen (message) > 39)
notify (player,
tprintf ("Poll set. %d characters lost.", strlen (message) - 39));
else
notify (player, "Poll set.");
}
#endif /* AT_DOING */
#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers (struct descriptor_data *call_by)
{
struct sockaddr_in addr;
struct hostent *hp;
char *p;
int fd;
int red;
char *srv = NULL;
int portnum = RWHOPORT;
char tbuf1[BUFFER_LEN];
p = srv = (char *) RWHOSERV;
while (*p != '\0' && (*p == '.' || isdigit ((int)*p)))
p++;
if (*p != '\0') {
if ((hp = gethostbyname (srv)) == (struct hostent *) 0) {
fprintf (stderr, "ERROR: unknown host %s\n", srv);
queue_string (call_by, "Error in connecting to the RWHO server.\n");
return;
}
(void) bcopy (hp->h_addr, (char *) &addr.sin_addr, hp->h_length);
} else {
unsigned long f;
if ((f = inet_addr (srv)) == INADDR_NONE) {
fprintf (stderr, "ERROR: unknown host %s\n", srv);
queue_string (call_by, "Error in connecting to the RWHO server.\n");
return;
}
(void) bcopy ((char *) &f, (char *) &addr.sin_addr, sizeof (f));
}
addr.sin_port = htons (portnum);
addr.sin_family = AF_INET;
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
queue_string (call_by, "Socket error in connecting to rwhod. sorry.\n");
return;
}
if (connect (fd, &addr, sizeof (addr)) < 0) {
queue_string (call_by, "Connect error in connecting to rwhod. sorry.\n");
return;
}
while ((red = recv (fd, tbuf1, sizeof (tbuf1), 0)) > 0)
queue_write (call_by, tbuf1, red);
close (fd);
}
#endif /*FULL_RWHO */
void rwho_update (void)
{
struct descriptor_data *d;
char tbuf1[BUFFER_LEN];
rwhocli_pingalive ();
for (d = descriptor_list; d; d = d->next) {
if (d->connected && !Dark (d->player)) {
sprintf (tbuf1, "%d@%s", d->player, MUDNAME);
rwhocli_userlogin (tbuf1, db[d->player].name, d->connected_at);
}
}
}
#endif /* RWHO_SEND */
#ifdef LOCKOUT
static int quick_wild (char *s, char *d)
{
switch (*s) {
case '?':
return (wild (s + 1, (*d) ? d + 1 : d, 0, 0));
case '*':
return (wild (s + 1, d, 0, 0) || ((*d) ? wild (s, d + 1, 0, 0) : 0));
default:
return ((UPCASE (*s) != UPCASE (*d)) ? 0 : ((*s) ? wild (s + 1,
d + 1, 0, 0) : 1));
}
}
static int forbidden_site (const char *hname)
{
char buf[MAXHOSTNAMELEN], *newlin;
FILE *fp;
fp = fopen (LOCKOUT_FILE, "rb");
if (fp == NULL)
return 0;
while (!feof (fp)) {
fgets (buf, MAXHOSTNAMELEN, fp);
/* step on the newline */
if ((newlin = index (buf, '\n')) != NULL)
*newlin = '\0';
if (!strcasecmp (hname, buf)) {
fclose (fp);
return 1;
}
}
fclose (fp);
return 0;
}
static const char *addrout (struct in_addr a)
{
struct hostent *he;
he = gethostbyaddr ((const char*)&a, sizeof (a), AF_INET);
if (he) {
return he->h_name;
} else {
return inet_ntoa (a);
}
}
#endif /* LOCKOUT */
dbref short_page (const char *match)
{
struct descriptor_data *d;
dbref who1 = NOTHING;
int count = 0;
for (d = descriptor_list; d; d = d->next) {
if (d->connected) {
if (match && !string_prefix (db[d->player].name, match))
continue;
if (!string_compare (db[d->player].name, match)) {
count = 1;
who1 = d->player;
break;
}
who1 = d->player;
count++;
}
}
if (count > 1)
return AMBIGUOUS;
else if (count == 0)
return NOTHING;
return who1;
}
/* LWHO() function - really belongs in eval.c but needs stuff declared here */
/* this function only works for wizards and royalty */
void fun_lwho (char *buff, char *args[10], dbref privs, dbref dumm3)
{
struct descriptor_data *d;
char tbuf1[BUFFER_LEN];
buff[0] = '\0';
if (Hasprivs (privs)) {
for (d = descriptor_list; d; d = d->next) {
if (d->connected) {
if (!Dark (d->player) || Hasprivs (privs)) {
if (buff[0] == '\0')
sprintf (buff, "#%d", d->player);
else {
if ((strlen (buff) + 15) < BUFFER_LEN) {
sprintf (tbuf1, "%s #%d", buff, d->player);
strcpy (buff, tbuf1);
}
}
}
}
}
} else {
notify (privs, "Permission denied.");
strcpy (buff, "#-1");
}
}
#ifdef IDLE_TIMEOUT
void inactivity_check (void)
{
DESC *d;
register struct tm *idle;
time_t now;
int check, hrs, mns;
check = hrs = mns = 0;
now = time ((time_t *) 0);
for (mns = INACTIVITY_LIMIT; mns > 60; mns -= 60, hrs++);
DESC_ITER_CONN (d) {
if (!Wizard (d->player)) {
check = (now - d->last_time);
idle = gmtime ((time_t *) & check);
if ((idle->tm_hour > hrs) ||
((idle->tm_hour == hrs) && (idle->tm_min > mns))) {
notify (d->player, "\n*** Inactivity timeout ***\n");
boot_off (d->player);
}
}
}
}
#endif