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