[PATCH 7/7] winhttp: Properly handle request paths with Unicode characters.

Hans Leidekker hans at codeweavers.com
Tue Nov 6 09:09:04 CST 2018


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46076
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/winhttp/request.c         | 202 +++++++++++++++++++++++++++++++++++++----
 dlls/winhttp/session.c         |  51 ++---------
 dlls/winhttp/tests/url.c       |  62 +++++++++++++
 dlls/winhttp/tests/winhttp.c   |  21 ++++-
 dlls/winhttp/url.c             |  74 ++++++++-------
 dlls/winhttp/winhttp_private.h |   9 --
 6 files changed, 315 insertions(+), 104 deletions(-)

diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 0409c3c615..77d5315862 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -826,7 +826,6 @@ BOOL WINAPI WinHttpQueryHeaders( HINTERNET hrequest, DWORD level, LPCWSTR name,
     return ret;
 }
 
-
 static const WCHAR basicW[]     = {'B','a','s','i','c',0};
 static const WCHAR ntlmW[]      = {'N','T','L','M',0};
 static const WCHAR passportW[]  = {'P','a','s','s','p','o','r','t',0};
@@ -2046,6 +2045,176 @@ static void drain_content( request_t *request )
     }
 }
 
+enum escape_flags
+{
+    ESCAPE_FLAG_NON_PRINTABLE = 0x01,
+    ESCAPE_FLAG_SPACE         = 0x02,
+    ESCAPE_FLAG_PERCENT       = 0x04,
+    ESCAPE_FLAG_UNSAFE        = 0x08,
+    ESCAPE_FLAG_DEL           = 0x10,
+    ESCAPE_FLAG_8BIT          = 0x20,
+    ESCAPE_FLAG_STRIP_CRLF    = 0x40,
+};
+
+#define ESCAPE_MASK_DEFAULT (ESCAPE_FLAG_NON_PRINTABLE | ESCAPE_FLAG_SPACE | ESCAPE_FLAG_UNSAFE |\
+                             ESCAPE_FLAG_DEL | ESCAPE_FLAG_8BIT)
+#define ESCAPE_MASK_PERCENT (ESCAPE_FLAG_PERCENT | ESCAPE_MASK_DEFAULT)
+#define ESCAPE_MASK_DISABLE (ESCAPE_FLAG_SPACE | ESCAPE_FLAG_8BIT | ESCAPE_FLAG_STRIP_CRLF)
+
+static inline BOOL need_escape( char ch, enum escape_flags flags )
+{
+    static const char unsafe[] = "\"#<>[\\]^`{|}";
+    const char *ptr = unsafe;
+
+    if ((flags & ESCAPE_FLAG_SPACE) && ch == ' ') return TRUE;
+    if ((flags & ESCAPE_FLAG_PERCENT) && ch == '%') return TRUE;
+    if ((flags & ESCAPE_FLAG_NON_PRINTABLE) && ch < 0x20) return TRUE;
+    if ((flags & ESCAPE_FLAG_DEL) && ch == 0x7f) return TRUE;
+    if ((flags & ESCAPE_FLAG_8BIT) && (ch & 0x80)) return TRUE;
+    if ((flags & ESCAPE_FLAG_UNSAFE)) while (*ptr) { if (ch == *ptr++) return TRUE; }
+    return FALSE;
+}
+
+static DWORD escape_string( const char *src, DWORD len, char *dst, enum escape_flags flags )
+{
+    static const char hex[] = "0123456789ABCDEF";
+    DWORD i, ret = len;
+    char *ptr = dst;
+
+    for (i = 0; i < len; i++)
+    {
+        if ((flags & ESCAPE_FLAG_STRIP_CRLF) && (src[i] == '\r' || src[i] == '\n'))
+        {
+            ret--;
+            continue;
+        }
+        if (need_escape( src[i], flags ))
+        {
+            if (dst)
+            {
+                ptr[0] = '%';
+                ptr[1] = hex[(src[i] >> 4) & 0xf];
+                ptr[2] = hex[src[i] & 0xf];
+                ptr += 3;
+            }
+            ret += 2;
+        }
+        else if (dst) *ptr++ = src[i];
+    }
+
+    if (dst) dst[ret] = 0;
+    return ret;
+}
+
+static DWORD str_to_wire( const WCHAR *src, int src_len, char *dst, enum escape_flags flags )
+{
+    DWORD len;
+    char *utf8;
+
+    if (src_len < 0) src_len = strlenW( src );
+    len = WideCharToMultiByte( CP_UTF8, 0, src, src_len, NULL, 0, NULL, NULL );
+    if (!(utf8 = heap_alloc( len ))) return 0;
+
+    WideCharToMultiByte( CP_UTF8, 0, src, -1, utf8, len, NULL, NULL );
+    len = escape_string( utf8, len, dst, flags );
+    heap_free( utf8 );
+
+    return len;
+}
+
+static char *build_wire_path( request_t *request, DWORD *ret_len )
+{
+    WCHAR *full_path;
+    const WCHAR *path, *query = NULL;
+    DWORD len, len_path = 0, len_query = 0;
+    enum escape_flags path_flags, query_flags;
+    char *ret;
+
+    if (!strcmpiW( request->connect->hostname, request->connect->servername )) full_path = request->path;
+    else if (!(full_path = build_absolute_request_path( request ))) return NULL;
+
+    len = strlenW( full_path );
+    if ((path = strchrW( full_path, '/' )))
+    {
+        len_path = strlenW( path );
+        if ((query = strchrW( path, '?' )))
+        {
+            len_query = strlenW( query );
+            len_path -= len_query;
+        }
+    }
+
+    if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_MASK_DISABLE;
+    else if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_MASK_PERCENT;
+    else path_flags = ESCAPE_MASK_DEFAULT;
+
+    if (request->hdr.flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_MASK_DISABLE;
+    else query_flags = path_flags;
+
+    *ret_len = str_to_wire( full_path, len - len_path - len_query, NULL, 0 );
+    if (path) *ret_len += str_to_wire( path, len_path, NULL, path_flags );
+    if (query) *ret_len += str_to_wire( query, len_query, NULL, query_flags );
+
+    if ((ret = heap_alloc( *ret_len + 1 )))
+    {
+        len = str_to_wire( full_path, len - len_path - len_query, ret, 0 );
+        if (path) len += str_to_wire( path, len_path, ret + len, path_flags );
+        if (query) str_to_wire( query, len_query, ret + len, query_flags );
+    }
+
+    if (full_path != request->path) heap_free( full_path );
+    return ret;
+}
+
+static char *build_wire_request( request_t *request, DWORD *len )
+{
+    char *path, *ptr, *ret;
+    DWORD i, len_path;
+
+    if (!(path = build_wire_path( request, &len_path ))) return NULL;
+
+    *len = str_to_wire( request->verb, -1, NULL, 0 ) + 1; /* ' ' */
+    *len += len_path + 1; /* ' ' */
+    *len += str_to_wire( request->version, -1, NULL, 0 );
+
+    for (i = 0; i < request->num_headers; i++)
+    {
+        if (request->headers[i].is_request)
+        {
+            *len += str_to_wire( request->headers[i].field, -1, NULL, 0 ) + 2; /* ': ' */
+            *len += str_to_wire( request->headers[i].value, -1, NULL, 0 ) + 2; /* '\r\n' */
+        }
+    }
+    *len += 4; /* '\r\n\r\n' */
+
+    if ((ret = ptr = heap_alloc( *len + 1 )))
+    {
+        ptr += str_to_wire( request->verb, -1, ptr, 0 );
+        *ptr++ = ' ';
+        memcpy( ptr, path, len_path );
+        ptr += len_path;
+        *ptr++ = ' ';
+        ptr += str_to_wire( request->version, -1, ptr, 0 );
+
+        for (i = 0; i < request->num_headers; i++)
+        {
+            if (request->headers[i].is_request)
+            {
+                *ptr++ = '\r';
+                *ptr++ = '\n';
+                ptr += str_to_wire( request->headers[i].field, -1, ptr, 0 );
+                *ptr++ = ':';
+                *ptr++ = ' ';
+                ptr += str_to_wire( request->headers[i].value, -1, ptr, 0 );
+            }
+        }
+        memcpy( ptr, "\r\n\r\n", sizeof("\r\n\r\n") );
+    }
+
+    heap_free( path );
+    return ret;
+}
+
 static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
                           DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
 {
@@ -2056,8 +2225,7 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
     BOOL ret = FALSE;
     connect_t *connect = request->connect;
     session_t *session = connect->session;
-    WCHAR *req = NULL;
-    char *req_ascii;
+    char *wire_req;
     int bytes_sent;
     DWORD len, i, flags;
 
@@ -2107,16 +2275,13 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
     if (context) request->hdr.context = context;
 
     if (!(ret = open_connection( request ))) goto end;
-    if (!(req = build_request_string( request ))) goto end;
-
-    if (!(req_ascii = strdupWA( req ))) goto end;
-    TRACE("full request: %s\n", debugstr_a(req_ascii));
-    len = strlen(req_ascii);
+    if (!(wire_req = build_wire_request( request, &len ))) goto end;
+    TRACE("full request: %s\n", debugstr_a(wire_req));
 
     send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 );
 
-    ret = netconn_send( request->netconn, req_ascii, len, &bytes_sent );
-    heap_free( req_ascii );
+    ret = netconn_send( request->netconn, wire_req, len, &bytes_sent );
+    heap_free( wire_req );
     if (!ret) goto end;
 
     if (optional_len)
@@ -2140,7 +2305,6 @@ end:
             send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) );
         }
     }
-    heap_free( req );
     return ret;
 }
 
@@ -2524,18 +2688,19 @@ static BOOL handle_redirect( request_t *request, DWORD status )
 
         if (location[0] == '/')
         {
-            len = escape_string( NULL, location, len_loc, 0 );
-            if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
-            escape_string( path, location, len_loc, 0 );
+            if (!(path = heap_alloc( (len_loc + 1) * sizeof(WCHAR) ))) goto end;
+            memcpy( path, location, len_loc * sizeof(WCHAR) );
+            path[len_loc] = 0;
         }
         else
         {
             if ((p = strrchrW( request->path, '/' ))) *p = 0;
-            len = strlenW( request->path ) + 1 + escape_string( NULL, location, len_loc, 0 );
+            len = strlenW( request->path ) + 1 + len_loc;
             if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
             strcpyW( path, request->path );
             strcatW( path, slashW );
-            escape_string( path + strlenW(path), location, len_loc, 0 );
+            memcpy( path + strlenW(path), location, len_loc * sizeof(WCHAR) );
+            path[len_loc] = 0;
         }
         heap_free( request->path );
         request->path = path;
@@ -2586,9 +2751,10 @@ static BOOL handle_redirect( request_t *request, DWORD status )
         request->path = NULL;
         if (uc.dwUrlPathLength)
         {
-            len = escape_string( NULL, uc.lpszUrlPath, uc.dwUrlPathLength + uc.dwExtraInfoLength, 0 );
+            len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
             if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
-            escape_string( request->path, uc.lpszUrlPath, uc.dwUrlPathLength + uc.dwExtraInfoLength, 0 );
+            memcpy( request->path, uc.lpszUrlPath, (len + 1) * sizeof(WCHAR) );
+            request->path[len] = 0;
         }
         else request->path = strdupW( slashW );
     }
diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c
index d5896e1137..93c10e0679 100644
--- a/dlls/winhttp/session.c
+++ b/dlls/winhttp/session.c
@@ -1090,47 +1090,16 @@ static BOOL store_accept_types( request_t *request, const WCHAR **accept_types )
     return TRUE;
 }
 
-static WCHAR *get_request_path( const WCHAR *object, DWORD flags )
+static WCHAR *get_request_path( const WCHAR *object )
 {
-    DWORD len, path_len = 0, query_len = 0;
-    const WCHAR *p = object, *q = NULL;
-    enum escape_flags path_flags, query_flags;
-    WCHAR *ret;
-
-    if (flags & WINHTTP_FLAG_ESCAPE_DISABLE) path_flags = ESCAPE_FLAG_SPACE_ONLY|ESCAPE_FLAG_REMOVE_CRLF;
-    else if (flags & WINHTTP_FLAG_ESCAPE_PERCENT) path_flags = ESCAPE_FLAG_PERCENT;
-    else path_flags = 0;
-
-    if (flags & WINHTTP_FLAG_ESCAPE_DISABLE_QUERY) query_flags = ESCAPE_FLAG_SPACE_ONLY|ESCAPE_FLAG_REMOVE_CRLF;
-    else query_flags = path_flags;
-
-    if (object)
-    {
-        path_len = strlenW( object );
-        if (object[0] == '/')
-        {
-            path_len--;
-            p++;
-        }
-        if ((q = strchrW( p, '?' )))
-        {
-            q++;
-            query_len = path_len - (q - p);
-            path_len -= query_len + 1;
-        }
-    }
-
-    len = escape_string( NULL, p, path_len, path_flags );
-    len += escape_string( NULL, q, query_len, query_flags );
-    if (!(ret = heap_alloc( (len + 3) * sizeof(WCHAR) ))) return NULL;
-
-    ret[0] = '/';
-    len = escape_string( ret + 1, p, path_len, path_flags ) + 1;
-    if (q)
-    {
-        ret[len] = '?';
-        escape_string( ret + 1 + len, q, query_len, query_flags );
-    }
+    int len = object ? strlenW(object) : 0;
+    WCHAR *p, *ret;
+
+    if (!object || object[0] != '/') len++;
+    if (!(p = ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
+    if (!object || object[0] != '/') *p++ = '/';
+    if (object) strcpyW( p, object );
+    ret[len] = 0;
     return ret;
 }
 
@@ -1193,7 +1162,7 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o
 
     if (!verb || !verb[0]) verb = getW;
     if (!(request->verb = strdupW( verb ))) goto end;
-    if (!(request->path = get_request_path( object, flags ))) goto end;
+    if (!(request->path = get_request_path( object ))) goto end;
 
     if (!version || !version[0]) version = http1_1;
     if (!(request->version = strdupW( version ))) goto end;
diff --git a/dlls/winhttp/tests/url.c b/dlls/winhttp/tests/url.c
index 1980ad4da2..0a31e181c2 100644
--- a/dlls/winhttp/tests/url.c
+++ b/dlls/winhttp/tests/url.c
@@ -35,6 +35,8 @@ static WCHAR about[]    = {'/','s','i','t','e','/','a','b','o','u','t',0};
 static WCHAR query[]    = {'?','q','u','e','r','y',0};
 static WCHAR escape[]   = {' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/',':',';','<','=','>','?','@','[','\\',']','^','_','`','{','|','}','~',0};
 static WCHAR escape2[]  = {'\r',0x1f,' ','\n',0x7f,'\r','\n',0};
+static WCHAR escape3[]  = {'?','t','e','x','t','=',0xfb00,0};
+static WCHAR escape4[]  = {'/','t','e','x','t','=',0xfb00,0};
 
 static const WCHAR url1[]  =
     {'h','t','t','p',':','/','/','u','s','e','r','n','a','m','e',':','p','a','s','s','w','o','r','d',
@@ -75,6 +77,10 @@ static const WCHAR url16[] = {'h','t','t','p',':','/','/','w','i','n','e','h','q
 static const WCHAR url17[] = {'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',':',0};
 static const WCHAR url18[] =
     {'h','t','t','p',':','/','/','%','0','D','%','1','F','%','2','0','%','0','A','%','7','F','%','0','D','%','0','A',0};
+static const WCHAR url19[] =
+    {'h','t','t','p',':','/','/','?','t','e','x','t','=',0xfb00,0};
+static const WCHAR url20[] =
+    {'h','t','t','p',':','/','/','/','t','e','x','t','=',0xfb00,0};
 
 static const WCHAR url_k1[]  =
     {'h','t','t','p',':','/','/','u','s','e','r','n','a','m','e',':','p','a','s','s','w','o','r','d',
@@ -318,6 +324,34 @@ static void WinHttpCreateUrl_test( void )
     ok( len == lstrlenW(url18), "expected len %u got %u\n", lstrlenW(url18), len );
     ok( !lstrcmpW( url, url18 ), "url doesn't match\n" );
 
+    /* extra info with Unicode characters */
+    memset( &uc, 0, sizeof(uc) );
+    uc.dwStructSize = sizeof(uc);
+    uc.lpszExtraInfo = escape3;
+    uc.dwExtraInfoLength = lstrlenW( uc.lpszExtraInfo );
+    url[0] = 0;
+    len = 256;
+    SetLastError( 0xdeadbeef );
+    ret = WinHttpCreateUrl( &uc, ICU_ESCAPE, url, &len );
+    err = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( err == ERROR_INVALID_PARAMETER, "got %u\n", err );
+
+    /* extra info with Unicode characters, no ICU_ESCAPE */
+    memset( &uc, 0, sizeof(uc) );
+    uc.dwStructSize = sizeof(uc);
+    uc.lpszExtraInfo = escape3;
+    uc.dwExtraInfoLength = lstrlenW( uc.lpszExtraInfo );
+    url[0] = 0;
+    len = 256;
+    ret = WinHttpCreateUrl( &uc, 0, url, &len );
+    ok( ret || broken(!ret) /* < win7 */, "expected success\n" );
+    if (ret)
+    {
+        ok( len == lstrlenW(url19), "expected len %u got %u\n", lstrlenW(url19), len );
+        ok( !lstrcmpW( url, url19 ), "url doesn't match %s\n", wine_dbgstr_w(url) );
+    }
+
     /* escape path */
     memset( &uc, 0, sizeof(uc) );
     uc.dwStructSize = sizeof(uc);
@@ -330,6 +364,34 @@ static void WinHttpCreateUrl_test( void )
     ok( len == lstrlenW(url18), "expected len %u got %u\n", lstrlenW(url18), len );
     ok( !lstrcmpW( url, url18 ), "url doesn't match\n" );
 
+    /* path with Unicode characters */
+    memset( &uc, 0, sizeof(uc) );
+    uc.dwStructSize = sizeof(uc);
+    uc.lpszUrlPath = escape4;
+    uc.dwUrlPathLength = lstrlenW( uc.lpszUrlPath );
+    url[0] = 0;
+    len = 256;
+    SetLastError( 0xdeadbeef );
+    ret = WinHttpCreateUrl( &uc, ICU_ESCAPE, url, &len );
+    err = GetLastError();
+    ok( !ret, "expected failure\n" );
+    ok( err == ERROR_INVALID_PARAMETER, "got %u\n", err );
+
+    /* path with Unicode characters, no ICU_ESCAPE */
+    memset( &uc, 0, sizeof(uc) );
+    uc.dwStructSize = sizeof(uc);
+    uc.lpszUrlPath = escape4;
+    uc.dwUrlPathLength = lstrlenW( uc.lpszUrlPath );
+    url[0] = 0;
+    len = 256;
+    ret = WinHttpCreateUrl( &uc, 0, url, &len );
+    ok( ret || broken(!ret) /* < win7 */, "expected success\n" );
+    if (ret)
+    {
+        ok( len == lstrlenW(url20), "expected len %u got %u\n", lstrlenW(url20), len );
+        ok( !lstrcmpW( url, url20 ), "url doesn't match %s\n", wine_dbgstr_w(url) );
+    }
+
     /* NULL lpszScheme, 0 nScheme and nPort */
     fill_url_components( &uc );
     uc.lpszScheme = NULL;
diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c
index e13f95057b..70d0b4c747 100644
--- a/dlls/winhttp/tests/winhttp.c
+++ b/dlls/winhttp/tests/winhttp.c
@@ -2368,9 +2368,16 @@ static DWORD CALLBACK server_thread(LPVOID param)
                                        "%5E_%60%7B%7C%7D~%0D%0A ";
             static const char res3[] = "\x1f\x7f<%20%three?\x1f\x7f%20!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
             static const char res4[] = "%0D%0A%1F%7F%3C%20%four?\x1f\x7f%20!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ";
+            static const char res5[] = "&text=one%C2%80%7F~";
+            static const char res6[] = "&text=two%C2%80\x7f~";
+            static const char res7[] = "&text=%E5%90%9B%E3%81%AE%E5%90%8D%E3%81%AF";
 
             if (strstr(buffer + 11, res) || strstr(buffer + 11, res2) || strstr(buffer + 11, res3) ||
-                strstr(buffer + 11, res4 )) send(c, okmsg, sizeof(okmsg) - 1, 0);
+                strstr(buffer + 11, res4) || strstr(buffer + 11, res5) || strstr(buffer + 11, res6) ||
+                strstr(buffer + 11, res7))
+            {
+                send(c, okmsg, sizeof(okmsg) - 1, 0);
+            }
             else send(c, notokmsg, sizeof(notokmsg) - 1, 0);
         }
         if (strstr(buffer, "GET /quit"))
@@ -3367,7 +3374,8 @@ static void do_request( HINTERNET con, const WCHAR *obj, DWORD flags )
     size = sizeof(status);
     ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL );
     ok( ret, "failed to query status code %u\n", GetLastError() );
-    ok( status == HTTP_STATUS_OK, "request %s with flags %08x failed %u\n", wine_dbgstr_w(obj), flags, status );
+    ok( status == HTTP_STATUS_OK || broken(status == HTTP_STATUS_BAD_REQUEST) /* < win7 */,
+        "request %s with flags %08x failed %u\n", wine_dbgstr_w(obj), flags, status );
     WinHttpCloseHandle( req );
 }
 
@@ -3389,6 +3397,12 @@ static void test_request_path_escapes( int port )
         {'/','e','s','c','a','p','e','\r','\n',0x1f,0x7f,'<',' ','%','f','o','u','r','?',0x1f,0x7f,' ','!','"',
          '#','$','%','&','\'','(',')','*','+',',','-','.','/',':',';','<','=','>','?','@','[','\\',']','^','_',
          '`','{','|','}','~','\r','\n',0};
+    static const WCHAR obj5W[] =
+        {'/','e','s','c','a','p','e','&','t','e','x','t','=','o','n','e',0x80,0x7f,0x7e,0};
+    static const WCHAR obj6W[] =
+        {'/','e','s','c','a','p','e','&','t','e','x','t','=','t','w','o',0x80,0x7f,0x7e,0};
+    static const WCHAR obj7W[] =
+        {'/','e','s','c','a','p','e','&','t','e','x','t','=',0x541b,0x306e,0x540d,0x306f,0};
     HINTERNET ses, con;
 
     ses = WinHttpOpen( test_useragent, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0 );
@@ -3401,6 +3415,9 @@ static void test_request_path_escapes( int port )
     do_request( con, obj2W, WINHTTP_FLAG_ESCAPE_PERCENT );
     do_request( con, obj3W, WINHTTP_FLAG_ESCAPE_DISABLE );
     do_request( con, obj4W, WINHTTP_FLAG_ESCAPE_DISABLE_QUERY );
+    do_request( con, obj5W, 0 );
+    do_request( con, obj6W, WINHTTP_FLAG_ESCAPE_DISABLE );
+    do_request( con, obj7W, WINHTTP_FLAG_ESCAPE_DISABLE );
 
     WinHttpCloseHandle( con );
     WinHttpCloseHandle( ses );
diff --git a/dlls/winhttp/url.c b/dlls/winhttp/url.c
index 2019305ab3..f66e5105ab 100644
--- a/dlls/winhttp/url.c
+++ b/dlls/winhttp/url.c
@@ -87,16 +87,11 @@ static WCHAR *decode_url( LPCWSTR url, DWORD *len )
     return ret;
 }
 
-
-static BOOL need_escape( WCHAR ch, enum escape_flags flags )
+static inline BOOL need_escape( WCHAR ch )
 {
-    static const WCHAR escapes[] = {' ','"','#','<','>','[','\\',']','^','`','{','|','}',0};
+    static const WCHAR escapes[] = {' ','"','#','%','<','>','[','\\',']','^','`','{','|','}','~',0};
     const WCHAR *p = escapes;
 
-    if (ch != ' ' && (flags & ESCAPE_FLAG_SPACE_ONLY)) return FALSE;
-    if (ch == '%' && (flags & ESCAPE_FLAG_PERCENT)) return TRUE;
-    if (ch == '~' && (flags & ESCAPE_FLAG_TILDE)) return TRUE;
-
     if (ch <= 31 || ch >= 127) return TRUE;
     while (*p)
     {
@@ -105,21 +100,17 @@ static BOOL need_escape( WCHAR ch, enum escape_flags flags )
     return FALSE;
 }
 
-DWORD escape_string( WCHAR *dst, const WCHAR *src, DWORD len, enum escape_flags flags )
+static BOOL escape_string( const WCHAR *src, DWORD src_len, WCHAR *dst, DWORD *dst_len )
 {
     static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
-    DWORD ret = len;
-    unsigned int i;
     WCHAR *p = dst;
+    DWORD i;
 
-    for (i = 0; i < len; i++)
+    *dst_len = src_len;
+    for (i = 0; i < src_len; i++)
     {
-        if ((flags & ESCAPE_FLAG_REMOVE_CRLF) && (src[i] == '\r' || src[i] == '\n'))
-        {
-            ret--;
-            continue;
-        }
-        if (need_escape( src[i], flags ))
+        if (src[i] > 0xff) return FALSE;
+        if (need_escape( src[i] ))
         {
             if (dst)
             {
@@ -128,25 +119,24 @@ DWORD escape_string( WCHAR *dst, const WCHAR *src, DWORD len, enum escape_flags
                 p[2] = hex[src[i] & 0xf];
                 p += 3;
             }
-            ret += 2;
+            *dst_len += 2;
         }
         else if (dst) *p++ = src[i];
     }
 
-    if (dst) dst[ret] = 0;
-    return ret;
+    if (dst) dst[*dst_len] = 0;
+    return TRUE;
 }
 
-static WCHAR *escape_url( const WCHAR *url, DWORD *len )
+static DWORD escape_url( const WCHAR *url, DWORD *len, WCHAR **ret )
 {
-    WCHAR *ret;
     const WCHAR *p;
     DWORD len_base, len_path;
 
     if ((p = strrchrW( url, '/' )))
     {
         len_base = p - url;
-        len_path = escape_string( NULL, p, *len - len_base, ESCAPE_FLAG_PERCENT|ESCAPE_FLAG_TILDE );
+        if (!escape_string( p, *len - len_base, NULL, &len_path )) return ERROR_INVALID_PARAMETER;
     }
     else
     {
@@ -154,14 +144,14 @@ static WCHAR *escape_url( const WCHAR *url, DWORD *len )
         len_path = 0;
     }
 
-    if (!(ret = heap_alloc( (len_base + len_path + 1) * sizeof(WCHAR) ))) return NULL;
-    memcpy( ret, url, len_base * sizeof(WCHAR) );
+    if (!(*ret = heap_alloc( (len_base + len_path + 1) * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
+    memcpy( *ret, url, len_base * sizeof(WCHAR) );
 
-    if (p) escape_string( ret + len_base, p, *len - (p - url), ESCAPE_FLAG_PERCENT|ESCAPE_FLAG_TILDE );
-    ret[len_base + len_path] = 0;
+    if (p) escape_string( p, *len - (p - url), *ret + len_base, &len_path );
+    (*ret)[len_base + len_path] = 0;
 
     *len = len_base + len_path;
-    return ret;
+    return ERROR_SUCCESS;
 }
 
 static DWORD parse_port( const WCHAR *str, DWORD len, INTERNET_PORT *ret )
@@ -198,9 +188,9 @@ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONEN
 
     if (flags & ICU_ESCAPE)
     {
-        if (!(url_escaped = escape_url( url, &len )))
+        if ((err = escape_url( url, &len, &url_escaped )))
         {
-            set_last_error( ERROR_OUTOFMEMORY );
+            set_last_error( err );
             return FALSE;
         }
         url = url_escaped;
@@ -349,7 +339,7 @@ static DWORD get_comp_length( DWORD len, DWORD flags, WCHAR *comp )
 
     ret = len ? len : strlenW( comp );
     if (!(flags & ICU_ESCAPE)) return ret;
-    for (i = 0; i < len; i++) if (need_escape( comp[i], ESCAPE_FLAG_PERCENT|ESCAPE_FLAG_TILDE )) ret += 2;
+    for (i = 0; i < len; i++) if (need_escape( comp[i] )) ret += 2;
     return ret;
 }
 
@@ -415,7 +405,7 @@ static BOOL get_url_length( URL_COMPONENTS *uc, DWORD flags, DWORD *len )
 BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDWORD required )
 {
     static const WCHAR formatW[] = {'%','u',0};
-    DWORD len;
+    DWORD len, len_escaped;
     INTERNET_SCHEME scheme;
 
     TRACE("%p, 0x%08x, %p, %p\n", uc, flags, url, required);
@@ -503,7 +493,15 @@ BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDW
     if (uc->lpszUrlPath)
     {
         len = get_comp_length( uc->dwUrlPathLength, 0, uc->lpszUrlPath );
-        if (flags & ICU_ESCAPE) url += escape_string( url, uc->lpszUrlPath, len, ESCAPE_FLAG_PERCENT|ESCAPE_FLAG_TILDE );
+        if (flags & ICU_ESCAPE)
+        {
+            if (!escape_string( uc->lpszUrlPath, len, url, &len_escaped ))
+            {
+                set_last_error( ERROR_INVALID_PARAMETER );
+                return FALSE;
+            }
+            url += len_escaped;
+        }
         else
         {
             memcpy( url, uc->lpszUrlPath, len * sizeof(WCHAR) );
@@ -513,7 +511,15 @@ BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDW
     if (uc->lpszExtraInfo)
     {
         len = get_comp_length( uc->dwExtraInfoLength, 0, uc->lpszExtraInfo );
-        if (flags & ICU_ESCAPE) url += escape_string( url, uc->lpszExtraInfo, len, ESCAPE_FLAG_PERCENT|ESCAPE_FLAG_TILDE );
+        if (flags & ICU_ESCAPE)
+        {
+            if (!escape_string( uc->lpszExtraInfo, len, url, &len_escaped ))
+            {
+                set_last_error( ERROR_INVALID_PARAMETER );
+                return FALSE;
+            }
+            url += len_escaped;
+        }
         else
         {
             memcpy( url, uc->lpszExtraInfo, len * sizeof(WCHAR) );
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index e50e5fad80..cbf9a36599 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -292,15 +292,6 @@ void destroy_authinfo( struct authinfo * ) DECLSPEC_HIDDEN;
 
 void release_host( hostdata_t *host ) DECLSPEC_HIDDEN;
 
-enum escape_flags
-{
-    ESCAPE_FLAG_REMOVE_CRLF = 0x01,
-    ESCAPE_FLAG_SPACE_ONLY  = 0x02,
-    ESCAPE_FLAG_PERCENT     = 0x04,
-    ESCAPE_FLAG_TILDE       = 0x08,
-};
-DWORD escape_string( WCHAR *, const WCHAR *, DWORD, enum escape_flags ) DECLSPEC_HIDDEN;
-
 extern HRESULT WinHttpRequest_create( void ** ) DECLSPEC_HIDDEN;
 void release_typelib( void ) DECLSPEC_HIDDEN;
 
-- 
2.11.0




More information about the wine-devel mailing list