NeoMutt  2020-03-20-65-g141838
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)
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_strdup(token);
264 
265  if (*commentlen && !addr->personal)
266  {
267  terminate_string(comment, *commentlen, commentmax);
268  addr->personal = mutt_str_strdup(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_strdup("@");
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_strdup(personal);
402  a->mailbox = mutt_str_strdup(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_str_strcasecmp(mailbox, a->mailbox) == 0)
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_strdup(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_strdup(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_strdup(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  s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
564  if (!s)
565  {
567  return 0;
568  }
569  break;
570  } // switch (*s)
571 
572  ws_pending = mutt_str_is_email_wsp(*s);
574  } // while (*s)
575 
576  if (phraselen != 0)
577  {
578  terminate_buffer(phrase, phraselen);
579  terminate_buffer(comment, commentlen);
580  if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
581  {
582  parsed++;
583  }
584  }
585  else if (commentlen != 0)
586  {
587  struct Address *last = TAILQ_LAST(al, AddressList);
588  if (last && !last->personal)
589  {
590  terminate_buffer(comment, commentlen);
591  last->personal = mutt_str_strdup(comment);
592  }
593  }
594 
595  return parsed;
596 }
597 
607 int mutt_addrlist_parse2(struct AddressList *al, const char *s)
608 {
609  if (!s || !*s)
610  return 0;
611 
612  int parsed = 0;
613 
614  /* check for a simple whitespace separated list of addresses */
615  if (!strpbrk(s, "\"<>():;,\\"))
616  {
617  char *copy = mutt_str_strdup(s);
618  char *r = copy;
619  while ((r = strtok(r, " \t")))
620  {
621  parsed += mutt_addrlist_parse(al, r);
622  r = NULL;
623  }
624  FREE(&copy);
625  }
626  else
627  parsed = mutt_addrlist_parse(al, s);
628 
629  return parsed;
630 }
631 
641 void mutt_addrlist_qualify(struct AddressList *al, const char *host)
642 {
643  if (!al || !host || !*host)
644  return;
645 
646  struct Address *a = NULL;
647  TAILQ_FOREACH(a, al, entries)
648  {
649  if (!a->group && a->mailbox && !strchr(a->mailbox, '@'))
650  {
651  char *p = mutt_mem_malloc(mutt_str_strlen(a->mailbox) + mutt_str_strlen(host) + 2);
652  sprintf(p, "%s@%s", a->mailbox, host);
653  FREE(&a->mailbox);
654  a->mailbox = p;
655  }
656  }
657 }
658 
672 void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
673 {
674  if (!buf || !value || !specials)
675  return;
676 
677  if (strpbrk(value, specials))
678  {
679  char tmp[256];
680  char *pc = tmp;
681  size_t tmplen = sizeof(tmp) - 3;
682 
683  *pc++ = '"';
684  for (; *value && (tmplen > 1); value++)
685  {
686  if ((*value == '\\') || (*value == '"'))
687  {
688  *pc++ = '\\';
689  tmplen--;
690  }
691  *pc++ = *value;
692  tmplen--;
693  }
694  *pc++ = '"';
695  *pc = '\0';
696  mutt_str_strfcpy(buf, tmp, buflen);
697  }
698  else
699  mutt_str_strfcpy(buf, value, buflen);
700 }
701 
707 struct Address *mutt_addr_copy(const struct Address *addr)
708 {
709  if (!addr)
710  return NULL;
711 
712  struct Address *p = mutt_addr_new();
713 
714  p->personal = mutt_str_strdup(addr->personal);
715  p->mailbox = mutt_str_strdup(addr->mailbox);
716  p->group = addr->group;
717  p->is_intl = addr->is_intl;
718  p->intl_checked = addr->intl_checked;
719  return p;
720 }
721 
728 void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
729 {
730  if (!dst || !src)
731  return;
732 
733  struct Address *a = NULL;
734  TAILQ_FOREACH(a, src, entries)
735  {
736  struct Address *next = TAILQ_NEXT(a, entries);
737  if (prune && a->group && (!next || !next->mailbox))
738  {
739  /* ignore this element of the list */
740  }
741  else
742  {
744  }
745  }
746 }
747 
755 bool mutt_addr_valid_msgid(const char *msgid)
756 {
757  /* msg-id = "<" addr-spec ">"
758  * addr-spec = local-part "@" domain
759  * local-part = word *("." word)
760  * word = atom / quoted-string
761  * atom = 1*<any CHAR except specials, SPACE and CTLs>
762  * CHAR = ( 0.-127. )
763  * specials = "(" / ")" / "<" / ">" / "@"
764  * / "," / ";" / ":" / "\" / <">
765  * / "." / "[" / "]"
766  * SPACE = ( 32. )
767  * CTLS = ( 0.-31., 127.)
768  * quoted-string = <"> *(qtext/quoted-pair) <">
769  * qtext = <any CHAR except <">, "\" and CR>
770  * CR = ( 13. )
771  * quoted-pair = "\" CHAR
772  * domain = sub-domain *("." sub-domain)
773  * sub-domain = domain-ref / domain-literal
774  * domain-ref = atom
775  * domain-literal = "[" *(dtext / quoted-pair) "]"
776  */
777 
778  if (!msgid || !*msgid)
779  return false;
780 
781  size_t l = mutt_str_strlen(msgid);
782  if (l < 5) /* <atom@atom> */
783  return false;
784  if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
785  return false;
786  if (!(strrchr(msgid, '@')))
787  return false;
788 
789  /* TODO: complete parser */
790  for (size_t i = 0; i < l; i++)
791  if ((unsigned char) msgid[i] > 127)
792  return false;
793 
794  return true;
795 }
796 
803 bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
804 {
805  if (!ala || !alb)
806  {
807  return !(ala || alb);
808  }
809 
810  struct Address *ana = TAILQ_FIRST(ala);
811  struct Address *anb = TAILQ_FIRST(alb);
812 
813  while (ana && anb)
814  {
815  if ((mutt_str_strcmp(ana->mailbox, anb->mailbox) != 0) ||
816  (mutt_str_strcmp(ana->personal, anb->personal) != 0))
817  {
818  break;
819  }
820 
821  ana = TAILQ_NEXT(ana, entries);
822  anb = TAILQ_NEXT(anb, entries);
823  }
824 
825  return !(ana || anb);
826 }
827 
835 int mutt_addrlist_count_recips(const struct AddressList *al)
836 {
837  if (!al)
838  return 0;
839 
840  int c = 0;
841  struct Address *a = NULL;
842  TAILQ_FOREACH(a, al, entries)
843  {
844  c += (a->mailbox && !a->group);
845  }
846  return c;
847 }
848 
855 bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
856 {
857  if (!a || !b)
858  return false;
859  if (!a->mailbox || !b->mailbox)
860  return false;
861  if (mutt_str_strcasecmp(a->mailbox, b->mailbox) != 0)
862  return false;
863  return true;
864 }
865 
872 bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
873 {
874  if (!needle || !haystack)
875  return false;
876 
877  struct Address *a = NULL;
878  TAILQ_FOREACH(a, haystack, entries)
879  {
880  if (mutt_addr_cmp(needle, a))
881  return true;
882  }
883  return false;
884 }
885 
891 static bool addr_is_intl(const struct Address *a)
892 {
893  if (!a)
894  return false;
895  return a->intl_checked && a->is_intl;
896 }
897 
903 static bool addr_is_local(const struct Address *a)
904 {
905  if (!a)
906  return false;
907  return a->intl_checked && !a->is_intl;
908 }
909 
920 static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
921 {
922  if (!mbox || !user || !domain)
923  return -1;
924 
925  char *ptr = strchr(mbox, '@');
926 
927  /* Fail if '@' is missing, at the start, or at the end */
928  if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
929  return -1;
930 
931  *user = mutt_str_substr_dup(mbox, ptr);
932  *domain = mutt_str_strdup(ptr + 1);
933 
934  return 0;
935 }
936 
942 static void addr_set_intl(struct Address *a, char *intl_mailbox)
943 {
944  if (!a)
945  return;
946 
947  FREE(&a->mailbox);
948  a->mailbox = intl_mailbox;
949  a->intl_checked = true;
950  a->is_intl = true;
951 }
952 
958 static void addr_set_local(struct Address *a, char *local_mailbox)
959 {
960  if (!a)
961  return;
962 
963  FREE(&a->mailbox);
964  a->mailbox = local_mailbox;
965  a->intl_checked = true;
966  a->is_intl = false;
967 }
968 
977 const char *mutt_addr_for_display(const struct Address *a)
978 {
979  if (!a)
980  return NULL;
981 
982  char *user = NULL, *domain = NULL;
983  static char *buf = NULL;
984 
985  if (!a->mailbox || addr_is_local(a))
986  return a->mailbox;
987 
988  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
989  return a->mailbox;
990 
991  char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
992 
993  FREE(&user);
994  FREE(&domain);
995 
996  if (!local_mailbox)
997  return a->mailbox;
998 
999  mutt_str_replace(&buf, local_mailbox);
1000  FREE(&local_mailbox);
1001 
1002  return buf;
1003 }
1004 
1016 size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display)
1017 {
1018  if (!buf || buflen == 0 || !addr)
1019  return 0;
1020 
1021  size_t len;
1022  char *pbuf = buf;
1023  char *pc = NULL;
1024 
1025  buflen--; /* save room for the terminal nul */
1026 
1027  if (addr->personal)
1028  {
1029  if (strpbrk(addr->personal, AddressSpecials))
1030  {
1031  if (buflen == 0)
1032  goto done;
1033  *pbuf++ = '"';
1034  buflen--;
1035  for (pc = addr->personal; *pc && (buflen > 0); pc++)
1036  {
1037  if ((*pc == '"') || (*pc == '\\'))
1038  {
1039  *pbuf++ = '\\';
1040  buflen--;
1041  }
1042  if (buflen == 0)
1043  goto done;
1044  *pbuf++ = *pc;
1045  buflen--;
1046  }
1047  if (buflen == 0)
1048  goto done;
1049  *pbuf++ = '"';
1050  buflen--;
1051  }
1052  else
1053  {
1054  if (buflen == 0)
1055  goto done;
1056  len = mutt_str_strfcpy(pbuf, addr->personal, buflen + 1 /* strfcpy terminates */);
1057  pbuf += len;
1058  buflen -= len;
1059  }
1060 
1061  if (buflen == 0)
1062  goto done;
1063  *pbuf++ = ' ';
1064  buflen--;
1065  }
1066 
1067  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1068  {
1069  if (buflen == 0)
1070  goto done;
1071  *pbuf++ = '<';
1072  buflen--;
1073  }
1074 
1075  if (addr->mailbox)
1076  {
1077  if (buflen == 0)
1078  goto done;
1079  if (mutt_str_strcmp(addr->mailbox, "@") == 0)
1080  {
1081  *pbuf = '\0';
1082  }
1083  else
1084  {
1085  const char *a = display ? mutt_addr_for_display(addr) : addr->mailbox;
1086  len = mutt_str_strfcpy(pbuf, a, buflen + 1 /* strfcpy terminates */);
1087  pbuf += len;
1088  buflen -= len;
1089  }
1090 
1091  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1092  {
1093  if (buflen == 0)
1094  goto done;
1095  *pbuf++ = '>';
1096  buflen--;
1097  }
1098 
1099  if (addr->group)
1100  {
1101  if (buflen == 0)
1102  goto done;
1103  *pbuf++ = ':';
1104  buflen--;
1105  if (buflen == 0)
1106  goto done;
1107  *pbuf++ = ' ';
1108  buflen--;
1109  }
1110  }
1111  else
1112  {
1113  if (buflen == 0)
1114  goto done;
1115  *pbuf++ = ';';
1116  }
1117 done:
1118  /* no need to check for length here since we already save space at the
1119  * beginning of this routine */
1120  *pbuf = '\0';
1121 
1122  return pbuf - buf;
1123 }
1124 
1138 size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
1139 {
1140  if (!buf || buflen == 0 || !al)
1141  return 0;
1142 
1143  size_t len = mutt_str_strlen(buf);
1144  if (len >= buflen)
1145  {
1146  return 0;
1147  }
1148 
1149  char *pbuf = buf + len;
1150  buflen -= len;
1151 
1152  struct Address *a = NULL;
1153  TAILQ_FOREACH(a, al, entries)
1154  {
1155  if (len > 0)
1156  {
1157  if (buflen > 1)
1158  {
1159  *pbuf++ = ',';
1160  buflen--;
1161  }
1162  if (buflen > 1)
1163  {
1164  *pbuf++ = ' ';
1165  buflen--;
1166  }
1167  }
1168 
1169  if (buflen == 1)
1170  {
1171  break;
1172  }
1173 
1174  len = mutt_addr_write(pbuf, buflen, a, display);
1175  pbuf += len;
1176  buflen -= len;
1177  }
1178 
1179  *pbuf = '\0';
1180  return pbuf - buf;
1181 }
1182 
1189 {
1190  if (!a || !a->mailbox || addr_is_intl(a))
1191  return true;
1192 
1193  char *user = NULL;
1194  char *domain = NULL;
1195  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1196  return true;
1197 
1198  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1199 
1200  FREE(&user);
1201  FREE(&domain);
1202 
1203  if (!intl_mailbox)
1204  return false;
1205 
1206  addr_set_intl(a, intl_mailbox);
1207  return true;
1208 }
1209 
1217 int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1218 {
1219  if (!al)
1220  return 0;
1221 
1222  int rc = 0;
1223 
1224  if (err)
1225  *err = NULL;
1226 
1227  struct Address *a = NULL;
1228  TAILQ_FOREACH(a, al, entries)
1229  {
1230  if (!a->mailbox || addr_is_intl(a))
1231  continue;
1232 
1233  char *user = NULL;
1234  char *domain = NULL;
1235  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1236  continue;
1237 
1238  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1239 
1240  FREE(&user);
1241  FREE(&domain);
1242 
1243  if (!intl_mailbox)
1244  {
1245  rc = -1;
1246  if (err && !*err)
1247  *err = mutt_str_strdup(a->mailbox);
1248  continue;
1249  }
1250 
1251  addr_set_intl(a, intl_mailbox);
1252  }
1253 
1254  return rc;
1255 }
1256 
1263 {
1264  if (!a->mailbox)
1265  {
1266  return false;
1267  }
1268 
1269  if (addr_is_local(a))
1270  {
1271  return true;
1272  }
1273 
1274  char *user = NULL;
1275  char *domain = NULL;
1276  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1277  {
1278  return false;
1279  }
1280 
1281  char *local_mailbox = mutt_idna_intl_to_local(user, domain, 0);
1282  FREE(&user);
1283  FREE(&domain);
1284 
1285  if (!local_mailbox)
1286  {
1287  return false;
1288  }
1289 
1290  addr_set_local(a, local_mailbox);
1291  return true;
1292 }
1293 
1299 int mutt_addrlist_to_local(struct AddressList *al)
1300 {
1301  if (!al)
1302  return 0;
1303 
1304  struct Address *a = NULL;
1305  TAILQ_FOREACH(a, al, entries)
1306  {
1307  mutt_addr_to_local(a);
1308  }
1309  return 0;
1310 }
1311 
1319 void mutt_addrlist_dedupe(struct AddressList *al)
1320 {
1321  if (!al)
1322  return;
1323 
1324  struct Address *a = NULL;
1325  TAILQ_FOREACH(a, al, entries)
1326  {
1327  if (a->mailbox)
1328  {
1329  struct Address *a2 = TAILQ_NEXT(a, entries);
1330  struct Address *tmp = NULL;
1331 
1332  if (a2)
1333  {
1334  TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1335  {
1336  if (a2->mailbox && (mutt_str_strcasecmp(a->mailbox, a2->mailbox) == 0))
1337  {
1338  mutt_debug(LL_DEBUG2, "Removing %s\n", a2->mailbox);
1339  TAILQ_REMOVE(al, a2, entries);
1340  mutt_addr_free(&a2);
1341  }
1342  }
1343  }
1344  }
1345  }
1346 }
1347 
1355 void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1356 {
1357  if (!a || !b)
1358  return;
1359 
1360  struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1361 
1362  TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1363  {
1364  TAILQ_FOREACH(aa, a, entries)
1365  {
1366  if (mutt_addr_cmp(aa, ab))
1367  {
1368  TAILQ_REMOVE(b, ab, entries);
1369  mutt_addr_free(&ab);
1370  break;
1371  }
1372  }
1373  }
1374 }
1375 
1382 void mutt_addrlist_clear(struct AddressList *al)
1383 {
1384  if (!al)
1385  return;
1386 
1387  struct Address *a = TAILQ_FIRST(al), *next = NULL;
1388  while (a)
1389  {
1390  next = TAILQ_NEXT(a, entries);
1391  mutt_addr_free(&a);
1392  a = next;
1393  }
1394  TAILQ_INIT(al);
1395 }
1396 
1402 void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1403 {
1404  if (al && a)
1405  TAILQ_INSERT_TAIL(al, a, entries);
1406 }
1407 
1413 void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1414 {
1415  if (al && a)
1416  TAILQ_INSERT_HEAD(al, a, entries);
1417 }
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:958
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition: address.c:942
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1299
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:1382
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:728
Handling of international domain names.
Representation of an email address.
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:728
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:803
char * mailbox
Mailbox and host address.
Definition: address.h:37
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:689
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:707
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:607
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:1262
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition: address.c:920
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:1319
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:977
#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
#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:1016
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
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:834
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:811
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:60
static bool addr_is_local(const struct Address *a)
Does the Address have NO IDN components.
Definition: address.c:903
#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:855
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:773
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1413
#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:799
bool mutt_addrlist_search(const struct AddressList *haystack, const struct Address *needle)
Search for an e-mail address in a list.
Definition: address.c:872
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:453
int mutt_addrlist_count_recips(const struct AddressList *al)
Count the number of Addresses with valid recipients.
Definition: address.c:835
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:789
char * personal
Real name of address.
Definition: address.h:36
void mutt_addrlist_qualify(struct AddressList *al, const char *host)
Expand local names in an Address list using a hostname.
Definition: address.c:641
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1188
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1217
bool group
Group mailbox?
Definition: address.h:38
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition: address.c:891
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:755
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:651
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1355
#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.
char * mutt_str_substr_dup(const char *begin, const char *end)
Duplicate a sub-string.
Definition: string.c:602
static unsigned char * pbuf
Cache PGP data packet.
Definition: pgppacket.c:38
#define terminate_string(str, strlen, buflen)
Definition: string2.h:51
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:1138
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:638
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:672
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:1402