root/ext/iconv/iconv.c

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

DEFINITIONS

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

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

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