1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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)) |
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 | |
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 | |
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 | unsigned int trail_ws; |
795 | int opts; |
796 | static char qp_digits[] = "0123456789ABCDEF" ; |
797 | |
798 | line_ccnt = inst->line_ccnt; |
799 | opts = inst->opts; |
800 | lb_ptr = inst->lb_ptr; |
801 | lb_cnt = inst->lb_cnt; |
802 | |
803 | if ((in_pp == NULL || in_left_p == NULL) && (lb_ptr >=lb_cnt)) { |
804 | return PHP_CONV_ERR_SUCCESS; |
805 | } |
806 | |
807 | ps = (unsigned char *)(*in_pp); |
808 | icnt = *in_left_p; |
809 | pd = (unsigned char *)(*out_pp); |
810 | ocnt = *out_left_p; |
811 | trail_ws = 0; |
812 | |
813 | for (;;) { |
814 | if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) { |
815 | /* look ahead for the line break chars to make a right decision |
816 | * how to consume incoming characters */ |
817 | |
818 | if (icnt > 0 && *ps == inst->lbchars[lb_cnt]) { |
819 | lb_cnt++; |
820 | |
821 | if (lb_cnt >= inst->lbchars_len) { |
822 | unsigned int i; |
823 | |
824 | if (ocnt < lb_cnt) { |
825 | lb_cnt--; |
826 | err = PHP_CONV_ERR_TOO_BIG; |
827 | break; |
828 | } |
829 | |
830 | for (i = 0; i < lb_cnt; i++) { |
831 | *(pd++) = inst->lbchars[i]; |
832 | ocnt--; |
833 | } |
834 | line_ccnt = inst->line_len; |
835 | lb_ptr = lb_cnt = 0; |
836 | } |
837 | ps++, icnt--; |
838 | continue; |
839 | } |
840 | } |
841 | |
842 | if (lb_ptr >= lb_cnt && icnt <= 0) { |
843 | break; |
844 | } |
845 | |
846 | c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars); |
847 | |
848 | if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && |
849 | (trail_ws == 0) && |
850 | (c == '\t' || c == ' ')) { |
851 | if (line_ccnt < 2 && inst->lbchars != NULL) { |
852 | if (ocnt < inst->lbchars_len + 1) { |
853 | err = PHP_CONV_ERR_TOO_BIG; |
854 | break; |
855 | } |
856 | |
857 | *(pd++) = '='; |
858 | ocnt--; |
859 | line_ccnt--; |
860 | |
861 | memcpy(pd, inst->lbchars, inst->lbchars_len); |
862 | pd += inst->lbchars_len; |
863 | ocnt -= inst->lbchars_len; |
864 | line_ccnt = inst->line_len; |
865 | } else { |
866 | if (ocnt < 1) { |
867 | err = PHP_CONV_ERR_TOO_BIG; |
868 | break; |
869 | } |
870 | |
871 | /* Check to see if this is EOL whitespace. */ |
872 | if (inst->lbchars != NULL) { |
873 | unsigned char *ps2; |
874 | unsigned int j, lb_cnt2; |
875 | |
876 | lb_cnt2 = 0; |
877 | ps2 = ps; |
878 | trail_ws = 1; |
879 | |
880 | for (j = icnt - 1; j > 0; j--, ps2++) { |
881 | if (*ps2 == inst->lbchars[lb_cnt2]) { |
882 | lb_cnt2++; |
883 | if (lb_cnt2 >= inst->lbchars_len) { |
884 | /* Found trailing ws. Reset to top of main |
885 | * for loop to allow for code to do necessary |
886 | * wrapping/encoding. */ |
887 | break; |
888 | } |
889 | } else if (lb_cnt2 != 0 || (*ps2 != '\t' && *ps2 != ' ')) { |
890 | /* At least one non-EOL character following, so |
891 | * don't need to encode ws. */ |
892 | trail_ws = 0; |
893 | break; |
894 | } else { |
895 | trail_ws++; |
896 | } |
897 | } |
898 | } |
899 | |
900 | if (trail_ws == 0) { |
901 | *(pd++) = c; |
902 | ocnt--; |
903 | line_ccnt--; |
904 | CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); |
905 | } |
906 | } |
907 | } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { |
908 | if (line_ccnt < 2 && inst->lbchars != NULL) { |
909 | if (ocnt < inst->lbchars_len + 1) { |
910 | err = PHP_CONV_ERR_TOO_BIG; |
911 | break; |
912 | } |
913 | *(pd++) = '='; |
914 | ocnt--; |
915 | line_ccnt--; |
916 | |
917 | memcpy(pd, inst->lbchars, inst->lbchars_len); |
918 | pd += inst->lbchars_len; |
919 | ocnt -= inst->lbchars_len; |
920 | line_ccnt = inst->line_len; |
921 | } |
922 | if (ocnt < 1) { |
923 | err = PHP_CONV_ERR_TOO_BIG; |
924 | break; |
925 | } |
926 | *(pd++) = c; |
927 | ocnt--; |
928 | line_ccnt--; |
929 | CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); |
930 | } else { |
931 | if (line_ccnt < 4) { |
932 | if (ocnt < inst->lbchars_len + 1) { |
933 | err = PHP_CONV_ERR_TOO_BIG; |
934 | break; |
935 | } |
936 | *(pd++) = '='; |
937 | ocnt--; |
938 | line_ccnt--; |
939 | |
940 | memcpy(pd, inst->lbchars, inst->lbchars_len); |
941 | pd += inst->lbchars_len; |
942 | ocnt -= inst->lbchars_len; |
943 | line_ccnt = inst->line_len; |
944 | } |
945 | if (ocnt < 3) { |
946 | err = PHP_CONV_ERR_TOO_BIG; |
947 | break; |
948 | } |
949 | *(pd++) = '='; |
950 | *(pd++) = qp_digits[(c >> 4)]; |
951 | *(pd++) = qp_digits[(c & 0x0f)]; |
952 | ocnt -= 3; |
953 | line_ccnt -= 3; |
954 | if (trail_ws > 0) { |
955 | trail_ws--; |
956 | } |
957 | CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); |
958 | } |
959 | } |
960 | |
961 | *in_pp = (const char *)ps; |
962 | *in_left_p = icnt; |
963 | *out_pp = (char *)pd; |
964 | *out_left_p = ocnt; |
965 | inst->line_ccnt = line_ccnt; |
966 | inst->lb_ptr = lb_ptr; |
967 | inst->lb_cnt = lb_cnt; |
968 | return err; |
969 | } |
970 | #undef NEXT_CHAR |
971 | #undef CONSUME_CHAR |
972 | |
973 | 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) |
974 | { |
975 | if (line_len < 4 && lbchars != NULL) { |
976 | return PHP_CONV_ERR_TOO_BIG; |
977 | } |
978 | inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_encode_convert; |
979 | inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_encode_dtor; |
980 | inst->line_ccnt = line_len; |
981 | inst->line_len = line_len; |
982 | if (lbchars != NULL) { |
983 | inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars); |
984 | inst->lbchars_len = lbchars_len; |
985 | } else { |
986 | inst->lbchars = NULL; |
987 | } |
988 | inst->lbchars_dup = lbchars_dup; |
989 | inst->persistent = persistent; |
990 | inst->opts = opts; |
991 | inst->lb_cnt = inst->lb_ptr = 0; |
992 | return PHP_CONV_ERR_SUCCESS; |
993 | } |
994 | /* }}} */ |
995 | |
996 | /* {{{ php_conv_qprint_decode */ |
997 | typedef struct _php_conv_qprint_decode { |
998 | php_conv _super; |
999 | |
1000 | int scan_stat; |
1001 | unsigned int next_char; |
1002 | const char *lbchars; |
1003 | int lbchars_dup; |
1004 | size_t lbchars_len; |
1005 | int persistent; |
1006 | unsigned int lb_ptr; |
1007 | unsigned int lb_cnt; |
1008 | } php_conv_qprint_decode; |
1009 | |
1010 | static void php_conv_qprint_decode_dtor(php_conv_qprint_decode *inst) |
1011 | { |
1012 | assert(inst != NULL); |
1013 | if (inst->lbchars_dup && inst->lbchars != NULL) { |
1014 | pefree((void *)inst->lbchars, inst->persistent); |
1015 | } |
1016 | } |
1017 | |
1018 | 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) |
1019 | { |
1020 | php_conv_err_t err = PHP_CONV_ERR_SUCCESS; |
1021 | size_t icnt, ocnt; |
1022 | unsigned char *ps, *pd; |
1023 | unsigned int scan_stat; |
1024 | unsigned int next_char; |
1025 | unsigned int lb_ptr, lb_cnt; |
1026 | |
1027 | lb_ptr = inst->lb_ptr; |
1028 | lb_cnt = inst->lb_cnt; |
1029 | |
1030 | if ((in_pp == NULL || in_left_p == NULL) && lb_cnt == lb_ptr) { |
1031 | if (inst->scan_stat != 0) { |
1032 | return PHP_CONV_ERR_UNEXPECTED_EOS; |
1033 | } |
1034 | return PHP_CONV_ERR_SUCCESS; |
1035 | } |
1036 | |
1037 | ps = (unsigned char *)(*in_pp); |
1038 | icnt = *in_left_p; |
1039 | pd = (unsigned char *)(*out_pp); |
1040 | ocnt = *out_left_p; |
1041 | scan_stat = inst->scan_stat; |
1042 | next_char = inst->next_char; |
1043 | |
1044 | for (;;) { |
1045 | switch (scan_stat) { |
1046 | case 0: { |
1047 | if (icnt <= 0) { |
1048 | goto out; |
1049 | } |
1050 | if (*ps == '=') { |
1051 | scan_stat = 1; |
1052 | } else { |
1053 | if (ocnt < 1) { |
1054 | err = PHP_CONV_ERR_TOO_BIG; |
1055 | goto out; |
1056 | } |
1057 | *(pd++) = *ps; |
1058 | ocnt--; |
1059 | } |
1060 | ps++, icnt--; |
1061 | } break; |
1062 | |
1063 | case 1: { |
1064 | if (icnt <= 0) { |
1065 | goto out; |
1066 | } |
1067 | if (*ps == ' ' || *ps == '\t') { |
1068 | scan_stat = 4; |
1069 | ps++, icnt--; |
1070 | break; |
1071 | } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\r') { |
1072 | /* auto-detect line endings, looks like network line ending \r\n (could be mac \r) */ |
1073 | lb_cnt++; |
1074 | scan_stat = 5; |
1075 | ps++, icnt--; |
1076 | break; |
1077 | } else if (!inst->lbchars && lb_cnt == 0 && *ps == '\n') { |
1078 | /* auto-detect line endings, looks like unix-lineendings, not to spec, but it is seem in the wild, a lot */ |
1079 | lb_cnt = lb_ptr = 0; |
1080 | scan_stat = 0; |
1081 | ps++, icnt--; |
1082 | break; |
1083 | } else if (lb_cnt < inst->lbchars_len && |
1084 | *ps == (unsigned char)inst->lbchars[lb_cnt]) { |
1085 | lb_cnt++; |
1086 | scan_stat = 5; |
1087 | ps++, icnt--; |
1088 | break; |
1089 | } |
1090 | } /* break is missing intentionally */ |
1091 | |
1092 | case 2: { |
1093 | if (icnt <= 0) { |
1094 | goto out; |
1095 | } |
1096 | |
1097 | if (!isxdigit((int) *ps)) { |
1098 | err = PHP_CONV_ERR_INVALID_SEQ; |
1099 | goto out; |
1100 | } |
1101 | next_char = (next_char << 4) | (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30); |
1102 | scan_stat++; |
1103 | ps++, icnt--; |
1104 | if (scan_stat != 3) { |
1105 | break; |
1106 | } |
1107 | } /* break is missing intentionally */ |
1108 | |
1109 | case 3: { |
1110 | if (ocnt < 1) { |
1111 | err = PHP_CONV_ERR_TOO_BIG; |
1112 | goto out; |
1113 | } |
1114 | *(pd++) = next_char; |
1115 | ocnt--; |
1116 | scan_stat = 0; |
1117 | } break; |
1118 | |
1119 | case 4: { |
1120 | if (icnt <= 0) { |
1121 | goto out; |
1122 | } |
1123 | if (lb_cnt < inst->lbchars_len && |
1124 | *ps == (unsigned char)inst->lbchars[lb_cnt]) { |
1125 | lb_cnt++; |
1126 | scan_stat = 5; |
1127 | } |
1128 | if (*ps != '\t' && *ps != ' ') { |
1129 | err = PHP_CONV_ERR_INVALID_SEQ; |
1130 | goto out; |
1131 | } |
1132 | ps++, icnt--; |
1133 | } break; |
1134 | |
1135 | case 5: { |
1136 | if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') { |
1137 | /* auto-detect soft line breaks, found network line break */ |
1138 | lb_cnt = lb_ptr = 0; |
1139 | scan_stat = 0; |
1140 | ps++, icnt--; /* consume \n */ |
1141 | } else if (!inst->lbchars && lb_cnt > 0) { |
1142 | /* auto-detect soft line breaks, found mac line break */ |
1143 | lb_cnt = lb_ptr = 0; |
1144 | scan_stat = 0; |
1145 | } else if (lb_cnt >= inst->lbchars_len) { |
1146 | /* soft line break */ |
1147 | lb_cnt = lb_ptr = 0; |
1148 | scan_stat = 0; |
1149 | } else if (icnt > 0) { |
1150 | if (*ps == (unsigned char)inst->lbchars[lb_cnt]) { |
1151 | lb_cnt++; |
1152 | ps++, icnt--; |
1153 | } else { |
1154 | scan_stat = 6; /* no break for short-cut */ |
1155 | } |
1156 | } else { |
1157 | goto out; |
1158 | } |
1159 | } break; |
1160 | |
1161 | case 6: { |
1162 | if (lb_ptr < lb_cnt) { |
1163 | if (ocnt < 1) { |
1164 | err = PHP_CONV_ERR_TOO_BIG; |
1165 | goto out; |
1166 | } |
1167 | *(pd++) = inst->lbchars[lb_ptr++]; |
1168 | ocnt--; |
1169 | } else { |
1170 | scan_stat = 0; |
1171 | lb_cnt = lb_ptr = 0; |
1172 | } |
1173 | } break; |
1174 | } |
1175 | } |
1176 | out: |
1177 | *in_pp = (const char *)ps; |
1178 | *in_left_p = icnt; |
1179 | *out_pp = (char *)pd; |
1180 | *out_left_p = ocnt; |
1181 | inst->scan_stat = scan_stat; |
1182 | inst->lb_ptr = lb_ptr; |
1183 | inst->lb_cnt = lb_cnt; |
1184 | inst->next_char = next_char; |
1185 | |
1186 | return err; |
1187 | } |
1188 | 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) |
1189 | { |
1190 | inst->_super.convert_op = (php_conv_convert_func) php_conv_qprint_decode_convert; |
1191 | inst->_super.dtor = (php_conv_dtor_func) php_conv_qprint_decode_dtor; |
1192 | inst->scan_stat = 0; |
1193 | inst->next_char = 0; |
1194 | inst->lb_ptr = inst->lb_cnt = 0; |
1195 | if (lbchars != NULL) { |
1196 | inst->lbchars = (lbchars_dup ? pestrdup(lbchars, persistent) : lbchars); |
1197 | inst->lbchars_len = lbchars_len; |
1198 | } else { |
1199 | inst->lbchars = NULL; |
1200 | inst->lbchars_len = 0; |
1201 | } |
1202 | inst->lbchars_dup = lbchars_dup; |
1203 | inst->persistent = persistent; |
1204 | return PHP_CONV_ERR_SUCCESS; |
1205 | } |
1206 | /* }}} */ |
1207 | |
1208 | typedef struct _php_convert_filter { |
1209 | php_conv *cd; |
1210 | int persistent; |
1211 | char *filtername; |
1212 | char stub[128]; |
1213 | size_t stub_len; |
1214 | } php_convert_filter; |
1215 | |
1216 | #define PHP_CONV_BASE64_ENCODE 1 |
1217 | #define PHP_CONV_BASE64_DECODE 2 |
1218 | #define PHP_CONV_QPRINT_ENCODE 3 |
1219 | #define PHP_CONV_QPRINT_DECODE 4 |
1220 | |
1221 | 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) |
1222 | { |
1223 | zval **tmpval; |
1224 | |
1225 | *pretval = NULL; |
1226 | *pretval_len = 0; |
1227 | |
1228 | if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { |
1229 | if (Z_TYPE_PP(tmpval) != IS_STRING) { |
1230 | zval zt = **tmpval; |
1231 | |
1232 | convert_to_string(&zt); |
1233 | |
1234 | if (NULL == (*pretval = pemalloc(Z_STRLEN(zt) + 1, persistent))) { |
1235 | return PHP_CONV_ERR_ALLOC; |
1236 | } |
1237 | |
1238 | *pretval_len = Z_STRLEN(zt); |
1239 | memcpy(*pretval, Z_STRVAL(zt), Z_STRLEN(zt) + 1); |
1240 | zval_dtor(&zt); |
1241 | } else { |
1242 | if (NULL == (*pretval = pemalloc(Z_STRLEN_PP(tmpval) + 1, persistent))) { |
1243 | return PHP_CONV_ERR_ALLOC; |
1244 | } |
1245 | *pretval_len = Z_STRLEN_PP(tmpval); |
1246 | memcpy(*pretval, Z_STRVAL_PP(tmpval), Z_STRLEN_PP(tmpval) + 1); |
1247 | } |
1248 | } else { |
1249 | return PHP_CONV_ERR_NOT_FOUND; |
1250 | } |
1251 | return PHP_CONV_ERR_SUCCESS; |
1252 | } |
1253 | |
1254 | #if IT_WAS_USED |
1255 | static php_conv_err_t php_conv_get_long_prop_ex(const HashTable *ht, long *pretval, char *field_name, size_t field_name_len) |
1256 | { |
1257 | zval **tmpval; |
1258 | |
1259 | *pretval = 0; |
1260 | |
1261 | if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { |
1262 | zval tmp, *ztval = *tmpval; |
1263 | |
1264 | if (Z_TYPE_PP(tmpval) != IS_LONG) { |
1265 | tmp = *ztval; |
1266 | zval_copy_ctor(&tmp); |
1267 | convert_to_long(&tmp); |
1268 | ztval = &tmp; |
1269 | } |
1270 | *pretval = Z_LVAL_P(ztval); |
1271 | } else { |
1272 | return PHP_CONV_ERR_NOT_FOUND; |
1273 | } |
1274 | return PHP_CONV_ERR_SUCCESS; |
1275 | } |
1276 | #endif |
1277 | |
1278 | 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) |
1279 | { |
1280 | zval **tmpval; |
1281 | |
1282 | *pretval = 0; |
1283 | |
1284 | if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { |
1285 | zval tmp, *ztval = *tmpval; |
1286 | |
1287 | if (Z_TYPE_PP(tmpval) != IS_LONG) { |
1288 | tmp = *ztval; |
1289 | zval_copy_ctor(&tmp); |
1290 | convert_to_long(&tmp); |
1291 | ztval = &tmp; |
1292 | } |
1293 | if (Z_LVAL_P(ztval) < 0) { |
1294 | *pretval = 0; |
1295 | } else { |
1296 | *pretval = Z_LVAL_P(ztval); |
1297 | } |
1298 | } else { |
1299 | return PHP_CONV_ERR_NOT_FOUND; |
1300 | } |
1301 | return PHP_CONV_ERR_SUCCESS; |
1302 | } |
1303 | |
1304 | static php_conv_err_t php_conv_get_bool_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len) |
1305 | { |
1306 | zval **tmpval; |
1307 | |
1308 | *pretval = 0; |
1309 | |
1310 | if (zend_hash_find((HashTable *)ht, field_name, field_name_len, (void **)&tmpval) == SUCCESS) { |
1311 | zval tmp, *ztval = *tmpval; |
1312 | |
1313 | if (Z_TYPE_PP(tmpval) != IS_BOOL) { |
1314 | tmp = *ztval; |
1315 | zval_copy_ctor(&tmp); |
1316 | convert_to_boolean(&tmp); |
1317 | ztval = &tmp; |
1318 | } |
1319 | *pretval = Z_BVAL_P(ztval); |
1320 | } else { |
1321 | return PHP_CONV_ERR_NOT_FOUND; |
1322 | } |
1323 | return PHP_CONV_ERR_SUCCESS; |
1324 | } |
1325 | |
1326 | |
1327 | #if IT_WAS_USED |
1328 | static int php_conv_get_int_prop_ex(const HashTable *ht, int *pretval, char *field_name, size_t field_name_len) |
1329 | { |
1330 | long l; |
1331 | php_conv_err_t err; |
1332 | |
1333 | *pretval = 0; |
1334 | |
1335 | if ((err = php_conv_get_long_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) { |
1336 | *pretval = l; |
1337 | } |
1338 | return err; |
1339 | } |
1340 | #endif |
1341 | |
1342 | static int php_conv_get_uint_prop_ex(const HashTable *ht, unsigned int *pretval, char *field_name, size_t field_name_len) |
1343 | { |
1344 | long l; |
1345 | php_conv_err_t err; |
1346 | |
1347 | *pretval = 0; |
1348 | |
1349 | if ((err = php_conv_get_ulong_prop_ex(ht, &l, field_name, field_name_len)) == PHP_CONV_ERR_SUCCESS) { |
1350 | *pretval = l; |
1351 | } |
1352 | return err; |
1353 | } |
1354 | |
1355 | #define GET_STR_PROP(ht, var, var_len, fldname, persistent) \ |
1356 | php_conv_get_string_prop_ex(ht, &var, &var_len, fldname, sizeof(fldname), persistent) |
1357 | |
1358 | #define GET_INT_PROP(ht, var, fldname) \ |
1359 | php_conv_get_int_prop_ex(ht, &var, fldname, sizeof(fldname)) |
1360 | |
1361 | #define GET_UINT_PROP(ht, var, fldname) \ |
1362 | php_conv_get_uint_prop_ex(ht, &var, fldname, sizeof(fldname)) |
1363 | |
1364 | #define GET_BOOL_PROP(ht, var, fldname) \ |
1365 | php_conv_get_bool_prop_ex(ht, &var, fldname, sizeof(fldname)) |
1366 | |
1367 | static php_conv *php_conv_open(int conv_mode, const HashTable *options, int persistent) |
1368 | { |
1369 | /* FIXME: I'll have to replace this ugly code by something neat |
1370 | (factories?) in the near future. */ |
1371 | php_conv *retval = NULL; |
1372 | |
1373 | switch (conv_mode) { |
1374 | case PHP_CONV_BASE64_ENCODE: { |
1375 | unsigned int line_len = 0; |
1376 | char *lbchars = NULL; |
1377 | size_t lbchars_len; |
1378 | |
1379 | if (options != NULL) { |
1380 | GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars" , 0); |
1381 | GET_UINT_PROP(options, line_len, "line-length" ); |
1382 | if (line_len < 4) { |
1383 | if (lbchars != NULL) { |
1384 | pefree(lbchars, 0); |
1385 | } |
1386 | lbchars = NULL; |
1387 | } else { |
1388 | if (lbchars == NULL) { |
1389 | lbchars = pestrdup("\r\n" , 0); |
1390 | lbchars_len = 2; |
1391 | } |
1392 | } |
1393 | } |
1394 | retval = pemalloc(sizeof(php_conv_base64_encode), persistent); |
1395 | if (lbchars != NULL) { |
1396 | if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, line_len, lbchars, lbchars_len, 1, persistent)) { |
1397 | if (lbchars != NULL) { |
1398 | pefree(lbchars, 0); |
1399 | } |
1400 | goto out_failure; |
1401 | } |
1402 | pefree(lbchars, 0); |
1403 | } else { |
1404 | if (php_conv_base64_encode_ctor((php_conv_base64_encode *)retval, 0, NULL, 0, 0, persistent)) { |
1405 | goto out_failure; |
1406 | } |
1407 | } |
1408 | } break; |
1409 | |
1410 | case PHP_CONV_BASE64_DECODE: |
1411 | retval = pemalloc(sizeof(php_conv_base64_decode), persistent); |
1412 | if (php_conv_base64_decode_ctor((php_conv_base64_decode *)retval)) { |
1413 | goto out_failure; |
1414 | } |
1415 | break; |
1416 | |
1417 | case PHP_CONV_QPRINT_ENCODE: { |
1418 | unsigned int line_len = 0; |
1419 | char *lbchars = NULL; |
1420 | size_t lbchars_len; |
1421 | int opts = 0; |
1422 | |
1423 | if (options != NULL) { |
1424 | int opt_binary = 0; |
1425 | int opt_force_encode_first = 0; |
1426 | |
1427 | GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars" , 0); |
1428 | GET_UINT_PROP(options, line_len, "line-length" ); |
1429 | GET_BOOL_PROP(options, opt_binary, "binary" ); |
1430 | GET_BOOL_PROP(options, opt_force_encode_first, "force-encode-first" ); |
1431 | |
1432 | if (line_len < 4) { |
1433 | if (lbchars != NULL) { |
1434 | pefree(lbchars, 0); |
1435 | } |
1436 | lbchars = NULL; |
1437 | } else { |
1438 | if (lbchars == NULL) { |
1439 | lbchars = pestrdup("\r\n" , 0); |
1440 | lbchars_len = 2; |
1441 | } |
1442 | } |
1443 | opts |= (opt_binary ? PHP_CONV_QPRINT_OPT_BINARY : 0); |
1444 | opts |= (opt_force_encode_first ? PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST : 0); |
1445 | } |
1446 | retval = pemalloc(sizeof(php_conv_qprint_encode), persistent); |
1447 | if (lbchars != NULL) { |
1448 | if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, line_len, lbchars, lbchars_len, 1, opts, persistent)) { |
1449 | pefree(lbchars, 0); |
1450 | goto out_failure; |
1451 | } |
1452 | pefree(lbchars, 0); |
1453 | } else { |
1454 | if (php_conv_qprint_encode_ctor((php_conv_qprint_encode *)retval, 0, NULL, 0, 0, opts, persistent)) { |
1455 | goto out_failure; |
1456 | } |
1457 | } |
1458 | } break; |
1459 | |
1460 | case PHP_CONV_QPRINT_DECODE: { |
1461 | char *lbchars = NULL; |
1462 | size_t lbchars_len; |
1463 | |
1464 | if (options != NULL) { |
1465 | /* If line-break-chars are not specified, filter will attempt to detect line endings (\r, \n, or \r\n) */ |
1466 | GET_STR_PROP(options, lbchars, lbchars_len, "line-break-chars" , 0); |
1467 | } |
1468 | |
1469 | retval = pemalloc(sizeof(php_conv_qprint_decode), persistent); |
1470 | if (lbchars != NULL) { |
1471 | if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, lbchars, lbchars_len, 1, persistent)) { |
1472 | pefree(lbchars, 0); |
1473 | goto out_failure; |
1474 | } |
1475 | pefree(lbchars, 0); |
1476 | } else { |
1477 | if (php_conv_qprint_decode_ctor((php_conv_qprint_decode *)retval, NULL, 0, 0, persistent)) { |
1478 | goto out_failure; |
1479 | } |
1480 | } |
1481 | } break; |
1482 | |
1483 | default: |
1484 | retval = NULL; |
1485 | break; |
1486 | } |
1487 | return retval; |
1488 | |
1489 | out_failure: |
1490 | if (retval != NULL) { |
1491 | pefree(retval, persistent); |
1492 | } |
1493 | return NULL; |
1494 | } |
1495 | |
1496 | #undef GET_STR_PROP |
1497 | #undef GET_INT_PROP |
1498 | #undef GET_UINT_PROP |
1499 | #undef GET_BOOL_PROP |
1500 | |
1501 | static int php_convert_filter_ctor(php_convert_filter *inst, |
1502 | int conv_mode, HashTable *conv_opts, |
1503 | const char *filtername, int persistent) |
1504 | { |
1505 | inst->persistent = persistent; |
1506 | inst->filtername = pestrdup(filtername, persistent); |
1507 | inst->stub_len = 0; |
1508 | |
1509 | if ((inst->cd = php_conv_open(conv_mode, conv_opts, persistent)) == NULL) { |
1510 | goto out_failure; |
1511 | } |
1512 | |
1513 | return SUCCESS; |
1514 | |
1515 | out_failure: |
1516 | if (inst->cd != NULL) { |
1517 | php_conv_dtor(inst->cd); |
1518 | pefree(inst->cd, persistent); |
1519 | } |
1520 | if (inst->filtername != NULL) { |
1521 | pefree(inst->filtername, persistent); |
1522 | } |
1523 | return FAILURE; |
1524 | } |
1525 | |
1526 | static void php_convert_filter_dtor(php_convert_filter *inst) |
1527 | { |
1528 | if (inst->cd != NULL) { |
1529 | php_conv_dtor(inst->cd); |
1530 | pefree(inst->cd, inst->persistent); |
1531 | } |
1532 | |
1533 | if (inst->filtername != NULL) { |
1534 | pefree(inst->filtername, inst->persistent); |
1535 | } |
1536 | } |
1537 | |
1538 | /* {{{ strfilter_convert_append_bucket */ |
1539 | static int strfilter_convert_append_bucket( |
1540 | php_convert_filter *inst, |
1541 | php_stream *stream, php_stream_filter *filter, |
1542 | php_stream_bucket_brigade *buckets_out, |
1543 | const char *ps, size_t buf_len, size_t *consumed, |
1544 | int persistent TSRMLS_DC) |
1545 | { |
1546 | php_conv_err_t err; |
1547 | php_stream_bucket *new_bucket; |
1548 | char *out_buf = NULL; |
1549 | size_t out_buf_size; |
1550 | char *pd; |
1551 | const char *pt; |
1552 | size_t ocnt, icnt, tcnt; |
1553 | size_t initial_out_buf_size; |
1554 | |
1555 | if (ps == NULL) { |
1556 | initial_out_buf_size = 64; |
1557 | icnt = 1; |
1558 | } else { |
1559 | initial_out_buf_size = buf_len; |
1560 | icnt = buf_len; |
1561 | } |
1562 | |
1563 | out_buf_size = ocnt = initial_out_buf_size; |
1564 | if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { |
1565 | return FAILURE; |
1566 | } |
1567 | |
1568 | pd = out_buf; |
1569 | |
1570 | if (inst->stub_len > 0) { |
1571 | pt = inst->stub; |
1572 | tcnt = inst->stub_len; |
1573 | |
1574 | while (tcnt > 0) { |
1575 | err = php_conv_convert(inst->cd, &pt, &tcnt, &pd, &ocnt); |
1576 | |
1577 | switch (err) { |
1578 | case PHP_CONV_ERR_INVALID_SEQ: |
1579 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence" , inst->filtername); |
1580 | goto out_failure; |
1581 | |
1582 | case PHP_CONV_ERR_MORE: |
1583 | if (ps != NULL) { |
1584 | if (icnt > 0) { |
1585 | if (inst->stub_len >= sizeof(inst->stub)) { |
1586 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer" , inst->filtername); |
1587 | goto out_failure; |
1588 | } |
1589 | inst->stub[inst->stub_len++] = *(ps++); |
1590 | icnt--; |
1591 | pt = inst->stub; |
1592 | tcnt = inst->stub_len; |
1593 | } else { |
1594 | tcnt = 0; |
1595 | break; |
1596 | } |
1597 | } |
1598 | break; |
1599 | |
1600 | case PHP_CONV_ERR_UNEXPECTED_EOS: |
1601 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected end of stream" , inst->filtername); |
1602 | goto out_failure; |
1603 | |
1604 | case PHP_CONV_ERR_TOO_BIG: { |
1605 | char *new_out_buf; |
1606 | size_t new_out_buf_size; |
1607 | |
1608 | new_out_buf_size = out_buf_size << 1; |
1609 | |
1610 | if (new_out_buf_size < out_buf_size) { |
1611 | /* whoa! no bigger buckets are sold anywhere... */ |
1612 | if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { |
1613 | goto out_failure; |
1614 | } |
1615 | |
1616 | php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); |
1617 | |
1618 | out_buf_size = ocnt = initial_out_buf_size; |
1619 | if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { |
1620 | return FAILURE; |
1621 | } |
1622 | pd = out_buf; |
1623 | } else { |
1624 | if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) { |
1625 | if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { |
1626 | goto out_failure; |
1627 | } |
1628 | |
1629 | php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); |
1630 | return FAILURE; |
1631 | } |
1632 | |
1633 | pd = new_out_buf + (pd - out_buf); |
1634 | ocnt += (new_out_buf_size - out_buf_size); |
1635 | out_buf = new_out_buf; |
1636 | out_buf_size = new_out_buf_size; |
1637 | } |
1638 | } break; |
1639 | |
1640 | case PHP_CONV_ERR_UNKNOWN: |
1641 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error" , inst->filtername); |
1642 | goto out_failure; |
1643 | |
1644 | default: |
1645 | break; |
1646 | } |
1647 | } |
1648 | memmove(inst->stub, pt, tcnt); |
1649 | inst->stub_len = tcnt; |
1650 | } |
1651 | |
1652 | while (icnt > 0) { |
1653 | err = ((ps == NULL ? php_conv_convert(inst->cd, NULL, NULL, &pd, &ocnt): |
1654 | php_conv_convert(inst->cd, &ps, &icnt, &pd, &ocnt))); |
1655 | switch (err) { |
1656 | case PHP_CONV_ERR_INVALID_SEQ: |
1657 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid byte sequence" , inst->filtername); |
1658 | goto out_failure; |
1659 | |
1660 | case PHP_CONV_ERR_MORE: |
1661 | if (ps != NULL) { |
1662 | if (icnt > sizeof(inst->stub)) { |
1663 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): insufficient buffer" , inst->filtername); |
1664 | goto out_failure; |
1665 | } |
1666 | memcpy(inst->stub, ps, icnt); |
1667 | inst->stub_len = icnt; |
1668 | ps += icnt; |
1669 | icnt = 0; |
1670 | } else { |
1671 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unexpected octet values" , inst->filtername); |
1672 | goto out_failure; |
1673 | } |
1674 | break; |
1675 | |
1676 | case PHP_CONV_ERR_TOO_BIG: { |
1677 | char *new_out_buf; |
1678 | size_t new_out_buf_size; |
1679 | |
1680 | new_out_buf_size = out_buf_size << 1; |
1681 | |
1682 | if (new_out_buf_size < out_buf_size) { |
1683 | /* whoa! no bigger buckets are sold anywhere... */ |
1684 | if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { |
1685 | goto out_failure; |
1686 | } |
1687 | |
1688 | php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); |
1689 | |
1690 | out_buf_size = ocnt = initial_out_buf_size; |
1691 | if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) { |
1692 | return FAILURE; |
1693 | } |
1694 | pd = out_buf; |
1695 | } else { |
1696 | if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) { |
1697 | if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { |
1698 | goto out_failure; |
1699 | } |
1700 | |
1701 | php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); |
1702 | return FAILURE; |
1703 | } |
1704 | pd = new_out_buf + (pd - out_buf); |
1705 | ocnt += (new_out_buf_size - out_buf_size); |
1706 | out_buf = new_out_buf; |
1707 | out_buf_size = new_out_buf_size; |
1708 | } |
1709 | } break; |
1710 | |
1711 | case PHP_CONV_ERR_UNKNOWN: |
1712 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): unknown error" , inst->filtername); |
1713 | goto out_failure; |
1714 | |
1715 | default: |
1716 | if (ps == NULL) { |
1717 | icnt = 0; |
1718 | } |
1719 | break; |
1720 | } |
1721 | } |
1722 | |
1723 | if (out_buf_size - ocnt > 0) { |
1724 | if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) { |
1725 | goto out_failure; |
1726 | } |
1727 | php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC); |
1728 | } else { |
1729 | pefree(out_buf, persistent); |
1730 | } |
1731 | *consumed += buf_len - icnt; |
1732 | |
1733 | return SUCCESS; |
1734 | |
1735 | out_failure: |
1736 | pefree(out_buf, persistent); |
1737 | return FAILURE; |
1738 | } |
1739 | /* }}} */ |
1740 | |
1741 | static php_stream_filter_status_t strfilter_convert_filter( |
1742 | php_stream *stream, |
1743 | php_stream_filter *thisfilter, |
1744 | php_stream_bucket_brigade *buckets_in, |
1745 | php_stream_bucket_brigade *buckets_out, |
1746 | size_t *bytes_consumed, |
1747 | int flags |
1748 | TSRMLS_DC) |
1749 | { |
1750 | php_stream_bucket *bucket = NULL; |
1751 | size_t consumed = 0; |
1752 | php_convert_filter *inst = (php_convert_filter *)thisfilter->abstract; |
1753 | |
1754 | while (buckets_in->head != NULL) { |
1755 | bucket = buckets_in->head; |
1756 | |
1757 | php_stream_bucket_unlink(bucket TSRMLS_CC); |
1758 | |
1759 | if (strfilter_convert_append_bucket(inst, stream, thisfilter, |
1760 | buckets_out, bucket->buf, bucket->buflen, &consumed, |
1761 | php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) { |
1762 | goto out_failure; |
1763 | } |
1764 | |
1765 | php_stream_bucket_delref(bucket TSRMLS_CC); |
1766 | } |
1767 | |
1768 | if (flags != PSFS_FLAG_NORMAL) { |
1769 | if (strfilter_convert_append_bucket(inst, stream, thisfilter, |
1770 | buckets_out, NULL, 0, &consumed, |
1771 | php_stream_is_persistent(stream) TSRMLS_CC) != SUCCESS) { |
1772 | goto out_failure; |
1773 | } |
1774 | } |
1775 | |
1776 | if (bytes_consumed) { |
1777 | *bytes_consumed = consumed; |
1778 | } |
1779 | |
1780 | return PSFS_PASS_ON; |
1781 | |
1782 | out_failure: |
1783 | if (bucket != NULL) { |
1784 | php_stream_bucket_delref(bucket TSRMLS_CC); |
1785 | } |
1786 | return PSFS_ERR_FATAL; |
1787 | } |
1788 | |
1789 | static void strfilter_convert_dtor(php_stream_filter *thisfilter TSRMLS_DC) |
1790 | { |
1791 | assert(thisfilter->abstract != NULL); |
1792 | |
1793 | php_convert_filter_dtor((php_convert_filter *)thisfilter->abstract); |
1794 | pefree(thisfilter->abstract, ((php_convert_filter *)thisfilter->abstract)->persistent); |
1795 | } |
1796 | |
1797 | static php_stream_filter_ops strfilter_convert_ops = { |
1798 | strfilter_convert_filter, |
1799 | strfilter_convert_dtor, |
1800 | "convert.*" |
1801 | }; |
1802 | |
1803 | static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) |
1804 | { |
1805 | php_convert_filter *inst; |
1806 | php_stream_filter *retval = NULL; |
1807 | |
1808 | char *dot; |
1809 | int conv_mode = 0; |
1810 | |
1811 | if (filterparams != NULL && Z_TYPE_P(filterparams) != IS_ARRAY) { |
1812 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "stream filter (%s): invalid filter parameter" , filtername); |
1813 | return NULL; |
1814 | } |
1815 | |
1816 | if ((dot = strchr(filtername, '.')) == NULL) { |
1817 | return NULL; |
1818 | } |
1819 | ++dot; |
1820 | |
1821 | inst = pemalloc(sizeof(php_convert_filter), persistent); |
1822 | |
1823 | if (strcasecmp(dot, "base64-encode" ) == 0) { |
1824 | conv_mode = PHP_CONV_BASE64_ENCODE; |
1825 | } else if (strcasecmp(dot, "base64-decode" ) == 0) { |
1826 | conv_mode = PHP_CONV_BASE64_DECODE; |
1827 | } else if (strcasecmp(dot, "quoted-printable-encode" ) == 0) { |
1828 | conv_mode = PHP_CONV_QPRINT_ENCODE; |
1829 | } else if (strcasecmp(dot, "quoted-printable-decode" ) == 0) { |
1830 | conv_mode = PHP_CONV_QPRINT_DECODE; |
1831 | } |
1832 | |
1833 | if (php_convert_filter_ctor(inst, conv_mode, |
1834 | (filterparams != NULL ? Z_ARRVAL_P(filterparams) : NULL), |
1835 | filtername, persistent) != SUCCESS) { |
1836 | goto out; |
1837 | } |
1838 | |
1839 | retval = php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent); |
1840 | out: |
1841 | if (retval == NULL) { |
1842 | pefree(inst, persistent); |
1843 | } |
1844 | |
1845 | return retval; |
1846 | } |
1847 | |
1848 | static php_stream_filter_factory strfilter_convert_factory = { |
1849 | strfilter_convert_create |
1850 | }; |
1851 | /* }}} */ |
1852 | |
1853 | /* {{{ consumed filter implementation */ |
1854 | typedef struct _php_consumed_filter_data { |
1855 | int persistent; |
1856 | size_t consumed; |
1857 | off_t offset; |
1858 | } php_consumed_filter_data; |
1859 | |
1860 | static php_stream_filter_status_t consumed_filter_filter( |
1861 | php_stream *stream, |
1862 | php_stream_filter *thisfilter, |
1863 | php_stream_bucket_brigade *buckets_in, |
1864 | php_stream_bucket_brigade *buckets_out, |
1865 | size_t *bytes_consumed, |
1866 | int flags |
1867 | TSRMLS_DC) |
1868 | { |
1869 | php_consumed_filter_data *data = (php_consumed_filter_data *)(thisfilter->abstract); |
1870 | php_stream_bucket *bucket; |
1871 | size_t consumed = 0; |
1872 | |
1873 | if (data->offset == ~0) { |
1874 | data->offset = php_stream_tell(stream); |
1875 | } |
1876 | while ((bucket = buckets_in->head) != NULL) { |
1877 | php_stream_bucket_unlink(bucket TSRMLS_CC); |
1878 | consumed += bucket->buflen; |
1879 | php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); |
1880 | } |
1881 | if (bytes_consumed) { |
1882 | *bytes_consumed = consumed; |
1883 | } |
1884 | if (flags & PSFS_FLAG_FLUSH_CLOSE) { |
1885 | php_stream_seek(stream, data->offset + data->consumed, SEEK_SET); |
1886 | } |
1887 | data->consumed += consumed; |
1888 | |
1889 | return PSFS_PASS_ON; |
1890 | } |
1891 | |
1892 | static void consumed_filter_dtor(php_stream_filter *thisfilter TSRMLS_DC) |
1893 | { |
1894 | if (thisfilter && thisfilter->abstract) { |
1895 | php_consumed_filter_data *data = (php_consumed_filter_data*)thisfilter->abstract; |
1896 | pefree(data, data->persistent); |
1897 | } |
1898 | } |
1899 | |
1900 | static php_stream_filter_ops consumed_filter_ops = { |
1901 | consumed_filter_filter, |
1902 | consumed_filter_dtor, |
1903 | "consumed" |
1904 | }; |
1905 | |
1906 | static php_stream_filter *consumed_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) |
1907 | { |
1908 | php_stream_filter_ops *fops = NULL; |
1909 | php_consumed_filter_data *data; |
1910 | |
1911 | if (strcasecmp(filtername, "consumed" )) { |
1912 | return NULL; |
1913 | } |
1914 | |
1915 | /* Create this filter */ |
1916 | data = pecalloc(1, sizeof(php_consumed_filter_data), persistent); |
1917 | if (!data) { |
1918 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes" , sizeof(php_consumed_filter_data)); |
1919 | return NULL; |
1920 | } |
1921 | data->persistent = persistent; |
1922 | data->consumed = 0; |
1923 | data->offset = ~0; |
1924 | fops = &consumed_filter_ops; |
1925 | |
1926 | return php_stream_filter_alloc(fops, data, persistent); |
1927 | } |
1928 | |
1929 | php_stream_filter_factory consumed_filter_factory = { |
1930 | consumed_filter_create |
1931 | }; |
1932 | |
1933 | /* }}} */ |
1934 | |
1935 | /* {{{ chunked filter implementation */ |
1936 | typedef enum _php_chunked_filter_state { |
1937 | CHUNK_SIZE_START, |
1938 | CHUNK_SIZE, |
1939 | CHUNK_SIZE_EXT, |
1940 | CHUNK_SIZE_CR, |
1941 | CHUNK_SIZE_LF, |
1942 | CHUNK_BODY, |
1943 | CHUNK_BODY_CR, |
1944 | CHUNK_BODY_LF, |
1945 | CHUNK_TRAILER, |
1946 | CHUNK_ERROR |
1947 | } php_chunked_filter_state; |
1948 | |
1949 | typedef struct _php_chunked_filter_data { |
1950 | php_chunked_filter_state state; |
1951 | size_t chunk_size; |
1952 | int persistent; |
1953 | } php_chunked_filter_data; |
1954 | |
1955 | static int php_dechunk(char *buf, int len, php_chunked_filter_data *data) |
1956 | { |
1957 | char *p = buf; |
1958 | char *end = p + len; |
1959 | char *out = buf; |
1960 | int out_len = 0; |
1961 | |
1962 | while (p < end) { |
1963 | switch (data->state) { |
1964 | case CHUNK_SIZE_START: |
1965 | data->chunk_size = 0; |
1966 | case CHUNK_SIZE: |
1967 | while (p < end) { |
1968 | if (*p >= '0' && *p <= '9') { |
1969 | data->chunk_size = (data->chunk_size * 16) + (*p - '0'); |
1970 | } else if (*p >= 'A' && *p <= 'F') { |
1971 | data->chunk_size = (data->chunk_size * 16) + (*p - 'A' + 10); |
1972 | } else if (*p >= 'a' && *p <= 'f') { |
1973 | data->chunk_size = (data->chunk_size * 16) + (*p - 'a' + 10); |
1974 | } else if (data->state == CHUNK_SIZE_START) { |
1975 | data->state = CHUNK_ERROR; |
1976 | break; |
1977 | } else { |
1978 | data->state = CHUNK_SIZE_EXT; |
1979 | break; |
1980 | } |
1981 | data->state = CHUNK_SIZE; |
1982 | p++; |
1983 | } |
1984 | if (data->state == CHUNK_ERROR) { |
1985 | continue; |
1986 | } else if (p == end) { |
1987 | return out_len; |
1988 | } |
1989 | case CHUNK_SIZE_EXT: |
1990 | /* skip extension */ |
1991 | while (p < end && *p != '\r' && *p != '\n') { |
1992 | p++; |
1993 | } |
1994 | if (p == end) { |
1995 | return out_len; |
1996 | } |
1997 | case CHUNK_SIZE_CR: |
1998 | if (*p == '\r') { |
1999 | p++; |
2000 | if (p == end) { |
2001 | data->state = CHUNK_SIZE_LF; |
2002 | return out_len; |
2003 | } |
2004 | } |
2005 | case CHUNK_SIZE_LF: |
2006 | if (*p == '\n') { |
2007 | p++; |
2008 | if (data->chunk_size == 0) { |
2009 | /* last chunk */ |
2010 | data->state = CHUNK_TRAILER; |
2011 | continue; |
2012 | } else if (p == end) { |
2013 | data->state = CHUNK_BODY; |
2014 | return out_len; |
2015 | } |
2016 | } else { |
2017 | data->state = CHUNK_ERROR; |
2018 | continue; |
2019 | } |
2020 | case CHUNK_BODY: |
2021 | if ((size_t) (end - p) >= data->chunk_size) { |
2022 | if (p != out) { |
2023 | memmove(out, p, data->chunk_size); |
2024 | } |
2025 | out += data->chunk_size; |
2026 | out_len += data->chunk_size; |
2027 | p += data->chunk_size; |
2028 | if (p == end) { |
2029 | data->state = CHUNK_BODY_CR; |
2030 | return out_len; |
2031 | } |
2032 | } else { |
2033 | if (p != out) { |
2034 | memmove(out, p, end - p); |
2035 | } |
2036 | data->chunk_size -= end - p; |
2037 | data->state=CHUNK_BODY; |
2038 | out_len += end - p; |
2039 | return out_len; |
2040 | } |
2041 | case CHUNK_BODY_CR: |
2042 | if (*p == '\r') { |
2043 | p++; |
2044 | if (p == end) { |
2045 | data->state = CHUNK_BODY_LF; |
2046 | return out_len; |
2047 | } |
2048 | } |
2049 | case CHUNK_BODY_LF: |
2050 | if (*p == '\n') { |
2051 | p++; |
2052 | data->state = CHUNK_SIZE_START; |
2053 | continue; |
2054 | } else { |
2055 | data->state = CHUNK_ERROR; |
2056 | continue; |
2057 | } |
2058 | case CHUNK_TRAILER: |
2059 | /* ignore trailer */ |
2060 | p = end; |
2061 | continue; |
2062 | case CHUNK_ERROR: |
2063 | if (p != out) { |
2064 | memmove(out, p, end - p); |
2065 | } |
2066 | out_len += end - p; |
2067 | return out_len; |
2068 | } |
2069 | } |
2070 | return out_len; |
2071 | } |
2072 | |
2073 | static php_stream_filter_status_t php_chunked_filter( |
2074 | php_stream *stream, |
2075 | php_stream_filter *thisfilter, |
2076 | php_stream_bucket_brigade *buckets_in, |
2077 | php_stream_bucket_brigade *buckets_out, |
2078 | size_t *bytes_consumed, |
2079 | int flags |
2080 | TSRMLS_DC) |
2081 | { |
2082 | php_stream_bucket *bucket; |
2083 | size_t consumed = 0; |
2084 | php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract; |
2085 | |
2086 | while (buckets_in->head) { |
2087 | bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC); |
2088 | consumed += bucket->buflen; |
2089 | bucket->buflen = php_dechunk(bucket->buf, bucket->buflen, data); |
2090 | php_stream_bucket_append(buckets_out, bucket TSRMLS_CC); |
2091 | } |
2092 | |
2093 | if (bytes_consumed) { |
2094 | *bytes_consumed = consumed; |
2095 | } |
2096 | |
2097 | return PSFS_PASS_ON; |
2098 | } |
2099 | |
2100 | static void php_chunked_dtor(php_stream_filter *thisfilter TSRMLS_DC) |
2101 | { |
2102 | if (thisfilter && thisfilter->abstract) { |
2103 | php_chunked_filter_data *data = (php_chunked_filter_data *) thisfilter->abstract; |
2104 | pefree(data, data->persistent); |
2105 | } |
2106 | } |
2107 | |
2108 | static php_stream_filter_ops chunked_filter_ops = { |
2109 | php_chunked_filter, |
2110 | php_chunked_dtor, |
2111 | "dechunk" |
2112 | }; |
2113 | |
2114 | static php_stream_filter *chunked_filter_create(const char *filtername, zval *filterparams, int persistent TSRMLS_DC) |
2115 | { |
2116 | php_stream_filter_ops *fops = NULL; |
2117 | php_chunked_filter_data *data; |
2118 | |
2119 | if (strcasecmp(filtername, "dechunk" )) { |
2120 | return NULL; |
2121 | } |
2122 | |
2123 | /* Create this filter */ |
2124 | data = (php_chunked_filter_data *)pecalloc(1, sizeof(php_chunked_filter_data), persistent); |
2125 | if (!data) { |
2126 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed allocating %zd bytes" , sizeof(php_chunked_filter_data)); |
2127 | return NULL; |
2128 | } |
2129 | data->state = CHUNK_SIZE_START; |
2130 | data->chunk_size = 0; |
2131 | data->persistent = persistent; |
2132 | fops = &chunked_filter_ops; |
2133 | |
2134 | return php_stream_filter_alloc(fops, data, persistent); |
2135 | } |
2136 | |
2137 | static php_stream_filter_factory chunked_filter_factory = { |
2138 | chunked_filter_create |
2139 | }; |
2140 | /* }}} */ |
2141 | |
2142 | static const struct { |
2143 | php_stream_filter_ops *ops; |
2144 | php_stream_filter_factory *factory; |
2145 | } standard_filters[] = { |
2146 | { &strfilter_rot13_ops, &strfilter_rot13_factory }, |
2147 | { &strfilter_toupper_ops, &strfilter_toupper_factory }, |
2148 | { &strfilter_tolower_ops, &strfilter_tolower_factory }, |
2149 | { &strfilter_strip_tags_ops, &strfilter_strip_tags_factory }, |
2150 | { &strfilter_convert_ops, &strfilter_convert_factory }, |
2151 | { &consumed_filter_ops, &consumed_filter_factory }, |
2152 | { &chunked_filter_ops, &chunked_filter_factory }, |
2153 | /* additional filters to go here */ |
2154 | { NULL, NULL } |
2155 | }; |
2156 | |
2157 | /* {{{ filter MINIT and MSHUTDOWN */ |
2158 | PHP_MINIT_FUNCTION(standard_filters) |
2159 | { |
2160 | int i; |
2161 | |
2162 | for (i = 0; standard_filters[i].ops; i++) { |
2163 | if (FAILURE == php_stream_filter_register_factory( |
2164 | standard_filters[i].ops->label, |
2165 | standard_filters[i].factory |
2166 | TSRMLS_CC)) { |
2167 | return FAILURE; |
2168 | } |
2169 | } |
2170 | return SUCCESS; |
2171 | } |
2172 | |
2173 | PHP_MSHUTDOWN_FUNCTION(standard_filters) |
2174 | { |
2175 | int i; |
2176 | |
2177 | for (i = 0; standard_filters[i].ops; i++) { |
2178 | php_stream_filter_unregister_factory(standard_filters[i].ops->label TSRMLS_CC); |
2179 | } |
2180 | return SUCCESS; |
2181 | } |
2182 | /* }}} */ |
2183 | |
2184 | /* |
2185 | * Local variables: |
2186 | * tab-width: 4 |
2187 | * c-basic-offset: 4 |
2188 | * End: |
2189 | * vim600: sw=4 ts=4 fdm=marker |
2190 | * vim<600: sw=4 ts=4 |
2191 | */ |
2192 | |