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