NeoMutt  2025-09-05-7-geaa2bd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
util.c
Go to the documentation of this file.
1
34#include "config.h"
35#include <arpa/inet.h>
36#include <errno.h>
37#include <netdb.h>
38#include <signal.h>
39#include <stdbool.h>
40#include <stdio.h>
41#include <string.h>
42#include <sys/types.h>
43#include <sys/wait.h>
44#include <unistd.h>
45#include "private.h"
46#include "mutt/lib.h"
47#include "config/lib.h"
48#include "email/lib.h"
49#include "core/lib.h"
50#include "conn/lib.h"
51#include "lib.h"
52#include "bcache/lib.h"
53#include "question/lib.h"
54#include "adata.h"
55#include "edata.h"
56#include "globals.h"
57#include "mdata.h"
58#include "msn.h"
59#ifdef USE_HCACHE
60#include "hcache/lib.h"
61#endif
62
71int imap_adata_find(const char *path, struct ImapAccountData **adata,
72 struct ImapMboxData **mdata)
73{
74 struct ConnAccount cac = { { 0 } };
75 struct ImapAccountData *tmp_adata = NULL;
76 char tmp[1024] = { 0 };
77
78 if (imap_parse_path(path, &cac, tmp, sizeof(tmp)) < 0)
79 return -1;
80
81 struct Account *np = NULL;
82 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
83 {
84 if (np->type != MUTT_IMAP)
85 continue;
86
87 tmp_adata = np->adata;
88 if (!tmp_adata)
89 continue;
90 if (imap_account_match(&tmp_adata->conn->account, &cac))
91 {
92 if (mdata)
93 {
94 *mdata = imap_mdata_new(tmp_adata, tmp);
95 }
96 *adata = tmp_adata;
97 return 0;
98 }
99 }
100 mutt_debug(LL_DEBUG3, "no ImapAccountData found\n");
101 return -1;
102}
103
109{
110 mutt_hash_free(&mdata->uid_hash);
111 imap_msn_free(&mdata->msn);
112 mutt_bcache_close(&mdata->bcache);
113}
114
122void imap_get_parent(const char *mbox, char delim, char *buf, size_t buflen)
123{
124 /* Make a copy of the mailbox name, but only if the pointers are different */
125 if (mbox != buf)
126 mutt_str_copy(buf, mbox, buflen);
127
128 int n = mutt_str_len(buf);
129
130 /* Let's go backwards until the next delimiter
131 *
132 * If buf[n] is a '/', the first n-- will allow us
133 * to ignore it. If it isn't, then buf looks like
134 * "/aaaaa/bbbb". There is at least one "b", so we can't skip
135 * the "/" after the 'a's.
136 *
137 * If buf == '/', then n-- => n == 0, so the loop ends
138 * immediately */
139 for (n--; (n >= 0) && (buf[n] != delim); n--)
140 ; // do nothing
141
142 /* We stopped before the beginning. There is a trailing slash. */
143 if (n > 0)
144 {
145 /* Strip the trailing delimiter. */
146 buf[n] = '\0';
147 }
148 else
149 {
150 buf[0] = (n == 0) ? delim : '\0';
151 }
152}
153
163void imap_get_parent_path(const char *path, char *buf, size_t buflen)
164{
165 struct ImapAccountData *adata = NULL;
166 struct ImapMboxData *mdata = NULL;
167 char mbox[1024] = { 0 };
168
169 if (imap_adata_find(path, &adata, &mdata) < 0)
170 {
171 mutt_str_copy(buf, path, buflen);
172 return;
173 }
174
175 /* Gets the parent mbox in mbox */
176 imap_get_parent(mdata->name, adata->delim, mbox, sizeof(mbox));
177
178 /* Returns a fully qualified IMAP url */
179 imap_qualify_path(buf, buflen, &adata->conn->account, mbox);
180 imap_mdata_free((void *) &mdata);
181}
182
190void imap_clean_path(char *path, size_t plen)
191{
192 struct ImapAccountData *adata = NULL;
193 struct ImapMboxData *mdata = NULL;
194
195 if (imap_adata_find(path, &adata, &mdata) < 0)
196 return;
197
198 /* Returns a fully qualified IMAP url */
199 imap_qualify_path(path, plen, &adata->conn->account, mdata->name);
200 imap_mdata_free((void *) &mdata);
201}
202
206static const char *imap_get_field(enum ConnAccountField field, void *gf_data)
207{
208 switch (field)
209 {
210 case MUTT_CA_LOGIN:
211 return cs_subset_string(NeoMutt->sub, "imap_login");
212 case MUTT_CA_USER:
213 return cs_subset_string(NeoMutt->sub, "imap_user");
214 case MUTT_CA_PASS:
215 return cs_subset_string(NeoMutt->sub, "imap_pass");
217 return cs_subset_string(NeoMutt->sub, "imap_oauth_refresh_command");
218 case MUTT_CA_HOST:
219 default:
220 return NULL;
221 }
222}
223
224#ifdef USE_HCACHE
233static void imap_msn_index_to_uid_seqset(struct Buffer *buf, struct ImapMboxData *mdata)
234{
235 int first = 1, state = 0;
236 unsigned int cur_uid = 0, last_uid = 0;
237 unsigned int range_begin = 0, range_end = 0;
238 const size_t max_msn = imap_msn_highest(&mdata->msn);
239
240 for (unsigned int msn = 1; msn <= max_msn + 1; msn++)
241 {
242 bool match = false;
243 if (msn <= max_msn)
244 {
245 struct Email *e_cur = imap_msn_get(&mdata->msn, msn - 1);
246 cur_uid = e_cur ? imap_edata_get(e_cur)->uid : 0;
247 if (!state || (cur_uid && ((cur_uid - 1) == last_uid)))
248 match = true;
249 last_uid = cur_uid;
250 }
251
252 if (match)
253 {
254 switch (state)
255 {
256 case 1: /* single: convert to a range */
257 state = 2;
259
260 case 2: /* extend range ending */
261 range_end = cur_uid;
262 break;
263 default:
264 state = 1;
265 range_begin = cur_uid;
266 break;
267 }
268 }
269 else if (state)
270 {
271 if (first)
272 first = 0;
273 else
274 buf_addch(buf, ',');
275
276 if (state == 1)
277 buf_add_printf(buf, "%u", range_begin);
278 else if (state == 2)
279 buf_add_printf(buf, "%u:%u", range_begin, range_end);
280
281 state = 1;
282 range_begin = cur_uid;
283 }
284 }
285}
286
290static void imap_hcache_namer(const char *path, struct Buffer *dest)
291{
292 buf_printf(dest, "%s.hcache", path);
293}
294
301void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool create)
302{
303 if (!adata || !mdata)
304 return;
305
306 if (mdata->hcache)
307 return;
308
309 struct HeaderCache *hc = NULL;
310 struct Buffer *mbox = buf_pool_get();
311 struct Buffer *cachepath = buf_pool_get();
312
313 imap_cachepath(adata->delim, mdata->name, mbox);
314
315 if (strstr(buf_string(mbox), "/../") || mutt_str_equal(buf_string(mbox), "..") ||
316 mutt_strn_equal(buf_string(mbox), "../", 3))
317 {
318 goto cleanup;
319 }
320 size_t len = buf_len(mbox);
321 if ((len > 3) && (mutt_str_equal(buf_string(mbox) + len - 3, "/..")))
322 goto cleanup;
323
324 struct Url url = { 0 };
325 account_to_url(&adata->conn->account, &url);
326 url.path = mbox->data;
327 url_tobuffer(&url, cachepath, U_PATH);
328
329 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
330 hc = hcache_open(c_header_cache, buf_string(cachepath), imap_hcache_namer, create);
331
332cleanup:
333 buf_pool_release(&mbox);
334 buf_pool_release(&cachepath);
335 mdata->hcache = hc;
336}
337
343{
344 if (!mdata->hcache)
345 return;
346
347 hcache_close(&mdata->hcache);
348}
349
357struct Email *imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
358{
359 if (!mdata->hcache)
360 return NULL;
361
362 char key[16] = { 0 };
363
364 snprintf(key, sizeof(key), "%u", uid);
365 struct HCacheEntry hce = hcache_fetch_email(mdata->hcache, key, mutt_str_len(key),
366 mdata->uidvalidity);
367 if (!hce.email && hce.uidvalidity)
368 {
369 mutt_debug(LL_DEBUG3, "hcache uidvalidity mismatch: %u\n", hce.uidvalidity);
370 }
371
372 return hce.email;
373}
374
382int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
383{
384 if (!mdata->hcache)
385 return -1;
386
387 char key[16] = { 0 };
388
389 snprintf(key, sizeof(key), "%u", imap_edata_get(e)->uid);
390 return hcache_store_email(mdata->hcache, key, mutt_str_len(key), e, mdata->uidvalidity);
391}
392
400int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
401{
402 if (!mdata->hcache)
403 return -1;
404
405 char key[16] = { 0 };
406
407 snprintf(key, sizeof(key), "%u", uid);
408 return hcache_delete_email(mdata->hcache, key, mutt_str_len(key));
409}
410
418{
419 if (!mdata->hcache)
420 return -1;
421
422 struct Buffer *buf = buf_pool_get();
423 buf_alloc(buf, 8192); // The seqset is likely large. Preallocate to reduce reallocs
425
426 int rc = hcache_store_raw(mdata->hcache, "UIDSEQSET", 9, buf->data, buf_len(buf) + 1);
427 mutt_debug(LL_DEBUG3, "Stored UIDSEQSET %s\n", buf_string(buf));
428 buf_pool_release(&buf);
429 return rc;
430}
431
439{
440 if (!mdata->hcache)
441 return -1;
442
443 return hcache_delete_raw(mdata->hcache, "UIDSEQSET", 9);
444}
445
453{
454 if (!mdata->hcache)
455 return NULL;
456
457 char *seqset = hcache_fetch_raw_str(mdata->hcache, "UIDSEQSET", 9);
458 mutt_debug(LL_DEBUG3, "Retrieved UIDSEQSET %s\n", NONULL(seqset));
459
460 return seqset;
461}
462#endif
463
476int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
477{
478 static unsigned short ImapPort = 0;
479 static unsigned short ImapsPort = 0;
480
481 if (ImapPort == 0)
482 {
483 struct servent *service = getservbyname("imap", "tcp");
484 if (service)
485 ImapPort = ntohs(service->s_port);
486 else
487 ImapPort = IMAP_PORT;
488 mutt_debug(LL_DEBUG3, "Using default IMAP port %d\n", ImapPort);
489 }
490
491 if (ImapsPort == 0)
492 {
493 struct servent *service = getservbyname("imaps", "tcp");
494 if (service)
495 ImapsPort = ntohs(service->s_port);
496 else
497 ImapsPort = IMAP_SSL_PORT;
498 mutt_debug(LL_DEBUG3, "Using default IMAPS port %d\n", ImapsPort);
499 }
500
501 /* Defaults */
502 cac->port = ImapPort;
504 cac->service = "imap";
506
507 struct Url *url = url_parse(path);
508 if (!url)
509 return -1;
510
511 if ((url->scheme != U_IMAP) && (url->scheme != U_IMAPS))
512 {
513 url_free(&url);
514 return -1;
515 }
516
517 if ((account_from_url(cac, url) < 0) || (cac->host[0] == '\0'))
518 {
519 url_free(&url);
520 return -1;
521 }
522
523 if (url->scheme == U_IMAPS)
524 cac->flags |= MUTT_ACCT_SSL;
525
526 mutt_str_copy(mailbox, url->path, mailboxlen);
527
528 url_free(&url);
529
530 if ((cac->flags & MUTT_ACCT_SSL) && !(cac->flags & MUTT_ACCT_PORT))
531 cac->port = ImapsPort;
532
533 return 0;
534}
535
547int imap_mxcmp(const char *mx1, const char *mx2)
548{
549 char *b1 = NULL;
550 char *b2 = NULL;
551 int rc;
552
553 if (!mx1 || (*mx1 == '\0'))
554 mx1 = "INBOX";
555 if (!mx2 || (*mx2 == '\0'))
556 mx2 = "INBOX";
557 if (mutt_istr_equal(mx1, "INBOX") && mutt_istr_equal(mx2, "INBOX"))
558 {
559 return 0;
560 }
561
562 b1 = MUTT_MEM_MALLOC(strlen(mx1) + 1, char);
563 b2 = MUTT_MEM_MALLOC(strlen(mx2) + 1, char);
564
565 imap_fix_path(mx1, b1, strlen(mx1) + 1);
566 imap_fix_path(mx2, b2, strlen(mx2) + 1);
567
568 rc = mutt_str_cmp(b1, b2);
569 FREE(&b1);
570 FREE(&b2);
571
572 return rc;
573}
574
583void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
584{
585 struct ConnAccount cac_target = { { 0 } };
586 struct ConnAccount cac_home = { { 0 } };
587 struct Url url = { 0 };
588 const char *delim = NULL;
589 int tlen;
590 int hlen = 0;
591 bool home_match = false;
592 char target_mailbox[1024] = { 0 };
593 char home_mailbox[1024] = { 0 };
594
595 if (imap_parse_path(path, &cac_target, target_mailbox, sizeof(target_mailbox)) < 0)
596 return;
597
598 if (imap_path_probe(folder, NULL) != MUTT_IMAP)
599 goto fallback;
600
601 if (imap_parse_path(folder, &cac_home, home_mailbox, sizeof(home_mailbox)) < 0)
602 goto fallback;
603
604 tlen = mutt_str_len(target_mailbox);
605 hlen = mutt_str_len(home_mailbox);
606
607 /* check whether we can do '+' substitution */
608 if (tlen && imap_account_match(&cac_home, &cac_target) &&
609 mutt_strn_equal(home_mailbox, target_mailbox, hlen))
610 {
611 const char *const c_imap_delim_chars = cs_subset_string(NeoMutt->sub, "imap_delim_chars");
612 if (hlen == 0)
613 {
614 home_match = true;
615 }
616 else if (c_imap_delim_chars)
617 {
618 for (delim = c_imap_delim_chars; *delim != '\0'; delim++)
619 if (target_mailbox[hlen] == *delim)
620 home_match = true;
621 }
622 }
623
624 /* do the '+' substitution */
625 if (home_match)
626 {
627 *path++ = '+';
628 /* copy remaining path, skipping delimiter */
629 if (hlen != 0)
630 hlen++;
631 memcpy(path, target_mailbox + hlen, tlen - hlen);
632 path[tlen - hlen] = '\0';
633 return;
634 }
635
636fallback:
637 account_to_url(&cac_target, &url);
638 url.path = target_mailbox;
639 url_tostring(&url, path, pathlen, U_NO_FLAGS);
640}
641
648enum QuadOption imap_continue(const char *msg, const char *resp)
649{
650 imap_error(msg, resp);
651 return query_yesorno(_("Continue?"), MUTT_NO);
652}
653
659void imap_error(const char *where, const char *msg)
660{
661 mutt_error("%s [%s]", where, msg);
662}
663
680char *imap_fix_path(const char *mailbox, char *path, size_t plen)
681{
682 const char *const c_imap_delim_chars = cs_subset_string(NeoMutt->sub, "imap_delim_chars");
683
684 char *out = path;
685 size_t space_left = plen - 1;
686
687 if (mailbox)
688 {
689 for (const char *c = mailbox; *c && space_left; c++, space_left--)
690 {
691 if (strchr(NONULL(c_imap_delim_chars), *c))
692 {
693 return imap_fix_path_with_delim(*c, mailbox, path, plen);
694 }
695 *out++ = *c;
696 }
697 }
698
699 *out = '\0';
700 return path;
701}
702
712char *imap_fix_path_with_delim(const char delim, const char *mailbox, char *path, size_t plen)
713{
714 char *out = path;
715 size_t space_left = plen - 1;
716
717 if (mailbox)
718 {
719 for (const char *c = mailbox; *c && space_left; c++, space_left--)
720 {
721 if (*c == delim || *c == '/')
722 {
723 while (*c && *(c + 1) == *c)
724 c++;
725 *out++ = delim;
726 }
727 else
728 {
729 *out++ = *c;
730 }
731 }
732 }
733
734 if (out != path && *(out - 1) == delim)
735 {
736 out--;
737 }
738 *out = '\0';
739 return path;
740}
741
748void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
749{
750 const char *p = mailbox;
751 buf_reset(dest);
752 if (!p)
753 return;
754
755 while (*p)
756 {
757 if (p[0] == delim)
758 {
759 buf_addch(dest, '/');
760 /* simple way to avoid collisions with UIDs */
761 if ((p[1] >= '0') && (p[1] <= '9'))
762 buf_addch(dest, '_');
763 }
764 else
765 {
766 buf_addch(dest, *p);
767 }
768 p++;
769 }
770}
771
779int imap_get_literal_count(const char *buf, unsigned int *bytes)
780{
781 char *pc = NULL;
782 char *pn = NULL;
783
784 if (!buf || !(pc = strchr(buf, '{')))
785 return -1;
786
787 pc++;
788 pn = pc;
789 while (mutt_isdigit(*pc))
790 pc++;
791 *pc = '\0';
792 if (!mutt_str_atoui(pn, bytes))
793 return -1;
794
795 return 0;
796}
797
806char *imap_get_qualifier(char *buf)
807{
808 char *s = buf;
809
810 /* skip tag */
811 s = imap_next_word(s);
812 /* skip OK/NO/BAD response */
813 s = imap_next_word(s);
814
815 return s;
816}
817
823char *imap_next_word(char *s)
824{
825 bool quoted = false;
826
827 while (*s)
828 {
829 if (*s == '\\')
830 {
831 s++;
832 if (*s)
833 s++;
834 continue;
835 }
836 if (*s == '\"')
837 quoted = !quoted;
838 if (!quoted && mutt_isspace(*s))
839 break;
840 s++;
841 }
842
843 SKIPWS(s);
844 return s;
845}
846
854void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *cac, char *path)
855{
856 struct Url url = { 0 };
857 account_to_url(cac, &url);
858 url.path = path;
859 url_tostring(&url, buf, buflen, U_NO_FLAGS);
860}
861
868void imap_buf_qualify_path(struct Buffer *buf, struct ConnAccount *cac, char *path)
869{
870 struct Url url = { 0 };
871 account_to_url(cac, &url);
872 url.path = path;
873 url_tobuffer(&url, buf, U_NO_FLAGS);
874}
875
885void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
886{
887 const char *quote = "`\"\\";
888 if (!quote_backtick)
889 quote++;
890
891 char *pt = dest;
892 const char *s = src;
893
894 *pt++ = '"';
895 /* save room for quote-chars */
896 dlen -= 3;
897
898 for (; *s && dlen; s++)
899 {
900 if (strchr(quote, *s))
901 {
902 if (dlen < 2)
903 break;
904 dlen -= 2;
905 *pt++ = '\\';
906 *pt++ = *s;
907 }
908 else
909 {
910 *pt++ = *s;
911 dlen--;
912 }
913 }
914 *pt++ = '"';
915 *pt = '\0';
916}
917
923{
924 char *d = s;
925
926 if (*s == '\"')
927 s++;
928 else
929 return;
930
931 while (*s)
932 {
933 if (*s == '\"')
934 {
935 *d = '\0';
936 return;
937 }
938 if (*s == '\\')
939 {
940 s++;
941 }
942 if (*s)
943 {
944 *d = *s;
945 d++;
946 s++;
947 }
948 }
949 *d = '\0';
950}
951
959void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
960{
961 char *buf = mutt_str_dup(src);
962 imap_utf_encode(unicode, &buf);
963
964 imap_quote_string(dest, dlen, buf, false);
965
966 FREE(&buf);
967}
968
976void imap_unmunge_mbox_name(bool unicode, char *s)
977{
979
980 char *buf = mutt_str_dup(s);
981 if (buf)
982 {
983 imap_utf_decode(unicode, &buf);
984 strncpy(s, buf, strlen(s));
985 }
986
987 FREE(&buf);
988}
989
994{
995 time_t now = mutt_date_now();
996 struct Account *np = NULL;
997 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
998 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
999 {
1000 if (np->type != MUTT_IMAP)
1001 continue;
1002
1003 struct ImapAccountData *adata = np->adata;
1004 if (!adata || !adata->mailbox)
1005 continue;
1006
1007 if ((adata->state >= IMAP_AUTHENTICATED) && (now >= (adata->lastread + c_imap_keep_alive)))
1008 imap_check_mailbox(adata->mailbox, true);
1009 }
1010}
1011
1018{
1019 struct sigaction oldalrm = { 0 };
1020 struct sigaction act = { 0 };
1021 sigset_t oldmask = { 0 };
1022 int rc;
1023
1024 const bool c_imap_passive = cs_subset_bool(NeoMutt->sub, "imap_passive");
1025 cs_subset_str_native_set(NeoMutt->sub, "imap_passive", true, NULL);
1026 OptKeepQuiet = true;
1027
1028 sigprocmask(SIG_SETMASK, NULL, &oldmask);
1029
1030 sigemptyset(&act.sa_mask);
1031 act.sa_handler = mutt_sig_empty_handler;
1032#ifdef SA_INTERRUPT
1033 act.sa_flags = SA_INTERRUPT;
1034#else
1035 act.sa_flags = 0;
1036#endif
1037
1038 sigaction(SIGALRM, &act, &oldalrm);
1039
1040 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1041 alarm(c_imap_keep_alive);
1042 while ((waitpid(pid, &rc, 0) < 0) && (errno == EINTR))
1043 {
1044 alarm(0); /* cancel a possibly pending alarm */
1046 alarm(c_imap_keep_alive);
1047 }
1048
1049 alarm(0); /* cancel a possibly pending alarm */
1050
1051 sigaction(SIGALRM, &oldalrm, NULL);
1052 sigprocmask(SIG_SETMASK, &oldmask, NULL);
1053
1054 OptKeepQuiet = false;
1055 cs_subset_str_native_set(NeoMutt->sub, "imap_passive", c_imap_passive, NULL);
1056
1057 return rc;
1058}
1059
1065{
1067 struct ImapMboxData *mdata = imap_mdata_get(m);
1068 if (!adata || !adata->mailbox || (adata->mailbox != m) || !mdata)
1069 return;
1070 mdata->reopen |= IMAP_REOPEN_ALLOW;
1071}
1072
1078{
1080 struct ImapMboxData *mdata = imap_mdata_get(m);
1081 if (!adata || !adata->mailbox || (adata->mailbox != m) || !mdata)
1082 return;
1083 mdata->reopen &= ~IMAP_REOPEN_ALLOW;
1084}
1085
1092bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
1093{
1094 if (!a1 || !a2)
1095 return false;
1096 if (a1->type != a2->type)
1097 return false;
1098 if (!mutt_istr_equal(a1->host, a2->host))
1099 return false;
1100 if ((a1->port != 0) && (a2->port != 0) && (a1->port != a2->port))
1101 return false;
1102 if (a1->flags & a2->flags & MUTT_ACCT_USER)
1103 return mutt_str_equal(a1->user, a2->user);
1104
1105 const char *user = NONULL(NeoMutt->username);
1106
1107 const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
1108 if ((a1->type == MUTT_ACCT_TYPE_IMAP) && c_imap_user)
1109 user = c_imap_user;
1110
1111 if (a1->flags & MUTT_ACCT_USER)
1112 return mutt_str_equal(a1->user, user);
1113 if (a2->flags & MUTT_ACCT_USER)
1114 return mutt_str_equal(a2->user, user);
1115
1116 return true;
1117}
1118
1124struct SeqsetIterator *mutt_seqset_iterator_new(const char *seqset)
1125{
1126 if (!seqset || (*seqset == '\0'))
1127 return NULL;
1128
1129 struct SeqsetIterator *iter = MUTT_MEM_CALLOC(1, struct SeqsetIterator);
1130 iter->full_seqset = mutt_str_dup(seqset);
1131 iter->eostr = strchr(iter->full_seqset, '\0');
1132 iter->substr_cur = iter->substr_end = iter->full_seqset;
1133
1134 return iter;
1135}
1136
1145int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
1146{
1147 if (!iter || !next)
1148 return -1;
1149
1150 if (iter->in_range)
1151 {
1152 if ((iter->down && (iter->range_cur == (iter->range_end - 1))) ||
1153 (!iter->down && (iter->range_cur == (iter->range_end + 1))))
1154 {
1155 iter->in_range = 0;
1156 }
1157 }
1158
1159 if (!iter->in_range)
1160 {
1161 iter->substr_cur = iter->substr_end;
1162 if (iter->substr_cur == iter->eostr)
1163 return 1;
1164
1165 iter->substr_end = strchr(iter->substr_cur, ',');
1166 if (!iter->substr_end)
1167 iter->substr_end = iter->eostr;
1168 else
1169 *(iter->substr_end++) = '\0';
1170
1171 char *range_sep = strchr(iter->substr_cur, ':');
1172 if (range_sep)
1173 *range_sep++ = '\0';
1174
1175 if (!mutt_str_atoui_full(iter->substr_cur, &iter->range_cur))
1176 return -1;
1177 if (range_sep)
1178 {
1179 if (!mutt_str_atoui_full(range_sep, &iter->range_end))
1180 return -1;
1181 }
1182 else
1183 {
1184 iter->range_end = iter->range_cur;
1185 }
1186
1187 iter->down = (iter->range_end < iter->range_cur);
1188 iter->in_range = 1;
1189 }
1190
1191 *next = iter->range_cur;
1192 if (iter->down)
1193 iter->range_cur--;
1194 else
1195 iter->range_cur++;
1196
1197 return 0;
1198}
1199
1205{
1206 if (!ptr || !*ptr)
1207 return;
1208
1209 struct SeqsetIterator *iter = *ptr;
1210 FREE(&iter->full_seqset);
1211 FREE(ptr);
1212}
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)
void mutt_bcache_close(struct BodyCache **ptr)
Close an Email-Body Cache.
Definition: bcache.c:167
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
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:337
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
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:168
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.
ConnAccountField
Login credentials.
Definition: connaccount.h:33
@ MUTT_CA_OAUTH_CMD
OAuth refresh command.
Definition: connaccount.h:38
@ MUTT_CA_USER
User name.
Definition: connaccount.h:36
@ MUTT_CA_LOGIN
Login name.
Definition: connaccount.h:35
@ MUTT_CA_HOST
Server name.
Definition: connaccount.h:34
@ MUTT_CA_PASS
Password.
Definition: connaccount.h:37
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition: connaccount.h:47
#define MUTT_ACCT_USER
User field has been set.
Definition: connaccount.h:44
#define MUTT_ACCT_PORT
Port field has been set.
Definition: connaccount.h:43
Convenience wrapper for the core headers.
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
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
Structs that make up an email.
bool OptKeepQuiet
(pseudo) shut up the message and refresh functions while we are executing an external program
Definition: globals.c:59
static const char * imap_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition: util.c:206
static void imap_hcache_namer(const char *path, struct Buffer *dest)
Generate a filename for the header cache - Implements hcache_namer_t -.
Definition: util.c:290
#define mutt_error(...)
Definition: logging2.h:93
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition: mdata.c:40
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2349
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer, bool create)
Multiplexor for StoreOps::open.
Definition: hcache.c:471
int hcache_delete_email(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:739
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition: hcache.c:542
struct HCacheEntry hcache_fetch_email(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:562
int hcache_store_email(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition: hcache.c:670
char * hcache_fetch_raw_str(struct HeaderCache *hc, const char *key, size_t keylen)
Fetch a string from the cache.
Definition: hcache.c:652
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.
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:123
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:67
struct ImapMboxData * imap_mdata_new(struct ImapAccountData *adata, const char *name)
Allocate and initialise a new ImapMboxData structure.
Definition: mdata.c:74
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:61
#define IMAP_PORT
Default port for IMAP.
Definition: private.h:44
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition: private.h:107
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:64
void imap_utf_encode(bool unicode, char **s)
Encode email from local charset to UTF-8.
Definition: utf7.c:397
#define IMAP_SSL_PORT
Port for IMAP over SSL/TLS.
Definition: private.h:45
void imap_utf_decode(bool unicode, char **s)
Decode email from UTF-8 to local charset.
Definition: utf7.c:430
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition: imap.c:1034
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:46
#define FREE(x)
Definition: memory.h:62
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:47
#define MUTT_MEM_MALLOC(n, type)
Definition: memory.h:48
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
IMAP MSN helper functions.
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:456
Convenience wrapper for the library headers.
#define FALLTHROUGH
Definition: lib.h:113
#define _(a)
Definition: message.h:28
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:400
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:671
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:659
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:426
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
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
void account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:80
int account_from_url(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:44
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:36
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.
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
Ask the user a question.
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:326
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:782
GUI display the mailboxes in a side panel.
void mutt_sig_empty_handler(int sig)
Dummy signal handler.
Definition: signal.c:131
Key value store.
#define NONULL(x)
Definition: string2.h:36
#define SKIPWS(ch)
Definition: string2.h:44
A group of associated Mailboxes.
Definition: account.h:36
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:37
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
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
char user[128]
Username.
Definition: connaccount.h:56
const char * service
Name of the service, e.g. "imap".
Definition: connaccount.h:61
char host[128]
Server to login to.
Definition: connaccount.h:54
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Definition: connaccount.h:70
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition: connaccount.h:59
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition: connaccount.h:60
unsigned short port
Port to connect to.
Definition: connaccount.h:58
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
The envelope/body of an email.
Definition: email.h:39
char * path
Path of Email (for local Mailboxes)
Definition: email.h:70
Wrapper for Email retrieved from the header cache.
Definition: lib.h:99
uint32_t uidvalidity
IMAP-specific UIDVALIDITY.
Definition: lib.h:100
struct Email * email
Retrieved email.
Definition: lib.h:102
Header Cache.
Definition: lib.h:86
IMAP-specific Account data -.
Definition: adata.h:40
char delim
Path delimiter.
Definition: adata.h:75
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
unsigned int uid
32-bit Message UID
Definition: edata.h:45
IMAP-specific Mailbox data -.
Definition: mdata.h:40
struct HeaderCache * hcache
Email header cache.
Definition: mdata.h:63
struct BodyCache * bcache
Email body cache.
Definition: mdata.h:61
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition: mdata.h:59
uint32_t uidvalidity
Definition: mdata.h:51
char * name
Mailbox name.
Definition: mdata.h:41
A mailbox.
Definition: mailbox.h:79
void * mdata
Driver specific data.
Definition: mailbox.h:132
Container for Accounts, Notifications.
Definition: neomutt.h:43
char * username
User's login name.
Definition: neomutt.h:54
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:48
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:47
UID Sequence Set Iterator.
Definition: private.h:169
char * eostr
Definition: private.h:171
char * substr_end
Definition: private.h:177
unsigned int range_end
Definition: private.h:175
char * substr_cur
Definition: private.h:176
char * full_seqset
Definition: private.h:170
unsigned int range_cur
Definition: private.h:174
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * src
Raw URL string.
Definition: url.h:77
char * path
Path.
Definition: url.h:75
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:70
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:299
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:238
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
int url_tostring(const struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:422
int url_tobuffer(const struct Url *url, struct Buffer *buf, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:357
#define U_NO_FLAGS
Definition: url.h:49
@ U_IMAP
Url is imap://.
Definition: url.h:39
@ U_IMAPS
Url is imaps://.
Definition: url.h:40
#define U_PATH
Definition: url.h:50
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:976
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
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *cac, char *path)
Make an absolute IMAP folder target.
Definition: util.c:854
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1064
void imap_disallow_reopen(struct Mailbox *m)
Disallow re-opening a folder upon expunge.
Definition: util.c:1077
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
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
void imap_mdata_cache_reset(struct ImapMboxData *mdata)
Release and clear cache data of ImapMboxData structure.
Definition: util.c:108
void imap_get_parent(const char *mbox, char delim, char *buf, size_t buflen)
Get an IMAP folder's parent.
Definition: util.c:122
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1124
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:885
char * imap_hcache_get_uid_seqset(struct ImapMboxData *mdata)
Get a UID Sequence Set from the header cache.
Definition: util.c:452
enum QuadOption imap_continue(const char *msg, const char *resp)
Display a message and ask the user if they want to go on.
Definition: util.c:648
void imap_unquote_string(char *s)
Equally stupid unquoting routine.
Definition: util.c:922
struct Email * imap_hcache_get(struct ImapMboxData *mdata, unsigned int uid)
Get a header cache entry by its UID.
Definition: util.c:357
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition: util.c:1017
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:163
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1145
void imap_clean_path(char *path, size_t plen)
Cleans an IMAP path using imap_fix_path.
Definition: util.c:190
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1204
int imap_mxcmp(const char *mx1, const char *mx2)
Compare mailbox names, giving priority to INBOX.
Definition: util.c:547
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:583
void imap_cachepath(char delim, const char *mailbox, struct Buffer *dest)
Generate a cache path for a mailbox.
Definition: util.c:748
char * imap_fix_path(const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:680
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(const char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:712
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition: util.c:400
int imap_hcache_clear_uid_seqset(struct ImapMboxData *mdata)
Delete a UID Sequence Set from the header cache.
Definition: util.c:438
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:71
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
void imap_keep_alive(void)
Poll the current folder to keep the connection alive.
Definition: util.c:993
static void imap_msn_index_to_uid_seqset(struct Buffer *buf, struct ImapMboxData *mdata)
Convert MSN index of UIDs to Seqset.
Definition: util.c:233
void imap_buf_qualify_path(struct Buffer *buf, struct ConnAccount *cac, char *path)
Make an absolute IMAP folder target to a buffer.
Definition: util.c:868
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:823
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:806