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 static inline char *localhostname(void)
17 char hostname
[1024] = {0};
20 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
21 return estrdup(hostname
);
23 #elif defined(HAVE_GETHOSTNAME)
24 if (SUCCESS
== gethostname(hostname
, lenof(hostname
))) {
25 # if defined(HAVE_GETDOMAINNAME)
26 size_t hlen
= strlen(hostname
);
27 if (hlen
<= lenof(hostname
) - lenof("(none)")) {
28 hostname
[hlen
++] = '.';
29 if (SUCCESS
== getdomainname(&hostname
[hlen
], lenof(hostname
) - hlen
)) {
30 if (!strcmp(&hostname
[hlen
], "(none)")) {
31 hostname
[hlen
- 1] = '\0';
33 return estrdup(hostname
);
37 if (strcmp(hostname
, "(none)")) {
38 return estrdup(hostname
);
42 return estrndup("localhost", lenof("localhost"));
45 static php_url
*php_http_url_from_env(php_url
*url TSRMLS_DC
)
47 zval
*https
, *zhost
, *zport
;
51 url
= ecalloc(1, sizeof(*url
));
55 zport
= php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC
);
56 if (zport
&& IS_LONG
== is_numeric_string(Z_STRVAL_P(zport
), Z_STRLEN_P(zport
), &port
, NULL
, 0)) {
61 https
= php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC
);
62 if (https
&& !strcasecmp(Z_STRVAL_P(https
), "ON")) {
63 url
->scheme
= estrndup("https", lenof("https"));
65 url
->scheme
= estrndup("http", lenof("http"));
69 if ((((zhost
= php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC
)) ||
70 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC
)) ||
71 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC
)))) && Z_STRLEN_P(zhost
)) {
72 size_t stop_at
= strspn(Z_STRVAL_P(zhost
), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
74 url
->host
= estrndup(Z_STRVAL_P(zhost
), stop_at
);
76 url
->host
= localhostname();
80 if (SG(request_info
).request_uri
&& SG(request_info
).request_uri
[0]) {
81 const char *q
= strchr(SG(request_info
).request_uri
, '?');
84 url
->path
= estrndup(SG(request_info
).request_uri
, q
- SG(request_info
).request_uri
);
86 url
->path
= estrdup(SG(request_info
).request_uri
);
91 if (SG(request_info
).query_string
&& SG(request_info
).query_string
[0]) {
92 url
->query
= estrdup(SG(request_info
).query_string
);
98 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
)
100 php_url
*url
, *tmp_url
= NULL
;
102 /* set from env if requested */
103 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
104 php_url
*env_url
= php_http_url_from_env(NULL TSRMLS_CC
);
106 php_http_url(flags
^ PHP_HTTP_URL_FROM_ENV
, env_url
, old_url
, &tmp_url
, NULL
, NULL TSRMLS_CC
);
108 php_url_free(env_url
);
112 url
= ecalloc(1, sizeof(*url
));
114 #define __URLSET(u,n) \
116 #define __URLCPY(n) \
117 url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL)
119 if (!(flags
& PHP_HTTP_URL_STRIP_PORT
)) {
120 url
->port
= __URLSET(new_url
, port
) ? new_url
->port
: ((old_url
) ? old_url
->port
: 0);
122 if (!(flags
& PHP_HTTP_URL_STRIP_USER
)) {
125 if (!(flags
& PHP_HTTP_URL_STRIP_PASS
)) {
132 if (!(flags
& PHP_HTTP_URL_STRIP_PATH
)) {
133 if ((flags
& PHP_HTTP_URL_JOIN_PATH
) && __URLSET(old_url
, path
) && __URLSET(new_url
, path
) && *new_url
->path
!= '/') {
134 size_t old_path_len
= strlen(old_url
->path
), new_path_len
= strlen(new_url
->path
);
136 url
->path
= ecalloc(1, old_path_len
+ new_path_len
+ 1 + 1);
138 strcat(url
->path
, old_url
->path
);
139 if (url
->path
[old_path_len
- 1] != '/') {
140 php_dirname(url
->path
, old_path_len
);
141 strcat(url
->path
, "/");
143 strcat(url
->path
, new_url
->path
);
148 if (!(flags
& PHP_HTTP_URL_STRIP_QUERY
)) {
149 if ((flags
& PHP_HTTP_URL_JOIN_QUERY
) && __URLSET(new_url
, query
) && __URLSET(old_url
, query
)) {
156 ZVAL_STRING(&qstr
, old_url
->query
, 0);
157 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
158 ZVAL_STRING(&qstr
, new_url
->query
, 0);
159 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
162 php_http_querystring_update(&qarr
, NULL
, &qstr TSRMLS_CC
);
163 url
->query
= Z_STRVAL(qstr
);
169 if (!(flags
& PHP_HTTP_URL_STRIP_FRAGMENT
)) {
173 /* done with copy & combine & strip */
175 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
176 /* free old_url we tainted above */
177 php_url_free(tmp_url
);
180 /* set some sane defaults */
183 url
->scheme
= estrndup("http", lenof("http"));
187 url
->host
= estrndup("localhost", lenof("localhost"));
191 url
->path
= estrndup("/", 1);
192 } else if (url
->path
[0] != '/') {
193 size_t plen
= strlen(url
->path
);
194 char *path
= emalloc(plen
+ 1 + 1);
197 memcpy(&path
[1], url
->path
, plen
+ 1);
198 STR_SET(url
->path
, path
);
200 /* replace directory references if path is not a single slash */
201 if ((flags
& PHP_HTTP_URL_SANITIZE_PATH
)
202 && url
->path
[0] && (url
->path
[0] != '/' || url
->path
[1])) {
203 char *ptr
, *end
= url
->path
+ strlen(url
->path
) + 1;
205 for (ptr
= strchr(url
->path
, '/'); ptr
; ptr
= strchr(ptr
, '/')) {
208 memmove(&ptr
[1], &ptr
[2], end
- &ptr
[2]);
218 memmove(&ptr
[1], &ptr
[3], end
- &ptr
[3]);
224 while (ptr
!= url
->path
) {
229 memmove(&ptr
[1], pos
, end
- pos
);
231 } else if (!ptr
[3]) {
250 /* unset default ports */
252 if ( ((url
->port
== 80) && !strcmp(url
->scheme
, "http"))
253 || ((url
->port
==443) && !strcmp(url
->scheme
, "https"))
260 php_http_url_to_string(url
, url_str
, url_len TSRMLS_CC
);
270 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
)
272 const char *arg_sep_str
;
274 php_http_buffer_t
*qstr
= php_http_buffer_new();
276 php_http_url_argsep(&arg_sep_str
, &arg_sep_len TSRMLS_CC
);
278 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
)) {
279 php_http_buffer_free(&qstr
);
283 php_http_buffer_data(qstr
, encoded_str
, encoded_len
);
284 php_http_buffer_free(&qstr
);
289 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
)
291 if (pre_encoded_len
&& pre_encoded_str
) {
292 php_http_buffer_append(qstr
, pre_encoded_str
, pre_encoded_len
);
295 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
)) {
302 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct
, 0, 0, 0)
303 ZEND_ARG_INFO(0, old_url
)
304 ZEND_ARG_INFO(0, new_url
)
305 ZEND_ARG_INFO(0, flags
)
307 PHP_METHOD(HttpUrl
, __construct
)
309 zval
*new_url
= NULL
, *old_url
= NULL
;
310 long flags
= PHP_HTTP_URL_FROM_ENV
;
311 zend_error_handling zeh
;
313 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|z!z!l", &old_url
, &new_url
, &flags
), invalid_arg
, return);
315 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
317 php_url
*res_purl
, *new_purl
= NULL
, *old_purl
= NULL
;
320 switch (Z_TYPE_P(new_url
)) {
323 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
326 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
328 new_purl
= php_url_parse(Z_STRVAL_P(cpy
));
334 zend_restore_error_handling(&zeh TSRMLS_CC
);
339 switch (Z_TYPE_P(old_url
)) {
342 old_purl
= php_http_url_from_struct(NULL
, HASH_OF(old_url
) TSRMLS_CC
);
345 zval
*cpy
= php_http_ztyp(IS_STRING
, old_url
);
347 old_purl
= php_url_parse(Z_STRVAL_P(cpy
));
354 php_url_free(new_purl
);
356 zend_restore_error_handling(&zeh TSRMLS_CC
);
361 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
362 php_http_url_to_struct(res_purl
, getThis() TSRMLS_CC
);
364 php_url_free(res_purl
);
366 php_url_free(old_purl
);
369 php_url_free(new_purl
);
372 zend_restore_error_handling(&zeh TSRMLS_CC
);
375 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod
, 0, 0, 1)
376 ZEND_ARG_INFO(0, more_url_parts
)
377 ZEND_ARG_INFO(0, flags
)
379 PHP_METHOD(HttpUrl
, mod
)
381 zval
*new_url
= NULL
;
382 long flags
= PHP_HTTP_URL_JOIN_PATH
| PHP_HTTP_URL_JOIN_QUERY
;
383 zend_error_handling zeh
;
385 php_http_expect(SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z!|l", &new_url
, &flags
), invalid_arg
, return);
387 zend_replace_error_handling(EH_THROW
, php_http_exception_bad_url_class_entry
, &zeh TSRMLS_CC
);
389 php_url
*new_purl
= NULL
, *old_purl
= NULL
;
392 switch (Z_TYPE_P(new_url
)) {
395 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
398 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
400 new_purl
= php_url_parse(Z_STRVAL_P(new_url
));
406 zend_restore_error_handling(&zeh TSRMLS_CC
);
411 if ((old_purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
414 ZVAL_OBJVAL(return_value
, zend_objects_clone_obj(getThis() TSRMLS_CC
), 0);
416 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
417 php_http_url_to_struct(res_purl
, return_value TSRMLS_CC
);
419 php_url_free(res_purl
);
420 php_url_free(old_purl
);
423 php_url_free(new_purl
);
426 zend_restore_error_handling(&zeh TSRMLS_CC
);
429 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString
, 0, 0, 0)
431 PHP_METHOD(HttpUrl
, toString
)
433 if (SUCCESS
== zend_parse_parameters_none()) {
436 if ((purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
440 php_http_url(0, purl
, NULL
, NULL
, &str
, &len TSRMLS_CC
);
442 RETURN_STRINGL(str
, len
, 0);
445 RETURN_EMPTY_STRING();
448 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray
, 0, 0, 0)
450 PHP_METHOD(HttpUrl
, toArray
)
454 if (SUCCESS
!= zend_parse_parameters_none()) {
458 /* strip any non-URL properties */
459 purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
);
460 php_http_url_to_struct(purl
, return_value TSRMLS_CC
);
464 static zend_function_entry php_http_url_methods
[] = {
465 PHP_ME(HttpUrl
, __construct
, ai_HttpUrl___construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
466 PHP_ME(HttpUrl
, mod
, ai_HttpUrl_mod
, ZEND_ACC_PUBLIC
)
467 PHP_ME(HttpUrl
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
468 ZEND_MALIAS(HttpUrl
, __toString
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
469 PHP_ME(HttpUrl
, toArray
, ai_HttpUrl_toArray
, ZEND_ACC_PUBLIC
)
473 zend_class_entry
*php_http_url_class_entry
;
475 PHP_MINIT_FUNCTION(http_url
)
477 zend_class_entry ce
= {0};
479 INIT_NS_CLASS_ENTRY(ce
, "http", "Url", php_http_url_methods
);
480 php_http_url_class_entry
= zend_register_internal_class(&ce TSRMLS_CC
);
482 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC
);
483 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC
);
484 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC
);
485 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC
);
486 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC
);
487 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC
);
488 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC
);
489 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC
);
491 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC
);
492 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC
);
493 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC
);
494 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC
);
495 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC
);
496 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC
);
497 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC
);
498 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC
);
499 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC
);
500 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC
);
501 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC
);
502 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC
);
503 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC
);
514 * vim600: noet sw=4 ts=4 fdm=marker
515 * vim<600: noet sw=4 ts=4