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
59 #ifdef PHP_ICONV_IMPL
60 #define PHP_ICONV_IMPL_VALUE PHP_ICONV_IMPL
61 #elif HAVE_LIBICONV
62 #define PHP_ICONV_IMPL_VALUE "libiconv"
63 #else
64 #define PHP_ICONV_IMPL_VALUE "unknown"
65 #endif
66
get_iconv_version(void)67 char *get_iconv_version(void) {
68 char *version = "unknown";
69
70 #ifdef HAVE_LIBICONV
71 static char buf[16];
72 snprintf(buf, sizeof(buf), "%d.%d", _libiconv_version >> 8, _libiconv_version & 0xff);
73 version = buf;
74 #elif HAVE_GLIBC_ICONV
75 version = (char *) gnu_get_libc_version();
76 #endif
77
78 return version;
79 }
80
81 #define PHP_ICONV_MIME_DECODE_STRICT (1<<0)
82 #define PHP_ICONV_MIME_DECODE_CONTINUE_ON_ERROR (1<<1)
83
84 #include "iconv_arginfo.h"
85
86 #define _php_iconv_memequal(a, b, c) \
87 (memcmp(a, b, c) == 0)
88
89 ZEND_DECLARE_MODULE_GLOBALS(iconv)
90 static PHP_GINIT_FUNCTION(iconv);
91
92 /* {{{ iconv_module_entry */
93 zend_module_entry iconv_module_entry = {
94 STANDARD_MODULE_HEADER,
95 "iconv",
96 ext_functions,
97 PHP_MINIT(miconv),
98 PHP_MSHUTDOWN(miconv),
99 NULL,
100 NULL,
101 PHP_MINFO(miconv),
102 PHP_ICONV_VERSION,
103 PHP_MODULE_GLOBALS(iconv),
104 PHP_GINIT(iconv),
105 NULL,
106 NULL,
107 STANDARD_MODULE_PROPERTIES_EX
108 };
109 /* }}} */
110
111 #ifdef COMPILE_DL_ICONV
112 #ifdef ZTS
113 ZEND_TSRMLS_CACHE_DEFINE()
114 #endif
ZEND_GET_MODULE(iconv)115 ZEND_GET_MODULE(iconv)
116 #endif
117
118 /* {{{ PHP_GINIT_FUNCTION */
119 static PHP_GINIT_FUNCTION(iconv)
120 {
121 #if defined(COMPILE_DL_ICONV) && defined(ZTS)
122 ZEND_TSRMLS_CACHE_UPDATE();
123 #endif
124 iconv_globals->input_encoding = NULL;
125 iconv_globals->output_encoding = NULL;
126 iconv_globals->internal_encoding = NULL;
127 }
128 /* }}} */
129
130 #if defined(HAVE_LIBICONV) && defined(ICONV_ALIASED_LIBICONV)
131 #define iconv libiconv
132 #endif
133
134 /* {{{ typedef enum php_iconv_enc_scheme_t */
135 typedef enum _php_iconv_enc_scheme_t {
136 PHP_ICONV_ENC_SCHEME_BASE64,
137 PHP_ICONV_ENC_SCHEME_QPRINT
138 } php_iconv_enc_scheme_t;
139 /* }}} */
140
141 /* {{{ prototypes */
142 static php_iconv_err_t _php_iconv_appendl(smart_str *d, const char *s, size_t l, iconv_t cd);
143 static php_iconv_err_t _php_iconv_appendc(smart_str *d, const char c, iconv_t cd);
144
145 static void _php_iconv_show_error(php_iconv_err_t err, const char *out_charset, const char *in_charset);
146
147 static php_iconv_err_t _php_iconv_strlen(size_t *pretval, const char *str, size_t nbytes, const char *enc);
148
149 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);
150
151 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);
152
153 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);
154
155 static php_iconv_err_t php_iconv_stream_filter_register_factory(void);
156 static php_iconv_err_t php_iconv_stream_filter_unregister_factory(void);
157
158 static int php_iconv_output_conflict(const char *handler_name, size_t handler_name_len);
159 static php_output_handler *php_iconv_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags);
160 static int php_iconv_output_handler(void **nothing, php_output_context *output_context);
161 /* }}} */
162
163 /* {{{ static globals */
164 static const char _generic_superset_name[] = ICONV_UCS4_ENCODING;
165 #define GENERIC_SUPERSET_NAME _generic_superset_name
166 #define GENERIC_SUPERSET_NBYTES 4
167 /* }}} */
168
169
PHP_INI_MH(OnUpdateInputEncoding)170 static PHP_INI_MH(OnUpdateInputEncoding)
171 {
172 if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
173 return FAILURE;
174 }
175 if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
176 php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.input_encoding is deprecated");
177 }
178 OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
179 return SUCCESS;
180 }
181
182
PHP_INI_MH(OnUpdateOutputEncoding)183 static PHP_INI_MH(OnUpdateOutputEncoding)
184 {
185 if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
186 return FAILURE;
187 }
188 if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
189 php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.output_encoding is deprecated");
190 }
191 OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
192 return SUCCESS;
193 }
194
195
PHP_INI_MH(OnUpdateInternalEncoding)196 static PHP_INI_MH(OnUpdateInternalEncoding)
197 {
198 if (ZSTR_LEN(new_value) >= ICONV_CSNMAXLEN) {
199 return FAILURE;
200 }
201 if (stage & (PHP_INI_STAGE_ACTIVATE | PHP_INI_STAGE_RUNTIME)) {
202 php_error_docref("ref.iconv", E_DEPRECATED, "Use of iconv.internal_encoding is deprecated");
203 }
204 OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
205 return SUCCESS;
206 }
207
208
209 /* {{{ PHP_INI */
210 PHP_INI_BEGIN()
211 STD_PHP_INI_ENTRY("iconv.input_encoding", "", PHP_INI_ALL, OnUpdateInputEncoding, input_encoding, zend_iconv_globals, iconv_globals)
212 STD_PHP_INI_ENTRY("iconv.output_encoding", "", PHP_INI_ALL, OnUpdateOutputEncoding, output_encoding, zend_iconv_globals, iconv_globals)
213 STD_PHP_INI_ENTRY("iconv.internal_encoding", "", PHP_INI_ALL, OnUpdateInternalEncoding, internal_encoding, zend_iconv_globals, iconv_globals)
PHP_INI_END()214 PHP_INI_END()
215 /* }}} */
216
217 /* {{{ PHP_MINIT_FUNCTION */
218 PHP_MINIT_FUNCTION(miconv)
219 {
220 REGISTER_INI_ENTRIES();
221
222 if (php_iconv_stream_filter_register_factory() != PHP_ICONV_ERR_SUCCESS) {
223 return FAILURE;
224 }
225
226 register_iconv_symbols(module_number);
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_STR(smart_str_extract(&retval));
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 RETVAL_STR(smart_str_extract(&retval));
2050 } else {
2051 smart_str_free(&retval);
2052 RETVAL_FALSE;
2053 }
2054
2055 if (tmp_str) {
2056 zend_string_release_ex(tmp_str, 0);
2057 }
2058 }
2059 /* }}} */
2060
2061 /* {{{ Decodes a mime header field */
PHP_FUNCTION(iconv_mime_decode)2062 PHP_FUNCTION(iconv_mime_decode)
2063 {
2064 zend_string *encoded_str;
2065 const char *charset = NULL;
2066 size_t charset_len;
2067 zend_long mode = 0;
2068
2069 smart_str retval = {0};
2070
2071 php_iconv_err_t err;
2072
2073 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls!",
2074 &encoded_str, &mode, &charset, &charset_len) == FAILURE) {
2075
2076 RETURN_THROWS();
2077 }
2078
2079 if (charset == NULL) {
2080 charset = get_internal_encoding();
2081 } else if (charset_len >= ICONV_CSNMAXLEN) {
2082 php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
2083 RETURN_FALSE;
2084 }
2085
2086 err = _php_iconv_mime_decode(&retval, ZSTR_VAL(encoded_str), ZSTR_LEN(encoded_str), charset, NULL, (int)mode);
2087 _php_iconv_show_error(err, charset, "???");
2088
2089 if (err == PHP_ICONV_ERR_SUCCESS) {
2090 RETVAL_STR(smart_str_extract(&retval));
2091 } else {
2092 smart_str_free(&retval);
2093 RETVAL_FALSE;
2094 }
2095 }
2096 /* }}} */
2097
2098 /* {{{ Decodes multiple mime header fields */
PHP_FUNCTION(iconv_mime_decode_headers)2099 PHP_FUNCTION(iconv_mime_decode_headers)
2100 {
2101 zend_string *encoded_str;
2102 const char *charset = NULL;
2103 size_t charset_len;
2104 zend_long mode = 0;
2105 char *enc_str_tmp;
2106 size_t enc_str_len_tmp;
2107
2108 php_iconv_err_t err = PHP_ICONV_ERR_SUCCESS;
2109
2110 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|ls!",
2111 &encoded_str, &mode, &charset, &charset_len) == FAILURE) {
2112
2113 RETURN_THROWS();
2114 }
2115
2116 if (charset == NULL) {
2117 charset = get_internal_encoding();
2118 } else if (charset_len >= ICONV_CSNMAXLEN) {
2119 php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
2120 RETURN_FALSE;
2121 }
2122
2123 array_init(return_value);
2124
2125 enc_str_tmp = ZSTR_VAL(encoded_str);
2126 enc_str_len_tmp = ZSTR_LEN(encoded_str);
2127 while (enc_str_len_tmp > 0) {
2128 smart_str decoded_header = {0};
2129 char *header_name = NULL;
2130 size_t header_name_len = 0;
2131 char *header_value = NULL;
2132 size_t header_value_len = 0;
2133 char *p, *limit;
2134 const char *next_pos;
2135
2136 if (PHP_ICONV_ERR_SUCCESS != (err = _php_iconv_mime_decode(&decoded_header, enc_str_tmp, enc_str_len_tmp, charset, &next_pos, (int)mode))) {
2137 smart_str_free(&decoded_header);
2138 break;
2139 }
2140
2141 if (decoded_header.s == NULL) {
2142 break;
2143 }
2144
2145 limit = ZSTR_VAL(decoded_header.s) + ZSTR_LEN(decoded_header.s);
2146 for (p = ZSTR_VAL(decoded_header.s); p < limit; p++) {
2147 if (*p == ':') {
2148 *p = '\0';
2149 header_name = ZSTR_VAL(decoded_header.s);
2150 header_name_len = p - ZSTR_VAL(decoded_header.s);
2151
2152 while (++p < limit) {
2153 if (*p != ' ' && *p != '\t') {
2154 break;
2155 }
2156 }
2157
2158 header_value = p;
2159 header_value_len = limit - p;
2160
2161 break;
2162 }
2163 }
2164
2165 if (header_name != NULL) {
2166 zval *elem;
2167
2168 if ((elem = zend_hash_str_find(Z_ARRVAL_P(return_value), header_name, header_name_len)) != NULL) {
2169 if (Z_TYPE_P(elem) != IS_ARRAY) {
2170 zval new_elem;
2171
2172 array_init(&new_elem);
2173 Z_ADDREF_P(elem);
2174 add_next_index_zval(&new_elem, elem);
2175
2176 elem = zend_hash_str_update(Z_ARRVAL_P(return_value), header_name, header_name_len, &new_elem);
2177 }
2178 add_next_index_stringl(elem, header_value, header_value_len);
2179 } else {
2180 add_assoc_stringl_ex(return_value, header_name, header_name_len, header_value, header_value_len);
2181 }
2182 }
2183 enc_str_len_tmp -= next_pos - enc_str_tmp;
2184 enc_str_tmp = (char *)next_pos;
2185
2186 smart_str_free(&decoded_header);
2187 }
2188
2189 if (err != PHP_ICONV_ERR_SUCCESS) {
2190 _php_iconv_show_error(err, charset, "???");
2191 zend_array_destroy(Z_ARR_P(return_value));
2192 RETVAL_FALSE;
2193 }
2194 }
2195 /* }}} */
2196
2197 /* {{{ Returns str converted to the out_charset character set */
PHP_FUNCTION(iconv)2198 PHP_FUNCTION(iconv)
2199 {
2200 char *in_charset, *out_charset;
2201 zend_string *in_buffer;
2202 size_t in_charset_len = 0, out_charset_len = 0;
2203 php_iconv_err_t err;
2204 zend_string *out_buffer;
2205
2206 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssS",
2207 &in_charset, &in_charset_len, &out_charset, &out_charset_len, &in_buffer) == FAILURE) {
2208 RETURN_THROWS();
2209 }
2210
2211 if (in_charset_len >= ICONV_CSNMAXLEN || out_charset_len >= ICONV_CSNMAXLEN) {
2212 php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
2213 RETURN_FALSE;
2214 }
2215
2216 err = php_iconv_string(ZSTR_VAL(in_buffer), (size_t)ZSTR_LEN(in_buffer), &out_buffer, out_charset, in_charset);
2217 _php_iconv_show_error(err, out_charset, in_charset);
2218 if (err == PHP_ICONV_ERR_SUCCESS && out_buffer != NULL) {
2219 RETVAL_NEW_STR(out_buffer);
2220 } else {
2221 if (out_buffer != NULL) {
2222 zend_string_efree(out_buffer);
2223 }
2224 RETURN_FALSE;
2225 }
2226 }
2227 /* }}} */
2228
2229 /* {{{ Sets internal encoding and output encoding for ob_iconv_handler() */
PHP_FUNCTION(iconv_set_encoding)2230 PHP_FUNCTION(iconv_set_encoding)
2231 {
2232 zend_string *type;
2233 zend_string *charset;
2234 zend_result retval;
2235 zend_string *name;
2236
2237 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &type, &charset) == FAILURE) {
2238 RETURN_THROWS();
2239 }
2240
2241 if (ZSTR_LEN(charset) >= ICONV_CSNMAXLEN) {
2242 php_error_docref(NULL, E_WARNING, "Encoding parameter exceeds the maximum allowed length of %d characters", ICONV_CSNMAXLEN);
2243 RETURN_FALSE;
2244 }
2245
2246 if(zend_string_equals_literal_ci(type, "input_encoding")) {
2247 name = zend_string_init("iconv.input_encoding", sizeof("iconv.input_encoding") - 1, 0);
2248 } else if(zend_string_equals_literal_ci(type, "output_encoding")) {
2249 name = zend_string_init("iconv.output_encoding", sizeof("iconv.output_encoding") - 1, 0);
2250 } else if(zend_string_equals_literal_ci(type, "internal_encoding")) {
2251 name = zend_string_init("iconv.internal_encoding", sizeof("iconv.internal_encoding") - 1, 0);
2252 } else {
2253 RETURN_FALSE;
2254 }
2255
2256 retval = zend_alter_ini_entry(name, charset, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
2257 zend_string_release_ex(name, 0);
2258
2259 if (retval == SUCCESS) {
2260 RETURN_TRUE;
2261 } else {
2262 RETURN_FALSE;
2263 }
2264 }
2265 /* }}} */
2266
2267 /* {{{ Get internal encoding and output encoding for ob_iconv_handler() */
PHP_FUNCTION(iconv_get_encoding)2268 PHP_FUNCTION(iconv_get_encoding)
2269 {
2270 zend_string *type = NULL;
2271
2272 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &type) == FAILURE) {
2273 RETURN_THROWS();
2274 }
2275
2276 if (!type || zend_string_equals_literal_ci(type, "all")) {
2277 array_init(return_value);
2278 add_assoc_string(return_value, "input_encoding", get_input_encoding());
2279 add_assoc_string(return_value, "output_encoding", get_output_encoding());
2280 add_assoc_string(return_value, "internal_encoding", get_internal_encoding());
2281 } else if (zend_string_equals_literal_ci(type, "input_encoding")) {
2282 RETVAL_STRING(get_input_encoding());
2283 } else if (zend_string_equals_literal_ci(type, "output_encoding")) {
2284 RETVAL_STRING(get_output_encoding());
2285 } else if (zend_string_equals_literal_ci(type, "internal_encoding")) {
2286 RETVAL_STRING(get_internal_encoding());
2287 } else {
2288 /* TODO Warning/ValueError? */
2289 RETURN_FALSE;
2290 }
2291
2292 }
2293 /* }}} */
2294
2295 /* {{{ iconv stream filter */
2296 typedef struct _php_iconv_stream_filter {
2297 iconv_t cd;
2298 int persistent;
2299 char *to_charset;
2300 size_t to_charset_len;
2301 char *from_charset;
2302 size_t from_charset_len;
2303 char stub[128];
2304 size_t stub_len;
2305 } php_iconv_stream_filter;
2306 /* }}} iconv stream filter */
2307
2308 /* {{{ php_iconv_stream_filter_dtor */
php_iconv_stream_filter_dtor(php_iconv_stream_filter * self)2309 static void php_iconv_stream_filter_dtor(php_iconv_stream_filter *self)
2310 {
2311 iconv_close(self->cd);
2312 pefree(self->to_charset, self->persistent);
2313 pefree(self->from_charset, self->persistent);
2314 }
2315 /* }}} */
2316
2317 /* {{{ 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)2318 static php_iconv_err_t php_iconv_stream_filter_ctor(php_iconv_stream_filter *self,
2319 const char *to_charset, size_t to_charset_len,
2320 const char *from_charset, size_t from_charset_len, int persistent)
2321 {
2322 self->to_charset = pemalloc(to_charset_len + 1, persistent);
2323 self->to_charset_len = to_charset_len;
2324 self->from_charset = pemalloc(from_charset_len + 1, persistent);
2325 self->from_charset_len = from_charset_len;
2326
2327 memcpy(self->to_charset, to_charset, to_charset_len);
2328 self->to_charset[to_charset_len] = '\0';
2329 memcpy(self->from_charset, from_charset, from_charset_len);
2330 self->from_charset[from_charset_len] = '\0';
2331
2332 if ((iconv_t)-1 == (self->cd = iconv_open(self->to_charset, self->from_charset))) {
2333 pefree(self->from_charset, persistent);
2334 pefree(self->to_charset, persistent);
2335 return PHP_ICONV_ERR_UNKNOWN;
2336 }
2337 self->persistent = persistent;
2338 self->stub_len = 0;
2339 return PHP_ICONV_ERR_SUCCESS;
2340 }
2341 /* }}} */
2342
2343 /* {{{ 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)2344 static int php_iconv_stream_filter_append_bucket(
2345 php_iconv_stream_filter *self,
2346 php_stream *stream, php_stream_filter *filter,
2347 php_stream_bucket_brigade *buckets_out,
2348 const char *ps, size_t buf_len, size_t *consumed,
2349 int persistent)
2350 {
2351 php_stream_bucket *new_bucket;
2352 char *out_buf = NULL;
2353 size_t out_buf_size;
2354 char *pd, *pt;
2355 size_t ocnt, prev_ocnt, icnt, tcnt;
2356 size_t initial_out_buf_size;
2357
2358 if (ps == NULL) {
2359 initial_out_buf_size = 64;
2360 icnt = 1;
2361 } else {
2362 initial_out_buf_size = buf_len;
2363 icnt = buf_len;
2364 }
2365
2366 out_buf_size = ocnt = prev_ocnt = initial_out_buf_size;
2367 out_buf = pemalloc(out_buf_size, persistent);
2368
2369 pd = out_buf;
2370
2371 if (self->stub_len > 0) {
2372 pt = self->stub;
2373 tcnt = self->stub_len;
2374
2375 while (tcnt > 0) {
2376 if (iconv(self->cd, (ICONV_CONST char **)&pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
2377 switch (errno) {
2378 case EILSEQ:
2379 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
2380 goto out_failure;
2381
2382 case EINVAL:
2383 if (ps != NULL) {
2384 if (icnt > 0) {
2385 if (self->stub_len >= sizeof(self->stub)) {
2386 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
2387 goto out_failure;
2388 }
2389 self->stub[self->stub_len++] = *(ps++);
2390 icnt--;
2391 pt = self->stub;
2392 tcnt = self->stub_len;
2393 } else {
2394 tcnt = 0;
2395 break;
2396 }
2397 } else {
2398 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
2399 goto out_failure;
2400 }
2401 break;
2402
2403 case E2BIG: {
2404 char *new_out_buf;
2405 size_t new_out_buf_size;
2406
2407 new_out_buf_size = out_buf_size << 1;
2408
2409 if (new_out_buf_size < out_buf_size) {
2410 /* whoa! no bigger buckets are sold anywhere... */
2411 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
2412 goto out_failure;
2413 }
2414
2415 php_stream_bucket_append(buckets_out, new_bucket);
2416
2417 out_buf_size = ocnt = initial_out_buf_size;
2418 out_buf = pemalloc(out_buf_size, persistent);
2419 pd = out_buf;
2420 } else {
2421 new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
2422 pd = new_out_buf + (pd - out_buf);
2423 ocnt += (new_out_buf_size - out_buf_size);
2424 out_buf = new_out_buf;
2425 out_buf_size = new_out_buf_size;
2426 }
2427 } break;
2428
2429 default:
2430 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
2431 goto out_failure;
2432 }
2433 }
2434 prev_ocnt = ocnt;
2435 }
2436 memmove(self->stub, pt, tcnt);
2437 self->stub_len = tcnt;
2438 }
2439
2440 while (icnt > 0) {
2441 if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
2442 iconv(self->cd, (ICONV_CONST char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) {
2443 switch (errno) {
2444 case EILSEQ:
2445 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
2446 goto out_failure;
2447
2448 case EINVAL:
2449 if (ps != NULL) {
2450 if (icnt > sizeof(self->stub)) {
2451 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
2452 goto out_failure;
2453 }
2454 memcpy(self->stub, ps, icnt);
2455 self->stub_len = icnt;
2456 ps += icnt;
2457 icnt = 0;
2458 } else {
2459 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset);
2460 goto out_failure;
2461 }
2462 break;
2463
2464 case E2BIG: {
2465 char *new_out_buf;
2466 size_t new_out_buf_size;
2467
2468 new_out_buf_size = out_buf_size << 1;
2469
2470 if (new_out_buf_size < out_buf_size) {
2471 /* whoa! no bigger buckets are sold anywhere... */
2472 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
2473 goto out_failure;
2474 }
2475
2476 php_stream_bucket_append(buckets_out, new_bucket);
2477
2478 out_buf_size = ocnt = initial_out_buf_size;
2479 out_buf = pemalloc(out_buf_size, persistent);
2480 pd = out_buf;
2481 } else {
2482 new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
2483 pd = new_out_buf + (pd - out_buf);
2484 ocnt += (new_out_buf_size - out_buf_size);
2485 out_buf = new_out_buf;
2486 out_buf_size = new_out_buf_size;
2487 }
2488 } break;
2489
2490 default:
2491 php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
2492 goto out_failure;
2493 }
2494 } else {
2495 if (ps == NULL) {
2496 break;
2497 }
2498 }
2499 prev_ocnt = ocnt;
2500 }
2501
2502 if (out_buf_size > ocnt) {
2503 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
2504 goto out_failure;
2505 }
2506 php_stream_bucket_append(buckets_out, new_bucket);
2507 } else {
2508 pefree(out_buf, persistent);
2509 }
2510 *consumed += buf_len - icnt;
2511
2512 return SUCCESS;
2513
2514 out_failure:
2515 pefree(out_buf, persistent);
2516 return FAILURE;
2517 }
2518 /* }}} php_iconv_stream_filter_append_bucket */
2519
2520 /* {{{ 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)2521 static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
2522 php_stream *stream, php_stream_filter *filter,
2523 php_stream_bucket_brigade *buckets_in,
2524 php_stream_bucket_brigade *buckets_out,
2525 size_t *bytes_consumed, int flags)
2526 {
2527 php_stream_bucket *bucket = NULL;
2528 size_t consumed = 0;
2529 php_iconv_stream_filter *self = (php_iconv_stream_filter *)Z_PTR(filter->abstract);
2530
2531 while (buckets_in->head != NULL) {
2532 bucket = buckets_in->head;
2533
2534 php_stream_bucket_unlink(bucket);
2535
2536 if (php_iconv_stream_filter_append_bucket(self, stream, filter,
2537 buckets_out, bucket->buf, bucket->buflen, &consumed,
2538 php_stream_is_persistent(stream)) != SUCCESS) {
2539 goto out_failure;
2540 }
2541
2542 php_stream_bucket_delref(bucket);
2543 }
2544
2545 if (flags != PSFS_FLAG_NORMAL) {
2546 if (php_iconv_stream_filter_append_bucket(self, stream, filter,
2547 buckets_out, NULL, 0, &consumed,
2548 php_stream_is_persistent(stream)) != SUCCESS) {
2549 goto out_failure;
2550 }
2551 }
2552
2553 if (bytes_consumed != NULL) {
2554 *bytes_consumed = consumed;
2555 }
2556
2557 return PSFS_PASS_ON;
2558
2559 out_failure:
2560 if (bucket != NULL) {
2561 php_stream_bucket_delref(bucket);
2562 }
2563 return PSFS_ERR_FATAL;
2564 }
2565 /* }}} */
2566
2567 /* {{{ php_iconv_stream_filter_cleanup */
php_iconv_stream_filter_cleanup(php_stream_filter * filter)2568 static void php_iconv_stream_filter_cleanup(php_stream_filter *filter)
2569 {
2570 php_iconv_stream_filter_dtor((php_iconv_stream_filter *)Z_PTR(filter->abstract));
2571 pefree(Z_PTR(filter->abstract), ((php_iconv_stream_filter *)Z_PTR(filter->abstract))->persistent);
2572 }
2573 /* }}} */
2574
2575 static const php_stream_filter_ops php_iconv_stream_filter_ops = {
2576 php_iconv_stream_filter_do_filter,
2577 php_iconv_stream_filter_cleanup,
2578 "convert.iconv.*"
2579 };
2580
2581 /* {{{ php_iconv_stream_filter_create */
php_iconv_stream_filter_factory_create(const char * name,zval * params,uint8_t persistent)2582 static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, uint8_t persistent)
2583 {
2584 php_stream_filter *retval = NULL;
2585 php_iconv_stream_filter *inst;
2586 char *from_charset = NULL, *to_charset = NULL;
2587 size_t from_charset_len, to_charset_len;
2588
2589 if ((from_charset = strchr(name, '.')) == NULL) {
2590 return NULL;
2591 }
2592 ++from_charset;
2593 if ((from_charset = strchr(from_charset, '.')) == NULL) {
2594 return NULL;
2595 }
2596 ++from_charset;
2597 if ((to_charset = strpbrk(from_charset, "/.")) == NULL) {
2598 return NULL;
2599 }
2600 from_charset_len = to_charset - from_charset;
2601 ++to_charset;
2602 to_charset_len = strlen(to_charset);
2603
2604 if (from_charset_len >= ICONV_CSNMAXLEN || to_charset_len >= ICONV_CSNMAXLEN) {
2605 return NULL;
2606 }
2607
2608 inst = pemalloc(sizeof(php_iconv_stream_filter), persistent);
2609
2610 if (php_iconv_stream_filter_ctor(inst, to_charset, to_charset_len, from_charset, from_charset_len, persistent) != PHP_ICONV_ERR_SUCCESS) {
2611 pefree(inst, persistent);
2612 return NULL;
2613 }
2614
2615 if (NULL == (retval = php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent))) {
2616 php_iconv_stream_filter_dtor(inst);
2617 pefree(inst, persistent);
2618 }
2619
2620 return retval;
2621 }
2622 /* }}} */
2623
2624 /* {{{ php_iconv_stream_register_factory */
php_iconv_stream_filter_register_factory(void)2625 static php_iconv_err_t php_iconv_stream_filter_register_factory(void)
2626 {
2627 static const php_stream_filter_factory filter_factory = {
2628 php_iconv_stream_filter_factory_create
2629 };
2630
2631 if (FAILURE == php_stream_filter_register_factory(
2632 php_iconv_stream_filter_ops.label,
2633 &filter_factory)) {
2634 return PHP_ICONV_ERR_UNKNOWN;
2635 }
2636 return PHP_ICONV_ERR_SUCCESS;
2637 }
2638 /* }}} */
2639
2640 /* {{{ php_iconv_stream_unregister_factory */
php_iconv_stream_filter_unregister_factory(void)2641 static php_iconv_err_t php_iconv_stream_filter_unregister_factory(void)
2642 {
2643 if (FAILURE == php_stream_filter_unregister_factory(
2644 php_iconv_stream_filter_ops.label)) {
2645 return PHP_ICONV_ERR_UNKNOWN;
2646 }
2647 return PHP_ICONV_ERR_SUCCESS;
2648 }
2649 /* }}} */
2650 /* }}} */
2651 #endif
2652