This patch logs raw socket data, to assist debugging when discriminating
between server and fetchmail bugs.

Apply it to fetchmail 6.3.20 and set the environment variable
FETCHMAIL_RAW_LOGFILE to a log file writable by fetchmail. If it's not
there, it gets created with mode 0600 (which requires directory write
permission).

The file gets appended to, so you can log into named pipes, character
(stream) devices and to the console if you're so inclined.

Note 1: any logging failures cause fetchmail to abort() forcefully.

Note 2: non-printable characters are hex-escaped, so it is safe to use
FETCHMAIL_RAW_LOGFILE=/dev/stderr or similar.

-- Matthias Andree, August 2011

diff --git a/sink.c b/sink.c
index 5d92556..ff6208d 100644
--- a/sink.c
+++ b/sink.c
@@ -649,6 +649,10 @@ int stuffline(struct query *ctl, char *buf)
     while ((last += strlen(last)) && (last[-1] != '\n'))
         last++;
 
+    if (outlevel >= O_DEBUG && (size_t)(last - buf) != strlen(buf))
+	report(stdout, GT_("DEBUG: stuffline shipping line with NULs, length=%lu, strlen=%lu\n"), last - buf, strlen(buf));
+
+
     /* fix message lines that have only \n termination (for qmail) */
     if (ctl->forcecr)
     {
diff --git a/socket.c b/socket.c
index e338207..dcaf19d 100644
--- a/socket.c
+++ b/socket.c
@@ -381,6 +381,49 @@ static	SSL *_ssl_context[FD_SETSIZE];
 static SSL	*SSLGetContext( int );
 #endif /* SSL_ENABLE */
 
+#include <fcntl.h>
+
+static const char *rawlogfile;
+static FILE *rlogstream;
+
+static int SockLog(void) {
+    static int haveinit;
+    static int wantlog;
+    static int logfd = -1;
+
+    if (!haveinit) {
+	haveinit = 1;
+	if ((rawlogfile = getenv("FETCHMAIL_RAW_LOGFILE"))) {
+	    if ((logfd = open(rawlogfile, O_WRONLY|O_APPEND|O_CREAT, 0600)) == -1) {
+		report(stderr, GT_("FETCHMAIL_RAW_LOGFILE is set, but opening \"%s\" for appending write failed: %s\n"), rawlogfile, strerror(errno));
+		abort();
+	    }
+	    if (!(rlogstream = fdopen(logfd, "a"))) {
+		report(stderr, GT_("FETCHMAIL_RAW_LOGFILE is set, but fdopen(%d) failed: %s\n"), logfd, strerror(errno));
+		abort();
+	    }
+	    setvbuf(rlogstream, NULL, _IOLBF, 0);
+	    wantlog = 1;
+	}
+    }
+    return wantlog;
+}
+
+static void LogPrintf(const char *fmt, ...) {
+    va_list va;
+    const char *locsav;
+    va_start(va, fmt);
+    if (!SockLog()) return;
+    locsav = setlocale(LC_ALL, NULL);
+    (void)setlocale(LC_ALL, "C");
+    if (EOF == vfprintf(rlogstream, fmt, va) || EOF == fflush(rlogstream)) {
+	report(stderr, GT_("FETCHMAIL_RAW_LOGFILE is set, but logging failed: %s\n"), strerror(errno));
+	abort();
+    }
+    (void)setlocale(LC_ALL, locsav);
+    va_end(va);
+}
+
 int SockWrite(int sock, const char *buf, int len)
 {
     int n, wrlen = 0;
@@ -388,6 +431,12 @@ int SockWrite(int sock, const char *buf, int len)
     SSL *ssl;
 #endif
 
+    if (SockLog()) {
+	char *tmps = sdump(buf, len);
+	LogPrintf("[>%d-%s count=%04d] %s\n", sock, SSLGetContext(sock) ? "crypt" : "plain", len, tmps);
+	free(tmps);
+    }
+
     while (len)
     {
 #ifdef SSL_ENABLE
@@ -504,6 +553,13 @@ int SockRead(int sock, char *buf, int len)
 	    (!newline && len);
     *bp = '\0';
 
+    if (SockLog())
+    {
+	char *tmps = sdump(buf, bp - buf);
+	LogPrintf("[<%d-%s count=%04d] %s\n", sock, SSLGetContext(sock) ? "crypt" : "plain", bp - buf, tmps);
+	free(tmps);
+    }
+
     return bp - buf;
 }
 
