NeoMutt  2020-11-20
Teaching an old dog new tricks
DOXYGEN
imap.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <limits.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "private.h"
39 #include "mutt/lib.h"
40 #include "config/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "conn/lib.h"
44 #include "gui/lib.h"
45 #include "mutt.h"
46 #include "lib.h"
47 #include "bcache/lib.h"
48 #include "pattern/lib.h"
49 #include "auth.h"
50 #include "command_parse.h"
51 #include "commands.h"
52 #include "hook.h"
53 #include "init.h"
54 #include "message.h"
55 #include "msn.h"
56 #include "mutt_globals.h"
57 #include "mutt_logging.h"
58 #include "mutt_socket.h"
59 #include "muttlib.h"
60 #include "mx.h"
61 #include "progress.h"
62 #include "sort.h"
63 #ifdef ENABLE_NLS
64 #include <libintl.h>
65 #endif
66 
67 struct stat;
68 
69 static const struct Command imap_commands[] = {
70  // clang-format off
71  { "subscribe-to", parse_subscribe_to, 0 },
72  { "unsubscribe-from", parse_unsubscribe_from, 0 },
73  // clang-format on
74 };
75 
79 void imap_init(void)
80 {
81  COMMANDS_REGISTER(imap_commands);
82 }
83 
90 static int check_capabilities(struct ImapAccountData *adata)
91 {
92  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
93  {
94  imap_error("check_capabilities", adata->buf);
95  return -1;
96  }
97 
98  if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
99  {
100  mutt_error(
101  _("This IMAP server is ancient. NeoMutt does not work with it."));
102  return -1;
103  }
104 
105  return 0;
106 }
107 
117 static char *get_flags(struct ListHead *hflags, char *s)
118 {
119  /* sanity-check string */
120  const size_t plen = mutt_istr_startswith(s, "FLAGS");
121  if (plen == 0)
122  {
123  mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
124  return NULL;
125  }
126  s += plen;
127  SKIPWS(s);
128  if (*s != '(')
129  {
130  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
131  return NULL;
132  }
133 
134  /* update caller's flags handle */
135  while (*s && (*s != ')'))
136  {
137  s++;
138  SKIPWS(s);
139  const char *flag_word = s;
140  while (*s && (*s != ')') && !IS_SPACE(*s))
141  s++;
142  const char ctmp = *s;
143  *s = '\0';
144  if (*flag_word)
145  mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
146  *s = ctmp;
147  }
148 
149  /* note bad flags response */
150  if (*s != ')')
151  {
152  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
153  mutt_list_free(hflags);
154 
155  return NULL;
156  }
157 
158  s++;
159 
160  return s;
161 }
162 
172 static void set_flag(struct Mailbox *m, AclFlags aclflag, int flag,
173  const char *str, char *flags, size_t flsize)
174 {
175  if (m->rights & aclflag)
176  if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
177  mutt_str_cat(flags, flsize, str);
178 }
179 
193 static int make_msg_set(struct Mailbox *m, struct Buffer *buf, int flag,
194  bool changed, bool invert, int *pos)
195 {
196  int count = 0; /* number of messages in message set */
197  unsigned int setstart = 0; /* start of current message range */
198  int n;
199  bool started = false;
200 
201  struct ImapAccountData *adata = imap_adata_get(m);
202  if (!adata || (adata->mailbox != m))
203  return -1;
204 
205  for (n = *pos; (n < m->msg_count) && (mutt_buffer_len(buf) < IMAP_MAX_CMDLEN); n++)
206  {
207  struct Email *e = m->emails[n];
208  if (!e)
209  break;
210  bool match = false; /* whether current message matches flag condition */
211  /* don't include pending expunged messages.
212  *
213  * TODO: can we unset active in cmd_parse_expunge() and
214  * cmd_parse_vanished() instead of checking for index != INT_MAX. */
215  if (e->active && (e->index != INT_MAX))
216  {
217  switch (flag)
218  {
219  case MUTT_DELETED:
220  if (e->deleted != imap_edata_get(e)->deleted)
221  match = invert ^ e->deleted;
222  break;
223  case MUTT_FLAG:
224  if (e->flagged != imap_edata_get(e)->flagged)
225  match = invert ^ e->flagged;
226  break;
227  case MUTT_OLD:
228  if (e->old != imap_edata_get(e)->old)
229  match = invert ^ e->old;
230  break;
231  case MUTT_READ:
232  if (e->read != imap_edata_get(e)->read)
233  match = invert ^ e->read;
234  break;
235  case MUTT_REPLIED:
236  if (e->replied != imap_edata_get(e)->replied)
237  match = invert ^ e->replied;
238  break;
239  case MUTT_TAG:
240  if (e->tagged)
241  match = true;
242  break;
243  case MUTT_TRASH:
244  if (e->deleted && !e->purge)
245  match = true;
246  break;
247  }
248  }
249 
250  if (match && (!changed || e->changed))
251  {
252  count++;
253  if (setstart == 0)
254  {
255  setstart = imap_edata_get(e)->uid;
256  if (started)
257  {
258  mutt_buffer_add_printf(buf, ",%u", imap_edata_get(e)->uid);
259  }
260  else
261  {
262  mutt_buffer_add_printf(buf, "%u", imap_edata_get(e)->uid);
263  started = true;
264  }
265  }
266  /* tie up if the last message also matches */
267  else if (n == (m->msg_count - 1))
268  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(e)->uid);
269  }
270  /* End current set if message doesn't match or we've reached the end
271  * of the mailbox via inactive messages following the last match. */
272  else if (setstart && (e->active || (n == adata->mailbox->msg_count - 1)))
273  {
274  if (imap_edata_get(m->emails[n - 1])->uid > setstart)
275  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(m->emails[n - 1])->uid);
276  setstart = 0;
277  }
278  }
279 
280  *pos = n;
281 
282  return count;
283 }
284 
293 static bool compare_flags_for_copy(struct Email *e)
294 {
295  struct ImapEmailData *edata = e->edata;
296 
297  if (e->read != edata->read)
298  return true;
299  if (e->old != edata->old)
300  return true;
301  if (e->flagged != edata->flagged)
302  return true;
303  if (e->replied != edata->replied)
304  return true;
305 
306  return false;
307 }
308 
318 static int sync_helper(struct Mailbox *m, AclFlags right, int flag, const char *name)
319 {
320  int count = 0;
321  int rc;
322  char buf[1024];
323 
324  if (!m)
325  return -1;
326 
327  if ((m->rights & right) == 0)
328  return 0;
329 
330  if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
331  return 0;
332 
333  snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
334  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, false);
335  if (rc < 0)
336  return rc;
337  count += rc;
338 
339  buf[0] = '-';
340  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, true);
341  if (rc < 0)
342  return rc;
343  count += rc;
344 
345  return count;
346 }
347 
358 static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
359 {
360  size_t pos = start;
361 
362  while ((pos < dlen) && dest[pos] && (dest[pos] == src[pos]))
363  pos++;
364  dest[pos] = '\0';
365 
366  return pos;
367 }
368 
379 static int complete_hosts(char *buf, size_t buflen)
380 {
381  // struct Connection *conn = NULL;
382  int rc = -1;
383  size_t matchlen;
384 
385  matchlen = mutt_str_len(buf);
386  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
388  struct MailboxNode *np = NULL;
389  STAILQ_FOREACH(np, &ml, entries)
390  {
391  if (!mutt_str_startswith(mailbox_path(np->mailbox), buf))
392  continue;
393 
394  if (rc)
395  {
396  mutt_str_copy(buf, mailbox_path(np->mailbox), buflen);
397  rc = 0;
398  }
399  else
400  longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen, buflen);
401  }
403 
404 #if 0
405  TAILQ_FOREACH(conn, mutt_socket_head(), entries)
406  {
407  struct Url url = { 0 };
408  char urlstr[1024];
409 
410  if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
411  continue;
412 
413  mutt_account_tourl(&conn->account, &url);
414  /* FIXME: how to handle multiple users on the same host? */
415  url.user = NULL;
416  url.path = NULL;
417  url_tostring(&url, urlstr, sizeof(urlstr), 0);
418  if (mutt_strn_equal(buf, urlstr, matchlen))
419  {
420  if (rc)
421  {
422  mutt_str_copy(buf, urlstr, buflen);
423  rc = 0;
424  }
425  else
426  longest_common_prefix(buf, urlstr, matchlen, buflen);
427  }
428  }
429 #endif
430 
431  return rc;
432 }
433 
441 int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
442 {
443  char buf[2048], mbox[1024];
444 
445  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
446  snprintf(buf, sizeof(buf), "CREATE %s", mbox);
447 
448  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
449  {
450  mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
451  return -1;
452  }
453 
454  return 0;
455 }
456 
467 int imap_access(const char *path)
468 {
469  if (imap_path_status(path, false) >= 0)
470  return 0;
471  return -1;
472 }
473 
482 int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
483 {
484  char oldmbox[1024];
485  char newmbox[1024];
486  int rc = 0;
487 
488  imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
489  imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
490 
491  struct Buffer *buf = mutt_buffer_pool_get();
492  mutt_buffer_printf(buf, "RENAME %s %s", oldmbox, newmbox);
493 
495  rc = -1;
496 
498 
499  return rc;
500 }
501 
509 int imap_delete_mailbox(struct Mailbox *m, char *path)
510 {
511  char buf[PATH_MAX + 7];
512  char mbox[PATH_MAX];
513  struct Url *url = url_parse(path);
514 
515  struct ImapAccountData *adata = imap_adata_get(m);
516  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
517  url_free(&url);
518  snprintf(buf, sizeof(buf), "DELETE %s", mbox);
520  return -1;
521 
522  return 0;
523 }
524 
529 static void imap_logout(struct ImapAccountData *adata)
530 {
531  /* we set status here to let imap_handle_untagged know we _expect_ to
532  * receive a bye response (so it doesn't freak out and close the conn) */
533  if (adata->state == IMAP_DISCONNECTED)
534  {
535  return;
536  }
537 
538  adata->status = IMAP_BYE;
539  imap_cmd_start(adata, "LOGOUT");
540  if ((C_ImapPollTimeout <= 0) || (mutt_socket_poll(adata->conn, C_ImapPollTimeout) != 0))
541  {
542  while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
543  ; // do nothing
544  }
545  mutt_socket_close(adata->conn);
546  adata->state = IMAP_DISCONNECTED;
547 }
548 
554 void imap_logout_all(void)
555 {
556  struct Account *np = NULL;
557  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
558  {
559  if (np->type != MUTT_IMAP)
560  continue;
561 
562  struct ImapAccountData *adata = np->adata;
563  if (!adata)
564  continue;
565 
566  struct Connection *conn = adata->conn;
567  if (!conn || (conn->fd < 0))
568  continue;
569 
570  mutt_message(_("Closing connection to %s..."), conn->account.host);
571  imap_logout(np->adata);
573  }
574 }
575 
590 int imap_read_literal(FILE *fp, struct ImapAccountData *adata,
591  unsigned long bytes, struct Progress *pbar)
592 {
593  char c;
594  bool r = false;
595  struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
596 
598  mutt_buffer_alloc(&buf, bytes + 10);
599 
600  mutt_debug(LL_DEBUG2, "reading %ld bytes\n", bytes);
601 
602  for (unsigned long pos = 0; pos < bytes; pos++)
603  {
604  if (mutt_socket_readchar(adata->conn, &c) != 1)
605  {
606  mutt_debug(LL_DEBUG1, "error during read, %ld bytes read\n", pos);
607  adata->status = IMAP_FATAL;
608 
609  mutt_buffer_dealloc(&buf);
610  return -1;
611  }
612 
613  if (r && (c != '\n'))
614  fputc('\r', fp);
615 
616  if (c == '\r')
617  {
618  r = true;
619  continue;
620  }
621  else
622  r = false;
623 
624  fputc(c, fp);
625 
626  if (pbar && !(pos % 1024))
627  mutt_progress_update(pbar, pos, -1);
629  mutt_buffer_addch(&buf, c);
630  }
631 
633  {
634  mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
635  mutt_buffer_dealloc(&buf);
636  }
637  return 0;
638 }
639 
645 void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
646 {
647  struct ImapMboxData *mdata = imap_mdata_get(m);
648  struct ImapEmailData *edata = imap_edata_get(e);
649 
650  if (!mdata || !edata)
651  return;
652 
653  imap_msn_remove(&mdata->msn, edata->msn - 1);
654  edata->msn = 0;
655 }
656 
666 {
667  struct ImapAccountData *adata = imap_adata_get(m);
668  struct ImapMboxData *mdata = imap_mdata_get(m);
669  if (!adata || !mdata)
670  return;
671 
672  struct Email *e = NULL;
673 
674 #ifdef USE_HCACHE
675  imap_hcache_open(adata, mdata);
676 #endif
677 
678  for (int i = 0; i < m->msg_count; i++)
679  {
680  e = m->emails[i];
681  if (!e)
682  break;
683 
684  if (e->index == INT_MAX)
685  {
686  mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
687 
688  e->deleted = true;
689 
690  imap_cache_del(m, e);
691 #ifdef USE_HCACHE
692  imap_hcache_del(mdata, imap_edata_get(e)->uid);
693 #endif
694 
695  mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
696 
697  imap_edata_free((void **) &e->edata);
698  }
699  else
700  {
701  /* NeoMutt has several places where it turns off e->active as a
702  * hack. For example to avoid FLAG updates, or to exclude from
703  * imap_exec_msgset.
704  *
705  * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
706  * flag becomes set (e.g. a flag update to a modified header),
707  * this function will be called by imap_cmd_finish().
708  *
709  * The ctx_update_tables() will free and remove these "inactive" headers,
710  * despite that an EXPUNGE was not received for them.
711  * This would result in memory leaks and segfaults due to dangling
712  * pointers in the msn_index and uid_hash.
713  *
714  * So this is another hack to work around the hacks. We don't want to
715  * remove the messages, so make sure active is on. */
716  e->active = true;
717  }
718  }
719 
720 #ifdef USE_HCACHE
721  imap_hcache_close(mdata);
722 #endif
723 
726 }
727 
735 {
736  if (mutt_socket_open(adata->conn) < 0)
737  return -1;
738 
739  adata->state = IMAP_CONNECTED;
740 
741  if (imap_cmd_step(adata) != IMAP_RES_OK)
742  {
743  imap_close_connection(adata);
744  return -1;
745  }
746 
747  if (mutt_istr_startswith(adata->buf, "* OK"))
748  {
749  if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
750  {
751  goto bail;
752  }
753 #ifdef USE_SSL
754  /* Attempt STARTTLS if available and desired. */
755  if ((adata->conn->ssf == 0) && (C_SslForceTls || (adata->capabilities & IMAP_CAP_STARTTLS)))
756  {
757  enum QuadOption ans;
758 
759  if (C_SslForceTls)
760  ans = MUTT_YES;
761  else if ((ans = query_quadoption(C_SslStarttls,
762  _("Secure connection with TLS?"))) == MUTT_ABORT)
763  {
764  goto bail;
765  }
766  if (ans == MUTT_YES)
767  {
768  enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
769  // Clear any data after the STARTTLS acknowledgement
770  mutt_socket_empty(adata->conn);
771 
772  if (rc == IMAP_EXEC_FATAL)
773  goto bail;
774  if (rc != IMAP_EXEC_ERROR)
775  {
776  if (mutt_ssl_starttls(adata->conn))
777  {
778  mutt_error(_("Could not negotiate TLS connection"));
779  goto bail;
780  }
781  else
782  {
783  /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
784  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS))
785  goto bail;
786  }
787  }
788  }
789  }
790 
791  if (C_SslForceTls && (adata->conn->ssf == 0))
792  {
793  mutt_error(_("Encrypted connection unavailable"));
794  goto bail;
795  }
796 #endif
797  }
798  else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
799  {
800 #ifdef USE_SSL
801  /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
802  * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
803  * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
804  * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
805  * decide whether to abort. Note that if using $tunnel and
806  * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
807  if ((adata->conn->ssf == 0) && C_SslForceTls)
808  {
809  mutt_error(_("Encrypted connection unavailable"));
810  goto bail;
811  }
812 #endif
813 
814  adata->state = IMAP_AUTHENTICATED;
815  if (check_capabilities(adata) != 0)
816  goto bail;
817  FREE(&adata->capstr);
818  }
819  else
820  {
821  imap_error("imap_open_connection()", adata->buf);
822  goto bail;
823  }
824 
825  return 0;
826 
827 bail:
828  imap_close_connection(adata);
829  FREE(&adata->capstr);
830  return -1;
831 }
832 
838 {
839  if (adata->state != IMAP_DISCONNECTED)
840  {
841  mutt_socket_close(adata->conn);
842  adata->state = IMAP_DISCONNECTED;
843  }
844  adata->seqno = 0;
845  adata->nextcmd = 0;
846  adata->lastcmd = 0;
847  adata->status = 0;
848  memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
849 }
850 
862 bool imap_has_flag(struct ListHead *flag_list, const char *flag)
863 {
864  if (STAILQ_EMPTY(flag_list))
865  return false;
866 
867  const size_t flaglen = mutt_str_len(flag);
868  struct ListNode *np = NULL;
869  STAILQ_FOREACH(np, flag_list, entries)
870  {
871  const size_t nplen = strlen(np->data);
872  if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
873  mutt_istrn_equal(np->data, flag, nplen))
874  {
875  return true;
876  }
877 
878  if (mutt_str_equal(np->data, "\\*"))
879  return true;
880  }
881 
882  return false;
883 }
884 
888 static int compare_uid(const void *a, const void *b)
889 {
890  const struct Email *ea = *(struct Email const *const *) a;
891  const struct Email *eb = *(struct Email const *const *) b;
892  return imap_edata_get((struct Email *) ea)->uid -
893  imap_edata_get((struct Email *) eb)->uid;
894 }
895 
911 int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post,
912  int flag, bool changed, bool invert)
913 {
914  struct ImapAccountData *adata = imap_adata_get(m);
915  if (!adata || (adata->mailbox != m))
916  return -1;
917 
918  struct Email **emails = NULL;
919  short oldsort;
920  int pos;
921  int rc;
922  int count = 0;
923 
924  struct Buffer cmd = mutt_buffer_make(0);
925 
926  /* We make a copy of the headers just in case resorting doesn't give
927  exactly the original order (duplicate messages?), because other parts of
928  the ctx are tied to the header order. This may be overkill. */
929  oldsort = C_Sort;
930  if (C_Sort != SORT_ORDER)
931  {
932  emails = m->emails;
933  // We overcommit here, just in case new mail arrives whilst we're sync-ing
934  m->emails = mutt_mem_malloc(m->email_max * sizeof(struct Email *));
935  memcpy(m->emails, emails, m->email_max * sizeof(struct Email *));
936 
937  C_Sort = SORT_ORDER;
938  qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
939  }
940 
941  pos = 0;
942 
943  do
944  {
945  mutt_buffer_reset(&cmd);
946  mutt_buffer_add_printf(&cmd, "%s ", pre);
947  rc = make_msg_set(m, &cmd, flag, changed, invert, &pos);
948  if (rc > 0)
949  {
950  mutt_buffer_add_printf(&cmd, " %s", post);
951  if (imap_exec(adata, cmd.data, IMAP_CMD_QUEUE) != IMAP_EXEC_SUCCESS)
952  {
953  rc = -1;
954  goto out;
955  }
956  count += rc;
957  }
958  } while (rc > 0);
959 
960  rc = count;
961 
962 out:
963  mutt_buffer_dealloc(&cmd);
964  if (oldsort != C_Sort)
965  {
966  C_Sort = oldsort;
967  FREE(&m->emails);
968  m->emails = emails;
969  }
970 
971  return rc;
972 }
973 
989 int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e,
990  struct Buffer *cmd, enum QuadOption *err_continue)
991 {
992  struct ImapAccountData *adata = imap_adata_get(m);
993  if (!adata || (adata->mailbox != m))
994  return -1;
995 
996  char flags[1024];
997  char *tags = NULL;
998  char uid[11];
999 
1000  if (!compare_flags_for_copy(e))
1001  {
1002  if (e->deleted == imap_edata_get(e)->deleted)
1003  e->changed = false;
1004  return 0;
1005  }
1006 
1007  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
1008  mutt_buffer_reset(cmd);
1009  mutt_buffer_addstr(cmd, "UID STORE ");
1010  mutt_buffer_addstr(cmd, uid);
1011 
1012  flags[0] = '\0';
1013 
1014  set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
1015  set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
1016  set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
1017  set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
1018  set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
1019  sizeof(flags));
1020 
1021  if (m->rights & MUTT_ACL_WRITE)
1022  {
1023  /* restore system flags */
1024  if (imap_edata_get(e)->flags_system)
1025  mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
1026  /* set custom flags */
1027  tags = driver_tags_get_with_hidden(&e->tags);
1028  if (tags)
1029  {
1030  mutt_str_cat(flags, sizeof(flags), tags);
1031  FREE(&tags);
1032  }
1033  }
1034 
1036 
1037  /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1038  * explicitly revoke all system flags (if we have permission) */
1039  if (*flags == '\0')
1040  {
1041  set_flag(m, MUTT_ACL_SEEN, 1, "\\Seen ", flags, sizeof(flags));
1042  set_flag(m, MUTT_ACL_WRITE, 1, "Old ", flags, sizeof(flags));
1043  set_flag(m, MUTT_ACL_WRITE, 1, "\\Flagged ", flags, sizeof(flags));
1044  set_flag(m, MUTT_ACL_WRITE, 1, "\\Answered ", flags, sizeof(flags));
1045  set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
1046  flags, sizeof(flags));
1047 
1048  /* erase custom flags */
1049  if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
1050  mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
1051 
1053 
1054  mutt_buffer_addstr(cmd, " -FLAGS.SILENT (");
1055  }
1056  else
1057  mutt_buffer_addstr(cmd, " FLAGS.SILENT (");
1058 
1059  mutt_buffer_addstr(cmd, flags);
1060  mutt_buffer_addstr(cmd, ")");
1061 
1062  /* after all this it's still possible to have no flags, if you
1063  * have no ACL rights */
1064  if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
1065  err_continue && (*err_continue != MUTT_YES))
1066  {
1067  *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1068  if (*err_continue != MUTT_YES)
1069  return -1;
1070  }
1071 
1072  /* server have now the updated flags */
1073  FREE(&imap_edata_get(e)->flags_remote);
1075 
1076  if (e->deleted == imap_edata_get(e)->deleted)
1077  e->changed = false;
1078 
1079  return 0;
1080 }
1081 
1091 int imap_check_mailbox(struct Mailbox *m, bool force)
1092 {
1093  if (!m || !m->account)
1094  return -1;
1095 
1096  struct ImapAccountData *adata = imap_adata_get(m);
1097  struct ImapMboxData *mdata = imap_mdata_get(m);
1098 
1099  /* overload keyboard timeout to avoid many mailbox checks in a row.
1100  * Most users don't like having to wait exactly when they press a key. */
1101  int rc = 0;
1102 
1103  /* try IDLE first, unless force is set */
1104  if (!force && C_ImapIdle && (adata->capabilities & IMAP_CAP_IDLE) &&
1105  ((adata->state != IMAP_IDLE) || (mutt_date_epoch() >= adata->lastread + C_ImapKeepalive)))
1106  {
1107  if (imap_cmd_idle(adata) < 0)
1108  return -1;
1109  }
1110  if (adata->state == IMAP_IDLE)
1111  {
1112  while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1113  {
1114  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1115  {
1116  mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1117  return -1;
1118  }
1119  }
1120  if (rc < 0)
1121  {
1122  mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1123  adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1124  }
1125  }
1126 
1127  if ((force || ((adata->state != IMAP_IDLE) &&
1128  (mutt_date_epoch() >= adata->lastread + C_Timeout))) &&
1129  (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1130  {
1131  return -1;
1132  }
1133 
1134  /* We call this even when we haven't run NOOP in case we have pending
1135  * changes to process, since we can reopen here. */
1136  imap_cmd_finish(adata);
1137 
1138  if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1139  rc = MUTT_REOPENED;
1140  else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1141  rc = MUTT_NEW_MAIL;
1142  else if (mdata->check_status & IMAP_FLAGS_PENDING)
1143  rc = MUTT_FLAGS;
1144 
1146 
1147  return rc;
1148 }
1149 
1157 static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
1158 {
1159  char *uidvalidity_flag = NULL;
1160  char cmd[2048];
1161 
1162  if (!adata || !mdata)
1163  return -1;
1164 
1165  /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1166  * IDLEd elsewhere.
1167  * adata->mailbox may be NULL for connections other than the current
1168  * mailbox's. */
1169  if (adata->mailbox && (adata->mailbox->mdata == mdata))
1170  {
1171  adata->mailbox->has_new = false;
1172  return mdata->messages;
1173  }
1174 
1175  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1176  uidvalidity_flag = "UIDVALIDITY";
1177  else if (adata->capabilities & IMAP_CAP_STATUS)
1178  uidvalidity_flag = "UID-VALIDITY";
1179  else
1180  {
1181  mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1182  return -1;
1183  }
1184 
1185  snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1186  mdata->munge_name, uidvalidity_flag);
1187 
1188  int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_NO_FLAGS | IMAP_CMD_POLL);
1189  if (rc < 0)
1190  {
1191  mutt_debug(LL_DEBUG1, "Error queueing command\n");
1192  return rc;
1193  }
1194  return mdata->messages;
1195 }
1196 
1200 static int imap_mbox_check_stats(struct Mailbox *m, int flags)
1201 {
1202  return imap_mailbox_status(m, true);
1203 }
1204 
1211 int imap_path_status(const char *path, bool queue)
1212 {
1213  struct Mailbox *m = mx_mbox_find2(path);
1214 
1215  const bool is_temp = !m;
1216  if (is_temp)
1217  {
1218  m = mx_path_resolve(path);
1219  if (!mx_mbox_ac_link(m))
1220  {
1221  mailbox_free(&m);
1222  return 0;
1223  }
1224  }
1225 
1226  int rc = imap_mailbox_status(m, queue);
1227 
1228  if (is_temp)
1229  {
1230  mx_ac_remove(m);
1231  }
1232 
1233  return rc;
1234 }
1235 
1245 int imap_mailbox_status(struct Mailbox *m, bool queue)
1246 {
1247  struct ImapAccountData *adata = imap_adata_get(m);
1248  struct ImapMboxData *mdata = imap_mdata_get(m);
1249  if (!adata || !mdata)
1250  return -1;
1251  return imap_status(adata, mdata, queue);
1252 }
1253 
1261 int imap_subscribe(char *path, bool subscribe)
1262 {
1263  struct ImapAccountData *adata = NULL;
1264  struct ImapMboxData *mdata = NULL;
1265  char buf[2048];
1266  struct Buffer err;
1267 
1268  if (imap_adata_find(path, &adata, &mdata) < 0)
1269  return -1;
1270 
1271  if (subscribe)
1272  mutt_message(_("Subscribing to %s..."), mdata->name);
1273  else
1274  mutt_message(_("Unsubscribing from %s..."), mdata->name);
1275 
1276  snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1277 
1278  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1279  {
1280  imap_mdata_free((void *) &mdata);
1281  return -1;
1282  }
1283 
1285  {
1286  char mbox[1024];
1287  mutt_buffer_init(&err);
1288  err.dsize = 256;
1289  err.data = mutt_mem_malloc(err.dsize);
1290  size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1291  imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1292  if (mutt_parse_rc_line(mbox, &err))
1293  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
1294  FREE(&err.data);
1295  }
1296 
1297  if (subscribe)
1298  mutt_message(_("Subscribed to %s"), mdata->name);
1299  else
1300  mutt_message(_("Unsubscribed from %s"), mdata->name);
1301  imap_mdata_free((void *) &mdata);
1302  return 0;
1303 }
1304 
1316 int imap_complete(char *buf, size_t buflen, const char *path)
1317 {
1318  struct ImapAccountData *adata = NULL;
1319  struct ImapMboxData *mdata = NULL;
1320  char tmp[2048];
1321  struct ImapList listresp = { 0 };
1322  char completion[1024];
1323  int clen;
1324  size_t matchlen = 0;
1325  int completions = 0;
1326  int rc;
1327 
1328  if (imap_adata_find(path, &adata, &mdata) < 0)
1329  {
1330  mutt_str_copy(buf, path, buflen);
1331  return complete_hosts(buf, buflen);
1332  }
1333 
1334  /* fire off command */
1335  snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1336  C_ImapListSubscribed ? "LSUB" : "LIST", mdata->real_name);
1337 
1338  imap_cmd_start(adata, tmp);
1339 
1340  /* and see what the results are */
1341  mutt_str_copy(completion, mdata->name, sizeof(completion));
1342  imap_mdata_free((void *) &mdata);
1343 
1344  adata->cmdresult = &listresp;
1345  do
1346  {
1347  listresp.name = NULL;
1348  rc = imap_cmd_step(adata);
1349 
1350  if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1351  {
1352  /* if the folder isn't selectable, append delimiter to force browse
1353  * to enter it on second tab. */
1354  if (listresp.noselect)
1355  {
1356  clen = strlen(listresp.name);
1357  listresp.name[clen++] = listresp.delim;
1358  listresp.name[clen] = '\0';
1359  }
1360  /* copy in first word */
1361  if (!completions)
1362  {
1363  mutt_str_copy(completion, listresp.name, sizeof(completion));
1364  matchlen = strlen(completion);
1365  completions++;
1366  continue;
1367  }
1368 
1369  matchlen = longest_common_prefix(completion, listresp.name, 0, matchlen);
1370  completions++;
1371  }
1372  } while (rc == IMAP_RES_CONTINUE);
1373  adata->cmdresult = NULL;
1374 
1375  if (completions)
1376  {
1377  /* reformat output */
1378  imap_qualify_path(buf, buflen, &adata->conn->account, completion);
1379  mutt_pretty_mailbox(buf, buflen);
1380  return 0;
1381  }
1382 
1383  return -1;
1384 }
1385 
1394 int imap_fast_trash(struct Mailbox *m, char *dest)
1395 {
1396  char prompt[1024];
1397  int rc = -1;
1398  bool triedcreate = false;
1399  enum QuadOption err_continue = MUTT_NO;
1400 
1401  struct ImapAccountData *adata = imap_adata_get(m);
1402  struct ImapAccountData *dest_adata = NULL;
1403  struct ImapMboxData *dest_mdata = NULL;
1404 
1405  if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1406  return -1;
1407 
1408  struct Buffer sync_cmd = mutt_buffer_make(0);
1409 
1410  /* check that the save-to folder is in the same account */
1411  if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1412  {
1413  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1414  goto out;
1415  }
1416 
1417  for (int i = 0; i < m->msg_count; i++)
1418  {
1419  struct Email *e = m->emails[i];
1420  if (!e)
1421  break;
1422  if (e->active && e->changed && e->deleted && !e->purge)
1423  {
1424  rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1425  if (rc < 0)
1426  {
1427  mutt_debug(LL_DEBUG1, "could not sync\n");
1428  goto out;
1429  }
1430  }
1431  }
1432 
1433  /* loop in case of TRYCREATE */
1434  do
1435  {
1436  rc = imap_exec_msgset(m, "UID COPY", dest_mdata->munge_name, MUTT_TRASH, false, false);
1437  if (rc == 0)
1438  {
1439  mutt_debug(LL_DEBUG1, "No messages to trash\n");
1440  rc = -1;
1441  goto out;
1442  }
1443  else if (rc < 0)
1444  {
1445  mutt_debug(LL_DEBUG1, "could not queue copy\n");
1446  goto out;
1447  }
1448  else if (m->verbose)
1449  {
1450  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1451  rc, dest_mdata->name);
1452  }
1453 
1454  /* let's get it on */
1455  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1456  if (rc == IMAP_EXEC_ERROR)
1457  {
1458  if (triedcreate)
1459  {
1460  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1461  break;
1462  }
1463  /* bail out if command failed for reasons other than nonexistent target */
1464  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1465  break;
1466  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1467  snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1468  if (C_Confirmcreate && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1469  {
1470  mutt_clear_error();
1471  goto out;
1472  }
1473  if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1474  break;
1475  triedcreate = true;
1476  }
1477  } while (rc == IMAP_EXEC_ERROR);
1478 
1479  if (rc != IMAP_EXEC_SUCCESS)
1480  {
1481  imap_error("imap_fast_trash", adata->buf);
1482  goto out;
1483  }
1484 
1485  rc = IMAP_EXEC_SUCCESS;
1486 
1487 out:
1488  mutt_buffer_dealloc(&sync_cmd);
1489  imap_mdata_free((void *) &dest_mdata);
1490 
1491  return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1492 }
1493 
1506 int imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
1507 {
1508  if (!m)
1509  return -1;
1510 
1511  struct Email **emails = NULL;
1512  int oldsort;
1513  int rc;
1514  int check;
1515 
1516  struct ImapAccountData *adata = imap_adata_get(m);
1517  struct ImapMboxData *mdata = imap_mdata_get(m);
1518 
1519  if (adata->state < IMAP_SELECTED)
1520  {
1521  mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1522  return -1;
1523  }
1524 
1525  /* This function is only called when the calling code expects the context
1526  * to be changed. */
1527  imap_allow_reopen(m);
1528 
1529  check = imap_check_mailbox(m, false);
1530  if (check < 0)
1531  return check;
1532 
1533  /* if we are expunging anyway, we can do deleted messages very quickly... */
1534  if (expunge && (m->rights & MUTT_ACL_DELETE))
1535  {
1536  rc = imap_exec_msgset(m, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
1537  MUTT_DELETED, true, false);
1538  if (rc < 0)
1539  {
1540  mutt_error(_("Expunge failed"));
1541  return rc;
1542  }
1543 
1544  if (rc > 0)
1545  {
1546  /* mark these messages as unchanged so second pass ignores them. Done
1547  * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1548  for (int i = 0; i < m->msg_count; i++)
1549  {
1550  struct Email *e = m->emails[i];
1551  if (!e)
1552  break;
1553  if (e->deleted && e->changed)
1554  e->active = false;
1555  }
1556  if (m->verbose)
1557  {
1558  mutt_message(ngettext("Marking %d message deleted...",
1559  "Marking %d messages deleted...", rc),
1560  rc);
1561  }
1562  }
1563  }
1564 
1565 #ifdef USE_HCACHE
1566  imap_hcache_open(adata, mdata);
1567 #endif
1568 
1569  /* save messages with real (non-flag) changes */
1570  for (int i = 0; i < m->msg_count; i++)
1571  {
1572  struct Email *e = m->emails[i];
1573  if (!e)
1574  break;
1575 
1576  if (e->deleted)
1577  {
1578  imap_cache_del(m, e);
1579 #ifdef USE_HCACHE
1580  imap_hcache_del(mdata, imap_edata_get(e)->uid);
1581 #endif
1582  }
1583 
1584  if (e->active && e->changed)
1585  {
1586 #ifdef USE_HCACHE
1587  imap_hcache_put(mdata, e);
1588 #endif
1589  /* if the message has been rethreaded or attachments have been deleted
1590  * we delete the message and reupload it.
1591  * This works better if we're expunging, of course. */
1592  /* TODO: why the e->env check? */
1593  if ((e->env && e->env->changed) || e->attach_del)
1594  {
1595  /* L10N: The plural is chosen by the last %d, i.e. the total number */
1596  if (m->verbose)
1597  {
1598  mutt_message(ngettext("Saving changed message... [%d/%d]",
1599  "Saving changed messages... [%d/%d]", m->msg_count),
1600  i + 1, m->msg_count);
1601  }
1602  bool save_append = m->append;
1603  m->append = true;
1604  mutt_save_message_ctx(e, true, false, false, m);
1605  m->append = save_append;
1606  /* TODO: why the check for e->env? Is this possible? */
1607  if (e->env)
1608  e->env->changed = 0;
1609  }
1610  }
1611  }
1612 
1613 #ifdef USE_HCACHE
1614  imap_hcache_close(mdata);
1615 #endif
1616 
1617  /* presort here to avoid doing 10 resorts in imap_exec_msgset */
1618  oldsort = C_Sort;
1619  if (C_Sort != SORT_ORDER)
1620  {
1621  emails = m->emails;
1622  m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1623  memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));
1624 
1625  C_Sort = SORT_ORDER;
1626  qsort(m->emails, m->msg_count, sizeof(struct Email *), mutt_get_sort_func(SORT_ORDER));
1627  }
1628 
1629  rc = sync_helper(m, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1630  if (rc >= 0)
1631  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1632  if (rc >= 0)
1633  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1634  if (rc >= 0)
1635  rc |= sync_helper(m, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1636  if (rc >= 0)
1637  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1638 
1639  if (oldsort != C_Sort)
1640  {
1641  C_Sort = oldsort;
1642  FREE(&m->emails);
1643  m->emails = emails;
1644  }
1645 
1646  /* Flush the queued flags if any were changed in sync_helper. */
1647  if (rc > 0)
1648  if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1649  rc = -1;
1650 
1651  if (rc < 0)
1652  {
1653  if (close)
1654  {
1655  if (mutt_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1656  {
1657  adata->state = IMAP_AUTHENTICATED;
1658  return 0;
1659  }
1660  }
1661  else
1662  mutt_error(_("Error saving flags"));
1663  return -1;
1664  }
1665 
1666  /* Update local record of server state to reflect the synchronization just
1667  * completed. imap_read_headers always overwrites hcache-origin flags, so
1668  * there is no need to mutate the hcache after flag-only changes. */
1669  for (int i = 0; i < m->msg_count; i++)
1670  {
1671  struct Email *e = m->emails[i];
1672  if (!e)
1673  break;
1674  struct ImapEmailData *edata = imap_edata_get(e);
1675  edata->deleted = e->deleted;
1676  edata->flagged = e->flagged;
1677  edata->old = e->old;
1678  edata->read = e->read;
1679  edata->replied = e->replied;
1680  e->changed = false;
1681  }
1682  m->changed = false;
1683 
1684  /* We must send an EXPUNGE command if we're not closing. */
1685  if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1686  {
1687  if (m->verbose)
1688  mutt_message(_("Expunging messages from server..."));
1689  /* Set expunge bit so we don't get spurious reopened messages */
1690  mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1691  if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1692  {
1693  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1694  imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1695  return -1;
1696  }
1697  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1698  }
1699 
1700  if (expunge && close)
1701  {
1702  adata->closing = true;
1703  imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
1704  adata->state = IMAP_AUTHENTICATED;
1705  }
1706 
1707  if (C_MessageCacheClean)
1708  imap_cache_clean(m);
1709 
1710  return check;
1711 }
1712 
1716 static struct Account *imap_ac_find(struct Account *a, const char *path)
1717 {
1718  struct Url *url = url_parse(path);
1719  if (!url)
1720  return NULL;
1721 
1722  struct ImapAccountData *adata = a->adata;
1723  struct ConnAccount *cac = &adata->conn->account;
1724 
1725  if (!mutt_istr_equal(url->host, cac->host))
1726  a = NULL;
1727  else if (url->user && !mutt_istr_equal(url->user, cac->user))
1728  a = NULL;
1729 
1730  url_free(&url);
1731  return a;
1732 }
1733 
1737 static int imap_ac_add(struct Account *a, struct Mailbox *m)
1738 {
1739  struct ImapAccountData *adata = a->adata;
1740 
1741  if (!adata)
1742  {
1743  struct ConnAccount cac = { { 0 } };
1744  char mailbox[PATH_MAX];
1745 
1746  if (imap_parse_path(mailbox_path(m), &cac, mailbox, sizeof(mailbox)) < 0)
1747  return -1;
1748 
1749  adata = imap_adata_new(a);
1750  adata->conn = mutt_conn_new(&cac);
1751  if (!adata->conn)
1752  {
1753  imap_adata_free((void **) &adata);
1754  return -1;
1755  }
1756 
1758 
1759  if (imap_login(adata) < 0)
1760  {
1761  imap_adata_free((void **) &adata);
1762  return -1;
1763  }
1764 
1765  a->adata = adata;
1767  }
1768 
1769  if (!m->mdata)
1770  {
1771  struct Url *url = url_parse(mailbox_path(m));
1772  struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);
1773 
1774  /* fixup path and realpath, mainly to replace / by /INBOX */
1775  char buf[1024];
1776  imap_qualify_path(buf, sizeof(buf), &adata->conn->account, mdata->name);
1777  mutt_buffer_strcpy(&m->pathbuf, buf);
1779 
1780  m->mdata = mdata;
1782  url_free(&url);
1783  }
1784  return 0;
1785 }
1786 
1791 static void imap_mbox_select(struct Mailbox *m)
1792 {
1793  struct ImapAccountData *adata = imap_adata_get(m);
1794  struct ImapMboxData *mdata = imap_mdata_get(m);
1795  if (!adata || !mdata)
1796  return;
1797 
1798  const char *condstore = NULL;
1799 #ifdef USE_HCACHE
1801  condstore = " (CONDSTORE)";
1802  else
1803 #endif
1804  condstore = "";
1805 
1806  char buf[PATH_MAX];
1807  snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1808  mdata->munge_name, condstore);
1809 
1810  adata->state = IMAP_SELECTED;
1811 
1812  imap_cmd_start(adata, buf);
1813 }
1814 
1823 int imap_login(struct ImapAccountData *adata)
1824 {
1825  if (!adata)
1826  return -1;
1827 
1828  if (adata->state == IMAP_DISCONNECTED)
1829  {
1830  mutt_buffer_reset(&adata->cmdbuf); // purge outstanding queued commands
1831  imap_open_connection(adata);
1832  }
1833  if (adata->state == IMAP_CONNECTED)
1834  {
1835  if (imap_authenticate(adata) == IMAP_AUTH_SUCCESS)
1836  {
1837  adata->state = IMAP_AUTHENTICATED;
1838  FREE(&adata->capstr);
1839  if (adata->conn->ssf != 0)
1840  {
1841  mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1842  adata->conn->ssf);
1843  }
1844  }
1845  else
1847  }
1848  if (adata->state == IMAP_AUTHENTICATED)
1849  {
1850  /* capabilities may have changed */
1851  imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1852 
1853 #ifdef USE_ZLIB
1854  /* RFC4978 */
1855  if ((adata->capabilities & IMAP_CAP_COMPRESS) && C_ImapDeflate &&
1856  (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1857  {
1858  mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1859  adata->conn->account.host);
1860  mutt_zstrm_wrap_conn(adata->conn);
1861  }
1862 #endif
1863 
1864  /* enable RFC6855, if the server supports that */
1865  if (C_ImapRfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1866  imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1867 
1868  /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1869  * is supported (even if not advertised), so flip that bit. */
1870  if (adata->capabilities & IMAP_CAP_QRESYNC)
1871  {
1872  adata->capabilities |= IMAP_CAP_CONDSTORE;
1874  imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1875  }
1876 
1877  /* get root delimiter, '/' as default */
1878  adata->delim = '/';
1879  imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1880 
1881  /* we may need the root delimiter before we open a mailbox */
1882  imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1883 
1884  /* select the mailbox that used to be open before disconnect */
1885  if (adata->mailbox)
1886  {
1887  imap_mbox_select(adata->mailbox);
1888  }
1889  }
1890 
1891  if (adata->state < IMAP_AUTHENTICATED)
1892  return -1;
1893 
1894  return 0;
1895 }
1896 
1900 static int imap_mbox_open(struct Mailbox *m)
1901 {
1902  if (!m->account || !m->mdata)
1903  return -1;
1904 
1905  char buf[PATH_MAX];
1906  int count = 0;
1907  int rc;
1908 
1909  struct ImapAccountData *adata = imap_adata_get(m);
1910  struct ImapMboxData *mdata = imap_mdata_get(m);
1911 
1912  mutt_debug(LL_DEBUG3, "opening %s, saving %s\n", m->pathbuf.data,
1913  (adata->mailbox ? adata->mailbox->pathbuf.data : "(none)"));
1914  adata->prev_mailbox = adata->mailbox;
1915  adata->mailbox = m;
1916 
1917  /* clear mailbox status */
1918  adata->status = 0;
1919  m->rights = 0;
1920  mdata->new_mail_count = 0;
1921 
1922  if (m->verbose)
1923  mutt_message(_("Selecting %s..."), mdata->name);
1924 
1925  /* pipeline ACL test */
1926  if (adata->capabilities & IMAP_CAP_ACL)
1927  {
1928  snprintf(buf, sizeof(buf), "MYRIGHTS %s", mdata->munge_name);
1929  imap_exec(adata, buf, IMAP_CMD_QUEUE);
1930  }
1931  /* assume we have all rights if ACL is unavailable */
1932  else
1933  {
1936  }
1937 
1938  /* pipeline the postponed count if possible */
1939  struct Mailbox *m_postponed = mx_mbox_find2(C_Postponed);
1940  struct ImapAccountData *postponed_adata = imap_adata_get(m_postponed);
1941  if (postponed_adata &&
1942  imap_account_match(&postponed_adata->conn->account, &adata->conn->account))
1943  {
1944  imap_mailbox_status(m_postponed, true);
1945  }
1946 
1948  imap_exec(adata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
1949 
1950  imap_mbox_select(m);
1951 
1952  do
1953  {
1954  char *pc = NULL;
1955 
1956  rc = imap_cmd_step(adata);
1957  if (rc != IMAP_RES_CONTINUE)
1958  break;
1959 
1960  pc = adata->buf + 2;
1961 
1962  /* Obtain list of available flags here, may be overridden by a
1963  * PERMANENTFLAGS tag in the OK response */
1964  if (mutt_istr_startswith(pc, "FLAGS"))
1965  {
1966  /* don't override PERMANENTFLAGS */
1967  if (STAILQ_EMPTY(&mdata->flags))
1968  {
1969  mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
1970  pc = get_flags(&mdata->flags, pc);
1971  if (!pc)
1972  goto fail;
1973  }
1974  }
1975  /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
1976  else if (mutt_istr_startswith(pc, "OK [PERMANENTFLAGS"))
1977  {
1978  mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
1979  /* safe to call on NULL */
1980  mutt_list_free(&mdata->flags);
1981  /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
1982  pc += 13;
1983  pc = get_flags(&(mdata->flags), pc);
1984  if (!pc)
1985  goto fail;
1986  }
1987  /* save UIDVALIDITY for the header cache */
1988  else if (mutt_istr_startswith(pc, "OK [UIDVALIDITY"))
1989  {
1990  mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
1991  pc += 3;
1992  pc = imap_next_word(pc);
1993  if (mutt_str_atoui(pc, &mdata->uidvalidity) < 0)
1994  goto fail;
1995  }
1996  else if (mutt_istr_startswith(pc, "OK [UIDNEXT"))
1997  {
1998  mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
1999  pc += 3;
2000  pc = imap_next_word(pc);
2001  if (mutt_str_atoui(pc, &mdata->uid_next) < 0)
2002  goto fail;
2003  }
2004  else if (mutt_istr_startswith(pc, "OK [HIGHESTMODSEQ"))
2005  {
2006  mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
2007  pc += 3;
2008  pc = imap_next_word(pc);
2009  if (mutt_str_atoull(pc, &mdata->modseq) < 0)
2010  goto fail;
2011  }
2012  else if (mutt_istr_startswith(pc, "OK [NOMODSEQ"))
2013  {
2014  mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
2015  mdata->modseq = 0;
2016  }
2017  else
2018  {
2019  pc = imap_next_word(pc);
2020  if (mutt_istr_startswith(pc, "EXISTS"))
2021  {
2022  count = mdata->new_mail_count;
2023  mdata->new_mail_count = 0;
2024  }
2025  }
2026  } while (rc == IMAP_RES_CONTINUE);
2027 
2028  if (rc == IMAP_RES_NO)
2029  {
2030  char *s = imap_next_word(adata->buf); /* skip seq */
2031  s = imap_next_word(s); /* Skip response */
2032  mutt_error("%s", s);
2033  goto fail;
2034  }
2035 
2036  if (rc != IMAP_RES_OK)
2037  goto fail;
2038 
2039  /* check for READ-ONLY notification */
2040  if (mutt_istr_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]") &&
2041  !(adata->capabilities & IMAP_CAP_ACL))
2042  {
2043  mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2044  m->readonly = true;
2045  }
2046 
2047  /* dump the mailbox flags we've found */
2048  if (C_DebugLevel > LL_DEBUG2)
2049  {
2050  if (STAILQ_EMPTY(&mdata->flags))
2051  mutt_debug(LL_DEBUG3, "No folder flags found\n");
2052  else
2053  {
2054  struct ListNode *np = NULL;
2055  struct Buffer flag_buffer;
2056  mutt_buffer_init(&flag_buffer);
2057  mutt_buffer_printf(&flag_buffer, "Mailbox flags: ");
2058  STAILQ_FOREACH(np, &mdata->flags, entries)
2059  {
2060  mutt_buffer_add_printf(&flag_buffer, "[%s] ", np->data);
2061  }
2062  mutt_debug(LL_DEBUG3, "%s\n", flag_buffer.data);
2063  FREE(&flag_buffer.data);
2064  }
2065  }
2066 
2067  if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2068  (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2069  {
2070  m->readonly = true;
2071  }
2072 
2073  while (m->email_max < count)
2074  mx_alloc_memory(m);
2075 
2076  m->msg_count = 0;
2077  m->msg_unread = 0;
2078  m->msg_flagged = 0;
2079  m->msg_new = 0;
2080  m->msg_deleted = 0;
2081  m->size = 0;
2082  m->vcount = 0;
2083 
2084  if (count && (imap_read_headers(m, 1, count, true) < 0))
2085  {
2086  mutt_error(_("Error opening mailbox"));
2087  goto fail;
2088  }
2089 
2090  mutt_debug(LL_DEBUG2, "msg_count is %d\n", m->msg_count);
2091  return 0;
2092 
2093 fail:
2094  if (adata->state == IMAP_SELECTED)
2095  adata->state = IMAP_AUTHENTICATED;
2096  return -1;
2097 }
2098 
2102 static int imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
2103 {
2104  if (!m->account)
2105  return -1;
2106 
2107  /* in APPEND mode, we appear to hijack an existing IMAP connection -
2108  * ctx is brand new and mostly empty */
2109  struct ImapAccountData *adata = imap_adata_get(m);
2110  struct ImapMboxData *mdata = imap_mdata_get(m);
2111 
2112  int rc = imap_mailbox_status(m, false);
2113  if (rc >= 0)
2114  return 0;
2115  if (rc == -1)
2116  return -1;
2117 
2118  char buf[PATH_MAX + 64];
2119  snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
2120  if (C_Confirmcreate && (mutt_yesorno(buf, MUTT_YES) != MUTT_YES))
2121  return -1;
2122 
2123  if (imap_create_mailbox(adata, mdata->name) < 0)
2124  return -1;
2125 
2126  return 0;
2127 }
2128 
2135 static int imap_mbox_check(struct Mailbox *m)
2136 {
2137  imap_allow_reopen(m);
2138  int rc = imap_check_mailbox(m, false);
2139  /* NOTE - ctx might have been changed at this point. In particular,
2140  * m could be NULL. Beware. */
2142 
2143  return rc;
2144 }
2145 
2149 static int imap_mbox_close(struct Mailbox *m)
2150 {
2151  struct ImapAccountData *adata = imap_adata_get(m);
2152  struct ImapMboxData *mdata = imap_mdata_get(m);
2153 
2154  /* Check to see if the mailbox is actually open */
2155  if (!adata || !mdata)
2156  return 0;
2157 
2158  /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
2159  * just for the connection.
2160  *
2161  * So when these are equal, it means we are actually closing the
2162  * mailbox and should clean up adata. Otherwise, we don't want to
2163  * touch adata - it's still being used. */
2164  if (m == adata->mailbox)
2165  {
2166  if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
2167  {
2168  /* mx_mbox_close won't sync if there are no deleted messages
2169  * and the mailbox is unchanged, so we may have to close here */
2170  if (m->msg_deleted == 0)
2171  {
2172  adata->closing = true;
2173  imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
2174  }
2175  adata->state = IMAP_AUTHENTICATED;
2176  }
2177 
2178  mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
2179  (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
2180  adata->mailbox = adata->prev_mailbox;
2183  }
2184 
2185  return 0;
2186 }
2187 
2191 static int imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
2192 {
2193  int rc = -1;
2194 
2195  struct Buffer *tmp = mutt_buffer_pool_get();
2196  mutt_buffer_mktemp(tmp);
2197 
2198  msg->fp = mutt_file_fopen(mutt_b2s(tmp), "w");
2199  if (!msg->fp)
2200  {
2201  mutt_perror(mutt_b2s(tmp));
2202  goto cleanup;
2203  }
2204 
2205  msg->path = mutt_buffer_strdup(tmp);
2206  rc = 0;
2207 
2208 cleanup:
2210  return rc;
2211 }
2212 
2216 static int imap_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
2217 {
2218  struct ImapMboxData *mdata = imap_mdata_get(m);
2219  if (!mdata)
2220  return -1;
2221 
2222  char *new_tag = NULL;
2223  char *checker = NULL;
2224 
2225  /* Check for \* flags capability */
2226  if (!imap_has_flag(&mdata->flags, NULL))
2227  {
2228  mutt_error(_("IMAP server doesn't support custom flags"));
2229  return -1;
2230  }
2231 
2232  *buf = '\0';
2233  if (tags)
2234  mutt_str_copy(buf, tags, buflen);
2235 
2236  if (mutt_get_field("Tags: ", buf, buflen, MUTT_COMP_NO_FLAGS) != 0)
2237  return -1;
2238 
2239  /* each keyword must be atom defined by rfc822 as:
2240  *
2241  * atom = 1*<any CHAR except specials, SPACE and CTLs>
2242  * CHAR = ( 0.-127. )
2243  * specials = "(" / ")" / "<" / ">" / "@"
2244  * / "," / ";" / ":" / "\" / <">
2245  * / "." / "[" / "]"
2246  * SPACE = ( 32. )
2247  * CTLS = ( 0.-31., 127.)
2248  *
2249  * And must be separated by one space.
2250  */
2251 
2252  new_tag = buf;
2253  checker = buf;
2254  SKIPWS(checker);
2255  while (*checker != '\0')
2256  {
2257  if ((*checker < 32) || (*checker >= 127) || // We allow space because it's the separator
2258  (*checker == 40) || // (
2259  (*checker == 41) || // )
2260  (*checker == 60) || // <
2261  (*checker == 62) || // >
2262  (*checker == 64) || // @
2263  (*checker == 44) || // ,
2264  (*checker == 59) || // ;
2265  (*checker == 58) || // :
2266  (*checker == 92) || // backslash
2267  (*checker == 34) || // "
2268  (*checker == 46) || // .
2269  (*checker == 91) || // [
2270  (*checker == 93)) // ]
2271  {
2272  mutt_error(_("Invalid IMAP flags"));
2273  return 0;
2274  }
2275 
2276  /* Skip duplicate space */
2277  while ((checker[0] == ' ') && (checker[1] == ' '))
2278  checker++;
2279 
2280  /* copy char to new_tag and go the next one */
2281  *new_tag++ = *checker++;
2282  }
2283  *new_tag = '\0';
2284  new_tag = buf; /* rewind */
2285  mutt_str_remove_trailing_ws(new_tag);
2286 
2287  return !mutt_str_equal(tags, buf);
2288 }
2289 
2303 static int imap_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
2304 {
2305  char uid[11];
2306 
2307  struct ImapAccountData *adata = imap_adata_get(m);
2308 
2309  if (*buf == '\0')
2310  buf = NULL;
2311 
2312  if (!(adata->mailbox->rights & MUTT_ACL_WRITE))
2313  return 0;
2314 
2315  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
2316 
2317  /* Remove old custom flags */
2318  if (imap_edata_get(e)->flags_remote)
2319  {
2320  struct Buffer cmd = mutt_buffer_make(128); // just a guess
2321  mutt_buffer_addstr(&cmd, "UID STORE ");
2322  mutt_buffer_addstr(&cmd, uid);
2323  mutt_buffer_addstr(&cmd, " -FLAGS.SILENT (");
2324  mutt_buffer_addstr(&cmd, imap_edata_get(e)->flags_remote);
2325  mutt_buffer_addstr(&cmd, ")");
2326 
2327  /* Should we return here, or we are fine and we could
2328  * continue to add new flags */
2329  int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2330  mutt_buffer_dealloc(&cmd);
2331  if (rc != IMAP_EXEC_SUCCESS)
2332  {
2333  return -1;
2334  }
2335  }
2336 
2337  /* Add new custom flags */
2338  if (buf)
2339  {
2340  struct Buffer cmd = mutt_buffer_make(128); // just a guess
2341  mutt_buffer_addstr(&cmd, "UID STORE ");
2342  mutt_buffer_addstr(&cmd, uid);
2343  mutt_buffer_addstr(&cmd, " +FLAGS.SILENT (");
2344  mutt_buffer_addstr(&cmd, buf);
2345  mutt_buffer_addstr(&cmd, ")");
2346 
2347  int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2348  mutt_buffer_dealloc(&cmd);
2349  if (rc != IMAP_EXEC_SUCCESS)
2350  {
2351  mutt_debug(LL_DEBUG1, "fail to add new flags\n");
2352  return -1;
2353  }
2354  }
2355 
2356  /* We are good sync them */
2357  mutt_debug(LL_DEBUG1, "NEW TAGS: %s\n", buf);
2358  driver_tags_replace(&e->tags, buf);
2359  FREE(&imap_edata_get(e)->flags_remote);
2361  return 0;
2362 }
2363 
2367 enum MailboxType imap_path_probe(const char *path, const struct stat *st)
2368 {
2369  if (mutt_istr_startswith(path, "imap://"))
2370  return MUTT_IMAP;
2371 
2372  if (mutt_istr_startswith(path, "imaps://"))
2373  return MUTT_IMAP;
2374 
2375  return MUTT_UNKNOWN;
2376 }
2377 
2381 int imap_path_canon(char *buf, size_t buflen)
2382 {
2383  struct Url *url = url_parse(buf);
2384  if (!url)
2385  return 0;
2386 
2387  char tmp[PATH_MAX];
2388  char tmp2[PATH_MAX];
2389 
2390  imap_fix_path('\0', url->path, tmp, sizeof(tmp));
2391  url->path = tmp;
2392  url_tostring(url, tmp2, sizeof(tmp2), 0);
2393  mutt_str_copy(buf, tmp2, buflen);
2394  url_free(&url);
2395 
2396  return 0;
2397 }
2398 
2407 int imap_expand_path(struct Buffer *buf)
2408 {
2410  return imap_path_canon(buf->data, PATH_MAX);
2411 }
2412 
2416 static int imap_path_pretty(char *buf, size_t buflen, const char *folder)
2417 {
2418  if (!folder)
2419  return -1;
2420 
2421  imap_pretty_mailbox(buf, buflen, folder);
2422  return 0;
2423 }
2424 
2428 static int imap_path_parent(char *buf, size_t buflen)
2429 {
2430  char tmp[PATH_MAX] = { 0 };
2431 
2432  imap_get_parent_path(buf, tmp, sizeof(tmp));
2433  mutt_str_copy(buf, tmp, buflen);
2434  return 0;
2435 }
2436 
2440 static int imap_path_is_empty(const char *path)
2441 {
2442  int rc = imap_path_status(path, false);
2443  if (rc < 0)
2444  return -1;
2445  if (rc == 0)
2446  return 1;
2447  return 0;
2448 }
2449 
2450 // clang-format off
2454 struct MxOps MxImapOps = {
2455  .type = MUTT_IMAP,
2456  .name = "imap",
2457  .is_local = false,
2458  .ac_find = imap_ac_find,
2459  .ac_add = imap_ac_add,
2460  .mbox_open = imap_mbox_open,
2461  .mbox_open_append = imap_mbox_open_append,
2462  .mbox_check = imap_mbox_check,
2463  .mbox_check_stats = imap_mbox_check_stats,
2464  .mbox_sync = NULL, /* imap syncing is handled by imap_sync_mailbox */
2465  .mbox_close = imap_mbox_close,
2466  .msg_open = imap_msg_open,
2467  .msg_open_new = imap_msg_open_new,
2468  .msg_commit = imap_msg_commit,
2469  .msg_close = imap_msg_close,
2470  .msg_padding_size = NULL,
2471  .msg_save_hcache = imap_msg_save_hcache,
2472  .tags_edit = imap_tags_edit,
2473  .tags_commit = imap_tags_commit,
2474  .path_probe = imap_path_probe,
2475  .path_canon = imap_path_canon,
2476  .path_pretty = imap_path_pretty,
2477  .path_parent = imap_path_parent,
2478  .path_is_empty = imap_path_is_empty,
2479 };
2480 // clang-format on
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: util.c:225
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
void imap_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free()
Definition: util.c:66
#define IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition: private.h:140
int mutt_save_message_ctx(struct Email *e, bool delete_original, bool decode, bool decrypt, struct Mailbox *m)
Save a message to a given mailbox.
Definition: commands.c:998
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:416
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:787
Deleted messages.
Definition: mutt.h:101
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:203
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:77
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:522
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: message.c:98
struct ImapCommand * cmds
Definition: private.h:200
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition: util.c:954
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:123
int msg_count
Total number of messages.
Definition: mailbox.h:91
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:2063
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:1163
off_t size
Size of the Mailbox.
Definition: mailbox.h:87
#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:1071
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:215
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1211
Logged out from server.
Definition: private.h:98
#define mutt_perror(...)
Definition: logging.h:85
bool C_MessageCacheClean
Config: (imap/pop) Clean out obsolete entries from the message cache.
Definition: bcache.c:44
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2367
Config/command parsing.
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
uint32_t uidvalidity
Definition: private.h:229
Imap connection failure.
Definition: private.h:86
Update internal tables.
Definition: mailbox.h:176
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
unsigned int ssf
Security strength factor, in bits (see below)
Definition: connection.h:37
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:734
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:2216
WHERE short C_Timeout
Config: Time to wait for user input in menus.
Definition: mutt_globals.h:115
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
#define mutt_message(...)
Definition: logging.h:83
static int sync_helper(struct Mailbox *m, AclFlags right, int flag, const char *name)
Sync flag changes to the server.
Definition: imap.c:318
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:509
unsigned int seqno
tag sequence number, e.g. &#39;{seqid}0001&#39;
Definition: private.h:188
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:40
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:137
Unrecoverable error occurred.
Definition: private.h:97
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1823
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:618
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:516
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:665
#define IMAP_CAP_ENABLE
RFC5161.
Definition: private.h:136
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:65
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:534
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1157
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:1045
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: private.h:175
struct ImapAccountData * imap_adata_new(struct Account *a)
Allocate and initialise a new ImapAccountData structure.
Definition: util.c:92
#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
NeoMutt Logging.
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:160
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: private.h:223
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
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1732
bool C_ImapRfc5161
Config: (imap) Use the IMAP ENABLE extension to select capabilities.
Definition: config.c:59
An open network connection (socket)
Definition: connection.h:34
String manipulation buffer.
Definition: buffer.h:33
#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:529
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
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:102
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1230
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:482
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close()
Definition: message.c:2055
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
static int make_msg_set(struct Mailbox *m, struct Buffer *buf, int flag, bool changed, bool invert, int *pos)
Make a message set.
Definition: imap.c:193
struct Mailbox * mailbox
Current selected mailbox.
Definition: private.h:207
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1205
bool changed
Email has been edited.
Definition: email.h:48
static int imap_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent()
Definition: imap.c:2428
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:837
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:725
A user-callable command.
Definition: mutt_commands.h:45
char * capstr
Definition: private.h:185
static int imap_mbox_open(struct Mailbox *m)
Open a mailbox - Implements MxOps::mbox_open()
Definition: imap.c:1900
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: util.c:244
Items in an IMAP browser.
Definition: private.h:148
ImapExecResult
imap_exec return code
Definition: private.h:82
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:50
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1178
void imap_init(void)
Setup feature commands.
Definition: imap.c:79
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1057
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
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:1654
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:590
char delim
Definition: private.h:151
unsigned int msn
Message Sequence Number.
Definition: message.h:45
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition: private.h:132
Container for Accounts, Notifications.
Definition: neomutt.h:36
A progress bar.
Definition: progress.h:50
bool C_ImapCondstore
Config: (imap) Enable the CONDSTORE extension.
Definition: config.c:41
#define COMMANDS_REGISTER(cmds)
Definition: mutt_commands.h:77
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
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:117
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:91
Messages that have been replied to.
Definition: mutt.h:95
int vcount
The number of virtual messages.
Definition: mailbox.h:102
Imap command executed or queued successfully.
Definition: private.h:84
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
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:209
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:75
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: private.h:225
int imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition: imap.c:1506
bool flagged
Definition: message.h:39
void imap_logout_all(void)
close all open connections
Definition: imap.c:554
static int 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:2191
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
bool mutt_istrn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:621
Assorted sorting methods.
char host[128]
Server to login to.
Definition: connaccount.h:53
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:142
Some miscellaneous functions.
Manage IMAP messages.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:129
bool C_ImapListSubscribed
Config: (imap) When browsing a mailbox, only display subscribed folders.
Definition: config.c:50
bool deleted
Definition: message.h:38
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:923
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:191
bool read
Email is read.
Definition: email.h:51
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
static int complete_hosts(char *buf, size_t buflen)
Look for completion matches for mailboxes.
Definition: imap.c:379
static struct Account * imap_ac_find(struct Account *a, const char *path)
Find an Account that matches a Mailbox path - Implements MxOps::ac_find()
Definition: imap.c:1716
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:379
Disconnected from server.
Definition: private.h:107
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:475
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2407
Parse and execute user-defined hooks.
static int imap_path_is_empty(const char *path)
Is the mailbox empty - Implements MxOps::path_is_empty()
Definition: imap.c:2440
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:113
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: message.c:72
bool old
Email is seen, but unread.
Definition: email.h:50
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:63
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:299
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1373
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:862
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:50
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:1243
static int imap_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add()
Definition: imap.c:1737
struct Buffer cmdbuf
Definition: private.h:204
struct Envelope * env
Envelope information.
Definition: email.h:90
short C_ImapPollTimeout
Config: (imap) Maximum time to wait for a server response.
Definition: config.c:57
Convenience wrapper for the core headers.
void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
Inform IMAP that an Email has been deleted.
Definition: imap.c:645
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: private.h:224
#define SKIPWS(ch)
Definition: string2.h:46
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, int flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:911
bool C_ImapQresync
Config: (imap) Enable the QRESYNC extension.
Definition: config.c:58
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
struct Mailbox * prev_mailbox
Previously selected mailbox.
Definition: private.h:208
Connected to server.
Definition: private.h:108
IMAP command structure.
Definition: private.h:159
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
User aborted the question (with Ctrl-G)
Definition: quad.h:38
Progress bar.
void(* adata_free)(void **ptr)
Free the private data attached to the Account.
Definition: account.h:49
Shared constants/structs that are private to IMAP.
struct ImapMboxData * imap_mdata_new(struct ImapAccountData *adata, const char *name)
Allocate and initialise a new ImapMboxData structure.
Definition: util.c:165
#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: private.h:231
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:1242
int 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:1840
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:106
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
Body Caching - local copies of email bodies.
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
char * flags_remote
Definition: message.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:989
struct HashTable * uid_hash
Definition: private.h:237
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()
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:700
Old messages.
Definition: mutt.h:94
Email list needs resorting.
Definition: mailbox.h:174
#define mutt_b2s(buf)
Definition: buffer.h:41
bool closing
If true, we are waiting for CLOSE completion.
Definition: private.h:174
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount&#39;s password.
Definition: connaccount.c:141
void imap_error(const char *where, const char *msg)
show an error and abort
Definition: util.c:798
struct Connection * mutt_conn_new(const struct ConnAccount *cac)
Create a new Connection.
Definition: mutt_socket.c:46
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:124
bool active
Message is not to be removed.
Definition: email.h:59
short C_DebugLevel
Config: Logging level for debug logs.
Definition: mutt_logging.c:48
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: mx.h:82
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
unsigned char C_SslStarttls
Config: (ssl) Use STARTTLS on servers advertising the capability.
Definition: config.c:47
unsigned int uid
32-bit Message UID
Definition: message.h:44
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:755
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:467
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:44
char * user
Username.
Definition: url.h:69
Functions to parse commands in a config file.
struct ListHead flags
Definition: private.h:228
int fd
Socket file descriptor.
Definition: connection.h:40
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
Nondestructive flags change (IMAP)
Definition: mx.h:76
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: message.c:2041
Manage where the email is piped to external commands.
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition: hash.c:434
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: private.h:176
Match patterns to emails.
Tagged messages.
Definition: mutt.h:103
char * data
Pointer to data.
Definition: buffer.h:35
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1306
#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:96
bool replied
Definition: message.h:40
char * name
Mailbox name.
Definition: private.h:219
bool old
Definition: message.h:37
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
static int imap_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check()
Definition: imap.c:2135
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
char * host
Host.
Definition: url.h:71
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1150
bool purge
Skip trash folder when deleting.
Definition: email.h:46
WHERE bool C_Confirmcreate
Config: Confirm before creating a new mailbox.
Definition: mutt_globals.h:142
int mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: string.c:343
static int imap_mbox_check_stats(struct Mailbox *m, int flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats()
Definition: imap.c:1200
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:1245
bool C_ImapCheckSubscribed
Config: (imap) When opening a mailbox, ask the server for a list of subscribed folders.
Definition: config.c:40
ImapCapFlags capabilities
Definition: private.h:186
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
#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: private.h:189
bool C_SslForceTls
Config: (ssl) Require TLS encryption for all connections.
Definition: config.c:46
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:441
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:96
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1751
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:73
Connection is idle.
Definition: private.h:113
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition: private.h:221
short C_ImapKeepalive
Config: (imap) Time to wait before polling an open IMAP connection.
Definition: config.c:49
Imap command failure.
Definition: private.h:85
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1791
bool mutt_strn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
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:312
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: curs_lib.c:517
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
int imap_complete(char *buf, size_t buflen, const char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1316
int imap_check_mailbox(struct Mailbox *m, bool force)
use the NOOP or IDLE command to poll for new mail
Definition: imap.c:1091
IMAP-specific Account data -.
Definition: private.h:170
unsigned int uid_next
Definition: private.h:230
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:358
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:155
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:906
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:293
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
Log at debug level 1.
Definition: logging.h:40
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: util.c:114
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:820
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:716
IMAP-specific Mailbox data -.
Definition: private.h:217
char * buf
Definition: private.h:190
int msg_new
Number of new messages.
Definition: mailbox.h:95
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
static int imap_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close()
Definition: imap.c:2149
bool deleted
Email is deleted.
Definition: email.h:45
void * edata
Driver-specific data.
Definition: email.h:111
bool C_ImapDeflate
Config: (imap) Compress network traffic.
Definition: config.c:43
#define mutt_error(...)
Definition: logging.h:84
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
static int imap_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message - Implements MxOps::tags_commit()
Definition: imap.c:2303
Connection Library.
FILE * fp
pointer to the message data
Definition: mx.h:84
static int imap_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty()
Definition: imap.c:2416
int index
The absolute (unsorted) message number.
Definition: email.h:86
#define FREE(x)
Definition: memory.h:40
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1681
int url_tostring(struct Url *url, char *dest, size_t len, int flags)
Output the URL string for a given Url object.
Definition: url.c:418
bool read
Definition: message.h:36
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:266
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:73
#define STAILQ_EMPTY(head)
Definition: queue.h:345
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition: auth.c:105
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
int imap_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon()
Definition: imap.c:2381
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:90
struct ImapList * cmdresult
Definition: private.h:197
List of Mailboxes.
Definition: mailbox.h:152
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
Hundreds of global variables to back the user variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
IMAP-specific Email data -.
Definition: message.h:33
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:183
New mail received in Mailbox.
Definition: mx.h:73
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:1784
sort_t mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:324
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:588
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:888
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
char * munge_name
Munged version of the mailbox name.
Definition: private.h:220
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
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()
Mailbox was reopened.
Definition: mx.h:75
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1136
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
bool C_ImapIdle
Config: (imap) Use the IMAP IDLE extension to check for new mail.
Definition: config.c:48
int imap_fast_trash(struct Mailbox *m, char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1394
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:435
#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:177
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
Trashed messages.
Definition: mutt.h:108
#define IMAP_CAP_ACL
RFC2086: IMAP4 ACL extension.
Definition: private.h:126
Authentication successful.
Definition: auth.h:38
Log at debug level 3.
Definition: logging.h:42
WHERE char * C_Postponed
Config: Folder to store postponed messages.
Definition: mutt_globals.h:101
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: private.h:232
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:971
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: private.h:193
char * path
path to temp file
Definition: mx.h:85
The Mailbox API.
Definition: mx.h:104
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1261
static int imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append()
Definition: imap.c:2102
#define IMAP_LOG_LTRL
Definition: private.h:50
static void set_flag(struct Mailbox *m, AclFlags aclflag, int flag, const char *str, char *flags, size_t flsize)
append str to flags if we currently have permission according to aclflag
Definition: imap.c:172
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: private.h:172
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234