define off_t
[m6w6/ext-http] / php_http_url.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
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 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 static inline char *localhostname(void)
16 {
17 char hostname[1024] = {0};
18
19 #ifdef PHP_WIN32
20 if (SUCCESS == gethostname(hostname, lenof(hostname))) {
21 return estrdup(hostname);
22 }
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';
32 }
33 return estrdup(hostname);
34 }
35 }
36 # endif
37 if (strcmp(hostname, "(none)")) {
38 return estrdup(hostname);
39 }
40 }
41 #endif
42 return estrndup("localhost", lenof("localhost"));
43 }
44
45 static php_url *php_http_url_from_env(php_url *url TSRMLS_DC)
46 {
47 zval *https, *zhost, *zport;
48 long port;
49
50 if (!url) {
51 url = ecalloc(1, sizeof(*url));
52 }
53
54 /* port */
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)) {
57 url->port = port;
58 }
59
60 /* scheme */
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"));
64 } else {
65 url->scheme = estrndup("http", lenof("http"));
66 }
67
68 /* host */
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-.");
73
74 url->host = estrndup(Z_STRVAL_P(zhost), stop_at);
75 } else {
76 url->host = localhostname();
77 }
78
79 /* path */
80 if (SG(request_info).request_uri && SG(request_info).request_uri[0]) {
81 const char *q = strchr(SG(request_info).request_uri, '?');
82
83 if (q) {
84 url->path = estrndup(SG(request_info).request_uri, q - SG(request_info).request_uri);
85 } else {
86 url->path = estrdup(SG(request_info).request_uri);
87 }
88 }
89
90 /* query */
91 if (SG(request_info).query_string && SG(request_info).query_string[0]) {
92 url->query = estrdup(SG(request_info).query_string);
93 }
94
95 return url;
96 }
97
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)
99 {
100 php_url *url, *tmp_url = NULL;
101
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);
105
106 php_http_url(flags ^ PHP_HTTP_URL_FROM_ENV, env_url, old_url, &tmp_url, NULL, NULL TSRMLS_CC);
107
108 php_url_free(env_url);
109 old_url = tmp_url;
110 }
111
112 url = ecalloc(1, sizeof(*url));
113
114 #define __URLSET(u,n) \
115 ((u)&&(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)
118
119 if (!(flags & PHP_HTTP_URL_STRIP_PORT)) {
120 url->port = __URLSET(new_url, port) ? new_url->port : ((old_url) ? old_url->port : 0);
121 }
122 if (!(flags & PHP_HTTP_URL_STRIP_USER)) {
123 __URLCPY(user);
124 }
125 if (!(flags & PHP_HTTP_URL_STRIP_PASS)) {
126 __URLCPY(pass);
127 }
128
129 __URLCPY(scheme);
130 __URLCPY(host);
131
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);
135
136 url->path = ecalloc(1, old_path_len + new_path_len + 1 + 1);
137
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, "/");
142 }
143 strcat(url->path, new_url->path);
144 } else {
145 __URLCPY(path);
146 }
147 }
148 if (!(flags & PHP_HTTP_URL_STRIP_QUERY)) {
149 if ((flags & PHP_HTTP_URL_JOIN_QUERY) && __URLSET(new_url, query) && __URLSET(old_url, query)) {
150 zval qarr, qstr;
151
152 INIT_PZVAL(&qstr);
153 INIT_PZVAL(&qarr);
154 array_init(&qarr);
155
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);
160
161 ZVAL_NULL(&qstr);
162 php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC);
163 url->query = Z_STRVAL(qstr);
164 zval_dtor(&qarr);
165 } else {
166 __URLCPY(query);
167 }
168 }
169 if (!(flags & PHP_HTTP_URL_STRIP_FRAGMENT)) {
170 __URLCPY(fragment);
171 }
172
173 /* done with copy & combine & strip */
174
175 if (flags & PHP_HTTP_URL_FROM_ENV) {
176 /* free old_url we tainted above */
177 php_url_free(tmp_url);
178 }
179
180 /* set some sane defaults */
181
182 if (!url->scheme) {
183 url->scheme = estrndup("http", lenof("http"));
184 }
185
186 if (!url->host) {
187 url->host = estrndup("localhost", lenof("localhost"));
188 }
189
190 if (!url->path) {
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);
195
196 path[0] = '/';
197 memcpy(&path[1], url->path, plen + 1);
198 STR_SET(url->path, path);
199 }
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;
204
205 for (ptr = strchr(url->path, '/'); ptr; ptr = strchr(ptr, '/')) {
206 switch (ptr[1]) {
207 case '/':
208 memmove(&ptr[1], &ptr[2], end - &ptr[2]);
209 break;
210
211 case '.':
212 switch (ptr[2]) {
213 case '\0':
214 ptr[1] = '\0';
215 break;
216
217 case '/':
218 memmove(&ptr[1], &ptr[3], end - &ptr[3]);
219 break;
220
221 case '.':
222 if (ptr[3] == '/') {
223 char *pos = &ptr[4];
224 while (ptr != url->path) {
225 if (*--ptr == '/') {
226 break;
227 }
228 }
229 memmove(&ptr[1], pos, end - pos);
230 break;
231 } else if (!ptr[3]) {
232 /* .. at the end */
233 ptr[1] = '\0';
234 }
235 /* no break */
236
237 default:
238 /* something else */
239 ++ptr;
240 break;
241 }
242 break;
243
244 default:
245 ++ptr;
246 break;
247 }
248 }
249 }
250 /* unset default ports */
251 if (url->port) {
252 if ( ((url->port == 80) && !strcmp(url->scheme, "http"))
253 || ((url->port ==443) && !strcmp(url->scheme, "https"))
254 ) {
255 url->port = 0;
256 }
257 }
258
259 if (url_str) {
260 php_http_url_to_string(url, url_str, url_len TSRMLS_CC);
261 }
262
263 if (url_ptr) {
264 *url_ptr = url;
265 } else {
266 php_url_free(url);
267 }
268 }
269
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)
271 {
272 const char *arg_sep_str;
273 size_t arg_sep_len;
274 php_http_buffer_t *qstr = php_http_buffer_new();
275
276 php_http_url_argsep(&arg_sep_str, &arg_sep_len TSRMLS_CC);
277
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);
280 return FAILURE;
281 }
282
283 php_http_buffer_data(qstr, encoded_str, encoded_len);
284 php_http_buffer_free(&qstr);
285
286 return SUCCESS;
287 }
288
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)
290 {
291 if (pre_encoded_len && pre_encoded_str) {
292 php_http_buffer_append(qstr, pre_encoded_str, pre_encoded_len);
293 }
294
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)) {
296 return FAILURE;
297 }
298
299 return SUCCESS;
300 }
301
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)
306 ZEND_END_ARG_INFO();
307 PHP_METHOD(HttpUrl, __construct)
308 {
309 zval *new_url = NULL, *old_url = NULL;
310 long flags = PHP_HTTP_URL_FROM_ENV;
311 zend_error_handling zeh;
312
313 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!z!l", &old_url, &new_url, &flags), invalid_arg, return);
314
315 zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
316 {
317 php_url *res_purl, *new_purl = NULL, *old_purl = NULL;
318
319 if (new_url) {
320 switch (Z_TYPE_P(new_url)) {
321 case IS_OBJECT:
322 case IS_ARRAY:
323 new_purl = php_http_url_from_struct(NULL, HASH_OF(new_url) TSRMLS_CC);
324 break;
325 default: {
326 zval *cpy = php_http_ztyp(IS_STRING, new_url);
327
328 new_purl = php_url_parse(Z_STRVAL_P(cpy));
329 zval_ptr_dtor(&cpy);
330 break;
331 }
332 }
333 if (!new_purl) {
334 zend_restore_error_handling(&zeh TSRMLS_CC);
335 return;
336 }
337 }
338 if (old_url) {
339 switch (Z_TYPE_P(old_url)) {
340 case IS_OBJECT:
341 case IS_ARRAY:
342 old_purl = php_http_url_from_struct(NULL, HASH_OF(old_url) TSRMLS_CC);
343 break;
344 default: {
345 zval *cpy = php_http_ztyp(IS_STRING, old_url);
346
347 old_purl = php_url_parse(Z_STRVAL_P(cpy));
348 zval_ptr_dtor(&cpy);
349 break;
350 }
351 }
352 if (!old_purl) {
353 if (new_purl) {
354 php_url_free(new_purl);
355 }
356 zend_restore_error_handling(&zeh TSRMLS_CC);
357 return;
358 }
359 }
360
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);
363
364 php_url_free(res_purl);
365 if (old_purl) {
366 php_url_free(old_purl);
367 }
368 if (new_purl) {
369 php_url_free(new_purl);
370 }
371 }
372 zend_restore_error_handling(&zeh TSRMLS_CC);
373 }
374
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)
378 ZEND_END_ARG_INFO();
379 PHP_METHOD(HttpUrl, mod)
380 {
381 zval *new_url = NULL;
382 long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY;
383 zend_error_handling zeh;
384
385 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return);
386
387 zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
388 {
389 php_url *new_purl = NULL, *old_purl = NULL;
390
391 if (new_url) {
392 switch (Z_TYPE_P(new_url)) {
393 case IS_OBJECT:
394 case IS_ARRAY:
395 new_purl = php_http_url_from_struct(NULL, HASH_OF(new_url) TSRMLS_CC);
396 break;
397 default: {
398 zval *cpy = php_http_ztyp(IS_STRING, new_url);
399
400 new_purl = php_url_parse(Z_STRVAL_P(new_url));
401 zval_ptr_dtor(&cpy);
402 break;
403 }
404 }
405 if (!new_purl) {
406 zend_restore_error_handling(&zeh TSRMLS_CC);
407 return;
408 }
409 }
410
411 if ((old_purl = php_http_url_from_struct(NULL, HASH_OF(getThis()) TSRMLS_CC))) {
412 php_url *res_purl;
413
414 ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0);
415
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);
418
419 php_url_free(res_purl);
420 php_url_free(old_purl);
421 }
422 if (new_purl) {
423 php_url_free(new_purl);
424 }
425 }
426 zend_restore_error_handling(&zeh TSRMLS_CC);
427 }
428
429 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString, 0, 0, 0)
430 ZEND_END_ARG_INFO();
431 PHP_METHOD(HttpUrl, toString)
432 {
433 if (SUCCESS == zend_parse_parameters_none()) {
434 php_url *purl;
435
436 if ((purl = php_http_url_from_struct(NULL, HASH_OF(getThis()) TSRMLS_CC))) {
437 char *str;
438 size_t len;
439
440 php_http_url(0, purl, NULL, NULL, &str, &len TSRMLS_CC);
441 php_url_free(purl);
442 RETURN_STRINGL(str, len, 0);
443 }
444 }
445 RETURN_EMPTY_STRING();
446 }
447
448 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray, 0, 0, 0)
449 ZEND_END_ARG_INFO();
450 PHP_METHOD(HttpUrl, toArray)
451 {
452 php_url *purl;
453
454 if (SUCCESS != zend_parse_parameters_none()) {
455 return;
456 }
457
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);
461 php_url_free(purl);
462 }
463
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)
470 EMPTY_FUNCTION_ENTRY
471 };
472
473 zend_class_entry *php_http_url_class_entry;
474
475 PHP_MINIT_FUNCTION(http_url)
476 {
477 zend_class_entry ce = {0};
478
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);
481
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);
490
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);
504
505 return SUCCESS;
506 }
507
508
509 /*
510 * Local variables:
511 * tab-width: 4
512 * c-basic-offset: 4
513 * End:
514 * vim600: noet sw=4 ts=4 fdm=marker
515 * vim<600: noet sw=4 ts=4
516 */
517