Permalink
Fetching contributors…
Cannot retrieve contributors at this time
10525 lines (8984 sloc) 310 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 "multiclass.h"
#include "comm.h"
#include "constants.h"
#include "modify.h"
#include "interpreter.h"
#include "version.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
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_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_THISMUD = NULL;
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 i3timeout; /* Number of loops to wait before giving up on an
* initial router connection */
int i3justconnected = 0; // So we can say something for the logs.
//int justconnected_lag = PULSE_PER_SECOND * 5; // Don't start urlbot for a few seconds
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;
int timeout_ticks = 3000;
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 tics_since_last_message = TAUNT_DELAY;
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;
void i3_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_nuke_url_file(void);
//void i3_check_urls(void);
#define I3KEY( literal, field, value ) \
if( !strcasecmp( word, literal ) ) \
{ \
field = value; \
fMatch = TRUE; \
break; \
}
const char *perm_names[] = {
"Notset", "None", "Mort", "Imm", "Admin", "Imp"
};
/* creates a random number in interval [from;to] */
int i3number(int from, int to)
{
if (DEBUG > 3)
log_info("called %s with %d, %d", __PRETTY_FUNCTION__, from, to);
if (to - from + 1)
return ((random() % (to - from + 1)) + from);
else
return from;
}
/*******************************************
* String buffering and logging functions. *
******************************************/
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*
* Renamed so it can play itself system independent.
* Samson 10-12-03
*/
size_t i3strlcpy(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
/*
* Copy as many bytes as will fit
*/
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
}
while (--n != 0);
}
/*
* Not enough room in dst, add NUL and traverse rest of src
*/
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++);
}
return (s - src - 1); /* count does not include NUL */
}
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(initial dst) + strlen(src); if retval >= siz,
* truncation occurred.
*
* Renamed so it can play itself system independent.
* Samson 10-12-03
*/
size_t i3strlcat(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
size_t dlen;
/*
* Find the end of dst and adjust bytes left but don't go past end
*/
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return (dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return (dlen + (s - src)); /* count does not include NUL */
}
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;
}
/*
Original Code from SW:FotE 1.1
Reworked strrep function.
Fixed a few glaring errors. It also will not overrun the bounds of a string.
-- Xorith
*/
char *i3strrep(const char *src, const char *sch,
const char *rep)
{
int src_len = strlen(src),
sch_len = strlen(sch),
rep_len = strlen(rep),
src_p,
offset,
dest_p;
static char dest[MAX_STRING_LENGTH];
bool searching = FALSE;
dest[0] = '\0';
for (src_p = 0, dest_p = 0; src_p < src_len; src_p++, dest_p++) {
if (src[src_p] == sch[0]) {
searching = TRUE;
for (offset = 0; offset < sch_len; offset++)
if (src[src_p + offset] != sch[offset])
searching = FALSE;
if (searching) {
for (offset = 0; offset < rep_len; offset++, dest_p++) {
if (dest_p == (MAX_STRING_LENGTH - 1)) {
dest[dest_p] = '\0';
return dest;
}
#if 0
if (src[src_p - 1] == sch[0]) {
if (rep[0] == '\033') {
if (offset < sch_len) {
if (offset == 0)
dest[dest_p - 1] = sch[offset];
else
dest[dest_p] = sch[offset];
} else
offset = rep_len;
} else {
if (offset == 0)
dest[dest_p - 1] = rep[offset];
dest[dest_p] = rep[offset];
}
} else
#endif
dest[dest_p] = rep[offset];
}
src_p += sch_len - 1;
dest_p--;
searching = FALSE;
continue;
}
}
if (dest_p == (MAX_STRING_LENGTH - 1)) {
dest[dest_p] = '\0';
return dest;
}
dest[dest_p] = src[src_p];
}
dest[dest_p] = '\0';
return dest;
}
char *i3_strip_colors(const char *txt)
{
I3_COLOR *color;
static char tbuf[MAX_STRING_LENGTH];
i3strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
i3strlcpy(tbuf, i3strrep(tbuf, color->i3tag, ""), MAX_STRING_LENGTH);
#ifdef IMC
// for (color = first_i3_color; color; color = color->next)
// i3strlcpy(tbuf, i3strrep(tbuf, color->imctag, ""), MAX_STRING_LENGTH);
#endif
for (color = first_i3_color; color; color = color->next)
i3strlcpy(tbuf, i3strrep(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;
i3strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
i3strlcpy(tbuf, i3strrep(tbuf, color->mudtag, color->i3tag), MAX_STRING_LENGTH);
return tbuf;
}
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;
i3strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
i3strlcpy(tbuf, i3strrep(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)) {
i3strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
i3strlcpy(tbuf, i3strrep(tbuf, color->imctag, color->mudtag), MAX_STRING_LENGTH);
} else
i3strlcpy(tbuf, i3_strip_colors(txt), 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)) {
i3strlcpy(tbuf, txt, MAX_STRING_LENGTH);
for (color = first_i3_color; color; color = color->next)
i3strlcpy(tbuf, i3strrep(tbuf, color->i3tag, color->mudtag), MAX_STRING_LENGTH);
} else
i3strlcpy(tbuf, i3_strip_colors(txt), MAX_STRING_LENGTH);
return tbuf;
}
/********************************
* 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];
//#ifdef IMC
// char buf3[MAX_STRING_LENGTH];
//#endif
va_list args;
va_start(args, fmt);
vsnprintf(buf, MAX_STRING_LENGTH, fmt, args);
va_end(args);
i3strlcpy(buf2, I3_i3tag_to_mudtag(ch, buf), MAX_STRING_LENGTH);
//#ifdef IMC
// i3strlcpy(buf3, I3_imctag_to_mudtag(ch, buf2), MAX_STRING_LENGTH);
// cprintf(ch, "%s", buf3);
//#else
cprintf(ch, "%s", buf2);
//#endif
return;
}
/* Generic send_to_pager type function to send to the proper code for each codebase */
void i3send_to_pager(const char *txt, CHAR_DATA *ch)
{
char buf[MAX_STRING_LENGTH];
#ifdef IMC
// char buf2[MAX_STRING_LENGTH];
#endif
i3strlcpy(buf, I3_i3tag_to_mudtag(ch, txt), MAX_STRING_LENGTH);
#ifdef IMC
// i3strlcpy(buf2, I3_imctag_to_mudtag(ch, buf), MAX_STRING_LENGTH);
// page_printf(ch, "%s\033[0m", buf2);
#else
page_printf(ch, "%s", buf);
#endif
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);
vsnprintf(buf, MAX_STRING_LENGTH, fmt, args);
va_end(args);
i3strlcpy(buf2, I3_i3tag_to_mudtag(ch, buf), MAX_STRING_LENGTH);
i3send_to_pager(buf2, ch);
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
i3strlcpy(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])
i3strlcat(buf, " ", MAX_STRING_LENGTH);
i3strlcat(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;
}
/* Fixed this function yet again. If the socket is negative or 0, then it will return
* a FALSE. Used to just check to see if the socket was positive, and that just wasn't
* working for the way some places checked for this. Any negative value is an indication
* that the socket never existed.
*/
bool i3_is_connected(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 i3_is_really_connected(void)
{
if( ! connected_at )
return FALSE;
return i3_is_connected();
}
/*
* 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;
}
/*
* Remove "'s at begin/end of string
* If a character is prefixed by \'s it also will be unescaped
*/
void I3_remove_quotes(char **ps)
{
char *ps1,
*ps2;
if (*ps[0] == '"')
(*ps)++;
if ((*ps)[strlen(*ps) - 1] == '"')
(*ps)[strlen(*ps) - 1] = 0;
ps1 = ps2 = *ps;
while (ps2[0]) {
if (ps2[0] == '\\') {
ps2++;
}
ps1[0] = ps2[0];
ps1++;
ps2++;
}
ps1[0] = '\0';
}
/*
* Adds "'s at begin/end of string.
* Also escapes any " or ' characters inside the string.
*/
char *I3_add_quotes(char *s)
{
static char buf[MAX_STRING_LENGTH];
char *ps = buf;
int i;
if(s[0] != '"') {
*ps++ = '"';
}
for(i = 0; i < strlen(s); i++) {
switch( s[i] ) {
case '"':
case '\'':
*ps++ = '\\';
*ps++ = s[i];
break;
default:
*ps++ = s[i];
break;
}
}
if(s[strlen(s)-1] != '"') {
*ps++ = '"';
}
*ps = '\0';
return buf;
}
/* 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;
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) {
i3_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';
i3strlcpy(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';
i3strlcpy(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.");
i3strlcpy(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;
}
i3strlcpy(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;
check = send(I3_socket, msg, oldsize + 4, 0);
#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:
//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", errno );
I3_connection_close(TRUE);
return FALSE;
}
}
if (packetdebug) {
log_info("Size: %d. Bytes Sent: %d.", oldsize, check);
log_info("Packet Sent: %s", msg + 4);
}
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;
if (!i3_is_connected())
return;
I3_output_pointer = 4;
I3_output_buffer[0] = '\0';
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,
*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); /* Just checking for now */
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;
break;
}
}
i3wait = 0;
i3timeout = 0;
i3justconnected = 1;
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->local_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);
I3_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 (!i3_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);
I3_remove_quotes(&ps);
i3strlcpy(type, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
/*
* Since VargonMUD likes to spew errors for no good reason....
*/
if (!strcasecmp(header->originator_mudname, "VargonMUD"))
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 (!i3_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);
I3_remove_quotes(&ps);
i3strlcpy(username, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_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 (!i3_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);
I3_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);
I3_remove_quotes(&ps);
i3strlcpy(username, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_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];
I3_get_field(ps, &next_ps);
this_i3mud->mudlist_id = atoi(ps);
I3_save_id();
ps = next_ps;
ps += 2;
while (1) {
char *next_ps2;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(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);
I3_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);
I3_remove_quotes(&ps);
I3STRFREE(mud->mudlib);
mud->mudlib = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
I3STRFREE(mud->base_mudlib);
mud->base_mudlib = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
I3STRFREE(mud->driver);
mud->driver = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
I3STRFREE(mud->mud_type);
mud->mud_type = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
I3STRFREE(mud->open_status);
mud->open_status = I3STRALLOC(ps);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_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);
I3_remove_quotes(&ps);
i3strlcpy(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")) {
I3_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];
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();
ps = next_ps;
ps += 2;
while (1) {
char *next_ps2;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(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);
I3_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 (!i3_is_connected())
return;
i3strlcpy(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 (!i3_is_connected())
return;
if (strstr(message, "$N") == NULL)
snprintf(buf, MAX_STRING_LENGTH, "$N %s", message);
else
i3strlcpy(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 (!i3_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[50];
char *p;
switch (type) {
default:
code[0] = type;
code[1] = '\0';
return 1;
case '$':
i3strlcpy(code, "$", 50);
break;
case ' ':
i3strlcpy(code, " ", 50);
break;
case 'N': /* Originator's name */
i3strlcpy(code, oname, 50);
break;
case 'O': /* Target's name */
i3strlcpy(code, tname, 50);
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;
}
i3strlcpy(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)) {
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)) {
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);
I3_remove_quotes(&ps);
i3strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(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);
I3_remove_quotes(&ps);
i3strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_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);
I3_remove_quotes(&ps);
i3strlcpy(targetmud, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(targetuser, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
snprintf(message_o, MAX_STRING_LENGTH, "%s%s", ps, " (filtered T)");
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
snprintf(message_t, MAX_STRING_LENGTH, "%s%s", ps, " (filtered T)");
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(visname_o, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_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;
I3_get_field(ps, &next_ps);
I3_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)
return;
ps = next_ps;
ps += 2;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(ptype, ps, MAX_INPUT_LENGTH);
second_header = I3_get_header(&ps);
I3_get_field(ps, &next_ps);
I3_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"
void allchan_log( int is_emote, char *channel, char *speaker, 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, 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;
}
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);
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("I3_process_channel_t: received unknown channel (%s)", ps);
return;
}
if (!channel->local_name)
return;
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(targetmud, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(targetuser, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(message_o, ps, MAX_STRING_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(message_t, ps, MAX_STRING_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
i3strlcpy(visname_o, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_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);
for (d = first_descriptor; d; d = d->next) {
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (!I3_hasname(I3LISTEN(vch), channel->local_name)
|| I3_hasname(I3DENY(vch), channel->local_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->local_name, tmsg);
i3_printf(vch, "%s%%^RESET%%^\r\n", buf);
} else {
sprintf(buf, layout, color_time(local), channel->local_name, omsg);
i3_printf(vch, "%s%%^RESET%%^\r\n", buf);
}
}
}
update_chanhistory(channel, omsg);
tics_since_last_message = 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;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("channel_m: received unknown channel (%s)", ps);
return;
}
if (!channel->local_name)
return;
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);
I3_remove_quotes(&ps);
i3strlcpy(visname, ps, MAX_INPUT_LENGTH);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
/* Try to squash multiple trailing newlines in the packet message */
i3strlcpy(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(buf, MAX_STRING_LENGTH, format, color_time(local), channel->local_name, visname, header->originator_mudname, tps);
snprintf(tmpvisname, MAX_INPUT_LENGTH, "%s@%s", visname, header->originator_mudname);
allchan_log(0, channel->local_name, visname, header->originator_mudname, tps);
for (d = first_descriptor; d; d = d->next) {
vch = d->original ? d->original : d->character;
if (!vch)
continue;
if (!I3_hasname(I3LISTEN(vch), channel->local_name)
|| I3_hasname(I3DENY(vch), channel->local_name))
continue;
if (d->connected == CON_PLAYING) {
if (i3ignoring(vch, visname))
continue;
if (i3ignoring(vch, tmpvisname))
continue;
i3_printf(vch, "%s%%^RESET%%^\r\n", buf);
}
}
update_chanhistory(channel, buf);
tics_since_last_message = 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;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
if (!(channel = find_I3_channel_by_name(ps))) {
log_info("channel_e: received unknown channel (%s)", ps);
return;
}
if (!channel->local_name)
return;
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
snprintf(visname, MAX_INPUT_LENGTH, "%s@%s", ps, header->originator_mudname);
ps = next_ps;
I3_get_field(ps, &next_ps);
I3_remove_quotes(&ps);
/* Try to squash multiple trailing newlines in the packet message */
i3strlcpy(tps, ps, MAX_STRING_LENGTH);
len = strlen(tps);
while(ISNEWL(tps[len-1]) && len > 1) {
tps[len-1] = '\0';
len--;
}
snprintf(msg, MAX_STRING_LENGTH, "%s", I3_convert_channel_message(tps, visname, visname));
//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->local_name, msg);
strcpy(format, "%s ");
strcat(format, channel->layout_e);
snprintf(buf, MAX_STRING_LENGTH, format, color_time(local), channel->local_name, msg);
snprintf(tmpvisname, MAX_INPUT_LENGTH, "%s@%s", visname, header->originator_mudname);
allchan_log(1, channel->local_name, visname, 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->local_name)
|| I3_hasname(I3DENY(vch), channel->local_name))
continue;
if (d->connected == CON_PLAYING) {
if (i3ignoring(vch, visname))
continue;
if (i3ignoring(vch, tmpvisname))
continue;
i3_printf(vch, "%s%%^RESET%%^\r\n", buf);
}
}
update_chanhistory(channel, buf);
tics_since_last_message = 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);
I3_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);
I3_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);
I3_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 (!i3_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 (!i3_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);
I3_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 (!i3_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);
I3_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);
I3_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);
tics_since_last_message = TAUNT_DELAY;
return;
}
void I3_send_who(CHAR_DATA *ch, char *mud)
{
if (!i3_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[300];
static char outbuf[400];
int amount;
i3strlcpy(stripped, i3_strip_colors(string), 300);
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, 400, "%*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) {
i3strlcpy(rbuf, "~YStaff", MAX_INPUT_LENGTH);
if (CH_I3RANK(ch) && CH_I3RANK(ch)[0] != '\0')
snprintf(rbuf, MAX_INPUT_LENGTH, "~Y%s", I3_mudtag_to_i3tag(CH_I3RANK(ch)));
} else {
i3strlcpy(rbuf, "~BPlayer", MAX_INPUT_LENGTH);
if (CH_I3RANK(ch) && CH_I3RANK(ch)[0] != '\0')
snprintf(rbuf, MAX_INPUT_LENGTH, "~B%s", I3_mudtag_to_i3tag(CH_I3RANK(ch)));
}
return rbuf;
}
void I3_process_who_req(I3_HEADER *header, char *s)
{
DESCRIPTOR_DATA *d;
CHAR_DATA *person;
char ibuf[MAX_INPUT_LENGTH],
personbuf[MAX_STRING_LENGTH],
tailbuf[MAX_STRING_LENGTH];