/* netcommon.c - Network utility routines */
/* This file contains routines used by the networking code that do not
* depend on the implementation of the networking code. The network-specific
* portions of the descriptor data structure are not used.
*/
#include "copyright.h"
#include <varargs.h>
#include <stdio.h>
#include <string.h>
#ifdef WANT_ANSI
#ifdef __STDC__
#include <stdlib.h>
#include <unistd.h>
#endif
#endif
#include "db.h"
#include "mudconf.h"
#include "interface.h"
#include "command.h"
#include "externs.h"
#include "rwho_clilib.h"
/* ---------------------------------------------------------------------------
* timeval_sub: return difference between two times as a timeval
*/
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;
}
/* ---------------------------------------------------------------------------
* msec_diff: return difference between two times in msec
*/
int msec_diff(struct timeval now, struct timeval then)
{
return ((now.tv_sec - then.tv_sec) * 1000 +
(now.tv_usec - then.tv_usec) / 1000);
}
/* ---------------------------------------------------------------------------
* msec_add: add milliseconds to a timeval
*/
struct timeval msec_add(struct timeval t, int x)
{
t.tv_sec += x / 1000;
t.tv_usec += (x % 1000) * 1000;
if (t.tv_usec >= 1000000) {
t.tv_sec += t.tv_usec / 1000000;
t.tv_usec = t.tv_usec % 1000000;
}
return t;
}
/* ---------------------------------------------------------------------------
* update_quotas: Update timeslice quotas
*/
struct timeval update_quotas(struct timeval last, struct timeval current)
{
int nslices;
DESC *d;
nslices = msec_diff(current, last) / mudconf.timeslice;
if (nslices > 0) {
DESC_ITER_ALL(d) {
d->quota += mudconf.cmd_quota_incr * nslices;
if (d->quota > mudconf.cmd_quota_max)
d->quota = mudconf.cmd_quota_max;
}
}
return msec_add(last, nslices * mudconf.timeslice);
}
/* ---------------------------------------------------------------------------
* raw_notify: write a message to a player
*/
void raw_notify(dbref player, const char *msg)
{
DESC *d;
if (!msg || !*msg) return;
if (!(Flags(player) & PLAYER_CONNECT)) return;
DESC_ITER_PLAYER(player, d) {
queue_string(d, msg);
queue_write(d, "\r\n", 2);
}
}
/* ---------------------------------------------------------------------------
* raw_broadcast: Send message to players who have indicated flags
*/
#ifdef NEVER
void raw_broadcast (int inflags, char *template,
int a1, int a2, int a3, int a4, int a5, int a6)
{
char *buff;
DESC *d;
if (!template || !*template) return;
buff=alloc_lbuf("raw_broadcast");
sprintf(buff, template, a1, a2, a3, a4, a5, a6);
DESC_ITER_CONN(d) {
if ((Flags(d->player) & inflags) == inflags) {
queue_string(d, buff);
queue_write(d, "\r\n", 2);
process_output(d);
}
}
free_lbuf(buff);
}
#else
void raw_broadcast (va_alist)
va_dcl
{
char *buff;
DESC *d;
int inflags;
char *template;
va_list ap;
va_start(ap);
inflags = va_arg(ap, int);
template = va_arg(ap, char *);
if (!template || !*template) return;
buff=alloc_lbuf("raw_broadcast");
vsprintf(buff, template, ap);
DESC_ITER_CONN(d) {
if ((Flags(d->player) & inflags) == inflags) {
queue_string(d, buff);
queue_write(d, "\r\n", 2);
process_output(d);
}
}
free_lbuf(buff);
va_end(ap);
}
#endif
/* ---------------------------------------------------------------------------
* clearstrings: clear out prefix and suffix strings
*/
void clearstrings(DESC *d)
{
if (d->output_prefix) {
free_lbuf(d->output_prefix);
d->output_prefix = NULL;
}
if (d->output_suffix) {
free_lbuf(d->output_suffix);
d->output_suffix = NULL;
}
}
/* ---------------------------------------------------------------------------
* queue_write: Add text to the output queue for the indicated descriptor.
*/
void queue_write(DESC *d, const char *b, int n)
{
int left;
char *buf;
TBLOCK *tp;
if (n <= 0) return;
if (d->output_size + n > mudconf.output_limit)
process_output(d);
left = mudconf.output_limit - d->output_size - n;
if (left < 0) {
tp = d->output_head;
if (tp == NULL) {
STARTLOG(LOG_PROBLEMS,"QUE","WRITE")
log_text((char *)"Flushing when output_head is null!");
ENDLOG
} else {
STARTLOG(LOG_NET,"NET","WRITE")
buf = alloc_lbuf("queue_write.LOG");
sprintf(buf,
"[%d/%s] Output buffer overflow, %d chars discarded by ",
d->descriptor, d->addr, tp->hdr.nchars);
log_text(buf);
free_lbuf(buf);
log_name(d->player);
ENDLOG
d->output_size -= tp->hdr.nchars;
d->output_head = tp->hdr.nxt;
d->output_lost += tp->hdr.nchars;
if (d->output_head == NULL) d->output_tail = NULL;
free_lbuf(tp);
}
}
/* Allocate an output buffer if needed */
if (d->output_head == NULL) {
tp = (TBLOCK *)alloc_lbuf("queue_write.new");
tp->hdr.nxt = NULL;
tp->hdr.start = tp->data;
tp->hdr.end = tp->data;
tp->hdr.nchars = 0;
d->output_head = tp;
d->output_tail = tp;
} else {
tp = d->output_tail;
}
/* Now tp points to the last buffer in the chain */
d->output_size += n;
d->output_tot += n;
do {
/* See if there is enough space in the buffer to hold the
* string. If so, copy it and update the pointers..
*/
left = LBUF_SIZE - (tp->hdr.end - (char *)tp);
if (n <= left) {
strncpy(tp->hdr.end, b, n);
tp->hdr.end += n;
tp->hdr.nchars += n;
n = 0;
} else {
/* It didn't fit. Copy what will fit and then allocate
* another buffer and retry.
*/
if (left > 0) {
strncpy(tp->hdr.end, b, left);
tp->hdr.end += left;
tp->hdr.nchars += left;
b += left;
n -= left;
}
tp = (TBLOCK *)alloc_lbuf("queue_write.extend");
tp->hdr.nxt = NULL;
tp->hdr.start = tp->data;
tp->hdr.end = tp->data;
tp->hdr.nchars = 0;
d->output_tail->hdr.nxt = tp;
d->output_tail = tp;
}
} while (n > 0);
}
void queue_string(DESC *d, const char *s)
{
if (s)
queue_write(d, s, strlen(s));
}
void freeqs(DESC *d)
{
TBLOCK *tb, *tnext;
CBLOCK *cb, *cnext;
tb = d->output_head;
while (tb) {
tnext = tb->hdr.nxt;
free_lbuf(tb);
tb = tnext;
}
d->output_head = NULL;
d->output_tail = NULL;
cb = d->input_head;
while (cb) {
cnext = (CBLOCK *)cb->hdr.nxt;
free_lbuf(cb);
cb = cnext;
}
d->input_head = NULL;
d->input_tail = NULL;
if (d->raw_input)
free_lbuf(d->raw_input);
d->raw_input = NULL;
d->raw_input_at = NULL;
}
/* ---------------------------------------------------------------------------
* desc_addhash: Add a net descriptor to its player hash list.
*/
static void desc_addhash (DESC *d)
{
dbref player;
DESC *hdesc;
player = d->player;
hdesc = (DESC *)nhashfind((int) player, &mudstate.desc_htab);
if (hdesc == NULL) {
d->hashnext = NULL;
nhashadd((int) player, (int *) d, &mudstate.desc_htab);
} else {
d->hashnext = hdesc;
nhashrepl((int) player, (int *) d, &mudstate.desc_htab);
}
}
/* ---------------------------------------------------------------------------
* desc_delhash: Remove a net descriptor from its player hash list.
*/
static void desc_delhash (DESC *d)
{
DESC *hdesc, *last;
dbref player;
player = d->player;
last = NULL;
hdesc = (DESC *)nhashfind((int) player, &mudstate.desc_htab);
while (hdesc != NULL) {
if (d == hdesc) {
if (last == NULL) {
if (d->hashnext == NULL) {
nhashdelete((int) player,
&mudstate.desc_htab);
} else {
nhashrepl((int) player,
(int *)d->hashnext,
&mudstate.desc_htab);
}
} else {
last->hashnext = d->hashnext;
}
break;
}
last = hdesc;
hdesc = hdesc->hashnext;
}
d->hashnext = NULL;
}
void welcome_user(DESC *d)
{
if (d->host_info & H_REGISTRATION)
fcache_dump(d, mudstate.creg_fcache);
else
fcache_dump(d, mudstate.conn_fcache);
}
int fcache_read (FBLOCK **cp, char *filename)
{
int n, nmax, fd, tchars;
char *bufp;
FBLOCK *fp, *tfp;
/* Free a prior buffer chain */
fp = *cp;
while (fp != NULL) {
tfp = fp->hdr.nxt;
free_mbuf(fp);
fp = tfp;
}
*cp = NULL;
/* Read the text file into a new chain */
close(mudstate.reserved_fileid);
if ((fd = open(filename, O_RDONLY, 0)) == -1) {
/* Failure: log the event */
STARTLOG(LOG_PROBLEMS,"FIL","OPEN")
bufp = alloc_mbuf("fcache_read.LOG");
sprintf(bufp, "Couldn't open file '%s'.", filename);
log_text(bufp);
free_mbuf(bufp);
ENDLOG
mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0);
return -1;
}
/* Success. Allocate and initialize the first buffer */
fp = (FBLOCK *)alloc_mbuf("fcache_read.first");
fp->hdr.nxt = NULL;
fp->hdr.nchars = 0;
*cp = fp;
tfp = NULL;
tchars = 0;
/* Read in the first chunk of the file */
nmax = MBUF_SIZE - sizeof(FBLKHDR);
bufp = fp->data;
n = read(fd, bufp, nmax);
#ifdef VMS
if ((n < nmax) && (n > 0))
{
bufp[n] = bufp[n-1];
bufp[n-1] = '\r';
bufp[++n] = 0;
}
#endif VMS
while (n > 0) {
/* If we didn't read in all we wanted, update the pointers and
* try to fill the current buffer
*/
fp->hdr.nchars += n;
tchars += n;
if (fp->hdr.nchars < (MBUF_SIZE - sizeof(FBLKHDR))) {
nmax -= n;
bufp += n;
} else {
/* We filled the current buffer. Go get a new one. */
tfp = fp;
fp = (FBLOCK *)alloc_mbuf("fcache_read.next");
fp->hdr.nxt = NULL;
fp->hdr.nchars = 0;
tfp->hdr.nxt = fp;
nmax = MBUF_SIZE - sizeof(FBLKHDR);
bufp = fp->data;
}
/* Read in the next chunk of the file */
n = read(fd, bufp, nmax);
#ifdef VMS
if ((n < nmax) && (n > 0))
{
bufp[n] = bufp[n-1];
bufp[n-1] = '\r';
bufp[++n] = 0;
}
#endif VMS
}
close(fd);
mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0);
if (fp->hdr.nchars == 0) {
free_mbuf(fp);
if (tfp == NULL)
*cp = NULL;
else
tfp->hdr.nxt = NULL;
}
return tchars;
}
void fcache_dump (DESC *d, FBLOCK *fp)
{
while(fp != NULL) {
queue_write(d, fp->data, fp->hdr.nchars);
fp = fp->hdr.nxt;
}
}
void fcache_send (dbref player, FBLOCK *fp)
{
DESC *d;
DESC_ITER_PLAYER(player, d) {
fcache_dump(d, fp);
}
}
void fcache_load (dbref player)
{
int guest, conn, creg, regf, motd, wmotd, quit, down, site, crea, full;
char *buf;
guest = fcache_read(&mudstate.guest_fcache, mudconf.guest_file);
conn = fcache_read(&mudstate.conn_fcache, mudconf.conn_file);
creg = fcache_read(&mudstate.creg_fcache, mudconf.creg_file);
regf = fcache_read(&mudstate.regf_fcache, mudconf.regf_file);
motd = fcache_read(&mudstate.motd_fcache, mudconf.motd_file);
wmotd = fcache_read(&mudstate.wizmotd_fcache, mudconf.wizmotd_file);
quit = fcache_read(&mudstate.quit_fcache, mudconf.quit_file);
down = fcache_read(&mudstate.down_fcache, mudconf.down_file);
site = fcache_read(&mudstate.site_fcache, mudconf.site_file);
crea = fcache_read(&mudstate.crea_fcache, mudconf.crea_file);
full = fcache_read(&mudstate.full_fcache, mudconf.full_file);
if ((player != NOTHING) && !Quiet(player)) {
buf = alloc_lbuf("fcache_load");
sprintf(buf, "File sizes: Guest...%d Connect...%d Conn/Reg...%d Crea/Reg...%d Motd...%d Wizmotd...%d Quit...%d Down...%d Full...%d Conn/Badsite...%d Newuser...%d",
guest, conn, creg, regf, motd, wmotd, quit, down,
full, site, crea);
notify(player, buf);
free_lbuf(buf);
}
}
void fcache_init ()
{
mudstate.guest_fcache = NULL;
mudstate.conn_fcache = NULL;
mudstate.creg_fcache = NULL;
mudstate.regf_fcache = NULL;
mudstate.motd_fcache = NULL;
mudstate.wizmotd_fcache = NULL;
mudstate.quit_fcache = NULL;
mudstate.down_fcache = NULL;
mudstate.site_fcache = NULL;
mudstate.crea_fcache = NULL;
mudstate.full_fcache = NULL;
fcache_load(NOTHING);
}
void save_command(DESC *d, const char *command)
{
CBLOCK *cp;
cp = (CBLOCK *)alloc_lbuf("save_command");
cp->hdr.nxt = NULL;
strcpy(cp->cmd, command);
if (d->input_tail == NULL)
d->input_head = cp;
else
d->input_tail->hdr.nxt = cp;
d->input_tail = cp;
}
static void set_userstring(char **userstring, const char *command)
{
while (*command && isascii(*command) && isspace(*command))
command++;
if (!*command) {
if (*userstring != NULL) {
free_lbuf(*userstring);
*userstring = NULL;
}
} else {
if (*userstring == NULL)
*userstring = alloc_lbuf("set_userstring");
strcpy(*userstring, command);
}
}
static 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';
}
static const char * time_format_1(time_t dt)
{
register struct tm *delta;
static char buf[64];
if (dt < 0) dt = 0;
delta = gmtime(&dt);
if (delta->tm_yday > 0) {
sprintf(buf, "%dd %02d:%02d",
delta->tm_yday, delta->tm_hour, delta->tm_min);
} else {
sprintf(buf, "%02d:%02d",
delta->tm_hour, delta->tm_min);
}
return buf;
}
static const char *time_format_2(time_t dt)
{
register struct tm *delta;
static char buf[64];
if (dt < 0) dt = 0;
delta = gmtime(&dt);
if (delta->tm_yday > 0) {
sprintf(buf, "%dd", delta->tm_yday);
} else if (delta->tm_hour > 0) {
sprintf(buf, "%dh", delta->tm_hour);
} else if (delta->tm_min > 0) {
sprintf(buf, "%dm", delta->tm_min);
} else {
sprintf(buf, "%ds", delta->tm_sec);
}
return buf;
}
static void announce_connect(dbref player, DESC *d)
{
dbref loc, aowner;
int aflags, num;
char *buf, *atr_temp, *time_str;
time_t tt;
DESC *dtemp;
desc_addhash(d);
if (atr_temp = atr_pget(player, A_TIMEOUT, &aowner, &aflags)) {
d->timeout = atoi(atr_temp);
if (d->timeout <= 0) d->timeout = mudconf.idle_timeout;
}
free_lbuf(atr_temp);
loc = Location(player);
s_Flags(player, Flags(player) | PLAYER_CONNECT);
raw_notify(player, mudconf.motd_msg);
if (Wizard(player))
raw_notify(player, mudconf.wizmotd_msg);
buf=alloc_mbuf("announce_connect");
#ifdef RWHO_IN_USE
if (mudconf.rwho_transmit && (mudconf.control_flags & CF_RWHO_XMIT)) {
sprintf(buf, "%d@%s", player, mudconf.mud_name);
rwhocli_userlogin(buf, Name(player), time((time_t *) 0));
}
#endif
num = 0;
DESC_ITER_PLAYER(player, dtemp) num++;
if (num < 2)
sprintf(buf, "%s has connected.", Name(player));
else
sprintf(buf, "%s has reconnected.", Name(player));
notify_except(player, player, player, buf, 1); /* Thanks, Micro. */
if ((loc != NOTHING) && !(Dark(player) && Wizard(player)))
notify_except(loc, player, player, buf, 1);
free_mbuf(buf);
if (Suspect(player)) {
raw_broadcast(WIZARD, (char *)"[Suspect] %s has connected.",
(int)Name(player), 0, 0, 0, 0, 0);
}
if (d->host_info & H_SUSPECT) {
raw_broadcast(WIZARD,
(char *)"[Suspect site: %s] %s has connected.",
(int)(d->addr), (int)Name(player), 0, 0, 0, 0);
}
if (atr_temp = atr_pget(player, A_ACONNECT, &aowner, &aflags))
wait_que(player, player, RU_ARG1_COPY, 0, NOTHING,
atr_temp, (char **)NULL, 0);
free_lbuf(atr_temp);
time(&tt);
time_str = ctime(&tt);
time_str[strlen(time_str) - 1] = '\0';
record_login(player, 1, time_str, d->addr);
look_in (player, Location(player), 1);
}
void announce_disconnect(dbref player, DESC *d, const char *reason)
{
dbref loc, aowner;
int num, aflags;
char *buf, *atr_temp;
DESC *dtemp;
char *argv[1];
if (Suspect(player)) {
raw_broadcast(WIZARD,
(char *)"[Suspect] %s has disconnected.",
(int)Name(player), 0, 0, 0, 0, 0);
}
if (d->host_info & H_SUSPECT) {
raw_broadcast(WIZARD,
(char *)"[Suspect site: %s] %s has disconnected.",
(int)d->addr, (int)Name(d->player), 0, 0, 0, 0);
}
loc = Location(player);
num = 0;
DESC_ITER_PLAYER(player, dtemp) num++;
if (num < 2) {
buf = alloc_mbuf("announce_disconnect.only");
#ifdef RWHO_IN_USE
if (mudconf.rwho_transmit &&
(mudconf.control_flags & CF_RWHO_XMIT)) {
sprintf(buf, "%d@%s", player, mudconf.mud_name);
rwhocli_userlogout(buf);
}
#endif
sprintf(buf, "%s has disconnected.", Name(player));
if ((loc != NOTHING) && !(Dark(player) && Wizard(player)))
notify_except(loc, player, player, buf, 1);
notify_except(player, player, player, buf, 1);
free_mbuf(buf);
argv[0] = (char *)reason;
s_Flags(player, Flags(player) & ~PLAYER_CONNECT);
atr_temp = atr_pget(player, A_ADISCONNECT, &aowner, &aflags);
if (atr_temp && *atr_temp)
wait_que(player, player, RU_ARG1_COPY, 0, NOTHING,
atr_temp, argv, 1);
free_lbuf(atr_temp);
if (d->flags & DS_AUTODARK) {
s_Flags(d->player, Flags(d->player) & ~DARK);
d->flags &= ~DS_AUTODARK;
}
} else {
buf = alloc_mbuf("announce_disconnect.partial");
sprintf(buf, "%s has partially disconnected.", Name(player));
if ((loc != NOTHING) && !(Dark(player) && Wizard(player)))
notify_except(loc, player, player, buf, 1);
notify_except(player, player, player, buf, 1);
free_mbuf(buf);
}
desc_delhash(d);
}
int boot_off (dbref player, char *message)
{
DESC *d, *dnext;
int count;
count = 0;
DESC_SAFEITER_PLAYER(player, d, dnext) {
if (message && *message) {
queue_string(d, message);
queue_string(d, "\r\n");
}
shutdownsock(d, R_BOOT);
count++;
}
return count;
}
int boot_by_port (int port, int no_god, char *message)
{
DESC *d, *dnext;
int count;
count = 0;
DESC_SAFEITER_ALL(d, dnext) {
if ((d->descriptor == port) && (!no_god || !God(d->player))) {
if (message && *message) {
queue_string(d, message);
queue_string(d, "\r\n");
}
shutdownsock(d, R_BOOT);
count++;
}
}
return count;
}
void check_idle()
{
DESC *d, *dnext;
time_t now, idletime;
time(&now);
DESC_SAFEITER_ALL(d,dnext) {
if (d->flags & DS_CONNECTED) {
idletime = now - d->last_time;
if (idletime > d->timeout) {
queue_string(d,
"*** Inactivity Timeout ***\r\n");
shutdownsock(d, R_TIMEOUT);
} else if (mudconf.idle_wiz_dark &&
(idletime > mudconf.idle_timeout) &&
Wizard(d->player) && !Dark(d->player)) {
s_Flags(d->player, Flags(d->player) | DARK);
d->flags |= DS_AUTODARK;
}
} else {
idletime = now - d->connected_at;
if (idletime > mudconf.conn_timeout) {
queue_string(d,
"*** Login Timeout ***\r\n");
shutdownsock(d, R_TIMEOUT);
}
}
}
}
static void dump_users(DESC *e, char *match, int key)
{
DESC *d;
int count;
time_t now;
char *buf, *fp, *sp, flist[4], slist[4];
while (match && *match && isspace(*match))
match++;
if (!match || !*match)
match = NULL;
buf=alloc_mbuf("dump_users");
time(&now);
if (key == CMD_SESSION) {
queue_string(e, " ");
queue_string(e, " Characters Input---- Characters Output---\r\n");
}
queue_string(e, "Player Name On For Idle ");
if (key == CMD_SESSION) {
queue_string(e, "Port Pend Lost Total Pend Lost Total\r\n");
} else if ((e->flags & DS_CONNECTED) && Wizard(e->player) && (key == CMD_WHO)) {
queue_string(e, " Room Cmds Host\r\n");
} else {
if (Wizard(e->player))
queue_string(e, " ");
else
queue_string(e, " ");
queue_string(e, mudstate.doing_hdr);
queue_string(e, "\r\n");
}
count = 0;
DESC_ITER_CONN(d) {
if (!Dark(d->player) || Wizard(e->player)) {
count++;
if (match && !(string_prefix(Name(d->player), match)))
continue;
if ((key == CMD_SESSION) && !Wizard(e->player) &&
(d->player != e->player))
continue;
/* Get choice flags for wizards */
fp = flist;
sp = slist;
if ((e->flags & DS_CONNECTED) && Wizard(e->player)) {
if (Dark(d->player)) {
if (d->flags & DS_AUTODARK)
*fp++ = 'd';
else
*fp++ = 'D';
}
if (Unfindable(d->player)) *fp++ = 'U';
if (Suspect(d->player)) *fp++ = '+';
if (d->host_info & H_FORBIDDEN) *sp++ = 'F';
if (d->host_info & H_REGISTRATION) *sp++ = 'R';
if (d->host_info & H_SUSPECT) *sp++ = '+';
}
*fp = '\0';
*sp = '\0';
if ((e->flags & DS_CONNECTED) &&
Wizard(e->player) &&
(key == CMD_WHO)) {
sprintf(buf,
"%-16s%9s %4s%-3s#%-6d%5d%3s%s\r\n",
Name(d->player),
time_format_1(now - d->connected_at),
time_format_2(now - d->last_time),
flist,
Location(d->player), d->command_count,
slist,
d->addr);
} else if (key == CMD_SESSION) {
sprintf(buf,
"%-16s%9s %4s%5d%5d%6d%10d%6d%6d%10d\r\n",
Name(d->player),
time_format_1(now - d->connected_at),
time_format_2(now - d->last_time),
d->descriptor,
d->input_size, d->input_lost,
d->input_tot,
d->output_size, d->output_lost,
d->output_tot);
} else if (Wizard(e->player)) {
sprintf(buf, "%-16s%9s %4s%-3s%s\r\n",
Name(d->player),
time_format_1(now - d->connected_at),
time_format_2(now - d->last_time),
flist,
d->doing);
} else {
sprintf(buf, "%-16s%9s %4s %s\r\n",
Name(d->player),
time_format_1(now - d->connected_at),
time_format_2(now - d->last_time),
d->doing);
}
queue_string(e, buf);
}
}
/* sometimes I like the ternary operator.... */
sprintf(buf,"%d Player%slogged in.\r\n", count,
(count == 1) ? " " : "s ");
queue_string(e, buf);
free_mbuf(buf);
}
/* ---------------------------------------------------------------------------
* do_doing: Set the doing string that appears in the WHO report.
* Idea from R'nice@TinyTIM.
*/
void do_doing (dbref player, dbref cause, int key, char *arg)
{
DESC *d;
char *c;
int foundany, over;
if (key == DOING_MESSAGE) {
foundany = 0;
over = 0;
DESC_ITER_PLAYER(player, d) {
c = d->doing;
over = safe_copy_str(arg, d->doing, &c, 40);
*c = '\0';
foundany = 1;
}
if (foundany) {
if (over) {
notify(player,
tprintf("Warning: %d characters lost.",
over));
}
if (!Quiet(player)) notify(player, "Set.");
} else {
notify(player, "Not connected.");
}
} else if (key == DOING_HEADER) {
if (!arg || !*arg) {
strcpy(mudstate.doing_hdr, "Doing");
over = 0;
} else {
c = mudstate.doing_hdr;
over = safe_copy_str(arg, mudstate.doing_hdr, &c, 40);
*c = '\0';
}
if (over) {
notify(player,
tprintf("Warning: %d characters lost.", over));
}
if (!Quiet(player)) notify(player, "Set.");
} else {
notify(player, tprintf("Poll: %s", mudstate.doing_hdr));
}
}
NAMETAB logout_cmdtable[] = {
{(char *)"DOING", 5, CA_PUBLIC, CMD_DOING},
{(char *)"LOGOUT", 6, CA_PUBLIC, CMD_LOGOUT},
{(char *)"OUTPUTPREFIX",12, CA_PUBLIC, CMD_PREFIX|CMD_NOxFIX},
{(char *)"OUTPUTSUFFIX",12, CA_PUBLIC, CMD_SUFFIX|CMD_NOxFIX},
{(char *)"QUIT", 4, CA_PUBLIC, CMD_QUIT},
#ifdef LOCAL_RWHO_SERVER
{(char *)"RWHO", 4, CA_PUBLIC, CMD_RWHO},
#endif
{(char *)"SESSION", 7, CA_PUBLIC, CMD_SESSION},
{(char *)"WHO", 3, CA_PUBLIC, CMD_WHO},
{NULL, 0, 0, 0}};
void init_logout_cmdtab()
{
NAMETAB *cp;
/* Make the htab bigger than the number of entries so that we find
* things on the first check. Remember that the admin can add aliases.
*/
hashinit(&mudstate.logout_cmd_htab, 19);
for (cp=logout_cmdtable; cp->flag; cp++)
hashadd(cp->name, (int *)cp, &mudstate.logout_cmd_htab);
}
static void failconn (const char *logcode, const char *logtype,
const char *logreason, DESC *d, int disconnect_reason, dbref player,
FBLOCK *filecache, char *motd_msg,
char *command, char *user, char *password, char *cmdsave)
{
char *buff;
STARTLOG(LOG_LOGIN|LOG_SECURITY,logcode,"RJCT")
buff=alloc_mbuf("failconn.LOG");
sprintf(buff, "[%d/%s] %s rejected to ",
d->descriptor, d->addr, logtype);
log_text(buff);
free_mbuf(buff);
if (player != NOTHING)
log_name(player);
else
log_text(user);
log_text((char *)" (");
log_text((char *)logreason);
log_text((char *)")");
ENDLOG
fcache_dump(d, filecache);
if (*motd_msg) {
queue_string(d, motd_msg);
queue_write(d, "\r\n", 2);
}
free_mbuf(command);
free_mbuf(user);
free_mbuf(password);
shutdownsock(d, disconnect_reason);
mudstate.debug_cmd = cmdsave;
return;
}
static const char *connect_fail =
"Either that player does not exist, or has a different password.\r\n";
static const char *create_fail =
"Either there is already a player with that name, or that name is illegal.\r\n";
static int check_connect(DESC *d, const char *msg)
{
char *command, *user, *password, *buff, *cmdsave;
dbref player, aowner;
int aflags, nplayers;
DESC *d2;
cmdsave = mudstate.debug_cmd;
mudstate.debug_cmd = (char *)"< check_connect >";
/* Hide the password length from SESSION */
d->input_tot -= (strlen(msg) + 1);
/* Crack the command apart */
command = alloc_mbuf("check_conn.cmd");
user = alloc_mbuf("check_conn.user");
password = alloc_mbuf("check_conn.pass");
parse_connect(msg, command, user, password);
if (!strncmp(command, "co", 2)) {
/* See if this connection would exceed the max #players */
if (mudconf.max_players < 0) {
nplayers = mudconf.max_players - 1;
} else {
nplayers = 0;
DESC_ITER_CONN(d2)
nplayers++;
}
player = connect_player(user, password, d->addr);
if (player == NOTHING) {
/* Not a player, or wrong password */
queue_string(d, connect_fail);
STARTLOG(LOG_LOGIN|LOG_SECURITY,"CON","BAD")
buff=alloc_mbuf("check_conn.LOG.bad");
sprintf(buff, "[%d/%s] Failed connect to '%s'",
d->descriptor, d->addr, user);
log_text(buff);
free_mbuf(buff);
ENDLOG
if (--(d->retries_left) <= 0) {
free_mbuf(command);
free_mbuf(user);
free_mbuf(password);
shutdownsock(d, R_BADLOGIN);
mudstate.debug_cmd = cmdsave;
return 0;
}
} else if (((mudconf.control_flags & CF_LOGIN) &&
(nplayers < mudconf.max_players)) ||
Wizard(player) || God(player)) {
/* Logins are enabled, or wiz or god */
STARTLOG(LOG_LOGIN,"CON","LOGIN")
buff=alloc_mbuf("check_conn.LOG.login");
sprintf(buff, "[%d/%s] Connected to ",
d->descriptor, d->addr);
log_text(buff);
log_name_and_loc(player);
free_mbuf(buff);
ENDLOG
d->flags |= DS_CONNECTED;
d->connected_at = time(0);
d->player = player;
/* Give the player the MOTD file and the settable MOTD
* message(s). Use raw notifies so the player doesn't
* try to match on the text. */
if(Guest(player)) {
fcache_dump(d, mudstate.guest_fcache);
} else {
buff = atr_get(player, A_LAST, &aowner, &aflags);
if ((buff == NULL) || (*buff == '\0'))
fcache_dump(d, mudstate.crea_fcache);
else
fcache_dump(d, mudstate.motd_fcache);
if (Wizard(player))
fcache_dump(d, mudstate.wizmotd_fcache);
free_lbuf(buff);
}
announce_connect(player, d);
} else if (!(mudconf.control_flags & CF_LOGIN)) {
failconn("CON", "Connect", "Logins Disabled", d,
R_GAMEDOWN, player, mudstate.down_fcache,
mudconf.downmotd_msg, command, user, password,
cmdsave);
return 0;
} else {
failconn("CON", "Connect", "Game Full", d,
R_GAMEFULL, player, mudstate.full_fcache,
mudconf.fullmotd_msg, command, user, password,
cmdsave);
return 0;
}
} else if (!strncmp(command, "cr", 2)) {
/* Enforce game down */
if (!(mudconf.control_flags & CF_LOGIN)) {
failconn("CRE", "Create", "Logins Disabled", d,
R_GAMEDOWN, NOTHING, mudstate.down_fcache,
mudconf.downmotd_msg, command, user, password,
cmdsave);
return 0;
}
/* Enforce max #players */
if (mudconf.max_players < 0) {
nplayers = mudconf.max_players;
} else {
nplayers = 0;
DESC_ITER_CONN(d2)
nplayers++;
}
if (nplayers > mudconf.max_players) {
/* Too many players on, reject the attempt */
failconn("CRE", "Create", "Game Full", d,
R_GAMEFULL, NOTHING, mudstate.full_fcache,
mudconf.fullmotd_msg, command, user, password,
cmdsave);
return 0;
}
if (d->host_info & H_REGISTRATION) {
fcache_dump(d, mudstate.regf_fcache);
} else {
player = create_player(user, password, NOTHING, 0);
if (player == NOTHING) {
queue_string(d, create_fail);
STARTLOG(LOG_SECURITY|LOG_PCREATES,"CON","BAD")
buff=alloc_mbuf("check_conn.LOG.badcrea");
sprintf(buff,
"[%d/%s] Create of '%s' failed",
d->descriptor, d->addr, user);
log_text(buff);
free_mbuf(buff);
ENDLOG
} else {
STARTLOG(LOG_LOGIN|LOG_PCREATES,"CON","CREA")
buff=alloc_mbuf("check_conn.LOG.create");
sprintf(buff, "[%d/%s] Created ",
d->descriptor, d->addr);
log_text(buff);
log_name(player);
free_mbuf(buff);
ENDLOG
move_object(player, mudconf.start_room);
d->flags |= DS_CONNECTED;
d->connected_at = time(0);
d->player = player;
fcache_dump(d, mudstate.crea_fcache);
announce_connect(player, d);
}
}
} else {
welcome_user(d);
}
free_mbuf(command);
free_mbuf(user);
free_mbuf(password);
mudstate.debug_cmd = cmdsave;
return 1;
}
int do_command(DESC *d, char *command)
{
char *arg, *cmdsave;
NAMETAB *cp;
cmdsave = mudstate.debug_cmd;
mudstate.debug_cmd = (char *)"< do_command >";
/* Split off the command from the arguments */
arg = command;
while(*arg && !isspace(*arg)) arg++;
if (*arg) *arg++ = '\0';
/* Look up the command. If we don't find it, turn it over to the
* normal logged-in command processor or to create/connect
*/
cp = (NAMETAB *)hashfind(command, &mudstate.logout_cmd_htab);
if (cp == NULL) {
if (*arg) *--arg = ' '; /* restore nullified space */
if (d->flags & DS_CONNECTED) {
d->command_count++;
if (d->output_prefix) {
queue_string(d, d->output_prefix);
queue_write(d, "\r\n", 2);
}
mudstate.curr_player = d->player;
mudstate.curr_enactor = d->player;
process_command(d->player, d->player, 1,
command, (char **)NULL, 0);
if (d->output_suffix) {
queue_string(d, d->output_suffix);
queue_write(d, "\r\n", 2);
}
mudstate.debug_cmd = cmdsave;
return 1;
} else {
mudstate.debug_cmd = cmdsave;
return (check_connect(d, command));
}
}
/* The command was in the logged-out command table. Perform prefix
* and suffix processing, and invoke the command handler.
*/
d->command_count++;
if (!(cp->flag & CMD_NOxFIX)) {
if (d->output_prefix) {
queue_string(d, d->output_prefix);
queue_write(d, "\r\n", 2);
}
}
if ((!check_access(d->player, cp->perm)) ||
((cp->perm & CA_PLAYER) && !(d->flags & DS_CONNECTED))) {
queue_string(d, "Permission denied.\r\n");
} else {
mudstate.debug_cmd = cp->name;
switch (cp->flag & CMD_MASK) {
case CMD_QUIT:
shutdownsock(d, R_QUIT);
mudstate.debug_cmd = cmdsave;
return 0;
case CMD_LOGOUT:
shutdownsock(d, R_LOGOUT);
break;
case CMD_WHO:
dump_users(d, arg, CMD_WHO);
break;
case CMD_DOING:
dump_users(d, arg, CMD_DOING);
break;
case CMD_SESSION:
dump_users(d, arg, CMD_SESSION);
break;
#ifdef LOCAL_RWHO_SERVER
case CMD_RWHO:
dump_rusers(d);
break;
#endif
case CMD_PREFIX:
set_userstring(&d->output_prefix, arg);
break;
case CMD_SUFFIX:
set_userstring(&d->output_suffix, arg);
break;
default:
STARTLOG(LOG_BUGS,"BUG","PARSE")
arg = alloc_lbuf("do_command.LOG");
sprintf(arg,
"Prefix command with no handler: '%s'",
command);
log_text(arg);
free_lbuf(arg);
ENDLOG
}
}
if (!(cp->flag & CMD_NOxFIX)) {
if (d->output_prefix) {
queue_string(d, d->output_suffix);
queue_write(d, "\r\n", 2);
}
}
mudstate.debug_cmd = cmdsave;
return 1;
}
void process_commands()
{
int nprocessed;
DESC *d, *dnext;
CBLOCK *t;
char *cmdsave;
cmdsave = mudstate.debug_cmd;
mudstate.debug_cmd = (char *)"process_commands";
do {
nprocessed = 0;
DESC_SAFEITER_ALL(d,dnext) {
if (d->quota > 0 && (t = d->input_head)) {
d->quota--;
nprocessed++;
d->input_head = (CBLOCK *)t->hdr.nxt;
if (!d->input_head)
d->input_tail = NULL;
d->input_size -= (strlen(t->cmd) + 1);
do_command(d, t->cmd);
free_lbuf(t);
}
}
} while (nprocessed > 0);
mudstate.debug_cmd = cmdsave;
}
/* ---------------------------------------------------------------------------
* site_check: Check for site flags in a site list.
*/
int site_check (struct in_addr host, SITE *site_list)
{
SITE *this;
for (this=site_list; this; this=this->next) {
if ((host.s_addr & this->mask.s_addr) == this->address.s_addr)
return this->flag;
}
return 0;
}
/* --------------------------------------------------------------------------
* site_list: Display information in a site list
*/
const char *access_statstrings (int flag)
{
const char *str;
switch (flag) {
case H_FORBIDDEN:
str = "Forbidden";
break;
case H_REGISTRATION:
str = "Registration";
break;
case 0:
str = "Unrestricted";
break;
default:
str = "Strange";
}
return str;
}
const char *suspect_statstrings (int flag)
{
const char *str;
if (flag)
str = "Suspected";
else
str = "Trusted";
return str;
}
void site_list (dbref player, SITE *site_list, const char *header_txt,
const char *stat_str())
{
char *buff, *buff1, *str;
SITE *this;
buff = alloc_mbuf("site_list.buff");
buff1 = alloc_sbuf("site_list.addr");
sprintf(buff, "----- %s -----", header_txt);
notify(player, buff);
notify(player, "Address Mask Status");
for (this=site_list; this; this=this->next) {
str = (char *)stat_str(this->flag);
strcpy(buff1, inet_ntoa(this->mask));
sprintf(buff, "%-20s %-20s %s",
inet_ntoa(this->address), buff1, str);
notify(player, buff);
}
free_mbuf(buff);
free_sbuf(buff1);
}
/* ---------------------------------------------------------------------------
* list_siteinfo: List information about specially-marked sites.
*/
void list_siteinfo (dbref player)
{
site_list(player, mudstate.access_list, "Site Access",
access_statstrings);
site_list(player, mudstate.suspect_list, "Suspected Sites",
suspect_statstrings);
}
/* ---------------------------------------------------------------------------
* make_ulist: Make a list of connected user numbers for the LWHO function.
*/
void make_ulist (dbref player, char *buff)
{
char *cp;
DESC *d;
cp = buff;
DESC_ITER_CONN(d) {
if (!Wizard(player) && Dark(d->player))
continue;
if (cp != buff)
safe_chr(' ', buff, &cp);
safe_chr('#', buff, &cp);
safe_str(tprintf("%d", d->player), buff, &cp);
}
*cp = '\0';
}
/* ---------------------------------------------------------------------------
* find_connected_name: Resolve a playername from the list of connected
* players using prefix matching. We only return a match if the prefix
* was unique.
*/
dbref find_connected_name (dbref player, char *name)
{
DESC *d;
dbref found;
found = NOTHING;
DESC_ITER_CONN(d) {
if (Good_obj(player) && !Wizard(player) && Dark(d->player))
continue;
if (!string_prefix(Name(d->player), name))
continue;
if ((found != NOTHING) && (found != d->player))
return NOTHING;
found = d->player;
}
return found;
}
/* ---------------------------------------------------------------------------
* rwho_update: Send RWHO info to the remote RWHO server.
*/
#ifdef RWHO_IN_USE
void rwho_update(void)
{
DESC *d;
char *buf;
if (!(mudconf.rwho_transmit && (mudconf.control_flags & CF_RWHO_XMIT)))
return;
buf = alloc_mbuf("rwho_update");
rwhocli_pingalive();
DESC_ITER_ALL(d) {
if((d->flags & DS_CONNECTED) && !Dark(d->player)) {
sprintf(buf, "%d@%s", d->player, mudconf.mud_name);
rwhocli_userlogin(buf, Name(d->player), d->connected_at);
}
}
free_mbuf(buf);
}
#endif