mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2011-2013 Michael R. Elkins <me@mutt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include <string.h>
25#include <stdlib.h>
26
27#ifndef TESTING
28#include "mutt.h"
29#else
30#define safe_strdup strdup
31#define safe_malloc malloc
32#define FREE(x) safe_free(x)
33#define strfcpy(a,b,c) {if (c) {strncpy(a,b,c);a[c-1]=0;}}
34#define LONG_STRING 1024
35#include "rfc822.h"
36#endif
37
38#include "mutt_idna.h"
39
40#define terminate_string(a, b, c) do { if ((b) < (c)) a[(b)] = 0; else \
41 a[(c)] = 0; } while (0)
42
43#define terminate_buffer(a, b) terminate_string(a, b, sizeof (a) - 1)
44
45const char RFC822Specials[] = "@.,:;<>[]\\\"()";
46#define is_special(x) strchr(RFC822Specials,x)
47
48int RFC822Error = 0;
49
50/* these must defined in the same order as the numerated errors given in rfc822.h */
51const char * const RFC822Errors[] = {
52 "out of memory",
53 "mismatched parenthesis",
54 "mismatched quotes",
55 "bad route in <>",
56 "bad address in <>",
57 "bad address spec"
58};
59
60void rfc822_dequote_comment (char *s)
61{
62 char *w = s;
63
64 for (; *s; s++)
65 {
66 if (*s == '\\')
67 {
68 if (!*++s)
69 break; /* error? */
70 *w++ = *s;
71 }
72 else if (*s != '\"')
73 {
74 if (w != s)
75 *w = *s;
76 w++;
77 }
78 }
79 *w = 0;
80}
81
82static void free_address (ADDRESS *a)
83{
84 FREE(&a->personal);
85 FREE(&a->mailbox);
86#ifdef EXACT_ADDRESS
87 FREE(&a->val);
88#endif
89 FREE(&a);
90}
91
92int rfc822_remove_from_adrlist (ADDRESS **a, const char *mailbox)
93{
94 ADDRESS *p, *last = NULL, *t;
95 int rv = -1;
96
97 p = *a;
98 last = NULL;
99 while (p)
100 {
101 if (ascii_strcasecmp (mailbox, p->mailbox) == 0)
102 {
103 if (last)
104 last->next = p->next;
105 else
106 (*a) = p->next;
107 t = p;
108 p = p->next;
109 free_address (t);
110 rv = 0;
111 }
112 else
113 {
114 last = p;
115 p = p->next;
116 }
117 }
118
119 return (rv);
120}
121
122void rfc822_free_address (ADDRESS **p)
123{
124 ADDRESS *t;
125
126 while (*p)
127 {
128 t = *p;
129 *p = (*p)->next;
130#ifdef EXACT_ADDRESS
131 FREE (&t->val);
132#endif
133 FREE (&t->personal);
134 FREE (&t->mailbox);
135 FREE (&t);
136 }
137}
138
139static const char *
140parse_comment (const char *s,
141 char *comment, size_t *commentlen, size_t commentmax)
142{
143 int level = 1;
144
145 while (*s && level)
146 {
147 if (*s == '(')
148 level++;
149 else if (*s == ')')
150 {
151 if (--level == 0)
152 {
153 s++;
154 break;
155 }
156 }
157 else if (*s == '\\')
158 {
159 if (!*++s)
160 break;
161 }
162 if (*commentlen < commentmax)
163 comment[(*commentlen)++] = *s;
164 s++;
165 }
166 if (level)
167 {
168 RFC822Error = ERR_MISMATCH_PAREN;
169 return NULL;
170 }
171 return s;
172}
173
174static const char *
175parse_quote (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
176{
177 while (*s)
178 {
179 if (*tokenlen < tokenmax)
180 token[*tokenlen] = *s;
181 if (*s == '\\')
182 {
183 if (!*++s)
184 break;
185
186 if (*tokenlen < tokenmax)
187 token[*tokenlen] = *s;
188 }
189 else if (*s == '"')
190 return (s + 1);
191 (*tokenlen)++;
192 s++;
193 }
194 RFC822Error = ERR_MISMATCH_QUOTE;
195 return NULL;
196}
197
198static const char *
199next_token (const char *s, char *token, size_t *tokenlen, size_t tokenmax)
200{
201 if (*s == '(')
202 return (parse_comment (s + 1, token, tokenlen, tokenmax));
203 if (*s == '"')
204 return (parse_quote (s + 1, token, tokenlen, tokenmax));
205 if (*s && is_special (*s))
206 {
207 if (*tokenlen < tokenmax)
208 token[(*tokenlen)++] = *s;
209 return (s + 1);
210 }
211 while (*s)
212 {
213 if (is_email_wsp(*s) || is_special (*s))
214 break;
215 if (*tokenlen < tokenmax)
216 token[(*tokenlen)++] = *s;
217 s++;
218 }
219 return s;
220}
221
222static const char *
223parse_mailboxdomain (const char *s, const char *nonspecial,
224 char *mailbox, size_t *mailboxlen, size_t mailboxmax,
225 char *comment, size_t *commentlen, size_t commentmax)
226{
227 const char *ps;
228
229 while (*s)
230 {
231 s = skip_email_wsp(s);
232 if (! *s)
233 return s;
234
235 if (strchr (nonspecial, *s) == NULL && is_special (*s))
236 return s;
237
238 if (*s == '(')
239 {
240 if (*commentlen && *commentlen < commentmax)
241 comment[(*commentlen)++] = ' ';
242 ps = next_token (s, comment, commentlen, commentmax);
243 }
244 else
245 ps = next_token (s, mailbox, mailboxlen, mailboxmax);
246 if (!ps)
247 return NULL;
248 s = ps;
249 }
250
251 return s;
252}
253
254static const char *
255parse_address (const char *s,
256 char *token, size_t *tokenlen, size_t tokenmax,
257 char *comment, size_t *commentlen, size_t commentmax,
258 ADDRESS *addr)
259{
260 s = parse_mailboxdomain (s, ".\"(\\",
261 token, tokenlen, tokenmax,
262 comment, commentlen, commentmax);
263 if (!s)
264 return NULL;
265
266 if (*s == '@')
267 {
268 if (*tokenlen < tokenmax)
269 token[(*tokenlen)++] = '@';
270 s = parse_mailboxdomain (s + 1, ".([]\\",
271 token, tokenlen, tokenmax,
272 comment, commentlen, commentmax);
273 if (!s)
274 return NULL;
275 }
276
277 terminate_string (token, *tokenlen, tokenmax);
278 addr->mailbox = safe_strdup (token);
279
280 if (*commentlen && !addr->personal)
281 {
282 terminate_string (comment, *commentlen, commentmax);
283 addr->personal = safe_strdup (comment);
284 }
285
286 return s;
287}
288
289static const char *
290parse_route_addr (const char *s,
291 char *comment, size_t *commentlen, size_t commentmax,
292 ADDRESS *addr)
293{
294 char token[LONG_STRING];
295 size_t tokenlen = 0;
296
297 s = skip_email_wsp(s);
298
299 /* find the end of the route */
300 if (*s == '@')
301 {
302 while (s && *s == '@')
303 {
304 if (tokenlen < sizeof (token) - 1)
305 token[tokenlen++] = '@';
306 s = parse_mailboxdomain (s + 1, ",.\\[](", token,
307 &tokenlen, sizeof (token) - 1,
308 comment, commentlen, commentmax);
309 }
310 if (!s || *s != ':')
311 {
312 RFC822Error = ERR_BAD_ROUTE;
313 return NULL; /* invalid route */
314 }
315
316 if (tokenlen < sizeof (token) - 1)
317 token[tokenlen++] = ':';
318 s++;
319 }
320
321 if ((s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr)) == NULL)
322 return NULL;
323
324 if (*s != '>')
325 {
326 RFC822Error = ERR_BAD_ROUTE_ADDR;
327 return NULL;
328 }
329
330 if (!addr->mailbox)
331 addr->mailbox = safe_strdup ("@");
332
333 s++;
334 return s;
335}
336
337static const char *
338parse_addr_spec (const char *s,
339 char *comment, size_t *commentlen, size_t commentmax,
340 ADDRESS *addr)
341{
342 char token[LONG_STRING];
343 size_t tokenlen = 0;
344
345 s = parse_address (s, token, &tokenlen, sizeof (token) - 1, comment, commentlen, commentmax, addr);
346 if (s && *s && *s != ',' && *s != ';')
347 {
348 RFC822Error = ERR_BAD_ADDR_SPEC;
349 return NULL;
350 }
351 return s;
352}
353
354static void
355add_addrspec (ADDRESS **top, ADDRESS **last, const char *phrase,
356 char *comment, size_t *commentlen, size_t commentmax)
357{
358 ADDRESS *cur = rfc822_new_address ();
359
360 if (parse_addr_spec (phrase, comment, commentlen, commentmax, cur) == NULL)
361 {
362 rfc822_free_address (&cur);
363 return;
364 }
365
366 if (*last)
367 (*last)->next = cur;
368 else
369 *top = cur;
370 *last = cur;
371}
372
373ADDRESS *rfc822_parse_adrlist (ADDRESS *top, const char *s)
374{
375 int ws_pending, nl;
376#ifdef EXACT_ADDRESS
377 const char *begin;
378#endif
379 const char *ps;
380 char comment[LONG_STRING], phrase[LONG_STRING];
381 size_t phraselen = 0, commentlen = 0;
382 ADDRESS *cur, *last = NULL;
383
384 RFC822Error = 0;
385
386 last = top;
387 while (last && last->next)
388 last = last->next;
389
390 ws_pending = is_email_wsp (*s);
391 if ((nl = mutt_strlen (s)))
392 nl = s[nl - 1] == '\n';
393
394 s = skip_email_wsp(s);
395#ifdef EXACT_ADDRESS
396 begin = s;
397#endif
398 while (*s)
399 {
400 if (*s == ',')
401 {
402 if (phraselen)
403 {
404 terminate_buffer (phrase, phraselen);
405 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
406 }
407 else if (commentlen && last && !last->personal)
408 {
409 terminate_buffer (comment, commentlen);
410 last->personal = safe_strdup (comment);
411 }
412
413#ifdef EXACT_ADDRESS
414 if (last && !last->val)
415 last->val = mutt_substrdup (begin, s);
416#endif
417 commentlen = 0;
418 phraselen = 0;
419 s++;
420#ifdef EXACT_ADDRESS
421 begin = skip_email_wsp(s);
422#endif
423 }
424 else if (*s == '(')
425 {
426 if (commentlen && commentlen < sizeof (comment) - 1)
427 comment[commentlen++] = ' ';
428 if ((ps = next_token (s, comment, &commentlen, sizeof (comment) - 1)) == NULL)
429 {
430 rfc822_free_address (&top);
431 return NULL;
432 }
433 s = ps;
434 }
435 else if (*s == '"')
436 {
437 if (phraselen && phraselen < sizeof (phrase) - 1)
438 phrase[phraselen++] = ' ';
439 if ((ps = parse_quote (s + 1, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
440 {
441 rfc822_free_address (&top);
442 return NULL;
443 }
444 s = ps;
445 }
446 else if (*s == ':')
447 {
448 cur = rfc822_new_address ();
449 terminate_buffer (phrase, phraselen);
450 cur->mailbox = safe_strdup (phrase);
451 cur->group = 1;
452
453 if (last)
454 last->next = cur;
455 else
456 top = cur;
457 last = cur;
458
459#ifdef EXACT_ADDRESS
460 last->val = mutt_substrdup (begin, s);
461#endif
462
463 phraselen = 0;
464 commentlen = 0;
465 s++;
466#ifdef EXACT_ADDRESS
467 begin = skip_email_wsp(s);
468#endif
469 }
470 else if (*s == ';')
471 {
472 if (phraselen)
473 {
474 terminate_buffer (phrase, phraselen);
475 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
476 }
477 else if (commentlen && last && !last->personal)
478 {
479 terminate_buffer (comment, commentlen);
480 last->personal = safe_strdup (comment);
481 }
482#ifdef EXACT_ADDRESS
483 if (last && !last->val)
484 last->val = mutt_substrdup (begin, s);
485#endif
486
487 /* add group terminator */
488 cur = rfc822_new_address ();
489 if (last)
490 {
491 last->next = cur;
492 last = cur;
493 }
494
495 phraselen = 0;
496 commentlen = 0;
497 s++;
498#ifdef EXACT_ADDRESS
499 begin = skip_email_wsp(s);
500#endif
501 }
502 else if (*s == '<')
503 {
504 terminate_buffer (phrase, phraselen);
505 cur = rfc822_new_address ();
506 if (phraselen)
507 cur->personal = safe_strdup (phrase);
508 if ((ps = parse_route_addr (s + 1, comment, &commentlen, sizeof (comment) - 1, cur)) == NULL)
509 {
510 rfc822_free_address (&top);
511 rfc822_free_address (&cur);
512 return NULL;
513 }
514
515 if (last)
516 last->next = cur;
517 else
518 top = cur;
519 last = cur;
520
521 phraselen = 0;
522 commentlen = 0;
523 s = ps;
524 }
525 else
526 {
527 if (phraselen && phraselen < sizeof (phrase) - 1 && ws_pending)
528 phrase[phraselen++] = ' ';
529 if ((ps = next_token (s, phrase, &phraselen, sizeof (phrase) - 1)) == NULL)
530 {
531 rfc822_free_address (&top);
532 return NULL;
533 }
534 s = ps;
535 }
536 ws_pending = is_email_wsp(*s);
537 s = skip_email_wsp(s);
538 }
539
540 if (phraselen)
541 {
542 terminate_buffer (phrase, phraselen);
543 terminate_buffer (comment, commentlen);
544 add_addrspec (&top, &last, phrase, comment, &commentlen, sizeof (comment) - 1);
545 }
546 else if (commentlen && last && !last->personal)
547 {
548 terminate_buffer (comment, commentlen);
549 last->personal = safe_strdup (comment);
550 }
551#ifdef EXACT_ADDRESS
552 if (last)
553 last->val = mutt_substrdup (begin, s - nl < begin ? begin : s - nl);
554#endif
555
556 return top;
557}
558
559void rfc822_qualify (ADDRESS *addr, const char *host)
560{
561 char *p;
562
563 for (; addr; addr = addr->next)
564 if (!addr->group && addr->mailbox && strchr (addr->mailbox, '@') == NULL)
565 {
566 p = safe_malloc (mutt_strlen (addr->mailbox) + mutt_strlen (host) + 2);
567 sprintf (p, "%s@%s", addr->mailbox, host); /* __SPRINTF_CHECKED__ */
568 FREE (&addr->mailbox);
569 addr->mailbox = p;
570 }
571}
572
573void
574rfc822_cat (char *buf, size_t buflen, const char *value, const char *specials)
575{
576 if (strpbrk (value, specials))
577 {
578 char tmp[256], *pc = tmp;
579 size_t tmplen = sizeof (tmp) - 3;
580
581 *pc++ = '"';
582 for (; *value && tmplen > 1; value++)
583 {
584 if (*value == '\\' || *value == '"')
585 {
586 *pc++ = '\\';
587 tmplen--;
588 }
589 *pc++ = *value;
590 tmplen--;
591 }
592 *pc++ = '"';
593 *pc = 0;
594 strfcpy (buf, tmp, buflen);
595 }
596 else
597 strfcpy (buf, value, buflen);
598}
599
600void rfc822_write_address_single (char *buf, size_t buflen, ADDRESS *addr,
601 int display)
602{
603 size_t len;
604 char *pbuf = buf;
605 char *pc;
606
607 if (!addr)
608 return;
609
610 buflen--; /* save room for the terminal nul */
611
612#ifdef EXACT_ADDRESS
613 if (addr->val)
614 {
615 if (!buflen)
616 goto done;
617 strfcpy (pbuf, addr->val, buflen);
618 len = mutt_strlen (pbuf);
619 pbuf += len;
620 buflen -= len;
621 if (addr->group)
622 {
623 if (!buflen)
624 goto done;
625 *pbuf++ = ':';
626 buflen--;
627 *pbuf = 0;
628 }
629 return;
630 }
631#endif
632
633 if (addr->personal)
634 {
635 if (strpbrk (addr->personal, RFC822Specials))
636 {
637 if (!buflen)
638 goto done;
639 *pbuf++ = '"';
640 buflen--;
641 for (pc = addr->personal; *pc && buflen > 0; pc++)
642 {
643 if (*pc == '"' || *pc == '\\')
644 {
645 *pbuf++ = '\\';
646 buflen--;
647 }
648 if (!buflen)
649 goto done;
650 *pbuf++ = *pc;
651 buflen--;
652 }
653 if (!buflen)
654 goto done;
655 *pbuf++ = '"';
656 buflen--;
657 }
658 else
659 {
660 if (!buflen)
661 goto done;
662 strfcpy (pbuf, addr->personal, buflen);
663 len = mutt_strlen (pbuf);
664 pbuf += len;
665 buflen -= len;
666 }
667
668 if (!buflen)
669 goto done;
670 *pbuf++ = ' ';
671 buflen--;
672 }
673
674 if (addr->personal || (addr->mailbox && *addr->mailbox == '@'))
675 {
676 if (!buflen)
677 goto done;
678 *pbuf++ = '<';
679 buflen--;
680 }
681
682 if (addr->mailbox)
683 {
684 if (!buflen)
685 goto done;
686 if (ascii_strcmp (addr->mailbox, "@") && !display)
687 {
688 strfcpy (pbuf, addr->mailbox, buflen);
689 len = mutt_strlen (pbuf);
690 }
691 else if (ascii_strcmp (addr->mailbox, "@") && display)
692 {
693 strfcpy (pbuf, mutt_addr_for_display (addr), buflen);
694 len = mutt_strlen (pbuf);
695 }
696 else
697 {
698 *pbuf = '\0';
699 len = 0;
700 }
701 pbuf += len;
702 buflen -= len;
703
704 if (addr->personal || (addr->mailbox && *addr->mailbox == '@'))
705 {
706 if (!buflen)
707 goto done;
708 *pbuf++ = '>';
709 buflen--;
710 }
711
712 if (addr->group)
713 {
714 if (!buflen)
715 goto done;
716 *pbuf++ = ':';
717 buflen--;
718 if (!buflen)
719 goto done;
720 *pbuf++ = ' ';
721 buflen--;
722 }
723 }
724 else
725 {
726 if (!buflen)
727 goto done;
728 *pbuf++ = ';';
729 buflen--;
730 }
731done:
732 /* no need to check for length here since we already save space at the
733 beginning of this routine */
734 *pbuf = 0;
735}
736
737/* note: it is assumed that `buf' is nul terminated! */
738int rfc822_write_address (char *buf, size_t buflen, ADDRESS *addr, int display)
739{
740 char *pbuf = buf;
741 size_t len = mutt_strlen (buf);
742
743 buflen--; /* save room for the terminal nul */
744
745 if (len > 0)
746 {
747 if (len > buflen)
748 return pbuf - buf; /* safety check for bogus arguments */
749
750 pbuf += len;
751 buflen -= len;
752 if (!buflen)
753 goto done;
754 *pbuf++ = ',';
755 buflen--;
756 if (!buflen)
757 goto done;
758 *pbuf++ = ' ';
759 buflen--;
760 }
761
762 for (; addr && buflen > 0; addr = addr->next)
763 {
764 /* use buflen+1 here because we already saved space for the trailing
765 nul char, and the subroutine can make use of it */
766 rfc822_write_address_single (pbuf, buflen + 1, addr, display);
767
768 /* this should be safe since we always have at least 1 char passed into
769 the above call, which means `pbuf' should always be nul terminated */
770 len = mutt_strlen (pbuf);
771 pbuf += len;
772 buflen -= len;
773
774 /* if there is another address, and its not a group mailbox name or
775 group terminator, add a comma to separate the addresses */
776 if (addr->next && addr->next->mailbox && !addr->group)
777 {
778 if (!buflen)
779 goto done;
780 *pbuf++ = ',';
781 buflen--;
782 if (!buflen)
783 goto done;
784 *pbuf++ = ' ';
785 buflen--;
786 }
787 }
788done:
789 *pbuf = 0;
790 return pbuf - buf;
791}
792
793/* this should be rfc822_cpy_adr */
794ADDRESS *rfc822_cpy_adr_real (ADDRESS *addr)
795{
796 ADDRESS *p = rfc822_new_address ();
797
798#ifdef EXACT_ADDRESS
799 p->val = safe_strdup (addr->val);
800#endif
801 p->personal = safe_strdup (addr->personal);
802 p->mailbox = safe_strdup (addr->mailbox);
803 p->group = addr->group;
804 p->is_intl = addr->is_intl;
805 p->intl_checked = addr->intl_checked;
806 return p;
807}
808
809/* this should be rfc822_cpy_adrlist */
810ADDRESS *rfc822_cpy_adr (ADDRESS *addr, int prune)
811{
812 ADDRESS *top = NULL, *last = NULL;
813
814 for (; addr; addr = addr->next)
815 {
816 if (prune && addr->group && (!addr->next || !addr->next->mailbox))
817 {
818 /* ignore this element of the list */
819 }
820 else if (last)
821 {
822 last->next = rfc822_cpy_adr_real (addr);
823 last = last->next;
824 }
825 else
826 top = last = rfc822_cpy_adr_real (addr);
827 }
828 return top;
829}
830
831/* append list 'b' to list 'a' and return the last element in the new list */
832ADDRESS *rfc822_append (ADDRESS **a, ADDRESS *b, int prune)
833{
834 ADDRESS *tmp = *a;
835
836 while (tmp && tmp->next)
837 tmp = tmp->next;
838 if (!b)
839 return tmp;
840 if (tmp)
841 tmp->next = rfc822_cpy_adr (b, prune);
842 else
843 tmp = *a = rfc822_cpy_adr (b, prune);
844 while (tmp && tmp->next)
845 tmp = tmp->next;
846 return tmp;
847}
848
849/* incomplete. Only used to thwart the APOP MD5 attack (#2846). */
850int rfc822_valid_msgid (const char *msgid)
851{
852 /* msg-id = "<" addr-spec ">"
853 * addr-spec = local-part "@" domain
854 * local-part = word *("." word)
855 * word = atom / quoted-string
856 * atom = 1*<any CHAR except specials, SPACE and CTLs>
857 * CHAR = ( 0.-127. )
858 * specials = "(" / ")" / "<" / ">" / "@"
859 / "," / ";" / ":" / "\" / <">
860 / "." / "[" / "]"
861 * SPACE = ( 32. )
862 * CTLS = ( 0.-31., 127.)
863 * quoted-string = <"> *(qtext/quoted-pair) <">
864 * qtext = <any CHAR except <">, "\" and CR>
865 * CR = ( 13. )
866 * quoted-pair = "\" CHAR
867 * domain = sub-domain *("." sub-domain)
868 * sub-domain = domain-ref / domain-literal
869 * domain-ref = atom
870 * domain-literal = "[" *(dtext / quoted-pair) "]"
871 */
872
873 unsigned int l, i;
874
875 if (!msgid || !*msgid)
876 return -1;
877
878 l = mutt_strlen (msgid);
879 if (l < 5) /* <atom@atom> */
880 return -1;
881 if (msgid[0] != '<' || msgid[l-1] != '>')
882 return -1;
883 if (!(strrchr (msgid, '@')))
884 return -1;
885
886 /* TODO: complete parser */
887 for (i = 0; i < l; i++)
888 if ((unsigned char)msgid[i] > 127)
889 return -1;
890
891 return 0;
892}
893
894#ifdef TESTING
895int safe_free (void **p) /* __SAFE_FREE_CHECKED__ */
896{
897 free(*p); /* __MEM_CHECKED__ */
898 *p = 0;
899}
900
901int main (int argc, char **argv)
902{
903 ADDRESS *list;
904 char buf[256];
905# if 0
906 char *str = "michael, Michael Elkins <me@mutt.org>, testing a really complex address: this example <@contains.a.source.route,@with.multiple.hosts:address@example.com>;, lothar@of.the.hillpeople (lothar)";
907# else
908 char *str = "a b c ";
909# endif
910
911 list = rfc822_parse_adrlist (NULL, str);
912 buf[0] = 0;
913 rfc822_write_address (buf, sizeof (buf), list);
914 rfc822_free_address (&list);
915 puts (buf);
916 exit (0);
917}
918#endif