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