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_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
393 switch (conv_guess_ja_encoding(inbuf)) {
395 conv_euctojis(outbuf, outlen, inbuf);
398 strncpy2(outbuf, inbuf, outlen);
403 static gchar valid_eucjp_tbl[][96] = {
404 /* 0xa2a0 - 0xa2ff */
405 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
406 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
407 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
408 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
409 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
410 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
412 /* 0xa3a0 - 0xa3ff */
413 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
414 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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,
417 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
418 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
420 /* 0xa4a0 - 0xa4ff */
421 { 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
425 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
426 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
428 /* 0xa5a0 - 0xa5ff */
429 { 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1,
433 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
434 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
436 /* 0xa6a0 - 0xa6ff */
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, 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, 0, 0, 0, 0, 0, 0, 0,
441 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
442 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
444 /* 0xa7a0 - 0xa7ff */
445 { 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
448 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
452 /* 0xa8a0 - 0xa8ff */
453 { 0, 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, 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,
457 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
458 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
461 static gboolean isprintableeuckanji(guchar c1, guchar c2)
463 if (c1 <= 0xa0 || c1 >= 0xf5)
465 if (c2 <= 0xa0 || c2 == 0xff)
468 if (c1 >= 0xa9 && c1 <= 0xaf)
471 if (c1 >= 0xa2 && c1 <= 0xa8)
472 return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0];
475 if (c2 >= 0xd4 && c2 <= 0xfe)
477 } else if (c1 == 0xf4) {
478 if (c2 >= 0xa7 && c2 <= 0xfe)
485 void conv_unreadable_eucjp(gchar *str)
487 register guchar *p = str;
491 /* convert CR+LF -> LF */
492 if (*p == '\r' && *(p + 1) == '\n')
493 memmove(p, p + 1, strlen(p));
494 /* printable 7 bit code */
496 } else if (iseuckanji(*p)) {
497 if (isprintableeuckanji(*p, *(p + 1))) {
498 /* printable euc-jp code */
501 /* substitute unprintable code */
510 } else if (iseuchwkana1(*p)) {
511 if (iseuchwkana2(*(p + 1)))
512 /* euc-jp hankaku kana */
516 } else if (iseucaux(*p)) {
517 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
518 /* auxiliary kanji */
523 /* substitute unprintable 1 byte code */
528 void conv_unreadable_8bit(gchar *str)
530 register guchar *p = str;
533 /* convert CR+LF -> LF */
534 if (*p == '\r' && *(p + 1) == '\n')
535 memmove(p, p + 1, strlen(p));
536 else if (!isascii(*p)) *p = SUBST_CHAR;
541 void conv_unreadable_latin(gchar *str)
543 register guchar *p = str;
546 /* convert CR+LF -> LF */
547 if (*p == '\r' && *(p + 1) == '\n')
548 memmove(p, p + 1, strlen(p));
549 else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f)
555 void conv_unreadable_locale(gchar *str)
557 switch (conv_get_current_charset()) {
573 conv_unreadable_latin(str);
576 conv_unreadable_eucjp(str);
585 void conv_mb_alnum(gchar *str)
587 static guchar char_tbl[] = {
589 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
590 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
592 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
593 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
595 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
596 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
598 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
599 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
601 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
602 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
605 register guchar *p = str;
612 register guchar ch = *(p + 1);
614 if (ch >= 0xb0 && ch <= 0xfa) {
619 memmove(p, p + 1, len);
625 } else if (*p == 0xa1) {
626 register guchar ch = *(p + 1);
628 if (ch >= 0xa0 && ch <= 0xef &&
629 NCV != char_tbl[ch - 0xa0]) {
630 *p = char_tbl[ch - 0xa0];
633 memmove(p, p + 1, len);
639 } else if (iseuckanji(*p)) {
649 CharSet conv_guess_ja_encoding(const gchar *str)
651 const guchar *p = str;
652 CharSet guessed = C_US_ASCII;
655 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
656 if (guessed == C_US_ASCII)
657 return C_ISO_2022_JP;
659 } else if (isascii(*p)) {
661 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
662 if (*p >= 0xfd && *p <= 0xfe)
664 else if (guessed == C_SHIFT_JIS) {
665 if ((issjiskanji1(*p) &&
666 issjiskanji2(*(p + 1))) ||
668 guessed = C_SHIFT_JIS;
674 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
675 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
676 guessed = C_SHIFT_JIS;
680 } else if (issjishwkana(*p)) {
681 guessed = C_SHIFT_JIS;
691 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
693 conv_jistoeuc(outbuf, outlen, inbuf);
694 conv_unreadable_eucjp(outbuf);
697 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
699 conv_sjistoeuc(outbuf, outlen, inbuf);
700 conv_unreadable_eucjp(outbuf);
703 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
705 strncpy2(outbuf, inbuf, outlen);
706 conv_unreadable_eucjp(outbuf);
709 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
711 conv_anytoeuc(outbuf, outlen, inbuf);
712 conv_unreadable_eucjp(outbuf);
715 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
717 strncpy2(outbuf, inbuf, outlen);
718 conv_unreadable_8bit(outbuf);
721 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
723 strncpy2(outbuf, inbuf, outlen);
724 conv_unreadable_latin(outbuf);
727 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
729 strncpy2(outbuf, inbuf, outlen);
730 conv_unreadable_locale(outbuf);
733 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
735 strncpy2(outbuf, inbuf, outlen);
738 CodeConverter *conv_code_converter_new(const gchar *charset)
742 conv = g_new0(CodeConverter, 1);
743 conv->code_conv_func = conv_get_code_conv_func(charset, NULL);
744 conv->charset_str = g_strdup(charset);
745 conv->charset = conv_get_charset_from_str(charset);
750 void conv_code_converter_destroy(CodeConverter *conv)
752 g_free(conv->charset_str);
756 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
760 if (conv->code_conv_func != conv_noconv)
761 conv->code_conv_func(outbuf, outlen, inbuf);
765 str = conv_iconv_strdup(inbuf, conv->charset_str, NULL);
769 strncpy2(outbuf, str, outlen);
773 #else /* !HAVE_ICONV */
774 conv->code_conv_func(outbuf, outlen, inbuf);
780 gchar *conv_codeset_strdup(const gchar *inbuf,
781 const gchar *src_code, const gchar *dest_code)
785 CodeConvFunc conv_func;
787 conv_func = conv_get_code_conv_func(src_code, dest_code);
788 if (conv_func != conv_noconv) {
789 len = (strlen(inbuf) + 1) * 3;
791 if (!buf) return NULL;
793 conv_func(buf, len, inbuf);
794 return g_realloc(buf, strlen(buf) + 1);
798 return conv_iconv_strdup(inbuf, src_code, dest_code);
800 return g_strdup(inbuf);
801 #endif /* HAVE_ICONV */
804 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
805 const gchar *dest_charset_str)
807 CodeConvFunc code_conv = conv_noconv;
809 CharSet dest_charset;
811 if (!src_charset_str)
812 src_charset = conv_get_current_charset();
814 src_charset = conv_get_charset_from_str(src_charset_str);
816 /* auto detection mode */
817 if (!src_charset_str && !dest_charset_str) {
818 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
819 return conv_anytodisp;
824 dest_charset = conv_get_charset_from_str(dest_charset_str);
826 if (dest_charset == C_US_ASCII)
827 return conv_ustodisp;
828 else if (dest_charset == C_UTF_8 ||
829 (dest_charset == C_AUTO &&
830 conv_get_current_charset() == C_UTF_8))
833 switch (src_charset) {
835 case C_ISO_2022_JP_2:
836 case C_ISO_2022_JP_3:
837 if (dest_charset == C_AUTO &&
838 conv_get_current_charset() == C_EUC_JP)
839 code_conv = conv_jistodisp;
840 else if (dest_charset == C_EUC_JP)
841 code_conv = conv_jistoeuc;
844 if (dest_charset == C_AUTO)
845 code_conv = conv_ustodisp;
861 if (dest_charset == C_AUTO &&
862 (conv_get_current_charset() == src_charset ||
864 code_conv = conv_latintodisp;
867 if (dest_charset == C_AUTO &&
868 conv_get_current_charset() == C_EUC_JP)
869 code_conv = conv_sjistodisp;
870 else if (dest_charset == C_EUC_JP)
871 code_conv = conv_sjistoeuc;
874 if (dest_charset == C_AUTO &&
875 conv_get_current_charset() == C_EUC_JP)
876 code_conv = conv_euctodisp;
877 else if (dest_charset == C_ISO_2022_JP ||
878 dest_charset == C_ISO_2022_JP_2 ||
879 dest_charset == C_ISO_2022_JP_3)
880 code_conv = conv_euctojis;
890 gchar *conv_iconv_strdup(const gchar *inbuf,
891 const gchar *src_code, const gchar *dest_code)
894 const gchar *inbuf_p;
905 src_code = conv_get_outgoing_charset_str();
907 dest_code = conv_get_current_charset_str();
909 /* don't convert if current codeset is US-ASCII */
910 if (!strcasecmp(dest_code, CS_US_ASCII))
911 return g_strdup(inbuf);
913 /* don't convert if src and dest codeset are identical */
914 if (!strcasecmp(src_code, dest_code))
915 return g_strdup(inbuf);
917 cd = iconv_open(dest_code, src_code);
918 if (cd == (iconv_t)-1)
922 in_size = strlen(inbuf);
924 out_size = (in_size + 1) * 2;
925 outbuf = g_malloc(out_size);
929 #define EXPAND_BUF() \
931 len = outbuf_p - outbuf; \
933 outbuf = g_realloc(outbuf, out_size); \
934 outbuf_p = outbuf + len; \
935 out_left = out_size - len; \
938 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
939 &outbuf_p, &out_left)) == (size_t)-1) {
940 if (EILSEQ == errno) {
946 *outbuf_p++ = SUBST_CHAR;
948 } else if (EINVAL == errno) {
950 } else if (E2BIG == errno) {
953 g_warning("conv_iconv_strdup(): %s\n",
959 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) ==
961 if (E2BIG == errno) {
964 g_warning("conv_iconv_strdup(): %s\n",
972 len = outbuf_p - outbuf;
973 outbuf = g_realloc(outbuf, len + 1);
980 #endif /* HAVE_ICONV */
982 static const struct {
986 {C_US_ASCII, CS_US_ASCII},
987 {C_US_ASCII, CS_ANSI_X3_4_1968},
990 {C_ISO_8859_1, CS_ISO_8859_1},
991 {C_ISO_8859_2, CS_ISO_8859_2},
992 {C_ISO_8859_3, CS_ISO_8859_3},
993 {C_ISO_8859_4, CS_ISO_8859_4},
994 {C_ISO_8859_5, CS_ISO_8859_5},
995 {C_ISO_8859_6, CS_ISO_8859_6},
996 {C_ISO_8859_7, CS_ISO_8859_7},
997 {C_ISO_8859_8, CS_ISO_8859_8},
998 {C_ISO_8859_9, CS_ISO_8859_9},
999 {C_ISO_8859_10, CS_ISO_8859_10},
1000 {C_ISO_8859_11, CS_ISO_8859_11},
1001 {C_ISO_8859_13, CS_ISO_8859_13},
1002 {C_ISO_8859_14, CS_ISO_8859_14},
1003 {C_ISO_8859_15, CS_ISO_8859_15},
1004 {C_BALTIC, CS_BALTIC},
1005 {C_CP1250, CS_CP1250},
1006 {C_CP1251, CS_CP1251},
1007 {C_CP1252, CS_CP1252},
1008 {C_CP1253, CS_CP1253},
1009 {C_CP1254, CS_CP1254},
1010 {C_CP1255, CS_CP1255},
1011 {C_CP1256, CS_CP1256},
1012 {C_CP1257, CS_CP1257},
1013 {C_CP1258, CS_CP1258},
1014 {C_WINDOWS_1250, CS_WINDOWS_1250},
1015 {C_WINDOWS_1251, CS_WINDOWS_1251},
1016 {C_WINDOWS_1252, CS_WINDOWS_1252},
1017 {C_WINDOWS_1253, CS_WINDOWS_1253},
1018 {C_WINDOWS_1254, CS_WINDOWS_1254},
1019 {C_WINDOWS_1255, CS_WINDOWS_1255},
1020 {C_WINDOWS_1256, CS_WINDOWS_1256},
1021 {C_WINDOWS_1257, CS_WINDOWS_1257},
1022 {C_WINDOWS_1258, CS_WINDOWS_1258},
1023 {C_KOI8_R, CS_KOI8_R},
1024 {C_KOI8_T, CS_KOI8_T},
1025 {C_KOI8_U, CS_KOI8_U},
1026 {C_ISO_2022_JP, CS_ISO_2022_JP},
1027 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
1028 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3},
1029 {C_EUC_JP, CS_EUC_JP},
1030 {C_EUC_JP, CS_EUCJP},
1031 {C_SHIFT_JIS, CS_SHIFT_JIS},
1032 {C_SHIFT_JIS, CS_SHIFT__JIS},
1033 {C_SHIFT_JIS, CS_SJIS},
1034 {C_ISO_2022_KR, CS_ISO_2022_KR},
1035 {C_EUC_KR, CS_EUC_KR},
1036 {C_ISO_2022_CN, CS_ISO_2022_CN},
1037 {C_EUC_CN, CS_EUC_CN},
1038 {C_GB2312, CS_GB2312},
1040 {C_EUC_TW, CS_EUC_TW},
1042 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
1043 {C_TIS_620, CS_TIS_620},
1044 {C_WINDOWS_874, CS_WINDOWS_874},
1045 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
1046 {C_TCVN5712_1, CS_TCVN5712_1},
1049 static const struct {
1050 gchar *const locale;
1052 CharSet out_charset;
1053 } locale_table[] = {
1054 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
1055 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
1056 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
1057 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
1058 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
1059 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
1060 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
1061 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
1062 {"ko_KR" , C_EUC_KR , C_EUC_KR},
1063 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
1064 {"zh_CN.GBK" , C_GBK , C_GB2312},
1065 {"zh_CN" , C_GB2312 , C_GB2312},
1066 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
1067 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
1068 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
1069 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
1070 {"zh_TW" , C_BIG5 , C_BIG5},
1072 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
1073 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
1074 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
1075 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
1076 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
1077 {"ru_UA" , C_KOI8_U , C_KOI8_U},
1078 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U},
1079 {"uk_UA" , C_KOI8_U , C_KOI8_U},
1081 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
1082 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
1084 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
1086 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1087 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1088 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1089 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1090 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
1091 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1092 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1093 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
1094 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1095 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1096 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1097 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1098 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1099 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1100 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1101 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1102 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1103 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1104 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1105 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1106 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1107 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1108 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1109 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1110 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1111 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1112 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1113 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1114 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1115 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1116 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1117 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1118 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1119 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1120 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1121 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1122 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1123 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1124 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1125 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1126 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1127 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1128 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1129 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1130 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1131 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1132 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1133 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1134 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1135 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1136 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1137 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1138 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1139 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1140 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1141 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1142 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1143 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1144 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1145 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1146 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1147 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1148 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1149 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1150 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1151 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1152 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1153 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1154 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1155 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1156 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1157 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1158 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1159 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1161 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1162 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1163 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1164 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1165 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1166 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1167 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1168 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1170 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1171 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1173 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1175 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1176 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1177 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1178 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1180 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1182 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1183 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1184 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1185 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1186 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1187 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1188 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1189 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1190 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1191 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1192 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1193 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1194 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1195 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1196 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1197 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1198 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1200 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1201 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1202 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1203 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1205 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1206 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1208 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1210 {"ar_IN" , C_UTF_8 , C_UTF_8},
1211 {"en_IN" , C_UTF_8 , C_UTF_8},
1212 {"se_NO" , C_UTF_8 , C_UTF_8},
1213 {"ta_IN" , C_UTF_8 , C_UTF_8},
1214 {"te_IN" , C_UTF_8 , C_UTF_8},
1215 {"ur_PK" , C_UTF_8 , C_UTF_8},
1217 {"th_TH" , C_TIS_620 , C_TIS_620},
1218 /* {"th_TH" , C_WINDOWS_874}, */
1219 /* {"th_TH" , C_ISO_8859_11}, */
1221 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1222 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1224 {"C" , C_US_ASCII , C_US_ASCII},
1225 {"POSIX" , C_US_ASCII , C_US_ASCII},
1226 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1229 static GHashTable *conv_get_charset_to_str_table(void)
1231 static GHashTable *table;
1237 table = g_hash_table_new(NULL, g_direct_equal);
1239 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1240 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1243 (table, GUINT_TO_POINTER(charsets[i].charset),
1251 static GHashTable *conv_get_charset_from_str_table(void)
1253 static GHashTable *table;
1259 table = g_hash_table_new(str_case_hash, str_case_equal);
1261 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1262 g_hash_table_insert(table, charsets[i].name,
1263 GUINT_TO_POINTER(charsets[i].charset));
1269 const gchar *conv_get_charset_str(CharSet charset)
1273 table = conv_get_charset_to_str_table();
1274 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1277 CharSet conv_get_charset_from_str(const gchar *charset)
1281 if (!charset) return C_AUTO;
1283 table = conv_get_charset_from_str_table();
1284 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1287 CharSet conv_get_current_charset(void)
1289 static CharSet cur_charset = -1;
1290 const gchar *cur_locale;
1294 if (cur_charset != -1)
1297 cur_locale = conv_get_current_locale();
1299 cur_charset = C_US_ASCII;
1303 if (strcasestr(cur_locale, "UTF-8")) {
1304 cur_charset = C_UTF_8;
1308 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1309 cur_charset = C_ISO_8859_15;
1313 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1316 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1317 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1318 if (!strncasecmp(cur_locale, locale_table[i].locale,
1319 strlen(locale_table[i].locale))) {
1320 cur_charset = locale_table[i].charset;
1322 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1323 !strchr(p + 1, '.')) {
1324 if (strlen(cur_locale) == 2 &&
1325 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1326 cur_charset = locale_table[i].charset;
1332 cur_charset = C_AUTO;
1336 const gchar *conv_get_current_charset_str(void)
1338 static const gchar *codeset = NULL;
1341 codeset = conv_get_charset_str(conv_get_current_charset());
1343 return codeset ? codeset : CS_US_ASCII;
1346 CharSet conv_get_outgoing_charset(void)
1348 static CharSet out_charset = -1;
1349 const gchar *cur_locale;
1353 if (out_charset != -1)
1356 cur_locale = conv_get_current_locale();
1358 out_charset = C_AUTO;
1362 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1363 out_charset = C_ISO_8859_15;
1367 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1370 if (!strncasecmp(cur_locale, locale_table[i].locale,
1371 strlen(locale_table[i].locale))) {
1372 out_charset = locale_table[i].out_charset;
1374 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1375 !strchr(p + 1, '.')) {
1376 if (strlen(cur_locale) == 2 &&
1377 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1378 out_charset = locale_table[i].out_charset;
1385 /* encoding conversion without iconv() is only supported
1386 on Japanese locale for now */
1387 if (out_charset == C_ISO_2022_JP)
1390 return conv_get_current_charset();
1396 const gchar *conv_get_outgoing_charset_str(void)
1398 CharSet out_charset;
1401 if (prefs_common.outgoing_charset) {
1402 if (!isalpha((guchar)prefs_common.outgoing_charset[0])) {
1403 g_free(prefs_common.outgoing_charset);
1404 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
1405 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
1406 return prefs_common.outgoing_charset;
1409 out_charset = conv_get_outgoing_charset();
1410 str = conv_get_charset_str(out_charset);
1412 return str ? str : CS_US_ASCII;
1415 gboolean conv_is_multibyte_encoding(CharSet encoding)
1423 case C_ISO_2022_JP_2:
1424 case C_ISO_2022_JP_3:
1438 const gchar *conv_get_current_locale(void)
1442 cur_locale = g_getenv("LC_ALL");
1443 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1444 if (!cur_locale) cur_locale = g_getenv("LANG");
1445 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1447 debug_print("current locale: %s\n",
1448 cur_locale ? cur_locale : "(none)");
1453 void conv_unmime_header_overwrite(gchar *str)
1457 CharSet cur_charset;
1459 g_return_if_fail(str != NULL);
1461 cur_charset = conv_get_current_charset();
1463 if (cur_charset == C_EUC_JP) {
1464 buflen = strlen(str) * 2 + 1;
1465 Xalloca(buf, buflen, return);
1466 conv_anytodisp(buf, buflen, str);
1467 unmime_header(str, buf);
1469 buflen = strlen(str) + 1;
1470 Xalloca(buf, buflen, return);
1471 unmime_header(buf, str);
1472 strncpy2(str, buf, buflen);
1476 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1477 const gchar *charset)
1479 CharSet cur_charset;
1481 cur_charset = conv_get_current_charset();
1483 if (cur_charset == C_EUC_JP) {
1487 buflen = strlen(str) * 2 + 1;
1488 Xalloca(buf, buflen, return);
1489 conv_anytodisp(buf, buflen, str);
1490 unmime_header(outbuf, buf);
1492 unmime_header(outbuf, str);
1495 #define MAX_LINELEN 76
1496 #define MAX_HARD_LINELEN 996
1497 #define MIMESEP_BEGIN "=?"
1498 #define MIMESEP_END "?="
1500 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1502 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1504 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1509 if ((cond) && *srcp) { \
1510 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1511 if (isspace(*(destp - 1))) \
1513 else if (is_plain_text && isspace(*srcp)) \
1518 left = MAX_LINELEN - 1; \
1524 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1525 gint header_len, gboolean addr_field)
1527 const gchar *cur_encoding;
1528 const gchar *out_encoding;
1532 const guchar *srcp = src;
1533 guchar *destp = dest;
1534 gboolean use_base64;
1536 if (MB_CUR_MAX > 1) {
1538 mimesep_enc = "?B?";
1541 mimesep_enc = "?Q?";
1544 cur_encoding = conv_get_current_charset_str();
1545 if (!strcmp(cur_encoding, CS_US_ASCII))
1546 cur_encoding = CS_ISO_8859_1;
1547 out_encoding = conv_get_outgoing_charset_str();
1548 if (!strcmp(out_encoding, CS_US_ASCII))
1549 out_encoding = CS_ISO_8859_1;
1551 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1552 strlen(mimesep_enc) + strlen(MIMESEP_END);
1554 left = MAX_LINELEN - header_len;
1557 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1559 while (isspace(*srcp)) {
1562 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1565 /* output as it is if the next word is ASCII string */
1566 if (!is_next_nonascii(srcp)) {
1569 word_len = get_next_word_len(srcp);
1570 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1571 while (word_len > 0) {
1572 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1581 /* don't include parentheses in encoded strings */
1582 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1583 LBREAK_IF_REQUIRED(left < 2, FALSE);
1594 const guchar *p = srcp;
1596 gint out_enc_str_len;
1597 gint mime_block_len;
1598 gboolean cont = FALSE;
1600 while (*p != '\0') {
1601 if (isspace(*p) && !is_next_nonascii(p + 1))
1603 /* don't include parentheses in encoded
1605 if (addr_field && (*p == '(' || *p == ')'))
1608 if (MB_CUR_MAX > 1) {
1609 mb_len = mblen(p, MB_CUR_MAX);
1611 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1617 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1618 out_str = conv_codeset_strdup
1619 (part_str, cur_encoding, out_encoding);
1621 g_warning("conv_encode_header(): code conversion failed\n");
1622 conv_unreadable_8bit(part_str);
1623 out_str = g_strdup(part_str);
1625 out_str_len = strlen(out_str);
1628 out_enc_str_len = B64LEN(out_str_len);
1631 qp_get_q_encoding_len(out_str);
1635 if (mimestr_len + out_enc_str_len <= left) {
1638 } else if (cur_len == 0) {
1639 LBREAK_IF_REQUIRED(1, FALSE);
1648 Xstrndup_a(part_str, srcp, cur_len, );
1649 out_str = conv_codeset_strdup
1650 (part_str, cur_encoding, out_encoding);
1652 g_warning("conv_encode_header(): code conversion failed\n");
1653 conv_unreadable_8bit(part_str);
1654 out_str = g_strdup(part_str);
1656 out_str_len = strlen(out_str);
1659 out_enc_str_len = B64LEN(out_str_len);
1662 qp_get_q_encoding_len(out_str);
1664 Xalloca(enc_str, out_enc_str_len + 1, );
1666 base64_encode(enc_str, out_str, out_str_len);
1668 qp_q_encode(enc_str, out_str);
1672 /* output MIME-encoded string block */
1673 mime_block_len = mimestr_len + strlen(enc_str);
1674 g_snprintf(destp, mime_block_len + 1,
1675 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1676 out_encoding, mimesep_enc, enc_str);
1677 destp += mime_block_len;
1680 left -= mime_block_len;
1683 LBREAK_IF_REQUIRED(cont, FALSE);
1693 #undef LBREAK_IF_REQUIRED