NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
message.c
Go to the documentation of this file.
1 
31 #include "config.h"
32 #include <assert.h>
33 #include <ctype.h>
34 #include <limits.h>
35 #include <stdbool.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include "private.h"
41 #include "mutt/lib.h"
42 #include "config/lib.h"
43 #include "email/lib.h"
44 #include "core/lib.h"
45 #include "conn/lib.h"
46 #include "gui/lib.h"
47 #include "mutt.h"
48 #include "message.h"
49 #include "lib.h"
50 #include "bcache/lib.h"
51 #include "progress/lib.h"
52 #include "question/lib.h"
53 #include "adata.h"
54 #include "commands.h"
55 #include "edata.h"
56 #include "mdata.h"
57 #include "msn.h"
58 #include "mutt_globals.h"
59 #include "mutt_logging.h"
60 #include "mutt_socket.h"
61 #include "muttlib.h"
62 #include "mx.h"
63 #include "protos.h"
64 #ifdef ENABLE_NLS
65 #include <libintl.h>
66 #endif
67 #ifdef USE_HCACHE
68 #include "hcache/lib.h"
69 #endif
70 
71 struct BodyCache;
72 
79 static struct BodyCache *msg_cache_open(struct Mailbox *m)
80 {
82  struct ImapMboxData *mdata = imap_mdata_get(m);
83 
84  if (!adata || (adata->mailbox != m))
85  return NULL;
86 
87  if (mdata->bcache)
88  return mdata->bcache;
89 
90  struct Buffer *mailbox = mutt_buffer_pool_get();
91  imap_cachepath(adata->delim, mdata->name, mailbox);
92 
93  struct BodyCache *bc =
94  mutt_bcache_open(&adata->conn->account, mutt_buffer_string(mailbox));
95  mutt_buffer_pool_release(&mailbox);
96 
97  return bc;
98 }
99 
107 static FILE *msg_cache_get(struct Mailbox *m, struct Email *e)
108 {
109  struct ImapAccountData *adata = imap_adata_get(m);
110  struct ImapMboxData *mdata = imap_mdata_get(m);
111 
112  if (!e || !adata || (adata->mailbox != m))
113  return NULL;
114 
115  mdata->bcache = msg_cache_open(m);
116  char id[64];
117  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
118  return mutt_bcache_get(mdata->bcache, id);
119 }
120 
128 static FILE *msg_cache_put(struct Mailbox *m, struct Email *e)
129 {
130  struct ImapAccountData *adata = imap_adata_get(m);
131  struct ImapMboxData *mdata = imap_mdata_get(m);
132 
133  if (!e || !adata || (adata->mailbox != m))
134  return NULL;
135 
136  mdata->bcache = msg_cache_open(m);
137  char id[64];
138  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
139  return mutt_bcache_put(mdata->bcache, id);
140 }
141 
149 static int msg_cache_commit(struct Mailbox *m, struct Email *e)
150 {
151  struct ImapAccountData *adata = imap_adata_get(m);
152  struct ImapMboxData *mdata = imap_mdata_get(m);
153 
154  if (!e || !adata || (adata->mailbox != m))
155  return -1;
156 
157  mdata->bcache = msg_cache_open(m);
158  char id[64];
159  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
160 
161  return mutt_bcache_commit(mdata->bcache, id);
162 }
163 
168 static int msg_cache_clean_cb(const char *id, struct BodyCache *bcache, void *data)
169 {
170  uint32_t uv;
171  unsigned int uid;
172  struct ImapMboxData *mdata = data;
173 
174  if (sscanf(id, "%u-%u", &uv, &uid) != 2)
175  return 0;
176 
177  /* bad UID */
178  if ((uv != mdata->uidvalidity) || !mutt_hash_int_find(mdata->uid_hash, uid))
179  mutt_bcache_del(bcache, id);
180 
181  return 0;
182 }
183 
191 static char *msg_parse_flags(struct ImapHeader *h, char *s)
192 {
193  struct ImapEmailData *edata = h->edata;
194 
195  /* sanity-check string */
196  size_t plen = mutt_istr_startswith(s, "FLAGS");
197  if (plen == 0)
198  {
199  mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
200  return NULL;
201  }
202  s += plen;
203  SKIPWS(s);
204  if (*s != '(')
205  {
206  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
207  return NULL;
208  }
209  s++;
210 
211  FREE(&edata->flags_system);
212  FREE(&edata->flags_remote);
213 
214  edata->deleted = false;
215  edata->flagged = false;
216  edata->replied = false;
217  edata->read = false;
218  edata->old = false;
219 
220  /* start parsing */
221  while (*s && (*s != ')'))
222  {
223  if ((plen = mutt_istr_startswith(s, "\\deleted")))
224  {
225  s += plen;
226  edata->deleted = true;
227  }
228  else if ((plen = mutt_istr_startswith(s, "\\flagged")))
229  {
230  s += plen;
231  edata->flagged = true;
232  }
233  else if ((plen = mutt_istr_startswith(s, "\\answered")))
234  {
235  s += plen;
236  edata->replied = true;
237  }
238  else if ((plen = mutt_istr_startswith(s, "\\seen")))
239  {
240  s += plen;
241  edata->read = true;
242  }
243  else if ((plen = mutt_istr_startswith(s, "\\recent")))
244  {
245  s += plen;
246  }
247  else if ((plen = mutt_istr_startswith(s, "old")))
248  {
249  s += plen;
250  edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
251  }
252  else
253  {
254  char ctmp;
255  char *flag_word = s;
256  bool is_system_keyword = mutt_istr_startswith(s, "\\");
257 
258  while (*s && !IS_SPACE(*s) && (*s != ')'))
259  s++;
260 
261  ctmp = *s;
262  *s = '\0';
263 
264  /* store other system flags as well (mainly \\Draft) */
265  if (is_system_keyword)
266  mutt_str_append_item(&edata->flags_system, flag_word, ' ');
267  /* store custom flags as well */
268  else
269  mutt_str_append_item(&edata->flags_remote, flag_word, ' ');
270 
271  *s = ctmp;
272  }
273  SKIPWS(s);
274  }
275 
276  /* wrap up, or note bad flags response */
277  if (*s == ')')
278  s++;
279  else
280  {
281  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
282  return NULL;
283  }
284 
285  return s;
286 }
287 
296 static int msg_parse_fetch(struct ImapHeader *h, char *s)
297 {
298  if (!s)
299  return -1;
300 
301  char tmp[128];
302  char *ptmp = NULL;
303  size_t plen = 0;
304 
305  while (*s)
306  {
307  SKIPWS(s);
308 
309  if (mutt_istr_startswith(s, "FLAGS"))
310  {
311  s = msg_parse_flags(h, s);
312  if (!s)
313  return -1;
314  }
315  else if ((plen = mutt_istr_startswith(s, "UID")))
316  {
317  s += plen;
318  SKIPWS(s);
319  if (mutt_str_atoui(s, &h->edata->uid) < 0)
320  return -1;
321 
322  s = imap_next_word(s);
323  }
324  else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
325  {
326  s += plen;
327  SKIPWS(s);
328  if (*s != '\"')
329  {
330  mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
331  return -1;
332  }
333  s++;
334  ptmp = tmp;
335  while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
336  *ptmp++ = *s++;
337  if (*s != '\"')
338  return -1;
339  s++; /* skip past the trailing " */
340  *ptmp = '\0';
341  h->received = mutt_date_parse_imap(tmp);
342  }
343  else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
344  {
345  s += plen;
346  SKIPWS(s);
347  ptmp = tmp;
348  while (isdigit((unsigned char) *s) && (ptmp != (tmp + sizeof(tmp) - 1)))
349  *ptmp++ = *s++;
350  *ptmp = '\0';
351  if (mutt_str_atol(tmp, &h->content_length) < 0)
352  return -1;
353  }
354  else if (mutt_istr_startswith(s, "BODY") ||
355  mutt_istr_startswith(s, "RFC822.HEADER"))
356  {
357  /* handle above, in msg_fetch_header */
358  return -2;
359  }
360  else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
361  {
362  s += plen;
363  SKIPWS(s);
364  if (*s != '(')
365  {
366  mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
367  return -1;
368  }
369  s++;
370  while (*s && (*s != ')'))
371  s++;
372  if (*s == ')')
373  s++;
374  else
375  {
376  mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
377  return -1;
378  }
379  }
380  else if (*s == ')')
381  s++; /* end of request */
382  else if (*s)
383  {
384  /* got something i don't understand */
385  imap_error("msg_parse_fetch", s);
386  return -1;
387  }
388  }
389 
390  return 0;
391 }
392 
405 static int msg_fetch_header(struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
406 {
407  int rc = -1; /* default now is that string isn't FETCH response */
408 
409  struct ImapAccountData *adata = imap_adata_get(m);
410 
411  if (buf[0] != '*')
412  return rc;
413 
414  /* skip to message number */
415  buf = imap_next_word(buf);
416  if (mutt_str_atoui(buf, &ih->edata->msn) < 0)
417  return rc;
418 
419  /* find FETCH tag */
420  buf = imap_next_word(buf);
421  if (!mutt_istr_startswith(buf, "FETCH"))
422  return rc;
423 
424  rc = -2; /* we've got a FETCH response, for better or worse */
425  buf = strchr(buf, '(');
426  if (!buf)
427  return rc;
428  buf++;
429 
430  /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
431  * read header lines and call it again. Silly. */
432  int parse_rc = msg_parse_fetch(ih, buf);
433  if (parse_rc == 0)
434  return 0;
435  if ((parse_rc != -2) || !fp)
436  return rc;
437 
438  unsigned int bytes = 0;
439  if (imap_get_literal_count(buf, &bytes) == 0)
440  {
441  imap_read_literal(fp, adata, bytes, NULL);
442 
443  /* we may have other fields of the FETCH _after_ the literal
444  * (eg Domino puts FLAGS here). Nothing wrong with that, either.
445  * This all has to go - we should accept literals and nonliterals
446  * interchangeably at any time. */
447  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
448  return rc;
449 
450  if (msg_parse_fetch(ih, adata->buf) == -1)
451  return rc;
452  }
453 
454  rc = 0; /* success */
455 
456  /* subtract headers from message size - unfortunately only the subset of
457  * headers we've requested. */
458  ih->content_length -= bytes;
459 
460  return rc;
461 }
462 
469 static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
470 {
471  buf[*len] = '\0';
472  int rc = mutt_socket_write_n(conn, buf, *len);
473  *len = 0;
474  return rc;
475 }
476 
486 {
487  bool abort = false;
488 
489  mutt_flushinp();
490  /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
491  if (mutt_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
492  {
493  abort = true;
494  imap_close_connection(adata);
495  }
496  SigInt = false;
497 
498  return abort;
499 }
500 
509 static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
510 {
511  struct ImapMboxData *mdata = adata->mailbox->mdata;
512  if (!mdata->uid_hash)
513  mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
514 }
515 
528 static unsigned int imap_fetch_msn_seqset(struct Buffer *buf, struct ImapAccountData *adata,
529  bool evalhc, unsigned int msn_begin,
530  unsigned int msn_end, unsigned int *fetch_msn_end)
531 {
532  struct ImapMboxData *mdata = adata->mailbox->mdata;
533  unsigned int max_headers_per_fetch = UINT_MAX;
534  bool first_chunk = true;
535  int state = 0; /* 1: single msn, 2: range of msn */
536  unsigned int msn;
537  unsigned int range_begin = 0;
538  unsigned int range_end = 0;
539  unsigned int msn_count = 0;
540 
541  mutt_buffer_reset(buf);
542  if (msn_end < msn_begin)
543  return 0;
544 
545  const long c_imap_fetch_chunk_size =
546  cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
547  if (c_imap_fetch_chunk_size > 0)
548  max_headers_per_fetch = c_imap_fetch_chunk_size;
549 
550  if (!evalhc)
551  {
552  if (msn_end - msn_begin + 1 <= max_headers_per_fetch)
553  *fetch_msn_end = msn_end;
554  else
555  *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
556  mutt_buffer_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
557  return (*fetch_msn_end - msn_begin + 1);
558  }
559 
560  for (msn = msn_begin; msn <= (msn_end + 1); msn++)
561  {
562  if (msn_count < max_headers_per_fetch && msn <= msn_end &&
563  !imap_msn_get(&mdata->msn, msn - 1))
564  {
565  msn_count++;
566 
567  switch (state)
568  {
569  case 1: /* single: convert to a range */
570  state = 2;
571  /* fallthrough */
572  case 2: /* extend range ending */
573  range_end = msn;
574  break;
575  default:
576  state = 1;
577  range_begin = msn;
578  break;
579  }
580  }
581  else if (state)
582  {
583  if (first_chunk)
584  first_chunk = false;
585  else
586  mutt_buffer_addch(buf, ',');
587 
588  if (state == 1)
589  mutt_buffer_add_printf(buf, "%u", range_begin);
590  else if (state == 2)
591  mutt_buffer_add_printf(buf, "%u:%u", range_begin, range_end);
592  state = 0;
593 
594  if ((mutt_buffer_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
595  break;
596  }
597  }
598 
599  /* The loop index goes one past to terminate the range if needed. */
600  *fetch_msn_end = msn - 1;
601 
602  return msn_count;
603 }
604 
620 static void set_changed_flag(struct Mailbox *m, struct Email *e, int local_changes,
621  bool *server_changes, enum MessageType flag_name,
622  bool old_hd_flag, bool new_hd_flag, bool h_flag)
623 {
624  /* If there are local_changes, we only want to note if the server
625  * flags have changed, so we can set a reopen flag in
626  * cmd_parse_fetch(). We don't want to count a local modification
627  * to the header flag as a "change". */
628  if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
629  return;
630 
631  if (new_hd_flag == h_flag)
632  return;
633 
634  if (server_changes)
635  *server_changes = true;
636 
637  /* Local changes have priority */
638  if (local_changes == 0)
639  mutt_set_flag(m, e, flag_name, new_hd_flag);
640 }
641 
642 #ifdef USE_HCACHE
643 
661  unsigned int msn_end, unsigned int uid_next,
662  bool store_flag_updates, bool eval_condstore)
663 {
664  struct Progress *progress = NULL;
665  char buf[1024];
666  int rc = -1;
667 
668  struct Mailbox *m = adata->mailbox;
669  struct ImapMboxData *mdata = imap_mdata_get(m);
670  int idx = m->msg_count;
671 
672  if (m->verbose)
673  {
674  /* L10N: Comparing the cached data with the IMAP server's data */
675  progress = progress_new(_("Evaluating cache..."), MUTT_PROGRESS_READ, msn_end);
676  }
677 
678  /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
679  * the flags in the header cache, and update them further below.
680  * Otherwise, we fetch the current state of the flags here. */
681  snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
682  eval_condstore ? "" : " FLAGS");
683 
684  imap_cmd_start(adata, buf);
685 
686  rc = IMAP_RES_CONTINUE;
687  int mfhrc = 0;
688  struct ImapHeader h;
689  for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
690  {
691  if (SigInt && query_abort_header_download(adata))
692  goto fail;
693 
694  if (m->verbose)
695  progress_update(progress, msgno, -1);
696 
697  memset(&h, 0, sizeof(h));
698  h.edata = imap_edata_new();
699  do
700  {
701  rc = imap_cmd_step(adata);
702  if (rc != IMAP_RES_CONTINUE)
703  break;
704 
705  mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
706  if (mfhrc < 0)
707  continue;
708 
709  if (!h.edata->uid)
710  {
711  mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
712  h.edata->msn);
713  continue;
714  }
715 
716  if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
717  {
718  mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
719  h.edata->msn);
720  continue;
721  }
722 
723  if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
724  {
725  mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
726  h.edata->msn);
727  continue;
728  }
729 
730  struct Email *e = imap_hcache_get(mdata, h.edata->uid);
731  m->emails[idx] = e;
732  if (e)
733  {
734  imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
735  mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
736 
737  e->index = h.edata->uid;
738  /* messages which have not been expunged are ACTIVE (borrowed from mh
739  * folders) */
740  e->active = true;
741  e->changed = false;
742  if (eval_condstore)
743  {
744  h.edata->read = e->read;
745  h.edata->old = e->old;
746  h.edata->deleted = e->deleted;
747  h.edata->flagged = e->flagged;
748  h.edata->replied = e->replied;
749  }
750  else
751  {
752  e->read = h.edata->read;
753  e->old = h.edata->old;
754  e->deleted = h.edata->deleted;
755  e->flagged = h.edata->flagged;
756  e->replied = h.edata->replied;
757  }
758 
759  /* mailbox->emails[msgno]->received is restored from mutt_hcache_restore */
760  e->edata = h.edata;
762  STAILQ_INIT(&e->tags);
763 
764  /* We take a copy of the tags so we can split the string */
765  char *tags_copy = mutt_str_dup(h.edata->flags_remote);
766  driver_tags_replace(&e->tags, tags_copy);
767  FREE(&tags_copy);
768 
769  m->msg_count++;
770  mailbox_size_add(m, e);
771 
772  /* If this is the first time we are fetching, we need to
773  * store the current state of flags back into the header cache */
774  if (!eval_condstore && store_flag_updates)
775  imap_hcache_put(mdata, e);
776 
777  h.edata = NULL;
778  idx++;
779  }
780  } while (mfhrc == -1);
781 
782  imap_edata_free((void **) &h.edata);
783 
784  if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
785  goto fail;
786  }
787 
788  rc = 0;
789 fail:
790  progress_free(&progress);
791  return rc;
792 }
793 
806 static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
807 {
808  int rc;
809  unsigned int uid = 0;
810 
811  mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
812  struct Mailbox *m = adata->mailbox;
813  struct ImapMboxData *mdata = adata->mailbox->mdata;
814  unsigned int msn = 1;
815 
816  if (m->verbose)
817  mutt_message(_("Evaluating cache..."));
818 
819  struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
820  if (!iter)
821  return -1;
822 
823  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
824  {
825  /* The seqset may contain more headers than the fetch request, so
826  * we need to watch and reallocate the context and msn_index */
827  imap_msn_reserve(&mdata->msn, msn);
828 
829  struct Email *e = imap_hcache_get(mdata, uid);
830  if (e)
831  {
832  imap_msn_set(&mdata->msn, msn - 1, e);
833 
834  if (m->msg_count >= m->email_max)
835  mx_alloc_memory(m);
836 
837  struct ImapEmailData *edata = imap_edata_new();
838  e->edata = edata;
840 
841  e->index = uid;
842  e->active = true;
843  e->changed = false;
844  edata->read = e->read;
845  edata->old = e->old;
846  edata->deleted = e->deleted;
847  edata->flagged = e->flagged;
848  edata->replied = e->replied;
849 
850  edata->msn = msn;
851  edata->uid = uid;
852  mutt_hash_int_insert(mdata->uid_hash, uid, e);
853 
854  mailbox_size_add(m, e);
855  m->emails[m->msg_count++] = e;
856 
857  msn++;
858  }
859  }
860 
862 
863  return rc;
864 }
865 
879  unsigned int msn_end, unsigned int uid_next,
880  unsigned long long hc_modseq, bool eval_qresync)
881 {
882  struct Progress *progress = NULL;
883  char buf[1024];
884  unsigned int header_msn = 0;
885 
886  struct Mailbox *m = adata->mailbox;
887  struct ImapMboxData *mdata = imap_mdata_get(m);
888 
889  if (m->verbose)
890  {
891  /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
892  progress = progress_new(_("Fetching flag updates..."), MUTT_PROGRESS_READ, msn_end);
893  }
894 
895  snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
896  uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
897 
898  imap_cmd_start(adata, buf);
899 
900  int rc = IMAP_RES_CONTINUE;
901  for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
902  {
903  if (SigInt && query_abort_header_download(adata))
904  goto fail;
905 
906  if (m->verbose)
907  progress_update(progress, msgno, -1);
908 
909  /* cmd_parse_fetch will update the flags */
910  rc = imap_cmd_step(adata);
911  if (rc != IMAP_RES_CONTINUE)
912  break;
913 
914  /* so we just need to grab the header and persist it back into
915  * the header cache */
916  char *fetch_buf = adata->buf;
917  if (fetch_buf[0] != '*')
918  continue;
919 
920  fetch_buf = imap_next_word(fetch_buf);
921  if (!isdigit((unsigned char) *fetch_buf) || (mutt_str_atoui(fetch_buf, &header_msn) < 0))
922  continue;
923 
924  if ((header_msn < 1) || (header_msn > msn_end) ||
925  !imap_msn_get(&mdata->msn, header_msn - 1))
926  {
927  mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
928  header_msn);
929  continue;
930  }
931 
932  imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
933  }
934 
935  if (rc != IMAP_RES_OK)
936  goto fail;
937 
938  /* The IMAP flag setting as part of cmd_parse_fetch() ends up
939  * flipping these on. */
940  mdata->check_status &= ~IMAP_FLAGS_PENDING;
941  m->changed = false;
942 
943  /* VANISHED handling: we need to empty out the messages */
944  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
945  {
946  imap_hcache_close(mdata);
948 
949  imap_hcache_open(adata, mdata);
950  mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
951  }
952 
953  /* undo expunge count updates.
954  * ctx_update() will do this at the end of the header fetch. */
955  m->vcount = 0;
956  m->msg_tagged = 0;
957  m->msg_deleted = 0;
958  m->msg_new = 0;
959  m->msg_unread = 0;
960  m->msg_flagged = 0;
961  m->changed = false;
962 
963  rc = 0;
964 fail:
965  progress_free(&progress);
966  return rc;
967 }
968 
977 static int imap_verify_qresync(struct Mailbox *m)
978 {
979  assert(m);
980  struct ImapAccountData *adata = imap_adata_get(m);
981  struct ImapMboxData *mdata = imap_mdata_get(m);
982  if (!adata || (adata->mailbox != m))
983  return -1;
984 
985  const size_t max_msn = imap_msn_highest(&mdata->msn);
986 
987  unsigned int msn;
988  unsigned int uid;
989  struct Email *e = NULL;
990  struct Email *uidh = NULL;
991 
992  for (int i = 0; i < m->msg_count; i++)
993  {
994  e = m->emails[i];
995  const struct ImapEmailData *edata = imap_edata_get(e);
996  if (!edata)
997  goto fail;
998 
999  msn = imap_edata_get(e)->msn;
1000  uid = imap_edata_get(e)->uid;
1001 
1002  if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1003  goto fail;
1004 
1005  uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1006  if (uidh != e)
1007  goto fail;
1008  }
1009 
1010  return 0;
1011 
1012 fail:
1013  imap_msn_free(&mdata->msn);
1014  mutt_hash_free(&mdata->uid_hash);
1015 
1016  for (int i = 0; i < m->msg_count; i++)
1017  {
1018  if (m->emails[i] && m->emails[i]->edata)
1019  imap_edata_free(&m->emails[i]->edata);
1020  email_free(&m->emails[i]);
1021  }
1022  m->msg_count = 0;
1023  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1025  imap_hcache_close(mdata);
1026 
1027  if (m->verbose)
1028  {
1029  /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1030  sanity check. If that fails, Mutt reopens the mailbox using a normal
1031  download. */
1032  mutt_error(_("QRESYNC failed. Reopening mailbox."));
1033  }
1034  return -1;
1035 }
1036 
1037 #endif /* USE_HCACHE */
1038 
1050 static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin,
1051  unsigned int msn_end, bool evalhc,
1052  unsigned int *maxuid, bool initial_download)
1053 {
1054  int rc, mfhrc = 0, retval = -1;
1055  unsigned int fetch_msn_end = 0;
1056  struct Progress *progress = NULL;
1057  char *hdrreq = NULL;
1058  struct Buffer *tempfile = NULL;
1059  FILE *fp = NULL;
1060  struct ImapHeader h;
1061  struct Buffer *buf = NULL;
1062  static const char *const want_headers =
1063  "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE "
1064  "CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL "
1065  "X-ORIGINAL-TO";
1066 
1067  struct ImapAccountData *adata = imap_adata_get(m);
1068  struct ImapMboxData *mdata = imap_mdata_get(m);
1069  int idx = m->msg_count;
1070 
1071  if (!adata || (adata->mailbox != m))
1072  return -1;
1073 
1074  struct Buffer *hdr_list = mutt_buffer_pool_get();
1075  mutt_buffer_strcpy(hdr_list, want_headers);
1076  const char *const c_imap_headers =
1077  cs_subset_string(NeoMutt->sub, "imap_headers");
1078  if (c_imap_headers)
1079  {
1080  mutt_buffer_addch(hdr_list, ' ');
1081  mutt_buffer_addstr(hdr_list, c_imap_headers);
1082  }
1083 #ifdef USE_AUTOCRYPT
1084  const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1085  if (c_autocrypt)
1086  {
1087  mutt_buffer_addch(hdr_list, ' ');
1088  mutt_buffer_addstr(hdr_list, "AUTOCRYPT");
1089  }
1090 #endif
1091 
1092  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1093  {
1094  mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", mutt_buffer_string(hdr_list));
1095  }
1096  else if (adata->capabilities & IMAP_CAP_IMAP4)
1097  {
1098  mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", mutt_buffer_string(hdr_list));
1099  }
1100  else
1101  { /* Unable to fetch headers for lower versions */
1102  mutt_error(_("Unable to fetch headers from this IMAP server version"));
1103  goto bail;
1104  }
1105 
1106  mutt_buffer_pool_release(&hdr_list);
1107 
1108  /* instead of downloading all headers and then parsing them, we parse them
1109  * as they come in. */
1110  tempfile = mutt_buffer_pool_get();
1111  mutt_buffer_mktemp(tempfile);
1112  fp = mutt_file_fopen(mutt_buffer_string(tempfile), "w+");
1113  if (!fp)
1114  {
1115  mutt_error(_("Could not create temporary file %s"), mutt_buffer_string(tempfile));
1116  goto bail;
1117  }
1118  unlink(mutt_buffer_string(tempfile));
1119  mutt_buffer_pool_release(&tempfile);
1120 
1121  if (m->verbose)
1122  {
1123  progress = progress_new(_("Fetching message headers..."), MUTT_PROGRESS_READ, msn_end);
1124  }
1125 
1126  buf = mutt_buffer_pool_get();
1127 
1128  /* NOTE:
1129  * The (fetch_msn_end < msn_end) used to be important to prevent
1130  * an infinite loop, in the event the server did not return all
1131  * the headers (due to a pending expunge, for example).
1132  *
1133  * I believe the new chunking imap_fetch_msn_seqset()
1134  * implementation and "msn_begin = fetch_msn_end + 1" assignment
1135  * at the end of the loop makes the comparison unneeded, but to be
1136  * cautious I'm keeping it.
1137  */
1138  while ((fetch_msn_end < msn_end) &&
1139  imap_fetch_msn_seqset(buf, adata, evalhc, msn_begin, msn_end, &fetch_msn_end))
1140  {
1141  char *cmd = NULL;
1142  mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1143  mutt_buffer_string(buf), hdrreq);
1144  imap_cmd_start(adata, cmd);
1145  FREE(&cmd);
1146 
1147  rc = IMAP_RES_CONTINUE;
1148  for (int msgno = msn_begin; rc == IMAP_RES_CONTINUE; msgno++)
1149  {
1150  if (initial_download && SigInt && query_abort_header_download(adata))
1151  goto bail;
1152 
1153  if (m->verbose)
1154  progress_update(progress, msgno, -1);
1155 
1156  rewind(fp);
1157  memset(&h, 0, sizeof(h));
1158  h.edata = imap_edata_new();
1159 
1160  /* this DO loop does two things:
1161  * 1. handles untagged messages, so we can try again on the same msg
1162  * 2. fetches the tagged response at the end of the last message. */
1163  do
1164  {
1165  rc = imap_cmd_step(adata);
1166  if (rc != IMAP_RES_CONTINUE)
1167  break;
1168 
1169  mfhrc = msg_fetch_header(m, &h, adata->buf, fp);
1170  if (mfhrc < 0)
1171  continue;
1172 
1173  if (!ftello(fp))
1174  {
1175  mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1176  continue;
1177  }
1178 
1179  /* make sure we don't get remnants from older larger message headers */
1180  fputs("\n\n", fp);
1181 
1182  if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1183  {
1184  mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1185  h.edata->msn);
1186  continue;
1187  }
1188 
1189  /* May receive FLAGS updates in a separate untagged response */
1190  if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1191  {
1192  mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1193  h.edata->msn);
1194  continue;
1195  }
1196 
1197  struct Email *e = email_new();
1198  m->emails[idx] = e;
1199 
1200  imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
1201  mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
1202 
1203  e->index = h.edata->uid;
1204  /* messages which have not been expunged are ACTIVE (borrowed from mh
1205  * folders) */
1206  e->active = true;
1207  e->changed = false;
1208  e->read = h.edata->read;
1209  e->old = h.edata->old;
1210  e->deleted = h.edata->deleted;
1211  e->flagged = h.edata->flagged;
1212  e->replied = h.edata->replied;
1213  e->received = h.received;
1214  e->edata = (void *) (h.edata);
1216  STAILQ_INIT(&e->tags);
1217 
1218  /* We take a copy of the tags so we can split the string */
1219  char *tags_copy = mutt_str_dup(h.edata->flags_remote);
1220  driver_tags_replace(&e->tags, tags_copy);
1221  FREE(&tags_copy);
1222 
1223  if (*maxuid < h.edata->uid)
1224  *maxuid = h.edata->uid;
1225 
1226  rewind(fp);
1227  /* NOTE: if Date: header is missing, mutt_rfc822_read_header depends
1228  * on h.received being set */
1229  e->env = mutt_rfc822_read_header(fp, e, false, false);
1230  /* body built as a side-effect of mutt_rfc822_read_header */
1231  e->body->length = h.content_length;
1232  mailbox_size_add(m, e);
1233 
1234 #ifdef USE_HCACHE
1235  imap_hcache_put(mdata, e);
1236 #endif /* USE_HCACHE */
1237 
1238  m->msg_count++;
1239 
1240  h.edata = NULL;
1241  idx++;
1242  } while (mfhrc == -1);
1243 
1244  imap_edata_free((void **) &h.edata);
1245 
1246  if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
1247  goto bail;
1248  }
1249 
1250  /* In case we get new mail while fetching the headers. */
1251  if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1252  {
1253  msn_end = mdata->new_mail_count;
1254  while (msn_end > m->email_max)
1255  mx_alloc_memory(m);
1256  imap_msn_reserve(&mdata->msn, msn_end);
1257  mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1258  mdata->new_mail_count = 0;
1259  }
1260 
1261  /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1262  * must not get any EXPUNGE/VANISHED responses in the middle of a
1263  * FETCH, nor when no command is in progress (e.g. between the
1264  * chunked FETCH commands). We previously tried to be robust by
1265  * setting:
1266  * msn_begin = mdata->max_msn + 1;
1267  * but with chunking (and the mythical header cache holes) this
1268  * may not be correct. So here we must assume the msn values have
1269  * not been altered during or after the fetch. */
1270  msn_begin = fetch_msn_end + 1;
1271  }
1272 
1273  retval = 0;
1274 
1275 bail:
1276  mutt_buffer_pool_release(&hdr_list);
1278  mutt_buffer_pool_release(&tempfile);
1279  mutt_file_fclose(&fp);
1280  FREE(&hdrreq);
1281  progress_free(&progress);
1282 
1283  return retval;
1284 }
1285 
1299 int imap_read_headers(struct Mailbox *m, unsigned int msn_begin,
1300  unsigned int msn_end, bool initial_download)
1301 {
1302  int oldmsgcount;
1303  unsigned int maxuid = 0;
1304  int retval = -1;
1305  bool evalhc = false;
1306 
1307 #ifdef USE_HCACHE
1308  void *uidvalidity = NULL;
1309  void *puid_next = NULL;
1310  unsigned int uid_next = 0;
1311  bool has_condstore = false;
1312  bool has_qresync = false;
1313  bool eval_condstore = false;
1314  bool eval_qresync = false;
1315  unsigned long long *pmodseq = NULL;
1316  unsigned long long hc_modseq = 0;
1317  char *uid_seqset = NULL;
1318 #endif /* USE_HCACHE */
1319 
1320  struct ImapAccountData *adata = imap_adata_get(m);
1321  struct ImapMboxData *mdata = imap_mdata_get(m);
1322  if (!adata || (adata->mailbox != m))
1323  return -1;
1324 
1325 #ifdef USE_HCACHE
1326 retry:
1327 #endif /* USE_HCACHE */
1328 
1329  /* make sure context has room to hold the mailbox */
1330  while (msn_end > m->email_max)
1331  mx_alloc_memory(m);
1332  imap_msn_reserve(&mdata->msn, msn_end);
1333  imap_alloc_uid_hash(adata, msn_end);
1334 
1335  oldmsgcount = m->msg_count;
1337  mdata->new_mail_count = 0;
1338 
1339 #ifdef USE_HCACHE
1340  imap_hcache_open(adata, mdata);
1341 
1342  if (mdata->hcache && initial_download)
1343  {
1344  size_t dlen = 0;
1345  uidvalidity = mutt_hcache_fetch_raw(mdata->hcache, "/UIDVALIDITY", 12, &dlen);
1346  puid_next = mutt_hcache_fetch_raw(mdata->hcache, "/UIDNEXT", 8, &dlen);
1347  if (puid_next)
1348  {
1349  uid_next = *(unsigned int *) puid_next;
1350  mutt_hcache_free_raw(mdata->hcache, &puid_next);
1351  }
1352 
1353  if (mdata->modseq)
1354  {
1355  const bool c_imap_condstore =
1356  cs_subset_bool(NeoMutt->sub, "imap_condstore");
1357  if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1358  has_condstore = true;
1359 
1360  /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1361  * If we receive an ENABLED response back, then adata->qresync is set. */
1362  if (adata->qresync)
1363  has_qresync = true;
1364  }
1365 
1366  if (uidvalidity && uid_next && (*(uint32_t *) uidvalidity == mdata->uidvalidity))
1367  {
1368  size_t dlen2 = 0;
1369  evalhc = true;
1370  pmodseq = mutt_hcache_fetch_raw(mdata->hcache, "/MODSEQ", 7, &dlen2);
1371  if (pmodseq)
1372  {
1373  hc_modseq = *pmodseq;
1374  mutt_hcache_free_raw(mdata->hcache, (void **) &pmodseq);
1375  }
1376  if (hc_modseq)
1377  {
1378  if (has_qresync)
1379  {
1380  uid_seqset = imap_hcache_get_uid_seqset(mdata);
1381  if (uid_seqset)
1382  eval_qresync = true;
1383  }
1384 
1385  if (!eval_qresync && has_condstore)
1386  eval_condstore = true;
1387  }
1388  }
1389  mutt_hcache_free_raw(mdata->hcache, &uidvalidity);
1390  }
1391  if (evalhc)
1392  {
1393  if (eval_qresync)
1394  {
1395  if (read_headers_qresync_eval_cache(adata, uid_seqset) < 0)
1396  goto bail;
1397  }
1398  else
1399  {
1400  if (read_headers_normal_eval_cache(adata, msn_end, uid_next, has_condstore || has_qresync,
1401  eval_condstore) < 0)
1402  goto bail;
1403  }
1404 
1405  if ((eval_condstore || eval_qresync) && (hc_modseq != mdata->modseq))
1406  {
1407  if (read_headers_condstore_qresync_updates(adata, msn_end, uid_next,
1408  hc_modseq, eval_qresync) < 0)
1409  {
1410  goto bail;
1411  }
1412  }
1413 
1414  /* Look for the first empty MSN and start there */
1415  while (msn_begin <= msn_end)
1416  {
1417  if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1418  break;
1419  msn_begin++;
1420  }
1421  }
1422 #endif /* USE_HCACHE */
1423 
1424  if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1425  goto bail;
1426 
1427 #ifdef USE_HCACHE
1428  if (eval_qresync && initial_download)
1429  {
1430  if (imap_verify_qresync(m) != 0)
1431  {
1432  eval_qresync = false;
1433  eval_condstore = false;
1434  evalhc = false;
1435  hc_modseq = 0;
1436  maxuid = 0;
1437  FREE(&uid_seqset);
1438  uidvalidity = NULL;
1439  uid_next = 0;
1440 
1441  goto retry;
1442  }
1443  }
1444 #endif /* USE_HCACHE */
1445 
1446  if (maxuid && (mdata->uid_next < maxuid + 1))
1447  mdata->uid_next = maxuid + 1;
1448 
1449 #ifdef USE_HCACHE
1450  mutt_hcache_store_raw(mdata->hcache, "/UIDVALIDITY", 12, &mdata->uidvalidity,
1451  sizeof(mdata->uidvalidity));
1452  if (maxuid && (mdata->uid_next < maxuid + 1))
1453  {
1454  mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1455  mdata->uid_next = maxuid + 1;
1456  }
1457  if (mdata->uid_next > 1)
1458  {
1459  mutt_hcache_store_raw(mdata->hcache, "/UIDNEXT", 8, &mdata->uid_next,
1460  sizeof(mdata->uid_next));
1461  }
1462 
1463  /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1464  * To do it more often, we'll need to deal with flag updates combined with
1465  * unsync'ed local flag changes. We'll also need to properly sync flags to
1466  * the header cache on close. I'm not sure it's worth the added complexity. */
1467  if (initial_download)
1468  {
1469  if (has_condstore || has_qresync)
1470  {
1471  mutt_hcache_store_raw(mdata->hcache, "/MODSEQ", 7, &mdata->modseq,
1472  sizeof(mdata->modseq));
1473  }
1474  else
1475  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1476 
1477  if (has_qresync)
1479  else
1481  }
1482 #endif /* USE_HCACHE */
1483 
1484  if (m->msg_count > oldmsgcount)
1485  {
1486  /* TODO: it's not clear to me why we are calling mx_alloc_memory
1487  * yet again. */
1488  mx_alloc_memory(m);
1489  }
1490 
1491  mdata->reopen |= IMAP_REOPEN_ALLOW;
1492 
1493  retval = msn_end;
1494 
1495 bail:
1496 #ifdef USE_HCACHE
1497  imap_hcache_close(mdata);
1498  FREE(&uid_seqset);
1499 #endif /* USE_HCACHE */
1500 
1501  return retval;
1502 }
1503 
1511 int imap_append_message(struct Mailbox *m, struct Message *msg)
1512 {
1513  if (!m || !msg)
1514  return -1;
1515 
1516  FILE *fp = NULL;
1517  char buf[1024 * 2];
1518  char internaldate[IMAP_DATELEN];
1519  char imap_flags[128];
1520  size_t len;
1521  struct Progress *progress = NULL;
1522  size_t sent;
1523  int c, last;
1524  int rc;
1525 
1526  struct ImapAccountData *adata = imap_adata_get(m);
1527  struct ImapMboxData *mdata = imap_mdata_get(m);
1528 
1529  fp = fopen(msg->path, "r");
1530  if (!fp)
1531  {
1532  mutt_perror(msg->path);
1533  goto fail;
1534  }
1535 
1536  /* currently we set the \Seen flag on all messages, but probably we
1537  * should scan the message Status header for flag info. Since we're
1538  * already rereading the whole file for length it isn't any more
1539  * expensive (it'd be nice if we had the file size passed in already
1540  * by the code that writes the file, but that's a lot of changes.
1541  * Ideally we'd have an Email structure with flag info here... */
1542  for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1543  {
1544  if ((c == '\n') && (last != '\r'))
1545  len++;
1546 
1547  len++;
1548  }
1549  rewind(fp);
1550 
1551  if (m->verbose)
1552  progress = progress_new(_("Uploading message..."), MUTT_PROGRESS_NET, len);
1553 
1554  mutt_date_make_imap(internaldate, sizeof(internaldate), msg->received);
1555 
1556  imap_flags[0] = '\0';
1557  imap_flags[1] = '\0';
1558 
1559  if (msg->flags.read)
1560  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Seen");
1561  if (msg->flags.replied)
1562  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Answered");
1563  if (msg->flags.flagged)
1564  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Flagged");
1565  if (msg->flags.draft)
1566  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Draft");
1567 
1568  snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1569  imap_flags + 1, internaldate, (unsigned long) len);
1570 
1571  imap_cmd_start(adata, buf);
1572 
1573  do
1574  {
1575  rc = imap_cmd_step(adata);
1576  } while (rc == IMAP_RES_CONTINUE);
1577 
1578  if (rc != IMAP_RES_RESPOND)
1579  goto cmd_step_fail;
1580 
1581  for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1582  {
1583  if ((c == '\n') && (last != '\r'))
1584  buf[len++] = '\r';
1585 
1586  buf[len++] = c;
1587 
1588  if (len > sizeof(buf) - 3)
1589  {
1590  sent += len;
1591  if (flush_buffer(buf, &len, adata->conn) < 0)
1592  goto fail;
1593  if (m->verbose)
1594  progress_update(progress, sent, -1);
1595  }
1596  }
1597 
1598  if (len)
1599  if (flush_buffer(buf, &len, adata->conn) < 0)
1600  goto fail;
1601 
1602  if (mutt_socket_send(adata->conn, "\r\n") < 0)
1603  goto fail;
1604  mutt_file_fclose(&fp);
1605 
1606  do
1607  {
1608  rc = imap_cmd_step(adata);
1609  } while (rc == IMAP_RES_CONTINUE);
1610 
1611  if (rc != IMAP_RES_OK)
1612  goto cmd_step_fail;
1613 
1614  progress_free(&progress);
1615  return 0;
1616 
1617 cmd_step_fail:
1618  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1619  if (rc != IMAP_RES_BAD)
1620  {
1621  char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1622  pc = imap_next_word(pc); /* skip response code */
1623  if (*pc != '\0')
1624  mutt_error("%s", pc);
1625  }
1626 
1627 fail:
1628  mutt_file_fclose(&fp);
1629  progress_free(&progress);
1630  return -1;
1631 }
1632 
1643 int imap_copy_messages(struct Mailbox *m, struct EmailList *el,
1644  const char *dest, enum MessageSaveOpt save_opt)
1645 {
1646  if (!m || !el || !dest)
1647  return -1;
1648 
1649  struct Buffer cmd, sync_cmd;
1650  char buf[PATH_MAX];
1651  char mbox[PATH_MAX];
1652  char mmbox[PATH_MAX];
1653  char prompt[PATH_MAX + 64];
1654  int rc;
1655  struct ConnAccount cac = { { 0 } };
1656  enum QuadOption err_continue = MUTT_NO;
1657  int triedcreate = 0;
1658  struct EmailNode *en = STAILQ_FIRST(el);
1659  bool single = !STAILQ_NEXT(en, entries);
1660  struct ImapAccountData *adata = imap_adata_get(m);
1661 
1662  if (single && en->email->attach_del)
1663  {
1664  mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1665  return 1;
1666  }
1667 
1668  if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1669  {
1670  mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1671  return -1;
1672  }
1673 
1674  /* check that the save-to folder is in the same account */
1675  if (!imap_account_match(&adata->conn->account, &cac))
1676  {
1677  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1678  return 1;
1679  }
1680 
1681  imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1682  if (*mbox == '\0')
1683  mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1684  imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1685 
1686  /* loop in case of TRYCREATE */
1687  do
1688  {
1689  mutt_buffer_init(&sync_cmd);
1690  mutt_buffer_init(&cmd);
1691 
1692  if (!single) /* copy tagged messages */
1693  {
1694  /* if any messages have attachments to delete, fall through to FETCH
1695  * and APPEND. TODO: Copy what we can with COPY, fall through for the
1696  * remainder. */
1697  STAILQ_FOREACH(en, el, entries)
1698  {
1699  if (en->email->attach_del)
1700  {
1702  "#2 Message contains attachments to be deleted\n");
1703  return 1;
1704  }
1705 
1706  if (en->email->active && en->email->changed)
1707  {
1708  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1709  if (rc < 0)
1710  {
1711  mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1712  goto out;
1713  }
1714  }
1715  }
1716 
1717  rc = imap_exec_msgset(m, "UID COPY", mmbox, MUTT_TAG, false, false);
1718  if (rc == 0)
1719  {
1720  mutt_debug(LL_DEBUG1, "No messages tagged\n");
1721  rc = -1;
1722  goto out;
1723  }
1724  else if (rc < 0)
1725  {
1726  mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1727  goto out;
1728  }
1729  else
1730  {
1731  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1732  rc, mbox);
1733  }
1734  }
1735  else
1736  {
1737  mutt_message(_("Copying message %d to %s..."), en->email->index + 1, mbox);
1738  mutt_buffer_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(en->email)->uid, mmbox);
1739 
1740  if (en->email->active && en->email->changed)
1741  {
1742  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1743  if (rc < 0)
1744  {
1745  mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1746  goto out;
1747  }
1748  }
1749  rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1750  if (rc != IMAP_EXEC_SUCCESS)
1751  {
1752  mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1753  goto out;
1754  }
1755  }
1756 
1757  /* let's get it on */
1758  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1759  if (rc == IMAP_EXEC_ERROR)
1760  {
1761  if (triedcreate)
1762  {
1763  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1764  break;
1765  }
1766  /* bail out if command failed for reasons other than nonexistent target */
1767  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1768  break;
1769  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1770  snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1771  const bool c_confirm_create =
1772  cs_subset_bool(NeoMutt->sub, "confirm_create");
1773  if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1774  {
1775  mutt_clear_error();
1776  goto out;
1777  }
1778  if (imap_create_mailbox(adata, mbox) < 0)
1779  break;
1780  triedcreate = 1;
1781  }
1782  } while (rc == IMAP_EXEC_ERROR);
1783 
1784  if (rc != 0)
1785  {
1786  imap_error("imap_copy_messages", adata->buf);
1787  goto out;
1788  }
1789 
1790  /* cleanup */
1791  if (save_opt == SAVE_MOVE)
1792  {
1793  const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
1794  STAILQ_FOREACH(en, el, entries)
1795  {
1796  mutt_set_flag(m, en->email, MUTT_DELETE, true);
1797  mutt_set_flag(m, en->email, MUTT_PURGE, true);
1798  if (c_delete_untag)
1799  mutt_set_flag(m, en->email, MUTT_TAG, false);
1800  }
1801  }
1802 
1803  rc = 0;
1804 
1805 out:
1806  FREE(&cmd.data);
1807  FREE(&sync_cmd.data);
1808 
1809  return (rc < 0) ? -1 : rc;
1810 }
1811 
1819 int imap_cache_del(struct Mailbox *m, struct Email *e)
1820 {
1821  struct ImapAccountData *adata = imap_adata_get(m);
1822  struct ImapMboxData *mdata = imap_mdata_get(m);
1823 
1824  if (!e || !adata || (adata->mailbox != m))
1825  return -1;
1826 
1827  mdata->bcache = msg_cache_open(m);
1828  char id[64];
1829  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1830  return mutt_bcache_del(mdata->bcache, id);
1831 }
1832 
1839 {
1840  struct ImapAccountData *adata = imap_adata_get(m);
1841  struct ImapMboxData *mdata = imap_mdata_get(m);
1842 
1843  if (!adata || (adata->mailbox != m))
1844  return -1;
1845 
1846  mdata->bcache = msg_cache_open(m);
1847  mutt_bcache_list(mdata->bcache, msg_cache_clean_cb, mdata);
1848 
1849  return 0;
1850 }
1851 
1870 char *imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
1871 {
1872  struct ImapAccountData *adata = imap_adata_get(m);
1873  if (!adata || (adata->mailbox != m))
1874  return NULL;
1875 
1876  struct ImapHeader newh = { 0 };
1877  struct ImapEmailData old_edata = { 0 };
1878  int local_changes = e->changed;
1879 
1880  struct ImapEmailData *edata = e->edata;
1881  newh.edata = edata;
1882 
1883  mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1884  s = msg_parse_flags(&newh, s);
1885  if (!s)
1886  return NULL;
1887 
1888  /* Update tags system */
1889  /* We take a copy of the tags so we can split the string */
1890  char *tags_copy = mutt_str_dup(edata->flags_remote);
1891  driver_tags_replace(&e->tags, tags_copy);
1892  FREE(&tags_copy);
1893 
1894  /* YAUH (yet another ugly hack): temporarily set context to
1895  * read-write even if it's read-only, so *server* updates of
1896  * flags can be processed by mutt_set_flag. mailbox->changed must
1897  * be restored afterwards */
1898  bool readonly = m->readonly;
1899  m->readonly = false;
1900 
1901  /* This is redundant with the following two checks. Removing:
1902  * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old)); */
1903  set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1904  edata->old, e->old);
1905  set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1906  old_edata.read, edata->read, e->read);
1907  set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1908  old_edata.deleted, edata->deleted, e->deleted);
1909  set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1910  old_edata.flagged, edata->flagged, e->flagged);
1911  set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1912  old_edata.replied, edata->replied, e->replied);
1913 
1914  /* this message is now definitively *not* changed (mutt_set_flag
1915  * marks things changed as a side-effect) */
1916  if (local_changes == 0)
1917  e->changed = false;
1918  m->changed &= !readonly;
1919  m->readonly = readonly;
1920 
1921  return s;
1922 }
1923 
1927 bool imap_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1928 {
1929  struct Envelope *newenv = NULL;
1930  char buf[1024];
1931  char *pc = NULL;
1932  unsigned int bytes;
1933  struct Progress *progress = NULL;
1934  unsigned int uid;
1935  bool retried = false;
1936  bool read;
1937  int rc;
1938 
1939  /* Sam's weird courier server returns an OK response even when FETCH
1940  * fails. Thanks Sam. */
1941  bool fetched = false;
1942 
1943  struct ImapAccountData *adata = imap_adata_get(m);
1944 
1945  if (!adata || (adata->mailbox != m))
1946  return false;
1947 
1948  struct Email *e = m->emails[msgno];
1949  if (!e)
1950  return false;
1951 
1952  msg->fp = msg_cache_get(m, e);
1953  if (msg->fp)
1954  {
1955  if (imap_edata_get(e)->parsed)
1956  return true;
1957  goto parsemsg;
1958  }
1959 
1960  /* This function is called in a few places after endwin()
1961  * e.g. mutt_pipe_message(). */
1962  bool output_progress = !isendwin() && m->verbose;
1963  if (output_progress)
1964  mutt_message(_("Fetching message..."));
1965 
1966  msg->fp = msg_cache_put(m, e);
1967  if (!msg->fp)
1968  {
1969  struct Buffer *path = mutt_buffer_pool_get();
1970  mutt_buffer_mktemp(path);
1971  msg->fp = mutt_file_fopen(mutt_buffer_string(path), "w+");
1972  unlink(mutt_buffer_string(path));
1973  mutt_buffer_pool_release(&path);
1974 
1975  if (!msg->fp)
1976  return false;
1977  }
1978 
1979  /* mark this header as currently inactive so the command handler won't
1980  * also try to update it. HACK until all this code can be moved into the
1981  * command handler */
1982  e->active = false;
1983 
1984  const bool c_imap_peek = cs_subset_bool(NeoMutt->sub, "imap_peek");
1985  snprintf(buf, sizeof(buf), "UID FETCH %u %s", imap_edata_get(e)->uid,
1986  ((adata->capabilities & IMAP_CAP_IMAP4REV1) ?
1987  (c_imap_peek ? "BODY.PEEK[]" : "BODY[]") :
1988  "RFC822"));
1989 
1990  imap_cmd_start(adata, buf);
1991  do
1992  {
1993  rc = imap_cmd_step(adata);
1994  if (rc != IMAP_RES_CONTINUE)
1995  break;
1996 
1997  pc = adata->buf;
1998  pc = imap_next_word(pc);
1999  pc = imap_next_word(pc);
2000 
2001  if (mutt_istr_startswith(pc, "FETCH"))
2002  {
2003  while (*pc)
2004  {
2005  pc = imap_next_word(pc);
2006  if (pc[0] == '(')
2007  pc++;
2008  if (mutt_istr_startswith(pc, "UID"))
2009  {
2010  pc = imap_next_word(pc);
2011  if (mutt_str_atoui(pc, &uid) < 0)
2012  goto bail;
2013  if (uid != imap_edata_get(e)->uid)
2014  {
2015  mutt_error(_(
2016  "The message index is incorrect. Try reopening the mailbox."));
2017  }
2018  }
2019  else if (mutt_istr_startswith(pc, "RFC822") || mutt_istr_startswith(pc, "BODY[]"))
2020  {
2021  pc = imap_next_word(pc);
2022  if (imap_get_literal_count(pc, &bytes) < 0)
2023  {
2024  imap_error("imap_msg_open()", buf);
2025  goto bail;
2026  }
2027  if (output_progress)
2028  {
2029  progress = progress_new(_("Fetching message..."), MUTT_PROGRESS_NET, bytes);
2030  }
2031  if (imap_read_literal(msg->fp, adata, bytes, output_progress ? progress : NULL) < 0)
2032  {
2033  goto bail;
2034  }
2035  /* pick up trailing line */
2036  rc = imap_cmd_step(adata);
2037  if (rc != IMAP_RES_CONTINUE)
2038  goto bail;
2039  pc = adata->buf;
2040 
2041  fetched = true;
2042  }
2043  /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
2044  * change (eg from \Unseen to \Seen).
2045  * Uncommitted changes in neomutt take precedence. If we decide to
2046  * incrementally update flags later, this won't stop us syncing */
2047  else if (!e->changed && mutt_istr_startswith(pc, "FLAGS"))
2048  {
2049  pc = imap_set_flags(m, e, pc, NULL);
2050  if (!pc)
2051  goto bail;
2052  }
2053  }
2054  }
2055  } while (rc == IMAP_RES_CONTINUE);
2056 
2057  /* see comment before command start. */
2058  e->active = true;
2059 
2060  fflush(msg->fp);
2061  if (ferror(msg->fp))
2062  goto bail;
2063 
2064  if (rc != IMAP_RES_OK)
2065  goto bail;
2066 
2067  if (!fetched || !imap_code(adata->buf))
2068  goto bail;
2069 
2070  msg_cache_commit(m, e);
2071 
2072 parsemsg:
2073  /* Update the header information. Previously, we only downloaded a
2074  * portion of the headers, those required for the main display. */
2075  rewind(msg->fp);
2076  /* It may be that the Status header indicates a message is read, but the
2077  * IMAP server doesn't know the message has been \Seen. So we capture
2078  * the server's notion of 'read' and if it differs from the message info
2079  * picked up in mutt_rfc822_read_header, we mark the message (and context
2080  * changed). Another possibility: ignore Status on IMAP? */
2081  read = e->read;
2082  newenv = mutt_rfc822_read_header(msg->fp, e, false, false);
2083  mutt_env_merge(e->env, &newenv);
2084 
2085  /* see above. We want the new status in e->read, so we unset it manually
2086  * and let mutt_set_flag set it correctly, updating context. */
2087  if (read != e->read)
2088  {
2089  e->read = read;
2090  mutt_set_flag(m, e, MUTT_NEW, read);
2091  }
2092 
2093  e->lines = 0;
2094  fgets(buf, sizeof(buf), msg->fp);
2095  while (!feof(msg->fp))
2096  {
2097  e->lines++;
2098  fgets(buf, sizeof(buf), msg->fp);
2099  }
2100 
2101  e->body->length = ftell(msg->fp) - e->body->offset;
2102 
2103  mutt_clear_error();
2104  rewind(msg->fp);
2105  imap_edata_get(e)->parsed = true;
2106 
2107  /* retry message parse if cached message is empty */
2108  if (!retried && ((e->lines == 0) || (e->body->length == 0)))
2109  {
2110  imap_cache_del(m, e);
2111  retried = true;
2112  goto parsemsg;
2113  }
2114 
2115  progress_free(&progress);
2116  return true;
2117 
2118 bail:
2119  e->active = true;
2120  mutt_file_fclose(&msg->fp);
2121  imap_cache_del(m, e);
2122  progress_free(&progress);
2123  return false;
2124 }
2125 
2131 int imap_msg_commit(struct Mailbox *m, struct Message *msg)
2132 {
2133  int rc = mutt_file_fclose(&msg->fp);
2134  if (rc != 0)
2135  return rc;
2136 
2137  return imap_append_message(m, msg);
2138 }
2139 
2145 int imap_msg_close(struct Mailbox *m, struct Message *msg)
2146 {
2147  return mutt_file_fclose(&msg->fp);
2148 }
2149 
2153 int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
2154 {
2155  int rc = 0;
2156 #ifdef USE_HCACHE
2157  bool close_hc = true;
2158  struct ImapAccountData *adata = imap_adata_get(m);
2159  struct ImapMboxData *mdata = imap_mdata_get(m);
2160  if (!mdata || !adata)
2161  return -1;
2162  if (mdata->hcache)
2163  close_hc = false;
2164  else
2165  imap_hcache_open(adata, mdata);
2166  rc = imap_hcache_put(mdata, e);
2167  if (close_hc)
2168  imap_hcache_close(mdata);
2169 #endif
2170  return rc;
2171 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
time_t received
Definition: message.h:36
#define mutt_socket_write_n(conn, buf, len)
Definition: mutt_socket.h:39
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:123
int lines
How many lines in the body of this message?
Definition: email.h:85
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:485
#define IMAP_RES_RESPOND
+
Definition: private.h:58
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
Save message to the header cache - Implements MxOps::msg_save_hcache() -.
Definition: message.c:2153
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
static int read_headers_normal_eval_cache(struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, bool store_flag_updates, bool eval_condstore)
Retrieve data from the header cache.
Definition: message.c:660
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Imap-specific Email data.
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
uint32_t uidvalidity
Definition: mdata.h:50
struct Body * body
List of MIME parts.
Definition: email.h:91
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
void mailbox_size_add(struct Mailbox *m, const struct Email *e)
Add an email&#39;s size to the total size of a Mailbox.
Definition: mailbox.c:223
Structs that make up an email.
#define mutt_error(...)
Definition: logging.h:88
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:143
bool replied
Definition: mxapi.h:51
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:449
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:67
static int msg_parse_fetch(struct ImapHeader *h, char *s)
handle headers returned from header fetch
Definition: message.c:296
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition: util.c:483
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:381
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: message.c:2131
size_t idx
Definition: mailbox.c:257
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:66
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:668
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition: util.c:914
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition: bcache.c:247
long content_length
Definition: message.h:37
static int msg_cache_clean_cb(const char *id, struct BodyCache *bcache, void *data)
Delete an entry from the message cache - Implements bcache_list_t.
Definition: message.c:168
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
Store a UID Sequence Set in the header cache.
Definition: util.c:416
static FILE * msg_cache_put(struct Mailbox *m, struct Email *e)
Put an email into the message cache.
Definition: message.c:128
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1081
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1819
String manipulation buffer.
Definition: buffer.h:33
Flagged messages.
Definition: mutt.h:98
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1212
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
static FILE * msg_cache_get(struct Mailbox *m, struct Email *e)
Get the message cache entry for an email.
Definition: message.c:107
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
#define _(a)
Definition: message.h:28
Progress tracks elements, according to $read_inc
Definition: lib.h:46
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:54
bool changed
Email has been edited.
Definition: email.h:48
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
bool imap_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox - Implements MxOps::msg_open() -.
Definition: message.c:1927
Messages to be purged (bypass trash)
Definition: mutt.h:96
int mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: string.c:188
struct HeaderCache * hcache
Definition: mdata.h:62
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1049
Imap-specific Account data.
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1065
void mutt_hcache_free_raw(struct HeaderCache *hc, void **data)
Multiplexor for StoreOps::free.
Definition: hcache.c:541
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition: util.c:452
IMAP MSN helper functions.
int imap_read_literal(FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *pbar)
Read bytes bytes from server into file.
Definition: imap.c:600
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition: hash.c:277
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:384
A Progress Bar.
Definition: progress.c:47
static unsigned int imap_fetch_msn_seqset(struct Buffer *buf, struct ImapAccountData *adata, bool evalhc, unsigned int msn_begin, unsigned int msn_end, unsigned int *fetch_msn_end)
Generate a sequence set.
Definition: message.c:528
Messages that have been replied to.
Definition: mutt.h:91
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:35
Convenience wrapper for the config headers.
struct HashElem * mutt_hash_int_insert(struct HashTable *table, unsigned int intkey, void *data)
Add a new element to the Hash Table (with integer keys)
Definition: hash.c:339
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:329
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
bool flagged
Definition: edata.h:39
struct BodyCache * bcache
Definition: mdata.h:60
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
void imap_msn_free(struct MSN *msn)
Free the cache.
Definition: msn.c:58
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
Manage IMAP messages.
void mutt_env_merge(struct Envelope *base, struct Envelope **extra)
Merge the headers of two Envelopes.
Definition: envelope.c:151
bool deleted
Definition: edata.h:38
Progress bar.
Imap-specific Mailbox data.
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition: message.c:469
bool read
Email is read.
Definition: email.h:51
Header cache multiplexor.
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:340
MessageType
To set flags or match patterns.
Definition: mutt.h:85
Many unsorted constants and some structs.
Log at debug level 2.
Definition: logging.h:41
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:179
API for mailboxes.
bool old
Email is seen, but unread.
Definition: email.h:50
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:79
#define STAILQ_INIT(head)
Definition: queue.h:372
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
static int msg_fetch_header(struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
import IMAP FETCH response into an ImapHeader
Definition: message.c:405
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
int imap_read_headers(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
Read headers from the server.
Definition: message.c:1299
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition: message.c:509
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
static void set_changed_flag(struct Mailbox *m, struct Email *e, int local_changes, bool *server_changes, enum MessageType flag_name, bool old_hd_flag, bool new_hd_flag, bool h_flag)
Have the flags of an email changed.
Definition: message.c:620
struct Envelope * env
Envelope information.
Definition: email.h:90
int imap_copy_messages(struct Mailbox *m, struct EmailList *el, const char *dest, enum MessageSaveOpt save_opt)
Server COPY messages to another folder.
Definition: message.c:1643
Convenience wrapper for the core headers.
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: mdata.h:45
#define SKIPWS(ch)
Definition: string2.h:46
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
char delim
Definition: adata.h:75
Shared constants/structs that are private to IMAP.
void * mdata
Driver specific data.
Definition: mailbox.h:136
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
unsigned long long modseq
Definition: mdata.h:52
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1250
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void imap_msn_reserve(struct MSN *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:40
Body Caching - local copies of email bodies.
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition: bcache.c:207
char * flags_remote
Definition: edata.h:48
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition: imap.c:1004
struct HashTable * uid_hash
Definition: mdata.h:58
Old messages.
Definition: mutt.h:90
void imap_error(const char *where, const char *msg)
show an error and abort
Definition: util.c:665
Prototypes for many functions.
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:124
bool active
Message is not to be removed.
Definition: email.h:59
bool flagged
Definition: mxapi.h:50
A local copy of an email.
Definition: mxapi.h:41
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
unsigned int uid
32-bit Message UID
Definition: edata.h:44
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
Messages to be deleted.
Definition: mutt.h:94
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:40
IMAP-specific header.
Definition: message.h:32
struct Message::@1 flags
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Manage where the email is piped to external commands.
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1159
Tagged messages.
Definition: mutt.h:99
char * data
Pointer to data.
Definition: buffer.h:35
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:121
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:637
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:927
bool qresync
true, if QRESYNC is successfully ENABLE&#39;d
Definition: adata.h:63
New messages.
Definition: mutt.h:89
static struct BodyCache * msg_cache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c:79
Messages that have been read.
Definition: mutt.h:92
struct ColorLineList hdr_list
List of colours applied to the email headers.
Definition: color.c:81
bool replied
Definition: edata.h:40
char * name
Mailbox name.
Definition: mdata.h:40
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool old
Definition: edata.h:37
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
Ask the user a question.
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:264
bool draft
Definition: mxapi.h:52
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
ImapCapFlags capabilities
Definition: adata.h:55
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
Progress tracks bytes, according to $net_inc
Definition: lib.h:48
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool evalhc, unsigned int *maxuid, bool initial_download)
Retrieve new messages from the server.
Definition: message.c:1050
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:90
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:448
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Login details for a remote server.
Definition: connaccount.h:51
#define STAILQ_NEXT(elm, field)
Definition: queue.h:400
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition: message.c:806
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:228
void progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:175
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:180
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1838
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
Imap command failure.
Definition: private.h:85
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
Local cache of email bodies.
Definition: bcache.c:50
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: message.c:2145
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
int imap_get_literal_count(const char *buf, unsigned int *bytes)
write number of bytes in an IMAP literal into bytes
Definition: util.c:748
IMAP-specific Account data -.
Definition: adata.h:39
int imap_append_message(struct Mailbox *m, struct Message *msg)
Write an email back to the server.
Definition: message.c:1511
unsigned int uid_next
Definition: mdata.h:51
static int read_headers_condstore_qresync_updates(struct ImapAccountData *adata, unsigned int msn_end, unsigned int uid_next, unsigned long long hc_modseq, bool eval_qresync)
Retrieve updates from the server.
Definition: message.c:878
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:775
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:356
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:137
Move message to another mailbox, removing the original.
Definition: commands.h:52
Log at debug level 1.
Definition: logging.h:40
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:687
bool flagged
Marked important?
Definition: email.h:43
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
char * flags_system
Definition: edata.h:47
IMAP-specific Mailbox data -.
Definition: mdata.h:38
char * buf
Definition: adata.h:59
int msg_new
Number of new messages.
Definition: mailbox.h:95
bool deleted
Email is deleted.
Definition: email.h:45
void * edata
Driver-specific data.
Definition: email.h:111
struct Email * email
Email in the list.
Definition: email.h:131
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:246
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
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition: util.c:719
Connection Library.
FILE * fp
pointer to the message data
Definition: mxapi.h:43
MessageSaveOpt
Message save option.
Definition: commands.h:49
int index
The absolute (unsorted) message number.
Definition: email.h:86
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
bool read
Definition: edata.h:36
int mutt_date_make_imap(char *buf, size_t buflen, time_t timestamp)
Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:557
char * imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
fill the message header according to the server flags
Definition: message.c:1870
time_t received
the time at which this message was received
Definition: mxapi.h:54
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition: message.c:977
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
List of Emails.
Definition: email.h:129
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
Hundreds of global variables to back the user variables.
int mutt_hcache_store_raw(struct HeaderCache *hc, const char *key, size_t keylen, void *data, size_t dlen)
store a key / data pair
Definition: hcache.c:615
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
IMAP-specific Email data -.
Definition: edata.h:33
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:186
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:600
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1128
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
NeoMutt connections.
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:91
static char * msg_parse_flags(struct ImapHeader *h, char *s)
read a FLAGS token into an ImapHeader
Definition: message.c:191
void * mutt_hcache_fetch_raw(struct HeaderCache *hc, const char *key, size_t keylen, size_t *dlen)
Fetch a message&#39;s header from the cache.
Definition: hcache.c:521
Convenience wrapper for the library headers.
#define IMAP_DATELEN
Definition: private.h:90
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
bool read
Definition: mxapi.h:49
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:466
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
char * munge_name
Munged version of the mailbox name.
Definition: mdata.h:41
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1124
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:297
long cs_subset_long(const struct ConfigSubset *sub, const char *name)
Get a long config item by name.
Definition: helpers.c:121
bool parsed
Definition: edata.h:42
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:100
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1102
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1202
Log at debug level 3.
Definition: logging.h:42
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
UID Sequence Set Iterator.
Definition: private.h:168
char * path
path to temp file
Definition: mxapi.h:44
The header of an Email.
Definition: envelope.h:54
int msgno
Number displayed to the user.
Definition: email.h:87
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
static int msg_cache_commit(struct Mailbox *m, struct Email *e)
Add to the message cache.
Definition: message.c:149
struct Connection * conn
Definition: adata.h:41
struct ImapEmailData * edata
Definition: message.h:34
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55