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