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