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