NeoMutt  2024-10-02-37-gfa9146
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
address.c
Go to the documentation of this file.
1
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, sizeof(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
493 while (*s)
494 {
495 switch (*s)
496 {
497 case ';':
498 case ',':
499 if (phraselen != 0)
500 {
501 terminate_buffer(phrase, phraselen);
502 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
503 {
504 parsed++;
505 }
506 }
507 else if (commentlen != 0)
508 {
509 struct Address *last = TAILQ_LAST(al, AddressList);
510 if (last && !last->personal && !buf_is_empty(last->mailbox))
511 {
512 terminate_buffer(comment, commentlen);
513 last->personal = buf_new(comment);
514 }
515 }
516
517 if (*s == ';')
518 {
519 /* add group terminator */
521 }
522
523 phraselen = 0;
524 commentlen = 0;
525 s++;
526 break;
527
528 case '(':
529 if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
530 comment[commentlen++] = ' ';
531 s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
532 if (!s)
533 {
535 return 0;
536 }
537 break;
538
539 case '"':
540 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
541 phrase[phraselen++] = ' ';
542 s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
543 if (!s)
544 {
546 return 0;
547 }
548 break;
549
550 case ':':
551 {
552 struct Address *a = mutt_addr_new();
553 terminate_buffer(phrase, phraselen);
554 if (phraselen != 0)
555 {
556 a->mailbox = buf_new(phrase);
557 }
558 a->group = true;
560 phraselen = 0;
561 commentlen = 0;
562 s++;
563 break;
564 }
565
566 case '<':
567 {
568 struct Address *a = mutt_addr_new();
569 terminate_buffer(phrase, phraselen);
570 if (phraselen != 0)
571 {
572 a->personal = buf_new(phrase);
573 }
574 s = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, a);
575 if (!s)
576 {
578 mutt_addr_free(&a);
579 return 0;
580 }
582 phraselen = 0;
583 commentlen = 0;
584 parsed++;
585 break;
586 }
587
588 default:
589 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)) && ws_pending)
590 phrase[phraselen++] = ' ';
591 if (*s == '\\')
592 {
593 s++;
594 if (*s && (phraselen < (sizeof(phrase) - 1)))
595 {
596 phrase[phraselen++] = *s;
597 s++;
598 }
599 }
600 s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
601 if (!s)
602 {
604 return 0;
605 }
606 break;
607 } // switch (*s)
608
609 ws_pending = mutt_str_is_email_wsp(*s);
611 } // while (*s)
612
613 if (phraselen != 0)
614 {
615 terminate_buffer(phrase, phraselen);
616 terminate_buffer(comment, commentlen);
617 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
618 {
619 parsed++;
620 }
621 }
622 else if (commentlen != 0)
623 {
624 struct Address *last = TAILQ_LAST(al, AddressList);
625 if (last && buf_is_empty(last->personal) && !buf_is_empty(last->mailbox))
626 {
627 terminate_buffer(comment, commentlen);
628 buf_strcpy(last->personal, comment);
629 }
630 }
631
632 return parsed;
633}
634
644int mutt_addrlist_parse2(struct AddressList *al, const char *s)
645{
646 if (!s || (*s == '\0'))
647 return 0;
648
649 int parsed = 0;
650
651 /* check for a simple whitespace separated list of addresses */
652 if (!strpbrk(s, "\"<>():;,\\"))
653 {
654 char *copy = mutt_str_dup(s);
655 char *r = copy;
656 while ((r = strtok(r, " \t")))
657 {
658 parsed += mutt_addrlist_parse(al, r);
659 r = NULL;
660 }
661 FREE(&copy);
662 }
663 else
664 {
665 parsed = mutt_addrlist_parse(al, s);
666 }
667
668 return parsed;
669}
670
680void mutt_addrlist_qualify(struct AddressList *al, const char *host)
681{
682 if (!al || !host || (*host == '\0'))
683 return;
684
685 struct Address *a = NULL;
686 TAILQ_FOREACH(a, al, entries)
687 {
688 if (!a->group && a->mailbox && !buf_find_char(a->mailbox, '@'))
689 {
690 buf_add_printf(a->mailbox, "@%s", host);
691 }
692 }
693}
694
708void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
709{
710 if (!buf || !value || !specials)
711 return;
712
713 if (strpbrk(value, specials))
714 {
715 char tmp[256] = { 0 };
716 char *pc = tmp;
717 size_t tmplen = sizeof(tmp) - 3;
718
719 *pc++ = '"';
720 for (; *value && (tmplen > 1); value++)
721 {
722 if ((*value == '\\') || (*value == '"'))
723 {
724 *pc++ = '\\';
725 tmplen--;
726 }
727 *pc++ = *value;
728 tmplen--;
729 }
730 *pc++ = '"';
731 *pc = '\0';
732 mutt_str_copy(buf, tmp, buflen);
733 }
734 else
735 {
736 mutt_str_copy(buf, value, buflen);
737 }
738}
739
745struct Address *mutt_addr_copy(const struct Address *addr)
746{
747 if (!addr)
748 return NULL;
749
750 struct Address *p = mutt_addr_new();
751 p->personal = buf_dup(addr->personal);
752 p->mailbox = buf_dup(addr->mailbox);
753 p->group = addr->group;
754 p->is_intl = addr->is_intl;
755 p->intl_checked = addr->intl_checked;
756 return p;
757}
758
765void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
766{
767 if (!dst || !src)
768 return;
769
770 struct Address *a = NULL;
771 TAILQ_FOREACH(a, src, entries)
772 {
773 struct Address *next = TAILQ_NEXT(a, entries);
774 if (prune && a->group && (!next || !next->mailbox))
775 {
776 /* ignore this element of the list */
777 }
778 else
779 {
781 }
782 }
783}
784
792bool mutt_addr_valid_msgid(const char *msgid)
793{
794 /* msg-id = "<" addr-spec ">"
795 * addr-spec = local-part "@" domain
796 * local-part = word *("." word)
797 * word = atom / quoted-string
798 * atom = 1*<any CHAR except specials, SPACE and CTLs>
799 * CHAR = ( 0.-127. )
800 * specials = "(" / ")" / "<" / ">" / "@"
801 * / "," / ";" / ":" / "\" / <">
802 * / "." / "[" / "]"
803 * SPACE = ( 32. )
804 * CTLS = ( 0.-31., 127.)
805 * quoted-string = <"> *(qtext/quoted-pair) <">
806 * qtext = <any CHAR except <">, "\" and CR>
807 * CR = ( 13. )
808 * quoted-pair = "\" CHAR
809 * domain = sub-domain *("." sub-domain)
810 * sub-domain = domain-ref / domain-literal
811 * domain-ref = atom
812 * domain-literal = "[" *(dtext / quoted-pair) "]"
813 */
814
815 if (!msgid || (*msgid == '\0'))
816 return false;
817
818 size_t l = mutt_str_len(msgid);
819 if (l < 5) /* <atom@atom> */
820 return false;
821 if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
822 return false;
823 if (!(strrchr(msgid, '@')))
824 return false;
825
826 /* TODO: complete parser */
827 for (size_t i = 0; i < l; i++)
828 if ((unsigned char) msgid[i] > 127)
829 return false;
830
831 return true;
832}
833
840bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
841{
842 if (!ala || !alb)
843 {
844 return !(ala || alb);
845 }
846
847 struct Address *ana = TAILQ_FIRST(ala);
848 struct Address *anb = TAILQ_FIRST(alb);
849
850 while (ana && anb)
851 {
852 if (!buf_str_equal(ana->mailbox, anb->mailbox) ||
853 !buf_str_equal(ana->personal, anb->personal))
854 {
855 break;
856 }
857
858 ana = TAILQ_NEXT(ana, entries);
859 anb = TAILQ_NEXT(anb, entries);
860 }
861
862 return !(ana || anb);
863}
864
872int mutt_addrlist_count_recips(const struct AddressList *al)
873{
874 if (!al)
875 return 0;
876
877 int c = 0;
878 struct Address *a = NULL;
879 TAILQ_FOREACH(a, al, entries)
880 {
881 c += (a->mailbox && !a->group);
882 }
883 return c;
884}
885
892bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
893{
894 if (!a || !b)
895 return false;
896 if (!a->mailbox || !b->mailbox)
897 return false;
898 if (!buf_istr_equal(a->mailbox, b->mailbox))
899 return false;
900 return true;
901}
902
909bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
910{
911 if (!needle || !haystack)
912 return false;
913
914 struct Address *a = NULL;
915 TAILQ_FOREACH(a, haystack, entries)
916 {
917 if (mutt_addr_cmp(needle, a))
918 return true;
919 }
920 return false;
921}
922
928static bool addr_is_intl(const struct Address *a)
929{
930 if (!a)
931 return false;
932 return a->intl_checked && a->is_intl;
933}
934
940static bool addr_is_local(const struct Address *a)
941{
942 if (!a)
943 return false;
944 return a->intl_checked && !a->is_intl;
945}
946
957static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
958{
959 if (!mbox || !user || !domain)
960 return -1;
961
962 char *ptr = strchr(mbox, '@');
963
964 /* Fail if '@' is missing, at the start, or at the end */
965 if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
966 return -1;
967
968 *user = mutt_strn_dup(mbox, ptr - mbox);
969 *domain = mutt_str_dup(ptr + 1);
970
971 return 0;
972}
973
979static void addr_set_intl(struct Address *a, char *intl_mailbox)
980{
981 if (!a)
982 return;
983
984 buf_strcpy(a->mailbox, intl_mailbox);
985 a->intl_checked = true;
986 a->is_intl = true;
987}
988
994static void addr_set_local(struct Address *a, char *local_mailbox)
995{
996 if (!a)
997 return;
998
999 buf_strcpy(a->mailbox, local_mailbox);
1000 a->intl_checked = true;
1001 a->is_intl = false;
1002}
1003
1012const char *mutt_addr_for_display(const struct Address *a)
1013{
1014 if (!a)
1015 return NULL;
1016
1017 char *user = NULL, *domain = NULL;
1018 static char *buf = NULL;
1019
1020 if (!a->mailbox || addr_is_local(a))
1021 return buf_string(a->mailbox);
1022
1023 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1024 return buf_string(a->mailbox);
1025
1026 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
1027
1028 FREE(&user);
1029 FREE(&domain);
1030
1031 if (!local_mailbox)
1032 return buf_string(a->mailbox);
1033
1034 mutt_str_replace(&buf, local_mailbox);
1035 FREE(&local_mailbox);
1036
1037 return buf;
1038}
1039
1050size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
1051{
1052 if (!buf || !addr || (!addr->personal && !addr->mailbox))
1053 {
1054 return 0;
1055 }
1056
1057 const size_t initial_len = buf_len(buf);
1058
1059 if (addr->personal)
1060 {
1061 if (strpbrk(buf_string(addr->personal), AddressSpecials))
1062 {
1063 buf_addch(buf, '"');
1064 for (const char *pc = buf_string(addr->personal); *pc; pc++)
1065 {
1066 if ((*pc == '"') || (*pc == '\\'))
1067 {
1068 buf_addch(buf, '\\');
1069 }
1070 buf_addch(buf, *pc);
1071 }
1072 buf_addch(buf, '"');
1073 }
1074 else
1075 {
1076 buf_addstr(buf, buf_string(addr->personal));
1077 }
1078
1079 buf_addch(buf, ' ');
1080 }
1081
1082 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1083 {
1084 buf_addch(buf, '<');
1085 }
1086
1087 if (addr->mailbox)
1088 {
1089 if (!mutt_str_equal(buf_string(addr->mailbox), "@"))
1090 {
1091 const char *a = display ? mutt_addr_for_display(addr) : buf_string(addr->mailbox);
1092 buf_addstr(buf, a);
1093 }
1094
1095 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1096 {
1097 buf_addch(buf, '>');
1098 }
1099
1100 if (addr->group)
1101 {
1102 buf_addstr(buf, ": ");
1103 }
1104 }
1105 else
1106 {
1107 buf_addch(buf, ';');
1108 }
1109
1110 return buf_len(buf) - initial_len;
1111}
1112
1126static size_t addrlist_write(const struct AddressList *al, struct Buffer *buf,
1127 bool display, const char *header, int cols)
1128{
1129 if (!buf || !al || TAILQ_EMPTY(al))
1130 return 0;
1131
1132 if (header)
1133 {
1134 buf_printf(buf, "%s: ", header);
1135 }
1136
1137 size_t cur_col = buf_len(buf);
1138 bool in_group = false;
1139 struct Address *a = NULL;
1140 TAILQ_FOREACH(a, al, entries)
1141 {
1142 struct Address *next = TAILQ_NEXT(a, entries);
1143
1144 if (a->group)
1145 {
1146 in_group = true;
1147 }
1148
1149 // wrap if needed
1150 const size_t cur_len = buf_len(buf);
1151 cur_col += mutt_addr_write(buf, a, display);
1152 if ((cols > 0) && (cur_col > cols) && (a != TAILQ_FIRST(al)))
1153 {
1154 buf_insert(buf, cur_len, "\n\t");
1155 cur_col = 8;
1156 }
1157
1158 if (!a->group)
1159 {
1160 // group terminator
1161 if (in_group && !a->mailbox && !a->personal)
1162 {
1163 buf_addch(buf, ';');
1164 cur_col++;
1165 in_group = false;
1166 }
1167 if (next && (next->mailbox || next->personal))
1168 {
1169 buf_addstr(buf, ", ");
1170 cur_col += 2;
1171 }
1172 if (!next)
1173 {
1174 break;
1175 }
1176 }
1177 }
1178
1179 return buf_len(buf);
1180}
1181
1189size_t mutt_addrlist_write_wrap(const struct AddressList *al,
1190 struct Buffer *buf, const char *header)
1191{
1192 return addrlist_write(al, buf, false, header, 74);
1193}
1194
1206size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
1207{
1208 return addrlist_write(al, buf, display, NULL, -1);
1209}
1210
1217size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1218{
1219 if (!al || !list)
1220 return 0;
1221
1222 size_t count = 0;
1223 struct Address *a = NULL;
1224 TAILQ_FOREACH(a, al, entries)
1225 {
1226 struct Buffer buf = { 0 };
1227 mutt_addr_write(&buf, a, true);
1228 if (!buf_is_empty(&buf))
1229 {
1230 /* We're taking the ownership of the buffer string here */
1231 mutt_list_insert_tail(list, (char *) buf_string(&buf));
1232 count++;
1233 }
1234 }
1235
1236 return count;
1237}
1238
1248void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
1249{
1250 struct Buffer *buf = buf_pool_get();
1251 mutt_addrlist_write_wrap(al, buf, header);
1252 fputs(buf_string(buf), fp);
1253 buf_pool_release(&buf);
1254 fputc('\n', fp);
1255}
1256
1264{
1265 if (!a || !a->mailbox || addr_is_intl(a))
1266 return true;
1267
1268 char *user = NULL;
1269 char *domain = NULL;
1270 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1271 return true;
1272
1273 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1274
1275 FREE(&user);
1276 FREE(&domain);
1277
1278 if (!intl_mailbox)
1279 return false;
1280
1281 addr_set_intl(a, intl_mailbox);
1282 FREE(&intl_mailbox);
1283 return true;
1284}
1285
1293int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1294{
1295 if (!al)
1296 return 0;
1297
1298 int rc = 0;
1299
1300 if (err)
1301 *err = NULL;
1302
1303 struct Address *a = NULL;
1304 TAILQ_FOREACH(a, al, entries)
1305 {
1306 if (!a->mailbox || addr_is_intl(a))
1307 continue;
1308
1309 char *user = NULL;
1310 char *domain = NULL;
1311 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1312 continue;
1313
1314 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1315
1316 FREE(&user);
1317 FREE(&domain);
1318
1319 if (!intl_mailbox)
1320 {
1321 rc = -1;
1322 if (err && !*err)
1323 *err = buf_strdup(a->mailbox);
1324 continue;
1325 }
1326
1327 addr_set_intl(a, intl_mailbox);
1328 FREE(&intl_mailbox);
1329 }
1330
1331 return rc;
1332}
1333
1341{
1342 if (!a->mailbox)
1343 {
1344 return false;
1345 }
1346
1347 if (addr_is_local(a))
1348 {
1349 return true;
1350 }
1351
1352 char *user = NULL;
1353 char *domain = NULL;
1354 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1355 {
1356 return false;
1357 }
1358
1359 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_NO_FLAGS);
1360 FREE(&user);
1361 FREE(&domain);
1362
1363 if (!local_mailbox)
1364 {
1365 return false;
1366 }
1367
1368 addr_set_local(a, local_mailbox);
1369 FREE(&local_mailbox);
1370 return true;
1371}
1372
1378int mutt_addrlist_to_local(struct AddressList *al)
1379{
1380 if (!al)
1381 return 0;
1382
1383 struct Address *a = NULL;
1384 TAILQ_FOREACH(a, al, entries)
1385 {
1387 }
1388 return 0;
1389}
1390
1397void mutt_addrlist_dedupe(struct AddressList *al)
1398{
1399 if (!al)
1400 return;
1401
1402 struct Address *a = NULL;
1403 TAILQ_FOREACH(a, al, entries)
1404 {
1405 if (a->mailbox)
1406 {
1407 struct Address *a2 = TAILQ_NEXT(a, entries);
1408 struct Address *tmp = NULL;
1409
1410 if (a2)
1411 {
1412 TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1413 {
1414 if (a2->mailbox && buf_istr_equal(a->mailbox, a2->mailbox))
1415 {
1416 mutt_debug(LL_DEBUG2, "Removing %s\n", buf_string(a2->mailbox));
1417 TAILQ_REMOVE(al, a2, entries);
1418 mutt_addr_free(&a2);
1419 }
1420 }
1421 }
1422 }
1423 }
1424}
1425
1433void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1434{
1435 if (!a || !b)
1436 return;
1437
1438 struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1439
1440 TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1441 {
1442 TAILQ_FOREACH(aa, a, entries)
1443 {
1444 if (mutt_addr_cmp(aa, ab))
1445 {
1446 TAILQ_REMOVE(b, ab, entries);
1447 mutt_addr_free(&ab);
1448 break;
1449 }
1450 }
1451 }
1452}
1453
1460void mutt_addrlist_clear(struct AddressList *al)
1461{
1462 if (!al)
1463 return;
1464
1465 struct Address *a = TAILQ_FIRST(al), *next = NULL;
1466 while (a)
1467 {
1468 next = TAILQ_NEXT(a, entries);
1469 mutt_addr_free(&a);
1470 a = next;
1471 }
1472 TAILQ_INIT(al);
1473}
1474
1480void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1481{
1482 if (al && a)
1483 TAILQ_INSERT_TAIL(al, a, entries);
1484}
1485
1491void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1492{
1493 if (al && a)
1494 TAILQ_INSERT_HEAD(al, a, entries);
1495}
1496
1502bool mutt_addr_uses_unicode(const char *str)
1503{
1504 if (!str)
1505 return false;
1506
1507 while (*str)
1508 {
1509 if ((unsigned char) *str & (1 << 7))
1510 return true;
1511 str++;
1512 }
1513
1514 return false;
1515}
1516
1522bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1523{
1524 if (!al)
1525 {
1526 return false;
1527 }
1528
1529 struct Address *a = NULL;
1530 TAILQ_FOREACH(a, al, entries)
1531 {
1533 return true;
1534 }
1535 return false;
1536}
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition: address.c:928
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:765
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:680
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition: address.c:957
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition: address.c:940
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:840
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:1460
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:1217
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:792
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition: address.c:979
size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition: address.c:1050
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition: address.c:994
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition: address.c:1522
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:708
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:892
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1480
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:1189
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:1126
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1378
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition: address.c:1206
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1502
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:644
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:745
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:45
#define USER_SPECIAL_MASK
AddressSpecials except " ( .
Definition: address.c:74
#define ROUTE_SPECIAL_MASK
AddressSpecials except ( , .
Definition: address.c:80
bool mutt_addr_to_local(struct Address *a)
Convert an Address from Punycode.
Definition: address.c:1340
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1433
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition: address.c:872
#define ADDRESS_SPECIAL_MASK
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:1491
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:1248
#define DOMAIN_SPECIAL_MASK
AddressSpecials except ( .
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:1293
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1263
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:909
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:1012
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1397
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:697
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:670
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:685
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:655
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:89
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:44
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
#define FREE(x)
Definition: memory.h:45
Convenience wrapper for the library headers.
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:380
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:672
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:608
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
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:581
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:280
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define TAILQ_INIT(head)
Definition: queue.h:765
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)
Definition: queue.h:740
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
#define TAILQ_NEXT(elm, field)
Definition: queue.h:832
#define TAILQ_EMPTY(head)
Definition: queue.h:721
#define TAILQ_LAST(head, headname)
Definition: queue.h:819
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:796
#define terminate_string(str, strlen, buflen)
Definition: string2.h:49
#define terminate_buffer(str, strlen)
Definition: string2.h:52
static bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string2.h:104
An email address.
Definition: address.h:36
struct Buffer * personal
Real name of address.
Definition: address.h:37
bool group
Group mailbox?
Definition: address.h:39
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
bool intl_checked
Checked for IDN?
Definition: address.h:41
bool is_intl
International Domain Name.
Definition: address.h:40
String manipulation buffer.
Definition: buffer.h:36