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