2bf40b77c49af552a2c226c0e335acca7d6e35b2
[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 #ifdef PHP_HTTP_HAVE_IDN
16 # include <idna.h>
17 #endif
18
19 #ifdef PHP_HTTP_HAVE_WCHAR
20 # include <wchar.h>
21 # include <wctype.h>
22 #endif
23
24 static inline char *localhostname(void)
25 {
26 char hostname[1024] = {0};
27
28 #ifdef PHP_WIN32
29 if (SUCCESS == gethostname(hostname, lenof(hostname))) {
30 return estrdup(hostname);
31 }
32 #elif defined(HAVE_GETHOSTNAME)
33 if (SUCCESS == gethostname(hostname, lenof(hostname))) {
34 # if defined(HAVE_GETDOMAINNAME)
35 size_t hlen = strlen(hostname);
36 if (hlen <= lenof(hostname) - lenof("(none)")) {
37 hostname[hlen++] = '.';
38 if (SUCCESS == getdomainname(&hostname[hlen], lenof(hostname) - hlen)) {
39 if (!strcmp(&hostname[hlen], "(none)")) {
40 hostname[hlen - 1] = '\0';
41 }
42 return estrdup(hostname);
43 }
44 }
45 # endif
46 if (strcmp(hostname, "(none)")) {
47 return estrdup(hostname);
48 }
49 }
50 #endif
51 return estrndup("localhost", lenof("localhost"));
52 }
53
54 static php_url *php_http_url_from_env(php_url *url TSRMLS_DC)
55 {
56 zval *https, *zhost, *zport;
57 long port;
58
59 if (!url) {
60 url = ecalloc(1, sizeof(*url));
61 }
62
63 /* port */
64 zport = php_http_env_get_server_var(ZEND_STRL("SERVER_PORT"), 1 TSRMLS_CC);
65 if (zport && IS_LONG == is_numeric_string(Z_STRVAL_P(zport), Z_STRLEN_P(zport), &port, NULL, 0)) {
66 url->port = port;
67 }
68
69 /* scheme */
70 https = php_http_env_get_server_var(ZEND_STRL("HTTPS"), 1 TSRMLS_CC);
71 if (https && !strcasecmp(Z_STRVAL_P(https), "ON")) {
72 url->scheme = estrndup("https", lenof("https"));
73 } else {
74 url->scheme = estrndup("http", lenof("http"));
75 }
76
77 /* host */
78 if ((((zhost = php_http_env_get_server_var(ZEND_STRL("HTTP_HOST"), 1 TSRMLS_CC)) ||
79 (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_NAME"), 1 TSRMLS_CC)) ||
80 (zhost = php_http_env_get_server_var(ZEND_STRL("SERVER_ADDR"), 1 TSRMLS_CC)))) && Z_STRLEN_P(zhost)) {
81 size_t stop_at = strspn(Z_STRVAL_P(zhost), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-.");
82
83 url->host = estrndup(Z_STRVAL_P(zhost), stop_at);
84 } else {
85 url->host = localhostname();
86 }
87
88 /* path */
89 if (SG(request_info).request_uri && SG(request_info).request_uri[0]) {
90 const char *q = strchr(SG(request_info).request_uri, '?');
91
92 if (q) {
93 url->path = estrndup(SG(request_info).request_uri, q - SG(request_info).request_uri);
94 } else {
95 url->path = estrdup(SG(request_info).request_uri);
96 }
97 }
98
99 /* query */
100 if (SG(request_info).query_string && SG(request_info).query_string[0]) {
101 url->query = estrdup(SG(request_info).query_string);
102 }
103
104 return url;
105 }
106
107 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)
108 {
109 php_url *url, *tmp_url = NULL;
110
111 /* set from env if requested */
112 if (flags & PHP_HTTP_URL_FROM_ENV) {
113 php_url *env_url = php_http_url_from_env(NULL TSRMLS_CC);
114
115 php_http_url(flags ^ PHP_HTTP_URL_FROM_ENV, env_url, old_url, &tmp_url, NULL, NULL TSRMLS_CC);
116
117 php_url_free(env_url);
118 old_url = tmp_url;
119 }
120
121 url = ecalloc(1, sizeof(*url));
122
123 #define __URLSET(u,n) \
124 ((u)&&(u)->n)
125 #define __URLCPY(n) \
126 url->n = __URLSET(new_url,n) ? estrdup(new_url->n) : (__URLSET(old_url,n) ? estrdup(old_url->n) : NULL)
127
128 if (!(flags & PHP_HTTP_URL_STRIP_PORT)) {
129 url->port = __URLSET(new_url, port) ? new_url->port : ((old_url) ? old_url->port : 0);
130 }
131 if (!(flags & PHP_HTTP_URL_STRIP_USER)) {
132 __URLCPY(user);
133 }
134 if (!(flags & PHP_HTTP_URL_STRIP_PASS)) {
135 __URLCPY(pass);
136 }
137
138 __URLCPY(scheme);
139 __URLCPY(host);
140
141 if (!(flags & PHP_HTTP_URL_STRIP_PATH)) {
142 if ((flags & PHP_HTTP_URL_JOIN_PATH) && __URLSET(old_url, path) && __URLSET(new_url, path) && *new_url->path != '/') {
143 size_t old_path_len = strlen(old_url->path), new_path_len = strlen(new_url->path);
144
145 url->path = ecalloc(1, old_path_len + new_path_len + 1 + 1);
146
147 strcat(url->path, old_url->path);
148 if (url->path[old_path_len - 1] != '/') {
149 php_dirname(url->path, old_path_len);
150 strcat(url->path, "/");
151 }
152 strcat(url->path, new_url->path);
153 } else {
154 __URLCPY(path);
155 }
156 }
157 if (!(flags & PHP_HTTP_URL_STRIP_QUERY)) {
158 if ((flags & PHP_HTTP_URL_JOIN_QUERY) && __URLSET(new_url, query) && __URLSET(old_url, query)) {
159 zval qarr, qstr;
160
161 INIT_PZVAL(&qstr);
162 INIT_PZVAL(&qarr);
163 array_init(&qarr);
164
165 ZVAL_STRING(&qstr, old_url->query, 0);
166 php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
167 ZVAL_STRING(&qstr, new_url->query, 0);
168 php_http_querystring_update(&qarr, &qstr, NULL TSRMLS_CC);
169
170 ZVAL_NULL(&qstr);
171 php_http_querystring_update(&qarr, NULL, &qstr TSRMLS_CC);
172 url->query = Z_STRVAL(qstr);
173 zval_dtor(&qarr);
174 } else {
175 __URLCPY(query);
176 }
177 }
178 if (!(flags & PHP_HTTP_URL_STRIP_FRAGMENT)) {
179 __URLCPY(fragment);
180 }
181
182 /* done with copy & combine & strip */
183
184 if (flags & PHP_HTTP_URL_FROM_ENV) {
185 /* free old_url we tainted above */
186 php_url_free(tmp_url);
187 }
188
189 /* set some sane defaults */
190
191 if (!url->scheme) {
192 url->scheme = estrndup("http", lenof("http"));
193 }
194
195 if (!url->host) {
196 url->host = estrndup("localhost", lenof("localhost"));
197 }
198
199 if (!url->path) {
200 url->path = estrndup("/", 1);
201 } else if (url->path[0] != '/') {
202 size_t plen = strlen(url->path);
203 char *path = emalloc(plen + 1 + 1);
204
205 path[0] = '/';
206 memcpy(&path[1], url->path, plen + 1);
207 STR_SET(url->path, path);
208 }
209 /* replace directory references if path is not a single slash */
210 if ((flags & PHP_HTTP_URL_SANITIZE_PATH)
211 && url->path[0] && (url->path[0] != '/' || url->path[1])) {
212 char *ptr, *end = url->path + strlen(url->path) + 1;
213
214 for (ptr = strchr(url->path, '/'); ptr; ptr = strchr(ptr, '/')) {
215 switch (ptr[1]) {
216 case '/':
217 memmove(&ptr[1], &ptr[2], end - &ptr[2]);
218 break;
219
220 case '.':
221 switch (ptr[2]) {
222 case '\0':
223 ptr[1] = '\0';
224 break;
225
226 case '/':
227 memmove(&ptr[1], &ptr[3], end - &ptr[3]);
228 break;
229
230 case '.':
231 if (ptr[3] == '/') {
232 char *pos = &ptr[4];
233 while (ptr != url->path) {
234 if (*--ptr == '/') {
235 break;
236 }
237 }
238 memmove(&ptr[1], pos, end - pos);
239 break;
240 } else if (!ptr[3]) {
241 /* .. at the end */
242 ptr[1] = '\0';
243 }
244 /* no break */
245
246 default:
247 /* something else */
248 ++ptr;
249 break;
250 }
251 break;
252
253 default:
254 ++ptr;
255 break;
256 }
257 }
258 }
259 /* unset default ports */
260 if (url->port) {
261 if ( ((url->port == 80) && !strcmp(url->scheme, "http"))
262 || ((url->port ==443) && !strcmp(url->scheme, "https"))
263 ) {
264 url->port = 0;
265 }
266 }
267
268 if (url_str) {
269 php_http_url_to_string(url, url_str, url_len TSRMLS_CC);
270 }
271
272 if (url_ptr) {
273 *url_ptr = url;
274 } else {
275 php_url_free(url);
276 }
277 }
278
279 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)
280 {
281 const char *arg_sep_str;
282 size_t arg_sep_len;
283 php_http_buffer_t *qstr = php_http_buffer_new();
284
285 php_http_url_argsep(&arg_sep_str, &arg_sep_len TSRMLS_CC);
286
287 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)) {
288 php_http_buffer_free(&qstr);
289 return FAILURE;
290 }
291
292 php_http_buffer_data(qstr, encoded_str, encoded_len);
293 php_http_buffer_free(&qstr);
294
295 return SUCCESS;
296 }
297
298 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)
299 {
300 if (pre_encoded_len && pre_encoded_str) {
301 php_http_buffer_append(qstr, pre_encoded_str, pre_encoded_len);
302 }
303
304 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)) {
305 return FAILURE;
306 }
307
308 return SUCCESS;
309 }
310
311 void php_http_url_dtor(php_http_url_t *url)
312 {
313 STR_FREE(url->scheme.str);
314 STR_FREE(url->authority.userinfo.username.str);
315 STR_FREE(url->authority.userinfo.password.str);
316 STR_FREE(url->authority.host.str);
317 STR_FREE(url->path.str);
318 STR_FREE(url->query.str);
319 STR_FREE(url->fragment.str);
320 }
321
322 void php_http_url_free(php_http_url_t **url)
323 {
324 if (*url) {
325 php_http_url_dtor(*url);
326 efree(*url);
327 *url = NULL;
328 }
329 }
330
331 static const unsigned char utf8mblen[256] = {
332 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
333 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
334 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
335 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
336 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
337 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
338 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
339 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
340 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
341 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
342 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
343 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
344 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
345 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
346 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
347 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6
348 };
349 static const unsigned char utf8mask[] = {
350 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01
351 };
352
353 static inline size_t utf8towc(unsigned *wc, const unsigned char *uc, size_t len)
354 {
355 unsigned char ub = utf8mblen[*uc];
356
357 if (!ub || ub > len || ub > 3) {
358 return 0;
359 }
360
361 *wc = *uc & utf8mask[ub];
362
363 switch (ub) {
364 case 4:
365 if ((uc[1] & 0xc0) != 0x80) {
366 return 0;
367 }
368 *wc <<= 6;
369 *wc += *++uc & 0x3f;
370 /* no break */
371 case 3:
372 if ((uc[1] & 0xc0) != 0x80) {
373 return 0;
374 }
375 *wc <<= 6;
376 *wc += *++uc & 0x3f;
377 /* no break */
378 case 2:
379 if ((uc[1] & 0xc0) != 0x80) {
380 return 0;
381 }
382 *wc <<= 6;
383 *wc += *++uc & 0x3f;
384 break;
385
386 default:
387 return 0;
388 }
389
390 return ub;
391 }
392
393 #include "ualpha.h"
394
395 static inline zend_bool isualnum(unsigned ch)
396 {
397 unsigned i;
398
399 /* digits */
400 if (ch >= 0x30 && ch <= 0x39) {
401 return 1;
402 }
403
404 for (i = 0; i < sizeof(utf8_ranges)/sizeof(utf8_range_t); ++i) {
405 if (utf8_ranges[i].start == ch) {
406 return 1;
407 } else if (utf8_ranges[i].start <= ch && utf8_ranges[i].end >= ch) {
408 if (utf8_ranges[i].step == 1) {
409 return 1;
410 }
411 /* FIXME step */
412 return 0;
413 }
414 }
415 return 0;
416 }
417
418 static size_t parse_mb_utf8(php_http_url_t *url, const char *ptr, const char *end, zend_bool idn)
419 {
420 unsigned wchar;
421 size_t consumed = utf8towc(&wchar, (const unsigned char *) ptr, end - ptr);
422
423 if (!consumed || consumed == (size_t) -1) {
424 return 0;
425 }
426 if (!idn && !isualnum(wchar)) {
427 return 0;
428 }
429
430 return consumed;
431 }
432
433 #ifdef PHP_HTTP_HAVE_WCHAR
434 static size_t parse_mb_loc(php_http_url_t *url, const char *ptr, const char *end, zend_bool idn)
435 {
436 wchar_t wchar;
437 size_t consumed = 0;
438 #if defined(HAVE_MBRTOWC)
439 mbstate_t ps = {0};
440
441 consumed = mbrtowc(&wchar, ptr, end - ptr, &ps);
442 #elif defined(HAVE_MBTOWC)
443 consumed = mbtowc(&wchar, ptr, end - ptr);
444 #endif
445
446 if (!consumed || consumed == (size_t) -1) {
447 return 0;
448 }
449 if (!idn && !iswalnum(wchar)) {
450 return 0;
451 }
452
453 return consumed;
454 }
455 #endif
456
457 typedef enum parse_mb_what {
458 PARSE_SCHEME,
459 PARSE_USERINFO,
460 PARSE_HOSTINFO,
461 PARSE_PATH,
462 PARSE_QUERY,
463 PARSE_FRAGMENT
464 } parse_mb_what_t;
465
466 static const char * const parse_what[] = {
467 "scheme",
468 "userinfo",
469 "hostinfo",
470 "path",
471 "query",
472 "fragment"
473 };
474
475 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)
476 {
477 size_t consumed = 0;
478 zend_bool idn = (what == PARSE_HOSTINFO) && (url->flags & PHP_HTTP_URL_PARSE_IDN);
479
480 if (url->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
481 consumed = parse_mb_utf8(url, ptr, end, idn);
482 }
483 #ifdef PHP_HTTP_HAVE_WCHAR
484 else if (url->flags & PHP_HTTP_URL_PARSE_MBLOC) {
485 consumed = parse_mb_loc(url, ptr, end, idn);
486 }
487 #endif
488
489 if (!consumed && !silent) {
490 TSRMLS_FETCH_FROM_CTX(url->ts);
491 php_error_docref(NULL TSRMLS_CC, E_WARNING,
492 "Failed to parse %s; unexpected byte 0x%02x at pos %u in '%s'",
493 parse_what[what], (unsigned char) *ptr, (unsigned) (ptr - begin), begin);
494 }
495
496 return consumed;
497 }
498
499 static STATUS parse_userinfo(php_http_url_t *url, const char *ptr, const char *end)
500 {
501 size_t mb;
502 const char *password = NULL, *tmp = ptr;
503 TSRMLS_FETCH_FROM_CTX(url->ts);
504
505 do {
506 switch (*ptr) {
507 case ':':
508 if (password) {
509 php_error_docref(NULL TSRMLS_CC, E_WARNING,
510 "Failed to parse password; duplicate ':' at pos %u in '%s'",
511 (unsigned) (ptr - tmp), tmp);
512 return FAILURE;
513 }
514 password = ptr + 1;
515 break;
516
517 case '%':
518 if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
519 php_error_docref(NULL TSRMLS_CC, E_WARNING,
520 "Failed to parse userinfo; invalid percent encoding at pos %u in '%s'",
521 (unsigned) (ptr - tmp), tmp);
522 return FAILURE;
523 }
524 ptr += 2;
525 break;
526
527 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
528 case '+': case ',': case ';': case '=': /* sub-delims */
529 case '-': case '.': case '_': case '~': /* unreserved */
530 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
531 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
532 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
533 case 'V': case 'W': case 'X': case 'Y': case 'Z':
534 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
535 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
536 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
537 case 'v': case 'w': case 'x': case 'y': case 'z':
538 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
539 case '7': case '8': case '9':
540 /* allowed */
541 break;
542
543 default:
544 if (!(mb = parse_mb(url, PARSE_USERINFO, ptr, end, tmp, 0))) {
545 return FAILURE;
546 }
547 ptr += mb - 1;
548 }
549 } while(++ptr != end);
550
551 if (password) {
552 if ((url->authority.userinfo.username.len = password - tmp - 1)) {
553 url->authority.userinfo.username.str = estrndup(tmp,
554 url->authority.userinfo.username.len);
555 }
556 if ((url->authority.userinfo.password.len = end - password)) {
557 url->authority.userinfo.password.str = estrndup(password,
558 url->authority.userinfo.password.len);
559 }
560 } else {
561 if ((url->authority.userinfo.username.len = end - tmp)) {
562 url->authority.userinfo.username.str = estrndup(tmp,
563 url->authority.userinfo.username.len);
564 }
565 }
566
567 return SUCCESS;
568 }
569
570 static STATUS parse_hostinfo(php_http_url_t *url, const char *ptr, const char *end)
571 {
572 size_t mb;
573 const char *tmp = ptr, *port = NULL;
574 TSRMLS_FETCH_FROM_CTX(url->ts);
575
576 /* FIXME: IP(v6) addresses */
577 do {
578 switch (*ptr) {
579 case ':':
580 if (port) {
581 php_error_docref(NULL TSRMLS_CC, E_WARNING,
582 "Failed to parse port; duplicate ':' at pos %u in '%s'",
583 (unsigned) (ptr - tmp), tmp);
584 return FAILURE;
585 }
586 port = ptr + 1;
587 break;
588
589 case '%':
590 if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
591 php_error_docref(NULL TSRMLS_CC, E_WARNING,
592 "Failed to parse hostinfo; invalid percent encoding at pos %u in '%s'",
593 (unsigned) (ptr - tmp), tmp);
594 return FAILURE;
595 }
596 ptr += 2;
597 break;
598
599 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
600 case '+': case ',': case ';': case '=': /* sub-delims */
601 case '-': case '.': case '_': case '~': /* unreserved */
602 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
603 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
604 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
605 case 'V': case 'W': case 'X': case 'Y': case 'Z':
606 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
607 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
608 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
609 case 'v': case 'w': case 'x': case 'y': case 'z':
610 if (port) {
611 php_error_docref(NULL TSRMLS_CC, E_WARNING,
612 "Failed to parse port; unexpected char '%c' at pos %u in '%s'",
613 (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
614 return FAILURE;
615 }
616 /* no break */
617 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
618 case '7': case '8': case '9':
619 /* allowed */
620 if (port) {
621 url->authority.port *= 10;
622 url->authority.port += *ptr - '0';
623 }
624 break;
625
626 default:
627 if (port) {
628 php_error_docref(NULL TSRMLS_CC, E_WARNING,
629 "Failed to parse port; unexpected byte 0x%02x at pos %u in '%s'",
630 (unsigned char) *ptr, (unsigned) (ptr - tmp), tmp);
631 return FAILURE;
632 } else if (!(mb = parse_mb(url, PARSE_HOSTINFO, ptr, end, tmp, 0))) {
633 return FAILURE;
634 }
635 ptr += mb - 1;
636 }
637 } while (++ptr != end);
638
639 if (port) {
640 url->authority.host.len = port - tmp - 1;
641 } else {
642 url->authority.host.len = end - tmp;
643 }
644
645 url->authority.host.str = estrndup(tmp, url->authority.host.len);
646
647 #ifdef PHP_HTTP_HAVE_IDN
648 if (url->flags & PHP_HTTP_URL_PARSE_IDN) {
649 if (url->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
650 char *idn = NULL;
651 int rv = idna_to_ascii_8z(url->authority.host.str, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
652
653 if (rv != IDNA_SUCCESS) {
654 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
655 return FAILURE;
656 } else {
657 STR_SET(url->authority.host.str, estrdup(idn));
658 url->authority.host.len = strlen(idn);
659 free(idn);
660 }
661 }
662 # ifdef PHP_HTTP_HAVE_WCHAR
663 else if (url->flags & PHP_HTTP_URL_PARSE_MBLOC) {
664 char *idn = NULL;
665 int rv = idna_to_ascii_lz(url->authority.host.str, &idn, IDNA_ALLOW_UNASSIGNED|IDNA_USE_STD3_ASCII_RULES);
666
667 if (rv != IDNA_SUCCESS) {
668 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse IDN; %s", idna_strerror(rv));
669 return FAILURE;
670 } else {
671 STR_SET(url->authority.host.str, estrdup(idn));
672 url->authority.host.len = strlen(idn);
673 free(idn);
674 }
675 }
676 }
677 # endif
678 #endif
679
680 return SUCCESS;
681 }
682
683 static const char *parse_authority(php_http_url_t *url, const char *ptr, const char *end)
684 {
685 const char *tmp = ptr;
686
687 do {
688 switch (*ptr) {
689 case '@':
690 /* userinfo delimiter */
691 if (tmp != ptr && SUCCESS != parse_userinfo(url, tmp, ptr)) {
692 return NULL;
693 }
694 tmp = ptr + 1;
695 break;
696
697 case '/':
698 case '?':
699 case '#':
700 case '\0':
701 /* host delimiter */
702 if (tmp != ptr && SUCCESS != parse_hostinfo(url, tmp, ptr)) {
703 return NULL;
704 }
705 return ptr;
706 }
707 } while (++ptr <= end);
708
709 return NULL;
710 }
711
712 static const char *parse_path(php_http_url_t *url, const char *ptr, const char *end)
713 {
714 size_t mb;
715 const char *tmp = ptr;
716 TSRMLS_FETCH_FROM_CTX(url->ts);
717
718 do {
719 switch (*ptr) {
720 case '?':
721 case '\0':
722 if ((url->path.len = ptr - tmp)) {
723 url->path.str = estrndup(tmp, url->path.len);
724 }
725 return ptr;
726
727 case '%':
728 if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
729 php_error_docref(NULL TSRMLS_CC, E_WARNING,
730 "Failed to parse path; invalid percent encoding at pos %u in '%s'",
731 (unsigned) (ptr - tmp), tmp);
732 return NULL;
733 }
734 ptr += 2;
735 break;
736
737 case '/': /* yeah, well */
738 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
739 case '+': case ',': case ';': case '=': /* sub-delims */
740 case '-': case '.': case '_': case '~': /* unreserved */
741 case ':': case '@': /* pchar */
742 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
743 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
744 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
745 case 'V': case 'W': case 'X': case 'Y': case 'Z':
746 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
747 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
748 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
749 case 'v': case 'w': case 'x': case 'y': case 'z':
750 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
751 case '7': case '8': case '9':
752 /* allowed */
753 break;
754
755 default:
756 if (!(mb = parse_mb(url, PARSE_PATH, ptr, end, tmp, 0))) {
757 return NULL;
758 }
759 ptr += mb - 1;
760 }
761 } while (++ptr <= end);
762
763 return NULL;
764 }
765
766 static const char *parse_query(php_http_url_t *url, const char *ptr, const char *end)
767 {
768 size_t mb;
769 const char *tmp = ptr + !!*ptr;
770 TSRMLS_FETCH_FROM_CTX(url->ts);
771
772 do {
773 switch (*ptr) {
774 case '#':
775 case '\0':
776 if ((url->query.len = ptr - tmp)) {
777 url->query.str = estrndup(tmp, url->query.len);
778 }
779 return ptr;
780
781 case '%':
782 if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
783 php_error_docref(NULL TSRMLS_CC, E_WARNING,
784 "Failed to parse query; invalid percent encoding at pos %u in '%s'",
785 (unsigned) (ptr - tmp), tmp);
786 return NULL;
787 }
788 ptr += 2;
789 break;
790
791 case '?': case '/': /* yeah, well */
792 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
793 case '+': case ',': case ';': case '=': /* sub-delims */
794 case '-': case '.': case '_': case '~': /* unreserved */
795 case ':': case '@': /* pchar */
796 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
797 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
798 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
799 case 'V': case 'W': case 'X': case 'Y': case 'Z':
800 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
801 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
802 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
803 case 'v': case 'w': case 'x': case 'y': case 'z':
804 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
805 case '7': case '8': case '9':
806 /* allowed */
807 break;
808
809 default:
810 if (!(mb = parse_mb(url, PARSE_QUERY, ptr, end, tmp, 0))) {
811 return NULL;
812 }
813 ptr += mb - 1;
814 }
815 } while (++ptr <= end);
816
817 return NULL;
818 }
819
820 static const char *parse_fragment(php_http_url_t *url, const char *ptr, const char *end)
821 {
822 size_t mb;
823 const char *tmp = ptr + !!*ptr;
824 TSRMLS_FETCH_FROM_CTX(url->ts);
825
826 do {
827 switch (*ptr) {
828 case '\0':
829 if ((url->fragment.len = ptr - tmp)) {
830 url->fragment.str = estrndup(tmp, url->fragment.len);
831 }
832 return ptr;
833
834 case '%':
835 if (ptr[1] != '%' && (end - ptr <= 2 || !isxdigit(*(ptr+1)) || !isxdigit(*(ptr+2)))) {
836 php_error_docref(NULL TSRMLS_CC, E_WARNING,
837 "Failed to parse query; invalid percent encoding at pos %u in '%s'",
838 (unsigned) (ptr - tmp), tmp);
839 return NULL;
840 }
841 ptr += 2;
842 break;
843
844 case '?': case '/': /* yeah, well */
845 case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
846 case '+': case ',': case ';': case '=': /* sub-delims */
847 case '-': case '.': case '_': case '~': /* unreserved */
848 case ':': case '@': /* pchar */
849 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
850 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
851 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
852 case 'V': case 'W': case 'X': case 'Y': case 'Z':
853 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
854 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
855 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
856 case 'v': case 'w': case 'x': case 'y': case 'z':
857 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
858 case '7': case '8': case '9':
859 /* allowed */
860 break;
861
862 default:
863 if (!(mb = parse_mb(url, PARSE_FRAGMENT, ptr, end, tmp, 0))) {
864 return NULL;
865 }
866 ptr += mb - 1;
867 }
868 } while (++ptr <= end);
869
870 return NULL;
871 }
872
873 static const char *parse_hier(php_http_url_t *url, const char *ptr, const char *end)
874 {
875 if (*ptr == '/') {
876 if (end - ptr > 1) {
877 if (*(ptr + 1) == '/') {
878 if (!(ptr = parse_authority(url, ptr + 2, end))) {
879 return NULL;
880 }
881 }
882 }
883 }
884 return parse_path(url, ptr, end);
885 }
886
887 static const char *parse_scheme(php_http_url_t *url, const char *ptr, const char *end)
888 {
889 size_t mb;
890 const char *tmp = ptr;
891
892 do {
893 switch (*ptr) {
894 case ':':
895 /* scheme delimiter */
896 url->scheme.len = ptr - tmp;
897 url->scheme.str = estrndup(tmp, url->scheme.len);
898 return ++ptr;
899
900 case '0': case '1': case '2': case '3': case '4': case '5': case '6':
901 case '7': case '8': case '9':
902 case '+': case '-': case '.':
903 if (ptr == tmp) {
904 return tmp;
905 }
906 /* no break */
907 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
908 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
909 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
910 case 'V': case 'W': case 'X': case 'Y': case 'Z':
911 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
912 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
913 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
914 case 'v': case 'w': case 'x': case 'y': case 'z':
915 /* scheme part */
916 break;
917
918 default:
919 if (!(mb = parse_mb(url, PARSE_SCHEME, ptr, end, tmp, 1))) {
920 /* soft fail; parse path next */
921 return tmp;
922 }
923 ptr += mb - 1;
924 }
925 } while (++ptr != end);
926
927 return tmp;
928 }
929
930 php_http_url_t *php_http_url_init(php_http_url_t *url, const char *str, size_t len, unsigned flags TSRMLS_DC)
931 {
932 const char *ptr, *end = str + len;
933 zend_bool free_url = !url;
934
935 if (url) {
936 memset(url, 0, sizeof(*url));
937 } else {
938 url = ecalloc(1, sizeof(*url));
939 }
940
941 url->flags = flags;
942 TSRMLS_SET_CTX(url->ts);
943
944 if ((ptr = str) && !(str = parse_scheme(url, ptr, end))) {
945 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL scheme: '%s'", ptr);
946 if (free_url) {
947 php_http_url_free(&url);
948 } else {
949 php_http_url_dtor(url);
950 }
951 return NULL;
952 }
953
954 if ((ptr = str) && !(str = parse_hier(url, ptr, end))) {
955 if (free_url) {
956 php_http_url_free(&url);
957 } else {
958 php_http_url_dtor(url);
959 }
960 return NULL;
961 }
962
963 if ((ptr = str) && !(str = parse_query(url, ptr, end))) {
964 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL query: '%s'", ptr);
965 if (free_url) {
966 php_http_url_free(&url);
967 } else {
968 php_http_url_dtor(url);
969 }
970 return NULL;
971 }
972
973 if ((ptr = str) && !(str = parse_fragment(url, ptr, end))) {
974 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse URL fragment: '%s'", ptr);
975 if (free_url) {
976 php_http_url_free(&url);
977 } else {
978 php_http_url_dtor(url);
979 }
980 return NULL;
981 }
982
983 return url;
984 }
985
986 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl___construct, 0, 0, 0)
987 ZEND_ARG_INFO(0, old_url)
988 ZEND_ARG_INFO(0, new_url)
989 ZEND_ARG_INFO(0, flags)
990 ZEND_END_ARG_INFO();
991 PHP_METHOD(HttpUrl, __construct)
992 {
993 zval *new_url = NULL, *old_url = NULL;
994 long flags = PHP_HTTP_URL_FROM_ENV;
995 zend_error_handling zeh;
996
997 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!z!l", &old_url, &new_url, &flags), invalid_arg, return);
998
999 zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
1000 {
1001 php_url *res_purl, *new_purl = NULL, *old_purl = NULL;
1002
1003 if (new_url) {
1004 switch (Z_TYPE_P(new_url)) {
1005 case IS_OBJECT:
1006 case IS_ARRAY:
1007 new_purl = php_http_url_from_struct(NULL, HASH_OF(new_url) TSRMLS_CC);
1008 break;
1009 default: {
1010 zval *cpy = php_http_ztyp(IS_STRING, new_url);
1011
1012 new_purl = php_url_parse(Z_STRVAL_P(cpy));
1013 zval_ptr_dtor(&cpy);
1014 break;
1015 }
1016 }
1017 if (!new_purl) {
1018 zend_restore_error_handling(&zeh TSRMLS_CC);
1019 return;
1020 }
1021 }
1022 if (old_url) {
1023 switch (Z_TYPE_P(old_url)) {
1024 case IS_OBJECT:
1025 case IS_ARRAY:
1026 old_purl = php_http_url_from_struct(NULL, HASH_OF(old_url) TSRMLS_CC);
1027 break;
1028 default: {
1029 zval *cpy = php_http_ztyp(IS_STRING, old_url);
1030
1031 old_purl = php_url_parse(Z_STRVAL_P(cpy));
1032 zval_ptr_dtor(&cpy);
1033 break;
1034 }
1035 }
1036 if (!old_purl) {
1037 if (new_purl) {
1038 php_url_free(new_purl);
1039 }
1040 zend_restore_error_handling(&zeh TSRMLS_CC);
1041 return;
1042 }
1043 }
1044
1045 php_http_url(flags, old_purl, new_purl, &res_purl, NULL, NULL TSRMLS_CC);
1046 php_http_url_to_struct(res_purl, getThis() TSRMLS_CC);
1047
1048 php_url_free(res_purl);
1049 if (old_purl) {
1050 php_url_free(old_purl);
1051 }
1052 if (new_purl) {
1053 php_url_free(new_purl);
1054 }
1055 }
1056 zend_restore_error_handling(&zeh TSRMLS_CC);
1057 }
1058
1059 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_mod, 0, 0, 1)
1060 ZEND_ARG_INFO(0, more_url_parts)
1061 ZEND_ARG_INFO(0, flags)
1062 ZEND_END_ARG_INFO();
1063 PHP_METHOD(HttpUrl, mod)
1064 {
1065 zval *new_url = NULL;
1066 long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY;
1067 zend_error_handling zeh;
1068
1069 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!|l", &new_url, &flags), invalid_arg, return);
1070
1071 zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
1072 {
1073 php_url *new_purl = NULL, *old_purl = NULL;
1074
1075 if (new_url) {
1076 switch (Z_TYPE_P(new_url)) {
1077 case IS_OBJECT:
1078 case IS_ARRAY:
1079 new_purl = php_http_url_from_struct(NULL, HASH_OF(new_url) TSRMLS_CC);
1080 break;
1081 default: {
1082 zval *cpy = php_http_ztyp(IS_STRING, new_url);
1083
1084 new_purl = php_url_parse(Z_STRVAL_P(new_url));
1085 zval_ptr_dtor(&cpy);
1086 break;
1087 }
1088 }
1089 if (!new_purl) {
1090 zend_restore_error_handling(&zeh TSRMLS_CC);
1091 return;
1092 }
1093 }
1094
1095 if ((old_purl = php_http_url_from_struct(NULL, HASH_OF(getThis()) TSRMLS_CC))) {
1096 php_url *res_purl;
1097
1098 ZVAL_OBJVAL(return_value, zend_objects_clone_obj(getThis() TSRMLS_CC), 0);
1099
1100 php_http_url(flags, old_purl, new_purl, &res_purl, NULL, NULL TSRMLS_CC);
1101 php_http_url_to_struct(res_purl, return_value TSRMLS_CC);
1102
1103 php_url_free(res_purl);
1104 php_url_free(old_purl);
1105 }
1106 if (new_purl) {
1107 php_url_free(new_purl);
1108 }
1109 }
1110 zend_restore_error_handling(&zeh TSRMLS_CC);
1111 }
1112
1113 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toString, 0, 0, 0)
1114 ZEND_END_ARG_INFO();
1115 PHP_METHOD(HttpUrl, toString)
1116 {
1117 if (SUCCESS == zend_parse_parameters_none()) {
1118 php_url *purl;
1119
1120 if ((purl = php_http_url_from_struct(NULL, HASH_OF(getThis()) TSRMLS_CC))) {
1121 char *str;
1122 size_t len;
1123
1124 php_http_url(0, purl, NULL, NULL, &str, &len TSRMLS_CC);
1125 php_url_free(purl);
1126 RETURN_STRINGL(str, len, 0);
1127 }
1128 }
1129 RETURN_EMPTY_STRING();
1130 }
1131
1132 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_toArray, 0, 0, 0)
1133 ZEND_END_ARG_INFO();
1134 PHP_METHOD(HttpUrl, toArray)
1135 {
1136 php_url *purl;
1137
1138 if (SUCCESS != zend_parse_parameters_none()) {
1139 return;
1140 }
1141
1142 /* strip any non-URL properties */
1143 purl = php_http_url_from_struct(NULL, HASH_OF(getThis()) TSRMLS_CC);
1144 php_http_url_to_struct(purl, return_value TSRMLS_CC);
1145 php_url_free(purl);
1146 }
1147
1148 ZEND_BEGIN_ARG_INFO_EX(ai_HttpUrl_parse, 0, 0, 1)
1149 ZEND_ARG_INFO(0, url)
1150 ZEND_ARG_INFO(0, flags)
1151 ZEND_END_ARG_INFO();
1152 PHP_METHOD(HttpUrl, parse)
1153 {
1154 char *str;
1155 int len;
1156 long flags = 0;
1157 php_http_url_t url;
1158 zend_error_handling zeh;
1159
1160 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &str, &len, &flags), invalid_arg, return);
1161
1162 zend_replace_error_handling(EH_THROW, php_http_exception_bad_url_class_entry, &zeh TSRMLS_CC);
1163 if (php_http_url_init(&url, str, len, flags TSRMLS_CC)) {
1164 object_init_ex(return_value, php_http_url_class_entry);
1165 if (url.scheme.len) {
1166 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("scheme"),
1167 url.scheme.str, url.scheme.len TSRMLS_CC);
1168 }
1169 if (url.authority.userinfo.username.len) {
1170 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("user"),
1171 url.authority.userinfo.username.str, url.authority.userinfo.username.len TSRMLS_CC);
1172 }
1173 if (url.authority.userinfo.password.len) {
1174 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("pass"),
1175 url.authority.userinfo.password.str, url.authority.userinfo.password.len TSRMLS_CC);
1176 }
1177 if (url.authority.host.len) {
1178 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("host"),
1179 url.authority.host.str, url.authority.host.len TSRMLS_CC);
1180 }
1181 if (url.authority.port) {
1182 zend_update_property_long(php_http_url_class_entry, return_value, ZEND_STRL("port"),
1183 url.authority.port TSRMLS_CC);
1184 }
1185 if (url.path.len) {
1186 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("path"),
1187 url.path.str, url.path.len TSRMLS_CC);
1188 }
1189 if (url.query.len) {
1190 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("query"),
1191 url.query.str, url.query.len TSRMLS_CC);
1192 }
1193 if (url.fragment.len) {
1194 zend_update_property_stringl(php_http_url_class_entry, return_value, ZEND_STRL("fragment"),
1195 url.fragment.str, url.fragment.len TSRMLS_CC);
1196 }
1197 php_http_url_dtor(&url);
1198 }
1199 zend_restore_error_handling(&zeh TSRMLS_CC);
1200 }
1201
1202 static zend_function_entry php_http_url_methods[] = {
1203 PHP_ME(HttpUrl, __construct, ai_HttpUrl___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
1204 PHP_ME(HttpUrl, mod, ai_HttpUrl_mod, ZEND_ACC_PUBLIC)
1205 PHP_ME(HttpUrl, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
1206 ZEND_MALIAS(HttpUrl, __toString, toString, ai_HttpUrl_toString, ZEND_ACC_PUBLIC)
1207 PHP_ME(HttpUrl, toArray, ai_HttpUrl_toArray, ZEND_ACC_PUBLIC)
1208 PHP_ME(HttpUrl, parse, ai_HttpUrl_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
1209 EMPTY_FUNCTION_ENTRY
1210 };
1211
1212 zend_class_entry *php_http_url_class_entry;
1213
1214 PHP_MINIT_FUNCTION(http_url)
1215 {
1216 zend_class_entry ce = {0};
1217
1218 INIT_NS_CLASS_ENTRY(ce, "http", "Url", php_http_url_methods);
1219 php_http_url_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
1220
1221 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("scheme"), ZEND_ACC_PUBLIC TSRMLS_CC);
1222 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC);
1223 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC);
1224 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC);
1225 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC);
1226 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("path"), ZEND_ACC_PUBLIC TSRMLS_CC);
1227 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC);
1228 zend_declare_property_null(php_http_url_class_entry, ZEND_STRL("fragment"), ZEND_ACC_PUBLIC TSRMLS_CC);
1229
1230 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("REPLACE"), PHP_HTTP_URL_REPLACE TSRMLS_CC);
1231 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_PATH"), PHP_HTTP_URL_JOIN_PATH TSRMLS_CC);
1232 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("JOIN_QUERY"), PHP_HTTP_URL_JOIN_QUERY TSRMLS_CC);
1233 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_USER"), PHP_HTTP_URL_STRIP_USER TSRMLS_CC);
1234 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PASS"), PHP_HTTP_URL_STRIP_PASS TSRMLS_CC);
1235 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_AUTH"), PHP_HTTP_URL_STRIP_AUTH TSRMLS_CC);
1236 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PORT"), PHP_HTTP_URL_STRIP_PORT TSRMLS_CC);
1237 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_PATH"), PHP_HTTP_URL_STRIP_PATH TSRMLS_CC);
1238 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_QUERY"), PHP_HTTP_URL_STRIP_QUERY TSRMLS_CC);
1239 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_FRAGMENT"), PHP_HTTP_URL_STRIP_FRAGMENT TSRMLS_CC);
1240 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("STRIP_ALL"), PHP_HTTP_URL_STRIP_ALL TSRMLS_CC);
1241 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("FROM_ENV"), PHP_HTTP_URL_FROM_ENV TSRMLS_CC);
1242 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("SANITIZE_PATH"), PHP_HTTP_URL_SANITIZE_PATH TSRMLS_CC);
1243
1244 #ifdef PHP_HTTP_HAVE_WCHAR
1245 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC TSRMLS_CC);
1246 #endif
1247 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8 TSRMLS_CC);
1248 #ifdef PHP_HTTP_HAVE_IDN
1249 zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_IDN"), PHP_HTTP_URL_PARSE_IDN TSRMLS_CC);
1250 #endif
1251
1252 return SUCCESS;
1253 }
1254
1255
1256 /*
1257 * Local variables:
1258 * tab-width: 4
1259 * c-basic-offset: 4
1260 * End:
1261 * vim600: noet sw=4 ts=4 fdm=marker
1262 * vim<600: noet sw=4 ts=4
1263 */
1264