Commit 458307e7 authored by Thomas Roessler's avatar Thomas Roessler

* redoes the folder update optimisation I did yesterday. It's somewhat

  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 <[email protected]>)
parent f7e524f1
......@@ -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 == '+')
......
......@@ -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;
......
* 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 <[email protected]>
Updated 19990903
Updated 19990906
......@@ -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
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 <[email protected]>
Updated: 19990904
Updated: 19990906
......@@ -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++)
{
......
......@@ -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
This diff is collapsed.
......@@ -22,8 +22,17 @@
#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
......@@ -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;
......
......@@ -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;
}
......@@ -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);
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment