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 p_delete(&addr->personal);
43 p_delete(&addr->mailbox);
44 address_delete(&addr->next);
48 void rfc822_qualify(address_t *addr, const char *host)
52 for (; addr; addr = addr->next) {
53 if (!addr->group && addr->mailbox && strchr(addr->mailbox, '@') == NULL) {
54 p = p_new(char, m_strlen(addr->mailbox) + m_strlen(host) + 2);
55 sprintf(p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
56 p_delete(&addr->mailbox);
62 address_t *address_dup(address_t *addr)
64 address_t *res = address_new();
66 res->personal = m_strdup(addr->personal);
67 res->mailbox = m_strdup(addr->mailbox);
68 res->group = addr->group;
72 address_t *address_list_dup(address_t *addr)
74 address_t *res = NULL, **resp = &res;
76 for (; addr; addr = addr->next) {
77 *resp = address_dup(addr);
78 resp = &(*resp)->next;
84 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
85 a[(c)] = 0; } while (0)
87 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
90 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
92 #define is_special(x) strchr(RFC822Specials,x)
96 /* these must defined in the same order as the numerated errors given in rfc822.h */
97 const char *RFC822Errors[] = {
99 "mismatched parenthesis",
106 void rfc822_dequote_comment (char *s)
116 else if (*s != '\"') {
125 static const char *parse_comment (const char *s,
126 char *comment, size_t * commentlen,
131 while (*s && level) {
134 else if (*s == ')') {
140 else if (*s == '\\') {
144 if (*commentlen < commentmax)
145 comment[(*commentlen)++] = *s;
149 RFC822Error = ERR_MISMATCH_PAREN;
155 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
158 if (*tokenlen < tokenmax)
159 token[(*tokenlen)++] = '"';
161 if (*tokenlen < tokenmax)
162 token[*tokenlen] = *s;
171 if (*tokenlen < tokenmax)
172 token[*tokenlen] = *s;
177 RFC822Error = ERR_MISMATCH_QUOTE;
181 static const char *next_token (const char *s, char *token, size_t * tokenlen,
185 return (parse_comment (s + 1, token, tokenlen, tokenmax));
187 return (parse_quote (s + 1, token, tokenlen, tokenmax));
188 if (is_special (*s)) {
189 if (*tokenlen < tokenmax)
190 token[(*tokenlen)++] = *s;
194 if (ISSPACE ((unsigned char) *s) || is_special (*s))
196 if (*tokenlen < tokenmax)
197 token[(*tokenlen)++] = *s;
203 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
204 char *mailbox, size_t * mailboxlen,
205 size_t mailboxmax, char *comment,
213 if (strchr (nonspecial, *s) == NULL && is_special (*s))
217 if (*commentlen && *commentlen < commentmax)
218 comment[(*commentlen)++] = ' ';
219 ps = next_token (s, comment, commentlen, commentmax);
222 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
231 static const char *parse_address (const char *s,
232 char *token, size_t * tokenlen,
233 size_t tokenmax, char *comment,
234 size_t * commentlen, size_t commentmax,
237 s = parse_mailboxdomain (s, ".\"(\\",
238 token, tokenlen, tokenmax,
239 comment, commentlen, commentmax);
244 if (*tokenlen < tokenmax)
245 token[(*tokenlen)++] = '@';
246 s = parse_mailboxdomain (s + 1, ".([]\\",
247 token, tokenlen, tokenmax,
248 comment, commentlen, commentmax);
253 terminate_string (token, *tokenlen, tokenmax);
254 addr->mailbox = m_strdup(token);
256 if (*commentlen && !addr->personal) {
257 terminate_string (comment, *commentlen, commentmax);
258 addr->personal = m_strdup(comment);
264 static const char *parse_route_addr (const char *s,
265 char *comment, size_t * commentlen,
266 size_t commentmax, address_t * addr)
273 /* find the end of the route */
275 while (s && *s == '@') {
276 if (tokenlen < sizeof (token) - 1)
277 token[tokenlen++] = '@';
278 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
279 &tokenlen, sizeof (token) - 1,
280 comment, commentlen, commentmax);
282 if (!s || *s != ':') {
283 RFC822Error = ERR_BAD_ROUTE;
284 return NULL; /* invalid route */
287 if (tokenlen < sizeof (token) - 1)
288 token[tokenlen++] = ':';
293 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
294 commentlen, commentmax, addr)) == NULL)
298 RFC822Error = ERR_BAD_ROUTE_ADDR;
303 addr->mailbox = m_strdup("@");
309 static const char *parse_addr_spec (const char *s,
310 char *comment, size_t * commentlen,
311 size_t commentmax, address_t * addr)
317 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
318 commentlen, commentmax, addr);
319 if (s && *s && *s != ',' && *s != ';') {
320 RFC822Error = ERR_BAD_ADDR_SPEC;
327 add_addrspec (address_t ** top, address_t ** last, const char *phrase,
328 char *comment, size_t * commentlen, size_t commentmax)
330 address_t *cur = address_new ();
332 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
333 address_delete (&cur);
344 address_t *rfc822_parse_adrlist (address_t * top, const char *s)
347 const char *begin, *ps;
348 char comment[STRING], phrase[STRING];
349 size_t phraselen = 0, commentlen = 0;
350 address_t *cur, *last = NULL;
355 while (last && last->next)
358 ws_pending = isspace ((unsigned char) *s);
360 begin = s = vskipspaces(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);
376 begin = vskipspaces(s);
378 else if (*s == '(') {
379 if (commentlen && commentlen < sizeof (comment) - 1)
380 comment[commentlen++] = ' ';
382 next_token (s, comment, &commentlen,
383 sizeof (comment) - 1)) == NULL) {
384 address_delete (&top);
389 else if (*s == ':') {
390 cur = address_new ();
391 terminate_buffer (phrase, phraselen);
392 cur->mailbox = m_strdup(phrase);
404 begin = vskipspaces(s);
406 else if (*s == ';') {
408 terminate_buffer (phrase, phraselen);
409 add_addrspec (&top, &last, phrase, comment, &commentlen,
410 sizeof (comment) - 1);
412 else if (commentlen && last && !last->personal) {
413 terminate_buffer (comment, commentlen);
414 last->personal = m_strdup(comment);
417 /* add group terminator */
418 cur = address_new ();
427 begin = vskipspaces(s);
429 else if (*s == '<') {
430 terminate_buffer (phrase, phraselen);
431 cur = address_new ();
434 p_delete(&cur->personal);
435 /* if we get something like "Michael R. Elkins" remove the quotes */
436 rfc822_dequote_comment (phrase);
437 cur->personal = m_strdup(phrase);
440 parse_route_addr (s + 1, comment, &commentlen,
441 sizeof (comment) - 1, cur)) == NULL) {
442 address_delete (&top);
443 address_delete (&cur);
458 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
459 phrase[phraselen++] = ' ';
461 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
462 address_delete (&top);
467 ws_pending = isspace ((unsigned char) *s);
472 terminate_buffer (phrase, phraselen);
473 terminate_buffer (comment, commentlen);
474 add_addrspec (&top, &last, phrase, comment, &commentlen,
475 sizeof (comment) - 1);
477 else if (commentlen && last && !last->personal) {
478 terminate_buffer (comment, commentlen);
479 last->personal = m_strdup(comment);
486 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
488 if (strpbrk (value, specials)) {
489 char tmp[256], *pc = tmp;
490 size_t tmplen = sizeof (tmp) - 3;
493 for (; *value && tmplen > 1; value++) {
494 if (*value == '\\' || *value == '"') {
503 m_strcpy(buf, buflen, tmp);
506 m_strcpy(buf, buflen, value);
509 void rfc822_write_address_single (char *buf, size_t buflen, address_t * addr,
519 buflen--; /* save room for the terminal nul */
521 if (addr->personal) {
522 if (strpbrk (addr->personal, RFC822Specials)) {
527 for (pc = addr->personal; *pc && buflen > 0; pc++) {
528 if (*pc == '"' || *pc == '\\') {
547 m_strcpy(pbuf, buflen, addr->personal);
548 len = m_strlen(pbuf);
559 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
569 if (ascii_strcmp (addr->mailbox, "@") && !display) {
570 m_strcpy(pbuf, buflen, addr->mailbox);
571 len = m_strlen(pbuf);
573 else if (ascii_strcmp (addr->mailbox, "@") && display) {
574 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
575 len = m_strlen(pbuf);
584 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
609 /* no need to check for length here since we already save space at the
610 beginning of this routine */
614 /* note: it is assumed that `buf' is nul terminated! */
615 void rfc822_write_address (char *buf, size_t buflen, address_t * addr,
619 size_t len = m_strlen(buf);
621 buflen--; /* save room for the terminal nul */
625 return; /* safety check for bogus arguments */
639 for (; addr && buflen > 0; addr = addr->next) {
640 /* use buflen+1 here because we already saved space for the trailing
641 nul char, and the subroutine can make use of it */
642 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
644 /* this should be safe since we always have at least 1 char passed into
645 the above call, which means `pbuf' should always be nul terminated */
646 len = m_strlen(pbuf);
650 /* if there is another address, and its not a group mailbox name or
651 group terminator, add a comma to separate the addresses */
652 if (addr->next && addr->next->mailbox && !addr->group) {