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.
18 #include <lib-lib/mem.h>
19 #include <lib-lib/str.h>
20 #include <lib-lib/ascii.h>
21 #include <lib-lib/macros.h>
24 #include "mutt_idna.h"
27 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
28 a[(c)] = 0; } while (0)
30 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
33 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
35 #define is_special(x) strchr(RFC822Specials,x)
39 /* these must defined in the same order as the numerated errors given in rfc822.h */
40 const char *RFC822Errors[] = {
42 "mismatched parenthesis",
49 void rfc822_dequote_comment (char *s)
59 else if (*s != '\"') {
68 void rfc822_free_address (ADDRESS ** p)
75 p_delete(&t->personal);
76 p_delete(&t->mailbox);
81 static const char *parse_comment (const char *s,
82 char *comment, size_t * commentlen,
96 else if (*s == '\\') {
100 if (*commentlen < commentmax)
101 comment[(*commentlen)++] = *s;
105 RFC822Error = ERR_MISMATCH_PAREN;
111 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
114 if (*tokenlen < tokenmax)
115 token[(*tokenlen)++] = '"';
117 if (*tokenlen < tokenmax)
118 token[*tokenlen] = *s;
127 if (*tokenlen < tokenmax)
128 token[*tokenlen] = *s;
133 RFC822Error = ERR_MISMATCH_QUOTE;
137 static const char *next_token (const char *s, char *token, size_t * tokenlen,
141 return (parse_comment (s + 1, token, tokenlen, tokenmax));
143 return (parse_quote (s + 1, token, tokenlen, tokenmax));
144 if (is_special (*s)) {
145 if (*tokenlen < tokenmax)
146 token[(*tokenlen)++] = *s;
150 if (ISSPACE ((unsigned char) *s) || is_special (*s))
152 if (*tokenlen < tokenmax)
153 token[(*tokenlen)++] = *s;
159 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
160 char *mailbox, size_t * mailboxlen,
161 size_t mailboxmax, char *comment,
169 if (strchr (nonspecial, *s) == NULL && is_special (*s))
173 if (*commentlen && *commentlen < commentmax)
174 comment[(*commentlen)++] = ' ';
175 ps = next_token (s, comment, commentlen, commentmax);
178 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
187 static const char *parse_address (const char *s,
188 char *token, size_t * tokenlen,
189 size_t tokenmax, char *comment,
190 size_t * commentlen, size_t commentmax,
193 s = parse_mailboxdomain (s, ".\"(\\",
194 token, tokenlen, tokenmax,
195 comment, commentlen, commentmax);
200 if (*tokenlen < tokenmax)
201 token[(*tokenlen)++] = '@';
202 s = parse_mailboxdomain (s + 1, ".([]\\",
203 token, tokenlen, tokenmax,
204 comment, commentlen, commentmax);
209 terminate_string (token, *tokenlen, tokenmax);
210 addr->mailbox = m_strdup(token);
212 if (*commentlen && !addr->personal) {
213 terminate_string (comment, *commentlen, commentmax);
214 addr->personal = m_strdup(comment);
220 static const char *parse_route_addr (const char *s,
221 char *comment, size_t * commentlen,
222 size_t commentmax, ADDRESS * addr)
229 /* find the end of the route */
231 while (s && *s == '@') {
232 if (tokenlen < sizeof (token) - 1)
233 token[tokenlen++] = '@';
234 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
235 &tokenlen, sizeof (token) - 1,
236 comment, commentlen, commentmax);
238 if (!s || *s != ':') {
239 RFC822Error = ERR_BAD_ROUTE;
240 return NULL; /* invalid route */
243 if (tokenlen < sizeof (token) - 1)
244 token[tokenlen++] = ':';
249 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
250 commentlen, commentmax, addr)) == NULL)
254 RFC822Error = ERR_BAD_ROUTE_ADDR;
259 addr->mailbox = m_strdup("@");
265 static const char *parse_addr_spec (const char *s,
266 char *comment, size_t * commentlen,
267 size_t commentmax, ADDRESS * addr)
273 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
274 commentlen, commentmax, addr);
275 if (s && *s && *s != ',' && *s != ';') {
276 RFC822Error = ERR_BAD_ADDR_SPEC;
283 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
284 char *comment, size_t * commentlen, size_t commentmax)
286 ADDRESS *cur = rfc822_new_address ();
288 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
289 rfc822_free_address (&cur);
300 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
303 const char *begin, *ps;
304 char comment[STRING], phrase[STRING];
305 size_t phraselen = 0, commentlen = 0;
306 ADDRESS *cur, *last = NULL;
311 while (last && last->next)
314 ws_pending = isspace ((unsigned char) *s);
316 begin = s = vskipspaces(s);
320 terminate_buffer (phrase, phraselen);
321 add_addrspec (&top, &last, phrase, comment, &commentlen,
322 sizeof (comment) - 1);
324 else if (commentlen && last && !last->personal) {
325 terminate_buffer (comment, commentlen);
326 last->personal = m_strdup(comment);
332 begin = vskipspaces(s);
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 = m_strdup(phrase);
360 begin = vskipspaces(s);
362 else if (*s == ';') {
364 terminate_buffer (phrase, phraselen);
365 add_addrspec (&top, &last, phrase, comment, &commentlen,
366 sizeof (comment) - 1);
368 else if (commentlen && last && !last->personal) {
369 terminate_buffer (comment, commentlen);
370 last->personal = m_strdup(comment);
373 /* add group terminator */
374 cur = rfc822_new_address ();
383 begin = vskipspaces(s);
385 else if (*s == '<') {
386 terminate_buffer (phrase, phraselen);
387 cur = rfc822_new_address ();
390 p_delete(&cur->personal);
391 /* if we get something like "Michael R. Elkins" remove the quotes */
392 rfc822_dequote_comment (phrase);
393 cur->personal = m_strdup(phrase);
396 parse_route_addr (s + 1, comment, &commentlen,
397 sizeof (comment) - 1, cur)) == NULL) {
398 rfc822_free_address (&top);
399 rfc822_free_address (&cur);
414 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
415 phrase[phraselen++] = ' ';
417 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
418 rfc822_free_address (&top);
423 ws_pending = isspace ((unsigned char) *s);
428 terminate_buffer (phrase, phraselen);
429 terminate_buffer (comment, commentlen);
430 add_addrspec (&top, &last, phrase, comment, &commentlen,
431 sizeof (comment) - 1);
433 else if (commentlen && last && !last->personal) {
434 terminate_buffer (comment, commentlen);
435 last->personal = m_strdup(comment);
441 void rfc822_qualify (ADDRESS * addr, const char *host)
445 for (; addr; addr = addr->next)
446 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
447 p = p_new(char, m_strlen(addr->mailbox) + m_strlen(host) + 2);
448 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
449 p_delete(&addr->mailbox);
455 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
457 if (strpbrk (value, specials)) {
458 char tmp[256], *pc = tmp;
459 size_t tmplen = sizeof (tmp) - 3;
462 for (; *value && tmplen > 1; value++) {
463 if (*value == '\\' || *value == '"') {
472 m_strcpy(buf, buflen, tmp);
475 m_strcpy(buf, buflen, value);
478 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
488 buflen--; /* save room for the terminal nul */
490 if (addr->personal) {
491 if (strpbrk (addr->personal, RFC822Specials)) {
496 for (pc = addr->personal; *pc && buflen > 0; pc++) {
497 if (*pc == '"' || *pc == '\\') {
516 m_strcpy(pbuf, buflen, addr->personal);
517 len = m_strlen(pbuf);
528 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
538 if (ascii_strcmp (addr->mailbox, "@") && !display) {
539 m_strcpy(pbuf, buflen, addr->mailbox);
540 len = m_strlen(pbuf);
542 else if (ascii_strcmp (addr->mailbox, "@") && display) {
543 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
544 len = m_strlen(pbuf);
553 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
578 /* no need to check for length here since we already save space at the
579 beginning of this routine */
583 /* note: it is assumed that `buf' is nul terminated! */
584 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
588 size_t len = m_strlen(buf);
590 buflen--; /* save room for the terminal nul */
594 return; /* safety check for bogus arguments */
608 for (; addr && buflen > 0; addr = addr->next) {
609 /* use buflen+1 here because we already saved space for the trailing
610 nul char, and the subroutine can make use of it */
611 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
613 /* this should be safe since we always have at least 1 char passed into
614 the above call, which means `pbuf' should always be nul terminated */
615 len = m_strlen(pbuf);
619 /* if there is another address, and its not a group mailbox name or
620 group terminator, add a comma to separate the addresses */
621 if (addr->next && addr->next->mailbox && !addr->group) {
636 /* this should be rfc822_cpy_adr */
637 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
639 ADDRESS *p = rfc822_new_address ();
641 p->personal = m_strdup(addr->personal);
642 p->mailbox = m_strdup(addr->mailbox);
643 p->group = addr->group;
647 /* this should be rfc822_cpy_adrlist */
648 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
650 ADDRESS *top = NULL, *last = NULL;
652 for (; addr; addr = addr->next) {
654 last->next = rfc822_cpy_adr_real (addr);
658 top = last = rfc822_cpy_adr_real (addr);
663 /* append list 'b' to list 'a' and return the last element in the new list */
664 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
668 while (tmp && tmp->next)
673 tmp->next = rfc822_cpy_adr (b);
675 tmp = *a = rfc822_cpy_adr (b);
676 while (tmp && tmp->next)