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