[Pkg-wmaker-commits] [wmbiff] 06/09: * Big patch from Neil Spring which adds lots of crypto support to WMBiff. * WMBiff can now speak IMAP over TLS and CRAM-MD5, and APOP using libgcrypt. * Known problems: - gnutls is being developed still, so it may have security related bugs. - A bug in gnutls (a too-small buffer) may cause problems in the parsing of openssl certificates. This should be fixed by gnutls soon, hopefully. Added error messages if this bug is tickled. If you've got problems with IMAP SSL, please try upgrading gnutls. If the problem persists, let us know. - IMAP has totally been rewritten, so bugs may crop up. - Pop3 hasn't been rewritten to use the TLS primitives, though it probably could be if someone wanted to. * There's a new interface for reading and writing to the socket in tlsComm.[ch]. This makes the IMAP code somewhat independent of whether ssl is used, and provides nicer primitives to help skip 'informational' messages. * WITH_TLS and WITH_GCRYPT are on-by-default in the Makefile. TLS applies to encryption, GCRYPT to cram-md5 and apop authentication. Since gnutls depends on libgcrypt anyway, these probably don't need to be independent. Some compile warnings may be generated when these are disabled. * Added code to optionally include dmalloc.h and link -ldmalloc. This doesn't do anything at the moment, but shouldn't hurt. It's off-by-default. * IMAP connections are now persistent. persistent. This is to cut down on the need to re-negotiate an SSL connection every time you want to check mail. It tries to use just one connection per (server/username/password/port number), which means multiple mailboxes need only one connection. * There are a handful of lclint (http://lclint.cs.virginia.edu) annotations in tlsComm.[ch]. These should also not hurt anyone, and are meant to keep the signal to noise ratio of lclint high. * The rewritten IMAP code uses the GNU regex library to handle the configuration line. I think its clearer than the cascading strtok() solution, but the regex might not be perfect. * Removed an unnecessary "inline" keyword from charutil.h. * Added a TODO document with the bits that are missing from the picture. * Please bow in awe at NAKAYAMA Takao, Jay T. Francis and specially Neil Spring for all of this.

Doug Torrance dtorrance-guest at moszumanska.debian.org
Thu Aug 20 02:59:58 UTC 2015


This is an automated email from the git hooks/post-receive script.

dtorrance-guest pushed a commit to tag wmbiff_0_3_2
in repository wmbiff.

commit 0b77492452e040769cdb99ee8547240066bf4497
Author: jordi <jordi>
Date:   Thu Oct 4 09:50:59 2001 +0000

    * Big patch from Neil Spring which adds lots of crypto support to WMBiff.
    * WMBiff can now speak IMAP over TLS and CRAM-MD5, and APOP using libgcrypt.
    * Known problems:
      - gnutls is being developed still, so it may have security related bugs.
      - A bug in gnutls (a too-small buffer) may cause problems
        in the parsing of openssl certificates. This should be fixed by gnutls
        soon, hopefully. Added error messages if this bug is tickled. If you've
        got problems with IMAP SSL, please try upgrading gnutls. If the problem
        persists, let us know.
      - IMAP has totally been rewritten, so bugs may crop up.
      - Pop3 hasn't been rewritten to use the TLS primitives, though it probably
        could be if someone wanted to.
    * There's a new interface for reading and writing to the socket in
      tlsComm.[ch].  This makes the IMAP code somewhat independent of whether
      ssl is used, and provides nicer primitives to help skip 'informational'
      messages.
    * WITH_TLS and WITH_GCRYPT are on-by-default in the Makefile.  TLS
      applies to encryption, GCRYPT to cram-md5 and apop authentication.
      Since gnutls depends on libgcrypt anyway, these probably don't need to be
      independent.  Some compile warnings may be generated when these are
      disabled.
    * Added code to optionally include dmalloc.h and link -ldmalloc. This
      doesn't do anything at the moment, but shouldn't hurt. It's off-by-default.
    * IMAP connections are now persistent.  persistent.  This is to cut down on
      the need to re-negotiate an SSL connection every time you want to check
      mail. It tries to use just one connection per (server/username/password/port
      number), which means multiple mailboxes need only one connection.
    * There are a handful of lclint (http://lclint.cs.virginia.edu) annotations
      in tlsComm.[ch]. These should also not hurt anyone, and are meant to keep
      the signal to noise ratio of lclint high.
    * The rewritten IMAP code uses the GNU regex library to handle the
      configuration line.  I think its clearer than the cascading strtok()
      solution, but the regex might not be perfect.
    * Removed an unnecessary "inline" keyword from charutil.h.
    * Added a TODO document with the bits that are missing from the picture.
    * Please bow in awe at NAKAYAMA Takao, Jay T. Francis and specially
      Neil Spring for all of this.
---
 NEWS                   |   8 +
 README                 |   6 +-
 TODO                   |   5 +
 wmbiff/Client.h        |  18 +-
 wmbiff/Imap4Client.c   | 489 ++++++++++++++++++++++++++++++++++++++-----------
 wmbiff/LicqClient.c    |   5 +-
 wmbiff/Makefile        |  32 +++-
 wmbiff/Pop3Client.c    | 133 +++++++++++++-
 wmbiff/charutil.c      | 118 +++++++++++-
 wmbiff/charutil.h      |  11 +-
 wmbiff/maildirClient.c |   5 +-
 wmbiff/mboxClient.c    |   5 +-
 wmbiff/socket.c        |   7 +-
 wmbiff/tlsComm.c       | 416 +++++++++++++++++++++++++++++++++++++++++
 wmbiff/tlsComm.h       |  43 +++++
 wmbiff/wmbiff.1        |   6 +-
 wmbiff/wmbiff.c        |   8 +-
 wmbiff/wmbiffrc.5      |  16 +-
 18 files changed, 1185 insertions(+), 146 deletions(-)

diff --git a/NEWS b/NEWS
index d636b49..b4c496c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,14 @@
 Release Notes
 ~~~~~~~~~~~~~
 
+Release 0.3.2 - [UNRELEASED]
+  * After some tries (other OpenSSL based patches, independent APOP
+    & CRAM-Md5 patches) we've come up with something based on GNUtls and
+    gcrypt, which are GPL based and have no licensing issues for us.
+  * WMBiff now supports IMAP-SSL, APOP and CRAM-Md5 authentication. These
+    can be disabled at compile time, commenting out the WITH_TLS and
+    WITH_GCRYPT defines. (Neil Spring, NAKAYAMA Takao and Jay T. Francis).
+
 Release 0.3.1 - Sun, 24 Jun 2001 20:15:41 +0200
 
   * Replaced /var/spool/mail with /var/mail, which is what the FHS mandates.
diff --git a/README b/README
index 6db9dac..1e170df 100644
--- a/README
+++ b/README
@@ -11,8 +11,8 @@
    Yellow digits display number of unread messages, with blinking on new
    mail arrival, if any.
    
-   At this moment unix-style (mbox), maildir POP3 and IMAP mailboxes are
-   supported. wmBiff also understands Licq's history files.
+   At this moment unix-style (mbox), maildir, POP3, APOP and IMAP mailboxes
+   are supported. wmBiff also understands Licq's history files.
    wmBiff supports up to 5 mailboxes (but you can start 2 or more
    wmbiff's with differrent configs).
    
@@ -78,7 +78,9 @@ position. All other positions will be empty.
    Dwayne C. Litzenberger (dlitz at dlitz.net)
    Mark Hurley (debian4tux at telocity.com)
    Rob Funk (rfunk at funkinet.net)
+   Neil Spring (nspring at cs.washington.edu)
    NAKAYAMA Takao (hoehoe at wakaba.jp)
+   Jay T Francis (jtf at u880.org)
 
 
      _________________________________________________________________
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..f4e98f1
--- /dev/null
+++ b/TODO
@@ -0,0 +1,5 @@
+- Make imap regex parser limit username, password to 32
+  characters as referenced in the structure that stores them.
+- Store and or verify server certificates.
+
+$Id: TODO,v 1.1 2001/10/04 09:50:59 jordi Exp $
diff --git a/wmbiff/Client.h b/wmbiff/Client.h
index f3fb7fd..2cb45f3 100644
--- a/wmbiff/Client.h
+++ b/wmbiff/Client.h
@@ -1,4 +1,4 @@
-/* $Id: Client.h,v 1.4 2001/10/04 08:54:02 jordi Exp $ */
+/* $Id: Client.h,v 1.5 2001/10/04 09:50:59 jordi Exp $ */
 /* Author : Scott Holden ( scotth at thezone.net )
    Modified : Yong-iL Joh ( tolkien at mizi.com )
    Modified : Jorge Garc�a ( Jorge.Garcia at uv.es )
@@ -16,6 +16,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef WITH_GCRYPT
+#include <gcrypt.h>
+#endif
+
 typedef struct _mbox_t *Pop3;
 typedef struct _mbox_t {
 	char label[32];				/* Printed at left; max 5 chars */
@@ -51,6 +55,14 @@ typedef struct _mbox_t {
 			int serverPort;
 			int localPort;
 		} pop;
+		struct {
+			char password[32];
+			char userName[32];
+			char serverName[256];
+			int serverPort;
+			int localPort;
+			unsigned int dossl:1;
+		} imap;
 	} u;
 
 	FILE *(*open) (Pop3);
@@ -63,9 +75,9 @@ typedef struct _mbox_t {
 
 #define BUF_SIZE 1024
 
-int sock_connect(char *hostname, int port);
+int sock_connect(const char *hostname, int port);
 int pop3Create(Pop3 pc, char *str);
-int imap4Create(Pop3 pc, char *str);
+int imap4Create(Pop3 pc, const char *str);
 int licqCreate(Pop3 pc, char *str);
 int mboxCreate(Pop3 pc, char *str);
 int maildirCreate(Pop3 pc, char *str);
diff --git a/wmbiff/Imap4Client.c b/wmbiff/Imap4Client.c
index 0cc5866..2d93ec5 100644
--- a/wmbiff/Imap4Client.c
+++ b/wmbiff/Imap4Client.c
@@ -1,152 +1,417 @@
-/* $Id: Imap4Client.c,v 1.4 2001/06/23 00:09:32 oskuro Exp $ */
-/* Author : Yong-iL Joh ( tolkien at mizi.com )
-   Modified: Jorge Garc�a ( Jorge.Garcia at uv.es )
- * 
- * Imap4 Email checker.
- *
- * Last Updated : Mar 20, 05:32:35 CET 2001
- *
- */
+/* rewrite of the IMAP code by Neil Spring
+ * (nspring at cs.washington.edu) to support gnutls and
+ * persistent connections to servers.  */
+
+/* Originally written by Yong-iL Joh (tolkien at mizi.com),
+ * modified by Jorge Garcia (Jorge.Garcia at uv.es), and
+ * modified by Jay Francis (jtf at u880.org) to support
+ * CRAM-MD5 */
+
+/* get asprintf */
+#define _GNU_SOURCE
 
 #include "Client.h"
+#include "charutil.h"
+#include "tlsComm.h"
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
+#define	PCU	(pc->u).imap
+
+#ifdef __LCLINT__
+void asprintf( /*@out@ */ char **out, char *fmt, ...);
+#endif
+
+/* this array maps server:port pairs to file descriptors, so
+   that when more than one mailbox is queried from a server,
+   we only use one socket.  It's limited in size by the
+   number of different mailboxes displayed. */
+#define FDMAP_SIZE 5
+static struct fdmap_struct {
+	const char *user_password_server_port;	/* tuple, in string form */
+	/*@owned@ */ struct connection_state *cs;
+} fdmap[FDMAP_SIZE];
+
+
+int imap4Create(Pop3 pc, const char *str);
+#ifdef WITH_GCRYPT
+static int authenticate_md5(Pop3 pc, struct connection_state *scs);
+#endif
+
 
-#define	PCU	(pc->u).pop
+/* recover a socket from the connection cache */
+/*@null@*//*@dependent@ */
+static struct connection_state *state_for_pcu(Pop3 pc)
+{
+	char *connection_id;
+	struct connection_state *retval = NULL;
+	int i;
+	asprintf(&connection_id, "%s|%s|%s|%d", PCU.userName,
+			 PCU.password, PCU.serverName, PCU.serverPort);
+	for (i = 0; i < FDMAP_SIZE; i++)
+		if (fdmap[i].user_password_server_port != NULL &&
+			(strcmp(connection_id,
+					fdmap[i].user_password_server_port) == 0)) {
+			retval = fdmap[i].cs;
+		}
+	free(connection_id);
+	return (retval);
+}
+
+/* bind to the connection cache */
+static void bind_state_to_pcu(Pop3 pc,
+							  /*@owned@ */ struct connection_state *scs)
+{
+	char *connection_id;
+	int i;
+	if (scs == NULL) {
+		abort();
+	}
+	asprintf(&connection_id, "%s|%s|%s|%d", PCU.userName,
+			 PCU.password, PCU.serverName, PCU.serverPort);
+	for (i = 0; i < FDMAP_SIZE && fdmap[i].cs != NULL; i++);
+	fdmap[i].user_password_server_port = connection_id;
+	fdmap[i].cs = scs;
+}
+
+/* remove from the connection cache */
+static
+/*@owned@*//*@null@ */
+struct connection_state *unbind(struct connection_state *scs)
+{
+	int i;
+	struct connection_state *retval = NULL;
+	if (scs == NULL) {
+		abort();
+	}
+	for (i = 0; i < FDMAP_SIZE && fdmap[i].cs != scs; i++);
+	if (i < FDMAP_SIZE) {
+		free((char *) fdmap[i].user_password_server_port);
+		retval = fdmap[i].cs;
+		fdmap[i].cs = NULL;
+	}
+	return (retval);
+}
 
-FILE *imap4Login(Pop3 pc)
+/* creates a connection to the server, if a matching one doesn't exist. */
+/*@null@*/
+FILE *imap_open(Pop3 pc)
 {
-	int fd;
-	FILE *f;
+	struct connection_state *scs;
+	char *connection_name;
+	int sd;
 	char buf[BUF_SIZE];
 
-	if ((fd = sock_connect(PCU.serverName, PCU.serverPort)) == -1) {
-		fprintf(stderr, "Not Connected To Server '%s:%d'\n",
+
+	if (state_for_pcu(pc) != NULL) {
+		/* don't need to open. */
+		return NULL;
+	}
+
+	/* no cached connection */
+	sd = sock_connect((const char *) PCU.serverName, PCU.serverPort);
+	if (sd == -1) {
+		fprintf(stderr, "Couldn't connect to %s:%d\n",
 				PCU.serverName, PCU.serverPort);
 		return NULL;
 	}
+	asprintf(&connection_name, "%s:%d", PCU.serverName, PCU.serverPort);
+
+	/* build the connection using STARTTLS */
+	if (PCU.dossl && (PCU.serverPort == 143)) {
+		/* setup an unencrypted binding long enough to invoke STARTTLS */
+		scs = initialize_unencrypted(sd, connection_name);
+
+		/* can we? */
+		tlscomm_printf(scs, "a000 CAPABILITY\r\n");
+		if (!tlscomm_expect(scs, "* CAPABILITY", buf, BUF_SIZE))
+			goto communication_failure;
 
-	f = fdopen(fd, "r+");
-	fgets(buf, BUF_SIZE, f);
+		if (!strstr(buf, "STARTTLS")) {
+			printf("server doesn't support ssl imap on port 143.");
+			goto communication_failure;
+		}
+
+		/* we sure can! */
+#ifdef DEBUG_IMAP
+		printf("Negotiating TLS within IMAP");
+#endif
+		tlscomm_printf(scs, "a001 STARTTLS\r\n");
 
-	/* Login to the server */
-	fflush(f);
-	fprintf(f, "a001 LOGIN %s %s\r\n", PCU.userName, PCU.password);
+		if (!tlscomm_expect(scs, "a001 ", buf, BUF_SIZE))
+			goto communication_failure;
 
-	/* Ensure that the buffer is not an informational line */
-	do {
-		fflush(f);
-		fgets(buf, BUF_SIZE, f);
+		if (!strstr(buf, "a001 OK")) {
+			printf("couldn't negotiate tls. :(\n");
+			goto communication_failure;
+		}
+
+		/* we don't need the unencrypted state anymore */
+		/* note that communication_failure will close the 
+		   socket and free via tls_close() */
+		free(scs);				// fall through will scs = initialize_gnutls(sd);
 	}
-	while (buf[0] == '*');
 
-	if (buf[5] != 'O') {		/* Looking for "a001 OK" */
-		fprintf(f, "a002 LOGOUT\r\n");
-		fclose(f);
-		return NULL;
-	};
+	/* either we've negotiated ssl from starttls, or
+	   we're starting an encrypted connection now */
+	if (PCU.dossl) {
+		scs = initialize_gnutls(sd, connection_name);
+		if (scs == NULL) {
+			fprintf(stderr, "Failed to initialize TLS\n");
+			return NULL;
+		}
+	} else {
+		scs = initialize_unencrypted(sd, connection_name);
+	}
+
+	/* authenticate; first find out how */
+	/* note that capabilities may have changed since last
+	   time we may have asked, if we called STARTTLS, my 
+	   server will allow plain password login within an 
+	   encrypted session. */
+	tlscomm_printf(scs, "a000 CAPABILITY\r\n");
+	if (!tlscomm_expect(scs, "* CAPABILITY", buf, BUF_SIZE)) {
+		printf("unable to query capability string");
+		goto communication_failure;
+	}
+#ifdef WITH_GCRYPT
+	/* is cram-md5 permitted? */
+	if (strstr(buf, "AUTH=CRAM-MD5")) {
+		if (authenticate_md5(pc, scs) == 0) {
+			return NULL;
+		}
+		goto success;
+	}
+#endif
+
+	/* is login prohibited? */
+	if (strstr(buf, "LOGINDISABLED")) {
+		printf
+			("I don't know how to login to this server (LOGINDISABLED).\n");
+		goto communication_failure;
+	}
+
+	/* login */
+	tlscomm_printf(scs, "a001 LOGIN %s %s\r\n", PCU.userName,
+				   PCU.password);
+	if (!tlscomm_expect(scs, "a001 ", buf, BUF_SIZE)) {
+		printf("unable to login");
+		goto communication_failure;
+	}
+
+	if (buf[5] != 'O') {
+		tlscomm_printf(scs, "a002 LOGOUT\r\n");
+		printf("login failed\n");
+		goto communication_failure;
+	}
+
+  success:
+	/* store this well setup connection in the cache */
+	bind_state_to_pcu(pc, scs);
+	/* I don't do file handles. */
+	return NULL;
+
+  communication_failure:
+	tlscomm_close(scs);
+	return NULL;
 
-	return f;
 }
 
-int imap4CheckMail(Pop3 pc)
+int imap_checkmail(Pop3 pc)
 {
-	FILE *f;
+	/* recover connection state from the cache */
+	struct connection_state *scs = state_for_pcu(pc);
 	char buf[BUF_SIZE];
 
-	f = pc->open(pc);
-	if (f == NULL)
-		return -1;
+	/* if it's not in the cache, try to open */
+	if (scs == NULL) {
+		(void) pc->open(pc);
+		scs = state_for_pcu(pc);
+	}
 
-	fflush(f);
-	fprintf(f, "a003 STATUS %s (MESSAGES UNSEEN)\r\n", pc->path);
-	fflush(f);
-	fgets(buf, 127, f);
-	if (buf[0] != '*') {		/* Looking for "* STATUS ..." */
-		fprintf(stderr, "Error Receiving Stats '%s@%s:%d'\n\t%s\n",
-				PCU.userName, PCU.serverName, PCU.serverPort, buf);
-		fclose(f);
-		return -1;
+	/* if we've got it by now, try the status query */
+	if (scs) {
+		tlscomm_printf(scs, "a003 STATUS %s (MESSAGES UNSEEN)\r\n",
+					   pc->path);
+		if (tlscomm_expect(scs, "* STATUS", buf, 127)) {
+			/* a valid response? */
+			(void) sscanf(buf, "* STATUS %*s (MESSAGES %d UNSEEN %d)",
+						  &(pc->TotalMsgs), &(pc->UnreadMsgs));
+		} else {
+			/* something went wrong. bail. */
+			tlscomm_close(unbind(scs));
+			return -1;
+		}
 	} else {
-		sscanf(buf, "* STATUS %*s (MESSAGES %d UNSEEN %d)",
-			   &(pc->TotalMsgs), &(pc->UnreadMsgs));
-#ifdef DEBUG_IMAP4
-		fprintf(stderr, "[%s:%d] %s", __FILE__, __LINE__, buf);
-#endif
-		fgets(buf, 127, f);
-#ifdef DEBUG_IMAP4
-		fprintf(stderr, "[%s:%d] %s", __FILE__, __LINE__, buf);
+		/* login must've failed */
+		return -1;
+	}
+	return 0;
+}
+
+/* helper function for the configuration line parser */
+static void assign(char *destination,
+				   int startidx, int endidx, const char *source)
+{
+	if (startidx > -1) {
+		strncpy(destination, source + startidx, endidx - startidx);
+		destination[endidx - startidx] = '\0';
+	}
+}
+
+/* parse the config line to setup the Pop3 structure */
+int imap4Create(Pop3 pc, const char *const str)
+{
+	int matchedchars;
+	struct re_pattern_buffer rpbuf;
+	struct re_registers regs;
+	const char *errstr;
+	const char *regex =
+		".*imap:([^:]+):([^@]+)@([^/:]+)(/[^:]+)?(:[0-9]+)?";
+
+	/* IMAP4 format: imap:user:password at server/mailbox[:port] */
+	/* If 'str' line is badly formatted, wmbiff won't display the mailbox. */
+	if (strncmp("ssl", str, 3) == 0) {
+#ifdef WITH_TLS
+		static int haveBeenWarned;
+		PCU.dossl = 1;
+		if (!haveBeenWarned) {
+			printf("wmbiff uses gnutls for TLS/SSL encryption support:\n"
+				   "  If you distribute software that uses gnutls, don't forget\n"
+				   "  to warn the users of your software that gnutls is at a\n"
+				   "  testing phase and may be totally insecure.\n"
+				   "\nConsider yourself warned.\n");
+			haveBeenWarned = 1;
+		}
+#else
+		printf("This copy of wmbiff was not compiled with gnutls;\n"
+			   "sslimap is unavailable.  Exiting to protect your\n"
+			   "passwords and privacy.\n");
+		exit(EXIT_FAILURE);
 #endif
+	} else
+		PCU.dossl = 0;
+
+	/* compile the regex pattern */
+	memset(&rpbuf, 0, sizeof(struct re_pattern_buffer));
+	re_syntax_options = RE_SYNTAX_EGREP;
+	errstr = re_compile_pattern(regex, strlen(regex), &rpbuf);
+	if (errstr != NULL) {
+		pc->label[0] = '\0';
+		fprintf(stderr, "error in compiling regular expression: %s\n",
+				errstr);
+		return -1;
+	}
+
+	/* match the regex */
+	regs.num_regs = REGS_UNALLOCATED;
+	matchedchars = re_match(&rpbuf, str, strlen(str), 0, &regs);
+	if (matchedchars <= 0) {
+		pc->label[0] = '\0';
+		fprintf(stderr, "Couldn't parse line %s (%d)\n", str,
+				matchedchars);
+		return -1;
 	}
+#ifdef undef
+	printf("--\n");
+	for (i = 1; i < 6; i++) {
+		printf("%d %d, (%.*s)\n", regs.start[i], regs.end[i],
+			   (regs.end[i] - regs.start[i]),
+			   (regs.start[i] >= 0) ? &str[regs.start[i]] : "");
+	}
+#endif
 
-	fflush(f);
+	/* copy matches where they belong */
+	assign(PCU.userName, regs.start[1], regs.end[1], str);
+	assign(PCU.password, regs.start[2], regs.end[2], str);
+	assign(PCU.serverName, regs.start[3], regs.end[3], str);
+	if (regs.start[4] != -1)
+		assign(pc->path, regs.start[4] + 1, regs.end[4], str);
+	else
+		strcpy(pc->path, "INBOX");
+	if (regs.start[5] != -1)
+		PCU.serverPort = atoi(str + regs.start[5] + 1);
+	else
+		PCU.serverPort = (PCU.dossl) ? 993 : 143;
 
-	fprintf(f, "a004 LOGOUT\r\n");
-	fclose(f);
+#ifdef DEBUG_IMAP4
+	printf("imap4: userName= '%s'\n", PCU.userName);
+	printf("imap4: password= '%s'\n", PCU.password);
+	printf("imap4: serverName= '%s'\n", PCU.serverName);
+	printf("imap4: serverPath= '%s'\n", pc->path);
+	printf("imap4: serverPort= '%d'\n", PCU.serverPort);
+#endif
 
+	pc->open = imap_open;
+	pc->checkMail = imap_checkmail;
+	pc->TotalMsgs = 0;
+	pc->UnreadMsgs = 0;
+	pc->OldMsgs = -1;
+	pc->OldUnreadMsgs = -1;
 	return 0;
 }
 
-int imap4Create(Pop3 pc, char *str)
+#ifdef WITH_GCRYPT
+static int authenticate_md5(Pop3 pc, struct connection_state *scs)
 {
-	/* IMAP4 format: imap:user:password at server/mailbox[:port] */
-	/* If 'str' line is badly formatted, wmbiff won't display the mailbox. */
-	char *tmp;
-	char *p;
+	char buf[BUF_SIZE];
+	char buf2[BUF_SIZE];
+	unsigned char *md5;
+	GCRY_MD_HD gmh;
+
+	tlscomm_printf(scs, "a007 AUTHENTICATE CRAM-MD5\r\n");
+	if (!tlscomm_expect(scs, "+ ", buf, BUF_SIZE))
+		goto expect_failure;
 
+	Decode_Base64(buf + 2, buf2);
 #ifdef DEBUG_IMAP4
-	printf("imap4: str = '%s'\n", str);
+	fprintf(stderr, "IMAP4 CRAM-MD5 challenge: %s\n", buf2);
 #endif
 
-	strcpy(PCU.password, "");
-	strcpy(PCU.userName, "");
-	strcpy(PCU.serverName, "");
-	PCU.serverPort = 143;
-
-	tmp = strdup(str);
-
-	/* We start with imap:user:password at server[/mailbox][:port] */
-	p = strtok(tmp, ":");		/* cut off ``imap:'' */
-	/* Now, we have user:password at server[/mailbox][:port] */
-	if ((p = strtok(NULL, ":"))) {	/* p pointed to username */
-		strcpy(PCU.userName, p);
-		/* Now, we have password at server[/mailbox][:port] */
-		if ((p = strtok(NULL, "@"))) {	/* p -> password */
-			strcpy(PCU.password, p);
-			/* Now we have server[/mailbox][:port] */
-			if ((p = strtok(NULL, "/"))) {	/* p -> server */
-				strcpy(PCU.serverName, p);
-				/* It should now be [mailbox][:port] */
-				if ((p = strtok(NULL, ":"))) {	/* p -> mailbox */
-					strcpy(pc->path, p);
-				} else {
-					strcpy(pc->path, "INBOX");
-				}
-				/* and finally [port] */
-				if ((p = strtok(NULL, ":"))) {	/* port selected; p -> port */
-					PCU.serverPort = atoi(p);
-				}
-				free(tmp);
+	strcpy(buf, PCU.userName);
+	strcat(buf, " ");
+	gmh = gcry_md_open(GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
+	gcry_md_setkey(gmh, PCU.password, strlen(PCU.password));
+	gcry_md_write(gmh, (unsigned char *) buf2, strlen(buf2));
+	gcry_md_final(gmh);
+	md5 = gcry_md_read(gmh, 0);
+	Bin2Hex(md5, 16, buf2);
+	gcry_md_close(gmh);
 
+	strcat(buf, buf2);
 #ifdef DEBUG_IMAP4
-				printf("imap4: userName= '%s'\n", PCU.userName);
-				printf("imap4: password= '%s'\n", PCU.password);
-				printf("imap4: serverName= '%s'\n", PCU.serverName);
-				printf("imap4: serverPath= '%s'\n", pc->path);
-				printf("imap4: serverPort= '%d'\n", PCU.serverPort);
+	fprintf(stderr, "IMAP4 CRAM-MD5 response: %s\n", buf);
 #endif
-				pc->open = imap4Login;
-				pc->checkMail = imap4CheckMail;
-				pc->TotalMsgs = 0;
-				pc->UnreadMsgs = 0;
-				pc->OldMsgs = -1;
-				pc->OldUnreadMsgs = -1;
-				return 0;
-			}
-		}
-	}
-	/* The line is badly formatted, we're not creating the mailbox line */
-	pc->label[0] = 0;
-	fprintf(stderr, "config line with bad format '%s'.\n", str);
-	return -1;
-}
+	Encode_Base64(buf, buf2);
+
+	tlscomm_printf(scs, "%s\r\n", buf2);
+	if (!tlscomm_expect(scs, "a007 ", buf, BUF_SIZE))
+		goto expect_failure;
 
-/* vim:set ts=4: */
+	if (!strncmp(buf, "a007 OK", 7))
+		return 1;				/* AUTH successful */
+
+	fprintf(stderr,
+			"IMAP4 CRAM-MD5 AUTH failed for user '%s@%s:%d'\n",
+			PCU.userName, PCU.serverName, PCU.serverPort);
+	fprintf(stderr, "It said %s", buf);
+	tlscomm_printf(scs, "a002 LOGOUT\r\n");
+	tlscomm_close(scs);
+	return 0;
+
+  expect_failure:
+	fprintf(stderr, "tlscomm_expect failed: %s", buf);
+	tlscomm_printf(scs, "a002 LOGOUT\r\n");
+	tlscomm_close(scs);
+	return 0;
+}
+#endif
diff --git a/wmbiff/LicqClient.c b/wmbiff/LicqClient.c
index d747584..0b176cb 100644
--- a/wmbiff/LicqClient.c
+++ b/wmbiff/LicqClient.c
@@ -1,4 +1,4 @@
-/* $Id: LicqClient.c,v 1.2 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: LicqClient.c,v 1.3 2001/10/04 09:50:59 jordi Exp $ */
 /* Author : Yong-iL Joh ( tolkien at mizi.com )
    Modified: Jorge Garc�a ( Jorge.Garcia at uv.es )
  * 
@@ -12,6 +12,9 @@
 #include <sys/stat.h>
 #include <utime.h>
 #include <errno.h>
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
 
 #define PCM     (pc->u).mbox
 
diff --git a/wmbiff/Makefile b/wmbiff/Makefile
index bf48dea..a6628ab 100644
--- a/wmbiff/Makefile
+++ b/wmbiff/Makefile
@@ -1,5 +1,11 @@
-# $Id: Makefile,v 1.9 2001/10/04 08:54:02 jordi Exp $
-WMBIFF_VERSION="0.3.1"
+# $Id: Makefile,v 1.10 2001/10/04 09:50:59 jordi Exp $
+
+WMBIFF_VERSION="0.3.1ssl"
+EXTRAFLAGS = -DWITH_TLS -DWITH_GCRYPT
+LIBS   = -lgnutls -lgcrypt
+WMBIFF_VERSION = "0.3.1ssl"
+
+
 DESTDIR=
 prefix=/usr/local
 bindir=${prefix}/bin
@@ -8,17 +14,22 @@ CONF=/etc
 
 CC     = gcc
 LIBDIR = -L/usr/X11R6/lib
-LIBS   = -lXpm -lXext -lX11
-CFLAGS = -O2 -Wall
+LIBS   += -lXpm -lXext -lX11 
+CFLAGS = -O2 -Wall -Wpointer-arith
 #CFLAGS = -g -Wall -pedantic 
 
-EXTRAFLAGS = -DWMBIFF_VERSION='$(WMBIFF_VERSION)'
+EXTRAFLAGS += -DWMBIFF_VERSION='$(WMBIFF_VERSION)' 
+
 #EXTRAFLAGS += -DDEBUG_POP3 -DDEBUG_IMAP4 -DDEBUG_LICQ \
-#	-DDEBUG_MBOX -DDEBUG_MAILDIR -DDEBUG -DDEBUG_MAIL_COUNT
+#	-DDEBUG_MBOX -DDEBUG_MAILDIR -DDEBUG -DDEBUG_MAIL_COUNT \
+#       -DDEBUG_COMM
+
+#CFLAGS += -DUSE_DMALLOC
+#LIBS   += -ldmalloc
 
 OBJS =	wmbiff.o socket.o \
-	Pop3Client.o Imap4Client.o LicqClient.o mboxClient.o \
-	maildirClient.o \
+	Pop3Client.o LicqClient.o mboxClient.o \
+	maildirClient.o Imap4Client.o tlsComm.o \
 	../wmgeneral/wmgeneral.o \
 	../wmgeneral/misc.o \
 	../wmgeneral/list.o \
@@ -29,11 +40,13 @@ INSTALL_DIR	= $(INSTALL) -p -d -o root -g root -m 755
 INSTALL_PROGRAM	= $(INSTALL) -p -o root -g root -m 755
 INSTALL_FILE 	= $(INSTALL) -p -o root -g root -m 644
 
+all: wmbiff-master.xpm wmbiff
+
+Imap4Client.o: Imap4Client.c Client.h Makefile
 
 .c.o:
 	$(CC) $(CFLAGS) $(EXTRAFLAGS) -c $< -o $*.o
 
-all: wmbiff-master.xpm wmbiff
 
 wmbiff-master.xpm:
 	ln -s wmbiff-master-led.xpm wmbiff-master.xpm
@@ -74,3 +87,4 @@ install: wmbiff
 #	-- Dwayne C. Litzenberger <dlitz at dlitz.net>
 indent:
 	indent -npro -kr -i4 -ts4 *.[ch] || true
+
diff --git a/wmbiff/Pop3Client.c b/wmbiff/Pop3Client.c
index f393f63..1195141 100644
--- a/wmbiff/Pop3Client.c
+++ b/wmbiff/Pop3Client.c
@@ -1,4 +1,4 @@
-/* $Id: Pop3Client.c,v 1.2 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: Pop3Client.c,v 1.3 2001/10/04 09:50:59 jordi Exp $ */
 /* Author : Scott Holden ( scotth at thezone.net )
    Modified : Yong-iL Joh ( tolkien at mizi.com )
    Modified : Jorge Garc�a ( Jorge.Garcia at uv.es )
@@ -11,14 +11,24 @@
  */
 
 #include "Client.h"
+#include "charutil.h"
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
 
 #define	PCU	(pc->u).pop
 
+FILE *authenticate_md5(Pop3 pc, FILE * fp, char *buf);
+FILE *authenticate_apop(Pop3 pc, FILE * fp, char *apop_str);
+
 FILE *pop3Login(Pop3 pc)
 {
 	int fd;
 	FILE *fp;
 	char buf[BUF_SIZE];
+	char apop_str[BUF_SIZE];
+	char *ptr1, *ptr2;
+	int has_apop = 0;
 
 	if ((fd = sock_connect(PCU.serverName, PCU.serverPort)) == -1) {
 		fprintf(stderr, "Not Connected To Server '%s:%d'\n",
@@ -28,8 +38,43 @@ FILE *pop3Login(Pop3 pc)
 
 	fp = fdopen(fd, "r+");
 	fgets(buf, BUF_SIZE, fp);
+	fflush(fp);
+#ifdef DEBUG_POP3
+	fprintf(stderr, "%s", buf);
+#endif
+	/* Detect APOP */
+	for (ptr1 = buf + strlen(buf), ptr2 = NULL; ptr1 > buf; --ptr1) {
+		if (*ptr1 == '>') {
+			ptr2 = ptr1;
+		} else if (*ptr1 == '<') {
+			if (ptr2) {
+				has_apop = 1;
+				*(ptr2 + 1) = 0;
+				strncpy(apop_str, ptr1, BUF_SIZE);
+			}
+			break;
+		}
+	}
 
+#ifdef WITH_GCRYPT
+	/* Try to authenticate using AUTH CRAM-MD5 first */
+	fprintf(fp, "AUTH CRAM-MD5\r\n");
 	fflush(fp);
+	fgets(buf, BUF_SIZE, fp);
+#ifdef DEBUG_POP3
+	fprintf(stderr, "%s", buf);
+#endif
+
+	if (buf[0] == '+' && buf[1] == ' ') {
+		return (authenticate_md5(pc, fp, buf));
+	}
+
+	if (has_apop) {				/* APOP is better than nothing */
+		return (authenticate_apop(pc, fp, apop_str));
+	}
+#endif
+	/* A brave man has nothing to hide */
+
 	fprintf(fp, "USER %s\r\n", PCU.userName);
 	fflush(fp);
 	fgets(buf, BUF_SIZE, fp);
@@ -237,5 +282,91 @@ int parse_new_pop3_path(Pop3 pc, char *str)
 	return 1;
 }
 
+#ifdef WITH_GCRYPT
+
+FILE *authenticate_md5(Pop3 pc, FILE * fp, char *buf)
+{
+	char buf2[BUF_SIZE];
+	unsigned char *md5;
+	GCRY_MD_HD gmh;
+
+	Decode_Base64(buf + 2, buf2);
+#ifdef DEBUG_POP3
+	fprintf(stderr, "POP3 CRAM-MD5 challenge: %s\n", buf2);
+#endif
+
+	strcpy(buf, PCU.userName);
+	strcat(buf, " ");
+
+	gmh = gcry_md_open(GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
+	gcry_md_setkey(gmh, PCU.password, strlen(PCU.password));
+	gcry_md_write(gmh, (unsigned char *) buf2, strlen(buf2));
+	gcry_md_final(gmh);
+	md5 = gcry_md_read(gmh, 0);
+	//hmac_md5(buf2, strlen(buf2), PCU.password,
+	//      strlen(PCU.password), md5);
+	Bin2Hex(md5, 16, buf2);
+	gcry_md_close(gmh);
+
+	strcat(buf, buf2);
+#ifdef DEBUG_POP3
+	fprintf(stderr, "POP3 CRAM-MD5 response: %s\n", buf);
+#endif
+	Encode_Base64(buf, buf2);
+
+	fprintf(fp, "%s\r\n", buf2);
+	fflush(fp);
+	fgets(buf, BUF_SIZE, fp);
+
+	if (!strncmp(buf, "+OK", 3))
+		return fp;				/* AUTH successful */
+	else {
+		fprintf(stderr,
+				"POP3 CRAM-MD5 AUTH failed for user '%s@%s:%d'\n",
+				PCU.userName, PCU.serverName, PCU.serverPort);
+		fprintf(stderr, "It said %s", buf);
+		fprintf(fp, "QUIT\r\n");
+		fclose(fp);
+		return NULL;
+	}
+}
+
+FILE *authenticate_apop(Pop3 pc, FILE * fp, char *apop_str)
+{
+	GCRY_MD_HD gmh;
+	char buf[BUF_SIZE];
+	unsigned char *md5;
+
+#ifdef DEBUG_POP3
+	fprintf(stderr, "POP3 APOP challenge: %s\n", apop_str);
+#endif
+	strcat(apop_str, PCU.password);
+
+	gmh = gcry_md_open(GCRY_MD_MD5, 0);
+	gcry_md_write(gmh, (unsigned char *) apop_str, strlen(apop_str));
+	gcry_md_final(gmh);
+	md5 = gcry_md_read(gmh, 0);
+	Bin2Hex(md5, 16, buf);
+	gcry_md_close(gmh);
+
+#ifdef DEBUG_POP3
+	fprintf(stderr, "POP3 APOP response: %s %s\n", PCU.userName, buf);
+#endif
+	fprintf(fp, "APOP %s %s\r\n", PCU.userName, buf);
+	fflush(fp);
+	fgets(buf, BUF_SIZE, fp);
+
+	if (!strncmp(buf, "+OK", 3))
+		return fp;				/* AUTH successful */
+	else {
+		fprintf(stderr, "POP3 APOP AUTH failed for user '%s@%s:%d'\n",
+				PCU.userName, PCU.serverName, PCU.serverPort);
+		fprintf(stderr, "It said %s", buf);
+		fprintf(fp, "QUIT\r\n");
+		fclose(fp);
+		return NULL;
+	}
+}
+#endif
 
 /* vim:set ts=4: */
diff --git a/wmbiff/charutil.c b/wmbiff/charutil.c
index fb8a697..d4a9f23 100644
--- a/wmbiff/charutil.c
+++ b/wmbiff/charutil.c
@@ -1,10 +1,13 @@
-/* $Id: charutil.c,v 1.3 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: charutil.c,v 1.4 2001/10/04 09:50:59 jordi Exp $ */
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
 #include "charutil.h"
 
-inline int LeftTrim(char *psValue)
+static __inline__ int LeftTrim(char *psValue)
 {
 
 	char *psTmp = psValue;
@@ -17,7 +20,7 @@ inline int LeftTrim(char *psValue)
 	return EXIT_SUCCESS;
 }
 
-inline int RightTrim(char *psValue)
+static __inline__ int RightTrim(char *psValue)
 {
 
 	long lLength = strlen(psValue) - 1;
@@ -31,7 +34,7 @@ inline int RightTrim(char *psValue)
 	return EXIT_SUCCESS;
 }
 
-inline int FullTrim(char *psValue)
+int FullTrim(char *psValue)
 {
 
 	if (LeftTrim(psValue) != 0)
@@ -40,3 +43,110 @@ inline int FullTrim(char *psValue)
 		return EXIT_FAILURE;
 	return EXIT_SUCCESS;
 }
+
+void Bin2Hex(unsigned char *src, int length, char *dst)
+{
+	static char hex_tbl[] = "0123456789abcdef";
+
+	int i = 0;
+	char *ptr = dst;
+
+	if (src && ptr) {
+		for (i = 0; i < length; i++) {
+			*ptr++ = hex_tbl[*src >> 4];
+			*ptr++ = hex_tbl[*src++ & 0xf];
+		}
+		*ptr = 0;
+	}
+}
+
+
+#define PAD '='
+char ALPHABET[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\000";
+
+/* find char in in alphabet, return offset, -1 otherwise */
+int find_char(char c)
+{
+	char *a = ALPHABET;
+
+	while (*a)
+		if (*(a++) == c)
+			return a - ALPHABET - 1;
+
+	return -1;
+}
+
+void Encode_Base64(char *src, char *dst)
+{
+	int g = 0;
+	int c = 0;
+
+	if (!src || !dst)
+		return;
+
+	while (*src) {
+		g = (g << 8) | *src++;
+		if (c == 2) {
+			*dst++ = ALPHABET[0x3f & (g >> 18)];
+			*dst++ = ALPHABET[0x3f & (g >> 12)];
+			*dst++ = ALPHABET[0x3f & (g >> 6)];
+			*dst++ = ALPHABET[0x3f & g];
+		}
+		c = (c + 1) % 3;
+	}
+
+	if (c) {
+		if (c == 1) {
+			*dst++ = ALPHABET[0x3f & (g >> 2)];
+			*dst++ = ALPHABET[0x3f & (g << 4)];
+			*dst++ = PAD;
+			*dst++ = PAD;
+		} else {
+			*dst++ = ALPHABET[0x3f & (g >> 10)];
+			*dst++ = ALPHABET[0x3f & (g >> 4)];
+			*dst++ = ALPHABET[0x3f & (g << 2)];
+			*dst++ = PAD;
+		}
+	}
+	*dst = 0;
+}
+
+
+void Decode_Base64(char *src, char *dst)
+{
+	int g = 0;
+	int c = 0;
+	int n = 0;
+
+	if (!src || !dst)
+		return;
+
+	while (*src) {
+		n = find_char(*src++);
+		if (n < 0)
+			continue;
+
+		g <<= 6;
+		g |= n;
+		if (c == 3) {
+			*dst++ = g >> 16;
+			*dst++ = g >> 8;
+			*dst++ = g;
+			g = 0;
+		}
+		c = (c + 1) % 4;
+	}
+	if (c) {
+		if (c == 1) {
+			/* shouldn't happen, but do something anyway */
+			*dst++ = g << 2;
+		} else if (c == 2) {
+			*dst++ = g >> 4;
+		} else {
+			*dst++ = g >> 10;
+			*dst++ = g >> 2;
+		}
+	}
+	*dst = 0;
+}
diff --git a/wmbiff/charutil.h b/wmbiff/charutil.h
index 5e3cbeb..8917c8f 100644
--- a/wmbiff/charutil.h
+++ b/wmbiff/charutil.h
@@ -1,4 +1,4 @@
-/* $Id: charutil.h,v 1.2 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: charutil.h,v 1.3 2001/10/04 09:50:59 jordi Exp $ */
 /* Author: Mark Hurley  (debian4tux at telocity.com)
  *
  * Simple char util's to trim char string arrays
@@ -8,8 +8,11 @@
 #ifndef CHARUTIL
 #define CHARUTIL
 
-inline int LeftTrim(char *psValue);
-inline int RightTrim(char *psValue);
-inline int FullTrim(char *psValue);
+int FullTrim(char *psValue);
+
+void Bin2Hex(unsigned char *src, int length, char *dst);
+
+void Encode_Base64(char *src, char *dst);
+void Decode_Base64(char *src, char *dst);
 
 #endif
diff --git a/wmbiff/maildirClient.c b/wmbiff/maildirClient.c
index 5ef751c..b7b1a2a 100644
--- a/wmbiff/maildirClient.c
+++ b/wmbiff/maildirClient.c
@@ -1,4 +1,4 @@
-/* $Id: maildirClient.c,v 1.2 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: maildirClient.c,v 1.3 2001/10/04 09:50:59 jordi Exp $ */
 /* Author : Yong-iL Joh ( tolkien at mizi.com )
    Modified : Jorge Garc�a ( Jorge.Garcia at uv.es )
  * 
@@ -13,6 +13,9 @@
 #include <dirent.h>
 #include <errno.h>
 #include <utime.h>
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
 
 
 #define PCM	(pc->u).maildir
diff --git a/wmbiff/mboxClient.c b/wmbiff/mboxClient.c
index 7ba2072..41fed5e 100644
--- a/wmbiff/mboxClient.c
+++ b/wmbiff/mboxClient.c
@@ -1,4 +1,4 @@
-/* $Id: mboxClient.c,v 1.2 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: mboxClient.c,v 1.3 2001/10/04 09:50:59 jordi Exp $ */
 /* Author:		Yong-iL Joh <tolkien at mizi.com>
    Modified:	Jorge Garc�a <Jorge.Garcia at uv.es>
    			 	Rob Funk <rfunk at funknet.net>
@@ -13,6 +13,9 @@
 #include <sys/stat.h>
 #include <errno.h>
 #include <utime.h>
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
 
 #define PCM	(pc->u).mbox
 #define FROM_STR   "From "
diff --git a/wmbiff/socket.c b/wmbiff/socket.c
index 1aec4f8..4e353ba 100644
--- a/wmbiff/socket.c
+++ b/wmbiff/socket.c
@@ -1,4 +1,4 @@
-/* $Id: socket.c,v 1.2 2001/06/19 03:38:58 dwonis Exp $ */
+/* $Id: socket.c,v 1.3 2001/10/04 09:50:59 jordi Exp $ */
 /* Copyright (C) 1998 Trent Piepho  <xyzzy at u.washington.edu>
  *           (C) 1999 Trent Piepho  <xyzzy at speakeasy.org>
  *
@@ -24,8 +24,11 @@
 #include <netdb.h>
 #include <stdio.h>
 
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
 
-int sock_connect(char *hostname, int port)
+int sock_connect(const char *hostname, int port)
 {
 	struct hostent *host;
 	struct sockaddr_in addr;
diff --git a/wmbiff/tlsComm.c b/wmbiff/tlsComm.c
new file mode 100644
index 0000000..d2a341a
--- /dev/null
+++ b/wmbiff/tlsComm.c
@@ -0,0 +1,416 @@
+/* tlsComm.c - primitive routines to aid TLS communication
+   within wmbiff, without rewriting each mailbox access
+   scheme.  These functions hide whether the underlying
+   transport is encrypted.
+
+   Neil Spring (nspring at cs.washington.edu) */
+
+/* TODO: handle "* BYE" internally? */
+
+#include <stdarg.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#ifdef WITH_TLS
+#include <gnutls.h>
+#endif
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
+#include "tlsComm.h"
+
+/* this is the per-connection state that is maintained for
+   each connection; BIG variables are for ssl (null if not
+   used). */
+#define BUF_SIZE 1024
+struct connection_state {
+	int sd;
+	char *name;
+#ifdef WITH_TLS
+	GNUTLS_STATE state;
+	X509PKI_CLIENT_CREDENTIALS xcred;
+#else
+	/*@null@ */ void *state;
+	/*@null@ */ void *xcred;
+#endif
+	char unprocessed[BUF_SIZE];
+};
+
+/* gotta do our own line buffering, sigh */
+static int
+getline_from_buffer(char *readbuffer, char *linebuffer, int linebuflen);
+void handle_gnutls_read_error(int readbytes, struct connection_state *scs);
+
+void tlscomm_close(struct connection_state *scs)
+{
+	/* not ok to call this more than once */
+	if (scs->state) {
+#ifdef WITH_TLS
+		gnutls_bye(scs->sd, scs->state, GNUTLS_SHUT_RDWR);
+		gnutls_free_x509_client_sc(scs->xcred);
+		gnutls_deinit(scs->state);
+		scs->xcred = NULL;
+#endif
+	} else {
+		(void) close(scs->sd);
+	}
+	scs->sd = -1;
+	scs->state = NULL;
+	scs->xcred = NULL;
+	free(scs->name);
+	scs->name = NULL;
+	free(scs);
+}
+
+/* this avoids blocking without using non-blocking i/o */
+static int wait_for_it(int sd, int timeoutseconds)
+{
+	fd_set readfds;
+	struct timeval tv;
+	tv.tv_sec = timeoutseconds;
+	tv.tv_usec = 0;
+	FD_ZERO(&readfds);
+	FD_SET(sd, &readfds);
+	if (select(sd + 1, &readfds, NULL, NULL, &tv) == 0) {
+#ifdef DEBUG_COMM
+		fprintf(stderr,
+				"select timed out after %d seconds on socket: %d\n",
+				timeoutseconds, sd);
+#endif
+		return (0);
+	}
+	return (FD_ISSET(sd, &readfds));
+}
+
+static int
+getline_from_buffer(char *readbuffer, char *linebuffer, int linebuflen)
+{
+	/* TODO: respect linebuflen */
+	char *p, *q;
+	int i;
+	/* find end of line */
+	for (p = readbuffer, i = 0; *p != '\n' && *p != '\0'; p++, i++);
+	if (i != 0) {
+		/* grab the end of line too! */
+		i++;
+		/* copy a line into the linebuffer */
+		strncpy(linebuffer, readbuffer, (size_t) i);
+		/* sigh, null terminate */
+		linebuffer[i] = '\0';
+		/* shift the rest over; this could be done
+		   instead with strcpy... I think. */
+		q = readbuffer;
+		if (*p != '\0') {
+			p++;
+			do {
+				*(q++) = *(p++);
+			} while (*p != '\0');
+		}
+		/* null terminate */
+		*(q++) = *(p++);
+		/* return the length of the line */
+	}
+	return i;
+}
+
+/* eat lines, until one starting with prefix is found;
+   this skips 'informational' IMAP responses */
+/* the correct response to a return value of 0 is almost
+   certainly tlscomm_close(scs): don't _expect() anything
+   unless anything else would represent failure */
+int tlscomm_expect(struct connection_state *scs,
+				   const char *prefix, char *buf, int buflen)
+{
+	int prefixlen = (int) strlen(prefix);
+	memset(buf, 0, buflen);
+#ifdef DEBUG_COMM
+	fprintf(stderr, "%s: expecting: %s\n", scs->name, prefix);
+#endif
+	while (wait_for_it(scs->sd, 10)) {
+		int readbytes;
+#ifdef WITH_TLS
+		if (scs->state) {
+			readbytes =
+				gnutls_read(scs->sd, scs->state, scs->unprocessed,
+							BUF_SIZE);
+			if (readbytes < 0) {
+				handle_gnutls_read_error(readbytes, scs);
+				return 0;
+			}
+		} else
+#endif
+		{
+			readbytes = read(scs->sd, scs->unprocessed, BUF_SIZE);
+			if (readbytes < 0) {
+				fprintf(stderr, "%s: error reading: %s\n", scs->name,
+						strerror(errno));
+				return 0;
+			}
+		}
+		if (readbytes == 0) {
+			return 0;			/* bummer */
+		} else
+			while (readbytes >= prefixlen) {
+				int linebytes;
+				linebytes =
+					getline_from_buffer(scs->unprocessed, buf, buflen);
+				if (linebytes == 0) {
+					readbytes = 0;
+				} else {
+					readbytes -= linebytes;
+					if (strncmp(buf, prefix, prefixlen) == 0) {
+#ifdef DEBUG_COMM
+						fprintf(stderr, "%s: got: %*s", scs->name,
+								readbytes, buf);
+#endif
+						return 1;	/* got it! */
+					}
+#ifdef DEBUG_COMM
+					fprintf(stderr, "%s: dumped(%d/%d): %.*s", scs->name,
+							linebytes, readbytes, linebytes, buf);
+#endif
+				}
+			}
+	}
+	fprintf(stderr, "%s: expecting: '%s', saw '%s'\n", scs->name, prefix,
+			buf);
+	return 0;					/* wait_for_it failed */
+}
+
+int tlscomm_gets(char *buf, int buflen, struct connection_state *scs)
+{
+	return (tlscomm_expect(scs, "", buf, buflen));
+}
+
+void tlscomm_printf(struct connection_state *scs, const char *format, ...)
+{
+	va_list args;
+	char buf[1024];
+	int bytes;
+
+	if (scs == NULL) {
+		fprintf(stderr, "null connection to tlscomm_printf\n");
+		abort();
+	}
+	va_start(args, format);
+	bytes = vsnprintf(buf, 1024, format, args);
+	va_end(args);
+
+	if (scs->sd != -1) {
+#ifdef WITH_TLS
+		if (scs->state) {
+			int written = gnutls_write(scs->sd, scs->state, buf, bytes);
+			if (written < bytes) {
+				fprintf(stderr, "Error %s prevented writing: %*s\n",
+						gnutls_strerror(written), bytes, buf);
+				return;
+			}
+		} else
+#endif
+			(void) write(scs->sd, buf, bytes);
+	} else {
+		fprintf(stderr,
+				"warning: tlscomm_printf called with an invalid socket descriptor\n");
+		return;
+	}
+#ifdef DEBUG_COMM
+	fprintf(stderr, "wrote %*s", bytes, buf);
+#endif
+}
+
+/* most of this file only makes sense if using TLS. */
+#ifdef WITH_TLS
+
+#ifdef DEBUG_COMM
+/* taken from the GNUTLS documentation; edited to work
+   on more recent versions of gnutls */
+#define PRINTX(x,y) if (y[0]!=0) printf(" -   %s %s\n", x, y)
+#define PRINT_DN(X) PRINTX( "CN:", X->common_name); \
+	PRINTX( "OU:", X->organizational_unit_name); \
+	PRINTX( "O:", X->organization); \
+	PRINTX( "L:", X->locality_name); \
+	PRINTX( "S:", X->state_or_province_name); \
+	PRINTX( "C:", X->country);
+
+static int print_info(GNUTLS_STATE state)
+{
+	const char *tmp;
+	X509PKI_CLIENT_AUTH_INFO x509_info;
+	const gnutls_DN *dn;
+
+	/* print the key exchange's algorithm name
+	 */
+	tmp = gnutls_kx_get_name(gnutls_get_current_kx(state));
+	printf("- Key Exchange: %s\n", tmp);
+
+	/* in case of X509 PKI
+	 */
+	if (gnutls_get_auth_info_type(state) == GNUTLS_X509PKI) {
+		x509_info = gnutls_get_auth_info(state);
+		if (x509_info != NULL) {
+			switch (gnutls_x509pki_client_get_peer_certificate_status
+					(x509_info)) {
+			case GNUTLS_CERT_NOT_TRUSTED:
+				printf("- Peer's X509 Certificate was NOT verified\n");
+				break;
+			case GNUTLS_CERT_EXPIRED:
+				printf
+					("- Peer's X509 Certificate was verified but is expired\n");
+				break;
+			case GNUTLS_CERT_TRUSTED:
+				printf("- Peer's X509 Certificate was verified\n");
+				break;
+			case GNUTLS_CERT_INVALID:
+			default:
+				printf("- Peer's X509 Certificate was invalid\n");
+				break;
+
+			}
+			printf(" - Certificate info:\n");
+			printf(" - Certificate version: #%d\n",
+				   gnutls_x509pki_client_get_peer_certificate_version
+				   (x509_info));
+
+			dn = gnutls_x509pki_client_get_peer_dn(x509_info);
+			PRINT_DN(dn);
+
+			printf(" - Certificate Issuer's info:\n");
+			dn = gnutls_x509pki_client_get_issuer_dn(x509_info);
+			PRINT_DN(dn);
+		}
+	}
+
+	tmp = gnutls_version_get_name(gnutls_get_current_version(state));
+	printf("- Version: %s\n", tmp);
+
+	tmp =
+		gnutls_compression_get_name(gnutls_get_current_compression_method
+									(state));
+	printf("- Compression: %s\n", tmp);
+
+	tmp = gnutls_cipher_get_name(gnutls_get_current_cipher(state));
+	printf("- Cipher: %s\n", tmp);
+
+	tmp = gnutls_mac_get_name(gnutls_get_current_mac_algorithm(state));
+	printf("- MAC: %s\n", tmp);
+
+	return 0;
+}
+#endif
+
+
+
+struct connection_state *initialize_gnutls(int sd, char *name)
+{
+	static int gnutls_initialized;
+	int zok;
+	struct connection_state *ret = malloc(sizeof(struct connection_state));
+
+	if (gnutls_initialized == 0) {
+		gnutls_global_init();
+		gnutls_initialized = 1;
+	}
+
+	assert(gnutls_init(&ret->state, GNUTLS_CLIENT) == 0);
+	assert(gnutls_set_protocol_priority(ret->state, GNUTLS_TLS1,
+										GNUTLS_SSL3, 0) == 0);
+	assert(gnutls_set_cipher_priority(ret->state, GNUTLS_3DES_CBC,
+									  GNUTLS_ARCFOUR, 0) == 0);
+	assert(gnutls_set_compression_priority(ret->state, GNUTLS_ZLIB,
+										   GNUTLS_NULL_COMPRESSION,
+										   0) == 0);
+	assert(gnutls_set_kx_priority(ret->state, GNUTLS_KX_RSA, 0) == 0);
+	assert(gnutls_set_mac_priority(ret->state, GNUTLS_MAC_SHA,
+								   GNUTLS_MAC_MD5, 0) == 0);
+
+	/* no client private key */
+	if (gnutls_allocate_x509_client_sc(&ret->xcred, 0) < 0) {
+		fprintf(stderr, "gnutls memory error\n");
+		exit(1);
+	}
+
+	/* TODO: 
+	   zok =
+	   gnutls_set_x509_client_trust(ret->xcred, "cafile.pem",
+	   "crlfile.pem");
+	   if (zok != 0) {
+	   // printf("error setting client trust\n");
+	   }
+	 */
+
+	gnutls_set_cred(ret->state, GNUTLS_X509PKI, ret->xcred);
+
+	zok = gnutls_handshake(sd, ret->state);
+	if (zok < 0) {
+		fprintf(stderr, "%s: Handshake failed\n", name);
+		fprintf(stderr, "%s: This may be a problem in gnutls, "
+				"which is under development\n", name);
+		fprintf(stderr,
+				"%s: Specifically, problems have been found where the extnValue \n"
+				"  buffer in _gnutls_get_ext_type() in lib/x509_extensions.c is too small in\n"
+				"  gnutls versions up to 0.2.3.  This copy of wmbiff was compiled with \n"
+				"  gnutls version %s.\n", name, LIBGNUTLS_VERSION);
+		gnutls_perror(zok);
+		gnutls_deinit(ret->state);
+		free(ret);
+		return (NULL);
+	} else {
+#ifdef DEBUG_COMM
+		printf("%s: Handshake was completed\n", name);
+		print_info(ret->state);
+#endif
+		ret->sd = sd;
+		ret->name = name;
+	}
+	return (ret);
+}
+
+/* moved down here, to keep from interrupting the flow with
+   verbose error crap */
+void handle_gnutls_read_error(int readbytes, struct connection_state *scs)
+{
+	if (gnutls_is_fatal_error(readbytes) == 1) {
+		fprintf(stderr,
+				"%s: Received corrupted data(%d) - server has terminated the connection abnormally\n",
+				scs->name, readbytes);
+	} else {
+		if (readbytes == GNUTLS_E_WARNING_ALERT_RECEIVED
+			|| readbytes == GNUTLS_E_FATAL_ALERT_RECEIVED)
+			printf("* Received alert [%d]\n",
+				   gnutls_get_last_alert(scs->state));
+		if (readbytes == GNUTLS_E_REHANDSHAKE)
+			printf("* Received HelloRequest message\n");
+	}
+	fprintf(stderr, "%s: error reading: %s\n",
+			scs->name, gnutls_strerror(readbytes));
+}
+
+#else
+/* declare stubs when tls isn't compiled in */
+struct connection_state *initialize_gnutls( /*@unused@ */ int sd,
+										   /*@unused@ */ char *name)
+{
+	fprintf(stderr,
+			"FATAL: tried to initialize ssl when ssl wasn't compiled in.\n");
+	exit(EXIT_FAILURE);
+}
+#endif
+
+/* either way: */
+struct connection_state *initialize_unencrypted(int sd,
+												/*@only@ */ char *name)
+{
+	struct connection_state *ret = malloc(sizeof(struct connection_state));
+	assert(ret != NULL);
+	ret->sd = sd;
+	ret->name = name;
+	ret->state = NULL;
+	ret->xcred = NULL;
+	return (ret);
+}
diff --git a/wmbiff/tlsComm.h b/wmbiff/tlsComm.h
new file mode 100644
index 0000000..78237ac
--- /dev/null
+++ b/wmbiff/tlsComm.h
@@ -0,0 +1,43 @@
+/* tlsComm.h - interface for the thin layer that looks
+   sort of like fgets and fprintf, but might read or write
+   to a socket or a TLS association 
+
+   Neil Spring (nspring at cs.washington.edu)
+
+   Comments in @'s are for lclint's benefit:
+   http://lclint.cs.virginia.edu/
+*/
+
+
+/* opaque reference to the state associated with a 
+   connection: may be just a file handle, or may include
+   encryption state */
+struct connection_state;
+
+/* take a socket descriptor and negotiate a TLS connection
+   over it */
+/*@only@*/
+struct connection_state *initialize_gnutls(int sd, /*@only@ */ char *name);
+
+/* take a socket descriptor and bundle it into a connection
+   state structure for later communication */
+/*@only@*/
+struct connection_state *initialize_unencrypted(int sd,	/*@only@ */
+												char *name);
+
+/* just like fprintf, only takes a connection state structure */
+void tlscomm_printf(struct connection_state *scs, const char *format, ...);
+
+/* modeled after fgets; may not work exactly the same */
+int tlscomm_gets( /*@out@ */ char *buf,
+				 int buflen, struct connection_state *scs);
+
+/* gobbles lines until it finds one starting with {prefix},
+   which is returned in buf */
+int tlscomm_expect(struct connection_state *scs, const char *prefix,
+				   /*@out@ */ char *buf,
+				   int buflen);
+
+/* terminates the TLS association or just closes the socket,
+   and frees the connection state */
+void tlscomm_close( /*@only@ */ struct connection_state *scs);
diff --git a/wmbiff/wmbiff.1 b/wmbiff/wmbiff.1
index 2e1f23f..7d44fd6 100644
--- a/wmbiff/wmbiff.1
+++ b/wmbiff/wmbiff.1
@@ -1,12 +1,12 @@
 .\" Hey, Emacs!  This is an -*- nroff -*- source file.
-.\" $Id: wmbiff.1,v 1.4 2001/10/04 08:54:02 jordi Exp $
+.\" $Id: wmbiff.1,v 1.5 2001/10/04 09:50:59 jordi Exp $
 .\"
 .\" wmbiff.1 and wmbiffrc.5 are copyright 1999-2001 by
 .\" Jordi Mallach <jordi at debian.org>
 .\"
 .\" This is free documentation, see the latest version of the GNU
 .\" General Public License for copying conditions. There is NO warranty.
-.TH WMBIFF 1 "March 12, 2001" "wmbiff"
+.TH WMBIFF 1 "October 4, 2001" "wmbiff"
 
 .SH NAME
 WMBiff \- A dockable Mailbox Monitor
@@ -20,7 +20,7 @@ WMBiff \- A dockable Mailbox Monitor
 WMbiff displays the status of up to five mailboxes. It gives information
 about new mail, if any, or total number of messages. It also has mail
 retrieval capabilies, and can be configured to do this automatically. At the
-moment, UNIX-style, maildir, POP3, and IMAP4 mailboxes are supported.
+moment, UNIX-style, maildir, POP3, APOP and IMAP4 mailboxes are supported.
 WMbiff also supports Licq history files, displaying the number of new
 messages in your running Licq session, as if they were mail.
 
diff --git a/wmbiff/wmbiff.c b/wmbiff/wmbiff.c
index a4604bf..b3256de 100644
--- a/wmbiff/wmbiff.c
+++ b/wmbiff/wmbiff.c
@@ -1,4 +1,4 @@
-/* $Id: wmbiff.c,v 1.6 2001/10/04 08:54:02 jordi Exp $ */
+/* $Id: wmbiff.c,v 1.7 2001/10/04 09:50:59 jordi Exp $ */
 
 #define	USE_POLL
 
@@ -24,6 +24,10 @@
 #include "Client.h"
 #include "charutil.h"
 
+#ifdef USE_DMALLOC
+#include <dmalloc.h>
+#endif
+
 #include "wmbiff-master.xpm"
 char wmbiff_mask_bits[64 * 64];
 int wmbiff_mask_width = 64;
@@ -470,6 +474,8 @@ void parse_mbox_path(int item)
 		licqCreate((&mbox[item]), mbox[item].path);
 	} else if (!strncasecmp(mbox[item].path, "imap:", 5)) {	/* imap4 account */
 		imap4Create((&mbox[item]), mbox[item].path);
+	} else if (!strncasecmp(mbox[item].path, "sslimap:", 8)) {	/* sslimap4 account */
+		imap4Create((&mbox[item]), mbox[item].path);
 	} else if (!strncasecmp(mbox[item].path, "maildir:", 8)) {	/* maildir */
 		maildirCreate((&mbox[item]), mbox[item].path);
 	} else
diff --git a/wmbiff/wmbiffrc.5 b/wmbiff/wmbiffrc.5
index c557547..0f8b563 100644
--- a/wmbiff/wmbiffrc.5
+++ b/wmbiff/wmbiffrc.5
@@ -1,12 +1,12 @@
 .\" Hey, Emacs!  This is an -*- nroff -*- source file.
-.\" $Id: wmbiffrc.5,v 1.4 2001/10/04 08:54:02 jordi Exp $
+.\" $Id: wmbiffrc.5,v 1.5 2001/10/04 09:50:59 jordi Exp $
 .\"
 .\" wmbiff.1 and wmbiffrc.5 are copyright 1999-2001 by 
 .\" Jordi Mallach <jordi at debian.org>
 .\"
 .\" This is free documentation, see the latest version of the GNU
 .\" General Public License for copying conditions. There is NO warranty.
-.TH WMBIFFRC 5 "March 12, 2001" "wmbiff"
+.TH WMBIFFRC 5 "October 4, 2001" "wmbiff"
 
 .SH NAME
 wmbiffrc \- configuration file for
@@ -77,6 +77,18 @@ accepts user, password, host and optional path to mailbox and port number.
 imap:user:password at server[/mailbox][:port]
 .RE
 .TP
+.I sslimap
+These are IMAP4 boxes wrapped in a TLS (SSL) connection, only available if 
+wmbiff was compiled with TLS support.  Parameters are the same as those for 
+ordinary IMAP4 boxes.  Port defaults to 993. If 143 is specified, 
+wmbiff will attempt to connect unencrypted but negotiate TLS using
+IMAP's STARTTLS command.  TLS support uses gnutls, which is under development
+and may be insecure.  TLS support is only for encryption: since certificates
+are not yet checked, it is vulnerable to man-in-the-middle attack.
+.RS
+sslimap:user:password at server[/mailbox][:port]
+.RE
+.TP
 .I licq
 With this box type, wmbiff will read the given history file and track the
 number of messages in it. It just needs a path to a given licq history file.

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-wmaker/wmbiff.git



More information about the Pkg-wmaker-commits mailing list