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