root/ext/standard/filters.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. strfilter_rot13_filter
  2. strfilter_rot13_create
  3. strfilter_toupper_filter
  4. strfilter_tolower_filter
  5. strfilter_toupper_create
  6. strfilter_tolower_create
  7. php_strip_tags_filter_ctor
  8. php_strip_tags_filter_dtor
  9. strfilter_strip_tags_filter
  10. strfilter_strip_tags_dtor
  11. strfilter_strip_tags_create
  12. php_conv_base64_encode_ctor
  13. php_conv_base64_encode_dtor
  14. php_conv_base64_encode_flush
  15. php_conv_base64_encode_convert
  16. php_conv_base64_decode_ctor
  17. php_conv_base64_decode_dtor
  18. php_conv_base64_decode_convert
  19. php_conv_qprint_encode_dtor
  20. php_conv_qprint_encode_convert
  21. php_conv_qprint_encode_ctor
  22. php_conv_qprint_decode_dtor
  23. php_conv_qprint_decode_convert
  24. php_conv_qprint_decode_ctor
  25. php_conv_get_string_prop_ex
  26. php_conv_get_long_prop_ex
  27. php_conv_get_ulong_prop_ex
  28. php_conv_get_bool_prop_ex
  29. php_conv_get_int_prop_ex
  30. php_conv_get_uint_prop_ex
  31. php_conv_open
  32. php_convert_filter_ctor
  33. php_convert_filter_dtor
  34. strfilter_convert_append_bucket
  35. strfilter_convert_filter
  36. strfilter_convert_dtor
  37. strfilter_convert_create
  38. consumed_filter_filter
  39. consumed_filter_dtor
  40. consumed_filter_create
  41. php_dechunk
  42. php_chunked_filter
  43. php_chunked_dtor
  44. chunked_filter_create
  45. PHP_MINIT_FUNCTION
  46. PHP_MSHUTDOWN_FUNCTION

   1 /*
   2    +----------------------------------------------------------------------+
   3    | PHP Version 5                                                        |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1997-2016 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:                                                             |
  16    | Wez Furlong (wez@thebrainroom.com)                                   |
  17    | Sara Golemon (pollita@php.net)                                       |
  18    | Moriyoshi Koizumi (moriyoshi@php.net)                                |
  19    | Marcus Boerger (helly@php.net)                                       |
  20    +----------------------------------------------------------------------+
  21 */
  22 
  23 /* $Id$ */
  24 
  25 #include "php.h"
  26 #include "php_globals.h"
  27 #include "ext/standard/basic_functions.h"
  28 #include "ext/standard/file.h"
  29 #include "ext/standard/php_string.h"
  30 #include "ext/standard/php_smart_str.h"
  31 
  32 /* {{{ rot13 stream filter implementation */
  33 static char rot13_from[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  34 static char rot13_to[] = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM";
  35 
  36 static php_stream_filter_status_t strfilter_rot13_filter(
  37         php_stream *stream,
  38         php_stream_filter *thisfilter,
  39         php_stream_bucket_brigade *buckets_in,
  40         php_stream_bucket_brigade *buckets_out,
  41         size_t *bytes_consumed,
  42         int flags
  43         TSRMLS_DC)
  44 {
  45         php_stream_bucket *bucket;
  46         size_t consumed = 0;
  47 
  48         while (buckets_in->head) {
  49                 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
  50                 
  51                 php_strtr(bucket->buf, bucket->buflen, rot13_from, rot13_to, 52);
  52                 consumed += bucket->buflen;
  53                 
  54                 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
  55         }
  56 
  57         if (bytes_consumed) {
  58                 *bytes_consumed = consumed;
  59         }
  60         
  61         return PSFS_PASS_ON;
  62 }
  63 
  64 static php_stream_filter_ops strfilter_rot13_ops = {
  65         strfilter_rot13_filter,
  66         NULL,
  67         "string.rot13"
  68 };
  69 
  70 static php_stream_filter *strfilter_rot13_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
  71 {
  72         return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent);
  73 }
  74 
  75 static php_stream_filter_factory strfilter_rot13_factory = {
  76         strfilter_rot13_create
  77 };
  78 /* }}} */
  79 
  80 /* {{{ string.toupper / string.tolower stream filter implementation */
  81 static char lowercase[] = "abcdefghijklmnopqrstuvwxyz";
  82 static char uppercase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  83 
  84 static php_stream_filter_status_t strfilter_toupper_filter(
  85         php_stream *stream,
  86         php_stream_filter *thisfilter,
  87         php_stream_bucket_brigade *buckets_in,
  88         php_stream_bucket_brigade *buckets_out,
  89         size_t *bytes_consumed,
  90         int flags
  91         TSRMLS_DC)
  92 {
  93         php_stream_bucket *bucket;
  94         size_t consumed = 0;
  95 
  96         while (buckets_in->head) {
  97                 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
  98                 
  99                 php_strtr(bucket->buf, bucket->buflen, lowercase, uppercase, 26);
 100                 consumed += bucket->buflen;
 101                 
 102                 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
 103         }
 104 
 105         if (bytes_consumed) {
 106                 *bytes_consumed = consumed;
 107         }
 108 
 109         return PSFS_PASS_ON;
 110 }
 111 
 112 static php_stream_filter_status_t strfilter_tolower_filter(
 113         php_stream *stream,
 114         php_stream_filter *thisfilter,
 115         php_stream_bucket_brigade *buckets_in,
 116         php_stream_bucket_brigade *buckets_out,
 117         size_t *bytes_consumed,
 118         int flags
 119         TSRMLS_DC)
 120 {
 121         php_stream_bucket *bucket;
 122         size_t consumed = 0;
 123 
 124         while (buckets_in->head) {
 125                 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
 126                 
 127                 php_strtr(bucket->buf, bucket->buflen, uppercase, lowercase, 26);
 128                 consumed += bucket->buflen;
 129                 
 130                 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
 131         }
 132 
 133         if (bytes_consumed) {
 134                 *bytes_consumed = consumed;
 135         }
 136 
 137         return PSFS_PASS_ON;
 138 }
 139 
 140 static php_stream_filter_ops strfilter_toupper_ops = {
 141         strfilter_toupper_filter,
 142         NULL,
 143         "string.toupper"
 144 };
 145 
 146 static php_stream_filter_ops strfilter_tolower_ops = {
 147         strfilter_tolower_filter,
 148         NULL,
 149         "string.tolower"
 150 };
 151 
 152 static php_stream_filter *strfilter_toupper_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
 153 {
 154         return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent);
 155 }
 156 
 157 static php_stream_filter *strfilter_tolower_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
 158 {
 159         return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent);
 160 }
 161 
 162 static php_stream_filter_factory strfilter_toupper_factory = {
 163         strfilter_toupper_create
 164 };
 165 
 166 static php_stream_filter_factory strfilter_tolower_factory = {
 167         strfilter_tolower_create
 168 };
 169 /* }}} */
 170 
 171 /* {{{ strip_tags filter implementation */
 172 typedef struct _php_strip_tags_filter {
 173         const char *allowed_tags;
 174         int allowed_tags_len;
 175         int state;
 176         int persistent;
 177 } php_strip_tags_filter;
 178 
 179 static int php_strip_tags_filter_ctor(php_strip_tags_filter *inst, const char *allowed_tags, int allowed_tags_len, int persistent)
 180 {
 181         if (allowed_tags != NULL) {
 182                 if (NULL == (inst->allowed_tags = pemalloc(allowed_tags_len, persistent))) {
 183                         return FAILURE;
 184                 }
 185                 memcpy((char *)inst->allowed_tags, allowed_tags, allowed_tags_len);
 186                 inst->allowed_tags_len = allowed_tags_len; 
 187         } else {
 188                 inst->allowed_tags = NULL;
 189         }
 190         inst->state = 0;
 191         inst->persistent = persistent;
 192 
 193         return SUCCESS;
 194 }       
 195 
 196 static void php_strip_tags_filter_dtor(php_strip_tags_filter *inst)
 197 {
 198         if (inst->allowed_tags != NULL) {
 199                 pefree((void *)inst->allowed_tags, inst->persistent);
 200         }
 201 }
 202 
 203 static php_stream_filter_status_t strfilter_strip_tags_filter(
 204         php_stream *stream,
 205         php_stream_filter *thisfilter,
 206         php_stream_bucket_brigade *buckets_in,
 207         php_stream_bucket_brigade *buckets_out,
 208         size_t *bytes_consumed,
 209         int flags
 210         TSRMLS_DC)
 211 {
 212         php_stream_bucket *bucket;
 213         size_t consumed = 0;
 214         php_strip_tags_filter *inst = (php_strip_tags_filter *) thisfilter->abstract;
 215 
 216         while (buckets_in->head) {
 217                 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
 218                 consumed = bucket->buflen;
 219                 
 220                 bucket->buflen = php_strip_tags(bucket->buf, bucket->buflen, &(inst->state), (char *)inst->allowed_tags, inst->allowed_tags_len);
 221         
 222                 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
 223         }
 224 
 225         if (bytes_consumed) {
 226                 *bytes_consumed = consumed;
 227         }
 228         
 229         return PSFS_PASS_ON;
 230 }
 231 
 232 static void strfilter_strip_tags_dtor(php_stream_filter *thisfilter TSRMLS_DC)
 233 {
 234         assert(thisfilter->abstract != NULL);
 235 
 236         php_strip_tags_filter_dtor((php_strip_tags_filter *)thisfilter->abstract);
 237 
 238         pefree(thisfilter->abstract, ((php_strip_tags_filter *)thisfilter->abstract)->persistent);
 239 }
 240 
 241 static php_stream_filter_ops strfilter_strip_tags_ops = {
 242         strfilter_strip_tags_filter,
 243         strfilter_strip_tags_dtor,
 244         "string.strip_tags"
 245 };
 246 
 247 static php_stream_filter *strfilter_strip_tags_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
 248 {
 249         php_strip_tags_filter *inst;
 250         smart_str tags_ss = { 0, 0, 0 };
 251         
 252         inst = pemalloc(sizeof(php_strip_tags_filter), persistent);
 253 
 254         if (inst == NULL) { /* it's possible pemalloc returns NULL
 255                                                    instead of causing it to bail out */
 256                 return NULL;
 257         }
 258         
 259         if (filterparams != NULL) {
 260                 if (Z_TYPE_P(filterparams) == IS_ARRAY) {
 261                         HashPosition pos;
 262                         zval **tmp;
 263 
 264                         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(filterparams), &pos);
 265                         while (zend_hash_get_current_data_ex(Z_ARRVAL_P(filterparams), (void **) &tmp, &pos) == SUCCESS) {
 266                                 convert_to_string_ex(tmp);
 267                                 smart_str_appendc(&tags_ss, '<');
 268                                 smart_str_appendl(&tags_ss, Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));
 269                                 smart_str_appendc(&tags_ss, '>');
 270                                 zend_hash_move_forward_ex(Z_ARRVAL_P(filterparams), &pos);
 271                         }
 272                         smart_str_0(&tags_ss);
 273                 } else {
 274                         /* FIXME: convert_to_* may clutter zvals and lead it into segfault ? */
 275                         convert_to_string_ex(&filterparams);
 276 
 277                         tags_ss.c = Z_STRVAL_P(filterparams);
 278                         tags_ss.len = Z_STRLEN_P(filterparams);
 279                         tags_ss.a = 0;
 280                 }
 281         }
 282 
 283         if (php_strip_tags_filter_ctor(inst, tags_ss.c, tags_ss.len, persistent) != SUCCESS) {
 284                 if (tags_ss.a != 0) {
 285                         STR_FREE(tags_ss.c);
 286                 }
 287                 pefree(inst, persistent);
 288                 return NULL;
 289         }
 290 
 291         if (tags_ss.a != 0) {
 292                 STR_FREE(tags_ss.c);
 293         }
 294 
 295         return php_stream_filter_alloc(&strfilter_strip_tags_ops, inst, persistent);
 296 }
 297 
 298 static php_stream_filter_factory strfilter_strip_tags_factory = {
 299         strfilter_strip_tags_create
 300 };
 301 
 302 /* }}} */
 303 
 304 /* {{{ base64 / quoted_printable stream filter implementation */
 305 
 306 typedef enum _php_conv_err_t {
 307         PHP_CONV_ERR_SUCCESS = SUCCESS,
 308         PHP_CONV_ERR_UNKNOWN,
 309         PHP_CONV_ERR_TOO_BIG,
 310         PHP_CONV_ERR_INVALID_SEQ,
 311         PHP_CONV_ERR_UNEXPECTED_EOS,
 312         PHP_CONV_ERR_EXISTS,
 313         PHP_CONV_ERR_MORE,
 314         PHP_CONV_ERR_ALLOC,
 315         PHP_CONV_ERR_NOT_FOUND
 316 } php_conv_err_t;
 317 
 318 typedef struct _php_conv php_conv;
 319 
 320 typedef php_conv_err_t (*php_conv_convert_func)(php_conv *, const char **, size_t *, char **, size_t *);
 321 typedef void (*php_conv_dtor_func)(php_conv *);
 322 
 323 struct _php_conv {
 324         php_conv_convert_func convert_op;
 325         php_conv_dtor_func dtor;
 326 };
 327 
 328 #define php_conv_convert(a, b, c, d, e) ((php_conv *)(a))->convert_op((php_conv *)(a), (b), (c), (d), (e))
 329 #define php_conv_dtor(a) ((php_conv *)a)->dtor((a))
 330 
 331 /* {{{ php_conv_base64_encode */
 332 typedef struct _php_conv_base64_encode {
 333         php_conv _super;
 334 
 335         unsigned char erem[3];
 336         size_t erem_len;
 337         unsigned int line_ccnt;
 338         unsigned int line_len;
 339         const char *lbchars;
 340         int lbchars_dup;
 341         size_t lbchars_len;
 342         int persistent;
 343 } php_conv_base64_encode;
 344 
 345 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);
 346 static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst);
 347 
 348 static unsigned char b64_tbl_enc[256] = {
 349         'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
 350         'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
 351         'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
 352         'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
 353         'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
 354         'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
 355         'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
 356         'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
 357         'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
 358         'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
 359         'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
 360         'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
 361         'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
 362         'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
 363         'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
 364         'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
 365 };
 366 
 367 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)
 368 {
 369         inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_encode_convert;
 370         inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_encode_dtor;
 371         inst->erem_len = 0;
 372         inst->line_ccnt = line_len;
 373         inst->line_len = line_len;
 374         if (lbchars != NULL) {
 375                 inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
 376                 inst->lbchars_len = lbchars_len;
 377         } else {
 378                 inst->lbchars = NULL;
 379         }
 380         inst->lbchars_dup = lbchars_dup;
 381         inst->persistent = persistent;
 382         return PHP_CONV_ERR_SUCCESS;
 383 }
 384 
 385 static void php_conv_base64_encode_dtor(php_conv_base64_encode *inst)
 386 {
 387         assert(inst != NULL);
 388         if (inst->lbchars_dup && inst->lbchars != NULL) {
 389                 pefree((void *)inst->lbchars, inst->persistent);
 390         }
 391 }
 392 
 393 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)
 394 {
 395         volatile php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
 396         register unsigned char *pd;
 397         register size_t ocnt;
 398         unsigned int line_ccnt;
 399 
 400         pd = (unsigned char *)(*out_pp);
 401         ocnt = *out_left_p;
 402         line_ccnt = inst->line_ccnt;
 403 
 404         switch (inst->erem_len) {
 405                 case 0:
 406                         /* do nothing */
 407                         break;
 408 
 409                 case 1:
 410                         if (line_ccnt < 4 && inst->lbchars != NULL) {
 411                                 if (ocnt < inst->lbchars_len) {
 412                                         return PHP_CONV_ERR_TOO_BIG;
 413                                 }
 414                                 memcpy(pd, inst->lbchars, inst->lbchars_len);
 415                                 pd += inst->lbchars_len;
 416                                 ocnt -= inst->lbchars_len;
 417                                 line_ccnt = inst->line_len;
 418                         }
 419                         if (ocnt < 4) {
 420                                 err = PHP_CONV_ERR_TOO_BIG;
 421                                 goto out;
 422                         }
 423                         *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
 424                         *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4)];
 425                         *(pd++) = '=';
 426                         *(pd++) = '=';
 427                         inst->erem_len = 0;
 428                         ocnt -= 4;
 429                         line_ccnt -= 4;
 430                         break;
 431 
 432                 case 2: 
 433                         if (line_ccnt < 4 && inst->lbchars != NULL) {
 434                                 if (ocnt < inst->lbchars_len) {
 435                                         return PHP_CONV_ERR_TOO_BIG;
 436                                 }
 437                                 memcpy(pd, inst->lbchars, inst->lbchars_len);
 438                                 pd += inst->lbchars_len;
 439                                 ocnt -= inst->lbchars_len;
 440                                 line_ccnt = inst->line_len;
 441                         }
 442                         if (ocnt < 4) {
 443                                 err = PHP_CONV_ERR_TOO_BIG;
 444                                 goto out;
 445                         }
 446                         *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
 447                         *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (inst->erem[1] >> 4)];
 448                         *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[1] << 2)];
 449                         *(pd++) = '=';
 450                         inst->erem_len = 0;
 451                         ocnt -=4;
 452                         line_ccnt -= 4;
 453                         break;
 454 
 455                 default:
 456                         /* should not happen... */
 457                         err = PHP_CONV_ERR_UNKNOWN;
 458                         break;
 459         }
 460 out:
 461         *out_pp = (char *)pd;
 462         *out_left_p = ocnt;
 463         inst->line_ccnt = line_ccnt;
 464         return err;
 465 }
 466 
 467 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)
 468 {
 469         volatile php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
 470         register size_t ocnt, icnt;
 471         register unsigned char *ps, *pd;
 472         register unsigned int line_ccnt;
 473 
 474         if (in_pp == NULL || in_left_p == NULL) { 
 475                 return php_conv_base64_encode_flush(inst, in_pp, in_left_p, out_pp, out_left_p);
 476         }
 477 
 478         pd = (unsigned char *)(*out_pp);
 479         ocnt = *out_left_p;
 480         ps = (unsigned char *)(*in_pp);
 481         icnt = *in_left_p;
 482         line_ccnt = inst->line_ccnt;
 483 
 484         /* consume the remainder first */
 485         switch (inst->erem_len) {
 486                 case 1:
 487                         if (icnt >= 2) {
 488                                 if (line_ccnt < 4 && inst->lbchars != NULL) {
 489                                         if (ocnt < inst->lbchars_len) {
 490                                                 return PHP_CONV_ERR_TOO_BIG;
 491                                         }
 492                                         memcpy(pd, inst->lbchars, inst->lbchars_len);
 493                                         pd += inst->lbchars_len;
 494                                         ocnt -= inst->lbchars_len;
 495                                         line_ccnt = inst->line_len;
 496                                 }
 497                                 if (ocnt < 4) {
 498                                         err = PHP_CONV_ERR_TOO_BIG;
 499                                         goto out;
 500                                 }
 501                                 *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
 502                                 *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (ps[0] >> 4)];
 503                                 *(pd++) = b64_tbl_enc[(unsigned char)(ps[0] << 2) | (ps[1] >> 6)];
 504                                 *(pd++) = b64_tbl_enc[ps[1]];
 505                                 ocnt -= 4;
 506                                 ps += 2;
 507                                 icnt -= 2;
 508                                 inst->erem_len = 0;
 509                                 line_ccnt -= 4;
 510                         }
 511                         break;
 512 
 513                 case 2: 
 514                         if (icnt >= 1) {
 515                                 if (inst->line_ccnt < 4 && inst->lbchars != NULL) {
 516                                         if (ocnt < inst->lbchars_len) {
 517                                                 return PHP_CONV_ERR_TOO_BIG;
 518                                         }
 519                                         memcpy(pd, inst->lbchars, inst->lbchars_len);
 520                                         pd += inst->lbchars_len;
 521                                         ocnt -= inst->lbchars_len;
 522                                         line_ccnt = inst->line_len;
 523                                 }
 524                                 if (ocnt < 4) {
 525                                         err = PHP_CONV_ERR_TOO_BIG;
 526                                         goto out;
 527                                 }
 528                                 *(pd++) = b64_tbl_enc[(inst->erem[0] >> 2)];
 529                                 *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[0] << 4) | (inst->erem[1] >> 4)];
 530                                 *(pd++) = b64_tbl_enc[(unsigned char)(inst->erem[1] << 2) | (ps[0] >> 6)];
 531                                 *(pd++) = b64_tbl_enc[ps[0]];
 532                                 ocnt -= 4;
 533                                 ps += 1;
 534                                 icnt -= 1;
 535                                 inst->erem_len = 0;
 536                                 line_ccnt -= 4;
 537                         }
 538                         break;
 539         }
 540 
 541         while (icnt >= 3) {
 542                 if (line_ccnt < 4 && inst->lbchars != NULL) {
 543                         if (ocnt < inst->lbchars_len) {
 544                                 err = PHP_CONV_ERR_TOO_BIG;
 545                                 goto out;
 546                         }
 547                         memcpy(pd, inst->lbchars, inst->lbchars_len);
 548                         pd += inst->lbchars_len;
 549                         ocnt -= inst->lbchars_len;
 550                         line_ccnt = inst->line_len;
 551                 }
 552                 if (ocnt < 4) {
 553                         err = PHP_CONV_ERR_TOO_BIG;
 554                         goto out;
 555                 }
 556                 *(pd++) = b64_tbl_enc[ps[0] >> 2];
 557                 *(pd++) = b64_tbl_enc[(unsigned char)(ps[0] << 4) | (ps[1] >> 4)];
 558                 *(pd++) = b64_tbl_enc[(unsigned char)(ps[1] << 2) | (ps[2] >> 6)];
 559                 *(pd++) = b64_tbl_enc[ps[2]];
 560 
 561                 ps += 3;
 562                 icnt -=3;
 563                 ocnt -= 4;
 564                 line_ccnt -= 4;
 565         }
 566         for (;icnt > 0; icnt--) {
 567                 inst->erem[inst->erem_len++] = *(ps++);
 568         }
 569 
 570 out:
 571         *in_pp = (const char *)ps;
 572         *in_left_p = icnt;
 573         *out_pp = (char *)pd;
 574         *out_left_p = ocnt;
 575         inst->line_ccnt = line_ccnt;
 576 
 577         return err;
 578 }
 579 
 580 /* }}} */
 581 
 582 /* {{{ php_conv_base64_decode */
 583 typedef struct _php_conv_base64_decode {
 584         php_conv _super;
 585 
 586         unsigned int urem;
 587         unsigned int urem_nbits;
 588         unsigned int ustat;
 589         int eos;
 590 } php_conv_base64_decode;
 591 
 592 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);
 593 static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst);
 594 
 595 static unsigned int b64_tbl_dec[256] = {
 596         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 597         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 598         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
 599         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64,128, 64, 64,
 600         64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 601         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
 602         64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 603         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
 604         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 605         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 606         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 607         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 608         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 609         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 610         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
 611         64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
 612 };
 613 
 614 static int php_conv_base64_decode_ctor(php_conv_base64_decode *inst)
 615 {
 616         inst->_super.convert_op = (php_conv_convert_func) php_conv_base64_decode_convert;
 617         inst->_super.dtor = (php_conv_dtor_func) php_conv_base64_decode_dtor;
 618 
 619         inst->urem = 0;
 620         inst->urem_nbits = 0;
 621         inst->ustat = 0;
 622         inst->eos = 0;
 623         return SUCCESS;
 624 }
 625 
 626 static void php_conv_base64_decode_dtor(php_conv_base64_decode *inst)
 627 {
 628         /* do nothing */
 629 }
 630 
 631 #define bmask(a) (0xffff >> (16 - a))
 632 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)
 633 {
 634         php_conv_err_t err;
 635 
 636         unsigned int urem, urem_nbits;
 637         unsigned int pack, pack_bcnt;
 638         unsigned char *ps, *pd;
 639         size_t icnt, ocnt;
 640         unsigned int ustat;
 641 
 642         static const unsigned int nbitsof_pack = 8;
 643 
 644         if (in_pp == NULL || in_left_p == NULL) {
 645                 if (inst->eos || inst->urem_nbits == 0) { 
 646                         return PHP_CONV_ERR_SUCCESS;
 647                 }
 648                 return PHP_CONV_ERR_UNEXPECTED_EOS;
 649         }
 650 
 651         err = PHP_CONV_ERR_SUCCESS;
 652 
 653         ps = (unsigned char *)*in_pp;
 654         pd = (unsigned char *)*out_pp;
 655         icnt = *in_left_p;
 656         ocnt = *out_left_p;
 657 
 658         urem = inst->urem;
 659         urem_nbits = inst->urem_nbits;
 660         ustat = inst->ustat;
 661 
 662         pack = 0;
 663         pack_bcnt = nbitsof_pack;
 664 
 665         for (;;) {
 666                 if (pack_bcnt >= urem_nbits) {
 667                         pack_bcnt -= urem_nbits;
 668                         pack |= (urem << pack_bcnt);
 669                         urem_nbits = 0;
 670                 } else {
 671                         urem_nbits -= pack_bcnt;
 672                         pack |= (urem >> urem_nbits);
 673                         urem &= bmask(urem_nbits);
 674                         pack_bcnt = 0;
 675                 }
 676                 if (pack_bcnt > 0) {
 677                         unsigned int i;
 678 
 679                         if (icnt < 1) {
 680                                 break;
 681                         }
 682 
 683                         i = b64_tbl_dec[(unsigned int)*(ps++)];
 684                         icnt--;
 685                         ustat |= i & 0x80;
 686 
 687                         if (!(i & 0xc0)) {
 688                                 if (ustat) {
 689                                         err = PHP_CONV_ERR_INVALID_SEQ;
 690                                         break;
 691                                 }
 692                                 if (6 <= pack_bcnt) {
 693                                         pack_bcnt -= 6;
 694                                         pack |= (i << pack_bcnt);
 695                                         urem = 0;
 696                                 } else {
 697                                         urem_nbits = 6 - pack_bcnt;
 698                                         pack |= (i >> urem_nbits);
 699                                         urem = i & bmask(urem_nbits);
 700                                         pack_bcnt = 0;
 701                                 }
 702                         } else if (ustat) {
 703                                 if (pack_bcnt == 8 || pack_bcnt == 2) {
 704                                         err = PHP_CONV_ERR_INVALID_SEQ;
 705                                         break;
 706                                 }
 707                                 inst->eos = 1;
 708                         }
 709                 }
 710                 if ((pack_bcnt | ustat) == 0) {
 711                         if (ocnt < 1) {
 712                                 err = PHP_CONV_ERR_TOO_BIG;
 713                                 break;
 714                         }
 715                         *(pd++) = pack;
 716                         ocnt--;
 717                         pack = 0;
 718                         pack_bcnt = nbitsof_pack;
 719                 }
 720         }
 721 
 722         if (urem_nbits >= pack_bcnt) {
 723                 urem |= (pack << (urem_nbits - pack_bcnt));
 724                 urem_nbits += (nbitsof_pack - pack_bcnt);
 725         } else {
 726                 urem |= (pack >> (pack_bcnt - urem_nbits));
 727                 urem_nbits += (nbitsof_pack - pack_bcnt);
 728         }
 729 
 730         inst->urem = urem;
 731         inst->urem_nbits = urem_nbits;
 732         inst->ustat = ustat;
 733 
 734         *in_pp = (const char *)ps;
 735         *in_left_p = icnt;
 736         *out_pp = (char *)pd;
 737         *out_left_p = ocnt;
 738 
 739         return err;
 740 }
 741 #undef bmask
 742 /* }}} */
 743 
 744 /* {{{ php_conv_qprint_encode */
 745 typedef struct _php_conv_qprint_encode {
 746         php_conv _super;
 747 
 748         int opts;
 749         unsigned int line_ccnt;
 750         unsigned int line_len;
 751         const char *lbchars;
 752         int lbchars_dup;
 753         size_t lbchars_len;
 754         int persistent;
 755         unsigned int lb_ptr;
 756         unsigned int lb_cnt;
 757 } php_conv_qprint_encode;
 758 
 759 #define PHP_CONV_QPRINT_OPT_BINARY             0x00000001
 760 #define PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST 0x00000002
 761 
 762 static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst);
 763 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);
 764 
 765 static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst)
 766 {
 767         assert(inst != NULL);
 768         if (inst->lbchars_dup && inst->lbchars != NULL) {
 769                 pefree((void *)inst->lbchars, inst->persistent);
 770         }
 771 }
 772 
 773 #define NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, lbchars) \
 774         ((lb_ptr) < (lb_cnt) ? (lbchars)[(lb_ptr)] : *(ps))
 775 
 776 #define CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt) \
 777         if ((lb_ptr) < (lb_cnt)) { \
 778                 (lb_ptr)++; \
 779         } else { \
 780                 (lb_cnt) = (lb_ptr) = 0; \
 781                 --(icnt); \
 782                 (ps)++; \
 783         }
 784 
 785 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)
 786 {
 787         php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
 788         unsigned char *ps, *pd;
 789         size_t icnt, ocnt;
 790         unsigned int c;
 791         unsigned int line_ccnt;
 792         unsigned int lb_ptr;
 793         unsigned int lb_cnt;
 794         unsigned int trail_ws;
 795         int opts;
 796         static char qp_digits[] = "0123456789ABCDEF";
 797 
 798         line_ccnt = inst->line_ccnt;
 799         opts = inst->opts;
 800         lb_ptr = inst->lb_ptr;
 801         lb_cnt = inst->lb_cnt;
 802 
 803         if ((in_pp == NULL || in_left_p == NULL) && (lb_ptr >=lb_cnt)) {
 804                 return PHP_CONV_ERR_SUCCESS;
 805         }
 806 
 807         ps = (unsigned char *)(*in_pp);
 808         icnt = *in_left_p;
 809         pd = (unsigned char *)(*out_pp);
 810         ocnt = *out_left_p;
 811         trail_ws = 0;
 812 
 813         for (;;) {
 814                 if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) {
 815                         /* look ahead for the line break chars to make a right decision
 816                          * how to consume incoming characters */
 817 
 818                         if (icnt > 0 && *ps == inst->lbchars[lb_cnt]) {
 819                                 lb_cnt++;
 820 
 821                                 if (lb_cnt >= inst->lbchars_len) {
 822                                         unsigned int i;
 823 
 824                                         if (ocnt < lb_cnt) {
 825                                                 lb_cnt--;
 826                                                 err = PHP_CONV_ERR_TOO_BIG;
 827                                                 break;
 828                                         }
 829 
 830                                         for (i = 0; i < lb_cnt; i++) {
 831                                                 *(pd++) = inst->lbchars[i];
 832                                                 ocnt--;
 833                                         }
 834                                         line_ccnt = inst->line_len;
 835                                         lb_ptr = lb_cnt = 0;
 836                                 }
 837                                 ps++, icnt--;
 838                                 continue;
 839                         }
 840                 }
 841 
 842                 if (lb_ptr >= lb_cnt && icnt <= 0) {
 843                         break;
 844                 }
 845 
 846                 c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars);
 847 
 848                 if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) &&
 849                         (trail_ws == 0) &&
 850                         (c == '\t' || c == ' ')) {
 851                         if (line_ccnt < 2 && inst->lbchars != NULL) {
 852                                 if (ocnt < inst->lbchars_len + 1) {
 853                                         err = PHP_CONV_ERR_TOO_BIG;
 854                                         break;
 855                                 }
 856 
 857                                 *(pd++) = '=';
 858                                 ocnt--;
 859                                 line_ccnt--;
 860 
 861                                 memcpy(pd, inst->lbchars, inst->lbchars_len);
 862                                 pd += inst->lbchars_len;
 863                                 ocnt -= inst->lbchars_len;
 864                                 line_ccnt = inst->line_len;
 865                         } else {
 866                                 if (ocnt < 1) {
 867                                         err = PHP_CONV_ERR_TOO_BIG;
 868                                         break;
 869                                 }
 870 
 871                                 /* Check to see if this is EOL whitespace. */
 872                                 if (inst->lbchars != NULL) {
 873                                         unsigned char *ps2;
 874                                         unsigned int j, lb_cnt2;
 875 
 876                                         lb_cnt2 = 0;
 877                                         ps2 = ps;
 878                                         trail_ws = 1;
 879 
 880                                         for (j = icnt - 1; j > 0; j--, ps2++) {
 881                                                 if (*ps2 == inst->lbchars[lb_cnt2]) {
 882                                                         lb_cnt2++;
 883                                                         if (lb_cnt2 >= inst->lbchars_len) {
 884                                                                 /* Found trailing ws. Reset to top of main
 885                                                                  * for loop to allow for code to do necessary
 886                                                                  * wrapping/encoding. */
 887                                                                 break;
 888                                                         }
 889                                                 } else if (lb_cnt2 != 0 || (*ps2 != '\t' && *ps2 != ' ')) {
 890                                                         /* At least one non-EOL character following, so
 891                                                          * don't need to encode ws. */
 892                                                         trail_ws = 0;
 893                                                         break;
 894                                                 } else {
 895                                                         trail_ws++;
 896                                                 }
 897                                         }
 898                                 }
 899 
 900                                 if (trail_ws == 0) {
 901                                         *(pd++) = c;
 902                                         ocnt--;
 903                                         line_ccnt--;
 904                                         CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
 905                                 }
 906                         }
 907                 } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) {
 908                         if (line_ccnt < 2 && inst->lbchars != NULL) {
 909                                 if (ocnt < inst->lbchars_len + 1) {
 910                                         err = PHP_CONV_ERR_TOO_BIG;
 911                                         break;
 912                                 }
 913                                 *(pd++) = '=';
 914                                 ocnt--;
 915                                 line_ccnt--;
 916 
 917                                 memcpy(pd, inst->lbchars, inst->lbchars_len);
 918                                 pd += inst->lbchars_len;
 919                                 ocnt -= inst->lbchars_len;
 920                                 line_ccnt = inst->line_len;
 921                         }
 922                         if (ocnt < 1) {
 923                                 err = PHP_CONV_ERR_TOO_BIG;
 924                                 break;
 925                         }
 926                         *(pd++) = c;
 927                         ocnt--;
 928                         line_ccnt--;
 929                         CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
 930                 } else {
 931                         if (line_ccnt < 4) {
 932                                 if (ocnt < inst->lbchars_len + 1) {
 933                                         err = PHP_CONV_ERR_TOO_BIG;
 934                                         break;
 935                                 }
 936                                 *(pd++) = '=';
 937                                 ocnt--;
 938                                 line_ccnt--;
 939 
 940                                 memcpy(pd, inst->lbchars, inst->lbchars_len);
 941                                 pd += inst->lbchars_len;
 942                                 ocnt -= inst->lbchars_len;
 943                                 line_ccnt = inst->line_len;
 944                         }
 945                         if (ocnt < 3) {
 946                                 err = PHP_CONV_ERR_TOO_BIG;
 947                                 break;
 948                         }
 949                         *(pd++) = '=';
 950                         *(pd++) = qp_digits[(c >> 4)];
 951                         *(pd++) = qp_digits[(c & 0x0f)];
 952                         ocnt -= 3;
 953                         line_ccnt -= 3;
 954                         if (trail_ws > 0) {
 955                                 trail_ws--;
 956                         }
 957                         CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt);
 958                 }
 959         }
 960 
 961         *in_pp = (const char *)ps;
 962         *in_left_p = icnt;
 963         *out_pp = (char *)pd;
 964         *out_left_p = ocnt;
 965         inst->line_ccnt = line_ccnt;
 966         inst->lb_ptr = lb_ptr;
 967         inst->lb_cnt = lb_cnt;
 968         return err;
 969 }
 970 #undef NEXT_CHAR
 971 #undef CONSUME_CHAR
 972 
 973 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)
 974 {
 975         if (line_len < 4 && lbchars != NULL) {
 976                 return PHP_CONV_ERR_TOO_BIG;
 977         }
 978         inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_encode_convert;
 979         inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_encode_dtor;
 980         inst->line_ccnt = line_len;
 981         inst->line_len = line_len;
 982         if (lbchars != NULL) {
 983                 inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
 984                 inst->lbchars_len = lbchars_len;
 985         } else {
 986                 inst->lbchars = NULL;
 987         }
 988         inst->lbchars_dup = lbchars_dup;
 989         inst->persistent = persistent;
 990         inst->opts = opts;
 991         inst->lb_cnt = inst->lb_ptr = 0;
 992         return PHP_CONV_ERR_SUCCESS;
 993 }
 994 /* }}} */
 995 
 996 /* {{{ php_conv_qprint_decode */
 997 typedef struct _php_conv_qprint_decode {
 998         php_conv _super;
 999 
1000         int scan_stat;
1001         unsigned int next_char;
1002         const char *lbchars;
1003         int lbchars_dup;
1004         size_t lbchars_len;
1005         int persistent;
1006         unsigned int lb_ptr;
1007         unsigned int lb_cnt;    
1008 } php_conv_qprint_decode;
1009 
1010 static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst)
1011 {
1012         assert(inst != NULL);
1013         if (inst->lbchars_dup && inst->lbchars != NULL) {
1014                 pefree((void *)inst->lbchars, inst->persistent);
1015         }
1016 }
1017 
1018 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)
1019 {
1020         php_conv_err_t err = PHP_CONV_ERR_SUCCESS;
1021         size_t icnt, ocnt;
1022         unsigned char *ps, *pd;
1023         unsigned int scan_stat;
1024         unsigned int next_char;
1025         unsigned int lb_ptr, lb_cnt;
1026 
1027         lb_ptr = inst->lb_ptr;
1028         lb_cnt = inst->lb_cnt;
1029 
1030         if ((in_pp == NULL || in_left_p == NULL) && lb_cnt == lb_ptr) {
1031                 if (inst->scan_stat != 0) {
1032                         return PHP_CONV_ERR_UNEXPECTED_EOS;
1033                 }
1034                 return PHP_CONV_ERR_SUCCESS;
1035         }
1036 
1037         ps = (unsigned char *)(*in_pp);
1038         icnt = *in_left_p;
1039         pd = (unsigned char *)(*out_pp);
1040         ocnt = *out_left_p;
1041         scan_stat = inst->scan_stat;
1042         next_char = inst->next_char;
1043 
1044         for (;;) {
1045                 switch (scan_stat) {
1046                         case 0: {
1047                                 if (icnt <= 0) {
1048                                         goto out;
1049                                 }
1050                                 if (*ps == '=') {
1051                                         scan_stat = 1;
1052                                 } else {
1053                                         if (ocnt < 1) {
1054                                                 err = PHP_CONV_ERR_TOO_BIG;
1055                                                 goto out;
1056                                         }
1057                                         *(pd++) = *ps;
1058                                         ocnt--;
1059                                 }
1060                                 ps++, icnt--;
1061                         } break;
1062 
1063                         case 1: {
1064                                 if (icnt <= 0) {
1065                                         goto out;
1066                                 }
1067                                 if (*ps == ' ' || *ps == '\t') {
1068                                         scan_stat = 4;
1069                                         ps++, icnt--;
1070                                         break;
1071                                 } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\r') {
1072                                         /* auto-detect line endings, looks like network line ending \r\n (could be mac \r) */
1073                                         lb_cnt++;
1074                                         scan_stat = 5;
1075                                         ps++, icnt--;
1076                                         break;
1077                                 } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\n') {
1078                                         /* auto-detect line endings, looks like unix-lineendings, not to spec, but it is seem in the wild, a lot */
1079                                         lb_cnt = lb_ptr = 0;
1080                                         scan_stat = 0;
1081                                         ps++, icnt--;
1082                                         break;
1083                                 } else if (lb_cnt < inst->lbchars_len &&
1084                                                         *ps == (unsigned char)inst->lbchars[lb_cnt]) {
1085                                         lb_cnt++;
1086                                         scan_stat = 5;
1087                                         ps++, icnt--;
1088                                         break;
1089                                 }
1090                         } /* break is missing intentionally */
1091 
1092                         case 2: {       
1093                                 if (icnt <= 0) {
1094                                         goto out;
1095                                 }
1096 
1097                                 if (!isxdigit((int) *ps)) {
1098                                         err = PHP_CONV_ERR_INVALID_SEQ;
1099                                         goto out;
1100                                 }
1101                                 next_char = (next_char << 4) | (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30);
1102                                 scan_stat++;
1103                                 ps++, icnt--;
1104                                 if (scan_stat != 3) {
1105                                         break;
1106                                 }
1107                         } /* break is missing intentionally */
1108 
1109                         case 3: {
1110                                 if (ocnt < 1) {
1111                                         err = PHP_CONV_ERR_TOO_BIG;
1112                                         goto out;
1113                                 }
1114                                 *(pd++) = next_char;
1115                                 ocnt--;
1116                                 scan_stat = 0;
1117                         } break;
1118 
1119                         case 4: {
1120                                 if (icnt <= 0) {
1121                                         goto out;
1122                                 }
1123                                 if (lb_cnt < inst->lbchars_len &&
1124                                         *ps == (unsigned char)inst->lbchars[lb_cnt]) {
1125                                         lb_cnt++;
1126                                         scan_stat = 5;
1127                                 }
1128                                 if (*ps != '\t' && *ps != ' ') {
1129                                         err = PHP_CONV_ERR_INVALID_SEQ;
1130                                         goto out;
1131                                 }
1132                                 ps++, icnt--;
1133                         } break;
1134 
1135                         case 5: {
1136                                 if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') {
1137                                         /* auto-detect soft line breaks, found network line break */
1138                                         lb_cnt = lb_ptr = 0;
1139                                         scan_stat = 0;
1140                                         ps++, icnt--; /* consume \n */
1141                                 } else if (!inst->lbchars && lb_cnt > 0) {
1142                                         /* auto-detect soft line breaks, found mac line break */
1143                                         lb_cnt = lb_ptr = 0;
1144                                         scan_stat = 0;
1145                                 } else if (lb_cnt >= inst->lbchars_len) {
1146                                         /* soft line break */
1147                                         lb_cnt = lb_ptr = 0;
1148                                         scan_stat = 0;
1149                                 } else if (icnt > 0) {
1150                                         if (*ps == (unsigned char)inst->lbchars[lb_cnt]) {
1151                                                 lb_cnt++;
1152                                                 ps++, icnt--;
1153                                         } else {
1154                                                 scan_stat = 6; /* no break for short-cut */
1155                                         }
1156                                 } else {
1157                                         goto out;
1158                                 }
1159                         } break;
1160 
1161                         case 6: {
1162                                 if (lb_ptr < lb_cnt) {
1163                                         if (ocnt < 1) {
1164                                                 err = PHP_CONV_ERR_TOO_BIG;
1165                                                 goto out;
1166                                         }
1167                                         *(pd++) = inst->lbchars[lb_ptr++];
1168                                         ocnt--;
1169                                 } else {
1170                                         scan_stat = 0;
1171                                         lb_cnt = lb_ptr = 0;
1172                                 }
1173                         } break;
1174                 }
1175         }
1176 out:
1177         *in_pp = (const char *)ps;
1178         *in_left_p = icnt;
1179         *out_pp = (char *)pd;
1180         *out_left_p = ocnt;
1181         inst->scan_stat = scan_stat;
1182         inst->lb_ptr = lb_ptr;
1183         inst->lb_cnt = lb_cnt;
1184         inst->next_char = next_char;
1185 
1186         return err;
1187 }
1188 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)
1189 {
1190         inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_decode_convert;
1191         inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_decode_dtor;
1192         inst->scan_stat = 0;
1193         inst->next_char = 0;
1194         inst->lb_ptr = inst->lb_cnt = 0;
1195         if (lbchars != NULL) {
1196                 inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars);
1197                 inst->lbchars_len = lbchars_len;
1198         } else {
1199                 inst->lbchars = NULL;
1200                 inst->lbchars_len = 0;
1201         }
1202         inst->lbchars_dup = lbchars_dup;
1203         inst->persistent = persistent;
1204         return PHP_CONV_ERR_SUCCESS;
1205 }
1206 /* }}} */
1207 
1208 typedef struct _php_convert_filter {
1209         php_conv *cd;
1210         int persistent;
1211         char *filtername;
1212         char stub[128];
1213         size_t stub_len;
1214 } php_convert_filter;
1215 
1216 #define PHP_CONV_BASE64_ENCODE 1
1217 #define PHP_CONV_BASE64_DECODE 2
1218 #define PHP_CONV_QPRINT_ENCODE 3 
1219 #define PHP_CONV_QPRINT_DECODE 4
1220 
1221 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)
1222 {
1223         zval **tmpval;
1224 
1225         *pretval = NULL;
1226         *pretval_len = 0;
1227  
1228         if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
1229                 if (Z_TYPE_PP(tmpval) != IS_STRING) {
1230                         zval zt = **tmpval;
1231 
1232                         convert_to_string(&zt);
1233 
1234                         if (NULL == (*pretval = pemalloc(Z_STRLEN(zt) + 1, persistent))) {
1235                                 return PHP_CONV_ERR_ALLOC;
1236                         }
1237 
1238                         *pretval_len = Z_STRLEN(zt);
1239                         memcpy(*pretval, Z_STRVAL(zt), Z_STRLEN(zt) + 1);
1240                         zval_dtor(&zt);
1241                 } else {
1242                         if (NULL == (*pretval = pemalloc(Z_STRLEN_PP(tmpval) + 1, persistent))) {
1243                                 return PHP_CONV_ERR_ALLOC;
1244                         }
1245                         *pretval_len = Z_STRLEN_PP(tmpval);
1246                         memcpy(*pretval, Z_STRVAL_PP(tmpval), Z_STRLEN_PP(tmpval) + 1);
1247                 }
1248         } else {
1249                 return PHP_CONV_ERR_NOT_FOUND;
1250         }
1251         return PHP_CONV_ERR_SUCCESS;
1252 }
1253 
1254 #if IT_WAS_USED
1255 static php_conv_err_t php_conv_get_long_prop_ex(const HashTable *ht, long *pretval, char *field_name, size_t field_name_len)
1256 {
1257         zval **tmpval;
1258 
1259         *pretval = 0;
1260 
1261         if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
1262                 zval tmp, *ztval = *tmpval;
1263 
1264                 if (Z_TYPE_PP(tmpval) != IS_LONG) {
1265                         tmp = *ztval;
1266                         zval_copy_ctor(&tmp);
1267                         convert_to_long(&tmp);
1268                         ztval = &tmp;
1269                 }
1270                 *pretval = Z_LVAL_P(ztval);
1271         } else {
1272                 return PHP_CONV_ERR_NOT_FOUND;
1273         } 
1274         return PHP_CONV_ERR_SUCCESS;
1275 }
1276 #endif
1277 
1278 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)
1279 {
1280         zval **tmpval;
1281 
1282         *pretval = 0;
1283 
1284         if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
1285                 zval tmp, *ztval = *tmpval;
1286 
1287                 if (Z_TYPE_PP(tmpval) != IS_LONG) {
1288                         tmp = *ztval;
1289                         zval_copy_ctor(&tmp);
1290                         convert_to_long(&tmp);
1291                         ztval = &tmp;
1292                 }
1293                 if (Z_LVAL_P(ztval) < 0) {
1294                         *pretval = 0;
1295                 } else {
1296                         *pretval = Z_LVAL_P(ztval);
1297                 }
1298         } else {
1299                 return PHP_CONV_ERR_NOT_FOUND;
1300         } 
1301         return PHP_CONV_ERR_SUCCESS;
1302 }
1303 
1304 static php_conv_err_t php_conv_get_bool_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len)
1305 {
1306         zval **tmpval;
1307 
1308         *pretval = 0;
1309 
1310         if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) {
1311                 zval tmp, *ztval = *tmpval;
1312 
1313                 if (Z_TYPE_PP(tmpval) != IS_BOOL) {
1314                         tmp = *ztval;
1315                         zval_copy_ctor(&tmp);
1316                         convert_to_boolean(&tmp);
1317                         ztval = &tmp;
1318                 }
1319                 *pretval = Z_BVAL_P(ztval);
1320         } else {
1321                 return PHP_CONV_ERR_NOT_FOUND;
1322         } 
1323         return PHP_CONV_ERR_SUCCESS;
1324 }
1325 
1326 
1327 #if IT_WAS_USED
1328 static int php_conv_get_int_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len)
1329 {
1330         long l;
1331         php_conv_err_t err;
1332 
1333         *pretval = 0;
1334 
1335         if ((err = php_conv_get_long_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) {
1336                 *pretval = l;
1337         }
1338         return err;
1339 }
1340 #endif
1341 
1342 static int php_conv_get_uint_prop_ex(const HashTable *ht, unsigned int *pretval, char *field_name, size_t field_name_len)
1343 {
1344         long l;
1345         php_conv_err_t err;
1346 
1347         *pretval = 0;
1348 
1349         if ((err = php_conv_get_ulong_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) {
1350                 *pretval = l;
1351         }
1352         return err;
1353 }
1354 
1355 #define GET_STR_PROP(ht, var, var_len, fldname, persistent) \
1356         php_conv_get_string_prop_ex(ht, &var, &var_len, fldname, sizeof(fldname), persistent) 
1357 
1358 #define GET_INT_PROP(ht, var, fldname) \
1359         php_conv_get_int_prop_ex(ht, &var, fldname, sizeof(fldname))
1360 
1361 #define GET_UINT_PROP(ht, var, fldname) \
1362         php_conv_get_uint_prop_ex(ht, &var, fldname, sizeof(fldname))
1363 
1364 #define GET_BOOL_PROP(ht, var, fldname) \
1365         php_conv_get_bool_prop_ex(ht, &var, fldname, sizeof(fldname))
1366 
1367 static php_conv *php_conv_open(int conv_mode, const HashTable *options, int persistent)
1368 {
1369         /* FIXME: I'll have to replace this ugly code by something neat
1370            (factories?) in the near future. */ 
1371         php_conv *retval = NULL;
1372 
1373         switch (conv_mode) {
1374                 case PHP_CONV_BASE64_ENCODE: {
1375                         unsigned int line_len = 0;
1376                         char *lbchars = NULL;
1377                         size_t lbchars_len;
1378 
1379                         if (options != NULL) {
1380                                 GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
1381                                 GET_UINT_PROP(options, line_len, "line-length");
1382                                 if (line_len < 4) {
1383                                         if (lbchars != NULL) {
1384                                                 pefree(lbchars, 0);
1385                                         }
1386                                         lbchars = NULL;
1387                                 } else {
1388                                         if (lbchars == NULL) {
1389                                                 lbchars = pestrdup("\r\n", 0);
1390                                                 lbchars_len = 2;
1391                                         }
1392                                 }
1393                         }
1394                         retval = pemalloc(sizeof(php_conv_base64_encode), persistent);
1395                         if (lbchars != NULL) {
1396                                 if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, line_len, lbchars, lbchars_len, 1, persistent)) {
1397                                         if (lbchars != NULL) {
1398                                                 pefree(lbchars, 0);
1399                                         }
1400                                         goto out_failure;
1401                                 }
1402                                 pefree(lbchars, 0);
1403                         } else {
1404                                 if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, 0, NULL, 0, 0, persistent)) {
1405                                         goto out_failure;
1406                                 }
1407                         }
1408                 } break;
1409 
1410                 case PHP_CONV_BASE64_DECODE:
1411                         retval = pemalloc(sizeof(php_conv_base64_decode), persistent);
1412                         if (php_conv_base64_decode_ctor((php_conv_base64_decode *)retval)) {
1413                                 goto out_failure;
1414                         }
1415                         break;
1416 
1417                 case PHP_CONV_QPRINT_ENCODE: {
1418                         unsigned int line_len = 0;
1419                         char *lbchars = NULL;
1420                         size_t lbchars_len;
1421                         int opts = 0;
1422 
1423                         if (options != NULL) {
1424                                 int opt_binary = 0;
1425                                 int opt_force_encode_first = 0;
1426 
1427                                 GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
1428                                 GET_UINT_PROP(options, line_len, "line-length");
1429                                 GET_BOOL_PROP(options, opt_binary, "binary"); 
1430                                 GET_BOOL_PROP(options, opt_force_encode_first, "force-encode-first"); 
1431 
1432                                 if (line_len < 4) {
1433                                         if (lbchars != NULL) {
1434                                                 pefree(lbchars, 0);
1435                                         }
1436                                         lbchars = NULL;
1437                                 } else {
1438                                         if (lbchars == NULL) {
1439                                                 lbchars = pestrdup("\r\n", 0);
1440                                                 lbchars_len = 2;
1441                                         }
1442                                 }
1443                                 opts |= (opt_binary ? PHP_CONV_QPRINT_OPT_BINARY : 0);
1444                                 opts |= (opt_force_encode_first ? PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST : 0);
1445                         }
1446                         retval = pemalloc(sizeof(php_conv_qprint_encode), persistent);
1447                         if (lbchars != NULL) {
1448                                 if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, line_len, lbchars, lbchars_len, 1, opts, persistent)) {
1449                                         pefree(lbchars, 0);
1450                                         goto out_failure;
1451                                 }
1452                                 pefree(lbchars, 0);
1453                         } else {
1454                                 if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, 0, NULL, 0, 0, opts, persistent)) {
1455                                         goto out_failure;
1456                                 }
1457                         }
1458                 } break;
1459         
1460                 case PHP_CONV_QPRINT_DECODE: {
1461                         char *lbchars = NULL;
1462                         size_t lbchars_len;
1463 
1464                         if (options != NULL) {
1465                                 /* If line-break-chars are not specified, filter will attempt to detect line endings (\r, \n, or \r\n) */
1466                                 GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0);
1467                         }
1468 
1469                         retval = pemalloc(sizeof(php_conv_qprint_decode), persistent);
1470                         if (lbchars != NULL) {
1471                                 if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, lbchars, lbchars_len, 1, persistent)) {
1472                                         pefree(lbchars, 0);
1473                                         goto out_failure;
1474                                 }
1475                                 pefree(lbchars, 0);
1476                         } else {
1477                                 if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, NULL, 0, 0, persistent)) {
1478                                         goto out_failure;
1479                                 }
1480                         }
1481                 } break;
1482 
1483                 default:
1484                         retval = NULL;
1485                         break;
1486         }
1487         return retval;
1488 
1489 out_failure:
1490         if (retval != NULL) {
1491                 pefree(retval, persistent);
1492         }
1493         return NULL;    
1494 }
1495 
1496 #undef GET_STR_PROP
1497 #undef GET_INT_PROP
1498 #undef GET_UINT_PROP
1499 #undef GET_BOOL_PROP
1500 
1501 static int php_convert_filter_ctor(php_convert_filter *inst,
1502         int conv_mode, HashTable *conv_opts,
1503         const char *filtername, int persistent)
1504 {
1505         inst->persistent = persistent;
1506         inst->filtername = pestrdup(filtername, persistent);
1507         inst->stub_len = 0;
1508 
1509         if ((inst->cd = php_conv_open(conv_mode, conv_opts, persistent)) == NULL) {
1510                 goto out_failure;
1511         }
1512 
1513         return SUCCESS;
1514 
1515 out_failure:
1516         if (inst->cd != NULL) {
1517                 php_conv_dtor(inst->cd);
1518                 pefree(inst->cd, persistent);
1519         }
1520         if (inst->filtername != NULL) {
1521                 pefree(inst->filtername, persistent);
1522         }
1523         return FAILURE;
1524 }
1525 
1526 static void php_convert_filter_dtor(php_convert_filter *inst)
1527 {
1528         if (inst->cd != NULL) {
1529                 php_conv_dtor(inst->cd);
1530                 pefree(inst->cd, inst->persistent);
1531         }
1532 
1533         if (inst->filtername != NULL) {
1534                 pefree(inst->filtername, inst->persistent);
1535         }
1536 }
1537 
1538 /* {{{ strfilter_convert_append_bucket */
1539 static int strfilter_convert_append_bucket(
1540                 php_convert_filter *inst,
1541                 php_stream *stream, php_stream_filter *filter,
1542                 php_stream_bucket_brigade *buckets_out,
1543                 const char *ps, size_t buf_len, size_t *consumed,
1544                 int persistent TSRMLS_DC)
1545 {
1546         php_conv_err_t err;
1547         php_stream_bucket *new_bucket;
1548         char *out_buf = NULL;
1549         size_t out_buf_size;
1550         char *pd;
1551         const char *pt;
1552         size_t ocnt, icnt, tcnt;
1553         size_t initial_out_buf_size;
1554         
1555         if (ps == NULL) {
1556                 initial_out_buf_size = 64;
1557                 icnt = 1;
1558         } else {
1559                 initial_out_buf_size = buf_len;
1560                 icnt = buf_len;
1561         }
1562 
1563         out_buf_size = ocnt = initial_out_buf_size; 
1564         if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
1565                 return FAILURE;
1566         }
1567 
1568         pd = out_buf;
1569 
1570         if (inst->stub_len > 0) {
1571                 pt = inst->stub;
1572                 tcnt = inst->stub_len;
1573 
1574                 while (tcnt > 0) {
1575                         err = php_conv_convert(inst->cd, &pt, &tcnt, &pd, &ocnt);
1576 
1577                         switch (err) {
1578                                 case PHP_CONV_ERR_INVALID_SEQ:
1579                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername);
1580                                         goto out_failure;
1581 
1582                                 case PHP_CONV_ERR_MORE:
1583                                         if (ps != NULL) {
1584                                                 if (icnt > 0) {
1585                                                         if (inst->stub_len >= sizeof(inst->stub)) {
1586                                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername);
1587                                                                 goto out_failure;
1588                                                         }
1589                                                         inst->stub[inst->stub_len++] = *(ps++);
1590                                                         icnt--;
1591                                                         pt = inst->stub;
1592                                                         tcnt = inst->stub_len;
1593                                                 } else {
1594                                                         tcnt = 0;
1595                                                         break;
1596                                                 }
1597                                         }
1598                                         break;
1599 
1600                                 case PHP_CONV_ERR_UNEXPECTED_EOS:
1601                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected end of stream", inst->filtername);
1602                                         goto out_failure;
1603 
1604                                 case PHP_CONV_ERR_TOO_BIG: {
1605                                         char *new_out_buf;
1606                                         size_t new_out_buf_size;
1607 
1608                                         new_out_buf_size = out_buf_size << 1;
1609 
1610                                         if (new_out_buf_size < out_buf_size) {
1611                                                 /* whoa! no bigger buckets are sold anywhere... */
1612                                                 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
1613                                                         goto out_failure;
1614                                                 }
1615 
1616                                                 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
1617 
1618                                                 out_buf_size = ocnt = initial_out_buf_size;
1619                                                 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
1620                                                         return FAILURE;
1621                                                 }
1622                                                 pd = out_buf;
1623                                         } else {
1624                                                 if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
1625                                                         if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
1626                                                                 goto out_failure;
1627                                                         }
1628 
1629                                                         php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
1630                                                         return FAILURE;
1631                                                 }
1632 
1633                                                 pd = new_out_buf + (pd - out_buf);
1634                                                 ocnt += (new_out_buf_size - out_buf_size);
1635                                                 out_buf = new_out_buf;
1636                                                 out_buf_size = new_out_buf_size;
1637                                         }
1638                                 } break;
1639 
1640                                 case PHP_CONV_ERR_UNKNOWN:
1641                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername);
1642                                         goto out_failure;
1643 
1644                                 default:
1645                                         break;
1646                         }
1647                 }
1648                 memmove(inst->stub, pt, tcnt);
1649                 inst->stub_len = tcnt;
1650         }
1651 
1652         while (icnt > 0) {
1653                 err = ((ps == NULL ? php_conv_convert(inst->cd, NULL, NULL, &pd, &ocnt):
1654                                 php_conv_convert(inst->cd, &ps, &icnt, &pd, &ocnt)));
1655                 switch (err) {
1656                         case PHP_CONV_ERR_INVALID_SEQ:
1657                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername);
1658                                 goto out_failure;
1659 
1660                         case PHP_CONV_ERR_MORE:
1661                                 if (ps != NULL) {
1662                                         if (icnt > sizeof(inst->stub)) {
1663                                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername);
1664                                                 goto out_failure;
1665                                         }
1666                                         memcpy(inst->stub, ps, icnt);
1667                                         inst->stub_len = icnt;
1668                                         ps += icnt;
1669                                         icnt = 0;
1670                                 } else {
1671                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected octet values", inst->filtername);
1672                                         goto out_failure;
1673                                 }
1674                                 break;
1675 
1676                         case PHP_CONV_ERR_TOO_BIG: {
1677                                 char *new_out_buf;
1678                                 size_t new_out_buf_size;
1679 
1680                                 new_out_buf_size = out_buf_size << 1;
1681 
1682                                 if (new_out_buf_size < out_buf_size) {
1683                                         /* whoa! no bigger buckets are sold anywhere... */
1684                                         if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
1685                                                 goto out_failure;
1686                                         }
1687 
1688                                         php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
1689 
1690                                         out_buf_size = ocnt = initial_out_buf_size;
1691                                         if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
1692                                                 return FAILURE;
1693                                         }
1694                                         pd = out_buf;
1695                                 } else {
1696                                         if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
1697                                                 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
1698                                                         goto out_failure;
1699                                                 }
1700 
1701                                                 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
1702                                                 return FAILURE;
1703                                         }
1704                                         pd = new_out_buf + (pd - out_buf);
1705                                         ocnt += (new_out_buf_size - out_buf_size);
1706                                         out_buf = new_out_buf;
1707                                         out_buf_size = new_out_buf_size;
1708                                 }
1709                         } break;
1710 
1711                         case PHP_CONV_ERR_UNKNOWN:
1712                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername);
1713                                 goto out_failure;
1714 
1715                         default:
1716                                 if (ps == NULL) {
1717                                         icnt = 0;
1718                                 }
1719                                 break;
1720                 }
1721         }
1722 
1723         if (out_buf_size - ocnt > 0) {
1724                 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
1725                         goto out_failure;
1726                 }
1727                 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
1728         } else {
1729                 pefree(out_buf, persistent);
1730         }
1731         *consumed += buf_len - icnt;
1732 
1733         return SUCCESS;
1734 
1735 out_failure:
1736         pefree(out_buf, persistent);
1737         return FAILURE;
1738 }
1739 /* }}} */
1740 
1741 static php_stream_filter_status_t strfilter_convert_filter(
1742         php_stream *stream,
1743         php_stream_filter *thisfilter,
1744         php_stream_bucket_brigade *buckets_in,
1745         php_stream_bucket_brigade *buckets_out,
1746         size_t *bytes_consumed,
1747         int flags
1748         TSRMLS_DC)
1749 {
1750         php_stream_bucket *bucket = NULL;
1751         size_t consumed = 0;
1752         php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract;
1753 
1754         while (buckets_in->head != NULL) {
1755                 bucket = buckets_in->head;
1756 
1757                 php_stream_bucket_unlink(bucket TSRMLS_CC);
1758 
1759                 if (strfilter_convert_append_bucket(inst, stream, thisfilter,
1760                                 buckets_out, bucket->buf, bucket->buflen, &consumed,
1761                                 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
1762                         goto out_failure;
1763                 }
1764 
1765                 php_stream_bucket_delref(bucket TSRMLS_CC);
1766         }
1767 
1768         if (flags != PSFS_FLAG_NORMAL) {
1769                 if (strfilter_convert_append_bucket(inst, stream, thisfilter,
1770                                 buckets_out, NULL, 0, &consumed,
1771                                 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) {
1772                         goto out_failure;
1773                 }
1774         }
1775 
1776         if (bytes_consumed) {
1777                 *bytes_consumed = consumed;
1778         }
1779 
1780         return PSFS_PASS_ON;
1781 
1782 out_failure:
1783         if (bucket != NULL) {
1784                 php_stream_bucket_delref(bucket TSRMLS_CC);
1785         }
1786         return PSFS_ERR_FATAL;
1787 }
1788 
1789 static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC)
1790 {
1791         assert(thisfilter->abstract != NULL);
1792 
1793         php_convert_filter_dtor((php_convert_filter *)thisfilter->abstract);
1794         pefree(thisfilter->abstract, ((php_convert_filter *)thisfilter->abstract)->persistent);
1795 }
1796 
1797 static php_stream_filter_ops strfilter_convert_ops = {
1798         strfilter_convert_filter,
1799         strfilter_convert_dtor,
1800         "convert.*"
1801 };
1802 
1803 static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
1804 {
1805         php_convert_filter *inst;
1806         php_stream_filter *retval = NULL;
1807 
1808         char *dot;
1809         int conv_mode = 0;
1810 
1811         if (filterparams != NULL && Z_TYPE_P(filterparams) != IS_ARRAY) {
1812                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid filter parameter", filtername);
1813                 return NULL;
1814         }
1815 
1816         if ((dot = strchr(filtername, '.')) == NULL) {
1817                 return NULL;
1818         }
1819         ++dot;
1820 
1821         inst = pemalloc(sizeof(php_convert_filter), persistent);
1822 
1823         if (strcasecmp(dot, "base64-encode") == 0) {
1824                 conv_mode = PHP_CONV_BASE64_ENCODE;
1825         } else if (strcasecmp(dot, "base64-decode") == 0) {
1826                 conv_mode = PHP_CONV_BASE64_DECODE;
1827         } else if (strcasecmp(dot, "quoted-printable-encode") == 0) {
1828                 conv_mode = PHP_CONV_QPRINT_ENCODE;
1829         } else if (strcasecmp(dot, "quoted-printable-decode") == 0) {
1830                 conv_mode = PHP_CONV_QPRINT_DECODE;
1831         }
1832         
1833         if (php_convert_filter_ctor(inst, conv_mode,
1834                 (filterparams != NULL ? Z_ARRVAL_P(filterparams) : NULL),
1835                 filtername, persistent) != SUCCESS) {
1836                 goto out;
1837         }       
1838 
1839         retval = php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent);
1840 out:
1841         if (retval == NULL) {
1842                 pefree(inst, persistent);
1843         }
1844 
1845         return retval;
1846 }
1847 
1848 static php_stream_filter_factory strfilter_convert_factory = {
1849         strfilter_convert_create
1850 };
1851 /* }}} */
1852 
1853 /* {{{ consumed filter implementation */
1854 typedef struct _php_consumed_filter_data {
1855         int persistent;
1856         size_t consumed;
1857         off_t offset;
1858 } php_consumed_filter_data;
1859 
1860 static php_stream_filter_status_t consumed_filter_filter(
1861         php_stream *stream,
1862         php_stream_filter *thisfilter,
1863         php_stream_bucket_brigade *buckets_in,
1864         php_stream_bucket_brigade *buckets_out,
1865         size_t *bytes_consumed,
1866         int flags
1867         TSRMLS_DC)
1868 {
1869         php_consumed_filter_data *data = (php_consumed_filter_data *)(thisfilter->abstract);
1870         php_stream_bucket *bucket;
1871         size_t consumed = 0;
1872 
1873         if (data->offset == ~0) {
1874                 data->offset = php_stream_tell(stream);
1875         }
1876         while ((bucket = buckets_in->head) != NULL) {
1877                 php_stream_bucket_unlink(bucket TSRMLS_CC);
1878                 consumed += bucket->buflen;
1879                 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
1880         }
1881         if (bytes_consumed) {
1882                 *bytes_consumed = consumed;
1883         }
1884         if (flags & PSFS_FLAG_FLUSH_CLOSE) {
1885                 php_stream_seek(stream, data->offset + data->consumed, SEEK_SET);
1886         }
1887         data->consumed += consumed;
1888         
1889         return PSFS_PASS_ON;
1890 }
1891 
1892 static void consumed_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC)
1893 {
1894         if (thisfilter && thisfilter->abstract) {
1895                 php_consumed_filter_data *data = (php_consumed_filter_data*)thisfilter->abstract;
1896                 pefree(data, data->persistent);
1897         }
1898 }
1899 
1900 static php_stream_filter_ops consumed_filter_ops = {
1901         consumed_filter_filter,
1902         consumed_filter_dtor,
1903         "consumed"
1904 };
1905 
1906 static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
1907 {
1908         php_stream_filter_ops *fops = NULL;
1909         php_consumed_filter_data *data;
1910 
1911         if (strcasecmp(filtername, "consumed")) {
1912                 return NULL;
1913         }
1914 
1915         /* Create this filter */
1916         data = pecalloc(1, sizeof(php_consumed_filter_data), persistent);
1917         if (!data) {
1918                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_consumed_filter_data));
1919                 return NULL;
1920         }
1921         data->persistent = persistent;
1922         data->consumed = 0;
1923         data->offset = ~0;
1924         fops = &consumed_filter_ops;
1925 
1926         return php_stream_filter_alloc(fops, data, persistent);
1927 }
1928 
1929 php_stream_filter_factory consumed_filter_factory = {
1930         consumed_filter_create
1931 };
1932 
1933 /* }}} */
1934 
1935 /* {{{ chunked filter implementation */
1936 typedef enum _php_chunked_filter_state {
1937         CHUNK_SIZE_START,
1938         CHUNK_SIZE,
1939         CHUNK_SIZE_EXT,
1940         CHUNK_SIZE_CR,
1941         CHUNK_SIZE_LF,
1942         CHUNK_BODY,
1943         CHUNK_BODY_CR,
1944         CHUNK_BODY_LF,
1945         CHUNK_TRAILER,
1946         CHUNK_ERROR
1947 } php_chunked_filter_state;
1948 
1949 typedef struct _php_chunked_filter_data {
1950         php_chunked_filter_state state;
1951         size_t chunk_size;
1952         int persistent;
1953 } php_chunked_filter_data;
1954 
1955 static int php_dechunk(char *buf, int len, php_chunked_filter_data *data)
1956 {
1957         char *p = buf;
1958         char *end = p + len;
1959         char *out = buf;
1960         int out_len = 0;
1961 
1962         while (p < end) {
1963                 switch (data->state) {
1964                         case CHUNK_SIZE_START:
1965                                 data->chunk_size = 0;
1966                         case CHUNK_SIZE:
1967                                 while (p < end) {
1968                                         if (*p >= '0' && *p <= '9') {
1969                                                 data->chunk_size = (data->chunk_size * 16) + (*p - '0');
1970                                         } else if (*p >= 'A' && *p <= 'F') {
1971                                                 data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10);
1972                                         } else if (*p >= 'a' && *p <= 'f') {
1973                                                 data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10);
1974                                         } else if (data->state == CHUNK_SIZE_START) {
1975                                                 data->state = CHUNK_ERROR;
1976                                                 break;
1977                                         } else {
1978                                                 data->state = CHUNK_SIZE_EXT;
1979                                                 break;
1980                                         }
1981                                         data->state = CHUNK_SIZE;
1982                                         p++;
1983                                 }
1984                                 if (data->state == CHUNK_ERROR) {
1985                                         continue;
1986                                 } else if (p == end) {
1987                                         return out_len;
1988                                 }
1989                         case CHUNK_SIZE_EXT:
1990                                 /* skip extension */
1991                                 while (p < end && *p != '\r' && *p != '\n') {
1992                                         p++;
1993                                 }
1994                                 if (p == end) {
1995                                         return out_len;
1996                                 }
1997                         case CHUNK_SIZE_CR:
1998                                 if (*p == '\r') {
1999                                         p++;
2000                                         if (p == end) {
2001                                                 data->state = CHUNK_SIZE_LF;
2002                                                 return out_len;
2003                                         }
2004                                 }
2005                         case CHUNK_SIZE_LF:
2006                                 if (*p == '\n') {
2007                                         p++;
2008                                         if (data->chunk_size == 0) {
2009                                                 /* last chunk */
2010                                                 data->state = CHUNK_TRAILER;
2011                                                 continue;
2012                                         } else if (p == end) {
2013                                                 data->state = CHUNK_BODY;
2014                                                 return out_len;
2015                                         }
2016                                 } else {
2017                                         data->state = CHUNK_ERROR;
2018                                         continue;
2019                                 }
2020                         case CHUNK_BODY:
2021                                 if ((size_t) (end - p) >= data->chunk_size) {
2022                                         if (p != out) {
2023                                                 memmove(out, p, data->chunk_size);
2024                                         }
2025                                         out += data->chunk_size;
2026                                         out_len += data->chunk_size;
2027                                         p += data->chunk_size;
2028                                         if (p == end) {
2029                                                 data->state = CHUNK_BODY_CR;
2030                                                 return out_len;
2031                                         }
2032                                 } else {
2033                                         if (p != out) {
2034                                                 memmove(out, p, end - p);
2035                                         }
2036                                         data->chunk_size -= end - p;
2037                                         data->state=CHUNK_BODY;
2038                                         out_len += end - p;
2039                                         return out_len;
2040                                 }
2041                         case CHUNK_BODY_CR:
2042                                 if (*p == '\r') {
2043                                         p++;
2044                                         if (p == end) {
2045                                                 data->state = CHUNK_BODY_LF;
2046                                                 return out_len;
2047                                         }
2048                                 }
2049                         case CHUNK_BODY_LF:
2050                                 if (*p == '\n') {
2051                                         p++;
2052                                         data->state = CHUNK_SIZE_START;
2053                                         continue;
2054                                 } else {
2055                                         data->state = CHUNK_ERROR;
2056                                         continue;
2057                                 }
2058                         case CHUNK_TRAILER:
2059                                 /* ignore trailer */
2060                                 p = end;
2061                                 continue;
2062                         case CHUNK_ERROR:
2063                                 if (p != out) {
2064                                         memmove(out, p, end - p);
2065                                 }
2066                                 out_len += end - p;
2067                                 return out_len; 
2068                 }
2069         }
2070         return out_len;
2071 }
2072 
2073 static php_stream_filter_status_t php_chunked_filter(
2074         php_stream *stream,
2075         php_stream_filter *thisfilter,
2076         php_stream_bucket_brigade *buckets_in,
2077         php_stream_bucket_brigade *buckets_out,
2078         size_t *bytes_consumed,
2079         int flags
2080         TSRMLS_DC)
2081 {
2082         php_stream_bucket *bucket;
2083         size_t consumed = 0;
2084         php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract;
2085 
2086         while (buckets_in->head) {
2087                 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
2088                 consumed += bucket->buflen;
2089                 bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, data);        
2090                 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
2091         }
2092 
2093         if (bytes_consumed) {
2094                 *bytes_consumed = consumed;
2095         }
2096         
2097         return PSFS_PASS_ON;
2098 }
2099 
2100 static void php_chunked_dtor(php_stream_filter *thisfilter TSRMLS_DC)
2101 {
2102         if (thisfilter && thisfilter->abstract) {
2103                 php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract;
2104                 pefree(data, data->persistent);
2105         }
2106 }
2107 
2108 static php_stream_filter_ops chunked_filter_ops = {
2109         php_chunked_filter,
2110         php_chunked_dtor,
2111         "dechunk"
2112 };
2113 
2114 static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC)
2115 {
2116         php_stream_filter_ops *fops = NULL;
2117         php_chunked_filter_data *data;
2118 
2119         if (strcasecmp(filtername, "dechunk")) {
2120                 return NULL;
2121         }
2122 
2123         /* Create this filter */
2124         data = (php_chunked_filter_data *)pecalloc(1, sizeof(php_chunked_filter_data), persistent);
2125         if (!data) {
2126                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_chunked_filter_data));
2127                 return NULL;
2128         }
2129         data->state = CHUNK_SIZE_START;
2130         data->chunk_size = 0;
2131         data->persistent = persistent;
2132         fops = &chunked_filter_ops;
2133 
2134         return php_stream_filter_alloc(fops, data, persistent);
2135 }
2136 
2137 static php_stream_filter_factory chunked_filter_factory = {
2138         chunked_filter_create
2139 };
2140 /* }}} */
2141 
2142 static const struct {
2143         php_stream_filter_ops *ops;
2144         php_stream_filter_factory *factory;
2145 } standard_filters[] = {
2146         { &strfilter_rot13_ops, &strfilter_rot13_factory },
2147         { &strfilter_toupper_ops, &strfilter_toupper_factory },
2148         { &strfilter_tolower_ops, &strfilter_tolower_factory },
2149         { &strfilter_strip_tags_ops, &strfilter_strip_tags_factory },
2150         { &strfilter_convert_ops, &strfilter_convert_factory },
2151         { &consumed_filter_ops, &consumed_filter_factory },
2152         { &chunked_filter_ops, &chunked_filter_factory },
2153         /* additional filters to go here */
2154         { NULL, NULL }
2155 };
2156 
2157 /* {{{ filter MINIT and MSHUTDOWN */
2158 PHP_MINIT_FUNCTION(standard_filters)
2159 {
2160         int i;
2161 
2162         for (i = 0; standard_filters[i].ops; i++) {
2163                 if (FAILURE == php_stream_filter_register_factory(
2164                                         standard_filters[i].ops->label,
2165                                         standard_filters[i].factory
2166                                         TSRMLS_CC)) {
2167                         return FAILURE;
2168                 }
2169         }
2170         return SUCCESS;
2171 }
2172 
2173 PHP_MSHUTDOWN_FUNCTION(standard_filters)
2174 {
2175         int i;
2176 
2177         for (i = 0; standard_filters[i].ops; i++) {
2178                 php_stream_filter_unregister_factory(standard_filters[i].ops->label TSRMLS_CC);
2179         }
2180         return SUCCESS;
2181 }
2182 /* }}} */
2183 
2184 /*
2185  * Local variables:
2186  * tab-width: 4
2187  * c-basic-offset: 4
2188  * End:
2189  * vim600: sw=4 ts=4 fdm=marker
2190  * vim<600: sw=4 ts=4
2191  */

/* [<][>][^][v][top][bottom][index][help] */