legend/
legend/area/
legend/player/
/***************************************************************************
 *  God Wars Mud copyright (C) 1994, 1995, 1996 by Richard Woolcock        *
 *                                                                         *
 *  Legend of Chrystancia copyright (C) 1999, 2000, 2001 by Matthew Little *
 *  This mud is NOT to be copied in whole or in part, or to be run without *
 *  the permission of Matthew Little. Nobody else has permission to        *
 *  authorise the use of this code.                                        *
 ***************************************************************************/

/*
 * mccp.c - support functions for the Mud Client Compression Protocol
 *
 * see http://www.randomly.org/projects/MCCP/
 *
 * Copyright (c) 1999, Oliver Jowett <oliver@randomly.org>
 *
 * This code may be freely distributed and used if this copyright
 * notice is retained intact.
 */

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/telnet.h>
#include <arpa/inet.h>
#include <zlib.h>                 /* MCCP */
#include "merc.h"


/* local functions */
bool processCompressed (DESCRIPTOR_DATA * dsock);

char enable_compress[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' };
char enable_compress2[] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, '\0' };

/*
 * Memory management - zlib uses these hooks to allocate and free memory
 * it needs
 */
#if defined(__OpenBSD__) || defined(__FreeBSD__)
#define ENOSR 63
#endif

void *zlib_alloc (void *opaque, unsigned int items, unsigned int size)
{
   extern int nAllocPerm, sAllocPerm;
   nAllocPerm ++;
   sAllocPerm += items*size;
   return calloc (items, size);
}

void zlib_free (void *opaque, void *address)
{
   free (address);
}

/*
 * Begin compressing data on `desc'
 */
bool compressStart (DESCRIPTOR_DATA * dsock, unsigned char teleopt)
{
   z_stream *s;

   /* already compressing */
   if (dsock->compressing == TELOPT_COMPRESS
       || dsock->compressing == TELOPT_COMPRESS2)
      return TRUE;

   /* allocate and init stream, buffer */
   s = (z_stream *) malloc (sizeof (*s));
   dsock->out_compress_buf = (unsigned char *) malloc (COMPRESS_BUF_SIZE);

   s->next_in = NULL;
   s->avail_in = 0;
   s->next_out = dsock->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)
   {
      free (dsock->out_compress_buf);
      free (s);
      return FALSE;
   }

   /* version 1 or 2 support */
   if (teleopt == TELOPT_COMPRESS)
      write_to_descriptor (dsock, enable_compress, 0);
   else if (teleopt == TELOPT_COMPRESS2)
      write_to_descriptor (dsock, enable_compress2, 0);
   else
   {
      bug ("Bad teleoption %d passed", teleopt);
      free (dsock->out_compress_buf);
      free (s);
      return FALSE;
   }

   /* now we're compressing */
   dsock->compressing = teleopt;
   dsock->out_compress = s;

   /* success */
   return TRUE;
}

/* Cleanly shut down compression on `dsock' */
bool compressEnd (DESCRIPTOR_DATA * dsock, unsigned char teleopt, bool forced)
{
   unsigned char dummy[1];

   if (!dsock->out_compress)
      return TRUE;

   if (dsock->compressing != teleopt)
      return FALSE;

   dsock->out_compress->avail_in = 0;
   dsock->out_compress->next_in = dummy;

   /* No terminating signature is needed - receiver will get Z_STREAM_END */
   if (deflate (dsock->out_compress, Z_FINISH) != Z_STREAM_END && !forced)
      return FALSE;

   /* try to send any residual data */
   if (!processCompressed (dsock) && !forced)
      return FALSE;

   /* reset compression values */
   deflateEnd (dsock->out_compress);
   free (dsock->out_compress_buf);
   free (dsock->out_compress);
   dsock->compressing = 0;
   dsock->out_compress = NULL;
   dsock->out_compress_buf = NULL;

   /* success */
   return TRUE;
}

/* Try to send any pending compressed-but-not-sent data in `desc' */
bool processCompressed (DESCRIPTOR_DATA * dsock)
{
   int iStart, nBlock, nWrite, len;

   if (!dsock->out_compress)
      return TRUE;

   len = dsock->out_compress->next_out - dsock->out_compress_buf;
   if (len > 0)
   {
      for (iStart = 0; iStart < len; iStart += nWrite)
      {
	 nBlock = UMIN (len - iStart, 4096);
	 if ((nWrite =
	      write (dsock->descriptor, dsock->out_compress_buf + iStart,
		     nBlock)) < 0)
	 {
	    if (errno == EAGAIN || errno == ENOSR)
	       break;

	    /* write error */
	    return FALSE;
	 }
	 if (nWrite <= 0)
	    break;
      }

      if (iStart)
      {
	 if (iStart < len)
	    memmove (dsock->out_compress_buf,
		     dsock->out_compress_buf + iStart, len - iStart);

	 dsock->out_compress->next_out =
	    dsock->out_compress_buf + len - iStart;
      }
   }

   /* success */
   return TRUE;
}


void do_showcompress (CHAR_DATA * ch, char *argument)
{
   DESCRIPTOR_DATA *d;
   CHAR_DATA *gch;
   char buf[MAX_STRING_LENGTH];
   int count1 = 0;
   int count2 = 0;

   if (IS_NPC (ch))
      return;

   for (d = first_descriptor; d != NULL; d = d->next)
   {
      if (d->connected != CON_PLAYING)
	 continue;
      if (d->character != NULL)
	 gch = d->character;
      else
	 continue;
      if (gch->desc->out_compress)
      {
	 sprintf (buf, "%-15s uses mccp\n\r", gch->name);
	 count1++;
      }
      else
      {
	 sprintf (buf, "%-15s Does not use mccp.\n\r", gch->name);
	 count2++;
      }
      send_to_char (buf, ch);
   }
   sprintf (buf, "\n\r%d out of %d players uses mccp\n\r",
	    count1, count2 + count1);
   send_to_char (buf, ch);
   return;
}