[Evolution-hackers] camel debug log helper tools



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.
fejj novell com  - www.novell.com
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Jeffrey Stedfast <fejj novell com>
 *
 *  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;
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Jeffrey Stedfast <fejj novell com>
 *
 *  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;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]