Back to index

php5  5.3.10
iconv.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Rui Hirokawa <rui_hirokawa@ybb.ne.jp>                       |
00016    |          Stig Bakken <ssb@php.net>                                   |
00017    |          Moriyoshi Koizumi <moriyoshi@php.net>                       |
00018    +----------------------------------------------------------------------+
00019  */
00020 
00021 /* $Id: iconv.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include "config.h"
00025 #endif
00026 
00027 #include "php.h"
00028 #include "php_globals.h"
00029 #include "ext/standard/info.h"
00030 #include "main/php_output.h"
00031 #include "SAPI.h"
00032 #include "php_ini.h"
00033 
00034 #ifdef HAVE_STDLIB_H
00035 # include <stdlib.h>
00036 #endif
00037 
00038 #include <errno.h>
00039 
00040 #include "php_iconv.h"
00041 
00042 #ifdef HAVE_ICONV
00043 
00044 #ifdef PHP_ICONV_H_PATH
00045 #include PHP_ICONV_H_PATH
00046 #else
00047 #include <iconv.h>
00048 #endif
00049 
00050 #ifdef HAVE_GLIBC_ICONV
00051 #include <gnu/libc-version.h>
00052 #endif
00053 
00054 #ifdef HAVE_LIBICONV
00055 #undef iconv
00056 #endif
00057 
00058 #include "ext/standard/php_smart_str.h"
00059 #include "ext/standard/base64.h"
00060 #include "ext/standard/quot_print.h"
00061 
00062 #define _php_iconv_memequal(a, b, c) \
00063   ((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))
00064 
00065 /* {{{ arginfo */
00066 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strlen, 0, 0, 1)
00067        ZEND_ARG_INFO(0, str)
00068        ZEND_ARG_INFO(0, charset)
00069 ZEND_END_ARG_INFO()
00070 
00071 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_substr, 0, 0, 2)
00072        ZEND_ARG_INFO(0, str)
00073        ZEND_ARG_INFO(0, offset)
00074        ZEND_ARG_INFO(0, length)
00075        ZEND_ARG_INFO(0, charset)
00076 ZEND_END_ARG_INFO()
00077 
00078 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strpos, 0, 0, 2)
00079        ZEND_ARG_INFO(0, haystack)
00080        ZEND_ARG_INFO(0, needle)
00081        ZEND_ARG_INFO(0, offset)
00082        ZEND_ARG_INFO(0, charset)
00083 ZEND_END_ARG_INFO()
00084 
00085 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_strrpos, 0, 0, 2)
00086        ZEND_ARG_INFO(0, haystack)
00087        ZEND_ARG_INFO(0, needle)
00088        ZEND_ARG_INFO(0, charset)
00089 ZEND_END_ARG_INFO()
00090 
00091 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_encode, 0, 0, 2)
00092        ZEND_ARG_INFO(0, field_name)
00093        ZEND_ARG_INFO(0, field_value)
00094        ZEND_ARG_INFO(0, preference) /* ZEND_ARG_ARRAY_INFO(0, preference, 1) */
00095 ZEND_END_ARG_INFO()
00096 
00097 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_decode, 0, 0, 1)
00098        ZEND_ARG_INFO(0, encoded_string)
00099        ZEND_ARG_INFO(0, mode)
00100        ZEND_ARG_INFO(0, charset)
00101 ZEND_END_ARG_INFO()
00102 
00103 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_mime_decode_headers, 0, 0, 1)
00104        ZEND_ARG_INFO(0, headers)
00105        ZEND_ARG_INFO(0, mode)
00106        ZEND_ARG_INFO(0, charset)
00107 ZEND_END_ARG_INFO()
00108 
00109 ZEND_BEGIN_ARG_INFO(arginfo_iconv, 0)
00110        ZEND_ARG_INFO(0, in_charset)
00111        ZEND_ARG_INFO(0, out_charset)
00112        ZEND_ARG_INFO(0, str)
00113 ZEND_END_ARG_INFO()
00114 
00115 ZEND_BEGIN_ARG_INFO(arginfo_ob_iconv_handler, 0)
00116        ZEND_ARG_INFO(0, contents)
00117        ZEND_ARG_INFO(0, status)
00118 ZEND_END_ARG_INFO()
00119 
00120 ZEND_BEGIN_ARG_INFO(arginfo_iconv_set_encoding, 0)
00121        ZEND_ARG_INFO(0, type)
00122        ZEND_ARG_INFO(0, charset)
00123 ZEND_END_ARG_INFO()
00124 
00125 ZEND_BEGIN_ARG_INFO_EX(arginfo_iconv_get_encoding, 0, 0, 0)
00126        ZEND_ARG_INFO(0, type)
00127 ZEND_END_ARG_INFO()
00128 
00129 /* }}} */
00130 
00131 /* {{{ iconv_functions[]
00132  */
00133 const zend_function_entry iconv_functions[] = {
00134        PHP_RAW_NAMED_FE(iconv,php_if_iconv,                           arginfo_iconv)
00135        PHP_FE(ob_iconv_handler,                                       arginfo_ob_iconv_handler)
00136        PHP_FE(iconv_get_encoding,                                     arginfo_iconv_get_encoding)
00137        PHP_FE(iconv_set_encoding,                                     arginfo_iconv_set_encoding)
00138        PHP_FE(iconv_strlen,                                           arginfo_iconv_strlen)
00139        PHP_FE(iconv_substr,                                           arginfo_iconv_substr)
00140        PHP_FE(iconv_strpos,                                           arginfo_iconv_strpos)
00141        PHP_FE(iconv_strrpos,                                                 arginfo_iconv_strrpos)
00142        PHP_FE(iconv_mime_encode,                                      arginfo_iconv_mime_encode)
00143        PHP_FE(iconv_mime_decode,                                      arginfo_iconv_mime_decode)
00144        PHP_FE(iconv_mime_decode_headers,                       arginfo_iconv_mime_decode_headers)
00145        PHP_FE_END
00146 };
00147 /* }}} */
00148 
00149 ZEND_DECLARE_MODULE_GLOBALS(iconv)
00150 static PHP_GINIT_FUNCTION(iconv);
00151 
00152 /* {{{ iconv_module_entry
00153  */
00154 zend_module_entry iconv_module_entry = {
00155        STANDARD_MODULE_HEADER,
00156        "iconv",
00157        iconv_functions,
00158        PHP_MINIT(miconv),
00159        PHP_MSHUTDOWN(miconv),
00160        NULL,
00161        NULL,
00162        PHP_MINFO(miconv),
00163        NO_VERSION_YET,
00164        PHP_MODULE_GLOBALS(iconv),
00165        PHP_GINIT(iconv),
00166        NULL,
00167        NULL,
00168        STANDARD_MODULE_PROPERTIES_EX
00169 };
00170 /* }}} */
00171 
00172 #ifdef COMPILE_DL_ICONV
00173 ZEND_GET_MODULE(iconv)
00174 #endif
00175 
00176 /* {{{ PHP_GINIT_FUNCTION */
00177 static PHP_GINIT_FUNCTION(iconv)
00178 {
00179        iconv_globals->input_encoding = NULL;
00180        iconv_globals->output_encoding = NULL;
00181        iconv_globals->internal_encoding = NULL;
00182 }
00183 /* }}} */
00184 
00185 #if defined(HAVE_LIBICONV) && defined(ICONV_ALIASED_LIBICONV)
00186 #define iconv libiconv
00187 #endif
00188 
00189 /* {{{ typedef enum php_iconv_enc_scheme_t */
00190 typedef enum _php_iconv_enc_scheme_t {
00191        PHP_ICONV_ENC_SCHEME_BASE64,
00192        PHP_ICONV_ENC_SCHEME_QPRINT
00193 } php_iconv_enc_scheme_t;
00194 /* }}} */
00195 
00196 #define PHP_ICONV_MIME_DECODE_STRICT            (1<<0)
00197 #define PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR (1<<1)
00198 
00199 /* {{{ prototypes */ 
00200 static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd);
00201 static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd);
00202 
00203 static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset TSRMLS_DC);
00204 
00205 static php_iconv_err_t _php_iconv_strlen(unsigned int *pretval, const char *str, size_t nbytes, const char *enc);
00206 
00207 static php_iconv_err_t _php_iconv_substr(smart_str *pretval, const char *str, size_t nbytes, int offset, int len, const char *enc);
00208 
00209 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);
00210 
00211 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);
00212 
00213 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);
00214 
00215 static php_iconv_err_t php_iconv_stream_filter_register_factory(TSRMLS_D);
00216 static php_iconv_err_t php_iconv_stream_filter_unregister_factory(TSRMLS_D);
00217 /* }}} */
00218 
00219 /* {{{ static globals */
00220 static char _generic_superset_name[] = ICONV_UCS4_ENCODING;
00221 #define GENERIC_SUPERSET_NAME _generic_superset_name
00222 #define GENERIC_SUPERSET_NBYTES 4
00223 /* }}} */
00224 
00225 static PHP_INI_MH(OnUpdateStringIconvCharset)
00226 {
00227        if(new_value_length >= ICONV_CSNMAXLEN) {
00228               return FAILURE;
00229        }
00230        OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
00231        return SUCCESS;
00232 }
00233 
00234 /* {{{ PHP_INI
00235  */
00236 PHP_INI_BEGIN()
00237        STD_PHP_INI_ENTRY("iconv.input_encoding",    ICONV_INPUT_ENCODING,    PHP_INI_ALL, OnUpdateStringIconvCharset, input_encoding,    zend_iconv_globals, iconv_globals)
00238        STD_PHP_INI_ENTRY("iconv.output_encoding",   ICONV_OUTPUT_ENCODING,   PHP_INI_ALL, OnUpdateStringIconvCharset, output_encoding,   zend_iconv_globals, iconv_globals)
00239        STD_PHP_INI_ENTRY("iconv.internal_encoding", ICONV_INTERNAL_ENCODING, PHP_INI_ALL, OnUpdateStringIconvCharset, internal_encoding, zend_iconv_globals, iconv_globals)
00240 PHP_INI_END()
00241 /* }}} */
00242 
00243 /* {{{ PHP_MINIT_FUNCTION */
00244 PHP_MINIT_FUNCTION(miconv)
00245 {
00246        char *version = "unknown";
00247 
00248        REGISTER_INI_ENTRIES();
00249 
00250 #if HAVE_LIBICONV
00251        {
00252               static char buf[16];
00253               snprintf(buf, sizeof(buf), "%d.%d",
00254                   ((_libiconv_version >> 8) & 0x0f), (_libiconv_version & 0x0f)); 
00255               version = buf;
00256        }
00257 #elif HAVE_GLIBC_ICONV
00258        version = (char *)gnu_get_libc_version();
00259 #elif defined(NETWARE)
00260        version = "OS built-in";
00261 #endif
00262 
00263 #ifdef PHP_ICONV_IMPL
00264        REGISTER_STRING_CONSTANT("ICONV_IMPL", PHP_ICONV_IMPL, CONST_CS | CONST_PERSISTENT);
00265 #elif HAVE_LIBICONV
00266        REGISTER_STRING_CONSTANT("ICONV_IMPL", "libiconv", CONST_CS | CONST_PERSISTENT);
00267 #elif defined(NETWARE)
00268        REGISTER_STRING_CONSTANT("ICONV_IMPL", "Novell", CONST_CS | CONST_PERSISTENT);
00269 #else
00270        REGISTER_STRING_CONSTANT("ICONV_IMPL", "unknown", CONST_CS | CONST_PERSISTENT);
00271 #endif
00272        REGISTER_STRING_CONSTANT("ICONV_VERSION", version, CONST_CS | CONST_PERSISTENT);
00273 
00274        REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_STRICT", PHP_ICONV_MIME_DECODE_STRICT, CONST_CS | CONST_PERSISTENT);
00275        REGISTER_LONG_CONSTANT("ICONV_MIME_DECODE_CONTINUE_ON_ERROR", PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR, CONST_CS | CONST_PERSISTENT);
00276 
00277        if (php_iconv_stream_filter_register_factory(TSRMLS_C) != PHP_ICONV_ERR_SUCCESS) {
00278               return FAILURE;
00279        }
00280 
00281        return SUCCESS;
00282 }
00283 /* }}} */
00284 
00285 /* {{{ PHP_MSHUTDOWN_FUNCTION */
00286 PHP_MSHUTDOWN_FUNCTION(miconv)
00287 {
00288        php_iconv_stream_filter_unregister_factory(TSRMLS_C);
00289        UNREGISTER_INI_ENTRIES();
00290        return SUCCESS;
00291 }
00292 /* }}} */
00293 
00294 /* {{{ PHP_MINFO_FUNCTION */
00295 PHP_MINFO_FUNCTION(miconv)
00296 {
00297        zval iconv_impl, iconv_ver;
00298 
00299        zend_get_constant("ICONV_IMPL", sizeof("ICONV_IMPL")-1, &iconv_impl TSRMLS_CC);
00300        zend_get_constant("ICONV_VERSION", sizeof("ICONV_VERSION")-1, &iconv_ver TSRMLS_CC);
00301 
00302        php_info_print_table_start();
00303        php_info_print_table_row(2, "iconv support", "enabled");
00304        php_info_print_table_row(2, "iconv implementation", Z_STRVAL(iconv_impl));
00305        php_info_print_table_row(2, "iconv library version", Z_STRVAL(iconv_ver));
00306        php_info_print_table_end();
00307 
00308        DISPLAY_INI_ENTRIES();
00309 
00310        zval_dtor(&iconv_impl);
00311        zval_dtor(&iconv_ver);
00312 }
00313 /* }}} */
00314 
00315 /* {{{ _php_iconv_appendl() */
00316 static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd)
00317 {
00318        const char *in_p = s;
00319        size_t in_left = l;
00320        char *out_p;
00321        size_t out_left = 0;
00322        size_t buf_growth = 128;
00323 #if !ICONV_SUPPORTS_ERRNO
00324        size_t prev_in_left = in_left;
00325 #endif
00326 
00327        if (in_p != NULL) {
00328               while (in_left > 0) {
00329                      out_left = buf_growth - out_left;
00330                      {
00331                             size_t newlen;
00332                             smart_str_alloc((d), out_left, 0);
00333                      }
00334 
00335                      out_p = (d)->c + (d)->len;
00336 
00337                      if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
00338 #if ICONV_SUPPORTS_ERRNO
00339                             switch (errno) { 
00340                                    case EINVAL:
00341                                           return PHP_ICONV_ERR_ILLEGAL_CHAR;
00342 
00343                                    case EILSEQ:
00344                                           return PHP_ICONV_ERR_ILLEGAL_SEQ;
00345 
00346                                    case E2BIG:
00347                                           break;
00348 
00349                                    default:
00350                                           return PHP_ICONV_ERR_UNKNOWN;
00351                             }
00352 #else
00353                             if (prev_in_left == in_left) {
00354                                    return PHP_ICONV_ERR_UNKNOWN;             
00355                             }
00356 #endif
00357                      }
00358 #if !ICONV_SUPPORTS_ERRNO
00359                      prev_in_left = in_left;
00360 #endif
00361                      (d)->len += (buf_growth - out_left);
00362                      buf_growth <<= 1;
00363               }
00364        } else {
00365               for (;;) {
00366                      out_left = buf_growth - out_left;
00367                      {
00368                             size_t newlen;
00369                             smart_str_alloc((d), out_left, 0);
00370                      }
00371 
00372                      out_p = (d)->c + (d)->len;
00373 
00374                      if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)0) {
00375                             (d)->len += (buf_growth - out_left);
00376                             break;
00377                      } else {
00378 #if ICONV_SUPPORTS_ERRNO
00379                             if (errno != E2BIG) {
00380                                    return PHP_ICONV_ERR_UNKNOWN;
00381                             }
00382 #else
00383                             if (out_left != 0) {
00384                                    return PHP_ICONV_ERR_UNKNOWN;
00385                             }      
00386 #endif
00387                      }
00388                      (d)->len += (buf_growth - out_left);
00389                      buf_growth <<= 1;
00390               }
00391        }
00392        return PHP_ICONV_ERR_SUCCESS;
00393 }
00394 /* }}} */
00395 
00396 /* {{{ _php_iconv_appendc() */
00397 static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd)
00398 {
00399        return _php_iconv_appendl(d, &c, 1, cd);
00400 }
00401 /* }}} */
00402 
00403 /* {{{ php_iconv_string()
00404  */
00405 PHP_ICONV_API php_iconv_err_t php_iconv_string(const char *in_p, size_t in_len,
00406                                                  char **out, size_t *out_len,
00407                                                  const char *out_charset, const char *in_charset)
00408 {
00409 #if !ICONV_SUPPORTS_ERRNO
00410        size_t in_size, out_size, out_left;
00411        char *out_buffer, *out_p;
00412        iconv_t cd;
00413        size_t result;
00414 
00415        *out = NULL;
00416        *out_len = 0;
00417 
00418        /*
00419          This is not the right way to get output size...
00420          This is not space efficient for large text.
00421          This is also problem for encoding like UTF-7/UTF-8/ISO-2022 which
00422          a single char can be more than 4 bytes.
00423          I added 15 extra bytes for safety. <yohgaki@php.net>
00424        */
00425        out_size = in_len * sizeof(int) + 15;
00426        out_left = out_size;
00427 
00428        in_size = in_len;
00429 
00430        cd = iconv_open(out_charset, in_charset);
00431        
00432        if (cd == (iconv_t)(-1)) {
00433               return PHP_ICONV_ERR_UNKNOWN;
00434        }
00435 
00436        out_buffer = (char *) emalloc(out_size + 1);
00437        out_p = out_buffer;
00438        
00439 #ifdef NETWARE
00440        result = iconv(cd, (char **) &in_p, &in_size, (char **)
00441 #else
00442        result = iconv(cd, (const char **) &in_p, &in_size, (char **)
00443 #endif
00444                             &out_p, &out_left);
00445        
00446        if (result == (size_t)(-1)) {
00447               efree(out_buffer);
00448               return PHP_ICONV_ERR_UNKNOWN;
00449        }
00450 
00451        if (out_left < 8) {
00452               out_buffer = (char *) erealloc(out_buffer, out_size + 8);
00453        }
00454 
00455        /* flush the shift-out sequences */ 
00456        result = iconv(cd, NULL, NULL, &out_p, &out_left);
00457 
00458        if (result == (size_t)(-1)) {
00459               efree(out_buffer);
00460               return PHP_ICONV_ERR_UNKNOWN;
00461        }
00462 
00463        *out_len = out_size - out_left;
00464        out_buffer[*out_len] = '\0';
00465        *out = out_buffer;
00466 
00467        iconv_close(cd);
00468 
00469        return PHP_ICONV_ERR_SUCCESS;
00470 
00471 #else
00472        /*
00473          iconv supports errno. Handle it better way.
00474        */
00475        iconv_t cd;
00476        size_t in_left, out_size, out_left;
00477        char *out_p, *out_buf, *tmp_buf;
00478        size_t bsz, result = 0;
00479        php_iconv_err_t retval = PHP_ICONV_ERR_SUCCESS;
00480 
00481        *out = NULL;
00482        *out_len = 0;
00483 
00484        cd = iconv_open(out_charset, in_charset);
00485 
00486        if (cd == (iconv_t)(-1)) {
00487               if (errno == EINVAL) {
00488                      return PHP_ICONV_ERR_WRONG_CHARSET;
00489               } else {
00490                      return PHP_ICONV_ERR_CONVERTER;
00491               }
00492        }
00493        in_left= in_len;
00494        out_left = in_len + 32; /* Avoid realloc() most cases */ 
00495        out_size = 0;
00496        bsz = out_left;
00497        out_buf = (char *) emalloc(bsz+1); 
00498        out_p = out_buf;
00499 
00500        while (in_left > 0) {
00501               result = iconv(cd, (char **) &in_p, &in_left, (char **) &out_p, &out_left);
00502               out_size = bsz - out_left;
00503               if (result == (size_t)(-1)) {
00504                      if (errno == E2BIG && in_left > 0) {
00505                             /* converted string is longer than out buffer */
00506                             bsz += in_len;
00507 
00508                             tmp_buf = (char*) erealloc(out_buf, bsz+1);
00509                             out_p = out_buf = tmp_buf;
00510                             out_p += out_size;
00511                             out_left = bsz - out_size;
00512                             continue;     
00513                      }
00514               }
00515               break;
00516        }
00517 
00518        if (result != (size_t)(-1)) {
00519               /* flush the shift-out sequences */ 
00520               for (;;) {
00521                      result = iconv(cd, NULL, NULL, (char **) &out_p, &out_left);
00522                      out_size = bsz - out_left;
00523 
00524                      if (result != (size_t)(-1)) {
00525                             break;
00526                      }
00527 
00528                      if (errno == E2BIG) {
00529                             bsz += 16;
00530                             tmp_buf = (char *) erealloc(out_buf, bsz);
00531                             
00532                             out_p = out_buf = tmp_buf;
00533                             out_p += out_size;
00534                             out_left = bsz - out_size;
00535                      } else {
00536                             break;
00537                      }
00538               }
00539        }
00540 
00541        iconv_close(cd);
00542 
00543        if (result == (size_t)(-1)) {
00544               switch (errno) {
00545                      case EINVAL:
00546                             retval = PHP_ICONV_ERR_ILLEGAL_CHAR;
00547                             break;
00548 
00549                      case EILSEQ:
00550                             retval = PHP_ICONV_ERR_ILLEGAL_SEQ;
00551                             break;
00552 
00553                      case E2BIG:
00554                             /* should not happen */
00555                             retval = PHP_ICONV_ERR_TOO_BIG;
00556                             break;
00557 
00558                      default:
00559                             /* other error */
00560                             retval = PHP_ICONV_ERR_UNKNOWN;
00561                             efree(out_buf);
00562                             return PHP_ICONV_ERR_UNKNOWN;
00563               }
00564        }
00565        *out_p = '\0';
00566        *out = out_buf;
00567        *out_len = out_size;
00568        return retval;
00569 #endif
00570 }
00571 /* }}} */
00572 
00573 /* {{{ _php_iconv_strlen() */
00574 static php_iconv_err_t _php_iconv_strlen(unsigned int *pretval, const char *str, size_t nbytes, const char *enc)
00575 {
00576        char buf[GENERIC_SUPERSET_NBYTES*2];
00577 
00578        php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
00579 
00580        iconv_t cd;
00581 
00582        const char *in_p;
00583        size_t in_left;
00584 
00585        char *out_p;
00586        size_t out_left;
00587 
00588        unsigned int cnt;
00589 
00590        *pretval = (unsigned int)-1;
00591 
00592        cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
00593 
00594        if (cd == (iconv_t)(-1)) {
00595 #if ICONV_SUPPORTS_ERRNO
00596               if (errno == EINVAL) {
00597                      return PHP_ICONV_ERR_WRONG_CHARSET;
00598               } else {
00599                      return PHP_ICONV_ERR_CONVERTER;
00600               }
00601 #else
00602               return PHP_ICONV_ERR_UNKNOWN;
00603 #endif
00604        }
00605 
00606        errno = out_left = 0;
00607 
00608        for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0; cnt+=2) {
00609               size_t prev_in_left;
00610               out_p = buf;
00611               out_left = sizeof(buf);
00612 
00613               prev_in_left = in_left;
00614 
00615               if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
00616                      if (prev_in_left == in_left) {
00617                             break;
00618                      }
00619               }
00620        }
00621 
00622        if (out_left > 0) {
00623               cnt -= out_left / GENERIC_SUPERSET_NBYTES; 
00624        }
00625 
00626 #if ICONV_SUPPORTS_ERRNO
00627        switch (errno) {
00628               case EINVAL:
00629                      err = PHP_ICONV_ERR_ILLEGAL_CHAR;
00630                      break;
00631 
00632               case EILSEQ:
00633                      err = PHP_ICONV_ERR_ILLEGAL_SEQ;
00634                      break;
00635 
00636               case E2BIG:
00637               case 0:
00638                      *pretval = cnt;
00639                      break;
00640 
00641               default:
00642                      err = PHP_ICONV_ERR_UNKNOWN;
00643                      break;
00644        }
00645 #else
00646        *pretval = cnt;
00647 #endif
00648 
00649        iconv_close(cd);
00650 
00651        return err;
00652 }
00653 
00654 /* }}} */
00655 
00656 /* {{{ _php_iconv_substr() */
00657 static php_iconv_err_t _php_iconv_substr(smart_str *pretval,
00658        const char *str, size_t nbytes, int offset, int len, const char *enc)
00659 {
00660        char buf[GENERIC_SUPERSET_NBYTES];
00661 
00662        php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
00663 
00664        iconv_t cd1, cd2;
00665 
00666        const char *in_p;
00667        size_t in_left;
00668 
00669        char *out_p;
00670        size_t out_left;
00671 
00672        unsigned int cnt;
00673        int total_len;
00674        
00675        err = _php_iconv_strlen(&total_len, str, nbytes, enc);
00676        if (err != PHP_ICONV_ERR_SUCCESS) {
00677               return err;
00678        }
00679        
00680        if (len < 0) {
00681               if ((len += (total_len - offset)) < 0) {
00682                      return PHP_ICONV_ERR_SUCCESS;
00683               }
00684        }
00685 
00686        if (offset < 0) {
00687               if ((offset += total_len) < 0) {
00688                      return PHP_ICONV_ERR_SUCCESS;
00689               }
00690        }
00691 
00692        if(len > total_len) {
00693               len = total_len;
00694        }
00695 
00696 
00697        if (offset >= total_len) {
00698               return PHP_ICONV_ERR_SUCCESS;
00699        }
00700 
00701        if ((offset + len) > total_len ) {
00702               /* trying to compute the length */
00703               len = total_len - offset;
00704        }
00705 
00706        if (len == 0) {
00707               smart_str_appendl(pretval, "", 0);
00708               smart_str_0(pretval);
00709               return PHP_ICONV_ERR_SUCCESS;
00710        }
00711        
00712        cd1 = iconv_open(GENERIC_SUPERSET_NAME, enc);
00713 
00714        if (cd1 == (iconv_t)(-1)) {
00715 #if ICONV_SUPPORTS_ERRNO
00716               if (errno == EINVAL) {
00717                      return PHP_ICONV_ERR_WRONG_CHARSET;
00718               } else {
00719                      return PHP_ICONV_ERR_CONVERTER;
00720               }
00721 #else
00722               return PHP_ICONV_ERR_UNKNOWN;
00723 #endif
00724        }
00725 
00726        cd2 = (iconv_t)NULL;
00727        errno = 0;
00728 
00729        for (in_p = str, in_left = nbytes, cnt = 0; in_left > 0 && len > 0; ++cnt) {
00730               size_t prev_in_left;
00731               out_p = buf;
00732               out_left = sizeof(buf);
00733 
00734               prev_in_left = in_left;
00735 
00736               if (iconv(cd1, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
00737                      if (prev_in_left == in_left) {
00738                             break;
00739                      }
00740               }
00741 
00742               if (cnt >= (unsigned int)offset) {
00743                      if (cd2 == (iconv_t)NULL) {
00744                             cd2 = iconv_open(enc, GENERIC_SUPERSET_NAME);
00745 
00746                             if (cd2 == (iconv_t)(-1)) {
00747                                    cd2 = (iconv_t)NULL;
00748 #if ICONV_SUPPORTS_ERRNO
00749                                    if (errno == EINVAL) {
00750                                           err = PHP_ICONV_ERR_WRONG_CHARSET;
00751                                    } else {
00752                                           err = PHP_ICONV_ERR_CONVERTER;
00753                                    }
00754 #else
00755                                    err = PHP_ICONV_ERR_UNKNOWN;
00756 #endif
00757                                    break;
00758                             }
00759                      }
00760 
00761                      if (_php_iconv_appendl(pretval, buf, sizeof(buf), cd2) != PHP_ICONV_ERR_SUCCESS) {
00762                             break;
00763                      }
00764                      --len;
00765               }
00766 
00767        }
00768 
00769 #if ICONV_SUPPORTS_ERRNO
00770        switch (errno) {
00771               case EINVAL:
00772                      err = PHP_ICONV_ERR_ILLEGAL_CHAR;
00773                      break;
00774 
00775               case EILSEQ:
00776                      err = PHP_ICONV_ERR_ILLEGAL_SEQ;
00777                      break;
00778 
00779               case E2BIG:
00780                      break;
00781        }
00782 #endif
00783        if (err == PHP_ICONV_ERR_SUCCESS) {
00784               if (cd2 != (iconv_t)NULL) {
00785                      _php_iconv_appendl(pretval, NULL, 0, cd2);
00786               }
00787               smart_str_0(pretval);
00788        }
00789 
00790        if (cd1 != (iconv_t)NULL) {
00791               iconv_close(cd1);
00792        }
00793 
00794        if (cd2 != (iconv_t)NULL) {
00795               iconv_close(cd2);
00796        }      
00797        return err;
00798 }
00799 
00800 /* }}} */
00801 
00802 /* {{{ _php_iconv_strpos() */
00803 static php_iconv_err_t _php_iconv_strpos(unsigned int *pretval,
00804        const char *haystk, size_t haystk_nbytes,
00805        const char *ndl, size_t ndl_nbytes,
00806        int offset, const char *enc)
00807 {
00808        char buf[GENERIC_SUPERSET_NBYTES];
00809 
00810        php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
00811 
00812        iconv_t cd;
00813 
00814        const char *in_p;
00815        size_t in_left;
00816 
00817        char *out_p;
00818        size_t out_left;
00819 
00820        unsigned int cnt;
00821 
00822        char *ndl_buf;
00823        const char *ndl_buf_p;
00824        size_t ndl_buf_len, ndl_buf_left;
00825 
00826        unsigned int match_ofs;
00827 
00828        *pretval = (unsigned int)-1;
00829 
00830        err = php_iconv_string(ndl, ndl_nbytes,
00831               &ndl_buf, &ndl_buf_len, GENERIC_SUPERSET_NAME, enc);
00832 
00833        if (err != PHP_ICONV_ERR_SUCCESS) {
00834               if (ndl_buf != NULL) {
00835                      efree(ndl_buf);
00836               }
00837               return err;
00838        }
00839 
00840        cd = iconv_open(GENERIC_SUPERSET_NAME, enc);
00841 
00842        if (cd == (iconv_t)(-1)) {
00843               if (ndl_buf != NULL) {
00844                      efree(ndl_buf);
00845               }
00846 #if ICONV_SUPPORTS_ERRNO
00847               if (errno == EINVAL) {
00848                      return PHP_ICONV_ERR_WRONG_CHARSET;
00849               } else {
00850                      return PHP_ICONV_ERR_CONVERTER;
00851               }
00852 #else
00853               return PHP_ICONV_ERR_UNKNOWN;
00854 #endif
00855        }
00856 
00857        ndl_buf_p = ndl_buf;
00858        ndl_buf_left = ndl_buf_len;
00859        match_ofs = (unsigned int)-1;
00860 
00861        for (in_p = haystk, in_left = haystk_nbytes, cnt = 0; in_left > 0; ++cnt) {
00862               size_t prev_in_left;
00863               out_p = buf;
00864               out_left = sizeof(buf);
00865 
00866               prev_in_left = in_left;
00867 
00868               if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
00869                      if (prev_in_left == in_left) {
00870 #if ICONV_SUPPORTS_ERRNO
00871                             switch (errno) {
00872                                    case EINVAL:
00873                                           err = PHP_ICONV_ERR_ILLEGAL_CHAR;
00874                                           break;
00875 
00876                                    case EILSEQ:
00877                                           err = PHP_ICONV_ERR_ILLEGAL_SEQ;
00878                                           break;
00879 
00880                                    case E2BIG:
00881                                           break;
00882 
00883                                    default:
00884                                           err = PHP_ICONV_ERR_UNKNOWN;
00885                                           break;
00886                             }
00887 #endif
00888                             break;
00889                      }
00890               }
00891               if (offset >= 0) {
00892                      if (cnt >= (unsigned int)offset) {
00893                             if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
00894                                    if (match_ofs == (unsigned int)-1) {
00895                                           match_ofs = cnt;
00896                                    }
00897                                    ndl_buf_p += GENERIC_SUPERSET_NBYTES;
00898                                    ndl_buf_left -= GENERIC_SUPERSET_NBYTES;
00899                                    if (ndl_buf_left == 0) {
00900                                           *pretval = match_ofs;
00901                                           break;
00902                                    }
00903                             } else {
00904                                    unsigned int i, j, lim;
00905 
00906                                    i = 0;
00907                                    j = GENERIC_SUPERSET_NBYTES;
00908                                    lim = (unsigned int)(ndl_buf_p - ndl_buf);
00909 
00910                                    while (j < lim) {
00911                                           if (_php_iconv_memequal(&ndl_buf[j], &ndl_buf[i],
00912                                                      GENERIC_SUPERSET_NBYTES)) {
00913                                                  i += GENERIC_SUPERSET_NBYTES;
00914                                           } else {
00915                                                  j -= i;
00916                                                  i = 0;
00917                                           }
00918                                           j += GENERIC_SUPERSET_NBYTES;
00919                                    }
00920 
00921                                    if (_php_iconv_memequal(buf, &ndl_buf[i], sizeof(buf))) {
00922                                           match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES;
00923                                           i += GENERIC_SUPERSET_NBYTES;
00924                                           ndl_buf_p = &ndl_buf[i];
00925                                           ndl_buf_left = ndl_buf_len - i;
00926                                    } else {
00927                                           match_ofs = (unsigned int)-1;
00928                                           ndl_buf_p = ndl_buf;
00929                                           ndl_buf_left = ndl_buf_len;
00930                                    }
00931                             }
00932                      }
00933               } else {
00934                      if (_php_iconv_memequal(buf, ndl_buf_p, sizeof(buf))) {
00935                             if (match_ofs == (unsigned int)-1) {
00936                                    match_ofs = cnt;
00937                             }
00938                             ndl_buf_p += GENERIC_SUPERSET_NBYTES;
00939                             ndl_buf_left -= GENERIC_SUPERSET_NBYTES;
00940                             if (ndl_buf_left == 0) {
00941                                    *pretval = match_ofs;
00942                                    ndl_buf_p = ndl_buf;
00943                                    ndl_buf_left = ndl_buf_len;
00944                                    match_ofs = -1;
00945                             }
00946                      } else {
00947                             unsigned int i, j, lim;
00948 
00949                             i = 0;
00950                             j = GENERIC_SUPERSET_NBYTES;
00951                             lim = (unsigned int)(ndl_buf_p - ndl_buf);
00952 
00953                             while (j < lim) {
00954                                    if (_php_iconv_memequal(&ndl_buf[j], &ndl_buf[i],
00955                                                     GENERIC_SUPERSET_NBYTES)) {
00956                                           i += GENERIC_SUPERSET_NBYTES;
00957                                    } else {
00958                                           j -= i;
00959                                           i = 0;
00960                                    }
00961                                    j += GENERIC_SUPERSET_NBYTES;
00962                             }
00963 
00964                             if (_php_iconv_memequal(buf, &ndl_buf[i], sizeof(buf))) {
00965                                    match_ofs += (lim - i) / GENERIC_SUPERSET_NBYTES;
00966                                    i += GENERIC_SUPERSET_NBYTES;
00967                                    ndl_buf_p = &ndl_buf[i];
00968                                    ndl_buf_left = ndl_buf_len - i;
00969                             } else {
00970                                    match_ofs = (unsigned int)-1;
00971                                    ndl_buf_p = ndl_buf;
00972                                    ndl_buf_left = ndl_buf_len;
00973                             }
00974                      }
00975               }
00976        }
00977 
00978        if (ndl_buf) {
00979               efree(ndl_buf);
00980        }
00981        
00982        iconv_close(cd);
00983 
00984        return err;
00985 }
00986 /* }}} */
00987 
00988 /* {{{ _php_iconv_mime_encode() */
00989 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)
00990 {
00991        php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
00992        iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
00993        unsigned int char_cnt = 0;
00994        size_t out_charset_len;
00995        size_t lfchars_len;
00996        char *buf = NULL;
00997        char *encoded = NULL;
00998        size_t encoded_len;
00999        const char *in_p;
01000        size_t in_left;
01001        char *out_p;
01002        size_t out_left;
01003        static int qp_table[256] = {
01004               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x00 */
01005               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */
01006               3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20 */
01007               1, 1, 1, 1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 3, 1, 3, /* 0x30 */
01008               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
01009               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x50 */
01010               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 */
01011               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x70 */
01012               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */
01013               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */
01014               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xA0 */
01015               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xB0 */
01016               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xC0 */
01017               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xD0 */
01018               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xE0 */
01019               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3  /* 0xF0 */
01020        };
01021 
01022        out_charset_len = strlen(out_charset);
01023        lfchars_len = strlen(lfchars);
01024 
01025        if ((fname_nbytes + 2) >= max_line_len
01026               || (out_charset_len + 12) >= max_line_len) {
01027               /* field name is too long */
01028               err = PHP_ICONV_ERR_TOO_BIG; 
01029               goto out;
01030        }
01031 
01032        cd_pl = iconv_open(ICONV_ASCII_ENCODING, enc);
01033        if (cd_pl == (iconv_t)(-1)) {
01034 #if ICONV_SUPPORTS_ERRNO
01035               if (errno == EINVAL) {
01036                      err = PHP_ICONV_ERR_WRONG_CHARSET;
01037               } else {
01038                      err = PHP_ICONV_ERR_CONVERTER;
01039               }
01040 #else
01041               err = PHP_ICONV_ERR_UNKNOWN;
01042 #endif
01043               goto out;
01044        }
01045 
01046        cd = iconv_open(out_charset, enc);
01047        if (cd == (iconv_t)(-1)) {
01048 #if ICONV_SUPPORTS_ERRNO
01049               if (errno == EINVAL) {
01050                      err = PHP_ICONV_ERR_WRONG_CHARSET;
01051               } else {
01052                      err = PHP_ICONV_ERR_CONVERTER;
01053               }
01054 #else
01055               err = PHP_ICONV_ERR_UNKNOWN;
01056 #endif
01057               goto out;
01058        }
01059 
01060        buf = safe_emalloc(1, max_line_len, 5);
01061 
01062        char_cnt = max_line_len;
01063 
01064        _php_iconv_appendl(pretval, fname, fname_nbytes, cd_pl);
01065        char_cnt -= fname_nbytes;
01066        smart_str_appendl(pretval, ": ", sizeof(": ") - 1);
01067        char_cnt -= 2;
01068 
01069        in_p = fval;
01070        in_left = fval_nbytes; 
01071 
01072        do {
01073               size_t prev_in_left;
01074               size_t out_size;
01075 
01076               if (char_cnt < (out_charset_len + 12)) {
01077                      /* lfchars must be encoded in ASCII here*/
01078                      smart_str_appendl(pretval, lfchars, lfchars_len);
01079                      smart_str_appendc(pretval, ' ');
01080                      char_cnt = max_line_len - 1;
01081               } 
01082  
01083               smart_str_appendl(pretval, "=?", sizeof("=?") - 1);
01084               char_cnt -= 2;
01085               smart_str_appendl(pretval, out_charset, out_charset_len);
01086               char_cnt -= out_charset_len;
01087               smart_str_appendc(pretval, '?');
01088               char_cnt --;
01089 
01090               switch (enc_scheme) {
01091                      case PHP_ICONV_ENC_SCHEME_BASE64: {
01092                             size_t ini_in_left;
01093                             const char *ini_in_p;
01094                             size_t out_reserved = 4;
01095                             int dummy;
01096 
01097                             smart_str_appendc(pretval, 'B');
01098                             char_cnt--;
01099                             smart_str_appendc(pretval, '?');
01100                             char_cnt--;
01101 
01102                             prev_in_left = ini_in_left = in_left;
01103                             ini_in_p = in_p;
01104 
01105                             out_size = (char_cnt - 2) / 4 * 3;
01106 
01107                             for (;;) {
01108                                    out_p = buf;
01109 
01110                                    if (out_size <= out_reserved) {
01111                                           err = PHP_ICONV_ERR_TOO_BIG;
01112                                           goto out;
01113                                    }
01114 
01115                                    out_left = out_size - out_reserved;
01116 
01117                                    if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
01118 #if ICONV_SUPPORTS_ERRNO
01119                                           switch (errno) {
01120                                                  case EINVAL:
01121                                                         err = PHP_ICONV_ERR_ILLEGAL_CHAR;
01122                                                         goto out;
01123 
01124                                                  case EILSEQ:
01125                                                         err = PHP_ICONV_ERR_ILLEGAL_SEQ;
01126                                                         goto out;
01127 
01128                                                  case E2BIG:
01129                                                         if (prev_in_left == in_left) {
01130                                                                err = PHP_ICONV_ERR_TOO_BIG;
01131                                                                goto out;
01132                                                         }
01133                                                         break;
01134 
01135                                                  default:
01136                                                         err = PHP_ICONV_ERR_UNKNOWN;
01137                                                         goto out;
01138                                           }
01139 #else
01140                                           if (prev_in_left == in_left) {
01141                                                  err = PHP_ICONV_ERR_UNKNOWN;
01142                                                  goto out;
01143                                           }
01144 #endif
01145                                    }
01146 
01147                                    out_left += out_reserved;
01148 
01149                                    if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
01150 #if ICONV_SUPPORTS_ERRNO
01151                                           if (errno != E2BIG) {
01152                                                  err = PHP_ICONV_ERR_UNKNOWN;
01153                                                  goto out;
01154                                           }
01155 #else
01156                                           if (out_left != 0) {
01157                                                  err = PHP_ICONV_ERR_UNKNOWN;
01158                                                  goto out;
01159                                           }
01160 #endif
01161                                    } else {
01162                                           break;
01163                                    }
01164 
01165                                    if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
01166                                           err = PHP_ICONV_ERR_UNKNOWN;
01167                                           goto out;
01168                                    }
01169 
01170                                    out_reserved += 4;
01171                                    in_left = ini_in_left;
01172                                    in_p = ini_in_p;
01173                             }
01174 
01175                             prev_in_left = in_left;
01176 
01177                             encoded = (char *) php_base64_encode((unsigned char *) buf, (int)(out_size - out_left), &dummy);
01178                             encoded_len = (size_t)dummy;
01179 
01180                             if (char_cnt < encoded_len) {
01181                                    /* something went wrong! */
01182                                    err = PHP_ICONV_ERR_UNKNOWN;
01183                                    goto out;
01184                             }
01185 
01186                             smart_str_appendl(pretval, encoded, encoded_len);
01187                             char_cnt -= encoded_len;
01188                             smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
01189                             char_cnt -= 2;
01190 
01191                             efree(encoded);
01192                             encoded = NULL;
01193                      } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
01194 
01195                      case PHP_ICONV_ENC_SCHEME_QPRINT: {
01196                             size_t ini_in_left;
01197                             const char *ini_in_p;
01198                             const unsigned char *p;
01199                             size_t nbytes_required;
01200 
01201                             smart_str_appendc(pretval, 'Q');
01202                             char_cnt--;
01203                             smart_str_appendc(pretval, '?');
01204                             char_cnt--;
01205 
01206                             prev_in_left = ini_in_left = in_left;
01207                             ini_in_p = in_p;
01208 
01209                             for (out_size = (char_cnt - 2) / 3; out_size > 0;) {
01210                                    size_t prev_out_left;
01211 
01212                                    nbytes_required = 0;
01213 
01214                                    out_p = buf;
01215                                    out_left = out_size;
01216 
01217                                    if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
01218 #if ICONV_SUPPORTS_ERRNO
01219                                           switch (errno) {
01220                                                  case EINVAL:
01221                                                         err = PHP_ICONV_ERR_ILLEGAL_CHAR;
01222                                                         goto out;
01223 
01224                                                  case EILSEQ:
01225                                                         err = PHP_ICONV_ERR_ILLEGAL_SEQ;
01226                                                         goto out;
01227 
01228                                                  case E2BIG:
01229                                                         if (prev_in_left == in_left) {
01230                                                                err = PHP_ICONV_ERR_UNKNOWN;
01231                                                                goto out;
01232                                                         }
01233                                                         break;
01234        
01235                                                  default:
01236                                                         err = PHP_ICONV_ERR_UNKNOWN;
01237                                                         goto out;
01238                                           }
01239 #else
01240                                           if (prev_in_left == in_left) {
01241                                                  err = PHP_ICONV_ERR_UNKNOWN;
01242                                                  goto out;
01243                                           }
01244 #endif
01245                                    }
01246 
01247                                    prev_out_left = out_left;
01248                                    if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
01249 #if ICONV_SUPPORTS_ERRNO
01250                                           if (errno != E2BIG) {
01251                                                  err = PHP_ICONV_ERR_UNKNOWN;
01252                                                  goto out;
01253                                           }
01254 #else
01255                                           if (out_left == prev_out_left) {
01256                                                  err = PHP_ICONV_ERR_UNKNOWN;
01257                                                  goto out;
01258                                           }
01259 #endif
01260                                    }
01261 
01262                                    for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
01263                                           nbytes_required += qp_table[*p];
01264                                    }
01265 
01266                                    if (nbytes_required <= char_cnt - 2) {
01267                                           break;
01268                                    }
01269 
01270                                    out_size -= ((nbytes_required - (char_cnt - 2)) + 1) / 3;
01271                                    in_left = ini_in_left;
01272                                    in_p = ini_in_p;
01273                             }
01274 
01275                             for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
01276                                    if (qp_table[*p] == 1) {
01277                                           smart_str_appendc(pretval, *(char *)p);
01278                                           char_cnt--;
01279                                    } else {
01280                                           static char qp_digits[] = "0123456789ABCDEF";
01281                                           smart_str_appendc(pretval, '=');
01282                                           smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
01283                                           smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
01284                                           char_cnt -= 3;
01285                                    }
01286                             }
01287 
01288                             smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
01289                             char_cnt -= 2;
01290 
01291                             if (iconv(cd, NULL, NULL, NULL, NULL) == (size_t)-1) {
01292                                    err = PHP_ICONV_ERR_UNKNOWN;
01293                                    goto out;
01294                             }
01295 
01296                      } break; /* case PHP_ICONV_ENC_SCHEME_QPRINT: */
01297               }
01298        } while (in_left > 0);
01299 
01300        smart_str_0(pretval);
01301 
01302 out:
01303        if (cd != (iconv_t)(-1)) {
01304               iconv_close(cd);
01305        }
01306        if (cd_pl != (iconv_t)(-1)) {
01307               iconv_close(cd_pl);
01308        }
01309        if (encoded != NULL) {
01310               efree(encoded);
01311        }      
01312        if (buf != NULL) {
01313               efree(buf);
01314        }
01315        return err;
01316 }
01317 /* }}} */
01318 
01319 /* {{{ _php_iconv_mime_decode() */
01320 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)
01321 {
01322        php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
01323 
01324        iconv_t cd = (iconv_t)(-1), cd_pl = (iconv_t)(-1);
01325 
01326        const char *p1;
01327        size_t str_left;
01328        unsigned int scan_stat = 0;
01329        const char *csname = NULL;
01330        size_t csname_len; 
01331        const char *encoded_text = NULL;
01332        size_t encoded_text_len = 0;
01333        const char *encoded_word = NULL;
01334        const char *spaces = NULL;
01335 
01336        php_iconv_enc_scheme_t enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
01337 
01338        if (next_pos != NULL) {
01339               *next_pos = NULL;
01340        }
01341 
01342        cd_pl = iconv_open(enc, ICONV_ASCII_ENCODING);
01343 
01344        if (cd_pl == (iconv_t)(-1)) {
01345 #if ICONV_SUPPORTS_ERRNO
01346               if (errno == EINVAL) {
01347                      err = PHP_ICONV_ERR_WRONG_CHARSET;
01348               } else {
01349                      err = PHP_ICONV_ERR_CONVERTER;
01350               }
01351 #else
01352               err = PHP_ICONV_ERR_UNKNOWN;
01353 #endif
01354               goto out;
01355        }
01356 
01357        p1 = str;
01358        for (str_left = str_nbytes; str_left > 0; str_left--, p1++) {
01359               int eos = 0;
01360 
01361               switch (scan_stat) {
01362                      case 0: /* expecting any character */
01363                             switch (*p1) {
01364                                    case '\r': /* part of an EOL sequence? */
01365                                           scan_stat = 7;
01366                                           break;
01367 
01368                                    case '\n':
01369                                           scan_stat = 8;       
01370                                           break;
01371 
01372                                    case '=': /* first letter of an encoded chunk */
01373                                           encoded_word = p1;
01374                                           scan_stat = 1;
01375                                           break;
01376 
01377                                    case ' ': case '\t': /* a chunk of whitespaces */
01378                                           spaces = p1;
01379                                           scan_stat = 11;
01380                                           break;
01381 
01382                                    default: /* first letter of a non-encoded word */
01383                                           _php_iconv_appendc(pretval, *p1, cd_pl);
01384                                           encoded_word = NULL;
01385                                           if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01386                                                  scan_stat = 12;
01387                                           }
01388                                           break;
01389                             }
01390                             break;
01391 
01392                      case 1: /* expecting a delimiter */
01393                             if (*p1 != '?') {
01394                                    err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01395                                    if (err != PHP_ICONV_ERR_SUCCESS) {
01396                                           goto out;
01397                                    }
01398                                    encoded_word = NULL;
01399                                    if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01400                                           scan_stat = 12;
01401                                    } else {
01402                                           scan_stat = 0;
01403                                    }
01404                                    break;
01405                             }
01406                             csname = p1 + 1;
01407                             scan_stat = 2;
01408                             break;
01409                      
01410                      case 2: /* expecting a charset name */
01411                             switch (*p1) {
01412                                    case '?': /* normal delimiter: encoding scheme follows */
01413                                           scan_stat = 3;
01414                                           break;
01415 
01416                                    case '*': /* new style delimiter: locale id follows */
01417                                           scan_stat = 10;
01418                                           break;
01419                             } 
01420                             if (scan_stat != 2) {
01421                                    char tmpbuf[80];
01422 
01423                                    if (csname == NULL) {
01424                                           err = PHP_ICONV_ERR_MALFORMED;
01425                                           goto out;
01426                                    }
01427 
01428                                    csname_len = (size_t)(p1 - csname);
01429 
01430                                    if (csname_len > sizeof(tmpbuf) - 1) {
01431                                           if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01432                                                  err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01433                                                  if (err != PHP_ICONV_ERR_SUCCESS) {
01434                                                         goto out;
01435                                                  }
01436                                                  encoded_word = NULL;
01437                                                  if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01438                                                         scan_stat = 12;
01439                                                  } else {
01440                                                         scan_stat = 0;
01441                                                  }
01442                                                  break;
01443                                           } else {
01444                                                  err = PHP_ICONV_ERR_MALFORMED;
01445                                                  goto out;
01446                                           }
01447                                    }
01448 
01449                                    memcpy(tmpbuf, csname, csname_len);
01450                                    tmpbuf[csname_len] = '\0';
01451 
01452                                    if (cd != (iconv_t)(-1)) {
01453                                           iconv_close(cd);
01454                                    }
01455 
01456                                    cd = iconv_open(enc, tmpbuf);
01457 
01458                                    if (cd == (iconv_t)(-1)) {
01459                                           if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01460                                                  /* Bad character set, but the user wants us to
01461                                                   * press on. In this case, we'll just insert the
01462                                                   * undecoded encoded word, since there isn't really
01463                                                   * a more sensible behaviour available; the only
01464                                                   * other options are to swallow the encoded word
01465                                                   * entirely or decode it with an arbitrarily chosen
01466                                                   * single byte encoding, both of which seem to have
01467                                                   * a higher WTF factor than leaving it undecoded.
01468                                                   *
01469                                                   * Given this approach, we need to skip ahead to
01470                                                   * the end of the encoded word. */
01471                                                  int qmarks = 2;
01472                                                  while (qmarks > 0 && str_left > 1) {
01473                                                         if (*(++p1) == '?') {
01474                                                                --qmarks;
01475                                                         }
01476                                                         --str_left;
01477                                                  }
01478 
01479                                                  /* Look ahead to check for the terminating = that
01480                                                   * should be there as well; if it's there, we'll
01481                                                   * also include that. If it's not, there isn't much
01482                                                   * we can do at this point. */
01483                                                  if (*(p1 + 1) == '=') {
01484                                                         ++p1;
01485                                                         --str_left;
01486                                                  }
01487 
01488                                                  err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01489                                                  if (err != PHP_ICONV_ERR_SUCCESS) {
01490                                                         goto out;
01491                                                  }
01492 
01493                                                  /* Let's go back and see if there are further
01494                                                   * encoded words or bare content, and hope they
01495                                                   * might actually have a valid character set. */
01496                                                  scan_stat = 12;
01497                                                  break;
01498                                           } else {
01499 #if ICONV_SUPPORTS_ERRNO
01500                                                  if (errno == EINVAL) {
01501                                                         err = PHP_ICONV_ERR_WRONG_CHARSET;
01502                                                  } else {
01503                                                         err = PHP_ICONV_ERR_CONVERTER;
01504                                                  }
01505 #else
01506                                                  err = PHP_ICONV_ERR_UNKNOWN;
01507 #endif
01508                                                  goto out;
01509                                           }
01510                                    }
01511                             }
01512                             break;
01513 
01514                      case 3: /* expecting a encoding scheme specifier */
01515                             switch (*p1) {
01516                                    case 'b':
01517                                    case 'B':
01518                                           enc_scheme = PHP_ICONV_ENC_SCHEME_BASE64;
01519                                           scan_stat = 4;
01520                                           break;
01521 
01522                                    case 'q':
01523                                    case 'Q':
01524                                           enc_scheme = PHP_ICONV_ENC_SCHEME_QPRINT;
01525                                           scan_stat = 4;
01526                                           break;
01527 
01528                                    default:
01529                                           if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01530                                                  err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);
01531                                                  if (err != PHP_ICONV_ERR_SUCCESS) {
01532                                                         goto out;
01533                                                  }
01534                                                  encoded_word = NULL;
01535                                                  if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01536                                                         scan_stat = 12;
01537                                                  } else {
01538                                                         scan_stat = 0;
01539                                                  }
01540                                                  break;
01541                                           } else {
01542                                                  err = PHP_ICONV_ERR_MALFORMED;
01543                                                  goto out;
01544                                           }
01545                             }
01546                             break;
01547               
01548                      case 4: /* expecting a delimiter */
01549                             if (*p1 != '?') {
01550                                    if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01551                                           /* pass the entire chunk through the converter */
01552                                           err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01553                                           if (err != PHP_ICONV_ERR_SUCCESS) {
01554                                                  goto out;
01555                                           }
01556                                           encoded_word = NULL;
01557                                           if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01558                                                  scan_stat = 12;
01559                                           } else {
01560                                                  scan_stat = 0;
01561                                           }
01562                                           break;
01563                                    } else {
01564                                           err = PHP_ICONV_ERR_MALFORMED;
01565                                           goto out;
01566                                    }
01567                             }
01568                             encoded_text = p1 + 1;
01569                             scan_stat = 5;
01570                             break;
01571 
01572                      case 5: /* expecting an encoded portion */
01573                             if (*p1 == '?') {
01574                                    encoded_text_len = (size_t)(p1 - encoded_text);
01575                                    scan_stat = 6;
01576                             }
01577                             break;
01578 
01579                      case 7: /* expecting a "\n" character */
01580                             if (*p1 == '\n') {
01581                                    scan_stat = 8;
01582                             } else {
01583                                    /* bare CR */
01584                                    _php_iconv_appendc(pretval, '\r', cd_pl);
01585                                    _php_iconv_appendc(pretval, *p1, cd_pl);
01586                                    scan_stat = 0;
01587                             }
01588                             break;
01589 
01590                      case 8: /* checking whether the following line is part of a
01591                                       folded header */
01592                             if (*p1 != ' ' && *p1 != '\t') {
01593                                    --p1;
01594                                    str_left = 1; /* quit_loop */
01595                                    break;
01596                             }
01597                             if (encoded_word == NULL) {
01598                                    _php_iconv_appendc(pretval, ' ', cd_pl);
01599                             }
01600                             spaces = NULL;
01601                             scan_stat = 11;
01602                             break;
01603 
01604                      case 6: /* expecting a End-Of-Chunk character "=" */
01605                             if (*p1 != '=') {
01606                                    if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01607                                           /* pass the entire chunk through the converter */
01608                                           err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01609                                           if (err != PHP_ICONV_ERR_SUCCESS) {
01610                                                  goto out;
01611                                           }
01612                                           encoded_word = NULL;
01613                                           if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01614                                                  scan_stat = 12;
01615                                           } else {
01616                                                  scan_stat = 0;
01617                                           }
01618                                           break;
01619                                    } else {
01620                                           err = PHP_ICONV_ERR_MALFORMED;
01621                                           goto out;
01622                                    }
01623                             }
01624                             scan_stat = 9;
01625                             if (str_left == 1) {
01626                                    eos = 1;
01627                             } else {
01628                                    break;
01629                             }
01630 
01631                      case 9: /* choice point, seeing what to do next.*/
01632                             switch (*p1) {
01633                                    default:
01634                                           /* Handle non-RFC-compliant formats
01635                                            * 
01636                                            * RFC2047 requires the character that comes right
01637                                            * after an encoded word (chunk) to be a whitespace,
01638                                            * while there are lots of broken implementations that
01639                                            * generate such malformed headers that don't fulfill
01640                                            * that requirement.
01641                                            */ 
01642                                           if (!eos) { 
01643                                                  if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01644                                                         /* pass the entire chunk through the converter */
01645                                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01646                                                         if (err != PHP_ICONV_ERR_SUCCESS) {
01647                                                                goto out;
01648                                                         }
01649                                                         scan_stat = 12;
01650                                                         break;
01651                                                  }
01652                                           }
01653                                           /* break is omitted intentionally */
01654 
01655                                    case '\r': case '\n': case ' ': case '\t': {
01656                                           char *decoded_text;
01657                                           size_t decoded_text_len;
01658                                           int dummy;
01659 
01660                                           switch (enc_scheme) {
01661                                                  case PHP_ICONV_ENC_SCHEME_BASE64:
01662                                                         decoded_text = (char *)php_base64_decode((unsigned char*)encoded_text, (int)encoded_text_len, &dummy);
01663                                                         decoded_text_len = (size_t)dummy;
01664                                                         break;
01665 
01666                                                  case PHP_ICONV_ENC_SCHEME_QPRINT:
01667                                                         decoded_text = (char *)php_quot_print_decode((unsigned char*)encoded_text, (int)encoded_text_len, &decoded_text_len, 1);
01668                                                         break;
01669                                                  default:
01670                                                         decoded_text = NULL;
01671                                                         break;
01672                                           }
01673 
01674                                           if (decoded_text == NULL) {
01675                                                  if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01676                                                         /* pass the entire chunk through the converter */
01677                                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl); 
01678                                                         if (err != PHP_ICONV_ERR_SUCCESS) {
01679                                                                goto out;
01680                                                         }
01681                                                         encoded_word = NULL;
01682                                                         if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01683                                                                scan_stat = 12;
01684                                                         } else {
01685                                                                scan_stat = 0;
01686                                                         }
01687                                                         break;
01688                                                  } else {
01689                                                         err = PHP_ICONV_ERR_UNKNOWN;
01690                                                         goto out;
01691                                                  }
01692                                           }
01693 
01694                                           err = _php_iconv_appendl(pretval, decoded_text, decoded_text_len, cd);
01695                                           efree(decoded_text);
01696 
01697                                           if (err != PHP_ICONV_ERR_SUCCESS) {
01698                                                  if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01699                                                         /* pass the entire chunk through the converter */
01700                                                         err = _php_iconv_appendl(pretval, encoded_word, (size_t)(p1 - encoded_word), cd_pl); 
01701                                                         encoded_word = NULL;
01702                                                         if (err != PHP_ICONV_ERR_SUCCESS) {
01703                                                                break;
01704                                                         }
01705                                                  } else {
01706                                                         goto out;
01707                                                  }
01708                                           }
01709 
01710                                           if (eos) { /* reached end-of-string. done. */
01711                                                  scan_stat = 0;
01712                                                  break;
01713                                           }
01714 
01715                                           switch (*p1) {
01716                                                  case '\r': /* part of an EOL sequence? */
01717                                                         scan_stat = 7;
01718                                                         break;
01719 
01720                                                  case '\n':
01721                                                         scan_stat = 8;
01722                                                         break;
01723 
01724                                                  case '=': /* first letter of an encoded chunk */
01725                                                         scan_stat = 1;
01726                                                         break;
01727 
01728                                                  case ' ': case '\t': /* medial whitespaces */
01729                                                         spaces = p1;
01730                                                         scan_stat = 11;
01731                                                         break;
01732 
01733                                                  default: /* first letter of a non-encoded word */
01734                                                         _php_iconv_appendc(pretval, *p1, cd_pl);
01735                                                         scan_stat = 12;
01736                                                         break;
01737                                           }
01738                                    } break;
01739                             }
01740                             break;
01741 
01742                      case 10: /* expects a language specifier. dismiss it for now */
01743                             if (*p1 == '?') {
01744                                    scan_stat = 3;
01745                             }
01746                             break;
01747 
01748                      case 11: /* expecting a chunk of whitespaces */
01749                             switch (*p1) {
01750                                    case '\r': /* part of an EOL sequence? */
01751                                           scan_stat = 7;
01752                                           break;
01753 
01754                                    case '\n':
01755                                           scan_stat = 8;       
01756                                           break;
01757 
01758                                    case '=': /* first letter of an encoded chunk */
01759                                           if (spaces != NULL && encoded_word == NULL) {
01760                                                  _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
01761                                                  spaces = NULL;
01762                                           }
01763                                           encoded_word = p1;
01764                                           scan_stat = 1;
01765                                           break;
01766 
01767                                    case ' ': case '\t':
01768                                           break;
01769 
01770                                    default: /* first letter of a non-encoded word */
01771                                           if (spaces != NULL) {
01772                                                  _php_iconv_appendl(pretval, spaces, (size_t)(p1 - spaces), cd_pl);
01773                                                  spaces = NULL;
01774                                           }
01775                                           _php_iconv_appendc(pretval, *p1, cd_pl);
01776                                           encoded_word = NULL;
01777                                           if ((mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01778                                                  scan_stat = 12;
01779                                           } else {
01780                                                  scan_stat = 0;
01781                                           }
01782                                           break;
01783                             }
01784                             break;
01785 
01786                      case 12: /* expecting a non-encoded word */
01787                             switch (*p1) {
01788                                    case '\r': /* part of an EOL sequence? */
01789                                           scan_stat = 7;
01790                                           break;
01791 
01792                                    case '\n':
01793                                           scan_stat = 8;
01794                                           break;
01795 
01796                                    case ' ': case '\t':
01797                                           spaces = p1;
01798                                           scan_stat = 11;
01799                                           break;
01800 
01801                                    case '=': /* first letter of an encoded chunk */
01802                                           if (!(mode & PHP_ICONV_MIME_DECODE_STRICT)) {
01803                                                  encoded_word = p1;
01804                                                  scan_stat = 1;
01805                                                  break;
01806                                           }
01807                                           /* break is omitted intentionally */
01808 
01809                                    default:
01810                                           _php_iconv_appendc(pretval, *p1, cd_pl);
01811                                           break;
01812                             }
01813                             break;
01814               }
01815        }
01816        switch (scan_stat) {
01817               case 0: case 8: case 11: case 12:
01818                      break;
01819               default:
01820                      if ((mode & PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR)) {
01821                             if (scan_stat == 1) {
01822                                    _php_iconv_appendc(pretval, '=', cd_pl);
01823                             }
01824                             err = PHP_ICONV_ERR_SUCCESS;
01825                      } else {
01826                             err = PHP_ICONV_ERR_MALFORMED;
01827                             goto out;
01828                      }
01829        }
01830 
01831        if (next_pos != NULL) {
01832               *next_pos = p1;
01833        }
01834 
01835        smart_str_0(pretval);
01836 out:
01837        if (cd != (iconv_t)(-1)) {
01838               iconv_close(cd);
01839        }
01840        if (cd_pl != (iconv_t)(-1)) {
01841               iconv_close(cd_pl);
01842        }
01843        return err;
01844 }
01845 /* }}} */
01846 
01847 /* {{{ php_iconv_show_error() */
01848 static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset TSRMLS_DC)
01849 {
01850        switch (err) {
01851               case PHP_ICONV_ERR_SUCCESS:
01852                      break;
01853 
01854               case PHP_ICONV_ERR_CONVERTER:
01855                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Cannot open converter");
01856                      break;
01857 
01858               case PHP_ICONV_ERR_WRONG_CHARSET:
01859                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Wrong charset, conversion from `%s' to `%s' is not allowed",
01860                                in_charset, out_charset);
01861                      break;
01862 
01863               case PHP_ICONV_ERR_ILLEGAL_CHAR:
01864                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected an incomplete multibyte character in input string");
01865                      break;
01866 
01867               case PHP_ICONV_ERR_ILLEGAL_SEQ:
01868                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected an illegal character in input string");
01869                      break;
01870 
01871               case PHP_ICONV_ERR_TOO_BIG:
01872                      /* should not happen */
01873                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Buffer length exceeded");
01874                      break;
01875 
01876               case PHP_ICONV_ERR_MALFORMED:
01877                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Malformed string");
01878                      break;
01879 
01880               default:
01881                      /* other error */
01882                      php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unknown error (%d)", errno);
01883                      break;
01884        }
01885 }
01886 /* }}} */
01887 
01888 /* {{{ proto int iconv_strlen(string str [, string charset])
01889    Returns the character count of str */
01890 PHP_FUNCTION(iconv_strlen)
01891 {
01892        char *charset = ICONVG(internal_encoding);
01893        int charset_len = 0;
01894        char *str;
01895        int str_len; 
01896 
01897        php_iconv_err_t err;
01898 
01899        unsigned int retval;
01900 
01901        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
01902               &str, &str_len, &charset, &charset_len) == FAILURE) {
01903               RETURN_FALSE;
01904        }
01905 
01906        if (charset_len >= ICONV_CSNMAXLEN) {
01907               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
01908               RETURN_FALSE;
01909        }
01910 
01911        err = _php_iconv_strlen(&retval, str, str_len, charset); 
01912        _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
01913        if (err == PHP_ICONV_ERR_SUCCESS) {
01914               RETVAL_LONG(retval);
01915        } else {
01916               RETVAL_FALSE;
01917        }
01918 }
01919 /* }}} */
01920 
01921 /* {{{ proto string iconv_substr(string str, int offset, [int length, string charset])
01922    Returns specified part of a string */
01923 PHP_FUNCTION(iconv_substr)
01924 {
01925        char *charset = ICONVG(internal_encoding);
01926        int charset_len = 0;
01927        char *str;
01928        int str_len; 
01929        long offset, length = 0;
01930 
01931        php_iconv_err_t err;
01932 
01933        smart_str retval = {0};
01934 
01935        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|ls",
01936               &str, &str_len, &offset, &length,
01937               &charset, &charset_len) == FAILURE) {
01938               RETURN_FALSE;
01939        }
01940 
01941        if (charset_len >= ICONV_CSNMAXLEN) {
01942               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
01943               RETURN_FALSE;
01944        }
01945 
01946        if (ZEND_NUM_ARGS() < 3) {
01947               length = str_len; 
01948        }
01949 
01950        err = _php_iconv_substr(&retval, str, str_len, offset, length, charset); 
01951        _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
01952 
01953        if (err == PHP_ICONV_ERR_SUCCESS && str != NULL && retval.c != NULL) {
01954               RETURN_STRINGL(retval.c, retval.len, 0);
01955        }
01956        smart_str_free(&retval);
01957        RETURN_FALSE;
01958 }
01959 /* }}} */
01960 
01961 /* {{{ proto int iconv_strpos(string haystack, string needle [, int offset [, string charset]])
01962    Finds position of first occurrence of needle within part of haystack beginning with offset */
01963 PHP_FUNCTION(iconv_strpos)
01964 {
01965        char *charset = ICONVG(internal_encoding);
01966        int charset_len = 0;
01967        char *haystk;
01968        int haystk_len; 
01969        char *ndl;
01970        int ndl_len;
01971        long offset = 0;
01972 
01973        php_iconv_err_t err;
01974 
01975        unsigned int retval;
01976 
01977        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ls",
01978               &haystk, &haystk_len, &ndl, &ndl_len,
01979               &offset, &charset, &charset_len) == FAILURE) {
01980               RETURN_FALSE;
01981        }
01982 
01983        if (charset_len >= ICONV_CSNMAXLEN) {
01984               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
01985               RETURN_FALSE;
01986        }
01987 
01988        if (offset < 0) {
01989               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string.");
01990               RETURN_FALSE;
01991        }
01992 
01993        if (ndl_len < 1) {
01994               RETURN_FALSE;
01995        }
01996 
01997        err = _php_iconv_strpos(&retval, haystk, haystk_len, ndl, ndl_len,
01998                                offset, charset); 
01999        _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
02000 
02001        if (err == PHP_ICONV_ERR_SUCCESS && retval != (unsigned int)-1) {
02002               RETVAL_LONG((long)retval);
02003        } else {
02004               RETVAL_FALSE;
02005        }
02006 }
02007 /* }}} */
02008 
02009 /* {{{ proto int iconv_strrpos(string haystack, string needle [, string charset])
02010    Finds position of last occurrence of needle within part of haystack beginning with offset */
02011 PHP_FUNCTION(iconv_strrpos)
02012 {
02013        char *charset = ICONVG(internal_encoding);
02014        int charset_len = 0;
02015        char *haystk;
02016        int haystk_len; 
02017        char *ndl;
02018        int ndl_len;
02019 
02020        php_iconv_err_t err;
02021 
02022        unsigned int retval;
02023 
02024        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s",
02025               &haystk, &haystk_len, &ndl, &ndl_len,
02026               &charset, &charset_len) == FAILURE) {
02027               RETURN_FALSE;
02028        }
02029 
02030        if (ndl_len < 1) {
02031               RETURN_FALSE;
02032        }
02033 
02034        if (charset_len >= ICONV_CSNMAXLEN) {
02035               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02036               RETURN_FALSE;
02037        }
02038 
02039        err = _php_iconv_strpos(&retval, haystk, haystk_len, ndl, ndl_len,
02040                                -1, charset); 
02041        _php_iconv_show_error(err, GENERIC_SUPERSET_NAME, charset TSRMLS_CC);
02042 
02043        if (err == PHP_ICONV_ERR_SUCCESS && retval != (unsigned int)-1) {
02044               RETVAL_LONG((long)retval);
02045        } else {
02046               RETVAL_FALSE;
02047        }
02048 }
02049 /* }}} */
02050 
02051 /* {{{ proto string iconv_mime_encode(string field_name, string field_value [, array preference])
02052    Composes a mime header field with field_name and field_value in a specified scheme */
02053 PHP_FUNCTION(iconv_mime_encode)
02054 {
02055        const char *field_name = NULL;
02056        int field_name_len;
02057        const char *field_value = NULL;
02058        int field_value_len;
02059        zval *pref = NULL;
02060        zval tmp_zv, *tmp_zv_p = NULL;
02061        smart_str retval = {0};
02062        php_iconv_err_t err;
02063 
02064        const char *in_charset = ICONVG(internal_encoding);
02065        const char *out_charset = in_charset;
02066        long line_len = 76;
02067        const char *lfchars = "\r\n";
02068        php_iconv_enc_scheme_t scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
02069 
02070        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a",
02071               &field_name, &field_name_len, &field_value, &field_value_len,
02072               &pref) == FAILURE) {
02073 
02074               RETURN_FALSE;
02075        }
02076 
02077        if (pref != NULL) {
02078               zval **ppval;
02079 
02080               if (zend_hash_find(Z_ARRVAL_P(pref), "scheme", sizeof("scheme"), (void **)&ppval) == SUCCESS) {
02081                      if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
02082                             switch (Z_STRVAL_PP(ppval)[0]) {
02083                                    case 'B': case 'b':
02084                                           scheme_id = PHP_ICONV_ENC_SCHEME_BASE64;
02085                                           break;
02086 
02087                                    case 'Q': case 'q':
02088                                           scheme_id = PHP_ICONV_ENC_SCHEME_QPRINT;
02089                                           break;
02090                             }
02091                      }
02092               }
02093 
02094               if (zend_hash_find(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset"), (void **)&ppval) == SUCCESS) {
02095                      if (Z_STRLEN_PP(ppval) >= ICONV_CSNMAXLEN) {
02096                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02097                             RETURN_FALSE;
02098                      }
02099 
02100                      if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
02101                             in_charset = Z_STRVAL_PP(ppval);
02102                      }
02103               }
02104 
02105 
02106               if (zend_hash_find(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset"), (void **)&ppval) == SUCCESS) {
02107                      if (Z_STRLEN_PP(ppval) >= ICONV_CSNMAXLEN) {
02108                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02109                             RETURN_FALSE;
02110                      }
02111 
02112                      if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
02113                             out_charset = Z_STRVAL_PP(ppval);
02114                      }
02115               }
02116 
02117               if (zend_hash_find(Z_ARRVAL_P(pref), "line-length", sizeof("line-length"), (void **)&ppval) == SUCCESS) {
02118                      zval val, *pval = *ppval;
02119 
02120                      if (Z_TYPE_P(pval) != IS_LONG) {
02121                             val = *pval;
02122                             zval_copy_ctor(&val);
02123                             convert_to_long(&val);
02124                             pval = &val;
02125                      }
02126 
02127                      line_len = Z_LVAL_P(pval);
02128 
02129                      if (pval == &val) {
02130                             zval_dtor(&val);
02131                      }
02132               }
02133 
02134               if (zend_hash_find(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars"), (void **)&ppval) == SUCCESS) {
02135                      if (Z_TYPE_PP(ppval) != IS_STRING) {
02136                             tmp_zv = **ppval;
02137                             zval_copy_ctor(&tmp_zv);
02138                             convert_to_string(&tmp_zv);
02139 
02140                             lfchars = Z_STRVAL(tmp_zv);
02141 
02142                             tmp_zv_p = &tmp_zv;
02143                      } else {
02144                             lfchars = Z_STRVAL_PP(ppval);
02145                      }
02146               }
02147        }
02148 
02149        err = _php_iconv_mime_encode(&retval, field_name, field_name_len,
02150               field_value, field_value_len, line_len, lfchars, scheme_id,
02151               out_charset, in_charset);
02152        _php_iconv_show_error(err, out_charset, in_charset TSRMLS_CC);
02153 
02154        if (err == PHP_ICONV_ERR_SUCCESS) {
02155               if (retval.c != NULL) {
02156                      RETVAL_STRINGL(retval.c, retval.len, 0);
02157               } else {
02158                      RETVAL_EMPTY_STRING();
02159               }
02160        } else {
02161               smart_str_free(&retval);
02162               RETVAL_FALSE;
02163        }
02164 
02165        if (tmp_zv_p != NULL) {
02166               zval_dtor(tmp_zv_p);
02167        }
02168 }
02169 /* }}} */
02170 
02171 /* {{{ proto string iconv_mime_decode(string encoded_string [, int mode, string charset])
02172    Decodes a mime header field */
02173 PHP_FUNCTION(iconv_mime_decode)
02174 {
02175        char *encoded_str;
02176        int encoded_str_len;
02177        char *charset = ICONVG(internal_encoding);
02178        int charset_len = 0;
02179        long mode = 0;
02180        
02181        smart_str retval = {0};
02182 
02183        php_iconv_err_t err;
02184 
02185        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls",
02186               &encoded_str, &encoded_str_len, &mode, &charset, &charset_len) == FAILURE) {
02187 
02188               RETURN_FALSE;
02189        }
02190 
02191        if (charset_len >= ICONV_CSNMAXLEN) {
02192               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02193               RETURN_FALSE;
02194        }
02195 
02196        err = _php_iconv_mime_decode(&retval, encoded_str, encoded_str_len, charset, NULL, mode);
02197        _php_iconv_show_error(err, charset, "???" TSRMLS_CC);
02198 
02199        if (err == PHP_ICONV_ERR_SUCCESS) {
02200               if (retval.c != NULL) {
02201                      RETVAL_STRINGL(retval.c, retval.len, 0);
02202               } else {
02203                      RETVAL_EMPTY_STRING();
02204               }
02205        } else {
02206               smart_str_free(&retval);
02207               RETVAL_FALSE;
02208        }
02209 }
02210 /* }}} */
02211 
02212 /* {{{ proto array iconv_mime_decode_headers(string headers [, int mode, string charset])
02213    Decodes multiple mime header fields */
02214 PHP_FUNCTION(iconv_mime_decode_headers)
02215 {
02216        const char *encoded_str;
02217        int encoded_str_len;
02218        char *charset = ICONVG(internal_encoding);
02219        int charset_len = 0;
02220        long mode = 0;
02221        
02222        php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
02223 
02224        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ls",
02225               &encoded_str, &encoded_str_len, &mode, &charset, &charset_len) == FAILURE) {
02226 
02227               RETURN_FALSE;
02228        }
02229 
02230        if (charset_len >= ICONV_CSNMAXLEN) {
02231               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02232               RETURN_FALSE;
02233        }
02234 
02235        array_init(return_value);
02236 
02237        while (encoded_str_len > 0) {
02238               smart_str decoded_header = {0};
02239               char *header_name = NULL;
02240               size_t header_name_len = 0;
02241               char *header_value = NULL;
02242               size_t header_value_len = 0;
02243               char *p, *limit;
02244               const char *next_pos;
02245 
02246               if (PHP_ICONV_ERR_SUCCESS != (err = _php_iconv_mime_decode(&decoded_header, encoded_str, encoded_str_len, charset, &next_pos, mode))) {
02247                      smart_str_free(&decoded_header);
02248                      break;
02249               }
02250 
02251               if (decoded_header.c == NULL) {
02252                      break;
02253               }
02254 
02255               limit = decoded_header.c + decoded_header.len;
02256               for (p = decoded_header.c; p < limit; p++) {
02257                      if (*p == ':') {
02258                             *p = '\0';
02259                             header_name = decoded_header.c;
02260                             header_name_len = (p - decoded_header.c) + 1;
02261 
02262                             while (++p < limit) {
02263                                    if (*p != ' ' && *p != '\t') {
02264                                           break;
02265                                    }
02266                             }
02267 
02268                             header_value = p;
02269                             header_value_len = limit - p;
02270 
02271                             break;
02272                      }
02273               }
02274 
02275               if (header_name != NULL) {
02276                      zval **elem, *new_elem;
02277 
02278                      if (zend_hash_find(Z_ARRVAL_P(return_value), header_name, header_name_len, (void **)&elem) == SUCCESS) {
02279                             if (Z_TYPE_PP(elem) != IS_ARRAY) {
02280                                    MAKE_STD_ZVAL(new_elem);
02281                                    array_init(new_elem);
02282 
02283                                    Z_ADDREF_PP(elem);
02284                                    add_next_index_zval(new_elem, *elem);
02285 
02286                                    zend_hash_update(Z_ARRVAL_P(return_value), header_name, header_name_len, (void *)&new_elem, sizeof(new_elem), NULL);
02287 
02288                                    elem = &new_elem;
02289                             }      
02290                             add_next_index_stringl(*elem, header_value, header_value_len, 1);
02291                      } else {
02292                             add_assoc_stringl_ex(return_value, header_name, header_name_len, header_value, header_value_len, 1);
02293                      }
02294               }
02295               encoded_str_len -= next_pos - encoded_str;
02296               encoded_str = next_pos;     
02297 
02298               smart_str_free(&decoded_header);
02299        }
02300 
02301        if (err != PHP_ICONV_ERR_SUCCESS) {
02302               _php_iconv_show_error(err, charset, "???" TSRMLS_CC);
02303               zval_dtor(return_value);
02304               RETVAL_FALSE;
02305        }
02306 }
02307 /* }}} */
02308 
02309 /* {{{ proto string iconv(string in_charset, string out_charset, string str)
02310    Returns str converted to the out_charset character set */
02311 PHP_NAMED_FUNCTION(php_if_iconv)
02312 {
02313        char *in_charset, *out_charset, *in_buffer, *out_buffer;
02314        size_t out_len;
02315        int in_charset_len = 0, out_charset_len = 0, in_buffer_len;
02316        php_iconv_err_t err;
02317        
02318        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss",
02319               &in_charset, &in_charset_len, &out_charset, &out_charset_len, &in_buffer, &in_buffer_len) == FAILURE)
02320               return;
02321 
02322        if (in_charset_len >= ICONV_CSNMAXLEN || out_charset_len >= ICONV_CSNMAXLEN) {
02323               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02324               RETURN_FALSE;
02325        }
02326 
02327        err = php_iconv_string(in_buffer, (size_t)in_buffer_len,
02328               &out_buffer, &out_len, out_charset, in_charset);
02329        _php_iconv_show_error(err, out_charset, in_charset TSRMLS_CC); 
02330        if (out_buffer != NULL) {
02331               RETVAL_STRINGL(out_buffer, out_len, 0);
02332        } else {
02333               RETURN_FALSE;
02334        }
02335 }
02336 /* }}} */
02337 
02338 /* {{{ proto string ob_iconv_handler(string contents, int status)
02339    Returns str in output buffer converted to the iconv.output_encoding character set */
02340 PHP_FUNCTION(ob_iconv_handler)
02341 {
02342        char *out_buffer, *content_type, *mimetype = NULL, *s;
02343        zval *zv_string;
02344        size_t out_len;
02345        int mimetype_alloced  = 0;
02346        long status;
02347 
02348        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zl", &zv_string, &status) == FAILURE)
02349               return;
02350 
02351        convert_to_string(zv_string);
02352 
02353        if (SG(sapi_headers).mimetype && 
02354               strncasecmp(SG(sapi_headers).mimetype, "text/", 5) == 0) {
02355               if ((s = strchr(SG(sapi_headers).mimetype,';')) == NULL){
02356                      mimetype = SG(sapi_headers).mimetype;
02357               } else {
02358                      mimetype = estrndup(SG(sapi_headers).mimetype, s-SG(sapi_headers).mimetype);
02359                      mimetype_alloced = 1;
02360               }
02361        } else if (SG(sapi_headers).send_default_content_type) {
02362               mimetype =(SG(default_mimetype) ? SG(default_mimetype) : SAPI_DEFAULT_MIMETYPE);
02363        }
02364        if (mimetype != NULL) {
02365               php_iconv_err_t err = php_iconv_string(Z_STRVAL_P(zv_string),
02366                             Z_STRLEN_P(zv_string), &out_buffer, &out_len,
02367                             ICONVG(output_encoding), ICONVG(internal_encoding));
02368               _php_iconv_show_error(err, ICONVG(output_encoding), ICONVG(internal_encoding) TSRMLS_CC);
02369               if (out_buffer != NULL) {
02370                      int len;
02371                      char *p = strstr(ICONVG(output_encoding), "//"); 
02372                      if (p) {
02373                             len = spprintf(&content_type, 0, "Content-Type:%s; charset=%.*s", mimetype, (int)(p - ICONVG(output_encoding)), ICONVG(output_encoding));
02374                      } else {
02375                             len = spprintf(&content_type, 0, "Content-Type:%s; charset=%s", mimetype, ICONVG(output_encoding));
02376                      }
02377                      if (content_type && sapi_add_header(content_type, len, 0) != FAILURE) {
02378                             SG(sapi_headers).send_default_content_type = 0;
02379                      }
02380                      if (mimetype_alloced) {
02381                             efree(mimetype);
02382                      }
02383                      RETURN_STRINGL(out_buffer, out_len, 0);
02384               }
02385               if (mimetype_alloced) {
02386                      efree(mimetype);
02387               }
02388        }
02389 
02390        zval_dtor(return_value);
02391        *return_value = *zv_string;
02392        zval_copy_ctor(return_value);
02393 }
02394 /* }}} */
02395 
02396 /* {{{ proto bool iconv_set_encoding(string type, string charset)
02397    Sets internal encoding and output encoding for ob_iconv_handler() */
02398 PHP_FUNCTION(iconv_set_encoding)
02399 {
02400        char *type, *charset;
02401        int type_len, charset_len =0, retval;
02402 
02403        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &type, &type_len, &charset, &charset_len) == FAILURE)
02404               return;
02405 
02406        if (charset_len >= ICONV_CSNMAXLEN) {
02407               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Charset parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
02408               RETURN_FALSE;
02409        }
02410 
02411        if(!strcasecmp("input_encoding", type)) {
02412               retval = zend_alter_ini_entry("iconv.input_encoding", sizeof("iconv.input_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
02413        } else if(!strcasecmp("output_encoding", type)) {
02414               retval = zend_alter_ini_entry("iconv.output_encoding", sizeof("iconv.output_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
02415        } else if(!strcasecmp("internal_encoding", type)) {
02416               retval = zend_alter_ini_entry("iconv.internal_encoding", sizeof("iconv.internal_encoding"), charset, charset_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
02417        } else {
02418               RETURN_FALSE;
02419        }
02420 
02421        if (retval == SUCCESS) {
02422               RETURN_TRUE;
02423        } else {
02424               RETURN_FALSE;
02425        }
02426 }
02427 /* }}} */
02428 
02429 /* {{{ proto mixed iconv_get_encoding([string type])
02430    Get internal encoding and output encoding for ob_iconv_handler() */
02431 PHP_FUNCTION(iconv_get_encoding)
02432 {
02433        char *type = "all";
02434        int type_len = sizeof("all")-1;
02435 
02436        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &type, &type_len) == FAILURE)
02437               return;
02438 
02439        if (!strcasecmp("all", type)) {
02440               array_init(return_value);
02441               add_assoc_string(return_value, "input_encoding",    ICONVG(input_encoding), 1);
02442               add_assoc_string(return_value, "output_encoding",   ICONVG(output_encoding), 1);
02443               add_assoc_string(return_value, "internal_encoding", ICONVG(internal_encoding), 1);
02444        } else if (!strcasecmp("input_encoding", type)) {
02445               RETVAL_STRING(ICONVG(input_encoding), 1);
02446        } else if (!strcasecmp("output_encoding", type)) {
02447               RETVAL_STRING(ICONVG(output_encoding), 1);
02448        } else if (!strcasecmp("internal_encoding", type)) {
02449               RETVAL_STRING(ICONVG(internal_encoding), 1);
02450        } else {
02451               RETURN_FALSE;
02452        }
02453 
02454 }
02455 /* }}} */
02456 
02457 /* {{{ iconv stream filter */
02458 typedef struct _php_iconv_stream_filter {
02459        iconv_t cd;
02460        int persistent;
02461        char *to_charset;
02462        size_t to_charset_len;
02463        char *from_charset;
02464        size_t from_charset_len;
02465        char stub[128];
02466        size_t stub_len;
02467 } php_iconv_stream_filter;
02468 /* }}} iconv stream filter */
02469 
02470 /* {{{ php_iconv_stream_filter_dtor */
02471 static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self)
02472 {
02473        iconv_close(self->cd);
02474        pefree(self->to_charset, self->persistent);
02475        pefree(self->from_charset, self->persistent);
02476 }
02477 /* }}} */
02478 
02479 /* {{{ php_iconv_stream_filter_ctor() */
02480 static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self,
02481               const char *to_charset, size_t to_charset_len,
02482               const char *from_charset, size_t from_charset_len, int persistent)
02483 {
02484        if (NULL == (self->to_charset = pemalloc(to_charset_len + 1, persistent))) {
02485               return PHP_ICONV_ERR_ALLOC;
02486        }
02487        self->to_charset_len = to_charset_len;
02488        if (NULL == (self->from_charset = pemalloc(from_charset_len + 1, persistent))) {
02489               pefree(self->to_charset, persistent);
02490               return PHP_ICONV_ERR_ALLOC;
02491        }
02492        self->from_charset_len = from_charset_len;
02493 
02494        memcpy(self->to_charset, to_charset, to_charset_len);
02495        self->to_charset[to_charset_len] = '\0';
02496        memcpy(self->from_charset, from_charset, from_charset_len);
02497        self->from_charset[from_charset_len] = '\0';
02498 
02499        if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) {
02500               pefree(self->from_charset, persistent);
02501               pefree(self->to_charset, persistent);
02502               return PHP_ICONV_ERR_UNKNOWN;
02503        }
02504        self->persistent = persistent;
02505        self->stub_len = 0;
02506        return PHP_ICONV_ERR_SUCCESS;
02507 }
02508 /* }}} */
02509 
02510 /* {{{ php_iconv_stream_filter_append_bucket */
02511 static int php_iconv_stream_filter_append_bucket(
02512               php_iconv_stream_filter *self,
02513               php_stream *stream, php_stream_filter *filter,
02514               php_stream_bucket_brigade *buckets_out,
02515               const char *ps, size_t buf_len, size_t *consumed,
02516               int persistent TSRMLS_DC)
02517 {
02518        php_stream_bucket *new_bucket;
02519        char *out_buf = NULL;
02520        size_t out_buf_size;
02521        char *pd, *pt;
02522        size_t ocnt, prev_ocnt, icnt, tcnt;
02523        size_t initial_out_buf_size;
02524        
02525        if (ps == NULL) {
02526               initial_out_buf_size = 64;
02527               icnt = 1;
02528        } else {
02529               initial_out_buf_size = buf_len;
02530               icnt = buf_len;
02531        }
02532 
02533        out_buf_size = ocnt = prev_ocnt = initial_out_buf_size; 
02534        if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
02535               return FAILURE;
02536        }
02537 
02538        pd = out_buf;
02539 
02540        if (self->stub_len > 0) {
02541               pt = self->stub;
02542               tcnt = self->stub_len;
02543 
02544               while (tcnt > 0) {
02545                      if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
02546 #if ICONV_SUPPORTS_ERRNO
02547                             switch (errno) {
02548                                    case EILSEQ:
02549                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
02550                                           goto out_failure;
02551 
02552                                    case EINVAL:
02553                                           if (ps != NULL) {
02554                                                  if (icnt > 0) {
02555                                                         if (self->stub_len >= sizeof(self->stub)) {
02556                                                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
02557                                                                goto out_failure;
02558                                                         }
02559                                                         self->stub[self->stub_len++] = *(ps++);
02560                                                         icnt--;
02561                                                         pt = self->stub;
02562                                                         tcnt = self->stub_len;
02563                                                  } else {
02564                                                         tcnt = 0;
02565                                                         break;
02566                                                  }
02567                                           }
02568                                           break;
02569 
02570                                    case E2BIG: {
02571                                           char *new_out_buf;
02572                                           size_t new_out_buf_size;
02573 
02574                                           new_out_buf_size = out_buf_size << 1;
02575 
02576                                           if (new_out_buf_size < out_buf_size) {
02577                                                  /* whoa! no bigger buckets are sold anywhere... */
02578                                                  if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
02579                                                         goto out_failure;
02580                                                  }
02581 
02582                                                  php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
02583 
02584                                                  out_buf_size = ocnt = initial_out_buf_size;
02585                                                  if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
02586                                                         return FAILURE;
02587                                                  }
02588                                                  pd = out_buf;
02589                                           } else {
02590                                                  if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
02591                                                         if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
02592                                                                goto out_failure;
02593                                                         }
02594 
02595                                                         php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
02596                                                         return FAILURE;
02597                                                  }
02598                                                  pd = new_out_buf + (pd - out_buf);
02599                                                  ocnt += (new_out_buf_size - out_buf_size);
02600                                                  out_buf = new_out_buf;
02601                                                  out_buf_size = new_out_buf_size;
02602                                           }
02603                                    } break;
02604 
02605                                    default:
02606                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
02607                                           goto out_failure;
02608                             }
02609 #else
02610                             if (ocnt == prev_ocnt) {
02611                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
02612                                    goto out_failure;
02613                             }
02614 #endif
02615                      }
02616                      prev_ocnt = ocnt;
02617               }
02618               memmove(self->stub, pt, tcnt);
02619               self->stub_len = tcnt;
02620        }
02621 
02622        while (icnt > 0) {
02623               if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
02624                                    iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) {
02625 #if ICONV_SUPPORTS_ERRNO
02626                      switch (errno) {
02627                             case EILSEQ:
02628                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
02629                                    goto out_failure;
02630 
02631                             case EINVAL:
02632                                    if (ps != NULL) {
02633                                           if (icnt > sizeof(self->stub)) {
02634                                                  php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
02635                                                  goto out_failure;
02636                                           }
02637                                           memcpy(self->stub, ps, icnt);
02638                                           self->stub_len = icnt;
02639                                           ps += icnt;
02640                                           icnt = 0;
02641                                    } else {
02642                                           php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset);
02643                                           goto out_failure;
02644                                    }
02645                                    break;
02646 
02647                             case E2BIG: {
02648                                    char *new_out_buf;
02649                                    size_t new_out_buf_size;
02650 
02651                                    new_out_buf_size = out_buf_size << 1;
02652 
02653                                    if (new_out_buf_size < out_buf_size) {
02654                                           /* whoa! no bigger buckets are sold anywhere... */
02655                                           if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
02656                                                  goto out_failure;
02657                                           }
02658 
02659                                           php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
02660 
02661                                           out_buf_size = ocnt = initial_out_buf_size;
02662                                           if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
02663                                                  return FAILURE;
02664                                           }
02665                                           pd = out_buf;
02666                                    } else {
02667                                           if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
02668                                                  if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
02669                                                         goto out_failure;
02670                                                  }
02671 
02672                                                  php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
02673                                                  return FAILURE;
02674                                           }
02675                                           pd = new_out_buf + (pd - out_buf);
02676                                           ocnt += (new_out_buf_size - out_buf_size);
02677                                           out_buf = new_out_buf;
02678                                           out_buf_size = new_out_buf_size;
02679                                    }
02680                             } break;
02681 
02682                             default:
02683                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
02684                                    goto out_failure;
02685                      }
02686 #else
02687                      if (ocnt == prev_ocnt) {
02688                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
02689                             goto out_failure;
02690                      }
02691 #endif
02692               } else {
02693                      if (ps == NULL) {
02694                             break;
02695                      }
02696               }
02697               prev_ocnt = ocnt;
02698        }
02699 
02700        if (out_buf_size - ocnt > 0) {
02701               if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
02702                      goto out_failure;
02703               }
02704               php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
02705        } else {
02706               pefree(out_buf, persistent);
02707        }
02708        *consumed += buf_len - icnt;
02709 
02710        return SUCCESS;
02711 
02712 out_failure:
02713        pefree(out_buf, persistent);
02714        return FAILURE;
02715 }
02716 /* }}} php_iconv_stream_filter_append_bucket */
02717 
02718 /* {{{ php_iconv_stream_filter_do_filter */
02719 static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
02720               php_stream *stream, php_stream_filter *filter,
02721               php_stream_bucket_brigade *buckets_in,
02722               php_stream_bucket_brigade *buckets_out,
02723               size_t *bytes_consumed, int flags TSRMLS_DC)
02724 {
02725        php_stream_bucket *bucket = NULL;
02726        size_t consumed = 0;
02727        php_iconv_stream_filter *self = (php_iconv_stream_filter *)filter->abstract;
02728 
02729        while (buckets_in->head != NULL) {
02730               bucket = buckets_in->head;
02731 
02732               php_stream_bucket_unlink(bucket TSRMLS_CC);
02733 
02734               if (php_iconv_stream_filter_append_bucket(self, stream, filter,
02735                             buckets_out, bucket->buf, bucket->buflen, &consumed,
02736                             php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
02737                      goto out_failure;
02738               }
02739 
02740               php_stream_bucket_delref(bucket TSRMLS_CC);
02741        }
02742 
02743        if (flags != PSFS_FLAG_NORMAL) {
02744               if (php_iconv_stream_filter_append_bucket(self, stream, filter,
02745                             buckets_out, NULL, 0, &consumed,
02746                             php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
02747                      goto out_failure;
02748               }
02749        }
02750 
02751        if (bytes_consumed != NULL) {
02752               *bytes_consumed = consumed;
02753        }
02754 
02755        return PSFS_PASS_ON;
02756 
02757 out_failure:
02758        if (bucket != NULL) {
02759               php_stream_bucket_delref(bucket TSRMLS_CC);
02760        }
02761        return PSFS_ERR_FATAL;
02762 }
02763 /* }}} */
02764 
02765 /* {{{ php_iconv_stream_filter_cleanup */
02766 static void php_iconv_stream_filter_cleanup(php_stream_filter *filter TSRMLS_DC)
02767 {
02768        php_iconv_stream_filter_dtor((php_iconv_stream_filter *)filter->abstract);
02769        pefree(filter->abstract, ((php_iconv_stream_filter *)filter->abstract)->persistent);
02770 }
02771 /* }}} */
02772 
02773 static php_stream_filter_ops php_iconv_stream_filter_ops = {
02774        php_iconv_stream_filter_do_filter,
02775        php_iconv_stream_filter_cleanup,
02776        "convert.iconv.*"
02777 };
02778 
02779 /* {{{ php_iconv_stream_filter_create */
02780 static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, int persistent TSRMLS_DC)
02781 {
02782        php_stream_filter *retval = NULL;
02783        php_iconv_stream_filter *inst;
02784        char *from_charset = NULL, *to_charset = NULL;
02785        size_t from_charset_len, to_charset_len;
02786 
02787        if ((from_charset = strchr(name, '.')) == NULL) {
02788               return NULL;
02789        }
02790        ++from_charset;
02791        if ((from_charset = strchr(from_charset, '.')) == NULL) {
02792               return NULL;
02793        }
02794        ++from_charset;
02795        if ((to_charset = strpbrk(from_charset, "/.")) == NULL) {
02796               return NULL;
02797        }
02798        from_charset_len = to_charset - from_charset;
02799        ++to_charset;
02800        to_charset_len = strlen(to_charset);
02801 
02802        if (from_charset_len >= ICONV_CSNMAXLEN || to_charset_len >= ICONV_CSNMAXLEN) {
02803               return NULL;
02804        }
02805 
02806        if (NULL == (inst = pemalloc(sizeof(php_iconv_stream_filter), persistent))) {
02807               return NULL;
02808        }
02809 
02810        if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) {
02811               pefree(inst, persistent);
02812               return NULL;
02813        }
02814 
02815        if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) {
02816               php_iconv_stream_filter_dtor(inst);
02817               pefree(inst, persistent);
02818        }
02819 
02820        return retval;       
02821 }
02822 /* }}} */
02823 
02824 /* {{{ php_iconv_stream_register_factory */
02825 static php_iconv_err_t php_iconv_stream_filter_register_factory(TSRMLS_D)
02826 {
02827        static php_stream_filter_factory filter_factory = {
02828               php_iconv_stream_filter_factory_create
02829        };
02830 
02831        if (FAILURE == php_stream_filter_register_factory(
02832                             php_iconv_stream_filter_ops.label,
02833                             &filter_factory TSRMLS_CC)) {
02834               return PHP_ICONV_ERR_UNKNOWN;
02835        }
02836        return PHP_ICONV_ERR_SUCCESS;
02837 }
02838 /* }}} */
02839 
02840 /* {{{ php_iconv_stream_unregister_factory */
02841 static php_iconv_err_t php_iconv_stream_filter_unregister_factory(TSRMLS_D)
02842 {
02843        if (FAILURE == php_stream_filter_unregister_factory(
02844                             php_iconv_stream_filter_ops.label TSRMLS_CC)) {
02845               return PHP_ICONV_ERR_UNKNOWN;
02846        }
02847        return PHP_ICONV_ERR_SUCCESS;
02848 }
02849 /* }}} */
02850 /* }}} */
02851 #endif
02852 
02853 /*
02854  * Local variables:
02855  * tab-width: 4
02856  * c-basic-offset: 4
02857  * End:
02858  * vim600: sw=4 ts=4 fdm=marker
02859  * vim<600: sw=4 ts=4
02860  */