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.
38 #include "quoted-printable.h"
40 #include "prefs_common.h"
50 #define SUBST_CHAR '_'
53 #define iseuckanji(c) \
54 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe)
55 #define iseuchwkana1(c) \
56 (((c) & 0xff) == 0x8e)
57 #define iseuchwkana2(c) \
58 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
60 (((c) & 0xff) == 0x8f)
61 #define issjiskanji1(c) \
62 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \
63 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc))
64 #define issjiskanji2(c) \
65 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \
66 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc))
67 #define issjishwkana(c) \
68 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf)
71 if (state != JIS_KANJI) { \
79 if (state != JIS_ASCII) { \
87 if (state != JIS_HWKANA) { \
95 if (state != JIS_AUXKANJI) { \
100 state = JIS_AUXKANJI; \
103 void conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
105 const guchar *in = inbuf;
106 guchar *out = outbuf;
107 JISState state = JIS_ASCII;
109 while (*in != '\0') {
113 if (*(in + 1) == '@' || *(in + 1) == 'B') {
116 } else if (*(in + 1) == '(' &&
118 state = JIS_AUXKANJI;
121 /* unknown escape sequence */
124 } else if (*in == '(') {
125 if (*(in + 1) == 'B' || *(in + 1) == 'J') {
128 } else if (*(in + 1) == 'I') {
132 /* unknown escape sequence */
136 /* unknown escape sequence */
139 } else if (*in == 0x0e) {
142 } else if (*in == 0x0f) {
151 *out++ = *in++ | 0x80;
152 if (*in == '\0') break;
153 *out++ = *in++ | 0x80;
157 *out++ = *in++ | 0x80;
161 *out++ = *in++ | 0x80;
162 if (*in == '\0') break;
163 *out++ = *in++ | 0x80;
172 #define JIS_HWDAKUTEN 0x5e
173 #define JIS_HWHANDAKUTEN 0x5f
175 static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym)
177 static guint16 h2z_tbl[] = {
179 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521,
180 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543,
182 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d,
183 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d,
185 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c,
186 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e,
188 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569,
189 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c
192 static guint16 dakuten_tbl[] = {
194 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e,
195 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e,
197 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000,
198 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000
201 static guint16 handakuten_tbl[] = {
203 0x2551, 0x2554, 0x2557, 0x255a, 0x255d
211 if (jis_code < 0x21 || jis_code > 0x5f)
214 if (sound_sym == JIS_HWDAKUTEN &&
215 jis_code >= 0x36 && jis_code <= 0x4e) {
216 out_code = dakuten_tbl[jis_code - 0x30];
218 *outbuf = out_code >> 8;
219 *(outbuf + 1) = out_code & 0xff;
224 if (sound_sym == JIS_HWHANDAKUTEN &&
225 jis_code >= 0x4a && jis_code <= 0x4e) {
226 out_code = handakuten_tbl[jis_code - 0x4a];
227 *outbuf = out_code >> 8;
228 *(outbuf + 1) = out_code & 0xff;
232 out_code = h2z_tbl[jis_code - 0x20];
233 *outbuf = out_code >> 8;
234 *(outbuf + 1) = out_code & 0xff;
238 void conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
240 const guchar *in = inbuf;
241 guchar *out = outbuf;
242 JISState state = JIS_ASCII;
244 while (*in != '\0') {
248 } else if (iseuckanji(*in)) {
249 if (iseuckanji(*(in + 1))) {
251 *out++ = *in++ & 0x7f;
252 *out++ = *in++ & 0x7f;
257 if (*in != '\0' && !isascii(*in)) {
262 } else if (iseuchwkana1(*in)) {
263 if (iseuchwkana2(*(in + 1))) {
264 if (prefs_common.allow_jisx0201_kana) {
267 *out++ = *in++ & 0x7f;
272 if (iseuchwkana1(*(in + 2)) &&
273 iseuchwkana2(*(in + 3)))
274 len = conv_jis_hantozen
276 *(in + 1), *(in + 3));
278 len = conv_jis_hantozen
293 if (*in != '\0' && !isascii(*in)) {
298 } else if (iseucaux(*in)) {
300 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
302 *out++ = *in++ & 0x7f;
303 *out++ = *in++ & 0x7f;
306 if (*in != '\0' && !isascii(*in)) {
309 if (*in != '\0' && !isascii(*in)) {
326 void conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
328 const guchar *in = inbuf;
329 guchar *out = outbuf;
331 while (*in != '\0') {
334 } else if (issjiskanji1(*in)) {
335 if (issjiskanji2(*(in + 1))) {
337 guchar out2 = *(in + 1);
340 row = out1 < 0xa0 ? 0x70 : 0xb0;
342 out1 = (out1 - row) * 2 - 1;
343 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
345 out1 = (out1 - row) * 2;
349 *out++ = out1 | 0x80;
350 *out++ = out2 | 0x80;
355 if (*in != '\0' && !isascii(*in)) {
360 } else if (issjishwkana(*in)) {
372 void conv_anytoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
374 switch (conv_guess_ja_encoding(inbuf)) {
376 conv_jistoeuc(outbuf, outlen, inbuf);
379 conv_sjistoeuc(outbuf, outlen, inbuf);
382 strncpy2(outbuf, inbuf, outlen);
387 void conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf)
389 gchar *tmpstr = NULL;
391 switch (conv_guess_ja_encoding(inbuf)) {
393 tmpstr = conv_codeset_strdup(inbuf, CS_ISO_2022_JP, CS_UTF_8);
394 strncpy2(outbuf, tmpstr, outlen);
398 tmpstr = conv_codeset_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8);
399 strncpy2(outbuf, tmpstr, outlen);
403 tmpstr = conv_codeset_strdup(inbuf, CS_EUC_JP, CS_UTF_8);
404 strncpy2(outbuf, tmpstr, outlen);
408 strncpy2(outbuf, inbuf, outlen);
413 void conv_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
415 switch (conv_guess_ja_encoding(inbuf)) {
417 conv_euctojis(outbuf, outlen, inbuf);
420 strncpy2(outbuf, inbuf, outlen);
425 static gchar valid_eucjp_tbl[][96] = {
426 /* 0xa2a0 - 0xa2ff */
427 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
428 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
429 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
430 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
431 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
432 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
434 /* 0xa3a0 - 0xa3ff */
435 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
436 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
437 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
438 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
439 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
440 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
442 /* 0xa4a0 - 0xa4ff */
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, 1, 1, 1, 1, 1,
445 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
446 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
447 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
448 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
450 /* 0xa5a0 - 0xa5ff */
451 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
452 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
453 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
454 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
455 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
456 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
458 /* 0xa6a0 - 0xa6ff */
459 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
460 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
461 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
462 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
463 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
464 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
466 /* 0xa7a0 - 0xa7ff */
467 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
468 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
469 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
470 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
471 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
472 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
474 /* 0xa8a0 - 0xa8ff */
475 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
476 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
477 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
478 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
479 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
480 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
483 static gboolean isprintableeuckanji(guchar c1, guchar c2)
485 if (c1 <= 0xa0 || c1 >= 0xf5)
487 if (c2 <= 0xa0 || c2 == 0xff)
490 if (c1 >= 0xa9 && c1 <= 0xaf)
493 if (c1 >= 0xa2 && c1 <= 0xa8)
494 return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0];
497 if (c2 >= 0xd4 && c2 <= 0xfe)
499 } else if (c1 == 0xf4) {
500 if (c2 >= 0xa7 && c2 <= 0xfe)
507 void conv_unreadable_eucjp(gchar *str)
509 register guchar *p = str;
513 /* convert CR+LF -> LF */
514 if (*p == '\r' && *(p + 1) == '\n')
515 memmove(p, p + 1, strlen(p));
516 /* printable 7 bit code */
518 } else if (iseuckanji(*p)) {
519 if (isprintableeuckanji(*p, *(p + 1))) {
520 /* printable euc-jp code */
523 /* substitute unprintable code */
532 } else if (iseuchwkana1(*p)) {
533 if (iseuchwkana2(*(p + 1)))
534 /* euc-jp hankaku kana */
538 } else if (iseucaux(*p)) {
539 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
540 /* auxiliary kanji */
545 /* substitute unprintable 1 byte code */
550 void conv_unreadable_8bit(gchar *str)
552 register guchar *p = str;
555 /* convert CR+LF -> LF */
556 if (*p == '\r' && *(p + 1) == '\n')
557 memmove(p, p + 1, strlen(p));
558 else if (!isascii(*p)) *p = SUBST_CHAR;
563 void conv_unreadable_latin(gchar *str)
565 register guchar *p = str;
568 /* convert CR+LF -> LF */
569 if (*p == '\r' && *(p + 1) == '\n')
570 memmove(p, p + 1, strlen(p));
571 else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f)
577 void conv_unreadable_locale(gchar *str)
579 switch (conv_get_current_charset()) {
595 conv_unreadable_latin(str);
598 conv_unreadable_eucjp(str);
607 void conv_mb_alnum(gchar *str)
609 static guchar char_tbl[] = {
611 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
612 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
614 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
615 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
617 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
618 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
620 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
621 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
623 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
624 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
627 register guchar *p = str;
634 register guchar ch = *(p + 1);
636 if (ch >= 0xb0 && ch <= 0xfa) {
641 memmove(p, p + 1, len);
647 } else if (*p == 0xa1) {
648 register guchar ch = *(p + 1);
650 if (ch >= 0xa0 && ch <= 0xef &&
651 NCV != char_tbl[ch - 0xa0]) {
652 *p = char_tbl[ch - 0xa0];
655 memmove(p, p + 1, len);
661 } else if (iseuckanji(*p)) {
671 CharSet conv_guess_ja_encoding(const gchar *str)
673 const guchar *p = str;
674 CharSet guessed = C_US_ASCII;
677 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
678 if (guessed == C_US_ASCII)
679 return C_ISO_2022_JP;
681 } else if (isascii(*p)) {
683 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
684 if (*p >= 0xfd && *p <= 0xfe)
686 else if (guessed == C_SHIFT_JIS) {
687 if ((issjiskanji1(*p) &&
688 issjiskanji2(*(p + 1))) ||
690 guessed = C_SHIFT_JIS;
696 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
697 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
698 guessed = C_SHIFT_JIS;
702 } else if (issjishwkana(*p)) {
703 guessed = C_SHIFT_JIS;
713 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
715 conv_jistoeuc(outbuf, outlen, inbuf);
716 conv_unreadable_eucjp(outbuf);
719 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
721 conv_sjistoeuc(outbuf, outlen, inbuf);
722 conv_unreadable_eucjp(outbuf);
725 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
727 strncpy2(outbuf, inbuf, outlen);
728 conv_unreadable_eucjp(outbuf);
731 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
733 conv_anytoutf8(outbuf, outlen, inbuf);
737 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
739 strncpy2(outbuf, inbuf, outlen);
740 conv_unreadable_8bit(outbuf);
744 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
746 strncpy2(outbuf, inbuf, outlen);
747 conv_unreadable_latin(outbuf);
751 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
753 strncpy2(outbuf, inbuf, outlen);
754 conv_unreadable_locale(outbuf);
757 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
759 strncpy2(outbuf, inbuf, outlen);
762 CodeConverter *conv_code_converter_new(const gchar *charset)
766 conv = g_new0(CodeConverter, 1);
767 conv->code_conv_func = conv_get_code_conv_func(charset, CS_UTF_8);
768 conv->charset_str = g_strdup(charset);
769 conv->charset = conv_get_charset_from_str(charset);
774 void conv_code_converter_destroy(CodeConverter *conv)
776 g_free(conv->charset_str);
780 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
783 if (conv->code_conv_func != conv_noconv)
784 conv->code_conv_func(outbuf, outlen, inbuf);
788 str = conv_iconv_strdup(inbuf, conv->charset_str, CS_UTF_8);
792 strncpy2(outbuf, str, outlen);
800 gchar *conv_codeset_strdup(const gchar *inbuf,
801 const gchar *src_code, const gchar *dest_code)
805 CodeConvFunc conv_func;
807 conv_func = conv_get_code_conv_func(src_code, dest_code);
808 if (conv_func != conv_noconv) {
809 len = (strlen(inbuf) + 1) * 3;
811 if (!buf) return NULL;
813 conv_func(buf, len, inbuf);
814 return g_realloc(buf, strlen(buf) + 1);
817 return conv_iconv_strdup(inbuf, src_code, dest_code);
820 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
821 const gchar *dest_charset_str)
823 CodeConvFunc code_conv = conv_noconv;
825 CharSet dest_charset;
827 if (!src_charset_str)
828 src_charset = conv_get_current_charset();
830 src_charset = conv_get_charset_from_str(src_charset_str);
832 /* auto detection mode */
833 if (!src_charset_str && !dest_charset_str) {
834 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
835 return conv_anytodisp;
840 dest_charset = conv_get_charset_from_str(dest_charset_str);
842 if (dest_charset == C_US_ASCII)
843 return conv_ustodisp;
844 else if (dest_charset == C_UTF_8 ||
845 (dest_charset == C_AUTO &&
846 conv_get_current_charset() == C_UTF_8))
849 switch (src_charset) {
851 case C_ISO_2022_JP_2:
852 case C_ISO_2022_JP_3:
853 if (dest_charset == C_AUTO &&
854 conv_get_current_charset() == C_EUC_JP)
855 code_conv = conv_jistodisp;
856 else if (dest_charset == C_EUC_JP)
857 code_conv = conv_jistoeuc;
860 if (dest_charset == C_AUTO)
861 code_conv = conv_ustodisp;
877 if (dest_charset == C_AUTO &&
878 (conv_get_current_charset() == src_charset ||
880 code_conv = conv_latintodisp;
883 if (dest_charset == C_AUTO &&
884 conv_get_current_charset() == C_EUC_JP)
885 code_conv = conv_sjistodisp;
886 else if (dest_charset == C_EUC_JP)
887 code_conv = conv_sjistoeuc;
890 if (dest_charset == C_AUTO &&
891 conv_get_current_charset() == C_EUC_JP)
892 code_conv = conv_euctodisp;
893 else if (dest_charset == C_ISO_2022_JP ||
894 dest_charset == C_ISO_2022_JP_2 ||
895 dest_charset == C_ISO_2022_JP_3)
896 code_conv = conv_euctojis;
905 gchar *conv_iconv_strdup(const gchar *inbuf,
906 const gchar *isrc_code, const gchar *idest_code)
908 /* presumably GLib 2's function handles the conversion details,
909 * whether iconv is sitting below, or something else */
911 gsize read_len, written_len;
912 gchar *src_code = (char *)conv_get_outgoing_charset_str();
913 gchar *dest_code = (char *)conv_get_current_charset_str();
916 src_code = (char *)isrc_code;
918 dest_code = (char *)idest_code;
920 /* don't convert if current codeset is US-ASCII */
921 if (!strcasecmp(dest_code, CS_US_ASCII))
922 return g_strdup(inbuf);
924 /* don't convert if src and dest codeset are identical */
925 if (!strcasecmp(src_code, dest_code))
926 return g_strdup(inbuf);
928 /* FIXME: unchecked inbuf? Can't see at this level. */
929 outbuf = g_convert(inbuf, strlen(inbuf), dest_code, src_code,
930 &read_len, &written_len, NULL);
933 g_warning(_("Valid locale type set? (Currently: %s to %s)\n"),
934 src_code, dest_code);
939 static const struct {
943 {C_US_ASCII, CS_US_ASCII},
944 {C_US_ASCII, CS_ANSI_X3_4_1968},
947 {C_ISO_8859_1, CS_ISO_8859_1},
948 {C_ISO_8859_2, CS_ISO_8859_2},
949 {C_ISO_8859_3, CS_ISO_8859_3},
950 {C_ISO_8859_4, CS_ISO_8859_4},
951 {C_ISO_8859_5, CS_ISO_8859_5},
952 {C_ISO_8859_6, CS_ISO_8859_6},
953 {C_ISO_8859_7, CS_ISO_8859_7},
954 {C_ISO_8859_8, CS_ISO_8859_8},
955 {C_ISO_8859_9, CS_ISO_8859_9},
956 {C_ISO_8859_10, CS_ISO_8859_10},
957 {C_ISO_8859_11, CS_ISO_8859_11},
958 {C_ISO_8859_13, CS_ISO_8859_13},
959 {C_ISO_8859_14, CS_ISO_8859_14},
960 {C_ISO_8859_15, CS_ISO_8859_15},
961 {C_BALTIC, CS_BALTIC},
962 {C_CP1250, CS_CP1250},
963 {C_CP1251, CS_CP1251},
964 {C_CP1252, CS_CP1252},
965 {C_CP1253, CS_CP1253},
966 {C_CP1254, CS_CP1254},
967 {C_CP1255, CS_CP1255},
968 {C_CP1256, CS_CP1256},
969 {C_CP1257, CS_CP1257},
970 {C_CP1258, CS_CP1258},
971 {C_WINDOWS_1250, CS_WINDOWS_1250},
972 {C_WINDOWS_1251, CS_WINDOWS_1251},
973 {C_WINDOWS_1252, CS_WINDOWS_1252},
974 {C_WINDOWS_1253, CS_WINDOWS_1253},
975 {C_WINDOWS_1254, CS_WINDOWS_1254},
976 {C_WINDOWS_1255, CS_WINDOWS_1255},
977 {C_WINDOWS_1256, CS_WINDOWS_1256},
978 {C_WINDOWS_1257, CS_WINDOWS_1257},
979 {C_WINDOWS_1258, CS_WINDOWS_1258},
980 {C_KOI8_R, CS_KOI8_R},
981 {C_KOI8_T, CS_KOI8_T},
982 {C_KOI8_U, CS_KOI8_U},
983 {C_ISO_2022_JP, CS_ISO_2022_JP},
984 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
985 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3},
986 {C_EUC_JP, CS_EUC_JP},
987 {C_EUC_JP, CS_EUCJP},
988 {C_SHIFT_JIS, CS_SHIFT_JIS},
989 {C_SHIFT_JIS, CS_SHIFT__JIS},
990 {C_SHIFT_JIS, CS_SJIS},
991 {C_ISO_2022_KR, CS_ISO_2022_KR},
992 {C_EUC_KR, CS_EUC_KR},
993 {C_ISO_2022_CN, CS_ISO_2022_CN},
994 {C_EUC_CN, CS_EUC_CN},
995 {C_GB2312, CS_GB2312},
997 {C_EUC_TW, CS_EUC_TW},
999 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1000 {C_TIS_620, CS_TIS_620},
1001 {C_WINDOWS_874, CS_WINDOWS_874},
1002 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1003 {C_TCVN5712_1, CS_TCVN5712_1},
1006 static const struct {
1007 gchar *const locale;
1009 CharSet out_charset;
1010 } locale_table[] = {
1011 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1012 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1013 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1014 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1015 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1016 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1017 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1018 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1019 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1020 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1021 {"zh_CN.GBK" , C_GBK , C_GB2312},
1022 {"zh_CN" , C_GB2312 , C_GB2312},
1023 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1024 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1025 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1026 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1027 {"zh_TW" , C_BIG5 , C_BIG5},
1029 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1030 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1031 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1032 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1033 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1034 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1035 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U},
1036 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1038 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1039 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1041 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1043 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1044 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1045 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1046 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1047 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1048 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1049 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1050 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1051 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1052 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1053 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1054 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1055 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1056 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1057 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1058 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1059 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1060 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1061 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1062 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1063 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1064 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1065 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1066 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1067 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1068 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1069 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1070 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1071 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1072 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1073 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1074 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1075 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1076 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1077 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1078 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1079 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1080 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1081 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1082 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1083 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1084 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1085 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1086 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1087 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1088 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1089 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1090 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1091 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1092 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1093 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1094 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1095 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1096 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1097 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1098 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1099 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1100 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1101 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1102 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1103 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1104 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1105 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1106 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1107 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1108 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1109 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1110 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1111 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1112 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1113 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1114 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1115 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1116 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1118 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1119 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1120 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1121 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1122 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1123 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1124 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1125 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1127 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1128 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1130 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1132 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1133 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1134 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1135 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1137 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1139 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1140 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1141 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1142 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1143 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1144 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1145 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1146 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1147 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1148 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1149 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1150 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1151 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1152 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1153 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1154 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1155 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1157 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1158 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1159 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1160 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1162 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1163 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1165 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1167 {"ar_IN" , C_UTF_8 , C_UTF_8},
1168 {"en_IN" , C_UTF_8 , C_UTF_8},
1169 {"se_NO" , C_UTF_8 , C_UTF_8},
1170 {"ta_IN" , C_UTF_8 , C_UTF_8},
1171 {"te_IN" , C_UTF_8 , C_UTF_8},
1172 {"ur_PK" , C_UTF_8 , C_UTF_8},
1174 {"th_TH" , C_TIS_620 , C_TIS_620},
1175 /* {"th_TH" , C_WINDOWS_874}, */
1176 /* {"th_TH" , C_ISO_8859_11}, */
1178 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1179 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1181 {"C" , C_US_ASCII , C_US_ASCII},
1182 {"POSIX" , C_US_ASCII , C_US_ASCII},
1183 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1186 static GHashTable *conv_get_charset_to_str_table(void)
1188 static GHashTable *table;
1194 table = g_hash_table_new(NULL, g_direct_equal);
1196 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1197 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1200 (table, GUINT_TO_POINTER(charsets[i].charset),
1208 static GHashTable *conv_get_charset_from_str_table(void)
1210 static GHashTable *table;
1216 table = g_hash_table_new(str_case_hash, str_case_equal);
1218 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1219 g_hash_table_insert(table, charsets[i].name,
1220 GUINT_TO_POINTER(charsets[i].charset));
1226 const gchar *conv_get_charset_str(CharSet charset)
1230 table = conv_get_charset_to_str_table();
1231 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1234 CharSet conv_get_charset_from_str(const gchar *charset)
1238 if (!charset) return C_AUTO;
1240 table = conv_get_charset_from_str_table();
1241 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1244 CharSet conv_get_current_charset(void)
1246 static CharSet cur_charset = -1;
1247 const gchar *cur_locale;
1251 if (cur_charset != -1)
1254 cur_locale = conv_get_current_locale();
1256 cur_charset = C_US_ASCII;
1260 if (strcasestr(cur_locale, "UTF-8")) {
1261 cur_charset = C_UTF_8;
1265 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1266 cur_charset = C_ISO_8859_15;
1270 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1273 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1274 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1275 if (!strncasecmp(cur_locale, locale_table[i].locale,
1276 strlen(locale_table[i].locale))) {
1277 cur_charset = locale_table[i].charset;
1279 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1280 !strchr(p + 1, '.')) {
1281 if (strlen(cur_locale) == 2 &&
1282 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1283 cur_charset = locale_table[i].charset;
1289 cur_charset = C_AUTO;
1293 const gchar *conv_get_current_charset_str(void)
1295 static const gchar *codeset = NULL;
1298 codeset = conv_get_charset_str(conv_get_current_charset());
1300 return codeset ? codeset : CS_US_ASCII;
1303 CharSet conv_get_outgoing_charset(void)
1305 static CharSet out_charset = -1;
1306 const gchar *cur_locale;
1310 if (out_charset != -1)
1313 cur_locale = conv_get_current_locale();
1315 out_charset = C_AUTO;
1319 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1320 out_charset = C_ISO_8859_15;
1324 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1327 if (!strncasecmp(cur_locale, locale_table[i].locale,
1328 strlen(locale_table[i].locale))) {
1329 out_charset = locale_table[i].out_charset;
1331 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1332 !strchr(p + 1, '.')) {
1333 if (strlen(cur_locale) == 2 &&
1334 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1335 out_charset = locale_table[i].out_charset;
1344 const gchar *conv_get_outgoing_charset_str(void)
1346 CharSet out_charset;
1349 if (prefs_common.outgoing_charset) {
1350 if (!isalpha((guchar)prefs_common.outgoing_charset[0])) {
1351 g_free(prefs_common.outgoing_charset);
1352 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
1353 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
1354 return prefs_common.outgoing_charset;
1357 out_charset = conv_get_outgoing_charset();
1358 str = conv_get_charset_str(out_charset);
1360 return str ? str : CS_US_ASCII;
1363 gboolean conv_is_multibyte_encoding(CharSet encoding)
1371 case C_ISO_2022_JP_2:
1372 case C_ISO_2022_JP_3:
1386 const gchar *conv_get_current_locale(void)
1388 static const gchar *cur_locale = NULL;
1390 if (cur_locale != NULL)
1393 cur_locale = g_getenv("LC_ALL");
1394 if (!cur_locale || !strlen(cur_locale))
1395 cur_locale = g_getenv("LC_CTYPE");
1396 if (!cur_locale || !strlen(cur_locale))
1397 cur_locale = g_getenv("LANG");
1398 if (!cur_locale || !strlen(cur_locale))
1399 cur_locale = setlocale(LC_CTYPE, NULL);
1401 if (cur_locale && strlen(cur_locale)) {
1402 gchar *tmp = g_strdup(cur_locale);
1403 cur_locale = g_strdup(tmp);
1410 void conv_unmime_header_overwrite(gchar *str)
1414 CharSet cur_charset;
1415 const gchar *locale;
1417 g_return_if_fail(str != NULL);
1419 cur_charset = conv_get_current_charset();
1422 /* Should we always ensure to convert? */
1423 locale = conv_get_current_locale();
1425 if (locale && !strncasecmp(locale, "ja", 2)) {
1426 buflen = strlen(str) * 2 + 1;
1427 Xalloca(buf, buflen, return);
1428 conv_anytodisp(buf, buflen, str);
1429 unmime_header(str, buf);
1431 buflen = strlen(str) + 1;
1432 Xalloca(buf, buflen, return);
1433 unmime_header(buf, str);
1434 strncpy2(str, buf, buflen);
1438 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1439 const gchar *charset)
1441 const gchar *locale;
1443 memset(outbuf, 0, outlen);
1446 /* Should we always ensure to convert? */
1447 locale = conv_get_current_locale();
1449 if (locale && !strncasecmp(locale, "ja", 2)) {
1453 buflen = strlen(str) * 2 + 1;
1454 Xalloca(buf, buflen, return);
1455 conv_anytodisp(buf, buflen, str);
1456 unmime_header(outbuf, buf);
1459 unmime_header(outbuf, str);
1460 if (outbuf && !g_utf8_validate(outbuf, -1, NULL)) {
1461 tmp = conv_codeset_strdup(outbuf,
1462 conv_get_current_charset_str(),
1465 strncpy(outbuf, tmp, outlen-1);
1472 #define MAX_LINELEN 76
1473 #define MAX_HARD_LINELEN 996
1474 #define MIMESEP_BEGIN "=?"
1475 #define MIMESEP_END "?="
1477 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1479 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1481 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1486 if ((cond) && *srcp) { \
1487 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1488 if (isspace(*(destp - 1))) \
1490 else if (is_plain_text && isspace(*srcp)) \
1495 left = MAX_LINELEN - 1; \
1501 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1502 gint header_len, gboolean addr_field)
1504 const gchar *cur_encoding;
1505 const gchar *out_encoding;
1509 const guchar *srcp = src;
1510 guchar *destp = dest;
1511 gboolean use_base64;
1514 if (MB_CUR_MAX > 1) {
1516 mimesep_enc = "?B?";
1519 mimesep_enc = "?Q?";
1522 cur_encoding = CS_UTF_8; /* gtk2 */
1524 out_encoding = conv_get_outgoing_charset_str();
1525 if (!strcmp(out_encoding, CS_US_ASCII))
1526 out_encoding = CS_ISO_8859_1;
1528 testbuf = conv_codeset_strdup(src, cur_encoding, out_encoding);
1530 if (testbuf != NULL)
1533 out_encoding = CS_UTF_8;
1535 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1536 strlen(mimesep_enc) + strlen(MIMESEP_END);
1538 left = MAX_LINELEN - header_len;
1541 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1543 while (isspace(*srcp)) {
1546 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1549 /* output as it is if the next word is ASCII string */
1550 if (!is_next_nonascii(srcp)) {
1553 word_len = get_next_word_len(srcp);
1554 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1555 while (word_len > 0) {
1556 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1565 /* don't include parentheses in encoded strings */
1566 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1567 LBREAK_IF_REQUIRED(left < 2, FALSE);
1578 const guchar *p = srcp;
1580 gint out_enc_str_len;
1581 gint mime_block_len;
1582 gboolean cont = FALSE;
1584 while (*p != '\0') {
1585 if (isspace(*p) && !is_next_nonascii(p + 1))
1587 /* don't include parentheses in encoded
1589 if (addr_field && (*p == '(' || *p == ')'))
1592 if (MB_CUR_MAX > 1) {
1593 mb_len = mblen(p, MB_CUR_MAX);
1595 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1601 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1602 out_str = conv_codeset_strdup
1603 (part_str, cur_encoding, out_encoding);
1605 g_warning("conv_encode_header(): code conversion failed\n");
1606 conv_unreadable_8bit(part_str);
1607 out_str = g_strdup(part_str);
1609 out_str_len = strlen(out_str);
1612 out_enc_str_len = B64LEN(out_str_len);
1615 qp_get_q_encoding_len(out_str);
1619 if (mimestr_len + out_enc_str_len <= left) {
1622 } else if (cur_len == 0) {
1623 LBREAK_IF_REQUIRED(1, FALSE);
1632 Xstrndup_a(part_str, srcp, cur_len, );
1633 out_str = conv_codeset_strdup
1634 (part_str, cur_encoding, out_encoding);
1636 g_warning("conv_encode_header(): code conversion failed\n");
1637 conv_unreadable_8bit(part_str);
1638 out_str = g_strdup(part_str);
1640 out_str_len = strlen(out_str);
1643 out_enc_str_len = B64LEN(out_str_len);
1646 qp_get_q_encoding_len(out_str);
1648 Xalloca(enc_str, out_enc_str_len + 1, );
1650 base64_encode(enc_str, out_str, out_str_len);
1652 qp_q_encode(enc_str, out_str);
1656 /* output MIME-encoded string block */
1657 mime_block_len = mimestr_len + strlen(enc_str);
1658 g_snprintf(destp, mime_block_len + 1,
1659 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1660 out_encoding, mimesep_enc, enc_str);
1661 destp += mime_block_len;
1664 left -= mime_block_len;
1667 LBREAK_IF_REQUIRED(cont, FALSE);
1677 #undef LBREAK_IF_REQUIRED