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