2 * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
4 * Copyright (C) 1999-2003 Brendan Cully <brendan@kublai.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 /* Support for IMAP4rev1, with the occasional nod to IMAP 4. */
24 #include "mutt_curses.h"
31 #include "imap_private.h"
32 #if defined(USE_SSL) || defined(USE_GNUTLS)
33 # include "mutt_ssl.h"
40 #include <sys/types.h>
43 /* imap forward declarations */
44 static int imap_get_delim (IMAP_DATA *idata);
45 static char* imap_get_flags (LIST** hflags, char* s);
46 static int imap_check_acl (IMAP_DATA *idata);
47 static int imap_check_capabilities (IMAP_DATA* idata);
48 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
49 const char* str, char* flags, size_t flsize);
51 /* imap_access: Check permissions on an IMAP mailbox. */
52 int imap_access (const char* path, int flags)
56 char buf[LONG_STRING];
57 char mailbox[LONG_STRING];
58 char mbox[LONG_STRING];
60 if (imap_parse_path (path, &mx))
63 if (!(idata = imap_conn_find (&mx.account,
64 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
70 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
72 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
74 /* TODO: ACL checks. Right now we assume if it exists we can mess with it. */
75 if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
76 snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
77 else if (mutt_bit_isset (idata->capabilities, STATUS))
78 snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
81 dprint (2, (debugfile, "imap_access: STATUS not supported?\n"));
85 if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0)
87 dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox));
94 int imap_create_mailbox (IMAP_DATA* idata, char* mailbox)
96 char buf[LONG_STRING], mbox[LONG_STRING];
98 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
99 snprintf (buf, sizeof (buf), "CREATE %s", mbox);
101 if (imap_exec (idata, buf, 0) != 0)
107 int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx)
109 char buf[LONG_STRING], mbox[LONG_STRING];
112 if (!ctx || !ctx->data) {
113 if (!(idata = imap_conn_find (&mx.account,
114 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
123 imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
124 snprintf (buf, sizeof (buf), "DELETE %s", mbox);
126 if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0)
132 /* imap_logout_all: close all open connections. Quick and dirty until we can
133 * make sure we've got all the context we need. */
134 void imap_logout_all (void)
139 conn = mutt_socket_head ();
145 if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0)
147 mutt_message (_("Closing connection to %s..."), conn->account.host);
148 imap_logout ((IMAP_DATA*) conn->data);
150 mutt_socket_close (conn);
151 mutt_socket_free (conn);
158 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
159 * buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
160 * Apparently even literals use \r\n-terminated strings ?! */
161 int imap_read_literal (FILE* fp, IMAP_DATA* idata, long bytes)
168 dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes));
170 for (pos = 0; pos < bytes; pos++)
172 if (mutt_socket_readchar (idata->conn, &c) != 1)
174 dprint (1, (debugfile, "imap_read_literal: error during read, %ld bytes read\n", pos));
175 idata->status = IMAP_FATAL;
181 if (r == 1 && c != '\n')
194 if (debuglevel >= IMAP_LOG_LTRL)
195 fputc (c, debugfile);
202 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
203 * context. Must not be done while something has a handle on any headers
204 * (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
205 void imap_expunge_mailbox (IMAP_DATA* idata)
210 for (i = 0; i < idata->ctx->msgcount; i++)
212 h = idata->ctx->hdrs[i];
216 dprint (2, (debugfile, "Expunging message UID %d.\n", HEADER_DATA (h)->uid));
220 /* free cached body from disk, if neccessary */
221 cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
222 if (idata->cache[cacheno].uid == HEADER_DATA(h)->uid &&
223 idata->cache[cacheno].path)
225 unlink (idata->cache[cacheno].path);
226 FREE (&idata->cache[cacheno].path);
229 imap_free_header_data (&h->data);
233 /* We may be called on to expunge at any time. We can't rely on the caller
234 * to always know to rethread */
235 mx_update_tables (idata->ctx, 0);
236 mutt_sort_headers (idata->ctx, 1);
239 static int imap_get_delim (IMAP_DATA *idata)
244 /* assume that the delim is /. If this fails, we're in bigger trouble
245 * than getting the delim wrong */
248 imap_cmd_start (idata, "LIST \"\" \"\"");
252 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
255 s = imap_next_word (idata->cmd.buf);
256 if (ascii_strncasecmp ("LIST", s, 4) == 0)
258 s = imap_next_word (s);
259 s = imap_next_word (s);
260 if (s && s[0] == '\"' && s[1] && s[2] == '\"')
262 else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2] && s[3] == '\"')
266 while (rc == IMAP_CMD_CONTINUE);
268 if (rc != IMAP_CMD_OK)
270 dprint (1, (debugfile, "imap_get_delim: failed.\n"));
274 dprint (2, (debugfile, "Delimiter: %c\n", idata->delim));
279 /* get rights for folder, let imap_handle_untagged do the rest */
280 static int imap_check_acl (IMAP_DATA *idata)
282 char buf[LONG_STRING];
283 char mbox[LONG_STRING];
285 imap_munge_mbox_name (mbox, sizeof(mbox), idata->mailbox);
286 snprintf (buf, sizeof (buf), "MYRIGHTS %s", mbox);
287 if (imap_exec (idata, buf, 0) != 0)
289 imap_error ("imap_check_acl", buf);
295 /* imap_check_capabilities: make sure we can log in to this server. */
296 static int imap_check_capabilities (IMAP_DATA* idata)
298 if (imap_exec (idata, "CAPABILITY", 0) != 0)
300 imap_error ("imap_check_capabilities", idata->cmd.buf);
304 if (!(mutt_bit_isset(idata->capabilities,IMAP4)
305 ||mutt_bit_isset(idata->capabilities,IMAP4REV1)))
307 mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
308 mutt_sleep (2); /* pause a moment to let the user see the error */
316 /* imap_conn_find: Find an open IMAP connection matching account, or open
317 * a new one if none can be found. */
318 IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags)
324 if (!(conn = mutt_conn_find (NULL, account)))
327 /* if opening a new UNSELECTED connection, preserve existing creds */
328 creds = &(conn->account);
330 /* make sure this connection is not in SELECTED state, if neccessary */
331 if (flags & M_IMAP_CONN_NOSELECT)
332 while (conn->data && ((IMAP_DATA*) conn->data)->state == IMAP_SELECTED)
334 if (!(conn = mutt_conn_find (conn, account)))
336 memcpy (&(conn->account), creds, sizeof (ACCOUNT));
339 idata = (IMAP_DATA*) conn->data;
341 /* don't open a new connection if one isn't wanted */
342 if (flags & M_IMAP_CONN_NONEW)
346 mutt_socket_free (conn);
349 if (idata->state < IMAP_AUTHENTICATED)
355 /* The current connection is a new connection */
356 if (! (idata = imap_new_idata ()))
358 mutt_socket_free (conn);
366 if (idata->state == IMAP_DISCONNECTED)
367 imap_open_connection (idata);
368 if (idata->state == IMAP_CONNECTED)
370 if (!imap_authenticate (idata))
372 idata->state = IMAP_AUTHENTICATED;
373 if (idata->conn->ssf)
374 dprint (2, (debugfile, "Communication encrypted at %d bits\n",
378 mutt_account_unsetpass (&idata->conn->account);
380 FREE (&idata->capstr);
382 if (idata->state == IMAP_AUTHENTICATED)
383 imap_get_delim (idata);
388 int imap_open_connection (IMAP_DATA* idata)
390 char buf[LONG_STRING];
392 if (mutt_socket_open (idata->conn) < 0)
395 idata->state = IMAP_CONNECTED;
397 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) {
398 mutt_error (_("Unexpected response received from server: %s"), idata->cmd.buf);
401 mutt_socket_close (idata->conn);
402 idata->state = IMAP_DISCONNECTED;
406 if (ascii_strncasecmp ("* OK", idata->cmd.buf, 4) == 0)
408 /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
409 if (imap_check_capabilities (idata))
411 #if defined(USE_SSL) || defined(USE_GNUTLS)
412 /* Attempt STARTTLS if available and desired. */
413 if (mutt_bit_isset (idata->capabilities, STARTTLS) && !idata->conn->ssf)
417 if ((rc = query_quadoption (OPT_SSLSTARTTLS,
418 _("Secure connection with TLS?"))) == -1)
421 if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
426 if (mutt_ssl_starttls (idata->conn))
428 if (mutt_gnutls_starttls (idata->conn))
431 mutt_error (_("Could not negotiate TLS connection"));
437 /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
438 if (imap_exec (idata, "CAPABILITY", 0))
446 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);
455 imap_error ("imap_open_connection()", buf);
462 mutt_socket_close (idata->conn);
463 idata->state = IMAP_DISCONNECTED;
465 FREE (&idata->capstr);
469 /* imap_get_flags: Make a simple list out of a FLAGS response.
470 * return stream following FLAGS response */
471 static char* imap_get_flags (LIST** hflags, char* s)
477 /* sanity-check string */
478 if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
480 dprint (1, (debugfile, "imap_get_flags: not a FLAGS response: %s\n",
488 dprint (1, (debugfile, "imap_get_flags: bogus FLAGS response: %s\n",
493 /* create list, update caller's flags handle */
494 flags = mutt_new_list();
497 while (*s && *s != ')')
502 while (*s && (*s != ')') && !ISSPACE (*s))
507 mutt_add_list (flags, flag_word);
511 /* note bad flags response */
514 dprint (1, (debugfile,
515 "imap_get_flags: Unterminated FLAGS response: %s\n", s));
516 mutt_free_list (hflags);
526 int imap_open_mailbox (CONTEXT* ctx)
530 char buf[LONG_STRING];
531 char bufout[LONG_STRING];
536 if (imap_parse_path (ctx->path, &mx))
538 mutt_error (_("%s is an invalid IMAP path"), ctx->path);
542 /* we require a connection which isn't currently in IMAP_SELECTED state */
543 if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
545 if (idata->state < IMAP_AUTHENTICATED)
550 /* once again the context is new */
553 /* Clean up path and replace the one in the ctx */
554 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
555 FREE(&(idata->mailbox));
556 idata->mailbox = safe_strdup (buf);
557 imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
560 ctx->path = safe_strdup (buf);
564 /* clear mailbox status */
566 memset (idata->rights, 0, (RIGHTSMAX+7)/8);
567 idata->newMailCount = 0;
569 mutt_message (_("Selecting %s..."), idata->mailbox);
570 imap_munge_mbox_name (buf, sizeof(buf), idata->mailbox);
571 snprintf (bufout, sizeof (bufout), "%s %s",
572 ctx->readonly ? "EXAMINE" : "SELECT", buf);
574 idata->state = IMAP_SELECTED;
576 imap_cmd_start (idata, bufout);
582 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
585 pc = idata->cmd.buf + 2;
587 /* Obtain list of available flags here, may be overridden by a
588 * PERMANENTFLAGS tag in the OK response */
589 if (ascii_strncasecmp ("FLAGS", pc, 5) == 0)
591 /* don't override PERMANENTFLAGS */
594 dprint (2, (debugfile, "Getting mailbox FLAGS\n"));
595 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
599 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
600 else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0)
602 dprint (2, (debugfile, "Getting mailbox PERMANENTFLAGS\n"));
603 /* safe to call on NULL */
604 mutt_free_list (&(idata->flags));
605 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
607 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
611 /* save UIDVALIDITY for the header cache */
612 else if (ascii_strncasecmp("OK [UIDVALIDITY", pc, 14) == 0)
614 dprint(2, (debugfile, "Getting mailbox UIDVALIDITY\n"));
616 pc = imap_next_word(pc);
618 sscanf(pc, "%u", &(idata->uid_validity));
623 pc = imap_next_word (pc);
624 if (!ascii_strncasecmp ("EXISTS", pc, 6))
626 count = idata->newMailCount;
627 idata->newMailCount = 0;
631 while (rc == IMAP_CMD_CONTINUE);
633 if (rc == IMAP_CMD_NO)
636 s = imap_next_word (idata->cmd.buf); /* skip seq */
637 s = imap_next_word (s); /* Skip response */
638 mutt_error ("%s", s);
643 if (rc != IMAP_CMD_OK)
646 /* check for READ-ONLY notification */
647 if (!ascii_strncasecmp (imap_get_qualifier (idata->cmd.buf), "[READ-ONLY]", 11) \
648 && !mutt_bit_isset (idata->capabilities, ACL))
650 dprint (2, (debugfile, "Mailbox is read-only.\n"));
655 /* dump the mailbox flags we've found */
659 dprint (3, (debugfile, "No folder flags found\n"));
662 LIST* t = idata->flags;
664 dprint (3, (debugfile, "Mailbox flags: "));
669 dprint (3, (debugfile, "[%s] ", t->data));
672 dprint (3, (debugfile, "\n"));
677 if (mutt_bit_isset (idata->capabilities, ACL))
679 if (imap_check_acl (idata))
681 if (!(mutt_bit_isset(idata->rights, IMAP_ACL_DELETE) ||
682 mutt_bit_isset(idata->rights, IMAP_ACL_SEEN) ||
683 mutt_bit_isset(idata->rights, IMAP_ACL_WRITE) ||
684 mutt_bit_isset(idata->rights, IMAP_ACL_INSERT)))
687 /* assume we have all rights if ACL is unavailable */
690 mutt_bit_set (idata->rights, IMAP_ACL_LOOKUP);
691 mutt_bit_set (idata->rights, IMAP_ACL_READ);
692 mutt_bit_set (idata->rights, IMAP_ACL_SEEN);
693 mutt_bit_set (idata->rights, IMAP_ACL_WRITE);
694 mutt_bit_set (idata->rights, IMAP_ACL_INSERT);
695 mutt_bit_set (idata->rights, IMAP_ACL_POST);
696 mutt_bit_set (idata->rights, IMAP_ACL_CREATE);
697 mutt_bit_set (idata->rights, IMAP_ACL_DELETE);
701 ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
702 ctx->v2r = safe_calloc (count, sizeof (int));
704 if (count && (imap_read_headers (idata, 0, count-1) < 0))
706 mutt_error _("Error opening mailbox");
711 dprint (2, (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount));
716 if (idata->state == IMAP_SELECTED)
717 idata->state = IMAP_AUTHENTICATED;
723 int imap_open_mailbox_append (CONTEXT *ctx)
727 char buf[LONG_STRING], mbox[LONG_STRING];
728 char mailbox[LONG_STRING];
732 if (imap_parse_path (ctx->path, &mx))
735 /* in APPEND mode, we appear to hijack an existing IMAP connection -
736 * ctx is brand new and mostly empty */
738 if (!(idata = imap_conn_find (&(mx.account), 0)))
745 /* check mailbox existance */
747 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
749 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
751 if (mutt_bit_isset(idata->capabilities,IMAP4REV1))
752 snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
753 else if (mutt_bit_isset(idata->capabilities,STATUS))
754 /* We have no idea what the other guy wants. UW imapd 8.3 wants this
755 * (but it does not work if another mailbox is selected) */
756 snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
759 /* STATUS not supported */
760 mutt_message _("Unable to append to IMAP mailboxes at this server");
765 r = imap_exec (idata, buf, IMAP_CMD_FAIL_OK);
768 /* command failed cause folder doesn't exist */
769 snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
770 if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
773 if (imap_create_mailbox (idata, mailbox) < 0)
777 /* Hmm, some other failure */
788 /* imap_logout: Gracefully log out of server. */
789 void imap_logout (IMAP_DATA* idata)
791 /* we set status here to let imap_handle_untagged know we _expect_ to
792 * receive a bye response (so it doesn't freak out and close the conn) */
793 idata->status = IMAP_BYE;
794 imap_cmd_start (idata, "LOGOUT");
795 while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE)
797 FREE(& idata->cmd.buf);
802 int imap_close_connection (CONTEXT *ctx)
804 dprint (1, (debugfile, "imap_close_connection(): closing connection\n"));
805 if (CTX_DATA->status != IMAP_BYE)
807 mutt_message _("Closing connection to IMAP server...");
808 imap_logout (CTX_DATA);
811 mutt_socket_close (CTX_DATA->conn);
812 CTX_DATA->state = IMAP_DISCONNECTED;
813 CTX_DATA->conn->data = NULL;
818 /* imap_set_flag: append str to flags if we currently have permission
819 * according to aclbit */
820 static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
821 const char *str, char *flags, size_t flsize)
823 if (mutt_bit_isset (idata->rights, aclbit))
825 safe_strcat (flags, flsize, str);
828 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of
829 * headers, given a flag enum to filter on.
830 * Params: idata: IMAP_DATA containing context containing header set
831 * buf: to write message set into
832 * buflen: length of buffer
833 * flag: enum of flag type on which to filter
834 * changed: include only changed messages in message set
835 * Returns: number of messages in message set (0 if no matches) */
836 int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, int changed)
838 HEADER** hdrs; /* sorted local copy */
839 int count = 0; /* number of messages in message set */
840 int match = 0; /* whether current message matches flag condition */
841 unsigned int setstart = 0; /* start of current message range */
843 short oldsort; /* we clobber reverse, must restore it */
844 /* assuming 32-bit UIDs */
848 /* make copy of header pointers to sort in natural order */
849 hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER*));
850 memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER*));
852 if (Sort != SORT_ORDER)
856 qsort ((void*) hdrs, idata->ctx->msgcount, sizeof (HEADER*),
857 mutt_get_sort_func (SORT_ORDER));
861 for (n = 0; n < idata->ctx->msgcount; n++)
864 /* don't include pending expunged messages */
869 if (hdrs[n]->deleted)
878 if (match && (!changed || hdrs[n]->changed))
883 setstart = HEADER_DATA (hdrs[n])->uid;
886 snprintf (uid, sizeof (uid), "%u", HEADER_DATA (hdrs[n])->uid);
887 mutt_buffer_addstr (buf, uid);
892 snprintf (uid, sizeof (uid), ",%u", HEADER_DATA (hdrs[n])->uid);
893 mutt_buffer_addstr (buf, uid);
896 /* tie up if the last message also matches */
897 else if (n == idata->ctx->msgcount-1)
899 snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n])->uid);
900 mutt_buffer_addstr (buf, uid);
903 /* this message is not expunged and doesn't match. End current set. */
904 else if (setstart && hdrs[n]->active)
906 if (HEADER_DATA (hdrs[n-1])->uid > setstart)
908 snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n-1])->uid);
909 mutt_buffer_addstr (buf, uid);
920 /* update the IMAP server to reflect message changes done within mutt.
922 * ctx: the current context
923 * expunge: 0 or 1 - do expunge?
926 int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint)
929 CONTEXT* appendctx = NULL;
931 char flags[LONG_STRING];
935 int err_continue = M_NO; /* continue on error? */
938 idata = (IMAP_DATA*) ctx->data;
940 if (idata->state != IMAP_SELECTED)
942 dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n"));
946 /* CLOSE purges deleted messages. If we don't want to purge them, we must
947 * tell imap_close_mailbox not to issue the CLOSE command */
953 /* This function is only called when the calling code expects the context
955 imap_allow_reopen (ctx);
957 if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
960 memset (&cmd, 0, sizeof (cmd));
962 /* if we are expunging anyway, we can do deleted messages very quickly... */
963 if (expunge && mutt_bit_isset (idata->rights, IMAP_ACL_DELETE))
965 mutt_buffer_addstr (&cmd, "UID STORE ");
966 deleted = imap_make_msg_set (idata, &cmd, M_DELETE, 1);
968 /* if we have a message set, then let's delete */
971 mutt_message (_("Marking %d messages deleted..."), deleted);
972 mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)");
973 /* mark these messages as unchanged so second pass ignores them. Done
974 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
975 for (n = 0; n < ctx->msgcount; n++)
976 if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
977 ctx->hdrs[n]->active = 0;
978 if (imap_exec (idata, cmd.data, 0) != 0)
980 mutt_error (_("Expunge failed"));
988 /* save status changes */
989 for (n = 0; n < ctx->msgcount; n++)
991 if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed)
993 ctx->hdrs[n]->changed = 0;
995 mutt_message (_("Saving message status flags... [%d/%d]"), n+1,
998 snprintf (uid, sizeof (uid), "%u", HEADER_DATA(ctx->hdrs[n])->uid);
1000 mutt_buffer_addstr (&cmd, "UID STORE ");
1001 mutt_buffer_addstr (&cmd, uid);
1003 /* if the message has been rethreaded or attachments have been deleted
1004 * we delete the message and reupload it.
1005 * This works better if we're expunging, of course. */
1006 if (ctx->hdrs[n]->refs_changed || ctx->hdrs[n]->irt_changed ||
1007 ctx->hdrs[n]->attach_del)
1009 dprint (3, (debugfile, "imap_sync_mailbox: Attachments to be deleted, falling back to _mutt_save_message\n"));
1011 appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
1014 dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n"));
1017 _mutt_save_message (ctx->hdrs[n], appendctx, 1, 0, 0);
1021 imap_set_flag (idata, IMAP_ACL_SEEN, ctx->hdrs[n]->read, "\\Seen ",
1022 flags, sizeof (flags));
1023 imap_set_flag (idata, IMAP_ACL_WRITE, ctx->hdrs[n]->flagged,
1024 "\\Flagged ", flags, sizeof (flags));
1025 imap_set_flag (idata, IMAP_ACL_WRITE, ctx->hdrs[n]->replied,
1026 "\\Answered ", flags, sizeof (flags));
1027 imap_set_flag (idata, IMAP_ACL_DELETE, ctx->hdrs[n]->deleted,
1028 "\\Deleted ", flags, sizeof (flags));
1030 /* now make sure we don't lose custom tags */
1031 if (mutt_bit_isset (idata->rights, IMAP_ACL_WRITE))
1032 imap_add_keywords (flags, ctx->hdrs[n], idata->flags, sizeof (flags));
1034 mutt_remove_trailing_ws (flags);
1036 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1037 * explicitly revoke all system flags (if we have permission) */
1040 imap_set_flag (idata, IMAP_ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
1041 imap_set_flag (idata, IMAP_ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
1042 imap_set_flag (idata, IMAP_ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
1043 imap_set_flag (idata, IMAP_ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
1045 mutt_remove_trailing_ws (flags);
1047 mutt_buffer_addstr (&cmd, " -FLAGS.SILENT (");
1050 mutt_buffer_addstr (&cmd, " FLAGS.SILENT (");
1052 mutt_buffer_addstr (&cmd, flags);
1053 mutt_buffer_addstr (&cmd, ")");
1055 /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
1056 ctx->hdrs[n]->active = 0;
1058 /* after all this it's still possible to have no flags, if you
1059 * have no ACL rights */
1060 if (*flags && (imap_exec (idata, cmd.data, 0) != 0) &&
1061 (err_continue != M_YES))
1063 err_continue = imap_continue ("imap_sync_mailbox: STORE failed",
1065 if (err_continue != M_YES)
1072 ctx->hdrs[n]->active = 1;
1077 /* We must send an EXPUNGE command if we're not closing. */
1078 if (expunge && !(ctx->closing) &&
1079 mutt_bit_isset(idata->rights, IMAP_ACL_DELETE))
1081 mutt_message _("Expunging messages from server...");
1082 /* Set expunge bit so we don't get spurious reopened messages */
1083 idata->reopen |= IMAP_EXPUNGE_EXPECTED;
1084 if (imap_exec (idata, "EXPUNGE", 0) != 0)
1086 imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->cmd.buf);
1098 mx_fastclose_mailbox (appendctx);
1104 /* imap_close_mailbox: issue close command if neccessary, reset IMAP_DATA */
1105 void imap_close_mailbox (CONTEXT* ctx)
1110 idata = (IMAP_DATA*) ctx->data;
1111 /* Check to see if the mailbox is actually open */
1115 if ((idata->status != IMAP_FATAL) &&
1116 (idata->state == IMAP_SELECTED) &&
1117 (ctx == idata->ctx))
1119 if (!(idata->noclose) && imap_exec (idata, "CLOSE", 0))
1120 mutt_error (_("CLOSE failed"));
1122 idata->reopen &= IMAP_REOPEN_ALLOW;
1123 idata->state = IMAP_AUTHENTICATED;
1124 FREE (&(idata->mailbox));
1125 mutt_free_list (&idata->flags);
1129 /* free IMAP part of headers */
1130 for (i = 0; i < ctx->msgcount; i++)
1131 imap_free_header_data (&(ctx->hdrs[i]->data));
1133 for (i = 0; i < IMAP_CACHE_LEN; i++)
1135 if (idata->cache[i].path)
1137 unlink (idata->cache[i].path);
1138 FREE (&idata->cache[i].path);
1143 /* use the NOOP command to poll for new mail
1146 * M_REOPENED mailbox has been externally modified
1147 * M_NEW_MAIL new mail has arrived!
1151 int imap_check_mailbox (CONTEXT *ctx, int *index_hint, int force)
1153 /* overload keyboard timeout to avoid many mailbox checks in a row.
1154 * Most users don't like having to wait exactly when they press a key. */
1158 idata = (IMAP_DATA*) ctx->data;
1160 if ((force || time(NULL) > idata->lastread + Timeout)
1161 && imap_exec (idata, "NOOP", 0) != 0)
1164 /* We call this even when we haven't run NOOP in case we have pending
1165 * changes to process, since we can reopen here. */
1166 imap_cmd_finish (idata);
1168 if (idata->check_status & IMAP_EXPUNGE_PENDING)
1169 result = M_REOPENED;
1170 else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1171 result = M_NEW_MAIL;
1172 else if (idata->check_status & IMAP_FLAGS_PENDING)
1175 idata->check_status = 0;
1180 /* returns count of recent messages if new = 1, else count of total messages.
1181 * (useful for at least postponed function)
1182 * Question of taste: use RECENT or UNSEEN for new?
1183 * 0+ number of messages in mailbox
1184 * -1 error while polling mailboxes
1186 int imap_mailbox_check (char* path, int new)
1190 char buf[LONG_STRING];
1191 char mbox[LONG_STRING];
1192 char mbox_unquoted[LONG_STRING];
1199 if (imap_parse_path (path, &mx))
1202 /* If imap_passive is set, don't open a connection to check for new mail */
1203 if (option (OPTIMAPPASSIVE))
1204 connflags = M_IMAP_CONN_NONEW;
1206 if (!(idata = imap_conn_find (&(mx.account), connflags)))
1213 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1216 imap_munge_mbox_name (mbox, sizeof(mbox), buf);
1217 strfcpy (mbox_unquoted, buf, sizeof (mbox_unquoted));
1219 /* The draft IMAP implementor's guide warns againts using the STATUS
1220 * command on a mailbox that you have selected
1223 if (mutt_strcmp (mbox_unquoted, idata->mailbox) == 0
1224 || (ascii_strcasecmp (mbox_unquoted, "INBOX") == 0
1225 && mutt_strcasecmp (mbox_unquoted, idata->mailbox) == 0))
1227 strfcpy (buf, "NOOP", sizeof (buf));
1229 else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) ||
1230 mutt_bit_isset(idata->capabilities,STATUS))
1232 snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
1233 new ? "RECENT" : "MESSAGES");
1236 /* Server does not support STATUS, and this is not the current mailbox.
1237 * There is no lightweight way to check recent arrivals */
1240 imap_cmd_start (idata, buf);
1244 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1247 s = imap_next_word (idata->cmd.buf);
1248 if (ascii_strncasecmp ("STATUS", s, 6) == 0)
1250 s = imap_next_word (s);
1251 /* The mailbox name may or may not be quoted here. We could try to
1252 * munge the server response and compare with quoted (or vise versa)
1253 * but it is probably more efficient to just strncmp against both. */
1254 if (mutt_strncmp (mbox_unquoted, s, mutt_strlen (mbox_unquoted)) == 0
1255 || mutt_strncmp (mbox, s, mutt_strlen (mbox)) == 0)
1257 s = imap_next_word (s);
1258 s = imap_next_word (s);
1259 if (isdigit ((unsigned char) *s))
1264 dprint (2, (debugfile, "%d new messages in %s\n", msgcount, path));
1269 dprint (1, (debugfile, "imap_mailbox_check: STATUS response doesn't match requested mailbox.\n"));
1272 while (rc == IMAP_CMD_CONTINUE);
1277 /* all this listing/browsing is a mess. I don't like that name is a pointer
1278 * into idata->buf (used to be a pointer into the passed in buffer, just
1279 * as bad), nor do I like the fact that the fetch is done here. This
1280 * code can't possibly handle non-LIST untagged responses properly.
1282 int imap_parse_list_response(IMAP_DATA* idata, char **name, int *noselect,
1283 int *noinferiors, char *delim)
1291 rc = imap_cmd_step (idata);
1292 if (rc == IMAP_CMD_OK)
1294 if (rc != IMAP_CMD_CONTINUE)
1297 s = imap_next_word (idata->cmd.buf);
1298 if ((ascii_strncasecmp ("LIST", s, 4) == 0) ||
1299 (ascii_strncasecmp ("LSUB", s, 4) == 0))
1304 s = imap_next_word (s); /* flags */
1311 while (*ep && *ep != ')') ep++;
1314 if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1316 if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1318 /* See draft-gahrns-imap-child-mailbox-?? */
1319 if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1323 while (*s && *s != '\\' && *s != ')') s++;
1328 s = imap_next_word (s); /* delim */
1329 /* Reset the delimiter, this can change */
1330 if (ascii_strncasecmp (s, "NIL", 3))
1332 if (s && s[0] == '\"' && s[1] && s[2] == '\"')
1334 else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2] && s[3] == '\"')
1337 s = imap_next_word (s); /* name */
1338 if (s && *s == '{') /* Literal */
1340 if (imap_get_literal_count(idata->cmd.buf, &bytes) < 0)
1342 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1344 *name = idata->cmd.buf;
1353 int imap_subscribe (char *path, int subscribe)
1357 char buf[LONG_STRING];
1358 char mbox[LONG_STRING];
1361 if (!mx_is_imap (path) || imap_parse_path (path, &mx))
1363 mutt_error (_("Bad mailbox name"));
1368 if (!(idata = imap_conn_find (&(mx.account), 0)))
1373 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1375 mutt_message (_("Subscribing to %s..."), buf);
1377 mutt_message (_("Unsubscribing to %s..."), buf);
1378 imap_munge_mbox_name (mbox, sizeof(mbox), buf);
1380 snprintf (buf, sizeof (buf), "%s %s", subscribe ? "SUBSCRIBE" :
1381 "UNSUBSCRIBE", mbox);
1383 if (imap_exec (idata, buf, 0) < 0)
1394 /* imap_complete: given a partial IMAP folder path, return a string which
1395 * adds as much to the path as is unique */
1396 int imap_complete(char* dest, size_t dlen, char* path) {
1399 char list[LONG_STRING];
1400 char buf[LONG_STRING];
1401 char* list_word = NULL;
1402 int noselect, noinferiors;
1404 char completion[LONG_STRING];
1405 int clen, matchlen = 0;
1406 int completions = 0;
1410 /* verify passed in path is an IMAP path */
1411 if (imap_parse_path (path, &mx))
1413 dprint(2, (debugfile, "imap_complete: bad path %s\n", path));
1417 /* don't open a new socket just for completion */
1418 if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW)))
1422 /* reformat path for IMAP list, and append wildcard */
1423 /* don't use INBOX in place of "" */
1424 if (mx.mbox && mx.mbox[0])
1425 imap_fix_path (idata, mx.mbox, list, sizeof(list));
1429 /* fire off command */
1430 snprintf (buf, sizeof(buf), "%s \"\" \"%s%%\"",
1431 option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
1433 imap_cmd_start (idata, buf);
1435 /* and see what the results are */
1436 strfcpy (completion, NONULL(mx.mbox), sizeof(completion));
1439 if (imap_parse_list_response(idata, &list_word, &noselect, &noinferiors,
1445 /* store unquoted */
1446 imap_unmunge_mbox_name (list_word);
1448 /* if the folder isn't selectable, append delimiter to force browse
1449 * to enter it on second tab. */
1452 clen = strlen(list_word);
1453 list_word[clen++] = delim;
1454 list_word[clen] = '\0';
1456 /* copy in first word */
1459 strfcpy (completion, list_word, sizeof(completion));
1460 matchlen = strlen (completion);
1466 while (pos < matchlen && list_word[pos] &&
1467 completion[pos] == list_word[pos])
1469 completion[pos] = '\0';
1475 while (ascii_strncmp(idata->cmd.seq, idata->cmd.buf, SEQLEN));
1479 /* reformat output */
1480 imap_qualify_path (dest, dlen, &mx, completion);
1481 mutt_pretty_mailbox (dest);
1492 /* reconnect if connection was lost */
1493 int imap_reconnect(CONTEXT* ctx) {
1494 IMAP_DATA* imap_data = (IMAP_DATA *)ctx->data;
1497 if (imap_data->status == IMAP_CONNECTED)
1501 if (query_quadoption(OPT_IMAPRECONNECT,_("Connection lost. Reconnect to IMAP server?")) != M_YES)
1504 mx_open_mailbox(ctx->path,0,ctx);