Skip to content
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
18848 lines (17162 sloc) 647 KB
/****************************************************************************
* ^ +----- | / ^ ^ | | +-\ *
* / \ | | / |\ /| | | | \ *
* / \ +--- |< | \ / | | | | | *
* /-----\ | | \ | v | | | | / *
* / \ | | \ | | +-----+ +-/ *
****************************************************************************
* AFKMud Copyright 1997-2006 by Roger Libiez (Samson), *
* Levi Beckerson (Whir), Michael Ward (Tarl), Erik Wolfe (Dwip), *
* Cameron Carroll (Cam), Cyberfox, Karangi, Rathian, Raine, and Adjani. *
* All Rights Reserved. *
* Registered with the United States Copyright Office. TX 5-877-286 *
* *
* External contributions from Xorith, Quixadhal, Zarius, and many others. *
* *
* Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag, *
* Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard, *
* Grishnakh, Fireblade, and Nivek. *
* *
* Original MERC 2.1 code by Hatchet, Furey, and Kahn. *
* *
* Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen, *
* Michael Seifert, and Sebastian Hammer. *
****************************************************************************
* Intermud-3 Network Module *
****************************************************************************/
/*
* Copyright (c) 2000 Fatal Dimensions
*
* See the file "LICENSE" or information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
/* Ported to Smaug 1.4a by Samson of Alsherok.
* Consolidated for cross-codebase compatibility by Samson of Alsherok.
* Modifications and enhancements to the code
* Copyright (c)2001-2003 Roger Libiez ( Samson )
* Registered with the United States Copyright Office
* TX 5-562-404
*
* I condensed the 14 or so Fatal Dimensions source code files into this
* one file, because I for one find it far easier to maintain when all of
* the functions are right here in one file.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fnmatch.h>
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <signal.h>
#include "global.h"
#include "sql.h"
#include "bug.h"
#include "utils.h"
#include "db.h"
#include "multiclass.h"
#include "comm.h"
#include "modify.h"
#include "interpreter.h"
#include "version.h"
#include "stringmap.h"
#include "scheduler.h"
#define _I3_C
#include "i3.h"
/* Global variables for I3 */
#define I3_PACKET_STATE_NONE 0
#define I3_PACKET_STATE_GOT_SIZE 1
#define I3_PACKET_STATE_READING 2
#define I3_PACKET_STATE_GOT_PACKET 3
#define I3_PACKET_STATE_PROCESSING 4
#define I3_PACKET_STATE_LEFTOVERS 5
#define I3_PACKET_STATE_MISSING 6
char I3_incoming_packet_size[4];
uint32_t I3_incoming_packet_length = 0;
char *I3_incoming_packet = NULL;
long I3_incoming_packet_read = 0;
int I3_packet_processing_state = I3_PACKET_STATE_NONE;
char *I3_incoming_packet_leftovers = NULL;
long I3_incoming_packet_strlen = 0;
stringMap *speaker_db = NULL;
int speaker_count = 0;
stringMap *pinkfish_to_ansi_db = NULL;
int pinkfish_to_ansi_count = 0;
stringMap *pinkfish_to_i3_db = NULL;
int pinkfish_to_i3_count = 0;
stringMap *pinkfish_to_xterm256_db = NULL;
int pinkfish_to_xterm256_count = 0;
stringMap *pinkfish_to_greyscale_db = NULL;
int pinkfish_to_greyscale_count = 0;
char I3_input_buffer[IPS];
char I3_output_buffer[OPS];
char I3_currentpacket[IPS];
bool packetdebug = FALSE; /* Packet debugging toggle, can be turned on to check
* outgoing packets */
long I3_input_pointer = 0;
long I3_output_pointer = 4;
#define I3_THISMUD (this_i3mud->name)
char *I3_ROUTER_NAME;
char *I3_ROUTER_IP;
const char *manual_router;
int I3_socket;
int i3wait; /* Number of game loops to wait before attempting to
* reconnect when a socket dies */
int i3justconnected = 0; // So we can say something for the logs.
time_t ucache_clock; /* Timer for pruning the ucache */
long channel_m_received;
long channel_m_sent;
long bytes_received;
long bytes_sent;
time_t i3_time = 0; /* Current clock time for the client */
time_t connected_at = 0;
time_t connect_started_at = 0;
int connection_timeouts = 0;
time_t uptime = 0;
time_t record_uptime = 0;
time_t lag_spike = 0;
time_t record_lag_spike = 0;
I3_MUD *this_i3mud = NULL;
I3_MUD *first_mud;
I3_MUD *last_mud;
I3_CHANNEL *first_I3chan;
I3_CHANNEL *last_I3chan;
I3_BAN *first_i3ban;
I3_BAN *last_i3ban;
UCACHE_DATA *first_ucache;
UCACHE_DATA *last_ucache;
ROUTER_DATA *first_router;
ROUTER_DATA *last_router;
I3_COLOR *first_i3_color;
I3_COLOR *last_i3_color;
I3_CMD_DATA *first_i3_command;
I3_CMD_DATA *last_i3_command;
I3_HELP_DATA *first_i3_help;
I3_HELP_DATA *last_i3_help;
int router_reconnect_short_delay = 15 * PULSE_PER_SECOND;
int router_reconnect_medium_delay = 60 * PULSE_PER_SECOND;
int router_reconnect_long_delay = 300 * PULSE_PER_SECOND;
// These two are for the cases where we're going to be handling multiple requests that
// could end up with the same timestamp (even with milliseconds). In those cases, we need
// to add an incrementing value so we can still sort by time (timestamp + sub_second_counter)
time_t last_second = 0;
int sub_second_counter = 0;
// To move away from "ticks", we need to start using microsecond timing info.
int64_t time_to_taunt = (int64_t)1730793600 * (int64_t)1000000;
int64_t timeout_marker = (int64_t)1730793600 * (int64_t)1000000;
int expecting_timeout = 0;
void i3_printf(CHAR_DATA *ch, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
void i3wrap_printf(CHAR_DATA *ch, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
void i3page_printf(CHAR_DATA *ch, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
I3_HEADER *I3_get_header(char **pps);
void I3_send_channel_listen(I3_CHANNEL *channel,
bool lconnect);
void I3_write_channel_config(void);
const char *i3_funcname(I3_FUN *func);
I3_FUN *i3_function(const char *func);
void I3_saveconfig(void);
void to_channel(const char *argument, char *xchannel,
int level);
void I3_connection_close(bool reconnect);
char *i3rankbuffer(CHAR_DATA *ch);
char *I3_nameescape(const char *ps);
char *I3_nameremap(const char *ps);
void i3_npc_chat(const char *chan_name, const char *actor, const char *message);
void i3_npc_speak(const char *chan_name, const char *actor, const char *message);
void I3_packet_cleanup(void);
//void i3_nuke_url_file(void);
//void i3_check_urls(void);
const char *perm_names[] = {
"Notset", "None", "Mort", "Imm", "Admin", "Imp"
};
/*******************************************
* String buffering and logging functions. *
******************************************/
const char *i3one_argument(const char *argument, char *arg_first)
{
char cEnd;
int count;
count = 0;
if (arg_first)
arg_first[0] = '\0';
if (!argument || argument[0] == '\0')
return NULL;
while (isspace(*argument))
argument++;
cEnd = ' ';
if (*argument == '\'' || *argument == '"')
cEnd = *argument++;
while (*argument != '\0' && ++count <= MAX_INPUT_LENGTH - 1) {
if (*argument == cEnd) {
argument++;
break;
}
if (arg_first)
*arg_first++ = *argument++;
else
argument++;
}
if (arg_first)
*arg_first = '\0';
while (isspace(*argument))
argument++;
return argument;
}
char *I3_imctag_to_i3tag(const char *txt)
{
I3_COLOR *color;
static char tbuf[MAX_STRING_LENGTH];
*tbuf = '\0';
if (!txt || *txt == '\0')
return tbuf;
strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
strlcpy(tbuf, strrep(tbuf, color->imctag, color->i3tag), MAX_STRING_LENGTH);
return tbuf;
}
char *I3_imctag_to_mudtag(CHAR_DATA *ch, const char *txt)
{
I3_COLOR *color;
static char tbuf[MAX_STRING_LENGTH];
*tbuf = '\0';
if (!txt || *txt == '\0')
return tbuf;
if (I3IS_SET(I3FLAG(ch), I3_COLORFLAG)) {
strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
strlcpy(tbuf, strrep(tbuf, color->imctag, color->mudtag), MAX_STRING_LENGTH);
} else
strlcpy(tbuf, i3_strip_colors(txt), MAX_STRING_LENGTH);
return tbuf;
}
#if 0
char *i3_strip_colors(const char *txt)
{
I3_COLOR *color;
static char tbuf[MAX_STRING_LENGTH];
strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
strlcpy(tbuf, strrep(tbuf, color->i3tag, ""), MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
strlcpy(tbuf, strrep(tbuf, color->mudtag, ""), MAX_STRING_LENGTH);
return tbuf;
}
char *I3_mudtag_to_i3tag(const char *txt)
{
I3_COLOR *color;
static char tbuf[MAX_STRING_LENGTH];
*tbuf = '\0';
if (!txt || *txt == '\0')
return tbuf;
strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
strlcpy(tbuf, strrep(tbuf, color->mudtag, color->i3tag), MAX_STRING_LENGTH);
return tbuf;
}
char *I3_i3tag_to_mudtag(CHAR_DATA *ch, const char *txt)
{
I3_COLOR *color;
static char tbuf[MAX_STRING_LENGTH];
*tbuf = '\0';
if (!txt || *txt == '\0')
return tbuf;
if (I3IS_SET(I3FLAG(ch), I3_COLORFLAG)) {
strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
strlcpy(tbuf, strrep(tbuf, color->i3tag, color->mudtag), MAX_STRING_LENGTH);
} else
strlcpy(tbuf, i3_strip_colors(txt), MAX_STRING_LENGTH);
return tbuf;
}
#else
char *i3_strip_colors(const char *txt)
{
return pinkfish_to_null(txt);
}
char *I3_mudtag_to_i3tag(const char *txt)
{
return pinkfish_to_i3(txt);
}
char *I3_i3tag_to_mudtag(CHAR_DATA *ch, const char *txt)
{
return (I3IS_SET(I3FLAG(ch), I3_COLORFLAG))
? pinkfish_to_xterm(txt)
: pinkfish_to_null(txt);
}
#endif
/********************************
* User level output functions. *
*******************************/
/* Generic substitute for cprintf that has color support */
void i3_printf(CHAR_DATA *ch, const char *fmt, ...)
{
char buf[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
va_list args;
va_start(args, fmt);
vsnprintf(buf, MAX_STRING_LENGTH, fmt, args);
va_end(args);
strlcpy(buf2, I3_i3tag_to_mudtag(ch, buf), MAX_STRING_LENGTH);
cprintf(ch, "%s", buf2);
return;
}
void i3wrap_printf(CHAR_DATA *ch, const char *fmt, ...)
{
char buf[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
va_list args;
va_start(args, fmt);
vsnprintf(buf, MAX_STRING_LENGTH, fmt, args);
va_end(args);
strlcpy(buf2, I3_i3tag_to_mudtag(ch, buf), MAX_STRING_LENGTH);
//cprintf(ch, "%s", buf2);
//cprintf(ch, "%s", color_wrap(78, 96, " ", buf2));
cprintf(ch, "%s", color_wrap(MAX(18, ch->desc->telnet.cols - 12), MAX(18, ch->desc->telnet.cols - 2), " ", buf2));
return;
}
/* Generic page_printf type function */
void i3page_printf(CHAR_DATA *ch, const char *fmt, ...)
{
char buf[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
va_list args;
va_start(args, fmt);
//log_info("NL-fmt %s for %s", strpbrk(fmt, "\n") ? "FOUND" : "NOT FOUND", fmt);
vsnprintf(buf, MAX_STRING_LENGTH, fmt, args);
va_end(args);
//log_info("NL-pre %s for %s", strpbrk(buf, "\n") ? "FOUND" : "NOT FOUND", buf);
strlcpy(buf2, I3_i3tag_to_mudtag(ch, buf), MAX_STRING_LENGTH);
//log_info("NL-post %s for %s", strpbrk(buf2, "\n") ? "FOUND" : "NOT FOUND", buf2);
page_string(ch->desc, buf2, 1);
return;
}
/********************************
* Low level utility functions. *
********************************/
int i3todikugender(int gender)
{
int sex = 0;
if (gender == 0)
sex = SEX_MALE;
if (gender == 1)
sex = SEX_FEMALE;
if (gender > 1)
sex = SEX_NEUTRAL;
return sex;
}
int dikutoi3gender(int gender)
{
int sex = 0;
if (gender > 2 || gender < 0)
sex = 2; /* I3 neuter */
if (gender == SEX_MALE)
sex = 0; /* I3 Male */
if (gender == SEX_FEMALE)
sex = 1; /* I3 Female */
return sex;
}
int get_permvalue(const char *flag)
{
unsigned int x;
for (x = 0; x < (sizeof(perm_names) / sizeof(perm_names[0])); x++)
if (!strcasecmp(flag, perm_names[x]))
return x;
return -1;
}
/* I3_getarg: extract a single argument (with given max length) from
* argument to arg; if arg==NULL, just skip an arg, don't copy it out
*/
char *I3_getarg(char *argument, char *arg, int maxlen)
{
int len = 0;
if (!argument || argument[0] == '\0') {
if (arg)
arg[0] = '\0';
return argument;
}
while (*argument && isspace(*argument))
argument++;
if (arg)
while (*argument && !isspace(*argument) && len < maxlen - 1)
*arg++ = *argument++, len++;
else
while (*argument && !isspace(*argument))
argument++;
while (*argument && !isspace(*argument))
argument++;
while (*argument && isspace(*argument))
argument++;
if (arg)
*arg = '\0';
return argument;
}
/* Check for a name in a list */
bool I3_hasname(char *list, const char *name)
{
char *p;
char arg[MAX_INPUT_LENGTH];
if (!list)
return FALSE;
p = I3_getarg(list, arg, MAX_INPUT_LENGTH);
while (arg[0]) {
if (!strcasecmp(name, arg))
return TRUE;
p = I3_getarg(p, arg, MAX_INPUT_LENGTH);
}
return FALSE;
}
/* Add a name to a list */
void I3_flagchan(char **list, const char *name)
{
char buf[MAX_STRING_LENGTH];
if (I3_hasname(*list, name))
return;
if (*list && *list[0] != '\0')
snprintf(buf, MAX_STRING_LENGTH, "%s %s", *list, name);
else
strlcpy(buf, name, MAX_STRING_LENGTH);
I3STRFREE(*list);
*list = I3STRALLOC(buf);
}
/* Remove a name from a list */
void I3_unflagchan(char **list, const char *name)
{
char buf[MAX_STRING_LENGTH],
arg[MAX_INPUT_LENGTH];
char *p;
buf[0] = '\0';
p = I3_getarg(*list, arg, MAX_INPUT_LENGTH);
while (arg[0]) {
if (strcasecmp(arg, name)) {
if (buf[0])
strlcat(buf, " ", MAX_STRING_LENGTH);
strlcat(buf, arg, MAX_STRING_LENGTH);
}
p = I3_getarg(p, arg, MAX_INPUT_LENGTH);
}
I3STRFREE(*list);
*list = I3STRALLOC(buf);
}
bool i3_str_prefix(const char *astr, const char *bstr)
{
if (!astr) {
log_error("Strn_cmp: null astr.");
return TRUE;
}
if (!bstr) {
log_error("Strn_cmp: null bstr.");
return TRUE;
}
for (; *astr; astr++, bstr++) {
if (LOWER(*astr) != LOWER(*bstr))
return TRUE;
}
return FALSE;
}
/*
* Returns an initial-capped string.
*/
char *i3capitalize(const char *str)
{
static char strcap[MAX_STRING_LENGTH];
int i;
for (i = 0; str[i] != '\0'; i++)
strcap[i] = tolower(str[i]);
strcap[i] = '\0';
strcap[0] = toupper(strcap[0]);
return strcap;
}
/* Borrowed from Samson's new_auth snippet - checks to see if a particular player exists in the mud.
* This is called from i3locate and i3finger to report on offline characters.
*/
bool i3exists_player(char *name)
{
struct stat fst;
char buf[MAX_INPUT_LENGTH];
/*
* Stands to reason that if there ain't a name to look at, they damn well don't exist!
*/
if (!name || !strcasecmp(name, ""))
return FALSE;
snprintf(buf, MAX_INPUT_LENGTH, "%s%c/%s", PLAYER_DIR, tolower(name[0]),
i3capitalize(name));
if (stat(buf, &fst) != -1)
return TRUE;
else
return FALSE;
}
bool verify_i3layout(const char *fmt, int num)
{
const char *c;
int i = 0;
c = fmt;
while ((c = strchr(c, '%')) != NULL) {
if (*(c + 1) == '%') { /* %% */
c += 2;
continue;
}
if (*(c + 1) != 's') /* not %s */
return FALSE;
c++;
i++;
}
if (i != num)
return FALSE;
return TRUE;
}
/*
* This means we're in the process of connecting to the I3 network, but may not have
* fully finished yet. This will be false only if there is no I3 socket, which happens
* before any connection attempts, or after a disconnect without a reconnect.
*/
bool is_connecting(void)
{
if (I3_socket < 1)
return FALSE;
return TRUE;
}
/*
* This says the I3 connection is actually up and functional, or was... it returns true
* only after we have received the startup_reply packet.
*/
bool is_connected(void)
{
if( connected_at )
return TRUE;
return FALSE;
}
/*
* Add backslashes in front of the " and \'s
*/
char *I3_escape(const char *ps)
{
static char xnew[MAX_STRING_LENGTH];
char *pnew = xnew;
while (ps[0]) {
if (ps[0] == '"') {
pnew[0] = '\\';
pnew++;
}
if (ps[0] == '\\') {
pnew[0] = '\\';
pnew++;
}
if (ps[0] == '\r' || ps[0] == '\n') {
ps++;
continue;
}
pnew[0] = ps[0];
pnew++;
ps++;
}
pnew[0] = '\0';
return xnew;
}
/* Searches through the channel list to see if one exists with the localname supplied to it. */
I3_CHANNEL *find_I3_channel_by_localname(const char *name)
{
I3_CHANNEL *channel = NULL;
for (channel = first_I3chan; channel; channel = channel->next) {
if (!channel->local_name)
continue;
if (!strcasecmp(channel->local_name, name))
return channel;
}
return NULL;
}
/* Searches through the channel list to see if one exists with the I3 channel name supplied to it.*/
I3_CHANNEL *find_I3_channel_by_name(const char *name)
{
I3_CHANNEL *channel = NULL;
if (!name || !*name)
return NULL;
for (channel = first_I3chan; channel; channel = channel->next) {
if (!strcasecmp(channel->I3_name, name))
return channel;
}
return NULL;
}
/* Sets up a channel on the mud for the first time, configuring its default layout.
* If you don't like the default layout of channels, this is where you should edit it to your liking.
*/
I3_CHANNEL *new_I3_channel(void)
{
I3_CHANNEL *cnew;
I3CREATE(cnew, I3_CHANNEL, 1);
I3LINK(cnew, first_I3chan, last_I3chan, next, prev);
return cnew;
}
/* Deletes a channel's information from the mud. */
void destroy_I3_channel(I3_CHANNEL *channel)
{
int x;
if (channel == NULL) {
log_error("%s", "destroy_I3_channel: Null parameter");
return;
}
I3STRFREE(channel->local_name);
I3STRFREE(channel->host_mud);
I3STRFREE(channel->I3_name);
I3STRFREE(channel->layout_m);
I3STRFREE(channel->layout_e);
for (x = 0; x < MAX_I3HISTORY; x++) {
if (channel->history[x] && channel->history[x] != '\0')
I3STRFREE(channel->history[x]);
}
I3UNLINK(channel, first_I3chan, last_I3chan, next, prev);
I3DISPOSE(channel);
}
/* Finds a mud with the name supplied on the mudlist */
I3_MUD *find_I3_mud_by_name(const char *name)
{
I3_MUD *mud;
for (mud = first_mud; mud; mud = mud->next) {
if (!strcasecmp(mud->name, name))
return mud;
}
return NULL;
}
I3_MUD *new_I3_mud(char *name)
{
I3_MUD *cnew,
*mud_prev;
I3CREATE(cnew, I3_MUD, 1);
cnew->name = I3STRALLOC(name);
for (mud_prev = first_mud; mud_prev; mud_prev = mud_prev->next)
if (strcasecmp(mud_prev->name, name) >= 0)
break;
if (!mud_prev)
I3LINK(cnew, first_mud, last_mud, next, prev);
else
I3INSERT(cnew, mud_prev, first_mud, next, prev);
return cnew;
}
I3_MUD *create_I3_mud()
{
I3_MUD *mud = NULL;
I3CREATE(mud, I3_MUD, 1);
/* make sure string pointers are NULL */
mud->name = NULL;
mud->ipaddress = NULL;
mud->mudlib = NULL;
mud->base_mudlib = NULL;
mud->driver = NULL;
mud->mud_type = NULL;
mud->open_status = NULL;
mud->admin_email = NULL;
mud->telnet = NULL;
mud->web_wrong = NULL;
mud->banner = NULL;
mud->web = NULL;
mud->time = NULL;
mud->daemon = NULL;
mud->routerName = NULL;
/* default values */
mud->status = -1;
mud->player_port = 0;
mud->imud_tcp_port = 0;
mud->imud_udp_port = 0;
mud->tell = FALSE;
mud->beep = FALSE;
mud->emoteto = FALSE;
mud->who = FALSE;
mud->finger = FALSE;
mud->locate = FALSE;
mud->channel = FALSE;
mud->news = FALSE;
mud->mail = FALSE;
mud->file = FALSE;
mud->auth = FALSE;
mud->ucache = FALSE;
mud->smtp = 0;
mud->ftp = 0;
mud->nntp = 0;
mud->http = 0;
mud->pop3 = 0;
mud->rcp = 0;
mud->amrcp = 0;
mud->jeamland = 0;
mud->autoconnect = FALSE;
mud->password = 0;
mud->mudlist_id = 0;
mud->chanlist_id = 0;
mud->minlevel = 1; /* Minimum default level before I3 will acknowledge you exist */
mud->immlevel = 2; /* Default immortal level */
mud->adminlevel = 51; /* Default administration level */
mud->implevel = 60; /* Default implementor level */
return mud;
}
void destroy_I3_mud(I3_MUD *mud)
{
if (mud == NULL) {
log_error("%s", "destroy_I3_mud: Null parameter");
return;
}
I3STRFREE(mud->name);
I3STRFREE(mud->ipaddress);
I3STRFREE(mud->mudlib);
I3STRFREE(mud->base_mudlib);
I3STRFREE(mud->driver);
I3STRFREE(mud->mud_type);
I3STRFREE(mud->open_status);
I3STRFREE(mud->admin_email);
I3STRFREE(mud->telnet);
I3STRFREE(mud->web_wrong);
I3STRFREE(mud->banner);
I3STRFREE(mud->web);
I3STRFREE(mud->time);
I3STRFREE(mud->daemon);
I3STRFREE(mud->routerName);
if (mud != this_i3mud)
I3UNLINK(mud, first_mud, last_mud, next, prev);
I3DISPOSE(mud);
}
/*
* Returns a CHAR_DATA class which matches the string
*
*/
CHAR_DATA *I3_find_user(const char *name)
{
DESCRIPTOR_DATA *d;
CHAR_DATA *vch = NULL;
for (d = first_descriptor; d; d = d->next) {
if ((vch = d->character ? d->character : d->original) != NULL
&& !strcasecmp(CH_I3NAME(vch), name)
&& d->connected == CON_PLAYING)
return vch;
}
return NULL;
}
void i3_message_to_players( char *str ) {
DESCRIPTOR_DATA *d;
CHAR_DATA *vch = NULL;
for (d = first_descriptor; d; d = d->next) {
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (!I3_hasname(I3LISTEN(vch), "wiley")
|| I3_hasname(I3DENY(vch), "wiley"))
continue;
if (d->connected == CON_PLAYING) {
i3wrap_printf(vch, "%s%%^RESET%%^\r\n", str);
}
}
}
/* Beefed up to include wildcard ignores and user-level IP ignores.
* Be careful when setting IP based ignores - Last resort measure, etc.
*/
bool i3ignoring(CHAR_DATA *ch, const char *ignore)
{
I3_IGNORE *temp;
I3_MUD *mud;
char *ps;
char ipbuf[MAX_INPUT_LENGTH],
mudname[MAX_INPUT_LENGTH];
/*
* Wildcard support thanks to Xorith
*/
for (temp = FIRST_I3IGNORE(ch); temp; temp = temp->next) {
if (!fnmatch(temp->name, ignore, 0))
return TRUE;
}
/*
* In theory, getting this far should be the result of an IP:Port ban
*/
ps = strchr(ignore, '@');
if (ignore[0] == '\0' || ps == NULL)
return FALSE;
ps[0] = '\0';
strlcpy(mudname, ps + 1, MAX_INPUT_LENGTH);
for (mud = first_mud; mud; mud = mud->next) {
if (!strcasecmp(mud->name, mudname)) {
snprintf(ipbuf, MAX_INPUT_LENGTH, "%s:%d", mud->ipaddress, mud->player_port);
for (temp = FIRST_I3IGNORE(ch); temp; temp = temp->next) {
if (!strcasecmp(temp->name, ipbuf))
return TRUE;
}
}
}
return FALSE;
}
/* Be careful with an IP ban - Last resort measure, etc. */
bool i3banned(const char *ignore)
{
I3_BAN *temp;
I3_MUD *mud;
char *ps;
char mudname[MAX_INPUT_LENGTH],
ipbuf[MAX_INPUT_LENGTH];
/*
* Wildcard support thanks to Xorith
*/
for (temp = first_i3ban; temp; temp = temp->next) {
if (!fnmatch(temp->name, ignore, 0))
return TRUE;
}
/*
* In theory, getting this far should be the result of an IP:Port ban
*/
ps = strchr(ignore, '@');
if (!ignore || ignore[0] == '\0' || ps == NULL)
return FALSE;
ps[0] = '\0';
strlcpy(mudname, ps + 1, MAX_INPUT_LENGTH);
for (mud = first_mud; mud; mud = mud->next) {
if (!strcasecmp(mud->name, mudname)) {
snprintf(ipbuf, MAX_INPUT_LENGTH, "%s:%d", mud->ipaddress, mud->player_port);
for (temp = first_i3ban; temp; temp = temp->next) {
if (!strcasecmp(temp->name, ipbuf))
return TRUE;
}
}
}
return FALSE;
}
bool i3check_permissions(CHAR_DATA *ch, int checkvalue, int targetvalue, bool enforceequal)
{
if (checkvalue < 0 || checkvalue > I3PERM_IMP) {
i3_printf(ch, "Invalid permission setting.\r\n");
return FALSE;
}
if (checkvalue > I3PERM(ch)) {
i3_printf(ch, "You cannot set permissions higher than your own.\r\n");
return FALSE;
}
if (checkvalue == I3PERM(ch) && I3PERM(ch) != I3PERM_IMP && enforceequal) {
i3_printf(ch, "You cannot set permissions equal to your own. Someone higher up must do this.\r\n");
return FALSE;
}
if (I3PERM(ch) < targetvalue) {
i3_printf(ch, "You cannot alter the permissions of someone or something above your own.\r\n");
return FALSE;
}
return TRUE;
}
/*
* Read a number from a file. [Taken from Smaug's fread_number]
*/
int i3fread_number(FILE * fp)
{
int num;
bool sign;
char c;
do {
if (feof(fp)) {
log_info("%s", "i3fread_number: EOF encountered on read.");
return 0;
}
c = getc(fp);
}
while (isspace(c));
num = 0;
sign = FALSE;
if (c == '+') {
c = getc(fp);
} else if (c == '-') {
sign = TRUE;
c = getc(fp);
}
if (!isdigit(c)) {
log_info("i3fread_number: bad format. (%c)", c);
return 0;
}
while (isdigit(c)) {
if (feof(fp)) {
log_info("%s", "i3fread_number: EOF encountered on read.");
return num;
}
num = num * 10 + c - '0';
c = getc(fp);
}
if (sign)
num = 0 - num;
if (c == '|')
num += i3fread_number(fp);
else if (c != ' ')
ungetc(c, fp);
return num;
}
/*
* Read to end of line into static buffer [Taken from Smaug's fread_line]
*/
char *i3fread_line(FILE * fp)
{
char line[MAX_STRING_LENGTH];
char *pline;
char c;
int ln;
pline = line;
line[0] = '\0';
ln = 0;
/*
* Skip blanks.
* Read first char.
*/
do {
if (feof(fp)) {
log_error("%s", "i3fread_line: EOF encountered on read.");
strlcpy(line, "", MAX_STRING_LENGTH);
return I3STRALLOC(line);
}
c = getc(fp);
}
while (isspace(c));
ungetc(c, fp);
do {
if (feof(fp)) {
log_error("%s", "i3fread_line: EOF encountered on read.");
*pline = '\0';
return I3STRALLOC(line);
}
c = getc(fp);
*pline++ = c;
ln++;
if (ln >= (MAX_STRING_LENGTH - 1)) {
log_error("%s", "i3fread_line: line too long");
break;
}
}
while (c != '\n' && c != '\r');
do {
c = getc(fp);
}
while (c == '\n' || c == '\r');
ungetc(c, fp);
pline--;
*pline = '\0';
/*
* Since tildes generally aren't found at the end of lines, this seems workable. Will enable reading old configs.
*/
if (line[strlen(line) - 1] == '~')
line[strlen(line) - 1] = '\0';
return I3STRALLOC(line);
}
/*
* Read one word (into static buffer). [Taken from Smaug's fread_word]
*/
char *i3fread_word(FILE * fp)
{
static char word[MAX_INPUT_LENGTH];
char *pword;
char cEnd;
do {
if (feof(fp)) {
log_info("%s", "i3fread_word: EOF encountered on read.");
word[0] = '\0';
return word;
}
cEnd = getc(fp);
}
while (isspace(cEnd));
if (cEnd == '\'' || cEnd == '"') {
pword = word;
} else {
word[0] = cEnd;
pword = word + 1;
cEnd = ' ';
}
for (; pword < word + MAX_INPUT_LENGTH; pword++) {
if (feof(fp)) {
log_info("%s", "i3fread_word: EOF encountered on read.");
*pword = '\0';
return word;
}
*pword = getc(fp);
if (cEnd == ' ' ? isspace(*pword) : *pword == cEnd) {
if (cEnd == ' ')
ungetc(*pword, fp);
*pword = '\0';
return word;
}
}
log_info("%s", "i3fread_word: word too long");
return NULL;
}
char *i3fread_rest_of_line(FILE * fp)
{
static char word[MAX_STRING_LENGTH];
char *pword;
char c;
do {
if (feof(fp)) {
log_info("%s", "i3fread_rest_of_line: EOF encountered on read.");
word[0] = '\0';
return word;
}
c = getc(fp);
} while (isspace(c));
word[0] = c;
pword = word + 1;
for (; pword < word + MAX_STRING_LENGTH; pword++) {
if (feof(fp)) {
log_info("%s", "i3fread_rest_of_line: EOF encountered on read.");
*pword = '\0';
return word;
}
c = getc(fp);
if(c == '\n' || c == '\r') {
do {
c = getc(fp);
} while (c == '\n' || c == '\r');
if (!feof(fp))
ungetc(c, fp);
*pword = '\0';
return word;
} else {
*pword = c;
}
}
log_info("%s", "i3fread_rest_of_line: line too long");
return NULL;
}
/*
* Read a letter from a file. [Taken from Smaug's fread_letter]
*/
char i3fread_letter(FILE * fp)
{
char c;
do {
if (feof(fp)) {
log_info("%s", "i3fread_letter: EOF encountered on read.");
return '\0';
}
c = getc(fp);
}
while (isspace(c));
return c;
}
/*
* Read to end of line (for comments). [Taken from Smaug's fread_to_eol]
*/
void i3fread_to_eol(FILE * fp)
{
char c;
do {
if (feof(fp)) {
log_info("%s", "i3fread_to_eol: EOF encountered on read.");
return;
}
c = getc(fp);
}
while (c != '\n' && c != '\r');
do {
c = getc(fp);
}
while (c == '\n' || c == '\r');
ungetc(c, fp);
return;
}
/*
* Read and allocate space for a string from a file.
*/
char *i3fread_string(FILE * fp)
{
static char buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
char *ack = NULL;
int flag = FALSE;
int c = 0;
static char Empty[1] = "";
bzero(buf, MAX_STRING_LENGTH);
ack = buf;
flag = 0;
do {
c = getc(fp);
} while (isspace(c));
if ((*ack++ = c) == '~')
return Empty;
if (((int)(*ack++ = c)) == '\0xA2')
return Empty;
for (;;) {
if (ack > &buf[MAX_STRING_LENGTH - 1]) {
log_error("new_fread_string: MAX_STRING %d exceeded, truncating.", MAX_STRING_LENGTH);
return buf;
}
switch ((int)(*ack = getc(fp))) {
default:
flag = 0;
ack++;
break;
case EOF:
log_error("Fread_string: EOF");
return buf;
case '\r':
break;
case '~':
case '\0xA2':
ack++;
flag = 1;
break;
case '\n':
if (flag) {
if (ack > buf) {
ack--;
*ack = '\0';
}
return buf;
} else {
flag = 0;
ack++;
*ack++ = '\r';
}
break;
}
}
}
/******************************************
* Packet handling and routing functions. *
******************************************/
/*
* Write a string into the send-buffer. Does not yet send it.
*/
void I3_write_buffer(const char *msg)
{
long newsize = I3_output_pointer + strlen(msg);
if (newsize > OPS - 1) {
log_error("I3_write_buffer: buffer too large (would become %ld)", newsize);
return;
}
strlcpy(I3_output_buffer + I3_output_pointer, msg, newsize);
I3_output_pointer = newsize;
}
/* Use this function in place of I3_write_buffer ONLY if the text to be sent could
* contain color tags to parse into Pinkfish codes. Otherwise it will mess up the packet.
*/
void send_to_i3(const char *text)
{
char buf[MAX_STRING_LENGTH];
snprintf(buf, MAX_STRING_LENGTH, "%s", I3_mudtag_to_i3tag(text));
I3_write_buffer(buf);
}
/*
* Put a I3-header in the send-buffer. If a field is NULL it will
* be replaced by a 0 (zero).
*/
void I3_write_header(const char *identifier, const char *originator_mudname,
const char *originator_username, const char *target_mudname,
const char *target_username)
{
I3_write_buffer("({\"");
I3_write_buffer(identifier);
I3_write_buffer("\",5,");
if (originator_mudname) {
I3_write_buffer("\"");
I3_write_buffer(originator_mudname);
I3_write_buffer("\",");
} else
I3_write_buffer("0,");
if (originator_username) {
I3_write_buffer("\"");
I3_write_buffer(I3_nameescape(originator_username));
I3_write_buffer("\",");
} else
I3_write_buffer("0,");
if (target_mudname) {
I3_write_buffer("\"");
I3_write_buffer(target_mudname);
I3_write_buffer("\",");
} else
I3_write_buffer("0,");
if (target_username) {
I3_write_buffer("\"");
I3_write_buffer(target_username);
I3_write_buffer("\",");
} else
I3_write_buffer("0,");
}
/*
* Gets the next I3 field, that is when the amount of {[("'s and
* ")]}'s match each other when a , is read. It's not foolproof, it
* should honestly be some kind of statemachine, which does error-
* checking. Right now I trust the I3-router to send proper packets
* only. How naive :-) [Indeed Edwin, but I suppose we have little choice :P - Samson]
*
* ps will point to the beginning of the next field.
*
*/
char *I3_get_field(char *packet, char **ps)
{
int count[MAX_INPUT_LENGTH];
char has_apostrophe = 0,
has_backslash = 0;
char foundit = 0;
bzero(count, sizeof(count));
*ps = packet;
while (1) {
switch (*ps[0]) {
case '{':
if (!has_apostrophe)
count[(int)'{']++;
break;
case '}':
if (!has_apostrophe)
count[(int)'}']++;
break;
case '[':
if (!has_apostrophe)
count[(int)'[']++;
break;
case ']':
if (!has_apostrophe)
count[(int)']']++;
break;
case '(':
if (!has_apostrophe)
count[(int)'(']++;
break;
case ')':
if (!has_apostrophe)
count[(int)')']++;
break;
case '\\':
if (has_backslash)
has_backslash = 0;
else
has_backslash = 1;
break;
case '"':
if (has_backslash) {
has_backslash = 0;
} else {
if (has_apostrophe)
has_apostrophe = 0;
else
has_apostrophe = 1;
}
break;
case ',':
case ':':
if (has_apostrophe)
break;
if (has_backslash)
break;
if (count[(int)'{'] != count[(int)'}'])
break;
if (count[(int)'['] != count[(int)']'])
break;
if (count[(int)'('] != count[(int)')'])
break;
foundit = 1;
break;
}
if (foundit)
break;
(*ps)++;
}
*ps[0] = '\0';
(*ps)++;
return *ps;
}
/*
* Writes the string into the socket, prefixed by the size.
*/
bool I3_write_packet(char *msg)
{
int oldsize,
size,
check,
x;
char *s = I3_output_buffer;
oldsize = size = strlen(msg + 4);
s[3] = size % 256;
size >>= 8;
s[2] = size % 256;
size >>= 8;
s[1] = size % 256;
size >>= 8;
s[0] = size % 256;
/*
* Scan for \r used in Diku client packets and change to NULL
*/
for (x = 0; x < oldsize + 4; x++)
if (msg[x] == '\r' && x > 3)
msg[x] = '\0';
bytes_sent += oldsize + 4;
if (packetdebug) {
log_info("Sending: %d Bytes...", oldsize);
}
check = send(I3_socket, msg, oldsize + 4, 0);
if (packetdebug) {
log_info("Sent: %d Bytes...", check);
log_info("Packet Sent: %s", msg + 4);
}
#if 0
if (!check || (check < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
if (check < 0)
log_info("%s", "Write error on socket.");
else
log_info("%s", "EOF encountered on socket write.");
I3_connection_close(TRUE);
return FALSE;
}
if (check < 0) /* EAGAIN */
return TRUE;
//bytes_sent += check;
#endif
if ( check == 0 ) {
log_error( "EOF encountered on socket write." );
I3_connection_close(TRUE);
return FALSE;
}
if ( check < 0 ) {
switch( errno ) {
// Linux morons -- EAGAIN and EWOULDBLOCK are not actually the same error,
// even if you tend to do the same thing when they happen. 'tards.
//case EAGAIN:
case EWOULDBLOCK:
if (packetdebug) {
log_info("Socket would block, retrying later.");
}
return TRUE;
break;
case EMSGSIZE:
log_error( "Message too big for tcp/ip?" );
I3_connection_close(TRUE);
return FALSE;
break;
case ENOTCONN:
log_error( "Socket not actualy connected?" );
I3_connection_close(TRUE);
return FALSE;
break;
default:
log_error( "Socket error: %d (%s)", errno, strerror(errno) );
I3_connection_close(TRUE);
return FALSE;
}
}
I3_output_pointer = 4;
return TRUE;
}
void I3_send_packet(void)
{
I3_write_packet(I3_output_buffer);
return;
}
/* The all important startup packet. This is what will be initially sent upon trying to connect
* to the I3 router. It is therefore quite important that the information here be exactly correct.
* If anything is wrong, your packet will be dropped by the router as invalid and your mud simply
* won't connect to I3. DO NOT USE COLOR TAGS FOR ANY OF THIS INFORMATION!!!
* Bugs fixed in this on 8-31-03 for improperly sent tags. Streamlined for less packet bloat.
*/
void I3_startup_packet(void)
{
char s[MAX_INPUT_LENGTH];
char *strtime;
struct timeval last_time;
ROUTER_DATA *router;
if (!is_connecting())
return;
I3_output_pointer = 4;
I3_output_buffer[0] = '\0';
for (router = first_router; router; router = router->next) {
if (!strcasecmp(router->name, I3_ROUTER_NAME)) {
router->password = this_i3mud->password;
router->mudlist_id = this_i3mud->mudlist_id;
router->chanlist_id = this_i3mud->chanlist_id;
break;
}
}
I3_saverouters();
log_info("Sending startup_packet to %s", I3_ROUTER_NAME);
I3_write_header("startup-req-3", this_i3mud->name, NULL, I3_ROUTER_NAME, NULL);
snprintf(s, MAX_INPUT_LENGTH, "%d", this_i3mud->password);
I3_write_buffer(s);
I3_write_buffer(",");
snprintf(s, MAX_INPUT_LENGTH, "%d", this_i3mud->mudlist_id);
I3_write_buffer(s);
I3_write_buffer(",");
snprintf(s, MAX_INPUT_LENGTH, "%d", this_i3mud->chanlist_id);
I3_write_buffer(s);
I3_write_buffer(",");
snprintf(s, MAX_INPUT_LENGTH, "%d", this_i3mud->player_port);
I3_write_buffer(s);
I3_write_buffer(",0,0,\"");
I3_write_buffer(this_i3mud->mudlib);
I3_write_buffer("\",\"");
I3_write_buffer(this_i3mud->base_mudlib);
I3_write_buffer("\",\"");
I3_write_buffer(this_i3mud->driver);
I3_write_buffer("\",\"");
I3_write_buffer(this_i3mud->mud_type);
I3_write_buffer("\",\"");
I3_write_buffer(this_i3mud->open_status);
I3_write_buffer("\",\"");
I3_write_buffer(this_i3mud->admin_email);
I3_write_buffer("\",");
/*
* Begin first mapping set
*/
I3_write_buffer("([");
if (this_i3mud->emoteto)
I3_write_buffer("\"emoteto\":1,");
if (this_i3mud->news)
I3_write_buffer("\"news\":1,");
if (this_i3mud->ucache)
I3_write_buffer("\"ucache\":1,");
if (this_i3mud->auth)
I3_write_buffer("\"auth\":1,");
if (this_i3mud->locate)
I3_write_buffer("\"locate\":1,");
if (this_i3mud->finger)
I3_write_buffer("\"finger\":1,");
if (this_i3mud->channel)
I3_write_buffer("\"channel\":1,");
if (this_i3mud->who)
I3_write_buffer("\"who\":1,");
if (this_i3mud->tell)
I3_write_buffer("\"tell\":1,");
if (this_i3mud->beep)
I3_write_buffer("\"beep\":1,");
if (this_i3mud->mail)
I3_write_buffer("\"mail\":1,");
if (this_i3mud->file)
I3_write_buffer("\"file\":1,");
if (this_i3mud->http) {
snprintf(s, MAX_INPUT_LENGTH, "\"http\":%d,", this_i3mud->http);
I3_write_buffer(s);
}
if (this_i3mud->smtp) {
snprintf(s, MAX_INPUT_LENGTH, "\"smtp\":%d,", this_i3mud->smtp);
I3_write_buffer(s);
}
if (this_i3mud->pop3) {
snprintf(s, MAX_INPUT_LENGTH, "\"pop3\":%d,", this_i3mud->pop3);
I3_write_buffer(s);
}
if (this_i3mud->ftp) {
snprintf(s, MAX_INPUT_LENGTH, "\"ftp\":%d,", this_i3mud->ftp);
I3_write_buffer(s);
}
if (this_i3mud->nntp) {
snprintf(s, MAX_INPUT_LENGTH, "\"nntp\":%d,", this_i3mud->nntp);
I3_write_buffer(s);
}
if (this_i3mud->rcp) {
snprintf(s, MAX_INPUT_LENGTH, "\"rcp\":%d,", this_i3mud->rcp);
I3_write_buffer(s);
}
if (this_i3mud->amrcp) {
snprintf(s, MAX_INPUT_LENGTH, "\"amrcp\":%d,", this_i3mud->amrcp);
I3_write_buffer(s);
}
I3_write_buffer("]),([");
/*
* END first set of "mappings", start of second set
*/
if (this_i3mud->web && this_i3mud->web[0] != '\0') {
snprintf(s, MAX_INPUT_LENGTH, "\"url\":\"%s\",", this_i3mud->web);
I3_write_buffer(s);
}
strtime = ctime(&i3_time);
strtime[strlen(strtime) - 1] = '\0';
snprintf(s, MAX_INPUT_LENGTH, "\"time\":\"%s\",", strtime);
I3_write_buffer(s);
I3_write_buffer("]),})\r");
I3_send_packet();
if(i3_time == 0) {
// We called this before the first run of i3_loop(), from outside.
// Either that, or this code has travelled back in time, and I'm going to be rich!
gettimeofday(&last_time, NULL);
i3_time = (time_t) last_time.tv_sec;
}
connect_started_at = i3_time;
}
/* This function saves the password, mudlist ID, and chanlist ID that are used by the mud.
* The password value is returned from the I3 router upon your initial connection.
* The mudlist and chanlist ID values are updated as needed while your mud is connected.
* Do not modify the file it generates because doing so may prevent your mud from reconnecting
* to the router in the future. This file will be rewritten each time the i3_shutdown function
* is called, or any of the id values change.
*/
void I3_save_id(void)
{
FILE *fp;
if (!(fp = fopen(I3_PASSWORD_FILE, "w"))) {
log_info("%s", "Couldn't write to I3 password file.");
return;
}
fprintf(fp, "%s", "#PASSWORD\n");
fprintf(fp, "%d %d %d\n", this_i3mud->password, this_i3mud->mudlist_id,
this_i3mud->chanlist_id);
I3FCLOSE(fp);
}
/* The second most important packet your mud will deal with. If you never get this
* coming back from the I3 router, something was wrong with your startup packet
* or the router may be jammed up. Whatever the case, if you don't get a reply back
* your mud won't be acknowledged as connected.
*/
void I3_process_startup_reply(I3_HEADER *header, char *s)
{
ROUTER_DATA *router;
I3_CHANNEL *channel;
char *ps = s;
char *next_ps;
// Recevies the router list. Nothing much to do here until there's more than 1 router.
I3_get_field(ps, &next_ps);
//log_info("%s", ps);
ps = next_ps;
// Receives your mud's updated password, which may or may not be the same as what it sent out before
I3_get_field(ps, &next_ps);
this_i3mud->password = atoi(ps);
log_info("Received startup_reply from %s", header->originator_mudname);
I3_save_id();
for (router = first_router; router; router = router->next) {
if (!strcasecmp(router->name, header->originator_mudname)) {
router->reconattempts = 0;
I3_ROUTER_NAME = router->name;
I3_ROUTER_IP = router->ip;
router->password = this_i3mud->password;
break;
}
}
I3_saverouters();
i3wait = 0;
expecting_timeout = 0;
i3justconnected = 1; // This is just a flag to let us process on_connect things
// later, after we've had a chance to get channels setup
uptime = 0;
connected_at = i3_time;
time_to_taunt = getTimestamp() + I3_TAUNT_DELAY;
log_info("%s", "Intermud-3 Network connection complete.");
for (channel = first_I3chan; channel; channel = channel->next) {
//if (channel->local_name && channel->local_name[0] != '\0') {
log_info("Subscribing to %s", channel->I3_name);
I3_send_channel_listen(channel, TRUE);
//}
}
return;
}
void I3_process_chanack(I3_HEADER *header, char *s)
{
CHAR_DATA *ch;
char *next_ps,
*ps = s;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
if (!(ch = I3_find_user(header->target_username)))
log_info("%s", ps);
else
i3_printf(ch, "%%^GREEN%%^%%^BOLD%%^%s%%^RESET%%^\r\n", ps);
return;
}
void I3_send_error(const char *mud, const char *user, const char *code, const char *message)
{
if (!is_connected())
return;
I3_write_header("error", this_i3mud->name, 0, mud, user);
I3_write_buffer("\"");
I3_write_buffer(code);
I3_write_buffer("\",\"");
I3_write_buffer(I3_escape(message));
I3_write_buffer("\",0,})\r");
I3_send_packet();
}
void I3_process_error(I3_HEADER *header, char *s)
{
CHAR_DATA *ch;
char *next_ps,
*ps = s;
char type[MAX_INPUT_LENGTH],
error[MAX_STRING_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(type, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
/*
* Since VargonMUD likes to spew errors for no good reason....
*/
if (!strcasecmp(header->originator_mudname, "VargonMUD"))
return;
if (!strcasecmp(header->originator_mudname, "Ulario"))
return;
snprintf(error, MAX_STRING_LENGTH, "Error: from %s to %s@%s\r\n%s: %s",
header->originator_mudname, header->target_username, header->target_mudname, type,
ps);
if (!(ch = I3_find_user(header->target_username)))
log_info("%s", error);
else
i3_printf(ch, "%%^RED%%^%%^BOLD%%^%s%%^RESET%%^\r\n", error);
}
int I3_get_ucache_gender(char *name)
{
UCACHE_DATA *user;
for (user = first_ucache; user; user = user->next) {
if (!strcasecmp(user->name, name))
return user->gender;
}
/*
* -1 means you aren't in the list and need to be put there.
*/
return -1;
}
/* Saves the ucache info to disk because it would just be spamcity otherwise */
void I3_save_ucache(void)
{
FILE *fp;
UCACHE_DATA *user;
if (!(fp = fopen(I3_UCACHE_FILE, "w"))) {
log_info("%s", "Couldn't write to I3 ucache file.");
return;
}
for (user = first_ucache; user; user = user->next) {
fprintf(fp, "%s", "#UCACHE\n");
fprintf(fp, "Name %s\n", user->name);
fprintf(fp, "Sex %d\n", user->gender);
fprintf(fp, "Time %ld\n", (long int)user->time);
fprintf(fp, "%s", "End\n\n");
}
fprintf(fp, "%s", "#END\n");
I3FCLOSE(fp);
return;
}
void I3_prune_ucache(void)
{
UCACHE_DATA *ucache,
*next_ucache;
for (ucache = first_ucache; ucache; ucache = next_ucache) {
next_ucache = ucache->next;
/*
* Info older than 30 days is removed since this person likely hasn't logged in at all
*/
if (i3_time - ucache->time >= 2592000) {
I3STRFREE(ucache->name);
I3UNLINK(ucache, first_ucache, last_ucache, next, prev);
I3DISPOSE(ucache);
}
}
I3_save_ucache();
return;
}
/* Updates user info if they exist, adds them if they don't. */
void I3_ucache_update(char *name, int gender)
{
UCACHE_DATA *user;
for (user = first_ucache; user; user = user->next) {
if (!strcasecmp(user->name, name)) {
user->gender = gender;
user->time = i3_time;
return;
}
}
I3CREATE(user, UCACHE_DATA, 1);
user->name = I3STRALLOC(name);
user->gender = gender;
user->time = i3_time;
I3LINK(user, first_ucache, last_ucache, next, prev);
I3_save_ucache();
return;
}
void I3_send_ucache_update(const char *visname, int gender)
{
char buf[10];
if (!is_connected())
return;
I3_write_header("ucache-update", this_i3mud->name, NULL, NULL, NULL);
I3_write_buffer("\"");
I3_write_buffer(visname);
I3_write_buffer("\",\"");
I3_write_buffer(visname);
I3_write_buffer("\",");
snprintf(buf, 10, "%d", gender);
I3_write_buffer(buf);
I3_write_buffer(",})\r");
I3_send_packet();
return;
}
void I3_process_ucache_update(I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
char username[MAX_INPUT_LENGTH],
visname[MAX_INPUT_LENGTH],
buf[MAX_STRING_LENGTH];
int sex,
gender;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(username, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
gender = atoi(ps);
snprintf(buf, MAX_STRING_LENGTH, "%s@%s", visname, header->originator_mudname);
sex = I3_get_ucache_gender(buf);
if (sex == gender)
return;
I3_ucache_update(buf, gender);
return;
}
void I3_send_chan_user_req(char *targetmud, char *targetuser)
{
if (!is_connected())
return;
I3_write_header("chan-user-req", this_i3mud->name, NULL, targetmud, NULL);
I3_write_buffer("\"");
I3_write_buffer(targetuser);
I3_write_buffer("\",})\r");
I3_send_packet();
return;
}
void I3_process_chan_user_req(I3_HEADER *header, char *s)
{
char buf[MAX_STRING_LENGTH];
char *ps = s,
*next_ps;
int gender;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(buf, MAX_STRING_LENGTH, "%s@%s", header->target_username, this_i3mud->name);
gender = I3_get_ucache_gender(buf);
/*
* Gender of -1 means they aren't in the mud's ucache table, don't waste a packet on a reply
*/
if (gender == -1)
return;
I3_write_header("chan-user-reply", this_i3mud->name, NULL, header->originator_mudname, NULL);
I3_write_buffer("\"");
I3_write_buffer(ps);
I3_write_buffer("\",\"");
I3_write_buffer(ps);
I3_write_buffer("\",");
snprintf(buf, MAX_STRING_LENGTH, "%d", gender);
I3_write_buffer(buf);
I3_write_buffer(",})\r");
I3_send_packet();
return;
}
void I3_process_chan_user_reply(I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
char username[MAX_INPUT_LENGTH],
visname[MAX_INPUT_LENGTH],
buf[MAX_STRING_LENGTH];
int sex,
gender;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(username, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
gender = atoi(ps);
snprintf(buf, MAX_STRING_LENGTH, "%s@%s", visname, header->originator_mudname);
sex = I3_get_ucache_gender(buf);
if (sex == gender)
return;
I3_ucache_update(buf, gender);
return;
}
void I3_process_mudlist(I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
I3_MUD *mud = NULL;
char mud_name[MAX_INPUT_LENGTH];
ROUTER_DATA *router;
I3_get_field(ps, &next_ps);
this_i3mud->mudlist_id = atoi(ps);
I3_save_id();
for (router = first_router; router; router = router->next) {
if (!strcasecmp(router->name, I3_ROUTER_NAME)) {
router->mudlist_id = this_i3mud->mudlist_id;
break;
}
}
I3_saverouters();
// If we haven't processed the mudlist recently, do it now!
if(pulse_mudlist > (PULSE_MUDLIST / 10))
pulse_mudlist = 0;
ps = next_ps;
ps += 2;
while (1) {
char *next_ps2;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(mud_name, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps2);
if (ps[0] != '0') {
mud = find_I3_mud_by_name(mud_name);
if (!mud)
mud = new_I3_mud(mud_name);
ps += 2;
I3_get_field(ps, &next_ps);
mud->status = atoi(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->ipaddress);
mud->ipaddress = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
mud->player_port = atoi(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
mud->imud_tcp_port = atoi(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
mud->imud_udp_port = atoi(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->mudlib);
mud->mudlib = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->base_mudlib);
mud->base_mudlib = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->driver);
mud->driver = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->mud_type);
mud->mud_type = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->open_status);
mud->open_status = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(mud->admin_email);
mud->admin_email = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
ps += 2;
while (1) {
char *next_ps3;
char key[MAX_INPUT_LENGTH];
if (ps[0] == ']')
break;
I3_get_field(ps, &next_ps3);
remove_quotes(&ps);
strlcpy(key, ps, MAX_INPUT_LENGTH);
ps = next_ps3;
I3_get_field(ps, &next_ps3);
switch (key[0]) {
case 'a':
if (!strcasecmp(key, "auth")) {
mud->auth = ps[0] == '0' ? 0 : 1;
break;
}
if (!strcasecmp(key, "amrcp")) {
mud->amrcp = atoi(ps);
break;
}
break;
case 'b':
if (!strcasecmp(key, "beep")) {
mud->beep = ps[0] == '0' ? 0 : 1;
break;
}
break;
case 'c':
if (!strcasecmp(key, "channel")) {
mud->channel = ps[0] == '0' ? 0 : 1;
break;
}
break;
case 'e':
if (!strcasecmp(key, "emoteto")) {
mud->emoteto = ps[0] == '0' ? 0 : 1;
break;
}
break;
case 'f':
if (!strcasecmp(key, "file")) {
mud->file = ps[0] == '0' ? 0 : 1;
break;
}
if (!strcasecmp(key, "finger")) {
mud->finger = ps[0] == '0' ? 0 : 1;
break;
}
if (!strcasecmp(key, "ftp")) {
mud->ftp = atoi(ps);
break;
}
break;
case 'h':
if (!strcasecmp(key, "http")) {
mud->http = atoi(ps);
break;
}
break;
case 'l':
if (!strcasecmp(key, "locate")) {
mud->locate = ps[0] == '0' ? 0 : 1;
break;
}
break;
case 'm':
if (!strcasecmp(key, "mail")) {
mud->mail = ps[0] == '0' ? 0 : 1;
break;
}
break;
case 'n':
if (!strcasecmp(key, "news")) {
mud->news = ps[0] == '0' ? 0 : 1;
break;
}
if (!strcasecmp(key, "nntp")) {
mud->nntp = atoi(ps);
break;
}
break;
case 'p':
if (!strcasecmp(key, "pop3")) {
mud->pop3 = atoi(ps);
break;
}
break;
case 'r':
if (!strcasecmp(key, "rcp")) {
mud->rcp = atoi(ps);
break;
}
break;
case 's':
if (!strcasecmp(key, "smtp")) {
mud->smtp = atoi(ps);
break;
}
break;
case 't':
if (!strcasecmp(key, "tell")) {
mud->tell = ps[0] == '0' ? 0 : 1;
break;
}
break;
case 'u':
if (!strcasecmp(key, "ucache")) {
mud->ucache = ps[0] == '0' ? 0 : 1;
break;
}
if (!strcasecmp(key, "url")) {
remove_quotes(&ps);
I3STRFREE(mud->web_wrong);
mud->web_wrong = I3STRALLOC(ps);
break;
}
break;
case 'w':
if (!strcasecmp(key, "who")) {
mud->who = ps[0] == '0' ? 0 : 1;
break;
}
break;
default:
break;
}
ps = next_ps3;
if (ps[0] == ']')
break;
}
ps = next_ps;
I3_get_field(ps, &next_ps);
ps = next_ps;
} else {
if ((mud = find_I3_mud_by_name(mud_name)) != NULL)
destroy_I3_mud(mud);
}
ps = next_ps2;
if (ps[0] == ']')
break;
}
return;
}
void I3_process_chanlist_reply(I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
I3_CHANNEL *channel;
char chan[MAX_INPUT_LENGTH];
ROUTER_DATA *router;
log_info("I3_process_chanlist_reply: %s", "Got chanlist-reply packet!");
I3_get_field(ps, &next_ps);
this_i3mud->chanlist_id = atoi(ps);
I3_save_id();
for (router = first_router; router; router = router->next) {
if (!strcasecmp(router->name, I3_ROUTER_NAME)) {
router->chanlist_id = this_i3mud->chanlist_id;
break;
}
}
I3_saverouters();
ps = next_ps;
ps += 2;
while (1) {
char *next_ps2;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(chan, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps2);
if (ps[0] != '0') {
if (!(channel = find_I3_channel_by_name(chan))) {
channel = new_I3_channel();
channel->I3_name = I3STRALLOC(chan);
log_info("New channel %s has been added from router %s", channel->I3_name,
I3_ROUTER_NAME);
} else {
log_info("Channel %s has been updated from router %s", channel->I3_name,
I3_ROUTER_NAME);
}
ps += 2;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3STRFREE(channel->host_mud);
channel->host_mud = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
channel->status = atoi(ps);
} else {
if ((channel = find_I3_channel_by_name(chan)) != NULL) {
if (channel->local_name && channel->local_name[0] != '\0')
log_info("Locally configured channel %s has been purged from router %s",
channel->local_name, I3_ROUTER_NAME);
destroy_I3_channel(channel);
I3_write_channel_config();
}
}
ps = next_ps2;
if (ps[0] == ']')
break;
}
log_info("I3_process_chanlist_reply: %s", "Saving channel config data.");
I3_write_channel_config();
return;
}
void I3_send_channel_message(I3_CHANNEL *channel, const char *name, const char *message)
{
char buf[MAX_STRING_LENGTH];
if (!is_connected())
return;
strlcpy(buf, message, MAX_STRING_LENGTH);
//log_info("I3_send_channel(%s@%s, %s, %s)", channel->I3_name, channel->host_mud, name, message);
I3_write_header("channel-m", this_i3mud->name, name, NULL, NULL);
//log_info("I3_send_channel() header setup.");
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",\"");
I3_write_buffer(I3_nameremap(name));
//log_info("I3_send_channel() name remap %s to %s.", name, I3_nameremap(name));
I3_write_buffer("\",\"");
send_to_i3(I3_escape(buf));
//log_info("I3_send_channel() escaped buffer.");
I3_write_buffer("\",})\r");
lag_spike = i3_time;
I3_send_packet();
channel_m_sent++;
//log_info("I3_send_channel() done.");
return;
}
void I3_send_channel_emote(I3_CHANNEL *channel, const char *name, const char *message)
{
char buf[MAX_STRING_LENGTH];
if (!is_connected())
return;
if (strstr(message, "$N") == NULL)
snprintf(buf, MAX_STRING_LENGTH, "$N %s", message);
else
strlcpy(buf, message, MAX_STRING_LENGTH);
I3_write_header("channel-e", this_i3mud->name, name, NULL, NULL);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",\"");
I3_write_buffer(I3_nameremap(name));
I3_write_buffer("\",\"");
send_to_i3(I3_escape(buf));
I3_write_buffer("\",})\r");
I3_send_packet();
return;
}
void I3_send_channel_t(I3_CHANNEL *channel, const char *name, char *tmud, char *tuser, char *msg_o,
char *msg_t, char *tvis)
{
if (!is_connected())
return;
I3_write_header("channel-t", this_i3mud->name, name, NULL, NULL);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",\"");
I3_write_buffer(tmud);
I3_write_buffer("\",\"");
I3_write_buffer(tuser);
I3_write_buffer("\",\"");
send_to_i3(I3_escape(msg_o));
I3_write_buffer("\",\"");
send_to_i3(I3_escape(msg_t));
I3_write_buffer("\",\"");
I3_write_buffer(name);
I3_write_buffer("\",\"");
I3_write_buffer(tvis);
I3_write_buffer("\",})\r");
I3_send_packet();
return;
}
int I3_token(char type, char *string, char *oname, char *tname)
{
char code[500];
char *p;
switch (type) {
default:
code[0] = type;
code[1] = '\0';
return 1;
case '$':
strlcpy(code, "$", 500);
break;
case ' ':
strlcpy(code, " ", 500);
break;
case 'N': /* Originator's name */
strlcpy(code, oname, 500);
break;
case 'O': /* Target's name */
strlcpy(code, tname, 500);
break;
}
p = code;
while (*p != '\0') {
*string = *p++;
*++string = '\0';
}
return (strlen(code));
}
void I3_message_convert(char *buffer, const char *txt, char *oname, char *tname)
{
const char *point;
int skip = 0;
for (point = txt; *point; point++) {
if (*point == '$') {
point++;
if (*point == '\0')
point--;
else
skip = I3_token(*point, buffer, oname, tname);
while (skip-- > 0)
++buffer;
continue;
}
*buffer = *point;
*++buffer = '\0';
}
*buffer = '\0';
return;
}
char *I3_convert_channel_message(const char *message,
char *sname, char *tname)
{
static char msgbuf[MAX_STRING_LENGTH];
strcpy(msgbuf, "ERROR");
/*
* Sanity checks - if any of these are NULL, bad things will happen - Samson 6-29-01
*/
if (!message) {
log_error("%s", "I3_convert_channel_message: NULL message!");
return msgbuf;
}
if (!sname) {
log_error("%s", "I3_convert_channel_message: NULL sname!");
return msgbuf;
}
if (!tname) {
log_error("%s", "I3_convert_channel_message: NULL tname!");
return msgbuf;
}
I3_message_convert(msgbuf, message, sname, tname);
return msgbuf;
}
void update_chanhistory(I3_CHANNEL *channel, char *message)
{
char msg[MAX_STRING_LENGTH],
buf[MAX_STRING_LENGTH];
struct tm *local;
time_t t;
int x;
if (!channel) {
log_error("%s", "update_chanhistory: NULL channel received!");
return;
}
if (!message || message[0] == '\0') {
log_error("%s", "update_chanhistory: NULL message received!");
return;
}
strlcpy(msg, message, MAX_STRING_LENGTH);
for (x = 0; x < MAX_I3HISTORY; x++) {
if (channel->history[x] == NULL) {
t = time(NULL);
local = localtime(&t);
snprintf(buf, MAX_STRING_LENGTH, "%%^RED%%^%%^BOLD%%^%-4.4d-%-2.2d-%-2.2d%%^RESET%%^ %%^GREEN%%^%%^BOLD%%^%s",
local->tm_year + 1900, local->tm_mon + 1, local->tm_mday, msg);
channel->history[x] = I3STRALLOC(buf);
if (I3IS_SET(channel->flags, I3CHAN_LOG) && channel->local_name && channel->local_name[0]) {
FILE *fp;
snprintf(buf, MAX_STRING_LENGTH, "%s%s.log", I3_DIR, channel->local_name);
if (!(fp = fopen(buf, "a"))) {
perror(buf);
log_error("Could not open file %s!", buf);
} else {
fprintf(fp, "%s\n", i3_strip_colors(channel->history[x]));
I3FCLOSE(fp);
}
}
break;
}
if (x == MAX_I3HISTORY - 1) {
int y;
for (y = 1; y < MAX_I3HISTORY; y++) {
int z = y - 1;
if (channel->history[z] != NULL) {
I3STRFREE(channel->history[z]);
channel->history[z] = I3STRALLOC(channel->history[y]);
}
}
t = time(NULL);
local = localtime(&t);
snprintf(buf, MAX_STRING_LENGTH, "%%^RED%%^%%^BOLD%%^%-4.4d-%-2.2d-%-2.2d%%^RESET%%^ %%^GREEN%%^%%^BOLD%%^%s",
local->tm_year + 1900, local->tm_mon + 1, local->tm_mday, msg);
I3STRFREE(channel->history[x]);
channel->history[x] = I3STRALLOC(buf);
if (I3IS_SET(channel->flags, I3CHAN_LOG) && channel->local_name && channel->local_name[0]) {
FILE *fp;
snprintf(buf, MAX_STRING_LENGTH, "%s%s.log", I3_DIR, channel->local_name);
if (!(fp = fopen(buf, "a"))) {
perror(buf);
log_error("Could not open file %s!", buf);
} else {
fprintf(fp, "%s\n", i3_strip_colors(channel->history[x]));
I3FCLOSE(fp);
}
}
}
}
return;
}
/* Handles the support for channel filtering.
* Pretty basic right now. Any truly useful filtering would have to be at the discretion of the channel owner anyway.
*/
void I3_chan_filter_m(I3_CHANNEL *channel, I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
char visname[MAX_INPUT_LENGTH],
newmsg[MAX_STRING_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(newmsg, ps, MAX_STRING_LENGTH);
snprintf(newmsg, MAX_STRING_LENGTH, "%s%s", ps, " (filtered M)");
I3_write_header("chan-filter-reply", this_i3mud->name, NULL, I3_ROUTER_NAME, NULL);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",({\"channel-m\",5,\"");
I3_write_buffer(header->originator_mudname);
I3_write_buffer("\",\"");
I3_write_buffer(header->originator_username);
I3_write_buffer("\",0,0,\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",\"");
I3_write_buffer(visname);
I3_write_buffer("\",\"");
I3_write_buffer(newmsg);
I3_write_buffer("\",}),})\r");
I3_send_packet();
return;
}
/* Handles the support for channel filtering.
* Pretty basic right now. Any truly useful filtering would have to be at the discretion of the channel owner anyway.
*/
void I3_chan_filter_e(I3_CHANNEL *channel, I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
char visname[MAX_INPUT_LENGTH],
newmsg[MAX_STRING_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(newmsg, MAX_STRING_LENGTH, "%s%s", ps, " (filtered E)");
I3_write_header("chan-filter-reply", this_i3mud->name, NULL, I3_ROUTER_NAME, NULL);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",({\"channel-e\",5,\"");
I3_write_buffer(header->originator_mudname);
I3_write_buffer("\",\"");
I3_write_buffer(header->originator_username);
I3_write_buffer("\",0,0,\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",\"");
I3_write_buffer(visname);
I3_write_buffer("\",\"");
I3_write_buffer(newmsg);
I3_write_buffer("\",}),})\r");
I3_send_packet();
return;
}
/* Handles the support for channel filtering.
* Pretty basic right now. Any truly useful filtering would have to be at the discretion of the channel owner anyway.
*/
void I3_chan_filter_t(I3_CHANNEL *channel, I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
char targetmud[MAX_INPUT_LENGTH],
targetuser[MAX_INPUT_LENGTH],
message_o[MAX_STRING_LENGTH],
message_t[MAX_STRING_LENGTH];
char visname_o[MAX_INPUT_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(targetmud, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(targetuser, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(message_o, MAX_STRING_LENGTH, "%s%s", ps, " (filtered T)");
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(message_t, MAX_STRING_LENGTH, "%s%s", ps, " (filtered T)");
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname_o, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
I3_write_header("chan-filter-reply", this_i3mud->name, NULL, I3_ROUTER_NAME, NULL);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",({\"channel-t\",5,\"");
I3_write_buffer(header->originator_mudname);
I3_write_buffer("\",\"");
I3_write_buffer(header->originator_username);
I3_write_buffer("\",0,0,\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",\"");
I3_write_buffer(targetmud);
I3_write_buffer("\",\"");
I3_write_buffer(targetuser);
I3_write_buffer("\",\"");
send_to_i3(I3_escape(message_o));
I3_write_buffer("\",\"");
send_to_i3(I3_escape(message_t));
I3_write_buffer("\",\"");
I3_write_buffer(visname_o);
I3_write_buffer("\",\"");
I3_write_buffer(ps);
I3_write_buffer("\",}),})\r");
I3_send_packet();
return;
}
void I3_process_channel_filter(I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
char ptype[MAX_INPUT_LENGTH];
I3_CHANNEL *channel = NULL;
I3_HEADER *second_header;
char channel_name[MAX_INPUT_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("I3_process_channel_filter: received unknown channel (%s)", ps);
return;
}
if (!channel->local_name || !channel->local_name[0]) {
//return; // We don't listen to it.
// But let's go ahead and log it anyways...
strlcpy(channel_name, ps, MAX_INPUT_LENGTH);
} else {
// Situation normal, copy this to simplify the code down there...
strlcpy(channel_name, channel->local_name, MAX_INPUT_LENGTH);
}
ps = next_ps;
ps += 2;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(ptype, ps, MAX_INPUT_LENGTH);
second_header = I3_get_header(&ps);
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
if (!strcasecmp(ptype, "channel-m"))
I3_chan_filter_m(channel, second_header, next_ps);
if (!strcasecmp(ptype, "channel-e"))
I3_chan_filter_e(channel, second_header, next_ps);
if (!strcasecmp(ptype, "channel-t"))
I3_chan_filter_t(channel, second_header, next_ps);
I3DISPOSE(second_header);
return;
}
#define I3_ALLCHAN_LOG I3_DIR "i3.allchan.log"
// The speaker here is visname, not originator_username.
// We should probably pass in both, so we can use both.
//
void allchan_log( int is_emote, char *channel, char *speaker, char *username, char *mud, char *str ) {
FILE *fp = NULL;
struct tm *local = NULL;
struct timeval last_time;
if(i3_time == 0) {
// We called this before the first run of i3_loop(), from outside.
// Either that, or this code has travelled back in time, and I'm going to be rich!
gettimeofday(&last_time, NULL);
i3_time = (time_t) last_time.tv_sec;
}
local = localtime(&i3_time);
if(last_second != i3_time) {
last_second = i3_time;
sub_second_counter = 0;
} else {
sub_second_counter++;
}
if(!(fp = fopen(I3_ALLCHAN_LOG, "a"))) {
log_error("Could not open file %s!", I3_ALLCHAN_LOG);
} else {
fprintf(fp, "%04d.%02d.%02d-%02d.%02d,%02d%03d\t%s\t%s@%s\t%c\t%s\n",
local->tm_year + 1900, local->tm_mon + 1, local->tm_mday,
local->tm_hour, local->tm_min, local->tm_sec, sub_second_counter,
channel, speaker, mud,
is_emote ? 't' : 'f',
str);
I3FCLOSE(fp);
}
allchan_sql(is_emote, channel, speaker, username, mud, str);
}
char *color_time( struct tm *local )
{
static char timestamp[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0\0";
char *hours[] = {
"%^BLACK%^%^BOLD%^",
"%^BLACK%^%^BOLD%^",
"%^BLACK%^%^BOLD%^",
"%^BLACK%^%^BOLD%^",
"%^RED%^",
"%^RED%^",
"%^ORANGE%^",
"%^ORANGE%^",
"%^YELLOW%^",
"%^YELLOW%^",
"%^GREEN%^",
"%^GREEN%^",
"%^GREEN%^%^BOLD%^",
"%^GREEN%^%^BOLD%^",
"%^WHITE%^",
"%^WHITE%^",
"%^CYAN%^%^BOLD%^",
"%^CYAN%^%^BOLD%^",
"%^CYAN%^",
"%^CYAN%^",
"%^BLUE%^%^BOLD%^",
"%^BLUE%^%^BOLD%^",
"%^BLUE%^",
"%^BLUE%^"
};
snprintf(timestamp, MAX_INPUT_LENGTH,
"%s%-2.2d:%s%-2.2d%%^RESET%%^",
hours[local->tm_hour], local->tm_hour, hours[local->tm_hour], local->tm_min);
return timestamp;
}
char *color_speaker( const char *speaker )
{
static char result[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0\0";
char lowercase_name[MAX_INPUT_LENGTH];
const char *found = NULL;
char *colormap[] = {
"%^BLACK%^%^BOLD%^",
"%^RED%^",
"%^GREEN%^",
"%^ORANGE%^",
"%^BLUE%^",
"%^MAGENTA%^",
"%^CYAN%^",
"%^WHITE%^",
"%^RED%^%^BOLD%^",
"%^GREEN%^%^BOLD%^",
"%^YELLOW%^",
"%^MAGENTA%^%^BOLD%^",
"%^BLUE%^%^BOLD%^",
"%^CYAN%^%^BOLD%^",
"%^WHITE%^%^BOLD%^"
};
if( speaker && *speaker ) {
bzero(lowercase_name, MAX_INPUT_LENGTH);
for(int i = 0; i < strlen(speaker) && i < MAX_INPUT_LENGTH; i++) {
if(isalpha(speaker[i]))
lowercase_name[i] = tolower(speaker[i]);
else
lowercase_name[i] = speaker[i];
}
found = (const char *)stringmap_find(speaker_db, lowercase_name);
if(found) {
// Simply return the color code found.
snprintf(result, MAX_INPUT_LENGTH, "%s", found);
// For now, always add so it can catch up...
// addspeaker_sql(lowercase_name, found);
} else {
// Add them as a new speaker and THEN return the color code.
found = colormap[speaker_count % (sizeof(colormap) / sizeof(colormap[0]))];
stringmap_add( speaker_db, lowercase_name, (void *)found, strlen(found)+1 );
speaker_count++;
I3_saveSpeakers();
snprintf(result, MAX_INPUT_LENGTH, "%s", found);
addspeaker_sql(lowercase_name, found);
}
}
return result;
}
void I3_process_channel_t(I3_HEADER *header, char *s)
{
char *ps = s;
char *next_ps;
DESCRIPTOR_DATA *d;
CHAR_DATA *vch = NULL;
char targetmud[MAX_INPUT_LENGTH],
targetuser[MAX_INPUT_LENGTH],
message_o[MAX_STRING_LENGTH],
message_t[MAX_STRING_LENGTH],
buf[MAX_STRING_LENGTH];
char visname_o[MAX_INPUT_LENGTH],
sname[MAX_INPUT_LENGTH],
tname[MAX_INPUT_LENGTH],
lname[MAX_INPUT_LENGTH],
layout[MAX_INPUT_LENGTH],
tmsg[MAX_STRING_LENGTH],
omsg[MAX_STRING_LENGTH];
I3_CHANNEL *channel = NULL;
struct tm *local = localtime(&i3_time);
char channel_name[MAX_INPUT_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("I3_process_channel_t: received unknown channel (%s)", ps);
return; // We totally don't recognize this channel.
}
if (!channel->local_name || !channel->local_name[0]) {
//return; // We don't listen to it.
// But let's go ahead and log it anyways...
strlcpy(channel_name, ps, MAX_INPUT_LENGTH);
} else {
// Situation normal, copy this to simplify the code down there...
strlcpy(channel_name, channel->local_name, MAX_INPUT_LENGTH);
}
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(targetmud, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(targetuser, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(message_o, ps, MAX_STRING_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(message_t, ps, MAX_STRING_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname_o, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(sname, MAX_INPUT_LENGTH, "%s@%s", visname_o, header->originator_mudname);
snprintf(tname, MAX_INPUT_LENGTH, "%s@%s", ps, targetmud);
snprintf(omsg, MAX_STRING_LENGTH, "%s",
I3_convert_channel_message(message_o, sname, tname));
snprintf(tmsg, MAX_STRING_LENGTH, "%s",
I3_convert_channel_message(message_t, sname, tname));
strcpy(layout, "%s ");
strcat(layout, channel->layout_e);
allchan_log(1, channel_name, visname_o, header->originator_username, header->originator_mudname, omsg);
for (d = first_descriptor; d; d = d->next) {
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (!I3_hasname(I3LISTEN(vch), channel_name)
|| I3_hasname(I3DENY(vch), channel_name))
continue;
snprintf(lname, MAX_INPUT_LENGTH, "%s@%s", CH_I3NAME(vch), this_i3mud->name);
if (d->connected == CON_PLAYING && !i3ignoring(vch, sname)) {
if (!strcasecmp(lname, tname)) {
sprintf(buf, layout, color_time(local), channel_name, tmsg);
i3_printf(vch, "%s%%^RESET%%^\r\n", buf);
} else {
sprintf(buf, layout, color_time(local), channel_name, omsg);
i3_printf(vch, "%s%%^RESET%%^\r\n", buf);
}
}
}
update_chanhistory(channel, omsg);
time_to_taunt = getTimestamp() + I3_TAUNT_DELAY;
return;
}
void I3_process_channel_m(I3_HEADER *header, char *s)
{
char *ps = s;
char *next_ps;
DESCRIPTOR_DATA *d;
CHAR_DATA *vch = NULL;
char visname[MAX_INPUT_LENGTH],
tmpvisname[MAX_INPUT_LENGTH],
buf[MAX_STRING_LENGTH],
tps[MAX_STRING_LENGTH],
format[MAX_INPUT_LENGTH];
I3_CHANNEL *channel;
struct tm *local = localtime(&i3_time);
int len;
char speaker_color[MAX_INPUT_LENGTH];
char mud_color[MAX_INPUT_LENGTH];
char magic_visname[MAX_INPUT_LENGTH];
char channel_name[MAX_INPUT_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("channel_m: received unknown channel (%s)", ps);
return; // We totally don't recognize this channel.
}
if (!channel->local_name || !channel->local_name[0]) {
//return; // We don't listen to it.
// But let's go ahead and log it anyways...
strlcpy(channel_name, ps, MAX_INPUT_LENGTH);
} else {
// Situation normal, copy this to simplify the code down there...
strlcpy(channel_name, channel->local_name, MAX_INPUT_LENGTH);
}
channel_m_received++;
if (lag_spike > 0) {
lag_spike = i3_time - lag_spike;
if (lag_spike > record_lag_spike)
record_lag_spike = lag_spike;
lag_spike = 0;
}
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
/* Try to squash multiple trailing newlines in the packet message */
strlcpy(tps, ps, MAX_STRING_LENGTH);
len = strlen(tps);
while(ISNEWL(tps[len-1]) && len > 1) {
tps[len-1] = '\0';
len--;
}
strcpy(format, "%s ");
strcat(format, channel->layout_m);
snprintf(tmpvisname, MAX_INPUT_LENGTH, "%s@%s", visname, header->originator_mudname);
if(!strcasecmp(visname, header->originator_username)) {
strlcpy(magic_visname, visname, MAX_INPUT_LENGTH);
} else {
snprintf(magic_visname, MAX_INPUT_LENGTH, "%s(%s)", visname, header->originator_username);
}
//snprintf(buf, MAX_STRING_LENGTH, format, color_time(local), channel_name, visname, header->originator_mudname, tps);
// We omit the RESET from the end of the speaker name, so it can also catch the @ if the channel
// format string doesn't override it.
snprintf(speaker_color, MAX_INPUT_LENGTH, "%s%s", color_speaker(header->originator_username), magic_visname);
snprintf(mud_color, MAX_INPUT_LENGTH, "%s%s%%^RESET%%^", color_speaker(header->originator_username), header->originator_mudname);
snprintf(buf, MAX_STRING_LENGTH, format, color_time(local), channel_name, speaker_color, mud_color, tps);
allchan_log(0, channel_name, visname, header->originator_username, header->originator_mudname, tps);
for (d = first_descriptor; d; d = d->next) {
if (!d || !d->character)
continue;
if (d->connected != CON_PLAYING)
continue;
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (!I3_hasname(I3LISTEN(vch), channel_name)
|| I3_hasname(I3DENY(vch), channel_name))
continue;
if (d->connected == CON_PLAYING) {
if (i3ignoring(vch, visname))
continue;
if (i3ignoring(vch, tmpvisname))
continue;
i3wrap_printf(vch, "%s%%^RESET%%^\r\n", buf);
}
}
update_chanhistory(channel, buf);
time_to_taunt = getTimestamp() + I3_TAUNT_DELAY;
return;
}
void I3_process_channel_e(I3_HEADER *header, char *s)
{
char *ps = s;
char *next_ps;
DESCRIPTOR_DATA *d;
CHAR_DATA *vch = NULL;
char visname[MAX_INPUT_LENGTH],
tmpvisname[MAX_INPUT_LENGTH],
msg[MAX_STRING_LENGTH],
buf[MAX_STRING_LENGTH],
tps[MAX_STRING_LENGTH],
format[MAX_INPUT_LENGTH];
I3_CHANNEL *channel;
struct tm *local = localtime(&i3_time);
int len;
char speaker_color[MAX_INPUT_LENGTH];
char mud_color[MAX_INPUT_LENGTH];
int hack_len = 0;
char channel_name[MAX_INPUT_LENGTH];
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("channel_e: received unknown channel (%s)", ps);
return; // We totally don't recognize this channel.
}
if (!channel->local_name || !channel->local_name[0]) {
//return; // We don't listen to it.
// But let's go ahead and log it anyways...
strlcpy(channel_name, ps, MAX_INPUT_LENGTH);
} else {
// Situation normal, copy this to simplify the code down there...
strlcpy(channel_name, channel->local_name, MAX_INPUT_LENGTH);
}
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
//snprintf(visname, MAX_INPUT_LENGTH, "%s@%s", ps, header->originator_mudname);
strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
/* Try to squash multiple trailing newlines in the packet message */
strlcpy(tps, ps, MAX_STRING_LENGTH);
len = strlen(tps);
while(ISNEWL(tps[len-1]) && len > 1) {
tps[len-1] = '\0';
len--;
}
//strcpy(format, "%%^GREEN%%^%%^BOLD%%^%-2.2d%%^WHITE%%^%%^BOLD%%^:%%^GREEN%%^%%^BOLD%%^%-2.2d%%^RESET%%^ ");
//strcat(format, channel->layout_e);
//snprintf(buf, MAX_STRING_LENGTH, format, local->tm_hour, local->tm_min, channel_name, msg);
strcpy(format, "%s ");
strcat(format, channel->layout_e);
snprintf(tmpvisname, MAX_INPUT_LENGTH, "%s@%s", visname, header->originator_mudname);
// We omit the RESET from the end of the speaker name, so it can also catch the @ if the channel
// format string doesn't override it.
snprintf(speaker_color, MAX_INPUT_LENGTH, "%s%s", color_speaker(header->originator_username), visname);
snprintf(mud_color, MAX_INPUT_LENGTH, "%s%s%%^RESET%%^", color_speaker(header->originator_username), header->originator_mudname);
snprintf(msg, MAX_STRING_LENGTH, "%s", I3_convert_channel_message(tps, tmpvisname, tmpvisname));
// Emotes suck. The message itself has user@mud embedded in it, so to do things right,
// we need to skip over that for the actual message, but still handle it with hackery
strcat(format, "@%s%s");
hack_len = strlen(tmpvisname);
snprintf(buf, MAX_STRING_LENGTH, format, color_time(local), channel_name, speaker_color, mud_color, &msg[hack_len]);
allchan_log(1, channel_name, visname, header->originator_username, header->originator_mudname, msg);
for (d = first_descriptor; d; d = d->next) {
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (!I3_hasname(I3LISTEN(vch), channel_name)
|| I3_hasname(I3DENY(vch), channel_name))
continue;
if (d->connected == CON_PLAYING) {
if (i3ignoring(vch, visname))
continue;
if (i3ignoring(vch, tmpvisname))
continue;
i3wrap_printf(vch, "%s%%^RESET%%^\r\n", buf);
}
}
update_chanhistory(channel, buf);
time_to_taunt = getTimestamp() + I3_TAUNT_DELAY;
return;
}
void I3_process_chan_who_req(I3_HEADER *header, char *s)
{
CHAR_DATA *vch;
DESCRIPTOR_DATA *d;
char *ps = s,
*next_ps;
char buf[MAX_STRING_LENGTH],
ibuf[MAX_INPUT_LENGTH];
I3_CHANNEL *channel;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(ibuf, MAX_INPUT_LENGTH, "%s@%s", header->originator_username,
header->originator_mudname);
if (!(channel = find_I3_channel_by_name(ps))) {
snprintf(buf, MAX_STRING_LENGTH, "The channel you specified (%s) is unknown at %s", ps,
this_i3mud->name);
I3_send_error(header->originator_mudname, header->originator_username, "unk-channel",
buf);
log_info("chan_who_req: received unknown channel (%s)", ps);
return;
}
if (!channel->local_name) {
snprintf(buf, MAX_STRING_LENGTH,
"The channel you specified (%s) is not registered at %s", ps, this_i3mud->name);
I3_send_error(header->originator_mudname, header->originator_username, "unk-channel",
buf);
return;
}
I3_write_header("chan-who-reply", this_i3mud->name, NULL, header->originator_mudname,
header->originator_username);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",({");
for (d = first_descriptor; d; d = d->next) {
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (I3ISINVIS(vch))
continue;
if (I3_hasname(I3LISTEN(vch), channel->local_name) && !i3ignoring(vch, ibuf)
&& !I3_hasname(I3DENY(vch), channel->local_name)) {
I3_write_buffer("\"");
I3_write_buffer(CH_I3NAME(vch));
I3_write_buffer("\",");
}
}
I3_write_buffer("}),})\r");
I3_send_packet();
return;
}
void I3_process_chan_who_reply(I3_HEADER *header, char *s)
{
char *ps = s,
*next_ps;
CHAR_DATA *ch;
if (!(ch = I3_find_user(header->target_username))) {
log_error("I3_process_chan_who_reply(): user %s not found.", header->target_username);
return;
}
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
i3_printf(ch, "%%^WHITE%%^%%^BOLD%%^Users listening to %s on %s:%%^RESET%%^\r\n\r\n", ps, header->originator_mudname);
ps = next_ps;
I3_get_field(ps, &next_ps);
ps += 2;
while (1) {
if (ps[0] == '}') {
i3_printf(ch, "%%^CYAN%%^No information returned or no people listening.%%^RESET%%^\r\n");
return;
}
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
i3_printf(ch, "%%^CYAN%%^%s%%^RESET%%^\r\n", ps);
ps = next_ps;
if (ps[0] == '}')
break;
}
return;
}
void I3_send_chan_who(CHAR_DATA *ch, I3_CHANNEL *channel, I3_MUD *mud)
{
if (!is_connected())
return;
I3_write_header("chan-who-req", this_i3mud->name, CH_I3NAME(ch), mud->name, NULL);
I3_write_buffer("\"");
I3_write_buffer(channel->I3_name);
I3_write_buffer("\",})\r");
I3_send_packet();
return;
}
void I3_send_beep(CHAR_DATA *ch, const char *to, I3_MUD *mud)
{
if (!is_connected())
return;
I3_escape(to);
I3_write_header("beep", this_i3mud->name, CH_I3NAME(ch), mud->name, to);
I3_write_buffer("\"");
I3_write_buffer(CH_I3NAME(ch));
I3_write_buffer("\",})\r");
I3_send_packet();
return;
}
void I3_process_beep(I3_HEADER *header, char *s)
{
char buf[MAX_INPUT_LENGTH];
char *ps = s,
*next_ps;
CHAR_DATA *ch;
snprintf(buf, MAX_INPUT_LENGTH, "%s@%s", header->originator_username,
header->originator_mudname);
if (!(ch = I3_find_user(header->target_username))) {
if (!i3exists_player(header->target_username))
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"No such player.");
else
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"That player is offline.");
return;
}
if (I3PERM(ch) < I3PERM_MORT) {
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"No such player.");
return;
}
if (I3ISINVIS(ch) || i3ignoring(ch, buf)) {
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"That player is offline.");
return;
}
if (I3IS_SET(I3FLAG(ch), I3_BEEP)) {
snprintf(buf, MAX_INPUT_LENGTH, "%s is not accepting beeps.", CH_I3NAME(ch));
I3_send_error(header->originator_mudname, header->originator_username, "unk-user", buf);
return;
}
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
i3_printf(ch, "%%^YELLOW%%^\a%s@%s i3beeps you.%%^RESET%%^\r\n", ps, header->originator_mudname);
return;
}
void I3_send_tell(CHAR_DATA *ch, const char *to, const char *mud, const char *message)
{
if (!is_connected())
return;
I3_escape(to);
I3_write_header("tell", this_i3mud->name, CH_I3NAME(ch), mud, to);
I3_write_buffer("\"");
I3_write_buffer(CH_I3NAME(ch));
I3_write_buffer("\",\"");
send_to_i3(I3_escape(message));
I3_write_buffer("\",})\r");
I3_send_packet();
return;
}
void i3_update_tellhistory(CHAR_DATA *ch, const char *msg)
{
char new_msg[MAX_STRING_LENGTH];
time_t t = time(NULL);
struct tm *local = localtime(&t);
int x;
snprintf(new_msg, MAX_STRING_LENGTH, "%%^RED%%^%%^BOLD%%^[%-2.2d:%-2.2d] %s", local->tm_hour, local->tm_min,
msg);
for (x = 0; x < MAX_I3TELLHISTORY; x++) {
if (I3TELLHISTORY(ch, x) == '\0') {
I3TELLHISTORY(ch, x) = I3STRALLOC(new_msg);
break;
}
if (x == MAX_I3TELLHISTORY - 1) {
int i;
for (i = 1; i < MAX_I3TELLHISTORY; i++) {
I3STRFREE(I3TELLHISTORY(ch, i - 1));
I3TELLHISTORY(ch, i - 1) = I3STRALLOC(I3TELLHISTORY(ch, i));
}
I3STRFREE(I3TELLHISTORY(ch, x));
I3TELLHISTORY(ch, x) = I3STRALLOC(new_msg);
}
}
return;
}
void I3_process_tell(I3_HEADER *header, char *s)
{
char buf[MAX_INPUT_LENGTH],
usr[MAX_INPUT_LENGTH];
char *ps = s,
*next_ps;
CHAR_DATA *ch;
struct tm *local = localtime(&i3_time);
snprintf(buf, MAX_INPUT_LENGTH, "%s@%s", header->originator_username,
header->originator_mudname);
if (!(ch = I3_find_user(header->target_username))) {
if (!i3exists_player(header->target_username))
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"No such player.");
else
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"That player is offline.");
return;
}
if (I3PERM(ch) < I3PERM_MORT) {
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"No such player.");
return;
}
if (I3ISINVIS(ch) || i3ignoring(ch, buf)) {
I3_send_error(header->originator_mudname, header->originator_username, "unk-user",
"That player is offline.");
return;
}
if (I3IS_SET(I3FLAG(ch), I3_TELL)) {
snprintf(buf, MAX_INPUT_LENGTH, "%s is not accepting tells.", CH_I3NAME(ch));
I3_send_error(header->originator_mudname, header->originator_username, "unk-user", buf);
return;
}
if (CH_I3AFK(ch)) {
snprintf(buf, MAX_INPUT_LENGTH, "%s is currently AFK. Try back later.", CH_I3NAME(ch));
I3_send_error(header->originator_mudname, header->originator_username, "unk-user", buf);
return;
}
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(usr, MAX_INPUT_LENGTH, "%s@%s", ps, header->originator_mudname);
//snprintf(buf, MAX_INPUT_LENGTH, "'%s@%s'", ps, header->originator_mudname);
//snprintf(buf, MAX_INPUT_LENGTH, "%s@%s", header->originator_username, header->originator_mudname);
I3STRFREE(I3REPLYNAME(ch));
I3STRFREE(I3REPLYMUD(ch));
I3REPLYNAME(ch) = I3STRALLOC(header->originator_username);
I3REPLYMUD(ch) = I3STRALLOC(header->originator_mudname);
ps = next_ps;
I3_get_field(ps, &next_ps);
remove_quotes(&ps);
snprintf(buf, MAX_INPUT_LENGTH, "%s %%^CYAN%%^%%^BOLD%%^%s%%^RESET%%^ %%^YELLOW%%^i3tells you: %%^RESET%%^%s", color_time(local), usr, ps);
i3_printf(ch, "%s%%^RESET%%^\r\n", buf);
i3_update_tellhistory(ch, buf);
time_to_taunt = getTimestamp() + I3_TAUNT_DELAY;
return;
}
void I3_send_who(CHAR_DATA *ch, char *mud)
{
if (!is_connected())
return;
I3_escape(mud);
I3_write_header("who-req", this_i3mud->name, CH_I3NAME(ch), mud, NULL);
I3_write_buffer("})\r");
I3_send_packet();
return;
}
char *i3centerline(const char *string, int len)
{
char stripped[MAX_STRING_LENGTH];
static char outbuf[MAX_STRING_LENGTH];
int amount;
strlcpy(stripped, i3_strip_colors(string), MAX_STRING_LENGTH);
amount = len - strlen(stripped); /* Determine amount to put in front of line */
if (amount < 1)
amount = 1;
/*
* Justice, you are the String God!
*/
snprintf(outbuf, MAX_STRING_LENGTH, "%*s%s%*s", (amount / 2), "", string,
((amount / 2) * 2) == amount ? (amount / 2) : ((amount / 2) + 1), "");
return outbuf;
}
char *i3rankbuffer(CHAR_DATA *ch)
{
static char rbuf[MAX_INPUT_LENGTH];
if (I3PERM(ch) >= I3PERM_IMM) {
strlcpy(rbuf, "%^YELLOW%^Staff%^RESET%^", MAX_INPUT_LENGTH);
if (CH_I3RANK(ch) && CH_I3RANK(ch)[0] != '\0')
snprintf(rbuf, MAX_INPUT_LENGTH, "%%^YELLOW%%^%s%%^RESET%%^", I3_mudtag_to_i3tag(CH_I3RANK(ch)));
} else {
strlcpy(rbuf, "%^GREEN%^Player%^RESET%^", MAX_INPUT_LENGTH);
if (CH_I3RANK(ch) && CH_I3RANK(ch)[0] != '\0')
snprintf(rbuf, MAX_INPUT_LENGTH, "%%^GREEN%%^BOLD%%^%s%%^RESET%%^", I3_mudtag_to_i3tag(CH_I3RANK(ch)));
}
return rbuf;
}
void I3_write_who_line(const char *str, int idle, const char *xtra)
{
char tmp[MAX_STRING_LENGTH];
snprintf(tmp, MAX_STRING_LENGTH, "({\"%s\",%d,\"%s\"})", str, idle, xtra);
send_to_i3(I3_escape(tmp));
}
void new_I3_process_who_req(I3_HEADER *header, char *s)
{
struct descriptor_data *d = NULL;
struct char_data *person = NULL;
//int player_count = 0;
//int immortal_count = 0;
//int npc_count = 0;
char boottimebuf[MAX_INPUT_LENGTH];
//char uptimebuf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
//char nowtimebuf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
char ibuf[MAX_INPUT_LENGTH];
char tmp[MAX_INPUT_LENGTH];
char header_line_one[MAX_INPUT_LENGTH];
char header_line_two[MAX_INPUT_LENGTH];
strftime(boottimebuf, MAX_INPUT_LENGTH, RFC1123FMT, localtime((time_t *) &Uptime));
snprintf(ibuf, MAX_INPUT_LENGTH, "%s@%s", header->originator_username,
header->originator_mudname);
/*
* Proper who-reply format should be:
*
* ({
* (string) "who-req",
* (int) 5,
* (string) originator_mudname,
* (string) "0",
* (string) target_mudname,
* (string) target_username,
* (mixed *) who_data
* })
*
* and who_data should be an array with each row being:
*
* ({
* (string) user_visname
* (int) idle_time,
* (string) xtra_info
* })
*/
// Header line one
snprintf(tmp, MAX_INPUT_LENGTH, "%%^RED%%^%%^BOLD%%^-=[ %%^WHITE%%^%%^BOLD%%^Who's on %s %%^RED%%^%%^BOLD%%^]=-%%^RESET%%^", this_i3mud->name);
strlcpy(header_line_one, i3centerline(tmp, 78), MAX_INPUT_LENGTH);
// Header line two
snprintf(tmp, MAX_INPUT_LENGTH, "%%^YELLOW%%^-=[ %%^WHITE%%^%%^BOLD%%^telnet://%s:%d %%^YELLOW%%^]=-%%^RESET%%^", this_i3mud->telnet, this_i3mud->player_port);