2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
4 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
5 * Copyright (C) 1999-2005 Brendan Cully <brendan@kublai.com>
7 * This file is part of mutt-ng, see http://www.muttng.org/.
8 * It's licensed under the GNU General Public License,
9 * please see the file GPL in the top level source directory.
12 /* Support for IMAP4rev1, with the occasional nod to IMAP 4. */
19 #include "mutt_curses.h"
25 #include "imap_private.h"
26 #if defined(USE_SSL) || defined(USE_GNUTLS)
27 # include "mutt_ssl.h"
33 #include "lib/debug.h"
39 #include <sys/types.h>
42 /* imap forward declarations */
43 static int imap_get_delim (IMAP_DATA * idata);
44 static char *imap_get_flags (LIST ** hflags, char *s);
45 static int imap_check_acl (IMAP_DATA * idata);
46 static int imap_check_capabilities (IMAP_DATA * idata);
47 static void imap_set_flag (IMAP_DATA * idata, int aclbit, int flag,
48 const char *str, char *flags, size_t flsize);
50 /* imap_access: Check permissions on an IMAP mailbox.
51 * TODO: ACL checks. Right now we assume if it exists we can
53 int imap_access (const char *path, int flags)
57 char buf[LONG_STRING];
58 char mailbox[LONG_STRING];
59 char mbox[LONG_STRING];
61 if (imap_parse_path (path, &mx))
64 if (!(idata = imap_conn_find (&mx.account,
65 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW :
71 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
73 /* we may already be in the folder we're checking */
74 if (!ascii_strcmp(idata->mailbox, mx.mbox)) {
80 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
82 if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
83 snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
84 else if (mutt_bit_isset (idata->capabilities, STATUS))
85 snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
87 debug_print (2, ("STATUS not supported?\n"));
91 if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0) {
92 debug_print (1, ("Can't check STATUS of %s\n", mbox));
99 int imap_create_mailbox (IMAP_DATA * idata, char *mailbox)
101 char buf[LONG_STRING], mbox[LONG_STRING];
103 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
104 snprintf (buf, sizeof (buf), "CREATE %s", mbox);
106 if (imap_exec (idata, buf, 0) != 0)
112 int imap_rename_mailbox (IMAP_DATA * idata, IMAP_MBOX * mx,
115 char oldmbox[LONG_STRING];
116 char newmbox[LONG_STRING];
117 char buf[LONG_STRING];
119 imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox);
120 imap_munge_mbox_name (newmbox, sizeof (newmbox), newname);
122 snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox);
124 if (imap_exec (idata, buf, 0) != 0)
130 int imap_delete_mailbox (CONTEXT * ctx, IMAP_MBOX mx)
132 char buf[LONG_STRING], mbox[LONG_STRING];
135 if (!ctx || !ctx->data) {
136 if (!(idata = imap_conn_find (&mx.account,
137 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW
147 imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
148 snprintf (buf, sizeof (buf), "DELETE %s", mbox);
150 if (imap_exec ((IMAP_DATA *) idata, buf, 0) != 0)
156 /* imap_logout_all: close all open connections. Quick and dirty until we can
157 * make sure we've got all the context we need. */
158 void imap_logout_all (void)
163 conn = mutt_socket_head ();
168 if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0) {
169 mutt_message (_("Closing connection to %s..."), conn->account.host);
170 imap_logout ((IMAP_DATA *) conn->data);
172 mutt_socket_close (conn);
173 mutt_socket_free (conn);
180 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
181 * buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
182 * Apparently even literals use \r\n-terminated strings ?! */
183 int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes)
190 debug_print (2, ("reading %ld bytes\n", bytes));
192 for (pos = 0; pos < bytes; pos++) {
193 if (mutt_socket_readchar (idata->conn, &c) != 1) {
194 debug_print (1, ("error during read, %ld bytes read\n", pos));
195 idata->status = IMAP_FATAL;
201 if (r == 1 && c != '\n')
213 if (DebugLevel >= IMAP_LOG_LTRL)
214 fputc (c, DebugFile);
221 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
222 * context. Must not be done while something has a handle on any headers
223 * (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
224 void imap_expunge_mailbox (IMAP_DATA * idata)
229 for (i = 0; i < idata->ctx->msgcount; i++) {
230 h = idata->ctx->hdrs[i];
232 if (h->index == -1) {
233 debug_print (2, ("Expunging message UID %d.\n", HEADER_DATA (h)->uid));
237 /* free cached body from disk, if neccessary */
238 cacheno = HEADER_DATA (h)->uid % IMAP_CACHE_LEN;
239 if (idata->cache[cacheno].uid == HEADER_DATA (h)->uid &&
240 idata->cache[cacheno].path) {
241 unlink (idata->cache[cacheno].path);
242 FREE (&idata->cache[cacheno].path);
245 imap_free_header_data (&h->data);
249 /* We may be called on to expunge at any time. We can't rely on the caller
250 * to always know to rethread */
251 mx_update_tables (idata->ctx, 0);
252 mutt_sort_headers (idata->ctx, 1);
255 static int imap_get_delim (IMAP_DATA * idata)
260 /* assume that the delim is /. If this fails, we're in bigger trouble
261 * than getting the delim wrong */
264 imap_cmd_start (idata, "LIST \"\" \"\"");
267 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
270 s = imap_next_word (idata->cmd.buf);
271 if (ascii_strncasecmp ("LIST", s, 4) == 0) {
272 s = imap_next_word (s);
273 s = imap_next_word (s);
274 if (s && s[0] == '\"' && s[1] && s[2] == '\"')
276 else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
281 while (rc == IMAP_CMD_CONTINUE);
283 if (rc != IMAP_CMD_OK) {
284 debug_print (1, ("failed.\n"));
288 debug_print (2, ("Delimiter: %c\n", idata->delim));
293 /* get rights for folder, let imap_handle_untagged do the rest */
294 static int imap_check_acl (IMAP_DATA * idata)
296 char buf[LONG_STRING];
297 char mbox[LONG_STRING];
299 imap_munge_mbox_name (mbox, sizeof (mbox), idata->mailbox);
300 snprintf (buf, sizeof (buf), "MYRIGHTS %s", mbox);
301 if (imap_exec (idata, buf, 0) != 0) {
302 imap_error ("imap_check_acl", buf);
308 /* imap_check_capabilities: make sure we can log in to this server. */
309 static int imap_check_capabilities (IMAP_DATA * idata)
311 if (imap_exec (idata, "CAPABILITY", 0) != 0) {
312 imap_error ("imap_check_capabilities", idata->cmd.buf);
316 if (!(mutt_bit_isset (idata->capabilities, IMAP4)
317 || mutt_bit_isset (idata->capabilities, IMAP4REV1))) {
318 mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
320 mutt_sleep (2); /* pause a moment to let the user see the error */
328 /* imap_conn_find: Find an open IMAP connection matching account, or open
329 * a new one if none can be found. */
330 IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
336 if (!(conn = mutt_conn_find (NULL, account)))
339 /* if opening a new UNSELECTED connection, preserve existing creds */
340 creds = &(conn->account);
342 /* make sure this connection is not in SELECTED state, if neccessary */
343 if (flags & M_IMAP_CONN_NOSELECT)
344 while (conn->data && ((IMAP_DATA *) conn->data)->state == IMAP_SELECTED) {
345 if (!(conn = mutt_conn_find (conn, account)))
347 memcpy (&(conn->account), creds, sizeof (ACCOUNT));
350 idata = (IMAP_DATA *) conn->data;
352 /* don't open a new connection if one isn't wanted */
353 if (flags & M_IMAP_CONN_NONEW) {
355 mutt_socket_free (conn);
358 if (idata->state < IMAP_AUTHENTICATED)
363 /* The current connection is a new connection */
364 if (!(idata = imap_new_idata ())) {
365 mutt_socket_free (conn);
373 if (idata->state == IMAP_DISCONNECTED)
374 imap_open_connection (idata);
375 if (idata->state == IMAP_CONNECTED) {
376 if (!imap_authenticate (idata)) {
377 idata->state = IMAP_AUTHENTICATED;
378 if (idata->conn->ssf)
379 debug_print (2, ("Communication encrypted at %d bits\n", idata->conn->ssf));
382 mutt_account_unsetpass (&idata->conn->account);
384 FREE (&idata->capstr);
386 if (idata->state == IMAP_AUTHENTICATED)
387 imap_get_delim (idata);
392 int imap_open_connection (IMAP_DATA * idata)
394 char buf[LONG_STRING];
396 if (mutt_socket_open (idata->conn) < 0)
399 idata->state = IMAP_CONNECTED;
401 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) {
402 mutt_error (_("Unexpected response received from server: %s"),
406 mutt_socket_close (idata->conn);
407 idata->state = IMAP_DISCONNECTED;
411 if (ascii_strncasecmp ("* OK", idata->cmd.buf, 4) == 0) {
412 /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
413 if (imap_check_capabilities (idata))
415 #if defined(USE_SSL) || defined(USE_GNUTLS)
416 /* Attempt STARTTLS if available and desired. */
417 if (mutt_bit_isset (idata->capabilities, STARTTLS) && !idata->conn->ssf) {
420 if ((rc = query_quadoption (OPT_SSLSTARTTLS,
421 _("Secure connection with TLS?"))) == -1)
424 if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
428 if (mutt_ssl_starttls (idata->conn))
430 if (mutt_gnutls_starttls (idata->conn))
433 mutt_error (_("Could not negotiate TLS connection"));
438 /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
439 if (imap_exec (idata, "CAPABILITY", 0))
447 else if (ascii_strncasecmp ("* PREAUTH", idata->cmd.buf, 9) == 0) {
448 idata->state = IMAP_AUTHENTICATED;
449 if (imap_check_capabilities (idata) != 0)
451 FREE (&idata->capstr);
454 imap_error ("imap_open_connection()", buf);
461 mutt_socket_close (idata->conn);
462 idata->state = IMAP_DISCONNECTED;
464 FREE (&idata->capstr);
468 /* imap_get_flags: Make a simple list out of a FLAGS response.
469 * return stream following FLAGS response */
470 static char *imap_get_flags (LIST ** hflags, char *s)
476 /* sanity-check string */
477 if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
478 debug_print (1, ("not a FLAGS response: %s\n", s));
484 debug_print (1, ("bogus FLAGS response: %s\n", s));
488 /* create list, update caller's flags handle */
489 flags = mutt_new_list ();
492 while (*s && *s != ')') {
496 while (*s && (*s != ')') && !ISSPACE (*s))
501 mutt_add_list (flags, flag_word);
505 /* note bad flags response */
507 debug_print (1, ("Unterminated FLAGS response: %s\n", s));
508 mutt_free_list (hflags);
518 int imap_open_mailbox (CONTEXT * ctx)
522 char buf[LONG_STRING];
523 char bufout[LONG_STRING];
528 if (imap_parse_path (ctx->path, &mx)) {
529 mutt_error (_("%s is an invalid IMAP path"), ctx->path);
533 /* we require a connection which isn't currently in IMAP_SELECTED state */
534 if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
536 if (idata->state < IMAP_AUTHENTICATED)
541 /* once again the context is new */
544 /* Clean up path and replace the one in the ctx */
545 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
546 FREE (&(idata->mailbox));
547 idata->mailbox = safe_strdup (buf);
548 imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
551 ctx->path = safe_strdup (buf);
555 /* clear mailbox status */
557 memset (idata->rights, 0, (RIGHTSMAX + 7) / 8);
558 idata->newMailCount = 0;
560 mutt_message (_("Selecting %s..."), idata->mailbox);
561 imap_munge_mbox_name (buf, sizeof (buf), idata->mailbox);
562 snprintf (bufout, sizeof (bufout), "%s %s",
563 ctx->readonly ? "EXAMINE" : "SELECT", buf);
565 idata->state = IMAP_SELECTED;
567 imap_cmd_start (idata, bufout);
572 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
575 pc = idata->cmd.buf + 2;
577 /* Obtain list of available flags here, may be overridden by a
578 * PERMANENTFLAGS tag in the OK response */
579 if (ascii_strncasecmp ("FLAGS", pc, 5) == 0) {
580 /* don't override PERMANENTFLAGS */
582 debug_print (2, ("Getting mailbox FLAGS\n"));
583 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
587 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
588 else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0) {
589 debug_print (2, ("Getting mailbox PERMANENTFLAGS\n"));
590 /* safe to call on NULL */
591 mutt_free_list (&(idata->flags));
592 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
594 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
598 /* save UIDVALIDITY for the header cache */
599 else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0) {
600 debug_print (2, ("Getting mailbox UIDVALIDITY\n"));
602 pc = imap_next_word (pc);
604 sscanf (pc, "%lu", &(idata->uid_validity));
608 pc = imap_next_word (pc);
609 if (!ascii_strncasecmp ("EXISTS", pc, 6)) {
610 count = idata->newMailCount;
611 idata->newMailCount = 0;
615 while (rc == IMAP_CMD_CONTINUE);
617 if (rc == IMAP_CMD_NO) {
620 s = imap_next_word (idata->cmd.buf); /* skip seq */
621 s = imap_next_word (s); /* Skip response */
622 mutt_error ("%s", s);
627 if (rc != IMAP_CMD_OK)
630 /* check for READ-ONLY notification */
631 if (!ascii_strncasecmp
632 (imap_get_qualifier (idata->cmd.buf), "[READ-ONLY]", 11)
633 && !mutt_bit_isset (idata->capabilities, ACL)) {
634 debug_print (2, ("Mailbox is read-only.\n"));
639 /* dump the mailbox flags we've found */
640 if (DebugLevel > 2) {
642 debug_print (3, ("No folder flags found\n"));
644 LIST *t = idata->flags;
646 debug_print (3, ("Mailbox flags:\n"));
650 debug_print (3, ("[%s]\n", t->data));
657 if (mutt_bit_isset (idata->capabilities, ACL)) {
658 if (imap_check_acl (idata))
660 if (!(mutt_bit_isset (idata->rights, ACL_DELETE) ||
661 mutt_bit_isset (idata->rights, ACL_SEEN) ||
662 mutt_bit_isset (idata->rights, ACL_WRITE) ||
663 mutt_bit_isset (idata->rights, ACL_INSERT)))
666 /* assume we have all rights if ACL is unavailable */
668 mutt_bit_set (idata->rights, ACL_LOOKUP);
669 mutt_bit_set (idata->rights, ACL_READ);
670 mutt_bit_set (idata->rights, ACL_SEEN);
671 mutt_bit_set (idata->rights, ACL_WRITE);
672 mutt_bit_set (idata->rights, ACL_INSERT);
673 mutt_bit_set (idata->rights, ACL_POST);
674 mutt_bit_set (idata->rights, ACL_CREATE);
675 mutt_bit_set (idata->rights, ACL_DELETE);
679 ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
680 ctx->v2r = safe_calloc (count, sizeof (int));
682 if (count && (imap_read_headers (idata, 0, count - 1) < 0)) {
683 mutt_error _("Error opening mailbox");
689 debug_print (2, ("msgcount is %d\n", ctx->msgcount));
694 if (idata->state == IMAP_SELECTED)
695 idata->state = IMAP_AUTHENTICATED;
701 int imap_open_mailbox_append (CONTEXT * ctx)
705 char buf[LONG_STRING];
706 char mailbox[LONG_STRING];
709 if (imap_parse_path (ctx->path, &mx))
712 /* in APPEND mode, we appear to hijack an existing IMAP connection -
713 * ctx is brand new and mostly empty */
715 if (!(idata = imap_conn_find (&(mx.account), 0))) {
724 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
728 /* really we should also check for W_OK */
729 if (!imap_access (ctx->path, F_OK))
732 snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
733 if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
736 if (imap_create_mailbox (idata, mailbox) < 0)
742 /* imap_logout: Gracefully log out of server. */
743 void imap_logout (IMAP_DATA * idata)
745 /* we set status here to let imap_handle_untagged know we _expect_ to
746 * receive a bye response (so it doesn't freak out and close the conn) */
747 idata->status = IMAP_BYE;
748 imap_cmd_start (idata, "LOGOUT");
749 while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE);
750 FREE (&idata->cmd.buf);
755 int imap_close_connection (CONTEXT *ctx)
757 debug_print (1, (debugfile, "imap_close_connection(): closing connection\n"));
758 if (CTX_DATA->status != IMAP_BYE)
760 mutt_message _("Closing connection to IMAP server...");
761 imap_logout (CTX_DATA);
764 mutt_socket_close (CTX_DATA->conn);
765 CTX_DATA->state = IMAP_DISCONNECTED;
766 CTX_DATA->conn->data = NULL;
771 /* imap_set_flag: append str to flags if we currently have permission
772 * according to aclbit */
773 static void imap_set_flag (IMAP_DATA * idata, int aclbit, int flag,
774 const char *str, char *flags, size_t flsize)
776 if (mutt_bit_isset (idata->rights, aclbit))
778 safe_strcat (flags, flsize, str);
781 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of
782 * headers, given a flag enum to filter on.
783 * Params: idata: IMAP_DATA containing context containing header set
784 * buf: to write message set into
785 * buflen: length of buffer
786 * flag: enum of flag type on which to filter
787 * changed: include only changed messages in message set
788 * Returns: number of messages in message set (0 if no matches) */
789 int imap_make_msg_set (IMAP_DATA * idata, BUFFER * buf, int flag, int changed)
791 HEADER **hdrs; /* sorted local copy */
792 int count = 0; /* number of messages in message set */
793 int match = 0; /* whether current message matches flag condition */
794 unsigned int setstart = 0; /* start of current message range */
796 short oldsort; /* we clobber reverse, must restore it */
798 /* assuming 32-bit UIDs */
802 /* make copy of header pointers to sort in natural order */
803 hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER *));
804 memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER *));
806 if (Sort != SORT_ORDER) {
809 qsort ((void *) hdrs, idata->ctx->msgcount, sizeof (HEADER *),
810 mutt_get_sort_func (SORT_ORDER));
814 for (n = 0; n < idata->ctx->msgcount; n++) {
816 /* don't include pending expunged messages */
820 if (hdrs[n]->deleted)
829 if (match && (!changed || hdrs[n]->changed)) {
832 setstart = HEADER_DATA (hdrs[n])->uid;
834 snprintf (uid, sizeof (uid), "%u", HEADER_DATA (hdrs[n])->uid);
835 mutt_buffer_addstr (buf, uid);
839 snprintf (uid, sizeof (uid), ",%u", HEADER_DATA (hdrs[n])->uid);
840 mutt_buffer_addstr (buf, uid);
843 /* tie up if the last message also matches */
844 else if (n == idata->ctx->msgcount - 1) {
845 snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n])->uid);
846 mutt_buffer_addstr (buf, uid);
849 /* this message is not expunged and doesn't match. End current set. */
850 else if (setstart && hdrs[n]->active) {
851 if (HEADER_DATA (hdrs[n - 1])->uid > setstart) {
852 snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n - 1])->uid);
853 mutt_buffer_addstr (buf, uid);
864 /* Update the IMAP server to reflect the flags a single message. */
866 int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd,
869 char flags[LONG_STRING];
874 snprintf (uid, sizeof (uid), "%u", HEADER_DATA(hdr)->uid);
875 cmd->dptr = cmd->data;
876 mutt_buffer_addstr (cmd, "UID STORE ");
877 mutt_buffer_addstr (cmd, uid);
881 imap_set_flag (idata, ACL_SEEN, hdr->read, "\\Seen ",
882 flags, sizeof (flags));
883 imap_set_flag (idata, ACL_WRITE, hdr->flagged,
884 "\\Flagged ", flags, sizeof (flags));
885 imap_set_flag (idata, ACL_WRITE, hdr->replied,
886 "\\Answered ", flags, sizeof (flags));
887 imap_set_flag (idata, ACL_DELETE, hdr->deleted,
888 "\\Deleted ", flags, sizeof (flags));
890 /* now make sure we don't lose custom tags */
891 if (mutt_bit_isset (idata->rights, ACL_WRITE))
892 imap_add_keywords (flags, hdr, idata->flags, sizeof (flags));
894 str_skip_trailws (flags);
896 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
897 * explicitly revoke all system flags (if we have permission) */
900 imap_set_flag (idata, ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
901 imap_set_flag (idata, ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
902 imap_set_flag (idata, ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
903 imap_set_flag (idata, ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
905 str_skip_trailws (flags);
907 mutt_buffer_addstr (cmd, " -FLAGS.SILENT (");
909 mutt_buffer_addstr (cmd, " FLAGS.SILENT (");
911 mutt_buffer_addstr (cmd, flags);
912 mutt_buffer_addstr (cmd, ")");
914 /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
917 /* after all this it's still possible to have no flags, if you
918 * have no ACL rights */
919 if (*flags && (imap_exec (idata, cmd->data, 0) != 0) &&
920 err_continue && (*err_continue != M_YES))
922 *err_continue = imap_continue ("imap_sync_message: STORE failed",
924 if (*err_continue != M_YES)
929 idata->ctx->changed--;
934 /* update the IMAP server to reflect message changes done within mutt.
936 * ctx: the current context
937 * expunge: 0 or 1 - do expunge?
940 int imap_sync_mailbox (CONTEXT * ctx, int expunge, int *index_hint)
943 CONTEXT *appendctx = NULL;
947 int err_continue = M_NO; /* continue on error? */
950 idata = (IMAP_DATA *) ctx->data;
952 if (idata->state != IMAP_SELECTED) {
953 debug_print (2, ("no mailbox selected\n"));
957 /* This function is only called when the calling code expects the context
959 imap_allow_reopen (ctx);
961 if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
964 memset (&cmd, 0, sizeof (cmd));
966 /* if we are expunging anyway, we can do deleted messages very quickly... */
967 if (expunge && mutt_bit_isset (idata->rights, ACL_DELETE)) {
968 mutt_buffer_addstr (&cmd, "UID STORE ");
969 deleted = imap_make_msg_set (idata, &cmd, M_DELETE, 1);
971 /* if we have a message set, then let's delete */
973 mutt_message (_("Marking %d messages deleted..."), deleted);
974 mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)");
975 /* mark these messages as unchanged so second pass ignores them. Done
976 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
977 for (n = 0; n < ctx->msgcount; n++)
978 if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
979 ctx->hdrs[n]->active = 0;
980 if (imap_exec (idata, cmd.data, 0) != 0) {
981 mutt_error (_("Expunge failed"));
989 /* save status changes */
990 for (n = 0; n < ctx->msgcount; n++) {
991 if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed) {
993 mutt_message (_("Saving message status flags... [%d/%d]"), n + 1,
996 /* if the message has been rethreaded or attachments have been deleted
997 * we delete the message and reupload it.
998 * This works better if we're expunging, of course. */
999 if ((ctx->hdrs[n]->env && (ctx->hdrs[n]->env->refs_changed || ctx->hdrs[n]->env->irt_changed)) ||
1000 ctx->hdrs[n]->attach_del) {
1001 debug_print (3, ("Attachments to be deleted, falling back to _mutt_save_message\n"));
1003 appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
1005 debug_print (1, ("Error opening mailbox in append mode\n"));
1008 _mutt_save_message (ctx->hdrs[n], appendctx, 1, 0, 0);
1011 if (imap_sync_message (idata, ctx->hdrs[n], &cmd, &err_continue) < 0) {
1019 /* We must send an EXPUNGE command if we're not closing. */
1020 if (expunge && !(ctx->closing) &&
1021 mutt_bit_isset (idata->rights, ACL_DELETE)) {
1022 mutt_message _("Expunging messages from server...");
1024 /* Set expunge bit so we don't get spurious reopened messages */
1025 idata->reopen |= IMAP_EXPUNGE_EXPECTED;
1026 if (imap_exec (idata, "EXPUNGE", 0) != 0) {
1027 imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->cmd.buf);
1028 rc = imap_reconnect (ctx);
1034 if (imap_exec (idata, "CLOSE", 0))
1035 mutt_error (_("CLOSE failed"));
1036 idata->state = IMAP_AUTHENTICATED;
1044 mx_fastclose_mailbox (appendctx);
1050 /* imap_close_mailbox: clean up IMAP data in CONTEXT */
1051 void imap_close_mailbox (CONTEXT * ctx)
1056 idata = (IMAP_DATA *) ctx->data;
1057 /* Check to see if the mailbox is actually open */
1061 if (ctx == idata->ctx) {
1062 if (idata->state != IMAP_FATAL && idata->state == IMAP_SELECTED) {
1063 /* mx_close_mailbox won't sync if there are no deleted messages
1064 * and the mailbox is unchanged, so we may have to close here */
1065 if (!ctx->deleted && imap_exec (idata, "CLOSE", 0))
1066 mutt_error (_("CLOSE failed"));
1067 idata->state = IMAP_AUTHENTICATED;
1070 idata->reopen &= IMAP_REOPEN_ALLOW;
1071 FREE (&(idata->mailbox));
1072 mutt_free_list (&idata->flags);
1076 /* free IMAP part of headers */
1077 for (i = 0; i < ctx->msgcount; i++)
1078 imap_free_header_data (&(ctx->hdrs[i]->data));
1080 for (i = 0; i < IMAP_CACHE_LEN; i++) {
1081 if (idata->cache[i].path) {
1082 unlink (idata->cache[i].path);
1083 FREE (&idata->cache[i].path);
1088 /* use the NOOP command to poll for new mail
1091 * M_REOPENED mailbox has been externally modified
1092 * M_NEW_MAIL new mail has arrived!
1096 int imap_check_mailbox (CONTEXT * ctx, int *index_hint, int force)
1098 /* overload keyboard timeout to avoid many mailbox checks in a row.
1099 * Most users don't like having to wait exactly when they press a key. */
1103 idata = (IMAP_DATA *) ctx->data;
1105 if ((force || time (NULL) > idata->lastread + Timeout)
1106 && imap_exec (idata, "NOOP", 0) != 0)
1109 /* We call this even when we haven't run NOOP in case we have pending
1110 * changes to process, since we can reopen here. */
1111 imap_cmd_finish (idata);
1113 if (idata->check_status & IMAP_EXPUNGE_PENDING)
1114 result = M_REOPENED;
1115 else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1116 result = M_NEW_MAIL;
1117 else if (idata->check_status & IMAP_FLAGS_PENDING)
1120 idata->check_status = 0;
1125 /* returns count of recent messages if new = 1, else count of total messages.
1126 * (useful for at least postponed function)
1127 * Question of taste: use RECENT or UNSEEN for new?
1128 * 0+ number of messages in mailbox
1129 * -1 error while polling mailboxes
1131 int imap_mailbox_check (char *path, int new)
1135 char buf[LONG_STRING];
1136 char mbox[LONG_STRING];
1137 char mbox_unquoted[LONG_STRING];
1144 if (imap_parse_path (path, &mx))
1147 /* If imap_passive is set, don't open a connection to check for new mail */
1148 if (option (OPTIMAPPASSIVE))
1149 connflags = M_IMAP_CONN_NONEW;
1151 if (!(idata = imap_conn_find (&(mx.account), connflags))) {
1157 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1160 imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1161 strfcpy (mbox_unquoted, buf, sizeof (mbox_unquoted));
1163 /* The draft IMAP implementor's guide warns againts using the STATUS
1164 * command on a mailbox that you have selected
1167 if (mutt_strcmp (mbox_unquoted, idata->mailbox) == 0
1168 || (ascii_strcasecmp (mbox_unquoted, "INBOX") == 0
1169 && safe_strcasecmp (mbox_unquoted, idata->mailbox) == 0)) {
1170 strfcpy (buf, "NOOP", sizeof (buf));
1172 else if (mutt_bit_isset (idata->capabilities, IMAP4REV1) ||
1173 mutt_bit_isset (idata->capabilities, STATUS)) {
1174 snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
1175 new ? "RECENT" : "MESSAGES");
1178 /* Server does not support STATUS, and this is not the current mailbox.
1179 * There is no lightweight way to check recent arrivals */
1182 imap_cmd_start (idata, buf);
1185 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1188 s = imap_next_word (idata->cmd.buf);
1189 if (ascii_strncasecmp ("STATUS", s, 6) == 0) {
1190 s = imap_next_word (s);
1191 /* The mailbox name may or may not be quoted here. We could try to
1192 * munge the server response and compare with quoted (or vise versa)
1193 * but it is probably more efficient to just strncmp against both. */
1194 if (safe_strncmp (mbox_unquoted, s, mutt_strlen (mbox_unquoted)) == 0
1195 || safe_strncmp (mbox, s, mutt_strlen (mbox)) == 0) {
1196 s = imap_next_word (s);
1197 s = imap_next_word (s);
1198 if (isdigit ((unsigned char) *s)) {
1200 msgcount = atoi (s);
1201 debug_print (2, ("%d new messages in %s\n", msgcount, path));
1206 debug_print (1, ("STATUS response doesn't match requested mailbox.\n"));
1209 while (rc == IMAP_CMD_CONTINUE);
1214 /* all this listing/browsing is a mess. I don't like that name is a pointer
1215 * into idata->buf (used to be a pointer into the passed in buffer, just
1216 * as bad), nor do I like the fact that the fetch is done here. This
1217 * code can't possibly handle non-LIST untagged responses properly.
1219 int imap_parse_list_response (IMAP_DATA * idata, char **name, int *noselect,
1220 int *noinferiors, char *delim)
1228 rc = imap_cmd_step (idata);
1229 if (rc == IMAP_CMD_OK)
1231 if (rc != IMAP_CMD_CONTINUE)
1234 s = imap_next_word (idata->cmd.buf);
1235 if ((ascii_strncasecmp ("LIST", s, 4) == 0) ||
1236 (ascii_strncasecmp ("LSUB", s, 4) == 0)) {
1240 s = imap_next_word (s); /* flags */
1246 while (*ep && *ep != ')')
1249 if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1251 if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1253 /* See draft-gahrns-imap-child-mailbox-?? */
1254 if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1258 while (*s && *s != '\\' && *s != ')')
1264 s = imap_next_word (s); /* delim */
1265 /* Reset the delimiter, this can change */
1266 if (ascii_strncasecmp (s, "NIL", 3)) {
1267 if (s && s[0] == '\"' && s[1] && s[2] == '\"')
1269 else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
1273 s = imap_next_word (s); /* name */
1274 if (s && *s == '{') { /* Literal */
1275 if (imap_get_literal_count (idata->cmd.buf, &bytes) < 0)
1277 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1279 *name = idata->cmd.buf;
1288 int imap_subscribe (char *path, int subscribe)
1292 char buf[LONG_STRING];
1293 char mbox[LONG_STRING];
1296 if (mx_get_magic (path) == M_IMAP || imap_parse_path (path, &mx)) {
1297 mutt_error (_("Bad mailbox name"));
1302 if (!(idata = imap_conn_find (&(mx.account), 0)))
1307 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1309 mutt_message (_("Subscribing to %s..."), buf);
1311 mutt_message (_("Unsubscribing to %s..."), buf);
1312 imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1314 snprintf (buf, sizeof (buf), "%s %s", subscribe ? "SUBSCRIBE" :
1315 "UNSUBSCRIBE", mbox);
1317 if (imap_exec (idata, buf, 0) < 0)
1328 /* imap_complete: given a partial IMAP folder path, return a string which
1329 * adds as much to the path as is unique */
1330 int imap_complete (char *dest, size_t dlen, char *path)
1334 char list[LONG_STRING];
1335 char buf[LONG_STRING];
1336 char *list_word = NULL;
1337 int noselect, noinferiors;
1339 char completion[LONG_STRING];
1340 int clen, matchlen = 0;
1341 int completions = 0;
1345 /* verify passed in path is an IMAP path */
1346 if (imap_parse_path (path, &mx)) {
1347 debug_print (2, ("bad path %s\n", path));
1351 /* don't open a new socket just for completion */
1352 if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW)))
1356 /* reformat path for IMAP list, and append wildcard */
1357 /* don't use INBOX in place of "" */
1358 if (mx.mbox && mx.mbox[0])
1359 imap_fix_path (idata, mx.mbox, list, sizeof (list));
1363 /* fire off command */
1364 snprintf (buf, sizeof (buf), "%s \"\" \"%s%%\"",
1365 option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
1367 imap_cmd_start (idata, buf);
1369 /* and see what the results are */
1370 strfcpy (completion, NONULL (mx.mbox), sizeof (completion));
1372 if (imap_parse_list_response (idata, &list_word, &noselect, &noinferiors,
1377 /* store unquoted */
1378 imap_unmunge_mbox_name (list_word);
1380 /* if the folder isn't selectable, append delimiter to force browse
1381 * to enter it on second tab. */
1383 clen = mutt_strlen (list_word);
1384 list_word[clen++] = delim;
1385 list_word[clen] = '\0';
1387 /* copy in first word */
1389 strfcpy (completion, list_word, sizeof (completion));
1390 matchlen = mutt_strlen (completion);
1396 while (pos < matchlen && list_word[pos] &&
1397 completion[pos] == list_word[pos])
1399 completion[pos] = '\0';
1405 while (ascii_strncmp (idata->cmd.seq, idata->cmd.buf, SEQLEN));
1408 /* reformat output */
1409 imap_qualify_path (dest, dlen, &mx, completion);
1410 mutt_pretty_mailbox (dest);
1421 /* reconnect if connection was lost */
1422 int imap_reconnect (CONTEXT * ctx)
1424 IMAP_DATA *imap_data;
1429 imap_data = (IMAP_DATA *) ctx->data;
1432 if (imap_data->status == IMAP_CONNECTED)
1436 if (query_quadoption
1438 _("Connection lost. Reconnect to IMAP server?")) != M_YES)
1441 mx_open_mailbox (ctx->path, 0, ctx);