codeconv.c (47282B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team 4 * 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 3 of the License, or 8 * (at your option) any later version. 9 * 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. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 #include "defs.h" 21 22 #include <glib.h> 23 #include <glib/gi18n.h> 24 #include <string.h> 25 #include <ctype.h> 26 #include <stdlib.h> 27 #include <errno.h> 28 #include <locale.h> 29 30 #include "codeconv.h" 31 #include "unmime.h" 32 #include "quoted-printable.h" 33 #include "utils.h" 34 35 /* For unknown reasons the inconv.m4 macro undefs that macro if no 36 const is needed. This would break the code below so we define it. */ 37 #ifndef ICONV_CONST 38 #define ICONV_CONST 39 #endif 40 41 typedef enum 42 { 43 JIS_ASCII, 44 JIS_KANJI, 45 JIS_HWKANA, 46 JIS_AUXKANJI 47 } JISState; 48 49 #define SUBST_CHAR 0x5f; 50 #define ESC '\033' 51 52 #define iseuckanji(c) \ 53 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe) 54 #define iseuchwkana1(c) \ 55 (((c) & 0xff) == 0x8e) 56 #define iseuchwkana2(c) \ 57 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) 58 #define iseucaux(c) \ 59 (((c) & 0xff) == 0x8f) 60 #define issjiskanji1(c) \ 61 ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \ 62 (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc)) 63 #define issjiskanji2(c) \ 64 ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \ 65 (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc)) 66 #define issjishwkana(c) \ 67 (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) 68 69 #define K_IN() \ 70 if (state != JIS_KANJI) { \ 71 *out++ = ESC; \ 72 *out++ = '$'; \ 73 *out++ = 'B'; \ 74 state = JIS_KANJI; \ 75 } 76 77 #define K_OUT() \ 78 if (state != JIS_ASCII) { \ 79 *out++ = ESC; \ 80 *out++ = '('; \ 81 *out++ = 'B'; \ 82 state = JIS_ASCII; \ 83 } 84 85 #define HW_IN() \ 86 if (state != JIS_HWKANA) { \ 87 *out++ = ESC; \ 88 *out++ = '('; \ 89 *out++ = 'I'; \ 90 state = JIS_HWKANA; \ 91 } 92 93 #define AUX_IN() \ 94 if (state != JIS_AUXKANJI) { \ 95 *out++ = ESC; \ 96 *out++ = '$'; \ 97 *out++ = '('; \ 98 *out++ = 'D'; \ 99 state = JIS_AUXKANJI; \ 100 } 101 102 static CodeConvFunc conv_get_code_conv_func (const gchar *src_charset_str, 103 const gchar *dest_charset_str); 104 105 static gchar *conv_iconv_strdup_with_cd (const gchar *inbuf, 106 iconv_t cd); 107 108 static gchar *conv_iconv_strdup (const gchar *inbuf, 109 const gchar *src_code, 110 const gchar *dest_code); 111 112 static CharSet conv_get_locale_charset (void); 113 static CharSet conv_get_outgoing_charset (void); 114 static CharSet conv_guess_ja_encoding(const gchar *str); 115 static gboolean conv_is_ja_locale (void); 116 117 static gint conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf); 118 static gint conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf); 119 static gint conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf); 120 121 static gint conv_jistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf); 122 static gint conv_sjistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf); 123 static gint conv_euctoutf8(gchar *outbuf, gint outlen, const gchar *inbuf); 124 static gint conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf); 125 126 static gint conv_utf8toeuc(gchar *outbuf, gint outlen, const gchar *inbuf); 127 static gint conv_utf8tojis(gchar *outbuf, gint outlen, const gchar *inbuf); 128 129 static void conv_unreadable_8bit(gchar *str); 130 131 static gint conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf); 132 static gint conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf); 133 static gint conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf); 134 135 static gint conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf); 136 static gint conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf); 137 static gint conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf); 138 139 static gboolean codeconv_strict_mode = FALSE; 140 static gboolean codeconv_allow_jisx0201_kana = FALSE; 141 static gboolean codeconv_broken_are_utf8 = FALSE; 142 143 void codeconv_set_strict(gboolean mode) 144 { 145 codeconv_strict_mode = mode; 146 } 147 148 void codeconv_set_allow_jisx0201_kana(gboolean allow) 149 { 150 codeconv_allow_jisx0201_kana = allow; 151 } 152 153 void codeconv_set_broken_are_utf8(gboolean are) 154 { 155 codeconv_broken_are_utf8 = are; 156 } 157 158 static gint conv_jistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf) 159 { 160 const guchar *in = inbuf; 161 gchar *out = outbuf; 162 JISState state = JIS_ASCII; 163 164 cm_return_val_if_fail(outbuf != NULL, 0); 165 166 /* 167 * Loop outputs up to 3 bytes in each pass (aux kanji) and we 168 * need 1 byte to terminate the output 169 */ 170 while (*in != '\0' && (out - outbuf) < outlen - 4) { 171 if (*in == ESC) { 172 in++; 173 if (*in == '$') { 174 if (*(in + 1) == '@' || *(in + 1) == 'B') { 175 state = JIS_KANJI; 176 in += 2; 177 } else if (*(in + 1) == '(' && 178 *(in + 2) == 'D') { 179 state = JIS_AUXKANJI; 180 in += 3; 181 } else { 182 /* unknown escape sequence */ 183 state = JIS_ASCII; 184 } 185 } else if (*in == '(') { 186 if (*(in + 1) == 'B' || *(in + 1) == 'J') { 187 state = JIS_ASCII; 188 in += 2; 189 } else if (*(in + 1) == 'I') { 190 state = JIS_HWKANA; 191 in += 2; 192 } else { 193 /* unknown escape sequence */ 194 state = JIS_ASCII; 195 } 196 } else { 197 /* unknown escape sequence */ 198 state = JIS_ASCII; 199 } 200 } else if (*in == 0x0e) { 201 state = JIS_HWKANA; 202 in++; 203 } else if (*in == 0x0f) { 204 state = JIS_ASCII; 205 in++; 206 } else { 207 switch (state) { 208 case JIS_ASCII: 209 *out++ = *in++; 210 break; 211 case JIS_KANJI: 212 *out++ = *in++ | 0x80; 213 if (*in == '\0') break; 214 *out++ = *in++ | 0x80; 215 break; 216 case JIS_HWKANA: 217 *out++ = 0x8e; 218 *out++ = *in++ | 0x80; 219 break; 220 case JIS_AUXKANJI: 221 *out++ = 0x8f; 222 *out++ = *in++ | 0x80; 223 if (*in == '\0') break; 224 *out++ = *in++ | 0x80; 225 break; 226 } 227 } 228 } 229 230 *out = '\0'; 231 return 0; 232 } 233 234 #define JIS_HWDAKUTEN 0x5e 235 #define JIS_HWHANDAKUTEN 0x5f 236 237 static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym) 238 { 239 static guint16 h2z_tbl[] = { 240 /* 0x20 - 0x2f */ 241 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521, 242 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543, 243 /* 0x30 - 0x3f */ 244 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d, 245 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d, 246 /* 0x40 - 0x4f */ 247 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c, 248 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e, 249 /* 0x50 - 0x5f */ 250 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569, 251 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c 252 }; 253 254 static guint16 dakuten_tbl[] = { 255 /* 0x30 - 0x3f */ 256 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e, 257 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e, 258 /* 0x40 - 0x4f */ 259 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000, 260 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000 261 }; 262 263 static guint16 handakuten_tbl[] = { 264 /* 0x4a - 0x4e */ 265 0x2551, 0x2554, 0x2557, 0x255a, 0x255d 266 }; 267 268 guint16 out_code; 269 270 cm_return_val_if_fail(outbuf != NULL, 0); 271 272 jis_code &= 0x7f; 273 sound_sym &= 0x7f; 274 275 if (jis_code < 0x21 || jis_code > 0x5f) 276 return 0; 277 278 if (sound_sym == JIS_HWDAKUTEN && 279 jis_code >= 0x36 && jis_code <= 0x4e) { 280 out_code = dakuten_tbl[jis_code - 0x30]; 281 if (out_code != 0) { 282 *outbuf = out_code >> 8; 283 *(outbuf + 1) = out_code & 0xff; 284 return 2; 285 } 286 } 287 288 if (sound_sym == JIS_HWHANDAKUTEN && 289 jis_code >= 0x4a && jis_code <= 0x4e) { 290 out_code = handakuten_tbl[jis_code - 0x4a]; 291 *outbuf = out_code >> 8; 292 *(outbuf + 1) = out_code & 0xff; 293 return 2; 294 } 295 296 out_code = h2z_tbl[jis_code - 0x20]; 297 *outbuf = out_code >> 8; 298 *(outbuf + 1) = out_code & 0xff; 299 return 1; 300 } 301 302 static gint conv_euctojis(gchar *outbuf, gint outlen, const gchar *inbuf) 303 { 304 const guchar *in = inbuf; 305 gchar *out = outbuf; 306 JISState state = JIS_ASCII; 307 308 cm_return_val_if_fail(outbuf != NULL, 0); 309 310 /* 311 * Loop outputs up to 6 bytes in each pass (aux shift + aux 312 * kanji) and we need up to 4 bytes to terminate the output 313 * (ASCII shift + null) 314 */ 315 while (*in != '\0' && (out - outbuf) < outlen - 10) { 316 if (IS_ASCII(*in)) { 317 K_OUT(); 318 *out++ = *in++; 319 } else if (iseuckanji(*in)) { 320 if (iseuckanji(*(in + 1))) { 321 K_IN(); 322 *out++ = *in++ & 0x7f; 323 *out++ = *in++ & 0x7f; 324 } else { 325 K_OUT(); 326 *out++ = SUBST_CHAR; 327 in++; 328 if (*in != '\0' && !IS_ASCII(*in)) { 329 *out++ = SUBST_CHAR; 330 in++; 331 } 332 } 333 } else if (iseuchwkana1(*in)) { 334 if (iseuchwkana2(*(in + 1))) { 335 if (codeconv_allow_jisx0201_kana) { 336 HW_IN(); 337 in++; 338 *out++ = *in++ & 0x7f; 339 } else { 340 guchar jis_ch[2]; 341 gint len; 342 343 if (iseuchwkana1(*(in + 2)) && 344 iseuchwkana2(*(in + 3))) 345 len = conv_jis_hantozen 346 (jis_ch, 347 *(in + 1), *(in + 3)); 348 else 349 len = conv_jis_hantozen 350 (jis_ch, 351 *(in + 1), '\0'); 352 if (len == 0) 353 in += 2; 354 else { 355 K_IN(); 356 in += len * 2; 357 *out++ = jis_ch[0]; 358 *out++ = jis_ch[1]; 359 } 360 } 361 } else { 362 K_OUT(); 363 in++; 364 if (*in != '\0' && !IS_ASCII(*in)) { 365 *out++ = SUBST_CHAR; 366 in++; 367 } 368 } 369 } else if (iseucaux(*in)) { 370 in++; 371 if (iseuckanji(*in) && iseuckanji(*(in + 1))) { 372 AUX_IN(); 373 *out++ = *in++ & 0x7f; 374 *out++ = *in++ & 0x7f; 375 } else { 376 K_OUT(); 377 if (*in != '\0' && !IS_ASCII(*in)) { 378 *out++ = SUBST_CHAR; 379 in++; 380 if (*in != '\0' && !IS_ASCII(*in)) { 381 *out++ = SUBST_CHAR; 382 in++; 383 } 384 } 385 } 386 } else { 387 K_OUT(); 388 *out++ = SUBST_CHAR; 389 in++; 390 } 391 } 392 393 K_OUT(); 394 *out = '\0'; 395 return 0; 396 } 397 398 static gint conv_sjistoeuc(gchar *outbuf, gint outlen, const gchar *inbuf) 399 { 400 const guchar *in = inbuf; 401 gchar *out = outbuf; 402 403 cm_return_val_if_fail(outbuf != NULL, 0); 404 405 /* 406 * Loop outputs up to 2 bytes in each pass and we need 1 byte 407 * to terminate the output 408 */ 409 while (*in != '\0' && (out - outbuf) < outlen - 3) { 410 if (IS_ASCII(*in)) { 411 *out++ = *in++; 412 } else if (issjiskanji1(*in)) { 413 if (issjiskanji2(*(in + 1))) { 414 guchar out1 = *in; 415 guchar out2 = *(in + 1); 416 guchar row; 417 418 row = out1 < 0xa0 ? 0x70 : 0xb0; 419 if (out2 < 0x9f) { 420 out1 = (out1 - row) * 2 - 1; 421 out2 -= out2 > 0x7f ? 0x20 : 0x1f; 422 } else { 423 out1 = (out1 - row) * 2; 424 out2 -= 0x7e; 425 } 426 427 *out++ = out1 | 0x80; 428 *out++ = out2 | 0x80; 429 in += 2; 430 } else { 431 *out++ = SUBST_CHAR; 432 in++; 433 if (*in != '\0' && !IS_ASCII(*in)) { 434 *out++ = SUBST_CHAR; 435 in++; 436 } 437 } 438 } else if (issjishwkana(*in)) { 439 *out++ = 0x8e; 440 *out++ = *in++; 441 } else { 442 *out++ = SUBST_CHAR; 443 in++; 444 } 445 } 446 447 *out = '\0'; 448 return 0; 449 } 450 451 static gint conv_jistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf) 452 { 453 gchar *eucstr; 454 455 cm_return_val_if_fail(inbuf != NULL, 0); 456 cm_return_val_if_fail(outbuf != NULL, 0); 457 458 Xalloca(eucstr, outlen, return -1); 459 460 if (conv_jistoeuc(eucstr, outlen, inbuf) <0) 461 return -1; 462 if (conv_euctoutf8(outbuf, outlen, eucstr) < 0) 463 return -1; 464 return 0; 465 } 466 467 static gint conv_sjistoutf8(gchar *outbuf, gint outlen, const gchar *inbuf) 468 { 469 gchar *tmpstr; 470 471 cm_return_val_if_fail(inbuf != NULL, 0); 472 cm_return_val_if_fail(outbuf != NULL, 0); 473 474 tmpstr = conv_iconv_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8); 475 if (tmpstr) { 476 strncpy2(outbuf, tmpstr, outlen); 477 g_free(tmpstr); 478 return 0; 479 } else { 480 strncpy2(outbuf, inbuf, outlen); 481 return -1; 482 } 483 } 484 485 static gint conv_euctoutf8(gchar *outbuf, gint outlen, const gchar *inbuf) 486 { 487 static iconv_t cd = (iconv_t)-1; 488 static gboolean iconv_ok = TRUE; 489 gchar *tmpstr; 490 491 cm_return_val_if_fail(inbuf != NULL, 0); 492 cm_return_val_if_fail(outbuf != NULL, 0); 493 494 if (cd == (iconv_t)-1) { 495 if (!iconv_ok) { 496 strncpy2(outbuf, inbuf, outlen); 497 return -1; 498 } 499 cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS); 500 if (cd == (iconv_t)-1) { 501 cd = iconv_open(CS_UTF_8, CS_EUC_JP); 502 if (cd == (iconv_t)-1) { 503 g_warning("conv_euctoutf8(): %s", 504 g_strerror(errno)); 505 iconv_ok = FALSE; 506 strncpy2(outbuf, inbuf, outlen); 507 return -1; 508 } 509 } 510 } 511 512 tmpstr = conv_iconv_strdup_with_cd(inbuf, cd); 513 if (tmpstr) { 514 strncpy2(outbuf, tmpstr, outlen); 515 g_free(tmpstr); 516 return 0; 517 } else { 518 strncpy2(outbuf, inbuf, outlen); 519 return -1; 520 } 521 } 522 523 static gint conv_anytoutf8(gchar *outbuf, gint outlen, const gchar *inbuf) 524 { 525 gint r = -1; 526 527 cm_return_val_if_fail(inbuf != NULL, 0); 528 cm_return_val_if_fail(outbuf != NULL, 0); 529 530 switch (conv_guess_ja_encoding(inbuf)) { 531 case C_ISO_2022_JP: 532 r = conv_jistoutf8(outbuf, outlen, inbuf); 533 break; 534 case C_SHIFT_JIS: 535 r = conv_sjistoutf8(outbuf, outlen, inbuf); 536 break; 537 case C_EUC_JP: 538 r = conv_euctoutf8(outbuf, outlen, inbuf); 539 break; 540 default: 541 r = 0; 542 strncpy2(outbuf, inbuf, outlen); 543 break; 544 } 545 546 return r; 547 } 548 549 static gint conv_utf8toeuc(gchar *outbuf, gint outlen, const gchar *inbuf) 550 { 551 static iconv_t cd = (iconv_t)-1; 552 static gboolean iconv_ok = TRUE; 553 gchar *tmpstr; 554 555 cm_return_val_if_fail(inbuf != NULL, 0); 556 cm_return_val_if_fail(outbuf != NULL, 0); 557 558 if (cd == (iconv_t)-1) { 559 if (!iconv_ok) { 560 strncpy2(outbuf, inbuf, outlen); 561 return -1; 562 } 563 cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8); 564 if (cd == (iconv_t)-1) { 565 cd = iconv_open(CS_EUC_JP, CS_UTF_8); 566 if (cd == (iconv_t)-1) { 567 g_warning("conv_utf8toeuc(): %s", 568 g_strerror(errno)); 569 iconv_ok = FALSE; 570 strncpy2(outbuf, inbuf, outlen); 571 return -1; 572 } 573 } 574 } 575 576 tmpstr = conv_iconv_strdup_with_cd(inbuf, cd); 577 if (tmpstr) { 578 strncpy2(outbuf, tmpstr, outlen); 579 g_free(tmpstr); 580 return 0; 581 } else { 582 strncpy2(outbuf, inbuf, outlen); 583 return -1; 584 } 585 } 586 587 static gint conv_utf8tojis(gchar *outbuf, gint outlen, const gchar *inbuf) 588 { 589 gchar *eucstr; 590 591 cm_return_val_if_fail(inbuf != NULL, 0); 592 cm_return_val_if_fail(outbuf != NULL, 0); 593 594 Xalloca(eucstr, outlen, return -1); 595 596 if (conv_utf8toeuc(eucstr, outlen, inbuf) < 0) 597 return -1; 598 if (conv_euctojis(outbuf, outlen, eucstr) < 0) 599 return -1; 600 601 return 0; 602 } 603 604 static void conv_unreadable_8bit(gchar *str) 605 { 606 register guchar *p = str; 607 608 while (*p != '\0') { 609 /* convert CR+LF -> LF */ 610 if (*p == '\r' && *(p + 1) == '\n') 611 memmove(p, p + 1, strlen(p)); 612 else if (!IS_ASCII(*p)) *p = SUBST_CHAR; 613 p++; 614 } 615 } 616 617 static CharSet conv_guess_ja_encoding(const gchar *str) 618 { 619 const guchar *p = str; 620 CharSet guessed = C_US_ASCII; 621 622 while (*p != '\0') { 623 if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) { 624 if (guessed == C_US_ASCII) 625 return C_ISO_2022_JP; 626 p += 2; 627 } else if (IS_ASCII(*p)) { 628 p++; 629 } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) { 630 if (*p >= 0xfd && *p <= 0xfe) 631 return C_EUC_JP; 632 else if (guessed == C_SHIFT_JIS) { 633 if ((issjiskanji1(*p) && 634 issjiskanji2(*(p + 1))) || 635 issjishwkana(*p)) 636 guessed = C_SHIFT_JIS; 637 else 638 guessed = C_EUC_JP; 639 } else 640 guessed = C_EUC_JP; 641 p += 2; 642 } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) { 643 if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1))) 644 guessed = C_SHIFT_JIS; 645 else 646 return C_SHIFT_JIS; 647 p += 2; 648 } else if (issjishwkana(*p)) { 649 guessed = C_SHIFT_JIS; 650 p++; 651 } else { 652 p++; 653 } 654 } 655 656 return guessed; 657 } 658 659 static gint conv_jistodisp(gchar *outbuf, gint outlen, const gchar *inbuf) 660 { 661 cm_return_val_if_fail(inbuf != NULL, 0); 662 cm_return_val_if_fail(outbuf != NULL, 0); 663 664 return conv_jistoutf8(outbuf, outlen, inbuf); 665 } 666 667 static gint conv_sjistodisp(gchar *outbuf, gint outlen, const gchar *inbuf) 668 { 669 cm_return_val_if_fail(inbuf != NULL, 0); 670 cm_return_val_if_fail(outbuf != NULL, 0); 671 672 return conv_sjistoutf8(outbuf, outlen, inbuf); 673 } 674 675 static gint conv_euctodisp(gchar *outbuf, gint outlen, const gchar *inbuf) 676 { 677 cm_return_val_if_fail(inbuf != NULL, 0); 678 cm_return_val_if_fail(outbuf != NULL, 0); 679 680 return conv_euctoutf8(outbuf, outlen, inbuf); 681 } 682 683 void conv_utf8todisp(gchar *outbuf, gint outlen, const gchar *inbuf) 684 { 685 cm_return_if_fail(inbuf != NULL); 686 cm_return_if_fail(outbuf != NULL); 687 688 if (g_utf8_validate(inbuf, -1, NULL) == TRUE) 689 strncpy2(outbuf, inbuf, outlen); 690 else 691 conv_ustodisp(outbuf, outlen, inbuf); 692 } 693 694 static gint conv_anytodisp(gchar *outbuf, gint outlen, const gchar *inbuf) 695 { 696 gint r = 0; 697 698 cm_return_val_if_fail(inbuf != NULL, 0); 699 cm_return_val_if_fail(outbuf != NULL, 0); 700 701 if (conv_anytoutf8(outbuf, outlen, inbuf) < 0) 702 r = -1; 703 if (g_utf8_validate(outbuf, -1, NULL) != TRUE) 704 conv_unreadable_8bit(outbuf); 705 return r; 706 } 707 708 static gint conv_ustodisp(gchar *outbuf, gint outlen, const gchar *inbuf) 709 { 710 cm_return_val_if_fail(inbuf != NULL, 0); 711 cm_return_val_if_fail(outbuf != NULL, 0); 712 713 strncpy2(outbuf, inbuf, outlen); 714 conv_unreadable_8bit(outbuf); 715 716 return 0; 717 } 718 719 void conv_localetodisp(gchar *outbuf, gint outlen, const gchar *inbuf) 720 { 721 gchar *tmpstr; 722 723 cm_return_if_fail(inbuf != NULL); 724 cm_return_if_fail(outbuf != NULL); 725 726 codeconv_set_strict(TRUE); 727 tmpstr = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(), 728 CS_INTERNAL); 729 codeconv_set_strict(FALSE); 730 if (tmpstr && g_utf8_validate(tmpstr, -1, NULL)) { 731 strncpy2(outbuf, tmpstr, outlen); 732 g_free(tmpstr); 733 return; 734 } else if (tmpstr && !g_utf8_validate(tmpstr, -1, NULL)) { 735 g_free(tmpstr); 736 codeconv_set_strict(TRUE); 737 tmpstr = conv_iconv_strdup(inbuf, 738 conv_get_locale_charset_str_no_utf8(), 739 CS_INTERNAL); 740 codeconv_set_strict(FALSE); 741 } 742 if (tmpstr && g_utf8_validate(tmpstr, -1, NULL)) { 743 strncpy2(outbuf, tmpstr, outlen); 744 g_free(tmpstr); 745 return; 746 } else { 747 g_free(tmpstr); 748 conv_utf8todisp(outbuf, outlen, inbuf); 749 } 750 } 751 752 static gint conv_noconv(gchar *outbuf, gint outlen, const gchar *inbuf) 753 { 754 cm_return_val_if_fail(inbuf != NULL, 0); 755 cm_return_val_if_fail(outbuf != NULL, 0); 756 757 strncpy2(outbuf, inbuf, outlen); 758 return 0; 759 } 760 761 static const gchar * 762 conv_get_fallback_for_private_encoding(const gchar *encoding) 763 { 764 if (encoding) { 765 if ((encoding[0] == 'X' || encoding[0] == 'x') && 766 encoding[1] == '-') { 767 if (!g_ascii_strcasecmp(encoding, CS_X_MACCYR)) 768 return CS_MACCYR; 769 if (!g_ascii_strcasecmp(encoding, CS_X_GBK)) 770 return CS_GBK; 771 } 772 else if(!g_ascii_strcasecmp(encoding, CS_ISO_8859_8_I)) { 773 /* 774 * ISO-8859-8-I is a variant which fully 775 * agrees with ISO-8859-8 on character 776 * codings, and differs only in directionality 777 * implications, which are ignored here 778 * anyway; and is not recognized by iconv 779 */ 780 return CS_ISO_8859_8; 781 } 782 } 783 784 return encoding; 785 } 786 787 CodeConverter *conv_code_converter_new(const gchar *src_charset) 788 { 789 CodeConverter *conv; 790 791 src_charset = conv_get_fallback_for_private_encoding(src_charset); 792 793 conv = g_new0(CodeConverter, 1); 794 conv->code_conv_func = conv_get_code_conv_func(src_charset, NULL); 795 conv->charset_str = g_strdup(src_charset); 796 conv->charset = conv_get_charset_from_str(src_charset); 797 798 return conv; 799 } 800 801 void conv_code_converter_destroy(CodeConverter *conv) 802 { 803 g_free(conv->charset_str); 804 g_free(conv); 805 } 806 807 gint conv_convert(CodeConverter *conv, gchar *outbuf, gint outlen, 808 const gchar *inbuf) 809 { 810 cm_return_val_if_fail(inbuf != NULL, -1); 811 cm_return_val_if_fail(outbuf != NULL, -1); 812 813 if (conv->code_conv_func != conv_noconv) 814 return conv->code_conv_func(outbuf, outlen, inbuf); 815 else { 816 gchar *str; 817 818 str = conv_iconv_strdup(inbuf, conv->charset_str, NULL); 819 if (!str) 820 return -1; 821 else { 822 strncpy2(outbuf, str, outlen); 823 g_free(str); 824 } 825 } 826 827 return 0; 828 } 829 830 gchar *conv_codeset_strdup(const gchar *inbuf, 831 const gchar *src_code, const gchar *dest_code) 832 { 833 gchar *buf; 834 size_t len; 835 CodeConvFunc conv_func; 836 837 cm_return_val_if_fail(inbuf != NULL, NULL); 838 839 if (!g_strcmp0(src_code, dest_code)) { 840 CharSet dest_charset = conv_get_charset_from_str(dest_code); 841 if (codeconv_strict_mode && dest_charset == C_UTF_8) { 842 /* ensure valid UTF-8 if target is UTF-8 */ 843 if (!g_utf8_validate(inbuf, -1, NULL)) { 844 return NULL; 845 } 846 } 847 /* otherwise, try for a lucky day */ 848 return g_strdup(inbuf); 849 } 850 851 src_code = conv_get_fallback_for_private_encoding(src_code); 852 conv_func = conv_get_code_conv_func(src_code, dest_code); 853 if (conv_func == conv_ustodisp 854 && codeconv_strict_mode 855 && !is_ascii_str(inbuf)) 856 return NULL; 857 858 if (conv_func != conv_noconv) { 859 len = (strlen(inbuf) + 1) * 3; 860 buf = g_malloc(len); 861 862 if (conv_func(buf, len, inbuf) == 0 || !codeconv_strict_mode) 863 return g_realloc(buf, strlen(buf) + 1); 864 else { 865 g_free(buf); 866 return NULL; 867 } 868 } 869 870 return conv_iconv_strdup(inbuf, src_code, dest_code); 871 } 872 873 static CodeConvFunc conv_get_code_conv_func(const gchar *src_charset_str, 874 const gchar *dest_charset_str) 875 { 876 CodeConvFunc code_conv = conv_noconv; 877 CharSet src_charset; 878 CharSet dest_charset; 879 880 if (!src_charset_str) 881 src_charset = conv_get_locale_charset(); 882 else 883 src_charset = conv_get_charset_from_str(src_charset_str); 884 885 /* auto detection mode */ 886 if (!src_charset_str && !dest_charset_str) { 887 if (conv_is_ja_locale()) 888 return conv_anytodisp; 889 else 890 return conv_noconv; 891 } 892 893 dest_charset = conv_get_charset_from_str(dest_charset_str); 894 895 if (dest_charset == C_US_ASCII) 896 return conv_ustodisp; 897 898 switch (src_charset) { 899 case C_US_ASCII: 900 case C_ISO_8859_1: 901 case C_ISO_8859_2: 902 case C_ISO_8859_3: 903 case C_ISO_8859_4: 904 case C_ISO_8859_5: 905 case C_ISO_8859_6: 906 case C_ISO_8859_7: 907 case C_ISO_8859_8: 908 case C_ISO_8859_9: 909 case C_ISO_8859_10: 910 case C_ISO_8859_11: 911 case C_ISO_8859_13: 912 case C_ISO_8859_14: 913 case C_ISO_8859_15: 914 break; 915 case C_ISO_2022_JP: 916 case C_ISO_2022_JP_2: 917 case C_ISO_2022_JP_3: 918 if (dest_charset == C_AUTO) 919 code_conv = conv_jistodisp; 920 else if (dest_charset == C_EUC_JP) 921 code_conv = conv_jistoeuc; 922 else if (dest_charset == C_UTF_8) 923 code_conv = conv_jistoutf8; 924 break; 925 case C_SHIFT_JIS: 926 if (dest_charset == C_AUTO) 927 code_conv = conv_sjistodisp; 928 else if (dest_charset == C_EUC_JP) 929 code_conv = conv_sjistoeuc; 930 else if (dest_charset == C_UTF_8) 931 code_conv = conv_sjistoutf8; 932 break; 933 case C_EUC_JP: 934 if (dest_charset == C_AUTO) 935 code_conv = conv_euctodisp; 936 else if (dest_charset == C_ISO_2022_JP || 937 dest_charset == C_ISO_2022_JP_2 || 938 dest_charset == C_ISO_2022_JP_3) 939 code_conv = conv_euctojis; 940 else if (dest_charset == C_UTF_8) 941 code_conv = conv_euctoutf8; 942 break; 943 case C_UTF_8: 944 if (dest_charset == C_EUC_JP) 945 code_conv = conv_utf8toeuc; 946 else if (dest_charset == C_ISO_2022_JP || 947 dest_charset == C_ISO_2022_JP_2 || 948 dest_charset == C_ISO_2022_JP_3) 949 code_conv = conv_utf8tojis; 950 break; 951 default: 952 break; 953 } 954 955 return code_conv; 956 } 957 958 static gchar *conv_iconv_strdup(const gchar *inbuf, 959 const gchar *src_code, const gchar *dest_code) 960 { 961 iconv_t cd; 962 gchar *outbuf; 963 964 cm_return_val_if_fail(inbuf != NULL, NULL); 965 966 if (!src_code && !dest_code && 967 g_utf8_validate(inbuf, -1, NULL)) 968 return g_strdup(inbuf); 969 970 if (!src_code) 971 src_code = conv_get_outgoing_charset_str(); 972 if (!dest_code) 973 dest_code = CS_INTERNAL; 974 975 /* don't convert if src and dest codeset are identical */ 976 if (!strcasecmp(src_code, dest_code)) 977 return g_strdup(inbuf); 978 979 /* don't convert if dest codeset is US-ASCII */ 980 if (!strcasecmp(src_code, CS_US_ASCII)) 981 return g_strdup(inbuf); 982 983 /* don't convert if dest codeset is US-ASCII */ 984 if (!strcasecmp(dest_code, CS_US_ASCII)) 985 return g_strdup(inbuf); 986 987 cd = iconv_open(dest_code, src_code); 988 if (cd == (iconv_t)-1) 989 return NULL; 990 991 outbuf = conv_iconv_strdup_with_cd(inbuf, cd); 992 993 iconv_close(cd); 994 995 return outbuf; 996 } 997 998 gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd) 999 { 1000 const gchar *inbuf_p; 1001 gchar *outbuf; 1002 gchar *outbuf_p; 1003 size_t in_size; 1004 size_t in_left; 1005 size_t out_size; 1006 size_t out_left; 1007 size_t n_conv; 1008 size_t len; 1009 1010 cm_return_val_if_fail(inbuf != NULL, NULL); 1011 1012 inbuf_p = inbuf; 1013 in_size = strlen(inbuf); 1014 in_left = in_size; 1015 out_size = (in_size + 1) * 2; 1016 outbuf = g_malloc(out_size); 1017 outbuf_p = outbuf; 1018 out_left = out_size; 1019 1020 #define EXPAND_BUF() \ 1021 { \ 1022 len = outbuf_p - outbuf; \ 1023 out_size *= 2; \ 1024 outbuf = g_realloc(outbuf, out_size); \ 1025 outbuf_p = outbuf + len; \ 1026 out_left = out_size - len; \ 1027 } 1028 1029 while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left, 1030 &outbuf_p, &out_left)) == (size_t)-1) { 1031 if (EILSEQ == errno) { 1032 if (codeconv_strict_mode) { 1033 g_free(outbuf); 1034 return NULL; 1035 } 1036 //g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno)); 1037 inbuf_p++; 1038 in_left--; 1039 if (out_left == 0) { 1040 EXPAND_BUF(); 1041 } 1042 *outbuf_p++ = SUBST_CHAR; 1043 out_left--; 1044 } else if (EINVAL == errno) { 1045 break; 1046 } else if (E2BIG == errno) { 1047 EXPAND_BUF(); 1048 } else { 1049 g_warning("conv_iconv_strdup(): %s", 1050 g_strerror(errno)); 1051 break; 1052 } 1053 } 1054 1055 while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) == 1056 (size_t)-1) { 1057 if (E2BIG == errno) { 1058 EXPAND_BUF(); 1059 } else { 1060 g_warning("conv_iconv_strdup(): %s", 1061 g_strerror(errno)); 1062 break; 1063 } 1064 } 1065 1066 #undef EXPAND_BUF 1067 1068 len = outbuf_p - outbuf; 1069 outbuf = g_realloc(outbuf, len + 1); 1070 outbuf[len] = '\0'; 1071 1072 return outbuf; 1073 } 1074 1075 static const struct { 1076 CharSet charset; 1077 gchar *const name; 1078 } charsets[] = { 1079 {C_US_ASCII, CS_US_ASCII}, 1080 {C_US_ASCII, CS_ANSI_X3_4_1968}, 1081 {C_UTF_8, CS_UTF_8}, 1082 {C_UTF_7, CS_UTF_7}, 1083 {C_ISO_8859_1, CS_ISO_8859_1}, 1084 {C_ISO_8859_2, CS_ISO_8859_2}, 1085 {C_ISO_8859_3, CS_ISO_8859_3}, 1086 {C_ISO_8859_4, CS_ISO_8859_4}, 1087 {C_ISO_8859_5, CS_ISO_8859_5}, 1088 {C_ISO_8859_6, CS_ISO_8859_6}, 1089 {C_ISO_8859_7, CS_ISO_8859_7}, 1090 {C_ISO_8859_8, CS_ISO_8859_8}, 1091 {C_ISO_8859_9, CS_ISO_8859_9}, 1092 {C_ISO_8859_10, CS_ISO_8859_10}, 1093 {C_ISO_8859_11, CS_ISO_8859_11}, 1094 {C_ISO_8859_13, CS_ISO_8859_13}, 1095 {C_ISO_8859_14, CS_ISO_8859_14}, 1096 {C_ISO_8859_15, CS_ISO_8859_15}, 1097 {C_BALTIC, CS_BALTIC}, 1098 {C_CP1250, CS_CP1250}, 1099 {C_CP1251, CS_CP1251}, 1100 {C_CP1252, CS_CP1252}, 1101 {C_CP1253, CS_CP1253}, 1102 {C_CP1254, CS_CP1254}, 1103 {C_CP1255, CS_CP1255}, 1104 {C_CP1256, CS_CP1256}, 1105 {C_CP1257, CS_CP1257}, 1106 {C_CP1258, CS_CP1258}, 1107 {C_WINDOWS_1250, CS_WINDOWS_1250}, 1108 {C_WINDOWS_1251, CS_WINDOWS_1251}, 1109 {C_WINDOWS_1252, CS_WINDOWS_1252}, 1110 {C_WINDOWS_1253, CS_WINDOWS_1253}, 1111 {C_WINDOWS_1254, CS_WINDOWS_1254}, 1112 {C_WINDOWS_1255, CS_WINDOWS_1255}, 1113 {C_WINDOWS_1256, CS_WINDOWS_1256}, 1114 {C_WINDOWS_1257, CS_WINDOWS_1257}, 1115 {C_WINDOWS_1258, CS_WINDOWS_1258}, 1116 {C_KOI8_R, CS_KOI8_R}, 1117 {C_MACCYR, CS_MACCYR}, 1118 {C_KOI8_T, CS_KOI8_T}, 1119 {C_KOI8_U, CS_KOI8_U}, 1120 {C_ISO_2022_JP, CS_ISO_2022_JP}, 1121 {C_ISO_2022_JP_2, CS_ISO_2022_JP_2}, 1122 {C_ISO_2022_JP_3, CS_ISO_2022_JP_3}, 1123 {C_EUC_JP, CS_EUC_JP}, 1124 {C_EUC_JP, CS_EUCJP}, 1125 {C_EUC_JP_MS, CS_EUC_JP_MS}, 1126 {C_SHIFT_JIS, CS_SHIFT_JIS}, 1127 {C_SHIFT_JIS, CS_SHIFT__JIS}, 1128 {C_SHIFT_JIS, CS_SJIS}, 1129 {C_ISO_2022_KR, CS_ISO_2022_KR}, 1130 {C_EUC_KR, CS_EUC_KR}, 1131 {C_ISO_2022_CN, CS_ISO_2022_CN}, 1132 {C_EUC_CN, CS_EUC_CN}, 1133 {C_GB18030, CS_GB18030}, 1134 {C_GB2312, CS_GB2312}, 1135 {C_GBK, CS_GBK}, 1136 {C_EUC_TW, CS_EUC_TW}, 1137 {C_BIG5, CS_BIG5}, 1138 {C_BIG5_HKSCS, CS_BIG5_HKSCS}, 1139 {C_TIS_620, CS_TIS_620}, 1140 {C_WINDOWS_874, CS_WINDOWS_874}, 1141 {C_GEORGIAN_PS, CS_GEORGIAN_PS}, 1142 {C_TCVN5712_1, CS_TCVN5712_1}, 1143 }; 1144 1145 static const struct { 1146 gchar *const locale; 1147 CharSet charset; 1148 CharSet out_charset; 1149 } locale_table[] = { 1150 {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP}, 1151 {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP}, 1152 {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP}, 1153 {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP}, 1154 {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP}, 1155 {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP}, 1156 {"ja_JP" , C_EUC_JP , C_ISO_2022_JP}, 1157 {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR}, 1158 {"ko_KR" , C_EUC_KR , C_EUC_KR}, 1159 {"zh_CN.GB18030" , C_GB18030 , C_GB18030}, 1160 {"zh_CN.GB2312" , C_GB2312 , C_GB2312}, 1161 {"zh_CN.GBK" , C_GBK , C_GBK}, 1162 {"zh_CN" , C_GB18030 , C_GB18030}, 1163 {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS}, 1164 {"zh_TW.eucTW" , C_EUC_TW , C_BIG5}, 1165 {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5}, 1166 {"zh_TW.Big5" , C_BIG5 , C_BIG5}, 1167 {"zh_TW" , C_BIG5 , C_BIG5}, 1168 1169 {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R}, 1170 {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R}, 1171 {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R}, 1172 {"ru_RU" , C_ISO_8859_5 , C_KOI8_R}, 1173 1174 {"tg_TJ" , C_KOI8_T , C_KOI8_T}, 1175 {"ru_UA" , C_KOI8_U , C_KOI8_U}, 1176 {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U}, 1177 {"uk_UA" , C_KOI8_U , C_KOI8_U}, 1178 1179 {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251}, 1180 {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251}, 1181 1182 {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255}, 1183 1184 {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, 1185 {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1}, 1186 {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1}, 1187 {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1}, 1188 {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1}, 1189 {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1}, 1190 {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1}, 1191 {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1}, 1192 {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1}, 1193 {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1}, 1194 {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1}, 1195 {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1}, 1196 {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1}, 1197 {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1}, 1198 {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1}, 1199 {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1}, 1200 {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1}, 1201 {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1}, 1202 {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1}, 1203 {"en_US" , C_ISO_8859_1 , C_ISO_8859_1}, 1204 {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, 1205 {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1}, 1206 {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1}, 1207 {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1}, 1208 {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1}, 1209 {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1}, 1210 {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1}, 1211 {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1}, 1212 {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1}, 1213 {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1}, 1214 {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1}, 1215 {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1}, 1216 {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1}, 1217 {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1}, 1218 {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1}, 1219 {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1}, 1220 {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1}, 1221 {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1}, 1222 {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1}, 1223 {"es_US" , C_ISO_8859_1 , C_ISO_8859_1}, 1224 {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1}, 1225 {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1}, 1226 {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1}, 1227 {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1}, 1228 {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1}, 1229 {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1}, 1230 {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1}, 1231 {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1}, 1232 {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1}, 1233 {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1}, 1234 {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1}, 1235 {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1}, 1236 {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1}, 1237 {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1}, 1238 {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1}, 1239 {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1}, 1240 {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1}, 1241 {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1}, 1242 {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1}, 1243 {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1}, 1244 {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1}, 1245 {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1}, 1246 {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1}, 1247 {"nb_NO" , C_ISO_8859_1 , C_ISO_8859_1}, 1248 {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1}, 1249 {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1}, 1250 {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1}, 1251 {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1}, 1252 {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1}, 1253 {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1}, 1254 {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1}, 1255 {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1}, 1256 {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1}, 1257 {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1}, 1258 {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1}, 1259 1260 {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2}, 1261 {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2}, 1262 {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2}, 1263 {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2}, 1264 {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2}, 1265 {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2}, 1266 {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2}, 1267 {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2}, 1268 1269 {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5}, 1270 {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2}, 1271 1272 {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3}, 1273 1274 {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4}, 1275 {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, 1276 {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, 1277 {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13}, 1278 1279 {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5}, 1280 1281 {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6}, 1282 {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6}, 1283 {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6}, 1284 {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6}, 1285 {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6}, 1286 {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6}, 1287 {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6}, 1288 {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6}, 1289 {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6}, 1290 {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6}, 1291 {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6}, 1292 {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6}, 1293 {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6}, 1294 {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6}, 1295 {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6}, 1296 {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6}, 1297 {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6}, 1298 1299 {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7}, 1300 {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8}, 1301 {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8}, 1302 {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9}, 1303 1304 {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13}, 1305 {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13}, 1306 1307 {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14}, 1308 1309 {"ar_IN" , C_UTF_8 , C_UTF_8}, 1310 {"en_IN" , C_UTF_8 , C_UTF_8}, 1311 {"se_NO" , C_UTF_8 , C_UTF_8}, 1312 {"ta_IN" , C_UTF_8 , C_UTF_8}, 1313 {"te_IN" , C_UTF_8 , C_UTF_8}, 1314 {"ur_PK" , C_UTF_8 , C_UTF_8}, 1315 1316 {"th_TH" , C_TIS_620 , C_TIS_620}, 1317 /* {"th_TH" , C_WINDOWS_874}, */ 1318 /* {"th_TH" , C_ISO_8859_11}, */ 1319 1320 {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS}, 1321 {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1}, 1322 1323 {"C" , C_US_ASCII , C_US_ASCII}, 1324 {"POSIX" , C_US_ASCII , C_US_ASCII}, 1325 {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII}, 1326 }; 1327 1328 static GHashTable *conv_get_charset_to_str_table(void) 1329 { 1330 static GHashTable *table; 1331 gint i; 1332 1333 if (table) 1334 return table; 1335 1336 table = g_hash_table_new(NULL, g_direct_equal); 1337 1338 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { 1339 if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset)) 1340 == NULL) { 1341 g_hash_table_insert 1342 (table, GUINT_TO_POINTER(charsets[i].charset), 1343 charsets[i].name); 1344 } 1345 } 1346 1347 return table; 1348 } 1349 1350 static GHashTable *conv_get_charset_from_str_table(void) 1351 { 1352 static GHashTable *table; 1353 gint i; 1354 1355 if (table) 1356 return table; 1357 1358 table = g_hash_table_new(str_case_hash, str_case_equal); 1359 1360 for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { 1361 g_hash_table_insert(table, charsets[i].name, 1362 GUINT_TO_POINTER(charsets[i].charset)); 1363 } 1364 1365 return table; 1366 } 1367 1368 const gchar *conv_get_charset_str(CharSet charset) 1369 { 1370 GHashTable *table; 1371 1372 table = conv_get_charset_to_str_table(); 1373 return g_hash_table_lookup(table, GUINT_TO_POINTER(charset)); 1374 } 1375 1376 CharSet conv_get_charset_from_str(const gchar *charset) 1377 { 1378 GHashTable *table; 1379 1380 if (!charset) return C_AUTO; 1381 1382 table = conv_get_charset_from_str_table(); 1383 return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset)); 1384 } 1385 1386 static CharSet conv_get_locale_charset(void) 1387 { 1388 static CharSet cur_charset = C_UNINITIALIZED; 1389 const gchar *cur_locale; 1390 const gchar *p; 1391 gint i; 1392 1393 if (cur_charset != C_UNINITIALIZED) 1394 return cur_charset; 1395 1396 cur_locale = conv_get_current_locale(); 1397 if (!cur_locale) { 1398 cur_charset = C_US_ASCII; 1399 return cur_charset; 1400 } 1401 1402 if (strcasestr(cur_locale, "UTF-8") || 1403 strcasestr(cur_locale, "utf8")) { 1404 cur_charset = C_UTF_8; 1405 return cur_charset; 1406 } 1407 1408 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { 1409 cur_charset = C_ISO_8859_15; 1410 return cur_charset; 1411 } 1412 1413 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { 1414 const gchar *p; 1415 1416 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and 1417 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */ 1418 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 1419 strlen(locale_table[i].locale))) { 1420 cur_charset = locale_table[i].charset; 1421 return cur_charset; 1422 } else if ((p = strchr(locale_table[i].locale, '_')) && 1423 !strchr(p + 1, '.')) { 1424 if (strlen(cur_locale) == 2 && 1425 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) { 1426 cur_charset = locale_table[i].charset; 1427 return cur_charset; 1428 } 1429 } 1430 } 1431 1432 cur_charset = C_AUTO; 1433 return cur_charset; 1434 } 1435 1436 static CharSet conv_get_locale_charset_no_utf8(void) 1437 { 1438 static CharSet cur_charset = C_UNINITIALIZED; 1439 const gchar *cur_locale; 1440 const gchar *p; 1441 gint i; 1442 1443 if (codeconv_broken_are_utf8) { 1444 cur_charset = C_UTF_8; 1445 return cur_charset; 1446 } 1447 1448 cur_locale = conv_get_current_locale(); 1449 if (!cur_locale) { 1450 cur_charset = C_US_ASCII; 1451 return cur_charset; 1452 } 1453 1454 if (strcasestr(cur_locale, "UTF-8") || 1455 strcasestr(cur_locale, "utf8")) { 1456 cur_charset = C_UTF_8; 1457 return cur_charset; 1458 } 1459 1460 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { 1461 cur_charset = C_ISO_8859_15; 1462 return cur_charset; 1463 } 1464 1465 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { 1466 const gchar *p; 1467 1468 /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and 1469 "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */ 1470 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 1471 strlen(locale_table[i].locale))) { 1472 cur_charset = locale_table[i].charset; 1473 return cur_charset; 1474 } else if ((p = strchr(locale_table[i].locale, '_')) && 1475 !strchr(p + 1, '.')) { 1476 if (strlen(cur_locale) == 2 && 1477 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) { 1478 cur_charset = locale_table[i].charset; 1479 return cur_charset; 1480 } 1481 } 1482 } 1483 1484 cur_charset = C_AUTO; 1485 return cur_charset; 1486 } 1487 1488 const gchar *conv_get_locale_charset_str(void) 1489 { 1490 static const gchar *codeset = NULL; 1491 1492 if (!codeset) 1493 codeset = conv_get_charset_str(conv_get_locale_charset()); 1494 1495 return codeset ? codeset : CS_INTERNAL; 1496 } 1497 1498 const gchar *conv_get_locale_charset_str_no_utf8(void) 1499 { 1500 static const gchar *codeset = NULL; 1501 1502 if (!codeset) 1503 codeset = conv_get_charset_str(conv_get_locale_charset_no_utf8()); 1504 1505 return codeset ? codeset : CS_INTERNAL; 1506 } 1507 1508 static CharSet conv_get_outgoing_charset(void) 1509 { 1510 static CharSet out_charset = C_UNINITIALIZED; 1511 const gchar *cur_locale; 1512 const gchar *p; 1513 gint i; 1514 1515 if (out_charset != C_UNINITIALIZED) 1516 return out_charset; 1517 1518 cur_locale = conv_get_current_locale(); 1519 if (!cur_locale) { 1520 out_charset = C_AUTO; 1521 return out_charset; 1522 } 1523 1524 if (strcasestr(cur_locale, "UTF-8") || 1525 strcasestr(cur_locale, "utf8")) { 1526 out_charset = C_UTF_8; 1527 return out_charset; 1528 } 1529 1530 if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { 1531 out_charset = C_ISO_8859_15; 1532 return out_charset; 1533 } 1534 1535 for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { 1536 const gchar *p; 1537 1538 if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 1539 strlen(locale_table[i].locale))) { 1540 out_charset = locale_table[i].out_charset; 1541 break; 1542 } else if ((p = strchr(locale_table[i].locale, '_')) && 1543 !strchr(p + 1, '.')) { 1544 if (strlen(cur_locale) == 2 && 1545 !g_ascii_strncasecmp(cur_locale, locale_table[i].locale, 2)) { 1546 out_charset = locale_table[i].out_charset; 1547 break; 1548 } 1549 } 1550 } 1551 1552 return out_charset; 1553 } 1554 1555 const gchar *conv_get_outgoing_charset_str(void) 1556 { 1557 CharSet out_charset; 1558 const gchar *str; 1559 1560 out_charset = conv_get_outgoing_charset(); 1561 str = conv_get_charset_str(out_charset); 1562 1563 return str ? str : CS_UTF_8; 1564 } 1565 1566 const gchar *conv_get_current_locale(void) 1567 { 1568 const gchar *cur_locale; 1569 1570 cur_locale = g_getenv("LC_ALL"); 1571 if (!cur_locale) cur_locale = g_getenv("LC_CTYPE"); 1572 if (!cur_locale) cur_locale = g_getenv("LANG"); 1573 if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL); 1574 1575 debug_print("current locale: %s\n", 1576 cur_locale ? cur_locale : "(none)"); 1577 1578 return cur_locale; 1579 } 1580 1581 static gboolean conv_is_ja_locale(void) 1582 { 1583 static gint is_ja_locale = -1; 1584 const gchar *cur_locale; 1585 1586 if (is_ja_locale != -1) 1587 return is_ja_locale != 0; 1588 1589 is_ja_locale = 0; 1590 cur_locale = conv_get_current_locale(); 1591 if (cur_locale) { 1592 if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0) 1593 is_ja_locale = 1; 1594 } 1595 1596 return is_ja_locale != 0; 1597 } 1598 1599 gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding, 1600 gboolean addr_field) 1601 { 1602 gchar buf[BUFFSIZE]; 1603 1604 cm_return_val_if_fail(str != NULL, NULL); 1605 1606 if (is_ascii_str(str)) 1607 return unmime_header(str, addr_field); 1608 1609 if (default_encoding) { 1610 gchar *utf8_buf; 1611 1612 utf8_buf = conv_codeset_strdup 1613 (str, default_encoding, CS_INTERNAL); 1614 if (utf8_buf) { 1615 gchar *decoded_str; 1616 1617 decoded_str = unmime_header(utf8_buf, addr_field); 1618 g_free(utf8_buf); 1619 return decoded_str; 1620 } 1621 } 1622 1623 if (conv_is_ja_locale()) 1624 conv_anytodisp(buf, sizeof(buf), str); 1625 else 1626 conv_localetodisp(buf, sizeof(buf), str); 1627 1628 return unmime_header(buf, addr_field); 1629 } 1630 1631 #define MAX_LINELEN 76 1632 #define MAX_HARD_LINELEN 996 1633 #define MIMESEP_BEGIN "=?" 1634 #define MIMESEP_END "?=" 1635 1636 #define LBREAK_IF_REQUIRED(cond, is_plain_text) \ 1637 { \ 1638 if (len - (destp - (guchar *)dest) < MAX_LINELEN + 2) { \ 1639 *destp = '\0'; \ 1640 return; \ 1641 } \ 1642 \ 1643 if ((cond) && *srcp) { \ 1644 if (destp > (guchar *)dest && left < MAX_LINELEN - 1) { \ 1645 if (isspace(*(destp - 1))) \ 1646 destp--; \ 1647 else if (is_plain_text && isspace(*srcp)) \ 1648 srcp++; \ 1649 if (*srcp) { \ 1650 *destp++ = '\n'; \ 1651 *destp++ = ' '; \ 1652 left = MAX_LINELEN - 1; \ 1653 } \ 1654 } else if (destp == (guchar *)dest && left < 7) { \ 1655 if (is_plain_text && isspace(*srcp)) \ 1656 srcp++; \ 1657 if (*srcp) { \ 1658 *destp++ = '\n'; \ 1659 *destp++ = ' '; \ 1660 left = MAX_LINELEN - 1; \ 1661 } \ 1662 } \ 1663 } \ 1664 } 1665 1666 #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0)) 1667 1668 void conv_encode_header_full(gchar *dest, gint len, const gchar *src, 1669 gint header_len, gboolean addr_field, 1670 const gchar *out_encoding_) 1671 { 1672 const gchar *cur_encoding; 1673 const gchar *out_encoding; 1674 gint mimestr_len; 1675 gchar *mimesep_enc; 1676 gint left; 1677 const guchar *srcp = src; 1678 guchar *destp = dest; 1679 gboolean use_base64; 1680 1681 cm_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE); 1682 cm_return_if_fail(destp != NULL); 1683 1684 if (MB_CUR_MAX > 1) { 1685 use_base64 = TRUE; 1686 mimesep_enc = "?B?"; 1687 } else { 1688 use_base64 = FALSE; 1689 mimesep_enc = "?Q?"; 1690 } 1691 1692 cur_encoding = CS_INTERNAL; 1693 1694 if (out_encoding_) 1695 out_encoding = out_encoding_; 1696 else 1697 out_encoding = conv_get_outgoing_charset_str(); 1698 1699 if (!strcmp(out_encoding, CS_US_ASCII)) 1700 out_encoding = CS_ISO_8859_1; 1701 1702 mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) + 1703 strlen(mimesep_enc) + strlen(MIMESEP_END); 1704 1705 left = MAX_LINELEN - header_len; 1706 1707 while (*srcp) { 1708 LBREAK_IF_REQUIRED(left <= 0, TRUE); 1709 1710 while (isspace(*srcp)) { 1711 *destp++ = *srcp++; 1712 left--; 1713 LBREAK_IF_REQUIRED(left <= 0, TRUE); 1714 } 1715 1716 /* output as it is if the next word is ASCII string */ 1717 if (!is_next_nonascii(srcp)) { 1718 gint word_len; 1719 1720 word_len = get_next_word_len(srcp); 1721 LBREAK_IF_REQUIRED(left < word_len, TRUE); 1722 while (word_len > 0) { 1723 LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE) 1724 *destp++ = *srcp++; 1725 left--; 1726 word_len--; 1727 } 1728 1729 continue; 1730 } 1731 1732 /* don't include parentheses and quotes in encoded strings */ 1733 if (addr_field && (*srcp == '(' || *srcp == ')' || *srcp == '"')) { 1734 LBREAK_IF_REQUIRED(left < 2, FALSE); 1735 *destp++ = *srcp++; 1736 left--; 1737 } 1738 1739 while (1) { 1740 gint mb_len = 0; 1741 gint cur_len = 0; 1742 gchar *part_str; 1743 gchar *out_str; 1744 gchar *enc_str; 1745 const guchar *p = srcp; 1746 gint out_str_len; 1747 gint out_enc_str_len; 1748 gint mime_block_len; 1749 gboolean cont = FALSE; 1750 1751 while (*p != '\0') { 1752 if (isspace(*p) && !is_next_nonascii(p + 1)) 1753 break; 1754 /* don't include parentheses in encoded 1755 strings */ 1756 if (addr_field && (*p == '(' || *p == ')' || *p == '"')) 1757 break; 1758 1759 mb_len = g_utf8_skip[*p]; 1760 1761 Xstrndup_a(part_str, srcp, cur_len + mb_len, ); 1762 out_str = conv_codeset_strdup 1763 (part_str, cur_encoding, out_encoding); 1764 if (!out_str) { 1765 if (codeconv_strict_mode) { 1766 *dest = '\0'; 1767 return; 1768 } else { 1769 g_warning("conv_encode_header_full(): code conversion failed"); 1770 conv_unreadable_8bit(part_str); 1771 out_str = g_strdup(part_str); 1772 } 1773 } 1774 out_str_len = strlen(out_str); 1775 1776 if (use_base64) 1777 out_enc_str_len = B64LEN(out_str_len); 1778 else 1779 out_enc_str_len = 1780 qp_get_q_encoding_len(out_str); 1781 1782 g_free(out_str); 1783 1784 if (mimestr_len + out_enc_str_len <= left) { 1785 cur_len += mb_len; 1786 p += mb_len; 1787 } else if (cur_len == 0) { 1788 left = 0; 1789 LBREAK_IF_REQUIRED(1, FALSE); 1790 continue; 1791 } else { 1792 cont = TRUE; 1793 break; 1794 } 1795 } 1796 1797 if (cur_len > 0) { 1798 Xstrndup_a(part_str, srcp, cur_len, ); 1799 out_str = conv_codeset_strdup 1800 (part_str, cur_encoding, out_encoding); 1801 if (!out_str) { 1802 g_warning("conv_encode_header_full(): code conversion failed"); 1803 conv_unreadable_8bit(part_str); 1804 out_str = g_strdup(part_str); 1805 } 1806 out_str_len = strlen(out_str); 1807 1808 if (use_base64) 1809 out_enc_str_len = B64LEN(out_str_len); 1810 else 1811 out_enc_str_len = 1812 qp_get_q_encoding_len(out_str); 1813 1814 if (use_base64) 1815 enc_str = g_base64_encode(out_str, out_str_len); 1816 else { 1817 Xalloca(enc_str, out_enc_str_len + 1, ); 1818 qp_q_encode(enc_str, out_str); 1819 } 1820 1821 g_free(out_str); 1822 1823 /* output MIME-encoded string block */ 1824 mime_block_len = mimestr_len + strlen(enc_str); 1825 g_snprintf(destp, mime_block_len + 1, 1826 MIMESEP_BEGIN "%s%s%s" MIMESEP_END, 1827 out_encoding, mimesep_enc, enc_str); 1828 1829 if (use_base64) 1830 g_free(enc_str); 1831 1832 destp += mime_block_len; 1833 srcp += cur_len; 1834 1835 left -= mime_block_len; 1836 } 1837 1838 LBREAK_IF_REQUIRED(cont, FALSE); 1839 1840 if (cur_len == 0) 1841 break; 1842 } 1843 } 1844 1845 *destp = '\0'; 1846 } 1847 1848 void conv_encode_header(gchar *dest, gint len, const gchar *src, 1849 gint header_len, gboolean addr_field) 1850 { 1851 conv_encode_header_full(dest,len,src,header_len,addr_field,NULL); 1852 } 1853 1854 #undef LBREAK_IF_REQUIRED 1855 #undef B64LEN 1856 1857 gchar *conv_filename_from_utf8(const gchar *utf8_file) 1858 { 1859 gchar *fs_file; 1860 GError *error = NULL; 1861 1862 cm_return_val_if_fail(utf8_file != NULL, NULL); 1863 1864 fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error); 1865 if (error) { 1866 debug_print("failed to convert encoding of file name: %s\n", 1867 error->message); 1868 g_error_free(error); 1869 } 1870 if (!fs_file) 1871 fs_file = g_strdup(utf8_file); 1872 1873 return fs_file; 1874 } 1875 1876 gchar *conv_filename_to_utf8(const gchar *fs_file) 1877 { 1878 gchar *utf8_file = NULL; 1879 GError *error = NULL; 1880 1881 cm_return_val_if_fail(fs_file != NULL, NULL); 1882 1883 utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error); 1884 if (error) { 1885 g_warning("failed to convert encoding of file name: %s", 1886 error->message); 1887 g_error_free(error); 1888 } 1889 1890 if (!utf8_file || !g_utf8_validate(utf8_file, -1, NULL)) { 1891 g_free(utf8_file); 1892 utf8_file = g_strdup(fs_file); 1893 conv_unreadable_8bit(utf8_file); 1894 } 1895 1896 return utf8_file; 1897 }