/* interpret untagged status responses */
if (strstr(buf, "* CAPABILITY"))
+ {
strncpy(capabilities, buf + 12, sizeof(capabilities));
+ capabilities[sizeof(capabilities)-1] = '\0';
+ }
else if (strstr(buf, "EXISTS"))
{
count = atoi(buf+2);
return(i);
}
-static int imap_getauth(int sock, struct query *ctl, char *greeting)
-/* apply for connection authorization */
+static void capa_probe(int sock, struct query *ctl)
+/* set capability variables from a CAPA probe */
{
- int ok = 0;
+ int ok;
/* probe to see if we're running IMAP4 and can use RFC822.PEEK */
capabilities[0] = '\0';
if (outlevel >= O_DEBUG)
report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
}
- else
- return(ok);
-
- peek_capable = (imap_version >= IMAP4);
-
- /*
- * 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;
/*
* Handle idling. We depend on coming through here on startup
if (outlevel >= O_VERBOSE)
report(stdout, GT_("will idle after poll\n"));
}
+}
+
+static int imap_getauth(int sock, struct query *ctl, char *greeting)
+/* apply for connection authorization */
+{
+ int ok = 0;
+#ifdef SSL_ENABLE
+ flag did_stls = FALSE;
+#endif /* SSL_ENABLE */
+
+ capa_probe(sock, ctl);
/*
* If either (a) we saw a PREAUTH token in the greeting, or
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.
#endif /* KERBEROS_V4 */
#ifdef SSL_ENABLE
- if ((ctl->server.authenticate == A_ANY)
+ if ((!ctl->sslproto || !strcmp(ctl->sslproto,"tls1"))
&& !ctl->use_ssl
&& strstr(capabilities, "STARTTLS"))
{
char *realhost;
realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
- gen_transact(sock, "STARTTLS");
+ ok = gen_transact(sock, "STARTTLS");
/* We use "tls1" instead of ctl->sslproto, as we want STARTTLS,
* not other SSL protocols
*/
- if (SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
+ if (ok == PS_SUCCESS &&
+ SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
{
+ if (!ctl->sslproto && !ctl->wehaveauthed)
+ {
+ ctl->sslproto = xstrdup("");
+ /* repoll immediately */
+ return(PS_REPOLL);
+ }
report(stderr,
GT_("SSL connection failed.\n"));
return(PS_AUTHFAIL);
}
+ did_stls = TRUE;
+
+ /*
+ * RFC 2595 says this:
+ *
+ * "Once TLS has been started, the client MUST discard cached
+ * information about server capabilities and SHOULD re-issue the
+ * CAPABILITY command. This is necessary to protect against
+ * man-in-the-middle attacks which alter the capabilities list prior
+ * to STARTTLS. The server MAY advertise different capabilities
+ * after STARTTLS."
+ */
+ capa_probe(sock, ctl);
}
#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.
+ */
+ if (NUM_SPECIFIED(ctl->expunge))
+ expunge_period = NUM_VALUE_OUT(ctl->expunge);
+ else
+ expunge_period = 1;
+
/*
* 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 ((ctl->server.authenticate == A_ANY && strstr(capabilities, "AUTH=CRAM-MD5"))
+ || ctl->server.authenticate == A_CRAM_MD5)
{
if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
{
strcpy(shroud, password);
ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
shroud[0] = '\0';
+#ifdef SSL_ENABLE
+ /* this is for servers which claim to support TLS, but actually
+ * don't! */
+ if (did_stls && ok == PS_SOCKET && !ctl->sslproto && !ctl->wehaveauthed)
+ {
+ ctl->sslproto = xstrdup("");
+ /* repoll immediately */
+ ok = PS_REPOLL;
+ }
+#endif
if (ok)
{
/* SASL cancellation of authentication */
static int imap_idle(int sock)
/* start an RFC2177 IDLE */
{
+ int ok;
+
+ /* special timeout to terminate the IDLE and re-issue it
+ * at least every 28 minutes:
+ * (the server may have an inactivity timeout) */
stage = STAGE_IDLE;
saved_timeout = mytimeout;
- mytimeout = 0;
+ mytimeout = 1680; /* 28 min */
+
+ /* enter IDLE mode */
+ ok = gen_transact(sock, "IDLE");
+
+ if(ok == PS_IDLETIMEOUT) {
+ /* send "DONE" continuation */
+ SockWrite(sock, "DONE\r\n", 6);
+ if (outlevel >= O_MONITOR)
+ report(stdout, "IMAP> DONE\n");
- return (gen_transact(sock, "IDLE"));
+ /* restore normal timeout value */
+ mytimeout = saved_timeout;
+ stage = STAGE_FETCH;
+
+ /* get OK IDLE message */
+ return imap_ok(sock, NULL);
+ } else
+ /* not idle timeout */
+ return ok;
}
static int imap_getrange(int sock,
* craps out during the message, it will still be marked `unseen' on
* the server.
*
- * However...*don't* do this if we're using keep to suppress deletion!
- * In that case, marking the seen flag is the only way to prevent the
- * message from being re-fetched on subsequent runs (and according
- * to RFC2060 p.43 this fetch should set Seen as a side effect).
- *
* According to RFC2060, and Mark Crispin the IMAP maintainer,
* FETCH %d BODY[TEXT] and RFC822.TEXT are "functionally
* equivalent". However, we know of at least one server that
switch (imap_version)
{
case IMAP4rev1: /* RFC 2060 */
- if (!ctl->keep)
- gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
- else
- gen_send(sock, "FETCH %d BODY[TEXT]", number);
+ gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
break;
case IMAP4: /* RFC 1730 */
- if (!ctl->keep)
- gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
- else
- gen_send(sock, "FETCH %d RFC822.TEXT", number);
+ gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
break;
default: /* RFC 1176 */
/* UW IMAP returns "OK FETCH", Cyrus returns "OK Completed" */
if (strstr(buf, "OK"))
break;
-
-#ifdef __UNUSED__
- /*
- * Any IMAP server that fails to set Seen on a BODY[TEXT]
- * fetch violates RFC2060 p.43 (top). This becomes an issue
- * when keep is on, because seen messages aren't deleted and
- * get refetched on each poll. As a workaround, if keep is on
- * we can set the Seen flag explicitly.
- *
- * This code isn't used yet because we don't know of any IMAP
- * servers broken in this way.
- */
- if (ctl->keep)
- if ((ok = gen_transact(sock,
- imap_version == IMAP4
- ? "STORE %d +FLAGS.SILENT (\\Seen)"
- : "STORE %d +FLAGS (\\Seen)",
- number)))
- return(ok);
-#endif /* __UNUSED__ */
}
return(PS_SUCCESS);
return(PS_SUCCESS);
}
+static int imap_mark_seen(int sock, struct query *ctl, int number)
+/* mark the given message as seen */
+{
+ return(gen_transact(sock,
+ imap_version == IMAP4
+ ? "STORE %d +FLAGS.SILENT (\\Seen)"
+ : "STORE %d +FLAGS (\\Seen)",
+ number));
+}
+
static int imap_logout(int sock, struct query *ctl)
/* send logout command */
{
imap_fetch_body, /* request given message body */
imap_trail, /* eat message trailer */
imap_delete, /* delete the message */
+ imap_mark_seen, /* how to mark a message as seen */
imap_logout, /* expunge and exit */
TRUE, /* yes, we can re-poll */
};