/*___________________________________________________________________________* )()( DalekenMUD 1.12 (C) 2000 )()( `][' by Martin Thomson, Lee Brooks, `][' || Ken Herbert and David Jacques || || ----------------------------------------------------------------- || || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, || || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse. || || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael || || Chastain, Michael Quan, and Mitchell Tse. || || Original Diku Mud copyright (C) 1990, 1991 || || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, || || Tom Madsen, and Katja Nyboe. || || ----------------------------------------------------------------- || || Any use of this software must follow the licenses of the || || creators. 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 || || The Mud Client Compression Protocol implementation. || *_/<>\_________________________________________________________________/<>\_*/ /* * mccp.c - support functions for mccp (the Mud Client Compression Protocol) * * see http://homepages.ihug.co.nz/~icecube/compress/ and README.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. */ /* * At the moment I have no way of having this compile with zlib on windows, * but the #ifdef's can't hurt much. */ #if defined( WIN32 ) # if defined( BCB ) # include <windows.h> # else # include <winsock.h> # endif # include <sys/timeb.h> /* for _ftime( ), uses _timeb struct */ #else # include <sys/time.h> # include <unistd.h> #endif #include <errno.h> #include <arpa/telnet.h> #include "mud.h" /* * This argument determines how much to compress data. It should be between * 1 and 9, 9 being the most compression, 1 is fastest. If you are concerned * more with processor usage, 6 is the default value used by zlib, this may * give some bonus to speed without a massive drop in compression. */ #define MCCP_LEVEL 9 const char compress_start_v1[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' }; const char compress_start_v2[] = { 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 compress_start( DESCRIPTOR_DATA *desc, int ver ) { 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, MCCP_LEVEL ) != 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; } if( ver != 2 ) write_to_descriptor( desc->descriptor, compress_start_v1, strlen( compress_start_v1 ) ); else write_to_descriptor( desc->descriptor, compress_start_v2, strlen( compress_start_v2 ) ); /* now we're compressing */ desc->out_compress = s; desc->mccp_version = ver; return TRUE; } /* Cleanly shut down compression on `desc' */ bool compress_end( DESCRIPTOR_DATA *desc, int ver ) { unsigned char dummy[1]; if( !desc->out_compress ) return TRUE; if( desc->mccp_version != ver ) return FALSE; 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( !write_to_descriptor( desc->descriptor, desc->out_compress_buf, desc->out_compress->next_out - desc->out_compress_buf ) ) 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; } /* * This code's purpose has changed to be that of converting the uncompressed * data in outbuf into compressed data that is stored in out_compress_buf. */ bool process_compressed( DESCRIPTOR_DATA *desc ) { z_stream *s = desc->out_compress; /* compress the data */ if( desc->large_buffer ) s->next_in = (unsigned char *)desc->large_buffer->str; else s->next_in = (unsigned char *)&desc->outbuf[0]; s->avail_in = desc->outtop; s->avail_out = COMPRESS_BUF_SIZE - ( s->next_out - desc->out_compress_buf ); while( s->avail_in > 0 && s->avail_out > 0 ) { if( deflate( s, Z_SYNC_FLUSH ) != Z_OK ) { bug( "Bad compression on desc %d.", desc->descriptor ); return FALSE; } s->avail_out = COMPRESS_BUF_SIZE - ( s->next_out - desc->out_compress_buf ); } /* if( s->next_out - desc->out_compress_buf > record ) { record = s->next_out - desc->out_compress_buf; fprintf( stderr, "Compress used %d bytes of the buffer.\n", record ); } */ /* copy any uncompressed data back */ if( desc->large_buffer ) { if( s->avail_in >= SMALL_OUTBUF_SIZE - 1 ) { memcpy( desc->large_buffer->str, desc->large_buffer->str + desc->outtop - s->avail_in, s->avail_in + 1 ); } else { if( s->avail_in > 0 ) memcpy( desc->outbuf, desc->large_buffer->str + desc->outtop - s->avail_in, s->avail_in + 1 ); free_text_block( desc->large_buffer ); desc->large_buffer = NULL; } } else if( s->avail_in > 0 ) memcpy( desc->outbuf, desc->outbuf + desc->outtop - s->avail_in, s->avail_in + 1 ); desc->outtop = s->avail_in; return TRUE; } /* User-level compression toggle */ void do_compress( CHAR_DATA *ch, const char *argument ) { if( !ch->desc ) return; if( argument[0] == '\0' ) { if( ch->desc->out_compress ) charprintf( ch, "Version %d compression is enabled.\n\r", ch->desc->mccp_version ); else send_to_char( "Compression is not currently enabled.\n\r", ch ); return; } if( !str_cmp( argument, "on" ) ) { if( !compress_start( ch->desc, ch->desc->mccp_version ) ) { send_to_char( "Failed, see help MCCP for details..\n\r", ch ); return; } charprintf( ch, "Ok, version %d compression enabled.\n\r", ch->desc->mccp_version ); } else if( !str_cmp( argument, "off" ) ) { if( !compress_end( ch->desc, ch->desc->mccp_version ) ) { send_to_char( "Failed.\n\r", ch ); return; } send_to_char( "Ok, compression disabled.\n\r", ch ); } else send_to_char( "Usage: compress [on|off]\n\r", ch ); }