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