]> granicus.if.org Git - neomutt/commitdiff
* redoes the folder update optimisation I did yesterday. It's somewhat
authorThomas Roessler <roessler@does-not-exist.org>
Tue, 7 Sep 1999 06:53:08 +0000 (06:53 +0000)
committerThomas Roessler <roessler@does-not-exist.org>
Tue, 7 Sep 1999 06:53:08 +0000 (06:53 +0000)
  cleaner and less invasive, and I'm not so worried about memory leaks
  now.

* Fixes the bug where mutt would append a '/' to $folder even if it
  was only {mailhost}, causing mutt to browse the root directory
  instead of the home directory.

* includes a first stab at preserving the D flag on the IMAP server.
  Now if you answer no to 'Purge deleted', the server still stores
  the messages as deleted, but doesn't expunge them on exit.

  NOTE: this is a first attempt. Play around, but don't mark things
  as deleted that you'd be sorry to see disappear.

(From: Brendan Cully <brendan@kublai.com>)

13 files changed:
complete.c
flags.c
imap/BUGS
imap/Makefile.am
imap/TODO
imap/imap.c
imap/imap_private.h
imap/message.c
imap/message.h
mutt.h
muttlib.c
mx.c
status.c

index d9e1c011ac8a4050540239411e8fc7ff73dd213e..272b263b4efb280a65b3528b07ecdc8d80b1a8bc 100644 (file)
@@ -51,20 +51,22 @@ int mutt_complete (char *s, size_t slen)
   if (*s == '=' || *s == '+')
   {
     if (s[1])
-      snprintf (imap_path, sizeof(imap_path), "%s/%s", NONULL(Maildir),
-                s+1);
+    {
+      /* don't append '/' if Maildir is {host} only */
+      if (mx_is_imap (NONULL (Maildir)) && Maildir[strlen (Maildir)-1] == '}')
+        snprintf (imap_path, sizeof (imap_path), "%s%s", Maildir, s+1);
+      else
+        snprintf (imap_path, sizeof (imap_path), "%s/%s", NONULL (Maildir),
+          s+1);
+    }
     else
       strfcpy (imap_path, NONULL(Maildir), sizeof(imap_path));
   }
   else
-  {
     strfcpy (imap_path, s, sizeof(imap_path));
-  }
 
   if (mx_is_imap (imap_path))
-  {
     return imap_complete (s, slen, imap_path);
-  }
 #endif
   
   if (*s == '=' || *s == '+')
diff --git a/flags.c b/flags.c
index 0f62e6e163a2727f19f06daf316a75711b3d027d..0a454ba74d1ae57e04e751b91581a295032e88a7 100644 (file)
--- a/flags.c
+++ b/flags.c
@@ -39,6 +39,15 @@ void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx)
        {
          h->deleted = 1;
          if (upd_ctx) ctx->deleted++;
+#ifdef USE_IMAP
+          /* deleted messages aren't treated as changed elsewhere so that the
+           * purge-on-sync option works correctly. This isn't applicable here */
+          if (ctx->magic == M_IMAP)
+          {
+            h->changed = 1;
+           if (upd_ctx) ctx->changed = 1;
+          }
+#endif
        }
       }
       else if (h->deleted)
@@ -46,7 +55,7 @@ void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx)
        h->deleted = 0;
        if (upd_ctx) ctx->deleted--;
 #ifdef USE_IMAP
-/* if you undelete a message, the imap server will probably need to know. */
+        /* see my comment above */
        if (ctx->magic == M_IMAP) 
        {
          h->changed = 1;
index b75eb413ee659db44518b5334b1c4a1e81db7b18..d52db96e960e73793b8cb8eee18ce9980af99065 100644 (file)
--- a/imap/BUGS
+++ b/imap/BUGS
@@ -1,8 +1,6 @@
-* Mutt doesn't handle timeouts or dropped connections gracefully.
+In no particular order:
 
-* Mutt appends a '/' to $folder even when it is just '{server}', causing some
-  servers to look for folders off the root of the server instead of in the
-  home namespace.
+* Mutt doesn't handle timeouts or dropped connections gracefully.
 
 * Have a hard time when the home namespace isn't default "".
 
@@ -13,4 +11,4 @@
 * The mutt_pretty routines don't work well when the delimiter isn't '/'.
 
 Brendan Cully <brendan@kublai.com>
-Updated 19990903
+Updated 19990906
index 33eca53e1fc08dd0427fded9048653a1424e21cd..c69a17e0bbdef465aa75a92d83e2bf65695a09fd 100644 (file)
@@ -7,5 +7,4 @@ EXTRA_DIST = BUGS TODO
 noinst_LIBRARIES = libimap.a
 noinst_HEADERS = imap_private.h imap_socket.h md5.h message.h
 
-libimap_a_SOURCES = imap.c imap.h auth.c browse.c md5c.c socket.c \
-       message.c
+libimap_a_SOURCES = auth.c browse.c imap.c imap.h md5c.c message.c socket.c
index 340071d77f1f17c614e9c43d8564a05d1f819eb6..28b967125ef7f95c615776262b244ded5b22f418 100644 (file)
--- a/imap/TODO
+++ b/imap/TODO
@@ -1,4 +1,4 @@
-IMAP enhancements/fixes, by priority:
+IMAP enhancements, by priority:
 
 [ -- socket -- ]
 * Smarter connection code. Mutt should handle dropped connections/routing
@@ -18,23 +18,17 @@ IMAP enhancements/fixes, by priority:
   Possibly we could use a tree-view in the browser, w/ expand, collapse.
   For low-bandwidth lines we could defer getting subfolder lists until the
   folder is expanded.
-
-[ -- new features -- ]
-* Implement server message COPY, instead of FETCH/APPEND.
-
-  PRIORITY: [** ]
-
-* Implement the received folder on IMAP. (Wait on COPY).
-
+  Current thought is that <ENTER> will select a mailbox if it doesn't have
+  subfolders or enter the folder if it does. If it has messages and subfolders,
+  we'll use a new key to select it as a mailbox.
+  We should maybe add a new imap_format string for IMAP browsing, without all
+  the stat variables but with tags like how many messages are in the folders,
+  how many subfolders, that weird \Marked tag, etc.
   PRIORITY: [** ]
 
-* Commands for creating/deleting folders on the server, since users may not
-  otherwise be able to do this on IMAP servers.
-
-  PRIORITY: [** ]
-
-* Implement READ-ONLY support, and the x (quit without saving changes)
-  command.
+[ -- speed -- ]
+* Implement server message COPY, instead of FETCH/APPEND.
 
   PRIORITY: [** ]
 
@@ -54,5 +48,20 @@ IMAP enhancements/fixes, by priority:
 
   PRIORITY: [*  ]
 
+[ -- new features -- ]
+* Implement the received folder on IMAP. (Wait on COPY).
+
+  PRIORITY: [** ]
+
+* Commands for creating/deleting folders on the server, since users may not
+  otherwise be able to do this on IMAP servers.
+
+  PRIORITY: [** ]
+
+* Implement READ-ONLY support, and the x (quit without saving changes)
+  command.
+
+  PRIORITY: [** ]
+
 Brendan Cully <brendan@kublai.com>
-Updated: 19990904
+Updated: 19990906
index ea66c6888d1468d7e76a6e582163110bdd9c074f..b577de91dc40e1428bbe4fdced0757f9abae569e 100644 (file)
@@ -50,7 +50,6 @@ static void imap_parse_capabilities (IMAP_DATA *idata, char *s);
 static int imap_reopen_mailbox (CONTEXT *ctx, int *index_hint);
 static int imap_get_delim (IMAP_DATA *idata, CONNECTION *conn);
 static char* imap_get_flags (LIST** hflags, char* s);
-static int imap_has_flag (LIST* flag_list, const char* flag);
 static int imap_check_acl (IMAP_DATA *idata);
 static int imap_check_capabilities (IMAP_DATA *idata);
 static int imap_create_mailbox (IMAP_DATA *idata, char *mailbox);
@@ -1007,10 +1006,10 @@ int imap_open_mailbox (CONTEXT *ctx)
       else if (mutt_strncasecmp ("FLAGS", pc, 5) == 0)
       {
         /* don't override PERMANENTFLAGS */
-        if (!idata->mailbox_flags)
+        if (!idata->flags)
         {
           dprint (2, (debugfile, "Getting mailbox FLAGS\n"));
-          if ((pc = imap_get_flags (&(idata->mailbox_flags), pc)) == NULL)
+          if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
             return -1;
         }
       }
@@ -1020,10 +1019,10 @@ int imap_open_mailbox (CONTEXT *ctx)
         dprint (2, (debugfile,
           "Getting mailbox PERMANENTFLAGS\n"));
         /* safe to call on NULL */
-        mutt_free_list (&(idata->mailbox_flags));
+        mutt_free_list (&(idata->flags));
        /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
         pc += 13;
-        if ((pc = imap_get_flags (&(idata->mailbox_flags), pc)) == NULL)
+        if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
           return -1;
       }
       else if (imap_handle_untagged (idata, buf) != 0)
@@ -1035,11 +1034,11 @@ int imap_open_mailbox (CONTEXT *ctx)
   /* dump the mailbox flags we've found */
   if (debuglevel > 2)
   {
-    if (!idata->mailbox_flags)
+    if (!idata->flags)
       dprint (3, (debugfile, "No folder flags found\n"));
     else
     {
-      LIST* t = idata->mailbox_flags;
+      LIST* t = idata->flags;
 
       dprint (3, (debugfile, "Mailbox flags: "));
 
@@ -1118,7 +1117,7 @@ int imap_select_mailbox (CONTEXT* ctx, const char* path)
     return -1;
   }
 
-  if (imap_sync_mailbox (ctx, M_NO) < 0)
+  if (imap_sync_mailbox (ctx, 0) < 0)
     return -1;
 
   idata = CTX_DATA;
@@ -1261,44 +1260,6 @@ int imap_close_connection (CONTEXT *ctx)
   return 0;
 }
 
-/* imap_has_flag: do a caseless comparison of the flag against a flag list,
- *   return 1 if found or flag list has '\*', 0 otherwise */
-static int imap_has_flag (LIST* flag_list, const char* flag)
-{
-  if (!flag_list)
-    return 0;
-
-  flag_list = flag_list->next;
-  while (flag_list)
-  {
-    if (!mutt_strncasecmp (flag_list->data, flag, strlen (flag_list->data)))
-      return 1;
-
-    flag_list = flag_list->next;
-  }
-
-  return 0;
-}
-
-/* imap_stringify_flaglist: concatenate custom IMAP tags to list, if they
- *   appear in the folder flags list. Why wouldn't they? */
-static void imap_stringify_flaglist (LIST* flags, LIST* mailbox_flags, char *s)
-{
-  if (!mailbox_flags || !flags)
-    return;
-
-  flags = flags->next;
-  while (flags)
-  {
-    if (imap_has_flag (mailbox_flags, flags->data))
-    {
-      strcat (s, flags->data);
-      strcat (s, " ");
-    }
-    flags = flags->next;
-  }
-}
-
 static void imap_set_flag (CONTEXT *ctx, int aclbit, int flag, const char *str, 
   char *flags)
 {
@@ -1310,7 +1271,7 @@ static void imap_set_flag (CONTEXT *ctx, int aclbit, int flag, const char *str,
 /* update the IMAP server to reflect message changes done within mutt.
  * Arguments
  *   ctx: the current context
- *   expunge: M_YES or M_NO - do expunge? */
+ *   expunge: 0 or 1 - do expunge? */
 int imap_sync_mailbox (CONTEXT *ctx, int expunge)
 {
   char seq[8];
@@ -1321,7 +1282,7 @@ int imap_sync_mailbox (CONTEXT *ctx, int expunge)
   /* save status changes */
   for (n = 0; n < ctx->msgcount; n++)
   {
-    if (ctx->hdrs[n]->deleted || ctx->hdrs[n]->changed)
+    if (ctx->hdrs[n]->changed)
     {
       snprintf (buf, sizeof (buf), _("Saving message status flags... [%d/%d]"),
                n+1, ctx->msgcount);
@@ -1339,23 +1300,42 @@ int imap_sync_mailbox (CONTEXT *ctx, int expunge)
         flags);
 
       /* now make sure we don't lose custom tags */
-      imap_stringify_flaglist (ctx->hdrs[n]->server_flags,
-        CTX_DATA->mailbox_flags, flags);
+      imap_add_keywords (flags, ctx->hdrs[n], CTX_DATA->flags);
       
       mutt_remove_trailing_ws (flags);
       
       imap_make_sequence (seq, sizeof (seq));
-      snprintf (buf, sizeof (buf), "%s STORE %d FLAGS.SILENT (%s)\r\n", seq,
-        ctx->hdrs[n]->index + 1, NONULL(flags));
+      /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
+       * explicitly revoke all system flags (if we have permission) */
+      if (!*flags)
+      {
+        imap_set_flag (ctx, IMAP_ACL_SEEN, 1, "\\Seen ", flags);
+        imap_set_flag (ctx, IMAP_ACL_WRITE, 1, "\\Flagged ", flags);
+        imap_set_flag (ctx, IMAP_ACL_WRITE, 1, "\\Answered ", flags);
+        imap_set_flag (ctx, IMAP_ACL_DELETE, 1, "\\Deleted ", flags);
+
+        mutt_remove_trailing_ws (flags);
+
+        snprintf (buf, sizeof (buf), "%s STORE %d -FLAGS.SILENT (%s)\r\n", seq,
+          ctx->hdrs[n]->index + 1, flags);
+      }
+      else
+        snprintf (buf, sizeof (buf), "%s STORE %d FLAGS.SILENT (%s)\r\n", seq,
+          ctx->hdrs[n]->index + 1, flags);
       if (imap_exec (buf, sizeof (buf), CTX_DATA, seq, buf, 0) != 0)
       {
         imap_error ("imap_sync_mailbox()", buf);
-        return (-1);
+        /* Give up on this message if we pass here again */
+        ctx->hdrs[n]->changed = 0;
+        return -1;
       }
+
+      ctx->hdrs[n]->changed = 0;
     }
   }
+  ctx->changed = 0;
 
-  if (expunge == M_YES)
+  if (expunge == 1)
   {
     if (mutt_bit_isset(CTX_DATA->rights, IMAP_ACL_DELETE))
     {
@@ -1384,27 +1364,6 @@ int imap_sync_mailbox (CONTEXT *ctx, int expunge)
   return 0;
 }
 
-/* commit changes and terminate connection */
-static int imap_close_mailbox (IMAP_DATA *idata)
-{
-  char seq[8];
-  char buf[LONG_STRING];
-
-  /* tell the server to commit changes */
-  mutt_message _("Closing mailbox...");
-  imap_make_sequence (seq, sizeof (seq));
-  snprintf (buf, sizeof (buf), "%s CLOSE\r\n", seq);
-  if (imap_exec (buf, sizeof (buf), idata, seq, buf, 0) != 0)
-  {
-    imap_error ("imap_close_mailbox()", buf);
-    idata->status = IMAP_FATAL;
-    return (-1);
-  }
-  idata->state = IMAP_AUTHENTICATED;
-  return 0;
-}
-
-
 void imap_fastclose_mailbox (CONTEXT *ctx)
 {
   int i;
@@ -1414,8 +1373,11 @@ void imap_fastclose_mailbox (CONTEXT *ctx)
     return;
 
   if ((CTX_DATA->state == IMAP_SELECTED) && (ctx == CTX_DATA->selected_ctx))
-    if (imap_close_mailbox (CTX_DATA) != 0)
-      return;
+    CTX_DATA->state = IMAP_AUTHENTICATED;
+
+  /* free IMAP part of headers */
+  for (i = 0; i < ctx->msgcount; i++)
+    imap_free_header_data (&(ctx->hdrs[i]->data));
 
   for (i = 0; i < IMAP_CACHE_LEN; i++)
   {
index 6b10bcbc43a943b3e572643663a7f93705de74f8..3fcfbc0d3618da271b44d02fd4f3310f2ba6006d 100644 (file)
@@ -135,7 +135,8 @@ typedef struct
   unsigned char rights[(RIGHTSMAX + 7)/8];
   unsigned int newMailCount;
   IMAP_CACHE cache[IMAP_CACHE_LEN];
-  LIST *mailbox_flags;
+  /* all folder flags - system flags AND keywords */
+  LIST *flags;
 } IMAP_DATA;
 
 /* -- macros -- */
@@ -166,6 +167,8 @@ void imap_unquote_string (char *s);
 int imap_authenticate (IMAP_DATA *idata, CONNECTION *conn);
 
 /* message.c */
+void imap_add_keywords (char* s, HEADER* keywords, LIST* mailbox_flags);
+void imap_free_header_data (void** data);
 int imap_read_headers (CONTEXT* ctx, int msgbegin, int msgend);
 
 #endif
index 5789d5da1c1bf028c553bc320c9a71c9b390f997..6b0faf3e1bef2e6e80b5f1b834db931883c09ebe 100644 (file)
 #include "pgp.h"
 #endif
 
-static void flush_buffer(char *buf, size_t *len, CONNECTION *conn);
-static int msg_parse_fetch (IMAP_HEADER_INFO *h, char *s);
-static char* msg_parse_flags (IMAP_FLAGS* flags, char* s);
+static void flush_buffer(char* buf, size_t* len, CONNECTION* conn);
+static int msg_has_flag (LIST* flag_list, const char* flag);
+static IMAP_HEADER* msg_new_header (void);
+static int msg_parse_fetch (IMAP_HEADER* h, char* s);
+static char* msg_parse_flags (IMAP_HEADER* h, char* s);
 
 /* imap_read_headers:
  * Changed to read many headers instead of just one. It will return the
@@ -52,25 +54,41 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
   long ploc;
   long bytes = 0;
   int msgno,fetchlast;
-  IMAP_HEADER_INFO *h0,*h,*htemp;
-#define WANT_HEADERS  "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE IN-REPLY-TO REPLY-TO" 
-  const char *want_headers = WANT_HEADERS;
+  IMAP_HEADER *h, *h0;
+  const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE IN-REPLY-TO REPLY-TO";
   int using_body_peek = 0;
   fetchlast = 0;
 
+  /* define search string */
+  if (mutt_bit_isset(CTX_DATA->capabilities,IMAP4REV1))
+  {
+    snprintf(hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s)]", 
+      want_headers); 
+    using_body_peek = 1;
+  } 
+  else if (mutt_bit_isset(CTX_DATA->capabilities,IMAP4))
+  {
+    snprintf(hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s)", 
+      want_headers);
+  }
+  else
+  {    /* Unable to fetch headers for lower versions */
+    mutt_error _("Unable to fetch headers from this IMAP server version.");
+    sleep (1); /* pause a moment to let the user see the error */
+    return -1;
+  }
+
   /*
    * We now download all of the headers into one file. This should be
    * faster on most systems.
    */
   mutt_mktemp (tempfile);
   if (!(fp = safe_fopen (tempfile, "w+")))
-  {
-    return (-1);
-  }
+    return -1;
 
-  h0 = (IMAP_HEADER_INFO*) safe_malloc(sizeof(IMAP_HEADER_INFO));
-  h = h0;
-  for (msgno=msgbegin; msgno <= msgend ; msgno++)
+  h = msg_new_header ();
+  h0 = h;
+  for (msgno = msgbegin; msgno <= msgend ; msgno++)
   {
     snprintf (buf, sizeof (buf), _("Fetching message headers... [%d/%d]"), 
       msgno + 1, msgend + 1);
@@ -86,26 +104,9 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
        * If we get more messages while doing this, we make another
        * request for all the new messages.
        */
-      if (mutt_bit_isset(CTX_DATA->capabilities,IMAP4REV1))
-      {
-       snprintf(hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s)]", 
-               want_headers); 
-       using_body_peek = 1;
-      } 
-      else if (mutt_bit_isset(CTX_DATA->capabilities,IMAP4))
-      {
-       snprintf(hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s)", 
-               want_headers);
-      }
-      else
-      {        /* Unable to fetch headers for lower versions */
-       mutt_error _("Unable to fetch headers from this IMAP server version.");
-       sleep (1);      /* pause a moment to let the user see the error */
-       return (-1);
-      }
       snprintf (buf, sizeof (buf), 
-               "%s FETCH %d:%d (FLAGS INTERNALDATE RFC822.SIZE %s)\r\n", 
-               seq, msgno + 1, msgend + 1, hdrreq);
+       "%s FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)\r\n", 
+       seq, msgno + 1, msgend + 1, hdrreq);
 
       mutt_socket_write (CTX_DATA->conn, buf);
       fetchlast = msgend + 1;
@@ -114,9 +115,7 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
     do
     {
       if (mutt_socket_read_line_d (buf, sizeof (buf), CTX_DATA->conn) < 0)
-      {
-        return (-1);
-      }
+        return -1;
 
       if (buf[0] == '*')
       {
@@ -143,7 +142,7 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
            if (!hdr)
             {
               imap_error ("imap_read_headers()", buf);
-              return (-1);
+              return -1;
             }
             strncpy(fpc,pc,hdr-pc);
             fpc += hdr-pc;
@@ -153,25 +152,24 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
            if (imap_get_literal_count(buf, &bytes) < 0)
             {
               imap_error ("imap_read_headers()", buf);
-              return (-1);
+              return -1;
             }
             imap_read_bytes (fp, CTX_DATA->conn, bytes);
             if (mutt_socket_read_line_d (buf, sizeof (buf), CTX_DATA->conn) < 0)
-            {
-              return (-1);
-            }
+              return -1;
+
             pc = buf;
           }
         }
         else if (imap_handle_untagged (CTX_DATA, buf) != 0)
-          return (-1);
+          return -1;
       }
     }
     while ((msgno + 1) >= fetchlast && mutt_strncmp (seq, buf, SEQLEN) != 0);
 
     h->content_length = -bytes;
     if (msg_parse_fetch (h, fetchbuf) == -1)
-      return (-1);
+      return -1;
 
     /* subtract the header length; the total message size will be
        added to this */
@@ -185,12 +183,12 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
       ((IMAP_DATA *) ctx->data)->status = 0;
     }
 
-    h->next=safe_malloc(sizeof(IMAP_HEADER_INFO));
-    h=h->next;
+    h->next = msg_new_header ();
+    h = h->next;
   }
 
   rewind(fp);
-  h=h0;
+  h = h0;
 
   /*
    * Now that we have all the header information, we can tell mutt about
@@ -204,21 +202,22 @@ int imap_read_headers (CONTEXT *ctx, int msgbegin, int msgend)
 
     ctx->hdrs[msgno]->env = mutt_read_rfc822_header (fp, ctx->hdrs[msgno], 0);
     ploc=ftell(fp);
-    ctx->hdrs[msgno]->read = h->flags.read;
-    ctx->hdrs[msgno]->old = h->flags.old;
-    ctx->hdrs[msgno]->deleted = h->flags.deleted;
-    ctx->hdrs[msgno]->flagged = h->flags.flagged;
-    ctx->hdrs[msgno]->replied = h->flags.replied;
-    ctx->hdrs[msgno]->changed = h->flags.changed;
-    ctx->hdrs[msgno]->server_flags = h->flags.server_flags;
+    ctx->hdrs[msgno]->read = h->read;
+    ctx->hdrs[msgno]->old = h->old;
+    ctx->hdrs[msgno]->deleted = h->deleted;
+    ctx->hdrs[msgno]->flagged = h->flagged;
+    ctx->hdrs[msgno]->replied = h->replied;
+    ctx->hdrs[msgno]->changed = h->changed;
     ctx->hdrs[msgno]->received = h->received;
     ctx->hdrs[msgno]->content->length = h->content_length;
+    ctx->hdrs[msgno]->data = (void *) (h->data);
 
     mx_update_context (ctx); /* increments ->msgcount */
 
-    htemp=h;
-    h=h->next;
-    safe_free ((void **) &htemp);
+    h0 = h;
+    h = h->next;
+    /* hdata is freed later */
+    safe_free ((void **) &h0);
   }
   fclose(fp);
   unlink(tempfile);
@@ -318,35 +317,38 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
            pc = buf;
          }
           /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
-           * change (eg from \Unseen to \Seen) */
-          else if (strncasecmp ("FLAGS", pc, 5) == 0)
+           * change (eg from \Unseen to \Seen).
+           * Uncommitted changes in mutt take precedence. If we decide to
+           * incrementally update flags later, this won't stop us syncing */
+          else if ((strncasecmp ("FLAGS", pc, 5) == 0) &&
+            !ctx->hdrs[msgno]->changed)
           {
-           IMAP_FLAGS flags;
+           IMAP_HEADER* newh;
             HEADER* h = ctx->hdrs[msgno];
 
-            flags.server_flags = NULL;
+            newh = msg_new_header ();
 
             dprint (2, (debugfile, "imap_fetch_message: parsing FLAGS\n"));
-            if ((pc = msg_parse_flags (&flags, pc)) == NULL)
+            if ((pc = msg_parse_flags (newh, pc)) == NULL)
               return -1;
 
             /* update context sums */
-            ctx->new += ((flags.read | flags.old) ? 0 : 1) -
+            ctx->new += ((newh->read | newh->old) ? 0 : 1) -
               ((h->read | h->old) ? 0 : 1);
-            ctx->unread += h->read - flags.read;
-            ctx->deleted += h->deleted = flags.deleted;
-            ctx->flagged += h->flagged - flags.flagged;
+            ctx->unread += h->read - newh->read;
+            ctx->deleted += h->deleted = newh->deleted;
+            ctx->flagged += h->flagged - newh->flagged;
 
             /* now commit the new flags */
-            ctx->hdrs[msgno]->read = flags.read;
-            ctx->hdrs[msgno]->old = flags.old;
-            ctx->hdrs[msgno]->deleted = flags.deleted;
-            ctx->hdrs[msgno]->flagged = flags.flagged;
-            ctx->hdrs[msgno]->replied = flags.replied;
-            ctx->hdrs[msgno]->changed = flags.changed;
-
-            mutt_free_list (&(h->server_flags));
-            h->server_flags = flags.server_flags;
+            ctx->hdrs[msgno]->read = newh->read;
+            ctx->hdrs[msgno]->old = newh->old;
+            ctx->hdrs[msgno]->deleted = newh->deleted;
+            ctx->hdrs[msgno]->flagged = newh->flagged;
+            ctx->hdrs[msgno]->replied = newh->replied;
+
+            mutt_free_list (&(HEADER_DATA(h)->keywords));
+            HEADER_DATA(h)->keywords = newh->data->keywords;
+            safe_free ((void**) &newh);
           }
        }
       }
@@ -505,8 +507,79 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg)
   return 0;
 }
 
+/* imap_add_keywords: concatenate custom IMAP tags to list, if they
+ *   appear in the folder flags list. Why wouldn't they? */
+void imap_add_keywords (char* s, HEADER* h, LIST* mailbox_flags)
+{
+  LIST *keywords;
+
+  if (!mailbox_flags || !HEADER_DATA(h) || !HEADER_DATA(h)->keywords)
+    return;
+
+  keywords = HEADER_DATA(h)->keywords->next;
+
+  while (keywords)
+  {
+    if (msg_has_flag (mailbox_flags, keywords->data))
+    {
+      strcat (s, keywords->data);
+      strcat (s, " ");
+    }
+    keywords = keywords->next;
+  }
+}
+
+/* imap_free_header_data: free IMAP_HEADER structure */
+void imap_free_header_data (void** data)
+{
+  /* this should be safe even if the list wasn't used */
+  mutt_free_list (&(((IMAP_HEADER_DATA*) *data)->keywords));
+
+  safe_free (data);
+}
+
+/* msg_new_header: allocate and initialise a new IMAP_HEADER structure */
+static IMAP_HEADER* msg_new_header (void)
+{
+  IMAP_HEADER* h;
+
+  h = (IMAP_HEADER*) safe_malloc (sizeof (IMAP_HEADER));
+  h->data = (IMAP_HEADER_DATA*) safe_malloc (sizeof (IMAP_HEADER_DATA));
+
+  /* lists aren't allocated unless they're used */
+  h->data->keywords = NULL;
+
+  h->deleted = 0;
+  h->flagged = 0;
+  h->replied = 0;
+  h->read = 0;
+  h->old = 0;
+  h->changed = 0;
+
+  return h;
+}
+
+/* msg_has_flag: do a caseless comparison of the flag against a flag list,
+ *   return 1 if found or flag list has '\*', 0 otherwise */
+static int msg_has_flag (LIST* flag_list, const char* flag)
+{
+  if (!flag_list)
+    return 0;
+
+  flag_list = flag_list->next;
+  while (flag_list)
+  {
+    if (!mutt_strncasecmp (flag_list->data, flag, strlen (flag_list->data)))
+      return 1;
+
+    flag_list = flag_list->next;
+  }
+
+  return 0;
+}
+
 /* msg_parse_fetch: handle headers returned from header fetch */
-static int msg_parse_fetch (IMAP_HEADER_INFO *h, char *s)
+static int msg_parse_fetch (IMAP_HEADER *h, char *s)
 {
   char tmp[SHORT_STRING];
   char *ptmp;
@@ -520,10 +593,17 @@ static int msg_parse_fetch (IMAP_HEADER_INFO *h, char *s)
 
     if (mutt_strncasecmp ("FLAGS", s, 5) == 0)
     {
-      h->flags.server_flags = NULL;
-      if ((s = msg_parse_flags (&(h->flags), s)) == NULL)
+      if ((s = msg_parse_flags (h, s)) == NULL)
         return -1;
     }
+    else if (mutt_strncasecmp ("UID", s, 3) == 0)
+    {
+      s += 3;
+      SKIPWS (s);
+      h->data->uid = (unsigned int) atoi (s);
+
+      s = imap_next_word (s);
+    }
     else if (mutt_strncasecmp ("INTERNALDATE", s, 12) == 0)
     {
       s += 12;
@@ -568,7 +648,7 @@ static int msg_parse_fetch (IMAP_HEADER_INFO *h, char *s)
 
 /* msg_parse_flags: fill out the message header according to the flags from the
  *   server. Expects a flags line of the form "FLAGS (flag flag ...)" */
-static char* msg_parse_flags (IMAP_FLAGS* flags, char* s)
+static char* msg_parse_flags (IMAP_HEADER* h, char* s)
 {
   int recent = 0;
 
@@ -589,36 +669,28 @@ static char* msg_parse_flags (IMAP_FLAGS* flags, char* s)
   }
   s++;
 
-  /* reset the current flags */
-  flags->deleted = 0;
-  flags->flagged = 0;
-  flags->replied = 0;
-  flags->read = 0;
-  flags->old = 0;
-  flags->changed = 0;
-
   /* start parsing */
   while (*s && *s != ')')
   {
     if (mutt_strncasecmp ("\\deleted", s, 8) == 0)
     {
       s += 8;
-      flags->deleted = 1;
+      h->deleted = 1;
     }
     else if (mutt_strncasecmp ("\\flagged", s, 8) == 0)
     {
       s += 8;
-      flags->flagged = 1;
+      h->flagged = 1;
     }
     else if (mutt_strncasecmp ("\\answered", s, 9) == 0)
     {
       s += 9;
-      flags->replied = 1;
+      h->replied = 1;
     }
     else if (mutt_strncasecmp ("\\seen", s, 5) == 0)
     {
       s += 5;
-      flags->read = 1;
+      h->read = 1;
     }
     else if (mutt_strncasecmp ("\\recent", s, 5) == 0)
     {
@@ -631,14 +703,14 @@ static char* msg_parse_flags (IMAP_FLAGS* flags, char* s)
       char ctmp;
       char* flag_word = s;
 
-      if (!flags->server_flags)
-        flags->server_flags = mutt_new_list ();
+      if (!h->data->keywords)
+        h->data->keywords = mutt_new_list ();
 
       while (*s && !ISSPACE (*s) && *s != ')')
         s++;
       ctmp = *s;
       *s = '\0';
-      mutt_add_list (flags->server_flags, flag_word);
+      mutt_add_list (h->data->keywords, flag_word);
       *s = ctmp;
     }
     SKIPWS(s);
@@ -648,8 +720,8 @@ static char* msg_parse_flags (IMAP_FLAGS* flags, char* s)
   if (*s == ')')
   {
     /* if a message is neither seen nor recent, it is OLD. */
-    if (option (OPTMARKOLD) && !recent && !(flags->read))
-      flags->old = 1;
+    if (option (OPTMARKOLD) && !recent && !(h->read))
+      h->old = 1;
     s++;
   }
   else
index 1669d8c60157c9be614e8f4e6efa7a813a8b7e75..ab31fd8ff104ff976c453c30ef04439f95fb78bb 100644 (file)
 #ifndef MESSAGE_H
 #define MESSAGE_H 1
 
-/* Data from the FLAGS response */
-typedef struct imap_flags
+/* -- data structures -- */
+/* IMAP-specific header data, stored as HEADER->data */
+typedef struct imap_header_data
+{
+  unsigned int uid;    /* 32-bit Message UID */
+  LIST *keywords;
+} IMAP_HEADER_DATA;
+
+/* Linked list to hold header information while downloading message
+ * headers */
+typedef struct imap_header
 {
   unsigned int read : 1;
   unsigned int old : 1;
@@ -32,19 +41,14 @@ typedef struct imap_flags
   unsigned int replied : 1;
   unsigned int changed : 1;
 
-  LIST* server_flags;  /* flags mutt doesn't use, but the server does */
-} IMAP_FLAGS;
-
-/* Linked list to hold header information while downloading message
- * headers */
-typedef struct imap_header_info
-{
-  IMAP_FLAGS flags;
+  IMAP_HEADER_DATA* data;
   unsigned int number;
 
   time_t received;
   long content_length;
-  struct imap_header_info *next;
-} IMAP_HEADER_INFO;
+  struct imap_header *next;
+} IMAP_HEADER;
 
+/* -- macros -- */
+#define HEADER_DATA(ph) ((IMAP_HEADER_DATA*) ((ph)->data))
 #endif
diff --git a/mutt.h b/mutt.h
index a3a5110d5d85e93f68e0e4f3250e049cb3217bcf..508b1f4760a12384c9a1975c21028adaaf560802 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -585,7 +585,7 @@ typedef struct header
 #endif
 
 #ifdef USE_IMAP
-  LIST *server_flags;  /* server custom flags, which should be preserved */
+  void *data;            /* driver-specific data (only used by IMAP */
 #endif
 } HEADER;
 
index c8cdedaa0f7de48803f841c33e4c1d165b561ba6..1b9958037dbedd05ca3f069e63e6431d1dcbe036 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -158,9 +158,6 @@ void mutt_free_header (HEADER **h)
   safe_free ((void **) &(*h)->path);
 #ifdef MIXMASTER
   mutt_free_list (&(*h)->chain);
-#endif
-#ifdef USE_IMAP
-  mutt_free_list (&(*h)->server_flags);
 #endif
   safe_free ((void **) h);
 }
@@ -229,7 +226,15 @@ char *mutt_expand_path (char *s, size_t slen)
     }
   }
   else if (*s == '=' || *s == '+')
+  {
+#ifdef USE_IMAP
+  /* special case: folder = {host}: don't append slash */
+  if (mx_is_imap (NONULL (Maildir)) && Maildir[strlen (Maildir) - 1] == '}')
+    snprintf (p, sizeof (p), "%s%s", NONULL (Maildir), s + 1);
+  else
+#endif
     snprintf (p, sizeof (p), "%s/%s", NONULL (Maildir), s + 1);
+  }
   else if (*s == '@')
   {
     /* elm compatibility, @ expands alias to user name */
@@ -912,4 +917,3 @@ int state_printf(STATE *s, const char *fmt, ...)
   
   return rv;
 }
-
diff --git a/mx.c b/mx.c
index 606db5871886be0bb0ed8901e77aaaf7c26d27e0..b1ec0fd27475c0063431ff6354eaa7b7a146208c 100644 (file)
--- a/mx.c
+++ b/mx.c
@@ -730,7 +730,7 @@ static int sync_mailbox (CONTEXT *ctx)
 #ifdef USE_IMAP
     case M_IMAP:
       /* extra argument means EXPUNGE */
-      rc = imap_sync_mailbox (ctx, M_YES);
+      rc = imap_sync_mailbox (ctx, 1);
       break;
 #endif /* USE_IMAP */
   }
@@ -862,6 +862,14 @@ int mx_close_mailbox (CONTEXT *ctx)
     return 0;
   }
   
+#ifdef USE_IMAP
+  /* allow IMAP to preserve the deleted flag across sessions */
+  if (ctx->magic == M_IMAP)
+  {
+    if (imap_sync_mailbox (ctx, purge) == -1)
+      return -1;
+  }
+#endif
   if (!purge)
   {
     for (i = 0; i < ctx->msgcount; i++)
@@ -875,6 +883,11 @@ int mx_close_mailbox (CONTEXT *ctx)
       return (-1);
   }
 
+#ifdef USE_IMAP
+  if (ctx->magic == M_IMAP && !purge)
+    mutt_message (_("%d kept."), ctx->msgcount);
+  else
+#endif
   if (move_messages)
     mutt_message (_("%d kept, %d moved, %d deleted."),
                  ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
@@ -979,6 +992,7 @@ void mx_update_tables(CONTEXT *ctx, int committing)
 int mx_sync_mailbox (CONTEXT *ctx)
 {
   int rc, i;
+  int purge = 1;
 
   if (ctx->dontwrite)
   {
@@ -1011,22 +1025,40 @@ int mx_sync_mailbox (CONTEXT *ctx)
     snprintf (buf, sizeof (buf), ctx->deleted == 1
             ? _("Purge %d deleted message?") : _("Purge %d deleted messages?"),
              ctx->deleted);
-    if ((rc = query_quadoption (OPT_DELETE, buf)) < 0)
+    if ((purge = query_quadoption (OPT_DELETE, buf)) < 0)
       return (-1);
-    else if (rc == M_NO)
+    else if (purge == M_NO)
     {
       if (!ctx->changed)
        return 0; /* nothing to do! */
-      for (i = 0 ; i < ctx->msgcount ; i++)
-       ctx->hdrs[i]->deleted = 0;
-      ctx->deleted = 0;
+#ifdef USE_IMAP
+      /* let IMAP servers hold on to D flags */
+      if (ctx->magic != M_IMAP)
+#endif
+      {
+        for (i = 0 ; i < ctx->msgcount ; i++)
+          ctx->hdrs[i]->deleted = 0;
+        ctx->deleted = 0;
+      }
     }
   }
 
-  if ((rc = sync_mailbox (ctx)) == 0)
+#ifdef USE_IMAP
+  if (ctx->magic == M_IMAP)
+    rc = imap_sync_mailbox (ctx, purge);
+  else
+#endif
+  rc = sync_mailbox (ctx);
+  if (rc == 0)
   {
+#ifdef USE_IMAP
+    if (ctx->magic == M_IMAP && !purge)
+      mutt_message (_("%d kept."), ctx->msgcount);
+    else
+#endif
     mutt_message (_("%d kept, %d deleted."), ctx->msgcount - ctx->deleted,
-                 ctx->deleted);
+      ctx->deleted);
+
     sleep (1); /* allow the user time to read the message */
 
     if (ctx->msgcount == ctx->deleted &&
@@ -1038,8 +1070,12 @@ int mx_sync_mailbox (CONTEXT *ctx)
       return 0;
     }
 
-    mx_update_tables(ctx, 1);
-    mutt_sort_headers (ctx, 1); /* rethread from scratch */
+    /* if we haven't deleted any messages, we don't need to resort */
+    if (purge)
+    {
+      mx_update_tables(ctx, 1);
+      mutt_sort_headers (ctx, 1); /* rethread from scratch */
+    }
   }
 
   return (rc);
index 20553862b586e3e9ee2aca4bffdc9de47f6ff6f9..1f11fb27e5fb7b5d39d3fa16b759ea32d5736d57 100644 (file)
--- a/status.c
+++ b/status.c
@@ -21,6 +21,7 @@
 #include "mutt_curses.h"
 #include "sort.h"
 #include "mapping.h"
+#include "mx.h"
 
 #include <string.h>
 #include <ctype.h>
@@ -204,8 +205,13 @@ status_format_str (char *buf, size_t buflen, char op, const char *src,
 
       if (Context)
       {
-       i = option(OPTATTACHMSG) ? 3 : ((Context->readonly || Context->dontwrite) ? 2 :
-                                       (Context->changed || Context->deleted) ? 1 : 0);
+       i = option(OPTATTACHMSG) ? 3 : ((Context->readonly ||
+          Context->dontwrite) ? 2 : (Context->changed || (
+#ifdef USE_IMAP
+        /* deleted doesn't necessarily mean changed in IMAP */
+          Context->magic != M_IMAP &&
+#endif
+          Context->deleted)) ? 1 : 0);
       }
       
       if (!StChars)