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