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 unsigned int trail_ws; 795 int opts; 796 static char qp_digits[] = "0123456789ABCDEF"; 797 798 line_ccnt = inst->line_ccnt; 799 opts = inst->opts; 800 lb_ptr = inst->lb_ptr; 801 lb_cnt = inst->lb_cnt; 802 803 if ((in_pp == NULL || in_left_p == NULL) && (lb_ptr >=lb_cnt)) { 804 return PHP_CONV_ERR_SUCCESS; 805 } 806 807 ps = (unsigned char *)(*in_pp); 808 icnt = *in_left_p; 809 pd = (unsigned char *)(*out_pp); 810 ocnt = *out_left_p; 811 trail_ws = 0; 812 813 for (;;) { 814 if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) { 815 /* look ahead for the line break chars to make a right decision 816 * how to consume incoming characters */ 817 818 if (icnt > 0 && *ps == inst->lbchars[lb_cnt]) { 819 lb_cnt++; 820 821 if (lb_cnt >= inst->lbchars_len) { 822 unsigned int i; 823 824 if (ocnt < lb_cnt) { 825 lb_cnt--; 826 err = PHP_CONV_ERR_TOO_BIG; 827 break; 828 } 829 830 for (i = 0; i < lb_cnt; i++) { 831 *(pd++) = inst->lbchars[i]; 832 ocnt--; 833 } 834 line_ccnt = inst->line_len; 835 lb_ptr = lb_cnt = 0; 836 } 837 ps++, icnt--; 838 continue; 839 } 840 } 841 842 if (lb_ptr >= lb_cnt && icnt <= 0) { 843 break; 844 } 845 846 c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars); 847 848 if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && 849 (trail_ws == 0) && 850 (c == '\t' || c == ' ')) { 851 if (line_ccnt < 2 && inst->lbchars != NULL) { 852 if (ocnt < inst->lbchars_len + 1) { 853 err = PHP_CONV_ERR_TOO_BIG; 854 break; 855 } 856 857 *(pd++) = '='; 858 ocnt--; 859 line_ccnt--; 860 861 memcpy(pd, inst->lbchars, inst->lbchars_len); 862 pd += inst->lbchars_len; 863 ocnt -= inst->lbchars_len; 864 line_ccnt = inst->line_len; 865 } else { 866 if (ocnt < 1) { 867 err = PHP_CONV_ERR_TOO_BIG; 868 break; 869 } 870 871 /* Check to see if this is EOL whitespace. */ 872 if (inst->lbchars != NULL) { 873 unsigned char *ps2; 874 unsigned int j, lb_cnt2; 875 876 lb_cnt2 = 0; 877 ps2 = ps; 878 trail_ws = 1; 879 880 for (j = icnt - 1; j > 0; j--, ps2++) { 881 if (*ps2 == inst->lbchars[lb_cnt2]) { 882 lb_cnt2++; 883 if (lb_cnt2 >= inst->lbchars_len) { 884 /* Found trailing ws. Reset to top of main 885 * for loop to allow for code to do necessary 886 * wrapping/encoding. */ 887 break; 888 } 889 } else if (lb_cnt2 != 0 || (*ps2 != '\t' && *ps2 != ' ')) { 890 /* At least one non-EOL character following, so 891 * don't need to encode ws. */ 892 trail_ws = 0; 893 break; 894 } else { 895 trail_ws++; 896 } 897 } 898 } 899 900 if (trail_ws == 0) { 901 *(pd++) = c; 902 ocnt--; 903 line_ccnt--; 904 CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); 905 } 906 } 907 } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { 908 if (line_ccnt < 2 && inst->lbchars != NULL) { 909 if (ocnt < inst->lbchars_len + 1) { 910 err = PHP_CONV_ERR_TOO_BIG; 911 break; 912 } 913 *(pd++) = '='; 914 ocnt--; 915 line_ccnt--; 916 917 memcpy(pd, inst->lbchars, inst->lbchars_len); 918 pd += inst->lbchars_len; 919 ocnt -= inst->lbchars_len; 920 line_ccnt = inst->line_len; 921 } 922 if (ocnt < 1) { 923 err = PHP_CONV_ERR_TOO_BIG; 924 break; 925 } 926 *(pd++) = c; 927 ocnt--; 928 line_ccnt--; 929 CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); 930 } else { 931 if (line_ccnt < 4) { 932 if (ocnt < inst->lbchars_len + 1) { 933 err = PHP_CONV_ERR_TOO_BIG; 934 break; 935 } 936 *(pd++) = '='; 937 ocnt--; 938 line_ccnt--; 939 940 memcpy(pd, inst->lbchars, inst->lbchars_len); 941 pd += inst->lbchars_len; 942 ocnt -= inst->lbchars_len; 943 line_ccnt = inst->line_len; 944 } 945 if (ocnt < 3) { 946 err = PHP_CONV_ERR_TOO_BIG; 947 break; 948 } 949 *(pd++) = '='; 950 *(pd++) = qp_digits[(c >> 4)]; 951 *(pd++) = qp_digits[(c & 0x0f)]; 952 ocnt -= 3; 953 line_ccnt -= 3; 954 trail_ws--; 955 CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); 956 } 957 } 958 959 *in_pp = (const char *)ps; 960 *in_left_p = icnt; 961 *out_pp = (char *)pd; 962 *out_left_p = ocnt; 963 inst->line_ccnt = line_ccnt; 964 inst->lb_ptr = lb_ptr; 965 inst->lb_cnt = lb_cnt; 966 return err; 967} 968#undef NEXT_CHAR 969#undef CONSUME_CHAR 970 971static 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) 972{ 973 if (line_len < 4 && lbchars != NULL) { 974 return PHP_CONV_ERR_TOO_BIG; 975 } 976 inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_encode_convert; 977 inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_encode_dtor; 978 inst->line_ccnt = line_len; 979 inst->line_len = line_len; 980 if (lbchars != NULL) { 981 inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars); 982 inst->lbchars_len = lbchars_len; 983 } else { 984 inst->lbchars = NULL; 985 } 986 inst->lbchars_dup = lbchars_dup; 987 inst->persistent = persistent; 988 inst->opts = opts; 989 inst->lb_cnt = inst->lb_ptr = 0; 990 return PHP_CONV_ERR_SUCCESS; 991} 992/* }}} */ 993 994/* {{{ php_conv_qprint_decode */ 995typedef struct _php_conv_qprint_decode { 996 php_conv _super; 997 998 int scan_stat; 999 unsigned int next_char; 1000 const char *lbchars; 1001 int lbchars_dup; 1002 size_t lbchars_len; 1003 int persistent; 1004 unsigned int lb_ptr; 1005 unsigned int lb_cnt; 1006} php_conv_qprint_decode; 1007 1008static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst) 1009{ 1010 assert(inst != NULL); 1011 if (inst->lbchars_dup && inst->lbchars != NULL) { 1012 pefree((void *)inst->lbchars, inst->persistent); 1013 } 1014} 1015 1016static 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) 1017{ 1018 php_conv_err_t err = PHP_CONV_ERR_SUCCESS; 1019 size_t icnt, ocnt; 1020 unsigned char *ps, *pd; 1021 unsigned int scan_stat; 1022 unsigned int next_char; 1023 unsigned int lb_ptr, lb_cnt; 1024 1025 lb_ptr = inst->lb_ptr; 1026 lb_cnt = inst->lb_cnt; 1027 1028 if ((in_pp == NULL || in_left_p == NULL) && lb_cnt == lb_ptr) { 1029 if (inst->scan_stat != 0) { 1030 return PHP_CONV_ERR_UNEXPECTED_EOS; 1031 } 1032 return PHP_CONV_ERR_SUCCESS; 1033 } 1034 1035 ps = (unsigned char *)(*in_pp); 1036 icnt = *in_left_p; 1037 pd = (unsigned char *)(*out_pp); 1038 ocnt = *out_left_p; 1039 scan_stat = inst->scan_stat; 1040 next_char = inst->next_char; 1041 1042 for (;;) { 1043 switch (scan_stat) { 1044 case 0: { 1045 if (icnt <= 0) { 1046 goto out; 1047 } 1048 if (*ps == '=') { 1049 scan_stat = 1; 1050 } else { 1051 if (ocnt < 1) { 1052 err = PHP_CONV_ERR_TOO_BIG; 1053 goto out; 1054 } 1055 *(pd++) = *ps; 1056 ocnt--; 1057 } 1058 ps++, icnt--; 1059 } break; 1060 1061 case 1: { 1062 if (icnt <= 0) { 1063 goto out; 1064 } 1065 if (*ps == ' ' || *ps == '\t') { 1066 scan_stat = 4; 1067 ps++, icnt--; 1068 break; 1069 } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\r') { 1070 /* auto-detect line endings, looks like network line ending \r\n (could be mac \r) */ 1071 lb_cnt++; 1072 scan_stat = 5; 1073 ps++, icnt--; 1074 break; 1075 } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\n') { 1076 /* auto-detect line endings, looks like unix-lineendings, not to spec, but it is seem in the wild, a lot */ 1077 lb_cnt = lb_ptr = 0; 1078 scan_stat = 0; 1079 ps++, icnt--; 1080 break; 1081 } else if (lb_cnt < inst->lbchars_len && 1082 *ps == (unsigned char)inst->lbchars[lb_cnt]) { 1083 lb_cnt++; 1084 scan_stat = 5; 1085 ps++, icnt--; 1086 break; 1087 } 1088 } /* break is missing intentionally */ 1089 1090 case 2: { 1091 if (icnt <= 0) { 1092 goto out; 1093 } 1094 1095 if (!isxdigit((int) *ps)) { 1096 err = PHP_CONV_ERR_INVALID_SEQ; 1097 goto out; 1098 } 1099 next_char = (next_char << 4) | (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30); 1100 scan_stat++; 1101 ps++, icnt--; 1102 if (scan_stat != 3) { 1103 break; 1104 } 1105 } /* break is missing intentionally */ 1106 1107 case 3: { 1108 if (ocnt < 1) { 1109 err = PHP_CONV_ERR_TOO_BIG; 1110 goto out; 1111 } 1112 *(pd++) = next_char; 1113 ocnt--; 1114 scan_stat = 0; 1115 } break; 1116 1117 case 4: { 1118 if (icnt <= 0) { 1119 goto out; 1120 } 1121 if (lb_cnt < inst->lbchars_len && 1122 *ps == (unsigned char)inst->lbchars[lb_cnt]) { 1123 lb_cnt++; 1124 scan_stat = 5; 1125 } 1126 if (*ps != '\t' && *ps != ' ') { 1127 err = PHP_CONV_ERR_INVALID_SEQ; 1128 goto out; 1129 } 1130 ps++, icnt--; 1131 } break; 1132 1133 case 5: { 1134 if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') { 1135 /* auto-detect soft line breaks, found network line break */ 1136 lb_cnt = lb_ptr = 0; 1137 scan_stat = 0; 1138 ps++, icnt--; /* consume \n */ 1139 } else if (!inst->lbchars && lb_cnt > 0) { 1140 /* auto-detect soft line breaks, found mac line break */ 1141 lb_cnt = lb_ptr = 0; 1142 scan_stat = 0; 1143 } else if (lb_cnt >= inst->lbchars_len) { 1144 /* soft line break */ 1145 lb_cnt = lb_ptr = 0; 1146 scan_stat = 0; 1147 } else if (icnt > 0) { 1148 if (*ps == (unsigned char)inst->lbchars[lb_cnt]) { 1149 lb_cnt++; 1150 ps++, icnt--; 1151 } else { 1152 scan_stat = 6; /* no break for short-cut */ 1153 } 1154 } else { 1155 goto out; 1156 } 1157 } break; 1158 1159 case 6: { 1160 if (lb_ptr < lb_cnt) { 1161 if (ocnt < 1) { 1162 err = PHP_CONV_ERR_TOO_BIG; 1163 goto out; 1164 } 1165 *(pd++) = inst->lbchars[lb_ptr++]; 1166 ocnt--; 1167 } else { 1168 scan_stat = 0; 1169 lb_cnt = lb_ptr = 0; 1170 } 1171 } break; 1172 } 1173 } 1174out: 1175 *in_pp = (const char *)ps; 1176 *in_left_p = icnt; 1177 *out_pp = (char *)pd; 1178 *out_left_p = ocnt; 1179 inst->scan_stat = scan_stat; 1180 inst->lb_ptr = lb_ptr; 1181 inst->lb_cnt = lb_cnt; 1182 inst->next_char = next_char; 1183 1184 return err; 1185} 1186static 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) 1187{ 1188 inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_decode_convert; 1189 inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_decode_dtor; 1190 inst->scan_stat = 0; 1191 inst->next_char = 0; 1192 inst->lb_ptr = inst->lb_cnt = 0; 1193 if (lbchars != NULL) { 1194 inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars); 1195 inst->lbchars_len = lbchars_len; 1196 } else { 1197 inst->lbchars = NULL; 1198 inst->lbchars_len = 0; 1199 } 1200 inst->lbchars_dup = lbchars_dup; 1201 inst->persistent = persistent; 1202 return PHP_CONV_ERR_SUCCESS; 1203} 1204/* }}} */ 1205 1206typedef struct _php_convert_filter { 1207 php_conv *cd; 1208 int persistent; 1209 char *filtername; 1210 char stub[128]; 1211 size_t stub_len; 1212} php_convert_filter; 1213 1214#define PHP_CONV_BASE64_ENCODE 1 1215#define PHP_CONV_BASE64_DECODE 2 1216#define PHP_CONV_QPRINT_ENCODE 3 1217#define PHP_CONV_QPRINT_DECODE 4 1218 1219static 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) 1220{ 1221 zval **tmpval; 1222 1223 *pretval = NULL; 1224 *pretval_len = 0; 1225 1226 if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { 1227 if (Z_TYPE_PP(tmpval) != IS_STRING) { 1228 zval zt = **tmpval; 1229 1230 convert_to_string(&zt); 1231 1232 if (NULL == (*pretval = pemalloc(Z_STRLEN(zt) + 1, persistent))) { 1233 return PHP_CONV_ERR_ALLOC; 1234 } 1235 1236 *pretval_len = Z_STRLEN(zt); 1237 memcpy(*pretval, Z_STRVAL(zt), Z_STRLEN(zt) + 1); 1238 zval_dtor(&zt); 1239 } else { 1240 if (NULL == (*pretval = pemalloc(Z_STRLEN_PP(tmpval) + 1, persistent))) { 1241 return PHP_CONV_ERR_ALLOC; 1242 } 1243 *pretval_len = Z_STRLEN_PP(tmpval); 1244 memcpy(*pretval, Z_STRVAL_PP(tmpval), Z_STRLEN_PP(tmpval) + 1); 1245 } 1246 } else { 1247 return PHP_CONV_ERR_NOT_FOUND; 1248 } 1249 return PHP_CONV_ERR_SUCCESS; 1250} 1251 1252#if IT_WAS_USED 1253static php_conv_err_t php_conv_get_long_prop_ex(const HashTable *ht, long *pretval, char *field_name, size_t field_name_len) 1254{ 1255 zval **tmpval; 1256 1257 *pretval = 0; 1258 1259 if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { 1260 zval tmp, *ztval = *tmpval; 1261 1262 if (Z_TYPE_PP(tmpval) != IS_LONG) { 1263 tmp = *ztval; 1264 zval_copy_ctor(&tmp); 1265 convert_to_long(&tmp); 1266 ztval = &tmp; 1267 } 1268 *pretval = Z_LVAL_P(ztval); 1269 } else { 1270 return PHP_CONV_ERR_NOT_FOUND; 1271 } 1272 return PHP_CONV_ERR_SUCCESS; 1273} 1274#endif 1275 1276static php_conv_err_t php_conv_get_ulong_prop_ex(const HashTable *ht, unsigned long *pretval, char *field_name, size_t field_name_len) 1277{ 1278 zval **tmpval; 1279 1280 *pretval = 0; 1281 1282 if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { 1283 zval tmp, *ztval = *tmpval; 1284 1285 if (Z_TYPE_PP(tmpval) != IS_LONG) { 1286 tmp = *ztval; 1287 zval_copy_ctor(&tmp); 1288 convert_to_long(&tmp); 1289 ztval = &tmp; 1290 } 1291 if (Z_LVAL_P(ztval) < 0) { 1292 *pretval = 0; 1293 } else { 1294 *pretval = Z_LVAL_P(ztval); 1295 } 1296 } else { 1297 return PHP_CONV_ERR_NOT_FOUND; 1298 } 1299 return PHP_CONV_ERR_SUCCESS; 1300} 1301 1302static php_conv_err_t php_conv_get_bool_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len) 1303{ 1304 zval **tmpval; 1305 1306 *pretval = 0; 1307 1308 if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { 1309 zval tmp, *ztval = *tmpval; 1310 1311 if (Z_TYPE_PP(tmpval) != IS_BOOL) { 1312 tmp = *ztval; 1313 zval_copy_ctor(&tmp); 1314 convert_to_boolean(&tmp); 1315 ztval = &tmp; 1316 } 1317 *pretval = Z_BVAL_P(ztval); 1318 } else { 1319 return PHP_CONV_ERR_NOT_FOUND; 1320 } 1321 return PHP_CONV_ERR_SUCCESS; 1322} 1323 1324 1325#if IT_WAS_USED 1326static int php_conv_get_int_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len) 1327{ 1328 long l; 1329 php_conv_err_t err; 1330 1331 *pretval = 0; 1332 1333 if ((err = php_conv_get_long_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) { 1334 *pretval = l; 1335 } 1336 return err; 1337} 1338#endif 1339 1340static int php_conv_get_uint_prop_ex(const HashTable *ht, unsigned int *pretval, char *field_name, size_t field_name_len) 1341{ 1342 long l; 1343 php_conv_err_t err; 1344 1345 *pretval = 0; 1346 1347 if ((err = php_conv_get_ulong_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) { 1348 *pretval = l; 1349 } 1350 return err; 1351} 1352 1353#define GET_STR_PROP(ht, var, var_len, fldname, persistent) \ 1354 php_conv_get_string_prop_ex(ht, &var, &var_len, fldname, sizeof(fldname), persistent) 1355 1356#define GET_INT_PROP(ht, var, fldname) \ 1357 php_conv_get_int_prop_ex(ht, &var, fldname, sizeof(fldname)) 1358 1359#define GET_UINT_PROP(ht, var, fldname) \ 1360 php_conv_get_uint_prop_ex(ht, &var, fldname, sizeof(fldname)) 1361 1362#define GET_BOOL_PROP(ht, var, fldname) \ 1363 php_conv_get_bool_prop_ex(ht, &var, fldname, sizeof(fldname)) 1364 1365static php_conv *php_conv_open(int conv_mode, const HashTable *options, int persistent) 1366{ 1367 /* FIXME: I'll have to replace this ugly code by something neat 1368 (factories?) in the near future. */ 1369 php_conv *retval = NULL; 1370 1371 switch (conv_mode) { 1372 case PHP_CONV_BASE64_ENCODE: { 1373 unsigned int line_len = 0; 1374 char *lbchars = NULL; 1375 size_t lbchars_len; 1376 1377 if (options != NULL) { 1378 GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0); 1379 GET_UINT_PROP(options, line_len, "line-length"); 1380 if (line_len < 4) { 1381 if (lbchars != NULL) { 1382 pefree(lbchars, 0); 1383 } 1384 lbchars = NULL; 1385 } else { 1386 if (lbchars == NULL) { 1387 lbchars = pestrdup("\r\n", 0); 1388 lbchars_len = 2; 1389 } 1390 } 1391 } 1392 retval = pemalloc(sizeof(php_conv_base64_encode), persistent); 1393 if (lbchars != NULL) { 1394 if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, line_len, lbchars, lbchars_len, 1, persistent)) { 1395 if (lbchars != NULL) { 1396 pefree(lbchars, 0); 1397 } 1398 goto out_failure; 1399 } 1400 pefree(lbchars, 0); 1401 } else { 1402 if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, 0, NULL, 0, 0, persistent)) { 1403 goto out_failure; 1404 } 1405 } 1406 } break; 1407 1408 case PHP_CONV_BASE64_DECODE: 1409 retval = pemalloc(sizeof(php_conv_base64_decode), persistent); 1410 if (php_conv_base64_decode_ctor((php_conv_base64_decode *)retval)) { 1411 goto out_failure; 1412 } 1413 break; 1414 1415 case PHP_CONV_QPRINT_ENCODE: { 1416 unsigned int line_len = 0; 1417 char *lbchars = NULL; 1418 size_t lbchars_len; 1419 int opts = 0; 1420 1421 if (options != NULL) { 1422 int opt_binary = 0; 1423 int opt_force_encode_first = 0; 1424 1425 GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0); 1426 GET_UINT_PROP(options, line_len, "line-length"); 1427 GET_BOOL_PROP(options, opt_binary, "binary"); 1428 GET_BOOL_PROP(options, opt_force_encode_first, "force-encode-first"); 1429 1430 if (line_len < 4) { 1431 if (lbchars != NULL) { 1432 pefree(lbchars, 0); 1433 } 1434 lbchars = NULL; 1435 } else { 1436 if (lbchars == NULL) { 1437 lbchars = pestrdup("\r\n", 0); 1438 lbchars_len = 2; 1439 } 1440 } 1441 opts |= (opt_binary ? PHP_CONV_QPRINT_OPT_BINARY : 0); 1442 opts |= (opt_force_encode_first ? PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST : 0); 1443 } 1444 retval = pemalloc(sizeof(php_conv_qprint_encode), persistent); 1445 if (lbchars != NULL) { 1446 if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, line_len, lbchars, lbchars_len, 1, opts, persistent)) { 1447 pefree(lbchars, 0); 1448 goto out_failure; 1449 } 1450 pefree(lbchars, 0); 1451 } else { 1452 if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, 0, NULL, 0, 0, opts, persistent)) { 1453 goto out_failure; 1454 } 1455 } 1456 } break; 1457 1458 case PHP_CONV_QPRINT_DECODE: { 1459 char *lbchars = NULL; 1460 size_t lbchars_len; 1461 1462 if (options != NULL) { 1463 /* If line-break-chars are not specified, filter will attempt to detect line endings (\r, \n, or \r\n) */ 1464 GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars", 0); 1465 } 1466 1467 retval = pemalloc(sizeof(php_conv_qprint_decode), persistent); 1468 if (lbchars != NULL) { 1469 if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, lbchars, lbchars_len, 1, persistent)) { 1470 pefree(lbchars, 0); 1471 goto out_failure; 1472 } 1473 pefree(lbchars, 0); 1474 } else { 1475 if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, NULL, 0, 0, persistent)) { 1476 goto out_failure; 1477 } 1478 } 1479 } break; 1480 1481 default: 1482 retval = NULL; 1483 break; 1484 } 1485 return retval; 1486 1487out_failure: 1488 if (retval != NULL) { 1489 pefree(retval, persistent); 1490 } 1491 return NULL; 1492} 1493 1494#undef GET_STR_PROP 1495#undef GET_INT_PROP 1496#undef GET_UINT_PROP 1497#undef GET_BOOL_PROP 1498 1499static int php_convert_filter_ctor(php_convert_filter *inst, 1500 int conv_mode, HashTable *conv_opts, 1501 const char *filtername, int persistent) 1502{ 1503 inst->persistent = persistent; 1504 inst->filtername = pestrdup(filtername, persistent); 1505 inst->stub_len = 0; 1506 1507 if ((inst->cd = php_conv_open(conv_mode, conv_opts, persistent)) == NULL) { 1508 goto out_failure; 1509 } 1510 1511 return SUCCESS; 1512 1513out_failure: 1514 if (inst->cd != NULL) { 1515 php_conv_dtor(inst->cd); 1516 pefree(inst->cd, persistent); 1517 } 1518 if (inst->filtername != NULL) { 1519 pefree(inst->filtername, persistent); 1520 } 1521 return FAILURE; 1522} 1523 1524static void php_convert_filter_dtor(php_convert_filter *inst) 1525{ 1526 if (inst->cd != NULL) { 1527 php_conv_dtor(inst->cd); 1528 pefree(inst->cd, inst->persistent); 1529 } 1530 1531 if (inst->filtername != NULL) { 1532 pefree(inst->filtername, inst->persistent); 1533 } 1534} 1535 1536/* {{{ strfilter_convert_append_bucket */ 1537static int strfilter_convert_append_bucket( 1538 php_convert_filter *inst, 1539 php_stream *stream, php_stream_filter *filter, 1540 php_stream_bucket_brigade *buckets_out, 1541 const char *ps, size_t buf_len, size_t *consumed, 1542 int persistent TSRMLS_DC) 1543{ 1544 php_conv_err_t err; 1545 php_stream_bucket *new_bucket; 1546 char *out_buf = NULL; 1547 size_t out_buf_size; 1548 char *pd; 1549 const char *pt; 1550 size_t ocnt, icnt, tcnt; 1551 size_t initial_out_buf_size; 1552 1553 if (ps == NULL) { 1554 initial_out_buf_size = 64; 1555 icnt = 1; 1556 } else { 1557 initial_out_buf_size = buf_len; 1558 icnt = buf_len; 1559 } 1560 1561 out_buf_size = ocnt = initial_out_buf_size; 1562 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { 1563 return FAILURE; 1564 } 1565 1566 pd = out_buf; 1567 1568 if (inst->stub_len > 0) { 1569 pt = inst->stub; 1570 tcnt = inst->stub_len; 1571 1572 while (tcnt > 0) { 1573 err = php_conv_convert(inst->cd, &pt, &tcnt, &pd, &ocnt); 1574 1575 switch (err) { 1576 case PHP_CONV_ERR_INVALID_SEQ: 1577 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername); 1578 goto out_failure; 1579 1580 case PHP_CONV_ERR_MORE: 1581 if (ps != NULL) { 1582 if (icnt > 0) { 1583 if (inst->stub_len >= sizeof(inst->stub)) { 1584 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername); 1585 goto out_failure; 1586 } 1587 inst->stub[inst->stub_len++] = *(ps++); 1588 icnt--; 1589 pt = inst->stub; 1590 tcnt = inst->stub_len; 1591 } else { 1592 tcnt = 0; 1593 break; 1594 } 1595 } 1596 break; 1597 1598 case PHP_CONV_ERR_UNEXPECTED_EOS: 1599 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected end of stream", inst->filtername); 1600 goto out_failure; 1601 1602 case PHP_CONV_ERR_TOO_BIG: { 1603 char *new_out_buf; 1604 size_t new_out_buf_size; 1605 1606 new_out_buf_size = out_buf_size << 1; 1607 1608 if (new_out_buf_size < out_buf_size) { 1609 /* whoa! no bigger buckets are sold anywhere... */ 1610 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 1611 goto out_failure; 1612 } 1613 1614 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 1615 1616 out_buf_size = ocnt = initial_out_buf_size; 1617 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { 1618 return FAILURE; 1619 } 1620 pd = out_buf; 1621 } else { 1622 if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) { 1623 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 1624 goto out_failure; 1625 } 1626 1627 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 1628 return FAILURE; 1629 } 1630 1631 pd = new_out_buf + (pd - out_buf); 1632 ocnt += (new_out_buf_size - out_buf_size); 1633 out_buf = new_out_buf; 1634 out_buf_size = new_out_buf_size; 1635 } 1636 } break; 1637 1638 case PHP_CONV_ERR_UNKNOWN: 1639 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername); 1640 goto out_failure; 1641 1642 default: 1643 break; 1644 } 1645 } 1646 memmove(inst->stub, pt, tcnt); 1647 inst->stub_len = tcnt; 1648 } 1649 1650 while (icnt > 0) { 1651 err = ((ps == NULL ? php_conv_convert(inst->cd, NULL, NULL, &pd, &ocnt): 1652 php_conv_convert(inst->cd, &ps, &icnt, &pd, &ocnt))); 1653 switch (err) { 1654 case PHP_CONV_ERR_INVALID_SEQ: 1655 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence", inst->filtername); 1656 goto out_failure; 1657 1658 case PHP_CONV_ERR_MORE: 1659 if (ps != NULL) { 1660 if (icnt > sizeof(inst->stub)) { 1661 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer", inst->filtername); 1662 goto out_failure; 1663 } 1664 memcpy(inst->stub, ps, icnt); 1665 inst->stub_len = icnt; 1666 ps += icnt; 1667 icnt = 0; 1668 } else { 1669 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected octet values", inst->filtername); 1670 goto out_failure; 1671 } 1672 break; 1673 1674 case PHP_CONV_ERR_TOO_BIG: { 1675 char *new_out_buf; 1676 size_t new_out_buf_size; 1677 1678 new_out_buf_size = out_buf_size << 1; 1679 1680 if (new_out_buf_size < out_buf_size) { 1681 /* whoa! no bigger buckets are sold anywhere... */ 1682 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 1683 goto out_failure; 1684 } 1685 1686 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 1687 1688 out_buf_size = ocnt = initial_out_buf_size; 1689 if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { 1690 return FAILURE; 1691 } 1692 pd = out_buf; 1693 } else { 1694 if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) { 1695 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 1696 goto out_failure; 1697 } 1698 1699 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 1700 return FAILURE; 1701 } 1702 pd = new_out_buf + (pd - out_buf); 1703 ocnt += (new_out_buf_size - out_buf_size); 1704 out_buf = new_out_buf; 1705 out_buf_size = new_out_buf_size; 1706 } 1707 } break; 1708 1709 case PHP_CONV_ERR_UNKNOWN: 1710 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error", inst->filtername); 1711 goto out_failure; 1712 1713 default: 1714 if (ps == NULL) { 1715 icnt = 0; 1716 } 1717 break; 1718 } 1719 } 1720 1721 if (out_buf_size - ocnt > 0) { 1722 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { 1723 goto out_failure; 1724 } 1725 php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); 1726 } else { 1727 pefree(out_buf, persistent); 1728 } 1729 *consumed += buf_len - icnt; 1730 1731 return SUCCESS; 1732 1733out_failure: 1734 pefree(out_buf, persistent); 1735 return FAILURE; 1736} 1737/* }}} */ 1738 1739static php_stream_filter_status_t strfilter_convert_filter( 1740 php_stream *stream, 1741 php_stream_filter *thisfilter, 1742 php_stream_bucket_brigade *buckets_in, 1743 php_stream_bucket_brigade *buckets_out, 1744 size_t *bytes_consumed, 1745 int flags 1746 TSRMLS_DC) 1747{ 1748 php_stream_bucket *bucket = NULL; 1749 size_t consumed = 0; 1750 php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract; 1751 1752 while (buckets_in->head != NULL) { 1753 bucket = buckets_in->head; 1754 1755 php_stream_bucket_unlink(bucket TSRMLS_CC); 1756 1757 if (strfilter_convert_append_bucket(inst, stream, thisfilter, 1758 buckets_out, bucket->buf, bucket->buflen, &consumed, 1759 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) { 1760 goto out_failure; 1761 } 1762 1763 php_stream_bucket_delref(bucket TSRMLS_CC); 1764 } 1765 1766 if (flags != PSFS_FLAG_NORMAL) { 1767 if (strfilter_convert_append_bucket(inst, stream, thisfilter, 1768 buckets_out, NULL, 0, &consumed, 1769 php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) { 1770 goto out_failure; 1771 } 1772 } 1773 1774 if (bytes_consumed) { 1775 *bytes_consumed = consumed; 1776 } 1777 1778 return PSFS_PASS_ON; 1779 1780out_failure: 1781 if (bucket != NULL) { 1782 php_stream_bucket_delref(bucket TSRMLS_CC); 1783 } 1784 return PSFS_ERR_FATAL; 1785} 1786 1787static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC) 1788{ 1789 assert(thisfilter->abstract != NULL); 1790 1791 php_convert_filter_dtor((php_convert_filter *)thisfilter->abstract); 1792 pefree(thisfilter->abstract, ((php_convert_filter *)thisfilter->abstract)->persistent); 1793} 1794 1795static php_stream_filter_ops strfilter_convert_ops = { 1796 strfilter_convert_filter, 1797 strfilter_convert_dtor, 1798 "convert.*" 1799}; 1800 1801static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) 1802{ 1803 php_convert_filter *inst; 1804 php_stream_filter *retval = NULL; 1805 1806 char *dot; 1807 int conv_mode = 0; 1808 1809 if (filterparams != NULL && Z_TYPE_P(filterparams) != IS_ARRAY) { 1810 php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid filter parameter", filtername); 1811 return NULL; 1812 } 1813 1814 if ((dot = strchr(filtername, '.')) == NULL) { 1815 return NULL; 1816 } 1817 ++dot; 1818 1819 inst = pemalloc(sizeof(php_convert_filter), persistent); 1820 1821 if (strcasecmp(dot, "base64-encode") == 0) { 1822 conv_mode = PHP_CONV_BASE64_ENCODE; 1823 } else if (strcasecmp(dot, "base64-decode") == 0) { 1824 conv_mode = PHP_CONV_BASE64_DECODE; 1825 } else if (strcasecmp(dot, "quoted-printable-encode") == 0) { 1826 conv_mode = PHP_CONV_QPRINT_ENCODE; 1827 } else if (strcasecmp(dot, "quoted-printable-decode") == 0) { 1828 conv_mode = PHP_CONV_QPRINT_DECODE; 1829 } 1830 1831 if (php_convert_filter_ctor(inst, conv_mode, 1832 (filterparams != NULL ? Z_ARRVAL_P(filterparams) : NULL), 1833 filtername, persistent) != SUCCESS) { 1834 goto out; 1835 } 1836 1837 retval = php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent); 1838out: 1839 if (retval == NULL) { 1840 pefree(inst, persistent); 1841 } 1842 1843 return retval; 1844} 1845 1846static php_stream_filter_factory strfilter_convert_factory = { 1847 strfilter_convert_create 1848}; 1849/* }}} */ 1850 1851/* {{{ consumed filter implementation */ 1852typedef struct _php_consumed_filter_data { 1853 int persistent; 1854 size_t consumed; 1855 off_t offset; 1856} php_consumed_filter_data; 1857 1858static php_stream_filter_status_t consumed_filter_filter( 1859 php_stream *stream, 1860 php_stream_filter *thisfilter, 1861 php_stream_bucket_brigade *buckets_in, 1862 php_stream_bucket_brigade *buckets_out, 1863 size_t *bytes_consumed, 1864 int flags 1865 TSRMLS_DC) 1866{ 1867 php_consumed_filter_data *data = (php_consumed_filter_data *)(thisfilter->abstract); 1868 php_stream_bucket *bucket; 1869 size_t consumed = 0; 1870 1871 if (data->offset == ~0) { 1872 data->offset = php_stream_tell(stream); 1873 } 1874 while ((bucket = buckets_in->head) != NULL) { 1875 php_stream_bucket_unlink(bucket TSRMLS_CC); 1876 consumed += bucket->buflen; 1877 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); 1878 } 1879 if (bytes_consumed) { 1880 *bytes_consumed = consumed; 1881 } 1882 if (flags & PSFS_FLAG_FLUSH_CLOSE) { 1883 php_stream_seek(stream, data->offset + data->consumed, SEEK_SET); 1884 } 1885 data->consumed += consumed; 1886 1887 return PSFS_PASS_ON; 1888} 1889 1890static void consumed_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC) 1891{ 1892 if (thisfilter && thisfilter->abstract) { 1893 php_consumed_filter_data *data = (php_consumed_filter_data*)thisfilter->abstract; 1894 pefree(data, data->persistent); 1895 } 1896} 1897 1898static php_stream_filter_ops consumed_filter_ops = { 1899 consumed_filter_filter, 1900 consumed_filter_dtor, 1901 "consumed" 1902}; 1903 1904static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) 1905{ 1906 php_stream_filter_ops *fops = NULL; 1907 php_consumed_filter_data *data; 1908 1909 if (strcasecmp(filtername, "consumed")) { 1910 return NULL; 1911 } 1912 1913 /* Create this filter */ 1914 data = pecalloc(1, sizeof(php_consumed_filter_data), persistent); 1915 if (!data) { 1916 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_consumed_filter_data)); 1917 return NULL; 1918 } 1919 data->persistent = persistent; 1920 data->consumed = 0; 1921 data->offset = ~0; 1922 fops = &consumed_filter_ops; 1923 1924 return php_stream_filter_alloc(fops, data, persistent); 1925} 1926 1927php_stream_filter_factory consumed_filter_factory = { 1928 consumed_filter_create 1929}; 1930 1931/* }}} */ 1932 1933/* {{{ chunked filter implementation */ 1934typedef enum _php_chunked_filter_state { 1935 CHUNK_SIZE_START, 1936 CHUNK_SIZE, 1937 CHUNK_SIZE_EXT, 1938 CHUNK_SIZE_CR, 1939 CHUNK_SIZE_LF, 1940 CHUNK_BODY, 1941 CHUNK_BODY_CR, 1942 CHUNK_BODY_LF, 1943 CHUNK_TRAILER, 1944 CHUNK_ERROR 1945} php_chunked_filter_state; 1946 1947typedef struct _php_chunked_filter_data { 1948 php_chunked_filter_state state; 1949 size_t chunk_size; 1950 int persistent; 1951} php_chunked_filter_data; 1952 1953static int php_dechunk(char *buf, int len, php_chunked_filter_data *data) 1954{ 1955 char *p = buf; 1956 char *end = p + len; 1957 char *out = buf; 1958 int out_len = 0; 1959 1960 while (p < end) { 1961 switch (data->state) { 1962 case CHUNK_SIZE_START: 1963 data->chunk_size = 0; 1964 case CHUNK_SIZE: 1965 while (p < end) { 1966 if (*p >= '0' && *p <= '9') { 1967 data->chunk_size = (data->chunk_size * 16) + (*p - '0'); 1968 } else if (*p >= 'A' && *p <= 'F') { 1969 data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10); 1970 } else if (*p >= 'a' && *p <= 'f') { 1971 data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10); 1972 } else if (data->state == CHUNK_SIZE_START) { 1973 data->state = CHUNK_ERROR; 1974 break; 1975 } else { 1976 data->state = CHUNK_SIZE_EXT; 1977 break; 1978 } 1979 data->state = CHUNK_SIZE; 1980 p++; 1981 } 1982 if (data->state == CHUNK_ERROR) { 1983 continue; 1984 } else if (p == end) { 1985 return out_len; 1986 } 1987 case CHUNK_SIZE_EXT: 1988 /* skip extension */ 1989 while (p < end && *p != '\r' && *p != '\n') { 1990 p++; 1991 } 1992 if (p == end) { 1993 return out_len; 1994 } 1995 case CHUNK_SIZE_CR: 1996 if (*p == '\r') { 1997 p++; 1998 if (p == end) { 1999 data->state = CHUNK_SIZE_LF; 2000 return out_len; 2001 } 2002 } 2003 case CHUNK_SIZE_LF: 2004 if (*p == '\n') { 2005 p++; 2006 if (data->chunk_size == 0) { 2007 /* last chunk */ 2008 data->state = CHUNK_TRAILER; 2009 continue; 2010 } else if (p == end) { 2011 data->state = CHUNK_BODY; 2012 return out_len; 2013 } 2014 } else { 2015 data->state = CHUNK_ERROR; 2016 continue; 2017 } 2018 case CHUNK_BODY: 2019 if ((size_t) (end - p) >= data->chunk_size) { 2020 if (p != out) { 2021 memmove(out, p, data->chunk_size); 2022 } 2023 out += data->chunk_size; 2024 out_len += data->chunk_size; 2025 p += data->chunk_size; 2026 if (p == end) { 2027 data->state = CHUNK_BODY_CR; 2028 return out_len; 2029 } 2030 } else { 2031 if (p != out) { 2032 memmove(out, p, end - p); 2033 } 2034 data->chunk_size -= end - p; 2035 data->state=CHUNK_BODY; 2036 out_len += end - p; 2037 return out_len; 2038 } 2039 case CHUNK_BODY_CR: 2040 if (*p == '\r') { 2041 p++; 2042 if (p == end) { 2043 data->state = CHUNK_BODY_LF; 2044 return out_len; 2045 } 2046 } 2047 case CHUNK_BODY_LF: 2048 if (*p == '\n') { 2049 p++; 2050 data->state = CHUNK_SIZE_START; 2051 continue; 2052 } else { 2053 data->state = CHUNK_ERROR; 2054 continue; 2055 } 2056 case CHUNK_TRAILER: 2057 /* ignore trailer */ 2058 p = end; 2059 continue; 2060 case CHUNK_ERROR: 2061 if (p != out) { 2062 memmove(out, p, end - p); 2063 } 2064 out_len += end - p; 2065 return out_len; 2066 } 2067 } 2068 return out_len; 2069} 2070 2071static php_stream_filter_status_t php_chunked_filter( 2072 php_stream *stream, 2073 php_stream_filter *thisfilter, 2074 php_stream_bucket_brigade *buckets_in, 2075 php_stream_bucket_brigade *buckets_out, 2076 size_t *bytes_consumed, 2077 int flags 2078 TSRMLS_DC) 2079{ 2080 php_stream_bucket *bucket; 2081 size_t consumed = 0; 2082 php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract; 2083 2084 while (buckets_in->head) { 2085 bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); 2086 consumed += bucket->buflen; 2087 bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, data); 2088 php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); 2089 } 2090 2091 if (bytes_consumed) { 2092 *bytes_consumed = consumed; 2093 } 2094 2095 return PSFS_PASS_ON; 2096} 2097 2098static void php_chunked_dtor(php_stream_filter *thisfilter TSRMLS_DC) 2099{ 2100 if (thisfilter && thisfilter->abstract) { 2101 php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract; 2102 pefree(data, data->persistent); 2103 } 2104} 2105 2106static php_stream_filter_ops chunked_filter_ops = { 2107 php_chunked_filter, 2108 php_chunked_dtor, 2109 "dechunk" 2110}; 2111 2112static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) 2113{ 2114 php_stream_filter_ops *fops = NULL; 2115 php_chunked_filter_data *data; 2116 2117 if (strcasecmp(filtername, "dechunk")) { 2118 return NULL; 2119 } 2120 2121 /* Create this filter */ 2122 data = (php_chunked_filter_data *)pecalloc(1, sizeof(php_chunked_filter_data), persistent); 2123 if (!data) { 2124 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes", sizeof(php_chunked_filter_data)); 2125 return NULL; 2126 } 2127 data->state = CHUNK_SIZE_START; 2128 data->chunk_size = 0; 2129 data->persistent = persistent; 2130 fops = &chunked_filter_ops; 2131 2132 return php_stream_filter_alloc(fops, data, persistent); 2133} 2134 2135static php_stream_filter_factory chunked_filter_factory = { 2136 chunked_filter_create 2137}; 2138/* }}} */ 2139 2140static const struct { 2141 php_stream_filter_ops *ops; 2142 php_stream_filter_factory *factory; 2143} standard_filters[] = { 2144 { &strfilter_rot13_ops, &strfilter_rot13_factory }, 2145 { &strfilter_toupper_ops, &strfilter_toupper_factory }, 2146 { &strfilter_tolower_ops, &strfilter_tolower_factory }, 2147 { &strfilter_strip_tags_ops, &strfilter_strip_tags_factory }, 2148 { &strfilter_convert_ops, &strfilter_convert_factory }, 2149 { &consumed_filter_ops, &consumed_filter_factory }, 2150 { &chunked_filter_ops, &chunked_filter_factory }, 2151 /* additional filters to go here */ 2152 { NULL, NULL } 2153}; 2154 2155/* {{{ filter MINIT and MSHUTDOWN */ 2156PHP_MINIT_FUNCTION(standard_filters) 2157{ 2158 int i; 2159 2160 for (i = 0; standard_filters[i].ops; i++) { 2161 if (FAILURE == php_stream_filter_register_factory( 2162 standard_filters[i].ops->label, 2163 standard_filters[i].factory 2164 TSRMLS_CC)) { 2165 return FAILURE; 2166 } 2167 } 2168 return SUCCESS; 2169} 2170 2171PHP_MSHUTDOWN_FUNCTION(standard_filters) 2172{ 2173 int i; 2174 2175 for (i = 0; standard_filters[i].ops; i++) { 2176 php_stream_filter_unregister_factory(standard_filters[i].ops->label TSRMLS_CC); 2177 } 2178 return SUCCESS; 2179} 2180/* }}} */ 2181 2182/* 2183 * Local variables: 2184 * tab-width: 4 2185 * c-basic-offset: 4 2186 * End: 2187 * vim600: sw=4 ts=4 fdm=marker 2188 * vim<600: sw=4 ts=4 2189 */ 2190