2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 static char sccsid[] = "$TOG: RFCMIME.c /main/11 1999/06/30 12:08:55 mgreess $";
25 * COMPONENT_NAME: desktop
28 * DtXlateOpToStdLocale
29 * DtXlateStdToOpLocale
45 * OBJECT CODE ONLY SOURCE MATERIALS
60 #include <EUSCompat.h>
62 #include <LocaleXlate.h>
64 #ifdef ICONV_INBUF_CONST
65 # define ICONV_INBUF_TYPE const char **
67 # define ICONV_INBUF_TYPE char **
70 #define WORKSIZE 1024*10
72 * The following escape sequence is defined as "To ASCII".
73 * But is it correct regardless of ISO-2022-XX ???
76 static char ToASCII[ToASCII_NUM] = { 0x1b, 0x28, 0x42 };
79 * _i18nwork1[] is used to convert the passed string with CD iconv.
83 static void *_i18nwork1 = NULL;
84 static unsigned long _i18nsize1 = 0;
85 static int shouldAlloc1 = ~0;
88 * _i18nwork2[] is used to convert the passed string with CD iconv.
92 static void *_i18nwork2 = NULL;
93 static unsigned long _i18nsize2 = 0;
94 static int shouldAlloc2 = ~0;
97 * Forward declarations
99 extern void writeBase64(char * buf, const char * bp, const unsigned long len);
101 static const char *DfltStdCharset = "us-ascii";
102 static const char *DfltStdLang = "C";
104 static void crlf(char *buf)
109 /******************************************************************************
110 * Function: int DtXlateOpToStdLocale (char *operation, char *opLocale,
111 * char **ret_stdLocale, char **ret_stdLang, char **ret_stdSet)
114 * operation Operation associated with the locale value
115 * opLocale An operation-specific locale string
116 * ret_locale Returns the std locale
117 * Caller must free this string.
118 * ret_stdLang Returns the std language & territory string.
119 * Caller must free this string.
120 * ret_stdSet Returns the std code set string.
121 * Caller must free this string.
125 * Purpose: Gets the standard locale given an operation and its locale
127 *****************************************************************************/
129 DtXlateOpToStdLocale (
132 char **ret_stdLocale,
136 _DtXlateDb MyDb = NULL;
137 char MyPlatform[_DtPLATFORM_MAX_LEN + 1];
141 if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
142 _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
144 _DtLcxCloseDb(&MyDb);
151 (void) _DtLcxXlateOpToStd(MyDb, MyPlatform, ExecVer,
153 ret_stdLocale, ret_stdLang, ret_stdSet, NULL);
156 /* if failed, give default values */
157 if (ret_stdLocale != NULL && *ret_stdLocale == NULL)
159 *ret_stdLocale = (char *)malloc(strlen(DfltStdLang)+strlen(DfltStdCharset)+3);
160 sprintf(*ret_stdLocale,"%s.%s",DfltStdLang,DfltStdCharset);
162 if (ret_stdLang != NULL && *ret_stdLang == NULL)
163 *ret_stdLang = (char *)strdup(DfltStdLang);
164 if (ret_stdSet != NULL && *ret_stdSet == NULL)
165 *ret_stdSet = (char *)strdup(DfltStdCharset);
168 /******************************************************************************
169 * Function: int DtXlateStdToOpLocale ( char *operation, char *stdLocale,
170 * char *stdLang, char *stdCodeSet,
171 * char *dflt_opLocale, char **ret_opLocale)
174 * operation operation whose locale value will be retrieved
175 * stdLocale standard locale value
176 * stdLang standard Lang/Territory Value
177 * stdCodeSet standard CodeSet Value
178 * dflt_opLocale operation-specific locale-value
179 * This is the default value used in error case
180 * ret_opLocale operation-specific locale-value placed here
181 * Caller must free this string.
185 * Purpose: Gets an operation-specific locale string given the standard string
187 *****************************************************************************/
189 DtXlateStdToOpLocale (
197 _DtXlateDb MyDb = NULL;
198 char MyPlatform[_DtPLATFORM_MAX_LEN + 1];
202 if (_DtLcxOpenAllDbs(&MyDb) == 0 &&
203 _DtXlateGetXlateEnv(MyDb,MyPlatform,&ExecVer,&CompVer) != 0)
205 _DtLcxCloseDb(&MyDb);
212 (void) _DtLcxXlateStdToOp(MyDb, MyPlatform, ExecVer,
213 operation, stdLocale, stdLang, stdCodeSet, NULL,
217 /* if translation fails, use a default value */
218 if (ret_opLocale && *ret_opLocale == NULL)
220 if (dflt_opLocale) *ret_opLocale = (char *)strdup(dflt_opLocale);
221 else if (stdLocale) *ret_opLocale = (char *)strdup(stdLocale);
227 char *ret_locale = NULL;
228 char *ret_lang = NULL;
229 char *ret_codeset = NULL;
230 char *ret_target = NULL;
232 DtXlateOpToStdLocale(DtLCX_OPER_SETLOCALE,
233 setlocale(LC_CTYPE, NULL),
237 DtXlateStdToOpLocale(DtLCX_OPER_INTERCHANGE_CODESET,
243 DtXlateStdToOpLocale(DtLCX_OPER_MIME,
258 getCharSet(char * charset)
262 mimeCS = targetTagName();
265 strcpy(charset, mimeCS);
268 strcpy(charset, "us-ascii"); /* default MIME codeset */
274 md5PlainText(const char * bp, const unsigned long len, unsigned char * digest)
276 /* We need to compute the md5 signature based on a message that has
277 // the CRLF line terminator. Most of our buffers don't so we will need
278 // to scan the body and do some magic. The approach will be to sum
279 // one line at a time. If the buffer doesn't have CRLF we will do that
284 unsigned char * local_crlf = (unsigned char *)"\r\n";
285 const char * last = bp;
289 for (cur = bp; cur < (bp + len); cur++) {
291 if (cur == bp || *(cur - 1) == '\r') {
292 MD5Update(&context, (unsigned char *)last,
296 MD5Update(&context, (unsigned char *)last,
298 MD5Update(&context, local_crlf, 2);
304 if (bp[len - 1] != '\n') {
305 /* Need to sum the trailing fraction with a CRLF. */
306 MD5Update(&context, (unsigned char *)last,
308 MD5Update(&context, local_crlf, 2);
311 MD5Final(digest, &context);
315 static void _converter_( iconv_t CD,
316 void *from, unsigned long from_len,
317 void **to, unsigned long *to_len )
322 size_t OutBytesLeft = 0;
323 size_t _OutBytesLeft = 0;
325 size_t converted_num = 0;
331 if ( shouldAlloc1 ) {
332 /* Obtain work area */
333 _i18nwork1 = (size_t *)malloc( WORKSIZE );
338 _i18nsize1 = WORKSIZE;
342 InBuf = (char *)from;
343 InBytesLeft = from_len;
344 OutBytesLeft = _i18nsize1;
345 OutBuf = (char *)_i18nwork1;
348 * Need to place iconv state to the initial one by
349 * setting inbuf to NULL of iconv().
351 iconv( CD, (ICONV_INBUF_TYPE)NULL, 0, NULL, 0 );
356 * +----------------------------+
358 * +----------------------------+
359 * <-------------------------->
368 * +----------------------------+
370 * +----------------------------+
371 * <-------------------------->
375 iconv_ret = iconv( CD, (ICONV_INBUF_TYPE)&InBuf, &InBytesLeft,
376 &OutBuf, &OutBytesLeft );
377 if ( iconv_ret == 0 ) {
381 * +----------------------------+
382 * |XXXXXXXXXXXXXXXXXXXXXXXXXXXX|
383 * +----------------------------+
390 * +----------------------------+
391 * |XXXXXXXXXXXXXXXXX| | | |
392 * +----------------------------+
393 * <---------------> <-------->
394 * converted_num OutBytesLeft
396 converted_num = (unsigned long)((char *)OutBuf-(char *)_i18nwork1);
397 *to = (void *)_i18nwork1;
398 *to_len = (unsigned long)converted_num;
401 if ( errno == E2BIG ) {
402 /* Overflow. still data is left.
405 * +----------------------------+
406 * |XXXXXXXXXXXXXX| | | |
407 * +----------------------------+
414 * +----------------------------+
415 * |XXXXXXXXXXXXXXXXXXXXXXXXXXX |
416 * +----------------------------+
417 * <------------------------->
418 * converted_num OutBytesLeft=?
422 /* Check how many converted already.
425 (unsigned long)((char *)OutBuf - (char *)_i18nwork1);
426 _i18nsize1 += WORKSIZE;
427 _p = realloc( _i18nwork1, _i18nsize1 );
438 OutBuf = (char *)((char*)_i18nwork1 + converted_num);
439 OutBytesLeft += WORKSIZE;
452 CvtStr( char *charSet, void *from, unsigned long from_len,
453 void **to, unsigned long *to_len, Direction dir )
455 char *ret_locale = NULL;
456 char *ret_lang = NULL;
457 char *ret_codeset = NULL;
458 char *from_codeset = NULL;
459 char *to_codeset = NULL;
460 char *CuStdCodeSet = NULL;
461 char *InterChCodeSet = NULL;
462 char *StdCodeSet = NULL;
465 int isStopASCII = ~0;
466 unsigned long converted_num = 0;
468 /* Get CuStdCodeSet */
469 DtXlateOpToStdLocale( DtLCX_OPER_SETLOCALE,
470 setlocale( LC_CTYPE, NULL ),
476 * If charSet is NULL, it means the passed string's charset in *from is
477 * unknown by dtmail. In this case, this converter assumes that
478 * when dir = CURRENT_TO_INTERNET,
479 * *from's encoding is the current locale's one.
480 * when dir = INTERNET_TO_CURRENT,
481 * *from's encoding is the current locale's Internet Message's one.
484 * dtmail is running under ja_JP locale.
485 * dir : CURRENT_TO_INTERNET
488 * dir : INTERNET_TO_CURRENT
489 * *from = ISO-2022-JP
493 * ISO-2022-JP can be converted to either EUC-JP or IBM-932 practically.
494 * But the current AIX.lcx says
495 * StdCodeSet InterchangeCodeset
496 * EUC-JP <--> ISO-2022-JP
497 * IBM-932 ---> ISO-2022-JP
498 * HP-SJIS ---> ISO-2022-JP
499 * HP-KANA8 ---> ISO-2022-JP
500 * therefore DtXlateOpToStdLocale() can convert ISO-2022-JP to EUC-JP only.
501 * To fix this, we hard-code'ed this situation with the CDE Standard Name
503 * ???? Is it correct ???
505 if ( dir == INTERNET_TO_CURRENT ) {
507 * As for from_codeset
509 if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
510 /* Convert CuStdCodeSet to StdInterChangeCodeSet */
511 DtXlateStdToOpLocale( DtLCX_OPER_INTERCHANGE_CODESET,
518 /* Convert charSet to StdInterChangeCodeSet */
522 ret_locale = ret_lang = ret_codeset = NULL;
523 DtXlateOpToStdLocale( DtLCX_OPER_MIME,
530 /* Convert StdInterChangeCodeSet to OpIVONC3 codeset */
531 DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
540 if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
541 /* Convert CuStdCodeSet to OpIVONC3 codeset */
542 DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
550 if ( (!strncasecmp(InterChCodeSet,"ISO-2022-JP",11) &&
551 !strncasecmp(CuStdCodeSet,"IBM-932",7) ) ||
552 (!strncasecmp(InterChCodeSet,"ISO-2022-JP",11) &&
553 !strncasecmp(CuStdCodeSet,"EUC-JP",6) ) ) {
554 ret_codeset = CuStdCodeSet;
558 /* Convert InterChCodeSet to StdCodeSet */
562 ret_locale = ret_lang = ret_codeset = NULL;
563 DtXlateOpToStdLocale( DtLCX_OPER_INTERCHANGE_CODESET,
569 DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
576 } else { /* dir == CURRENT_TO_INTERNET */
578 * As for from_codeset
580 if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
581 /* Convert CuStdCodeSet to OpICONV3 codeset */
582 DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
589 /* Convert charSet to StdInterChangeCodeSet */
593 ret_locale = ret_lang = ret_codeset = NULL;
594 DtXlateOpToStdLocale( DtLCX_OPER_MIME,
599 /* Convert StdInterChangeCodeSet to OpIVONC3 codeset */
600 DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
610 if ( ( charSet == NULL ) || ( *charSet == '\0' ) ) {
611 /* Convert CuStdCodeSet to StdInterChangeCodeSet */
612 DtXlateStdToOpLocale( DtLCX_OPER_INTERCHANGE_CODESET,
619 /* Convert charSet to StdInterChangeCodeSet */
623 ret_locale = ret_lang = ret_codeset = NULL;
624 DtXlateOpToStdLocale( DtLCX_OPER_MIME,
631 /* Convert StdInterChangeCodeSet to OpIVONC3 codeset */
632 DtXlateStdToOpLocale( DtLCX_OPER_ICONV3,
647 if ( shouldAlloc2 ) {
648 /* Obtain work area */
649 _i18nwork2 = (size_t *)malloc( WORKSIZE );
654 _i18nsize2 = WORKSIZE;
658 if (NULL == to_codeset || NULL == from_codeset)
661 if ( ( CD = iconv_open( to_codeset, from_codeset ) ) != (iconv_t)-1 ) {
663 * According to several RFCs( 822, 1468, 1557, ..... )
664 * the escape sequence to switch to ASCII is needed just before
665 * '\n'. IBM-eucJP/IBM-932 <--> fold 7 does while the other doesn't.
666 * Therefore CvtStr() does take care of this here.
668 if ( dir == INTERNET_TO_CURRENT ) {
669 _converter_( CD, from, from_len, to, to_len );
671 void *new_from = from;
672 unsigned long new_from_len = from_len;
673 unsigned long _passed = 0;
676 unsigned long _tmp_len = 0;
678 while ( _passed < from_len ) {
682 for ( ; _passed < from_len; _passed += clen ) {
683 clen = mblen(&(((char *)from)[_passed]), MB_CUR_MAX);
687 if ( ( clen > 1 ) || !isascii( ((char*)from)[_passed] ) ){
688 /* Here, maybe MB or non-ASCII */
692 if ( ( ((char*)from)[_passed] != '\n' ) &&
693 ( ((char*)from)[_passed] != '\0' ) ) {
698 if ( ((char*)from)[_passed] == '\n' ||
699 ((char*)from)[_passed] == '\0' )
702 new_from_len = &(((char *)from)[_passed])-(char *)new_from;
703 if ( ( _passed < from_len ) && ( clen == 1 ) &&
704 ( ((char*)from)[_passed] == '\n' ) ) {
709 * new_from from[_passed]
711 * +------------------------+--+------.................+
713 * +------------------------+--+------.................+
714 * <-------------------------> $
715 * new_from_len next new_from
716 * <------------------------------------------------->
722 * ********** DO 1 LINE CONVERSION **********
724 _tmp = NULL; _tmp_len = 0;
725 _converter_( CD, new_from, new_from_len, &_tmp, &_tmp_len );
727 if ( ( _tmp == NULL ) && ( _tmp_len == 0 ) ) {
728 /* Conversion fail */
737 * +-----------------------+ +-------------+
739 * +-----------------------+ +-------------+
742 * <---------------------> <----------->
745 /* Append _tmp to target */
746 if ( converted_num + _tmp_len > _i18nsize2 ) {
747 /* Need much memory..... */
750 _i18nsize2 += WORKSIZE;
751 _i18n = realloc( _i18nwork2, _i18nsize2 );
767 * +---------------------------+
768 * |XXXXXXXXXXX(COPIED)XX |
769 * +---------------------------+
770 * <---------><-------->
771 * (old)conv. _tmp_len
772 * <------------------->
774 * <-------------------------->
777 strncpy( (char *)_i18nwork2 + converted_num,
778 (char *)_tmp, _tmp_len );
779 converted_num += _tmp_len;
781 *to = (void *)_i18nwork2;
782 *to_len = converted_num;
783 new_from = &(((char *)from)[_passed]) + 1;
787 * According to RFC1468, if the line is ended with non-ASCII
788 * char, but not not switch to ASCII before the end of line,
789 * we must switch to ASCII just before the end of the line.
793 * +---------------------------+ +------+
794 * |XXXXXXXXXXXXXXXXXXXXX | + |1b2842|
795 * +---------------------------+ +------+
796 * <-------------------> <---->
797 * converted_num ToASCII_NUM
798 * <------------------------->
804 if ( !isStopASCII ) {
805 if ( (((char *)_i18nwork2)[converted_num-1] == '\n') ||
806 (((char *)_i18nwork2)[converted_num-1] == '\0') ) {
808 char _p = ((char *)_i18nwork2)[converted_num-1];
810 if (!((converted_num >=3+1 ) &&
811 !memcmp((void *)((char *)_i18nwork2+converted_num-3-1),
812 ToASCII, ToASCII_NUM )) ){
813 /* if not ToASCII there, then */
816 if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
817 /* Need much memory..... */
820 _i18nsize2 += WORKSIZE;
821 _i18n=realloc(_i18nwork2,_i18nsize2);
831 strncpy( (char *)_i18nwork2+converted_num-1,
832 ToASCII, ToASCII_NUM );
833 converted_num += ToASCII_NUM;
834 ((char *)_i18nwork2)[converted_num-1] = _p;
836 *to_len = converted_num;
840 if (!((converted_num >=3 ) &&
841 !memcmp((void *)((char *)_i18nwork2+converted_num-3),
842 ToASCII, ToASCII_NUM )) ){
844 // if not ToASCII there, then
847 if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
850 _i18nsize2 += WORKSIZE;
851 _i18n=realloc(_i18nwork2,_i18nsize2);
861 strncpy( (char *)_i18nwork2+converted_num,
862 ToASCII, ToASCII_NUM );
863 converted_num += ToASCII_NUM;
865 *to_len = converted_num;
874 if( ( *to != NULL ) && ( *to_len != 0 ) ) {
875 if ( !isStopASCII ) {
876 if ( (((char *)_i18nwork2)[converted_num-1] == '\n') ||
877 (((char *)_i18nwork2)[converted_num-1] == '\0') ) {
879 char _p = ((char *)_i18nwork2)[converted_num-1];
881 if (!((converted_num >=3+1 ) &&
882 !memcmp((void *)((char *)_i18nwork2+converted_num-3-1),
883 ToASCII, ToASCII_NUM )) ){
884 /* if not ToASCII there, then
888 if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
891 _i18nsize2 += WORKSIZE;
892 _i18n=realloc(_i18nwork2,_i18nsize2);
901 strncpy( (char *)_i18nwork2+converted_num-1,
902 ToASCII, ToASCII_NUM );
903 converted_num += ToASCII_NUM;
904 ((char *)_i18nwork2)[converted_num-1] = _p;
906 *to_len = converted_num;
910 if (!((converted_num >=3 ) &&
911 !memcmp((void *)((char *)_i18nwork2+converted_num-3),
912 ToASCII, ToASCII_NUM )) ){
913 /* if not ToASCII there, then
917 if ( converted_num + ToASCII_NUM > _i18nsize2 ) {
920 _i18nsize2 += WORKSIZE;
921 _i18n=realloc(_i18nwork2,_i18nsize2);
930 strncpy( (char *)_i18nwork2+converted_num,
931 ToASCII, ToASCII_NUM );
932 converted_num += ToASCII_NUM;
934 *to_len = converted_num;
941 * If InterChCodeSet is either ISO-2022-JP/ISO-2022-KR/ISO-2022-TW
942 * ISO-2022-CN, strip MSB here since iconv'ed UDC has MSB set to 1
944 if ( !strncasecmp( InterChCodeSet, "ISO-2022-JP", 11 ) ||
945 !strncasecmp( InterChCodeSet, "ISO-2022-KR", 11 ) ||
946 !strncasecmp( InterChCodeSet, "ISO-2022-TW", 11 ) ||
947 !strncasecmp( InterChCodeSet, "ISO-2022-CN", 11 ) ) {
950 for ( _i = 0; _i < *to_len; _i++ ) {
951 ((unsigned char *)(*to))[_i] &= (unsigned char)0x7f;
960 base64size(const unsigned long len)
962 int b_len = len + (len / 3);
963 b_len += (b_len / 72 * 2) + 4;
968 getEncodingType(const char * body,
969 const unsigned int len,
970 boolean_t strict_mime)
973 // Our goal here is to produce the most readable, safe encoding.
974 // We have a couple of parameters that will guide our
977 // 1) RFC822 allows lines to be a minimum of 1000 characters,
978 // but MIME encourages mailers to keep lines to <76 characters
979 // and use quoted-printable if necessary to achieve this.
981 // 2) The base64 encoding will grow the body size by 33%, and
982 // also render it unreadable by humans. We don't want to use
983 // it unless really necessary.
985 // Given the above 2 rules, we want to scan the body part and
986 // select an encoding. The 3 choices will be decided by:
988 // 1) If the text is 7 bit clean, and all lines are <76 chars,
989 // then no encoding will be applied.
992 // 2) If the text is not 7 bit clean, or there are lines >76 chars,
993 // and the quoted-printable size is less than the base64 size,
994 // then quoted-printable will be done.
996 // 3) If 1 & 2 are not true, then base64 will be applied.
998 // If "strict_mime" is false we will only encode if the message
999 // is not 7 bit clean.
1003 int base64_growth = base64size(len) - len;
1004 int qprint_growth = 0;
1005 boolean_t eight_bit = B_FALSE;
1006 boolean_t base64 = B_FALSE;
1007 boolean_t encode = B_FALSE;
1010 const char * last_nl = body;
1013 if (body == NULL || len == 0) {
1016 if (strncmp(body, "From ", 5) == 0) {
1020 for (cur = body; cur < (body + len); cur++) {
1021 if (*cur != (*cur & 0x7f)) {
1025 } else if (*cur == '=' || *cur == 0) {
1027 // These characters don't force encoding, but will be
1028 // encoded if we end up encoding.
1034 if ((cur - last_nl) > 76) {
1040 if ((cur != body && (*(cur - 1) == ' ' || *(cur - 1) == '\t'))) {
1045 if ((cur + 6) < (body + len) &&
1046 strncmp((cur + 1), "From ", 5) == 0) {
1054 if (encode && (qprint_growth > base64_growth)) {
1060 /* Deal with buffers that don't end with a new line. */
1062 if ((cur - last_nl) > 76) {
1069 if (!strict_mime && !eight_bit) {
1070 /* If strict_mime is off we only encode if we have 8 bit data */
1072 } else if (encode) {
1073 /* strict_mime is TRUE and we have reason to encode. */
1085 writeContentHeaders(char * hdr_buf,
1088 const char * digest,
1091 char default_charset[64];
1093 strcat(hdr_buf,"Content-Type: ");
1094 strcat(hdr_buf,type);
1097 strcpy(default_charset,"US-ASCII");
1099 getCharSet(default_charset);
1101 strcat(hdr_buf,"; charset=");
1103 strcat(hdr_buf,default_charset);
1107 strcat(hdr_buf,"Content-Transfer-Encoding: ");
1111 strcat(hdr_buf,"7bit\n");
1115 default: /* Assume the worst. */
1116 strcat(hdr_buf,"8bit\n");
1119 strcat(hdr_buf,"quoted-printable\n");
1123 strcat(hdr_buf,"base64\n");
1127 strcat(hdr_buf,"Content-MD5: ");
1128 writeBase64(hdr_buf, digest, 16);
1132 // Base64 Alphabet (65-character subset of US-ASCII as per RFC1521)
1136 static const char base64_chars[] =
1137 {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1138 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a',
1139 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
1140 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
1141 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
1145 writeBase64(char * buf, const char * bp, const unsigned long len)
1148 /* The length has to be a multiple of 3. We will need to pad
1149 // any extra. Let's just work on the main body and save the
1150 // fractional stuff for the end.
1152 unsigned long main_len = len - (len % 3);
1153 const unsigned char * ubp = (const unsigned char *)bp;
1157 unsigned int enc_char;
1163 if (bp == NULL || len == 0) {
1168 for (block = 0; block < main_len; block += 3) {
1169 enc_char = (ubp[block] >> 2) & 0x3f;
1170 line[lf++] = base64_chars[enc_char];
1172 enc_char = ((ubp[block] & 0x3) << 4) | ((ubp[block+1] >> 4) & 0xf);
1173 line[lf++] = base64_chars[enc_char];
1175 enc_char = ((ubp[block + 1] & 0xf) << 2) | ((ubp[block + 2] >> 6) & 0x3);
1176 line[lf++] = base64_chars[enc_char];
1178 enc_char = ubp[block + 2] & 0x3f;
1179 line[lf++] = base64_chars[enc_char];
1182 strncat(buf,line,lf);
1189 strncat(buf, line,lf);
1192 if (((lf + 4) % 72) == 0) {
1198 enc_char = (ubp[block] >> 2) & 0x3f ;
1199 strncat(buf, &base64_chars[enc_char], 1);
1201 enc_char = ((ubp[block] & 0x3) << 4);
1202 strncat(buf, &base64_chars[enc_char], 1);
1204 strncat(buf,"==", 2);
1208 enc_char = (ubp[block] >> 2) & 0x3f;
1209 strncat(buf,&base64_chars[enc_char], 1);
1211 enc_char = ((ubp[block] & 0x3) << 4) | ((ubp[block+1] >> 4) & 0xf);
1212 strncat(buf,&base64_chars[enc_char], 1);
1214 enc_char = ((ubp[block + 1] & 0xf) << 2);
1215 strncat(buf,&base64_chars[enc_char], 1);
1217 strncat(buf,"=", 1);
1223 writeQPrint(char *buf, const char * bp, const unsigned long bp_len,
1238 if (bp == NULL || bp_len == 0) {
1245 // A line buffer for improving formatting performance. Note that
1246 // QP requires all lines to be < 72 characters plus CRLF. So, a
1247 // fixed size 80 character buffer is safe.
1250 /* There are probably more elegant ways to deal with a message that
1251 // begins with "From ", but we will simply due it this more simplistic
1254 if (strncmp(bp, "From ", 5) == 0) {
1255 memcpy(&line_buf[off], "=46", 3);
1263 /* This loop will apply the encodings, following the rules identified
1264 // in RFC1521 (though not necessarily in the order presented.
1266 for (cur = start; cur < (bp + bp_len); cur++) {
1268 /* Rule #5: Part 1! We will try to break at white space
1269 // if possible, but it may not be possible. In any case,
1270 // we want to force the lines to be less than 76 characters.
1273 line_buf[off++] = '=';
1274 strncat(buf,line_buf, off);
1280 /* Rule #1: Any octet, except those indicating a line break
1281 // according to the newline convention mabe represented by
1282 // an = followed by a two digit hexadecimal representation
1283 // of the octet's value. We will represent any non-7bit
1284 // data this way, but let the rest slide. We do wrap "="
1287 if (*cur != (*cur & 0x7f) || *cur == '=') {
1289 sprintf(tmp, "=%02X", (int)(unsigned char)*cur);
1290 memcpy(&line_buf[off], tmp, 3);
1296 * Under ISO-2022-XX codeset, several escape sequence may be in
1297 * From, Subject field. To pass them, writeQPrint() also accept
1298 * such kind of character.
1300 if ( *cur == (char)0x1b ) {
1301 /* Only 0x1b ????? */
1303 sprintf(tmp, "=%02X", (int)(unsigned char)*cur);
1304 memcpy(&line_buf[off], tmp, 3);
1310 /* Rule #2: Octets with decimal values of 33 through 60
1311 // inclusive and 62 through 126, inclusive, MAY be represented
1312 // as the ASCII characters which correspond to those octets.
1314 if ((*cur >= 33 && *cur <= 60) ||
1315 (*cur >= 62 && *cur <= 126)) {
1316 line_buf[off++] = *cur;
1320 /* Rule #5: The q-p encoding REQUIRES that encoded lines be
1321 // no more than 76 characters long. If longer, an equal sign
1322 // as the last character n the line indicates a soft line break.
1324 // This is tricky if you want to leave it reasonably readable
1325 // (why else do this?). We only want to break on white space.
1326 // At each white gap, we need to count forward to the next
1327 // white gap and see if we exceed the 76 character limit.
1328 // We will cheat a few characters to allow us some room
1331 if (*cur == ' ' || *cur == '\t') {
1332 /* Find the end of this clump of white space.
1335 nw < (bp + bp_len) && *nw && *nw != '\n'; nw++) {
1336 if (!isspace(*nw)) {
1341 /* Find the end of the next non-white region.
1344 white < (bp + bp_len) && *white && !isspace(*white);
1349 line_len = (off - last_nl) + (white - cur);
1350 if (line_len > 72) {
1351 /* Need a soft line break. Lets put it after the
1352 // current clump of white space. We will break
1353 // at 72 characters, even if we arent at the end
1354 // of the white space. This prevents buffer overruns.
1356 for (cp_w = cur; cp_w < nw; cp_w++) {
1357 line_buf[off++] = *cp_w;
1359 line_buf[off++] = '=';
1360 strncat(buf,line_buf, off);
1367 /* There is an edge case that we may have written the last
1368 // white space character in the for loop above. This will
1369 // prevent us from spitting an extra continuation line.
1372 line_buf[off++] = '=';
1373 strncat(buf,line_buf, off);
1379 /* If we created a "From " at the front we need to wrap
1380 // it to protect from parsers.
1382 if ((nw + 5) < (bp + bp_len) && strncmp(nw, "From ", 5) == 0) {
1383 memcpy(&line_buf[off], "=46", 3);
1392 line_buf[off++] = *cur;
1398 /* Rule 3: Octets with values of 9 and 32 MAY be represented
1399 // as ASCII TAB and SPACE but MUST NOT be represented at the
1400 // end of an encoded line. We solve this be encoding the last
1401 // white space before a new line (except a new line) using
1412 if ((prev == ' ' || prev == '\t') && prev != '\n') {
1413 off = off ? off - 1 : off;
1415 sprintf(tmpbuf, "=%02X", *(cur - 1));
1416 memcpy(&line_buf[off], tmpbuf, 3);
1420 strncat(buf,line_buf, off);
1424 if (*(cur - 1) == '\r') {
1425 strncat(buf,cur, 1);
1433 /* We need to munge a line that starts with "From " to it
1434 // protect from parsers. The simplest way is to encode the
1435 // "F" using rule #1.
1437 if ((cur + 5) < (bp + bp_len) && strncmp((cur + 1), "From ", 5) == 0) {
1438 memcpy(&line_buf[off], "=46", 3);
1447 strncat(buf,line_buf, off);
1450 if (*(cur - 1) != '\n') {
1460 return((c & 0x7f) == c && isspace(c));
1464 rfc1522cpy(char * buf, const char * value)
1467 const char * scan_c;
1468 boolean_t eight_bit = B_FALSE;
1470 char *ret_locale = NULL;
1471 char *ret_lang = NULL;
1472 char *ret_codeset = NULL;
1474 char *NewBuf = NULL;
1475 unsigned long _len = 0;
1477 // We are going to encode 8 bit data, one word at a time. This may
1478 // not be the best possible algorithm, but it will get the correct
1479 // information in the header.
1481 for (cur = value; *cur; cur++) {
1482 if (mbisspace(*cur)) {
1483 strncat(buf,cur, 1);
1487 for (scan_c = cur; *scan_c && !mbisspace(*scan_c); scan_c++) {
1488 if (*scan_c != (*scan_c & 0x7f)) {
1493 if (eight_bit == B_FALSE) {
1494 /* Simple! Copy the chars to the output. */
1495 strncat(buf,cur,scan_c - cur);
1500 getCharSet( charset );
1501 /* Convert default_charset to InterchangeCodeset name. */
1502 DtXlateOpToStdLocale( DtLCX_OPER_MIME,
1508 /* We have a word here. It has 8 bit data, so we will put
1509 // it out as RFC1522 chunk.
1512 if ( !strncasecmp( ret_codeset, "ISO-2022-KR", 11 ) ) {
1514 * According to RFC1557, in the Header Field, we don't use
1515 * ISO-2022-KR encoding char.
1521 ret_locale = ret_lang = _tmp1_ = NULL;
1522 DtXlateOpToStdLocale( DtLCX_OPER_INTERCHANGE_CODESET,
1528 DtXlateStdToOpLocale( DtLCX_OPER_MIME,
1534 strncat(buf,"=?", 2);
1535 strncat(buf,_tmp2_, strlen(_tmp2_));
1536 strncat(buf,"?q?", 3);
1540 } else if ( !strncasecmp( ret_codeset, "ISO-2022-JP", 11 ) ) {
1542 * According to RFC1468, in the Header Field, we should use
1545 strncat(buf,"=?", 2);
1546 strncat(buf,charset, strlen(charset));
1547 strncat(buf,"?b?", 3);
1549 strncat(buf,"=?", 2);
1550 strncat(buf,charset, strlen(charset));
1551 strncat(buf,"?q?", 3);
1555 * According to RFC1557, in the Header Field, we don't use
1556 * ISO-2022-KR encoding char. Also in us-ascci, we don't have to
1559 memset(tmp, '\0', 1024);
1560 if (!( !strncasecmp( ret_codeset, "ISO-2022-KR", 11 ) ||
1561 !strncasecmp( charset, "us-ascii", 8 ) ) )
1562 (void)CvtStr( (char *)NULL, (void *)cur, scan_c - cur,
1563 (void **)&NewBuf, &_len, CURRENT_TO_INTERNET );
1564 if ( ( NewBuf != NULL ) && ( _len != 0 ) ) {
1566 * if ret_codeset == ISO-2022-KR, we don't come here.
1569 * According to RFC1468, we should use B-encoding.
1571 if ( !strncasecmp( ret_codeset, "ISO-2022-JP", 11 ) ) {
1572 writeBase64( tmp, NewBuf, _len );
1574 writeQPrint( tmp, NewBuf, _len,
1575 (!strncasecmp( ret_codeset, "ISO-2022-TW", 11 ) ||
1576 !strncasecmp( ret_codeset, "ISO-2022-CN", 11 ) ));
1579 writeQPrint( tmp, cur, scan_c - cur, 0 );
1581 strncat(buf,tmp,strlen(tmp));