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