2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 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 void conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf)
178 const guchar *in = inbuf;
179 guchar *out = outbuf;
180 JISState state = JIS_ASCII;
182 while (*in != '\0') {
186 } else if (iseuckanji(*in)) {
187 if (iseuckanji(*(in + 1))) {
189 *out++ = *in++ & 0x7f;
190 *out++ = *in++ & 0x7f;
195 if (*in != '\0' && !isascii(*in)) {
200 } else if (iseuchwkana1(*in)) {
202 if (iseuchwkana2(*in)) {
204 *out++ = *in++ & 0x7f;
207 if (*in != '\0' && !isascii(*in)) {
212 } else if (iseucaux(*in)) {
214 if (iseuckanji(*in) && iseuckanji(*(in + 1))) {
216 *out++ = *in++ & 0x7f;
217 *out++ = *in++ & 0x7f;
220 if (*in != '\0' && !isascii(*in)) {
223 if (*in != '\0' && !isascii(*in)) {
240 void conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
242 const guchar *in = inbuf;
243 guchar *out = outbuf;
245 while (*in != '\0') {
248 } else if (issjiskanji1(*in)) {
249 if (issjiskanji2(*(in + 1))) {
251 guchar out2 = *(in + 1);
254 row = out1 < 0xa0 ? 0x70 : 0xb0;
256 out1 = (out1 - row) * 2 - 1;
257 out2 -= out2 > 0x7f ? 0x20 : 0x1f;
259 out1 = (out1 - row) * 2;
263 *out++ = out1 | 0x80;
264 *out++ = out2 | 0x80;
269 if (*in != '\0' && !isascii(*in)) {
274 } else if (issjishwkana(*in)) {
286 void conv_anytoeuc(gchar *outbuf, gint outlen, const gchar *inbuf)
288 switch (conv_guess_ja_encoding(inbuf)) {
290 conv_jistoeuc(outbuf, outlen, inbuf);
293 conv_sjistoeuc(outbuf, outlen, inbuf);
296 strncpy2(outbuf, inbuf, outlen);
301 void conv_anytojis(gchar *outbuf, gint outlen, const gchar *inbuf)
303 switch (conv_guess_ja_encoding(inbuf)) {
305 conv_euctojis(outbuf, outlen, inbuf);
308 strncpy2(outbuf, inbuf, outlen);
313 static gchar valid_eucjp_tbl[][96] = {
314 /* 0xa2a0 - 0xa2ff */
315 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
316 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
317 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
318 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
319 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
320 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 },
322 /* 0xa3a0 - 0xa3ff */
323 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
324 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
325 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
326 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
327 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
328 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 },
330 /* 0xa4a0 - 0xa4ff */
331 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
332 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
333 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
334 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
335 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
336 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
338 /* 0xa5a0 - 0xa5ff */
339 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
340 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
341 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
342 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
343 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
344 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
346 /* 0xa6a0 - 0xa6ff */
347 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
348 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
349 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
350 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
354 /* 0xa7a0 - 0xa7ff */
355 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
356 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
357 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
358 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
359 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
360 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
362 /* 0xa8a0 - 0xa8ff */
363 { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
364 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
365 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
367 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
371 static gboolean isprintableeuckanji(guchar c1, guchar c2)
373 if (c1 <= 0xa0 || c1 >= 0xf5)
375 if (c2 <= 0xa0 || c2 == 0xff)
378 if (c1 >= 0xa9 && c1 <= 0xaf)
381 if (c1 >= 0xa2 && c1 <= 0xa8)
382 return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0];
385 if (c2 >= 0xd4 && c2 <= 0xfe)
387 } else if (c1 == 0xf4) {
388 if (c2 >= 0xa7 && c2 <= 0xfe)
395 void conv_unreadable_eucjp(gchar *str)
397 register guchar *p = str;
401 /* convert CR+LF -> LF */
402 if (*p == '\r' && *(p + 1) == '\n')
403 memmove(p, p + 1, strlen(p));
404 /* printable 7 bit code */
406 } else if (iseuckanji(*p)) {
407 if (isprintableeuckanji(*p, *(p + 1))) {
408 /* printable euc-jp code */
411 /* substitute unprintable code */
420 } else if (iseuchwkana1(*p)) {
421 if (iseuchwkana2(*(p + 1)))
422 /* euc-jp hankaku kana */
426 } else if (iseucaux(*p)) {
427 if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) {
428 /* auxiliary kanji */
433 /* substitute unprintable 1 byte code */
438 void conv_unreadable_8bit(gchar *str)
440 register guchar *p = str;
443 /* convert CR+LF -> LF */
444 if (*p == '\r' && *(p + 1) == '\n')
445 memmove(p, p + 1, strlen(p));
446 else if (!isascii(*p)) *p = SUBST_CHAR;
451 void conv_unreadable_latin(gchar *str)
453 register guchar *p = str;
456 /* convert CR+LF -> LF */
457 if (*p == '\r' && *(p + 1) == '\n')
458 memmove(p, p + 1, strlen(p));
459 else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f)
465 void conv_unreadable_locale(gchar *str)
467 switch (conv_get_current_charset()) {
483 conv_unreadable_latin(str);
486 conv_unreadable_eucjp(str);
495 void conv_mb_alnum(gchar *str)
497 static guchar char_tbl[] = {
499 NCV, ' ', NCV, NCV, ',', '.', NCV, ':',
500 ';', '?', '!', NCV, NCV, NCV, NCV, NCV,
502 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
503 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
505 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV,
506 NCV, NCV, '(', ')', NCV, NCV, '[', ']',
508 '{', '}', NCV, NCV, NCV, NCV, NCV, NCV,
509 NCV, NCV, NCV, NCV, '+', '-', NCV, NCV,
511 NCV, '=', NCV, '<', '>', NCV, NCV, NCV,
512 NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV
515 register guchar *p = str;
522 register guchar ch = *(p + 1);
524 if (ch >= 0xb0 && ch <= 0xfa) {
529 memmove(p, p + 1, len);
535 } else if (*p == 0xa1) {
536 register guchar ch = *(p + 1);
538 if (ch >= 0xa0 && ch <= 0xef &&
539 NCV != char_tbl[ch - 0xa0]) {
540 *p = char_tbl[ch - 0xa0];
543 memmove(p, p + 1, len);
549 } else if (iseuckanji(*p)) {
559 CharSet conv_guess_ja_encoding(const gchar *str)
561 const guchar *p = str;
562 CharSet guessed = C_US_ASCII;
565 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) {
566 if (guessed == C_US_ASCII)
567 return C_ISO_2022_JP;
569 } else if (isascii(*p)) {
571 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) {
572 if (*p >= 0xfd && *p <= 0xfe)
574 else if (guessed == C_SHIFT_JIS) {
575 if ((issjiskanji1(*p) &&
576 issjiskanji2(*(p + 1))) ||
578 guessed = C_SHIFT_JIS;
584 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) {
585 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1)))
586 guessed = C_SHIFT_JIS;
590 } else if (issjishwkana(*p)) {
591 guessed = C_SHIFT_JIS;
601 void conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
603 conv_jistoeuc(outbuf, outlen, inbuf);
604 conv_unreadable_eucjp(outbuf);
607 void conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
609 conv_sjistoeuc(outbuf, outlen, inbuf);
610 conv_unreadable_eucjp(outbuf);
613 void conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
615 strncpy2(outbuf, inbuf, outlen);
616 conv_unreadable_eucjp(outbuf);
619 void conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
621 conv_anytoeuc(outbuf, outlen, inbuf);
622 conv_unreadable_eucjp(outbuf);
625 void conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
627 strncpy2(outbuf, inbuf, outlen);
628 conv_unreadable_8bit(outbuf);
631 void conv_latintodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
633 strncpy2(outbuf, inbuf, outlen);
634 conv_unreadable_latin(outbuf);
637 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf)
639 strncpy2(outbuf, inbuf, outlen);
640 conv_unreadable_locale(outbuf);
643 void conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf)
645 strncpy2(outbuf, inbuf, outlen);
648 CodeConverter *conv_code_converter_new(const gchar *charset)
652 conv = g_new0(CodeConverter, 1);
653 conv->code_conv_func = conv_get_code_conv_func(charset, NULL);
654 conv->charset_str = g_strdup(charset);
655 conv->charset = conv_get_charset_from_str(charset);
660 void conv_code_converter_destroy(CodeConverter *conv)
662 g_free(conv->charset_str);
666 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen,
670 if (conv->code_conv_func != conv_noconv)
671 conv->code_conv_func(outbuf, outlen, inbuf);
675 str = conv_iconv_strdup(inbuf, conv->charset_str, NULL);
679 strncpy2(outbuf, str, outlen);
683 #else /* !HAVE_ICONV */
684 conv->code_conv_func(outbuf, outlen, inbuf);
690 gchar *conv_codeset_strdup(const gchar *inbuf,
691 const gchar *src_code, const gchar *dest_code)
695 CodeConvFunc conv_func;
697 conv_func = conv_get_code_conv_func(src_code, dest_code);
698 if (conv_func != conv_noconv) {
699 len = (strlen(inbuf) + 1) * 3;
701 if (!buf) return NULL;
703 conv_func(buf, len, inbuf);
704 return g_realloc(buf, strlen(buf) + 1);
708 return conv_iconv_strdup(inbuf, src_code, dest_code);
710 return g_strdup(inbuf);
711 #endif /* HAVE_ICONV */
714 CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str,
715 const gchar *dest_charset_str)
717 CodeConvFunc code_conv = conv_noconv;
719 CharSet dest_charset;
721 if (!src_charset_str)
722 src_charset = conv_get_current_charset();
724 src_charset = conv_get_charset_from_str(src_charset_str);
726 /* auto detection mode */
727 if (!src_charset_str && !dest_charset_str) {
728 if (src_charset == C_EUC_JP || src_charset == C_SHIFT_JIS)
729 return conv_anytodisp;
734 dest_charset = conv_get_charset_from_str(dest_charset_str);
736 if (dest_charset == C_US_ASCII)
737 return conv_ustodisp;
738 else if (dest_charset == C_UTF_8 ||
739 (dest_charset == C_AUTO &&
740 conv_get_current_charset() == C_UTF_8))
743 switch (src_charset) {
745 case C_ISO_2022_JP_2:
746 if (dest_charset == C_AUTO &&
747 conv_get_current_charset() == C_EUC_JP)
748 code_conv = conv_jistodisp;
749 else if (dest_charset == C_EUC_JP)
750 code_conv = conv_jistoeuc;
753 if (dest_charset == C_AUTO)
754 code_conv = conv_ustodisp;
770 if (dest_charset == C_AUTO &&
771 (conv_get_current_charset() == src_charset ||
773 code_conv = conv_latintodisp;
776 if (dest_charset == C_AUTO &&
777 conv_get_current_charset() == C_EUC_JP)
778 code_conv = conv_sjistodisp;
779 else if (dest_charset == C_EUC_JP)
780 code_conv = conv_sjistoeuc;
783 if (dest_charset == C_AUTO &&
784 conv_get_current_charset() == C_EUC_JP)
785 code_conv = conv_euctodisp;
786 else if (dest_charset == C_ISO_2022_JP ||
787 dest_charset == C_ISO_2022_JP_2)
788 code_conv = conv_euctojis;
798 gchar *conv_iconv_strdup(const gchar *inbuf,
799 const gchar *src_code, const gchar *dest_code)
802 const gchar *inbuf_p;
813 src_code = conv_get_outgoing_charset_str();
815 dest_code = conv_get_current_charset_str();
817 /* don't convert if current codeset is US-ASCII */
818 if (!strcasecmp(dest_code, CS_US_ASCII))
819 return g_strdup(inbuf);
821 /* don't convert if src and dest codeset are identical */
822 if (!strcasecmp(src_code, dest_code))
823 return g_strdup(inbuf);
825 cd = iconv_open(dest_code, src_code);
826 if (cd == (iconv_t)-1)
830 in_size = strlen(inbuf);
832 out_size = (in_size + 1) * 2;
833 outbuf = g_malloc(out_size);
837 #define EXPAND_BUF() \
839 len = outbuf_p - outbuf; \
841 outbuf = g_realloc(outbuf, out_size); \
842 outbuf_p = outbuf + len; \
843 out_left = out_size - len; \
846 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left,
847 &outbuf_p, &out_left)) < 0) {
848 if (EILSEQ == errno) {
854 *outbuf_p++ = SUBST_CHAR;
856 } else if (EINVAL == errno) {
858 } else if (E2BIG == errno) {
861 g_warning("conv_iconv_strdup(): %s\n",
867 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) < 0) {
868 if (E2BIG == errno) {
871 g_warning("conv_iconv_strdup(): %s\n",
879 len = outbuf_p - outbuf;
880 outbuf = g_realloc(outbuf, len + 1);
887 #endif /* HAVE_ICONV */
889 static const struct {
893 {C_US_ASCII, CS_US_ASCII},
894 {C_US_ASCII, CS_ANSI_X3_4_1968},
897 {C_ISO_8859_1, CS_ISO_8859_1},
898 {C_ISO_8859_2, CS_ISO_8859_2},
899 {C_ISO_8859_3, CS_ISO_8859_3},
900 {C_ISO_8859_4, CS_ISO_8859_4},
901 {C_ISO_8859_5, CS_ISO_8859_5},
902 {C_ISO_8859_6, CS_ISO_8859_6},
903 {C_ISO_8859_7, CS_ISO_8859_7},
904 {C_ISO_8859_8, CS_ISO_8859_8},
905 {C_ISO_8859_9, CS_ISO_8859_9},
906 {C_ISO_8859_10, CS_ISO_8859_10},
907 {C_ISO_8859_11, CS_ISO_8859_11},
908 {C_ISO_8859_13, CS_ISO_8859_13},
909 {C_ISO_8859_14, CS_ISO_8859_14},
910 {C_ISO_8859_15, CS_ISO_8859_15},
911 {C_BALTIC, CS_BALTIC},
912 {C_CP1250, CS_CP1250},
913 {C_CP1251, CS_CP1251},
914 {C_CP1252, CS_CP1252},
915 {C_CP1253, CS_CP1253},
916 {C_CP1254, CS_CP1254},
917 {C_CP1255, CS_CP1255},
918 {C_CP1256, CS_CP1256},
919 {C_CP1257, CS_CP1257},
920 {C_CP1258, CS_CP1258},
921 {C_WINDOWS_1250, CS_WINDOWS_1250},
922 {C_WINDOWS_1251, CS_WINDOWS_1251},
923 {C_WINDOWS_1252, CS_WINDOWS_1252},
924 {C_WINDOWS_1253, CS_WINDOWS_1253},
925 {C_WINDOWS_1254, CS_WINDOWS_1254},
926 {C_WINDOWS_1255, CS_WINDOWS_1255},
927 {C_WINDOWS_1256, CS_WINDOWS_1256},
928 {C_WINDOWS_1257, CS_WINDOWS_1257},
929 {C_WINDOWS_1258, CS_WINDOWS_1258},
930 {C_KOI8_R, CS_KOI8_R},
931 {C_KOI8_T, CS_KOI8_T},
932 {C_KOI8_U, CS_KOI8_U},
933 {C_ISO_2022_JP, CS_ISO_2022_JP},
934 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2},
935 {C_EUC_JP, CS_EUC_JP},
936 {C_EUC_JP, CS_EUCJP},
937 {C_SHIFT_JIS, CS_SHIFT_JIS},
938 {C_SHIFT_JIS, CS_SHIFT__JIS},
939 {C_SHIFT_JIS, CS_SJIS},
940 {C_ISO_2022_KR, CS_ISO_2022_KR},
941 {C_EUC_KR, CS_EUC_KR},
942 {C_ISO_2022_CN, CS_ISO_2022_CN},
943 {C_EUC_CN, CS_EUC_CN},
944 {C_GB2312, CS_GB2312},
946 {C_EUC_TW, CS_EUC_TW},
948 {C_BIG5_HKSCS, CS_BIG5_HKSCS},
949 {C_TIS_620, CS_TIS_620},
950 {C_WINDOWS_874, CS_WINDOWS_874},
951 {C_GEORGIAN_PS, CS_GEORGIAN_PS},
952 {C_TCVN5712_1, CS_TCVN5712_1},
955 static const struct {
960 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP},
961 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP},
962 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP},
963 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP},
964 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP},
965 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP},
966 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP},
967 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR},
968 {"ko_KR" , C_EUC_KR , C_EUC_KR},
969 {"zh_CN.GB2312" , C_GB2312 , C_GB2312},
970 {"zh_CN.GBK" , C_GBK , C_GB2312},
971 {"zh_CN" , C_GB2312 , C_GB2312},
972 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS},
973 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5},
974 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5},
975 {"zh_TW.Big5" , C_BIG5 , C_BIG5},
976 {"zh_TW" , C_BIG5 , C_BIG5},
978 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R},
979 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R},
980 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R},
981 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R},
982 {"tg_TJ" , C_KOI8_T , C_KOI8_T},
983 {"ru_UA" , C_KOI8_U , C_KOI8_U},
984 {"uk_UA" , C_KOI8_U , C_KOI8_U},
986 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251},
987 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251},
989 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255},
991 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
992 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1},
993 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1},
994 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1},
995 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1},
996 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1},
997 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1},
998 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1},
999 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1000 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1},
1001 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1},
1002 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1003 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1},
1004 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1005 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1},
1006 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1007 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1},
1008 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1009 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1},
1010 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1},
1011 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1},
1012 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1},
1013 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1},
1014 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1},
1015 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1},
1016 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1},
1017 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1},
1018 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1},
1019 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1},
1020 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1021 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1},
1022 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1},
1023 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1},
1024 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1},
1025 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1},
1026 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1},
1027 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1},
1028 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1},
1029 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1},
1030 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1},
1031 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1},
1032 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1},
1033 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1},
1034 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1035 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1036 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1},
1037 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1038 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1},
1039 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1040 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1041 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1},
1042 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1},
1043 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1},
1044 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1045 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1},
1046 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1},
1047 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1},
1048 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1},
1049 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1},
1050 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1},
1051 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1},
1052 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1053 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1},
1054 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1055 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1},
1056 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1},
1057 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1},
1058 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1},
1059 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1},
1060 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1},
1061 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1},
1062 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1},
1063 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1},
1064 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1},
1066 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2},
1067 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2},
1068 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2},
1069 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2},
1070 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2},
1071 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2},
1072 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2},
1073 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2},
1075 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5},
1076 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2},
1078 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3},
1080 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4},
1081 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1082 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4},
1083 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13},
1085 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5},
1087 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6},
1088 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6},
1089 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6},
1090 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6},
1091 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6},
1092 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6},
1093 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6},
1094 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6},
1095 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6},
1096 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6},
1097 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6},
1098 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6},
1099 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6},
1100 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6},
1101 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6},
1102 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6},
1103 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6},
1105 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7},
1106 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1107 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8},
1108 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9},
1110 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13},
1111 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13},
1113 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14},
1115 {"ar_IN" , C_UTF_8 , C_UTF_8},
1116 {"en_IN" , C_UTF_8 , C_UTF_8},
1117 {"se_NO" , C_UTF_8 , C_UTF_8},
1118 {"ta_IN" , C_UTF_8 , C_UTF_8},
1119 {"te_IN" , C_UTF_8 , C_UTF_8},
1120 {"ur_PK" , C_UTF_8 , C_UTF_8},
1122 {"th_TH" , C_TIS_620 , C_TIS_620},
1123 /* {"th_TH" , C_WINDOWS_874}, */
1124 /* {"th_TH" , C_ISO_8859_11}, */
1126 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS},
1127 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1},
1129 {"C" , C_US_ASCII , C_US_ASCII},
1130 {"POSIX" , C_US_ASCII , C_US_ASCII},
1131 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII},
1134 static GHashTable *conv_get_charset_to_str_table(void)
1136 static GHashTable *table;
1142 table = g_hash_table_new(NULL, g_direct_equal);
1144 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1145 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset))
1148 (table, GUINT_TO_POINTER(charsets[i].charset),
1156 static GHashTable *conv_get_charset_from_str_table(void)
1158 static GHashTable *table;
1164 table = g_hash_table_new(str_case_hash, str_case_equal);
1166 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) {
1167 g_hash_table_insert(table, charsets[i].name,
1168 GUINT_TO_POINTER(charsets[i].charset));
1174 const gchar *conv_get_charset_str(CharSet charset)
1178 table = conv_get_charset_to_str_table();
1179 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset));
1182 CharSet conv_get_charset_from_str(const gchar *charset)
1186 if (!charset) return C_AUTO;
1188 table = conv_get_charset_from_str_table();
1189 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset));
1192 CharSet conv_get_current_charset(void)
1194 static CharSet cur_charset = -1;
1195 const gchar *cur_locale;
1199 if (cur_charset != -1)
1202 cur_locale = conv_get_current_locale();
1204 cur_charset = C_US_ASCII;
1208 if (strcasestr(cur_locale, "UTF-8")) {
1209 cur_charset = C_UTF_8;
1213 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1214 cur_charset = C_ISO_8859_15;
1218 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1221 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and
1222 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */
1223 if (!strncasecmp(cur_locale, locale_table[i].locale,
1224 strlen(locale_table[i].locale))) {
1225 cur_charset = locale_table[i].charset;
1227 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1228 !strchr(p + 1, '.')) {
1229 if (strlen(cur_locale) == 2 &&
1230 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1231 cur_charset = locale_table[i].charset;
1237 cur_charset = C_AUTO;
1241 const gchar *conv_get_current_charset_str(void)
1243 static const gchar *codeset = NULL;
1246 codeset = conv_get_charset_str(conv_get_current_charset());
1248 return codeset ? codeset : CS_US_ASCII;
1251 CharSet conv_get_outgoing_charset(void)
1253 static CharSet out_charset = -1;
1254 const gchar *cur_locale;
1258 if (out_charset != -1)
1261 cur_locale = conv_get_current_locale();
1263 out_charset = C_AUTO;
1267 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') {
1268 out_charset = C_ISO_8859_15;
1272 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) {
1275 if (!strncasecmp(cur_locale, locale_table[i].locale,
1276 strlen(locale_table[i].locale))) {
1277 out_charset = locale_table[i].out_charset;
1279 } else if ((p = strchr(locale_table[i].locale, '_')) &&
1280 !strchr(p + 1, '.')) {
1281 if (strlen(cur_locale) == 2 &&
1282 !strncasecmp(cur_locale, locale_table[i].locale, 2)) {
1283 out_charset = locale_table[i].out_charset;
1290 /* encoding conversion without iconv() is only supported
1291 on Japanese locale for now */
1292 if (out_charset == C_ISO_2022_JP)
1295 return conv_get_current_charset();
1301 const gchar *conv_get_outgoing_charset_str(void)
1303 CharSet out_charset;
1306 if (prefs_common.outgoing_charset) {
1307 if (!isalpha(prefs_common.outgoing_charset[0])) {
1308 g_free(prefs_common.outgoing_charset);
1309 prefs_common.outgoing_charset = g_strdup(CS_AUTO);
1310 } else if (strcmp(prefs_common.outgoing_charset, CS_AUTO) != 0)
1311 return prefs_common.outgoing_charset;
1314 out_charset = conv_get_outgoing_charset();
1315 str = conv_get_charset_str(out_charset);
1317 return str ? str : CS_US_ASCII;
1320 gboolean conv_is_multibyte_encoding(CharSet encoding)
1328 case C_ISO_2022_JP_2:
1342 const gchar *conv_get_current_locale(void)
1346 cur_locale = g_getenv("LC_ALL");
1347 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE");
1348 if (!cur_locale) cur_locale = g_getenv("LANG");
1349 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL);
1351 debug_print("current locale: %s\n",
1352 cur_locale ? cur_locale : "(none)");
1357 void conv_unmime_header_overwrite(gchar *str)
1361 CharSet cur_charset;
1363 cur_charset = conv_get_current_charset();
1365 if (cur_charset == C_EUC_JP) {
1366 buflen = strlen(str) * 2 + 1;
1367 Xalloca(buf, buflen, return);
1368 conv_anytodisp(buf, buflen, str);
1369 unmime_header(str, buf);
1371 buflen = strlen(str) + 1;
1372 Xalloca(buf, buflen, return);
1373 unmime_header(buf, str);
1374 strncpy2(str, buf, buflen);
1378 void conv_unmime_header(gchar *outbuf, gint outlen, const gchar *str,
1379 const gchar *charset)
1381 CharSet cur_charset;
1383 cur_charset = conv_get_current_charset();
1385 if (cur_charset == C_EUC_JP) {
1389 buflen = strlen(str) * 2 + 1;
1390 Xalloca(buf, buflen, return);
1391 conv_anytodisp(buf, buflen, str);
1392 unmime_header(outbuf, buf);
1394 unmime_header(outbuf, str);
1397 #define MAX_LINELEN 76
1398 #define MAX_HARD_LINELEN 996
1399 #define MIMESEP_BEGIN "=?"
1400 #define MIMESEP_END "?="
1402 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0))
1404 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \
1406 if (len - (destp - dest) < MAX_LINELEN + 2) { \
1411 if ((cond) && *srcp) { \
1412 if (destp > dest && left < MAX_LINELEN - 1) { \
1413 if (isspace(*(destp - 1))) \
1415 else if (is_plain_text && isspace(*srcp)) \
1420 left = MAX_LINELEN - 1; \
1426 void conv_encode_header(gchar *dest, gint len, const gchar *src,
1427 gint header_len, gboolean addr_field)
1429 const gchar *cur_encoding;
1430 const gchar *out_encoding;
1434 const gchar *srcp = src;
1435 gchar *destp = dest;
1436 gboolean use_base64;
1438 if (MB_CUR_MAX > 1) {
1440 mimesep_enc = "?B?";
1443 mimesep_enc = "?Q?";
1446 cur_encoding = conv_get_current_charset_str();
1447 if (!strcmp(cur_encoding, CS_US_ASCII))
1448 cur_encoding = CS_ISO_8859_1;
1449 out_encoding = conv_get_outgoing_charset_str();
1450 if (!strcmp(out_encoding, CS_US_ASCII))
1451 out_encoding = CS_ISO_8859_1;
1453 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) +
1454 strlen(mimesep_enc) + strlen(MIMESEP_END);
1456 left = MAX_LINELEN - header_len;
1459 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1461 while (isspace(*srcp)) {
1464 LBREAK_IF_REQUIRED(left <= 0, TRUE);
1467 /* output as it is if the next word is ASCII string */
1468 if (!is_next_nonascii(srcp)) {
1471 word_len = get_next_word_len(srcp);
1472 LBREAK_IF_REQUIRED(left < word_len, TRUE);
1473 while (word_len > 0) {
1474 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE)
1483 /* don't include parentheses in encoded strings */
1484 if (addr_field && (*srcp == '(' || *srcp == ')')) {
1485 LBREAK_IF_REQUIRED(left < 2, FALSE);
1496 const gchar *p = srcp;
1498 gint out_enc_str_len;
1499 gint mime_block_len;
1500 gboolean cont = FALSE;
1502 while (*p != '\0') {
1503 if (isspace(*p) && !is_next_nonascii(p + 1))
1505 /* don't include parentheses in encoded
1507 if (addr_field && (*p == '(' || *p == ')'))
1510 if (MB_CUR_MAX > 1) {
1511 mb_len = mblen(p, MB_CUR_MAX);
1513 g_warning("conv_encode_header(): invalid multibyte character encountered\n");
1519 Xstrndup_a(part_str, srcp, cur_len + mb_len, );
1520 out_str = conv_codeset_strdup
1521 (part_str, cur_encoding, out_encoding);
1523 g_warning("conv_encode_header(): code conversion failed\n");
1524 conv_unreadable_8bit(part_str);
1525 out_str = g_strdup(part_str);
1527 out_str_len = strlen(out_str);
1530 out_enc_str_len = B64LEN(out_str_len);
1533 qp_get_q_encoding_len(out_str);
1537 if (mimestr_len + out_enc_str_len <= left) {
1540 } else if (cur_len == 0) {
1541 LBREAK_IF_REQUIRED(1, FALSE);
1550 Xstrndup_a(part_str, srcp, cur_len, );
1551 out_str = conv_codeset_strdup
1552 (part_str, cur_encoding, out_encoding);
1554 g_warning("conv_encode_header(): code conversion failed\n");
1555 conv_unreadable_8bit(part_str);
1556 out_str = g_strdup(part_str);
1558 out_str_len = strlen(out_str);
1561 out_enc_str_len = B64LEN(out_str_len);
1564 qp_get_q_encoding_len(out_str);
1566 Xalloca(enc_str, out_enc_str_len + 1, );
1568 base64_encode(enc_str, out_str, out_str_len);
1570 qp_q_encode(enc_str, out_str);
1574 /* output MIME-encoded string block */
1575 mime_block_len = mimestr_len + strlen(enc_str);
1576 g_snprintf(destp, mime_block_len + 1,
1577 MIMESEP_BEGIN "%s%s%s" MIMESEP_END,
1578 out_encoding, mimesep_enc, enc_str);
1579 destp += mime_block_len;
1582 left -= mime_block_len;
1585 LBREAK_IF_REQUIRED(cont, FALSE);
1595 #undef LBREAK_IF_REQUIRED