summaryrefslogtreecommitdiffstats
path: root/imap.c
diff options
context:
space:
mode:
Diffstat (limited to 'imap.c')
-rw-r--r--imap.c918
1 files changed, 918 insertions, 0 deletions
diff --git a/imap.c b/imap.c
new file mode 100644
index 00000000..022d72d1
--- /dev/null
+++ b/imap.c
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mutt.h"
+#include "mutt_curses.h"
+#include "mx.h"
+#include "mailbox.h"
+
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/* Minimal support for IMAP 4rev1 */
+
+#define IMAP_PORT 143
+
+#define SEQLEN 5
+
+/* number of entries in the hash table */
+#define IMAP_CACHE_LEN 10
+
+enum
+{
+ IMAP_FATAL = 1,
+ IMAP_NEW_MAIL,
+ IMAP_BYE
+};
+
+typedef struct
+{
+ int index;
+ char *path;
+} IMAP_CACHE;
+
+typedef struct
+{
+ short status;
+ unsigned short sequence;
+ unsigned short newMailCount;
+ short xxx;
+ IMAP_CACHE cache[IMAP_CACHE_LEN];
+} IMAP_DATA;
+
+static char ImapUser[SHORT_STRING] = { 0 };
+static char ImapPass[SHORT_STRING] = { 0 };
+
+static int imap_read_line (char *buf, size_t buflen, int fd)
+{
+ char ch;
+ int i;
+
+ for (i = 0; i < buflen; i++)
+ {
+ if (read (fd, &ch, 1) != 1)
+ return (-1);
+ if (ch == '\n')
+ break;
+ buf[i] = ch;
+ }
+ buf[i-1] = 0;
+ return (i + 1);
+}
+
+static int imap_read_line_d (char *buf, size_t buflen, int fd)
+{
+ int r = imap_read_line (buf, buflen, fd);
+ dprint (1,(debugfile,"imap_read_line_d():%s\n", buf));
+ return r;
+}
+
+static void imap_make_sequence (char *buf, size_t buflen, CONTEXT *ctx)
+{
+ snprintf (buf, buflen, "a%04d", ((IMAP_DATA *) ctx->data)->sequence++);
+}
+
+static int imap_write (int fd, const char *buf)
+{
+ dprint (1,(debugfile,"imap_write():%s", buf));
+ return (write (fd, buf, strlen (buf)));
+}
+
+static void imap_error (const char *where, const char *msg)
+{
+ dprint (1, (debugfile, "imap_error(): unexpected response in %s: %s\n", where, msg));
+}
+
+/* date is of the form: DD-MMM-YYYY HH:MM:SS +ZZzz */
+static time_t imap_parse_date (char *s)
+{
+ struct tm t;
+ time_t tz;
+
+ t.tm_mday = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != '-')
+ return 0;
+ s++;
+ t.tm_mon = mutt_check_month (s);
+ s += 3;
+ if (*s != '-')
+ return 0;
+ s++;
+ t.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0') - 1900;
+ s += 4;
+ if (*s != ' ')
+ return 0;
+ s++;
+
+ /* time */
+ t.tm_hour = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ':')
+ return 0;
+ s++;
+ t.tm_min = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ':')
+ return 0;
+ s++;
+ t.tm_sec = (s[0] - '0') * 10 + (s[1] - '0');
+ s += 2;
+ if (*s != ' ')
+ return 0;
+ s++;
+
+ /* timezone */
+ tz = ((s[1] - '0') * 10 + (s[2] - '0')) * 3600 +
+ ((s[3] - '0') * 10 + (s[4] - '0')) * 60;
+ if (s[0] == '+')
+ tz = -tz;
+
+ return (mutt_mktime (&t, 0) + tz);
+}
+
+static int imap_parse_fetch (HEADER *h, char *s)
+{
+ char tmp[SHORT_STRING];
+ char *ptmp;
+ int state = 0;
+
+ if (!s)
+ return (-1);
+
+ h->read = 0;
+ h->old = 0;
+
+ while (*s)
+ {
+ SKIPWS (s);
+
+ switch (state)
+ {
+ case 0:
+ if (strncasecmp ("FLAGS", s, 5) == 0)
+ {
+ s += 5;
+ SKIPWS (s);
+ if (*s != '(')
+ {
+ dprint (1, (debugfile, "imap_parse_fetch(): bogus FLAGS entry: %s\n", s));
+ return (-1); /* parse error */
+ }
+ s++;
+ state = 1;
+ }
+ else if (strncasecmp ("INTERNALDATE", s, 12) == 0)
+ {
+ s += 12;
+ SKIPWS (s);
+ if (*s != '\"')
+ {
+ dprint (1, (debugfile, "imap_parse_fetch(): bogus INTERNALDATE entry: %s\n", s));
+ return (-1);
+ }
+ s++;
+ ptmp = tmp;
+ while (*s && *s != '\"')
+ *ptmp++ = *s++;
+ if (*s != '\"')
+ return (-1);
+ s++; /* skip past the trailing " */
+ *ptmp = 0;
+ h->received = imap_parse_date (tmp);
+ }
+ else if (strncasecmp ("RFC822.SIZE", s, 11) == 0)
+ {
+ s += 11;
+ SKIPWS (s);
+ ptmp = tmp;
+ while (isdigit (*s))
+ *ptmp++ = *s++;
+ *ptmp = 0;
+ }
+ else if (*s == ')')
+ s++; /* end of request */
+ else
+ {
+ /* got something i don't understand */
+ imap_error ("imap_parse_fetch()", s);
+ return (-1);
+ }
+ break;
+ case 1: /* flags */
+ if (*s == ')')
+ {
+ s++;
+ state = 0;
+ }
+ else if (strncasecmp ("\\deleted", s, 8) == 0)
+ {
+ s += 8;
+ h->deleted = 1;
+ }
+ else if (strncasecmp ("\\flagged", s, 8) == 0)
+ {
+ s += 8;
+ h->flagged = 1;
+ }
+ else if (strncasecmp ("\\answered", s, 9) == 0)
+ {
+ s += 9;
+ h->replied = 1;
+ }
+ else if (strncasecmp ("\\seen", s, 5) == 0)
+ {
+ s += 5;
+ h->read = 1;
+ }
+ else
+ {
+ while (*s && !ISSPACE (*s) && *s != ')')
+ s++;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int imap_read_bytes (FILE *fp, int fd, long bytes)
+{
+ long pos;
+ long len;
+ char buf[LONG_STRING];
+
+ for (pos = 0; pos < bytes; )
+ {
+ len = imap_read_line (buf, sizeof (buf), fd);
+ if (len < 0)
+ return (-1);
+ pos += len;
+ fputs (buf, fp);
+ fputs ("\n", fp);
+ }
+
+ return 0;
+}
+
+/* returns 1 if the command result was OK, or 0 if NO or BAD */
+static int imap_code (const char *s)
+{
+ s += SEQLEN;
+ SKIPWS (s);
+ return (strncasecmp ("OK", s, 2) == 0);
+}
+
+static char *imap_next_word (char *s)
+{
+ while (*s && !ISSPACE (*s))
+ s++;
+ SKIPWS (s);
+ return s;
+}
+
+static int imap_handle_untagged (CONTEXT *ctx, char *s)
+{
+ char *pn;
+ int count;
+
+ s = imap_next_word (s);
+
+ if (isdigit (*s))
+ {
+ pn = s;
+ s = imap_next_word (s);
+
+ if (strncasecmp ("EXISTS", s, 6) == 0)
+ {
+ /* new mail arrived */
+ count = atoi (pn);
+
+ if (count <= ctx->msgcount)
+ {
+ /* something is wrong because the server reported fewer messages
+ * than we previously saw
+ */
+ mutt_error ("Fatal error. Message count is out of sync!");
+ ((IMAP_DATA *) ctx->data)->status = IMAP_FATAL;
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+ else
+ {
+ ((IMAP_DATA *) ctx->data)->status = IMAP_NEW_MAIL;
+ ((IMAP_DATA *) ctx->data)->newMailCount = count;
+ }
+ }
+ }
+ else if (strncasecmp ("BYE", s, 3) == 0)
+ {
+ /* server shut down our connection */
+ s += 3;
+ SKIPWS (s);
+ mutt_error (s);
+ ((IMAP_DATA *) ctx->data)->status = IMAP_BYE;
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+ else
+ {
+ dprint (1, (debugfile, "imap_unhandle_untagged(): unhandled request: %s\n",
+ s));
+ }
+
+ return 0;
+}
+
+static int imap_read_header (CONTEXT *ctx, int msgno)
+{
+ char buf[LONG_STRING];
+ FILE *fp;
+ char tempfile[_POSIX_PATH_MAX];
+ char seq[8];
+ char *pc;
+ char *pn;
+ long bytes;
+
+ ctx->hdrs[ctx->msgcount]->index = ctx->msgcount;
+
+ mutt_mktemp (tempfile);
+ if (!(fp = safe_fopen (tempfile, "w+")))
+ {
+ return (-1);
+ }
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s FETCH %d RFC822.HEADER\r\n", seq, msgno + 1);
+ imap_write (ctx->fd, buf);
+
+ do
+ {
+ if (imap_read_line (buf, sizeof (buf), ctx->fd) < 0)
+ {
+ return (-1);
+ }
+
+ if (buf[0] == '*')
+ {
+ pc = buf;
+ pc = imap_next_word (pc);
+ pc = imap_next_word (pc);
+ if (strncasecmp ("FETCH", pc, 5) == 0)
+ {
+ if (!(pc = strchr (pc, '{')))
+ {
+ imap_error ("imap_read_header()", buf);
+ return (-1);
+ }
+ pc++;
+ pn = pc;
+ while (isdigit (*pc))
+ pc++;
+ *pc = 0;
+ bytes = atoi (pn);
+
+ imap_read_bytes (fp, ctx->fd, bytes);
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (seq, buf, SEQLEN) != 0);
+
+ rewind (fp);
+ ctx->hdrs[msgno]->env = mutt_read_rfc822_header (fp, ctx->hdrs[msgno]);
+
+ fclose (fp);
+ unlink (tempfile);
+
+ /* get the status of this message */
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s FETCH %d FAST\r\n", seq, msgno + 1);
+ imap_write (ctx->fd, buf);
+ do
+ {
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ break;
+ if (buf[0] == '*')
+ {
+ pc = buf;
+ pc = imap_next_word (pc);
+ pc = imap_next_word (pc);
+ if (strncasecmp ("FETCH", pc, 5) == 0)
+ {
+ if (!(pc = strchr (pc, '(')))
+ {
+ imap_error ("imap_read_header()", buf);
+ return (-1);
+ }
+ if (imap_parse_fetch (ctx->hdrs[msgno], pc + 1) != 0)
+ return (-1);
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (seq, buf, SEQLEN) != 0)
+ ;
+
+ return 0;
+}
+
+static int imap_exec (char *buf, size_t buflen,
+ CONTEXT *ctx, const char *seq, const char *cmd)
+{
+ int count;
+
+ imap_write (ctx->fd, cmd);
+
+ do
+ {
+ if (imap_read_line_d (buf, buflen, ctx->fd) < 0)
+ return (-1);
+
+ if (buf[0] == '*' && imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ while (strncmp (buf, seq, SEQLEN) != 0);
+
+ if (((IMAP_DATA *) ctx->data)->status == IMAP_NEW_MAIL)
+ {
+ /* read new mail messages */
+
+ dprint (1, (debugfile, "imap_exec(): new mail detected\n"));
+ mutt_message ("Fetching headers for new mail...");
+
+ ((IMAP_DATA *) ctx->data)->status = 0;
+
+ count = ((IMAP_DATA *) ctx->data)->newMailCount;
+ while (count > ctx->hdrmax)
+ mx_alloc_memory (ctx);
+
+ while (ctx->msgcount < count)
+ {
+ ctx->hdrs[ctx->msgcount] = mutt_new_header ();
+ imap_read_header (ctx, ctx->msgcount);
+ mx_update_context (ctx); /* incremements ->msgcount */
+
+ /* check to make sure that new mail hasn't arrived in the middle of
+ * checking for new mail (sigh)
+ */
+ if (((IMAP_DATA *) ctx->data)->status == IMAP_NEW_MAIL)
+ {
+ count = ((IMAP_DATA *) ctx->data)->newMailCount;
+ while (count > ctx->hdrmax)
+ mx_alloc_memory (ctx);
+ ((IMAP_DATA *) ctx->data)->status = 0;
+ }
+ }
+
+ mutt_clear_error ();
+ }
+
+ if (!imap_code (buf))
+ {
+ char *pc;
+
+ dprint (1, (debugfile, "imap_exec(): command failed: %s\n", buf));
+ pc = buf + SEQLEN;
+ SKIPWS (pc);
+ pc = imap_next_word (pc);
+ mutt_error (pc);
+ sleep (1);
+ return (-1);
+ }
+
+ return 0;
+}
+
+int imap_open_mailbox (CONTEXT *ctx)
+{
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char buf[LONG_STRING];
+ char bufout[LONG_STRING];
+ char host[SHORT_STRING];
+ char mailbox[_POSIX_PATH_MAX];
+ char seq[16];
+ int count = 0;
+ int n;
+ char *pc;
+
+ pc = ctx->path;
+ if (*pc != '{')
+ return (-1);
+ pc++;
+ n = 0;
+ while (*pc && *pc != '}')
+ host[n++] = *pc++;
+ host[n] = 0;
+ if (!*pc)
+ return (-1);
+ pc++;
+ strfcpy (mailbox, pc, sizeof (mailbox));
+
+ if (!ImapUser[0])
+ strfcpy (ImapUser, Username, sizeof (ImapUser));
+ if (mutt_get_field ("IMAP Username: ", ImapUser, sizeof (ImapUser), 0) != 0 ||
+ !ImapUser[0])
+ {
+ ImapUser[0] = 0;
+ return (-1);
+ }
+
+ snprintf (buf, sizeof (buf), "Password for %s@%s: ", ImapUser, host);
+ ImapPass[0] = 0;
+ if (mutt_get_field (buf, ImapPass, sizeof (ImapPass), M_PASS) != 0 ||
+ !ImapPass[0])
+ {
+ return (-1);
+ }
+
+ memset (&sin, 0, sizeof (sin));
+ sin.sin_port = htons (IMAP_PORT);
+ sin.sin_family = AF_INET;
+ if ((he = gethostbyname (host)) == NULL)
+ {
+ mutt_perror (host);
+ return (-1);
+ }
+ memcpy (&sin.sin_addr, he->h_addr_list[0], he->h_length);
+
+ if ((ctx->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
+ {
+ mutt_perror ("socket");
+ return (-1);
+ }
+
+ mutt_message ("Connecting to %s...", host);
+
+ if (connect (ctx->fd, (struct sockaddr *) &sin, sizeof (sin)) < 0)
+ {
+ mutt_perror ("connect");
+ close (ctx->fd);
+ }
+
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ {
+ close (ctx->fd);
+ return (-1);
+ }
+
+ if (strncmp ("* OK", buf, 4) != 0)
+ {
+ imap_error ("imap_open_mailbox()", buf);
+ close (ctx->fd);
+ return (-1);
+ }
+
+ /* create IMAP-specific state struct */
+ ctx->data = safe_malloc (sizeof (IMAP_DATA));
+ memset (ctx->data, 0, sizeof (IMAP_DATA));
+
+ mutt_message ("Logging in...");
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s LOGIN %s %s\r\n", seq, ImapUser, ImapPass);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_open_mailbox()", buf);
+ return (-1);
+ }
+
+ mutt_message ("Selecting %s...", mailbox);
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (bufout, sizeof (bufout), "%s SELECT %s\r\n", seq, mailbox);
+ imap_write (ctx->fd, bufout);
+
+ do
+ {
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ break;
+
+ if (buf[0] == '*')
+ {
+ pc = buf + 2;
+
+ if (isdigit (*pc))
+ {
+ char *pn = pc;
+
+ while (*pc && isdigit (*pc))
+ pc++;
+ *pc++ = 0;
+ n = atoi (pn);
+ SKIPWS (pc);
+ if (strncasecmp ("EXISTS", pc, 6) == 0)
+ count = n;
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (seq, buf, strlen (seq)) != 0);
+
+ mutt_message ("Fetching message headers...");
+ ctx->hdrmax = count;
+ ctx->hdrs = safe_malloc (count * sizeof (HEADER *));
+ ctx->v2r = safe_malloc (count * sizeof (int));
+ for (ctx->msgcount = 0; ctx->msgcount < count; )
+ {
+ ctx->hdrs[ctx->msgcount] = mutt_new_header ();
+
+ /* `count' can get modified if new mail arrives while fetching the
+ * header for this message
+ */
+ if (imap_read_header (ctx, ctx->msgcount) != 0)
+ {
+ mx_fastclose_mailbox (ctx);
+ return (-1);
+ }
+ mx_update_context (ctx); /* increments ->msgcount */
+
+ /* in case we get new mail while fetching the headers */
+ if (((IMAP_DATA *) ctx->data)->status == IMAP_NEW_MAIL)
+ {
+ count = ((IMAP_DATA *) ctx->data)->newMailCount;
+ while (count > ctx->hdrmax)
+ mx_alloc_memory (ctx);
+ ((IMAP_DATA *) ctx->data)->status = 0;
+ }
+ }
+
+ return 0;
+}
+
+int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+ char path[_POSIX_PATH_MAX];
+ char *pc;
+ char *pn;
+ long bytes;
+ IMAP_CACHE *cache;
+
+ /* see if we already have the message in our cache */
+ cache = &((IMAP_DATA *) ctx->data)->cache[ctx->hdrs[msgno]->index % IMAP_CACHE_LEN];
+
+ if (cache->path)
+ {
+ if (cache->index == ctx->hdrs[msgno]->index)
+ {
+ /* yes, so just return a pointer to the message */
+ if (!(msg->fp = fopen (cache->path, "r")))
+ {
+ mutt_perror (cache->path);
+ return (-1);
+ }
+ return 0;
+ }
+ else
+ {
+ /* clear the previous entry */
+ unlink (cache->path);
+ free (cache->path);
+ }
+ }
+
+ mutt_message ("Fetching message...");
+
+ cache->index = ctx->hdrs[msgno]->index;
+ mutt_mktemp (path);
+ cache->path = safe_strdup (path);
+ if (!(msg->fp = safe_fopen (path, "w+")))
+ {
+ safe_free ((void **) &cache->path);
+ return (-1);
+ }
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s FETCH %d RFC822\r\n", seq,
+ ctx->hdrs[msgno]->index + 1);
+ imap_write (ctx->fd, buf);
+ do
+ {
+ if (imap_read_line (buf, sizeof (buf), ctx->fd) < 0)
+ {
+ return (-1);
+ }
+
+ if (buf[0] == '*')
+ {
+ pc = buf;
+ pc = imap_next_word (pc);
+ pc = imap_next_word (pc);
+ if (strncasecmp ("FETCH", pc, 5) == 0)
+ {
+ if (!(pc = strchr (buf, '{')))
+ {
+ imap_error ("imap_fetch_message()", buf);
+ return (-1);
+ }
+ pc++;
+ pn = pc;
+ while (isdigit (*pc))
+ pc++;
+ *pc = 0;
+ bytes = atoi (pn);
+ imap_read_bytes (msg->fp, ctx->fd, bytes);
+ }
+ else if (imap_handle_untagged (ctx, buf) != 0)
+ return (-1);
+ }
+ }
+ while (strncmp (buf, seq, SEQLEN) != 0)
+ ;
+
+ if (!imap_code (buf))
+ {
+ return (-1);
+ }
+
+ return 0;
+}
+
+int imap_close_connection (CONTEXT *ctx)
+{
+ char buf[LONG_STRING];
+ char seq[8];
+
+ /* if the server didn't shut down on us, close the connection gracefully */
+ if (((IMAP_DATA *) ctx->data)->status != IMAP_BYE)
+ {
+ mutt_message ("Closing connection to IMAP server...");
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s LOGOUT\r\n", seq);
+ imap_write (ctx->fd, buf);
+ do
+ {
+ if (imap_read_line_d (buf, sizeof (buf), ctx->fd) < 0)
+ break;
+ }
+ while (strncmp (seq, buf, SEQLEN) != 0);
+ mutt_clear_error ();
+ }
+ close (ctx->fd);
+ return 0;
+}
+
+static int make_delete_list (char *list, size_t listlen, CONTEXT *ctx)
+{
+ int first = -1, last = -1;
+ int n;
+ char tmp[LONG_STRING];
+
+ *list = 0;
+ for (n=0; n<ctx->msgcount; n++)
+ {
+ if (ctx->hdrs[n]->deleted)
+ {
+ if (first < 0)
+ {
+ first = n;
+ last = n;
+ }
+ else if (last != n - 1)
+ {
+ if (first != last)
+ snprintf (tmp, sizeof (tmp), "%d:%d", first + 1, last + 1);
+ else
+ snprintf (tmp, sizeof (tmp), "%d", first + 1);
+ if (list[0])
+ strcat (list, ",");
+ strcat (list, tmp);
+ first = last = n;
+ }
+ else
+ last = n;
+ }
+ }
+
+ if (first >= 0)
+ {
+ if (first != last)
+ snprintf (tmp, sizeof (tmp), "%d:%d", first + 1, last + 1);
+ else
+ snprintf (tmp, sizeof (tmp), "%d", first + 1);
+ if (list[0])
+ strcat (list, ",");
+ strcat (list, tmp);
+ }
+
+ return 0;
+}
+
+int imap_sync_mailbox (CONTEXT *ctx)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+ char tmp[LONG_STRING];
+ int n;
+
+ /* save status changes */
+ mutt_message ("Saving message status flags...");
+ for (n = 0; n < ctx->msgcount; n++)
+ {
+ if (!ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
+ {
+ if (ctx->hdrs[n]->read)
+ strcat (tmp, "\\Seen ");
+ if (ctx->hdrs[n]->flagged)
+ strcat (tmp, "\\Flagged ");
+ if (ctx->hdrs[n]->replied)
+ strcat (tmp, "\\Answered");
+ mutt_remove_trailing_ws (tmp);
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s STORE %d FLAGS.SILENT (%s)\r\n", seq, tmp);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_sync_mailbox()", buf);
+ return (-1);
+ }
+ }
+ }
+
+ mutt_message ("Marking messages as deleted...");
+ make_delete_list (tmp, sizeof (tmp), ctx);
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s STORE %s +FLAGS.SILENT (\\Deleted)\r\n", seq, tmp);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_sync_mailbox()", buf);
+ return (-1);
+ }
+
+ return 0;
+}
+
+void imap_fastclose_mailbox (CONTEXT *ctx)
+{
+ int i;
+
+ imap_close_connection (ctx);
+ for (i = 0; i < IMAP_CACHE_LEN; i++)
+ {
+ if (((IMAP_DATA *) ctx->data)->cache[i].path)
+ {
+ unlink (((IMAP_DATA *) ctx->data)->cache[i].path);
+ safe_free ((void **) &((IMAP_DATA *) ctx->data)->cache[i].path);
+ }
+ }
+ safe_free ((void **) &ctx->data);
+}
+
+/* commit changes and terminate connection */
+int imap_close_mailbox (CONTEXT *ctx)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+
+ /* tell the server to commit changes */
+ mutt_message ("Closing mailbox...");
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s CLOSE\r\n", seq);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_close_mailbox()", buf);
+ return (-1);
+ }
+ return 0;
+}
+
+/* use the NOOP command to poll for new mail */
+int imap_check_mailbox (CONTEXT *ctx, int *index_hint)
+{
+ char seq[8];
+ char buf[LONG_STRING];
+ int msgcount = ctx->msgcount;
+
+ imap_make_sequence (seq, sizeof (seq), ctx);
+ snprintf (buf, sizeof (buf), "%s NOOP\r\n", seq);
+ if (imap_exec (buf, sizeof (buf), ctx, seq, buf) != 0)
+ {
+ imap_error ("imap_check_mailbox()", buf);
+ return (-1);
+ }
+
+ return (msgcount != ctx->msgcount);
+}