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