commit ba7650c9b748b5180a65469d164fbce0f6a09ae8
parent 0addaad0321ac1faedea60a2b8ff8f6c2ea01816
Author: Michael Elkins <me@mutt.org>
Date: Thu, 21 Dec 2000 10:24:53 +0000
added generic IMAP list parser and rewrote imap_exec() to handle
arbitrary data instead of hardcoded
Diffstat:
M | Makefile.am | | | 2 | +- |
M | README | | | 13 | +++++++++++-- |
M | TODO | | | 2 | ++ |
M | imap.c | | | 180 | ++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------- |
M | isync.h | | | 23 | +++++++++++++++++++++++ |
A | list.c | | | 175 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
6 files changed, 330 insertions(+), 65 deletions(-)
diff --git a/Makefile.am b/Makefile.am
@@ -1,5 +1,5 @@
bin_PROGRAMS=isync
-isync_SOURCES=main.c imap.c sync.c maildir.c isync.h
+isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c
man_MANS=isync.1
EXTRA_DIST=sample.isyncrc $(man_MANS)
diff --git a/README b/README
@@ -16,9 +16,10 @@ maintained, and all flags are synchronized.
* Features:
- * Supports imaps: (port 993) TLS/SSL connections
- * Supports STARTTLS
* Fast mode for fetching new mail only
+ * Supports imaps: (port 993) TLS/SSL connections
+ * Supports STARTTLS (RFC2595)
+ * Supports NAMESPACE (RFC2342)
* Compatibility
@@ -26,6 +27,14 @@ maintained, and all flags are synchronized.
* Microsoft Exchange 2000 IMAP4rev1 server version 6.0.4417.0
+* Platforms
+
+ ``isync'' has successfully be compiled under:
+
+ * Linux 2.2.18
+ * Solaris 2.7
+ * OpenBSD 2.8
+
* Requirements
OpenSSL for TLS/SSL support (optional)
diff --git a/TODO b/TODO
@@ -1 +1,3 @@
add upload support to mirror local msgs on the server
+
+add support for syncing with other: and shared: via NAMESPACE
diff --git a/imap.c b/imap.c
@@ -150,6 +150,67 @@ buffer_gets (buffer_t * b, char **s)
}
static int
+parse_fetch (imap_t * imap, list_t * list, message_t *cur)
+{
+ list_t *tmp;
+
+ if (!is_list (list))
+ return -1;
+
+ for (tmp = list->child; tmp; tmp = tmp->next)
+ {
+ if (is_atom (tmp))
+ {
+ if (!strcmp ("UID", tmp->val))
+ {
+ tmp = tmp->next;
+ if (is_atom (tmp))
+ cur->uid = atoi (tmp->val);
+ else
+ puts ("Error, unable to parse UID");
+ }
+ else if (!strcmp ("FLAGS", tmp->val))
+ {
+ tmp = tmp->next;
+ if (is_list (tmp))
+ {
+ list_t *flags = tmp->child;
+
+ for (; flags; flags = flags->next)
+ {
+ if (is_atom (flags))
+ {
+ if (!strcmp ("\\Seen", flags->val))
+ cur->flags |= D_SEEN;
+ else if (!strcmp ("\\Flagged", flags->val))
+ cur->flags |= D_FLAGGED;
+ else if (!strcmp ("\\Deleted", flags->val))
+ {
+ cur->flags |= D_DELETED;
+ imap->deleted++;
+ }
+ else if (!strcmp ("\\Answered", flags->val))
+ cur->flags |= D_ANSWERED;
+ else if (!strcmp ("\\Draft", flags->val))
+ cur->flags |= D_DRAFT;
+ else if (!strcmp ("\\Recent", flags->val))
+ cur->flags |= D_RECENT;
+ else
+ printf ("Warning, unknown flag %s\n",flags->val);
+ }
+ else
+ puts ("Error, unable to parse FLAGS list");
+ }
+ }
+ else
+ puts ("Error, unable to parse FLAGS");
+ }
+ }
+ }
+ return 0;
+}
+
+static int
imap_exec (imap_t * imap, const char *fmt, ...)
{
va_list ap;
@@ -181,12 +242,18 @@ imap_exec (imap_t * imap, const char *fmt, ...)
if (*arg == '*')
{
arg = next_arg (&cmd);
- arg1 = next_arg (&cmd);
+ if (!arg)
+ {
+ puts ("Error, unable to parse untagged command");
+ return -1;
+ }
- if (arg1 && !strcmp ("EXISTS", arg1))
- imap->count = atoi (arg);
- else if (arg1 && !strcmp ("RECENT", arg1))
- imap->recent = atoi (arg);
+ if (!strcmp ("NAMESPACE", arg))
+ {
+ imap->ns_personal = parse_list (cmd, &cmd);
+ imap->ns_other = parse_list (cmd, &cmd);
+ imap->ns_shared = parse_list (cmd, 0);
+ }
else if (!strcmp ("SEARCH", arg))
{
if (!rec)
@@ -195,10 +262,6 @@ imap_exec (imap_t * imap, const char *fmt, ...)
while (*rec)
rec = &(*rec)->next;
}
- /* need to add arg1 */
- *rec = calloc (1, sizeof (message_t));
- (*rec)->uid = atoi (arg1);
- rec = &(*rec)->next;
/* parse rest of `cmd' */
while ((arg = next_arg (&cmd)))
{
@@ -207,66 +270,41 @@ imap_exec (imap_t * imap, const char *fmt, ...)
rec = &(*rec)->next;
}
}
- else if (arg1 && !strcmp ("FETCH", arg1))
+ else if ((arg1 = next_arg (&cmd)))
{
- if (!cur)
- {
- cur = &imap->msgs;
- while (*cur)
- cur = &(*cur)->next;
- }
-
- /* new message
- * * <N> FETCH (UID <uid> FLAGS (...))
- */
- arg = next_arg (&cmd); /* (UID */
- arg = next_arg (&cmd); /* <uid> */
- *cur = calloc (1, sizeof (message_t));
- (*cur)->uid = atoi (arg);
-
- arg = next_arg (&cmd); /* FLAGS */
- if (!arg || strcmp ("FLAGS", arg))
+ if (!strcmp ("EXISTS", arg1))
+ imap->count = atoi (arg);
+ else if (!strcmp ("RECENT", arg1))
+ imap->recent = atoi (arg);
+ else if (!strcmp ("FETCH", arg1))
{
- printf ("FETCH parse error: expected FLAGS at %s\n", arg);
- return -1;
- }
+ list_t *list;
- /* if we need to parse additional info, we should keep
- * a copy of this `arg' pointer
- */
+ if (!cur)
+ {
+ cur = &imap->msgs;
+ while (*cur)
+ cur = &(*cur)->next;
+ }
- cmd++;
- arg = strchr (cmd, ')');
- if (!arg)
- {
- puts ("FETCH parse error");
- return -1;
- }
- *arg = 0;
+ list = parse_list (cmd, 0);
- /* parse message flags */
- while ((arg = next_arg (&cmd)))
- {
- if (!strcmp ("\\Seen", arg))
- (*cur)->flags |= D_SEEN;
- else if (!strcmp ("\\Flagged", arg))
- (*cur)->flags |= D_FLAGGED;
- else if (!strcmp ("\\Deleted", arg))
+ *cur = calloc (1, sizeof(message_t));
+ if (parse_fetch (imap, list, *cur))
{
- (*cur)->flags |= D_DELETED;
- imap->deleted++;
+ free_list (list);
+ return -1;
}
- else if (!strcmp ("\\Answered", arg))
- (*cur)->flags |= D_ANSWERED;
- else if (!strcmp ("\\Draft", arg))
- (*cur)->flags |= D_DRAFT;
- else if (!strcmp ("\\Recent", arg))
- (*cur)->flags |= D_RECENT;
- else
- printf ("warning, unknown flag %s\n", arg);
- }
- cur = &(*cur)->next;
+ free_list (list);
+
+ cur = &(*cur)->next;
+ }
+ }
+ else
+ {
+ puts ("Error, unable to parse untagged command");
+ return -1;
}
}
else if ((size_t) atol (arg) != Tag)
@@ -344,6 +382,7 @@ imap_open (config_t * box, int fast)
int s;
struct sockaddr_in sin;
struct hostent *he;
+ char *ns_prefix = 0;
#if HAVE_LIBSSL
int use_ssl = 0;
#endif
@@ -427,11 +466,28 @@ imap_open (config_t * box, int fast)
puts ("Logging in...");
ret = imap_exec (imap, "LOGIN %s %s", box->user, box->pass);
+
+ if (!ret)
+ {
+ /* get NAMESPACE info */
+ if (!imap_exec (imap, "NAMESPACE"))
+ {
+ /* XXX for now assume personal namespace */
+ if (is_list (imap->ns_personal) &&
+ is_list(imap->ns_personal->child) &&
+ is_atom(imap->ns_personal->child->child))
+ {
+ ns_prefix = imap->ns_personal->child->child->val;
+ }
+ }
+ }
+
if (!ret)
{
fputs ("Selecting mailbox... ", stdout);
fflush (stdout);
- ret = imap_exec (imap, "SELECT %s", box->box);
+ ret = imap_exec (imap, "SELECT %s%s",
+ ns_prefix ? ns_prefix : "", box->box);
if (!ret)
printf ("%d messages, %d recent\n", imap->count, imap->recent);
}
diff --git a/isync.h b/isync.h
@@ -92,6 +92,18 @@ struct message
unsigned int dead:1; /* message doesn't exist on the server */
};
+/* struct used for parsing IMAP lists */
+typedef struct _list list_t;
+
+#define NIL (void*)0x1
+#define LIST (void*)0x2
+
+struct _list {
+ char *val;
+ list_t *next;
+ list_t *child;
+};
+
/* imap connection info */
typedef struct
{
@@ -105,6 +117,10 @@ typedef struct
* UID to be used in a FETCH FLAGS command
*/
unsigned int deleted; /* # of deleted messages */
+ /* NAMESPACE info */
+ list_t *ns_personal;
+ list_t *ns_other;
+ list_t *ns_shared;
}
imap_t;
@@ -135,3 +151,10 @@ imap_t *imap_open (config_t *, int);
mailbox_t *maildir_open (const char *, int fast);
int maildir_expunge (mailbox_t *, int);
int maildir_sync (mailbox_t *);
+
+/* parse an IMAP list construct */
+list_t * parse_list (char *s, char **end);
+int is_atom (list_t *list);
+int is_list (list_t *list);
+int is_nil (list_t *list);
+void free_list (list_t *list);
diff --git a/list.c b/list.c
@@ -0,0 +1,175 @@
+/* $Id$
+ *
+ * isync - IMAP4 to maildir mailbox synchronizer
+ * Copyright (C) 2000 Michael R. Elkins <me@mutt.org>
+ *
+ * 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 Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "isync.h"
+
+static char *
+skip_string (char *s)
+{
+ while (*s && *s != '"')
+ s++;
+ return s;
+}
+
+list_t *
+parse_list (char *s, char **end)
+{
+ int level = 1;
+ list_t *cur;
+ list_t **list;
+ char *b;
+
+ cur = calloc (1, sizeof (list_t));
+ while (isspace ((unsigned char) *s))
+ s++;
+ if (*s == '(')
+ {
+ /* start of list. find the end of the list */
+ s++;
+ b = s; /* save beginning */
+ cur->val = LIST;
+ while (*s)
+ {
+ if (*s == '(')
+ {
+ level++;
+ }
+ else if (*s == ')')
+ {
+ level--;
+ if (level == 0)
+ break;
+ }
+ else if (*s == '"')
+ {
+ s = skip_string (s + 1);
+ if (!*s)
+ {
+ /* parse error */
+ free (cur);
+ return NULL;
+ }
+ }
+ s++;
+ }
+ if (level != 0)
+ {
+ free (cur); /* parse error */
+ return NULL;
+ }
+ *s++ = 0;
+
+ list = &cur->child;
+ while (*b)
+ {
+ *list = parse_list (b, &b);
+ if (*list == NULL)
+ {
+ /* parse error */
+ free (cur);
+ return NULL;
+ }
+ while (*list)
+ list = &(*list)->next;
+ }
+ }
+ else if (*s == '"')
+ {
+ /* quoted string */
+ s++;
+ cur->val = s;
+ s = skip_string (s);
+ if (!*s)
+ {
+ /* parse error */
+ free (cur);
+ return NULL;
+ }
+ *s++ = 0;
+ cur->val = strdup (cur->val);
+ }
+ else
+ {
+ /* atom */
+ cur->val = s;
+ while (*s && !isspace ((unsigned char) *s))
+ s++;
+ if (*s)
+ *s++ = 0;
+ if (strcmp ("NIL", cur->val))
+ cur->val = strdup (cur->val);
+ else
+ cur->val = NIL;
+ }
+ if (end)
+ *end = s;
+ return cur;
+}
+
+int
+is_atom (list_t * list)
+{
+ return (list && list->val && list->val != NIL && list->val != LIST);
+}
+
+int
+is_list (list_t * list)
+{
+ return (list && list->val == LIST);
+}
+
+int
+is_nil (list_t * list)
+{
+ return (list && list->val == NIL);
+}
+
+void
+free_list (list_t * list)
+{
+ list_t *tmp;
+
+ while (list)
+ {
+ tmp = list;
+ list = list->next;
+ if (is_list (list))
+ free_list (tmp->child);
+ else if (is_atom (tmp))
+ free (tmp->val);
+ free (tmp);
+ }
+}
+
+#if TEST
+int
+main (int argc, char **argv)
+{
+ char buf[256];
+ list_t *list;
+
+ strcpy (buf,
+ "((compound list) atom NIL \"string with a (\" (another list))");
+ list = parse_list (buf, 0);
+}
+#endif