added generic IMAP list parser and rewrote imap_exec() to handle
arbitrary data instead of hardcoded
This commit is contained in:
parent
0addaad032
commit
ba7650c9b7
|
@ -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
13
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
TODO
2
TODO
|
@ -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
184
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)
|
||||
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
23
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
list.c
Normal file
175
list.c
Normal 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
|
Loading…
Reference in New Issue
Block a user