NeoMutt  2022-04-29-247-gc6aae8
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
1025size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display)
1026{
1027 if (!buf || (buflen == 0) || !addr)
1028 return 0;
1029
1030 if (!addr->personal && !addr->mailbox)
1031 return 0;
1032
1033 size_t len;
1034 char *pbuf = buf;
1035 char *pc = NULL;
1036
1037 buflen--; /* save room for the terminal nul */
1038
1039 if (addr->personal)
1040 {
1041 if (strpbrk(addr->personal, AddressSpecials))
1042 {
1043 if (buflen == 0)
1044 goto done;
1045 *pbuf++ = '"';
1046 buflen--;
1047 for (pc = addr->personal; *pc && (buflen > 0); pc++)
1048 {
1049 if ((*pc == '"') || (*pc == '\\'))
1050 {
1051 *pbuf++ = '\\';
1052 buflen--;
1053 }
1054 if (buflen == 0)
1055 goto done;
1056 *pbuf++ = *pc;
1057 buflen--;
1058 }
1059 if (buflen == 0)
1060 goto done;
1061 *pbuf++ = '"';
1062 buflen--;
1063 }
1064 else
1065 {
1066 if (buflen == 0)
1067 goto done;
1068 len = mutt_str_copy(pbuf, addr->personal, buflen + 1);
1069 pbuf += len;
1070 buflen -= len;
1071 }
1072
1073 if (buflen == 0)
1074 goto done;
1075 *pbuf++ = ' ';
1076 buflen--;
1077 }
1078
1079 if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1080 {
1081 if (buflen == 0)
1082 goto done;
1083 *pbuf++ = '<';
1084 buflen--;
1085 }
1086
1087 if (addr->mailbox)
1088 {
1089 if (buflen == 0)
1090 goto done;
1091 if (mutt_str_equal(addr->mailbox, "@"))
1092 {
1093 *pbuf = '\0';
1094 }
1095 else
1096 {
1097 const char *a = display ? mutt_addr_for_display(addr) : addr->mailbox;
1098 len = mutt_str_copy(pbuf, a, buflen + 1);
1099 pbuf += len;
1100 buflen -= len;
1101 }
1102
1103 if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1104 {
1105 if (buflen == 0)
1106 goto done;
1107 *pbuf++ = '>';
1108 buflen--;
1109 }
1110
1111 if (addr->group)
1112 {
1113 if (buflen == 0)
1114 goto done;
1115 *pbuf++ = ':';
1116 buflen--;
1117 if (buflen == 0)
1118 goto done;
1119 *pbuf++ = ' ';
1120 buflen--;
1121 }
1122 }
1123 else
1124 {
1125 if (buflen == 0)
1126 goto done;
1127 *pbuf++ = ';';
1128 }
1129done:
1130 /* no need to check for length here since we already save space at the
1131 * beginning of this routine */
1132 *pbuf = '\0';
1133
1134 return pbuf - buf;
1135}
1136
1150size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
1151{
1152 if (!buf || (buflen == 0) || !al)
1153 return 0;
1154
1155 size_t len = mutt_str_len(buf);
1156 if (len >= buflen)
1157 {
1158 return 0;
1159 }
1160
1161 char *pbuf = buf + len;
1162 buflen -= len;
1163
1164 struct Address *a = NULL;
1165 TAILQ_FOREACH(a, al, entries)
1166 {
1167 if (len > 0)
1168 {
1169 if (buflen > 1)
1170 {
1171 *pbuf++ = ',';
1172 buflen--;
1173 }
1174 if (buflen > 1)
1175 {
1176 *pbuf++ = ' ';
1177 buflen--;
1178 }
1179 }
1180
1181 if (buflen == 1)
1182 {
1183 break;
1184 }
1185
1186 len = mutt_addr_write(pbuf, buflen, a, display);
1187 pbuf += len;
1188 buflen -= len;
1189 }
1190
1191 *pbuf = '\0';
1192 return pbuf - buf;
1193}
1194
1201size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1202{
1203 if (!al || !list)
1204 return 0;
1205
1206 char addr[256] = { 0 };
1207 size_t count = 0;
1208 struct Address *a = NULL;
1209 TAILQ_FOREACH(a, al, entries)
1210 {
1211 if (mutt_addr_write(addr, sizeof(addr), a, true) != 0)
1212 {
1213 mutt_list_insert_tail(list, strdup(addr));
1214 count++;
1215 }
1216 }
1217
1218 return count;
1219}
1220
1231void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, int start_col, bool display)
1232{
1233 char buf[1024] = { 0 };
1234 int count = 0;
1235 int linelen = start_col;
1236
1237 struct Address *a = NULL;
1238 TAILQ_FOREACH(a, al, entries)
1239 {
1240 buf[0] = '\0';
1241 size_t len = mutt_addr_write(buf, sizeof(buf), a, display);
1242 if (len == 0)
1243 continue;
1244 if (count && (linelen + len > 74))
1245 {
1246 fputs("\n\t", fp);
1247 linelen = len + 8; /* tab is usually about 8 spaces... */
1248 }
1249 else
1250 {
1251 if (count && a->mailbox)
1252 {
1253 fputc(' ', fp);
1254 linelen++;
1255 }
1256 linelen += len;
1257 }
1258 fputs(buf, fp);
1259 struct Address *next = TAILQ_NEXT(a, entries);
1260 if (!a->group && next && next->mailbox)
1261 {
1262 linelen++;
1263 fputc(',', fp);
1264 }
1265 count++;
1266 }
1267 fputc('\n', fp);
1268}
1269
1277{
1278 if (!a || !a->mailbox || addr_is_intl(a))
1279 return true;
1280
1281 char *user = NULL;
1282 char *domain = NULL;
1283 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1284 return true;
1285
1286 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1287
1288 FREE(&user);
1289 FREE(&domain);
1290
1291 if (!intl_mailbox)
1292 return false;
1293
1294 addr_set_intl(a, intl_mailbox);
1295 return true;
1296}
1297
1305int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1306{
1307 if (!al)
1308 return 0;
1309
1310 int rc = 0;
1311
1312 if (err)
1313 *err = NULL;
1314
1315 struct Address *a = NULL;
1316 TAILQ_FOREACH(a, al, entries)
1317 {
1318 if (!a->mailbox || addr_is_intl(a))
1319 continue;
1320
1321 char *user = NULL;
1322 char *domain = NULL;
1323 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1324 continue;
1325
1326 char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1327
1328 FREE(&user);
1329 FREE(&domain);
1330
1331 if (!intl_mailbox)
1332 {
1333 rc = -1;
1334 if (err && !*err)
1335 *err = mutt_str_dup(a->mailbox);
1336 continue;
1337 }
1338
1339 addr_set_intl(a, intl_mailbox);
1340 }
1341
1342 return rc;
1343}
1344
1352{
1353 if (!a->mailbox)
1354 {
1355 return false;
1356 }
1357
1358 if (addr_is_local(a))
1359 {
1360 return true;
1361 }
1362
1363 char *user = NULL;
1364 char *domain = NULL;
1365 if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1366 {
1367 return false;
1368 }
1369
1370 char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_NO_FLAGS);
1371 FREE(&user);
1372 FREE(&domain);
1373
1374 if (!local_mailbox)
1375 {
1376 return false;
1377 }
1378
1379 addr_set_local(a, local_mailbox);
1380 return true;
1381}
1382
1388int mutt_addrlist_to_local(struct AddressList *al)
1389{
1390 if (!al)
1391 return 0;
1392
1393 struct Address *a = NULL;
1394 TAILQ_FOREACH(a, al, entries)
1395 {
1397 }
1398 return 0;
1399}
1400
1407void mutt_addrlist_dedupe(struct AddressList *al)
1408{
1409 if (!al)
1410 return;
1411
1412 struct Address *a = NULL;
1413 TAILQ_FOREACH(a, al, entries)
1414 {
1415 if (a->mailbox)
1416 {
1417 struct Address *a2 = TAILQ_NEXT(a, entries);
1418 struct Address *tmp = NULL;
1419
1420 if (a2)
1421 {
1422 TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1423 {
1424 if (a2->mailbox && mutt_istr_equal(a->mailbox, a2->mailbox))
1425 {
1426 mutt_debug(LL_DEBUG2, "Removing %s\n", a2->mailbox);
1427 TAILQ_REMOVE(al, a2, entries);
1428 mutt_addr_free(&a2);
1429 }
1430 }
1431 }
1432 }
1433 }
1434}
1435
1443void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1444{
1445 if (!a || !b)
1446 return;
1447
1448 struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1449
1450 TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1451 {
1452 TAILQ_FOREACH(aa, a, entries)
1453 {
1454 if (mutt_addr_cmp(aa, ab))
1455 {
1456 TAILQ_REMOVE(b, ab, entries);
1457 mutt_addr_free(&ab);
1458 break;
1459 }
1460 }
1461 }
1462}
1463
1470void mutt_addrlist_clear(struct AddressList *al)
1471{
1472 if (!al)
1473 return;
1474
1475 struct Address *a = TAILQ_FIRST(al), *next = NULL;
1476 while (a)
1477 {
1478 next = TAILQ_NEXT(a, entries);
1479 mutt_addr_free(&a);
1480 a = next;
1481 }
1482 TAILQ_INIT(al);
1483}
1484
1490void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1491{
1492 if (al && a)
1493 TAILQ_INSERT_TAIL(al, a, entries);
1494}
1495
1501void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1502{
1503 if (al && a)
1504 TAILQ_INSERT_HEAD(al, a, entries);
1505}
1506
1512bool mutt_addr_uses_unicode(const char *str)
1513{
1514 if (!str)
1515 return false;
1516
1517 while (*str)
1518 {
1519 if ((unsigned char) *str & (1 << 7))
1520 return true;
1521 str++;
1522 }
1523
1524 return false;
1525}
1526
1532bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1533{
1534 if (!al)
1535 {
1536 return false;
1537 }
1538
1539 struct Address *a = NULL;
1540 TAILQ_FOREACH(a, al, entries)
1541 {
1542 if (a->mailbox && !a->group && mutt_addr_uses_unicode(a->mailbox))
1543 return true;
1544 }
1545 return false;
1546}
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:1470
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:1201
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
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:1532
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:1490
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1150
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1388
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1512
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:616
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:1351
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1443
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_write_file(const struct AddressList *al, FILE *fp, int start_col, bool display)
Wrapper for mutt_write_address()
Definition: address.c:1231
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1501
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
#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:1305
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1276
size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition: address.c:1025
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:1407
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
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
static unsigned char * pbuf
Cached PGP data packet.
Definition: pgppacket.c:38
#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_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