1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: Rui Hirokawa <rui_hirokawa@ybb.ne.jp> | 16 | Stig Bakken <ssb@php.net> | 17 | Moriyoshi Koizumi <moriyoshi@php.net> | 18 +----------------------------------------------------------------------+ 19 */ 20 21/* $Id$ */ 22 23#ifdef HAVE_CONFIG_H 24#include "config.h" 25#endif 26 27#include "php.h" 28#include "php_globals.h" 29#include "ext/standard/info.h" 30#include "main/php_output.h" 31#include "SAPI.h" 32#include "php_ini.h" 33 34#ifdef HAVE_STDLIB_H 35# include <stdlib.h> 36#endif 37 38#include <errno.h> 39 40#include "php_iconv.h" 41 42#ifdef HAVE_ICONV 43 44#ifdef PHP_ICONV_H_PATH 45#include PHP_ICONV_H_PATH 46#else 47#include <iconv.h> 48#endif 49 50#ifdef HAVE_GLIBC_ICONV 51#include <gnu/libc-version.h> 52#endif 53 54#ifdef HAVE_LIBICONV 55#undef iconv 56#endif 57 58#include "ext/standard/php_smart_str.h" 59#include "ext/standard/base64.h" 60#include "ext/standard/quot_print.h" 61 62#define _php_iconv_memequal(a, b, c) \ 63 ((c) == sizeof(unsigned long) ? *((unsigned long *)(a)) == *((unsigned long *)(b)) : ((c) == sizeof(unsigned int) ? *((unsigned int *)(a)) == *((unsigned int *)(b)) : memcmp(a, b, c) == 0)) 64 65/* {{{ arginfo */ 66ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strlen, 0, 0, 1) 67 ZEND_ARG_INFO(0, str) 68 ZEND_ARG_INFO(0, charset) 69ZEND_END_ARG_INFO() 70 71ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_substr, 0, 0, 2) 72 ZEND_ARG_INFO(0, str) 73 ZEND_ARG_INFO(0, offset) 74 ZEND_ARG_INFO(0, length) 75 ZEND_ARG_INFO(0, charset) 76ZEND_END_ARG_INFO() 77 78ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strpos, 0, 0, 2) 79 ZEND_ARG_INFO(0, haystack) 80 ZEND_ARG_INFO(0, needle) 81 ZEND_ARG_INFO(0, offset) 82 ZEND_ARG_INFO(0, charset) 83ZEND_END_ARG_INFO() 84 85ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strrpos, 0, 0, 2) 86 ZEND_ARG_INFO(0, haystack) 87 ZEND_ARG_INFO(0, needle) 88 ZEND_ARG_INFO(0, charset) 89ZEND_END_ARG_INFO() 90 91ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_encode, 0, 0, 2) 92 ZEND_ARG_INFO(0, field_name) 93 ZEND_ARG_INFO(0, field_value) 94 ZEND_ARG_INFO(0, preference) /* ZEND_ARG_ARRAY_INFO(0, preference, 1) */ 95ZEND_END_ARG_INFO() 96 97ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_decode, 0, 0, 1) 98 ZEND_ARG_INFO(0, encoded_string) 99 ZEND_ARG_INFO(0, mode) 100 ZEND_ARG_INFO(0, charset) 101ZEND_END_ARG_INFO() 102 103ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_decode_headers, 0, 0, 1) 104 ZEND_ARG_INFO(0, headers) 105 ZEND_ARG_INFO(0, mode) 106 ZEND_ARG_INFO(0, charset) 107ZEND_END_ARG_INFO() 108 109ZEND_BEGIN_ARG_INFO(arginfo_iconv, 0) 110 ZEND_ARG_INFO(0, in_charset) 111 ZEND_ARG_INFO(0, out_charset) 112 ZEND_ARG_INFO(0, str) 113ZEND_END_ARG_INFO() 114 115ZEND_BEGIN_ARG_INFO(arginfo_ob_iconv_handler, 0) 116 ZEND_ARG_INFO(0, contents) 117 ZEND_ARG_INFO(0, status) 118ZEND_END_ARG_INFO() 119 120ZEND_BEGIN_ARG_INFO(arginfo_iconv_set_encoding, 0) 121 ZEND_ARG_INFO(0, type) 122 ZEND_ARG_INFO(0, charset) 123ZEND_END_ARG_INFO() 124 125ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_get_encoding, 0, 0, 0) 126 ZEND_ARG_INFO(0, type) 127ZEND_END_ARG_INFO() 128 129/* }}} */ 130 131/* {{{ iconv_functions[] 132 */ 133const zend_function_entry iconv_functions[] = { 134 PHP_RAW_NAMED_FE(iconv,php_if_iconv, arginfo_iconv) 135 PHP_FE(ob_iconv_handler, arginfo_ob_iconv_handler) 136 PHP_FE(iconv_get_encoding, arginfo_iconv_get_encoding) 137 PHP_FE(iconv_set_encoding, arginfo_iconv_set_encoding) 138 PHP_FE(iconv_strlen, arginfo_iconv_strlen) 139 PHP_FE(iconv_substr, arginfo_iconv_substr) 140 PHP_FE(iconv_strpos, arginfo_iconv_strpos) 141 PHP_FE(iconv_strrpos, arginfo_iconv_strrpos) 142 PHP_FE(iconv_mime_encode, arginfo_iconv_mime_encode) 143 PHP_FE(iconv_mime_decode, arginfo_iconv_mime_decode) 144 PHP_FE(iconv_mime_decode_headers, arginfo_iconv_mime_decode_headers) 145 PHP_FE_END 146}; 147/* }}} */ 148 149ZEND_DECLARE_MODULE_GLOBALS(iconv) 150static PHP_GINIT_FUNCTION(iconv); 151 152/* {{{ iconv_module_entry 153 */ 154zend_module_entry iconv_module_entry = { 155 STANDARD_MODULE_HEADER, 156 "iconv", 157 iconv_functions, 158 PHP_MINIT(miconv), 159 PHP_MSHUTDOWN(miconv), 160 NULL, 161 NULL, 162 PHP_MINFO(miconv), 163 NO_VERSION_YET, 164 PHP_MODULE_GLOBALS(iconv), 165 PHP_GINIT(iconv), 166 NULL, 167 NULL, 168 STANDARD_MODULE_PROPERTIES_EX 169}; 170/* }}} */ 171 172#ifdef COMPILE_DL_ICONV 173ZEND_GET_MODULE(iconv) 174#endif 175 176/* {{{ PHP_GINIT_FUNCTION */ 177static PHP_GINIT_FUNCTION(iconv) 178{ 179 iconv_globals->input_encoding = NULL; 180 iconv_globals->output_encoding = NULL; 181 iconv_globals->internal_encoding = NULL; 182} 183/* }}} */ 184 185#if defined(HAVE_LIBICONV) && defined(ICONV_ALIASED_LIBICONV) 186#define iconv libiconv 187#endif 188 189/* {{{ typedef enum php_iconv_enc_scheme_t */ 190typedef enum _php_iconv_enc_scheme_t { 191 PHP_ICONV_ENC_SCHEME_BASE64, 192 PHP_ICONV_ENC_SCHEME_QPRINT 193} php_iconv_enc_scheme_t; 194/* }}} */ 195 196#define PHP_ICONV_MIME_DECODE_STRICT (1<<0) 197#define PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR (1<<1) 198 199/* {{{ prototypes */ 200static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd); 201static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd); 202 203static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset TSRMLS_DC); 204 205static php_iconv_err_t _php_iconv_strlen(unsigned int *pretval, const char *str, size_t nbytes, const char *enc); 206 207static php_iconv_err_t _php_iconv_substr(smart_str *pretval, const char *str, size_t nbytes, int offset, int len, const char *enc); 208 209static php_iconv_err_t _php_iconv_strpos(unsigned int *pretval, const char *haystk, size_t haystk_nbytes, const char *ndl, size_t ndl_nbytes, int offset, const char *enc); 210 211static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, unsigned int max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc); 212 213static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode); 214 215static php_iconv_err_t php_iconv_stream_filter_register_factory(TSRMLS_D); 216static php_iconv_err_t php_iconv_stream_filter_unregister_factory(TSRMLS_D); 217/* }}} */ 218 219/* {{{ static globals */ 220static char _generic_superset_name[] = ICONV_UCS4_ENCODING; 221#define GENERIC_SUPERSET_NAME _generic_superset_name 222#define GENERIC_SUPERSET_NBYTES 4 223/* }}} */ 224 225static PHP_INI_MH(OnUpdateStringIconvCharset) 226{ 227 if(new_value_length >= ICONV_CSNMAXLEN) { 228 return FAILURE; 229 } 230 OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); 231 return SUCCESS; 232} 233 234/* {{{ PHP_INI 235 */ 236PHP_INI_BEGIN() 237 STD_PHP_INI_ENTRY("iconv.input_encoding", ICONV_INPUT_ENCODING, PHP_INI_ALL, OnUpdateStringIconvCharset, input_encoding, zend_iconv_globals, iconv_globals) 238 STD_PHP_INI_ENTRY("iconv.output_encoding", ICONV_OUTPUT_ENCODING, PHP_INI_ALL, OnUpdateStringIconvCharset, output_encoding, zend_iconv_globals, iconv_globals) 239 STD_PHP_INI_ENTRY("iconv.internal_encoding", ICONV_INTERNAL_ENCODING, PHP_INI_ALL, OnUpdateStringIconvCharset, internal_encoding, zend_iconv_globals, iconv_globals) 240PHP_INI_END() 241/* }}} */ 242 243/* {{{ PHP_MINIT_FUNCTION */ 244PHP_MINIT_FUNCTION(miconv) 245{ 246 char *version = "unknown"; 247 248 REGISTER_INI_ENTRIES(); 249 250#if HAVE_LIBICONV 251 { 252 static char buf[16]; 253 snprintf(buf, sizeof(buf), "%d.%d", 254 ((_libiconv_version >> 8) & 0x0f), (_libiconv_version & 0x0f)); 255 version = buf; 256 } 257#elif HAVE_GLIBC_ICONV 258 version = (char *)gnu_get_libc_version(); 259#elif defined(NETWARE) 260 version = "OS built-in"; 261#endif 262 263#ifdef PHP_ICONV_IMPL 264 REGISTER_STRING_CONSTANT("ICONV_IMPL", PHP_ICONV_IMPL, CONST_CS | CONST_PERSISTENT); 265#elif HAVE_LIBICONV 266 REGISTER_STRING_CONSTANT("ICONV_IMPL", "libiconv", CONST_CS | CONST_PERSISTENT); 267#elif defined(NETWARE) 268 REGISTER_STRING_CONSTANT("ICONV_IMPL", "Novell", CONST_CS | CONST_PERSISTENT); 269#else 270 REGISTER_STRING_CONSTANT("ICONV_IMPL", "unknown", CONST_CS | CONST_PERSISTENT); 271#endif 272 REGISTER_STRING_CONSTANT("ICONV_VERSION", version, CONST_CS | CONST_PERSISTENT); 273 274 REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_STRICT", PHP_ICONV_MIME_DECODE_STRICT, CONST_CS | CONST_PERSISTENT); 275 REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_CONTINUE_ON_ERROR", PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR, CONST_CS | CONST_PERSISTENT); 276 277 if (php_iconv_stream_filter_register_factory(TSRMLS_C) != PHP_ICONV_ERR_SUCCESS) { 278 return FAILURE; 279 } 280 281 return SUCCESS; 282} 283/* }}} */ 284 285/* {{{ PHP_MSHUTDOWN_FUNCTION */ 286PHP_MSHUTDOWN_FUNCTION(miconv) 287{ 288 php_iconv_stream_filter_unregister_factory(TSRMLS_C); 289 UNREGISTER_INI_ENTRIES(); 290 return SUCCESS; 291} 292/* }}} */ 293 294/* {{{ PHP_MINFO_FUNCTION */ 295PHP_MINFO_FUNCTION(miconv) 296{ 297 zval iconv_impl, iconv_ver; 298 299 zend_get_constant("ICONV_IMPL", sizeof("ICONV_IMPL")-1, &iconv_impl TSRMLS_CC); 300 zend_get_constant("ICONV_VERSION", sizeof("ICONV_VERSION")-1, &iconv_ver TSRMLS_CC); 301 302 php_info_print_table_start(); 303 php_info_print_table_row(2, "iconv support", "enabled"); 304 php_info_print_table_row(2, "iconv implementation", Z_STRVAL(iconv_impl)); 305 php_info_print_table_row(2, "iconv library version", Z_STRVAL(iconv_ver)); 306 php_info_print_table_end(); 307 308 DISPLAY_INI_ENTRIES(); 309 310 zval_dtor(&iconv_impl); 311 zval_dtor(&iconv_ver); 312} 313/* }}} */ 314 315/* {{{ _php_iconv_appendl() */ 316static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd) 317{ 318 const char *in_p = s; 319 size_t in_left = l; 320 char *out_p; 321 size_t out_left = 0; 322 size_t buf_growth = 128; 323#if !ICONV_SUPPORTS_ERRNO 324 size_t prev_in_left = in_left; 325#endif 326 327 if (in_p != NULL) { 328 while (in_left > 0) { 329 out_left = buf_growth - out_left; 330 { 331 size_t newlen; 332 smart_str_alloc((d), out_left, 0); 333 } 334 335 out_p = (d)->c + (d)->len; 336 337 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { 338#if ICONV_SUPPORTS_ERRNO 339 switch (errno) { 340 case EINVAL: 341 return PHP_ICONV_ERR_ILLEGAL_CHAR; 342 343 case EILSEQ: 344 return PHP_ICONV_ERR_ILLEGAL_SEQ; 345 346 case E2BIG: 347 break; 348 349 default: 350 return PHP_ICONV_ERR_UNKNOWN; 351 } 352#else 353 if (prev_in_left == in_left) { 354 return PHP_ICONV_ERR_UNKNOWN; 355 } 356#endif 357 } 358#if !ICONV_SUPPORTS_ERRNO 359 prev_in_left = in_left; 360#endif 361 (d)->len += (buf_growth - out_left); 362 buf_growth <<= 1; 363 } 364 } else { 365 for (;;) { 366 out_left = buf_growth - out_left; 367 { 368 size_t newlen; 369 smart_str_alloc((d), out_left, 0); 370 } 371 372 out_p = (d)->c + (d)->len; 373 374 if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)0) { 375 (d)->len += (buf_growth - out_left); 376 break; 377 } else { 378#if ICONV_SUPPORTS_ERRNO 379 if (errno != E2BIG) { 380 return PHP_ICONV_ERR_UNKNOWN; 381 } 382#else 383 if (out_left != 0) { 384 return PHP_ICONV_ERR_UNKNOWN; 385 } 386#endif 387 } 388 (d)->len += (buf_growth - out_left); 389 buf_growth <<= 1; 390 } 391 } 392 return PHP_ICONV_ERR_SUCCESS; 393} 394/* }}} */ 395 396/* {{{ _php_iconv_appendc() */ 397static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd) 398{ 399 return _php_iconv_appendl(d, &c, 1, cd); 400} 401/* }}} */ 402 403/* {{{ php_iconv_string() 404 */ 405PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len, 406 char **out, size_t *out_len, 407 const char *out_charset, const char *in_charset) 408{ 409#if !ICONV_SUPPORTS_ERRNO 410 size_t in_size, out_size, out_left; 411 char *out_buffer, *out_p; 412 iconv_t cd; 413 size_t result; 414 415 *out = NULL; 416 *out_len = 0; 417 418 /* 419 This is not the right way to get output size... 420 This is not space efficient for large text. 421 This is also problem for encoding like UTF-7/UTF-8/ISO-2022 which 422 a single char can be more than 4 bytes. 423 I added 15 extra bytes for safety. <yohgaki@php.net> 424 */ 425 out_size = in_len * sizeof(int) + 15; 426 out_left = out_size; 427 428 in_size = in_len; 429 430 cd = iconv_open(out_charset, in_charset); 431 432 if (cd == (iconv_t)(-1)) { 433 return PHP_ICONV_ERR_UNKNOWN; 434 } 435 436 out_buffer = (char *) emalloc(out_size + 1); 437 out_p = out_buffer; 438 439#ifdef NETWARE 440 result = iconv(cd, (char **) &in_p, &in_size, (char **) 441#else 442 result = iconv(cd, (const char **) &in_p, &in_size, (char **) 443#endif 444 &out_p, &out_left); 445 446 if (result == (size_t)(-1)) { 447 efree(out_buffer); 448 return PHP_ICONV_ERR_UNKNOWN; 449 } 450 451 if (out_left < 8) { 452 out_buffer = (char *) erealloc(out_buffer, out_size + 8); 453 } 454 455 /* flush the shift-out sequences */ 456 result = iconv(cd, NULL, NULL, &out_p, &out_left); 457 458 if (result == (size_t)(-1)) { 459 efree(out_buffer); 460 return PHP_ICONV_ERR_UNKNOWN; 461 } 462 463 *out_len = out_size - out_left; 464 out_buffer[*out_len] = '\0'; 465 *out = out_buffer; 466 467 iconv_close(cd); 468 469 return PHP_ICONV_ERR_SUCCESS; 470 471#else 472 /* 473 iconv supports errno. Handle it better way. 474 */ 475 iconv_t cd; 476 size_t in_left, out_size, out_left; 477 char *out_p, *out_buf, *tmp_buf; 478 size_t bsz, result = 0; 479 php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS; 480 481 *out = NULL; 482 *out_len = 0; 483 484 cd = iconv_open(out_charset, in_charset); 485 486 if (cd == (iconv_t)(-1)) { 487 if (errno == EINVAL) { 488 return PHP_ICONV_ERR_WRONG_CHARSET; 489 } else { 490 return PHP_ICONV_ERR_CONVERTER; 491 } 492 } 493 in_left= in_len; 494 out_left = in_len + 32; /* Avoid realloc() most cases */ 495 out_size = 0; 496 bsz = out_left; 497 out_buf = (char *) emalloc(bsz+1); 498 out_p = out_buf; 499 500 while (in_left > 0) { 501 result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left); 502 out_size = bsz - out_left; 503 if (result == (size_t)(-1)) { 504 if (errno == E2BIG && in_left > 0) { 505 /* converted string is longer than out buffer */ 506 bsz += in_len; 507 508 tmp_buf = (char*) erealloc(out_buf, bsz+1); 509 out_p = out_buf = tmp_buf; 510 out_p += out_size; 511 out_left = bsz - out_size; 512 continue; 513 } 514 } 515 break; 516 } 517 518 if (result != (size_t)(-1)) { 519 /* flush the shift-out sequences */ 520 for (;;) { 521 result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left); 522 out_size = bsz - out_left; 523 524 if (result != (size_t)(-1)) { 525 break; 526 } 527 528 if (errno == E2BIG) { 529 bsz += 16; 530 tmp_buf = (char *) erealloc(out_buf, bsz); 531 532 out_p = out_buf = tmp_buf; 533 out_p += out_size; 534 out_left = bsz - out_size; 535 } else { 536 break; 537 } 538 } 539 } 540 541 iconv_close(cd); 542 543 if (result == (size_t)(-1)) { 544 switch (errno) { 545 case EINVAL: 546 retval = PHP_ICONV_ERR_ILLEGAL_CHAR; 547 break; 548 549 case EILSEQ: 550 retval = PHP_ICONV_ERR_ILLEGAL_SEQ; 551 break; 552 553 case E2BIG: 554 /* should not happen */ 555 retval = PHP_ICONV_ERR_TOO_BIG; 556 break; 557 558 default: 559 /* other error */ 560 retval = PHP_ICONV_ERR_UNKNOWN; 561 efree(out_buf); 562 return PHP_ICONV_ERR_UNKNOWN; 563 } 564 } 565 *out_p = '\0'; 566 *out = out_buf; 567 *out_len = out_size; 568 return retval; 569#endif 570} 571/* }}} */ 572 573/* {{{ _php_iconv_strlen() */ 574static php_iconv_err_t _php_iconv_strlen(unsigned int *pretval, const char *str, size_t nbytes, const char *enc) 575{ 576 char buf[GENERIC_SUPERSET_NBYTES*2]; 577 578 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS; 579 580 iconv_t cd; 581 582 const char *in_p; 583 size_t in_left; 584 585 char *out_p; 586 size_t out_left; 587 588 unsigned int cnt; 589 590 *pretval = (unsigned int)-1; 591 592 cd = iconv_open(GENERIC_SUPERSET_NAME, enc); 593 594 if (cd == (iconv_t)(-1)) { 595#if ICONV_SUPPORTS_ERRNO 596 if (errno == EINVAL) { 597 return PHP_ICONV_ERR_WRONG_CHARSET; 598 } else { 599 return PHP_ICONV_ERR_CONVERTER; 600 } 601#else 602 return PHP_ICONV_ERR_UNKNOWN; 603#endif 604 } 605 606 errno = out_left = 0; 607 608 for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0; cnt+=2) { 609 size_t prev_in_left; 610 out_p = buf; 611 out_left = sizeof(buf); 612 613 prev_in_left = in_left; 614 615 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { 616 if (prev_in_left == in_left) { 617 break; 618 } 619 } 620 } 621 622 if (out_left > 0) { 623 cnt -= out_left / GENERIC_SUPERSET_NBYTES; 624 } 625 626#if ICONV_SUPPORTS_ERRNO 627 switch (errno) { 628 case EINVAL: 629 err = PHP_ICONV_ERR_ILLEGAL_CHAR; 630 break; 631 632 case EILSEQ: 633 err = PHP_ICONV_ERR_ILLEGAL_SEQ; 634 break; 635 636 case E2BIG: 637 case 0: 638 *pretval = cnt; 639 break; 640 641 default: 642 err = PHP_ICONV_ERR_UNKNOWN; 643 break; 644 } 645#else 646 *pretval = cnt; 647#endif 648 649 iconv_close(cd); 650 651 return err; 652} 653 654/* }}} */ 655 656/* {{{ _php_iconv_substr() */ 657static php_iconv_err_t _php_iconv_substr(smart_str *pretval, 658 const char *str, size_t nbytes, int offset, int len, const char *enc) 659{ 660 char buf[GENERIC_SUPERSET_NBYTES]; 661 662 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS; 663 664 iconv_t cd1, cd2; 665 666 const char *in_p; 667 size_t in_left; 668 669 char *out_p; 670 size_t out_left; 671 672 unsigned int cnt; 673 int total_len; 674 675 err = _php_iconv_strlen(&total_len, str, nbytes, enc); 676 if (err != PHP_ICONV_ERR_SUCCESS) { 677 return err; 678 } 679 680 if (len < 0) { 681 if ((len += (total_len - offset)) < 0) { 682 return PHP_ICONV_ERR_SUCCESS; 683 } 684 } 685 686 if (offset < 0) { 687 if ((offset += total_len) < 0) { 688 return PHP_ICONV_ERR_SUCCESS; 689 } 690 } 691 692 if(len > total_len) { 693 len = total_len; 694 } 695 696 697 if (offset >= total_len) { 698 return PHP_ICONV_ERR_SUCCESS; 699 } 700 701 if ((offset + len) > total_len ) { 702 /* trying to compute the length */ 703 len = total_len - offset; 704 } 705 706 if (len == 0) { 707 smart_str_appendl(pretval, "", 0); 708 smart_str_0(pretval); 709 return PHP_ICONV_ERR_SUCCESS; 710 } 711 712 cd1 = iconv_open(GENERIC_SUPERSET_NAME, enc); 713 714 if (cd1 == (iconv_t)(-1)) { 715#if ICONV_SUPPORTS_ERRNO 716 if (errno == EINVAL) { 717 return PHP_ICONV_ERR_WRONG_CHARSET; 718 } else { 719 return PHP_ICONV_ERR_CONVERTER; 720 } 721#else 722 return PHP_ICONV_ERR_UNKNOWN; 723#endif 724 } 725 726 cd2 = (iconv_t)NULL; 727 errno = 0; 728 729 for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0 && len > 0; ++cnt) { 730 size_t prev_in_left; 731 out_p = buf; 732 out_left = sizeof(buf); 733 734 prev_in_left = in_left; 735 736 if (iconv(cd1, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { 737 if (prev_in_left == in_left) { 738 break; 739 } 740 } 741 742 if (cnt >= (unsigned int)offset) { 743 if (cd2 == (iconv_t)NULL) { 744 cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME); 745 746 if (cd2 == (iconv_t)(-1)) { 747 cd2 = (iconv_t)NULL; 748#if ICONV_SUPPORTS_ERRNO 749 if (errno == EINVAL) { 750 err = PHP_ICONV_ERR_WRONG_CHARSET; 751 } else { 752 err = PHP_ICONV_ERR_CONVERTER; 753 } 754#else 755 err = PHP_ICONV_ERR_UNKNOWN; 756#endif 757 break; 758 } 759 } 760 761 if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) { 762 break; 763 } 764 --len; 765 } 766 767 } 768 769#if ICONV_SUPPORTS_ERRNO 770 switch (errno) { 771 case EINVAL: 772 err = PHP_ICONV_ERR_ILLEGAL_CHAR; 773 break; 774 775 case EILSEQ: 776 err = PHP_ICONV_ERR_ILLEGAL_SEQ; 777 break; 778 779 case E2BIG: 780 break; 781 } 782#endif 783 if (err == PHP_ICONV_ERR_SUCCESS) { 784 if (cd2 != (iconv_t)NULL) { 785 _php_iconv_appendl(pretval, NULL, 0, cd2); 786 } 787 smart_str_0(pretval); 788 } 789 790 if (cd1 != (iconv_t)NULL) { 791 iconv_close(cd1); 792 } 793 794 if (cd2 != (iconv_t)NULL) { 795 iconv_close(cd2); 796 } 797 return err; 798} 799 800/* }}} */ 801 802/* {{{ _php_iconv_strpos() */ 803static php_iconv_err_t _php_iconv_strpos(unsigned int *pretval, 804 const char *haystk, size_t haystk_nbytes, 805 const char *ndl, size_t ndl_nbytes, 806 int offset, const char *enc) 807{ 808 char buf[GENERIC_SUPERSET_NBYTES]; 809 810 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS; 811 812 iconv_t cd; 813 814 const char *in_p; 815 size_t in_left; 816 817 char *out_p; 818 size_t out_left; 819 820 unsigned int cnt; 821 822 char *ndl_buf; 823 const char *ndl_buf_p; 824 size_t ndl_buf_len, ndl_buf_left; 825 826 unsigned int match_ofs; 827 828 *pretval = (unsigned int)-1; 829 830 err = php_iconv_string(ndl, ndl_nbytes, 831 &ndl_buf, &ndl_buf_len, GENERIC_SUPERSET_NAME, enc); 832 833 if (err != PHP_ICONV_ERR_SUCCESS) { 834 if (ndl_buf != NULL) { 835 efree(ndl_buf); 836 } 837 return err; 838 } 839 840 cd = iconv_open(GENERIC_SUPERSET_NAME, enc); 841 842 if (cd == (iconv_t)(-1)) { 843 if (ndl_buf != NULL) { 844 efree(ndl_buf); 845 } 846#if ICONV_SUPPORTS_ERRNO 847 if (errno == EINVAL) { 848 return PHP_ICONV_ERR_WRONG_CHARSET; 849 } else { 850 return PHP_ICONV_ERR_CONVERTER; 851 } 852#else 853 return PHP_ICONV_ERR_UNKNOWN; 854#endif 855 } 856 857 ndl_buf_p = ndl_buf; 858 ndl_buf_left = ndl_buf_len; 859 match_ofs = (unsigned int)-1; 860 861 for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; in_left > 0; ++cnt) { 862 size_t prev_in_left; 863 out_p = buf; 864 out_left = sizeof(buf); 865 866 prev_in_left = in_left; 867 868 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { 869 if (prev_in_left == in_left) { 870#if ICONV_SUPPORTS_ERRNO 871 switch (errno) { 872 case EINVAL: 873 err = PHP_ICONV_ERR_ILLEGAL_CHAR; 874 break; 875 876 case EILSEQ: 877 err = PHP_ICONV_ERR_ILLEGAL_SEQ; 878 break; 879 880 case E2BIG: 881 break; 882 883 default: 884 err = PHP_ICONV_ERR_UNKNOWN; 885 break; 886 } 887#endif 888 break; 889 } 890 } 891 if (offset >= 0) { 892 if (cnt >= (unsigned int)offset) { 893 if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) { 894 if (match_ofs == (unsigned int)-1) { 895 match_ofs = cnt; 896 } 897 ndl_buf_p += GENERIC_SUPERSET_NBYTES; 898 ndl_buf_left -= GENERIC_SUPERSET_NBYTES; 899 if (ndl_buf_left == 0) { 900 *pretval = match_ofs; 901 break; 902 } 903 } else { 904 unsigned int i, j, lim; 905 906 i = 0; 907 j = GENERIC_SUPERSET_NBYTES; 908 lim = (unsigned int)(ndl_buf_p - ndl_buf); 909 910 while (j < lim) { 911 if (_php_iconv_memequal(&ndl_buf[j], &ndl_buf[i], 912 GENERIC_SUPERSET_NBYTES)) { 913 i += GENERIC_SUPERSET_NBYTES; 914 } else { 915 j -= i; 916 i = 0; 917 } 918 j += GENERIC_SUPERSET_NBYTES; 919 } 920 921 if (_php_iconv_memequal(buf, &ndl_buf[i], sizeof(buf))) { 922 match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES; 923 i += GENERIC_SUPERSET_NBYTES; 924 ndl_buf_p = &ndl_buf[i]; 925 ndl_buf_left = ndl_buf_len - i; 926 } else { 927 match_ofs = (unsigned int)-1; 928 ndl_buf_p = ndl_buf; 929 ndl_buf_left = ndl_buf_len; 930 } 931 } 932 } 933 } else { 934 if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) { 935 if (match_ofs == (unsigned int)-1) { 936 match_ofs = cnt; 937 } 938 ndl_buf_p += GENERIC_SUPERSET_NBYTES; 939 ndl_buf_left -= GENERIC_SUPERSET_NBYTES; 940 if (ndl_buf_left == 0) { 941 *pretval = match_ofs; 942 ndl_buf_p = ndl_buf; 943 ndl_buf_left = ndl_buf_len; 944 match_ofs = -1; 945 } 946 } else { 947 unsigned int i, j, lim; 948 949 i = 0; 950 j = GENERIC_SUPERSET_NBYTES; 951 lim = (unsigned int)(ndl_buf_p - ndl_buf); 952 953 while (j < lim) { 954 if (_php_iconv_memequal(&ndl_buf[j], &ndl_buf[i], 955 GENERIC_SUPERSET_NBYTES)) { 956 i += GENERIC_SUPERSET_NBYTES; 957 } else { 958 j -= i; 959 i = 0; 960 } 961 j += GENERIC_SUPERSET_NBYTES; 962 } 963 964 if (_php_iconv_memequal(buf, &ndl_buf[i], sizeof(buf))) { 965 match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES; 966 i += GENERIC_SUPERSET_NBYTES; 967 ndl_buf_p = &ndl_buf[i]; 968 ndl_buf_left = ndl_buf_len - i; 969 } else { 970 match_ofs = (unsigned int)-1; 971 ndl_buf_p = ndl_buf; 972 ndl_buf_left = ndl_buf_len; 973 } 974 } 975 } 976 } 977 978 if (ndl_buf) { 979 efree(ndl_buf); 980 } 981 982 iconv_close(cd); 983 984 return err; 985} 986/* }}} */ 987 988/* {{{ _php_iconv_mime_encode() */ 989static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fname, size_t fname_nbytes, const char *fval, size_t fval_nbytes, unsigned int max_line_len, const char *lfchars, php_iconv_enc_scheme_t enc_scheme, const char *out_charset, const char *enc) 990{ 991 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS; 992 iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1); 993 unsigned int char_cnt = 0; 994 size_t out_charset_len; 995 size_t lfchars_len; 996 char *buf = NULL; 997 char *encoded = NULL; 998 size_t encoded_len; 999 const char *in_p; 1000 size_t in_left; 1001 char *out_p; 1002 size_t out_left; 1003 static int qp_table[256] = { 1004 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x00 */ 1005 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */ 1006 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20 */ 1007 1, 1, 1, 1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 3, 1, 3, /* 0x30 */ 1008 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */ 1009 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x50 */ 1010 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 */ 1011 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x70 */ 1012 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */ 1013 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */ 1014 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xA0 */ 1015 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xB0 */ 1016 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xC0 */ 1017 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xD0 */ 1018 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xE0 */ 1019 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 /* 0xF0 */ 1020 }; 1021 1022 out_charset_len = strlen(out_charset); 1023 lfchars_len = strlen(lfchars); 1024 1025 if ((fname_nbytes + 2) >= max_line_len 1026 || (out_charset_len + 12) >= max_line_len) { 1027 /* field name is too long */ 1028 err = PHP_ICONV_ERR_TOO_BIG; 1029 goto out; 1030 } 1031 1032 cd_pl = iconv_open(ICONV_ASCII_ENCODING, enc); 1033 if (cd_pl == (iconv_t)(-1)) { 1034#if ICONV_SUPPORTS_ERRNO 1035 if (errno == EINVAL) { 1036 err = PHP_ICONV_ERR_WRONG_CHARSET; 1037 } else { 1038 err = PHP_ICONV_ERR_CONVERTER; 1039 } 1040#else 1041 err = PHP_ICONV_ERR_UNKNOWN; 1042#endif 1043 goto out; 1044 } 1045 1046 cd = iconv_open(out_charset, enc); 1047 if (cd == (iconv_t)(-1)) { 1048#if ICONV_SUPPORTS_ERRNO 1049 if (errno == EINVAL) { 1050 err = PHP_ICONV_ERR_WRONG_CHARSET; 1051 } else { 1052 err = PHP_ICONV_ERR_CONVERTER; 1053 } 1054#else 1055 err = PHP_ICONV_ERR_UNKNOWN; 1056#endif 1057 goto out; 1058 } 1059 1060 buf = safe_emalloc(1, max_line_len, 5); 1061 1062 char_cnt = max_line_len; 1063 1064 _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl); 1065 char_cnt -= fname_nbytes; 1066 smart_str_appendl(pretval, ": ", sizeof(": ") - 1); 1067 char_cnt -= 2; 1068 1069 in_p = fval; 1070 in_left = fval_nbytes; 1071 1072 do { 1073 size_t prev_in_left; 1074 size_t out_size; 1075 1076 if (char_cnt < (out_charset_len + 12)) { 1077 /* lfchars must be encoded in ASCII here*/ 1078 smart_str_appendl(pretval, lfchars, lfchars_len); 1079 smart_str_appendc(pretval, ' '); 1080 char_cnt = max_line_len - 1; 1081 } 1082 1083 smart_str_appendl(pretval, "=?", sizeof("=?") - 1); 1084 char_cnt -= 2; 1085 smart_str_appendl(pretval, out_charset, out_charset_len); 1086 char_cnt -= out_charset_len; 1087 smart_str_appendc(pretval, '?'); 1088 char_cnt --; 1089 1090 switch (enc_scheme) { 1091 case PHP_ICONV_ENC_SCHEME_BASE64: { 1092 size_t ini_in_left; 1093 const char *ini_in_p; 1094 size_t out_reserved = 4; 1095 int dummy; 1096 1097 smart_str_appendc(pretval, 'B'); 1098 char_cnt--; 1099 smart_str_appendc(pretval, '?'); 1100 char_cnt--; 1101 1102 prev_in_left = ini_in_left = in_left; 1103 ini_in_p = in_p; 1104 1105 out_size = (char_cnt - 2) / 4 * 3; 1106 1107 for (;;) { 1108 out_p = buf; 1109 1110 if (out_size <= out_reserved) { 1111 err = PHP_ICONV_ERR_TOO_BIG; 1112 goto out; 1113 } 1114 1115 out_left = out_size - out_reserved; 1116 1117 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { 1118#if ICONV_SUPPORTS_ERRNO 1119 switch (errno) { 1120 case EINVAL: 1121 err = PHP_ICONV_ERR_ILLEGAL_CHAR; 1122 goto out; 1123 1124 case EILSEQ: 1125 err = PHP_ICONV_ERR_ILLEGAL_SEQ; 1126 goto out; 1127 1128 case E2BIG: 1129 if (prev_in_left == in_left) { 1130 err = PHP_ICONV_ERR_TOO_BIG; 1131 goto out; 1132 } 1133 break; 1134 1135 default: 1136 err = PHP_ICONV_ERR_UNKNOWN; 1137 goto out; 1138 } 1139#else 1140 if (prev_in_left == in_left) { 1141 err = PHP_ICONV_ERR_UNKNOWN; 1142 goto out; 1143 } 1144#endif 1145 } 1146 1147 out_left += out_reserved; 1148 1149 if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) { 1150#if ICONV_SUPPORTS_ERRNO 1151 if (errno != E2BIG) { 1152 err = PHP_ICONV_ERR_UNKNOWN; 1153 goto out; 1154 } 1155#else 1156 if (out_left != 0) { 1157 err = PHP_ICONV_ERR_UNKNOWN; 1158 goto out; 1159 } 1160#endif 1161 } else { 1162 break; 1163 } 1164 1165 if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) { 1166 err = PHP_ICONV_ERR_UNKNOWN; 1167 goto out; 1168 } 1169 1170 out_reserved += 4; 1171 in_left = ini_in_left; 1172 in_p = ini_in_p; 1173 } 1174 1175 prev_in_left = in_left; 1176 1177 encoded = (char *) php_base64_encode((unsigned char *) buf, (int)(out_size - out_left), &dummy); 1178 encoded_len = (size_t)dummy; 1179 1180 if (char_cnt < encoded_len) { 1181 /* something went wrong! */ 1182 err = PHP_ICONV_ERR_UNKNOWN; 1183 goto out; 1184 } 1185 1186 smart_str_appendl(pretval, encoded, encoded_len); 1187 char_cnt -= encoded_len; 1188 smart_str_appendl(pretval, "?=", sizeof("?=") - 1); 1189 char_cnt -= 2; 1190 1191 efree(encoded); 1192 encoded = NULL; 1193 } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */ 1194 1195 case PHP_ICONV_ENC_SCHEME_QPRINT: { 1196 size_t ini_in_left; 1197 const char *ini_in_p; 1198 const unsigned char *p; 1199 size_t nbytes_required; 1200 1201 smart_str_appendc(pretval, 'Q'); 1202 char_cnt--; 1203 smart_str_appendc(pretval, '?'); 1204 char_cnt--; 1205 1206 prev_in_left = ini_in_left = in_left; 1207 ini_in_p = in_p; 1208 1209 for (out_size = (char_cnt - 2) / 3; out_size > 0;) { 1210 size_t prev_out_left; 1211 1212 nbytes_required = 0; 1213 1214 out_p = buf; 1215 out_left = out_size; 1216 1217 if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) { 1218#if ICONV_SUPPORTS_ERRNO 1219 switch (errno) { 1220 case EINVAL: 1221 err = PHP_ICONV_ERR_ILLEGAL_CHAR; 1222 goto out; 1223 1224 case EILSEQ: 1225 err = PHP_ICONV_ERR_ILLEGAL_SEQ; 1226 goto out; 1227 1228 case E2BIG: 1229 if (prev_in_left == in_left) { 1230 err = PHP_ICONV_ERR_UNKNOWN; 1231 goto out; 1232 } 1233 break; 1234 1235 default: 1236 err = PHP_ICONV_ERR_UNKNOWN; 1237 goto out; 1238 } 1239#else 1240 if (prev_in_left == in_left) { 1241 err = PHP_ICONV_ERR_UNKNOWN; 1242 goto out; 1243 } 1244#endif 1245 } 1246 1247 prev_out_left = out_left; 1248 if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) { 1249#if ICONV_SUPPORTS_ERRNO 1250 if (errno != E2BIG) { 1251 err = PHP_ICONV_ERR_UNKNOWN; 1252 goto out; 1253 } 1254#else 1255 if (out_left == prev_out_left) { 1256 err = PHP_ICONV_ERR_UNKNOWN; 1257 goto out; 1258 } 1259#endif 1260 } 1261 1262 for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) { 1263 nbytes_required += qp_table[*p]; 1264 } 1265 1266 if (nbytes_required <= char_cnt - 2) { 1267 break; 1268 } 1269 1270 out_size -= ((nbytes_required - (char_cnt - 2)) + 1) / 3; 1271 in_left = ini_in_left; 1272 in_p = ini_in_p; 1273 } 1274 1275 for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) { 1276 if (qp_table[*p] == 1) { 1277 smart_str_appendc(pretval, *(char *)p); 1278 char_cnt--; 1279 } else { 1280 static char qp_digits[] = "0123456789ABCDEF"; 1281 smart_str_appendc(pretval, '='); 1282 smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]); 1283 smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]); 1284 char_cnt -= 3; 1285 } 1286 } 1287 1288 smart_str_appendl(pretval, "?=", sizeof("?=") - 1); 1289 char_cnt -= 2; 1290 1291 if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) { 1292 err = PHP_ICONV_ERR_UNKNOWN; 1293 goto out; 1294 } 1295 1296 } break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */ 1297 } 1298 } while (in_left > 0); 1299 1300 smart_str_0(pretval); 1301 1302out: 1303 if (cd != (iconv_t)(-1)) { 1304 iconv_close(cd); 1305 } 1306 if (cd_pl != (iconv_t)(-1)) { 1307 iconv_close(cd_pl); 1308 } 1309 if (encoded != NULL) { 1310 efree(encoded); 1311 } 1312 if (buf != NULL) { 1313 efree(buf); 1314 } 1315 return err; 1316} 1317/* }}} */ 1318 1319/* {{{ _php_iconv_mime_decode() */ 1320static php_iconv_err_t _php_iconv_mime_decode(smart_str *pretval, const char *str, size_t str_nbytes, const char *enc, const char **next_pos, int mode) 1321{ 1322 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS; 1323 1324 iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1); 1325 1326 const char *p1; 1327 size_t str_left; 1328 unsigned int scan_stat = 0; 1329 const char *csname = NULL; 1330 size_t csname_len; 1331 const char *encoded_text = NULL; 1332 size_t encoded_text_len = 0; 1333 const char *encoded_word = NULL; 1334 const char *spaces = NULL; 1335 1336 php_iconv_enc_scheme_t enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64; 1337 1338 if (next_pos != NULL) { 1339 *next_pos = NULL; 1340 } 1341 1342 cd_pl = iconv_open(enc, ICONV_ASCII_ENCODING); 1343 1344 if (cd_pl == (iconv_t)(-1)) { 1345#if ICONV_SUPPORTS_ERRNO 1346 if (errno == EINVAL) { 1347 err = PHP_ICONV_ERR_WRONG_CHARSET; 1348 } else { 1349 err = PHP_ICONV_ERR_CONVERTER; 1350 } 1351#else 1352 err = PHP_ICONV_ERR_UNKNOWN; 1353#endif 1354 goto out; 1355 } 1356 1357 p1 = str; 1358 for (str_left = str_nbytes; str_left > 0; str_left--, p1++) { 1359 int eos = 0; 1360 1361 switch (scan_stat) { 1362 case 0: /* expecting any character */ 1363 switch (*p1) { 1364 case '\r': /* part of an EOL sequence? */ 1365 scan_stat = 7; 1366 break; 1367 1368 case '\n': 1369 scan_stat = 8; 1370 break; 1371 1372 case '=': /* first letter of an encoded chunk */ 1373 encoded_word = p1; 1374 scan_stat = 1; 1375 break; 1376 1377 case ' ': case '\t': /* a chunk of whitespaces */ 1378 spaces = p1; 1379 scan_stat = 11; 1380 break; 1381 1382 default: /* first letter of a non-encoded word */ 1383 _php_iconv_appendc(pretval, *p1, cd_pl); 1384 encoded_word = NULL; 1385 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1386 scan_stat = 12; 1387 } 1388 break; 1389 } 1390 break; 1391 1392 case 1: /* expecting a delimiter */ 1393 if (*p1 != '?') { 1394 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1395 if (err != PHP_ICONV_ERR_SUCCESS) { 1396 goto out; 1397 } 1398 encoded_word = NULL; 1399 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1400 scan_stat = 12; 1401 } else { 1402 scan_stat = 0; 1403 } 1404 break; 1405 } 1406 csname = p1 + 1; 1407 scan_stat = 2; 1408 break; 1409 1410 case 2: /* expecting a charset name */ 1411 switch (*p1) { 1412 case '?': /* normal delimiter: encoding scheme follows */ 1413 scan_stat = 3; 1414 break; 1415 1416 case '*': /* new style delimiter: locale id follows */ 1417 scan_stat = 10; 1418 break; 1419 } 1420 if (scan_stat != 2) { 1421 char tmpbuf[80]; 1422 1423 if (csname == NULL) { 1424 err = PHP_ICONV_ERR_MALFORMED; 1425 goto out; 1426 } 1427 1428 csname_len = (size_t)(p1 - csname); 1429 1430 if (csname_len > sizeof(tmpbuf) - 1) { 1431 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1432 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1433 if (err != PHP_ICONV_ERR_SUCCESS) { 1434 goto out; 1435 } 1436 encoded_word = NULL; 1437 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1438 scan_stat = 12; 1439 } else { 1440 scan_stat = 0; 1441 } 1442 break; 1443 } else { 1444 err = PHP_ICONV_ERR_MALFORMED; 1445 goto out; 1446 } 1447 } 1448 1449 memcpy(tmpbuf, csname, csname_len); 1450 tmpbuf[csname_len] = '\0'; 1451 1452 if (cd != (iconv_t)(-1)) { 1453 iconv_close(cd); 1454 } 1455 1456 cd = iconv_open(enc, tmpbuf); 1457 1458 if (cd == (iconv_t)(-1)) { 1459 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1460 /* Bad character set, but the user wants us to 1461 * press on. In this case, we'll just insert the 1462 * undecoded encoded word, since there isn't really 1463 * a more sensible behaviour available; the only 1464 * other options are to swallow the encoded word 1465 * entirely or decode it with an arbitrarily chosen 1466 * single byte encoding, both of which seem to have 1467 * a higher WTF factor than leaving it undecoded. 1468 * 1469 * Given this approach, we need to skip ahead to 1470 * the end of the encoded word. */ 1471 int qmarks = 2; 1472 while (qmarks > 0 && str_left > 1) { 1473 if (*(++p1) == '?') { 1474 --qmarks; 1475 } 1476 --str_left; 1477 } 1478 1479 /* Look ahead to check for the terminating = that 1480 * should be there as well; if it's there, we'll 1481 * also include that. If it's not, there isn't much 1482 * we can do at this point. */ 1483 if (*(p1 + 1) == '=') { 1484 ++p1; 1485 --str_left; 1486 } 1487 1488 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1489 if (err != PHP_ICONV_ERR_SUCCESS) { 1490 goto out; 1491 } 1492 1493 /* Let's go back and see if there are further 1494 * encoded words or bare content, and hope they 1495 * might actually have a valid character set. */ 1496 scan_stat = 12; 1497 break; 1498 } else { 1499#if ICONV_SUPPORTS_ERRNO 1500 if (errno == EINVAL) { 1501 err = PHP_ICONV_ERR_WRONG_CHARSET; 1502 } else { 1503 err = PHP_ICONV_ERR_CONVERTER; 1504 } 1505#else 1506 err = PHP_ICONV_ERR_UNKNOWN; 1507#endif 1508 goto out; 1509 } 1510 } 1511 } 1512 break; 1513 1514 case 3: /* expecting a encoding scheme specifier */ 1515 switch (*p1) { 1516 case 'b': 1517 case 'B': 1518 enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64; 1519 scan_stat = 4; 1520 break; 1521 1522 case 'q': 1523 case 'Q': 1524 enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT; 1525 scan_stat = 4; 1526 break; 1527 1528 default: 1529 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1530 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1531 if (err != PHP_ICONV_ERR_SUCCESS) { 1532 goto out; 1533 } 1534 encoded_word = NULL; 1535 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1536 scan_stat = 12; 1537 } else { 1538 scan_stat = 0; 1539 } 1540 break; 1541 } else { 1542 err = PHP_ICONV_ERR_MALFORMED; 1543 goto out; 1544 } 1545 } 1546 break; 1547 1548 case 4: /* expecting a delimiter */ 1549 if (*p1 != '?') { 1550 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1551 /* pass the entire chunk through the converter */ 1552 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1553 if (err != PHP_ICONV_ERR_SUCCESS) { 1554 goto out; 1555 } 1556 encoded_word = NULL; 1557 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1558 scan_stat = 12; 1559 } else { 1560 scan_stat = 0; 1561 } 1562 break; 1563 } else { 1564 err = PHP_ICONV_ERR_MALFORMED; 1565 goto out; 1566 } 1567 } 1568 encoded_text = p1 + 1; 1569 scan_stat = 5; 1570 break; 1571 1572 case 5: /* expecting an encoded portion */ 1573 if (*p1 == '?') { 1574 encoded_text_len = (size_t)(p1 - encoded_text); 1575 scan_stat = 6; 1576 } 1577 break; 1578 1579 case 7: /* expecting a "\n" character */ 1580 if (*p1 == '\n') { 1581 scan_stat = 8; 1582 } else { 1583 /* bare CR */ 1584 _php_iconv_appendc(pretval, '\r', cd_pl); 1585 _php_iconv_appendc(pretval, *p1, cd_pl); 1586 scan_stat = 0; 1587 } 1588 break; 1589 1590 case 8: /* checking whether the following line is part of a 1591 folded header */ 1592 if (*p1 != ' ' && *p1 != '\t') { 1593 --p1; 1594 str_left = 1; /* quit_loop */ 1595 break; 1596 } 1597 if (encoded_word == NULL) { 1598 _php_iconv_appendc(pretval, ' ', cd_pl); 1599 } 1600 spaces = NULL; 1601 scan_stat = 11; 1602 break; 1603 1604 case 6: /* expecting a End-Of-Chunk character "=" */ 1605 if (*p1 != '=') { 1606 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1607 /* pass the entire chunk through the converter */ 1608 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1609 if (err != PHP_ICONV_ERR_SUCCESS) { 1610 goto out; 1611 } 1612 encoded_word = NULL; 1613 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1614 scan_stat = 12; 1615 } else { 1616 scan_stat = 0; 1617 } 1618 break; 1619 } else { 1620 err = PHP_ICONV_ERR_MALFORMED; 1621 goto out; 1622 } 1623 } 1624 scan_stat = 9; 1625 if (str_left == 1) { 1626 eos = 1; 1627 } else { 1628 break; 1629 } 1630 1631 case 9: /* choice point, seeing what to do next.*/ 1632 switch (*p1) { 1633 default: 1634 /* Handle non-RFC-compliant formats 1635 * 1636 * RFC2047 requires the character that comes right 1637 * after an encoded word (chunk) to be a whitespace, 1638 * while there are lots of broken implementations that 1639 * generate such malformed headers that don't fulfill 1640 * that requirement. 1641 */ 1642 if (!eos) { 1643 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1644 /* pass the entire chunk through the converter */ 1645 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1646 if (err != PHP_ICONV_ERR_SUCCESS) { 1647 goto out; 1648 } 1649 scan_stat = 12; 1650 break; 1651 } 1652 } 1653 /* break is omitted intentionally */ 1654 1655 case '\r': case '\n': case ' ': case '\t': { 1656 char *decoded_text; 1657 size_t decoded_text_len; 1658 int dummy; 1659 1660 switch (enc_scheme) { 1661 case PHP_ICONV_ENC_SCHEME_BASE64: 1662 decoded_text = (char *)php_base64_decode((unsigned char*)encoded_text, (int)encoded_text_len, &dummy); 1663 decoded_text_len = (size_t)dummy; 1664 break; 1665 1666 case PHP_ICONV_ENC_SCHEME_QPRINT: 1667 decoded_text = (char *)php_quot_print_decode((unsigned char*)encoded_text, (int)encoded_text_len, &decoded_text_len, 1); 1668 break; 1669 default: 1670 decoded_text = NULL; 1671 break; 1672 } 1673 1674 if (decoded_text == NULL) { 1675 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1676 /* pass the entire chunk through the converter */ 1677 err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 1678 if (err != PHP_ICONV_ERR_SUCCESS) { 1679 goto out; 1680 } 1681 encoded_word = NULL; 1682 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1683 scan_stat = 12; 1684 } else { 1685 scan_stat = 0; 1686 } 1687 break; 1688 } else { 1689 err = PHP_ICONV_ERR_UNKNOWN; 1690 goto out; 1691 } 1692 } 1693 1694 err = _php_iconv_appendl(pretval, decoded_text, decoded_text_len, cd); 1695 efree(decoded_text); 1696 1697 if (err != PHP_ICONV_ERR_SUCCESS) { 1698 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1699 /* pass the entire chunk through the converter */ 1700 err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl); 1701 encoded_word = NULL; 1702 if (err != PHP_ICONV_ERR_SUCCESS) { 1703 break; 1704 } 1705 } else { 1706 goto out; 1707 } 1708 } 1709 1710 if (eos) { /* reached end-of-string. done. */ 1711 scan_stat = 0; 1712 break; 1713 } 1714 1715 switch (*p1) { 1716 case '\r': /* part of an EOL sequence? */ 1717 scan_stat = 7; 1718 break; 1719 1720 case '\n': 1721 scan_stat = 8; 1722 break; 1723 1724 case '=': /* first letter of an encoded chunk */ 1725 scan_stat = 1; 1726 break; 1727 1728 case ' ': case '\t': /* medial whitespaces */ 1729 spaces = p1; 1730 scan_stat = 11; 1731 break; 1732 1733 default: /* first letter of a non-encoded word */ 1734 _php_iconv_appendc(pretval, *p1, cd_pl); 1735 scan_stat = 12; 1736 break; 1737 } 1738 } break; 1739 } 1740 break; 1741 1742 case 10: /* expects a language specifier. dismiss it for now */ 1743 if (*p1 == '?') { 1744 scan_stat = 3; 1745 } 1746 break; 1747 1748 case 11: /* expecting a chunk of whitespaces */ 1749 switch (*p1) { 1750 case '\r': /* part of an EOL sequence? */ 1751 scan_stat = 7; 1752 break; 1753 1754 case '\n': 1755 scan_stat = 8; 1756 break; 1757 1758 case '=': /* first letter of an encoded chunk */ 1759 if (spaces != NULL && encoded_word == NULL) { 1760 _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl); 1761 spaces = NULL; 1762 } 1763 encoded_word = p1; 1764 scan_stat = 1; 1765 break; 1766 1767 case ' ': case '\t': 1768 break; 1769 1770 default: /* first letter of a non-encoded word */ 1771 if (spaces != NULL) { 1772 _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl); 1773 spaces = NULL; 1774 } 1775 _php_iconv_appendc(pretval, *p1, cd_pl); 1776 encoded_word = NULL; 1777 if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1778 scan_stat = 12; 1779 } else { 1780 scan_stat = 0; 1781 } 1782 break; 1783 } 1784 break; 1785 1786 case 12: /* expecting a non-encoded word */ 1787 switch (*p1) { 1788 case '\r': /* part of an EOL sequence? */ 1789 scan_stat = 7; 1790 break; 1791 1792 case '\n': 1793 scan_stat = 8; 1794 break; 1795 1796 case ' ': case '\t': 1797 spaces = p1; 1798 scan_stat = 11; 1799 break; 1800 1801 case '=': /* first letter of an encoded chunk */ 1802 if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) { 1803 encoded_word = p1; 1804 scan_stat = 1; 1805 break; 1806 } 1807 /* break is omitted intentionally */ 1808 1809 default: 1810 _php_iconv_appendc(pretval, *p1, cd_pl); 1811 break; 1812 } 1813 break; 1814 } 1815 } 1816 switch (scan_stat) { 1817 case 0: case 8: case 11: case 12: 1818 break; 1819 default: 1820 if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) { 1821 if (scan_stat == 1) { 1822 _php_iconv_appendc(pretval, '=', cd_pl); 1823 } 1824 err = PHP_ICONV_ERR_SUCCESS; 1825 } else { 1826 err = PHP_ICONV_ERR_MALFORMED; 1827 goto out; 1828 } 1829 } 1830 1831 if (next_pos != NULL) { 1832 *next_pos = p1; 1833 } 1834 1835 smart_str_0(pretval); 1836out: 1837 if (cd != (iconv_t)(-1)) { 1838 iconv_close(cd); 1839 } 1840 if (cd_pl != (iconv_t)(-1)) { 1841 iconv_close(cd_pl); 1842 } 1843 return err; 1844} 1845/* }}} */ 1846 1847/* {{{ php_iconv_show_error() */ 1848static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset TSRMLS_DC) 1849{ 1850 switch (err) { 1851 case PHP_ICONV_ERR_SUCCESS: 1852 break; 1853 1854 case PHP_ICONV_ERR_CONVERTER: 1855 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot open converter"); 1856 break; 1857 1858 case PHP_ICONV_ERR_WRONG_CHARSET: 1859 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong charset, conversion from `%s' to `%s' is not allowed", 1860 in_charset, out_charset); 1861 break; 1862 1863 case PHP_ICONV_ERR_ILLEGAL_CHAR: 1864 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected an incomplete multibyte character in input string"); 1865 break; 1866 1867 case PHP_ICONV_ERR_ILLEGAL_SEQ: 1868 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected an illegal character in input string"); 1869 break; 1870 1871 case PHP_ICONV_ERR_TOO_BIG: 1872 /* should not happen */ 1873 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Buffer length exceeded"); 1874 break; 1875 1876 case PHP_ICONV_ERR_MALFORMED: 1877 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed string"); 1878 break; 1879 1880 default: 1881 /* other error */ 1882 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown error (%d)", errno); 1883 break; 1884 } 1885} 1886/* }}} */ 1887 1888/* {{{ proto int iconv_strlen(string str [, string charset]) 1889 Returns the character count of str */ 1890PHP_FUNCTION(iconv_strlen) 1891{ 1892 char *charset = ICONVG(internal_encoding); 1893 int charset_len = 0; 1894 char *str; 1895 int str_len; 1896 1897 php_iconv_err_t err; 1898 1899 unsigned int retval; 1900 1901 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", 1902 &str, &str_len, &charset, &charset_len) == FAILURE) { 1903 RETURN_FALSE; 1904 } 1905 1906 if (charset_len >= ICONV_CSNMAXLEN) { 1907 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 1908 RETURN_FALSE; 1909 } 1910 1911 err = _php_iconv_strlen(&retval, str, str_len, charset); 1912 _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC); 1913 if (err == PHP_ICONV_ERR_SUCCESS) { 1914 RETVAL_LONG(retval); 1915 } else { 1916 RETVAL_FALSE; 1917 } 1918} 1919/* }}} */ 1920 1921/* {{{ proto string iconv_substr(string str, int offset, [int length, string charset]) 1922 Returns specified part of a string */ 1923PHP_FUNCTION(iconv_substr) 1924{ 1925 char *charset = ICONVG(internal_encoding); 1926 int charset_len = 0; 1927 char *str; 1928 int str_len; 1929 long offset, length = 0; 1930 1931 php_iconv_err_t err; 1932 1933 smart_str retval = {0}; 1934 1935 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|ls", 1936 &str, &str_len, &offset, &length, 1937 &charset, &charset_len) == FAILURE) { 1938 RETURN_FALSE; 1939 } 1940 1941 if (charset_len >= ICONV_CSNMAXLEN) { 1942 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 1943 RETURN_FALSE; 1944 } 1945 1946 if (ZEND_NUM_ARGS() < 3) { 1947 length = str_len; 1948 } 1949 1950 err = _php_iconv_substr(&retval, str, str_len, offset, length, charset); 1951 _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC); 1952 1953 if (err == PHP_ICONV_ERR_SUCCESS && str != NULL && retval.c != NULL) { 1954 RETURN_STRINGL(retval.c, retval.len, 0); 1955 } 1956 smart_str_free(&retval); 1957 RETURN_FALSE; 1958} 1959/* }}} */ 1960 1961/* {{{ proto int iconv_strpos(string haystack, string needle [, int offset [, string charset]]) 1962 Finds position of first occurrence of needle within part of haystack beginning with offset */ 1963PHP_FUNCTION(iconv_strpos) 1964{ 1965 char *charset = ICONVG(internal_encoding); 1966 int charset_len = 0; 1967 char *haystk; 1968 int haystk_len; 1969 char *ndl; 1970 int ndl_len; 1971 long offset = 0; 1972 1973 php_iconv_err_t err; 1974 1975 unsigned int retval; 1976 1977 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ls", 1978 &haystk, &haystk_len, &ndl, &ndl_len, 1979 &offset, &charset, &charset_len) == FAILURE) { 1980 RETURN_FALSE; 1981 } 1982 1983 if (charset_len >= ICONV_CSNMAXLEN) { 1984 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 1985 RETURN_FALSE; 1986 } 1987 1988 if (offset < 0) { 1989 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string."); 1990 RETURN_FALSE; 1991 } 1992 1993 if (ndl_len < 1) { 1994 RETURN_FALSE; 1995 } 1996 1997 err = _php_iconv_strpos(&retval, haystk, haystk_len, ndl, ndl_len, 1998 offset, charset); 1999 _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC); 2000 2001 if (err == PHP_ICONV_ERR_SUCCESS && retval != (unsigned int)-1) { 2002 RETVAL_LONG((long)retval); 2003 } else { 2004 RETVAL_FALSE; 2005 } 2006} 2007/* }}} */ 2008 2009/* {{{ proto int iconv_strrpos(string haystack, string needle [, string charset]) 2010 Finds position of last occurrence of needle within part of haystack beginning with offset */ 2011PHP_FUNCTION(iconv_strrpos) 2012{ 2013 char *charset = ICONVG(internal_encoding); 2014 int charset_len = 0; 2015 char *haystk; 2016 int haystk_len; 2017 char *ndl; 2018 int ndl_len; 2019 2020 php_iconv_err_t err; 2021 2022 unsigned int retval; 2023 2024 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s", 2025 &haystk, &haystk_len, &ndl, &ndl_len, 2026 &charset, &charset_len) == FAILURE) { 2027 RETURN_FALSE; 2028 } 2029 2030 if (ndl_len < 1) { 2031 RETURN_FALSE; 2032 } 2033 2034 if (charset_len >= ICONV_CSNMAXLEN) { 2035 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2036 RETURN_FALSE; 2037 } 2038 2039 err = _php_iconv_strpos(&retval, haystk, haystk_len, ndl, ndl_len, 2040 -1, charset); 2041 _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC); 2042 2043 if (err == PHP_ICONV_ERR_SUCCESS && retval != (unsigned int)-1) { 2044 RETVAL_LONG((long)retval); 2045 } else { 2046 RETVAL_FALSE; 2047 } 2048} 2049/* }}} */ 2050 2051/* {{{ proto string iconv_mime_encode(string field_name, string field_value [, array preference]) 2052 Composes a mime header field with field_name and field_value in a specified scheme */ 2053PHP_FUNCTION(iconv_mime_encode) 2054{ 2055 const char *field_name = NULL; 2056 int field_name_len; 2057 const char *field_value = NULL; 2058 int field_value_len; 2059 zval *pref = NULL; 2060 zval tmp_zv, *tmp_zv_p = NULL; 2061 smart_str retval = {0}; 2062 php_iconv_err_t err; 2063 2064 const char *in_charset = ICONVG(internal_encoding); 2065 const char *out_charset = in_charset; 2066 long line_len = 76; 2067 const char *lfchars = "\r\n"; 2068 php_iconv_enc_scheme_t scheme_id = PHP_ICONV_ENC_SCHEME_BASE64; 2069 2070 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a", 2071 &field_name, &field_name_len, &field_value, &field_value_len, 2072 &pref) == FAILURE) { 2073 2074 RETURN_FALSE; 2075 } 2076 2077 if (pref != NULL) { 2078 zval **ppval; 2079 2080 if (zend_hash_find(Z_ARRVAL_P(pref), "scheme", sizeof("scheme"), (void **)&ppval) == SUCCESS) { 2081 if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) { 2082 switch (Z_STRVAL_PP(ppval)[0]) { 2083 case 'B': case 'b': 2084 scheme_id = PHP_ICONV_ENC_SCHEME_BASE64; 2085 break; 2086 2087 case 'Q': case 'q': 2088 scheme_id = PHP_ICONV_ENC_SCHEME_QPRINT; 2089 break; 2090 } 2091 } 2092 } 2093 2094 if (zend_hash_find(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset"), (void **)&ppval) == SUCCESS) { 2095 if (Z_STRLEN_PP(ppval) >= ICONV_CSNMAXLEN) { 2096 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2097 RETURN_FALSE; 2098 } 2099 2100 if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) { 2101 in_charset = Z_STRVAL_PP(ppval); 2102 } 2103 } 2104 2105 2106 if (zend_hash_find(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset"), (void **)&ppval) == SUCCESS) { 2107 if (Z_STRLEN_PP(ppval) >= ICONV_CSNMAXLEN) { 2108 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2109 RETURN_FALSE; 2110 } 2111 2112 if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) { 2113 out_charset = Z_STRVAL_PP(ppval); 2114 } 2115 } 2116 2117 if (zend_hash_find(Z_ARRVAL_P(pref), "line-length", sizeof("line-length"), (void **)&ppval) == SUCCESS) { 2118 zval val, *pval = *ppval; 2119 2120 if (Z_TYPE_P(pval) != IS_LONG) { 2121 val = *pval; 2122 zval_copy_ctor(&val); 2123 convert_to_long(&val); 2124 pval = &val; 2125 } 2126 2127 line_len = Z_LVAL_P(pval); 2128 2129 if (pval == &val) { 2130 zval_dtor(&val); 2131 } 2132 } 2133 2134 if (zend_hash_find(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars"), (void **)&ppval) == SUCCESS) { 2135 if (Z_TYPE_PP(ppval) != IS_STRING) { 2136 tmp_zv = **ppval; 2137 zval_copy_ctor(&tmp_zv); 2138 convert_to_string(&tmp_zv); 2139 2140 lfchars = Z_STRVAL(tmp_zv); 2141 2142 tmp_zv_p = &tmp_zv; 2143 } else { 2144 lfchars = Z_STRVAL_PP(ppval); 2145 } 2146 } 2147 } 2148 2149 err = _php_iconv_mime_encode(&retval, field_name, field_name_len, 2150 field_value, field_value_len, line_len, lfchars, scheme_id, 2151 out_charset, in_charset); 2152 _php_iconv_show_error(err, out_charset, in_charset TSRMLS_CC); 2153 2154 if (err == PHP_ICONV_ERR_SUCCESS) { 2155 if (retval.c != NULL) { 2156 RETVAL_STRINGL(retval.c, retval.len, 0); 2157 } else { 2158 RETVAL_EMPTY_STRING(); 2159 } 2160 } else { 2161 smart_str_free(&retval); 2162 RETVAL_FALSE; 2163 } 2164 2165 if (tmp_zv_p != NULL) { 2166 zval_dtor(tmp_zv_p); 2167 } 2168} 2169/* }}} */ 2170 2171/* {{{ proto string iconv_mime_decode(string encoded_string [, int mode, string charset]) 2172 Decodes a mime header field */ 2173PHP_FUNCTION(iconv_mime_decode) 2174{ 2175 char *encoded_str; 2176 int encoded_str_len; 2177 char *charset = ICONVG(internal_encoding); 2178 int charset_len = 0; 2179 long mode = 0; 2180 2181 smart_str retval = {0}; 2182 2183 php_iconv_err_t err; 2184 2185 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", 2186 &encoded_str, &encoded_str_len, &mode, &charset, &charset_len) == FAILURE) { 2187 2188 RETURN_FALSE; 2189 } 2190 2191 if (charset_len >= ICONV_CSNMAXLEN) { 2192 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2193 RETURN_FALSE; 2194 } 2195 2196 err = _php_iconv_mime_decode(&retval, encoded_str, encoded_str_len, charset, NULL, mode); 2197 _php_iconv_show_error(err, charset, "???" TSRMLS_CC); 2198 2199 if (err == PHP_ICONV_ERR_SUCCESS) { 2200 if (retval.c != NULL) { 2201 RETVAL_STRINGL(retval.c, retval.len, 0); 2202 } else { 2203 RETVAL_EMPTY_STRING(); 2204 } 2205 } else { 2206 smart_str_free(&retval); 2207 RETVAL_FALSE; 2208 } 2209} 2210/* }}} */ 2211 2212/* {{{ proto array iconv_mime_decode_headers(string headers [, int mode, string charset]) 2213 Decodes multiple mime header fields */ 2214PHP_FUNCTION(iconv_mime_decode_headers) 2215{ 2216 const char *encoded_str; 2217 int encoded_str_len; 2218 char *charset = ICONVG(internal_encoding); 2219 int charset_len = 0; 2220 long mode = 0; 2221 2222 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS; 2223 2224 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls", 2225 &encoded_str, &encoded_str_len, &mode, &charset, &charset_len) == FAILURE) { 2226 2227 RETURN_FALSE; 2228 } 2229 2230 if (charset_len >= ICONV_CSNMAXLEN) { 2231 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2232 RETURN_FALSE; 2233 } 2234 2235 array_init(return_value); 2236 2237 while (encoded_str_len > 0) { 2238 smart_str decoded_header = {0}; 2239 char *header_name = NULL; 2240 size_t header_name_len = 0; 2241 char *header_value = NULL; 2242 size_t header_value_len = 0; 2243 char *p, *limit; 2244 const char *next_pos; 2245 2246 if (PHP_ICONV_ERR_SUCCESS != (err = _php_iconv_mime_decode(&decoded_header, encoded_str, encoded_str_len, charset, &next_pos, mode))) { 2247 smart_str_free(&decoded_header); 2248 break; 2249 } 2250 2251 if (decoded_header.c == NULL) { 2252 break; 2253 } 2254 2255 limit = decoded_header.c + decoded_header.len; 2256 for (p = decoded_header.c; p < limit; p++) { 2257 if (*p == ':') { 2258 *p = '\0'; 2259 header_name = decoded_header.c; 2260 header_name_len = (p - decoded_header.c) + 1; 2261 2262 while (++p < limit) { 2263 if (*p != ' ' && *p != '\t') { 2264 break; 2265 } 2266 } 2267 2268 header_value = p; 2269 header_value_len = limit - p; 2270 2271 break; 2272 } 2273 } 2274 2275 if (header_name != NULL) { 2276 zval **elem, *new_elem; 2277 2278 if (zend_hash_find(Z_ARRVAL_P(return_value), header_name, header_name_len, (void **)&elem) == SUCCESS) { 2279 if (Z_TYPE_PP(elem) != IS_ARRAY) { 2280 MAKE_STD_ZVAL(new_elem); 2281 array_init(new_elem); 2282 2283 Z_ADDREF_PP(elem); 2284 add_next_index_zval(new_elem, *elem); 2285 2286 zend_hash_update(Z_ARRVAL_P(return_value), header_name, header_name_len, (void *)&new_elem, sizeof(new_elem), NULL); 2287 2288 elem = &new_elem; 2289 } 2290 add_next_index_stringl(*elem, header_value, header_value_len, 1); 2291 } else { 2292 add_assoc_stringl_ex(return_value, header_name, header_name_len, header_value, header_value_len, 1); 2293 } 2294 } 2295 encoded_str_len -= next_pos - encoded_str; 2296 encoded_str = next_pos; 2297 2298 smart_str_free(&decoded_header); 2299 } 2300 2301 if (err != PHP_ICONV_ERR_SUCCESS) { 2302 _php_iconv_show_error(err, charset, "???" TSRMLS_CC); 2303 zval_dtor(return_value); 2304 RETVAL_FALSE; 2305 } 2306} 2307/* }}} */ 2308 2309/* {{{ proto string iconv(string in_charset, string out_charset, string str) 2310 Returns str converted to the out_charset character set */ 2311PHP_NAMED_FUNCTION(php_if_iconv) 2312{ 2313 char *in_charset, *out_charset, *in_buffer, *out_buffer; 2314 size_t out_len; 2315 int in_charset_len = 0, out_charset_len = 0, in_buffer_len; 2316 php_iconv_err_t err; 2317 2318 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", 2319 &in_charset, &in_charset_len, &out_charset, &out_charset_len, &in_buffer, &in_buffer_len) == FAILURE) 2320 return; 2321 2322 if (in_charset_len >= ICONV_CSNMAXLEN || out_charset_len >= ICONV_CSNMAXLEN) { 2323 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2324 RETURN_FALSE; 2325 } 2326 2327 err = php_iconv_string(in_buffer, (size_t)in_buffer_len, 2328 &out_buffer, &out_len, out_charset, in_charset); 2329 _php_iconv_show_error(err, out_charset, in_charset TSRMLS_CC); 2330 if (out_buffer != NULL) { 2331 RETVAL_STRINGL(out_buffer, out_len, 0); 2332 } else { 2333 RETURN_FALSE; 2334 } 2335} 2336/* }}} */ 2337 2338/* {{{ proto string ob_iconv_handler(string contents, int status) 2339 Returns str in output buffer converted to the iconv.output_encoding character set */ 2340PHP_FUNCTION(ob_iconv_handler) 2341{ 2342 char *out_buffer, *content_type, *mimetype = NULL, *s; 2343 zval *zv_string; 2344 size_t out_len; 2345 int mimetype_alloced = 0; 2346 long status; 2347 2348 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zl", &zv_string, &status) == FAILURE) 2349 return; 2350 2351 convert_to_string(zv_string); 2352 2353 if (SG(sapi_headers).mimetype && 2354 strncasecmp(SG(sapi_headers).mimetype, "text/", 5) == 0) { 2355 if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){ 2356 mimetype = SG(sapi_headers).mimetype; 2357 } else { 2358 mimetype = estrndup(SG(sapi_headers).mimetype, s-SG(sapi_headers).mimetype); 2359 mimetype_alloced = 1; 2360 } 2361 } else if (SG(sapi_headers).send_default_content_type) { 2362 mimetype =(SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE); 2363 } 2364 if (mimetype != NULL) { 2365 php_iconv_err_t err = php_iconv_string(Z_STRVAL_P(zv_string), 2366 Z_STRLEN_P(zv_string), &out_buffer, &out_len, 2367 ICONVG(output_encoding), ICONVG(internal_encoding)); 2368 _php_iconv_show_error(err, ICONVG(output_encoding), ICONVG(internal_encoding) TSRMLS_CC); 2369 if (out_buffer != NULL) { 2370 int len; 2371 char *p = strstr(ICONVG(output_encoding), "//"); 2372 if (p) { 2373 len = spprintf(&content_type, 0, "Content-Type:%s; charset=%.*s", mimetype, (int)(p - ICONVG(output_encoding)), ICONVG(output_encoding)); 2374 } else { 2375 len = spprintf(&content_type, 0, "Content-Type:%s; charset=%s", mimetype, ICONVG(output_encoding)); 2376 } 2377 if (content_type && sapi_add_header(content_type, len, 0) != FAILURE) { 2378 SG(sapi_headers).send_default_content_type = 0; 2379 } 2380 if (mimetype_alloced) { 2381 efree(mimetype); 2382 } 2383 RETURN_STRINGL(out_buffer, out_len, 0); 2384 } 2385 if (mimetype_alloced) { 2386 efree(mimetype); 2387 } 2388 } 2389 2390 zval_dtor(return_value); 2391 *return_value = *zv_string; 2392 zval_copy_ctor(return_value); 2393} 2394/* }}} */ 2395 2396/* {{{ proto bool iconv_set_encoding(string type, string charset) 2397 Sets internal encoding and output encoding for ob_iconv_handler() */ 2398PHP_FUNCTION(iconv_set_encoding) 2399{ 2400 char *type, *charset; 2401 int type_len, charset_len =0, retval; 2402 2403 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &type, &type_len, &charset, &charset_len) == FAILURE) 2404 return; 2405 2406 if (charset_len >= ICONV_CSNMAXLEN) { 2407 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN); 2408 RETURN_FALSE; 2409 } 2410 2411 if(!strcasecmp("input_encoding", type)) { 2412 retval = zend_alter_ini_entry("iconv.input_encoding", sizeof("iconv.input_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); 2413 } else if(!strcasecmp("output_encoding", type)) { 2414 retval = zend_alter_ini_entry("iconv.output_encoding", sizeof("iconv.output_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); 2415 } else if(!strcasecmp("internal_encoding", type)) { 2416 retval = zend_alter_ini_entry("iconv.internal_encoding", sizeof("iconv.internal_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); 2417 } else { 2418 RETURN_FALSE; 2419 } 2420 2421 if (retval == SUCCESS) { 2422 RETURN_TRUE; 2423 } else { 2424 RETURN_FALSE; 2425 } 2426} 2427/* }}} */ 2428 2429/* {{{ proto mixed iconv_get_encoding([string type]) 2430 Get internal encoding and output encoding for ob_iconv_handler() */ 2431PHP_FUNCTION(iconv_get_encoding) 2432{ 2433 char *type = "all"; 2434 int type_len = sizeof("all")-1; 2435 2436 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &type, &type_len) == FAILURE) 2437 return; 2438 2439 if (!strcasecmp("all", type)) { 2440 array_init(return_value); 2441 add_assoc_string(return_value, "input_encoding", ICONVG(input_encoding), 1); 2442 add_assoc_string(return_value, "output_encoding", ICONVG(output_encoding), 1); 2443 add_assoc_string(return_value, "internal_encoding", ICONVG(internal_encoding), 1); 2444 } else if (!strcasecmp("input_encoding", type)) { 2445 RETVAL_STRING(ICONVG(input_encoding), 1); 2446 } else if (!strcasecmp("output_encoding", type)) { 2447 RETVAL_STRING(ICONVG(output_encoding), 1); 2448 } else if (!strcasecmp("internal_encoding", type)) { 2449 RETVAL_STRING(ICONVG(internal_encoding), 1); 2450 } else { 2451 RETURN_FALSE; 2452 } 2453 2454} 2455/* }}} */ 2456 2457/* {{{ iconv stream filter */ 2458typedef struct _php_iconv_stream_filter { 2459 iconv_t cd; 2460 int persistent; 2461 char *to_charset; 2462 size_t to_charset_len; 2463 char *from_charset; 2464 size_t from_charset_len; 2465 char stub[128]; 2466 size_t stub_len; 2467} php_iconv_stream_filter; 2468/* }}} iconv stream filter */ 2469 2470/* {{{ php_iconv_stream_filter_dtor */ 2471static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self) 2472{ 2473 iconv_close(self->cd); 2474 pefree(self->to_charset, self->persistent); 2475 pefree(self->from_charset, self->persistent); 2476} 2477/* }}} */ 2478 2479/* {{{ php_iconv_stream_filter_ctor() */ 2480static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self, 2481 const char *to_charset, size_t to_charset_len, 2482 const char *from_charset, size_t from_charset_len, int persistent) 2483{ 2484 if (NULL == (self->to_charset = pemalloc(to_charset_len + 1, persistent))) { 2485 return PHP_ICONV_ERR_ALLOC; 2486 } 2487 self->to_charset_len = to_charset_len; 2488 if (NULL == (self->from_charset = pemalloc(from_charset_len + 1, persistent))) { 2489 pefree(self->to_charset, persistent); 2490 return PHP_ICONV_ERR_ALLOC; 2491 } 2492 self->from_charset_len = from_charset_len; 2493 2494 memcpy(self->to_charset, to_charset, to_charset_len); 2495 self->to_charset[to_charset_len] = '\0'; 2496 memcpy(self->from_charset, from_charset, from_charset_len); 2497 self->from_charset[from_charset_len] = '\0'; 2498 2499 if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) { 2500 pefree(self->from_charset, persistent); 2501 pefree(self->to_charset, persistent); 2502 return PHP_ICONV_ERR_UNKNOWN; 2503 } 2504 self->persistent = persistent; 2505 self->stub_len = 0; 2506 return PHP_ICONV_ERR_SUCCESS; 2507} 2508/* }}} */ 2509 2510/* {{{ php_iconv_stream_filter_append_bucket */ 2511static int php_iconv_stream_filter_append_bucket( 2512 php_iconv_stream_filter *self, 2513 php_stream *stream, php_stream_filter *filter, 2514 php_stream_bucket_brigade *buckets_out, 2515 const char *ps, size_t buf_len, size_t *consumed, 2516 int persistent TSRMLS_DC) 2517{ 2518 php_stream_bucket *new_bucket; 2519 char *out_buf = NULL; 2520 size_t out_buf_size; 2521 char *pd, *pt; 2522 size_t ocnt, prev_ocnt, icnt, tcnt; 2523 size_t initial_out_buf_size; 2524 2525 if (ps == NULL) { 2526 initial_out_buf_size = 64; 2527 icnt = 1; 2528 } else { 2529 initial_out_buf_size = buf_len; 2530 icnt = buf_len; 2531 } 2532 2533 out_buf_size = ocnt = prev_ocnt = initial_out_buf_size; 2534 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { 2535 return FAILURE; 2536 } 2537 2538 pd = out_buf; 2539 2540 if (self->stub_len > 0) { 2541 pt = self->stub; 2542 tcnt = self->stub_len; 2543 2544 while (tcnt > 0) { 2545 if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) { 2546#if ICONV_SUPPORTS_ERRNO 2547 switch (errno) { 2548 case EILSEQ: 2549 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset); 2550 goto out_failure; 2551 2552 case EINVAL: 2553 if (ps != NULL) { 2554 if (icnt > 0) { 2555 if (self->stub_len >= sizeof(self->stub)) { 2556 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset); 2557 goto out_failure; 2558 } 2559 self->stub[self->stub_len++] = *(ps++); 2560 icnt--; 2561 pt = self->stub; 2562 tcnt = self->stub_len; 2563 } else { 2564 tcnt = 0; 2565 break; 2566 } 2567 } 2568 break; 2569 2570 case E2BIG: { 2571 char *new_out_buf; 2572 size_t new_out_buf_size; 2573 2574 new_out_buf_size = out_buf_size << 1; 2575 2576 if (new_out_buf_size < out_buf_size) { 2577 /* whoa! no bigger buckets are sold anywhere... */ 2578 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 2579 goto out_failure; 2580 } 2581 2582 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 2583 2584 out_buf_size = ocnt = initial_out_buf_size; 2585 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { 2586 return FAILURE; 2587 } 2588 pd = out_buf; 2589 } else { 2590 if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) { 2591 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 2592 goto out_failure; 2593 } 2594 2595 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 2596 return FAILURE; 2597 } 2598 pd = new_out_buf + (pd - out_buf); 2599 ocnt += (new_out_buf_size - out_buf_size); 2600 out_buf = new_out_buf; 2601 out_buf_size = new_out_buf_size; 2602 } 2603 } break; 2604 2605 default: 2606 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); 2607 goto out_failure; 2608 } 2609#else 2610 if (ocnt == prev_ocnt) { 2611 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); 2612 goto out_failure; 2613 } 2614#endif 2615 } 2616 prev_ocnt = ocnt; 2617 } 2618 memmove(self->stub, pt, tcnt); 2619 self->stub_len = tcnt; 2620 } 2621 2622 while (icnt > 0) { 2623 if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt): 2624 iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) { 2625#if ICONV_SUPPORTS_ERRNO 2626 switch (errno) { 2627 case EILSEQ: 2628 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset); 2629 goto out_failure; 2630 2631 case EINVAL: 2632 if (ps != NULL) { 2633 if (icnt > sizeof(self->stub)) { 2634 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset); 2635 goto out_failure; 2636 } 2637 memcpy(self->stub, ps, icnt); 2638 self->stub_len = icnt; 2639 ps += icnt; 2640 icnt = 0; 2641 } else { 2642 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset); 2643 goto out_failure; 2644 } 2645 break; 2646 2647 case E2BIG: { 2648 char *new_out_buf; 2649 size_t new_out_buf_size; 2650 2651 new_out_buf_size = out_buf_size << 1; 2652 2653 if (new_out_buf_size < out_buf_size) { 2654 /* whoa! no bigger buckets are sold anywhere... */ 2655 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 2656 goto out_failure; 2657 } 2658 2659 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 2660 2661 out_buf_size = ocnt = initial_out_buf_size; 2662 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { 2663 return FAILURE; 2664 } 2665 pd = out_buf; 2666 } else { 2667 if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) { 2668 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 2669 goto out_failure; 2670 } 2671 2672 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 2673 return FAILURE; 2674 } 2675 pd = new_out_buf + (pd - out_buf); 2676 ocnt += (new_out_buf_size - out_buf_size); 2677 out_buf = new_out_buf; 2678 out_buf_size = new_out_buf_size; 2679 } 2680 } break; 2681 2682 default: 2683 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); 2684 goto out_failure; 2685 } 2686#else 2687 if (ocnt == prev_ocnt) { 2688 php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset); 2689 goto out_failure; 2690 } 2691#endif 2692 } else { 2693 if (ps == NULL) { 2694 break; 2695 } 2696 } 2697 prev_ocnt = ocnt; 2698 } 2699 2700 if (out_buf_size - ocnt > 0) { 2701 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 2702 goto out_failure; 2703 } 2704 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 2705 } else { 2706 pefree(out_buf, persistent); 2707 } 2708 *consumed += buf_len - icnt; 2709 2710 return SUCCESS; 2711 2712out_failure: 2713 pefree(out_buf, persistent); 2714 return FAILURE; 2715} 2716/* }}} php_iconv_stream_filter_append_bucket */ 2717 2718/* {{{ php_iconv_stream_filter_do_filter */ 2719static php_stream_filter_status_t php_iconv_stream_filter_do_filter( 2720 php_stream *stream, php_stream_filter *filter, 2721 php_stream_bucket_brigade *buckets_in, 2722 php_stream_bucket_brigade *buckets_out, 2723 size_t *bytes_consumed, int flags TSRMLS_DC) 2724{ 2725 php_stream_bucket *bucket = NULL; 2726 size_t consumed = 0; 2727 php_iconv_stream_filter *self = (php_iconv_stream_filter *)filter->abstract; 2728 2729 while (buckets_in->head != NULL) { 2730 bucket = buckets_in->head; 2731 2732 php_stream_bucket_unlink(bucket TSRMLS_CC); 2733 2734 if (php_iconv_stream_filter_append_bucket(self, stream, filter, 2735 buckets_out, bucket->buf, bucket->buflen, &consumed, 2736 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) { 2737 goto out_failure; 2738 } 2739 2740 php_stream_bucket_delref(bucket TSRMLS_CC); 2741 } 2742 2743 if (flags != PSFS_FLAG_NORMAL) { 2744 if (php_iconv_stream_filter_append_bucket(self, stream, filter, 2745 buckets_out, NULL, 0, &consumed, 2746 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) { 2747 goto out_failure; 2748 } 2749 } 2750 2751 if (bytes_consumed != NULL) { 2752 *bytes_consumed = consumed; 2753 } 2754 2755 return PSFS_PASS_ON; 2756 2757out_failure: 2758 if (bucket != NULL) { 2759 php_stream_bucket_delref(bucket TSRMLS_CC); 2760 } 2761 return PSFS_ERR_FATAL; 2762} 2763/* }}} */ 2764 2765/* {{{ php_iconv_stream_filter_cleanup */ 2766static void php_iconv_stream_filter_cleanup(php_stream_filter *filter TSRMLS_DC) 2767{ 2768 php_iconv_stream_filter_dtor((php_iconv_stream_filter *)filter->abstract); 2769 pefree(filter->abstract, ((php_iconv_stream_filter *)filter->abstract)->persistent); 2770} 2771/* }}} */ 2772 2773static php_stream_filter_ops php_iconv_stream_filter_ops = { 2774 php_iconv_stream_filter_do_filter, 2775 php_iconv_stream_filter_cleanup, 2776 "convert.iconv.*" 2777}; 2778 2779/* {{{ php_iconv_stream_filter_create */ 2780static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, int persistent TSRMLS_DC) 2781{ 2782 php_stream_filter *retval = NULL; 2783 php_iconv_stream_filter *inst; 2784 char *from_charset = NULL, *to_charset = NULL; 2785 size_t from_charset_len, to_charset_len; 2786 2787 if ((from_charset = strchr(name, '.')) == NULL) { 2788 return NULL; 2789 } 2790 ++from_charset; 2791 if ((from_charset = strchr(from_charset, '.')) == NULL) { 2792 return NULL; 2793 } 2794 ++from_charset; 2795 if ((to_charset = strpbrk(from_charset, "/.")) == NULL) { 2796 return NULL; 2797 } 2798 from_charset_len = to_charset - from_charset; 2799 ++to_charset; 2800 to_charset_len = strlen(to_charset); 2801 2802 if (from_charset_len >= ICONV_CSNMAXLEN || to_charset_len >= ICONV_CSNMAXLEN) { 2803 return NULL; 2804 } 2805 2806 if (NULL == (inst = pemalloc(sizeof(php_iconv_stream_filter), persistent))) { 2807 return NULL; 2808 } 2809 2810 if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) { 2811 pefree(inst, persistent); 2812 return NULL; 2813 } 2814 2815 if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) { 2816 php_iconv_stream_filter_dtor(inst); 2817 pefree(inst, persistent); 2818 } 2819 2820 return retval; 2821} 2822/* }}} */ 2823 2824/* {{{ php_iconv_stream_register_factory */ 2825static php_iconv_err_t php_iconv_stream_filter_register_factory(TSRMLS_D) 2826{ 2827 static php_stream_filter_factory filter_factory = { 2828 php_iconv_stream_filter_factory_create 2829 }; 2830 2831 if (FAILURE == php_stream_filter_register_factory( 2832 php_iconv_stream_filter_ops.label, 2833 &filter_factory TSRMLS_CC)) { 2834 return PHP_ICONV_ERR_UNKNOWN; 2835 } 2836 return PHP_ICONV_ERR_SUCCESS; 2837} 2838/* }}} */ 2839 2840/* {{{ php_iconv_stream_unregister_factory */ 2841static php_iconv_err_t php_iconv_stream_filter_unregister_factory(TSRMLS_D) 2842{ 2843 if (FAILURE == php_stream_filter_unregister_factory( 2844 php_iconv_stream_filter_ops.label TSRMLS_CC)) { 2845 return PHP_ICONV_ERR_UNKNOWN; 2846 } 2847 return PHP_ICONV_ERR_SUCCESS; 2848} 2849/* }}} */ 2850/* }}} */ 2851#endif 2852 2853/* 2854 * Local variables: 2855 * tab-width: 4 2856 * c-basic-offset: 4 2857 * End: 2858 * vim600: sw=4 ts=4 fdm=marker 2859 * vim<600: sw=4 ts=4 2860 */ 2861