/* * 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 <sys/types.h> #include <sys/time.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include "merc.h" #include "telnet.h" char compress_start[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' }; bool processCompressed(DESCRIPTOR_DATA *desc); bool write_to_descriptor( DESCRIPTOR_DATA *d, char *txt, int length ); /* * 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) { z_stream *s; if (desc->out_compress) /* already compressing */ return TRUE; /* allocate and init stream, buffer */ s = (z_stream *) alloc_mem(sizeof(*s)); desc->out_compress_buf = (unsigned char *) alloc_mem(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, COMPRESS_BUF_SIZE); free_mem(s, sizeof(z_stream)); return FALSE; } write_to_descriptor(desc, compress_start, strlen(compress_start)); /* now we're compressing */ desc->out_compress = s; return TRUE; } /* Cleanly shut down compression on `desc' */ bool compressEnd(DESCRIPTOR_DATA *desc) { unsigned char dummy[1]; if (!desc->out_compress) 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, COMPRESS_BUF_SIZE); free_mem(desc->out_compress, sizeof(z_stream)); 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 */ 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 (errno == EAGAIN || errno == ENOSR) 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; } /* write_to_descriptor, the compressed case */ int writeCompressed(DESCRIPTOR_DATA *desc, 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 0; } } /* Try to write out some data.. */ if (!processCompressed(desc)) return 0; /* loop */ } /* Done. */ return length; } /* User-level compression toggle */ void do_compress(CHAR_DATA *ch, char *argument) { if (!ch->desc) { send_to_char("What descriptor?!\n", ch); return; } if (!ch->desc->out_compress) { if (!compressStart(ch->desc)) { send_to_char("Failed.\n", ch); return; } send_to_char("Ok, compression enabled.\n", ch); } else { if (!compressEnd(ch->desc)) { send_to_char("Failed.\n", ch); return; } send_to_char("Ok, compression disabled.\n", ch); } }