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