root/ext/iconv/iconv.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ZEND_TSRMLS_CACHE_DEFINE
  2. PHP_INI_MH
  3. PHP_INI_MH
  4. PHP_INI_MH
  5. PHP_INI_BEGIN
  6. PHP_MSHUTDOWN_FUNCTION
  7. PHP_MINFO_FUNCTION
  8. get_internal_encoding
  9. get_input_encoding
  10. get_output_encoding
  11. php_iconv_output_conflict
  12. php_iconv_output_handler_init
  13. php_iconv_output_handler
  14. _php_iconv_appendl
  15. _php_iconv_appendc
  16. _php_check_ignore
  17. php_iconv_string
  18. _php_iconv_strlen
  19. _php_iconv_substr
  20. _php_iconv_strpos
  21. _php_iconv_mime_encode
  22. _php_iconv_mime_decode
  23. _php_iconv_show_error
  24. PHP_FUNCTION
  25. PHP_FUNCTION
  26. PHP_FUNCTION
  27. PHP_FUNCTION
  28. PHP_FUNCTION
  29. PHP_FUNCTION
  30. PHP_FUNCTION
  31. PHP_NAMED_FUNCTION
  32. PHP_FUNCTION
  33. PHP_FUNCTION
  34. php_iconv_stream_filter_dtor
  35. php_iconv_stream_filter_ctor
  36. php_iconv_stream_filter_append_bucket
  37. php_iconv_stream_filter_do_filter
  38. php_iconv_stream_filter_cleanup
  39. php_iconv_stream_filter_factory_create
  40. php_iconv_stream_filter_register_factory
  41. php_iconv_stream_filter_unregister_factory

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

/* [<][>][^][v][top][bottom][index][help] */