added generic IMAP list parser and rewrote imap_exec() to handle

arbitrary data instead of hardcoded
This commit is contained in:
Michael Elkins 2000-12-21 10:24:53 +00:00
parent 0addaad032
commit ba7650c9b7
6 changed files with 332 additions and 67 deletions

View File

@ -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)

13
README
View File

@ -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
TODO
View File

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

184
imap.c
View File

@ -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)
if (!strcmp ("EXISTS", arg1))
imap->count = atoi (arg);
else if (!strcmp ("RECENT", arg1))
imap->recent = atoi (arg);
else if (!strcmp ("FETCH", arg1))
{
cur = &imap->msgs;
while (*cur)
cur = &(*cur)->next;
}
list_t *list;
/* 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))
{
printf ("FETCH parse error: expected FLAGS at %s\n", arg);
return -1;
}
/* if we need to parse additional info, we should keep
* a copy of this `arg' pointer
*/
cmd++;
arg = strchr (cmd, ')');
if (!arg)
{
puts ("FETCH parse error");
return -1;
}
*arg = 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))
if (!cur)
{
(*cur)->flags |= D_DELETED;
imap->deleted++;
cur = &imap->msgs;
while (*cur)
cur = &(*cur)->next;
}
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;
list = parse_list (cmd, 0);
*cur = calloc (1, sizeof(message_t));
if (parse_fetch (imap, list, *cur))
{
free_list (list);
return -1;
}
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
isync.h
View File

@ -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
list.c Normal file
View File

@ -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