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 DWORD
set_component( WCHAR
**str
, DWORD
*str_len
, WCHAR
*value
, DWORD len
, DWORD flags
, BOOL
*overflow
)
39 if (*str
&& !*str_len
) return ERROR_INVALID_PARAMETER
;
40 if (!*str_len
) return ERROR_SUCCESS
;
43 if (len
&& *str_len
&& (flags
& (ICU_DECODE
|ICU_ESCAPE
))) return ERROR_INVALID_PARAMETER
;
55 memcpy( *str
, value
, len
* sizeof(WCHAR
) );
62 static WCHAR
*decode_url( LPCWSTR url
, DWORD
*len
)
65 WCHAR hex
[3], *q
, *ret
;
67 if (!(ret
= heap_alloc( *len
* sizeof(WCHAR
) ))) return NULL
;
71 if (p
[0] == '%' && isxdigitW( p
[1] ) && isxdigitW( p
[2] ))
76 *q
++ = strtolW( hex
, NULL
, 16 );
90 static BOOL
need_escape( WCHAR c
)
92 if (isalnumW( c
)) return FALSE
;
94 if (c
<= 31 || c
>= 127) return TRUE
;
121 static DWORD
copy_escape( WCHAR
*dst
, const WCHAR
*src
, DWORD len
)
123 static const WCHAR hex
[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
128 for (i
= 0; i
< len
; i
++, p
++)
130 if (need_escape( src
[i
] ))
133 p
[1] = hex
[(src
[i
] >> 4) & 0xf];
134 p
[2] = hex
[src
[i
] & 0xf];
144 static WCHAR
*escape_url( LPCWSTR url
, DWORD
*len
)
149 if ((p
= q
= strrchrW( url
, '/' )))
153 if (need_escape( *q
)) *len
+= 2;
157 if (!(ret
= heap_alloc( (*len
+ 1) * sizeof(WCHAR
) ))) return NULL
;
158 if (!p
) strcpyW( ret
, url
);
161 memcpy( ret
, url
, (p
- url
) * sizeof(WCHAR
) );
162 copy_escape( ret
+ (p
- url
), p
, q
- p
);
167 static DWORD
parse_port( const WCHAR
*str
, DWORD len
, INTERNET_PORT
*ret
)
169 const WCHAR
*p
= str
;
171 while (len
&& isdigitW( *p
))
173 if ((port
= port
* 10 + *p
- '0') > 65535) return ERROR_WINHTTP_INVALID_URL
;
177 return ERROR_SUCCESS
;
180 /***********************************************************************
181 * WinHttpCrackUrl (winhttp.@)
183 BOOL WINAPI
WinHttpCrackUrl( LPCWSTR url
, DWORD len
, DWORD flags
, LPURL_COMPONENTSW uc
)
185 WCHAR
*p
, *q
, *r
, *url_decoded
= NULL
, *url_escaped
= NULL
;
186 INTERNET_SCHEME scheme
= 0;
187 BOOL overflow
= FALSE
;
190 TRACE("%s, %d, %x, %p\n", debugstr_wn(url
, len
), len
, flags
, uc
);
192 if (!url
|| !uc
|| uc
->dwStructSize
!= sizeof(URL_COMPONENTS
))
194 set_last_error( ERROR_INVALID_PARAMETER
);
197 if (!len
) len
= strlenW( url
);
199 if (flags
& ICU_ESCAPE
)
201 if (!(url_escaped
= escape_url( url
, &len
)))
203 set_last_error( ERROR_OUTOFMEMORY
);
208 else if (flags
& ICU_DECODE
)
210 if (!(url_decoded
= decode_url( url
, &len
)))
212 set_last_error( ERROR_OUTOFMEMORY
);
217 if (!(p
= strchrW( url
, ':' )))
219 set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME
);
222 if (p
- url
== 4 && !strncmpiW( url
, scheme_http
, 4 )) scheme
= INTERNET_SCHEME_HTTP
;
223 else if (p
- url
== 5 && !strncmpiW( url
, scheme_https
, 5 )) scheme
= INTERNET_SCHEME_HTTPS
;
226 err
= ERROR_WINHTTP_UNRECOGNIZED_SCHEME
;
230 if ((err
= set_component( &uc
->lpszScheme
, &uc
->dwSchemeLength
, (WCHAR
*)url
, p
- url
, flags
, &overflow
))) goto exit
;
233 if (!p
[0] || p
[0] != '/' || p
[1] != '/')
235 err
= ERROR_WINHTTP_INVALID_URL
;
241 err
= ERROR_WINHTTP_INVALID_URL
;
244 if ((q
= memchrW( p
, '@', len
- (p
- url
) )) && !(memchrW( p
, '/', q
- p
)))
246 if ((r
= memchrW( p
, ':', q
- p
)))
248 if ((err
= set_component( &uc
->lpszUserName
, &uc
->dwUserNameLength
, p
, r
- p
, flags
, &overflow
))) goto exit
;
250 if ((err
= set_component( &uc
->lpszPassword
, &uc
->dwPasswordLength
, r
, q
- r
, flags
, &overflow
))) goto exit
;
254 if ((err
= set_component( &uc
->lpszUserName
, &uc
->dwUserNameLength
, p
, q
- p
, flags
, &overflow
))) goto exit
;
255 if ((err
= set_component( &uc
->lpszPassword
, &uc
->dwPasswordLength
, NULL
, 0, flags
, &overflow
))) goto exit
;
261 if ((err
= set_component( &uc
->lpszUserName
, &uc
->dwUserNameLength
, NULL
, 0, flags
, &overflow
))) goto exit
;
262 if ((err
= set_component( &uc
->lpszPassword
, &uc
->dwPasswordLength
, NULL
, 0, flags
, &overflow
))) goto exit
;
264 if ((q
= memchrW( p
, '/', len
- (p
- url
) )))
266 if ((r
= memchrW( p
, ':', q
- p
)))
268 if ((err
= set_component( &uc
->lpszHostName
, &uc
->dwHostNameLength
, p
, r
- p
, flags
, &overflow
))) goto exit
;
270 if ((err
= parse_port( r
, q
- r
, &uc
->nPort
))) goto exit
;
274 if ((err
= set_component( &uc
->lpszHostName
, &uc
->dwHostNameLength
, p
, q
- p
, flags
, &overflow
))) goto exit
;
275 if (scheme
== INTERNET_SCHEME_HTTP
) uc
->nPort
= INTERNET_DEFAULT_HTTP_PORT
;
276 if (scheme
== INTERNET_SCHEME_HTTPS
) uc
->nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
279 if ((r
= memchrW( q
, '?', len
- (q
- url
) )))
281 if ((err
= set_component( &uc
->lpszUrlPath
, &uc
->dwUrlPathLength
, q
, r
- q
, flags
, &overflow
))) goto exit
;
282 if ((err
= set_component( &uc
->lpszExtraInfo
, &uc
->dwExtraInfoLength
, r
, len
- (r
- url
), flags
, &overflow
))) goto exit
;
286 if ((err
= set_component( &uc
->lpszUrlPath
, &uc
->dwUrlPathLength
, q
, len
- (q
- url
), flags
, &overflow
))) goto exit
;
287 if ((err
= set_component( &uc
->lpszExtraInfo
, &uc
->dwExtraInfoLength
, (WCHAR
*)url
+ len
, 0, flags
, &overflow
))) goto exit
;
292 if ((r
= memchrW( p
, ':', len
- (p
- url
) )))
294 if ((err
= set_component( &uc
->lpszHostName
, &uc
->dwHostNameLength
, p
, r
- p
, flags
, &overflow
))) goto exit
;
296 if ((err
= parse_port( r
, len
- (r
- url
), &uc
->nPort
))) goto exit
;
300 if ((err
= set_component( &uc
->lpszHostName
, &uc
->dwHostNameLength
, p
, len
- (p
- url
), flags
, &overflow
))) goto exit
;
301 if (scheme
== INTERNET_SCHEME_HTTP
) uc
->nPort
= INTERNET_DEFAULT_HTTP_PORT
;
302 if (scheme
== INTERNET_SCHEME_HTTPS
) uc
->nPort
= INTERNET_DEFAULT_HTTPS_PORT
;
304 if ((err
= set_component( &uc
->lpszUrlPath
, &uc
->dwUrlPathLength
, (WCHAR
*)url
+ len
, 0, flags
, &overflow
))) goto exit
;
305 if ((err
= set_component( &uc
->lpszExtraInfo
, &uc
->dwExtraInfoLength
, (WCHAR
*)url
+ len
, 0, flags
, &overflow
))) goto exit
;
308 TRACE("scheme(%s) host(%s) port(%d) path(%s) extra(%s)\n", debugstr_wn( uc
->lpszScheme
, uc
->dwSchemeLength
),
309 debugstr_wn( uc
->lpszHostName
, uc
->dwHostNameLength
), uc
->nPort
, debugstr_wn( uc
->lpszUrlPath
, uc
->dwUrlPathLength
),
310 debugstr_wn( uc
->lpszExtraInfo
, uc
->dwExtraInfoLength
));
315 if (overflow
) err
= ERROR_INSUFFICIENT_BUFFER
;
316 uc
->nScheme
= scheme
;
318 heap_free( url_decoded
);
319 heap_free( url_escaped
);
320 set_last_error( err
);
324 static INTERNET_SCHEME
get_scheme( const WCHAR
*scheme
, DWORD len
)
326 if (!strncmpW( scheme
, scheme_http
, len
)) return INTERNET_SCHEME_HTTP
;
327 if (!strncmpW( scheme
, scheme_https
, len
)) return INTERNET_SCHEME_HTTPS
;
331 static const WCHAR
*get_scheme_string( INTERNET_SCHEME scheme
)
333 if (scheme
== INTERNET_SCHEME_HTTP
) return scheme_http
;
334 if (scheme
== INTERNET_SCHEME_HTTPS
) return scheme_https
;
338 static BOOL
uses_default_port( INTERNET_SCHEME scheme
, INTERNET_PORT port
)
340 if ((scheme
== INTERNET_SCHEME_HTTP
) && (port
== INTERNET_DEFAULT_HTTP_PORT
)) return TRUE
;
341 if ((scheme
== INTERNET_SCHEME_HTTPS
) && (port
== INTERNET_DEFAULT_HTTPS_PORT
)) return TRUE
;
345 static DWORD
comp_length( DWORD len
, DWORD flags
, WCHAR
*comp
)
350 ret
= len
? len
: strlenW( comp
);
351 if (!(flags
& ICU_ESCAPE
)) return ret
;
352 for (i
= 0; i
< len
; i
++) if (need_escape( comp
[i
] )) ret
+= 2;
356 static BOOL
calc_length( URL_COMPONENTS
*uc
, DWORD flags
, LPDWORD len
)
358 static const WCHAR formatW
[] = {'%','u',0};
359 INTERNET_SCHEME scheme
;
364 DWORD scheme_len
= comp_length( uc
->dwSchemeLength
, 0, uc
->lpszScheme
);
366 scheme
= get_scheme( uc
->lpszScheme
, scheme_len
);
370 scheme
= uc
->nScheme
;
371 if (!scheme
) scheme
= INTERNET_SCHEME_HTTP
;
372 *len
+= strlenW( get_scheme_string( scheme
) );
375 if (uc
->lpszHostName
) *len
+= 2; /* "//" */
377 if (uc
->lpszUserName
)
379 *len
+= comp_length( uc
->dwUserNameLength
, 0, uc
->lpszUserName
);
384 if (uc
->lpszPassword
)
386 set_last_error( ERROR_INVALID_PARAMETER
);
390 if (uc
->lpszPassword
)
393 *len
+= comp_length( uc
->dwPasswordLength
, 0, uc
->lpszPassword
);
395 if (uc
->lpszHostName
)
397 *len
+= comp_length( uc
->dwHostNameLength
, 0, uc
->lpszHostName
);
399 if (!uses_default_port( scheme
, uc
->nPort
))
401 WCHAR port
[sizeof("65535")];
403 *len
+= sprintfW( port
, formatW
, uc
->nPort
);
406 if (uc
->lpszUrlPath
&& *uc
->lpszUrlPath
!= '/') *len
+= 1; /* '/' */
408 if (uc
->lpszUrlPath
) *len
+= comp_length( uc
->dwUrlPathLength
, flags
, uc
->lpszUrlPath
);
409 if (uc
->lpszExtraInfo
) *len
+= comp_length( uc
->dwExtraInfoLength
, flags
, uc
->lpszExtraInfo
);
413 /***********************************************************************
414 * WinHttpCreateUrl (winhttp.@)
416 BOOL WINAPI
WinHttpCreateUrl( LPURL_COMPONENTS uc
, DWORD flags
, LPWSTR url
, LPDWORD required
)
418 static const WCHAR formatW
[] = {'%','u',0};
419 static const WCHAR twoslashW
[] = {'/','/'};
421 INTERNET_SCHEME scheme
;
423 TRACE("%p, 0x%08x, %p, %p\n", uc
, flags
, url
, required
);
425 if (!uc
|| uc
->dwStructSize
!= sizeof(URL_COMPONENTS
) || !required
)
427 set_last_error( ERROR_INVALID_PARAMETER
);
431 if (!calc_length( uc
, flags
, &len
)) return FALSE
;
436 set_last_error( ERROR_INSUFFICIENT_BUFFER
);
441 set_last_error( ERROR_INVALID_PARAMETER
);
449 len
= comp_length( uc
->dwSchemeLength
, 0, uc
->lpszScheme
);
450 memcpy( url
, uc
->lpszScheme
, len
* sizeof(WCHAR
) );
453 scheme
= get_scheme( uc
->lpszScheme
, len
);
457 const WCHAR
*schemeW
;
458 scheme
= uc
->nScheme
;
460 if (!scheme
) scheme
= INTERNET_SCHEME_HTTP
;
462 schemeW
= get_scheme_string( scheme
);
463 len
= strlenW( schemeW
);
464 memcpy( url
, schemeW
, len
* sizeof(WCHAR
) );
468 /* all schemes are followed by at least a colon */
472 if (uc
->lpszHostName
)
474 memcpy( url
, twoslashW
, sizeof(twoslashW
) );
475 url
+= sizeof(twoslashW
) / sizeof(twoslashW
[0]);
477 if (uc
->lpszUserName
)
479 len
= comp_length( uc
->dwUserNameLength
, 0, uc
->lpszUserName
);
480 memcpy( url
, uc
->lpszUserName
, len
* sizeof(WCHAR
) );
483 if (uc
->lpszPassword
)
488 len
= comp_length( uc
->dwPasswordLength
, 0, uc
->lpszPassword
);
489 memcpy( url
, uc
->lpszPassword
, len
* sizeof(WCHAR
) );
495 if (uc
->lpszHostName
)
497 len
= comp_length( uc
->dwHostNameLength
, 0, uc
->lpszHostName
);
498 memcpy( url
, uc
->lpszHostName
, len
* sizeof(WCHAR
) );
501 if (!uses_default_port( scheme
, uc
->nPort
))
506 url
+= sprintfW( url
, formatW
, uc
->nPort
);
509 /* add slash between hostname and path if necessary */
510 if (uc
->lpszUrlPath
&& *uc
->lpszUrlPath
!= '/')
518 len
= comp_length( uc
->dwUrlPathLength
, 0, uc
->lpszUrlPath
);
519 if (flags
& ICU_ESCAPE
) url
+= copy_escape( url
, uc
->lpszUrlPath
, len
);
522 memcpy( url
, uc
->lpszUrlPath
, len
* sizeof(WCHAR
) );
526 if (uc
->lpszExtraInfo
)
528 len
= comp_length( uc
->dwExtraInfoLength
, 0, uc
->lpszExtraInfo
);
529 if (flags
& ICU_ESCAPE
) url
+= copy_escape( url
, uc
->lpszExtraInfo
, len
);
532 memcpy( url
, uc
->lpszExtraInfo
, len
* sizeof(WCHAR
) );
537 set_last_error( ERROR_SUCCESS
);