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