NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
message.c
Go to the documentation of this file.
1
30
36
37#include "config.h"
38#include <limits.h>
39#include <stdbool.h>
40#include <stdint.h>
41#include <stdio.h>
42#include <string.h>
43#include <unistd.h>
44#include "private.h"
45#include "mutt/lib.h"
46#include "config/lib.h"
47#include "email/lib.h"
48#include "core/lib.h"
49#include "conn/lib.h"
50#include "gui/lib.h"
51#include "mutt.h"
52#include "message.h"
53#include "lib.h"
54#include "bcache/lib.h"
55#include "key/lib.h"
56#include "progress/lib.h"
57#include "question/lib.h"
58#include "adata.h"
59#include "edata.h"
60#include "external.h"
61#include "mdata.h"
62#include "msg_set.h"
63#include "msn.h"
64#include "mutt_logging.h"
65#include "mx.h"
66#ifdef ENABLE_NLS
67#include <libintl.h>
68#endif
69#ifdef USE_HCACHE
70#include "hcache/lib.h"
71#endif
72
73struct BodyCache;
74
81static struct BodyCache *imap_bcache_open(struct Mailbox *m)
82{
85
86 if (!adata || (adata->mailbox != m) || !mdata)
87 return NULL;
88
89 if (mdata->bcache)
90 return mdata->bcache;
91
92 struct Buffer *mailbox = buf_pool_get();
93 imap_cachepath(adata->delim, mdata->name, mailbox);
94
95 struct BodyCache *bc = mutt_bcache_open(&adata->conn->account, buf_string(mailbox));
96 buf_pool_release(&mailbox);
97
98 return bc;
99}
100
108static FILE *msg_cache_get(struct Mailbox *m, struct Email *e)
109{
111 struct ImapMboxData *mdata = imap_mdata_get(m);
112
113 if (!e || !adata || (adata->mailbox != m) || !mdata)
114 return NULL;
115
116 mdata->bcache = imap_bcache_open(m);
117 char id[64] = { 0 };
118 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
119 return mutt_bcache_get(mdata->bcache, id);
120}
121
129static FILE *msg_cache_put(struct Mailbox *m, struct Email *e)
130{
132 struct ImapMboxData *mdata = imap_mdata_get(m);
133
134 if (!e || !adata || (adata->mailbox != m) || !mdata)
135 return NULL;
136
137 mdata->bcache = imap_bcache_open(m);
138 char id[64] = { 0 };
139 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
140 return mutt_bcache_put(mdata->bcache, id);
141}
142
150static int msg_cache_commit(struct Mailbox *m, struct Email *e)
151{
153 struct ImapMboxData *mdata = imap_mdata_get(m);
154
155 if (!e || !adata || (adata->mailbox != m) || !mdata)
156 return -1;
157
158 mdata->bcache = imap_bcache_open(m);
159 char id[64] = { 0 };
160 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
161
162 return mutt_bcache_commit(mdata->bcache, id);
163}
164
169static int imap_bcache_delete(const char *id, struct BodyCache *bcache, void *data)
170{
171 uint32_t uv = 0;
172 unsigned int uid = 0;
173 struct ImapMboxData *mdata = data;
174 if (!mdata)
175 return 0;
176
177 if (sscanf(id, "%u-%u", &uv, &uid) != 2)
178 return 0;
179
180 /* bad UID */
181 if ((uv != mdata->uidvalidity) || !mutt_hash_int_find(mdata->uid_hash, uid))
183
184 return 0;
185}
186
194static char *msg_parse_flags(struct ImapHeader *h, char *s)
195{
196 struct ImapEmailData *edata = h->edata;
197
198 /* sanity-check string */
199 size_t plen = mutt_istr_startswith(s, "FLAGS");
200 if (plen == 0)
201 {
202 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
203 return NULL;
204 }
205 s += plen;
206 SKIPWS(s);
207 if (*s != '(')
208 {
209 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
210 return NULL;
211 }
212 s++;
213
214 FREE(&edata->flags_system);
215 FREE(&edata->flags_remote);
216
217 edata->deleted = false;
218 edata->flagged = false;
219 edata->replied = false;
220 edata->read = false;
221 edata->old = false;
222
223 /* start parsing */
224 while (*s && (*s != ')'))
225 {
226 if ((plen = mutt_istr_startswith(s, "\\deleted")))
227 {
228 s += plen;
229 edata->deleted = true;
230 }
231 else if ((plen = mutt_istr_startswith(s, "\\flagged")))
232 {
233 s += plen;
234 edata->flagged = true;
235 }
236 else if ((plen = mutt_istr_startswith(s, "\\answered")))
237 {
238 s += plen;
239 edata->replied = true;
240 }
241 else if ((plen = mutt_istr_startswith(s, "\\seen")))
242 {
243 s += plen;
244 edata->read = true;
245 }
246 else if ((plen = mutt_istr_startswith(s, "\\recent")))
247 {
248 s += plen;
249 }
250 else if ((plen = mutt_istr_startswith(s, "old")))
251 {
252 s += plen;
253 edata->old = cs_subset_bool(NeoMutt->sub, "mark_old");
254 }
255 else
256 {
257 char ctmp;
258 char *flag_word = s;
259 bool is_system_keyword = mutt_istr_startswith(s, "\\");
260
261 while (*s && !mutt_isspace(*s) && (*s != ')'))
262 s++;
263
264 ctmp = *s;
265 *s = '\0';
266
267 struct Buffer *buf = buf_pool_get();
268 if (is_system_keyword)
269 {
270 /* store other system flags as well (mainly \\Draft) */
271 buf_addstr(buf, edata->flags_system);
272 buf_join_str(buf, flag_word, ' ');
273 FREE(&edata->flags_system);
274 edata->flags_system = buf_strdup(buf);
275 }
276 else
277 {
278 /* store custom flags as well */
279 buf_addstr(buf, edata->flags_remote);
280 buf_join_str(buf, flag_word, ' ');
281 FREE(&edata->flags_remote);
282 edata->flags_remote = buf_strdup(buf);
283 }
284 buf_pool_release(&buf);
285
286 *s = ctmp;
287 }
288 SKIPWS(s);
289 }
290
291 /* wrap up, or note bad flags response */
292 if (*s == ')')
293 {
294 s++;
295 }
296 else
297 {
298 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
299 return NULL;
300 }
301
302 return s;
303}
304
313static int msg_parse_fetch(struct ImapHeader *h, char *s)
314{
315 if (!s)
316 return -1;
317
318 char tmp[128] = { 0 };
319 char *ptmp = NULL;
320 size_t plen = 0;
321
322 while (*s)
323 {
324 SKIPWS(s);
325
326 if (mutt_istr_startswith(s, "FLAGS"))
327 {
328 s = msg_parse_flags(h, s);
329 if (!s)
330 return -1;
331 }
332 else if ((plen = mutt_istr_startswith(s, "UID")))
333 {
334 s += plen;
335 SKIPWS(s);
336 if (!mutt_str_atoui(s, &h->edata->uid))
337 return -1;
338
339 s = imap_next_word(s);
340 }
341 else if ((plen = mutt_istr_startswith(s, "INTERNALDATE")))
342 {
343 s += plen;
344 SKIPWS(s);
345 if (*s != '\"')
346 {
347 mutt_debug(LL_DEBUG1, "bogus INTERNALDATE entry: %s\n", s);
348 return -1;
349 }
350 s++;
351 ptmp = tmp;
352 while (*s && (*s != '\"') && (ptmp != (tmp + sizeof(tmp) - 1)))
353 *ptmp++ = *s++;
354 if (*s != '\"')
355 return -1;
356 s++; /* skip past the trailing " */
357 *ptmp = '\0';
359 }
360 else if ((plen = mutt_istr_startswith(s, "RFC822.SIZE")))
361 {
362 s += plen;
363 SKIPWS(s);
364 ptmp = tmp;
365 while (mutt_isdigit(*s) && (ptmp != (tmp + sizeof(tmp) - 1)))
366 *ptmp++ = *s++;
367 *ptmp = '\0';
368 if (!mutt_str_atol(tmp, &h->content_length))
369 return -1;
370 }
371 else if (mutt_istr_startswith(s, "BODY") || mutt_istr_startswith(s, "RFC822.HEADER"))
372 {
373 /* handle above, in msg_fetch_header */
374 return -2;
375 }
376 else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
377 {
378 s += plen;
379 SKIPWS(s);
380 if (*s != '(')
381 {
382 mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
383 return -1;
384 }
385 s++;
386 while (*s && (*s != ')'))
387 s++;
388 if (*s == ')')
389 {
390 s++;
391 }
392 else
393 {
394 mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
395 return -1;
396 }
397 }
398 else if (*s == ')')
399 {
400 s++; /* end of request */
401 }
402 else if (*s)
403 {
404 /* got something i don't understand */
405 imap_error("msg_parse_fetch", s);
406 return -1;
407 }
408 }
409
410 return 0;
411}
412
425static int msg_fetch_header(struct Mailbox *m, struct ImapHeader *ih, char *buf, FILE *fp)
426{
427 int rc = -1; /* default now is that string isn't FETCH response */
428
430 if (!adata)
431 return rc;
432
433 if (buf[0] != '*')
434 return rc;
435
436 /* skip to message number */
438 if (!mutt_str_atoui(buf, &ih->edata->msn))
439 return rc;
440
441 /* find FETCH tag */
443 if (!mutt_istr_startswith(buf, "FETCH"))
444 return rc;
445
446 rc = -2; /* we've got a FETCH response, for better or worse */
447 buf = strchr(buf, '(');
448 if (!buf)
449 return rc;
450 buf++;
451
452 /* FIXME: current implementation - call msg_parse_fetch - if it returns -2,
453 * read header lines and call it again. Silly. */
454 int parse_rc = msg_parse_fetch(ih, buf);
455 if (parse_rc == 0)
456 return 0;
457 if ((parse_rc != -2) || !fp)
458 return rc;
459
460 unsigned int bytes = 0;
461 if (imap_get_literal_count(buf, &bytes) == 0)
462 {
463 imap_read_literal(fp, adata, bytes, NULL);
464
465 /* we may have other fields of the FETCH _after_ the literal
466 * (eg Domino puts FLAGS here). Nothing wrong with that, either.
467 * This all has to go - we should accept literals and nonliterals
468 * interchangeably at any time. */
470 return rc;
471
472 if (msg_parse_fetch(ih, adata->buf) == -1)
473 return rc;
474 }
475
476 rc = 0; /* success */
477
478 /* subtract headers from message size - unfortunately only the subset of
479 * headers we've requested. */
480 ih->content_length -= bytes;
481
482 return rc;
483}
484
493static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
494{
495 buf[*len] = '\0';
496 int rc = mutt_socket_write_n(conn, buf, *len);
497 *len = 0;
498 return rc;
499}
500
510{
511 bool abort = false;
512
514 /* L10N: This prompt is made if the user hits Ctrl-C when opening an IMAP mailbox */
515 if (query_yesorno(_("Abort download and close mailbox?"), MUTT_YES) == MUTT_YES)
516 {
517 abort = true;
519 }
520 SigInt = false;
521
522 return abort;
523}
524
535 struct Mailbox *m, unsigned int msn_count)
536{
537 struct ImapMboxData *mdata = imap_mdata_get(m);
538 if (mdata && !mdata->uid_hash)
539 mdata->uid_hash = mutt_hash_int_new(MAX(6 * msn_count / 5, 30), MUTT_HASH_NONE);
540}
541
560static unsigned int imap_fetch_msn_seqset(struct Buffer *buf, struct ImapAccountData *adata,
561 struct Mailbox *m, bool evalhc,
562 unsigned int msn_begin, unsigned int msn_end,
563 unsigned int *fetch_msn_end)
564{
565 buf_reset(buf);
566 if (msn_end < msn_begin)
567 return 0;
568
569 struct ImapMboxData *mdata = imap_mdata_get(m);
570 if (!mdata)
571 return 0;
572
573 unsigned int max_headers_per_fetch = UINT_MAX;
574 bool first_chunk = true;
575 int state = 0; /* 1: single msn, 2: range of msn */
576 unsigned int msn;
577 unsigned int range_begin = 0;
578 unsigned int range_end = 0;
579 unsigned int msn_count = 0;
580
581 const long c_imap_fetch_chunk_size = cs_subset_long(NeoMutt->sub, "imap_fetch_chunk_size");
582 if (c_imap_fetch_chunk_size > 0)
583 max_headers_per_fetch = c_imap_fetch_chunk_size;
584
585 if (!evalhc)
586 {
587 if ((msn_end - msn_begin + 1) <= max_headers_per_fetch)
588 *fetch_msn_end = msn_end;
589 else
590 *fetch_msn_end = msn_begin + max_headers_per_fetch - 1;
591 buf_printf(buf, "%u:%u", msn_begin, *fetch_msn_end);
592 return (*fetch_msn_end - msn_begin + 1);
593 }
594
595 for (msn = msn_begin; msn <= (msn_end + 1); msn++)
596 {
597 if ((msn_count < max_headers_per_fetch) && (msn <= msn_end) &&
598 !imap_msn_get(&mdata->msn, msn - 1))
599 {
600 msn_count++;
601
602 switch (state)
603 {
604 case 1: /* single: convert to a range */
605 state = 2;
607
608 case 2: /* extend range ending */
609 range_end = msn;
610 break;
611 default:
612 state = 1;
613 range_begin = msn;
614 break;
615 }
616 }
617 else if (state)
618 {
619 if (first_chunk)
620 first_chunk = false;
621 else
622 buf_addch(buf, ',');
623
624 if (state == 1)
625 buf_add_printf(buf, "%u", range_begin);
626 else if (state == 2)
627 buf_add_printf(buf, "%u:%u", range_begin, range_end);
628 state = 0;
629
630 if ((buf_len(buf) > 500) || (msn_count >= max_headers_per_fetch))
631 break;
632 }
633 }
634
635 /* The loop index goes one past to terminate the range if needed. */
636 *fetch_msn_end = msn - 1;
637
638 return msn_count;
639}
640
656static void set_changed_flag(struct Mailbox *m, struct Email *e, int local_changes,
657 bool *server_changes, enum MessageType flag_name,
658 bool old_hd_flag, bool new_hd_flag, bool h_flag)
659{
660 /* If there are local_changes, we only want to note if the server
661 * flags have changed, so we can set a reopen flag in
662 * cmd_parse_fetch(). We don't want to count a local modification
663 * to the header flag as a "change". */
664 if ((old_hd_flag == new_hd_flag) && (local_changes != 0))
665 return;
666
667 if (new_hd_flag == h_flag)
668 return;
669
670 if (server_changes)
671 *server_changes = true;
672
673 /* Local changes have priority */
674 if (local_changes == 0)
675 mutt_set_flag(m, e, flag_name, new_hd_flag, true);
676}
677
678#ifdef USE_HCACHE
698 struct Mailbox *m, unsigned int msn_end,
699 unsigned int uid_next,
700 bool store_flag_updates, bool eval_condstore)
701{
702 struct Progress *progress = NULL;
703 char buf[1024] = { 0 };
704 int rc = -1;
705
706 struct ImapMboxData *mdata = imap_mdata_get(m);
707 if (!mdata)
708 return -1;
709
710 int idx = m->msg_count;
711
712 if (m->verbose)
713 {
714 /* L10N: Comparing the cached data with the IMAP server's data */
716 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
717 progress_set_message(progress, _("Evaluating cache..."));
718 }
719
720 /* If we are using CONDSTORE's "FETCH CHANGEDSINCE", then we keep
721 * the flags in the header cache, and update them further below.
722 * Otherwise, we fetch the current state of the flags here. */
723 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (UID%s)", uid_next - 1,
724 eval_condstore ? "" : " FLAGS");
725
726 imap_cmd_start(adata, buf);
727
729 int mfhrc = 0;
730 struct ImapHeader h = { 0 };
731 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
732 {
734 goto fail;
735
736 progress_update(progress, msgno, -1);
737
738 memset(&h, 0, sizeof(h));
739 h.edata = imap_edata_new();
740 do
741 {
742 rc = imap_cmd_step(adata);
743 if (rc != IMAP_RES_CONTINUE)
744 break;
745
746 mfhrc = msg_fetch_header(m, &h, adata->buf, NULL);
747 if (mfhrc < 0)
748 continue;
749
750 if (!h.edata->uid)
751 {
752 mutt_debug(LL_DEBUG2, "skipping hcache FETCH response for message number %d missing a UID\n",
753 h.edata->msn);
754 continue;
755 }
756
757 if ((h.edata->msn < 1) || (h.edata->msn > msn_end))
758 {
759 mutt_debug(LL_DEBUG1, "skipping hcache FETCH response for unknown message number %d\n",
760 h.edata->msn);
761 continue;
762 }
763
764 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
765 {
766 mutt_debug(LL_DEBUG2, "skipping hcache FETCH for duplicate message %d\n",
767 h.edata->msn);
768 continue;
769 }
770
771 struct Email *e = imap_hcache_get(mdata, h.edata->uid);
772 m->emails[idx] = e;
773 if (e)
774 {
775 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
776 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
777
778 e->index = h.edata->uid;
779 /* messages which have not been expunged are ACTIVE (borrowed from mh
780 * folders) */
781 e->active = true;
782 e->changed = false;
783 if (eval_condstore)
784 {
785 h.edata->read = e->read;
786 h.edata->old = e->old;
787 h.edata->deleted = e->deleted;
788 h.edata->flagged = e->flagged;
789 h.edata->replied = e->replied;
790 }
791 else
792 {
793 e->read = h.edata->read;
794 e->old = h.edata->old;
795 e->deleted = h.edata->deleted;
796 e->flagged = h.edata->flagged;
797 e->replied = h.edata->replied;
798 }
799
800 /* mailbox->emails[msgno]->received is restored from hcache_fetch_email() */
801 e->edata = h.edata;
803
804 /* We take a copy of the tags so we can split the string */
805 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
806 driver_tags_replace(&e->tags, tags_copy);
807 FREE(&tags_copy);
808
809 m->msg_count++;
810 mailbox_size_add(m, e);
811
812 /* If this is the first time we are fetching, we need to
813 * store the current state of flags back into the header cache */
814 if (!eval_condstore && store_flag_updates)
815 imap_hcache_put(mdata, e);
816
817 h.edata = NULL;
818 idx++;
819 }
820 } while (mfhrc == -1);
821
822 imap_edata_free((void **) &h.edata);
823
824 if ((mfhrc < -1) || ((rc != IMAP_RES_CONTINUE) && (rc != IMAP_RES_OK)))
825 goto fail;
826 }
827
828 rc = 0;
829fail:
830 progress_free(&progress);
831 return rc;
832}
833
848 struct Mailbox *m, char *uid_seqset)
849{
850 int rc;
851 unsigned int uid = 0;
852
853 mutt_debug(LL_DEBUG2, "Reading uid seqset from header cache\n");
854 unsigned int msn = 1;
855 struct ImapMboxData *mdata = imap_mdata_get(m);
856 if (!mdata)
857 return -1;
858
859 if (m->verbose)
860 mutt_message(_("Evaluating cache..."));
861
862 struct SeqsetIterator *iter = mutt_seqset_iterator_new(uid_seqset);
863 if (!iter)
864 return -1;
865
866 while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
867 {
868 /* The seqset may contain more headers than the fetch request, so
869 * we need to watch and reallocate the context and msn_index */
870 imap_msn_reserve(&mdata->msn, msn);
871
872 struct Email *e = imap_hcache_get(mdata, uid);
873 if (e)
874 {
875 imap_msn_set(&mdata->msn, msn - 1, e);
876
878
880 e->edata = edata;
882
883 e->index = uid;
884 e->active = true;
885 e->changed = false;
886 edata->read = e->read;
887 edata->old = e->old;
888 edata->deleted = e->deleted;
889 edata->flagged = e->flagged;
890 edata->replied = e->replied;
891
892 edata->msn = msn;
893 edata->uid = uid;
895
896 mailbox_size_add(m, e);
897 m->emails[m->msg_count++] = e;
898
899 msn++;
900 }
901 else if (!uid)
902 {
903 /* A non-zero uid missing from the header cache is either the
904 * result of an expunged message (not recorded in the uid seqset)
905 * or a hole in the header cache.
906 *
907 * We have to assume it's an earlier expunge and compact the msn's
908 * in that case, because cmd_parse_vanished() won't find it in the
909 * uid_hash and decrement later msn's there.
910 *
911 * Thus we only increment the uid if the uid was 0: an actual
912 * stored "blank" in the uid seqset.
913 */
914 msn++;
915 }
916 }
917
919
920 return rc;
921}
922
937 struct Mailbox *m, unsigned int msn_end,
938 unsigned int uid_next,
939 unsigned long long hc_modseq, bool eval_qresync)
940{
941 struct Progress *progress = NULL;
942 char buf[1024] = { 0 };
943 unsigned int header_msn = 0;
944
945 struct ImapMboxData *mdata = imap_mdata_get(m);
946 if (!mdata)
947 return -1;
948
949 if (m->verbose)
950 {
951 /* L10N: Fetching IMAP flag changes, using the CONDSTORE extension */
953 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
954 progress_set_message(progress, _("Fetching flag updates..."));
955 }
956
957 snprintf(buf, sizeof(buf), "UID FETCH 1:%u (FLAGS) (CHANGEDSINCE %llu%s)",
958 uid_next - 1, hc_modseq, eval_qresync ? " VANISHED" : "");
959
960 imap_cmd_start(adata, buf);
961
962 int rc = IMAP_RES_CONTINUE;
963 for (int msgno = 1; rc == IMAP_RES_CONTINUE; msgno++)
964 {
966 goto fail;
967
968 progress_update(progress, msgno, -1);
969
970 /* cmd_parse_fetch will update the flags */
971 rc = imap_cmd_step(adata);
972 if (rc != IMAP_RES_CONTINUE)
973 break;
974
975 /* so we just need to grab the header and persist it back into
976 * the header cache */
977 char *fetch_buf = adata->buf;
978 if (fetch_buf[0] != '*')
979 continue;
980
981 fetch_buf = imap_next_word(fetch_buf);
982 if (!mutt_isdigit(*fetch_buf) || !mutt_str_atoui(fetch_buf, &header_msn))
983 continue;
984
985 if ((header_msn < 1) || (header_msn > msn_end) ||
986 !imap_msn_get(&mdata->msn, header_msn - 1))
987 {
988 mutt_debug(LL_DEBUG1, "skipping CONDSTORE flag update for unknown message number %u\n",
989 header_msn);
990 continue;
991 }
992
993 imap_hcache_put(mdata, imap_msn_get(&mdata->msn, header_msn - 1));
994 }
995
996 if (rc != IMAP_RES_OK)
997 goto fail;
998
999 /* The IMAP flag setting as part of cmd_parse_fetch() ends up
1000 * flipping these on. */
1001 mdata->check_status &= ~IMAP_FLAGS_PENDING;
1002 m->changed = false;
1003
1004 /* VANISHED handling: we need to empty out the messages */
1005 if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1006 {
1008 imap_expunge_mailbox(m, false);
1009
1010 imap_hcache_open(adata, mdata, false);
1011 mdata->reopen &= ~IMAP_EXPUNGE_PENDING;
1012 }
1013
1014 /* undo expunge count updates.
1015 * mview_update() will do this at the end of the header fetch. */
1016 m->vcount = 0;
1017 m->msg_tagged = 0;
1018 m->msg_deleted = 0;
1019 m->msg_new = 0;
1020 m->msg_unread = 0;
1021 m->msg_flagged = 0;
1022 m->changed = false;
1023
1024 rc = 0;
1025fail:
1026 progress_free(&progress);
1027 return rc;
1028}
1029
1038static int imap_verify_qresync(struct Mailbox *m)
1039{
1040 ASSERT(m);
1042 struct ImapMboxData *mdata = imap_mdata_get(m);
1043 if (!adata || (adata->mailbox != m) || !mdata)
1044 return -1;
1045
1046 const size_t max_msn = imap_msn_highest(&mdata->msn);
1047
1048 unsigned int msn;
1049 unsigned int uid;
1050 struct Email *e = NULL;
1051 struct Email *uidh = NULL;
1052
1053 for (int i = 0; i < m->msg_count; i++)
1054 {
1055 e = m->emails[i];
1056 const struct ImapEmailData *edata = imap_edata_get(e);
1057 if (!edata)
1058 goto fail;
1059
1060 msn = imap_edata_get(e)->msn;
1061 uid = imap_edata_get(e)->uid;
1062
1063 if ((msn < 1) || (msn > max_msn) || imap_msn_get(&mdata->msn, msn - 1) != e)
1064 goto fail;
1065
1066 uidh = (struct Email *) mutt_hash_int_find(mdata->uid_hash, uid);
1067 if (uidh != e)
1068 goto fail;
1069 }
1070
1071 return 0;
1072
1073fail:
1074 imap_msn_free(&mdata->msn);
1075 mutt_hash_free(&mdata->uid_hash);
1079
1080 for (int i = 0; i < m->msg_count; i++)
1081 {
1082 if (m->emails[i] && m->emails[i]->edata)
1083 imap_edata_free(&m->emails[i]->edata);
1084 email_free(&m->emails[i]);
1085 }
1086 m->msg_count = 0;
1087 m->size = 0;
1088 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1090 imap_hcache_close(mdata);
1091
1092 if (m->verbose)
1093 {
1094 /* L10N: After opening an IMAP mailbox using QRESYNC, Mutt performs a quick
1095 sanity check. If that fails, Mutt reopens the mailbox using a normal
1096 download. */
1097 mutt_error(_("QRESYNC failed. Reopening mailbox."));
1098 }
1099 return -1;
1100}
1101
1102#endif /* USE_HCACHE */
1103
1115static int read_headers_fetch_new(struct Mailbox *m, unsigned int msn_begin,
1116 unsigned int msn_end, bool evalhc,
1117 unsigned int *maxuid, bool initial_download)
1118{
1119 int rc = -1;
1120 unsigned int fetch_msn_end = 0;
1121 struct Progress *progress = NULL;
1122 char *hdrreq = NULL;
1123 struct Buffer *tempfile = NULL;
1124 FILE *fp = NULL;
1125 struct ImapHeader h = { 0 };
1126 struct Buffer *buf = NULL;
1127 static const char *const want_headers = "DATE FROM SENDER SUBJECT TO CC MESSAGE-ID REFERENCES "
1128 "CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO "
1129 "LINES LIST-POST LIST-SUBSCRIBE LIST-UNSUBSCRIBE X-LABEL "
1130 "X-ORIGINAL-TO";
1131
1133 struct ImapMboxData *mdata = imap_mdata_get(m);
1134 struct ImapEmailData *edata = NULL;
1135
1136 if (!adata || (adata->mailbox != m) || !mdata)
1137 return -1;
1138
1139 struct Buffer *hdr_list = buf_pool_get();
1140 buf_strcpy(hdr_list, want_headers);
1141 const char *const c_imap_headers = cs_subset_string(NeoMutt->sub, "imap_headers");
1142 if (c_imap_headers)
1143 {
1144 buf_addch(hdr_list, ' ');
1145 buf_addstr(hdr_list, c_imap_headers);
1146 }
1147#ifdef USE_AUTOCRYPT
1148 const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
1149 if (c_autocrypt)
1150 {
1151 buf_addch(hdr_list, ' ');
1152 buf_addstr(hdr_list, "AUTOCRYPT");
1153 }
1154#endif
1155
1156 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1157 {
1158 mutt_str_asprintf(&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s)]", buf_string(hdr_list));
1159 }
1160 else if (adata->capabilities & IMAP_CAP_IMAP4)
1161 {
1162 mutt_str_asprintf(&hdrreq, "RFC822.HEADER.LINES (%s)", buf_string(hdr_list));
1163 }
1164 else
1165 { /* Unable to fetch headers for lower versions */
1166 mutt_error(_("Unable to fetch headers from this IMAP server version"));
1167 goto bail;
1168 }
1169
1170 buf_pool_release(&hdr_list);
1171
1172 /* instead of downloading all headers and then parsing them, we parse them
1173 * as they come in. */
1174 tempfile = buf_pool_get();
1175 buf_mktemp(tempfile);
1176 fp = mutt_file_fopen(buf_string(tempfile), "w+");
1177 if (!fp)
1178 {
1179 mutt_error(_("Could not create temporary file %s"), buf_string(tempfile));
1180 goto bail;
1181 }
1182 unlink(buf_string(tempfile));
1183 buf_pool_release(&tempfile);
1184
1185 if (m->verbose)
1186 {
1188 progress = progress_new(MUTT_PROGRESS_READ, msn_end);
1189 progress_set_message(progress, _("Fetching message headers..."));
1190 }
1191
1192 buf = buf_pool_get();
1193
1194 /* NOTE:
1195 * The (fetch_msn_end < msn_end) used to be important to prevent
1196 * an infinite loop, in the event the server did not return all
1197 * the headers (due to a pending expunge, for example).
1198 *
1199 * I believe the new chunking imap_fetch_msn_seqset()
1200 * implementation and "msn_begin = fetch_msn_end + 1" assignment
1201 * at the end of the loop makes the comparison unneeded, but to be
1202 * cautious I'm keeping it.
1203 */
1204 edata = imap_edata_new();
1205 while ((fetch_msn_end < msn_end) &&
1206 imap_fetch_msn_seqset(buf, adata, m, evalhc, msn_begin, msn_end, &fetch_msn_end))
1207 {
1208 char *cmd = NULL;
1209 mutt_str_asprintf(&cmd, "FETCH %s (UID FLAGS INTERNALDATE RFC822.SIZE %s)",
1210 buf_string(buf), hdrreq);
1211 imap_cmd_start(adata, cmd);
1212 FREE(&cmd);
1213
1214 int msgno = msn_begin;
1215
1216 while (true)
1217 {
1218 fseek(fp, 0, SEEK_SET);
1219 clearerr(fp);
1220 memset(&h, 0, sizeof(h));
1221 h.edata = edata;
1222
1223 if (initial_download && SigInt && query_abort_header_download(adata))
1224 {
1225 goto bail;
1226 }
1227
1228 const int rc2 = imap_cmd_step(adata);
1229 if (rc2 != IMAP_RES_CONTINUE)
1230 {
1231 if (rc2 != IMAP_RES_OK)
1232 {
1233 goto bail;
1234 }
1235 break;
1236 }
1237
1238 switch (msg_fetch_header(m, &h, adata->buf, fp))
1239 {
1240 case 0:
1241 break;
1242 case -1:
1243 continue;
1244 case -2:
1245 goto bail;
1246 default:
1247 continue;
1248 }
1249
1250 if (!ftello(fp))
1251 {
1252 mutt_debug(LL_DEBUG2, "ignoring fetch response with no body\n");
1253 continue;
1254 }
1255
1256 /* make sure we don't get remnants from older larger message headers */
1257 fputs("\n\n", fp);
1258
1259 if ((h.edata->msn < 1) || (h.edata->msn > fetch_msn_end))
1260 {
1261 mutt_debug(LL_DEBUG1, "skipping FETCH response for unknown message number %d\n",
1262 h.edata->msn);
1263 continue;
1264 }
1265
1266 /* May receive FLAGS updates in a separate untagged response */
1267 if (imap_msn_get(&mdata->msn, h.edata->msn - 1))
1268 {
1269 mutt_debug(LL_DEBUG2, "skipping FETCH response for duplicate message %d\n",
1270 h.edata->msn);
1271 continue;
1272 }
1273
1274 progress_update(progress, msgno++, -1);
1275
1276 struct Email *e = email_new();
1278
1279 m->emails[m->msg_count++] = e;
1280
1281 imap_msn_set(&mdata->msn, h.edata->msn - 1, e);
1282 mutt_hash_int_insert(mdata->uid_hash, h.edata->uid, e);
1283
1284 e->index = h.edata->uid;
1285 /* messages which have not been expunged are ACTIVE (borrowed from mh
1286 * folders) */
1287 e->active = true;
1288 e->changed = false;
1289 e->read = h.edata->read;
1290 e->old = h.edata->old;
1291 e->deleted = h.edata->deleted;
1292 e->flagged = h.edata->flagged;
1293 e->replied = h.edata->replied;
1294 e->received = h.received;
1295 e->edata = (void *) imap_edata_clone(h.edata);
1297 STAILQ_INIT(&e->tags);
1298
1299 /* We take a copy of the tags so we can split the string */
1300 char *tags_copy = mutt_str_dup(h.edata->flags_remote);
1301 driver_tags_replace(&e->tags, tags_copy);
1302 FREE(&tags_copy);
1303
1304 if (*maxuid < h.edata->uid)
1305 *maxuid = h.edata->uid;
1306
1307 fseek(fp, 0, SEEK_SET);
1308 clearerr(fp);
1309 /* NOTE: if Date: header is missing, mutt_rfc822_read_header depends
1310 * on h.received being set */
1311 e->env = mutt_rfc822_read_header(fp, e, false, false);
1312 /* body built as a side-effect of mutt_rfc822_read_header */
1313 e->body->length = h.content_length;
1314 mailbox_size_add(m, e);
1315
1316#ifdef USE_HCACHE
1317 imap_hcache_put(mdata, e);
1318#endif /* USE_HCACHE */
1319 }
1320
1321 /* In case we get new mail while fetching the headers. */
1322 if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1323 {
1324 msn_end = mdata->new_mail_count;
1325 mx_alloc_memory(m, msn_end);
1326 imap_msn_reserve(&mdata->msn, msn_end);
1327 mdata->reopen &= ~IMAP_NEWMAIL_PENDING;
1328 mdata->new_mail_count = 0;
1329 }
1330
1331 /* Note: RFC3501 section 7.4.1 and RFC7162 section 3.2.10.2 say we
1332 * must not get any EXPUNGE/VANISHED responses in the middle of a
1333 * FETCH, nor when no command is in progress (e.g. between the
1334 * chunked FETCH commands). We previously tried to be robust by
1335 * setting:
1336 * msn_begin = mdata->max_msn + 1;
1337 * but with chunking and header cache holes this
1338 * may not be correct. So here we must assume the msn values have
1339 * not been altered during or after the fetch. */
1340 msn_begin = fetch_msn_end + 1;
1341 }
1342
1343 rc = 0;
1344
1345bail:
1346 buf_pool_release(&hdr_list);
1347 buf_pool_release(&buf);
1348 buf_pool_release(&tempfile);
1349 mutt_file_fclose(&fp);
1350 FREE(&hdrreq);
1351 imap_edata_free((void **) &edata);
1352 progress_free(&progress);
1353
1354 return rc;
1355}
1356
1370int imap_read_headers(struct Mailbox *m, unsigned int msn_begin,
1371 unsigned int msn_end, bool initial_download)
1372{
1373 unsigned int maxuid = 0;
1374 int rc = -1;
1375 bool evalhc = false;
1376
1377#ifdef USE_HCACHE
1378 uint32_t uidvalidity = 0;
1379 unsigned int uid_next = 0;
1380 unsigned long long modseq = 0;
1381 bool has_condstore = false;
1382 bool has_qresync = false;
1383 bool eval_condstore = false;
1384 bool eval_qresync = false;
1385 char *uid_seqset = NULL;
1386 const unsigned int msn_begin_save = msn_begin;
1387#endif /* USE_HCACHE */
1388
1390 struct ImapMboxData *mdata = imap_mdata_get(m);
1391 if (!adata || (adata->mailbox != m) || !mdata)
1392 return -1;
1393
1394#ifdef USE_HCACHE
1395retry:
1396#endif /* USE_HCACHE */
1397
1398 /* make sure context has room to hold the mailbox */
1399 mx_alloc_memory(m, msn_end);
1400 imap_msn_reserve(&mdata->msn, msn_end);
1401 imap_alloc_uid_hash(adata, m, msn_end);
1402
1404 mdata->new_mail_count = 0;
1405
1406#ifdef USE_HCACHE
1407 imap_hcache_open(adata, mdata, true);
1408
1409 if (mdata->hcache && initial_download)
1410 {
1411 hcache_fetch_raw_obj(mdata->hcache, "UIDVALIDITY", 11, &uidvalidity);
1412 hcache_fetch_raw_obj(mdata->hcache, "UIDNEXT", 7, &uid_next);
1413 if (mdata->modseq)
1414 {
1415 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1416 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1417 has_condstore = true;
1418
1419 /* If IMAP_CAP_QRESYNC and ImapQResync then NeoMutt sends ENABLE QRESYNC.
1420 * If we receive an ENABLED response back, then adata->qresync is set. */
1421 if (adata->qresync)
1422 has_qresync = true;
1423 }
1424
1425 if (uidvalidity && uid_next && (uidvalidity == mdata->uidvalidity))
1426 {
1427 evalhc = true;
1428 if (hcache_fetch_raw_obj(mdata->hcache, "MODSEQ", 6, &modseq))
1429 {
1430 if (has_qresync)
1431 {
1432 uid_seqset = imap_hcache_get_uid_seqset(mdata);
1433 if (uid_seqset)
1434 eval_qresync = true;
1435 }
1436
1437 if (!eval_qresync && has_condstore)
1438 eval_condstore = true;
1439 }
1440 }
1441 }
1442 if (evalhc)
1443 {
1444 if (eval_qresync)
1445 {
1446 if (read_headers_qresync_eval_cache(adata, m, uid_seqset) < 0)
1447 goto bail;
1448 }
1449 else
1450 {
1451 if (read_headers_normal_eval_cache(adata, m, msn_end, uid_next,
1452 has_condstore || has_qresync, eval_condstore) < 0)
1453 goto bail;
1454 }
1455
1456 if ((eval_condstore || eval_qresync) && (modseq != mdata->modseq))
1457 {
1458 if (read_headers_condstore_qresync_updates(adata, m, msn_end, uid_next,
1459 modseq, eval_qresync) < 0)
1460 {
1461 goto bail;
1462 }
1463 }
1464
1465 /* Look for the first empty MSN and start there */
1466 while (msn_begin <= msn_end)
1467 {
1468 if (!imap_msn_get(&mdata->msn, msn_begin - 1))
1469 break;
1470 msn_begin++;
1471 }
1472 }
1473#endif /* USE_HCACHE */
1474
1475 if (read_headers_fetch_new(m, msn_begin, msn_end, evalhc, &maxuid, initial_download) < 0)
1476 goto bail;
1477
1478#ifdef USE_HCACHE
1479 if (eval_qresync && initial_download)
1480 {
1481 if (imap_verify_qresync(m) != 0)
1482 {
1483 eval_qresync = false;
1484 eval_condstore = false;
1485 evalhc = false;
1486 modseq = 0;
1487 maxuid = 0;
1488 FREE(&uid_seqset);
1489 uidvalidity = 0;
1490 uid_next = 0;
1491 msn_begin = msn_begin_save;
1492
1493 goto retry;
1494 }
1495 }
1496#endif /* USE_HCACHE */
1497
1498 if (maxuid && (mdata->uid_next < maxuid + 1))
1499 mdata->uid_next = maxuid + 1;
1500
1501#ifdef USE_HCACHE
1502 hcache_store_raw(mdata->hcache, "UIDVALIDITY", 11, &mdata->uidvalidity,
1503 sizeof(mdata->uidvalidity));
1504 if (maxuid && (mdata->uid_next < maxuid + 1))
1505 {
1506 mutt_debug(LL_DEBUG2, "Overriding UIDNEXT: %u -> %u\n", mdata->uid_next, maxuid + 1);
1507 mdata->uid_next = maxuid + 1;
1508 }
1509 if (mdata->uid_next > 1)
1510 {
1511 hcache_store_raw(mdata->hcache, "UIDNEXT", 7, &mdata->uid_next, sizeof(mdata->uid_next));
1512 }
1513
1514 /* We currently only sync CONDSTORE and QRESYNC on the initial download.
1515 * To do it more often, we'll need to deal with flag updates combined with
1516 * unsync'ed local flag changes. We'll also need to properly sync flags to
1517 * the header cache on close. I'm not sure it's worth the added complexity. */
1518 if (initial_download)
1519 {
1520 if (has_condstore || has_qresync)
1521 {
1522 hcache_store_raw(mdata->hcache, "MODSEQ", 6, &mdata->modseq, sizeof(mdata->modseq));
1523 }
1524 else
1525 {
1526 hcache_delete_raw(mdata->hcache, "MODSEQ", 6);
1527 }
1528
1529 if (has_qresync)
1531 else
1533 }
1534#endif /* USE_HCACHE */
1535
1536 /* TODO: it's not clear to me why we are calling mx_alloc_memory yet again. */
1538
1539 mdata->reopen |= IMAP_REOPEN_ALLOW;
1540
1541 rc = msn_end;
1542
1543bail:
1544#ifdef USE_HCACHE
1546 FREE(&uid_seqset);
1547#endif /* USE_HCACHE */
1548
1549 return rc;
1550}
1551
1559int imap_append_message(struct Mailbox *m, struct Message *msg)
1560{
1561 if (!m || !msg)
1562 return -1;
1563
1565 struct ImapMboxData *mdata = imap_mdata_get(m);
1566 if (!adata || !mdata)
1567 return -1;
1568
1569 FILE *fp = NULL;
1570 char buf[2048] = { 0 };
1571 struct Buffer *internaldate = NULL;
1572 struct Buffer *imap_flags = NULL;
1573 size_t len = 0;
1574 struct Progress *progress = NULL;
1575 size_t sent;
1576 int c;
1577 int last;
1578 int rc;
1579
1580 fp = mutt_file_fopen(msg->path, "r");
1581 if (!fp)
1582 {
1583 mutt_perror("%s", msg->path);
1584 goto fail;
1585 }
1586
1587 /* currently we set the \Seen flag on all messages, but probably we
1588 * should scan the message Status header for flag info. Since we're
1589 * already rereading the whole file for length it isn't any more
1590 * expensive (it'd be nice if we had the file size passed in already
1591 * by the code that writes the file, but that's a lot of changes.
1592 * Ideally we'd have an Email structure with flag info here... */
1593 for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
1594 {
1595 if ((c == '\n') && (last != '\r'))
1596 len++;
1597
1598 len++;
1599 }
1600 fseek(fp, 0, SEEK_SET);
1601 clearerr(fp);
1602
1603 if (m->verbose)
1604 {
1606 progress = progress_new(MUTT_PROGRESS_NET, len);
1607 progress_set_message(progress, _("Uploading message..."));
1608 }
1609
1610 internaldate = buf_pool_get();
1611 mutt_date_make_imap(internaldate, msg->received);
1612
1613 imap_flags = buf_pool_get();
1614
1615 if (msg->flags.read)
1616 buf_addstr(imap_flags, " \\Seen");
1617 if (msg->flags.replied)
1618 buf_addstr(imap_flags, " \\Answered");
1619 if (msg->flags.flagged)
1620 buf_addstr(imap_flags, " \\Flagged");
1621 if (msg->flags.draft)
1622 buf_addstr(imap_flags, " \\Draft");
1623
1624 snprintf(buf, sizeof(buf), "APPEND %s (%s) \"%s\" {%lu}", mdata->munge_name,
1625 imap_flags->data + 1, buf_string(internaldate), (unsigned long) len);
1626 buf_pool_release(&internaldate);
1627
1628 imap_cmd_start(adata, buf);
1629
1630 do
1631 {
1632 rc = imap_cmd_step(adata);
1633 } while (rc == IMAP_RES_CONTINUE);
1634
1635 if (rc != IMAP_RES_RESPOND)
1636 goto cmd_step_fail;
1637
1638 for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
1639 {
1640 if ((c == '\n') && (last != '\r'))
1641 buf[len++] = '\r';
1642
1643 buf[len++] = c;
1644
1645 if (len > sizeof(buf) - 3)
1646 {
1647 sent += len;
1648 if (flush_buffer(buf, &len, adata->conn) < 0)
1649 goto fail;
1650 progress_update(progress, sent, -1);
1651 }
1652 }
1653
1654 if (len > 0)
1655 if (flush_buffer(buf, &len, adata->conn) < 0)
1656 goto fail;
1657
1658 if (mutt_socket_send(adata->conn, "\r\n") < 0)
1659 goto fail;
1660 mutt_file_fclose(&fp);
1661
1662 do
1663 {
1664 rc = imap_cmd_step(adata);
1665 } while (rc == IMAP_RES_CONTINUE);
1666
1667 if (rc != IMAP_RES_OK)
1668 goto cmd_step_fail;
1669
1670 progress_free(&progress);
1671 buf_pool_release(&imap_flags);
1672 return 0;
1673
1674cmd_step_fail:
1675 mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1676 if (rc != IMAP_RES_BAD)
1677 {
1678 char *pc = imap_next_word(adata->buf); /* skip sequence number or token */
1679 pc = imap_next_word(pc); /* skip response code */
1680 if (*pc != '\0')
1681 mutt_error("%s", pc);
1682 }
1683
1684fail:
1685 mutt_file_fclose(&fp);
1686 progress_free(&progress);
1687 buf_pool_release(&imap_flags);
1688 return -1;
1689}
1690
1697static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
1698{
1699 struct Email **ep = NULL;
1700 ARRAY_FOREACH(ep, ea)
1701 {
1702 struct Email *e = *ep;
1703 struct ImapEmailData *edata = imap_edata_get(e);
1704
1705 ARRAY_ADD(uida, edata->uid);
1706 }
1707 ARRAY_SORT(uida, imap_sort_uid, NULL);
1708
1709 return ARRAY_SIZE(uida);
1710}
1711
1722int imap_copy_messages(struct Mailbox *m, struct EmailArray *ea,
1723 const char *dest, enum MessageSaveOpt save_opt)
1724{
1725 if (!m || !ea || ARRAY_EMPTY(ea) || !dest)
1726 return -1;
1727
1729 if (!adata)
1730 return -1;
1731
1732 char buf[PATH_MAX] = { 0 };
1733 char mbox[PATH_MAX] = { 0 };
1734 char mmbox[PATH_MAX] = { 0 };
1735 char prompt[PATH_MAX + 64] = { 0 };
1736 int rc;
1737 struct ConnAccount cac = { { 0 } };
1738 enum QuadOption err_continue = MUTT_NO;
1739 int triedcreate = 0;
1740 struct Email *e_cur = *ARRAY_GET(ea, 0);
1741 bool single = (ARRAY_SIZE(ea) == 1);
1742
1743 if (single && e_cur->attach_del)
1744 {
1745 mutt_debug(LL_DEBUG3, "#1 Message contains attachments to be deleted\n");
1746 return 1;
1747 }
1748
1749 if (imap_parse_path(dest, &cac, buf, sizeof(buf)) != 0)
1750 {
1751 mutt_debug(LL_DEBUG1, "bad destination %s\n", dest);
1752 return -1;
1753 }
1754
1755 /* check that the save-to folder is in the same account */
1756 if (!imap_account_match(&adata->conn->account, &cac))
1757 {
1758 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1759 return 1;
1760 }
1761
1762 imap_fix_path_with_delim(adata->delim, buf, mbox, sizeof(mbox));
1763 if (*mbox == '\0')
1764 mutt_str_copy(mbox, "INBOX", sizeof(mbox));
1765 imap_munge_mbox_name(adata->unicode, mmbox, sizeof(mmbox), mbox);
1766
1767 /* loop in case of TRYCREATE */
1768 struct Buffer *cmd = buf_pool_get();
1769 struct Buffer *sync_cmd = buf_pool_get();
1770 do
1771 {
1772 buf_reset(sync_cmd);
1773 buf_reset(cmd);
1774
1775 if (single)
1776 {
1777 mutt_message(_("Copying message %d to %s..."), e_cur->index + 1, mbox);
1778 buf_add_printf(cmd, "UID COPY %u %s", imap_edata_get(e_cur)->uid, mmbox);
1779
1780 if (e_cur->active && e_cur->changed)
1781 {
1782 rc = imap_sync_message_for_copy(m, e_cur, sync_cmd, &err_continue);
1783 if (rc < 0)
1784 {
1785 mutt_debug(LL_DEBUG1, "#2 could not sync\n");
1786 goto out;
1787 }
1788 }
1789 rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_QUEUE);
1790 if (rc != IMAP_EXEC_SUCCESS)
1791 {
1792 mutt_debug(LL_DEBUG1, "#2 could not queue copy\n");
1793 goto out;
1794 }
1795 }
1796 else /* copy tagged messages */
1797 {
1798 /* if any messages have attachments to delete, fall through to FETCH
1799 * and APPEND. TODO: Copy what we can with COPY, fall through for the
1800 * remainder. */
1801 struct Email **ep = NULL;
1802 ARRAY_FOREACH(ep, ea)
1803 {
1804 struct Email *e = *ep;
1805 if (e->attach_del)
1806 {
1807 mutt_debug(LL_DEBUG3, "#2 Message contains attachments to be deleted\n");
1808 rc = 1;
1809 goto out;
1810 }
1811
1812 if (e->active && e->changed)
1813 {
1814 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1815 if (rc < 0)
1816 {
1817 mutt_debug(LL_DEBUG1, "#1 could not sync\n");
1818 goto out;
1819 }
1820 }
1821 }
1822
1823 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1824 emails_to_uid_array(ea, &uida);
1825 rc = imap_exec_msg_set(adata, "UID COPY", mmbox, &uida);
1826 ARRAY_FREE(&uida);
1827
1828 if (rc == 0)
1829 {
1830 mutt_debug(LL_DEBUG1, "No messages tagged\n");
1831 rc = -1;
1832 goto out;
1833 }
1834 else if (rc < 0)
1835 {
1836 mutt_debug(LL_DEBUG1, "#1 could not queue copy\n");
1837 goto out;
1838 }
1839 else
1840 {
1841 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1842 rc, mbox);
1843 }
1844 }
1845
1846 /* let's get it on */
1847 rc = imap_exec(adata, NULL, IMAP_CMD_NONE);
1848 if (rc == IMAP_EXEC_ERROR)
1849 {
1850 if (triedcreate)
1851 {
1852 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", mbox);
1853 break;
1854 }
1855 /* bail out if command failed for reasons other than nonexistent target */
1856 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1857 break;
1858 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1859 snprintf(prompt, sizeof(prompt), _("Create %s?"), mbox);
1860 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1861 if (c_confirm_create &&
1862 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1863 {
1865 goto out;
1866 }
1867 if (imap_create_mailbox(adata, mbox) < 0)
1868 break;
1869 triedcreate = 1;
1870 }
1871 } while (rc == IMAP_EXEC_ERROR);
1872
1873 if (rc != 0)
1874 {
1875 imap_error("imap_copy_messages", adata->buf);
1876 goto out;
1877 }
1878
1879 /* cleanup */
1880 if (save_opt == SAVE_MOVE)
1881 {
1882 struct Email **ep = NULL;
1883 ARRAY_FOREACH(ep, ea)
1884 {
1885 struct Email *e = *ep;
1886 mutt_set_flag(m, e, MUTT_DELETE, true, true);
1887 mutt_set_flag(m, e, MUTT_PURGE, true, true);
1888 }
1889 }
1890
1891 rc = 0;
1892
1893out:
1894 buf_pool_release(&cmd);
1895 buf_pool_release(&sync_cmd);
1896
1897 return (rc < 0) ? -1 : rc;
1898}
1899
1907int imap_cache_del(struct Mailbox *m, struct Email *e)
1908{
1910 struct ImapMboxData *mdata = imap_mdata_get(m);
1911
1912 if (!e || !adata || (adata->mailbox != m) || !mdata)
1913 return -1;
1914
1915 mdata->bcache = imap_bcache_open(m);
1916 char id[64] = { 0 };
1917 snprintf(id, sizeof(id), "%u-%u", mdata->uidvalidity, imap_edata_get(e)->uid);
1918 return mutt_bcache_del(mdata->bcache, id);
1919}
1920
1927{
1929 struct ImapMboxData *mdata = imap_mdata_get(m);
1930
1931 if (!adata || (adata->mailbox != m) || !mdata)
1932 return -1;
1933
1934 mdata->bcache = imap_bcache_open(m);
1936
1937 return 0;
1938}
1939
1958char *imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
1959{
1961 if (!adata || (adata->mailbox != m))
1962 return NULL;
1963
1964 struct ImapHeader newh = { 0 };
1965 struct ImapEmailData old_edata = { 0 };
1966 int local_changes = e->changed;
1967
1968 struct ImapEmailData *edata = e->edata;
1969 newh.edata = edata;
1970
1971 mutt_debug(LL_DEBUG2, "parsing FLAGS\n");
1972 s = msg_parse_flags(&newh, s);
1973 if (!s)
1974 return NULL;
1975
1976 /* Update tags system */
1977 /* We take a copy of the tags so we can split the string */
1978 char *tags_copy = mutt_str_dup(edata->flags_remote);
1979 driver_tags_replace(&e->tags, tags_copy);
1980 FREE(&tags_copy);
1981
1982 /* YAUH (yet another ugly hack): temporarily set context to
1983 * read-write even if it's read-only, so *server* updates of
1984 * flags can be processed by mutt_set_flag. mailbox->changed must
1985 * be restored afterwards */
1986 bool readonly = m->readonly;
1987 m->readonly = false;
1988
1989 /* This is redundant with the following two checks. Removing:
1990 * mutt_set_flag (m, e, MUTT_NEW, !(edata->read || edata->old), true); */
1991 set_changed_flag(m, e, local_changes, server_changes, MUTT_OLD, old_edata.old,
1992 edata->old, e->old);
1993 set_changed_flag(m, e, local_changes, server_changes, MUTT_READ,
1994 old_edata.read, edata->read, e->read);
1995 set_changed_flag(m, e, local_changes, server_changes, MUTT_DELETE,
1996 old_edata.deleted, edata->deleted, e->deleted);
1997 set_changed_flag(m, e, local_changes, server_changes, MUTT_FLAG,
1998 old_edata.flagged, edata->flagged, e->flagged);
1999 set_changed_flag(m, e, local_changes, server_changes, MUTT_REPLIED,
2000 old_edata.replied, edata->replied, e->replied);
2001
2002 /* this message is now definitively *not* changed (mutt_set_flag
2003 * marks things changed as a side-effect) */
2004 if (local_changes == 0)
2005 e->changed = false;
2006 m->changed &= !readonly;
2007 m->readonly = readonly;
2008
2009 return s;
2010}
2011
2015bool imap_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
2016{
2017 struct Envelope *newenv = NULL;
2018 char buf[1024] = { 0 };
2019 char *pc = NULL;
2020 unsigned int bytes = 0;
2021 unsigned int uid = 0;
2022 bool retried = false;
2023 bool read;
2024 int rc;
2025
2026 /* Sam's weird courier server returns an OK response even when FETCH
2027 * fails. Thanks Sam. */
2028 bool fetched = false;
2029
2031
2032 if (!adata || (adata->mailbox != m))
2033 return false;
2034
2035 msg->fp = msg_cache_get(m, e);
2036 if (msg->fp)
2037 {
2038 if (imap_edata_get(e)->parsed)
2039 return true;
2040 goto parsemsg;
2041 }
2042
2043 /* This function is called in a few places after endwin()
2044 * e.g. mutt_pipe_message(). */
2045 bool output_progress = !isendwin() && m->verbose;
2046 if (output_progress)
2047 mutt_message(_("Fetching message..."));
2048
2049 msg->fp = msg_cache_put(m, e);
2050 if (!msg->fp)
2051 {
2052 struct Buffer *tempfile = buf_pool_get();
2053 buf_mktemp(tempfile);
2054 msg->fp = mutt_file_fopen(buf_string(tempfile), "w+");
2055 unlink(buf_string(tempfile));
2056 buf_pool_release(&tempfile);
2057
2058 if (!msg->fp)
2059 return false;
2060 }
2061
2062 /* mark this header as currently inactive so the command handler won't
2063 * also try to update it. HACK until all this code can be moved into the
2064 * command handler */
2065 e->active = false;
2066
2067 const bool c_imap_peek = cs_subset_bool(NeoMutt->sub, "imap_peek");
2068 snprintf(buf, sizeof(buf), "UID FETCH %u %s", imap_edata_get(e)->uid,
2069 ((adata->capabilities & IMAP_CAP_IMAP4REV1) ?
2070 (c_imap_peek ? "BODY.PEEK[]" : "BODY[]") :
2071 "RFC822"));
2072
2073 imap_cmd_start(adata, buf);
2074 do
2075 {
2076 rc = imap_cmd_step(adata);
2077 if (rc != IMAP_RES_CONTINUE)
2078 break;
2079
2080 pc = adata->buf;
2081 pc = imap_next_word(pc);
2082 pc = imap_next_word(pc);
2083
2084 if (mutt_istr_startswith(pc, "FETCH"))
2085 {
2086 while (*pc)
2087 {
2088 pc = imap_next_word(pc);
2089 if (pc[0] == '(')
2090 pc++;
2091 if (mutt_istr_startswith(pc, "UID"))
2092 {
2093 pc = imap_next_word(pc);
2094 if (!mutt_str_atoui(pc, &uid))
2095 goto bail;
2096 if (uid != imap_edata_get(e)->uid)
2097 {
2098 mutt_error(_("The message index is incorrect. Try reopening the mailbox."));
2099 }
2100 }
2101 else if (mutt_istr_startswith(pc, "RFC822") || mutt_istr_startswith(pc, "BODY[]"))
2102 {
2103 pc = imap_next_word(pc);
2104 if (imap_get_literal_count(pc, &bytes) < 0)
2105 {
2106 imap_error("imap_msg_open()", buf);
2107 goto bail;
2108 }
2109
2110 const int res = imap_read_literal(msg->fp, adata, bytes, NULL);
2111 if (res < 0)
2112 {
2113 goto bail;
2114 }
2115 /* pick up trailing line */
2116 rc = imap_cmd_step(adata);
2117 if (rc != IMAP_RES_CONTINUE)
2118 goto bail;
2119 pc = adata->buf;
2120
2121 fetched = true;
2122 }
2123 else if (!e->changed && mutt_istr_startswith(pc, "FLAGS"))
2124 {
2125 /* UW-IMAP will provide a FLAGS update here if the FETCH causes a
2126 * change (eg from \Unseen to \Seen).
2127 * Uncommitted changes in neomutt take precedence. If we decide to
2128 * incrementally update flags later, this won't stop us syncing */
2129 pc = imap_set_flags(m, e, pc, NULL);
2130 if (!pc)
2131 goto bail;
2132 }
2133 }
2134 }
2135 } while (rc == IMAP_RES_CONTINUE);
2136
2137 /* see comment before command start. */
2138 e->active = true;
2139
2140 fflush(msg->fp);
2141 if (ferror(msg->fp))
2142 goto bail;
2143
2144 if (rc != IMAP_RES_OK)
2145 goto bail;
2146
2147 if (!fetched || !imap_code(adata->buf))
2148 goto bail;
2149
2150 if (msg_cache_commit(m, e) < 0)
2151 mutt_debug(LL_DEBUG1, "failed to add message to cache\n");
2152
2153parsemsg:
2154 /* Update the header information. Previously, we only downloaded a
2155 * portion of the headers, those required for the main display. */
2156 fseek(msg->fp, 0, SEEK_SET);
2157 clearerr(msg->fp);
2158 /* It may be that the Status header indicates a message is read, but the
2159 * IMAP server doesn't know the message has been \Seen. So we capture
2160 * the server's notion of 'read' and if it differs from the message info
2161 * picked up in mutt_rfc822_read_header, we mark the message (and context
2162 * changed). Another possibility: ignore Status on IMAP? */
2163 read = e->read;
2164 newenv = mutt_rfc822_read_header(msg->fp, e, false, false);
2165 mutt_env_merge(e->env, &newenv);
2166
2167 /* see above. We want the new status in e->read, so we unset it manually
2168 * and let mutt_set_flag set it correctly, updating context. */
2169 if (read != e->read)
2170 {
2171 e->read = read;
2172 mutt_set_flag(m, e, MUTT_NEW, read, true);
2173 }
2174
2175 e->lines = 0;
2176 while (fgets(buf, sizeof(buf), msg->fp) && !feof(msg->fp))
2177 {
2178 e->lines++;
2179 }
2180
2181 e->body->length = ftell(msg->fp) - e->body->offset;
2182
2184 fseek(msg->fp, 0, SEEK_SET);
2185 clearerr(msg->fp);
2186 imap_edata_get(e)->parsed = true;
2187
2188 /* retry message parse if cached message is empty */
2189 if (!retried && ((e->lines == 0) || (e->body->length == 0)))
2190 {
2191 imap_cache_del(m, e);
2192 retried = true;
2193 goto parsemsg;
2194 }
2195
2196 return true;
2197
2198bail:
2199 e->active = true;
2200 mutt_file_fclose(&msg->fp);
2201 imap_cache_del(m, e);
2202 return false;
2203}
2204
2210int imap_msg_commit(struct Mailbox *m, struct Message *msg)
2211{
2212 int rc = mutt_file_fclose(&msg->fp);
2213 if (rc != 0)
2214 return rc;
2215
2216 return imap_append_message(m, msg);
2217}
2218
2224int imap_msg_close(struct Mailbox *m, struct Message *msg)
2225{
2226 return mutt_file_fclose(&msg->fp);
2227}
2228
2232int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
2233{
2234 int rc = 0;
2235#ifdef USE_HCACHE
2236 bool close_hc = true;
2238 struct ImapMboxData *mdata = imap_mdata_get(m);
2239 if (!mdata || !adata)
2240 return -1;
2241 if (mdata->hcache)
2242 close_hc = false;
2243 else
2244 imap_hcache_open(adata, mdata, true);
2245 rc = imap_hcache_put(mdata, e);
2246 if (close_hc)
2248#endif
2249 return rc;
2250}
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:373
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#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:209
#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:142
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
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:270
struct BodyCache * mutt_bcache_open(struct ConnAccount *account, const char *mailbox)
Open an Email-Body Cache.
Definition bcache.c:162
int mutt_bcache_list(struct BodyCache *bcache, bcache_list_t want_id, void *data)
Find matching entries in the Body Cache.
Definition bcache.c:355
FILE * mutt_bcache_get(struct BodyCache *bcache, const char *id)
Open a file in the Body Cache.
Definition bcache.c:201
int mutt_bcache_del(struct BodyCache *bcache, const char *id)
Delete a file from the Body Cache.
Definition bcache.c:290
FILE * mutt_bcache_put(struct BodyCache *bcache, const char *id)
Create a file in the Body Cache.
Definition bcache.c:228
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:211
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:497
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
void buf_join_str(struct Buffer *buf, const char *str, char sep)
Join a buffer with a string separated by sep.
Definition buffer.c:754
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:577
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:248
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:216
bool mutt_isspace(int arg)
Wrapper for isspace(3).
Definition ctype.c:96
bool mutt_isdigit(int arg)
Wrapper for isdigit(3).
Definition ctype.c:66
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:1358
void mutt_env_merge(struct Envelope *base, struct Envelope **extra)
Merge the headers of two Envelopes.
Definition envelope.c:192
Manage where the email is piped to external commands.
MessageSaveOpt
Message save option.
Definition external.h:52
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition external.h:54
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
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:54
void mutt_flushinp(void)
MacroEvents moved to KeyModuleData UngetKeyEvents moved to KeyModuleData.
Definition get.c:81
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:169
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition edata.c:39
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition message.c:2224
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition message.c:2210
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:2015
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:2232
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition msg_set.c:48
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:288
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:395
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:350
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:460
@ MUTT_HASH_NONE
No flags are set.
Definition hash.h:115
int hcache_delete_raw(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition hcache.c:785
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:757
Header cache multiplexor.
#define hcache_fetch_raw_obj(hc, key, keylen, dst)
Definition lib.h:168
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition adata.c:162
Imap-specific Account data.
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition command.c:1216
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1230
bool imap_code(const char *s)
Was the command successful.
Definition command.c:1372
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:1420
struct ImapEmailData * imap_edata_new(void)
Create a new ImapEmailData.
Definition edata.c:56
struct ImapEmailData * imap_edata_clone(struct ImapEmailData *src)
Clone an ImapEmailData.
Definition edata.c:78
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:66
Imap-specific Email data.
IMAP network mailbox.
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:485
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition mdata.c:61
Imap-specific Mailbox data.
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition message.c:1926
static struct BodyCache * imap_bcache_open(struct Mailbox *m)
Open a message cache.
Definition message.c:81
static FILE * msg_cache_put(struct Mailbox *m, struct Email *e)
Put an email into the message cache.
Definition message.c:129
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:1958
static int msg_parse_fetch(struct ImapHeader *h, char *s)
Handle headers returned from header fetch.
Definition message.c:313
static int read_headers_normal_eval_cache(struct ImapAccountData *adata, struct Mailbox *m, unsigned int msn_end, unsigned int uid_next, bool store_flag_updates, bool eval_condstore)
Retrieve data from the header cache.
Definition message.c:697
static int imap_verify_qresync(struct Mailbox *m)
Check to see if QRESYNC got jumbled.
Definition message.c:1038
static int flush_buffer(char *buf, size_t *len, struct Connection *conn)
Write data to a connection.
Definition message.c:493
int imap_append_message(struct Mailbox *m, struct Message *msg)
Write an email back to the server.
Definition message.c:1559
static int emails_to_uid_array(struct EmailArray *ea, struct UidArray *uida)
Extract IMAP UIDs from Emails.
Definition message.c:1697
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:425
static int read_headers_condstore_qresync_updates(struct ImapAccountData *adata, struct Mailbox *m, unsigned int msn_end, unsigned int uid_next, unsigned long long hc_modseq, bool eval_qresync)
Retrieve updates from the server.
Definition message.c:936
static void imap_alloc_uid_hash(struct ImapAccountData *adata, struct Mailbox *m, unsigned int msn_count)
Create a Hash Table for the UIDs.
Definition message.c:534
static int read_headers_qresync_eval_cache(struct ImapAccountData *adata, struct Mailbox *m, char *uid_seqset)
Retrieve data from the header cache.
Definition message.c:847
static FILE * msg_cache_get(struct Mailbox *m, struct Email *e)
Get the message cache entry for an email.
Definition message.c:108
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:1722
static bool query_abort_header_download(struct ImapAccountData *adata)
Ask the user whether to abort the download.
Definition message.c:509
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition message.c:1907
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:656
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:1115
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:1370
static int msg_cache_commit(struct Mailbox *m, struct Email *e)
Add to the message cache.
Definition message.c:150
static unsigned int imap_fetch_msn_seqset(struct Buffer *buf, struct ImapAccountData *adata, struct Mailbox *m, bool evalhc, unsigned int msn_begin, unsigned int msn_end, unsigned int *fetch_msn_end)
Generate a sequence set.
Definition message.c:560
static char * msg_parse_flags(struct ImapHeader *h, char *s)
Read a FLAGS token into an ImapHeader.
Definition message.c:194
Manage IMAP messages.
Shared constants/structs that are private to IMAP.
#define IMAP_RES_RESPOND
+
Definition private.h:56
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool create)
Open a header cache.
Definition util.c:310
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:54
int imap_hcache_store_uid_seqset(struct ImapMboxData *mdata)
Store a UID Sequence Set in the header cache.
Definition util.c:426
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition util.c:391
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition util.c:1140
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition util.c:461
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition util.c:366
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition util.c:1161
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition private.h:95
@ IMAP_EXEC_ERROR
Imap command failure.
Definition private.h:96
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition util.c:1220
int imap_get_literal_count(char *buf, unsigned int *bytes)
Write number of bytes in an IMAP literal into bytes.
Definition util.c:787
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition util.c:756
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition util.c:668
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition util.c:351
char * imap_fix_path_with_delim(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition util.c:720
@ IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition private.h:70
@ IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:71
@ IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition private.h:72
@ IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition private.h:68
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition util.c:447
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition util.c:1108
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:973
@ IMAP_CAP_CONDSTORE
RFC7162.
Definition private.h:150
@ IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition private.h:136
@ IMAP_CAP_IMAP4
Server supports IMAP4.
Definition private.h:135
#define IMAP_RES_CONTINUE
* ...
Definition private.h:55
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:831
#define IMAP_RES_BAD
<tag> BAD ...
Definition private.h:53
@ IMAP_CMD_NONE
No flags are set.
Definition private.h:82
@ IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition private.h:84
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition util.c:814
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:1029
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition imap.c:849
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:542
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:1114
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:704
Manage keymappings.
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
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:127
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:816
time_t mutt_date_parse_imap(const char *s)
Parse date of the form: DD-MMM-YYYY HH:MM:SS +ZZzz.
Definition date.c:859
Convenience wrapper for the library headers.
#define FALLTHROUGH
Definition lib.h:117
#define _(a)
Definition message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:809
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:587
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
Many unsorted constants and some structs.
MessageType
To set flags or match patterns.
Definition mutt.h:86
@ MUTT_READ
Messages that have been read.
Definition mutt.h:92
@ MUTT_OLD
Old messages.
Definition mutt.h:90
@ MUTT_PURGE
Messages to be purged (bypass trash).
Definition mutt.h:96
@ MUTT_FLAG
Flagged messages.
Definition mutt.h:98
@ MUTT_DELETE
Messages to be deleted.
Definition mutt.h:94
@ MUTT_NEW
New messages.
Definition mutt.h:89
@ MUTT_REPLIED
Messages that have been replied to.
Definition mutt.h:91
#define PATH_MAX
Definition mutt.h:49
void mutt_clear_error(void)
Clear the message line (bottom line of screen).
NeoMutt Logging.
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1211
API for mailboxes.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
Progress Bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc.
Definition lib.h:83
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc.
Definition lib.h:84
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
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:357
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
#define STAILQ_INIT(head)
Definition queue.h:410
#define ASSERT(COND)
Definition signal2.h:59
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition signal.c:68
#define mutt_socket_write_n(conn, buf, len)
Definition socket.h:58
#define mutt_socket_send(conn, buf)
Definition socket.h:56
#define SKIPWS(ch)
Definition string2.h:52
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:59
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:79
bool qresync
true, if QRESYNC is successfully ENABLE'd
Definition adata.h:65
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:64
ImapCapFlags capabilities
Capability flags.
Definition adata.h:56
struct Mailbox * mailbox
Current selected mailbox.
Definition adata.h:80
char * buf
Command buffer.
Definition adata.h:61
struct Connection * conn
Connection to IMAP server.
Definition adata.h:41
IMAP-specific Email data -.
Definition edata.h:35
bool parsed
Email has been 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
Remote flags.
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
System flags.
Definition edata.h:48
IMAP-specific header.
Definition message.h:34
time_t received
Time when message was received.
Definition message.h:37
struct ImapEmailData * edata
IMAP-specific Email data.
Definition message.h:35
long content_length
Length of message content.
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
Next UID for new message.
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
Modification sequence number.
Definition mdata.h:53
char * munge_name
Munged version of the mailbox name.
Definition mdata.h:42
uint32_t uidvalidity
UID validity.
Definition mdata.h:51
char * name
Mailbox name.
Definition mdata.h:41
A mailbox.
Definition mailbox.h:81
int vcount
The number of virtual messages.
Definition mailbox.h:101
bool changed
Mailbox has been modified.
Definition mailbox.h:112
int msg_new
Number of new messages.
Definition mailbox.h:94
int msg_count
Total number of messages.
Definition mailbox.h:90
void * mdata
Driver specific data.
Definition mailbox.h:134
struct HashTable * subj_hash
Hash Table: "Subject" -> Email.
Definition mailbox.h:126
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
struct HashTable * id_hash
Hash Table: "Message-ID" -> Email.
Definition mailbox.h:125
int msg_deleted
Number of deleted messages.
Definition mailbox.h:95
off_t size
Size of the Mailbox.
Definition mailbox.h:86
struct HashTable * label_hash
Hash Table: "X-Label" -> Email.
Definition mailbox.h:127
int msg_flagged
Number of flagged messages.
Definition mailbox.h:92
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:118
int msg_tagged
How many messages are tagged?
Definition mailbox.h:96
bool verbose
Display status messages?
Definition mailbox.h:119
int msg_unread
Number of unread messages.
Definition mailbox.h:91
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
struct Message::@264267271004327071125374067057142037276212342100 flags
Flags for the Message.
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
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
UID Sequence Set Iterator.
Definition private.h:185
bool driver_tags_replace(struct TagList *tl, const char *tags)
Replace all tags.
Definition tags.c:202
#define buf_mktemp(buf)
Definition tmp.h:33