- Sync with trunk r58248 to bring the latest changes from Amine (headers) and others...
[reactos.git] / dll / win32 / winhttp / url.c
1 /*
2 * Copyright 2008 Hans Leidekker for CodeWeavers
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #define WIN32_NO_STATUS
20 #define _INC_WINDOWS
21 #define COM_NO_WINDOWS_H
22
23 #include <config.h>
24 //#include <stdarg.h>
25
26 #include <wine/debug.h>
27
28 //#include "windef.h"
29 #include <winbase.h>
30 //#include "winreg.h"
31 #include <winhttp.h>
32 //#include "shlwapi.h"
33
34 #include "winhttp_private.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(winhttp);
37
38 static const WCHAR scheme_http[] = {'h','t','t','p',0};
39 static const WCHAR scheme_https[] = {'h','t','t','p','s',0};
40
41 static BOOL set_component( WCHAR **str, DWORD *str_len, WCHAR *value, DWORD len, DWORD flags )
42 {
43 if (!*str)
44 {
45 if (len && *str_len && (flags & (ICU_DECODE|ICU_ESCAPE)))
46 {
47 set_last_error( ERROR_INVALID_PARAMETER );
48 return FALSE;
49 }
50 *str = value;
51 *str_len = len;
52 }
53 else
54 {
55 if (len > (*str_len) - 1)
56 {
57 *str_len = len + 1;
58 set_last_error( ERROR_INSUFFICIENT_BUFFER );
59 return FALSE;
60 }
61 memcpy( *str, value, len * sizeof(WCHAR) );
62 (*str)[len] = 0;
63 *str_len = len;
64 }
65 return TRUE;
66 }
67
68 static WCHAR *decode_url( LPCWSTR url, DWORD *len )
69 {
70 const WCHAR *p = url;
71 WCHAR hex[3], *q, *ret;
72
73 if (!(ret = heap_alloc( *len * sizeof(WCHAR) ))) return NULL;
74 q = ret;
75 while (*len > 0)
76 {
77 if (p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] ))
78 {
79 hex[0] = p[1];
80 hex[1] = p[2];
81 hex[2] = 0;
82 *q++ = strtolW( hex, NULL, 16 );
83 p += 3;
84 *len -= 3;
85 }
86 else
87 {
88 *q++ = *p++;
89 *len -= 1;
90 }
91 }
92 *len = q - ret;
93 return ret;
94 }
95
96 static BOOL need_escape( WCHAR c )
97 {
98 if (isalnumW( c )) return FALSE;
99
100 if (c <= 31 || c >= 127) return TRUE;
101 else
102 {
103 switch (c)
104 {
105 case ' ':
106 case '"':
107 case '#':
108 case '%':
109 case '<':
110 case '>':
111 case ']':
112 case '\\':
113 case '[':
114 case '^':
115 case '`':
116 case '{':
117 case '|':
118 case '}':
119 case '~':
120 return TRUE;
121 default:
122 return FALSE;
123 }
124 }
125 }
126
127 static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len )
128 {
129 static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
130 DWORD ret = len;
131 unsigned int i;
132 WCHAR *p = dst;
133
134 for (i = 0; i < len; i++, p++)
135 {
136 if (need_escape( src[i] ))
137 {
138 p[0] = '%';
139 p[1] = hex[(src[i] >> 4) & 0xf];
140 p[2] = hex[src[i] & 0xf];
141 ret += 2;
142 p += 2;
143 }
144 else *p = src[i];
145 }
146 dst[ret] = 0;
147 return ret;
148 }
149
150 static WCHAR *escape_url( LPCWSTR url, DWORD *len )
151 {
152 WCHAR *ret;
153 const WCHAR *p, *q;
154
155 if ((p = q = strrchrW( url, '/' )))
156 {
157 while (*q)
158 {
159 if (need_escape( *q )) *len += 2;
160 q++;
161 }
162 }
163 if (!(ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL;
164 if (!p) strcpyW( ret, url );
165 else
166 {
167 memcpy( ret, url, (p - url) * sizeof(WCHAR) );
168 copy_escape( ret + (p - url), p, q - p );
169 }
170 return ret;
171 }
172
173 /***********************************************************************
174 * WinHttpCrackUrl (winhttp.@)
175 */
176 BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW uc )
177 {
178 BOOL ret = FALSE;
179 WCHAR *p, *q, *r, *url_decoded = NULL, *url_escaped = NULL;
180
181 TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, uc);
182
183 if (!url || !uc || uc->dwStructSize != sizeof(URL_COMPONENTS))
184 {
185 set_last_error( ERROR_INVALID_PARAMETER );
186 return FALSE;
187 }
188 if (!len) len = strlenW( url );
189
190 if (flags & ICU_ESCAPE)
191 {
192 if (!(url_escaped = escape_url( url, &len )))
193 {
194 set_last_error( ERROR_OUTOFMEMORY );
195 return FALSE;
196 }
197 url = url_escaped;
198 }
199 else if (flags & ICU_DECODE)
200 {
201 if (!(url_decoded = decode_url( url, &len )))
202 {
203 set_last_error( ERROR_OUTOFMEMORY );
204 return FALSE;
205 }
206 url = url_decoded;
207 }
208 if (!(p = strchrW( url, ':' )))
209 {
210 set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME );
211 return FALSE;
212 }
213 if (p - url == 4 && !strncmpiW( url, scheme_http, 4 )) uc->nScheme = INTERNET_SCHEME_HTTP;
214 else if (p - url == 5 && !strncmpiW( url, scheme_https, 5 )) uc->nScheme = INTERNET_SCHEME_HTTPS;
215 else
216 {
217 set_last_error( ERROR_WINHTTP_UNRECOGNIZED_SCHEME );
218 goto exit;
219 }
220 if (!(set_component( &uc->lpszScheme, &uc->dwSchemeLength, (WCHAR *)url, p - url, flags ))) goto exit;
221
222 p++; /* skip ':' */
223 if (!p[0] || p[0] != '/' || p[1] != '/') goto exit;
224 p += 2;
225
226 if (!p[0]) goto exit;
227 if ((q = memchrW( p, '@', len - (p - url) )) && !(memchrW( p, '/', q - p )))
228 {
229 if ((r = memchrW( p, ':', q - p )))
230 {
231 if (!(set_component( &uc->lpszUserName, &uc->dwUserNameLength, p, r - p, flags ))) goto exit;
232 r++;
233 if (!(set_component( &uc->lpszPassword, &uc->dwPasswordLength, r, q - r, flags ))) goto exit;
234 }
235 else
236 {
237 if (!(set_component( &uc->lpszUserName, &uc->dwUserNameLength, p, q - p, flags ))) goto exit;
238 if (!(set_component( &uc->lpszPassword, &uc->dwPasswordLength, NULL, 0, flags ))) goto exit;
239 }
240 p = q + 1;
241 }
242 else
243 {
244 if (!(set_component( &uc->lpszUserName, &uc->dwUserNameLength, NULL, 0, flags ))) goto exit;
245 if (!(set_component( &uc->lpszPassword, &uc->dwPasswordLength, NULL, 0, flags ))) goto exit;
246 }
247 if ((q = memchrW( p, '/', len - (p - url) )))
248 {
249 if ((r = memchrW( p, ':', q - p )))
250 {
251 if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, r - p, flags ))) goto exit;
252 r++;
253 uc->nPort = atoiW( r );
254 }
255 else
256 {
257 if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, q - p, flags ))) goto exit;
258 if (uc->nScheme == INTERNET_SCHEME_HTTP) uc->nPort = INTERNET_DEFAULT_HTTP_PORT;
259 if (uc->nScheme == INTERNET_SCHEME_HTTPS) uc->nPort = INTERNET_DEFAULT_HTTPS_PORT;
260 }
261
262 if ((r = memchrW( q, '?', len - (q - url) )))
263 {
264 if (!(set_component( &uc->lpszUrlPath, &uc->dwUrlPathLength, q, r - q, flags ))) goto exit;
265 if (!(set_component( &uc->lpszExtraInfo, &uc->dwExtraInfoLength, r, len - (r - url), flags ))) goto exit;
266 }
267 else
268 {
269 if (!(set_component( &uc->lpszUrlPath, &uc->dwUrlPathLength, q, len - (q - url), flags ))) goto exit;
270 if (!(set_component( &uc->lpszExtraInfo, &uc->dwExtraInfoLength, (WCHAR *)url + len, 0, flags ))) goto exit;
271 }
272 }
273 else
274 {
275 if ((r = memchrW( p, ':', len - (p - url) )))
276 {
277 if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, r - p, flags ))) goto exit;
278 r++;
279 uc->nPort = atoiW( r );
280 }
281 else
282 {
283 if (!(set_component( &uc->lpszHostName, &uc->dwHostNameLength, p, len - (p - url), flags ))) goto exit;
284 if (uc->nScheme == INTERNET_SCHEME_HTTP) uc->nPort = INTERNET_DEFAULT_HTTP_PORT;
285 if (uc->nScheme == INTERNET_SCHEME_HTTPS) uc->nPort = INTERNET_DEFAULT_HTTPS_PORT;
286 }
287 if (!(set_component( &uc->lpszUrlPath, &uc->dwUrlPathLength, (WCHAR *)url + len, 0, flags ))) goto exit;
288 if (!(set_component( &uc->lpszExtraInfo, &uc->dwExtraInfoLength, (WCHAR *)url + len, 0, flags ))) goto exit;
289 }
290
291 ret = TRUE;
292
293 TRACE("scheme(%s) host(%s) port(%d) path(%s) extra(%s)\n",
294 debugstr_wn( uc->lpszScheme, uc->dwSchemeLength ),
295 debugstr_wn( uc->lpszHostName, uc->dwHostNameLength ),
296 uc->nPort,
297 debugstr_wn( uc->lpszUrlPath, uc->dwUrlPathLength ),
298 debugstr_wn( uc->lpszExtraInfo, uc->dwExtraInfoLength ));
299
300 exit:
301 heap_free( url_decoded );
302 heap_free( url_escaped );
303 return ret;
304 }
305
306 static INTERNET_SCHEME get_scheme( const WCHAR *scheme, DWORD len )
307 {
308 if (!strncmpW( scheme, scheme_http, len )) return INTERNET_SCHEME_HTTP;
309 if (!strncmpW( scheme, scheme_https, len )) return INTERNET_SCHEME_HTTPS;
310 return 0;
311 }
312
313 static const WCHAR *get_scheme_string( INTERNET_SCHEME scheme )
314 {
315 if (scheme == INTERNET_SCHEME_HTTP) return scheme_http;
316 if (scheme == INTERNET_SCHEME_HTTPS) return scheme_https;
317 return NULL;
318 }
319
320 static BOOL uses_default_port( INTERNET_SCHEME scheme, INTERNET_PORT port )
321 {
322 if ((scheme == INTERNET_SCHEME_HTTP) && (port == INTERNET_DEFAULT_HTTP_PORT)) return TRUE;
323 if ((scheme == INTERNET_SCHEME_HTTPS) && (port == INTERNET_DEFAULT_HTTPS_PORT)) return TRUE;
324 return FALSE;
325 }
326
327 static DWORD comp_length( DWORD len, DWORD flags, WCHAR *comp )
328 {
329 DWORD ret;
330 unsigned int i;
331
332 ret = len ? len : strlenW( comp );
333 if (!(flags & ICU_ESCAPE)) return ret;
334 for (i = 0; i < len; i++) if (need_escape( comp[i] )) ret += 2;
335 return ret;
336 }
337
338 static BOOL calc_length( URL_COMPONENTS *uc, DWORD flags, LPDWORD len )
339 {
340 static const WCHAR formatW[] = {'%','u',0};
341 INTERNET_SCHEME scheme;
342
343 *len = 0;
344 if (uc->lpszScheme)
345 {
346 DWORD scheme_len = comp_length( uc->dwSchemeLength, 0, uc->lpszScheme );
347 *len += scheme_len;
348 scheme = get_scheme( uc->lpszScheme, scheme_len );
349 }
350 else
351 {
352 scheme = uc->nScheme;
353 if (!scheme) scheme = INTERNET_SCHEME_HTTP;
354 *len += strlenW( get_scheme_string( scheme ) );
355 }
356 *len += 1; /* ':' */
357 if (uc->lpszHostName) *len += 2; /* "//" */
358
359 if (uc->lpszUserName)
360 {
361 *len += comp_length( uc->dwUserNameLength, 0, uc->lpszUserName );
362 *len += 1; /* "@" */
363 }
364 else
365 {
366 if (uc->lpszPassword)
367 {
368 set_last_error( ERROR_INVALID_PARAMETER );
369 return FALSE;
370 }
371 }
372 if (uc->lpszPassword)
373 {
374 *len += 1; /* ":" */
375 *len += comp_length( uc->dwPasswordLength, 0, uc->lpszPassword );
376 }
377 if (uc->lpszHostName)
378 {
379 *len += comp_length( uc->dwHostNameLength, 0, uc->lpszHostName );
380
381 if (!uses_default_port( scheme, uc->nPort ))
382 {
383 WCHAR port[sizeof("65535")];
384
385 sprintfW( port, formatW, uc->nPort );
386 *len += strlenW( port );
387 *len += 1; /* ":" */
388 }
389 if (uc->lpszUrlPath && *uc->lpszUrlPath != '/') *len += 1; /* '/' */
390 }
391 if (uc->lpszUrlPath) *len += comp_length( uc->dwUrlPathLength, flags, uc->lpszUrlPath );
392 if (uc->lpszExtraInfo) *len += comp_length( uc->dwExtraInfoLength, flags, uc->lpszExtraInfo );
393 return TRUE;
394 }
395
396 /***********************************************************************
397 * WinHttpCreateUrl (winhttp.@)
398 */
399 BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDWORD required )
400 {
401 static const WCHAR formatW[] = {'%','u',0};
402 static const WCHAR twoslashW[] = {'/','/'};
403
404 DWORD len;
405 INTERNET_SCHEME scheme;
406
407 TRACE("%p, 0x%08x, %p, %p\n", uc, flags, url, required);
408
409 if (!uc || uc->dwStructSize != sizeof(URL_COMPONENTS) || !required || !url)
410 {
411 set_last_error( ERROR_INVALID_PARAMETER );
412 return FALSE;
413 }
414
415 if (!calc_length( uc, flags, &len )) return FALSE;
416
417 if (*required < len)
418 {
419 *required = len + 1;
420 set_last_error( ERROR_INSUFFICIENT_BUFFER );
421 return FALSE;
422 }
423
424 url[0] = 0;
425 *required = len;
426 if (uc->lpszScheme)
427 {
428 len = comp_length( uc->dwSchemeLength, 0, uc->lpszScheme );
429 memcpy( url, uc->lpszScheme, len * sizeof(WCHAR) );
430 url += len;
431
432 scheme = get_scheme( uc->lpszScheme, len );
433 }
434 else
435 {
436 const WCHAR *schemeW;
437 scheme = uc->nScheme;
438
439 if (!scheme) scheme = INTERNET_SCHEME_HTTP;
440
441 schemeW = get_scheme_string( scheme );
442 len = strlenW( schemeW );
443 memcpy( url, schemeW, len * sizeof(WCHAR) );
444 url += len;
445 }
446
447 /* all schemes are followed by at least a colon */
448 *url = ':';
449 url++;
450
451 if (uc->lpszHostName)
452 {
453 memcpy( url, twoslashW, sizeof(twoslashW) );
454 url += sizeof(twoslashW) / sizeof(twoslashW[0]);
455 }
456 if (uc->lpszUserName)
457 {
458 len = comp_length( uc->dwUserNameLength, 0, uc->lpszUserName );
459 memcpy( url, uc->lpszUserName, len * sizeof(WCHAR) );
460 url += len;
461
462 if (uc->lpszPassword)
463 {
464 *url = ':';
465 url++;
466
467 len = comp_length( uc->dwPasswordLength, 0, uc->lpszPassword );
468 memcpy( url, uc->lpszPassword, len * sizeof(WCHAR) );
469 url += len;
470 }
471 *url = '@';
472 url++;
473 }
474 if (uc->lpszHostName)
475 {
476 len = comp_length( uc->dwHostNameLength, 0, uc->lpszHostName );
477 memcpy( url, uc->lpszHostName, len * sizeof(WCHAR) );
478 url += len;
479
480 if (!uses_default_port( scheme, uc->nPort ))
481 {
482 WCHAR port[sizeof("65535")];
483
484 sprintfW( port, formatW, uc->nPort );
485 *url = ':';
486 url++;
487
488 len = strlenW( port );
489 memcpy( url, port, len * sizeof(WCHAR) );
490 url += len;
491 }
492
493 /* add slash between hostname and path if necessary */
494 if (uc->lpszUrlPath && *uc->lpszUrlPath != '/')
495 {
496 *url = '/';
497 url++;
498 }
499 }
500 if (uc->lpszUrlPath)
501 {
502 len = comp_length( uc->dwUrlPathLength, 0, uc->lpszUrlPath );
503 if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszUrlPath, len );
504 else
505 {
506 memcpy( url, uc->lpszUrlPath, len * sizeof(WCHAR) );
507 url += len;
508 }
509 }
510 if (uc->lpszExtraInfo)
511 {
512 len = comp_length( uc->dwExtraInfoLength, 0, uc->lpszExtraInfo );
513 if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszExtraInfo, len );
514 else
515 {
516 memcpy( url, uc->lpszExtraInfo, len * sizeof(WCHAR) );
517 url += len;
518 }
519 }
520 *url = 0;
521 return TRUE;
522 }