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/lib-lib.h>
35 #include "mutt_idna.h"
37 void rfc822_qualify(address_t *addr, const char *host)
41 for (; addr; addr = addr->next) {
42 if (!addr->group && addr->mailbox && !strchr(addr->mailbox, '@')) {
43 p = p_new(char, m_strlen(addr->mailbox) + m_strlen(host) + 2);
44 sprintf(p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
45 p_delete(&addr->mailbox);
51 address_t *address_dup(const address_t *addr)
53 address_t *res = address_new();
55 res->personal = m_strdup(addr->personal);
56 res->mailbox = m_strdup(addr->mailbox);
57 res->group = addr->group;
61 address_t *address_list_dup(const address_t *addr)
63 address_t *res = NULL, **resp = &res;
65 for (; addr; addr = addr->next) {
66 *resp = address_dup(addr);
67 resp = &(*resp)->next;
73 /* given a list of addresses, return a list of unique addresses */
74 void address_list_uniq(address_t *a)
76 for (; a; a = a->next) {
77 address_t **b = &a->next;
83 if ((*b)->mailbox && !ascii_strcasecmp((*b)->mailbox, a->mailbox))
85 address_t *pop = address_list_pop(b);
94 /****************************************************************************/
95 /* Parsing functions */
96 /****************************************************************************/
98 typedef struct static_buf {
103 static inline void stbuf_append(static_buf *buf, int c) {
104 if (buf->len < ssizeof(buf->buf) - 1) {
105 buf->buf[buf->len++] = c;
106 buf->buf[buf->len] = '\0';
110 static inline void stbuf_append_sp(static_buf *buf) {
112 stbuf_append(buf, ' ');
115 static char *rfc822_dequote_comment(static_buf *buf)
117 char *res = p_new(char, buf->len + 1);
122 for (i = 0; i < buf->len; i++) {
127 if (++i >= buf->len) /* should not happen */
138 static const char *parse_comment(const char *s, static_buf *buf)
155 s++; /* if *++s is NUL it will be an error anyway */
162 stbuf_append(buf, *s);
168 static const char *parse_quote(const char *s, static_buf *buf)
173 stbuf_append(buf, *s);
182 stbuf_append(buf, *s);
190 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
192 static const char *next_phrase(const char *s, static_buf *buf)
195 stbuf_append(buf, '"');
196 return parse_quote(s + 1, buf);
199 if (strchr(RFC822Specials, *s)) {
200 stbuf_append(buf, *s);
205 if (ISSPACE(*s) || strchr(RFC822Specials, *s))
207 stbuf_append(buf, *s++);
214 parse_mailboxdomain(const char *s, const char *nonspecial, static_buf *mbox,
220 if (!strchr(nonspecial, *s) && strchr(RFC822Specials, *s))
224 stbuf_append_sp(comment);
225 s = parse_comment(s + 1, comment);
227 s = next_phrase(s, mbox);
238 parse_address(const char *s, static_buf *comment, address_t *cur)
240 static_buf token = {"", 0};
242 s = parse_mailboxdomain(s, ".\"(\\", &token, comment);
247 stbuf_append(&token, '@');
248 s = parse_mailboxdomain(s + 1, ".([]\\", &token, comment);
253 cur->mailbox = p_dupstr(token.buf, token.len);
255 if (comment->len && !cur->personal) {
256 cur->personal = p_dupstr(comment->buf, comment->len);
262 static address_t **rfc822_eotoken(address_t **last, static_buf *phrase, static_buf *comment)
266 address_t *cur = address_new();
268 s = parse_address(phrase->buf, comment, cur);
269 if (s && *s && *s != ',' && *s != ';') {
270 address_list_wipe(&cur);
275 return &(*last)->next;
281 address_t *rfc822_parse_adrlist(address_t *top, const char *s)
283 static_buf comment = {"", 0};
284 static_buf phrase = {"", 0};
286 address_t **last = address_list_last(&top);
290 ws_pending = ISSPACE(*s);
298 stbuf_append_sp(&phrase);
299 s = next_phrase(s, &phrase);
301 address_list_wipe(&top);
307 stbuf_append_sp(&comment);
308 s = parse_comment(s + 1, &comment);
310 address_list_wipe(&top);
319 /* if we get something like "Michael R. Elkins" remove the quotes */
320 cur->personal = rfc822_dequote_comment(&phrase);
323 s = parse_address(skipspaces(s + 1), &comment, cur);
324 if (!s || *s != '>' || !cur->mailbox) {
325 address_list_wipe(&top);
326 address_list_wipe(&cur);
331 last = &(*last)->next;
335 last = rfc822_eotoken(last, &phrase, &comment);
338 case ':': /* group start */
339 *last = address_new();
340 (*last)->mailbox = p_dupstr(phrase.buf, phrase.len);
342 last = &(*last)->next;
346 last = rfc822_eotoken(last, &phrase, &comment);
347 /* add group terminator */
348 *last = address_new();
349 last = &(*last)->next;
353 last = rfc822_eotoken(last, &phrase, &comment);
357 comment.len = phrase.len = 0;
365 /****************************************************************************/
366 /* Output functions */
367 /****************************************************************************/
370 rfc822_strcpy(char *buf, ssize_t buflen, const char *p, const char *specials)
372 if (strpbrk(p, specials)) {
377 while (*p && pos < buflen - 2) {
378 if (*p == '\\' || *p == '"') {
379 if (pos >= buflen - 4)
391 return m_strcpy(buf, buflen, p);
395 ssize_t rfc822_write_address_single(char *buf, ssize_t buflen,
396 address_t *addr, int display)
403 buflen--; /* save room for the terminal nul */
405 if (addr->personal) {
406 pos = rfc822_strcpy(buf, buflen, addr->personal, RFC822Specials);
407 if (pos + 2 >= buflen)
416 pos += m_strcpy(buf + pos, buflen - pos, addr->mailbox);
418 pos += m_strcpy(buf + pos, buflen - pos, mutt_addr_for_display(addr));
421 if (addr->personal) {
422 if (pos + 1 >= buflen)
428 if (pos + 1 >= buflen)
433 if (pos + 1 >= buflen)
439 /* no need to check for length here since we already save space at the
440 beginning of this routine */
445 /* note: it is assumed that `buf' is nul terminated! */
447 rfc822_write_address(char *buf, ssize_t buflen, address_t *addr, int display)
451 buflen--; /* save room for the terminal nul */
452 pos = m_strnlen(buf, buflen);
455 if (pos + 2 >= buflen)
462 for (; addr; addr = addr->next) {
463 pos += rfc822_write_address_single(buf + pos, buflen + 1 - pos,
466 if (!addr->group && addr->next && addr->next->mailbox) {
467 /* if there is another address, and its not a group mailbox name or
468 group terminator, add a comma to separate the addresses */
469 if (pos + 2 >= buflen)
482 address_t *mutt_parse_adrlist(address_t *p, const char *s)
484 /* check for a simple whitespace separated list of addresses */
485 char *q = strpbrk(s, "\"<>():;,\\");
486 char tmp[HUGE_STRING];
489 return rfc822_parse_adrlist(p, s);
491 m_strcpy(tmp, sizeof(tmp), s);
493 while ((q = strtok(q, " \t"))) {
494 p = rfc822_parse_adrlist(p, q);