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