LCOV - code coverage report
Current view: top level - ext/http - php_http_url.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 580 703 82.5 %
Date: 2015-02-17 20:30:22 Functions: 30 30 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11