/***************************************************************************
* 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;
}