Pārlūkot izejas kodu

added generic IMAP list parser and rewrote imap_exec() to handle
arbitrary data instead of hardcoded

Michael Elkins 24 gadi atpakaļ
vecāks
revīzija
ba7650c9b7
6 mainītis faili ar 330 papildinājumiem un 65 dzēšanām
  1. 1 1
      Makefile.am
  2. 11 2
      README
  3. 2 0
      TODO
  4. 118 62
      imap.c
  5. 23 0
      isync.h
  6. 175 0
      list.c

+ 1 - 1
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)
 

+ 11 - 2
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)

+ 2 - 0
TODO

@@ -1 +1,3 @@
 add upload support to mirror local msgs on the server
+
+add support for syncing with other: and shared: via NAMESPACE

+ 118 - 62
imap.c

@@ -149,6 +149,67 @@ buffer_gets (buffer_t * b, char **s)
     /* not reached */
 }
 
+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, ...)
 {
@@ -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);
     }

+ 23 - 0
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);

+ 175 - 0
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