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 */
 }