/*
* IMC2 - an inter-mud communications protocol
*
* imc-interp.c: packet interpretation code
*
* Copyright (C) 1996,1997 Oliver Jowett <oliver@sa-search.massey.ac.nz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING); if not, write to the
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include "imc.h"
/* rignore'd people */
imc_ignore_data *imc_ignore_list;
/* prefixes for all data files */
char *imc_prefix;
/* called when a keepalive has been received */
void imc_recv_keepalive(const char *from, const char *version)
{
imc_reminfo *p;
if (!strcasecmp(from, imc_name))
return;
/* this should never fail, imc.c should create an
* entry if one doesn't exist (in the path update
* code)
*/
p=imc_find_reminfo(from, 0);
if (!p) /* boggle */
return;
/* lower-level code has already updated p->alive */
if (strcasecmp(version, p->version)) /* remote version has changed? */
{
imc_strfree(p->version); /* if so, update it */
p->version=imc_strdup(version);
}
/* Only routers should ping - and even then, only directly connected muds */
if (imc_is_router && imc_getinfo(from))
{
struct timeval tv;
gettimeofday(&tv, NULL);
imc_send_ping(from, tv.tv_sec, tv.tv_usec);
}
}
/* called when a ping request is received */
void imc_recv_ping(const char *from, int time_s, int time_u)
{
/* ping 'em back */
imc_send_pingreply(from, time_s, time_u);
}
/* called when a ping reply is received */
void imc_recv_pingreply(const char *from, int time_s, int time_u)
{
imc_reminfo *p;
struct timeval tv;
p = imc_find_reminfo(from, 0); /* should always exist */
if (!p) /* boggle */
return;
gettimeofday(&tv, NULL); /* grab the exact time now and calc RTT */
p->ping = (tv.tv_sec - time_s) * 1000 + (tv.tv_usec - time_u) / 1000;
}
/* send a standard 'you are being ignored' rtell */
static void sendignore(const char *to)
{
char buf[IMC_DATA_LENGTH];
if (strcmp(imc_nameof(to), "*"))
{
sprintf(buf, "%s is ignoring you", imc_name);
imc_send_tell(NULL, to, buf, 1);
}
}
/* imc_char_data representation:
*
* All levels are "MAX_LEVEL-independent". 0 and up are mortal levels as
* usual. -1 is MAX_LEVEL. -2 is MAX_LEVEL-1, and so on. Conversion between
* imc_char_data and the mud's internal levels etc. is done in the interface
* file (eg. imc-rom.c or imc-envy.c)
*
* Invis/hidden state, and detecting them, use the IMC_INVIS and IMC_HIDDEN
* #defines.
*
* d->wizi is the wizi or incog level of the character (whichever is higher)
* d->invis is a bitvector representing the invis/hidden state of a character
* d->see is a bitvector representing detect invis/hidden for a character
* d->level is the level of the character (for the purposes of seeing
* wizi/incog)
* d->sex is currently unused, but will eventually be used for socials
*
* also checks rignores for a 'notrust' flag which makes that person a
* level 0 mortal for the purposes of wizi visibility checks, etc
*
* Default behavior is now: untrusted. If there's a trust flags, trusted.
* If there's also a notrust flag, untrusted.
*/
/* convert from the char data in 'p' to an internal representation in 'd' */
static void getdata(const imc_packet *p, imc_char_data *d)
{
int trust=0;
/* growl. why are mature people so hard to find these days? */
if (!strcasecmp(imc_mudof(p->from), imc_name))
trust=1;
else
{
if (imc_findignore(p->from, IMC_TRUST))
trust=1;
if (imc_findignore(p->from, IMC_NOTRUST))
trust=0;
}
strcpy(d->name, p->from);
d->invis = trust ? imc_getkeyi(&p->data, "invis", 0) : 0;
d->wizi = trust ? imc_getkeyi(&p->data, "wizi", 0) : 0;
d->see = trust ? imc_getkeyi(&p->data, "see", 0) : 0;
d->level = trust ? imc_getkeyi(&p->data, "level", 0) : 0;
#if 0
d->sex = imc_getkeyi(&p->data, "sex", 0);
#endif
}
/* convert back from 'd' to 'p' */
static void setdata(imc_packet *p, const imc_char_data *d)
{
imc_initdata(&p->data);
if (!d)
{
strcpy(p->from, "*");
imc_addkeyi(&p->data, "level", -1);
return;
}
strcpy(p->from, d->name);
if (d->invis)
imc_addkeyi(&p->data, "invis", d->invis);
if (d->wizi)
imc_addkeyi(&p->data, "wizi", d->wizi);
if (d->see)
imc_addkeyi(&p->data, "see", d->see);
if (d->level)
imc_addkeyi(&p->data, "level", d->level);
#if 0
if (d->sex)
imc_addkeyi(&p->data, "sex", d->sex);
#endif
}
/* handle a packet destined for us, or a broadcast */
void imc_recv(const imc_packet *p)
{
imc_char_data d;
getdata(p, &d);
/* chat: message to a channel (broadcast) */
if (!strcasecmp(p->type, "chat") && !imc_isignored(p->from))
imc_recv_chat(&d, imc_getkeyi(&p->data, "channel", 0),
imc_getkey(&p->data, "text", ""));
/* emote: emote to a channel (broadcast) */
else if (!strcasecmp(p->type, "emote") && !imc_isignored(p->from))
imc_recv_emote(&d, imc_getkeyi(&p->data, "channel", 0),
imc_getkey(&p->data, "text", ""));
/* tell: tell a player here something */
else if (!strcasecmp(p->type, "tell"))
if (imc_isignored(p->from))
sendignore(p->from);
else
imc_recv_tell(&d, p->to, imc_getkey(&p->data, "text", ""),
imc_getkeyi(&p->data, "isreply", 0));
/* who-reply: receive a who response */
else if (!strcasecmp(p->type, "who-reply"))
imc_recv_whoreply(p->to, imc_getkey(&p->data, "text", ""),
imc_getkeyi(&p->data, "sequence", -1));
/* who: receive a who request */
else if (!strcasecmp(p->type, "who"))
if (imc_isignored(p->from))
sendignore(p->from);
else
imc_recv_who(&d, imc_getkey(&p->data, "type", "who"));
/* whois-reply: receive a whois response */
else if (!strcasecmp(p->type, "whois-reply"))
imc_recv_whoisreply(p->to, imc_getkey(&p->data, "text", ""));
/* whois: receive a whois request */
else if (!strcasecmp(p->type, "whois"))
imc_recv_whois(&d, p->to);
/* beep: beep a player */
else if (!strcasecmp(p->type, "beep"))
if (imc_isignored(p->from))
sendignore(p->from);
else
imc_recv_beep(&d, p->to);
/* is-alive: receive a keepalive (broadcast) */
else if (!strcasecmp(p->type, "is-alive"))
imc_recv_keepalive(imc_mudof(p->from),
imc_getkey(&p->data, "versionid", "unknown"));
/* ping: receive a ping request */
else if (!strcasecmp(p->type, "ping"))
imc_recv_ping(imc_mudof(p->from), imc_getkeyi(&p->data, "time-s", 0),
imc_getkeyi(&p->data, "time-us", 0));
/* ping-reply: receive a ping reply */
else if (!strcasecmp(p->type, "ping-reply"))
imc_recv_pingreply(imc_mudof(p->from), imc_getkeyi(&p->data, "time-s", 0),
imc_getkeyi(&p->data, "time-us", 0));
/* mail: mail something to a local player */
else if (!strcasecmp(p->type, "mail"))
imc_recv_mail(imc_getkey(&p->data, "from", "error@hell"),
imc_getkey(&p->data, "to", "error@hell"),
imc_getkey(&p->data, "date", "(IMC error: bad date)"),
imc_getkey(&p->data, "subject", "no subject"),
imc_getkey(&p->data, "id", "bad_id"),
imc_getkey(&p->data, "text", ""));
/* mail-ok: remote confirmed that they got the mail ok */
else if (!strcasecmp(p->type, "mail-ok"))
imc_recv_mailok(p->from, imc_getkey(&p->data, "id", "bad_id"));
/* mail-reject: remote rejected our mail, bounce it */
else if (!strcasecmp(p->type, "mail-reject"))
imc_recv_mailrej(p->from, imc_getkey(&p->data, "id", "bad_id"),
imc_getkey(&p->data, "reason",
"(IMC error: no reason supplied"));
}
/* Commands called by the interface layer */
/* send a message out on a channel */
void imc_send_chat(const imc_char_data *from, int channel,
const char *argument, const char *to)
{
imc_packet out;
char tobuf[IMC_MNAME_LENGTH];
if (imc_active<IA_UP)
return;
setdata(&out, from);
strcpy(out.type, "chat");
strcpy(out.to, "*@*");
imc_addkey(&out.data, "text", argument);
imc_addkeyi(&out.data, "channel", channel);
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
while (tobuf[0])
{
if (!strcmp(tobuf, "*") || !strcasecmp(tobuf, imc_name) ||
imc_find_reminfo(tobuf, 0))
{
strcpy(out.to, "*@");
strcat(out.to, tobuf);
imc_send(&out);
}
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
}
imc_freedata(&out.data);
}
/* send an emote out on a channel */
void imc_send_emote(const imc_char_data *from, int channel,
const char *argument, const char *to)
{
imc_packet out;
char tobuf[IMC_MNAME_LENGTH];
if (imc_active<IA_UP)
return;
setdata(&out, from);
strcpy(out.type, "emote");
imc_addkeyi(&out.data, "channel", channel);
imc_addkey(&out.data, "text", argument);
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
while (tobuf[0])
{
if (!strcmp(tobuf, "*") || !strcasecmp(tobuf, imc_name) ||
imc_find_reminfo(tobuf, 0))
{
strcpy(out.to, "*@");
strcat(out.to, tobuf);
imc_send(&out);
}
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
}
imc_freedata(&out.data);
}
/* send a tell to a remote player */
void imc_send_tell(const imc_char_data *from, const char *to,
const char *argument, int isreply)
{
imc_packet out;
if (imc_active<IA_UP)
return;
if (!strcmp(imc_mudof(to), "*"))
return; /* don't let them do this */
setdata(&out, from);
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
strcpy(out.type, "tell");
imc_addkey(&out.data, "text", argument);
if (isreply)
imc_addkeyi(&out.data, "isreply", isreply);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a who-request to a remote mud */
void imc_send_who(const imc_char_data *from, const char *to, const char *type)
{
imc_packet out;
if (imc_active<IA_UP)
return;
if (!strcmp(imc_mudof(to), "*"))
return; /* don't let them do this */
setdata(&out, from);
sprintf(out.to, "*@%s", to);
strcpy(out.type, "who");
imc_addkey(&out.data, "type", type);
imc_send(&out);
imc_freedata(&out.data);
}
/* respond to a who request with the given data */
void imc_send_whoreply(const char *to, const char *data, int sequence)
{
imc_packet out;
if (imc_active<IA_UP)
return;
if (!strcmp(imc_mudof(to), "*"))
return; /* don't let them do this */
imc_initdata(&out.data);
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
strcpy(out.type, "who-reply");
strcpy(out.from, "*");
imc_addkey(&out.data, "text", data);
if (sequence!=-1)
imc_addkeyi(&out.data, "sequence", sequence);
imc_send(&out);
imc_freedata(&out.data);
}
/* special handling of whoreply construction for sequencing */
static char *wr_to;
static char *wr_buf;
static int wr_sequence;
void imc_whoreply_start(const char *to)
{
wr_sequence=0;
wr_to=imc_strdup(to);
wr_buf=imc_getsbuf(IMC_DATA_LENGTH);
}
void imc_whoreply_add(const char *text)
{
/* give a bit of a margin for error here */
if (strlen(wr_to) + strlen(text) >= IMC_DATA_LENGTH-500)
{
imc_send_whoreply(wr_to, wr_buf, wr_sequence);
wr_sequence++;
imc_sncpy(wr_buf, text, IMC_DATA_LENGTH);
return;
}
strcat(wr_buf, text);
}
void imc_whoreply_end(void)
{
imc_send_whoreply(wr_to, wr_buf, -(wr_sequence+1));
imc_strfree(wr_to);
wr_buf[0]=0;
imc_shrinksbuf(wr_buf);
}
/* send a whois-request to a remote mud */
void imc_send_whois(const imc_char_data *from, const char *to)
{
imc_packet out;
if (imc_active<IA_UP)
return;
if (strchr(to, '@'))
return;
setdata(&out, from);
sprintf(out.to, "%s@*", to);
strcpy(out.type, "whois");
imc_send(&out);
imc_freedata(&out.data);
}
/* respond with a whois-reply */
void imc_send_whoisreply(const char *to, const char *data)
{
imc_packet out;
if (imc_active<IA_UP)
return;
if (!strcmp(imc_mudof(to), "*"))
return; /* don't let them do this */
imc_initdata(&out.data);
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
strcpy(out.type, "whois-reply");
strcpy(out.from, "*");
imc_addkey(&out.data, "text", data);
imc_send(&out);
imc_freedata(&out.data);
}
/* beep a remote player */
void imc_send_beep(const imc_char_data *from, const char *to)
{
imc_packet out;
if (imc_active<IA_UP)
return;
if (!strcmp(imc_mudof(to), "*"))
return; /* don't let them do this */
setdata(&out, from);
strcpy(out.type, "beep");
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a keepalive to everyone */
void imc_send_keepalive(void)
{
imc_packet out;
if (imc_active<IA_UP)
return;
imc_initdata(&out.data);
strcpy(out.type, "is-alive");
strcpy(out.from, "*");
strcpy(out.to, "*@*");
imc_addkey(&out.data, "versionid", IMC_VERSIONID);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a ping with a given timestamp */
void imc_send_ping(const char *to, int time_s, int time_u)
{
imc_packet out;
if (imc_active<IA_UP)
return;
imc_initdata(&out.data);
strcpy(out.type, "ping");
strcpy(out.from, "*");
strcpy(out.to, "*@");
imc_sncpy(out.to+2, to, IMC_MNAME_LENGTH-2);
imc_addkeyi(&out.data, "time-s", time_s);
imc_addkeyi(&out.data, "time-us", time_u);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a pingreply with the given timestamp */
void imc_send_pingreply(const char *to, int time_s, int time_u)
{
imc_packet out;
if (imc_active<IA_UP)
return;
imc_initdata(&out.data);
strcpy(out.type, "ping-reply");
strcpy(out.from, "*");
strcpy(out.to, "*@");
imc_sncpy(out.to+2, to, IMC_MNAME_LENGTH-2);
imc_addkeyi(&out.data, "time-s", time_s);
imc_addkeyi(&out.data, "time-us", time_u);
imc_send(&out);
imc_freedata(&out.data);
}