camel debug log helper tools

Jeffrey Stedfast <[email protected]>
Newsgroups gmane.comp.gnome.evolution.devel
Organization Novell, Inc.
Message-ID <[email protected]>
While sifting thru some camel debug logs yesterday, I wrote a couple
useful tools.

protocol-log.c: this program extracts imap and smtp protocol spew from
the camel debug log (doesn't handle pop, nntp, nor imap4 protocol
spewage because they each use different formats)

imap-state.c: meant for use debugging imap (not imap4, imapp, nor
imapx). adds inline state dumps to the log file stream (dumps message
sequence id -> uid mapping and message flags). also prints out warnings
in a few cases if it notices something fishy going on...

example usage:

./imap-state INBOX.Evolution -

runs 'imap-state', reading from stdin, and watches the INBOX.Evolution
folder and dumps state whenever it changes.

Jeff

-- 
Jeffrey Stedfast
Evolution Hacker - Novell, Inc.
[email protected]  - www.novell.com

_______________________________________________
Evolution-hackers mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/evolution-hackers
protocol-log.c (text/x-csrc, 1.9 KB)
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Jeffrey Stedfast <[email protected]>
 *
 *  Copyright 2006 Novell, Inc. (www.novell.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 *
 */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>


int main (int argc, char **argv)
{
	size_t inleft, insize, offset, n;
	char *inbuf, *inptr, *str;
	FILE *fp;
	
	if (argc < 2 || !strcmp (argv[1], "-")) {
		fp = stdin;
	} else if (!(fp = fopen (argv[1], "rt"))) {
		return 0;
	}
	
	insize = 128;
	inbuf = malloc (insize);
	
	while (!feof (fp)) {
		inleft = insize;
		inptr = inbuf;
		
		while (fgets (inptr, inleft, fp)) {
			n = strlen (inptr);
			inleft -= n;
			inptr += n;
			
			if (n > 0 && inptr[-1] == '\n')
				break;
			
			if (inleft <= 2) {
				insize += 128;
				inleft += 128;
				offset = inptr - inbuf;
				inbuf = realloc (inbuf, insize);
				inptr = inbuf + offset;
			}
		}
		
		if (inleft == insize)
			break;
		
		if ((str = strstr (inbuf, "sending : "))) {
			fputs (str, stdout);
			continue;
		}
		
		if ((str = strstr (inbuf, "received: "))) {
			fputs (str, stdout);
			continue;
		}
	}
	
	free (inbuf);
	
	if (fp != stdin)
		fclose (fp);
	
	return 0;
}
imap-state.c (text/x-csrc, 11.6 KB)
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Jeffrey Stedfast <[email protected]>
 *
 *  Copyright 2006 Novell, Inc. (www.novell.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 *
 */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <errno.h>


enum {
	ANSWERED = (1 << 0),
	DELETED  = (1 << 1),
	DRAFT    = (1 << 2),
	FLAGGED  = (1 << 3),
	RECENT   = (1 << 4),
	SEEN     = (1 << 5),
};

static struct {
	const char *name;
	size_t namelen;
	unsigned int flag;
} imap_flags[] = {
	{ "\\Answered", 9, ANSWERED },
	{ "\\Deleted",  8, DELETED  },
	{ "\\Draft",    6, DRAFT    },
	{ "\\Flagged",  8, FLAGGED  },
	{ "\\Recent",   7, RECENT   },
	{ "\\Seen",     5, SEEN     },
};

static unsigned int
parse_flags (const char *flags_list)
{
	register const char *inptr = flags_list;
	unsigned int flags = 0;
	int i;
	
	if (*inptr != '(')
		return 0;
	
	inptr++;
	while (*inptr != ')') {
		while (*inptr == ' ')
			inptr++;
		
		if (*inptr == '\\') {
			for (i = 0; i < (sizeof (imap_flags) / sizeof (imap_flags[0])); i++) {
				if (!strncmp (imap_flags[i].name, inptr, imap_flags[i].namelen)) {
					inptr += imap_flags[i].namelen;
					flags |= imap_flags[i].flag;
					break;
				}
			}
		}
		
		while (*inptr != ' ' && *inptr != ')')
			inptr++;
	}
	
	return flags;
}

static void
write_flags (unsigned int flags)
{
	int space = 0;
	size_t i;
	
	for (i = 0; i < (sizeof (imap_flags) / sizeof (imap_flags[0])); i++) {
		if (flags & imap_flags[i].flag) {
			if (space)
				fputs (" ", stdout);
			fputs (imap_flags[i].name, stdout);
			space = 1;
		}
	}
}

struct msg {
	int known;
	unsigned int uid;
	unsigned int flags;
};

struct folder {
	const char *name;
	short selecting;
	short selected;
	
	struct msg **msgs;  /* message array */
	size_t mmsgs;       /* max number of messages our array can hold */
	size_t nmsgs;       /* number of messages in array */
};

static void
folder_free (struct folder *folder)
{
	size_t i;
	
	for (i = 0; i < folder->mmsgs; i++)
		free (folder->msgs[i]);
	free (folder->msgs);
}

static void
folder_reset (struct folder *folder)
{
	size_t i;
	
	for (i = 0; i < folder->nmsgs; i++) {
		folder->msgs[i]->known = 0;
		folder->msgs[i]->flags = 0;
		folder->msgs[i]->uid = 0;
	}
	
	folder->nmsgs = 0;
}

enum {
	FLAGS,
	PLUS_FLAGS,
	MINUS_FLAGS
};

static void
set_flags (struct msg *msg, int mode, unsigned int flags)
{
	switch (mode) {
	case FLAGS:
		msg->flags = flags;
		break;
	case PLUS_FLAGS:
		msg->flags |= flags;
		break;
	case MINUS_FLAGS:
		msg->flags &= ~flags;
		break;
	}
}

static void
store_flags (struct folder *folder, int uids, const char *set, int mode, unsigned int flags)
{
        size_t min, max, cur, i;
	char *end;
	
	while (1) {
		cur = strtoul (set, &end, 10);
		if (*end == ',' || *end == ' ') {
			/* singleton message */
			if (uids) {
				i = 0;
				while (i < folder->nmsgs && cur != folder->msgs[i]->uid)
					i++;
				
				if (i < folder->nmsgs)
					set_flags (folder->msgs[i], mode, flags);
			} else {
				if (cur > 0 && cur < folder->nmsgs)
					set_flags (folder->msgs[cur - 1], mode, flags);
			}
		} else if (*end == ':') {
			/* message range */
			set = end + 1;
			max = strtoul (set, &end, 10);
			
			if (cur < max) {
				min = cur;
			} else {
				min = max;
				max = cur;
			}
			
			if (uids) {
				i = 0;
				while (i < folder->nmsgs && folder->msgs[i]->uid < min)
					i++;
				
				while (i < folder->nmsgs && folder->msgs[i]->uid <= max)
					set_flags (folder->msgs[i++], mode, flags);
			} else {
				for (i = min - 1; i < max && i < folder->nmsgs; i++)
					set_flags (folder->msgs[i], mode, flags);
			}
		} else {
			break;
		}
		
		if (*end != ',')
			break;
		
		set = end + 1;
	}
}

static void
untagged_expunge (struct folder *folder, size_t index)
{
	size_t i, j;
	
	if (index > folder->nmsgs) {
		fprintf (stdout, "\n\tWARNING: expunging message %u; out of range\n\n", index);
		return;
	}
	
	if (!(folder->msgs[index - 1]->flags & DELETED))
		fprintf (stdout, "\n\tWARNING: expunging undeleted message %u!?!?!?\n\n", index);
	else if (!folder->msgs[index - 1]->known)
		fprintf (stdout, "\n\tWARNING: expunging unknown message %u\n\n", index);
	
	if (folder->nmsgs > index) {
		for (i = index - 1, j = index; j < folder->nmsgs; i++, j++)
			memcpy (folder->msgs[i], folder->msgs[j], sizeof (struct msg));
	}
	
	folder->nmsgs--;
}

static void
untagged_fetch_flags (struct folder *folder, size_t index, unsigned int uid, unsigned int flags)
{
	struct msg *msg;
	size_t i;
	
	if (index > folder->mmsgs) {
		if (folder->msgs)
			folder->msgs = realloc (folder->msgs, (index + 100) * sizeof (struct msg *));
		else
			folder->msgs = malloc ((index + 100) * sizeof (struct msg *));
		folder->mmsgs = index + 100;
		
		for (i = folder->nmsgs; i < folder->mmsgs; i++) {
			msg = folder->msgs[i] = malloc (sizeof (struct msg));
			msg->known = 0;
			msg->flags = 0;
			msg->uid = 0;
		}
	}
	
	if (folder->nmsgs < index)
		folder->nmsgs = index;
	
	msg = folder->msgs[index - 1];
	if (uid != 0) {
		msg->known = 1;
		msg->uid = uid;
		msg->flags = flags;
	} else {
		if (!msg->known)
			fprintf (stdout, "\n\tWARNING: getting flag updates for unknown msg %u\n\n", index);
		msg->flags = flags;
	}
}

static void
write_folder_state (struct folder *folder)
{
	size_t i;
	
	for (i = 0; i < folder->nmsgs; i++) {
		if (folder->msgs[i]->known) {
			printf ("\t* %u UID %u FLAGS (", i + 1, folder->msgs[i]->uid);
			write_flags (folder->msgs[i]->flags);
			fputs (")\n", stdout);
		} else {
			printf ("\t* %u UID ??? FLAGS (", i + 1);
			write_flags (folder->msgs[i]->flags);
			fputs (")\n", stdout);
		}
	}
}

int main (int argc, char **argv)
{
	size_t inleft, insize, offset, n, index;
	const char *progname;
	struct folder folder;
	char *inbuf, *inptr;
	int mode, uids, log;
	unsigned int flags;
	unsigned int uid;
	const char *set;
	char tag = 'A';
	FILE *fp;
	
	if (!(progname = strrchr (argv[0], '/')))
		progname = argv[0];
	else
		progname++;
	
	if (argc < 2 || argc > 4 || (argc == 4 && !(argv[1][0] >= 'A' && argv[1][0] <= 'Z') && argv[1][1] != '\0')) {
		printf ("Usage: %s [[TAG] Folder] filename\n", progname);
		return 0;
	}
	
	if (argc == 4) {
		folder.name = argv[2];
		tag = argv[1][0];
	} else if (argc == 3) {
		folder.name = argv[1];
	} else {
		folder.name = "INBOX";
	}
	
	folder.selecting = 0;
	folder.selected = 0;
	folder.msgs = NULL;
	folder.mmsgs = 0;
	folder.nmsgs = 0;
	
	if (!strcmp (argv[argc - 1], "-")) {
		fp = stdin;
	} else {
		if (!(fp = fopen (argv[argc - 1], "rt"))) {
			fprintf (stderr, "%s: fopen: `%s': %s\n", progname, argv[argc - 1], strerror (errno));
			return 0;
		}
	}
	
	insize = 128;
	inbuf = malloc (insize);
	
	while (!feof (fp)) {
		inleft = insize;
		inptr = inbuf;
		log = 0;
		
		while (fgets (inptr, inleft, fp)) {
			n = strlen (inptr);
			inleft -= n;
			inptr += n;
			
			if (n > 0 && inptr[-1] == '\n') {
				if (strncmp (inbuf, "received: ", 10) != 0 || (inptr[-2] != '}' && inptr[-3] != '}'))
					break;
			}
			
			if (inleft <= 2) {
				insize += 128;
				inleft += 128;
				offset = inptr - inbuf;
				inbuf = realloc (inbuf, insize);
				inptr = inbuf + offset;
			}
		}
		
		if (inleft == insize)
			break;
		
		fputs (inbuf, stdout);
		
		if (!strncmp (inbuf, "sending : ", 10) && inbuf[10] == tag) {
			inptr = inbuf + 10;
			while (*inptr == ' ')
				inptr++;
			
			/* skip past tag */
			while (*inptr && *inptr != ' ')
				inptr++;
			while (*inptr == ' ')
				inptr++;
			
			if (!strncmp (inptr, "SELECT", 6)) {
				inptr += 6;
				while (*inptr == ' ')
					inptr++;
				
				if (!strncmp (inptr, folder.name, strlen (folder.name)))
					folder.selecting = 1;
				else
					folder.selecting = 0;
				
				goto loop;
			}
			
			if (!folder.selected)
				goto loop;
			
			if (!strncmp (inptr, "UID", 3)) {
				inptr += 3;
				while (*inptr == ' ')
					inptr++;
				uids = 1;
			} else
				uids = 0;
			
			if (!strncmp (inptr, "STORE", 5)) {
				inptr += 5;
				while (*inptr == ' ')
					inptr++;
				
				set = inptr;
				while (*inptr && *inptr != ' ')
					inptr++;
				while (*inptr == ' ')
					inptr++;
				
				if (!strncmp (inptr, "+FLAGS", 6)) {
					mode = PLUS_FLAGS;
					inptr += 6;
				} else if (!strncmp (inptr, "-FLAGS", 6)) {
					mode = MINUS_FLAGS;
					inptr += 6;
				} else if (!strncmp (inptr, "FLAGS", 5)) {
					mode = FLAGS;
					inptr += 5;
				} else {
					goto loop;
				}
				
				if (strncmp (inptr, ".SILENT", 7) != 0) {
					/* we'll get untagged responses to update our state */
					goto loop;
				}
				
				inptr += 7;
				while (*inptr == ' ')
					inptr++;
				
				if (*inptr != '(')
					goto loop;
				
				flags = parse_flags (inptr);
				store_flags (&folder, uids, set, mode, flags);
				log = 1;
			}
		} else if (!strncmp (inbuf, "received: ", 10)) {
			inptr = inbuf + 10;
			while (*inptr == ' ')
				inptr++;
			
			if (*inptr == '*' && folder.selected) {
				inptr++;
				while (*inptr == ' ')
					inptr++;
				
				if ((index = strtoul (inptr, &inptr, 10)) == 0) {
					/* "* 0 RECENT" or "* 0 EXISTS" perhaps? */
					goto loop;
				}
				
				while (*inptr == ' ')
					inptr++;
				
				if (!strncmp (inptr, "FETCH", 5)) {
					inptr += 5;
					while (*inptr == ' ')
						inptr++;
					
					offset = inptr - inbuf;
					if ((inptr = strstr (inptr, "UID ")))
						uid = strtoul (inptr + 4, NULL, 10);
					else
						uid = 0;
					
					inptr = inbuf + offset;
					if ((inptr = strstr (inptr, "FLAGS ("))) {
						flags = parse_flags (inptr + 6);
						untagged_fetch_flags (&folder, index, uid, flags);
						log = 1;
					}
				} else if (!strncmp (inptr, "EXPUNGE", 7)) {
					untagged_expunge (&folder, index);
					log = 1;
				}
			} else if (*inptr == tag) {
				/* tagged response */
				while (*inptr && *inptr != ' ')
					inptr++;
				while (*inptr == ' ')
					inptr++;
				
				if (!strncmp (inptr, "OK ", 3)) {
					inptr += 3;
					while (*inptr == ' ')
						inptr++;
					
					/* skip over [READ-WRITE] or [READ-ONLY] RESP-CODE */
					if (*inptr == '[') {
						while (*inptr && *inptr != ' ')
							inptr++;
						while (*inptr == ' ')
							inptr++;
					}
					
					if (!strncmp (inptr, "SELECT", 6)) {
						if (folder.selecting) {
							folder.selecting = 0;
							folder.selected = 1;
							
							folder_reset (&folder);
						} else {
							/* another folder got selected */
							folder.selected = 0;
						}
					}
				} else if (!strncmp (inptr, "NO ", 3)) {
					if (folder.selecting && strstr (inptr, "SELECT"))
						folder.selecting = 0;
				} else if (!strncmp (inptr, "BAD ", 4)) {
					if (folder.selecting && strstr (inptr, "SELECT"))
						folder.selecting = 0;
				}
			}
		}
		
	loop:
		
		if (log) {
			inptr = inbuf + 10;
			while (*inptr == ' ')
				inptr++;
			while (*inptr && *inptr != ' ')
				inptr++;
			*inptr = '\0';
			inptr = inbuf + 10;
			while (*inptr == ' ')
				inptr++;
			
			printf ("\n\tCurrent state of %s:\n", folder.name);
			write_folder_state (&folder);
			fputs ("\n\n", stdout);
			fflush (stdout);
		}
	}
	
	folder_free (&folder);
	
	free (inbuf);
	
	if (fp != stdin)
		fclose (fp);
	
	return 0;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.