NeoMutt  2025-09-05-7-geaa2bd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c
Go to the documentation of this file.
1
36#include "config.h"
37#include <limits.h>
38#include <stdbool.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <string.h>
42#include <unistd.h>
43#include "private.h"
44#include "mutt/lib.h"
45#include "config/lib.h"
46#include "email/lib.h"
47#include "core/lib.h"
48#include "conn/lib.h"
49#include "gui/lib.h"
50#include "mutt.h"
51#include "message.h"
52#include "lib.h"
53#include "bcache/lib.h"
54#include "progress/lib.h"
55#include "question/lib.h"
56#include "adata.h"
57#include "edata.h"
58#include "external.h"
59#include "mdata.h"
60#include "msg_set.h"
61#include "msn.h"
62#include "mutt_logging.h"
63#include "mx.h"
64#include "protos.h"
65#ifdef ENABLE_NLS
66#include <libintl.h>
67#endif
68#ifdef USE_HCACHE
69#include "hcache/lib.h"
70#endif
71
72struct BodyCache;
73
80static struct BodyCache *imap_bcache_open(struct Mailbox *m)
81{
84
85 if (!adata || (adata->mailbox != m))
86 return NULL;
87
88 if (mdata->bcache)
89 return mdata->bcache;
90
91 struct Buffer *mailbox = buf_pool_get();
92 imap_cachepath(adata->delim, mdata->name, mailbox);
93
94 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account, buf_string(mailbox));
95 buf_pool_release(&mailbox);
96
97 return bc;
98}
99
107static FILE *msg_cache_get(struct Mailbox *m, struct Email *e)
108{
110 struct ImapMboxData *mdata = imap_mdata_get(m);
111
112 if (!e || !adata || (adata->mailbox != m))
113 return NULL;
114
115 mdata->bcache = imap_bcache_open(m);
116 char id[64] = { 0 };
117 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
118 return mutt_bcache_get(mdata->bcache, id);
119}
120
128static FILE *msg_cache_put(struct Mailbox *m, struct Email *e)
129{
131 struct ImapMboxData *mdata = imap_mdata_get(m);
132
133 if (!e || !adata || (adata->mailbox != m))
134 return NULL;
135
136 mdata->bcache = imap_bcache_open(m);
137 char id[64] = { 0 };
138 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
139 return mutt_bcache_put(mdata->bcache, id);
140}
141
149static int msg_cache_commit(struct Mailbox *m, struct Email *e)
150{
152 struct ImapMboxData *mdata = imap_mdata_get(m);
153
154 if (!e || !adata || (adata->mailbox != m))
155 return -1;
156
157 mdata->bcache = imap_bcache_open(m);
158 char id[64] = { 0 };
159 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
160
161 return mutt_bcache_commit(mdata->bcache, id);
162}
163
168static int imap_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
169{
170 uint32_t uv = 0;
171 unsigned int uid = 0;
172 struct ImapMboxData *mdata = data;
173
174 if (sscanf(id, "%u-%u", &uv, &uid) != 2)
175 return 0;
176
177 /* bad UID */
178 if ((uv != mdata->uidvalidity) || !mutt_hash_int_find(mdata->uid_hash, uid))
180
181 return 0;
182}
183
191static char *msg_parse_flags(struct ImapHeader *h, char *s)
192{
193 struct ImapEmailData *edata = h->edata;
194
195 /* sanity-check string */
196 size_t plen = mutt_istr_startswith(s, "FLAGS");
197 if (plen == 0)
198 {
199 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
200 return NULL;
201 }
202 s += plen;
203 SKIPWS(s);
204 if (*s != '(')
205 {
206 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
207 return NULL;
208 }
209 s++;
210
211 FREE(&edata->flags_system);
212 FREE(&edata->flags_remote);
213
214 edata->deleted = false;
215 edata->flagged = false;
216 edata->replied = false;
217 edata->read = false;
218 edata->old = false;
219
220 /* start parsing */
221 while (*s && (*s != ')'))
222 {
223 if ((plen = mutt_istr_startswith(s, "\\deleted")))
224 {
225 s += plen;
226 edata->deleted = true;
227 }
228 else if ((plen = mutt_istr_startswith(s, "\\flagged")))
229 {
230 s += plen;
231 edata->flagged = true;
232 }
233 else if ((plen = mutt_istr_startswith(s, "\\answered")))
234 {
235 s += plen;
236 edata->replied = true;
237 }
238 else if ((plen = mutt_istr_startswith(s, "\\seen")))
239 {
240 s += plen;
241 edata->read = true;
242 }
243 else if ((plen = mutt_istr_startswith(s, "\\recent")))
244 {
245 s += plen;
246 }
247 else if ((plen = mutt_istr_startswith(s, "old")))
248 {
249 s += plen;
250 edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
251 }
252 else
253 {
254 char ctmp;
255 char *flag_word = s;
256 bool is_system_keyword = mutt_istr_startswith(s, "\\");
257
258 while (*s && !mutt_isspace(*s) && (*s != ')'))
259 s++;
260
261 ctmp = *s;
262 *s = '\0';
263
264 struct Buffer *buf = buf_pool_get();
265 if (is_system_keyword)
266 {
267 /* store other system flags as well (mainly \\Draft) */
268 buf_addstr(buf, edata->flags_system);
269 buf_join_str(buf, flag_word, ' ');
270 edata->flags_system = buf_strdup(buf);
271 }
272 else
273 {
274 /* store custom flags as well */
275 buf_addstr(buf, edata->flags_remote);
276 buf_join_str(buf, flag_word, ' ');
277 edata->flags_remote = buf_strdup(buf);
278 }
279 buf_pool_release(&buf);
280
281 *s = ctmp;
282 }
283 SKIPWS(s);
284 }
285
286 /* wrap up, or note bad flags response */
287 if (*s == ')')
288 {
289 s++;
290 }
291 else
292 {
293 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
294 return NULL;
295 }
296
297 return s;
298}
299
308static int msg_parse_fetch(struct ImapHeader *h, char *s)
309{
310 if (!s)
311 return -1;
312
313 char tmp[128] = { 0 };
314 char *ptmp = NULL;
315 size_t plen = 0;
316
317 while (*s)
318 {
319 SKIPWS(s);
320
321 if (mutt_istr_startswith(s, "FLAGS"))
322 {
323 s = msg_parse_flags(h, s);
324 if (!s)
325 return -1;
326 }
327 else if ((plen = mutt_istr_startswith(s, "UID")))
328 {
329 s += plen;
330 SKIPWS(s);
331 if (!mutt_str_atoui(s, &h->edata->uid))
332 return -1;
333
334 s = imap_next_word(s);
335 }
336 else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
337 {
338 s += plen;
339 SKIPWS(s);
340 if (*s != '\"')
341 {
342 mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
343 return -1;
344 }
345 s++;
346 ptmp = tmp;
347 while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
348 *ptmp++ = *s++;
349 if (*s != '\"')
350 return -1;
351 s++; /* skip past the trailing " */
352 *ptmp = '\0';
354 }
355 else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
356 {
357 s += plen;
358 SKIPWS(s);
359 ptmp = tmp;
360 while (mutt_isdigit(*s) && (ptmp != (tmp + sizeof(tmp) - 1)))
361 *ptmp++ = *s++;
362 *ptmp = '\0';
363 if (!mutt_str_atol(tmp, &h->content_length))
364 return -1;
365 }
366 else if (mutt_istr_startswith(s, "BODY") || mutt_istr_startswith(s, "RFC822.HEADER"))
367 {
368 /* handle above, in msg_fetch_header */
369 return -2;
370 }
371 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
372 {
373 s += plen;
374 SKIPWS(s);
375 if (*s != '(')
376 {
377 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
378 return -1;
379 }
380 s++;
381 while (*s && (*s != ')'))
382 s++;
383 if (*s == ')')
384 {
385 s++;
386 }
387 else
388 {
389 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
390 return -1;
391 }
392 }
393 else if (*s == ')')
394 {
395 s++; /* end of request */
396 }
397 else if (*s)
398 {
399 /* got something i don't understand */
400 imap_error("msg_parse_fetch", s);
401 return -1;
402 }
403 }
404
405 return 0;
406}
407
420static int msg_fetch_header(struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
421{
422 int rc = -1; /* default now is that string isn't FETCH response */
423
425
426 if (buf[0] != '*')
427 return rc;
428
429 /* skip to message number */
431 if (!mutt_str_atoui(buf, &ih->edata->msn))
432 return rc;
433
434 /* find FETCH tag */
436 if (!mutt_istr_startswith(buf, "FETCH"))
437 return rc;
438
439 rc = -2; /* we've got a FETCH response, for better or worse */
440 buf = strchr(buf, '(');
441 if (!buf)
442 return rc;
443 buf++;
444
445 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
446 * read header lines and call it again. Silly. */
447 int parse_rc = msg_parse_fetch(ih, buf);
448 if (parse_rc == 0)
449 return 0;
450 if ((parse_rc != -2) || !fp)
451 return rc;
452
453 unsigned int bytes = 0;
454 if (imap_get_literal_count(buf, &bytes) == 0)
455 {
456 imap_read_literal(fp, adata, bytes, NULL);
457
458 /* we may have other fields of the FETCH _after_ the literal
459 * (eg Domino puts FLAGS here). Nothing wrong with that, either.
460 * This all has to go - we should accept literals and nonliterals
461 * interchangeably at any time. */
463 return rc;
464
465 if (msg_parse_fetch(ih, adata->buf) == -1)
466 return rc;
467 }
468
469 rc = 0; /* success */
470
471 /* subtract headers from message size - unfortunately only the subset of
472 * headers we've requested. */
473 ih->content_length -= bytes;
474
475 return rc;
476}
477
486static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
487{
488 buf[*len] = '\0';
489 int rc = mutt_socket_write_n(conn, buf, *len);
490 *len = 0;
491 return rc;
492}
493
503{
504 bool abort = false;
505
507 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
508 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
509 {
510 abort = true;
512 }
513 SigInt = false;
514
515 return abort;
516}
517
526static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
527{
528 struct ImapMboxData *mdata = adata->mailbox->mdata;
529 if (!mdata->uid_hash)
530 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NO_FLAGS);
531}
532
550static unsigned int imap_fetch_msn_seqset(struct Buffer *buf, struct ImapAccountData *adata,
551 bool evalhc, unsigned int msn_begin,
552 unsigned int msn_end, unsigned int *fetch_msn_end)
553{
554 struct ImapMboxData *mdata = adata->mailbox->mdata;
555 unsigned int max_headers_per_fetch = UINT_MAX;
556 bool first_chunk = true;
557 int state = 0; /* 1: single msn, 2: range of msn */
558 unsigned int msn;
559 unsigned int range_begin = 0;
560 unsigned int range_end = 0;
561 unsigned int msn_count = 0;
562
563 buf_reset(buf);
564 if (msn_end < msn_begin)
565 return 0;
566
567 const long c_imap_fetch_chunk_size = cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
568 if (c_imap_fetch_chunk_size > 0)
569 max_headers_per_fetch = c_imap_fetch_chunk_size;
570
571 if (!evalhc)
572 {
573 if ((msn_end - msn_begin + 1) <= max_headers_per_fetch)
574 *fetch_msn_end = msn_end;
575 else
576 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
577 buf_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
578 return (*fetch_msn_end - msn_begin + 1);
579 }
580
581 for (msn = msn_begin; msn <= (msn_end + 1); msn++)
582 {
583 if ((msn_count < max_headers_per_fetch) && (msn <= msn_end) &&
584 !imap_msn_get(&mdata->msn, msn - 1))
585 {
586 msn_count++;
587
588 switch (state)
589 {
590 case 1: /* single: convert to a range */
591 state = 2;
593
594 case 2: /* extend range ending */
595 range_end = msn;
596 break;
597 default:
598 state = 1;
599 range_begin = msn;
600 break;
601 }
602 }
603 else if (state)
604 {
605 if (first_chunk)
606 first_chunk = false;
607 else
608 buf_addch(buf, ',');
609
610 if (state == 1)
611 buf_add_printf(buf, "%u", range_begin);
612 else if (state == 2)
613 buf_add_printf(buf, "%u:%u", range_begin, range_end);
614 state = 0;
615
616 if ((buf_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
617 break;
618 }
619 }
620
621 /* The loop index goes one past to terminate the range if needed. */
622 *fetch_msn_end = msn - 1;
623
624 return msn_count;
625}
626
642static void set_changed_flag(struct Mailbox *m, struct Email *e, int local_changes,
643 bool *server_changes, enum MessageType flag_name,
644 bool old_hd_flag, bool new_hd_flag, bool h_flag)
645{
646 /* If there are local_changes, we only want to note if the server
647 * flags have changed, so we can set a reopen flag in
648 * cmd_parse_fetch(). We don't want to count a local modification
649 * to the header flag as a "change". */
650 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
651 return;
652
653 if (new_hd_flag == h_flag)
654 return;
655
656 if (server_changes)
657 *server_changes = true;
658
659 /* Local changes have priority */
660 if (local_changes == 0)
661 mutt_set_flag(m, e, flag_name, new_hd_flag, true);
662}
663
664#ifdef USE_HCACHE
683 unsigned int msn_end, unsigned int uid_next,
684 bool store_flag_updates, bool eval_condstore)
685{
686 struct Progress *progress = NULL;
687 char buf[1024] = { 0 };
688 int rc = -1;
689
690 struct Mailbox *m = adata->mailbox;
691 struct ImapMboxData *mdata = imap_mdata_get(m);
692 int idx = m->msg_count;
693
694 if (m->verbose)
695 {
696 /* L10N: Comparing the cached data with the IMAP server's data */
697 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
698 progress_set_message(progress, _("Evaluating cache..."));
699 }
700
701 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
702 * the flags in the header cache, and update them further below.
703 * Otherwise, we fetch the current state of the flags here. */
704 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
705 eval_condstore ? "" : " FLAGS");
706
707 imap_cmd_start(adata, buf);
708
710 int mfhrc = 0;
711 struct ImapHeader h = { 0 };
712 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
713 {
715 goto fail;
716
717 progress_update(progress, msgno, -1);
718
719 memset(&h, 0, sizeof(h));
720 h.edata = imap_edata_new();
721 do
722 {
723 rc = imap_cmd_step(adata);
724 if (rc != IMAP_RES_CONTINUE)
725 break;
726
727 mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
728 if (mfhrc < 0)
729 continue;
730
731 if (!h.edata->uid)
732 {
733 mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
734 h.edata->msn);
735 continue;
736 }
737
738 if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
739 {
740 mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
741 h.edata->msn);
742 continue;
743 }
744
745 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
746 {
747 mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
748 h.edata->msn);
749 continue;
750 }
751
752 struct Email *e = imap_hcache_get(mdata, h.edata->uid);
753 m->emails[idx] = e;
754 if (e)
755 {
756 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
757 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
758
759 e->index = h.edata->uid;
760 /* messages which have not been expunged are ACTIVE (borrowed from mh
761 * folders) */
762 e->active = true;
763 e->changed = false;
764 if (eval_condstore)
765 {
766 h.edata->read = e->read;
767 h.edata->old = e->old;
768 h.edata->deleted = e->deleted;
769 h.edata->flagged = e->flagged;
770 h.edata->replied = e->replied;
771 }
772 else
773 {
774 e->read = h.edata->read;
775 e->old = h.edata->old;
776 e->deleted = h.edata->deleted;
777 e->flagged = h.edata->flagged;
778 e->replied = h.edata->replied;
779 }
780
781 /* mailbox->emails[msgno]->received is restored from hcache_fetch_email() */
782 e->edata = h.edata;
784
785 /* We take a copy of the tags so we can split the string */
786 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
787 driver_tags_replace(&e->tags, tags_copy);
788 FREE(&tags_copy);
789
790 m->msg_count++;
791 mailbox_size_add(m, e);
792
793 /* If this is the first time we are fetching, we need to
794 * store the current state of flags back into the header cache */
795 if (!eval_condstore && store_flag_updates)
796 imap_hcache_put(mdata, e);
797
798 h.edata = NULL;
799 idx++;
800 }
801 } while (mfhrc == -1);
802
803 imap_edata_free((void **) &h.edata);
804
805 if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
806 goto fail;
807 }
808
809 rc = 0;
810fail:
811 progress_free(&progress);
812 return rc;
813}
814
827static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
828{
829 int rc;
830 unsigned int uid = 0;
831
832 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
833 struct Mailbox *m = adata->mailbox;
834 struct ImapMboxData *mdata = adata->mailbox->mdata;
835 unsigned int msn = 1;
836
837 if (m->verbose)
838 mutt_message(_("Evaluating cache..."));
839
840 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
841 if (!iter)
842 return -1;
843
844 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
845 {
846 /* The seqset may contain more headers than the fetch request, so
847 * we need to watch and reallocate the context and msn_index */
848 imap_msn_reserve(&mdata->msn, msn);
849
850 struct Email *e = imap_hcache_get(mdata, uid);
851 if (e)
852 {
853 imap_msn_set(&mdata->msn, msn - 1, e);
854
856
858 e->edata = edata;
860
861 e->index = uid;
862 e->active = true;
863 e->changed = false;
864 edata->read = e->read;
865 edata->old = e->old;
866 edata->deleted = e->deleted;
867 edata->flagged = e->flagged;
868 edata->replied = e->replied;
869
870 edata->msn = msn;
871 edata->uid = uid;
873
874 mailbox_size_add(m, e);
875 m->emails[m->msg_count++] = e;
876
877 msn++;
878 }
879 else if (!uid)
880 {
881 /* A non-zero uid missing from the header cache is either the
882 * result of an expunged message (not recorded in the uid seqset)
883 * or a hole in the header cache.
884 *
885 * We have to assume it's an earlier expunge and compact the msn's
886 * in that case, because cmd_parse_vanished() won't find it in the
887 * uid_hash and decrement later msn's there.
888 *
889 * Thus we only increment the uid if the uid was 0: an actual
890 * stored "blank" in the uid seqset.
891 */
892 msn++;
893 }
894 }
895
897
898 return rc;
899}
900
914 unsigned int msn_end, unsigned int uid_next,
915 unsigned long long hc_modseq, bool eval_qresync)
916{
917 struct Progress *progress = NULL;
918 char buf[1024] = { 0 };
919 unsigned int header_msn = 0;
920
921 struct Mailbox *m = adata->mailbox;
922 struct ImapMboxData *mdata = imap_mdata_get(m);
923
924 if (m->verbose)
925 {
926 /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
927 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
928 progress_set_message(progress, _("Fetching flag updates..."));
929 }
930
931 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
932 uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
933
934 imap_cmd_start(adata, buf);
935
936 int rc = IMAP_RES_CONTINUE;
937 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
938 {
940 goto fail;
941
942 progress_update(progress, msgno, -1);
943
944 /* cmd_parse_fetch will update the flags */
945 rc = imap_cmd_step(adata);
946 if (rc != IMAP_RES_CONTINUE)
947 break;
948
949 /* so we just need to grab the header and persist it back into
950 * the header cache */
951 char *fetch_buf = adata->buf;
952 if (fetch_buf[0] != '*')
953 continue;
954
955 fetch_buf = imap_next_word(fetch_buf);
956 if (!mutt_isdigit(*fetch_buf) || !mutt_str_atoui(fetch_buf, &header_msn))
957 continue;
958
959 if ((header_msn < 1) || (header_msn > msn_end) ||
960 !imap_msn_get(&mdata->msn, header_msn - 1))
961 {
962 mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
963 header_msn);
964 continue;
965 }
966
967 imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
968 }
969
970 if (rc != IMAP_RES_OK)
971 goto fail;
972
973 /* The IMAP flag setting as part of cmd_parse_fetch() ends up
974 * flipping these on. */
975 mdata->check_status &= ~IMAP_FLAGS_PENDING;
976 m->changed = false;
977
978 /* VANISHED handling: we need to empty out the messages */
979 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
980 {
982 imap_expunge_mailbox(m, false);
983
984 imap_hcache_open(adata, mdata, false);
985 mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
986 }
987
988 /* undo expunge count updates.
989 * mview_update() will do this at the end of the header fetch. */
990 m->vcount = 0;
991 m->msg_tagged = 0;
992 m->msg_deleted = 0;
993 m->msg_new = 0;
994 m->msg_unread = 0;
995 m->msg_flagged = 0;
996 m->changed = false;
997
998 rc = 0;
999fail:
1000 progress_free(&progress);
1001 return rc;
1002}
1003
1012static int imap_verify_qresync(struct Mailbox *m)
1013{
1014 ASSERT(m);
1016 struct ImapMboxData *mdata = imap_mdata_get(m);
1017 if (!adata || (adata->mailbox != m))
1018 return -1;
1019
1020 const size_t max_msn = imap_msn_highest(&mdata->msn);
1021
1022 unsigned int msn;
1023 unsigned int uid;
1024 struct Email *e = NULL;
1025 struct Email *uidh = NULL;
1026
1027 for (int i = 0; i < m->msg_count; i++)
1028 {
1029 e = m->emails[i];
1030 const struct ImapEmailData *edata = imap_edata_get(e);
1031 if (!edata)
1032 goto fail;
1033
1034 msn = imap_edata_get(e)->msn;
1035 uid = imap_edata_get(e)->uid;
1036
1037 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1038 goto fail;
1039
1040 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1041 if (uidh != e)
1042 goto fail;
1043 }
1044
1045 return 0;
1046
1047fail:
1048 imap_msn_free(&mdata->msn);
1049 mutt_hash_free(&mdata->uid_hash);
1053
1054 for (int i = 0; i < m->msg_count; i++)
1055 {
1056 if (m->emails[i] && m->emails[i]->edata)
1057 imap_edata_free(&m->emails[i]->edata);
1058 email_free(&m->emails[i]);
1059 }
1060 m->msg_count = 0;
1061 m->size = 0;
1062 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1064 imap_hcache_close(mdata);
1065
1066 if (m->verbose)
1067 {
1068 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1069 sanity check. If that fails, Mutt reopens the mailbox using a normal
1070 download. */
1071 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1072 }
1073 return -1;
1074}
1075
1076#endif /* USE_HCACHE */
1077
1089static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin,
1090 unsigned int msn_end, bool evalhc,
1091 unsigned int *maxuid, bool initial_download)
1092{
1093 int rc = -1;
1094 unsigned int fetch_msn_end = 0;
1095 struct Progress *progress = NULL;
1096 char *hdrreq = NULL;
1097 struct Buffer *tempfile = NULL;
1098 FILE *fp = NULL;
1099 struct ImapHeader h = { 0 };
1100 struct Buffer *buf = NULL;
1101 static const char *const want_headers = "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES "
1102 "CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO "
1103 "LINES LIST-POST LIST-SUBSCRIBE LIST-UNSUBSCRIBE X-LABEL "
1104 "X-ORIGINAL-TO";
1105
1107 struct ImapMboxData *mdata = imap_mdata_get(m);
1108 struct ImapEmailData *edata = NULL;
1109
1110 if (!adata || (adata->mailbox != m))
1111 return -1;
1112
1113 struct Buffer *hdr_list = buf_pool_get();
1114 buf_strcpy(hdr_list, want_headers);
1115 const char *const c_imap_headers = cs_subset_string(NeoMutt->sub, "imap_headers");
1116 if (c_imap_headers)
1117 {
1118 buf_addch(hdr_list, ' ');
1119 buf_addstr(hdr_list, c_imap_headers);
1120 }
1121#ifdef USE_AUTOCRYPT
1122 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1123 if (c_autocrypt)
1124 {
1125 buf_addch(hdr_list, ' ');
1126 buf_addstr(hdr_list, "AUTOCRYPT");
1127 }
1128#endif
1129
1130 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1131 {
1132 mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", buf_string(hdr_list));
1133 }
1134 else if (adata->capabilities & IMAP_CAP_IMAP4)
1135 {
1136 mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", buf_string(hdr_list));
1137 }
1138 else
1139 { /* Unable to fetch headers for lower versions */
1140 mutt_error(_("Unable to fetch headers from this IMAP server version"));
1141 goto bail;
1142 }
1143
1144 buf_pool_release(&hdr_list);
1145
1146 /* instead of downloading all headers and then parsing them, we parse them
1147 * as they come in. */
1148 tempfile = buf_pool_get();
1149 buf_mktemp(tempfile);
1150 fp = mutt_file_fopen(buf_string(tempfile), "w+");
1151 if (!fp)
1152 {
1153 mutt_error(_("Could not create temporary file %s"), buf_string(tempfile));
1154 goto bail;
1155 }
1156 unlink(buf_string(tempfile));
1157 buf_pool_release(&tempfile);
1158
1159 if (m->verbose)
1160 {
1161 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
1162 progress_set_message(progress, _("Fetching message headers..."));
1163 }
1164
1165 buf = buf_pool_get();
1166
1167 /* NOTE:
1168 * The (fetch_msn_end < msn_end) used to be important to prevent
1169 * an infinite loop, in the event the server did not return all
1170 * the headers (due to a pending expunge, for example).
1171 *
1172 * I believe the new chunking imap_fetch_msn_seqset()
1173 * implementation and "msn_begin = fetch_msn_end + 1" assignment
1174 * at the end of the loop makes the comparison unneeded, but to be
1175 * cautious I'm keeping it.
1176 */
1177 edata = imap_edata_new();
1178 while ((fetch_msn_end < msn_end) &&
1179 imap_fetch_msn_seqset(buf, adata, evalhc, msn_begin, msn_end, &fetch_msn_end))
1180 {
1181 char *cmd = NULL;
1182 mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1183 buf_string(buf), hdrreq);
1184 imap_cmd_start(adata, cmd);
1185 FREE(&cmd);
1186
1187 int msgno = msn_begin;
1188
1189 while (true)
1190 {
1191 rewind(fp);
1192 memset(&h, 0, sizeof(h));
1193 h.edata = edata;
1194
1195 if (initial_download && SigInt && query_abort_header_download(adata))
1196 {
1197 goto bail;
1198 }
1199
1200 const int rc2 = imap_cmd_step(adata);
1201 if (rc2 != IMAP_RES_CONTINUE)
1202 {
1203 if (rc2 != IMAP_RES_OK)
1204 {
1205 goto bail;
1206 }
1207 break;
1208 }
1209
1210 switch (msg_fetch_header(m, &h, adata->buf, fp))
1211 {
1212 case 0:
1213 break;
1214 case -1:
1215 continue;
1216 case -2:
1217 goto bail;
1218 }
1219
1220 if (!ftello(fp))
1221 {
1222 mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1223 continue;
1224 }
1225
1226 /* make sure we don't get remnants from older larger message headers */
1227 fputs("\n\n", fp);
1228
1229 if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1230 {
1231 mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1232 h.edata->msn);
1233 continue;
1234 }
1235
1236 /* May receive FLAGS updates in a separate untagged response */
1237 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1238 {
1239 mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1240 h.edata->msn);
1241 continue;
1242 }
1243
1244 progress_update(progress, msgno++, -1);
1245
1246 struct Email *e = email_new();
1248
1249 m->emails[m->msg_count++] = e;
1250
1251 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
1252 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
1253
1254 e->index = h.edata->uid;
1255 /* messages which have not been expunged are ACTIVE (borrowed from mh
1256 * folders) */
1257 e->active = true;
1258 e->changed = false;
1259 e->read = h.edata->read;
1260 e->old = h.edata->old;
1261 e->deleted = h.edata->deleted;
1262 e->flagged = h.edata->flagged;
1263 e->replied = h.edata->replied;
1264 e->received = h.received;
1265 e->edata = (void *) imap_edata_clone(h.edata);
1267 STAILQ_INIT(&e->tags);
1268
1269 /* We take a copy of the tags so we can split the string */
1270 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
1271 driver_tags_replace(&e->tags, tags_copy);
1272 FREE(&tags_copy);
1273
1274 if (*maxuid < h.edata->uid)
1275 *maxuid = h.edata->uid;
1276
1277 rewind(fp);
1278 /* NOTE: if Date: header is missing, mutt_rfc822_read_header depends
1279 * on h.received being set */
1280 e->env = mutt_rfc822_read_header(fp, e, false, false);
1281 /* body built as a side-effect of mutt_rfc822_read_header */
1282 e->body->length = h.content_length;
1283 mailbox_size_add(m, e);
1284
1285#ifdef USE_HCACHE
1286 imap_hcache_put(mdata, e);
1287#endif /* USE_HCACHE */
1288 }
1289
1290 /* In case we get new mail while fetching the headers. */
1291 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1292 {
1293 msn_end = mdata->new_mail_count;
1294 mx_alloc_memory(m, msn_end);
1295 imap_msn_reserve(&mdata->msn, msn_end);
1296 mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1297 mdata->new_mail_count = 0;
1298 }
1299
1300 /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1301 * must not get any EXPUNGE/VANISHED responses in the middle of a
1302 * FETCH, nor when no command is in progress (e.g. between the
1303 * chunked FETCH commands). We previously tried to be robust by
1304 * setting:
1305 * msn_begin = mdata->max_msn + 1;
1306 * but with chunking and header cache holes this
1307 * may not be correct. So here we must assume the msn values have
1308 * not been altered during or after the fetch. */
1309 msn_begin = fetch_msn_end + 1;
1310 }
1311
1312 rc = 0;
1313
1314bail:
1315 buf_pool_release(&hdr_list);
1316 buf_pool_release(&buf);
1317 buf_pool_release(&tempfile);
1318 mutt_file_fclose(&fp);
1319 FREE(&hdrreq);
1320 imap_edata_free((void **) &edata);
1321 progress_free(&progress);
1322
1323 return rc;
1324}
1325
1339int imap_read_headers(struct Mailbox *m, unsigned int msn_begin,
1340 unsigned int msn_end, bool initial_download)
1341{
1342 unsigned int maxuid = 0;
1343 int rc = -1;
1344 bool evalhc = false;
1345
1346#ifdef USE_HCACHE
1347 uint32_t uidvalidity = 0;
1348 unsigned int uid_next = 0;
1349 unsigned long long modseq = 0;
1350 bool has_condstore = false;
1351 bool has_qresync = false;
1352 bool eval_condstore = false;
1353 bool eval_qresync = false;
1354 char *uid_seqset = NULL;
1355 const unsigned int msn_begin_save = msn_begin;
1356#endif /* USE_HCACHE */
1357
1359 struct ImapMboxData *mdata = imap_mdata_get(m);
1360 if (!adata || (adata->mailbox != m))
1361 return -1;
1362
1363#ifdef USE_HCACHE
1364retry:
1365#endif /* USE_HCACHE */
1366
1367 /* make sure context has room to hold the mailbox */
1368 mx_alloc_memory(m, msn_end);
1369 imap_msn_reserve(&mdata->msn, msn_end);
1370 imap_alloc_uid_hash(adata, msn_end);
1371
1373 mdata->new_mail_count = 0;
1374
1375#ifdef USE_HCACHE
1376 imap_hcache_open(adata, mdata, true);
1377
1378 if (mdata->hcache && initial_download)
1379 {
1380 hcache_fetch_raw_obj(mdata->hcache, "UIDVALIDITY", 11, &uidvalidity);
1381 hcache_fetch_raw_obj(mdata->hcache, "UIDNEXT", 7, &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 (hcache_fetch_raw_obj(mdata->hcache, "MODSEQ", 6, &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 hcache_store_raw(mdata->hcache, "UIDVALIDITY", 11, &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 hcache_store_raw(mdata->hcache, "UIDNEXT", 7, &mdata->uid_next, sizeof(mdata->uid_next));
1481 }
1482
1483 /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1484 * To do it more often, we'll need to deal with flag updates combined with
1485 * unsync'ed local flag changes. We'll also need to properly sync flags to
1486 * the header cache on close. I'm not sure it's worth the added complexity. */
1487 if (initial_download)
1488 {
1489 if (has_condstore || has_qresync)
1490 {
1491 hcache_store_raw(mdata->hcache, "MODSEQ", 6, &mdata->modseq, sizeof(mdata->modseq));
1492 }
1493 else
1494 {
1495 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1496 }
1497
1498 if (has_qresync)
1500 else
1502 }
1503#endif /* USE_HCACHE */
1504
1505 /* TODO: it's not clear to me why we are calling mx_alloc_memory yet again. */
1507
1508 mdata->reopen |= IMAP_REOPEN_ALLOW;
1509
1510 rc = msn_end;
1511
1512bail:
1513#ifdef USE_HCACHE
1515 FREE(&uid_seqset);
1516#endif /* USE_HCACHE */
1517
1518 return rc;
1519}
1520
1528int imap_append_message(struct Mailbox *m, struct Message *msg)
1529{
1530 if (!m || !msg)
1531 return -1;
1532
1533 FILE *fp = NULL;
1534 char buf[2048] = { 0 };
1535 struct Buffer *internaldate = NULL;
1536 struct Buffer *imap_flags = NULL;
1537 size_t len;
1538 struct Progress *progress = NULL;
1539 size_t sent;
1540 int c, last;
1541 int rc;
1542
1544 struct ImapMboxData *mdata = imap_mdata_get(m);
1545
1546 fp = mutt_file_fopen(msg->path, "r");
1547 if (!fp)
1548 {
1549 mutt_perror("%s", msg->path);
1550 goto fail;
1551 }
1552
1553 /* currently we set the \Seen flag on all messages, but probably we
1554 * should scan the message Status header for flag info. Since we're
1555 * already rereading the whole file for length it isn't any more
1556 * expensive (it'd be nice if we had the file size passed in already
1557 * by the code that writes the file, but that's a lot of changes.
1558 * Ideally we'd have an Email structure with flag info here... */
1559 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1560 {
1561 if ((c == '\n') && (last != '\r'))
1562 len++;
1563
1564 len++;
1565 }
1566 rewind(fp);
1567
1568 if (m->verbose)
1569 {
1570 progress = progress_new(MUTT_PROGRESS_NET, len);
1571 progress_set_message(progress, _("Uploading message..."));
1572 }
1573
1574 internaldate = buf_pool_get();
1575 mutt_date_make_imap(internaldate, msg->received);
1576
1577 imap_flags = buf_pool_get();
1578
1579 if (msg->flags.read)
1580 buf_addstr(imap_flags, " \\Seen");
1581 if (msg->flags.replied)
1582 buf_addstr(imap_flags, " \\Answered");
1583 if (msg->flags.flagged)
1584 buf_addstr(imap_flags, " \\Flagged");
1585 if (msg->flags.draft)
1586 buf_addstr(imap_flags, " \\Draft");
1587
1588 snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1589 imap_flags->data + 1, buf_string(internaldate), (unsigned long) len);
1590 buf_pool_release(&internaldate);
1591
1592 imap_cmd_start(adata, buf);
1593
1594 do
1595 {
1596 rc = imap_cmd_step(adata);
1597 } while (rc == IMAP_RES_CONTINUE);
1598
1599 if (rc != IMAP_RES_RESPOND)
1600 goto cmd_step_fail;
1601
1602 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1603 {
1604 if ((c == '\n') && (last != '\r'))
1605 buf[len++] = '\r';
1606
1607 buf[len++] = c;
1608
1609 if (len > sizeof(buf) - 3)
1610 {
1611 sent += len;
1612 if (flush_buffer(buf, &len, adata->conn) < 0)
1613 goto fail;
1614 progress_update(progress, sent, -1);
1615 }
1616 }
1617
1618 if (len > 0)
1619 if (flush_buffer(buf, &len, adata->conn) < 0)
1620 goto fail;
1621
1622 if (mutt_socket_send(adata->conn, "\r\n") < 0)
1623 goto fail;
1624 mutt_file_fclose(&fp);
1625
1626 do
1627 {
1628 rc = imap_cmd_step(adata);
1629 } while (rc == IMAP_RES_CONTINUE);
1630
1631 if (rc != IMAP_RES_OK)
1632 goto cmd_step_fail;
1633
1634 progress_free(&progress);
1635 buf_pool_release(&imap_flags);
1636 return 0;
1637
1638cmd_step_fail:
1639 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1640 if (rc != IMAP_RES_BAD)
1641 {
1642 char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1643 pc = imap_next_word(pc); /* skip response code */
1644 if (*pc != '\0')
1645 mutt_error("%s", pc);
1646 }
1647
1648fail:
1649 mutt_file_fclose(&fp);
1650 progress_free(&progress);
1651 buf_pool_release(&imap_flags);
1652 return -1;
1653}
1654
1661static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
1662{
1663 struct Email **ep = NULL;
1664 ARRAY_FOREACH(ep, ea)
1665 {
1666 struct Email *e = *ep;
1667 struct ImapEmailData *edata = imap_edata_get(e);
1668
1669 ARRAY_ADD(uida, edata->uid);
1670 }
1671 ARRAY_SORT(uida, imap_sort_uid, NULL);
1672
1673 return ARRAY_SIZE(uida);
1674}
1675
1686int imap_copy_messages(struct Mailbox *m, struct EmailArray *ea,
1687 const char *dest, enum MessageSaveOpt save_opt)
1688{
1689 if (!m || !ea || ARRAY_EMPTY(ea) || !dest)
1690 return -1;
1691
1692 char buf[PATH_MAX] = { 0 };
1693 char mbox[PATH_MAX] = { 0 };
1694 char mmbox[PATH_MAX] = { 0 };
1695 char prompt[PATH_MAX + 64];
1696 int rc;
1697 struct ConnAccount cac = { { 0 } };
1698 enum QuadOption err_continue = MUTT_NO;
1699 int triedcreate = 0;
1700 struct Email *e_cur = *ARRAY_GET(ea, 0);
1701 bool single = (ARRAY_SIZE(ea) == 1);
1703
1704 if (single && e_cur->attach_del)
1705 {
1706 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1707 return 1;
1708 }
1709
1710 if (imap_parse_path(dest, &cac, buf, sizeof(buf)))
1711 {
1712 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1713 return -1;
1714 }
1715
1716 /* check that the save-to folder is in the same account */
1717 if (!imap_account_match(&adata->conn->account, &cac))
1718 {
1719 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1720 return 1;
1721 }
1722
1723 imap_fix_path_with_delim(adata->delim, buf, mbox, sizeof(mbox));
1724 if (*mbox == '\0')
1725 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1726 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1727
1728 /* loop in case of TRYCREATE */
1729 struct Buffer *cmd = buf_pool_get();
1730 struct Buffer *sync_cmd = buf_pool_get();
1731 do
1732 {
1733 buf_reset(sync_cmd);
1734 buf_reset(cmd);
1735
1736 if (single)
1737 {
1738 mutt_message(_("Copying message %d to %s..."), e_cur->index + 1, mbox);
1739 buf_add_printf(cmd, "UID COPY %u %s", imap_edata_get(e_cur)->uid, mmbox);
1740
1741 if (e_cur->active && e_cur->changed)
1742 {
1743 rc = imap_sync_message_for_copy(m, e_cur, sync_cmd, &err_continue);
1744 if (rc < 0)
1745 {
1746 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1747 goto out;
1748 }
1749 }
1750 rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_QUEUE);
1751 if (rc != IMAP_EXEC_SUCCESS)
1752 {
1753 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1754 goto out;
1755 }
1756 }
1757 else /* copy tagged messages */
1758 {
1759 /* if any messages have attachments to delete, fall through to FETCH
1760 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1761 * remainder. */
1762 struct Email **ep = NULL;
1763 ARRAY_FOREACH(ep, ea)
1764 {
1765 struct Email *e = *ep;
1766 if (e->attach_del)
1767 {
1768 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1769 rc = 1;
1770 goto out;
1771 }
1772
1773 if (e->active && e->changed)
1774 {
1775 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1776 if (rc < 0)
1777 {
1778 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1779 goto out;
1780 }
1781 }
1782 }
1783
1784 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1785 emails_to_uid_array(ea, &uida);
1786 rc = imap_exec_msg_set(adata, "UID COPY", mmbox, &uida);
1787 ARRAY_FREE(&uida);
1788
1789 if (rc == 0)
1790 {
1791 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1792 rc = -1;
1793 goto out;
1794 }
1795 else if (rc < 0)
1796 {
1797 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1798 goto out;
1799 }
1800 else
1801 {
1802 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1803 rc, mbox);
1804 }
1805 }
1806
1807 /* let's get it on */
1808 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1809 if (rc == IMAP_EXEC_ERROR)
1810 {
1811 if (triedcreate)
1812 {
1813 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1814 break;
1815 }
1816 /* bail out if command failed for reasons other than nonexistent target */
1817 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1818 break;
1819 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1820 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1821 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1822 if (c_confirm_create &&
1823 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1824 {
1826 goto out;
1827 }
1828 if (imap_create_mailbox(adata, mbox) < 0)
1829 break;
1830 triedcreate = 1;
1831 }
1832 } while (rc == IMAP_EXEC_ERROR);
1833
1834 if (rc != 0)
1835 {
1836 imap_error("imap_copy_messages", adata->buf);
1837 goto out;
1838 }
1839
1840 /* cleanup */
1841 if (save_opt == SAVE_MOVE)
1842 {
1843 struct Email **ep = NULL;
1844 ARRAY_FOREACH(ep, ea)
1845 {
1846 struct Email *e = *ep;
1847 mutt_set_flag(m, e, MUTT_DELETE, true, true);
1848 mutt_set_flag(m, e, MUTT_PURGE, true, true);
1849 }
1850 }
1851
1852 rc = 0;
1853
1854out:
1855 buf_pool_release(&cmd);
1856 buf_pool_release(&sync_cmd);
1857
1858 return (rc < 0) ? -1 : rc;
1859}
1860
1868int imap_cache_del(struct Mailbox *m, struct Email *e)
1869{
1871 struct ImapMboxData *mdata = imap_mdata_get(m);
1872
1873 if (!e || !adata || (adata->mailbox != m))
1874 return -1;
1875
1876 mdata->bcache = imap_bcache_open(m);
1877 char id[64] = { 0 };
1878 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1879 return mutt_bcache_del(mdata->bcache, id);
1880}
1881
1888{
1890 struct ImapMboxData *mdata = imap_mdata_get(m);
1891
1892 if (!adata || (adata->mailbox != m))
1893 return -1;
1894
1895 mdata->bcache = imap_bcache_open(m);
1897
1898 return 0;
1899}
1900
1919char *imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
1920{
1922 if (!adata || (adata->mailbox != m))
1923 return NULL;
1924
1925 struct ImapHeader newh = { 0 };
1926 struct ImapEmailData old_edata = { 0 };
1927 int local_changes = e->changed;
1928
1929 struct ImapEmailData *edata = e->edata;
1930 newh.edata = edata;
1931
1932 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1933 s = msg_parse_flags(&newh, s);
1934 if (!s)
1935 return NULL;
1936
1937 /* Update tags system */
1938 /* We take a copy of the tags so we can split the string */
1939 char *tags_copy = mutt_str_dup(edata->flags_remote);
1940 driver_tags_replace(&e->tags, tags_copy);
1941 FREE(&tags_copy);
1942
1943 /* YAUH (yet another ugly hack): temporarily set context to
1944 * read-write even if it's read-only, so *server* updates of
1945 * flags can be processed by mutt_set_flag. mailbox->changed must
1946 * be restored afterwards */
1947 bool readonly = m->readonly;
1948 m->readonly = false;
1949
1950 /* This is redundant with the following two checks. Removing:
1951 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old), true); */
1952 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1953 edata->old, e->old);
1954 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1955 old_edata.read, edata->read, e->read);
1956 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1957 old_edata.deleted, edata->deleted, e->deleted);
1958 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1959 old_edata.flagged, edata->flagged, e->flagged);
1960 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
1961 old_edata.replied, edata->replied, e->replied);
1962
1963 /* this message is now definitively *not* changed (mutt_set_flag
1964 * marks things changed as a side-effect) */
1965 if (local_changes == 0)
1966 e->changed = false;
1967 m->changed &= !readonly;
1968 m->readonly = readonly;
1969
1970 return s;
1971}
1972
1976bool imap_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
1977{
1978 struct Envelope *newenv = NULL;
1979 char buf[1024] = { 0 };
1980 char *pc = NULL;
1981 unsigned int bytes;
1982 unsigned int uid;
1983 bool retried = false;
1984 bool read;
1985 int rc;
1986
1987 /* Sam's weird courier server returns an OK response even when FETCH
1988 * fails. Thanks Sam. */
1989 bool fetched = false;
1990
1992
1993 if (!adata || (adata->mailbox != m))
1994 return false;
1995
1996 msg->fp = msg_cache_get(m, e);
1997 if (msg->fp)
1998 {
1999 if (imap_edata_get(e)->parsed)
2000 return true;
2001 goto parsemsg;
2002 }
2003
2004 /* This function is called in a few places after endwin()
2005 * e.g. mutt_pipe_message(). */
2006 bool output_progress = !isendwin() && m->verbose;
2007 if (output_progress)
2008 mutt_message(_("Fetching message..."));
2009
2010 msg->fp = msg_cache_put(m, e);
2011 if (!msg->fp)
2012 {
2013 struct Buffer *tempfile = buf_pool_get();
2014 buf_mktemp(tempfile);
2015 msg->fp = mutt_file_fopen(buf_string(tempfile), "w+");
2016 unlink(buf_string(tempfile));
2017 buf_pool_release(&tempfile);
2018
2019 if (!msg->fp)
2020 return false;
2021 }
2022
2023 /* mark this header as currently inactive so the command handler won't
2024 * also try to update it. HACK until all this code can be moved into the
2025 * command handler */
2026 e->active = false;
2027
2028 const bool c_imap_peek = cs_subset_bool(NeoMutt->sub, "imap_peek");
2029 snprintf(buf, sizeof(buf), "UID FETCH %u %s", imap_edata_get(e)->uid,
2030 ((adata->capabilities & IMAP_CAP_IMAP4REV1) ?
2031 (c_imap_peek ? "BODY.PEEK[]" : "BODY[]") :
2032 "RFC822"));
2033
2034 imap_cmd_start(adata, buf);
2035 do
2036 {
2037 rc = imap_cmd_step(adata);
2038 if (rc != IMAP_RES_CONTINUE)
2039 break;
2040
2041 pc = adata->buf;
2042 pc = imap_next_word(pc);
2043 pc = imap_next_word(pc);
2044
2045 if (mutt_istr_startswith(pc, "FETCH"))
2046 {
2047 while (*pc)
2048 {
2049 pc = imap_next_word(pc);
2050 if (pc[0] == '(')
2051 pc++;
2052 if (mutt_istr_startswith(pc, "UID"))
2053 {
2054 pc = imap_next_word(pc);
2055 if (!mutt_str_atoui(pc, &uid))
2056 goto bail;
2057 if (uid != imap_edata_get(e)->uid)
2058 {
2059 mutt_error(_("The message index is incorrect. Try reopening the mailbox."));
2060 }
2061 }
2062 else if (mutt_istr_startswith(pc, "RFC822") || mutt_istr_startswith(pc, "BODY[]"))
2063 {
2064 pc = imap_next_word(pc);
2065 if (imap_get_literal_count(pc, &bytes) < 0)
2066 {
2067 imap_error("imap_msg_open()", buf);
2068 goto bail;
2069 }
2070
2071 const int res = imap_read_literal(msg->fp, adata, bytes, NULL);
2072 if (res < 0)
2073 {
2074 goto bail;
2075 }
2076 /* pick up trailing line */
2077 rc = imap_cmd_step(adata);
2078 if (rc != IMAP_RES_CONTINUE)
2079 goto bail;
2080 pc = adata->buf;
2081
2082 fetched = true;
2083 }
2084 else if (!e->changed && mutt_istr_startswith(pc, "FLAGS"))
2085 {
2086 /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
2087 * change (eg from \Unseen to \Seen).
2088 * Uncommitted changes in neomutt take precedence. If we decide to
2089 * incrementally update flags later, this won't stop us syncing */
2090 pc = imap_set_flags(m, e, pc, NULL);
2091 if (!pc)
2092 goto bail;
2093 }
2094 }
2095 }
2096 } while (rc == IMAP_RES_CONTINUE);
2097
2098 /* see comment before command start. */
2099 e->active = true;
2100
2101 fflush(msg->fp);
2102 if (ferror(msg->fp))
2103 goto bail;
2104
2105 if (rc != IMAP_RES_OK)
2106 goto bail;
2107
2108 if (!fetched || !imap_code(adata->buf))
2109 goto bail;
2110
2111 if (msg_cache_commit(m, e) < 0)
2112 mutt_debug(LL_DEBUG1, "failed to add message to cache\n");
2113
2114parsemsg:
2115 /* Update the header information. Previously, we only downloaded a
2116 * portion of the headers, those required for the main display. */
2117 rewind(msg->fp);
2118 /* It may be that the Status header indicates a message is read, but the
2119 * IMAP server doesn't know the message has been \Seen. So we capture
2120 * the server's notion of 'read' and if it differs from the message info
2121 * picked up in mutt_rfc822_read_header, we mark the message (and context
2122 * changed). Another possibility: ignore Status on IMAP? */
2123 read = e->read;
2124 newenv = mutt_rfc822_read_header(msg->fp, e, false, false);
2125 mutt_env_merge(e->env, &newenv);
2126
2127 /* see above. We want the new status in e->read, so we unset it manually
2128 * and let mutt_set_flag set it correctly, updating context. */
2129 if (read != e->read)
2130 {
2131 e->read = read;
2132 mutt_set_flag(m, e, MUTT_NEW, read, true);
2133 }
2134
2135 e->lines = 0;
2136 while (fgets(buf, sizeof(buf), msg->fp) && !feof(msg->fp))
2137 {
2138 e->lines++;
2139 }
2140
2141 e->body->length = ftell(msg->fp) - e->body->offset;
2142
2144 rewind(msg->fp);
2145 imap_edata_get(e)->parsed = true;
2146
2147 /* retry message parse if cached message is empty */
2148 if (!retried && ((e->lines == 0) || (e->body->length == 0)))
2149 {
2150 imap_cache_del(m, e);
2151 retried = true;
2152 goto parsemsg;
2153 }
2154
2155 return true;
2156
2157bail:
2158 e->active = true;
2159 mutt_file_fclose(&msg->fp);
2160 imap_cache_del(m, e);
2161 return false;
2162}
2163
2169int imap_msg_commit(struct Mailbox *m, struct Message *msg)
2170{
2171 int rc = mutt_file_fclose(&msg->fp);
2172 if (rc != 0)
2173 return rc;
2174
2175 return imap_append_message(m, msg);
2176}
2177
2183int imap_msg_close(struct Mailbox *m, struct Message *msg)
2184{
2185 return mutt_file_fclose(&msg->fp);
2186}
2187
2191int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
2192{
2193 int rc = 0;
2194#ifdef USE_HCACHE
2195 bool close_hc = true;
2197 struct ImapMboxData *mdata = imap_mdata_get(m);
2198 if (!mdata || !adata)
2199 return -1;
2200 if (mdata->hcache)
2201 close_hc = false;
2202 else
2203 imap_hcache_open(adata, mdata, true);
2204 rc = imap_hcache_put(mdata, e);
2205 if (close_hc)
2207#endif
2208 return rc;
2209}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:335
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:156
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:214
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:109
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
const char * mutt_str_atol(const char *str, long *dst)
Convert ASCII string to a long.
Definition: atoi.c:143
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:218
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:252
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition: bcache.c:146
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition: bcache.c:334
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition: bcache.c:185
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition: bcache.c:269
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition: bcache.c:212
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:204
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
void buf_join_str(struct Buffer *buf, const char *str, char sep)
Join a buffer with a string separated by sep.
Definition: buffer.c:748
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
long cs_subset_long(const struct ConfigSubset *sub, const char *name)
Get a long config item by name.
Definition: helpers.c:95
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
Convenience wrapper for the config headers.
Connection Library.
Convenience wrapper for the core headers.
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:249
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:223
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition: ctype.c:95
bool mutt_isdigit(int arg)
Wrapper for isdigit(3)
Definition: ctype.c:65
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: get.c:58
struct Email * email_new(void)
Create a new Email.
Definition: email.c:77
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:46
Structs that make up an email.
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1204
void mutt_env_merge(struct Envelope *base, struct Envelope **extra)
Merge the headers of two Envelopes.
Definition: envelope.c:193
Manage where the email is piped to external commands.
MessageSaveOpt
Message save option.
Definition: external.h:51
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: external.h:53
#define mutt_file_fclose(FP)
Definition: file.h:139
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:138
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:57
static int imap_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
Delete an entry from the message cache - Implements bcache_list_t -.
Definition: message.c:168
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition: edata.c:40
#define mutt_error(...)
Definition: logging2.h:93
#define mutt_message(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
#define mutt_perror(...)
Definition: logging2.h:94
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: message.c:2183
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: message.c:2169
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:1976
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:2191
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition: msg_set.c:54
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:111
int hcache_delete_raw(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:752
int 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:724
Header cache multiplexor.
#define hcache_fetch_raw_obj(hc, key, keylen, dst)
Definition: lib.h:162
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:123
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1113
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1127
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1254
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:1302
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition: edata.c:57
struct ImapEmailData * imap_edata_clone(struct ImapEmailData *src)
Clone an ImapEmailData.
Definition: edata.c:79
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:67
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:476
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:61
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1887
static struct BodyCache * imap_bcache_open(struct Mailbox *m)
Open a message cache.
Definition: message.c:80
static FILE * msg_cache_put(struct Mailbox *m, struct Email *e)
Put an email into the message cache.
Definition: message.c:128
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:1919
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:550
static int msg_parse_fetch(struct ImapHeader *h, char *s)
Handle headers returned from header fetch.
Definition: message.c:308
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition: message.c:1012
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition: message.c:486
int imap_append_message(struct Mailbox *m, struct Message *msg)
Write an email back to the server.
Definition: message.c:1528
static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
Extract IMAP UIDs from Emails.
Definition: message.c:1661
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:420
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:913
static FILE * msg_cache_get(struct Mailbox *m, struct Email *e)
Get the message cache entry for an email.
Definition: message.c:107
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:1686
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition: message.c:502
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1868
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:642
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:1089
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:1339
static int msg_cache_commit(struct Mailbox *m, struct Email *e)
Add to the message cache.
Definition: message.c:149
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:682
static char * msg_parse_flags(struct ImapHeader *h, char *s)
Read a FLAGS token into an ImapHeader.
Definition: message.c:191
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, char *uid_seqset)
Retrieve data from the header cache.
Definition: message.c:827
static void imap_alloc_uid_hash(struct ImapAccountData *adata, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition: message.c:526
#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
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool create)
Open a header cache.
Definition: util.c:301
int imap_get_literal_count(const char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition: util.c:779
#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:417
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:382
#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:1124
#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:452
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:357
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1145
@ 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:1204
#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:748
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:659
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:342
char * imap_fix_path_with_delim(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:712
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1092
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:959
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:823
#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:806
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:851
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition: imap.c:671
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:435
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:928
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:592
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:46
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
#define FREE(x)
Definition: memory.h:62
#define MAX(a, b)
Definition: memory.h:36
int imap_exec_msg_set(struct ImapAccountData *adata, const char *pre, const char *post, struct UidArray *uida)
Execute a command using a set of UIDs.
Definition: msg_set.c:132
IMAP Message Sets.
void imap_msn_free(struct MSNArray *msn)
Free the cache.
Definition: msn.c:62
struct Email * imap_msn_get(const struct MSNArray *msn, int idx)
Return the Email associated with an msn.
Definition: msn.c:83
size_t imap_msn_highest(const struct MSNArray *msn)
Return the highest MSN in use.
Definition: msn.c:72
void imap_msn_set(struct MSNArray *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:95
void imap_msn_reserve(struct MSNArray *msn, size_t num)
Create / reallocate the cache.
Definition: msn.c:44
IMAP MSN helper functions.
int mutt_date_make_imap(struct Buffer *buf, time_t timestamp)
Format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:811
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition: date.c:854
Convenience wrapper for the library headers.
#define FALLTHROUGH
Definition: lib.h:113
Message logging.
#define _(a)
Definition: message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:802
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:580
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:243
Many unsorted constants and some structs.
MessageType
To set flags or match patterns.
Definition: mutt.h:67
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:73
@ MUTT_OLD
Old messages.
Definition: mutt.h:71
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:77
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:79
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:75
@ MUTT_NEW
New messages.
Definition: mutt.h:70
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:72
#define PATH_MAX
Definition: mutt.h:42
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition: mx.c:1211
API for mailboxes.
Notmuch-specific Mailbox data.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
Pop-specific Account data.
Pop-specific Email data.
Progress Bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition: lib.h:82
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:83
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:80
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 query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition: question.c:354
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:326
#define STAILQ_INIT(head)
Definition: queue.h:410
GUI display the mailboxes in a side panel.
#define ASSERT(COND)
Definition: signal2.h:60
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:69
#define mutt_socket_write_n(conn, buf, len)
Definition: socket.h:59
#define mutt_socket_send(conn, buf)
Definition: socket.h:57
Key value store.
#define SKIPWS(ch)
Definition: string2.h:44
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
Local cache of email bodies.
Definition: bcache.c:49
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:36
char * data
Pointer to data.
Definition: buffer.h:37
Login details for a remote server.
Definition: connaccount.h:53
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
The envelope/body of an email.
Definition: email.h:39
bool read
Email is read.
Definition: email.h:50
struct Envelope * env
Envelope information.
Definition: email.h:68
void * edata
Driver-specific data.
Definition: email.h:74
int lines
How many lines in the body of this message?
Definition: email.h:62
struct Body * body
List of MIME parts.
Definition: email.h:69
bool active
Message is not to be removed.
Definition: email.h:76
bool old
Email is seen, but unread.
Definition: email.h:49
void(* edata_free)(void **ptr)
Definition: email.h:90
bool changed
Email has been edited.
Definition: email.h:77
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:99
bool flagged
Marked important?
Definition: email.h:47
bool replied
Email has been replied to.
Definition: email.h:51
struct TagList tags
For drivers that support server tagging.
Definition: email.h:72
bool deleted
Email is deleted.
Definition: email.h:78
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:61
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:35
bool parsed
Definition: edata.h:43
unsigned int uid
32-bit Message UID
Definition: edata.h:45
unsigned int msn
Message Sequence Number.
Definition: edata.h:46
char * flags_remote
Definition: edata.h:49
bool deleted
Email has been deleted.
Definition: edata.h:39
bool old
Email has been seen.
Definition: edata.h:38
bool read
Email has been read.
Definition: edata.h:37
bool flagged
Email has been flagged.
Definition: edata.h:40
bool replied
Email has been replied to.
Definition: edata.h:41
char * flags_system
Definition: edata.h:48
IMAP-specific header.
Definition: message.h:34
time_t received
Definition: message.h:37
struct ImapEmailData * edata
Definition: message.h:35
long content_length
Definition: message.h:38
IMAP-specific Mailbox data -.
Definition: mdata.h:40
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:45
unsigned int uid_next
Definition: mdata.h:52
struct HeaderCache * hcache
Email header cache.
Definition: mdata.h:63
struct BodyCache * bcache
Email body cache.
Definition: mdata.h:61
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:47
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition: mdata.h:59
unsigned long long modseq
Definition: mdata.h:53
uint32_t uidvalidity
Definition: mdata.h:51
char * name
Mailbox name.
Definition: mdata.h:41
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
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:116
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:94
bool verbose
Display status messages?
Definition: mailbox.h:117
int msg_unread
Number of unread messages.
Definition: mailbox.h:89
A local copy of an email.
Definition: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
char * path
path to temp file
Definition: message.h:36
bool draft
Message has been read.
Definition: message.h:44
bool replied
Message has been replied to.
Definition: message.h:43
time_t received
Time at which this message was received.
Definition: message.h:46
bool flagged
Message is flagged.
Definition: message.h:42
bool read
Message has been read.
Definition: message.h:41
struct Message::@0 flags
Flags for the Message.
Container for Accounts, Notifications.
Definition: neomutt.h:43
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
UID Sequence Set Iterator.
Definition: private.h:169
bool driver_tags_replace(struct TagList *tl, const char *tags)
Replace all tags.
Definition: tags.c:201
#define buf_mktemp(buf)
Definition: tmp.h:33