1 /* Generated by re2c 2.1.1 */
2 /*
3   +----------------------------------------------------------------------+
4   | Copyright (c) The PHP Group                                          |
5   +----------------------------------------------------------------------+
6   | This source file is subject to version 3.01 of the PHP license,      |
7   | that is bundled with this package in the file LICENSE, and is        |
8   | available through the world-wide-web at the following url:           |
9   | https://www.php.net/license/3_01.txt                                 |
10   | If you did not receive a copy of the PHP license and are unable to   |
11   | obtain it through the world-wide-web, please send a note to          |
12   | license@php.net so we can mail you a copy immediately.               |
13   +----------------------------------------------------------------------+
14   | Author: Sascha Schumann <sascha@schumann.cx>                         |
15   |         Yasuo Ohgaki <yohgaki@ohgaki.net>                            |
16   +----------------------------------------------------------------------+
17 */
18 
19 #include "php.h"
20 
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "SAPI.h"
31 #include "php_ini.h"
32 #include "php_globals.h"
33 #include "php_string.h"
34 #define STATE_TAG SOME_OTHER_STATE_TAG
35 #include "basic_functions.h"
36 #include "url.h"
37 #include "html.h"
38 #undef STATE_TAG
39 
40 #define url_scanner url_scanner_ex
41 
42 #include "zend_smart_str.h"
43 
tag_dtor(zval * zv)44 static void tag_dtor(zval *zv)
45 {
46 	free(Z_PTR_P(zv));
47 }
48 
php_ini_on_update_tags(zend_ini_entry * entry,zend_string * new_value,void * mh_arg1,void * mh_arg2,void * mh_arg3,int stage,int type)49 static int php_ini_on_update_tags(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type)
50 {
51 	url_adapt_state_ex_t *ctx;
52 	char *key;
53 	char *tmp;
54 	char *lasts = NULL;
55 
56 	if (type) {
57 		ctx = &BG(url_adapt_session_ex);
58 	} else {
59 		ctx = &BG(url_adapt_output_ex);
60 	}
61 
62 	tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
63 
64 	if (ctx->tags)
65 		zend_hash_destroy(ctx->tags);
66 	else {
67 		ctx->tags = malloc(sizeof(HashTable));
68 		if (!ctx->tags) {
69 			efree(tmp);
70 			return FAILURE;
71 		}
72 	}
73 
74 	zend_hash_init(ctx->tags, 0, NULL, tag_dtor, 1);
75 
76 	for (key = php_strtok_r(tmp, ",", &lasts);
77 		 key;
78 		 key = php_strtok_r(NULL, ",", &lasts)) {
79 		char *val;
80 
81 		val = strchr(key, '=');
82 		if (val) {
83 			char *q;
84 			size_t keylen;
85 			zend_string *str;
86 
87 			*val++ = '\0';
88 			for (q = key; *q; q++) {
89 				*q = tolower(*q);
90 			}
91 			keylen = q - key;
92 			str = zend_string_init(key, keylen, 1);
93 			GC_MAKE_PERSISTENT_LOCAL(str);
94 			zend_hash_add_mem(ctx->tags, str, val, strlen(val)+1);
95 			zend_string_release_ex(str, 1);
96 		}
97 	}
98 
99 	efree(tmp);
100 
101 	return SUCCESS;
102 }
103 
PHP_INI_MH(OnUpdateSessionTags)104 static PHP_INI_MH(OnUpdateSessionTags)
105 {
106 	return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
107 }
108 
PHP_INI_MH(OnUpdateOutputTags)109 static PHP_INI_MH(OnUpdateOutputTags)
110 {
111 	return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
112 }
113 
php_ini_on_update_hosts(zend_ini_entry * entry,zend_string * new_value,void * mh_arg1,void * mh_arg2,void * mh_arg3,int stage,int type)114 static int php_ini_on_update_hosts(zend_ini_entry *entry, zend_string *new_value, void *mh_arg1, void *mh_arg2, void *mh_arg3, int stage, int type)
115 {
116 	HashTable *hosts;
117 	char *key;
118 	char *tmp;
119 	char *lasts = NULL;
120 
121 	if (type) {
122 		hosts = &BG(url_adapt_session_hosts_ht);
123 	} else {
124 		hosts = &BG(url_adapt_output_hosts_ht);
125 	}
126 	zend_hash_clean(hosts);
127 
128 	/* Use user supplied host whitelist */
129 	tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
130 	for (key = php_strtok_r(tmp, ",", &lasts);
131 		 key;
132 		 key = php_strtok_r(NULL, ",", &lasts)) {
133 		size_t keylen;
134 		zend_string *tmp_key;
135 		char *q;
136 
137 		for (q = key; *q; q++) {
138 			*q = tolower(*q);
139 		}
140 		keylen = q - key;
141 		if (keylen > 0) {
142 			tmp_key = zend_string_init(key, keylen, 0);
143 			zend_hash_add_empty_element(hosts, tmp_key);
144 			zend_string_release_ex(tmp_key, 0);
145 		}
146 	}
147 	efree(tmp);
148 
149 	return SUCCESS;
150 }
151 
PHP_INI_MH(OnUpdateSessionHosts)152 static PHP_INI_MH(OnUpdateSessionHosts)
153 {
154 	return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
155 }
156 
PHP_INI_MH(OnUpdateOutputHosts)157 static PHP_INI_MH(OnUpdateOutputHosts)
158 {
159 	return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
160 }
161 
162 /* FIXME: OnUpdate*Hosts cannot set default to $_SERVER['HTTP_HOST'] at startup */
163 PHP_INI_BEGIN()
164 	STD_PHP_INI_ENTRY("session.trans_sid_tags", "a=href,area=href,frame=src,form=", PHP_INI_ALL, OnUpdateSessionTags, url_adapt_session_ex, php_basic_globals, basic_globals)
165 	STD_PHP_INI_ENTRY("session.trans_sid_hosts", "", PHP_INI_ALL, OnUpdateSessionHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
166 	STD_PHP_INI_ENTRY("url_rewriter.tags", "form=", PHP_INI_ALL, OnUpdateOutputTags, url_adapt_session_ex, php_basic_globals, basic_globals)
167 	STD_PHP_INI_ENTRY("url_rewriter.hosts", "", PHP_INI_ALL, OnUpdateOutputHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
PHP_INI_END()168 PHP_INI_END()
169 
170 
171 
172 #define YYFILL(n) goto done
173 #define YYCTYPE unsigned char
174 #define YYCURSOR p
175 #define YYLIMIT q
176 #define YYMARKER r
177 
178 static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator)
179 {
180 	php_url *url_parts;
181 
182 	smart_str_0(url); /* FIXME: Bug #70480 php_url_parse_ex() crashes by processing chars exceed len */
183 	url_parts = php_url_parse_ex(ZSTR_VAL(url->s), ZSTR_LEN(url->s));
184 
185 	/* Ignore malformed URLs */
186 	if (!url_parts) {
187 		smart_str_append_smart_str(dest, url);
188 		return;
189 	}
190 
191 	/* Don't modify URLs of the format "#mark" */
192 	if (url_parts->fragment && '#' == ZSTR_VAL(url->s)[0]) {
193 		smart_str_append_smart_str(dest, url);
194 		php_url_free(url_parts);
195 		return;
196 	}
197 
198 	/* Check protocol. Only http/https is allowed. */
199 	if (url_parts->scheme
200 		&& !zend_string_equals_literal_ci(url_parts->scheme, "http")
201 		&& !zend_string_equals_literal_ci(url_parts->scheme, "https")) {
202 		smart_str_append_smart_str(dest, url);
203 		php_url_free(url_parts);
204 		return;
205 	}
206 
207 	/* Check host whitelist. If it's not listed, do nothing. */
208 	if (url_parts->host) {
209 		zend_string *tmp = zend_string_tolower(url_parts->host);
210 		if (!zend_hash_exists(&BG(url_adapt_session_hosts_ht), tmp)) {
211 			zend_string_release_ex(tmp, 0);
212 			smart_str_append_smart_str(dest, url);
213 			php_url_free(url_parts);
214 			return;
215 		}
216 		zend_string_release_ex(tmp, 0);
217 	}
218 
219 	/*
220 	 * When URL does not have path and query string add "/?".
221 	 * i.e. If URL is only "?foo=bar", should not add "/?".
222 	 */
223 	if (!url_parts->path && !url_parts->query && !url_parts->fragment) {
224 		/* URL is http://php.net or like */
225 		smart_str_append_smart_str(dest, url);
226 		smart_str_appendc(dest, '/');
227 		smart_str_appendc(dest, '?');
228 		smart_str_append_smart_str(dest, url_app);
229 		php_url_free(url_parts);
230 		return;
231 	}
232 
233 	if (url_parts->scheme) {
234 		smart_str_appends(dest, ZSTR_VAL(url_parts->scheme));
235 		smart_str_appends(dest, "://");
236 	} else if (*(ZSTR_VAL(url->s)) == '/' && *(ZSTR_VAL(url->s)+1) == '/') {
237 		smart_str_appends(dest, "//");
238 	}
239 	if (url_parts->user) {
240 		smart_str_appends(dest, ZSTR_VAL(url_parts->user));
241 		if (url_parts->pass) {
242 			smart_str_appends(dest, ZSTR_VAL(url_parts->pass));
243 			smart_str_appendc(dest, ':');
244 		}
245 		smart_str_appendc(dest, '@');
246 	}
247 	if (url_parts->host) {
248 		smart_str_appends(dest, ZSTR_VAL(url_parts->host));
249 	}
250 	if (url_parts->port) {
251 		smart_str_appendc(dest, ':');
252 		smart_str_append_unsigned(dest, (long)url_parts->port);
253 	}
254 	if (url_parts->path) {
255 		smart_str_appends(dest, ZSTR_VAL(url_parts->path));
256 	}
257 	smart_str_appendc(dest, '?');
258 	if (url_parts->query) {
259 		smart_str_appends(dest, ZSTR_VAL(url_parts->query));
260 		smart_str_appends(dest, separator);
261 		smart_str_append_smart_str(dest, url_app);
262 	} else {
263 		smart_str_append_smart_str(dest, url_app);
264 	}
265 	if (url_parts->fragment) {
266 		smart_str_appendc(dest, '#');
267 		smart_str_appends(dest, ZSTR_VAL(url_parts->fragment));
268 	}
269 	php_url_free(url_parts);
270 }
271 
272 enum {
273 	TAG_NORMAL = 0,
274 	TAG_FORM
275 };
276 
277 enum {
278 	ATTR_NORMAL = 0,
279 	ATTR_ACTION
280 };
281 
282 #undef YYFILL
283 #undef YYCTYPE
284 #undef YYCURSOR
285 #undef YYLIMIT
286 #undef YYMARKER
287 
tag_arg(url_adapt_state_ex_t * ctx,char quotes,char type)288 static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type)
289 {
290 	char f = 0;
291 
292 	/* arg.s is string WITHOUT NUL.
293 	   To avoid partial match, NUL is added here */
294 	ZSTR_VAL(ctx->arg.s)[ZSTR_LEN(ctx->arg.s)] = '\0';
295 	if (!strcasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data)) {
296 		f = 1;
297 	}
298 
299 	if (quotes) {
300 		smart_str_appendc(&ctx->result, type);
301 	}
302 	if (f) {
303 		append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output);
304 	} else {
305 		smart_str_append_smart_str(&ctx->result, &ctx->val);
306 	}
307 	if (quotes) {
308 		smart_str_appendc(&ctx->result, type);
309 	}
310 }
311 
312 enum {
313 	STATE_PLAIN = 0,
314 	STATE_TAG,
315 	STATE_NEXT_ARG,
316 	STATE_ARG,
317 	STATE_BEFORE_VAL,
318 	STATE_VAL
319 };
320 
321 #define YYFILL(n) goto stop
322 #define YYCTYPE unsigned char
323 #define YYCURSOR xp
324 #define YYLIMIT end
325 #define YYMARKER q
326 #define STATE ctx->state
327 
328 #define STD_PARA url_adapt_state_ex_t *ctx, char *start, char *YYCURSOR
329 #define STD_ARGS ctx, start, xp
330 
331 #if SCANNER_DEBUG
332 #define scdebug(x) printf x
333 #else
334 #define scdebug(x)
335 #endif
336 
passthru(STD_PARA)337 static inline void passthru(STD_PARA)
338 {
339 	scdebug(("appending %d chars, starting with %c\n", YYCURSOR-start, *start));
340 	smart_str_appendl(&ctx->result, start, YYCURSOR - start);
341 }
342 
343 
check_http_host(char * target)344 static int check_http_host(char *target)
345 {
346 	zval *host, *tmp;
347 	zend_string *host_tmp;
348 	char *colon;
349 
350 	if ((tmp = zend_hash_find(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER))) &&
351 		Z_TYPE_P(tmp) == IS_ARRAY &&
352 		(host = zend_hash_str_find(Z_ARRVAL_P(tmp), ZEND_STRL("HTTP_HOST"))) &&
353 		Z_TYPE_P(host) == IS_STRING) {
354 		host_tmp = zend_string_init(Z_STRVAL_P(host), Z_STRLEN_P(host), 0);
355 		/* HTTP_HOST could be 'localhost:8888' etc. */
356 		colon = strchr(ZSTR_VAL(host_tmp), ':');
357 		if (colon) {
358 			ZSTR_LEN(host_tmp) = colon - ZSTR_VAL(host_tmp);
359 			ZSTR_VAL(host_tmp)[ZSTR_LEN(host_tmp)] = '\0';
360 		}
361 		if (!strcasecmp(ZSTR_VAL(host_tmp), target)) {
362 			zend_string_release_ex(host_tmp, 0);
363 			return SUCCESS;
364 		}
365 		zend_string_release_ex(host_tmp, 0);
366 	}
367 	return FAILURE;
368 }
369 
check_host_whitelist(url_adapt_state_ex_t * ctx)370 static int check_host_whitelist(url_adapt_state_ex_t *ctx)
371 {
372 	php_url *url_parts = NULL;
373 	HashTable *allowed_hosts = ctx->type ? &BG(url_adapt_session_hosts_ht) : &BG(url_adapt_output_hosts_ht);
374 
375 	ZEND_ASSERT(ctx->tag_type == TAG_FORM);
376 
377 	if (ctx->attr_val.s && ZSTR_LEN(ctx->attr_val.s)) {
378 		url_parts = php_url_parse_ex(ZSTR_VAL(ctx->attr_val.s), ZSTR_LEN(ctx->attr_val.s));
379 	} else {
380 		return SUCCESS; /* empty URL is valid */
381 	}
382 
383 	if (!url_parts) {
384 		return FAILURE;
385 	}
386 	if (url_parts->scheme) {
387 		/* Only http/https should be handled.
388 		   A bit hacky check this here, but saves a URL parse. */
389 		if (!zend_string_equals_literal_ci(url_parts->scheme, "http") &&
390 			!zend_string_equals_literal_ci(url_parts->scheme, "https")) {
391 		php_url_free(url_parts);
392 		return FAILURE;
393 		}
394 	}
395 	if (!url_parts->host) {
396 		php_url_free(url_parts);
397 		return SUCCESS;
398 	}
399 	if (!zend_hash_num_elements(allowed_hosts) &&
400 		check_http_host(ZSTR_VAL(url_parts->host)) == SUCCESS) {
401 		php_url_free(url_parts);
402 		return SUCCESS;
403 	}
404 	if (!zend_hash_find(allowed_hosts, url_parts->host)) {
405 		php_url_free(url_parts);
406 		return FAILURE;
407 	}
408 	php_url_free(url_parts);
409 	return SUCCESS;
410 }
411 
412 /*
413  * This function appends a hidden input field after a <form>.
414  */
handle_form(STD_PARA)415 static void handle_form(STD_PARA)
416 {
417 	int doit = 0;
418 
419 	if (ZSTR_LEN(ctx->form_app.s) > 0) {
420 		switch (ZSTR_LEN(ctx->tag.s)) {
421 			case sizeof("form") - 1:
422 				if (!strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))
423 					&& check_host_whitelist(ctx) == SUCCESS) {
424 					doit = 1;
425 				}
426 				break;
427 		}
428 	}
429 
430 	if (doit) {
431 		smart_str_append_smart_str(&ctx->result, &ctx->form_app);
432 	}
433 }
434 
435 /*
436  *  HANDLE_TAG copies the HTML Tag and checks whether we
437  *  have that tag in our table. If we might modify it,
438  *  we continue to scan the tag, otherwise we simply copy the complete
439  *  HTML stuff to the result buffer.
440  */
441 
handle_tag(STD_PARA)442 static inline void handle_tag(STD_PARA)
443 {
444 	int ok = 0;
445 	unsigned int i;
446 
447 	if (ctx->tag.s) {
448 		ZSTR_LEN(ctx->tag.s) = 0;
449 	}
450 	smart_str_appendl(&ctx->tag, start, YYCURSOR - start);
451 	for (i = 0; i < ZSTR_LEN(ctx->tag.s); i++)
452 		ZSTR_VAL(ctx->tag.s)[i] = tolower((int)(unsigned char)ZSTR_VAL(ctx->tag.s)[i]);
453     /* intentionally using str_find here, in case the hash value is set, but the string val is changed later */
454 	if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) {
455 		ok = 1;
456 		if (ZSTR_LEN(ctx->tag.s) == sizeof("form")-1
457 			&& !strncasecmp(ZSTR_VAL(ctx->tag.s), "form", ZSTR_LEN(ctx->tag.s))) {
458 			ctx->tag_type = TAG_FORM;
459 		} else {
460 			ctx->tag_type = TAG_NORMAL;
461 		}
462 	}
463 	STATE = ok ? STATE_NEXT_ARG : STATE_PLAIN;
464 }
465 
handle_arg(STD_PARA)466 static inline void handle_arg(STD_PARA)
467 {
468 	if (ctx->arg.s) {
469 		ZSTR_LEN(ctx->arg.s) = 0;
470 	}
471 	smart_str_appendl(&ctx->arg, start, YYCURSOR - start);
472 	if (ctx->tag_type == TAG_FORM &&
473 		strncasecmp(ZSTR_VAL(ctx->arg.s), "action", ZSTR_LEN(ctx->arg.s)) == 0) {
474 		ctx->attr_type = ATTR_ACTION;
475 	} else {
476 		ctx->attr_type = ATTR_NORMAL;
477 	}
478 }
479 
handle_val(STD_PARA,char quotes,char type)480 static inline void handle_val(STD_PARA, char quotes, char type)
481 {
482 	smart_str_setl(&ctx->val, start + quotes, YYCURSOR - start - quotes * 2);
483 	if (ctx->tag_type == TAG_FORM && ctx->attr_type == ATTR_ACTION) {
484 		smart_str_setl(&ctx->attr_val, start + quotes, YYCURSOR - start - quotes * 2);
485 	}
486 	tag_arg(ctx, quotes, type);
487 }
488 
xx_mainloop(url_adapt_state_ex_t * ctx,const char * newdata,size_t newlen)489 static inline void xx_mainloop(url_adapt_state_ex_t *ctx, const char *newdata, size_t newlen)
490 {
491 	char *end, *q;
492 	char *xp;
493 	char *start;
494 	size_t rest;
495 
496 	smart_str_appendl(&ctx->buf, newdata, newlen);
497 
498 	YYCURSOR = ZSTR_VAL(ctx->buf.s);
499 	YYLIMIT = ZSTR_VAL(ctx->buf.s) + ZSTR_LEN(ctx->buf.s);
500 
501 	switch (STATE) {
502 		case STATE_PLAIN: goto state_plain;
503 		case STATE_TAG: goto state_tag;
504 		case STATE_NEXT_ARG: goto state_next_arg;
505 		case STATE_ARG: goto state_arg;
506 		case STATE_BEFORE_VAL: goto state_before_val;
507 		case STATE_VAL: goto state_val;
508 	}
509 
510 
511 state_plain_begin:
512 	STATE = STATE_PLAIN;
513 
514 state_plain:
515 	start = YYCURSOR;
516 
517 {
518 	YYCTYPE yych;
519 	static const unsigned char yybm[] = {
520 		128, 128, 128, 128, 128, 128, 128, 128,
521 		128, 128, 128, 128, 128, 128, 128, 128,
522 		128, 128, 128, 128, 128, 128, 128, 128,
523 		128, 128, 128, 128, 128, 128, 128, 128,
524 		128, 128, 128, 128, 128, 128, 128, 128,
525 		128, 128, 128, 128, 128, 128, 128, 128,
526 		128, 128, 128, 128, 128, 128, 128, 128,
527 		128, 128, 128, 128,   0, 128, 128, 128,
528 		128, 128, 128, 128, 128, 128, 128, 128,
529 		128, 128, 128, 128, 128, 128, 128, 128,
530 		128, 128, 128, 128, 128, 128, 128, 128,
531 		128, 128, 128, 128, 128, 128, 128, 128,
532 		128, 128, 128, 128, 128, 128, 128, 128,
533 		128, 128, 128, 128, 128, 128, 128, 128,
534 		128, 128, 128, 128, 128, 128, 128, 128,
535 		128, 128, 128, 128, 128, 128, 128, 128,
536 		128, 128, 128, 128, 128, 128, 128, 128,
537 		128, 128, 128, 128, 128, 128, 128, 128,
538 		128, 128, 128, 128, 128, 128, 128, 128,
539 		128, 128, 128, 128, 128, 128, 128, 128,
540 		128, 128, 128, 128, 128, 128, 128, 128,
541 		128, 128, 128, 128, 128, 128, 128, 128,
542 		128, 128, 128, 128, 128, 128, 128, 128,
543 		128, 128, 128, 128, 128, 128, 128, 128,
544 		128, 128, 128, 128, 128, 128, 128, 128,
545 		128, 128, 128, 128, 128, 128, 128, 128,
546 		128, 128, 128, 128, 128, 128, 128, 128,
547 		128, 128, 128, 128, 128, 128, 128, 128,
548 		128, 128, 128, 128, 128, 128, 128, 128,
549 		128, 128, 128, 128, 128, 128, 128, 128,
550 		128, 128, 128, 128, 128, 128, 128, 128,
551 		128, 128, 128, 128, 128, 128, 128, 128,
552 	};
553 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
554 	yych = *YYCURSOR;
555 	if (yybm[0+yych] & 128) {
556 		goto yy2;
557 	}
558 	goto yy5;
559 yy2:
560 	++YYCURSOR;
561 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
562 	yych = *YYCURSOR;
563 	if (yybm[0+yych] & 128) {
564 		goto yy2;
565 	}
566 	{ passthru(STD_ARGS); goto state_plain; }
567 yy5:
568 	++YYCURSOR;
569 	{ passthru(STD_ARGS); STATE = STATE_TAG; goto state_tag; }
570 }
571 
572 
573 state_tag:
574 	start = YYCURSOR;
575 
576 {
577 	YYCTYPE yych;
578 	static const unsigned char yybm[] = {
579 		  0,   0,   0,   0,   0,   0,   0,   0,
580 		  0,   0,   0,   0,   0,   0,   0,   0,
581 		  0,   0,   0,   0,   0,   0,   0,   0,
582 		  0,   0,   0,   0,   0,   0,   0,   0,
583 		  0,   0,   0,   0,   0,   0,   0,   0,
584 		  0,   0,   0,   0,   0,   0,   0,   0,
585 		  0,   0,   0,   0,   0,   0,   0,   0,
586 		  0,   0, 128,   0,   0,   0,   0,   0,
587 		  0, 128, 128, 128, 128, 128, 128, 128,
588 		128, 128, 128, 128, 128, 128, 128, 128,
589 		128, 128, 128, 128, 128, 128, 128, 128,
590 		128, 128, 128,   0,   0,   0,   0,   0,
591 		  0, 128, 128, 128, 128, 128, 128, 128,
592 		128, 128, 128, 128, 128, 128, 128, 128,
593 		128, 128, 128, 128, 128, 128, 128, 128,
594 		128, 128, 128,   0,   0,   0,   0,   0,
595 		  0,   0,   0,   0,   0,   0,   0,   0,
596 		  0,   0,   0,   0,   0,   0,   0,   0,
597 		  0,   0,   0,   0,   0,   0,   0,   0,
598 		  0,   0,   0,   0,   0,   0,   0,   0,
599 		  0,   0,   0,   0,   0,   0,   0,   0,
600 		  0,   0,   0,   0,   0,   0,   0,   0,
601 		  0,   0,   0,   0,   0,   0,   0,   0,
602 		  0,   0,   0,   0,   0,   0,   0,   0,
603 		  0,   0,   0,   0,   0,   0,   0,   0,
604 		  0,   0,   0,   0,   0,   0,   0,   0,
605 		  0,   0,   0,   0,   0,   0,   0,   0,
606 		  0,   0,   0,   0,   0,   0,   0,   0,
607 		  0,   0,   0,   0,   0,   0,   0,   0,
608 		  0,   0,   0,   0,   0,   0,   0,   0,
609 		  0,   0,   0,   0,   0,   0,   0,   0,
610 		  0,   0,   0,   0,   0,   0,   0,   0,
611 	};
612 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
613 	yych = *YYCURSOR;
614 	if (yybm[0+yych] & 128) {
615 		goto yy11;
616 	}
617 	++YYCURSOR;
618 	{ passthru(STD_ARGS); goto state_plain_begin; }
619 yy11:
620 	++YYCURSOR;
621 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
622 	yych = *YYCURSOR;
623 	if (yybm[0+yych] & 128) {
624 		goto yy11;
625 	}
626 	{ handle_tag(STD_ARGS); /* Sets STATE */; passthru(STD_ARGS); if (STATE == STATE_PLAIN) goto state_plain; else goto state_next_arg; }
627 }
628 
629 
630 state_next_arg_begin:
631 	STATE = STATE_NEXT_ARG;
632 
633 state_next_arg:
634 	start = YYCURSOR;
635 
636 {
637 	YYCTYPE yych;
638 	static const unsigned char yybm[] = {
639 		  0,   0,   0,   0,   0,   0,   0,   0,
640 		  0, 128, 128, 128,   0, 128,   0,   0,
641 		  0,   0,   0,   0,   0,   0,   0,   0,
642 		  0,   0,   0,   0,   0,   0,   0,   0,
643 		128,   0,   0,   0,   0,   0,   0,   0,
644 		  0,   0,   0,   0,   0,   0,   0,   0,
645 		  0,   0,   0,   0,   0,   0,   0,   0,
646 		  0,   0,   0,   0,   0,   0,   0,   0,
647 		  0,   0,   0,   0,   0,   0,   0,   0,
648 		  0,   0,   0,   0,   0,   0,   0,   0,
649 		  0,   0,   0,   0,   0,   0,   0,   0,
650 		  0,   0,   0,   0,   0,   0,   0,   0,
651 		  0,   0,   0,   0,   0,   0,   0,   0,
652 		  0,   0,   0,   0,   0,   0,   0,   0,
653 		  0,   0,   0,   0,   0,   0,   0,   0,
654 		  0,   0,   0,   0,   0,   0,   0,   0,
655 		  0,   0,   0,   0,   0,   0,   0,   0,
656 		  0,   0,   0,   0,   0,   0,   0,   0,
657 		  0,   0,   0,   0,   0,   0,   0,   0,
658 		  0,   0,   0,   0,   0,   0,   0,   0,
659 		  0,   0,   0,   0,   0,   0,   0,   0,
660 		  0,   0,   0,   0,   0,   0,   0,   0,
661 		  0,   0,   0,   0,   0,   0,   0,   0,
662 		  0,   0,   0,   0,   0,   0,   0,   0,
663 		  0,   0,   0,   0,   0,   0,   0,   0,
664 		  0,   0,   0,   0,   0,   0,   0,   0,
665 		  0,   0,   0,   0,   0,   0,   0,   0,
666 		  0,   0,   0,   0,   0,   0,   0,   0,
667 		  0,   0,   0,   0,   0,   0,   0,   0,
668 		  0,   0,   0,   0,   0,   0,   0,   0,
669 		  0,   0,   0,   0,   0,   0,   0,   0,
670 		  0,   0,   0,   0,   0,   0,   0,   0,
671 	};
672 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
673 	yych = *YYCURSOR;
674 	if (yybm[0+yych] & 128) {
675 		goto yy18;
676 	}
677 	if (yych <= '>') {
678 		if (yych == '/') goto yy21;
679 		if (yych >= '>') goto yy22;
680 	} else {
681 		if (yych <= 'Z') {
682 			if (yych >= 'A') goto yy24;
683 		} else {
684 			if (yych <= '`') goto yy16;
685 			if (yych <= 'z') goto yy24;
686 		}
687 	}
688 yy16:
689 	++YYCURSOR;
690 yy17:
691 	{ passthru(STD_ARGS); goto state_plain_begin; }
692 yy18:
693 	++YYCURSOR;
694 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
695 	yych = *YYCURSOR;
696 	if (yybm[0+yych] & 128) {
697 		goto yy18;
698 	}
699 	{ passthru(STD_ARGS); goto state_next_arg; }
700 yy21:
701 	yych = *++YYCURSOR;
702 	if (yych != '>') goto yy17;
703 yy22:
704 	++YYCURSOR;
705 	{ passthru(STD_ARGS); handle_form(STD_ARGS); goto state_plain_begin; }
706 yy24:
707 	++YYCURSOR;
708 	{ --YYCURSOR; STATE = STATE_ARG; goto state_arg; }
709 }
710 
711 
712 state_arg:
713 	start = YYCURSOR;
714 
715 {
716 	YYCTYPE yych;
717 	static const unsigned char yybm[] = {
718 		  0,   0,   0,   0,   0,   0,   0,   0,
719 		  0,   0,   0,   0,   0,   0,   0,   0,
720 		  0,   0,   0,   0,   0,   0,   0,   0,
721 		  0,   0,   0,   0,   0,   0,   0,   0,
722 		  0,   0,   0,   0,   0,   0,   0,   0,
723 		  0,   0,   0,   0,   0, 128,   0,   0,
724 		  0,   0,   0,   0,   0,   0,   0,   0,
725 		  0,   0,   0,   0,   0,   0,   0,   0,
726 		  0, 128, 128, 128, 128, 128, 128, 128,
727 		128, 128, 128, 128, 128, 128, 128, 128,
728 		128, 128, 128, 128, 128, 128, 128, 128,
729 		128, 128, 128,   0,   0,   0,   0,   0,
730 		  0, 128, 128, 128, 128, 128, 128, 128,
731 		128, 128, 128, 128, 128, 128, 128, 128,
732 		128, 128, 128, 128, 128, 128, 128, 128,
733 		128, 128, 128,   0,   0,   0,   0,   0,
734 		  0,   0,   0,   0,   0,   0,   0,   0,
735 		  0,   0,   0,   0,   0,   0,   0,   0,
736 		  0,   0,   0,   0,   0,   0,   0,   0,
737 		  0,   0,   0,   0,   0,   0,   0,   0,
738 		  0,   0,   0,   0,   0,   0,   0,   0,
739 		  0,   0,   0,   0,   0,   0,   0,   0,
740 		  0,   0,   0,   0,   0,   0,   0,   0,
741 		  0,   0,   0,   0,   0,   0,   0,   0,
742 		  0,   0,   0,   0,   0,   0,   0,   0,
743 		  0,   0,   0,   0,   0,   0,   0,   0,
744 		  0,   0,   0,   0,   0,   0,   0,   0,
745 		  0,   0,   0,   0,   0,   0,   0,   0,
746 		  0,   0,   0,   0,   0,   0,   0,   0,
747 		  0,   0,   0,   0,   0,   0,   0,   0,
748 		  0,   0,   0,   0,   0,   0,   0,   0,
749 		  0,   0,   0,   0,   0,   0,   0,   0,
750 	};
751 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
752 	yych = *YYCURSOR;
753 	if (yych <= '@') goto yy28;
754 	if (yych <= 'Z') goto yy30;
755 	if (yych <= '`') goto yy28;
756 	if (yych <= 'z') goto yy30;
757 yy28:
758 	++YYCURSOR;
759 	{ passthru(STD_ARGS); STATE = STATE_NEXT_ARG; goto state_next_arg; }
760 yy30:
761 	++YYCURSOR;
762 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
763 	yych = *YYCURSOR;
764 	if (yybm[0+yych] & 128) {
765 		goto yy30;
766 	}
767 	{ passthru(STD_ARGS); handle_arg(STD_ARGS); STATE = STATE_BEFORE_VAL; goto state_before_val; }
768 }
769 
770 
771 state_before_val:
772 	start = YYCURSOR;
773 
774 {
775 	YYCTYPE yych;
776 	static const unsigned char yybm[] = {
777 		  0,   0,   0,   0,   0,   0,   0,   0,
778 		  0,   0,   0,   0,   0,   0,   0,   0,
779 		  0,   0,   0,   0,   0,   0,   0,   0,
780 		  0,   0,   0,   0,   0,   0,   0,   0,
781 		128,   0,   0,   0,   0,   0,   0,   0,
782 		  0,   0,   0,   0,   0,   0,   0,   0,
783 		  0,   0,   0,   0,   0,   0,   0,   0,
784 		  0,   0,   0,   0,   0,   0,   0,   0,
785 		  0,   0,   0,   0,   0,   0,   0,   0,
786 		  0,   0,   0,   0,   0,   0,   0,   0,
787 		  0,   0,   0,   0,   0,   0,   0,   0,
788 		  0,   0,   0,   0,   0,   0,   0,   0,
789 		  0,   0,   0,   0,   0,   0,   0,   0,
790 		  0,   0,   0,   0,   0,   0,   0,   0,
791 		  0,   0,   0,   0,   0,   0,   0,   0,
792 		  0,   0,   0,   0,   0,   0,   0,   0,
793 		  0,   0,   0,   0,   0,   0,   0,   0,
794 		  0,   0,   0,   0,   0,   0,   0,   0,
795 		  0,   0,   0,   0,   0,   0,   0,   0,
796 		  0,   0,   0,   0,   0,   0,   0,   0,
797 		  0,   0,   0,   0,   0,   0,   0,   0,
798 		  0,   0,   0,   0,   0,   0,   0,   0,
799 		  0,   0,   0,   0,   0,   0,   0,   0,
800 		  0,   0,   0,   0,   0,   0,   0,   0,
801 		  0,   0,   0,   0,   0,   0,   0,   0,
802 		  0,   0,   0,   0,   0,   0,   0,   0,
803 		  0,   0,   0,   0,   0,   0,   0,   0,
804 		  0,   0,   0,   0,   0,   0,   0,   0,
805 		  0,   0,   0,   0,   0,   0,   0,   0,
806 		  0,   0,   0,   0,   0,   0,   0,   0,
807 		  0,   0,   0,   0,   0,   0,   0,   0,
808 		  0,   0,   0,   0,   0,   0,   0,   0,
809 	};
810 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
811 	yych = *YYCURSOR;
812 	if (yych == ' ') goto yy37;
813 	if (yych == '=') goto yy38;
814 	++YYCURSOR;
815 yy36:
816 	{ --YYCURSOR; goto state_next_arg_begin; }
817 yy37:
818 	yych = *(YYMARKER = ++YYCURSOR);
819 	if (yych == ' ') goto yy41;
820 	if (yych != '=') goto yy36;
821 yy38:
822 	++YYCURSOR;
823 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
824 	yych = *YYCURSOR;
825 	if (yybm[0+yych] & 128) {
826 		goto yy38;
827 	}
828 	{ passthru(STD_ARGS); STATE = STATE_VAL; goto state_val; }
829 yy41:
830 	++YYCURSOR;
831 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
832 	yych = *YYCURSOR;
833 	if (yych == ' ') goto yy41;
834 	if (yych == '=') goto yy38;
835 	YYCURSOR = YYMARKER;
836 	goto yy36;
837 }
838 
839 
840 
841 state_val:
842 	start = YYCURSOR;
843 
844 {
845 	YYCTYPE yych;
846 	static const unsigned char yybm[] = {
847 		224, 224, 224, 224, 224, 224, 224, 224,
848 		224, 192, 192, 224, 224, 192, 224, 224,
849 		224, 224, 224, 224, 224, 224, 224, 224,
850 		224, 224, 224, 224, 224, 224, 224, 224,
851 		192, 224, 128, 224, 224, 224, 224,  64,
852 		224, 224, 224, 224, 224, 224, 224, 224,
853 		224, 224, 224, 224, 224, 224, 224, 224,
854 		224, 224, 224, 224, 224, 224,   0, 224,
855 		224, 224, 224, 224, 224, 224, 224, 224,
856 		224, 224, 224, 224, 224, 224, 224, 224,
857 		224, 224, 224, 224, 224, 224, 224, 224,
858 		224, 224, 224, 224, 224, 224, 224, 224,
859 		224, 224, 224, 224, 224, 224, 224, 224,
860 		224, 224, 224, 224, 224, 224, 224, 224,
861 		224, 224, 224, 224, 224, 224, 224, 224,
862 		224, 224, 224, 224, 224, 224, 224, 224,
863 		224, 224, 224, 224, 224, 224, 224, 224,
864 		224, 224, 224, 224, 224, 224, 224, 224,
865 		224, 224, 224, 224, 224, 224, 224, 224,
866 		224, 224, 224, 224, 224, 224, 224, 224,
867 		224, 224, 224, 224, 224, 224, 224, 224,
868 		224, 224, 224, 224, 224, 224, 224, 224,
869 		224, 224, 224, 224, 224, 224, 224, 224,
870 		224, 224, 224, 224, 224, 224, 224, 224,
871 		224, 224, 224, 224, 224, 224, 224, 224,
872 		224, 224, 224, 224, 224, 224, 224, 224,
873 		224, 224, 224, 224, 224, 224, 224, 224,
874 		224, 224, 224, 224, 224, 224, 224, 224,
875 		224, 224, 224, 224, 224, 224, 224, 224,
876 		224, 224, 224, 224, 224, 224, 224, 224,
877 		224, 224, 224, 224, 224, 224, 224, 224,
878 		224, 224, 224, 224, 224, 224, 224, 224,
879 	};
880 	if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
881 	yych = *YYCURSOR;
882 	if (yybm[0+yych] & 32) {
883 		goto yy46;
884 	}
885 	if (yych <= ' ') goto yy49;
886 	if (yych <= '"') goto yy51;
887 	if (yych <= '\'') goto yy52;
888 	goto yy49;
889 yy46:
890 	++YYCURSOR;
891 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
892 	yych = *YYCURSOR;
893 	if (yybm[0+yych] & 32) {
894 		goto yy46;
895 	}
896 	{ handle_val(STD_ARGS, 0, ' '); goto state_next_arg_begin; }
897 yy49:
898 	++YYCURSOR;
899 yy50:
900 	{ passthru(STD_ARGS); goto state_next_arg_begin; }
901 yy51:
902 	yych = *(YYMARKER = ++YYCURSOR);
903 	if (yych == '>') goto yy50;
904 	goto yy54;
905 yy52:
906 	yych = *(YYMARKER = ++YYCURSOR);
907 	if (yych == '>') goto yy50;
908 	goto yy59;
909 yy53:
910 	++YYCURSOR;
911 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
912 	yych = *YYCURSOR;
913 yy54:
914 	if (yybm[0+yych] & 64) {
915 		goto yy53;
916 	}
917 	if (yych <= '"') goto yy56;
918 yy55:
919 	YYCURSOR = YYMARKER;
920 	goto yy50;
921 yy56:
922 	++YYCURSOR;
923 	{ handle_val(STD_ARGS, 1, '"'); goto state_next_arg_begin; }
924 yy58:
925 	++YYCURSOR;
926 	if (YYLIMIT <= YYCURSOR) YYFILL(1);
927 	yych = *YYCURSOR;
928 yy59:
929 	if (yybm[0+yych] & 128) {
930 		goto yy58;
931 	}
932 	if (yych >= '(') goto yy55;
933 	++YYCURSOR;
934 	{ handle_val(STD_ARGS, 1, '\''); goto state_next_arg_begin; }
935 }
936 
937 
938 stop:
939 	if (YYLIMIT < start) {
940 		/* XXX: Crash avoidance. Need to work with reporter to figure out what goes wrong */
941 		rest = 0;
942 	} else {
943 		rest = YYLIMIT - start;
944 		scdebug(("stopped in state %d at pos %d (%d:%c) %d\n", STATE, YYCURSOR - ctx->buf.c, *YYCURSOR, *YYCURSOR, rest));
945 	}
946 
947 	if (rest) memmove(ZSTR_VAL(ctx->buf.s), start, rest);
948 	ZSTR_LEN(ctx->buf.s) = rest;
949 }
950 
951 
php_url_scanner_adapt_single_url(const char * url,size_t urllen,const char * name,const char * value,size_t * newlen,int encode)952 PHPAPI char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen, int encode)
953 {
954 	char *result;
955 	smart_str surl = {0};
956 	smart_str buf = {0};
957 	smart_str url_app = {0};
958 	zend_string *encoded;
959 
960 	smart_str_appendl(&surl, url, urllen);
961 
962 	if (encode) {
963 		encoded = php_raw_url_encode(name, strlen(name));
964 		smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
965 		zend_string_free(encoded);
966 	} else {
967 		smart_str_appends(&url_app, name);
968 	}
969 	smart_str_appendc(&url_app, '=');
970 	if (encode) {
971 		encoded = php_raw_url_encode(value, strlen(value));
972 		smart_str_appendl(&url_app, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
973 		zend_string_free(encoded);
974 	} else {
975 		smart_str_appends(&url_app, value);
976 	}
977 
978 	append_modified_url(&surl, &buf, &url_app, PG(arg_separator).output);
979 
980 	smart_str_0(&buf);
981 	if (newlen) *newlen = ZSTR_LEN(buf.s);
982 	result = estrndup(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
983 
984 	smart_str_free(&url_app);
985 	smart_str_free(&buf);
986 
987 	return result;
988 }
989 
990 
url_adapt_ext(const char * src,size_t srclen,size_t * newlen,bool do_flush,url_adapt_state_ex_t * ctx)991 static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, bool do_flush, url_adapt_state_ex_t *ctx)
992 {
993 	char *retval;
994 
995 	xx_mainloop(ctx, src, srclen);
996 
997 	if (!ctx->result.s) {
998 		smart_str_appendl(&ctx->result, "", 0);
999 		*newlen = 0;
1000 	} else {
1001 		*newlen = ZSTR_LEN(ctx->result.s);
1002 	}
1003 	smart_str_0(&ctx->result);
1004 	if (do_flush) {
1005 		smart_str_append(&ctx->result, ctx->buf.s);
1006 		*newlen += ZSTR_LEN(ctx->buf.s);
1007 		smart_str_free(&ctx->buf);
1008 		smart_str_free(&ctx->val);
1009 		smart_str_free(&ctx->attr_val);
1010 	}
1011 	retval = estrndup(ZSTR_VAL(ctx->result.s), ZSTR_LEN(ctx->result.s));
1012 	smart_str_free(&ctx->result);
1013 	return retval;
1014 }
1015 
php_url_scanner_ex_activate(int type)1016 static int php_url_scanner_ex_activate(int type)
1017 {
1018 	url_adapt_state_ex_t *ctx;
1019 
1020 	if (type) {
1021 		ctx = &BG(url_adapt_session_ex);
1022 	} else {
1023 		ctx = &BG(url_adapt_output_ex);
1024 	}
1025 
1026 	memset(ctx, 0, XtOffsetOf(url_adapt_state_ex_t, tags));
1027 
1028 	return SUCCESS;
1029 }
1030 
php_url_scanner_ex_deactivate(int type)1031 static int php_url_scanner_ex_deactivate(int type)
1032 {
1033 	url_adapt_state_ex_t *ctx;
1034 
1035 	if (type) {
1036 		ctx = &BG(url_adapt_session_ex);
1037 	} else {
1038 		ctx = &BG(url_adapt_output_ex);
1039 	}
1040 
1041 	smart_str_free(&ctx->result);
1042 	smart_str_free(&ctx->buf);
1043 	smart_str_free(&ctx->tag);
1044 	smart_str_free(&ctx->arg);
1045 	smart_str_free(&ctx->attr_val);
1046 
1047 	return SUCCESS;
1048 }
1049 
php_url_scanner_session_handler_impl(char * output,size_t output_len,char ** handled_output,size_t * handled_output_len,int mode,int type)1050 static inline void php_url_scanner_session_handler_impl(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode, int type)
1051 {
1052 	size_t len;
1053 	url_adapt_state_ex_t *url_state;
1054 
1055 	if (type) {
1056 		url_state = &BG(url_adapt_session_ex);
1057 	} else {
1058 		url_state = &BG(url_adapt_output_ex);
1059 	}
1060 
1061 	if (ZSTR_LEN(url_state->url_app.s) != 0) {
1062 		*handled_output = url_adapt_ext(output, output_len, &len, (bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT | PHP_OUTPUT_HANDLER_FLUSH | PHP_OUTPUT_HANDLER_FINAL) ? 1 : 0), url_state);
1063 		if (sizeof(unsigned int) < sizeof(size_t)) {
1064 			if (len > UINT_MAX)
1065 				len = UINT_MAX;
1066 		}
1067 		*handled_output_len = len;
1068 	} else if (ZSTR_LEN(url_state->url_app.s) == 0) {
1069 		url_adapt_state_ex_t *ctx = url_state;
1070 		if (ctx->buf.s && ZSTR_LEN(ctx->buf.s)) {
1071 			smart_str_append(&ctx->result, ctx->buf.s);
1072 			smart_str_appendl(&ctx->result, output, output_len);
1073 
1074 			*handled_output = estrndup(ZSTR_VAL(ctx->result.s), ZSTR_LEN(ctx->result.s));
1075 			*handled_output_len = ZSTR_LEN(ctx->buf.s) + output_len;
1076 
1077 			smart_str_free(&ctx->buf);
1078 			smart_str_free(&ctx->result);
1079 		} else {
1080 			*handled_output = estrndup(output, *handled_output_len = output_len);
1081 		}
1082 	} else {
1083 		*handled_output = NULL;
1084 	}
1085 }
1086 
php_url_scanner_session_handler(char * output,size_t output_len,char ** handled_output,size_t * handled_output_len,int mode)1087 static void php_url_scanner_session_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
1088 {
1089 	php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 1);
1090 }
1091 
php_url_scanner_output_handler(char * output,size_t output_len,char ** handled_output,size_t * handled_output_len,int mode)1092 static void php_url_scanner_output_handler(char *output, size_t output_len, char **handled_output, size_t *handled_output_len, int mode)
1093 {
1094 	php_url_scanner_session_handler_impl(output, output_len, handled_output, handled_output_len, mode, 0);
1095 }
1096 
php_url_scanner_add_var_impl(const char * name,size_t name_len,const char * value,size_t value_len,int encode,int type)1097 static inline int php_url_scanner_add_var_impl(const char *name, size_t name_len, const char *value, size_t value_len, int encode, int type)
1098 {
1099 	smart_str sname = {0};
1100 	smart_str svalue = {0};
1101 	smart_str hname = {0};
1102 	smart_str hvalue = {0};
1103 	zend_string *encoded;
1104 	url_adapt_state_ex_t *url_state;
1105 	php_output_handler_func_t handler;
1106 
1107 	if (type) {
1108 		url_state = &BG(url_adapt_session_ex);
1109 		handler = php_url_scanner_session_handler;
1110 	} else {
1111 		url_state = &BG(url_adapt_output_ex);
1112 		handler = php_url_scanner_output_handler;
1113 	}
1114 
1115 	if (!url_state->active) {
1116 		php_url_scanner_ex_activate(type);
1117 		php_output_start_internal(ZEND_STRL("URL-Rewriter"), handler, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
1118 		url_state->active = 1;
1119 	}
1120 
1121 	if (url_state->url_app.s && ZSTR_LEN(url_state->url_app.s) != 0) {
1122 		smart_str_appends(&url_state->url_app, PG(arg_separator).output);
1123 	}
1124 
1125 	if (encode) {
1126 		encoded = php_raw_url_encode(name, name_len);
1127 		smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
1128 		encoded = php_raw_url_encode(value, value_len);
1129 		smart_str_appendl(&svalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
1130 		encoded = php_escape_html_entities_ex((const unsigned char *) name, name_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, NULL, /* double_encode */ 0, /* quiet */ 1);
1131 		smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
1132 		encoded = php_escape_html_entities_ex((const unsigned char *) value, value_len, 0, ENT_QUOTES|ENT_SUBSTITUTE, NULL, /* double_encode */ 0, /* quiet */ 1);
1133 		smart_str_appendl(&hvalue, ZSTR_VAL(encoded), ZSTR_LEN(encoded)); zend_string_free(encoded);
1134 	} else {
1135 		smart_str_appendl(&sname, name, name_len);
1136 		smart_str_appendl(&svalue, value, value_len);
1137 		smart_str_appendl(&hname, name, name_len);
1138 		smart_str_appendl(&hvalue, value, value_len);
1139 	}
1140 
1141 	smart_str_append_smart_str(&url_state->url_app, &sname);
1142 	smart_str_appendc(&url_state->url_app, '=');
1143 	smart_str_append_smart_str(&url_state->url_app, &svalue);
1144 
1145 	smart_str_appends(&url_state->form_app, "<input type=\"hidden\" name=\"");
1146 	smart_str_append_smart_str(&url_state->form_app, &hname);
1147 	smart_str_appends(&url_state->form_app, "\" value=\"");
1148 	smart_str_append_smart_str(&url_state->form_app, &hvalue);
1149 	smart_str_appends(&url_state->form_app, "\" />");
1150 
1151 	smart_str_free(&sname);
1152 	smart_str_free(&svalue);
1153 	smart_str_free(&hname);
1154 	smart_str_free(&hvalue);
1155 
1156 	return SUCCESS;
1157 }
1158 
1159 
php_url_scanner_add_session_var(const char * name,size_t name_len,const char * value,size_t value_len,int encode)1160 PHPAPI int php_url_scanner_add_session_var(const char *name, size_t name_len, const char *value, size_t value_len, int encode)
1161 {
1162 	return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 1);
1163 }
1164 
1165 
php_url_scanner_add_var(const char * name,size_t name_len,const char * value,size_t value_len,int encode)1166 PHPAPI int php_url_scanner_add_var(const char *name, size_t name_len, const char *value, size_t value_len, int encode)
1167 {
1168 	return php_url_scanner_add_var_impl(name, name_len, value, value_len, encode, 0);
1169 }
1170 
1171 
php_url_scanner_reset_vars_impl(int type)1172 static inline void php_url_scanner_reset_vars_impl(int type) {
1173 	url_adapt_state_ex_t *url_state;
1174 
1175 	if (type) {
1176 		url_state = &BG(url_adapt_session_ex);
1177 	} else {
1178 		url_state = &BG(url_adapt_output_ex);
1179 	}
1180 
1181 	if (url_state->form_app.s) {
1182 		ZSTR_LEN(url_state->form_app.s) = 0;
1183 	}
1184 	if (url_state->url_app.s) {
1185 		ZSTR_LEN(url_state->url_app.s) = 0;
1186 	}
1187 }
1188 
1189 
php_url_scanner_reset_session_vars(void)1190 PHPAPI int php_url_scanner_reset_session_vars(void)
1191 {
1192 	php_url_scanner_reset_vars_impl(1);
1193 	return SUCCESS;
1194 }
1195 
1196 
php_url_scanner_reset_vars(void)1197 PHPAPI int php_url_scanner_reset_vars(void)
1198 {
1199 	php_url_scanner_reset_vars_impl(0);
1200 	return SUCCESS;
1201 }
1202 
1203 
php_url_scanner_reset_var_impl(zend_string * name,int encode,int type)1204 static inline int php_url_scanner_reset_var_impl(zend_string *name, int encode, int type)
1205 {
1206 	char *start, *end, *limit;
1207 	size_t separator_len;
1208 	smart_str sname = {0};
1209 	smart_str hname = {0};
1210 	smart_str url_app = {0};
1211 	smart_str form_app = {0};
1212 	zend_string *encoded;
1213 	int ret = SUCCESS;
1214 	bool sep_removed = 0;
1215 	url_adapt_state_ex_t *url_state;
1216 
1217 	if (type) {
1218 		url_state = &BG(url_adapt_session_ex);
1219 	} else {
1220 		url_state = &BG(url_adapt_output_ex);
1221 	}
1222 
1223 	/* Short circuit check. Only check url_app. */
1224 	if (!url_state->url_app.s || !ZSTR_LEN(url_state->url_app.s)) {
1225 		return SUCCESS;
1226 	}
1227 
1228 	if (encode) {
1229 		encoded = php_raw_url_encode(ZSTR_VAL(name), ZSTR_LEN(name));
1230 		smart_str_appendl(&sname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
1231 		zend_string_free(encoded);
1232 		encoded = php_escape_html_entities_ex((const unsigned char *) ZSTR_VAL(name), ZSTR_LEN(name), 0, ENT_QUOTES|ENT_SUBSTITUTE, SG(default_charset), /* double_encode */ 0, /* quiet */ 1);
1233 		smart_str_appendl(&hname, ZSTR_VAL(encoded), ZSTR_LEN(encoded));
1234 		zend_string_free(encoded);
1235 	} else {
1236 		smart_str_appendl(&sname, ZSTR_VAL(name), ZSTR_LEN(name));
1237 		smart_str_appendl(&hname, ZSTR_VAL(name), ZSTR_LEN(name));
1238 	}
1239 	smart_str_0(&sname);
1240 	smart_str_0(&hname);
1241 
1242 	smart_str_append_smart_str(&url_app, &sname);
1243 	smart_str_appendc(&url_app, '=');
1244 	smart_str_0(&url_app);
1245 
1246 	smart_str_appends(&form_app, "<input type=\"hidden\" name=\"");
1247 	smart_str_append_smart_str(&form_app, &hname);
1248 	smart_str_appends(&form_app, "\" value=\"");
1249 	smart_str_0(&form_app);
1250 
1251 	/* Short circuit check. Only check url_app. */
1252 	start = (char *) php_memnstr(ZSTR_VAL(url_state->url_app.s),
1253 								 ZSTR_VAL(url_app.s), ZSTR_LEN(url_app.s),
1254 								 ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s));
1255 	if (!start) {
1256 		ret = FAILURE;
1257 		goto finish;
1258 	}
1259 
1260 	/* Get end of url var */
1261 	limit = ZSTR_VAL(url_state->url_app.s) + ZSTR_LEN(url_state->url_app.s);
1262 	end = start + ZSTR_LEN(url_app.s);
1263 	separator_len = strlen(PG(arg_separator).output);
1264 	while (end < limit) {
1265 		if (!memcmp(end, PG(arg_separator).output, separator_len)) {
1266 			end += separator_len;
1267 			sep_removed = 1;
1268 			break;
1269 		}
1270 		end++;
1271 	}
1272 	/* Remove all when this is the only rewrite var */
1273 	if (ZSTR_LEN(url_state->url_app.s) == end - start) {
1274 		php_url_scanner_reset_vars_impl(type);
1275 		goto finish;
1276 	}
1277 	/* Check preceding separator */
1278 	if (!sep_removed
1279 		&& (size_t)(start - PG(arg_separator).output) >= separator_len
1280 		&& !memcmp(start - separator_len, PG(arg_separator).output, separator_len)) {
1281 		start -= separator_len;
1282 	}
1283 	/* Remove partially */
1284 	memmove(start, end,
1285 			ZSTR_LEN(url_state->url_app.s) - (end - ZSTR_VAL(url_state->url_app.s)));
1286 	ZSTR_LEN(url_state->url_app.s) -= end - start;
1287 	ZSTR_VAL(url_state->url_app.s)[ZSTR_LEN(url_state->url_app.s)] = '\0';
1288 
1289 	/* Remove form var */
1290 	start = (char *) php_memnstr(ZSTR_VAL(url_state->form_app.s),
1291 						ZSTR_VAL(form_app.s), ZSTR_LEN(form_app.s),
1292 						ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s));
1293 	if (!start) {
1294 		/* Should not happen */
1295 		ret = FAILURE;
1296 		php_url_scanner_reset_vars_impl(type);
1297 		goto finish;
1298 	}
1299 	/* Get end of form var */
1300 	limit = ZSTR_VAL(url_state->form_app.s) + ZSTR_LEN(url_state->form_app.s);
1301 	end = start + ZSTR_LEN(form_app.s);
1302 	while (end < limit) {
1303 		if (*end == '>') {
1304 			end += 1;
1305 			break;
1306 		}
1307 		end++;
1308 	}
1309 	/* Remove partially */
1310 	memmove(start, end,
1311 			ZSTR_LEN(url_state->form_app.s) - (end - ZSTR_VAL(url_state->form_app.s)));
1312 	ZSTR_LEN(url_state->form_app.s) -= end - start;
1313 	ZSTR_VAL(url_state->form_app.s)[ZSTR_LEN(url_state->form_app.s)] = '\0';
1314 
1315 finish:
1316 	smart_str_free(&url_app);
1317 	smart_str_free(&form_app);
1318 	smart_str_free(&sname);
1319 	smart_str_free(&hname);
1320 	return ret;
1321 }
1322 
1323 
php_url_scanner_reset_session_var(zend_string * name,int encode)1324 PHPAPI int php_url_scanner_reset_session_var(zend_string *name, int encode)
1325 {
1326 	return php_url_scanner_reset_var_impl(name, encode, 1);
1327 }
1328 
1329 
php_url_scanner_reset_var(zend_string * name,int encode)1330 PHPAPI int php_url_scanner_reset_var(zend_string *name, int encode)
1331 {
1332 	return php_url_scanner_reset_var_impl(name, encode, 0);
1333 }
1334 
1335 
PHP_MINIT_FUNCTION(url_scanner)1336 PHP_MINIT_FUNCTION(url_scanner)
1337 {
1338 	REGISTER_INI_ENTRIES();
1339 	return SUCCESS;
1340 }
1341 
PHP_MSHUTDOWN_FUNCTION(url_scanner)1342 PHP_MSHUTDOWN_FUNCTION(url_scanner)
1343 {
1344 	UNREGISTER_INI_ENTRIES();
1345 
1346 	return SUCCESS;
1347 }
1348 
PHP_RINIT_FUNCTION(url_scanner)1349 PHP_RINIT_FUNCTION(url_scanner)
1350 {
1351 	BG(url_adapt_session_ex).active    = 0;
1352 	BG(url_adapt_session_ex).tag_type  = 0;
1353 	BG(url_adapt_session_ex).attr_type = 0;
1354 	BG(url_adapt_output_ex).active    = 0;
1355 	BG(url_adapt_output_ex).tag_type  = 0;
1356 	BG(url_adapt_output_ex).attr_type = 0;
1357 	return SUCCESS;
1358 }
1359 
PHP_RSHUTDOWN_FUNCTION(url_scanner)1360 PHP_RSHUTDOWN_FUNCTION(url_scanner)
1361 {
1362 	if (BG(url_adapt_session_ex).active) {
1363 		php_url_scanner_ex_deactivate(1);
1364 		BG(url_adapt_session_ex).active    = 0;
1365 		BG(url_adapt_session_ex).tag_type  = 0;
1366 		BG(url_adapt_session_ex).attr_type = 0;
1367 	}
1368 	smart_str_free(&BG(url_adapt_session_ex).form_app);
1369 	smart_str_free(&BG(url_adapt_session_ex).url_app);
1370 
1371 	if (BG(url_adapt_output_ex).active) {
1372 		php_url_scanner_ex_deactivate(0);
1373 		BG(url_adapt_output_ex).active    = 0;
1374 		BG(url_adapt_output_ex).tag_type  = 0;
1375 		BG(url_adapt_output_ex).attr_type = 0;
1376 	}
1377 	smart_str_free(&BG(url_adapt_output_ex).form_app);
1378 	smart_str_free(&BG(url_adapt_output_ex).url_app);
1379 
1380 	return SUCCESS;
1381 }
1382