xref: /PHP-5.3/ext/standard/filters.c (revision a2045ff3)
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