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_ulong_prop_ex
  27. php_conv_get_bool_prop_ex
  28. php_conv_get_uint_prop_ex
  29. php_conv_open
  30. php_convert_filter_ctor
  31. php_convert_filter_dtor
  32. strfilter_convert_append_bucket
  33. strfilter_convert_filter
  34. strfilter_convert_dtor
  35. strfilter_convert_create
  36. consumed_filter_filter
  37. consumed_filter_dtor
  38. consumed_filter_create
  39. php_dechunk
  40. php_chunked_filter
  41. php_chunked_dtor
  42. chunked_filter_create
  43. PHP_MINIT_FUNCTION
  44. PHP_MSHUTDOWN_FUNCTION

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

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