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