Fix some -Wsigned-compare warnings.
[wine/multimedia.git] / dlls / shlwapi / url.c
blobc7281f42b6a50b7436223b988eaff7ee4d5600cf
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #define NO_SHLWAPI_STREAM
34 #include "shlwapi.h"
35 #include "wine/debug.h"
37 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
38 BOOL WINAPI MLFreeLibrary(HMODULE);
39 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
41 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43 /* The following schemes were identified in the native version of
44 * SHLWAPI.DLL version 5.50
46 typedef enum {
47 URL_SCHEME_INVALID = -1,
48 URL_SCHEME_UNKNOWN = 0,
49 URL_SCHEME_FTP,
50 URL_SCHEME_HTTP,
51 URL_SCHEME_GOPHER,
52 URL_SCHEME_MAILTO,
53 URL_SCHEME_NEWS,
54 URL_SCHEME_NNTP,
55 URL_SCHEME_TELNET,
56 URL_SCHEME_WAIS,
57 URL_SCHEME_FILE,
58 URL_SCHEME_MK,
59 URL_SCHEME_HTTPS,
60 URL_SCHEME_SHELL,
61 URL_SCHEME_SNEWS,
62 URL_SCHEME_LOCAL,
63 URL_SCHEME_JAVASCRIPT,
64 URL_SCHEME_VBSCRIPT,
65 URL_SCHEME_ABOUT,
66 URL_SCHEME_RES,
67 URL_SCHEME_MAXVALUE
68 } URL_SCHEME;
70 typedef struct {
71 URL_SCHEME scheme_number;
72 LPCSTR scheme_name;
73 } SHL_2_inet_scheme;
75 static const SHL_2_inet_scheme shlwapi_schemes[] = {
76 {URL_SCHEME_FTP, "ftp"},
77 {URL_SCHEME_HTTP, "http"},
78 {URL_SCHEME_GOPHER, "gopher"},
79 {URL_SCHEME_MAILTO, "mailto"},
80 {URL_SCHEME_NEWS, "news"},
81 {URL_SCHEME_NNTP, "nntp"},
82 {URL_SCHEME_TELNET, "telnet"},
83 {URL_SCHEME_WAIS, "wais"},
84 {URL_SCHEME_FILE, "file"},
85 {URL_SCHEME_MK, "mk"},
86 {URL_SCHEME_HTTPS, "https"},
87 {URL_SCHEME_SHELL, "shell"},
88 {URL_SCHEME_SNEWS, "snews"},
89 {URL_SCHEME_LOCAL, "local"},
90 {URL_SCHEME_JAVASCRIPT, "javascript"},
91 {URL_SCHEME_VBSCRIPT, "vbscript"},
92 {URL_SCHEME_ABOUT, "about"},
93 {URL_SCHEME_RES, "res"},
94 {0, 0}
97 typedef struct {
98 LPCWSTR pScheme; /* [out] start of scheme */
99 DWORD szScheme; /* [out] size of scheme (until colon) */
100 LPCWSTR pUserName; /* [out] start of Username */
101 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
102 LPCWSTR pPassword; /* [out] start of Password */
103 DWORD szPassword; /* [out] size of Password (until "@") */
104 LPCWSTR pHostName; /* [out] start of Hostname */
105 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
106 LPCWSTR pPort; /* [out] start of Port */
107 DWORD szPort; /* [out] size of Port (until "/" or eos) */
108 LPCWSTR pQuery; /* [out] start of Query */
109 DWORD szQuery; /* [out] size of Query (until eos) */
110 } WINE_PARSE_URL;
112 typedef enum {
113 SCHEME,
114 HOST,
115 PORT,
116 USERPASS,
117 } WINE_URL_SCAN_TYPE;
119 typedef struct {
120 INT size; /* [in] (always 0x18) */
121 LPCSTR ap1; /* [out] start of scheme */
122 INT sizep1; /* [out] size of scheme (until colon) */
123 LPCSTR ap2; /* [out] pointer following first colon */
124 INT sizep2; /* [out] size of remainder */
125 INT fcncde; /* [out] function match of p1 (0 if unknown) */
126 } UNKNOWN_SHLWAPI_1;
128 typedef struct {
129 INT size; /* [in] (always 0x18) */
130 LPCWSTR ap1; /* [out] start of scheme */
131 INT sizep1; /* [out] size of scheme (until colon) */
132 LPCWSTR ap2; /* [out] pointer following first colon */
133 INT sizep2; /* [out] size of remainder */
134 INT fcncde; /* [out] function match of p1 (0 if unknown) */
135 } UNKNOWN_SHLWAPI_2;
137 static const CHAR hexDigits[] = "0123456789ABCDEF";
139 static const WCHAR fileW[] = {'f','i','l','e','\0'};
141 static const unsigned char HashDataLookup[256] = {
142 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
143 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
144 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
145 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
146 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
147 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
148 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
149 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
150 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
151 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
152 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
153 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
154 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
155 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
156 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
157 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
158 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
159 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
160 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
161 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
163 static inline BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
166 if (isalnum(ch))
167 return FALSE;
169 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
170 if(ch == ' ')
171 return TRUE;
172 else
173 return FALSE;
176 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
177 return TRUE;
179 if (ch <= 31 || ch >= 127)
180 return TRUE;
182 else {
183 switch (ch) {
184 case ' ':
185 case '<':
186 case '>':
187 case '\"':
188 case '{':
189 case '}':
190 case '|':
191 /* case '\\': */
192 case '^':
193 case ']':
194 case '[':
195 case '`':
196 case '&':
197 return TRUE;
199 case '/':
200 case '?':
201 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
202 default:
203 return FALSE;
208 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
211 if (isalnumW(ch))
212 return FALSE;
214 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
215 if(ch == L' ')
216 return TRUE;
217 else
218 return FALSE;
221 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
222 return TRUE;
224 if (ch <= 31 || ch >= 127)
225 return TRUE;
227 else {
228 switch (ch) {
229 case L' ':
230 case L'<':
231 case L'>':
232 case L'\"':
233 case L'{':
234 case L'}':
235 case L'|':
236 case L'\\':
237 case L'^':
238 case L']':
239 case L'[':
240 case L'`':
241 case L'&':
242 return TRUE;
244 case L'/':
245 case L'?':
246 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
247 default:
248 return FALSE;
253 static BOOL URL_JustLocation(LPCWSTR str)
255 while(*str && (*str == L'/')) str++;
256 if (*str) {
257 while (*str && ((*str == L'-') ||
258 (*str == L'.') ||
259 isalnumW(*str))) str++;
260 if (*str == L'/') return FALSE;
262 return TRUE;
266 /*************************************************************************
267 * @ [SHLWAPI.1]
269 * Parse a Url into its constituent parts.
271 * PARAMS
272 * x [I] Url to parse
273 * y [O] Undocumented structure holding the parsed information
275 * RETURNS
276 * Success: S_OK. y contains the parsed Url details.
277 * Failure: An HRESULT error code.
279 DWORD WINAPI ParseURLA(LPCSTR x, UNKNOWN_SHLWAPI_1 *y)
281 DWORD cnt;
282 const SHL_2_inet_scheme *inet_pro;
284 y->fcncde = URL_SCHEME_INVALID;
285 if (y->size != 0x18) return E_INVALIDARG;
286 /* FIXME: leading white space generates error of 0x80041001 which
287 * is undefined
289 if (*x <= ' ') return 0x80041001;
290 cnt = 0;
291 y->sizep1 = 0;
292 y->ap1 = x;
293 while (*x) {
294 if (*x == ':') {
295 y->sizep1 = cnt;
296 cnt = -1;
297 y->ap2 = x+1;
298 break;
300 x++;
301 cnt++;
304 /* check for no scheme in string start */
305 /* (apparently schemes *must* be larger than a single character) */
306 if ((*x == '\0') || (y->sizep1 <= 1)) {
307 y->ap1 = 0;
308 return 0x80041001;
311 /* found scheme, set length of remainder */
312 y->sizep2 = lstrlenA(y->ap2);
314 /* see if known scheme and return indicator number */
315 y->fcncde = URL_SCHEME_UNKNOWN;
316 inet_pro = shlwapi_schemes;
317 while (inet_pro->scheme_name) {
318 if (!strncasecmp(inet_pro->scheme_name, y->ap1,
319 min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) {
320 y->fcncde = inet_pro->scheme_number;
321 break;
323 inet_pro++;
325 return S_OK;
328 /*************************************************************************
329 * @ [SHLWAPI.2]
331 * Unicode version of ParseURLA.
333 DWORD WINAPI ParseURLW(LPCWSTR x, UNKNOWN_SHLWAPI_2 *y)
335 DWORD cnt;
336 const SHL_2_inet_scheme *inet_pro;
337 LPSTR cmpstr;
338 INT len;
340 y->fcncde = URL_SCHEME_INVALID;
341 if (y->size != 0x18) return E_INVALIDARG;
342 /* FIXME: leading white space generates error of 0x80041001 which
343 * is undefined
345 if (*x <= L' ') return 0x80041001;
346 cnt = 0;
347 y->sizep1 = 0;
348 y->ap1 = x;
349 while (*x) {
350 if (*x == L':') {
351 y->sizep1 = cnt;
352 cnt = -1;
353 y->ap2 = x+1;
354 break;
356 x++;
357 cnt++;
360 /* check for no scheme in string start */
361 /* (apparently schemes *must* be larger than a single character) */
362 if ((*x == L'\0') || (y->sizep1 <= 1)) {
363 y->ap1 = 0;
364 return 0x80041001;
367 /* found scheme, set length of remainder */
368 y->sizep2 = lstrlenW(y->ap2);
370 /* see if known scheme and return indicator number */
371 len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0);
372 cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len);
373 WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len, 0, 0);
374 y->fcncde = URL_SCHEME_UNKNOWN;
375 inet_pro = shlwapi_schemes;
376 while (inet_pro->scheme_name) {
377 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
378 min(len, lstrlenA(inet_pro->scheme_name)))) {
379 y->fcncde = inet_pro->scheme_number;
380 break;
382 inet_pro++;
384 HeapFree(GetProcessHeap(), 0, cmpstr);
385 return S_OK;
388 /*************************************************************************
389 * UrlCanonicalizeA [SHLWAPI.@]
391 * Canonicalize a Url.
393 * PARAMS
394 * pszUrl [I] Url to cCanonicalize
395 * pszCanonicalized [O] Destination for converted Url.
396 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
397 * dwFlags [I] Flags controlling the conversion.
399 * RETURNS
400 * Success: S_OK. The pszCanonicalized contains the converted Url.
401 * Failure: E_POINTER, if *pcchCanonicalized is too small.
403 * MSDN incorrectly describes the flags for this function. They should be:
404 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
405 *| URL_ESCAPE_SPACES_ONLY 0x04000000
406 *| URL_ESCAPE_PERCENT 0x00001000
407 *| URL_ESCAPE_UNSAFE 0x10000000
408 *| URL_UNESCAPE 0x10000000
409 *| URL_DONT_SIMPLIFY 0x08000000
410 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
412 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
413 LPDWORD pcchCanonicalized, DWORD dwFlags)
415 LPWSTR base, canonical;
416 DWORD ret, len, len2;
418 TRACE("(%s %p %p 0x%08lx) using W version\n",
419 debugstr_a(pszUrl), pszCanonicalized,
420 pcchCanonicalized, dwFlags);
422 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
423 return E_INVALIDARG;
425 base = HeapAlloc(GetProcessHeap(), 0,
426 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
427 canonical = base + INTERNET_MAX_URL_LENGTH;
429 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
430 len = INTERNET_MAX_URL_LENGTH;
432 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
433 if (ret != S_OK) {
434 HeapFree(GetProcessHeap(), 0, base);
435 return ret;
438 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
439 if (len2 > *pcchCanonicalized) {
440 *pcchCanonicalized = len;
441 HeapFree(GetProcessHeap(), 0, base);
442 return E_POINTER;
444 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
445 *pcchCanonicalized, 0, 0);
446 *pcchCanonicalized = len2;
447 HeapFree(GetProcessHeap(), 0, base);
448 return S_OK;
451 /*************************************************************************
452 * UrlCanonicalizeW [SHLWAPI.@]
454 * See UrlCanonicalizeA.
456 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
457 LPDWORD pcchCanonicalized, DWORD dwFlags)
459 HRESULT hr = S_OK;
460 DWORD EscapeFlags;
461 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
462 INT nByteLen, state;
463 DWORD nLen;
465 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
466 pcchCanonicalized, dwFlags);
468 if(!pszUrl || !pszCanonicalized || !pcchCanonicalized)
469 return E_INVALIDARG;
471 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
472 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
474 if (dwFlags & URL_DONT_SIMPLIFY)
475 memcpy(lpszUrlCpy, pszUrl, nByteLen);
476 else {
479 * state =
480 * 0 initial 1,3
481 * 1 have 2[+] alnum 2,3
482 * 2 have scheme (found :) 4,6,3
483 * 3 failed (no location)
484 * 4 have // 5,3
485 * 5 have 1[+] alnum 6,3
486 * 6 have location (found /) save root location
489 wk1 = (LPWSTR)pszUrl;
490 wk2 = lpszUrlCpy;
491 state = 0;
492 while (*wk1) {
493 switch (state) {
494 case 0:
495 if (!isalnumW(*wk1)) {state = 3; break;}
496 *wk2++ = *wk1++;
497 if (!isalnumW(*wk1)) {state = 3; break;}
498 *wk2++ = *wk1++;
499 state = 1;
500 break;
501 case 1:
502 *wk2++ = *wk1;
503 if (*wk1++ == L':') state = 2;
504 break;
505 case 2:
506 if (*wk1 != L'/') {state = 3; break;}
507 *wk2++ = *wk1++;
508 if (*wk1 != L'/') {state = 6; break;}
509 *wk2++ = *wk1++;
510 state = 4;
511 break;
512 case 3:
513 strcpyW(wk2, wk1);
514 wk1 += strlenW(wk1);
515 wk2 += strlenW(wk2);
516 break;
517 case 4:
518 if (!isalnumW(*wk1) && (*wk1 != L'-') && (*wk1 != L'.')) {state = 3; break;}
519 while(isalnumW(*wk1) || (*wk1 == L'-') || (*wk1 == L'.')) *wk2++ = *wk1++;
520 state = 5;
521 break;
522 case 5:
523 if (*wk1 != L'/') {state = 3; break;}
524 *wk2++ = *wk1++;
525 state = 6;
526 break;
527 case 6:
528 /* Now at root location, cannot back up any more. */
529 /* "root" will point at the '/' */
530 root = wk2-1;
531 while (*wk1) {
532 TRACE("wk1=%c\n", (CHAR)*wk1);
533 mp = strchrW(wk1, L'/');
534 if (!mp) {
535 strcpyW(wk2, wk1);
536 wk1 += strlenW(wk1);
537 wk2 += strlenW(wk2);
538 continue;
540 nLen = mp - wk1 + 1;
541 strncpyW(wk2, wk1, nLen);
542 wk2 += nLen;
543 wk1 += nLen;
544 if (*wk1 == L'.') {
545 TRACE("found '/.'\n");
546 if (*(wk1+1) == L'/') {
547 /* case of /./ -> skip the ./ */
548 wk1 += 2;
550 else if (*(wk1+1) == L'.') {
551 /* found /.. look for next / */
552 TRACE("found '/..'\n");
553 if (*(wk1+2) == L'/' || *(wk1+2) == L'?' || *(wk1+2) == L'#' || *(wk1+2) == 0) {
554 /* case /../ -> need to backup wk2 */
555 TRACE("found '/../'\n");
556 *(wk2-1) = L'\0'; /* set end of string */
557 mp = strrchrW(root, L'/');
558 if (mp && (mp >= root)) {
559 /* found valid backup point */
560 wk2 = mp + 1;
561 if(*(wk1+2) != L'/')
562 wk1 += 2;
563 else
564 wk1 += 3;
566 else {
567 /* did not find point, restore '/' */
568 *(wk2-1) = L'/';
574 *wk2 = L'\0';
575 break;
576 default:
577 FIXME("how did we get here - state=%d\n", state);
578 return E_INVALIDARG;
581 *wk2 = L'\0';
582 TRACE("Simplified, orig <%s>, simple <%s>\n",
583 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
585 nLen = lstrlenW(lpszUrlCpy);
586 while ((nLen > 0) && ((lpszUrlCpy[nLen-1] == '\r')||(lpszUrlCpy[nLen-1] == '\n')))
587 lpszUrlCpy[--nLen]=0;
589 if(dwFlags & URL_UNESCAPE)
590 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
592 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
593 URL_ESCAPE_SPACES_ONLY |
594 URL_ESCAPE_PERCENT |
595 URL_DONT_ESCAPE_EXTRA_INFO |
596 URL_ESCAPE_SEGMENT_ONLY ))) {
597 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
598 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
599 EscapeFlags);
600 } else { /* No escaping needed, just copy the string */
601 nLen = lstrlenW(lpszUrlCpy);
602 if(nLen < *pcchCanonicalized)
603 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
604 else {
605 hr = E_POINTER;
606 nLen++;
608 *pcchCanonicalized = nLen;
611 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
613 if (hr == S_OK)
614 TRACE("result %s\n", debugstr_w(pszCanonicalized));
616 return hr;
619 /*************************************************************************
620 * UrlCombineA [SHLWAPI.@]
622 * Combine two Urls.
624 * PARAMS
625 * pszBase [I] Base Url
626 * pszRelative [I] Url to combine with pszBase
627 * pszCombined [O] Destination for combined Url
628 * pcchCombined [O] Destination for length of pszCombined
629 * dwFlags [I] URL_ flags from "shlwapi.h"
631 * RETURNS
632 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
633 * contains its length.
634 * Failure: An HRESULT error code indicating the error.
636 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
637 LPSTR pszCombined, LPDWORD pcchCombined,
638 DWORD dwFlags)
640 LPWSTR base, relative, combined;
641 DWORD ret, len, len2;
643 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
644 debugstr_a(pszBase),debugstr_a(pszRelative),
645 pcchCombined?*pcchCombined:0,dwFlags);
647 if(!pszBase || !pszRelative || !pcchCombined)
648 return E_INVALIDARG;
650 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
651 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
652 relative = base + INTERNET_MAX_URL_LENGTH;
653 combined = relative + INTERNET_MAX_URL_LENGTH;
655 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
656 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
657 len = *pcchCombined;
659 ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
660 if (ret != S_OK) {
661 *pcchCombined = len;
662 HeapFree(GetProcessHeap(), 0, base);
663 return ret;
666 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
667 if (len2 > *pcchCombined) {
668 *pcchCombined = len2;
669 HeapFree(GetProcessHeap(), 0, base);
670 return E_POINTER;
672 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
673 0, 0);
674 *pcchCombined = len2;
675 HeapFree(GetProcessHeap(), 0, base);
676 return S_OK;
679 /*************************************************************************
680 * UrlCombineW [SHLWAPI.@]
682 * See UrlCombineA.
684 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
685 LPWSTR pszCombined, LPDWORD pcchCombined,
686 DWORD dwFlags)
688 UNKNOWN_SHLWAPI_2 base, relative;
689 DWORD myflags, sizeloc = 0;
690 DWORD len, res1, res2, process_case = 0;
691 LPWSTR work, preliminary, mbase, mrelative;
692 static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
693 static const WCHAR single_slash[] = {'/','\0'};
694 HRESULT ret;
696 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
697 debugstr_w(pszBase),debugstr_w(pszRelative),
698 pcchCombined?*pcchCombined:0,dwFlags);
700 if(!pszBase || !pszRelative || !pcchCombined)
701 return E_INVALIDARG;
703 base.size = 24;
704 relative.size = 24;
706 /* Get space for duplicates of the input and the output */
707 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
708 sizeof(WCHAR));
709 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
710 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
711 *preliminary = L'\0';
713 /* Canonicalize the base input prior to looking for the scheme */
714 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
715 len = INTERNET_MAX_URL_LENGTH;
716 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
718 /* Canonicalize the relative input prior to looking for the scheme */
719 len = INTERNET_MAX_URL_LENGTH;
720 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
722 /* See if the base has a scheme */
723 res1 = ParseURLW(mbase, &base);
724 if (res1) {
725 /* if pszBase has no scheme, then return pszRelative */
726 TRACE("no scheme detected in Base\n");
727 process_case = 1;
729 else do {
731 /* get size of location field (if it exists) */
732 work = (LPWSTR)base.ap2;
733 sizeloc = 0;
734 if (*work++ == L'/') {
735 if (*work++ == L'/') {
736 /* At this point have start of location and
737 * it ends at next '/' or end of string.
739 while(*work && (*work != L'/')) work++;
740 sizeloc = (DWORD)(work - base.ap2);
744 /* Change .sizep2 to not have the last leaf in it,
745 * Note: we need to start after the location (if it exists)
747 work = strrchrW((base.ap2+sizeloc), L'/');
748 if (work) {
749 len = (DWORD)(work - base.ap2 + 1);
750 base.sizep2 = len;
753 * At this point:
754 * .ap2 points to location (starting with '//')
755 * .sizep2 length of location (above) and rest less the last
756 * leaf (if any)
757 * sizeloc length of location (above) up to but not including
758 * the last '/'
761 res2 = ParseURLW(mrelative, &relative);
762 if (res2) {
763 /* no scheme in pszRelative */
764 TRACE("no scheme detected in Relative\n");
765 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
766 relative.sizep2 = strlenW(mrelative);
767 if (*pszRelative == L':') {
768 /* case that is either left alone or uses pszBase */
769 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
770 process_case = 5;
771 break;
773 process_case = 1;
774 break;
776 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
777 /* case that becomes "file:///" */
778 strcpyW(preliminary, myfilestr);
779 process_case = 1;
780 break;
782 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
783 /* pszRelative has location and rest */
784 process_case = 3;
785 break;
787 if (*mrelative == L'/') {
788 /* case where pszRelative is root to location */
789 process_case = 4;
790 break;
792 process_case = (*base.ap2 == L'/') ? 5 : 3;
793 break;
796 /* handle cases where pszRelative has scheme */
797 if ((base.sizep1 == relative.sizep1) &&
798 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
800 /* since the schemes are the same */
801 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
802 /* case where pszRelative replaces location and following */
803 process_case = 3;
804 break;
806 if (*relative.ap2 == L'/') {
807 /* case where pszRelative is root to location */
808 process_case = 4;
809 break;
811 /* case where scheme is followed by document path */
812 process_case = 5;
813 break;
815 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
816 /* case where pszRelative replaces scheme, location,
817 * and following and handles PLUGGABLE
819 process_case = 2;
820 break;
822 process_case = 1;
823 break;
824 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
827 ret = S_OK;
828 switch (process_case) {
830 case 1: /*
831 * Return pszRelative appended to what ever is in pszCombined,
832 * (which may the string "file:///"
834 strcatW(preliminary, mrelative);
835 break;
837 case 2: /*
838 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
839 * and pszRelative starts with "//", then append a "/"
841 strcpyW(preliminary, mrelative);
842 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
843 URL_JustLocation(relative.ap2))
844 strcatW(preliminary, single_slash);
845 break;
847 case 3: /*
848 * Return the pszBase scheme with pszRelative. Basically
849 * keeps the scheme and replaces the domain and following.
851 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
852 work = preliminary + base.sizep1 + 1;
853 strcpyW(work, relative.ap2);
854 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
855 URL_JustLocation(relative.ap2))
856 strcatW(work, single_slash);
857 break;
859 case 4: /*
860 * Return the pszBase scheme and location but everything
861 * after the location is pszRelative. (Replace document
862 * from root on.)
864 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
865 work = preliminary + base.sizep1 + 1 + sizeloc;
866 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
867 *(work++) = L'/';
868 strcpyW(work, relative.ap2);
869 break;
871 case 5: /*
872 * Return the pszBase without its document (if any) and
873 * append pszRelative after its scheme.
875 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
876 work = preliminary + base.sizep1+1+base.sizep2 - 1;
877 if (*work++ != L'/')
878 *(work++) = L'/';
879 strcpyW(work, relative.ap2);
880 break;
882 default:
883 FIXME("How did we get here????? process_case=%ld\n", process_case);
884 ret = E_INVALIDARG;
887 if (ret == S_OK) {
888 /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
889 ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, dwFlags);
890 if(SUCCEEDED(ret) && pszCombined) {
891 lstrcpyW(pszCombined, mrelative);
893 TRACE("return-%ld len=%ld, %s\n",
894 process_case, *pcchCombined, debugstr_w(pszCombined));
896 HeapFree(GetProcessHeap(), 0, preliminary);
897 return ret;
900 /*************************************************************************
901 * UrlEscapeA [SHLWAPI.@]
903 * Converts unsafe characters in a Url into escape sequences.
905 * PARAMS
906 * pszUrl [I] Url to modify
907 * pszEscaped [O] Destination for modified Url
908 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
909 * dwFlags [I] URL_ flags from "shlwapi.h"
911 * RETURNS
912 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
913 * contains its length.
914 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
915 * pcchEscaped is set to the required length.
917 * Converts unsafe characters into their escape sequences.
919 * NOTES
920 * - By default this function stops converting at the first '?' or
921 * '#' character (MSDN does not document this).
922 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
923 * converted, but the conversion continues past a '?' or '#'.
924 * - Note that this function did not work well (or at all) in shlwapi version 4.
926 * BUGS
927 * Only the following flags are implemented:
928 *| URL_ESCAPE_SPACES_ONLY
929 *| URL_DONT_ESCAPE_EXTRA_INFO
930 *| URL_ESCAPE_SEGMENT_ONLY
931 *| URL_ESCAPE_PERCENT
933 HRESULT WINAPI UrlEscapeA(
934 LPCSTR pszUrl,
935 LPSTR pszEscaped,
936 LPDWORD pcchEscaped,
937 DWORD dwFlags)
939 LPCSTR src;
940 DWORD needed = 0, ret;
941 BOOL stop_escaping = FALSE;
942 char next[3], *dst = pszEscaped;
943 INT len;
945 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
946 pcchEscaped?*pcchEscaped:0, dwFlags);
948 if(!pszUrl || !pszEscaped || !pcchEscaped)
949 return E_INVALIDARG;
951 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
952 URL_ESCAPE_SEGMENT_ONLY |
953 URL_DONT_ESCAPE_EXTRA_INFO |
954 URL_ESCAPE_PERCENT))
955 FIXME("Unimplemented flags: %08lx\n", dwFlags);
957 /* fix up flags */
958 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
959 /* if SPACES_ONLY specified, reset the other controls */
960 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
961 URL_ESCAPE_PERCENT |
962 URL_ESCAPE_SEGMENT_ONLY);
964 else
965 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
966 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
968 for(src = pszUrl; *src; src++) {
969 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
970 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
971 (*src == '#' || *src == '?'))
972 stop_escaping = TRUE;
974 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
975 /* TRACE("escaping %c\n", *src); */
976 next[0] = '%';
977 next[1] = hexDigits[(*src >> 4) & 0xf];
978 next[2] = hexDigits[*src & 0xf];
979 len = 3;
980 } else {
981 /* TRACE("passing %c\n", *src); */
982 next[0] = *src;
983 len = 1;
986 if(needed + len <= *pcchEscaped) {
987 memcpy(dst, next, len);
988 dst += len;
990 needed += len;
993 if(needed < *pcchEscaped) {
994 *dst = '\0';
995 ret = S_OK;
996 } else {
997 needed++; /* add one for the '\0' */
998 ret = E_POINTER;
1000 *pcchEscaped = needed;
1001 return ret;
1004 /*************************************************************************
1005 * UrlEscapeW [SHLWAPI.@]
1007 * See UrlEscapeA.
1009 HRESULT WINAPI UrlEscapeW(
1010 LPCWSTR pszUrl,
1011 LPWSTR pszEscaped,
1012 LPDWORD pcchEscaped,
1013 DWORD dwFlags)
1015 LPCWSTR src;
1016 DWORD needed = 0, ret;
1017 BOOL stop_escaping = FALSE;
1018 WCHAR next[5], *dst = pszEscaped;
1019 INT len;
1021 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
1022 pcchEscaped, dwFlags);
1024 if(!pszUrl || !pszEscaped || !pcchEscaped)
1025 return E_INVALIDARG;
1027 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1028 URL_ESCAPE_SEGMENT_ONLY |
1029 URL_DONT_ESCAPE_EXTRA_INFO |
1030 URL_ESCAPE_PERCENT))
1031 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1033 /* fix up flags */
1034 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1035 /* if SPACES_ONLY specified, reset the other controls */
1036 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1037 URL_ESCAPE_PERCENT |
1038 URL_ESCAPE_SEGMENT_ONLY);
1040 else
1041 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1042 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1044 for(src = pszUrl; *src; src++) {
1046 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1047 * (*src == L'#' || *src == L'?'))
1048 * stop_escaping = TRUE;
1050 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1051 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1052 (*src == L'#' || *src == L'?'))
1053 stop_escaping = TRUE;
1055 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1056 /* TRACE("escaping %c\n", *src); */
1057 next[0] = L'%';
1059 * I would have assumed that the W form would escape
1060 * the character with 4 hex digits (or even 8),
1061 * however, experiments show that native shlwapi escapes
1062 * with only 2 hex digits.
1063 * next[1] = hexDigits[(*src >> 12) & 0xf];
1064 * next[2] = hexDigits[(*src >> 8) & 0xf];
1065 * next[3] = hexDigits[(*src >> 4) & 0xf];
1066 * next[4] = hexDigits[*src & 0xf];
1067 * len = 5;
1069 next[1] = hexDigits[(*src >> 4) & 0xf];
1070 next[2] = hexDigits[*src & 0xf];
1071 len = 3;
1072 } else {
1073 /* TRACE("passing %c\n", *src); */
1074 next[0] = *src;
1075 len = 1;
1078 if(needed + len <= *pcchEscaped) {
1079 memcpy(dst, next, len*sizeof(WCHAR));
1080 dst += len;
1082 needed += len;
1085 if(needed < *pcchEscaped) {
1086 *dst = L'\0';
1087 ret = S_OK;
1088 } else {
1089 needed++; /* add one for the '\0' */
1090 ret = E_POINTER;
1092 *pcchEscaped = needed;
1093 return ret;
1097 /*************************************************************************
1098 * UrlUnescapeA [SHLWAPI.@]
1100 * Converts Url escape sequences back to ordinary characters.
1102 * PARAMS
1103 * pszUrl [I/O] Url to convert
1104 * pszUnescaped [O] Destination for converted Url
1105 * pcchUnescaped [I/O] Size of output string
1106 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1108 * RETURNS
1109 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1110 * dwFlags includes URL_ESCAPE_INPLACE.
1111 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1112 * this case pcchUnescaped is set to the size required.
1113 * NOTES
1114 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1115 * the first occurrence of either a '?' or '#' character.
1117 HRESULT WINAPI UrlUnescapeA(
1118 LPSTR pszUrl,
1119 LPSTR pszUnescaped,
1120 LPDWORD pcchUnescaped,
1121 DWORD dwFlags)
1123 char *dst, next;
1124 LPCSTR src;
1125 HRESULT ret;
1126 DWORD needed;
1127 BOOL stop_unescaping = FALSE;
1129 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1130 pcchUnescaped, dwFlags);
1132 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1133 return E_INVALIDARG;
1135 if(dwFlags & URL_UNESCAPE_INPLACE)
1136 dst = pszUrl;
1137 else
1138 dst = pszUnescaped;
1140 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1141 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1142 (*src == '#' || *src == '?')) {
1143 stop_unescaping = TRUE;
1144 next = *src;
1145 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1146 && stop_unescaping == FALSE) {
1147 INT ih;
1148 char buf[3];
1149 memcpy(buf, src + 1, 2);
1150 buf[2] = '\0';
1151 ih = strtol(buf, NULL, 16);
1152 next = (CHAR) ih;
1153 src += 2; /* Advance to end of escape */
1154 } else
1155 next = *src;
1157 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1158 *dst++ = next;
1161 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1162 *dst = '\0';
1163 ret = S_OK;
1164 } else {
1165 needed++; /* add one for the '\0' */
1166 ret = E_POINTER;
1168 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1169 *pcchUnescaped = needed;
1171 if (ret == S_OK) {
1172 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1173 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1176 return ret;
1179 /*************************************************************************
1180 * UrlUnescapeW [SHLWAPI.@]
1182 * See UrlUnescapeA.
1184 HRESULT WINAPI UrlUnescapeW(
1185 LPWSTR pszUrl,
1186 LPWSTR pszUnescaped,
1187 LPDWORD pcchUnescaped,
1188 DWORD dwFlags)
1190 WCHAR *dst, next;
1191 LPCWSTR src;
1192 HRESULT ret;
1193 DWORD needed;
1194 BOOL stop_unescaping = FALSE;
1196 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1197 pcchUnescaped, dwFlags);
1199 if(!pszUrl || !pszUnescaped || !pcchUnescaped)
1200 return E_INVALIDARG;
1202 if(dwFlags & URL_UNESCAPE_INPLACE)
1203 dst = pszUrl;
1204 else
1205 dst = pszUnescaped;
1207 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1208 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1209 (*src == L'#' || *src == L'?')) {
1210 stop_unescaping = TRUE;
1211 next = *src;
1212 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1213 && stop_unescaping == FALSE) {
1214 INT ih;
1215 WCHAR buf[3];
1216 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1217 buf[2] = L'\0';
1218 ih = StrToIntW(buf);
1219 next = (WCHAR) ih;
1220 src += 2; /* Advance to end of escape */
1221 } else
1222 next = *src;
1224 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1225 *dst++ = next;
1228 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1229 *dst = L'\0';
1230 ret = S_OK;
1231 } else {
1232 needed++; /* add one for the '\0' */
1233 ret = E_POINTER;
1235 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1236 *pcchUnescaped = needed;
1238 if (ret == S_OK) {
1239 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1240 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1243 return ret;
1246 /*************************************************************************
1247 * UrlGetLocationA [SHLWAPI.@]
1249 * Get the location from a Url.
1251 * PARAMS
1252 * pszUrl [I] Url to get the location from
1254 * RETURNS
1255 * A pointer to the start of the location in pszUrl, or NULL if there is
1256 * no location.
1258 * NOTES
1259 * - MSDN erroneously states that "The location is the segment of the Url
1260 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1261 * stop at '?' and always return a NULL in this case.
1262 * - MSDN also erroneously states that "If a file URL has a query string,
1263 * the returned string is the query string". In all tested cases, if the
1264 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1265 *| Result Url
1266 *| ------ ---
1267 *| NULL file://aa/b/cd#hohoh
1268 *| #hohoh http://aa/b/cd#hohoh
1269 *| NULL fi://aa/b/cd#hohoh
1270 *| #hohoh ff://aa/b/cd#hohoh
1272 LPCSTR WINAPI UrlGetLocationA(
1273 LPCSTR pszUrl)
1275 UNKNOWN_SHLWAPI_1 base;
1276 DWORD res1;
1278 base.size = 24;
1279 res1 = ParseURLA(pszUrl, &base);
1280 if (res1) return NULL; /* invalid scheme */
1282 /* if scheme is file: then never return pointer */
1283 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1285 /* Look for '#' and return its addr */
1286 return strchr(base.ap2, '#');
1289 /*************************************************************************
1290 * UrlGetLocationW [SHLWAPI.@]
1292 * See UrlGetLocationA.
1294 LPCWSTR WINAPI UrlGetLocationW(
1295 LPCWSTR pszUrl)
1297 UNKNOWN_SHLWAPI_2 base;
1298 DWORD res1;
1300 base.size = 24;
1301 res1 = ParseURLW(pszUrl, &base);
1302 if (res1) return NULL; /* invalid scheme */
1304 /* if scheme is file: then never return pointer */
1305 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1307 /* Look for '#' and return its addr */
1308 return strchrW(base.ap2, L'#');
1311 /*************************************************************************
1312 * UrlCompareA [SHLWAPI.@]
1314 * Compare two Urls.
1316 * PARAMS
1317 * pszUrl1 [I] First Url to compare
1318 * pszUrl2 [I] Url to compare to pszUrl1
1319 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1321 * RETURNS
1322 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1323 * than, equal to, or less than pszUrl1 respectively.
1325 INT WINAPI UrlCompareA(
1326 LPCSTR pszUrl1,
1327 LPCSTR pszUrl2,
1328 BOOL fIgnoreSlash)
1330 INT ret, len, len1, len2;
1332 if (!fIgnoreSlash)
1333 return strcmp(pszUrl1, pszUrl2);
1334 len1 = strlen(pszUrl1);
1335 if (pszUrl1[len1-1] == '/') len1--;
1336 len2 = strlen(pszUrl2);
1337 if (pszUrl2[len2-1] == '/') len2--;
1338 if (len1 == len2)
1339 return strncmp(pszUrl1, pszUrl2, len1);
1340 len = min(len1, len2);
1341 ret = strncmp(pszUrl1, pszUrl2, len);
1342 if (ret) return ret;
1343 if (len1 > len2) return 1;
1344 return -1;
1347 /*************************************************************************
1348 * UrlCompareW [SHLWAPI.@]
1350 * See UrlCompareA.
1352 INT WINAPI UrlCompareW(
1353 LPCWSTR pszUrl1,
1354 LPCWSTR pszUrl2,
1355 BOOL fIgnoreSlash)
1357 INT ret;
1358 size_t len, len1, len2;
1360 if (!fIgnoreSlash)
1361 return strcmpW(pszUrl1, pszUrl2);
1362 len1 = strlenW(pszUrl1);
1363 if (pszUrl1[len1-1] == '/') len1--;
1364 len2 = strlenW(pszUrl2);
1365 if (pszUrl2[len2-1] == '/') len2--;
1366 if (len1 == len2)
1367 return strncmpW(pszUrl1, pszUrl2, len1);
1368 len = min(len1, len2);
1369 ret = strncmpW(pszUrl1, pszUrl2, len);
1370 if (ret) return ret;
1371 if (len1 > len2) return 1;
1372 return -1;
1375 /*************************************************************************
1376 * HashData [SHLWAPI.@]
1378 * Hash an input block into a variable sized digest.
1380 * PARAMS
1381 * lpSrc [I] Input block
1382 * nSrcLen [I] Length of lpSrc
1383 * lpDest [I] Output for hash digest
1384 * nDestLen [I] Length of lpDest
1386 * RETURNS
1387 * Success: TRUE. lpDest is filled with the computed hash value.
1388 * Failure: FALSE, if any argument is invalid.
1390 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1391 unsigned char *lpDest, DWORD nDestLen)
1393 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1395 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1396 IsBadWritePtr(lpDest, nDestLen))
1397 return E_INVALIDARG;
1399 while (destCount >= 0)
1401 lpDest[destCount] = (destCount & 0xff);
1402 destCount--;
1405 while (srcCount >= 0)
1407 destCount = nDestLen - 1;
1408 while (destCount >= 0)
1410 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1411 destCount--;
1413 srcCount--;
1415 return S_OK;
1418 /*************************************************************************
1419 * UrlHashA [SHLWAPI.@]
1421 * Produce a Hash from a Url.
1423 * PARAMS
1424 * pszUrl [I] Url to hash
1425 * lpDest [O] Destinationh for hash
1426 * nDestLen [I] Length of lpDest
1428 * RETURNS
1429 * Success: S_OK. lpDest is filled with the computed hash value.
1430 * Failure: E_INVALIDARG, if any argument is invalid.
1432 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1434 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1435 return E_INVALIDARG;
1437 HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1438 return S_OK;
1441 /*************************************************************************
1442 * UrlHashW [SHLWAPI.@]
1444 * See UrlHashA.
1446 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1448 char szUrl[MAX_PATH];
1450 TRACE("(%s,%p,%ld)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1452 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1453 return E_INVALIDARG;
1455 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1456 * return the same digests for the same URL.
1458 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1459 HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1460 return S_OK;
1463 /*************************************************************************
1464 * UrlApplySchemeA [SHLWAPI.@]
1466 * Apply a scheme to a Url.
1468 * PARAMS
1469 * pszIn [I] Url to apply scheme to
1470 * pszOut [O] Destination for modified Url
1471 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1472 * dwFlags [I] URL_ flags from "shlwapi.h"
1474 * RETURNS
1475 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1476 * Failure: An HRESULT error code describing the error.
1478 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1480 LPWSTR in, out;
1481 DWORD ret, len, len2;
1483 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1484 debugstr_a(pszIn), *pcchOut, dwFlags);
1486 in = HeapAlloc(GetProcessHeap(), 0,
1487 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1488 out = in + INTERNET_MAX_URL_LENGTH;
1490 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1491 len = INTERNET_MAX_URL_LENGTH;
1493 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1494 if ((ret != S_OK) && (ret != S_FALSE)) {
1495 HeapFree(GetProcessHeap(), 0, in);
1496 return ret;
1499 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1500 if (len2 > *pcchOut) {
1501 *pcchOut = len2;
1502 HeapFree(GetProcessHeap(), 0, in);
1503 return E_POINTER;
1505 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1506 *pcchOut = len2;
1507 HeapFree(GetProcessHeap(), 0, in);
1508 return ret;
1511 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1513 HKEY newkey;
1514 BOOL j;
1515 INT index;
1516 DWORD value_len, data_len, dwType, i;
1517 WCHAR reg_path[MAX_PATH];
1518 WCHAR value[MAX_PATH], data[MAX_PATH];
1519 WCHAR Wxx, Wyy;
1521 MultiByteToWideChar(0, 0,
1522 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1523 -1, reg_path, MAX_PATH);
1524 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1525 index = 0;
1526 while(value_len = data_len = MAX_PATH,
1527 RegEnumValueW(newkey, index, value, &value_len,
1528 0, &dwType, (LPVOID)data, &data_len) == 0) {
1529 TRACE("guess %d %s is %s\n",
1530 index, debugstr_w(value), debugstr_w(data));
1532 j = FALSE;
1533 for(i=0; i<value_len; i++) {
1534 Wxx = pszIn[i];
1535 Wyy = value[i];
1536 /* remember that TRUE is not-equal */
1537 j = ChrCmpIW(Wxx, Wyy);
1538 if (j) break;
1540 if ((i == value_len) && !j) {
1541 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1542 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1543 RegCloseKey(newkey);
1544 return E_POINTER;
1546 strcpyW(pszOut, data);
1547 strcatW(pszOut, pszIn);
1548 *pcchOut = strlenW(pszOut);
1549 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1550 RegCloseKey(newkey);
1551 return S_OK;
1553 index++;
1555 RegCloseKey(newkey);
1556 return -1;
1559 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1561 HKEY newkey;
1562 DWORD data_len, dwType;
1563 WCHAR reg_path[MAX_PATH];
1564 WCHAR value[MAX_PATH], data[MAX_PATH];
1566 /* get and prepend default */
1567 MultiByteToWideChar(0, 0,
1568 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1569 -1, reg_path, MAX_PATH);
1570 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1571 data_len = MAX_PATH;
1572 value[0] = L'@';
1573 value[1] = L'\0';
1574 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1575 RegCloseKey(newkey);
1576 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1577 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1578 return E_POINTER;
1580 strcpyW(pszOut, data);
1581 strcatW(pszOut, pszIn);
1582 *pcchOut = strlenW(pszOut);
1583 TRACE("used default %s\n", debugstr_w(pszOut));
1584 return S_OK;
1587 /*************************************************************************
1588 * UrlApplySchemeW [SHLWAPI.@]
1590 * See UrlApplySchemeA.
1592 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1594 UNKNOWN_SHLWAPI_2 in_scheme;
1595 DWORD res1;
1596 HRESULT ret;
1598 TRACE("(in %s, out size %ld, flags %08lx)\n",
1599 debugstr_w(pszIn), *pcchOut, dwFlags);
1601 if (dwFlags & URL_APPLY_GUESSFILE) {
1602 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1603 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1604 strcpyW(pszOut, pszIn);
1605 *pcchOut = strlenW(pszOut);
1606 return S_FALSE;
1609 in_scheme.size = 24;
1610 /* See if the base has a scheme */
1611 res1 = ParseURLW(pszIn, &in_scheme);
1612 if (res1) {
1613 /* no scheme in input, need to see if we need to guess */
1614 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1615 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1616 return ret;
1619 else {
1620 /* we have a scheme, see if valid (known scheme) */
1621 if (in_scheme.fcncde) {
1622 /* have valid scheme, so just copy and exit */
1623 if (strlenW(pszIn) + 1 > *pcchOut) {
1624 *pcchOut = strlenW(pszIn) + 1;
1625 return E_POINTER;
1627 strcpyW(pszOut, pszIn);
1628 *pcchOut = strlenW(pszOut);
1629 TRACE("valid scheme, returing copy\n");
1630 return S_OK;
1634 /* If we are here, then either invalid scheme,
1635 * or no scheme and can't/failed guess.
1637 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1638 ((res1 != 0)) ) &&
1639 (dwFlags & URL_APPLY_DEFAULT)) {
1640 /* find and apply default scheme */
1641 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1644 /* just copy and give proper return code */
1645 if (strlenW(pszIn) + 1 > *pcchOut) {
1646 *pcchOut = strlenW(pszIn) + 1;
1647 return E_POINTER;
1649 strcpyW(pszOut, pszIn);
1650 *pcchOut = strlenW(pszOut);
1651 TRACE("returning copy, left alone\n");
1652 return S_FALSE;
1655 /*************************************************************************
1656 * UrlIsA [SHLWAPI.@]
1658 * Determine if a Url is of a certain class.
1660 * PARAMS
1661 * pszUrl [I] Url to check
1662 * Urlis [I] URLIS_ constant from "shlwapi.h"
1664 * RETURNS
1665 * TRUE if pszUrl belongs to the class type in Urlis.
1666 * FALSE Otherwise.
1668 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1670 UNKNOWN_SHLWAPI_1 base;
1671 DWORD res1;
1672 LPCSTR last;
1674 switch (Urlis) {
1676 case URLIS_OPAQUE:
1677 base.size = 24;
1678 res1 = ParseURLA(pszUrl, &base);
1679 if (res1) return FALSE; /* invalid scheme */
1680 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1681 /* has scheme followed by 2 '/' */
1682 return FALSE;
1683 return TRUE;
1685 case URLIS_FILEURL:
1686 return !StrCmpNA("file://", pszUrl, 7);
1688 case URLIS_DIRECTORY:
1689 last = pszUrl + strlen(pszUrl) - 1;
1690 return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1692 case URLIS_URL:
1693 case URLIS_NOHISTORY:
1694 case URLIS_APPLIABLE:
1695 case URLIS_HASQUERY:
1696 default:
1697 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1699 return FALSE;
1702 /*************************************************************************
1703 * UrlIsW [SHLWAPI.@]
1705 * See UrlIsA.
1707 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1709 static const WCHAR stemp[] = { 'f','i','l','e',':','/','/',0 };
1710 UNKNOWN_SHLWAPI_2 base;
1711 DWORD res1;
1712 LPCWSTR last;
1714 switch (Urlis) {
1716 case URLIS_OPAQUE:
1717 base.size = 24;
1718 res1 = ParseURLW(pszUrl, &base);
1719 if (res1) return FALSE; /* invalid scheme */
1720 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1721 /* has scheme followed by 2 '/' */
1722 return FALSE;
1723 return TRUE;
1725 case URLIS_FILEURL:
1726 return !strncmpW(stemp, pszUrl, 7);
1728 case URLIS_DIRECTORY:
1729 last = pszUrl + strlenW(pszUrl) - 1;
1730 return (last >= pszUrl && (*last == '/' || *last == '\\'));
1732 case URLIS_URL:
1733 case URLIS_NOHISTORY:
1734 case URLIS_APPLIABLE:
1735 case URLIS_HASQUERY:
1736 default:
1737 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1739 return FALSE;
1742 /*************************************************************************
1743 * UrlIsNoHistoryA [SHLWAPI.@]
1745 * Determine if a Url should not be stored in the users history list.
1747 * PARAMS
1748 * pszUrl [I] Url to check
1750 * RETURNS
1751 * TRUE, if pszUrl should be excluded from the history list,
1752 * FALSE otherwise.
1754 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1756 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1759 /*************************************************************************
1760 * UrlIsNoHistoryW [SHLWAPI.@]
1762 * See UrlIsNoHistoryA.
1764 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1766 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1769 /*************************************************************************
1770 * UrlIsOpaqueA [SHLWAPI.@]
1772 * Determine if a Url is opaque.
1774 * PARAMS
1775 * pszUrl [I] Url to check
1777 * RETURNS
1778 * TRUE if pszUrl is opaque,
1779 * FALSE Otherwise.
1781 * NOTES
1782 * An opaque Url is one that does not start with "<protocol>://".
1784 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1786 return UrlIsA(pszUrl, URLIS_OPAQUE);
1789 /*************************************************************************
1790 * UrlIsOpaqueW [SHLWAPI.@]
1792 * See UrlIsOpaqueA.
1794 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1796 return UrlIsW(pszUrl, URLIS_OPAQUE);
1799 /*************************************************************************
1800 * Scans for characters of type "type" and when not matching found,
1801 * returns pointer to it and length in size.
1803 * Characters tested based on RFC 1738
1805 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1807 static DWORD alwayszero = 0;
1808 BOOL cont = TRUE;
1810 *size = 0;
1812 switch(type){
1814 case SCHEME:
1815 while (cont) {
1816 if ( (islowerW(*start) && isalphaW(*start)) ||
1817 isdigitW(*start) ||
1818 (*start == L'+') ||
1819 (*start == L'-') ||
1820 (*start == L'.')) {
1821 start++;
1822 (*size)++;
1824 else
1825 cont = FALSE;
1827 break;
1829 case USERPASS:
1830 while (cont) {
1831 if ( isalphaW(*start) ||
1832 isdigitW(*start) ||
1833 /* user/password only characters */
1834 (*start == L';') ||
1835 (*start == L'?') ||
1836 (*start == L'&') ||
1837 (*start == L'=') ||
1838 /* *extra* characters */
1839 (*start == L'!') ||
1840 (*start == L'*') ||
1841 (*start == L'\'') ||
1842 (*start == L'(') ||
1843 (*start == L')') ||
1844 (*start == L',') ||
1845 /* *safe* characters */
1846 (*start == L'$') ||
1847 (*start == L'_') ||
1848 (*start == L'+') ||
1849 (*start == L'-') ||
1850 (*start == L'.')) {
1851 start++;
1852 (*size)++;
1853 } else if (*start == L'%') {
1854 if (isxdigitW(*(start+1)) &&
1855 isxdigitW(*(start+2))) {
1856 start += 3;
1857 *size += 3;
1858 } else
1859 cont = FALSE;
1860 } else
1861 cont = FALSE;
1863 break;
1865 case PORT:
1866 while (cont) {
1867 if (isdigitW(*start)) {
1868 start++;
1869 (*size)++;
1871 else
1872 cont = FALSE;
1874 break;
1876 case HOST:
1877 while (cont) {
1878 if (isalnumW(*start) ||
1879 (*start == L'-') ||
1880 (*start == L'.') ) {
1881 start++;
1882 (*size)++;
1884 else
1885 cont = FALSE;
1887 break;
1888 default:
1889 FIXME("unknown type %d\n", type);
1890 return (LPWSTR)&alwayszero;
1892 /* TRACE("scanned %ld characters next char %p<%c>\n",
1893 *size, start, *start); */
1894 return start;
1897 /*************************************************************************
1898 * Attempt to parse URL into pieces.
1900 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1902 LPCWSTR work;
1904 memset(pl, 0, sizeof(WINE_PARSE_URL));
1905 pl->pScheme = pszUrl;
1906 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1907 if (!*work || (*work != L':')) goto ErrorExit;
1908 work++;
1909 if ((*work != L'/') || (*(work+1) != L'/')) goto ErrorExit;
1910 pl->pUserName = work + 2;
1911 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1912 if (*work == L':' ) {
1913 /* parse password */
1914 work++;
1915 pl->pPassword = work;
1916 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1917 if (*work != L'@') {
1918 /* what we just parsed must be the hostname and port
1919 * so reset pointers and clear then let it parse */
1920 pl->szUserName = pl->szPassword = 0;
1921 work = pl->pUserName - 1;
1922 pl->pUserName = pl->pPassword = 0;
1924 } else if (*work == L'@') {
1925 /* no password */
1926 pl->szPassword = 0;
1927 pl->pPassword = 0;
1928 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1929 /* what was parsed was hostname, so reset pointers and let it parse */
1930 pl->szUserName = pl->szPassword = 0;
1931 work = pl->pUserName - 1;
1932 pl->pUserName = pl->pPassword = 0;
1933 } else goto ErrorExit;
1935 /* now start parsing hostname or hostnumber */
1936 work++;
1937 pl->pHostName = work;
1938 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1939 if (*work == L':') {
1940 /* parse port */
1941 work++;
1942 pl->pPort = work;
1943 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1945 if (*work == L'/') {
1946 /* see if query string */
1947 pl->pQuery = strchrW(work, L'?');
1948 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1950 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1951 pl->pScheme, pl->szScheme,
1952 pl->pUserName, pl->szUserName,
1953 pl->pPassword, pl->szPassword,
1954 pl->pHostName, pl->szHostName,
1955 pl->pPort, pl->szPort,
1956 pl->pQuery, pl->szQuery);
1957 return S_OK;
1958 ErrorExit:
1959 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1960 return E_INVALIDARG;
1963 /*************************************************************************
1964 * UrlGetPartA [SHLWAPI.@]
1966 * Retrieve part of a Url.
1968 * PARAMS
1969 * pszIn [I] Url to parse
1970 * pszOut [O] Destination for part of pszIn requested
1971 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1972 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1973 * dwFlags [I] URL_ flags from "shlwapi.h"
1975 * RETURNS
1976 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1977 * Failure: An HRESULT error code describing the error.
1979 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1980 DWORD dwPart, DWORD dwFlags)
1982 LPWSTR in, out;
1983 DWORD ret, len, len2;
1985 in = HeapAlloc(GetProcessHeap(), 0,
1986 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1987 out = in + INTERNET_MAX_URL_LENGTH;
1989 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1991 len = INTERNET_MAX_URL_LENGTH;
1992 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1994 if (ret != S_OK) {
1995 HeapFree(GetProcessHeap(), 0, in);
1996 return ret;
1999 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2000 if (len2 > *pcchOut) {
2001 *pcchOut = len2;
2002 HeapFree(GetProcessHeap(), 0, in);
2003 return E_POINTER;
2005 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2006 *pcchOut = len2;
2007 HeapFree(GetProcessHeap(), 0, in);
2008 return S_OK;
2011 /*************************************************************************
2012 * UrlGetPartW [SHLWAPI.@]
2014 * See UrlGetPartA.
2016 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2017 DWORD dwPart, DWORD dwFlags)
2019 WINE_PARSE_URL pl;
2020 HRESULT ret;
2021 DWORD size, schsize;
2022 LPCWSTR addr, schaddr;
2023 LPWSTR work;
2025 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2026 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2028 ret = URL_ParseUrl(pszIn, &pl);
2029 if (!ret) {
2030 schaddr = pl.pScheme;
2031 schsize = pl.szScheme;
2033 switch (dwPart) {
2034 case URL_PART_SCHEME:
2035 if (!pl.szScheme) return E_INVALIDARG;
2036 addr = pl.pScheme;
2037 size = pl.szScheme;
2038 break;
2040 case URL_PART_HOSTNAME:
2041 if (!pl.szHostName) return E_INVALIDARG;
2042 addr = pl.pHostName;
2043 size = pl.szHostName;
2044 break;
2046 case URL_PART_USERNAME:
2047 if (!pl.szUserName) return E_INVALIDARG;
2048 addr = pl.pUserName;
2049 size = pl.szUserName;
2050 break;
2052 case URL_PART_PASSWORD:
2053 if (!pl.szPassword) return E_INVALIDARG;
2054 addr = pl.pPassword;
2055 size = pl.szPassword;
2056 break;
2058 case URL_PART_PORT:
2059 if (!pl.szPort) return E_INVALIDARG;
2060 addr = pl.pPort;
2061 size = pl.szPort;
2062 break;
2064 case URL_PART_QUERY:
2065 if (!pl.szQuery) return E_INVALIDARG;
2066 addr = pl.pQuery;
2067 size = pl.szQuery;
2068 break;
2070 default:
2071 return E_INVALIDARG;
2074 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2075 if (*pcchOut < size + schsize + 2) {
2076 *pcchOut = size + schsize + 2;
2077 return E_POINTER;
2079 strncpyW(pszOut, schaddr, schsize);
2080 work = pszOut + schsize;
2081 *work = L':';
2082 strncpyW(work+1, addr, size);
2083 *pcchOut = size + schsize + 1;
2084 work += (size + 1);
2085 *work = L'\0';
2087 else {
2088 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2089 strncpyW(pszOut, addr, size);
2090 *pcchOut = size;
2091 work = pszOut + size;
2092 *work = L'\0';
2094 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2096 return ret;
2099 /*************************************************************************
2100 * PathIsURLA [SHLWAPI.@]
2102 * Check if the given path is a Url.
2104 * PARAMS
2105 * lpszPath [I] Path to check.
2107 * RETURNS
2108 * TRUE if lpszPath is a Url.
2109 * FALSE if lpszPath is NULL or not a Url.
2111 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2113 UNKNOWN_SHLWAPI_1 base;
2114 DWORD res1;
2116 if (!lpstrPath || !*lpstrPath) return FALSE;
2118 /* get protocol */
2119 base.size = sizeof(base);
2120 res1 = ParseURLA(lpstrPath, &base);
2121 return (base.fcncde > 0);
2124 /*************************************************************************
2125 * PathIsURLW [SHLWAPI.@]
2127 * See PathIsURLA.
2129 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2131 UNKNOWN_SHLWAPI_2 base;
2132 DWORD res1;
2134 if (!lpstrPath || !*lpstrPath) return FALSE;
2136 /* get protocol */
2137 base.size = sizeof(base);
2138 res1 = ParseURLW(lpstrPath, &base);
2139 return (base.fcncde > 0);
2142 /*************************************************************************
2143 * UrlCreateFromPathA [SHLWAPI.@]
2145 * Create a Url from a file path.
2147 * PARAMS
2148 * pszPath [I] Path to convert
2149 * pszUrl [O] Destination for the converted Url
2150 * pcchUrl [I/O] Length of pszUrl
2151 * dwReserved [I] Reserved, must be 0
2153 * RETURNS
2154 * Success: S_OK. pszUrl contains the converted path.
2155 * Failure: An HRESULT error code.
2157 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2159 DWORD nCharBeforeColon = 0;
2160 DWORD nSlashes = 0;
2161 DWORD dwChRequired = 0;
2162 LPSTR pszNewUrl = NULL;
2163 LPCSTR pszConstPointer = NULL;
2164 LPSTR pszPointer = NULL;
2165 DWORD i;
2166 HRESULT ret;
2168 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2170 /* Validate arguments */
2171 if (dwReserved != 0)
2173 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2174 return E_INVALIDARG;
2176 if (!pszUrl || !pcchUrl || !pszUrl)
2178 ERR("Invalid argument\n");
2179 return E_INVALIDARG;
2182 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2184 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2185 *pszConstPointer == '.' || *pszConstPointer == '-')
2186 nCharBeforeColon++;
2187 else break;
2189 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2191 dwChRequired = lstrlenA(pszPath);
2192 if (dwChRequired > *pcchUrl)
2194 *pcchUrl = dwChRequired;
2195 return E_POINTER;
2197 else
2199 *pcchUrl = dwChRequired;
2200 StrCpyA(pszUrl, pszPath);
2201 return S_FALSE;
2204 /* then must need converting to file: format */
2206 /* Strip off leading slashes */
2207 while (*pszPath == '\\' || *pszPath == '/')
2209 pszPath++;
2210 nSlashes++;
2213 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2214 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2215 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2216 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2217 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2218 TRACE("%ld\n", dwChRequired);
2219 if (ret != E_POINTER && FAILED(ret))
2220 return ret;
2221 dwChRequired += 5; /* "file:" */
2222 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2224 dwChRequired += 3; /* "///" */
2225 nSlashes = 3;
2227 else
2228 switch (nSlashes)
2230 case 0: /* no slashes */
2231 break;
2232 case 2: /* two slashes */
2233 case 4:
2234 case 5:
2235 case 6:
2236 dwChRequired += 2;
2237 nSlashes = 2;
2238 break;
2239 default: /* three slashes */
2240 dwChRequired += 3;
2241 nSlashes = 3;
2244 if (dwChRequired > *pcchUrl)
2245 return E_POINTER;
2246 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2247 StrCpyA(pszUrl, "file:");
2248 pszPointer = pszUrl + lstrlenA(pszUrl);
2249 for (i=0; i < nSlashes; i++)
2251 *pszPointer = '/';
2252 pszPointer++;
2254 StrCpyA(pszPointer, pszNewUrl);
2255 TRACE("<- %s\n", debugstr_a(pszUrl));
2256 return S_OK;
2259 /*************************************************************************
2260 * UrlCreateFromPathW [SHLWAPI.@]
2262 * See UrlCreateFromPathA.
2264 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2266 DWORD nCharBeforeColon = 0;
2267 DWORD nSlashes = 0;
2268 DWORD dwChRequired = 0;
2269 LPWSTR pszNewUrl = NULL;
2270 LPCWSTR pszConstPointer = NULL;
2271 LPWSTR pszPointer = NULL;
2272 DWORD i;
2273 HRESULT ret;
2275 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2277 /* Validate arguments */
2278 if (dwReserved != 0)
2279 return E_INVALIDARG;
2280 if (!pszUrl || !pcchUrl || !pszUrl)
2281 return E_INVALIDARG;
2283 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2285 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2286 *pszConstPointer == '.' || *pszConstPointer == '-')
2287 nCharBeforeColon++;
2288 else break;
2290 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2292 dwChRequired = lstrlenW(pszPath);
2293 *pcchUrl = dwChRequired;
2294 if (dwChRequired > *pcchUrl)
2295 return E_POINTER;
2296 else
2298 StrCpyW(pszUrl, pszPath);
2299 return S_FALSE;
2302 /* then must need converting to file: format */
2304 /* Strip off leading slashes */
2305 while (*pszPath == '\\' || *pszPath == '/')
2307 pszPath++;
2308 nSlashes++;
2311 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2312 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2313 if (ret != E_POINTER && FAILED(ret))
2314 return ret;
2315 dwChRequired += 5; /* "file:" */
2316 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2318 dwChRequired += 3; /* "///" */
2319 nSlashes = 3;
2321 else
2322 switch (nSlashes)
2324 case 0: /* no slashes */
2325 break;
2326 case 2: /* two slashes */
2327 case 4:
2328 case 5:
2329 case 6:
2330 dwChRequired += 2;
2331 nSlashes = 2;
2332 break;
2333 default: /* three slashes */
2334 dwChRequired += 3;
2335 nSlashes = 3;
2338 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2339 if (dwChRequired > *pcchUrl)
2340 return E_POINTER;
2341 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2342 StrCpyW(pszNewUrl, fileW);
2343 pszPointer = pszNewUrl + 4;
2344 *pszPointer = ':';
2345 pszPointer++;
2346 for (i=0; i < nSlashes; i++)
2348 *pszPointer = '/';
2349 pszPointer++;
2351 StrCpyW(pszPointer, pszPath);
2352 StrCpyW(pszUrl, pszNewUrl);
2353 return S_OK;
2356 /*************************************************************************
2357 * SHAutoComplete [SHLWAPI.@]
2359 * Enable auto-completion for an edit control.
2361 * PARAMS
2362 * hwndEdit [I] Handle of control to enable auto-completion for
2363 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2365 * RETURNS
2366 * Success: S_OK. Auto-completion is enabled for the control.
2367 * Failure: An HRESULT error code indicating the error.
2369 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2371 FIXME("SHAutoComplete stub\n");
2372 return S_FALSE;
2375 /*************************************************************************
2376 * MLBuildResURLA [SHLWAPI.405]
2378 * Create a Url pointing to a resource in a module.
2380 * PARAMS
2381 * lpszLibName [I] Name of the module containing the resource
2382 * hMod [I] Callers module handle
2383 * dwFlags [I] Undocumented flags for loading the module
2384 * lpszRes [I] Resource name
2385 * lpszDest [O] Destination for resulting Url
2386 * dwDestLen [I] Length of lpszDest
2388 * RETURNS
2389 * Success: S_OK. lpszDest constains the resource Url.
2390 * Failure: E_INVALIDARG, if any argument is invalid, or
2391 * E_FAIL if dwDestLen is too small.
2393 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2394 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2396 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2397 HRESULT hRet;
2399 if (lpszLibName)
2400 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2402 if (lpszRes)
2403 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2405 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2406 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2408 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2409 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2410 if (SUCCEEDED(hRet) && lpszDest)
2411 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2413 return hRet;
2416 /*************************************************************************
2417 * MLBuildResURLA [SHLWAPI.406]
2419 * See MLBuildResURLA.
2421 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2422 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2424 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2425 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2426 HRESULT hRet = E_FAIL;
2428 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2429 debugstr_w(lpszRes), lpszDest, dwDestLen);
2431 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2432 !lpszDest || (dwFlags && dwFlags != 2))
2433 return E_INVALIDARG;
2435 if (dwDestLen >= szResLen + 1)
2437 dwDestLen -= (szResLen + 1);
2438 memcpy(lpszDest, szRes, sizeof(szRes));
2440 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2442 if (hMod)
2444 WCHAR szBuff[MAX_PATH];
2445 DWORD len;
2447 len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2448 if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2450 DWORD dwPathLen = strlenW(szBuff) + 1;
2452 if (dwDestLen >= dwPathLen)
2454 DWORD dwResLen;
2456 dwDestLen -= dwPathLen;
2457 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2459 dwResLen = strlenW(lpszRes) + 1;
2460 if (dwDestLen >= dwResLen + 1)
2462 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2463 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2464 hRet = S_OK;
2468 MLFreeLibrary(hMod);
2471 return hRet;