/* * 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); }