initial client-only MCCP2 support
diff --git a/Makefile b/Makefile
index 83978da..5b59f9a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,2 +1,5 @@
+CFLAGS = -Wall -g -O0 -DHAVE_ZLIB
+LFLAGS = -lz
+
 telnet-proxy: telnet-proxy.c libtelnet.c libtelnet.h
-	$(CC) -o telnet-proxy -Wall telnet-proxy.c libtelnet.c
+	$(CC) -o telnet-proxy $(CFLAGS) telnet-proxy.c libtelnet.c $(LFLAGS)
diff --git a/libtelnet.c b/libtelnet.c
index 3921b16..37b7b9b 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -1,4 +1,7 @@
 /*
+ * Sean Middleditch
+ * sean@sourcemud.org
+ *
  * The author or authors of this code dedicate any and all copyright interest
  * in this code to the public domain. We make this dedication for the benefit
  * of the public at large and to the detriment of our heirs and successors. We
@@ -7,6 +10,12 @@
  */
 
 #include <malloc.h>
+#include <string.h>
+
+#ifdef HAVE_ZLIB
+#include "zlib.h"
+#endif
+
 #include "libtelnet.h"
 
 /* buffer sizes */
@@ -22,20 +31,25 @@
 
 /* initialize a telnet state tracker */
 void libtelnet_init(struct libtelnet_t *telnet) {
-	telnet->state = LIBTELNET_STATE_TEXT;
-	telnet->buffer = 0;
-	telnet->size = 0;
-	telnet->length = 0;
+	memset(telnet, 0, sizeof(struct libtelnet_t));
 }
 
 /* free up any memory allocated by a state tracker */
 void libtelnet_free(struct libtelnet_t *telnet) {
+	/* free sub-request buffer */
 	if (telnet->buffer != 0) {
 		free(telnet->buffer);
 		telnet->buffer = 0;
 		telnet->size = 0;
 		telnet->length = 0;
 	}
+
+	/* free zlib box */
+	if (telnet->zlib != 0) {
+		inflateEnd(telnet->zlib);
+		free(telnet->zlib);
+		telnet->zlib = 0;
+	}
 }
 
 /* push a byte into the telnet buffer */
@@ -78,8 +92,7 @@
 	return LIBTELNET_ERROR_OK;
 }
 
-/* push a single byte into the state tracker */
-void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
+static void _process(struct libtelnet_t *telnet, unsigned char *buffer,
 		unsigned int size, void *user_data) {
 	unsigned char byte;
 	unsigned int i, start;
@@ -87,7 +100,7 @@
 		byte = buffer[i];
 		switch (telnet->state) {
 		/* regular data */
-		case LIBTELNET_STATE_TEXT:
+		case LIBTELNET_STATE_DATA:
 			/* on an IAC byte, pass through all pending bytes and
 			 * switch states */
 			if (byte == LIBTELNET_IAC) {
@@ -122,13 +135,13 @@
 			case LIBTELNET_IAC:
 				libtelnet_data_cb(telnet, &byte, 1, user_data);
 				start = i + 1;
-				telnet->state = LIBTELNET_STATE_TEXT;
+				telnet->state = LIBTELNET_STATE_DATA;
 				break;
 			/* some other command */
 			default:
 				libtelnet_command_cb(telnet, byte, user_data);
 				start = i + 1;
-				telnet->state = LIBTELNET_STATE_TEXT;
+				telnet->state = LIBTELNET_STATE_DATA;
 			}
 			break;
 
@@ -136,22 +149,22 @@
 		case LIBTELNET_STATE_DO:
 			libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
 			start = i + 1;
-			telnet->state = LIBTELNET_STATE_TEXT;
+			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 		case LIBTELNET_STATE_DONT:
 			libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
 			start = i + 1;
-			telnet->state = LIBTELNET_STATE_TEXT;
+			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 		case LIBTELNET_STATE_WILL:
 			libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
 			start = i + 1;
-			telnet->state = LIBTELNET_STATE_TEXT;
+			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 		case LIBTELNET_STATE_WONT:
 			libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
 			start = i + 1;
-			telnet->state = LIBTELNET_STATE_TEXT;
+			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 
 		/* subrequest -- buffer bytes until end request */
@@ -163,7 +176,7 @@
 			} else if (_buffer_byte(telnet, byte, user_data) !=
 					LIBTELNET_ERROR_OK) {
 				start = i + 1;
-				telnet->state = LIBTELNET_STATE_TEXT;
+				telnet->state = LIBTELNET_STATE_DATA;
 			} else {
 				telnet->state = LIBTELNET_STATE_SB;
 			}
@@ -174,20 +187,51 @@
 			switch (byte) {
 			/* end subrequest */
 			case LIBTELNET_SE:
+				/* return to default state */
+				start = i + 1;
+				telnet->state = LIBTELNET_STATE_DATA;
+
 				/* zero-size buffer is a protocol error */
 				if (telnet->length == 0) {
 					libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
 						user_data);
-				/* process */
-				} else {
-					libtelnet_subrequest_cb(telnet, telnet->buffer[0],
-						telnet->buffer + 1, telnet->length - 1, user_data);
-					telnet->length = 0;
+					break;
 				}
-				
-				/* return to default state */
-				start = i + 1;
-				telnet->state = LIBTELNET_STATE_TEXT;
+
+				/* invoke callback */
+				libtelnet_subrequest_cb(telnet, telnet->buffer[0],
+					telnet->buffer + 1, telnet->length - 1, user_data);
+				telnet->length = 0;
+
+				/* if this was MCCP2, enable compression stream */
+				if (telnet->buffer[0] == LIBTELNET_OPTION_COMPRESS2) {
+					/* allocate zstream box */
+					if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
+							== 0) {
+						libtelnet_error_cb(telnet,
+							LIBTELNET_ERROR_NOMEM, user_data);
+					}
+
+					/* initialize */
+					memset(telnet->zlib, 0, sizeof(z_stream));
+					if (inflateInit(telnet->zlib) != Z_OK) {
+						free(telnet->zlib);
+						telnet->zlib = 0;
+						libtelnet_error_cb(telnet,
+							LIBTELNET_ERROR_UNKNOWN, user_data);
+						break;
+					}
+
+					/* any remaining bytes in the buffer are compressed.
+					 * we have to re-invoke libtelnet_push to get those
+					 * bytes inflated and abort trying to process the
+					 * remaining compressed bytes in the current _process
+					 * buffer argument
+					 */
+					libtelnet_push(telnet, &buffer[i + 1], size - i - 1,
+							user_data);
+					return;
+				}
 				break;
 			/* escaped IAC byte */
 			case LIBTELNET_IAC:
@@ -195,7 +239,7 @@
 				if (_buffer_byte(telnet, LIBTELNET_IAC, user_data) !=
 						LIBTELNET_ERROR_OK) {
 					start = i + 1;
-					telnet->state = LIBTELNET_STATE_TEXT;
+					telnet->state = LIBTELNET_STATE_DATA;
 				} else {
 					telnet->state = LIBTELNET_STATE_SB;
 				}
@@ -205,7 +249,7 @@
 				libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
 						user_data);
 				start = i + 1;
-				telnet->state = LIBTELNET_STATE_TEXT;
+				telnet->state = LIBTELNET_STATE_DATA;
 				break;
 			}
 			break;
@@ -217,6 +261,56 @@
 		libtelnet_data_cb(telnet, &buffer[start], i - start, user_data);
 }
 
+/* push a bytes into the state tracker */
+void libtelnet_push(struct libtelnet_t *telnet, unsigned char *buffer,
+		unsigned int size, void *user_data) {
+	/* if COMPRESS2 has been negotiated and we have a zlib box, we
+	 * need to inflate the buffer before processing
+	 */
+	if (telnet->zlib) {
+		unsigned char inflate_buffer[4096];
+		int rs;
+
+		/* initialize zlib state */
+		telnet->zlib->next_in = buffer;
+		telnet->zlib->avail_in = size;
+		telnet->zlib->next_out = inflate_buffer;
+		telnet->zlib->avail_out = sizeof(inflate_buffer);
+
+		/* inflate until buffer exhausted and all output is produced */
+		while (telnet->zlib->avail_in > 0 || telnet->zlib->avail_out == 0) {
+			/* reset output buffer */
+
+			/* decompress */
+			rs = inflate(telnet->zlib, Z_SYNC_FLUSH);
+
+			/* process the decompressed bytes on success */
+			if (rs == Z_OK || rs == Z_STREAM_END)
+				_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
+						telnet->zlib->avail_out, user_data);
+			else
+				libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+						user_data);
+
+			/* prepare output buffer for next run */
+			telnet->zlib->next_out = inflate_buffer;
+			telnet->zlib->avail_out = sizeof(inflate_buffer);
+
+			/* on error (or on end of stream) disable further inflation */
+			if (rs != Z_OK) {
+				inflateEnd(telnet->zlib);
+				free(telnet->zlib);
+				telnet->zlib = 0;
+				break;
+			}
+		}
+
+	/* COMPRESS2 is not negotiated, just ignore */
+	} else {
+		_process(telnet, buffer, size, user_data);
+	}
+}
+
 /* send an iac command */
 void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
 		void *user_data) {
diff --git a/libtelnet.h b/libtelnet.h
index 16e16f0..1ad8e3d 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -1,4 +1,7 @@
 /*
+ * Sean Middleditch
+ * sean@sourcemud.org
+ *
  * The author or authors of this code dedicate any and all copyright interest
  * in this code to the public domain. We make this dedication for the benefit
  * of the public at large and to the detriment of our heirs and successors. We
@@ -22,11 +25,12 @@
 #define LIBTELNET_OPTION_BINARY 0
 #define LIBTELNET_OPTION_ECHO 1
 #define LIBTELNET_OPTION_NAWS 31
+#define LIBTELNET_OPTION_COMPRESS2 86
 #define LIBTELNET_OPTION_ZMP 93
 
 /* telnet states */
 enum libtelnet_state_t {
-	LIBTELNET_STATE_TEXT = 0,
+	LIBTELNET_STATE_DATA = 0,
 	LIBTELNET_STATE_IAC,
 	LIBTELNET_STATE_DO,
 	LIBTELNET_STATE_DONT,
@@ -47,6 +51,10 @@
 
 /* state tracker */
 struct libtelnet_t {
+	/* zlib (mccp2) compression */
+#ifdef HAVE_ZLIB
+	z_stream *zlib;
+#endif
 	/* sub-request buffer */
 	unsigned char *buffer;
 	/* current size of the buffer */
diff --git a/telnet-proxy.c b/telnet-proxy.c
index 2f1904f..9a38af6 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -20,6 +20,10 @@
 #include <ctype.h>
 #include <unistd.h>
 
+#ifdef HAVE_ZLIB
+#include "zlib.h"
+#endif
+
 #include "libtelnet.h"
 
 struct conn_t {
@@ -177,6 +181,15 @@
 	printf("%s IAC %s %d (%s)\e[0m\n", conn->name, get_cmd(cmd),
 			(int)opt, get_opt(opt));
 
+	/* FIXME: HACK: allow MCCP2 from server without passing it
+	 * through to client.  this is temporary until libtelnet supports
+	 * the server-end of MCCP2.
+	 */
+	if (cmd == LIBTELNET_WILL && opt == LIBTELNET_OPTION_COMPRESS2) {
+		libtelnet_send_negotiate(&conn->telnet, LIBTELNET_DO, opt, conn);
+		return;
+	}
+
 	libtelnet_send_negotiate(&conn->remote->telnet, cmd, opt,
 			conn->remote);
 }
@@ -201,6 +214,7 @@
 	struct conn_t *conn = (struct conn_t*)user_data;
 
 	printf("%s ERROR: %d\e[0m\n", conn->name, (int)error);
+	exit(1);
 }
 
 int main(int argc, char **argv) {
@@ -245,6 +259,7 @@
 		fprintf(stderr, "listen() failed: %s\n", strerror(errno));
 		return 1;
 	}
+	addrlen = sizeof(addr);
 	if ((client.sock = accept(listen_sock, (struct sockaddr *)&addr, &addrlen)) == -1) {
 		fprintf(stderr, "accept() failed: %s\n", strerror(errno));
 		return 1;