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