ackmud/
ackmud/area/imc/
ackmud/npcs/a/
ackmud/npcs/c/
ackmud/npcs/d/
ackmud/npcs/e/
ackmud/npcs/f/
ackmud/npcs/h/
ackmud/npcs/i/
ackmud/npcs/k/
ackmud/npcs/l/
ackmud/npcs/n/
ackmud/npcs/o/
ackmud/npcs/p/
ackmud/npcs/r/
ackmud/npcs/s/
ackmud/npcs/w/
ackmud/player/c/
ackmud/player/s/
ackmud/player/z/
/*
 * 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);
}