From 7e5b5cb1aa253ce2f20a790989d0f18bcbcab946 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 6 Nov 2018 16:09:04 +0100 Subject: [PATCH] winhttp: Properly handle request paths with Unicode characters. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46076 Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/winhttp/request.c | 202 ++++++++++++++++++++++++++++++--- dlls/winhttp/session.c | 49 ++------ 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, 314 insertions(+), 103 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 0409c3c615b..77d5315862b 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 d5896e1137f..93c10e06795 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; + int len = object ? strlenW(object) : 0; + WCHAR *p, *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 ); - } + 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 1980ad4da2c..0a31e181c2d 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 e13f95057bd..70d0b4c7471 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 2019305ab3c..f66e5105ab6 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 e50e5fad809..cbf9a365993 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;