1stMUD4.0/bin/
1stMUD4.0/doc/MPDocs/
1stMUD4.0/player/
1stMUD4.0/win32/
1stMUD4.0/win32/rom/
/**************************************************************************
*  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
}