NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
address.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <stdbool.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <string.h>
36#include "mutt/lib.h"
37#include "address.h"
38#include "idna2.h"
39
43const char AddressSpecials[] = "\"(),.:;<>@[\\]";
44
65#define is_special(ch, mask) \
66 ((ch) >= 32 && (ch) < 96 && ((mask >> ((ch) -32)) & 1))
67
69#define ADDRESS_SPECIAL_MASK 0x380000015c005304ULL
70
72#define USER_SPECIAL_MASK 0x280000015c001200ULL
73
75#define DOMAIN_SPECIAL_MASK 0x000000015c001204ULL
76
78#define ROUTE_SPECIAL_MASK 0x000000015c000204ULL
79
89static const char *parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
90{
91 int level = 1;
92
93 while (*s && level)
94 {
95 if (*s == '(')
96 {
97 level++;
98 }
99 else if (*s == ')')
100 {
101 if (--level == 0)
102 {
103 s++;
104 break;
105 }
106 }
107 else if (*s == '\\')
108 {
109 if (!*++s)
110 break;
111 }
112 if (*commentlen < commentmax)
113 comment[(*commentlen)++] = *s;
114 s++;
115 }
116 if (level != 0)
117 {
118 return NULL;
119 }
120 return s;
121}
122
132static const char *parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
133{
134 while (*s)
135 {
136 if (*tokenlen < tokenmax)
137 token[*tokenlen] = *s;
138 if (*s == '\\')
139 {
140 if (!*++s)
141 break;
142
143 if (*tokenlen < tokenmax)
144 token[*tokenlen] = *s;
145 }
146 else if (*s == '"')
147 {
148 return s + 1;
149 }
150 (*tokenlen)++;
151 s++;
152 }
153 return NULL;
154}
155
164static const char *next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
165{
166 if (*s == '(')
167 return parse_comment(s + 1, token, tokenlen, tokenmax);
168 if (*s == '"')
169 return parse_quote(s + 1, token, tokenlen, tokenmax);
170 if (*s && is_special(*s, ADDRESS_SPECIAL_MASK))
171 {
172 if (*tokenlen < tokenmax)
173 token[(*tokenlen)++] = *s;
174 return s + 1;
175 }
176 while (*s)
177 {
179 break;
180 if (*tokenlen < tokenmax)
181 token[(*tokenlen)++] = *s;
182 s++;
183 }
184 return s;
185}
186
211static const char *parse_mailboxdomain(const char *s, uint64_t special_mask,
212 char *mailbox, size_t *mailboxlen,
213 size_t mailboxmax, char *comment,
214 size_t *commentlen, size_t commentmax)
215{
216 const char *ps = NULL;
217
218 while (*s)
219 {
221 if ((*s == '\0'))
222 return s;
223
224 if (is_special(*s, special_mask))
225 return s;
226
227 if (*s == '(')
228 {
229 if (*commentlen && (*commentlen < commentmax))
230 comment[(*commentlen)++] = ' ';
231 ps = next_token(s, comment, commentlen, commentmax);
232 }
233 else
234 {
235 ps = next_token(s, mailbox, mailboxlen, mailboxmax);
236 }
237 if (!ps)
238 return NULL;
239 s = ps;
240 }
241
242 return s;
243}
244
258static const char *parse_address(const char *s, char *token, size_t *tokenlen,
259 size_t tokenmax, char *comment, size_t *commentlen,
260 size_t commentmax, struct Address *addr)
261{
262 s = parse_mailboxdomain(s, USER_SPECIAL_MASK, token, tokenlen, tokenmax,
263 comment, commentlen, commentmax);
264 if (!s)
265 return NULL;
266
267 if (*s == '@')
268 {
269 if (*tokenlen < tokenmax)
270 token[(*tokenlen)++] = '@';
271 s = parse_mailboxdomain(s + 1, DOMAIN_SPECIAL_MASK, token, tokenlen,
272 tokenmax, comment, commentlen, commentmax);
273 if (!s)
274 return NULL;
275 }
276
277 terminate_string(token, *tokenlen, tokenmax);
278 addr->mailbox = buf_new(token);
279
280 if (*commentlen && !addr->personal)
281 {
282 terminate_string(comment, *commentlen, commentmax);
283 addr->personal = buf_new(comment);
284 }
285
286 return s;
287}
288
298static const char *parse_route_addr(const char *s, char *comment, size_t *commentlen,
299 size_t commentmax, struct Address *addr)
300{
301 char token[1024] = { 0 };
302 size_t tokenlen = 0;
303
305
306 /* find the end of the route */
307 if (*s == '@')
308 {
309 while (s && (*s == '@'))
310 {
311 if (tokenlen < (sizeof(token) - 1))
312 token[tokenlen++] = '@';
313 s = parse_mailboxdomain(s + 1, ROUTE_SPECIAL_MASK, token, &tokenlen,
314 sizeof(token) - 1, comment, commentlen, commentmax);
315 }
316 if (!s || (*s != ':'))
317 {
318 return NULL; /* invalid route */
319 }
320
321 if (tokenlen < (sizeof(token) - 1))
322 token[tokenlen++] = ':';
323 s++;
324 }
325
326 s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
327 commentmax, addr);
328 if (!s)
329 return NULL;
330
331 if (*s != '>')
332 {
333 return NULL;
334 }
335
336 if (!addr->mailbox)
337 {
338 addr->mailbox = buf_new("@");
339 }
340
341 s++;
342 return s;
343}
344
354static const char *parse_addr_spec(const char *s, char *comment, size_t *commentlen,
355 size_t commentmax, struct Address *addr)
356{
357 char token[1024] = { 0 };
358 size_t tokenlen = 0;
359
360 s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
361 commentmax, addr);
362 if (s && *s && (*s != ',') && (*s != ';'))
363 {
364 return NULL;
365 }
366 return s;
367}
368
378static bool add_addrspec(struct AddressList *al, const char *phrase,
379 char *comment, size_t *commentlen, size_t commentmax)
380{
381 struct Address *cur = mutt_addr_new();
382
383 if (!parse_addr_spec(phrase, comment, commentlen, commentmax, cur))
384 {
385 mutt_addr_free(&cur);
386 return false;
387 }
388
389 mutt_addrlist_append(al, cur);
390 return true;
391}
392
400{
401 return mutt_mem_calloc(1, sizeof(struct Address));
402}
403
412struct Address *mutt_addr_create(const char *personal, const char *mailbox)
413{
414 struct Address *a = mutt_addr_new();
415 if (personal)
416 {
418 }
419 if (mailbox)
420 {
421 a->mailbox = buf_new(mailbox);
422 }
423 return a;
424}
425
433int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
434{
435 if (!al)
436 return -1;
437
438 if (!mailbox)
439 return 0;
440
441 int rc = -1;
442 struct Address *a = NULL, *tmp = NULL;
443 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
444 {
446 {
447 TAILQ_REMOVE(al, a, entries);
448 mutt_addr_free(&a);
449 rc = 0;
450 }
451 }
452
453 return rc;
454}
455
460void mutt_addr_free(struct Address **ptr)
461{
462 if (!ptr || !*ptr)
463 return;
464
465 struct Address *a = *ptr;
466
467 buf_free(&a->personal);
468 buf_free(&a->mailbox);
469 FREE(ptr);
470}
471
478int mutt_addrlist_parse(struct AddressList *al, const char *s)
479{
480 if (!s)
481 return 0;
482
483 int parsed = 0;
484 char comment[1024], phrase[1024];
485 size_t phraselen = 0, commentlen = 0;
486
487 bool ws_pending = mutt_str_is_email_wsp(*s);
488
490 while (*s)
491 {
492 switch (*s)
493 {
494 case ';':
495 case ',':
496 if (phraselen != 0)
497 {
498 terminate_buffer(phrase, phraselen);
499 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
500 {
501 parsed++;
502 }
503 }
504 else if (commentlen != 0)
505 {
506 struct Address *last = TAILQ_LAST(al, AddressList);
507 if (last && !last->personal && !buf_is_empty(last->mailbox))
508 {
509 terminate_buffer(comment, commentlen);
510 last->personal = buf_new(comment);
511 }
512 }
513
514 if (*s == ';')
515 {
516 /* add group terminator */
518 }
519
520 phraselen = 0;
521 commentlen = 0;
522 s++;
523 break;
524
525 case '(':
526 if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
527 comment[commentlen++] = ' ';
528 s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
529 if (!s)
530 {
532 return 0;
533 }
534 break;
535
536 case '"':
537 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
538 phrase[phraselen++] = ' ';
539 s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
540 if (!s)
541 {
543 return 0;
544 }
545 break;
546
547 case ':':
548 {
549 struct Address *a = mutt_addr_new();
550 terminate_buffer(phrase, phraselen);
551 if (phraselen != 0)
552 {
553 a->mailbox = buf_new(phrase);
554 }
555 a->group = true;
557 phraselen = 0;
558 commentlen = 0;
559 s++;
560 break;
561 }
562
563 case '<':
564 {
565 struct Address *a = mutt_addr_new();
566 terminate_buffer(phrase, phraselen);
567 if (phraselen != 0)
568 {
569 a->personal = buf_new(phrase);
570 }
571 s = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, a);
572 if (!s)
573 {
575 mutt_addr_free(&a);
576 return 0;
577 }
579 phraselen = 0;
580 commentlen = 0;
581 parsed++;
582 break;
583 }
584
585 default:
586 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)) && ws_pending)
587 phrase[phraselen++] = ' ';
588 if (*s == '\\')
589 {
590 s++;
591 if (*s && (phraselen < (sizeof(phrase) - 1)))
592 {
593 phrase[phraselen++] = *s;
594 s++;
595 }
596 }
597 s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
598 if (!s)
599 {
601 return 0;
602 }
603 break;
604 } // switch (*s)
605
606 ws_pending = mutt_str_is_email_wsp(*s);
608 } // while (*s)
609
610 if (phraselen != 0)
611 {
612 terminate_buffer(phrase, phraselen);
613 terminate_buffer(comment, commentlen);
614 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
615 {
616 parsed++;
617 }
618 }
619 else if (commentlen != 0)
620 {
621 struct Address *last = TAILQ_LAST(al, AddressList);
622 if (last && buf_is_empty(last->personal) && !buf_is_empty(last->mailbox))
623 {
624 terminate_buffer(comment, commentlen);
625 buf_strcpy(last->personal, comment);
626 }
627 }
628
629 return parsed;
630}
631
641int mutt_addrlist_parse2(struct AddressList *al, const char *s)
642{
643 if (!s || (*s == '\0'))
644 return 0;
645
646 int parsed = 0;
647
648 /* check for a simple whitespace separated list of addresses */
649 if (!strpbrk(s, "\"<>():;,\\"))
650 {
651 char *copy = mutt_str_dup(s);
652 char *r = copy;
653 while ((r = strtok(r, " \t")))
654 {
655 parsed += mutt_addrlist_parse(al, r);
656 r = NULL;
657 }
658 FREE(&copy);
659 }
660 else
661 {
662 parsed = mutt_addrlist_parse(al, s);
663 }
664
665 return parsed;
666}
667
677void mutt_addrlist_qualify(struct AddressList *al, const char *host)
678{
679 if (!al || !host || (*host == '\0'))
680 return;
681
682 struct Address *a = NULL;
683 TAILQ_FOREACH(a, al, entries)
684 {
685 if (!a->group && a->mailbox && !buf_find_char(a->mailbox, '@'))
686 {
687 buf_add_printf(a->mailbox, "@%s", host);
688 }
689 }
690}
691
705void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
706{
707 if (!buf || !value || !specials)
708 return;
709
710 if (strpbrk(value, specials))
711 {
712 char tmp[256] = { 0 };
713 char *pc = tmp;
714 size_t tmplen = sizeof(tmp) - 3;
715
716 *pc++ = '"';
717 for (; *value && (tmplen > 1); value++)
718 {
719 if ((*value == '\\') || (*value == '"'))
720 {
721 *pc++ = '\\';
722 tmplen--;
723 }
724 *pc++ = *value;
725 tmplen--;
726 }
727 *pc++ = '"';
728 *pc = '\0';
729 mutt_str_copy(buf, tmp, buflen);
730 }
731 else
732 {
733 mutt_str_copy(buf, value, buflen);
734 }
735}
736
742struct Address *mutt_addr_copy(const struct Address *addr)
743{
744 if (!addr)
745 return NULL;
746
747 struct Address *p = mutt_addr_new();
748 p->personal = buf_dup(addr->personal);
749 p->mailbox = buf_dup(addr->mailbox);
750 p->group = addr->group;
751 p->is_intl = addr->is_intl;
752 p->intl_checked = addr->intl_checked;
753 return p;
754}
755
762void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
763{
764 if (!dst || !src)
765 return;
766
767 struct Address *a = NULL;
768 TAILQ_FOREACH(a, src, entries)
769 {
770 struct Address *next = TAILQ_NEXT(a, entries);
771 if (prune && a->group && (!next || !next->mailbox))
772 {
773 /* ignore this element of the list */
774 }
775 else
776 {
778 }
779 }
780}
781
789bool mutt_addr_valid_msgid(const char *msgid)
790{
791 /* msg-id = "<" addr-spec ">"
792 * addr-spec = local-part "@" domain
793 * local-part = word *("." word)
794 * word = atom / quoted-string
795 * atom = 1*<any CHAR except specials, SPACE and CTLs>
796 * CHAR = ( 0.-127. )
797 * specials = "(" / ")" / "<" / ">" / "@"
798 * / "," / ";" / ":" / "\" / <">
799 * / "." / "[" / "]"
800 * SPACE = ( 32. )
801 * CTLS = ( 0.-31., 127.)
802 * quoted-string = <"> *(qtext/quoted-pair) <">
803 * qtext = <any CHAR except <">, "\" and CR>
804 * CR = ( 13. )
805 * quoted-pair = "\" CHAR
806 * domain = sub-domain *("." sub-domain)
807 * sub-domain = domain-ref / domain-literal
808 * domain-ref = atom
809 * domain-literal = "[" *(dtext / quoted-pair) "]"
810 */
811
812 if (!msgid || (*msgid == '\0'))
813 return false;
814
815 size_t l = mutt_str_len(msgid);
816 if (l < 5) /* <atom@atom> */
817 return false;
818 if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
819 return false;
820 if (!(strrchr(msgid, '@')))
821 return false;
822
823 /* TODO: complete parser */
824 for (size_t i = 0; i < l; i++)
825 if ((unsigned char) msgid[i] > 127)
826 return false;
827
828 return true;
829}
830
837bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
838{
839 if (!ala || !alb)
840 {
841 return !(ala || alb);
842 }
843
844 struct Address *ana = TAILQ_FIRST(ala);
845 struct Address *anb = TAILQ_FIRST(alb);
846
847 while (ana && anb)
848 {
849 if (!buf_str_equal(ana->mailbox, anb->mailbox) ||
850 !buf_str_equal(ana->personal, anb->personal))
851 {
852 break;
853 }
854
855 ana = TAILQ_NEXT(ana, entries);
856 anb = TAILQ_NEXT(anb, entries);
857 }
858
859 return !(ana || anb);
860}
861
869int mutt_addrlist_count_recips(const struct AddressList *al)
870{
871 if (!al)
872 return 0;
873
874 int c = 0;
875 struct Address *a = NULL;
876 TAILQ_FOREACH(a, al, entries)
877 {
878 c += (a->mailbox && !a->group);
879 }
880 return c;
881}
882
889bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
890{
891 if (!a || !b)
892 return false;
893 if (!a->mailbox || !b->mailbox)
894 return false;
895 if (!buf_istr_equal(a->mailbox, b->mailbox))
896 return false;
897 return true;
898}
899
906bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
907{
908 if (!needle || !haystack)
909 return false;
910
911 struct Address *a = NULL;
912 TAILQ_FOREACH(a, haystack, entries)
913 {
914 if (mutt_addr_cmp(needle, a))
915 return true;
916 }
917 return false;
918}
919
925static bool addr_is_intl(const struct Address *a)
926{
927 if (!a)
928 return false;
929 return a->intl_checked && a->is_intl;
930}
931
937static bool addr_is_local(const struct Address *a)
938{
939 if (!a)
940 return false;
941 return a->intl_checked && !a->is_intl;
942}
943
954static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
955{
956 if (!mbox || !user || !domain)
957 return -1;
958
959 char *ptr = strchr(mbox, '@');
960
961 /* Fail if '@' is missing, at the start, or at the end */
962 if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
963 return -1;
964
965 *user = mutt_strn_dup(mbox, ptr - mbox);
966 *domain = mutt_str_dup(ptr + 1);
967
968 return 0;
969}
970
976static void addr_set_intl(struct Address *a, char *intl_mailbox)
977{
978 if (!a)
979 return;
980
981 buf_strcpy(a->mailbox, intl_mailbox);
982 a->intl_checked = true;
983 a->is_intl = true;
984}
985
991static void addr_set_local(struct Address *a, char *local_mailbox)
992{
993 if (!a)
994 return;
995
996 buf_strcpy(a->mailbox, local_mailbox);
997 a->intl_checked = true;
998 a->is_intl = false;
999}
1000
1009const char *mutt_addr_for_display(const struct Address *a)
1010{
1011 if (!a)
1012 return NULL;
1013
1014 char *user = NULL, *domain = NULL;
1015 static char *buf = NULL;
1016
1017 if (!a->mailbox || addr_is_local(a))
1018 return buf_string(a->mailbox);
1019
1020 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1021 return buf_string(a->mailbox);
1022
1023 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
1024
1025 FREE(&user);
1026 FREE(&domain);
1027
1028 if (!local_mailbox)
1029 return buf_string(a->mailbox);
1030
1031 mutt_str_replace(&buf, local_mailbox);
1032 FREE(&local_mailbox);
1033
1034 return buf;
1035}
1036
1047size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
1048{
1049 if (!buf || !addr || (!addr->personal && !addr->mailbox))
1050 {
1051 return 0;
1052 }
1053
1054 const size_t initial_len = buf_len(buf);
1055
1056 if (addr->personal)
1057 {
1058 if (strpbrk(buf_string(addr->personal), AddressSpecials))
1059 {
1060 buf_addch(buf, '"');
1061 for (const char *pc = buf_string(addr->personal); *pc; pc++)
1062 {
1063 if ((*pc == '"') || (*pc == '\\'))
1064 {
1065 buf_addch(buf, '\\');
1066 }
1067 buf_addch(buf, *pc);
1068 }
1069 buf_addch(buf, '"');
1070 }
1071 else
1072 {
1073 buf_addstr(buf, buf_string(addr->personal));
1074 }
1075
1076 buf_addch(buf, ' ');
1077 }
1078
1079 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1080 {
1081 buf_addch(buf, '<');
1082 }
1083
1084 if (addr->mailbox)
1085 {
1086 if (!mutt_str_equal(buf_string(addr->mailbox), "@"))
1087 {
1088 const char *a = display ? mutt_addr_for_display(addr) : buf_string(addr->mailbox);
1089 buf_addstr(buf, a);
1090 }
1091
1092 if (addr->personal || (addr->mailbox && (buf_at(addr->mailbox, 0) == '@')))
1093 {
1094 buf_addch(buf, '>');
1095 }
1096
1097 if (addr->group)
1098 {
1099 buf_addstr(buf, ": ");
1100 }
1101 }
1102 else
1103 {
1104 buf_addch(buf, ';');
1105 }
1106
1107 return buf_len(buf) - initial_len;
1108}
1109
1123static size_t addrlist_write(const struct AddressList *al, struct Buffer *buf,
1124 bool display, const char *header, int cols)
1125{
1126 if (!buf || !al || TAILQ_EMPTY(al))
1127 return 0;
1128
1129 if (header)
1130 {
1131 buf_printf(buf, "%s: ", header);
1132 }
1133
1134 size_t cur_col = buf_len(buf);
1135 bool in_group = false;
1136 struct Address *a = NULL;
1137 TAILQ_FOREACH(a, al, entries)
1138 {
1139 struct Address *next = TAILQ_NEXT(a, entries);
1140
1141 if (a->group)
1142 {
1143 in_group = true;
1144 }
1145
1146 // wrap if needed
1147 const size_t cur_len = buf_len(buf);
1148 cur_col += mutt_addr_write(buf, a, display);
1149 if (cur_col > cols)
1150 {
1151 buf_insert(buf, cur_len, "\n\t");
1152 cur_col = 8;
1153 }
1154
1155 if (!a->group)
1156 {
1157 // group terminator
1158 if (in_group && !a->mailbox && !a->personal)
1159 {
1160 buf_addch(buf, ';');
1161 ++cur_col;
1162 in_group = false;
1163 }
1164 if (next && (next->mailbox || next->personal))
1165 {
1166 buf_addstr(buf, ", ");
1167 cur_col += 2;
1168 }
1169 if (!next)
1170 {
1171 break;
1172 }
1173 }
1174 }
1175
1176 return buf_len(buf);
1177}
1178
1190size_t mutt_addrlist_write_wrap(const struct AddressList *al,
1191 struct Buffer *buf, const char *header)
1192{
1193 return addrlist_write(al, buf, false, header, 74);
1194}
1195
1207size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
1208{
1209 return addrlist_write(al, buf, display, NULL, -1);
1210}
1211
1218size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1219{
1220 if (!al || !list)
1221 return 0;
1222
1223 size_t count = 0;
1224 struct Address *a = NULL;
1225 TAILQ_FOREACH(a, al, entries)
1226 {
1227 struct Buffer buf = { 0 };
1228 mutt_addr_write(&buf, a, true);
1229 if (!buf_is_empty(&buf))
1230 {
1231 /* We're taking the ownership of the buffer string here */
1232 mutt_list_insert_tail(list, (char *) buf_string(&buf));
1233 count++;
1234 }
1235 }
1236
1237 return count;
1238}
1239
1249void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
1250{
1251 struct Buffer *buf = buf_pool_get();
1252 mutt_addrlist_write_wrap(al, buf, header);
1253 fputs(buf_string(buf), fp);
1254 buf_pool_release(&buf);
1255 fputc('\n', fp);
1256}
1257
1265{
1266 if (!a || !a->mailbox || addr_is_intl(a))
1267 return true;
1268
1269 char *user = NULL;
1270 char *domain = NULL;
1271 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1272 return true;
1273
1274 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1275
1276 FREE(&user);
1277 FREE(&domain);
1278
1279 if (!intl_mailbox)
1280 return false;
1281
1282 addr_set_intl(a, intl_mailbox);
1283 FREE(&intl_mailbox);
1284 return true;
1285}
1286
1294int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1295{
1296 if (!al)
1297 return 0;
1298
1299 int rc = 0;
1300
1301 if (err)
1302 *err = NULL;
1303
1304 struct Address *a = NULL;
1305 TAILQ_FOREACH(a, al, entries)
1306 {
1307 if (!a->mailbox || addr_is_intl(a))
1308 continue;
1309
1310 char *user = NULL;
1311 char *domain = NULL;
1312 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1313 continue;
1314
1315 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1316
1317 FREE(&user);
1318 FREE(&domain);
1319
1320 if (!intl_mailbox)
1321 {
1322 rc = -1;
1323 if (err && !*err)
1324 *err = buf_strdup(a->mailbox);
1325 continue;
1326 }
1327
1328 addr_set_intl(a, intl_mailbox);
1329 FREE(&intl_mailbox);
1330 }
1331
1332 return rc;
1333}
1334
1342{
1343 if (!a->mailbox)
1344 {
1345 return false;
1346 }
1347
1348 if (addr_is_local(a))
1349 {
1350 return true;
1351 }
1352
1353 char *user = NULL;
1354 char *domain = NULL;
1355 if (addr_mbox_to_udomain(buf_string(a->mailbox), &user, &domain) == -1)
1356 {
1357 return false;
1358 }
1359
1360 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_NO_FLAGS);
1361 FREE(&user);
1362 FREE(&domain);
1363
1364 if (!local_mailbox)
1365 {
1366 return false;
1367 }
1368
1369 addr_set_local(a, local_mailbox);
1370 FREE(&local_mailbox);
1371 return true;
1372}
1373
1379int mutt_addrlist_to_local(struct AddressList *al)
1380{
1381 if (!al)
1382 return 0;
1383
1384 struct Address *a = NULL;
1385 TAILQ_FOREACH(a, al, entries)
1386 {
1388 }
1389 return 0;
1390}
1391
1398void mutt_addrlist_dedupe(struct AddressList *al)
1399{
1400 if (!al)
1401 return;
1402
1403 struct Address *a = NULL;
1404 TAILQ_FOREACH(a, al, entries)
1405 {
1406 if (a->mailbox)
1407 {
1408 struct Address *a2 = TAILQ_NEXT(a, entries);
1409 struct Address *tmp = NULL;
1410
1411 if (a2)
1412 {
1413 TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1414 {
1415 if (a2->mailbox && buf_istr_equal(a->mailbox, a2->mailbox))
1416 {
1417 mutt_debug(LL_DEBUG2, "Removing %s\n", buf_string(a2->mailbox));
1418 TAILQ_REMOVE(al, a2, entries);
1419 mutt_addr_free(&a2);
1420 }
1421 }
1422 }
1423 }
1424 }
1425}
1426
1434void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1435{
1436 if (!a || !b)
1437 return;
1438
1439 struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1440
1441 TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1442 {
1443 TAILQ_FOREACH(aa, a, entries)
1444 {
1445 if (mutt_addr_cmp(aa, ab))
1446 {
1447 TAILQ_REMOVE(b, ab, entries);
1448 mutt_addr_free(&ab);
1449 break;
1450 }
1451 }
1452 }
1453}
1454
1461void mutt_addrlist_clear(struct AddressList *al)
1462{
1463 if (!al)
1464 return;
1465
1466 struct Address *a = TAILQ_FIRST(al), *next = NULL;
1467 while (a)
1468 {
1469 next = TAILQ_NEXT(a, entries);
1470 mutt_addr_free(&a);
1471 a = next;
1472 }
1473 TAILQ_INIT(al);
1474}
1475
1481void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1482{
1483 if (al && a)
1484 TAILQ_INSERT_TAIL(al, a, entries);
1485}
1486
1492void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1493{
1494 if (al && a)
1495 TAILQ_INSERT_HEAD(al, a, entries);
1496}
1497
1503bool mutt_addr_uses_unicode(const char *str)
1504{
1505 if (!str)
1506 return false;
1507
1508 while (*str)
1509 {
1510 if ((unsigned char) *str & (1 << 7))
1511 return true;
1512 str++;
1513 }
1514
1515 return false;
1516}
1517
1523bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1524{
1525 if (!al)
1526 {
1527 return false;
1528 }
1529
1530 struct Address *a = NULL;
1531 TAILQ_FOREACH(a, al, entries)
1532 {
1534 return true;
1535 }
1536 return false;
1537}
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition: address.c:925
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition: address.c:412
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:378
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:354
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:762
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:677
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition: address.c:954
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition: address.c:937
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:258
bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
Compare two Address lists for equality.
Definition: address.c:837
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:298
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:211
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1461
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:164
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition: address.c:1218
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:460
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:789
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition: address.c:976
size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition: address.c:1047
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition: address.c:991
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition: address.c:1523
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:705
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:889
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1481
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:1190
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:399
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:1123
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1379
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition: address.c:1207
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1503
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:641
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:742
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:43
#define USER_SPECIAL_MASK
AddressSpecials except " ( .
Definition: address.c:72
#define ROUTE_SPECIAL_MASK
AddressSpecials except ( , .
Definition: address.c:78
bool mutt_addr_to_local(struct Address *a)
Convert an Address from Punycode.
Definition: address.c:1341
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1434
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition: address.c:869
#define ADDRESS_SPECIAL_MASK
AddressSpecials, for is_special()
Definition: address.c:69
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:478
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1492
static const char * parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
Extract a comment (parenthesised string)
Definition: address.c:89
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
Wrapper for mutt_write_address()
Definition: address.c:1249
#define DOMAIN_SPECIAL_MASK
AddressSpecials except ( .
Definition: address.c:75
#define is_special(ch, mask)
Is this character special to an email address?
Definition: address.c:65
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1294
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1264
int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
Remove an Address from a list.
Definition: address.c:433
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition: address.c:906
static const char * parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
Extract a quoted string.
Definition: address.c:132
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:1009
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1398
Representation of an email address.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
bool buf_istr_equal(const struct Buffer *a, const struct Buffer *b)
Return if two buffers are equal, case insensitive.
Definition: buffer.c:665
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition: buffer.c:638
void buf_free(struct Buffer **ptr)
Deallocates a buffer.
Definition: buffer.c:331
struct Buffer * buf_new(const char *str)
Allocate a new Buffer.
Definition: buffer.c:316
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
bool buf_str_equal(const struct Buffer *a, const struct Buffer *b)
Return if two buffers are equal.
Definition: buffer.c:653
struct Buffer * buf_dup(const struct Buffer *buf)
Copy a Buffer into a new allocated buffer.
Definition: buffer.c:557
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:623
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
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:268
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
#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:226
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:116
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
@ 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:50
#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:452
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:680
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
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:111
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:34