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.
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;}}
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[] = "@.,:;<>[]\\\"()";
50 #define is_special(x) strchr(RFC822Specials,x)
54 /* these must defined in the same order as the numerated errors given in rfc822.h */
55 const char *RFC822Errors[] = {
57 "mismatched parenthesis",
64 void rfc822_dequote_comment (char *s)
74 else if (*s != '\"') {
83 void rfc822_free_address (ADDRESS ** p)
99 static const char *parse_comment (const char *s,
100 char *comment, size_t * commentlen,
105 while (*s && level) {
108 else if (*s == ')') {
114 else if (*s == '\\') {
118 if (*commentlen < commentmax)
119 comment[(*commentlen)++] = *s;
123 RFC822Error = ERR_MISMATCH_PAREN;
129 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
132 if (*tokenlen < tokenmax)
133 token[(*tokenlen)++] = '"';
135 if (*tokenlen < tokenmax)
136 token[*tokenlen] = *s;
145 if (*tokenlen < tokenmax)
146 token[*tokenlen] = *s;
151 RFC822Error = ERR_MISMATCH_QUOTE;
155 static const char *next_token (const char *s, char *token, size_t * tokenlen,
159 return (parse_comment (s + 1, token, tokenlen, tokenmax));
161 return (parse_quote (s + 1, token, tokenlen, tokenmax));
162 if (is_special (*s)) {
163 if (*tokenlen < tokenmax)
164 token[(*tokenlen)++] = *s;
168 if (ISSPACE ((unsigned char) *s) || is_special (*s))
170 if (*tokenlen < tokenmax)
171 token[(*tokenlen)++] = *s;
177 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
178 char *mailbox, size_t * mailboxlen,
179 size_t mailboxmax, char *comment,
187 if (strchr (nonspecial, *s) == NULL && is_special (*s))
191 if (*commentlen && *commentlen < commentmax)
192 comment[(*commentlen)++] = ' ';
193 ps = next_token (s, comment, commentlen, commentmax);
196 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
205 static const char *parse_address (const char *s,
206 char *token, size_t * tokenlen,
207 size_t tokenmax, char *comment,
208 size_t * commentlen, size_t commentmax,
211 s = parse_mailboxdomain (s, ".\"(\\",
212 token, tokenlen, tokenmax,
213 comment, commentlen, commentmax);
218 if (*tokenlen < tokenmax)
219 token[(*tokenlen)++] = '@';
220 s = parse_mailboxdomain (s + 1, ".([]\\",
221 token, tokenlen, tokenmax,
222 comment, commentlen, commentmax);
227 terminate_string (token, *tokenlen, tokenmax);
228 addr->mailbox = safe_strdup (token);
230 if (*commentlen && !addr->personal) {
231 terminate_string (comment, *commentlen, commentmax);
232 addr->personal = safe_strdup (comment);
238 static const char *parse_route_addr (const char *s,
239 char *comment, size_t * commentlen,
240 size_t commentmax, ADDRESS * addr)
247 /* find the end of the route */
249 while (s && *s == '@') {
250 if (tokenlen < sizeof (token) - 1)
251 token[tokenlen++] = '@';
252 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
253 &tokenlen, sizeof (token) - 1,
254 comment, commentlen, commentmax);
256 if (!s || *s != ':') {
257 RFC822Error = ERR_BAD_ROUTE;
258 return NULL; /* invalid route */
261 if (tokenlen < sizeof (token) - 1)
262 token[tokenlen++] = ':';
267 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
268 commentlen, commentmax, addr)) == NULL)
272 RFC822Error = ERR_BAD_ROUTE_ADDR;
277 addr->mailbox = safe_strdup ("@");
283 static const char *parse_addr_spec (const char *s,
284 char *comment, size_t * commentlen,
285 size_t commentmax, ADDRESS * addr)
291 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
292 commentlen, commentmax, addr);
293 if (s && *s && *s != ',' && *s != ';') {
294 RFC822Error = ERR_BAD_ADDR_SPEC;
301 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
302 char *comment, size_t * commentlen, size_t commentmax)
304 ADDRESS *cur = rfc822_new_address ();
306 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
307 rfc822_free_address (&cur);
318 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
321 const char *begin, *ps;
322 char comment[STRING], phrase[STRING];
323 size_t phraselen = 0, commentlen = 0;
324 ADDRESS *cur, *last = NULL;
329 while (last && last->next)
332 ws_pending = isspace ((unsigned char) *s);
339 terminate_buffer (phrase, phraselen);
340 add_addrspec (&top, &last, phrase, comment, &commentlen,
341 sizeof (comment) - 1);
343 else if (commentlen && last && !last->personal) {
344 terminate_buffer (comment, commentlen);
345 last->personal = safe_strdup (comment);
349 if (last && !last->val)
350 last->val = mutt_substrdup (begin, s);
358 else if (*s == '(') {
359 if (commentlen && commentlen < sizeof (comment) - 1)
360 comment[commentlen++] = ' ';
362 next_token (s, comment, &commentlen,
363 sizeof (comment) - 1)) == NULL) {
364 rfc822_free_address (&top);
369 else if (*s == ':') {
370 cur = rfc822_new_address ();
371 terminate_buffer (phrase, phraselen);
372 cur->mailbox = safe_strdup (phrase);
382 last->val = mutt_substrdup (begin, s);
391 else if (*s == ';') {
393 terminate_buffer (phrase, phraselen);
394 add_addrspec (&top, &last, phrase, comment, &commentlen,
395 sizeof (comment) - 1);
397 else if (commentlen && last && !last->personal) {
398 terminate_buffer (comment, commentlen);
399 last->personal = safe_strdup (comment);
402 if (last && !last->val)
403 last->val = mutt_substrdup (begin, s);
406 /* add group terminator */
407 cur = rfc822_new_address ();
419 else if (*s == '<') {
420 terminate_buffer (phrase, phraselen);
421 cur = rfc822_new_address ();
424 FREE (&cur->personal);
425 /* if we get something like "Michael R. Elkins" remove the quotes */
426 rfc822_dequote_comment (phrase);
427 cur->personal = safe_strdup (phrase);
430 parse_route_addr (s + 1, comment, &commentlen,
431 sizeof (comment) - 1, cur)) == NULL) {
432 rfc822_free_address (&top);
433 rfc822_free_address (&cur);
448 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
449 phrase[phraselen++] = ' ';
451 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
452 rfc822_free_address (&top);
457 ws_pending = isspace ((unsigned char) *s);
462 terminate_buffer (phrase, phraselen);
463 terminate_buffer (comment, commentlen);
464 add_addrspec (&top, &last, phrase, comment, &commentlen,
465 sizeof (comment) - 1);
467 else if (commentlen && last && !last->personal) {
468 terminate_buffer (comment, commentlen);
469 last->personal = safe_strdup (comment);
473 last->val = mutt_substrdup (begin, s);
479 void rfc822_qualify (ADDRESS * addr, const char *host)
483 for (; addr; addr = addr->next)
484 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
485 p = safe_malloc (mutt_strlen (addr->mailbox) + mutt_strlen (host) + 2);
486 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
487 FREE (&addr->mailbox);
493 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
495 if (strpbrk (value, specials)) {
496 char tmp[256], *pc = tmp;
497 size_t tmplen = sizeof (tmp) - 3;
500 for (; *value && tmplen > 1; value++) {
501 if (*value == '\\' || *value == '"') {
510 strfcpy (buf, tmp, buflen);
513 strfcpy (buf, value, buflen);
516 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
526 buflen--; /* save room for the terminal nul */
532 strfcpy (pbuf, addr->val, buflen);
533 len = mutt_strlen (pbuf);
547 if (addr->personal) {
548 if (strpbrk (addr->personal, RFC822Specials)) {
553 for (pc = addr->personal; *pc && buflen > 0; pc++) {
554 if (*pc == '"' || *pc == '\\') {
573 strfcpy (pbuf, addr->personal, buflen);
574 len = mutt_strlen (pbuf);
585 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
595 if (ascii_strcmp (addr->mailbox, "@") && !display) {
596 strfcpy (pbuf, addr->mailbox, buflen);
597 len = mutt_strlen (pbuf);
599 else if (ascii_strcmp (addr->mailbox, "@") && display) {
600 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
601 len = mutt_strlen (pbuf);
610 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
635 /* no need to check for length here since we already save space at the
636 beginning of this routine */
640 /* note: it is assumed that `buf' is nul terminated! */
641 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
645 size_t len = mutt_strlen (buf);
647 buflen--; /* save room for the terminal nul */
651 return; /* safety check for bogus arguments */
665 for (; addr && buflen > 0; addr = addr->next) {
666 /* use buflen+1 here because we already saved space for the trailing
667 nul char, and the subroutine can make use of it */
668 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
670 /* this should be safe since we always have at least 1 char passed into
671 the above call, which means `pbuf' should always be nul terminated */
672 len = mutt_strlen (pbuf);
676 /* if there is another address, and its not a group mailbox name or
677 group terminator, add a comma to separate the addresses */
678 if (addr->next && addr->next->mailbox && !addr->group) {
693 /* this should be rfc822_cpy_adr */
694 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
696 ADDRESS *p = rfc822_new_address ();
699 p->val = safe_strdup (addr->val);
701 p->personal = safe_strdup (addr->personal);
702 p->mailbox = safe_strdup (addr->mailbox);
703 p->group = addr->group;
707 /* this should be rfc822_cpy_adrlist */
708 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
710 ADDRESS *top = NULL, *last = NULL;
712 for (; addr; addr = addr->next) {
714 last->next = rfc822_cpy_adr_real (addr);
718 top = last = rfc822_cpy_adr_real (addr);
723 /* append list 'b' to list 'a' and return the last element in the new list */
724 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
728 while (tmp && tmp->next)
733 tmp->next = rfc822_cpy_adr (b);
735 tmp = *a = rfc822_cpy_adr (b);
736 while (tmp && tmp->next)
742 int safe_free (void **p)
744 free (*p); /* __MEM_CHECKED__ */
748 int main (int argc, char **argv)
755 "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)";
757 char *str = "a b c ";
760 list = rfc822_parse_adrlist (NULL, str);
762 rfc822_write_address (buf, sizeof (buf), list);
763 rfc822_free_address (&list);