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