2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
42 #include "quoted-printable.h"
44 #include "prefs_common.h"
54 #define SUBST_CHAR '_'
57 #define iseuckanji(c) \
58 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe)
59 #define iseuchwkana1(c) \
60 (((c) & 0xff) == 0x8e)
61 #define iseuchwkana2(c) \
62 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
64 (((c) & 0xff) == 0x8f)
65 #define isunprintableeuckanji(c) \
66 (((c) & 0xff) >= 0xa9 && ((c) & 0xff) <= 0xaf)
67 #define issjiskanji1(c) \
68 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
69 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc))
70 #define issjiskanji2(c) \
71 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
72 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
73 #define issjishwkana(c) \
74 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
77 if (state != JIS_KANJI) { \
85 if (state != JIS_ASCII) { \
93 if (state != JIS_HWKANA) { \
101 if (state != JIS_AUXKANJI) { \
106 state = JIS_AUXKANJI; \
109 void conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
111 const guchar *in = inbuf;
112 guchar *out = outbuf;
113 JISState state = JIS_ASCII;
115 while (*in != '\0') {
119 if (*(in + 1) == '@' || *(in + 1) == 'B') {
122 } else if (*(in + 1) == '(' &&
124 state = JIS_AUXKANJI;
127 /* unknown escape sequence */
130 } else if (*in == '(') {
131 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
134 } else if (*(in + 1) == 'I') {
138 /* unknown escape sequence */
142 /* unknown escape sequence */
145 } else if (*in == 0x0e) {
148 } else if (*in == 0x0f) {
157 *out++ = *in++ | 0x80;
158 if (*in == '\0') break;
159 *out++ = *in++ | 0x80;
163 *out++ = *in++ | 0x80;
167 *out++ = *in++ | 0x80;
168 if (*in == '\0') break;
169 *out++ = *in++ | 0x80;
178 void conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
180 const guchar *in = inbuf;
181 guchar *out = outbuf;
182 JISState state = JIS_ASCII;
184 while (*in != '\0') {
188 } else if (iseuckanji(*in)) {
189 if (iseuckanji(*(in + 1))) {
191 *out++ = *in++ & 0x7f;
192 *out++ = *in++ & 0x7f;
197 if (*in != '\0' && !isascii(*in)) {
202 } else if (iseuchwkana1(*in)) {
204 if (iseuchwkana2(*in)) {
206 *out++ = *in++ & 0x7f;
209 if (*in != '\0' && !isascii(*in)) {
214 } else if (iseucaux(*in)) {
216 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
218 *out++ = *in++ & 0x7f;
219 *out++ = *in++ & 0x7f;
222 if (*in != '\0' && !isascii(*in)) {
225 if (*in != '\0' && !isascii(*in)) {
242 void conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
244 const guchar *in = inbuf;
245 guchar *out = outbuf;
247 while (*in != '\0') {
250 } else if (issjiskanji1(*in)) {
251 if (issjiskanji2(*(in + 1))) {
253 guchar out2 = *(in + 1);
256 row = out1 < 0xa0 ? 0x70 : 0xb0;
258 out1 = (out1 - row) * 2 - 1;
259 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
261 out1 = (out1 - row) * 2;
265 *out++ = out1 | 0x80;
266 *out++ = out2 | 0x80;
271 if (*in != '\0' && !isascii(*in)) {
276 } else if (issjishwkana(*in)) {
288 void conv_anytoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
290 switch (conv_guess_encoding(inbuf)) {
292 conv_jistoeuc(outbuf, outlen, inbuf);
295 conv_sjistoeuc(outbuf, outlen, inbuf);
298 strncpy2(outbuf, inbuf, outlen);
303 void conv_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
305 switch (conv_guess_encoding(inbuf)) {
307 conv_euctojis(outbuf, outlen, inbuf);
310 strncpy2(outbuf, inbuf, outlen);
315 void conv_unreadable_eucjp(gchar *str)
317 register guchar *p = str;
321 /* convert CR+LF -> LF */
322 if (*p == '\r' && *(p + 1) == '\n')
323 memmove(p, p + 1, strlen(p));
324 /* printable 7 bit code */
326 } else if (iseuckanji(*p)) {
327 if (iseuckanji(*(p + 1)) && !isunprintableeuckanji(*p))
328 /* printable euc-jp code */
331 /* substitute unprintable code */
340 } else if (iseuchwkana1(*p)) {
341 if (iseuchwkana2(*(p + 1)))
342 /* euc-jp hankaku kana */
346 } else if (iseucaux(*p)) {
347 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
348 /* auxiliary kanji */
353 /* substitute unprintable 1 byte code */
358 void conv_unreadable_8bit(gchar *str)
360 register guchar *p = str;
363 /* convert CR+LF -> LF */
364 if (*p == '\r' && *(p + 1) == '\n')
365 memmove(p, p + 1, strlen(p));
366 else if (!isascii(*p)) *p = SUBST_CHAR;
371 void conv_unreadable_latin(gchar *str)
373 register guchar *p = str;
376 /* convert CR+LF -> LF */
377 if (*p == '\r' && *(p + 1) == '\n')
378 memmove(p, p + 1, strlen(p));
379 else if ((*p & 0xff) >= 0x80 && (*p & 0xff) <= 0x9f)
387 void conv_mb_alnum(gchar *str)
389 static guchar char_tbl[] = {
391 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
392 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
394 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
395 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
397 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
398 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
400 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
401 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
403 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
404 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
407 register guchar *p = str;
414 register guchar ch = *(p + 1);
416 if (ch >= 0xb0 && ch <= 0xfa) {
421 memmove(p, p + 1, len);
427 } else if (*p == 0xa1) {
428 register guchar ch = *(p + 1);
430 if (ch >= 0xa0 && ch <= 0xef &&
431 NCV != char_tbl[ch - 0xa0]) {
432 *p = char_tbl[ch - 0xa0];
435 memmove(p, p + 1, len);
441 } else if (iseuckanji(*p)) {
451 CharSet conv_guess_encoding(const gchar *str)
453 const guchar *p = str;
454 CharSet guessed = C_US_ASCII;
457 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
458 if (guessed == C_US_ASCII)
459 return C_ISO_2022_JP;
461 } else if (isascii(*p)) {
463 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
464 if (*p >= 0xfd && *p <= 0xfe)
466 else if (guessed == C_SHIFT_JIS) {
467 if ((issjiskanji1(*p) &&
468 issjiskanji2(*(p + 1))) ||
470 guessed = C_SHIFT_JIS;
476 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
477 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
478 guessed = C_SHIFT_JIS;
482 } else if (issjishwkana(*p)) {
483 guessed = C_SHIFT_JIS;
493 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
495 conv_jistoeuc(outbuf, outlen, inbuf);
496 conv_unreadable_eucjp(outbuf);
499 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
501 conv_sjistoeuc(outbuf, outlen, inbuf);
502 conv_unreadable_eucjp(outbuf);
505 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
507 strncpy2(outbuf, inbuf, outlen);
508 conv_unreadable_eucjp(outbuf);
511 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
513 conv_anytoeuc(outbuf, outlen, inbuf);
514 conv_unreadable_eucjp(outbuf);
517 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
519 strncpy2(outbuf, inbuf, outlen);
520 conv_unreadable_8bit(outbuf);
523 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
525 strncpy2(outbuf, inbuf, outlen);
526 conv_unreadable_latin(outbuf);
529 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
531 strncpy2(outbuf, inbuf, outlen);
534 CodeConverter *conv_code_converter_new(const gchar *charset)
538 conv = g_new0(CodeConverter, 1);
539 conv->code_conv_func = conv_get_code_conv_func(charset, NULL);
540 conv->charset_str = g_strdup(charset);
541 conv->charset = conv_get_charset_from_str(charset);
546 void conv_code_converter_destroy(CodeConverter *conv)
548 g_free(conv->charset_str);
552 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
556 if (conv->code_conv_func != conv_noconv)
557 conv->code_conv_func(outbuf, outlen, inbuf);
561 str = conv_codeset_strdup(inbuf, conv->charset_str, NULL);
565 strncpy2(outbuf, str, outlen);
569 #else /* !HAVE_ICONV */
570 conv->code_conv_func(outbuf, outlen, inbuf);
576 gchar *conv_codeset_strdup(const gchar *inbuf,
577 const gchar *src_code, const gchar *dest_code)
581 CodeConvFunc conv_func;
583 conv_func = conv_get_code_conv_func(src_code, dest_code);
584 if (conv_func != conv_noconv) {
585 len = (strlen(inbuf) + 1) * 3;
587 if (!buf) return NULL;
589 conv_func(buf, len, inbuf);
590 return g_realloc(buf, strlen(buf) + 1);
595 src_code = conv_get_outgoing_charset_str();
597 dest_code = conv_get_current_charset_str();
599 /* don't convert if current codeset is US-ASCII */
600 if (!strcasecmp(dest_code, CS_US_ASCII))
601 return g_strdup(inbuf);
603 /* don't convert if src and dest codeset are identical */
604 if (!strcasecmp(src_code, dest_code))
605 return g_strdup(inbuf);
607 return conv_iconv_strdup(inbuf, src_code, dest_code);
609 return g_strdup(inbuf);
610 #endif /* HAVE_ICONV */
613 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
614 const gchar *dest_charset_str)
616 CodeConvFunc code_conv = conv_noconv;
618 CharSet dest_charset;
620 if (!src_charset_str)
621 src_charset = conv_get_current_charset();
623 src_charset = conv_get_charset_from_str(src_charset_str);
625 /* auto detection mode */
626 if (!src_charset_str && !dest_charset_str) {
627 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
628 return conv_anytodisp;
633 dest_charset = conv_get_charset_from_str(dest_charset_str);
635 switch (src_charset) {
637 case C_ISO_2022_JP_2:
638 if (dest_charset == C_AUTO)
639 code_conv = conv_jistodisp;
640 else if (dest_charset == C_EUC_JP)
641 code_conv = conv_jistoeuc;
644 if (dest_charset == C_AUTO)
645 code_conv = conv_ustodisp;
659 if (dest_charset == C_AUTO)
660 code_conv = conv_latintodisp;
663 if (dest_charset == C_AUTO)
664 code_conv = conv_sjistodisp;
665 else if (dest_charset == C_EUC_JP)
666 code_conv = conv_sjistoeuc;
669 if (dest_charset == C_AUTO)
670 code_conv = conv_euctodisp;
671 else if (dest_charset == C_ISO_2022_JP ||
672 dest_charset == C_ISO_2022_JP_2)
673 code_conv = conv_euctojis;
683 gchar *conv_iconv_strdup(const gchar *inbuf,
684 const gchar *src_code, const gchar *dest_code)
687 const gchar *inbuf_p;
696 cd = iconv_open(dest_code, src_code);
697 if (cd == (iconv_t)-1)
701 in_size = strlen(inbuf) + 1;
703 out_size = in_size * 2;
704 outbuf = g_malloc(out_size);
708 while ((n_conv = iconv(cd, (gchar **)&inbuf_p, &in_left,
709 &outbuf_p, &out_left)) < 0) {
710 if (EILSEQ == errno) {
713 } else if (EINVAL == errno) {
716 } else if (E2BIG == errno) {
718 outbuf = g_realloc(outbuf, out_size);
724 g_warning("conv_iconv_strdup(): %s\n",
735 #endif /* HAVE_ICONV */
737 static const struct {
741 {C_US_ASCII, CS_US_ASCII},
742 {C_US_ASCII, CS_ANSI_X3_4_1968},
744 {C_ISO_8859_1, CS_ISO_8859_1},
745 {C_ISO_8859_2, CS_ISO_8859_2},
746 {C_ISO_8859_4, CS_ISO_8859_4},
747 {C_ISO_8859_5, CS_ISO_8859_5},
748 {C_ISO_8859_7, CS_ISO_8859_7},
749 {C_ISO_8859_8, CS_ISO_8859_8},
750 {C_ISO_8859_9, CS_ISO_8859_9},
751 {C_ISO_8859_11, CS_ISO_8859_11},
752 {C_ISO_8859_13, CS_ISO_8859_13},
753 {C_ISO_8859_15, CS_ISO_8859_15},
754 {C_BALTIC, CS_BALTIC},
755 {C_CP1251, CS_CP1251},
756 {C_WINDOWS_1251, CS_WINDOWS_1251},
757 {C_KOI8_R, CS_KOI8_R},
758 {C_KOI8_U, CS_KOI8_U},
759 {C_ISO_2022_JP, CS_ISO_2022_JP},
760 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
761 {C_EUC_JP, CS_EUC_JP},
762 {C_EUC_JP, CS_EUCJP},
763 {C_SHIFT_JIS, CS_SHIFT_JIS},
764 {C_SHIFT_JIS, CS_SHIFT__JIS},
765 {C_SHIFT_JIS, CS_SJIS},
766 {C_ISO_2022_KR, CS_ISO_2022_KR},
767 {C_EUC_KR, CS_EUC_KR},
768 {C_ISO_2022_CN, CS_ISO_2022_CN},
769 {C_EUC_CN, CS_EUC_CN},
770 {C_GB2312, CS_GB2312},
771 {C_EUC_TW, CS_EUC_TW},
773 {C_TIS_620, CS_TIS_620},
774 {C_WINDOWS_874, CS_WINDOWS_874},
777 static const struct {
782 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
783 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
784 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
785 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
786 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
787 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
788 {"ko_KR" , C_EUC_KR , C_EUC_KR},
789 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
790 {"zh_CN" , C_GB2312 , C_GB2312},
791 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
792 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
793 {"zh_TW" , C_BIG5 , C_BIG5},
795 {"ru_RU.KOI8-R" , C_KOI8_R , C_ISO_8859_5},
796 {"ru_RU.CP1251" , C_WINDOWS_1251, C_ISO_8859_5},
798 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
800 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
801 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
802 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
803 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
804 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
805 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
806 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
807 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
808 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
809 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
810 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
811 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
812 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
813 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
814 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
816 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
817 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
818 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
819 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
820 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
821 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
822 {"ru_RU" , C_ISO_8859_5 , C_ISO_8859_5},
823 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
824 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
825 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
827 {"th_TH" , C_TIS_620 , C_TIS_620},
828 /* {"th_TH" , C_WINDOWS_874}, */
829 /* {"th_TH" , C_ISO_8859_11}, */
831 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
832 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
833 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
834 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
835 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
837 {"C" , C_US_ASCII , C_US_ASCII},
838 {"POSIX" , C_US_ASCII , C_US_ASCII},
839 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
842 const gchar *conv_get_charset_str(CharSet charset)
846 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
847 if (charsets[i].charset == charset)
848 return charsets[i].name;
854 CharSet conv_get_charset_from_str(const gchar *charset)
858 if (!charset) return C_AUTO;
860 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
861 if (!strcasecmp(charsets[i].name, charset))
862 return charsets[i].charset;
868 CharSet conv_get_current_charset(void)
870 static CharSet cur_charset = -1;
871 const gchar *cur_locale;
874 if (cur_charset != -1)
877 cur_locale = conv_get_current_locale();
879 cur_charset = C_US_ASCII;
883 if (strcasestr(cur_locale, "UTF-8")) {
884 cur_charset = C_UTF_8;
888 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
891 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
892 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
893 if (!strncasecmp(cur_locale, locale_table[i].locale,
894 strlen(locale_table[i].locale))) {
895 cur_charset = locale_table[i].charset;
897 } else if ((p = strchr(locale_table[i].locale, '_')) &&
898 !strchr(p + 1, '.')) {
899 if (strlen(cur_locale) == 2 &&
900 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
901 cur_charset = locale_table[i].charset;
907 cur_charset = C_AUTO;
911 const gchar *conv_get_current_charset_str(void)
913 static const gchar *codeset = NULL;
916 codeset = conv_get_charset_str(conv_get_current_charset());
918 return codeset ? codeset : "US-ASCII";
921 CharSet conv_get_outgoing_charset(void)
923 static CharSet out_charset = -1;
924 const gchar *cur_locale;
927 if (out_charset != -1)
930 cur_locale = conv_get_current_locale();
932 out_charset = C_AUTO;
936 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
939 if (!strncasecmp(cur_locale, locale_table[i].locale,
940 strlen(locale_table[i].locale))) {
941 out_charset = locale_table[i].out_charset;
943 } else if ((p = strchr(locale_table[i].locale, '_')) &&
944 !strchr(p + 1, '.')) {
945 if (strlen(cur_locale) == 2 &&
946 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
947 out_charset = locale_table[i].out_charset;
954 /* encoding conversion without iconv() is only supported
955 on Japanese locale for now */
956 if (out_charset == C_ISO_2022_JP)
959 return conv_get_current_charset();
965 const gchar *conv_get_outgoing_charset_str(void)
970 if (prefs_common.outgoing_charset) {
971 if (!isalpha(prefs_common.outgoing_charset[0])) {
972 g_free(prefs_common.outgoing_charset);
973 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
974 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
975 return prefs_common.outgoing_charset;
978 out_charset = conv_get_outgoing_charset();
979 str = conv_get_charset_str(out_charset);
981 return str ? str : "US-ASCII";
984 const gchar *conv_get_current_locale(void)
988 cur_locale = g_getenv("LC_ALL");
989 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
990 if (!cur_locale) cur_locale = g_getenv("LANG");
991 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
993 debug_print("current locale: %s\n",
994 cur_locale ? cur_locale : "(none)");
999 void conv_unmime_header_overwrite(gchar *str)
1003 CharSet cur_charset;
1005 cur_charset = conv_get_current_charset();
1007 if (cur_charset == C_EUC_JP) {
1008 buflen = strlen(str) * 2 + 1;
1009 Xalloca(buf, buflen, return);
1010 conv_anytodisp(buf, buflen, str);
1011 unmime_header(str, buf);
1013 buflen = strlen(str) + 1;
1014 Xalloca(buf, buflen, return);
1015 unmime_header(buf, str);
1016 strncpy2(str, buf, buflen);
1020 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1021 const gchar *charset)
1023 CharSet cur_charset;
1025 cur_charset = conv_get_current_charset();
1027 if (cur_charset == C_EUC_JP) {
1031 buflen = strlen(str) * 2 + 1;
1032 Xalloca(buf, buflen, return);
1033 conv_anytodisp(buf, buflen, str);
1034 unmime_header(outbuf, buf);
1036 unmime_header(outbuf, str);
1039 #define MAX_LINELEN 76
1040 #define MIMESEP_BEGIN "=?"
1041 #define MIMESEP_END "?="
1043 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1045 #define LBREAK_IF_REQUIRED(cond) \
1047 if (len - (destp - dest) < MAX_LINELEN + 2) { \
1052 if ((cond) && *srcp) { \
1053 if (destp > dest && isspace(*(destp - 1))) \
1055 else if (isspace(*srcp)) \
1060 left = MAX_LINELEN - 1; \
1065 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1068 const gchar *cur_encoding;
1069 const gchar *out_encoding;
1073 const gchar *srcp = src;
1074 gchar *destp = dest;
1075 gboolean use_base64;
1077 if (MB_CUR_MAX > 1) {
1079 mimesep_enc = "?B?";
1082 mimesep_enc = "?Q?";
1085 cur_encoding = conv_get_current_charset_str();
1086 if (!strcmp(cur_encoding, "US-ASCII"))
1087 cur_encoding = "ISO-8859-1";
1088 out_encoding = conv_get_outgoing_charset_str();
1089 if (!strcmp(out_encoding, "US-ASCII"))
1090 out_encoding = "ISO-8859-1";
1092 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1093 strlen(mimesep_enc) + strlen(MIMESEP_END);
1095 left = MAX_LINELEN - header_len;
1098 LBREAK_IF_REQUIRED(left <= 0);
1100 while (isspace(*srcp)) {
1103 LBREAK_IF_REQUIRED(left <= 0);
1106 /* output as it is if the next word is ASCII string */
1107 if (!is_next_nonascii(srcp)) {
1110 word_len = get_next_word_len(srcp);
1111 LBREAK_IF_REQUIRED(left < word_len);
1112 while (word_len > 0) {
1113 LBREAK_IF_REQUIRED(left <= 0);
1128 const gchar *p = srcp;
1130 gint out_enc_str_len;
1131 gint mime_block_len;
1132 gboolean cont = FALSE;
1134 while (*p != '\0') {
1135 if (isspace(*p) && !is_next_nonascii(p + 1))
1138 if (MB_CUR_MAX > 1) {
1139 mb_len = mblen(p, MB_CUR_MAX);
1141 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1147 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1148 out_str = conv_codeset_strdup
1149 (part_str, cur_encoding, out_encoding);
1151 g_warning("conv_encode_header(): code conversion failed\n");
1152 out_str = g_strdup(out_str);
1154 out_str_len = strlen(out_str);
1157 out_enc_str_len = B64LEN(out_str_len);
1160 qp_get_q_encoding_len(out_str);
1164 if (mimestr_len + out_enc_str_len <= left) {
1167 } else if (cur_len == 0) {
1168 LBREAK_IF_REQUIRED(1);
1177 Xstrndup_a(part_str, srcp, cur_len, );
1178 out_str = conv_codeset_strdup
1179 (part_str, cur_encoding, out_encoding);
1181 g_warning("conv_encode_header(): code conversion failed\n");
1182 out_str = g_strdup(out_str);
1184 out_str_len = strlen(out_str);
1187 out_enc_str_len = B64LEN(out_str_len);
1190 qp_get_q_encoding_len(out_str);
1192 Xalloca(enc_str, out_enc_str_len + 1, );
1194 base64_encode(enc_str, out_str, out_str_len);
1196 qp_q_encode(enc_str, out_str);
1200 /* output MIME-encoded string block */
1201 mime_block_len = mimestr_len + strlen(enc_str);
1202 g_snprintf(destp, mime_block_len + 1,
1203 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1204 out_encoding, mimesep_enc, enc_str);
1205 destp += mime_block_len;
1208 left -= mime_block_len;
1211 LBREAK_IF_REQUIRED(cont);
1221 #undef LBREAK_IF_REQUIRED