2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2002 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.
41 #include "quoted-printable.h"
43 #include "prefs_common.h"
53 #define SUBST_CHAR '_'
56 #define iseuckanji(c) \
57 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe)
58 #define iseuchwkana1(c) \
59 (((c) & 0xff) == 0x8e)
60 #define iseuchwkana2(c) \
61 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
63 (((c) & 0xff) == 0x8f)
64 #define isunprintableeuckanji(c) \
65 (((c) & 0xff) >= 0xa9 && ((c) & 0xff) <= 0xaf)
66 #define issjiskanji1(c) \
67 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
68 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc))
69 #define issjiskanji2(c) \
70 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
71 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
72 #define issjishwkana(c) \
73 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
76 if (state != JIS_KANJI) { \
84 if (state != JIS_ASCII) { \
92 if (state != JIS_HWKANA) { \
100 if (state != JIS_AUXKANJI) { \
105 state = JIS_AUXKANJI; \
108 void conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
110 const guchar *in = inbuf;
111 guchar *out = outbuf;
112 JISState state = JIS_ASCII;
114 while (*in != '\0') {
118 if (*(in + 1) == '@' || *(in + 1) == 'B') {
121 } else if (*(in + 1) == '(' &&
123 state = JIS_AUXKANJI;
126 /* unknown escape sequence */
129 } else if (*in == '(') {
130 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
133 } else if (*(in + 1) == 'I') {
137 /* unknown escape sequence */
141 /* unknown escape sequence */
144 } else if (*in == 0x0e) {
147 } else if (*in == 0x0f) {
156 *out++ = *in++ | 0x80;
157 if (*in == '\0') break;
158 *out++ = *in++ | 0x80;
162 *out++ = *in++ | 0x80;
166 *out++ = *in++ | 0x80;
167 if (*in == '\0') break;
168 *out++ = *in++ | 0x80;
177 void conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
179 const guchar *in = inbuf;
180 guchar *out = outbuf;
181 JISState state = JIS_ASCII;
183 while (*in != '\0') {
187 } else if (iseuckanji(*in)) {
188 if (iseuckanji(*(in + 1))) {
190 *out++ = *in++ & 0x7f;
191 *out++ = *in++ & 0x7f;
196 if (*in != '\0' && !isascii(*in)) {
201 } else if (iseuchwkana1(*in)) {
203 if (iseuchwkana2(*in)) {
205 *out++ = *in++ & 0x7f;
208 if (*in != '\0' && !isascii(*in)) {
213 } else if (iseucaux(*in)) {
215 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
217 *out++ = *in++ & 0x7f;
218 *out++ = *in++ & 0x7f;
221 if (*in != '\0' && !isascii(*in)) {
224 if (*in != '\0' && !isascii(*in)) {
241 void conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
243 const guchar *in = inbuf;
244 guchar *out = outbuf;
246 while (*in != '\0') {
249 } else if (issjiskanji1(*in)) {
250 if (issjiskanji2(*(in + 1))) {
252 guchar out2 = *(in + 1);
255 row = out1 < 0xa0 ? 0x70 : 0xb0;
257 out1 = (out1 - row) * 2 - 1;
258 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
260 out1 = (out1 - row) * 2;
264 *out++ = out1 | 0x80;
265 *out++ = out2 | 0x80;
270 if (*in != '\0' && !isascii(*in)) {
275 } else if (issjishwkana(*in)) {
287 void conv_anytoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
289 switch (conv_guess_encoding(inbuf)) {
291 conv_jistoeuc(outbuf, outlen, inbuf);
294 conv_sjistoeuc(outbuf, outlen, inbuf);
297 strncpy2(outbuf, inbuf, outlen);
302 void conv_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
304 switch (conv_guess_encoding(inbuf)) {
306 conv_euctojis(outbuf, outlen, inbuf);
309 strncpy2(outbuf, inbuf, outlen);
314 void conv_unreadable_eucjp(gchar *str)
316 register guchar *p = str;
320 /* convert CR+LF -> LF */
321 if (*p == '\r' && *(p + 1) == '\n')
322 memmove(p, p + 1, strlen(p));
323 /* printable 7 bit code */
325 } else if (iseuckanji(*p)) {
326 if (iseuckanji(*(p + 1)) && !isunprintableeuckanji(*p))
327 /* printable euc-jp code */
330 /* substitute unprintable code */
339 } else if (iseuchwkana1(*p)) {
340 if (iseuchwkana2(*(p + 1)))
341 /* euc-jp hankaku kana */
345 } else if (iseucaux(*p)) {
346 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
347 /* auxiliary kanji */
352 /* substitute unprintable 1 byte code */
357 void conv_unreadable_8bit(gchar *str)
359 register guchar *p = str;
362 /* convert CR+LF -> LF */
363 if (*p == '\r' && *(p + 1) == '\n')
364 memmove(p, p + 1, strlen(p));
365 else if (!isascii(*p)) *p = SUBST_CHAR;
370 void conv_unreadable_latin(gchar *str)
372 register guchar *p = str;
375 /* convert CR+LF -> LF */
376 if (*p == '\r' && *(p + 1) == '\n')
377 memmove(p, p + 1, strlen(p));
378 else if ((*p & 0xff) >= 0x80 && (*p & 0xff) <= 0x9f)
386 void conv_mb_alnum(gchar *str)
388 static guchar char_tbl[] = {
390 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
391 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
393 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
394 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
396 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
397 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
399 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
400 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
402 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
403 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
406 register guchar *p = str;
413 register guchar ch = *(p + 1);
415 if (ch >= 0xb0 && ch <= 0xfa) {
420 memmove(p, p + 1, len);
426 } else if (*p == 0xa1) {
427 register guchar ch = *(p + 1);
429 if (ch >= 0xa0 && ch <= 0xef &&
430 NCV != char_tbl[ch - 0xa0]) {
431 *p = char_tbl[ch - 0xa0];
434 memmove(p, p + 1, len);
440 } else if (iseuckanji(*p)) {
450 CharSet conv_guess_encoding(const gchar *str)
452 const guchar *p = str;
453 CharSet guessed = C_US_ASCII;
456 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
457 if (guessed == C_US_ASCII)
458 return C_ISO_2022_JP;
460 } else if (isascii(*p)) {
462 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
463 if (*p >= 0xfd && *p <= 0xfe)
465 else if (guessed == C_SHIFT_JIS) {
466 if ((issjiskanji1(*p) &&
467 issjiskanji2(*(p + 1))) ||
469 guessed = C_SHIFT_JIS;
475 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
476 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
477 guessed = C_SHIFT_JIS;
481 } else if (issjishwkana(*p)) {
482 guessed = C_SHIFT_JIS;
492 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
494 conv_jistoeuc(outbuf, outlen, inbuf);
495 conv_unreadable_eucjp(outbuf);
498 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
500 conv_sjistoeuc(outbuf, outlen, inbuf);
501 conv_unreadable_eucjp(outbuf);
504 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
506 strncpy2(outbuf, inbuf, outlen);
507 conv_unreadable_eucjp(outbuf);
510 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
512 conv_anytoeuc(outbuf, outlen, inbuf);
513 conv_unreadable_eucjp(outbuf);
516 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
518 strncpy2(outbuf, inbuf, outlen);
519 conv_unreadable_8bit(outbuf);
522 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
524 strncpy2(outbuf, inbuf, outlen);
525 conv_unreadable_latin(outbuf);
528 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
530 strncpy2(outbuf, inbuf, outlen);
533 CodeConverter *conv_code_converter_new(const gchar *charset)
537 conv = g_new0(CodeConverter, 1);
538 conv->code_conv_func = conv_get_code_conv_func(charset, NULL);
539 conv->charset_str = g_strdup(charset);
540 conv->charset = conv_get_charset_from_str(charset);
545 void conv_code_converter_destroy(CodeConverter *conv)
547 g_free(conv->charset_str);
551 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
555 if (conv->code_conv_func != conv_noconv)
556 conv->code_conv_func(outbuf, outlen, inbuf);
560 str = conv_codeset_strdup(inbuf, conv->charset_str, NULL);
564 strncpy2(outbuf, str, outlen);
568 #else /* !HAVE_LIBJCONV */
569 conv->code_conv_func(outbuf, outlen, inbuf);
575 gchar *conv_codeset_strdup(const gchar *inbuf,
576 const gchar *src_codeset, const gchar *dest_codeset)
580 CodeConvFunc conv_func;
583 const gchar *const *codesets;
587 conv_func = conv_get_code_conv_func(src_codeset, dest_codeset);
588 if (conv_func != conv_noconv) {
589 len = (strlen(inbuf) + 1) * 3;
591 if (!buf) return NULL;
593 conv_func(buf, len, inbuf);
594 return g_realloc(buf, strlen(buf) + 1);
598 return g_strdup(inbuf);
600 /* don't convert if src and dest codeset are identical */
601 if (src_codeset && dest_codeset &&
602 !strcasecmp(src_codeset, dest_codeset))
603 return g_strdup(inbuf);
606 codesets = &src_codeset;
609 codesets = jconv_info_get_pref_codesets(&n_codesets);
611 dest_codeset = conv_get_current_charset_str();
612 /* don't convert if current codeset is US-ASCII */
613 if (!strcasecmp(dest_codeset, CS_US_ASCII))
614 return g_strdup(inbuf);
617 if (jconv_alloc_conv(inbuf, strlen(inbuf), &buf, &len,
618 codesets, n_codesets,
619 &actual_codeset, dest_codeset)
624 g_warning("code conversion from %s to %s failed\n",
625 codesets && codesets[0] ? codesets[0] : "(unknown)",
630 #endif /* HAVE_LIBJCONV */
633 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
634 const gchar *dest_charset_str)
636 CodeConvFunc code_conv = conv_noconv;
638 CharSet dest_charset;
640 if (!src_charset_str)
641 src_charset = conv_get_current_charset();
643 src_charset = conv_get_charset_from_str(src_charset_str);
645 /* auto detection mode */
646 if (!src_charset_str && !dest_charset_str) {
647 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
648 return conv_anytodisp;
653 dest_charset = conv_get_charset_from_str(dest_charset_str);
655 switch (src_charset) {
657 case C_ISO_2022_JP_2:
658 if (dest_charset == C_AUTO)
659 code_conv = conv_jistodisp;
660 else if (dest_charset == C_EUC_JP)
661 code_conv = conv_jistoeuc;
664 if (dest_charset == C_AUTO)
665 code_conv = conv_ustodisp;
679 if (dest_charset == C_AUTO)
680 code_conv = conv_latintodisp;
683 if (dest_charset == C_AUTO)
684 code_conv = conv_sjistodisp;
685 else if (dest_charset == C_EUC_JP)
686 code_conv = conv_sjistoeuc;
689 if (dest_charset == C_AUTO)
690 code_conv = conv_euctodisp;
691 else if (dest_charset == C_ISO_2022_JP ||
692 dest_charset == C_ISO_2022_JP_2)
693 code_conv = conv_euctojis;
702 static const struct {
706 {C_US_ASCII, CS_US_ASCII},
707 {C_US_ASCII, CS_ANSI_X3_4_1968},
709 {C_ISO_8859_1, CS_ISO_8859_1},
710 {C_ISO_8859_2, CS_ISO_8859_2},
711 {C_ISO_8859_4, CS_ISO_8859_4},
712 {C_ISO_8859_5, CS_ISO_8859_5},
713 {C_ISO_8859_7, CS_ISO_8859_7},
714 {C_ISO_8859_8, CS_ISO_8859_8},
715 {C_ISO_8859_9, CS_ISO_8859_9},
716 {C_ISO_8859_11, CS_ISO_8859_11},
717 {C_ISO_8859_13, CS_ISO_8859_13},
718 {C_ISO_8859_15, CS_ISO_8859_15},
719 {C_BALTIC, CS_BALTIC},
720 {C_CP1251, CS_CP1251},
721 {C_WINDOWS_1251, CS_WINDOWS_1251},
722 {C_KOI8_R, CS_KOI8_R},
723 {C_KOI8_U, CS_KOI8_U},
724 {C_ISO_2022_JP, CS_ISO_2022_JP},
725 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
726 {C_EUC_JP, CS_EUC_JP},
727 {C_EUC_JP, CS_EUCJP},
728 {C_SHIFT_JIS, CS_SHIFT_JIS},
729 {C_SHIFT_JIS, CS_SHIFT__JIS},
730 {C_SHIFT_JIS, CS_SJIS},
731 {C_ISO_2022_KR, CS_ISO_2022_KR},
732 {C_EUC_KR, CS_EUC_KR},
733 {C_ISO_2022_CN, CS_ISO_2022_CN},
734 {C_EUC_CN, CS_EUC_CN},
735 {C_GB2312, CS_GB2312},
736 {C_EUC_TW, CS_EUC_TW},
738 {C_TIS_620, CS_TIS_620},
739 {C_WINDOWS_874, CS_WINDOWS_874},
743 static const struct {
748 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
749 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
750 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
751 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
752 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
753 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
754 {"ko_KR" , C_EUC_KR , C_EUC_KR},
755 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
756 {"zh_CN" , C_GB2312 , C_GB2312},
757 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
758 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
759 {"zh_TW" , C_BIG5 , C_BIG5},
761 {"ru_RU.KOI8-R" , C_KOI8_R , C_ISO_8859_5},
762 {"ru_RU.CP1251" , C_WINDOWS_1251, C_ISO_8859_5},
764 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
766 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
767 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
768 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
769 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
770 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
771 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
772 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
773 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
774 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
775 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
776 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
777 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
778 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
779 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
780 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
782 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
783 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
784 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
785 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
786 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
787 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
788 {"ru_RU" , C_ISO_8859_5 , C_ISO_8859_5},
789 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
790 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
791 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
793 {"th_TH" , C_TIS_620 , C_TIS_620},
794 /* {"th_TH" , C_WINDOWS_874}, */
795 /* {"th_TH" , C_ISO_8859_11}, */
797 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
798 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
799 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
800 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
801 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
803 {"C" , C_US_ASCII , C_US_ASCII},
804 {"POSIX" , C_US_ASCII , C_US_ASCII},
805 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
807 #endif /* !HAVE_LIBJCONV */
809 const gchar *conv_get_charset_str(CharSet charset)
813 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
814 if (charsets[i].charset == charset)
815 return charsets[i].name;
821 CharSet conv_get_charset_from_str(const gchar *charset)
825 if (!charset) return C_AUTO;
827 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
828 if (!strcasecmp(charsets[i].name, charset))
829 return charsets[i].charset;
835 CharSet conv_get_current_charset(void)
837 static CharSet cur_charset = -1;
841 const gchar *cur_codeset;
843 const gchar *cur_locale;
846 if (cur_charset != -1)
850 cur_codeset = jconv_info_get_current_codeset();
851 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
852 if (!strcasecmp(cur_codeset, charsets[i].name)) {
853 cur_charset = charsets[i].charset;
858 cur_locale = conv_get_current_locale();
860 cur_charset = C_US_ASCII;
864 if (strcasestr(cur_locale, "UTF-8")) {
865 cur_charset = C_UTF_8;
869 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
872 /* "ja_JP.EUC" matches with "ja_JP.eucJP" and "ja_JP.EUC" */
873 /* "ja_JP" matches with "ja_JP.xxxx" and "ja" */
874 if (!strncasecmp(cur_locale, locale_table[i].locale,
875 strlen(locale_table[i].locale))) {
876 cur_charset = locale_table[i].charset;
878 } else if ((p = strchr(locale_table[i].locale, '_')) &&
879 !strchr(p + 1, '.')) {
880 if (strlen(cur_locale) == 2 &&
881 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
882 cur_charset = locale_table[i].charset;
889 cur_charset = C_AUTO;
893 const gchar *conv_get_current_charset_str(void)
895 static const gchar *codeset = NULL;
898 codeset = conv_get_charset_str(conv_get_current_charset());
900 return codeset ? codeset : "US-ASCII";
903 CharSet conv_get_outgoing_charset(void)
905 static CharSet out_charset = -1;
909 gint j, n_pref_codesets;
910 const gchar *const *pref_codesets;
912 const gchar *cur_locale;
915 if (out_charset != -1)
919 /* skip US-ASCII and UTF-8 */
920 pref_codesets = jconv_info_get_pref_codesets(&n_pref_codesets);
921 for (i = 0; i < n_pref_codesets; i++) {
922 for (j = 3; j < sizeof(charsets) / sizeof(charsets[0]); j++) {
923 if (!strcasecmp(pref_codesets[i], charsets[j].name)) {
924 out_charset = charsets[j].charset;
930 for (i = 0; i < n_pref_codesets; i++) {
931 if (!strcasecmp(pref_codesets[i], "UTF-8")) {
932 out_charset = C_UTF_8;
937 out_charset = C_AUTO;
939 cur_locale = conv_get_current_locale();
941 out_charset = C_AUTO;
945 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
948 if (!strncasecmp(cur_locale, locale_table[i].locale,
949 strlen(locale_table[i].locale))) {
950 out_charset = locale_table[i].out_charset;
952 } else if ((p = strchr(locale_table[i].locale, '_')) &&
953 !strchr(p + 1, '.')) {
954 if (strlen(cur_locale) == 2 &&
955 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
956 out_charset = locale_table[i].out_charset;
962 /* encoding conversion without libjconv is only supported
963 on Japanese locale for now */
964 if (out_charset == C_ISO_2022_JP)
967 out_charset = conv_get_current_charset();
973 const gchar *conv_get_outgoing_charset_str(void)
978 if (prefs_common.outgoing_charset) {
979 if (!isalpha(prefs_common.outgoing_charset[0])) {
980 g_free(prefs_common.outgoing_charset);
981 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
982 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
983 return prefs_common.outgoing_charset;
986 out_charset = conv_get_outgoing_charset();
987 str = conv_get_charset_str(out_charset);
989 return str ? str : "US-ASCII";
992 const gchar *conv_get_current_locale(void)
996 cur_locale = g_getenv("LC_ALL");
997 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
998 if (!cur_locale) cur_locale = g_getenv("LANG");
999 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1001 debug_print("current locale: %s\n",
1002 cur_locale ? cur_locale : "(none)");
1007 void conv_unmime_header_overwrite(gchar *str)
1011 CharSet cur_charset;
1013 cur_charset = conv_get_current_charset();
1015 if (cur_charset == C_EUC_JP) {
1016 buflen = strlen(str) * 2 + 1;
1017 Xalloca(buf, buflen, return);
1018 conv_anytodisp(buf, buflen, str);
1019 unmime_header(str, buf);
1021 buflen = strlen(str) + 1;
1022 Xalloca(buf, buflen, return);
1023 unmime_header(buf, str);
1024 strncpy2(str, buf, buflen);
1028 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1029 const gchar *charset)
1031 CharSet cur_charset;
1033 cur_charset = conv_get_current_charset();
1035 if (cur_charset == C_EUC_JP) {
1039 buflen = strlen(str) * 2 + 1;
1040 Xalloca(buf, buflen, return);
1041 conv_anytodisp(buf, buflen, str);
1042 unmime_header(outbuf, buf);
1044 unmime_header(outbuf, str);
1047 #define MAX_LINELEN 76
1048 #define MIMESEP_BEGIN "=?"
1049 #define MIMESEP_END "?="
1051 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1053 #define LBREAK_IF_REQUIRED(cond) \
1055 if (len - (destp - dest) < MAX_LINELEN + 2) { \
1060 if ((cond) && *srcp) { \
1061 if (destp > dest && isspace(*(destp - 1))) \
1063 else if (isspace(*srcp)) \
1068 left = MAX_LINELEN - 1; \
1073 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1076 const gchar *cur_encoding;
1077 const gchar *out_encoding;
1081 const gchar *srcp = src;
1082 gchar *destp = dest;
1083 gboolean use_base64;
1085 if (MB_CUR_MAX > 1) {
1087 mimesep_enc = "?B?";
1090 mimesep_enc = "?Q?";
1093 cur_encoding = conv_get_current_charset_str();
1094 if (!strcmp(cur_encoding, "US-ASCII"))
1095 cur_encoding = "ISO-8859-1";
1096 out_encoding = conv_get_outgoing_charset_str();
1097 if (!strcmp(out_encoding, "US-ASCII"))
1098 out_encoding = "ISO-8859-1";
1100 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1101 strlen(mimesep_enc) + strlen(MIMESEP_END);
1103 left = MAX_LINELEN - header_len;
1106 LBREAK_IF_REQUIRED(left <= 0);
1108 while (isspace(*srcp)) {
1111 LBREAK_IF_REQUIRED(left <= 0);
1114 /* output as it is if the next word is ASCII string */
1115 if (!is_next_nonascii(srcp)) {
1118 word_len = get_next_word_len(srcp);
1119 LBREAK_IF_REQUIRED(left < word_len);
1120 while (word_len > 0) {
1121 LBREAK_IF_REQUIRED(left <= 0);
1136 const gchar *p = srcp;
1138 gint out_enc_str_len;
1139 gint mime_block_len;
1140 gboolean cont = FALSE;
1142 while (*p != '\0') {
1143 if (isspace(*p) && !is_next_nonascii(p + 1))
1146 if (MB_CUR_MAX > 1) {
1147 mb_len = mblen(p, MB_CUR_MAX);
1149 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1155 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1156 out_str = conv_codeset_strdup
1157 (part_str, cur_encoding, out_encoding);
1159 g_warning("conv_encode_header(): code conversion failed\n");
1160 out_str = g_strdup(out_str);
1162 out_str_len = strlen(out_str);
1165 out_enc_str_len = B64LEN(out_str_len);
1168 qp_get_q_encoding_len(out_str);
1172 if (mimestr_len + out_enc_str_len <= left) {
1175 } else if (cur_len == 0) {
1176 LBREAK_IF_REQUIRED(1);
1185 Xstrndup_a(part_str, srcp, cur_len, );
1186 out_str = conv_codeset_strdup
1187 (part_str, cur_encoding, out_encoding);
1189 g_warning("conv_encode_header(): code conversion failed\n");
1190 out_str = g_strdup(out_str);
1192 out_str_len = strlen(out_str);
1195 out_enc_str_len = B64LEN(out_str_len);
1198 qp_get_q_encoding_len(out_str);
1200 Xalloca(enc_str, out_enc_str_len + 1, );
1202 base64_encode(enc_str, out_str, out_str_len);
1204 qp_q_encode(enc_str, out_str);
1208 /* output MIME-encoded string block */
1209 mime_block_len = mimestr_len + strlen(enc_str);
1210 g_snprintf(destp, mime_block_len + 1,
1211 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1212 out_encoding, mimesep_enc, enc_str);
1213 destp += mime_block_len;
1216 left -= mime_block_len;
1219 LBREAK_IF_REQUIRED(cont);
1229 #undef LBREAK_IF_REQUIRED