2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
27 #include <glib/gi18n.h>
40 #include "quoted-printable.h"
42 #include "prefs_common.h"
44 /* For unknown reasons the inconv.m4 macro undefs that macro if no
45 const is needed. This would break the code below so we define it. */
58 #define SUBST_CHAR 0x5f;
61 #define iseuckanji(c) \
62 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe)
63 #define iseuchwkana1(c) \
64 (((c) & 0xff) == 0x8e)
65 #define iseuchwkana2(c) \
66 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
68 (((c) & 0xff) == 0x8f)
69 #define issjiskanji1(c) \
70 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
71 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc))
72 #define issjiskanji2(c) \
73 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
74 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
75 #define issjishwkana(c) \
76 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
79 if (state != JIS_KANJI) { \
87 if (state != JIS_ASCII) { \
95 if (state != JIS_HWKANA) { \
103 if (state != JIS_AUXKANJI) { \
108 state = JIS_AUXKANJI; \
111 static CodeConvFunc conv_get_code_conv_func (const gchar *src_charset_str,
112 const gchar *dest_charset_str);
114 static gchar *conv_iconv_strdup_with_cd (const gchar *inbuf,
117 static gchar *conv_iconv_strdup (const gchar *inbuf,
118 const gchar *src_code,
119 const gchar *dest_code);
121 static CharSet conv_get_locale_charset (void);
122 static CharSet conv_get_outgoing_charset (void);
123 static CharSet conv_guess_ja_encoding(const gchar *str);
124 static gboolean conv_is_ja_locale (void);
126 static gint conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf);
127 static gint conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf);
128 static gint conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf);
130 static gint conv_jistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
131 static gint conv_sjistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
132 static gint conv_euctoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
133 static gint conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf);
135 static gint conv_utf8toeuc(gchar *outbuf, gint outlen, const gchar *inbuf);
136 static gint conv_utf8tojis(gchar *outbuf, gint outlen, const gchar *inbuf);
138 static void conv_unreadable_8bit(gchar *str);
140 static gint conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
141 static gint conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
142 static gint conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
144 static gint conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
145 static gint conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf);
146 static gint conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf);
148 static gboolean strict_mode = FALSE;
150 void codeconv_set_strict(gboolean mode)
155 static gint conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
157 const guchar *in = inbuf;
158 guchar *out = outbuf;
159 JISState state = JIS_ASCII;
161 while (*in != '\0') {
165 if (*(in + 1) == '@' || *(in + 1) == 'B') {
168 } else if (*(in + 1) == '(' &&
170 state = JIS_AUXKANJI;
173 /* unknown escape sequence */
176 } else if (*in == '(') {
177 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
180 } else if (*(in + 1) == 'I') {
184 /* unknown escape sequence */
188 /* unknown escape sequence */
191 } else if (*in == 0x0e) {
194 } else if (*in == 0x0f) {
203 *out++ = *in++ | 0x80;
204 if (*in == '\0') break;
205 *out++ = *in++ | 0x80;
209 *out++ = *in++ | 0x80;
213 *out++ = *in++ | 0x80;
214 if (*in == '\0') break;
215 *out++ = *in++ | 0x80;
225 #define JIS_HWDAKUTEN 0x5e
226 #define JIS_HWHANDAKUTEN 0x5f
228 static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym)
230 static guint16 h2z_tbl[] = {
232 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521,
233 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543,
235 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d,
236 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d,
238 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c,
239 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e,
241 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569,
242 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c
245 static guint16 dakuten_tbl[] = {
247 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e,
248 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e,
250 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000,
251 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000
254 static guint16 handakuten_tbl[] = {
256 0x2551, 0x2554, 0x2557, 0x255a, 0x255d
264 if (jis_code < 0x21 || jis_code > 0x5f)
267 if (sound_sym == JIS_HWDAKUTEN &&
268 jis_code >= 0x36 && jis_code <= 0x4e) {
269 out_code = dakuten_tbl[jis_code - 0x30];
271 *outbuf = out_code >> 8;
272 *(outbuf + 1) = out_code & 0xff;
277 if (sound_sym == JIS_HWHANDAKUTEN &&
278 jis_code >= 0x4a && jis_code <= 0x4e) {
279 out_code = handakuten_tbl[jis_code - 0x4a];
280 *outbuf = out_code >> 8;
281 *(outbuf + 1) = out_code & 0xff;
285 out_code = h2z_tbl[jis_code - 0x20];
286 *outbuf = out_code >> 8;
287 *(outbuf + 1) = out_code & 0xff;
291 static gint conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
293 const guchar *in = inbuf;
294 guchar *out = outbuf;
295 JISState state = JIS_ASCII;
297 while (*in != '\0') {
301 } else if (iseuckanji(*in)) {
302 if (iseuckanji(*(in + 1))) {
304 *out++ = *in++ & 0x7f;
305 *out++ = *in++ & 0x7f;
310 if (*in != '\0' && !IS_ASCII(*in)) {
315 } else if (iseuchwkana1(*in)) {
316 if (iseuchwkana2(*(in + 1))) {
317 if (prefs_common.allow_jisx0201_kana) {
320 *out++ = *in++ & 0x7f;
325 if (iseuchwkana1(*(in + 2)) &&
326 iseuchwkana2(*(in + 3)))
327 len = conv_jis_hantozen
329 *(in + 1), *(in + 3));
331 len = conv_jis_hantozen
346 if (*in != '\0' && !IS_ASCII(*in)) {
351 } else if (iseucaux(*in)) {
353 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
355 *out++ = *in++ & 0x7f;
356 *out++ = *in++ & 0x7f;
359 if (*in != '\0' && !IS_ASCII(*in)) {
362 if (*in != '\0' && !IS_ASCII(*in)) {
380 static gint conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
382 const guchar *in = inbuf;
383 guchar *out = outbuf;
385 while (*in != '\0') {
388 } else if (issjiskanji1(*in)) {
389 if (issjiskanji2(*(in + 1))) {
391 guchar out2 = *(in + 1);
394 row = out1 < 0xa0 ? 0x70 : 0xb0;
396 out1 = (out1 - row) * 2 - 1;
397 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
399 out1 = (out1 - row) * 2;
403 *out++ = out1 | 0x80;
404 *out++ = out2 | 0x80;
409 if (*in != '\0' && !IS_ASCII(*in)) {
414 } else if (issjishwkana(*in)) {
427 static gint conv_jistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
431 Xalloca(eucstr, outlen, return -1);
433 if (conv_jistoeuc(eucstr, outlen, inbuf) <0)
435 if (conv_euctoutf8(outbuf, outlen, eucstr) < 0)
440 static gint conv_sjistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
444 tmpstr = conv_iconv_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8);
446 strncpy2(outbuf, tmpstr, outlen);
450 strncpy2(outbuf, inbuf, outlen);
455 static gint conv_euctoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
457 static iconv_t cd = (iconv_t)-1;
458 static gboolean iconv_ok = TRUE;
461 if (cd == (iconv_t)-1) {
463 strncpy2(outbuf, inbuf, outlen);
466 cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS);
467 if (cd == (iconv_t)-1) {
468 cd = iconv_open(CS_UTF_8, CS_EUC_JP);
469 if (cd == (iconv_t)-1) {
470 g_warning("conv_euctoutf8(): %s\n",
473 strncpy2(outbuf, inbuf, outlen);
479 tmpstr = conv_iconv_strdup_with_cd(inbuf, cd);
481 strncpy2(outbuf, tmpstr, outlen);
485 strncpy2(outbuf, inbuf, outlen);
490 static gint conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
493 switch (conv_guess_ja_encoding(inbuf)) {
495 r = conv_jistoutf8(outbuf, outlen, inbuf);
498 r = conv_sjistoutf8(outbuf, outlen, inbuf);
501 r = conv_euctoutf8(outbuf, outlen, inbuf);
505 strncpy2(outbuf, inbuf, outlen);
512 static gint conv_utf8toeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
514 static iconv_t cd = (iconv_t)-1;
515 static gboolean iconv_ok = TRUE;
518 if (cd == (iconv_t)-1) {
520 strncpy2(outbuf, inbuf, outlen);
523 cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8);
524 if (cd == (iconv_t)-1) {
525 cd = iconv_open(CS_EUC_JP, CS_UTF_8);
526 if (cd == (iconv_t)-1) {
527 g_warning("conv_utf8toeuc(): %s\n",
530 strncpy2(outbuf, inbuf, outlen);
536 tmpstr = conv_iconv_strdup_with_cd(inbuf, cd);
538 strncpy2(outbuf, tmpstr, outlen);
542 strncpy2(outbuf, inbuf, outlen);
547 static gint conv_utf8tojis(gchar *outbuf, gint outlen, const gchar *inbuf)
551 Xalloca(eucstr, outlen, return -1);
553 if (conv_utf8toeuc(eucstr, outlen, inbuf) < 0)
555 if (conv_euctojis(outbuf, outlen, eucstr) < 0)
561 static void conv_unreadable_8bit(gchar *str)
563 register guchar *p = str;
566 /* convert CR+LF -> LF */
567 if (*p == '\r' && *(p + 1) == '\n')
568 memmove(p, p + 1, strlen(p));
569 else if (!IS_ASCII(*p)) *p = SUBST_CHAR;
574 static CharSet conv_guess_ja_encoding(const gchar *str)
576 const guchar *p = str;
577 CharSet guessed = C_US_ASCII;
580 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
581 if (guessed == C_US_ASCII)
582 return C_ISO_2022_JP;
584 } else if (IS_ASCII(*p)) {
586 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
587 if (*p >= 0xfd && *p <= 0xfe)
589 else if (guessed == C_SHIFT_JIS) {
590 if ((issjiskanji1(*p) &&
591 issjiskanji2(*(p + 1))) ||
593 guessed = C_SHIFT_JIS;
599 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
600 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
601 guessed = C_SHIFT_JIS;
605 } else if (issjishwkana(*p)) {
606 guessed = C_SHIFT_JIS;
616 static gint conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
618 return conv_jistoutf8(outbuf, outlen, inbuf);
621 static gint conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
623 return conv_sjistoutf8(outbuf, outlen, inbuf);
626 static gint conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
628 return conv_euctoutf8(outbuf, outlen, inbuf);
631 void conv_utf8todisp(gchar *outbuf, gint outlen, const gchar *inbuf)
633 if (g_utf8_validate(inbuf, -1, NULL) == TRUE)
634 strncpy2(outbuf, inbuf, outlen);
636 conv_ustodisp(outbuf, outlen, inbuf);
639 static gint conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
642 if (conv_anytoutf8(outbuf, outlen, inbuf) < 0)
644 if (g_utf8_validate(outbuf, -1, NULL) != TRUE)
645 conv_unreadable_8bit(outbuf);
649 static gint conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
651 strncpy2(outbuf, inbuf, outlen);
652 conv_unreadable_8bit(outbuf);
657 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
661 codeconv_set_strict(TRUE);
662 tmpstr = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(),
664 codeconv_set_strict(FALSE);
665 if (tmpstr && g_utf8_validate(tmpstr, -1, NULL)) {
666 strncpy2(outbuf, tmpstr, outlen);
669 } else if (tmpstr && !g_utf8_validate(tmpstr, -1, NULL)) {
671 codeconv_set_strict(TRUE);
672 tmpstr = conv_iconv_strdup(inbuf,
673 conv_get_locale_charset_str_no_utf8(),
675 codeconv_set_strict(FALSE);
677 if (tmpstr && g_utf8_validate(tmpstr, -1, NULL)) {
678 strncpy2(outbuf, tmpstr, outlen);
683 conv_utf8todisp(outbuf, outlen, inbuf);
687 static gint conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
689 strncpy2(outbuf, inbuf, outlen);
694 conv_get_fallback_for_private_encoding(const gchar *encoding)
696 if (encoding && (encoding[0] == 'X' || encoding[0] == 'x') &&
697 encoding[1] == '-') {
698 if (!g_ascii_strcasecmp(encoding, CS_X_GBK))
705 CodeConverter *conv_code_converter_new(const gchar *src_charset)
709 src_charset = conv_get_fallback_for_private_encoding(src_charset);
711 conv = g_new0(CodeConverter, 1);
712 conv->code_conv_func = conv_get_code_conv_func(src_charset, NULL);
713 conv->charset_str = g_strdup(src_charset);
714 conv->charset = conv_get_charset_from_str(src_charset);
719 void conv_code_converter_destroy(CodeConverter *conv)
721 g_free(conv->charset_str);
725 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
728 if (conv->code_conv_func != conv_noconv)
729 return conv->code_conv_func(outbuf, outlen, inbuf);
733 str = conv_iconv_strdup(inbuf, conv->charset_str, NULL);
737 strncpy2(outbuf, str, outlen);
745 gchar *conv_codeset_strdup(const gchar *inbuf,
746 const gchar *src_code, const gchar *dest_code)
750 CodeConvFunc conv_func;
752 if (!strcmp2(src_code, dest_code))
753 return g_strdup(inbuf);
755 src_code = conv_get_fallback_for_private_encoding(src_code);
756 conv_func = conv_get_code_conv_func(src_code, dest_code);
757 if (conv_func == conv_ustodisp && strict_mode && !is_ascii_str(inbuf))
760 if (conv_func != conv_noconv) {
761 len = (strlen(inbuf) + 1) * 3;
763 if (!buf) return NULL;
765 if (conv_func(buf, len, inbuf) == 0 || !strict_mode)
766 return g_realloc(buf, strlen(buf) + 1);
773 return conv_iconv_strdup(inbuf, src_code, dest_code);
776 static CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
777 const gchar *dest_charset_str)
779 CodeConvFunc code_conv = conv_noconv;
781 CharSet dest_charset;
783 if (!src_charset_str)
784 src_charset = conv_get_locale_charset();
786 src_charset = conv_get_charset_from_str(src_charset_str);
788 /* auto detection mode */
789 if (!src_charset_str && !dest_charset_str) {
790 if (conv_is_ja_locale())
791 return conv_anytodisp;
796 dest_charset = conv_get_charset_from_str(dest_charset_str);
798 if (dest_charset == C_US_ASCII)
799 return conv_ustodisp;
801 switch (src_charset) {
819 case C_ISO_2022_JP_2:
820 case C_ISO_2022_JP_3:
821 if (dest_charset == C_AUTO)
822 code_conv = conv_jistodisp;
823 else if (dest_charset == C_EUC_JP)
824 code_conv = conv_jistoeuc;
825 else if (dest_charset == C_UTF_8)
826 code_conv = conv_jistoutf8;
829 if (dest_charset == C_AUTO)
830 code_conv = conv_sjistodisp;
831 else if (dest_charset == C_EUC_JP)
832 code_conv = conv_sjistoeuc;
833 else if (dest_charset == C_UTF_8)
834 code_conv = conv_sjistoutf8;
837 if (dest_charset == C_AUTO)
838 code_conv = conv_euctodisp;
839 else if (dest_charset == C_ISO_2022_JP ||
840 dest_charset == C_ISO_2022_JP_2 ||
841 dest_charset == C_ISO_2022_JP_3)
842 code_conv = conv_euctojis;
843 else if (dest_charset == C_UTF_8)
844 code_conv = conv_euctoutf8;
847 if (dest_charset == C_EUC_JP)
848 code_conv = conv_utf8toeuc;
849 else if (dest_charset == C_ISO_2022_JP ||
850 dest_charset == C_ISO_2022_JP_2 ||
851 dest_charset == C_ISO_2022_JP_3)
852 code_conv = conv_utf8tojis;
861 static gchar *conv_iconv_strdup(const gchar *inbuf,
862 const gchar *src_code, const gchar *dest_code)
867 if (!src_code && !dest_code &&
868 g_utf8_validate(inbuf, -1, NULL))
869 return g_strdup(inbuf);
872 src_code = conv_get_outgoing_charset_str();
874 dest_code = CS_INTERNAL;
876 /* don't convert if src and dest codeset are identical */
877 if (!strcasecmp(src_code, dest_code))
878 return g_strdup(inbuf);
880 /* don't convert if dest codeset is US-ASCII */
881 if (!strcasecmp(src_code, CS_US_ASCII))
882 return g_strdup(inbuf);
884 /* don't convert if dest codeset is US-ASCII */
885 if (!strcasecmp(dest_code, CS_US_ASCII))
886 return g_strdup(inbuf);
888 cd = iconv_open(dest_code, src_code);
889 if (cd == (iconv_t)-1)
892 outbuf = conv_iconv_strdup_with_cd(inbuf, cd);
899 gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd)
901 const gchar *inbuf_p;
912 in_size = strlen(inbuf);
914 out_size = (in_size + 1) * 2;
915 outbuf = g_malloc(out_size);
919 #define EXPAND_BUF() \
921 len = outbuf_p - outbuf; \
923 outbuf = g_realloc(outbuf, out_size); \
924 outbuf_p = outbuf + len; \
925 out_left = out_size - len; \
928 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
929 &outbuf_p, &out_left)) == (size_t)-1) {
930 if (EILSEQ == errno) {
935 //g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno));
941 *outbuf_p++ = SUBST_CHAR;
943 } else if (EINVAL == errno) {
945 } else if (E2BIG == errno) {
948 g_warning("conv_iconv_strdup(): %s\n",
954 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
956 if (E2BIG == errno) {
959 g_warning("conv_iconv_strdup(): %s\n",
967 len = outbuf_p - outbuf;
968 outbuf = g_realloc(outbuf, len + 1);
974 static const struct {
978 {C_US_ASCII, CS_US_ASCII},
979 {C_US_ASCII, CS_ANSI_X3_4_1968},
982 {C_ISO_8859_1, CS_ISO_8859_1},
983 {C_ISO_8859_2, CS_ISO_8859_2},
984 {C_ISO_8859_3, CS_ISO_8859_3},
985 {C_ISO_8859_4, CS_ISO_8859_4},
986 {C_ISO_8859_5, CS_ISO_8859_5},
987 {C_ISO_8859_6, CS_ISO_8859_6},
988 {C_ISO_8859_7, CS_ISO_8859_7},
989 {C_ISO_8859_8, CS_ISO_8859_8},
990 {C_ISO_8859_9, CS_ISO_8859_9},
991 {C_ISO_8859_10, CS_ISO_8859_10},
992 {C_ISO_8859_11, CS_ISO_8859_11},
993 {C_ISO_8859_13, CS_ISO_8859_13},
994 {C_ISO_8859_14, CS_ISO_8859_14},
995 {C_ISO_8859_15, CS_ISO_8859_15},
996 {C_BALTIC, CS_BALTIC},
997 {C_CP1250, CS_CP1250},
998 {C_CP1251, CS_CP1251},
999 {C_CP1252, CS_CP1252},
1000 {C_CP1253, CS_CP1253},
1001 {C_CP1254, CS_CP1254},
1002 {C_CP1255, CS_CP1255},
1003 {C_CP1256, CS_CP1256},
1004 {C_CP1257, CS_CP1257},
1005 {C_CP1258, CS_CP1258},
1006 {C_WINDOWS_1250, CS_WINDOWS_1250},
1007 {C_WINDOWS_1251, CS_WINDOWS_1251},
1008 {C_WINDOWS_1252, CS_WINDOWS_1252},
1009 {C_WINDOWS_1253, CS_WINDOWS_1253},
1010 {C_WINDOWS_1254, CS_WINDOWS_1254},
1011 {C_WINDOWS_1255, CS_WINDOWS_1255},
1012 {C_WINDOWS_1256, CS_WINDOWS_1256},
1013 {C_WINDOWS_1257, CS_WINDOWS_1257},
1014 {C_WINDOWS_1258, CS_WINDOWS_1258},
1015 {C_KOI8_R, CS_KOI8_R},
1016 {C_KOI8_T, CS_KOI8_T},
1017 {C_KOI8_U, CS_KOI8_U},
1018 {C_ISO_2022_JP, CS_ISO_2022_JP},
1019 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1020 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3},
1021 {C_EUC_JP, CS_EUC_JP},
1022 {C_EUC_JP, CS_EUCJP},
1023 {C_EUC_JP_MS, CS_EUC_JP_MS},
1024 {C_SHIFT_JIS, CS_SHIFT_JIS},
1025 {C_SHIFT_JIS, CS_SHIFT__JIS},
1026 {C_SHIFT_JIS, CS_SJIS},
1027 {C_ISO_2022_KR, CS_ISO_2022_KR},
1028 {C_EUC_KR, CS_EUC_KR},
1029 {C_ISO_2022_CN, CS_ISO_2022_CN},
1030 {C_EUC_CN, CS_EUC_CN},
1031 {C_GB18030, CS_GB18030},
1032 {C_GB2312, CS_GB2312},
1034 {C_EUC_TW, CS_EUC_TW},
1036 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1037 {C_TIS_620, CS_TIS_620},
1038 {C_WINDOWS_874, CS_WINDOWS_874},
1039 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1040 {C_TCVN5712_1, CS_TCVN5712_1},
1043 static const struct {
1044 gchar *const locale;
1046 CharSet out_charset;
1047 } locale_table[] = {
1048 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1049 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1050 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1051 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1052 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1053 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1055 {"ja_JP" , C_SHIFT_JIS , C_ISO_2022_JP},
1057 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1059 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1060 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1061 {"zh_CN.GB18030" , C_GB18030 , C_GB18030},
1062 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1063 {"zh_CN.GBK" , C_GBK , C_GBK},
1064 {"zh_CN" , C_GB18030 , C_GB18030},
1065 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1066 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1067 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1068 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1069 {"zh_TW" , C_BIG5 , C_BIG5},
1071 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1072 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1073 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1074 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1075 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1076 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1077 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U},
1078 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1080 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1081 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1083 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1085 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1086 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1087 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1088 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1089 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1090 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1091 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1092 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1093 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1094 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1095 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1096 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1097 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1098 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1099 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1100 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1101 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1102 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1103 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1104 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1105 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1106 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1107 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1108 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1109 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1110 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1111 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1112 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1113 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1114 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1115 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1116 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1117 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1118 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1119 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1120 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1121 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1122 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1123 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1124 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1125 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1126 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1127 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1128 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1129 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1130 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1131 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1132 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1133 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1134 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1135 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1136 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1137 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1138 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1139 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1140 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1141 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1142 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1143 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1144 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1145 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1146 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1147 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1148 {"nb_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1149 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1150 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1151 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1152 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1153 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1154 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1155 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1156 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1157 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1158 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1159 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1161 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1162 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1163 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1164 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1165 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1166 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1167 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1168 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1170 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1171 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1173 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1175 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1176 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1177 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1178 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1180 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1182 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1183 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1184 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1185 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1186 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1187 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1188 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1189 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1190 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1191 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1192 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1193 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1194 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1195 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1196 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1197 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1198 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1200 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1201 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1202 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1203 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1205 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1206 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1208 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1210 {"ar_IN" , C_UTF_8 , C_UTF_8},
1211 {"en_IN" , C_UTF_8 , C_UTF_8},
1212 {"se_NO" , C_UTF_8 , C_UTF_8},
1213 {"ta_IN" , C_UTF_8 , C_UTF_8},
1214 {"te_IN" , C_UTF_8 , C_UTF_8},
1215 {"ur_PK" , C_UTF_8 , C_UTF_8},
1217 {"th_TH" , C_TIS_620 , C_TIS_620},
1218 /* {"th_TH" , C_WINDOWS_874}, */
1219 /* {"th_TH" , C_ISO_8859_11}, */
1221 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1222 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1224 {"C" , C_US_ASCII , C_US_ASCII},
1225 {"POSIX" , C_US_ASCII , C_US_ASCII},
1226 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1229 static GHashTable *conv_get_charset_to_str_table(void)
1231 static GHashTable *table;
1237 table = g_hash_table_new(NULL, g_direct_equal);
1239 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1240 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1243 (table, GUINT_TO_POINTER(charsets[i].charset),
1251 static GHashTable *conv_get_charset_from_str_table(void)
1253 static GHashTable *table;
1259 table = g_hash_table_new(str_case_hash, str_case_equal);
1261 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1262 g_hash_table_insert(table, charsets[i].name,
1263 GUINT_TO_POINTER(charsets[i].charset));
1269 const gchar *conv_get_charset_str(CharSet charset)
1273 table = conv_get_charset_to_str_table();
1274 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1277 CharSet conv_get_charset_from_str(const gchar *charset)
1281 if (!charset) return C_AUTO;
1283 table = conv_get_charset_from_str_table();
1284 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1287 static CharSet conv_get_locale_charset(void)
1289 static CharSet cur_charset = -1;
1290 const gchar *cur_locale;
1294 if (cur_charset != -1)
1297 cur_locale = conv_get_current_locale();
1299 cur_charset = C_US_ASCII;
1303 if (strcasestr(cur_locale, ".UTF-8") ||
1304 strcasestr(cur_locale, ".utf8")) {
1305 cur_charset = C_UTF_8;
1309 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1310 cur_charset = C_ISO_8859_15;
1314 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1317 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1318 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1319 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1320 strlen(locale_table[i].locale))) {
1321 cur_charset = locale_table[i].charset;
1323 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1324 !strchr(p + 1, '.')) {
1325 if (strlen(cur_locale) == 2 &&
1326 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1327 cur_charset = locale_table[i].charset;
1333 cur_charset = C_AUTO;
1337 static CharSet conv_get_locale_charset_no_utf8(void)
1339 static CharSet cur_charset = -1;
1340 const gchar *cur_locale;
1345 if (prefs_common.broken_are_utf8)
1346 return conv_get_locale_charset();
1348 if (cur_charset != -1)
1351 cur_locale = conv_get_current_locale();
1353 cur_charset = C_US_ASCII;
1357 if (strcasestr(cur_locale, "UTF-8")) {
1358 tmp = g_strdup(cur_locale);
1359 *(strcasestr(tmp, ".UTF-8")) = '\0';
1363 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1364 cur_charset = C_ISO_8859_15;
1368 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1371 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1372 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1373 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1374 strlen(locale_table[i].locale))) {
1375 cur_charset = locale_table[i].charset;
1377 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1378 !strchr(p + 1, '.')) {
1379 if (strlen(cur_locale) == 2 &&
1380 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1381 cur_charset = locale_table[i].charset;
1387 cur_charset = C_AUTO;
1391 const gchar *conv_get_locale_charset_str(void)
1393 static const gchar *codeset = NULL;
1396 codeset = conv_get_charset_str(conv_get_locale_charset());
1398 return codeset ? codeset : CS_INTERNAL;
1401 const gchar *conv_get_locale_charset_str_no_utf8(void)
1403 static const gchar *codeset = NULL;
1406 codeset = conv_get_charset_str(conv_get_locale_charset_no_utf8());
1408 return codeset ? codeset : CS_INTERNAL;
1411 static CharSet conv_get_outgoing_charset(void)
1413 static CharSet out_charset = -1;
1414 const gchar *cur_locale;
1418 if (out_charset != -1)
1421 cur_locale = conv_get_current_locale();
1423 out_charset = C_AUTO;
1427 if (strcasestr(cur_locale, "UTF-8")) {
1428 out_charset = C_UTF_8;
1432 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1433 out_charset = C_ISO_8859_15;
1437 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1440 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1441 strlen(locale_table[i].locale))) {
1442 out_charset = locale_table[i].out_charset;
1444 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1445 !strchr(p + 1, '.')) {
1446 if (strlen(cur_locale) == 2 &&
1447 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1448 out_charset = locale_table[i].out_charset;
1457 const gchar *conv_get_outgoing_charset_str(void)
1459 CharSet out_charset;
1462 out_charset = conv_get_outgoing_charset();
1463 str = conv_get_charset_str(out_charset);
1465 return str ? str : CS_UTF_8;
1468 const gchar *conv_get_current_locale(void)
1470 const gchar *cur_locale;
1473 cur_locale = g_win32_getlocale();
1475 cur_locale = g_getenv("LC_ALL");
1476 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1477 if (!cur_locale) cur_locale = g_getenv("LANG");
1478 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1479 #endif /* G_OS_WIN32 */
1481 debug_print("current locale: %s\n",
1482 cur_locale ? cur_locale : "(none)");
1487 static gboolean conv_is_ja_locale(void)
1489 static gint is_ja_locale = -1;
1490 const gchar *cur_locale;
1492 if (is_ja_locale != -1)
1493 return is_ja_locale != 0;
1496 cur_locale = conv_get_current_locale();
1498 if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0)
1502 return is_ja_locale != 0;
1505 gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding,
1506 gboolean addr_field)
1508 gchar buf[BUFFSIZE];
1510 if (is_ascii_str(str))
1511 return unmime_header(str, addr_field);
1513 if (default_encoding) {
1516 utf8_buf = conv_codeset_strdup
1517 (str, default_encoding, CS_INTERNAL);
1521 decoded_str = unmime_header(utf8_buf, addr_field);
1527 if (conv_is_ja_locale())
1528 conv_anytodisp(buf, sizeof(buf), str);
1530 conv_localetodisp(buf, sizeof(buf), str);
1532 return unmime_header(buf, addr_field);
1535 #define MAX_LINELEN 76
1536 #define MAX_HARD_LINELEN 996
1537 #define MIMESEP_BEGIN "=?"
1538 #define MIMESEP_END "?="
1540 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1542 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1547 if ((cond) && *srcp) { \
1548 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1549 if (isspace(*(destp - 1))) \
1551 else if (is_plain_text && isspace(*srcp)) \
1556 left = MAX_LINELEN - 1; \
1558 } else if (destp == (guchar *)dest && left < 7) { \
1559 if (isspace(*(destp - 1))) \
1561 else if (is_plain_text && isspace(*srcp)) \
1566 left = MAX_LINELEN - 1; \
1572 void conv_encode_header_full(gchar *dest, gint len, const gchar *src,
1573 gint header_len, gboolean addr_field,
1574 const gchar *out_encoding_)
1576 const gchar *cur_encoding;
1577 const gchar *out_encoding;
1581 const guchar *srcp = src;
1582 guchar *destp = dest;
1583 gboolean use_base64;
1585 cm_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE);
1586 cm_return_if_fail(destp != NULL);
1588 if (MB_CUR_MAX > 1) {
1590 mimesep_enc = "?B?";
1593 mimesep_enc = "?Q?";
1596 cur_encoding = CS_INTERNAL;
1599 out_encoding = out_encoding_;
1601 out_encoding = conv_get_outgoing_charset_str();
1603 if (!strcmp(out_encoding, CS_US_ASCII))
1604 out_encoding = CS_ISO_8859_1;
1606 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1607 strlen(mimesep_enc) + strlen(MIMESEP_END);
1609 left = MAX_LINELEN - header_len;
1612 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1614 while (isspace(*srcp)) {
1617 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1620 /* output as it is if the next word is ASCII string */
1621 if (!is_next_nonascii(srcp)) {
1624 word_len = get_next_word_len(srcp);
1625 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1626 while (word_len > 0) {
1627 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1636 /* don't include parentheses and quotes in encoded strings */
1637 if (addr_field && (*srcp == '(' || *srcp == ')' || *srcp == '"')) {
1638 LBREAK_IF_REQUIRED(left < 2, FALSE);
1649 const guchar *p = srcp;
1651 gint out_enc_str_len;
1652 gint mime_block_len;
1653 gboolean cont = FALSE;
1655 while (*p != '\0') {
1656 if (isspace(*p) && !is_next_nonascii(p + 1))
1658 /* don't include parentheses in encoded
1660 if (addr_field && (*p == '(' || *p == ')' || *p == '"'))
1663 mb_len = g_utf8_skip[*p];
1665 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1666 out_str = conv_codeset_strdup
1667 (part_str, cur_encoding, out_encoding);
1673 g_warning("conv_encode_header(): code conversion failed\n");
1674 conv_unreadable_8bit(part_str);
1675 out_str = g_strdup(part_str);
1678 out_str_len = strlen(out_str);
1681 out_enc_str_len = B64LEN(out_str_len);
1684 qp_get_q_encoding_len(out_str);
1688 if (mimestr_len + out_enc_str_len <= left) {
1691 } else if (cur_len == 0) {
1692 LBREAK_IF_REQUIRED(1, FALSE);
1701 Xstrndup_a(part_str, srcp, cur_len, );
1702 out_str = conv_codeset_strdup
1703 (part_str, cur_encoding, out_encoding);
1705 g_warning("conv_encode_header(): code conversion failed\n");
1706 conv_unreadable_8bit(part_str);
1707 out_str = g_strdup(part_str);
1709 out_str_len = strlen(out_str);
1712 out_enc_str_len = B64LEN(out_str_len);
1715 qp_get_q_encoding_len(out_str);
1717 Xalloca(enc_str, out_enc_str_len + 1, );
1719 base64_encode(enc_str, out_str, out_str_len);
1721 qp_q_encode(enc_str, out_str);
1725 /* output MIME-encoded string block */
1726 mime_block_len = mimestr_len + strlen(enc_str);
1727 g_snprintf(destp, mime_block_len + 1,
1728 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1729 out_encoding, mimesep_enc, enc_str);
1730 destp += mime_block_len;
1733 left -= mime_block_len;
1736 LBREAK_IF_REQUIRED(cont, FALSE);
1746 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1747 gint header_len, gboolean addr_field)
1749 conv_encode_header_full(dest,len,src,header_len,addr_field,NULL);
1752 #undef LBREAK_IF_REQUIRED
1753 gchar *conv_filename_from_utf8(const gchar *utf8_file)
1756 GError *error = NULL;
1758 fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error);
1760 debug_print("failed to convert encoding of file name: %s\n",
1762 g_error_free(error);
1765 fs_file = g_strdup(utf8_file);
1770 gchar *conv_filename_to_utf8(const gchar *fs_file)
1772 gchar *utf8_file = NULL;
1773 GError *error = NULL;
1775 utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error);
1777 g_warning("failed to convert encoding of file name: %s\n",
1779 g_error_free(error);
1782 if (!utf8_file || !g_utf8_validate(utf8_file, -1, NULL)) {
1784 utf8_file = g_strdup(fs_file);
1785 conv_unreadable_8bit(utf8_file);