NeoMutt  2020-06-26-30-g76c339
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 
42 const char AddressSpecials[] = "@.,:;<>[]\\\"()";
43 
48 #define is_special(ch) strchr(AddressSpecials, ch)
49 
57 int AddressError = 0;
58 
64 const char *const AddressErrors[] = {
65  "out of memory", "mismatched parentheses", "mismatched quotes",
66  "bad route in <>", "bad address in <>", "bad address spec",
67 };
68 
78 static 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 
120 static 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 
151 static 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 
198 static 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 
243 static 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 
283 static 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];
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 
339 static 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];
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 
364 static 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 
385 struct Address *mutt_addr_new(void)
386 {
387  return mutt_mem_calloc(1, sizeof(struct Address));
388 }
389 
398 struct Address *mutt_addr_create(const char *personal, const char *mailbox)
399 {
400  struct Address *a = mutt_addr_new();
401  a->personal = mutt_str_dup(personal);
402  a->mailbox = mutt_str_dup(mailbox);
403  return a;
404 }
405 
413 int 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  {
425  if (mutt_istr_equal(mailbox, a->mailbox))
426  {
427  TAILQ_REMOVE(al, a, entries);
428  mutt_addr_free(&a);
429  rc = 0;
430  }
431  }
432 
433  return rc;
434 }
435 
440 void 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 
458 int 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)
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;
534  mutt_addrlist_append(al, a);
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  }
553  mutt_addrlist_append(al, a);
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)
598  {
599  terminate_buffer(comment, commentlen);
600  last->personal = mutt_str_dup(comment);
601  }
602  }
603 
604  return parsed;
605 }
606 
616 int 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 
650 void 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 
681 void 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];
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 
716 struct 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 
737 void 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 
764 bool 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 
812 bool 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 
844 int 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 
864 bool 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 
881 bool 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 
900 static 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 
912 static 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 
929 static 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 
951 static 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 
967 static 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 
986 const 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 
1025 size_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  size_t len;
1031  char *pbuf = buf;
1032  char *pc = NULL;
1033 
1034  buflen--; /* save room for the terminal nul */
1035 
1036  if (addr->personal)
1037  {
1038  if (strpbrk(addr->personal, AddressSpecials))
1039  {
1040  if (buflen == 0)
1041  goto done;
1042  *pbuf++ = '"';
1043  buflen--;
1044  for (pc = addr->personal; *pc && (buflen > 0); pc++)
1045  {
1046  if ((*pc == '"') || (*pc == '\\'))
1047  {
1048  *pbuf++ = '\\';
1049  buflen--;
1050  }
1051  if (buflen == 0)
1052  goto done;
1053  *pbuf++ = *pc;
1054  buflen--;
1055  }
1056  if (buflen == 0)
1057  goto done;
1058  *pbuf++ = '"';
1059  buflen--;
1060  }
1061  else
1062  {
1063  if (buflen == 0)
1064  goto done;
1065  len = mutt_str_copy(pbuf, addr->personal, buflen + 1);
1066  pbuf += len;
1067  buflen -= len;
1068  }
1069 
1070  if (buflen == 0)
1071  goto done;
1072  *pbuf++ = ' ';
1073  buflen--;
1074  }
1075 
1076  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1077  {
1078  if (buflen == 0)
1079  goto done;
1080  *pbuf++ = '<';
1081  buflen--;
1082  }
1083 
1084  if (addr->mailbox)
1085  {
1086  if (buflen == 0)
1087  goto done;
1088  if (mutt_str_equal(addr->mailbox, "@"))
1089  {
1090  *pbuf = '\0';
1091  }
1092  else
1093  {
1094  const char *a = display ? mutt_addr_for_display(addr) : addr->mailbox;
1095  len = mutt_str_copy(pbuf, a, buflen + 1);
1096  pbuf += len;
1097  buflen -= len;
1098  }
1099 
1100  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1101  {
1102  if (buflen == 0)
1103  goto done;
1104  *pbuf++ = '>';
1105  buflen--;
1106  }
1107 
1108  if (addr->group)
1109  {
1110  if (buflen == 0)
1111  goto done;
1112  *pbuf++ = ':';
1113  buflen--;
1114  if (buflen == 0)
1115  goto done;
1116  *pbuf++ = ' ';
1117  buflen--;
1118  }
1119  }
1120  else
1121  {
1122  if (buflen == 0)
1123  goto done;
1124  *pbuf++ = ';';
1125  }
1126 done:
1127  /* no need to check for length here since we already save space at the
1128  * beginning of this routine */
1129  *pbuf = '\0';
1130 
1131  return pbuf - buf;
1132 }
1133 
1147 size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
1148 {
1149  if (!buf || buflen == 0 || !al)
1150  return 0;
1151 
1152  size_t len = mutt_str_len(buf);
1153  if (len >= buflen)
1154  {
1155  return 0;
1156  }
1157 
1158  char *pbuf = buf + len;
1159  buflen -= len;
1160 
1161  struct Address *a = NULL;
1162  TAILQ_FOREACH(a, al, entries)
1163  {
1164  if (len > 0)
1165  {
1166  if (buflen > 1)
1167  {
1168  *pbuf++ = ',';
1169  buflen--;
1170  }
1171  if (buflen > 1)
1172  {
1173  *pbuf++ = ' ';
1174  buflen--;
1175  }
1176  }
1177 
1178  if (buflen == 1)
1179  {
1180  break;
1181  }
1182 
1183  len = mutt_addr_write(pbuf, buflen, a, display);
1184  pbuf += len;
1185  buflen -= len;
1186  }
1187 
1188  *pbuf = '\0';
1189  return pbuf - buf;
1190 }
1191 
1198 size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1199 {
1200  if (!al || !list)
1201  return 0;
1202 
1203  char addr[256];
1204  size_t count = 0;
1205  struct Address *a = NULL;
1206  TAILQ_FOREACH(a, al, entries)
1207  {
1208  mutt_addr_write(addr, sizeof(addr), a, true);
1209  mutt_list_insert_tail(list, strdup(addr));
1210  count++;
1211  }
1212 
1213  return count;
1214 }
1215 
1222 {
1223  if (!a || !a->mailbox || addr_is_intl(a))
1224  return true;
1225 
1226  char *user = NULL;
1227  char *domain = NULL;
1228  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1229  return true;
1230 
1231  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1232 
1233  FREE(&user);
1234  FREE(&domain);
1235 
1236  if (!intl_mailbox)
1237  return false;
1238 
1239  addr_set_intl(a, intl_mailbox);
1240  return true;
1241 }
1242 
1250 int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1251 {
1252  if (!al)
1253  return 0;
1254 
1255  int rc = 0;
1256 
1257  if (err)
1258  *err = NULL;
1259 
1260  struct Address *a = NULL;
1261  TAILQ_FOREACH(a, al, entries)
1262  {
1263  if (!a->mailbox || addr_is_intl(a))
1264  continue;
1265 
1266  char *user = NULL;
1267  char *domain = NULL;
1268  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1269  continue;
1270 
1271  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1272 
1273  FREE(&user);
1274  FREE(&domain);
1275 
1276  if (!intl_mailbox)
1277  {
1278  rc = -1;
1279  if (err && !*err)
1280  *err = mutt_str_dup(a->mailbox);
1281  continue;
1282  }
1283 
1284  addr_set_intl(a, intl_mailbox);
1285  }
1286 
1287  return rc;
1288 }
1289 
1296 {
1297  if (!a->mailbox)
1298  {
1299  return false;
1300  }
1301 
1302  if (addr_is_local(a))
1303  {
1304  return true;
1305  }
1306 
1307  char *user = NULL;
1308  char *domain = NULL;
1309  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1310  {
1311  return false;
1312  }
1313 
1314  char *local_mailbox = mutt_idna_intl_to_local(user, domain, 0);
1315  FREE(&user);
1316  FREE(&domain);
1317 
1318  if (!local_mailbox)
1319  {
1320  return false;
1321  }
1322 
1323  addr_set_local(a, local_mailbox);
1324  return true;
1325 }
1326 
1332 int mutt_addrlist_to_local(struct AddressList *al)
1333 {
1334  if (!al)
1335  return 0;
1336 
1337  struct Address *a = NULL;
1338  TAILQ_FOREACH(a, al, entries)
1339  {
1340  mutt_addr_to_local(a);
1341  }
1342  return 0;
1343 }
1344 
1351 void mutt_addrlist_dedupe(struct AddressList *al)
1352 {
1353  if (!al)
1354  return;
1355 
1356  struct Address *a = NULL;
1357  TAILQ_FOREACH(a, al, entries)
1358  {
1359  if (a->mailbox)
1360  {
1361  struct Address *a2 = TAILQ_NEXT(a, entries);
1362  struct Address *tmp = NULL;
1363 
1364  if (a2)
1365  {
1366  TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1367  {
1368  if (a2->mailbox && mutt_istr_equal(a->mailbox, a2->mailbox))
1369  {
1370  mutt_debug(LL_DEBUG2, "Removing %s\n", a2->mailbox);
1371  TAILQ_REMOVE(al, a2, entries);
1372  mutt_addr_free(&a2);
1373  }
1374  }
1375  }
1376  }
1377  }
1378 }
1379 
1387 void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1388 {
1389  if (!a || !b)
1390  return;
1391 
1392  struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1393 
1394  TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1395  {
1396  TAILQ_FOREACH(aa, a, entries)
1397  {
1398  if (mutt_addr_cmp(aa, ab))
1399  {
1400  TAILQ_REMOVE(b, ab, entries);
1401  mutt_addr_free(&ab);
1402  break;
1403  }
1404  }
1405  }
1406 }
1407 
1414 void mutt_addrlist_clear(struct AddressList *al)
1415 {
1416  if (!al)
1417  return;
1418 
1419  struct Address *a = TAILQ_FIRST(al), *next = NULL;
1420  while (a)
1421  {
1422  next = TAILQ_NEXT(a, entries);
1423  mutt_addr_free(&a);
1424  a = next;
1425  }
1426  TAILQ_INIT(al);
1427 }
1428 
1434 void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1435 {
1436  if (al && a)
1437  TAILQ_INSERT_TAIL(al, a, entries);
1438 }
1439 
1445 void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1446 {
1447  if (al && a)
1448  TAILQ_INSERT_HEAD(al, a, entries);
1449 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:879
int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
Remove an Address from a list.
Definition: address.c:413
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define TAILQ_FIRST(head)
Definition: queue.h:716
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
#define TAILQ_LAST(head, headname)
Definition: queue.h:812
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition: address.c:967
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition: address.c:951
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1332
const char AddressSpecials[]
Characters with special meaning for email addresses.
Definition: address.c:42
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1414
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_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:737
Handling of international domain names.
Representation of an email address.
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:728
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:375
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
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:385
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
An email address.
Definition: address.h:34
bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
Compare two Address lists for equality.
Definition: address.c:812
char * mailbox
Mailbox and host address.
Definition: address.h:37
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:716
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:616
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:440
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition: address.c:398
Bad route address.
Definition: address.h:54
bool mutt_addr_to_local(struct Address *a)
Convert an Address from Punycode.
Definition: address.c:1295
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 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
bool is_intl
International Domain Name.
Definition: address.h:39
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1351
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:986
#define is_special(ch)
Is this character special to an email address?
Definition: address.c:48
Log at debug level 2.
Definition: logging.h:41
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:551
#define TAILQ_INIT(head)
Definition: queue.h:758
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
static const char * parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
Extract a quoted string.
Definition: address.c:120
bool intl_checked
Checked for IDN?
Definition: address.h:40
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:891
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:834
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
Mismatches quotes.
Definition: address.h:52
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
AddressError
possible values for AddressError
Definition: address.h:48
bool mutt_str_is_email_wsp(char c)
Is this a whitespace character (for an email header)
Definition: string.c:767
Bad address specifier.
Definition: address.h:55
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define terminate_buffer(str, strlen)
Definition: string2.h:53
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition: address.c:912
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)
Definition: queue.h:733
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:864
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1445
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:802
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:751
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
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:450
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition: address.c:1198
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition: address.c:844
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:789
char * personal
Real name of address.
Definition: address.h:36
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:639
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:650
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1221
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1250
bool group
Group mailbox?
Definition: address.h:38
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:724
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition: address.c:900
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:764
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1387
#define FREE(x)
Definition: memory.h:40
#define MI_MAY_BE_IRREVERSIBLE
Definition: idna2.h:32
char * mutt_idna_local_to_intl(const char *user, const char *domain)
Convert an email&#39;s domain to Punycode.
Definition: idna.c:265
const char *const AddressErrors[]
Messages for the error codes in AddressError.
Definition: address.c:64
#define TAILQ_NEXT(elm, field)
Definition: queue.h:825
Mismatched parentheses.
Definition: address.h:51
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Bad route.
Definition: address.h:53
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
Convenience wrapper for the library headers.
static unsigned char * pbuf
Cache PGP data packet.
Definition: pgppacket.c:38
#define terminate_string(str, strlen, buflen)
Definition: string2.h:50
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:1147
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
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
char * mutt_idna_intl_to_local(const char *user, const char *domain, int flags)
Convert an email&#39;s domain from Punycode.
Definition: idna.c:147
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1434