#include <string.h>
#include <strings.h>
#include <ctype.h>
-#if defined(STDC_HEADERS)
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
-#endif
#include "fetchmail.h"
#include "socket.h"
-#include "i18n.h"
+#include "gettext.h"
/* imap_version values */
-#define IMAP2 -1 /* IMAP2 or IMAP2BIS, RFC1176 */
#define IMAP4 0 /* IMAP4 rev 0, RFC1730 */
#define IMAP4rev1 1 /* IMAP4 rev 1, RFC2060 */
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];
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 */
{
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);
}
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;
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;
report(stdout, GT_("Protocol identified as IMAP4 rev 0\n"));
}
}
- else if (ok == PS_ERROR)
- {
- imap_version = IMAP2;
- 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
report(stdout, GT_("will idle after poll\n"));
}
- peek_capable = (imap_version >= IMAP4);
+ peek_capable = TRUE;
+
+ return PS_SUCCESS;
}
static int do_authcert (int sock, const char *command, const char *name)
/* apply for connection authorization */
{
int ok = 0;
-#ifdef SSL_ENABLE
- int got_tls = 0;
-#endif
(void)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
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,
+ && (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)
+ ctl->server.pollname, &ctl->remotename)) != -1)
{
/*
* RFC 2595 says this:
* 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);
}
if ((ok = do_gssauth(sock, "AUTHENTICATE", "imap",
ctl->server.truename, ctl->remotename)))
{
- /* SASL cancellation of authentication */
if (ctl->server.authenticate != A_ANY)
return ok;
} else {
}
#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
* in a challenge-response.
{
if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
{
- /* SASL cancellation of authentication */
- gen_send(sock, "*");
if(ctl->server.authenticate != A_ANY)
return ok;
}
&& 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;
}
}
#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,
- GT_("Required LOGIN capability not supported by server\n"));
- }
-#endif /* __UNUSED__ */
-
/*
* We're stuck with sending the password en clair.
* The reason for this odd-looking logic is that some
free(remotename);
if (ok)
{
- /* SASL cancellation of authentication */
- gen_send(sock, "*");
if(ctl->server.authenticate != A_ANY)
return ok;
}
return(ok);
}
-/* maximum number of numbers we can process in "SEARCH" response */
-# define IMAP_SEARCH_MAX 1000
-
static int imap_search(int sock, struct query *ctl, int count)
/* search for unseen messages */
{
- int ok, first, last;
+ 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;
+ flag skipdeleted = ctl->keep;
const char *undeleted;
- /* Skip range search if there are less than or equal to
- * IMAP_SEARCH_MAX mails. */
- flag skiprangesearch = (count <= IMAP_SEARCH_MAX);
+ /* 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 (first = 1, last = IMAP_SEARCH_MAX; first <= count; first += IMAP_SEARCH_MAX, last += IMAP_SEARCH_MAX)
+ for (;;)
{
- if (last > count)
- last = count;
-
-restartsearch:
undeleted = (skipdeleted ? " UNDELETED" : "");
- if (skiprangesearch)
- gen_send(sock, "SEARCH UNSEEN%s", undeleted);
- else if (last == first)
- gen_send(sock, "SEARCH %d UNSEEN%s", last, undeleted);
- else
- gen_send(sock, "SEARCH %d:%d UNSEEN%s", first, last, undeleted);
- while ((ok = imap_response(sock, buf)) == PS_UNTAGGED)
+ 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")))
{
}
}
}
- /* if there is a protocol error on the first loop, try a
- * different search command */
- if (ok == PS_ERROR && first == 1)
+ if (ok != PS_ERROR) /* success or non-protocol error */
+ return(ok);
+
+ /* there is a protocol error. try a different search command. */
+ if (skipdeleted)
{
- if (skipdeleted)
- {
- /* retry with "SEARCH 1:1000 UNSEEN" */
- skipdeleted = FALSE;
- goto restartsearch;
- }
- if (!skiprangesearch)
- {
- /* retry with "SEARCH UNSEEN" */
- skiprangesearch = TRUE;
- goto restartsearch;
- }
- /* try with "FETCH 1:n FLAGS" */
- goto fetchflags;
+ /* retry with "SEARCH UNSEEN" */
+ skipdeleted = FALSE;
+ continue;
}
- if (ok != PS_SUCCESS)
- return(ok);
- /* loop back only when searching in range */
- if (skiprangesearch)
- break;
+ /* try with "FETCH 1:n FLAGS" */
+ break;
}
- return(PS_SUCCESS);
-fetchflags:
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)) == PS_UNTAGGED)
+ while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED)
{
unsigned int num;
int consumed;
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;
gen_send(sock, "FETCH %d RFC822.HEADER", number);
/* looking for FETCH response */
- if ((ok = imap_response(sock, buf)) == PS_UNTAGGED)
+ if ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED)
{
int consumed;
/* expected response formats:
/* try to recover for some responses */
if (!strncmp(buf, "* NO", 4) ||
- !strncmp(buf, "* BAD", 5))
+ !strncmp(buf, "* BAD", 5) ||
+ strstr(buf, "FETCH ()"))
{
return(PS_TRANSIENT);
}
/* an unexpected tagged response */
if (outlevel > O_SILENT)
report(stderr, GT_("Incorrect FETCH response: %s.\n"), buf);
- return(PS_ERROR);
+ return(PS_TRANSIENT);
}
return(ok);
}
* equivalent". However, we know of at least one server that
* treats them differently in the presence of MIME attachments;
* the latter form downloads the attachment, the former does not.
- * The server is InterChange, and the fool who implemented this
- * misfeature ought to be strung up by his thumbs.
+ * The server is InterChange.
*
* When I tried working around this by disabling use of the 4rev1 form,
* I found that doing this breaks operation with M$ Exchange.
* Annoyingly enough, Exchange's refusal to cope is technically legal
- * under RFC2062. Trust Microsoft, the Great Enemy of interoperability
- * standards, to find a way to make standards compliance irritating....
+ * under RFC2062.
*/
switch (imap_version)
{
case IMAP4rev1: /* RFC 2060 */
+ default:
gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
break;
case IMAP4: /* RFC 1730 */
gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
break;
-
- default: /* RFC 1176 */
- gen_send(sock, "FETCH %d RFC822.TEXT", number);
- break;
}
/* looking for FETCH response */
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)
number -= expunged;
/*
- * 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
* 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)))
+ if ((ok = gen_transact(sock, "STORE %d +FLAGS.SILENT (\\Seen \\Deleted)", number)))
return(ok);
else
deletions++;
/* expunges change the message numbers */
number -= expunged;
- return(gen_transact(sock,
- imap_version == IMAP4
- ? "STORE %d +FLAGS.SILENT (\\Seen)"
- : "STORE %d +FLAGS (\\Seen)",
- number));
+ return(gen_transact(sock,"STORE %d +FLAGS.SILENT (\\Seen)", number));
}
static int imap_end_mailbox_poll(int sock, struct query *ctl)