ソースを参照

more sophisticated CAPABILITY handling. also, don't issue the command if
the initial response already had it in the status code.

Oswald Buddenhagen 21 年 前
コミット
a52fd7dde0
2 ファイル変更62 行追加38 行削除
  1. 50 33
      src/imap.c
  2. 12 5
      src/isync.h

+ 50 - 33
src/imap.c

@@ -42,6 +42,10 @@
 # include <openssl/err.h>
 #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_response_code (imap_t * imap, char *s)
+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, *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;

+ 12 - 5
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 */