2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
28 #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;
159 JISState state = JIS_ASCII;
161 while (*in != '\0' && (out - outbuf) > outlen - 3) {
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;
295 JISState state = JIS_ASCII;
297 while (*in != '\0' && (out - outbuf) < outlen - 3) {
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;
385 while (*in != '\0' && (out - outbuf) < outlen - 3) {
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",
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",
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_MACCYR))
700 if (!g_ascii_strcasecmp(encoding, CS_X_GBK))
707 CodeConverter *conv_code_converter_new(const gchar *src_charset)
711 src_charset = conv_get_fallback_for_private_encoding(src_charset);
713 conv = g_new0(CodeConverter, 1);
714 conv->code_conv_func = conv_get_code_conv_func(src_charset, NULL);
715 conv->charset_str = g_strdup(src_charset);
716 conv->charset = conv_get_charset_from_str(src_charset);
721 void conv_code_converter_destroy(CodeConverter *conv)
723 g_free(conv->charset_str);
727 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
730 if (conv->code_conv_func != conv_noconv)
731 return conv->code_conv_func(outbuf, outlen, inbuf);
735 str = conv_iconv_strdup(inbuf, conv->charset_str, NULL);
739 strncpy2(outbuf, str, outlen);
747 gchar *conv_codeset_strdup(const gchar *inbuf,
748 const gchar *src_code, const gchar *dest_code)
752 CodeConvFunc conv_func;
754 if (!strcmp2(src_code, dest_code)) {
755 CharSet dest_charset = conv_get_charset_from_str(dest_code);
756 if (strict_mode && dest_charset == C_UTF_8) {
757 /* ensure valid UTF-8 if target is UTF-8 */
758 if (!g_utf8_validate(inbuf, -1, NULL)) {
762 /* otherwise, try for a lucky day */
763 return g_strdup(inbuf);
766 src_code = conv_get_fallback_for_private_encoding(src_code);
767 conv_func = conv_get_code_conv_func(src_code, dest_code);
768 if (conv_func == conv_ustodisp && strict_mode && !is_ascii_str(inbuf))
771 if (conv_func != conv_noconv) {
772 len = (strlen(inbuf) + 1) * 3;
774 if (!buf) return NULL;
776 if (conv_func(buf, len, inbuf) == 0 || !strict_mode)
777 return g_realloc(buf, strlen(buf) + 1);
784 return conv_iconv_strdup(inbuf, src_code, dest_code);
787 static CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
788 const gchar *dest_charset_str)
790 CodeConvFunc code_conv = conv_noconv;
792 CharSet dest_charset;
794 if (!src_charset_str)
795 src_charset = conv_get_locale_charset();
797 src_charset = conv_get_charset_from_str(src_charset_str);
799 /* auto detection mode */
800 if (!src_charset_str && !dest_charset_str) {
801 if (conv_is_ja_locale())
802 return conv_anytodisp;
807 dest_charset = conv_get_charset_from_str(dest_charset_str);
809 if (dest_charset == C_US_ASCII)
810 return conv_ustodisp;
812 switch (src_charset) {
830 case C_ISO_2022_JP_2:
831 case C_ISO_2022_JP_3:
832 if (dest_charset == C_AUTO)
833 code_conv = conv_jistodisp;
834 else if (dest_charset == C_EUC_JP)
835 code_conv = conv_jistoeuc;
836 else if (dest_charset == C_UTF_8)
837 code_conv = conv_jistoutf8;
840 if (dest_charset == C_AUTO)
841 code_conv = conv_sjistodisp;
842 else if (dest_charset == C_EUC_JP)
843 code_conv = conv_sjistoeuc;
844 else if (dest_charset == C_UTF_8)
845 code_conv = conv_sjistoutf8;
848 if (dest_charset == C_AUTO)
849 code_conv = conv_euctodisp;
850 else if (dest_charset == C_ISO_2022_JP ||
851 dest_charset == C_ISO_2022_JP_2 ||
852 dest_charset == C_ISO_2022_JP_3)
853 code_conv = conv_euctojis;
854 else if (dest_charset == C_UTF_8)
855 code_conv = conv_euctoutf8;
858 if (dest_charset == C_EUC_JP)
859 code_conv = conv_utf8toeuc;
860 else if (dest_charset == C_ISO_2022_JP ||
861 dest_charset == C_ISO_2022_JP_2 ||
862 dest_charset == C_ISO_2022_JP_3)
863 code_conv = conv_utf8tojis;
872 static gchar *conv_iconv_strdup(const gchar *inbuf,
873 const gchar *src_code, const gchar *dest_code)
878 if (!src_code && !dest_code &&
879 g_utf8_validate(inbuf, -1, NULL))
880 return g_strdup(inbuf);
883 src_code = conv_get_outgoing_charset_str();
885 dest_code = CS_INTERNAL;
887 /* don't convert if src and dest codeset are identical */
888 if (!strcasecmp(src_code, dest_code))
889 return g_strdup(inbuf);
891 /* don't convert if dest codeset is US-ASCII */
892 if (!strcasecmp(src_code, CS_US_ASCII))
893 return g_strdup(inbuf);
895 /* don't convert if dest codeset is US-ASCII */
896 if (!strcasecmp(dest_code, CS_US_ASCII))
897 return g_strdup(inbuf);
899 cd = iconv_open(dest_code, src_code);
900 if (cd == (iconv_t)-1)
903 outbuf = conv_iconv_strdup_with_cd(inbuf, cd);
910 gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd)
912 const gchar *inbuf_p;
923 in_size = strlen(inbuf);
925 out_size = (in_size + 1) * 2;
926 outbuf = g_malloc(out_size);
930 #define EXPAND_BUF() \
932 len = outbuf_p - outbuf; \
934 outbuf = g_realloc(outbuf, out_size); \
935 outbuf_p = outbuf + len; \
936 out_left = out_size - len; \
939 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
940 &outbuf_p, &out_left)) == (size_t)-1) {
941 if (EILSEQ == errno) {
946 //g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno));
952 *outbuf_p++ = SUBST_CHAR;
954 } else if (EINVAL == errno) {
956 } else if (E2BIG == errno) {
959 g_warning("conv_iconv_strdup(): %s",
965 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
967 if (E2BIG == errno) {
970 g_warning("conv_iconv_strdup(): %s",
978 len = outbuf_p - outbuf;
979 outbuf = g_realloc(outbuf, len + 1);
985 static const struct {
989 {C_US_ASCII, CS_US_ASCII},
990 {C_US_ASCII, CS_ANSI_X3_4_1968},
993 {C_ISO_8859_1, CS_ISO_8859_1},
994 {C_ISO_8859_2, CS_ISO_8859_2},
995 {C_ISO_8859_3, CS_ISO_8859_3},
996 {C_ISO_8859_4, CS_ISO_8859_4},
997 {C_ISO_8859_5, CS_ISO_8859_5},
998 {C_ISO_8859_6, CS_ISO_8859_6},
999 {C_ISO_8859_7, CS_ISO_8859_7},
1000 {C_ISO_8859_8, CS_ISO_8859_8},
1001 {C_ISO_8859_9, CS_ISO_8859_9},
1002 {C_ISO_8859_10, CS_ISO_8859_10},
1003 {C_ISO_8859_11, CS_ISO_8859_11},
1004 {C_ISO_8859_13, CS_ISO_8859_13},
1005 {C_ISO_8859_14, CS_ISO_8859_14},
1006 {C_ISO_8859_15, CS_ISO_8859_15},
1007 {C_BALTIC, CS_BALTIC},
1008 {C_CP1250, CS_CP1250},
1009 {C_CP1251, CS_CP1251},
1010 {C_CP1252, CS_CP1252},
1011 {C_CP1253, CS_CP1253},
1012 {C_CP1254, CS_CP1254},
1013 {C_CP1255, CS_CP1255},
1014 {C_CP1256, CS_CP1256},
1015 {C_CP1257, CS_CP1257},
1016 {C_CP1258, CS_CP1258},
1017 {C_WINDOWS_1250, CS_WINDOWS_1250},
1018 {C_WINDOWS_1251, CS_WINDOWS_1251},
1019 {C_WINDOWS_1252, CS_WINDOWS_1252},
1020 {C_WINDOWS_1253, CS_WINDOWS_1253},
1021 {C_WINDOWS_1254, CS_WINDOWS_1254},
1022 {C_WINDOWS_1255, CS_WINDOWS_1255},
1023 {C_WINDOWS_1256, CS_WINDOWS_1256},
1024 {C_WINDOWS_1257, CS_WINDOWS_1257},
1025 {C_WINDOWS_1258, CS_WINDOWS_1258},
1026 {C_KOI8_R, CS_KOI8_R},
1027 {C_MACCYR, CS_MACCYR},
1028 {C_KOI8_T, CS_KOI8_T},
1029 {C_KOI8_U, CS_KOI8_U},
1030 {C_ISO_2022_JP, CS_ISO_2022_JP},
1031 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1032 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3},
1033 {C_EUC_JP, CS_EUC_JP},
1034 {C_EUC_JP, CS_EUCJP},
1035 {C_EUC_JP_MS, CS_EUC_JP_MS},
1036 {C_SHIFT_JIS, CS_SHIFT_JIS},
1037 {C_SHIFT_JIS, CS_SHIFT__JIS},
1038 {C_SHIFT_JIS, CS_SJIS},
1039 {C_ISO_2022_KR, CS_ISO_2022_KR},
1040 {C_EUC_KR, CS_EUC_KR},
1041 {C_ISO_2022_CN, CS_ISO_2022_CN},
1042 {C_EUC_CN, CS_EUC_CN},
1043 {C_GB18030, CS_GB18030},
1044 {C_GB2312, CS_GB2312},
1046 {C_EUC_TW, CS_EUC_TW},
1048 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1049 {C_TIS_620, CS_TIS_620},
1050 {C_WINDOWS_874, CS_WINDOWS_874},
1051 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1052 {C_TCVN5712_1, CS_TCVN5712_1},
1055 static const struct {
1056 gchar *const locale;
1058 CharSet out_charset;
1059 } locale_table[] = {
1060 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1061 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1062 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1063 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1064 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1065 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1067 {"ja_JP" , C_SHIFT_JIS , C_ISO_2022_JP},
1069 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1071 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1072 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1073 {"zh_CN.GB18030" , C_GB18030 , C_GB18030},
1074 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1075 {"zh_CN.GBK" , C_GBK , C_GBK},
1076 {"zh_CN" , C_GB18030 , C_GB18030},
1077 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1078 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1079 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1080 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1081 {"zh_TW" , C_BIG5 , C_BIG5},
1083 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1084 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1085 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1087 {"ru_RU" , C_WINDOWS_1251, C_KOI8_R},
1089 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1091 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1092 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1093 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U},
1094 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1096 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1097 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1099 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1101 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1102 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1103 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1104 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1105 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1106 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1107 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1108 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1109 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1110 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1111 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1112 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1113 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1114 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1115 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1116 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1117 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1118 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1119 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1120 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1121 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1122 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1123 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1124 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1125 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1126 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1127 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1128 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1129 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1130 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1131 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1132 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1133 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1134 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1135 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1136 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1137 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1138 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1139 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1140 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1141 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1142 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1143 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1144 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1145 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1146 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1147 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1148 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1149 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1150 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1151 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1152 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1153 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1154 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1155 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1156 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1157 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1158 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1159 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1160 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1161 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1162 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1163 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1164 {"nb_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1165 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1166 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1167 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1168 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1169 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1170 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1171 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1172 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1173 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1174 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1175 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1177 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1178 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1179 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1180 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1181 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1182 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1183 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1184 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1186 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1187 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1189 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1191 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1192 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1193 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1194 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1196 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1198 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1199 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1200 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1201 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1202 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1203 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1204 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1205 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1206 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1207 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1208 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1209 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1210 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1211 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1212 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1213 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1214 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1216 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1217 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1218 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1219 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1221 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1222 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1224 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1226 {"ar_IN" , C_UTF_8 , C_UTF_8},
1227 {"en_IN" , C_UTF_8 , C_UTF_8},
1228 {"se_NO" , C_UTF_8 , C_UTF_8},
1229 {"ta_IN" , C_UTF_8 , C_UTF_8},
1230 {"te_IN" , C_UTF_8 , C_UTF_8},
1231 {"ur_PK" , C_UTF_8 , C_UTF_8},
1233 {"th_TH" , C_TIS_620 , C_TIS_620},
1234 /* {"th_TH" , C_WINDOWS_874}, */
1235 /* {"th_TH" , C_ISO_8859_11}, */
1237 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1238 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1240 {"C" , C_US_ASCII , C_US_ASCII},
1241 {"POSIX" , C_US_ASCII , C_US_ASCII},
1242 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1245 static GHashTable *conv_get_charset_to_str_table(void)
1247 static GHashTable *table;
1253 table = g_hash_table_new(NULL, g_direct_equal);
1255 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1256 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1259 (table, GUINT_TO_POINTER(charsets[i].charset),
1267 static GHashTable *conv_get_charset_from_str_table(void)
1269 static GHashTable *table;
1275 table = g_hash_table_new(str_case_hash, str_case_equal);
1277 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1278 g_hash_table_insert(table, charsets[i].name,
1279 GUINT_TO_POINTER(charsets[i].charset));
1285 const gchar *conv_get_charset_str(CharSet charset)
1289 table = conv_get_charset_to_str_table();
1290 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1293 CharSet conv_get_charset_from_str(const gchar *charset)
1297 if (!charset) return C_AUTO;
1299 table = conv_get_charset_from_str_table();
1300 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1303 static CharSet conv_get_locale_charset(void)
1305 static CharSet cur_charset = -1;
1306 const gchar *cur_locale;
1310 if (cur_charset != -1)
1313 cur_locale = conv_get_current_locale();
1315 cur_charset = C_US_ASCII;
1319 if (strcasestr(cur_locale, "UTF-8") ||
1320 strcasestr(cur_locale, "utf8")) {
1321 cur_charset = C_UTF_8;
1325 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1326 cur_charset = C_ISO_8859_15;
1330 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1333 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1334 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1335 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1336 strlen(locale_table[i].locale))) {
1337 cur_charset = locale_table[i].charset;
1339 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1340 !strchr(p + 1, '.')) {
1341 if (strlen(cur_locale) == 2 &&
1342 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1343 cur_charset = locale_table[i].charset;
1349 cur_charset = C_AUTO;
1353 static CharSet conv_get_locale_charset_no_utf8(void)
1355 static CharSet cur_charset = -1;
1356 const gchar *cur_locale;
1360 if (prefs_common.broken_are_utf8) {
1361 cur_charset = C_UTF_8;
1365 cur_locale = conv_get_current_locale();
1367 cur_charset = C_US_ASCII;
1371 if (strcasestr(cur_locale, "UTF-8") ||
1372 strcasestr(cur_locale, "utf8")) {
1373 cur_charset = C_UTF_8;
1377 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1378 cur_charset = C_ISO_8859_15;
1382 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1385 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1386 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1387 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1388 strlen(locale_table[i].locale))) {
1389 cur_charset = locale_table[i].charset;
1391 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1392 !strchr(p + 1, '.')) {
1393 if (strlen(cur_locale) == 2 &&
1394 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1395 cur_charset = locale_table[i].charset;
1401 cur_charset = C_AUTO;
1405 const gchar *conv_get_locale_charset_str(void)
1407 static const gchar *codeset = NULL;
1410 codeset = conv_get_charset_str(conv_get_locale_charset());
1412 return codeset ? codeset : CS_INTERNAL;
1415 const gchar *conv_get_locale_charset_str_no_utf8(void)
1417 static const gchar *codeset = NULL;
1420 codeset = conv_get_charset_str(conv_get_locale_charset_no_utf8());
1422 return codeset ? codeset : CS_INTERNAL;
1425 static CharSet conv_get_outgoing_charset(void)
1427 static CharSet out_charset = -1;
1428 const gchar *cur_locale;
1432 if (out_charset != -1)
1435 cur_locale = conv_get_current_locale();
1437 out_charset = C_AUTO;
1441 if (strcasestr(cur_locale, "UTF-8") ||
1442 strcasestr(cur_locale, "utf8")) {
1443 out_charset = C_UTF_8;
1447 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1448 out_charset = C_ISO_8859_15;
1452 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1455 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale,
1456 strlen(locale_table[i].locale))) {
1457 out_charset = locale_table[i].out_charset;
1459 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1460 !strchr(p + 1, '.')) {
1461 if (strlen(cur_locale) == 2 &&
1462 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1463 out_charset = locale_table[i].out_charset;
1472 const gchar *conv_get_outgoing_charset_str(void)
1474 CharSet out_charset;
1477 out_charset = conv_get_outgoing_charset();
1478 str = conv_get_charset_str(out_charset);
1480 return str ? str : CS_UTF_8;
1483 const gchar *conv_get_current_locale(void)
1485 const gchar *cur_locale;
1488 cur_locale = g_win32_getlocale();
1490 cur_locale = g_getenv("LC_ALL");
1491 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1492 if (!cur_locale) cur_locale = g_getenv("LANG");
1493 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1494 #endif /* G_OS_WIN32 */
1496 debug_print("current locale: %s\n",
1497 cur_locale ? cur_locale : "(none)");
1502 static gboolean conv_is_ja_locale(void)
1504 static gint is_ja_locale = -1;
1505 const gchar *cur_locale;
1507 if (is_ja_locale != -1)
1508 return is_ja_locale != 0;
1511 cur_locale = conv_get_current_locale();
1513 if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0)
1517 return is_ja_locale != 0;
1520 gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding,
1521 gboolean addr_field)
1523 gchar buf[BUFFSIZE];
1525 if (is_ascii_str(str))
1526 return unmime_header(str, addr_field);
1528 if (default_encoding) {
1531 utf8_buf = conv_codeset_strdup
1532 (str, default_encoding, CS_INTERNAL);
1536 decoded_str = unmime_header(utf8_buf, addr_field);
1542 if (conv_is_ja_locale())
1543 conv_anytodisp(buf, sizeof(buf), str);
1545 conv_localetodisp(buf, sizeof(buf), str);
1547 return unmime_header(buf, addr_field);
1550 #define MAX_LINELEN 76
1551 #define MAX_HARD_LINELEN 996
1552 #define MIMESEP_BEGIN "=?"
1553 #define MIMESEP_END "?="
1555 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1557 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1562 if ((cond) && *srcp) { \
1563 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1564 if (isspace(*(destp - 1))) \
1566 else if (is_plain_text && isspace(*srcp)) \
1571 left = MAX_LINELEN - 1; \
1573 } else if (destp == (guchar *)dest && left < 7) { \
1574 if (isspace(*(destp - 1))) \
1576 else if (is_plain_text && isspace(*srcp)) \
1581 left = MAX_LINELEN - 1; \
1587 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1589 void conv_encode_header_full(gchar *dest, gint len, const gchar *src,
1590 gint header_len, gboolean addr_field,
1591 const gchar *out_encoding_)
1593 const gchar *cur_encoding;
1594 const gchar *out_encoding;
1598 const guchar *srcp = src;
1599 guchar *destp = dest;
1600 gboolean use_base64;
1602 cm_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE);
1603 cm_return_if_fail(destp != NULL);
1605 if (MB_CUR_MAX > 1) {
1607 mimesep_enc = "?B?";
1610 mimesep_enc = "?Q?";
1613 cur_encoding = CS_INTERNAL;
1616 out_encoding = out_encoding_;
1618 out_encoding = conv_get_outgoing_charset_str();
1620 if (!strcmp(out_encoding, CS_US_ASCII))
1621 out_encoding = CS_ISO_8859_1;
1623 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1624 strlen(mimesep_enc) + strlen(MIMESEP_END);
1626 left = MAX_LINELEN - header_len;
1629 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1631 while (isspace(*srcp)) {
1634 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1637 /* output as it is if the next word is ASCII string */
1638 if (!is_next_nonascii(srcp)) {
1641 word_len = get_next_word_len(srcp);
1642 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1643 while (word_len > 0) {
1644 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1653 /* don't include parentheses and quotes in encoded strings */
1654 if (addr_field && (*srcp == '(' || *srcp == ')' || *srcp == '"')) {
1655 LBREAK_IF_REQUIRED(left < 2, FALSE);
1666 const guchar *p = srcp;
1668 gint out_enc_str_len;
1669 gint mime_block_len;
1670 gboolean cont = FALSE;
1672 while (*p != '\0') {
1673 if (isspace(*p) && !is_next_nonascii(p + 1))
1675 /* don't include parentheses in encoded
1677 if (addr_field && (*p == '(' || *p == ')' || *p == '"'))
1680 mb_len = g_utf8_skip[*p];
1682 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1683 out_str = conv_codeset_strdup
1684 (part_str, cur_encoding, out_encoding);
1690 g_warning("conv_encode_header(): code conversion failed");
1691 conv_unreadable_8bit(part_str);
1692 out_str = g_strdup(part_str);
1695 out_str_len = strlen(out_str);
1698 out_enc_str_len = B64LEN(out_str_len);
1701 qp_get_q_encoding_len(out_str);
1705 if (mimestr_len + out_enc_str_len <= left) {
1708 } else if (cur_len == 0) {
1710 LBREAK_IF_REQUIRED(1, FALSE);
1719 Xstrndup_a(part_str, srcp, cur_len, );
1720 out_str = conv_codeset_strdup
1721 (part_str, cur_encoding, out_encoding);
1723 g_warning("conv_encode_header(): code conversion failed");
1724 conv_unreadable_8bit(part_str);
1725 out_str = g_strdup(part_str);
1727 out_str_len = strlen(out_str);
1730 out_enc_str_len = B64LEN(out_str_len);
1733 qp_get_q_encoding_len(out_str);
1736 enc_str = g_base64_encode(out_str, out_str_len);
1738 Xalloca(enc_str, out_enc_str_len + 1, );
1739 qp_q_encode(enc_str, out_str);
1744 /* output MIME-encoded string block */
1745 mime_block_len = mimestr_len + strlen(enc_str);
1746 g_snprintf(destp, mime_block_len + 1,
1747 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1748 out_encoding, mimesep_enc, enc_str);
1753 destp += mime_block_len;
1756 left -= mime_block_len;
1759 LBREAK_IF_REQUIRED(cont, FALSE);
1769 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1770 gint header_len, gboolean addr_field)
1772 conv_encode_header_full(dest,len,src,header_len,addr_field,NULL);
1775 #undef LBREAK_IF_REQUIRED
1778 gchar *conv_filename_from_utf8(const gchar *utf8_file)
1781 GError *error = NULL;
1783 fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error);
1785 debug_print("failed to convert encoding of file name: %s\n",
1787 g_error_free(error);
1790 fs_file = g_strdup(utf8_file);
1795 gchar *conv_filename_to_utf8(const gchar *fs_file)
1797 gchar *utf8_file = NULL;
1798 GError *error = NULL;
1800 utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error);
1802 g_warning("failed to convert encoding of file name: %s",
1804 g_error_free(error);
1807 if (!utf8_file || !g_utf8_validate(utf8_file, -1, NULL)) {
1809 utf8_file = g_strdup(fs_file);
1810 conv_unreadable_8bit(utf8_file);