]> granicus.if.org Git - mutt/commitdiff
IMAP IDLE support.
authorBrendan Cully <brendan@kublai.com>
Mon, 12 Dec 2005 02:24:43 +0000 (02:24 +0000)
committerBrendan Cully <brendan@kublai.com>
Mon, 12 Dec 2005 02:24:43 +0000 (02:24 +0000)
configure.in
imap/command.c
imap/imap.c
imap/imap_private.h
mutt_socket.c
mutt_socket.h
mutt_tunnel.c

index 844eda069f4db5c05f2fdfb073ac704f89c18056..f5ecc9c661bb242c0700084b494ded4bf9526ad1 100644 (file)
@@ -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])
index e163b260c15d356722402ebe4c43b6b6adb049e5..0d3c380c20fc47641d49ec5513ca1485907772c4 100644 (file)
@@ -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);
index 71212dc0fa9b5932edffa4571bd15a34ff43191c..4b6e293a2b4970e5e9f7c5e60910e129082775e9 100644 (file)
@@ -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)
   {
index 848ac9832bbecb216cc2885854400369b5bbd0be..cd66c51742c7855e49494acebeeaa04087f76687 100644 (file)
@@ -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
 };
index 6ecf4305b377e2737daeb9390c0006b840d5e913..08edee346809ae0d41443e9d1ad9e63803fcc986 100644 (file)
@@ -39,6 +39,9 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
 #include <string.h>
 #include <errno.h>
 
@@ -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;
index 5be2a0fdfcf8dfdab016d9fd9c624bf2ca06efe4..808ebe4b200e30d45aeb465469817a7865db63d9 100644 (file)
@@ -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_ */
index eddfbdf8b74c3eea0215a6faf884fa422ec26a29..b43bbf9356aa328c6fd2e543dcefc5b773bd57e5 100644 (file)
@@ -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;
+}