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