NeoMutt  2025-12-11-694-ga89709
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, *tmp = NULL;
445 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
446 {
448 {
449 TAILQ_REMOVE(al, a, entries);
450 mutt_addr_free(&a);
451 rc = 0;
452 }
453 }
454
455 return rc;
456}
457
462void mutt_addr_free(struct Address **ptr)
463{
464 if (!ptr || !*ptr)
465 return;
466
467 struct Address *a = *ptr;
468
469 buf_free(&a->personal);
470 buf_free(&a->mailbox);
471 FREE(ptr);
472}
473
480int mutt_addrlist_parse(struct AddressList *al, const char *s)
481{
482 if (!s)
483 return 0;
484
485 int parsed = 0;
486 char comment[1024] = { 0 };
487 char phrase[1024] = { 0 };
488 size_t phraselen = 0, commentlen = 0;
489
490 bool ws_pending = mutt_str_is_email_wsp(*s);
491
492 /* Parse the address string character by character, handling RFC 2822
493 * constructs: quoted strings, comments, route-addr, group syntax */
495 while (*s)
496 {
497 switch (*s)
498 {
499 case ';':
500 case ',':
501 /* Address separator: convert accumulated phrase into an address */
502 if (phraselen != 0)
503 {
504 terminate_buffer(phrase, phraselen);
505 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
506 {
507 parsed++;
508 }
509 }
510 else if (commentlen != 0)
511 {
512 struct Address *last = TAILQ_LAST(al, AddressList);
513 if (last && !last->personal && !buf_is_empty(last->mailbox))
514 {
515 terminate_buffer(comment, commentlen);
516 last->personal = buf_new(comment);
517 }
518 }
519
520 if (*s == ';')
521 {
522 /* add group terminator */
524 }
525
526 phraselen = 0;
527 commentlen = 0;
528 s++;
529 break;
530
531 case '(':
532 /* RFC 822 comment in parentheses */
533 if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
534 comment[commentlen++] = ' ';
535 s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
536 if (!s)
537 {
539 return 0;
540 }
541 break;
542
543 case '"':
544 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
545 phrase[phraselen++] = ' ';
546 s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
547 if (!s)
548 {
550 return 0;
551 }
552 break;
553
554 case ':':
555 {
556 struct Address *a = mutt_addr_new();
557 terminate_buffer(phrase, phraselen);
558 if (phraselen != 0)
559 {
560 a->mailbox = buf_new(phrase);
561 }
562 a->group = true;
564 phraselen = 0;
565 commentlen = 0;
566 s++;
567 break;
568 }
569
570 case '<':
571 {
572 /* Route-addr: phrase before '<' becomes personal name */
573 struct Address *a = mutt_addr_new();
574 terminate_buffer(phrase, phraselen);
575 if (phraselen != 0)
576 {
577 a->personal = buf_new(phrase);
578 }
579 s = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, a);
580 if (!s)
581 {
583 mutt_addr_free(&a);
584 return 0;
585 }
587 phraselen = 0;
588 commentlen = 0;
589 parsed++;
590 break;
591 }
592
593 default:
594 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)) && ws_pending)
595 phrase[phraselen++] = ' ';
596 if (*s == '\\')
597 {
598 s++;
599 if (*s && (phraselen < (sizeof(phrase) - 1)))
600 {
601 phrase[phraselen++] = *s;
602 s++;
603 }
604 }
605 s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
606 if (!s)
607 {
609 return 0;
610 }
611 break;
612 } // switch (*s)
613
614 ws_pending = mutt_str_is_email_wsp(*s);
616 } // while (*s)
617
618 if (phraselen != 0)
619 {
620 terminate_buffer(phrase, phraselen);
621 terminate_buffer(comment, commentlen);
622 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
623 {
624 parsed++;
625 }
626 }
627 else if (commentlen != 0)
628 {
629 struct Address *last = TAILQ_LAST(al, AddressList);
630 if (last && !last->personal && !buf_is_empty(last->mailbox))
631 {
632 terminate_buffer(comment, commentlen);
633 last->personal = buf_new(comment);
634 }
635 }
636
637 return parsed;
638}
639
649int mutt_addrlist_parse2(struct AddressList *al, const char *s)
650{
651 if (!s || (*s == '\0'))
652 return 0;
653
654 int parsed = 0;
655
656 /* check for a simple whitespace separated list of addresses */
657 if (!strpbrk(s, "\"<>():;,\\"))
658 {
659 char *copy = mutt_str_dup(s);
660 char *r = copy;
661 while ((r = strtok(r, " \t")))
662 {
663 parsed += mutt_addrlist_parse(al, r);
664 r = NULL;
665 }
666 FREE(&copy);
667 }
668 else
669 {
670 parsed = mutt_addrlist_parse(al, s);
671 }
672
673 return parsed;
674}
675
685void mutt_addrlist_qualify(struct AddressList *al, const char *host)
686{
687 if (!al || !host || (*host == '\0'))
688 return;
689
690 struct Address *a = NULL;
691 TAILQ_FOREACH(a, al, entries)
692 {
693 if (!a->group && a->mailbox && !buf_find_char(a->mailbox, '@'))
694 {
695 buf_add_printf(a->mailbox, "@%s", host);
696 }
697 }
698}
699
713void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
714{
715 if (!buf || !value || !specials)
716 return;
717
718 if (strpbrk(value, specials))
719 {
720 if (buflen < 4)
721 {
722 mutt_str_copy(buf, value, buflen);
723 return;
724 }
725
726 char *pc = buf;
727 size_t remaining = buflen - 3; // Reserve for opening quote, closing quote, NUL
728
729 *pc++ = '"';
730 for (; *value && (remaining > 1); value++)
731 {
732 if ((*value == '\\') || (*value == '"'))
733 {
734 *pc++ = '\\';
735 remaining--;
736 }
737 *pc++ = *value;
738 remaining--;
739 }
740 *pc++ = '"';
741 *pc = '\0';
742 }
743 else
744 {
745 mutt_str_copy(buf, value, buflen);
746 }
747}
748
754struct Address *mutt_addr_copy(const struct Address *addr)
755{
756 if (!addr)
757 return NULL;
758
759 struct Address *p = mutt_addr_new();
760 p->personal = buf_dup(addr->personal);
761 p->mailbox = buf_dup(addr->mailbox);
762 p->group = addr->group;
763 p->is_intl = addr->is_intl;
764 p->intl_checked = addr->intl_checked;
765 return p;
766}
767
774void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
775{
776 if (!dst || !src)
777 return;
778
779 struct Address *a = NULL;
780 TAILQ_FOREACH(a, src, entries)
781 {
782 struct Address *next = TAILQ_NEXT(a, entries);
783 if (prune && a->group && (!next || !next->mailbox))
784 {
785 /* ignore this element of the list */
786 }
787 else
788 {
790 }
791 }
792}
793
801bool mutt_addr_valid_msgid(const char *msgid)
802{
803 /* msg-id = "<" addr-spec ">"
804 * addr-spec = local-part "@" domain
805 * local-part = word *("." word)
806 * word = atom / quoted-string
807 * atom = 1*<any CHAR except specials, SPACE and CTLs>
808 * CHAR = ( 0.-127. )
809 * specials = "(" / ")" / "<" / ">" / "@"
810 * / "," / ";" / ":" / "\" / <">
811 * / "." / "[" / "]"
812 * SPACE = ( 32. )
813 * CTLS = ( 0.-31., 127.)
814 * quoted-string = <"> *(qtext/quoted-pair) <">
815 * qtext = <any CHAR except <">, "\" and CR>
816 * CR = ( 13. )
817 * quoted-pair = "\" CHAR
818 * domain = sub-domain *("." sub-domain)
819 * sub-domain = domain-ref / domain-literal
820 * domain-ref = atom
821 * domain-literal = "[" *(dtext / quoted-pair) "]"
822 */
823
824 if (!msgid || (*msgid == '\0'))
825 return false;
826
827 size_t l = mutt_str_len(msgid);
828 if (l < 5) /* <atom@atom> */
829 return false;
830 if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
831 return false;
832 if (!(strrchr(msgid, '@')))
833 return false;
834
835 /* TODO: complete parser */
836 for (size_t i = 0; i < l; i++)
837 if ((unsigned char) msgid[i] > 127)
838 return false;
839
840 return true;
841}
842
849bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
850{
851 if (!ala || !alb)
852 {
853 return !(ala || alb);
854 }
855
856 struct Address *ana = TAILQ_FIRST(ala);
857 struct Address *anb = TAILQ_FIRST(alb);
858
859 while (ana && anb)
860 {
861 if (!buf_str_equal(ana->mailbox, anb->mailbox) ||
862 !buf_str_equal(ana->personal, anb->personal))
863 {
864 break;
865 }
866
867 ana = TAILQ_NEXT(ana, entries);
868 anb = TAILQ_NEXT(anb, entries);
869 }
870
871 return !(ana || anb);
872}
873
881int mutt_addrlist_count_recips(const struct AddressList *al)
882{
883 if (!al)
884 return 0;
885
886 int c = 0;
887 struct Address *a = NULL;
888 TAILQ_FOREACH(a, al, entries)
889 {
890 c += (a->mailbox && !a->group);
891 }
892 return c;
893}
894
901bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
902{
903 if (!a || !b)
904 return false;
905 if (!a->mailbox || !b->mailbox)
906 return false;
907 if (!buf_istr_equal(a->mailbox, b->mailbox))
908 return false;
909 return true;
910}
911
918bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
919{
920 if (!needle || !haystack)
921 return false;
922
923 struct Address *a = NULL;
924 TAILQ_FOREACH(a, haystack, entries)
925 {
926 if (mutt_addr_cmp(needle, a))
927 return true;
928 }
929 return false;
930}
931
937static bool addr_is_intl(const struct Address *a)
938{
939 if (!a)
940 return false;
941 return a->intl_checked && a->is_intl;
942}
943
949static bool addr_is_local(const struct Address *a)
950{
951 if (!a)
952 return false;
953 return a->intl_checked && !a->is_intl;
954}
955
966static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
967{
968 if (!mbox || !user || !domain)
969 return -1;
970
971 char *ptr = strchr(mbox, '@');
972
973 /* Fail if '@' is missing, at the start, or at the end */
974 if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
975 return -1;
976
977 *user = mutt_strn_dup(mbox, ptr - mbox);
978 *domain = mutt_str_dup(ptr + 1);
979
980 return 0;
981}
982
988static void addr_set_intl(struct Address *a, char *intl_mailbox)
989{
990 if (!a)
991 return;
992
993 buf_strcpy(a->mailbox, intl_mailbox);
994 a->intl_checked = true;
995 a->is_intl = true;
996}
997
1003static void addr_set_local(struct Address *a, char *local_mailbox)
1004{
1005 if (!a)
1006 return;
1007
1008 buf_strcpy(a->mailbox, local_mailbox);
1009 a->intl_checked = true;
1010 a->is_intl = false;
1011}
1012
1021const char *mutt_addr_for_display(const struct Address *a)
1022{
1023 if (!a)
1024 return NULL;
1025
1026 char *user = NULL, *domain = NULL;
1027 static char *buf = NULL;
1028
1029 if (!a->mailbox || addr_is_local(a))
1030 return buf_string(a->mailbox);
1031
1032 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1033 return buf_string(a->mailbox);
1034
1035 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
1036
1037 FREE(&user);
1038 FREE(&domain);
1039
1040 if (!local_mailbox)
1041 return buf_string(a->mailbox);
1042
1043 mutt_str_replace(&buf, local_mailbox);
1044 FREE(&local_mailbox);
1045
1046 return buf;
1047}
1048
1059size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
1060{
1061 if (!buf || !addr || (!addr->personal && !addr->mailbox))
1062 {
1063 return 0;
1064 }
1065
1066 const size_t initial_len = buf_len(buf);
1067
1068 if (addr->personal)
1069 {
1070 if (strpbrk(buf_string(addr->personal), AddressSpecials))
1071 {
1072 buf_addch(buf, '"');
1073 for (const char *pc = buf_string(addr->personal); *pc; pc++)
1074 {
1075 if ((*pc == '"') || (*pc == '\\'))
1076 {
1077 buf_addch(buf, '\\');
1078 }
1079 buf_addch(buf, *pc);
1080 }
1081 buf_addch(buf, '"');
1082 }
1083 else
1084 {
1085 buf_addstr(buf, buf_string(addr->personal));
1086 }
1087
1088 buf_addch(buf, ' ');
1089 }
1090
1091 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1092 {
1093 buf_addch(buf, '<');
1094 }
1095
1096 if (addr->mailbox)
1097 {
1098 if (!mutt_str_equal(buf_string(addr->mailbox), "@"))
1099 {
1100 const char *a = display ? mutt_addr_for_display(addr) : buf_string(addr->mailbox);
1101 buf_addstr(buf, a);
1102 }
1103
1104 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1105 {
1106 buf_addch(buf, '>');
1107 }
1108
1109 if (addr->group)
1110 {
1111 buf_addstr(buf, ": ");
1112 }
1113 }
1114 else
1115 {
1116 buf_addch(buf, ';');
1117 }
1118
1119 return buf_len(buf) - initial_len;
1120}
1121
1135static size_t addrlist_write(const struct AddressList *al, struct Buffer *buf,
1136 bool display, const char *header, int cols)
1137{
1138 if (!buf || !al || TAILQ_EMPTY(al))
1139 return 0;
1140
1141 if (header)
1142 {
1143 buf_printf(buf, "%s: ", header);
1144 }
1145
1146 size_t cur_col = buf_len(buf);
1147 bool in_group = false;
1148 struct Address *a = NULL;
1149 TAILQ_FOREACH(a, al, entries)
1150 {
1151 struct Address *next = TAILQ_NEXT(a, entries);
1152
1153 if (a->group)
1154 {
1155 in_group = true;
1156 }
1157
1158 // wrap if needed
1159 const size_t cur_len = buf_len(buf);
1160 cur_col += mutt_addr_write(buf, a, display);
1161 if ((cols > 0) && (cur_col > cols) && (a != TAILQ_FIRST(al)))
1162 {
1163 buf_insert(buf, cur_len, "\n\t");
1164 cur_col = 8;
1165 }
1166
1167 if (!a->group)
1168 {
1169 // group terminator
1170 if (in_group && !a->mailbox && !a->personal)
1171 {
1172 buf_addch(buf, ';');
1173 cur_col++;
1174 in_group = false;
1175 }
1176 if (next && (next->mailbox || next->personal))
1177 {
1178 buf_addstr(buf, ", ");
1179 cur_col += 2;
1180 }
1181 if (!next)
1182 {
1183 break;
1184 }
1185 }
1186 }
1187
1188 return buf_len(buf);
1189}
1190
1198size_t mutt_addrlist_write_wrap(const struct AddressList *al,
1199 struct Buffer *buf, const char *header)
1200{
1201 return addrlist_write(al, buf, false, header, 74);
1202}
1203
1215size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
1216{
1217 return addrlist_write(al, buf, display, NULL, -1);
1218}
1219
1226size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1227{
1228 if (!al || !list)
1229 return 0;
1230
1231 size_t count = 0;
1232 struct Address *a = NULL;
1233 TAILQ_FOREACH(a, al, entries)
1234 {
1235 struct Buffer buf = { 0 };
1236 mutt_addr_write(&buf, a, true);
1237 if (!buf_is_empty(&buf))
1238 {
1239 /* We're taking the ownership of the buffer string here */
1240 mutt_list_insert_tail(list, (char *) buf_string(&buf));
1241 count++;
1242 }
1243 }
1244
1245 return count;
1246}
1247
1257void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
1258{
1259 struct Buffer *buf = buf_pool_get();
1260 mutt_addrlist_write_wrap(al, buf, header);
1261 fputs(buf_string(buf), fp);
1262 buf_pool_release(&buf);
1263 fputc('\n', fp);
1264}
1265
1273{
1274 if (!a || !a->mailbox || addr_is_intl(a))
1275 return true;
1276
1277 char *user = NULL;
1278 char *domain = NULL;
1279 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1280 return true;
1281
1282 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1283
1284 FREE(&user);
1285 FREE(&domain);
1286
1287 if (!intl_mailbox)
1288 return false;
1289
1290 addr_set_intl(a, intl_mailbox);
1291 FREE(&intl_mailbox);
1292 return true;
1293}
1294
1302int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1303{
1304 if (!al)
1305 return 0;
1306
1307 int rc = 0;
1308
1309 if (err)
1310 *err = NULL;
1311
1312 struct Address *a = NULL;
1313 TAILQ_FOREACH(a, al, entries)
1314 {
1315 if (!a->mailbox || addr_is_intl(a))
1316 continue;
1317
1318 char *user = NULL;
1319 char *domain = NULL;
1320 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1321 continue;
1322
1323 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1324
1325 FREE(&user);
1326 FREE(&domain);
1327
1328 if (!intl_mailbox)
1329 {
1330 rc = -1;
1331 if (err && !*err)
1332 *err = buf_strdup(a->mailbox);
1333 continue;
1334 }
1335
1336 addr_set_intl(a, intl_mailbox);
1337 FREE(&intl_mailbox);
1338 }
1339
1340 return rc;
1341}
1342
1350{
1351 if (!a || !a->mailbox)
1352 {
1353 return false;
1354 }
1355
1356 if (addr_is_local(a))
1357 {
1358 return true;
1359 }
1360
1361 char *user = NULL;
1362 char *domain = NULL;
1363 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1364 {
1365 return false;
1366 }
1367
1368 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_NO_FLAGS);
1369 FREE(&user);
1370 FREE(&domain);
1371
1372 if (!local_mailbox)
1373 {
1374 return false;
1375 }
1376
1377 addr_set_local(a, local_mailbox);
1378 FREE(&local_mailbox);
1379 return true;
1380}
1381
1387int mutt_addrlist_to_local(struct AddressList *al)
1388{
1389 if (!al)
1390 return 0;
1391
1392 struct Address *a = NULL;
1393 TAILQ_FOREACH(a, al, entries)
1394 {
1396 }
1397 return 0;
1398}
1399
1406void mutt_addrlist_dedupe(struct AddressList *al)
1407{
1408 if (!al)
1409 return;
1410
1411 struct Address *a = NULL;
1412 TAILQ_FOREACH(a, al, entries)
1413 {
1414 if (a->mailbox)
1415 {
1416 struct Address *a2 = TAILQ_NEXT(a, entries);
1417 struct Address *tmp = NULL;
1418
1419 if (a2)
1420 {
1421 TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1422 {
1423 if (a2->mailbox && buf_istr_equal(a->mailbox, a2->mailbox))
1424 {
1425 mutt_debug(LL_DEBUG2, "Removing %s\n", buf_string(a2->mailbox));
1426 TAILQ_REMOVE(al, a2, entries);
1427 mutt_addr_free(&a2);
1428 }
1429 }
1430 }
1431 }
1432 }
1433}
1434
1442void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1443{
1444 if (!a || !b)
1445 return;
1446
1447 struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1448
1449 TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1450 {
1451 TAILQ_FOREACH(aa, a, entries)
1452 {
1453 if (mutt_addr_cmp(aa, ab))
1454 {
1455 TAILQ_REMOVE(b, ab, entries);
1456 mutt_addr_free(&ab);
1457 break;
1458 }
1459 }
1460 }
1461}
1462
1469void mutt_addrlist_clear(struct AddressList *al)
1470{
1471 if (!al)
1472 return;
1473
1474 struct Address *a = TAILQ_FIRST(al), *next = NULL;
1475 while (a)
1476 {
1477 next = TAILQ_NEXT(a, entries);
1478 mutt_addr_free(&a);
1479 a = next;
1480 }
1481 TAILQ_INIT(al);
1482}
1483
1489void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1490{
1491 if (al && a)
1492 TAILQ_INSERT_TAIL(al, a, entries);
1493}
1494
1500void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1501{
1502 if (al && a)
1503 TAILQ_INSERT_HEAD(al, a, entries);
1504}
1505
1511bool mutt_addr_uses_unicode(const char *str)
1512{
1513 if (!str)
1514 return false;
1515
1516 while (*str)
1517 {
1518 if ((unsigned char) *str & (1 << 7))
1519 return true;
1520 str++;
1521 }
1522
1523 return false;
1524}
1525
1531bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1532{
1533 if (!al)
1534 {
1535 return false;
1536 }
1537
1538 struct Address *a = NULL;
1539 TAILQ_FOREACH(a, al, entries)
1540 {
1542 return true;
1543 }
1544 return false;
1545}
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition address.c:937
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:774
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition address.c:685
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition address.c:966
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition address.c:949
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:849
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:1469
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:1226
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition address.c:462
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition address.c:801
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition address.c:988
size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition address.c:1059
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition address.c:1003
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition address.c:1531
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:713
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition address.c:901
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition address.c:1489
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:1198
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:1135
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition address.c:1387
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition address.c:1215
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition address.c:1511
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition address.c:649
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition address.c:754
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:1349
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition address.c:1442
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition address.c:881
#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:480
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition address.c:1500
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:1257
#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:1302
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition address.c:1272
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:918
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:1021
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition address.c:1406
Representation of an email address.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
bool buf_istr_equal(const struct Buffer *a, const struct Buffer *b)
Return if two buffers are equal, case insensitive.
Definition buffer.c:695
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
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
void buf_free(struct Buffer **ptr)
Deallocates a buffer.
Definition buffer.c:319
struct Buffer * buf_new(const char *str)
Allocate a new Buffer.
Definition buffer.c:304
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
bool buf_str_equal(const struct Buffer *a, const struct Buffer *b)
Return if two buffers are equal.
Definition buffer.c:683
struct Buffer * buf_dup(const struct Buffer *buf)
Copy a Buffer into a new allocated buffer.
Definition buffer.c:586
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:653
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
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:256
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
Handling of international domain names.
#define MI_NO_FLAGS
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:227
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:677
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:613
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
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:586
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