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 || !strlen(cur_locale))
1444 cur_locale = g_getenv("LC_CTYPE");
1445 if (!cur_locale || !strlen(cur_locale))
1446 cur_locale = g_getenv("LANG");
1447 if (!cur_locale || !strlen(cur_locale))
1448 cur_locale = setlocale(LC_CTYPE, NULL);
1450 debug_print("current locale: %s\n",
1451 cur_locale ? cur_locale : "(none)");
1456 void conv_unmime_header_overwrite(gchar *str)
1460 CharSet cur_charset;
1462 g_return_if_fail(str != NULL);
1464 cur_charset = conv_get_current_charset();
1466 if (cur_charset == C_EUC_JP) {
1467 buflen = strlen(str) * 2 + 1;
1468 Xalloca(buf, buflen, return);
1469 conv_anytodisp(buf, buflen, str);
1470 unmime_header(str, buf);
1472 buflen = strlen(str) + 1;
1473 Xalloca(buf, buflen, return);
1474 unmime_header(buf, str);
1475 strncpy2(str, buf, buflen);
1479 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1480 const gchar *charset)
1482 CharSet cur_charset;
1484 cur_charset = conv_get_current_charset();
1486 if (cur_charset == C_EUC_JP) {
1490 buflen = strlen(str) * 2 + 1;
1491 Xalloca(buf, buflen, return);
1492 conv_anytodisp(buf, buflen, str);
1493 unmime_header(outbuf, buf);
1495 unmime_header(outbuf, str);
1498 #define MAX_LINELEN 76
1499 #define MAX_HARD_LINELEN 996
1500 #define MIMESEP_BEGIN "=?"
1501 #define MIMESEP_END "?="
1503 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1505 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1507 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \
1512 if ((cond) && *srcp) { \
1513 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \
1514 if (isspace(*(destp - 1))) \
1516 else if (is_plain_text && isspace(*srcp)) \
1521 left = MAX_LINELEN - 1; \
1527 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1528 gint header_len, gboolean addr_field)
1530 const gchar *cur_encoding;
1531 const gchar *out_encoding;
1535 const guchar *srcp = src;
1536 guchar *destp = dest;
1537 gboolean use_base64;
1539 if (MB_CUR_MAX > 1) {
1541 mimesep_enc = "?B?";
1544 mimesep_enc = "?Q?";
1547 cur_encoding = conv_get_current_charset_str();
1548 if (!strcmp(cur_encoding, CS_US_ASCII))
1549 cur_encoding = CS_ISO_8859_1;
1550 out_encoding = conv_get_outgoing_charset_str();
1551 if (!strcmp(out_encoding, CS_US_ASCII))
1552 out_encoding = CS_ISO_8859_1;
1554 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1555 strlen(mimesep_enc) + strlen(MIMESEP_END);
1557 left = MAX_LINELEN - header_len;
1560 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1562 while (isspace(*srcp)) {
1565 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1568 /* output as it is if the next word is ASCII string */
1569 if (!is_next_nonascii(srcp)) {
1572 word_len = get_next_word_len(srcp);
1573 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1574 while (word_len > 0) {
1575 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1584 /* don't include parentheses in encoded strings */
1585 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1586 LBREAK_IF_REQUIRED(left < 2, FALSE);
1597 const guchar *p = srcp;
1599 gint out_enc_str_len;
1600 gint mime_block_len;
1601 gboolean cont = FALSE;
1603 while (*p != '\0') {
1604 if (isspace(*p) && !is_next_nonascii(p + 1))
1606 /* don't include parentheses in encoded
1608 if (addr_field && (*p == '(' || *p == ')'))
1611 if (MB_CUR_MAX > 1) {
1612 mb_len = mblen(p, MB_CUR_MAX);
1614 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1620 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1621 out_str = conv_codeset_strdup
1622 (part_str, cur_encoding, out_encoding);
1624 g_warning("conv_encode_header(): code conversion failed\n");
1625 conv_unreadable_8bit(part_str);
1626 out_str = g_strdup(part_str);
1628 out_str_len = strlen(out_str);
1631 out_enc_str_len = B64LEN(out_str_len);
1634 qp_get_q_encoding_len(out_str);
1638 if (mimestr_len + out_enc_str_len <= left) {
1641 } else if (cur_len == 0) {
1642 LBREAK_IF_REQUIRED(1, FALSE);
1651 Xstrndup_a(part_str, srcp, cur_len, );
1652 out_str = conv_codeset_strdup
1653 (part_str, cur_encoding, out_encoding);
1655 g_warning("conv_encode_header(): code conversion failed\n");
1656 conv_unreadable_8bit(part_str);
1657 out_str = g_strdup(part_str);
1659 out_str_len = strlen(out_str);
1662 out_enc_str_len = B64LEN(out_str_len);
1665 qp_get_q_encoding_len(out_str);
1667 Xalloca(enc_str, out_enc_str_len + 1, );
1669 base64_encode(enc_str, out_str, out_str_len);
1671 qp_q_encode(enc_str, out_str);
1675 /* output MIME-encoded string block */
1676 mime_block_len = mimestr_len + strlen(enc_str);
1677 g_snprintf(destp, mime_block_len + 1,
1678 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1679 out_encoding, mimesep_enc, enc_str);
1680 destp += mime_block_len;
1683 left -= mime_block_len;
1686 LBREAK_IF_REQUIRED(cont, FALSE);
1696 #undef LBREAK_IF_REQUIRED