2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 #ifdef PHP_HTTP_HAVE_IDN
19 #ifdef PHP_HTTP_HAVE_WCHAR
24 #ifdef HAVE_LANGINFO_H
25 # include <langinfo.h>
29 static inline char *localhostname(void)
31 char hostname
[1024] = {0};
34 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
35 return estrdup(hostname
);
37 #elif defined(HAVE_GETHOSTNAME)
38 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
39 # if defined(HAVE_GETDOMAINNAME)
40 size_t hlen
= strlen(hostname
);
41 if (hlen
<= lenof(hostname
) - lenof("(none)")) {
42 hostname
[hlen
++] = '.';
43 if (SUCCESS
== getdomainname(&hostname
[hlen
], lenof(hostname
) - hlen
)) {
44 if (!strcmp(&hostname
[hlen
], "(none)")) {
45 hostname
[hlen
- 1] = '\0';
47 return estrdup(hostname
);
51 if (strcmp(hostname
, "(none)")) {
52 return estrdup(hostname
);
56 return estrndup("localhost", lenof("localhost"));
59 static php_url
*php_http_url_from_env(php_url
*url TSRMLS_DC
)
61 zval
*https
, *zhost
, *zport
;
65 url
= ecalloc(1, sizeof(*url
));
69 zport
= php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC
);
70 if (zport
&& IS_LONG
== is_numeric_string(Z_STRVAL_P(zport
), Z_STRLEN_P(zport
), &port
, NULL
, 0)) {
75 https
= php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC
);
76 if (https
&& !strcasecmp(Z_STRVAL_P(https
), "ON")) {
77 url
->scheme
= estrndup("https", lenof("https"));
79 url
->scheme
= estrndup("http", lenof("http"));
83 if ((((zhost
= php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC
)) ||
84 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC
)) ||
85 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC
)))) && Z_STRLEN_P(zhost
)) {
86 size_t stop_at
= strspn(Z_STRVAL_P(zhost
), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
88 url
->host
= estrndup(Z_STRVAL_P(zhost
), stop_at
);
90 url
->host
= localhostname();
94 if (SG(request_info
).request_uri
&& SG(request_info
).request_uri
[0]) {
95 const char *q
= strchr(SG(request_info
).request_uri
, '?');
98 url
->path
= estrndup(SG(request_info
).request_uri
, q
- SG(request_info
).request_uri
);
100 url
->path
= estrdup(SG(request_info
).request_uri
);
105 if (SG(request_info
).query_string
&& SG(request_info
).query_string
[0]) {
106 url
->query
= estrdup(SG(request_info
).query_string
);
112 void php_http_url(int flags
, const php_url
*old_url
, const php_url
*new_url
, php_url
**url_ptr
, char **url_str
, size_t *url_len TSRMLS_DC
)
114 php_url
*url
, *tmp_url
= NULL
;
116 /* set from env if requested */
117 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
118 php_url
*env_url
= php_http_url_from_env(NULL TSRMLS_CC
);
120 php_http_url(flags
^ PHP_HTTP_URL_FROM_ENV
, env_url
, old_url
, &tmp_url
, NULL
, NULL TSRMLS_CC
);
122 php_url_free(env_url
);
126 url
= ecalloc(1, sizeof(*url
));
128 #define __URLSET(u,n) \
130 #define __URLCPY(n) \
131 url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL)
133 if (!(flags
& PHP_HTTP_URL_STRIP_PORT
)) {
134 url
->port
= __URLSET(new_url
, port
) ? new_url
->port
: ((old_url
) ? old_url
->port
: 0);
136 if (!(flags
& PHP_HTTP_URL_STRIP_USER
)) {
139 if (!(flags
& PHP_HTTP_URL_STRIP_PASS
)) {
146 if (!(flags
& PHP_HTTP_URL_STRIP_PATH
)) {
147 if ((flags
& PHP_HTTP_URL_JOIN_PATH
) && __URLSET(old_url
, path
) && __URLSET(new_url
, path
) && *new_url
->path
!= '/') {
148 size_t old_path_len
= strlen(old_url
->path
), new_path_len
= strlen(new_url
->path
);
150 url
->path
= ecalloc(1, old_path_len
+ new_path_len
+ 1 + 1);
152 strcat(url
->path
, old_url
->path
);
153 if (url
->path
[old_path_len
- 1] != '/') {
154 php_dirname(url
->path
, old_path_len
);
155 strcat(url
->path
, "/");
157 strcat(url
->path
, new_url
->path
);
162 if (!(flags
& PHP_HTTP_URL_STRIP_QUERY
)) {
163 if ((flags
& PHP_HTTP_URL_JOIN_QUERY
) && __URLSET(new_url
, query
) && __URLSET(old_url
, query
)) {
170 ZVAL_STRING(&qstr
, old_url
->query
, 0);
171 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
172 ZVAL_STRING(&qstr
, new_url
->query
, 0);
173 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
176 php_http_querystring_update(&qarr
, NULL
, &qstr TSRMLS_CC
);
177 url
->query
= Z_STRVAL(qstr
);
183 if (!(flags
& PHP_HTTP_URL_STRIP_FRAGMENT
)) {
187 /* done with copy & combine & strip */
189 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
190 /* free old_url we tainted above */
191 php_url_free(tmp_url
);
194 /* set some sane defaults */
197 url
->scheme
= estrndup("http", lenof("http"));
201 url
->host
= estrndup("localhost", lenof("localhost"));
205 url
->path
= estrndup("/", 1);
206 } else if (url
->path
[0] != '/') {
207 size_t plen
= strlen(url
->path
);
208 char *path
= emalloc(plen
+ 1 + 1);
211 memcpy(&path
[1], url
->path
, plen
+ 1);
212 STR_SET(url
->path
, path
);
214 /* replace directory references if path is not a single slash */
215 if ((flags
& PHP_HTTP_URL_SANITIZE_PATH
)
216 && url
->path
[0] && (url
->path
[0] != '/' || url
->path
[1])) {
217 char *ptr
, *end
= url
->path
+ strlen(url
->path
) + 1;
219 for (ptr
= strchr(url
->path
, '/'); ptr
; ptr
= strchr(ptr
, '/')) {
222 memmove(&ptr
[1], &ptr
[2], end
- &ptr
[2]);
232 memmove(&ptr
[1], &ptr
[3], end
- &ptr
[3]);
238 while (ptr
!= url
->path
) {
243 memmove(&ptr
[1], pos
, end
- pos
);
245 } else if (!ptr
[3]) {
264 /* unset default ports */
266 if ( ((url
->port
== 80) && !strcmp(url
->scheme
, "http"))
267 || ((url
->port
==443) && !strcmp(url
->scheme
, "https"))
274 php_http_url_to_string(url
, url_str
, url_len TSRMLS_CC
);
284 STATUS
php_http_url_encode_hash(HashTable
*hash
, const char *pre_encoded_str
, size_t pre_encoded_len
, char **encoded_str
, size_t *encoded_len TSRMLS_DC
)
286 const char *arg_sep_str
;
288 php_http_buffer_t
*qstr
= php_http_buffer_new();
290 php_http_url_argsep(&arg_sep_str
, &arg_sep_len TSRMLS_CC
);
292 if (SUCCESS
!= php_http_url_encode_hash_ex(hash
, qstr
, arg_sep_str
, arg_sep_len
, "=", 1, pre_encoded_str
, pre_encoded_len TSRMLS_CC
)) {
293 php_http_buffer_free(&qstr
);
297 php_http_buffer_data(qstr
, encoded_str
, encoded_len
);
298 php_http_buffer_free(&qstr
);
303 STATUS
php_http_url_encode_hash_ex(HashTable
*hash
, php_http_buffer_t
*qstr
, const char *arg_sep_str
, size_t arg_sep_len
, const char *val_sep_str
, size_t val_sep_len
, const char *pre_encoded_str
, size_t pre_encoded_len TSRMLS_DC
)
305 if (pre_encoded_len
&& pre_encoded_str
) {
306 php_http_buffer_append(qstr
, pre_encoded_str
, pre_encoded_len
);
309 if (!php_http_params_to_string(qstr
, hash
, arg_sep_str
, arg_sep_len
, "", 0, val_sep_str
, val_sep_len
, PHP_HTTP_PARAMS_QUERY TSRMLS_CC
)) {
316 void php_http_url_dtor(php_http_url_t
*url
)
318 STR_FREE(url
->scheme
.str
);
319 STR_FREE(url
->authority
.userinfo
.username
.str
);
320 STR_FREE(url
->authority
.userinfo
.password
.str
);
321 STR_FREE(url
->authority
.host
.str
);
322 STR_FREE(url
->path
.str
);
323 STR_FREE(url
->query
.str
);
324 STR_FREE(url
->fragment
.str
);
327 void php_http_url_free(php_http_url_t
**url
)
330 php_http_url_dtor(*url
);
336 #ifdef PHP_HTTP_HAVE_WCHAR
337 static zend_bool
cs_is_utf8(char **lc_ctype
)
340 if (strcmp("UTF-8", nl_langinfo(CODESET
))) {
341 *lc_ctype
= setlocale(LC_CTYPE
, NULL
);
346 *lc_ctype
= setlocale(LC_CTYPE
, NULL
);
351 if ((cs
= strstr(*lc_ctype
, ".utf")) || (cs
= strstr(*lc_ctype
, ".UTF"))) {
355 if (cs
[4] == '8' && (cs
[5] == '\0' || cs
[5] == '@')) {
364 static const unsigned char utf8mblen
[256] = {
365 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
366 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
367 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
368 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
369 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
370 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
371 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
372 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
373 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
374 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
375 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
376 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
377 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
378 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
379 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
380 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
382 static const unsigned char utf8mask
[] = {
383 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01
386 static size_t utf8towc(wchar_t *wc
, const unsigned char *uc
, size_t len
)
388 unsigned char ub
= utf8mblen
[*uc
];
390 if (!ub
|| ub
> len
|| ub
> 3) {
394 *wc
= *uc
& utf8mask
[ub
];
398 if ((uc
[1] & 0xc0) != 0x80) {
405 if ((uc
[1] & 0xc0) != 0x80) {
412 if ((uc
[1] & 0xc0) != 0x80) {
426 static size_t parse_locmb(php_http_url_t
*url
, const char *ptr
, const char *end
)
430 #if defined(HAVE_MBRTOWC)
433 consumed
= mbrtowc(&wchar
, ptr
, end
- ptr
, &ps
);
434 #elif defined(HAVE_MBTOWC)
435 consumed
= mbtowc(&wchar
, ptr
, end
- ptr
);
438 if (!consumed
|| consumed
== (size_t) -1 || !iswalnum(wchar
)) {
447 static zend_bool
isualnum(wchar_t ch
)
452 if (ch
>= 0x30 && ch
<= 0x39) {
455 for (i
= 0; i
< sizeof(utf8_ranges
)/sizeof(utf8_range_t
); ++i
) {
456 if (utf8_ranges
[i
].start
== ch
) {
458 } else if (utf8_ranges
[i
].start
<= ch
&& utf8_ranges
[i
].end
>= ch
) {
459 if (utf8_ranges
[i
].step
== 1) {
469 static size_t parse_utf8mb(php_http_url_t
*url
, const char *ptr
, const char *end
)
471 char *lc_ctype
= NULL
;
473 if (0 && cs_is_utf8(&lc_ctype
)) {
474 return parse_locmb(url
, ptr
, end
);
477 size_t consumed
= utf8towc(&wchar
, (const unsigned char *) ptr
, end
- ptr
);
479 if (!consumed
|| consumed
== (size_t) -1 || !isualnum(wchar
)) {
488 static STATUS
parse_userinfo(php_http_url_t
*url
, const char *ptr
, const char *end
)
490 const char *password
= NULL
, *tmp
= ptr
;
491 TSRMLS_FETCH_FROM_CTX(url
->ts
);
497 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
498 "Failed to parse password; duplicate ':' at pos %u in '%s'",
499 (unsigned) (ptr
- tmp
), tmp
);
506 if (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2))) {
507 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
508 "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'",
509 (unsigned) (ptr
- tmp
), tmp
);
515 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
516 case '+': case ',': case ';': case '=': /* sub-delims */
517 case '-': case '.': case '_': case '~': /* unreserved */
518 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
519 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
520 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
521 case 'V': case 'W': case 'X': case 'Y': case 'Z':
522 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
523 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
524 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
525 case 'v': case 'w': case 'x': case 'y': case 'z':
526 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
527 case '7': case '8': case '9':
532 if (url
->flags
& PHP_HTTP_URL_PARSE_UTF8MB
) {
533 size_t n
= parse_utf8mb(url
, ptr
, end
);
540 #ifdef PHP_HTTP_HAVE_WCHAR
541 else if (url
->flags
& PHP_HTTP_URL_PARSE_LOCMB
) {
542 size_t n
= parse_locmb(url
, ptr
, end
);
550 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
551 "Failed to parse userinfo; unexpected byte 0x%02x at pos %u in '%s'",
552 *ptr
, (unsigned) (ptr
- tmp
), tmp
);
554 } while(++ptr
!= end
);
557 url
->authority
.userinfo
.username
.len
= password
- tmp
- 1;
558 url
->authority
.userinfo
.username
.str
= estrndup(tmp
,
559 url
->authority
.userinfo
.username
.len
);
560 url
->authority
.userinfo
.password
.len
= end
- password
;
561 url
->authority
.userinfo
.password
.str
= estrndup(password
,
562 url
->authority
.userinfo
.password
.len
);
564 url
->authority
.userinfo
.username
.len
= end
- tmp
;
565 url
->authority
.userinfo
.username
.str
= estrndup(tmp
,
566 url
->authority
.userinfo
.username
.len
);
572 static STATUS
parse_hostinfo(php_http_url_t
*url
, const char *ptr
, const char *end
)
574 const char *tmp
= ptr
, *port
= NULL
;
575 TSRMLS_FETCH_FROM_CTX(url
->ts
);
577 /* FIXME: IP(v6) addresses */
582 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
583 "Failed to parse port; duplicate ':' at pos %u in '%s'",
584 (unsigned) (ptr
- tmp
), tmp
);
591 if (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2))) {
592 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
593 "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'",
594 (unsigned) (ptr
- tmp
), tmp
);
600 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
601 case '+': case ',': case ';': case '=': /* sub-delims */
602 case '-': case '.': case '_': case '~': /* unreserved */
603 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
604 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
605 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
606 case 'V': case 'W': case 'X': case 'Y': case 'Z':
607 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
608 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
609 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
610 case 'v': case 'w': case 'x': case 'y': case 'z':
612 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
613 "Failed to parse port; unexpected char '%c' at pos %u in '%s'",
614 *ptr
, (unsigned) (ptr
- tmp
), tmp
);
618 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
619 case '7': case '8': case '9':
622 url
->authority
.port
*= 10;
623 url
->authority
.port
+= *ptr
- '0';
629 if (url
->flags
& PHP_HTTP_URL_PARSE_UTF8MB
) {
630 size_t n
= parse_utf8mb(url
, ptr
, end
);
637 #ifdef PHP_HTTP_HAVE_WCHAR
638 else if ((url
->flags
& PHP_HTTP_URL_PARSE_LOCMB
) || (url
->flags
& PHP_HTTP_URL_PARSE_LOCIDN
)) {
639 size_t n
= parse_locmb(url
, ptr
, end
);
648 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
649 "Failed to parse hostinfo; unexpected byte 0x%02x at pos %u in '%s'",
650 (unsigned char) *ptr
, (unsigned) (ptr
- tmp
), tmp
);
653 } while (++ptr
!= end
);
656 url
->authority
.host
.len
= port
- tmp
- 1;
658 url
->authority
.host
.len
= end
- tmp
;
661 url
->authority
.host
.str
= estrndup(tmp
, url
->authority
.host
.len
);
663 #ifdef PHP_HTTP_HAVE_IDN
664 if (url
->flags
& PHP_HTTP_URL_PARSE_UTF8IDN
) {
666 int rv
= idna_to_ascii_8z(url
->authority
.host
.str
, &idn
, IDNA_ALLOW_UNASSIGNED
|IDNA_USE_STD3_ASCII_RULES
);
668 if (rv
!= IDNA_SUCCESS
) {
669 php_error_docref(NULL TSRMLS_CC
, E_NOTICE
, "Failed to parse IDN: '%s'", idna_strerror(rv
));
671 STR_SET(url
->authority
.host
.str
, estrdup(idn
));
674 } else if (url
->flags
& PHP_HTTP_URL_PARSE_LOCIDN
) {
676 int rv
= idna_to_ascii_lz(url
->authority
.host
.str
, &idn
, IDNA_ALLOW_UNASSIGNED
|IDNA_USE_STD3_ASCII_RULES
);
678 if (rv
!= IDNA_SUCCESS
) {
679 php_error_docref(NULL TSRMLS_CC
, E_NOTICE
, "Failed to parse IDN: '%s'", idna_strerror(rv
));
681 STR_SET(url
->authority
.host
.str
, estrdup(idn
));
690 static const char *parse_authority(php_http_url_t
*url
, const char *ptr
, const char *end
)
692 const char *tmp
= ptr
;
697 /* userinfo delimiter */
698 if (tmp
!= ptr
&& SUCCESS
!= parse_userinfo(url
, tmp
, ptr
)) {
709 if (tmp
!= ptr
&& SUCCESS
!= parse_hostinfo(url
, tmp
, ptr
)) {
714 } while (++ptr
<= end
);
719 static const char *parse_path(php_http_url_t
*url
, const char *ptr
, const char *end
)
721 const char *tmp
= ptr
;
722 TSRMLS_FETCH_FROM_CTX(url
->ts
);
728 url
->path
.len
= ptr
- tmp
;
729 url
->path
.str
= estrndup(tmp
, url
->path
.len
);
733 if (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2))) {
734 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
735 "Failed to parse path; invalid percent encoding at pos %u in '%s'",
736 (unsigned) (ptr
- tmp
), tmp
);
742 case '/': /* yeah, well */
743 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
744 case '+': case ',': case ';': case '=': /* sub-delims */
745 case '-': case '.': case '_': case '~': /* unreserved */
746 case ':': case '@': /* pchar */
747 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
748 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
749 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
750 case 'V': case 'W': case 'X': case 'Y': case 'Z':
751 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
752 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
753 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
754 case 'v': case 'w': case 'x': case 'y': case 'z':
755 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
756 case '7': case '8': case '9':
761 if (url
->flags
& PHP_HTTP_URL_PARSE_UTF8MB
) {
762 size_t n
= parse_utf8mb(url
, ptr
, end
);
769 #if PHP_HTTP_HAVE_WCHAR
770 else if (url
->flags
& PHP_HTTP_URL_PARSE_LOCMB
) {
771 size_t n
= parse_locmb(url
, ptr
, end
);
779 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
780 "Failed to parse path; unexpected byte 0x%02x pos %u in '%s'",
781 *ptr
, (unsigned) (ptr
- tmp
), tmp
);
783 } while (++ptr
<= end
);
788 static const char *parse_query(php_http_url_t
*url
, const char *ptr
, const char *end
)
790 const char *tmp
= ptr
+ !!*ptr
;
791 TSRMLS_FETCH_FROM_CTX(url
->ts
);
797 url
->query
.len
= ptr
- tmp
;
798 url
->query
.str
= estrndup(tmp
, url
->query
.len
);
802 if (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2))) {
803 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
804 "Failed to parse query; invalid percent encoding at pos %u in '%s'",
805 (unsigned) (ptr
- tmp
), tmp
);
811 case '?': case '/': /* yeah, well */
812 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
813 case '+': case ',': case ';': case '=': /* sub-delims */
814 case '-': case '.': case '_': case '~': /* unreserved */
815 case ':': case '@': /* pchar */
816 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
817 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
818 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
819 case 'V': case 'W': case 'X': case 'Y': case 'Z':
820 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
821 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
822 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
823 case 'v': case 'w': case 'x': case 'y': case 'z':
824 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
825 case '7': case '8': case '9':
830 #ifdef PHP_HTTP_HAVE_WCHAR
831 if (url
->flags
& PHP_HTTP_URL_PARSE_LOCMB
) {
832 size_t n
= parse_locmb(url
, ptr
, end
);
840 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
841 "Failed to parse query; unexpected byte 0x%02x at pos %u in '%s'",
842 *ptr
, (unsigned) (ptr
- tmp
), tmp
);
844 } while (++ptr
<= end
);
849 static const char *parse_fragment(php_http_url_t
*url
, const char *ptr
, const char *end
)
851 const char *tmp
= ptr
+ !!*ptr
;
852 TSRMLS_FETCH_FROM_CTX(url
->ts
);
857 url
->fragment
.len
= ptr
- tmp
;
858 url
->fragment
.str
= estrndup(tmp
, url
->fragment
.len
);
862 if (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2))) {
863 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
864 "Failed to parse query; invalid percent encoding at pos %u in '%s'",
865 (unsigned) (ptr
- tmp
), tmp
);
871 case '?': case '/': /* yeah, well */
872 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
873 case '+': case ',': case ';': case '=': /* sub-delims */
874 case '-': case '.': case '_': case '~': /* unreserved */
875 case ':': case '@': /* pchar */
876 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
877 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
878 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
879 case 'V': case 'W': case 'X': case 'Y': case 'Z':
880 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
881 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
882 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
883 case 'v': case 'w': case 'x': case 'y': case 'z':
884 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
885 case '7': case '8': case '9':
890 #if PHP_HTTP_HAVE_WCHAR
891 if (url
->flags
& PHP_HTTP_URL_PARSE_LOCMB
) {
892 size_t n
= parse_locmb(url
, ptr
, end
);
900 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
901 "Failed to parse fragment; unexpected byte 0x%02x at pos %u in '%s'",
902 *ptr
, (unsigned) (ptr
- tmp
), tmp
);
904 } while (++ptr
<= end
);
909 static const char *parse_hier(php_http_url_t
*url
, const char *ptr
, const char *end
)
913 if (*(ptr
+ 1) == '/') {
914 if (!(ptr
= parse_authority(url
, ptr
+ 2, end
))) {
920 return parse_path(url
, ptr
, end
);
923 static const char *parse_scheme(php_http_url_t
*url
, const char *ptr
, const char *end
)
925 const char *tmp
= ptr
;
930 /* scheme delimiter */
931 url
->scheme
.len
= ptr
- tmp
;
932 url
->scheme
.str
= estrndup(tmp
, url
->scheme
.len
);
935 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
936 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
937 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
938 case 'V': case 'W': case 'X': case 'Y': case 'Z':
939 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
940 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
941 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
942 case 'v': case 'w': case 'x': case 'y': case 'z':
943 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
944 case '7': case '8': case '9':
945 case '+': case '-': case '.':
950 #ifdef PHP_HTTP_HAVE_WCHAR
951 if (url
->flags
& PHP_HTTP_URL_PARSE_LOCMB
) {
952 size_t n
= parse_locmb(url
, ptr
, end
);
963 } while (++ptr
!= end
);
968 php_http_url_t
*php_http_url_init(php_http_url_t
*url
, const char *str
, size_t len
, unsigned flags TSRMLS_DC
)
970 const char *ptr
, *end
= str
+ len
;
971 zend_bool free_url
= !url
;
974 memset(url
, 0, sizeof(*url
));
976 url
= ecalloc(1, sizeof(*url
));
980 TSRMLS_SET_CTX(url
->ts
);
982 if ((ptr
= str
) && !(str
= parse_scheme(url
, ptr
, end
))) {
983 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse URL scheme: '%s'", ptr
);
985 php_http_url_free(&url
);
987 php_http_url_dtor(url
);
992 if ((ptr
= str
) && !(str
= parse_hier(url
, ptr
, end
))) {
994 php_http_url_free(&url
);
996 php_http_url_dtor(url
);
1001 if ((ptr
= str
) && !(str
= parse_query(url
, ptr
, end
))) {
1002 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse URL query: '%s'", ptr
);
1004 php_http_url_free(&url
);
1006 php_http_url_dtor(url
);
1011 if ((ptr
= str
) && !(str
= parse_fragment(url
, ptr
, end
))) {
1012 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse URL fragment: '%s'", ptr
);
1014 php_http_url_free(&url
);
1016 php_http_url_dtor(url
);
1024 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct
, 0, 0, 0)
1025 ZEND_ARG_INFO(0, old_url
)
1026 ZEND_ARG_INFO(0, new_url
)
1027 ZEND_ARG_INFO(0, flags
)
1028 ZEND_END_ARG_INFO();
1029 PHP_METHOD(HttpUrl
, __construct
)
1031 zval
*new_url
= NULL
, *old_url
= NULL
;
1032 long flags
= PHP_HTTP_URL_FROM_ENV
;
1033 zend_error_handling zeh
;
1035 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|z!z!l", &old_url
, &new_url
, &flags
), invalid_arg
, return);
1037 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
1039 php_url
*res_purl
, *new_purl
= NULL
, *old_purl
= NULL
;
1042 switch (Z_TYPE_P(new_url
)) {
1045 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
1048 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
1050 new_purl
= php_url_parse(Z_STRVAL_P(cpy
));
1051 zval_ptr_dtor(&cpy
);
1056 zend_restore_error_handling(&zeh TSRMLS_CC
);
1061 switch (Z_TYPE_P(old_url
)) {
1064 old_purl
= php_http_url_from_struct(NULL
, HASH_OF(old_url
) TSRMLS_CC
);
1067 zval
*cpy
= php_http_ztyp(IS_STRING
, old_url
);
1069 old_purl
= php_url_parse(Z_STRVAL_P(cpy
));
1070 zval_ptr_dtor(&cpy
);
1076 php_url_free(new_purl
);
1078 zend_restore_error_handling(&zeh TSRMLS_CC
);
1083 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
1084 php_http_url_to_struct(res_purl
, getThis() TSRMLS_CC
);
1086 php_url_free(res_purl
);
1088 php_url_free(old_purl
);
1091 php_url_free(new_purl
);
1094 zend_restore_error_handling(&zeh TSRMLS_CC
);
1097 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod
, 0, 0, 1)
1098 ZEND_ARG_INFO(0, more_url_parts
)
1099 ZEND_ARG_INFO(0, flags
)
1100 ZEND_END_ARG_INFO();
1101 PHP_METHOD(HttpUrl
, mod
)
1103 zval
*new_url
= NULL
;
1104 long flags
= PHP_HTTP_URL_JOIN_PATH
| PHP_HTTP_URL_JOIN_QUERY
;
1105 zend_error_handling zeh
;
1107 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z!|l", &new_url
, &flags
), invalid_arg
, return);
1109 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
1111 php_url
*new_purl
= NULL
, *old_purl
= NULL
;
1114 switch (Z_TYPE_P(new_url
)) {
1117 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
1120 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
1122 new_purl
= php_url_parse(Z_STRVAL_P(new_url
));
1123 zval_ptr_dtor(&cpy
);
1128 zend_restore_error_handling(&zeh TSRMLS_CC
);
1133 if ((old_purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
1136 ZVAL_OBJVAL(return_value
, zend_objects_clone_obj(getThis() TSRMLS_CC
), 0);
1138 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
1139 php_http_url_to_struct(res_purl
, return_value TSRMLS_CC
);
1141 php_url_free(res_purl
);
1142 php_url_free(old_purl
);
1145 php_url_free(new_purl
);
1148 zend_restore_error_handling(&zeh TSRMLS_CC
);
1151 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString
, 0, 0, 0)
1152 ZEND_END_ARG_INFO();
1153 PHP_METHOD(HttpUrl
, toString
)
1155 if (SUCCESS
== zend_parse_parameters_none()) {
1158 if ((purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
1162 php_http_url(0, purl
, NULL
, NULL
, &str
, &len TSRMLS_CC
);
1164 RETURN_STRINGL(str
, len
, 0);
1167 RETURN_EMPTY_STRING();
1170 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray
, 0, 0, 0)
1171 ZEND_END_ARG_INFO();
1172 PHP_METHOD(HttpUrl
, toArray
)
1176 if (SUCCESS
!= zend_parse_parameters_none()) {
1180 /* strip any non-URL properties */
1181 purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
);
1182 php_http_url_to_struct(purl
, return_value TSRMLS_CC
);
1186 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_parse
, 0, 0, 1)
1187 ZEND_ARG_INFO(0, url
)
1188 ZEND_ARG_INFO(0, flags
)
1189 ZEND_END_ARG_INFO();
1190 PHP_METHOD(HttpUrl
, parse
)
1197 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|l", &str
, &len
, &flags
)) {
1201 if (php_http_url_init(&url
, str
, len
, flags TSRMLS_CC
)) {
1202 printf(" scheme=(%zu)%s\n", url
.scheme
.len
,url
.scheme
.str
);
1203 printf("username=(%zu)%s\n", url
.authority
.userinfo
.username
.len
,url
.authority
.userinfo
.username
.str
);
1204 printf("password=(%zu)%s\n", url
.authority
.userinfo
.password
.len
,url
.authority
.userinfo
.password
.str
);
1205 printf(" host=(%zu)%s\n", url
.authority
.host
.len
,url
.authority
.host
.str
);
1206 printf(" port=%d\n", (int) url
.authority
.port
);
1207 printf(" path=(%zu)%s\n", url
.path
.len
,url
.path
.str
);
1208 printf(" query=(%zu)%s\n", url
.query
.len
,url
.query
.str
);
1209 printf("fragment=(%zu)%s\n", url
.fragment
.len
,url
.fragment
.str
);
1210 php_http_url_dtor(&url
);
1214 static zend_function_entry php_http_url_methods
[] = {
1215 PHP_ME(HttpUrl
, __construct
, ai_HttpUrl___construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
1216 PHP_ME(HttpUrl
, mod
, ai_HttpUrl_mod
, ZEND_ACC_PUBLIC
)
1217 PHP_ME(HttpUrl
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
1218 ZEND_MALIAS(HttpUrl
, __toString
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
1219 PHP_ME(HttpUrl
, toArray
, ai_HttpUrl_toArray
, ZEND_ACC_PUBLIC
)
1220 PHP_ME(HttpUrl
, parse
, ai_HttpUrl_parse
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
1221 EMPTY_FUNCTION_ENTRY
1224 zend_class_entry
*php_http_url_class_entry
;
1226 PHP_MINIT_FUNCTION(http_url
)
1228 zend_class_entry ce
= {0};
1230 INIT_NS_CLASS_ENTRY(ce
, "http", "Url", php_http_url_methods
);
1231 php_http_url_class_entry
= zend_register_internal_class(&ce TSRMLS_CC
);
1233 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1234 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1235 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1236 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1237 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1238 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1239 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1240 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1242 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC
);
1243 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC
);
1244 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC
);
1245 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC
);
1246 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC
);
1247 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC
);
1248 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC
);
1249 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC
);
1250 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC
);
1251 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC
);
1252 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC
);
1253 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC
);
1254 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC
);
1256 #ifdef PHP_HTTP_HAVE_WCHAR
1257 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_LOCMB"), PHP_HTTP_URL_PARSE_LOCMB TSRMLS_CC
);
1259 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_UTF8MB"), PHP_HTTP_URL_PARSE_UTF8MB TSRMLS_CC
);
1260 #ifdef PHP_HTTP_HAVE_IDN
1261 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_LOCIDN"), PHP_HTTP_URL_PARSE_LOCIDN TSRMLS_CC
);
1262 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_UTF8IDN"), PHP_HTTP_URL_PARSE_UTF8IDN TSRMLS_CC
);
1274 * vim600: noet sw=4 ts=4 fdm=marker
1275 * vim<600: noet sw=4 ts=4