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