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