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