1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2006 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Sascha Schumann <sascha@schumann.cx> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: url_scanner_ex.re 286379 2009-07-26 23:20:34Z jani $ */
20 :
21 : #include "php.h"
22 :
23 : #ifdef HAVE_UNISTD_H
24 : #include <unistd.h>
25 : #endif
26 : #ifdef HAVE_LIMITS_H
27 : #include <limits.h>
28 : #endif
29 :
30 : #include <stdio.h>
31 : #include <stdlib.h>
32 : #include <string.h>
33 :
34 : #include "php_ini.h"
35 : #include "php_globals.h"
36 : #define STATE_TAG SOME_OTHER_STATE_TAG
37 : #include "basic_functions.h"
38 : #include "url.h"
39 : #undef STATE_TAG
40 :
41 : #define url_scanner url_scanner_ex
42 :
43 : #include "php_smart_str.h"
44 :
45 : static PHP_INI_MH(OnUpdateTags)
46 13569 : {
47 : url_adapt_state_ex_t *ctx;
48 : char *key;
49 : char *lasts;
50 : char *tmp;
51 :
52 13569 : ctx = &BG(url_adapt_state_ex);
53 :
54 13569 : tmp = estrndup(new_value, new_value_length);
55 :
56 13569 : if (ctx->tags)
57 4 : zend_hash_destroy(ctx->tags);
58 : else
59 13565 : ctx->tags = malloc(sizeof(HashTable));
60 :
61 13569 : zend_hash_init(ctx->tags, 0, NULL, NULL, 1);
62 :
63 13569 : for (key = php_strtok_r(tmp, ",", &lasts);
64 94986 : key;
65 67848 : key = php_strtok_r(NULL, ",", &lasts)) {
66 : char *val;
67 :
68 67848 : val = strchr(key, '=');
69 67848 : if (val) {
70 : char *q;
71 : int keylen;
72 :
73 67848 : *val++ = '\0';
74 366376 : for (q = key; *q; q++)
75 298528 : *q = tolower(*q);
76 67848 : keylen = q - key;
77 : /* key is stored withOUT NUL
78 : val is stored WITH NUL */
79 67848 : zend_hash_add(ctx->tags, key, keylen, val, strlen(val)+1, NULL);
80 : }
81 : }
82 :
83 13569 : efree(tmp);
84 :
85 13569 : return SUCCESS;
86 : }
87 :
88 : PHP_INI_BEGIN()
89 : STD_PHP_INI_ENTRY("url_rewriter.tags", "a=href,area=href,frame=src,form=,fieldset=", PHP_INI_ALL, OnUpdateTags, url_adapt_state_ex, php_basic_globals, basic_globals)
90 : PHP_INI_END()
91 :
92 : /*!re2c
93 : any = [\000-\377];
94 : N = (any\[<]);
95 : alpha = [a-zA-Z];
96 : alphanamespace = [a-zA-Z:];
97 : alphadash = ([a-zA-Z] | "-");
98 : */
99 :
100 : #define YYFILL(n) goto done
101 : #define YYCTYPE unsigned char
102 : #define YYCURSOR p
103 : #define YYLIMIT q
104 : #define YYMARKER r
105 :
106 : static inline void append_modified_url(smart_str *url, smart_str *dest, smart_str *url_app, const char *separator)
107 11 : {
108 : register const char *p, *q;
109 11 : const char *bash = NULL;
110 11 : const char *sep = "?";
111 :
112 11 : q = (p = url->c) + url->len;
113 :
114 25 : scan:
115 : /*!re2c
116 0 : ":" { smart_str_append(dest, url); return; }
117 7 : "?" { sep = separator; goto scan; }
118 0 : "#" { bash = p - 1; goto done; }
119 7 : (any\[:?#])+ { goto scan; }
120 : */
121 11 : done:
122 :
123 : /* Don't modify URLs of the format "#mark" */
124 11 : if (bash && bash - url->c == 0) {
125 0 : smart_str_append(dest, url);
126 0 : return;
127 : }
128 :
129 11 : if (bash)
130 0 : smart_str_appendl(dest, url->c, bash - url->c);
131 : else
132 11 : smart_str_append(dest, url);
133 :
134 11 : smart_str_appends(dest, sep);
135 11 : smart_str_append(dest, url_app);
136 :
137 11 : if (bash)
138 0 : smart_str_appendl(dest, bash, q - bash);
139 : }
140 :
141 :
142 : #undef YYFILL
143 : #undef YYCTYPE
144 : #undef YYCURSOR
145 : #undef YYLIMIT
146 : #undef YYMARKER
147 :
148 : static inline void tag_arg(url_adapt_state_ex_t *ctx, char quotes, char type TSRMLS_DC)
149 15 : {
150 15 : char f = 0;
151 :
152 15 : if (strncasecmp(ctx->arg.c, ctx->lookup_data, ctx->arg.len) == 0)
153 11 : f = 1;
154 :
155 15 : if (quotes)
156 14 : smart_str_appendc(&ctx->result, type);
157 15 : if (f) {
158 11 : append_modified_url(&ctx->val, &ctx->result, &ctx->url_app, PG(arg_separator).output);
159 : } else {
160 4 : smart_str_append(&ctx->result, &ctx->val);
161 : }
162 15 : if (quotes)
163 14 : smart_str_appendc(&ctx->result, type);
164 15 : }
165 :
166 : enum {
167 : STATE_PLAIN = 0,
168 : STATE_TAG,
169 : STATE_NEXT_ARG,
170 : STATE_ARG,
171 : STATE_BEFORE_VAL,
172 : STATE_VAL
173 : };
174 :
175 : #define YYFILL(n) goto stop
176 : #define YYCTYPE unsigned char
177 : #define YYCURSOR xp
178 : #define YYLIMIT end
179 : #define YYMARKER q
180 : #define STATE ctx->state
181 :
182 : #define STD_PARA url_adapt_state_ex_t *ctx, char *start, char *YYCURSOR TSRMLS_DC
183 : #define STD_ARGS ctx, start, xp TSRMLS_CC
184 :
185 : #if SCANNER_DEBUG
186 : #define scdebug(x) printf x
187 : #else
188 : #define scdebug(x)
189 : #endif
190 :
191 : static inline void passthru(STD_PARA)
192 160 : {
193 : scdebug(("appending %d chars, starting with %c\n", YYCURSOR-start, *start));
194 160 : smart_str_appendl(&ctx->result, start, YYCURSOR - start);
195 160 : }
196 :
197 : /*
198 : * This function appends a hidden input field after a <form> or
199 : * <fieldset>. The latter is important for XHTML.
200 : */
201 :
202 : static void handle_form(STD_PARA)
203 17 : {
204 17 : int doit = 0;
205 :
206 17 : if (ctx->form_app.len > 0) {
207 17 : switch (ctx->tag.len) {
208 : case sizeof("form") - 1:
209 4 : if (!strncasecmp(ctx->tag.c, "form", sizeof("form") - 1)) {
210 4 : doit = 1;
211 : }
212 4 : if (doit && ctx->val.c && ctx->lookup_data && *ctx->lookup_data) {
213 0 : char *e, *p = zend_memnstr(ctx->val.c, "://", sizeof("://") - 1, ctx->val.c + ctx->val.len);
214 0 : if (p) {
215 0 : e = memchr(p, '/', (ctx->val.c + ctx->val.len) - p);
216 0 : if (!e) {
217 0 : e = ctx->val.c + ctx->val.len;
218 : }
219 0 : if ((e - p) && strncasecmp(p, ctx->lookup_data, (e - p))) {
220 0 : doit = 0;
221 : }
222 : }
223 : }
224 4 : break;
225 :
226 : case sizeof("fieldset") - 1:
227 2 : if (!strncasecmp(ctx->tag.c, "fieldset", sizeof("fieldset") - 1)) {
228 2 : doit = 1;
229 : }
230 : break;
231 : }
232 :
233 17 : if (doit)
234 6 : smart_str_append(&ctx->result, &ctx->form_app);
235 : }
236 17 : }
237 :
238 : /*
239 : * HANDLE_TAG copies the HTML Tag and checks whether we
240 : * have that tag in our table. If we might modify it,
241 : * we continue to scan the tag, otherwise we simply copy the complete
242 : * HTML stuff to the result buffer.
243 : */
244 :
245 : static inline void handle_tag(STD_PARA)
246 27 : {
247 27 : int ok = 0;
248 : int i;
249 :
250 27 : ctx->tag.len = 0;
251 27 : smart_str_appendl(&ctx->tag, start, YYCURSOR - start);
252 112 : for (i = 0; i < ctx->tag.len; i++)
253 85 : ctx->tag.c[i] = tolower((int)(unsigned char)ctx->tag.c[i]);
254 27 : if (zend_hash_find(ctx->tags, ctx->tag.c, ctx->tag.len, (void **) &ctx->lookup_data) == SUCCESS)
255 17 : ok = 1;
256 27 : STATE = ok ? STATE_NEXT_ARG : STATE_PLAIN;
257 27 : }
258 :
259 : static inline void handle_arg(STD_PARA)
260 15 : {
261 15 : ctx->arg.len = 0;
262 15 : smart_str_appendl(&ctx->arg, start, YYCURSOR - start);
263 15 : }
264 :
265 : static inline void handle_val(STD_PARA, char quotes, char type)
266 15 : {
267 15 : smart_str_setl(&ctx->val, start + quotes, YYCURSOR - start - quotes * 2);
268 15 : tag_arg(ctx, quotes, type TSRMLS_CC);
269 15 : }
270 :
271 : static inline void xx_mainloop(url_adapt_state_ex_t *ctx, const char *newdata, size_t newlen TSRMLS_DC)
272 15 : {
273 : char *end, *q;
274 : char *xp;
275 : char *start;
276 : int rest;
277 :
278 15 : smart_str_appendl(&ctx->buf, newdata, newlen);
279 :
280 15 : YYCURSOR = ctx->buf.c;
281 15 : YYLIMIT = ctx->buf.c + ctx->buf.len;
282 :
283 15 : switch (STATE) {
284 15 : case STATE_PLAIN: goto state_plain;
285 0 : case STATE_TAG: goto state_tag;
286 0 : case STATE_NEXT_ARG: goto state_next_arg;
287 0 : case STATE_ARG: goto state_arg;
288 0 : case STATE_BEFORE_VAL: goto state_before_val;
289 0 : case STATE_VAL: goto state_val;
290 : }
291 :
292 :
293 27 : state_plain_begin:
294 27 : STATE = STATE_PLAIN;
295 :
296 76 : state_plain:
297 76 : start = YYCURSOR;
298 : /*!re2c
299 37 : "<" { passthru(STD_ARGS); STATE = STATE_TAG; goto state_tag; }
300 24 : N+ { passthru(STD_ARGS); goto state_plain; }
301 : */
302 :
303 37 : state_tag:
304 37 : start = YYCURSOR;
305 : /*!re2c
306 27 : alphanamespace+ { handle_tag(STD_ARGS); /* Sets STATE */; passthru(STD_ARGS); if (STATE == STATE_PLAIN) goto state_plain; else goto state_next_arg; }
307 10 : any { passthru(STD_ARGS); goto state_plain_begin; }
308 : */
309 :
310 15 : state_next_arg_begin:
311 15 : STATE = STATE_NEXT_ARG;
312 :
313 47 : state_next_arg:
314 47 : start = YYCURSOR;
315 : /*!re2c
316 17 : ">" { passthru(STD_ARGS); handle_form(STD_ARGS); goto state_plain_begin; }
317 15 : [ \v\r\t\n]+ { passthru(STD_ARGS); goto state_next_arg; }
318 15 : alpha { --YYCURSOR; STATE = STATE_ARG; goto state_arg; }
319 0 : any { passthru(STD_ARGS); goto state_plain_begin; }
320 : */
321 :
322 15 : state_arg:
323 15 : start = YYCURSOR;
324 : /*!re2c
325 15 : alpha alphadash* { passthru(STD_ARGS); handle_arg(STD_ARGS); STATE = STATE_BEFORE_VAL; goto state_before_val; }
326 0 : any { passthru(STD_ARGS); STATE = STATE_NEXT_ARG; goto state_next_arg; }
327 : */
328 :
329 15 : state_before_val:
330 15 : start = YYCURSOR;
331 : /*!re2c
332 15 : [ ]* "=" [ ]* { passthru(STD_ARGS); STATE = STATE_VAL; goto state_val; }
333 0 : any { --YYCURSOR; goto state_next_arg_begin; }
334 : */
335 :
336 :
337 15 : state_val:
338 15 : start = YYCURSOR;
339 : /*!re2c
340 12 : ["] (any\[">])* ["] { handle_val(STD_ARGS, 1, '"'); goto state_next_arg_begin; }
341 2 : ['] (any\['>])* ['] { handle_val(STD_ARGS, 1, '\''); goto state_next_arg_begin; }
342 1 : (any\[ \r\t\n>])+ { handle_val(STD_ARGS, 0, ' '); goto state_next_arg_begin; }
343 0 : any { passthru(STD_ARGS); goto state_next_arg_begin; }
344 : */
345 :
346 15 : stop:
347 15 : rest = YYLIMIT - start;
348 : scdebug(("stopped in state %d at pos %d (%d:%c) %d\n", STATE, YYCURSOR - ctx->buf.c, *YYCURSOR, *YYCURSOR, rest));
349 : /* XXX: Crash avoidance. Need to work with reporter to figure out what goes wrong */
350 15 : if (rest < 0) rest = 0;
351 :
352 15 : if (rest) memmove(ctx->buf.c, start, rest);
353 15 : ctx->buf.len = rest;
354 15 : }
355 :
356 : char *php_url_scanner_adapt_single_url(const char *url, size_t urllen, const char *name, const char *value, size_t *newlen TSRMLS_DC)
357 0 : {
358 0 : smart_str surl = {0};
359 0 : smart_str buf = {0};
360 0 : smart_str url_app = {0};
361 :
362 0 : smart_str_setl(&surl, url, urllen);
363 :
364 0 : smart_str_appends(&url_app, name);
365 0 : smart_str_appendc(&url_app, '=');
366 0 : smart_str_appends(&url_app, value);
367 :
368 0 : append_modified_url(&surl, &buf, &url_app, PG(arg_separator).output);
369 :
370 0 : smart_str_0(&buf);
371 0 : if (newlen) *newlen = buf.len;
372 :
373 0 : smart_str_free(&url_app);
374 :
375 0 : return buf.c;
376 : }
377 :
378 :
379 : static char *url_adapt_ext(const char *src, size_t srclen, size_t *newlen, zend_bool do_flush TSRMLS_DC)
380 15 : {
381 : url_adapt_state_ex_t *ctx;
382 : char *retval;
383 :
384 15 : ctx = &BG(url_adapt_state_ex);
385 :
386 15 : xx_mainloop(ctx, src, srclen TSRMLS_CC);
387 :
388 15 : *newlen = ctx->result.len;
389 15 : if (!ctx->result.c) {
390 2 : smart_str_appendl(&ctx->result, "", 0);
391 : }
392 15 : smart_str_0(&ctx->result);
393 15 : if (do_flush) {
394 15 : smart_str_appendl(&ctx->result, ctx->buf.c, ctx->buf.len);
395 15 : *newlen += ctx->buf.len;
396 15 : smart_str_free(&ctx->buf);
397 : }
398 15 : retval = ctx->result.c;
399 15 : ctx->result.c = NULL;
400 15 : ctx->result.len = 0;
401 15 : return retval;
402 : }
403 :
404 : static int php_url_scanner_ex_activate(TSRMLS_D)
405 11 : {
406 : url_adapt_state_ex_t *ctx;
407 :
408 11 : ctx = &BG(url_adapt_state_ex);
409 :
410 11 : memset(ctx, 0, ((size_t) &((url_adapt_state_ex_t *)0)->tags));
411 :
412 11 : return SUCCESS;
413 : }
414 :
415 : static int php_url_scanner_ex_deactivate(TSRMLS_D)
416 11 : {
417 : url_adapt_state_ex_t *ctx;
418 :
419 11 : ctx = &BG(url_adapt_state_ex);
420 :
421 11 : smart_str_free(&ctx->result);
422 11 : smart_str_free(&ctx->buf);
423 11 : smart_str_free(&ctx->tag);
424 11 : smart_str_free(&ctx->arg);
425 :
426 11 : return SUCCESS;
427 : }
428 :
429 : static void php_url_scanner_output_handler(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC)
430 16 : {
431 : size_t len;
432 :
433 16 : if (BG(url_adapt_state_ex).url_app.len != 0) {
434 15 : *handled_output = url_adapt_ext(output, output_len, &len, (zend_bool) (mode & (PHP_OUTPUT_HANDLER_END | PHP_OUTPUT_HANDLER_CONT) ? 1 : 0) TSRMLS_CC);
435 : if (sizeof(uint) < sizeof(size_t)) {
436 : if (len > UINT_MAX)
437 : len = UINT_MAX;
438 : }
439 15 : *handled_output_len = len;
440 1 : } else if (BG(url_adapt_state_ex).url_app.len == 0) {
441 1 : url_adapt_state_ex_t *ctx = &BG(url_adapt_state_ex);
442 1 : if (ctx->buf.len) {
443 0 : smart_str_appendl(&ctx->result, ctx->buf.c, ctx->buf.len);
444 0 : smart_str_appendl(&ctx->result, output, output_len);
445 :
446 0 : *handled_output = ctx->result.c;
447 0 : *handled_output_len = ctx->buf.len + output_len;
448 :
449 0 : ctx->result.c = NULL;
450 0 : ctx->result.len = 0;
451 0 : smart_str_free(&ctx->buf);
452 : } else {
453 1 : *handled_output = NULL;
454 : }
455 : } else {
456 0 : *handled_output = NULL;
457 : }
458 16 : }
459 :
460 : PHPAPI int php_url_scanner_add_var(char *name, int name_len, char *value, int value_len, int urlencode TSRMLS_DC)
461 12 : {
462 : char *encoded;
463 : int encoded_len;
464 : smart_str val;
465 :
466 12 : if (! BG(url_adapt_state_ex).active) {
467 11 : php_url_scanner_ex_activate(TSRMLS_C);
468 11 : php_ob_set_internal_handler(php_url_scanner_output_handler, 0, "URL-Rewriter", 1 TSRMLS_CC);
469 11 : BG(url_adapt_state_ex).active = 1;
470 : }
471 :
472 :
473 12 : if (BG(url_adapt_state_ex).url_app.len != 0) {
474 1 : smart_str_appends(&BG(url_adapt_state_ex).url_app, PG(arg_separator).output);
475 : }
476 :
477 12 : if (urlencode) {
478 12 : encoded = php_url_encode(value, value_len, &encoded_len);
479 12 : smart_str_setl(&val, encoded, encoded_len);
480 : } else {
481 0 : smart_str_setl(&val, value, value_len);
482 : }
483 :
484 12 : smart_str_appendl(&BG(url_adapt_state_ex).url_app, name, name_len);
485 12 : smart_str_appendc(&BG(url_adapt_state_ex).url_app, '=');
486 12 : smart_str_append(&BG(url_adapt_state_ex).url_app, &val);
487 :
488 12 : smart_str_appends(&BG(url_adapt_state_ex).form_app, "<input type=\"hidden\" name=\"");
489 12 : smart_str_appendl(&BG(url_adapt_state_ex).form_app, name, name_len);
490 12 : smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" value=\"");
491 12 : smart_str_append(&BG(url_adapt_state_ex).form_app, &val);
492 12 : smart_str_appends(&BG(url_adapt_state_ex).form_app, "\" />");
493 :
494 12 : if (urlencode)
495 12 : efree(encoded);
496 :
497 12 : return SUCCESS;
498 : }
499 :
500 : PHPAPI int php_url_scanner_reset_vars(TSRMLS_D)
501 10 : {
502 10 : BG(url_adapt_state_ex).form_app.len = 0;
503 10 : BG(url_adapt_state_ex).url_app.len = 0;
504 :
505 10 : return SUCCESS;
506 : }
507 :
508 : PHP_MINIT_FUNCTION(url_scanner)
509 13565 : {
510 13565 : BG(url_adapt_state_ex).tags = NULL;
511 :
512 13565 : BG(url_adapt_state_ex).form_app.c = BG(url_adapt_state_ex).url_app.c = 0;
513 13565 : BG(url_adapt_state_ex).form_app.len = BG(url_adapt_state_ex).url_app.len = 0;
514 :
515 13565 : REGISTER_INI_ENTRIES();
516 13565 : return SUCCESS;
517 : }
518 :
519 : PHP_MSHUTDOWN_FUNCTION(url_scanner)
520 13597 : {
521 13597 : UNREGISTER_INI_ENTRIES();
522 :
523 13597 : return SUCCESS;
524 : }
525 :
526 : PHP_RINIT_FUNCTION(url_scanner)
527 13551 : {
528 13551 : BG(url_adapt_state_ex).active = 0;
529 :
530 13551 : return SUCCESS;
531 : }
532 :
533 : PHP_RSHUTDOWN_FUNCTION(url_scanner)
534 13584 : {
535 13584 : if (BG(url_adapt_state_ex).active) {
536 11 : php_url_scanner_ex_deactivate(TSRMLS_C);
537 11 : BG(url_adapt_state_ex).active = 0;
538 : }
539 :
540 13584 : smart_str_free(&BG(url_adapt_state_ex).form_app);
541 13584 : smart_str_free(&BG(url_adapt_state_ex).url_app);
542 :
543 13584 : return SUCCESS;
544 : }
|