PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/iconv - iconv.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 907 1215 74.7 %
Date: 2015-03-01 Functions: 40 40 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Sun, 01 Mar 2015 23:22:25 +0000 (5 days ago)

Copyright © 2005-2015 The PHP Group
All rights reserved.