NeoMutt  2021-10-29-43-g6b8931
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))
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))
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 */
416  if (!mutt_str_atoui(buf, &ih->edata->msn))
417  return rc;
418 
419  /* find FETCH tag */
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. */
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;
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
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 
763  /* We take a copy of the tags so we can split the string */
764  char *tags_copy = mutt_str_dup(h.edata->flags_remote);
765  driver_tags_replace(&e->tags, tags_copy);
766  FREE(&tags_copy);
767 
768  m->msg_count++;
769  mailbox_size_add(m, e);
770 
771  /* If this is the first time we are fetching, we need to
772  * store the current state of flags back into the header cache */
773  if (!eval_condstore && store_flag_updates)
774  imap_hcache_put(mdata, e);
775 
776  h.edata = NULL;
777  idx++;
778  }
779  } while (mfhrc == -1);
780 
781  imap_edata_free((void **) &h.edata);
782 
783  if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
784  goto fail;
785  }
786 
787  rc = 0;
788 fail:
789  progress_free(&progress);
790  return rc;
791 }
792 
805 static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
806 {
807  int rc;
808  unsigned int uid = 0;
809 
810  mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
811  struct Mailbox *m = adata->mailbox;
812  struct ImapMboxData *mdata = adata->mailbox->mdata;
813  unsigned int msn = 1;
814 
815  if (m->verbose)
816  mutt_message(_("Evaluating cache..."));
817 
818  struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
819  if (!iter)
820  return -1;
821 
822  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
823  {
824  /* The seqset may contain more headers than the fetch request, so
825  * we need to watch and reallocate the context and msn_index */
826  imap_msn_reserve(&mdata->msn, msn);
827 
828  struct Email *e = imap_hcache_get(mdata, uid);
829  if (e)
830  {
831  imap_msn_set(&mdata->msn, msn - 1, e);
832 
833  if (m->msg_count >= m->email_max)
834  mx_alloc_memory(m);
835 
836  struct ImapEmailData *edata = imap_edata_new();
837  e->edata = edata;
839 
840  e->index = uid;
841  e->active = true;
842  e->changed = false;
843  edata->read = e->read;
844  edata->old = e->old;
845  edata->deleted = e->deleted;
846  edata->flagged = e->flagged;
847  edata->replied = e->replied;
848 
849  edata->msn = msn;
850  edata->uid = uid;
851  mutt_hash_int_insert(mdata->uid_hash, uid, e);
852 
853  mailbox_size_add(m, e);
854  m->emails[m->msg_count++] = e;
855 
856  msn++;
857  }
858  }
859 
861 
862  return rc;
863 }
864 
878  unsigned int msn_end, unsigned int uid_next,
879  unsigned long long hc_modseq, bool eval_qresync)
880 {
881  struct Progress *progress = NULL;
882  char buf[1024];
883  unsigned int header_msn = 0;
884 
885  struct Mailbox *m = adata->mailbox;
886  struct ImapMboxData *mdata = imap_mdata_get(m);
887 
888  if (m->verbose)
889  {
890  /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
891  progress = progress_new(_("Fetching flag updates..."), MUTT_PROGRESS_READ, msn_end);
892  }
893 
894  snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
895  uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
896 
897  imap_cmd_start(adata, buf);
898 
899  int rc = IMAP_RES_CONTINUE;
900  for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
901  {
902  if (SigInt && query_abort_header_download(adata))
903  goto fail;
904 
905  if (m->verbose)
906  progress_update(progress, msgno, -1);
907 
908  /* cmd_parse_fetch will update the flags */
909  rc = imap_cmd_step(adata);
910  if (rc != IMAP_RES_CONTINUE)
911  break;
912 
913  /* so we just need to grab the header and persist it back into
914  * the header cache */
915  char *fetch_buf = adata->buf;
916  if (fetch_buf[0] != '*')
917  continue;
918 
919  fetch_buf = imap_next_word(fetch_buf);
920  if (!isdigit((unsigned char) *fetch_buf) || !mutt_str_atoui(fetch_buf, &header_msn))
921  continue;
922 
923  if ((header_msn < 1) || (header_msn > msn_end) ||
924  !imap_msn_get(&mdata->msn, header_msn - 1))
925  {
926  mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
927  header_msn);
928  continue;
929  }
930 
931  imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
932  }
933 
934  if (rc != IMAP_RES_OK)
935  goto fail;
936 
937  /* The IMAP flag setting as part of cmd_parse_fetch() ends up
938  * flipping these on. */
939  mdata->check_status &= ~IMAP_FLAGS_PENDING;
940  m->changed = false;
941 
942  /* VANISHED handling: we need to empty out the messages */
943  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
944  {
947 
948  imap_hcache_open(adata, mdata);
949  mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
950  }
951 
952  /* undo expunge count updates.
953  * ctx_update() will do this at the end of the header fetch. */
954  m->vcount = 0;
955  m->msg_tagged = 0;
956  m->msg_deleted = 0;
957  m->msg_new = 0;
958  m->msg_unread = 0;
959  m->msg_flagged = 0;
960  m->changed = false;
961 
962  rc = 0;
963 fail:
964  progress_free(&progress);
965  return rc;
966 }
967 
976 static int imap_verify_qresync(struct Mailbox *m)
977 {
978  assert(m);
979  struct ImapAccountData *adata = imap_adata_get(m);
980  struct ImapMboxData *mdata = imap_mdata_get(m);
981  if (!adata || (adata->mailbox != m))
982  return -1;
983 
984  const size_t max_msn = imap_msn_highest(&mdata->msn);
985 
986  unsigned int msn;
987  unsigned int uid;
988  struct Email *e = NULL;
989  struct Email *uidh = NULL;
990 
991  for (int i = 0; i < m->msg_count; i++)
992  {
993  e = m->emails[i];
994  const struct ImapEmailData *edata = imap_edata_get(e);
995  if (!edata)
996  goto fail;
997 
998  msn = imap_edata_get(e)->msn;
999  uid = imap_edata_get(e)->uid;
1000 
1001  if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1002  goto fail;
1003 
1004  uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1005  if (uidh != e)
1006  goto fail;
1007  }
1008 
1009  return 0;
1010 
1011 fail:
1012  imap_msn_free(&mdata->msn);
1013  mutt_hash_free(&mdata->uid_hash);
1014 
1015  for (int i = 0; i < m->msg_count; i++)
1016  {
1017  if (m->emails[i] && m->emails[i]->edata)
1018  imap_edata_free(&m->emails[i]->edata);
1019  email_free(&m->emails[i]);
1020  }
1021  m->msg_count = 0;
1022  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1024  imap_hcache_close(mdata);
1025 
1026  if (m->verbose)
1027  {
1028  /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1029  sanity check. If that fails, Mutt reopens the mailbox using a normal
1030  download. */
1031  mutt_error(_("QRESYNC failed. Reopening mailbox."));
1032  }
1033  return -1;
1034 }
1035 
1036 #endif /* USE_HCACHE */
1037 
1049 static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin,
1050  unsigned int msn_end, bool evalhc,
1051  unsigned int *maxuid, bool initial_download)
1052 {
1053  int rc, mfhrc = 0, retval = -1;
1054  unsigned int fetch_msn_end = 0;
1055  struct Progress *progress = NULL;
1056  char *hdrreq = NULL;
1057  struct Buffer *tempfile = NULL;
1058  FILE *fp = NULL;
1059  struct ImapHeader h;
1060  struct Buffer *buf = NULL;
1061  static const char *const want_headers =
1062  "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE "
1063  "CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST "
1064  "LIST-SUBSCRIBE LIST-UNSUBSCRIBE X-LABEL X-ORIGINAL-TO";
1065 
1066  struct ImapAccountData *adata = imap_adata_get(m);
1067  struct ImapMboxData *mdata = imap_mdata_get(m);
1068  int idx = m->msg_count;
1069 
1070  if (!adata || (adata->mailbox != m))
1071  return -1;
1072 
1073  struct Buffer *hdr_list = mutt_buffer_pool_get();
1074  mutt_buffer_strcpy(hdr_list, want_headers);
1075  const char *const c_imap_headers =
1076  cs_subset_string(NeoMutt->sub, "imap_headers");
1077  if (c_imap_headers)
1078  {
1079  mutt_buffer_addch(hdr_list, ' ');
1080  mutt_buffer_addstr(hdr_list, c_imap_headers);
1081  }
1082 #ifdef USE_AUTOCRYPT
1083  const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1084  if (c_autocrypt)
1085  {
1086  mutt_buffer_addch(hdr_list, ' ');
1087  mutt_buffer_addstr(hdr_list, "AUTOCRYPT");
1088  }
1089 #endif
1090 
1091  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1092  {
1093  mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", mutt_buffer_string(hdr_list));
1094  }
1095  else if (adata->capabilities & IMAP_CAP_IMAP4)
1096  {
1097  mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", mutt_buffer_string(hdr_list));
1098  }
1099  else
1100  { /* Unable to fetch headers for lower versions */
1101  mutt_error(_("Unable to fetch headers from this IMAP server version"));
1102  goto bail;
1103  }
1104 
1105  mutt_buffer_pool_release(&hdr_list);
1106 
1107  /* instead of downloading all headers and then parsing them, we parse them
1108  * as they come in. */
1109  tempfile = mutt_buffer_pool_get();
1110  mutt_buffer_mktemp(tempfile);
1111  fp = mutt_file_fopen(mutt_buffer_string(tempfile), "w+");
1112  if (!fp)
1113  {
1114  mutt_error(_("Could not create temporary file %s"), mutt_buffer_string(tempfile));
1115  goto bail;
1116  }
1117  unlink(mutt_buffer_string(tempfile));
1118  mutt_buffer_pool_release(&tempfile);
1119 
1120  if (m->verbose)
1121  {
1122  progress = progress_new(_("Fetching message headers..."), MUTT_PROGRESS_READ, msn_end);
1123  }
1124 
1125  buf = mutt_buffer_pool_get();
1126 
1127  /* NOTE:
1128  * The (fetch_msn_end < msn_end) used to be important to prevent
1129  * an infinite loop, in the event the server did not return all
1130  * the headers (due to a pending expunge, for example).
1131  *
1132  * I believe the new chunking imap_fetch_msn_seqset()
1133  * implementation and "msn_begin = fetch_msn_end + 1" assignment
1134  * at the end of the loop makes the comparison unneeded, but to be
1135  * cautious I'm keeping it.
1136  */
1137  while ((fetch_msn_end < msn_end) &&
1138  imap_fetch_msn_seqset(buf, adata, evalhc, msn_begin, msn_end, &fetch_msn_end))
1139  {
1140  char *cmd = NULL;
1141  mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1142  mutt_buffer_string(buf), hdrreq);
1143  imap_cmd_start(adata, cmd);
1144  FREE(&cmd);
1145 
1146  rc = IMAP_RES_CONTINUE;
1147  for (int msgno = msn_begin; rc == IMAP_RES_CONTINUE; msgno++)
1148  {
1149  if (initial_download && SigInt && query_abort_header_download(adata))
1150  goto bail;
1151 
1152  if (m->verbose)
1153  progress_update(progress, msgno, -1);
1154 
1155  rewind(fp);
1156  memset(&h, 0, sizeof(h));
1157  h.edata = imap_edata_new();
1158 
1159  /* this DO loop does two things:
1160  * 1. handles untagged messages, so we can try again on the same msg
1161  * 2. fetches the tagged response at the end of the last message. */
1162  do
1163  {
1164  rc = imap_cmd_step(adata);
1165  if (rc != IMAP_RES_CONTINUE)
1166  break;
1167 
1168  mfhrc = msg_fetch_header(m, &h, adata->buf, fp);
1169  if (mfhrc < 0)
1170  continue;
1171 
1172  if (!ftello(fp))
1173  {
1174  mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1175  continue;
1176  }
1177 
1178  /* make sure we don't get remnants from older larger message headers */
1179  fputs("\n\n", fp);
1180 
1181  if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1182  {
1183  mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1184  h.edata->msn);
1185  continue;
1186  }
1187 
1188  /* May receive FLAGS updates in a separate untagged response */
1189  if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1190  {
1191  mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1192  h.edata->msn);
1193  continue;
1194  }
1195 
1196  struct Email *e = email_new();
1197  m->emails[idx] = e;
1198 
1199  imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
1200  mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
1201 
1202  e->index = h.edata->uid;
1203  /* messages which have not been expunged are ACTIVE (borrowed from mh
1204  * folders) */
1205  e->active = true;
1206  e->changed = false;
1207  e->read = h.edata->read;
1208  e->old = h.edata->old;
1209  e->deleted = h.edata->deleted;
1210  e->flagged = h.edata->flagged;
1211  e->replied = h.edata->replied;
1212  e->received = h.received;
1213  e->edata = (void *) (h.edata);
1215  STAILQ_INIT(&e->tags);
1216 
1217  /* We take a copy of the tags so we can split the string */
1218  char *tags_copy = mutt_str_dup(h.edata->flags_remote);
1219  driver_tags_replace(&e->tags, tags_copy);
1220  FREE(&tags_copy);
1221 
1222  if (*maxuid < h.edata->uid)
1223  *maxuid = h.edata->uid;
1224 
1225  rewind(fp);
1226  /* NOTE: if Date: header is missing, mutt_rfc822_read_header depends
1227  * on h.received being set */
1228  e->env = mutt_rfc822_read_header(fp, e, false, false);
1229  /* body built as a side-effect of mutt_rfc822_read_header */
1230  e->body->length = h.content_length;
1231  mailbox_size_add(m, e);
1232 
1233 #ifdef USE_HCACHE
1234  imap_hcache_put(mdata, e);
1235 #endif /* USE_HCACHE */
1236 
1237  m->msg_count++;
1238 
1239  h.edata = NULL;
1240  idx++;
1241  } while (mfhrc == -1);
1242 
1243  imap_edata_free((void **) &h.edata);
1244 
1245  if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
1246  goto bail;
1247  }
1248 
1249  /* In case we get new mail while fetching the headers. */
1250  if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1251  {
1252  msn_end = mdata->new_mail_count;
1253  while (msn_end > m->email_max)
1254  mx_alloc_memory(m);
1255  imap_msn_reserve(&mdata->msn, msn_end);
1256  mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1257  mdata->new_mail_count = 0;
1258  }
1259 
1260  /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1261  * must not get any EXPUNGE/VANISHED responses in the middle of a
1262  * FETCH, nor when no command is in progress (e.g. between the
1263  * chunked FETCH commands). We previously tried to be robust by
1264  * setting:
1265  * msn_begin = mdata->max_msn + 1;
1266  * but with chunking (and the mythical header cache holes) this
1267  * may not be correct. So here we must assume the msn values have
1268  * not been altered during or after the fetch. */
1269  msn_begin = fetch_msn_end + 1;
1270  }
1271 
1272  retval = 0;
1273 
1274 bail:
1275  mutt_buffer_pool_release(&hdr_list);
1277  mutt_buffer_pool_release(&tempfile);
1278  mutt_file_fclose(&fp);
1279  FREE(&hdrreq);
1280  progress_free(&progress);
1281 
1282  return retval;
1283 }
1284 
1298 int imap_read_headers(struct Mailbox *m, unsigned int msn_begin,
1299  unsigned int msn_end, bool initial_download)
1300 {
1301  int oldmsgcount;
1302  unsigned int maxuid = 0;
1303  int retval = -1;
1304  bool evalhc = false;
1305 
1306 #ifdef USE_HCACHE
1307  void *uidvalidity = NULL;
1308  void *puid_next = NULL;
1309  unsigned int uid_next = 0;
1310  bool has_condstore = false;
1311  bool has_qresync = false;
1312  bool eval_condstore = false;
1313  bool eval_qresync = false;
1314  unsigned long long *pmodseq = NULL;
1315  unsigned long long hc_modseq = 0;
1316  char *uid_seqset = NULL;
1317 #endif /* USE_HCACHE */
1318 
1319  struct ImapAccountData *adata = imap_adata_get(m);
1320  struct ImapMboxData *mdata = imap_mdata_get(m);
1321  if (!adata || (adata->mailbox != m))
1322  return -1;
1323 
1324 #ifdef USE_HCACHE
1325 retry:
1326 #endif /* USE_HCACHE */
1327 
1328  /* make sure context has room to hold the mailbox */
1329  while (msn_end > m->email_max)
1330  mx_alloc_memory(m);
1331  imap_msn_reserve(&mdata->msn, msn_end);
1332  imap_alloc_uid_hash(adata, msn_end);
1333 
1334  oldmsgcount = m->msg_count;
1336  mdata->new_mail_count = 0;
1337 
1338 #ifdef USE_HCACHE
1339  imap_hcache_open(adata, mdata);
1340 
1341  if (mdata->hcache && initial_download)
1342  {
1343  size_t dlen = 0;
1344  uidvalidity = mutt_hcache_fetch_raw(mdata->hcache, "/UIDVALIDITY", 12, &dlen);
1345  puid_next = mutt_hcache_fetch_raw(mdata->hcache, "/UIDNEXT", 8, &dlen);
1346  if (puid_next)
1347  {
1348  uid_next = *(unsigned int *) puid_next;
1349  mutt_hcache_free_raw(mdata->hcache, &puid_next);
1350  }
1351 
1352  if (mdata->modseq)
1353  {
1354  const bool c_imap_condstore =
1355  cs_subset_bool(NeoMutt->sub, "imap_condstore");
1356  if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1357  has_condstore = true;
1358 
1359  /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1360  * If we receive an ENABLED response back, then adata->qresync is set. */
1361  if (adata->qresync)
1362  has_qresync = true;
1363  }
1364 
1365  if (uidvalidity && uid_next && (*(uint32_t *) uidvalidity == mdata->uidvalidity))
1366  {
1367  size_t dlen2 = 0;
1368  evalhc = true;
1369  pmodseq = mutt_hcache_fetch_raw(mdata->hcache, "/MODSEQ", 7, &dlen2);
1370  if (pmodseq)
1371  {
1372  hc_modseq = *pmodseq;
1373  mutt_hcache_free_raw(mdata->hcache, (void **) &pmodseq);
1374  }
1375  if (hc_modseq)
1376  {
1377  if (has_qresync)
1378  {
1379  uid_seqset = imap_hcache_get_uid_seqset(mdata);
1380  if (uid_seqset)
1381  eval_qresync = true;
1382  }
1383 
1384  if (!eval_qresync && has_condstore)
1385  eval_condstore = true;
1386  }
1387  }
1389  }
1390  if (evalhc)
1391  {
1392  if (eval_qresync)
1393  {
1394  if (read_headers_qresync_eval_cache(adata, uid_seqset) < 0)
1395  goto bail;
1396  }
1397  else
1398  {
1399  if (read_headers_normal_eval_cache(adata, msn_end, uid_next, has_condstore || has_qresync,
1400  eval_condstore) < 0)
1401  goto bail;
1402  }
1403 
1404  if ((eval_condstore || eval_qresync) && (hc_modseq != mdata->modseq))
1405  {
1407  hc_modseq, eval_qresync) < 0)
1408  {
1409  goto bail;
1410  }
1411  }
1412 
1413  /* Look for the first empty MSN and start there */
1414  while (msn_begin <= msn_end)
1415  {
1416  if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1417  break;
1418  msn_begin++;
1419  }
1420  }
1421 #endif /* USE_HCACHE */
1422 
1423  if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1424  goto bail;
1425 
1426 #ifdef USE_HCACHE
1427  if (eval_qresync && initial_download)
1428  {
1429  if (imap_verify_qresync(m) != 0)
1430  {
1431  eval_qresync = false;
1432  eval_condstore = false;
1433  evalhc = false;
1434  hc_modseq = 0;
1435  maxuid = 0;
1436  FREE(&uid_seqset);
1437  uidvalidity = NULL;
1438  uid_next = 0;
1439 
1440  goto retry;
1441  }
1442  }
1443 #endif /* USE_HCACHE */
1444 
1445  if (maxuid && (mdata->uid_next < maxuid + 1))
1446  mdata->uid_next = maxuid + 1;
1447 
1448 #ifdef USE_HCACHE
1449  mutt_hcache_store_raw(mdata->hcache, "/UIDVALIDITY", 12, &mdata->uidvalidity,
1450  sizeof(mdata->uidvalidity));
1451  if (maxuid && (mdata->uid_next < maxuid + 1))
1452  {
1453  mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1454  mdata->uid_next = maxuid + 1;
1455  }
1456  if (mdata->uid_next > 1)
1457  {
1458  mutt_hcache_store_raw(mdata->hcache, "/UIDNEXT", 8, &mdata->uid_next,
1459  sizeof(mdata->uid_next));
1460  }
1461 
1462  /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1463  * To do it more often, we'll need to deal with flag updates combined with
1464  * unsync'ed local flag changes. We'll also need to properly sync flags to
1465  * the header cache on close. I'm not sure it's worth the added complexity. */
1466  if (initial_download)
1467  {
1468  if (has_condstore || has_qresync)
1469  {
1470  mutt_hcache_store_raw(mdata->hcache, "/MODSEQ", 7, &mdata->modseq,
1471  sizeof(mdata->modseq));
1472  }
1473  else
1474  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1475 
1476  if (has_qresync)
1478  else
1480  }
1481 #endif /* USE_HCACHE */
1482 
1483  if (m->msg_count > oldmsgcount)
1484  {
1485  /* TODO: it's not clear to me why we are calling mx_alloc_memory
1486  * yet again. */
1487  mx_alloc_memory(m);
1488  }
1489 
1490  mdata->reopen |= IMAP_REOPEN_ALLOW;
1491 
1492  retval = msn_end;
1493 
1494 bail:
1495 #ifdef USE_HCACHE
1497  FREE(&uid_seqset);
1498 #endif /* USE_HCACHE */
1499 
1500  return retval;
1501 }
1502 
1510 int imap_append_message(struct Mailbox *m, struct Message *msg)
1511 {
1512  if (!m || !msg)
1513  return -1;
1514 
1515  FILE *fp = NULL;
1516  char buf[1024 * 2];
1517  char internaldate[IMAP_DATELEN];
1518  char imap_flags[128];
1519  size_t len;
1520  struct Progress *progress = NULL;
1521  size_t sent;
1522  int c, last;
1523  int rc;
1524 
1525  struct ImapAccountData *adata = imap_adata_get(m);
1526  struct ImapMboxData *mdata = imap_mdata_get(m);
1527 
1528  fp = fopen(msg->path, "r");
1529  if (!fp)
1530  {
1531  mutt_perror(msg->path);
1532  goto fail;
1533  }
1534 
1535  /* currently we set the \Seen flag on all messages, but probably we
1536  * should scan the message Status header for flag info. Since we're
1537  * already rereading the whole file for length it isn't any more
1538  * expensive (it'd be nice if we had the file size passed in already
1539  * by the code that writes the file, but that's a lot of changes.
1540  * Ideally we'd have an Email structure with flag info here... */
1541  for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1542  {
1543  if ((c == '\n') && (last != '\r'))
1544  len++;
1545 
1546  len++;
1547  }
1548  rewind(fp);
1549 
1550  if (m->verbose)
1551  progress = progress_new(_("Uploading message..."), MUTT_PROGRESS_NET, len);
1552 
1553  mutt_date_make_imap(internaldate, sizeof(internaldate), msg->received);
1554 
1555  imap_flags[0] = '\0';
1556  imap_flags[1] = '\0';
1557 
1558  if (msg->flags.read)
1559  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Seen");
1560  if (msg->flags.replied)
1561  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Answered");
1562  if (msg->flags.flagged)
1563  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Flagged");
1564  if (msg->flags.draft)
1565  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Draft");
1566 
1567  snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1568  imap_flags + 1, internaldate, (unsigned long) len);
1569 
1570  imap_cmd_start(adata, buf);
1571 
1572  do
1573  {
1574  rc = imap_cmd_step(adata);
1575  } while (rc == IMAP_RES_CONTINUE);
1576 
1577  if (rc != IMAP_RES_RESPOND)
1578  goto cmd_step_fail;
1579 
1580  for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1581  {
1582  if ((c == '\n') && (last != '\r'))
1583  buf[len++] = '\r';
1584 
1585  buf[len++] = c;
1586 
1587  if (len > sizeof(buf) - 3)
1588  {
1589  sent += len;
1590  if (flush_buffer(buf, &len, adata->conn) < 0)
1591  goto fail;
1592  if (m->verbose)
1593  progress_update(progress, sent, -1);
1594  }
1595  }
1596 
1597  if (len)
1598  if (flush_buffer(buf, &len, adata->conn) < 0)
1599  goto fail;
1600 
1601  if (mutt_socket_send(adata->conn, "\r\n") < 0)
1602  goto fail;
1603  mutt_file_fclose(&fp);
1604 
1605  do
1606  {
1607  rc = imap_cmd_step(adata);
1608  } while (rc == IMAP_RES_CONTINUE);
1609 
1610  if (rc != IMAP_RES_OK)
1611  goto cmd_step_fail;
1612 
1613  progress_free(&progress);
1614  return 0;
1615 
1616 cmd_step_fail:
1617  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1618  if (rc != IMAP_RES_BAD)
1619  {
1620  char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1621  pc = imap_next_word(pc); /* skip response code */
1622  if (*pc != '\0')
1623  mutt_error("%s", pc);
1624  }
1625 
1626 fail:
1627  mutt_file_fclose(&fp);
1628  progress_free(&progress);
1629  return -1;
1630 }
1631 
1642 int imap_copy_messages(struct Mailbox *m, struct EmailList *el,
1643  const char *dest, enum MessageSaveOpt save_opt)
1644 {
1645  if (!m || !el || !dest)
1646  return -1;
1647 
1648  struct Buffer cmd, sync_cmd;
1649  char buf[PATH_MAX];
1650  char mbox[PATH_MAX];
1651  char mmbox[PATH_MAX];
1652  char prompt[PATH_MAX + 64];
1653  int rc;
1654  struct ConnAccount cac = { { 0 } };
1655  enum QuadOption err_continue = MUTT_NO;
1656  int triedcreate = 0;
1657  struct EmailNode *en = STAILQ_FIRST(el);
1658  bool single = !STAILQ_NEXT(en, entries);
1659  struct ImapAccountData *adata = imap_adata_get(m);
1660 
1661  if (single && en->email->attach_del)
1662  {
1663  mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1664  return 1;
1665  }
1666 
1667  if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1668  {
1669  mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1670  return -1;
1671  }
1672 
1673  /* check that the save-to folder is in the same account */
1674  if (!imap_account_match(&adata->conn->account, &cac))
1675  {
1676  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1677  return 1;
1678  }
1679 
1680  imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1681  if (*mbox == '\0')
1682  mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1683  imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1684 
1685  /* loop in case of TRYCREATE */
1686  do
1687  {
1688  mutt_buffer_init(&sync_cmd);
1689  mutt_buffer_init(&cmd);
1690 
1691  if (!single) /* copy tagged messages */
1692  {
1693  /* if any messages have attachments to delete, fall through to FETCH
1694  * and APPEND. TODO: Copy what we can with COPY, fall through for the
1695  * remainder. */
1696  STAILQ_FOREACH(en, el, entries)
1697  {
1698  if (en->email->attach_del)
1699  {
1701  "#2 Message contains attachments to be deleted\n");
1702  return 1;
1703  }
1704 
1705  if (en->email->active && en->email->changed)
1706  {
1707  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1708  if (rc < 0)
1709  {
1710  mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1711  goto out;
1712  }
1713  }
1714  }
1715 
1716  rc = imap_exec_msgset(m, "UID COPY", mmbox, MUTT_TAG, false, false);
1717  if (rc == 0)
1718  {
1719  mutt_debug(LL_DEBUG1, "No messages tagged\n");
1720  rc = -1;
1721  goto out;
1722  }
1723  else if (rc < 0)
1724  {
1725  mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1726  goto out;
1727  }
1728  else
1729  {
1730  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1731  rc, mbox);
1732  }
1733  }
1734  else
1735  {
1736  mutt_message(_("Copying message %d to %s..."), en->email->index + 1, mbox);
1737  mutt_buffer_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(en->email)->uid, mmbox);
1738 
1739  if (en->email->active && en->email->changed)
1740  {
1741  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1742  if (rc < 0)
1743  {
1744  mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1745  goto out;
1746  }
1747  }
1748  rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1749  if (rc != IMAP_EXEC_SUCCESS)
1750  {
1751  mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1752  goto out;
1753  }
1754  }
1755 
1756  /* let's get it on */
1757  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1758  if (rc == IMAP_EXEC_ERROR)
1759  {
1760  if (triedcreate)
1761  {
1762  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1763  break;
1764  }
1765  /* bail out if command failed for reasons other than nonexistent target */
1766  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1767  break;
1768  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1769  snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1770  const bool c_confirm_create =
1771  cs_subset_bool(NeoMutt->sub, "confirm_create");
1772  if (c_confirm_create && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1773  {
1774  mutt_clear_error();
1775  goto out;
1776  }
1777  if (imap_create_mailbox(adata, mbox) < 0)
1778  break;
1779  triedcreate = 1;
1780  }
1781  } while (rc == IMAP_EXEC_ERROR);
1782 
1783  if (rc != 0)
1784  {
1785  imap_error("imap_copy_messages", adata->buf);
1786  goto out;
1787  }
1788 
1789  /* cleanup */
1790  if (save_opt == SAVE_MOVE)
1791  {
1792  const bool c_delete_untag = cs_subset_bool(NeoMutt->sub, "delete_untag");
1793  STAILQ_FOREACH(en, el, entries)
1794  {
1795  mutt_set_flag(m, en->email, MUTT_DELETE, true);
1796  mutt_set_flag(m, en->email, MUTT_PURGE, true);
1797  if (c_delete_untag)
1798  mutt_set_flag(m, en->email, MUTT_TAG, false);
1799  }
1800  }
1801 
1802  rc = 0;
1803 
1804 out:
1805  FREE(&cmd.data);
1806  FREE(&sync_cmd.data);
1807 
1808  return (rc < 0) ? -1 : rc;
1809 }
1810 
1818 int imap_cache_del(struct Mailbox *m, struct Email *e)
1819 {
1820  struct ImapAccountData *adata = imap_adata_get(m);
1821  struct ImapMboxData *mdata = imap_mdata_get(m);
1822 
1823  if (!e || !adata || (adata->mailbox != m))
1824  return -1;
1825 
1826  mdata->bcache = msg_cache_open(m);
1827  char id[64];
1828  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1829  return mutt_bcache_del(mdata->bcache, id);
1830 }
1831 
1838 {
1839  struct ImapAccountData *adata = imap_adata_get(m);
1840  struct ImapMboxData *mdata = imap_mdata_get(m);
1841 
1842  if (!adata || (adata->mailbox != m))
1843  return -1;
1844 
1845  mdata->bcache = msg_cache_open(m);
1847 
1848  return 0;
1849 }
1850 
1869 char *imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
1870 {
1871  struct ImapAccountData *adata = imap_adata_get(m);
1872  if (!adata || (adata->mailbox != m))
1873  return NULL;
1874 
1875  struct ImapHeader newh = { 0 };
1876  struct ImapEmailData old_edata = { 0 };
1877  int local_changes = e->changed;
1878 
1879  struct ImapEmailData *edata = e->edata;
1880  newh.edata = edata;
1881 
1882  mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1883  s = msg_parse_flags(&newh, s);
1884  if (!s)
1885  return NULL;
1886 
1887  /* Update tags system */
1888  /* We take a copy of the tags so we can split the string */
1889  char *tags_copy = mutt_str_dup(edata->flags_remote);
1890  driver_tags_replace(&e->tags, tags_copy);
1891  FREE(&tags_copy);
1892 
1893  /* YAUH (yet another ugly hack): temporarily set context to
1894  * read-write even if it's read-only, so *server* updates of
1895  * flags can be processed by mutt_set_flag. mailbox->changed must
1896  * be restored afterwards */
1897  bool readonly = m->readonly;
1898  m->readonly = false;
1899 
1900  /* This is redundant with the following two checks. Removing:
1901  * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old)); */
1902  set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1903  edata->old, e->old);
1904  set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1905  old_edata.read, edata->read, e->read);
1906  set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1907  old_edata.deleted, edata->deleted, e->deleted);
1908  set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1909  old_edata.flagged, edata->flagged, e->flagged);
1910  set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1911  old_edata.replied, edata->replied, e->replied);
1912 
1913  /* this message is now definitively *not* changed (mutt_set_flag
1914  * marks things changed as a side-effect) */
1915  if (local_changes == 0)
1916  e->changed = false;
1917  m->changed &= !readonly;
1918  m->readonly = readonly;
1919 
1920  return s;
1921 }
1922 
1926 bool imap_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1927 {
1928  struct Envelope *newenv = NULL;
1929  char buf[1024];
1930  char *pc = NULL;
1931  unsigned int bytes;
1932  struct Progress *progress = NULL;
1933  unsigned int uid;
1934  bool retried = false;
1935  bool read;
1936  int rc;
1937 
1938  /* Sam's weird courier server returns an OK response even when FETCH
1939  * fails. Thanks Sam. */
1940  bool fetched = false;
1941 
1942  struct ImapAccountData *adata = imap_adata_get(m);
1943 
1944  if (!adata || (adata->mailbox != m))
1945  return false;
1946 
1947  struct Email *e = m->emails[msgno];
1948  if (!e)
1949  return false;
1950 
1951  msg->fp = msg_cache_get(m, e);
1952  if (msg->fp)
1953  {
1954  if (imap_edata_get(e)->parsed)
1955  return true;
1956  goto parsemsg;
1957  }
1958 
1959  /* This function is called in a few places after endwin()
1960  * e.g. mutt_pipe_message(). */
1961  bool output_progress = !isendwin() && m->verbose;
1962  if (output_progress)
1963  mutt_message(_("Fetching message..."));
1964 
1965  msg->fp = msg_cache_put(m, e);
1966  if (!msg->fp)
1967  {
1968  struct Buffer *path = mutt_buffer_pool_get();
1969  mutt_buffer_mktemp(path);
1970  msg->fp = mutt_file_fopen(mutt_buffer_string(path), "w+");
1971  unlink(mutt_buffer_string(path));
1972  mutt_buffer_pool_release(&path);
1973 
1974  if (!msg->fp)
1975  return false;
1976  }
1977 
1978  /* mark this header as currently inactive so the command handler won't
1979  * also try to update it. HACK until all this code can be moved into the
1980  * command handler */
1981  e->active = false;
1982 
1983  const bool c_imap_peek = cs_subset_bool(NeoMutt->sub, "imap_peek");
1984  snprintf(buf, sizeof(buf), "UID FETCH %u %s", imap_edata_get(e)->uid,
1985  ((adata->capabilities & IMAP_CAP_IMAP4REV1) ?
1986  (c_imap_peek ? "BODY.PEEK[]" : "BODY[]") :
1987  "RFC822"));
1988 
1989  imap_cmd_start(adata, buf);
1990  do
1991  {
1992  rc = imap_cmd_step(adata);
1993  if (rc != IMAP_RES_CONTINUE)
1994  break;
1995 
1996  pc = adata->buf;
1997  pc = imap_next_word(pc);
1998  pc = imap_next_word(pc);
1999 
2000  if (mutt_istr_startswith(pc, "FETCH"))
2001  {
2002  while (*pc)
2003  {
2004  pc = imap_next_word(pc);
2005  if (pc[0] == '(')
2006  pc++;
2007  if (mutt_istr_startswith(pc, "UID"))
2008  {
2009  pc = imap_next_word(pc);
2010  if (!mutt_str_atoui(pc, &uid))
2011  goto bail;
2012  if (uid != imap_edata_get(e)->uid)
2013  {
2014  mutt_error(_(
2015  "The message index is incorrect. Try reopening the mailbox."));
2016  }
2017  }
2018  else if (mutt_istr_startswith(pc, "RFC822") || mutt_istr_startswith(pc, "BODY[]"))
2019  {
2020  pc = imap_next_word(pc);
2021  if (imap_get_literal_count(pc, &bytes) < 0)
2022  {
2023  imap_error("imap_msg_open()", buf);
2024  goto bail;
2025  }
2026  if (output_progress)
2027  {
2028  progress = progress_new(_("Fetching message..."), MUTT_PROGRESS_NET, bytes);
2029  }
2030  if (imap_read_literal(msg->fp, adata, bytes, output_progress ? progress : NULL) < 0)
2031  {
2032  goto bail;
2033  }
2034  /* pick up trailing line */
2035  rc = imap_cmd_step(adata);
2036  if (rc != IMAP_RES_CONTINUE)
2037  goto bail;
2038  pc = adata->buf;
2039 
2040  fetched = true;
2041  }
2042  /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
2043  * change (eg from \Unseen to \Seen).
2044  * Uncommitted changes in neomutt take precedence. If we decide to
2045  * incrementally update flags later, this won't stop us syncing */
2046  else if (!e->changed && mutt_istr_startswith(pc, "FLAGS"))
2047  {
2048  pc = imap_set_flags(m, e, pc, NULL);
2049  if (!pc)
2050  goto bail;
2051  }
2052  }
2053  }
2054  } while (rc == IMAP_RES_CONTINUE);
2055 
2056  /* see comment before command start. */
2057  e->active = true;
2058 
2059  fflush(msg->fp);
2060  if (ferror(msg->fp))
2061  goto bail;
2062 
2063  if (rc != IMAP_RES_OK)
2064  goto bail;
2065 
2066  if (!fetched || !imap_code(adata->buf))
2067  goto bail;
2068 
2069  msg_cache_commit(m, e);
2070 
2071 parsemsg:
2072  /* Update the header information. Previously, we only downloaded a
2073  * portion of the headers, those required for the main display. */
2074  rewind(msg->fp);
2075  /* It may be that the Status header indicates a message is read, but the
2076  * IMAP server doesn't know the message has been \Seen. So we capture
2077  * the server's notion of 'read' and if it differs from the message info
2078  * picked up in mutt_rfc822_read_header, we mark the message (and context
2079  * changed). Another possibility: ignore Status on IMAP? */
2080  read = e->read;
2081  newenv = mutt_rfc822_read_header(msg->fp, e, false, false);
2082  mutt_env_merge(e->env, &newenv);
2083 
2084  /* see above. We want the new status in e->read, so we unset it manually
2085  * and let mutt_set_flag set it correctly, updating context. */
2086  if (read != e->read)
2087  {
2088  e->read = read;
2089  mutt_set_flag(m, e, MUTT_NEW, read);
2090  }
2091 
2092  e->lines = 0;
2093  fgets(buf, sizeof(buf), msg->fp);
2094  while (!feof(msg->fp))
2095  {
2096  e->lines++;
2097  fgets(buf, sizeof(buf), msg->fp);
2098  }
2099 
2100  e->body->length = ftell(msg->fp) - e->body->offset;
2101 
2102  mutt_clear_error();
2103  rewind(msg->fp);
2104  imap_edata_get(e)->parsed = true;
2105 
2106  /* retry message parse if cached message is empty */
2107  if (!retried && ((e->lines == 0) || (e->body->length == 0)))
2108  {
2109  imap_cache_del(m, e);
2110  retried = true;
2111  goto parsemsg;
2112  }
2113 
2114  progress_free(&progress);
2115  return true;
2116 
2117 bail:
2118  e->active = true;
2119  mutt_file_fclose(&msg->fp);
2120  imap_cache_del(m, e);
2121  progress_free(&progress);
2122  return false;
2123 }
2124 
2130 int imap_msg_commit(struct Mailbox *m, struct Message *msg)
2131 {
2132  int rc = mutt_file_fclose(&msg->fp);
2133  if (rc != 0)
2134  return rc;
2135 
2136  return imap_append_message(m, msg);
2137 }
2138 
2144 int imap_msg_close(struct Mailbox *m, struct Message *msg)
2145 {
2146  return mutt_file_fclose(&msg->fp);
2147 }
2148 
2152 int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
2153 {
2154  int rc = 0;
2155 #ifdef USE_HCACHE
2156  bool close_hc = true;
2157  struct ImapAccountData *adata = imap_adata_get(m);
2158  struct ImapMboxData *mdata = imap_mdata_get(m);
2159  if (!mdata || !adata)
2160  return -1;
2161  if (mdata->hcache)
2162  close_hc = false;
2163  else
2164  imap_hcache_open(adata, mdata);
2165  rc = imap_hcache_put(mdata, e);
2166  if (close_hc)
2168 #endif
2169  return rc;
2170 }
const char * mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: atoi.c:135
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:202
Body Caching (local copies of email bodies)
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:143
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition: bcache.c:247
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
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:264
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition: bcache.c:207
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:179
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Manage where the email is piped to external commands.
MessageSaveOpt
Message save option.
Definition: commands.h:49
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: commands.h:51
Convenience wrapper for the config headers.
Connection Library.
Convenience wrapper for the core headers.
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:673
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
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
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
void mutt_env_merge(struct Envelope *base, struct Envelope **extra)
Merge the headers of two Envelopes.
Definition: envelope.c:153
Structs that make up an email.
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:593
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 mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: message.c:2144
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: message.c:2130
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:1926
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:2152
Convenience wrapper for the gui 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:347
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition: hash.c:285
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:392
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:109
Header cache multiplexor.
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:639
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:617
void mutt_hcache_free_raw(struct HeaderCache *hc, void **data)
Multiplexor for StoreOps::free.
Definition: hcache.c:543
void * mutt_hcache_fetch_raw(struct HeaderCache *hc, const char *key, size_t keylen, size_t *dlen)
Fetch a message's header from the cache.
Definition: hcache.c:523
long cs_subset_long(const struct ConfigSubset *sub, const char *name)
Get a long config item by name.
Definition: helpers.c:121
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:90
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1070
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1084
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1207
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:1255
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:54
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
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
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
static FILE * msg_cache_put(struct Mailbox *m, struct Email *e)
Put an email into the message cache.
Definition: message.c:128
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1837
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
static int msg_parse_fetch(struct ImapHeader *h, char *s)
Handle headers returned from header fetch.
Definition: message.c:296
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:1869
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition: message.c:976
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition: message.c:469
int imap_append_message(struct Mailbox *m, struct Message *msg)
Write an email back to the server.
Definition: message.c:1510
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:1642
static struct BodyCache * msg_cache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c:79
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
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:877
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:485
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1818
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
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:1049
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:1298
static int msg_cache_commit(struct Mailbox *m, struct Email *e)
Add to the message cache.
Definition: message.c:149
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
static char * msg_parse_flags(struct ImapHeader *h, char *s)
Read a FLAGS token into an ImapHeader.
Definition: message.c:191
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition: message.c:805
static FILE * msg_cache_get(struct Mailbox *m, struct Email *e)
Get the message cache entry for an email.
Definition: message.c:107
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition: message.c:509
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:687
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition: util.c:452
#define IMAP_RES_RESPOND
+
Definition: private.h:58
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h: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
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
Store a UID Sequence Set in the header cache.
Definition: util.c:416
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:381
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:66
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:123
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:124
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1102
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:85
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1159
#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
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:665
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:340
#define IMAP_DATELEN
Definition: private.h:90
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1049
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
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:297
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:775
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1081
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:138
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:356
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
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
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, enum MessageType flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:927
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition: imap.c:1004
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:448
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void mailbox_size_add(struct Mailbox *m, const struct Email *e)
Add an email's size to the total size of a Mailbox.
Definition: mailbox.c:226
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:215
#define FREE(x)
Definition: memory.h:40
#define MAX(a, b)
Definition: memory.h:30
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:91
void imap_msn_free(struct MSN *msn)
Free the cache.
Definition: msn.c:58
void imap_msn_reserve(struct MSN *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:40
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:79
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
IMAP MSN helper functions.
Convenience wrapper for the library headers.
Message logging.
#define _(a)
Definition: message.h:28
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:939
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:181
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:277
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:560
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:196
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:170
Many unsorted constants and some structs.
MessageType
To set flags or match patterns.
Definition: mutt.h:86
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:92
@ MUTT_OLD
Old messages.
Definition: mutt.h:90
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:96
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:99
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:98
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:94
@ MUTT_NEW
New messages.
Definition: mutt.h:89
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:91
#define PATH_MAX
Definition: mutt.h:40
Hundreds of global variables to back the user variables.
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:73
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
NeoMutt Logging.
NeoMutt connections.
#define mutt_socket_write_n(conn, buf, len)
Definition: mutt_socket.h:39
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
Some miscellaneous functions.
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1217
API for mailboxes.
Notmuch-specific Mailbox data.
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1164
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
Pop-specific Account data.
Pop-specific Email data.
Progress bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition: lib.h:48
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:46
void progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:177
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:232
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:252
Prototypes for many functions.
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:182
#define STAILQ_INIT(head)
Definition: queue.h:372
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_NEXT(elm, field)
Definition: queue.h:400
GUI display the mailboxes in a side panel.
Key value store.
#define IS_SPACE(ch)
Definition: string2.h:38
#define SKIPWS(ch)
Definition: string2.h:46
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Local cache of email bodies.
Definition: bcache.c:51
LOFF_T offset
offset where the actual data begins
Definition: body.h:51
LOFF_T length
length (in bytes) of attachment
Definition: body.h:52
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
Login details for a remote server.
Definition: connaccount.h:53
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
List of Emails.
Definition: email.h:131
struct Email * email
Email in the list.
Definition: email.h:132
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
struct Envelope * env
Envelope information.
Definition: email.h:66
void * edata
Driver-specific data.
Definition: email.h:72
int lines
How many lines in the body of this message?
Definition: email.h:60
struct Body * body
List of MIME parts.
Definition: email.h:67
bool active
Message is not to be removed.
Definition: email.h:74
bool old
Email is seen, but unread.
Definition: email.h:47
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:87
bool changed
Email has been edited.
Definition: email.h:75
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:99
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
int msgno
Number displayed to the user.
Definition: email.h:111
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:110
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
The header of an Email.
Definition: envelope.h:55
IMAP-specific Account data -.
Definition: adata.h:40
char delim
Path delimiter.
Definition: adata.h:75
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition: adata.h:63
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
char * buf
Definition: adata.h:59
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
IMAP-specific Email data -.
Definition: edata.h:34
bool parsed
Definition: edata.h:42
unsigned int uid
32-bit Message UID
Definition: edata.h:44
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
char * flags_remote
Definition: edata.h:48
bool deleted
Email has been deleted.
Definition: edata.h:38
bool old
Email has been seen.
Definition: edata.h:37
bool read
Email has been read.
Definition: edata.h:36
bool flagged
Email has been flagged.
Definition: edata.h:39
bool replied
Email has been replied to.
Definition: edata.h:40
IMAP-specific header.
Definition: message.h:33
time_t received
Definition: message.h:36
struct ImapEmailData * edata
Definition: message.h:34
long content_length
Definition: message.h:37
IMAP-specific Mailbox data -.
Definition: mdata.h:39
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
unsigned int uid_next
Definition: mdata.h:51
struct HeaderCache * hcache
Email header cache.
Definition: mdata.h:62
struct BodyCache * bcache
Email body cache.
Definition: mdata.h:60
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
struct HashTable * uid_hash
Definition: mdata.h:58
uint32_t uidvalidity
Definition: mdata.h:50
char * name
Mailbox name.
Definition: mdata.h:40
A mailbox.
Definition: mailbox.h:82
int vcount
The number of virtual messages.
Definition: mailbox.h:102
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
int msg_new
Number of new messages.
Definition: mailbox.h:95
int msg_count
Total number of messages.
Definition: mailbox.h:91
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
void * mdata
Driver specific data.
Definition: mailbox.h:136
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:119
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
bool verbose
Display status messages?
Definition: mailbox.h:118
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
A local copy of an email.
Definition: mxapi.h:42
FILE * fp
pointer to the message data
Definition: mxapi.h:43
char * path
path to temp file
Definition: mxapi.h:44
bool draft
Message has been read.
Definition: mxapi.h:52
bool replied
Message has been replied to.
Definition: mxapi.h:51
time_t received
Time at which this message was received.
Definition: mxapi.h:54
bool flagged
Message is flagged.
Definition: mxapi.h:50
bool read
Message has been read.
Definition: mxapi.h:49
struct Message::@0 flags
Flags for the Message.
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
A Progress Bar.
Definition: progress.c:49
UID Sequence Set Iterator.
Definition: private.h:170
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:186