eventmud/doc/
eventmud/help/
/*
 * 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 "mud.h"

#ifndef NOMCCP

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

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

/*
 * 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(D_SOCKET *dsock, unsigned char teleopt)
{
  z_stream *s;

  /* already compressing */
  if (dsock->out_compress)
    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)
    text_to_socket(dsock, (char *) enable_compress);
  else if (teleopt == TELOPT_COMPRESS2)
    text_to_socket(dsock, (char *) enable_compress2);
  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 `desc' */
bool compressEnd(D_SOCKET *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;
  dsock->top_output = 0;

  /* 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(D_SOCKET *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->control, 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;
}

#endif