NeoMutt  2021-02-05-89-gabe350
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 <stdbool.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include "private.h"
40 #include "mutt/lib.h"
41 #include "config/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "conn/lib.h"
45 #include "gui/lib.h"
46 #include "mutt.h"
47 #include "message.h"
48 #include "bcache/lib.h"
49 #include "imap/lib.h"
50 #include "adata.h"
51 #include "edata.h"
52 #include "mdata.h"
53 #include "msn.h"
54 #include "mutt_globals.h"
55 #include "mutt_logging.h"
56 #include "mutt_socket.h"
57 #include "muttlib.h"
58 #include "mx.h"
59 #include "progress.h"
60 #include "protos.h"
61 #ifdef ENABLE_NLS
62 #include <libintl.h>
63 #endif
64 #ifdef USE_HCACHE
65 #include "hcache/lib.h"
66 #endif
67 #ifdef USE_AUTOCRYPT
68 #include "autocrypt/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 = C_MarkOld ? true : false;
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 */
416  if (mutt_str_atoui(buf, &ih->edata->msn) < 0)
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 = 0;
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  if (C_ImapFetchChunkSize > 0)
546  max_headers_per_fetch = C_ImapFetchChunkSize;
547 
548  if (!evalhc)
549  {
550  if (msn_end - msn_begin + 1 <= max_headers_per_fetch)
551  *fetch_msn_end = msn_end;
552  else
553  *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
554  mutt_buffer_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
555  return (*fetch_msn_end - msn_begin + 1);
556  }
557 
558  for (msn = msn_begin; msn <= (msn_end + 1); msn++)
559  {
560  if (msn_count < max_headers_per_fetch && msn <= msn_end &&
561  !imap_msn_get(&mdata->msn, msn - 1))
562  {
563  msn_count++;
564 
565  switch (state)
566  {
567  case 1: /* single: convert to a range */
568  state = 2;
569  /* fallthrough */
570  case 2: /* extend range ending */
571  range_end = msn;
572  break;
573  default:
574  state = 1;
575  range_begin = msn;
576  break;
577  }
578  }
579  else if (state)
580  {
581  if (first_chunk)
582  first_chunk = false;
583  else
584  mutt_buffer_addch(buf, ',');
585 
586  if (state == 1)
587  mutt_buffer_add_printf(buf, "%u", range_begin);
588  else if (state == 2)
589  mutt_buffer_add_printf(buf, "%u:%u", range_begin, range_end);
590  state = 0;
591 
592  if ((mutt_buffer_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
593  break;
594  }
595  }
596 
597  /* The loop index goes one past to terminate the range if needed. */
598  *fetch_msn_end = msn - 1;
599 
600  return msn_count;
601 }
602 
618 static void set_changed_flag(struct Mailbox *m, struct Email *e, int local_changes,
619  bool *server_changes, enum MessageType flag_name,
620  bool old_hd_flag, bool new_hd_flag, bool h_flag)
621 {
622  /* If there are local_changes, we only want to note if the server
623  * flags have changed, so we can set a reopen flag in
624  * cmd_parse_fetch(). We don't want to count a local modification
625  * to the header flag as a "change". */
626  if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
627  return;
628 
629  if (new_hd_flag == h_flag)
630  return;
631 
632  if (server_changes)
633  *server_changes = true;
634 
635  /* Local changes have priority */
636  if (local_changes == 0)
637  mutt_set_flag(m, e, flag_name, new_hd_flag);
638 }
639 
640 #ifdef USE_HCACHE
641 
659  unsigned int msn_end, unsigned int uid_next,
660  bool store_flag_updates, bool eval_condstore)
661 {
662  struct Progress progress;
663  char buf[1024];
664 
665  struct Mailbox *m = adata->mailbox;
666  struct ImapMboxData *mdata = imap_mdata_get(m);
667  int idx = m->msg_count;
668 
669  if (m->verbose)
670  {
671  /* L10N: Comparing the cached data with the IMAP server's data */
672  mutt_progress_init(&progress, _("Evaluating cache..."), MUTT_PROGRESS_READ, msn_end);
673  }
674 
675  /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
676  * the flags in the header cache, and update them further below.
677  * Otherwise, we fetch the current state of the flags here. */
678  snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
679  eval_condstore ? "" : " FLAGS");
680 
681  imap_cmd_start(adata, buf);
682 
683  int rc = IMAP_RES_CONTINUE;
684  int mfhrc = 0;
685  struct ImapHeader h;
686  for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
687  {
688  if (SigInt && query_abort_header_download(adata))
689  return -1;
690 
691  if (m->verbose)
692  mutt_progress_update(&progress, msgno, -1);
693 
694  memset(&h, 0, sizeof(h));
695  h.edata = imap_edata_new();
696  do
697  {
698  rc = imap_cmd_step(adata);
699  if (rc != IMAP_RES_CONTINUE)
700  break;
701 
702  mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
703  if (mfhrc < 0)
704  continue;
705 
706  if (!h.edata->uid)
707  {
708  mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
709  h.edata->msn);
710  continue;
711  }
712 
713  if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
714  {
715  mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
716  h.edata->msn);
717  continue;
718  }
719 
720  if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
721  {
722  mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
723  h.edata->msn);
724  continue;
725  }
726 
727  struct Email *e = imap_hcache_get(mdata, h.edata->uid);
728  m->emails[idx] = e;
729  if (e)
730  {
731  imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
732  mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
733 
734  e->index = h.edata->uid;
735  /* messages which have not been expunged are ACTIVE (borrowed from mh
736  * folders) */
737  e->active = true;
738  e->changed = false;
739  if (eval_condstore)
740  {
741  h.edata->read = e->read;
742  h.edata->old = e->old;
743  h.edata->deleted = e->deleted;
744  h.edata->flagged = e->flagged;
745  h.edata->replied = e->replied;
746  }
747  else
748  {
749  e->read = h.edata->read;
750  e->old = h.edata->old;
751  e->deleted = h.edata->deleted;
752  e->flagged = h.edata->flagged;
753  e->replied = h.edata->replied;
754  }
755 
756  /* mailbox->emails[msgno]->received is restored from mutt_hcache_restore */
757  e->edata = h.edata;
759  STAILQ_INIT(&e->tags);
760 
761  /* We take a copy of the tags so we can split the string */
762  char *tags_copy = mutt_str_dup(h.edata->flags_remote);
763  driver_tags_replace(&e->tags, tags_copy);
764  FREE(&tags_copy);
765 
766  m->msg_count++;
767  mailbox_size_add(m, e);
768 
769  /* If this is the first time we are fetching, we need to
770  * store the current state of flags back into the header cache */
771  if (!eval_condstore && store_flag_updates)
772  imap_hcache_put(mdata, e);
773 
774  h.edata = NULL;
775  idx++;
776  }
777  } while (mfhrc == -1);
778 
779  imap_edata_free((void **) &h.edata);
780 
781  if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
782  return -1;
783  }
784 
785  return 0;
786 }
787 
800 static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
801 {
802  int rc;
803  unsigned int uid = 0;
804 
805  mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
806  struct Mailbox *m = adata->mailbox;
807  struct ImapMboxData *mdata = adata->mailbox->mdata;
808  unsigned int msn = 1;
809 
810  struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
811  if (!iter)
812  return -1;
813 
814  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
815  {
816  /* The seqset may contain more headers than the fetch request, so
817  * we need to watch and reallocate the context and msn_index */
818  imap_msn_reserve(&mdata->msn, msn);
819 
820  struct Email *e = imap_hcache_get(mdata, uid);
821  if (e)
822  {
823  imap_msn_set(&mdata->msn, msn - 1, e);
824 
825  if (m->msg_count >= m->email_max)
826  mx_alloc_memory(m);
827 
828  struct ImapEmailData *edata = imap_edata_new();
829  e->edata = edata;
831 
832  e->index = uid;
833  e->active = true;
834  e->changed = false;
835  edata->read = e->read;
836  edata->old = e->old;
837  edata->deleted = e->deleted;
838  edata->flagged = e->flagged;
839  edata->replied = e->replied;
840 
841  edata->msn = msn;
842  edata->uid = uid;
843  mutt_hash_int_insert(mdata->uid_hash, uid, e);
844 
845  mailbox_size_add(m, e);
846  m->emails[m->msg_count++] = e;
847 
848  msn++;
849  }
850  }
851 
853 
854  return rc;
855 }
856 
870  unsigned int msn_end, unsigned int uid_next,
871  unsigned long long hc_modseq, bool eval_qresync)
872 {
873  struct Progress progress;
874  char buf[1024];
875  unsigned int header_msn = 0;
876 
877  struct Mailbox *m = adata->mailbox;
878  struct ImapMboxData *mdata = imap_mdata_get(m);
879 
880  if (m->verbose)
881  {
882  /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
883  mutt_progress_init(&progress, _("Fetching flag updates..."), MUTT_PROGRESS_READ, msn_end);
884  }
885 
886  snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
887  uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
888 
889  imap_cmd_start(adata, buf);
890 
891  int rc = IMAP_RES_CONTINUE;
892  for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
893  {
894  if (SigInt && query_abort_header_download(adata))
895  return -1;
896 
897  if (m->verbose)
898  mutt_progress_update(&progress, msgno, -1);
899 
900  /* cmd_parse_fetch will update the flags */
901  rc = imap_cmd_step(adata);
902  if (rc != IMAP_RES_CONTINUE)
903  break;
904 
905  /* so we just need to grab the header and persist it back into
906  * the header cache */
907  char *fetch_buf = adata->buf;
908  if (fetch_buf[0] != '*')
909  continue;
910 
911  fetch_buf = imap_next_word(fetch_buf);
912  if (!isdigit((unsigned char) *fetch_buf) || (mutt_str_atoui(fetch_buf, &header_msn) < 0))
913  continue;
914 
915  if ((header_msn < 1) || (header_msn > msn_end) ||
916  !imap_msn_get(&mdata->msn, header_msn - 1))
917  {
918  mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
919  header_msn);
920  continue;
921  }
922 
923  imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
924  }
925 
926  if (rc != IMAP_RES_OK)
927  return -1;
928 
929  /* The IMAP flag setting as part of cmd_parse_fetch() ends up
930  * flipping these on. */
931  mdata->check_status &= ~IMAP_FLAGS_PENDING;
932  m->changed = false;
933 
934  /* VANISHED handling: we need to empty out the messages */
935  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
936  {
939 
940  imap_hcache_open(adata, mdata);
941  mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
942  }
943 
944  /* undo expunge count updates.
945  * ctx_update() will do this at the end of the header fetch. */
946  m->vcount = 0;
947  m->msg_tagged = 0;
948  m->msg_deleted = 0;
949  m->msg_new = 0;
950  m->msg_unread = 0;
951  m->msg_flagged = 0;
952  m->changed = false;
953 
954  return 0;
955 }
956 
965 static int imap_verify_qresync(struct Mailbox *m)
966 {
967  assert(m);
968  struct ImapAccountData *adata = imap_adata_get(m);
969  struct ImapMboxData *mdata = imap_mdata_get(m);
970  if (!adata || (adata->mailbox != m))
971  return -1;
972 
973  const size_t max_msn = imap_msn_highest(&mdata->msn);
974 
975  unsigned int msn;
976  unsigned int uid;
977  struct Email *e = NULL;
978  struct Email *uidh = NULL;
979 
980  for (int i = 0; i < m->msg_count; i++)
981  {
982  e = m->emails[i];
983  if (!e)
984  goto fail;
985 
986  msn = imap_edata_get(e)->msn;
987  uid = imap_edata_get(e)->uid;
988 
989  if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
990  goto fail;
991 
992  uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
993  if (uidh != e)
994  goto fail;
995  }
996 
997  return 0;
998 
999 fail:
1000  imap_msn_free(&mdata->msn);
1001  mutt_hash_free(&mdata->uid_hash);
1002 
1003  for (int i = 0; i < m->msg_count; i++)
1004  {
1005  if (m->emails[i] && m->emails[i]->edata)
1006  imap_edata_free(&m->emails[i]->edata);
1007  email_free(&m->emails[i]);
1008  }
1009  m->msg_count = 0;
1010  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1012  imap_hcache_close(mdata);
1013 
1014  if (m->verbose)
1015  {
1016  /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1017  * sanity check. If that fails, Mutt reopens the mailbox using a normal
1018  * download. */
1019  mutt_error(_("QRESYNC failed. Reopening mailbox."));
1020  }
1021  return -1;
1022 }
1023 
1024 #endif /* USE_HCACHE */
1025 
1037 static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin,
1038  unsigned int msn_end, bool evalhc,
1039  unsigned int *maxuid, bool initial_download)
1040 {
1041  int rc, mfhrc = 0, retval = -1;
1042  unsigned int fetch_msn_end = 0;
1043  struct Progress progress;
1044  char *hdrreq = NULL;
1045  struct Buffer *tempfile = NULL;
1046  FILE *fp = NULL;
1047  struct ImapHeader h;
1048  struct Buffer *buf = NULL;
1049  static const char *const want_headers =
1050  "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE "
1051  "CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL "
1052  "X-ORIGINAL-TO";
1053 
1054  struct ImapAccountData *adata = imap_adata_get(m);
1055  struct ImapMboxData *mdata = imap_mdata_get(m);
1056  int idx = m->msg_count;
1057 
1058  if (!adata || (adata->mailbox != m))
1059  return -1;
1060 
1061  struct Buffer *hdr_list = mutt_buffer_pool_get();
1062  mutt_buffer_strcpy(hdr_list, want_headers);
1063  if (C_ImapHeaders)
1064  {
1065  mutt_buffer_addch(hdr_list, ' ');
1066  mutt_buffer_addstr(hdr_list, C_ImapHeaders);
1067  }
1068 #ifdef USE_AUTOCRYPT
1069  if (C_Autocrypt)
1070  {
1071  mutt_buffer_addch(hdr_list, ' ');
1072  mutt_buffer_addstr(hdr_list, "AUTOCRYPT");
1073  }
1074 #endif
1075 
1076  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1077  {
1078  mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", mutt_buffer_string(hdr_list));
1079  }
1080  else if (adata->capabilities & IMAP_CAP_IMAP4)
1081  {
1082  mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", mutt_buffer_string(hdr_list));
1083  }
1084  else
1085  { /* Unable to fetch headers for lower versions */
1086  mutt_error(_("Unable to fetch headers from this IMAP server version"));
1087  goto bail;
1088  }
1089 
1090  mutt_buffer_pool_release(&hdr_list);
1091 
1092  /* instead of downloading all headers and then parsing them, we parse them
1093  * as they come in. */
1094  tempfile = mutt_buffer_pool_get();
1095  mutt_buffer_mktemp(tempfile);
1096  fp = mutt_file_fopen(mutt_buffer_string(tempfile), "w+");
1097  if (!fp)
1098  {
1099  mutt_error(_("Could not create temporary file %s"), mutt_buffer_string(tempfile));
1100  goto bail;
1101  }
1102  unlink(mutt_buffer_string(tempfile));
1103  mutt_buffer_pool_release(&tempfile);
1104 
1105  if (m->verbose)
1106  {
1107  mutt_progress_init(&progress, _("Fetching message headers..."),
1108  MUTT_PROGRESS_READ, msn_end);
1109  }
1110 
1111  buf = mutt_buffer_pool_get();
1112 
1113  /* NOTE:
1114  * The (fetch_msn_end < msn_end) used to be important to prevent
1115  * an infinite loop, in the event the server did not return all
1116  * the headers (due to a pending expunge, for example).
1117  *
1118  * I believe the new chunking imap_fetch_msn_seqset()
1119  * implementation and "msn_begin = fetch_msn_end + 1" assignment
1120  * at the end of the loop makes the comparison unneeded, but to be
1121  * cautious I'm keeping it.
1122  */
1123  while ((fetch_msn_end < msn_end) &&
1124  imap_fetch_msn_seqset(buf, adata, evalhc, msn_begin, msn_end, &fetch_msn_end))
1125  {
1126  char *cmd = NULL;
1127  mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1128  mutt_buffer_string(buf), hdrreq);
1129  imap_cmd_start(adata, cmd);
1130  FREE(&cmd);
1131 
1132  rc = IMAP_RES_CONTINUE;
1133  for (int msgno = msn_begin; rc == IMAP_RES_CONTINUE; msgno++)
1134  {
1135  if (initial_download && SigInt && query_abort_header_download(adata))
1136  goto bail;
1137 
1138  if (m->verbose)
1139  mutt_progress_update(&progress, msgno, -1);
1140 
1141  rewind(fp);
1142  memset(&h, 0, sizeof(h));
1143  h.edata = imap_edata_new();
1144 
1145  /* this DO loop does two things:
1146  * 1. handles untagged messages, so we can try again on the same msg
1147  * 2. fetches the tagged response at the end of the last message. */
1148  do
1149  {
1150  rc = imap_cmd_step(adata);
1151  if (rc != IMAP_RES_CONTINUE)
1152  break;
1153 
1154  mfhrc = msg_fetch_header(m, &h, adata->buf, fp);
1155  if (mfhrc < 0)
1156  continue;
1157 
1158  if (!ftello(fp))
1159  {
1160  mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1161  continue;
1162  }
1163 
1164  /* make sure we don't get remnants from older larger message headers */
1165  fputs("\n\n", fp);
1166 
1167  if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1168  {
1169  mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1170  h.edata->msn);
1171  continue;
1172  }
1173 
1174  /* May receive FLAGS updates in a separate untagged response */
1175  if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1176  {
1177  mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1178  h.edata->msn);
1179  continue;
1180  }
1181 
1182  struct Email *e = email_new();
1183  m->emails[idx] = e;
1184 
1185  imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
1186  mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
1187 
1188  e->index = h.edata->uid;
1189  /* messages which have not been expunged are ACTIVE (borrowed from mh
1190  * folders) */
1191  e->active = true;
1192  e->changed = false;
1193  e->read = h.edata->read;
1194  e->old = h.edata->old;
1195  e->deleted = h.edata->deleted;
1196  e->flagged = h.edata->flagged;
1197  e->replied = h.edata->replied;
1198  e->received = h.received;
1199  e->edata = (void *) (h.edata);
1201  STAILQ_INIT(&e->tags);
1202 
1203  /* We take a copy of the tags so we can split the string */
1204  char *tags_copy = mutt_str_dup(h.edata->flags_remote);
1205  driver_tags_replace(&e->tags, tags_copy);
1206  FREE(&tags_copy);
1207 
1208  if (*maxuid < h.edata->uid)
1209  *maxuid = h.edata->uid;
1210 
1211  rewind(fp);
1212  /* NOTE: if Date: header is missing, mutt_rfc822_read_header depends
1213  * on h.received being set */
1214  e->env = mutt_rfc822_read_header(fp, e, false, false);
1215  /* body built as a side-effect of mutt_rfc822_read_header */
1216  e->body->length = h.content_length;
1217  mailbox_size_add(m, e);
1218 
1219 #ifdef USE_HCACHE
1220  imap_hcache_put(mdata, e);
1221 #endif /* USE_HCACHE */
1222 
1223  m->msg_count++;
1224 
1225  h.edata = NULL;
1226  idx++;
1227  } while (mfhrc == -1);
1228 
1229  imap_edata_free((void **) &h.edata);
1230 
1231  if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
1232  goto bail;
1233  }
1234 
1235  /* In case we get new mail while fetching the headers. */
1236  if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1237  {
1238  msn_end = mdata->new_mail_count;
1239  while (msn_end > m->email_max)
1240  mx_alloc_memory(m);
1241  imap_msn_reserve(&mdata->msn, msn_end);
1242  mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1243  mdata->new_mail_count = 0;
1244  }
1245 
1246  /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1247  * must not get any EXPUNGE/VANISHED responses in the middle of a
1248  * FETCH, nor when no command is in progress (e.g. between the
1249  * chunked FETCH commands). We previously tried to be robust by
1250  * setting:
1251  * msn_begin = mdata->max_msn + 1;
1252  * but with chunking (and the mythical header cache holes) this
1253  * may not be correct. So here we must assume the msn values have
1254  * not been altered during or after the fetch. */
1255  msn_begin = fetch_msn_end + 1;
1256  }
1257 
1258  retval = 0;
1259 
1260 bail:
1261  mutt_buffer_pool_release(&hdr_list);
1263  mutt_buffer_pool_release(&tempfile);
1264  mutt_file_fclose(&fp);
1265  FREE(&hdrreq);
1266 
1267  return retval;
1268 }
1269 
1283 int imap_read_headers(struct Mailbox *m, unsigned int msn_begin,
1284  unsigned int msn_end, bool initial_download)
1285 {
1286  int oldmsgcount;
1287  unsigned int maxuid = 0;
1288  int retval = -1;
1289  bool evalhc = false;
1290 
1291 #ifdef USE_HCACHE
1292  void *uidvalidity = NULL;
1293  void *puid_next = NULL;
1294  unsigned int uid_next = 0;
1295  bool has_condstore = false;
1296  bool has_qresync = false;
1297  bool eval_condstore = false;
1298  bool eval_qresync = false;
1299  unsigned long long *pmodseq = NULL;
1300  unsigned long long hc_modseq = 0;
1301  char *uid_seqset = NULL;
1302 #endif /* USE_HCACHE */
1303 
1304  struct ImapAccountData *adata = imap_adata_get(m);
1305  struct ImapMboxData *mdata = imap_mdata_get(m);
1306  if (!adata || (adata->mailbox != m))
1307  return -1;
1308 
1309 #ifdef USE_HCACHE
1310 retry:
1311 #endif /* USE_HCACHE */
1312 
1313  /* make sure context has room to hold the mailbox */
1314  while (msn_end > m->email_max)
1315  mx_alloc_memory(m);
1316  imap_msn_reserve(&mdata->msn, msn_end);
1317  imap_alloc_uid_hash(adata, msn_end);
1318 
1319  oldmsgcount = m->msg_count;
1321  mdata->new_mail_count = 0;
1322 
1323 #ifdef USE_HCACHE
1324  imap_hcache_open(adata, mdata);
1325 
1326  if (mdata->hcache && initial_download)
1327  {
1328  size_t dlen = 0;
1329  uidvalidity = mutt_hcache_fetch_raw(mdata->hcache, "/UIDVALIDITY", 12, &dlen);
1330  puid_next = mutt_hcache_fetch_raw(mdata->hcache, "/UIDNEXT", 8, &dlen);
1331  if (puid_next)
1332  {
1333  uid_next = *(unsigned int *) puid_next;
1334  mutt_hcache_free_raw(mdata->hcache, &puid_next);
1335  }
1336 
1337  if (mdata->modseq)
1338  {
1340  has_condstore = true;
1341 
1342  /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1343  * If we receive an ENABLED response back, then adata->qresync is set. */
1344  if (adata->qresync)
1345  has_qresync = true;
1346  }
1347 
1348  if (uidvalidity && uid_next && (*(uint32_t *) uidvalidity == mdata->uidvalidity))
1349  {
1350  size_t dlen2 = 0;
1351  evalhc = true;
1352  pmodseq = mutt_hcache_fetch_raw(mdata->hcache, "/MODSEQ", 7, &dlen2);
1353  if (pmodseq)
1354  {
1355  hc_modseq = *pmodseq;
1356  mutt_hcache_free_raw(mdata->hcache, (void **) &pmodseq);
1357  }
1358  if (hc_modseq)
1359  {
1360  if (has_qresync)
1361  {
1362  uid_seqset = imap_hcache_get_uid_seqset(mdata);
1363  if (uid_seqset)
1364  eval_qresync = true;
1365  }
1366 
1367  if (!eval_qresync && has_condstore)
1368  eval_condstore = true;
1369  }
1370  }
1372  }
1373  if (evalhc)
1374  {
1375  if (eval_qresync)
1376  {
1377  if (read_headers_qresync_eval_cache(adata, uid_seqset) < 0)
1378  goto bail;
1379  }
1380  else
1381  {
1382  if (read_headers_normal_eval_cache(adata, msn_end, uid_next, has_condstore || has_qresync,
1383  eval_condstore) < 0)
1384  goto bail;
1385  }
1386 
1387  if ((eval_condstore || eval_qresync) && (hc_modseq != mdata->modseq))
1388  {
1390  hc_modseq, eval_qresync) < 0)
1391  {
1392  goto bail;
1393  }
1394  }
1395 
1396  /* Look for the first empty MSN and start there */
1397  while (msn_begin <= msn_end)
1398  {
1399  if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1400  break;
1401  msn_begin++;
1402  }
1403  }
1404 #endif /* USE_HCACHE */
1405 
1406  if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1407  goto bail;
1408 
1409 #ifdef USE_HCACHE
1410  if (eval_qresync && initial_download)
1411  {
1412  if (imap_verify_qresync(m) != 0)
1413  {
1414  eval_qresync = false;
1415  eval_condstore = false;
1416  evalhc = false;
1417  hc_modseq = 0;
1418  maxuid = 0;
1419  FREE(&uid_seqset);
1420  uidvalidity = NULL;
1421  uid_next = 0;
1422 
1423  goto retry;
1424  }
1425  }
1426 #endif /* USE_HCACHE */
1427 
1428  if (maxuid && (mdata->uid_next < maxuid + 1))
1429  mdata->uid_next = maxuid + 1;
1430 
1431 #ifdef USE_HCACHE
1432  mutt_hcache_store_raw(mdata->hcache, "/UIDVALIDITY", 12, &mdata->uidvalidity,
1433  sizeof(mdata->uidvalidity));
1434  if (maxuid && (mdata->uid_next < maxuid + 1))
1435  {
1436  mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1437  mdata->uid_next = maxuid + 1;
1438  }
1439  if (mdata->uid_next > 1)
1440  {
1441  mutt_hcache_store_raw(mdata->hcache, "/UIDNEXT", 8, &mdata->uid_next,
1442  sizeof(mdata->uid_next));
1443  }
1444 
1445  /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1446  * To do it more often, we'll need to deal with flag updates combined with
1447  * unsync'ed local flag changes. We'll also need to properly sync flags to
1448  * the header cache on close. I'm not sure it's worth the added complexity. */
1449  if (initial_download)
1450  {
1451  if (has_condstore || has_qresync)
1452  {
1453  mutt_hcache_store_raw(mdata->hcache, "/MODSEQ", 7, &mdata->modseq,
1454  sizeof(mdata->modseq));
1455  }
1456  else
1457  mutt_hcache_delete_record(mdata->hcache, "/MODSEQ", 7);
1458 
1459  if (has_qresync)
1461  else
1463  }
1464 #endif /* USE_HCACHE */
1465 
1466  if (m->msg_count > oldmsgcount)
1467  {
1468  /* TODO: it's not clear to me why we are calling mx_alloc_memory
1469  * yet again. */
1470  mx_alloc_memory(m);
1471  }
1472 
1473  mdata->reopen |= IMAP_REOPEN_ALLOW;
1474 
1475  retval = msn_end;
1476 
1477 bail:
1478 #ifdef USE_HCACHE
1480  FREE(&uid_seqset);
1481 #endif /* USE_HCACHE */
1482 
1483  return retval;
1484 }
1485 
1493 int imap_append_message(struct Mailbox *m, struct Message *msg)
1494 {
1495  if (!m || !msg)
1496  return -1;
1497 
1498  FILE *fp = NULL;
1499  char buf[1024 * 2];
1500  char internaldate[IMAP_DATELEN];
1501  char imap_flags[128];
1502  size_t len;
1503  struct Progress progress;
1504  size_t sent;
1505  int c, last;
1506  int rc;
1507 
1508  struct ImapAccountData *adata = imap_adata_get(m);
1509  struct ImapMboxData *mdata = imap_mdata_get(m);
1510 
1511  fp = fopen(msg->path, "r");
1512  if (!fp)
1513  {
1514  mutt_perror(msg->path);
1515  goto fail;
1516  }
1517 
1518  /* currently we set the \Seen flag on all messages, but probably we
1519  * should scan the message Status header for flag info. Since we're
1520  * already rereading the whole file for length it isn't any more
1521  * expensive (it'd be nice if we had the file size passed in already
1522  * by the code that writes the file, but that's a lot of changes.
1523  * Ideally we'd have an Email structure with flag info here... */
1524  for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1525  {
1526  if ((c == '\n') && (last != '\r'))
1527  len++;
1528 
1529  len++;
1530  }
1531  rewind(fp);
1532 
1533  if (m->verbose)
1534  mutt_progress_init(&progress, _("Uploading message..."), MUTT_PROGRESS_NET, len);
1535 
1536  mutt_date_make_imap(internaldate, sizeof(internaldate), msg->received);
1537 
1538  imap_flags[0] = '\0';
1539  imap_flags[1] = '\0';
1540 
1541  if (msg->flags.read)
1542  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Seen");
1543  if (msg->flags.replied)
1544  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Answered");
1545  if (msg->flags.flagged)
1546  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Flagged");
1547  if (msg->flags.draft)
1548  mutt_str_cat(imap_flags, sizeof(imap_flags), " \\Draft");
1549 
1550  snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1551  imap_flags + 1, internaldate, (unsigned long) len);
1552 
1553  imap_cmd_start(adata, buf);
1554 
1555  do
1556  {
1557  rc = imap_cmd_step(adata);
1558  } while (rc == IMAP_RES_CONTINUE);
1559 
1560  if (rc != IMAP_RES_RESPOND)
1561  goto cmd_step_fail;
1562 
1563  for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1564  {
1565  if ((c == '\n') && (last != '\r'))
1566  buf[len++] = '\r';
1567 
1568  buf[len++] = c;
1569 
1570  if (len > sizeof(buf) - 3)
1571  {
1572  sent += len;
1573  if (flush_buffer(buf, &len, adata->conn) < 0)
1574  goto fail;
1575  if (m->verbose)
1576  mutt_progress_update(&progress, sent, -1);
1577  }
1578  }
1579 
1580  if (len)
1581  if (flush_buffer(buf, &len, adata->conn) < 0)
1582  goto fail;
1583 
1584  if (mutt_socket_send(adata->conn, "\r\n") < 0)
1585  goto fail;
1586  mutt_file_fclose(&fp);
1587 
1588  do
1589  {
1590  rc = imap_cmd_step(adata);
1591  } while (rc == IMAP_RES_CONTINUE);
1592 
1593  if (rc != IMAP_RES_OK)
1594  goto cmd_step_fail;
1595 
1596  return 0;
1597 
1598 cmd_step_fail:
1599  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1600  if (rc != IMAP_RES_BAD)
1601  {
1602  char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1603  pc = imap_next_word(pc); /* skip response code */
1604  if (*pc != '\0')
1605  mutt_error("%s", pc);
1606  }
1607 
1608 fail:
1609  mutt_file_fclose(&fp);
1610  return -1;
1611 }
1612 
1623 int imap_copy_messages(struct Mailbox *m, struct EmailList *el,
1624  const char *dest, enum MessageSaveOpt save_opt)
1625 {
1626  if (!m || !el || !dest)
1627  return -1;
1628 
1629  struct Buffer cmd, sync_cmd;
1630  char buf[PATH_MAX];
1631  char mbox[PATH_MAX];
1632  char mmbox[PATH_MAX];
1633  char prompt[PATH_MAX + 64];
1634  int rc;
1635  struct ConnAccount cac = { { 0 } };
1636  enum QuadOption err_continue = MUTT_NO;
1637  int triedcreate = 0;
1638  struct EmailNode *en = STAILQ_FIRST(el);
1639  bool single = !STAILQ_NEXT(en, entries);
1640  struct ImapAccountData *adata = imap_adata_get(m);
1641 
1642  if (single && en->email->attach_del)
1643  {
1644  mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1645  return 1;
1646  }
1647 
1648  if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1649  {
1650  mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1651  return -1;
1652  }
1653 
1654  /* check that the save-to folder is in the same account */
1655  if (!imap_account_match(&adata->conn->account, &cac))
1656  {
1657  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1658  return 1;
1659  }
1660 
1661  imap_fix_path(adata->delim, buf, mbox, sizeof(mbox));
1662  if (*mbox == '\0')
1663  mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1664  imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1665 
1666  /* loop in case of TRYCREATE */
1667  do
1668  {
1669  mutt_buffer_init(&sync_cmd);
1670  mutt_buffer_init(&cmd);
1671 
1672  if (!single) /* copy tagged messages */
1673  {
1674  /* if any messages have attachments to delete, fall through to FETCH
1675  * and APPEND. TODO: Copy what we can with COPY, fall through for the
1676  * remainder. */
1677  STAILQ_FOREACH(en, el, entries)
1678  {
1679  if (en->email->attach_del)
1680  {
1682  "#2 Message contains attachments to be deleted\n");
1683  return 1;
1684  }
1685 
1686  if (en->email->active && en->email->changed)
1687  {
1688  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1689  if (rc < 0)
1690  {
1691  mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1692  goto out;
1693  }
1694  }
1695  }
1696 
1697  rc = imap_exec_msgset(m, "UID COPY", mmbox, MUTT_TAG, false, false);
1698  if (rc == 0)
1699  {
1700  mutt_debug(LL_DEBUG1, "No messages tagged\n");
1701  rc = -1;
1702  goto out;
1703  }
1704  else if (rc < 0)
1705  {
1706  mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1707  goto out;
1708  }
1709  else
1710  {
1711  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1712  rc, mbox);
1713  }
1714  }
1715  else
1716  {
1717  mutt_message(_("Copying message %d to %s..."), en->email->index + 1, mbox);
1718  mutt_buffer_add_printf(&cmd, "UID COPY %u %s", imap_edata_get(en->email)->uid, mmbox);
1719 
1720  if (en->email->active && en->email->changed)
1721  {
1722  rc = imap_sync_message_for_copy(m, en->email, &sync_cmd, &err_continue);
1723  if (rc < 0)
1724  {
1725  mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1726  goto out;
1727  }
1728  }
1729  rc = imap_exec(adata, cmd.data, IMAP_CMD_QUEUE);
1730  if (rc != IMAP_EXEC_SUCCESS)
1731  {
1732  mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1733  goto out;
1734  }
1735  }
1736 
1737  /* let's get it on */
1738  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1739  if (rc == IMAP_EXEC_ERROR)
1740  {
1741  if (triedcreate)
1742  {
1743  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1744  break;
1745  }
1746  /* bail out if command failed for reasons other than nonexistent target */
1747  if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1748  break;
1749  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1750  snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1751  if (C_ConfirmCreate && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1752  {
1753  mutt_clear_error();
1754  goto out;
1755  }
1756  if (imap_create_mailbox(adata, mbox) < 0)
1757  break;
1758  triedcreate = 1;
1759  }
1760  } while (rc == IMAP_EXEC_ERROR);
1761 
1762  if (rc != 0)
1763  {
1764  imap_error("imap_copy_messages", adata->buf);
1765  goto out;
1766  }
1767 
1768  /* cleanup */
1769  if (save_opt == SAVE_MOVE)
1770  {
1771  STAILQ_FOREACH(en, el, entries)
1772  {
1773  mutt_set_flag(m, en->email, MUTT_DELETE, true);
1774  mutt_set_flag(m, en->email, MUTT_PURGE, true);
1775  if (C_DeleteUntag)
1776  mutt_set_flag(m, en->email, MUTT_TAG, false);
1777  }
1778  }
1779 
1780  rc = 0;
1781 
1782 out:
1783  FREE(&cmd.data);
1784  FREE(&sync_cmd.data);
1785 
1786  return (rc < 0) ? -1 : rc;
1787 }
1788 
1796 int imap_cache_del(struct Mailbox *m, struct Email *e)
1797 {
1798  struct ImapAccountData *adata = imap_adata_get(m);
1799  struct ImapMboxData *mdata = imap_mdata_get(m);
1800 
1801  if (!e || !adata || (adata->mailbox != m))
1802  return -1;
1803 
1804  mdata->bcache = msg_cache_open(m);
1805  char id[64];
1806  snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1807  return mutt_bcache_del(mdata->bcache, id);
1808 }
1809 
1816 {
1817  struct ImapAccountData *adata = imap_adata_get(m);
1818  struct ImapMboxData *mdata = imap_mdata_get(m);
1819 
1820  if (!adata || (adata->mailbox != m))
1821  return -1;
1822 
1823  mdata->bcache = msg_cache_open(m);
1825 
1826  return 0;
1827 }
1828 
1847 char *imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
1848 {
1849  struct ImapAccountData *adata = imap_adata_get(m);
1850  if (!adata || (adata->mailbox != m))
1851  return NULL;
1852 
1853  struct ImapHeader newh = { 0 };
1854  struct ImapEmailData old_edata = { 0 };
1855  int local_changes = e->changed;
1856 
1857  struct ImapEmailData *edata = e->edata;
1858  newh.edata = edata;
1859 
1860  mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1861  s = msg_parse_flags(&newh, s);
1862  if (!s)
1863  return NULL;
1864 
1865  /* Update tags system */
1866  /* We take a copy of the tags so we can split the string */
1867  char *tags_copy = mutt_str_dup(edata->flags_remote);
1868  driver_tags_replace(&e->tags, tags_copy);
1869  FREE(&tags_copy);
1870 
1871  /* YAUH (yet another ugly hack): temporarily set context to
1872  * read-write even if it's read-only, so *server* updates of
1873  * flags can be processed by mutt_set_flag. mailbox->changed must
1874  * be restored afterwards */
1875  bool readonly = m->readonly;
1876  m->readonly = false;
1877 
1878  /* This is redundant with the following two checks. Removing:
1879  * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old)); */
1880  set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1881  edata->old, e->old);
1882  set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1883  old_edata.read, edata->read, e->read);
1884  set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1885  old_edata.deleted, edata->deleted, e->deleted);
1886  set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1887  old_edata.flagged, edata->flagged, e->flagged);
1888  set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1889  old_edata.replied, edata->replied, e->replied);
1890 
1891  /* this message is now definitively *not* changed (mutt_set_flag
1892  * marks things changed as a side-effect) */
1893  if (local_changes == 0)
1894  e->changed = false;
1895  m->changed &= !readonly;
1896  m->readonly = readonly;
1897 
1898  return s;
1899 }
1900 
1904 bool imap_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1905 {
1906  struct Envelope *newenv = NULL;
1907  char buf[1024];
1908  char *pc = NULL;
1909  unsigned int bytes;
1910  struct Progress progress;
1911  unsigned int uid;
1912  bool retried = false;
1913  bool read;
1914  int rc;
1915 
1916  /* Sam's weird courier server returns an OK response even when FETCH
1917  * fails. Thanks Sam. */
1918  bool fetched = false;
1919 
1920  struct ImapAccountData *adata = imap_adata_get(m);
1921 
1922  if (!adata || (adata->mailbox != m))
1923  return false;
1924 
1925  struct Email *e = m->emails[msgno];
1926  if (!e)
1927  return false;
1928 
1929  msg->fp = msg_cache_get(m, e);
1930  if (msg->fp)
1931  {
1932  if (imap_edata_get(e)->parsed)
1933  return true;
1934  goto parsemsg;
1935  }
1936 
1937  /* This function is called in a few places after endwin()
1938  * e.g. mutt_pipe_message(). */
1939  bool output_progress = !isendwin() && m->verbose;
1940  if (output_progress)
1941  mutt_message(_("Fetching message..."));
1942 
1943  msg->fp = msg_cache_put(m, e);
1944  if (!msg->fp)
1945  {
1946  struct Buffer *path = mutt_buffer_pool_get();
1947  mutt_buffer_mktemp(path);
1948  msg->fp = mutt_file_fopen(mutt_buffer_string(path), "w+");
1949  unlink(mutt_buffer_string(path));
1950  mutt_buffer_pool_release(&path);
1951 
1952  if (!msg->fp)
1953  return false;
1954  }
1955 
1956  /* mark this header as currently inactive so the command handler won't
1957  * also try to update it. HACK until all this code can be moved into the
1958  * command handler */
1959  e->active = false;
1960 
1961  snprintf(buf, sizeof(buf), "UID FETCH %u %s", imap_edata_get(e)->uid,
1962  ((adata->capabilities & IMAP_CAP_IMAP4REV1) ?
1963  (C_ImapPeek ? "BODY.PEEK[]" : "BODY[]") :
1964  "RFC822"));
1965 
1966  imap_cmd_start(adata, buf);
1967  do
1968  {
1969  rc = imap_cmd_step(adata);
1970  if (rc != IMAP_RES_CONTINUE)
1971  break;
1972 
1973  pc = adata->buf;
1974  pc = imap_next_word(pc);
1975  pc = imap_next_word(pc);
1976 
1977  if (mutt_istr_startswith(pc, "FETCH"))
1978  {
1979  while (*pc)
1980  {
1981  pc = imap_next_word(pc);
1982  if (pc[0] == '(')
1983  pc++;
1984  if (mutt_istr_startswith(pc, "UID"))
1985  {
1986  pc = imap_next_word(pc);
1987  if (mutt_str_atoui(pc, &uid) < 0)
1988  goto bail;
1989  if (uid != imap_edata_get(e)->uid)
1990  {
1991  mutt_error(_(
1992  "The message index is incorrect. Try reopening the mailbox."));
1993  }
1994  }
1995  else if (mutt_istr_startswith(pc, "RFC822") || mutt_istr_startswith(pc, "BODY[]"))
1996  {
1997  pc = imap_next_word(pc);
1998  if (imap_get_literal_count(pc, &bytes) < 0)
1999  {
2000  imap_error("imap_msg_open()", buf);
2001  goto bail;
2002  }
2003  if (output_progress)
2004  {
2005  mutt_progress_init(&progress, _("Fetching message..."), MUTT_PROGRESS_NET, bytes);
2006  }
2007  if (imap_read_literal(msg->fp, adata, bytes, output_progress ? &progress : NULL) < 0)
2008  {
2009  goto bail;
2010  }
2011  /* pick up trailing line */
2012  rc = imap_cmd_step(adata);
2013  if (rc != IMAP_RES_CONTINUE)
2014  goto bail;
2015  pc = adata->buf;
2016 
2017  fetched = true;
2018  }
2019  /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
2020  * change (eg from \Unseen to \Seen).
2021  * Uncommitted changes in neomutt take precedence. If we decide to
2022  * incrementally update flags later, this won't stop us syncing */
2023  else if (!e->changed && mutt_istr_startswith(pc, "FLAGS"))
2024  {
2025  pc = imap_set_flags(m, e, pc, NULL);
2026  if (!pc)
2027  goto bail;
2028  }
2029  }
2030  }
2031  } while (rc == IMAP_RES_CONTINUE);
2032 
2033  /* see comment before command start. */
2034  e->active = true;
2035 
2036  fflush(msg->fp);
2037  if (ferror(msg->fp))
2038  goto bail;
2039 
2040  if (rc != IMAP_RES_OK)
2041  goto bail;
2042 
2043  if (!fetched || !imap_code(adata->buf))
2044  goto bail;
2045 
2046  msg_cache_commit(m, e);
2047 
2048 parsemsg:
2049  /* Update the header information. Previously, we only downloaded a
2050  * portion of the headers, those required for the main display. */
2051  rewind(msg->fp);
2052  /* It may be that the Status header indicates a message is read, but the
2053  * IMAP server doesn't know the message has been \Seen. So we capture
2054  * the server's notion of 'read' and if it differs from the message info
2055  * picked up in mutt_rfc822_read_header, we mark the message (and context
2056  * changed). Another possibility: ignore Status on IMAP? */
2057  read = e->read;
2058  newenv = mutt_rfc822_read_header(msg->fp, e, false, false);
2059  mutt_env_merge(e->env, &newenv);
2060 
2061  /* see above. We want the new status in e->read, so we unset it manually
2062  * and let mutt_set_flag set it correctly, updating context. */
2063  if (read != e->read)
2064  {
2065  e->read = read;
2066  mutt_set_flag(m, e, MUTT_NEW, read);
2067  }
2068 
2069  e->lines = 0;
2070  fgets(buf, sizeof(buf), msg->fp);
2071  while (!feof(msg->fp))
2072  {
2073  e->lines++;
2074  fgets(buf, sizeof(buf), msg->fp);
2075  }
2076 
2077  e->body->length = ftell(msg->fp) - e->body->offset;
2078 
2079  mutt_clear_error();
2080  rewind(msg->fp);
2081  imap_edata_get(e)->parsed = true;
2082 
2083  /* retry message parse if cached message is empty */
2084  if (!retried && ((e->lines == 0) || (e->body->length == 0)))
2085  {
2086  imap_cache_del(m, e);
2087  retried = true;
2088  goto parsemsg;
2089  }
2090 
2091  return true;
2092 
2093 bail:
2094  e->active = true;
2095  mutt_file_fclose(&msg->fp);
2096  imap_cache_del(m, e);
2097  return false;
2098 }
2099 
2105 int imap_msg_commit(struct Mailbox *m, struct Message *msg)
2106 {
2107  int rc = mutt_file_fclose(&msg->fp);
2108  if (rc != 0)
2109  return rc;
2110 
2111  return imap_append_message(m, msg);
2112 }
2113 
2119 int imap_msg_close(struct Mailbox *m, struct Message *msg)
2120 {
2121  return mutt_file_fclose(&msg->fp);
2122 }
2123 
2127 int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
2128 {
2129  int rc = 0;
2130 #ifdef USE_HCACHE
2131  bool close_hc = true;
2132  struct ImapAccountData *adata = imap_adata_get(m);
2133  struct ImapMboxData *mdata = imap_mdata_get(m);
2134  if (!mdata || !adata)
2135  return -1;
2136  if (mdata->hcache)
2137  close_hc = false;
2138  else
2139  imap_hcache_open(adata, mdata);
2140  rc = imap_hcache_put(mdata, e);
2141  if (close_hc)
2143 #endif
2144  return rc;
2145 }
ImapMboxData::hcache
struct HeaderCache * hcache
Definition: mdata.h:62
mutt_str_append_item
void mutt_str_append_item(char **str, const char *item, char sep)
Add string to another separated by sep.
Definition: string.c:466
Envelope
The header of an Email.
Definition: envelope.h:54
IMAP_RES_OK
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:58
read_headers_normal_eval_cache
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:658
msg_cache_commit
static int msg_cache_commit(struct Mailbox *m, struct Email *e)
Add to the message cache.
Definition: message.c:149
IMAP_RES_RESPOND
#define IMAP_RES_RESPOND
+
Definition: private.h:60
imap_cache_del
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1796
Message::flags
struct Message::@1 flags
Email::msgno
int msgno
Number displayed to the user.
Definition: email.h:87
imap_hcache_store_uid_seqset
int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
Store a UID Sequence Set in the header cache.
Definition: util.c:416
STAILQ_INIT
#define STAILQ_INIT(head)
Definition: queue.h:369
MUTT_PROGRESS_NET
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition: progress.h:44
ConnAccount
Login details for a remote server.
Definition: connaccount.h:51
SigInt
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:74
Connection
An open network connection (socket)
Definition: connection.h:34
msg_cache_get
static FILE * msg_cache_get(struct Mailbox *m, struct Email *e)
Get the message cache entry for an email.
Definition: message.c:107
MUTT_FLAG
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:102
imap_hcache_open
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:299
mutt_bcache_open
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:144
QuadOption
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
imap_hcache_put
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:381
ImapMboxData::uid_next
unsigned int uid_next
Definition: mdata.h:51
msn.h
ImapEmailData::replied
bool replied
Definition: edata.h:40
_
#define _(a)
Definition: message.h:28
adata.h
imap_sync_message_for_copy
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:995
Mailbox
A mailbox.
Definition: mailbox.h:81
ImapHeader::content_length
long content_length
Definition: message.h:38
Email::lines
int lines
How many lines in the body of this message?
Definition: email.h:85
IMAP_EXPUNGE_PENDING
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:70
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
mutt_hcache_store_raw
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:584
mutt_bcache_put
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition: bcache.c:208
ImapMboxData::uid_hash
struct HashTable * uid_hash
Definition: mdata.h:58
ImapMboxData::new_mail_count
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
IMAP_EXEC_SUCCESS
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:86
Buffer
String manipulation buffer.
Definition: buffer.h:33
LL_DEBUG3
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
C_DeleteUntag
WHERE bool C_DeleteUntag
Config: Untag messages when they are marked for deletion.
Definition: mutt_globals.h:145
Connection::account
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
Body::offset
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
mailbox_size_add
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:200
Mailbox::msg_deleted
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
driver_tags_replace
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:185
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
C_ImapFetchChunkSize
long C_ImapFetchChunkSize
Config: (imap) Download headers in blocks of this size.
Definition: config.c:46
mutt_seqset_iterator_new
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1074
ImapHeader::edata
struct ImapEmailData * edata
Definition: message.h:35
BodyCache
Local cache of email bodies.
Definition: bcache.c:53
mutt_buffer_add_printf
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
ImapEmailData::flags_remote
char * flags_remote
Definition: edata.h:48
ImapEmailData::old
bool old
Definition: edata.h:37
imap_munge_mbox_name
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:910
imap_msg_save_hcache
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:2127
msg_cache_clean_cb
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
imap_code
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1197
mutt_buffer_mktemp
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
ImapAccountData::buf
char * buf
Definition: adata.h:56
ImapAccountData::capabilities
ImapCapFlags capabilities
Definition: adata.h:52
EmailNode::email
struct Email * email
Email in the list.
Definition: email.h:127
imap_cmd_step
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1074
mutt_buffer_init
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
lib.h
MUTT_YES
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:40
msg_fetch_header
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
imap_msn_reserve
void imap_msn_reserve(struct MSN *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:41
ImapEmailData::deleted
bool deleted
Definition: edata.h:38
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
IMAP_FLAGS_PENDING
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:72
Message::received
time_t received
the time at which this message was received
Definition: mx.h:107
set_changed_flag
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:618
mutt_globals.h
mutt_file_fopen
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
imap_msg_close
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close()
Definition: message.c:2119
SAVE_MOVE
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: commands.h:62
FREE
#define FREE(x)
Definition: memory.h:40
C_Autocrypt
bool C_Autocrypt
Config: Enables the Autocrypt feature.
Definition: config.c:37
EmailNode
List of Emails.
Definition: email.h:125
STAILQ_FIRST
#define STAILQ_FIRST(head)
Definition: queue.h:347
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
C_ImapHeaders
char * C_ImapHeaders
Config: (imap) Additional email headers to download when getting index.
Definition: config.c:47
imap_alloc_uid_hash
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition: message.c:509
mdata.h
Email::edata_free
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:117
msg_parse_fetch
static int msg_parse_fetch(struct ImapHeader *h, char *s)
handle headers returned from header fetch
Definition: message.c:296
mutt_bcache_commit
int mutt_bcache_commit(struct BodyCache *bcache, const char *id)
Move a temporary file into the Body Cache.
Definition: bcache.c:248
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
private.h
mutt_buffer_reset
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
msg_cache_put
static FILE * msg_cache_put(struct Mailbox *m, struct Email *e)
Put an email into the message cache.
Definition: message.c:128
imap_exec_msgset
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:917
imap_adata_get
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:86
email_new
struct Email * email_new(void)
Create a new Email.
Definition: email.c:72
lib.h
mutt_date_make_imap
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:546
STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
ImapAccountData::conn
struct Connection * conn
Definition: adata.h:38
Mailbox::vcount
int vcount
The number of virtual messages.
Definition: mailbox.h:102
MUTT_READ
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:96
SKIPWS
#define SKIPWS(ch)
Definition: string2.h:46
mutt_hash_int_find
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
mutt_str_atol
int mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: string.c:188
mutt_date_parse_imap
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:589
IMAP_CAP_IMAP4REV1
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:126
lib.h
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
mutt_socket.h
Email::received
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
protos.h
mutt_progress_init
void mutt_progress_init(struct Progress *progress, const char *msg, enum ProgressType type, size_t size)
Set up a progress bar.
Definition: progress.c:153
MUTT_PURGE
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:100
Email::active
bool active
Message is not to be removed.
Definition: email.h:59
mutt_bcache_get
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:180
imap_account_match
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1043
mx_alloc_memory
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1230
ImapEmailData::msn
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
mutt_buffer_addch
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
imap_hcache_get_uid_seqset
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition: util.c:452
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
imap_hcache_close
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:340
Mailbox::mdata
void * mdata
Driver specific data.
Definition: mailbox.h:136
imap_msn_get
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:80
MUTT_NEW
@ MUTT_NEW
New messages.
Definition: mutt.h:93
lib.h
muttlib.h
imap_fetch_msn_seqset
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
imap_read_headers
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:1283
Message::path
char * path
path to temp file
Definition: mx.h:97
Progress
A progress bar.
Definition: progress.h:50
Body::length
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Mailbox::changed
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
Message::draft
bool draft
Definition: mx.h:105
MessageSaveOpt
MessageSaveOpt
Message save option.
Definition: commands.h:59
ImapEmailData
IMAP-specific Email data -.
Definition: edata.h:33
lib.h
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
mutt_socket_write_n
#define mutt_socket_write_n(conn, buf, len)
Definition: mutt_socket.h:39
Message::read
bool read
Definition: mx.h:102
message.h
mutt_clear_error
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
imap_msg_open
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:1904
mutt_seqset_iterator_free
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1154
imap_next_word
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:788
MAX
#define MAX(a, b)
Definition: memory.h:30
ImapEmailData::uid
unsigned int uid
32-bit Message UID
Definition: edata.h:44
ImapMboxData::bcache
struct BodyCache * bcache
Definition: mdata.h:60
imap_cmd_start
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1060
mutt_hcache_free_raw
void mutt_hcache_free_raw(struct HeaderCache *hc, void **data)
Multiplexor for StoreOps::free.
Definition: hcache.c:514
flush_buffer
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition: message.c:469
imap_copy_messages
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:1623
mutt_socket_send
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
MUTT_PROGRESS_READ
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: progress.h:42
imap_error
void imap_error(const char *where, const char *msg)
show an error and abort
Definition: util.c:663
Email::env
struct Envelope * env
Envelope information.
Definition: email.h:90
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Mailbox::email_max
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
MUTT_REPLIED
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:95
MUTT_NO
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:39
imap_msn_free
void imap_msn_free(struct MSN *msn)
Free the cache.
Definition: msn.c:59
mutt_bcache_list
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:330
imap_read_literal
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:596
Message::replied
bool replied
Definition: mx.h:104
Mailbox::msg_flagged
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
Message::fp
FILE * fp
pointer to the message data
Definition: mx.h:96
read_headers_fetch_new
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:1037
Email::flagged
bool flagged
Marked important?
Definition: email.h:43
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Mailbox::verbose
bool verbose
Display status messages?
Definition: mailbox.h:118
ImapAccountData
IMAP-specific Account data -.
Definition: adata.h:36
imap_close_connection
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:843
read_headers_qresync_eval_cache
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition: message.c:800
lib.h
ImapMboxData
IMAP-specific Mailbox data -.
Definition: mdata.h:38
MUTT_OLD
@ MUTT_OLD
Old messages.
Definition: mutt.h:94
mutt_logging.h
imap_edata_free
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:37
mutt_flushinp
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:923
lib.h
imap_cache_clean
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1815
Message::flagged
bool flagged
Definition: mx.h:103
Email::deleted
bool deleted
Email is deleted.
Definition: email.h:45
imap_msn_highest
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:69
MUTT_HASH_NO_FLAGS
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:97
mutt_bcache_del
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:265
imap_hcache_get
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:356
imap_fix_path
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:685
Mailbox::readonly
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:119
mutt_str_cat
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
read_headers_condstore_qresync_updates
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:869
imap_hcache_clear_uid_seqset
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
IMAP_CAP_CONDSTORE
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:139
imap_get_qualifier
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:771
MUTT_DELETE
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:98
imap_msg_commit
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: message.c:2105
mutt_hash_int_insert
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
Email::edata
void * edata
Driver-specific data.
Definition: email.h:111
Email::tags
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
imap_exec
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:1245
IMAP_DATELEN
#define IMAP_DATELEN
Definition: private.h:92
IMAP_RES_CONTINUE
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:59
MUTT_TAG
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:103
progress.h
mutt.h
mutt_hcache_delete_record
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:604
mutt_seqset_iterator_next
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1095
ImapAccountData::mailbox
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:73
mutt_istr_startswith
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
ImapEmailData::flagged
bool flagged
Definition: edata.h:39
imap_append_message
int imap_append_message(struct Mailbox *m, struct Message *msg)
Write an email back to the server.
Definition: message.c:1493
msg_cache_open
static struct BodyCache * msg_cache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c:79
MessageType
MessageType
To set flags or match patterns.
Definition: mutt.h:89
IMAP_EXEC_ERROR
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:87
mutt_hash_int_new
struct HashTable * mutt_hash_int_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with integer keys)
Definition: hash.c:277
C_ImapPeek
bool C_ImapPeek
Config: (imap) Don't mark messages as read when fetching them from the server.
Definition: config.c:55
imap_get_literal_count
int imap_get_literal_count(const char *buf, unsigned int *bytes)
write number of bytes in an IMAP literal into bytes
Definition: util.c:744
imap_mdata_get
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:55
mutt_buffer_len
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
IMAP_CMD_QUEUE
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:77
ImapEmailData::read
bool read
Definition: edata.h:36
Account::adata
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
C_MarkOld
bool C_MarkOld
Config: Mark new emails as old when leaving the mailbox.
Definition: globals.c:36
Email::index
int index
The absolute (unsorted) message number.
Definition: email.h:86
mutt_rfc822_read_header
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1112
imap_parse_path
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
imap_create_mailbox
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:447
email_free
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:43
mutt_progress_update
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
plen
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
mutt_buffer_addstr
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
Mailbox::msg_unread
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
IMAP_CMD_NO_FLAGS
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:75
Mailbox::msg_tagged
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
STAILQ_NEXT
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
IMAP_RES_BAD
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:57
ImapMboxData::reopen
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
IMAP_NEWMAIL_PENDING
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:71
mutt_hcache_fetch_raw
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:496
Email::attach_del
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
mailbox_path
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:206
mx.h
lib.h
lib.h
mutt_str_atoui
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:282
imap_set_flags
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:1847
Email::replied
bool replied
Email has been replied to.
Definition: email.h:54
mutt_yesorno
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:380
Buffer::data
char * data
Pointer to data.
Definition: buffer.h:35
IS_SPACE
#define IS_SPACE(ch)
Definition: string2.h:38
IMAP_CAP_IMAP4
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:125
Email
The envelope/body of an email.
Definition: email.h:37
imap_edata_new
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:53
SeqsetIterator
UID Sequence Set Iterator.
Definition: private.h:170
lib.h
mutt_env_merge
void mutt_env_merge(struct Envelope *base, struct Envelope **extra)
Merge the headers of two Envelopes.
Definition: envelope.c:151
imap_expunge_mailbox
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:671
query_abort_header_download
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:485
edata.h
mutt_message
#define mutt_message(...)
Definition: logging.h:83
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
Mailbox::msg_new
int msg_new
Number of new messages.
Definition: mailbox.h:95
mutt_str_asprintf
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1095
mutt_buffer_printf
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
imap_edata_get
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:63
msg_parse_flags
static char * msg_parse_flags(struct ImapHeader *h, char *s)
read a FLAGS token into an ImapHeader
Definition: message.c:191
Email::read
bool read
Email is read.
Definition: email.h:51
ImapEmailData::parsed
bool parsed
Definition: edata.h:42
C_ConfirmCreate
WHERE bool C_ConfirmCreate
Config: Confirm before creating a new mailbox.
Definition: mutt_globals.h:142
imap_msn_set
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:92
mutt_buffer_strcpy
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
C_ImapCondstore
bool C_ImapCondstore
Config: (imap) Enable the CONDSTORE extension.
Definition: config.c:41
imap_cachepath
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition: util.c:715
LL_DEBUG2
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
imap_verify_qresync
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition: message.c:965
mutt_hash_free
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
ImapMboxData::uidvalidity
uint32_t uidvalidity
Definition: mdata.h:50
ImapAccountData::qresync
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition: adata.h:60
Email::changed
bool changed
Email has been edited.
Definition: email.h:48
Message
A local copy of an email.
Definition: mx.h:94
ImapMboxData::name
char * name
Mailbox name.
Definition: mdata.h:40
ImapHeader::received
time_t received
Definition: message.h:37
ImapHeader
IMAP-specific header.
Definition: message.h:33
Email::body
struct Body * body
List of MIME parts.
Definition: email.h:91
IMAP_REOPEN_ALLOW
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:68
ImapAccountData::delim
char delim
Definition: adata.h:72
idx
size_t idx
Definition: mailbox.c:234
mutt_error
#define mutt_error(...)
Definition: logging.h:84
mutt_str_copy
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716