/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Dystopia Mud improvements copyright (C) 2000, 2001 by Brian Graversen * * * * Much time and thought has gone into this software and you are * * benefitting. We hope that you share your changes too. What goes * * around, comes around. * ***************************************************************************/ /* * 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 args( ( DESCRIPTOR_DATA *d, char *txt, int length ) ); int mccp_mem_usage = 0; int mccp_mem_freed = 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) { /* memory tracking */ mccp_mem_usage += size; /* return memory pointer */ return calloc(items, size); } void zlib_free(void *opaque, void *address) { /* memory tracking */ mccp_mem_freed += sizeof(*address); /* free the cell */ 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; } bool compressEnd2(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); 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 */ /* logging the amount of data send */ increase_total_output(len); 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 */ bool 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 FALSE; } } /* Try to write out some data.. */ if (!processCompressed(desc)) return FALSE; /* loop */ } /* Done. */ return TRUE; } void do_compres( CHAR_DATA *ch, char *argument ) { send_to_char("If you want to use compress, spell it out.\n\r",ch); return; } /* 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); } } 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 = descriptor_list; d != NULL; d = d->next) { if (d->connected != CON_PLAYING) continue; if (d->character != NULL) gch = d->character; else continue; if (gch->level > 6) 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; }