xref: /reactos/dll/win32/shlwapi/url.c (revision b917d826)
1 /*
2  * Url functions
3  *
4  * Copyright 2000 Huw D M Davies for CodeWeavers.
5  *
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.
10  *
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.
15  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "precomp.h"
22 
23 #include <wininet.h>
24 #include <intshcut.h>
25 
26 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
27 BOOL    WINAPI MLFreeLibrary(HMODULE);
28 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
29 
30 static inline WCHAR *heap_strdupAtoW(const char *str)
31 {
32     LPWSTR ret = NULL;
33 
34     if(str) {
35         DWORD len;
36 
37         len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
38         ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
39         MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
40     }
41 
42     return ret;
43 }
44 
45 /* The following schemes were identified in the native version of
46  * SHLWAPI.DLL version 5.50
47  */
48 static const struct {
49     URL_SCHEME  scheme_number;
50     WCHAR scheme_name[12];
51 } shlwapi_schemes[] = {
52   {URL_SCHEME_FTP,        {'f','t','p',0}},
53   {URL_SCHEME_HTTP,       {'h','t','t','p',0}},
54   {URL_SCHEME_GOPHER,     {'g','o','p','h','e','r',0}},
55   {URL_SCHEME_MAILTO,     {'m','a','i','l','t','o',0}},
56   {URL_SCHEME_NEWS,       {'n','e','w','s',0}},
57   {URL_SCHEME_NNTP,       {'n','n','t','p',0}},
58   {URL_SCHEME_TELNET,     {'t','e','l','n','e','t',0}},
59   {URL_SCHEME_WAIS,       {'w','a','i','s',0}},
60   {URL_SCHEME_FILE,       {'f','i','l','e',0}},
61   {URL_SCHEME_MK,         {'m','k',0}},
62   {URL_SCHEME_HTTPS,      {'h','t','t','p','s',0}},
63   {URL_SCHEME_SHELL,      {'s','h','e','l','l',0}},
64   {URL_SCHEME_SNEWS,      {'s','n','e','w','s',0}},
65   {URL_SCHEME_LOCAL,      {'l','o','c','a','l',0}},
66   {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
67   {URL_SCHEME_VBSCRIPT,   {'v','b','s','c','r','i','p','t',0}},
68   {URL_SCHEME_ABOUT,      {'a','b','o','u','t',0}},
69   {URL_SCHEME_RES,        {'r','e','s',0}},
70 };
71 
72 typedef struct {
73     LPCWSTR pScheme;      /* [out] start of scheme                     */
74     DWORD   szScheme;     /* [out] size of scheme (until colon)        */
75     LPCWSTR pUserName;    /* [out] start of Username                   */
76     DWORD   szUserName;   /* [out] size of Username (until ":" or "@") */
77     LPCWSTR pPassword;    /* [out] start of Password                   */
78     DWORD   szPassword;   /* [out] size of Password (until "@")        */
79     LPCWSTR pHostName;    /* [out] start of Hostname                   */
80     DWORD   szHostName;   /* [out] size of Hostname (until ":" or "/") */
81     LPCWSTR pPort;        /* [out] start of Port                       */
82     DWORD   szPort;       /* [out] size of Port (until "/" or eos)     */
83     LPCWSTR pQuery;       /* [out] start of Query                      */
84     DWORD   szQuery;      /* [out] size of Query (until eos)           */
85 } WINE_PARSE_URL;
86 
87 typedef enum {
88     SCHEME,
89     HOST,
90     PORT,
91     USERPASS,
92 } WINE_URL_SCAN_TYPE;
93 
94 static const CHAR hexDigits[] = "0123456789ABCDEF";
95 
96 static const WCHAR fileW[] = {'f','i','l','e','\0'};
97 
98 static const unsigned char HashDataLookup[256] = {
99  0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
100  0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
101  0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
102  0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
103  0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
104  0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
105  0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
106  0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
107  0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
108  0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
109  0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
110  0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
111  0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
112  0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
113  0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
114  0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
115  0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
116  0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
117  0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
118  0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
119 
120 static DWORD get_scheme_code(LPCWSTR scheme, DWORD scheme_len)
121 {
122     unsigned int i;
123 
124     for(i=0; i < sizeof(shlwapi_schemes)/sizeof(shlwapi_schemes[0]); i++) {
125         if(scheme_len == strlenW(shlwapi_schemes[i].scheme_name)
126            && !memicmpW(scheme, shlwapi_schemes[i].scheme_name, scheme_len))
127             return shlwapi_schemes[i].scheme_number;
128     }
129 
130     return URL_SCHEME_UNKNOWN;
131 }
132 
133 /*************************************************************************
134  *      @	[SHLWAPI.1]
135  *
136  * Parse a Url into its constituent parts.
137  *
138  * PARAMS
139  *  x [I] Url to parse
140  *  y [O] Undocumented structure holding the parsed information
141  *
142  * RETURNS
143  *  Success: S_OK. y contains the parsed Url details.
144  *  Failure: An HRESULT error code.
145  */
146 HRESULT WINAPI ParseURLA(LPCSTR x, PARSEDURLA *y)
147 {
148     WCHAR scheme[INTERNET_MAX_SCHEME_LENGTH];
149     const char *ptr = x;
150     int len;
151 
152     TRACE("%s %p\n", debugstr_a(x), y);
153 
154     if(y->cbSize != sizeof(*y))
155         return E_INVALIDARG;
156 
157     while(*ptr && (isalnum(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.'))
158         ptr++;
159 
160     if (*ptr != ':' || ptr <= x+1) {
161         y->pszProtocol = NULL;
162         return URL_E_INVALID_SYNTAX;
163     }
164 
165     y->pszProtocol = x;
166     y->cchProtocol = ptr-x;
167     y->pszSuffix = ptr+1;
168     y->cchSuffix = strlen(y->pszSuffix);
169 
170     len = MultiByteToWideChar(CP_ACP, 0, x, ptr-x,
171             scheme, sizeof(scheme)/sizeof(WCHAR));
172     y->nScheme = get_scheme_code(scheme, len);
173 
174     return S_OK;
175 }
176 
177 /*************************************************************************
178  *      @	[SHLWAPI.2]
179  *
180  * Unicode version of ParseURLA.
181  */
182 HRESULT WINAPI ParseURLW(LPCWSTR x, PARSEDURLW *y)
183 {
184     const WCHAR *ptr = x;
185 
186     TRACE("%s %p\n", debugstr_w(x), y);
187 
188     if(y->cbSize != sizeof(*y))
189         return E_INVALIDARG;
190 
191     while(*ptr && (isalnumW(*ptr) || *ptr == '-' || *ptr == '+' || *ptr == '.'))
192         ptr++;
193 
194     if (*ptr != ':' || ptr <= x+1) {
195         y->pszProtocol = NULL;
196         return URL_E_INVALID_SYNTAX;
197     }
198 
199     y->pszProtocol = x;
200     y->cchProtocol = ptr-x;
201     y->pszSuffix = ptr+1;
202     y->cchSuffix = strlenW(y->pszSuffix);
203     y->nScheme = get_scheme_code(x, ptr-x);
204 
205     return S_OK;
206 }
207 
208 /*************************************************************************
209  *        UrlCanonicalizeA     [SHLWAPI.@]
210  *
211  * Canonicalize a Url.
212  *
213  * PARAMS
214  *  pszUrl            [I]   Url to cCanonicalize
215  *  pszCanonicalized  [O]   Destination for converted Url.
216  *  pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
217  *  dwFlags           [I]   Flags controlling the conversion.
218  *
219  * RETURNS
220  *  Success: S_OK. The pszCanonicalized contains the converted Url.
221  *  Failure: E_POINTER, if *pcchCanonicalized is too small.
222  *
223  * MSDN incorrectly describes the flags for this function. They should be:
224  *|    URL_DONT_ESCAPE_EXTRA_INFO    0x02000000
225  *|    URL_ESCAPE_SPACES_ONLY        0x04000000
226  *|    URL_ESCAPE_PERCENT            0x00001000
227  *|    URL_ESCAPE_UNSAFE             0x10000000
228  *|    URL_UNESCAPE                  0x10000000
229  *|    URL_DONT_SIMPLIFY             0x08000000
230  *|    URL_ESCAPE_SEGMENT_ONLY       0x00002000
231  */
232 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
233 	LPDWORD pcchCanonicalized, DWORD dwFlags)
234 {
235     LPWSTR url, canonical;
236     HRESULT ret;
237 
238     TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_a(pszUrl), pszCanonicalized,
239         pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
240 
241     if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
242 	return E_INVALIDARG;
243 
244     url = heap_strdupAtoW(pszUrl);
245     canonical = HeapAlloc(GetProcessHeap(), 0, *pcchCanonicalized*sizeof(WCHAR));
246     if(!url || !canonical) {
247         HeapFree(GetProcessHeap(), 0, url);
248         HeapFree(GetProcessHeap(), 0, canonical);
249         return E_OUTOFMEMORY;
250     }
251 
252     ret = UrlCanonicalizeW(url, canonical, pcchCanonicalized, dwFlags);
253     if(ret == S_OK)
254         WideCharToMultiByte(CP_ACP, 0, canonical, -1, pszCanonicalized,
255                             *pcchCanonicalized+1, NULL, NULL);
256 
257     HeapFree(GetProcessHeap(), 0, url);
258     HeapFree(GetProcessHeap(), 0, canonical);
259     return ret;
260 }
261 
262 /*************************************************************************
263  *        UrlCanonicalizeW     [SHLWAPI.@]
264  *
265  * See UrlCanonicalizeA.
266  */
267 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
268 				LPDWORD pcchCanonicalized, DWORD dwFlags)
269 {
270     HRESULT hr = S_OK;
271     DWORD EscapeFlags;
272     LPCWSTR wk1, root;
273     LPWSTR lpszUrlCpy, url, wk2, mp, mp2;
274     INT state;
275     DWORD nByteLen, nLen, nWkLen;
276     BOOL is_file_url;
277     WCHAR slash = '\0';
278 
279     static const WCHAR wszFile[] = {'f','i','l','e',':'};
280     static const WCHAR wszRes[] = {'r','e','s',':'};
281     static const WCHAR wszHttp[] = {'h','t','t','p',':'};
282     static const WCHAR wszLocalhost[] = {'l','o','c','a','l','h','o','s','t'};
283     static const WCHAR wszFilePrefix[] = {'f','i','l','e',':','/','/','/'};
284 
285     TRACE("(%s, %p, %p, 0x%08x) *pcchCanonicalized: %d\n", debugstr_w(pszUrl), pszCanonicalized,
286         pcchCanonicalized, dwFlags, pcchCanonicalized ? *pcchCanonicalized : -1);
287 
288     if(!pszUrl || !pszCanonicalized || !pcchCanonicalized || !*pcchCanonicalized)
289 	return E_INVALIDARG;
290 
291     if(!*pszUrl) {
292         *pszCanonicalized = 0;
293         return S_OK;
294     }
295 
296     /* Remove '\t' characters from URL */
297     nByteLen = (strlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
298     url = HeapAlloc(GetProcessHeap(), 0, nByteLen);
299     if(!url)
300         return E_OUTOFMEMORY;
301 
302     wk1 = pszUrl;
303     wk2 = url;
304     do {
305         while(*wk1 == '\t')
306             wk1++;
307         *wk2++ = *wk1;
308     } while(*wk1++);
309 
310     /* Allocate memory for simplified URL (before escaping) */
311     nByteLen = (wk2-url)*sizeof(WCHAR);
312     lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0,
313             nByteLen+sizeof(wszFilePrefix)+sizeof(WCHAR));
314     if(!lpszUrlCpy) {
315         HeapFree(GetProcessHeap(), 0, url);
316         return E_OUTOFMEMORY;
317     }
318 
319     is_file_url = !strncmpW(wszFile, url, sizeof(wszFile)/sizeof(WCHAR));
320 
321     if ((nByteLen >= sizeof(wszHttp) &&
322          !memcmp(wszHttp, url, sizeof(wszHttp))) || is_file_url)
323         slash = '/';
324 
325     if((dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY)) && is_file_url)
326         slash = '\\';
327 
328     if(nByteLen >= sizeof(wszRes) && !memcmp(wszRes, url, sizeof(wszRes))) {
329         dwFlags &= ~URL_FILE_USE_PATHURL;
330         slash = '\0';
331     }
332 
333     /*
334      * state =
335      *         0   initial  1,3
336      *         1   have 2[+] alnum  2,3
337      *         2   have scheme (found :)  4,6,3
338      *         3   failed (no location)
339      *         4   have //  5,3
340      *         5   have 1[+] alnum  6,3
341      *         6   have location (found /) save root location
342      */
343 
344     wk1 = url;
345     wk2 = lpszUrlCpy;
346     state = 0;
347 
348     if(url[1] == ':') { /* Assume path */
349         memcpy(wk2, wszFilePrefix, sizeof(wszFilePrefix));
350         wk2 += sizeof(wszFilePrefix)/sizeof(WCHAR);
351         if (dwFlags & (URL_FILE_USE_PATHURL | URL_WININET_COMPATIBILITY))
352         {
353             slash = '\\';
354             --wk2;
355         }
356         else
357             dwFlags |= URL_ESCAPE_UNSAFE;
358         state = 5;
359         is_file_url = TRUE;
360     } else if(url[0] == '/') {
361         state = 5;
362         is_file_url = TRUE;
363     }
364 
365     while (*wk1) {
366         switch (state) {
367         case 0:
368             if (!isalnumW(*wk1)) {state = 3; break;}
369             *wk2++ = *wk1++;
370             if (!isalnumW(*wk1)) {state = 3; break;}
371             *wk2++ = *wk1++;
372             state = 1;
373             break;
374         case 1:
375             *wk2++ = *wk1;
376             if (*wk1++ == ':') state = 2;
377             break;
378         case 2:
379             *wk2++ = *wk1++;
380             if (*wk1 != '/') {state = 6; break;}
381             *wk2++ = *wk1++;
382             if((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszLocalhost)
383                         && is_file_url
384                         && !memcmp(wszLocalhost, wk1, sizeof(wszLocalhost))){
385                 wk1 += sizeof(wszLocalhost)/sizeof(WCHAR);
386                 while(*wk1 == '\\' && (dwFlags & URL_FILE_USE_PATHURL))
387                     wk1++;
388             }
389 
390             if(*wk1 == '/' && (dwFlags & URL_FILE_USE_PATHURL)){
391                 wk1++;
392             }else if(is_file_url){
393                 const WCHAR *body = wk1;
394 
395                 while(*body == '/')
396                     ++body;
397 
398                 if(isalnumW(*body) && *(body+1) == ':'){
399                     if(!(dwFlags & (URL_WININET_COMPATIBILITY | URL_FILE_USE_PATHURL))){
400                         if(slash)
401                             *wk2++ = slash;
402                         else
403                             *wk2++ = '/';
404                     }
405                 }else{
406                     if(dwFlags & URL_WININET_COMPATIBILITY){
407                         if(*wk1 == '/' && *(wk1+1) != '/'){
408                             *wk2++ = '\\';
409                         }else{
410                             *wk2++ = '\\';
411                             *wk2++ = '\\';
412                         }
413                     }else{
414                         if(*wk1 == '/' && *(wk1+1) != '/'){
415                             if(slash)
416                                 *wk2++ = slash;
417                             else
418                                 *wk2++ = '/';
419                         }
420                     }
421                 }
422                 wk1 = body;
423             }
424             state = 4;
425             break;
426         case 3:
427             nWkLen = strlenW(wk1);
428             memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
429             mp = wk2;
430             wk1 += nWkLen;
431             wk2 += nWkLen;
432 
433             if(slash) {
434                 while(mp < wk2) {
435                     if(*mp == '/' || *mp == '\\')
436                         *mp = slash;
437                     mp++;
438                 }
439             }
440             break;
441         case 4:
442             if (!isalnumW(*wk1) && (*wk1 != '-') && (*wk1 != '.') && (*wk1 != ':'))
443                 {state = 3; break;}
444             while(isalnumW(*wk1) || (*wk1 == '-') || (*wk1 == '.') || (*wk1 == ':'))
445                 *wk2++ = *wk1++;
446             state = 5;
447             if (!*wk1) {
448                 if(slash)
449                     *wk2++ = slash;
450                 else
451                     *wk2++ = '/';
452             }
453             break;
454         case 5:
455             if (*wk1 != '/' && *wk1 != '\\') {state = 3; break;}
456             while(*wk1 == '/' || *wk1 == '\\') {
457                 if(slash)
458                     *wk2++ = slash;
459                 else
460                     *wk2++ = *wk1;
461                 wk1++;
462             }
463             state = 6;
464             break;
465         case 6:
466             if(dwFlags & URL_DONT_SIMPLIFY) {
467                 state = 3;
468                 break;
469             }
470 
471             /* Now at root location, cannot back up any more. */
472             /* "root" will point at the '/' */
473 
474             root = wk2-1;
475             while (*wk1) {
476                 mp = strchrW(wk1, '/');
477                 mp2 = strchrW(wk1, '\\');
478                 if(mp2 && (!mp || mp2 < mp))
479                     mp = mp2;
480                 if (!mp) {
481                     nWkLen = strlenW(wk1);
482                     memcpy(wk2, wk1, (nWkLen + 1) * sizeof(WCHAR));
483                     wk1 += nWkLen;
484                     wk2 += nWkLen;
485                     continue;
486                 }
487                 nLen = mp - wk1;
488                 if(nLen) {
489                     memcpy(wk2, wk1, nLen * sizeof(WCHAR));
490                     wk2 += nLen;
491                     wk1 += nLen;
492                 }
493                 if(slash)
494                     *wk2++ = slash;
495                 else
496                     *wk2++ = *wk1;
497                 wk1++;
498 
499                 while (*wk1 == '.') {
500                     TRACE("found '/.'\n");
501                     if (wk1[1] == '/' || wk1[1] == '\\') {
502                         /* case of /./ -> skip the ./ */
503                         wk1 += 2;
504                     }
505                     else if (wk1[1] == '.' && (wk1[2] == '/'
506                             || wk1[2] == '\\' || wk1[2] == '?'
507                             || wk1[2] == '#' || !wk1[2])) {
508                         /* case /../ -> need to backup wk2 */
509                         TRACE("found '/../'\n");
510                         *(wk2-1) = '\0';  /* set end of string */
511                         mp = strrchrW(root, '/');
512                         mp2 = strrchrW(root, '\\');
513                         if(mp2 && (!mp || mp2 < mp))
514                             mp = mp2;
515                         if (mp && (mp >= root)) {
516                             /* found valid backup point */
517                             wk2 = mp + 1;
518                             if(wk1[2] != '/' && wk1[2] != '\\')
519                                 wk1 += 2;
520                             else
521                                 wk1 += 3;
522                         }
523                         else {
524                             /* did not find point, restore '/' */
525                             *(wk2-1) = slash;
526                             break;
527                         }
528                     }
529                     else
530                         break;
531                 }
532             }
533             *wk2 = '\0';
534             break;
535         default:
536             FIXME("how did we get here - state=%d\n", state);
537             HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
538             HeapFree(GetProcessHeap(), 0, url);
539             return E_INVALIDARG;
540         }
541         *wk2 = '\0';
542 	TRACE("Simplified, orig <%s>, simple <%s>\n",
543 	      debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
544     }
545     nLen = lstrlenW(lpszUrlCpy);
546     while ((nLen > 0) && ((lpszUrlCpy[nLen-1] <= ' ')))
547         lpszUrlCpy[--nLen]=0;
548 
549     if((dwFlags & URL_UNESCAPE) ||
550        ((dwFlags & URL_FILE_USE_PATHURL) && nByteLen >= sizeof(wszFile)
551                 && !memcmp(wszFile, url, sizeof(wszFile))))
552         UrlUnescapeW(lpszUrlCpy, NULL, &nLen, URL_UNESCAPE_INPLACE);
553 
554     if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
555                                  URL_ESCAPE_SPACES_ONLY |
556                                  URL_ESCAPE_PERCENT |
557                                  URL_DONT_ESCAPE_EXTRA_INFO |
558 				 URL_ESCAPE_SEGMENT_ONLY ))) {
559 	EscapeFlags &= ~URL_ESCAPE_UNSAFE;
560 	hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
561 			EscapeFlags);
562     } else { /* No escaping needed, just copy the string */
563         nLen = lstrlenW(lpszUrlCpy);
564 	if(nLen < *pcchCanonicalized)
565 	    memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
566 	else {
567 	    hr = E_POINTER;
568 	    nLen++;
569 	}
570 	*pcchCanonicalized = nLen;
571     }
572 
573     HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
574     HeapFree(GetProcessHeap(), 0, url);
575 
576     if (hr == S_OK)
577 	TRACE("result %s\n", debugstr_w(pszCanonicalized));
578 
579     return hr;
580 }
581 
582 /*************************************************************************
583  *        UrlCombineA     [SHLWAPI.@]
584  *
585  * Combine two Urls.
586  *
587  * PARAMS
588  *  pszBase      [I] Base Url
589  *  pszRelative  [I] Url to combine with pszBase
590  *  pszCombined  [O] Destination for combined Url
591  *  pcchCombined [O] Destination for length of pszCombined
592  *  dwFlags      [I] URL_ flags from "shlwapi.h"
593  *
594  * RETURNS
595  *  Success: S_OK. pszCombined contains the combined Url, pcchCombined
596  *           contains its length.
597  *  Failure: An HRESULT error code indicating the error.
598  */
599 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
600 			   LPSTR pszCombined, LPDWORD pcchCombined,
601 			   DWORD dwFlags)
602 {
603     LPWSTR base, relative, combined;
604     DWORD ret, len, len2;
605 
606     TRACE("(base %s, Relative %s, Combine size %d, flags %08x) using W version\n",
607 	  debugstr_a(pszBase),debugstr_a(pszRelative),
608 	  pcchCombined?*pcchCombined:0,dwFlags);
609 
610     if(!pszBase || !pszRelative || !pcchCombined)
611 	return E_INVALIDARG;
612 
613     base = HeapAlloc(GetProcessHeap(), 0,
614 			      (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
615     relative = base + INTERNET_MAX_URL_LENGTH;
616     combined = relative + INTERNET_MAX_URL_LENGTH;
617 
618     MultiByteToWideChar(CP_ACP, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
619     MultiByteToWideChar(CP_ACP, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
620     len = *pcchCombined;
621 
622     ret = UrlCombineW(base, relative, pszCombined?combined:NULL, &len, dwFlags);
623     if (ret != S_OK) {
624 	*pcchCombined = len;
625 	HeapFree(GetProcessHeap(), 0, base);
626 	return ret;
627     }
628 
629     len2 = WideCharToMultiByte(CP_ACP, 0, combined, len, NULL, 0, NULL, NULL);
630     if (len2 > *pcchCombined) {
631 	*pcchCombined = len2;
632 	HeapFree(GetProcessHeap(), 0, base);
633 	return E_POINTER;
634     }
635     WideCharToMultiByte(CP_ACP, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
636 			NULL, NULL);
637     *pcchCombined = len2;
638     HeapFree(GetProcessHeap(), 0, base);
639     return S_OK;
640 }
641 
642 /*************************************************************************
643  *        UrlCombineW     [SHLWAPI.@]
644  *
645  * See UrlCombineA.
646  */
647 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
648 			   LPWSTR pszCombined, LPDWORD pcchCombined,
649 			   DWORD dwFlags)
650 {
651     PARSEDURLW base, relative;
652     DWORD myflags, sizeloc = 0;
653     DWORD i, len, res1, res2, process_case = 0;
654     LPWSTR work, preliminary, mbase, mrelative;
655     static const WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
656     static const WCHAR fragquerystr[] = {'#','?',0};
657     HRESULT ret;
658 
659     TRACE("(base %s, Relative %s, Combine size %d, flags %08x)\n",
660 	  debugstr_w(pszBase),debugstr_w(pszRelative),
661 	  pcchCombined?*pcchCombined:0,dwFlags);
662 
663     if(!pszBase || !pszRelative || !pcchCombined)
664 	return E_INVALIDARG;
665 
666     base.cbSize = sizeof(base);
667     relative.cbSize = sizeof(relative);
668 
669     /* Get space for duplicates of the input and the output */
670     preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
671 			    sizeof(WCHAR));
672     mbase = preliminary + INTERNET_MAX_URL_LENGTH;
673     mrelative = mbase + INTERNET_MAX_URL_LENGTH;
674     *preliminary = '\0';
675 
676     /* Canonicalize the base input prior to looking for the scheme */
677     myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
678     len = INTERNET_MAX_URL_LENGTH;
679     UrlCanonicalizeW(pszBase, mbase, &len, myflags);
680 
681     /* Canonicalize the relative input prior to looking for the scheme */
682     len = INTERNET_MAX_URL_LENGTH;
683     UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
684 
685     /* See if the base has a scheme */
686     res1 = ParseURLW(mbase, &base);
687     if (res1) {
688 	/* if pszBase has no scheme, then return pszRelative */
689 	TRACE("no scheme detected in Base\n");
690 	process_case = 1;
691     }
692     else do {
693         BOOL manual_search = FALSE;
694 
695         work = (LPWSTR)base.pszProtocol;
696         for(i=0; i<base.cchProtocol; i++)
697             work[i] = tolowerW(work[i]);
698 
699         /* mk is a special case */
700         if(base.nScheme == URL_SCHEME_MK) {
701             static const WCHAR wsz[] = {':',':',0};
702 
703             WCHAR *ptr = strstrW(base.pszSuffix, wsz);
704             if(ptr) {
705                 int delta;
706 
707                 ptr += 2;
708                 delta = ptr-base.pszSuffix;
709                 base.cchProtocol += delta;
710                 base.pszSuffix += delta;
711                 base.cchSuffix -= delta;
712             }
713         }else {
714             /* get size of location field (if it exists) */
715             work = (LPWSTR)base.pszSuffix;
716             sizeloc = 0;
717             if (*work++ == '/') {
718                 if (*work++ == '/') {
719                     /* At this point have start of location and
720                      * it ends at next '/' or end of string.
721                      */
722                     while(*work && (*work != '/')) work++;
723                     sizeloc = (DWORD)(work - base.pszSuffix);
724                 }
725             }
726         }
727 
728         /* If there is a '?', then the remaining part can only contain a
729          * query string or fragment, so start looking for the last leaf
730          * from the '?'. Otherwise, if there is a '#' and the characters
731          * immediately preceding it are ".htm[l]", then begin looking for
732          * the last leaf starting from the '#'. Otherwise the '#' is not
733          * meaningful and just start looking from the end. */
734         if ((work = strpbrkW(base.pszSuffix + sizeloc, fragquerystr))) {
735             const WCHAR htmlW[] = {'.','h','t','m','l',0};
736             const int len_htmlW = 5;
737             const WCHAR htmW[] = {'.','h','t','m',0};
738             const int len_htmW = 4;
739 
740             if (*work == '?' || base.nScheme == URL_SCHEME_HTTP || base.nScheme == URL_SCHEME_HTTPS)
741                 manual_search = TRUE;
742             else if (work - base.pszSuffix > len_htmW) {
743                 work -= len_htmW;
744                 if (strncmpiW(work, htmW, len_htmW) == 0)
745                     manual_search = TRUE;
746                 work += len_htmW;
747             }
748 
749             if (!manual_search &&
750                     work - base.pszSuffix > len_htmlW) {
751                 work -= len_htmlW;
752                 if (strncmpiW(work, htmlW, len_htmlW) == 0)
753                     manual_search = TRUE;
754                 work += len_htmlW;
755             }
756         }
757 
758         if (manual_search) {
759             /* search backwards starting from the current position */
760             while (*work != '/' && work > base.pszSuffix + sizeloc)
761                 --work;
762             base.cchSuffix = work - base.pszSuffix + 1;
763         }else {
764             /* search backwards starting from the end of the string */
765             work = strrchrW((base.pszSuffix+sizeloc), '/');
766             if (work) {
767                 len = (DWORD)(work - base.pszSuffix + 1);
768                 base.cchSuffix = len;
769             }else
770                 base.cchSuffix = sizeloc;
771         }
772 
773 	/*
774 	 * At this point:
775 	 *    .pszSuffix   points to location (starting with '//')
776 	 *    .cchSuffix   length of location (above) and rest less the last
777 	 *                 leaf (if any)
778 	 *    sizeloc   length of location (above) up to but not including
779 	 *              the last '/'
780 	 */
781 
782 	res2 = ParseURLW(mrelative, &relative);
783 	if (res2) {
784 	    /* no scheme in pszRelative */
785 	    TRACE("no scheme detected in Relative\n");
786 	    relative.pszSuffix = mrelative;  /* case 3,4,5 depends on this */
787 	    relative.cchSuffix = strlenW(mrelative);
788             if (*pszRelative  == ':') {
789 		/* case that is either left alone or uses pszBase */
790 		if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
791 		    process_case = 5;
792 		    break;
793 		}
794 		process_case = 1;
795 		break;
796 	    }
797             if (isalnumW(*mrelative) && (*(mrelative + 1) == ':')) {
798 		/* case that becomes "file:///" */
799 		strcpyW(preliminary, myfilestr);
800 		process_case = 1;
801 		break;
802 	    }
803             if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
804 		/* pszRelative has location and rest */
805 		process_case = 3;
806 		break;
807 	    }
808             if (*mrelative == '/') {
809 		/* case where pszRelative is root to location */
810 		process_case = 4;
811 		break;
812 	    }
813             if (*mrelative == '#') {
814                 if(!(work = strchrW(base.pszSuffix+base.cchSuffix, '#')))
815                     work = (LPWSTR)base.pszSuffix + strlenW(base.pszSuffix);
816 
817                 memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR));
818                 preliminary[work-base.pszProtocol] = '\0';
819                 process_case = 1;
820                 break;
821             }
822             process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
823 	    break;
824 	}else {
825             work = (LPWSTR)relative.pszProtocol;
826             for(i=0; i<relative.cchProtocol; i++)
827                 work[i] = tolowerW(work[i]);
828         }
829 
830 	/* handle cases where pszRelative has scheme */
831 	if ((base.cchProtocol == relative.cchProtocol) &&
832 	    (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
833 
834 	    /* since the schemes are the same */
835             if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
836 		/* case where pszRelative replaces location and following */
837 		process_case = 3;
838 		break;
839 	    }
840             if (*relative.pszSuffix == '/') {
841 		/* case where pszRelative is root to location */
842 		process_case = 4;
843 		break;
844 	    }
845             /* replace either just location if base's location starts with a
846              * slash or otherwise everything */
847             process_case = (*base.pszSuffix == '/') ? 5 : 1;
848 	    break;
849 	}
850         if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
851 	    /* case where pszRelative replaces scheme, location,
852 	     * and following and handles PLUGGABLE
853 	     */
854 	    process_case = 2;
855 	    break;
856 	}
857 	process_case = 1;
858 	break;
859     } while(FALSE); /* a little trick to allow easy exit from nested if's */
860 
861     ret = S_OK;
862     switch (process_case) {
863 
864     case 1:  /*
865 	      * Return pszRelative appended to what ever is in pszCombined,
866 	      * (which may the string "file:///"
867 	      */
868 	strcatW(preliminary, mrelative);
869 	break;
870 
871     case 2:  /* case where pszRelative replaces scheme, and location */
872 	strcpyW(preliminary, mrelative);
873 	break;
874 
875     case 3:  /*
876 	      * Return the pszBase scheme with pszRelative. Basically
877 	      * keeps the scheme and replaces the domain and following.
878 	      */
879         memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
880 	work = preliminary + base.cchProtocol + 1;
881 	strcpyW(work, relative.pszSuffix);
882 	break;
883 
884     case 4:  /*
885 	      * Return the pszBase scheme and location but everything
886 	      * after the location is pszRelative. (Replace document
887 	      * from root on.)
888 	      */
889         memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
890 	work = preliminary + base.cchProtocol + 1 + sizeloc;
891 	if (dwFlags & URL_PLUGGABLE_PROTOCOL)
892             *(work++) = '/';
893 	strcpyW(work, relative.pszSuffix);
894 	break;
895 
896     case 5:  /*
897 	      * Return the pszBase without its document (if any) and
898 	      * append pszRelative after its scheme.
899 	      */
900         memcpy(preliminary, base.pszProtocol,
901                (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
902 	work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
903         if (*work++ != '/')
904             *(work++) = '/';
905 	strcpyW(work, relative.pszSuffix);
906 	break;
907 
908     default:
909 	FIXME("How did we get here????? process_case=%d\n", process_case);
910 	ret = E_INVALIDARG;
911     }
912 
913     if (ret == S_OK) {
914         /* Reuse mrelative as temp storage as it's already allocated and not needed anymore */
915         if(*pcchCombined == 0)
916             *pcchCombined = 1;
917 	ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
918 	if(SUCCEEDED(ret) && pszCombined) {
919 	    lstrcpyW(pszCombined, mrelative);
920 	}
921 	TRACE("return-%d len=%d, %s\n",
922 	      process_case, *pcchCombined, debugstr_w(pszCombined));
923     }
924     HeapFree(GetProcessHeap(), 0, preliminary);
925     return ret;
926 }
927 
928 /*************************************************************************
929  *      UrlEscapeA	[SHLWAPI.@]
930  */
931 
932 HRESULT WINAPI UrlEscapeA(
933 	LPCSTR pszUrl,
934 	LPSTR pszEscaped,
935 	LPDWORD pcchEscaped,
936 	DWORD dwFlags)
937 {
938     WCHAR bufW[INTERNET_MAX_URL_LENGTH];
939     WCHAR *escapedW = bufW;
940     UNICODE_STRING urlW;
941     HRESULT ret;
942     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
943 
944     if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
945         return E_INVALIDARG;
946 
947     if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
948         return E_INVALIDARG;
949     if(dwFlags & URL_ESCAPE_AS_UTF8) {
950         RtlFreeUnicodeString(&urlW);
951         return E_NOTIMPL;
952     }
953     if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
954         escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
955         ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
956     }
957     if(ret == S_OK) {
958         RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
959         if(*pcchEscaped > lenA) {
960             RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
961             pszEscaped[lenA] = 0;
962             *pcchEscaped = lenA;
963         } else {
964             *pcchEscaped = lenA + 1;
965             ret = E_POINTER;
966         }
967     }
968     if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
969     RtlFreeUnicodeString(&urlW);
970     return ret;
971 }
972 
973 #define WINE_URL_BASH_AS_SLASH    0x01
974 #define WINE_URL_COLLAPSE_SLASHES 0x02
975 #define WINE_URL_ESCAPE_SLASH     0x04
976 #define WINE_URL_ESCAPE_HASH      0x08
977 #define WINE_URL_ESCAPE_QUESTION  0x10
978 #define WINE_URL_STOP_ON_HASH     0x20
979 #define WINE_URL_STOP_ON_QUESTION 0x40
980 
981 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD flags, DWORD int_flags)
982 {
983     if (flags & URL_ESCAPE_SPACES_ONLY)
984         return ch == ' ';
985 
986     if ((flags & URL_ESCAPE_PERCENT) && (ch == '%'))
987 	return TRUE;
988 
989     if ((flags & URL_ESCAPE_AS_UTF8) && (ch >= 0x80))
990 	return TRUE;
991 
992     if (ch <= 31 || (ch >= 127 && ch <= 255) )
993 	return TRUE;
994 
995     if (isalnumW(ch))
996         return FALSE;
997 
998     switch (ch) {
999     case ' ':
1000     case '<':
1001     case '>':
1002     case '\"':
1003     case '{':
1004     case '}':
1005     case '|':
1006     case '\\':
1007     case '^':
1008     case ']':
1009     case '[':
1010     case '`':
1011     case '&':
1012         return TRUE;
1013     case '/':
1014         return !!(int_flags & WINE_URL_ESCAPE_SLASH);
1015     case '?':
1016         return !!(int_flags & WINE_URL_ESCAPE_QUESTION);
1017     case '#':
1018         return !!(int_flags & WINE_URL_ESCAPE_HASH);
1019     default:
1020         return FALSE;
1021     }
1022 }
1023 
1024 
1025 /*************************************************************************
1026  *      UrlEscapeW	[SHLWAPI.@]
1027  *
1028  * Converts unsafe characters in a Url into escape sequences.
1029  *
1030  * PARAMS
1031  *  pszUrl      [I]   Url to modify
1032  *  pszEscaped  [O]   Destination for modified Url
1033  *  pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1034  *  dwFlags     [I]   URL_ flags from "shlwapi.h"
1035  *
1036  * RETURNS
1037  *  Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1038  *           contains its length.
1039  *  Failure: E_POINTER, if pszEscaped is not large enough. In this case
1040  *           pcchEscaped is set to the required length.
1041  *
1042  * Converts unsafe characters into their escape sequences.
1043  *
1044  * NOTES
1045  * - By default this function stops converting at the first '?' or
1046  *  '#' character.
1047  * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1048  *   converted, but the conversion continues past a '?' or '#'.
1049  * - Note that this function did not work well (or at all) in shlwapi version 4.
1050  *
1051  * BUGS
1052  *  Only the following flags are implemented:
1053  *|     URL_ESCAPE_SPACES_ONLY
1054  *|     URL_DONT_ESCAPE_EXTRA_INFO
1055  *|     URL_ESCAPE_SEGMENT_ONLY
1056  *|     URL_ESCAPE_PERCENT
1057  */
1058 HRESULT WINAPI UrlEscapeW(
1059 	LPCWSTR pszUrl,
1060 	LPWSTR pszEscaped,
1061 	LPDWORD pcchEscaped,
1062 	DWORD dwFlags)
1063 {
1064     LPCWSTR src;
1065     DWORD needed = 0, ret;
1066     BOOL stop_escaping = FALSE;
1067     WCHAR next[12], *dst, *dst_ptr;
1068     INT i, len;
1069     PARSEDURLW parsed_url;
1070     DWORD int_flags;
1071     DWORD slashes = 0;
1072     static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1073 
1074     TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1075             pszEscaped, pcchEscaped, dwFlags);
1076 
1077     if(!pszUrl || !pcchEscaped || !pszEscaped || *pcchEscaped == 0)
1078         return E_INVALIDARG;
1079 
1080     if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1081 		   URL_ESCAPE_SEGMENT_ONLY |
1082 		   URL_DONT_ESCAPE_EXTRA_INFO |
1083 		   URL_ESCAPE_PERCENT |
1084 		   URL_ESCAPE_AS_UTF8))
1085         FIXME("Unimplemented flags: %08x\n", dwFlags);
1086 
1087     dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1088     if(!dst_ptr)
1089         return E_OUTOFMEMORY;
1090 
1091     /* fix up flags */
1092     if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1093 	/* if SPACES_ONLY specified, reset the other controls */
1094 	dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1095 		     URL_ESCAPE_PERCENT |
1096 		     URL_ESCAPE_SEGMENT_ONLY);
1097 
1098     else
1099 	/* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1100 	dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1101 
1102 
1103     int_flags = 0;
1104     if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1105         int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1106     } else {
1107         parsed_url.cbSize = sizeof(parsed_url);
1108         if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1109             parsed_url.nScheme = URL_SCHEME_INVALID;
1110 
1111         TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1112 
1113         if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1114             int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1115 
1116         switch(parsed_url.nScheme) {
1117         case URL_SCHEME_FILE:
1118             int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1119             int_flags &= ~WINE_URL_STOP_ON_HASH;
1120             break;
1121 
1122         case URL_SCHEME_HTTP:
1123         case URL_SCHEME_HTTPS:
1124             int_flags |= WINE_URL_BASH_AS_SLASH;
1125             if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1126                 int_flags |= WINE_URL_ESCAPE_SLASH;
1127             break;
1128 
1129         case URL_SCHEME_MAILTO:
1130             int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1131             int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1132             break;
1133 
1134         case URL_SCHEME_INVALID:
1135             break;
1136 
1137         case URL_SCHEME_FTP:
1138         default:
1139             if(parsed_url.pszSuffix[0] != '/')
1140                 int_flags |= WINE_URL_ESCAPE_SLASH;
1141             break;
1142         }
1143     }
1144 
1145     for(src = pszUrl; *src; ) {
1146         WCHAR cur = *src;
1147         len = 0;
1148 
1149         if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1150             int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1151             while(cur == '/' || cur == '\\') {
1152                 slashes++;
1153                 cur = *++src;
1154             }
1155             if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1156                 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1157                 src += localhost_len + 1;
1158                 slashes = 3;
1159             }
1160 
1161             switch(slashes) {
1162             case 1:
1163             case 3:
1164                 next[0] = next[1] = next[2] = '/';
1165                 len = 3;
1166                 break;
1167             case 0:
1168                 len = 0;
1169                 break;
1170             default:
1171                 next[0] = next[1] = '/';
1172                 len = 2;
1173                 break;
1174             }
1175         }
1176         if(len == 0) {
1177 
1178             if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1179                 stop_escaping = TRUE;
1180 
1181             if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1182                 stop_escaping = TRUE;
1183 
1184             if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1185 
1186             if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1187                 if(dwFlags & URL_ESCAPE_AS_UTF8) {
1188                     char utf[16];
1189 
1190                     if ((cur >= 0xd800 && cur <= 0xdfff) &&
1191                         (src[1] >= 0xdc00 && src[1] <= 0xdfff))
1192                     {
1193 #ifdef __REACTOS__
1194                         len = WideCharToMultiByte( CP_UTF8, 0, src, 2,
1195                                                    utf, sizeof(utf), NULL, NULL );
1196 #else
1197                         len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, src, 2,
1198                                                    utf, sizeof(utf), NULL, NULL );
1199 #endif
1200                         src++;
1201                     }
1202                     else
1203 #ifdef __REACTOS__
1204                         len = WideCharToMultiByte( CP_UTF8, 0, &cur, 1,
1205                                                    utf, sizeof(utf), NULL, NULL );
1206 #else
1207                         len = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, &cur, 1,
1208                                                    utf, sizeof(utf), NULL, NULL );
1209 #endif
1210 
1211                     if (!len) {
1212                         utf[0] = 0xef;
1213                         utf[1] = 0xbf;
1214                         utf[2] = 0xbd;
1215                         len = 3;
1216                     }
1217 
1218                     for(i = 0; i < len; i++) {
1219                         next[i*3+0] = '%';
1220                         next[i*3+1] = hexDigits[(utf[i] >> 4) & 0xf];
1221                         next[i*3+2] = hexDigits[utf[i] & 0xf];
1222                     }
1223                     len *= 3;
1224                 } else {
1225                     next[0] = '%';
1226                     next[1] = hexDigits[(cur >> 4) & 0xf];
1227                     next[2] = hexDigits[cur & 0xf];
1228                     len = 3;
1229                 }
1230             } else {
1231                 next[0] = cur;
1232                 len = 1;
1233             }
1234             src++;
1235         }
1236 
1237 	if(needed + len <= *pcchEscaped) {
1238 	    memcpy(dst, next, len*sizeof(WCHAR));
1239 	    dst += len;
1240 	}
1241 	needed += len;
1242     }
1243 
1244     if(needed < *pcchEscaped) {
1245         *dst = '\0';
1246         memcpy(pszEscaped, dst_ptr, (needed+1)*sizeof(WCHAR));
1247 
1248         ret = S_OK;
1249     } else {
1250         needed++; /* add one for the '\0' */
1251         ret = E_POINTER;
1252     }
1253     *pcchEscaped = needed;
1254 
1255     HeapFree(GetProcessHeap(), 0, dst_ptr);
1256     return ret;
1257 }
1258 
1259 
1260 /*************************************************************************
1261  *      UrlUnescapeA	[SHLWAPI.@]
1262  *
1263  * Converts Url escape sequences back to ordinary characters.
1264  *
1265  * PARAMS
1266  *  pszUrl        [I/O]  Url to convert
1267  *  pszUnescaped  [O]    Destination for converted Url
1268  *  pcchUnescaped [I/O]  Size of output string
1269  *  dwFlags       [I]    URL_ESCAPE_ Flags from "shlwapi.h"
1270  *
1271  * RETURNS
1272  *  Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1273  *           dwFlags includes URL_ESCAPE_INPLACE.
1274  *  Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1275  *           this case pcchUnescaped is set to the size required.
1276  * NOTES
1277  *  If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1278  *  the first occurrence of either a '?' or '#' character.
1279  */
1280 HRESULT WINAPI UrlUnescapeA(
1281 	LPSTR pszUrl,
1282 	LPSTR pszUnescaped,
1283 	LPDWORD pcchUnescaped,
1284 	DWORD dwFlags)
1285 {
1286     char *dst, next;
1287     LPCSTR src;
1288     HRESULT ret;
1289     DWORD needed;
1290     BOOL stop_unescaping = FALSE;
1291 
1292     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1293 	  pcchUnescaped, dwFlags);
1294 
1295     if (!pszUrl) return E_INVALIDARG;
1296 
1297     if(dwFlags & URL_UNESCAPE_INPLACE)
1298         dst = pszUrl;
1299     else
1300     {
1301         if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1302         dst = pszUnescaped;
1303     }
1304 
1305     for(src = pszUrl, needed = 0; *src; src++, needed++) {
1306         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1307 	   (*src == '#' || *src == '?')) {
1308 	    stop_unescaping = TRUE;
1309 	    next = *src;
1310 	} else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1311 		  && stop_unescaping == FALSE) {
1312 	    INT ih;
1313 	    char buf[3];
1314 	    memcpy(buf, src + 1, 2);
1315 	    buf[2] = '\0';
1316 	    ih = strtol(buf, NULL, 16);
1317 	    next = (CHAR) ih;
1318 	    src += 2; /* Advance to end of escape */
1319 	} else
1320 	    next = *src;
1321 
1322 	if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1323 	    *dst++ = next;
1324     }
1325 
1326     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1327         *dst = '\0';
1328 	ret = S_OK;
1329     } else {
1330         needed++; /* add one for the '\0' */
1331 	ret = E_POINTER;
1332     }
1333     if(!(dwFlags & URL_UNESCAPE_INPLACE))
1334         *pcchUnescaped = needed;
1335 
1336     if (ret == S_OK) {
1337 	TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1338 	      debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1339     }
1340 
1341     return ret;
1342 }
1343 
1344 /*************************************************************************
1345  *      UrlUnescapeW	[SHLWAPI.@]
1346  *
1347  * See UrlUnescapeA.
1348  */
1349 HRESULT WINAPI UrlUnescapeW(
1350 	LPWSTR pszUrl,
1351 	LPWSTR pszUnescaped,
1352 	LPDWORD pcchUnescaped,
1353 	DWORD dwFlags)
1354 {
1355     WCHAR *dst, next;
1356     LPCWSTR src;
1357     HRESULT ret;
1358     DWORD needed;
1359     BOOL stop_unescaping = FALSE;
1360 
1361     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1362 	  pcchUnescaped, dwFlags);
1363 
1364     if(!pszUrl) return E_INVALIDARG;
1365 
1366     if(dwFlags & URL_UNESCAPE_INPLACE)
1367         dst = pszUrl;
1368     else
1369     {
1370         if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1371         dst = pszUnescaped;
1372     }
1373 
1374     for(src = pszUrl, needed = 0; *src; src++, needed++) {
1375         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1376            (*src == '#' || *src == '?')) {
1377 	    stop_unescaping = TRUE;
1378 	    next = *src;
1379         } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1380 		  && stop_unescaping == FALSE) {
1381 	    INT ih;
1382 	    WCHAR buf[5] = {'0','x',0};
1383 	    memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1384 	    buf[4] = 0;
1385 	    StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1386 	    next = (WCHAR) ih;
1387 	    src += 2; /* Advance to end of escape */
1388 	} else
1389 	    next = *src;
1390 
1391 	if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1392 	    *dst++ = next;
1393     }
1394 
1395     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1396         *dst = '\0';
1397 	ret = S_OK;
1398     } else {
1399         needed++; /* add one for the '\0' */
1400 	ret = E_POINTER;
1401     }
1402     if(!(dwFlags & URL_UNESCAPE_INPLACE))
1403         *pcchUnescaped = needed;
1404 
1405     if (ret == S_OK) {
1406 	TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1407 	      debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1408     }
1409 
1410     return ret;
1411 }
1412 
1413 /*************************************************************************
1414  *      UrlGetLocationA 	[SHLWAPI.@]
1415  *
1416  * Get the location from a Url.
1417  *
1418  * PARAMS
1419  *  pszUrl [I] Url to get the location from
1420  *
1421  * RETURNS
1422  *  A pointer to the start of the location in pszUrl, or NULL if there is
1423  *  no location.
1424  *
1425  * NOTES
1426  *  - MSDN erroneously states that "The location is the segment of the Url
1427  *    starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1428  *    stop at '?' and always return a NULL in this case.
1429  *  - MSDN also erroneously states that "If a file URL has a query string,
1430  *    the returned string is the query string". In all tested cases, if the
1431  *    Url starts with "fi" then a NULL is returned. V5 gives the following results:
1432  *|       Result   Url
1433  *|       ------   ---
1434  *|       NULL     file://aa/b/cd#hohoh
1435  *|       #hohoh   http://aa/b/cd#hohoh
1436  *|       NULL     fi://aa/b/cd#hohoh
1437  *|       #hohoh   ff://aa/b/cd#hohoh
1438  */
1439 LPCSTR WINAPI UrlGetLocationA(
1440 	LPCSTR pszUrl)
1441 {
1442     PARSEDURLA base;
1443     DWORD res1;
1444 
1445     base.cbSize = sizeof(base);
1446     res1 = ParseURLA(pszUrl, &base);
1447     if (res1) return NULL;  /* invalid scheme */
1448 
1449     /* if scheme is file: then never return pointer */
1450     if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1451 
1452     /* Look for '#' and return its addr */
1453     return strchr(base.pszSuffix, '#');
1454 }
1455 
1456 /*************************************************************************
1457  *      UrlGetLocationW 	[SHLWAPI.@]
1458  *
1459  * See UrlGetLocationA.
1460  */
1461 LPCWSTR WINAPI UrlGetLocationW(
1462 	LPCWSTR pszUrl)
1463 {
1464     PARSEDURLW base;
1465     DWORD res1;
1466 
1467     base.cbSize = sizeof(base);
1468     res1 = ParseURLW(pszUrl, &base);
1469     if (res1) return NULL;  /* invalid scheme */
1470 
1471     /* if scheme is file: then never return pointer */
1472     if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1473 
1474     /* Look for '#' and return its addr */
1475     return strchrW(base.pszSuffix, '#');
1476 }
1477 
1478 /*************************************************************************
1479  *      UrlCompareA	[SHLWAPI.@]
1480  *
1481  * Compare two Urls.
1482  *
1483  * PARAMS
1484  *  pszUrl1      [I] First Url to compare
1485  *  pszUrl2      [I] Url to compare to pszUrl1
1486  *  fIgnoreSlash [I] TRUE = compare only up to a final slash
1487  *
1488  * RETURNS
1489  *  less than zero, zero, or greater than zero indicating pszUrl2 is greater
1490  *  than, equal to, or less than pszUrl1 respectively.
1491  */
1492 INT WINAPI UrlCompareA(
1493 	LPCSTR pszUrl1,
1494 	LPCSTR pszUrl2,
1495 	BOOL fIgnoreSlash)
1496 {
1497     INT ret, len, len1, len2;
1498 
1499     if (!fIgnoreSlash)
1500 	return strcmp(pszUrl1, pszUrl2);
1501     len1 = strlen(pszUrl1);
1502     if (pszUrl1[len1-1] == '/') len1--;
1503     len2 = strlen(pszUrl2);
1504     if (pszUrl2[len2-1] == '/') len2--;
1505     if (len1 == len2)
1506 	return strncmp(pszUrl1, pszUrl2, len1);
1507     len = min(len1, len2);
1508     ret = strncmp(pszUrl1, pszUrl2, len);
1509     if (ret) return ret;
1510     if (len1 > len2) return 1;
1511     return -1;
1512 }
1513 
1514 /*************************************************************************
1515  *      UrlCompareW	[SHLWAPI.@]
1516  *
1517  * See UrlCompareA.
1518  */
1519 INT WINAPI UrlCompareW(
1520 	LPCWSTR pszUrl1,
1521 	LPCWSTR pszUrl2,
1522 	BOOL fIgnoreSlash)
1523 {
1524     INT ret;
1525     size_t len, len1, len2;
1526 
1527     if (!fIgnoreSlash)
1528 	return strcmpW(pszUrl1, pszUrl2);
1529     len1 = strlenW(pszUrl1);
1530     if (pszUrl1[len1-1] == '/') len1--;
1531     len2 = strlenW(pszUrl2);
1532     if (pszUrl2[len2-1] == '/') len2--;
1533     if (len1 == len2)
1534 	return strncmpW(pszUrl1, pszUrl2, len1);
1535     len = min(len1, len2);
1536     ret = strncmpW(pszUrl1, pszUrl2, len);
1537     if (ret) return ret;
1538     if (len1 > len2) return 1;
1539     return -1;
1540 }
1541 
1542 /*************************************************************************
1543  *      HashData	[SHLWAPI.@]
1544  *
1545  * Hash an input block into a variable sized digest.
1546  *
1547  * PARAMS
1548  *  lpSrc    [I] Input block
1549  *  nSrcLen  [I] Length of lpSrc
1550  *  lpDest   [I] Output for hash digest
1551  *  nDestLen [I] Length of lpDest
1552  *
1553  * RETURNS
1554  *  Success: TRUE. lpDest is filled with the computed hash value.
1555  *  Failure: FALSE, if any argument is invalid.
1556  */
1557 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1558                      unsigned char *lpDest, DWORD nDestLen)
1559 {
1560   INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1561 
1562   if (!lpSrc || !lpDest)
1563     return E_INVALIDARG;
1564 
1565   while (destCount >= 0)
1566   {
1567     lpDest[destCount] = (destCount & 0xff);
1568     destCount--;
1569   }
1570 
1571   while (srcCount >= 0)
1572   {
1573     destCount = nDestLen - 1;
1574     while (destCount >= 0)
1575     {
1576       lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1577       destCount--;
1578     }
1579     srcCount--;
1580   }
1581   return S_OK;
1582 }
1583 
1584 /*************************************************************************
1585  *      UrlHashA	[SHLWAPI.@]
1586  *
1587  * Produce a Hash from a Url.
1588  *
1589  * PARAMS
1590  *  pszUrl   [I] Url to hash
1591  *  lpDest   [O] Destinationh for hash
1592  *  nDestLen [I] Length of lpDest
1593  *
1594  * RETURNS
1595  *  Success: S_OK. lpDest is filled with the computed hash value.
1596  *  Failure: E_INVALIDARG, if any argument is invalid.
1597  */
1598 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1599 {
1600   if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1601     return E_INVALIDARG;
1602 
1603   HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1604   return S_OK;
1605 }
1606 
1607 /*************************************************************************
1608  * UrlHashW	[SHLWAPI.@]
1609  *
1610  * See UrlHashA.
1611  */
1612 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1613 {
1614   char szUrl[MAX_PATH];
1615 
1616   TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1617 
1618   if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1619     return E_INVALIDARG;
1620 
1621   /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1622    * return the same digests for the same URL.
1623    */
1624   WideCharToMultiByte(CP_ACP, 0, pszUrl, -1, szUrl, MAX_PATH, NULL, NULL);
1625   HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1626   return S_OK;
1627 }
1628 
1629 /*************************************************************************
1630  *      UrlApplySchemeA	[SHLWAPI.@]
1631  *
1632  * Apply a scheme to a Url.
1633  *
1634  * PARAMS
1635  *  pszIn   [I]   Url to apply scheme to
1636  *  pszOut  [O]   Destination for modified Url
1637  *  pcchOut [I/O] Length of pszOut/destination for length of pszOut
1638  *  dwFlags [I]   URL_ flags from "shlwapi.h"
1639  *
1640  * RETURNS
1641  *  Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1642  *  Failure: An HRESULT error code describing the error.
1643  */
1644 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1645 {
1646     LPWSTR in, out;
1647     HRESULT ret;
1648     DWORD len;
1649 
1650     TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1651             pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1652 
1653     if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1654 
1655     in = HeapAlloc(GetProcessHeap(), 0,
1656                   (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1657     out = in + INTERNET_MAX_URL_LENGTH;
1658 
1659     MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1660     len = INTERNET_MAX_URL_LENGTH;
1661 
1662     ret = UrlApplySchemeW(in, out, &len, dwFlags);
1663     if (ret != S_OK) {
1664         HeapFree(GetProcessHeap(), 0, in);
1665         return ret;
1666     }
1667 
1668     len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1669     if (len > *pcchOut) {
1670         ret = E_POINTER;
1671         goto cleanup;
1672     }
1673 
1674     WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1675     len--;
1676 
1677 cleanup:
1678     *pcchOut = len;
1679     HeapFree(GetProcessHeap(), 0, in);
1680     return ret;
1681 }
1682 
1683 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1684 {
1685     HKEY newkey;
1686     BOOL j;
1687     INT index;
1688     DWORD value_len, data_len, dwType, i;
1689     WCHAR reg_path[MAX_PATH];
1690     WCHAR value[MAX_PATH], data[MAX_PATH];
1691     WCHAR Wxx, Wyy;
1692 
1693     MultiByteToWideChar(CP_ACP, 0,
1694 	      "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1695 			-1, reg_path, MAX_PATH);
1696     RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1697     index = 0;
1698     while(value_len = data_len = MAX_PATH,
1699 	  RegEnumValueW(newkey, index, value, &value_len,
1700 			0, &dwType, (LPVOID)data, &data_len) == 0) {
1701 	TRACE("guess %d %s is %s\n",
1702 	      index, debugstr_w(value), debugstr_w(data));
1703 
1704 	j = FALSE;
1705 	for(i=0; i<value_len; i++) {
1706 	    Wxx = pszIn[i];
1707 	    Wyy = value[i];
1708 	    /* remember that TRUE is not-equal */
1709 	    j = ChrCmpIW(Wxx, Wyy);
1710 	    if (j) break;
1711 	}
1712 	if ((i == value_len) && !j) {
1713 	    if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1714 		*pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1715 		RegCloseKey(newkey);
1716 		return E_POINTER;
1717 	    }
1718 	    strcpyW(pszOut, data);
1719 	    strcatW(pszOut, pszIn);
1720 	    *pcchOut = strlenW(pszOut);
1721 	    TRACE("matched and set to %s\n", debugstr_w(pszOut));
1722 	    RegCloseKey(newkey);
1723 	    return S_OK;
1724 	}
1725 	index++;
1726     }
1727     RegCloseKey(newkey);
1728     return E_FAIL;
1729 }
1730 
1731 static HRESULT URL_CreateFromPath(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl)
1732 {
1733     DWORD needed;
1734     HRESULT ret = S_OK;
1735     WCHAR *pszNewUrl;
1736     WCHAR file_colonW[] = {'f','i','l','e',':',0};
1737     WCHAR three_slashesW[] = {'/','/','/',0};
1738     PARSEDURLW parsed_url;
1739 
1740     parsed_url.cbSize = sizeof(parsed_url);
1741     if(ParseURLW(pszPath, &parsed_url) == S_OK) {
1742         if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
1743             needed = strlenW(pszPath);
1744             if (needed >= *pcchUrl) {
1745                 *pcchUrl = needed + 1;
1746                 return E_POINTER;
1747             } else {
1748                 *pcchUrl = needed;
1749                 return S_FALSE;
1750             }
1751         }
1752     }
1753 
1754     pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
1755     strcpyW(pszNewUrl, file_colonW);
1756     if(isalphaW(pszPath[0]) && pszPath[1] == ':')
1757         strcatW(pszNewUrl, three_slashesW);
1758     strcatW(pszNewUrl, pszPath);
1759     ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
1760     HeapFree(GetProcessHeap(), 0, pszNewUrl);
1761     return ret;
1762 }
1763 
1764 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1765 {
1766     HKEY newkey;
1767     DWORD data_len, dwType;
1768     WCHAR data[MAX_PATH];
1769 
1770     static const WCHAR prefix_keyW[] =
1771         {'S','o','f','t','w','a','r','e',
1772          '\\','M','i','c','r','o','s','o','f','t',
1773          '\\','W','i','n','d','o','w','s',
1774          '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1775          '\\','U','R','L',
1776          '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1777 
1778     /* get and prepend default */
1779     RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1780     data_len = sizeof(data);
1781     RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1782     RegCloseKey(newkey);
1783     if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1784         *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1785         return E_POINTER;
1786     }
1787     strcpyW(pszOut, data);
1788     strcatW(pszOut, pszIn);
1789     *pcchOut = strlenW(pszOut);
1790     TRACE("used default %s\n", debugstr_w(pszOut));
1791     return S_OK;
1792 }
1793 
1794 /*************************************************************************
1795  *      UrlApplySchemeW	[SHLWAPI.@]
1796  *
1797  * See UrlApplySchemeA.
1798  */
1799 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1800 {
1801     PARSEDURLW in_scheme;
1802     DWORD res1;
1803     HRESULT ret;
1804 
1805     TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1806             pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1807 
1808     if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1809 
1810     if (dwFlags & URL_APPLY_GUESSFILE) {
1811         if (*pcchOut > 1 && ':' == pszIn[1]) {
1812             res1 = *pcchOut;
1813             ret = URL_CreateFromPath(pszIn, pszOut, &res1);
1814             if (ret == S_OK || ret == E_POINTER){
1815                 *pcchOut = res1;
1816                 return ret;
1817             }
1818             else if (ret == S_FALSE)
1819             {
1820                 return ret;
1821             }
1822         }
1823     }
1824 
1825     in_scheme.cbSize = sizeof(in_scheme);
1826     /* See if the base has a scheme */
1827     res1 = ParseURLW(pszIn, &in_scheme);
1828     if (res1) {
1829 	/* no scheme in input, need to see if we need to guess */
1830 	if (dwFlags & URL_APPLY_GUESSSCHEME) {
1831 	    if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1832 		return ret;
1833 	}
1834     }
1835 
1836     /* If we are here, then either invalid scheme,
1837      * or no scheme and can't/failed guess.
1838      */
1839     if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1840 	   ((res1 != 0)) ) &&
1841 	 (dwFlags & URL_APPLY_DEFAULT)) {
1842 	/* find and apply default scheme */
1843 	return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1844     }
1845 
1846     return S_FALSE;
1847 }
1848 
1849 /*************************************************************************
1850  *      UrlIsA  	[SHLWAPI.@]
1851  *
1852  * Determine if a Url is of a certain class.
1853  *
1854  * PARAMS
1855  *  pszUrl [I] Url to check
1856  *  Urlis  [I] URLIS_ constant from "shlwapi.h"
1857  *
1858  * RETURNS
1859  *  TRUE if pszUrl belongs to the class type in Urlis.
1860  *  FALSE Otherwise.
1861  */
1862 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1863 {
1864     PARSEDURLA base;
1865     DWORD res1;
1866     LPCSTR last;
1867 
1868     TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1869 
1870     if(!pszUrl)
1871         return FALSE;
1872 
1873     switch (Urlis) {
1874 
1875     case URLIS_OPAQUE:
1876 	base.cbSize = sizeof(base);
1877 	res1 = ParseURLA(pszUrl, &base);
1878 	if (res1) return FALSE;  /* invalid scheme */
1879 	switch (base.nScheme)
1880 	{
1881 	case URL_SCHEME_MAILTO:
1882 	case URL_SCHEME_SHELL:
1883 	case URL_SCHEME_JAVASCRIPT:
1884 	case URL_SCHEME_VBSCRIPT:
1885 	case URL_SCHEME_ABOUT:
1886 	    return TRUE;
1887 	}
1888 	return FALSE;
1889 
1890     case URLIS_FILEURL:
1891         return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1892                                "file:", 5) == CSTR_EQUAL);
1893 
1894     case URLIS_DIRECTORY:
1895         last = pszUrl + strlen(pszUrl) - 1;
1896         return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1897 
1898     case URLIS_URL:
1899         return PathIsURLA(pszUrl);
1900 
1901     case URLIS_NOHISTORY:
1902     case URLIS_APPLIABLE:
1903     case URLIS_HASQUERY:
1904     default:
1905 	FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1906     }
1907     return FALSE;
1908 }
1909 
1910 /*************************************************************************
1911  *      UrlIsW  	[SHLWAPI.@]
1912  *
1913  * See UrlIsA.
1914  */
1915 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1916 {
1917     static const WCHAR file_colon[] = { 'f','i','l','e',':',0 };
1918     PARSEDURLW base;
1919     DWORD res1;
1920     LPCWSTR last;
1921 
1922     TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1923 
1924     if(!pszUrl)
1925         return FALSE;
1926 
1927     switch (Urlis) {
1928 
1929     case URLIS_OPAQUE:
1930 	base.cbSize = sizeof(base);
1931 	res1 = ParseURLW(pszUrl, &base);
1932 	if (res1) return FALSE;  /* invalid scheme */
1933 	switch (base.nScheme)
1934 	{
1935 	case URL_SCHEME_MAILTO:
1936 	case URL_SCHEME_SHELL:
1937 	case URL_SCHEME_JAVASCRIPT:
1938 	case URL_SCHEME_VBSCRIPT:
1939 	case URL_SCHEME_ABOUT:
1940 	    return TRUE;
1941 	}
1942 	return FALSE;
1943 
1944     case URLIS_FILEURL:
1945         return (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5,
1946                                file_colon, 5) == CSTR_EQUAL);
1947 
1948     case URLIS_DIRECTORY:
1949         last = pszUrl + strlenW(pszUrl) - 1;
1950         return (last >= pszUrl && (*last == '/' || *last == '\\'));
1951 
1952     case URLIS_URL:
1953         return PathIsURLW(pszUrl);
1954 
1955     case URLIS_NOHISTORY:
1956     case URLIS_APPLIABLE:
1957     case URLIS_HASQUERY:
1958     default:
1959 	FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1960     }
1961     return FALSE;
1962 }
1963 
1964 /*************************************************************************
1965  *      UrlIsNoHistoryA  	[SHLWAPI.@]
1966  *
1967  * Determine if a Url should not be stored in the users history list.
1968  *
1969  * PARAMS
1970  *  pszUrl [I] Url to check
1971  *
1972  * RETURNS
1973  *  TRUE, if pszUrl should be excluded from the history list,
1974  *  FALSE otherwise.
1975  */
1976 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1977 {
1978     return UrlIsA(pszUrl, URLIS_NOHISTORY);
1979 }
1980 
1981 /*************************************************************************
1982  *      UrlIsNoHistoryW  	[SHLWAPI.@]
1983  *
1984  * See UrlIsNoHistoryA.
1985  */
1986 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1987 {
1988     return UrlIsW(pszUrl, URLIS_NOHISTORY);
1989 }
1990 
1991 /*************************************************************************
1992  *      UrlIsOpaqueA  	[SHLWAPI.@]
1993  *
1994  * Determine if a Url is opaque.
1995  *
1996  * PARAMS
1997  *  pszUrl [I] Url to check
1998  *
1999  * RETURNS
2000  *  TRUE if pszUrl is opaque,
2001  *  FALSE Otherwise.
2002  *
2003  * NOTES
2004  *  An opaque Url is one that does not start with "<protocol>://".
2005  */
2006 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
2007 {
2008     return UrlIsA(pszUrl, URLIS_OPAQUE);
2009 }
2010 
2011 /*************************************************************************
2012  *      UrlIsOpaqueW  	[SHLWAPI.@]
2013  *
2014  * See UrlIsOpaqueA.
2015  */
2016 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
2017 {
2018     return UrlIsW(pszUrl, URLIS_OPAQUE);
2019 }
2020 
2021 /*************************************************************************
2022  *  Scans for characters of type "type" and when not matching found,
2023  *  returns pointer to it and length in size.
2024  *
2025  * Characters tested based on RFC 1738
2026  */
2027 static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
2028 {
2029     static DWORD alwayszero = 0;
2030     BOOL cont = TRUE;
2031 
2032     *size = 0;
2033 
2034     switch(type){
2035 
2036     case SCHEME:
2037 	while (cont) {
2038 	    if ( (islowerW(*start) && isalphaW(*start)) ||
2039 		 isdigitW(*start) ||
2040                  (*start == '+') ||
2041                  (*start == '-') ||
2042                  (*start == '.')) {
2043 		start++;
2044 		(*size)++;
2045 	    }
2046 	    else
2047 		cont = FALSE;
2048 	}
2049 
2050 	if(*start != ':')
2051 	    *size = 0;
2052 
2053         break;
2054 
2055     case USERPASS:
2056 	while (cont) {
2057 	    if ( isalphaW(*start) ||
2058 		 isdigitW(*start) ||
2059 		 /* user/password only characters */
2060                  (*start == ';') ||
2061                  (*start == '?') ||
2062                  (*start == '&') ||
2063                  (*start == '=') ||
2064 		 /* *extra* characters */
2065                  (*start == '!') ||
2066                  (*start == '*') ||
2067                  (*start == '\'') ||
2068                  (*start == '(') ||
2069                  (*start == ')') ||
2070                  (*start == ',') ||
2071 		 /* *safe* characters */
2072                  (*start == '$') ||
2073                  (*start == '_') ||
2074                  (*start == '+') ||
2075                  (*start == '-') ||
2076                  (*start == '.') ||
2077                  (*start == ' ')) {
2078 		start++;
2079 		(*size)++;
2080             } else if (*start == '%') {
2081 		if (isxdigitW(*(start+1)) &&
2082 		    isxdigitW(*(start+2))) {
2083 		    start += 3;
2084 		    *size += 3;
2085 		} else
2086 		    cont = FALSE;
2087 	    } else
2088 		cont = FALSE;
2089 	}
2090 	break;
2091 
2092     case PORT:
2093 	while (cont) {
2094 	    if (isdigitW(*start)) {
2095 		start++;
2096 		(*size)++;
2097 	    }
2098 	    else
2099 		cont = FALSE;
2100 	}
2101 	break;
2102 
2103     case HOST:
2104 	while (cont) {
2105 	    if (isalnumW(*start) ||
2106                 (*start == '-') ||
2107                 (*start == '.') ||
2108                 (*start == ' ') ||
2109                 (*start == '*') ) {
2110 		start++;
2111 		(*size)++;
2112 	    }
2113 	    else
2114 		cont = FALSE;
2115 	}
2116 	break;
2117     default:
2118 	FIXME("unknown type %d\n", type);
2119 	return (LPWSTR)&alwayszero;
2120     }
2121     /* TRACE("scanned %d characters next char %p<%c>\n",
2122      *size, start, *start); */
2123     return start;
2124 }
2125 
2126 /*************************************************************************
2127  *  Attempt to parse URL into pieces.
2128  */
2129 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2130 {
2131     LPCWSTR work;
2132 
2133     memset(pl, 0, sizeof(WINE_PARSE_URL));
2134     pl->pScheme = pszUrl;
2135     work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2136     if (!*work || (*work != ':')) goto ErrorExit;
2137     work++;
2138     if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2139     pl->pUserName = work + 2;
2140     work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2141     if (*work == ':' ) {
2142 	/* parse password */
2143 	work++;
2144 	pl->pPassword = work;
2145 	work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2146         if (*work != '@') {
2147 	    /* what we just parsed must be the hostname and port
2148 	     * so reset pointers and clear then let it parse */
2149 	    pl->szUserName = pl->szPassword = 0;
2150 	    work = pl->pUserName - 1;
2151 	    pl->pUserName = pl->pPassword = 0;
2152 	}
2153     } else if (*work == '@') {
2154 	/* no password */
2155 	pl->szPassword = 0;
2156 	pl->pPassword = 0;
2157     } else if (!*work || (*work == '/') || (*work == '.')) {
2158 	/* what was parsed was hostname, so reset pointers and let it parse */
2159 	pl->szUserName = pl->szPassword = 0;
2160 	work = pl->pUserName - 1;
2161 	pl->pUserName = pl->pPassword = 0;
2162     } else goto ErrorExit;
2163 
2164     /* now start parsing hostname or hostnumber */
2165     work++;
2166     pl->pHostName = work;
2167     work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2168     if (*work == ':') {
2169 	/* parse port */
2170 	work++;
2171 	pl->pPort = work;
2172 	work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2173     }
2174     if (*work == '/') {
2175 	/* see if query string */
2176         pl->pQuery = strchrW(work, '?');
2177 	if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2178     }
2179   SuccessExit:
2180     TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2181 	  pl->pScheme, pl->szScheme,
2182 	  pl->pUserName, pl->szUserName,
2183 	  pl->pPassword, pl->szPassword,
2184 	  pl->pHostName, pl->szHostName,
2185 	  pl->pPort, pl->szPort,
2186 	  pl->pQuery, pl->szQuery);
2187     return S_OK;
2188   ErrorExit:
2189     FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2190     return E_INVALIDARG;
2191 }
2192 
2193 /*************************************************************************
2194  *      UrlGetPartA  	[SHLWAPI.@]
2195  *
2196  * Retrieve part of a Url.
2197  *
2198  * PARAMS
2199  *  pszIn   [I]   Url to parse
2200  *  pszOut  [O]   Destination for part of pszIn requested
2201  *  pcchOut [I]   Size of pszOut
2202  *          [O]   length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2203  *                needed size of pszOut INCLUDING '\0'.
2204  *  dwPart  [I]   URL_PART_ enum from "shlwapi.h"
2205  *  dwFlags [I]   URL_ flags from "shlwapi.h"
2206  *
2207  * RETURNS
2208  *  Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2209  *  Failure: An HRESULT error code describing the error.
2210  */
2211 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2212 			   DWORD dwPart, DWORD dwFlags)
2213 {
2214     LPWSTR in, out;
2215     DWORD ret, len, len2;
2216 
2217     if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2218         return E_INVALIDARG;
2219 
2220     in = HeapAlloc(GetProcessHeap(), 0,
2221 			      (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2222     out = in + INTERNET_MAX_URL_LENGTH;
2223 
2224     MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2225 
2226     len = INTERNET_MAX_URL_LENGTH;
2227     ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2228 
2229     if (FAILED(ret)) {
2230 	HeapFree(GetProcessHeap(), 0, in);
2231 	return ret;
2232     }
2233 
2234     len2 = WideCharToMultiByte(CP_ACP, 0, out, len, NULL, 0, NULL, NULL);
2235     if (len2 > *pcchOut) {
2236 	*pcchOut = len2+1;
2237 	HeapFree(GetProcessHeap(), 0, in);
2238 	return E_POINTER;
2239     }
2240     len2 = WideCharToMultiByte(CP_ACP, 0, out, len+1, pszOut, *pcchOut, NULL, NULL);
2241     *pcchOut = len2-1;
2242     HeapFree(GetProcessHeap(), 0, in);
2243     return ret;
2244 }
2245 
2246 /*************************************************************************
2247  *      UrlGetPartW  	[SHLWAPI.@]
2248  *
2249  * See UrlGetPartA.
2250  */
2251 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2252 			   DWORD dwPart, DWORD dwFlags)
2253 {
2254     WINE_PARSE_URL pl;
2255     HRESULT ret;
2256     DWORD scheme, size, schsize;
2257     LPCWSTR addr, schaddr;
2258 
2259     TRACE("(%s %p %p(%d) %08x %08x)\n",
2260 	  debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2261 
2262     if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2263         return E_INVALIDARG;
2264 
2265     *pszOut = '\0';
2266 
2267     addr = strchrW(pszIn, ':');
2268     if(!addr)
2269         scheme = URL_SCHEME_UNKNOWN;
2270     else
2271         scheme = get_scheme_code(pszIn, addr-pszIn);
2272 
2273     ret = URL_ParseUrl(pszIn, &pl);
2274 
2275 	switch (dwPart) {
2276 	case URL_PART_SCHEME:
2277 	    if (!pl.szScheme) {
2278 	        *pcchOut = 0;
2279 	        return S_FALSE;
2280 	    }
2281 	    addr = pl.pScheme;
2282 	    size = pl.szScheme;
2283 	    break;
2284 
2285 	case URL_PART_HOSTNAME:
2286             switch(scheme) {
2287             case URL_SCHEME_FTP:
2288             case URL_SCHEME_HTTP:
2289             case URL_SCHEME_GOPHER:
2290             case URL_SCHEME_TELNET:
2291             case URL_SCHEME_FILE:
2292             case URL_SCHEME_HTTPS:
2293                 break;
2294             default:
2295                 *pcchOut = 0;
2296                 return E_FAIL;
2297             }
2298 
2299             if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2300                         (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2301                 *pcchOut = 0;
2302                 return S_FALSE;
2303             }
2304 
2305 	    if (!pl.szHostName) {
2306 	        *pcchOut = 0;
2307 	        return S_FALSE;
2308 	    }
2309 	    addr = pl.pHostName;
2310 	    size = pl.szHostName;
2311 	    break;
2312 
2313 	case URL_PART_USERNAME:
2314 	    if (!pl.szUserName) {
2315 	        *pcchOut = 0;
2316 	        return S_FALSE;
2317 	    }
2318 	    addr = pl.pUserName;
2319 	    size = pl.szUserName;
2320 	    break;
2321 
2322 	case URL_PART_PASSWORD:
2323 	    if (!pl.szPassword) {
2324 	        *pcchOut = 0;
2325 	        return S_FALSE;
2326 	    }
2327 	    addr = pl.pPassword;
2328 	    size = pl.szPassword;
2329 	    break;
2330 
2331 	case URL_PART_PORT:
2332 	    if (!pl.szPort) {
2333 	        *pcchOut = 0;
2334 	        return S_FALSE;
2335 	    }
2336 	    addr = pl.pPort;
2337 	    size = pl.szPort;
2338 	    break;
2339 
2340 	case URL_PART_QUERY:
2341 	    if (!pl.szQuery) {
2342 	        *pcchOut = 0;
2343 	        return S_FALSE;
2344 	    }
2345 	    addr = pl.pQuery;
2346 	    size = pl.szQuery;
2347 	    break;
2348 
2349 	default:
2350 	    *pcchOut = 0;
2351 	    return E_INVALIDARG;
2352 	}
2353 
2354 	if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2355             if(!pl.pScheme || !pl.szScheme) {
2356                 *pcchOut = 0;
2357                 return E_FAIL;
2358             }
2359             schaddr = pl.pScheme;
2360             schsize = pl.szScheme;
2361             if (*pcchOut < schsize + size + 2) {
2362                 *pcchOut = schsize + size + 2;
2363                 return E_POINTER;
2364             }
2365             memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2366             pszOut[schsize] = ':';
2367             memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2368             pszOut[schsize+1+size] = 0;
2369             *pcchOut = schsize + 1 + size;
2370 	}
2371 	else {
2372 	    if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2373             memcpy(pszOut, addr, size*sizeof(WCHAR));
2374             pszOut[size] = 0;
2375 	    *pcchOut = size;
2376 	}
2377 	TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2378 
2379     return ret;
2380 }
2381 
2382 /*************************************************************************
2383  * PathIsURLA	[SHLWAPI.@]
2384  *
2385  * Check if the given path is a Url.
2386  *
2387  * PARAMS
2388  *  lpszPath [I] Path to check.
2389  *
2390  * RETURNS
2391  *  TRUE  if lpszPath is a Url.
2392  *  FALSE if lpszPath is NULL or not a Url.
2393  */
2394 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2395 {
2396     PARSEDURLA base;
2397     HRESULT hres;
2398 
2399     TRACE("%s\n", debugstr_a(lpstrPath));
2400 
2401     if (!lpstrPath || !*lpstrPath) return FALSE;
2402 
2403     /* get protocol        */
2404     base.cbSize = sizeof(base);
2405     hres = ParseURLA(lpstrPath, &base);
2406     return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2407 }
2408 
2409 /*************************************************************************
2410  * PathIsURLW	[SHLWAPI.@]
2411  *
2412  * See PathIsURLA.
2413  */
2414 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2415 {
2416     PARSEDURLW base;
2417     HRESULT hres;
2418 
2419     TRACE("%s\n", debugstr_w(lpstrPath));
2420 
2421     if (!lpstrPath || !*lpstrPath) return FALSE;
2422 
2423     /* get protocol        */
2424     base.cbSize = sizeof(base);
2425     hres = ParseURLW(lpstrPath, &base);
2426     return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2427 }
2428 
2429 /*************************************************************************
2430  *      UrlCreateFromPathA  	[SHLWAPI.@]
2431  *
2432  * See UrlCreateFromPathW
2433  */
2434 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2435 {
2436     WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2437     WCHAR *urlW = bufW;
2438     UNICODE_STRING pathW;
2439     HRESULT ret;
2440     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2441 
2442     if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2443         return E_INVALIDARG;
2444     if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2445         urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2446         ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2447     }
2448     if(ret == S_OK || ret == S_FALSE) {
2449         RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2450         if(*pcchUrl > lenA) {
2451             RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2452             pszUrl[lenA] = 0;
2453             *pcchUrl = lenA;
2454         } else {
2455             *pcchUrl = lenA + 1;
2456             ret = E_POINTER;
2457         }
2458     }
2459     if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2460     RtlFreeUnicodeString(&pathW);
2461     return ret;
2462 }
2463 
2464 /*************************************************************************
2465  *      UrlCreateFromPathW  	[SHLWAPI.@]
2466  *
2467  * Create a Url from a file path.
2468  *
2469  * PARAMS
2470  *  pszPath [I]    Path to convert
2471  *  pszUrl  [O]    Destination for the converted Url
2472  *  pcchUrl [I/O]  Length of pszUrl
2473  *  dwReserved [I] Reserved, must be 0
2474  *
2475  * RETURNS
2476  *  Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2477  *  Failure: An HRESULT error code.
2478  */
2479 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2480 {
2481     HRESULT ret;
2482 
2483     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2484 
2485     /* Validate arguments */
2486     if (dwReserved != 0)
2487         return E_INVALIDARG;
2488     if (!pszUrl || !pcchUrl)
2489         return E_INVALIDARG;
2490 
2491     ret = URL_CreateFromPath(pszPath, pszUrl, pcchUrl);
2492 
2493     if (S_FALSE == ret)
2494         strcpyW(pszUrl, pszPath);
2495 
2496     return ret;
2497 }
2498 
2499 /*************************************************************************
2500  *      SHAutoComplete  	[SHLWAPI.@]
2501  *
2502  * Enable auto-completion for an edit control.
2503  *
2504  * PARAMS
2505  *  hwndEdit [I] Handle of control to enable auto-completion for
2506  *  dwFlags  [I] SHACF_ flags from "shlwapi.h"
2507  *
2508  * RETURNS
2509  *  Success: S_OK. Auto-completion is enabled for the control.
2510  *  Failure: An HRESULT error code indicating the error.
2511  */
2512 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2513 {
2514   FIXME("stub\n");
2515   return S_FALSE;
2516 }
2517 
2518 /*************************************************************************
2519  *  MLBuildResURLA	[SHLWAPI.405]
2520  *
2521  * Create a Url pointing to a resource in a module.
2522  *
2523  * PARAMS
2524  *  lpszLibName [I] Name of the module containing the resource
2525  *  hMod        [I] Callers module handle
2526  *  dwFlags     [I] Undocumented flags for loading the module
2527  *  lpszRes     [I] Resource name
2528  *  lpszDest    [O] Destination for resulting Url
2529  *  dwDestLen   [I] Length of lpszDest
2530  *
2531  * RETURNS
2532  *  Success: S_OK. lpszDest contains the resource Url.
2533  *  Failure: E_INVALIDARG, if any argument is invalid, or
2534  *           E_FAIL if dwDestLen is too small.
2535  */
2536 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2537                               LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2538 {
2539   WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2540   HRESULT hRet;
2541 
2542   if (lpszLibName)
2543     MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2544 
2545   if (lpszRes)
2546     MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2547 
2548   if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2549     dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2550 
2551   hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2552                         lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2553   if (SUCCEEDED(hRet) && lpszDest)
2554     WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, NULL, NULL);
2555 
2556   return hRet;
2557 }
2558 
2559 /*************************************************************************
2560  *  MLBuildResURLA	[SHLWAPI.406]
2561  *
2562  * See MLBuildResURLA.
2563  */
2564 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2565                               LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2566 {
2567   static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2568 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2569   HRESULT hRet = E_FAIL;
2570 
2571   TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2572         debugstr_w(lpszRes), lpszDest, dwDestLen);
2573 
2574   if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2575       !lpszDest || (dwFlags && dwFlags != 2))
2576     return E_INVALIDARG;
2577 
2578   if (dwDestLen >= szResLen + 1)
2579   {
2580     dwDestLen -= (szResLen + 1);
2581     memcpy(lpszDest, szRes, sizeof(szRes));
2582 
2583     hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2584 
2585     if (hMod)
2586     {
2587       WCHAR szBuff[MAX_PATH];
2588       DWORD len;
2589 
2590       len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2591       if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2592       {
2593         DWORD dwPathLen = strlenW(szBuff) + 1;
2594 
2595         if (dwDestLen >= dwPathLen)
2596         {
2597           DWORD dwResLen;
2598 
2599           dwDestLen -= dwPathLen;
2600           memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2601 
2602           dwResLen = strlenW(lpszRes) + 1;
2603           if (dwDestLen >= dwResLen + 1)
2604           {
2605             lpszDest[szResLen + dwPathLen-1] = '/';
2606             memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2607             hRet = S_OK;
2608           }
2609         }
2610       }
2611       MLFreeLibrary(hMod);
2612     }
2613   }
2614   return hRet;
2615 }
2616 
2617 /***********************************************************************
2618  *             UrlFixupW [SHLWAPI.462]
2619  *
2620  * Checks the scheme part of a URL and attempts to correct misspellings.
2621  *
2622  * PARAMS
2623  *  lpszUrl           [I] Pointer to the URL to be corrected
2624  *  lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2625  *  dwMaxChars        [I] Maximum size of corrected URL
2626  *
2627  * RETURNS
2628  *  success: S_OK if URL corrected or already correct
2629  *  failure: S_FALSE if unable to correct / COM error code if other error
2630  *
2631  */
2632 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2633 {
2634     DWORD srcLen;
2635 
2636     FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2637 
2638     if (!url)
2639         return E_FAIL;
2640 
2641     srcLen = lstrlenW(url) + 1;
2642 
2643     /* For now just copy the URL directly */
2644     lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2645 
2646     return S_OK;
2647 }
2648 
2649 /*************************************************************************
2650  * IsInternetESCEnabled [SHLWAPI.@]
2651  */
2652 BOOL WINAPI IsInternetESCEnabled(void)
2653 {
2654     FIXME(": stub\n");
2655     return FALSE;
2656 }
2657