/*
* file: comm.c , Communication module. Part of DIKUMUD
* Usage: Communication, central game loop.
* Copyright (C) 1990, 1991 - see 'license.doc' for complete information.
* All Rights Reserved
* Using *any* part of DikuMud without having read license.doc is
* violating our copyright.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/resource.h>
#include "global.h"
#ifdef I3
#include "i3.h"
#endif
#ifdef IMC
#include "imc.h"
#endif
#include "bug.h"
#include "utils.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "modify.h"
#include "whod.h"
#include "multiclass.h"
#include "weather.h"
#include "mudlimits.h"
#include "spells.h"
#include "spell_parser.h"
#include "sound.h"
#include "fight.h"
#include "mob_actions.h"
#include "act_other.h"
#include "signals.h"
#include "ban.h"
#include "board.h"
#define _COMM_C
#include "comm.h"
#ifdef RFC1413
#include "libident-0.19/ident.h"
#endif
struct descriptor_data *descriptor_list = NULL;
struct descriptor_data *next_to_process = NULL;
int mud_port = 0;
int slow_death = 0; /* Shut her down, Martha, she's sucking mud */
int diku_shutdown = 0; /* clean shutdown */
int diku_reboot = 0; /* reboot the game after a shutdown */
int DEBUG = FALSE;
int no_specials = 0; /* Suppress ass. of special routines */
long Uptime = 0L; /* time that the game has been up */
int maxdesc = 0;
int avail_descs = 0;
int tics = 0; /* for extern checkpointing */
int pulse = 0;
int pulse_update = PULSE_UPDATE + PULSE_VARIABLE;
int pulse_river = PULSE_RIVER;
int pulse_teleport = PULSE_TELEPORT;
int pulse_nature = PULSE_NATURE;
int pulse_sound = PULSE_SOUND;
int pulse_zone = PULSE_ZONE;
int pulse_mobile = PULSE_MOBILE;
int pulse_violence = PULSE_VIOLENCE;
int pulse_reboot = PULSE_REBOOT;
int pulse_dump = PULSE_DUMP;
int main(int argc, const char **argv)
{
int port = 0;
int pos = 1;
int exit_code = 0;
const char *dir = NULL;
const char *logfile = NULL;
const char *pidfile = NULL;
if (DEBUG > 1)
log_info("called %s with %d, %08zx", __PRETTY_FUNCTION__, argc, (size_t) argv);
port = DFLT_PORT;
dir = DFLT_DIR;
WizLock = FALSE;
while ((pos < argc) && (*(argv[pos]) == '-')) {
switch (*(argv[pos] + 1)) {
case 'w':
WizLock = TRUE;
log_info("WizLock is SET.");
break;
case 'D':
DEBUG = TRUE;
log_info("Debugging is on.");
break;
case 'l':
log_info("Lawful mode no longer available.");
break;
case 'L':
if (*(argv[pos] + 2))
logfile = argv[pos] + 2;
else if (++pos < argc)
logfile = argv[pos];
else {
log_fatal("LOG filename expected after option -L.");
proper_exit(MUD_HALT);
}
break;
case 'P':
if (*(argv[pos] + 2))
pidfile = argv[pos] + 2;
else if (++pos < argc)
pidfile = argv[pos];
else {
log_fatal("PID filename expected after option -P.");
proper_exit(MUD_HALT);
}
break;
case 'd':
if (*(argv[pos] + 2))
dir = argv[pos] + 2;
else if (++pos < argc)
dir = argv[pos];
else {
log_fatal("Directory arg expected after option -d.");
proper_exit(MUD_HALT);
}
break;
case 's':
no_specials = 1;
log_info("Suppressing assignment of special routines.");
break;
default:
log_info("Unknown option -% in argument string.", *(argv[pos] + 1));
break;
}
pos++;
}
if (pos < argc) {
if (!isdigit(*argv[pos])) {
log_fatal("Usage: %s [-l] [-s] [-d pathname] [ port # ]\n", argv[0]);
proper_exit(MUD_HALT);
} else if ((port = atoi(argv[pos])) <= 1024) {
log_fatal("Illegal port #\n");
proper_exit(MUD_HALT);
}
}
Uptime = time(0);
mud_port = port;
log_boot("Running game on port %d.", port);
if (chdir(dir) < 0) {
log_fatal("Cannot change directory to %s", dir);
proper_exit(MUD_HALT);
}
log_boot("Using %s as data directory.", dir);
if (pidfile) {
FILE *pidfp = NULL;
if (!(pidfp = fopen(pidfile, "w"))) {
log_fatal("Cannot open PID file %s", pidfile);
proper_exit(MUD_HALT);
}
fprintf(pidfp, "%d\n", getpid());
fclose(pidfp);
log_boot("PID written to %s", pidfile);
}
if (logfile) {
log_boot("Switching to %s as stderr.", logfile);
stderr = freopen(logfile, "a", stderr);
if (!stderr) {
log_fatal("Cannot reopen stderr!");
proper_exit(MUD_HALT);
}
close(fileno(stdout));
dup2(fileno(stderr), fileno(stdout));
log_boot("Switch to %s completed.", logfile);
}
srandom(time(0));
#if 0
if (init_sql()) {
log_boot("Connected to database!");
log_boot("%s\n", version_sql());
log_boot("Disconnecting from database!");
close_sql();
} else {
log_fatal("%s\n", "Couldn't open Database Connection! Aborting!");
return MUD_HALT;
}
#endif
exit_code = run_the_game(port);
return exit_code;
}
/* Init sockets, run game, and cleanup sockets */
int run_the_game(int port)
{
int s = 0;
if (DEBUG > 1)
log_info("called %s with %d", __PRETTY_FUNCTION__, port);
descriptor_list = NULL;
log_boot("Signal trapping.");
signal_setup();
log_boot("Opening mother connection.");
s = init_socket(port);
load_db();
log_boot("Opening WHO port.");
init_whod(port);
#ifdef IMC
log_boot("Opening I3 connection.");
i3_startup(FALSE, 3000, FALSE);
#endif
#ifdef IMC
log_boot("Opening IMC2 connection.");
imc_startup(FALSE, -1, FALSE);
#endif
log_boot("Entering game loop.");
game_loop(s);
#ifdef IMC
imc_shutdown(FALSE);
#endif
#ifdef I3
i3_shutdown(0);
#endif
close_sockets(s);
close_whod();
unload_db();
if (diku_reboot) {
log_boot("Rebooting.");
return MUD_REBOOT;
}
log_boot("Normal termination of game.");
return MUD_HALT; /* what's so great about HHGTTG, anyhow? */
}
/* Accept new connects, relay commands, and call 'heartbeat-functs' */
void game_loop(int s)
{
fd_set input_set;
fd_set output_set;
fd_set exc_set;
struct timeval last_time;
struct timeval now;
struct timeval timespent;
struct timeval timeout;
struct timeval null_time;
static struct timeval opt_time;
char comm[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
char promptbuf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
struct descriptor_data *point = NULL;
struct descriptor_data *next_point = NULL;
struct room_data *rm = NULL;
struct char_data *mount = NULL;
/*
* int mask = 0;
*/
sigset_t mask;
// int sql_reconnect;
if (DEBUG > 1)
log_info("called %s with %d", __PRETTY_FUNCTION__, s);
pulse = 0;
null_time.tv_sec = 0;
null_time.tv_usec = 0;
opt_time.tv_usec = OPT_USEC; /* Init time values */
opt_time.tv_sec = 0;
gettimeofday(&last_time, (struct timezone *)0);
maxdesc = s;
avail_descs = getdtablesize() - 2; /* !! Change if more needed !! */
/*
mask = sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |
sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) |
sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP);
*/
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGPIPE);
sigaddset(&mask, SIGALRM);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
sigaddset(&mask, SIGURG);
sigaddset(&mask, SIGXCPU);
/*
* Main loop
*/
while (!diku_shutdown) {
FD_ZERO(&input_set);
FD_ZERO(&output_set);
FD_ZERO(&exc_set);
FD_SET(s, &input_set);
for (point = descriptor_list; point; point = point->next) {
if (point->descriptor != 0) {
FD_SET(point->descriptor, &input_set);
FD_SET(point->descriptor, &exc_set);
FD_SET(point->descriptor, &output_set);
}
}
gettimeofday(&now, (struct timezone *)0);
timespent = timediff(&now, &last_time);
timeout = timediff(&opt_time, ×pent);
last_time.tv_sec = now.tv_sec + timeout.tv_sec;
last_time.tv_usec = now.tv_usec + timeout.tv_usec;
if (last_time.tv_usec >= 1000000) {
last_time.tv_usec -= 1000000;
last_time.tv_sec++;
}
/*
* sigsetmask(mask);
*/
sigprocmask(SIG_BLOCK, &mask, NULL);
whod_loop();
if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0) {
log_error("Select poll");
return;
}
if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
log_error("Select sleep");
/*
* proper_exit(MUD_HALT);
*/
}
/*
* sigsetmask(0);
*/
sigprocmask(SIG_UNBLOCK, &mask, NULL);
/*
* Respond to whatever might be happening
*/
/*
* New connection?
*/
if (FD_ISSET(s, &input_set))
if (new_descriptor(s) < 0)
log_info("New connection");
/*
* kick out the freaky folks
*/
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (FD_ISSET(point->descriptor, &exc_set)) {
FD_CLR(point->descriptor, &input_set);
FD_CLR(point->descriptor, &output_set);
close_socket(point);
}
}
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (FD_ISSET(point->descriptor, &input_set))
if (process_input(point) < 0)
close_socket(point);
}
/*
* process_commands;
*/
for (point = descriptor_list; point; point = next_to_process) {
next_to_process = point->next;
if ((--(point->wait) <= 0) && get_from_q(&point->input, comm)) {
if (point->character && point->connected == CON_PLAYING &&
point->character->specials.was_in_room != NOWHERE) {
if (point->character->in_room != NOWHERE)
char_from_room(point->character);
char_to_room(point->character, point->character->specials.was_in_room);
point->character->specials.was_in_room = NOWHERE;
act("$n has returned.", TRUE, point->character, 0, 0, TO_ROOM);
log_auth(point->character, "RECONNECTED %s (%s@%s/%s)!",
GET_NAME(point->character), point->username, point->host,
point->ip);
}
point->wait = 1;
if (point->character) /* This updates the idle ticker to say we're not idle */
point->character->specials.timer = 0;
point->prompt_mode = 1;
if (point->str)
string_add(point, comm);
else if (!point->connected)
if (point->page_first)
control_page(point, comm);
else
command_interpreter(point->character, comm);
else
nanny(point, comm);
}
#if 0
else {
if (point->character &&
(GET_POS(point->character) != POSITION_FIGHTING) &&
(GET_HIT(point->character) >= GET_MAX_HIT(point->character)) &&
(GET_MANA(point->character) >= GET_MAX_MANA(point->character)) &&
(GET_MOVE(point->character) >= GET_MAX_MOVE(point->character))
)
point->prompt_mode = 0; /* This might only show prompts if a command was
* processed */
}
#endif
}
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (point->page_first)
show_page(point);
}
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (FD_ISSET(point->descriptor, &output_set) && point->output.head) {
if (process_output(point) < 0)
close_socket(point);
else
point->prompt_mode = 1;
}
}
/*
* give the people some prompts
*/
for (point = descriptor_list; point; point = point->next) {
if (point->prompt_mode) {
/*
* Maybe we can somewhow not do prompts if nothing has happened (IE: no command entered
*/
if (point->str)
write_to_descriptor(point->descriptor, "] ");
else if (!point->connected) {
#if 0
if (point->showstr_point)
write_to_descriptor(point->descriptor, "*** Press return or q ***");
#endif
if (point->page_first)
write_to_descriptor(point->descriptor, "*** Press return or q ***");
else {
bzero(promptbuf, MAX_INPUT_LENGTH);
if (IS_IMMORTAL(point->character) && IS_PC(point->character)) {
if (MOUNTED(point->character)) {
mount = MOUNTED(point->character);
sprintf(promptbuf, "[%s has %d/%dh %d/%dv]\r\n",
GET_SDESC(mount),
GET_HIT(mount), GET_MAX_HIT(mount),
GET_MOVE(mount), GET_MAX_MOVE(mount));
}
if (IS_SET(point->character->specials.act, PLR_STEALTH))
sprintf(promptbuf + strlen(promptbuf), "S");
if (point->character->invis_level > 0)
sprintf(promptbuf + strlen(promptbuf), "I=%d: ",
point->character->invis_level);
rm = real_roomp(point->character->in_room);
sprintf(promptbuf + strlen(promptbuf),
"#%d - %s [#%d]> ", rm->zone, zone_table[rm->zone].name,
rm->number);
write_to_descriptor(point->descriptor, promptbuf);
/* OLD mobs didn't have classes.. this doesn't work anymore */
} else if (IS_NPC(point->character) &&
(IS_SET(point->character->specials.act, ACT_POLYSELF) ||
IS_SET(point->character->specials.act, ACT_POLYOTHER))) {
sprintf(promptbuf, "P %d/%dh %d/%dv > ",
GET_HIT(point->character),
GET_MAX_HIT(point->character),
GET_MOVE(point->character), GET_MAX_MOVE(point->character));
write_to_descriptor(point->descriptor, promptbuf);
} else if (IS_NPC(point->character) &&
IS_SET(point->character->specials.act, ACT_SWITCH)) {
sprintf(promptbuf, "*%s[#%d] in [#%d] %d/%dh %d/%dm %d/%dv > ",
NAME(point->character),
MobVnum(point->character),
point->character->in_room,
GET_HIT(point->character),
GET_MAX_HIT(point->character),
GET_MANA(point->character),
GET_MAX_MANA(point->character),
GET_MOVE(point->character), GET_MAX_MOVE(point->character));
write_to_descriptor(point->descriptor, promptbuf);
} else {
if (MOUNTED(point->character)) {
if (HasClass(point->character, CLASS_RANGER) ||
IS_AFFECTED(MOUNTED(point->character), AFF_CHARM)) {
mount = MOUNTED(point->character);
sprintf(promptbuf, "[%s has %d/%dh %d/%dv]\r\n",
GET_SDESC(mount),
GET_HIT(mount), GET_MAX_HIT(mount),
GET_MOVE(mount), GET_MAX_MOVE(mount));
}
}
sprintf(promptbuf + strlen(promptbuf), "%d/%dh %d/%dm %d/%dv > ",
GET_HIT(point->character),
GET_MAX_HIT(point->character),
GET_MANA(point->character),
GET_MAX_MANA(point->character),
GET_MOVE(point->character), GET_MAX_MOVE(point->character));
write_to_descriptor(point->descriptor, promptbuf);
}
}
}
point->prompt_mode = 0;
}
}
/*
* PULSE handling.... periodic events
*/
if ((++pulse) > PULSE_MAX)
pulse = 0;
if ((--pulse_zone) <= 0) {
pulse_zone = PULSE_ZONE;
zone_update();
}
if ((--pulse_teleport) <= 0) {
pulse_teleport = PULSE_TELEPORT;
Teleport(pulse);
}
if ((--pulse_nature) <= 0) {
pulse_nature = PULSE_NATURE;
check_all_nature(pulse);
}
if ((--pulse_violence) <= 0) {
pulse_violence = PULSE_VIOLENCE;
perform_violence(pulse);
}
if ((--pulse_mobile) <= 0) {
pulse_mobile = PULSE_MOBILE;
mobile_activity();
}
if ((--pulse_river) <= 0) {
pulse_river = PULSE_RIVER;
down_river(pulse);
}
if ((--pulse_sound) <= 0) {
pulse_sound = PULSE_SOUND;
MakeSound(pulse);
}
if ((--pulse_update) <= 0) {
pulse_update = PULSE_UPDATE + number(0, PULSE_VARIABLE);
weather_and_time(1);
affect_update();
point_update(pulse);
}
if ((--pulse_reboot) <= 0) {
pulse_reboot = PULSE_REBOOT;
check_reboot();
}
if ((--pulse_dump) <= 0) {
pulse_dump = PULSE_DUMP;
// dump_player_list();
}
#if 0
// This now verifies our connection is active, so we call it in
// the loop to ensure we can reconnect if the db bounces.
for (sql_reconnect = 0; 1; sql_reconnect++) {
if (verify_sql())
break;
if (sql_reconnect > 10) {
log_fatal("%s\n", "Database Connection LOST! Aborting!");
proper_exit(MUD_HALT);
}
sleep(1);
}
#endif
#if 1
#ifdef I3
i3_loop();
#endif
#ifdef IMC
imc_loop();
#endif
#endif
tics++; /* tics since last checkpoint signal */
}
}
/*
* general utility stuff (for local use)
*/
int get_from_q(struct txt_q *queue, char *dest)
{
struct txt_block *tmp = NULL;
if (DEBUG > 2)
log_info("called %s with %08zx, %s", __PRETTY_FUNCTION__, (size_t) queue, VNULL(dest));
if (!queue) {
log_info("Input from non-existant queue?");
return 0;
}
if (!queue->head)
return 0;
tmp = queue->head;
strcpy(dest, queue->head->text);
queue->head = queue->head->next;
DESTROY(tmp->text);
DESTROY(tmp);
return 1;
}
void write_to_q(const char *txt, struct txt_q *queue, int do_timestamp)
{
struct txt_block *new = NULL;
#ifdef TIME_DEBUG
struct timeval now;
char nowtime[26];
#endif
/*
* Cannot call things in bug.c from things bug.c calls!
*
* if (DEBUG > 2)
* log_info("called %s with %s, %08zx", __PRETTY_FUNCTION__, VNULL(txt), (size_t)queue);
*/
if (!queue) {
log_info("Output message to non-existant queue");
return;
}
#ifdef TIME_DEBUG
/* This is purely for debugging timing... don't leave this enabled! */
/* "Wed Jun 30 21:49:08 1993\n" */
/* ^ ^ */
/* 0 11 18 */
if (do_timestamp) {
gettimeofday(&now, (struct timezone *)0);
ctime_r((time_t *) & (now.tv_sec), nowtime);
}
#endif
CREATE(new, struct txt_block, 1);
#ifdef TIME_DEBUG
if (do_timestamp) {
CREATE(new->text, char, strlen (txt) + 1 + 14);
strncpy(new->text, nowtime + 11, 8);
sprintf(new->text + 8, ".%03ld: ", now.tv_usec / 1000);
strcat(new->text, txt);
} else {
CREATE(new->text, char, strlen (txt) + 1);
strcpy(new->text, txt);
}
#else
CREATE(new->text, char, strlen (txt) + 1);
strcpy(new->text, txt);
#endif
if (!queue->head) {
new->next = NULL;
queue->head = queue->tail = new;
} else {
queue->tail->next = new;
queue->tail = new;
new->next = NULL;
}
}
struct timeval timediff(struct timeval *a, struct timeval *b)
{
struct timeval result;
struct timeval tmp;
if (DEBUG > 3)
log_info("called %s with %08zx, %08zx", __PRETTY_FUNCTION__, (size_t) a, (size_t) b);
tmp = *a;
if ((result.tv_usec = tmp.tv_usec - b->tv_usec) < 0) {
result.tv_usec += 1000000;
--(tmp.tv_sec);
}
if ((result.tv_sec = tmp.tv_sec - b->tv_sec) < 0) {
result.tv_usec = 0;
result.tv_sec = 0;
}
return result;
}
/* Empty the queues before closing connection */
void flush_queues(struct descriptor_data *d)
{
char dummy[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
if (DEBUG > 2)
log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) d);
while (get_from_q(&d->output, dummy));
while (get_from_q(&d->input, dummy));
}
/*
* socket handling
*/
int init_socket(int port)
{
int opt = 1;
char hostname[MAX_HOSTNAME + 1] = "\0\0\0\0\0\0\0";
struct sockaddr_in sa;
struct hostent *hp = NULL;
int s = 0;
int i = 0;
int gotsocket = 0;
if (DEBUG > 2)
log_info("called %s with %d", __PRETTY_FUNCTION__, port);
bzero(&sa, sizeof(struct sockaddr_in));
gethostname(hostname, MAX_HOSTNAME);
hp = gethostbyname(hostname);
if (hp == NULL) {
log_fatal("gethostbyname");
proper_exit(MUD_HALT);
}
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons(port);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
log_fatal("Init-socket");
proper_exit(MUD_HALT);
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int *)&opt, sizeof(opt)) < 0) {
log_fatal("setsockopt REUSEADDR");
proper_exit(MUD_HALT);
}
for (i = 60; i > 0; i--) {
if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
gotsocket = 0;
log_info("Socket in use... retrying...\n");
sleep(2);
} else {
gotsocket = 1;
break;
}
}
if (!gotsocket) {
close(s);
log_fatal("bind");
proper_exit(MUD_HALT);
}
listen(s, 3);
return (s);
}
int new_connection(int s)
{
struct sockaddr_in isa;
socklen_t i = 0;
int t = 0;
if (DEBUG > 1)
log_info("called %s with %d", __PRETTY_FUNCTION__, s);
i = (socklen_t) sizeof(isa);
getsockname(s, (struct sockaddr *)&isa, &i);
if ((t = accept(s, (struct sockaddr *)&isa, &i)) < 0) {
log_error("Accept");
return (-1);
}
nonblock(t);
return t;
}
int new_descriptor(int s)
{
struct sockaddr_in isa;
struct hostent *host = NULL;
struct descriptor_data *newd = NULL;
time_t tc = (time_t) 0;
struct tm *t_info = NULL;
long remote_addr = 0L;
socklen_t i = 0;
/*
* int remote_port = 0;
*/
int desc = 0;
int old_maxdesc = 0;
int desc_index = 0;
#ifdef RFC1413
int badger = 0;
#endif
const char *timed_con[] = {
"\n"
};
/*
const char *bannished[] = {
"\n"
};
*/
if (DEBUG > 2)
log_info("called %s with %d", __PRETTY_FUNCTION__, s);
tc = time(0);
t_info = localtime(&tc);
old_maxdesc = maxdesc;
i = (socklen_t) sizeof(isa);
getsockname(s, (struct sockaddr *)&isa, &i);
if ((desc = accept(s, (struct sockaddr *)&isa, &i)) < 0) {
log_error("Accept");
return (-1);
}
nonblock(desc);
if ((maxdesc + 1) >= avail_descs) {
write_to_descriptor(desc, "Sorry.. full...\r\n");
close(desc);
return (0);
} else if (desc > maxdesc)
maxdesc = desc;
CREATE(newd, struct descriptor_data, 1);
/*
* remote_port = ntohs(isa.sin_port);
*/
remote_addr = htonl(isa.sin_addr.s_addr);
newd->username[0] = '\0';
#ifdef RFC1413
{
char *ack = NULL;
if ((ack = ident_id(desc, 10)))
strncpy(newd->username, ack, 16);
else
badger = 2;
}
#endif
if (!newd->username[0])
strcpy(newd->username, "adork");
sprintf(newd->ip, "%lu.%lu.%lu.%lu",
(remote_addr & 0xff000000) >> 24,
(remote_addr & 0x00ff0000) >> 16,
(remote_addr & 0x0000ff00) >> 8, (remote_addr & 0x000000ff) >> 0);
if ((host = gethostbyaddr((char *)&isa.sin_addr, sizeof(isa.sin_addr), AF_INET)))
strncpy(newd->host, host->h_name, 49);
/*
* init desc data
*/
newd->descriptor = desc;
newd->connected = 1;
newd->wait = 1;
newd->prompt_mode = 0;
*newd->buf = '\0';
newd->str = 0;
newd->showstr_head = 0;
newd->showstr_point = 0;
newd->page_first = NULL;
newd->page_last = NULL;
newd->page_control = ' '; /* show the first page! */
*newd->last_input = '\0';
newd->output.head = NULL;
newd->input.head = NULL;
newd->next = descriptor_list;
newd->character = 0;
newd->original = 0;
newd->snoop.snooping = 0;
newd->snoop.snoop_by = 0;
#ifdef RFC1413
switch (badger) {
case 1:
write_to_descriptor(desc,
"\r\n **** Tell your sys-admin to upgrade to the SHINY NEW telnet using RFC1413 ****\r\n\r\n");
break;
case 2:
write_to_descriptor(desc,
"\r\n **** Tell your sys-admin to practice safe telnet by using RFC1413 ****\r\n\r\n");
break;
default:
break;
}
#endif
/*
* prepend to list
*/
if (((t_info->tm_hour + 1) > 8) && ((t_info->tm_hour + 1) < 21))
for (desc_index = 0; strcmp(timed_con[desc_index], "\n"); desc_index++) {
if (!strncmp(timed_con[desc_index], newd->ip, 19)) {
log_info("TIMED site connecting:%s\n", newd->ip);
dcprintf(newd, "\r\nThis site is blocked from : 9 am - 9 pm\r\n");
dcprintf(newd, "You may connect after 9 pm from :[%s]\r\n", newd->ip);
maxdesc = old_maxdesc;
DESTROY(newd);
close(desc);
return (0);
}
}
/*
* for (desc_index = 0; bannished[desc_index] != "\n"; desc_index++) {
*/
/*
* if (!strncmp(bannished[desc_index], newd->ip, 19))
*/
if (banned_ip(newd->ip)) {
log_info("BAN site connecting:%s\n", newd->ip);
dcprintf(newd, "\r\nDue to your System Administrators request, or for some\r\n");
dcprintf(newd, "other reason, we are refusing all connections from:[%s]\r\n", newd->ip);
maxdesc = old_maxdesc;
DESTROY(newd);
close(desc);
return (0);
}
/*
* }
*/
descriptor_list = newd;
SEND_TO_Q(greetings, newd);
SEND_TO_Q("By what name do you wish to be known? ", newd);
return (0);
}
int process_output(struct descriptor_data *t)
{
char i[MAX_STRING_LENGTH + 1] = "\0\0\0\0\0\0\0";
if (DEBUG > 2)
log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) t);
if (!t->prompt_mode && !t->connected)
if (write_to_descriptor(t->descriptor, "\r\n") < 0)
return (-1);
/*
* Cycle thru output queue
*/
while (get_from_q(&t->output, i)) {
if ((t->snoop.snoop_by) && (t->snoop.snoop_by->desc)) {
write_to_q("S* ", &t->snoop.snoop_by->desc->output, 1);
write_to_q(i, &t->snoop.snoop_by->desc->output, 0);
}
if (write_to_descriptor(t->descriptor, i))
return (-1);
}
if (!t->connected && !(t->character && !IS_NPC(t->character) &&
IS_SET(t->character->specials.act, PLR_COMPACT)))
if (write_to_descriptor(t->descriptor, "\r\n") < 0)
return (-1);
return (1);
}
int write_to_descriptor(int desc, const char *txt)
{
int sofar = 0;
int thisround = 0;
int total = 0;
if (DEBUG > 2)
log_info("called %s with %d, %s", __PRETTY_FUNCTION__, desc, VNULL(txt));
total = strlen(txt);
do {
thisround = write(desc, txt + sofar, total - sofar);
if (thisround < 0) {
if (errno == EWOULDBLOCK)
break;
log_error("Write to socket");
return (-1);
}
sofar += thisround;
}
while (sofar < total);
return (0);
}
int process_input(struct descriptor_data *t)
{
int sofar = 0;
int thisround = 0;
int begin = 0;
int squelch = 0;
int i = 0;
int k = 0;
int flag = FALSE;
long now_time = 0L;
char tmp[MAX_INPUT_LENGTH + 2] = "\0\0\0\0\0\0\0";
char buffer[MAX_INPUT_LENGTH + 60] = "\0\0\0\0\0\0\0";
if (DEBUG > 2)
log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) t);
begin = strlen(t->buf);
/*
* Read in some stuff
*/
do {
if ((thisround = read(t->descriptor, t->buf + begin + sofar,
MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0) {
sofar += thisround;
} else {
if (thisround < 0) {
if (errno != EWOULDBLOCK) {
log_error("Read1 - ERROR");
return (-1);
} else {
break;
}
} else {
log_info("EOF encountered on socket read.");
return (-1);
}
}
} while (!ISNEWL(*(t->buf + begin + sofar - 1)));
*(t->buf + begin + sofar) = 0;
/*
* if no newline is contained in input, return without proc'ing
*/
for (i = begin; !ISNEWL(*(t->buf + i)); i++)
if (!*(t->buf + i))
return (0);
/*
* input contains 1 or more newlines; process the stuff
*/
for (i = 0, k = 0; *(t->buf + i);) {
if (!ISNEWL(*(t->buf + i)) && !(flag = (k >= (MAX_INPUT_LENGTH - 2))))
if (*(t->buf + i) == '\b') { /* backspace */
if (k) { /* more than one char ? */
if (*(tmp + --k) == '$')
k--;
i++;
} else {
i++; /* no or just one char.. Skip backsp */
}
} else {
if (isascii(*(t->buf + i)) && isprint(*(t->buf + i))) {
/*
* trans char, double for '$' (printf)
*/
if ((*(tmp + k) = *(t->buf + i)) == '$')
*(tmp + ++k) = '$';
k++;
i++;
} else {
i++;
}
} else {
*(tmp + k) = 0;
if (*tmp == '!')
strcpy(tmp, t->last_input);
else
strcpy(t->last_input, tmp);
write_to_q(tmp, &t->input, 0);
now_time = time(0);
t->idle_time = now_time;
if ((t->snoop.snoop_by) && (t->snoop.snoop_by->desc)) {
write_to_q("% ", &t->snoop.snoop_by->desc->output, 1);
write_to_q(tmp, &t->snoop.snoop_by->desc->output, 0);
write_to_q("\r\n", &t->snoop.snoop_by->desc->output, 0);
}
if (flag) {
sprintf(buffer, "Line too long. Truncated to:\r\n%s\r\n", tmp);
if (write_to_descriptor(t->descriptor, buffer) < 0)
return (-1);
/*
* skip the rest of the line
*/
for (; !ISNEWL(*(t->buf + i)); i++);
}
/*
* find end of entry
*/
for (; ISNEWL(*(t->buf + i)); i++);
/*
* squelch the entry from the buffer
*/
for (squelch = 0;; squelch++)
if ((*(t->buf + squelch) = *(t->buf + i + squelch)) == '\0')
break;
k = 0;
i = 0;
}
}
return (1);
}
void close_sockets(int s)
{
if (DEBUG > 2)
log_info("called %s with %d", __PRETTY_FUNCTION__, s);
log_info("Closing all sockets.");
while (descriptor_list)
close_socket(descriptor_list);
close(s);
}
void close_socket(struct descriptor_data *d)
{
struct descriptor_data *tmp = NULL;
if (DEBUG > 2)
log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) d);
if (!d)
return;
close(d->descriptor);
flush_queues(d);
if (d->descriptor == maxdesc)
--maxdesc;
/*
* Forget snooping
*/
if (d->snoop.snooping)
d->snoop.snooping->desc->snoop.snoop_by = 0;
if (d->snoop.snoop_by) {
cprintf(d->snoop.snoop_by, "Your victim is no longer among us.\r\n");
d->snoop.snoop_by->desc->snoop.snooping = 0;
}
if (d->character)
if (d->connected == CON_PLAYING) {
do_save(d->character, "", 0);
act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
/*
* log_info("Closing link to: %s.", GET_NAME(d->character));
*/
log_auth(d->character, "LINKDEAD %s (%s@%s/%s)!", GET_NAME(d->character),
d->username, d->host, d->ip);
if (IS_NPC(d->character)) {
if (d->character->desc)
d->character->orig = d->character->desc->original;
}
d->character->desc = NULL;
} else {
if (GET_NAME(d->character)) {
/*
* log_info("Losing player: %s.", GET_NAME(d->character));
*/
log_auth(d->character, "GOODBYE %s (%s@%s/%s)!", GET_NAME(d->character),
d->username, d->host, d->ip);
}
free_char(d->character);
d->character = NULL; /* need to wipe this out so we don't pick at it! */
} else
log_info("Losing descriptor without char.");
if (next_to_process == d) /* to avoid crashing the process loop */
next_to_process = next_to_process->next;
if (d == descriptor_list) /* this is the head of the list */
descriptor_list = descriptor_list->next;
else { /* This is somewhere inside the list */
/*
* Locate the previous element
*/
for (tmp = descriptor_list; (tmp->next != d) && tmp; tmp = tmp->next);
tmp->next = d->next;
}
/*
* if (d->showstr_head)
*/
DESTROY(d->showstr_head);
DESTROY(d);
}
void nonblock(int s)
{
if (DEBUG > 2)
log_info("called %s with %d", __PRETTY_FUNCTION__, s);
if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
log_fatal("Noblock");
proper_exit(MUD_HALT);
}
}
/*
* Public routines for system-to-player-communication
*/
/*
* This acts as an interface to write_to_q(), but it uses variable arguments
* to eliminate multiple calls to sprintf().
*/
void dcprintf(struct descriptor_data *d, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
if (Str && *Str && d) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
write_to_q(Result, &d->output, 1);
if (DEBUG > 2)
log_info("called %s with %08zx, %s, result of %s", __PRETTY_FUNCTION__, (size_t) d,
VNULL(Str), Result);
}
}
/*
* This works like send_to_char(), but it uses variable arguments to
* eliminate multiple calls to sprintf().
*/
void cprintf(struct char_data *ch, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
if (Str && *Str && ch && ch->desc) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
write_to_q(Result, &ch->desc->output, 1);
/*
* Cannot call things in bug.c from things bug.c calls!
*
* if (DEBUG > 2)
* log_info("called %s with %s, %s, result of %s", __PRETTY_FUNCTION__, SAFE_NAME(ch), VNULL(Str), Result);
*/
}
}
/*
* This one is an interface to replace send_to_room().
*/
void rprintf(int room, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct char_data *i = NULL;
struct room_data *rr = NULL;
if (Str && *Str && room >= 0 && (rr = real_roomp(room))) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = rr->people; i; i = i->next_in_room)
if (i->desc)
write_to_q(Result, &i->desc->output, 1);
if (DEBUG > 2)
log_info("called %s with %d, %s, result of %s", __PRETTY_FUNCTION__, room,
VNULL(Str), Result);
}
}
/*
* This one is everyone in the zone specified.
*/
void zprintf(int zone, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct descriptor_data *i = NULL;
struct room_data *rr = NULL;
if (Str && *Str && zone >= 0) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = descriptor_list; i; i = i->next)
if (!i->connected)
if (i->character)
if ((rr = real_roomp(i->character->in_room)))
if (rr->zone == zone)
write_to_q(Result, &i->output, 1);
if (DEBUG > 2)
log_info("called %s with %d, %s, result of %s", __PRETTY_FUNCTION__, zone,
VNULL(Str), Result);
}
}
/*
* And this one sends to EVERYBODY int the game!!!!!
*/
void allprintf(const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct descriptor_data *i = NULL;
if (Str && *Str) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = descriptor_list; i; i = i->next)
if (!i->connected)
write_to_q(Result, &i->output, 1);
if (DEBUG > 2)
log_info("called %s with %s, result of %s", __PRETTY_FUNCTION__, VNULL(Str),
Result);
}
}
/*
* Here is send_to_outdoor()
*/
void oprintf(const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct descriptor_data *i = NULL;
if (Str && *Str) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = descriptor_list; i; i = i->next)
if (!i->connected && i->character && OUTSIDE(i->character))
write_to_q(Result, &i->output, 1);
if (DEBUG > 2)
log_info("called %s with %s, result of %s", __PRETTY_FUNCTION__, VNULL(Str),
Result);
}
}
/*
* Send to everyone except the given character.
*/
void eprintf(struct char_data *ch, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct descriptor_data *i = NULL;
if (Str && *Str) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = descriptor_list; i; i = i->next)
if (ch && ch->desc != i && !i->connected)
write_to_q(Result, &i->output, 1);
if (DEBUG > 2)
log_info("called %s with %s, %s, result of %s", __PRETTY_FUNCTION__, SAFE_NAME(ch),
VNULL(Str), Result);
}
}
/*
* This one is for send_to_room_except()
*/
void reprintf(int room, struct char_data *ch, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct char_data *i = NULL;
struct room_data *rr = NULL;
if (Str && *Str && room >= 0 && (rr = real_roomp(room))) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = rr->people; i; i = i->next_in_room)
if (i != ch && i->desc)
write_to_q(Result, &i->desc->output, 1);
if (DEBUG > 2)
log_info("called %s with %d, %s, %s, result of %s", __PRETTY_FUNCTION__, room,
SAFE_NAME(ch), VNULL(Str), Result);
}
}
/*
* This one is for send_to_room_except()
*/
void re2printf(int room, struct char_data *ch1, struct char_data *ch2, const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct char_data *i = NULL;
struct room_data *rr = NULL;
if (Str && *Str && room >= 0 && (rr = real_roomp(room))) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = rr->people; i; i = i->next_in_room)
if (i != ch1 && i != ch2 && i->desc)
write_to_q(Result, &i->desc->output, 1);
if (DEBUG > 2)
log_info("called %s with %d, %s, %s, %s, result of %s", __PRETTY_FUNCTION__, room,
SAFE_NAME(ch1), SAFE_NAME(ch2), VNULL(Str), Result);
}
}
/*
* IMMORTAL printf.
*/
void iprintf(const char *Str, ...)
{
va_list arg;
char Result[MAX_STRING_LENGTH];
struct descriptor_data *i = NULL;
if (Str && *Str) {
bzero(Result, MAX_STRING_LENGTH);
va_start(arg, Str);
vsprintf(Result, Str, arg);
va_end(arg);
for (i = descriptor_list; i; i = i->next)
if (!i->connected && i->character && IS_IMMORTAL(i->character))
write_to_q(Result, &i->output, 1);
if (DEBUG > 2)
log_info("called %s with %s, result of %s", __PRETTY_FUNCTION__, VNULL(Str),
Result);
}
}
void save_all()
{
struct descriptor_data *i = NULL;
if (DEBUG > 2)
log_info("called %s with no arguments", __PRETTY_FUNCTION__);
for (i = descriptor_list; i; i = i->next)
if (i->character)
save_char(i->character, NOWHERE);
}
/* higher-level communication */
void act(const char *Str, int hide_invisible, struct char_data *ch,
struct obj_data *obj, void *vict_obj, int type, ...)
{
char *strp = NULL;
char *point = NULL;
const char *i = NULL;
struct char_data *to = NULL;
char buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
char str[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
va_list arg;
if (DEBUG > 2)
log_info("called %s with %s, %d, %s, %08zx, %08zx, %d", __PRETTY_FUNCTION__, VNULL(Str),
hide_invisible, SAFE_NAME(ch), (size_t) obj, (size_t) vict_obj, type);
if (!Str)
return;
if (!*Str)
return;
bzero(buf, MAX_STRING_LENGTH);
bzero(str, MAX_STRING_LENGTH);
va_start(arg, type);
vsprintf(str, Str, arg);
va_end(arg);
if (DEBUG > 1) {
log_info("act got: %s", Str);
log_info("act became: %s", str);
}
/*
* Added checks to ensure ch and to are NOT NULL
*/
if (type == TO_VICT) {
to = (struct char_data *)vict_obj;
if (!to || !ch)
return;
} else if (type == TO_CHAR) {
to = ch;
if (!to)
return;
} else {
if (!ch)
return;
if (!real_roomp(ch->in_room))
return;
if (!(to = real_roomp(ch->in_room)->people))
return;
}
for (; to; to = to->next_in_room) {
if (to->desc && ((to != ch) || (type == TO_CHAR)) &&
(CAN_SEE(to, ch) || !hide_invisible) && AWAKE(to) &&
!((type == TO_NOTVICT) && (to == (struct char_data *)vict_obj))) {
for (strp = str, point = buf;;)
if (*strp == '$') {
switch (*(++strp)) {
case 'n':
i = PERS(ch, to);
break;
case 'N':
i = PERS((struct char_data *)vict_obj, to);
break;
case 'm':
i = HMHR(ch);
break;
case 'M':
i = HMHR((struct char_data *)vict_obj);
break;
case 's':
i = HSHR(ch);
break;
case 'S':
i = HSHR((struct char_data *)vict_obj);
break;
case 'e':
i = HSSH(ch);
break;
case 'E':
i = HSSH((struct char_data *)vict_obj);
break;
case 'o':
i = OBJN(obj, to);
break;
case 'O':
i = OBJN((struct obj_data *)vict_obj, to);
break;
case 'p':
i = OBJS(obj, to);
break;
case 'P':
i = OBJS((struct obj_data *)vict_obj, to);
break;
case 'a':
i = SANA(obj);
break;
case 'A':
i = SANA((struct obj_data *)vict_obj);
break;
case 'T':
i = (char *)vict_obj;
break;
case 'F':
i = fname((char *)vict_obj);
break;
case '$':
i = "$";
break;
default:
log_info("Illegal $-code to act(): %s", str);
break;
}
while ((*point = *(i++)))
++point;
++strp;
} else if (!(*(point++) = *(strp++)))
break;
*(--point) = '\n';
*(++point) = '\r';
*(++point) = '\0';
write_to_q(CAP(buf), &to->desc->output, 1);
if (DEBUG > 1)
log_info("act sent: %s", buf);
}
if ((type == TO_VICT) || (type == TO_CHAR))
return;
}
}
void dump_player_list(void)
{
FILE *pfd = NULL;
int i = 0;
if (DEBUG > 2)
log_info("called %s with no arguments", __PRETTY_FUNCTION__);
log_info("Dumping player list");
if (!(pfd = fopen(PLAYER_FILE, "w"))) {
log_error("Cannot save player data for new user!");
} else {
fprintf(pfd, "%d\n", actual_players);
for (i = 0; i < number_of_players; i++)
if (list_of_players[i])
fprintf(pfd, "%s", list_of_players[i]);
FCLOSE(pfd);
}
}
void proper_exit(int exit_code)
{
#if 0
log_boot("Disconnecting from database!");
close_sql();
#endif
exit(exit_code);
}