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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 #define safe_strdup strdup
31 #define safe_malloc malloc
32 #define SKIPWS(x) while(isspace(*x))x++
33 #define FREE(x) safe_free(x)
34 #define ISSPACE isspace
35 #define strfcpy(a,b,c) {if (c) {strncpy(a,b,c);a[c-1]=0;}}
36 #define LONG_STRING 1024
40 #include "mutt_idna.h"
42 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
43 a[(c)] = 0; } while (0)
45 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
48 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
49 #define is_special(x) strchr(RFC822Specials,x)
53 /* these must defined in the same order as the numerated errors given in rfc822.h */
54 const char *RFC822Errors[] = {
56 "mismatched parenthesis",
63 void rfc822_dequote_comment (char *s)
85 static void free_address (ADDRESS *a)
95 int rfc822_remove_from_adrlist (ADDRESS **a, const char *mailbox)
97 ADDRESS *p, *last = NULL, *t;
104 if (ascii_strcasecmp (mailbox, p->mailbox) == 0)
107 last->next = p->next;
125 void rfc822_free_address (ADDRESS **p)
143 parse_comment (const char *s,
144 char *comment, size_t *commentlen, size_t commentmax)
165 if (*commentlen < commentmax)
166 comment[(*commentlen)++] = *s;
171 RFC822Error = ERR_MISMATCH_PAREN;
178 parse_quote (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
182 if (*tokenlen < tokenmax)
183 token[*tokenlen] = *s;
189 if (*tokenlen < tokenmax)
190 token[*tokenlen] = *s;
197 RFC822Error = ERR_MISMATCH_QUOTE;
202 next_token (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
205 return (parse_comment (s + 1, token, tokenlen, tokenmax));
207 return (parse_quote (s + 1, token, tokenlen, tokenmax));
210 if (*tokenlen < tokenmax)
211 token[(*tokenlen)++] = *s;
216 if (ISSPACE ((unsigned char) *s) || is_special (*s))
218 if (*tokenlen < tokenmax)
219 token[(*tokenlen)++] = *s;
226 parse_mailboxdomain (const char *s, const char *nonspecial,
227 char *mailbox, size_t *mailboxlen, size_t mailboxmax,
228 char *comment, size_t *commentlen, size_t commentmax)
235 if (strchr (nonspecial, *s) == NULL && is_special (*s))
240 if (*commentlen && *commentlen < commentmax)
241 comment[(*commentlen)++] = ' ';
242 ps = next_token (s, comment, commentlen, commentmax);
245 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
255 parse_address (const char *s,
256 char *token, size_t *tokenlen, size_t tokenmax,
257 char *comment, size_t *commentlen, size_t commentmax,
260 s = parse_mailboxdomain (s, ".\"(\\",
261 token, tokenlen, tokenmax,
262 comment, commentlen, commentmax);
268 if (*tokenlen < tokenmax)
269 token[(*tokenlen)++] = '@';
270 s = parse_mailboxdomain (s + 1, ".([]\\",
271 token, tokenlen, tokenmax,
272 comment, commentlen, commentmax);
277 terminate_string (token, *tokenlen, tokenmax);
278 addr->mailbox = safe_strdup (token);
280 if (*commentlen && !addr->personal)
282 terminate_string (comment, *commentlen, commentmax);
283 addr->personal = safe_strdup (comment);
290 parse_route_addr (const char *s,
291 char *comment, size_t *commentlen, size_t commentmax,
294 char token[LONG_STRING];
299 /* find the end of the route */
302 while (s && *s == '@')
304 if (tokenlen < sizeof (token) - 1)
305 token[tokenlen++] = '@';
306 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
307 &tokenlen, sizeof (token) - 1,
308 comment, commentlen, commentmax);
312 RFC822Error = ERR_BAD_ROUTE;
313 return NULL; /* invalid route */
316 if (tokenlen < sizeof (token) - 1)
317 token[tokenlen++] = ':';
321 if ((s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr)) == NULL)
326 RFC822Error = ERR_BAD_ROUTE_ADDR;
331 addr->mailbox = safe_strdup ("@");
338 parse_addr_spec (const char *s,
339 char *comment, size_t *commentlen, size_t commentmax,
342 char token[LONG_STRING];
345 s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr);
346 if (s && *s && *s != ',' && *s != ';')
348 RFC822Error = ERR_BAD_ADDR_SPEC;
355 add_addrspec (ADDRESS **top, ADDRESS **last, const char *phrase,
356 char *comment, size_t *commentlen, size_t commentmax)
358 ADDRESS *cur = rfc822_new_address ();
360 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL)
362 rfc822_free_address (&cur);
373 ADDRESS *rfc822_parse_adrlist (ADDRESS *top, const char *s)
376 const char *begin, *ps;
377 char comment[LONG_STRING], phrase[LONG_STRING];
378 size_t phraselen = 0, commentlen = 0;
379 ADDRESS *cur, *last = NULL;
384 while (last && last->next)
387 ws_pending = isspace ((unsigned char) *s);
388 if ((nl = mutt_strlen (s)))
389 nl = s[nl - 1] == '\n';
399 terminate_buffer (phrase, phraselen);
400 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
402 else if (commentlen && last && !last->personal)
404 terminate_buffer (comment, commentlen);
405 last->personal = safe_strdup (comment);
409 if (last && !last->val)
410 last->val = mutt_substrdup (begin, s);
420 if (commentlen && commentlen < sizeof (comment) - 1)
421 comment[commentlen++] = ' ';
422 if ((ps = next_token (s, comment, &commentlen, sizeof (comment) - 1)) == NULL)
424 rfc822_free_address (&top);
431 if (phraselen && phraselen < sizeof (phrase) - 1)
432 phrase[phraselen++] = ' ';
433 if ((ps = parse_quote (s + 1, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
435 rfc822_free_address (&top);
442 cur = rfc822_new_address ();
443 terminate_buffer (phrase, phraselen);
444 cur->mailbox = safe_strdup (phrase);
454 last->val = mutt_substrdup (begin, s);
467 terminate_buffer (phrase, phraselen);
468 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
470 else if (commentlen && last && !last->personal)
472 terminate_buffer (comment, commentlen);
473 last->personal = safe_strdup (comment);
476 if (last && !last->val)
477 last->val = mutt_substrdup (begin, s);
480 /* add group terminator */
481 cur = rfc822_new_address ();
496 terminate_buffer (phrase, phraselen);
497 cur = rfc822_new_address ();
499 cur->personal = safe_strdup (phrase);
500 if ((ps = parse_route_addr (s + 1, comment, &commentlen, sizeof (comment) - 1, cur)) == NULL)
502 rfc822_free_address (&top);
503 rfc822_free_address (&cur);
519 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
520 phrase[phraselen++] = ' ';
521 if ((ps = next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
523 rfc822_free_address (&top);
528 ws_pending = isspace ((unsigned char) *s);
534 terminate_buffer (phrase, phraselen);
535 terminate_buffer (comment, commentlen);
536 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
538 else if (commentlen && last && !last->personal)
540 terminate_buffer (comment, commentlen);
541 last->personal = safe_strdup (comment);
545 last->val = mutt_substrdup (begin, s - nl < begin ? begin : s - nl);
551 void rfc822_qualify (ADDRESS *addr, const char *host)
555 for (; addr; addr = addr->next)
556 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL)
558 p = safe_malloc (mutt_strlen (addr->mailbox) + mutt_strlen (host) + 2);
559 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
560 FREE (&addr->mailbox);
566 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
568 if (strpbrk (value, specials))
570 char tmp[256], *pc = tmp;
571 size_t tmplen = sizeof (tmp) - 3;
574 for (; *value && tmplen > 1; value++)
576 if (*value == '\\' || *value == '"')
586 strfcpy (buf, tmp, buflen);
589 strfcpy (buf, value, buflen);
592 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr,
602 buflen--; /* save room for the terminal nul */
609 strfcpy (pbuf, addr->val, buflen);
610 len = mutt_strlen (pbuf);
627 if (strpbrk (addr->personal, RFC822Specials))
633 for (pc = addr->personal; *pc && buflen > 0; pc++)
635 if (*pc == '"' || *pc == '\\')
654 strfcpy (pbuf, addr->personal, buflen);
655 len = mutt_strlen (pbuf);
666 if (addr->personal || (addr->mailbox && *addr->mailbox == '@'))
678 if (ascii_strcmp (addr->mailbox, "@") && !display)
680 strfcpy (pbuf, addr->mailbox, buflen);
681 len = mutt_strlen (pbuf);
683 else if (ascii_strcmp (addr->mailbox, "@") && display)
685 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
686 len = mutt_strlen (pbuf);
696 if (addr->personal || (addr->mailbox && *addr->mailbox == '@'))
724 /* no need to check for length here since we already save space at the
725 beginning of this routine */
729 /* note: it is assumed that `buf' is nul terminated! */
730 int rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr, int display)
733 size_t len = mutt_strlen (buf);
735 buflen--; /* save room for the terminal nul */
740 return pbuf - buf; /* safety check for bogus arguments */
754 for (; addr && buflen > 0; addr = addr->next)
756 /* use buflen+1 here because we already saved space for the trailing
757 nul char, and the subroutine can make use of it */
758 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
760 /* this should be safe since we always have at least 1 char passed into
761 the above call, which means `pbuf' should always be nul terminated */
762 len = mutt_strlen (pbuf);
766 /* if there is another address, and its not a group mailbox name or
767 group terminator, add a comma to separate the addresses */
768 if (addr->next && addr->next->mailbox && !addr->group)
785 /* this should be rfc822_cpy_adr */
786 ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr)
788 ADDRESS *p = rfc822_new_address ();
791 p->val = safe_strdup (addr->val);
793 p->personal = safe_strdup (addr->personal);
794 p->mailbox = safe_strdup (addr->mailbox);
795 p->group = addr->group;
799 /* this should be rfc822_cpy_adrlist */
800 ADDRESS *rfc822_cpy_adr (ADDRESS *addr, int prune)
802 ADDRESS *top = NULL, *last = NULL;
804 for (; addr; addr = addr->next)
806 if (prune && addr->group && (!addr->next || !addr->next->mailbox))
813 last->next = rfc822_cpy_adr_real (addr);
817 top = last = rfc822_cpy_adr_real (addr);
822 /* append list 'b' to list 'a' and return the last element in the new list */
823 ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b, int prune)
827 while (tmp && tmp->next)
832 tmp->next = rfc822_cpy_adr (b, prune);
834 tmp = *a = rfc822_cpy_adr (b, prune);
835 while (tmp && tmp->next)
840 /* incomplete. Only used to thwart the APOP MD5 attack (#2846). */
841 int rfc822_valid_msgid (const char *msgid)
843 /* msg-id = "<" addr-spec ">"
844 * addr-spec = local-part "@" domain
845 * local-part = word *("." word)
846 * word = atom / quoted-string
847 * atom = 1*<any CHAR except specials, SPACE and CTLs>
849 * specials = "(" / ")" / "<" / ">" / "@"
850 / "," / ";" / ":" / "\" / <">
853 * CTLS = ( 0.-31., 127.)
854 * quoted-string = <"> *(qtext/quoted-pair) <">
855 * qtext = <any CHAR except <">, "\" and CR>
857 * quoted-pair = "\" CHAR
858 * domain = sub-domain *("." sub-domain)
859 * sub-domain = domain-ref / domain-literal
861 * domain-literal = "[" *(dtext / quoted-pair) "]"
866 if (!msgid || !*msgid)
869 l = mutt_strlen (msgid);
870 if (l < 5) /* <atom@atom> */
872 if (msgid[0] != '<' || msgid[l-1] != '>')
874 if (!(strrchr (msgid, '@')))
877 /* TODO: complete parser */
878 for (i = 0; i < l; i++)
879 if ((unsigned char)msgid[i] > 127)
886 int safe_free (void **p) /* __SAFE_FREE_CHECKED__ */
888 free(*p); /* __MEM_CHECKED__ */
892 int main (int argc, char **argv)
897 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)";
899 char *str = "a b c ";
902 list = rfc822_parse_adrlist (NULL, str);
904 rfc822_write_address (buf, sizeof (buf), list);
905 rfc822_free_address (&list);