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