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