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