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