NeoMutt  2018-07-16 +2481-68dcde
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/mutt.h"
36 #include "address.h"
37 #include "idna2.h"
38 
42 const char AddressSpecials[] = "@.,:;<>[]\\\"()";
43 
47 #define is_special(ch) strchr(AddressSpecials, ch)
48 
56 int AddressError = 0;
57 
63 const char *const AddressErrors[] = {
64  "out of memory", "mismatched parentheses", "mismatched quotes",
65  "bad route in <>", "bad address in <>", "bad address spec",
66 };
67 
77 static const char *parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
78 {
79  int level = 1;
80 
81  while (*s && level)
82  {
83  if (*s == '(')
84  level++;
85  else if (*s == ')')
86  {
87  if (--level == 0)
88  {
89  s++;
90  break;
91  }
92  }
93  else if (*s == '\\')
94  {
95  if (!*++s)
96  break;
97  }
98  if (*commentlen < commentmax)
99  comment[(*commentlen)++] = *s;
100  s++;
101  }
102  if (level != 0)
103  {
105  return NULL;
106  }
107  return s;
108 }
109 
119 static const char *parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
120 {
121  while (*s)
122  {
123  if (*tokenlen < tokenmax)
124  token[*tokenlen] = *s;
125  if (*s == '\\')
126  {
127  if (!*++s)
128  break;
129 
130  if (*tokenlen < tokenmax)
131  token[*tokenlen] = *s;
132  }
133  else if (*s == '"')
134  return s + 1;
135  (*tokenlen)++;
136  s++;
137  }
139  return NULL;
140 }
141 
150 static const char *next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
151 {
152  if (*s == '(')
153  return parse_comment(s + 1, token, tokenlen, tokenmax);
154  if (*s == '"')
155  return parse_quote(s + 1, token, tokenlen, tokenmax);
156  if (*s && is_special(*s))
157  {
158  if (*tokenlen < tokenmax)
159  token[(*tokenlen)++] = *s;
160  return s + 1;
161  }
162  while (*s)
163  {
164  if (mutt_str_is_email_wsp(*s) || is_special(*s))
165  break;
166  if (*tokenlen < tokenmax)
167  token[(*tokenlen)++] = *s;
168  s++;
169  }
170  return s;
171 }
172 
197 static const char *parse_mailboxdomain(const char *s, const char *nonspecial,
198  char *mailbox, size_t *mailboxlen,
199  size_t mailboxmax, char *comment,
200  size_t *commentlen, size_t commentmax)
201 {
202  const char *ps = NULL;
203 
204  while (*s)
205  {
207  if (!*s)
208  return s;
209 
210  if (!strchr(nonspecial, *s) && is_special(*s))
211  return s;
212 
213  if (*s == '(')
214  {
215  if (*commentlen && (*commentlen < commentmax))
216  comment[(*commentlen)++] = ' ';
217  ps = next_token(s, comment, commentlen, commentmax);
218  }
219  else
220  ps = next_token(s, mailbox, mailboxlen, mailboxmax);
221  if (!ps)
222  return NULL;
223  s = ps;
224  }
225 
226  return s;
227 }
228 
242 static const char *parse_address(const char *s, char *token, size_t *tokenlen,
243  size_t tokenmax, char *comment, size_t *commentlen,
244  size_t commentmax, struct Address *addr)
245 {
246  s = parse_mailboxdomain(s, ".\"(\\", token, tokenlen, tokenmax, comment,
247  commentlen, commentmax);
248  if (!s)
249  return NULL;
250 
251  if (*s == '@')
252  {
253  if (*tokenlen < tokenmax)
254  token[(*tokenlen)++] = '@';
255  s = parse_mailboxdomain(s + 1, ".([]\\", token, tokenlen, tokenmax, comment,
256  commentlen, commentmax);
257  if (!s)
258  return NULL;
259  }
260 
261  terminate_string(token, *tokenlen, tokenmax);
262  addr->mailbox = mutt_str_strdup(token);
263 
264  if (*commentlen && !addr->personal)
265  {
266  terminate_string(comment, *commentlen, commentmax);
267  addr->personal = mutt_str_strdup(comment);
268  }
269 
270  return s;
271 }
272 
282 static const char *parse_route_addr(const char *s, char *comment, size_t *commentlen,
283  size_t commentmax, struct Address *addr)
284 {
285  char token[1024];
286  size_t tokenlen = 0;
287 
289 
290  /* find the end of the route */
291  if (*s == '@')
292  {
293  while (s && (*s == '@'))
294  {
295  if (tokenlen < (sizeof(token) - 1))
296  token[tokenlen++] = '@';
297  s = parse_mailboxdomain(s + 1, ",.\\[](", token, &tokenlen,
298  sizeof(token) - 1, comment, commentlen, commentmax);
299  }
300  if (!s || (*s != ':'))
301  {
303  return NULL; /* invalid route */
304  }
305 
306  if (tokenlen < (sizeof(token) - 1))
307  token[tokenlen++] = ':';
308  s++;
309  }
310 
311  s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
312  commentmax, addr);
313  if (!s)
314  return NULL;
315 
316  if (*s != '>')
317  {
319  return NULL;
320  }
321 
322  if (!addr->mailbox)
323  addr->mailbox = mutt_str_strdup("@");
324 
325  s++;
326  return s;
327 }
328 
338 static const char *parse_addr_spec(const char *s, char *comment, size_t *commentlen,
339  size_t commentmax, struct Address *addr)
340 {
341  char token[1024];
342  size_t tokenlen = 0;
343 
344  s = parse_address(s, token, &tokenlen, sizeof(token) - 1, comment, commentlen,
345  commentmax, addr);
346  if (s && *s && (*s != ',') && (*s != ';'))
347  {
349  return NULL;
350  }
351  return s;
352 }
353 
363 static bool add_addrspec(struct AddressList *al, const char *phrase,
364  char *comment, size_t *commentlen, size_t commentmax)
365 {
366  struct Address *cur = mutt_addr_new();
367 
368  if (!parse_addr_spec(phrase, comment, commentlen, commentmax, cur))
369  {
370  mutt_addr_free(&cur);
371  return false;
372  }
373 
374  mutt_addrlist_append(al, cur);
375  return true;
376 }
377 
384 struct Address *mutt_addr_new(void)
385 {
386  return mutt_mem_calloc(1, sizeof(struct Address));
387 }
388 
397 struct Address *mutt_addr_create(const char *personal, const char *mailbox)
398 {
399  struct Address *a = mutt_addr_new();
400  a->personal = mutt_str_strdup(personal);
401  a->mailbox = mutt_str_strdup(mailbox);
402  return a;
403 }
404 
412 int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
413 {
414  if (!al)
415  return -1;
416 
417  if (!mailbox)
418  return 0;
419 
420  int rc = -1;
421  struct Address *a = NULL, *tmp = NULL;
422  TAILQ_FOREACH_SAFE(a, al, entries, tmp)
423  {
424  if (mutt_str_strcasecmp(mailbox, a->mailbox) == 0)
425  {
426  TAILQ_REMOVE(al, a, entries);
427  mutt_addr_free(&a);
428  rc = 0;
429  }
430  }
431 
432  return rc;
433 }
434 
439 void mutt_addr_free(struct Address **ptr)
440 {
441  if (!ptr || !*ptr)
442  return;
443 
444  struct Address *a = *ptr;
445 
446  FREE(&a->personal);
447  FREE(&a->mailbox);
448  FREE(ptr);
449 }
450 
457 int mutt_addrlist_parse(struct AddressList *al, const char *s)
458 {
459  if (!s)
460  return 0;
461 
462  int parsed = 0;
463  char comment[1024], phrase[1024];
464  size_t phraselen = 0, commentlen = 0;
465  AddressError = 0;
466 
467  bool ws_pending = mutt_str_is_email_wsp(*s);
468 
470  while (*s)
471  {
472  switch (*s)
473  {
474  case ';':
475  case ',':
476  if (phraselen != 0)
477  {
478  terminate_buffer(phrase, phraselen);
479  if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
480  {
481  parsed++;
482  }
483  }
484  else if (commentlen != 0)
485  {
486  struct Address *last = TAILQ_LAST(al, AddressList);
487  if (last && !last->personal)
488  {
489  terminate_buffer(comment, commentlen);
490  last->personal = mutt_str_strdup(comment);
491  }
492  }
493 
494  if (*s == ';')
495  {
496  /* add group terminator */
498  }
499 
500  phraselen = 0;
501  commentlen = 0;
502  s++;
503  break;
504 
505  case '(':
506  if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
507  comment[commentlen++] = ' ';
508  s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
509  if (!s)
510  {
512  return 0;
513  }
514  break;
515 
516  case '"':
517  if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
518  phrase[phraselen++] = ' ';
519  s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
520  if (!s)
521  {
523  return 0;
524  }
525  break;
526 
527  case ':':
528  {
529  struct Address *a = mutt_addr_new();
530  terminate_buffer(phrase, phraselen);
531  a->mailbox = mutt_str_strdup(phrase);
532  a->group = true;
533  mutt_addrlist_append(al, a);
534  phraselen = 0;
535  commentlen = 0;
536  s++;
537  break;
538  }
539 
540  case '<':
541  {
542  struct Address *a = mutt_addr_new();
543  terminate_buffer(phrase, phraselen);
544  a->personal = mutt_str_strdup(phrase);
545  s = parse_route_addr(s + 1, comment, &commentlen, sizeof(comment) - 1, a);
546  if (!s)
547  {
549  mutt_addr_free(&a);
550  return 0;
551  }
552  mutt_addrlist_append(al, a);
553  phraselen = 0;
554  commentlen = 0;
555  parsed++;
556  break;
557  }
558 
559  default:
560  if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)) && ws_pending)
561  phrase[phraselen++] = ' ';
562  s = next_token(s, phrase, &phraselen, sizeof(phrase) - 1);
563  if (!s)
564  {
566  return 0;
567  }
568  break;
569  } // switch (*s)
570 
571  ws_pending = mutt_str_is_email_wsp(*s);
573  } // while (*s)
574 
575  if (phraselen != 0)
576  {
577  terminate_buffer(phrase, phraselen);
578  terminate_buffer(comment, commentlen);
579  if (add_addrspec(al, phrase, comment, &commentlen, sizeof(comment) - 1))
580  {
581  parsed++;
582  }
583  }
584  else if (commentlen != 0)
585  {
586  struct Address *last = TAILQ_LAST(al, AddressList);
587  if (last && !last->personal)
588  {
589  terminate_buffer(comment, commentlen);
590  last->personal = mutt_str_strdup(comment);
591  }
592  }
593 
594  return parsed;
595 }
596 
606 int mutt_addrlist_parse2(struct AddressList *al, const char *s)
607 {
608  if (!s || !*s)
609  return 0;
610 
611  int parsed = 0;
612 
613  /* check for a simple whitespace separated list of addresses */
614  if (!strpbrk(s, "\"<>():;,\\"))
615  {
616  char *copy = mutt_str_strdup(s);
617  char *r = copy;
618  while ((r = strtok(r, " \t")))
619  {
620  parsed += mutt_addrlist_parse(al, r);
621  r = NULL;
622  }
623  FREE(&copy);
624  }
625  else
626  parsed = mutt_addrlist_parse(al, s);
627 
628  return parsed;
629 }
630 
640 void mutt_addrlist_qualify(struct AddressList *al, const char *host)
641 {
642  if (!al || !host || !*host)
643  return;
644 
645  struct Address *a = NULL;
646  TAILQ_FOREACH(a, al, entries)
647  {
648  if (!a->group && a->mailbox && !strchr(a->mailbox, '@'))
649  {
650  char *p = mutt_mem_malloc(mutt_str_strlen(a->mailbox) + mutt_str_strlen(host) + 2);
651  sprintf(p, "%s@%s", a->mailbox, host);
652  FREE(&a->mailbox);
653  a->mailbox = p;
654  }
655  }
656 }
657 
671 void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
672 {
673  if (!buf || !value || !specials)
674  return;
675 
676  if (strpbrk(value, specials))
677  {
678  char tmp[256];
679  char *pc = tmp;
680  size_t tmplen = sizeof(tmp) - 3;
681 
682  *pc++ = '"';
683  for (; *value && (tmplen > 1); value++)
684  {
685  if ((*value == '\\') || (*value == '"'))
686  {
687  *pc++ = '\\';
688  tmplen--;
689  }
690  *pc++ = *value;
691  tmplen--;
692  }
693  *pc++ = '"';
694  *pc = '\0';
695  mutt_str_strfcpy(buf, tmp, buflen);
696  }
697  else
698  mutt_str_strfcpy(buf, value, buflen);
699 }
700 
706 struct Address *mutt_addr_copy(const struct Address *addr)
707 {
708  if (!addr)
709  return NULL;
710 
711  struct Address *p = mutt_addr_new();
712 
713  p->personal = mutt_str_strdup(addr->personal);
714  p->mailbox = mutt_str_strdup(addr->mailbox);
715  p->group = addr->group;
716  p->is_intl = addr->is_intl;
717  p->intl_checked = addr->intl_checked;
718  return p;
719 }
720 
727 void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
728 {
729  if (!dst || !src)
730  return;
731 
732  struct Address *a = NULL;
733  TAILQ_FOREACH(a, src, entries)
734  {
735  struct Address *next = TAILQ_NEXT(a, entries);
736  if (prune && a->group && (!next || !next->mailbox))
737  {
738  /* ignore this element of the list */
739  }
740  else
741  {
743  }
744  }
745 }
746 
754 bool mutt_addr_valid_msgid(const char *msgid)
755 {
756  /* msg-id = "<" addr-spec ">"
757  * addr-spec = local-part "@" domain
758  * local-part = word *("." word)
759  * word = atom / quoted-string
760  * atom = 1*<any CHAR except specials, SPACE and CTLs>
761  * CHAR = ( 0.-127. )
762  * specials = "(" / ")" / "<" / ">" / "@"
763  * / "," / ";" / ":" / "\" / <">
764  * / "." / "[" / "]"
765  * SPACE = ( 32. )
766  * CTLS = ( 0.-31., 127.)
767  * quoted-string = <"> *(qtext/quoted-pair) <">
768  * qtext = <any CHAR except <">, "\" and CR>
769  * CR = ( 13. )
770  * quoted-pair = "\" CHAR
771  * domain = sub-domain *("." sub-domain)
772  * sub-domain = domain-ref / domain-literal
773  * domain-ref = atom
774  * domain-literal = "[" *(dtext / quoted-pair) "]"
775  */
776 
777  if (!msgid || !*msgid)
778  return false;
779 
780  size_t l = mutt_str_strlen(msgid);
781  if (l < 5) /* <atom@atom> */
782  return false;
783  if ((msgid[0] != '<') || (msgid[l - 1] != '>'))
784  return false;
785  if (!(strrchr(msgid, '@')))
786  return false;
787 
788  /* TODO: complete parser */
789  for (size_t i = 0; i < l; i++)
790  if ((unsigned char) msgid[i] > 127)
791  return false;
792 
793  return true;
794 }
795 
802 bool mutt_addrlist_equal(const struct AddressList *ala, const struct AddressList *alb)
803 {
804  if (!ala || !alb)
805  {
806  return !(ala || alb);
807  }
808 
809  struct Address *ana = TAILQ_FIRST(ala);
810  struct Address *anb = TAILQ_FIRST(alb);
811 
812  while (ana && anb)
813  {
814  if ((mutt_str_strcmp(ana->mailbox, anb->mailbox) != 0) ||
815  (mutt_str_strcmp(ana->personal, anb->personal) != 0))
816  {
817  break;
818  }
819 
820  ana = TAILQ_NEXT(ana, entries);
821  anb = TAILQ_NEXT(anb, entries);
822  }
823 
824  return !(ana || anb);
825 }
826 
834 int mutt_addrlist_count_recips(const struct AddressList *al)
835 {
836  if (!al)
837  return 0;
838 
839  int c = 0;
840  struct Address *a = NULL;
841  TAILQ_FOREACH(a, al, entries)
842  {
843  c += (a->mailbox && !a->group);
844  }
845  return c;
846 }
847 
854 bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
855 {
856  if (!a || !b)
857  return false;
858  if (!a->mailbox || !b->mailbox)
859  return false;
860  if (mutt_str_strcasecmp(a->mailbox, b->mailbox) != 0)
861  return false;
862  return true;
863 }
864 
871 bool mutt_addrlist_search(const struct Address *needle, const struct AddressList *haystack)
872 {
873  if (!needle || !haystack)
874  return false;
875 
876  struct Address *a = NULL;
877  TAILQ_FOREACH(a, haystack, entries)
878  {
879  if (mutt_addr_cmp(needle, a))
880  return true;
881  }
882  return false;
883 }
884 
890 static bool addr_is_intl(const struct Address *a)
891 {
892  if (!a)
893  return false;
894  return a->intl_checked && a->is_intl;
895 }
896 
902 static bool addr_is_local(const struct Address *a)
903 {
904  if (!a)
905  return false;
906  return a->intl_checked && !a->is_intl;
907 }
908 
919 static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
920 {
921  if (!mbox || !user || !domain)
922  return -1;
923 
924  char *ptr = strchr(mbox, '@');
925 
926  /* Fail if '@' is missing, at the start, or at the end */
927  if (!ptr || (ptr == mbox) || (ptr[1] == '\0'))
928  return -1;
929 
930  *user = mutt_str_substr_dup(mbox, ptr);
931  *domain = mutt_str_strdup(ptr + 1);
932 
933  return 0;
934 }
935 
941 static void addr_set_intl(struct Address *a, char *intl_mailbox)
942 {
943  if (!a)
944  return;
945 
946  FREE(&a->mailbox);
947  a->mailbox = intl_mailbox;
948  a->intl_checked = true;
949  a->is_intl = true;
950 }
951 
957 static void addr_set_local(struct Address *a, char *local_mailbox)
958 {
959  if (!a)
960  return;
961 
962  FREE(&a->mailbox);
963  a->mailbox = local_mailbox;
964  a->intl_checked = true;
965  a->is_intl = false;
966 }
967 
976 const char *mutt_addr_for_display(const struct Address *a)
977 {
978  if (!a)
979  return NULL;
980 
981  char *user = NULL, *domain = NULL;
982  static char *buf = NULL;
983 
984  if (!a->mailbox || addr_is_local(a))
985  return a->mailbox;
986 
987  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
988  return a->mailbox;
989 
990  char *local_mailbox = mutt_idna_intl_to_local(user, domain, MI_MAY_BE_IRREVERSIBLE);
991 
992  FREE(&user);
993  FREE(&domain);
994 
995  if (!local_mailbox)
996  return a->mailbox;
997 
998  mutt_str_replace(&buf, local_mailbox);
999  FREE(&local_mailbox);
1000 
1001  return buf;
1002 }
1003 
1015 size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display)
1016 {
1017  if (!buf || buflen == 0 || !addr)
1018  return 0;
1019 
1020  size_t len;
1021  char *pbuf = buf;
1022  char *pc = NULL;
1023 
1024  buflen--; /* save room for the terminal nul */
1025 
1026  if (addr->personal)
1027  {
1028  if (strpbrk(addr->personal, AddressSpecials))
1029  {
1030  if (buflen == 0)
1031  goto done;
1032  *pbuf++ = '"';
1033  buflen--;
1034  for (pc = addr->personal; *pc && (buflen > 0); pc++)
1035  {
1036  if ((*pc == '"') || (*pc == '\\'))
1037  {
1038  *pbuf++ = '\\';
1039  buflen--;
1040  }
1041  if (buflen == 0)
1042  goto done;
1043  *pbuf++ = *pc;
1044  buflen--;
1045  }
1046  if (buflen == 0)
1047  goto done;
1048  *pbuf++ = '"';
1049  buflen--;
1050  }
1051  else
1052  {
1053  if (buflen == 0)
1054  goto done;
1055  len = mutt_str_strfcpy(pbuf, addr->personal, buflen + 1 /* strfcpy terminates */);
1056  pbuf += len;
1057  buflen -= len;
1058  }
1059 
1060  if (buflen == 0)
1061  goto done;
1062  *pbuf++ = ' ';
1063  buflen--;
1064  }
1065 
1066  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1067  {
1068  if (buflen == 0)
1069  goto done;
1070  *pbuf++ = '<';
1071  buflen--;
1072  }
1073 
1074  if (addr->mailbox)
1075  {
1076  if (buflen == 0)
1077  goto done;
1078  if (mutt_str_strcmp(addr->mailbox, "@") != 0)
1079  {
1080  const char *a = display ? mutt_addr_for_display(addr) : addr->mailbox;
1081  len = mutt_str_strfcpy(pbuf, a, buflen + 1 /* strfcpy terminates */);
1082  pbuf += len;
1083  buflen -= len;
1084  }
1085  else
1086  {
1087  *pbuf = '\0';
1088  }
1089 
1090  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1091  {
1092  if (buflen == 0)
1093  goto done;
1094  *pbuf++ = '>';
1095  buflen--;
1096  }
1097 
1098  if (addr->group)
1099  {
1100  if (buflen == 0)
1101  goto done;
1102  *pbuf++ = ':';
1103  buflen--;
1104  if (buflen == 0)
1105  goto done;
1106  *pbuf++ = ' ';
1107  buflen--;
1108  }
1109  }
1110  else
1111  {
1112  if (buflen == 0)
1113  goto done;
1114  *pbuf++ = ';';
1115  }
1116 done:
1117  /* no need to check for length here since we already save space at the
1118  * beginning of this routine */
1119  *pbuf = '\0';
1120 
1121  return pbuf - buf;
1122 }
1123 
1137 size_t mutt_addrlist_write(char *buf, size_t buflen, const struct AddressList *al, bool display)
1138 {
1139  if (!buf || buflen == 0 || !al)
1140  return 0;
1141 
1142  size_t len = mutt_str_strlen(buf);
1143  if (len >= buflen)
1144  {
1145  return 0;
1146  }
1147 
1148  char *pbuf = buf + len;
1149  buflen -= len;
1150 
1151  struct Address *a = NULL;
1152  TAILQ_FOREACH(a, al, entries)
1153  {
1154  if (len > 0)
1155  {
1156  if (buflen > 1)
1157  {
1158  *pbuf++ = ',';
1159  buflen--;
1160  }
1161  if (buflen > 1)
1162  {
1163  *pbuf++ = ' ';
1164  buflen--;
1165  }
1166  }
1167 
1168  if (buflen == 1)
1169  {
1170  break;
1171  }
1172 
1173  len = mutt_addr_write(pbuf, buflen, a, display);
1174  pbuf += len;
1175  buflen -= len;
1176  }
1177 
1178  *pbuf = '\0';
1179  return pbuf - buf;
1180 }
1181 
1188 {
1189  if (!a || !a->mailbox || addr_is_intl(a))
1190  return true;
1191 
1192  char *user = NULL;
1193  char *domain = NULL;
1194  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1195  return true;
1196 
1197  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1198 
1199  FREE(&user);
1200  FREE(&domain);
1201 
1202  if (!intl_mailbox)
1203  return false;
1204 
1205  addr_set_intl(a, intl_mailbox);
1206  return true;
1207 }
1208 
1216 int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1217 {
1218  if (!al)
1219  return 0;
1220 
1221  int rc = 0;
1222 
1223  if (err)
1224  *err = NULL;
1225 
1226  struct Address *a = NULL;
1227  TAILQ_FOREACH(a, al, entries)
1228  {
1229  if (!a->mailbox || addr_is_intl(a))
1230  continue;
1231 
1232  char *user = NULL;
1233  char *domain = NULL;
1234  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1235  continue;
1236 
1237  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1238 
1239  FREE(&user);
1240  FREE(&domain);
1241 
1242  if (!intl_mailbox)
1243  {
1244  rc = -1;
1245  if (err && !*err)
1246  *err = mutt_str_strdup(a->mailbox);
1247  continue;
1248  }
1249 
1250  addr_set_intl(a, intl_mailbox);
1251  }
1252 
1253  return rc;
1254 }
1255 
1262 {
1263  if (!a->mailbox)
1264  {
1265  return false;
1266  }
1267 
1268  if (addr_is_local(a))
1269  {
1270  return true;
1271  }
1272 
1273  char *user = NULL;
1274  char *domain = NULL;
1275  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1276  {
1277  return false;
1278  }
1279 
1280  char *local_mailbox = mutt_idna_intl_to_local(user, domain, 0);
1281  FREE(&user);
1282  FREE(&domain);
1283 
1284  if (!local_mailbox)
1285  {
1286  return false;
1287  }
1288 
1289  addr_set_local(a, local_mailbox);
1290  return true;
1291 }
1292 
1298 int mutt_addrlist_to_local(struct AddressList *al)
1299 {
1300  if (!al)
1301  return 0;
1302 
1303  struct Address *a = NULL;
1304  TAILQ_FOREACH(a, al, entries)
1305  {
1306  mutt_addr_to_local(a);
1307  }
1308  return 0;
1309 }
1310 
1318 void mutt_addrlist_dedupe(struct AddressList *al)
1319 {
1320  if (!al)
1321  return;
1322 
1323  struct Address *a = NULL;
1324  TAILQ_FOREACH(a, al, entries)
1325  {
1326  if (a->mailbox)
1327  {
1328  struct Address *a2 = TAILQ_NEXT(a, entries);
1329  struct Address *tmp = NULL;
1330 
1331  if (a2)
1332  {
1333  TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1334  {
1335  if (a2->mailbox && (mutt_str_strcasecmp(a->mailbox, a2->mailbox) == 0))
1336  {
1337  mutt_debug(LL_DEBUG2, "Removing %s\n", a2->mailbox);
1338  TAILQ_REMOVE(al, a2, entries);
1339  mutt_addr_free(&a2);
1340  }
1341  }
1342  }
1343  }
1344  }
1345 }
1346 
1354 void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1355 {
1356  if (!a || !b)
1357  return;
1358 
1359  struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1360 
1361  TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1362  {
1363  TAILQ_FOREACH(aa, a, entries)
1364  {
1365  if (mutt_addr_cmp(aa, ab))
1366  {
1367  TAILQ_REMOVE(b, ab, entries);
1368  mutt_addr_free(&ab);
1369  break;
1370  }
1371  }
1372  }
1373 }
1374 
1381 void mutt_addrlist_clear(struct AddressList *al)
1382 {
1383  if (!al)
1384  return;
1385 
1386  struct Address *a = TAILQ_FIRST(al), *next = NULL;
1387  while (a)
1388  {
1389  next = TAILQ_NEXT(a, entries);
1390  mutt_addr_free(&a);
1391  a = next;
1392  }
1393  TAILQ_INIT(al);
1394 }
1395 
1401 void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1402 {
1403  if (al && a)
1404  TAILQ_INSERT_TAIL(al, a, entries);
1405 }
1406 
1412 void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1413 {
1414  if (al && a)
1415  TAILQ_INSERT_HEAD(al, a, entries);
1416 }
int mutt_addrlist_remove(struct AddressList *al, const char *mailbox)
Remove an Address from a list.
Definition: address.c:412
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:717
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:719
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:457
#define TAILQ_LAST(head, headname)
Definition: queue.h:813
static void addr_set_local(struct Address *a, char *local_mailbox)
Mark an Address as having NO IDN components.
Definition: address.c:957
static void addr_set_intl(struct Address *a, char *intl_mailbox)
Mark an Address as having IDN components.
Definition: address.c:941
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1298
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:1381
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:282
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:727
Handling of international domain names.
Representation of an email address.
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:729
static const char * parse_comment(const char *s, char *comment, size_t *commentlen, size_t commentmax)
Extract a comment (parenthesised string)
Definition: address.c:77
struct Address * mutt_addr_new(void)
Create a new Address.
Definition: address.c:384
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:363
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:802
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:666
size_t mutt_addrlist_write(char *buf, size_t buflen, const struct AddressList *al, bool display)
Write an Address to a buffer.
Definition: address.c:1137
struct Address * mutt_addr_copy(const struct Address *addr)
Copy the real address.
Definition: address.c:706
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:606
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:439
struct Address * mutt_addr_create(const char *personal, const char *mailbox)
Create and populate a new Address.
Definition: address.c:397
Bad route address.
Definition: address.h:54
bool mutt_addr_to_local(struct Address *a)
Convert an Address from Punycode.
Definition: address.c:1261
static int addr_mbox_to_udomain(const char *mbox, char **user, char **domain)
Split a mailbox name into user and domain.
Definition: address.c:919
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:197
bool is_intl
International Domain Name.
Definition: address.h:39
void mutt_addrlist_dedupe(struct AddressList *al)
Remove duplicate addresses.
Definition: address.c:1318
const char * mutt_addr_for_display(const struct Address *a)
Convert an Address for display purposes.
Definition: address.c:976
#define is_special(ch)
Is this character special to an email address?
Definition: address.c:47
Log at debug level 2.
Definition: logging.h:57
#define TAILQ_INIT(head)
Definition: queue.h:759
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:1015
static const char * parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
Extract a quoted string.
Definition: address.c:119
bool intl_checked
Checked for IDN?
Definition: address.h:40
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:821
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:242
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:788
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:902
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar)
Definition: queue.h:734
bool mutt_addr_cmp(const struct Address *a, const struct Address *b)
Compare two e-mail addresses.
Definition: address.c:854
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:750
void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
Prepend an Address to an AddressList.
Definition: address.c:1412
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:803
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
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:834
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:790
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:640
bool mutt_addr_to_intl(struct Address *a)
Convert an Address to Punycode.
Definition: address.c:1187
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1216
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:890
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:754
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:628
void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
Remove cross-references.
Definition: address.c:1354
#define FREE(x)
Definition: memory.h:40
bool mutt_addrlist_search(const struct Address *needle, const struct AddressList *haystack)
Search for an e-mail address in a list.
Definition: address.c:871
#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:263
const char *const AddressErrors[]
Messages for the error codes in AddressError.
Definition: address.c:63
#define TAILQ_NEXT(elm, field)
Definition: queue.h:816
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:150
char * mutt_str_substr_dup(const char *begin, const char *end)
Duplicate a sub-string.
Definition: string.c:579
static unsigned char * pbuf
Cache PGP data packet.
Definition: pgppacket.c:37
#define terminate_string(str, strlen, buflen)
Definition: string2.h:51
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
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:671
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:338
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:145
void mutt_addrlist_append(struct AddressList *al, struct Address *a)
Append an Address to an AddressList.
Definition: address.c:1401