Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / dtcm / RFCMIME.c
1 static char sccsid[] = "$TOG: RFCMIME.c /main/11 1999/06/30 12:08:55 mgreess $";
2 /*
3  *   COMPONENT_NAME: desktop
4  *
5  *   FUNCTIONS: CvtStr
6  *              DtXlateOpToStdLocale
7  *              DtXlateStdToOpLocale
8  *              _converter_
9  *              base64size
10  *              crlf
11  *              getCharSet
12  *              getEncodingType
13  *              mbisspace
14  *              md5PlainText
15  *              rfc1522cpy
16  *              targetTagName
17  *              writeBase64
18  *              writeContentHeaders
19  *              writeQPrint
20  *
21  *   ORIGINS: 119
22  *
23  *   OBJECT CODE ONLY SOURCE MATERIALS
24  */
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <md5.h>
32 #define _ILS_MACROS
33 #include <ctype.h>
34 #include <assert.h>
35 #include <RFCMIME.h>
36
37 /* Iconv not defined for linux.  Use the EUSCompat stubs instead. */
38 #if !defined(linux)
39 #  include <iconv.h>
40 #endif
41 #include <EUSCompat.h>
42 #include <locale.h>
43 #include <LocaleXlate.h>
44
45 #ifdef ICONV_INBUF_CONST
46 # define ICONV_INBUF_TYPE       const char **
47 #else
48 # define ICONV_INBUF_TYPE       char **
49 #endif
50
51 #define WORKSIZE 1024*10        
52 /*
53  * The following escape sequence is defined as "To ASCII".
54  * But is it correct regardless of ISO-2022-XX ???
55  */
56 #define ToASCII_NUM 3
57 static char ToASCII[ToASCII_NUM] = { 0x1b, 0x28, 0x42 };
58
59 /*
60  * _i18nwork1[] is used to convert the passed string with CD iconv.
61  * in _converter_().
62  *
63  */
64 static void           *_i18nwork1 = NULL;
65 static unsigned long  _i18nsize1 = 0;
66 static int            shouldAlloc1 = ~0;
67
68 /*
69  * _i18nwork2[] is used to convert the passed string with CD iconv.
70  * in CvtStr().
71  *
72  */
73 static void           *_i18nwork2 = NULL;
74 static unsigned long  _i18nsize2 = 0;
75 static int            shouldAlloc2 = ~0;
76
77 /*
78  * Forward declarations
79  */
80 extern void writeBase64(char * buf, const char * bp, const unsigned long len);
81
82 static const char *DfltStdCharset = "us-ascii";
83 static const char *DfltStdLang = "C";
84
85 static void crlf(char *buf)
86 {
87         strcat(buf,"\n");
88 }
89
90 /******************************************************************************
91  * Function:    int DtXlateOpToStdLocale (char *operation, char *opLocale,
92  *                         char **ret_stdLocale, char **ret_stdLang, char **ret_stdSet)
93  *
94  * Parameters:
95  *              operation       Operation associated with the locale value
96  *              opLocale        An operation-specific locale string
97  *              ret_locale      Returns the std locale
98  *                              Caller must free this string.
99  *              ret_stdLang        Returns the std language & territory string.
100  *                              Caller must free this string.
101  *              ret_stdSet         Returns the std code set string.
102  *                              Caller must free this string.
103  *
104  * Return Value:
105  *
106  * Purpose:  Gets the standard locale given an operation and its locale
107  *
108  *****************************************************************************/
109 void
110 DtXlateOpToStdLocale (
111      char       *operation,
112      char       *opLocale,
113      char       **ret_stdLocale,
114      char       **ret_stdLang,
115      char       **ret_stdSet)
116 {
117      _DtXlateDb MyDb = NULL;
118      char MyPlatform[_DtPLATFORM_MAX_LEN + 1];
119      int        ExecVer;
120      int        CompVer;        
121
122      if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
123          _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
124      {
125           _DtLcxCloseDb(&MyDb);
126           MyDb = NULL;
127      }
128
129
130     if (MyDb != NULL)   
131         {
132         (void) _DtLcxXlateOpToStd(MyDb, MyPlatform, ExecVer,
133                                 operation,opLocale,
134                                 ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
135         }
136
137     /* if failed, give default values */
138     if (ret_stdLocale != NULL && *ret_stdLocale == NULL)
139     {
140         *ret_stdLocale = (char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
141         sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
142     }
143     if (ret_stdLang != NULL && *ret_stdLang == NULL)
144         *ret_stdLang = (char *)strdup(DfltStdLang);
145     if (ret_stdSet != NULL && *ret_stdSet == NULL)
146         *ret_stdSet = (char *)strdup(DfltStdCharset);
147 }
148
149 /******************************************************************************
150  * Function:    int DtXlateStdToOpLocale ( char *operation, char *stdLocale,
151  *                                      char *stdLang, char *stdCodeSet,
152  *                                      char *dflt_opLocale, char **ret_opLocale)
153  *
154  * Parameters:
155  *    operation         operation whose locale value will be retrieved
156  *    stdLocale         standard locale value
157  *    stdLang           standard Lang/Territory Value
158  *    stdCodeSet        standard CodeSet Value
159  *    dflt_opLocale     operation-specific locale-value
160  *                      This is the default value used in error case
161  *    ret_opLocale      operation-specific locale-value placed here
162  *                      Caller must free this string.
163  *
164  * Return Value:
165  *
166  * Purpose: Gets an operation-specific locale string given the standard string
167  *
168  *****************************************************************************/
169 void
170 DtXlateStdToOpLocale (
171      char       *operation,
172      char       *stdLocale,
173      char       *stdLang,
174      char       *stdCodeSet,
175      char       *dflt_opLocale,
176      char       **ret_opLocale)
177 {
178      _DtXlateDb MyDb = NULL;
179      char MyPlatform[_DtPLATFORM_MAX_LEN + 1];
180      int        ExecVer;
181      int        CompVer;
182
183      if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
184          _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
185      {
186           _DtLcxCloseDb(&MyDb);
187           MyDb = NULL;
188      }
189
190
191     if (MyDb != NULL)
192     {
193         (void) _DtLcxXlateStdToOp(MyDb, MyPlatform, ExecVer,
194                         operation, stdLocale, stdLang, stdCodeSet, NULL,
195                         ret_opLocale);
196     }
197
198     /* if translation fails, use a default value */
199     if (ret_opLocale && *ret_opLocale == NULL)
200     {
201        if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
202        else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
203     }
204 }
205 char *
206 targetTagName()
207 {
208    char *ret_locale = NULL;
209    char *ret_lang = NULL;
210    char *ret_codeset = NULL;
211    char *ret_target = NULL;
212
213    DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
214           setlocale(LC_CTYPE, NULL),
215           &ret_locale,
216           &ret_lang,
217           &ret_codeset);
218    DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
219           NULL,
220           NULL,
221           ret_codeset,
222           NULL,
223           &ret_target);
224    DtXlateStdToOpLocale(DtLCX_OPER_MIME,
225           NULL,
226           NULL,
227           ret_target,
228           NULL,
229           &ret_codeset);
230
231    return ret_codeset;
232 }
233
234 void
235 getCharSet(char * charset)
236 {
237         char *mimeCS = NULL;
238
239      mimeCS = targetTagName();
240
241     if (mimeCS) {
242        strcpy(charset, mimeCS);
243     } else {
244            strcpy(charset, "us-ascii");   /* default MIME codeset */
245     }
246 }
247
248
249 void
250 md5PlainText(const char * bp, const unsigned long len, unsigned char * digest)
251 {
252     /* We need to compute the md5 signature based on a message that has
253     // the CRLF line terminator. Most of our buffers don't so we will need
254     // to scan the body and do some magic. The approach will be to sum
255     // one line at a time. If the buffer doesn't have CRLF we will do that
256     // independently.
257     */
258
259     MD5_CTX context;
260     unsigned char * local_crlf = (unsigned char *)"\r\n";
261     const char * last = bp;
262     const char * cur;
263
264     MD5Init(&context);
265     for (cur = bp; cur < (bp + len); cur++) {
266         if (*cur == '\n') {
267             if (cur == bp || *(cur - 1) == '\r') {
268                 MD5Update(&context, (unsigned char *)last,
269                           cur - last + 1);
270             }
271             else {
272                 MD5Update(&context, (unsigned char *)last,
273                           cur - last);
274                 MD5Update(&context, local_crlf, 2);
275             }
276             last = cur + 1;
277         }
278     }
279
280     if (bp[len - 1] != '\n') {
281         /* Need to sum the trailing fraction with a CRLF. */
282         MD5Update(&context, (unsigned char *)last,
283                   cur - last);
284         MD5Update(&context, local_crlf, 2);
285     }
286
287     MD5Final(digest, &context);
288 }
289
290
291 static void _converter_( iconv_t CD,
292                         void *from, unsigned long from_len,
293                         void **to,  unsigned long *to_len )
294 {
295     char          *InBuf;
296     size_t        InBytesLeft;
297     char          *OutBuf = NULL;
298     size_t        OutBytesLeft = 0;
299     size_t        _OutBytesLeft = 0;
300     size_t        iconv_ret;
301     size_t        converted_num = 0;
302
303
304     *to = NULL;
305     *to_len = 0;
306
307     if ( shouldAlloc1 ) {
308         /* Obtain work area */
309         _i18nwork1 = (size_t *)malloc( WORKSIZE );
310         if ( !_i18nwork1 ) {
311             _i18nwork1 = NULL;
312             return;
313         }
314         _i18nsize1 = WORKSIZE; 
315         shouldAlloc1 = 0;
316     }
317
318     InBuf        = (char *)from;
319     InBytesLeft  = from_len;
320     OutBytesLeft = _i18nsize1;
321     OutBuf = (char *)_i18nwork1;
322
323     /*
324      * Need to place iconv state to the initial one by
325      * setting inbuf to NULL of iconv().
326      */
327     iconv( CD, (ICONV_INBUF_TYPE)NULL, 0, NULL, 0 );
328     while( 1 ) {
329         /*
330          * InBuf
331          *  v
332          * +----------------------------+
333          * | |                        | |
334          * +----------------------------+
335          *  <-------------------------->
336          *          InBytesLeft
337          *
338          *             |
339          *             | iconv()
340          *             V
341          * (_i18nwork1)
342          * OutBuf
343          *  v
344          * +----------------------------+
345          * | |                        | |
346          * +----------------------------+
347          *  <-------------------------->
348          *          InBytesLeft
349          */
350
351         iconv_ret = iconv( CD, (ICONV_INBUF_TYPE)&InBuf, &InBytesLeft,
352                                &OutBuf, &OutBytesLeft );
353         if ( iconv_ret == 0 ) {
354             /* iconv done
355              *                             InBuf
356              *                               v
357              * +----------------------------+
358              * |XXXXXXXXXXXXXXXXXXXXXXXXXXXX|
359              * +----------------------------+
360              *                               
361              *                               InBytesLeft=0
362              *
363              * (_i18nwork1)
364              *  |               OutBuf
365              *  V                 v
366              * +----------------------------+
367              * |XXXXXXXXXXXXXXXXX| |      | |
368              * +----------------------------+
369              *  <---------------> <-------->
370              *   converted_num    OutBytesLeft
371              */
372             converted_num = (unsigned long)((char *)OutBuf-(char *)_i18nwork1);
373             *to = (void *)_i18nwork1;
374             *to_len = (unsigned long)converted_num;
375             break;
376         } else {
377             if ( errno == E2BIG ) {
378                 /* Overflow. still data is left.
379                  *               InBuf
380                  *                 v
381                  * +----------------------------+
382                  * |XXXXXXXXXXXXXX| |         | |
383                  * +----------------------------+
384                  *                 <----------->
385                  *                  InBytesLeft
386                  *
387                  * (_i18nwork1)
388                  *  |                         OutBuf
389                  *  V                          v
390                  * +----------------------------+
391                  * |XXXXXXXXXXXXXXXXXXXXXXXXXXX |
392                  * +----------------------------+
393                  *  <-------------------------> 
394                  *          converted_num      OutBytesLeft=?
395                  */
396                 void *_p;
397
398                 /* Check how many converted already.
399                 */
400                 converted_num =
401                         (unsigned long)((char *)OutBuf - (char *)_i18nwork1);
402                 _i18nsize1 += WORKSIZE;
403                 _p = realloc( _i18nwork1, _i18nsize1 );
404                 if ( !_p ) {
405                     *to = NULL;
406                     *to_len = 0;
407                     free( _i18nwork1 );
408                     _i18nwork1 = NULL;
409                     _i18nsize1 = 0;
410                     shouldAlloc1 = ~0;
411                     break;
412                 } else {
413                     _i18nwork1 = _p;
414                     OutBuf = (char *)((char*)_i18nwork1 + converted_num);
415                     OutBytesLeft += WORKSIZE;
416                 }  
417             } else {
418                 *to = NULL;
419                 *to_len = 0;
420                 break;
421             }
422         }
423     }
424 }
425
426
427 int
428 CvtStr( char *charSet, void *from, unsigned long from_len,
429                  void **to, unsigned long *to_len, Direction dir )
430 {
431     char        *ret_locale = NULL;
432     char        *ret_lang = NULL;
433     char        *ret_codeset = NULL;
434     char        *from_codeset = NULL;
435     char        *to_codeset = NULL;
436     char        *CuStdCodeSet = NULL;
437     char        *InterChCodeSet = NULL;
438     char        *StdCodeSet = NULL;
439     iconv_t     CD;
440     int         isASCII=~0;
441     int         isStopASCII = ~0;
442     unsigned long converted_num = 0;
443
444 /* Get CuStdCodeSet */
445     DtXlateOpToStdLocale( DtLCX_OPER_SETLOCALE,
446                           setlocale( LC_CTYPE, NULL ),
447                           &ret_locale,
448                           &ret_lang,
449                           &CuStdCodeSet );
450
451 /*
452  * If charSet is NULL, it means the passed string's charset in *from is
453  * unknown by dtmail. In this case, this converter assumes that 
454  * when dir = CURRENT_TO_INTERNET,
455  *     *from's encoding is the current locale's one.
456  * when dir = INTERNET_TO_CURRENT,
457  *     *from's encoding is the current locale's Internet Message's one.
458  *
459  * Example.
460  *   dtmail is running under ja_JP locale.
461  *    dir : CURRENT_TO_INTERNET
462  *         *from = IBM-eucJP
463  *         *to   = ISO-2022-JP
464  *    dir : INTERNET_TO_CURRENT
465  *         *from = ISO-2022-JP
466  *         *to   = IBM-eucJP
467  */
468 /*
469  * ISO-2022-JP can be converted to either EUC-JP or IBM-932 practically.
470  * But the current AIX.lcx says 
471  *       StdCodeSet      InterchangeCodeset
472  *         EUC-JP   <-->   ISO-2022-JP
473  *         IBM-932  --->   ISO-2022-JP
474  *         HP-SJIS  --->   ISO-2022-JP
475  *         HP-KANA8 --->   ISO-2022-JP
476  * therefore DtXlateOpToStdLocale() can convert ISO-2022-JP to EUC-JP only.
477  * To fix this, we hard-code'ed this situation with the CDE Standard Name
478  *
479  * ???? Is it correct ???
480  */
481     if ( dir == INTERNET_TO_CURRENT ) {
482         /*
483          * As for from_codeset
484          */
485         if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
486             /* Convert CuStdCodeSet to StdInterChangeCodeSet */
487             DtXlateStdToOpLocale( DtLCX_OPER_INTERCHANGE_CODESET,
488                                 NULL,
489                                 NULL,
490                                 CuStdCodeSet,
491                                 NULL,
492                                 &InterChCodeSet );
493         } else {
494             /* Convert charSet to StdInterChangeCodeSet */
495             ret_locale = ret_lang = ret_codeset = NULL;
496             DtXlateOpToStdLocale( DtLCX_OPER_MIME,
497                                 charSet,
498                                 &ret_locale,
499                                 &ret_lang,
500                                 &InterChCodeSet );
501         }
502
503         /* Convert StdInterChangeCodeSet to OpIVONC3 codeset */
504         DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
505                                 NULL,
506                                 NULL,
507                                 InterChCodeSet,
508                                 NULL,
509                                 &from_codeset );
510         /*
511          * As for to_codeset
512          */
513         if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
514             /* Convert CuStdCodeSet to OpIVONC3 codeset */
515             DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
516                                 NULL,
517                                 NULL,
518                                 CuStdCodeSet,
519                                 NULL,
520                                 &to_codeset );
521         } else {
522 #ifdef _AIX
523             if ( (!strncasecmp(InterChCodeSet,"ISO-2022-JP",11) &&
524                   !strncasecmp(CuStdCodeSet,"IBM-932",7)        ) ||
525                  (!strncasecmp(InterChCodeSet,"ISO-2022-JP",11) &&
526                   !strncasecmp(CuStdCodeSet,"EUC-JP",6)         )    ) {
527                 ret_codeset = CuStdCodeSet;
528             } else 
529 #endif /* _AIX */
530             {
531                 /* Convert InterChCodeSet to StdCodeSet */
532                 ret_locale = ret_lang = ret_codeset = NULL;
533                 DtXlateOpToStdLocale( DtLCX_OPER_INTERCHANGE_CODESET,
534                                 InterChCodeSet,
535                                 &ret_locale,
536                                 &ret_lang,
537                                 &ret_codeset );
538             }
539             DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
540                                 NULL,
541                                 NULL,
542                                 ret_codeset,
543                                 NULL,
544                                 &to_codeset );
545         }
546     } else { /* dir == CURRENT_TO_INTERNET */
547         /*
548          * As for from_codeset
549          */
550         if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
551             /* Convert CuStdCodeSet to OpICONV3 codeset */
552             DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
553                                 NULL,
554                                 NULL,
555                                 CuStdCodeSet,
556                                 NULL,
557                                 &from_codeset );
558         } else {
559             /* Convert charSet to StdInterChangeCodeSet */
560             ret_locale = ret_lang = ret_codeset = NULL;
561             DtXlateOpToStdLocale( DtLCX_OPER_MIME,
562                                 charSet,
563                                 &ret_locale,
564                                 &ret_lang,
565                                 &ret_codeset );
566             /* Convert StdInterChangeCodeSet to OpIVONC3 codeset */
567             DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
568                                 NULL,
569                                 NULL,
570                                 ret_codeset,
571                                 NULL,
572                                 &from_codeset );
573         }
574         /*
575          * As for to_codeset
576          */
577         if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
578             /* Convert CuStdCodeSet to StdInterChangeCodeSet */
579             DtXlateStdToOpLocale( DtLCX_OPER_INTERCHANGE_CODESET,
580                                 NULL,
581                                 NULL,
582                                 CuStdCodeSet,
583                                 NULL,
584                                 &InterChCodeSet );
585         } else {
586             /* Convert charSet to StdInterChangeCodeSet */
587             ret_locale = ret_lang = ret_codeset = NULL;
588             DtXlateOpToStdLocale( DtLCX_OPER_MIME,
589                                 charSet,
590                                 &ret_locale,
591                                 &ret_lang,
592                                 &InterChCodeSet );
593         }
594
595         /* Convert StdInterChangeCodeSet to OpIVONC3 codeset */
596         DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
597                                 NULL,
598                                 NULL,
599                                 InterChCodeSet,
600                                 NULL,
601                                 &to_codeset );
602     }
603
604     *to = NULL;
605     *to_len = 0;
606
607     if ( shouldAlloc2 ) {
608         /* Obtain work area */
609         _i18nwork2 = (size_t *)malloc( WORKSIZE );
610         if ( !_i18nwork2 ) {
611             _i18nwork2 = NULL;
612             return( isASCII );
613         }
614         _i18nsize2 = WORKSIZE; 
615         shouldAlloc2 = 0;
616     }
617
618     if (NULL == to_codeset || NULL == from_codeset)
619       return( isASCII );
620
621     if ( ( CD = iconv_open( to_codeset, from_codeset ) ) != (iconv_t)-1 ) {
622         /*
623          * According to several RFCs( 822, 1468, 1557, ..... )
624          * the escape sequence to switch to ASCII is needed just before
625          * '\n'. IBM-eucJP/IBM-932 <--> fold 7 does while the other doesn't.
626          * Therefore CvtStr() does take care of this here.
627          */
628         if ( dir == INTERNET_TO_CURRENT ) {
629             _converter_( CD, from, from_len, to, to_len );
630         } else {
631             void *new_from = from;
632             unsigned long new_from_len = from_len;
633             unsigned long _passed = 0;
634             size_t clen;
635             void *_tmp = NULL;
636             unsigned long _tmp_len = 0;
637
638             while ( _passed < from_len ) {
639                 /*
640                  * Find \n or \0
641                  */
642                 for ( ; _passed < from_len; _passed += clen ) {
643                     clen = mblen(&(((char *)from)[_passed]), MB_CUR_MAX);
644                     if ( clen < 0 )
645                         break;
646
647                     if ( ( clen > 1 ) || !isascii( ((char*)from)[_passed] ) ){
648                         /* Here, maybe MB or non-ASCII */
649                         isASCII = 0;
650                         isStopASCII = 0;
651                     } else {
652                         if ( ( ((char*)from)[_passed] != '\n' ) &&
653                              ( ((char*)from)[_passed] != '\0' )    ) {
654                             isStopASCII = ~0;
655                         }
656                     }
657
658                     if ( ((char*)from)[_passed] == '\n' ||
659                          ((char*)from)[_passed] == '\0'    )
660                         break;
661                 }
662                 new_from_len = &(((char *)from)[_passed])-(char *)new_from;
663                 if ( ( _passed < from_len ) && ( clen == 1 ) &&
664                      ( ((char*)from)[_passed] == '\n' ) ) {
665                     new_from_len++;
666                 }
667
668                 /*
669                  * new_from                 from[_passed]
670                  *  V                        V
671                  * +------------------------+--+------.................+
672                  * | |                      |\n| |                     |
673                  * +------------------------+--+------.................+
674                  *  <-------------------------> $
675                  *     new_from_len             next new_from
676                  *  <------------------------------------------------->
677                  *  $                 from_len
678                  * from 
679                  */
680
681                 /*
682                  *      ********** DO 1 LINE CONVERSION **********
683                  */
684                 _tmp = NULL; _tmp_len = 0;
685                 _converter_( CD, new_from, new_from_len, &_tmp, &_tmp_len );
686
687                 if ( ( _tmp == NULL ) && ( _tmp_len == 0 ) ) {
688                     /* Conversion fail */
689                     *to = NULL;
690                     *to_len = 0;
691                     break;
692                 }
693
694                 /*
695                  * _i18nwork2                 _tmp
696                  *  V                           V
697                  * +-----------------------+   +-------------+  
698                  * |XXXXXXXX               | + |             |
699                  * +-----------------------+   +-------------+  
700                  *  <------>
701                  *  converted_num
702                  *  <--------------------->     <----------->
703                  *   _18nsize2                    _tmp_len
704                  */
705                 /* Append _tmp to target */
706                 if ( converted_num + _tmp_len > _i18nsize2 ) {
707                     /* Need much memory..... */
708                     void *_i18n = NULL;
709
710                     _i18nsize2 += WORKSIZE;
711                     _i18n = realloc( _i18nwork2, _i18nsize2  ); 
712                     if ( !_i18n ) {
713                         *to = NULL;
714                         *to_len = 0;
715                         _i18nwork2 = NULL;
716                         _i18nsize2 = 0;
717                         shouldAlloc2 = ~0;
718                         break;
719                     } else {
720                         _i18nwork2 = _i18n;
721                     }
722                 }
723
724                 /*
725                  * _i18nwork2  _tmp
726                  *  V          v
727                  * +---------------------------+  
728                  * |XXXXXXXXXXX(COPIED)XX      |
729                  * +---------------------------+  
730                  *  <---------><-------->
731                  *  (old)conv.  _tmp_len
732                  *  <------------------->
733                  *  (new)converted_num
734                  *  <--------------------------> 
735                  *        _i18nsize2
736                  */
737                 strncpy( (char *)_i18nwork2 + converted_num,
738                                 (char *)_tmp, _tmp_len );
739                 converted_num += _tmp_len;
740
741                 *to = (void *)_i18nwork2;
742                 *to_len = converted_num;
743                 new_from =  &(((char *)from)[_passed]) + 1;
744                 _passed++;
745
746                 /*
747                  * According to RFC1468, if the line is ended with non-ASCII
748                  * char, but not not switch to ASCII before the end of line,
749                  * we must switch to ASCII just before the end of the line.
750                  *
751                  * _i18nwork2                     ToASCII
752                  *  V                           ??? V
753                  * +---------------------------+   +------+
754                  * |XXXXXXXXXXXXXXXXXXXXX      | + |1b2842|
755                  * +---------------------------+   +------+
756                  *  <------------------->           <---->
757                  *  converted_num                   ToASCII_NUM
758                  *  <------------------------->
759                  *        _i18nsize2
760                  *                ========
761                  *                1b 28 42 ??
762                  *
763                  */
764                 if ( !isStopASCII ) {
765                     if ( (((char *)_i18nwork2)[converted_num-1] == '\n') ||
766                          (((char *)_i18nwork2)[converted_num-1] == '\0')    ) {
767
768                         char _p = ((char *)_i18nwork2)[converted_num-1];
769
770                         if (!((converted_num >=3+1 ) &&
771                          !memcmp((void *)((char *)_i18nwork2+converted_num-3-1),
772                                 ToASCII, ToASCII_NUM ))                      ){
773                             /* if not ToASCII there, then */
774                             /* Append ToASCII */
775
776                             if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
777                                 /* Need much memory..... */
778                                 void *_i18n = NULL;
779
780                                 _i18nsize2 += WORKSIZE;
781                                 _i18n=realloc(_i18nwork2,_i18nsize2); 
782                                 if ( !_i18n ) {
783                                     *to = NULL;
784                                     *to_len = 0;
785                                     shouldAlloc2 = ~0;
786                                     break;
787                                 } else {
788                                     _i18nwork2 = _i18n;
789                                 }
790                             }
791                             strncpy( (char *)_i18nwork2+converted_num-1,
792                                         ToASCII, ToASCII_NUM );
793                             converted_num += ToASCII_NUM;
794                             ((char *)_i18nwork2)[converted_num-1] = _p;
795                             *to = _i18nwork2;
796                             *to_len = converted_num;
797                             isStopASCII = ~0;
798                         }
799                     } else {
800                         if (!((converted_num >=3 ) &&
801                          !memcmp((void *)((char *)_i18nwork2+converted_num-3),
802                                 ToASCII, ToASCII_NUM ))                      ){
803                             /*
804                             // if not ToASCII there, then
805                             // Append ToASCII
806                             */
807                             if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
808                                 void *_i18n = NULL;
809
810                                 _i18nsize2 += WORKSIZE;
811                                 _i18n=realloc(_i18nwork2,_i18nsize2); 
812                                 if ( !_i18n ) {
813                                     *to = NULL;
814                                     *to_len = 0;
815                                     shouldAlloc2 = ~0;
816                                     break;
817                                 } else {
818                                     _i18nwork2 = _i18n;
819                                 }
820                             }
821                             strncpy( (char *)_i18nwork2+converted_num,
822                                         ToASCII, ToASCII_NUM );
823                             converted_num += ToASCII_NUM;
824                             *to = _i18nwork2;
825                             *to_len = converted_num;
826                             isStopASCII = ~0;
827                         }
828                     }
829                 }
830             }
831             /*
832              * Again........
833              */
834             if( ( *to != NULL ) && ( *to_len != 0 ) ) {
835                 if ( !isStopASCII ) {
836                     if ( (((char *)_i18nwork2)[converted_num-1] == '\n') ||
837                          (((char *)_i18nwork2)[converted_num-1] == '\0')    ) {
838
839                         char _p = ((char *)_i18nwork2)[converted_num-1];
840
841                         if (!((converted_num >=3+1 ) &&
842                          !memcmp((void *)((char *)_i18nwork2+converted_num-3-1),
843                                 ToASCII, ToASCII_NUM ))                      ){
844                             /* if not ToASCII there, then
845                             // Append ToASCII
846                             */
847
848                             if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
849                                 void *_i18n = NULL;
850
851                                 _i18nsize2 += WORKSIZE;
852                                 _i18n=realloc(_i18nwork2,_i18nsize2); 
853                                 if ( !_i18n ) {
854                                     *to = NULL;
855                                     *to_len = 0;
856                                     shouldAlloc2 = ~0;
857                                 } else {
858                                     _i18nwork2 = _i18n;
859                                 }
860                             }
861                             strncpy( (char *)_i18nwork2+converted_num-1,
862                                         ToASCII, ToASCII_NUM );
863                             converted_num += ToASCII_NUM;
864                             ((char *)_i18nwork2)[converted_num-1] = _p;
865                             *to = _i18nwork2;
866                             *to_len = converted_num;
867                             isStopASCII = ~0;
868                         }
869                     } else {
870                         if (!((converted_num >=3 ) &&
871                          !memcmp((void *)((char *)_i18nwork2+converted_num-3),
872                                 ToASCII, ToASCII_NUM ))                      ){
873                             /* if not ToASCII there, then
874                             // Append ToASCII
875                             */
876
877                             if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
878                                 void *_i18n = NULL;
879
880                                 _i18nsize2 += WORKSIZE;
881                                 _i18n=realloc(_i18nwork2,_i18nsize2); 
882                                 if ( !_i18n ) {
883                                     *to = NULL;
884                                     *to_len = 0;
885                                     shouldAlloc2 = ~0;
886                                 } else {
887                                     _i18nwork2 = _i18n;
888                                 }
889                             }
890                             strncpy( (char *)_i18nwork2+converted_num,
891                                         ToASCII, ToASCII_NUM );
892                             converted_num += ToASCII_NUM;
893                             *to = _i18nwork2;
894                             *to_len = converted_num;
895                             isStopASCII = ~0;
896                         }
897                     }
898                 }
899             }
900             /*
901              * If InterChCodeSet is either ISO-2022-JP/ISO-2022-KR/ISO-2022-TW
902              * ISO-2022-CN, strip MSB here since iconv'ed UDC has MSB set to 1
903              */
904             if ( !strncasecmp( InterChCodeSet, "ISO-2022-JP", 11 ) ||
905                  !strncasecmp( InterChCodeSet, "ISO-2022-KR", 11 ) ||
906                  !strncasecmp( InterChCodeSet, "ISO-2022-TW", 11 ) ||
907                  !strncasecmp( InterChCodeSet, "ISO-2022-CN", 11 )   ) {
908                 int _i;
909
910                 for ( _i = 0; _i < *to_len; _i++ ) {
911                         ((unsigned char *)(*to))[_i] &= (unsigned char)0x7f;
912                 }
913             }
914         }
915         iconv_close( CD );
916     }
917     return( isASCII );
918 }
919 unsigned int
920 base64size(const unsigned long len)
921 {
922     int b_len = len + (len / 3);
923     b_len += (b_len / 72 * 2) + 4;
924
925     return(b_len);
926 }
927
928 getEncodingType(const char * body,
929                          const unsigned int len,
930                          boolean_t strict_mime)
931 {
932     /*
933     // Our goal here is to produce the most readable, safe encoding.
934     // We have a couple of parameters that will guide our
935     // choices:
936     //
937     // 1) RFC822 allows lines to be a minimum of 1000 characters,
938     //    but MIME encourages mailers to keep lines to <76 characters
939     //    and use quoted-printable if necessary to achieve this.
940     //
941     // 2) The base64 encoding will grow the body size by 33%, and
942     //    also render it unreadable by humans. We don't want to use
943     //    it unless really necessary.
944     //
945     // Given the above 2 rules, we want to scan the body part and
946     // select an encoding. The 3 choices will be decided by:
947     //
948     // 1) If the text is 7 bit clean, and all lines are <76 chars,
949     //    then no encoding will be applied.
950     //
951     //
952     // 2) If the text is not 7 bit clean, or there are lines >76 chars,
953     //    and the quoted-printable size is less than the base64 size,
954     //    then quoted-printable will be done.
955     //
956     // 3) If 1 & 2 are not true, then base64 will be applied.
957     //
958     // If "strict_mime" is false we will only encode if the message
959     // is not 7 bit clean.
960     */
961
962
963     int base64_growth = base64size(len) - len;
964     int qprint_growth = 0;
965     boolean_t eight_bit = B_FALSE;
966     boolean_t base64 = B_FALSE;
967     boolean_t encode = B_FALSE;
968     Encoding    enc;
969
970     const char * last_nl = body;
971     const char * cur;
972
973     if (body == NULL || len == 0) {
974         return(MIME_7BIT);
975     }
976     if (strncmp(body, "From ", 5) == 0) {
977         qprint_growth += 2;
978     }
979
980     for (cur = body; cur < (body + len); cur++) {
981         if (*cur != (*cur & 0x7f)) {
982             eight_bit = B_TRUE;
983             encode = B_TRUE;
984             qprint_growth += 2;
985         } else if (*cur == '=' || *cur == 0) {
986             /*
987             // These characters don't force encoding, but will be
988             // encoded if we end up encoding.
989             qprint_growth += 2;
990             */
991         }
992
993         if (*cur == '\n') {
994             if ((cur - last_nl) > 76) {
995                 encode = B_TRUE;
996                 qprint_growth += 2;
997             }
998
999
1000             if ((cur != body && (*(cur - 1) == ' ' || *(cur - 1) == '\t'))) {
1001                 encode = B_TRUE;
1002                 qprint_growth += 2;
1003             }
1004
1005             if ((cur + 6) < (body + len) &&
1006                                         strncmp((cur + 1), "From ", 5) == 0) {
1007                 encode = B_TRUE;
1008                 qprint_growth += 2;
1009             }
1010
1011             last_nl = cur + 1;
1012         }
1013
1014         if (encode && (qprint_growth > base64_growth)) {
1015             base64 = B_TRUE;
1016             break;
1017         }
1018     }
1019
1020     /* Deal with buffers that dont end with a new line. */
1021
1022     if ((cur - last_nl) > 76) {
1023         encode = B_TRUE;
1024         qprint_growth += 2;
1025     }
1026
1027     enc = MIME_7BIT;
1028
1029     if (!strict_mime && !eight_bit) {
1030         /* If strict_mime is off we only encode if we have 8 bit data */
1031         enc = MIME_7BIT;
1032     } else if (encode) {
1033         /* strict_mime is TRUE and we have reason to encode. */
1034         if (base64) {
1035                 enc = MIME_BASE64;
1036         } else {
1037                 enc = MIME_QPRINT;
1038         }
1039     }
1040
1041     return(enc);
1042 }
1043
1044 void
1045 writeContentHeaders(char * hdr_buf,
1046                              const char * type,
1047                              const Encoding enc,
1048                              const char * digest,
1049                              int isAllASCII)
1050 {
1051     char default_charset[64];
1052
1053     strcat(hdr_buf,"Content-Type: ");
1054     strcat(hdr_buf,type);
1055
1056     if (isAllASCII)
1057         strcpy(default_charset,"US-ASCII");
1058     else
1059         getCharSet(default_charset);
1060
1061     strcat(hdr_buf,"; charset=");
1062
1063     strcat(hdr_buf,default_charset);
1064
1065     crlf(hdr_buf);
1066
1067     strcat(hdr_buf,"Content-Transfer-Encoding: ");
1068
1069     switch (enc) {
1070       case MIME_7BIT:
1071         strcat(hdr_buf,"7bit\n");
1072         break;
1073
1074       case MIME_8BIT:
1075       default: /* Assume the worst. */
1076         strcat(hdr_buf,"8bit\n");
1077         break;
1078       case MIME_QPRINT:
1079         strcat(hdr_buf,"quoted-printable\n");
1080         break;
1081
1082       case MIME_BASE64:
1083         strcat(hdr_buf,"base64\n");
1084         break;
1085     }
1086
1087     strcat(hdr_buf,"Content-MD5: ");
1088     writeBase64(hdr_buf, digest, 16);
1089 }
1090 /*
1091 //
1092 // Base64 Alphabet (65-character subset of US-ASCII as per RFC1521)
1093 //
1094 */
1095
1096 static const char base64_chars[] =
1097 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1098  'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a',
1099  'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
1100  'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
1101  '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
1102 };
1103
1104 void
1105 writeBase64(char * buf, const char * bp, const unsigned long len)
1106 {
1107
1108     /* The length has to be a multiple of 3. We will need to pad
1109     // any extra. Let's just work on the main body and save the
1110     // fractional stuff for the end.
1111     */
1112     unsigned long main_len = len - (len % 3);
1113     const unsigned char * ubp = (const unsigned char *)bp;
1114
1115     char line[80];
1116
1117     unsigned int enc_char;
1118
1119     int lf = 0;
1120
1121     int block;
1122
1123     if (bp == NULL || len == 0) {
1124         crlf(buf);
1125         return;
1126     }
1127
1128     for (block = 0; block < main_len; block += 3) {
1129         enc_char = (ubp[block] >> 2) & 0x3f;
1130         line[lf++] = base64_chars[enc_char];
1131
1132         enc_char = ((ubp[block] & 0x3) << 4) | ((ubp[block+1] >> 4) & 0xf);
1133         line[lf++] = base64_chars[enc_char];
1134
1135         enc_char = ((ubp[block + 1] & 0xf) << 2) | ((ubp[block + 2] >> 6) & 0x3);
1136         line[lf++] = base64_chars[enc_char];
1137
1138         enc_char = ubp[block + 2] & 0x3f;
1139         line[lf++] = base64_chars[enc_char];
1140
1141         if (lf == 72) {
1142             strncat(buf,line,lf);
1143             crlf(buf);
1144             lf = 0;
1145         }
1146     }
1147
1148     if (lf > 0) {
1149         strncat(buf, line,lf);
1150     }
1151
1152     if (((lf + 4) % 72) == 0) {
1153         crlf(buf);
1154     }
1155
1156     switch(len % 3) {
1157       case 1:
1158         enc_char = (ubp[block] >> 2) & 0x3f ;
1159         strncat(buf, &base64_chars[enc_char], 1);
1160
1161         enc_char = ((ubp[block] & 0x3) << 4);
1162         strncat(buf, &base64_chars[enc_char], 1);
1163
1164         strncat(buf,"==", 2);
1165         break;
1166
1167       case 2:
1168         enc_char = (ubp[block] >> 2) & 0x3f;
1169         strncat(buf,&base64_chars[enc_char], 1);
1170
1171         enc_char = ((ubp[block] & 0x3) << 4) | ((ubp[block+1] >> 4) & 0xf);
1172         strncat(buf,&base64_chars[enc_char], 1);
1173
1174         enc_char = ((ubp[block + 1] & 0xf) << 2);
1175         strncat(buf,&base64_chars[enc_char], 1);
1176
1177         strncat(buf,"=", 1);
1178     }
1179
1180 /*    crlf(buf); */
1181 }
1182 void
1183 writeQPrint(char *buf, const char * bp, const unsigned long bp_len,
1184                         int is_Special )
1185 {
1186     int last_nl = 0;
1187     int off = 0;
1188     char line_buf[80];
1189     const char * start;
1190     const char * cur;
1191     const char * white;
1192     const char * nw;
1193     int line_len;
1194     const char *cp_w;
1195     char prev;
1196     char tmpbuf[20];
1197
1198     if (bp == NULL || bp_len == 0) {
1199         crlf(buf);
1200         return;
1201     }
1202
1203
1204     /*
1205     // A line buffer for improving formatting performance. Note that
1206     // QP requires all lines to be < 72 characters plus CRLF. So, a
1207     // fixed size 80 character buffer is safe.
1208     */
1209
1210     /* There are probably more elegant ways to deal with a message that
1211     // begins with "From ", but we will simply due it this more simplistic
1212     // way.
1213     */
1214     if (strncmp(bp, "From ", 5) == 0) {
1215         memcpy(&line_buf[off], "=46", 3);
1216         start = bp + 1;
1217         off += 3;
1218     }
1219     else {
1220         start = bp;
1221     }
1222
1223     /* This loop will apply the encodings, following the rules identified
1224     // in RFC1521 (though not necessarily in the order presented.
1225     */
1226     for (cur = start; cur < (bp + bp_len); cur++) {
1227
1228         /* Rule #5: Part 1! We will try to break at white space
1229         // if possible, but it may not be possible. In any case,
1230         // we want to force the lines to be less than 76 characters.
1231         */
1232         if (off > 72) {
1233             line_buf[off++] = '=';
1234             strncat(buf,line_buf, off);
1235             crlf(buf);
1236             last_nl = 0;
1237             off = 0;
1238         }
1239
1240         /* Rule #1: Any octet, except those indicating a line break
1241         // according to the newline convention mabe represented by
1242         // an = followed by a two digit hexadecimal representation
1243         // of the octet's value. We will represent any non-7bit
1244         // data this way, but let the rest slide. We do wrap "="
1245         // just to be safe.
1246         */
1247         if (*cur != (*cur & 0x7f) || *cur == '=') {
1248             char tmp[20];
1249             sprintf(tmp, "=%02X", (int)(unsigned char)*cur);
1250             memcpy(&line_buf[off], tmp, 3);
1251             off += 3;
1252             continue;
1253         }
1254         if ( is_Special ){
1255         /*
1256          * Under ISO-2022-XX codeset, several escape sequence may be in
1257          * From, Subject field. To pass them, writeQPrint() also accept
1258          * such kind of character.
1259          */
1260             if ( *cur == (char)0x1b ) {
1261                 /* Only 0x1b ????? */
1262                 char tmp[3];
1263                 sprintf(tmp, "=%02X", (int)(unsigned char)*cur);
1264                 memcpy(&line_buf[off], tmp, 3);
1265                 off += 3;
1266                 continue;
1267             }
1268         }
1269
1270         /* Rule #2: Octets with decimal values of 33 through 60
1271         // inclusive and 62 through 126, inclusive, MAY be represented
1272         // as the ASCII characters which correspond to those octets.
1273         */
1274         if ((*cur >= 33 && *cur <= 60) ||
1275             (*cur >= 62 && *cur <= 126)) {
1276             line_buf[off++] = *cur;
1277             continue;
1278         }
1279
1280         /* Rule #5: The q-p encoding REQUIRES that encoded lines be
1281         // no more than 76 characters long. If longer, an equal sign
1282         // as the last character n the line indicates a soft line break.
1283         //
1284         // This is tricky if you want to leave it reasonably readable
1285         // (why else do this?). We only want to break on white space.
1286         // At each white gap, we need to count forward to the next
1287         // white gap and see if we exceed the 76 character limit.
1288         // We will cheat a few characters to allow us some room
1289         // for arithmetic.
1290         */
1291         if (*cur == ' ' || *cur == '\t') {
1292             /* Find the end of this clump of white space.
1293             */
1294             for (nw = cur;
1295                  nw < (bp + bp_len) && *nw && *nw != '\n'; nw++) {
1296                 if (!isspace(*nw)) {
1297                     break;
1298                 }
1299             }
1300
1301             /* Find the end of the next non-white region.
1302             */
1303             for (white = nw;
1304                  white < (bp + bp_len) && *white && !isspace(*white);
1305                  white++) {
1306                 continue;
1307             }
1308
1309             line_len = (off - last_nl) + (white - cur);
1310             if (line_len > 72) {
1311                 /* Need a soft line break. Lets put it after the
1312                 // current clump of white space. We will break
1313                 // at 72 characters, even if we arent at the end
1314                 // of the white space. This prevents buffer overruns.
1315                 */
1316                 for (cp_w = cur; cp_w < nw; cp_w++) {
1317                     line_buf[off++] = *cp_w;
1318                     if (off > 72) {
1319                         line_buf[off++] = '=';
1320                         strncat(buf,line_buf, off);
1321                         crlf(buf);
1322                         off = 0;
1323                         last_nl = 0;
1324                     }
1325                 }
1326
1327                 /* There is an edge case that we may have written the last
1328                 // white space character in the for loop above. This will
1329                 // prevent us from spitting an extra continuation line.
1330                 */
1331                 if (off) {
1332                     line_buf[off++] = '=';
1333                     strncat(buf,line_buf, off);
1334                     crlf(buf);
1335                     last_nl = 0;
1336                     off = 0;
1337                 }
1338
1339                 /* If we created a "From " at the front we need to wrap
1340                 // it to protect from parsers.
1341                 */
1342                 if ((nw + 5) < (bp + bp_len) && strncmp(nw, "From ", 5) == 0) {
1343                     memcpy(&line_buf[off], "=46", 3);
1344                     off += 3;
1345                     cur = nw + 1;
1346                 }
1347                 else {
1348                     cur = nw - 1;
1349                 }
1350             }
1351             else {
1352                 line_buf[off++] = *cur;
1353             }
1354
1355             continue;
1356         }
1357
1358         /* Rule 3: Octets with values of 9 and 32 MAY be represented
1359         // as ASCII TAB and SPACE but MUST NOT be represented at the
1360         // end of an encoded line. We solve this be encoding the last
1361         // white space before a new line (except a new line) using
1362         // Rule #1.
1363         */
1364         if (*cur == '\n') {
1365             if (cur == start) {
1366                 crlf(buf);
1367             }
1368             else {
1369                 last_nl = off + 1;
1370                 
1371                 prev = *(cur - 1);
1372                 if ((prev == ' ' || prev == '\t') && prev != '\n') {
1373                     off = off ? off - 1 : off;
1374                     
1375                     sprintf(tmpbuf, "=%02X", *(cur - 1));
1376                     memcpy(&line_buf[off], tmpbuf, 3);
1377                     off += 3;
1378                 }
1379
1380                 strncat(buf,line_buf, off);
1381                 last_nl = 0;
1382                 off = 0;
1383
1384                 if (*(cur - 1) == '\r') {
1385                     strncat(buf,cur, 1);
1386                 }
1387 /*
1388                 else {
1389                     crlf(buf);
1390                 }
1391 */
1392             }
1393             /* We need to munge a line that starts with "From " to it
1394             // protect from parsers. The simplest way is to encode the
1395             // "F" using rule #1.
1396             */
1397             if ((cur + 5) < (bp + bp_len) && strncmp((cur + 1), "From ", 5) == 0) {
1398                 memcpy(&line_buf[off], "=46", 3);
1399                 off += 3;
1400                 cur += 1;
1401             }
1402             continue;
1403         }
1404     }
1405
1406     if (off > 0) {
1407         strncat(buf,line_buf, off);
1408     }
1409 /*
1410     if (*(cur - 1) != '\n') {
1411         crlf(buf);
1412     }
1413 */
1414 }
1415
1416
1417 static int
1418 mbisspace(int c)
1419 {
1420     return((c & 0x7f) == c && isspace(c));
1421 }
1422
1423 void
1424 rfc1522cpy(char * buf, const char * value)
1425 {
1426     const char * cur;
1427     const char * scan_c;
1428     boolean_t eight_bit = B_FALSE;
1429     char charset[64];
1430     char *ret_locale = NULL;
1431     char *ret_lang = NULL;
1432     char *ret_codeset = NULL;
1433     char  tmp[1024];
1434     char *NewBuf = NULL;
1435     unsigned long _len = 0;
1436     /*
1437     // We are going to encode 8 bit data, one word at a time. This may
1438     // not be the best possible algorithm, but it will get the correct
1439     // information in the header.
1440     */
1441     for (cur = value; *cur; cur++) {
1442         if (mbisspace(*cur)) {
1443             strncat(buf,cur, 1);
1444             continue;
1445         }
1446
1447         for (scan_c = cur; *scan_c && !mbisspace(*scan_c); scan_c++) {
1448             if (*scan_c != (*scan_c & 0x7f)) {
1449                 eight_bit = B_TRUE;
1450             }
1451         }
1452
1453         if (eight_bit == B_FALSE) {
1454             /* Simple! Copy the chars to the output. */
1455             strncat(buf,cur,scan_c - cur);
1456             cur = scan_c - 1;
1457         }
1458         else {
1459
1460             getCharSet( charset );
1461             /* Convert default_charset to InterchangeCodeset name. */
1462             DtXlateOpToStdLocale( DtLCX_OPER_MIME,
1463                                 charset,
1464                                 &ret_locale,
1465                                 &ret_lang,
1466                                 &ret_codeset );
1467
1468             /* We have a word here. It has 8 bit data, so we will put
1469             // it out as RFC1522 chunk.
1470             */
1471
1472             if ( !strncasecmp( ret_codeset, "ISO-2022-KR", 11 ) ) {
1473             /*
1474              * According to RFC1557, in the Header Field, we don't use
1475              * ISO-2022-KR encoding char.
1476              */
1477                 char *_tmp1_;
1478                 char *_tmp2_;
1479                 ret_locale = ret_lang = _tmp1_ = NULL;
1480                 DtXlateOpToStdLocale( DtLCX_OPER_INTERCHANGE_CODESET,
1481                                         ret_codeset,
1482                                         &ret_locale,
1483                                         &ret_lang,
1484                                         &_tmp1_ );
1485                 _tmp2_ = NULL;
1486                 DtXlateStdToOpLocale( DtLCX_OPER_MIME,
1487                                 NULL,
1488                                 NULL,
1489                                 _tmp1_,
1490                                 NULL,
1491                                 &_tmp2_ );
1492                 strncat(buf,"=?", 2);
1493                 strncat(buf,_tmp2_, strlen(_tmp2_));
1494                 strncat(buf,"?q?", 3);
1495             } else if ( !strncasecmp( ret_codeset, "ISO-2022-JP", 11 ) ) {
1496             /*
1497              * According to RFC1468, in the Header Field, we should use
1498              * B-encoding
1499              */
1500                 strncat(buf,"=?", 2);
1501                 strncat(buf,charset, strlen(charset));
1502                 strncat(buf,"?b?", 3);
1503             } else {
1504                 strncat(buf,"=?", 2);
1505                 strncat(buf,charset, strlen(charset));
1506                 strncat(buf,"?q?", 3);
1507             }
1508
1509             /*
1510              * According to RFC1557, in the Header Field, we don't use
1511              * ISO-2022-KR encoding char. Also in us-ascci, we don't have to
1512              * call converter.
1513              */
1514             memset(tmp, '\0', 1024);
1515             if (!( !strncasecmp( ret_codeset, "ISO-2022-KR", 11 ) ||
1516                    !strncasecmp( charset, "us-ascii", 8 ) )          )
1517                 (void)CvtStr( (char *)NULL, (void *)cur, scan_c - cur,
1518                                 (void **)&NewBuf, &_len, CURRENT_TO_INTERNET );
1519             if ( ( NewBuf != NULL ) && ( _len != 0 ) ) {
1520                 /*
1521                  * if ret_codeset == ISO-2022-KR, we don't come here.
1522                  */
1523                 /*
1524                  * According to RFC1468, we should use B-encoding.
1525                  */
1526                 if ( !strncasecmp( ret_codeset, "ISO-2022-JP", 11 ) ) {
1527                     writeBase64( tmp, NewBuf, _len );
1528                 } else {
1529                     writeQPrint( tmp, NewBuf, _len, 
1530                            (!strncasecmp( ret_codeset, "ISO-2022-TW", 11 ) ||
1531                             !strncasecmp( ret_codeset, "ISO-2022-CN", 11 )   ));
1532                 }
1533             } else
1534                 writeQPrint( tmp, cur, scan_c - cur, 0 );
1535
1536             strncat(buf,tmp,strlen(tmp));
1537             strcat(buf,"?=");
1538             cur = scan_c - 1;
1539         }
1540     }
1541
1542     crlf(buf);
1543 }