#define IMAP4 0 /* IMAP4 rev 0, RFC1730 */
#define IMAP4rev1 1 /* IMAP4 rev 1, RFC2060 */
-static int count, unseen, deletions, imap_version, preauth;
-static int expunged, expunge_period, saved_timeout;
+static int count, unseen, deletions = 0;
+static int expunged, expunge_period, saved_timeout = 0;
+static int imap_version, preauth;
static flag do_idle;
static char capabilities[MSGBUFSIZE+1];
static unsigned int *unseen_messages;
-int imap_ok(int sock, char *argbuf)
+static int imap_ok(int sock, char *argbuf)
/* parse command response */
{
char buf[MSGBUFSIZE+1];
}
else if (strstr(buf, "PREAUTH"))
preauth = TRUE;
+ /*
+ * The server may decide to make the mailbox read-only,
+ * which causes fetchmail to go into a endless loop
+ * fetching the same message over and over again.
+ *
+ * However, for check_only, we use EXAMINE which will
+ * mark the mailbox read-only as per the RFC.
+ *
+ * This checks for the condition and aborts if
+ * the mailbox is read-only.
+ *
+ * See RFC 2060 section 6.3.1 (SELECT).
+ * See RFC 2060 section 6.3.2 (EXAMINE).
+ */
+ else if (!check_only && strstr(buf, "[READ-ONLY]"))
+ return(PS_LOCKBUSY);
} while
(tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
if ((gen_recv(sock, msgbuf, sizeof msgbuf)))
return result;
- len = from64tobits ((unsigned char*)&challenge, msgbuf);
+ len = from64tobits ((unsigned char*)&challenge, msgbuf, sizeof(msgbuf));
if (outlevel >= O_DEBUG)
dumpSmbNtlmAuthChallenge(stdout, &challenge);
report(stdout, "IMAP> %s\n", msgbuf);
strcat(msgbuf,"\r\n");
-
SockWrite (sock, msgbuf, strlen (msgbuf));
if ((result = gen_recv (sock, msgbuf, sizeof msgbuf)))
}
#endif /* NTLM */
-int imap_canonicalize(char *result, char *raw, int maxlen)
+static int imap_canonicalize(char *result, char *raw, int maxlen)
/* encode an IMAP password as per RFC1730's quoting conventions */
{
int i, j;
return(i);
}
-int imap_getauth(int sock, struct query *ctl, char *greeting)
+static int imap_getauth(int sock, struct query *ctl, char *greeting)
/* apply for connection authorization */
{
int ok = 0;
capabilities[0] = '\0';
if ((ok = gen_transact(sock, "CAPABILITY")) == PS_SUCCESS)
{
- /* UW-IMAP server 10.173 notifies in all caps */
+ char *cp;
+
+ /* capability checks are supposed to be caseblind */
+ for (cp = capabilities; *cp; cp++)
+ *cp = toupper(*cp);
+
+ /* UW-IMAP server 10.173 notifies in all caps, but RFC2060 says we
+ should expect a response in mixed-case */
if (strstr(capabilities, "IMAP4REV1"))
{
imap_version = IMAP4rev1;
if (outlevel >= O_DEBUG)
- report(stdout, _("Protocol identified as IMAP4 rev 1\n"));
+ report(stdout, GT_("Protocol identified as IMAP4 rev 1\n"));
}
else
{
imap_version = IMAP4;
if (outlevel >= O_DEBUG)
- report(stdout, _("Protocol identified as IMAP4 rev 0\n"));
+ report(stdout, GT_("Protocol identified as IMAP4 rev 0\n"));
}
}
else if (ok == PS_ERROR)
{
imap_version = IMAP2;
if (outlevel >= O_DEBUG)
- report(stdout, _("Protocol identified as IMAP2 or IMAP2BIS\n"));
+ report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
}
else
return(ok);
{
do_idle = TRUE;
if (outlevel >= O_VERBOSE)
- report(stdout, _("will idle after poll\n"));
+ report(stdout, GT_("will idle after poll\n"));
}
/*
* If either (a) we saw a PREAUTH token in the greeting, or
* (b) the user specified ssh preauthentication, then we're done.
*/
- if (preauth || ctl->server.preauthenticate == A_SSH)
+ if (preauth || ctl->server.authenticate == A_SSH)
{
preauth = FALSE; /* reset for the next session */
return(PS_SUCCESS);
}
-#if OPIE_ENABLE
- if ((ctl->server.protocol == P_IMAP) && strstr(capabilities, "AUTH=X-OTP"))
- {
- if (outlevel >= O_DEBUG)
- report(stdout, _("OTP authentication is supported\n"));
- if (do_otp(sock, ctl) == PS_SUCCESS)
- return(PS_SUCCESS);
- };
-#endif /* OPIE_ENABLE */
+ /*
+ * Time to authenticate the user.
+ * Try the protocol variants that don't require passwords first.
+ */
+ ok = PS_AUTHFAIL;
#ifdef GSSAPI
- if (strstr(capabilities, "AUTH=GSSAPI"))
- {
- if (ctl->server.protocol == P_IMAP_GSS)
- {
- if (outlevel >= O_DEBUG)
- report(stdout, _("GSS authentication is supported\n"));
- return do_gssauth(sock, ctl->server.truename, ctl->remotename);
- }
- }
- else if (ctl->server.protocol == P_IMAP_GSS)
- {
- report(stderr,
- _("Required GSS capability not supported by server\n"));
- return(PS_AUTHFAIL);
- }
+ 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 (strstr(capabilities, "AUTH=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 (outlevel >= O_DEBUG)
- report(stdout, _("KERBEROS_V4 authentication is supported\n"));
-
- if (ctl->server.protocol == P_IMAP_K4)
+ if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename)))
{
- if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename)))
- /* SASL cancellation of authentication */
- gen_send(sock, "*");
-
- return(ok);
+ /* SASL cancellation of authentication */
+ gen_send(sock, "*");
+ if(ctl->server.authenticate != A_ANY)
+ return ok;
}
- /* else fall through to ordinary AUTH=LOGIN case */
- }
- else if (ctl->server.protocol == P_IMAP_K4)
- {
- report(stderr,
- _("Required KERBEROS_V4 capability not supported by server\n"));
- return(PS_AUTHFAIL);
+ else
+ return ok;
}
#endif /* KERBEROS_V4 */
- if (strstr(capabilities, "AUTH=CRAM-MD5"))
+ /*
+ * 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 (outlevel >= O_DEBUG)
- report (stdout, _("CRAM-MD5 authentication is supported\n"));
- if (ctl->server.protocol != P_IMAP_LOGIN)
- {
- if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl)))
- /* SASL cancellation of authentication */
- gen_send(sock, "*");
-
- return(ok);
- }
+ if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
+ {
+ /* SASL cancellation of authentication */
+ gen_send(sock, "*");
+ if(ctl->server.authenticate != A_ANY)
+ return ok;
+ }
+ else
+ return ok;
}
- else if (ctl->server.protocol == P_IMAP_CRAM_MD5)
+
+#if OPIE_ENABLE
+ if ((ctl->server.authenticate == A_ANY
+ || ctl->server.authenticate == A_OTP)
+ && strstr(capabilities, "AUTH=X-OTP"))
+ if ((ok = do_otp(sock, "AUTHENTICATE", ctl)))
+ {
+ /* SASL cancellation of authentication */
+ gen_send(sock, "*");
+ if(ctl->server.authenticate != A_ANY)
+ return ok;
+ }
+ else
+ return ok;
+#else
+ if (ctl->server.authenticate == A_OTP)
{
- report(stderr,
- _("Required CRAM-MD5 capability not supported by server\n"));
- return(PS_AUTHFAIL);
+ report(stderr,
+ GT_("Required OTP capability not compiled into fetchmail\n"));
}
+#endif /* OPIE_ENABLE */
#ifdef NTLM_ENABLE
- if (strstr (capabilities, "AUTH=NTLM"))
+ if ((ctl->server.authenticate == A_ANY
+ || ctl->server.authenticate == A_NTLM)
+ && 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;
+ }
+ else
+ return(ok);
+ }
+#else
+ if (ctl->server.authenticate == A_NTLM)
{
- if (outlevel >= O_DEBUG)
- report (stdout, _("NTLM authentication is supported\n"));
- return do_imap_ntlm (sock, ctl);
+ report(stderr,
+ GT_("Required NTLM capability not compiled into fetchmail\n"));
}
#endif /* NTLM_ENABLE */
#ifdef __UNUSED__ /* The Cyrus IMAP4rev1 server chokes on this */
/* this handles either AUTH=LOGIN or AUTH-LOGIN */
- if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN"))) {
- report(stderr,
- _("Required LOGIN capability not supported by server\n"));
- return PS_AUTHFAIL;
- };
+ if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN")))
+ {
+ report(stderr,
+ GT_("Required LOGIN capability not supported by server\n"));
+ }
#endif /* __UNUSED__ */
+ /* we're stuck with sending the password en clair */
+ if ((ctl->server.authenticate == A_ANY
+ || ctl->server.authenticate == A_PASSWORD)
+ && !strstr (capabilities, "LOGINDISABLED"))
{
/* these sizes guarantee no buffer overflow */
char remotename[NAMELEN*2+1], password[PASSWORDLEN*2+1];
imap_canonicalize(remotename, ctl->remotename, NAMELEN);
imap_canonicalize(password, ctl->password, PASSWORDLEN);
+
+ strcpy(shroud, ctl->password);
ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
+ shroud[0] = '\0';
+ if (ok)
+ {
+ /* SASL cancellation of authentication */
+ gen_send(sock, "*");
+ if(ctl->server.authenticate != A_ANY)
+ return ok;
+ }
+ else
+ return(ok);
}
- if (ok)
- return(ok);
-
- return(PS_SUCCESS);
+ return(ok);
}
static int internal_expunge(int sock)
ok = imap_idle(sock);
if (ok || gen_transact(sock, "NOOP"))
{
- report(stderr, _("re-poll failed\n"));
+ report(stderr, GT_("re-poll failed\n"));
return(ok);
}
else if (count == -1) /* no EXISTS response to NOOP/IDLE */
count = 0;
}
if (outlevel >= O_DEBUG)
- report(stdout, _("%d messages waiting after re-poll\n"), count);
+ report(stdout, GT_("%d messages waiting after re-poll\n"), count);
}
else
{
folder ? folder : "INBOX");
if (ok != 0)
{
- report(stderr, _("mailbox selection failed\n"));
+ report(stderr, GT_("mailbox selection failed\n"));
return(ok);
}
else if (outlevel >= O_DEBUG)
- report(stdout, _("%d messages waiting after first poll\n"), count);
+ report(stdout, GT_("%d messages waiting after first poll\n"), count);
/* no messages? then we may need to idle until we get some */
if (count == 0 && do_idle)
imap_idle(sock);
+
+ /*
+ * We should have an expunge here to
+ * a) avoid fetching deleted mails during 'fetchall'
+ * b) getting a wrong count of mails during 'no fetchall'
+ */
+ if (!check_only && !ctl->keep && count > 0)
+ {
+ ok = internal_expunge(sock);
+ if (ok)
+ {
+ report(stderr, GT_("expunge failed\n"));
+ return(ok);
+ }
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("%d messages waiting after expunge\n"), count);
+ }
}
*countp = count;
ok = gen_recv(sock, buf, sizeof(buf));
if (ok != 0)
{
- report(stderr, _("search for unseen messages failed\n"));
+ report(stderr, GT_("search for unseen messages failed\n"));
return(PS_PROTOCOL);
}
else if ((cp = strstr(buf, "* SEARCH")))
if (outlevel >= O_DEBUG)
report(stdout,
- _("%u is unseen\n"),
+ GT_("%u is unseen\n"),
unseen_messages[unseen]);
unseen++;
}
} while
(tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
- }
+ } else
+ unseen = -1;
*newp = unseen;
expunged = 0;
+ deletions = 0;
return(PS_SUCCESS);
}
gen_send(sock, "FETCH 1:%d RFC822.SIZE", count);
for (;;)
{
- int num, size, ok;
+ unsigned int num, size;
+ int ok;
if ((ok = gen_recv(sock, buf, sizeof(buf))))
return(ok);
else if (strstr(buf, "OK") || strstr(buf, "NO"))
break;
- else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %d)", &num, &size) == 2)
- sizes[num - 1] = size;
+ else if (sscanf(buf, "* %u FETCH (RFC822.SIZE %u)", &num, &size) == 2) {
+ if (num > 0 && num <= count)
+ sizes[num - 1] = size;
+ else
+ report(stderr, "Warning: ignoring bogus data for message sizes returned by the server.\n");
+ }
}
return(PS_SUCCESS);
return(seen);
}
+static char *skip_token(char *ptr)
+{
+ while(isspace(*ptr)) ptr++;
+ while(!isspace(*ptr) && !iscntrl(*ptr)) ptr++;
+ while(isspace(*ptr)) ptr++;
+ return(ptr);
+}
+
static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
/* request headers of nth message */
{
gen_send(sock, "FETCH %d RFC822.HEADER", number);
/* looking for FETCH response */
- do {
+ for (;;)
+ {
int ok;
+ char *ptr;
if ((ok = gen_recv(sock, buf, sizeof(buf))))
return(ok);
- } while
- (sscanf(buf+2, "%d FETCH (%*s {%d}", &num, lenp) != 2);
+ ptr = skip_token(buf); /* either "* " or "AXXXX " */
+ if (sscanf(ptr, "%d FETCH (%*s {%d}", &num, lenp) == 2)
+ break;
+ /* try to recover from chronically fucked-up M$ Exchange servers */
+ else if (!strncmp(ptr, "NO", 2))
+ return(PS_TRANSIENT);
+ else if (!strncmp(ptr, "BAD", 3))
+ return(PS_TRANSIENT);
+ }
if (num != number)
return(PS_ERROR);
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 ESMTP SIZE option) */