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-2013, 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 inline unsigned port(const char *scheme
)
49 #if defined(ZTS) && defined(HAVE_GETSERVBYPORT_R)
53 struct servent
*se_res
= NULL
, se_buf
= {0};
56 buf
= erealloc(buf
, len
);
57 rc
= getservbyname_r(scheme
, "tcp", &se_buf
, buf
, len
, &se_res
);
59 } while (rc
== ERANGE
&& len
<= 0xfff);
62 port
= ntohs(se_res
->s_port
);
66 #elif !defined(ZTS) && defined(HAVE_GETSERVBYPORT)
69 if ((se
= getservbyname(scheme
, "tcp")) && se
->s_port
) {
70 port
= ntohs(se
->s_port
);
76 static inline char *scheme(unsigned port
)
79 #if defined(ZTS) && defined(HAVE_GETSERVBYPORT_R)
83 struct servent
*se_res
= NULL
, se_buf
= {0};
84 #elif !defined(ZTS) && defined(HAVE_GETSERVBYPORT)
90 scheme
= estrndup("https", lenof("https"));
93 #if defined(ZTS) && !defined(HAVE_GETSERVBYPORT_R)
95 #elif !defined(ZTS) && !defined(HAVE_GETSERVBYPORT)
100 scheme
= estrndup("http", lenof("http"));
103 #if defined(ZTS) && defined(HAVE_GETSERVBYPORT_R)
106 buf
= erealloc(buf
, len
);
107 rc
= getservbyport_r(htons(port
), "tcp", &se_buf
, buf
, len
, &se_res
);
109 } while (rc
== ERANGE
&& len
<= 0xfff);
112 scheme
= estrdup(se_res
->s_name
);
114 scheme
= estrndup("http", lenof("http"));
120 #elif !defined(ZTS) && defined(HAVE_GETSERVBYPORT)
122 if ((se
= getservbyport(htons(port
), "tcp")) && se
->s_name
) {
123 scheme
= estrdup(se
->s_name
);
125 scheme
= estrndup("http", lenof("http"));
133 static php_url
*php_http_url_from_env(php_url
*url TSRMLS_DC
)
135 zval
*https
, *zhost
, *zport
;
139 url
= ecalloc(1, sizeof(*url
));
143 zport
= php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC
);
144 if (zport
&& IS_LONG
== is_numeric_string(Z_STRVAL_P(zport
), Z_STRLEN_P(zport
), &port
, NULL
, 0)) {
149 https
= php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC
);
150 if (https
&& !strcasecmp(Z_STRVAL_P(https
), "ON")) {
151 url
->scheme
= estrndup("https", lenof("https"));
153 url
->scheme
= scheme(url
->port
);
157 if ((((zhost
= php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC
)) ||
158 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC
)) ||
159 (zhost
= php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC
)))) && Z_STRLEN_P(zhost
)) {
160 size_t stop_at
= strspn(Z_STRVAL_P(zhost
), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
162 url
->host
= estrndup(Z_STRVAL_P(zhost
), stop_at
);
164 url
->host
= localhostname();
168 if (SG(request_info
).request_uri
&& SG(request_info
).request_uri
[0]) {
169 const char *q
= strchr(SG(request_info
).request_uri
, '?');
172 url
->path
= estrndup(SG(request_info
).request_uri
, q
- SG(request_info
).request_uri
);
174 url
->path
= estrdup(SG(request_info
).request_uri
);
179 if (SG(request_info
).query_string
&& SG(request_info
).query_string
[0]) {
180 url
->query
= estrdup(SG(request_info
).query_string
);
186 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
)
188 php_url
*url
, *tmp_url
= NULL
;
190 /* set from env if requested */
191 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
192 php_url
*env_url
= php_http_url_from_env(NULL TSRMLS_CC
);
194 php_http_url(flags
^ PHP_HTTP_URL_FROM_ENV
, env_url
, old_url
, &tmp_url
, NULL
, NULL TSRMLS_CC
);
196 php_url_free(env_url
);
200 url
= ecalloc(1, sizeof(*url
));
202 #define __URLSET(u,n) \
204 #define __URLCPY(n) \
205 url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL)
207 if (!(flags
& PHP_HTTP_URL_STRIP_PORT
)) {
208 url
->port
= __URLSET(new_url
, port
) ? new_url
->port
: ((old_url
) ? old_url
->port
: 0);
210 if (!(flags
& PHP_HTTP_URL_STRIP_USER
)) {
213 if (!(flags
& PHP_HTTP_URL_STRIP_PASS
)) {
220 if (!(flags
& PHP_HTTP_URL_STRIP_PATH
)) {
221 if ((flags
& PHP_HTTP_URL_JOIN_PATH
) && __URLSET(old_url
, path
) && __URLSET(new_url
, path
) && *new_url
->path
!= '/') {
222 size_t old_path_len
= strlen(old_url
->path
), new_path_len
= strlen(new_url
->path
);
224 url
->path
= ecalloc(1, old_path_len
+ new_path_len
+ 1 + 1);
226 strcat(url
->path
, old_url
->path
);
227 if (url
->path
[old_path_len
- 1] != '/') {
228 php_dirname(url
->path
, old_path_len
);
229 strcat(url
->path
, "/");
231 strcat(url
->path
, new_url
->path
);
236 if (!(flags
& PHP_HTTP_URL_STRIP_QUERY
)) {
237 if ((flags
& PHP_HTTP_URL_JOIN_QUERY
) && __URLSET(new_url
, query
) && __URLSET(old_url
, query
)) {
244 ZVAL_STRING(&qstr
, old_url
->query
, 0);
245 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
246 ZVAL_STRING(&qstr
, new_url
->query
, 0);
247 php_http_querystring_update(&qarr
, &qstr
, NULL TSRMLS_CC
);
250 php_http_querystring_update(&qarr
, NULL
, &qstr TSRMLS_CC
);
251 url
->query
= Z_STRVAL(qstr
);
257 if (!(flags
& PHP_HTTP_URL_STRIP_FRAGMENT
)) {
261 /* done with copy & combine & strip */
263 if (flags
& PHP_HTTP_URL_FROM_ENV
) {
264 /* free old_url we tainted above */
265 php_url_free(tmp_url
);
268 /* set some sane defaults */
271 url
->scheme
= estrndup("http", lenof("http"));
275 url
->host
= estrndup("localhost", lenof("localhost"));
279 url
->path
= estrndup("/", 1);
280 } else if (url
->path
[0] != '/') {
281 size_t plen
= strlen(url
->path
);
282 char *path
= emalloc(plen
+ 1 + 1);
285 memcpy(&path
[1], url
->path
, plen
+ 1);
286 STR_SET(url
->path
, path
);
288 /* replace directory references if path is not a single slash */
289 if ((flags
& PHP_HTTP_URL_SANITIZE_PATH
)
290 && url
->path
[0] && (url
->path
[0] != '/' || url
->path
[1])) {
291 char *ptr
, *end
= url
->path
+ strlen(url
->path
) + 1;
293 for (ptr
= strchr(url
->path
, '/'); ptr
; ptr
= strchr(ptr
, '/')) {
296 memmove(&ptr
[1], &ptr
[2], end
- &ptr
[2]);
306 memmove(&ptr
[1], &ptr
[3], end
- &ptr
[3]);
312 while (ptr
!= url
->path
) {
317 memmove(&ptr
[1], pos
, end
- pos
);
319 } else if (!ptr
[3]) {
338 /* unset default ports */
340 if ( ((url
->port
== 80) && !strcmp(url
->scheme
, "http"))
341 || ((url
->port
==443) && !strcmp(url
->scheme
, "https"))
342 || ( url
->port
== port(url
->scheme
))
349 php_http_url_to_string(url
, url_str
, url_len TSRMLS_CC
);
359 PHP_HTTP_API 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
)
361 const char *arg_sep_str
;
363 php_http_buffer_t
*qstr
= php_http_buffer_new();
365 php_http_url_argsep(&arg_sep_str
, &arg_sep_len TSRMLS_CC
);
367 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
)) {
368 php_http_buffer_free(&qstr
);
372 php_http_buffer_data(qstr
, encoded_str
, encoded_len
);
373 php_http_buffer_free(&qstr
);
378 PHP_HTTP_API 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
)
380 if (pre_encoded_len
&& pre_encoded_str
) {
381 php_http_buffer_append(qstr
, pre_encoded_str
, pre_encoded_len
);
384 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
)) {
391 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct
, 0, 0, 0)
392 ZEND_ARG_INFO(0, old_url
)
393 ZEND_ARG_INFO(0, new_url
)
394 ZEND_ARG_INFO(0, flags
)
396 PHP_METHOD(HttpUrl
, __construct
)
398 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
399 zval
*new_url
= NULL
, *old_url
= NULL
;
400 long flags
= PHP_HTTP_URL_FROM_ENV
;
402 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|z!z!l", &old_url
, &new_url
, &flags
)) {
403 with_error_handling(EH_THROW
, php_http_exception_class_entry
) {
404 php_url
*res_purl
, *new_purl
= NULL
, *old_purl
= NULL
;
407 switch (Z_TYPE_P(new_url
)) {
410 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
413 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
415 new_purl
= php_url_parse(Z_STRVAL_P(cpy
));
425 switch (Z_TYPE_P(old_url
)) {
428 old_purl
= php_http_url_from_struct(NULL
, HASH_OF(old_url
) TSRMLS_CC
);
431 zval
*cpy
= php_http_ztyp(IS_STRING
, old_url
);
433 old_purl
= php_url_parse(Z_STRVAL_P(cpy
));
440 php_url_free(new_purl
);
446 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
447 php_http_url_to_struct(res_purl
, getThis() TSRMLS_CC
);
449 php_url_free(res_purl
);
451 php_url_free(old_purl
);
454 php_url_free(new_purl
);
456 } end_error_handling();
458 } end_error_handling();
461 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod
, 0, 0, 1)
462 ZEND_ARG_INFO(0, more_url_parts
)
463 ZEND_ARG_INFO(0, flags
)
465 PHP_METHOD(HttpUrl
, mod
)
467 zval
*new_url
= NULL
;
468 long flags
= PHP_HTTP_URL_JOIN_PATH
| PHP_HTTP_URL_JOIN_QUERY
;
470 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z!|l", &new_url
, &flags
)) {
471 php_url
*new_purl
= NULL
, *old_purl
= NULL
;
474 switch (Z_TYPE_P(new_url
)) {
477 new_purl
= php_http_url_from_struct(NULL
, HASH_OF(new_url
) TSRMLS_CC
);
480 zval
*cpy
= php_http_ztyp(IS_STRING
, new_url
);
482 new_purl
= php_url_parse(Z_STRVAL_P(new_url
));
492 if ((old_purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
495 ZVAL_OBJVAL(return_value
, zend_objects_clone_obj(getThis() TSRMLS_CC
), 0);
497 php_http_url(flags
, old_purl
, new_purl
, &res_purl
, NULL
, NULL TSRMLS_CC
);
498 php_http_url_to_struct(res_purl
, return_value TSRMLS_CC
);
500 php_url_free(res_purl
);
501 php_url_free(old_purl
);
504 php_url_free(new_purl
);
509 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString
, 0, 0, 0)
511 PHP_METHOD(HttpUrl
, toString
)
513 if (SUCCESS
== zend_parse_parameters_none()) {
516 if ((purl
= php_http_url_from_struct(NULL
, HASH_OF(getThis()) TSRMLS_CC
))) {
520 php_http_url(0, purl
, NULL
, NULL
, &str
, &len TSRMLS_CC
);
522 RETURN_STRINGL(str
, len
, 0);
525 RETURN_EMPTY_STRING();
528 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray
, 0, 0, 0)
530 PHP_METHOD(HttpUrl
, toArray
)
532 if (SUCCESS
!= zend_parse_parameters_none()) {
535 array_init(return_value
);
536 array_copy(HASH_OF(getThis()), HASH_OF(return_value
));
539 static zend_function_entry php_http_url_methods
[] = {
540 PHP_ME(HttpUrl
, __construct
, ai_HttpUrl___construct
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
)
541 PHP_ME(HttpUrl
, mod
, ai_HttpUrl_mod
, ZEND_ACC_PUBLIC
)
542 PHP_ME(HttpUrl
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
543 ZEND_MALIAS(HttpUrl
, __toString
, toString
, ai_HttpUrl_toString
, ZEND_ACC_PUBLIC
)
544 PHP_ME(HttpUrl
, toArray
, ai_HttpUrl_toArray
, ZEND_ACC_PUBLIC
)
548 zend_class_entry
*php_http_url_class_entry
;
550 PHP_MINIT_FUNCTION(http_url
)
552 zend_class_entry ce
= {0};
554 INIT_NS_CLASS_ENTRY(ce
, "http", "Url", php_http_url_methods
);
555 php_http_url_class_entry
= zend_register_internal_class_ex(&ce
, php_http_object_class_entry
, NULL TSRMLS_CC
);
557 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC
);
558 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC
);
559 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC
);
560 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC
);
561 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC
);
562 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC
);
563 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC
);
564 zend_declare_property_null(php_http_url_class_entry
, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC
);
566 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC
);
567 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC
);
568 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC
);
569 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC
);
570 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC
);
571 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC
);
572 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC
);
573 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC
);
574 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC
);
575 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC
);
576 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC
);
577 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC
);
578 zend_declare_class_constant_long(php_http_url_class_entry
, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC
);
589 * vim600: noet sw=4 ts=4 fdm=marker
590 * vim<600: noet sw=4 ts=4