2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 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 issjiskanji1(c) \
66 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
67 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc))
68 #define issjiskanji2(c) \
69 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
70 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
71 #define issjishwkana(c) \
72 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
75 if (state != JIS_KANJI) { \
83 if (state != JIS_ASCII) { \
91 if (state != JIS_HWKANA) { \
99 if (state != JIS_AUXKANJI) { \
104 state = JIS_AUXKANJI; \
107 void conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
109 const guchar *in = inbuf;
110 guchar *out = outbuf;
111 JISState state = JIS_ASCII;
113 while (*in != '\0') {
117 if (*(in + 1) == '@' || *(in + 1) == 'B') {
120 } else if (*(in + 1) == '(' &&
122 state = JIS_AUXKANJI;
125 /* unknown escape sequence */
128 } else if (*in == '(') {
129 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
132 } else if (*(in + 1) == 'I') {
136 /* unknown escape sequence */
140 /* unknown escape sequence */
143 } else if (*in == 0x0e) {
146 } else if (*in == 0x0f) {
155 *out++ = *in++ | 0x80;
156 if (*in == '\0') break;
157 *out++ = *in++ | 0x80;
161 *out++ = *in++ | 0x80;
165 *out++ = *in++ | 0x80;
166 if (*in == '\0') break;
167 *out++ = *in++ | 0x80;
176 #define JIS_HWDAKUTEN 0x5e
177 #define JIS_HWHANDAKUTEN 0x5f
179 static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym)
181 static guint16 h2z_tbl[] = {
183 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521,
184 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543,
186 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d,
187 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d,
189 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c,
190 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e,
192 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569,
193 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c
196 static guint16 dakuten_tbl[] = {
198 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e,
199 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e,
201 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000,
202 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000
205 static guint16 handakuten_tbl[] = {
207 0x2551, 0x2554, 0x2557, 0x255a, 0x255d
215 if (jis_code < 0x21 || jis_code > 0x5f)
218 if (sound_sym == JIS_HWDAKUTEN &&
219 jis_code >= 0x36 && jis_code <= 0x4e) {
220 out_code = dakuten_tbl[jis_code - 0x30];
222 *outbuf = out_code >> 8;
223 *(outbuf + 1) = out_code & 0xff;
228 if (sound_sym == JIS_HWHANDAKUTEN &&
229 jis_code >= 0x4a && jis_code <= 0x4e) {
230 out_code = handakuten_tbl[jis_code - 0x4a];
231 *outbuf = out_code >> 8;
232 *(outbuf + 1) = out_code & 0xff;
236 out_code = h2z_tbl[jis_code - 0x20];
237 *outbuf = out_code >> 8;
238 *(outbuf + 1) = out_code & 0xff;
242 void conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
244 const guchar *in = inbuf;
245 guchar *out = outbuf;
246 JISState state = JIS_ASCII;
248 while (*in != '\0') {
252 } else if (iseuckanji(*in)) {
253 if (iseuckanji(*(in + 1))) {
255 *out++ = *in++ & 0x7f;
256 *out++ = *in++ & 0x7f;
261 if (*in != '\0' && !isascii(*in)) {
266 } else if (iseuchwkana1(*in)) {
267 if (iseuchwkana2(*(in + 1))) {
268 if (prefs_common.allow_jisx0201_kana) {
271 *out++ = *in++ & 0x7f;
276 if (iseuchwkana1(*(in + 2)) &&
277 iseuchwkana2(*(in + 3)))
278 len = conv_jis_hantozen
280 *(in + 1), *(in + 3));
282 len = conv_jis_hantozen
297 if (*in != '\0' && !isascii(*in)) {
302 } else if (iseucaux(*in)) {
304 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
306 *out++ = *in++ & 0x7f;
307 *out++ = *in++ & 0x7f;
310 if (*in != '\0' && !isascii(*in)) {
313 if (*in != '\0' && !isascii(*in)) {
330 void conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
332 const guchar *in = inbuf;
333 guchar *out = outbuf;
335 while (*in != '\0') {
338 } else if (issjiskanji1(*in)) {
339 if (issjiskanji2(*(in + 1))) {
341 guchar out2 = *(in + 1);
344 row = out1 < 0xa0 ? 0x70 : 0xb0;
346 out1 = (out1 - row) * 2 - 1;
347 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
349 out1 = (out1 - row) * 2;
353 *out++ = out1 | 0x80;
354 *out++ = out2 | 0x80;
359 if (*in != '\0' && !isascii(*in)) {
364 } else if (issjishwkana(*in)) {
376 void conv_anytoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
378 switch (conv_guess_ja_encoding(inbuf)) {
380 conv_jistoeuc(outbuf, outlen, inbuf);
383 conv_sjistoeuc(outbuf, outlen, inbuf);
386 strncpy2(outbuf, inbuf, outlen);
391 void conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
393 gchar *tmpstr = NULL;
395 switch (conv_guess_ja_encoding(inbuf)) {
397 tmpstr = conv_codeset_strdup(inbuf, CS_ISO_2022_JP, CS_UTF_8);
398 strncpy2(outbuf, tmpstr, outlen);
402 tmpstr = conv_codeset_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8);
403 strncpy2(outbuf, tmpstr, outlen);
407 tmpstr = conv_codeset_strdup(inbuf, CS_EUC_JP, CS_UTF_8);
408 strncpy2(outbuf, tmpstr, outlen);
412 strncpy2(outbuf, inbuf, outlen);
417 void conv_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
419 switch (conv_guess_ja_encoding(inbuf)) {
421 conv_euctojis(outbuf, outlen, inbuf);
424 strncpy2(outbuf, inbuf, outlen);
429 static gchar valid_eucjp_tbl[][96] = {
430 /* 0xa2a0 - 0xa2ff */
431 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
432 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
433 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
434 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
435 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
436 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
438 /* 0xa3a0 - 0xa3ff */
439 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
440 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
441 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
442 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
443 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
444 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
446 /* 0xa4a0 - 0xa4ff */
447 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
448 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
449 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
450 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
451 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
452 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
454 /* 0xa5a0 - 0xa5ff */
455 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
456 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
457 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
458 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
459 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
460 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
462 /* 0xa6a0 - 0xa6ff */
463 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
464 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
465 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
466 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
467 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
468 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
470 /* 0xa7a0 - 0xa7ff */
471 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
472 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
473 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
474 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
475 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
476 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
478 /* 0xa8a0 - 0xa8ff */
479 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
480 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
481 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
482 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
483 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
484 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
487 static gboolean isprintableeuckanji(guchar c1, guchar c2)
489 if (c1 <= 0xa0 || c1 >= 0xf5)
491 if (c2 <= 0xa0 || c2 == 0xff)
494 if (c1 >= 0xa9 && c1 <= 0xaf)
497 if (c1 >= 0xa2 && c1 <= 0xa8)
498 return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0];
501 if (c2 >= 0xd4 && c2 <= 0xfe)
503 } else if (c1 == 0xf4) {
504 if (c2 >= 0xa7 && c2 <= 0xfe)
511 void conv_unreadable_eucjp(gchar *str)
513 register guchar *p = str;
517 /* convert CR+LF -> LF */
518 if (*p == '\r' && *(p + 1) == '\n')
519 memmove(p, p + 1, strlen(p));
520 /* printable 7 bit code */
522 } else if (iseuckanji(*p)) {
523 if (isprintableeuckanji(*p, *(p + 1))) {
524 /* printable euc-jp code */
527 /* substitute unprintable code */
536 } else if (iseuchwkana1(*p)) {
537 if (iseuchwkana2(*(p + 1)))
538 /* euc-jp hankaku kana */
542 } else if (iseucaux(*p)) {
543 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
544 /* auxiliary kanji */
549 /* substitute unprintable 1 byte code */
554 void conv_unreadable_8bit(gchar *str)
556 register guchar *p = str;
559 /* convert CR+LF -> LF */
560 if (*p == '\r' && *(p + 1) == '\n')
561 memmove(p, p + 1, strlen(p));
562 else if (!isascii(*p)) *p = SUBST_CHAR;
567 void conv_unreadable_latin(gchar *str)
569 register guchar *p = str;
572 /* convert CR+LF -> LF */
573 if (*p == '\r' && *(p + 1) == '\n')
574 memmove(p, p + 1, strlen(p));
575 else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f)
581 void conv_unreadable_locale(gchar *str)
583 switch (conv_get_current_charset()) {
599 conv_unreadable_latin(str);
602 conv_unreadable_eucjp(str);
611 void conv_mb_alnum(gchar *str)
613 static guchar char_tbl[] = {
615 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
616 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
618 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
619 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
621 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
622 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
624 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
625 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
627 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
628 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
631 register guchar *p = str;
638 register guchar ch = *(p + 1);
640 if (ch >= 0xb0 && ch <= 0xfa) {
645 memmove(p, p + 1, len);
651 } else if (*p == 0xa1) {
652 register guchar ch = *(p + 1);
654 if (ch >= 0xa0 && ch <= 0xef &&
655 NCV != char_tbl[ch - 0xa0]) {
656 *p = char_tbl[ch - 0xa0];
659 memmove(p, p + 1, len);
665 } else if (iseuckanji(*p)) {
675 CharSet conv_guess_ja_encoding(const gchar *str)
677 const guchar *p = str;
678 CharSet guessed = C_US_ASCII;
681 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
682 if (guessed == C_US_ASCII)
683 return C_ISO_2022_JP;
685 } else if (isascii(*p)) {
687 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
688 if (*p >= 0xfd && *p <= 0xfe)
690 else if (guessed == C_SHIFT_JIS) {
691 if ((issjiskanji1(*p) &&
692 issjiskanji2(*(p + 1))) ||
694 guessed = C_SHIFT_JIS;
700 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
701 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
702 guessed = C_SHIFT_JIS;
706 } else if (issjishwkana(*p)) {
707 guessed = C_SHIFT_JIS;
717 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
719 conv_jistoeuc(outbuf, outlen, inbuf);
720 conv_unreadable_eucjp(outbuf);
723 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
725 conv_sjistoeuc(outbuf, outlen, inbuf);
726 conv_unreadable_eucjp(outbuf);
729 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
731 strncpy2(outbuf, inbuf, outlen);
732 conv_unreadable_eucjp(outbuf);
737 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
739 conv_anytoeuc(outbuf, outlen, inbuf);
740 conv_unreadable_eucjp(outbuf);
743 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
745 conv_anytoutf8(outbuf, outlen, inbuf);
749 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
751 strncpy2(outbuf, inbuf, outlen);
752 conv_unreadable_8bit(outbuf);
755 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
757 strncpy2(outbuf, inbuf, outlen);
758 conv_unreadable_latin(outbuf);
761 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
763 strncpy2(outbuf, inbuf, outlen);
764 conv_unreadable_locale(outbuf);
767 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
769 strncpy2(outbuf, inbuf, outlen);
772 CodeConverter *conv_code_converter_new(const gchar *charset)
776 conv = g_new0(CodeConverter, 1);
778 conv->code_conv_func = conv_get_code_conv_func(charset, CS_UTF_8);
779 conv->charset_str = g_strdup(charset);
780 conv->charset = conv_get_charset_from_str(charset);
785 void conv_code_converter_destroy(CodeConverter *conv)
787 g_free(conv->charset_str);
791 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
795 if (conv->code_conv_func != conv_noconv)
796 conv->code_conv_func(outbuf, outlen, inbuf);
801 str = conv_iconv_strdup(inbuf, conv->charset_str, CS_UTF_8);
805 strncpy2(outbuf, str, outlen);
809 #else /* !HAVE_ICONV */
810 conv->code_conv_func(outbuf, outlen, inbuf);
816 gchar *conv_codeset_strdup(const gchar *inbuf,
817 const gchar *src_code, const gchar *dest_code)
821 CodeConvFunc conv_func;
823 conv_func = conv_get_code_conv_func(src_code, dest_code);
824 if (conv_func != conv_noconv) {
825 len = (strlen(inbuf) + 1) * 3;
827 if (!buf) return NULL;
829 conv_func(buf, len, inbuf);
830 return g_realloc(buf, strlen(buf) + 1);
834 return conv_iconv_strdup(inbuf, src_code, dest_code);
836 return g_strdup(inbuf);
837 #endif /* HAVE_ICONV */
840 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
841 const gchar *dest_charset_str)
843 CodeConvFunc code_conv = conv_noconv;
845 CharSet dest_charset;
847 if (!src_charset_str)
848 src_charset = conv_get_current_charset();
850 src_charset = conv_get_charset_from_str(src_charset_str);
852 /* auto detection mode */
853 if (!src_charset_str && !dest_charset_str) {
854 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
855 return conv_anytodisp;
860 dest_charset = conv_get_charset_from_str(dest_charset_str);
862 if (dest_charset == C_US_ASCII)
863 return conv_ustodisp;
864 else if (dest_charset == C_UTF_8 ||
865 (dest_charset == C_AUTO &&
866 conv_get_current_charset() == C_UTF_8))
869 switch (src_charset) {
871 case C_ISO_2022_JP_2:
872 if (dest_charset == C_AUTO &&
873 conv_get_current_charset() == C_EUC_JP)
874 code_conv = conv_jistodisp;
875 else if (dest_charset == C_EUC_JP)
876 code_conv = conv_jistoeuc;
879 if (dest_charset == C_AUTO)
880 code_conv = conv_ustodisp;
896 if (dest_charset == C_AUTO &&
897 (conv_get_current_charset() == src_charset ||
899 code_conv = conv_latintodisp;
902 if (dest_charset == C_AUTO &&
903 conv_get_current_charset() == C_EUC_JP)
904 code_conv = conv_sjistodisp;
905 else if (dest_charset == C_EUC_JP)
906 code_conv = conv_sjistoeuc;
909 if (dest_charset == C_AUTO &&
910 conv_get_current_charset() == C_EUC_JP)
911 code_conv = conv_euctodisp;
912 else if (dest_charset == C_ISO_2022_JP ||
913 dest_charset == C_ISO_2022_JP_2)
914 code_conv = conv_euctojis;
924 gchar *conv_iconv_strdup(const gchar *inbuf,
925 const gchar *src_code, const gchar *dest_code)
928 const gchar *inbuf_p;
939 src_code = conv_get_outgoing_charset_str();
941 dest_code = conv_get_current_charset_str();
943 /* don't convert if current codeset is US-ASCII */
944 if (!strcasecmp(dest_code, CS_US_ASCII))
945 return g_strdup(inbuf);
947 /* don't convert if src and dest codeset are identical */
948 if (!strcasecmp(src_code, dest_code))
949 return g_strdup(inbuf);
951 cd = iconv_open(dest_code, src_code);
952 if (cd == (iconv_t)-1)
956 in_size = strlen(inbuf);
958 out_size = (in_size + 1) * 2;
959 outbuf = g_malloc(out_size);
963 #define EXPAND_BUF() \
965 len = outbuf_p - outbuf; \
967 outbuf = g_realloc(outbuf, out_size); \
968 outbuf_p = outbuf + len; \
969 out_left = out_size - len; \
972 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
973 &outbuf_p, &out_left)) == (size_t)-1) {
974 if (EILSEQ == errno) {
980 *outbuf_p++ = SUBST_CHAR;
982 } else if (EINVAL == errno) {
984 } else if (E2BIG == errno) {
987 g_warning("conv_iconv_strdup(): %s\n",
993 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
995 if (E2BIG == errno) {
998 g_warning("conv_iconv_strdup(): %s\n",
1006 len = outbuf_p - outbuf;
1007 outbuf = g_realloc(outbuf, len + 1);
1014 #endif /* HAVE_ICONV */
1016 static const struct {
1020 {C_US_ASCII, CS_US_ASCII},
1021 {C_US_ASCII, CS_ANSI_X3_4_1968},
1022 {C_UTF_8, CS_UTF_8},
1023 {C_UTF_7, CS_UTF_7},
1024 {C_ISO_8859_1, CS_ISO_8859_1},
1025 {C_ISO_8859_2, CS_ISO_8859_2},
1026 {C_ISO_8859_3, CS_ISO_8859_3},
1027 {C_ISO_8859_4, CS_ISO_8859_4},
1028 {C_ISO_8859_5, CS_ISO_8859_5},
1029 {C_ISO_8859_6, CS_ISO_8859_6},
1030 {C_ISO_8859_7, CS_ISO_8859_7},
1031 {C_ISO_8859_8, CS_ISO_8859_8},
1032 {C_ISO_8859_9, CS_ISO_8859_9},
1033 {C_ISO_8859_10, CS_ISO_8859_10},
1034 {C_ISO_8859_11, CS_ISO_8859_11},
1035 {C_ISO_8859_13, CS_ISO_8859_13},
1036 {C_ISO_8859_14, CS_ISO_8859_14},
1037 {C_ISO_8859_15, CS_ISO_8859_15},
1038 {C_BALTIC, CS_BALTIC},
1039 {C_CP1250, CS_CP1250},
1040 {C_CP1251, CS_CP1251},
1041 {C_CP1252, CS_CP1252},
1042 {C_CP1253, CS_CP1253},
1043 {C_CP1254, CS_CP1254},
1044 {C_CP1255, CS_CP1255},
1045 {C_CP1256, CS_CP1256},
1046 {C_CP1257, CS_CP1257},
1047 {C_CP1258, CS_CP1258},
1048 {C_WINDOWS_1250, CS_WINDOWS_1250},
1049 {C_WINDOWS_1251, CS_WINDOWS_1251},
1050 {C_WINDOWS_1252, CS_WINDOWS_1252},
1051 {C_WINDOWS_1253, CS_WINDOWS_1253},
1052 {C_WINDOWS_1254, CS_WINDOWS_1254},
1053 {C_WINDOWS_1255, CS_WINDOWS_1255},
1054 {C_WINDOWS_1256, CS_WINDOWS_1256},
1055 {C_WINDOWS_1257, CS_WINDOWS_1257},
1056 {C_WINDOWS_1258, CS_WINDOWS_1258},
1057 {C_KOI8_R, CS_KOI8_R},
1058 {C_KOI8_T, CS_KOI8_T},
1059 {C_KOI8_U, CS_KOI8_U},
1060 {C_ISO_2022_JP, CS_ISO_2022_JP},
1061 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1062 {C_EUC_JP, CS_EUC_JP},
1063 {C_EUC_JP, CS_EUCJP},
1064 {C_SHIFT_JIS, CS_SHIFT_JIS},
1065 {C_SHIFT_JIS, CS_SHIFT__JIS},
1066 {C_SHIFT_JIS, CS_SJIS},
1067 {C_ISO_2022_KR, CS_ISO_2022_KR},
1068 {C_EUC_KR, CS_EUC_KR},
1069 {C_ISO_2022_CN, CS_ISO_2022_CN},
1070 {C_EUC_CN, CS_EUC_CN},
1071 {C_GB2312, CS_GB2312},
1073 {C_EUC_TW, CS_EUC_TW},
1075 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1076 {C_TIS_620, CS_TIS_620},
1077 {C_WINDOWS_874, CS_WINDOWS_874},
1078 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1079 {C_TCVN5712_1, CS_TCVN5712_1},
1082 static const struct {
1083 gchar *const locale;
1085 CharSet out_charset;
1086 } locale_table[] = {
1087 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1088 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1089 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1090 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1091 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1092 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1093 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1094 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1095 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1096 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1097 {"zh_CN.GBK" , C_GBK , C_GB2312},
1098 {"zh_CN" , C_GB2312 , C_GB2312},
1099 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1100 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1101 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1102 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1103 {"zh_TW" , C_BIG5 , C_BIG5},
1105 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1106 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1107 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1108 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1109 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1110 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1111 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1113 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1114 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1116 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1118 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1119 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1120 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1121 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1122 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1123 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1124 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1125 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1126 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1127 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1128 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1129 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1130 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1131 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1132 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1133 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1134 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1135 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1136 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1137 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1138 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1139 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1140 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1141 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1142 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1143 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1144 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1145 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1146 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1147 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1148 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1149 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1150 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1151 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1152 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1153 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1154 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1155 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1156 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1157 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1158 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1159 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1160 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1161 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1162 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1163 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1164 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1165 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1166 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1167 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1168 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1169 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1170 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1171 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1172 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1173 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1174 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1175 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1176 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1177 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1178 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1179 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1180 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1181 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1182 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1183 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1184 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1185 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1186 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1187 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1188 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1189 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1190 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1191 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1193 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1194 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1195 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1196 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1197 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1198 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1199 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1200 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1202 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1203 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1205 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1207 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1208 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1209 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1210 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1212 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1214 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1215 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1216 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1217 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1218 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1219 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1220 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1221 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1222 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1223 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1224 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1225 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1226 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1227 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1228 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1229 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1230 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1232 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1233 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1234 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1235 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1237 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1238 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1240 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1242 {"ar_IN" , C_UTF_8 , C_UTF_8},
1243 {"en_IN" , C_UTF_8 , C_UTF_8},
1244 {"se_NO" , C_UTF_8 , C_UTF_8},
1245 {"ta_IN" , C_UTF_8 , C_UTF_8},
1246 {"te_IN" , C_UTF_8 , C_UTF_8},
1247 {"ur_PK" , C_UTF_8 , C_UTF_8},
1249 {"th_TH" , C_TIS_620 , C_TIS_620},
1250 /* {"th_TH" , C_WINDOWS_874}, */
1251 /* {"th_TH" , C_ISO_8859_11}, */
1253 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1254 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1256 {"C" , C_US_ASCII , C_US_ASCII},
1257 {"POSIX" , C_US_ASCII , C_US_ASCII},
1258 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1261 static GHashTable *conv_get_charset_to_str_table(void)
1263 static GHashTable *table;
1269 table = g_hash_table_new(NULL, g_direct_equal);
1271 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1272 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1275 (table, GUINT_TO_POINTER(charsets[i].charset),
1283 static GHashTable *conv_get_charset_from_str_table(void)
1285 static GHashTable *table;
1291 table = g_hash_table_new(str_case_hash, str_case_equal);
1293 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1294 g_hash_table_insert(table, charsets[i].name,
1295 GUINT_TO_POINTER(charsets[i].charset));
1301 const gchar *conv_get_charset_str(CharSet charset)
1305 table = conv_get_charset_to_str_table();
1306 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1309 CharSet conv_get_charset_from_str(const gchar *charset)
1313 if (!charset) return C_AUTO;
1315 table = conv_get_charset_from_str_table();
1316 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1319 CharSet conv_get_current_charset(void)
1321 static CharSet cur_charset = -1;
1322 const gchar *cur_locale;
1326 if (cur_charset != -1)
1329 cur_locale = conv_get_current_locale();
1331 cur_charset = C_US_ASCII;
1335 if (strcasestr(cur_locale, "UTF-8")) {
1336 cur_charset = C_UTF_8;
1340 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1341 cur_charset = C_ISO_8859_15;
1345 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1348 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1349 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1350 if (!strncasecmp(cur_locale, locale_table[i].locale,
1351 strlen(locale_table[i].locale))) {
1352 cur_charset = locale_table[i].charset;
1354 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1355 !strchr(p + 1, '.')) {
1356 if (strlen(cur_locale) == 2 &&
1357 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1358 cur_charset = locale_table[i].charset;
1364 cur_charset = C_AUTO;
1368 const gchar *conv_get_current_charset_str(void)
1370 static const gchar *codeset = NULL;
1373 codeset = conv_get_charset_str(conv_get_current_charset());
1375 return codeset ? codeset : CS_US_ASCII;
1378 CharSet conv_get_outgoing_charset(void)
1380 static CharSet out_charset = -1;
1381 const gchar *cur_locale;
1385 if (out_charset != -1)
1388 cur_locale = conv_get_current_locale();
1390 out_charset = C_AUTO;
1394 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1395 out_charset = C_ISO_8859_15;
1399 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1402 if (!strncasecmp(cur_locale, locale_table[i].locale,
1403 strlen(locale_table[i].locale))) {
1404 out_charset = locale_table[i].out_charset;
1406 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1407 !strchr(p + 1, '.')) {
1408 if (strlen(cur_locale) == 2 &&
1409 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1410 out_charset = locale_table[i].out_charset;
1417 /* encoding conversion without iconv() is only supported
1418 on Japanese locale for now */
1419 if (out_charset == C_ISO_2022_JP)
1422 return conv_get_current_charset();
1428 const gchar *conv_get_outgoing_charset_str(void)
1430 CharSet out_charset;
1433 if (prefs_common.outgoing_charset) {
1434 if (!isalpha((guchar)prefs_common.outgoing_charset[0])) {
1435 g_free(prefs_common.outgoing_charset);
1436 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
1437 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
1438 return prefs_common.outgoing_charset;
1441 out_charset = conv_get_outgoing_charset();
1442 str = conv_get_charset_str(out_charset);
1444 return str ? str : CS_US_ASCII;
1447 gboolean conv_is_multibyte_encoding(CharSet encoding)
1455 case C_ISO_2022_JP_2:
1469 const gchar *conv_get_current_locale(void)
1471 const gchar *cur_locale;
1473 cur_locale = g_getenv("LC_ALL");
1474 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1475 if (!cur_locale) cur_locale = g_getenv("LANG");
1476 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1478 debug_print("current locale: %s\n",
1479 cur_locale ? cur_locale : "(none)");
1484 void conv_unmime_header_overwrite(gchar *str)
1488 CharSet cur_charset;
1489 const gchar *locale;
1491 cur_charset = conv_get_current_charset();
1494 /* Should we always ensure to convert? */
1495 locale = conv_get_current_locale();
1497 if (locale && !strncasecmp(locale, "ja", 2)) {
1498 buflen = strlen(str) * 2 + 1;
1499 Xalloca(buf, buflen, return);
1500 conv_anytodisp(buf, buflen, str);
1501 unmime_header(str, buf);
1503 buflen = strlen(str) + 1;
1504 Xalloca(buf, buflen, return);
1505 unmime_header(buf, str);
1506 strncpy2(str, buf, buflen);
1510 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1511 const gchar *charset)
1513 CharSet cur_charset;
1514 const gchar *locale;
1516 cur_charset = conv_get_current_charset();
1519 /* Should we always ensure to convert? */
1520 locale = conv_get_current_locale();
1522 if (locale && !strncasecmp(locale, "ja", 2)) {
1526 buflen = strlen(str) * 2 + 1;
1527 Xalloca(buf, buflen, return);
1528 conv_anytodisp(buf, buflen, str);
1529 unmime_header(outbuf, buf);
1531 unmime_header(outbuf, str);
1534 #define MAX_LINELEN 76
1535 #define MAX_HARD_LINELEN 996
1536 #define MIMESEP_BEGIN "=?"
1537 #define MIMESEP_END "?="
1539 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1541 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1543 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1548 if ((cond) && *srcp) { \
1549 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1550 if (isspace(*(destp - 1))) \
1552 else if (is_plain_text && isspace(*srcp)) \
1557 left = MAX_LINELEN - 1; \
1563 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1564 gint header_len, gboolean addr_field)
1566 const gchar *cur_encoding;
1567 const gchar *out_encoding;
1571 const guchar *srcp = src;
1572 guchar *destp = dest;
1573 gboolean use_base64;
1575 if (MB_CUR_MAX > 1) {
1577 mimesep_enc = "?B?";
1580 mimesep_enc = "?Q?";
1583 cur_encoding = conv_get_current_charset_str();
1584 if (!strcmp(cur_encoding, CS_US_ASCII))
1585 cur_encoding = CS_ISO_8859_1;
1586 out_encoding = conv_get_outgoing_charset_str();
1587 if (!strcmp(out_encoding, CS_US_ASCII))
1588 out_encoding = CS_ISO_8859_1;
1590 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1591 strlen(mimesep_enc) + strlen(MIMESEP_END);
1593 left = MAX_LINELEN - header_len;
1596 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1598 while (isspace(*srcp)) {
1601 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1604 /* output as it is if the next word is ASCII string */
1605 if (!is_next_nonascii(srcp)) {
1608 word_len = get_next_word_len(srcp);
1609 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1610 while (word_len > 0) {
1611 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1620 /* don't include parentheses in encoded strings */
1621 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1622 LBREAK_IF_REQUIRED(left < 2, FALSE);
1633 const guchar *p = srcp;
1635 gint out_enc_str_len;
1636 gint mime_block_len;
1637 gboolean cont = FALSE;
1639 while (*p != '\0') {
1640 if (isspace(*p) && !is_next_nonascii(p + 1))
1642 /* don't include parentheses in encoded
1644 if (addr_field && (*p == '(' || *p == ')'))
1647 if (MB_CUR_MAX > 1) {
1648 mb_len = mblen(p, MB_CUR_MAX);
1650 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1656 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1657 out_str = conv_codeset_strdup
1658 (part_str, cur_encoding, out_encoding);
1660 g_warning("conv_encode_header(): code conversion failed\n");
1661 conv_unreadable_8bit(part_str);
1662 out_str = g_strdup(part_str);
1664 out_str_len = strlen(out_str);
1667 out_enc_str_len = B64LEN(out_str_len);
1670 qp_get_q_encoding_len(out_str);
1674 if (mimestr_len + out_enc_str_len <= left) {
1677 } else if (cur_len == 0) {
1678 LBREAK_IF_REQUIRED(1, FALSE);
1687 Xstrndup_a(part_str, srcp, cur_len, );
1688 out_str = conv_codeset_strdup
1689 (part_str, cur_encoding, out_encoding);
1691 g_warning("conv_encode_header(): code conversion failed\n");
1692 conv_unreadable_8bit(part_str);
1693 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);
1703 Xalloca(enc_str, out_enc_str_len + 1, );
1705 base64_encode(enc_str, out_str, out_str_len);
1707 qp_q_encode(enc_str, out_str);
1711 /* output MIME-encoded string block */
1712 mime_block_len = mimestr_len + strlen(enc_str);
1713 g_snprintf(destp, mime_block_len + 1,
1714 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1715 out_encoding, mimesep_enc, enc_str);
1716 destp += mime_block_len;
1719 left -= mime_block_len;
1722 LBREAK_IF_REQUIRED(cont, FALSE);
1732 #undef LBREAK_IF_REQUIRED