From 314f76a3b3d0f495bf4dd9a2e668b1cf5e0e5f5a Mon Sep 17 00:00:00 2001 From: Brendan Cully Date: Mon, 12 Dec 2005 02:24:43 +0000 Subject: [PATCH] IMAP IDLE support. --- configure.in | 1 + imap/command.c | 17 ++++++++++++----- imap/imap.c | 35 +++++++++++++++++++++++++++++------ imap/imap_private.h | 6 +++++- mutt_socket.c | 33 +++++++++++++++++++++++++++++++++ mutt_socket.h | 3 +++ mutt_tunnel.c | 16 ++++++++++++++++ 7 files changed, 99 insertions(+), 12 deletions(-) diff --git a/configure.in b/configure.in index 844eda06..f5ecc9c6 100644 --- a/configure.in +++ b/configure.in @@ -519,6 +519,7 @@ dnl -- end socket dependencies -- if test "$need_socket" = "yes" then + AC_CHECK_HEADERS([sys/select.h]) AC_MSG_CHECKING([for socklen_t]) AC_EGREP_HEADER(socklen_t, sys/socket.h, AC_MSG_RESULT([yes]), AC_MSG_RESULT([no]) diff --git a/imap/command.c b/imap/command.c index e163b260..0d3c380c 100644 --- a/imap/command.c +++ b/imap/command.c @@ -60,6 +60,7 @@ static char *Capabilities[] = { "AUTH=ANONYMOUS", "STARTTLS", "LOGINDISABLED", + "IDLE", NULL }; @@ -81,6 +82,8 @@ int imap_cmd_queue (IMAP_DATA* idata, const char* cmdstr) /* seq, space, cmd, \r\n\0 */ cmdlen = strlen (cmd->seq) + strlen (cmdstr) + 4; + if (idata->state == IMAP_IDLE) + cmdlen += 6; /* DONE\r\n */ if (idata->cmdbuflen < cmdlen + (idata->cmdtail - idata->cmdbuf)) { unsigned int tailoff = idata->cmdtail - idata->cmdbuf; @@ -88,9 +91,13 @@ int imap_cmd_queue (IMAP_DATA* idata, const char* cmdstr) idata->cmdbuflen = tailoff + cmdlen; idata->cmdtail = idata->cmdbuf + tailoff; } - snprintf (idata->cmdtail, cmdlen, "%s %s\r\n", cmd->seq, cmdstr); + snprintf (idata->cmdtail, cmdlen, "%s%s %s\r\n", + idata->state == IMAP_IDLE ? "DONE\r\n" : "", cmd->seq, cmdstr); idata->cmdtail += cmdlen - 1; + if (idata->state == IMAP_IDLE) + idata->state = IMAP_SELECTED; + return 0; } @@ -265,7 +272,7 @@ void imap_cmd_finish (IMAP_DATA* idata) return; } - if (!(idata->state == IMAP_SELECTED) || idata->ctx->closing) + if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing) return; if (idata->reopen & IMAP_REOPEN_ALLOW) @@ -341,7 +348,7 @@ static void cmd_handle_fatal (IMAP_DATA* idata) { idata->status = IMAP_FATAL; - if ((idata->state == IMAP_SELECTED) && + if ((idata->state >= IMAP_SELECTED) && (idata->reopen & IMAP_REOPEN_ALLOW)) { mx_fastclose_mailbox (idata->ctx); @@ -350,7 +357,7 @@ static void cmd_handle_fatal (IMAP_DATA* idata) idata->state = IMAP_DISCONNECTED; } - if (idata->state != IMAP_SELECTED) + if (idata->state < IMAP_SELECTED) { idata->state = IMAP_DISCONNECTED; mutt_socket_close (idata->conn); @@ -367,7 +374,7 @@ static int cmd_handle_untagged (IMAP_DATA* idata) s = imap_next_word (idata->buf); - if ((idata->state == IMAP_SELECTED) && isdigit ((unsigned char) *s)) + if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s)) { pn = s; s = imap_next_word (s); diff --git a/imap/imap.c b/imap/imap.c index 71212dc0..4b6e293a 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -363,7 +363,7 @@ IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags) /* make sure this connection is not in SELECTED state, if neccessary */ if (flags & M_IMAP_CONN_NOSELECT) - while (conn->data && ((IMAP_DATA*) conn->data)->state == IMAP_SELECTED) + while (conn->data && ((IMAP_DATA*) conn->data)->state >= IMAP_SELECTED) { if (!(conn = mutt_conn_find (conn, account))) return NULL; @@ -1015,7 +1015,7 @@ int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint) idata = (IMAP_DATA*) ctx->data; - if (idata->state != IMAP_SELECTED) + if (idata->state < IMAP_SELECTED) { dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n")); return -1; @@ -1138,7 +1138,7 @@ void imap_close_mailbox (CONTEXT* ctx) if (ctx == idata->ctx) { - if (idata->status != IMAP_FATAL && idata->state == IMAP_SELECTED) + if (idata->status != IMAP_FATAL && idata->state >= IMAP_SELECTED) { /* mx_close_mailbox won't sync if there are no deleted messages * and the mailbox is unchanged, so we may have to close here */ @@ -1167,7 +1167,7 @@ void imap_close_mailbox (CONTEXT* ctx) } } -/* use the NOOP command to poll for new mail +/* use the NOOP or IDLE command to poll for new mail * * return values: * M_REOPENED mailbox has been externally modified @@ -1184,7 +1184,31 @@ int imap_check_mailbox (CONTEXT *ctx, int *index_hint, int force) idata = (IMAP_DATA*) ctx->data; - if ((force || time(NULL) >= idata->lastread + Timeout) + /* try IDLE first */ + if (mutt_bit_isset (idata->capabilities, IDLE) + && (idata->state != IMAP_IDLE + || time(NULL) >= idata->lastread + ImapKeepalive)) + { + imap_cmd_start (idata, "IDLE"); + if (imap_cmd_step (idata) != IMAP_CMD_RESPOND) + { + dprint (1, (debugfile, "Error starting IDLE\n")); + return -1; + } + idata->state = IMAP_IDLE; + } + if (idata->state == IMAP_IDLE) + { + while (mutt_socket_poll (idata->conn) > 0) + { + if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) + { + dprint (1, (debugfile, "Error reading IDLE response\n")); + return -1; + } + } + } + else if ((force || time(NULL) >= idata->lastread + Timeout) && imap_exec (idata, "NOOP", 0) != 0) return -1; @@ -1238,7 +1262,6 @@ int imap_buffy_check (int force) char command[LONG_STRING]; char munged[LONG_STRING]; int buffies = 0; - int rc; for (mailbox = Incoming; mailbox; mailbox = mailbox->next) { diff --git a/imap/imap_private.h b/imap/imap_private.h index 848ac983..cd66c517 100644 --- a/imap/imap_private.h +++ b/imap/imap_private.h @@ -79,7 +79,10 @@ enum IMAP_DISCONNECTED = 0, IMAP_CONNECTED, IMAP_AUTHENTICATED, - IMAP_SELECTED + IMAP_SELECTED, + + /* and pseudo-states */ + IMAP_IDLE }; enum @@ -119,6 +122,7 @@ enum AUTH_ANON, /* AUTH=ANONYMOUS */ STARTTLS, /* RFC 2595: STARTTLS */ LOGINDISABLED, /* LOGINDISABLED */ + IDLE, /* RFC 2177: IDLE */ CAPMAX }; diff --git a/mutt_socket.c b/mutt_socket.c index 6ecf4305..08edee34 100644 --- a/mutt_socket.c +++ b/mutt_socket.c @@ -39,6 +39,9 @@ #include #include #include +#ifdef HAVE_SYS_SELECT_H +#include +#endif #include #include @@ -129,6 +132,21 @@ int mutt_socket_write_d (CONNECTION *conn, const char *buf, int dbg) return rc; } +/* poll whether reads would block. + * Returns: >0 if there is data to read, + * 0 if a read would block, + * -1 if this connection doesn't support polling */ +int mutt_socket_poll (CONNECTION* conn) +{ + if (conn->bufpos < conn->available) + return conn->available - conn->bufpos; + + if (conn->conn_poll) + return conn->conn_poll (conn); + + return -1; +} + /* simple read buffering to speed things up. */ int mutt_socket_readchar (CONNECTION *conn, char *c) { @@ -279,6 +297,7 @@ CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account) conn->conn_write = raw_socket_write; conn->conn_open = raw_socket_open; conn->conn_close = raw_socket_close; + conn->conn_poll = raw_socket_poll; } return conn; @@ -390,6 +409,20 @@ int raw_socket_write (CONNECTION* conn, const char* buf, size_t count) return rc; } +int raw_socket_poll (CONNECTION* conn) +{ + fd_set rfds; + struct timeval tv = { 0, 0 }; + + if (conn->fd < 0) + return -1; + + FD_ZERO (&rfds); + FD_SET (conn->fd, &rfds); + + return select (conn->fd + 1, &rfds, NULL, NULL, &tv); +} + int raw_socket_open (CONNECTION* conn) { int rc; diff --git a/mutt_socket.h b/mutt_socket.h index 5be2a0fd..808ebe4b 100644 --- a/mutt_socket.h +++ b/mutt_socket.h @@ -48,11 +48,13 @@ typedef struct _connection int (*conn_write) (struct _connection *conn, const char *buf, size_t count); int (*conn_open) (struct _connection *conn); int (*conn_close) (struct _connection *conn); + int (*conn_poll) (struct _connection *conn); } CONNECTION; int mutt_socket_open (CONNECTION* conn); int mutt_socket_close (CONNECTION* conn); int mutt_socket_read (CONNECTION* conn, char* buf, size_t len); +int mutt_socket_poll (CONNECTION* conn); int mutt_socket_readchar (CONNECTION *conn, char *c); #define mutt_socket_readln(A,B,C) mutt_socket_readln_d(A,B,C,M_SOCK_LOG_CMD) int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION *conn, int dbg); @@ -68,5 +70,6 @@ int raw_socket_read (CONNECTION* conn, char* buf, size_t len); int raw_socket_write (CONNECTION* conn, const char* buf, size_t count); int raw_socket_open (CONNECTION *conn); int raw_socket_close (CONNECTION *conn); +int raw_socket_poll (CONNECTION* conn); #endif /* _MUTT_SOCKET_H_ */ diff --git a/mutt_tunnel.c b/mutt_tunnel.c index eddfbdf8..b43bbf93 100644 --- a/mutt_tunnel.c +++ b/mutt_tunnel.c @@ -45,6 +45,7 @@ static int tunnel_socket_open (CONNECTION*); static int tunnel_socket_close (CONNECTION*); static int tunnel_socket_read (CONNECTION* conn, char* buf, size_t len); static int tunnel_socket_write (CONNECTION* conn, const char* buf, size_t len); +static int tunnel_socket_poll (CONNECTION* conn); /* -- public functions -- */ int mutt_tunnel_socket_setup (CONNECTION *conn) @@ -53,6 +54,7 @@ int mutt_tunnel_socket_setup (CONNECTION *conn) conn->conn_close = tunnel_socket_close; conn->conn_read = tunnel_socket_read; conn->conn_write = tunnel_socket_write; + conn->conn_poll = tunnel_socket_poll; return 0; } @@ -175,3 +177,17 @@ static int tunnel_socket_write (CONNECTION* conn, const char* buf, size_t len) return rc; } + +static int tunnel_socket_poll (CONNECTION* conn) +{ + TUNNEL_DATA* tunnel = (TUNNEL_DATA*) conn->sockdata; + int ofd; + int rc; + + ofd = conn->fd; + conn->fd = tunnel->readfd; + rc = raw_socket_poll (conn); + conn->fd = ofd; + + return rc; +} -- 2.40.0