/* * IMC2 - an inter-mud communications protocol * * imc-version.c: packet generation/interpretation for various protocol * versions * * Copyright (C) 1996 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) { static char buf[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; return buf; } /* printkeys: print key-value pairs, escaping values */ static const char *printkeys(const imc_data * data) { static char buf[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); } 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) { static char buf[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; return buf; } /* unescape1: reverse escape1 */ static const char *unescape1(const char *data) { static char buf[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; return buf; } /* escape0: version 0 escape (\n\r -> ~) */ static const char *escape0(const char *string) { static char buf[IMC_DATA_LENGTH]; char *p=buf; while (*string && (p-buf < IMC_DATA_LENGTH-1)) { if (*string == '\n' && *(string+1) == '\r') { *p++='~'; string++; if (*string) string++; } else *p++=*string++; } *p=0; return buf; } /* unescape0: version 0 unescape */ static const char *unescape0(const char *string) { static char buf[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; return buf; } static const char *generate0(const imc_internal * p) { static char temp[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 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 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 return NULL; return temp; } static const char *generate1(const imc_internal * p) { static char temp[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 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)); return temp; } static const char *generate2(const imc_internal * p) { static char temp[IMC_PACKET_LENGTH]; 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); sprintf(temp, "%s %lu %s %s %s %s", p->from, p->sequence, newpath, p->type, p->to, printkeys(&p->data)); 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; }