summaryrefslogtreecommitdiff
path: root/ext/standard/filters.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/filters.c')
-rw-r--r--ext/standard/filters.c2152
1 files changed, 2152 insertions, 0 deletions
diff --git a/ext/standard/filters.c b/ext/standard/filters.c
new file mode 100644
index 0000000..084860c
--- /dev/null
+++ b/ext/standard/filters.c
@@ -0,0 +1,2152 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: |
+ | Wez Furlong (wez@thebrainroom.com) |
+ | Sara Golemon (pollita@php.net) |
+ | Moriyoshi Koizumi (moriyoshi@php.net) |
+ | Marcus Boerger (helly@php.net) |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "php_globals.h"
+#include "ext/standard/basic_functions.h"
+#include "ext/standard/file.h"
+#include "ext/standard/php_string.h"
+#include "ext/standard/php_smart_str.h"
+
+/* {{{ rot13 stream filter implementation */
+static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
+
+static php_stream_filter_status_t strfilter_rot13_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+
+ while (buckets_in->head) {
+ bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
+
+ php_strtr(bucket->buf, bucket->buflen, rot13_from, rot13_to, 52);
+ consumed += bucket->buflen;
+
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return PSFS_PASS_ON;
+}
+
+static php_stream_filter_ops strfilter_rot13_ops = {
+ strfilter_rot13_filter,
+ NULL,
+ "string.rot13"
+};
+
+static php_stream_filter *strfilter_rot13_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent);
+}
+
+static php_stream_filter_factory strfilter_rot13_factory = {
+ strfilter_rot13_create
+};
+/* }}} */
+
+/* {{{ string.toupper / string.tolower stream filter implementation */
+static char lowercase[] = "abcdefghijklmnopqrstuvwxyz";
+static char uppercase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+static php_stream_filter_status_t strfilter_toupper_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+
+ while (buckets_in->head) {
+ bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
+
+ php_strtr(bucket->buf, bucket->buflen, lowercase, uppercase, 26);
+ consumed += bucket->buflen;
+
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return PSFS_PASS_ON;
+}
+
+static php_stream_filter_status_t strfilter_tolower_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+
+ while (buckets_in->head) {
+ bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
+
+ php_strtr(bucket->buf, bucket->buflen, uppercase, lowercase, 26);
+ consumed += bucket->buflen;
+
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return PSFS_PASS_ON;
+}
+
+static php_stream_filter_ops strfilter_toupper_ops = {
+ strfilter_toupper_filter,
+ NULL,
+ "string.toupper"
+};
+
+static php_stream_filter_ops strfilter_tolower_ops = {
+ strfilter_tolower_filter,
+ NULL,
+ "string.tolower"
+};
+
+static php_stream_filter *strfilter_toupper_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent);
+}
+
+static php_stream_filter *strfilter_tolower_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent);
+}
+
+static php_stream_filter_factory strfilter_toupper_factory = {
+ strfilter_toupper_create
+};
+
+static php_stream_filter_factory strfilter_tolower_factory = {
+ strfilter_tolower_create
+};
+/* }}} */
+
+/* {{{ strip_tags filter implementation */
+typedef struct _php_strip_tags_filter {
+ const char *allowed_tags;
+ int allowed_tags_len;
+ int state;
+ int persistent;
+} php_strip_tags_filter;
+
+static int php_strip_tags_filter_ctor(php_strip_tags_filter *inst, const char *allowed_tags, int allowed_tags_len, int persistent)
+{
+ if (allowed_tags != NULL) {
+ if (NULL == (inst->allowed_tags = pemalloc(allowed_tags_len, persistent))) {
+ return FAILURE;
+ }
+ memcpy((char *)inst->allowed_tags, allowed_tags, allowed_tags_len);
+ inst->allowed_tags_len = allowed_tags_len;
+ } else {
+ inst->allowed_tags = NULL;
+ }
+ inst->state = 0;
+ inst->persistent = persistent;
+
+ return SUCCESS;
+}
+
+static void php_strip_tags_filter_dtor(php_strip_tags_filter *inst)
+{
+ if (inst->allowed_tags != NULL) {
+ pefree((void *)inst->allowed_tags, inst->persistent);
+ }
+}
+
+static php_stream_filter_status_t strfilter_strip_tags_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+ php_strip_tags_filter *inst = (php_strip_tags_filter *) thisfilter->abstract;
+
+ while (buckets_in->head) {
+ bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
+ consumed = bucket->buflen;
+
+ bucket->buflen = php_strip_tags(bucket->buf, bucket->buflen, &(inst->state), (char *)inst->allowed_tags, inst->allowed_tags_len);
+
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return PSFS_PASS_ON;
+}
+
+static void strfilter_strip_tags_dtor(php_stream_filter *thisfilter TSRMLS_DC)
+{
+ assert(thisfilter->abstract != NULL);
+
+ php_strip_tags_filter_dtor((php_strip_tags_filter *)thisfilter->abstract);
+
+ pefree(thisfilter->abstract, ((php_strip_tags_filter *)thisfilter->abstract)->persistent);
+}
+
+static php_stream_filter_ops strfilter_strip_tags_ops = {
+ strfilter_strip_tags_filter,
+ strfilter_strip_tags_dtor,
+ "string.strip_tags"
+};
+
+static php_stream_filter *strfilter_strip_tags_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ php_strip_tags_filter *inst;
+ smart_str tags_ss = { 0, 0, 0 };
+
+ inst = pemalloc(sizeof(php_strip_tags_filter), persistent);
+
+ if (inst == NULL) { /* it's possible pemalloc returns NULL
+ instead of causing it to bail out */
+ return NULL;
+ }
+
+ if (filterparams != NULL) {
+ if (Z_TYPE_P(filterparams) == IS_ARRAY) {
+ HashPosition pos;
+ zval **tmp;
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(filterparams), &pos);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(filterparams), (void **) &tmp, &pos) == SUCCESS) {
+ convert_to_string_ex(tmp);
+ smart_str_appendc(&tags_ss, '<');
+ smart_str_appendl(&tags_ss, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
+ smart_str_appendc(&tags_ss, '>');
+ zend_hash_move_forward_ex(Z_ARRVAL_P(filterparams), &pos);
+ }
+ smart_str_0(&tags_ss);
+ } else {
+ /* FIXME: convert_to_* may clutter zvals and lead it into segfault ? */
+ convert_to_string_ex(&filterparams);
+
+ tags_ss.c = Z_STRVAL_P(filterparams);
+ tags_ss.len = Z_STRLEN_P(filterparams);
+ tags_ss.a = 0;
+ }
+ }
+
+ if (php_strip_tags_filter_ctor(inst, tags_ss.c, tags_ss.len, persistent) != SUCCESS) {
+ if (tags_ss.a != 0) {
+ STR_FREE(tags_ss.c);
+ }
+ pefree(inst, persistent);
+ return NULL;
+ }
+
+ if (tags_ss.a != 0) {
+ STR_FREE(tags_ss.c);
+ }
+
+ return php_stream_filter_alloc(&strfilter_strip_tags_ops, inst, persistent);
+}
+
+static php_stream_filter_factory strfilter_strip_tags_factory = {
+ strfilter_strip_tags_create
+};
+
+/* }}} */
+
+/* {{{ base64 / quoted_printable stream filter implementation */
+
+typedef enum _php_conv_err_t {
+ PHP_CONV_ERR_SUCCESS = SUCCESS,
+ PHP_CONV_ERR_UNKNOWN,
+ PHP_CONV_ERR_TOO_BIG,
+ PHP_CONV_ERR_INVALID_SEQ,
+ PHP_CONV_ERR_UNEXPECTED_EOS,
+ PHP_CONV_ERR_EXISTS,
+ PHP_CONV_ERR_MORE,
+ PHP_CONV_ERR_ALLOC,
+ PHP_CONV_ERR_NOT_FOUND
+} php_conv_err_t;
+
+typedef struct _php_conv php_conv;
+
+typedef php_conv_err_t (*php_conv_convert_func)(php_conv *, const char **, size_t *, char **, size_t *);
+typedef void (*php_conv_dtor_func)(php_conv *);
+
+struct _php_conv {
+ php_conv_convert_func convert_op;
+ php_conv_dtor_func dtor;
+};
+
+#define php_conv_convert(a, b, c, d, e) ((php_conv *)(a))->convert_op((php_conv *)(a), (b), (c), (d), (e))
+#define php_conv_dtor(a) ((php_conv *)a)->dtor((a))
+
+/* {{{ php_conv_base64_encode */
+typedef struct _php_conv_base64_encode {
+ php_conv _super;
+
+ unsigned char erem[3];
+ size_t erem_len;
+ unsigned int line_ccnt;
+ unsigned int line_len;
+ const char *lbchars;
+ int lbchars_dup;
+ size_t lbchars_len;
+ int persistent;
+} php_conv_base64_encode;
+
+static php_conv_err_t php_conv_base64_encode_convert(php_conv_base64_encode *inst, const char **in_p, size_t *in_left, char **out_p, size_t *out_left);
+static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst);
+
+static unsigned char b64_tbl_enc[256] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+static php_conv_err_t php_conv_base64_encode_ctor(php_conv_base64_encode *inst, unsigned int line_len, const char *lbchars, size_t lbchars_len, int lbchars_dup, int persistent)
+{
+ inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_encode_convert;
+ inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_encode_dtor;
+ inst->erem_len = 0;
+ inst->line_ccnt = line_len;
+ inst->line_len = line_len;
+ if (lbchars != NULL) {
+ inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
+ inst->lbchars_len = lbchars_len;
+ } else {
+ inst->lbchars = NULL;
+ }
+ inst->lbchars_dup = lbchars_dup;
+ inst->persistent = persistent;
+ return PHP_CONV_ERR_SUCCESS;
+}
+
+static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst)
+{
+ assert(inst != NULL);
+ if (inst->lbchars_dup && inst->lbchars != NULL) {
+ pefree((void *)inst->lbchars, inst->persistent);
+ }
+}
+
+static php_conv_err_t php_conv_base64_encode_flush(php_conv_base64_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
+{
+ volatile php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
+ register unsigned char *pd;
+ register size_t ocnt;
+ unsigned int line_ccnt;
+
+ pd = (unsigned char *)(*out_pp);
+ ocnt = *out_left_p;
+ line_ccnt = inst->line_ccnt;
+
+ switch (inst->erem_len) {
+ case 0:
+ /* do nothing */
+ break;
+
+ case 1:
+ if (line_ccnt < 4 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len) {
+ return PHP_CONV_ERR_TOO_BIG;
+ }
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 4) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4)];
+ *(pd++) = '=';
+ *(pd++) = '=';
+ inst->erem_len = 0;
+ ocnt -= 4;
+ line_ccnt -= 4;
+ break;
+
+ case 2:
+ if (line_ccnt < 4 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len) {
+ return PHP_CONV_ERR_TOO_BIG;
+ }
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 4) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (inst->erem[1] >> 4)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[1] << 2)];
+ *(pd++) = '=';
+ inst->erem_len = 0;
+ ocnt -=4;
+ line_ccnt -= 4;
+ break;
+
+ default:
+ /* should not happen... */
+ err = PHP_CONV_ERR_UNKNOWN;
+ break;
+ }
+out:
+ *out_pp = (char *)pd;
+ *out_left_p = ocnt;
+ inst->line_ccnt = line_ccnt;
+ return err;
+}
+
+static php_conv_err_t php_conv_base64_encode_convert(php_conv_base64_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
+{
+ volatile php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
+ register size_t ocnt, icnt;
+ register unsigned char *ps, *pd;
+ register unsigned int line_ccnt;
+
+ if (in_pp == NULL || in_left_p == NULL) {
+ return php_conv_base64_encode_flush(inst, in_pp, in_left_p, out_pp, out_left_p);
+ }
+
+ pd = (unsigned char *)(*out_pp);
+ ocnt = *out_left_p;
+ ps = (unsigned char *)(*in_pp);
+ icnt = *in_left_p;
+ line_ccnt = inst->line_ccnt;
+
+ /* consume the remainder first */
+ switch (inst->erem_len) {
+ case 1:
+ if (icnt >= 2) {
+ if (line_ccnt < 4 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len) {
+ return PHP_CONV_ERR_TOO_BIG;
+ }
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 4) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (ps[0] >> 4)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(ps[0] << 2) | (ps[1] >> 6)];
+ *(pd++) = b64_tbl_enc[ps[1]];
+ ocnt -= 4;
+ ps += 2;
+ icnt -= 2;
+ inst->erem_len = 0;
+ line_ccnt -= 4;
+ }
+ break;
+
+ case 2:
+ if (icnt >= 1) {
+ if (inst->line_ccnt < 4 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len) {
+ return PHP_CONV_ERR_TOO_BIG;
+ }
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 4) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (inst->erem[1] >> 4)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[1] << 2) | (ps[0] >> 6)];
+ *(pd++) = b64_tbl_enc[ps[0]];
+ ocnt -= 4;
+ ps += 1;
+ icnt -= 1;
+ inst->erem_len = 0;
+ line_ccnt -= 4;
+ }
+ break;
+ }
+
+ while (icnt >= 3) {
+ if (line_ccnt < 4 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 4) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = b64_tbl_enc[ps[0] >> 2];
+ *(pd++) = b64_tbl_enc[(unsigned char)(ps[0] << 4) | (ps[1] >> 4)];
+ *(pd++) = b64_tbl_enc[(unsigned char)(ps[1] << 2) | (ps[2] >> 6)];
+ *(pd++) = b64_tbl_enc[ps[2]];
+
+ ps += 3;
+ icnt -=3;
+ ocnt -= 4;
+ line_ccnt -= 4;
+ }
+ for (;icnt > 0; icnt--) {
+ inst->erem[inst->erem_len++] = *(ps++);
+ }
+
+out:
+ *in_pp = (const char *)ps;
+ *in_left_p = icnt;
+ *out_pp = (char *)pd;
+ *out_left_p = ocnt;
+ inst->line_ccnt = line_ccnt;
+
+ return err;
+}
+
+/* }}} */
+
+/* {{{ php_conv_base64_decode */
+typedef struct _php_conv_base64_decode {
+ php_conv _super;
+
+ unsigned int urem;
+ unsigned int urem_nbits;
+ unsigned int ustat;
+ int eos;
+} php_conv_base64_decode;
+
+static php_conv_err_t php_conv_base64_decode_convert(php_conv_base64_decode *inst, const char **in_p, size_t *in_left, char **out_p, size_t *out_left);
+static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst);
+
+static unsigned int b64_tbl_dec[256] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64,128, 64, 64,
+ 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+ 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+};
+
+static int php_conv_base64_decode_ctor(php_conv_base64_decode *inst)
+{
+ inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_decode_convert;
+ inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_decode_dtor;
+
+ inst->urem = 0;
+ inst->urem_nbits = 0;
+ inst->ustat = 0;
+ inst->eos = 0;
+ return SUCCESS;
+}
+
+static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst)
+{
+ /* do nothing */
+}
+
+#define bmask(a) (0xffff >> (16 - a))
+static php_conv_err_t php_conv_base64_decode_convert(php_conv_base64_decode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
+{
+ php_conv_err_t err;
+
+ unsigned int urem, urem_nbits;
+ unsigned int pack, pack_bcnt;
+ unsigned char *ps, *pd;
+ size_t icnt, ocnt;
+ unsigned int ustat;
+
+ static const unsigned int nbitsof_pack = 8;
+
+ if (in_pp == NULL || in_left_p == NULL) {
+ if (inst->eos || inst->urem_nbits == 0) {
+ return PHP_CONV_ERR_SUCCESS;
+ }
+ return PHP_CONV_ERR_UNEXPECTED_EOS;
+ }
+
+ err = PHP_CONV_ERR_SUCCESS;
+
+ ps = (unsigned char *)*in_pp;
+ pd = (unsigned char *)*out_pp;
+ icnt = *in_left_p;
+ ocnt = *out_left_p;
+
+ urem = inst->urem;
+ urem_nbits = inst->urem_nbits;
+ ustat = inst->ustat;
+
+ pack = 0;
+ pack_bcnt = nbitsof_pack;
+
+ for (;;) {
+ if (pack_bcnt >= urem_nbits) {
+ pack_bcnt -= urem_nbits;
+ pack |= (urem << pack_bcnt);
+ urem_nbits = 0;
+ } else {
+ urem_nbits -= pack_bcnt;
+ pack |= (urem >> urem_nbits);
+ urem &= bmask(urem_nbits);
+ pack_bcnt = 0;
+ }
+ if (pack_bcnt > 0) {
+ unsigned int i;
+
+ if (icnt < 1) {
+ break;
+ }
+
+ i = b64_tbl_dec[(unsigned int)*(ps++)];
+ icnt--;
+ ustat |= i & 0x80;
+
+ if (!(i & 0xc0)) {
+ if (ustat) {
+ err = PHP_CONV_ERR_INVALID_SEQ;
+ break;
+ }
+ if (6 <= pack_bcnt) {
+ pack_bcnt -= 6;
+ pack |= (i << pack_bcnt);
+ urem = 0;
+ } else {
+ urem_nbits = 6 - pack_bcnt;
+ pack |= (i >> urem_nbits);
+ urem = i & bmask(urem_nbits);
+ pack_bcnt = 0;
+ }
+ } else if (ustat) {
+ if (pack_bcnt == 8 || pack_bcnt == 2) {
+ err = PHP_CONV_ERR_INVALID_SEQ;
+ break;
+ }
+ inst->eos = 1;
+ }
+ }
+ if ((pack_bcnt | ustat) == 0) {
+ if (ocnt < 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+ *(pd++) = pack;
+ ocnt--;
+ pack = 0;
+ pack_bcnt = nbitsof_pack;
+ }
+ }
+
+ if (urem_nbits >= pack_bcnt) {
+ urem |= (pack << (urem_nbits - pack_bcnt));
+ urem_nbits += (nbitsof_pack - pack_bcnt);
+ } else {
+ urem |= (pack >> (pack_bcnt - urem_nbits));
+ urem_nbits += (nbitsof_pack - pack_bcnt);
+ }
+
+ inst->urem = urem;
+ inst->urem_nbits = urem_nbits;
+ inst->ustat = ustat;
+
+ *in_pp = (const char *)ps;
+ *in_left_p = icnt;
+ *out_pp = (char *)pd;
+ *out_left_p = ocnt;
+
+ return err;
+}
+#undef bmask
+/* }}} */
+
+/* {{{ php_conv_qprint_encode */
+typedef struct _php_conv_qprint_encode {
+ php_conv _super;
+
+ int opts;
+ unsigned int line_ccnt;
+ unsigned int line_len;
+ const char *lbchars;
+ int lbchars_dup;
+ size_t lbchars_len;
+ int persistent;
+ unsigned int lb_ptr;
+ unsigned int lb_cnt;
+} php_conv_qprint_encode;
+
+#define PHP_CONV_QPRINT_OPT_BINARY 0x00000001
+#define PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST 0x00000002
+
+static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst);
+static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p);
+
+static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst)
+{
+ assert(inst != NULL);
+ if (inst->lbchars_dup && inst->lbchars != NULL) {
+ pefree((void *)inst->lbchars, inst->persistent);
+ }
+}
+
+#define NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, lbchars) \
+ ((lb_ptr) < (lb_cnt) ? (lbchars)[(lb_ptr)] : *(ps))
+
+#define CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt) \
+ if ((lb_ptr) < (lb_cnt)) { \
+ (lb_ptr)++; \
+ } else { \
+ (lb_cnt) = (lb_ptr) = 0; \
+ --(icnt); \
+ (ps)++; \
+ }
+
+static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
+{
+ php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
+ unsigned char *ps, *pd;
+ size_t icnt, ocnt;
+ unsigned int c;
+ unsigned int line_ccnt;
+ unsigned int lb_ptr;
+ unsigned int lb_cnt;
+ int opts;
+ static char qp_digits[] = "0123456789ABCDEF";
+
+ line_ccnt = inst->line_ccnt;
+ opts = inst->opts;
+ lb_ptr = inst->lb_ptr;
+ lb_cnt = inst->lb_cnt;
+
+ if ((in_pp == NULL || in_left_p == NULL) && (lb_ptr >=lb_cnt)) {
+ return PHP_CONV_ERR_SUCCESS;
+ }
+
+ ps = (unsigned char *)(*in_pp);
+ icnt = *in_left_p;
+ pd = (unsigned char *)(*out_pp);
+ ocnt = *out_left_p;
+
+ for (;;) {
+ if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) {
+ /* look ahead for the line break chars to make a right decision
+ * how to consume incoming characters */
+
+ if (icnt > 0 && *ps == inst->lbchars[lb_cnt]) {
+ lb_cnt++;
+
+ if (lb_cnt >= inst->lbchars_len) {
+ unsigned int i;
+
+ if (ocnt < lb_cnt) {
+ lb_cnt--;
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+
+ for (i = 0; i < lb_cnt; i++) {
+ *(pd++) = inst->lbchars[i];
+ ocnt--;
+ }
+ line_ccnt = inst->line_len;
+ lb_ptr = lb_cnt = 0;
+ }
+ ps++, icnt--;
+ continue;
+ }
+ }
+
+ if (lb_ptr >= lb_cnt && icnt <= 0) {
+ break;
+ }
+
+ c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars);
+
+ if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && (c == '\t' || c == ' ')) {
+ if (line_ccnt < 2 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len + 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+
+ *(pd++) = '=';
+ ocnt--;
+ line_ccnt--;
+
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ } else {
+ if (ocnt < 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+ *(pd++) = c;
+ ocnt--;
+ line_ccnt--;
+ CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
+ }
+ } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) {
+ if (line_ccnt < 2 && inst->lbchars != NULL) {
+ if (ocnt < inst->lbchars_len + 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+ *(pd++) = '=';
+ ocnt--;
+ line_ccnt--;
+
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+ *(pd++) = c;
+ ocnt--;
+ line_ccnt--;
+ CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
+ } else {
+ if (line_ccnt < 4) {
+ if (ocnt < inst->lbchars_len + 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+ *(pd++) = '=';
+ ocnt--;
+ line_ccnt--;
+
+ memcpy(pd, inst->lbchars, inst->lbchars_len);
+ pd += inst->lbchars_len;
+ ocnt -= inst->lbchars_len;
+ line_ccnt = inst->line_len;
+ }
+ if (ocnt < 3) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ break;
+ }
+ *(pd++) = '=';
+ *(pd++) = qp_digits[(c >> 4)];
+ *(pd++) = qp_digits[(c & 0x0f)];
+ ocnt -= 3;
+ line_ccnt -= 3;
+ CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
+ }
+ }
+
+ *in_pp = (const char *)ps;
+ *in_left_p = icnt;
+ *out_pp = (char *)pd;
+ *out_left_p = ocnt;
+ inst->line_ccnt = line_ccnt;
+ inst->lb_ptr = lb_ptr;
+ inst->lb_cnt = lb_cnt;
+ return err;
+}
+#undef NEXT_CHAR
+#undef CONSUME_CHAR
+
+static php_conv_err_t php_conv_qprint_encode_ctor(php_conv_qprint_encode *inst, unsigned int line_len, const char *lbchars, size_t lbchars_len, int lbchars_dup, int opts, int persistent)
+{
+ if (line_len < 4 && lbchars != NULL) {
+ return PHP_CONV_ERR_TOO_BIG;
+ }
+ inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_encode_convert;
+ inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_encode_dtor;
+ inst->line_ccnt = line_len;
+ inst->line_len = line_len;
+ if (lbchars != NULL) {
+ inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
+ inst->lbchars_len = lbchars_len;
+ } else {
+ inst->lbchars = NULL;
+ }
+ inst->lbchars_dup = lbchars_dup;
+ inst->persistent = persistent;
+ inst->opts = opts;
+ inst->lb_cnt = inst->lb_ptr = 0;
+ return PHP_CONV_ERR_SUCCESS;
+}
+/* }}} */
+
+/* {{{ php_conv_qprint_decode */
+typedef struct _php_conv_qprint_decode {
+ php_conv _super;
+
+ int scan_stat;
+ unsigned int next_char;
+ const char *lbchars;
+ int lbchars_dup;
+ size_t lbchars_len;
+ int persistent;
+ unsigned int lb_ptr;
+ unsigned int lb_cnt;
+} php_conv_qprint_decode;
+
+static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst)
+{
+ assert(inst != NULL);
+ if (inst->lbchars_dup && inst->lbchars != NULL) {
+ pefree((void *)inst->lbchars, inst->persistent);
+ }
+}
+
+static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *inst, const char **in_pp, size_t *in_left_p, char **out_pp, size_t *out_left_p)
+{
+ php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
+ size_t icnt, ocnt;
+ unsigned char *ps, *pd;
+ unsigned int scan_stat;
+ unsigned int next_char;
+ unsigned int lb_ptr, lb_cnt;
+
+ lb_ptr = inst->lb_ptr;
+ lb_cnt = inst->lb_cnt;
+
+ if ((in_pp == NULL || in_left_p == NULL) && lb_cnt == lb_ptr) {
+ if (inst->scan_stat != 0) {
+ return PHP_CONV_ERR_UNEXPECTED_EOS;
+ }
+ return PHP_CONV_ERR_SUCCESS;
+ }
+
+ ps = (unsigned char *)(*in_pp);
+ icnt = *in_left_p;
+ pd = (unsigned char *)(*out_pp);
+ ocnt = *out_left_p;
+ scan_stat = inst->scan_stat;
+ next_char = inst->next_char;
+
+ for (;;) {
+ switch (scan_stat) {
+ case 0: {
+ if (icnt <= 0) {
+ goto out;
+ }
+ if (*ps == '=') {
+ scan_stat = 1;
+ } else {
+ if (ocnt < 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = *ps;
+ ocnt--;
+ }
+ ps++, icnt--;
+ } break;
+
+ case 1: {
+ if (icnt <= 0) {
+ goto out;
+ }
+ if (*ps == ' ' || *ps == '\t') {
+ scan_stat = 4;
+ ps++, icnt--;
+ break;
+ } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\r') {
+ /* auto-detect line endings, looks like network line ending \r\n (could be mac \r) */
+ lb_cnt++;
+ scan_stat = 5;
+ ps++, icnt--;
+ break;
+ } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\n') {
+ /* auto-detect line endings, looks like unix-lineendings, not to spec, but it is seem in the wild, a lot */
+ lb_cnt = lb_ptr = 0;
+ scan_stat = 0;
+ ps++, icnt--;
+ break;
+ } else if (lb_cnt < inst->lbchars_len &&
+ *ps == (unsigned char)inst->lbchars[lb_cnt]) {
+ lb_cnt++;
+ scan_stat = 5;
+ ps++, icnt--;
+ break;
+ }
+ } /* break is missing intentionally */
+
+ case 2: {
+ if (icnt <= 0) {
+ goto out;
+ }
+
+ if (!isxdigit((int) *ps)) {
+ err = PHP_CONV_ERR_INVALID_SEQ;
+ goto out;
+ }
+ next_char = (next_char << 4) | (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30);
+ scan_stat++;
+ ps++, icnt--;
+ if (scan_stat != 3) {
+ break;
+ }
+ } /* break is missing intentionally */
+
+ case 3: {
+ if (ocnt < 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = next_char;
+ ocnt--;
+ scan_stat = 0;
+ } break;
+
+ case 4: {
+ if (icnt <= 0) {
+ goto out;
+ }
+ if (lb_cnt < inst->lbchars_len &&
+ *ps == (unsigned char)inst->lbchars[lb_cnt]) {
+ lb_cnt++;
+ scan_stat = 5;
+ }
+ if (*ps != '\t' && *ps != ' ') {
+ err = PHP_CONV_ERR_INVALID_SEQ;
+ goto out;
+ }
+ ps++, icnt--;
+ } break;
+
+ case 5: {
+ if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') {
+ /* auto-detect soft line breaks, found network line break */
+ lb_cnt = lb_ptr = 0;
+ scan_stat = 0;
+ ps++, icnt--; /* consume \n */
+ } else if (!inst->lbchars && lb_cnt > 0) {
+ /* auto-detect soft line breaks, found mac line break */
+ lb_cnt = lb_ptr = 0;
+ scan_stat = 0;
+ } else if (lb_cnt >= inst->lbchars_len) {
+ /* soft line break */
+ lb_cnt = lb_ptr = 0;
+ scan_stat = 0;
+ } else if (icnt > 0) {
+ if (*ps == (unsigned char)inst->lbchars[lb_cnt]) {
+ lb_cnt++;
+ ps++, icnt--;
+ } else {
+ scan_stat = 6; /* no break for short-cut */
+ }
+ } else {
+ goto out;
+ }
+ } break;
+
+ case 6: {
+ if (lb_ptr < lb_cnt) {
+ if (ocnt < 1) {
+ err = PHP_CONV_ERR_TOO_BIG;
+ goto out;
+ }
+ *(pd++) = inst->lbchars[lb_ptr++];
+ ocnt--;
+ } else {
+ scan_stat = 0;
+ lb_cnt = lb_ptr = 0;
+ }
+ } break;
+ }
+ }
+out:
+ *in_pp = (const char *)ps;
+ *in_left_p = icnt;
+ *out_pp = (char *)pd;
+ *out_left_p = ocnt;
+ inst->scan_stat = scan_stat;
+ inst->lb_ptr = lb_ptr;
+ inst->lb_cnt = lb_cnt;
+ inst->next_char = next_char;
+
+ return err;
+}
+static php_conv_err_t php_conv_qprint_decode_ctor(php_conv_qprint_decode *inst, const char *lbchars, size_t lbchars_len, int lbchars_dup, int persistent)
+{
+ inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_decode_convert;
+ inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_decode_dtor;
+ inst->scan_stat = 0;
+ inst->next_char = 0;
+ inst->lb_ptr = inst->lb_cnt = 0;
+ if (lbchars != NULL) {
+ inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
+ inst->lbchars_len = lbchars_len;
+ } else {
+ inst->lbchars = NULL;
+ inst->lbchars_len = 0;
+ }
+ inst->lbchars_dup = lbchars_dup;
+ inst->persistent = persistent;
+ return PHP_CONV_ERR_SUCCESS;
+}
+/* }}} */
+
+typedef struct _php_convert_filter {
+ php_conv *cd;
+ int persistent;
+ char *filtername;
+ char stub[128];
+ size_t stub_len;
+} php_convert_filter;
+
+#define PHP_CONV_BASE64_ENCODE 1
+#define PHP_CONV_BASE64_DECODE 2
+#define PHP_CONV_QPRINT_ENCODE 3
+#define PHP_CONV_QPRINT_DECODE 4
+
+static php_conv_err_t php_conv_get_string_prop_ex(const HashTable *ht, char **pretval, size_t *pretval_len, char *field_name, size_t field_name_len, int persistent)
+{
+ zval **tmpval;
+
+ *pretval = NULL;
+ *pretval_len = 0;
+
+ if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
+ if (Z_TYPE_PP(tmpval) != IS_STRING) {
+ zval zt = **tmpval;
+
+ convert_to_string(&zt);
+
+ if (NULL == (*pretval = pemalloc(Z_STRLEN(zt) + 1, persistent))) {
+ return PHP_CONV_ERR_ALLOC;
+ }
+
+ *pretval_len = Z_STRLEN(zt);
+ memcpy(*pretval, Z_STRVAL(zt), Z_STRLEN(zt) + 1);
+ zval_dtor(&zt);
+ } else {
+ if (NULL == (*pretval = pemalloc(Z_STRLEN_PP(tmpval) + 1, persistent))) {
+ return PHP_CONV_ERR_ALLOC;
+ }
+ *pretval_len = Z_STRLEN_PP(tmpval);
+ memcpy(*pretval, Z_STRVAL_PP(tmpval), Z_STRLEN_PP(tmpval) + 1);
+ }
+ } else {
+ return PHP_CONV_ERR_NOT_FOUND;
+ }
+ return PHP_CONV_ERR_SUCCESS;
+}
+
+#if IT_WAS_USED
+static php_conv_err_t php_conv_get_long_prop_ex(const HashTable *ht, long *pretval, char *field_name, size_t field_name_len)
+{
+ zval **tmpval;
+
+ *pretval = 0;
+
+ if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
+ zval tmp, *ztval = *tmpval;
+
+ if (Z_TYPE_PP(tmpval) != IS_LONG) {
+ tmp = *ztval;
+ zval_copy_ctor(&tmp);
+ convert_to_long(&tmp);
+ ztval = &tmp;
+ }
+ *pretval = Z_LVAL_P(ztval);
+ } else {
+ return PHP_CONV_ERR_NOT_FOUND;
+ }
+ return PHP_CONV_ERR_SUCCESS;
+}
+#endif
+
+static php_conv_err_t php_conv_get_ulong_prop_ex(const HashTable *ht, unsigned long *pretval, char *field_name, size_t field_name_len)
+{
+ zval **tmpval;
+
+ *pretval = 0;
+
+ if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
+ zval tmp, *ztval = *tmpval;
+
+ if (Z_TYPE_PP(tmpval) != IS_LONG) {
+ tmp = *ztval;
+ zval_copy_ctor(&tmp);
+ convert_to_long(&tmp);
+ ztval = &tmp;
+ }
+ if (Z_LVAL_P(ztval) < 0) {
+ *pretval = 0;
+ } else {
+ *pretval = Z_LVAL_P(ztval);
+ }
+ } else {
+ return PHP_CONV_ERR_NOT_FOUND;
+ }
+ return PHP_CONV_ERR_SUCCESS;
+}
+
+static php_conv_err_t php_conv_get_bool_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len)
+{
+ zval **tmpval;
+
+ *pretval = 0;
+
+ if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
+ zval tmp, *ztval = *tmpval;
+
+ if (Z_TYPE_PP(tmpval) != IS_BOOL) {
+ tmp = *ztval;
+ zval_copy_ctor(&tmp);
+ convert_to_boolean(&tmp);
+ ztval = &tmp;
+ }
+ *pretval = Z_BVAL_P(ztval);
+ } else {
+ return PHP_CONV_ERR_NOT_FOUND;
+ }
+ return PHP_CONV_ERR_SUCCESS;
+}
+
+
+#if IT_WAS_USED
+static int php_conv_get_int_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len)
+{
+ long l;
+ php_conv_err_t err;
+
+ *pretval = 0;
+
+ if ((err = php_conv_get_long_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) {
+ *pretval = l;
+ }
+ return err;
+}
+#endif
+
+static int php_conv_get_uint_prop_ex(const HashTable *ht, unsigned int *pretval, char *field_name, size_t field_name_len)
+{
+ long l;
+ php_conv_err_t err;
+
+ *pretval = 0;
+
+ if ((err = php_conv_get_ulong_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) {
+ *pretval = l;
+ }
+ return err;
+}
+
+#define GET_STR_PROP(ht, var, var_len, fldname, persistent) \
+ php_conv_get_string_prop_ex(ht, &var, &var_len, fldname, sizeof(fldname), persistent)
+
+#define GET_INT_PROP(ht, var, fldname) \
+ php_conv_get_int_prop_ex(ht, &var, fldname, sizeof(fldname))
+
+#define GET_UINT_PROP(ht, var, fldname) \
+ php_conv_get_uint_prop_ex(ht, &var, fldname, sizeof(fldname))
+
+#define GET_BOOL_PROP(ht, var, fldname) \
+ php_conv_get_bool_prop_ex(ht, &var, fldname, sizeof(fldname))
+
+static php_conv *php_conv_open(int conv_mode, const HashTable *options, int persistent)
+{
+ /* FIXME: I'll have to replace this ugly code by something neat
+ (factories?) in the near future. */
+ php_conv *retval = NULL;
+
+ switch (conv_mode) {
+ case PHP_CONV_BASE64_ENCODE: {
+ unsigned int line_len = 0;
+ char *lbchars = NULL;
+ size_t lbchars_len;
+
+ if (options != NULL) {
+ GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
+ GET_UINT_PROP(options, line_len, "line-length");
+ if (line_len < 4) {
+ if (lbchars != NULL) {
+ pefree(lbchars, 0);
+ }
+ lbchars = NULL;
+ } else {
+ if (lbchars == NULL) {
+ lbchars = pestrdup("\r\n", 0);
+ lbchars_len = 2;
+ }
+ }
+ }
+ retval = pemalloc(sizeof(php_conv_base64_encode), persistent);
+ if (lbchars != NULL) {
+ if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, line_len, lbchars, lbchars_len, 1, persistent)) {
+ if (lbchars != NULL) {
+ pefree(lbchars, 0);
+ }
+ goto out_failure;
+ }
+ pefree(lbchars, 0);
+ } else {
+ if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, 0, NULL, 0, 0, persistent)) {
+ goto out_failure;
+ }
+ }
+ } break;
+
+ case PHP_CONV_BASE64_DECODE:
+ retval = pemalloc(sizeof(php_conv_base64_decode), persistent);
+ if (php_conv_base64_decode_ctor((php_conv_base64_decode *)retval)) {
+ goto out_failure;
+ }
+ break;
+
+ case PHP_CONV_QPRINT_ENCODE: {
+ unsigned int line_len = 0;
+ char *lbchars = NULL;
+ size_t lbchars_len;
+ int opts = 0;
+
+ if (options != NULL) {
+ int opt_binary = 0;
+ int opt_force_encode_first = 0;
+
+ GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
+ GET_UINT_PROP(options, line_len, "line-length");
+ GET_BOOL_PROP(options, opt_binary, "binary");
+ GET_BOOL_PROP(options, opt_force_encode_first, "force-encode-first");
+
+ if (line_len < 4) {
+ if (lbchars != NULL) {
+ pefree(lbchars, 0);
+ }
+ lbchars = NULL;
+ } else {
+ if (lbchars == NULL) {
+ lbchars = pestrdup("\r\n", 0);
+ lbchars_len = 2;
+ }
+ }
+ opts |= (opt_binary ? PHP_CONV_QPRINT_OPT_BINARY : 0);
+ opts |= (opt_force_encode_first ? PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST : 0);
+ }
+ retval = pemalloc(sizeof(php_conv_qprint_encode), persistent);
+ if (lbchars != NULL) {
+ if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, line_len, lbchars, lbchars_len, 1, opts, persistent)) {
+ pefree(lbchars, 0);
+ goto out_failure;
+ }
+ pefree(lbchars, 0);
+ } else {
+ if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, 0, NULL, 0, 0, opts, persistent)) {
+ goto out_failure;
+ }
+ }
+ } break;
+
+ case PHP_CONV_QPRINT_DECODE: {
+ char *lbchars = NULL;
+ size_t lbchars_len;
+
+ if (options != NULL) {
+ /* If line-break-chars are not specified, filter will attempt to detect line endings (\r, \n, or \r\n) */
+ GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
+ }
+
+ retval = pemalloc(sizeof(php_conv_qprint_decode), persistent);
+ if (lbchars != NULL) {
+ if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, lbchars, lbchars_len, 1, persistent)) {
+ pefree(lbchars, 0);
+ goto out_failure;
+ }
+ pefree(lbchars, 0);
+ } else {
+ if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, NULL, 0, 0, persistent)) {
+ goto out_failure;
+ }
+ }
+ } break;
+
+ default:
+ retval = NULL;
+ break;
+ }
+ return retval;
+
+out_failure:
+ if (retval != NULL) {
+ pefree(retval, persistent);
+ }
+ return NULL;
+}
+
+#undef GET_STR_PROP
+#undef GET_INT_PROP
+#undef GET_UINT_PROP
+#undef GET_BOOL_PROP
+
+static int php_convert_filter_ctor(php_convert_filter *inst,
+ int conv_mode, HashTable *conv_opts,
+ const char *filtername, int persistent)
+{
+ inst->persistent = persistent;
+ inst->filtername = pestrdup(filtername, persistent);
+ inst->stub_len = 0;
+
+ if ((inst->cd = php_conv_open(conv_mode, conv_opts, persistent)) == NULL) {
+ goto out_failure;
+ }
+
+ return SUCCESS;
+
+out_failure:
+ if (inst->cd != NULL) {
+ php_conv_dtor(inst->cd);
+ pefree(inst->cd, persistent);
+ }
+ if (inst->filtername != NULL) {
+ pefree(inst->filtername, persistent);
+ }
+ return FAILURE;
+}
+
+static void php_convert_filter_dtor(php_convert_filter *inst)
+{
+ if (inst->cd != NULL) {
+ php_conv_dtor(inst->cd);
+ pefree(inst->cd, inst->persistent);
+ }
+
+ if (inst->filtername != NULL) {
+ pefree(inst->filtername, inst->persistent);
+ }
+}
+
+/* {{{ strfilter_convert_append_bucket */
+static int strfilter_convert_append_bucket(
+ php_convert_filter *inst,
+ php_stream *stream, php_stream_filter *filter,
+ php_stream_bucket_brigade *buckets_out,
+ const char *ps, size_t buf_len, size_t *consumed,
+ int persistent TSRMLS_DC)
+{
+ php_conv_err_t err;
+ php_stream_bucket *new_bucket;
+ char *out_buf = NULL;
+ size_t out_buf_size;
+ char *pd;
+ const char *pt;
+ size_t ocnt, icnt, tcnt;
+ size_t initial_out_buf_size;
+
+ if (ps == NULL) {
+ initial_out_buf_size = 64;
+ icnt = 1;
+ } else {
+ initial_out_buf_size = buf_len;
+ icnt = buf_len;
+ }
+
+ out_buf_size = ocnt = initial_out_buf_size;
+ if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
+ return FAILURE;
+ }
+
+ pd = out_buf;
+
+ if (inst->stub_len > 0) {
+ pt = inst->stub;
+ tcnt = inst->stub_len;
+
+ while (tcnt > 0) {
+ err = php_conv_convert(inst->cd, &pt, &tcnt, &pd, &ocnt);
+
+ switch (err) {
+ case PHP_CONV_ERR_INVALID_SEQ:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername);
+ goto out_failure;
+
+ case PHP_CONV_ERR_MORE:
+ if (ps != NULL) {
+ if (icnt > 0) {
+ if (inst->stub_len >= sizeof(inst->stub)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername);
+ goto out_failure;
+ }
+ inst->stub[inst->stub_len++] = *(ps++);
+ icnt--;
+ pt = inst->stub;
+ tcnt = inst->stub_len;
+ } else {
+ tcnt = 0;
+ break;
+ }
+ }
+ break;
+
+ case PHP_CONV_ERR_UNEXPECTED_EOS:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected end of stream", inst->filtername);
+ goto out_failure;
+
+ case PHP_CONV_ERR_TOO_BIG: {
+ char *new_out_buf;
+ size_t new_out_buf_size;
+
+ new_out_buf_size = out_buf_size << 1;
+
+ if (new_out_buf_size < out_buf_size) {
+ /* whoa! no bigger buckets are sold anywhere... */
+ if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
+ goto out_failure;
+ }
+
+ php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
+
+ out_buf_size = ocnt = initial_out_buf_size;
+ if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
+ return FAILURE;
+ }
+ pd = out_buf;
+ } else {
+ if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
+ if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
+ goto out_failure;
+ }
+
+ php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
+ return FAILURE;
+ }
+
+ pd = new_out_buf + (pd - out_buf);
+ ocnt += (new_out_buf_size - out_buf_size);
+ out_buf = new_out_buf;
+ out_buf_size = new_out_buf_size;
+ }
+ } break;
+
+ case PHP_CONV_ERR_UNKNOWN:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername);
+ goto out_failure;
+
+ default:
+ break;
+ }
+ }
+ memmove(inst->stub, pt, tcnt);
+ inst->stub_len = tcnt;
+ }
+
+ while (icnt > 0) {
+ err = ((ps == NULL ? php_conv_convert(inst->cd, NULL, NULL, &pd, &ocnt):
+ php_conv_convert(inst->cd, &ps, &icnt, &pd, &ocnt)));
+ switch (err) {
+ case PHP_CONV_ERR_INVALID_SEQ:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername);
+ goto out_failure;
+
+ case PHP_CONV_ERR_MORE:
+ if (ps != NULL) {
+ if (icnt > sizeof(inst->stub)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername);
+ goto out_failure;
+ }
+ memcpy(inst->stub, ps, icnt);
+ inst->stub_len = icnt;
+ ps += icnt;
+ icnt = 0;
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected octet values", inst->filtername);
+ goto out_failure;
+ }
+ break;
+
+ case PHP_CONV_ERR_TOO_BIG: {
+ char *new_out_buf;
+ size_t new_out_buf_size;
+
+ new_out_buf_size = out_buf_size << 1;
+
+ if (new_out_buf_size < out_buf_size) {
+ /* whoa! no bigger buckets are sold anywhere... */
+ if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
+ goto out_failure;
+ }
+
+ php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
+
+ out_buf_size = ocnt = initial_out_buf_size;
+ if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
+ return FAILURE;
+ }
+ pd = out_buf;
+ } else {
+ if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
+ if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
+ goto out_failure;
+ }
+
+ php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
+ return FAILURE;
+ }
+ pd = new_out_buf + (pd - out_buf);
+ ocnt += (new_out_buf_size - out_buf_size);
+ out_buf = new_out_buf;
+ out_buf_size = new_out_buf_size;
+ }
+ } break;
+
+ case PHP_CONV_ERR_UNKNOWN:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername);
+ goto out_failure;
+
+ default:
+ if (ps == NULL) {
+ icnt = 0;
+ }
+ break;
+ }
+ }
+
+ if (out_buf_size - ocnt > 0) {
+ if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
+ goto out_failure;
+ }
+ php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
+ } else {
+ pefree(out_buf, persistent);
+ }
+ *consumed += buf_len - icnt;
+
+ return SUCCESS;
+
+out_failure:
+ pefree(out_buf, persistent);
+ return FAILURE;
+}
+/* }}} */
+
+static php_stream_filter_status_t strfilter_convert_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_stream_bucket *bucket = NULL;
+ size_t consumed = 0;
+ php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract;
+
+ while (buckets_in->head != NULL) {
+ bucket = buckets_in->head;
+
+ php_stream_bucket_unlink(bucket TSRMLS_CC);
+
+ if (strfilter_convert_append_bucket(inst, stream, thisfilter,
+ buckets_out, bucket->buf, bucket->buflen, &consumed,
+ php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
+ goto out_failure;
+ }
+
+ php_stream_bucket_delref(bucket TSRMLS_CC);
+ }
+
+ if (flags != PSFS_FLAG_NORMAL) {
+ if (strfilter_convert_append_bucket(inst, stream, thisfilter,
+ buckets_out, NULL, 0, &consumed,
+ php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
+ goto out_failure;
+ }
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return PSFS_PASS_ON;
+
+out_failure:
+ if (bucket != NULL) {
+ php_stream_bucket_delref(bucket TSRMLS_CC);
+ }
+ return PSFS_ERR_FATAL;
+}
+
+static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC)
+{
+ assert(thisfilter->abstract != NULL);
+
+ php_convert_filter_dtor((php_convert_filter *)thisfilter->abstract);
+ pefree(thisfilter->abstract, ((php_convert_filter *)thisfilter->abstract)->persistent);
+}
+
+static php_stream_filter_ops strfilter_convert_ops = {
+ strfilter_convert_filter,
+ strfilter_convert_dtor,
+ "convert.*"
+};
+
+static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ php_convert_filter *inst;
+ php_stream_filter *retval = NULL;
+
+ char *dot;
+ int conv_mode = 0;
+
+ if (filterparams != NULL && Z_TYPE_P(filterparams) != IS_ARRAY) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid filter parameter", filtername);
+ return NULL;
+ }
+
+ if ((dot = strchr(filtername, '.')) == NULL) {
+ return NULL;
+ }
+ ++dot;
+
+ inst = pemalloc(sizeof(php_convert_filter), persistent);
+
+ if (strcasecmp(dot, "base64-encode") == 0) {
+ conv_mode = PHP_CONV_BASE64_ENCODE;
+ } else if (strcasecmp(dot, "base64-decode") == 0) {
+ conv_mode = PHP_CONV_BASE64_DECODE;
+ } else if (strcasecmp(dot, "quoted-printable-encode") == 0) {
+ conv_mode = PHP_CONV_QPRINT_ENCODE;
+ } else if (strcasecmp(dot, "quoted-printable-decode") == 0) {
+ conv_mode = PHP_CONV_QPRINT_DECODE;
+ }
+
+ if (php_convert_filter_ctor(inst, conv_mode,
+ (filterparams != NULL ? Z_ARRVAL_P(filterparams) : NULL),
+ filtername, persistent) != SUCCESS) {
+ goto out;
+ }
+
+ retval = php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent);
+out:
+ if (retval == NULL) {
+ pefree(inst, persistent);
+ }
+
+ return retval;
+}
+
+static php_stream_filter_factory strfilter_convert_factory = {
+ strfilter_convert_create
+};
+/* }}} */
+
+/* {{{ consumed filter implementation */
+typedef struct _php_consumed_filter_data {
+ int persistent;
+ size_t consumed;
+ off_t offset;
+} php_consumed_filter_data;
+
+static php_stream_filter_status_t consumed_filter_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_consumed_filter_data *data = (php_consumed_filter_data *)(thisfilter->abstract);
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+
+ if (data->offset == ~0) {
+ data->offset = php_stream_tell(stream);
+ }
+ while ((bucket = buckets_in->head) != NULL) {
+ php_stream_bucket_unlink(bucket TSRMLS_CC);
+ consumed += bucket->buflen;
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+ }
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+ if (flags & PSFS_FLAG_FLUSH_CLOSE) {
+ php_stream_seek(stream, data->offset + data->consumed, SEEK_SET);
+ }
+ data->consumed += consumed;
+
+ return PSFS_PASS_ON;
+}
+
+static void consumed_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
+{
+ if (thisfilter && thisfilter->abstract) {
+ php_consumed_filter_data *data = (php_consumed_filter_data*)thisfilter->abstract;
+ pefree(data, data->persistent);
+ }
+}
+
+static php_stream_filter_ops consumed_filter_ops = {
+ consumed_filter_filter,
+ consumed_filter_dtor,
+ "consumed"
+};
+
+static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ php_stream_filter_ops *fops = NULL;
+ php_consumed_filter_data *data;
+
+ if (strcasecmp(filtername, "consumed")) {
+ return NULL;
+ }
+
+ /* Create this filter */
+ data = pecalloc(1, sizeof(php_consumed_filter_data), persistent);
+ if (!data) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_consumed_filter_data));
+ return NULL;
+ }
+ data->persistent = persistent;
+ data->consumed = 0;
+ data->offset = ~0;
+ fops = &consumed_filter_ops;
+
+ return php_stream_filter_alloc(fops, data, persistent);
+}
+
+php_stream_filter_factory consumed_filter_factory = {
+ consumed_filter_create
+};
+
+/* }}} */
+
+/* {{{ chunked filter implementation */
+typedef enum _php_chunked_filter_state {
+ CHUNK_SIZE_START,
+ CHUNK_SIZE,
+ CHUNK_SIZE_EXT,
+ CHUNK_SIZE_CR,
+ CHUNK_SIZE_LF,
+ CHUNK_BODY,
+ CHUNK_BODY_CR,
+ CHUNK_BODY_LF,
+ CHUNK_TRAILER,
+ CHUNK_ERROR
+} php_chunked_filter_state;
+
+typedef struct _php_chunked_filter_data {
+ php_chunked_filter_state state;
+ size_t chunk_size;
+ int persistent;
+} php_chunked_filter_data;
+
+static int php_dechunk(char *buf, int len, php_chunked_filter_data *data)
+{
+ char *p = buf;
+ char *end = p + len;
+ char *out = buf;
+ int out_len = 0;
+
+ while (p < end) {
+ switch (data->state) {
+ case CHUNK_SIZE_START:
+ data->chunk_size = 0;
+ case CHUNK_SIZE:
+ while (p < end) {
+ if (*p >= '0' && *p <= '9') {
+ data->chunk_size = (data->chunk_size * 16) + (*p - '0');
+ } else if (*p >= 'A' && *p <= 'F') {
+ data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10);
+ } else if (*p >= 'a' && *p <= 'f') {
+ data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10);
+ } else if (data->state == CHUNK_SIZE_START) {
+ data->state = CHUNK_ERROR;
+ break;
+ } else {
+ data->state = CHUNK_SIZE_EXT;
+ break;
+ }
+ data->state = CHUNK_SIZE;
+ p++;
+ }
+ if (data->state == CHUNK_ERROR) {
+ continue;
+ } else if (p == end) {
+ return out_len;
+ }
+ case CHUNK_SIZE_EXT:
+ /* skip extension */
+ while (p < end && *p != '\r' && *p != '\n') {
+ p++;
+ }
+ if (p == end) {
+ return out_len;
+ }
+ case CHUNK_SIZE_CR:
+ if (*p == '\r') {
+ p++;
+ if (p == end) {
+ data->state = CHUNK_SIZE_LF;
+ return out_len;
+ }
+ }
+ case CHUNK_SIZE_LF:
+ if (*p == '\n') {
+ p++;
+ if (data->chunk_size == 0) {
+ /* last chunk */
+ data->state = CHUNK_TRAILER;
+ continue;
+ } else if (p == end) {
+ data->state = CHUNK_BODY;
+ return out_len;
+ }
+ } else {
+ data->state = CHUNK_ERROR;
+ continue;
+ }
+ case CHUNK_BODY:
+ if ((size_t) (end - p) >= data->chunk_size) {
+ if (p != out) {
+ memmove(out, p, data->chunk_size);
+ }
+ out += data->chunk_size;
+ out_len += data->chunk_size;
+ p += data->chunk_size;
+ if (p == end) {
+ data->state = CHUNK_BODY_CR;
+ return out_len;
+ }
+ } else {
+ if (p != out) {
+ memmove(out, p, end - p);
+ }
+ data->chunk_size -= end - p;
+ data->state=CHUNK_BODY;
+ out_len += end - p;
+ return out_len;
+ }
+ case CHUNK_BODY_CR:
+ if (*p == '\r') {
+ p++;
+ if (p == end) {
+ data->state = CHUNK_BODY_LF;
+ return out_len;
+ }
+ }
+ case CHUNK_BODY_LF:
+ if (*p == '\n') {
+ p++;
+ data->state = CHUNK_SIZE_START;
+ continue;
+ } else {
+ data->state = CHUNK_ERROR;
+ continue;
+ }
+ case CHUNK_TRAILER:
+ /* ignore trailer */
+ p = end;
+ continue;
+ case CHUNK_ERROR:
+ if (p != out) {
+ memmove(out, p, end - p);
+ }
+ out_len += end - p;
+ return out_len;
+ }
+ }
+ return out_len;
+}
+
+static php_stream_filter_status_t php_chunked_filter(
+ php_stream *stream,
+ php_stream_filter *thisfilter,
+ php_stream_bucket_brigade *buckets_in,
+ php_stream_bucket_brigade *buckets_out,
+ size_t *bytes_consumed,
+ int flags
+ TSRMLS_DC)
+{
+ php_stream_bucket *bucket;
+ size_t consumed = 0;
+ php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract;
+
+ while (buckets_in->head) {
+ bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
+ consumed += bucket->buflen;
+ bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, data);
+ php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
+ }
+
+ if (bytes_consumed) {
+ *bytes_consumed = consumed;
+ }
+
+ return PSFS_PASS_ON;
+}
+
+static void php_chunked_dtor(php_stream_filter *thisfilter TSRMLS_DC)
+{
+ if (thisfilter && thisfilter->abstract) {
+ php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract;
+ pefree(data, data->persistent);
+ }
+}
+
+static php_stream_filter_ops chunked_filter_ops = {
+ php_chunked_filter,
+ php_chunked_dtor,
+ "dechunk"
+};
+
+static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
+{
+ php_stream_filter_ops *fops = NULL;
+ php_chunked_filter_data *data;
+
+ if (strcasecmp(filtername, "dechunk")) {
+ return NULL;
+ }
+
+ /* Create this filter */
+ data = (php_chunked_filter_data *)pecalloc(1, sizeof(php_chunked_filter_data), persistent);
+ if (!data) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_chunked_filter_data));
+ return NULL;
+ }
+ data->state = CHUNK_SIZE_START;
+ data->chunk_size = 0;
+ data->persistent = persistent;
+ fops = &chunked_filter_ops;
+
+ return php_stream_filter_alloc(fops, data, persistent);
+}
+
+static php_stream_filter_factory chunked_filter_factory = {
+ chunked_filter_create
+};
+/* }}} */
+
+static const struct {
+ php_stream_filter_ops *ops;
+ php_stream_filter_factory *factory;
+} standard_filters[] = {
+ { &strfilter_rot13_ops, &strfilter_rot13_factory },
+ { &strfilter_toupper_ops, &strfilter_toupper_factory },
+ { &strfilter_tolower_ops, &strfilter_tolower_factory },
+ { &strfilter_strip_tags_ops, &strfilter_strip_tags_factory },
+ { &strfilter_convert_ops, &strfilter_convert_factory },
+ { &consumed_filter_ops, &consumed_filter_factory },
+ { &chunked_filter_ops, &chunked_filter_factory },
+ /* additional filters to go here */
+ { NULL, NULL }
+};
+
+/* {{{ filter MINIT and MSHUTDOWN */
+PHP_MINIT_FUNCTION(standard_filters)
+{
+ int i;
+
+ for (i = 0; standard_filters[i].ops; i++) {
+ if (FAILURE == php_stream_filter_register_factory(
+ standard_filters[i].ops->label,
+ standard_filters[i].factory
+ TSRMLS_CC)) {
+ return FAILURE;
+ }
+ }
+ return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(standard_filters)
+{
+ int i;
+
+ for (i = 0; standard_filters[i].ops; i++) {
+ php_stream_filter_unregister_factory(standard_filters[i].ops->label TSRMLS_CC);
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */