Back to index

courier  0.68.2
unicode.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2000-2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 **
00005 */
00006 
00007 #include      "unicode_config.h"
00008 #include      "unicode.h"
00009 #include      "../rfc822/rfc822hdr.h"
00010 #include      <string.h>
00011 #include      <ctype.h>
00012 #include      <stdlib.h>
00013 #include      <iconv.h>
00014 #include      <errno.h>
00015 #if    HAVE_LOCALE_H
00016 #if    HAVE_SETLOCALE
00017 #include      <locale.h>
00018 #if    USE_LIBCHARSET
00019 #if    HAVE_LOCALCHARSET_H
00020 #include      <localcharset.h>
00021 #elif  HAVE_LIBCHARSET_H
00022 #include      <libcharset.h>
00023 #endif /* HAVE_LOCALCHARSET_H */
00024 #elif  HAVE_LANGINFO_CODESET
00025 #include      <langinfo.h>
00026 #endif /* USE_LIBCHARSET */
00027 #endif /* HAVE_SETLOCALE */
00028 #endif /* HAVE_LOCALE_H */
00029 
00030 static char default_chset_buf[32];
00031 
00032 static void init_default_chset()
00033 {
00034        const char *old_locale=NULL;
00035        const char *chset=NULL;
00036        char *locale_cpy=NULL;
00037        char buf[sizeof(default_chset_buf)];
00038 
00039        chset=getenv("MM_CHARSET");
00040 
00041        if (chset == NULL)
00042               chset=getenv("CHARSET");
00043 
00044        if (chset == NULL)
00045        {
00046 #if    HAVE_LOCALE_H
00047 #if    HAVE_SETLOCALE
00048               old_locale=setlocale(LC_ALL, "");
00049               locale_cpy=old_locale ? strdup(old_locale):NULL;
00050 #if    USE_LIBCHARSET
00051               chset = locale_charset();
00052 #elif  HAVE_LANGINFO_CODESET
00053               chset=nl_langinfo(CODESET);
00054 #endif
00055 #endif
00056 #endif
00057        }
00058 
00059        memset(buf, 0, sizeof(buf));
00060 
00061        if (chset &&
00062 
00063            /* Map GNU libc iconv oddity to us-ascii */
00064 
00065            (strcmp(chset, "ANSI_X3.4") == 0 ||
00066             strncmp(chset, "ANSI_X3.4-", 10) == 0))
00067               chset="US-ASCII";
00068 
00069        if (chset)
00070        {
00071               strncat(buf, chset, sizeof(buf)-1);
00072        }
00073        else
00074        {
00075               const char *p=getenv("LANG");
00076 
00077               /* LANG is xx_yy.CHARSET@modifier */
00078 
00079               if (p && *p && (p=strchr(p, '.')) != NULL)
00080               {
00081                      const char *q=strchr(++p, '@');
00082 
00083                      if (!q)
00084                             q=p+strlen(p);
00085 
00086                      if (q-p >= sizeof(buf)-1)
00087                             q=p+sizeof(buf)-1;
00088 
00089                      memcpy(buf, p, q-p);
00090                      buf[q-p]=0;
00091               }
00092               else
00093                      strcpy(buf, "US-ASCII");
00094        }
00095 
00096        memcpy(default_chset_buf, buf, sizeof(buf));
00097 
00098 #if    HAVE_LOCALE_H
00099 #if    HAVE_SETLOCALE
00100        if (locale_cpy)
00101        {
00102               setlocale(LC_ALL, locale_cpy);
00103               free(locale_cpy);
00104        }
00105 #endif
00106 #endif
00107 
00108 }
00109 
00110 const char *unicode_default_chset()
00111 {
00112        if (default_chset_buf[0] == 0)
00113               init_default_chset();
00114 
00115        return default_chset_buf;
00116 }
00117 
00118 
00119 /*****************************************************************************/
00120 
00121 const char libmail_u_ucs4_native[]=
00122 #if WORDS_BIGENDIAN
00123        "UCS-4BE"
00124 #else
00125        "UCS-4LE"
00126 #endif
00127        ;
00128 
00129 const char libmail_u_ucs2_native[]=
00130 #if WORDS_BIGENDIAN
00131        "UCS-2BE"
00132 #else
00133        "UCS-2LE"
00134 #endif
00135        ;
00136 
00137 /* A stack of conversion modules */
00138 
00139 struct libmail_u_convert_hdr {
00140 
00141        int (*convert_handler)(void *ptr,
00142                             const char *text, size_t cnt);
00143        int (*deinit_handler)(void *ptr, int *errptr);
00144        void *ptr;
00145 
00146        struct libmail_u_convert_hdr *next;
00147 };
00148 
00149 /* Decoding table for modified UTF7-encoding as used in imap */
00150 
00151 static const char mbase64_lookup[]={
00152        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00153        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00154        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,63,-1,-1,-1,
00155        52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
00156        -1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
00157        15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
00158        -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
00159        41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
00160        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00161        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00162        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00163        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00164        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00165        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00166        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
00167        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
00168 
00169 static const char mbase64[]=
00170        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
00171 
00172 /*
00173 ** Conversion wrapper for converting to modified-utf7 IMAP encoding.
00174 **
00175 ** This is done by converting to UCS2, then stacking on a module that
00176 ** takes that and converts UCS2 to modified-UTF7.
00177 **
00178 ** init_nottoimaputf7() returns an opaque stack for converting to ucs2.
00179 */
00180 
00181 static libmail_u_convert_handle_t
00182 init_nottoimaputf7(const char *src_chset,
00183                  const char *dst_chset,
00184                  int (*output_func)(const char *, size_t, void *),
00185                  void *convert_arg);
00186 
00187 /*
00188 ** The to modified UTF7 module
00189 */
00190 
00191 struct libmail_u_convert_toimaputf7 {
00192 
00193        struct libmail_u_convert_hdr hdr;
00194 
00195        /* Accumulated output buffer */
00196 
00197        char utf7encodebuf[1024];
00198        size_t utf7encodebuf_cnt;
00199 
00200        /* Accumulated bits for base64 encoding */
00201        uint32_t utf7bits;
00202 
00203        /* How many bits in utf7bits */
00204        uint16_t utf7bitcount;
00205 
00206        /* Flag: in base64mode */
00207        uint16_t utfmode;
00208 
00209        int errflag;
00210 
00211        /* Any extra characters that should be munged */
00212 
00213        char smapmunge[16];
00214 
00215        /* Remembered output function */
00216 
00217        int (*output_func)(const char *, size_t, void *);
00218 
00219        /* Remembered arg to the output function */
00220        void *convert_arg;
00221 };
00222 
00223 /* Macro - flush the output buffer */
00224 #define toimaputf7_encode_flush(p) do {                               \
00225               int rc;                                                 \
00226                                                                \
00227               rc=(*(p)->output_func)((p)->utf7encodebuf,              \
00228                                    (p)->utf7encodebuf_cnt,            \
00229                                    (p)->convert_arg);          \
00230               if (rc)                                                 \
00231                      return ((p)->errflag=(rc));               \
00232                                                                \
00233               (p)->utf7encodebuf_cnt=0;                        \
00234        } while (0)
00235 
00236 static int toimaputf7_encode_flushfinal(struct libmail_u_convert_toimaputf7 *p)
00237 {
00238        if (p->utf7encodebuf_cnt > 0)
00239               toimaputf7_encode_flush(p);
00240        return 0;
00241 }
00242 
00243 /* Macro - add one char to the output buffer */
00244 
00245 #define toimaputf7_encode_add(p,c) do {                               \
00246               if ((p)->utf7encodebuf_cnt >= sizeof((p)->utf7encodebuf)) \
00247                      toimaputf7_encode_flush((p));                    \
00248                                                                \
00249               (p)->utf7encodebuf[(p)->utf7encodebuf_cnt++]=(c);       \
00250        } while (0);
00251 
00252 static int deinit_toimaputf7(void *ptr, int *errptr);
00253 
00254 static int do_convert_toutf7(const char *text, size_t cnt, void *arg);
00255 static int convert_utf7_handler(void *ptr, const char *text, size_t cnt);
00256 
00257 /*
00258 ** Create a conversion module stack
00259 */
00260 
00261 libmail_u_convert_handle_t
00262 libmail_u_convert_init(const char *src_chset,
00263                      const char *dst_chset,
00264                      int (*output_func)(const char *, size_t, void *),
00265                      void *convert_arg)
00266 {
00267        struct libmail_u_convert_toimaputf7 *toutf7;
00268        libmail_u_convert_handle_t h;
00269        const char *smapmunge;
00270        size_t l=strlen(unicode_x_imap_modutf7);
00271 
00272        if (strncmp(dst_chset, unicode_x_imap_modutf7, l) == 0 &&
00273            (dst_chset[l] == 0 || dst_chset[l] == ' '))
00274        {
00275               smapmunge=dst_chset + l;
00276 
00277               if (*smapmunge)
00278                      ++smapmunge;
00279        }
00280        else
00281               return init_nottoimaputf7(src_chset, dst_chset,
00282                                         output_func,
00283                                         convert_arg);
00284 
00285        toutf7=malloc(sizeof(struct libmail_u_convert_toimaputf7));
00286 
00287        if (!toutf7)
00288               return NULL;
00289 
00290        memset(toutf7, 0, sizeof(*toutf7));
00291 
00292        h=init_nottoimaputf7(src_chset, libmail_u_ucs2_native,
00293                           do_convert_toutf7, toutf7);
00294        if (!h)
00295        {
00296               free(toutf7);
00297               return (NULL);
00298        }
00299 
00300        toutf7->output_func=output_func;
00301        toutf7->convert_arg=convert_arg;
00302 
00303        strncat(toutf7->smapmunge, smapmunge, sizeof(toutf7->smapmunge)-1);
00304 
00305        toutf7->hdr.convert_handler=convert_utf7_handler;
00306        toutf7->hdr.deinit_handler=deinit_toimaputf7;
00307        toutf7->hdr.ptr=toutf7;
00308        toutf7->hdr.next=h;
00309        return &toutf7->hdr;
00310 }
00311 
00312 /* Passthrough to the wrapped stack */
00313 
00314 static int convert_utf7_handler(void *ptr, const char *text, size_t cnt)
00315 {
00316        struct libmail_u_convert_toimaputf7 *toutf7=
00317               (struct libmail_u_convert_toimaputf7 *)ptr;
00318 
00319        return (*toutf7->hdr.next->convert_handler)(toutf7->hdr.next->ptr,
00320                                               text, cnt);
00321 }
00322 
00323 static int utf7off(struct libmail_u_convert_toimaputf7 *toutf7)
00324 {
00325        if (!toutf7->utfmode)
00326               return 0;
00327        toutf7->utfmode=0;
00328 
00329        if (toutf7->utf7bitcount > 0)
00330               toimaputf7_encode_add(toutf7,
00331                                   mbase64[(toutf7->utf7bits
00332                                           << (6-toutf7->utf7bitcount))
00333                                          & 63]);
00334        toimaputf7_encode_add(toutf7, '-');
00335        return 0;
00336 }
00337 
00338 
00339 static int do_convert_toutf7(const char *text, size_t cnt, void *arg)
00340 {
00341        struct libmail_u_convert_toimaputf7 *toutf7=
00342               (struct libmail_u_convert_toimaputf7 *)arg;
00343 
00344        /* We better be getting UCS-2 here! */
00345 
00346        const uint16_t *utext=(const uint16_t *)text;
00347        cnt /= 2;
00348 
00349        while (cnt)
00350        {
00351               if (toutf7->errflag)
00352                      return toutf7->errflag;
00353 
00354               if (*utext >= 0x20 && *utext <= 0x7F
00355                   && strchr( toutf7->smapmunge, (char)*utext) == NULL)
00356 
00357                      /*
00358                        && (!toutf7->smapmunge || (*utext != '.' && *utext != '/' &&
00359                        *utext != '~' && *utext != ':')))
00360                        */
00361               {
00362                      if (utf7off(toutf7))
00363                             return toutf7->errflag;
00364 
00365                      toimaputf7_encode_add(toutf7, *utext);
00366 
00367                      if (*utext == '&')
00368                             toimaputf7_encode_add(toutf7, '-');
00369 
00370                      ++utext;
00371                      --cnt;
00372                      continue;
00373               }
00374 
00375               if (!toutf7->utfmode)
00376               {
00377                      toutf7->utfmode=1;
00378                      toutf7->utf7bitcount=0;
00379                      toimaputf7_encode_add(toutf7, '&');
00380                      continue;
00381               }
00382 
00383               toutf7->utf7bits = (toutf7->utf7bits << 16) |
00384                      (((uint32_t)*utext) & 0xFFFF);
00385               toutf7->utf7bitcount += 16;
00386 
00387               ++utext;
00388               --cnt;
00389 
00390               /* If there's at least 6 bits, output base64-encoded char */
00391 
00392               while (toutf7->utf7bitcount >= 6)
00393               {
00394                      uint32_t v;
00395                      int n;
00396 
00397                      if (toutf7->errflag)
00398                             return toutf7->errflag;
00399 
00400                      v=toutf7->utf7bits;
00401                      n=toutf7->utf7bitcount-6;
00402                      toutf7->utf7bitcount -= 6;
00403 
00404                      if (n > 0)
00405                             v >>= n;
00406 
00407                      toimaputf7_encode_add(toutf7, mbase64[v & 63]);
00408               }
00409        }
00410 
00411        return 0;
00412 }
00413 
00414 static int deinit_toimaputf7(void *ptr, int *errptr)
00415 {
00416        int rc;
00417 
00418        struct libmail_u_convert_toimaputf7 *toutf7=
00419               (struct libmail_u_convert_toimaputf7 *)ptr;
00420 
00421        /* Flush out the downstream stack */
00422        rc=(*toutf7->hdr.next->deinit_handler)(toutf7->hdr.next->ptr, errptr);
00423 
00424        /* Make sure we're out of modified base64 */
00425 
00426        if (rc == 0)
00427               rc=utf7off(toutf7);
00428 
00429        if (rc == 0 && toutf7->utf7encodebuf_cnt > 0)
00430               rc=toimaputf7_encode_flushfinal(toutf7);
00431                      
00432        free(toutf7);
00433        return rc;
00434 }
00435 
00436 /************/
00437 
00438 /*
00439 ** Convert from modified-utf7 IMAP encoding.
00440 **
00441 ** This module converts it to UCS-2, then this is attached to a stack that
00442 ** converts UCS-2 to the requested charset.
00443 */
00444 
00445 static libmail_u_convert_handle_t
00446 init_notfromimaputf7(const char *src_chset,
00447                    const char *dst_chset,
00448                    int (*output_func)(const char *, size_t, void *),
00449                    void *convert_arg);
00450 
00451 struct libmail_u_convert_fromimaputf7 {
00452 
00453        struct libmail_u_convert_hdr hdr;
00454 
00455        /* Accumulated UCS-2 stream */
00456        uint16_t convbuf[512];
00457        size_t convbuf_cnt;
00458 
00459        /* Accumulated base64 bits */
00460        uint32_t modbits;
00461 
00462        /* How many bits extracted from a base64 stream */
00463 
00464        short modcnt;
00465 
00466        /* Flag: seen the & */
00467        char seenamp;
00468 
00469        /* Flag: seen the &, and the next char wasn't - */
00470 
00471        char inmod;
00472        int errflag;
00473        int converr;
00474 };
00475 
00476 /* Flush the accumulated UCS-2 stream */
00477 
00478 #define convert_fromutf7_flush(p) do {                                \
00479               (p)->errflag=(*(p)->hdr.next->convert_handler)          \
00480                      ((p)->hdr.next->ptr,                      \
00481                       (const char *)(p)->convbuf,                     \
00482                       (p)->convbuf_cnt *                       \
00483                       sizeof((p)->convbuf[0]));                \
00484               (p)->convbuf_cnt=0;                              \
00485        } while (0)
00486 
00487 /* Accumulated a UCS-2 char */
00488 
00489 #define convert_fromutf7_add(p,c) do {                                \
00490               if ((p)->convbuf_cnt >=                                 \
00491                   sizeof((p)->convbuf)/sizeof((p)->convbuf[0]))       \
00492                      convert_fromutf7_flush((p));                     \
00493               (p)->convbuf[(p)->convbuf_cnt++]=(c);                   \
00494        } while (0)
00495 
00496 
00497 static int convert_fromutf7(void *ptr,
00498                          const char *text, size_t cnt);
00499 static int deinit_fromutf7(void *ptr, int *errptr);
00500 
00501 static libmail_u_convert_handle_t
00502 init_nottoimaputf7(const char *src_chset,
00503                  const char *dst_chset,
00504                  int (*output_func)(const char *, size_t, void *),
00505                  void *convert_arg)
00506 {
00507        struct libmail_u_convert_fromimaputf7 *fromutf7;
00508        libmail_u_convert_handle_t h;
00509        size_t l=strlen(unicode_x_imap_modutf7);
00510 
00511        if (strncmp(src_chset, unicode_x_imap_modutf7, l) == 0 &&
00512            (src_chset[l] == 0 || src_chset[l] == ' '))
00513               ;
00514        else
00515               return init_notfromimaputf7(src_chset, dst_chset,
00516                                        output_func,
00517                                        convert_arg);
00518 
00519        fromutf7=(struct libmail_u_convert_fromimaputf7 *)
00520               malloc(sizeof(struct libmail_u_convert_fromimaputf7));
00521 
00522        if (!fromutf7)
00523               return NULL;
00524 
00525        memset(fromutf7, 0, sizeof(*fromutf7));
00526 
00527        /* Create a stack for converting UCS-2 to the dest charset */
00528 
00529        h=init_notfromimaputf7(libmail_u_ucs2_native, dst_chset,
00530                             output_func, convert_arg);
00531 
00532        if (!h)
00533        {
00534               free(fromutf7);
00535               return (NULL);
00536        }
00537 
00538        fromutf7->hdr.next=h;
00539        fromutf7->hdr.convert_handler=convert_fromutf7;
00540        fromutf7->hdr.deinit_handler=deinit_fromutf7;
00541        fromutf7->hdr.ptr=fromutf7;
00542        return &fromutf7->hdr;
00543 }
00544 
00545 static int convert_fromutf7(void *ptr,
00546                          const char *text, size_t cnt)
00547 {
00548        struct libmail_u_convert_fromimaputf7 *fromutf7=
00549               (struct libmail_u_convert_fromimaputf7 *)ptr;
00550        int bits;
00551 
00552        while (cnt)
00553        {
00554               if (fromutf7->errflag)
00555                      return fromutf7->errflag;
00556 
00557               if (!fromutf7->seenamp && *text == '&')
00558               {
00559                      fromutf7->seenamp=1;
00560                      fromutf7->inmod=0;
00561                      fromutf7->modcnt=0;
00562                      ++text;
00563                      --cnt;
00564                      continue;
00565               }
00566 
00567               if (fromutf7->seenamp)
00568               {
00569                      if (*text == '-')
00570                      {
00571                             convert_fromutf7_add(fromutf7, '&');
00572                             ++text;
00573                             --cnt;
00574                             fromutf7->seenamp=0;
00575                             continue;
00576                      }
00577                      fromutf7->seenamp=0;
00578                      fromutf7->inmod=1;
00579               }
00580 
00581               if (!fromutf7->inmod)
00582               {
00583                      /* Not in the base64 encoded stream */
00584 
00585                      convert_fromutf7_add(fromutf7,
00586                                         ((uint16_t)*text) & 0xFFFF);
00587                      ++text;
00588                      --cnt;
00589                      continue;
00590               }
00591 
00592               if (*text == '-')
00593               {
00594                      /* End of the base64 encoded stream */
00595                      fromutf7->inmod=0;
00596                      ++text;
00597                      --cnt;
00598                      continue;
00599               }
00600 
00601               /* Got 6 more bits */
00602 
00603               bits=mbase64_lookup[(unsigned char)*text];
00604 
00605               ++text;
00606               --cnt;
00607 
00608               if (bits < 0)
00609               {
00610                      errno=EILSEQ;
00611                      return fromutf7->errflag=-1;
00612               }
00613 
00614               fromutf7->modbits = (fromutf7->modbits << 6) | bits;
00615               fromutf7->modcnt += 6;
00616 
00617               if (fromutf7->modcnt >= 16)
00618               {
00619                      /* Got a UCS-2 char */
00620 
00621                      int shiftcnt=fromutf7->modcnt - 16;
00622                      uint32_t v=fromutf7->modbits;
00623 
00624                      if (shiftcnt)
00625                             v >>= shiftcnt;
00626 
00627                      fromutf7->modcnt -= 16;
00628 
00629                      convert_fromutf7_add(fromutf7, v);
00630               }
00631        }
00632        return 0;
00633 }
00634 
00635 static int deinit_fromutf7(void *ptr, int *errptr)
00636 {
00637        struct libmail_u_convert_fromimaputf7 *fromutf7=
00638               (struct libmail_u_convert_fromimaputf7 *)ptr;
00639        int rc;
00640 
00641        if (fromutf7->seenamp || fromutf7->inmod)
00642        {
00643               if (fromutf7->errflag == 0)
00644               {
00645                      fromutf7->errflag= -1;
00646                      errno=EILSEQ;
00647               }
00648        }
00649 
00650        if (fromutf7->convbuf_cnt)
00651               convert_fromutf7_flush(fromutf7);
00652 
00653        rc=fromutf7->hdr.next->deinit_handler(fromutf7->hdr.next->ptr, errptr);
00654 
00655        if (fromutf7->errflag && rc == 0)
00656               rc=fromutf7->errflag;
00657 
00658        if (errptr && fromutf7->converr)
00659               *errptr=1;
00660 
00661        free(fromutf7);
00662        return rc;
00663 }
00664 
00665 /************/
00666 
00667 /* A real conversion module, via iconv */
00668 
00669 struct libmail_u_convert_iconv {
00670 
00671        struct libmail_u_convert_hdr hdr;
00672 
00673        iconv_t h;
00674        int errflag; /* Accumulated errors */
00675 
00676        int (*output_func)(const char *, size_t, void *);
00677        void *convert_arg;
00678 
00679        char buffer[1024]; /* Input buffer */
00680        size_t bufcnt; /* Accumulated input in buffer */
00681        char skipcnt; /* Skip this many bytes upon encountering EILSEQ */
00682        char skipleft; /* How many bytes are currently left to skip */
00683        char converr; /* Flag - an EILSEQ was encountered */
00684 } ;
00685 
00686 static int init_iconv(struct libmail_u_convert_iconv *h,
00687                     const char *src_chset,
00688                     const char *dst_chset,
00689                     int (*output_func)(const char *, size_t, void *),
00690                     void *convert_arg);
00691 
00692 static libmail_u_convert_handle_t
00693 init_notfromimaputf7(const char *src_chset,
00694                    const char *dst_chset,
00695                    int (*output_func)(const char *, size_t, void *),
00696                    void *convert_arg)
00697 {
00698 
00699 
00700        struct libmail_u_convert_iconv *h=
00701               malloc(sizeof(struct libmail_u_convert_iconv));
00702 
00703        if (!h)
00704               return NULL;
00705 
00706        memset(h, 0, sizeof(*h));
00707 
00708        if (init_iconv(h, src_chset, dst_chset, output_func, convert_arg))
00709        {
00710               free(h);
00711               return NULL;
00712        }
00713        return &h->hdr;
00714 }
00715 
00716 /* Run the stack */
00717 
00718 int libmail_u_convert(libmail_u_convert_handle_t h,
00719                     const char *text, size_t cnt)
00720 {
00721        return (*h->convert_handler)(h->ptr, text, cnt);
00722 }
00723 
00724 /* Destroy the stack */
00725 
00726 int libmail_u_convert_deinit(libmail_u_convert_handle_t h, int *errptr)
00727 {
00728        return (*h->deinit_handler)(h, errptr);
00729 }
00730 
00731 static int deinit_iconv(void *ptr, int *errptr);
00732 static int convert_iconv(void *ptr,
00733                       const char *text, size_t cnt);
00734 
00735 /* Initialize a single conversion module, in the stack */
00736 
00737 static int init_iconv(struct libmail_u_convert_iconv *h,
00738                     const char *src_chset,
00739                     const char *dst_chset,
00740                     int (*output_func)(const char *, size_t, void *),
00741                     void *convert_arg)
00742 {
00743        if ((h->h=iconv_open(dst_chset, src_chset)) == (iconv_t)-1)
00744               return -1;
00745 
00746        h->hdr.convert_handler=convert_iconv;
00747        h->hdr.deinit_handler=deinit_iconv;
00748        h->hdr.ptr=h;
00749 
00750        h->output_func=output_func;
00751        h->convert_arg=convert_arg;
00752 
00753        /* Heuristically determine how many octets to skip upon an EILSEQ */
00754 
00755        h->skipcnt=1;
00756        switch (src_chset[0]) {
00757        case 'u':
00758        case 'U':
00759               switch (src_chset[1]) {
00760               case 'c':
00761               case 'C':
00762                      switch (src_chset[2]) {
00763                      case 's':
00764                      case 'S':
00765                             if (src_chset[3] == '-')
00766                                    switch (src_chset[4]) {
00767                                    case '4':
00768                                           /* UCS-4 */
00769                                           h->skipcnt=4;
00770                                           break;
00771                                    case '2':
00772                                           /* UCS-2 */
00773                                           h->skipcnt=2;
00774                                           break;
00775                                    }
00776                      }
00777                      break;
00778               case 't':
00779               case 'T':
00780                      switch (src_chset[2]) {
00781                      case 'f':
00782                      case 'F':
00783                             if (src_chset[3] == '-')
00784                                    switch (src_chset[4]) {
00785                                    case '3':
00786                                           /* UTF-32 */
00787                                           h->skipcnt=4;
00788                                           break;
00789                                    case '1':
00790                                           /* UTF-16 */
00791                                           h->skipcnt=2;
00792                                           break;
00793                                    }
00794                      }
00795               }
00796        }
00797                                    
00798        return 0;
00799 }
00800 
00801 static void convert_flush(struct libmail_u_convert_iconv *);
00802 static void convert_flush_iconv(struct libmail_u_convert_iconv *, const char **,
00803                             size_t *);
00804 
00805 /*
00806 ** iconv conversion module. Accumulate input in an input buffer. When the
00807 ** input buffer is full, invoke convert_flush().
00808 */
00809 
00810 static int convert_iconv(void *ptr,
00811                       const char *text, size_t cnt)
00812 {
00813        struct libmail_u_convert_iconv *h=(struct libmail_u_convert_iconv *)ptr;
00814 
00815        while (cnt && h->errflag == 0)
00816        {
00817               if (h->bufcnt >= sizeof(h->buffer)-1)
00818               {
00819                      convert_flush(h);
00820 
00821                      if (h->errflag)
00822                             break;
00823               }
00824 
00825               h->buffer[h->bufcnt++]= *text++;
00826               --cnt;
00827        }
00828 
00829        return h->errflag;
00830 }
00831 
00832 /*
00833 ** Finish an iconv conversion module. Invoke convert_flush() to flush any
00834 ** buffered input. Invoke convert_flush_iconv() to return state to the initial
00835 ** conversion state.
00836 */
00837 
00838 static int deinit_iconv(void *ptr, int *errptr)
00839 {
00840        int rc;
00841        int converr;
00842        struct libmail_u_convert_iconv *h=(struct libmail_u_convert_iconv *)ptr;
00843        libmail_u_convert_handle_t next;
00844 
00845        if (h->errflag == 0)
00846               convert_flush(h);
00847 
00848        if (h->bufcnt && h->errflag == 0)
00849               h->converr=1;
00850 
00851        if (h->errflag == 0)
00852               convert_flush_iconv(h, NULL, NULL);
00853 
00854        rc=h->errflag;
00855        converr=h->converr != 0;
00856        iconv_close(h->h);
00857        next=h->hdr.next;
00858        free(h);
00859        if (errptr)
00860               *errptr=converr;
00861 
00862        /* If there's another module in the stack, clean that up */
00863 
00864        if (next)
00865        {
00866               int converrnext;
00867               int rcnext=libmail_u_convert_deinit(next, &converrnext);
00868 
00869               if (converrnext && errptr && *errptr == 0)
00870                      *errptr=converr;
00871 
00872               if (rcnext && rc == 0)
00873                      rc=rcnext;
00874        }
00875        return rc;
00876 }
00877 
00878 /*
00879 ** Invoke convert_flush_iconv() to flush the input buffer. If there's
00880 ** unconverted text remaining, reposition it at the beginning of the input
00881 ** buffer.
00882 */
00883 
00884 static void convert_flush(struct libmail_u_convert_iconv *h)
00885 {
00886        const char *p;
00887        size_t n;
00888 
00889        if (h->bufcnt == 0 || h->errflag)
00890               return;
00891 
00892        p=h->buffer;
00893        n=h->bufcnt;
00894 
00895        convert_flush_iconv(h, &p, &n);
00896 
00897        if (h->errflag)
00898               return;
00899 
00900        if (h->bufcnt == n)
00901               n=0; /* Unexpected error, dunno what to do, punt */
00902 
00903        h->bufcnt=0;
00904 
00905        while (n)
00906        {
00907               h->buffer[h->bufcnt]= *p;
00908 
00909               ++h->bufcnt;
00910               ++p;
00911               --n;
00912        }
00913 }
00914 
00915 /*
00916 ** Convert text via iconv.
00917 */
00918 
00919 static void convert_flush_iconv(struct libmail_u_convert_iconv *h,
00920                             const char **inbuf, size_t *inbytesleft)
00921 {
00922        int save_errno;
00923 
00924        while (1)
00925        {
00926               char outbuf[1024];
00927               char *outp;
00928               size_t outleft;
00929               size_t n;
00930               size_t origin=0;
00931 
00932               if (inbytesleft)
00933               {
00934                      if ((origin=*inbytesleft) == 0)
00935                             return;
00936 
00937                      if (inbuf && h->skipleft && origin)
00938                      {
00939                             /* Skipping after an EILSEQ */
00940 
00941                             --h->skipleft;
00942                             --*inbytesleft;
00943                             ++*inbuf;
00944                             continue;
00945                      }
00946 
00947               }
00948 
00949               if (h->errflag)
00950               {
00951                      /* Quietly eat everything after a previous error */
00952 
00953                      if (inbytesleft)
00954                             *inbytesleft=0;
00955 
00956                      return;
00957               }
00958 
00959               outp=outbuf;
00960               outleft=sizeof(outbuf);
00961 
00962               n=iconv(h->h, (char **)inbuf, inbytesleft, &outp, &outleft);
00963 
00964               save_errno=errno;
00965 
00966               /* Anything produced by iconv() gets pushed down the stack */
00967 
00968               if (outp > outbuf)
00969               {
00970                      int rc=(*h->output_func)(outbuf, outp-outbuf,
00971                                            h->convert_arg);
00972                      if (rc)
00973                      {
00974                             h->errflag=rc;
00975                             return;
00976                      }
00977               }
00978 
00979               if (n != (size_t)-1)
00980               {
00981                      /* iconv(3) reason #2 */
00982 
00983                      break;
00984               }
00985 
00986               if (inbytesleft == 0)
00987               {
00988                      /*
00989                      ** An error when generating the shift sequence to
00990                      ** return to the initial state. We don't know what to
00991                      ** do, now.
00992                      */
00993 
00994                      errno=EINVAL;
00995                      h->errflag= -1;
00996                      return;
00997               }
00998 
00999               /*
01000               ** convert_flush() gets invoked when the 1024 char input buffer
01001               ** fills or to convert input that has been buffered when
01002               ** convert_chset_end() gets invoked.
01003               **
01004               ** A return code of EINVAL from iconv() is iconv() encountering
01005               ** an incomplete multibyte sequence.
01006               **
01007               ** If iconv() failed without consuming any input:
01008               **
01009               ** - iconv(3) reason #1, EILSEQ, invalid multibyte sequence
01010               ** that starts at the beginning of the string we wish to
01011               ** convert. Discard one character, and try again.
01012               **
01013               ** - iconv(3) reason #3, EINVAL, incomplete multibyte sequence.
01014               ** If it's possible to have an incomplete 1024 character long
01015               ** multibyte sequence, we're in trouble. Or we've encountered
01016               ** an EINVAL when flushing out the remaining buffered input,
01017               ** in convert_chset_end(). In either case, it's ok to sicard
01018               ** one character at a time, until we either reach the end,
01019               ** or get some other result.
01020               **
01021               ** - iconv(3) reason #4, E2BIG. If the 1024 character output
01022               ** buffer, above, is insufficient to produce the output from a
01023               ** single converted character, we're in trouble.
01024               */
01025 
01026               if (*inbytesleft == origin)
01027               {
01028                      h->skipleft=h->skipcnt;
01029                      h->converr=1;
01030               }
01031 
01032               /*
01033               ** Stopped at an incomplete multibyte sequence, try again on
01034               ** the next round.
01035               */
01036               else if (save_errno == EINVAL)
01037                      break;
01038 
01039               if (save_errno == EILSEQ)
01040                      h->converr=1; /* Another possibility this can happen */
01041 
01042               /*
01043               ** If we get here because of iconv(3) reason #4, filled out
01044               ** the output buffer, we should continue with the conversion.
01045               ** Otherwise, upon encountering any other error condition,
01046               ** reset the conversion state.
01047               */
01048               if (save_errno != E2BIG)
01049                      iconv(h->h, NULL, NULL, NULL, NULL);
01050        }
01051 }
01052 
01053 /*****************************************************************************/
01054 
01055 /*
01056 ** A wrapper for libmail_u_convert() that collects the converted character
01057 ** text into a buffer. This is done by passing an output function to
01058 ** libmail_u_convert() that saves converted text in a linked-list
01059 ** of buffers.
01060 **
01061 ** Then, in the deinitialization function, the buffers get concatenated into
01062 ** the final character buffer.
01063 */
01064 
01065 struct libmail_u_convert_cbuf {
01066        struct libmail_u_convert_cbuf *next;
01067        char *fragment;
01068        size_t fragment_size;
01069 };
01070 
01071 struct libmail_u_convert_tocbuf {
01072        struct libmail_u_convert_hdr hdr;
01073 
01074        char **cbufptr_ret;
01075        size_t *cbufsize_ret;
01076        int errflag;
01077        size_t tot_size;
01078        int nullterminate;
01079 
01080        struct libmail_u_convert_cbuf *first, **last;
01081 };
01082 
01083 static int save_tocbuf(const char *, size_t, void *);
01084 static int convert_tocbuf(void *ptr,
01085                        const char *text, size_t cnt);
01086 static int deinit_tocbuf(void *ptr, int *errptr);
01087 
01088 libmail_u_convert_handle_t
01089 libmail_u_convert_tocbuf_init(const char *src_chset,
01090                            const char *dst_chset,
01091                            char **cbufptr_ret,
01092                            size_t *cbufsize_ret,
01093                            int nullterminate
01094                            )
01095 {
01096        struct libmail_u_convert_tocbuf *p=
01097               malloc(sizeof(struct libmail_u_convert_tocbuf));
01098        libmail_u_convert_handle_t h;
01099 
01100        if (!p)
01101               return NULL;
01102 
01103        memset(p, 0, sizeof(*p));
01104 
01105        h=libmail_u_convert_init(src_chset, dst_chset, save_tocbuf, p);
01106 
01107        if (!h)
01108        {
01109               free(p);
01110               return NULL;
01111        }
01112 
01113        p->cbufptr_ret=cbufptr_ret;
01114        p->cbufsize_ret=cbufsize_ret;
01115        p->last= &p->first;
01116        p->nullterminate=nullterminate;
01117        p->hdr.next=h;
01118        p->hdr.convert_handler=convert_tocbuf;
01119        p->hdr.deinit_handler=deinit_tocbuf;
01120        p->hdr.ptr=p;
01121        return &p->hdr;
01122 }
01123 
01124 /* Capture the output of the conversion stack */
01125 
01126 static int save_tocbuf(const char *text, size_t cnt, void *ptr)
01127 {
01128        struct libmail_u_convert_tocbuf *p=
01129               (struct libmail_u_convert_tocbuf *)ptr;
01130        struct libmail_u_convert_cbuf *fragment=
01131               malloc(sizeof(struct libmail_u_convert_cbuf)+cnt);
01132        size_t tot_size;
01133 
01134        if (!fragment)
01135        {
01136               p->errflag=1;
01137               return 1;
01138        }
01139 
01140        fragment->next=NULL;
01141        fragment->fragment=(char *)(fragment+1);
01142        if ((fragment->fragment_size=cnt) > 0)
01143               memcpy(fragment->fragment, text, cnt);
01144 
01145        *(p->last)=fragment;
01146        p->last=&fragment->next;
01147 
01148        tot_size=p->tot_size + cnt; /* Keep track of the total size saved */
01149 
01150        if (tot_size < p->tot_size) /* Overflow? */
01151        {
01152               errno=E2BIG;
01153               return 1;
01154        }
01155        p->tot_size=tot_size;
01156        return 0;
01157 }
01158 
01159 /* Punt converted text down the stack */
01160 
01161 static int convert_tocbuf(void *ptr, const char *text, size_t cnt)
01162 {
01163        struct libmail_u_convert_tocbuf *p=
01164               (struct libmail_u_convert_tocbuf *)ptr;
01165 
01166        return libmail_u_convert(p->hdr.next, text, cnt);
01167 }
01168 
01169 /*
01170 ** Destroy the conversion stack. Destroy the downstream, then assemble the
01171 ** final array.
01172 */
01173 
01174 static int deinit_tocbuf(void *ptr, int *errptr)
01175 {
01176        struct libmail_u_convert_tocbuf *p=
01177               (struct libmail_u_convert_tocbuf *)ptr;
01178        int rc=libmail_u_convert_deinit(p->hdr.next, errptr);
01179        struct libmail_u_convert_cbuf *bufptr;
01180 
01181        if (rc == 0 && p->nullterminate)
01182        {
01183               char zero=0;
01184 
01185               rc=save_tocbuf( &zero, sizeof(zero), p->hdr.ptr);
01186        }
01187 
01188        if (rc == 0)
01189        {
01190               if (((*p->cbufptr_ret)=malloc(p->tot_size ? p->tot_size:1)) !=
01191                   NULL)
01192               {
01193                      size_t i=0;
01194 
01195                      for (bufptr=p->first; bufptr; bufptr=bufptr->next)
01196                      {
01197                             if (bufptr->fragment_size)
01198                                    memcpy(&(*p->cbufptr_ret)[i],
01199                                           bufptr->fragment,
01200                                           bufptr->fragment_size);
01201                             i += bufptr->fragment_size;
01202                      }
01203                      (*p->cbufsize_ret)=i;
01204               }
01205               else
01206               {
01207                      rc= -1;
01208               }
01209        }
01210 
01211        for (bufptr=p->first; bufptr; )
01212        {
01213               struct libmail_u_convert_cbuf *b=bufptr;
01214 
01215               bufptr=bufptr->next;
01216 
01217               free(b);
01218        }
01219        free(p);
01220 
01221        return rc;
01222 }
01223 
01224 libmail_u_convert_handle_t
01225 libmail_u_convert_tocbuf_toutf8_init(const char *src_chset,
01226                                  char **cbufptr_ret,
01227                                  size_t *cbufsize_ret,
01228                                  int nullterminate
01229                                  )
01230 {
01231        return libmail_u_convert_tocbuf_init(src_chset, "utf-8",
01232                                         cbufptr_ret, cbufsize_ret,
01233                                         nullterminate);
01234 }
01235 
01236 libmail_u_convert_handle_t
01237 libmail_u_convert_tocbuf_fromutf8_init(const char *dst_chset,
01238                                    char **cbufptr_ret,
01239                                    size_t *cbufsize_ret,
01240                                    int nullterminate
01241                                    )
01242 {
01243        return libmail_u_convert_tocbuf_init("utf-8", dst_chset,
01244                                         cbufptr_ret, cbufsize_ret,
01245                                         nullterminate);
01246 }
01247 
01248 char *libmail_u_convert_toutf8(const char *text,
01249                             const char *charset,
01250                             int *error)
01251 {
01252        char *cbufptr;
01253        size_t cbufsize;
01254        libmail_u_convert_handle_t h=
01255               libmail_u_convert_tocbuf_toutf8_init(charset,
01256                                                &cbufptr,
01257                                                &cbufsize, 1);
01258 
01259        if (!h)
01260               return NULL;
01261 
01262        libmail_u_convert(h, text, strlen(text));
01263 
01264        if (libmail_u_convert_deinit(h, error) == 0)
01265               return cbufptr;
01266 
01267        return NULL;
01268 }
01269 
01270 char *libmail_u_convert_fromutf8(const char *text,
01271                              const char *charset,
01272                              int *error)
01273 {
01274        char *cbufptr;
01275        size_t cbufsize;
01276        libmail_u_convert_handle_t h=
01277               libmail_u_convert_tocbuf_fromutf8_init(charset,
01278                                                  &cbufptr,
01279                                                  &cbufsize, 1);
01280 
01281        if (!h)
01282               return NULL;
01283 
01284        libmail_u_convert(h, text, strlen(text));
01285 
01286        if (libmail_u_convert_deinit(h, error) == 0)
01287               return cbufptr;
01288 
01289        return NULL;
01290 }
01291 
01292 char *libmail_u_convert_tobuf(const char *text,
01293                            const char *charset,
01294                            const char *dstcharset,
01295                            int *error)
01296 {
01297        char *cbufptr;
01298        size_t cbufsize;
01299        libmail_u_convert_handle_t h=
01300               libmail_u_convert_tocbuf_init(charset,
01301                                          dstcharset,
01302                                          &cbufptr,
01303                                          &cbufsize, 1);
01304 
01305        if (!h)
01306               return NULL;
01307 
01308        libmail_u_convert(h, text, strlen(text));
01309 
01310        if (libmail_u_convert_deinit(h, error) == 0)
01311               return cbufptr;
01312 
01313        return NULL;
01314 }
01315 
01316 /*****************************************************************************/
01317 
01318 /*
01319 ** Convert text to unicode_chars. Same basic approach as
01320 ** libmail_u_convert_tocbuf_init(). The output character set gets specified
01321 ** as UCS-4, the final output size is divided by 4, and the output buffer gets
01322 ** typed as a unicode_char array.
01323 */
01324 
01325 struct libmail_u_convert_buf {
01326        struct libmail_u_convert_buf *next;
01327        unicode_char *fragment;
01328        size_t fragment_size;
01329        size_t max_fragment_size;
01330 };
01331 
01332 struct libmail_u_convert_tou {
01333        struct libmail_u_convert_hdr hdr;
01334 
01335        unicode_char **ucptr_ret;
01336        size_t *ucsize_ret;
01337        int errflag;
01338        size_t tot_size;
01339        int nullterminate;
01340 
01341        struct libmail_u_convert_buf *first, *tail, **last;
01342 };
01343 
01344 static int save_unicode(const char *, size_t, void *);
01345 static int convert_tounicode(void *ptr,
01346                       const char *text, size_t cnt);
01347 static int deinit_tounicode(void *ptr, int *errptr);
01348 
01349 libmail_u_convert_handle_t
01350 libmail_u_convert_tou_init(const char *src_chset,
01351                         unicode_char **ucptr_ret,
01352                         size_t *ucsize_ret,
01353                         int nullterminate
01354                         )
01355 {
01356        struct libmail_u_convert_tou *p=
01357               malloc(sizeof(struct libmail_u_convert_tou));
01358        libmail_u_convert_handle_t h;
01359 
01360        if (!p)
01361               return NULL;
01362 
01363        memset(p, 0, sizeof(*p));
01364 
01365        h=libmail_u_convert_init(src_chset, libmail_u_ucs4_native,
01366                              save_unicode, p);
01367 
01368        if (!h)
01369        {
01370               free(p);
01371               return NULL;
01372        }
01373 
01374        p->ucptr_ret=ucptr_ret;
01375        p->ucsize_ret=ucsize_ret;
01376        p->last= &p->first;
01377        p->nullterminate=nullterminate;
01378        p->hdr.next=h;
01379        p->hdr.convert_handler=convert_tounicode;
01380        p->hdr.deinit_handler=deinit_tounicode;
01381        p->hdr.ptr=p;
01382        return &p->hdr;
01383 }
01384 
01385 libmail_u_convert_handle_t
01386 libmail_u_convert_fromu_init(const char *dst_chset,
01387                           char **cbufptr_ret,
01388                           size_t *csize_ret,
01389                           int nullterminate
01390                           )
01391 {
01392        return libmail_u_convert_tocbuf_init(libmail_u_ucs4_native,
01393                                         dst_chset,
01394                                         cbufptr_ret,
01395                                         csize_ret,
01396                                         nullterminate);
01397 }
01398 
01399 int libmail_u_convert_uc(libmail_u_convert_handle_t handle,
01400                       const unicode_char *text,
01401                       size_t cnt)
01402 {
01403        return libmail_u_convert(handle, (const char *)text,
01404                              cnt * sizeof(*text));
01405 }
01406 
01407 /* Capture the output of the conversion stack */
01408 
01409 static int save_unicode(const char *text, size_t cnt, void *ptr)
01410 {
01411        struct libmail_u_convert_tou *p=
01412               (struct libmail_u_convert_tou *)ptr;
01413        struct libmail_u_convert_buf *fragment;
01414        size_t tot_size;
01415 
01416        cnt /= sizeof(unicode_char);
01417 
01418        tot_size=p->tot_size + cnt*sizeof(unicode_char);
01419        /* Keep track of the total size saved */
01420 
01421        if (p->tail)
01422        {
01423               size_t n=p->tail->max_fragment_size-p->tail->fragment_size;
01424 
01425               if (n > cnt)
01426                      n=cnt;
01427 
01428               if (n)
01429               {
01430                      memcpy(p->tail->fragment+p->tail->fragment_size,
01431                             text, n*sizeof(unicode_char));
01432 
01433                      cnt -= n;
01434                      text += n*sizeof(unicode_char);
01435                      p->tail->fragment_size += n;
01436               }
01437        }
01438 
01439        if (cnt > 0)
01440        {
01441               size_t cnt_alloc=cnt;
01442 
01443               if (cnt_alloc < 16)
01444                      cnt_alloc=16;
01445 
01446               if ((fragment=malloc(sizeof(struct libmail_u_convert_buf)
01447                                  +cnt_alloc*sizeof(unicode_char)))
01448                   == NULL)
01449               {
01450                      p->errflag=1;
01451                      return 1;
01452               }
01453 
01454               fragment->next=NULL;
01455               fragment->fragment=(unicode_char *)(fragment+1);
01456               fragment->max_fragment_size=cnt_alloc;
01457               fragment->fragment_size=cnt;
01458               memcpy(fragment->fragment, text, cnt*sizeof(unicode_char));
01459 
01460               *(p->last)=fragment;
01461               p->last=&fragment->next;
01462               p->tail=fragment;
01463        }
01464 
01465        if (tot_size < p->tot_size) /* Overflow? */
01466        {
01467               errno=E2BIG;
01468               return 1;
01469        }
01470        p->tot_size=tot_size;
01471        return 0;
01472 }
01473 
01474 /* Punt converted text down the stack */
01475 
01476 static int convert_tounicode(void *ptr,
01477                           const char *text, size_t cnt)
01478 {
01479        struct libmail_u_convert_tou *p=
01480               (struct libmail_u_convert_tou *)ptr;
01481 
01482        return libmail_u_convert(p->hdr.next, text, cnt);
01483 }
01484 
01485 /*
01486 ** Destroy the conversion stack. Destroy the downstream, then assemble the
01487 ** final array.
01488 */
01489 
01490 static int deinit_tounicode(void *ptr, int *errptr)
01491 {
01492        struct libmail_u_convert_tou *p=
01493               (struct libmail_u_convert_tou *)ptr;
01494        int rc=libmail_u_convert_deinit(p->hdr.next, errptr);
01495        struct libmail_u_convert_buf *bufptr;
01496 
01497        if (rc == 0 && p->nullterminate)
01498        {
01499               unicode_char zero=0;
01500 
01501               rc=save_unicode( (const char *)&zero, sizeof(zero),
01502                              p->hdr.ptr);
01503        }
01504 
01505        if (rc == 0)
01506        {
01507               if (((*p->ucptr_ret)=malloc(p->tot_size ? p->tot_size:1)) !=
01508                   NULL)
01509               {
01510                      size_t i=0;
01511 
01512                      for (bufptr=p->first; bufptr; bufptr=bufptr->next)
01513                      {
01514                             if (bufptr->fragment_size)
01515                                    memcpy(&(*p->ucptr_ret)[i],
01516                                           bufptr->fragment,
01517                                           bufptr->fragment_size
01518                                           *sizeof(*bufptr->fragment));
01519                             i += bufptr->fragment_size;
01520                      }
01521                      (*p->ucsize_ret)=i;
01522               }
01523               else
01524               {
01525                      rc= -1;
01526               }
01527        }
01528 
01529        for (bufptr=p->first; bufptr; )
01530        {
01531               struct libmail_u_convert_buf *b=bufptr;
01532 
01533               bufptr=bufptr->next;
01534 
01535               free(b);
01536        }
01537        free(p);
01538 
01539        return rc;
01540 }
01541 
01542 int libmail_u_convert_tou_tobuf(const char *text,
01543                             size_t text_l,
01544                             const char *charset,
01545                             unicode_char **uc,
01546                             size_t *ucsize,
01547                             int *err)
01548 {
01549        libmail_u_convert_handle_t h;
01550 
01551        if ((h=libmail_u_convert_tou_init(charset, uc, ucsize, 0)) == NULL)
01552               return -1;
01553 
01554        if (libmail_u_convert(h, text, text_l) < 0)
01555        {
01556               libmail_u_convert_deinit(h, NULL);
01557               return -1;
01558        }
01559 
01560        if (libmail_u_convert_deinit(h, err))
01561               return -1;
01562 
01563        return 0;
01564 }
01565 
01566 int libmail_u_convert_fromu_tobuf(const unicode_char *utext,
01567                               size_t utext_l,
01568                               const char *charset,
01569                               char **c,
01570                               size_t *csize,
01571                               int *err)
01572 {
01573        libmail_u_convert_handle_t h;
01574 
01575        if (utext_l == (size_t)-1)
01576        {
01577               for (utext_l=0; utext[utext_l]; ++utext_l)
01578                    ;
01579        }
01580 
01581        if ((h=libmail_u_convert_fromu_init(charset, c, csize, 1)) == NULL)
01582               return -1;
01583 
01584        if (libmail_u_convert_uc(h, utext, utext_l) < 0)
01585        {
01586               libmail_u_convert_deinit(h, NULL);
01587               return -1;
01588        }
01589 
01590        if (libmail_u_convert_deinit(h, err))
01591               return -1;
01592 
01593        return 0;
01594 }
01595 
01596 char *libmail_u_convert_tocase(const char *str,
01597                             const char *charset,
01598                             unicode_char (*first_char_func)(unicode_char),
01599                             unicode_char (*char_func)(unicode_char))
01600 {
01601        unicode_char *uc;
01602        size_t ucsize;
01603        size_t i;
01604        int err;
01605        char *c;
01606        size_t csize;
01607 
01608        if (libmail_u_convert_tou_tobuf(str, strlen(str),
01609                                    charset, &uc, &ucsize, &err))
01610               return NULL;
01611 
01612        if (err)
01613        {
01614               free(uc);
01615               return NULL;
01616        }
01617 
01618        for (i=0; i<ucsize; ++i)
01619        {
01620               uc[i]=(*first_char_func)(uc[i]);
01621 
01622               if (char_func)
01623                      first_char_func=char_func;
01624        }
01625 
01626        if (libmail_u_convert_fromu_tobuf(uc, ucsize,
01627                                      charset,
01628                                      &c, &csize, &err))
01629        {
01630               free(uc);
01631               return NULL;
01632        }
01633 
01634        free(uc);
01635 
01636        if (err)
01637        {
01638               free(c);
01639               return NULL;
01640        }
01641 
01642        return c;
01643 }