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 case C_ISO_2022_JP_3:
873 if (dest_charset == C_AUTO &&
874 conv_get_current_charset() == C_EUC_JP)
875 code_conv = conv_jistodisp;
876 else if (dest_charset == C_EUC_JP)
877 code_conv = conv_jistoeuc;
880 if (dest_charset == C_AUTO)
881 code_conv = conv_ustodisp;
897 if (dest_charset == C_AUTO &&
898 (conv_get_current_charset() == src_charset ||
900 code_conv = conv_latintodisp;
903 if (dest_charset == C_AUTO &&
904 conv_get_current_charset() == C_EUC_JP)
905 code_conv = conv_sjistodisp;
906 else if (dest_charset == C_EUC_JP)
907 code_conv = conv_sjistoeuc;
910 if (dest_charset == C_AUTO &&
911 conv_get_current_charset() == C_EUC_JP)
912 code_conv = conv_euctodisp;
913 else if (dest_charset == C_ISO_2022_JP ||
914 dest_charset == C_ISO_2022_JP_2 ||
915 dest_charset == C_ISO_2022_JP_3)
916 code_conv = conv_euctojis;
926 gchar *conv_iconv_strdup(const gchar *inbuf,
927 const gchar *src_code, const gchar *dest_code)
930 const gchar *inbuf_p;
941 src_code = conv_get_outgoing_charset_str();
943 dest_code = conv_get_current_charset_str();
945 /* don't convert if current codeset is US-ASCII */
946 if (!strcasecmp(dest_code, CS_US_ASCII))
947 return g_strdup(inbuf);
949 /* don't convert if src and dest codeset are identical */
950 if (!strcasecmp(src_code, dest_code))
951 return g_strdup(inbuf);
953 cd = iconv_open(dest_code, src_code);
954 if (cd == (iconv_t)-1)
958 in_size = strlen(inbuf);
960 out_size = (in_size + 1) * 2;
961 outbuf = g_malloc(out_size);
965 #define EXPAND_BUF() \
967 len = outbuf_p - outbuf; \
969 outbuf = g_realloc(outbuf, out_size); \
970 outbuf_p = outbuf + len; \
971 out_left = out_size - len; \
974 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
975 &outbuf_p, &out_left)) == (size_t)-1) {
976 if (EILSEQ == errno) {
982 *outbuf_p++ = SUBST_CHAR;
984 } else if (EINVAL == errno) {
986 } else if (E2BIG == errno) {
989 g_warning("conv_iconv_strdup(): %s\n",
995 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
997 if (E2BIG == errno) {
1000 g_warning("conv_iconv_strdup(): %s\n",
1008 len = outbuf_p - outbuf;
1009 outbuf = g_realloc(outbuf, len + 1);
1016 #endif /* HAVE_ICONV */
1018 static const struct {
1022 {C_US_ASCII, CS_US_ASCII},
1023 {C_US_ASCII, CS_ANSI_X3_4_1968},
1024 {C_UTF_8, CS_UTF_8},
1025 {C_UTF_7, CS_UTF_7},
1026 {C_ISO_8859_1, CS_ISO_8859_1},
1027 {C_ISO_8859_2, CS_ISO_8859_2},
1028 {C_ISO_8859_3, CS_ISO_8859_3},
1029 {C_ISO_8859_4, CS_ISO_8859_4},
1030 {C_ISO_8859_5, CS_ISO_8859_5},
1031 {C_ISO_8859_6, CS_ISO_8859_6},
1032 {C_ISO_8859_7, CS_ISO_8859_7},
1033 {C_ISO_8859_8, CS_ISO_8859_8},
1034 {C_ISO_8859_9, CS_ISO_8859_9},
1035 {C_ISO_8859_10, CS_ISO_8859_10},
1036 {C_ISO_8859_11, CS_ISO_8859_11},
1037 {C_ISO_8859_13, CS_ISO_8859_13},
1038 {C_ISO_8859_14, CS_ISO_8859_14},
1039 {C_ISO_8859_15, CS_ISO_8859_15},
1040 {C_BALTIC, CS_BALTIC},
1041 {C_CP1250, CS_CP1250},
1042 {C_CP1251, CS_CP1251},
1043 {C_CP1252, CS_CP1252},
1044 {C_CP1253, CS_CP1253},
1045 {C_CP1254, CS_CP1254},
1046 {C_CP1255, CS_CP1255},
1047 {C_CP1256, CS_CP1256},
1048 {C_CP1257, CS_CP1257},
1049 {C_CP1258, CS_CP1258},
1050 {C_WINDOWS_1250, CS_WINDOWS_1250},
1051 {C_WINDOWS_1251, CS_WINDOWS_1251},
1052 {C_WINDOWS_1252, CS_WINDOWS_1252},
1053 {C_WINDOWS_1253, CS_WINDOWS_1253},
1054 {C_WINDOWS_1254, CS_WINDOWS_1254},
1055 {C_WINDOWS_1255, CS_WINDOWS_1255},
1056 {C_WINDOWS_1256, CS_WINDOWS_1256},
1057 {C_WINDOWS_1257, CS_WINDOWS_1257},
1058 {C_WINDOWS_1258, CS_WINDOWS_1258},
1059 {C_KOI8_R, CS_KOI8_R},
1060 {C_KOI8_T, CS_KOI8_T},
1061 {C_KOI8_U, CS_KOI8_U},
1062 {C_ISO_2022_JP, CS_ISO_2022_JP},
1063 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1064 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3},
1065 {C_EUC_JP, CS_EUC_JP},
1066 {C_EUC_JP, CS_EUCJP},
1067 {C_SHIFT_JIS, CS_SHIFT_JIS},
1068 {C_SHIFT_JIS, CS_SHIFT__JIS},
1069 {C_SHIFT_JIS, CS_SJIS},
1070 {C_ISO_2022_KR, CS_ISO_2022_KR},
1071 {C_EUC_KR, CS_EUC_KR},
1072 {C_ISO_2022_CN, CS_ISO_2022_CN},
1073 {C_EUC_CN, CS_EUC_CN},
1074 {C_GB2312, CS_GB2312},
1076 {C_EUC_TW, CS_EUC_TW},
1078 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1079 {C_TIS_620, CS_TIS_620},
1080 {C_WINDOWS_874, CS_WINDOWS_874},
1081 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1082 {C_TCVN5712_1, CS_TCVN5712_1},
1085 static const struct {
1086 gchar *const locale;
1088 CharSet out_charset;
1089 } locale_table[] = {
1090 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1091 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1092 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1093 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1094 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1095 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1096 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1097 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1098 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1099 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1100 {"zh_CN.GBK" , C_GBK , C_GB2312},
1101 {"zh_CN" , C_GB2312 , C_GB2312},
1102 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1103 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1104 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1105 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1106 {"zh_TW" , C_BIG5 , C_BIG5},
1108 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1109 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1110 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1111 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1112 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1113 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1114 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U},
1115 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1117 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1118 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1120 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1122 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1123 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1124 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1125 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1126 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1127 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1128 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1129 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1130 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1131 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1132 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1133 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1134 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1135 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1136 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1137 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1138 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1139 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1140 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1141 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1142 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1143 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1144 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1145 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1146 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1147 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1148 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1149 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1150 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1151 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1152 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1153 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1154 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1155 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1156 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1157 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1158 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1159 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1160 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1161 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1162 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1163 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1164 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1165 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1166 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1167 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1168 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1169 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1170 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1171 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1172 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1173 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1174 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1175 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1176 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1177 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1178 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1179 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1180 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1181 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1182 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1183 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1184 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1185 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1186 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1187 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1188 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1189 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1190 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1191 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1192 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1193 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1194 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1195 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1197 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1198 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1199 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1200 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1201 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1202 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1203 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1204 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1206 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1207 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1209 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1211 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1212 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1213 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1214 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1216 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1218 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1219 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1220 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1221 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1222 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1223 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1224 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1225 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1226 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1227 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1228 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1229 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1230 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1231 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1232 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1233 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1234 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1236 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1237 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1238 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1239 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1241 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1242 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1244 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1246 {"ar_IN" , C_UTF_8 , C_UTF_8},
1247 {"en_IN" , C_UTF_8 , C_UTF_8},
1248 {"se_NO" , C_UTF_8 , C_UTF_8},
1249 {"ta_IN" , C_UTF_8 , C_UTF_8},
1250 {"te_IN" , C_UTF_8 , C_UTF_8},
1251 {"ur_PK" , C_UTF_8 , C_UTF_8},
1253 {"th_TH" , C_TIS_620 , C_TIS_620},
1254 /* {"th_TH" , C_WINDOWS_874}, */
1255 /* {"th_TH" , C_ISO_8859_11}, */
1257 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1258 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1260 {"C" , C_US_ASCII , C_US_ASCII},
1261 {"POSIX" , C_US_ASCII , C_US_ASCII},
1262 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1265 static GHashTable *conv_get_charset_to_str_table(void)
1267 static GHashTable *table;
1273 table = g_hash_table_new(NULL, g_direct_equal);
1275 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1276 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1279 (table, GUINT_TO_POINTER(charsets[i].charset),
1287 static GHashTable *conv_get_charset_from_str_table(void)
1289 static GHashTable *table;
1295 table = g_hash_table_new(str_case_hash, str_case_equal);
1297 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1298 g_hash_table_insert(table, charsets[i].name,
1299 GUINT_TO_POINTER(charsets[i].charset));
1305 const gchar *conv_get_charset_str(CharSet charset)
1309 table = conv_get_charset_to_str_table();
1310 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1313 CharSet conv_get_charset_from_str(const gchar *charset)
1317 if (!charset) return C_AUTO;
1319 table = conv_get_charset_from_str_table();
1320 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1323 CharSet conv_get_current_charset(void)
1325 static CharSet cur_charset = -1;
1326 const gchar *cur_locale;
1330 if (cur_charset != -1)
1333 cur_locale = conv_get_current_locale();
1335 cur_charset = C_US_ASCII;
1339 if (strcasestr(cur_locale, "UTF-8")) {
1340 cur_charset = C_UTF_8;
1344 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1345 cur_charset = C_ISO_8859_15;
1349 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1352 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1353 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1354 if (!strncasecmp(cur_locale, locale_table[i].locale,
1355 strlen(locale_table[i].locale))) {
1356 cur_charset = locale_table[i].charset;
1358 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1359 !strchr(p + 1, '.')) {
1360 if (strlen(cur_locale) == 2 &&
1361 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1362 cur_charset = locale_table[i].charset;
1368 cur_charset = C_AUTO;
1372 const gchar *conv_get_current_charset_str(void)
1374 static const gchar *codeset = NULL;
1377 codeset = conv_get_charset_str(conv_get_current_charset());
1379 return codeset ? codeset : CS_US_ASCII;
1382 CharSet conv_get_outgoing_charset(void)
1384 static CharSet out_charset = -1;
1385 const gchar *cur_locale;
1389 if (out_charset != -1)
1392 cur_locale = conv_get_current_locale();
1394 out_charset = C_AUTO;
1398 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1399 out_charset = C_ISO_8859_15;
1403 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1406 if (!strncasecmp(cur_locale, locale_table[i].locale,
1407 strlen(locale_table[i].locale))) {
1408 out_charset = locale_table[i].out_charset;
1410 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1411 !strchr(p + 1, '.')) {
1412 if (strlen(cur_locale) == 2 &&
1413 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1414 out_charset = locale_table[i].out_charset;
1421 /* encoding conversion without iconv() is only supported
1422 on Japanese locale for now */
1423 if (out_charset == C_ISO_2022_JP)
1426 return conv_get_current_charset();
1432 const gchar *conv_get_outgoing_charset_str(void)
1434 CharSet out_charset;
1437 if (prefs_common.outgoing_charset) {
1438 if (!isalpha((guchar)prefs_common.outgoing_charset[0])) {
1439 g_free(prefs_common.outgoing_charset);
1440 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
1441 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
1442 return prefs_common.outgoing_charset;
1445 out_charset = conv_get_outgoing_charset();
1446 str = conv_get_charset_str(out_charset);
1448 return str ? str : CS_US_ASCII;
1451 gboolean conv_is_multibyte_encoding(CharSet encoding)
1459 case C_ISO_2022_JP_2:
1460 case C_ISO_2022_JP_3:
1474 const gchar *conv_get_current_locale(void)
1476 const gchar *cur_locale;
1478 cur_locale = g_getenv("LC_ALL");
1479 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1480 if (!cur_locale) cur_locale = g_getenv("LANG");
1481 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1483 debug_print("current locale: %s\n",
1484 cur_locale ? cur_locale : "(none)");
1489 void conv_unmime_header_overwrite(gchar *str)
1493 CharSet cur_charset;
1494 const gchar *locale;
1496 g_return_if_fail(str != NULL);
1498 cur_charset = conv_get_current_charset();
1501 /* Should we always ensure to convert? */
1502 locale = conv_get_current_locale();
1504 if (locale && !strncasecmp(locale, "ja", 2)) {
1505 buflen = strlen(str) * 2 + 1;
1506 Xalloca(buf, buflen, return);
1507 conv_anytodisp(buf, buflen, str);
1508 unmime_header(str, buf);
1510 buflen = strlen(str) + 1;
1511 Xalloca(buf, buflen, return);
1512 unmime_header(buf, str);
1513 strncpy2(str, buf, buflen);
1517 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1518 const gchar *charset)
1520 CharSet cur_charset;
1521 const gchar *locale;
1523 cur_charset = conv_get_current_charset();
1526 /* Should we always ensure to convert? */
1527 locale = conv_get_current_locale();
1529 if (locale && !strncasecmp(locale, "ja", 2)) {
1533 buflen = strlen(str) * 2 + 1;
1534 Xalloca(buf, buflen, return);
1535 conv_anytodisp(buf, buflen, str);
1536 unmime_header(outbuf, buf);
1538 unmime_header(outbuf, str);
1541 #define MAX_LINELEN 76
1542 #define MAX_HARD_LINELEN 996
1543 #define MIMESEP_BEGIN "=?"
1544 #define MIMESEP_END "?="
1546 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1548 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1550 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1555 if ((cond) && *srcp) { \
1556 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1557 if (isspace(*(destp - 1))) \
1559 else if (is_plain_text && isspace(*srcp)) \
1564 left = MAX_LINELEN - 1; \
1570 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1571 gint header_len, gboolean addr_field)
1573 const gchar *cur_encoding;
1574 const gchar *out_encoding;
1578 const guchar *srcp = src;
1579 guchar *destp = dest;
1580 gboolean use_base64;
1582 if (MB_CUR_MAX > 1) {
1584 mimesep_enc = "?B?";
1587 mimesep_enc = "?Q?";
1590 cur_encoding = conv_get_current_charset_str();
1591 if (!strcmp(cur_encoding, CS_US_ASCII))
1592 cur_encoding = CS_ISO_8859_1;
1593 out_encoding = conv_get_outgoing_charset_str();
1594 if (!strcmp(out_encoding, CS_US_ASCII))
1595 out_encoding = CS_ISO_8859_1;
1597 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1598 strlen(mimesep_enc) + strlen(MIMESEP_END);
1600 left = MAX_LINELEN - header_len;
1603 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1605 while (isspace(*srcp)) {
1608 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1611 /* output as it is if the next word is ASCII string */
1612 if (!is_next_nonascii(srcp)) {
1615 word_len = get_next_word_len(srcp);
1616 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1617 while (word_len > 0) {
1618 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1627 /* don't include parentheses in encoded strings */
1628 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1629 LBREAK_IF_REQUIRED(left < 2, FALSE);
1640 const guchar *p = srcp;
1642 gint out_enc_str_len;
1643 gint mime_block_len;
1644 gboolean cont = FALSE;
1646 while (*p != '\0') {
1647 if (isspace(*p) && !is_next_nonascii(p + 1))
1649 /* don't include parentheses in encoded
1651 if (addr_field && (*p == '(' || *p == ')'))
1654 if (MB_CUR_MAX > 1) {
1655 mb_len = mblen(p, MB_CUR_MAX);
1657 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1663 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1664 out_str = conv_codeset_strdup
1665 (part_str, cur_encoding, out_encoding);
1667 g_warning("conv_encode_header(): code conversion failed\n");
1668 conv_unreadable_8bit(part_str);
1669 out_str = g_strdup(part_str);
1671 out_str_len = strlen(out_str);
1674 out_enc_str_len = B64LEN(out_str_len);
1677 qp_get_q_encoding_len(out_str);
1681 if (mimestr_len + out_enc_str_len <= left) {
1684 } else if (cur_len == 0) {
1685 LBREAK_IF_REQUIRED(1, FALSE);
1694 Xstrndup_a(part_str, srcp, cur_len, );
1695 out_str = conv_codeset_strdup
1696 (part_str, cur_encoding, out_encoding);
1698 g_warning("conv_encode_header(): code conversion failed\n");
1699 conv_unreadable_8bit(part_str);
1700 out_str = g_strdup(part_str);
1702 out_str_len = strlen(out_str);
1705 out_enc_str_len = B64LEN(out_str_len);
1708 qp_get_q_encoding_len(out_str);
1710 Xalloca(enc_str, out_enc_str_len + 1, );
1712 base64_encode(enc_str, out_str, out_str_len);
1714 qp_q_encode(enc_str, out_str);
1718 /* output MIME-encoded string block */
1719 mime_block_len = mimestr_len + strlen(enc_str);
1720 g_snprintf(destp, mime_block_len + 1,
1721 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1722 out_encoding, mimesep_enc, enc_str);
1723 destp += mime_block_len;
1726 left -= mime_block_len;
1729 LBREAK_IF_REQUIRED(cont, FALSE);
1739 #undef LBREAK_IF_REQUIRED