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