NeoMutt  2023-03-22
Teaching an old dog new tricks
DOXYGEN
address.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <stdbool.h>
33#include <stdio.h>
34#include <string.h>
35#include "mutt/lib.h"
36#include "address.h"
37#include "idna2.h"
38
42const char AddressSpecials[] = "@.,:;<>[]\\\"()";
43
48#define is_special(ch) strchr(AddressSpecials, ch)
49
58
64const char *const AddressErrors[] = {
65 "out of memory", "mismatched parentheses", "mismatched quotes",
66 "bad route in <>", "bad address in <>", "bad address spec",
67};
68
78static const char *parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
79{
80 int level = 1;
81
82 while (*s && level)
83 {
84 if (*s == '(')
85 level++;
86 else if (*s == ')')
87 {
88 if (--level == 0)
89 {
90 s++;
91 break;
92 }
93 }
94 else if (*s == '\\')
95 {
96 if (!*++s)
97 break;
98 }
99 if (*commentlen < commentmax)
100 comment[(*commentlen)++] = *s;
101 s++;
102 }
103 if (level != 0)
104 {
106 return NULL;
107 }
108 return s;
109}
110
120static const char *parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
121{
122 while (*s)
123 {
124 if (*tokenlen < tokenmax)
125 token[*tokenlen] = *s;
126 if (*s == '\\')
127 {
128 if (!*++s)
129 break;
130
131 if (*tokenlen < tokenmax)
132 token[*tokenlen] = *s;
133 }
134 else if (*s == '"')
135 return s + 1;
136 (*tokenlen)++;
137 s++;
138 }
140 return NULL;
141}
142
151static const char *next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
152{
153 if (*s == '(')
154 return parse_comment(s + 1, token, tokenlen, tokenmax);
155 if (*s == '"')
156 return parse_quote(s + 1, token, tokenlen, tokenmax);
157 if (*s && is_special(*s))
158 {
159 if (*tokenlen < tokenmax)
160 token[(*tokenlen)++] = *s;
161 return s + 1;
162 }
163 while (*s)
164 {
165 if (mutt_str_is_email_wsp(*s) || is_special(*s))
166 break;
167 if (*tokenlen < tokenmax)
168 token[(*tokenlen)++] = *s;
169 s++;
170 }
171 return s;
172}
173
198static const char *parse_mailboxdomain(const char *s, const char *nonspecial,
199 char *mailbox, size_t *mailboxlen,
200 size_t mailboxmax, char *comment,
201 size_t *commentlen, size_t commentmax)
202{
203 const char *ps = NULL;
204
205 while (*s)
206 {
208 if ((*s == '\0'))
209 return s;
210
211 if (!strchr(nonspecial, *s) && is_special(*s))
212 return s;
213
214 if (*s == '(')
215 {
216 if (*commentlen && (*commentlen < commentmax))
217 comment[(*commentlen)++] = ' ';
218 ps = next_token(s, comment, commentlen, commentmax);
219 }
220 else
221 ps = next_token(s, mailbox, mailboxlen, mailboxmax);
222 if (!ps)
223 return NULL;
224 s = ps;
225 }
226
227 return s;
228}
229
243static const char *parse_address(const char *s, char *token, size_t *tokenlen,
244 size_t tokenmax, char *comment, size_t *commentlen,
245 size_t commentmax, struct Address *addr)
246{
247 s = parse_mailboxdomain(s, ".\"(\\", token, tokenlen, tokenmax, comment,
248 commentlen, commentmax);
249 if (!s)
250 return NULL;
251
252 if (*s == '@')
253 {
254 if (*tokenlen < tokenmax)
255 token[(*tokenlen)++] = '@';
256 s = parse_mailboxdomain(s + 1, ".([]\\", token, tokenlen, tokenmax, comment,
257 commentlen, commentmax);
258 if (!s)
259 return NULL;
260 }
261
262 terminate_string(token, *tokenlen, tokenmax);
263 addr->mailbox = mutt_str_dup(token);
264
265 if (*commentlen && !addr->personal)
266 {
267 terminate_string(comment, *commentlen, commentmax);
268 addr->personal = mutt_str_dup(comment);
269 }
270
271 return s;
272}
273
283static const char *parse_route_addr(const char *s, char *comment, size_t *commentlen,
284 size_t commentmax, struct Address *addr)
285{
286 char token[1024] = { 0 };
287 size_t tokenlen = 0;
288
290
291 /* find the end of the route */
292 if (*s == '@')
293 {
294 while (s && (*s == '@'))
295 {
296 if (tokenlen < (sizeof(token) - 1))
297 token[tokenlen++] = '@';
298 s = parse_mailboxdomain(s + 1, ",.\\[](", token, &tokenlen,
299 sizeof(token) - 1, comment, commentlen, commentmax);
300 }
301 if (!s || (*s != ':'))
302 {
304 return NULL; /* invalid route */
305 }
306
307 if (tokenlen < (sizeof(token) - 1))
308 token[tokenlen++] = ':';
309 s++;
310 }
311
312 s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
313 commentmax, addr);
314 if (!s)
315 return NULL;
316
317 if (*s != '>')
318 {
320 return NULL;
321 }
322
323 if (!addr->mailbox)
324 addr->mailbox = mutt_str_dup("@");
325
326 s++;
327 return s;
328}
329
339static const char *parse_addr_spec(const char *s, char *comment, size_t *commentlen,
340 size_t commentmax, struct Address *addr)
341{
342 char token[1024] = { 0 };
343 size_t tokenlen = 0;
344
345 s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
346 commentmax, addr);
347 if (s && *s && (*s != ',') && (*s != ';'))
348 {
350 return NULL;
351 }
352 return s;
353}
354
364static bool add_addrspec(struct AddressList *al, const char *phrase,
365 char *comment, size_t *commentlen, size_t commentmax)
366{
367 struct Address *cur = mutt_addr_new();
368
369 if (!parse_addr_spec(phrase, comment, commentlen, commentmax, cur))
370 {
371 mutt_addr_free(&cur);
372 return false;
373 }
374
375 mutt_addrlist_append(al, cur);
376 return true;
377}
378
386{
387 return mutt_mem_calloc(1, sizeof(struct Address));
388}
389
398struct Address *mutt_addr_create(const char *personal, const char *mailbox)
399{
400 struct Address *a = mutt_addr_new();
403 return a;
404}
405
413int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
414{
415 if (!al)
416 return -1;
417
418 if (!mailbox)
419 return 0;
420
421 int rc = -1;
422 struct Address *a = NULL, *tmp = NULL;
423 TAILQ_FOREACH_SAFE(a, al, entries, tmp)
424 {
426 {
427 TAILQ_REMOVE(al, a, entries);
428 mutt_addr_free(&a);
429 rc = 0;
430 }
431 }
432
433 return rc;
434}
435
440void mutt_addr_free(struct Address **ptr)
441{
442 if (!ptr || !*ptr)
443 return;
444
445 struct Address *a = *ptr;
446
447 FREE(&a->personal);
448 FREE(&a->mailbox);
449 FREE(ptr);
450}
451
458int mutt_addrlist_parse(struct AddressList *al, const char *s)
459{
460 if (!s)
461 return 0;
462
463 int parsed = 0;
464 char comment[1024], phrase[1024];
465 size_t phraselen = 0, commentlen = 0;
466 AddressError = 0;
467
468 bool ws_pending = mutt_str_is_email_wsp(*s);
469
471 while (*s)
472 {
473 switch (*s)
474 {
475 case ';':
476 case ',':
477 if (phraselen != 0)
478 {
479 terminate_buffer(phrase, phraselen);
480 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
481 {
482 parsed++;
483 }
484 }
485 else if (commentlen != 0)
486 {
487 struct Address *last = TAILQ_LAST(al, AddressList);
488 if (last && !last->personal && last->mailbox)
489 {
490 terminate_buffer(comment, commentlen);
491 last->personal = mutt_str_dup(comment);
492 }
493 }
494
495 if (*s == ';')
496 {
497 /* add group terminator */
499 }
500
501 phraselen = 0;
502 commentlen = 0;
503 s++;
504 break;
505
506 case '(':
507 if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
508 comment[commentlen++] = ' ';
509 s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
510 if (!s)
511 {
513 return 0;
514 }
515 break;
516
517 case '"':
518 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
519 phrase[phraselen++] = ' ';
520 s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
521 if (!s)
522 {
524 return 0;
525 }
526 break;
527
528 case ':':
529 {
530 struct Address *a = mutt_addr_new();
531 terminate_buffer(phrase, phraselen);
532 a->mailbox = mutt_str_dup(phrase);
533 a->group = true;
535 phraselen = 0;
536 commentlen = 0;
537 s++;
538 break;
539 }
540
541 case '<':
542 {
543 struct Address *a = mutt_addr_new();
544 terminate_buffer(phrase, phraselen);
545 a->personal = mutt_str_dup(phrase);
546 s = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, a);
547 if (!s)
548 {
550 mutt_addr_free(&a);
551 return 0;
552 }
554 phraselen = 0;
555 commentlen = 0;
556 parsed++;
557 break;
558 }
559
560 default:
561 if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)) && ws_pending)
562 phrase[phraselen++] = ' ';
563 if (*s == '\\')
564 {
565 s++;
566 if (*s && (phraselen < (sizeof(phrase) - 1)))
567 {
568 phrase[phraselen++] = *s;
569 s++;
570 }
571 }
572 s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
573 if (!s)
574 {
576 return 0;
577 }
578 break;
579 } // switch (*s)
580
581 ws_pending = mutt_str_is_email_wsp(*s);
583 } // while (*s)
584
585 if (phraselen != 0)
586 {
587 terminate_buffer(phrase, phraselen);
588 terminate_buffer(comment, commentlen);
589 if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
590 {
591 parsed++;
592 }
593 }
594 else if (commentlen != 0)
595 {
596 struct Address *last = TAILQ_LAST(al, AddressList);
597 if (last && !last->personal && last->mailbox)
598 {
599 terminate_buffer(comment, commentlen);
600 last->personal = mutt_str_dup(comment);
601 }
602 }
603
604 return parsed;
605}
606
616int mutt_addrlist_parse2(struct AddressList *al, const char *s)
617{
618 if (!s || (*s == '\0'))
619 return 0;
620
621 int parsed = 0;
622
623 /* check for a simple whitespace separated list of addresses */
624 if (!strpbrk(s, "\"<>():;,\\"))
625 {
626 char *copy = mutt_str_dup(s);
627 char *r = copy;
628 while ((r = strtok(r, " \t")))
629 {
630 parsed += mutt_addrlist_parse(al, r);
631 r = NULL;
632 }
633 FREE(&copy);
634 }
635 else
636 parsed = mutt_addrlist_parse(al, s);
637
638 return parsed;
639}
640
650void mutt_addrlist_qualify(struct AddressList *al, const char *host)
651{
652 if (!al || !host || (*host == '\0'))
653 return;
654
655 struct Address *a = NULL;
656 TAILQ_FOREACH(a, al, entries)
657 {
658 if (!a->group && a->mailbox && !strchr(a->mailbox, '@'))
659 {
660 char *p = mutt_mem_malloc(mutt_str_len(a->mailbox) + mutt_str_len(host) + 2);
661 sprintf(p, "%s@%s", a->mailbox, host);
662 FREE(&a->mailbox);
663 a->mailbox = p;
664 }
665 }
666}
667
681void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
682{
683 if (!buf || !value || !specials)
684 return;
685
686 if (strpbrk(value, specials))
687 {
688 char tmp[256] = { 0 };
689 char *pc = tmp;
690 size_t tmplen = sizeof(tmp) - 3;
691
692 *pc++ = '"';
693 for (; *value && (tmplen > 1); value++)
694 {
695 if ((*value == '\\') || (*value == '"'))
696 {
697 *pc++ = '\\';
698 tmplen--;
699 }
700 *pc++ = *value;
701 tmplen--;
702 }
703 *pc++ = '"';
704 *pc = '\0';
705 mutt_str_copy(buf, tmp, buflen);
706 }
707 else
708 mutt_str_copy(buf, value, buflen);
709}
710
716struct Address *mutt_addr_copy(const struct Address *addr)
717{
718 if (!addr)
719 return NULL;
720
721 struct Address *p = mutt_addr_new();
722
723 p->personal = mutt_str_dup(addr->personal);
724 p->mailbox = mutt_str_dup(addr->mailbox);
725 p->group = addr->group;
726 p->is_intl = addr->is_intl;
727 p->intl_checked = addr->intl_checked;
728 return p;
729}
730
737void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
738{
739 if (!dst || !src)
740 return;
741
742 struct Address *a = NULL;
743 TAILQ_FOREACH(a, src, entries)
744 {
745 struct Address *next = TAILQ_NEXT(a, entries);
746 if (prune && a->group && (!next || !next->mailbox))
747 {
748 /* ignore this element of the list */
749 }
750 else
751 {
753 }
754 }
755}
756
764bool mutt_addr_valid_msgid(const char *msgid)
765{
766 /* msg-id = "<" addr-spec ">"
767 * addr-spec = local-part "@" domain
768 * local-part = word *("." word)
769 * word = atom / quoted-string
770 * atom = 1*<any CHAR except specials, SPACE and CTLs>
771 * CHAR = ( 0.-127. )
772 * specials = "(" / ")" / "<" / ">" / "@"
773 * / "," / ";" / ":" / "\" / <">
774 * / "." / "[" / "]"
775 * SPACE = ( 32. )
776 * CTLS = ( 0.-31., 127.)
777 * quoted-string = <"> *(qtext/quoted-pair) <">
778 * qtext = <any CHAR except <">, "\" and CR>
779 * CR = ( 13. )
780 * quoted-pair = "\" CHAR
781 * domain = sub-domain *("." sub-domain)
782 * sub-domain = domain-ref / domain-literal
783 * domain-ref = atom
784 * domain-literal = "[" *(dtext / quoted-pair) "]"
785 */
786
787 if (!msgid || (*msgid == '\0'))
788 return false;
789
790 size_t l = mutt_str_len(msgid);
791 if (l < 5) /* <atom@atom> */
792 return false;
793 if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
794 return false;
795 if (!(strrchr(msgid, '@')))
796 return false;
797
798 /* TODO: complete parser */
799 for (size_t i = 0; i < l; i++)
800 if ((unsigned char) msgid[i] > 127)
801 return false;
802
803 return true;
804}
805
812bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
813{
814 if (!ala || !alb)
815 {
816 return !(ala || alb);
817 }
818
819 struct Address *ana = TAILQ_FIRST(ala);
820 struct Address *anb = TAILQ_FIRST(alb);
821
822 while (ana && anb)
823 {
824 if (!mutt_str_equal(ana->mailbox, anb->mailbox) ||
825 !mutt_str_equal(ana->personal, anb->personal))
826 {
827 break;
828 }
829
830 ana = TAILQ_NEXT(ana, entries);
831 anb = TAILQ_NEXT(anb, entries);
832 }
833
834 return !(ana || anb);
835}
836
844int mutt_addrlist_count_recips(const struct AddressList *al)
845{
846 if (!al)
847 return 0;
848
849 int c = 0;
850 struct Address *a = NULL;
851 TAILQ_FOREACH(a, al, entries)
852 {
853 c += (a->mailbox && !a->group);
854 }
855 return c;
856}
857
864bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
865{
866 if (!a || !b)
867 return false;
868 if (!a->mailbox || !b->mailbox)
869 return false;
870 if (!mutt_istr_equal(a->mailbox, b->mailbox))
871 return false;
872 return true;
873}
874
881bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
882{
883 if (!needle || !haystack)
884 return false;
885
886 struct Address *a = NULL;
887 TAILQ_FOREACH(a, haystack, entries)
888 {
889 if (mutt_addr_cmp(needle, a))
890 return true;
891 }
892 return false;
893}
894
900static bool addr_is_intl(const struct Address *a)
901{
902 if (!a)
903 return false;
904 return a->intl_checked && a->is_intl;
905}
906
912static bool addr_is_local(const struct Address *a)
913{
914 if (!a)
915 return false;
916 return a->intl_checked && !a->is_intl;
917}
918
929static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
930{
931 if (!mbox || !user || !domain)
932 return -1;
933
934 char *ptr = strchr(mbox, '@');
935
936 /* Fail if '@' is missing, at the start, or at the end */
937 if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
938 return -1;
939
940 *user = mutt_strn_dup(mbox, ptr - mbox);
941 *domain = mutt_str_dup(ptr + 1);
942
943 return 0;
944}
945
951static void addr_set_intl(struct Address *a, char *intl_mailbox)
952{
953 if (!a)
954 return;
955
956 FREE(&a->mailbox);
957 a->mailbox = intl_mailbox;
958 a->intl_checked = true;
959 a->is_intl = true;
960}
961
967static void addr_set_local(struct Address *a, char *local_mailbox)
968{
969 if (!a)
970 return;
971
972 FREE(&a->mailbox);
973 a->mailbox = local_mailbox;
974 a->intl_checked = true;
975 a->is_intl = false;
976}
977
986const char *mutt_addr_for_display(const struct Address *a)
987{
988 if (!a)
989 return NULL;
990
991 char *user = NULL, *domain = NULL;
992 static char *buf = NULL;
993
994 if (!a->mailbox || addr_is_local(a))
995 return a->mailbox;
996
997 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
998 return a->mailbox;
999
1000 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
1001
1002 FREE(&user);
1003 FREE(&domain);
1004
1005 if (!local_mailbox)
1006 return a->mailbox;
1007
1008 mutt_str_replace(&buf, local_mailbox);
1009 FREE(&local_mailbox);
1010
1011 return buf;
1012}
1013
1024size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
1025{
1026 if (!buf || !addr || (!addr->personal && !addr->mailbox))
1027 {
1028 return 0;
1029 }
1030
1031 const size_t initial_len = mutt_buffer_len(buf);
1032
1033 if (addr->personal)
1034 {
1035 if (strpbrk(addr->personal, AddressSpecials))
1036 {
1037 mutt_buffer_addch(buf, '"');
1038 for (const char *pc = addr->personal; *pc; pc++)
1039 {
1040 if ((*pc == '"') || (*pc == '\\'))
1041 {
1042 mutt_buffer_addch(buf, '\\');
1043 }
1044 mutt_buffer_addch(buf, *pc);
1045 }
1046 mutt_buffer_addch(buf, '"');
1047 }
1048 else
1049 {
1050 mutt_buffer_addstr(buf, addr->personal);
1051 }
1052
1053 mutt_buffer_addch(buf, ' ');
1054 }
1055
1056 if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1057 {
1058 mutt_buffer_addch(buf, '<');
1059 }
1060
1061 if (addr->mailbox)
1062 {
1063 if (!mutt_str_equal(addr->mailbox, "@"))
1064 {
1065 const char *a = display ? mutt_addr_for_display(addr) : addr->mailbox;
1066 mutt_buffer_addstr(buf, a);
1067 }
1068
1069 if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1070 {
1071 mutt_buffer_addch(buf, '>');
1072 }
1073
1074 if (addr->group)
1075 {
1076 mutt_buffer_addstr(buf, ": ");
1077 }
1078 }
1079 else
1080 {
1081 mutt_buffer_addch(buf, ';');
1082 }
1083
1084 return mutt_buffer_len(buf) - initial_len;
1085}
1086
1100size_t addrlist_write(const struct AddressList *al, struct Buffer *buf,
1101 bool display, const char *header, int cols)
1102{
1103 if (!buf || !al || TAILQ_EMPTY(al))
1104 return 0;
1105
1106 if (header)
1107 {
1108 mutt_buffer_printf(buf, "%s: ", header);
1109 }
1110
1111 size_t cur_col = mutt_buffer_len(buf);
1112 bool in_group = false;
1113 struct Address *a = NULL;
1114 TAILQ_FOREACH(a, al, entries)
1115 {
1116 struct Address *next = TAILQ_NEXT(a, entries);
1117
1118 if (a->group)
1119 {
1120 in_group = true;
1121 }
1122
1123 // wrap if needed
1124 const size_t cur_len = mutt_buffer_len(buf);
1125 cur_col += mutt_addr_write(buf, a, display);
1126 if (cur_col > cols)
1127 {
1128 mutt_buffer_insert(buf, cur_len, "\n\t");
1129 cur_col = 8;
1130 }
1131
1132 if (!a->group)
1133 {
1134 // group terminator
1135 if (in_group && !a->mailbox && !a->personal)
1136 {
1137 mutt_buffer_addch(buf, ';');
1138 ++cur_col;
1139 in_group = false;
1140 }
1141 if (next && (next->mailbox || next->personal))
1142 {
1143 mutt_buffer_addstr(buf, ", ");
1144 cur_col += 2;
1145 }
1146 if (!next)
1147 {
1148 break;
1149 }
1150 }
1151 }
1152
1153 return mutt_buffer_len(buf);
1154}
1155
1167size_t mutt_addrlist_write_wrap(const struct AddressList *al,
1168 struct Buffer *buf, const char *header)
1169{
1170 return addrlist_write(al, buf, false, header, 74);
1171}
1172
1184size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
1185{
1186 return addrlist_write(al, buf, display, NULL, -1);
1187}
1188
1195size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1196{
1197 if (!al || !list)
1198 return 0;
1199
1200 size_t count = 0;
1201 struct Address *a = NULL;
1202 TAILQ_FOREACH(a, al, entries)
1203 {
1204 struct Buffer buf = { 0 };
1205 mutt_addr_write(&buf, a, true);
1206 if (!mutt_buffer_is_empty(&buf))
1207 {
1208 /* We're taking the ownership of the buffer string here */
1209 mutt_list_insert_tail(list, (char *) mutt_buffer_string(&buf));
1210 count++;
1211 }
1212 }
1213
1214 return count;
1215}
1216
1226void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
1227{
1228 struct Buffer *buf = mutt_buffer_pool_get();
1229 mutt_addrlist_write_wrap(al, buf, header);
1230 fputs(mutt_buffer_string(buf), fp);
1232 fputc('\n', fp);
1233}
1234
1242{
1243 if (!a || !a->mailbox || addr_is_intl(a))
1244 return true;
1245
1246 char *user = NULL;
1247 char *domain = NULL;
1248 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1249 return true;
1250
1251 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1252
1253 FREE(&user);
1254 FREE(&domain);
1255
1256 if (!intl_mailbox)
1257 return false;
1258
1259 addr_set_intl(a, intl_mailbox);
1260 return true;
1261}
1262
1270int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1271{
1272 if (!al)
1273 return 0;
1274
1275 int rc = 0;
1276
1277 if (err)
1278 *err = NULL;
1279
1280 struct Address *a = NULL;
1281 TAILQ_FOREACH(a, al, entries)
1282 {
1283 if (!a->mailbox || addr_is_intl(a))
1284 continue;
1285
1286 char *user = NULL;
1287 char *domain = NULL;
1288 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1289 continue;
1290
1291 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1292
1293 FREE(&user);
1294 FREE(&domain);
1295
1296 if (!intl_mailbox)
1297 {
1298 rc = -1;
1299 if (err && !*err)
1300 *err = mutt_str_dup(a->mailbox);
1301 continue;
1302 }
1303
1304 addr_set_intl(a, intl_mailbox);
1305 }
1306
1307 return rc;
1308}
1309
1317{
1318 if (!a->mailbox)
1319 {
1320 return false;
1321 }
1322
1323 if (addr_is_local(a))
1324 {
1325 return true;
1326 }
1327
1328 char *user = NULL;
1329 char *domain = NULL;
1330 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1331 {
1332 return false;
1333 }
1334
1335 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_NO_FLAGS);
1336 FREE(&user);
1337 FREE(&domain);
1338
1339 if (!local_mailbox)
1340 {
1341 return false;
1342 }
1343
1344 addr_set_local(a, local_mailbox);
1345 return true;
1346}
1347
1353int mutt_addrlist_to_local(struct AddressList *al)
1354{
1355 if (!al)
1356 return 0;
1357
1358 struct Address *a = NULL;
1359 TAILQ_FOREACH(a, al, entries)
1360 {
1362 }
1363 return 0;
1364}
1365
1372void mutt_addrlist_dedupe(struct AddressList *al)
1373{
1374 if (!al)
1375 return;
1376
1377 struct Address *a = NULL;
1378 TAILQ_FOREACH(a, al, entries)
1379 {
1380 if (a->mailbox)
1381 {
1382 struct Address *a2 = TAILQ_NEXT(a, entries);
1383 struct Address *tmp = NULL;
1384
1385 if (a2)
1386 {
1387 TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1388 {
1389 if (a2->mailbox && mutt_istr_equal(a->mailbox, a2->mailbox))
1390 {
1391 mutt_debug(LL_DEBUG2, "Removing %s\n", a2->mailbox);
1392 TAILQ_REMOVE(al, a2, entries);
1393 mutt_addr_free(&a2);
1394 }
1395 }
1396 }
1397 }
1398 }
1399}
1400
1408void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1409{
1410 if (!a || !b)
1411 return;
1412
1413 struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1414
1415 TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1416 {
1417 TAILQ_FOREACH(aa, a, entries)
1418 {
1419 if (mutt_addr_cmp(aa, ab))
1420 {
1421 TAILQ_REMOVE(b, ab, entries);
1422 mutt_addr_free(&ab);
1423 break;
1424 }
1425 }
1426 }
1427}
1428
1435void mutt_addrlist_clear(struct AddressList *al)
1436{
1437 if (!al)
1438 return;
1439
1440 struct Address *a = TAILQ_FIRST(al), *next = NULL;
1441 while (a)
1442 {
1443 next = TAILQ_NEXT(a, entries);
1444 mutt_addr_free(&a);
1445 a = next;
1446 }
1447 TAILQ_INIT(al);
1448}
1449
1455void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1456{
1457 if (al && a)
1458 TAILQ_INSERT_TAIL(al, a, entries);
1459}
1460
1466void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1467{
1468 if (al && a)
1469 TAILQ_INSERT_HEAD(al, a, entries);
1470}
1471
1477bool mutt_addr_uses_unicode(const char *str)
1478{
1479 if (!str)
1480 return false;
1481
1482 while (*str)
1483 {
1484 if ((unsigned char) *str & (1 << 7))
1485 return true;
1486 str++;
1487 }
1488
1489 return false;
1490}
1491
1497bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1498{
1499 if (!al)
1500 {
1501 return false;
1502 }
1503
1504 struct Address *a = NULL;
1505 TAILQ_FOREACH(a, al, entries)
1506 {
1507 if (a->mailbox && !a->group && mutt_addr_uses_unicode(a->mailbox))
1508 return true;
1509 }
1510 return false;
1511}
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition: address.c:900
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition: address.c:398
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:364
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:339
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:737
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:650
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition: address.c:929
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition: address.c:912
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:243
bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
Compare two Address lists for equality.
Definition: address.c:812
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:283
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1435
static const char * parse_mailboxdomain(const char *s, const char *nonspecial, 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:198
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:151
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition: address.c:1195
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:440
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:764
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition: address.c:951
size_t mutt_addr_write(struct Buffer *buf, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition: address.c:1024
const char *const AddressErrors[]
Messages for the error codes in AddressError.
Definition: address.c:64
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition: address.c:967
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition: address.c:1497
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:681
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:864
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1455
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:1167
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1353
size_t mutt_addrlist_write(const struct AddressList *al, struct Buffer *buf, bool display)
Write an Address to a buffer.
Definition: address.c:1184
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1477
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:616
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:1100
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:716
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:42
bool mutt_addr_to_local(struct Address *a)
Convert an Address from Punycode.
Definition: address.c:1316
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1408
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition: address.c:844
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1466
static const char * parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
Extract a comment (parenthesised string)
Definition: address.c:78
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, const char *header)
Wrapper for mutt_write_address()
Definition: address.c:1226
#define is_special(ch)
Is this character special to an email address?
Definition: address.c:48
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1270
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1241
int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
Remove an Address from a list.
Definition: address.c:413
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition: address.c:881
static const char * parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
Extract a quoted string.
Definition: address.c:120
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:986
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1372
AddressError
Possible values for AddressError.
Definition: address.h:50
@ ADDR_ERR_BAD_ROUTE
Bad route.
Definition: address.h:54
@ ADDR_ERR_BAD_ROUTE_ADDR
Bad route address.
Definition: address.h:55
@ ADDR_ERR_BAD_ADDR_SPEC
Bad address specifier.
Definition: address.h:56
@ ADDR_ERR_MISMATCH_PAREN
Mismatched parentheses.
Definition: address.h:52
@ ADDR_ERR_MISMATCH_QUOTE
Mismatches quotes.
Definition: address.h:53
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:409
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
size_t mutt_buffer_insert(struct Buffer *buf, size_t offset, const char *s)
Add a string in the middle of a buffer.
Definition: buffer.c:263
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
Type representing an email address.
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
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:264
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:144
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: logging.h:41
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:695
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:652
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#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:50
#define terminate_buffer(str, strlen)
Definition: string2.h:53
An email address.
Definition: address.h:36
bool group
Group mailbox?
Definition: address.h:39
bool intl_checked
Checked for IDN?
Definition: address.h:41
char * mailbox
Mailbox and host address.
Definition: address.h:38
bool is_intl
International Domain Name.
Definition: address.h:40
char * personal
Real name of address.
Definition: address.h:37
String manipulation buffer.
Definition: buffer.h:34