]> Pileus Git - ~andy/fetchmail/blobdiff - imap.c
Complete Dominik's name.
[~andy/fetchmail] / imap.c
diff --git a/imap.c b/imap.c
index 4a440865d6be1536616f285d0301d2b3978c615a..cb87eda5378f258f20955c3524f2f84c6d29cd0e 100644 (file)
--- a/imap.c
+++ b/imap.c
@@ -8,6 +8,7 @@
 #include  "config.h"
 #include  <stdio.h>
 #include  <string.h>
+#include  <strings.h>
 #include  <ctype.h>
 #if defined(STDC_HEADERS)
 #include  <stdlib.h>
@@ -74,7 +75,9 @@ static int imap_untagged_response(int sock, const char *buf)
     }
     else if (strstr(buf, " EXISTS"))
     {
-       count = atoi(buf+2);
+       char *t; unsigned long u;
+       errno = 0;
+       u = strtoul(buf+2, &t, 10);
        /*
         * Don't trust the message count passed by the server.
         * Without this check, it might be possible to do a
@@ -82,11 +85,15 @@ static int imap_untagged_response(int sock, const char *buf)
         * count, and allocate a malloc area that would overlap
         * a portion of the stack.
         */
-       if ((unsigned)count > INT_MAX/sizeof(int))
+       if (errno /* strtoul failed */
+               || t == buf+2 /* no valid data */
+               || u > (unsigned long)(INT_MAX/sizeof(int)) /* too large */)
        {
-           report(stderr, GT_("bogus message count!"));
+           report(stderr, GT_("bogus message count in \"%s\"!"), buf);
            return(PS_PROTOCOL);
        }
+       count = u; /* safe as long as count <= INT_MAX - checked above */
+
        if ((recentcount = count - oldcount) < 0)
            recentcount = 0;
 
@@ -117,14 +124,22 @@ static int imap_untagged_response(int sock, const char *buf)
 # if 0
     else if (strstr(buf, " RECENT"))
     {
+       /* fixme: use strto[u]l and error checking */
        recentcount = atoi(buf+2);
     }
 # endif
     else if (strstr(buf, " EXPUNGE"))
     {
+       unsigned long u; char *t;
        /* the response "* 10 EXPUNGE" means that the currently
         * tenth (i.e. only one) message has been deleted */
-       if (atoi(buf+2) > 0)
+       errno = 0;
+       u = strtoul(buf+2, &t, 10);
+       if (errno /* conversion error */ || t == buf+2 /* no number found */) {
+           report(stderr, GT_("bogus EXPUNGE count in \"%s\"!"), buf);
+           return PS_PROTOCOL;
+       }
+       if (u > 0)
        {
            if (count > 0)
                count--;
@@ -164,7 +179,7 @@ static int imap_untagged_response(int sock, const char *buf)
     return(PS_SUCCESS);
 }
 
-static int imap_response(int sock, char *argbuf)
+static int imap_response(int sock, char *argbuf, struct RecvSplit *rs)
 /* parse command response */
 {
     char buf[MSGBUFSIZE+1];
@@ -173,7 +188,11 @@ static int imap_response(int sock, char *argbuf)
        int     ok;
        char    *cp;
 
-       if ((ok = gen_recv(sock, buf, sizeof(buf))))
+       if (rs)
+           ok = gen_recv_split(sock, buf, sizeof(buf), rs);
+       else
+           ok = gen_recv(sock, buf, sizeof(buf));
+       if (ok != PS_SUCCESS)
            return(ok);
 
        /* all tokens in responses are caseblind */
@@ -254,7 +273,7 @@ static int imap_ok(int sock, char *argbuf)
 {
     int ok;
 
-    while ((ok = imap_response(sock, argbuf)) == PS_UNTAGGED)
+    while ((ok = imap_response(sock, argbuf, NULL)) == PS_UNTAGGED)
        ; /* wait for the tagged response */
     return(ok);
 }
@@ -275,57 +294,13 @@ static int imap_ok(int sock, char *argbuf)
 
 static int do_imap_ntlm(int sock, struct query *ctl)
 {
-    tSmbNtlmAuthRequest request;
-    tSmbNtlmAuthChallenge challenge;
-    tSmbNtlmAuthResponse response;
-
-    char msgbuf[2048];
-    int result,len;
+    int result;
 
     gen_send(sock, "AUTHENTICATE NTLM");
 
-    if ((result = gen_recv(sock, msgbuf, sizeof msgbuf)))
+    if ((result = ntlm_helper(sock, ctl, "IMAP")))
        return result;
-  
-    if (msgbuf[0] != '+')
-       return PS_AUTHFAIL;
-  
-    buildSmbNtlmAuthRequest(&request,ctl->remotename,NULL);
-
-    if (outlevel >= O_DEBUG)
-       dumpSmbNtlmAuthRequest(stdout, &request);
-
-    memset(msgbuf,0,sizeof msgbuf);
-    to64frombits (msgbuf, &request, SmbLength(&request));
-  
-    if (outlevel >= O_MONITOR)
-       report(stdout, "IMAP> %s\n", msgbuf);
-  
-    strcat(msgbuf,"\r\n");
-    SockWrite (sock, msgbuf, strlen (msgbuf));
-
-    if ((gen_recv(sock, msgbuf, sizeof msgbuf)))
-       return result;
-  
-    len = from64tobits (&challenge, msgbuf, sizeof(challenge));
-    
-    if (outlevel >= O_DEBUG)
-       dumpSmbNtlmAuthChallenge(stdout, &challenge);
-    
-    buildSmbNtlmAuthResponse(&challenge, &response,ctl->remotename,ctl->password);
-  
-    if (outlevel >= O_DEBUG)
-       dumpSmbNtlmAuthResponse(stdout, &response);
-  
-    memset(msgbuf,0,sizeof msgbuf);
-    to64frombits (msgbuf, &response, SmbLength(&response));
-
-    if (outlevel >= O_MONITOR)
-       report(stdout, "IMAP> %s\n", msgbuf);
-      
-    strcat(msgbuf,"\r\n");
-    SockWrite (sock, msgbuf, strlen (msgbuf));
-  
+
     result = imap_ok (sock, NULL);
     if (result == PS_SUCCESS)
        return PS_SUCCESS;
@@ -349,7 +324,7 @@ static void imap_canonicalize(char *result, char *raw, size_t maxlen)
     result[j] = '\0';
 }
 
-static void capa_probe(int sock, struct query *ctl)
+static int capa_probe(int sock, struct query *ctl)
 /* set capability variables from a CAPA probe */
 {
     int        ok;
@@ -385,6 +360,8 @@ static void capa_probe(int sock, struct query *ctl)
        if (outlevel >= O_DEBUG)
            report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
     }
+    else
+       return ok;
 
     /* 
      * Handle idling.  We depend on coming through here on startup
@@ -402,9 +379,11 @@ static void capa_probe(int sock, struct query *ctl)
     }
 
     peek_capable = (imap_version >= IMAP4);
+
+    return PS_SUCCESS;
 }
 
-static int do_authcert (int sock, char *command, const char *name)
+static int do_authcert (int sock, const char *command, const char *name)
 /* do authentication "external" (authentication provided by client cert) */
 {
     char buf[256];
@@ -426,9 +405,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
 /* apply for connection authorization */
 {
     int ok = 0;
-#ifdef SSL_ENABLE
-    int got_tls = 0;
-#endif
     (void)greeting;
 
     /*
@@ -440,7 +416,8 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
     else
        expunge_period = 1;
 
-    capa_probe(sock, ctl);
+    if ((ok = capa_probe(sock, ctl)))
+       return ok;
 
     /* 
      * If either (a) we saw a PREAUTH token in the greeting, or
@@ -462,16 +439,17 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        if (ctl->sslcommonname)
            commonname = ctl->sslcommonname;
 
-       if (strstr(capabilities, "STARTTLS"))
+       if (strstr(capabilities, "STARTTLS")
+               || must_tls(ctl)) /* if TLS is mandatory, ignore capabilities */
        {
            /* Use "tls1" rather than ctl->sslproto because tls1 is the only
             * protocol that will work with STARTTLS.  Don't need to worry
             * whether TLS is mandatory or opportunistic unless SSLOpen() fails
             * (see below). */
            if (gen_transact(sock, "STARTTLS") == PS_SUCCESS
-                   && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
-                       ctl->sslcertpath, ctl->sslfingerprint, commonname,
-                       ctl->server.pollname, &ctl->remotename) != -1)
+                   && (set_timeout(mytimeout), SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
+                       ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname,
+                       ctl->server.pollname, &ctl->remotename)) != -1)
            {
                /*
                 * RFC 2595 says this:
@@ -486,22 +464,20 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
                 * Now that we're confident in our TLS connection we can
                 * guarantee a secure capability re-probe.
                 */
-               got_tls = 1;
-               capa_probe(sock, ctl);
+               if ((ok = capa_probe(sock, ctl)))
+                   return ok;
                if (outlevel >= O_VERBOSE)
                {
                    report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname);
                }
-           }
-       }
-
-       if (!got_tls) {
-           if (must_tls(ctl)) {
+           } else if (must_tls(ctl)) {
                /* Config required TLS but we couldn't guarantee it, so we must
                 * stop. */
+               set_timeout(0);
                report(stderr, GT_("%s: upgrade to TLS failed.\n"), commonname);
                return PS_SOCKET;
            } else {
+               set_timeout(0);
                if (outlevel >= O_VERBOSE) {
                    report(stdout, GT_("%s: opportunistic upgrade to TLS failed, trying to continue\n"), commonname);
                }
@@ -529,7 +505,7 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
      * and IGNORE errors. */
     {
        char *tmp = strstr(capabilities, " ID");
-       if (tmp && !isalnum(tmp[3]) && strstr(ctl->server.via ? ctl->server.via : ctl->server.pollname, "yahoo.com")) {
+       if (tmp && !isalnum((unsigned char)tmp[3]) && strstr(ctl->server.via ? ctl->server.via : ctl->server.pollname, "yahoo.com")) {
                (void)gen_transact(sock, "ID (\"guid\" \"1\")");
        }
     }
@@ -551,15 +527,13 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
     }
 
 #ifdef GSSAPI
-    if ((ctl->server.authenticate == A_ANY 
+    if (((ctl->server.authenticate == A_ANY && check_gss_creds("imap", ctl->server.truename) == PS_SUCCESS)
         || 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  {
@@ -596,8 +570,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
     {
        if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
        {
-           /* SASL cancellation of authentication */
-           gen_send(sock, "*");
            if(ctl->server.authenticate != A_ANY)
                 return ok;
        }
@@ -633,8 +605,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        && strstr (capabilities, "AUTH=NTLM")) {
        if ((ok = do_imap_ntlm(sock, ctl)))
        {
-           /* SASL cancellation of authentication */
-           gen_send(sock, "*");
            if(ctl->server.authenticate != A_ANY)
                 return ok;
        }
@@ -688,8 +658,6 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting)
        free(remotename);
        if (ok)
        {
-           /* SASL cancellation of authentication */
-           gen_send(sock, "*");
            if(ctl->server.authenticate != A_ANY)
                 return ok;
        }
@@ -798,6 +766,113 @@ static int imap_idle(int sock)
     return(ok);
 }
 
+static int imap_search(int sock, struct query *ctl, int count)
+/* search for unseen messages */
+{
+    int ok;
+    char buf[MSGBUFSIZE+1], *cp;
+
+    /* Don't count deleted messages. Enabled only for IMAP4 servers or
+     * higher and only when keeping mails. This flag will have an
+     * effect only when user has marked some unread mails for deletion
+     * using another e-mail client. */
+    flag skipdeleted = (imap_version >= IMAP4) && ctl->keep;
+    const char *undeleted;
+
+    /* structure to keep the end portion of the incomplete response */
+    struct RecvSplit rs;
+
+    /* startcount is higher than count so that if there are no
+     * unseen messages, imap_getsizes() will not need to do
+     * anything! */
+    startcount = count + 1;
+
+    for (;;)
+    {
+       undeleted = (skipdeleted ? " UNDELETED" : "");
+       gen_send(sock, "SEARCH UNSEEN%s", undeleted);
+       gen_recv_split_init("* SEARCH", &rs);
+       while ((ok = imap_response(sock, buf, &rs)) == PS_UNTAGGED)
+       {
+           if ((cp = strstr(buf, "* SEARCH")))
+           {
+               char    *ep;
+
+               cp += 8;        /* skip "* SEARCH" */
+               while (*cp && unseen < count)
+               {
+                   /* skip whitespace */
+                   while (*cp && isspace((unsigned char)*cp))
+                       cp++;
+                   if (*cp) 
+                   {
+                       unsigned long um;
+
+                       errno = 0;
+                       um = strtoul(cp,&ep,10);
+                       if (errno == 0 && ep > cp
+                               && um <= INT_MAX && um <= (unsigned)count)
+                       {
+                           unseen_messages[unseen++] = um;
+                           if (outlevel >= O_DEBUG)
+                               report(stdout, GT_("%lu is unseen\n"), um);
+                           if (startcount > um)
+                               startcount = um;
+                       }
+                       cp = ep;
+                   }
+               }
+           }
+       }
+       if (ok != PS_ERROR) /* success or non-protocol error */
+           return(ok);
+
+       /* there is a protocol error. try a different search command. */
+       if (skipdeleted)
+       {
+           /* retry with "SEARCH UNSEEN" */
+           skipdeleted = FALSE;
+           continue;
+       }
+       /* try with "FETCH 1:n FLAGS" */
+       break;
+    }
+
+    if (count == 1)
+       gen_send(sock, "FETCH %d FLAGS", count);
+    else
+       gen_send(sock, "FETCH %d:%d FLAGS", 1, count);
+    while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED)
+    {
+       unsigned int num;
+       int consumed;
+
+       /* expected response format:
+        * IMAP< * 1 FETCH (FLAGS (\Seen))
+        * IMAP< * 2 FETCH (FLAGS (\Seen \Deleted))
+        * IMAP< * 3 FETCH (FLAGS ())
+        * IMAP< * 4 FETCH (FLAGS (\Recent))
+        * IMAP< * 5 FETCH (UID 10 FLAGS (\Recent))
+        */
+       if (unseen < count
+               && sscanf(buf, "* %u %n", &num, &consumed) == 1
+               && 0 == strncasecmp(buf+consumed, "FETCH", 5)
+               && isspace((unsigned char)buf[consumed+5])
+               && num >= 1 && num <= (unsigned)count
+               && strstr(buf, "FLAGS ")
+               && !strstr(buf, "\\SEEN")
+               && !strstr(buf, "\\DELETED"))
+       {
+           unseen_messages[unseen++] = num;
+           if (outlevel >= O_DEBUG)
+               report(stdout, GT_("%u is unseen\n"), num);
+           if (startcount > num)
+               startcount = num;
+       }
+    }
+    return(ok);
+}
+
 static int imap_getrange(int sock, 
                         struct query *ctl, 
                         const char *folder, 
@@ -805,7 +880,6 @@ static int imap_getrange(int sock,
 /* get range of messages to be fetched */
 {
     int ok;
-    char buf[MSGBUFSIZE+1], *cp;
 
     /* find out how many messages are waiting */
     *bytes = -1;
@@ -909,44 +983,7 @@ static int imap_getrange(int sock,
        memset(unseen_messages, 0, count * sizeof(unsigned int));
        unseen = 0;
 
-       /* don't count deleted messages, in case user enabled keep last time */
-       gen_send(sock, "SEARCH UNSEEN NOT DELETED");
-       while ((ok = imap_response(sock, buf)) == PS_UNTAGGED)
-       {
-           if ((cp = strstr(buf, "* SEARCH")))
-           {
-               char    *ep;
-
-               cp += 8;        /* skip "* SEARCH" */
-               /* startcount is higher than count so that if there are no
-                * unseen messages, imap_getsizes() will not need to do
-                * anything! */
-               startcount = count + 1;
-
-               while (*cp && unseen < count)
-               {
-                   /* skip whitespace */
-                   while (*cp && isspace((unsigned char)*cp))
-                       cp++;
-                   if (*cp) 
-                   {
-                       unsigned long um;
-
-                       errno = 0;
-                       um = strtoul(cp,&ep,10);
-                       if (errno == 0 && um <= UINT_MAX && um <= (unsigned)count)
-                       {
-                           unseen_messages[unseen++] = um;
-                           if (outlevel >= O_DEBUG)
-                               report(stdout, GT_("%lu is unseen\n"), um);
-                           if (startcount > um)
-                               startcount = um;
-                       }
-                       cp = ep;
-                   }
-               }
-           }
-       }
+       ok = imap_search(sock, ctl, count);
        if (ok != 0)
        {
            report(stderr, GT_("search for unseen messages failed\n"));
@@ -1016,20 +1053,23 @@ static int imap_getpartialsizes(int sock, int first, int last, int *sizes)
        gen_send(sock, "FETCH %d:%d RFC822.SIZE", first, last);
     else /* no unseen messages! */
        return(PS_SUCCESS);
-    while ((ok = imap_response(sock, buf)) == PS_UNTAGGED)
+    while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED)
     {
        unsigned int size;
        int num;
+       int consumed;
+       char *ptr;
 
-       if (sscanf(buf, "* %d FETCH (RFC822.SIZE %u)", &num, &size) == 2
-       /* some servers (like mail.internode.on.net bld-mail04) return UID information here
-        *
+       /* expected response formats:
         * IMAP> A0005 FETCH 1 RFC822.SIZE
+        * IMAP< * 1 FETCH (RFC822.SIZE 1187)
         * IMAP< * 1 FETCH (UID 16 RFC822.SIZE 1447)
-        * IMAP< A0005 OK FETCH completed
-        *
         */
-               || sscanf(buf, "* %d FETCH (UID %*s RFC822.SIZE %u)", &num, &size) == 2)
+       if (sscanf(buf, "* %d %n", &num, &consumed) == 1
+           && 0 == strncasecmp(buf + consumed, "FETCH", 5)
+           && isspace((unsigned char)buf[consumed + 5])
+               && (ptr = strstr(buf, "RFC822.SIZE "))
+               && sscanf(ptr, "RFC822.SIZE %u", &size) == 1)
        {
            if (num >= first && num <= last)
                sizes[num - first] = size;
@@ -1072,6 +1112,7 @@ static int imap_is_old(int sock, struct query *ctl, int number)
     return(seen);
 }
 
+#if 0
 static char *skip_token(char *ptr)
 {
     while(isspace((unsigned char)*ptr)) ptr++;
@@ -1079,12 +1120,15 @@ static char *skip_token(char *ptr)
     while(isspace((unsigned char)*ptr)) ptr++;
     return(ptr);
 }
+#endif
 
 static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
 /* request headers of nth message */
 {
     char buf [MSGBUFSIZE+1];
     int        num;
+    int ok;
+    char *ptr;
 
     (void)ctl;
     /* expunges change the fetch numbers */
@@ -1097,47 +1141,50 @@ static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
     gen_send(sock, "FETCH %d RFC822.HEADER", number);
 
     /* looking for FETCH response */
-    for (;;) 
+    if ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED)
     {
-       int     ok;
-       char    *ptr;
-
-       if ((ok = gen_recv(sock, buf, sizeof(buf))))
-           return(ok);
-       ptr = skip_token(buf);  /* either "* " or "AXXXX " */
-       if (sscanf(ptr, "%d FETCH (RFC822.HEADER {%d}", &num, lenp) == 2
-       /* some servers (like mail.internode.on.net bld-mail04) return UID information here
-        *
+               int consumed;
+       /* expected response formats:
         * IMAP> A0006 FETCH 1 RFC822.HEADER
+        * IMAP< * 1 FETCH (RFC822.HEADER {1360}
         * IMAP< * 1 FETCH (UID 16 RFC822.HEADER {1360}
-        * ...
-        * IMAP< )
-        * IMAP< A0006 OK FETCH completed
-        *
+        * IMAP< * 1 FETCH (UID 16 RFC822.SIZE 4029 RFC822.HEADER {1360}
         */
-               || sscanf(ptr, "%d FETCH (UID %*s RFC822.HEADER {%d}", &num, lenp) == 2)
-           break;
-       /* try to recover from chronically fucked-up M$ Exchange servers */
-       else if (!strncmp(ptr, "NO", 2))
+       if (sscanf(buf, "* %d %n", &num, &consumed) == 1
+           && 0 == strncasecmp(buf + consumed, "FETCH", 5)
+           && isspace((unsigned char)buf[5+consumed])
+               && num == number
+               && (ptr = strstr(buf, "RFC822.HEADER"))
+               && sscanf(ptr, "RFC822.HEADER {%d}%n", lenp, &consumed) == 1
+               && ptr[consumed-1] == '}')
        {
-           /* wait for a tagged response */
-           if (strstr (buf, "* NO"))
-               imap_ok (sock, 0);
-           return(PS_TRANSIENT);
+           return(PS_SUCCESS);
        }
-       else if (!strncmp(ptr, "BAD", 3))
+
+       /* wait for a tagged response */
+       imap_ok (sock, 0);
+
+       /* try to recover for some responses */
+       if (!strncmp(buf, "* NO", 4) ||
+               !strncmp(buf, "* BAD", 5) ||
+               strstr(buf, "FETCH ()"))
        {
-           /* wait for a tagged response */
-           if (strstr (buf, "* BAD"))
-               imap_ok (sock, 0);
-           return(PS_TRANSIENT);
+           return(PS_TRANSIENT);
        }
-    }
 
-    if (num != number)
+       /* a response which does not match any of the above */
+       if (outlevel > O_SILENT)
+           report(stderr, GT_("Incorrect FETCH response: %s.\n"), buf);
        return(PS_ERROR);
-    else
-       return(PS_SUCCESS);
+    }
+    else if (ok == PS_SUCCESS)
+    {
+       /* an unexpected tagged response */
+       if (outlevel > O_SILENT)
+           report(stderr, GT_("Incorrect FETCH response: %s.\n"), buf);
+       return(PS_TRANSIENT);
+    }
+    return(ok);
 }
 
 static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
@@ -1204,6 +1251,13 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
            return PS_SUCCESS;
     }
 
+    /* Understand the empty string. Seen on Yahoo. */
+    /* XXX FIXME: we should be able to handle strings here. */
+    if (strstr(buf+10, "\"\")")) {
+           *lenp = 0;
+           return PS_SUCCESS;
+    }
+
     /*
      * Try to extract a length from the FETCH response.  RFC2060 requires
      * it to be present, but at least one IMAP server (Novell GroupWise)
@@ -1211,15 +1265,21 @@ static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
      * server called dbmail that returns huge garbage lengths.
      */
     if ((cp = strchr(buf, '{'))) {
+       long l; char *t;
         errno = 0;
-       *lenp = (int)strtol(cp + 1, (char **)NULL, 10);
-        if (errno == ERANGE || *lenp < 0)
-            *lenp = -1;    /* length is too big/small for us to handle */
-    }
-    else
+       ++ cp;
+       l = strtol(cp, &t, 10);
+        if (errno || t == cp || (t && !strchr(t, '}')) /* parse error */
+                   || l < 0 || l > INT_MAX /* range check */) {
+           *lenp = -1;
+       } else {
+           *lenp = l;
+       }
+    } else {
        *lenp = -1;     /* missing length part in FETCH reponse */
+    }
 
-    return(PS_SUCCESS);
+    return PS_SUCCESS;
 }
 
 static int imap_trail(int sock, struct query *ctl, const char *tag)
@@ -1238,6 +1298,13 @@ static int imap_delete(int sock, struct query *ctl, int number)
 /* set delete flag for given message */
 {
     int        ok;
+    /* Select which flags to set on message deletion: */
+    const char delflags_seen[] = "\\Seen \\Deleted";
+    static const char *delflags;
+    /* Which environment variable to look for: */
+
+    /* DEFAULT since many fetchmail versions <= 6.3.X */
+    delflags = delflags_seen;
 
     (void)ctl;
     /* expunges change the fetch numbers */
@@ -1247,17 +1314,18 @@ static int imap_delete(int sock, struct query *ctl, int number)
      * Use SILENT if possible as a minor throughput optimization.
      * Note: this has been dropped from IMAP4rev1.
      *
-     * We set Seen because there are some IMAP servers (notably HP
-     * OpenMail) that do message-receipt DSNs, but only when the seen
-     * bit is set.  This is the appropriate time -- we get here right
+     * We set \Seen because there are some IMAP servers (notably HP
+     * OpenMail and MS Exchange) do message-receipt DSNs,
+     * but only when the seen bit gets set.
+     * This is the appropriate time -- we get here right
      * after the local SMTP response that says delivery was
      * successful.
      */
     if ((ok = gen_transact(sock,
                        imap_version == IMAP4 
-                               ? "STORE %d +FLAGS.SILENT (\\Seen \\Deleted)"
-                               : "STORE %d +FLAGS (\\Seen \\Deleted)", 
-                       number)))
+                               ? "STORE %d +FLAGS.SILENT (%s)"
+                               : "STORE %d +FLAGS (%s)",
+                       number, delflags)))
        return(ok);
     else
        deletions++;