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