2 * Copyright 2008 Hans Leidekker for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/debug.h"
30 #include "winhttp_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(winhttp
);
34 static const WCHAR scheme_http
[] = {'h','t','t','p',0};
35 static const WCHAR scheme_https
[] = {'h','t','t','p','s',0};
37 static BOOL
set_component( WCHAR
**str
, DWORD
*str_len
, WCHAR
*value
, DWORD len
)
46 if (len
> (*str_len
) - 1)
49 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
52 memcpy( *str
, value
, len
* sizeof(WCHAR
) );
59 static BOOL
decode_url( LPCWSTR url
, LPWSTR buffer
, LPDWORD buflen
)
61 HRESULT hr
= UrlCanonicalizeW( url
, buffer
, buflen
, URL_WININET_COMPATIBILITY
| URL_UNESCAPE
);
62 if (hr
== E_POINTER
) set_last_error( ERROR_INSUFFICIENT_BUFFER
);
63 if (hr
== E_INVALIDARG
) set_last_error( ERROR_INVALID_PARAMETER
);
64 return (SUCCEEDED(hr
)) ? TRUE
: FALSE
;
67 /***********************************************************************
68 * WinHttpCrackUrl (winhttp.@)
70 BOOL WINAPI
WinHttpCrackUrl( LPCWSTR url
, DWORD len
, DWORD flags
, LPURL_COMPONENTSW uc
)
74 WCHAR
*url_decoded
= NULL
;
76 TRACE("%s, %d, %x, %p\n", debugstr_w(url
), len
, flags
, uc
);
78 if (flags
& ICU_ESCAPE
) FIXME("flag ICU_ESCAPE not supported\n");
80 if (!url
|| !url
[0] || !uc
|| uc
->dwStructSize
!= sizeof(URL_COMPONENTS
))
82 set_last_error( ERROR_INVALID_PARAMETER
);
85 if (!len
) len
= strlenW( url
);
87 if (flags
& ICU_DECODE
)
90 DWORD url_len
= len
+ 1;
92 if (!uc
->lpszScheme
|| !uc
->lpszUserName
|| !uc
->lpszPassword
||
93 !uc
->lpszHostName
|| !uc
->lpszUrlPath
|| !uc
-> lpszExtraInfo
)
95 set_last_error( ERROR_INVALID_PARAMETER
);
98 if (!(url_tmp
= HeapAlloc( GetProcessHeap(), 0, url_len
* sizeof(WCHAR
) )))
100 set_last_error( ERROR_OUTOFMEMORY
);
103 memcpy( url_tmp
, url
, len
* sizeof(WCHAR
) );
105 if (!(url_decoded
= HeapAlloc( GetProcessHeap(), 0, url_len
* sizeof(WCHAR
) )))
107 HeapFree( GetProcessHeap(), 0, url_tmp
);
108 set_last_error( ERROR_OUTOFMEMORY
);
111 if (decode_url( url_tmp
, url_decoded
, &url_len
))
116 HeapFree( GetProcessHeap(), 0, url_tmp
);
118 if (!(p
= strchrW( url
, ':' ))) return FALSE
;
120 if (p
- url
== 4 && !strncmpiW( url
, scheme_http
, 4 )) uc
->nScheme
= INTERNET_SCHEME_HTTP
;
121 else if (p
- url
== 5 && !strncmpiW( url
, scheme_https
, 5 )) uc
->nScheme
= INTERNET_SCHEME_HTTPS
;
124 if (!(set_component( &uc
->lpszScheme
, &uc
->dwSchemeLength
, (WCHAR
*)url
, p
- url
))) goto exit
;
127 if (!p
[0] || p
[0] != '/' || p
[1] != '/') goto exit
;
130 if (!p
[0]) goto exit
;
131 if ((q
= memchrW( p
, '@', len
- (p
- url
) )))
133 if ((r
= memchrW( p
, ':', q
- p
)))
135 if (!(set_component( &uc
->lpszUserName
, &uc
->dwUserNameLength
, p
, r
- p
))) goto exit
;
137 if (!(set_component( &uc
->lpszPassword
, &uc
->dwPasswordLength
, r
, q
- r
))) goto exit
;
141 if (!(set_component( &uc
->lpszUserName
, &uc
->dwUserNameLength
, p
, q
- p
))) goto exit
;
142 if (!(set_component( &uc
->lpszPassword
, &uc
->dwPasswordLength
, NULL
, 0 ))) goto exit
;
148 if (!(set_component( &uc
->lpszUserName
, &uc
->dwUserNameLength
, NULL
, 0 ))) goto exit
;
149 if (!(set_component( &uc
->lpszPassword
, &uc
->dwPasswordLength
, NULL
, 0 ))) goto exit
;
151 if ((q
= memchrW( p
, '/', len
- (p
- url
) )))
153 if (!(set_component( &uc
->lpszHostName
, &uc
->dwHostNameLength
, p
, q
- p
))) goto exit
;
155 if ((r
= memchrW( q
, '?', len
- (q
- url
) )))
157 if (!(set_component( &uc
->lpszUrlPath
, &uc
->dwUrlPathLength
, q
, r
- q
))) goto exit
;
158 if (!(set_component( &uc
->lpszExtraInfo
, &uc
->dwExtraInfoLength
, r
, len
- (r
- url
) ))) goto exit
;
162 if (!(set_component( &uc
->lpszUrlPath
, &uc
->dwUrlPathLength
, q
, len
- (q
- url
) ))) goto exit
;
163 if (!(set_component( &uc
->lpszExtraInfo
, &uc
->dwExtraInfoLength
, (WCHAR
*)url
+ len
, 0 ))) goto exit
;
168 if (!(set_component( &uc
->lpszHostName
, &uc
->dwHostNameLength
, p
, len
- (p
- url
) ))) goto exit
;
169 if (!(set_component( &uc
->lpszUrlPath
, &uc
->dwUrlPathLength
, (WCHAR
*)url
+ len
, 0 ))) goto exit
;
170 if (!(set_component( &uc
->lpszExtraInfo
, &uc
->dwExtraInfoLength
, (WCHAR
*)url
+ len
, 0 ))) goto exit
;
175 TRACE("scheme(%s) host(%s) path(%s) extra(%s)\n",
176 debugstr_wn( uc
->lpszScheme
, uc
->dwSchemeLength
),
177 debugstr_wn( uc
->lpszHostName
, uc
->dwHostNameLength
),
178 debugstr_wn( uc
->lpszUrlPath
, uc
->dwUrlPathLength
),
179 debugstr_wn( uc
->lpszExtraInfo
, uc
->dwExtraInfoLength
));
182 HeapFree( GetProcessHeap(), 0, url_decoded
);
186 static INTERNET_SCHEME
get_scheme( const WCHAR
*scheme
, DWORD len
)
188 if (!strncmpW( scheme
, scheme_http
, len
)) return INTERNET_SCHEME_HTTP
;
189 if (!strncmpW( scheme
, scheme_https
, len
)) return INTERNET_SCHEME_HTTPS
;
193 static const WCHAR
*get_scheme_string( INTERNET_SCHEME scheme
)
195 if (scheme
== INTERNET_SCHEME_HTTP
) return scheme_http
;
196 if (scheme
== INTERNET_SCHEME_HTTPS
) return scheme_https
;
200 static BOOL
uses_default_port( INTERNET_SCHEME scheme
, INTERNET_PORT port
)
202 if ((scheme
== INTERNET_SCHEME_HTTP
) && (port
== INTERNET_DEFAULT_HTTP_PORT
)) return TRUE
;
203 if ((scheme
== INTERNET_SCHEME_HTTPS
) && (port
== INTERNET_DEFAULT_HTTPS_PORT
)) return TRUE
;
207 static BOOL
need_escape( WCHAR c
)
209 if (isalnumW( c
)) return FALSE
;
211 if (c
<= 31 || c
>= 127) return TRUE
;
238 static DWORD
copy_escape( WCHAR
*dst
, const WCHAR
*src
, DWORD len
)
240 static const WCHAR hex
[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
245 for (i
= 0; i
< len
; i
++, p
++)
247 if (need_escape( src
[i
] ))
250 p
[1] = hex
[(src
[i
] >> 4) & 0xf];
251 p
[2] = hex
[src
[i
] & 0xf];
261 static DWORD
comp_length( DWORD len
, DWORD flags
, WCHAR
*comp
)
266 ret
= len
? len
: strlenW( comp
);
267 if (!(flags
& ICU_ESCAPE
)) return ret
;
268 for (i
= 0; i
< len
; i
++) if (need_escape( comp
[i
] )) ret
+= 2;
272 static BOOL
calc_length( URL_COMPONENTS
*uc
, DWORD flags
, LPDWORD len
)
274 static const WCHAR formatW
[] = {'%','d',0};
275 INTERNET_SCHEME scheme
;
280 DWORD scheme_len
= comp_length( uc
->dwSchemeLength
, 0, uc
->lpszScheme
);
282 scheme
= get_scheme( uc
->lpszScheme
, scheme_len
);
286 scheme
= uc
->nScheme
;
287 if (!scheme
) scheme
= INTERNET_SCHEME_HTTP
;
288 *len
+= strlenW( get_scheme_string( scheme
) );
291 if (uc
->lpszHostName
) *len
+= 2; /* "//" */
293 if (uc
->lpszUserName
)
295 *len
+= comp_length( uc
->dwUserNameLength
, 0, uc
->lpszUserName
);
300 if (uc
->lpszPassword
)
302 set_last_error( ERROR_INVALID_PARAMETER
);
306 if (uc
->lpszPassword
)
309 *len
+= comp_length( uc
->dwPasswordLength
, 0, uc
->lpszPassword
);
311 if (uc
->lpszHostName
)
313 *len
+= comp_length( uc
->dwHostNameLength
, 0, uc
->lpszHostName
);
315 if (!uses_default_port( scheme
, uc
->nPort
))
317 WCHAR port
[sizeof("65535")];
319 sprintfW( port
, formatW
, uc
->nPort
);
320 *len
+= strlenW( port
);
323 if (uc
->lpszUrlPath
&& *uc
->lpszUrlPath
!= '/') *len
+= 1; /* '/' */
325 if (uc
->lpszUrlPath
) *len
+= comp_length( uc
->dwUrlPathLength
, flags
, uc
->lpszUrlPath
);
326 if (uc
->lpszExtraInfo
) *len
+= comp_length( uc
->dwExtraInfoLength
, flags
, uc
->lpszExtraInfo
);
330 /***********************************************************************
331 * WinHttpCreateUrl (winhttp.@)
333 BOOL WINAPI
WinHttpCreateUrl( LPURL_COMPONENTS uc
, DWORD flags
, LPWSTR url
, LPDWORD required
)
335 static const WCHAR formatW
[] = {'%','d',0};
336 static const WCHAR twoslashW
[] = {'/','/'};
339 INTERNET_SCHEME scheme
;
341 TRACE("%p, 0x%08x, %p, %p\n", uc
, flags
, url
, required
);
343 if (!uc
|| uc
->dwStructSize
!= sizeof(URL_COMPONENTS
) || !required
)
345 set_last_error( ERROR_INVALID_PARAMETER
);
349 if (!calc_length( uc
, flags
, &len
)) return FALSE
;
351 if (!url
|| *required
< len
)
354 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
362 len
= comp_length( uc
->dwSchemeLength
, 0, uc
->lpszScheme
);
363 memcpy( url
, uc
->lpszScheme
, len
* sizeof(WCHAR
) );
366 scheme
= get_scheme( uc
->lpszScheme
, len
);
370 const WCHAR
*schemeW
;
371 scheme
= uc
->nScheme
;
373 if (!scheme
) scheme
= INTERNET_SCHEME_HTTP
;
375 schemeW
= get_scheme_string( scheme
);
376 len
= strlenW( schemeW
);
377 memcpy( url
, schemeW
, len
* sizeof(WCHAR
) );
381 /* all schemes are followed by at least a colon */
385 if (uc
->lpszHostName
)
387 memcpy( url
, twoslashW
, sizeof(twoslashW
) );
388 url
+= sizeof(twoslashW
) / sizeof(twoslashW
[0]);
390 if (uc
->lpszUserName
)
392 len
= comp_length( uc
->dwUserNameLength
, 0, uc
->lpszUserName
);
393 memcpy( url
, uc
->lpszUserName
, len
* sizeof(WCHAR
) );
396 if (uc
->lpszPassword
)
401 len
= comp_length( uc
->dwPasswordLength
, 0, uc
->lpszPassword
);
402 memcpy( url
, uc
->lpszPassword
, len
* sizeof(WCHAR
) );
408 if (uc
->lpszHostName
)
410 len
= comp_length( uc
->dwHostNameLength
, 0, uc
->lpszHostName
);
411 memcpy( url
, uc
->lpszHostName
, len
* sizeof(WCHAR
) );
414 if (!uses_default_port( scheme
, uc
->nPort
))
416 WCHAR port
[sizeof("65535")];
418 sprintfW( port
, formatW
, uc
->nPort
);
422 len
= strlenW( port
);
423 memcpy( url
, port
, len
* sizeof(WCHAR
) );
427 /* add slash between hostname and path if necessary */
428 if (uc
->lpszUrlPath
&& *uc
->lpszUrlPath
!= '/')
436 len
= comp_length( uc
->dwUrlPathLength
, 0, uc
->lpszUrlPath
);
437 if (flags
& ICU_ESCAPE
) url
+= copy_escape( url
, uc
->lpszUrlPath
, len
);
440 memcpy( url
, uc
->lpszUrlPath
, len
* sizeof(WCHAR
) );
444 if (uc
->lpszExtraInfo
)
446 len
= comp_length( uc
->dwExtraInfoLength
, 0, uc
->lpszExtraInfo
);
447 if (flags
& ICU_ESCAPE
) url
+= copy_escape( url
, uc
->lpszExtraInfo
, len
);
450 memcpy( url
, uc
->lpszExtraInfo
, len
* sizeof(WCHAR
) );