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.
20 #include "mutt_idna.h"
26 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
27 a[(c)] = 0; } while (0)
29 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
32 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
34 #define is_special(x) strchr(RFC822Specials,x)
38 /* these must defined in the same order as the numerated errors given in rfc822.h */
39 const char *RFC822Errors[] = {
41 "mismatched parenthesis",
48 void rfc822_dequote_comment (char *s)
58 else if (*s != '\"') {
67 void rfc822_free_address (ADDRESS ** p)
74 mem_free (&t->personal);
75 mem_free (&t->mailbox);
80 static const char *parse_comment (const char *s,
81 char *comment, size_t * commentlen,
95 else if (*s == '\\') {
99 if (*commentlen < commentmax)
100 comment[(*commentlen)++] = *s;
104 RFC822Error = ERR_MISMATCH_PAREN;
110 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
113 if (*tokenlen < tokenmax)
114 token[(*tokenlen)++] = '"';
116 if (*tokenlen < tokenmax)
117 token[*tokenlen] = *s;
126 if (*tokenlen < tokenmax)
127 token[*tokenlen] = *s;
132 RFC822Error = ERR_MISMATCH_QUOTE;
136 static const char *next_token (const char *s, char *token, size_t * tokenlen,
140 return (parse_comment (s + 1, token, tokenlen, tokenmax));
142 return (parse_quote (s + 1, token, tokenlen, tokenmax));
143 if (is_special (*s)) {
144 if (*tokenlen < tokenmax)
145 token[(*tokenlen)++] = *s;
149 if (ISSPACE ((unsigned char) *s) || is_special (*s))
151 if (*tokenlen < tokenmax)
152 token[(*tokenlen)++] = *s;
158 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
159 char *mailbox, size_t * mailboxlen,
160 size_t mailboxmax, char *comment,
168 if (strchr (nonspecial, *s) == NULL && is_special (*s))
172 if (*commentlen && *commentlen < commentmax)
173 comment[(*commentlen)++] = ' ';
174 ps = next_token (s, comment, commentlen, commentmax);
177 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
186 static const char *parse_address (const char *s,
187 char *token, size_t * tokenlen,
188 size_t tokenmax, char *comment,
189 size_t * commentlen, size_t commentmax,
192 s = parse_mailboxdomain (s, ".\"(\\",
193 token, tokenlen, tokenmax,
194 comment, commentlen, commentmax);
199 if (*tokenlen < tokenmax)
200 token[(*tokenlen)++] = '@';
201 s = parse_mailboxdomain (s + 1, ".([]\\",
202 token, tokenlen, tokenmax,
203 comment, commentlen, commentmax);
208 terminate_string (token, *tokenlen, tokenmax);
209 addr->mailbox = str_dup (token);
211 if (*commentlen && !addr->personal) {
212 terminate_string (comment, *commentlen, commentmax);
213 addr->personal = str_dup (comment);
219 static const char *parse_route_addr (const char *s,
220 char *comment, size_t * commentlen,
221 size_t commentmax, ADDRESS * addr)
228 /* find the end of the route */
230 while (s && *s == '@') {
231 if (tokenlen < sizeof (token) - 1)
232 token[tokenlen++] = '@';
233 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
234 &tokenlen, sizeof (token) - 1,
235 comment, commentlen, commentmax);
237 if (!s || *s != ':') {
238 RFC822Error = ERR_BAD_ROUTE;
239 return NULL; /* invalid route */
242 if (tokenlen < sizeof (token) - 1)
243 token[tokenlen++] = ':';
248 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
249 commentlen, commentmax, addr)) == NULL)
253 RFC822Error = ERR_BAD_ROUTE_ADDR;
258 addr->mailbox = str_dup ("@");
264 static const char *parse_addr_spec (const char *s,
265 char *comment, size_t * commentlen,
266 size_t commentmax, ADDRESS * addr)
272 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
273 commentlen, commentmax, addr);
274 if (s && *s && *s != ',' && *s != ';') {
275 RFC822Error = ERR_BAD_ADDR_SPEC;
282 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
283 char *comment, size_t * commentlen, size_t commentmax)
285 ADDRESS *cur = rfc822_new_address ();
287 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
288 rfc822_free_address (&cur);
299 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
302 const char *begin, *ps;
303 char comment[STRING], phrase[STRING];
304 size_t phraselen = 0, commentlen = 0;
305 ADDRESS *cur, *last = NULL;
310 while (last && last->next)
313 ws_pending = isspace ((unsigned char) *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 = str_dup (comment);
335 else if (*s == '(') {
336 if (commentlen && commentlen < sizeof (comment) - 1)
337 comment[commentlen++] = ' ';
339 next_token (s, comment, &commentlen,
340 sizeof (comment) - 1)) == NULL) {
341 rfc822_free_address (&top);
346 else if (*s == ':') {
347 cur = rfc822_new_address ();
348 terminate_buffer (phrase, phraselen);
349 cur->mailbox = str_dup (phrase);
364 else if (*s == ';') {
366 terminate_buffer (phrase, phraselen);
367 add_addrspec (&top, &last, phrase, comment, &commentlen,
368 sizeof (comment) - 1);
370 else if (commentlen && last && !last->personal) {
371 terminate_buffer (comment, commentlen);
372 last->personal = str_dup (comment);
375 /* add group terminator */
376 cur = rfc822_new_address ();
388 else if (*s == '<') {
389 terminate_buffer (phrase, phraselen);
390 cur = rfc822_new_address ();
393 mem_free (&cur->personal);
394 /* if we get something like "Michael R. Elkins" remove the quotes */
395 rfc822_dequote_comment (phrase);
396 cur->personal = str_dup (phrase);
399 parse_route_addr (s + 1, comment, &commentlen,
400 sizeof (comment) - 1, cur)) == NULL) {
401 rfc822_free_address (&top);
402 rfc822_free_address (&cur);
417 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
418 phrase[phraselen++] = ' ';
420 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
421 rfc822_free_address (&top);
426 ws_pending = isspace ((unsigned char) *s);
431 terminate_buffer (phrase, phraselen);
432 terminate_buffer (comment, commentlen);
433 add_addrspec (&top, &last, phrase, comment, &commentlen,
434 sizeof (comment) - 1);
436 else if (commentlen && last && !last->personal) {
437 terminate_buffer (comment, commentlen);
438 last->personal = str_dup (comment);
444 void rfc822_qualify (ADDRESS * addr, const char *host)
448 for (; addr; addr = addr->next)
449 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
450 p = mem_malloc (str_len (addr->mailbox) + str_len (host) + 2);
451 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
452 mem_free (&addr->mailbox);
458 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
460 if (strpbrk (value, specials)) {
461 char tmp[256], *pc = tmp;
462 size_t tmplen = sizeof (tmp) - 3;
465 for (; *value && tmplen > 1; value++) {
466 if (*value == '\\' || *value == '"') {
475 strfcpy (buf, tmp, buflen);
478 strfcpy (buf, value, buflen);
481 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
491 buflen--; /* save room for the terminal nul */
493 if (addr->personal) {
494 if (strpbrk (addr->personal, RFC822Specials)) {
499 for (pc = addr->personal; *pc && buflen > 0; pc++) {
500 if (*pc == '"' || *pc == '\\') {
519 strfcpy (pbuf, addr->personal, buflen);
520 len = str_len (pbuf);
531 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
541 if (ascii_strcmp (addr->mailbox, "@") && !display) {
542 strfcpy (pbuf, addr->mailbox, buflen);
543 len = str_len (pbuf);
545 else if (ascii_strcmp (addr->mailbox, "@") && display) {
546 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
547 len = str_len (pbuf);
556 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
581 /* no need to check for length here since we already save space at the
582 beginning of this routine */
586 /* note: it is assumed that `buf' is nul terminated! */
587 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
591 size_t len = str_len (buf);
593 buflen--; /* save room for the terminal nul */
597 return; /* safety check for bogus arguments */
611 for (; addr && buflen > 0; addr = addr->next) {
612 /* use buflen+1 here because we already saved space for the trailing
613 nul char, and the subroutine can make use of it */
614 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
616 /* this should be safe since we always have at least 1 char passed into
617 the above call, which means `pbuf' should always be nul terminated */
618 len = str_len (pbuf);
622 /* if there is another address, and its not a group mailbox name or
623 group terminator, add a comma to separate the addresses */
624 if (addr->next && addr->next->mailbox && !addr->group) {
639 /* this should be rfc822_cpy_adr */
640 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
642 ADDRESS *p = rfc822_new_address ();
644 p->personal = str_dup (addr->personal);
645 p->mailbox = str_dup (addr->mailbox);
646 p->group = addr->group;
650 /* this should be rfc822_cpy_adrlist */
651 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
653 ADDRESS *top = NULL, *last = NULL;
655 for (; addr; addr = addr->next) {
657 last->next = rfc822_cpy_adr_real (addr);
661 top = last = rfc822_cpy_adr_real (addr);
666 /* append list 'b' to list 'a' and return the last element in the new list */
667 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
671 while (tmp && tmp->next)
676 tmp->next = rfc822_cpy_adr (b);
678 tmp = *a = rfc822_cpy_adr (b);
679 while (tmp && tmp->next)