2 * This code has been adapted from Mutt 1.5.16 which is
4 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #define safe_strdup xstrdup
26 #define safe_malloc xmalloc
27 #define SKIPWS(x) while(isspace(*x))x++
28 #define FREE(x) free(*(x)), *(x) = NULL
29 #define ISSPACE isspace
30 #define strfcpy(a,b,c) {if (c) {strncpy(a,b,c);a[c-1]=0;}}
36 #define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
37 a[(c)] = 0; } while (0)
39 #define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
41 const char RFC822Specials[] = "@.,:;<>[]\\\"()";
42 #define is_special(x) strchr(RFC822Specials,x)
46 /* these must defined in the same order as the numerated errors given in rfc822.h */
47 const char *RFC822Errors[] = {
49 "mismatched parenthesis",
56 static void rfc822_dequote_comment (char *s)
78 void rfc822_free_address (ADDRESS **p)
93 parse_comment (const char *s,
94 char *comment, size_t *commentlen, size_t commentmax)
115 if (*commentlen < commentmax)
116 comment[(*commentlen)++] = *s;
121 RFC822Error = ERR_MISMATCH_PAREN;
128 parse_quote (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
130 if (*tokenlen < tokenmax)
131 token[(*tokenlen)++] = '"';
134 if (*tokenlen < tokenmax)
135 token[*tokenlen] = *s;
146 if (*tokenlen < tokenmax)
147 token[*tokenlen] = *s;
152 RFC822Error = ERR_MISMATCH_QUOTE;
157 next_token (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
160 return (parse_comment (s + 1, token, tokenlen, tokenmax));
162 return (parse_quote (s + 1, token, tokenlen, tokenmax));
165 if (*tokenlen < tokenmax)
166 token[(*tokenlen)++] = *s;
171 if (ISSPACE ((unsigned char) *s) || is_special (*s))
173 if (*tokenlen < tokenmax)
174 token[(*tokenlen)++] = *s;
181 parse_mailboxdomain (const char *s, const char *nonspecial,
182 char *mailbox, size_t *mailboxlen, size_t mailboxmax,
183 char *comment, size_t *commentlen, size_t commentmax)
190 if (strchr (nonspecial, *s) == NULL && is_special (*s))
195 if (*commentlen && *commentlen < commentmax)
196 comment[(*commentlen)++] = ' ';
197 ps = next_token (s, comment, commentlen, commentmax);
200 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
210 parse_address (const char *s,
211 char *token, size_t *tokenlen, size_t tokenmax,
212 char *comment, size_t *commentlen, size_t commentmax,
215 s = parse_mailboxdomain (s, ".\"(\\",
216 token, tokenlen, tokenmax,
217 comment, commentlen, commentmax);
223 if (*tokenlen < tokenmax)
224 token[(*tokenlen)++] = '@';
225 s = parse_mailboxdomain (s + 1, ".([]\\",
226 token, tokenlen, tokenmax,
227 comment, commentlen, commentmax);
232 terminate_string (token, *tokenlen, tokenmax);
233 addr->mailbox = safe_strdup (token);
235 if (*commentlen && !addr->personal)
237 terminate_string (comment, *commentlen, commentmax);
238 addr->personal = safe_strdup (comment);
245 parse_route_addr (const char *s,
246 char *comment, size_t *commentlen, size_t commentmax,
254 /* find the end of the route */
257 while (s && *s == '@')
259 if (tokenlen < sizeof (token) - 1)
260 token[tokenlen++] = '@';
261 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
262 &tokenlen, sizeof (token) - 1,
263 comment, commentlen, commentmax);
267 RFC822Error = ERR_BAD_ROUTE;
268 return NULL; /* invalid route */
271 if (tokenlen < sizeof (token) - 1)
272 token[tokenlen++] = ':';
276 if ((s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr)) == NULL)
281 RFC822Error = ERR_BAD_ROUTE_ADDR;
286 addr->mailbox = safe_strdup ("@");
293 parse_addr_spec (const char *s,
294 char *comment, size_t *commentlen, size_t commentmax,
300 s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr);
301 if (s && *s && *s != ',' && *s != ';')
303 RFC822Error = ERR_BAD_ADDR_SPEC;
310 add_addrspec (ADDRESS **top, ADDRESS **last, const char *phrase,
311 char *comment, size_t *commentlen, size_t commentmax)
313 ADDRESS *cur = rfc822_new_address ();
315 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL)
317 rfc822_free_address (&cur);
328 ADDRESS *rfc822_parse_adrlist (ADDRESS *top, const char *s)
331 const char *begin, *ps;
332 char comment[STRING], phrase[STRING];
333 size_t phraselen = 0, commentlen = 0;
334 ADDRESS *cur, *last = NULL;
339 while (last && last->next)
342 ws_pending = isspace ((unsigned char) *s);
352 terminate_buffer (phrase, phraselen);
353 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
355 else if (commentlen && last && !last->personal)
357 terminate_buffer (comment, commentlen);
358 last->personal = safe_strdup (comment);
369 if (commentlen && commentlen < sizeof (comment) - 1)
370 comment[commentlen++] = ' ';
371 if ((ps = next_token (s, comment, &commentlen, sizeof (comment) - 1)) == NULL)
373 rfc822_free_address (&top);
380 cur = rfc822_new_address ();
381 terminate_buffer (phrase, phraselen);
382 cur->mailbox = safe_strdup (phrase);
401 terminate_buffer (phrase, phraselen);
402 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
404 else if (commentlen && last && !last->personal)
406 terminate_buffer (comment, commentlen);
407 last->personal = safe_strdup (comment);
410 /* add group terminator */
411 cur = rfc822_new_address ();
426 terminate_buffer (phrase, phraselen);
427 cur = rfc822_new_address ();
431 FREE (&cur->personal);
432 /* if we get something like "Michael R. Elkins" remove the quotes */
433 rfc822_dequote_comment (phrase);
434 cur->personal = safe_strdup (phrase);
436 if ((ps = parse_route_addr (s + 1, comment, &commentlen, sizeof (comment) - 1, cur)) == NULL)
438 rfc822_free_address (&top);
439 rfc822_free_address (&cur);
455 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
456 phrase[phraselen++] = ' ';
457 if ((ps = next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
459 rfc822_free_address (&top);
464 ws_pending = isspace ((unsigned char) *s);
470 terminate_buffer (phrase, phraselen);
471 terminate_buffer (comment, commentlen);
472 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
474 else if (commentlen && last && !last->personal)
476 terminate_buffer (comment, commentlen);
477 last->personal = safe_strdup (comment);