xref: /PHP-7.2/ext/standard/url_scanner_ex.c (revision 40bd84d3)
1 /* Generated by re2c 0.16 */
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 /* $Id$ */
23 
24 #include "php.h"
25 
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifdef HAVE_LIMITS_H
30 #include <limits.h>
31 #endif
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "SAPI.h"
38 #include "php_ini.h"
39 #include "php_globals.h"
40 #include "php_string.h"
41 #define STATE_TAG SOME_OTHER_STATE_TAG
42 #include "basic_functions.h"
43 #include "url.h"
44 #include "html.h"
45 #undef STATE_TAG
46 
47 #define url_scanner url_scanner_ex
48 
49 #include "zend_smart_str.h"
50 
tag_dtor(zval * zv)51 static void tag_dtor(zval *zv)
52 {
53 	free(Z_PTR_P(zv));
54 }
55 
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)56 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)
57 {
58 	url_adapt_state_ex_t *ctx;
59 	char *key;
60 	char *tmp;
61 	char *lasts = NULL;
62 
63 	if (type) {
64 		ctx = &BG(url_adapt_session_ex);
65 	} else {
66 		ctx = &BG(url_adapt_output_ex);
67 	}
68 
69 	tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
70 
71 	if (ctx->tags)
72 		zend_hash_destroy(ctx->tags);
73 	else {
74 		ctx->tags = malloc(sizeof(HashTable));
75 		if (!ctx->tags) {
76 			efree(tmp);
77 			return FAILURE;
78 		}
79 	}
80 
81 	zend_hash_init(ctx->tags, 0, NULL, tag_dtor, 1);
82 
83 	for (key = php_strtok_r(tmp, ",", &lasts);
84 		 key;
85 		 key = php_strtok_r(NULL, ",", &lasts)) {
86 		char *val;
87 
88 		val = strchr(key, '=');
89 		if (val) {
90 			char *q;
91 			size_t keylen;
92 
93 			*val++ = '\0';
94 			for (q = key; *q; q++) {
95 				*q = tolower(*q);
96 			}
97 			keylen = q - key;
98 			zend_hash_str_add_mem(ctx->tags, key, keylen, val, strlen(val)+1);
99 		}
100 	}
101 
102 	efree(tmp);
103 
104 	return SUCCESS;
105 }
106 
PHP_INI_MH(OnUpdateSessionTags)107 static PHP_INI_MH(OnUpdateSessionTags)
108 {
109 	return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
110 }
111 
PHP_INI_MH(OnUpdateOutputTags)112 static PHP_INI_MH(OnUpdateOutputTags)
113 {
114 	return php_ini_on_update_tags(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
115 }
116 
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)117 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)
118 {
119 	HashTable *hosts;
120 	char *key;
121 	char *tmp;
122 	char *lasts = NULL;
123 
124 	if (type) {
125 		hosts = &BG(url_adapt_session_hosts_ht);
126 	} else {
127 		hosts = &BG(url_adapt_output_hosts_ht);
128 	}
129 	zend_hash_clean(hosts);
130 
131 	/* Use user supplied host whitelist */
132 	tmp = estrndup(ZSTR_VAL(new_value), ZSTR_LEN(new_value));
133 	for (key = php_strtok_r(tmp, ",", &lasts);
134 		 key;
135 		 key = php_strtok_r(NULL, ",", &lasts)) {
136 		size_t keylen;
137 		zend_string *tmp_key;
138 		char *q;
139 
140 		for (q = key; *q; q++) {
141 			*q = tolower(*q);
142 		}
143 		keylen = q - key;
144 		if (keylen > 0) {
145 			tmp_key = zend_string_init(key, keylen, 0);
146 			zend_hash_add_empty_element(hosts, tmp_key);
147 			zend_string_release(tmp_key);
148 		}
149 	}
150 	efree(tmp);
151 
152 	return SUCCESS;
153 }
154 
PHP_INI_MH(OnUpdateSessionHosts)155 static PHP_INI_MH(OnUpdateSessionHosts)
156 {
157 	return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 1);
158 }
159 
PHP_INI_MH(OnUpdateOutputHosts)160 static PHP_INI_MH(OnUpdateOutputHosts)
161 {
162 	return php_ini_on_update_hosts(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage, 0);
163 }
164 
165 /* FIXME: OnUpdate*Hosts cannot set default to $_SERVER['HTTP_HOST'] at startup */
166 PHP_INI_BEGIN()
167 	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)
168 	STD_PHP_INI_ENTRY("session.trans_sid_hosts", "", PHP_INI_ALL, OnUpdateSessionHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
169 	STD_PHP_INI_ENTRY("url_rewriter.tags", "form=", PHP_INI_ALL, OnUpdateOutputTags, url_adapt_session_ex, php_basic_globals, basic_globals)
170 	STD_PHP_INI_ENTRY("url_rewriter.hosts", "", PHP_INI_ALL, OnUpdateOutputHosts, url_adapt_session_hosts_ht, php_basic_globals, basic_globals)
PHP_INI_END()171 PHP_INI_END()
172 
173 #line 177 "ext/standard/url_scanner_ex.re"
174 
175 
176 #define YYFILL(n) goto done
177 #define YYCTYPE unsigned char
178 #define YYCURSOR p
179 #define YYLIMIT q
180 #define YYMARKER r
181 
182 static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator)
183 {
184 	php_url *url_parts;
185 	char *tmp;
186 	size_t tmp_len;
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 		&& strcasecmp("http", url_parts->scheme)
207 		&& strcasecmp("https", url_parts->scheme)) {
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 		&& (tmp_len = strlen(url_parts->host))
216 		&& (tmp = php_strtolower(url_parts->host, tmp_len))
217 		&& !zend_hash_str_find(&BG(url_adapt_session_hosts_ht), tmp, tmp_len)) {
218 		smart_str_append_smart_str(dest, url);
219 		php_url_free(url_parts);
220 		return;
221 	}
222 
223 	/*
224 	 * When URL does not have path and query string add "/?".
225 	 * i.e. If URL is only "?foo=bar", should not add "/?".
226 	 */
227 	if (!url_parts->path && !url_parts->query && !url_parts->fragment) {
228 		/* URL is http://php.net or like */
229 		smart_str_append_smart_str(dest, url);
230 		smart_str_appendc(dest, '/');
231 		smart_str_appendc(dest, '?');
232 		smart_str_append_smart_str(dest, url_app);
233 		php_url_free(url_parts);
234 		return;
235 	}
236 
237 	if (url_parts->scheme) {
238 		smart_str_appends(dest, url_parts->scheme);
239 		smart_str_appends(dest, "://");
240 	} else if (*(ZSTR_VAL(url->s)) == '/' && *(ZSTR_VAL(url->s)+1) == '/') {
241 		smart_str_appends(dest, "//");
242 	}
243 	if (url_parts->user) {
244 		smart_str_appends(dest, url_parts->user);
245 		if (url_parts->pass) {
246 			smart_str_appends(dest, url_parts->pass);
247 			smart_str_appendc(dest, ':');
248 		}
249 		smart_str_appendc(dest, '@');
250 	}
251 	if (url_parts->host) {
252 				smart_str_appends(dest, url_parts->host);
253 	}
254 	if (url_parts->port) {
255 		smart_str_appendc(dest, ':');
256 		smart_str_append_unsigned(dest, (long)url_parts->port);
257 	}
258 	if (url_parts->path) {
259 		smart_str_appends(dest, url_parts->path);
260 	}
261 	smart_str_appendc(dest, '?');
262 	if (url_parts->query) {
263 		smart_str_appends(dest, url_parts->query);
264 		smart_str_appends(dest, separator);
265 		smart_str_append_smart_str(dest, url_app);
266 	} else {
267 		smart_str_append_smart_str(dest, url_app);
268 	}
269 	if (url_parts->fragment) {
270 		smart_str_appendc(dest, '#');
271 		smart_str_appends(dest, url_parts->fragment);
272 	}
273 	php_url_free(url_parts);
274 }
275 
276 enum {
277 	TAG_NORMAL = 0,
278 	TAG_FORM
279 };
280 
281 enum {
282 	ATTR_NORMAL = 0,
283 	ATTR_ACTION
284 };
285 
286 #undef YYFILL
287 #undef YYCTYPE
288 #undef YYCURSOR
289 #undef YYLIMIT
290 #undef YYMARKER
291 
tag_arg(url_adapt_state_ex_t * ctx,char quotes,char type)292 static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type)
293 {
294 	char f = 0;
295 
296 	/* arg.s is string WITHOUT NUL.
297 	   To avoid partial match, NUL is added here */
298 	ZSTR_VAL(ctx->arg.s)[ZSTR_LEN(ctx->arg.s)] = '\0';
299 	if (!strcasecmp(ZSTR_VAL(ctx->arg.s), ctx->lookup_data)) {
300 		f = 1;
301 	}
302 
303 	if (quotes) {
304 		smart_str_appendc(&ctx->result, type);
305 	}
306 	if (f) {
307 		append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output);
308 	} else {
309 		smart_str_append_smart_str(&ctx->result, &ctx->val);
310 	}
311 	if (quotes) {
312 		smart_str_appendc(&ctx->result, type);
313 	}
314 }
315 
316 enum {
317 	STATE_PLAIN = 0,
318 	STATE_TAG,
319 	STATE_NEXT_ARG,
320 	STATE_ARG,
321 	STATE_BEFORE_VAL,
322 	STATE_VAL
323 };
324 
325 #define YYFILL(n) goto stop
326 #define YYCTYPE unsigned char
327 #define YYCURSOR xp
328 #define YYLIMIT end
329 #define YYMARKER q
330 #define STATE ctx->state
331 
332 #define STD_PARA url_adapt_state_ex_t *ctx, char *start, char *YYCURSOR
333 #define STD_ARGS ctx, start, xp
334 
335 #if SCANNER_DEBUG
336 #define scdebug(x) printf x
337 #else
338 #define scdebug(x)
339 #endif
340 
passthru(STD_PARA)341 static inline void passthru(STD_PARA)
342 {
343 	scdebug(("appending %d chars, starting with %c\n", YYCURSOR-start, *start));
344 	smart_str_appendl(&ctx->result, start, YYCURSOR - start);
345 }
346 
347 
check_http_host(char * target)348 static int check_http_host(char *target)
349 {
350 	zval *host, *tmp;
351 	zend_string *host_tmp;
352 	char *colon;
353 
354 	if ((tmp  = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"))) &&
355 		Z_TYPE_P(tmp) == IS_ARRAY &&
356 		(host = zend_hash_str_find(Z_ARRVAL_P(tmp), ZEND_STRL("HTTP_HOST"))) &&
357 		Z_TYPE_P(host) == IS_STRING) {
358 		host_tmp = zend_string_init(Z_STRVAL_P(host), Z_STRLEN_P(host), 0);
359 		/* HTTP_HOST could be 'localhost:8888' etc. */
360 		colon = strchr(ZSTR_VAL(host_tmp), ':');
361 		if (colon) {
362 			ZSTR_LEN(host_tmp) = colon - ZSTR_VAL(host_tmp);
363 			ZSTR_VAL(host_tmp)[ZSTR_LEN(host_tmp)] = '\0';
364 		}
365 		if (!strcasecmp(ZSTR_VAL(host_tmp), target)) {
366 			zend_string_release(host_tmp);
367 			return SUCCESS;
368 		}
369 		zend_string_release(host_tmp);
370 	}
371 	return FAILURE;
372 }
373 
check_host_whitelist(url_adapt_state_ex_t * ctx)374 static int check_host_whitelist(url_adapt_state_ex_t *ctx)
375 {
376 	php_url *url_parts = NULL;
377 	HashTable *allowed_hosts = ctx->type ? &BG(url_adapt_session_hosts_ht) : &BG(url_adapt_output_hosts_ht);
378 
379 	ZEND_ASSERT(ctx->tag_type == TAG_FORM);
380 
381 	if (ctx->attr_val.s && ZSTR_LEN(ctx->attr_val.s)) {
382 		url_parts = php_url_parse_ex(ZSTR_VAL(ctx->attr_val.s), ZSTR_LEN(ctx->attr_val.s));
383 	} else {
384 		return SUCCESS; /* empty URL is valid */
385 	}
386 
387 	if (!url_parts) {
388 		return FAILURE;
389 	}
390 	if (url_parts->scheme) {
391 		/* Only http/https should be handled.
392 		   A bit hacky check this here, but saves a URL parse. */
393 		if (strcasecmp(url_parts->scheme, "http") &&
394 			strcasecmp(url_parts->scheme, "https")) {
395 		php_url_free(url_parts);
396 		return FAILURE;
397 		}
398 	}
399 	if (!url_parts->host) {
400 		php_url_free(url_parts);
401 		return SUCCESS;
402 	}
403 	if (!zend_hash_num_elements(allowed_hosts) &&
404 		check_http_host(url_parts->host) == SUCCESS) {
405 		php_url_free(url_parts);
406 		return SUCCESS;
407 	}
408 	if (!zend_hash_str_find(allowed_hosts,
409 							url_parts->host,
410 							strlen(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(uint32_t) < 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 preceeding separator */
1328 	if (!sep_removed
1329 		&& 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