2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * Copyright © 2006 Pierre Habouzit
21 * Copyright notice from original mutt:
22 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
24 * This file is part of mutt-ng, see http://www.muttng.org/.
25 * It's licensed under the GNU General Public License,
26 * please see the file GPL in the top level source directory.
33 #include <lib-lib/mem.h>
34 #include <lib-lib/str.h>
35 #include <lib-lib/ascii.h>
36 #include <lib-lib/macros.h>
38 #include "mutt_idna.h"
40 void address_wipe(address_t *addr)
42 address_delete(&addr->next);
43 p_delete(&addr->personal);
44 p_delete(&addr->mailbox);
48 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
49 a[(c)] = 0; } while (0)
51 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
54 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
56 #define is_special(x) strchr(RFC822Specials,x)
60 /* these must defined in the same order as the numerated errors given in rfc822.h */
61 const char *RFC822Errors[] = {
63 "mismatched parenthesis",
70 void rfc822_dequote_comment (char *s)
80 else if (*s != '\"') {
89 static const char *parse_comment (const char *s,
90 char *comment, size_t * commentlen,
104 else if (*s == '\\') {
108 if (*commentlen < commentmax)
109 comment[(*commentlen)++] = *s;
113 RFC822Error = ERR_MISMATCH_PAREN;
119 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
122 if (*tokenlen < tokenmax)
123 token[(*tokenlen)++] = '"';
125 if (*tokenlen < tokenmax)
126 token[*tokenlen] = *s;
135 if (*tokenlen < tokenmax)
136 token[*tokenlen] = *s;
141 RFC822Error = ERR_MISMATCH_QUOTE;
145 static const char *next_token (const char *s, char *token, size_t * tokenlen,
149 return (parse_comment (s + 1, token, tokenlen, tokenmax));
151 return (parse_quote (s + 1, token, tokenlen, tokenmax));
152 if (is_special (*s)) {
153 if (*tokenlen < tokenmax)
154 token[(*tokenlen)++] = *s;
158 if (ISSPACE ((unsigned char) *s) || is_special (*s))
160 if (*tokenlen < tokenmax)
161 token[(*tokenlen)++] = *s;
167 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
168 char *mailbox, size_t * mailboxlen,
169 size_t mailboxmax, char *comment,
177 if (strchr (nonspecial, *s) == NULL && is_special (*s))
181 if (*commentlen && *commentlen < commentmax)
182 comment[(*commentlen)++] = ' ';
183 ps = next_token (s, comment, commentlen, commentmax);
186 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
195 static const char *parse_address (const char *s,
196 char *token, size_t * tokenlen,
197 size_t tokenmax, char *comment,
198 size_t * commentlen, size_t commentmax,
201 s = parse_mailboxdomain (s, ".\"(\\",
202 token, tokenlen, tokenmax,
203 comment, commentlen, commentmax);
208 if (*tokenlen < tokenmax)
209 token[(*tokenlen)++] = '@';
210 s = parse_mailboxdomain (s + 1, ".([]\\",
211 token, tokenlen, tokenmax,
212 comment, commentlen, commentmax);
217 terminate_string (token, *tokenlen, tokenmax);
218 addr->mailbox = m_strdup(token);
220 if (*commentlen && !addr->personal) {
221 terminate_string (comment, *commentlen, commentmax);
222 addr->personal = m_strdup(comment);
228 static const char *parse_route_addr (const char *s,
229 char *comment, size_t * commentlen,
230 size_t commentmax, address_t * addr)
237 /* find the end of the route */
239 while (s && *s == '@') {
240 if (tokenlen < sizeof (token) - 1)
241 token[tokenlen++] = '@';
242 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
243 &tokenlen, sizeof (token) - 1,
244 comment, commentlen, commentmax);
246 if (!s || *s != ':') {
247 RFC822Error = ERR_BAD_ROUTE;
248 return NULL; /* invalid route */
251 if (tokenlen < sizeof (token) - 1)
252 token[tokenlen++] = ':';
257 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
258 commentlen, commentmax, addr)) == NULL)
262 RFC822Error = ERR_BAD_ROUTE_ADDR;
267 addr->mailbox = m_strdup("@");
273 static const char *parse_addr_spec (const char *s,
274 char *comment, size_t * commentlen,
275 size_t commentmax, address_t * addr)
281 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
282 commentlen, commentmax, addr);
283 if (s && *s && *s != ',' && *s != ';') {
284 RFC822Error = ERR_BAD_ADDR_SPEC;
291 add_addrspec (address_t ** top, address_t ** last, const char *phrase,
292 char *comment, size_t * commentlen, size_t commentmax)
294 address_t *cur = address_new ();
296 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
297 address_delete (&cur);
308 address_t *rfc822_parse_adrlist (address_t * top, const char *s)
311 const char *begin, *ps;
312 char comment[STRING], phrase[STRING];
313 size_t phraselen = 0, commentlen = 0;
314 address_t *cur, *last = NULL;
319 while (last && last->next)
322 ws_pending = isspace ((unsigned char) *s);
324 begin = s = vskipspaces(s);
328 terminate_buffer (phrase, phraselen);
329 add_addrspec (&top, &last, phrase, comment, &commentlen,
330 sizeof (comment) - 1);
332 else if (commentlen && last && !last->personal) {
333 terminate_buffer (comment, commentlen);
334 last->personal = m_strdup(comment);
340 begin = vskipspaces(s);
342 else if (*s == '(') {
343 if (commentlen && commentlen < sizeof (comment) - 1)
344 comment[commentlen++] = ' ';
346 next_token (s, comment, &commentlen,
347 sizeof (comment) - 1)) == NULL) {
348 address_delete (&top);
353 else if (*s == ':') {
354 cur = address_new ();
355 terminate_buffer (phrase, phraselen);
356 cur->mailbox = m_strdup(phrase);
368 begin = vskipspaces(s);
370 else if (*s == ';') {
372 terminate_buffer (phrase, phraselen);
373 add_addrspec (&top, &last, phrase, comment, &commentlen,
374 sizeof (comment) - 1);
376 else if (commentlen && last && !last->personal) {
377 terminate_buffer (comment, commentlen);
378 last->personal = m_strdup(comment);
381 /* add group terminator */
382 cur = address_new ();
391 begin = vskipspaces(s);
393 else if (*s == '<') {
394 terminate_buffer (phrase, phraselen);
395 cur = address_new ();
398 p_delete(&cur->personal);
399 /* if we get something like "Michael R. Elkins" remove the quotes */
400 rfc822_dequote_comment (phrase);
401 cur->personal = m_strdup(phrase);
404 parse_route_addr (s + 1, comment, &commentlen,
405 sizeof (comment) - 1, cur)) == NULL) {
406 address_delete (&top);
407 address_delete (&cur);
422 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
423 phrase[phraselen++] = ' ';
425 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
426 address_delete (&top);
431 ws_pending = isspace ((unsigned char) *s);
436 terminate_buffer (phrase, phraselen);
437 terminate_buffer (comment, commentlen);
438 add_addrspec (&top, &last, phrase, comment, &commentlen,
439 sizeof (comment) - 1);
441 else if (commentlen && last && !last->personal) {
442 terminate_buffer (comment, commentlen);
443 last->personal = m_strdup(comment);
449 void rfc822_qualify (address_t * addr, const char *host)
453 for (; addr; addr = addr->next)
454 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
455 p = p_new(char, m_strlen(addr->mailbox) + m_strlen(host) + 2);
456 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
457 p_delete(&addr->mailbox);
463 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
465 if (strpbrk (value, specials)) {
466 char tmp[256], *pc = tmp;
467 size_t tmplen = sizeof (tmp) - 3;
470 for (; *value && tmplen > 1; value++) {
471 if (*value == '\\' || *value == '"') {
480 m_strcpy(buf, buflen, tmp);
483 m_strcpy(buf, buflen, value);
486 void rfc822_write_address_single (char *buf, size_t buflen, address_t * addr,
496 buflen--; /* save room for the terminal nul */
498 if (addr->personal) {
499 if (strpbrk (addr->personal, RFC822Specials)) {
504 for (pc = addr->personal; *pc && buflen > 0; pc++) {
505 if (*pc == '"' || *pc == '\\') {
524 m_strcpy(pbuf, buflen, addr->personal);
525 len = m_strlen(pbuf);
536 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
546 if (ascii_strcmp (addr->mailbox, "@") && !display) {
547 m_strcpy(pbuf, buflen, addr->mailbox);
548 len = m_strlen(pbuf);
550 else if (ascii_strcmp (addr->mailbox, "@") && display) {
551 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
552 len = m_strlen(pbuf);
561 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
586 /* no need to check for length here since we already save space at the
587 beginning of this routine */
591 /* note: it is assumed that `buf' is nul terminated! */
592 void rfc822_write_address (char *buf, size_t buflen, address_t * addr,
596 size_t len = m_strlen(buf);
598 buflen--; /* save room for the terminal nul */
602 return; /* safety check for bogus arguments */
616 for (; addr && buflen > 0; addr = addr->next) {
617 /* use buflen+1 here because we already saved space for the trailing
618 nul char, and the subroutine can make use of it */
619 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
621 /* this should be safe since we always have at least 1 char passed into
622 the above call, which means `pbuf' should always be nul terminated */
623 len = m_strlen(pbuf);
627 /* if there is another address, and its not a group mailbox name or
628 group terminator, add a comma to separate the addresses */
629 if (addr->next && addr->next->mailbox && !addr->group) {
644 /* this should be rfc822_cpy_adr */
645 address_t *rfc822_cpy_adr_real (address_t * addr)
647 address_t *p = address_new ();
649 p->personal = m_strdup(addr->personal);
650 p->mailbox = m_strdup(addr->mailbox);
651 p->group = addr->group;
655 /* this should be rfc822_cpy_adrlist */
656 address_t *rfc822_cpy_adr (address_t * addr)
658 address_t *top = NULL, *last = NULL;
660 for (; addr; addr = addr->next) {
662 last->next = rfc822_cpy_adr_real (addr);
666 top = last = rfc822_cpy_adr_real (addr);
671 /* append list 'b' to list 'a' and return the last element in the new list */
672 address_t *rfc822_append (address_t ** a, address_t * b)
676 while (tmp && tmp->next)
681 tmp->next = rfc822_cpy_adr (b);
683 tmp = *a = rfc822_cpy_adr (b);
684 while (tmp && tmp->next)