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-version.c: packet generation/interpretation for various protocol
 *                versions
 *
 * 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 <stdio.h>
#include <ctype.h>
#include <string.h>

#define IMC_INTERNALS
#include "imc.h"

static const char *generate0(const imc_internal *);
static const char *generate1(const imc_internal *);
static const char *generate2(const imc_internal *);
static imc_internal *interpret0(const char *);
static imc_internal *interpret1(const char *);
static imc_internal *interpret2(const char *);

_imc_vinfo imc_vinfo[] =
{
  { 0, generate0, interpret0 },
  { 1, generate1, interpret1 },
  { 2, generate2, interpret2 }
};

/* escape2: escape " -> \", \ -> \\, CR -> \r, LF -> \n */

static const char *escape2(const char *data)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char *p;

  for (p=buf; *data && (p-buf < IMC_DATA_LENGTH-1); data++, p++)
  {
    if (*data == '\n')
    {
      *p++='\\';
      *p='n';
    }
    else if (*data == '\r')
    {
      *p++='\\';
      *p='r';
    }
    else if (*data == '\\')
    {
      *p++='\\';
      *p='\\';
    }
    else if (*data == '"')
    {
      *p++='\\';
      *p='"';
    }
    else
      *p=*data;
  }

  *p=0;

  imc_shrinksbuf(buf);
  return buf;
}

/* printkeys: print key-value pairs, escaping values */
static const char *printkeys(const imc_data * data)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char temp[IMC_DATA_LENGTH];
  int len=0;
  int i;

  buf[0]=0;

  for (i=0; i<IMC_MAX_KEYS; i++)
  {
    if (!data->key[i])
      continue;
    imc_sncpy(buf+len, data->key[i], IMC_DATA_LENGTH-len-1);
    strcat(buf, "=");
    len = strlen(buf);

    if (!strchr(data->value[i], ' '))
      imc_sncpy(temp, escape2(data->value[i]), IMC_DATA_LENGTH-1);
    else
    {
      temp[0]='"';
      imc_sncpy(temp+1, escape2(data->value[i]), IMC_DATA_LENGTH-3);
      strcat(temp, "\"");
    }

    strcat(temp, " ");
    imc_sncpy(buf+len, temp, IMC_DATA_LENGTH-len);
    len = strlen(buf);
  }

  imc_shrinksbuf(buf);
  return buf;
}

/* parsekeys: extract keys from string */

static void parsekeys(const char *string, imc_data * data)
{
  const char *p1;
  char *p2;
  char k[IMC_DATA_LENGTH], v[IMC_DATA_LENGTH];
  int quote;

  p1 = string;

  while (*p1)
  {
    while (*p1 && isspace(*p1))
      p1++;

    p2 = k;
    while (*p1 && *p1 != '=' && p2-k < IMC_DATA_LENGTH-1)
      *p2++=*p1++;
    *p2=0;

    if (!k[0] || !*p1)		/* no more keys? */
      break;

    p1++;			/* skip the '=' */

    if (*p1 == '"')
    {
      p1++;
      quote = 1;
    }
    else
      quote = 0;

    p2=v;
    while (*p1 && (!quote || *p1 != '"') && (quote || *p1 != ' ') &&
	   p2-v < IMC_DATA_LENGTH+1)
    {
      if (*p1 == '\\')
      {
	switch (*(++p1))
	{
	case '\\':
	  *p2++='\\';
	  break;
	case 'n':
	  *p2++='\n';
	  break;
	case 'r':
	  *p2++='\r';
	  break;
	case '"':
	  *p2++='"';
	  break;
	default:
	  *p2++=*p1;
	  break;
	}
	if (*p1)
	  p1++;
      }
      else
	*p2++=*p1++;
    }

    *p2=0;

    if (!v[0])
      continue;

    imc_addkey(data, k, v);

    if (quote && *p1)
      p1++;
  }
}

/* escape1: escape CR->\r, LF->\n, \->\\ */
static const char *escape1(const char *data)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char *p;

  for (p=buf; *data && (p-buf < IMC_DATA_LENGTH-1); data++, p++)
  {
    if (*data == '\n')
    {
      *p++='\\';
      *p='n';
    }
    else if (*data == '\r')
    {
      *p++='\\';
      *p='r';
    }
    else if (*data == '\\')
    {
      *p++='\\';
      *p='\\';
    }
    else
      *p=*data;
  }

  *p=0;

  imc_shrinksbuf(buf);
  return buf;
}

/* unescape1: reverse escape1 */
static const char *unescape1(const char *data)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char *p;
  char ch;

  for (p=buf; *data && (p-buf < IMC_DATA_LENGTH-1); data++, p++)
  {
    if (*data == '\\')
    {
      ch = *(++data);
      switch (ch)
      {
      case 'n':
	*p='\n';
	break;
      case 'r':
	*p='\r';
	break;
      case '\\':
	*p='\\';
	break;
      default:
	*p=ch;
	break;
      }
    }
    else
      *p=*data;
  }

  *p=0;

  imc_shrinksbuf(buf);
  return buf;
}

/* escape0: version 0 escape (\n\r -> ~, ~? -> nothing) */
static const char *escape0(const char *string)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char *p=buf;

  while (*string && (p-buf < IMC_DATA_LENGTH-1))
  {
    if (*string == '~')
    {
      string++;
      if (*string)
	string++;
    }
    else if (*string == '\n' && *(string+1) == '\r')
    {
      *p++='~';
      string++;
      if (*string)
	string++;
    }
    else
      *p++=*string++;
  }

  *p=0;

  imc_shrinksbuf(buf);
  return buf;
}

/* unescape0: version 0 unescape */

static const char *unescape0(const char *string)
{
  char *buf=imc_getsbuf(IMC_DATA_LENGTH);
  char *p=buf;

  while (*string && (p-buf < IMC_DATA_LENGTH-1))
  {
    if (*string == '~')
    {
      *p++='\n';
      *p++='\r';
    }
    else
      *p++=*string;

    string++;
  }

  *p=0;
  
  imc_shrinksbuf(buf);
  return buf;
}

static const char *generate0(const imc_internal * p)
{
  char *temp=imc_getsbuf(IMC_PACKET_LENGTH);
  char newpath[IMC_PATH_LENGTH];
  char fromname[IMC_NAME_LENGTH];

  if (!p->type[0] || !p->from[0] || !p->to[0])
  {
    imc_logerror("BUG: generate0: bad packet!");
    return NULL;		/* catch bad packets here */
  }

  if (!strcmp(imc_nameof(p->from), "*"))
    strcpy(fromname, imc_mudof(p->from));
  else
    strcpy(fromname, imc_nameof(p->from));

  if (!p->path[0])
    strcpy(newpath, imc_name);
  else
    sprintf(newpath, "%s!%s", p->path, imc_name);

  if (!strcasecmp(p->type, "chat"))
    if (imc_getkeyi(&p->data, "channel", 0) == 0)
      sprintf(temp, "MS %s %s %s %s",
	      imc_mudof(p->from), imc_nameof(p->from), newpath,
	      imc_getkey(&p->data, "text", ""));
    else
    {
      imc_shrinksbuf(temp);
      return NULL;
    }
  else if (!strcasecmp(p->type, "emote"))
    if (imc_getkeyi(&p->data, "channel", 0) == 0)
      sprintf(temp, "AC %s %s %s %s",
	      imc_mudof(p->from), imc_nameof(p->from), newpath,
	      imc_getkey(&p->data, "text", ""));
    else
    {
      imc_shrinksbuf(temp);
      return NULL;
    }
  else if (!strcasecmp(p->type, "tell"))
    sprintf(temp, "TE %s %s %s %s %s",
	    imc_mudof(p->from), fromname, newpath, p->to,
	    imc_getkey(&p->data, "text", ""));
  else if (!strcasecmp(p->type, "who-reply"))
    sprintf(temp, "PE %s %s %s %s %s",
	    imc_mudof(p->from), fromname, newpath,
	    p->to, escape0(imc_getkey(&p->data, "text", "")));
  else if (!strcasecmp(p->type, "who"))
    sprintf(temp, "WH %s %s %s",
	    imc_mudof(p->to), imc_nameof(p->from), newpath);
  else
  {
    imc_shrinksbuf(temp);
    return NULL;
  }

  imc_shrinksbuf(temp);
  return temp;
}

static const char *generate1(const imc_internal * p)
{
  char *temp=imc_getsbuf(IMC_PACKET_LENGTH);
  char newpath[IMC_PATH_LENGTH];

  if (!p->type[0] || !p->from[0] || !p->to[0])
  {
    imc_logerror("BUG: generate1: bad packet!");
    return NULL;		/* catch bad packets here */
  }

  if (!p->path[0])
    strcpy(newpath, imc_name);
  else
    sprintf(newpath, "%s!%s", p->path, imc_name);

  if (!strcasecmp(p->type, "chat") || !strcasecmp(p->type, "emote"))
    if (imc_getkeyi(&p->data, "channel", 0) == 0)
      sprintf(temp, "%s %ld %s %s %s %s",
	      p->from, p->sequence, newpath, p->type, p->to,
	      escape1(imc_getkey(&p->data, "text", "")));
    else
    {
      imc_shrinksbuf(temp);
      return NULL;
    }
  else if (!strcasecmp(p->type, "tell"))
    sprintf(temp, "%s %ld %s %s %s %s",
	    p->from, p->sequence, newpath, p->type, p->to,
	    escape1(imc_getkey(&p->data, "text", "")));
  else if (!strcasecmp(p->type, "who"))
    sprintf(temp, "%s %ld %s %s %s",
	    p->from, p->sequence, newpath, p->type, p->to);
  else if (!strcasecmp(p->type, "who-reply"))
    sprintf(temp, "%s %ld %s %s %s %s",
	    p->from, p->sequence, newpath, p->type, p->to,
	    escape1(imc_getkey(&p->data, "text", "")));
  else
    sprintf(temp, "%s %ld %s %s %s %s",
	    p->from, p->sequence, newpath, p->type, p->to,
	    printkeys(&p->data));

  imc_shrinksbuf(temp);
  return temp;
}

static const char *generate2(const imc_internal * p)
{
  char *temp;
  char newpath[IMC_PATH_LENGTH];

  if (!p->type[0] || !p->from[0] || !p->to[0])
  {
    imc_logerror("BUG: generate2: bad packet!");
    return NULL;		/* catch bad packets here */
  }

  if (!p->path[0])
    strcpy(newpath, imc_name);
  else
    sprintf(newpath, "%s!%s", p->path, imc_name);

  temp=imc_getsbuf(IMC_PACKET_LENGTH);
  sprintf(temp, "%s %lu %s %s %s %s",
	  p->from, p->sequence, newpath, p->type, p->to,
	  printkeys(&p->data));
  imc_shrinksbuf(temp);
  return temp;
}

static imc_internal *interpret0(const char *argument)
{
  static imc_internal out;
  char type[3];
  char arg1[IMC_NAME_LENGTH], arg2[IMC_NAME_LENGTH];
  char arg3[IMC_PATH_LENGTH];
  const char *orig=argument;

  out.sequence=imc_sequencenumber++;
  imc_initdata(&out.data);

  argument=imc_getarg(argument, type, 3);
  argument=imc_getarg(argument, arg1, IMC_NAME_LENGTH);
  argument=imc_getarg(argument, arg2, IMC_NAME_LENGTH);
  argument=imc_getarg(argument, arg3, IMC_PATH_LENGTH);

  if (!type[0] || !arg1[0] || !arg2[0] || !arg3[0])
  {
    imc_logerror("interpret0: bad packet received, discarding");
    return NULL;
  }

  if (!strcasecmp(type, "AC"))
  {
    strcpy(out.type, "emote");
    strcpy(out.to, "*@*");
    strcpy(out.from, imc_makename(arg2, arg1));
    strcpy(out.path, arg3);
    imc_addkey(&out.data, "text", argument);
    imc_addkeyi(&out.data, "channel", 0);
  }
  else if (!strcasecmp(type, "MS"))
  {
    strcpy(out.type, "chat");
    strcpy(out.to, "*@*");
    strcpy(out.from, imc_makename(arg2, arg1));
    strcpy(out.path, arg3);
    imc_addkey(&out.data, "text", argument);
    imc_addkeyi(&out.data, "channel", 0);
  }
  else if (!strcasecmp(type, "TE"))
  {
    strcpy(out.type, "tell");
    argument = imc_getarg(argument, out.to, IMC_NAME_LENGTH);
    if (!strcasecmp(arg2, arg1))	/* handle system messages */
      strcpy(out.from, imc_makename("*", arg1));
    else
      strcpy(out.from, imc_makename(arg2, arg1));
    strcpy(out.path, arg3);
    imc_addkey(&out.data, "text", argument);
  }
  else if (!strcasecmp(type, "PE"))
  {
    strcpy(out.type, "who-reply");
    argument = imc_getarg(argument, out.to, IMC_NAME_LENGTH);
    if (!strcasecmp(arg2, arg1))	/* handle system messages */
      strcpy(out.from, imc_makename("*", arg1));
    else
      strcpy(out.from, imc_makename(arg2, arg1));
    strcpy(out.path, arg3);
    imc_addkey(&out.data, "text", unescape0(argument));
  }
  else if (!strcasecmp(type, "WH"))
  {
    strcpy(out.type, "who");
    strcpy(out.to, imc_makename("*", arg1));
    strcpy(out.from, imc_makename(arg2, imc_lastinpath(arg3)));
    strcpy(out.path, arg3);
  }
  else if (!type[0] || !strcasecmp(type, "PW"))
    return NULL;
  else
  {
    imc_logerror("unknown version0 type: %s", orig);
    return NULL;
  }

  return &out;
}

static imc_internal *interpret1(const char *argument)
{
  char seq[20];
  static imc_internal out;

  imc_initdata(&out.data);

  argument=imc_getarg(argument, out.from, IMC_NAME_LENGTH);
  argument=imc_getarg(argument, seq, 20);
  argument=imc_getarg(argument, out.path, IMC_PATH_LENGTH);
  argument=imc_getarg(argument, out.type, IMC_TYPE_LENGTH);
  argument=imc_getarg(argument, out.to, IMC_NAME_LENGTH);

  if (!out.from[0] || !seq[0] || !out.path[0] || !out.type[0] || !out.to[0])
  {
    imc_logerror("interpret1: bad packet received, discarding");
    return NULL;
  }

  if (!strcasecmp(out.type, "who"))
    imc_addkey(&out.data, "level", "0");
  else if (!strcasecmp(out.type, "chat") || !strcasecmp(out.type, "emote"))
  {
    imc_addkey(&out.data, "text", unescape1(argument));
    imc_addkeyi(&out.data, "channel", 0);
  }
  else if (!strcasecmp(out.type, "tell"))
  {
    imc_addkey(&out.data, "text", unescape1(argument));
  }
  else if (!strcasecmp(out.type, "who-reply"))
    imc_addkey(&out.data, "text", unescape1(argument));
  else
    parsekeys(argument, &out.data);

  out.sequence = strtoul(seq, NULL, 10);
  return &out;
}

static imc_internal *interpret2(const char *argument)
{
  char seq[20];
  static imc_internal out;

  imc_initdata(&out.data);

  argument=imc_getarg(argument, out.from, IMC_NAME_LENGTH);
  argument=imc_getarg(argument, seq, 20);
  argument=imc_getarg(argument, out.path, IMC_PATH_LENGTH);
  argument=imc_getarg(argument, out.type, IMC_TYPE_LENGTH);
  argument=imc_getarg(argument, out.to, IMC_NAME_LENGTH);

  if (!out.from[0] || !seq[0] || !out.path[0] || !out.type[0] || !out.to[0])
  {
    imc_logerror("interpret2: bad packet received, discarding");
    return NULL;
  }

  parsekeys(argument, &out.data);

  out.sequence=strtoul(seq, NULL, 10);
  return &out;
}