Imported Upstream version 0.63.0
[hcoop/debian/courier-authlib.git] / rfc822 / rfc2047u.c
dissimilarity index 77%
index 56e36d1..50d49fc 100644 (file)
-/*
-** Copyright 1998 - 2000 Double Precision, Inc.  See COPYING for
-** distribution information.
-*/
-
-
-#include       <stdio.h>
-#include       <ctype.h>
-#include       <string.h>
-#include       <stdlib.h>
-#include       <errno.h>
-
-#include       "rfc822.h"
-#include       "rfc2047.h"
-
-static const char rcsid[]="$Id: rfc2047u.c,v 1.5 2004/05/23 14:28:24 mrsam Exp $";
-
-#if HAVE_LIBUNICODE
-
-#include "../unicode/unicode.h"
-
-struct decode_unicode_s {
-       const struct unicode_info *mychset;
-       int options;
-
-       char *bufptr;
-       size_t bufsize;
-} ;
-
-static void save_unicode_text(const char *p, int l, struct decode_unicode_s *s)
-{
-       if (s->bufptr)
-               memcpy(s->bufptr+s->bufsize, p, l);
-
-       s->bufsize += l;
-}
-
-static int save_unicode(const char *txt, int len, const char *chset,
-                       const char *lang,
-                       void *arg)
-{
-       struct decode_unicode_s *p=(struct decode_unicode_s *)arg;
-       char *txts=malloc(len+1);
-       char *s;
-       int i;
-
-       if (!txts)
-               return (-1);
-       memcpy(txts, txt, len);
-       txts[len]=0;
-
-       if (!chset)
-               chset=unicode_ISO8859_1.chset;
-
-       s=unicode_convert_fromchset(txts, chset, p->mychset);
-       if (!s && p->options & RFC2047_DECODE_REPLACE)
-       {
-       const struct unicode_info *uiptr=unicode_find(chset);
-               if (uiptr)
-                       s=unicode_xconvert(txts, uiptr, p->mychset);
-       }
-       free(txts);
-       if (s)
-       {
-               save_unicode_text(s, strlen(s), p);
-               free(s);
-               return (0);
-       }
-
-       if (p->options & RFC2047_DECODE_ABORT)
-       {
-               errno=EINVAL;
-               return (-1);
-       }
-
-       if (p->options & RFC2047_DECODE_DISCARD)
-               return (0);
-
-       if (!(p->options & RFC2047_DECODE_NOTAG))
-       {
-               save_unicode_text(" [", 2, p);
-               save_unicode_text(chset, strlen(chset), p);
-               save_unicode_text("] ", 2, p);
-               if (!(p->options & RFC2047_DECODE_REPLACE))
-               {
-                       save_unicode_text(txt, len, p);
-                       return (0);
-               }
-       }
-
-       if (p->options & RFC2047_DECODE_REPLACE)
-               for (i=0; i < strlen(txt); i++)
-                       save_unicode_text("?", 1, p);
-
-       return (0);
-}
-
-char *rfc2047_decode_unicode(const char *text,
-       const struct unicode_info *mychset,
-       int options)
-{
-       struct decode_unicode_s s;
-       char *p=0;
-
-       s.mychset=mychset;
-       s.options=0;
-
-       s.bufptr=0;
-       s.bufsize=1;
-
-
-       if (rfc2047_decode(text, &save_unicode, &s))
-               return (0);
-
-       s.bufptr=p=malloc(s.bufsize);
-       if (!s.bufptr)
-               return (0);
-
-       s.bufsize=0;
-       if (rfc2047_decode(text, &save_unicode, &s))
-       {
-               free(p);
-               return (0);
-       }
-       save_unicode_text("", 1, (void *)&s);
-       return (p);
-}
-
-
-static char *do_rfc2047_decode_enhanced(const char *text, const char *mychset)
-{
-       const struct unicode_info *u=unicode_find(mychset);
-
-       if (!u) u=&unicode_ISO8859_1;
-
-       return rfc2047_decode_unicode(text, u, 0);
-}
-
-void rfc2047_print_unicode(const struct rfc822a *a,
-                          const char *charset,
-                          void (*print_func)(char, void *),
-                          void (*print_separator)(const char *, void *),
-                          void *ptr)
-{
-       rfc822_print_common(a, &do_rfc2047_decode_enhanced, charset,
-                           print_func, print_separator, ptr);
-}
-
-
-#endif
+/*
+** Copyright 1998 - 2009 Double Precision, Inc.  See COPYING for
+** distribution information.
+*/
+
+
+#include       <stdio.h>
+#include       <ctype.h>
+#include       <string.h>
+#include       <stdlib.h>
+#include       <errno.h>
+
+#include       "rfc822.h"
+#include       "rfc822hdr.h"
+#include       "rfc2047.h"
+#include       "../unicode/unicode.h"
+
+#if LIBIDN
+#include <idna.h>
+#include <stringprep.h>
+#endif
+
+static const char rcsid[]="$Id: rfc2047u.c,v 1.11 2009/11/22 19:39:52 mrsam Exp $";
+
+static ssize_t rfc822_decode_rfc2047_atom(const char *str,
+                                         size_t cnt,
+
+                                         void (*callback)(const char *,
+                                                          const char *,
+                                                          const char *,
+                                                          size_t,
+                                                          void *),
+                                         void *ptr);
+
+static int rfc2047_decode_unicode(const char *text,
+                                 const struct unicode_info *u,
+                                 void (*callback)(const char *, size_t,
+                                                  void *),
+                                 void *ptr);
+
+struct decode_unicode_s {
+       const struct unicode_info *mychset;
+
+       char *bufptr;
+       size_t bufsize;
+} ;
+
+static void save_unicode_text(const char *p, size_t l, void *ptr)
+{
+       struct decode_unicode_s *s=
+               (struct decode_unicode_s *)ptr;
+
+       if (s->bufptr)
+               memcpy(s->bufptr+s->bufsize, p, l);
+
+       s->bufsize += l;
+}
+
+struct rfc822_display_name_s {
+       const struct unicode_info *u;
+       void (*print_func)(const char *, size_t, void *);
+       void *ptr;
+};
+
+static void unknown_charset(const char *chset,
+                           void (*print_func)(const char *, size_t, void *),
+                           void *ptr)
+{
+       static const char unknown[]="[unknown character set: ";
+
+       (*print_func)(unknown, sizeof(unknown)-1, ptr);
+       (*print_func)(chset, strlen(chset), ptr);
+       (*print_func)("]", 1, ptr);
+}
+
+static void rfc822_display_addr_cb(const char *chset,
+                                  const char *lang,
+                                  const char *content,
+                                  size_t cnt,
+                                  void *dummy)
+{
+       const struct unicode_info *uchset=unicode_find(chset);
+       struct rfc822_display_name_s *s=
+               (struct rfc822_display_name_s *)dummy;
+       char *ptr;
+       char *buf;
+
+       if (!uchset)
+       {
+               unknown_charset(chset, s->print_func, s->ptr);
+               return;
+       }
+
+       buf=malloc(cnt+1);
+
+       if (!buf)
+               return;
+
+       memcpy(buf, content, cnt);
+       buf[cnt]=0;
+
+       ptr=unicode_xconvert(buf, uchset, s->u);
+       free(buf);
+
+       if (ptr)
+       {
+               (*s->print_func)(ptr, strlen(ptr), s->ptr);
+               free(ptr);
+       }
+}
+
+static
+int rfc822_display_name_int(const struct rfc822a *rfcp, int index,
+                           const char *chset,
+                           void (*print_func)(const char *, size_t, void *),
+                           void *ptr)
+{
+       struct rfc822_display_name_s s;
+       const struct rfc822addr *addrs;
+
+       struct rfc822token *i;
+       int     prev_isatom=0;
+       int     isatom=0;
+       ssize_t rc;
+
+       if (index < 0 || index >= rfcp->naddrs) return 0;
+
+       addrs=rfcp->addrs+index;
+
+       if (!addrs->name)
+               return rfc822_display_addr(rfcp, index, chset, print_func, ptr);
+
+       if (chset == NULL)
+       {
+               s.u=NULL;
+       }
+       else
+       {
+               s.u=unicode_find(chset);
+
+               if (!s.u)
+               {
+                       unknown_charset(chset, print_func, ptr);
+                       return (0);
+               }
+       }
+
+       s.print_func=print_func;
+       s.ptr=ptr;
+
+       for (i=addrs->name; i; i=i->next, prev_isatom=isatom)
+       {
+               isatom=rfc822_is_atom(i->token);
+               if (isatom && prev_isatom)
+                       (*print_func)(" ", 1, ptr);
+
+               if (i->token == '"' || i->token == '(')
+               {
+                       size_t l=i->len;
+                       char *p, *q, *r;
+
+                       if (i->token == '(')
+                       {
+                               if (l > 2)
+                                       l -= 2;
+                               else
+                                       l=0;
+                       }
+
+                       p=malloc(l+1);
+
+                       if (!p)
+                               return -1;
+
+                       if (l)
+                       {
+                               if (i->token == '(')
+                               {
+                                       memcpy(p, i->ptr+1, l);
+                               }
+                               else
+                               {
+                                       memcpy(p, i->ptr, l);
+                               }
+                       }
+
+
+                       p[l]=0;
+
+                       for (q=r=p; *q; *r++ = *q++)
+                               if (*q == '\\' && q[1])
+                                       ++q;
+
+                       *r=0;
+
+                       if (chset == NULL)
+                       {
+                               (*print_func)(p, strlen(p), ptr);
+                       }
+                       else if (rfc822_display_hdrvalue("subject",
+                                                        p, s.u->chset,
+                                                        print_func,
+                                                        NULL, ptr) < 0)
+                       {
+                               free(p);
+                               return -1;
+                       }
+                       free(p);
+                       continue;
+               }
+
+               if (i->token)
+               {
+                       char c= (char)i->token;
+
+                       (*print_func)(&c, 1, ptr);
+                       continue;
+               }
+
+               rc=chset ? rfc822_decode_rfc2047_atom(i->ptr, i->len,
+                                                     rfc822_display_addr_cb,
+                                                     &s):0;
+
+               if (rc < 0)
+                       return -1;
+
+               if (rc == 0)
+               {
+                       (*print_func)(i->ptr, i->len, ptr);
+                       continue;
+               }
+
+               if (i->next && i->next->token == 0)
+               {
+                       rc=rfc822_decode_rfc2047_atom(i->next->ptr,
+                                                     i->next->len,
+                                                     NULL, NULL);
+
+                       if (rc < 0)
+                               return -1;
+
+                       if (rc > 0)
+                               isatom=0; /* Suppress the separating space */
+               }
+       }
+       return 0;
+}
+
+int rfc822_display_name(const struct rfc822a *rfcp, int index,
+                       const char *chset,
+                       void (*print_func)(const char *, size_t, void *),
+                       void *ptr)
+{
+       const struct rfc822addr *addrs;
+
+       if (index < 0 || index >= rfcp->naddrs) return 0;
+
+       addrs=rfcp->addrs+index;
+
+       if (!addrs->tokens)
+               return 0;
+
+       return rfc822_display_name_int(rfcp, index, chset,
+                                      print_func, ptr);
+}
+
+char *rfc822_display_name_tobuf(const struct rfc822a *rfcp, int index,
+                               const char *chset)
+{
+       struct decode_unicode_s s;
+       char *p;
+
+       s.bufptr=0;
+       s.bufsize=1;
+
+       if (rfc822_display_name(rfcp, index, chset, save_unicode_text, &s) < 0)
+               return NULL;
+       s.bufptr=p=malloc(s.bufsize);
+       if (!p)
+               return (0);
+
+       s.bufsize=0;
+       if (rfc822_display_name(rfcp, index, chset, save_unicode_text, &s) < 0)
+       {
+               free(s.bufptr);
+               return (0);
+       }
+       save_unicode_text("", 1, &s);
+
+       return (p);
+}
+
+int rfc822_display_namelist(const struct rfc822a *rfcp,
+                           const char *chset,
+                           void (*print_func)(const char *, size_t, void *),
+                           void *ptr)
+{
+       int n;
+
+       for (n=0; n<rfcp->naddrs; n++)
+       {
+               if (rfcp->addrs[n].tokens)
+               {
+                       int err=rfc822_display_name(rfcp, n, chset,
+                                                   print_func, ptr);
+
+                       if (err < 0)
+                               return err;
+
+                       (*print_func)("\n", 1, ptr);
+               }
+       }
+       return 0;
+}
+
+int rfc822_display_addr_str(const char *tok,
+                           const char *chset,
+                           void (*print_func)(const char *, size_t, void *),
+                           void *ptr)
+{
+       const char *p;
+       const struct unicode_info *uiptr;
+
+       p=strchr(tok,'@');
+
+       if (!p)
+               p=tok;
+       else
+               ++p;
+
+       if (chset != NULL && (uiptr=unicode_find(chset)) != NULL)
+       {
+               int err;
+               char *utf8_ptr;
+
+               if (p > tok)
+                       (*print_func)(tok, p-tok, ptr);
+
+#if LIBIDN
+               err=idna_to_unicode_8z8z(p, &utf8_ptr, 0);
+               if (err != IDNA_SUCCESS)
+                       utf8_ptr=0;
+#else
+               utf8_ptr=0;
+#endif
+
+               if (utf8_ptr == 0)
+                       (*print_func)(p, strlen(p), ptr);
+               else
+               {
+                       char *q=unicode_xconvert(utf8_ptr, &unicode_UTF8,
+                                                uiptr);
+
+                       if (q)
+                       {
+                               (*print_func)(q, strlen(q), ptr);
+                               free(q);
+                       }
+                       else
+                       {
+                               (*print_func)(p, strlen(p), ptr);
+                       }
+                       free(utf8_ptr);
+               }
+       }
+       else
+       {
+               (*print_func)(tok, strlen(tok), ptr);
+       }
+       return 0;
+}
+
+int rfc822_display_addr(const struct rfc822a *rfcp, int index,
+                       const char *chset,
+                       void (*print_func)(const char *, size_t, void *),
+                       void *ptr)
+{
+       const struct rfc822addr *addrs;
+       char *tok;
+       int rc;
+
+       if (index < 0 || index >= rfcp->naddrs) return 0;
+
+       addrs=rfcp->addrs+index;
+
+       if (!addrs->tokens)
+               return 0;
+
+       tok=rfc822_gettok(addrs->tokens);
+
+       if (!tok)
+               return 0;
+
+       rc=rfc822_display_addr_str(tok, chset, print_func, ptr);
+       free(tok);
+       return rc;
+}
+
+int rfc2047_print_unicodeaddr(const struct rfc822a *a,
+                             const char *charset,
+                             void (*print_func)(char, void *),
+                             void (*print_separator)(const char *, void *),
+                             void *ptr)
+{
+       const char *sep=NULL;
+       int n;
+       const struct unicode_info *charsetu=unicode_find(charset);
+
+       for (n=0; n<a->naddrs; ++n)
+       {
+               struct decode_unicode_s nbuf;
+               const struct rfc822addr *addrs;
+               size_t i=0;
+               char *cpbuf;
+               int need_braces=0;
+
+               addrs=a->addrs+n;
+
+               nbuf.bufptr=0;
+               nbuf.bufsize=1;
+
+               if (rfc822_display_name_int(a, n, charset,
+                                           save_unicode_text, &nbuf) < 0)
+                       return -1;
+
+               nbuf.bufptr=malloc(nbuf.bufsize);
+               nbuf.bufsize=0;
+               if (!nbuf.bufptr)
+                       return -1;
+
+               if (rfc822_display_name_int(a, n, charset,
+                                           save_unicode_text, &nbuf) < 0)
+               {
+                       free(nbuf.bufptr);
+                       return -1;
+               }
+               nbuf.bufptr[nbuf.bufsize]=0;
+
+               if (addrs->tokens == 0)
+               {
+                       size_t i;
+
+                       if (nbuf.bufsize == 1) /* ; */
+                               sep=0;
+
+                       if (sep)
+                               (*print_separator)(sep, ptr);
+
+                       for (i=0; i<nbuf.bufsize; ++i)
+                               (*print_func)(nbuf.bufptr[i], ptr);
+                       free(nbuf.bufptr);
+                       if (nbuf.bufsize > 1)
+                               (*print_separator)(" ", ptr);
+                       sep=NULL;
+                       continue;
+               }
+               if (sep)
+                       (*print_separator)(sep, ptr);
+
+               if (!addrs->name)
+               {
+                       nbuf.bufsize=0;
+                       nbuf.bufptr[0]=0;
+               }
+
+               for (i=0; i<nbuf.bufsize; i++)
+                       if (strchr(RFC822_SPECIALS, nbuf.bufptr[i]))
+                               break;
+
+               if (!charsetu)
+               {
+                       const char *errmsg="\"(unknown character set)\"";
+
+                       while (*errmsg)
+                               (*print_func)(*errmsg++, ptr);
+                       need_braces=1;
+               }
+               else
+               {
+                       cpbuf=unicode_xconvert(nbuf.bufptr, &unicode_UTF8,
+                                              charsetu);
+
+                       if (!cpbuf)
+                       {
+                               free(nbuf.bufptr);
+                               return -1;
+                       }
+
+                       if (i < nbuf.bufsize)
+                       {
+                               (*print_func)('"', ptr);
+
+                               for (i=0; cpbuf[i]; ++i)
+                               {
+                                       if (cpbuf[i] == '\\' ||
+                                           cpbuf[i] == '"')
+                                               (*print_func)('\\', ptr);
+                                       (*print_func)(cpbuf[i], ptr);
+                               }
+                               (*print_func)('"', ptr);
+                               need_braces=1;
+                       }
+                        else
+                        {
+                                for (i=0; cpbuf[i]; ++i)
+                               {
+                                       need_braces=1;
+                                        (*print_func)(cpbuf[i], ptr);
+                               }
+                        }
+
+                       free(cpbuf);
+               }
+               free(nbuf.bufptr);
+
+               if (need_braces)
+               {
+                       (*print_func)(' ', ptr);
+                       (*print_func)('<', ptr);
+               }
+
+               nbuf.bufptr=0;
+               nbuf.bufsize=1;
+
+               if (rfc822_display_addr(a, n, charset,
+                                       save_unicode_text, &nbuf) < 0)
+                       return -1;
+
+               nbuf.bufptr=malloc(nbuf.bufsize);
+               nbuf.bufsize=0;
+               if (!nbuf.bufptr)
+                       return -1;
+
+               if (rfc822_display_addr(a, n, charset,
+                                       save_unicode_text, &nbuf) < 0)
+               {
+                       free(nbuf.bufptr);
+                       return -1;
+               }
+               for (i=0; i<nbuf.bufsize; i++)
+                       (*print_func)(nbuf.bufptr[i], ptr);
+
+               free(nbuf.bufptr);
+
+               if (need_braces)
+                       (*print_func)('>', ptr);
+               sep=", ";
+       }
+
+       return 0;
+}
+
+static int rfc2047_print_unicode_addrstr(const char *addrheader,
+                                        const char *charset,
+                                        void (*print_func)(char, void *),
+                                        void (*print_separator)(const char *, void *),
+                                        void (*err_func)(const char *, int, void *),
+                                        void *ptr)
+{
+       struct rfc822t *t;
+       struct rfc822a *a;
+       int rc;
+
+       t=rfc822t_alloc_new(addrheader, err_func, ptr);
+
+       if (!t)
+               return -1;
+
+       a=rfc822a_alloc(t);
+
+       if (!a)
+       {
+               rfc822t_free(t);
+               return -1;
+       }
+       rc=rfc2047_print_unicodeaddr(a, charset, print_func, print_separator,
+                                    ptr);
+       rfc822a_free(a);
+       rfc822t_free(t);
+       return (rc);
+}
+
+struct rfc822_display_hdrvalue_s {
+
+       const struct unicode_info *u;
+       void (*display_func)(const char *, size_t, void *);
+       void *ptr;
+};
+
+static void rfc822_display_hdrvalue_print_func(char c, void *ptr)
+{
+       struct rfc822_display_hdrvalue_s *s=
+               (struct rfc822_display_hdrvalue_s *)ptr;
+
+       (*s->display_func)(&c, 1, s->ptr);
+}
+
+static void rfc822_display_hdrvalue_print_separator(const char *cp, void *ptr)
+{
+       struct rfc822_display_hdrvalue_s *s=
+               (struct rfc822_display_hdrvalue_s *)ptr;
+
+       (*s->display_func)(cp, strlen(cp), s->ptr);
+       (*s->display_func)("", 0, s->ptr); /* Signal wrap point */
+}
+
+int rfc822_display_hdrvalue(const char *hdrname,
+                           const char *hdrvalue,
+                           const char *charset,
+                           void (*display_func)(const char *, size_t,
+                                                void *),
+                           void (*err_func)(const char *, int, void *),
+                           void *ptr)
+{
+       struct rfc822_display_hdrvalue_s s;
+
+       s.display_func=display_func;
+       s.ptr=ptr;
+
+       if (rfc822hdr_is_addr(hdrname))
+       {
+               return rfc2047_print_unicode_addrstr(hdrvalue,
+                                                    charset,
+                                                    rfc822_display_hdrvalue_print_func,
+                                                    rfc822_display_hdrvalue_print_separator,
+                                                    NULL,
+                                                    &s);
+       }
+
+       s.u=unicode_find(charset);
+
+       if (!s.u)
+       {
+               unknown_charset(charset, display_func, ptr);
+               return (0);
+       }
+
+       return rfc2047_decode_unicode(hdrvalue, s.u, display_func, ptr);
+}
+
+struct rfc822_display_hdrvalue_tobuf_s {
+       void (*orig_err_func)(const char *, int, void *);
+       void *orig_ptr;
+
+       size_t cnt;
+       char *buf;
+};
+
+static void rfc822_display_hdrvalue_tobuf_cnt(const char *ptr, size_t cnt,
+                                             void *s)
+{
+       ((struct rfc822_display_hdrvalue_tobuf_s *)s)->cnt += cnt;
+}
+
+static void rfc822_display_hdrvalue_tobuf_save(const char *ptr, size_t cnt,
+                                              void *s)
+{
+       if (cnt)
+               memcpy(((struct rfc822_display_hdrvalue_tobuf_s *)s)->buf,
+                      ptr, cnt);
+
+       ((struct rfc822_display_hdrvalue_tobuf_s *)s)->buf += cnt;
+}
+
+static void rfc822_display_hdrvalue_tobuf_errfunc(const char *ptr, int index,
+                                                 void *s)
+{
+       void (*f)(const char *, int, void *)=
+               ((struct rfc822_display_hdrvalue_tobuf_s *)s)->orig_err_func;
+
+       if (f)
+               f(ptr, index,
+                 ((struct rfc822_display_hdrvalue_tobuf_s *)s)->orig_ptr);
+}
+
+char *rfc822_display_addr_tobuf(const struct rfc822a *rfcp, int index,
+                               const char *chset)
+{
+       struct rfc822_display_hdrvalue_tobuf_s nbuf;
+       int errcode;
+       char *ptr;
+
+       nbuf.buf=0;
+       nbuf.cnt=1;
+
+       errcode=rfc822_display_addr(rfcp, index, chset,
+                                   rfc822_display_hdrvalue_tobuf_cnt, &nbuf);
+
+       if (errcode < 0)
+               return NULL;
+
+       ptr=nbuf.buf=malloc(nbuf.cnt);
+       nbuf.cnt=0;
+       if (!ptr)
+               return NULL;
+
+       errcode=rfc822_display_addr(rfcp, index, chset,
+                                   rfc822_display_hdrvalue_tobuf_save, &nbuf);
+
+       if (errcode < 0)
+       {
+               free(nbuf.buf);
+               return NULL;
+       }
+       *nbuf.buf=0;
+       return ptr;
+}
+
+char *rfc822_display_hdrvalue_tobuf(const char *hdrname,
+                                   const char *hdrvalue,
+                                   const char *charset,
+                                   void (*err_func)(const char *, int,
+                                                    void *),
+                                   void *ptr)
+{
+       struct rfc822_display_hdrvalue_tobuf_s s;
+       int errcode;
+       char *bufptr;
+
+       s.orig_err_func=err_func;
+       s.orig_ptr=ptr;
+       s.cnt=1;
+
+       errcode=rfc822_display_hdrvalue(hdrname, hdrvalue, charset,
+                                       rfc822_display_hdrvalue_tobuf_cnt,
+                                       rfc822_display_hdrvalue_tobuf_errfunc,
+                                       &s);
+
+       if (errcode < 0)
+               return NULL;
+
+       bufptr=s.buf=malloc(s.cnt);
+
+       if (!bufptr)
+               return NULL;
+
+       errcode=rfc822_display_hdrvalue(hdrname, hdrvalue, charset,
+                                       rfc822_display_hdrvalue_tobuf_save,
+                                       rfc822_display_hdrvalue_tobuf_errfunc,
+                                       &s);
+       if (errcode)
+       {
+               free(bufptr);
+               return NULL;
+       }
+       *s.buf=0;
+       return bufptr;
+}
+
+char *rfc822_display_addr_str_tobuf(const char *tok, const char *chset)
+{
+       struct rfc822_display_hdrvalue_tobuf_s s;
+       int errcode;
+       char *bufptr;
+
+       s.cnt=1;
+
+       errcode=rfc822_display_addr_str(tok, chset,
+                                       rfc822_display_hdrvalue_tobuf_cnt,
+                                       &s);
+
+       if (errcode < 0)
+               return NULL;
+
+       bufptr=s.buf=malloc(s.cnt);
+
+       if (!bufptr)
+               return NULL;
+
+       errcode=rfc822_display_addr_str(tok, chset,
+                                       rfc822_display_hdrvalue_tobuf_save,
+                                       &s);
+       if (errcode < 0)
+       {
+               free(bufptr);
+               return NULL;
+       }
+       *s.buf=0;
+       return bufptr;
+}
+
+
+static const char xdigit[]="0123456789ABCDEFabcdef";
+
+static const unsigned char decode64tab[]={
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
+       52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0, 99,  0,  0,
+        0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+       15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
+        0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+       41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
+};
+
+static int nyb(int c)
+{
+       const char      *p;
+       int n;
+
+       p=strchr(xdigit, c);
+
+       if (!p)
+               return 0;
+
+       n=p-xdigit;
+
+       if (n > 15)
+               n -= 6;
+
+       return n;
+}
+
+static size_t decodebase64(const char *ptr, size_t cnt,
+                          char *dec_buf)
+{
+       size_t  i, j;
+       char    a,b,c;
+       size_t  k;
+
+       i=cnt / 4;
+       i=i*4;
+       k=0;
+       for (j=0; j<i; j += 4)
+       {
+               int     w=decode64tab[(int)(unsigned char)ptr[j]];
+               int     x=decode64tab[(int)(unsigned char)ptr[j+1]];
+               int     y=decode64tab[(int)(unsigned char)ptr[j+2]];
+               int     z=decode64tab[(int)(unsigned char)ptr[j+3]];
+
+               a= (w << 2) | (x >> 4);
+               b= (x << 4) | (y >> 2);
+               c= (y << 6) | z;
+               dec_buf[k++]=a;
+               if ( ptr[j+2] != '=')
+                       dec_buf[k++]=b;
+               if ( ptr[j+3] != '=')
+                       dec_buf[k++]=c;
+       }
+       return (k);
+}
+
+
+static ssize_t rfc822_decode_rfc2047_atom(const char *str,
+                                         size_t cnt,
+
+                                         void (*callback)(const char *,
+                                                          const char *,
+                                                          const char *,
+                                                          size_t,
+                                                          void *),
+                                         void *ptr)
+{
+       const char *chset_str;
+       const char *enc_str;
+       const char *content_str;
+
+       char *chset;
+       char *lang;
+
+       char *content;
+
+       size_t i;
+       size_t j;
+       size_t k;
+
+       size_t content_len;
+
+       if (cnt < 2 || str[0] != '=' || str[1] != '?')
+               return 0;
+
+       chset_str=str+2;
+
+       for (i=2; i<cnt; i++)
+               if (str[i] == '?')
+                       break;
+
+       if (i >= cnt)
+               return 0;
+
+       enc_str= str + ++i;
+
+       for (; i < cnt; i++)
+               if (str[i] == '?')
+                       break;
+
+       if (i >= cnt)
+               return 0;
+
+       content_str= str + ++i;
+
+       while (1)
+       {
+               if (cnt-i < 2)
+                       return 0;
+
+               if (str[i] == '?' && str[i+1] == '=')
+                       break;
+               ++i;
+       }
+
+       for (j=0; chset_str[j] != '?'; ++j)
+               ;
+
+       chset=malloc(j+1);
+
+       if (!chset)
+               return -1;
+
+       memcpy(chset, chset_str, j);
+       chset[j]=0;
+
+       lang=strchr(chset, '*');  /* RFC 2231 */
+
+       if (lang)
+               *lang++ = 0;
+       else
+               lang="";
+
+       content_len=str + i - content_str;
+
+       content=malloc(content_len+1);
+
+       if (!content)
+       {
+               free(chset);
+               return -1;
+       }
+
+       switch (*enc_str) {
+       case 'q':
+       case 'Q':
+
+               k=0;
+               for (j=0; j<content_len; j++)
+               {
+                       char c;
+
+                       if (content_str[j] == '=' && i-j >= 3)
+                       {
+                               content[k]=(char)(nyb(content_str[j+1])*16 +
+                                                 nyb(content_str[j+2]));
+                               ++k;
+                               j += 2;
+                               continue;
+                       }
+
+                       c=content_str[j];
+                       if (c == '_')
+                               c=' ';
+                       content[k]=c;
+                       ++k;
+               }
+               break;
+
+       case 'b':
+       case 'B':
+               k=decodebase64(content_str, content_len, content);
+               break;
+       default:
+               free(content);
+               free(chset);
+               return (0);
+       }
+
+       if (callback)
+               (*callback)(chset, lang, content, k, ptr);
+       free(content);
+       free(chset);
+       return i + 2;
+}
+
+int rfc2047_decoder(const char *text,
+                   void (*callback)(const char *chset,
+                                    const char *lang,
+                                    const char *content,
+                                    size_t cnt,
+                                    void *dummy),
+                   void *ptr)
+{
+       ssize_t rc;
+
+       while (text && *text)
+       {
+               size_t i;
+
+               for (i=0; text[i]; i++)
+               {
+                       if (text[i] == '=' && text[i+1] == '?')
+                               break;
+               }
+
+               if (i)
+                       (*callback)(unicode_ISO8859_1.chset, "", text, i, ptr);
+
+               text += i;
+
+               if (!*text)
+                       continue;
+
+               rc=rfc822_decode_rfc2047_atom(text, strlen(text),
+                                             callback, ptr);
+
+               if (rc < 0)
+                       return -1;
+
+               if (rc == 0)
+               {
+                       (*callback)(unicode_ISO8859_1.chset, "", text, 2, ptr);
+                       text += 2;
+                       continue;
+               }
+
+               text += rc;
+
+               for (i=0; text[i]; i++)
+               {
+                       if (strchr(" \t\r\n", text[i]) == NULL)
+                               break;
+               }
+
+               if (text[i] != '=' || text[i+1] != '?')
+                       continue;
+
+               rc=rfc822_decode_rfc2047_atom(text+i, strlen(text+i), NULL,
+                                             NULL);
+
+               if (rc < 0)
+                       return -1;
+               if (rc > 0)
+                       text += i;
+       }
+
+       return 0;
+}
+
+static int rfc2047_decode_unicode(const char *text,
+                                 const struct unicode_info *u,
+                                 void (*callback)(const char *, size_t,
+                                                  void *),
+                                 void *ptr)
+{
+       struct rfc822_display_name_s s;
+
+       s.u=u;
+       s.print_func=callback;
+       s.ptr=ptr;
+
+       return rfc2047_decoder(text, rfc822_display_addr_cb, &s);
+}