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