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