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