2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 #define safe_strdup strdup
27 #define safe_malloc malloc
28 #define SKIPWS(x) while(isspace(*x))x++
29 #define FREE(x) safe_free(x)
30 #define ISSPACE isspace
31 #define strfcpy(a,b,c) {if (c) {strncpy(a,b,c);a[c-1]=0;}}
36 #include "mutt_idna.h"
38 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
39 a[(c)] = 0; } while (0)
41 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
44 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)
81 void rfc822_free_address (ADDRESS **p)
99 parse_comment (const char *s,
100 char *comment, size_t *commentlen, size_t commentmax)
121 if (*commentlen < commentmax)
122 comment[(*commentlen)++] = *s;
127 RFC822Error = ERR_MISMATCH_PAREN;
134 parse_quote (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
136 if (*tokenlen < tokenmax)
137 token[(*tokenlen)++] = '"';
140 if (*tokenlen < tokenmax)
141 token[*tokenlen] = *s;
152 if (*tokenlen < tokenmax)
153 token[*tokenlen] = *s;
158 RFC822Error = ERR_MISMATCH_QUOTE;
163 next_token (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
166 return (parse_comment (s + 1, token, tokenlen, tokenmax));
168 return (parse_quote (s + 1, token, tokenlen, tokenmax));
171 if (*tokenlen < tokenmax)
172 token[(*tokenlen)++] = *s;
177 if (ISSPACE ((unsigned char) *s) || is_special (*s))
179 if (*tokenlen < tokenmax)
180 token[(*tokenlen)++] = *s;
187 parse_mailboxdomain (const char *s, const char *nonspecial,
188 char *mailbox, size_t *mailboxlen, size_t mailboxmax,
189 char *comment, size_t *commentlen, size_t commentmax)
196 if (strchr (nonspecial, *s) == NULL && is_special (*s))
201 if (*commentlen && *commentlen < commentmax)
202 comment[(*commentlen)++] = ' ';
203 ps = next_token (s, comment, commentlen, commentmax);
206 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
216 parse_address (const char *s,
217 char *token, size_t *tokenlen, size_t tokenmax,
218 char *comment, size_t *commentlen, size_t commentmax,
221 s = parse_mailboxdomain (s, ".\"(\\",
222 token, tokenlen, tokenmax,
223 comment, commentlen, commentmax);
229 if (*tokenlen < tokenmax)
230 token[(*tokenlen)++] = '@';
231 s = parse_mailboxdomain (s + 1, ".([]\\",
232 token, tokenlen, tokenmax,
233 comment, commentlen, commentmax);
238 terminate_string (token, *tokenlen, tokenmax);
239 addr->mailbox = safe_strdup (token);
241 if (*commentlen && !addr->personal)
243 terminate_string (comment, *commentlen, commentmax);
244 addr->personal = safe_strdup (comment);
251 parse_route_addr (const char *s,
252 char *comment, size_t *commentlen, size_t commentmax,
260 /* find the end of the route */
263 while (s && *s == '@')
265 if (tokenlen < sizeof (token) - 1)
266 token[tokenlen++] = '@';
267 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
268 &tokenlen, sizeof (token) - 1,
269 comment, commentlen, commentmax);
273 RFC822Error = ERR_BAD_ROUTE;
274 return NULL; /* invalid route */
277 if (tokenlen < sizeof (token) - 1)
278 token[tokenlen++] = ':';
282 if ((s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr)) == NULL)
287 RFC822Error = ERR_BAD_ROUTE_ADDR;
292 addr->mailbox = safe_strdup ("@");
299 parse_addr_spec (const char *s,
300 char *comment, size_t *commentlen, size_t commentmax,
306 s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr);
307 if (s && *s && *s != ',' && *s != ';')
309 RFC822Error = ERR_BAD_ADDR_SPEC;
316 add_addrspec (ADDRESS **top, ADDRESS **last, const char *phrase,
317 char *comment, size_t *commentlen, size_t commentmax)
319 ADDRESS *cur = rfc822_new_address ();
321 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL)
323 rfc822_free_address (&cur);
334 ADDRESS *rfc822_parse_adrlist (ADDRESS *top, const char *s)
337 const char *begin, *ps;
338 char comment[STRING], phrase[STRING];
339 size_t phraselen = 0, commentlen = 0;
340 ADDRESS *cur, *last = NULL;
345 while (last && last->next)
348 ws_pending = isspace ((unsigned char) *s);
358 terminate_buffer (phrase, phraselen);
359 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
361 else if (commentlen && last && !last->personal)
363 terminate_buffer (comment, commentlen);
364 last->personal = safe_strdup (comment);
368 if (last && !last->val)
369 last->val = mutt_substrdup (begin, s);
379 if (commentlen && commentlen < sizeof (comment) - 1)
380 comment[commentlen++] = ' ';
381 if ((ps = next_token (s, comment, &commentlen, sizeof (comment) - 1)) == NULL)
383 rfc822_free_address (&top);
390 cur = rfc822_new_address ();
391 terminate_buffer (phrase, phraselen);
392 cur->mailbox = safe_strdup (phrase);
402 last->val = mutt_substrdup (begin, s);
415 terminate_buffer (phrase, phraselen);
416 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
418 else if (commentlen && last && !last->personal)
420 terminate_buffer (comment, commentlen);
421 last->personal = safe_strdup (comment);
424 if (last && !last->val)
425 last->val = mutt_substrdup (begin, s);
428 /* add group terminator */
429 cur = rfc822_new_address ();
444 terminate_buffer (phrase, phraselen);
445 cur = rfc822_new_address ();
449 FREE (&cur->personal);
450 /* if we get something like "Michael R. Elkins" remove the quotes */
451 rfc822_dequote_comment (phrase);
452 cur->personal = safe_strdup (phrase);
454 if ((ps = parse_route_addr (s + 1, comment, &commentlen, sizeof (comment) - 1, cur)) == NULL)
456 rfc822_free_address (&top);
457 rfc822_free_address (&cur);
473 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
474 phrase[phraselen++] = ' ';
475 if ((ps = next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
477 rfc822_free_address (&top);
482 ws_pending = isspace ((unsigned char) *s);
488 terminate_buffer (phrase, phraselen);
489 terminate_buffer (comment, commentlen);
490 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
492 else if (commentlen && last && !last->personal)
494 terminate_buffer (comment, commentlen);
495 last->personal = safe_strdup (comment);
499 last->val = mutt_substrdup (begin, s);
505 void rfc822_qualify (ADDRESS *addr, const char *host)
509 for (; addr; addr = addr->next)
510 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL)
512 p = safe_malloc (mutt_strlen (addr->mailbox) + mutt_strlen (host) + 2);
513 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
514 FREE (&addr->mailbox);
520 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
522 if (strpbrk (value, specials))
524 char tmp[256], *pc = tmp;
525 size_t tmplen = sizeof (tmp) - 3;
528 for (; *value && tmplen > 1; value++)
530 if (*value == '\\' || *value == '"')
540 strfcpy (buf, tmp, buflen);
543 strfcpy (buf, value, buflen);
546 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr,
556 buflen--; /* save room for the terminal nul */
563 strfcpy (pbuf, addr->val, buflen);
564 len = mutt_strlen (pbuf);
581 if (strpbrk (addr->personal, RFC822Specials))
587 for (pc = addr->personal; *pc && buflen > 0; pc++)
589 if (*pc == '"' || *pc == '\\')
610 strfcpy (pbuf, addr->personal, buflen);
611 len = mutt_strlen (pbuf);
622 if (addr->personal || (addr->mailbox && *addr->mailbox == '@'))
634 if (ascii_strcmp (addr->mailbox, "@") && !display)
636 strfcpy (pbuf, addr->mailbox, buflen);
637 len = mutt_strlen (pbuf);
639 else if (ascii_strcmp (addr->mailbox, "@") && display)
641 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
642 len = mutt_strlen (pbuf);
652 if (addr->personal || (addr->mailbox && *addr->mailbox == '@'))
680 /* no need to check for length here since we already save space at the
681 beginning of this routine */
685 /* note: it is assumed that `buf' is nul terminated! */
686 void rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr, int display)
689 size_t len = mutt_strlen (buf);
691 buflen--; /* save room for the terminal nul */
696 return; /* safety check for bogus arguments */
710 for (; addr && buflen > 0; addr = addr->next)
712 /* use buflen+1 here because we already saved space for the trailing
713 nul char, and the subroutine can make use of it */
714 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
716 /* this should be safe since we always have at least 1 char passed into
717 the above call, which means `pbuf' should always be nul terminated */
718 len = mutt_strlen (pbuf);
722 /* if there is another address, and its not a group mailbox name or
723 group terminator, add a comma to separate the addresses */
724 if (addr->next && addr->next->mailbox && !addr->group)
740 /* this should be rfc822_cpy_adr */
741 ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr)
743 ADDRESS *p = rfc822_new_address ();
746 p->val = safe_strdup (addr->val);
748 p->personal = safe_strdup (addr->personal);
749 p->mailbox = safe_strdup (addr->mailbox);
750 p->group = addr->group;
754 /* this should be rfc822_cpy_adrlist */
755 ADDRESS *rfc822_cpy_adr (ADDRESS *addr)
757 ADDRESS *top = NULL, *last = NULL;
759 for (; addr; addr = addr->next)
763 last->next = rfc822_cpy_adr_real (addr);
767 top = last = rfc822_cpy_adr_real (addr);
772 /* append list 'b' to list 'a' and return the last element in the new list */
773 ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b)
777 while (tmp && tmp->next)
782 tmp->next = rfc822_cpy_adr (b);
784 tmp = *a = rfc822_cpy_adr (b);
785 while (tmp && tmp->next)
791 int safe_free (void **p)
793 free(*p); /* __MEM_CHECKED__ */
797 int main (int argc, char **argv)
802 char *str = "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)";
804 char *str = "a b c ";
807 list = rfc822_parse_adrlist (NULL, str);
809 rfc822_write_address (buf, sizeof (buf), list);
810 rfc822_free_address (&list);