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