/**************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefiting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************
* ROM 2.4 is copyright 1993-1998 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@hypercube.org) *
* Gabrielle Taylor (gtaylor@hypercube.org) *
* Brian Moore (zump@rom.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************
* 1stMUD ROM Derivative (c) 2001-2003 by Ryan Jennings *
* http://1stmud.dlmud.com/ <r-jenn@shaw.ca> *
***************************************************************************/
/*
* mccp.c - support functions for mccp (the Mud Client Compression Protocol)
*
* see http://homepages.ihug.co.nz/~icecube/compress/ and README.Rom24-mccp
*
* Copyright (c) 1999, Oliver Jowett <icecube@ihug.co.nz>.
*
* This code may be freely distributed and used if this copyright notice is
* retained intact.
*/
#include "merc.h"
#include "telnet.h"
#if !defined(NO_MCCP)
char compress2_start[] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, NUL };
char compress1_start[] = { IAC, SB, TELOPT_COMPRESS, IAC, SE, NUL };
PROTOTYPE(bool processCompressed, (DESCRIPTOR_DATA *));
#if defined(__FreeBSD__) && !defined(ENOSR)
#define ENOSR 63
#endif
/*
* Memory management - zlib uses these hooks to allocate and free memory
* it needs
*/
void *zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
return calloc(items, size);
}
void zlib_free(void *opaque, void *address)
{
free(address);
}
/*
* Begin compressing data on `desc'
*/
bool compressStart(DESCRIPTOR_DATA * desc, int version)
{
z_stream *s;
if (desc->out_compress) /* already compressing */
return TRUE;
/* allocate and init stream, buffer */
alloc_mem(s, z_stream, 1);
alloc_mem(desc->out_compress_buf, unsigned char, COMPRESS_BUF_SIZE);
s->next_in = NULL;
s->avail_in = 0;
s->next_out = desc->out_compress_buf;
s->avail_out = COMPRESS_BUF_SIZE;
s->zalloc = zlib_alloc;
s->zfree = zlib_free;
s->opaque = NULL;
if (deflateInit(s, 9) != Z_OK)
{
/* problems with zlib, try to clean up */
free_mem(desc->out_compress_buf);
free_mem(s);
return FALSE;
}
switch (version)
{
default:
case 1:
desc->mccp_version = 1;
d_write(desc, compress1_start, 0);
break;
case 2:
desc->mccp_version = 2;
d_write(desc, compress2_start, 0);
break;
}
/* now we're compressing */
desc->out_compress = s;
return TRUE;
}
/* Cleanly shut down compression on `desc' */
bool compressEnd(DESCRIPTOR_DATA * desc, int version)
{
unsigned char dummy[1];
if (!desc->out_compress)
return TRUE;
if (desc->mccp_version != version)
return TRUE;
desc->out_compress->avail_in = 0;
desc->out_compress->next_in = dummy;
/* No terminating signature is needed - receiver will get Z_STREAM_END */
if (deflate(desc->out_compress, Z_FINISH) != Z_STREAM_END)
return FALSE;
if (!processCompressed(desc)) /* try to send any residual data */
return FALSE;
deflateEnd(desc->out_compress);
free_mem(desc->out_compress_buf);
free_mem(desc->out_compress);
desc->out_compress = NULL;
desc->out_compress_buf = NULL;
return TRUE;
}
/* Try to send any pending compressed-but-not-sent data in `desc' */
bool processCompressed(DESCRIPTOR_DATA * desc)
{
int iStart, nBlock, nWrite, len;
if (!desc->out_compress)
return TRUE;
/* Try to write out some data.. */
len = desc->out_compress->next_out - desc->out_compress_buf;
if (len > 0)
{
/* we have some data to write */
desc->bytes_compressed += len;
for (iStart = 0; iStart < len; iStart += nWrite)
{
nBlock = UMIN(len - iStart, 4096);
if ((nWrite =
write(desc->descriptor,
desc->out_compress_buf + iStart, nBlock)) < 0)
{
#if !defined(WIN32)
if (errno == EAGAIN || errno == ENOSR)
#else
if (errno == EAGAIN)
#endif
break;
return FALSE; /* write error */
}
if (nWrite <= 0)
break;
}
if (iStart)
{
/* We wrote "iStart" bytes */
if (iStart < len)
memmove(desc->out_compress_buf,
desc->out_compress_buf + iStart, len - iStart);
desc->out_compress->next_out =
desc->out_compress_buf + len - iStart;
}
}
return TRUE;
}
/* d_write, the compressed case */
bool writeCompressed(DESCRIPTOR_DATA * desc, const char *txt, int length)
{
z_stream *s = desc->out_compress;
s->next_in = (unsigned char *) txt;
s->avail_in = length;
while (s->avail_in)
{
s->avail_out =
COMPRESS_BUF_SIZE - (s->next_out - desc->out_compress_buf);
if (s->avail_out)
{
int status = deflate(s, Z_SYNC_FLUSH);
if (status != Z_OK)
{
/* Boom */
return FALSE;
}
}
/* Try to write out some data.. */
if (!processCompressed(desc))
return FALSE;
/* loop */
}
/* Done. */
return TRUE;
}
#endif
/* User-level compression toggle */
CH_CMD(do_compress)
{
#if defined(NO_MCCP)
chprintln(ch, "MUD Compression is disabled.");
#else
if (!ch->desc)
{
chprint(ch, "What descriptor?!\n");
return;
}
if (IS_NULLSTR(argument))
{
if (ch->desc->out_compress)
{
double bcomp = ch->desc->bytes_compressed;
double bnorm = ch->desc->bytes_normal;
double percent = 100 - ((bcomp / bnorm) * 100);
chprintlnf(ch, "Bytes Normal : %ld", ch->desc->bytes_normal);
chprintlnf(ch, "Bytes Compressed: %ld", ch->desc->bytes_compressed);
chprintlnf(ch, "MCCP version : %d", ch->desc->mccp_version);
chprintlnf(ch, "Compression Rate: %2.2f%%", percent);
}
else
{
chprintlnf(ch, "Bytes Normal : %ld", ch->desc->bytes_normal);
chprintln(ch, "You are not compressing data.");
}
chprintln(ch, "Syntax: compress on/off");
return;
}
if (!str_cmp(argument, "on"))
{
if (!ch->desc->out_compress)
{
chprintln(ch,
"NOTE: If you did not have compression running after logging\n\r"
" in, chances are your client does not support it and you\n\r"
" will recieve strange output...");
if (!compressStart(ch->desc, ch->desc->mccp_version))
{
chprint(ch, "Failed.\n");
return;
}
chprintln(ch, "Ok, compression enabled.");
}
else
chprintln(ch, "You already have compression enabled.");
return;
}
if (!str_cmp(argument, "off"))
{
if (!compressEnd(ch->desc, ch->desc->mccp_version))
{
chprint(ch, "Failed.\n");
return;
}
chprint(ch, "Ok, compression disabled.\n");
return;
}
#endif
}