Coverage Report

Created: 2022-02-19 20:27

/src/php-src/ext/standard/url.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | http://www.php.net/license/3_01.txt                                  |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Author: Jim Winstead <jimw@php.net>                                  |
14
   +----------------------------------------------------------------------+
15
 */
16
17
#include <stdlib.h>
18
#include <string.h>
19
#include <ctype.h>
20
#include <sys/types.h>
21
22
#ifdef __SSE2__
23
#include <emmintrin.h>
24
#endif
25
26
#include "php.h"
27
28
#include "url.h"
29
#include "file.h"
30
31
/* {{{ free_url */
32
PHPAPI void php_url_free(php_url *theurl)
33
0
{
34
0
  if (theurl->scheme)
35
0
    zend_string_release_ex(theurl->scheme, 0);
36
0
  if (theurl->user)
37
0
    zend_string_release_ex(theurl->user, 0);
38
0
  if (theurl->pass)
39
0
    zend_string_release_ex(theurl->pass, 0);
40
0
  if (theurl->host)
41
0
    zend_string_release_ex(theurl->host, 0);
42
0
  if (theurl->path)
43
0
    zend_string_release_ex(theurl->path, 0);
44
0
  if (theurl->query)
45
0
    zend_string_release_ex(theurl->query, 0);
46
0
  if (theurl->fragment)
47
0
    zend_string_release_ex(theurl->fragment, 0);
48
0
  efree(theurl);
49
0
}
50
/* }}} */
51
52
/* {{{ php_replace_controlchars_ex */
53
PHPAPI char *php_replace_controlchars_ex(char *str, size_t len)
54
0
{
55
0
  unsigned char *s = (unsigned char *)str;
56
0
  unsigned char *e = (unsigned char *)str + len;
57
58
0
  if (!str) {
59
0
    return (NULL);
60
0
  }
61
62
0
  while (s < e) {
63
64
0
    if (iscntrl(*s)) {
65
0
      *s='_';
66
0
    }
67
0
    s++;
68
0
  }
69
70
0
  return (str);
71
0
}
72
/* }}} */
73
74
PHPAPI char *php_replace_controlchars(char *str)
75
0
{
76
0
  return php_replace_controlchars_ex(str, strlen(str));
77
0
}
78
79
PHPAPI php_url *php_url_parse(char const *str)
80
0
{
81
0
  return php_url_parse_ex(str, strlen(str));
82
0
}
83
84
/* {{{ php_url_parse */
85
PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
86
0
{
87
0
  char port_buf[6];
88
0
  php_url *ret = ecalloc(1, sizeof(php_url));
89
0
  char const *s, *e, *p, *pp, *ue;
90
91
0
  s = str;
92
0
  ue = s + length;
93
94
  /* parse scheme */
95
0
  if ((e = memchr(s, ':', length)) && e != s) {
96
    /* validate scheme */
97
0
    p = s;
98
0
    while (p < e) {
99
      /* scheme = 1*[ lowalpha | digit | "+" | "-" | "." ] */
100
0
      if (!isalpha(*p) && !isdigit(*p) && *p != '+' && *p != '.' && *p != '-') {
101
0
        if (e + 1 < ue && e < s + strcspn(s, "?#")) {
102
0
          goto parse_port;
103
0
        } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
104
0
          s += 2;
105
0
          e = 0;
106
0
          goto parse_host;
107
0
        } else {
108
0
          goto just_path;
109
0
        }
110
0
      }
111
0
      p++;
112
0
    }
113
114
0
    if (e + 1 == ue) { /* only scheme is available */
115
0
      ret->scheme = zend_string_init(s, (e - s), 0);
116
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->scheme), ZSTR_LEN(ret->scheme));
117
0
      return ret;
118
0
    }
119
120
    /*
121
     * certain schemas like mailto: and zlib: may not have any / after them
122
     * this check ensures we support those.
123
     */
124
0
    if (*(e+1) != '/') {
125
      /* check if the data we get is a port this allows us to
126
       * correctly parse things like a.com:80
127
       */
128
0
      p = e + 1;
129
0
      while (p < ue && isdigit(*p)) {
130
0
        p++;
131
0
      }
132
133
0
      if ((p == ue || *p == '/') && (p - e) < 7) {
134
0
        goto parse_port;
135
0
      }
136
137
0
      ret->scheme = zend_string_init(s, (e-s), 0);
138
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->scheme), ZSTR_LEN(ret->scheme));
139
140
0
      s = e + 1;
141
0
      goto just_path;
142
0
    } else {
143
0
      ret->scheme = zend_string_init(s, (e-s), 0);
144
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->scheme), ZSTR_LEN(ret->scheme));
145
146
0
      if (e + 2 < ue && *(e + 2) == '/') {
147
0
        s = e + 3;
148
0
        if (zend_string_equals_literal_ci(ret->scheme, "file")) {
149
0
          if (e + 3 < ue && *(e + 3) == '/') {
150
            /* support windows drive letters as in:
151
               file:///c:/somedir/file.txt
152
            */
153
0
            if (e + 5 < ue && *(e + 5) == ':') {
154
0
              s = e + 4;
155
0
            }
156
0
            goto just_path;
157
0
          }
158
0
        }
159
0
      } else {
160
0
        s = e + 1;
161
0
        goto just_path;
162
0
      }
163
0
    }
164
0
  } else if (e) { /* no scheme; starts with colon: look for port */
165
0
    parse_port:
166
0
    p = e + 1;
167
0
    pp = p;
168
169
0
    while (pp < ue && pp - p < 6 && isdigit(*pp)) {
170
0
      pp++;
171
0
    }
172
173
0
    if (pp - p > 0 && pp - p < 6 && (pp == ue || *pp == '/')) {
174
0
      zend_long port;
175
0
      memcpy(port_buf, p, (pp - p));
176
0
      port_buf[pp - p] = '\0';
177
0
      port = ZEND_STRTOL(port_buf, NULL, 10);
178
0
      if (port > 0 && port <= 65535) {
179
0
        ret->port = (unsigned short) port;
180
0
        if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
181
0
            s += 2;
182
0
        }
183
0
      } else {
184
0
        php_url_free(ret);
185
0
        return NULL;
186
0
      }
187
0
    } else if (p == pp && pp == ue) {
188
0
      php_url_free(ret);
189
0
      return NULL;
190
0
    } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
191
0
      s += 2;
192
0
    } else {
193
0
      goto just_path;
194
0
    }
195
0
  } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
196
0
    s += 2;
197
0
  } else {
198
0
    goto just_path;
199
0
  }
200
201
0
  parse_host:
202
  /* Binary-safe strcspn(s, "/?#") */
203
0
  e = ue;
204
0
  if ((p = memchr(s, '/', e - s))) {
205
0
    e = p;
206
0
  }
207
0
  if ((p = memchr(s, '?', e - s))) {
208
0
    e = p;
209
0
  }
210
0
  if ((p = memchr(s, '#', e - s))) {
211
0
    e = p;
212
0
  }
213
214
  /* check for login and password */
215
0
  if ((p = zend_memrchr(s, '@', (e-s)))) {
216
0
    if ((pp = memchr(s, ':', (p-s)))) {
217
0
      ret->user = zend_string_init(s, (pp-s), 0);
218
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
219
220
0
      pp++;
221
0
      ret->pass = zend_string_init(pp, (p-pp), 0);
222
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->pass), ZSTR_LEN(ret->pass));
223
0
    } else {
224
0
      ret->user = zend_string_init(s, (p-s), 0);
225
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
226
0
    }
227
228
0
    s = p + 1;
229
0
  }
230
231
  /* check for port */
232
0
  if (s < ue && *s == '[' && *(e-1) == ']') {
233
    /* Short circuit portscan,
234
       we're dealing with an
235
       IPv6 embedded address */
236
0
    p = NULL;
237
0
  } else {
238
0
    p = zend_memrchr(s, ':', (e-s));
239
0
  }
240
241
0
  if (p) {
242
0
    if (!ret->port) {
243
0
      p++;
244
0
      if (e-p > 5) { /* port cannot be longer then 5 characters */
245
0
        php_url_free(ret);
246
0
        return NULL;
247
0
      } else if (e - p > 0) {
248
0
        zend_long port;
249
0
        memcpy(port_buf, p, (e - p));
250
0
        port_buf[e - p] = '\0';
251
0
        port = ZEND_STRTOL(port_buf, NULL, 10);
252
0
        if (port > 0 && port <= 65535) {
253
0
          ret->port = (unsigned short)port;
254
0
        } else {
255
0
          php_url_free(ret);
256
0
          return NULL;
257
0
        }
258
0
      }
259
0
      p--;
260
0
    }
261
0
  } else {
262
0
    p = e;
263
0
  }
264
265
  /* check if we have a valid host, if we don't reject the string as url */
266
0
  if ((p-s) < 1) {
267
0
    php_url_free(ret);
268
0
    return NULL;
269
0
  }
270
271
0
  ret->host = zend_string_init(s, (p-s), 0);
272
0
  php_replace_controlchars_ex(ZSTR_VAL(ret->host), ZSTR_LEN(ret->host));
273
274
0
  if (e == ue) {
275
0
    return ret;
276
0
  }
277
278
0
  s = e;
279
280
0
  just_path:
281
282
0
  e = ue;
283
0
  p = memchr(s, '#', (e - s));
284
0
  if (p) {
285
0
    p++;
286
0
    if (p < e) {
287
0
      ret->fragment = zend_string_init(p, (e - p), 0);
288
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->fragment), ZSTR_LEN(ret->fragment));
289
0
    } else {
290
0
      ret->fragment = ZSTR_EMPTY_ALLOC();
291
0
    }
292
0
    e = p-1;
293
0
  }
294
295
0
  p = memchr(s, '?', (e - s));
296
0
  if (p) {
297
0
    p++;
298
0
    if (p < e) {
299
0
      ret->query = zend_string_init(p, (e - p), 0);
300
0
      php_replace_controlchars_ex(ZSTR_VAL(ret->query), ZSTR_LEN(ret->query));
301
0
    } else {
302
0
      ret->query = ZSTR_EMPTY_ALLOC();
303
0
    }
304
0
    e = p-1;
305
0
  }
306
307
0
  if (s < e || s == ue) {
308
0
    ret->path = zend_string_init(s, (e - s), 0);
309
0
    php_replace_controlchars_ex(ZSTR_VAL(ret->path), ZSTR_LEN(ret->path));
310
0
  }
311
312
0
  return ret;
313
0
}
314
/* }}} */
315
316
/* {{{ Parse a URL and return its components */
317
PHP_FUNCTION(parse_url)
318
0
{
319
0
  char *str;
320
0
  size_t str_len;
321
0
  php_url *resource;
322
0
  zend_long key = -1;
323
0
  zval tmp;
324
325
0
  ZEND_PARSE_PARAMETERS_START(1, 2)
326
0
    Z_PARAM_STRING(str, str_len)
327
0
    Z_PARAM_OPTIONAL
328
0
    Z_PARAM_LONG(key)
329
0
  ZEND_PARSE_PARAMETERS_END();
330
331
0
  resource = php_url_parse_ex(str, str_len);
332
0
  if (resource == NULL) {
333
    /* @todo Find a method to determine why php_url_parse_ex() failed */
334
0
    RETURN_FALSE;
335
0
  }
336
337
0
  if (key > -1) {
338
0
    switch (key) {
339
0
      case PHP_URL_SCHEME:
340
0
        if (resource->scheme != NULL) RETVAL_STR_COPY(resource->scheme);
341
0
        break;
342
0
      case PHP_URL_HOST:
343
0
        if (resource->host != NULL) RETVAL_STR_COPY(resource->host);
344
0
        break;
345
0
      case PHP_URL_PORT:
346
0
        if (resource->port != 0) RETVAL_LONG(resource->port);
347
0
        break;
348
0
      case PHP_URL_USER:
349
0
        if (resource->user != NULL) RETVAL_STR_COPY(resource->user);
350
0
        break;
351
0
      case PHP_URL_PASS:
352
0
        if (resource->pass != NULL) RETVAL_STR_COPY(resource->pass);
353
0
        break;
354
0
      case PHP_URL_PATH:
355
0
        if (resource->path != NULL) RETVAL_STR_COPY(resource->path);
356
0
        break;
357
0
      case PHP_URL_QUERY:
358
0
        if (resource->query != NULL) RETVAL_STR_COPY(resource->query);
359
0
        break;
360
0
      case PHP_URL_FRAGMENT:
361
0
        if (resource->fragment != NULL) RETVAL_STR_COPY(resource->fragment);
362
0
        break;
363
0
      default:
364
0
        zend_argument_value_error(2, "must be a valid URL component identifier, " ZEND_LONG_FMT " given", key);
365
0
        break;
366
0
    }
367
0
    goto done;
368
0
  }
369
370
  /* allocate an array for return */
371
0
  array_init(return_value);
372
373
    /* add the various elements to the array */
374
0
  if (resource->scheme != NULL) {
375
0
    ZVAL_STR_COPY(&tmp, resource->scheme);
376
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_SCHEME), &tmp);
377
0
  }
378
0
  if (resource->host != NULL) {
379
0
    ZVAL_STR_COPY(&tmp, resource->host);
380
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_HOST), &tmp);
381
0
  }
382
0
  if (resource->port != 0) {
383
0
    ZVAL_LONG(&tmp, resource->port);
384
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_PORT), &tmp);
385
0
  }
386
0
  if (resource->user != NULL) {
387
0
    ZVAL_STR_COPY(&tmp, resource->user);
388
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_USER), &tmp);
389
0
  }
390
0
  if (resource->pass != NULL) {
391
0
    ZVAL_STR_COPY(&tmp, resource->pass);
392
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_PASS), &tmp);
393
0
  }
394
0
  if (resource->path != NULL) {
395
0
    ZVAL_STR_COPY(&tmp, resource->path);
396
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_PATH), &tmp);
397
0
  }
398
0
  if (resource->query != NULL) {
399
0
    ZVAL_STR_COPY(&tmp, resource->query);
400
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_QUERY), &tmp);
401
0
  }
402
0
  if (resource->fragment != NULL) {
403
0
    ZVAL_STR_COPY(&tmp, resource->fragment);
404
0
    zend_hash_add_new(Z_ARRVAL_P(return_value), ZSTR_KNOWN(ZEND_STR_FRAGMENT), &tmp);
405
0
  }
406
0
done:
407
0
  php_url_free(resource);
408
0
}
409
/* }}} */
410
411
/* {{{ php_htoi */
412
static int php_htoi(char *s)
413
480
{
414
480
  int value;
415
480
  int c;
416
417
480
  c = ((unsigned char *)s)[0];
418
480
  if (isupper(c))
419
226
    c = tolower(c);
420
480
  value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
421
422
480
  c = ((unsigned char *)s)[1];
423
480
  if (isupper(c))
424
79
    c = tolower(c);
425
480
  value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
426
427
480
  return (value);
428
480
}
429
/* }}} */
430
431
/* rfc1738:
432
433
   ...The characters ";",
434
   "/", "?", ":", "@", "=" and "&" are the characters which may be
435
   reserved for special meaning within a scheme...
436
437
   ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
438
   reserved characters used for their reserved purposes may be used
439
   unencoded within a URL...
440
441
   For added safety, we only leave -_. unencoded.
442
 */
443
444
static unsigned char hexchars[] = "0123456789ABCDEF";
445
446
0
static zend_always_inline zend_string *php_url_encode_impl(const char *s, size_t len, zend_bool raw) /* {{{ */ {
447
0
  register unsigned char c;
448
0
  unsigned char *to;
449
0
  unsigned char const *from, *end;
450
0
  zend_string *start;
451
452
0
  from = (unsigned char *)s;
453
0
  end = (unsigned char *)s + len;
454
0
  start = zend_string_safe_alloc(3, len, 0, 0);
455
0
  to = (unsigned char*)ZSTR_VAL(start);
456
457
0
#ifdef __SSE2__
458
0
  while (from + 16 < end) {
459
0
    __m128i mask;
460
0
    uint32_t bits;
461
0
    const __m128i _A = _mm_set1_epi8('A' - 1);
462
0
    const __m128i Z_ = _mm_set1_epi8('Z' + 1);
463
0
    const __m128i _a = _mm_set1_epi8('a' - 1);
464
0
    const __m128i z_ = _mm_set1_epi8('z' + 1);
465
0
    const __m128i _zero = _mm_set1_epi8('0' - 1);
466
0
    const __m128i nine_ = _mm_set1_epi8('9' + 1);
467
0
    const __m128i dot = _mm_set1_epi8('.');
468
0
    const __m128i minus = _mm_set1_epi8('-');
469
0
    const __m128i under = _mm_set1_epi8('_');
470
471
0
    __m128i in = _mm_loadu_si128((__m128i *)from);
472
473
0
    __m128i gt = _mm_cmpgt_epi8(in, _A);
474
0
    __m128i lt = _mm_cmplt_epi8(in, Z_);
475
0
    mask = _mm_and_si128(lt, gt); /* upper */
476
0
    gt = _mm_cmpgt_epi8(in, _a);
477
0
    lt = _mm_cmplt_epi8(in, z_);
478
0
    mask = _mm_or_si128(mask, _mm_and_si128(lt, gt)); /* lower */
479
0
    gt = _mm_cmpgt_epi8(in, _zero);
480
0
    lt = _mm_cmplt_epi8(in, nine_);
481
0
    mask = _mm_or_si128(mask, _mm_and_si128(lt, gt)); /* number */
482
0
    mask = _mm_or_si128(mask, _mm_cmpeq_epi8(in, dot));
483
0
    mask = _mm_or_si128(mask, _mm_cmpeq_epi8(in, minus));
484
0
    mask = _mm_or_si128(mask, _mm_cmpeq_epi8(in, under));
485
486
0
    if (!raw) {
487
0
      const __m128i blank = _mm_set1_epi8(' ');
488
0
      __m128i eq = _mm_cmpeq_epi8(in, blank);
489
0
      if (_mm_movemask_epi8(eq)) {
490
0
        in = _mm_add_epi8(in, _mm_and_si128(eq, _mm_set1_epi8('+' - ' ')));
491
0
        mask = _mm_or_si128(mask, eq);
492
0
      }
493
0
    }
494
0
    if (raw) {
495
0
      const __m128i wavy = _mm_set1_epi8('~');
496
0
      mask = _mm_or_si128(mask, _mm_cmpeq_epi8(in, wavy));
497
0
    }
498
0
    if (((bits = _mm_movemask_epi8(mask)) & 0xffff) == 0xffff) {
499
0
      _mm_storeu_si128((__m128i*)to, in);
500
0
      to += 16;
501
0
    } else {
502
0
      int i;
503
0
      unsigned char xmm[16];
504
0
      _mm_storeu_si128((__m128i*)xmm, in);
505
0
      for (i = 0; i < sizeof(xmm); i++) {
506
0
        if ((bits & (0x1 << i))) {
507
0
          *to++ = xmm[i];
508
0
        } else {
509
0
          *to++ = '%';
510
0
          *to++ = hexchars[xmm[i] >> 4];
511
0
          *to++ = hexchars[xmm[i] & 0xf];
512
0
        }
513
0
      }
514
0
    }
515
0
    from += 16;
516
0
  }
517
0
#endif
518
0
  while (from < end) {
519
0
    c = *from++;
520
521
0
    if (!raw && c == ' ') {
522
0
      *to++ = '+';
523
0
    } else if ((c < '0' && c != '-' && c != '.') ||
524
0
        (c < 'A' && c > '9') ||
525
0
        (c > 'Z' && c < 'a' && c != '_') ||
526
0
        (c > 'z' && (!raw || c != '~'))) {
527
0
      to[0] = '%';
528
0
      to[1] = hexchars[c >> 4];
529
0
      to[2] = hexchars[c & 15];
530
0
      to += 3;
531
0
    } else {
532
0
      *to++ = c;
533
0
    }
534
0
  }
535
0
  *to = '\0';
536
537
0
  start = zend_string_truncate(start, to - (unsigned char*)ZSTR_VAL(start), 0);
538
539
0
  return start;
540
0
}
541
/* }}} */
542
543
/* {{{ php_url_encode */
544
PHPAPI zend_string *php_url_encode(char const *s, size_t len)
545
0
{
546
0
  return php_url_encode_impl(s, len, 0);
547
0
}
548
/* }}} */
549
550
/* {{{ URL-encodes string */
551
PHP_FUNCTION(urlencode)
552
0
{
553
0
  zend_string *in_str;
554
555
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
556
0
    Z_PARAM_STR(in_str)
557
0
  ZEND_PARSE_PARAMETERS_END();
558
559
0
  RETURN_STR(php_url_encode(ZSTR_VAL(in_str), ZSTR_LEN(in_str)));
560
0
}
561
/* }}} */
562
563
/* {{{ Decodes URL-encoded string */
564
PHP_FUNCTION(urldecode)
565
0
{
566
0
  zend_string *in_str, *out_str;
567
568
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
569
0
    Z_PARAM_STR(in_str)
570
0
  ZEND_PARSE_PARAMETERS_END();
571
572
0
  out_str = zend_string_init(ZSTR_VAL(in_str), ZSTR_LEN(in_str), 0);
573
0
  ZSTR_LEN(out_str) = php_url_decode(ZSTR_VAL(out_str), ZSTR_LEN(out_str));
574
575
0
    RETURN_NEW_STR(out_str);
576
0
}
577
/* }}} */
578
579
/* {{{ php_url_decode */
580
PHPAPI size_t php_url_decode(char *str, size_t len)
581
4.76k
{
582
4.76k
  char *dest = str;
583
4.76k
  char *data = str;
584
585
94.9k
  while (len--) {
586
90.1k
    if (*data == '+') {
587
1.19k
      *dest = ' ';
588
1.19k
    }
589
88.9k
    else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
590
480
         && isxdigit((int) *(data + 2))) {
591
480
      *dest = (char) php_htoi(data + 1);
592
480
      data += 2;
593
480
      len -= 2;
594
88.4k
    } else {
595
88.4k
      *dest = *data;
596
88.4k
    }
597
90.1k
    data++;
598
90.1k
    dest++;
599
90.1k
  }
600
4.76k
  *dest = '\0';
601
4.76k
  return dest - str;
602
4.76k
}
603
/* }}} */
604
605
/* {{{ php_raw_url_encode */
606
PHPAPI zend_string *php_raw_url_encode(char const *s, size_t len)
607
0
{
608
0
  return php_url_encode_impl(s, len, 1);
609
0
}
610
/* }}} */
611
612
/* {{{ URL-encodes string */
613
PHP_FUNCTION(rawurlencode)
614
0
{
615
0
  zend_string *in_str;
616
617
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
618
0
    Z_PARAM_STR(in_str)
619
0
  ZEND_PARSE_PARAMETERS_END();
620
621
0
  RETURN_STR(php_raw_url_encode(ZSTR_VAL(in_str), ZSTR_LEN(in_str)));
622
0
}
623
/* }}} */
624
625
/* {{{ Decodes URL-encodes string */
626
PHP_FUNCTION(rawurldecode)
627
0
{
628
0
  zend_string *in_str, *out_str;
629
630
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
631
0
    Z_PARAM_STR(in_str)
632
0
  ZEND_PARSE_PARAMETERS_END();
633
634
0
  out_str = zend_string_init(ZSTR_VAL(in_str), ZSTR_LEN(in_str), 0);
635
0
  ZSTR_LEN(out_str) = php_raw_url_decode(ZSTR_VAL(out_str), ZSTR_LEN(out_str));
636
637
0
    RETURN_NEW_STR(out_str);
638
0
}
639
/* }}} */
640
641
/* {{{ php_raw_url_decode */
642
PHPAPI size_t php_raw_url_decode(char *str, size_t len)
643
0
{
644
0
  char *dest = str;
645
0
  char *data = str;
646
647
0
  while (len--) {
648
0
    if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
649
0
      && isxdigit((int) *(data + 2))) {
650
0
      *dest = (char) php_htoi(data + 1);
651
0
      data += 2;
652
0
      len -= 2;
653
0
    } else {
654
0
      *dest = *data;
655
0
    }
656
0
    data++;
657
0
    dest++;
658
0
  }
659
0
  *dest = '\0';
660
0
  return dest - str;
661
0
}
662
/* }}} */
663
664
/* {{{ fetches all the headers sent by the server in response to a HTTP request */
665
PHP_FUNCTION(get_headers)
666
0
{
667
0
  char *url;
668
0
  size_t url_len;
669
0
  php_stream *stream;
670
0
  zval *prev_val, *hdr = NULL;
671
0
  zend_long format = 0;
672
0
  zval *zcontext = NULL;
673
0
  php_stream_context *context;
674
675
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
676
0
    Z_PARAM_PATH(url, url_len)
677
0
    Z_PARAM_OPTIONAL
678
0
    Z_PARAM_LONG(format)
679
0
    Z_PARAM_RESOURCE_OR_NULL(zcontext)
680
0
  ZEND_PARSE_PARAMETERS_END();
681
682
0
  context = php_stream_context_from_zval(zcontext, 0);
683
684
0
  if (!(stream = php_stream_open_wrapper_ex(url, "r", REPORT_ERRORS | STREAM_USE_URL | STREAM_ONLY_GET_HEADERS, NULL, context))) {
685
0
    RETURN_FALSE;
686
0
  }
687
688
0
  if (Z_TYPE(stream->wrapperdata) != IS_ARRAY) {
689
0
    php_stream_close(stream);
690
0
    RETURN_FALSE;
691
0
  }
692
693
0
  array_init(return_value);
694
695
0
  ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(&stream->wrapperdata), hdr) {
696
0
    if (Z_TYPE_P(hdr) != IS_STRING) {
697
0
      continue;
698
0
    }
699
0
    if (!format) {
700
0
no_name_header:
701
0
      add_next_index_str(return_value, zend_string_copy(Z_STR_P(hdr)));
702
0
    } else {
703
0
      char c;
704
0
      char *s, *p;
705
706
0
      if ((p = strchr(Z_STRVAL_P(hdr), ':'))) {
707
0
        c = *p;
708
0
        *p = '\0';
709
0
        s = p + 1;
710
0
        while (isspace((int)*(unsigned char *)s)) {
711
0
          s++;
712
0
        }
713
714
0
        if ((prev_val = zend_hash_str_find(Z_ARRVAL_P(return_value), Z_STRVAL_P(hdr), (p - Z_STRVAL_P(hdr)))) == NULL) {
715
0
          add_assoc_stringl_ex(return_value, Z_STRVAL_P(hdr), (p - Z_STRVAL_P(hdr)), s, (Z_STRLEN_P(hdr) - (s - Z_STRVAL_P(hdr))));
716
0
        } else { /* some headers may occur more than once, therefor we need to remake the string into an array */
717
0
          convert_to_array(prev_val);
718
0
          add_next_index_stringl(prev_val, s, (Z_STRLEN_P(hdr) - (s - Z_STRVAL_P(hdr))));
719
0
        }
720
721
0
        *p = c;
722
0
      } else {
723
0
        goto no_name_header;
724
0
      }
725
0
    }
726
0
  } ZEND_HASH_FOREACH_END();
727
728
0
  php_stream_close(stream);
729
0
}
730
/* }}} */