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