NeoMutt  2020-11-20
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 && last->mailbox)
489  {
490  terminate_buffer(comment, commentlen);
491  last->personal = mutt_str_dup(comment);
492  }
493  }
494 
495  if (*s == ';')
496  {
497  /* add group terminator */
499  }
500 
501  phraselen = 0;
502  commentlen = 0;
503  s++;
504  break;
505 
506  case '(':
507  if ((commentlen != 0) && (commentlen < (sizeof(comment) - 1)))
508  comment[commentlen++] = ' ';
509  s = next_token(s, comment, &commentlen, sizeof(comment) - 1);
510  if (!s)
511  {
513  return 0;
514  }
515  break;
516 
517  case '"':
518  if ((phraselen != 0) && (phraselen < (sizeof(phrase) - 1)))
519  phrase[phraselen++] = ' ';
520  s = parse_quote(s + 1, phrase, &phraselen, sizeof(phrase) - 1);
521  if (!s)
522  {
524  return 0;
525  }
526  break;
527 
528  case ':':
529  {
530  struct Address *a = mutt_addr_new();
531  terminate_buffer(phrase, phraselen);
532  a->mailbox = mutt_str_dup(phrase);
533  a->group = true;
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 && last->mailbox)
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  if (!addr->personal && !addr->mailbox)
1031  return 0;
1032 
1033  size_t len;
1034  char *pbuf = buf;
1035  char *pc = NULL;
1036 
1037  buflen--; /* save room for the terminal nul */
1038 
1039  if (addr->personal)
1040  {
1041  if (strpbrk(addr->personal, AddressSpecials))
1042  {
1043  if (buflen == 0)
1044  goto done;
1045  *pbuf++ = '"';
1046  buflen--;
1047  for (pc = addr->personal; *pc && (buflen > 0); pc++)
1048  {
1049  if ((*pc == '"') || (*pc == '\\'))
1050  {
1051  *pbuf++ = '\\';
1052  buflen--;
1053  }
1054  if (buflen == 0)
1055  goto done;
1056  *pbuf++ = *pc;
1057  buflen--;
1058  }
1059  if (buflen == 0)
1060  goto done;
1061  *pbuf++ = '"';
1062  buflen--;
1063  }
1064  else
1065  {
1066  if (buflen == 0)
1067  goto done;
1068  len = mutt_str_copy(pbuf, addr->personal, buflen + 1);
1069  pbuf += len;
1070  buflen -= len;
1071  }
1072 
1073  if (buflen == 0)
1074  goto done;
1075  *pbuf++ = ' ';
1076  buflen--;
1077  }
1078 
1079  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1080  {
1081  if (buflen == 0)
1082  goto done;
1083  *pbuf++ = '<';
1084  buflen--;
1085  }
1086 
1087  if (addr->mailbox)
1088  {
1089  if (buflen == 0)
1090  goto done;
1091  if (mutt_str_equal(addr->mailbox, "@"))
1092  {
1093  *pbuf = '\0';
1094  }
1095  else
1096  {
1097  const char *a = display ? mutt_addr_for_display(addr) : addr->mailbox;
1098  len = mutt_str_copy(pbuf, a, buflen + 1);
1099  pbuf += len;
1100  buflen -= len;
1101  }
1102 
1103  if (addr->personal || (addr->mailbox && (*addr->mailbox == '@')))
1104  {
1105  if (buflen == 0)
1106  goto done;
1107  *pbuf++ = '>';
1108  buflen--;
1109  }
1110 
1111  if (addr->group)
1112  {
1113  if (buflen == 0)
1114  goto done;
1115  *pbuf++ = ':';
1116  buflen--;
1117  if (buflen == 0)
1118  goto done;
1119  *pbuf++ = ' ';
1120  buflen--;
1121  }
1122  }
1123  else
1124  {
1125  if (buflen == 0)
1126  goto done;
1127  *pbuf++ = ';';
1128  }
1129 done:
1130  /* no need to check for length here since we already save space at the
1131  * beginning of this routine */
1132  *pbuf = '\0';
1133 
1134  return pbuf - buf;
1135 }
1136 
1150 size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
1151 {
1152  if (!buf || (buflen == 0) || !al)
1153  return 0;
1154 
1155  size_t len = mutt_str_len(buf);
1156  if (len >= buflen)
1157  {
1158  return 0;
1159  }
1160 
1161  char *pbuf = buf + len;
1162  buflen -= len;
1163 
1164  struct Address *a = NULL;
1165  TAILQ_FOREACH(a, al, entries)
1166  {
1167  if (len > 0)
1168  {
1169  if (buflen > 1)
1170  {
1171  *pbuf++ = ',';
1172  buflen--;
1173  }
1174  if (buflen > 1)
1175  {
1176  *pbuf++ = ' ';
1177  buflen--;
1178  }
1179  }
1180 
1181  if (buflen == 1)
1182  {
1183  break;
1184  }
1185 
1186  len = mutt_addr_write(pbuf, buflen, a, display);
1187  pbuf += len;
1188  buflen -= len;
1189  }
1190 
1191  *pbuf = '\0';
1192  return pbuf - buf;
1193 }
1194 
1201 size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
1202 {
1203  if (!al || !list)
1204  return 0;
1205 
1206  char addr[256];
1207  size_t count = 0;
1208  struct Address *a = NULL;
1209  TAILQ_FOREACH(a, al, entries)
1210  {
1211  if (mutt_addr_write(addr, sizeof(addr), a, true) != 0)
1212  {
1213  mutt_list_insert_tail(list, strdup(addr));
1214  count++;
1215  }
1216  }
1217 
1218  return count;
1219 }
1220 
1231 void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, int start_col, bool display)
1232 {
1233  char buf[1024];
1234  int count = 0;
1235  int linelen = start_col;
1236 
1237  struct Address *a = NULL;
1238  TAILQ_FOREACH(a, al, entries)
1239  {
1240  buf[0] = '\0';
1241  size_t len = mutt_addr_write(buf, sizeof(buf), a, display);
1242  if (len == 0)
1243  continue;
1244  if (count && (linelen + len > 74))
1245  {
1246  fputs("\n\t", fp);
1247  linelen = len + 8; /* tab is usually about 8 spaces... */
1248  }
1249  else
1250  {
1251  if (count && a->mailbox)
1252  {
1253  fputc(' ', fp);
1254  linelen++;
1255  }
1256  linelen += len;
1257  }
1258  fputs(buf, fp);
1259  struct Address *next = TAILQ_NEXT(a, entries);
1260  if (!a->group && next && next->mailbox)
1261  {
1262  linelen++;
1263  fputc(',', fp);
1264  }
1265  count++;
1266  }
1267  fputc('\n', fp);
1268 }
1269 
1276 {
1277  if (!a || !a->mailbox || addr_is_intl(a))
1278  return true;
1279 
1280  char *user = NULL;
1281  char *domain = NULL;
1282  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1283  return true;
1284 
1285  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1286 
1287  FREE(&user);
1288  FREE(&domain);
1289 
1290  if (!intl_mailbox)
1291  return false;
1292 
1293  addr_set_intl(a, intl_mailbox);
1294  return true;
1295 }
1296 
1304 int mutt_addrlist_to_intl(struct AddressList *al, char **err)
1305 {
1306  if (!al)
1307  return 0;
1308 
1309  int rc = 0;
1310 
1311  if (err)
1312  *err = NULL;
1313 
1314  struct Address *a = NULL;
1315  TAILQ_FOREACH(a, al, entries)
1316  {
1317  if (!a->mailbox || addr_is_intl(a))
1318  continue;
1319 
1320  char *user = NULL;
1321  char *domain = NULL;
1322  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1323  continue;
1324 
1325  char *intl_mailbox = mutt_idna_local_to_intl(user, domain);
1326 
1327  FREE(&user);
1328  FREE(&domain);
1329 
1330  if (!intl_mailbox)
1331  {
1332  rc = -1;
1333  if (err && !*err)
1334  *err = mutt_str_dup(a->mailbox);
1335  continue;
1336  }
1337 
1338  addr_set_intl(a, intl_mailbox);
1339  }
1340 
1341  return rc;
1342 }
1343 
1350 {
1351  if (!a->mailbox)
1352  {
1353  return false;
1354  }
1355 
1356  if (addr_is_local(a))
1357  {
1358  return true;
1359  }
1360 
1361  char *user = NULL;
1362  char *domain = NULL;
1363  if (addr_mbox_to_udomain(a->mailbox, &user, &domain) == -1)
1364  {
1365  return false;
1366  }
1367 
1368  char *local_mailbox = mutt_idna_intl_to_local(user, domain, 0);
1369  FREE(&user);
1370  FREE(&domain);
1371 
1372  if (!local_mailbox)
1373  {
1374  return false;
1375  }
1376 
1377  addr_set_local(a, local_mailbox);
1378  return true;
1379 }
1380 
1386 int mutt_addrlist_to_local(struct AddressList *al)
1387 {
1388  if (!al)
1389  return 0;
1390 
1391  struct Address *a = NULL;
1392  TAILQ_FOREACH(a, al, entries)
1393  {
1394  mutt_addr_to_local(a);
1395  }
1396  return 0;
1397 }
1398 
1405 void mutt_addrlist_dedupe(struct AddressList *al)
1406 {
1407  if (!al)
1408  return;
1409 
1410  struct Address *a = NULL;
1411  TAILQ_FOREACH(a, al, entries)
1412  {
1413  if (a->mailbox)
1414  {
1415  struct Address *a2 = TAILQ_NEXT(a, entries);
1416  struct Address *tmp = NULL;
1417 
1418  if (a2)
1419  {
1420  TAILQ_FOREACH_FROM_SAFE(a2, al, entries, tmp)
1421  {
1422  if (a2->mailbox && mutt_istr_equal(a->mailbox, a2->mailbox))
1423  {
1424  mutt_debug(LL_DEBUG2, "Removing %s\n", a2->mailbox);
1425  TAILQ_REMOVE(al, a2, entries);
1426  mutt_addr_free(&a2);
1427  }
1428  }
1429  }
1430  }
1431  }
1432 }
1433 
1441 void mutt_addrlist_remove_xrefs(const struct AddressList *a, struct AddressList *b)
1442 {
1443  if (!a || !b)
1444  return;
1445 
1446  struct Address *aa = NULL, *ab = NULL, *tmp = NULL;
1447 
1448  TAILQ_FOREACH_SAFE(ab, b, entries, tmp)
1449  {
1450  TAILQ_FOREACH(aa, a, entries)
1451  {
1452  if (mutt_addr_cmp(aa, ab))
1453  {
1454  TAILQ_REMOVE(b, ab, entries);
1455  mutt_addr_free(&ab);
1456  break;
1457  }
1458  }
1459  }
1460 }
1461 
1468 void mutt_addrlist_clear(struct AddressList *al)
1469 {
1470  if (!al)
1471  return;
1472 
1473  struct Address *a = TAILQ_FIRST(al), *next = NULL;
1474  while (a)
1475  {
1476  next = TAILQ_NEXT(a, entries);
1477  mutt_addr_free(&a);
1478  a = next;
1479  }
1480  TAILQ_INIT(al);
1481 }
1482 
1488 void mutt_addrlist_append(struct AddressList *al, struct Address *a)
1489 {
1490  if (al && a)
1491  TAILQ_INSERT_TAIL(al, a, entries);
1492 }
1493 
1499 void mutt_addrlist_prepend(struct AddressList *al, struct Address *a)
1500 {
1501  if (al && a)
1502  TAILQ_INSERT_HEAD(al, a, entries);
1503 }
1504 
1510 bool mutt_addr_uses_unicode(const char *str)
1511 {
1512  if (!str)
1513  return false;
1514 
1515  while (*str)
1516  {
1517  if ((unsigned char) *str & (1 << 7))
1518  return true;
1519  str++;
1520  }
1521 
1522  return false;
1523 }
1524 
1530 bool mutt_addrlist_uses_unicode(const struct AddressList *al)
1531 {
1532  if (!al)
1533  {
1534  return false;
1535  }
1536 
1537  struct Address *a = NULL;
1538  TAILQ_FOREACH(a, al, entries)
1539  {
1540  if (a->mailbox && !a->group && mutt_addr_uses_unicode(a->mailbox))
1541  return true;
1542  }
1543  return false;
1544 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
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:1386
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:1468
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.
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1510
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:370
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:1349
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:1405
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
void mutt_addrlist_write_file(const struct AddressList *al, FILE *fp, int start_col, bool display)
Wrapper for mutt_write_address()
Definition: address.c:1231
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:548
#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:883
#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:759
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:1499
#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:743
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
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition: address.c:1201
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:631
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:1275
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1304
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:716
static bool addr_is_intl(const struct Address *a)
Does the Address have IDN components.
Definition: address.c:900
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
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:1441
#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:1150
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition: address.c:1530
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:1488