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-2010, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
13 /* $Id: http_url_api.c 292841 2009-12-31 08:48:57Z mike $ */
17 #include <main/SAPI.h>
18 #include <ext/standard/php_string.h>
20 static inline char *localhostname(void)
22 char hostname
[1024] = {0};
25 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
26 return estrdup(hostname
);
28 #elif defined(HAVE_GETHOSTNAME)
29 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
30 # if defined(HAVE_GETDOMAINNAME)
31 size_t hlen
= strlen(hostname
);
32 if (hlen
<= lenof(hostname
) - lenof("(none)")) {
33 hostname
[hlen
++] = '.';
34 if (SUCCESS
== getdomainname(&hostname
[hlen
], lenof(hostname
) - hlen
)) {
35 if (!strcmp(&hostname
[hlen
], "(none)")) {
36 hostname
[hlen
- 1] = '\0';
38 return estrdup(hostname
);
42 if (strcmp(hostname
, "(none)")) {
43 return estrdup(hostname
);
47 return estrndup("localhost", lenof("localhost"));
50 PHP_HTTP_API
char *php_http_url_absolute(const char *url
, int flags TSRMLS_DC
)
56 purl
= php_url_parse(abs
= estrdup(url
));
59 php_http_error(HE_WARNING
, PHP_HTTP_E_URL
, "Could not parse URL (%s)", url
);
64 php_http_url(flags
, purl
, NULL
, NULL
, &abs
, NULL TSRMLS_CC
);
73 PHP_HTTP_API
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
)
75 #if defined(HAVE_GETSERVBYPORT) || defined(HAVE_GETSERVBYNAME)
78 php_url
*url
= ecalloc(1, sizeof(php_url
));
80 #define __URLSET(u,n) \
83 url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL)
85 if (!(flags
& PHP_HTTP_URL_STRIP_PORT
)) {
86 url
->port
= __URLSET(new_url
, port
) ? new_url
->port
: ((old_url
) ? old_url
->port
: 0);
88 if (!(flags
& PHP_HTTP_URL_STRIP_USER
)) {
91 if (!(flags
& PHP_HTTP_URL_STRIP_PASS
)) {
98 if (!(flags
& PHP_HTTP_URL_STRIP_PATH
)) {
99 if ((flags
& PHP_HTTP_URL_JOIN_PATH
) && __URLSET(old_url
, path
) && __URLSET(new_url
, path
) && *new_url
->path
!= '/') {
100 size_t old_path_len
= strlen(old_url
->path
), new_path_len
= strlen(new_url
->path
);
102 url
->path
= ecalloc(1, old_path_len
+ new_path_len
+ 1 + 1);
104 strcat(url
->path
, old_url
->path
);
105 if (url
->path
[old_path_len
- 1] != '/') {
106 php_dirname(url
->path
, old_path_len
);
107 strcat(url
->path
, "/");
109 strcat(url
->path
, new_url
->path
);
114 if (!(flags
& PHP_HTTP_URL_STRIP_QUERY
)) {
115 if ((flags
& PHP_HTTP_URL_JOIN_QUERY
) && __URLSET(new_url
, query
) && __URLSET(old_url
, query
)) {
122 ZVAL_STRING(&qstr
, old_url
->query
, 0);
123 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
124 ZVAL_STRING(&qstr
, new_url
->query
, 0);
125 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
128 php_http_querystring_update(&qarr
, NULL
, &qstr TSRMLS_CC
);
129 url
->query
= Z_STRVAL(qstr
);
135 if (!(flags
& PHP_HTTP_URL_STRIP_FRAGMENT
)) {
140 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
141 zval
*https
= php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC
);
142 if (https
&& !strcasecmp(Z_STRVAL_P(https
), "ON")) {
143 url
->scheme
= estrndup("https", lenof("https"));
144 } else switch (url
->port
) {
146 url
->scheme
= estrndup("https", lenof("https"));
149 #ifndef HAVE_GETSERVBYPORT
154 url
->scheme
= estrndup("http", lenof("http"));
157 #ifdef HAVE_GETSERVBYPORT
159 if ((se
= getservbyport(htons(url
->port
), "tcp")) && se
->s_name
) {
160 url
->scheme
= estrdup(se
->s_name
);
162 url
->scheme
= estrndup("http", lenof("http"));
168 url
->scheme
= estrndup("http", lenof("http"));
173 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
176 if ((((zhost
= php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC
)) ||
177 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC
)))) && Z_STRLEN_P(zhost
)) {
178 url
->host
= estrndup(Z_STRVAL_P(zhost
), Z_STRLEN_P(zhost
));
180 url
->host
= localhostname();
183 url
->host
= estrndup("localhost", lenof("localhost"));
188 if ((flags
& PHP_HTTP_URL_FROM_ENV
) && SG(request_info
).request_uri
&& SG(request_info
).request_uri
[0]) {
189 const char *q
= strchr(SG(request_info
).request_uri
, '?');
192 url
->path
= estrndup(SG(request_info
).request_uri
, q
- SG(request_info
).request_uri
);
194 url
->path
= estrdup(SG(request_info
).request_uri
);
197 url
->path
= estrndup("/", 1);
199 } else if (url
->path
[0] != '/') {
200 if ((flags
& PHP_HTTP_URL_FROM_ENV
) && SG(request_info
).request_uri
&& SG(request_info
).request_uri
[0]) {
201 size_t ulen
= strlen(SG(request_info
).request_uri
);
202 size_t plen
= strlen(url
->path
);
205 if (SG(request_info
).request_uri
[ulen
-1] != '/') {
206 for (--ulen
; ulen
&& SG(request_info
).request_uri
[ulen
- 1] != '/'; --ulen
);
209 path
= emalloc(ulen
+ plen
+ 1);
210 memcpy(path
, SG(request_info
).request_uri
, ulen
);
211 memcpy(path
+ ulen
, url
->path
, plen
);
212 path
[ulen
+ plen
] = '\0';
213 STR_SET(url
->path
, path
);
215 size_t plen
= strlen(url
->path
);
216 char *path
= emalloc(plen
+ 1 + 1);
219 memcpy(&path
[1], url
->path
, plen
+ 1);
220 STR_SET(url
->path
, path
);
223 /* replace directory references if path is not a single slash */
224 if (url
->path
[0] && (url
->path
[0] != '/' || url
->path
[1])) {
225 char *ptr
, *end
= url
->path
+ strlen(url
->path
) + 1;
227 for (ptr
= strstr(url
->path
, "/."); ptr
; ptr
= strstr(ptr
, "/.")) {
234 memmove(&ptr
[1], &ptr
[3], end
- &ptr
[3]);
240 while (ptr
!= url
->path
) {
245 memmove(&ptr
[1], pos
, end
- pos
);
247 } else if (!ptr
[3]) {
262 if ( ((url
->port
== 80) && !strcmp(url
->scheme
, "http"))
263 || ((url
->port
==443) && !strcmp(url
->scheme
, "https"))
264 #ifdef HAVE_GETSERVBYNAME
265 || ((se
= getservbyname(url
->scheme
, "tcp")) && se
->s_port
&&
266 (url
->port
== ntohs(se
->s_port
)))
276 *url_str
= emalloc(PHP_HTTP_URL_MAXLEN
+ 1);
279 strlcat(*url_str
, url
->scheme
, PHP_HTTP_URL_MAXLEN
);
280 strlcat(*url_str
, "://", PHP_HTTP_URL_MAXLEN
);
282 if (url
->user
&& *url
->user
) {
283 strlcat(*url_str
, url
->user
, PHP_HTTP_URL_MAXLEN
);
284 if (url
->pass
&& *url
->pass
) {
285 strlcat(*url_str
, ":", PHP_HTTP_URL_MAXLEN
);
286 strlcat(*url_str
, url
->pass
, PHP_HTTP_URL_MAXLEN
);
288 strlcat(*url_str
, "@", PHP_HTTP_URL_MAXLEN
);
291 strlcat(*url_str
, url
->host
, PHP_HTTP_URL_MAXLEN
);
296 snprintf(port_str
, sizeof(port_str
), "%d", (int) url
->port
);
297 strlcat(*url_str
, ":", PHP_HTTP_URL_MAXLEN
);
298 strlcat(*url_str
, port_str
, PHP_HTTP_URL_MAXLEN
);
301 strlcat(*url_str
, url
->path
, PHP_HTTP_URL_MAXLEN
);
303 if (url
->query
&& *url
->query
) {
304 strlcat(*url_str
, "?", PHP_HTTP_URL_MAXLEN
);
305 strlcat(*url_str
, url
->query
, PHP_HTTP_URL_MAXLEN
);
308 if (url
->fragment
&& *url
->fragment
) {
309 strlcat(*url_str
, "#", PHP_HTTP_URL_MAXLEN
);
310 strlcat(*url_str
, url
->fragment
, PHP_HTTP_URL_MAXLEN
);
313 if (PHP_HTTP_URL_MAXLEN
== (len
= strlen(*url_str
))) {
314 php_http_error(HE_NOTICE
, PHP_HTTP_E_URL
, "Length of URL exceeds PHP_HTTP_URL_MAXLEN");
328 PHP_HTTP_API STATUS
php_http_url_encode_hash(HashTable
*hash
, zend_bool override_argsep
, char *pre_encoded_data
, size_t pre_encoded_len
, char **encoded_data
, size_t *encoded_len TSRMLS_DC
)
332 php_http_buffer_t
*qstr
= php_http_buffer_new();
334 if (override_argsep
|| !(arg_sep_len
= strlen(arg_sep
= INI_STR("arg_separator.output")))) {
335 arg_sep
= PHP_HTTP_URL_ARGSEP
;
336 arg_sep_len
= lenof(PHP_HTTP_URL_ARGSEP
);
339 if (pre_encoded_len
&& pre_encoded_data
) {
340 php_http_buffer_append(qstr
, pre_encoded_data
, pre_encoded_len
);
343 if (SUCCESS
!= php_http_url_encode_hash_recursive(hash
, qstr
, arg_sep
, arg_sep_len
, NULL
, 0)) {
344 php_http_buffer_free(&qstr
);
348 php_http_buffer_data(qstr
, encoded_data
, encoded_len
);
349 php_http_buffer_free(&qstr
);
354 PHP_HTTP_API STATUS
php_http_url_encode_hash_recursive(HashTable
*ht
, php_http_buffer_t
*str
, const char *arg_sep
, size_t arg_sep_len
, const char *prefix
, size_t prefix_len TSRMLS_DC
)
356 php_http_array_hashkey_t key
= php_http_array_hashkey_init(0);
361 php_http_error(HE_WARNING
, PHP_HTTP_E_INVALID_PARAM
, "Invalid parameters");
364 if (ht
->nApplyCount
> 0) {
368 FOREACH_HASH_KEYVAL(pos
, ht
, key
, data
) {
371 php_http_buffer_t new_prefix
;
373 if (!data
|| !*data
) {
374 php_http_buffer_dtor(str
);
378 if (key
.type
== HASH_KEY_IS_STRING
) {
380 /* only public properties */
383 if (key
.len
&& key
.str
[key
.len
- 1] == '\0') {
386 encoded_key
= php_url_encode(key
.str
, key
.len
, &encoded_len
);
388 encoded_len
= spprintf(&encoded_key
, 0, "%ld", key
.num
);
392 php_http_buffer_init(&new_prefix
);
393 if (prefix
&& prefix_len
) {
394 php_http_buffer_append(&new_prefix
, prefix
, prefix_len
);
395 php_http_buffer_appends(&new_prefix
, "%5B");
398 php_http_buffer_append(&new_prefix
, encoded_key
, encoded_len
);
401 if (prefix
&& prefix_len
) {
402 php_http_buffer_appends(&new_prefix
, "%5D");
404 php_http_buffer_fix(&new_prefix
);
407 if (Z_TYPE_PP(data
) == IS_ARRAY
|| Z_TYPE_PP(data
) == IS_OBJECT
) {
410 status
= php_http_url_encode_hash_recursive(HASH_OF(*data
), str
, arg_sep
, arg_sep_len
, PHP_HTTP_BUFFER_VAL(&new_prefix
), PHP_HTTP_BUFFER_LEN(&new_prefix
) TSRMLS_CC
);
412 if (SUCCESS
!= status
) {
413 php_http_buffer_dtor(&new_prefix
);
414 php_http_buffer_dtor(str
);
418 zval
*val
= php_http_ztyp(IS_STRING
, *data
);
420 if (PHP_HTTP_BUFFER_LEN(str
)) {
421 php_http_buffer_append(str
, arg_sep
, arg_sep_len
);
423 php_http_buffer_append(str
, PHP_HTTP_BUFFER_VAL(&new_prefix
), PHP_HTTP_BUFFER_LEN(&new_prefix
));
424 php_http_buffer_appends(str
, "=");
426 if (Z_STRLEN_P(val
) && Z_STRVAL_P(val
)) {
430 encoded_val
= php_url_encode(Z_STRVAL_P(val
), Z_STRLEN_P(val
), &encoded_len
);
431 php_http_buffer_append(str
, encoded_val
, encoded_len
);
437 php_http_buffer_dtor(&new_prefix
);
442 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpUrl, method, 0, req_args)
443 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpUrl, method, 0)
444 #define PHP_HTTP_URL_ME(method, visibility) PHP_ME(HttpUrl, method, PHP_HTTP_ARGS(HttpUrl, method), visibility)
446 PHP_HTTP_BEGIN_ARGS(__construct
, 0)
447 PHP_HTTP_ARG_VAL(old_url
, 0)
448 PHP_HTTP_ARG_VAL(new_url
, 0)
449 PHP_HTTP_ARG_VAL(flags
, 0)
451 PHP_HTTP_EMPTY_ARGS(toString
);
453 zend_class_entry
*php_http_url_class_entry
;
454 zend_function_entry php_http_url_method_entry
[] = {
455 PHP_HTTP_URL_ME(__construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
456 PHP_HTTP_URL_ME(toString
, ZEND_ACC_PUBLIC
)
457 ZEND_MALIAS(HttpUrl
, __toString
, toString
, PHP_HTTP_ARGS(HttpUrl
, toString
), ZEND_ACC_PUBLIC
)
461 PHP_METHOD(HttpUrl
, __construct
)
463 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
464 zval
*new_url
= NULL
, *old_url
= NULL
;
465 long flags
= PHP_HTTP_URL_FROM_ENV
;
467 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|z!z!l", &old_url
, &new_url
, &flags
)) {
468 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
469 php_url
*res_purl
, *new_purl
= NULL
, *old_purl
= NULL
;
472 switch (Z_TYPE_P(new_url
)) {
475 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
478 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
480 new_purl
= php_url_parse(Z_STRVAL_P(new_url
));
490 switch (Z_TYPE_P(old_url
)) {
493 old_purl
= php_http_url_from_struct(NULL
, HASH_OF(old_url
) TSRMLS_CC
);
496 zval
*cpy
= php_http_ztyp(IS_STRING
, old_url
);
498 old_purl
= php_url_parse(Z_STRVAL_P(old_url
));
505 php_url_free(new_purl
);
511 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
512 php_http_url_to_struct(res_purl
, getThis() TSRMLS_CC
);
514 php_url_free(res_purl
);
516 php_url_free(old_purl
);
519 php_url_free(new_purl
);
521 } end_error_handling();
523 } end_error_handling();
526 PHP_METHOD(HttpUrl
, toString
)
528 if (SUCCESS
== zend_parse_parameters_none()) {
531 if ((purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
535 php_http_url(0, purl
, NULL
, NULL
, &str
, &len TSRMLS_CC
);
537 RETURN_STRINGL(str
, len
, 0);
540 RETURN_EMPTY_STRING();
543 PHP_MINIT_FUNCTION(http_url
)
545 PHP_HTTP_REGISTER_CLASS(http
, Url
, http_url
, php_http_object_class_entry
, 0);
547 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC
);
548 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC
);
549 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC
);
550 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC
);
551 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC
);
552 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC
);
553 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC
);
554 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC
);
556 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC
);
557 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC
);
558 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC
);
559 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC
);
560 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC
);
561 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC
);
562 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC
);
563 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC
);
564 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC
);
565 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC
);
566 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC
);
567 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC
);
578 * vim600: noet sw=4 ts=4 fdm=marker
579 * vim<600: noet sw=4 ts=4