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.
19 #include "mutt_idna.h"
25 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
26 a[(c)] = 0; } while (0)
28 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
31 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
33 #define is_special(x) strchr(RFC822Specials,x)
37 /* these must defined in the same order as the numerated errors given in rfc822.h */
38 const char *RFC822Errors[] = {
40 "mismatched parenthesis",
47 void rfc822_dequote_comment (char *s)
57 else if (*s != '\"') {
66 void rfc822_free_address (ADDRESS ** p)
79 static const char *parse_comment (const char *s,
80 char *comment, size_t * commentlen,
94 else if (*s == '\\') {
98 if (*commentlen < commentmax)
99 comment[(*commentlen)++] = *s;
103 RFC822Error = ERR_MISMATCH_PAREN;
109 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
112 if (*tokenlen < tokenmax)
113 token[(*tokenlen)++] = '"';
115 if (*tokenlen < tokenmax)
116 token[*tokenlen] = *s;
125 if (*tokenlen < tokenmax)
126 token[*tokenlen] = *s;
131 RFC822Error = ERR_MISMATCH_QUOTE;
135 static const char *next_token (const char *s, char *token, size_t * tokenlen,
139 return (parse_comment (s + 1, token, tokenlen, tokenmax));
141 return (parse_quote (s + 1, token, tokenlen, tokenmax));
142 if (is_special (*s)) {
143 if (*tokenlen < tokenmax)
144 token[(*tokenlen)++] = *s;
148 if (ISSPACE ((unsigned char) *s) || is_special (*s))
150 if (*tokenlen < tokenmax)
151 token[(*tokenlen)++] = *s;
157 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
158 char *mailbox, size_t * mailboxlen,
159 size_t mailboxmax, char *comment,
167 if (strchr (nonspecial, *s) == NULL && is_special (*s))
171 if (*commentlen && *commentlen < commentmax)
172 comment[(*commentlen)++] = ' ';
173 ps = next_token (s, comment, commentlen, commentmax);
176 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
185 static const char *parse_address (const char *s,
186 char *token, size_t * tokenlen,
187 size_t tokenmax, char *comment,
188 size_t * commentlen, size_t commentmax,
191 s = parse_mailboxdomain (s, ".\"(\\",
192 token, tokenlen, tokenmax,
193 comment, commentlen, commentmax);
198 if (*tokenlen < tokenmax)
199 token[(*tokenlen)++] = '@';
200 s = parse_mailboxdomain (s + 1, ".([]\\",
201 token, tokenlen, tokenmax,
202 comment, commentlen, commentmax);
207 terminate_string (token, *tokenlen, tokenmax);
208 addr->mailbox = safe_strdup (token);
210 if (*commentlen && !addr->personal) {
211 terminate_string (comment, *commentlen, commentmax);
212 addr->personal = safe_strdup (comment);
218 static const char *parse_route_addr (const char *s,
219 char *comment, size_t * commentlen,
220 size_t commentmax, ADDRESS * addr)
227 /* find the end of the route */
229 while (s && *s == '@') {
230 if (tokenlen < sizeof (token) - 1)
231 token[tokenlen++] = '@';
232 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
233 &tokenlen, sizeof (token) - 1,
234 comment, commentlen, commentmax);
236 if (!s || *s != ':') {
237 RFC822Error = ERR_BAD_ROUTE;
238 return NULL; /* invalid route */
241 if (tokenlen < sizeof (token) - 1)
242 token[tokenlen++] = ':';
247 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
248 commentlen, commentmax, addr)) == NULL)
252 RFC822Error = ERR_BAD_ROUTE_ADDR;
257 addr->mailbox = safe_strdup ("@");
263 static const char *parse_addr_spec (const char *s,
264 char *comment, size_t * commentlen,
265 size_t commentmax, ADDRESS * addr)
271 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
272 commentlen, commentmax, addr);
273 if (s && *s && *s != ',' && *s != ';') {
274 RFC822Error = ERR_BAD_ADDR_SPEC;
281 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
282 char *comment, size_t * commentlen, size_t commentmax)
284 ADDRESS *cur = rfc822_new_address ();
286 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
287 rfc822_free_address (&cur);
298 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
301 const char *begin, *ps;
302 char comment[STRING], phrase[STRING];
303 size_t phraselen = 0, commentlen = 0;
304 ADDRESS *cur, *last = NULL;
309 while (last && last->next)
312 ws_pending = isspace ((unsigned char) *s);
319 terminate_buffer (phrase, phraselen);
320 add_addrspec (&top, &last, phrase, comment, &commentlen,
321 sizeof (comment) - 1);
323 else if (commentlen && last && !last->personal) {
324 terminate_buffer (comment, commentlen);
325 last->personal = safe_strdup (comment);
334 else if (*s == '(') {
335 if (commentlen && commentlen < sizeof (comment) - 1)
336 comment[commentlen++] = ' ';
338 next_token (s, comment, &commentlen,
339 sizeof (comment) - 1)) == NULL) {
340 rfc822_free_address (&top);
345 else if (*s == ':') {
346 cur = rfc822_new_address ();
347 terminate_buffer (phrase, phraselen);
348 cur->mailbox = safe_strdup (phrase);
363 else if (*s == ';') {
365 terminate_buffer (phrase, phraselen);
366 add_addrspec (&top, &last, phrase, comment, &commentlen,
367 sizeof (comment) - 1);
369 else if (commentlen && last && !last->personal) {
370 terminate_buffer (comment, commentlen);
371 last->personal = safe_strdup (comment);
374 /* add group terminator */
375 cur = rfc822_new_address ();
387 else if (*s == '<') {
388 terminate_buffer (phrase, phraselen);
389 cur = rfc822_new_address ();
392 FREE (&cur->personal);
393 /* if we get something like "Michael R. Elkins" remove the quotes */
394 rfc822_dequote_comment (phrase);
395 cur->personal = safe_strdup (phrase);
398 parse_route_addr (s + 1, comment, &commentlen,
399 sizeof (comment) - 1, cur)) == NULL) {
400 rfc822_free_address (&top);
401 rfc822_free_address (&cur);
416 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
417 phrase[phraselen++] = ' ';
419 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
420 rfc822_free_address (&top);
425 ws_pending = isspace ((unsigned char) *s);
430 terminate_buffer (phrase, phraselen);
431 terminate_buffer (comment, commentlen);
432 add_addrspec (&top, &last, phrase, comment, &commentlen,
433 sizeof (comment) - 1);
435 else if (commentlen && last && !last->personal) {
436 terminate_buffer (comment, commentlen);
437 last->personal = safe_strdup (comment);
443 void rfc822_qualify (ADDRESS * addr, const char *host)
447 for (; addr; addr = addr->next)
448 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
449 p = safe_malloc (safe_strlen (addr->mailbox) + safe_strlen (host) + 2);
450 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
451 FREE (&addr->mailbox);
457 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
459 if (strpbrk (value, specials)) {
460 char tmp[256], *pc = tmp;
461 size_t tmplen = sizeof (tmp) - 3;
464 for (; *value && tmplen > 1; value++) {
465 if (*value == '\\' || *value == '"') {
474 strfcpy (buf, tmp, buflen);
477 strfcpy (buf, value, buflen);
480 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
490 buflen--; /* save room for the terminal nul */
492 if (addr->personal) {
493 if (strpbrk (addr->personal, RFC822Specials)) {
498 for (pc = addr->personal; *pc && buflen > 0; pc++) {
499 if (*pc == '"' || *pc == '\\') {
518 strfcpy (pbuf, addr->personal, buflen);
519 len = safe_strlen (pbuf);
530 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
540 if (ascii_strcmp (addr->mailbox, "@") && !display) {
541 strfcpy (pbuf, addr->mailbox, buflen);
542 len = safe_strlen (pbuf);
544 else if (ascii_strcmp (addr->mailbox, "@") && display) {
545 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
546 len = safe_strlen (pbuf);
555 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
580 /* no need to check for length here since we already save space at the
581 beginning of this routine */
585 /* note: it is assumed that `buf' is nul terminated! */
586 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
590 size_t len = safe_strlen (buf);
592 buflen--; /* save room for the terminal nul */
596 return; /* safety check for bogus arguments */
610 for (; addr && buflen > 0; addr = addr->next) {
611 /* use buflen+1 here because we already saved space for the trailing
612 nul char, and the subroutine can make use of it */
613 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
615 /* this should be safe since we always have at least 1 char passed into
616 the above call, which means `pbuf' should always be nul terminated */
617 len = safe_strlen (pbuf);
621 /* if there is another address, and its not a group mailbox name or
622 group terminator, add a comma to separate the addresses */
623 if (addr->next && addr->next->mailbox && !addr->group) {
638 /* this should be rfc822_cpy_adr */
639 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
641 ADDRESS *p = rfc822_new_address ();
643 p->personal = safe_strdup (addr->personal);
644 p->mailbox = safe_strdup (addr->mailbox);
645 p->group = addr->group;
649 /* this should be rfc822_cpy_adrlist */
650 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
652 ADDRESS *top = NULL, *last = NULL;
654 for (; addr; addr = addr->next) {
656 last->next = rfc822_cpy_adr_real (addr);
660 top = last = rfc822_cpy_adr_real (addr);
665 /* append list 'b' to list 'a' and return the last element in the new list */
666 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
670 while (tmp && tmp->next)
675 tmp->next = rfc822_cpy_adr (b);
677 tmp = *a = rfc822_cpy_adr (b);
678 while (tmp && tmp->next)