2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
21 #define safe_strdup strdup
22 #define safe_malloc malloc
23 #define SKIPWS(x) while(isspace(*x))x++
24 #define FREE(x) safe_free(x)
25 #define ISSPACE isspace
26 #define strfcpy(a,b,c) {if (c) {strncpy(a,b,c);a[c-1]=0;}}
31 #include "mutt_idna.h"
37 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
38 a[(c)] = 0; } while (0)
40 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
43 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
45 #define is_special(x) strchr(RFC822Specials,x)
49 /* these must defined in the same order as the numerated errors given in rfc822.h */
50 const char *RFC822Errors[] = {
52 "mismatched parenthesis",
59 void rfc822_dequote_comment (char *s)
69 else if (*s != '\"') {
78 void rfc822_free_address (ADDRESS ** p)
94 static const char *parse_comment (const char *s,
95 char *comment, size_t * commentlen,
100 while (*s && level) {
103 else if (*s == ')') {
109 else if (*s == '\\') {
113 if (*commentlen < commentmax)
114 comment[(*commentlen)++] = *s;
118 RFC822Error = ERR_MISMATCH_PAREN;
124 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
127 if (*tokenlen < tokenmax)
128 token[(*tokenlen)++] = '"';
130 if (*tokenlen < tokenmax)
131 token[*tokenlen] = *s;
140 if (*tokenlen < tokenmax)
141 token[*tokenlen] = *s;
146 RFC822Error = ERR_MISMATCH_QUOTE;
150 static const char *next_token (const char *s, char *token, size_t * tokenlen,
154 return (parse_comment (s + 1, token, tokenlen, tokenmax));
156 return (parse_quote (s + 1, token, tokenlen, tokenmax));
157 if (is_special (*s)) {
158 if (*tokenlen < tokenmax)
159 token[(*tokenlen)++] = *s;
163 if (ISSPACE ((unsigned char) *s) || is_special (*s))
165 if (*tokenlen < tokenmax)
166 token[(*tokenlen)++] = *s;
172 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
173 char *mailbox, size_t * mailboxlen,
174 size_t mailboxmax, char *comment,
182 if (strchr (nonspecial, *s) == NULL && is_special (*s))
186 if (*commentlen && *commentlen < commentmax)
187 comment[(*commentlen)++] = ' ';
188 ps = next_token (s, comment, commentlen, commentmax);
191 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
200 static const char *parse_address (const char *s,
201 char *token, size_t * tokenlen,
202 size_t tokenmax, char *comment,
203 size_t * commentlen, size_t commentmax,
206 s = parse_mailboxdomain (s, ".\"(\\",
207 token, tokenlen, tokenmax,
208 comment, commentlen, commentmax);
213 if (*tokenlen < tokenmax)
214 token[(*tokenlen)++] = '@';
215 s = parse_mailboxdomain (s + 1, ".([]\\",
216 token, tokenlen, tokenmax,
217 comment, commentlen, commentmax);
222 terminate_string (token, *tokenlen, tokenmax);
223 addr->mailbox = safe_strdup (token);
225 if (*commentlen && !addr->personal) {
226 terminate_string (comment, *commentlen, commentmax);
227 addr->personal = safe_strdup (comment);
233 static const char *parse_route_addr (const char *s,
234 char *comment, size_t * commentlen,
235 size_t commentmax, ADDRESS * addr)
242 /* find the end of the route */
244 while (s && *s == '@') {
245 if (tokenlen < sizeof (token) - 1)
246 token[tokenlen++] = '@';
247 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
248 &tokenlen, sizeof (token) - 1,
249 comment, commentlen, commentmax);
251 if (!s || *s != ':') {
252 RFC822Error = ERR_BAD_ROUTE;
253 return NULL; /* invalid route */
256 if (tokenlen < sizeof (token) - 1)
257 token[tokenlen++] = ':';
262 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
263 commentlen, commentmax, addr)) == NULL)
267 RFC822Error = ERR_BAD_ROUTE_ADDR;
272 addr->mailbox = safe_strdup ("@");
278 static const char *parse_addr_spec (const char *s,
279 char *comment, size_t * commentlen,
280 size_t commentmax, ADDRESS * addr)
286 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
287 commentlen, commentmax, addr);
288 if (s && *s && *s != ',' && *s != ';') {
289 RFC822Error = ERR_BAD_ADDR_SPEC;
296 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
297 char *comment, size_t * commentlen, size_t commentmax)
299 ADDRESS *cur = rfc822_new_address ();
301 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
302 rfc822_free_address (&cur);
313 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
316 const char *begin, *ps;
317 char comment[STRING], phrase[STRING];
318 size_t phraselen = 0, commentlen = 0;
319 ADDRESS *cur, *last = NULL;
324 while (last && last->next)
327 ws_pending = isspace ((unsigned char) *s);
334 terminate_buffer (phrase, phraselen);
335 add_addrspec (&top, &last, phrase, comment, &commentlen,
336 sizeof (comment) - 1);
338 else if (commentlen && last && !last->personal) {
339 terminate_buffer (comment, commentlen);
340 last->personal = safe_strdup (comment);
344 if (last && !last->val)
345 last->val = mutt_substrdup (begin, s);
353 else if (*s == '(') {
354 if (commentlen && commentlen < sizeof (comment) - 1)
355 comment[commentlen++] = ' ';
357 next_token (s, comment, &commentlen,
358 sizeof (comment) - 1)) == NULL) {
359 rfc822_free_address (&top);
364 else if (*s == ':') {
365 cur = rfc822_new_address ();
366 terminate_buffer (phrase, phraselen);
367 cur->mailbox = safe_strdup (phrase);
377 last->val = mutt_substrdup (begin, s);
386 else if (*s == ';') {
388 terminate_buffer (phrase, phraselen);
389 add_addrspec (&top, &last, phrase, comment, &commentlen,
390 sizeof (comment) - 1);
392 else if (commentlen && last && !last->personal) {
393 terminate_buffer (comment, commentlen);
394 last->personal = safe_strdup (comment);
397 if (last && !last->val)
398 last->val = mutt_substrdup (begin, s);
401 /* add group terminator */
402 cur = rfc822_new_address ();
414 else if (*s == '<') {
415 terminate_buffer (phrase, phraselen);
416 cur = rfc822_new_address ();
419 FREE (&cur->personal);
420 /* if we get something like "Michael R. Elkins" remove the quotes */
421 rfc822_dequote_comment (phrase);
422 cur->personal = safe_strdup (phrase);
425 parse_route_addr (s + 1, comment, &commentlen,
426 sizeof (comment) - 1, cur)) == NULL) {
427 rfc822_free_address (&top);
428 rfc822_free_address (&cur);
443 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
444 phrase[phraselen++] = ' ';
446 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
447 rfc822_free_address (&top);
452 ws_pending = isspace ((unsigned char) *s);
457 terminate_buffer (phrase, phraselen);
458 terminate_buffer (comment, commentlen);
459 add_addrspec (&top, &last, phrase, comment, &commentlen,
460 sizeof (comment) - 1);
462 else if (commentlen && last && !last->personal) {
463 terminate_buffer (comment, commentlen);
464 last->personal = safe_strdup (comment);
468 last->val = mutt_substrdup (begin, s);
474 void rfc822_qualify (ADDRESS * addr, const char *host)
478 for (; addr; addr = addr->next)
479 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
480 p = safe_malloc (mutt_strlen (addr->mailbox) + mutt_strlen (host) + 2);
481 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
482 FREE (&addr->mailbox);
488 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
490 if (strpbrk (value, specials)) {
491 char tmp[256], *pc = tmp;
492 size_t tmplen = sizeof (tmp) - 3;
495 for (; *value && tmplen > 1; value++) {
496 if (*value == '\\' || *value == '"') {
505 strfcpy (buf, tmp, buflen);
508 strfcpy (buf, value, buflen);
511 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
521 buflen--; /* save room for the terminal nul */
527 strfcpy (pbuf, addr->val, buflen);
528 len = mutt_strlen (pbuf);
542 if (addr->personal) {
543 if (strpbrk (addr->personal, RFC822Specials)) {
548 for (pc = addr->personal; *pc && buflen > 0; pc++) {
549 if (*pc == '"' || *pc == '\\') {
568 strfcpy (pbuf, addr->personal, buflen);
569 len = mutt_strlen (pbuf);
580 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
590 if (ascii_strcmp (addr->mailbox, "@") && !display) {
591 strfcpy (pbuf, addr->mailbox, buflen);
592 len = mutt_strlen (pbuf);
594 else if (ascii_strcmp (addr->mailbox, "@") && display) {
595 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
596 len = mutt_strlen (pbuf);
605 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
630 /* no need to check for length here since we already save space at the
631 beginning of this routine */
635 /* note: it is assumed that `buf' is nul terminated! */
636 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
640 size_t len = mutt_strlen (buf);
642 buflen--; /* save room for the terminal nul */
646 return; /* safety check for bogus arguments */
660 for (; addr && buflen > 0; addr = addr->next) {
661 /* use buflen+1 here because we already saved space for the trailing
662 nul char, and the subroutine can make use of it */
663 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
665 /* this should be safe since we always have at least 1 char passed into
666 the above call, which means `pbuf' should always be nul terminated */
667 len = mutt_strlen (pbuf);
671 /* if there is another address, and its not a group mailbox name or
672 group terminator, add a comma to separate the addresses */
673 if (addr->next && addr->next->mailbox && !addr->group) {
688 /* this should be rfc822_cpy_adr */
689 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
691 ADDRESS *p = rfc822_new_address ();
694 p->val = safe_strdup (addr->val);
696 p->personal = safe_strdup (addr->personal);
697 p->mailbox = safe_strdup (addr->mailbox);
698 p->group = addr->group;
702 /* this should be rfc822_cpy_adrlist */
703 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
705 ADDRESS *top = NULL, *last = NULL;
707 for (; addr; addr = addr->next) {
709 last->next = rfc822_cpy_adr_real (addr);
713 top = last = rfc822_cpy_adr_real (addr);
718 /* append list 'b' to list 'a' and return the last element in the new list */
719 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
723 while (tmp && tmp->next)
728 tmp->next = rfc822_cpy_adr (b);
730 tmp = *a = rfc822_cpy_adr (b);
731 while (tmp && tmp->next)
737 int safe_free (void **p)
739 free (*p); /* __MEM_CHECKED__ */
743 int main (int argc, char **argv)
750 "michael, Michael Elkins <me@mutt.org>, testing a really complex address: this example <@contains.a.source.route,@with.multiple.hosts:address@example.com>;, lothar@of.the.hillpeople (lothar)";
752 char *str = "a b c ";
755 list = rfc822_parse_adrlist (NULL, str);
757 rfc822_write_address (buf, sizeof (buf), list);
758 rfc822_free_address (&list);