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
|
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
|
man_MANS=isync.1
|
||||||
EXTRA_DIST=sample.isyncrc $(man_MANS)
|
EXTRA_DIST=sample.isyncrc $(man_MANS)
|
||||||
|
|
||||||
|
|
13
README
13
README
|
@ -16,9 +16,10 @@ maintained, and all flags are synchronized.
|
||||||
|
|
||||||
* Features:
|
* Features:
|
||||||
|
|
||||||
* Supports imaps: (port 993) TLS/SSL connections
|
|
||||||
* Supports STARTTLS
|
|
||||||
* Fast mode for fetching new mail only
|
* Fast mode for fetching new mail only
|
||||||
|
* Supports imaps: (port 993) TLS/SSL connections
|
||||||
|
* Supports STARTTLS (RFC2595)
|
||||||
|
* Supports NAMESPACE (RFC2342)
|
||||||
|
|
||||||
* Compatibility
|
* Compatibility
|
||||||
|
|
||||||
|
@ -26,6 +27,14 @@ maintained, and all flags are synchronized.
|
||||||
|
|
||||||
* Microsoft Exchange 2000 IMAP4rev1 server version 6.0.4417.0
|
* 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
|
* Requirements
|
||||||
|
|
||||||
OpenSSL for TLS/SSL support (optional)
|
OpenSSL for TLS/SSL support (optional)
|
||||||
|
|
2
TODO
2
TODO
|
@ -1 +1,3 @@
|
||||||
add upload support to mirror local msgs on the server
|
add upload support to mirror local msgs on the server
|
||||||
|
|
||||||
|
add support for syncing with other: and shared: via NAMESPACE
|
||||||
|
|
166
imap.c
166
imap.c
|
@ -149,6 +149,67 @@ buffer_gets (buffer_t * b, char **s)
|
||||||
/* not reached */
|
/* 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
|
static int
|
||||||
imap_exec (imap_t * imap, const char *fmt, ...)
|
imap_exec (imap_t * imap, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -181,12 +242,18 @@ imap_exec (imap_t * imap, const char *fmt, ...)
|
||||||
if (*arg == '*')
|
if (*arg == '*')
|
||||||
{
|
{
|
||||||
arg = next_arg (&cmd);
|
arg = next_arg (&cmd);
|
||||||
arg1 = next_arg (&cmd);
|
if (!arg)
|
||||||
|
{
|
||||||
|
puts ("Error, unable to parse untagged command");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg1 && !strcmp ("EXISTS", arg1))
|
if (!strcmp ("NAMESPACE", arg))
|
||||||
imap->count = atoi (arg);
|
{
|
||||||
else if (arg1 && !strcmp ("RECENT", arg1))
|
imap->ns_personal = parse_list (cmd, &cmd);
|
||||||
imap->recent = atoi (arg);
|
imap->ns_other = parse_list (cmd, &cmd);
|
||||||
|
imap->ns_shared = parse_list (cmd, 0);
|
||||||
|
}
|
||||||
else if (!strcmp ("SEARCH", arg))
|
else if (!strcmp ("SEARCH", arg))
|
||||||
{
|
{
|
||||||
if (!rec)
|
if (!rec)
|
||||||
|
@ -195,10 +262,6 @@ imap_exec (imap_t * imap, const char *fmt, ...)
|
||||||
while (*rec)
|
while (*rec)
|
||||||
rec = &(*rec)->next;
|
rec = &(*rec)->next;
|
||||||
}
|
}
|
||||||
/* need to add arg1 */
|
|
||||||
*rec = calloc (1, sizeof (message_t));
|
|
||||||
(*rec)->uid = atoi (arg1);
|
|
||||||
rec = &(*rec)->next;
|
|
||||||
/* parse rest of `cmd' */
|
/* parse rest of `cmd' */
|
||||||
while ((arg = next_arg (&cmd)))
|
while ((arg = next_arg (&cmd)))
|
||||||
{
|
{
|
||||||
|
@ -207,8 +270,16 @@ imap_exec (imap_t * imap, const char *fmt, ...)
|
||||||
rec = &(*rec)->next;
|
rec = &(*rec)->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (arg1 && !strcmp ("FETCH", arg1))
|
else if ((arg1 = next_arg (&cmd)))
|
||||||
{
|
{
|
||||||
|
if (!strcmp ("EXISTS", arg1))
|
||||||
|
imap->count = atoi (arg);
|
||||||
|
else if (!strcmp ("RECENT", arg1))
|
||||||
|
imap->recent = atoi (arg);
|
||||||
|
else if (!strcmp ("FETCH", arg1))
|
||||||
|
{
|
||||||
|
list_t *list;
|
||||||
|
|
||||||
if (!cur)
|
if (!cur)
|
||||||
{
|
{
|
||||||
cur = &imap->msgs;
|
cur = &imap->msgs;
|
||||||
|
@ -216,59 +287,26 @@ imap_exec (imap_t * imap, const char *fmt, ...)
|
||||||
cur = &(*cur)->next;
|
cur = &(*cur)->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* new message
|
list = parse_list (cmd, 0);
|
||||||
* * <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 */
|
*cur = calloc (1, sizeof(message_t));
|
||||||
if (!arg || strcmp ("FLAGS", arg))
|
if (parse_fetch (imap, list, *cur))
|
||||||
{
|
{
|
||||||
printf ("FETCH parse error: expected FLAGS at %s\n", arg);
|
free_list (list);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we need to parse additional info, we should keep
|
free_list (list);
|
||||||
* 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))
|
|
||||||
{
|
|
||||||
(*cur)->flags |= D_DELETED;
|
|
||||||
imap->deleted++;
|
|
||||||
}
|
|
||||||
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;
|
cur = &(*cur)->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts ("Error, unable to parse untagged command");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ((size_t) atol (arg) != Tag)
|
else if ((size_t) atol (arg) != Tag)
|
||||||
{
|
{
|
||||||
puts ("wrong tag");
|
puts ("wrong tag");
|
||||||
|
@ -344,6 +382,7 @@ imap_open (config_t * box, int fast)
|
||||||
int s;
|
int s;
|
||||||
struct sockaddr_in sin;
|
struct sockaddr_in sin;
|
||||||
struct hostent *he;
|
struct hostent *he;
|
||||||
|
char *ns_prefix = 0;
|
||||||
#if HAVE_LIBSSL
|
#if HAVE_LIBSSL
|
||||||
int use_ssl = 0;
|
int use_ssl = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -427,11 +466,28 @@ imap_open (config_t * box, int fast)
|
||||||
|
|
||||||
puts ("Logging in...");
|
puts ("Logging in...");
|
||||||
ret = imap_exec (imap, "LOGIN %s %s", box->user, box->pass);
|
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)
|
if (!ret)
|
||||||
{
|
{
|
||||||
fputs ("Selecting mailbox... ", stdout);
|
fputs ("Selecting mailbox... ", stdout);
|
||||||
fflush (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)
|
if (!ret)
|
||||||
printf ("%d messages, %d recent\n", imap->count, imap->recent);
|
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 */
|
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 */
|
/* imap connection info */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -105,6 +117,10 @@ typedef struct
|
||||||
* UID to be used in a FETCH FLAGS command
|
* UID to be used in a FETCH FLAGS command
|
||||||
*/
|
*/
|
||||||
unsigned int deleted; /* # of deleted messages */
|
unsigned int deleted; /* # of deleted messages */
|
||||||
|
/* NAMESPACE info */
|
||||||
|
list_t *ns_personal;
|
||||||
|
list_t *ns_other;
|
||||||
|
list_t *ns_shared;
|
||||||
}
|
}
|
||||||
imap_t;
|
imap_t;
|
||||||
|
|
||||||
|
@ -135,3 +151,10 @@ imap_t *imap_open (config_t *, int);
|
||||||
mailbox_t *maildir_open (const char *, int fast);
|
mailbox_t *maildir_open (const char *, int fast);
|
||||||
int maildir_expunge (mailbox_t *, int);
|
int maildir_expunge (mailbox_t *, int);
|
||||||
int maildir_sync (mailbox_t *);
|
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