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);
277 fprintf(stderr, "ADD [%s]\n", ctx->cur->mailbox);
279 return &(*last)->next;
282 address_t *rfc822_parse_adrlist(address_t *top, const char *s)
284 struct rfc822_parse_ctx ctx = { NULL, "", 0, "", 0 };
288 last = address_list_last(&top);
291 ws_pending = ISSPACE(*s);
297 terminate_buffer(ctx.phrase);
298 terminate_buffer(ctx.comment);
299 last = add_addrspec(last, &ctx);
301 if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
302 terminate_buffer(ctx.comment);
303 ctx.cur->personal = m_strdup(ctx.comment);
308 if (ctx.phraselen && ctx.phraselen < sizeof(ctx.phrase) - 1 && ws_pending)
309 ctx.phrase[ctx.phraselen++] = ' ';
310 s = next_token(s, ctx.phrase, &ctx.phraselen, sizeof(ctx.phrase) - 1);
312 address_delete(&top);
318 if (ctx.commentlen && ctx.commentlen < sizeof(ctx.comment) - 1)
319 ctx.comment[ctx.commentlen++] = ' ';
320 s = next_token(s, ctx.comment, &ctx.commentlen, sizeof(ctx.comment) - 1);
322 address_delete (&top);
329 terminate_buffer(ctx.phrase);
330 last = add_addrspec(last, &ctx);
332 if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
333 terminate_buffer(ctx.comment);
334 ctx.cur->personal = m_strdup(ctx.comment);
339 terminate_buffer(ctx.phrase);
340 *last = address_new();
341 (*last)->mailbox = m_strdup(ctx.phrase);
343 last = &(*last)->next;
348 terminate_buffer(ctx.phrase);
349 last = add_addrspec(last, &ctx);
351 if (ctx.commentlen && ctx.cur && !ctx.cur->personal) {
352 terminate_buffer(ctx.comment);
353 ctx.cur->personal = m_strdup(ctx.comment);
356 /* add group terminator */
357 *last = address_new();
361 terminate_buffer(ctx.phrase);
362 ctx.cur = address_new ();
364 /* if we get something like "Michael R. Elkins" remove the quotes */
365 rfc822_dequote_comment(ctx.phrase);
366 ctx.cur->personal = m_strdup(ctx.phrase);
369 s = parse_address(skipspaces(s + 1), &ctx);
370 if (!s || *s != '>' || !ctx.cur->mailbox) {
371 address_delete(&top);
372 address_delete(&ctx.cur);
389 /****************************************************************************/
390 /* Output functions */
391 /****************************************************************************/
394 rfc822_cat(char *buf, size_t buflen, const char *value, const char *specials)
396 if (strpbrk(value, specials)) {
397 char tmp[256], *pc = tmp;
398 size_t tmplen = sizeof (tmp) - 3;
401 for (; *value && tmplen > 1; value++) {
402 if (*value == '\\' || *value == '"') {
411 m_strcpy(buf, buflen, tmp);
413 m_strcpy(buf, buflen, value);
417 void rfc822_write_address_single(char *buf, size_t buflen, address_t * addr,
427 buflen--; /* save room for the terminal nul */
429 if (addr->personal) {
430 if (strpbrk (addr->personal, RFC822Specials)) {
435 for (pc = addr->personal; *pc && buflen > 0; pc++) {
436 if (*pc == '"' || *pc == '\\') {
455 m_strcpy(pbuf, buflen, addr->personal);
456 len = m_strlen(pbuf);
467 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
477 if (ascii_strcmp (addr->mailbox, "@") && !display) {
478 m_strcpy(pbuf, buflen, addr->mailbox);
479 len = m_strlen(pbuf);
481 else if (ascii_strcmp (addr->mailbox, "@") && display) {
482 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
483 len = m_strlen(pbuf);
492 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
517 /* no need to check for length here since we already save space at the
518 beginning of this routine */
522 /* note: it is assumed that `buf' is nul terminated! */
523 void rfc822_write_address (char *buf, size_t buflen, address_t * addr,
527 size_t len = m_strlen(buf);
529 buflen--; /* save room for the terminal nul */
533 return; /* safety check for bogus arguments */
547 for (; addr && buflen > 0; addr = addr->next) {
548 /* use buflen+1 here because we already saved space for the trailing
549 nul char, and the subroutine can make use of it */
550 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
552 /* this should be safe since we always have at least 1 char passed into
553 the above call, which means `pbuf' should always be nul terminated */
554 len = m_strlen(pbuf);
558 /* if there is another address, and its not a group mailbox name or
559 group terminator, add a comma to separate the addresses */
560 if (addr->next && addr->next->mailbox && !addr->group) {