]> Pileus Git - ~andy/fetchmail/blobdiff - imap.c
Cast arguments of is*() ctype.h functions to unsigned char to be 8-bit safe.
[~andy/fetchmail] / imap.c
diff --git a/imap.c b/imap.c
index 9154620d4d27e0d689e2c4e759959463efbb8a46..f1b516ee837e833a790d5f6faa4112a3b868bb8a 100644 (file)
--- a/imap.c
+++ b/imap.c
 #if defined(STDC_HEADERS)
 #include  <stdlib.h>
 #include  <limits.h>
+#include  <errno.h>
 #endif
 #include  "fetchmail.h"
 #include  "socket.h"
 
 #include  "i18n.h"
 
-#if OPIE_ENABLE
+#ifdef OPIE_ENABLE
 #endif /* OPIE_ENABLE */
 
 #ifndef strstr         /* glibc-2.1 declares this as a macro */
-extern char *strstr(); /* needed on sysV68 R3V7.1. */
+extern char *strstr(const char *, const char *);       /* needed on sysV68 R3V7.1. */
 #endif /* strstr */
 
 /* imap_version values */
@@ -52,8 +53,8 @@ static int imap_ok(int sock, char *argbuf)
 
        /* all tokens in responses are caseblind */
        for (cp = buf; *cp; cp++)
-           if (islower(*cp))
-               *cp = toupper(*cp);
+           if (islower((unsigned char)*cp))
+               *cp = toupper((unsigned char)*cp);
 
        /* interpret untagged status responses */
        if (strstr(buf, "* CAPABILITY"))
@@ -136,9 +137,9 @@ static int imap_ok(int sock, char *argbuf)
        char    *cp;
 
        /* skip the tag */
-       for (cp = buf; !isspace(*cp); cp++)
+       for (cp = buf; !isspace((unsigned char)*cp); cp++)
            continue;
-       while (isspace(*cp))
+       while (isspace((unsigned char)*cp))
            cp++;
 
         if (strncasecmp(cp, "OK", 2) == 0)
@@ -161,7 +162,7 @@ static int imap_ok(int sock, char *argbuf)
     }
 }
 
-#if NTLM_ENABLE
+#ifdef NTLM_ENABLE
 #include "ntlm.h"
 
 static tSmbNtlmAuthRequest   request;             
@@ -306,6 +307,8 @@ static void capa_probe(int sock, struct query *ctl)
        if (outlevel >= O_VERBOSE)
            report(stdout, GT_("will idle after poll\n"));
     }
+
+    peek_capable = (imap_version >= IMAP4);
 }
 
 static int imap_getauth(int sock, struct query *ctl, char *greeting)
@@ -316,6 +319,15 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
     flag did_stls = FALSE;
 #endif /* SSL_ENABLE */
 
+    /*
+     * 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;
+
     capa_probe(sock, ctl);
 
     /* 
@@ -327,44 +339,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.
-     */
-    ok = PS_AUTHFAIL;
-
-#ifdef GSSAPI
-    if ((ctl->server.authenticate == A_ANY 
-        || ctl->server.authenticate == A_GSSAPI)
-       && strstr(capabilities, "AUTH=GSSAPI"))
-       if(ok = do_gssauth(sock, "AUTHENTICATE", ctl->server.truename, ctl->remotename))
-       {
-           /* SASL cancellation of authentication */
-           gen_send(sock, "*");
-           if(ctl->server.authenticate != A_ANY)
-                return ok;
-       }
-       else
-           return ok;
-#endif /* GSSAPI */
-
-#ifdef KERBEROS_V4
-    if ((ctl->server.authenticate == A_ANY 
-        || ctl->server.authenticate == A_KERBEROS_V4
-        || ctl->server.authenticate == A_KERBEROS_V5) 
-       && strstr(capabilities, "AUTH=KERBEROS_V4"))
-    {
-       if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename)))
-       {
-           /* SASL cancellation of authentication */
-           gen_send(sock, "*");
-           if(ctl->server.authenticate != A_ANY)
-                return ok;
-       }
-       else
-           return ok;
-    }
-#endif /* KERBEROS_V4 */
 
 #ifdef SSL_ENABLE
     if ((!ctl->sslproto || !strcmp(ctl->sslproto,"tls1"))
@@ -408,16 +382,44 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
     }
 #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.
+    /*
+     * Time to authenticate the user.
+     * Try the protocol variants that don't require passwords first.
      */
-    if (NUM_SPECIFIED(ctl->expunge))
-       expunge_period = NUM_VALUE_OUT(ctl->expunge);
-    else
-       expunge_period = 1;
+    ok = PS_AUTHFAIL;
+
+#ifdef GSSAPI
+    if ((ctl->server.authenticate == A_ANY 
+        || ctl->server.authenticate == A_GSSAPI)
+       && strstr(capabilities, "AUTH=GSSAPI"))
+       if(ok = do_gssauth(sock, "AUTHENTICATE", "imap", ctl->server.truename, ctl->remotename))
+       {
+           /* SASL cancellation of authentication */
+           gen_send(sock, "*");
+           if(ctl->server.authenticate != A_ANY)
+                return ok;
+       }
+       else
+           return ok;
+#endif /* GSSAPI */
+
+#ifdef KERBEROS_V4
+    if ((ctl->server.authenticate == A_ANY 
+        || ctl->server.authenticate == A_KERBEROS_V4
+        || ctl->server.authenticate == A_KERBEROS_V5) 
+       && strstr(capabilities, "AUTH=KERBEROS_V4"))
+    {
+       if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename)))
+       {
+           /* SASL cancellation of authentication */
+           gen_send(sock, "*");
+           if(ctl->server.authenticate != A_ANY)
+                return ok;
+       }
+       else
+           return ok;
+    }
+#endif /* KERBEROS_V4 */
 
     /*
      * No such luck.  OK, now try the variants that mask your password
@@ -438,7 +440,7 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
            return ok;
     }
 
-#if OPIE_ENABLE
+#ifdef OPIE_ENABLE
     if ((ctl->server.authenticate == A_ANY 
         || ctl->server.authenticate == A_OTP)
        && strstr(capabilities, "AUTH=X-OTP"))
@@ -506,7 +508,13 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        imap_canonicalize(remotename, ctl->remotename, NAMELEN);
        imap_canonicalize(password, ctl->password, PASSWORDLEN);
 
-       strcpy(shroud, password);
+#ifdef HAVE_SNPRINTF
+       snprintf(shroud, sizeof (shroud), "\"%s\"", password);
+#else
+       strcpy(shroud, "\"");
+       strcat(shroud, password);
+       strcat(shroud, "\"");
+#endif
        ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
        shroud[0] = '\0';
 #ifdef SSL_ENABLE
@@ -717,7 +725,8 @@ static int imap_getrange(int sock,
        memset(unseen_messages, 0, count * sizeof(unsigned int));
        unseen = 0;
 
-       gen_send(sock, "SEARCH UNSEEN");
+       /* don't count deleted messages, in case user enabled keep last time */
+       gen_send(sock, "SEARCH UNSEEN NOT DELETED");
        do {
            ok = gen_recv(sock, buf, sizeof(buf));
            if (ok != 0)
@@ -738,7 +747,7 @@ static int imap_getrange(int sock,
                while (*cp && unseen < count)
                {
                    /* skip whitespace */
-                   while (*cp && isspace(*cp))
+                   while (*cp && isspace((unsigned char)*cp))
                        cp++;
                    if (*cp) 
                    {
@@ -776,8 +785,8 @@ static int imap_getrange(int sock,
     return(PS_SUCCESS);
 }
 
-static int imap_getsizes(int sock, int count, int *sizes)
-/* capture the sizes of all messages */
+static int imap_getpartialsizes(int sock, int first, int last, int *sizes)
+/* capture the sizes of messages #first-#last */
 {
     char buf [MSGBUFSIZE+1];
 
@@ -815,14 +824,15 @@ static int imap_getsizes(int sock, int count, int *sizes)
      * on the fact that the sizes array has been preinitialized with a
      * known-bad size value.
      */
-    /* if fetchall is specified, startcount is 1;
-     * else if there is new mail, startcount is first unseen message;
-     * else startcount is greater than count.
-     */
-    if (count == startcount)
-       gen_send(sock, "FETCH %d RFC822.SIZE", count);
-    else if (count > startcount)
-       gen_send(sock, "FETCH %d:%d RFC822.SIZE", startcount, count);
+
+    /* expunges change the fetch numbers */
+    first -= expunged;
+    last -= expunged;
+
+    if (last == first)
+       gen_send(sock, "FETCH %d RFC822.SIZE", last);
+    else if (last > first)
+       gen_send(sock, "FETCH %d:%d RFC822.SIZE", first, last);
     else /* no unseen messages! */
        return(PS_SUCCESS);
     for (;;)
@@ -843,8 +853,8 @@ static int imap_getsizes(int sock, int count, int *sizes)
            break;
        else if (sscanf(buf, "* %u FETCH (RFC822.SIZE %u)", &num, &size) == 2) 
        {
-           if (num > 0 && num <= count)
-               sizes[num - 1] = size;
+           if (num >= first && num <= last)
+               sizes[num - first] = size;
            else
                report(stderr, "Warning: ignoring bogus data for message sizes returned by the server.\n");
        }
@@ -853,6 +863,12 @@ static int imap_getsizes(int sock, int count, int *sizes)
     return(PS_SUCCESS);
 }
 
+static int imap_getsizes(int sock, int count, int *sizes)
+/* capture the sizes of all messages */
+{
+    return imap_getpartialsizes(sock, 1, count, sizes);
+}
+
 static int imap_is_old(int sock, struct query *ctl, int number)
 /* is the given message old? */
 {
@@ -878,9 +894,9 @@ static int imap_is_old(int sock, struct query *ctl, int number)
 
 static char *skip_token(char *ptr)
 {
-    while(isspace(*ptr)) ptr++;
-    while(!isspace(*ptr) && !iscntrl(*ptr)) ptr++;
-    while(isspace(*ptr)) ptr++;
+    while(isspace((unsigned char)*ptr)) ptr++;
+    while(!isspace((unsigned char)*ptr) && !iscntrl((unsigned char)*ptr)) ptr++;
+    while(isspace((unsigned char)*ptr)) ptr++;
     return(ptr);
 }
 
@@ -992,10 +1008,15 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
     /*
      * Try to extract a length from the FETCH response.  RFC2060 requires
      * it to be present, but at least one IMAP server (Novell GroupWise)
-     * botches this.
+     * botches this.  The overflow check is needed because of a broken
+     * server called dbmail that returns huge garbage lengths.
      */
-    if ((cp = strchr(buf, '{')))
-       *lenp = atoi(cp + 1);
+    if ((cp = strchr(buf, '{'))) {
+        errno = 0;
+       *lenp = (int)strtol(cp + 1, (char **)NULL, 10);
+        if (errno == ERANGE && (*lenp == LONG_MAX || *lenp == LONG_MIN))
+            *lenp = -1;    /* length is too big/small for us to handle */
+    }
     else
        *lenp = -1;     /* missing length part in FETCH reponse */
 
@@ -1089,10 +1110,10 @@ static int imap_logout(int sock, struct query *ctl)
     return(gen_transact(sock, "LOGOUT"));
 }
 
-const static struct method imap =
+static const struct method imap =
 {
     "IMAP",            /* Internet Message Access Protocol */
-#if INET6_ENABLE
+#ifdef INET6_ENABLE
     "imap",
     "imaps",
 #else /* INET6_ENABLE */
@@ -1105,6 +1126,7 @@ const static struct method imap =
     imap_getauth,      /* get authorization */
     imap_getrange,     /* query range of messages */
     imap_getsizes,     /* get sizes of messages (used for ESMTP SIZE option) */
+    imap_getpartialsizes,      /* get sizes of subset of messages (used for ESMTP SIZE option) */
     imap_is_old,       /* no UID check */
     imap_fetch_headers,        /* request given message headers */
     imap_fetch_body,   /* request given message body */