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"
41 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
42 a[(c)] = 0; } while (0)
44 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
47 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
49 #define is_special(x) strchr(RFC822Specials,x)
53 /* these must defined in the same order as the numerated errors given in rfc822.h */
54 const char *RFC822Errors[] = {
56 "mismatched parenthesis",
63 void rfc822_dequote_comment (char *s)
73 else if (*s != '\"') {
82 void rfc822_free_address (ADDRESS ** p)
89 p_delete(&t->personal);
90 p_delete(&t->mailbox);
95 static const char *parse_comment (const char *s,
96 char *comment, size_t * commentlen,
101 while (*s && level) {
104 else if (*s == ')') {
110 else if (*s == '\\') {
114 if (*commentlen < commentmax)
115 comment[(*commentlen)++] = *s;
119 RFC822Error = ERR_MISMATCH_PAREN;
125 static const char *parse_quote (const char *s, char *token, size_t * tokenlen,
128 if (*tokenlen < tokenmax)
129 token[(*tokenlen)++] = '"';
131 if (*tokenlen < tokenmax)
132 token[*tokenlen] = *s;
141 if (*tokenlen < tokenmax)
142 token[*tokenlen] = *s;
147 RFC822Error = ERR_MISMATCH_QUOTE;
151 static const char *next_token (const char *s, char *token, size_t * tokenlen,
155 return (parse_comment (s + 1, token, tokenlen, tokenmax));
157 return (parse_quote (s + 1, token, tokenlen, tokenmax));
158 if (is_special (*s)) {
159 if (*tokenlen < tokenmax)
160 token[(*tokenlen)++] = *s;
164 if (ISSPACE ((unsigned char) *s) || is_special (*s))
166 if (*tokenlen < tokenmax)
167 token[(*tokenlen)++] = *s;
173 static const char *parse_mailboxdomain (const char *s, const char *nonspecial,
174 char *mailbox, size_t * mailboxlen,
175 size_t mailboxmax, char *comment,
183 if (strchr (nonspecial, *s) == NULL && is_special (*s))
187 if (*commentlen && *commentlen < commentmax)
188 comment[(*commentlen)++] = ' ';
189 ps = next_token (s, comment, commentlen, commentmax);
192 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
201 static const char *parse_address (const char *s,
202 char *token, size_t * tokenlen,
203 size_t tokenmax, char *comment,
204 size_t * commentlen, size_t commentmax,
207 s = parse_mailboxdomain (s, ".\"(\\",
208 token, tokenlen, tokenmax,
209 comment, commentlen, commentmax);
214 if (*tokenlen < tokenmax)
215 token[(*tokenlen)++] = '@';
216 s = parse_mailboxdomain (s + 1, ".([]\\",
217 token, tokenlen, tokenmax,
218 comment, commentlen, commentmax);
223 terminate_string (token, *tokenlen, tokenmax);
224 addr->mailbox = m_strdup(token);
226 if (*commentlen && !addr->personal) {
227 terminate_string (comment, *commentlen, commentmax);
228 addr->personal = m_strdup(comment);
234 static const char *parse_route_addr (const char *s,
235 char *comment, size_t * commentlen,
236 size_t commentmax, ADDRESS * addr)
243 /* find the end of the route */
245 while (s && *s == '@') {
246 if (tokenlen < sizeof (token) - 1)
247 token[tokenlen++] = '@';
248 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
249 &tokenlen, sizeof (token) - 1,
250 comment, commentlen, commentmax);
252 if (!s || *s != ':') {
253 RFC822Error = ERR_BAD_ROUTE;
254 return NULL; /* invalid route */
257 if (tokenlen < sizeof (token) - 1)
258 token[tokenlen++] = ':';
263 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
264 commentlen, commentmax, addr)) == NULL)
268 RFC822Error = ERR_BAD_ROUTE_ADDR;
273 addr->mailbox = m_strdup("@");
279 static const char *parse_addr_spec (const char *s,
280 char *comment, size_t * commentlen,
281 size_t commentmax, ADDRESS * addr)
287 parse_address (s, token, &tokenlen, sizeof (token) - 1, comment,
288 commentlen, commentmax, addr);
289 if (s && *s && *s != ',' && *s != ';') {
290 RFC822Error = ERR_BAD_ADDR_SPEC;
297 add_addrspec (ADDRESS ** top, ADDRESS ** last, const char *phrase,
298 char *comment, size_t * commentlen, size_t commentmax)
300 ADDRESS *cur = rfc822_new_address ();
302 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL) {
303 rfc822_free_address (&cur);
314 ADDRESS *rfc822_parse_adrlist (ADDRESS * top, const char *s)
317 const char *begin, *ps;
318 char comment[STRING], phrase[STRING];
319 size_t phraselen = 0, commentlen = 0;
320 ADDRESS *cur, *last = NULL;
325 while (last && last->next)
328 ws_pending = isspace ((unsigned char) *s);
330 begin = s = vskipspaces(s);
334 terminate_buffer (phrase, phraselen);
335 add_addrspec (&top, &last, phrase, comment, &commentlen,
336 sizeof (comment) - 1);
338 else if (commentlen && last && !last->personal) {
339 terminate_buffer (comment, commentlen);
340 last->personal = m_strdup(comment);
346 begin = vskipspaces(s);
348 else if (*s == '(') {
349 if (commentlen && commentlen < sizeof (comment) - 1)
350 comment[commentlen++] = ' ';
352 next_token (s, comment, &commentlen,
353 sizeof (comment) - 1)) == NULL) {
354 rfc822_free_address (&top);
359 else if (*s == ':') {
360 cur = rfc822_new_address ();
361 terminate_buffer (phrase, phraselen);
362 cur->mailbox = m_strdup(phrase);
374 begin = vskipspaces(s);
376 else if (*s == ';') {
378 terminate_buffer (phrase, phraselen);
379 add_addrspec (&top, &last, phrase, comment, &commentlen,
380 sizeof (comment) - 1);
382 else if (commentlen && last && !last->personal) {
383 terminate_buffer (comment, commentlen);
384 last->personal = m_strdup(comment);
387 /* add group terminator */
388 cur = rfc822_new_address ();
397 begin = vskipspaces(s);
399 else if (*s == '<') {
400 terminate_buffer (phrase, phraselen);
401 cur = rfc822_new_address ();
404 p_delete(&cur->personal);
405 /* if we get something like "Michael R. Elkins" remove the quotes */
406 rfc822_dequote_comment (phrase);
407 cur->personal = m_strdup(phrase);
410 parse_route_addr (s + 1, comment, &commentlen,
411 sizeof (comment) - 1, cur)) == NULL) {
412 rfc822_free_address (&top);
413 rfc822_free_address (&cur);
428 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
429 phrase[phraselen++] = ' ';
431 next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL) {
432 rfc822_free_address (&top);
437 ws_pending = isspace ((unsigned char) *s);
442 terminate_buffer (phrase, phraselen);
443 terminate_buffer (comment, commentlen);
444 add_addrspec (&top, &last, phrase, comment, &commentlen,
445 sizeof (comment) - 1);
447 else if (commentlen && last && !last->personal) {
448 terminate_buffer (comment, commentlen);
449 last->personal = m_strdup(comment);
455 void rfc822_qualify (ADDRESS * addr, const char *host)
459 for (; addr; addr = addr->next)
460 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL) {
461 p = p_new(char, m_strlen(addr->mailbox) + m_strlen(host) + 2);
462 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
463 p_delete(&addr->mailbox);
469 rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
471 if (strpbrk (value, specials)) {
472 char tmp[256], *pc = tmp;
473 size_t tmplen = sizeof (tmp) - 3;
476 for (; *value && tmplen > 1; value++) {
477 if (*value == '\\' || *value == '"') {
486 m_strcpy(buf, buflen, tmp);
489 m_strcpy(buf, buflen, value);
492 void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS * addr,
502 buflen--; /* save room for the terminal nul */
504 if (addr->personal) {
505 if (strpbrk (addr->personal, RFC822Specials)) {
510 for (pc = addr->personal; *pc && buflen > 0; pc++) {
511 if (*pc == '"' || *pc == '\\') {
530 m_strcpy(pbuf, buflen, addr->personal);
531 len = m_strlen(pbuf);
542 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
552 if (ascii_strcmp (addr->mailbox, "@") && !display) {
553 m_strcpy(pbuf, buflen, addr->mailbox);
554 len = m_strlen(pbuf);
556 else if (ascii_strcmp (addr->mailbox, "@") && display) {
557 m_strcpy(pbuf, buflen, mutt_addr_for_display(addr));
558 len = m_strlen(pbuf);
567 if (addr->personal || (addr->mailbox && *addr->mailbox == '@')) {
592 /* no need to check for length here since we already save space at the
593 beginning of this routine */
597 /* note: it is assumed that `buf' is nul terminated! */
598 void rfc822_write_address (char *buf, size_t buflen, ADDRESS * addr,
602 size_t len = m_strlen(buf);
604 buflen--; /* save room for the terminal nul */
608 return; /* safety check for bogus arguments */
622 for (; addr && buflen > 0; addr = addr->next) {
623 /* use buflen+1 here because we already saved space for the trailing
624 nul char, and the subroutine can make use of it */
625 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
627 /* this should be safe since we always have at least 1 char passed into
628 the above call, which means `pbuf' should always be nul terminated */
629 len = m_strlen(pbuf);
633 /* if there is another address, and its not a group mailbox name or
634 group terminator, add a comma to separate the addresses */
635 if (addr->next && addr->next->mailbox && !addr->group) {
650 /* this should be rfc822_cpy_adr */
651 ADDRESS *rfc822_cpy_adr_real (ADDRESS * addr)
653 ADDRESS *p = rfc822_new_address ();
655 p->personal = m_strdup(addr->personal);
656 p->mailbox = m_strdup(addr->mailbox);
657 p->group = addr->group;
661 /* this should be rfc822_cpy_adrlist */
662 ADDRESS *rfc822_cpy_adr (ADDRESS * addr)
664 ADDRESS *top = NULL, *last = NULL;
666 for (; addr; addr = addr->next) {
668 last->next = rfc822_cpy_adr_real (addr);
672 top = last = rfc822_cpy_adr_real (addr);
677 /* append list 'b' to list 'a' and return the last element in the new list */
678 ADDRESS *rfc822_append (ADDRESS ** a, ADDRESS * b)
682 while (tmp && tmp->next)
687 tmp->next = rfc822_cpy_adr (b);
689 tmp = *a = rfc822_cpy_adr (b);
690 while (tmp && tmp->next)