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; }