NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
imap.c
Go to the documentation of this file.
1 
34 #include "config.h"
35 #include <limits.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include "private.h"
42 #include "mutt/lib.h"
43 #include "config/lib.h"
44 #include "email/lib.h"
45 #include "core/lib.h"
46 #include "conn/lib.h"
47 #include "gui/lib.h"
48 #include "mutt.h"
49 #include "lib.h"
50 #include "progress/lib.h"
51 #include "question/lib.h"
52 #include "adata.h"
53 #include "auth.h"
54 #include "command_parse.h"
55 #include "commands.h"
56 #include "edata.h"
57 #include "hook.h"
58 #include "init.h"
59 #include "mdata.h"
60 #include "msn.h"
61 #include "mutt_commands.h"
62 #include "mutt_logging.h"
63 #include "mutt_socket.h"
64 #include "muttlib.h"
65 #include "mx.h"
66 #ifdef ENABLE_NLS
67 #include <libintl.h>
68 #endif
69 
70 struct Progress;
71 struct stat;
72 
73 static const struct Command imap_commands[] = {
74  // clang-format off
75  { "subscribe-to", parse_subscribe_to, 0 },
76  { "unsubscribe-from", parse_unsubscribe_from, 0 },
77  // clang-format on
78 };
79 
83 void imap_init(void)
84 {
85  COMMANDS_REGISTER(imap_commands);
86 }
87 
94 static int check_capabilities(struct ImapAccountData *adata)
95 {
96  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
97  {
98  imap_error("check_capabilities", adata->buf);
99  return -1;
100  }
101 
102  if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
103  {
104  mutt_error(
105  _("This IMAP server is ancient. NeoMutt does not work with it."));
106  return -1;
107  }
108 
109  return 0;
110 }
111 
121 static char *get_flags(struct ListHead *hflags, char *s)
122 {
123  /* sanity-check string */
124  const size_t plen = mutt_istr_startswith(s, "FLAGS");
125  if (plen == 0)
126  {
127  mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
128  return NULL;
129  }
130  s += plen;
131  SKIPWS(s);
132  if (*s != '(')
133  {
134  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
135  return NULL;
136  }
137 
138  /* update caller's flags handle */
139  while (*s && (*s != ')'))
140  {
141  s++;
142  SKIPWS(s);
143  const char *flag_word = s;
144  while (*s && (*s != ')') && !IS_SPACE(*s))
145  s++;
146  const char ctmp = *s;
147  *s = '\0';
148  if (*flag_word)
149  mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
150  *s = ctmp;
151  }
152 
153  /* note bad flags response */
154  if (*s != ')')
155  {
156  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
157  mutt_list_free(hflags);
158 
159  return NULL;
160  }
161 
162  s++;
163 
164  return s;
165 }
166 
176 static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag,
177  const char *str, char *flags, size_t flsize)
178 {
179  if (m->rights & aclflag)
180  if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
181  mutt_str_cat(flags, flsize, str);
182 }
183 
197 static int make_msg_set(struct Mailbox *m, struct Buffer *buf,
198  enum MessageType flag, bool changed, bool invert, int *pos)
199 {
200  int count = 0; /* number of messages in message set */
201  unsigned int setstart = 0; /* start of current message range */
202  int n;
203  bool started = false;
204 
205  struct ImapAccountData *adata = imap_adata_get(m);
206  if (!adata || (adata->mailbox != m))
207  return -1;
208 
209  for (n = *pos; (n < m->msg_count) && (mutt_buffer_len(buf) < IMAP_MAX_CMDLEN); n++)
210  {
211  struct Email *e = m->emails[n];
212  if (!e)
213  break;
214  bool match = false; /* whether current message matches flag condition */
215  /* don't include pending expunged messages.
216  *
217  * TODO: can we unset active in cmd_parse_expunge() and
218  * cmd_parse_vanished() instead of checking for index != INT_MAX. */
219  if (e->active && (e->index != INT_MAX))
220  {
221  switch (flag)
222  {
223  case MUTT_DELETED:
224  if (e->deleted != imap_edata_get(e)->deleted)
225  match = invert ^ e->deleted;
226  break;
227  case MUTT_FLAG:
228  if (e->flagged != imap_edata_get(e)->flagged)
229  match = invert ^ e->flagged;
230  break;
231  case MUTT_OLD:
232  if (e->old != imap_edata_get(e)->old)
233  match = invert ^ e->old;
234  break;
235  case MUTT_READ:
236  if (e->read != imap_edata_get(e)->read)
237  match = invert ^ e->read;
238  break;
239  case MUTT_REPLIED:
240  if (e->replied != imap_edata_get(e)->replied)
241  match = invert ^ e->replied;
242  break;
243  case MUTT_TAG:
244  if (e->tagged)
245  match = true;
246  break;
247  case MUTT_TRASH:
248  if (e->deleted && !e->purge)
249  match = true;
250  break;
251  default:
252  break;
253  }
254  }
255 
256  if (match && (!changed || e->changed))
257  {
258  count++;
259  if (setstart == 0)
260  {
261  setstart = imap_edata_get(e)->uid;
262  if (started)
263  {
264  mutt_buffer_add_printf(buf, ",%u", imap_edata_get(e)->uid);
265  }
266  else
267  {
268  mutt_buffer_add_printf(buf, "%u", imap_edata_get(e)->uid);
269  started = true;
270  }
271  }
272  /* tie up if the last message also matches */
273  else if (n == (m->msg_count - 1))
274  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(e)->uid);
275  }
276  /* End current set if message doesn't match or we've reached the end
277  * of the mailbox via inactive messages following the last match. */
278  else if (setstart && (e->active || (n == adata->mailbox->msg_count - 1)))
279  {
280  if (imap_edata_get(m->emails[n - 1])->uid > setstart)
281  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(m->emails[n - 1])->uid);
282  setstart = 0;
283  }
284  }
285 
286  *pos = n;
287 
288  return count;
289 }
290 
299 static bool compare_flags_for_copy(struct Email *e)
300 {
301  struct ImapEmailData *edata = e->edata;
302 
303  if (e->read != edata->read)
304  return true;
305  if (e->old != edata->old)
306  return true;
307  if (e->flagged != edata->flagged)
308  return true;
309  if (e->replied != edata->replied)
310  return true;
311 
312  return false;
313 }
314 
324 static int sync_helper(struct Mailbox *m, AclFlags right, enum MessageType flag,
325  const char *name)
326 {
327  int count = 0;
328  int rc;
329  char buf[1024];
330 
331  if (!m)
332  return -1;
333 
334  if ((m->rights & right) == 0)
335  return 0;
336 
337  if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
338  return 0;
339 
340  snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
341  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, false);
342  if (rc < 0)
343  return rc;
344  count += rc;
345 
346  buf[0] = '-';
347  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, true);
348  if (rc < 0)
349  return rc;
350  count += rc;
351 
352  return count;
353 }
354 
365 static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
366 {
367  size_t pos = start;
368 
369  while ((pos < dlen) && dest[pos] && (dest[pos] == src[pos]))
370  pos++;
371  dest[pos] = '\0';
372 
373  return pos;
374 }
375 
386 static int complete_hosts(char *buf, size_t buflen)
387 {
388  // struct Connection *conn = NULL;
389  int rc = -1;
390  size_t matchlen;
391 
392  matchlen = mutt_str_len(buf);
393  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
395  struct MailboxNode *np = NULL;
396  STAILQ_FOREACH(np, &ml, entries)
397  {
398  if (!mutt_str_startswith(mailbox_path(np->mailbox), buf))
399  continue;
400 
401  if (rc)
402  {
403  mutt_str_copy(buf, mailbox_path(np->mailbox), buflen);
404  rc = 0;
405  }
406  else
407  longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen, buflen);
408  }
410 
411 #if 0
412  TAILQ_FOREACH(conn, mutt_socket_head(), entries)
413  {
414  struct Url url = { 0 };
415  char urlstr[1024];
416 
417  if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
418  continue;
419 
420  mutt_account_tourl(&conn->account, &url);
421  /* FIXME: how to handle multiple users on the same host? */
422  url.user = NULL;
423  url.path = NULL;
424  url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
425  if (mutt_strn_equal(buf, urlstr, matchlen))
426  {
427  if (rc)
428  {
429  mutt_str_copy(buf, urlstr, buflen);
430  rc = 0;
431  }
432  else
433  longest_common_prefix(buf, urlstr, matchlen, buflen);
434  }
435  }
436 #endif
437 
438  return rc;
439 }
440 
448 int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
449 {
450  char buf[2048], mbox[1024];
451 
452  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
453  snprintf(buf, sizeof(buf), "CREATE %s", mbox);
454 
455  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
456  {
457  mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
458  return -1;
459  }
460 
461  return 0;
462 }
463 
474 int imap_access(const char *path)
475 {
476  if (imap_path_status(path, false) >= 0)
477  return 0;
478  return -1;
479 }
480 
489 int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
490 {
491  char oldmbox[1024];
492  char newmbox[1024];
493  int rc = 0;
494 
495  imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
496  imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
497 
498  struct Buffer *buf = mutt_buffer_pool_get();
499  mutt_buffer_printf(buf, "RENAME %s %s", oldmbox, newmbox);
500 
502  rc = -1;
503 
505 
506  return rc;
507 }
508 
516 int imap_delete_mailbox(struct Mailbox *m, char *path)
517 {
518  char buf[PATH_MAX + 7];
519  char mbox[PATH_MAX];
520  struct Url *url = url_parse(path);
521 
522  struct ImapAccountData *adata = imap_adata_get(m);
523  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
524  url_free(&url);
525  snprintf(buf, sizeof(buf), "DELETE %s", mbox);
527  return -1;
528 
529  return 0;
530 }
531 
536 static void imap_logout(struct ImapAccountData *adata)
537 {
538  /* we set status here to let imap_handle_untagged know we _expect_ to
539  * receive a bye response (so it doesn't freak out and close the conn) */
540  if (adata->state == IMAP_DISCONNECTED)
541  {
542  return;
543  }
544 
545  adata->status = IMAP_BYE;
546  imap_cmd_start(adata, "LOGOUT");
547  const short c_imap_poll_timeout =
548  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
549  if ((c_imap_poll_timeout <= 0) ||
550  (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
551  {
552  while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
553  ; // do nothing
554  }
555  mutt_socket_close(adata->conn);
556  adata->state = IMAP_DISCONNECTED;
557 }
558 
564 void imap_logout_all(void)
565 {
566  struct Account *np = NULL;
567  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
568  {
569  if (np->type != MUTT_IMAP)
570  continue;
571 
572  struct ImapAccountData *adata = np->adata;
573  if (!adata)
574  continue;
575 
576  struct Connection *conn = adata->conn;
577  if (!conn || (conn->fd < 0))
578  continue;
579 
580  mutt_message(_("Closing connection to %s..."), conn->account.host);
581  imap_logout(np->adata);
583  }
584 }
585 
600 int imap_read_literal(FILE *fp, struct ImapAccountData *adata,
601  unsigned long bytes, struct Progress *pbar)
602 {
603  char c;
604  bool r = false;
605  struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
606 
607  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
608  if (c_debug_level >= IMAP_LOG_LTRL)
609  mutt_buffer_alloc(&buf, bytes + 10);
610 
611  mutt_debug(LL_DEBUG2, "reading %ld bytes\n", bytes);
612 
613  for (unsigned long pos = 0; pos < bytes; pos++)
614  {
615  if (mutt_socket_readchar(adata->conn, &c) != 1)
616  {
617  mutt_debug(LL_DEBUG1, "error during read, %ld bytes read\n", pos);
618  adata->status = IMAP_FATAL;
619 
620  mutt_buffer_dealloc(&buf);
621  return -1;
622  }
623 
624  if (r && (c != '\n'))
625  fputc('\r', fp);
626 
627  if (c == '\r')
628  {
629  r = true;
630  continue;
631  }
632  else
633  r = false;
634 
635  fputc(c, fp);
636 
637  if (pbar && !(pos % 1024))
638  progress_update(pbar, pos, -1);
639  if (c_debug_level >= IMAP_LOG_LTRL)
640  mutt_buffer_addch(&buf, c);
641  }
642 
643  if (c_debug_level >= IMAP_LOG_LTRL)
644  {
645  mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
646  mutt_buffer_dealloc(&buf);
647  }
648  return 0;
649 }
650 
656 void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
657 {
658  struct ImapMboxData *mdata = imap_mdata_get(m);
659  struct ImapEmailData *edata = imap_edata_get(e);
660 
661  if (!mdata || !edata)
662  return;
663 
664  imap_msn_remove(&mdata->msn, edata->msn - 1);
665  edata->msn = 0;
666 }
667 
677 {
678  struct ImapAccountData *adata = imap_adata_get(m);
679  struct ImapMboxData *mdata = imap_mdata_get(m);
680  if (!adata || !mdata)
681  return;
682 
683  struct Email *e = NULL;
684 
685 #ifdef USE_HCACHE
686  imap_hcache_open(adata, mdata);
687 #endif
688 
689  for (int i = 0; i < m->msg_count; i++)
690  {
691  e = m->emails[i];
692  if (!e)
693  break;
694 
695  if (e->index == INT_MAX)
696  {
697  mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
698 
699  e->deleted = true;
700 
701  imap_cache_del(m, e);
702 #ifdef USE_HCACHE
703  imap_hcache_del(mdata, imap_edata_get(e)->uid);
704 #endif
705 
706  mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
707 
708  imap_edata_free((void **) &e->edata);
709  }
710  else
711  {
712  /* NeoMutt has several places where it turns off e->active as a
713  * hack. For example to avoid FLAG updates, or to exclude from
714  * imap_exec_msgset.
715  *
716  * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
717  * flag becomes set (e.g. a flag update to a modified header),
718  * this function will be called by imap_cmd_finish().
719  *
720  * The ctx_update_tables() will free and remove these "inactive" headers,
721  * despite that an EXPUNGE was not received for them.
722  * This would result in memory leaks and segfaults due to dangling
723  * pointers in the msn_index and uid_hash.
724  *
725  * So this is another hack to work around the hacks. We don't want to
726  * remove the messages, so make sure active is on. */
727  e->active = true;
728  }
729  }
730 
731 #ifdef USE_HCACHE
732  imap_hcache_close(mdata);
733 #endif
734 
737 }
738 
746 {
747  if (mutt_socket_open(adata->conn) < 0)
748  return -1;
749 
750  adata->state = IMAP_CONNECTED;
751 
752  if (imap_cmd_step(adata) != IMAP_RES_OK)
753  {
754  imap_close_connection(adata);
755  return -1;
756  }
757 
758  if (mutt_istr_startswith(adata->buf, "* OK"))
759  {
760  if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
761  {
762  goto bail;
763  }
764 #ifdef USE_SSL
765  /* Attempt STARTTLS if available and desired. */
766  const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
767  if ((adata->conn->ssf == 0) &&
768  (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
769  {
770  enum QuadOption ans;
771 
772  const enum QuadOption c_ssl_starttls =
773  cs_subset_quad(NeoMutt->sub, "ssl_starttls");
774  if (c_ssl_force_tls)
775  ans = MUTT_YES;
776  else if ((ans = query_quadoption(c_ssl_starttls,
777  _("Secure connection with TLS?"))) == MUTT_ABORT)
778  {
779  goto bail;
780  }
781  if (ans == MUTT_YES)
782  {
783  enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
784  // Clear any data after the STARTTLS acknowledgement
785  mutt_socket_empty(adata->conn);
786 
787  if (rc == IMAP_EXEC_FATAL)
788  goto bail;
789  if (rc != IMAP_EXEC_ERROR)
790  {
791  if (mutt_ssl_starttls(adata->conn))
792  {
793  mutt_error(_("Could not negotiate TLS connection"));
794  goto bail;
795  }
796  else
797  {
798  /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
799  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS))
800  goto bail;
801  }
802  }
803  }
804  }
805 
806  if (c_ssl_force_tls && (adata->conn->ssf == 0))
807  {
808  mutt_error(_("Encrypted connection unavailable"));
809  goto bail;
810  }
811 #endif
812  }
813  else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
814  {
815 #ifdef USE_SSL
816  /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
817  * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
818  * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
819  * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
820  * decide whether to abort. Note that if using $tunnel and
821  * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
822  const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
823  if ((adata->conn->ssf == 0) && c_ssl_force_tls)
824  {
825  mutt_error(_("Encrypted connection unavailable"));
826  goto bail;
827  }
828 #endif
829 
830  adata->state = IMAP_AUTHENTICATED;
831  if (check_capabilities(adata) != 0)
832  goto bail;
833  FREE(&adata->capstr);
834  }
835  else
836  {
837  imap_error("imap_open_connection()", adata->buf);
838  goto bail;
839  }
840 
841  return 0;
842 
843 bail:
844  imap_close_connection(adata);
845  FREE(&adata->capstr);
846  return -1;
847 }
848 
854 {
855  if (adata->state != IMAP_DISCONNECTED)
856  {
857  mutt_socket_close(adata->conn);
858  adata->state = IMAP_DISCONNECTED;
859  }
860  adata->seqno = 0;
861  adata->nextcmd = 0;
862  adata->lastcmd = 0;
863  adata->status = 0;
864  memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
865 }
866 
878 bool imap_has_flag(struct ListHead *flag_list, const char *flag)
879 {
880  if (STAILQ_EMPTY(flag_list))
881  return false;
882 
883  const size_t flaglen = mutt_str_len(flag);
884  struct ListNode *np = NULL;
885  STAILQ_FOREACH(np, flag_list, entries)
886  {
887  const size_t nplen = strlen(np->data);
888  if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
889  mutt_istrn_equal(np->data, flag, nplen))
890  {
891  return true;
892  }
893 
894  if (mutt_str_equal(np->data, "\\*"))
895  return true;
896  }
897 
898  return false;
899 }
900 
904 static int compare_uid(const void *a, const void *b)
905 {
906  const struct Email *ea = *(struct Email const *const *) a;
907  const struct Email *eb = *(struct Email const *const *) b;
908  return imap_edata_get((struct Email *) ea)->uid -
909  imap_edata_get((struct Email *) eb)->uid;
910 }
911 
927 int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post,
928  enum MessageType flag, bool changed, bool invert)
929 {
930  struct ImapAccountData *adata = imap_adata_get(m);
931  if (!adata || (adata->mailbox != m))
932  return -1;
933 
934  struct Email **emails = NULL;
935  int pos;
936  int rc;
937  int count = 0;
938 
939  struct Buffer cmd = mutt_buffer_make(0);
940 
941  /* We make a copy of the headers just in case resorting doesn't give
942  exactly the original order (duplicate messages?), because other parts of
943  the ctx are tied to the header order. This may be overkill. */
944  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
945  if (c_sort != SORT_ORDER)
946  {
947  emails = m->emails;
948  // We overcommit here, just in case new mail arrives whilst we're sync-ing
949  m->emails = mutt_mem_malloc(m->email_max * sizeof(struct Email *));
950  memcpy(m->emails, emails, m->email_max * sizeof(struct Email *));
951 
953  qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
954  }
955 
956  pos = 0;
957 
958  do
959  {
960  mutt_buffer_reset(&cmd);
961  mutt_buffer_add_printf(&cmd, "%s ", pre);
962  rc = make_msg_set(m, &cmd, flag, changed, invert, &pos);
963  if (rc > 0)
964  {
965  mutt_buffer_add_printf(&cmd, " %s", post);
966  if (imap_exec(adata, cmd.data, IMAP_CMD_QUEUE) != IMAP_EXEC_SUCCESS)
967  {
968  rc = -1;
969  goto out;
970  }
971  count += rc;
972  }
973  } while (rc > 0);
974 
975  rc = count;
976 
977 out:
978  mutt_buffer_dealloc(&cmd);
979  if (c_sort != SORT_ORDER)
980  {
981  cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
982  FREE(&m->emails);
983  m->emails = emails;
984  }
985 
986  return rc;
987 }
988 
1004 int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e,
1005  struct Buffer *cmd, enum QuadOption *err_continue)
1006 {
1007  struct ImapAccountData *adata = imap_adata_get(m);
1008  if (!adata || (adata->mailbox != m))
1009  return -1;
1010 
1011  char flags[1024];
1012  char *tags = NULL;
1013  char uid[11];
1014 
1015  if (!compare_flags_for_copy(e))
1016  {
1017  if (e->deleted == imap_edata_get(e)->deleted)
1018  e->changed = false;
1019  return 0;
1020  }
1021 
1022  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
1023  mutt_buffer_reset(cmd);
1024  mutt_buffer_addstr(cmd, "UID STORE ");
1025  mutt_buffer_addstr(cmd, uid);
1026 
1027  flags[0] = '\0';
1028 
1029  set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
1030  set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
1031  set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
1032  set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
1033  set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
1034  sizeof(flags));
1035 
1036  if (m->rights & MUTT_ACL_WRITE)
1037  {
1038  /* restore system flags */
1039  if (imap_edata_get(e)->flags_system)
1040  mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
1041  /* set custom flags */
1042  tags = driver_tags_get_with_hidden(&e->tags);
1043  if (tags)
1044  {
1045  mutt_str_cat(flags, sizeof(flags), tags);
1046  FREE(&tags);
1047  }
1048  }
1049 
1051 
1052  /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1053  * explicitly revoke all system flags (if we have permission) */
1054  if (*flags == '\0')
1055  {
1056  set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags, sizeof(flags));
1057  set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags, sizeof(flags));
1058  set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags, sizeof(flags));
1059  set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags, sizeof(flags));
1060  set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
1061  flags, sizeof(flags));
1062 
1063  /* erase custom flags */
1064  if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
1065  mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
1066 
1068 
1069  mutt_buffer_addstr(cmd, " -FLAGS.SILENT (");
1070  }
1071  else
1072  mutt_buffer_addstr(cmd, " FLAGS.SILENT (");
1073 
1074  mutt_buffer_addstr(cmd, flags);
1075  mutt_buffer_addstr(cmd, ")");
1076 
1077  /* after all this it's still possible to have no flags, if you
1078  * have no ACL rights */
1079  if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
1080  err_continue && (*err_continue != MUTT_YES))
1081  {
1082  *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1083  if (*err_continue != MUTT_YES)
1084  return -1;
1085  }
1086 
1087  /* server have now the updated flags */
1088  FREE(&imap_edata_get(e)->flags_remote);
1090 
1091  if (e->deleted == imap_edata_get(e)->deleted)
1092  e->changed = false;
1093 
1094  return 0;
1095 }
1096 
1103 enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
1104 {
1105  if (!m || !m->account)
1106  return MX_STATUS_ERROR;
1107 
1108  struct ImapAccountData *adata = imap_adata_get(m);
1109  struct ImapMboxData *mdata = imap_mdata_get(m);
1110 
1111  /* overload keyboard timeout to avoid many mailbox checks in a row.
1112  * Most users don't like having to wait exactly when they press a key. */
1113  int rc = 0;
1114 
1115  /* try IDLE first, unless force is set */
1116  const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1117  const short c_imap_keepalive =
1118  cs_subset_number(NeoMutt->sub, "imap_keepalive");
1119  if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1120  ((adata->state != IMAP_IDLE) || (mutt_date_epoch() >= adata->lastread + c_imap_keepalive)))
1121  {
1122  if (imap_cmd_idle(adata) < 0)
1123  return MX_STATUS_ERROR;
1124  }
1125  if (adata->state == IMAP_IDLE)
1126  {
1127  while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1128  {
1129  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1130  {
1131  mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1132  return MX_STATUS_ERROR;
1133  }
1134  }
1135  if (rc < 0)
1136  {
1137  mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1138  adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1139  }
1140  }
1141 
1142  const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1143  if ((force || ((adata->state != IMAP_IDLE) &&
1144  (mutt_date_epoch() >= adata->lastread + c_timeout))) &&
1145  (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1146  {
1147  return MX_STATUS_ERROR;
1148  }
1149 
1150  /* We call this even when we haven't run NOOP in case we have pending
1151  * changes to process, since we can reopen here. */
1152  imap_cmd_finish(adata);
1153 
1154  enum MxStatus check = MX_STATUS_OK;
1155  if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1156  check = MX_STATUS_REOPENED;
1157  else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1158  check = MX_STATUS_NEW_MAIL;
1159  else if (mdata->check_status & IMAP_FLAGS_PENDING)
1160  check = MX_STATUS_FLAGS;
1161  else if (rc < 0)
1162  check = MX_STATUS_ERROR;
1163 
1165 
1166  return check;
1167 }
1168 
1176 static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
1177 {
1178  char *uidvalidity_flag = NULL;
1179  char cmd[2048];
1180 
1181  if (!adata || !mdata)
1182  return -1;
1183 
1184  /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1185  * IDLEd elsewhere.
1186  * adata->mailbox may be NULL for connections other than the current
1187  * mailbox's. */
1188  if (adata->mailbox && (adata->mailbox->mdata == mdata))
1189  {
1190  adata->mailbox->has_new = false;
1191  return mdata->messages;
1192  }
1193 
1194  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1195  uidvalidity_flag = "UIDVALIDITY";
1196  else if (adata->capabilities & IMAP_CAP_STATUS)
1197  uidvalidity_flag = "UID-VALIDITY";
1198  else
1199  {
1200  mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1201  return -1;
1202  }
1203 
1204  snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1205  mdata->munge_name, uidvalidity_flag);
1206 
1207  int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_NO_FLAGS | IMAP_CMD_POLL);
1208  if (rc < 0)
1209  {
1210  mutt_debug(LL_DEBUG1, "Error queueing command\n");
1211  return rc;
1212  }
1213  return mdata->messages;
1214 }
1215 
1219 static enum MxStatus imap_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1220 {
1221  const int new_msgs = imap_mailbox_status(m, true);
1222  if (new_msgs == -1)
1223  return MX_STATUS_ERROR;
1224  if (new_msgs == 0)
1225  return MX_STATUS_OK;
1226  return MX_STATUS_NEW_MAIL;
1227 }
1228 
1235 int imap_path_status(const char *path, bool queue)
1236 {
1237  struct Mailbox *m = mx_mbox_find2(path);
1238 
1239  const bool is_temp = !m;
1240  if (is_temp)
1241  {
1242  m = mx_path_resolve(path);
1243  if (!mx_mbox_ac_link(m))
1244  {
1245  mailbox_free(&m);
1246  return 0;
1247  }
1248  }
1249 
1250  int rc = imap_mailbox_status(m, queue);
1251 
1252  if (is_temp)
1253  {
1254  mx_ac_remove(m);
1255  mailbox_free(&m);
1256  }
1257 
1258  return rc;
1259 }
1260 
1270 int imap_mailbox_status(struct Mailbox *m, bool queue)
1271 {
1272  struct ImapAccountData *adata = imap_adata_get(m);
1273  struct ImapMboxData *mdata = imap_mdata_get(m);
1274  if (!adata || !mdata)
1275  return -1;
1276  return imap_status(adata, mdata, queue);
1277 }
1278 
1286 int imap_subscribe(char *path, bool subscribe)
1287 {
1288  struct ImapAccountData *adata = NULL;
1289  struct ImapMboxData *mdata = NULL;
1290  char buf[2048];
1291  struct Buffer err;
1292 
1293  if (imap_adata_find(path, &adata, &mdata) < 0)
1294  return -1;
1295 
1296  if (subscribe)
1297  mutt_message(_("Subscribing to %s..."), mdata->name);
1298  else
1299  mutt_message(_("Unsubscribing from %s..."), mdata->name);
1300 
1301  snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1302 
1303  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1304  {
1305  imap_mdata_free((void *) &mdata);
1306  return -1;
1307  }
1308 
1309  const bool c_imap_check_subscribed =
1310  cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1311  if (c_imap_check_subscribed)
1312  {
1313  char mbox[1024];
1314  mutt_buffer_init(&err);
1315  err.dsize = 256;
1316  err.data = mutt_mem_malloc(err.dsize);
1317  size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1318  imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1319  if (mutt_parse_rc_line(mbox, &err))
1320  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
1321  FREE(&err.data);
1322  }
1323 
1324  if (subscribe)
1325  mutt_message(_("Subscribed to %s"), mdata->name);
1326  else
1327  mutt_message(_("Unsubscribed from %s"), mdata->name);
1328  imap_mdata_free((void *) &mdata);
1329  return 0;
1330 }
1331 
1343 int imap_complete(char *buf, size_t buflen, const char *path)
1344 {
1345  struct ImapAccountData *adata = NULL;
1346  struct ImapMboxData *mdata = NULL;
1347  char tmp[2048];
1348  struct ImapList listresp = { 0 };
1349  char completion[1024];
1350  int clen;
1351  size_t matchlen = 0;
1352  int completions = 0;
1353  int rc;
1354 
1355  if (imap_adata_find(path, &adata, &mdata) < 0)
1356  {
1357  mutt_str_copy(buf, path, buflen);
1358  return complete_hosts(buf, buflen);
1359  }
1360 
1361  /* fire off command */
1362  const bool c_imap_list_subscribed =
1363  cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1364  snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1365  c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1366 
1367  imap_cmd_start(adata, tmp);
1368 
1369  /* and see what the results are */
1370  mutt_str_copy(completion, mdata->name, sizeof(completion));
1371  imap_mdata_free((void *) &mdata);
1372 
1373  adata->cmdresult = &listresp;
1374  do
1375  {
1376  listresp.name = NULL;
1377  rc = imap_cmd_step(adata);
1378 
1379  if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1380  {
1381  /* if the folder isn't selectable, append delimiter to force browse
1382  * to enter it on second tab. */
1383  if (listresp.noselect)
1384  {
1385  clen = strlen(listresp.name);
1386  listresp.name[clen++] = listresp.delim;
1387  listresp.name[clen] = '\0';
1388  }
1389  /* copy in first word */
1390  if (!completions)
1391  {
1392  mutt_str_copy(completion, listresp.name, sizeof(completion));
1393  matchlen = strlen(completion);
1394  completions++;
1395  continue;
1396  }
1397 
1398  matchlen = longest_common_prefix(completion, listresp.name, 0, matchlen);
1399  completions++;
1400  }
1401  } while (rc == IMAP_RES_CONTINUE);
1402  adata->cmdresult = NULL;
1403 
1404  if (completions)
1405  {
1406  /* reformat output */
1407  imap_qualify_path(buf, buflen, &adata->conn->account, completion);
1408  mutt_pretty_mailbox(buf, buflen);
1409  return 0;
1410  }
1411 
1412  return -1;
1413 }
1414 
1423 int imap_fast_trash(struct Mailbox *m, const char *dest)
1424 {
1425  char prompt[1024];
1426  int rc = -1;
1427  bool triedcreate = false;
1428  enum QuadOption err_continue = MUTT_NO;
1429 
1430  struct ImapAccountData *adata = imap_adata_get(m);
1431  struct ImapAccountData *dest_adata = NULL;
1432  struct ImapMboxData *dest_mdata = NULL;
1433 
1434  if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1435  return -1;
1436 
1437  struct Buffer sync_cmd = mutt_buffer_make(0);
1438 
1439  /* check that the save-to folder is in the same account */
1440  if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1441  {
1442  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1443  goto out;
1444  }
1445 
1446  for (int i = 0; i < m->msg_count; i++)
1447  {
1448  struct Email *e = m->emails[i];
1449  if (!e)
1450  break;
1451  if (e->active && e->changed && e->deleted && !e->purge)
1452  {
1453  rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1454  if (rc < 0)
1455  {
1456  mutt_debug(LL_DEBUG1, "could not sync\n");
1457  goto out;
1458  }
1459  }
1460  }
1461 
1462  /* loop in case of TRYCREATE */
1463  do
1464  {
1465  rc = imap_exec_msgset(m, "UID COPY", dest_mdata->munge_name, MUTT_TRASH, false, false);
1466  if (rc == 0)
1467  {
1468  mutt_debug(LL_DEBUG1, "No messages to trash\n");
1469  rc = -1;
1470  goto out;
1471  }
1472  else if (rc < 0)
1473  {
1474  mutt_debug(LL_DEBUG1, "could not queue copy\n");
1475  goto out;
1476  }
1477  else if (m->verbose)
1478  {
1479  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1480  rc, dest_mdata->name);
1481  }
1482 
1483  /* let's get it on */
1484  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1485  if (rc == IMAP_EXEC_ERROR)
1486  {
1487  if (triedcreate)
1488  {
1489  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1490  break;
1491  }
1492  /* bail out if command failed for reasons other than nonexistent target */
1493  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1494  break;
1495  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1496  snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1497  const bool c_confirm_create =
1498  cs_subset_bool(NeoMutt->sub, "confirm_create");
1499  if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1500  {
1501  mutt_clear_error();
1502  goto out;
1503  }
1504  if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1505  break;
1506  triedcreate = true;
1507  }
1508  } while (rc == IMAP_EXEC_ERROR);
1509 
1510  if (rc != IMAP_EXEC_SUCCESS)
1511  {
1512  imap_error("imap_fast_trash", adata->buf);
1513  goto out;
1514  }
1515 
1516  rc = IMAP_EXEC_SUCCESS;
1517 
1518 out:
1519  mutt_buffer_dealloc(&sync_cmd);
1520  imap_mdata_free((void *) &dest_mdata);
1521 
1522  return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1523 }
1524 
1534 enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
1535 {
1536  if (!m)
1537  return -1;
1538 
1539  struct Email **emails = NULL;
1540  int rc;
1541 
1542  struct ImapAccountData *adata = imap_adata_get(m);
1543  struct ImapMboxData *mdata = imap_mdata_get(m);
1544 
1545  if (adata->state < IMAP_SELECTED)
1546  {
1547  mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1548  return -1;
1549  }
1550 
1551  /* This function is only called when the calling code expects the context
1552  * to be changed. */
1553  imap_allow_reopen(m);
1554 
1555  enum MxStatus check = imap_check_mailbox(m, false);
1556  if (check == MX_STATUS_ERROR)
1557  return check;
1558 
1559  /* if we are expunging anyway, we can do deleted messages very quickly... */
1560  if (expunge && (m->rights & MUTT_ACL_DELETE))
1561  {
1562  rc = imap_exec_msgset(m, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
1563  MUTT_DELETED, true, false);
1564  if (rc < 0)
1565  {
1566  mutt_error(_("Expunge failed"));
1567  return rc;
1568  }
1569 
1570  if (rc > 0)
1571  {
1572  /* mark these messages as unchanged so second pass ignores them. Done
1573  * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1574  for (int i = 0; i < m->msg_count; i++)
1575  {
1576  struct Email *e = m->emails[i];
1577  if (!e)
1578  break;
1579  if (e->deleted && e->changed)
1580  e->active = false;
1581  }
1582  if (m->verbose)
1583  {
1584  mutt_message(ngettext("Marking %d message deleted...",
1585  "Marking %d messages deleted...", rc),
1586  rc);
1587  }
1588  }
1589  }
1590 
1591 #ifdef USE_HCACHE
1592  imap_hcache_open(adata, mdata);
1593 #endif
1594 
1595  /* save messages with real (non-flag) changes */
1596  for (int i = 0; i < m->msg_count; i++)
1597  {
1598  struct Email *e = m->emails[i];
1599  if (!e)
1600  break;
1601 
1602  if (e->deleted)
1603  {
1604  imap_cache_del(m, e);
1605 #ifdef USE_HCACHE
1606  imap_hcache_del(mdata, imap_edata_get(e)->uid);
1607 #endif
1608  }
1609 
1610  if (e->active && e->changed)
1611  {
1612 #ifdef USE_HCACHE
1613  imap_hcache_put(mdata, e);
1614 #endif
1615  /* if the message has been rethreaded or attachments have been deleted
1616  * we delete the message and reupload it.
1617  * This works better if we're expunging, of course. */
1618  /* TODO: why the e->env check? */
1619  if ((e->env && e->env->changed) || e->attach_del)
1620  {
1621  /* L10N: The plural is chosen by the last %d, i.e. the total number */
1622  if (m->verbose)
1623  {
1624  mutt_message(ngettext("Saving changed message... [%d/%d]",
1625  "Saving changed messages... [%d/%d]", m->msg_count),
1626  i + 1, m->msg_count);
1627  }
1628  bool save_append = m->append;
1629  m->append = true;
1631  m->append = save_append;
1632  /* TODO: why the check for e->env? Is this possible? */
1633  if (e->env)
1634  e->env->changed = 0;
1635  }
1636  }
1637  }
1638 
1639 #ifdef USE_HCACHE
1640  imap_hcache_close(mdata);
1641 #endif
1642 
1643  /* presort here to avoid doing 10 resorts in imap_exec_msgset */
1644  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1645  if (c_sort != SORT_ORDER)
1646  {
1647  emails = m->emails;
1648  m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1649  memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));
1650 
1652  qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
1653  }
1654 
1655  rc = sync_helper(m, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1656  if (rc >= 0)
1657  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1658  if (rc >= 0)
1659  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1660  if (rc >= 0)
1661  rc |= sync_helper(m, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1662  if (rc >= 0)
1663  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1664 
1665  if (c_sort != SORT_ORDER)
1666  {
1667  cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
1668  FREE(&m->emails);
1669  m->emails = emails;
1670  }
1671 
1672  /* Flush the queued flags if any were changed in sync_helper. */
1673  if (rc > 0)
1674  if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1675  rc = -1;
1676 
1677  if (rc < 0)
1678  {
1679  if (close)
1680  {
1681  if (mutt_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1682  {
1683  adata->state = IMAP_AUTHENTICATED;
1684  return 0;
1685  }
1686  }
1687  else
1688  mutt_error(_("Error saving flags"));
1689  return -1;
1690  }
1691 
1692  /* Update local record of server state to reflect the synchronization just
1693  * completed. imap_read_headers always overwrites hcache-origin flags, so
1694  * there is no need to mutate the hcache after flag-only changes. */
1695  for (int i = 0; i < m->msg_count; i++)
1696  {
1697  struct Email *e = m->emails[i];
1698  if (!e)
1699  break;
1700  struct ImapEmailData *edata = imap_edata_get(e);
1701  edata->deleted = e->deleted;
1702  edata->flagged = e->flagged;
1703  edata->old = e->old;
1704  edata->read = e->read;
1705  edata->replied = e->replied;
1706  e->changed = false;
1707  }
1708  m->changed = false;
1709 
1710  /* We must send an EXPUNGE command if we're not closing. */
1711  if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1712  {
1713  if (m->verbose)
1714  mutt_message(_("Expunging messages from server..."));
1715  /* Set expunge bit so we don't get spurious reopened messages */
1716  mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1717  if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1718  {
1719  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1720  imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1721  return -1;
1722  }
1723  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1724  }
1725 
1726  if (expunge && close)
1727  {
1728  adata->closing = true;
1729  imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
1730  adata->state = IMAP_AUTHENTICATED;
1731  }
1732 
1733  const bool c_message_cache_clean =
1734  cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1735  if (c_message_cache_clean)
1736  imap_cache_clean(m);
1737 
1738  return check;
1739 }
1740 
1744 static bool imap_ac_owns_path(struct Account *a, const char *path)
1745 {
1746  struct Url *url = url_parse(path);
1747  if (!url)
1748  return false;
1749 
1750  struct ImapAccountData *adata = a->adata;
1751  struct ConnAccount *cac = &adata->conn->account;
1752 
1753  const bool ret = mutt_istr_equal(url->host, cac->host) &&
1754  (!url->user || mutt_istr_equal(url->user, cac->user));
1755  url_free(&url);
1756  return ret;
1757 }
1758 
1762 static bool imap_ac_add(struct Account *a, struct Mailbox *m)
1763 {
1764  struct ImapAccountData *adata = a->adata;
1765 
1766  if (!adata)
1767  {
1768  struct ConnAccount cac = { { 0 } };
1769  char mailbox[PATH_MAX];
1770 
1771  if (imap_parse_path(mailbox_path(m), &cac, mailbox, sizeof(mailbox)) < 0)
1772  return false;
1773 
1774  adata = imap_adata_new(a);
1775  adata->conn = mutt_conn_new(&cac);
1776  if (!adata->conn)
1777  {
1778  imap_adata_free((void **) &adata);
1779  return false;
1780  }
1781 
1783 
1784  if (imap_login(adata) < 0)
1785  {
1786  imap_adata_free((void **) &adata);
1787  return false;
1788  }
1789 
1790  a->adata = adata;
1792  }
1793 
1794  if (!m->mdata)
1795  {
1796  struct Url *url = url_parse(mailbox_path(m));
1797  struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);
1798 
1799  /* fixup path and realpath, mainly to replace / by /INBOX */
1800  char buf[1024];
1801  imap_qualify_path(buf, sizeof(buf), &adata->conn->account, mdata->name);
1802  mutt_buffer_strcpy(&m->pathbuf, buf);
1804 
1805  m->mdata = mdata;
1807  url_free(&url);
1808  }
1809  return true;
1810 }
1811 
1816 static void imap_mbox_select(struct Mailbox *m)
1817 {
1818  struct ImapAccountData *adata = imap_adata_get(m);
1819  struct ImapMboxData *mdata = imap_mdata_get(m);
1820  if (!adata || !mdata)
1821  return;
1822 
1823  const char *condstore = NULL;
1824 #ifdef USE_HCACHE
1825  const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1826  if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1827  condstore = " (CONDSTORE)";
1828  else
1829 #endif
1830  condstore = "";
1831 
1832  char buf[PATH_MAX];
1833  snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1834  mdata->munge_name, condstore);
1835 
1836  adata->state = IMAP_SELECTED;
1837 
1838  imap_cmd_start(adata, buf);
1839 }
1840 
1849 int imap_login(struct ImapAccountData *adata)
1850 {
1851  if (!adata)
1852  return -1;
1853 
1854  if (adata->state == IMAP_DISCONNECTED)
1855  {
1856  mutt_buffer_reset(&adata->cmdbuf); // purge outstanding queued commands
1857  imap_open_connection(adata);
1858  }
1859  if (adata->state == IMAP_CONNECTED)
1860  {
1861  if (imap_authenticate(adata) == IMAP_AUTH_SUCCESS)
1862  {
1863  adata->state = IMAP_AUTHENTICATED;
1864  FREE(&adata->capstr);
1865  if (adata->conn->ssf != 0)
1866  {
1867  mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1868  adata->conn->ssf);
1869  }
1870  }
1871  else
1873  }
1874  if (adata->state == IMAP_AUTHENTICATED)
1875  {
1876  /* capabilities may have changed */
1877  imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1878 
1879 #ifdef USE_ZLIB
1880  /* RFC4978 */
1881  const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1882  if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1883  (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1884  {
1885  mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1886  adata->conn->account.host);
1887  mutt_zstrm_wrap_conn(adata->conn);
1888  }
1889 #endif
1890 
1891  /* enable RFC6855, if the server supports that */
1892  const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1893  if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1894  imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1895 
1896  /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1897  * is supported (even if not advertised), so flip that bit. */
1898  if (adata->capabilities & IMAP_CAP_QRESYNC)
1899  {
1900  adata->capabilities |= IMAP_CAP_CONDSTORE;
1901  const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1902  if (c_imap_rfc5161 && c_imap_qresync)
1903  imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1904  }
1905 
1906  /* get root delimiter, '/' as default */
1907  adata->delim = '/';
1908  imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1909 
1910  /* we may need the root delimiter before we open a mailbox */
1911  imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1912 
1913  /* select the mailbox that used to be open before disconnect */
1914  if (adata->mailbox)
1915  {
1916  imap_mbox_select(adata->mailbox);
1917  }
1918  }
1919 
1920  if (adata->state < IMAP_AUTHENTICATED)
1921  return -1;
1922 
1923  return 0;
1924 }
1925 
1929 static enum MxOpenReturns imap_mbox_open(struct Mailbox *m)
1930 {
1931  if (!m->account || !m->mdata)
1932  return MX_OPEN_ERROR;
1933 
1934  char buf[PATH_MAX];
1935  int count = 0;
1936  int rc;
1937 
1938  struct ImapAccountData *adata = imap_adata_get(m);
1939  struct ImapMboxData *mdata = imap_mdata_get(m);
1940 
1941  mutt_debug(LL_DEBUG3, "opening %s, saving %s\n", m->pathbuf.data,
1942  (adata->mailbox ? adata->mailbox->pathbuf.data : "(none)"));
1943  adata->prev_mailbox = adata->mailbox;
1944  adata->mailbox = m;
1945 
1946  /* clear mailbox status */
1947  adata->status = 0;
1948  m->rights = 0;
1949  mdata->new_mail_count = 0;
1950 
1951  if (m->verbose)
1952  mutt_message(_("Selecting %s..."), mdata->name);
1953 
1954  /* pipeline ACL test */
1955  if (adata->capabilities & IMAP_CAP_ACL)
1956  {
1957  snprintf(buf, sizeof(buf), "MYRIGHTS %s", mdata->munge_name);
1958  imap_exec(adata, buf, IMAP_CMD_QUEUE);
1959  }
1960  /* assume we have all rights if ACL is unavailable */
1961  else
1962  {
1965  }
1966 
1967  /* pipeline the postponed count if possible */
1968  const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed");
1969  struct Mailbox *m_postponed = mx_mbox_find2(c_postponed);
1970  struct ImapAccountData *postponed_adata = imap_adata_get(m_postponed);
1971  if (postponed_adata &&
1972  imap_account_match(&postponed_adata->conn->account, &adata->conn->account))
1973  {
1974  imap_mailbox_status(m_postponed, true);
1975  }
1976 
1977  const bool c_imap_check_subscribed =
1978  cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1979  if (c_imap_check_subscribed)
1980  imap_exec(adata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
1981 
1982  imap_mbox_select(m);
1983 
1984  do
1985  {
1986  char *pc = NULL;
1987 
1988  rc = imap_cmd_step(adata);
1989  if (rc != IMAP_RES_CONTINUE)
1990  break;
1991 
1992  pc = adata->buf + 2;
1993 
1994  /* Obtain list of available flags here, may be overridden by a
1995  * PERMANENTFLAGS tag in the OK response */
1996  if (mutt_istr_startswith(pc, "FLAGS"))
1997  {
1998  /* don't override PERMANENTFLAGS */
1999  if (STAILQ_EMPTY(&mdata->flags))
2000  {
2001  mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
2002  pc = get_flags(&mdata->flags, pc);
2003  if (!pc)
2004  goto fail;
2005  }
2006  }
2007  /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
2008  else if (mutt_istr_startswith(pc, "OK [PERMANENTFLAGS"))
2009  {
2010  mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
2011  /* safe to call on NULL */
2012  mutt_list_free(&mdata->flags);
2013  /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
2014  pc += 13;
2015  pc = get_flags(&(mdata->flags), pc);
2016  if (!pc)
2017  goto fail;
2018  }
2019  /* save UIDVALIDITY for the header cache */
2020  else if (mutt_istr_startswith(pc, "OK [UIDVALIDITY"))
2021  {
2022  mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
2023  pc += 3;
2024  pc = imap_next_word(pc);
2025  if (mutt_str_atoui(pc, &mdata->uidvalidity) < 0)
2026  goto fail;
2027  }
2028  else if (mutt_istr_startswith(pc, "OK [UIDNEXT"))
2029  {
2030  mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
2031  pc += 3;
2032  pc = imap_next_word(pc);
2033  if (mutt_str_atoui(pc, &mdata->uid_next) < 0)
2034  goto fail;
2035  }
2036  else if (mutt_istr_startswith(pc, "OK [HIGHESTMODSEQ"))
2037  {
2038  mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
2039  pc += 3;
2040  pc = imap_next_word(pc);
2041  if (mutt_str_atoull(pc, &mdata->modseq) < 0)
2042  goto fail;
2043  }
2044  else if (mutt_istr_startswith(pc, "OK [NOMODSEQ"))
2045  {
2046  mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
2047  mdata->modseq = 0;
2048  }
2049  else
2050  {
2051  pc = imap_next_word(pc);
2052  if (mutt_istr_startswith(pc, "EXISTS"))
2053  {
2054  count = mdata->new_mail_count;
2055  mdata->new_mail_count = 0;
2056  }
2057  }
2058  } while (rc == IMAP_RES_CONTINUE);
2059 
2060  if (rc == IMAP_RES_NO)
2061  {
2062  char *s = imap_next_word(adata->buf); /* skip seq */
2063  s = imap_next_word(s); /* Skip response */
2064  mutt_error("%s", s);
2065  goto fail;
2066  }
2067 
2068  if (rc != IMAP_RES_OK)
2069  goto fail;
2070 
2071  /* check for READ-ONLY notification */
2072  if (mutt_istr_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]") &&
2073  !(adata->capabilities & IMAP_CAP_ACL))
2074  {
2075  mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2076  m->readonly = true;
2077  }
2078 
2079  /* dump the mailbox flags we've found */
2080  const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
2081  if (c_debug_level > LL_DEBUG2)
2082  {
2083  if (STAILQ_EMPTY(&mdata->flags))
2084  mutt_debug(LL_DEBUG3, "No folder flags found\n");
2085  else
2086  {
2087  struct ListNode *np = NULL;
2088  struct Buffer flag_buffer;
2089  mutt_buffer_init(&flag_buffer);
2090  mutt_buffer_printf(&flag_buffer, "Mailbox flags: ");
2091  STAILQ_FOREACH(np, &mdata->flags, entries)
2092  {
2093  mutt_buffer_add_printf(&flag_buffer, "[%s] ", np->data);
2094  }
2095  mutt_debug(LL_DEBUG3, "%s\n", flag_buffer.data);
2096  FREE(&flag_buffer.data);
2097  }
2098  }
2099 
2100  if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2101  (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2102  {
2103  m->readonly = true;
2104  }
2105 
2106  while (m->email_max < count)
2107  mx_alloc_memory(m);
2108 
2109  m->msg_count = 0;
2110  m->msg_unread = 0;
2111  m->msg_flagged = 0;
2112  m->msg_new = 0;
2113  m->msg_deleted = 0;
2114  m->size = 0;
2115  m->vcount = 0;
2116 
2117  if (count && (imap_read_headers(m, 1, count, true) < 0))
2118  {
2119  mutt_error(_("Error opening mailbox"));
2120  goto fail;
2121  }
2122 
2123  mutt_debug(LL_DEBUG2, "msg_count is %d\n", m->msg_count);
2124  return MX_OPEN_OK;
2125 
2126 fail:
2127  if (adata->state == IMAP_SELECTED)
2128  adata->state = IMAP_AUTHENTICATED;
2129  return MX_OPEN_ERROR;
2130 }
2131 
2135 static bool imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
2136 {
2137  if (!m->account)
2138  return false;
2139 
2140  /* in APPEND mode, we appear to hijack an existing IMAP connection -
2141  * ctx is brand new and mostly empty */
2142  struct ImapAccountData *adata = imap_adata_get(m);
2143  struct ImapMboxData *mdata = imap_mdata_get(m);
2144 
2145  int rc = imap_mailbox_status(m, false);
2146  if (rc >= 0)
2147  return true;
2148  if (rc == -1)
2149  return false;
2150 
2151  char buf[PATH_MAX + 64];
2152  snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
2153  const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
2154  if (c_confirm_create && (mutt_yesorno(buf, MUTT_YES) != MUTT_YES))
2155  return false;
2156 
2157  if (imap_create_mailbox(adata, mdata->name) < 0)
2158  return false;
2159 
2160  return true;
2161 }
2162 
2169 static enum MxStatus imap_mbox_check(struct Mailbox *m)
2170 {
2171  imap_allow_reopen(m);
2172  enum MxStatus rc = imap_check_mailbox(m, false);
2173  /* NOTE - ctx might have been changed at this point. In particular,
2174  * m could be NULL. Beware. */
2176 
2177  return rc;
2178 }
2179 
2183 static enum MxStatus imap_mbox_close(struct Mailbox *m)
2184 {
2185  struct ImapAccountData *adata = imap_adata_get(m);
2186  struct ImapMboxData *mdata = imap_mdata_get(m);
2187 
2188  /* Check to see if the mailbox is actually open */
2189  if (!adata || !mdata)
2190  return MX_STATUS_OK;
2191 
2192  /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
2193  * just for the connection.
2194  *
2195  * So when these are equal, it means we are actually closing the
2196  * mailbox and should clean up adata. Otherwise, we don't want to
2197  * touch adata - it's still being used. */
2198  if (m == adata->mailbox)
2199  {
2200  if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
2201  {
2202  /* mx_mbox_close won't sync if there are no deleted messages
2203  * and the mailbox is unchanged, so we may have to close here */
2204  if (m->msg_deleted == 0)
2205  {
2206  adata->closing = true;
2207  imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
2208  }
2209  adata->state = IMAP_AUTHENTICATED;
2210  }
2211 
2212  mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
2213  (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
2214  adata->mailbox = adata->prev_mailbox;
2217  }
2218 
2219  return MX_STATUS_OK;
2220 }
2221 
2225 static bool imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
2226 {
2227  bool success = false;
2228 
2229  struct Buffer *tmp = mutt_buffer_pool_get();
2230  mutt_buffer_mktemp(tmp);
2231 
2232  msg->fp = mutt_file_fopen(mutt_buffer_string(tmp), "w");
2233  if (!msg->fp)
2234  {
2236  goto cleanup;
2237  }
2238 
2239  msg->path = mutt_buffer_strdup(tmp);
2240  success = true;
2241 
2242 cleanup:
2244  return success;
2245 }
2246 
2250 static int imap_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
2251 {
2252  struct ImapMboxData *mdata = imap_mdata_get(m);
2253  if (!mdata)
2254  return -1;
2255 
2256  char *new_tag = NULL;
2257  char *checker = NULL;
2258 
2259  /* Check for \* flags capability */
2260  if (!imap_has_flag(&mdata->flags, NULL))
2261  {
2262  mutt_error(_("IMAP server doesn't support custom flags"));
2263  return -1;
2264  }
2265 
2266  *buf = '\0';
2267  if (tags)
2268  mutt_str_copy(buf, tags, buflen);
2269 
2270  if (mutt_get_field("Tags: ", buf, buflen, MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0)
2271  return -1;
2272 
2273  /* each keyword must be atom defined by rfc822 as:
2274  *
2275  * atom = 1*<any CHAR except specials, SPACE and CTLs>
2276  * CHAR = ( 0.-127. )
2277  * specials = "(" / ")" / "<" / ">" / "@"
2278  * / "," / ";" / ":" / "\" / <">
2279  * / "." / "[" / "]"
2280  * SPACE = ( 32. )
2281  * CTLS = ( 0.-31., 127.)
2282  *
2283  * And must be separated by one space.
2284  */
2285 
2286  new_tag = buf;
2287  checker = buf;
2288  SKIPWS(checker);
2289  while (*checker != '\0')
2290  {
2291  if ((*checker < 32) || (*checker >= 127) || // We allow space because it's the separator
2292  (*checker == 40) || // (
2293  (*checker == 41) || // )
2294  (*checker == 60) || // <
2295  (*checker == 62) || // >
2296  (*checker == 64) || // @
2297  (*checker == 44) || // ,
2298  (*checker == 59) || // ;
2299  (*checker == 58) || // :
2300  (*checker == 92) || // backslash
2301  (*checker == 34) || // "
2302  (*checker == 46) || // .
2303  (*checker == 91) || // [
2304  (*checker == 93)) // ]
2305  {
2306  mutt_error(_("Invalid IMAP flags"));
2307  return 0;
2308  }
2309 
2310  /* Skip duplicate space */
2311  while ((checker[0] == ' ') && (checker[1] == ' '))
2312  checker++;
2313 
2314  /* copy char to new_tag and go the next one */
2315  *new_tag++ = *checker++;
2316  }
2317  *new_tag = '\0';
2318  new_tag = buf; /* rewind */
2319  mutt_str_remove_trailing_ws(new_tag);
2320 
2321  return !mutt_str_equal(tags, buf);
2322 }
2323 
2337 static int imap_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
2338 {
2339  char uid[11];
2340 
2341  struct ImapAccountData *adata = imap_adata_get(m);
2342 
2343  if (*buf == '\0')
2344  buf = NULL;
2345 
2346  if (!(adata->mailbox->rights & MUTT_ACL_WRITE))
2347  return 0;
2348 
2349  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
2350 
2351  /* Remove old custom flags */
2352  if (imap_edata_get(e)->flags_remote)
2353  {
2354  struct Buffer cmd = mutt_buffer_make(128); // just a guess
2355  mutt_buffer_addstr(&cmd, "UID STORE ");
2356  mutt_buffer_addstr(&cmd, uid);
2357  mutt_buffer_addstr(&cmd, " -FLAGS.SILENT (");
2358  mutt_buffer_addstr(&cmd, imap_edata_get(e)->flags_remote);
2359  mutt_buffer_addstr(&cmd, ")");
2360 
2361  /* Should we return here, or we are fine and we could
2362  * continue to add new flags */
2363  int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2364  mutt_buffer_dealloc(&cmd);
2365  if (rc != IMAP_EXEC_SUCCESS)
2366  {
2367  return -1;
2368  }
2369  }
2370 
2371  /* Add new custom flags */
2372  if (buf)
2373  {
2374  struct Buffer cmd = mutt_buffer_make(128); // just a guess
2375  mutt_buffer_addstr(&cmd, "UID STORE ");
2376  mutt_buffer_addstr(&cmd, uid);
2377  mutt_buffer_addstr(&cmd, " +FLAGS.SILENT (");
2378  mutt_buffer_addstr(&cmd, buf);
2379  mutt_buffer_addstr(&cmd, ")");
2380 
2381  int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2382  mutt_buffer_dealloc(&cmd);
2383  if (rc != IMAP_EXEC_SUCCESS)
2384  {
2385  mutt_debug(LL_DEBUG1, "fail to add new flags\n");
2386  return -1;
2387  }
2388  }
2389 
2390  /* We are good sync them */
2391  mutt_debug(LL_DEBUG1, "NEW TAGS: %s\n", buf);
2392  driver_tags_replace(&e->tags, buf);
2393  FREE(&imap_edata_get(e)->flags_remote);
2395  imap_msg_save_hcache(m, e);
2396  return 0;
2397 }
2398 
2402 enum MailboxType imap_path_probe(const char *path, const struct stat *st)
2403 {
2404  if (mutt_istr_startswith(path, "imap://"))
2405  return MUTT_IMAP;
2406 
2407  if (mutt_istr_startswith(path, "imaps://"))
2408  return MUTT_IMAP;
2409 
2410  return MUTT_UNKNOWN;
2411 }
2412 
2416 int imap_path_canon(char *buf, size_t buflen)
2417 {
2418  struct Url *url = url_parse(buf);
2419  if (!url)
2420  return 0;
2421 
2422  char tmp[PATH_MAX];
2423  char tmp2[PATH_MAX];
2424 
2425  imap_fix_path('\0', url->path, tmp, sizeof(tmp));
2426  url->path = tmp;
2427  url_tostring(url, tmp2, sizeof(tmp2), U_NO_FLAGS);
2428  mutt_str_copy(buf, tmp2, buflen);
2429  url_free(&url);
2430 
2431  return 0;
2432 }
2433 
2442 int imap_expand_path(struct Buffer *buf)
2443 {
2445  return imap_path_canon(buf->data, PATH_MAX);
2446 }
2447 
2451 static int imap_path_pretty(char *buf, size_t buflen, const char *folder)
2452 {
2453  if (!folder)
2454  return -1;
2455 
2456  imap_pretty_mailbox(buf, buflen, folder);
2457  return 0;
2458 }
2459 
2463 static int imap_path_parent(char *buf, size_t buflen)
2464 {
2465  char tmp[PATH_MAX] = { 0 };
2466 
2467  imap_get_parent_path(buf, tmp, sizeof(tmp));
2468  mutt_str_copy(buf, tmp, buflen);
2469  return 0;
2470 }
2471 
2475 static int imap_path_is_empty(const char *path)
2476 {
2477  int rc = imap_path_status(path, false);
2478  if (rc < 0)
2479  return -1;
2480  if (rc == 0)
2481  return 1;
2482  return 0;
2483 }
2484 
2485 // clang-format off
2489 struct MxOps MxImapOps = {
2490  .type = MUTT_IMAP,
2491  .name = "imap",
2492  .is_local = false,
2493  .ac_owns_path = imap_ac_owns_path,
2494  .ac_add = imap_ac_add,
2495  .mbox_open = imap_mbox_open,
2496  .mbox_open_append = imap_mbox_open_append,
2497  .mbox_check = imap_mbox_check,
2498  .mbox_check_stats = imap_mbox_check_stats,
2499  .mbox_sync = NULL, /* imap syncing is handled by imap_sync_mailbox */
2500  .mbox_close = imap_mbox_close,
2501  .msg_open = imap_msg_open,
2502  .msg_open_new = imap_msg_open_new,
2503  .msg_commit = imap_msg_commit,
2504  .msg_close = imap_msg_close,
2505  .msg_padding_size = NULL,
2506  .msg_save_hcache = imap_msg_save_hcache,
2507  .tags_edit = imap_tags_edit,
2508  .tags_commit = imap_tags_commit,
2509  .path_probe = imap_path_probe,
2510  .path_canon = imap_path_canon,
2511  .path_pretty = imap_path_pretty,
2512  .path_parent = imap_path_parent,
2513  .path_is_empty = imap_path_is_empty,
2514 };
2515 // clang-format on
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
#define IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition: private.h:140
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
enum QuadOption imap_continue(const char *msg, const char *resp)
display a message and ask the user if they want to go on
Definition: util.c:654
Deleted messages.
Definition: mutt.h:97
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:288
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:526
struct ImapCommand * cmds
Definition: adata.h:69
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition: util.c:823
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:123
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
void imap_disallow_reopen(struct Mailbox *m)
Disallow re-opening a folder upon expunge.
Definition: util.c:1034
off_t size
Size of the Mailbox.
Definition: mailbox.h:87
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
Save message to the header cache - Implements MxOps::msg_save_hcache() -.
Definition: message.c:2153
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void imap_mdata_cache_reset(struct ImapMboxData *mdata)
Release and clear cache data of ImapMboxData structure.
Definition: util.c:106
Imap-specific Email data.
static bool imap_ac_owns_path(struct Account *a, const char *path)
Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -.
Definition: imap.c:1744
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1235
Logged out from server.
Definition: private.h:98
Config/command parsing.
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
uint32_t uidvalidity
Definition: mdata.h:50
Imap connection failure.
Definition: private.h:86
Update internal tables.
Definition: mailbox.h:183
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:41
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
Structs that make up an email.
#define MUTT_ACL_READ
Read the mailbox.
Definition: mailbox.h:72
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:745
#define mutt_error(...)
Definition: logging.h:88
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
Connection is authenticated.
Definition: private.h:109
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition: imap.c:516
unsigned int seqno
tag sequence number, e.g. &#39;{seqid}0001&#39;
Definition: adata.h:57
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:68
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
int mutt_save_message_ctx(struct Mailbox *m_src, struct Email *e, enum MessageSaveOpt save_opt, enum MessageTransformOpt transform_opt, struct Mailbox *m_dst)
Save a message to a given mailbox.
Definition: commands.c:1103
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:141
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:335
Unrecoverable error occurred.
Definition: private.h:97
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1849
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition: util.c:483
enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition: imap.c:1534
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:381
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
#define IMAP_CAP_ENABLE
RFC5161.
Definition: private.h:136
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:65
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: message.c:2131
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition: util.c:399
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1176
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition: util.c:914
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:40
NeoMutt Logging.
int lastcmd
Definition: adata.h:72
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
Match any Mailbox type.
Definition: mailbox.h:45
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:164
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
A group of associated Mailboxes.
Definition: account.h:36
No transformation.
Definition: commands.h:41
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1819
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:621
String manipulation buffer.
Definition: buffer.h:33
Nondestructive flags change (IMAP)
Definition: mxapi.h:82
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:69
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:536
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
New mail received in Mailbox.
Definition: mxapi.h:79
char user[128]
Username.
Definition: connaccount.h:55
enum CommandResult mutt_parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: init.c:1039
Flagged messages.
Definition: mutt.h:98
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1212
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
IMAP authenticator multiplexor.
int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
Rename a mailbox.
Definition: imap.c:489
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
append str to flags if we currently have permission according to aclflag
Definition: imap.c:176
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1213
bool changed
Email has been edited.
Definition: email.h:48
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
#define U_NO_FLAGS
Definition: url.h:49
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:590
bool imap_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox - Implements MxOps::msg_open() -.
Definition: message.c:1927
char * capstr
Definition: adata.h:54
Items in an IMAP browser.
Definition: private.h:148
ImapExecResult
imap_exec return code
Definition: private.h:82
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1049
struct ImapAccountData * imap_adata_new(struct Account *a)
Allocate and initialise a new ImapAccountData structure.
Definition: adata.c:66
void imap_init(void)
Setup feature commands.
Definition: imap.c:83
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
Imap-specific Account data.
static enum MxStatus imap_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: imap.c:2169
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1065
static int sync_helper(struct Mailbox *m, AclFlags right, enum MessageType flag, const char *name)
Sync flag changes to the server.
Definition: imap.c:324
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:88
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1640
IMAP MSN helper functions.
int imap_read_literal(FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *pbar)
Read bytes bytes from server into file.
Definition: imap.c:600
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
char delim
Definition: private.h:151
struct ImapMboxData * imap_mdata_new(struct ImapAccountData *adata, const char *name)
Allocate and initialise a new ImapMboxData structure.
Definition: mdata.c:72
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition: private.h:132
#define mutt_perror(...)
Definition: logging.h:89
static enum MxStatus imap_mbox_check_stats(struct Mailbox *m, uint8_t flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
Definition: imap.c:1219
Container for Accounts, Notifications.
Definition: neomutt.h:36
A Progress Bar.
Definition: progress.c:47
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:82
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
Open succeeded.
Definition: mxapi.h:90
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
static char * get_flags(struct ListHead *hflags, char *s)
Make a simple list out of a FLAGS response.
Definition: imap.c:121
Messages that have been replied to.
Definition: mutt.h:91
int vcount
The number of virtual messages.
Definition: mailbox.h:102
Mailbox was reopened.
Definition: mxapi.h:81
Imap command executed or queued successfully.
Definition: private.h:84
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
Convenience wrapper for the config headers.
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
#define IMAP_MAX_CMDLEN
Maximum length of command lines before they must be split (for lazy servers)
Definition: private.h:62
int mutt_socket_readchar(struct Connection *conn, char *c)
simple read buffering to speed things up
Definition: socket.c:210
static bool imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
Open a new message in a Mailbox - Implements MxOps::msg_open_new() -.
Definition: imap.c:2225
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:76
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
int imap_fast_trash(struct Mailbox *m, const char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1423
bool flagged
Definition: edata.h:39
void imap_logout_all(void)
close all open connections
Definition: imap.c:564
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
char host[128]
Server to login to.
Definition: connaccount.h:53
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:146
Some miscellaneous functions.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
static int make_msg_set(struct Mailbox *m, struct Buffer *buf, enum MessageType flag, bool changed, bool invert, int *pos)
Make a message set.
Definition: imap.c:197
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:72
bool deleted
Definition: edata.h:38
Progress bar.
Imap-specific Mailbox data.
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
size_t dsize
Length of data.
Definition: buffer.h:37
bool tagged
Email is tagged.
Definition: email.h:44
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:192
bool read
Email is read.
Definition: email.h:51
int nextcmd
Definition: adata.h:71
static int complete_hosts(char *buf, size_t buflen)
Look for completion matches for mailboxes.
Definition: imap.c:386
Disconnected from server.
Definition: private.h:107
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:340
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2442
Parse and execute user-defined hooks.
MessageType
To set flags or match patterns.
Definition: mutt.h:85
Many unsorted constants and some structs.
void imap_msn_remove(struct MSN *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:112
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
bool old
Email is seen, but unread.
Definition: email.h:50
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:161
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1383
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:52
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:878
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
int imap_read_headers(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
Read headers from the server.
Definition: message.c:1299
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
struct Buffer cmdbuf
Definition: adata.h:73
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
static enum MxStatus imap_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: imap.c:2183
void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
Inform IMAP that an Email has been deleted.
Definition: imap.c:656
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: mdata.h:45
#define SKIPWS(ch)
Definition: string2.h:46
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
Mailbox is selected.
Definition: private.h:110
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2402
struct Mailbox * prev_mailbox
Previously selected mailbox.
Definition: adata.h:77
Connected to server.
Definition: private.h:108
IMAP command structure.
Definition: private.h:159
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:347
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
User aborted the question (with Ctrl-G)
Definition: quad.h:37
void(* adata_free)(void **ptr)
Free the private data attached to the Account.
Definition: account.h:53
char delim
Definition: adata.h:75
Shared constants/structs that are private to IMAP.
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:71
void * mdata
Driver specific data.
Definition: mailbox.h:136
uint16_t AclFlags
ACL Rights - These show permission to...
Definition: mailbox.h:62
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:74
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
unsigned long long modseq
Definition: mdata.h:52
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1250
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mxapi.h:105
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
char * flags_remote
Definition: edata.h:48
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition: imap.c:1004
int imap_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2416
struct HashTable * uid_hash
Definition: mdata.h:58
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:733
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
Old messages.
Definition: mutt.h:90
Email list needs resorting.
Definition: mailbox.h:181
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount&#39;s password.
Definition: connaccount.c:140
void imap_error(const char *where, const char *msg)
show an error and abort
Definition: util.c:665
struct Connection * mutt_conn_new(const struct ConnAccount *cac)
Create a new Connection.
Definition: mutt_socket.c:48
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:124
bool active
Message is not to be removed.
Definition: email.h:59
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
A local copy of an email.
Definition: mxapi.h:41
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
void imap_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free()
Definition: adata.c:40
unsigned int uid
32-bit Message UID
Definition: edata.h:44
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:782
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:474
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:40
char * user
Username.
Definition: url.h:71
Functions to parse commands in a config file.
struct ListHead flags
Definition: mdata.h:49
int fd
Socket file descriptor.
Definition: connection.h:44
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
char * name
Definition: private.h:150
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
static bool imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: imap.c:2135
Manage where the email is piped to external commands.
int cmdslots
Definition: adata.h:70
No changes.
Definition: mxapi.h:78
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition: hash.c:436
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
Tagged messages.
Definition: mutt.h:99
char * data
Pointer to data.
Definition: buffer.h:35
enum CommandResult parse_subscribe_to(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the &#39;subscribe-to&#39; command - Implements Command::parse() -The &#39;subscribe-to&#39; command allows to ...
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:418
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:927
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1316
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
#define IMAP_CAP_STATUS
Server supports STATUS command.
Definition: private.h:125
Messages that have been read.
Definition: mutt.h:92
Definitions of NeoMutt commands.
bool replied
Definition: edata.h:40
char * name
Mailbox name.
Definition: mdata.h:40
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool old
Definition: edata.h:37
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:282
bool verbose
Display status messages?
Definition: mailbox.h:118
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:59
Ask the user a question.
char * host
Host.
Definition: url.h:73
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1021
bool purge
Skip trash folder when deleting.
Definition: email.h:46
int mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: string.c:343
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1270
ImapCapFlags capabilities
Definition: adata.h:55
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
#define IMAP_OPEN_NO_FLAGS
No flags are set.
Definition: private.h:65
time_t lastread
last time we read a command for the server
Definition: adata.h:58
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:90
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:448
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Login details for a remote server.
Definition: connaccount.h:51
Imap Account.
Definition: mutt_account.h:37
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:97
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
void progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:175
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:180
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1838
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
char * path
Path.
Definition: url.h:75
Connection is idle.
Definition: private.h:113
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition: mdata.h:42
Imap command failure.
Definition: private.h:85
static bool imap_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: imap.c:1762
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1816
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
#define IS_SPACE(ch)
Definition: string2.h:38
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:313
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: message.c:2145
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
use the NOOP or IDLE command to poll for new mail
Definition: imap.c:1103
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
int imap_complete(char *buf, size_t buflen, const char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1343
IMAP-specific Account data -.
Definition: adata.h:39
unsigned int uid_next
Definition: mdata.h:51
static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
Find longest prefix common to two strings.
Definition: imap.c:365
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:158
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:775
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:299
char * data
String.
Definition: list.h:36
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:137
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:131
Move message to another mailbox, removing the original.
Definition: commands.h:52
Log at debug level 1.
Definition: logging.h:40
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:687
bool flagged
Marked important?
Definition: email.h:43
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
IMAP-specific Mailbox data -.
Definition: mdata.h:38
static int imap_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
Prompt and validate new messages tags - Implements MxOps::tags_edit() -.
Definition: imap.c:2250
char * buf
Definition: adata.h:59
int msg_new
Number of new messages.
Definition: mailbox.h:95
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
bool deleted
Email is deleted.
Definition: email.h:45
void * edata
Driver-specific data.
Definition: email.h:111
bool replied
Email has been replied to.
Definition: email.h:54
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
#define IMAP_CAP_QRESYNC
RFC7162.
Definition: private.h:138
Connection Library.
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
FILE * fp
pointer to the message data
Definition: mxapi.h:43
int index
The absolute (unsorted) message number.
Definition: email.h:86
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1668
bool read
Definition: edata.h:36
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:267
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:73
#define STAILQ_EMPTY(head)
Definition: queue.h:348
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition: auth.c:107
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:94
struct ImapList * cmdresult
Definition: adata.h:66
List of Mailboxes.
Definition: mailbox.h:156
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
static int imap_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: imap.c:2463
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
IMAP-specific Email data -.
Definition: edata.h:33
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:186
static enum MxOpenReturns imap_mbox_open(struct Mailbox *m)
Open a mailbox - Implements MxOps::mbox_open() -.
Definition: imap.c:1929
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
NeoMutt connections.
int mx_ac_remove(struct Mailbox *m)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1772
struct Buffer pathbuf
Definition: mailbox.h:83
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
Open failed with an error.
Definition: mxapi.h:91
A List node for strings.
Definition: list.h:34
static int compare_uid(const void *a, const void *b)
Compare two Emails by UID - Implements sort_t.
Definition: imap.c:904
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
char * munge_name
Munged version of the mailbox name.
Definition: mdata.h:41
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1167
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:297
#define IMAP_CAP_IDLE
RFC2177: IDLE.
Definition: private.h:134
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:208
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
Trashed messages.
Definition: mutt.h:104
#define IMAP_CAP_ACL
RFC2086: IMAP4 ACL extension.
Definition: private.h:126
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close() ...
Definition: mxapi.h:75
enum CommandResult parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the &#39;unsubscribe-from&#39; command - Implements Command::parse() -The &#39;unsubscribe-from&#39; command al...
Authentication successful.
Definition: auth.h:39
Log at debug level 3.
Definition: logging.h:42
bool noselect
Definition: private.h:152
#define MUTT_ACL_LOOKUP
Lookup mailbox (visible to &#39;list&#39;)
Definition: mailbox.h:70
unsigned int messages
Definition: mdata.h:53
static int imap_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: imap.c:2451
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:840
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
char * path
path to temp file
Definition: mxapi.h:44
Definition: mxapi.h:103
static int imap_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message - Implements MxOps::tags_commit() -This method update the server flags on ...
Definition: imap.c:2337
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1286
#define IMAP_LOG_LTRL
Definition: private.h:50
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
struct Connection * conn
Definition: adata.h:41
An error occurred.
Definition: mxapi.h:77
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:158
static int imap_path_is_empty(const char *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition: imap.c:2475
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234