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 == '+')
{
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)
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 "".
* The mutt_pretty routines don't work well when the delimiter isn't '/'.
Brendan Cully <brendan@kublai.com>
-Updated 19990903
+Updated 19990906
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
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: [** ]
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
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);
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;
}
}
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)
/* 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: "));
return -1;
}
- if (imap_sync_mailbox (ctx, M_NO) < 0)
+ if (imap_sync_mailbox (ctx, 0) < 0)
return -1;
idata = CTX_DATA;
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)
{
/* 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];
/* 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);
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))
{
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;
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++)
{
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 -- */
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
#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
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);
* 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;
do
{
if (mutt_socket_read_line_d (buf, sizeof (buf), CTX_DATA->conn) < 0)
- {
- return (-1);
- }
+ return -1;
if (buf[0] == '*')
{
if (!hdr)
{
imap_error ("imap_read_headers()", buf);
- return (-1);
+ return -1;
}
strncpy(fpc,pc,hdr-pc);
fpc += hdr-pc;
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 */
((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
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);
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);
}
}
}
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;
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;
/* 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;
}
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)
{
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);
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
#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;
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
#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;
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);
}
}
}
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 */
return rv;
}
-
#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 */
}
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++)
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);
int mx_sync_mailbox (CONTEXT *ctx)
{
int rc, i;
+ int purge = 1;
if (ctx->dontwrite)
{
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 &&
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);
#include "mutt_curses.h"
#include "sort.h"
#include "mapping.h"
+#include "mx.h"
#include <string.h>
#include <ctype.h>
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)