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 #include "php_http_utf8.h"
26 static inline char *localhostname(void)
28 char hostname
[1024] = {0};
31 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
32 return estrdup(hostname
);
34 #elif defined(HAVE_GETHOSTNAME)
35 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
36 # if defined(HAVE_GETDOMAINNAME)
37 size_t hlen
= strlen(hostname
);
38 if (hlen
<= lenof(hostname
) - lenof("(none)")) {
39 hostname
[hlen
++] = '.';
40 if (SUCCESS
== getdomainname(&hostname
[hlen
], lenof(hostname
) - hlen
)) {
41 if (!strcmp(&hostname
[hlen
], "(none)")) {
42 hostname
[hlen
- 1] = '\0';
44 return estrdup(hostname
);
48 if (strcmp(hostname
, "(none)")) {
49 return estrdup(hostname
);
53 return estrndup("localhost", lenof("localhost"));
56 static php_url
*php_http_url_from_env(php_url
*url TSRMLS_DC
)
58 zval
*https
, *zhost
, *zport
;
62 url
= ecalloc(1, sizeof(*url
));
66 zport
= php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC
);
67 if (zport
&& IS_LONG
== is_numeric_string(Z_STRVAL_P(zport
), Z_STRLEN_P(zport
), &port
, NULL
, 0)) {
72 https
= php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC
);
73 if (https
&& !strcasecmp(Z_STRVAL_P(https
), "ON")) {
74 url
->scheme
= estrndup("https", lenof("https"));
76 url
->scheme
= estrndup("http", lenof("http"));
80 if ((((zhost
= php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC
)) ||
81 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC
)) ||
82 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC
)))) && Z_STRLEN_P(zhost
)) {
83 size_t stop_at
= strspn(Z_STRVAL_P(zhost
), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
85 url
->host
= estrndup(Z_STRVAL_P(zhost
), stop_at
);
87 url
->host
= localhostname();
91 if (SG(request_info
).request_uri
&& SG(request_info
).request_uri
[0]) {
92 const char *q
= strchr(SG(request_info
).request_uri
, '?');
95 url
->path
= estrndup(SG(request_info
).request_uri
, q
- SG(request_info
).request_uri
);
97 url
->path
= estrdup(SG(request_info
).request_uri
);
102 if (SG(request_info
).query_string
&& SG(request_info
).query_string
[0]) {
103 url
->query
= estrdup(SG(request_info
).query_string
);
109 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
)
111 php_url
*url
, *tmp_url
= NULL
;
113 /* set from env if requested */
114 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
115 php_url
*env_url
= php_http_url_from_env(NULL TSRMLS_CC
);
117 php_http_url(flags
^ PHP_HTTP_URL_FROM_ENV
, env_url
, old_url
, &tmp_url
, NULL
, NULL TSRMLS_CC
);
119 php_url_free(env_url
);
123 url
= ecalloc(1, sizeof(*url
));
125 #define __URLSET(u,n) \
127 #define __URLCPY(n) \
128 url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL)
130 if (!(flags
& PHP_HTTP_URL_STRIP_PORT
)) {
131 url
->port
= __URLSET(new_url
, port
) ? new_url
->port
: ((old_url
) ? old_url
->port
: 0);
133 if (!(flags
& PHP_HTTP_URL_STRIP_USER
)) {
136 if (!(flags
& PHP_HTTP_URL_STRIP_PASS
)) {
143 if (!(flags
& PHP_HTTP_URL_STRIP_PATH
)) {
144 if ((flags
& PHP_HTTP_URL_JOIN_PATH
) && __URLSET(old_url
, path
) && __URLSET(new_url
, path
) && *new_url
->path
!= '/') {
145 size_t old_path_len
= strlen(old_url
->path
), new_path_len
= strlen(new_url
->path
);
147 url
->path
= ecalloc(1, old_path_len
+ new_path_len
+ 1 + 1);
149 strcat(url
->path
, old_url
->path
);
150 if (url
->path
[old_path_len
- 1] != '/') {
151 php_dirname(url
->path
, old_path_len
);
152 strcat(url
->path
, "/");
154 strcat(url
->path
, new_url
->path
);
159 if (!(flags
& PHP_HTTP_URL_STRIP_QUERY
)) {
160 if ((flags
& PHP_HTTP_URL_JOIN_QUERY
) && __URLSET(new_url
, query
) && __URLSET(old_url
, query
)) {
167 ZVAL_STRING(&qstr
, old_url
->query
, 0);
168 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
169 ZVAL_STRING(&qstr
, new_url
->query
, 0);
170 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
173 php_http_querystring_update(&qarr
, NULL
, &qstr TSRMLS_CC
);
174 url
->query
= Z_STRVAL(qstr
);
180 if (!(flags
& PHP_HTTP_URL_STRIP_FRAGMENT
)) {
184 /* done with copy & combine & strip */
186 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
187 /* free old_url we tainted above */
188 php_url_free(tmp_url
);
191 /* set some sane defaults */
194 url
->scheme
= estrndup("http", lenof("http"));
198 url
->host
= estrndup("localhost", lenof("localhost"));
202 url
->path
= estrndup("/", 1);
203 } else if (url
->path
[0] != '/') {
204 size_t plen
= strlen(url
->path
);
205 char *path
= emalloc(plen
+ 1 + 1);
208 memcpy(&path
[1], url
->path
, plen
+ 1);
209 STR_SET(url
->path
, path
);
211 /* replace directory references if path is not a single slash */
212 if ((flags
& PHP_HTTP_URL_SANITIZE_PATH
)
213 && url
->path
[0] && (url
->path
[0] != '/' || url
->path
[1])) {
214 char *ptr
, *end
= url
->path
+ strlen(url
->path
) + 1;
216 for (ptr
= strchr(url
->path
, '/'); ptr
; ptr
= strchr(ptr
, '/')) {
219 memmove(&ptr
[1], &ptr
[2], end
- &ptr
[2]);
229 memmove(&ptr
[1], &ptr
[3], end
- &ptr
[3]);
235 while (ptr
!= url
->path
) {
240 memmove(&ptr
[1], pos
, end
- pos
);
242 } else if (!ptr
[3]) {
261 /* unset default ports */
263 if ( ((url
->port
== 80) && !strcmp(url
->scheme
, "http"))
264 || ((url
->port
==443) && !strcmp(url
->scheme
, "https"))
271 php_http_url_to_string(url
, url_str
, url_len TSRMLS_CC
);
281 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
)
283 const char *arg_sep_str
;
285 php_http_buffer_t
*qstr
= php_http_buffer_new();
287 php_http_url_argsep(&arg_sep_str
, &arg_sep_len TSRMLS_CC
);
289 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
)) {
290 php_http_buffer_free(&qstr
);
294 php_http_buffer_data(qstr
, encoded_str
, encoded_len
);
295 php_http_buffer_free(&qstr
);
300 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
)
302 if (pre_encoded_len
&& pre_encoded_str
) {
303 php_http_buffer_append(qstr
, pre_encoded_str
, pre_encoded_len
);
306 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
)) {
313 void php_http_url_dtor(php_http_url_t
*url
)
315 STR_FREE(url
->scheme
.str
);
316 STR_FREE(url
->authority
.userinfo
.username
.str
);
317 STR_FREE(url
->authority
.userinfo
.password
.str
);
318 STR_FREE(url
->authority
.host
.str
);
319 STR_FREE(url
->path
.str
);
320 STR_FREE(url
->query
.str
);
321 STR_FREE(url
->fragment
.str
);
324 void php_http_url_free(php_http_url_t
**url
)
327 php_http_url_dtor(*url
);
333 static size_t parse_mb_utf8(php_http_url_t
*url
, const char *ptr
, const char *end
, zend_bool idn
)
336 size_t consumed
= utf8towc(&wchar
, (const unsigned char *) ptr
, end
- ptr
);
338 if (!consumed
|| consumed
== (size_t) -1) {
341 if (!idn
&& !isualnum(wchar
)) {
348 #ifdef PHP_HTTP_HAVE_WCHAR
349 static size_t parse_mb_loc(php_http_url_t
*url
, const char *ptr
, const char *end
, zend_bool idn
)
353 #if defined(HAVE_MBRTOWC)
356 consumed
= mbrtowc(&wchar
, ptr
, end
- ptr
, &ps
);
357 #elif defined(HAVE_MBTOWC)
358 consumed
= mbtowc(&wchar
, ptr
, end
- ptr
);
361 if (!consumed
|| consumed
== (size_t) -1) {
364 if (!idn
&& !iswalnum(wchar
)) {
372 typedef enum parse_mb_what
{
381 static const char * const parse_what
[] = {
390 static size_t parse_mb(php_http_url_t
*url
, parse_mb_what_t what
, const char *ptr
, const char *end
, const char *begin
, zend_bool silent
)
393 zend_bool idn
= (what
== PARSE_HOSTINFO
) && (url
->flags
& PHP_HTTP_URL_PARSE_IDN
);
395 if (url
->flags
& PHP_HTTP_URL_PARSE_MBUTF8
) {
396 consumed
= parse_mb_utf8(url
, ptr
, end
, idn
);
398 #ifdef PHP_HTTP_HAVE_WCHAR
399 else if (url
->flags
& PHP_HTTP_URL_PARSE_MBLOC
) {
400 consumed
= parse_mb_loc(url
, ptr
, end
, idn
);
404 if (!consumed
&& !silent
) {
405 TSRMLS_FETCH_FROM_CTX(url
->ts
);
406 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
407 "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
408 parse_what
[what
], (unsigned char) *ptr
, (unsigned) (ptr
- begin
), begin
);
414 static STATUS
parse_userinfo(php_http_url_t
*url
, const char *ptr
, const char *end
)
417 const char *password
= NULL
, *tmp
= ptr
;
418 TSRMLS_FETCH_FROM_CTX(url
->ts
);
424 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
425 "Failed to parse password; duplicate ':' at pos %u in '%s'",
426 (unsigned) (ptr
- tmp
), tmp
);
433 if (ptr
[1] != '%' && (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2)))) {
434 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
435 "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'",
436 (unsigned) (ptr
- tmp
), tmp
);
442 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
443 case '+': case ',': case ';': case '=': /* sub-delims */
444 case '-': case '.': case '_': case '~': /* unreserved */
445 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
446 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
447 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
448 case 'V': case 'W': case 'X': case 'Y': case 'Z':
449 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
450 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
451 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
452 case 'v': case 'w': case 'x': case 'y': case 'z':
453 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
454 case '7': case '8': case '9':
459 if (!(mb
= parse_mb(url
, PARSE_USERINFO
, ptr
, end
, tmp
, 0))) {
464 } while(++ptr
!= end
);
467 if ((url
->authority
.userinfo
.username
.len
= password
- tmp
- 1)) {
468 url
->authority
.userinfo
.username
.str
= estrndup(tmp
,
469 url
->authority
.userinfo
.username
.len
);
471 if ((url
->authority
.userinfo
.password
.len
= end
- password
)) {
472 url
->authority
.userinfo
.password
.str
= estrndup(password
,
473 url
->authority
.userinfo
.password
.len
);
476 if ((url
->authority
.userinfo
.username
.len
= end
- tmp
)) {
477 url
->authority
.userinfo
.username
.str
= estrndup(tmp
,
478 url
->authority
.userinfo
.username
.len
);
485 static STATUS
parse_hostinfo(php_http_url_t
*url
, const char *ptr
, const char *end
)
488 const char *tmp
= ptr
, *port
= NULL
;
489 TSRMLS_FETCH_FROM_CTX(url
->ts
);
491 /* FIXME: IP(v6) addresses */
496 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
497 "Failed to parse port; duplicate ':' at pos %u in '%s'",
498 (unsigned) (ptr
- tmp
), tmp
);
505 if (ptr
[1] != '%' && (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2)))) {
506 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
507 "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'",
508 (unsigned) (ptr
- tmp
), tmp
);
514 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
515 case '+': case ',': case ';': case '=': /* sub-delims */
516 case '-': case '.': case '_': case '~': /* unreserved */
517 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
518 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
519 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
520 case 'V': case 'W': case 'X': case 'Y': case 'Z':
521 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
522 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
523 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
524 case 'v': case 'w': case 'x': case 'y': case 'z':
526 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
527 "Failed to parse port; unexpected char '%c' at pos %u in '%s'",
528 (unsigned char) *ptr
, (unsigned) (ptr
- tmp
), tmp
);
532 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
533 case '7': case '8': case '9':
536 url
->authority
.port
*= 10;
537 url
->authority
.port
+= *ptr
- '0';
543 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
544 "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
545 (unsigned char) *ptr
, (unsigned) (ptr
- tmp
), tmp
);
547 } else if (!(mb
= parse_mb(url
, PARSE_HOSTINFO
, ptr
, end
, tmp
, 0))) {
552 } while (++ptr
!= end
);
555 url
->authority
.host
.len
= port
- tmp
- 1;
557 url
->authority
.host
.len
= end
- tmp
;
560 url
->authority
.host
.str
= estrndup(tmp
, url
->authority
.host
.len
);
562 #ifdef PHP_HTTP_HAVE_IDN
563 if (url
->flags
& PHP_HTTP_URL_PARSE_IDN
) {
567 if (url
->flags
& PHP_HTTP_URL_PARSE_MBUTF8
) {
568 rv
= idna_to_ascii_8z(url
->authority
.host
.str
, &idn
, IDNA_ALLOW_UNASSIGNED
|IDNA_USE_STD3_ASCII_RULES
);
570 # ifdef PHP_HTTP_HAVE_WCHAR
571 else if (url
->flags
& PHP_HTTP_URL_PARSE_MBLOC
) {
572 rv
= idna_to_ascii_lz(url
->authority
.host
.str
, &idn
, IDNA_ALLOW_UNASSIGNED
|IDNA_USE_STD3_ASCII_RULES
);
575 if (rv
!= IDNA_SUCCESS
) {
576 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse IDN; %s", idna_strerror(rv
));
579 STR_SET(url
->authority
.host
.str
, estrdup(idn
));
580 url
->authority
.host
.len
= strlen(idn
);
589 static const char *parse_authority(php_http_url_t
*url
, const char *ptr
, const char *end
)
591 const char *tmp
= ptr
;
596 /* userinfo delimiter */
597 if (tmp
!= ptr
&& SUCCESS
!= parse_userinfo(url
, tmp
, ptr
)) {
608 if (tmp
!= ptr
&& SUCCESS
!= parse_hostinfo(url
, tmp
, ptr
)) {
613 } while (++ptr
<= end
);
618 static const char *parse_path(php_http_url_t
*url
, const char *ptr
, const char *end
)
621 const char *tmp
= ptr
;
622 TSRMLS_FETCH_FROM_CTX(url
->ts
);
628 if ((url
->path
.len
= ptr
- tmp
)) {
629 url
->path
.str
= estrndup(tmp
, url
->path
.len
);
634 if (ptr
[1] != '%' && (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2)))) {
635 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
636 "Failed to parse path; invalid percent encoding at pos %u in '%s'",
637 (unsigned) (ptr
- tmp
), tmp
);
643 case '/': /* yeah, well */
644 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
645 case '+': case ',': case ';': case '=': /* sub-delims */
646 case '-': case '.': case '_': case '~': /* unreserved */
647 case ':': case '@': /* pchar */
648 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
649 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
650 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
651 case 'V': case 'W': case 'X': case 'Y': case 'Z':
652 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
653 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
654 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
655 case 'v': case 'w': case 'x': case 'y': case 'z':
656 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
657 case '7': case '8': case '9':
662 if (!(mb
= parse_mb(url
, PARSE_PATH
, ptr
, end
, tmp
, 0))) {
667 } while (++ptr
<= end
);
672 static const char *parse_query(php_http_url_t
*url
, const char *ptr
, const char *end
)
675 const char *tmp
= ptr
+ !!*ptr
;
676 TSRMLS_FETCH_FROM_CTX(url
->ts
);
682 if ((url
->query
.len
= ptr
- tmp
)) {
683 url
->query
.str
= estrndup(tmp
, url
->query
.len
);
688 if (ptr
[1] != '%' && (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2)))) {
689 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
690 "Failed to parse query; invalid percent encoding at pos %u in '%s'",
691 (unsigned) (ptr
- tmp
), tmp
);
697 case '?': case '/': /* yeah, well */
698 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
699 case '+': case ',': case ';': case '=': /* sub-delims */
700 case '-': case '.': case '_': case '~': /* unreserved */
701 case ':': case '@': /* pchar */
702 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
703 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
704 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
705 case 'V': case 'W': case 'X': case 'Y': case 'Z':
706 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
707 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
708 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
709 case 'v': case 'w': case 'x': case 'y': case 'z':
710 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
711 case '7': case '8': case '9':
716 if (!(mb
= parse_mb(url
, PARSE_QUERY
, ptr
, end
, tmp
, 0))) {
721 } while (++ptr
<= end
);
726 static const char *parse_fragment(php_http_url_t
*url
, const char *ptr
, const char *end
)
729 const char *tmp
= ptr
+ !!*ptr
;
730 TSRMLS_FETCH_FROM_CTX(url
->ts
);
735 if ((url
->fragment
.len
= ptr
- tmp
)) {
736 url
->fragment
.str
= estrndup(tmp
, url
->fragment
.len
);
741 if (ptr
[1] != '%' && (end
- ptr
<= 2 || !isxdigit(*(ptr
+1)) || !isxdigit(*(ptr
+2)))) {
742 php_error_docref(NULL TSRMLS_CC
, E_WARNING
,
743 "Failed to parse query; invalid percent encoding at pos %u in '%s'",
744 (unsigned) (ptr
- tmp
), tmp
);
750 case '?': case '/': /* yeah, well */
751 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
752 case '+': case ',': case ';': case '=': /* sub-delims */
753 case '-': case '.': case '_': case '~': /* unreserved */
754 case ':': case '@': /* pchar */
755 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
756 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
757 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
758 case 'V': case 'W': case 'X': case 'Y': case 'Z':
759 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
760 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
761 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
762 case 'v': case 'w': case 'x': case 'y': case 'z':
763 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
764 case '7': case '8': case '9':
769 if (!(mb
= parse_mb(url
, PARSE_FRAGMENT
, ptr
, end
, tmp
, 0))) {
774 } while (++ptr
<= end
);
779 static const char *parse_hier(php_http_url_t
*url
, const char *ptr
, const char *end
)
783 if (*(ptr
+ 1) == '/') {
784 if (!(ptr
= parse_authority(url
, ptr
+ 2, end
))) {
790 return parse_path(url
, ptr
, end
);
793 static const char *parse_scheme(php_http_url_t
*url
, const char *ptr
, const char *end
)
796 const char *tmp
= ptr
;
801 /* scheme delimiter */
802 url
->scheme
.len
= ptr
- tmp
;
803 url
->scheme
.str
= estrndup(tmp
, url
->scheme
.len
);
806 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
807 case '7': case '8': case '9':
808 case '+': case '-': case '.':
813 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
814 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
815 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
816 case 'V': case 'W': case 'X': case 'Y': case 'Z':
817 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
818 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
819 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
820 case 'v': case 'w': case 'x': case 'y': case 'z':
825 if (!(mb
= parse_mb(url
, PARSE_SCHEME
, ptr
, end
, tmp
, 1))) {
826 /* soft fail; parse path next */
831 } while (++ptr
!= end
);
836 php_http_url_t
*php_http_url_init(php_http_url_t
*url
, const char *str
, size_t len
, unsigned flags TSRMLS_DC
)
838 const char *ptr
, *end
= str
+ len
;
839 zend_bool free_url
= !url
;
842 memset(url
, 0, sizeof(*url
));
844 url
= ecalloc(1, sizeof(*url
));
848 TSRMLS_SET_CTX(url
->ts
);
850 if ((ptr
= str
) && !(str
= parse_scheme(url
, ptr
, end
))) {
851 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse URL scheme: '%s'", ptr
);
853 php_http_url_free(&url
);
855 php_http_url_dtor(url
);
860 if ((ptr
= str
) && !(str
= parse_hier(url
, ptr
, end
))) {
862 php_http_url_free(&url
);
864 php_http_url_dtor(url
);
869 if ((ptr
= str
) && !(str
= parse_query(url
, ptr
, end
))) {
870 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse URL query: '%s'", ptr
);
872 php_http_url_free(&url
);
874 php_http_url_dtor(url
);
879 if ((ptr
= str
) && !(str
= parse_fragment(url
, ptr
, end
))) {
880 php_error_docref(NULL TSRMLS_CC
, E_WARNING
, "Failed to parse URL fragment: '%s'", ptr
);
882 php_http_url_free(&url
);
884 php_http_url_dtor(url
);
892 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct
, 0, 0, 0)
893 ZEND_ARG_INFO(0, old_url
)
894 ZEND_ARG_INFO(0, new_url
)
895 ZEND_ARG_INFO(0, flags
)
897 PHP_METHOD(HttpUrl
, __construct
)
899 zval
*new_url
= NULL
, *old_url
= NULL
;
900 long flags
= PHP_HTTP_URL_FROM_ENV
;
901 zend_error_handling zeh
;
903 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|z!z!l", &old_url
, &new_url
, &flags
), invalid_arg
, return);
905 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
907 php_url
*res_purl
, *new_purl
= NULL
, *old_purl
= NULL
;
910 switch (Z_TYPE_P(new_url
)) {
913 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
916 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
918 new_purl
= php_url_parse(Z_STRVAL_P(cpy
));
924 zend_restore_error_handling(&zeh TSRMLS_CC
);
929 switch (Z_TYPE_P(old_url
)) {
932 old_purl
= php_http_url_from_struct(NULL
, HASH_OF(old_url
) TSRMLS_CC
);
935 zval
*cpy
= php_http_ztyp(IS_STRING
, old_url
);
937 old_purl
= php_url_parse(Z_STRVAL_P(cpy
));
944 php_url_free(new_purl
);
946 zend_restore_error_handling(&zeh TSRMLS_CC
);
951 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
952 php_http_url_to_struct(res_purl
, getThis() TSRMLS_CC
);
954 php_url_free(res_purl
);
956 php_url_free(old_purl
);
959 php_url_free(new_purl
);
962 zend_restore_error_handling(&zeh TSRMLS_CC
);
965 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod
, 0, 0, 1)
966 ZEND_ARG_INFO(0, more_url_parts
)
967 ZEND_ARG_INFO(0, flags
)
969 PHP_METHOD(HttpUrl
, mod
)
971 zval
*new_url
= NULL
;
972 long flags
= PHP_HTTP_URL_JOIN_PATH
| PHP_HTTP_URL_JOIN_QUERY
;
973 zend_error_handling zeh
;
975 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z!|l", &new_url
, &flags
), invalid_arg
, return);
977 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
979 php_url
*new_purl
= NULL
, *old_purl
= NULL
;
982 switch (Z_TYPE_P(new_url
)) {
985 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
988 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
990 new_purl
= php_url_parse(Z_STRVAL_P(new_url
));
996 zend_restore_error_handling(&zeh TSRMLS_CC
);
1001 if ((old_purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
1004 ZVAL_OBJVAL(return_value
, zend_objects_clone_obj(getThis() TSRMLS_CC
), 0);
1006 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
1007 php_http_url_to_struct(res_purl
, return_value TSRMLS_CC
);
1009 php_url_free(res_purl
);
1010 php_url_free(old_purl
);
1013 php_url_free(new_purl
);
1016 zend_restore_error_handling(&zeh TSRMLS_CC
);
1019 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString
, 0, 0, 0)
1020 ZEND_END_ARG_INFO();
1021 PHP_METHOD(HttpUrl
, toString
)
1023 if (SUCCESS
== zend_parse_parameters_none()) {
1026 if ((purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
1030 php_http_url(0, purl
, NULL
, NULL
, &str
, &len TSRMLS_CC
);
1032 RETURN_STRINGL(str
, len
, 0);
1035 RETURN_EMPTY_STRING();
1038 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray
, 0, 0, 0)
1039 ZEND_END_ARG_INFO();
1040 PHP_METHOD(HttpUrl
, toArray
)
1044 if (SUCCESS
!= zend_parse_parameters_none()) {
1048 /* strip any non-URL properties */
1049 purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
);
1050 php_http_url_to_struct(purl
, return_value TSRMLS_CC
);
1054 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_parse
, 0, 0, 1)
1055 ZEND_ARG_INFO(0, url
)
1056 ZEND_ARG_INFO(0, flags
)
1057 ZEND_END_ARG_INFO();
1058 PHP_METHOD(HttpUrl
, parse
)
1064 zend_error_handling zeh
;
1066 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|l", &str
, &len
, &flags
), invalid_arg
, return);
1068 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
1069 if (php_http_url_init(&url
, str
, len
, flags TSRMLS_CC
)) {
1070 object_init_ex(return_value
, php_http_url_class_entry
);
1071 if (url
.scheme
.len
) {
1072 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("scheme"),
1073 url
.scheme
.str
, url
.scheme
.len TSRMLS_CC
);
1075 if (url
.authority
.userinfo
.username
.len
) {
1076 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("user"),
1077 url
.authority
.userinfo
.username
.str
, url
.authority
.userinfo
.username
.len TSRMLS_CC
);
1079 if (url
.authority
.userinfo
.password
.len
) {
1080 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("pass"),
1081 url
.authority
.userinfo
.password
.str
, url
.authority
.userinfo
.password
.len TSRMLS_CC
);
1083 if (url
.authority
.host
.len
) {
1084 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("host"),
1085 url
.authority
.host
.str
, url
.authority
.host
.len TSRMLS_CC
);
1087 if (url
.authority
.port
) {
1088 zend_update_property_long(php_http_url_class_entry
, return_value
, ZEND_STRL("port"),
1089 url
.authority
.port TSRMLS_CC
);
1092 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("path"),
1093 url
.path
.str
, url
.path
.len TSRMLS_CC
);
1095 if (url
.query
.len
) {
1096 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("query"),
1097 url
.query
.str
, url
.query
.len TSRMLS_CC
);
1099 if (url
.fragment
.len
) {
1100 zend_update_property_stringl(php_http_url_class_entry
, return_value
, ZEND_STRL("fragment"),
1101 url
.fragment
.str
, url
.fragment
.len TSRMLS_CC
);
1103 php_http_url_dtor(&url
);
1105 zend_restore_error_handling(&zeh TSRMLS_CC
);
1108 static zend_function_entry php_http_url_methods
[] = {
1109 PHP_ME(HttpUrl
, __construct
, ai_HttpUrl___construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
1110 PHP_ME(HttpUrl
, mod
, ai_HttpUrl_mod
, ZEND_ACC_PUBLIC
)
1111 PHP_ME(HttpUrl
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
1112 ZEND_MALIAS(HttpUrl
, __toString
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
1113 PHP_ME(HttpUrl
, toArray
, ai_HttpUrl_toArray
, ZEND_ACC_PUBLIC
)
1114 PHP_ME(HttpUrl
, parse
, ai_HttpUrl_parse
, ZEND_ACC_PUBLIC
|ZEND_ACC_STATIC
)
1115 EMPTY_FUNCTION_ENTRY
1118 zend_class_entry
*php_http_url_class_entry
;
1120 PHP_MINIT_FUNCTION(http_url
)
1122 zend_class_entry ce
= {0};
1124 INIT_NS_CLASS_ENTRY(ce
, "http", "Url", php_http_url_methods
);
1125 php_http_url_class_entry
= zend_register_internal_class(&ce TSRMLS_CC
);
1127 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1128 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1129 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1130 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1131 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1132 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1133 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1134 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC
);
1136 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC
);
1137 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC
);
1138 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC
);
1139 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC
);
1140 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC
);
1141 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC
);
1142 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC
);
1143 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC
);
1144 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC
);
1145 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC
);
1146 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC
);
1147 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC
);
1148 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC
);
1150 #ifdef PHP_HTTP_HAVE_WCHAR
1151 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC TSRMLS_CC
);
1153 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC
);
1154 #ifdef PHP_HTTP_HAVE_IDN
1155 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("PARSE_IDN"), PHP_HTTP_URL_PARSE_IDN TSRMLS_CC
);
1167 * vim600: noet sw=4 ts=4 fdm=marker
1168 * vim<600: noet sw=4 ts=4