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