Coverage Report

Created: 2025-06-13 06:43

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