Coverage Report

Created: 2026-06-02 06:36

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