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 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
42 void address_wipe(address_t *addr)
44 p_delete(&addr->personal);
45 p_delete(&addr->mailbox);
46 address_delete(&addr->next);
50 void rfc822_qualify(address_t *addr, const char *host)
54 for (; addr; addr = addr->next) {
55 if (!addr->group && addr->mailbox && !strchr(addr->mailbox, '@')) {
56 p = p_new(char, m_strlen(addr->mailbox) + m_strlen(host) + 2);
57 sprintf(p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
58 p_delete(&addr->mailbox);
64 address_t *address_dup(address_t *addr)
66 address_t *res = address_new();
68 res->personal = m_strdup(addr->personal);
69 res->mailbox = m_strdup(addr->mailbox);
70 res->group = addr->group;
74 address_t *address_list_dup(address_t *addr)
76 address_t *res = NULL, **resp = &res;
78 for (; addr; addr = addr->next) {
79 *resp = address_dup(addr);
80 resp = &(*resp)->next;
87 /****************************************************************************/
88 /* Parsing functions */
89 /****************************************************************************/
91 typedef struct static_buf {
96 static inline void stbuf_append(static_buf *buf, int c) {
97 if (buf->len < ssizeof(buf->buf) - 1) {
98 buf->buf[buf->len++] = c;
99 buf->buf[buf->len] = '\0';
103 static inline void stbuf_append_sp(static_buf *buf) {
105 stbuf_append(buf, ' ');
108 static char *rfc822_dequote_comment(static_buf *buf)
110 char *res = p_new(char, buf->len + 1);
115 for (i = 0; i < buf->len; i++) {
120 if (++i >= buf->len) /* should not happen */
131 static const char *parse_comment(const char *s, static_buf *buf)
148 s++; /* if *++s is NUL it will be an error anyway */
155 stbuf_append(buf, *s);
161 static const char *parse_quote(const char *s, static_buf *buf)
166 stbuf_append(buf, *s);
175 stbuf_append(buf, *s);
183 #define is_special(x) strchr(RFC822Specials,x)
185 static const char *next_phrase(const char *s, static_buf *buf)
188 stbuf_append(buf, '"');
189 return parse_quote(s + 1, buf);
192 if (is_special(*s)) {
193 stbuf_append(buf, *s);
198 if (ISSPACE(*s) || is_special(*s))
200 stbuf_append(buf, *s++);
207 parse_mailboxdomain(const char *s, const char *nonspecial, static_buf *mbox,
213 if (!strchr(nonspecial, *s) && is_special(*s))
217 stbuf_append_sp(comment);
218 s = parse_comment(s + 1, comment);
220 s = next_phrase(s, mbox);
231 parse_address(const char *s, static_buf *comment, address_t *cur)
233 static_buf token = {"", 0};
235 s = parse_mailboxdomain(s, ".\"(\\", &token, comment);
240 stbuf_append(&token, '@');
241 s = parse_mailboxdomain(s + 1, ".([]\\", &token, comment);
246 cur->mailbox = p_dupstr(token.buf, token.len);
248 if (comment->len && !cur->personal) {
249 cur->personal = p_dupstr(comment->buf, comment->len);
255 address_t **rfc822_eotoken(address_t **last, static_buf *phrase, static_buf *comment)
259 address_t *cur = address_new();
261 s = parse_address(phrase->buf, comment, cur);
262 if (s && *s && *s != ',' && *s != ';') {
263 address_delete(&cur);
268 return &(*last)->next;
274 address_t *rfc822_parse_adrlist(address_t *top, const char *s)
276 static_buf comment = {"", 0};
277 static_buf phrase = {"", 0};
279 address_t **last = address_list_last(&top);
283 ws_pending = ISSPACE(*s);
291 stbuf_append_sp(&phrase);
292 s = next_phrase(s, &phrase);
294 address_delete(&top);
300 stbuf_append_sp(&comment);
301 s = parse_comment(s + 1, &comment);
303 address_delete(&top);
312 /* if we get something like "Michael R. Elkins" remove the quotes */
313 cur->personal = rfc822_dequote_comment(&phrase);
316 s = parse_address(skipspaces(s + 1), &comment, cur);
317 if (!s || *s != '>' || !cur->mailbox) {
318 address_delete(&top);
319 address_delete(&cur);
324 last = &(*last)->next;
328 last = rfc822_eotoken(last, &phrase, &comment);
331 case ':': /* group start */
332 *last = address_new();
333 (*last)->mailbox = p_dupstr(phrase.buf, phrase.len);
335 last = &(*last)->next;
339 last = rfc822_eotoken(last, &phrase, &comment);
340 /* add group terminator */
341 *last = address_new();
342 last = &(*last)->next;
346 last = rfc822_eotoken(last, &phrase, &comment);
350 comment.len = phrase.len = 0;
358 /****************************************************************************/
359 /* Output functions */
360 /****************************************************************************/
363 rfc822_cat(char *buf, size_t buflen, const char *value, const char *specials)
365 if (strpbrk(value, specials)) {
366 char tmp[256], *pc = tmp;
367 size_t tmplen = sizeof (tmp) - 3;
370 for (; *value && tmplen > 1; value++) {
371 if (*value == '\\' || *value == '"') {
380 m_strcpy(buf, buflen, tmp);
382 m_strcpy(buf, buflen, value);
386 void rfc822_write_address_single(char *buf, size_t buflen, address_t * addr,
396 buflen--; /* save room for the terminal nul */
398 if (addr->personal) {
399 if (strpbrk (addr->personal, RFC822Specials)) {
404 for (pc = addr->personal; *pc && buflen > 0; pc++) {
405 if (*pc == '"' || *pc == '\\') {
424 m_strcpy(pbuf, buflen, addr->personal);
425 len = m_strlen(pbuf);
436 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
446 if (ascii_strcmp (addr->mailbox, "@") && !display) {
447 m_strcpy(pbuf, buflen, addr->mailbox);
448 len = m_strlen(pbuf);
450 else if (ascii_strcmp (addr->mailbox, "@") && display) {
451 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
452 len = m_strlen(pbuf);
461 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
486 /* no need to check for length here since we already save space at the
487 beginning of this routine */
491 /* note: it is assumed that `buf' is nul terminated! */
492 void rfc822_write_address (char *buf, size_t buflen, address_t * addr,
496 size_t len = m_strlen(buf);
498 buflen--; /* save room for the terminal nul */
502 return; /* safety check for bogus arguments */
516 for (; addr && buflen > 0; addr = addr->next) {
517 /* use buflen+1 here because we already saved space for the trailing
518 nul char, and the subroutine can make use of it */
519 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
521 /* this should be safe since we always have at least 1 char passed into
522 the above call, which means `pbuf' should always be nul terminated */
523 len = m_strlen(pbuf);
527 /* if there is another address, and its not a group mailbox name or
528 group terminator, add a comma to separate the addresses */
529 if (addr->next && addr->next->mailbox && !addr->group) {