]> Pileus Git - ~andy/fetchmail/blobdiff - imap.c
First round ofmlong-delayed bug fixes.
[~andy/fetchmail] / imap.c
diff --git a/imap.c b/imap.c
index d7eb0fa3dd0dda3c7161f72c693e8008e05dc121..3e6d80fc8ed4a45d6413b7235a4dde3af80e59d8 100644 (file)
--- a/imap.c
+++ b/imap.c
@@ -57,7 +57,10 @@ static int imap_ok(int sock, char *argbuf)
 
        /* interpret untagged status responses */
        if (strstr(buf, "* CAPABILITY"))
+       {
            strncpy(capabilities, buf + 12, sizeof(capabilities));
+           capabilities[sizeof(capabilities)-1] = '\0';
+       }
        else if (strstr(buf, "EXISTS"))
        {
            count = atoi(buf+2);
@@ -248,10 +251,10 @@ static int imap_canonicalize(char *result, char *raw, int maxlen)
     return(i);
 }
 
-static int imap_getauth(int sock, struct query *ctl, char *greeting)
-/* apply for connection authorization */
+static void capa_probe(int sock, struct query *ctl)
+/* set capability variables from a CAPA probe */
 {
-    int ok = 0;
+    int        ok;
 
     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
     capabilities[0] = '\0';
@@ -284,19 +287,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        if (outlevel >= O_DEBUG)
            report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
     }
-    else
-       return(ok);
-
-    peek_capable = (imap_version >= IMAP4);
-
-    /* 
-     * Assumption: expunges are cheap, so we want to do them
-     * after every message unless user said otherwise.
-     */
-    if (NUM_SPECIFIED(ctl->expunge))
-       expunge_period = NUM_VALUE_OUT(ctl->expunge);
-    else
-       expunge_period = 1;
 
     /* 
      * Handle idling.  We depend on coming through here on startup
@@ -308,6 +298,17 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        if (outlevel >= O_VERBOSE)
            report(stdout, GT_("will idle after poll\n"));
     }
+}
+
+static int imap_getauth(int sock, struct query *ctl, char *greeting)
+/* apply for connection authorization */
+{
+    int ok = 0;
+#ifdef SSL_ENABLE
+    flag did_stls = FALSE;
+#endif /* SSL_ENABLE */
+
+    capa_probe(sock, ctl);
 
     /* 
      * If either (a) we saw a PREAUTH token in the greeting, or
@@ -318,7 +319,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
         preauth = FALSE;  /* reset for the next session */
         return(PS_SUCCESS);
     }
-
     /*
      * Time to authenticate the user.
      * Try the protocol variants that don't require passwords first.
@@ -359,35 +359,65 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
 #endif /* KERBEROS_V4 */
 
 #ifdef SSL_ENABLE
-    if ((ctl->server.authenticate == A_ANY)
+    if ((!ctl->sslproto || !strcmp(ctl->sslproto,"tls1"))
         && !ctl->use_ssl
         && strstr(capabilities, "STARTTLS"))
     {
            char *realhost;
 
            realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
-           gen_transact(sock, "STARTTLS");
+           ok = gen_transact(sock, "STARTTLS");
 
            /* We use "tls1" instead of ctl->sslproto, as we want STARTTLS,
             * not other SSL protocols
             */
-           if (SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
+           if (ok == PS_SUCCESS &&
+              SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
            {
+              if (!ctl->sslproto && !ctl->wehaveauthed)
+              {
+                  ctl->sslproto = xstrdup("");
+                  /* repoll immediately */
+                  return(PS_REPOLL);
+              }
                report(stderr,
                       GT_("SSL connection failed.\n"));
                return(PS_AUTHFAIL);
            }
+          did_stls = TRUE;
+
+          /*
+           * RFC 2595 says this:
+           *
+           * "Once TLS has been started, the client MUST discard cached
+           * information about server capabilities and SHOULD re-issue the
+           * CAPABILITY command.  This is necessary to protect against
+           * man-in-the-middle attacks which alter the capabilities list prior
+           * to STARTTLS.  The server MAY advertise different capabilities
+           * after STARTTLS."
+           */
+          capa_probe(sock, ctl);
     }
 #endif /* SSL_ENABLE */
 
+    peek_capable = (imap_version >= IMAP4);
+
+    /* 
+     * Assumption: expunges are cheap, so we want to do them
+     * after every message unless user said otherwise.
+     */
+    if (NUM_SPECIFIED(ctl->expunge))
+       expunge_period = NUM_VALUE_OUT(ctl->expunge);
+    else
+       expunge_period = 1;
+
     /*
      * No such luck.  OK, now try the variants that mask your password
      * in a challenge-response.
      */
 
-    if ((ctl->server.authenticate == A_ANY 
-        || ctl->server.authenticate == A_CRAM_MD5)
-       && strstr(capabilities, "AUTH=CRAM-MD5"))
+    if ((ctl->server.authenticate == A_ANY && strstr(capabilities, "AUTH=CRAM-MD5"))
+       || ctl->server.authenticate == A_CRAM_MD5)
     {
        if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
        {
@@ -471,6 +501,16 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        strcpy(shroud, password);
        ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
        shroud[0] = '\0';
+#ifdef SSL_ENABLE
+       /* this is for servers which claim to support TLS, but actually
+        * don't! */
+       if (did_stls && ok == PS_SOCKET && !ctl->sslproto && !ctl->wehaveauthed)
+       {
+           ctl->sslproto = xstrdup("");
+           /* repoll immediately */
+           ok = PS_REPOLL;
+       }
+#endif
        if (ok)
        {
            /* SASL cancellation of authentication */
@@ -506,11 +546,33 @@ static int internal_expunge(int sock)
 static int imap_idle(int sock)
 /* start an RFC2177 IDLE */
 {
+    int ok;
+
+    /* special timeout to terminate the IDLE and re-issue it
+     * at least every 28 minutes:
+     * (the server may have an inactivity timeout) */
     stage = STAGE_IDLE;
     saved_timeout = mytimeout;
-    mytimeout = 0;
+    mytimeout = 1680; /* 28 min */
+
+    /* enter IDLE mode */
+    ok = gen_transact(sock, "IDLE");
+
+    if(ok == PS_IDLETIMEOUT) {
+       /* send "DONE" continuation */
+       SockWrite(sock, "DONE\r\n", 6);
+       if (outlevel >= O_MONITOR)
+           report(stdout, "IMAP> DONE\n");
 
-    return (gen_transact(sock, "IDLE"));
+       /* restore normal timeout value */
+       mytimeout = saved_timeout;
+       stage = STAGE_FETCH;
+
+       /* get OK IDLE message */
+       return imap_ok(sock, NULL);
+    } else
+       /* not idle timeout */
+       return ok;
 }
 
 static int imap_getrange(int sock, 
@@ -856,11 +918,6 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
      * craps out during the message, it will still be marked `unseen' on
      * the server.
      *
-     * However...*don't* do this if we're using keep to suppress deletion!
-     * In that case, marking the seen flag is the only way to prevent the
-     * message from being re-fetched on subsequent runs (and according
-     * to RFC2060 p.43 this fetch should set Seen as a side effect).
-     *
      * According to RFC2060, and Mark Crispin the IMAP maintainer,
      * FETCH %d BODY[TEXT] and RFC822.TEXT are "functionally 
      * equivalent".  However, we know of at least one server that
@@ -878,17 +935,11 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
     switch (imap_version)
     {
     case IMAP4rev1:    /* RFC 2060 */
-       if (!ctl->keep)
-           gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
-       else
-           gen_send(sock, "FETCH %d BODY[TEXT]", number);
+       gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
        break;
 
     case IMAP4:                /* RFC 1730 */
-       if (!ctl->keep)
-           gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
-       else
-           gen_send(sock, "FETCH %d RFC822.TEXT", number);
+       gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
        break;
 
     default:           /* RFC 1176 */
@@ -938,26 +989,6 @@ static int imap_trail(int sock, struct query *ctl, int number)
        /* UW IMAP returns "OK FETCH", Cyrus returns "OK Completed" */
        if (strstr(buf, "OK"))
            break;
-
-#ifdef __UNUSED__
-       /*
-        * Any IMAP server that fails to set Seen on a BODY[TEXT]
-        * fetch violates RFC2060 p.43 (top).  This becomes an issue
-        * when keep is on, because seen messages aren't deleted and
-        * get refetched on each poll.  As a workaround, if keep is on
-        * we can set the Seen flag explicitly.
-        *
-        * This code isn't used yet because we don't know of any IMAP
-        * servers broken in this way.
-        */
-       if (ctl->keep)
-           if ((ok = gen_transact(sock,
-                       imap_version == IMAP4 
-                               ? "STORE %d +FLAGS.SILENT (\\Seen)"
-                               : "STORE %d +FLAGS (\\Seen)", 
-                       number)))
-               return(ok);
-#endif /* __UNUSED__ */
     }
 
     return(PS_SUCCESS);
@@ -1002,6 +1033,16 @@ static int imap_delete(int sock, struct query *ctl, int number)
     return(PS_SUCCESS);
 }
 
+static int imap_mark_seen(int sock, struct query *ctl, int number)
+/* mark the given message as seen */
+{
+    return(gen_transact(sock,
+       imap_version == IMAP4
+       ? "STORE %d +FLAGS.SILENT (\\Seen)"
+       : "STORE %d +FLAGS (\\Seen)",
+       number));
+}
+
 static int imap_logout(int sock, struct query *ctl)
 /* send logout command */
 {
@@ -1039,6 +1080,7 @@ const static struct method imap =
     imap_fetch_body,   /* request given message body */
     imap_trail,                /* eat message trailer */
     imap_delete,       /* delete the message */
+    imap_mark_seen,    /* how to mark a message as seen */
     imap_logout,       /* expunge and exit */
     TRUE,              /* yes, we can re-poll */
 };