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