]> Pileus Git - ~andy/fetchmail/blobdiff - imap.c
SockClose() abstraction.
[~andy/fetchmail] / imap.c
diff --git a/imap.c b/imap.c
index ca9970fb1eae48c73ceca5d9f9a59f5cb30ce3ad..c1187cf68b824af1bc1fa051d753a7f7fda33b17 100644 (file)
--- a/imap.c
+++ b/imap.c
@@ -20,7 +20,7 @@
 #include <des.h>
 #define krb_get_err_text(e) (krb_err_txt[e])
 #endif
-#if defined (__FreeBSD__) || defined(__linux__)
+#if defined(__NetBSD__) || (__FreeBSD__) || defined(__linux__)
 #define krb_get_err_text(e) (krb_err_txt[e])
 #endif
 #include <krb.h>
 #include <gssapi/gssapi_generic.h>
 #endif
 
+#if OPIE
+#include <opie.h>
+#endif /* OPIE */
+
 #ifndef strstr         /* glibc-2.1 declares this as a macro */
 extern char *strstr(); /* needed on sysV68 R3V7.1. */
 #endif /* strstr */
@@ -41,20 +45,29 @@ extern char *strstr();      /* needed on sysV68 R3V7.1. */
 #define IMAP4rev1      1       /* IMAP4 rev 1, RFC2060 */
 
 static int count, seen, recent, unseen, deletions,expunged, imap_version;
+static char capabilities[MSGBUFSIZE+1];
 
 int imap_ok(int sock, char *argbuf)
 /* parse command response */
 {
-    char buf [POPBUFSIZE+1];
+    char buf [MSGBUFSIZE+1];
 
     seen = 0;
     do {
        int     ok;
+       char    *cp;
 
        if ((ok = gen_recv(sock, buf, sizeof(buf))))
            return(ok);
 
+       /* all tokens in responses are caseblind */
+       for (cp = buf; *cp; cp++)
+           if (islower(*cp))
+               *cp = toupper(*cp);
+
        /* interpret untagged status responses */
+       if (strstr(buf, "* CAPABILITY"))
+           strncpy(capabilities, buf + 12, sizeof(capabilities));
        if (strstr(buf, "EXISTS"))
            count = atoi(buf+2);
        if (strstr(buf, "RECENT"))
@@ -74,7 +87,7 @@ int imap_ok(int sock, char *argbuf)
            unseen = atoi(cp);
        }
        if (strstr(buf, "FLAGS"))
-           seen = (strstr(buf, "Seen") != (char *)NULL);
+           seen = (strstr(buf, "SEEN") != (char *)NULL);
     } while
        (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
 
@@ -107,6 +120,69 @@ int imap_ok(int sock, char *argbuf)
     }
 }
 
+#if OPIE
+static int do_otp(int sock, struct query *ctl)
+{
+  int i, rval;
+  char buffer[128];
+  char challenge[OPIE_CHALLENGE_MAX+1];
+  char response[OPIE_RESPONSE_MAX+1];
+
+  gen_send(sock, "AUTHENTICATE X-OTP");
+
+  if (rval = gen_recv(sock, buffer, sizeof(buffer)))
+    return rval;
+
+  if ((i = from64tobits(challenge, buffer)) < 0) {
+    error(0, -1, "Could not decode initial BASE64 challenge");
+    return PS_AUTHFAIL;
+  };
+
+
+  to64frombits(buffer, ctl->remotename, strlen(ctl->remotename));
+
+  if (outlevel == O_VERBOSE)
+     error(0, 0, "IMAP> %s", buffer);
+  SockWrite(sock, buffer, strlen(buffer));
+  SockWrite(sock, "\r\n", 2);
+
+  if (rval = gen_recv(sock, buffer, sizeof(buffer)))
+    return rval;
+
+  if ((i = from64tobits(challenge, buffer)) < 0) {
+    error(0, -1, "Could not decode OTP challenge");
+    return PS_AUTHFAIL;
+  };
+
+  rval = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? "" : ctl->password, response);
+  if ((rval == -2) && !run.poll_interval) {
+    char secret[OPIE_SECRET_MAX+1];
+    fprintf(stderr, "Secret pass phrase: ");
+    if (opiereadpass(secret, sizeof(secret), 0))
+      rval = opiegenerator(challenge, secret, response);
+    memset(secret, 0, sizeof(secret));
+  };
+
+  if (rval)
+   return PS_AUTHFAIL;
+
+  to64frombits(buffer, response, strlen(response));
+
+  if (outlevel == O_VERBOSE)
+     error(0, 0, "IMAP> %s", buffer);
+  SockWrite(sock, buffer, strlen(buffer));
+  SockWrite(sock, "\r\n", 2);
+
+  if (rval = gen_recv(sock, buffer, sizeof(buffer)))
+    return rval;
+
+  if (strstr(buffer, "OK"))
+    return PS_SUCCESS;
+  else
+    return PS_AUTHFAIL;
+};
+#endif /* OPIE */
+
 #ifdef KERBEROS_V4
 #if SIZEOF_INT == 4
 typedef        int     int32;
@@ -485,6 +561,7 @@ static int do_gssauth(int sock, char *hostname, char *username)
          * credentials. RFC 1731 doesn't specify what to do, and since this
          * support is only for authentication, we'll assume the server
          * knows enough to flush its own credentials */
+        gss_release_buffer(&min_stat, &send_token);
         return PS_SUCCESS;
     }
 
@@ -492,44 +569,67 @@ static int do_gssauth(int sock, char *hostname, char *username)
 }      
 #endif /* GSSAPI */
 
+int imap_canonicalize(char *result, char *passwd)
+/* encode an IMAP password as per RFC1730's quoting conventions */
+{
+    int i, j;
+
+    j = 0;
+    for (i = 0; i < strlen(passwd); i++)
+    {
+       if ((passwd[i] == '\\') || (passwd[i] == '"'))
+           result[j++] = '\\';
+       result[j++] = passwd[i];
+    }
+    result[j] = '\0';
+
+    return(i);
+}
+
 int imap_getauth(int sock, struct query *ctl, char *greeting)
 /* apply for connection authorization */
 {
-    char capabilities[POPBUFSIZE+1];
     int ok = 0;
+    char       password[PASSWORDLEN*2];
 
     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
-    gen_send(sock, "CAPABILITY");
-    if ((ok = gen_recv(sock, capabilities, sizeof(capabilities))))
-       return(ok);
-    if (strstr(capabilities, "BAD"))
+    capabilities[0] = '\0';
+    if ((ok = gen_transact(sock, "CAPABILITY")) == PS_SUCCESS)
     {
-       imap_version = IMAP2;
-       if (outlevel == O_VERBOSE)
-           error(0, 0, "Protocol identified as IMAP2 or IMAP2BIS");
+       /* UW-IMAP server 10.173 notifies in all caps */
+       if (strstr(capabilities, "IMAP4REV1"))
+       {
+           imap_version = IMAP4rev1;
+           if (outlevel == O_VERBOSE)
+               error(0, 0, "Protocol identified as IMAP4 rev 1");
+       }
+       else
+       {
+           imap_version = IMAP4;
+           if (outlevel == O_VERBOSE)
+               error(0, 0, "Protocol identified as IMAP4 rev 0");
+       }
     }
-    /* UW-IMAP server 10.173 notifies in all caps */
-    else if (strstr(capabilities, "IMAP4rev1") || strstr(capabilities, "IMAP4REV1"))
+    else if (ok == PS_ERROR)
     {
-       imap_version = IMAP4rev1;
+       imap_version = IMAP2;
        if (outlevel == O_VERBOSE)
-           error(0, 0, "Protocol identified as IMAP4 rev 1");
+           error(0, 0, "Protocol identified as IMAP2 or IMAP2BIS");
     }
     else
-    {
-       imap_version = IMAP4;
-       if (outlevel == O_VERBOSE)
-           error(0, 0, "Protocol identified as IMAP4 rev 0");
-    }
+       return(ok);
 
-    /* eat the tail of the CAPABILITY response (if any) */
-    if ((peek_capable = (imap_version >= IMAP4)))
-    {
-       char    scratchbuf[POPBUFSIZE]; /* don't clobber capabilities buffer */
+    peek_capable = (imap_version >= IMAP4);
 
-       if ((ok = gen_recv(sock, scratchbuf, sizeof(scratchbuf))))
-           return(ok);
-    }
+#if OPIE
+    if ((ctl->server.protocol == P_IMAP) && strstr(capabilities, "AUTH=X-OTP"))
+    {
+       if (outlevel == O_VERBOSE)
+           error(0, 0, "OTP authentication is supported");
+       if (do_otp(sock, ctl) == PS_SUCCESS)
+           return(PS_SUCCESS);
+    };
+#endif /* OPIE */
 
 #ifdef GSSAPI
     if (strstr(capabilities, "AUTH=GSSAPI"))
@@ -574,10 +674,36 @@ int imap_getauth(int sock, struct query *ctl, char *greeting)
     }
 #endif /* KERBEROS_V4 */
 
-    /* try to get authorized in the ordinary (AUTH=LOGIN) way */
-    ok = gen_transact(sock, "LOGIN %s \"%s\"", ctl->remotename, ctl->password);
+#ifdef __UNUSED__      /* The Cyrus IMAP4rev1 server chokes on this */
+    /* this handles either AUTH=LOGIN or AUTH-LOGIN */
+    if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN"))) {
+      error(0,-1, "Required LOGIN capability not supported by server");
+      return PS_AUTHFAIL;
+    };
+#endif /* __UNUSED__ */
+
+    imap_canonicalize(password, ctl->password);
+    ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", ctl->remotename, password);
     if (ok)
        return(ok);
+    
+    return(PS_SUCCESS);
+}
+
+static int internal_expunge(int sock)
+/* ship an expunge, resetting associated counters */
+{
+    int        ok;
+
+    if ((ok = gen_transact(sock, "EXPUNGE")))
+       return(ok);
+
+    expunged += deletions;
+    deletions = 0;
+
+#ifdef IMAP_UID        /* not used */
+    expunge_uids(ctl);
+#endif /* IMAP_UID */
 
     return(PS_SUCCESS);
 }
@@ -585,13 +711,13 @@ int imap_getauth(int sock, struct query *ctl, char *greeting)
 static int imap_getrange(int sock, 
                         struct query *ctl, 
                         const char *folder, 
-                        int *countp, int *newp)
+                        int *countp, int *newp, int *bytes)
 /* get range of messages to be fetched */
 {
     int ok;
 
     /* find out how many messages are waiting */
-    recent = unseen = -1;
+    *bytes = recent = unseen = -1;
 
     if (pass > 1)
     {
@@ -601,11 +727,7 @@ static int imap_getrange(int sock,
         */
        ok = 0;
        if (deletions && ctl->expunge > 1)
-           ok = gen_transact(sock, "EXPUNGE");
-#ifdef IMAP_UID        /* not used */
-       if (!ok)
-           expunge_uids(ctl);
-#endif /* IMAP_UID */
+           internal_expunge(sock);
        count = -1;
        if (ok || gen_transact(sock, "NOOP"))
        {
@@ -655,7 +777,7 @@ static int imap_getrange(int sock,
 static int imap_getsizes(int sock, int count, int *sizes)
 /* capture the sizes of all messages */
 {
-    char buf [POPBUFSIZE+1];
+    char buf [MSGBUFSIZE+1];
 
     /*
      * Some servers (as in, PMDF5.1-9.1 under OpenVMS 6.1)
@@ -698,7 +820,7 @@ static int imap_is_old(int sock, struct query *ctl, int number)
 static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
 /* request headers of nth message */
 {
-    char buf [POPBUFSIZE+1];
+    char buf [MSGBUFSIZE+1];
     int        num;
 
     /* expunges change the fetch numbers */
@@ -728,7 +850,7 @@ static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
 static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
 /* request body of nth message */
 {
-    char buf [POPBUFSIZE+1], *cp;
+    char buf [MSGBUFSIZE+1], *cp;
     int        num;
 
     /* expunges change the fetch numbers */
@@ -794,7 +916,7 @@ static int imap_trail(int sock, struct query *ctl, int number)
 
     for (;;)
     {
-       char buf[POPBUFSIZE+1];
+       char buf[MSGBUFSIZE+1];
        int ok;
 
        if ((ok = gen_recv(sock, buf, sizeof(buf))))
@@ -836,17 +958,7 @@ static int imap_delete(int sock, struct query *ctl, int number)
      * the next session.
      */
     if (NUM_NONZERO(ctl->expunge) && (deletions % ctl->expunge) == 0)
-    {
-       if ((ok = gen_transact(sock, "EXPUNGE")))
-           return(ok);
-
-#ifdef IMAP_UID        /* not used */
-       expunge_uids(ctl);
-#endif /* IMAP_UID */
-
-       expunged = deletions;
-       deletions = 0;
-    }
+       internal_expunge(sock);
 
     return(PS_SUCCESS);
 }
@@ -856,19 +968,7 @@ static int imap_logout(int sock, struct query *ctl)
 {
     /* if expunges after deletion have been suppressed, ship one now */
     if (NUM_SPECIFIED(ctl->expunge) && NUM_ZERO(ctl->expunge) && deletions)
-    {
-       int     ok;
-
-       if ((ok = gen_transact(sock, "EXPUNGE")))
-           return(ok);
-
-       expunged = deletions;
-       deletions = 0;
-
-#ifdef IMAP_UID        /* not used */
-       expunge_uids(ctl);
-#endif /* IMAP_UID */
-    }
+       internal_expunge(sock);
 
     return(gen_transact(sock, "LOGOUT"));
 }
@@ -876,10 +976,15 @@ static int imap_logout(int sock, struct query *ctl)
 const static struct method imap =
 {
     "IMAP",            /* Internet Message Access Protocol */
-    143,               /* standard IMAP2bis/IMAP4 port */
+#if INET6
+    "imap",
+#else /* INET6 */
+    143,                /* standard IMAP2bis/IMAP4 port */
+#endif /* INET6 */
     TRUE,              /* this is a tagged protocol */
     FALSE,             /* no message delimiter */
     imap_ok,           /* parse command response */
+    imap_canonicalize, /* deal with embedded slashes and spaces */
     imap_getauth,      /* get authorization */
     imap_getrange,     /* query range of messages */
     imap_getsizes,     /* get sizes of messages (used for --limit option */