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>
22 #include "mutt_idna.h"
28 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
29 a[(c)] = 0; } while (0)
31 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
34 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
36 #define is_special(x) strchr(RFC822Specials,x)
40 /* these must defined in the same order as the numerated errors given in rfc822.h */
41 const char *RFC822Errors[] = {
43 "mismatched parenthesis",
50 void rfc822_dequote_comment (char *s)
60 else if (*s != '\"') {
69 void rfc822_free_address (ADDRESS ** p)
76 p_delete(&t->personal);
77 p_delete(&t->mailbox);
82 static const char *parse_comment (const char *s,
83 char *comment, size_t * commentlen,
97 else if (*s == '\\') {
101 if (*commentlen < commentmax)
102 comment[(*commentlen)++] = *s;
106 RFC822Error = ERR_MISMATCH_PAREN;
112 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
115 if (*tokenlen < tokenmax)
116 token[(*tokenlen)++] = '"';
118 if (*tokenlen < tokenmax)
119 token[*tokenlen] = *s;
128 if (*tokenlen < tokenmax)
129 token[*tokenlen] = *s;
134 RFC822Error = ERR_MISMATCH_QUOTE;
138 static const char *next_token (const char *s, char *token, size_t * tokenlen,
142 return (parse_comment (s + 1, token, tokenlen, tokenmax));
144 return (parse_quote (s + 1, token, tokenlen, tokenmax));
145 if (is_special (*s)) {
146 if (*tokenlen < tokenmax)
147 token[(*tokenlen)++] = *s;
151 if (ISSPACE ((unsigned char) *s) || is_special (*s))
153 if (*tokenlen < tokenmax)
154 token[(*tokenlen)++] = *s;
160 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
161 char *mailbox, size_t * mailboxlen,
162 size_t mailboxmax, char *comment,
170 if (strchr (nonspecial, *s) == NULL && is_special (*s))
174 if (*commentlen && *commentlen < commentmax)
175 comment[(*commentlen)++] = ' ';
176 ps = next_token (s, comment, commentlen, commentmax);
179 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
188 static const char *parse_address (const char *s,
189 char *token, size_t * tokenlen,
190 size_t tokenmax, char *comment,
191 size_t * commentlen, size_t commentmax,
194 s = parse_mailboxdomain (s, ".\"(\\",
195 token, tokenlen, tokenmax,
196 comment, commentlen, commentmax);
201 if (*tokenlen < tokenmax)
202 token[(*tokenlen)++] = '@';
203 s = parse_mailboxdomain (s + 1, ".([]\\",
204 token, tokenlen, tokenmax,
205 comment, commentlen, commentmax);
210 terminate_string (token, *tokenlen, tokenmax);
211 addr->mailbox = str_dup (token);
213 if (*commentlen && !addr->personal) {
214 terminate_string (comment, *commentlen, commentmax);
215 addr->personal = str_dup (comment);
221 static const char *parse_route_addr (const char *s,
222 char *comment, size_t * commentlen,
223 size_t commentmax, ADDRESS * addr)
230 /* find the end of the route */
232 while (s && *s == '@') {
233 if (tokenlen < sizeof (token) - 1)
234 token[tokenlen++] = '@';
235 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
236 &tokenlen, sizeof (token) - 1,
237 comment, commentlen, commentmax);
239 if (!s || *s != ':') {
240 RFC822Error = ERR_BAD_ROUTE;
241 return NULL; /* invalid route */
244 if (tokenlen < sizeof (token) - 1)
245 token[tokenlen++] = ':';
250 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
251 commentlen, commentmax, addr)) == NULL)
255 RFC822Error = ERR_BAD_ROUTE_ADDR;
260 addr->mailbox = str_dup ("@");
266 static const char *parse_addr_spec (const char *s,
267 char *comment, size_t * commentlen,
268 size_t commentmax, ADDRESS * addr)
274 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
275 commentlen, commentmax, addr);
276 if (s && *s && *s != ',' && *s != ';') {
277 RFC822Error = ERR_BAD_ADDR_SPEC;
284 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
285 char *comment, size_t * commentlen, size_t commentmax)
287 ADDRESS *cur = rfc822_new_address ();
289 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
290 rfc822_free_address (&cur);
301 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
304 const char *begin, *ps;
305 char comment[STRING], phrase[STRING];
306 size_t phraselen = 0, commentlen = 0;
307 ADDRESS *cur, *last = NULL;
312 while (last && last->next)
315 ws_pending = isspace ((unsigned char) *s);
322 terminate_buffer (phrase, phraselen);
323 add_addrspec (&top, &last, phrase, comment, &commentlen,
324 sizeof (comment) - 1);
326 else if (commentlen && last && !last->personal) {
327 terminate_buffer (comment, commentlen);
328 last->personal = str_dup (comment);
337 else if (*s == '(') {
338 if (commentlen && commentlen < sizeof (comment) - 1)
339 comment[commentlen++] = ' ';
341 next_token (s, comment, &commentlen,
342 sizeof (comment) - 1)) == NULL) {
343 rfc822_free_address (&top);
348 else if (*s == ':') {
349 cur = rfc822_new_address ();
350 terminate_buffer (phrase, phraselen);
351 cur->mailbox = str_dup (phrase);
366 else if (*s == ';') {
368 terminate_buffer (phrase, phraselen);
369 add_addrspec (&top, &last, phrase, comment, &commentlen,
370 sizeof (comment) - 1);
372 else if (commentlen && last && !last->personal) {
373 terminate_buffer (comment, commentlen);
374 last->personal = str_dup (comment);
377 /* add group terminator */
378 cur = rfc822_new_address ();
390 else if (*s == '<') {
391 terminate_buffer (phrase, phraselen);
392 cur = rfc822_new_address ();
395 p_delete(&cur->personal);
396 /* if we get something like "Michael R. Elkins" remove the quotes */
397 rfc822_dequote_comment (phrase);
398 cur->personal = str_dup (phrase);
401 parse_route_addr (s + 1, comment, &commentlen,
402 sizeof (comment) - 1, cur)) == NULL) {
403 rfc822_free_address (&top);
404 rfc822_free_address (&cur);
419 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
420 phrase[phraselen++] = ' ';
422 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
423 rfc822_free_address (&top);
428 ws_pending = isspace ((unsigned char) *s);
433 terminate_buffer (phrase, phraselen);
434 terminate_buffer (comment, commentlen);
435 add_addrspec (&top, &last, phrase, comment, &commentlen,
436 sizeof (comment) - 1);
438 else if (commentlen && last && !last->personal) {
439 terminate_buffer (comment, commentlen);
440 last->personal = str_dup (comment);
446 void rfc822_qualify (ADDRESS * addr, const char *host)
450 for (; addr; addr = addr->next)
451 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
452 p = p_new(char, str_len(addr->mailbox) + str_len(host) + 2);
453 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
454 p_delete(&addr->mailbox);
460 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
462 if (strpbrk (value, specials)) {
463 char tmp[256], *pc = tmp;
464 size_t tmplen = sizeof (tmp) - 3;
467 for (; *value && tmplen > 1; value++) {
468 if (*value == '\\' || *value == '"') {
477 strfcpy (buf, tmp, buflen);
480 strfcpy (buf, value, buflen);
483 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
493 buflen--; /* save room for the terminal nul */
495 if (addr->personal) {
496 if (strpbrk (addr->personal, RFC822Specials)) {
501 for (pc = addr->personal; *pc && buflen > 0; pc++) {
502 if (*pc == '"' || *pc == '\\') {
521 strfcpy (pbuf, addr->personal, buflen);
522 len = str_len (pbuf);
533 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
543 if (ascii_strcmp (addr->mailbox, "@") && !display) {
544 strfcpy (pbuf, addr->mailbox, buflen);
545 len = str_len (pbuf);
547 else if (ascii_strcmp (addr->mailbox, "@") && display) {
548 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
549 len = str_len (pbuf);
558 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
583 /* no need to check for length here since we already save space at the
584 beginning of this routine */
588 /* note: it is assumed that `buf' is nul terminated! */
589 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
593 size_t len = str_len (buf);
595 buflen--; /* save room for the terminal nul */
599 return; /* safety check for bogus arguments */
613 for (; addr && buflen > 0; addr = addr->next) {
614 /* use buflen+1 here because we already saved space for the trailing
615 nul char, and the subroutine can make use of it */
616 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
618 /* this should be safe since we always have at least 1 char passed into
619 the above call, which means `pbuf' should always be nul terminated */
620 len = str_len (pbuf);
624 /* if there is another address, and its not a group mailbox name or
625 group terminator, add a comma to separate the addresses */
626 if (addr->next && addr->next->mailbox && !addr->group) {
641 /* this should be rfc822_cpy_adr */
642 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
644 ADDRESS *p = rfc822_new_address ();
646 p->personal = str_dup (addr->personal);
647 p->mailbox = str_dup (addr->mailbox);
648 p->group = addr->group;
652 /* this should be rfc822_cpy_adrlist */
653 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
655 ADDRESS *top = NULL, *last = NULL;
657 for (; addr; addr = addr->next) {
659 last->next = rfc822_cpy_adr_real (addr);
663 top = last = rfc822_cpy_adr_real (addr);
668 /* append list 'b' to list 'a' and return the last element in the new list */
669 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
673 while (tmp && tmp->next)
678 tmp->next = rfc822_cpy_adr (b);
680 tmp = *a = rfc822_cpy_adr (b);
681 while (tmp && tmp->next)