NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
address.c
Go to the documentation of this file.
1
26
32
33#include "config.h"
34#include <stdbool.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <string.h>
38#include "mutt/lib.h"
39#include "address.h"
40#include "idna2.h"
41
45const char AddressSpecials[] = "\"(),.:;<>@[\\]";
46
67#define is_special(ch, mask) \
68 ((ch) >= 32 && (ch) < 96 && ((mask >> ((ch) - 32)) & 1))
69
71#define ADDRESS_SPECIAL_MASK 0x380000015c005304ULL
72
74#define USER_SPECIAL_MASK 0x280000015c001200ULL
75
77#define DOMAIN_SPECIAL_MASK 0x000000015c001204ULL
78
80#define ROUTE_SPECIAL_MASK 0x000000015c000204ULL
81
91static const char *parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
92{
93 int level = 1;
94
95 while (*s && level)
96 {
97 if (*s == '(')
98 {
99 level++;
100 }
101 else if (*s == ')')
102 {
103 if (--level == 0)
104 {
105 s++;
106 break;
107 }
108 }
109 else if (*s == '\\')
110 {
111 if (!*++s)
112 break;
113 }
114 if (*commentlen < commentmax)
115 comment[(*commentlen)++] = *s;
116 s++;
117 }
118 if (level != 0)
119 {
120 return NULL;
121 }
122 return s;
123}
124
134static const char *parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
135{
136 while (*s)
137 {
138 if (*tokenlen < tokenmax)
139 token[*tokenlen] = *s;
140 if (*s == '\\')
141 {
142 if (!*++s)
143 break;
144
145 if (*tokenlen < tokenmax)
146 token[*tokenlen] = *s;
147 }
148 else if (*s == '"')
149 {
150 return s + 1;
151 }
152 (*tokenlen)++;
153 s++;
154 }
155 return NULL;
156}
157
166static const char *next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
167{
168 if (*s == '(')
169 return parse_comment(s + 1, token, tokenlen, tokenmax);
170 if (*s == '"')
171 return parse_quote(s + 1, token, tokenlen, tokenmax);
172 if (*s && is_special(*s, ADDRESS_SPECIAL_MASK))
173 {
174 if (*tokenlen < tokenmax)
175 token[(*tokenlen)++] = *s;
176 return s + 1;
177 }
178 while (*s)
179 {
181 break;
182 if (*tokenlen < tokenmax)
183 token[(*tokenlen)++] = *s;
184 s++;
185 }
186 return s;
187}
188
213static const char *parse_mailboxdomain(const char *s, uint64_t special_mask,
214 char *mailbox, size_t *mailboxlen,
215 size_t mailboxmax, char *comment,
216 size_t *commentlen, size_t commentmax)
217{
218 const char *ps = NULL;
219
220 while (*s)
221 {
223 if ((*s == '\0'))
224 return s;
225
226 if (is_special(*s, special_mask))
227 return s;
228
229 if (*s == '(')
230 {
231 if (*commentlen && (*commentlen < commentmax))
232 comment[(*commentlen)++] = ' ';
233 ps = next_token(s, comment, commentlen, commentmax);
234 }
235 else
236 {
237 ps = next_token(s, mailbox, mailboxlen, mailboxmax);
238 }
239 if (!ps)
240 return NULL;
241 s = ps;
242 }
243
244 return s;
245}
246
260static const char *parse_address(const char *s, char *token, size_t *tokenlen,
261 size_t tokenmax, char *comment, size_t *commentlen,
262 size_t commentmax, struct Address *addr)
263{
264 s = parse_mailboxdomain(s, USER_SPECIAL_MASK, token, tokenlen, tokenmax,
265 comment, commentlen, commentmax);
266 if (!s)
267 return NULL;
268
269 if (*s == '@')
270 {
271 if (*tokenlen < tokenmax)
272 token[(*tokenlen)++] = '@';
273 s = parse_mailboxdomain(s + 1, DOMAIN_SPECIAL_MASK, token, tokenlen,
274 tokenmax, comment, commentlen, commentmax);
275 if (!s)
276 return NULL;
277 }
278
279 terminate_string(token, *tokenlen, tokenmax);
280 addr->mailbox = buf_new(token);
281
282 if (*commentlen && !addr->personal)
283 {
284 terminate_string(comment, *commentlen, commentmax);
285 addr->personal = buf_new(comment);
286 }
287
288 return s;
289}
290
300static const char *parse_route_addr(const char *s, char *comment, size_t *commentlen,
301 size_t commentmax, struct Address *addr)
302{
303 char token[1024] = { 0 };
304 size_t tokenlen = 0;
305
307
308 /* find the end of the route */
309 if (*s == '@')
310 {
311 while (s && (*s == '@'))
312 {
313 if (tokenlen < (sizeof(token) - 1))
314 token[tokenlen++] = '@';
315 s = parse_mailboxdomain(s + 1, ROUTE_SPECIAL_MASK, token, &tokenlen,
316 sizeof(token) - 1, comment, commentlen, commentmax);
317 }
318 if (!s || (*s != ':'))
319 {
320 return NULL; /* invalid route */
321 }
322
323 if (tokenlen < (sizeof(token) - 1))
324 token[tokenlen++] = ':';
325 s++;
326 }
327
328 s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
329 commentmax, addr);
330 if (!s)
331 return NULL;
332
333 if (*s != '>')
334 {
335 return NULL;
336 }
337
338 if (!addr->mailbox)
339 {
340 addr->mailbox = buf_new("@");
341 }
342
343 s++;
344 return s;
345}
346
356static const char *parse_addr_spec(const char *s, char *comment, size_t *commentlen,
357 size_t commentmax, struct Address *addr)
358{
359 char token[1024] = { 0 };
360 size_t tokenlen = 0;
361
362 s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
363 commentmax, addr);
364 if (s && *s && (*s != ',') && (*s != ';'))
365 {
366 return NULL;
367 }
368 return s;
369}
370
380static bool add_addrspec(struct AddressList *al, const char *phrase,
381 char *comment, size_t *commentlen, size_t commentmax)
382{
383 struct Address *cur = mutt_addr_new();
384
385 if (!parse_addr_spec(phrase, comment, commentlen, commentmax, cur))
386 {
387 mutt_addr_free(&cur);
388 return false;
389 }
390
391 mutt_addrlist_append(al, cur);
392 return true;
393}
394
402{
403 return MUTT_MEM_CALLOC(1, struct Address);
404}
405
414struct Address *mutt_addr_create(const char *personal, const char *mailbox)
415{
416 struct Address *a = mutt_addr_new();
417 if (personal)
418 {
420 }
421 if (mailbox)
422 {
423 a->mailbox = buf_new(mailbox);
424 }
425 return a;
426}
427
435int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
436{
437 if (!al)
438 return -1;
439
440 if (!mailbox)
441 return 0;
442
443 int rc = -1;
444 struct Address *a = NULL;
445 struct Address *tmp = NULL;
446 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
447 {
449 {
450 TAILQ_REMOVE(al, a, entries);
451 mutt_addr_free(&a);
452 rc = 0;
453 }
454 }
455
456 return rc;
457}
458
463void mutt_addr_free(struct Address **ptr)
464{
465 if (!ptr || !*ptr)
466 return;
467
468 struct Address *a = *ptr;
469
470 buf_free(&a->personal);
471 buf_free(&a->mailbox);
472 FREE(ptr);
473}
474
481int mutt_addrlist_parse(struct AddressList *al, const char *s)
482{
483 if (!s)
484 return 0;
485
486 int parsed = 0;
487 char comment[1024] = { 0 };
488 char phrase[1024] = { 0 };
489 size_t phraselen = 0;
490 size_t commentlen = 0;
491
492 bool ws_pending = mutt_str_is_email_wsp(*s);
493
494 /* Parse the address string character by character, handling RFC 2822
495 * constructs: quoted strings, comments, route-addr, group syntax */
497 while (*s)
498 {
499 switch (*s)
500 {
501 case ';':
502 case ',':
503 /* Address separator: convert accumulated phrase into an address */
504 if (phraselen != 0)
505 {
506 terminate_buffer(phrase, phraselen);
507 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
508 {
509 parsed++;
510 }
511 }
512 else if (commentlen != 0)
513 {
514 struct Address *last = TAILQ_LAST(al, AddressList);
515 if (last && !last->personal && !buf_is_empty(last->mailbox))
516 {
517 terminate_buffer(comment, commentlen);
518 last->personal = buf_new(comment);
519 }
520 }
521
522 if (*s == ';')
523 {
524 /* add group terminator */
526 }
527
528 phraselen = 0;
529 commentlen = 0;
530 s++;
531 break;
532
533 case '(':
534 /* RFC 822 comment in parentheses */
535 if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
536 comment[commentlen++] = ' ';
537 s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
538 if (!s)
539 {
541 return 0;
542 }
543 break;
544
545 case '"':
546 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
547 phrase[phraselen++] = ' ';
548 s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
549 if (!s)
550 {
552 return 0;
553 }
554 break;
555
556 case ':':
557 {
558 struct Address *a = mutt_addr_new();
559 terminate_buffer(phrase, phraselen);
560 if (phraselen != 0)
561 {
562 a->mailbox = buf_new(phrase);
563 }
564 a->group = true;
566 phraselen = 0;
567 commentlen = 0;
568 s++;
569 break;
570 }
571
572 case '<':
573 {
574 /* Route-addr: phrase before '<' becomes personal name */
575 struct Address *a = mutt_addr_new();
576 terminate_buffer(phrase, phraselen);
577 if (phraselen != 0)
578 {
579 a->personal = buf_new(phrase);
580 }
581 s = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, a);
582 if (!s)
583 {
585 mutt_addr_free(&a);
586 return 0;
587 }
589 phraselen = 0;
590 commentlen = 0;
591 parsed++;
592 break;
593 }
594
595 default:
596 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)) && ws_pending)
597 phrase[phraselen++] = ' ';
598 if (*s == '\\')
599 {
600 s++;
601 if (*s && (phraselen < (sizeof(phrase) - 1)))
602 {
603 phrase[phraselen++] = *s;
604 s++;
605 }
606 }
607 s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
608 if (!s)
609 {
611 return 0;
612 }
613 break;
614 } // switch (*s)
615
616 ws_pending = mutt_str_is_email_wsp(*s);
618 } // while (*s)
619
620 if (phraselen != 0)
621 {
622 terminate_buffer(phrase, phraselen);
623 terminate_buffer(comment, commentlen);
624 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
625 {
626 parsed++;
627 }
628 }
629 else if (commentlen != 0)
630 {
631 struct Address *last = TAILQ_LAST(al, AddressList);
632 if (last && !last->personal && !buf_is_empty(last->mailbox))
633 {
634 terminate_buffer(comment, commentlen);
635 last->personal = buf_new(comment);
636 }
637 }
638
639 return parsed;
640}
641
651int mutt_addrlist_parse2(struct AddressList *al, const char *s)
652{
653 if (!s || (*s == '\0'))
654 return 0;
655
656 int parsed = 0;
657
658 /* check for a simple whitespace separated list of addresses */
659 if (!strpbrk(s, "\"<>():;,\\"))
660 {
661 char *copy = mutt_str_dup(s);
662 char *r = copy;
663 while ((r = strtok(r, " \t")))
664 {
665 parsed += mutt_addrlist_parse(al, r);
666 r = NULL;
667 }
668 FREE(&copy);
669 }
670 else
671 {
672 parsed = mutt_addrlist_parse(al, s);
673 }
674
675 return parsed;
676}
677
687void mutt_addrlist_qualify(struct AddressList *al, const char *host)
688{
689 if (!al || !host || (*host == '\0'))
690 return;
691
692 struct Address *a = NULL;
693 TAILQ_FOREACH(a, al, entries)
694 {
695 if (!a->group && a->mailbox && !buf_find_char(a->mailbox, '@'))
696 {
697 buf_add_printf(a->mailbox, "@%s", host);
698 }
699 }
700}
701
715void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
716{
717 if (!buf || !value || !specials)
718 return;
719
720 if (strpbrk(value, specials))
721 {
722 if (buflen < 4)
723 {
724 mutt_str_copy(buf, value, buflen);
725 return;
726 }
727
728 char *pc = buf;
729 size_t remaining = buflen - 3; // Reserve for opening quote, closing quote, NUL
730
731 *pc++ = '"';
732 for (; *value && (remaining > 1); value++)
733 {
734 if ((*value == '\\') || (*value == '"'))
735 {
736 *pc++ = '\\';
737 remaining--;
738 }
739 *pc++ = *value;
740 remaining--;
741 }
742 *pc++ = '"';
743 *pc = '\0';
744 }
745 else
746 {
747 mutt_str_copy(buf, value, buflen);
748 }
749}
750
756struct Address *mutt_addr_copy(const struct Address *addr)
757{
758 if (!addr)
759 return NULL;
760
761 struct Address *p = mutt_addr_new();
762 p->personal = buf_dup(addr->personal);
763 p->mailbox = buf_dup(addr->mailbox);
764 p->group = addr->group;
765 p->is_intl = addr->is_intl;
766 p->intl_checked = addr->intl_checked;
767 return p;
768}
769
776void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
777{
778 if (!dst || !src)
779 return;
780
781 struct Address *a = NULL;
782 TAILQ_FOREACH(a, src, entries)
783 {
784 struct Address *next = TAILQ_NEXT(a, entries);
785 if (prune && a->group && (!next || !next->mailbox))
786 {
787 /* ignore this element of the list */
788 }
789 else
790 {
792 }
793 }
794}
795
803bool mutt_addr_valid_msgid(const char *msgid)
804{
805 /* msg-id = "<" addr-spec ">"
806 * addr-spec = local-part "@" domain
807 * local-part = word *("." word)
808 * word = atom / quoted-string
809 * atom = 1*<any CHAR except specials, SPACE and CTLs>
810 * CHAR = ( 0.-127. )
811 * specials = "(" / ")" / "<" / ">" / "@"
812 * / "," / ";" / ":" / "\" / <">
813 * / "." / "[" / "]"
814 * SPACE = ( 32. )
815 * CTLS = ( 0.-31., 127.)
816 * quoted-string = <"> *(qtext/quoted-pair) <">
817 * qtext = <any CHAR except <">, "\" and CR>
818 * CR = ( 13. )
819 * quoted-pair = "\" CHAR
820 * domain = sub-domain *("." sub-domain)
821 * sub-domain = domain-ref / domain-literal
822 * domain-ref = atom
823 * domain-literal = "[" *(dtext / quoted-pair) "]"
824 */
825
826 if (!msgid || (*msgid == '\0'))
827 return false;
828
829 size_t l = mutt_str_len(msgid);
830 if (l < 5) /* <atom@atom> */
831 return false;
832 if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
833 return false;
834 if (!(strrchr(msgid, '@')))
835 return false;
836
837 /* TODO: complete parser */
838 for (size_t i = 0; i < l; i++)
839 if ((unsigned char) msgid[i] > 127)
840 return false;
841
842 return true;
843}
844
851bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
852{
853 if (!ala || !alb)
854 {
855 return !(ala || alb);
856 }
857
858 struct Address *ana = TAILQ_FIRST(ala);
859 struct Address *anb = TAILQ_FIRST(alb);
860
861 while (ana && anb)
862 {
863 if (!buf_str_equal(ana->mailbox, anb->mailbox) ||
864 !buf_str_equal(ana->personal, anb->personal))
865 {
866 break;
867 }
868
869 ana = TAILQ_NEXT(ana, entries);
870 anb = TAILQ_NEXT(anb, entries);
871 }
872
873 return !(ana || anb);
874}
875
883int mutt_addrlist_count_recips(const struct AddressList *al)
884{
885 if (!al)
886 return 0;
887
888 int c = 0;
889 struct Address *a = NULL;
890 TAILQ_FOREACH(a, al, entries)
891 {
892 c += (a->mailbox && !a->group);
893 }
894 return c;
895}
896
903bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
904{
905 if (!a || !b)
906 return false;
907 if (!a->mailbox || !b->mailbox)
908 return false;
909 if (!buf_istr_equal(a->mailbox, b->mailbox))
910 return false;
911 return true;
912}
913
920bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
921{
922 if (!needle || !haystack)
923 return false;
924
925 struct Address *a = NULL;
926 TAILQ_FOREACH(a, haystack, entries)
927 {
928 if (mutt_addr_cmp(needle, a))
929 return true;
930 }
931 return false;
932}
933
939static bool addr_is_intl(const struct Address *a)
940{
941 if (!a)
942 return false;
943 return a->intl_checked && a->is_intl;
944}
945
951static bool addr_is_local(const struct Address *a)
952{
953 if (!a)
954 return false;
955 return a->intl_checked && !a->is_intl;
956}
957
968static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
969{
970 if (!mbox || !user || !domain)
971 return -1;
972
973 const char *ptr = strchr(mbox, '@');
974
975 /* Fail if '@' is missing, at the start, or at the end */
976 if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
977 return -1;
978
979 *user = mutt_strn_dup(mbox, ptr - mbox);
980 *domain = mutt_str_dup(ptr + 1);
981
982 return 0;
983}
984
990static void addr_set_intl(struct Address *a, char *intl_mailbox)
991{
992 if (!a)
993 return;
994
995 buf_strcpy(a->mailbox, intl_mailbox);
996 a->intl_checked = true;
997 a->is_intl = true;
998}
999
1005static void addr_set_local(struct Address *a, char *local_mailbox)
1006{
1007 if (!a)
1008 return;
1009
1010 buf_strcpy(a->mailbox, local_mailbox);
1011 a->intl_checked = true;
1012 a->is_intl = false;
1013}
1014
1023const char *mutt_addr_for_display(const struct Address *a)
1024{
1025 if (!a)
1026 return NULL;
1027
1028 char *user = NULL;
1029 char *domain = NULL;
1030 static char *buf = NULL;
1031
1032 if (!a->mailbox || addr_is_local(a))
1033 return buf_string(a->mailbox);
1034
1035 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1036 return buf_string(a->mailbox);
1037
1038 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
1039
1040 FREE(&user);
1041 FREE(&domain);
1042
1043 if (!local_mailbox)
1044 return buf_string(a->mailbox);
1045
1046 mutt_str_replace(&buf, local_mailbox);
1047 FREE(&local_mailbox);
1048
1049 return buf;
1050}
1051
1062size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
1063{
1064 if (!buf || !addr || (!addr->personal && !addr->mailbox))
1065 {
1066 return 0;
1067 }
1068
1069 const size_t initial_len = buf_len(buf);
1070
1071 if (addr->personal)
1072 {
1073 if (strpbrk(buf_string(addr->personal), AddressSpecials) ||
1075 {
1076 buf_addch(buf, '"');
1077 for (const char *pc = buf_string(addr->personal); *pc; pc++)
1078 {
1079 if ((*pc == '"') || (*pc == '\\'))
1080 {
1081 buf_addch(buf, '\\');
1082 }
1083 buf_addch(buf, *pc);
1084 }
1085 buf_addch(buf, '"');
1086 }
1087 else
1088 {
1089 buf_addstr(buf, buf_string(addr->personal));
1090 }
1091
1092 buf_addch(buf, ' ');
1093 }
1094
1095 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1096 {
1097 buf_addch(buf, '<');
1098 }
1099
1100 if (addr->mailbox)
1101 {
1102 if (!mutt_str_equal(buf_string(addr->mailbox), "@"))
1103 {
1104 const char *a = display ? mutt_addr_for_display(addr) : buf_string(addr->mailbox);
1105 buf_addstr(buf, a);
1106 }
1107
1108 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1109 {
1110 buf_addch(buf, '>');
1111 }
1112
1113 if (addr->group)
1114 {
1115 buf_addstr(buf, ": ");
1116 }
1117 }
1118 else
1119 {
1120 buf_addch(buf, ';');
1121 }
1122
1123 return buf_len(buf) - initial_len;
1124}
1125
1139static size_t addrlist_write(const struct AddressList *al, struct Buffer *buf,
1140 bool display, const char *header, int cols)
1141{
1142 if (!buf || !al || TAILQ_EMPTY(al))
1143 return 0;
1144
1145 if (header)
1146 {
1147 buf_printf(buf, "%s: ", header);
1148 }
1149
1150 size_t cur_col = buf_len(buf);
1151 bool in_group = false;
1152 struct Address *a = NULL;
1153 TAILQ_FOREACH(a, al, entries)
1154 {
1155 struct Address *next = TAILQ_NEXT(a, entries);
1156
1157 if (a->group)
1158 {
1159 in_group = true;
1160 }
1161
1162 // wrap if needed
1163 const size_t cur_len = buf_len(buf);
1164 cur_col += mutt_addr_write(buf, a, display);
1165 if ((cols > 0) && (cur_col > cols) && (a != TAILQ_FIRST(al)))
1166 {
1167 buf_insert(buf, cur_len, "\n\t");
1168 cur_col = 8;
1169 }
1170
1171 if (!a->group)
1172 {
1173 // group terminator
1174 if (in_group && !a->mailbox && !a->personal)
1175 {
1176 buf_addch(buf, ';');
1177 cur_col++;
1178 in_group = false;
1179 }
1180 if (next && (next->mailbox || next->personal))
1181 {
1182 buf_addstr(buf, ", ");
1183 cur_col += 2;
1184 }
1185 if (!next)
1186 {
1187 break;
1188 }
1189 }
1190 }
1191
1192 return buf_len(buf);
1193}
1194
1202size_t mutt_addrlist_write_wrap(const struct AddressList *al,
1203 struct Buffer *buf, const char *header)
1204{
1205 return addrlist_write(al, buf, false, header, 74);
1206}
1207
1219size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
1220{
1221 return addrlist_write(al, buf, display, NULL, -1);
1222}
1223
1230size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1231{
1232 if (!al || !list)
1233 return 0;
1234
1235 size_t count = 0;
1236 struct Address *a = NULL;
1237 TAILQ_FOREACH(a, al, entries)
1238 {
1239 struct Buffer buf = { 0 };
1240 mutt_addr_write(&buf, a, true);
1241 if (!buf_is_empty(&buf))
1242 {
1243 /* We're taking the ownership of the buffer string here */
1244 mutt_list_insert_tail(list, (char *) buf_string(&buf));
1245 count++;
1246 }
1247 }
1248
1249 return count;
1250}
1251
1261void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
1262{
1263 struct Buffer *buf = buf_pool_get();
1264 mutt_addrlist_write_wrap(al, buf, header);
1265 fputs(buf_string(buf), fp);
1266 buf_pool_release(&buf);
1267 fputc('\n', fp);
1268}
1269
1277{
1278 if (!a || !a->mailbox || addr_is_intl(a))
1279 return true;
1280
1281 char *user = NULL;
1282 char *domain = NULL;
1283 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1284 return true;
1285
1286 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1287
1288 FREE(&user);
1289 FREE(&domain);
1290
1291 if (!intl_mailbox)
1292 return false;
1293
1294 addr_set_intl(a, intl_mailbox);
1295 FREE(&intl_mailbox);
1296 return true;
1297}
1298
1306int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1307{
1308 if (!al)
1309 return 0;
1310
1311 int rc = 0;
1312
1313 if (err)
1314 *err = NULL;
1315
1316 struct Address *a = NULL;
1317 TAILQ_FOREACH(a, al, entries)
1318 {
1319 if (!a->mailbox || addr_is_intl(a))
1320 continue;
1321
1322 char *user = NULL;
1323 char *domain = NULL;
1324 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1325 continue;
1326
1327 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1328
1329 FREE(&user);
1330 FREE(&domain);
1331
1332 if (!intl_mailbox)
1333 {
1334 rc = -1;
1335 if (err && !*err)
1336 *err = buf_strdup(a->mailbox);
1337 continue;
1338 }
1339
1340 addr_set_intl(a, intl_mailbox);
1341 FREE(&intl_mailbox);
1342 }
1343
1344 return rc;
1345}
1346
1354{
1355 if (!a || !a->mailbox)
1356 {
1357 return false;
1358 }
1359
1360 if (addr_is_local(a))
1361 {
1362 return true;
1363 }
1364
1365 char *user = NULL;
1366 char *domain = NULL;
1367 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1368 {
1369 return false;
1370 }
1371
1372 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_NONE);
1373 FREE(&user);
1374 FREE(&domain);
1375
1376 if (!local_mailbox)
1377 {
1378 return false;
1379 }
1380
1381 addr_set_local(a, local_mailbox);
1382 FREE(&local_mailbox);
1383 return true;
1384}
1385
1391int mutt_addrlist_to_local(struct AddressList *al)
1392{
1393 if (!al)
1394 return 0;
1395
1396 struct Address *a = NULL;
1397 TAILQ_FOREACH(a, al, entries)
1398 {
1400 }
1401 return 0;
1402}
1403
1410void mutt_addrlist_dedupe(struct AddressList *al)
1411{
1412 if (!al)
1413 return;
1414
1415 struct Address *a = NULL;
1416 TAILQ_FOREACH(a, al, entries)
1417 {
1418 if (a->mailbox)
1419 {
1420 struct Address *a2 = TAILQ_NEXT(a, entries);
1421 struct Address *tmp = NULL;
1422
1423 if (a2)
1424 {
1425 TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1426 {
1427 if (a2->mailbox && buf_istr_equal(a->mailbox, a2->mailbox))
1428 {
1429 mutt_debug(LL_DEBUG2, "Removing %s\n", buf_string(a2->mailbox));
1430 TAILQ_REMOVE(al, a2, entries);
1431 mutt_addr_free(&a2);
1432 }
1433 }
1434 }
1435 }
1436 }
1437}
1438
1446void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1447{
1448 if (!a || !b)
1449 return;
1450
1451 struct Address *aa = NULL;
1452 struct Address *ab = NULL;
1453 struct Address *tmp = NULL;
1454
1455 TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1456 {
1457 TAILQ_FOREACH(aa, a, entries)
1458 {
1459 if (mutt_addr_cmp(aa, ab))
1460 {
1461 TAILQ_REMOVE(b, ab, entries);
1462 mutt_addr_free(&ab);
1463 break;
1464 }
1465 }
1466 }
1467}
1468
1475void mutt_addrlist_clear(struct AddressList *al)
1476{
1477 if (!al)
1478 return;
1479
1480 struct Address *a = TAILQ_FIRST(al);
1481 struct Address *next = NULL;
1482 while (a)
1483 {
1484 next = TAILQ_NEXT(a, entries);
1485 mutt_addr_free(&a);
1486 a = next;
1487 }
1488 TAILQ_INIT(al);
1489}
1490
1496void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1497{
1498 if (al && a)
1499 TAILQ_INSERT_TAIL(al, a, entries);
1500}
1501
1507void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1508{
1509 if (al && a)
1510 TAILQ_INSERT_HEAD(al, a, entries);
1511}
1512
1518bool mutt_addr_uses_unicode(const char *str)
1519{
1520 if (!str)
1521 return false;
1522
1523 while (*str)
1524 {
1525 if ((unsigned char) *str & (1 << 7))
1526 return true;
1527 str++;
1528 }
1529
1530 return false;
1531}
1532
1538bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1539{
1540 if (!al)
1541 {
1542 return false;
1543 }
1544
1545 struct Address *a = NULL;
1546 TAILQ_FOREACH(a, al, entries)
1547 {
1549 return true;
1550 }
1551 return false;
1552}
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition address.c:939
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition address.c:414
static bool add_addrspec(struct AddressList *al, const char *phrase, char *comment, size_t *commentlen, size_t commentmax)
Parse an email address and add an Address to a list.
Definition address.c:380
static const char * parse_addr_spec(const char *s, char *comment, size_t *commentlen, size_t commentmax, struct Address *addr)
Parse an email address.
Definition address.c:356
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition address.c:776
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition address.c:687
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition address.c:968
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition address.c:951
static const char * parse_address(const char *s, char *token, size_t *tokenlen, size_t tokenmax, char *comment, size_t *commentlen, size_t commentmax, struct Address *addr)
Extract an email address.
Definition address.c:260
bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
Compare two Address lists for equality.
Definition address.c:851
static const char * parse_route_addr(const char *s, char *comment, size_t *commentlen, size_t commentmax, struct Address *addr)
Parse an email addresses.
Definition address.c:300
static const char * parse_mailboxdomain(const char *s, uint64_t special_mask, char *mailbox, size_t *mailboxlen, size_t mailboxmax, char *comment, size_t *commentlen, size_t commentmax)
Extract part of an email address (and a comment)
Definition address.c:213
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition address.c:1475
static const char * next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
Find the next word, skipping quoted and parenthesised text.
Definition address.c:166
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition address.c:1230
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition address.c:463
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition address.c:803
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition address.c:990
size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition address.c:1062
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition address.c:1005
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition address.c:1538
void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
Copy a string and wrap it in quotes if it contains special characters.
Definition address.c:715
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition address.c:903
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition address.c:1496
size_t mutt_addrlist_write_wrap(const struct AddressList *al, struct Buffer *buf, const char *header)
Write an AddressList to a buffer, perform line wrapping.
Definition address.c:1202
struct Address * mutt_addr_new(void)
Create a new Address.
Definition address.c:401
static size_t addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display, const char *header, int cols)
Write an AddressList to a buffer, optionally perform line wrapping and display conversion.
Definition address.c:1139
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition address.c:1391
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1219
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition address.c:1518
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:651
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition address.c:756
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition address.c:45
#define USER_SPECIAL_MASK
Mask for AddressSpecials except " ( . \ characters.
Definition address.c:74
#define ROUTE_SPECIAL_MASK
Mask for AddressSpecials except ( , . [ \ ] characters.
Definition address.c:80
bool mutt_addr_to_local(struct Address *a)
Convert an Address from Punycode.
Definition address.c:1353
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition address.c:1446
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition address.c:883
#define ADDRESS_SPECIAL_MASK
Mask for AddressSpecials, for is_special()
Definition address.c:71
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:481
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition address.c:1507
static const char * parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
Extract a comment (parenthesised string)
Definition address.c:91
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
Wrapper for mutt_write_address()
Definition address.c:1261
#define DOMAIN_SPECIAL_MASK
Mask for AddressSpecials except ( . [ \ ] characters.
Definition address.c:77
#define is_special(ch, mask)
Is this character special to an email address?
Definition address.c:67
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition address.c:1306
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition address.c:1276
int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
Remove an Address from a list.
Definition address.c:435
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition address.c:920
static const char * parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
Extract a quoted string.
Definition address.c:134
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition address.c:1023
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition address.c:1410
Representation of an email address.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:168
bool buf_istr_equal(const struct Buffer *a, const struct Buffer *b)
Return if two buffers are equal, case insensitive.
Definition buffer.c:701
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
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:674
void buf_free(struct Buffer **ptr)
Deallocates a buffer.
Definition buffer.c:326
struct Buffer * buf_new(const char *str)
Allocate a new Buffer.
Definition buffer.c:311
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
bool buf_str_equal(const struct Buffer *a, const struct Buffer *b)
Return if two buffers are equal.
Definition buffer.c:689
struct Buffer * buf_dup(const struct Buffer *buf)
Copy a Buffer into a new allocated buffer.
Definition buffer.c:592
const char * buf_find_char(const struct Buffer *buf, const char c)
Return a pointer to a char found in the buffer.
Definition buffer.c:659
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
size_t buf_insert(struct Buffer *buf, size_t offset, const char *s)
Add a string in the middle of a buffer.
Definition buffer.c:263
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
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
Handling of international domain names.
#define MI_NONE
Definition idna2.h:29
#define MI_MAY_BE_IRREVERSIBLE
Definition idna2.h:30
char * mutt_idna_local_to_intl(const char *user, const char *domain)
Convert an email's domain to Punycode.
Definition idna.c:228
char * mutt_idna_intl_to_local(const char *user, const char *domain, uint8_t flags)
Convert an email's domain from Punycode.
Definition idna.c:117
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Convenience wrapper for the library headers.
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:614
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
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
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define TAILQ_INIT(head)
Definition queue.h:822
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:866
#define TAILQ_FIRST(head)
Definition queue.h:780
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)
Definition queue.h:797
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
#define TAILQ_NEXT(elm, field)
Definition queue.h:889
#define TAILQ_EMPTY(head)
Definition queue.h:778
#define TAILQ_LAST(head, headname)
Definition queue.h:876
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition queue.h:853
#define terminate_string(str, strlen, buflen)
Definition string2.h:56
#define terminate_buffer(str, strlen)
Definition string2.h:59
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition string2.h:111
An email address.
Definition address.h:35
struct Buffer * personal
Real name of address.
Definition address.h:36
bool group
Group mailbox?
Definition address.h:38
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
bool intl_checked
Checked for IDN?
Definition address.h:40
bool is_intl
International Domain Name.
Definition address.h:39
String manipulation buffer.
Definition buffer.h:36