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. */
26 #include "imap_private.h"
27 #if defined(USE_SSL) || defined(USE_GNUTLS)
28 # include "mutt_ssl.h"
35 #include "lib/debug.h"
41 #include <sys/types.h>
44 /* imap forward declarations */
45 static int imap_get_delim (IMAP_DATA * idata);
46 static char *imap_get_flags (LIST ** hflags, char *s);
47 static int imap_check_acl (IMAP_DATA * idata);
48 static int imap_check_capabilities (IMAP_DATA * idata);
49 static void imap_set_flag (IMAP_DATA * idata, int aclbit, int flag,
50 const char *str, char *flags, size_t flsize);
52 /* imap_access: Check permissions on an IMAP mailbox.
53 * TODO: ACL checks. Right now we assume if it exists we can
55 int imap_access (const char *path, int flags)
59 char buf[LONG_STRING];
60 char mailbox[LONG_STRING];
61 char mbox[LONG_STRING];
63 if (imap_parse_path (path, &mx))
66 if (!(idata = imap_conn_find (&mx.account,
67 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW :
73 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
75 /* we may already be in the folder we're checking */
76 if (!ascii_strcmp(idata->mailbox, mx.mbox)) {
82 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
84 if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
85 snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
86 else if (mutt_bit_isset (idata->capabilities, STATUS))
87 snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
89 debug_print (2, ("STATUS not supported?\n"));
93 if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0) {
94 debug_print (1, ("Can't check STATUS of %s\n", mbox));
101 int imap_create_mailbox (IMAP_DATA * idata, char *mailbox)
103 char buf[LONG_STRING], mbox[LONG_STRING];
105 imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
106 snprintf (buf, sizeof (buf), "CREATE %s", mbox);
108 if (imap_exec (idata, buf, 0) != 0)
114 int imap_rename_mailbox (IMAP_DATA * idata, IMAP_MBOX * mx,
117 char oldmbox[LONG_STRING];
118 char newmbox[LONG_STRING];
119 char buf[LONG_STRING];
121 imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox);
122 imap_munge_mbox_name (newmbox, sizeof (newmbox), newname);
124 snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox);
126 if (imap_exec (idata, buf, 0) != 0)
132 int imap_delete_mailbox (CONTEXT * ctx, IMAP_MBOX mx)
134 char buf[LONG_STRING], mbox[LONG_STRING];
137 if (!ctx || !ctx->data) {
138 if (!(idata = imap_conn_find (&mx.account,
139 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW
149 imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
150 snprintf (buf, sizeof (buf), "DELETE %s", mbox);
152 if (imap_exec ((IMAP_DATA *) idata, buf, 0) != 0)
158 /* imap_logout_all: close all open connections. Quick and dirty until we can
159 * make sure we've got all the context we need. */
160 void imap_logout_all (void)
165 conn = mutt_socket_head ();
170 if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0) {
171 mutt_message (_("Closing connection to %s..."), conn->account.host);
172 imap_logout ((IMAP_DATA *) conn->data);
174 mutt_socket_close (conn);
175 mutt_socket_free (conn);
182 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
183 * buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
184 * Apparently even literals use \r\n-terminated strings ?! */
185 int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes, progress_t* bar)
192 debug_print (2, ("reading %ld bytes\n", bytes));
194 for (pos = 0; pos < bytes; pos++) {
195 if (mutt_socket_readchar (idata->conn, &c) != 1) {
196 debug_print (1, ("error during read, %ld bytes read\n", pos));
197 idata->status = IMAP_FATAL;
203 if (r == 1 && c != '\n')
215 if (bar && pos % 1024)
216 mutt_progress_bar (bar, pos);
219 if (DebugLevel >= IMAP_LOG_LTRL)
220 fputc (c, DebugFile);
227 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
228 * context. Must not be done while something has a handle on any headers
229 * (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
230 void imap_expunge_mailbox (IMAP_DATA * idata)
235 for (i = 0; i < idata->ctx->msgcount; i++) {
236 h = idata->ctx->hdrs[i];
238 if (h->index == -1) {
239 debug_print (2, ("Expunging message UID %d.\n", HEADER_DATA (h)->uid));
243 /* free cached body from disk, if neccessary */
244 cacheno = HEADER_DATA (h)->uid % IMAP_CACHE_LEN;
245 if (idata->cache[cacheno].uid == HEADER_DATA (h)->uid &&
246 idata->cache[cacheno].path) {
247 unlink (idata->cache[cacheno].path);
248 mem_free (&idata->cache[cacheno].path);
251 imap_free_header_data (&h->data);
255 /* We may be called on to expunge at any time. We can't rely on the caller
256 * to always know to rethread */
257 mx_update_tables (idata->ctx, 0);
258 mutt_sort_headers (idata->ctx, 1);
261 static int imap_get_delim (IMAP_DATA * idata)
266 /* assume that the delim is /. If this fails, we're in bigger trouble
267 * than getting the delim wrong */
270 imap_cmd_start (idata, "LIST \"\" \"\"");
273 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
276 s = imap_next_word (idata->cmd.buf);
277 if (ascii_strncasecmp ("LIST", s, 4) == 0) {
278 s = imap_next_word (s);
279 s = imap_next_word (s);
280 if (s && s[0] == '\"' && s[1] && s[2] == '\"')
282 else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
287 while (rc == IMAP_CMD_CONTINUE);
289 if (rc != IMAP_CMD_OK) {
290 debug_print (1, ("failed.\n"));
294 debug_print (2, ("Delimiter: %c\n", idata->delim));
299 /* get rights for folder, let imap_handle_untagged do the rest */
300 static int imap_check_acl (IMAP_DATA * idata)
302 char buf[LONG_STRING];
303 char mbox[LONG_STRING];
305 imap_munge_mbox_name (mbox, sizeof (mbox), idata->mailbox);
306 snprintf (buf, sizeof (buf), "MYRIGHTS %s", mbox);
307 if (imap_exec (idata, buf, 0) != 0) {
308 imap_error ("imap_check_acl", buf);
314 /* imap_check_capabilities: make sure we can log in to this server. */
315 static int imap_check_capabilities (IMAP_DATA * idata)
317 if (imap_exec (idata, "CAPABILITY", 0) != 0) {
318 imap_error ("imap_check_capabilities", idata->cmd.buf);
322 if (!(mutt_bit_isset (idata->capabilities, IMAP4)
323 || mutt_bit_isset (idata->capabilities, IMAP4REV1))) {
324 mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
326 mutt_sleep (2); /* pause a moment to let the user see the error */
334 /* imap_conn_find: Find an open IMAP connection matching account, or open
335 * a new one if none can be found. */
336 IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
343 if (!(conn = mutt_conn_find (NULL, account)))
346 /* if opening a new UNSELECTED connection, preserve existing creds */
347 creds = &(conn->account);
349 /* make sure this connection is not in SELECTED state, if neccessary */
350 if (flags & M_IMAP_CONN_NOSELECT)
351 while (conn->data && ((IMAP_DATA *) conn->data)->state == IMAP_SELECTED) {
352 if (!(conn = mutt_conn_find (conn, account)))
354 memcpy (&(conn->account), creds, sizeof (ACCOUNT));
357 idata = (IMAP_DATA *) conn->data;
359 /* don't open a new connection if one isn't wanted */
360 if (flags & M_IMAP_CONN_NONEW) {
362 mutt_socket_free (conn);
365 if (idata->state < IMAP_AUTHENTICATED)
370 /* The current connection is a new connection */
371 if (!(idata = imap_new_idata ())) {
372 mutt_socket_free (conn);
381 if (idata->state == IMAP_DISCONNECTED)
382 imap_open_connection (idata);
383 if (idata->state == IMAP_CONNECTED) {
384 if (!imap_authenticate (idata)) {
385 idata->state = IMAP_AUTHENTICATED;
386 if (idata->conn->ssf)
387 debug_print (2, ("Communication encrypted at %d bits\n", idata->conn->ssf));
390 mutt_account_unsetpass (&idata->conn->account);
392 mem_free (&idata->capstr);
394 if (new && idata->state == IMAP_AUTHENTICATED) {
395 imap_get_delim (idata);
396 if (option (OPTIMAPCHECKSUBSCRIBED)) {
397 mutt_message _("Checking mailbox subscriptions");
398 imap_exec (idata, "LSUB \"\" \"*\"", 0);
405 int imap_open_connection (IMAP_DATA * idata)
407 char buf[LONG_STRING];
409 if (mutt_socket_open (idata->conn) < 0)
412 idata->state = IMAP_CONNECTED;
414 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) {
415 mutt_socket_close (idata->conn);
416 idata->state = IMAP_DISCONNECTED;
420 if (ascii_strncasecmp ("* OK", idata->cmd.buf, 4) == 0) {
421 /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
422 if (imap_check_capabilities (idata))
424 #if defined(USE_SSL) || defined(USE_GNUTLS)
425 /* Attempt STARTTLS if available and desired. */
426 if (!idata->conn->ssf && (option(OPTSSLFORCETLS) ||
427 mutt_bit_isset (idata->capabilities, STARTTLS))) {
430 if (option (OPTSSLFORCETLS))
432 else if ((rc = query_quadoption (OPT_SSLSTARTTLS,
433 _("Secure connection with TLS?"))) == -1)
436 if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
439 #if defined (USE_SSL) || defined (USE_GNUTLS)
440 if (mutt_ssl_starttls (idata->conn))
443 mutt_error (_("Could not negotiate TLS connection"));
448 /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
449 if (imap_exec (idata, "CAPABILITY", 0))
456 if (option(OPTSSLFORCETLS) && ! idata->conn->ssf) {
457 mutt_error _("Encrypted connection unavailable");
463 else if (ascii_strncasecmp ("* PREAUTH", idata->cmd.buf, 9) == 0) {
464 idata->state = IMAP_AUTHENTICATED;
465 if (imap_check_capabilities (idata) != 0)
467 mem_free (&idata->capstr);
470 imap_error ("imap_open_connection()", buf);
477 mutt_socket_close (idata->conn);
478 idata->state = IMAP_DISCONNECTED;
480 mem_free (&idata->capstr);
484 /* imap_get_flags: Make a simple list out of a FLAGS response.
485 * return stream following FLAGS response */
486 static char *imap_get_flags (LIST ** hflags, char *s)
492 /* sanity-check string */
493 if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
494 debug_print (1, ("not a FLAGS response: %s\n", s));
500 debug_print (1, ("bogus FLAGS response: %s\n", s));
504 /* create list, update caller's flags handle */
505 flags = mutt_new_list ();
508 while (*s && *s != ')') {
512 while (*s && (*s != ')') && !ISSPACE (*s))
517 mutt_add_list (flags, flag_word);
521 /* note bad flags response */
523 debug_print (1, ("Unterminated FLAGS response: %s\n", s));
524 mutt_free_list (hflags);
534 int imap_open_mailbox (CONTEXT * ctx)
538 char buf[LONG_STRING];
539 char bufout[LONG_STRING];
544 if (imap_parse_path (ctx->path, &mx)) {
545 mutt_error (_("%s is an invalid IMAP path"), ctx->path);
549 /* we require a connection which isn't currently in IMAP_SELECTED state */
550 if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
552 if (idata->state < IMAP_AUTHENTICATED)
557 /* once again the context is new */
560 /* Clean up path and replace the one in the ctx */
561 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
562 mem_free (&(idata->mailbox));
563 idata->mailbox = str_dup (buf);
564 imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
566 mem_free (&(ctx->path));
567 ctx->path = str_dup (buf);
571 /* clear mailbox status */
573 memset (idata->rights, 0, (RIGHTSMAX + 7) / 8);
574 idata->newMailCount = 0;
576 mutt_message (_("Selecting %s..."), idata->mailbox);
577 imap_munge_mbox_name (buf, sizeof (buf), idata->mailbox);
578 snprintf (bufout, sizeof (bufout), "%s %s",
579 ctx->readonly ? "EXAMINE" : "SELECT", buf);
581 idata->state = IMAP_SELECTED;
583 imap_cmd_start (idata, bufout);
588 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
591 pc = idata->cmd.buf + 2;
593 /* Obtain list of available flags here, may be overridden by a
594 * PERMANENTFLAGS tag in the OK response */
595 if (ascii_strncasecmp ("FLAGS", pc, 5) == 0) {
596 /* don't override PERMANENTFLAGS */
598 debug_print (2, ("Getting mailbox FLAGS\n"));
599 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
603 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
604 else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0) {
605 debug_print (2, ("Getting mailbox PERMANENTFLAGS\n"));
606 /* safe to call on NULL */
607 mutt_free_list (&(idata->flags));
608 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
610 if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
614 /* save UIDVALIDITY for the header cache */
615 else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0) {
616 debug_print (2, ("Getting mailbox UIDVALIDITY\n"));
618 pc = imap_next_word (pc);
620 sscanf (pc, "%lu", &(idata->uid_validity));
624 pc = imap_next_word (pc);
625 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
648 (imap_get_qualifier (idata->cmd.buf), "[READ-ONLY]", 11)
649 && !mutt_bit_isset (idata->capabilities, ACL)) {
650 debug_print (2, ("Mailbox is read-only.\n"));
655 /* dump the mailbox flags we've found */
656 if (DebugLevel > 2) {
658 debug_print (3, ("No folder flags found\n"));
660 LIST *t = idata->flags;
662 debug_print (3, ("Mailbox flags:\n"));
666 debug_print (3, ("[%s]\n", t->data));
673 if (mutt_bit_isset (idata->capabilities, ACL)) {
674 if (imap_check_acl (idata))
676 if (!(mutt_bit_isset (idata->rights, ACL_DELETE) ||
677 mutt_bit_isset (idata->rights, ACL_SEEN) ||
678 mutt_bit_isset (idata->rights, ACL_WRITE) ||
679 mutt_bit_isset (idata->rights, ACL_INSERT)))
682 /* assume we have all rights if ACL is unavailable */
684 mutt_bit_set (idata->rights, ACL_LOOKUP);
685 mutt_bit_set (idata->rights, ACL_READ);
686 mutt_bit_set (idata->rights, ACL_SEEN);
687 mutt_bit_set (idata->rights, ACL_WRITE);
688 mutt_bit_set (idata->rights, ACL_INSERT);
689 mutt_bit_set (idata->rights, ACL_POST);
690 mutt_bit_set (idata->rights, ACL_CREATE);
691 mutt_bit_set (idata->rights, ACL_DELETE);
695 ctx->hdrs = mem_calloc (count, sizeof (HEADER *));
696 ctx->v2r = mem_calloc (count, sizeof (int));
698 if (count && (imap_read_headers (idata, 0, count - 1) < 0)) {
699 mutt_error _("Error opening mailbox");
705 debug_print (2, ("msgcount is %d\n", ctx->msgcount));
710 if (idata->state == IMAP_SELECTED)
711 idata->state = IMAP_AUTHENTICATED;
717 int imap_open_mailbox_append (CONTEXT * ctx)
721 char buf[LONG_STRING];
722 char mailbox[LONG_STRING];
725 if (imap_parse_path (ctx->path, &mx))
728 /* in APPEND mode, we appear to hijack an existing IMAP connection -
729 * ctx is brand new and mostly empty */
731 if (!(idata = imap_conn_find (&(mx.account), 0))) {
740 imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
744 /* really we should also check for W_OK */
745 if (!imap_access (ctx->path, F_OK))
748 snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
749 if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
752 if (imap_create_mailbox (idata, mailbox) < 0)
758 /* imap_logout: Gracefully log out of server. */
759 void imap_logout (IMAP_DATA * idata)
761 /* we set status here to let imap_handle_untagged know we _expect_ to
762 * receive a bye response (so it doesn't freak out and close the conn) */
763 idata->status = IMAP_BYE;
764 imap_cmd_start (idata, "LOGOUT");
765 while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE);
766 mem_free (&idata->cmd.buf);
771 int imap_close_connection (CONTEXT *ctx)
773 debug_print (1, (debugfile, "imap_close_connection(): closing connection\n"));
774 if (CTX_DATA->status != IMAP_BYE)
776 mutt_message _("Closing connection to IMAP server...");
777 imap_logout (CTX_DATA);
780 mutt_socket_close (CTX_DATA->conn);
781 CTX_DATA->state = IMAP_DISCONNECTED;
782 CTX_DATA->conn->data = NULL;
787 /* imap_set_flag: append str to flags if we currently have permission
788 * according to aclbit */
789 static void imap_set_flag (IMAP_DATA * idata, int aclbit, int flag,
790 const char *str, char *flags, size_t flsize)
792 if (mutt_bit_isset (idata->rights, aclbit))
794 str_cat (flags, flsize, str);
797 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of
798 * headers, given a flag enum to filter on.
799 * Params: idata: IMAP_DATA containing context containing header set
800 * buf: to write message set into
801 * buflen: length of buffer
802 * flag: enum of flag type on which to filter
803 * changed: include only changed messages in message set
804 * Returns: number of messages in message set (0 if no matches) */
805 int imap_make_msg_set (IMAP_DATA * idata, BUFFER * buf, int flag, int changed)
807 HEADER **hdrs; /* sorted local copy */
808 int count = 0; /* number of messages in message set */
809 int match = 0; /* whether current message matches flag condition */
810 unsigned int setstart = 0; /* start of current message range */
812 short oldsort; /* we clobber reverse, must restore it */
814 /* assuming 32-bit UIDs */
818 /* make copy of header pointers to sort in natural order */
819 hdrs = mem_calloc (idata->ctx->msgcount, sizeof (HEADER *));
820 memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER *));
822 if (Sort != SORT_ORDER) {
825 qsort ((void *) hdrs, idata->ctx->msgcount, sizeof (HEADER *),
826 mutt_get_sort_func (SORT_ORDER));
830 for (n = 0; n < idata->ctx->msgcount; n++) {
832 /* don't include pending expunged messages */
836 if (hdrs[n]->deleted)
845 if (match && (!changed || hdrs[n]->changed)) {
848 setstart = HEADER_DATA (hdrs[n])->uid;
850 snprintf (uid, sizeof (uid), "%u", HEADER_DATA (hdrs[n])->uid);
851 mutt_buffer_addstr (buf, uid);
855 snprintf (uid, sizeof (uid), ",%u", HEADER_DATA (hdrs[n])->uid);
856 mutt_buffer_addstr (buf, uid);
859 /* tie up if the last message also matches */
860 else if (n == idata->ctx->msgcount - 1) {
861 snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n])->uid);
862 mutt_buffer_addstr (buf, uid);
865 /* this message is not expunged and doesn't match. End current set. */
866 else if (setstart && hdrs[n]->active) {
867 if (HEADER_DATA (hdrs[n - 1])->uid > setstart) {
868 snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n - 1])->uid);
869 mutt_buffer_addstr (buf, uid);
880 /* Update the IMAP server to reflect the flags a single message. */
882 int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd,
885 char flags[LONG_STRING];
890 snprintf (uid, sizeof (uid), "%u", HEADER_DATA(hdr)->uid);
891 cmd->dptr = cmd->data;
892 mutt_buffer_addstr (cmd, "UID STORE ");
893 mutt_buffer_addstr (cmd, uid);
897 imap_set_flag (idata, ACL_SEEN, hdr->read, "\\Seen ",
898 flags, sizeof (flags));
899 imap_set_flag (idata, ACL_WRITE, hdr->flagged,
900 "\\Flagged ", flags, sizeof (flags));
901 imap_set_flag (idata, ACL_WRITE, hdr->replied,
902 "\\Answered ", flags, sizeof (flags));
903 imap_set_flag (idata, ACL_DELETE, hdr->deleted,
904 "\\Deleted ", flags, sizeof (flags));
906 /* now make sure we don't lose custom tags */
907 if (mutt_bit_isset (idata->rights, ACL_WRITE))
908 imap_add_keywords (flags, hdr, idata->flags, sizeof (flags));
910 str_skip_trailws (flags);
912 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
913 * explicitly revoke all system flags (if we have permission) */
916 imap_set_flag (idata, ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
917 imap_set_flag (idata, ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
918 imap_set_flag (idata, ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
919 imap_set_flag (idata, ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
921 str_skip_trailws (flags);
923 mutt_buffer_addstr (cmd, " -FLAGS.SILENT (");
925 mutt_buffer_addstr (cmd, " FLAGS.SILENT (");
927 mutt_buffer_addstr (cmd, flags);
928 mutt_buffer_addstr (cmd, ")");
930 /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
933 /* after all this it's still possible to have no flags, if you
934 * have no ACL rights */
935 if (*flags && (imap_exec (idata, cmd->data, 0) != 0) &&
936 err_continue && (*err_continue != M_YES))
938 *err_continue = imap_continue ("imap_sync_message: STORE failed",
940 if (*err_continue != M_YES)
945 idata->ctx->changed--;
950 /* update the IMAP server to reflect message changes done within mutt.
952 * ctx: the current context
953 * expunge: 0 or 1 - do expunge?
956 int imap_sync_mailbox (CONTEXT * ctx, int expunge, int *index_hint)
959 CONTEXT *appendctx = NULL;
963 int err_continue = M_NO; /* continue on error? */
966 idata = (IMAP_DATA *) ctx->data;
968 if (idata->state != IMAP_SELECTED) {
969 debug_print (2, ("no mailbox selected\n"));
973 /* This function is only called when the calling code expects the context
975 imap_allow_reopen (ctx);
977 if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
980 memset (&cmd, 0, sizeof (cmd));
982 /* if we are expunging anyway, we can do deleted messages very quickly... */
983 if (expunge && mutt_bit_isset (idata->rights, ACL_DELETE)) {
984 mutt_buffer_addstr (&cmd, "UID STORE ");
985 deleted = imap_make_msg_set (idata, &cmd, M_DELETE, 1);
987 /* if we have a message set, then let's delete */
989 mutt_message (_("Marking %d messages deleted..."), deleted);
990 mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)");
991 /* mark these messages as unchanged so second pass ignores them. Done
992 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
993 for (n = 0; n < ctx->msgcount; n++)
994 if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
995 ctx->hdrs[n]->active = 0;
996 if (imap_exec (idata, cmd.data, 0) != 0) {
997 mutt_error (_("Expunge failed"));
1005 /* save status changes */
1006 for (n = 0; n < ctx->msgcount; n++) {
1007 if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed) {
1009 mutt_message (_("Saving message status flags... [%d/%d]"), n + 1,
1012 /* if the message has been rethreaded or attachments have been deleted
1013 * we delete the message and reupload it.
1014 * This works better if we're expunging, of course. */
1015 if ((ctx->hdrs[n]->env && (ctx->hdrs[n]->env->refs_changed || ctx->hdrs[n]->env->irt_changed)) ||
1016 ctx->hdrs[n]->attach_del) {
1017 debug_print (3, ("Attachments to be deleted, falling back to _mutt_save_message\n"));
1019 appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
1021 debug_print (1, ("Error opening mailbox in append mode\n"));
1024 _mutt_save_message (ctx->hdrs[n], appendctx, 1, 0, 0);
1027 if (imap_sync_message (idata, ctx->hdrs[n], &cmd, &err_continue) < 0) {
1035 /* We must send an EXPUNGE command if we're not closing. */
1036 if (expunge && !(ctx->closing) &&
1037 mutt_bit_isset (idata->rights, ACL_DELETE)) {
1038 mutt_message _("Expunging messages from server...");
1040 /* Set expunge bit so we don't get spurious reopened messages */
1041 idata->reopen |= IMAP_EXPUNGE_EXPECTED;
1042 if (imap_exec (idata, "EXPUNGE", 0) != 0) {
1043 imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->cmd.buf);
1044 rc = imap_reconnect (ctx);
1049 if (expunge && ctx->closing) {
1050 if (imap_exec (idata, "CLOSE", 0))
1051 mutt_error (_("CLOSE failed"));
1052 idata->state = IMAP_AUTHENTICATED;
1058 mem_free (&cmd.data);
1060 mx_fastclose_mailbox (appendctx);
1061 mem_free (&appendctx);
1066 /* imap_close_mailbox: clean up IMAP data in CONTEXT */
1067 void imap_close_mailbox (CONTEXT * ctx)
1072 idata = (IMAP_DATA *) ctx->data;
1073 /* Check to see if the mailbox is actually open */
1077 if (ctx == idata->ctx) {
1078 if (idata->state != IMAP_FATAL && idata->state == IMAP_SELECTED) {
1079 /* mx_close_mailbox won't sync if there are no deleted messages
1080 * and the mailbox is unchanged, so we may have to close here */
1081 if (!ctx->deleted && imap_exec (idata, "CLOSE", 0))
1082 mutt_error (_("CLOSE failed"));
1083 idata->state = IMAP_AUTHENTICATED;
1086 idata->reopen &= IMAP_REOPEN_ALLOW;
1087 mem_free (&(idata->mailbox));
1088 mutt_free_list (&idata->flags);
1092 /* free IMAP part of headers */
1093 for (i = 0; i < ctx->msgcount; i++)
1094 imap_free_header_data (&(ctx->hdrs[i]->data));
1096 for (i = 0; i < IMAP_CACHE_LEN; i++) {
1097 if (idata->cache[i].path) {
1098 unlink (idata->cache[i].path);
1099 mem_free (&idata->cache[i].path);
1104 /* use the NOOP command to poll for new mail
1107 * M_REOPENED mailbox has been externally modified
1108 * M_NEW_MAIL new mail has arrived!
1112 int imap_check_mailbox (CONTEXT * ctx, int *index_hint, int force)
1114 /* overload keyboard timeout to avoid many mailbox checks in a row.
1115 * Most users don't like having to wait exactly when they press a key. */
1119 idata = (IMAP_DATA *) ctx->data;
1121 if ((force || time (NULL) >= idata->lastread + Timeout)
1122 && imap_exec (idata, "NOOP", 0) != 0)
1125 /* We call this even when we haven't run NOOP in case we have pending
1126 * changes to process, since we can reopen here. */
1127 imap_cmd_finish (idata);
1129 if (idata->check_status & IMAP_EXPUNGE_PENDING)
1130 result = M_REOPENED;
1131 else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1132 result = M_NEW_MAIL;
1133 else if (idata->check_status & IMAP_FLAGS_PENDING)
1136 idata->check_status = 0;
1141 /* returns count of recent messages if new = 1, else count of total messages.
1142 * (useful for at least postponed function)
1143 * Question of taste: use RECENT or UNSEEN for new?
1144 * 0+ number of messages in mailbox
1145 * -1 error while polling mailboxes
1147 int imap_mailbox_check (char *path, int new)
1151 char buf[LONG_STRING];
1152 char mbox[LONG_STRING];
1153 char mbox_unquoted[LONG_STRING];
1160 if (imap_parse_path (path, &mx))
1163 /* If imap_passive is set, don't open a connection to check for new mail */
1164 if (option (OPTIMAPPASSIVE))
1165 connflags = M_IMAP_CONN_NONEW;
1167 if (!(idata = imap_conn_find (&(mx.account), connflags))) {
1168 mem_free (&mx.mbox);
1173 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1174 mem_free (&mx.mbox);
1176 imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1177 strfcpy (mbox_unquoted, buf, sizeof (mbox_unquoted));
1179 /* The draft IMAP implementor's guide warns againts using the STATUS
1180 * command on a mailbox that you have selected
1183 if (str_cmp (mbox_unquoted, idata->mailbox) == 0
1184 || (ascii_strcasecmp (mbox_unquoted, "INBOX") == 0
1185 && str_casecmp (mbox_unquoted, idata->mailbox) == 0)) {
1186 strfcpy (buf, "NOOP", sizeof (buf));
1188 else if (mutt_bit_isset (idata->capabilities, IMAP4REV1) ||
1189 mutt_bit_isset (idata->capabilities, STATUS)) {
1190 snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
1191 new ? "RECENT" : "MESSAGES");
1194 /* Server does not support STATUS, and this is not the current mailbox.
1195 * There is no lightweight way to check recent arrivals */
1198 imap_cmd_start (idata, buf);
1201 if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1204 s = imap_next_word (idata->cmd.buf);
1205 if (ascii_strncasecmp ("STATUS", s, 6) == 0) {
1206 s = imap_next_word (s);
1207 /* The mailbox name may or may not be quoted here. We could try to
1208 * munge the server response and compare with quoted (or vise versa)
1209 * but it is probably more efficient to just strncmp against both. */
1210 if (str_ncmp (mbox_unquoted, s, str_len (mbox_unquoted)) == 0
1211 || str_ncmp (mbox, s, str_len (mbox)) == 0) {
1212 s = imap_next_word (s);
1213 s = imap_next_word (s);
1214 if (isdigit ((unsigned char) *s)) {
1216 msgcount = atoi (s);
1217 debug_print (2, ("%d new messages in %s\n", msgcount, path));
1222 debug_print (1, ("STATUS response doesn't match requested mailbox.\n"));
1225 while (rc == IMAP_CMD_CONTINUE);
1230 /* returns number of patterns in the search that should be done server-side
1231 * (eg are full-text) */
1232 static int do_search (const pattern_t* search, int allpats)
1235 const pattern_t* pat;
1237 for (pat = search; pat; pat = pat->next) {
1242 if (pat->stringmatch)
1246 if (pat->child && do_search (pat->child, 1))
1257 /* convert mutt pattern_t to IMAP SEARCH command containing only elements
1258 * that require full-text search (mutt already has what it needs for most
1259 * match types, and does a better job (eg server doesn't support regexps). */
1260 static int imap_compile_search (const pattern_t* pat, BUFFER* buf)
1264 if (! do_search (pat, 0))
1268 mutt_buffer_addstr (buf, "NOT ");
1273 if ((clauses = do_search (pat->child, 1)) > 0) {
1274 const pattern_t* clause = pat->child;
1276 mutt_buffer_addch (buf, '(');
1279 if (do_search (clause, 0)) {
1280 if (pat->op == M_OR && clauses > 1)
1281 mutt_buffer_addstr (buf, "OR ");
1283 if (imap_compile_search (clause, buf) < 0)
1287 mutt_buffer_addch (buf, ' ');
1289 clause = clause->next;
1293 mutt_buffer_addch (buf, ')');
1300 mutt_buffer_addstr (buf, "HEADER ");
1302 /* extract header name */
1303 if (! (delim = strchr (pat->str, ':'))) {
1304 mutt_error (_("Header search without header name: %s"), pat->str);
1308 imap_quote_string (term, sizeof (term), pat->str);
1309 mutt_buffer_addstr (buf, term);
1310 mutt_buffer_addch (buf, ' ');
1316 imap_quote_string (term, sizeof (term), delim);
1317 mutt_buffer_addstr (buf, term);
1321 mutt_buffer_addstr (buf, "BODY ");
1322 imap_quote_string (term, sizeof (term), pat->str);
1323 mutt_buffer_addstr (buf, term);
1327 mutt_buffer_addstr (buf, "TEXT ");
1328 imap_quote_string (term, sizeof (term), pat->str);
1329 mutt_buffer_addstr (buf, term);
1337 int imap_search (CONTEXT* ctx, const pattern_t* pat) {
1339 IMAP_DATA* idata = (IMAP_DATA*)ctx->data;
1342 for (i = 0; i < ctx->msgcount; i++)
1343 ctx->hdrs[i]->matched = 0;
1345 if (!do_search (pat, 1))
1348 memset (&buf, 0, sizeof (buf));
1349 mutt_buffer_addstr (&buf, "UID SEARCH ");
1350 if (imap_compile_search (pat, &buf) < 0) {
1351 mem_free (&buf.data);
1354 if (imap_exec (idata, buf.data, 0) < 0) {
1355 mem_free (&buf.data);
1359 mem_free (&buf.data);
1363 /* all this listing/browsing is a mess. I don't like that name is a pointer
1364 * into idata->buf (used to be a pointer into the passed in buffer, just
1365 * as bad), nor do I like the fact that the fetch is done here. This
1366 * code can't possibly handle non-LIST untagged responses properly.
1368 int imap_parse_list_response (IMAP_DATA * idata, char **name, int *noselect,
1369 int *noinferiors, char *delim)
1377 rc = imap_cmd_step (idata);
1378 if (rc == IMAP_CMD_OK)
1380 if (rc != IMAP_CMD_CONTINUE)
1383 s = imap_next_word (idata->cmd.buf);
1384 if ((ascii_strncasecmp ("LIST", s, 4) == 0) ||
1385 (ascii_strncasecmp ("LSUB", s, 4) == 0)) {
1389 s = imap_next_word (s); /* flags */
1395 while (*ep && *ep != ')')
1398 if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1400 if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1402 /* See draft-gahrns-imap-child-mailbox-?? */
1403 if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1407 while (*s && *s != '\\' && *s != ')')
1413 s = imap_next_word (s); /* delim */
1414 /* Reset the delimiter, this can change */
1415 if (ascii_strncasecmp (s, "NIL", 3)) {
1416 if (s && s[0] == '\"' && s[1] && s[2] == '\"')
1418 else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
1422 s = imap_next_word (s); /* name */
1423 if (s && *s == '{') { /* Literal */
1424 if (imap_get_literal_count (idata->cmd.buf, &bytes) < 0)
1426 if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1428 *name = idata->cmd.buf;
1437 int imap_subscribe (char *path, int subscribe)
1441 char buf[LONG_STRING];
1442 char mbox[LONG_STRING];
1443 char errstr[STRING];
1447 if (mx_get_magic (path) == M_IMAP || imap_parse_path (path, &mx)) {
1448 mutt_error (_("Bad mailbox name"));
1452 if (!(idata = imap_conn_find (&(mx.account), 0)))
1457 imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1459 if (option (OPTIMAPCHECKSUBSCRIBED)) {
1460 memset (&token, 0, sizeof (token));
1462 err.dsize = sizeof (errstr);
1463 snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"",
1464 subscribe ? "" : "un", path);
1465 if (mutt_parse_rc_line (mbox, &token, &err))
1466 debug_print (1, ("Error adding subscribed mailbox: %s\n", errstr));
1467 mem_free (&token.data);
1471 mutt_message (_("Subscribing to %s..."), buf);
1473 mutt_message (_("Unsubscribing to %s..."), buf);
1474 imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1476 snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox);
1478 if (imap_exec (idata, buf, 0) < 0)
1481 mem_free (&mx.mbox);
1485 mem_free (&mx.mbox);
1489 /* trim dest to the length of the longest prefix it shares with src,
1490 * returning the length of the trimmed string */
1491 static int longest_common_prefix (char *dest, const char* src,
1492 int start, size_t dlen) {
1495 while (pos < dlen && dest[pos] && dest[pos] == src[pos])
1502 /* look for IMAP URLs to complete from defined mailboxes. Could be extended
1503 * to complete over open connections and account/folder hooks too. */
1504 static int imap_complete_hosts (char *dest, size_t len) {
1511 matchlen = str_len (dest);
1512 if (list_empty (Incoming))
1514 for (i = 0; i < Incoming->length; i++) {
1515 mailbox = (BUFFY*) Incoming->data[i];
1516 if (!str_ncmp (dest, mailbox->path, matchlen)) {
1518 strfcpy (dest, mailbox->path, len);
1521 longest_common_prefix (dest, mailbox->path, matchlen, len);
1525 for (conn = mutt_socket_head (); conn->next; conn = conn->next) {
1527 char urlstr[LONG_STRING];
1529 if (conn->account.type != M_ACCT_TYPE_IMAP)
1532 mutt_account_tourl (&conn->account, &url);
1533 /* FIXME: how to handle multiple users on the same host? */
1536 url_ciss_tostring (&url, urlstr, sizeof (urlstr), 0);
1537 if (!str_ncmp (dest, urlstr, matchlen)) {
1539 strfcpy (dest, urlstr, len);
1542 longest_common_prefix (dest, urlstr, matchlen, len);
1549 /* imap_complete: given a partial IMAP folder path, return a string which
1550 * adds as much to the path as is unique */
1551 int imap_complete (char *dest, size_t dlen, char *path) {
1554 char list[LONG_STRING];
1555 char buf[LONG_STRING];
1556 char *list_word = NULL;
1557 int noselect, noinferiors;
1559 char completion[LONG_STRING];
1560 int clen, matchlen = 0;
1561 int completions = 0;
1564 if (imap_parse_path (path, &mx) || !mx.mbox) {
1565 strfcpy (dest, path, dlen);
1566 return imap_complete_hosts (dest, dlen);
1569 /* don't open a new socket just for completion. Instead complete over
1570 * known mailboxes/hooks/etc */
1571 if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW))) {
1572 mem_free (&mx.mbox);
1573 strfcpy (dest, path, dlen);
1574 return imap_complete_hosts (dest, dlen);
1578 /* reformat path for IMAP list, and append wildcard */
1579 /* don't use INBOX in place of "" */
1580 if (mx.mbox && mx.mbox[0])
1581 imap_fix_path (idata, mx.mbox, list, sizeof (list));
1585 /* fire off command */
1586 snprintf (buf, sizeof (buf), "%s \"\" \"%s%%\"",
1587 option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
1589 imap_cmd_start (idata, buf);
1591 /* and see what the results are */
1592 strfcpy (completion, NONULL (mx.mbox), sizeof (completion));
1594 if (imap_parse_list_response (idata, &list_word, &noselect, &noinferiors,
1599 /* store unquoted */
1600 imap_unmunge_mbox_name (list_word);
1602 /* if the folder isn't selectable, append delimiter to force browse
1603 * to enter it on second tab. */
1605 clen = str_len (list_word);
1606 list_word[clen++] = delim;
1607 list_word[clen] = '\0';
1609 /* copy in first word */
1611 strfcpy (completion, list_word, sizeof (completion));
1612 matchlen = str_len (completion);
1617 matchlen = longest_common_prefix (completion, list_word, 0, matchlen);
1621 while (ascii_strncmp (idata->cmd.seq, idata->cmd.buf, SEQLEN));
1624 /* reformat output */
1625 imap_qualify_path (dest, dlen, &mx, completion);
1626 mutt_pretty_mailbox (dest);
1628 mem_free (&mx.mbox);
1635 /* reconnect if connection was lost */
1636 int imap_reconnect (CONTEXT * ctx)
1638 IMAP_DATA *imap_data;
1643 imap_data = (IMAP_DATA *) ctx->data;
1646 if (imap_data->status == IMAP_CONNECTED)
1650 if (query_quadoption
1652 _("Connection lost. Reconnect to IMAP server?")) != M_YES)
1655 mx_open_mailbox (ctx->path, 0, ctx);