Merge branch 'R_2_5' [ci skip]
[m6w6/ext-http] / php_http_url.c
index 5a267d898ee086fb3be58638e42c5d49f37cf840..afe39bfe4511711cb5db2cff518c31e0f4e8952c 100644 (file)
@@ -12,7 +12,9 @@
 
 #include "php_http_api.h"
 
-#ifdef PHP_HTTP_HAVE_IDN
+#if PHP_HTTP_HAVE_IDN2
+#      include <idn2.h>
+#elif PHP_HTTP_HAVE_IDN
 #      include <idna.h>
 #endif
 
@@ -567,8 +569,8 @@ HashTable *php_http_url_to_struct(const php_http_url_t *url, zval *strct)
 
 ZEND_RESULT_CODE php_http_url_encode_hash(HashTable *hash, const char *pre_encoded_str, size_t pre_encoded_len, char **encoded_str, size_t *encoded_len)
 {
-       const char *arg_sep_str;
-       size_t arg_sep_len;
+       const char *arg_sep_str = "&";
+       size_t arg_sep_len = 1;
        php_http_buffer_t *qstr = php_http_buffer_new();
 
        php_http_url_argsep(&arg_sep_str, &arg_sep_len);
@@ -880,7 +882,32 @@ static ZEND_RESULT_CODE to_utf16(parse_mb_func fn, const char *u8, uint16_t **u1
 #      define MAXHOSTNAMELEN 256
 #endif
 
-#ifdef PHP_HTTP_HAVE_IDN
+#if PHP_HTTP_HAVE_IDN2
+static ZEND_RESULT_CODE parse_idn2(struct parse_state *state, size_t prev_len)
+{
+       char *idn = NULL;
+       int rv = -1;
+
+       if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
+               rv = idn2_lookup_u8((const unsigned char *) state->url.host, (unsigned char **) &idn, IDN2_NFC_INPUT);
+       }
+#      ifdef PHP_HTTP_HAVE_WCHAR
+       else if (state->flags & PHP_HTTP_URL_PARSE_MBLOC) {
+               rv = idn2_lookup_ul(state->url.host, &idn, 0);
+       }
+#      endif
+       if (rv != IDN2_OK) {
+               php_error_docref(NULL, E_WARNING, "Failed to parse IDN; %s", idn2_strerror(rv));
+               return FAILURE;
+       } else {
+               size_t idnlen = strlen(idn);
+               memcpy(state->url.host, idn, idnlen + 1);
+               free(idn);
+               state->offset += idnlen - prev_len;
+               return SUCCESS;
+       }
+}
+#elif PHP_HTTP_HAVE_IDN
 static ZEND_RESULT_CODE parse_idn(struct parse_state *state, size_t prev_len)
 {
        char *idn = NULL;
@@ -921,7 +948,6 @@ static ZEND_RESULT_CODE parse_uidn(struct parse_state *state)
        uint16_t *uhost_str, ahost_str[MAXHOSTNAMELEN], *ahost_ptr;
        size_t uhost_len, ahost_len;
        UErrorCode error = U_ZERO_ERROR;
-       TSRMLS_FETCH_FROM_CTX(state->ts);
 
        if (state->flags & PHP_HTTP_URL_PARSE_MBUTF8) {
                if (SUCCESS != to_utf16(parse_mb_utf8, state->url.host, &uhost_str, &uhost_len)) {
@@ -1113,7 +1139,9 @@ static ZEND_RESULT_CODE parse_hostinfo(struct parse_state *state, const char *pt
        }
 
        if (state->flags & PHP_HTTP_URL_PARSE_TOIDN) {
-#ifdef PHP_HTTP_HAVE_IDN
+#if PHP_HTTP_HAVE_IDN2
+               return parse_idn2(state, len);
+#elif PHP_HTTP_HAVE_IDN
                return parse_idn(state, len);
 #endif
 #ifdef HAVE_UIDNA_IDNTOASCII
@@ -1245,7 +1273,7 @@ static const char *parse_query(struct parse_state *state)
        tmp = ++state->ptr;
        state->url.query = &state->buffer[state->offset];
 
-       do {
+       while (state->ptr < state->end) {
                switch (*state->ptr) {
                case '#':
                        goto done;
@@ -1262,8 +1290,11 @@ static const char *parse_query(struct parse_state *state)
                        state->buffer[state->offset++] = *state->ptr;
                        break;
 
-               case ']':
-               case '[':
+               /* RFC1738 unsafe */
+               case '{': case '}':
+               case '<': case '>':
+               case '[': case ']':
+               case '|': case '\\': case '^': case '`': case '"': case ' ':
                        if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) {
                                state->buffer[state->offset++] = '%';
                                state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4];
@@ -1297,7 +1328,9 @@ static const char *parse_query(struct parse_state *state)
                        }
                        state->ptr += mb - 1;
                }
-       } while (++state->ptr < state->end);
+
+               ++state->ptr;
+       }
 
        done:
        state->buffer[state->offset++] = 0;
@@ -1332,6 +1365,19 @@ static const char *parse_fragment(struct parse_state *state)
                        state->buffer[state->offset++] = *state->ptr;
                        break;
 
+               /* RFC1738 unsafe */
+               case '{': case '}':
+               case '<': case '>':
+               case '[': case ']':
+               case '|': case '\\': case '^': case '`': case '"': case ' ':
+                       if (state->flags & PHP_HTTP_URL_PARSE_TOPCT) {
+                               state->buffer[state->offset++] = '%';
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) >> 4];
+                               state->buffer[state->offset++] = parse_xdigits[((unsigned char) *state->ptr) & 0xf];
+                               break;
+                       }
+                       /* no break */
+
                case '?': case '/':
                case '!': case '$': case '&': case '\'': case '(': case ')': case '*':
                case '+': case ',': case ';': case '=': /* sub-delims */
@@ -1467,7 +1513,6 @@ php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsign
        state->ptr = str;
        state->flags = flags;
        state->maxlen = maxlen;
-       TSRMLS_SET_CTX(state->ts);
 
        if (!(state->ptr = parse_authority(state))) {
                efree(state);
@@ -1475,7 +1520,7 @@ php_http_url_t *php_http_url_parse_authority(const char *str, size_t len, unsign
        }
 
        if (state->ptr != state->end) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING,
+               php_error_docref(NULL, E_WARNING,
                                "Failed to parse URL authority, unexpected character at pos %u in '%s'",
                                (unsigned) (state->ptr - str), str);
                efree(state);
@@ -1541,7 +1586,7 @@ ZEND_END_ARG_INFO();
 PHP_METHOD(HttpUrl, mod)
 {
        zval *new_url = NULL;
-       zend_long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY;
+       zend_long flags = PHP_HTTP_URL_JOIN_PATH | PHP_HTTP_URL_JOIN_QUERY | PHP_HTTP_URL_SANITIZE_PATH;
        zend_error_handling zeh;
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS(), "z!|l", &new_url, &flags), invalid_arg, return);
@@ -1656,7 +1701,7 @@ PHP_MINIT_FUNCTION(http_url)
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBLOC"), PHP_HTTP_URL_PARSE_MBLOC);
 #endif
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_MBUTF8"), PHP_HTTP_URL_PARSE_MBUTF8);
-#if defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII)
+#if defined(PHP_HTTP_HAVE_IDN2) || defined(PHP_HTTP_HAVE_IDN) || defined(HAVE_UIDNA_IDNTOASCII)
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOIDN"), PHP_HTTP_URL_PARSE_TOIDN);
 #endif
        zend_declare_class_constant_long(php_http_url_class_entry, ZEND_STRL("PARSE_TOPCT"), PHP_HTTP_URL_PARSE_TOPCT);