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