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