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