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 seen 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) != PHP_CONV_ERR_SUCCESS) {
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) != PHP_CONV_ERR_SUCCESS) {
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) != PHP_CONV_ERR_SUCCESS) {
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) != PHP_CONV_ERR_SUCCESS) {
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) != PHP_CONV_ERR_SUCCESS) {
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) != PHP_CONV_ERR_SUCCESS) {
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 if (inst->filtername != NULL) {
1305 pefree(inst->filtername, persistent);
1306 }
1307 return FAILURE;
1308 }
1309
1310 return SUCCESS;
1311 }
1312
php_convert_filter_dtor(php_convert_filter * inst)1313 static void php_convert_filter_dtor(php_convert_filter *inst)
1314 {
1315 if (inst->cd != NULL) {
1316 php_conv_dtor(inst->cd);
1317 pefree(inst->cd, inst->persistent);
1318 }
1319
1320 if (inst->filtername != NULL) {
1321 pefree(inst->filtername, inst->persistent);
1322 }
1323 }
1324
1325 /* {{{ 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)1326 static int strfilter_convert_append_bucket(
1327 php_convert_filter *inst,
1328 php_stream *stream, php_stream_filter *filter,
1329 php_stream_bucket_brigade *buckets_out,
1330 const char *ps, size_t buf_len, size_t *consumed,
1331 int persistent)
1332 {
1333 php_conv_err_t err;
1334 php_stream_bucket *new_bucket;
1335 char *out_buf = NULL;
1336 size_t out_buf_size;
1337 char *pd;
1338 const char *pt;
1339 size_t ocnt, icnt, tcnt;
1340 size_t initial_out_buf_size;
1341
1342 if (ps == NULL) {
1343 initial_out_buf_size = 64;
1344 icnt = 1;
1345 } else {
1346 initial_out_buf_size = buf_len;
1347 icnt = buf_len;
1348 }
1349
1350 out_buf_size = ocnt = initial_out_buf_size;
1351 out_buf = pemalloc(out_buf_size, persistent);
1352
1353 pd = out_buf;
1354
1355 if (inst->stub_len > 0) {
1356 pt = inst->stub;
1357 tcnt = inst->stub_len;
1358
1359 while (tcnt > 0) {
1360 err = php_conv_convert(inst->cd, &pt, &tcnt, &pd, &ocnt);
1361
1362 switch (err) {
1363 case PHP_CONV_ERR_INVALID_SEQ:
1364 php_error_docref(NULL, E_WARNING, "Stream filter (%s): invalid byte sequence", inst->filtername);
1365 goto out_failure;
1366
1367 case PHP_CONV_ERR_MORE:
1368 if (ps != NULL) {
1369 if (icnt > 0) {
1370 if (inst->stub_len >= sizeof(inst->stub)) {
1371 php_error_docref(NULL, E_WARNING, "Stream filter (%s): insufficient buffer", inst->filtername);
1372 goto out_failure;
1373 }
1374 inst->stub[inst->stub_len++] = *(ps++);
1375 icnt--;
1376 pt = inst->stub;
1377 tcnt = inst->stub_len;
1378 } else {
1379 tcnt = 0;
1380 break;
1381 }
1382 }
1383 break;
1384
1385 case PHP_CONV_ERR_UNEXPECTED_EOS:
1386 php_error_docref(NULL, E_WARNING, "Stream filter (%s): unexpected end of stream", inst->filtername);
1387 goto out_failure;
1388
1389 case PHP_CONV_ERR_TOO_BIG: {
1390 char *new_out_buf;
1391 size_t new_out_buf_size;
1392
1393 new_out_buf_size = out_buf_size << 1;
1394
1395 if (new_out_buf_size < out_buf_size) {
1396 /* whoa! no bigger buckets are sold anywhere... */
1397 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
1398 goto out_failure;
1399 }
1400
1401 php_stream_bucket_append(buckets_out, new_bucket);
1402
1403 out_buf_size = ocnt = initial_out_buf_size;
1404 out_buf = pemalloc(out_buf_size, persistent);
1405 pd = out_buf;
1406 } else {
1407 new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
1408 pd = new_out_buf + (pd - out_buf);
1409 ocnt += (new_out_buf_size - out_buf_size);
1410 out_buf = new_out_buf;
1411 out_buf_size = new_out_buf_size;
1412 }
1413 } break;
1414
1415 case PHP_CONV_ERR_UNKNOWN:
1416 php_error_docref(NULL, E_WARNING, "Stream filter (%s): unknown error", inst->filtername);
1417 goto out_failure;
1418
1419 default:
1420 break;
1421 }
1422 }
1423 memmove(inst->stub, pt, tcnt);
1424 inst->stub_len = tcnt;
1425 }
1426
1427 while (icnt > 0) {
1428 err = ((ps == NULL ? php_conv_convert(inst->cd, NULL, NULL, &pd, &ocnt):
1429 php_conv_convert(inst->cd, &ps, &icnt, &pd, &ocnt)));
1430 switch (err) {
1431 case PHP_CONV_ERR_INVALID_SEQ:
1432 php_error_docref(NULL, E_WARNING, "Stream filter (%s): invalid byte sequence", inst->filtername);
1433 goto out_failure;
1434
1435 case PHP_CONV_ERR_MORE:
1436 if (ps != NULL) {
1437 if (icnt > sizeof(inst->stub)) {
1438 php_error_docref(NULL, E_WARNING, "Stream filter (%s): insufficient buffer", inst->filtername);
1439 goto out_failure;
1440 }
1441 memcpy(inst->stub, ps, icnt);
1442 inst->stub_len = icnt;
1443 ps += icnt;
1444 icnt = 0;
1445 } else {
1446 php_error_docref(NULL, E_WARNING, "Stream filter (%s): unexpected octet values", inst->filtername);
1447 goto out_failure;
1448 }
1449 break;
1450
1451 case PHP_CONV_ERR_TOO_BIG: {
1452 char *new_out_buf;
1453 size_t new_out_buf_size;
1454
1455 new_out_buf_size = out_buf_size << 1;
1456
1457 if (new_out_buf_size < out_buf_size) {
1458 /* whoa! no bigger buckets are sold anywhere... */
1459 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
1460 goto out_failure;
1461 }
1462
1463 php_stream_bucket_append(buckets_out, new_bucket);
1464
1465 out_buf_size = ocnt = initial_out_buf_size;
1466 out_buf = pemalloc(out_buf_size, persistent);
1467 pd = out_buf;
1468 } else {
1469 new_out_buf = perealloc(out_buf, new_out_buf_size, persistent);
1470 pd = new_out_buf + (pd - out_buf);
1471 ocnt += (new_out_buf_size - out_buf_size);
1472 out_buf = new_out_buf;
1473 out_buf_size = new_out_buf_size;
1474 }
1475 } break;
1476
1477 case PHP_CONV_ERR_UNKNOWN:
1478 php_error_docref(NULL, E_WARNING, "Stream filter (%s): unknown error", inst->filtername);
1479 goto out_failure;
1480
1481 default:
1482 if (ps == NULL) {
1483 icnt = 0;
1484 }
1485 break;
1486 }
1487 }
1488
1489 if (out_buf_size > ocnt) {
1490 if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent))) {
1491 goto out_failure;
1492 }
1493 php_stream_bucket_append(buckets_out, new_bucket);
1494 } else {
1495 pefree(out_buf, persistent);
1496 }
1497 *consumed += buf_len - icnt;
1498
1499 return SUCCESS;
1500
1501 out_failure:
1502 pefree(out_buf, persistent);
1503 return FAILURE;
1504 }
1505 /* }}} */
1506
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)1507 static php_stream_filter_status_t strfilter_convert_filter(
1508 php_stream *stream,
1509 php_stream_filter *thisfilter,
1510 php_stream_bucket_brigade *buckets_in,
1511 php_stream_bucket_brigade *buckets_out,
1512 size_t *bytes_consumed,
1513 int flags
1514 )
1515 {
1516 php_stream_bucket *bucket = NULL;
1517 size_t consumed = 0;
1518 php_convert_filter *inst = (php_convert_filter *)Z_PTR(thisfilter->abstract);
1519
1520 while (buckets_in->head != NULL) {
1521 bucket = buckets_in->head;
1522
1523 php_stream_bucket_unlink(bucket);
1524
1525 if (strfilter_convert_append_bucket(inst, stream, thisfilter,
1526 buckets_out, bucket->buf, bucket->buflen, &consumed,
1527 php_stream_is_persistent(stream)) != SUCCESS) {
1528 goto out_failure;
1529 }
1530
1531 php_stream_bucket_delref(bucket);
1532 }
1533
1534 if (flags != PSFS_FLAG_NORMAL) {
1535 if (strfilter_convert_append_bucket(inst, stream, thisfilter,
1536 buckets_out, NULL, 0, &consumed,
1537 php_stream_is_persistent(stream)) != SUCCESS) {
1538 goto out_failure;
1539 }
1540 }
1541
1542 if (bytes_consumed) {
1543 *bytes_consumed = consumed;
1544 }
1545
1546 return PSFS_PASS_ON;
1547
1548 out_failure:
1549 if (bucket != NULL) {
1550 php_stream_bucket_delref(bucket);
1551 }
1552 return PSFS_ERR_FATAL;
1553 }
1554
strfilter_convert_dtor(php_stream_filter * thisfilter)1555 static void strfilter_convert_dtor(php_stream_filter *thisfilter)
1556 {
1557 assert(Z_PTR(thisfilter->abstract) != NULL);
1558
1559 php_convert_filter_dtor((php_convert_filter *)Z_PTR(thisfilter->abstract));
1560 pefree(Z_PTR(thisfilter->abstract), ((php_convert_filter *)Z_PTR(thisfilter->abstract))->persistent);
1561 }
1562
1563 static const php_stream_filter_ops strfilter_convert_ops = {
1564 strfilter_convert_filter,
1565 strfilter_convert_dtor,
1566 "convert.*"
1567 };
1568
strfilter_convert_create(const char * filtername,zval * filterparams,uint8_t persistent)1569 static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, uint8_t persistent)
1570 {
1571 php_convert_filter *inst;
1572 php_stream_filter *retval = NULL;
1573
1574 char *dot;
1575 int conv_mode = 0;
1576
1577 if (filterparams != NULL && Z_TYPE_P(filterparams) != IS_ARRAY) {
1578 php_error_docref(NULL, E_WARNING, "Stream filter (%s): invalid filter parameter", filtername);
1579 return NULL;
1580 }
1581
1582 if ((dot = strchr(filtername, '.')) == NULL) {
1583 return NULL;
1584 }
1585 ++dot;
1586
1587 inst = pemalloc(sizeof(php_convert_filter), persistent);
1588
1589 if (strcasecmp(dot, "base64-encode") == 0) {
1590 conv_mode = PHP_CONV_BASE64_ENCODE;
1591 } else if (strcasecmp(dot, "base64-decode") == 0) {
1592 conv_mode = PHP_CONV_BASE64_DECODE;
1593 } else if (strcasecmp(dot, "quoted-printable-encode") == 0) {
1594 conv_mode = PHP_CONV_QPRINT_ENCODE;
1595 } else if (strcasecmp(dot, "quoted-printable-decode") == 0) {
1596 conv_mode = PHP_CONV_QPRINT_DECODE;
1597 }
1598
1599 if (php_convert_filter_ctor(inst, conv_mode,
1600 (filterparams != NULL ? Z_ARRVAL_P(filterparams) : NULL),
1601 filtername, persistent) != SUCCESS) {
1602 goto out;
1603 }
1604
1605 retval = php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent);
1606 out:
1607 if (retval == NULL) {
1608 pefree(inst, persistent);
1609 }
1610
1611 return retval;
1612 }
1613
1614 static const php_stream_filter_factory strfilter_convert_factory = {
1615 strfilter_convert_create
1616 };
1617 /* }}} */
1618
1619 /* {{{ consumed filter implementation */
1620 typedef struct _php_consumed_filter_data {
1621 size_t consumed;
1622 zend_off_t offset;
1623 uint8_t persistent;
1624 } php_consumed_filter_data;
1625
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)1626 static php_stream_filter_status_t consumed_filter_filter(
1627 php_stream *stream,
1628 php_stream_filter *thisfilter,
1629 php_stream_bucket_brigade *buckets_in,
1630 php_stream_bucket_brigade *buckets_out,
1631 size_t *bytes_consumed,
1632 int flags
1633 )
1634 {
1635 php_consumed_filter_data *data = (php_consumed_filter_data *)Z_PTR(thisfilter->abstract);
1636 php_stream_bucket *bucket;
1637 size_t consumed = 0;
1638
1639 if (data->offset == ~0) {
1640 data->offset = php_stream_tell(stream);
1641 }
1642 while ((bucket = buckets_in->head) != NULL) {
1643 php_stream_bucket_unlink(bucket);
1644 consumed += bucket->buflen;
1645 php_stream_bucket_append(buckets_out, bucket);
1646 }
1647 if (bytes_consumed) {
1648 *bytes_consumed = consumed;
1649 }
1650 if (flags & PSFS_FLAG_FLUSH_CLOSE) {
1651 php_stream_seek(stream, data->offset + data->consumed, SEEK_SET);
1652 }
1653 data->consumed += consumed;
1654
1655 return PSFS_PASS_ON;
1656 }
1657
consumed_filter_dtor(php_stream_filter * thisfilter)1658 static void consumed_filter_dtor(php_stream_filter *thisfilter)
1659 {
1660 if (thisfilter && Z_PTR(thisfilter->abstract)) {
1661 php_consumed_filter_data *data = (php_consumed_filter_data*)Z_PTR(thisfilter->abstract);
1662 pefree(data, data->persistent);
1663 }
1664 }
1665
1666 static const php_stream_filter_ops consumed_filter_ops = {
1667 consumed_filter_filter,
1668 consumed_filter_dtor,
1669 "consumed"
1670 };
1671
consumed_filter_create(const char * filtername,zval * filterparams,uint8_t persistent)1672 static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
1673 {
1674 const php_stream_filter_ops *fops = NULL;
1675 php_consumed_filter_data *data;
1676
1677 if (strcasecmp(filtername, "consumed")) {
1678 return NULL;
1679 }
1680
1681 /* Create this filter */
1682 data = pecalloc(1, sizeof(php_consumed_filter_data), persistent);
1683 data->persistent = persistent;
1684 data->consumed = 0;
1685 data->offset = ~0;
1686 fops = &consumed_filter_ops;
1687
1688 return php_stream_filter_alloc(fops, data, persistent);
1689 }
1690
1691 static const php_stream_filter_factory consumed_filter_factory = {
1692 consumed_filter_create
1693 };
1694
1695 /* }}} */
1696
1697 /* {{{ chunked filter implementation */
1698 typedef enum _php_chunked_filter_state {
1699 CHUNK_SIZE_START,
1700 CHUNK_SIZE,
1701 CHUNK_SIZE_EXT,
1702 CHUNK_SIZE_CR,
1703 CHUNK_SIZE_LF,
1704 CHUNK_BODY,
1705 CHUNK_BODY_CR,
1706 CHUNK_BODY_LF,
1707 CHUNK_TRAILER,
1708 CHUNK_ERROR
1709 } php_chunked_filter_state;
1710
1711 typedef struct _php_chunked_filter_data {
1712 size_t chunk_size;
1713 php_chunked_filter_state state;
1714 int persistent;
1715 } php_chunked_filter_data;
1716
php_dechunk(char * buf,size_t len,php_chunked_filter_data * data)1717 static size_t php_dechunk(char *buf, size_t len, php_chunked_filter_data *data)
1718 {
1719 char *p = buf;
1720 char *end = p + len;
1721 char *out = buf;
1722 size_t out_len = 0;
1723
1724 while (p < end) {
1725 switch (data->state) {
1726 case CHUNK_SIZE_START:
1727 data->chunk_size = 0;
1728 case CHUNK_SIZE:
1729 while (p < end) {
1730 if (*p >= '0' && *p <= '9') {
1731 data->chunk_size = (data->chunk_size * 16) + (*p - '0');
1732 } else if (*p >= 'A' && *p <= 'F') {
1733 data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10);
1734 } else if (*p >= 'a' && *p <= 'f') {
1735 data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10);
1736 } else if (data->state == CHUNK_SIZE_START) {
1737 data->state = CHUNK_ERROR;
1738 break;
1739 } else {
1740 data->state = CHUNK_SIZE_EXT;
1741 break;
1742 }
1743 data->state = CHUNK_SIZE;
1744 p++;
1745 }
1746 if (data->state == CHUNK_ERROR) {
1747 continue;
1748 } else if (p == end) {
1749 return out_len;
1750 }
1751 case CHUNK_SIZE_EXT:
1752 /* skip extension */
1753 while (p < end && *p != '\r' && *p != '\n') {
1754 p++;
1755 }
1756 if (p == end) {
1757 return out_len;
1758 }
1759 /* TODO: Check if Intentional? */
1760 ZEND_FALLTHROUGH;
1761 case CHUNK_SIZE_CR:
1762 if (*p == '\r') {
1763 p++;
1764 if (p == end) {
1765 data->state = CHUNK_SIZE_LF;
1766 return out_len;
1767 }
1768 }
1769 /* TODO: Check if Intentional? */
1770 ZEND_FALLTHROUGH;
1771 case CHUNK_SIZE_LF:
1772 if (*p == '\n') {
1773 p++;
1774 if (data->chunk_size == 0) {
1775 /* last chunk */
1776 data->state = CHUNK_TRAILER;
1777 continue;
1778 } else if (p == end) {
1779 data->state = CHUNK_BODY;
1780 return out_len;
1781 }
1782 } else {
1783 data->state = CHUNK_ERROR;
1784 continue;
1785 }
1786 /* TODO: Check if Intentional? */
1787 ZEND_FALLTHROUGH;
1788 case CHUNK_BODY:
1789 if ((size_t) (end - p) >= data->chunk_size) {
1790 if (p != out) {
1791 memmove(out, p, data->chunk_size);
1792 }
1793 out += data->chunk_size;
1794 out_len += data->chunk_size;
1795 p += data->chunk_size;
1796 if (p == end) {
1797 data->state = CHUNK_BODY_CR;
1798 return out_len;
1799 }
1800 } else {
1801 if (p != out) {
1802 memmove(out, p, end - p);
1803 }
1804 data->chunk_size -= end - p;
1805 data->state=CHUNK_BODY;
1806 out_len += end - p;
1807 return out_len;
1808 }
1809 /* TODO: Check if Intentional? */
1810 ZEND_FALLTHROUGH;
1811 case CHUNK_BODY_CR:
1812 if (*p == '\r') {
1813 p++;
1814 if (p == end) {
1815 data->state = CHUNK_BODY_LF;
1816 return out_len;
1817 }
1818 }
1819 /* TODO: Check if Intentional? */
1820 ZEND_FALLTHROUGH;
1821 case CHUNK_BODY_LF:
1822 if (*p == '\n') {
1823 p++;
1824 data->state = CHUNK_SIZE_START;
1825 continue;
1826 } else {
1827 data->state = CHUNK_ERROR;
1828 continue;
1829 }
1830 case CHUNK_TRAILER:
1831 /* ignore trailer */
1832 p = end;
1833 continue;
1834 case CHUNK_ERROR:
1835 if (p != out) {
1836 memmove(out, p, end - p);
1837 }
1838 out_len += end - p;
1839 return out_len;
1840 }
1841 }
1842 return out_len;
1843 }
1844
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)1845 static php_stream_filter_status_t php_chunked_filter(
1846 php_stream *stream,
1847 php_stream_filter *thisfilter,
1848 php_stream_bucket_brigade *buckets_in,
1849 php_stream_bucket_brigade *buckets_out,
1850 size_t *bytes_consumed,
1851 int flags
1852 )
1853 {
1854 php_stream_bucket *bucket;
1855 size_t consumed = 0;
1856 php_chunked_filter_data *data = (php_chunked_filter_data *) Z_PTR(thisfilter->abstract);
1857
1858 while (buckets_in->head) {
1859 bucket = php_stream_bucket_make_writeable(buckets_in->head);
1860 consumed += bucket->buflen;
1861 bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, data);
1862 php_stream_bucket_append(buckets_out, bucket);
1863 }
1864
1865 if (bytes_consumed) {
1866 *bytes_consumed = consumed;
1867 }
1868
1869 return PSFS_PASS_ON;
1870 }
1871
php_chunked_dtor(php_stream_filter * thisfilter)1872 static void php_chunked_dtor(php_stream_filter *thisfilter)
1873 {
1874 if (thisfilter && Z_PTR(thisfilter->abstract)) {
1875 php_chunked_filter_data *data = (php_chunked_filter_data *) Z_PTR(thisfilter->abstract);
1876 pefree(data, data->persistent);
1877 }
1878 }
1879
1880 static const php_stream_filter_ops chunked_filter_ops = {
1881 php_chunked_filter,
1882 php_chunked_dtor,
1883 "dechunk"
1884 };
1885
chunked_filter_create(const char * filtername,zval * filterparams,uint8_t persistent)1886 static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, uint8_t persistent)
1887 {
1888 const php_stream_filter_ops *fops = NULL;
1889 php_chunked_filter_data *data;
1890
1891 if (strcasecmp(filtername, "dechunk")) {
1892 return NULL;
1893 }
1894
1895 /* Create this filter */
1896 data = (php_chunked_filter_data *)pecalloc(1, sizeof(php_chunked_filter_data), persistent);
1897 data->state = CHUNK_SIZE_START;
1898 data->chunk_size = 0;
1899 data->persistent = persistent;
1900 fops = &chunked_filter_ops;
1901
1902 return php_stream_filter_alloc(fops, data, persistent);
1903 }
1904
1905 static const php_stream_filter_factory chunked_filter_factory = {
1906 chunked_filter_create
1907 };
1908 /* }}} */
1909
1910 static const struct {
1911 const php_stream_filter_ops *ops;
1912 const php_stream_filter_factory *factory;
1913 } standard_filters[] = {
1914 { &strfilter_rot13_ops, &strfilter_rot13_factory },
1915 { &strfilter_toupper_ops, &strfilter_toupper_factory },
1916 { &strfilter_tolower_ops, &strfilter_tolower_factory },
1917 { &strfilter_convert_ops, &strfilter_convert_factory },
1918 { &consumed_filter_ops, &consumed_filter_factory },
1919 { &chunked_filter_ops, &chunked_filter_factory },
1920 /* additional filters to go here */
1921 { NULL, NULL }
1922 };
1923
1924 /* {{{ filter MINIT and MSHUTDOWN */
PHP_MINIT_FUNCTION(standard_filters)1925 PHP_MINIT_FUNCTION(standard_filters)
1926 {
1927 int i;
1928
1929 for (i = 0; standard_filters[i].ops; i++) {
1930 if (FAILURE == php_stream_filter_register_factory(
1931 standard_filters[i].ops->label,
1932 standard_filters[i].factory
1933 )) {
1934 return FAILURE;
1935 }
1936 }
1937 return SUCCESS;
1938 }
1939
PHP_MSHUTDOWN_FUNCTION(standard_filters)1940 PHP_MSHUTDOWN_FUNCTION(standard_filters)
1941 {
1942 int i;
1943
1944 for (i = 0; standard_filters[i].ops; i++) {
1945 php_stream_filter_unregister_factory(standard_filters[i].ops->label);
1946 }
1947 return SUCCESS;
1948 }
1949 /* }}} */
1950