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