/* Concentrator upgraded June 1990 Robert Hood */
/* modifed Concentrator to match Fuzzy & Randoms 1.5.4 changes = 6/90 Fuzzy */
/* modified interface.c to support LOTS of people, using a concentrator */
/* May 1990, Robert Hood */
/* Warning: this file has not been upgraded to match all of the changes */
/* made to bsd.c. January 1992, Lydia Leong */
#include "copyright.h"
#include <stdio.h>
#include <varargs.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "globals.h"
#ifdef MEM_CHECK
#include "mem_check.h"
#endif
#define BUFSIZE 0xFFFF
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 {
int descriptor;
int num;
int connected;
char *hostname;
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 message {
char *data;
short len;
struct message *next;
};
struct conc_list {
struct conc_list *next;
int sock, current, status;
struct descriptor_data *firstd;
struct message *first, *last;
char *incoming, *outgoing;
int ilen, olen;
} *firstc = 0;
void welcome_user();
void spew_message();
void writelog();
void queue_message(struct conc_list * c, char *data, int len);
struct timeval timeval_sub(struct timeval now, struct timeval then);
struct timeval msec_add(struct timeval t, int x);
struct timeval update_quotas(struct timeval last, struct timeval current);
void main_loop();
void raw_notify(dbref player, const char *msg);
void process_output(struct conc_list * c);
int process_input(struct descriptor_data * d, char *buf, int got);
void process_commands();
void dump_users(struct descriptor_data *call_by, char *match);
void free_text_block(struct text_block * t);
void main(int argc, char **argv);
void set_signals();
int msec_diff(struct timeval now, struct timeval then);
void clearstrings(struct descriptor_data * d);
void shutdownsock(struct descriptor_data * d);
struct descriptor_data *initializesock(struct sockaddr_in * a);
struct text_block *make_text_block(const char *s, int n);
void add_to_queue(struct text_queue * q, const char *b, int n);
int flush_queue(struct text_queue * q, int n);
int queue_write(struct descriptor_data * d, const char *b, int n);
int queue_string(struct descriptor_data * d, const char *s);
void freeqs(struct descriptor_data * d);
void welcome_user(struct descriptor_data * d);
char *strsave(const char *s);
void save_command(struct descriptor_data * d, const char *command);
void set_userstring(char **userstring, const char *command);
int do_command(struct descriptor_data * d, char *command);
int check_connect(struct descriptor_data * d, const char *msg);
void parse_connect(const char *msg, char *command, char *user, char *pass);
void close_sockets();
void emergency_shutdown(void);
void boot_off(dbref player);
int bailout(int sig, int code, struct sigcontext * scp);
char *time_format_1(long dt);
char *time_format_2(long dt);
void announce_connect(dbref);
void announce_disconnect(dbref);
int sigshutdown(int, int, struct sigcontext *);
#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers();
#endif
void rwho_update();
#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 *asterisk_line =
"*****************************************************************";
int sock;
int shutdown_flag = 0;
extern dbref speaker;
extern int errno;
extern int reserved;
extern dbref db_top;
int login_allow = 1;
char ccom[BUFFER_LEN];
dbref cplr;
char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN],
cf_nologinmotd_msg[BUFFER_LEN];
int port = TINYPORT;
int intport = INTERNAL_PORT;
void start_port()
{
int temp;
struct sockaddr_in sin;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 1) {
perror("socket");
exit(-1);
}
temp = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &temp, sizeof(temp));
sin.sin_family = AF_INET;
sin.sin_port = htons(intport);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
temp = bind(sock, (struct sockaddr *) & sin, sizeof(sin));
if (temp < 0) {
perror("bind");
exit(-1);
}
temp = listen(sock, 5);
if (temp < 0) {
perror("listen");
exit(-1);
}
}
struct timeval timeval_sub(struct timeval now, struct timeval then)
{
now.tv_sec -= then.tv_sec;
now.tv_usec -= then.tv_usec;
if (now.tv_usec < 0) {
now.tv_usec += 1000000;
now.tv_sec--;
}
return now;
}
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;
struct conc_list *c;
nslices = msec_diff(current, last) / COMMAND_TIME_MSEC;
if (nslices > 0) {
for (c = firstc; c; c = c->next)
for (d = c->firstd; 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 raw_notify(dbref player, const char *msg)
{
struct descriptor_data *d;
struct conc_list *c;
#ifdef COMPRESS
extern const char *uncompress(const char *);
msg = uncompress(msg);
#endif /* COMPRESS */
for (c = firstc; c; c = c->next) {
for (d = c->firstd; d; d = d->next) {
if (d->connected && d->player == player) {
queue_string(d, msg);
queue_write(d, "\n", 1);
process_output(c);
}
}
process_output(c);
}
}
void raw_broadcast(va_alist)
va_dcl
{
char buff[BUFFER_LEN];
va_list args;
int inflags;
char *template;
struct descriptor_data *d;
struct conc_list *c;
va_start(args);
inflags = va_arg(args, int);
template = va_arg(args, char *);
if(!template || !*template) return;
vsprintf(buff, template, args);
for (c = firstc; c; c = c->next) {
for (d = c->firstd; d; d = d->next) {
if (d->connected && (db[d->player].flags & inflags) == inflags) {
queue_string(d, buff);
queue_write(d, "\n", 1);
process_output(c);
}
}
process_output(c);
}
}
int process_input(d, buf, got)
struct descriptor_data *d;
char *buf;
int got;
{
char *p, *pend, *q, *qend;
d->last_time = time(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 = buf, qend = buf + 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(*q) && isprint(*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;
}
void process_commands()
{
int nprocessed;
struct descriptor_data *d, *dnext, *dlast;
struct conc_list *c;
struct text_block *t;
char header[4];
do {
nprocessed = 0;
for (c = firstc; c; c = c->next) {
dlast = 0;
for (d = c->firstd; 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);
process_output(c);
header[0] = 0;
header[1] = 2;
header[2] = d->num;
queue_message(c, header, 3);
process_output(c);
if (dlast)
dlast->next = dnext;
else
c->firstd = dnext;
free((void *)d);
break;
} else {
d->input.head = t->nxt;
if (!d->input.head)
d->input.tail = &d->input.head;
free_text_block(t);
}
}
dlast = d;
}
}
} while (nprocessed > 0);
}
void dump_users(struct descriptor_data *call_by, char *match)
{
struct descriptor_data *d;
struct conc_list *c;
time_t now;
int count = 0;
char tbuf1[BUFFER_LEN];
while (*match && isspace(*match))
match++;
if (!*match)
match = NULL;
time(&now);
if(Wizard(call_by->player)) {
#ifdef AT_DOING
queue_string(call_by, "Player Name Location On For Idle [Host
] / Doing\n");
#else
queue_string(call_by, "Player Name Location On For Idle [Host]\n");
#endif
} else {
#ifdef AT_DOING
queue_string(call_by, "Player Name On For Idle Doing\n");
#else
queue_string(call_by, "Player Name On For Idle\n");
#endif
}
for (c = firstc; c; c = c->next) {
for (d = c->firstd; 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 && Wizard(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->hostname);
if(Dark(d->player))
sprintf(tbuf1+strlen(tbuf1)," (Dark)");
} else {
if(!Dark(d->player)) {
#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) || Wizard(call_by->player)) {
queue_string(call_by, tbuf1);
queue_write(call_by, "\n", 1);
}
#ifdef AT_DOING
if(Wizard(call_by->player) && d->doing[0]) {
sprintf(tbuf1, "%46s %s", " ", d->doing);
queue_string(call_by, tbuf1);
queue_write(call_by, "\n", 1);
}
#endif
}
}
}
sprintf(tbuf1, "There %s %d user%s connected\n", (count == 1 ? "is" : "are"),
count, (count == 1 ? "" : "s"));
queue_string(call_by, tbuf1);
}
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
}
#ifndef BOOLEXP_DEBUGGING
void main(int argc, char **argv)
{
int pid;
const char *def_db_in = DEF_DB_IN;
const char *def_db_out = DEF_DB_OUT;
if(argc > 1) {
--argc;
def_db_in = *++argv;
}
if(argc > 1) {
--argc;
def_db_out = *++argv;
}
if(argc > 1) {
--argc;
port = atoi(*++argv);
}
if(argc > 1) {
--argc;
intport = atoi(*++argv);
}
srand(time(NULL));
reserved = open("/dev/null", O_RDWR);
if (init_game(def_db_in, def_db_out) < 0) {
writelog("ERROR: Couldn't load %s! Exiting.\n", def_db_in);
exit(2);
}
pid = vfork();
if (pid < 0) {
perror("fork");
exit(-1);
}
if (pid == 0) {
char pstr[32], istr[32], clvl[32];
/* Add port argument to concentrator */
sprintf(pstr, "%d", port);
sprintf(istr, "%d", intport);
sprintf(clvl, "%d", 1);
execl("concentrate", "conc", pstr, istr, clvl, 0);
}
set_signals();
start_port(port);
main_loop();
close_sockets();
dump_database();
exit(0);
}
#endif
void set_signals(void)
{
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, (void *) bailout);
signal(SIGTERM, (void *) bailout);
}
int msec_diff(struct timeval now, struct timeval then)
{
return ((now.tv_sec - then.tv_sec) * 1000
+ (now.tv_usec - then.tv_usec) / 1000);
}
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;
}
}
void shutdownsock(struct descriptor_data *d)
{
struct conc_list *c;
struct descriptor_data *d1;
if (d->connected) {
spew_message(d, LEAVE_MSG_FILE);
writelog("DISCONNECT descriptor %d,%d player %s(%d)\n", d->descriptor,
d->num, db[d->player].name, d->player);
announce_disconnect(d->player);
} else {
writelog("DISCONNECT descriptor %d,%d never connected\n", d->descriptor,
d->num);
}
for(c = firstc; c; c = c->next) {
for(d1 = c->firstd; d1; d1 = d1->next) {
process_output(c);
}
}
clearstrings(d);
freeqs(d);
}
struct descriptor_data *
initializesock(struct sockaddr_in * a)
{
struct descriptor_data *d;
d = (struct descriptor_data *) malloc(sizeof(struct descriptor_data));
if(!d)
panic("Out of memory.");
#ifdef MEM_CHECK
add_check("descriptor");
#endif
d->connected = 0;
d->player = 0;
d->output_prefix = 0;
d->output_suffix = 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 = 0;
d->raw_input_at = 0;
d->quota = COMMAND_BURST_SIZE;
d->last_time = 0;
d->address = *a; /* This will be the address of the
* concentrator */
d->hostname = ""; /* This will be set during connect */
#ifdef AT_DOING
d->doing[0] = '\0';
#endif
welcome_user(d);
return d;
}
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;
}
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;
}
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;
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;
}
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;
}
int queue_string(struct descriptor_data * d, const char *s)
{
return queue_write(d, s, strlen(s));
}
void freeqs(struct descriptor_data * d)
{
struct text_block *cur, *next;
cur = d->output.head;
while (cur) {
next = cur->nxt;
free_text_block(cur);
cur = next;
}
d->output.head = 0;
d->output.tail = &d->output.head;
cur = d->input.head;
while (cur) {
next = cur->nxt;
free_text_block(cur);
cur = next;
}
d->input.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;
}
void welcome_user(struct descriptor_data * d)
{
spew_message(d, WELCOME_MSG_FILE);
}
void spew_message(struct descriptor_data *d, char *filename)
{
int n, fd;
char buf[512];
close(reserved);
if ((fd = open(filename, O_RDONLY)) != -1) {
while ((n = read(fd, buf, 512)) > 0)
queue_write(d, buf, n);
close(fd);
queue_write(d, "\n", 1);
}
reserved = open("/dev/null", O_RDWR);
}
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;
}
void save_command(struct descriptor_data * d, const char *command)
{
add_to_queue(&d->input, command, strlen(command) + 1);
}
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(*command) && isspace(*command))
command++;
if (*command)
*userstring = strsave(command);
}
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));
if (d->output_suffix) {
queue_string(d, d->output_suffix);
queue_write(d, "\n", 1);
}
} else if (d->connected &&
!strncmp(command, PREFIX_COMMAND, strlen(PREFIX_COMMAND))) {
set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND));
} else if (d->connected &&
!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;
}
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);
writelog("FAILED CONNECT %s on descriptor %d,%d\n", user, d->descriptor, d->num);
} else {
writelog("CONNECTED %s(%d) on descriptor %d,%d %s\n",
db[player].name, player, d->descriptor, d->num, d->hostname);
#ifdef AT_DOING
d->doing[0] = '\0';
#endif
d->connected = 1;
d->connected_at = time((time_t *) 0);
d->player = player;
if(!login_allow && !Wizard(player)) {
spew_message(d, DISABLE_MSG_FILE);
raw_notify(player, asterisk_line);
if(cf_nologinmotd_msg && *cf_nologinmotd_msg)
raw_notify(player, cf_nologinmotd_msg);
raw_notify(player, asterisk_line);
return 0;
}
spew_message(d, CONNECT_MSG_FILE);
if(Wizard(player)) {
spew_message(d, WIZARD_MSG_FILE);
}
/* set the Lastsite attribute */
atr_add(player, "LASTSITE", d -> addr, GOD, NOTHING);
announce_connect(player);
do_look_around(player);
/* give wizards their message, too - d'mike 7/15/91 */
if (db[player].flags & HAVEN) {
notify(player, "Your HAVEN flag is set. You cannot receive pages.");
}
}
} else if (!strncmp(command, "cr", 2)) {
#ifndef WCREAT
player = create_player(user, password);
if (player == NOTHING) {
queue_string(d, create_fail);
writelog("FAILED CREATE %s on descriptor %d,%d %s\n",
user, d->descriptor, d->num, d->hostname);
} else {
writelog("CREATED %s(%d) on descriptor %d,%d %s\n",
db[player].name, player, d->descriptor, d->num, d->hostname);
#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 && *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);
}
#else
spew_message(d, REGISTER_MSG_FILE);
#endif /* WCREAT */
} else {
welcome_user(d);
}
return 1;
}
void parse_connect(const char *msg, char *command, char *user, char *pass)
{
char *p;
while (*msg && isascii(*msg) && isspace(*msg))
msg++;
p = command;
while (*msg && isascii(*msg) && !isspace(*msg))
*p++ = *msg++;
*p = '\0';
while (*msg && isascii(*msg) && isspace(*msg))
msg++;
p = user;
while (*msg && isascii(*msg) && !isspace(*msg))
*p++ = *msg++;
*p = '\0';
while (*msg && isascii(*msg) && isspace(*msg))
msg++;
p = pass;
while (*msg && isascii(*msg) && !isspace(*msg))
*p++ = *msg++;
*p = '\0';
}
void close_sockets(void)
{
struct conc_list *c;
#ifdef RWHO_SEND
rwhocli_shutdown();
#endif
for (c = firstc; c; c = c->next) {
/* conc.c now handles printing the Going Down - Bye message */
shutdown(c->sock, 0);
close(c->sock);
}
close(sock);
}
void emergency_shutdown(void)
{
close_sockets();
}
int bailout(int sig, int code, struct sigcontext *scp)
{
int i;
writelog("BAILOUT: caught signal %d code %d", sig, code);
panic("PANIC on spurious signal");
_exit(7);
return 0;
}
char *time_format_1(long dt)
{
register struct tm *delta;
static char buf[64];
if(dt < 0) dt = 0;
delta = gmtime(&dt);
if (delta->tm_yday > 0) {
sprintf(buf, "%dd %02d:%02d",
delta->tm_yday, delta->tm_hour, delta->tm_min);
} else {
sprintf(buf, "%02d:%02d",
delta->tm_hour, delta->tm_min);
}
return buf;
}
char * time_format_2(long dt)
{
register struct tm *delta;
static char buf[64];
if(dt < 0) dt = 0;
delta = gmtime(&dt);
if (delta->tm_yday > 0) {
sprintf(buf, "%dd", delta->tm_yday);
} else if (delta->tm_hour > 0) {
sprintf(buf, "%dh", delta->tm_hour);
} else if (delta->tm_min > 0) {
sprintf(buf, "%dm", delta->tm_min);
} else {
sprintf(buf, "%ds", delta->tm_sec);
}
return buf;
}
void announce_connect(dbref player)
{
dbref loc;
ATTR *temp;
char buf[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(buf,"%d@%s", player, MUDNAME);
rwhocli_userlogin(buf, db[player].name, time((time_t *) 0));
#endif
raw_notify(player, asterisk_line);
if(cf_motd_msg && *cf_motd_msg)
raw_notify(player, cf_motd_msg);
raw_notify(player, " ");
if(Wizard(player) && cf_wizmotd_msg && *cf_wizmotd_msg)
raw_notify(player, cf_wizmotd_msg);
raw_notify(player, asterisk_line);
sprintf(buf, "%s has connected.", db[player].name);
notify_except(db[player].contents, player, buf);
/* added to allow player's inventory to hear a player connect */
if(!Dark(player))
notify_except(db[loc].contents, player, buf);
temp = atr_get(player, "ACONNECT");
if (temp) {
char *s = safe_uncompress(temp->value);
parse_que(player, s, player);
free(s);
}
}
void announce_disconnect(dbref player)
{
dbref loc;
int num;
ATTR *temp;
struct descriptor_data *d;
struct conc_list *c;
char tbuf1[BUFFER_LEN];
if ((loc = getloc(player)) == NOTHING)
return;
speaker = player;
for (num = 0, c = firstc; c; c = c->next)
for (d = c->firstd; 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
extern char *reconstruct_message();
void do_doing(player, arg1, arg2)
dbref player;
char *arg1, *arg2;
{
char *message, 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);
notify(player, "Set.");
}
#endif
#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers(call_by)
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(*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)) == -1L) {
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)) < 0) {
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 = read(fd, tbuf1, sizeof(tbuf1))) > 0)
queue_write(call_by, tbuf1, red);
close(fd);
}
#endif /*FULL_RWHO*/
void rwho_update()
{
struct descriptor_data *d;
struct conc_list *c;
char tbuf1[BUFFER_LEN];
rwhocli_pingalive();
for (c = firstc; c; c = c->next) {
for (d = c->firstd; 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
int quick_wild(s, d)
char *s;
char *d;
{
switch(*s) {
case '?':
return(wild(s+1, (*d) ? d+1 : d));
case '*':
return(wild(s+1, d) || ((*d) ? wild(s,d+1) : 0));
default:
return((UPCASE(*s) != UPCASE(*d)) ? 0 : ((*s) ? wild(s+1,d+1) : 1));
}
}
int forbidden_site(hname)
const char *hname;
{
char buf[MAXHOSTNAMELEN], *newlin;
FILE *fp;
fp = fopen(LOCKOUT_FILE, "r");
while ((fp != NULL) && (!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;
}
const char *addrout(a)
long a;
{
static char buf[MAXHOSTNAMELEN];
struct hostent *he;
he = gethostbyaddr(&a, sizeof(a), AF_INET);
if (he) {
return he->h_name;
} else {
a = ntohl(a);
sprintf (buf, "%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff,
(a >> 8) & 0xff, a & 0xff);
return buf;
}
}
#endif /* LOCKOUT */
dbref short_page(match)
const char *match;
{
struct descriptor_data *d;
struct conc_list *c;
dbref who1 = NOTHING;
int count = 0;
for(c = firstc; c; c = c->next) {
for(d = c->firstd; 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;
}
void main_loop()
{
struct message *ptr;
int found, newsock, lastsock, len;
int accepting = 1;
struct timeval tv;
struct sockaddr_in sin;
fd_set in, out;
char buf[BUFFER_LEN];
struct conc_list *c, *tempc, *nextc, *lastc;
struct descriptor_data *d, *tempd;
struct timeval last_slice, current_time;
struct timeval next_slice;
struct timeval slice_timeout, timeout;
short templen;
time_t tt;
extern void dispatch();
lastsock = sock + 1;
while (!shutdown_flag) {
gettimeofday(¤t_time, (struct timezone *) 0);
last_slice = update_quotas(last_slice, current_time);
process_commands();
if (shutdown_flag)
break;
dispatch();
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(&in);
FD_ZERO(&out);
FD_SET(sock, &in);
for (c = firstc; c; c = c->next)
process_output(c);
for (c = firstc; c; c = c->next)
if (c->sock) {
if (c->ilen < BUFSIZE)
FD_SET(c->sock, &in);
len = c->first ? c->first->len : 0;
while (c->first && ((c->olen + len + 2) < BUFSIZE)) {
templen = c->first->len;
bcopy(&templen, c->outgoing + c->olen, 2);
bcopy(c->first->data, c->outgoing + c->olen + 2, len);
c->olen += len + 2;
ptr = c->first;
c->first = ptr->next;
free((void *)ptr->data);
free((void *)ptr);
if (c->last == ptr)
c->last = 0;
len = c->first ? c->first->len : 0;
}
if (c->olen)
FD_SET(c->sock, &out);
} else
timeout = slice_timeout;
if((found = select(lastsock, &in, &out, (fd_set *) 0, &timeout)) < 0) {
if(errno != EINTR) {
perror("select");
return;
}
} else {
if(!found) {
do_top();
do_top();
do_top();
continue;
}
if (accepting && FD_ISSET(sock, &in)) {
len = sizeof(sin);
newsock = accept(sock, (struct sockaddr *) & sin, &len);
if (newsock >= 0) {
if (newsock >= lastsock)
lastsock = newsock + 1;
if (fcntl(newsock, F_SETFL, FNDELAY) == -1) {
perror("make_nonblocking: fcntl");
}
tempc = (struct conc_list *)malloc(sizeof(struct conc_list));
if(!tempc)
panic("Out of memory.");
tempc->next = firstc;
tempc->firstd = 0;
tempc->first = 0;
tempc->last = 0;
tempc->status = 0;
/* Imcomming and outgoing I/O buffers */
tempc->incoming = (char *)malloc(BUFSIZE);
if(!tempc->incoming)
panic("Out of memory.");
tempc->ilen = 0;
tempc->outgoing = (char *)malloc(BUFSIZE);
if(!tempc)
panic("Out of memory.");
tempc->olen = 0;
firstc = tempc;
firstc->sock = newsock;
writelog("CONCENTRATOR CONNECT: sock %d, addr %x\n", newsock,
sin.sin_addr.s_addr);
}
}
for (c = firstc; c; c = nextc) {
nextc = c->next;
#ifdef CHECKC
if (!(c->sock))
writelog("CONSISTENCY CHECK: Concentrator found with null socket #\n");
#endif
if ((FD_ISSET(c->sock, &in)) && (c->ilen < BUFSIZE)) {
len = recv(c->sock, c->incoming + c->ilen,
BUFSIZE - c->ilen, 0);
if (len == 0) {
struct message *mptr, *tempm;
writelog("CONCENTRATOR DISCONNECT: %d\n", c->sock);
close(c->sock);
d = c->firstd;
while (d) {
shutdownsock(d);
tempd = d;
d = d->next;
free((void *)tempd);
}
if (firstc == c)
firstc = firstc->next;
else
lastc->next = c->next;
free((void *)c->incoming);
free((void *)c->outgoing);
mptr = c->first;
while (mptr) {
tempm = mptr;
mptr = mptr->next;
free((void *)mptr->data);
free((void *)mptr);
}
free((void *)c);
break;
} else if (len < 0) {
writelog("recv: %s\n", strerror(errno));
} else {
int num;
c->ilen += len;
while (c->ilen > 2) {
bcopy(c->incoming, &templen, 2);
#ifdef CHECKC
if (templen < 1)
writelog("CONSISTENCY CHECK: Message recived with length < 1\n");
#endif
if (c->ilen >= (templen + 2)) {
num = *(c->incoming + 2);
/* Is it coming from the command user #? */
if (num == 0) {
/* Proccess commands */
switch (*(c->incoming + 3)) {
case 1: /* connect */
tt = time((time_t *) 0);
d = initializesock(&sin);
d->descriptor = c->sock;
d->next = c->firstd;
c->firstd = d;
d->num = *(c->incoming + 4);
d->hostname= (char *)malloc(templen - 5);
if(!d->hostname)
panic("Out of memory.");
bcopy(c->incoming + 9, d->hostname,
templen - 6);
*(d->hostname + templen - 7) = 0;
writelog("USER CONNECT %d,%d from host %s at %s\n",
c->sock, d->num, d->hostname, ctime(&tt));
break;
case 2: /* disconnect */
tempd = 0;
d = c->firstd;
num = *(c->incoming + 4);
while (d) {
if (d->num == num) {
writelog("USER ABORTED CONNECTION %d,%d\n", c->sock,
d->num);
shutdownsock(d);
if (c->firstd == d)
c->firstd = d->next;
else
tempd->next = d->next;
free((void *)d);
break;
}
tempd = d;
d = d->next;
}
#ifdef CHECKC
if (!d)
writelog("CONSISTENCY CHECK: Disconnect Received for unknown user %d,%d\n", c->sock, num);
#endif
break;
/*
* This take a message from a concentrator, and logs it
* in the log file
*/
case 4:
{
bcopy(c->incoming + 4, buf,
templen - 2);
*(buf + templen - 1) = 0;
writelog(buf);
break;
}
#ifdef CHECKC
default:
writelog("CONSISTENCY CHECK: Received unknown command from concentrator\n");
#endif
}
} else {
d = c->firstd;
while (d) {
if (d->num == num) {
process_input(d, c->incoming + 3,
templen - 1);
break;
}
d = d->next;
}
#ifdef CHECKC
if (!d)
writelog("CONSISTENCY CHECK: Message received for unknown user %d,%d\n", c->sock, num);
#endif
}
bcopy(c->incoming + templen + 2, c->incoming,
(c->ilen - templen - 2));
c->ilen = c->ilen - templen - 2;
} else
break;
}
}
}
lastc = c;
}
}
/* Send data loop */
for (c = firstc; c; c = c->next) {
if (FD_ISSET(c->sock, &out)) {
if (c->olen) {
len = send(c->sock, c->outgoing, c->olen, 0);
if (len > 0) {
c->olen -= len;
bcopy(c->outgoing + len, c->outgoing, c->olen);
}
}
}
}
}
}
void process_output(struct conc_list * c)
{
struct descriptor_data *d;
struct text_block **qp, *cur;
short templen;
for (d = c->firstd; d; d = d->next) {
qp = &d->output.head;
cur = *qp;
if (cur) {
if (cur->nchars < 512) {
if ((c->olen + cur->nchars + 3) < BUFSIZE) {
templen = cur->nchars + 1;
bcopy(&templen, c->outgoing + c->olen, 2);
*(c->outgoing + c->olen + 2) = d->num;
strncpy(c->outgoing + c->olen + 3, cur->start, cur->nchars);
d->output_size -= cur->nchars;
c->olen += cur->nchars + 3;
if (!cur->nxt)
d->output.tail = qp;
*qp = cur->nxt;
free_text_block(cur);
}
} else {
if ((c->olen + 512 + 3) < BUFSIZE) {
templen = 512 + 1;
bcopy(&templen, c->outgoing + c->olen, 2);
*(c->outgoing + c->olen + 2) = d->num;
strncpy(c->outgoing + c->olen + 3, cur->start, 512);
d->output_size -= 512;
c->olen += 512 + 3;
cur->nchars -= 512;
cur->start += 512;
}
}
}
}
}
void boot_off(dbref player)
{
struct conc_list *c;
struct descriptor_data *d, *lastd;
char header[4];
for (c = firstc; c; c = c->next) {
lastd = 0;
for (d = c->firstd; d; d = d->next) {
if (d->connected && d->player == player) {
shutdownsock(d);
header[0] = 0;
header[1] = 2;
header[2] = d->num;
queue_message(c, header, 3);
process_output(c);
if (lastd)
lastd->next = d->next;
else
c->firstd = d->next;
free((void *) d);
return;
}
lastd = d;
}
}
}
void queue_message(struct conc_list * c, char *data, int len)
{
struct message *ptr;
ptr = (struct message *) malloc(sizeof(struct message));
if(!ptr)
panic("Out of memory.");
ptr->data = (char *) malloc(len);
if(!ptr->data)
panic("Out of memory.");
ptr->len = len;
bcopy(data, ptr->data, len);
ptr->next = 0;
if (c->last == 0)
c->first = ptr;
else
c->last->next = ptr;
c->last = ptr;
}
void writelog(va_alist)
va_dcl
{
char buf[BUFFER_LEN];
va_list args;
char *fmt;
va_start(args);
fmt = va_arg(args, char *);
(void) vsprintf(buf, fmt, args);
buf[BUFFER_LEN -1] = '\0';
fprintf(stderr, buf);
}