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 #ifdef CONV_ALLOW_HWKANA
271 *out++ = *in++ & 0x7f;
276 if (iseuchwkana1(*(in + 2)) &&
277 iseuchwkana2(*(in + 3)))
278 len = conv_jis_hantozen
279 (jis_ch, *(in + 1), *(in + 3));
281 len = conv_jis_hantozen
282 (jis_ch, *(in + 1), '\0');
295 if (*in != '\0' && !isascii(*in)) {
300 } else if (iseucaux(*in)) {
302 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
304 *out++ = *in++ & 0x7f;
305 *out++ = *in++ & 0x7f;
308 if (*in != '\0' && !isascii(*in)) {
311 if (*in != '\0' && !isascii(*in)) {
328 void conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
330 const guchar *in = inbuf;
331 guchar *out = outbuf;
333 while (*in != '\0') {
336 } else if (issjiskanji1(*in)) {
337 if (issjiskanji2(*(in + 1))) {
339 guchar out2 = *(in + 1);
342 row = out1 < 0xa0 ? 0x70 : 0xb0;
344 out1 = (out1 - row) * 2 - 1;
345 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
347 out1 = (out1 - row) * 2;
351 *out++ = out1 | 0x80;
352 *out++ = out2 | 0x80;
357 if (*in != '\0' && !isascii(*in)) {
362 } else if (issjishwkana(*in)) {
374 void conv_anytoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
376 switch (conv_guess_ja_encoding(inbuf)) {
378 conv_jistoeuc(outbuf, outlen, inbuf);
381 conv_sjistoeuc(outbuf, outlen, inbuf);
384 strncpy2(outbuf, inbuf, outlen);
389 void conv_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
391 switch (conv_guess_ja_encoding(inbuf)) {
393 conv_euctojis(outbuf, outlen, inbuf);
396 strncpy2(outbuf, inbuf, outlen);
401 static gchar valid_eucjp_tbl[][96] = {
402 /* 0xa2a0 - 0xa2ff */
403 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
404 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
405 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
406 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
407 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
408 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
410 /* 0xa3a0 - 0xa3ff */
411 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
412 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
413 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
414 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
415 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
416 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
418 /* 0xa4a0 - 0xa4ff */
419 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
420 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
421 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
422 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
423 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
424 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
426 /* 0xa5a0 - 0xa5ff */
427 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
428 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
429 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
430 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
431 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
432 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
434 /* 0xa6a0 - 0xa6ff */
435 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
436 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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, 0, 0, 0, 0, 0, 0, 0,
439 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
440 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
442 /* 0xa7a0 - 0xa7ff */
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
446 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
450 /* 0xa8a0 - 0xa8ff */
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
454 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
455 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
456 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
459 static gboolean isprintableeuckanji(guchar c1, guchar c2)
461 if (c1 <= 0xa0 || c1 >= 0xf5)
463 if (c2 <= 0xa0 || c2 == 0xff)
466 if (c1 >= 0xa9 && c1 <= 0xaf)
469 if (c1 >= 0xa2 && c1 <= 0xa8)
470 return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0];
473 if (c2 >= 0xd4 && c2 <= 0xfe)
475 } else if (c1 == 0xf4) {
476 if (c2 >= 0xa7 && c2 <= 0xfe)
483 void conv_unreadable_eucjp(gchar *str)
485 register guchar *p = str;
489 /* convert CR+LF -> LF */
490 if (*p == '\r' && *(p + 1) == '\n')
491 memmove(p, p + 1, strlen(p));
492 /* printable 7 bit code */
494 } else if (iseuckanji(*p)) {
495 if (isprintableeuckanji(*p, *(p + 1))) {
496 /* printable euc-jp code */
499 /* substitute unprintable code */
508 } else if (iseuchwkana1(*p)) {
509 if (iseuchwkana2(*(p + 1)))
510 /* euc-jp hankaku kana */
514 } else if (iseucaux(*p)) {
515 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
516 /* auxiliary kanji */
521 /* substitute unprintable 1 byte code */
526 void conv_unreadable_8bit(gchar *str)
528 register guchar *p = str;
531 /* convert CR+LF -> LF */
532 if (*p == '\r' && *(p + 1) == '\n')
533 memmove(p, p + 1, strlen(p));
534 else if (!isascii(*p)) *p = SUBST_CHAR;
539 void conv_unreadable_latin(gchar *str)
541 register guchar *p = str;
544 /* convert CR+LF -> LF */
545 if (*p == '\r' && *(p + 1) == '\n')
546 memmove(p, p + 1, strlen(p));
547 else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f)
553 void conv_unreadable_locale(gchar *str)
555 switch (conv_get_current_charset()) {
571 conv_unreadable_latin(str);
574 conv_unreadable_eucjp(str);
583 void conv_mb_alnum(gchar *str)
585 static guchar char_tbl[] = {
587 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
588 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
590 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
591 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
593 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
594 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
596 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
597 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
599 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
600 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
603 register guchar *p = str;
610 register guchar ch = *(p + 1);
612 if (ch >= 0xb0 && ch <= 0xfa) {
617 memmove(p, p + 1, len);
623 } else if (*p == 0xa1) {
624 register guchar ch = *(p + 1);
626 if (ch >= 0xa0 && ch <= 0xef &&
627 NCV != char_tbl[ch - 0xa0]) {
628 *p = char_tbl[ch - 0xa0];
631 memmove(p, p + 1, len);
637 } else if (iseuckanji(*p)) {
647 CharSet conv_guess_ja_encoding(const gchar *str)
649 const guchar *p = str;
650 CharSet guessed = C_US_ASCII;
653 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
654 if (guessed == C_US_ASCII)
655 return C_ISO_2022_JP;
657 } else if (isascii(*p)) {
659 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
660 if (*p >= 0xfd && *p <= 0xfe)
662 else if (guessed == C_SHIFT_JIS) {
663 if ((issjiskanji1(*p) &&
664 issjiskanji2(*(p + 1))) ||
666 guessed = C_SHIFT_JIS;
672 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
673 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
674 guessed = C_SHIFT_JIS;
678 } else if (issjishwkana(*p)) {
679 guessed = C_SHIFT_JIS;
689 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
691 conv_jistoeuc(outbuf, outlen, inbuf);
692 conv_unreadable_eucjp(outbuf);
695 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
697 conv_sjistoeuc(outbuf, outlen, inbuf);
698 conv_unreadable_eucjp(outbuf);
701 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
703 strncpy2(outbuf, inbuf, outlen);
704 conv_unreadable_eucjp(outbuf);
707 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
709 conv_anytoeuc(outbuf, outlen, inbuf);
710 conv_unreadable_eucjp(outbuf);
713 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
715 strncpy2(outbuf, inbuf, outlen);
716 conv_unreadable_8bit(outbuf);
719 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
721 strncpy2(outbuf, inbuf, outlen);
722 conv_unreadable_latin(outbuf);
725 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
727 strncpy2(outbuf, inbuf, outlen);
728 conv_unreadable_locale(outbuf);
731 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
733 strncpy2(outbuf, inbuf, outlen);
736 CodeConverter *conv_code_converter_new(const gchar *charset)
740 conv = g_new0(CodeConverter, 1);
741 conv->code_conv_func = conv_get_code_conv_func(charset, NULL);
742 conv->charset_str = g_strdup(charset);
743 conv->charset = conv_get_charset_from_str(charset);
748 void conv_code_converter_destroy(CodeConverter *conv)
750 g_free(conv->charset_str);
754 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
758 if (conv->code_conv_func != conv_noconv)
759 conv->code_conv_func(outbuf, outlen, inbuf);
763 str = conv_iconv_strdup(inbuf, conv->charset_str, NULL);
767 strncpy2(outbuf, str, outlen);
771 #else /* !HAVE_ICONV */
772 conv->code_conv_func(outbuf, outlen, inbuf);
778 gchar *conv_codeset_strdup(const gchar *inbuf,
779 const gchar *src_code, const gchar *dest_code)
783 CodeConvFunc conv_func;
785 conv_func = conv_get_code_conv_func(src_code, dest_code);
786 if (conv_func != conv_noconv) {
787 len = (strlen(inbuf) + 1) * 3;
789 if (!buf) return NULL;
791 conv_func(buf, len, inbuf);
792 return g_realloc(buf, strlen(buf) + 1);
796 return conv_iconv_strdup(inbuf, src_code, dest_code);
798 return g_strdup(inbuf);
799 #endif /* HAVE_ICONV */
802 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
803 const gchar *dest_charset_str)
805 CodeConvFunc code_conv = conv_noconv;
807 CharSet dest_charset;
809 if (!src_charset_str)
810 src_charset = conv_get_current_charset();
812 src_charset = conv_get_charset_from_str(src_charset_str);
814 /* auto detection mode */
815 if (!src_charset_str && !dest_charset_str) {
816 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
817 return conv_anytodisp;
822 dest_charset = conv_get_charset_from_str(dest_charset_str);
824 if (dest_charset == C_US_ASCII)
825 return conv_ustodisp;
826 else if (dest_charset == C_UTF_8 ||
827 (dest_charset == C_AUTO &&
828 conv_get_current_charset() == C_UTF_8))
831 switch (src_charset) {
833 case C_ISO_2022_JP_2:
834 if (dest_charset == C_AUTO &&
835 conv_get_current_charset() == C_EUC_JP)
836 code_conv = conv_jistodisp;
837 else if (dest_charset == C_EUC_JP)
838 code_conv = conv_jistoeuc;
841 if (dest_charset == C_AUTO)
842 code_conv = conv_ustodisp;
858 if (dest_charset == C_AUTO &&
859 (conv_get_current_charset() == src_charset ||
861 code_conv = conv_latintodisp;
864 if (dest_charset == C_AUTO &&
865 conv_get_current_charset() == C_EUC_JP)
866 code_conv = conv_sjistodisp;
867 else if (dest_charset == C_EUC_JP)
868 code_conv = conv_sjistoeuc;
871 if (dest_charset == C_AUTO &&
872 conv_get_current_charset() == C_EUC_JP)
873 code_conv = conv_euctodisp;
874 else if (dest_charset == C_ISO_2022_JP ||
875 dest_charset == C_ISO_2022_JP_2)
876 code_conv = conv_euctojis;
886 gchar *conv_iconv_strdup(const gchar *inbuf,
887 const gchar *src_code, const gchar *dest_code)
890 const gchar *inbuf_p;
901 src_code = conv_get_outgoing_charset_str();
903 dest_code = conv_get_current_charset_str();
905 /* don't convert if current codeset is US-ASCII */
906 if (!strcasecmp(dest_code, CS_US_ASCII))
907 return g_strdup(inbuf);
909 /* don't convert if src and dest codeset are identical */
910 if (!strcasecmp(src_code, dest_code))
911 return g_strdup(inbuf);
913 cd = iconv_open(dest_code, src_code);
914 if (cd == (iconv_t)-1)
918 in_size = strlen(inbuf);
920 out_size = (in_size + 1) * 2;
921 outbuf = g_malloc(out_size);
925 #define EXPAND_BUF() \
927 len = outbuf_p - outbuf; \
929 outbuf = g_realloc(outbuf, out_size); \
930 outbuf_p = outbuf + len; \
931 out_left = out_size - len; \
934 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
935 &outbuf_p, &out_left)) == (size_t)-1) {
936 if (EILSEQ == errno) {
942 *outbuf_p++ = SUBST_CHAR;
944 } else if (EINVAL == errno) {
946 } else if (E2BIG == errno) {
949 g_warning("conv_iconv_strdup(): %s\n",
955 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
957 if (E2BIG == errno) {
960 g_warning("conv_iconv_strdup(): %s\n",
968 len = outbuf_p - outbuf;
969 outbuf = g_realloc(outbuf, len + 1);
976 #endif /* HAVE_ICONV */
978 static const struct {
982 {C_US_ASCII, CS_US_ASCII},
983 {C_US_ASCII, CS_ANSI_X3_4_1968},
986 {C_ISO_8859_1, CS_ISO_8859_1},
987 {C_ISO_8859_2, CS_ISO_8859_2},
988 {C_ISO_8859_3, CS_ISO_8859_3},
989 {C_ISO_8859_4, CS_ISO_8859_4},
990 {C_ISO_8859_5, CS_ISO_8859_5},
991 {C_ISO_8859_6, CS_ISO_8859_6},
992 {C_ISO_8859_7, CS_ISO_8859_7},
993 {C_ISO_8859_8, CS_ISO_8859_8},
994 {C_ISO_8859_9, CS_ISO_8859_9},
995 {C_ISO_8859_10, CS_ISO_8859_10},
996 {C_ISO_8859_11, CS_ISO_8859_11},
997 {C_ISO_8859_13, CS_ISO_8859_13},
998 {C_ISO_8859_14, CS_ISO_8859_14},
999 {C_ISO_8859_15, CS_ISO_8859_15},
1000 {C_BALTIC, CS_BALTIC},
1001 {C_CP1250, CS_CP1250},
1002 {C_CP1251, CS_CP1251},
1003 {C_CP1252, CS_CP1252},
1004 {C_CP1253, CS_CP1253},
1005 {C_CP1254, CS_CP1254},
1006 {C_CP1255, CS_CP1255},
1007 {C_CP1256, CS_CP1256},
1008 {C_CP1257, CS_CP1257},
1009 {C_CP1258, CS_CP1258},
1010 {C_WINDOWS_1250, CS_WINDOWS_1250},
1011 {C_WINDOWS_1251, CS_WINDOWS_1251},
1012 {C_WINDOWS_1252, CS_WINDOWS_1252},
1013 {C_WINDOWS_1253, CS_WINDOWS_1253},
1014 {C_WINDOWS_1254, CS_WINDOWS_1254},
1015 {C_WINDOWS_1255, CS_WINDOWS_1255},
1016 {C_WINDOWS_1256, CS_WINDOWS_1256},
1017 {C_WINDOWS_1257, CS_WINDOWS_1257},
1018 {C_WINDOWS_1258, CS_WINDOWS_1258},
1019 {C_KOI8_R, CS_KOI8_R},
1020 {C_KOI8_T, CS_KOI8_T},
1021 {C_KOI8_U, CS_KOI8_U},
1022 {C_ISO_2022_JP, CS_ISO_2022_JP},
1023 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1024 {C_EUC_JP, CS_EUC_JP},
1025 {C_EUC_JP, CS_EUCJP},
1026 {C_SHIFT_JIS, CS_SHIFT_JIS},
1027 {C_SHIFT_JIS, CS_SHIFT__JIS},
1028 {C_SHIFT_JIS, CS_SJIS},
1029 {C_ISO_2022_KR, CS_ISO_2022_KR},
1030 {C_EUC_KR, CS_EUC_KR},
1031 {C_ISO_2022_CN, CS_ISO_2022_CN},
1032 {C_EUC_CN, CS_EUC_CN},
1033 {C_GB2312, CS_GB2312},
1035 {C_EUC_TW, CS_EUC_TW},
1037 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1038 {C_TIS_620, CS_TIS_620},
1039 {C_WINDOWS_874, CS_WINDOWS_874},
1040 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1041 {C_TCVN5712_1, CS_TCVN5712_1},
1044 static const struct {
1045 gchar *const locale;
1047 CharSet out_charset;
1048 } locale_table[] = {
1049 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1050 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1051 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1052 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1053 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1054 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1055 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1056 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1057 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1058 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1059 {"zh_CN.GBK" , C_GBK , C_GB2312},
1060 {"zh_CN" , C_GB2312 , C_GB2312},
1061 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1062 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1063 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1064 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1065 {"zh_TW" , C_BIG5 , C_BIG5},
1067 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1068 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1069 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1070 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1071 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1072 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1073 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1075 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1076 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1078 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1080 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1081 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1082 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1083 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1084 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1085 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1086 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1087 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1088 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1089 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1090 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1091 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1092 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1093 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1094 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1095 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1096 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1097 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1098 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1099 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1100 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1101 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1102 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1103 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1104 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1105 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1106 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1107 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1108 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1109 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1110 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1111 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1112 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1113 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1114 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1115 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1116 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1117 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1118 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1119 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1120 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1121 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1122 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1123 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1124 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1125 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1126 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1127 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1128 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1129 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1130 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1131 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1132 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1133 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1134 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1135 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1136 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1137 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1138 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1139 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1140 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1141 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1142 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1143 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1144 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1145 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1146 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1147 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1148 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1149 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1150 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1151 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1152 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1153 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1155 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1156 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1157 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1158 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1159 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1160 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1161 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1162 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1164 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1165 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1167 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1169 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1170 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1171 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1172 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1174 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1176 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1177 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1178 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1179 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1180 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1181 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1182 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1183 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1184 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1185 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1186 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1187 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1188 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1189 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1190 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1191 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1192 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1194 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1195 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1196 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1197 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1199 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1200 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1202 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1204 {"ar_IN" , C_UTF_8 , C_UTF_8},
1205 {"en_IN" , C_UTF_8 , C_UTF_8},
1206 {"se_NO" , C_UTF_8 , C_UTF_8},
1207 {"ta_IN" , C_UTF_8 , C_UTF_8},
1208 {"te_IN" , C_UTF_8 , C_UTF_8},
1209 {"ur_PK" , C_UTF_8 , C_UTF_8},
1211 {"th_TH" , C_TIS_620 , C_TIS_620},
1212 /* {"th_TH" , C_WINDOWS_874}, */
1213 /* {"th_TH" , C_ISO_8859_11}, */
1215 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1216 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1218 {"C" , C_US_ASCII , C_US_ASCII},
1219 {"POSIX" , C_US_ASCII , C_US_ASCII},
1220 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1223 static GHashTable *conv_get_charset_to_str_table(void)
1225 static GHashTable *table;
1231 table = g_hash_table_new(NULL, g_direct_equal);
1233 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1234 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1237 (table, GUINT_TO_POINTER(charsets[i].charset),
1245 static GHashTable *conv_get_charset_from_str_table(void)
1247 static GHashTable *table;
1253 table = g_hash_table_new(str_case_hash, str_case_equal);
1255 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1256 g_hash_table_insert(table, charsets[i].name,
1257 GUINT_TO_POINTER(charsets[i].charset));
1263 const gchar *conv_get_charset_str(CharSet charset)
1267 table = conv_get_charset_to_str_table();
1268 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1271 CharSet conv_get_charset_from_str(const gchar *charset)
1275 if (!charset) return C_AUTO;
1277 table = conv_get_charset_from_str_table();
1278 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1281 CharSet conv_get_current_charset(void)
1283 static CharSet cur_charset = -1;
1284 const gchar *cur_locale;
1288 if (cur_charset != -1)
1291 cur_locale = conv_get_current_locale();
1293 cur_charset = C_US_ASCII;
1297 if (strcasestr(cur_locale, "UTF-8")) {
1298 cur_charset = C_UTF_8;
1302 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1303 cur_charset = C_ISO_8859_15;
1307 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1310 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1311 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1312 if (!strncasecmp(cur_locale, locale_table[i].locale,
1313 strlen(locale_table[i].locale))) {
1314 cur_charset = locale_table[i].charset;
1316 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1317 !strchr(p + 1, '.')) {
1318 if (strlen(cur_locale) == 2 &&
1319 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1320 cur_charset = locale_table[i].charset;
1326 cur_charset = C_AUTO;
1330 const gchar *conv_get_current_charset_str(void)
1332 static const gchar *codeset = NULL;
1335 codeset = conv_get_charset_str(conv_get_current_charset());
1337 return codeset ? codeset : CS_US_ASCII;
1340 CharSet conv_get_outgoing_charset(void)
1342 static CharSet out_charset = -1;
1343 const gchar *cur_locale;
1347 if (out_charset != -1)
1350 cur_locale = conv_get_current_locale();
1352 out_charset = C_AUTO;
1356 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1357 out_charset = C_ISO_8859_15;
1361 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1364 if (!strncasecmp(cur_locale, locale_table[i].locale,
1365 strlen(locale_table[i].locale))) {
1366 out_charset = locale_table[i].out_charset;
1368 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1369 !strchr(p + 1, '.')) {
1370 if (strlen(cur_locale) == 2 &&
1371 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1372 out_charset = locale_table[i].out_charset;
1379 /* encoding conversion without iconv() is only supported
1380 on Japanese locale for now */
1381 if (out_charset == C_ISO_2022_JP)
1384 return conv_get_current_charset();
1390 const gchar *conv_get_outgoing_charset_str(void)
1392 CharSet out_charset;
1395 if (prefs_common.outgoing_charset) {
1396 if (!isalpha((guchar)prefs_common.outgoing_charset[0])) {
1397 g_free(prefs_common.outgoing_charset);
1398 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
1399 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
1400 return prefs_common.outgoing_charset;
1403 out_charset = conv_get_outgoing_charset();
1404 str = conv_get_charset_str(out_charset);
1406 return str ? str : CS_US_ASCII;
1409 gboolean conv_is_multibyte_encoding(CharSet encoding)
1417 case C_ISO_2022_JP_2:
1431 const gchar *conv_get_current_locale(void)
1435 cur_locale = g_getenv("LC_ALL");
1436 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1437 if (!cur_locale) cur_locale = g_getenv("LANG");
1438 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1440 debug_print("current locale: %s\n",
1441 cur_locale ? cur_locale : "(none)");
1446 void conv_unmime_header_overwrite(gchar *str)
1450 CharSet cur_charset;
1452 cur_charset = conv_get_current_charset();
1454 if (cur_charset == C_EUC_JP) {
1455 buflen = strlen(str) * 2 + 1;
1456 Xalloca(buf, buflen, return);
1457 conv_anytodisp(buf, buflen, str);
1458 unmime_header(str, buf);
1460 buflen = strlen(str) + 1;
1461 Xalloca(buf, buflen, return);
1462 unmime_header(buf, str);
1463 strncpy2(str, buf, buflen);
1467 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1468 const gchar *charset)
1470 CharSet cur_charset;
1472 cur_charset = conv_get_current_charset();
1474 if (cur_charset == C_EUC_JP) {
1478 buflen = strlen(str) * 2 + 1;
1479 Xalloca(buf, buflen, return);
1480 conv_anytodisp(buf, buflen, str);
1481 unmime_header(outbuf, buf);
1483 unmime_header(outbuf, str);
1486 #define MAX_LINELEN 76
1487 #define MAX_HARD_LINELEN 996
1488 #define MIMESEP_BEGIN "=?"
1489 #define MIMESEP_END "?="
1491 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1493 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1495 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1500 if ((cond) && *srcp) { \
1501 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1502 if (isspace(*(destp - 1))) \
1504 else if (is_plain_text && isspace(*srcp)) \
1509 left = MAX_LINELEN - 1; \
1515 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1516 gint header_len, gboolean addr_field)
1518 const gchar *cur_encoding;
1519 const gchar *out_encoding;
1523 const guchar *srcp = src;
1524 guchar *destp = dest;
1525 gboolean use_base64;
1527 if (MB_CUR_MAX > 1) {
1529 mimesep_enc = "?B?";
1532 mimesep_enc = "?Q?";
1535 cur_encoding = conv_get_current_charset_str();
1536 if (!strcmp(cur_encoding, CS_US_ASCII))
1537 cur_encoding = CS_ISO_8859_1;
1538 out_encoding = conv_get_outgoing_charset_str();
1539 if (!strcmp(out_encoding, CS_US_ASCII))
1540 out_encoding = CS_ISO_8859_1;
1542 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1543 strlen(mimesep_enc) + strlen(MIMESEP_END);
1545 left = MAX_LINELEN - header_len;
1548 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1550 while (isspace(*srcp)) {
1553 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1556 /* output as it is if the next word is ASCII string */
1557 if (!is_next_nonascii(srcp)) {
1560 word_len = get_next_word_len(srcp);
1561 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1562 while (word_len > 0) {
1563 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1572 /* don't include parentheses in encoded strings */
1573 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1574 LBREAK_IF_REQUIRED(left < 2, FALSE);
1585 const guchar *p = srcp;
1587 gint out_enc_str_len;
1588 gint mime_block_len;
1589 gboolean cont = FALSE;
1591 while (*p != '\0') {
1592 if (isspace(*p) && !is_next_nonascii(p + 1))
1594 /* don't include parentheses in encoded
1596 if (addr_field && (*p == '(' || *p == ')'))
1599 if (MB_CUR_MAX > 1) {
1600 mb_len = mblen(p, MB_CUR_MAX);
1602 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1608 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1609 out_str = conv_codeset_strdup
1610 (part_str, cur_encoding, out_encoding);
1612 g_warning("conv_encode_header(): code conversion failed\n");
1613 conv_unreadable_8bit(part_str);
1614 out_str = g_strdup(part_str);
1616 out_str_len = strlen(out_str);
1619 out_enc_str_len = B64LEN(out_str_len);
1622 qp_get_q_encoding_len(out_str);
1626 if (mimestr_len + out_enc_str_len <= left) {
1629 } else if (cur_len == 0) {
1630 LBREAK_IF_REQUIRED(1, FALSE);
1639 Xstrndup_a(part_str, srcp, cur_len, );
1640 out_str = conv_codeset_strdup
1641 (part_str, cur_encoding, out_encoding);
1643 g_warning("conv_encode_header(): code conversion failed\n");
1644 conv_unreadable_8bit(part_str);
1645 out_str = g_strdup(part_str);
1647 out_str_len = strlen(out_str);
1650 out_enc_str_len = B64LEN(out_str_len);
1653 qp_get_q_encoding_len(out_str);
1655 Xalloca(enc_str, out_enc_str_len + 1, );
1657 base64_encode(enc_str, out_str, out_str_len);
1659 qp_q_encode(enc_str, out_str);
1663 /* output MIME-encoded string block */
1664 mime_block_len = mimestr_len + strlen(enc_str);
1665 g_snprintf(destp, mime_block_len + 1,
1666 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1667 out_encoding, mimesep_enc, enc_str);
1668 destp += mime_block_len;
1671 left -= mime_block_len;
1674 LBREAK_IF_REQUIRED(cont, FALSE);
1684 #undef LBREAK_IF_REQUIRED