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