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