diff --git a/src/imap.c b/src/imap.c index 3de0ec0..62fcaf6 100644 --- a/src/imap.c +++ b/src/imap.c @@ -42,6 +42,10 @@ # include #endif +#define as(ar) (sizeof(ar)/sizeof(ar[0])) + +#define CAP(cap) (imap->caps & (1 << (cap))) + static int Tag; const char *Flags[] = { @@ -395,14 +399,44 @@ parse_fetch (imap_t *imap, char *cmd) return 0; } +/* Keep this in sync with enum CAPABILITY */ +const char *cap_list[] = { + "LOGINDISABLED", + "UIDPLUS", + "NAMESPACE", +#if HAVE_LIBSSL + "AUTH=CRAM-MD5", + "STARTTLS", +#endif +}; + +static void +parse_capability (imap_t *imap, char *cmd) +{ + char *arg; + unsigned i; + + imap->caps = 0x80000000; + while ((arg = next_arg (&cmd))) + for (i = 0; i < as(cap_list); i++) + if (!strcmp (cap_list[i], arg)) + imap->caps |= 1 << i; +} + static void parse_response_code (imap_t * imap, char *s) { - char *arg; + char *arg, *p; if (*s != '[') return; /* no response code */ s++; + if (!(p = strchr (s, ']'))) + { + fprintf (stderr, "IMAP error: malformed response code\n"); + return; + } + *p++ = 0; arg = next_arg (&s); @@ -411,12 +445,17 @@ parse_response_code (imap_t * imap, char *s) arg = next_arg (&s); imap->uidvalidity = atol (arg); } + else if (!strcmp ("CAPABILITY", arg)) + { + parse_capability (imap, s); + } else if (!strcmp ("ALERT", arg)) { /* RFC2060 says that these messages MUST be displayed * to the user */ - fprintf (stderr, "*** IMAP ALERT *** %s\n", s); + for (; isspace ((unsigned char)*p); p++); + fprintf (stderr, "*** IMAP ALERT *** %s\n", p); } } @@ -477,21 +516,7 @@ imap_exec (imap_t * imap, const char *fmt, ...) } else if (!strcmp ("CAPABILITY", arg)) { - while ((arg = next_arg (&cmd))) - { - if (!strcmp ("UIDPLUS", arg)) - imap->have_uidplus = 1; - else if (!strcmp ("NAMESPACE", arg)) - imap->have_namespace = 1; - else if (!strcmp ("LOGINDISABLED", arg)) - imap->have_nologin = 1; -#if HAVE_LIBSSL - else if (!strcmp ("STARTTLS", arg)) - imap->have_starttls = 1; - else if (!strcmp ("AUTH=CRAM-MD5", arg)) - imap->have_cram = 1; -#endif - } + parse_capability (imap, cmd); } else if (!strcmp ("LIST", arg)) { @@ -736,8 +761,8 @@ imap_connect (config_t * cfg) fprintf (stderr, "IMAP error: unknown greeting response\n"); goto bail; } - /* let's see what this puppy can do... */ - if (imap_exec (imap, "CAPABILITY")) + parse_response_code (imap, rsp); + if (!imap->caps && imap_exec (imap, "CAPABILITY")) goto bail; if (!preauth) @@ -749,7 +774,7 @@ imap_connect (config_t * cfg) if (cfg->use_sslv2 || cfg->use_sslv3 || cfg->use_tlsv1) { /* always try to select SSL support if available */ - if (imap->have_starttls) + if (CAP(STARTTLS)) { if (imap_exec (imap, "STARTTLS")) goto bail; @@ -757,14 +782,6 @@ imap_connect (config_t * cfg) goto bail; use_ssl = 1; - /* to conform to RFC2595 we need to forget all information - * retrieved from CAPABILITY invocations before STARTTLS. - */ - imap->have_uidplus = 0; - imap->have_namespace = 0; - imap->have_cram = 0; - imap->have_nologin = 0; - /* imap->have_starttls = 0; */ if (imap_exec (imap, "CAPABILITY")) goto bail; } @@ -817,7 +834,7 @@ imap_connect (config_t * cfg) } #if HAVE_LIBSSL - if (imap->have_cram) + if (CAP(CRAM)) { info ("Authenticating with CRAM-MD5\n"); imap->cram = 1; @@ -832,7 +849,7 @@ imap_connect (config_t * cfg) else #endif { - if (imap->have_nologin) + if (CAP(NOLOGIN)) { fprintf (stderr, "Skipping %s, server forbids LOGIN\n", cfg->path); goto bail; @@ -850,7 +867,7 @@ imap_connect (config_t * cfg) } /* !preauth */ /* get NAMESPACE info */ - if (!global.folder && cfg->use_namespace && imap->have_namespace) + if (!global.folder && cfg->use_namespace && CAP(NAMESPACE)) { if (imap_exec (imap, "NAMESPACE")) goto bail; @@ -1204,7 +1221,7 @@ imap_append_message (imap_t * imap, int fd, message_t * msg) } extra = 0, i = 0; - if (!imap->have_uidplus) + if (!CAP(UIDPLUS)) { nloop: start = i; @@ -1266,7 +1283,7 @@ imap_append_message (imap_t * imap, int fd, message_t * msg) } i = 0; - if (!imap->have_uidplus) + if (!CAP(UIDPLUS)) { n1loop: start = i; diff --git a/src/isync.h b/src/isync.h index f61ca8a..a58f77a 100644 --- a/src/isync.h +++ b/src/isync.h @@ -153,17 +153,24 @@ typedef struct list_t *ns_personal; list_t *ns_other; list_t *ns_shared; - unsigned int have_nologin:1; - unsigned int have_uidplus:1; - unsigned int have_namespace:1; + unsigned int caps; #if HAVE_LIBSSL - unsigned int have_cram:1; - unsigned int have_starttls:1; unsigned int cram:1; #endif } imap_t; +/* Keep in sync with cap_list */ +enum CAPABILITY { + NOLOGIN, + UIDPLUS, + NAMESPACE, +#if HAVE_LIBSSL + CRAM, + STARTTLS, +#endif +}; + /* flags for sync_mailbox */ #define SYNC_DELETE (1<<0) /* delete local that don't exist on server */ #define SYNC_EXPUNGE (1<<1) /* don't fetch deleted messages */