PageRenderTime 105ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/iconv/iconv.c

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