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