]> granicus.if.org Git - mutt/commitdiff
New new mail detection code. Now we use UIDVALIDITY/UIDNEXT to detect whether
authorBrendan Cully <brendan@kublai.com>
Fri, 16 Dec 2005 18:18:52 +0000 (18:18 +0000)
committerBrendan Cully <brendan@kublai.com>
Fri, 16 Dec 2005 18:18:52 +0000 (18:18 +0000)
a mailbox has been changed since we last saw it, rather than the more
ephemeral RECENT flag. We also keep a cache of mailboxes we've visited or
called STATUS on, which might eventually make for better information in
the browser and mailbox views. Big changes, probably not stable. IWFM.

imap/command.c
imap/imap.c
imap/imap_private.h
imap/message.c
imap/util.c

index 738e38aca59e1d70925d61f51088827f2262c6b6..e8d5f4d51e14199f72329f1d761a2d5f5435ac2b 100644 (file)
@@ -711,16 +711,27 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s)
   BUFFY* inc;
   IMAP_MBOX mx;
   int count;
-  IMAP_STATUS status;
+  IMAP_STATUS *status, sb;
+  int olduv, oldun;
 
   dprint (2, (debugfile, "Handling STATUS\n"));
   
   mailbox = imap_next_word (s);
   s = imap_next_word (mailbox);
   *(s - 1) = '\0';
-  
   imap_unmunge_mbox_name (mailbox);
-  status.name = mailbox;
+
+  if (!(status = imap_mboxcache_get (idata, mailbox)))
+  {
+    /* ugly interface - why should I look up what I just added? */
+    memset (&sb, 0, sizeof (IMAP_STATUS));
+    sb.name = mailbox;
+    idata->mboxcache = mutt_add_list_n (idata->mboxcache, &sb, sizeof (IMAP_STATUS));
+    status = imap_mboxcache_get (idata, mailbox);
+    status->name = safe_strdup (mailbox);
+  }
+  olduv = status->uidvalidity;
+  oldun = status->uidnext;
 
   if (*s++ != '(')
   {
@@ -733,38 +744,28 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s)
     count = strtol (value, &value, 10);
 
     if (!ascii_strncmp ("MESSAGES", s, 8))
-    {
-      dprint (2, (debugfile, "%d messages in %s\n", count, mailbox));
-      status.messages = count;
-    }
+      status->messages = count;
     else if (!ascii_strncmp ("RECENT", s, 6))
-    {
-      dprint (2, (debugfile, "%d recent in %s\n", count, mailbox));
-      status.recent = count;
-    }
+      status->recent = count;
     else if (!ascii_strncmp ("UIDNEXT", s, 7))
-    {
-      dprint (2, (debugfile, "UIDNEXT for %s is %d\n", mailbox, count));
-      status.uidnext = count;
-    }
+      status->uidnext = count;
     else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
-    {
-      dprint (2, (debugfile, "UIDVALIDITY for %s is %d\n", mailbox, count));
-      status.uidvalidity = count;
-    }
+      status->uidvalidity = count;
     else if (!ascii_strncmp ("UNSEEN", s, 6))
-    {
-      dprint (2, (debugfile, "%d unseen in %s\n", count, mailbox));
-      status.unseen = count;
-    }
-    
+      status->unseen = count;
+
     s = value;
+    if (*s && *s != ')')
+      s = imap_next_word (s);
   }
+  dprint (2, (debugfile, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
+              status->name, status->uidvalidity, status->uidnext,
+              status->messages, status->recent, status->unseen));
 
   /* caller is prepared to handle the result herself */
   if (idata->cmddata)
   {
-    memcpy (idata->cmddata, &status, sizeof (status));
+    memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
     return;
   }
 
@@ -783,7 +784,18 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s)
     if (mutt_account_match (&idata->conn->account, &mx.account) && mx.mbox
         && mutt_strncmp (mailbox, mx.mbox, strlen (mailbox)) == 0)
     {
-      inc->new = status.recent;
+      if (olduv && olduv == status->uidvalidity)
+      {
+        if (oldun < status->uidnext)
+        {
+          inc->new = status->unseen;
+        }
+      }
+      else
+        inc->new = status->unseen;
+      /* forced back to keep detecting new mail until the mailbox is opened */
+      status->uidnext = oldun;
+
       FREE (&mx.mbox);
       return;
     }
index 60bd5e4c70eab5dde3a95a0db60b18ae9d895ed3..4a2eef742f392d94cedb6bb8969d4429193472c7 100644 (file)
@@ -577,6 +577,7 @@ int imap_open_mailbox (CONTEXT* ctx)
 {
   CONNECTION *conn;
   IMAP_DATA *idata;
+  IMAP_STATUS* status, sb;
   char buf[LONG_STRING];
   char bufout[LONG_STRING];
   int count = 0;
@@ -625,6 +626,14 @@ int imap_open_mailbox (CONTEXT* ctx)
 
   imap_cmd_start (idata, bufout);
 
+  if (!(status = imap_mboxcache_get (idata, idata->mailbox)))
+  {
+    memset (&sb, 0, sizeof (IMAP_STATUS));
+    sb.name = idata->mailbox;
+    idata->mboxcache = mutt_add_list_n (idata->mboxcache, &sb, sizeof (IMAP_STATUS));
+    status = imap_mboxcache_get (idata, idata->mailbox);
+    status->name = safe_strdup (idata->mailbox);
+  }
   do
   {
     char *pc;
@@ -657,16 +666,22 @@ int imap_open_mailbox (CONTEXT* ctx)
       if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
        goto fail;
     }
-#ifdef USE_HCACHE
     /* save UIDVALIDITY for the header cache */
-    else if (ascii_strncasecmp("OK [UIDVALIDITY", pc, 14) == 0)
+    else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0)
     {
-      dprint(2, (debugfile, "Getting mailbox UIDVALIDITY\n"));
+      dprint (2, (debugfile, "Getting mailbox UIDVALIDITY\n"));
       pc += 3;
-      pc = imap_next_word(pc);
-      sscanf(pc, "%lu", &(idata->uid_validity));
+      pc = imap_next_word (pc);
+      sscanf (pc, "%lu", &(idata->uid_validity));
+      status->uidvalidity = idata->uid_validity;
+    }
+    else if (ascii_strncasecmp ("OK [UIDNEXT", pc, 11) == 0)
+    {
+      dprint (2, (debugfile, "Getting mailbox UIDNEXT\n"));
+      pc += 3;
+      pc = imap_next_word (pc);
+      sscanf (pc, "%lu", &status->uidnext);
     }
-#endif
     else
     {
       pc = imap_next_word (pc);
@@ -1279,9 +1294,7 @@ int imap_buffy_check (int force)
 
     /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
      * IDLEd elsewhere */
-    if (mutt_strcmp (name, idata->mailbox) == 0
-        || (ascii_strcasecmp (name, "INBOX") == 0
-            && mutt_strcasecmp (name, idata->mailbox) == 0))
+    if (!imap_mxcmp (name, idata->mailbox))
       continue;
       
     if (!lastdata)
@@ -1305,8 +1318,7 @@ int imap_buffy_check (int force)
     }
     
     imap_munge_mbox_name (munged, sizeof (munged), name);
-    /* we need a better way to detect new mail... */
-    snprintf (command, sizeof (command), "STATUS %s (RECENT)", munged);
+    snprintf (command, sizeof (command), "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN)", munged);
 
     if (imap_cmd_queue (idata, command) < 0)
     {
@@ -1353,9 +1365,7 @@ int imap_status (char* path)
   if (imap_get_mailbox (path, &idata, buf, sizeof (buf)) < 0)
     return -1;
 
-  if (mutt_strcmp (buf, idata->mailbox) == 0
-      || (ascii_strcasecmp (buf, "INBOX") == 0
-         && mutt_strcasecmp (buf, idata->mailbox) == 0))
+  if (!imap_mxcmp (buf, idata->mailbox))
     /* We are in the folder we're polling - just return the mailbox count */
     return idata->ctx->msgcount;
   else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) ||
@@ -1387,6 +1397,38 @@ int imap_status (char* path)
   return messages;
 }
 
+/* return cached mailbox stats or NULL */
+IMAP_STATUS* imap_mboxcache_get (IMAP_DATA* idata, const char* mbox)
+{
+  LIST* cur;
+  IMAP_STATUS* status;
+  
+  for (cur = idata->mboxcache; cur; cur = cur->next)
+  {
+    status = (IMAP_STATUS*)cur->data;
+
+    if (!imap_mxcmp (mbox, status->name))
+      return status;
+  }
+  
+  return NULL;
+}
+
+void imap_mboxcache_free (IMAP_DATA* idata)
+{
+  LIST* cur;
+  IMAP_STATUS* status;
+
+  for (cur = idata->mboxcache; cur; cur = cur->next)
+  {
+    status = (IMAP_STATUS*)cur->data;
+
+    FREE (&status->name);
+  }
+
+  mutt_free_list (&idata->mboxcache);
+}
+
 /* returns number of patterns in the search that should be done server-side
  * (eg are full-text) */
 static int do_search (const pattern_t* search, int allpats)
index fe1cdcef9a1351c2b589575646e3f252b8427fe0..71aab69c03ff837c6f776bb16e2b17206ff34906 100644 (file)
@@ -198,6 +198,9 @@ typedef struct
   int lastcmd;
   BUFFER* cmdbuf;
 
+  /* cache IMAP_STATUS of visited mailboxes */
+  LIST* mboxcache;
+
   /* The following data is all specific to the currently SELECTED mbox */
   char delim;
   CONTEXT *ctx;
@@ -207,9 +210,7 @@ typedef struct
   unsigned char rights[(RIGHTSMAX + 7)/8];
   unsigned int newMailCount;
   IMAP_CACHE cache[IMAP_CACHE_LEN];
-#ifdef USE_HCACHE
   unsigned long uid_validity;
-#endif
   
   /* all folder flags - system flags AND keywords */
   LIST *flags;
@@ -223,6 +224,8 @@ typedef struct
 /* imap.c */
 int imap_create_mailbox (IMAP_DATA* idata, char* mailbox);
 int imap_rename_mailbox (IMAP_DATA* idata, IMAP_MBOX* mx, const char* newname);
+IMAP_STATUS* imap_mboxcache_get (IMAP_DATA* idata, const char* mbox);
+void imap_mboxcache_free (IMAP_DATA* idata);
 int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, int changed);
 int imap_open_connection (IMAP_DATA* idata);
 IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags);
@@ -260,6 +263,7 @@ char* imap_fix_path (IMAP_DATA* idata, char* mailbox, char* path,
   size_t plen);
 int imap_get_literal_count (const char* buf, long* bytes);
 char* imap_get_qualifier (char* buf);
+int imap_mxcmp (const char* mx1, const char* mx2);
 char* imap_next_word (char* s);
 time_t imap_parse_date (char* s);
 void imap_qualify_path (char *dest, size_t len, IMAP_MBOX *mx, char* path);
index 69e099189889881774e12c21cc49d1e697f12ef0..f0323bf0941dc0a13232613505b8084eed2b0a00 100644 (file)
@@ -16,7 +16,6 @@
  *     along with this program; if not, write to the Free Software
  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *
- * $Id$
  */ 
 
 /* message parsing/updating functions */
@@ -64,8 +63,10 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
   char tempfile[_POSIX_PATH_MAX];
   int msgno;
   IMAP_HEADER h;
+  IMAP_STATUS* status;
   int rc, mfhrc, oldmsgcount;
   int fetchlast = 0;
+  int maxuid = 0;
   const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL";
 
 #if USE_HCACHE
@@ -262,6 +263,9 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
       ctx->hdrs[msgno]->received = h.received;
       ctx->hdrs[msgno]->data = (void *) (h.data);
 
+      if (maxuid < h.data->uid)
+        maxuid = h.data->uid;
+
       rewind (fp);
       /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends
        *   on h.received being set */
@@ -313,6 +317,9 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
     mx_update_context (ctx, ctx->msgcount - oldmsgcount);
   }
 
+  if (maxuid && (status = imap_mboxcache_get (idata, idata->mailbox)))
+    status->uidnext = maxuid + 1;
+
   return msgend;
 }
 
index 1f6bebe0073a3ef552d40af9ab4f4afed5a7fb17..1814ac327519bc94529b38e564d6b21584259e01 100644 (file)
@@ -172,6 +172,15 @@ int imap_parse_path (const char* path, IMAP_MBOX* mx)
   return 0;
 }
 
+/* silly helper for mailbox name string comparisons, because of INBOX */
+int imap_mxcmp (const char* mx1, const char* mx2)
+{
+  if (!ascii_strcasecmp (mx1, "INBOX") && !ascii_strcasecmp (mx2, "INBOX"))
+    return 0;
+  
+  return mutt_strcmp (mx1, mx2);
+}
+
 /* imap_pretty_mailbox: called by mutt_pretty_mailbox to make IMAP paths
  *   look nice. */
 void imap_pretty_mailbox (char* path)
@@ -266,6 +275,7 @@ void imap_free_idata (IMAP_DATA** idata)
 
   FREE (&(*idata)->capstr);
   mutt_free_list (&(*idata)->flags);
+  imap_mboxcache_free (*idata);
   mutt_buffer_free(&(*idata)->cmdbuf);
   FREE (&(*idata)->buf);
   FREE (idata);