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 static void rfc822_dequote_comment(char *s)
93 /* if *++s is NUL that's an error, but we don't care */
104 /****************************************************************************/
105 /* Parsing functions */
106 /****************************************************************************/
108 struct rfc822_parse_ctx {
111 char comment[STRING];
118 #define is_special(x) strchr(RFC822Specials,x)
119 #define terminate_string(a, b, c) (a[MIN(b, c)] = 0)
120 #define terminate_buffer(a) terminate_string(a, a##len, sizeof (a) - 1)
124 parse_comment(const char *s, char *comment, size_t *commentlen,
142 s++; /* if *++s is NUL it will be an error anyway */
149 if (*commentlen < commentmax)
150 comment[(*commentlen)++] = *s;
157 parse_quote(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
159 if (*tokenlen < tokenmax)
160 token[(*tokenlen)++] = '"';
163 if (*tokenlen < tokenmax)
164 token[*tokenlen] = *s;
175 if (*tokenlen < tokenmax)
176 token[*tokenlen] = *s;
185 next_token(const char *s, char *token, size_t *tokenlen, size_t tokenmax)
188 return parse_comment(s + 1, token, tokenlen, tokenmax);
191 return parse_quote(s + 1, token, tokenlen, tokenmax);
193 if (is_special(*s)) {
194 if (*tokenlen < tokenmax)
195 token[(*tokenlen)++] = *s;
200 if (ISSPACE(*s) || is_special(*s))
202 if (*tokenlen < tokenmax)
203 token[(*tokenlen)++] = *s;
210 parse_mailboxdomain(const char *s, const char *nonspecial,
211 char *mailbox, size_t *mailboxlen, size_t mailboxmax,
212 struct rfc822_parse_ctx *ctx)
217 if (!strchr(nonspecial, *s) && is_special(*s))
221 if (ctx->commentlen && ctx->commentlen < sizeof(ctx->comment) - 1)
222 ctx->comment[ctx->commentlen++] = ' ';
223 s = next_token(s, ctx->comment, &ctx->commentlen, sizeof(ctx->comment) - 1);
225 s = next_token(s, mailbox, mailboxlen, mailboxmax);
236 parse_address(const char *s, struct rfc822_parse_ctx *ctx)
241 s = parse_mailboxdomain(s, ".\"(\\",
242 token, &tokenlen, sizeof(token) - 1, ctx);
247 if (tokenlen < sizeof(token) - 1)
248 token[tokenlen++] = '@';
249 s = parse_mailboxdomain(s + 1, ".([]\\",
250 token, &tokenlen, sizeof(token) - 1, ctx);
255 terminate_buffer(token);
256 ctx->cur->mailbox = m_strdup(token);
258 if (ctx->commentlen && !ctx->cur->personal) {
259 terminate_buffer(ctx->comment);
260 ctx->cur->personal = m_strdup(ctx->comment);
266 address_t **add_addrspec(address_t **last, struct rfc822_parse_ctx *ctx)
270 ctx->cur = address_new();
271 s = parse_address(ctx->phrase, ctx);
272 if (s && *s && *s != ',' && *s != ';') {
273 address_delete(&ctx->cur);
278 return &(*last)->next;
281 address_t *rfc822_parse_adrlist(address_t *top, const char *s)
283 struct rfc822_parse_ctx ctx = { NULL, "", 0, "", 0 };
287 last = address_list_last(&top);
290 ws_pending = ISSPACE(*s);
296 terminate_buffer(ctx.phrase);
297 terminate_buffer(ctx.comment);
298 last = add_addrspec(last, &ctx);
300 if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
301 terminate_buffer(ctx.comment);
302 ctx.cur->personal = m_strdup(ctx.comment);
307 if (ctx.phraselen && ctx.phraselen < sizeof(ctx.phrase) - 1 && ws_pending)
308 ctx.phrase[ctx.phraselen++] = ' ';
309 s = next_token(s, ctx.phrase, &ctx.phraselen, sizeof(ctx.phrase) - 1);
311 address_delete(&top);
317 if (ctx.commentlen && ctx.commentlen < sizeof(ctx.comment) - 1)
318 ctx.comment[ctx.commentlen++] = ' ';
319 s = next_token(s, ctx.comment, &ctx.commentlen, sizeof(ctx.comment) - 1);
321 address_delete (&top);
328 terminate_buffer(ctx.phrase);
329 last = add_addrspec(last, &ctx);
331 if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
332 terminate_buffer(ctx.comment);
333 ctx.cur->personal = m_strdup(ctx.comment);
338 terminate_buffer(ctx.phrase);
339 *last = address_new();
340 (*last)->mailbox = m_strdup(ctx.phrase);
342 last = &(*last)->next;
347 terminate_buffer(ctx.phrase);
348 last = add_addrspec(last, &ctx);
350 if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
351 terminate_buffer(ctx.comment);
352 ctx.cur->personal = m_strdup(ctx.comment);
355 /* add group terminator */
356 *last = address_new();
360 terminate_buffer(ctx.phrase);
361 ctx.cur = address_new ();
363 /* if we get something like "Michael R. Elkins" remove the quotes */
364 rfc822_dequote_comment(ctx.phrase);
365 ctx.cur->personal = m_strdup(ctx.phrase);
368 s = parse_address(skipspaces(s + 1), &ctx);
369 if (!s || *s != '>' || !ctx.cur->mailbox) {
370 address_delete(&top);
371 address_delete(&ctx.cur);
388 /****************************************************************************/
389 /* Output functions */
390 /****************************************************************************/
393 rfc822_cat(char *buf, size_t buflen, const char *value, const char *specials)
395 if (strpbrk(value, specials)) {
396 char tmp[256], *pc = tmp;
397 size_t tmplen = sizeof (tmp) - 3;
400 for (; *value && tmplen > 1; value++) {
401 if (*value == '\\' || *value == '"') {
410 m_strcpy(buf, buflen, tmp);
412 m_strcpy(buf, buflen, value);
416 void rfc822_write_address_single(char *buf, size_t buflen, address_t * addr,
426 buflen--; /* save room for the terminal nul */
428 if (addr->personal) {
429 if (strpbrk (addr->personal, RFC822Specials)) {
434 for (pc = addr->personal; *pc && buflen > 0; pc++) {
435 if (*pc == '"' || *pc == '\\') {
454 m_strcpy(pbuf, buflen, addr->personal);
455 len = m_strlen(pbuf);
466 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
476 if (ascii_strcmp (addr->mailbox, "@") && !display) {
477 m_strcpy(pbuf, buflen, addr->mailbox);
478 len = m_strlen(pbuf);
480 else if (ascii_strcmp (addr->mailbox, "@") && display) {
481 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
482 len = m_strlen(pbuf);
491 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
516 /* no need to check for length here since we already save space at the
517 beginning of this routine */
521 /* note: it is assumed that `buf' is nul terminated! */
522 void rfc822_write_address (char *buf, size_t buflen, address_t * addr,
526 size_t len = m_strlen(buf);
528 buflen--; /* save room for the terminal nul */
532 return; /* safety check for bogus arguments */
546 for (; addr && buflen > 0; addr = addr->next) {
547 /* use buflen+1 here because we already saved space for the trailing
548 nul char, and the subroutine can make use of it */
549 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
551 /* this should be safe since we always have at least 1 char passed into
552 the above call, which means `pbuf' should always be nul terminated */
553 len = m_strlen(pbuf);
557 /* if there is another address, and its not a group mailbox name or
558 group terminator, add a comma to separate the addresses */
559 if (addr->next && addr->next->mailbox && !addr->group) {