completed MCCP2 support
diff --git a/libtelnet.c b/libtelnet.c
index 37b7b9b..b7a3aed 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -30,8 +30,9 @@
 	sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
 
 /* initialize a telnet state tracker */
-void libtelnet_init(struct libtelnet_t *telnet) {
+void libtelnet_init(struct libtelnet_t *telnet, enum libtelnet_mode_t mode) {
 	memset(telnet, 0, sizeof(struct libtelnet_t));
+	telnet->mode = mode;
 }
 
 /* free up any memory allocated by a state tracker */
@@ -203,8 +204,13 @@
 					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) {
+#ifdef HAVE_ZLIB
+				/* if we are a client and just received the COMPRESS2
+				 * begin marker, setup our zlib box and start handling
+				 * the compressed stream
+				 */
+				if (telnet->mode == LIBTELNET_MODE_CLIENT &&
+						telnet->buffer[0] == LIBTELNET_OPTION_COMPRESS2) {
 					/* allocate zstream box */
 					if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
 							== 0) {
@@ -222,6 +228,9 @@
 						break;
 					}
 
+					/* notify app that compression was enabled */
+					libtelnet_compress_cb(telnet, 1, user_data);
+
 					/* 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
@@ -232,6 +241,8 @@
 							user_data);
 					return;
 				}
+#endif /* HAVE_ZLIB */
+
 				break;
 			/* escaped IAC byte */
 			case LIBTELNET_IAC:
@@ -264,10 +275,11 @@
 /* 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
+#ifdef HAVE_ZLIB
+	/* if we are a client and we have a zlib box, then COMPRESS2 has been
+	 * negotiated and we need to inflate the buffer before processing
 	 */
-	if (telnet->zlib) {
+	if (telnet->mode == LIBTELNET_MODE_CLIENT && telnet->zlib != 0) {
 		unsigned char inflate_buffer[4096];
 		int rs;
 
@@ -298,6 +310,8 @@
 
 			/* on error (or on end of stream) disable further inflation */
 			if (rs != Z_OK) {
+				libtelnet_compress_cb(telnet, 0, user_data);
+
 				inflateEnd(telnet->zlib);
 				free(telnet->zlib);
 				telnet->zlib = 0;
@@ -305,24 +319,67 @@
 			}
 		}
 
-	/* COMPRESS2 is not negotiated, just ignore */
-	} else {
+	/* COMPRESS2 is not negotiated, just process */
+	} else
+#endif /* HAVE_ZLIB */
 		_process(telnet, buffer, size, user_data);
-	}
+}
+
+static void _send(struct libtelnet_t *telnet, unsigned char *buffer,
+		unsigned int size, void *user_data) {
+#ifdef HAVE_ZLIB
+	/* if we are a server and we have a zlib box, then COMPRESS2 has been
+	 * negotiated and we need to deflate the buffer before sending it out
+	 */
+	if (telnet->mode == LIBTELNET_MODE_SERVER && telnet->zlib != 0) {
+		unsigned char deflate_buffer[1024];
+
+		/* initialize zlib state */
+		telnet->zlib->next_in = buffer;
+		telnet->zlib->avail_in = size;
+		telnet->zlib->next_out = deflate_buffer;
+		telnet->zlib->avail_out = sizeof(deflate_buffer);
+
+		/* deflate until buffer exhausted and all output is produced */
+		while (telnet->zlib->avail_in > 0 || telnet->zlib->avail_out == 0) {
+			/* reset output buffer */
+
+			/* compress */
+			if (deflate(telnet->zlib, Z_SYNC_FLUSH) != Z_OK) {
+				libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+						user_data);
+				deflateEnd(telnet->zlib);
+				free(telnet->zlib);
+				telnet->zlib = 0;
+				break;
+			}
+
+			libtelnet_send_cb(telnet, deflate_buffer, sizeof(deflate_buffer) -
+					telnet->zlib->avail_out, user_data);
+
+			/* prepare output buffer for next run */
+			telnet->zlib->next_out = deflate_buffer;
+			telnet->zlib->avail_out = sizeof(deflate_buffer);
+		}
+
+	/* COMPRESS2 is not negotiated, just send */
+	} else
+#endif /* HAVE_ZLIB */
+		libtelnet_send_cb(telnet, buffer, size, user_data);
 }
 
 /* send an iac command */
 void libtelnet_send_command(struct libtelnet_t *telnet, unsigned char cmd,
 		void *user_data) {
 	unsigned char bytes[2] = { LIBTELNET_IAC, cmd };
-	libtelnet_send_cb(telnet, bytes, 2, user_data);
+	_send(telnet, bytes, 2, user_data);
 }
 
 /* send negotiation */
 void libtelnet_send_negotiate(struct libtelnet_t *telnet, unsigned char cmd,
 		unsigned char opt, void *user_data) {
 	unsigned char bytes[3] = { LIBTELNET_IAC, cmd, opt };
-	libtelnet_send_cb(telnet, bytes, 3, user_data);
+	_send(telnet, bytes, 3, user_data);
 }
 
 /* send non-command data (escapes IAC bytes) */
@@ -334,7 +391,7 @@
 		if (buffer[i] == LIBTELNET_IAC) {
 			/* dump prior text if any */
 			if (i != l)
-				libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
+				_send(telnet, buffer + l, i - l, user_data);
 			l = i + 1;
 
 			/* send escape */
@@ -344,7 +401,7 @@
 
 	/* send whatever portion of buffer is left */
 	if (i != l)
-		libtelnet_send_cb(telnet, buffer + l, i - l, user_data);
+		_send(telnet, buffer + l, i - l, user_data);
 }
 
 /* send sub-request */
@@ -354,4 +411,31 @@
 	libtelnet_send_data(telnet, &type, 1, user_data);
 	libtelnet_send_data(telnet, buffer, size, user_data);
 	libtelnet_send_command(telnet, LIBTELNET_SE, user_data);
+
+#ifdef HAVE_ZLIB
+	/* if we're a server and we just sent the COMPRESS2 marker, we must
+	 * make sure all further data is compressed
+	 */
+	if (telnet->mode == LIBTELNET_MODE_SERVER && type ==
+			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 (deflateInit(telnet->zlib, Z_DEFAULT_COMPRESSION) != Z_OK) {
+			free(telnet->zlib);
+			telnet->zlib = 0;
+			libtelnet_error_cb(telnet,
+				LIBTELNET_ERROR_UNKNOWN, user_data);
+		}
+
+		/* notify app that compression was enabled */
+		libtelnet_compress_cb(telnet, 1, user_data);
+	}
+#endif /* HAVE_ZLIB */
 }
diff --git a/libtelnet.h b/libtelnet.h
index 1ad8e3d..575d7ba 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -28,6 +28,12 @@
 #define LIBTELNET_OPTION_COMPRESS2 86
 #define LIBTELNET_OPTION_ZMP 93
 
+/* libtelnet modes */
+enum libtelnet_mode_t {
+	LIBTELNET_MODE_SERVER = 0,
+	LIBTELNET_MODE_CLIENT
+};
+
 /* telnet states */
 enum libtelnet_state_t {
 	LIBTELNET_STATE_DATA = 0,
@@ -37,7 +43,7 @@
 	LIBTELNET_STATE_WILL,
 	LIBTELNET_STATE_WONT,
 	LIBTELNET_STATE_SB,
-	LIBTELNET_STATE_SB_IAC,
+	LIBTELNET_STATE_SB_IAC
 };
 
 /* error codes */
@@ -46,7 +52,7 @@
 	LIBTELNET_ERROR_NOMEM, /* memory allocation failure */
 	LIBTELNET_ERROR_OVERFLOW, /* data exceeds buffer size */
 	LIBTELNET_ERROR_PROTOCOL, /* invalid sequence of special bytes */
-	LIBTELNET_ERROR_UNKNOWN, /* some crazy unexplainable unknown error */
+	LIBTELNET_ERROR_UNKNOWN /* some crazy unexplainable unknown error */
 };
 
 /* state tracker */
@@ -63,50 +69,57 @@
 	unsigned int length;
 	/* current state */
 	enum libtelnet_state_t state;
+	/* processing mode */
+	enum libtelnet_mode_t mode;
 };
 
 /* libtelnet callback declarations
  * APPLICATION MUST IMPLEMENT THESE FUNCTIONS!!
  */
 extern void libtelnet_data_cb(struct libtelnet_t *telnet,
-	unsigned char *buffer, unsigned int size, void *user_data);
+		unsigned char *buffer, unsigned int size, void *user_data);
 extern void libtelnet_send_cb(struct libtelnet_t *telnet,
-	unsigned char *buffer, unsigned int size, void *user_data);
+		unsigned char *buffer, unsigned int size, void *user_data);
 extern void libtelnet_command_cb(struct libtelnet_t *telnet,
-	unsigned char cmd, void *user_data);
+		unsigned char cmd, void *user_data);
 extern void libtelnet_negotiate_cb(struct libtelnet_t *telnet,
-	unsigned char cmd, unsigned char opt, void *user_data);
+		unsigned char cmd, unsigned char opt, void *user_data);
 extern void libtelnet_subrequest_cb(struct libtelnet_t *telnet,
-	unsigned char type, unsigned char *data, unsigned int size,
-	void *user_data);
+		unsigned char type, unsigned char *data, unsigned int size,
+		void *user_data);
+#ifdef HAVE_ZLIB
+extern void libtelnet_compress_cb(struct libtelnet_t *telnet,
+		char enabled, void *user_data);
+#endif
 extern void libtelnet_error_cb(struct libtelnet_t *telnet,
-	enum libtelnet_error_t error, void *user_data);
+		enum libtelnet_error_t error, void *user_data);
 
 /* initialize a telnet state tracker */
-extern void libtelnet_init(struct libtelnet_t *telnet);
+extern void libtelnet_init(struct libtelnet_t *telnet,
+		enum libtelnet_mode_t mode);
 
 /* free up any memory allocated by a state tracker */
 extern void libtelnet_free(struct libtelnet_t *telnet);
 
 /* push a byte buffer into the state tracker */
 extern void libtelnet_push(struct libtelnet_t *telnet,
-	unsigned char *buffer, unsigned int size, void *user_data);
+		unsigned char *buffer, unsigned int size, void *user_data);
 
 /* send an iac command */
 extern void libtelnet_send_command(struct libtelnet_t *telnet,
-	unsigned char cmd, void *user_data);
+		unsigned char cmd, void *user_data);
 
 /* send negotiation */
 extern void libtelnet_send_negotiate(struct libtelnet_t *telnet,
-	unsigned char cmd, unsigned char opt, void *user_data);
+		unsigned char cmd, unsigned char opt, void *user_data);
 
 /* send non-command data (escapes IAC bytes) */
 extern void libtelnet_send_data(struct libtelnet_t *telnet,
-	unsigned char *buffer, unsigned int size, void *user_data);
+		unsigned char *buffer, unsigned int size, void *user_data);
 
 /* send sub-request */
 extern void libtelnet_send_subrequest(struct libtelnet_t *telnet,
-	unsigned char type, unsigned char *buffer, unsigned int size,
-	void *user_data);
+		unsigned char type, unsigned char *buffer, unsigned int size,
+		void *user_data);
 
 #endif /* !defined(LIBTELNET_INCLUDE) */
diff --git a/telnet-proxy.c b/telnet-proxy.c
index 9a38af6..218d860 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -181,15 +181,6 @@
 	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);
 }
@@ -209,6 +200,13 @@
 			conn->remote);
 }
 
+void libtelnet_compress_cb(struct libtelnet_t *telnet, char enabled,
+		void *user_data) {
+	struct conn_t *conn = (struct conn_t*)user_data;
+
+	printf("%s COMPRESSION %s\e[0m\n", conn->name, enabled ? "ON" : "OFF");
+}
+
 void libtelnet_error_cb(struct libtelnet_t *telnet,
 		enum libtelnet_error_t error, void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
@@ -296,9 +294,13 @@
 	client.name = "\e[34mCLIENT";
 	client.remote = &server;
 
-	/* initialize telnet boxes */
-	libtelnet_init(&server.telnet);
-	libtelnet_init(&client.telnet);
+	/* initialize telnet boxes
+	 * NOTE: we set the server connect to the CLIENT mode because we
+	 * are acting as a client of the server; likewise, we set the
+	 * client connection to SERVER mode becauser we are acting as a
+	 * server to the client. */
+	libtelnet_init(&server.telnet, LIBTELNET_MODE_CLIENT);
+	libtelnet_init(&client.telnet, LIBTELNET_MODE_SERVER);
 
 	/* initialize poll descriptors */
 	memset(pfd, 0, sizeof(pfd));