d3dcompiler: Move D3DCreateBlob() to blob.c.
[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(0, 0, canonical, -1, pszCanonicalized,
270                 *pcchCanonicalized+1, 0, 0);
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(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
631     MultiByteToWideChar(0, 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(0, 0, combined, len, 0, 0, 0, 0);
642     if (len2 > *pcchCombined) {
643         *pcchCombined = len2;
644         HeapFree(GetProcessHeap(), 0, base);
645         return E_POINTER;
646     }
647     WideCharToMultiByte(0, 0, combined, len+1, pszCombined, (*pcchCombined)+1,
648                         0, 0);
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 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         /* mk is a special case */
707         if(base.nScheme == URL_SCHEME_MK) {
708             static const WCHAR wsz[] = {':',':',0};
709
710             WCHAR *ptr = strstrW(base.pszSuffix, wsz);
711             if(ptr) {
712                 int delta;
713
714                 ptr += 2;
715                 delta = ptr-base.pszSuffix;
716                 base.cchProtocol += delta;
717                 base.pszSuffix += delta;
718                 base.cchSuffix -= delta;
719             }
720         }else {
721             /* get size of location field (if it exists) */
722             work = (LPWSTR)base.pszSuffix;
723             sizeloc = 0;
724             if (*work++ == '/') {
725                 if (*work++ == '/') {
726                     /* At this point have start of location and
727                      * it ends at next '/' or end of string.
728                      */
729                     while(*work && (*work != '/')) work++;
730                     sizeloc = (DWORD)(work - base.pszSuffix);
731                 }
732             }
733         }
734
735         /* If there is a '#' and the characters immediately preceding it are
736          * ".htm[l]", then begin looking for the last leaf starting from
737          * the '#'. Otherwise the '#' is not meaningful and just start
738          * looking from the end. */
739         if ((work = strchrW(base.pszSuffix + sizeloc, '#'))) {
740             const WCHAR htmlW[] = {'.','h','t','m','l',0};
741             const int len_htmlW = 5;
742             const WCHAR htmW[] = {'.','h','t','m',0};
743             const int len_htmW = 4;
744
745             if (base.nScheme == URL_SCHEME_HTTP || base.nScheme == URL_SCHEME_HTTPS)
746                 manual_search = TRUE;
747             else if (work - base.pszSuffix > len_htmW * sizeof(WCHAR)) {
748                 work -= len_htmW;
749                 if (strncmpiW(work, htmW, len_htmW) == 0)
750                     manual_search = TRUE;
751                 work += len_htmW;
752             }
753
754             if (!manual_search &&
755                     work - base.pszSuffix > len_htmlW * sizeof(WCHAR)) {
756                 work -= len_htmlW;
757                 if (strncmpiW(work, htmlW, len_htmlW) == 0)
758                     manual_search = TRUE;
759                 work += len_htmlW;
760             }
761         }
762
763         if (manual_search) {
764             /* search backwards starting from the current position */
765             while (*work != '/' && work > base.pszSuffix + sizeloc)
766                 --work;
767             base.cchSuffix = work - base.pszSuffix + 1;
768         }else {
769             /* search backwards starting from the end of the string */
770             work = strrchrW((base.pszSuffix+sizeloc), '/');
771             if (work) {
772                 len = (DWORD)(work - base.pszSuffix + 1);
773                 base.cchSuffix = len;
774             }else
775                 base.cchSuffix = sizeloc;
776         }
777
778         /*
779          * At this point:
780          *    .pszSuffix   points to location (starting with '//')
781          *    .cchSuffix   length of location (above) and rest less the last
782          *                 leaf (if any)
783          *    sizeloc   length of location (above) up to but not including
784          *              the last '/'
785          */
786
787         res2 = ParseURLW(mrelative, &relative);
788         if (res2) {
789             /* no scheme in pszRelative */
790             TRACE("no scheme detected in Relative\n");
791             relative.pszSuffix = mrelative;  /* case 3,4,5 depends on this */
792             relative.cchSuffix = strlenW(mrelative);
793             if (*pszRelative  == ':') {
794                 /* case that is either left alone or uses pszBase */
795                 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
796                     process_case = 5;
797                     break;
798                 }
799                 process_case = 1;
800                 break;
801             }
802             if (isalnum(*mrelative) && (*(mrelative + 1) == ':')) {
803                 /* case that becomes "file:///" */
804                 strcpyW(preliminary, myfilestr);
805                 process_case = 1;
806                 break;
807             }
808             if ((*mrelative == '/') && (*(mrelative+1) == '/')) {
809                 /* pszRelative has location and rest */
810                 process_case = 3;
811                 break;
812             }
813             if (*mrelative == '/') {
814                 /* case where pszRelative is root to location */
815                 process_case = 4;
816                 break;
817             }
818             if (*mrelative == '#') {
819                 if(!(work = strchrW(base.pszSuffix+base.cchSuffix, '#')))
820                     work = (LPWSTR)base.pszSuffix + strlenW(base.pszSuffix);
821
822                 memcpy(preliminary, base.pszProtocol, (work-base.pszProtocol)*sizeof(WCHAR));
823                 preliminary[work-base.pszProtocol] = '\0';
824                 process_case = 1;
825                 break;
826             }
827             process_case = (*base.pszSuffix == '/' || base.nScheme == URL_SCHEME_MK) ? 5 : 3;
828             break;
829         }
830
831         /* handle cases where pszRelative has scheme */
832         if ((base.cchProtocol == relative.cchProtocol) &&
833             (strncmpW(base.pszProtocol, relative.pszProtocol, base.cchProtocol) == 0)) {
834
835             /* since the schemes are the same */
836             if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
837                 /* case where pszRelative replaces location and following */
838                 process_case = 3;
839                 break;
840             }
841             if (*relative.pszSuffix == '/') {
842                 /* case where pszRelative is root to location */
843                 process_case = 4;
844                 break;
845             }
846             /* replace either just location if base's location starts with a
847              * slash or otherwise everything */
848             process_case = (*base.pszSuffix == '/') ? 5 : 1;
849             break;
850         }
851         if ((*relative.pszSuffix == '/') && (*(relative.pszSuffix+1) == '/')) {
852             /* case where pszRelative replaces scheme, location,
853              * and following and handles PLUGGABLE
854              */
855             process_case = 2;
856             break;
857         }
858         process_case = 1;
859         break;
860     } while(FALSE); /* a little trick to allow easy exit from nested if's */
861
862     ret = S_OK;
863     switch (process_case) {
864
865     case 1:  /*
866               * Return pszRelative appended to what ever is in pszCombined,
867               * (which may the string "file:///"
868               */
869         strcatW(preliminary, mrelative);
870         break;
871
872     case 2:  /* case where pszRelative replaces scheme, and location */
873         strcpyW(preliminary, mrelative);
874         break;
875
876     case 3:  /*
877               * Return the pszBase scheme with pszRelative. Basically
878               * keeps the scheme and replaces the domain and following.
879               */
880         memcpy(preliminary, base.pszProtocol, (base.cchProtocol + 1)*sizeof(WCHAR));
881         work = preliminary + base.cchProtocol + 1;
882         strcpyW(work, relative.pszSuffix);
883         break;
884
885     case 4:  /*
886               * Return the pszBase scheme and location but everything
887               * after the location is pszRelative. (Replace document
888               * from root on.)
889               */
890         memcpy(preliminary, base.pszProtocol, (base.cchProtocol+1+sizeloc)*sizeof(WCHAR));
891         work = preliminary + base.cchProtocol + 1 + sizeloc;
892         if (dwFlags & URL_PLUGGABLE_PROTOCOL)
893             *(work++) = '/';
894         strcpyW(work, relative.pszSuffix);
895         break;
896
897     case 5:  /*
898               * Return the pszBase without its document (if any) and
899               * append pszRelative after its scheme.
900               */
901         memcpy(preliminary, base.pszProtocol,
902                (base.cchProtocol+1+base.cchSuffix)*sizeof(WCHAR));
903         work = preliminary + base.cchProtocol+1+base.cchSuffix - 1;
904         if (*work++ != '/')
905             *(work++) = '/';
906         strcpyW(work, relative.pszSuffix);
907         break;
908
909     default:
910         FIXME("How did we get here????? process_case=%d\n", process_case);
911         ret = E_INVALIDARG;
912     }
913
914     if (ret == S_OK) {
915         /* Reuse mrelative as temp storage as its already allocated and not needed anymore */
916         if(*pcchCombined == 0)
917             *pcchCombined = 1;
918         ret = UrlCanonicalizeW(preliminary, mrelative, pcchCombined, (dwFlags & ~URL_FILE_USE_PATHURL));
919         if(SUCCEEDED(ret) && pszCombined) {
920             lstrcpyW(pszCombined, mrelative);
921         }
922         TRACE("return-%d len=%d, %s\n",
923               process_case, *pcchCombined, debugstr_w(pszCombined));
924     }
925     HeapFree(GetProcessHeap(), 0, preliminary);
926     return ret;
927 }
928
929 /*************************************************************************
930  *      UrlEscapeA      [SHLWAPI.@]
931  */
932
933 HRESULT WINAPI UrlEscapeA(
934         LPCSTR pszUrl,
935         LPSTR pszEscaped,
936         LPDWORD pcchEscaped,
937         DWORD dwFlags)
938 {
939     WCHAR bufW[INTERNET_MAX_URL_LENGTH];
940     WCHAR *escapedW = bufW;
941     UNICODE_STRING urlW;
942     HRESULT ret;
943     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
944
945     if (!pszEscaped || !pcchEscaped || !*pcchEscaped)
946         return E_INVALIDARG;
947
948     if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl))
949         return E_INVALIDARG;
950     if((ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags)) == E_POINTER) {
951         escapedW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
952         ret = UrlEscapeW(urlW.Buffer, escapedW, &lenW, dwFlags);
953     }
954     if(ret == S_OK) {
955         RtlUnicodeToMultiByteSize(&lenA, escapedW, lenW * sizeof(WCHAR));
956         if(*pcchEscaped > lenA) {
957             RtlUnicodeToMultiByteN(pszEscaped, *pcchEscaped - 1, &lenA, escapedW, lenW * sizeof(WCHAR));
958             pszEscaped[lenA] = 0;
959             *pcchEscaped = lenA;
960         } else {
961             *pcchEscaped = lenA + 1;
962             ret = E_POINTER;
963         }
964     }
965     if(escapedW != bufW) HeapFree(GetProcessHeap(), 0, escapedW);
966     RtlFreeUnicodeString(&urlW);
967     return ret;
968 }
969
970 #define WINE_URL_BASH_AS_SLASH    0x01
971 #define WINE_URL_COLLAPSE_SLASHES 0x02
972 #define WINE_URL_ESCAPE_SLASH     0x04
973 #define WINE_URL_ESCAPE_HASH      0x08
974 #define WINE_URL_ESCAPE_QUESTION  0x10
975 #define WINE_URL_STOP_ON_HASH     0x20
976 #define WINE_URL_STOP_ON_QUESTION 0x40
977
978 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags, DWORD int_flags)
979 {
980
981     if (isalnumW(ch))
982         return FALSE;
983
984     if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
985         if(ch == ' ')
986             return TRUE;
987         else
988             return FALSE;
989     }
990
991     if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
992         return TRUE;
993
994     if (ch <= 31 || ch >= 127)
995         return TRUE;
996
997     else {
998         switch (ch) {
999         case ' ':
1000         case '<':
1001         case '>':
1002         case '\"':
1003         case '{':
1004         case '}':
1005         case '|':
1006         case '\\':
1007         case '^':
1008         case ']':
1009         case '[':
1010         case '`':
1011         case '&':
1012             return TRUE;
1013
1014         case '/':
1015             if (int_flags & WINE_URL_ESCAPE_SLASH) return TRUE;
1016             return FALSE;
1017
1018         case '?':
1019             if (int_flags & WINE_URL_ESCAPE_QUESTION) return TRUE;
1020             return FALSE;
1021
1022         case '#':
1023             if (int_flags & WINE_URL_ESCAPE_HASH) return TRUE;
1024             return FALSE;
1025
1026         default:
1027             return FALSE;
1028         }
1029     }
1030 }
1031
1032
1033 /*************************************************************************
1034  *      UrlEscapeW      [SHLWAPI.@]
1035  *
1036  * Converts unsafe characters in a Url into escape sequences.
1037  *
1038  * PARAMS
1039  *  pszUrl      [I]   Url to modify
1040  *  pszEscaped  [O]   Destination for modified Url
1041  *  pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
1042  *  dwFlags     [I]   URL_ flags from "shlwapi.h"
1043  *
1044  * RETURNS
1045  *  Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
1046  *           contains its length.
1047  *  Failure: E_POINTER, if pszEscaped is not large enough. In this case
1048  *           pcchEscaped is set to the required length.
1049  *
1050  * Converts unsafe characters into their escape sequences.
1051  *
1052  * NOTES
1053  * - By default this function stops converting at the first '?' or
1054  *  '#' character.
1055  * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
1056  *   converted, but the conversion continues past a '?' or '#'.
1057  * - Note that this function did not work well (or at all) in shlwapi version 4.
1058  *
1059  * BUGS
1060  *  Only the following flags are implemented:
1061  *|     URL_ESCAPE_SPACES_ONLY
1062  *|     URL_DONT_ESCAPE_EXTRA_INFO
1063  *|     URL_ESCAPE_SEGMENT_ONLY
1064  *|     URL_ESCAPE_PERCENT
1065  */
1066 HRESULT WINAPI UrlEscapeW(
1067         LPCWSTR pszUrl,
1068         LPWSTR pszEscaped,
1069         LPDWORD pcchEscaped,
1070         DWORD dwFlags)
1071 {
1072     LPCWSTR src;
1073     DWORD needed = 0, ret;
1074     BOOL stop_escaping = FALSE;
1075     WCHAR next[5], *dst = pszEscaped, *dst_ptr = NULL;
1076     INT len;
1077     PARSEDURLW parsed_url;
1078     DWORD int_flags;
1079     DWORD slashes = 0;
1080     static const WCHAR localhost[] = {'l','o','c','a','l','h','o','s','t',0};
1081
1082     TRACE("(%p(%s) %p %p 0x%08x)\n", pszUrl, debugstr_w(pszUrl),
1083             pszEscaped, pcchEscaped, dwFlags);
1084
1085     if(!pszUrl || !pcchEscaped)
1086         return E_INVALIDARG;
1087
1088     if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1089                    URL_ESCAPE_SEGMENT_ONLY |
1090                    URL_DONT_ESCAPE_EXTRA_INFO |
1091                    URL_ESCAPE_PERCENT))
1092         FIXME("Unimplemented flags: %08x\n", dwFlags);
1093
1094     if(pszUrl == pszEscaped) {
1095         dst_ptr = dst = HeapAlloc(GetProcessHeap(), 0, *pcchEscaped*sizeof(WCHAR));
1096         if(!dst)
1097             return E_OUTOFMEMORY;
1098     }
1099
1100     /* fix up flags */
1101     if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1102         /* if SPACES_ONLY specified, reset the other controls */
1103         dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1104                      URL_ESCAPE_PERCENT |
1105                      URL_ESCAPE_SEGMENT_ONLY);
1106
1107     else
1108         /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1109         dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1110
1111
1112     int_flags = 0;
1113     if(dwFlags & URL_ESCAPE_SEGMENT_ONLY) {
1114         int_flags = WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH | WINE_URL_ESCAPE_SLASH;
1115     } else {
1116         parsed_url.cbSize = sizeof(parsed_url);
1117         if(ParseURLW(pszUrl, &parsed_url) != S_OK)
1118             parsed_url.nScheme = URL_SCHEME_INVALID;
1119
1120         TRACE("scheme = %d (%s)\n", parsed_url.nScheme, debugstr_wn(parsed_url.pszProtocol, parsed_url.cchProtocol));
1121
1122         if(dwFlags & URL_DONT_ESCAPE_EXTRA_INFO)
1123             int_flags = WINE_URL_STOP_ON_HASH | WINE_URL_STOP_ON_QUESTION;
1124
1125         switch(parsed_url.nScheme) {
1126         case URL_SCHEME_FILE:
1127             int_flags |= WINE_URL_BASH_AS_SLASH | WINE_URL_COLLAPSE_SLASHES | WINE_URL_ESCAPE_HASH;
1128             int_flags &= ~WINE_URL_STOP_ON_HASH;
1129             break;
1130
1131         case URL_SCHEME_HTTP:
1132         case URL_SCHEME_HTTPS:
1133             int_flags |= WINE_URL_BASH_AS_SLASH;
1134             if(parsed_url.pszSuffix[0] != '/' && parsed_url.pszSuffix[0] != '\\')
1135                 int_flags |= WINE_URL_ESCAPE_SLASH;
1136             break;
1137
1138         case URL_SCHEME_MAILTO:
1139             int_flags |= WINE_URL_ESCAPE_SLASH | WINE_URL_ESCAPE_QUESTION | WINE_URL_ESCAPE_HASH;
1140             int_flags &= ~(WINE_URL_STOP_ON_QUESTION | WINE_URL_STOP_ON_HASH);
1141             break;
1142
1143         case URL_SCHEME_INVALID:
1144             break;
1145
1146         case URL_SCHEME_FTP:
1147         default:
1148             if(parsed_url.pszSuffix[0] != '/')
1149                 int_flags |= WINE_URL_ESCAPE_SLASH;
1150             break;
1151         }
1152     }
1153
1154     for(src = pszUrl; *src; ) {
1155         WCHAR cur = *src;
1156         len = 0;
1157         
1158         if((int_flags & WINE_URL_COLLAPSE_SLASHES) && src == pszUrl + parsed_url.cchProtocol + 1) {
1159             int localhost_len = sizeof(localhost)/sizeof(WCHAR) - 1;
1160             while(cur == '/' || cur == '\\') {
1161                 slashes++;
1162                 cur = *++src;
1163             }
1164             if(slashes == 2 && !strncmpiW(src, localhost, localhost_len)) { /* file://localhost/ -> file:/// */
1165                 if(*(src + localhost_len) == '/' || *(src + localhost_len) == '\\')
1166                 src += localhost_len + 1;
1167                 slashes = 3;
1168             }
1169
1170             switch(slashes) {
1171             case 1:
1172             case 3:
1173                 next[0] = next[1] = next[2] = '/';
1174                 len = 3;
1175                 break;
1176             case 0:
1177                 len = 0;
1178                 break;
1179             default:
1180                 next[0] = next[1] = '/';
1181                 len = 2;
1182                 break;
1183             }
1184         }
1185         if(len == 0) {
1186
1187             if(cur == '#' && (int_flags & WINE_URL_STOP_ON_HASH))
1188                 stop_escaping = TRUE;
1189
1190             if(cur == '?' && (int_flags & WINE_URL_STOP_ON_QUESTION))
1191                 stop_escaping = TRUE;
1192
1193             if(cur == '\\' && (int_flags & WINE_URL_BASH_AS_SLASH) && !stop_escaping) cur = '/';
1194
1195             if(URL_NeedEscapeW(cur, dwFlags, int_flags) && stop_escaping == FALSE) {
1196                 next[0] = '%';
1197                 next[1] = hexDigits[(cur >> 4) & 0xf];
1198                 next[2] = hexDigits[cur & 0xf];
1199                 len = 3;
1200             } else {
1201                 next[0] = cur;
1202                 len = 1;
1203             }
1204             src++;
1205         }
1206
1207         if(needed + len <= *pcchEscaped) {
1208             memcpy(dst, next, len*sizeof(WCHAR));
1209             dst += len;
1210         }
1211         needed += len;
1212     }
1213
1214     if(needed < *pcchEscaped) {
1215         *dst = '\0';
1216         if(pszUrl == pszEscaped)
1217             memcpy(pszEscaped, dst-needed, (needed+1)*sizeof(WCHAR));
1218
1219         ret = S_OK;
1220     } else {
1221         needed++; /* add one for the '\0' */
1222         ret = E_POINTER;
1223     }
1224     *pcchEscaped = needed;
1225
1226     if(pszUrl == pszEscaped)
1227         HeapFree(GetProcessHeap(), 0, dst_ptr);
1228     return ret;
1229 }
1230
1231
1232 /*************************************************************************
1233  *      UrlUnescapeA    [SHLWAPI.@]
1234  *
1235  * Converts Url escape sequences back to ordinary characters.
1236  *
1237  * PARAMS
1238  *  pszUrl        [I/O]  Url to convert
1239  *  pszUnescaped  [O]    Destination for converted Url
1240  *  pcchUnescaped [I/O]  Size of output string
1241  *  dwFlags       [I]    URL_ESCAPE_ Flags from "shlwapi.h"
1242  *
1243  * RETURNS
1244  *  Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1245  *           dwFlags includes URL_ESCAPE_INPLACE.
1246  *  Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1247  *           this case pcchUnescaped is set to the size required.
1248  * NOTES
1249  *  If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1250  *  the first occurrence of either a '?' or '#' character.
1251  */
1252 HRESULT WINAPI UrlUnescapeA(
1253         LPSTR pszUrl,
1254         LPSTR pszUnescaped,
1255         LPDWORD pcchUnescaped,
1256         DWORD dwFlags)
1257 {
1258     char *dst, next;
1259     LPCSTR src;
1260     HRESULT ret;
1261     DWORD needed;
1262     BOOL stop_unescaping = FALSE;
1263
1264     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_a(pszUrl), pszUnescaped,
1265           pcchUnescaped, dwFlags);
1266
1267     if (!pszUrl) return E_INVALIDARG;
1268
1269     if(dwFlags & URL_UNESCAPE_INPLACE)
1270         dst = pszUrl;
1271     else
1272     {
1273         if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1274         dst = pszUnescaped;
1275     }
1276
1277     for(src = pszUrl, needed = 0; *src; src++, needed++) {
1278         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1279            (*src == '#' || *src == '?')) {
1280             stop_unescaping = TRUE;
1281             next = *src;
1282         } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1283                   && stop_unescaping == FALSE) {
1284             INT ih;
1285             char buf[3];
1286             memcpy(buf, src + 1, 2);
1287             buf[2] = '\0';
1288             ih = strtol(buf, NULL, 16);
1289             next = (CHAR) ih;
1290             src += 2; /* Advance to end of escape */
1291         } else
1292             next = *src;
1293
1294         if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1295             *dst++ = next;
1296     }
1297
1298     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1299         *dst = '\0';
1300         ret = S_OK;
1301     } else {
1302         needed++; /* add one for the '\0' */
1303         ret = E_POINTER;
1304     }
1305     if(!(dwFlags & URL_UNESCAPE_INPLACE))
1306         *pcchUnescaped = needed;
1307
1308     if (ret == S_OK) {
1309         TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1310               debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1311     }
1312
1313     return ret;
1314 }
1315
1316 /*************************************************************************
1317  *      UrlUnescapeW    [SHLWAPI.@]
1318  *
1319  * See UrlUnescapeA.
1320  */
1321 HRESULT WINAPI UrlUnescapeW(
1322         LPWSTR pszUrl,
1323         LPWSTR pszUnescaped,
1324         LPDWORD pcchUnescaped,
1325         DWORD dwFlags)
1326 {
1327     WCHAR *dst, next;
1328     LPCWSTR src;
1329     HRESULT ret;
1330     DWORD needed;
1331     BOOL stop_unescaping = FALSE;
1332
1333     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszUrl), pszUnescaped,
1334           pcchUnescaped, dwFlags);
1335
1336     if(!pszUrl) return E_INVALIDARG;
1337
1338     if(dwFlags & URL_UNESCAPE_INPLACE)
1339         dst = pszUrl;
1340     else
1341     {
1342         if (!pszUnescaped || !pcchUnescaped) return E_INVALIDARG;
1343         dst = pszUnescaped;
1344     }
1345
1346     for(src = pszUrl, needed = 0; *src; src++, needed++) {
1347         if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1348            (*src == '#' || *src == '?')) {
1349             stop_unescaping = TRUE;
1350             next = *src;
1351         } else if(*src == '%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1352                   && stop_unescaping == FALSE) {
1353             INT ih;
1354             WCHAR buf[5] = {'0','x',0};
1355             memcpy(buf + 2, src + 1, 2*sizeof(WCHAR));
1356             buf[4] = 0;
1357             StrToIntExW(buf, STIF_SUPPORT_HEX, &ih);
1358             next = (WCHAR) ih;
1359             src += 2; /* Advance to end of escape */
1360         } else
1361             next = *src;
1362
1363         if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1364             *dst++ = next;
1365     }
1366
1367     if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1368         *dst = '\0';
1369         ret = S_OK;
1370     } else {
1371         needed++; /* add one for the '\0' */
1372         ret = E_POINTER;
1373     }
1374     if(!(dwFlags & URL_UNESCAPE_INPLACE))
1375         *pcchUnescaped = needed;
1376
1377     if (ret == S_OK) {
1378         TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1379               debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1380     }
1381
1382     return ret;
1383 }
1384
1385 /*************************************************************************
1386  *      UrlGetLocationA         [SHLWAPI.@]
1387  *
1388  * Get the location from a Url.
1389  *
1390  * PARAMS
1391  *  pszUrl [I] Url to get the location from
1392  *
1393  * RETURNS
1394  *  A pointer to the start of the location in pszUrl, or NULL if there is
1395  *  no location.
1396  *
1397  * NOTES
1398  *  - MSDN erroneously states that "The location is the segment of the Url
1399  *    starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1400  *    stop at '?' and always return a NULL in this case.
1401  *  - MSDN also erroneously states that "If a file URL has a query string,
1402  *    the returned string is the query string". In all tested cases, if the
1403  *    Url starts with "fi" then a NULL is returned. V5 gives the following results:
1404  *|       Result   Url
1405  *|       ------   ---
1406  *|       NULL     file://aa/b/cd#hohoh
1407  *|       #hohoh   http://aa/b/cd#hohoh
1408  *|       NULL     fi://aa/b/cd#hohoh
1409  *|       #hohoh   ff://aa/b/cd#hohoh
1410  */
1411 LPCSTR WINAPI UrlGetLocationA(
1412         LPCSTR pszUrl)
1413 {
1414     PARSEDURLA base;
1415     DWORD res1;
1416
1417     base.cbSize = sizeof(base);
1418     res1 = ParseURLA(pszUrl, &base);
1419     if (res1) return NULL;  /* invalid scheme */
1420
1421     /* if scheme is file: then never return pointer */
1422     if (strncmp(base.pszProtocol, "file", min(4,base.cchProtocol)) == 0) return NULL;
1423
1424     /* Look for '#' and return its addr */
1425     return strchr(base.pszSuffix, '#');
1426 }
1427
1428 /*************************************************************************
1429  *      UrlGetLocationW         [SHLWAPI.@]
1430  *
1431  * See UrlGetLocationA.
1432  */
1433 LPCWSTR WINAPI UrlGetLocationW(
1434         LPCWSTR pszUrl)
1435 {
1436     PARSEDURLW base;
1437     DWORD res1;
1438
1439     base.cbSize = sizeof(base);
1440     res1 = ParseURLW(pszUrl, &base);
1441     if (res1) return NULL;  /* invalid scheme */
1442
1443     /* if scheme is file: then never return pointer */
1444     if (strncmpW(base.pszProtocol, fileW, min(4,base.cchProtocol)) == 0) return NULL;
1445
1446     /* Look for '#' and return its addr */
1447     return strchrW(base.pszSuffix, '#');
1448 }
1449
1450 /*************************************************************************
1451  *      UrlCompareA     [SHLWAPI.@]
1452  *
1453  * Compare two Urls.
1454  *
1455  * PARAMS
1456  *  pszUrl1      [I] First Url to compare
1457  *  pszUrl2      [I] Url to compare to pszUrl1
1458  *  fIgnoreSlash [I] TRUE = compare only up to a final slash
1459  *
1460  * RETURNS
1461  *  less than zero, zero, or greater than zero indicating pszUrl2 is greater
1462  *  than, equal to, or less than pszUrl1 respectively.
1463  */
1464 INT WINAPI UrlCompareA(
1465         LPCSTR pszUrl1,
1466         LPCSTR pszUrl2,
1467         BOOL fIgnoreSlash)
1468 {
1469     INT ret, len, len1, len2;
1470
1471     if (!fIgnoreSlash)
1472         return strcmp(pszUrl1, pszUrl2);
1473     len1 = strlen(pszUrl1);
1474     if (pszUrl1[len1-1] == '/') len1--;
1475     len2 = strlen(pszUrl2);
1476     if (pszUrl2[len2-1] == '/') len2--;
1477     if (len1 == len2)
1478         return strncmp(pszUrl1, pszUrl2, len1);
1479     len = min(len1, len2);
1480     ret = strncmp(pszUrl1, pszUrl2, len);
1481     if (ret) return ret;
1482     if (len1 > len2) return 1;
1483     return -1;
1484 }
1485
1486 /*************************************************************************
1487  *      UrlCompareW     [SHLWAPI.@]
1488  *
1489  * See UrlCompareA.
1490  */
1491 INT WINAPI UrlCompareW(
1492         LPCWSTR pszUrl1,
1493         LPCWSTR pszUrl2,
1494         BOOL fIgnoreSlash)
1495 {
1496     INT ret;
1497     size_t len, len1, len2;
1498
1499     if (!fIgnoreSlash)
1500         return strcmpW(pszUrl1, pszUrl2);
1501     len1 = strlenW(pszUrl1);
1502     if (pszUrl1[len1-1] == '/') len1--;
1503     len2 = strlenW(pszUrl2);
1504     if (pszUrl2[len2-1] == '/') len2--;
1505     if (len1 == len2)
1506         return strncmpW(pszUrl1, pszUrl2, len1);
1507     len = min(len1, len2);
1508     ret = strncmpW(pszUrl1, pszUrl2, len);
1509     if (ret) return ret;
1510     if (len1 > len2) return 1;
1511     return -1;
1512 }
1513
1514 /*************************************************************************
1515  *      HashData        [SHLWAPI.@]
1516  *
1517  * Hash an input block into a variable sized digest.
1518  *
1519  * PARAMS
1520  *  lpSrc    [I] Input block
1521  *  nSrcLen  [I] Length of lpSrc
1522  *  lpDest   [I] Output for hash digest
1523  *  nDestLen [I] Length of lpDest
1524  *
1525  * RETURNS
1526  *  Success: TRUE. lpDest is filled with the computed hash value.
1527  *  Failure: FALSE, if any argument is invalid.
1528  */
1529 HRESULT WINAPI HashData(const unsigned char *lpSrc, DWORD nSrcLen,
1530                      unsigned char *lpDest, DWORD nDestLen)
1531 {
1532   INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1533
1534   if (!lpSrc || !lpDest)
1535     return E_INVALIDARG;
1536
1537   while (destCount >= 0)
1538   {
1539     lpDest[destCount] = (destCount & 0xff);
1540     destCount--;
1541   }
1542
1543   while (srcCount >= 0)
1544   {
1545     destCount = nDestLen - 1;
1546     while (destCount >= 0)
1547     {
1548       lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1549       destCount--;
1550     }
1551     srcCount--;
1552   }
1553   return S_OK;
1554 }
1555
1556 /*************************************************************************
1557  *      UrlHashA        [SHLWAPI.@]
1558  *
1559  * Produce a Hash from a Url.
1560  *
1561  * PARAMS
1562  *  pszUrl   [I] Url to hash
1563  *  lpDest   [O] Destinationh for hash
1564  *  nDestLen [I] Length of lpDest
1565  * 
1566  * RETURNS
1567  *  Success: S_OK. lpDest is filled with the computed hash value.
1568  *  Failure: E_INVALIDARG, if any argument is invalid.
1569  */
1570 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1571 {
1572   if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1573     return E_INVALIDARG;
1574
1575   HashData((const BYTE*)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1576   return S_OK;
1577 }
1578
1579 /*************************************************************************
1580  * UrlHashW     [SHLWAPI.@]
1581  *
1582  * See UrlHashA.
1583  */
1584 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, DWORD nDestLen)
1585 {
1586   char szUrl[MAX_PATH];
1587
1588   TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1589
1590   if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1591     return E_INVALIDARG;
1592
1593   /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1594    * return the same digests for the same URL.
1595    */
1596   WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1597   HashData((const BYTE*)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1598   return S_OK;
1599 }
1600
1601 /*************************************************************************
1602  *      UrlApplySchemeA [SHLWAPI.@]
1603  *
1604  * Apply a scheme to a Url.
1605  *
1606  * PARAMS
1607  *  pszIn   [I]   Url to apply scheme to
1608  *  pszOut  [O]   Destination for modified Url
1609  *  pcchOut [I/O] Length of pszOut/destination for length of pszOut
1610  *  dwFlags [I]   URL_ flags from "shlwapi.h"
1611  *
1612  * RETURNS
1613  *  Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1614  *  Failure: An HRESULT error code describing the error.
1615  */
1616 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1617 {
1618     LPWSTR in, out;
1619     HRESULT ret;
1620     DWORD len;
1621
1622     TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_a(pszIn),
1623             pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1624
1625     if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1626
1627     in = HeapAlloc(GetProcessHeap(), 0,
1628                   (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1629     out = in + INTERNET_MAX_URL_LENGTH;
1630
1631     MultiByteToWideChar(CP_ACP, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1632     len = INTERNET_MAX_URL_LENGTH;
1633
1634     ret = UrlApplySchemeW(in, out, &len, dwFlags);
1635     if (ret != S_OK) {
1636         HeapFree(GetProcessHeap(), 0, in);
1637         return ret;
1638     }
1639
1640     len = WideCharToMultiByte(CP_ACP, 0, out, -1, NULL, 0, NULL, NULL);
1641     if (len > *pcchOut) {
1642         ret = E_POINTER;
1643         goto cleanup;
1644     }
1645
1646     WideCharToMultiByte(CP_ACP, 0, out, -1, pszOut, *pcchOut, NULL, NULL);
1647     len--;
1648
1649 cleanup:
1650     *pcchOut = len;
1651     HeapFree(GetProcessHeap(), 0, in);
1652     return ret;
1653 }
1654
1655 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1656 {
1657     HKEY newkey;
1658     BOOL j;
1659     INT index;
1660     DWORD value_len, data_len, dwType, i;
1661     WCHAR reg_path[MAX_PATH];
1662     WCHAR value[MAX_PATH], data[MAX_PATH];
1663     WCHAR Wxx, Wyy;
1664
1665     MultiByteToWideChar(0, 0,
1666               "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1667                         -1, reg_path, MAX_PATH);
1668     RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1669     index = 0;
1670     while(value_len = data_len = MAX_PATH,
1671           RegEnumValueW(newkey, index, value, &value_len,
1672                         0, &dwType, (LPVOID)data, &data_len) == 0) {
1673         TRACE("guess %d %s is %s\n",
1674               index, debugstr_w(value), debugstr_w(data));
1675
1676         j = FALSE;
1677         for(i=0; i<value_len; i++) {
1678             Wxx = pszIn[i];
1679             Wyy = value[i];
1680             /* remember that TRUE is not-equal */
1681             j = ChrCmpIW(Wxx, Wyy);
1682             if (j) break;
1683         }
1684         if ((i == value_len) && !j) {
1685             if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1686                 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1687                 RegCloseKey(newkey);
1688                 return E_POINTER;
1689             }
1690             strcpyW(pszOut, data);
1691             strcatW(pszOut, pszIn);
1692             *pcchOut = strlenW(pszOut);
1693             TRACE("matched and set to %s\n", debugstr_w(pszOut));
1694             RegCloseKey(newkey);
1695             return S_OK;
1696         }
1697         index++;
1698     }
1699     RegCloseKey(newkey);
1700     return E_FAIL;
1701 }
1702
1703 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1704 {
1705     HKEY newkey;
1706     DWORD data_len, dwType;
1707     WCHAR data[MAX_PATH];
1708
1709     static const WCHAR prefix_keyW[] =
1710         {'S','o','f','t','w','a','r','e',
1711          '\\','M','i','c','r','o','s','o','f','t',
1712          '\\','W','i','n','d','o','w','s',
1713          '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
1714          '\\','U','R','L',
1715          '\\','D','e','f','a','u','l','t','P','r','e','f','i','x',0};
1716
1717     /* get and prepend default */
1718     RegOpenKeyExW(HKEY_LOCAL_MACHINE, prefix_keyW, 0, 1, &newkey);
1719     data_len = sizeof(data);
1720     RegQueryValueExW(newkey, NULL, 0, &dwType, (LPBYTE)data, &data_len);
1721     RegCloseKey(newkey);
1722     if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1723         *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1724         return E_POINTER;
1725     }
1726     strcpyW(pszOut, data);
1727     strcatW(pszOut, pszIn);
1728     *pcchOut = strlenW(pszOut);
1729     TRACE("used default %s\n", debugstr_w(pszOut));
1730     return S_OK;
1731 }
1732
1733 /*************************************************************************
1734  *      UrlApplySchemeW [SHLWAPI.@]
1735  *
1736  * See UrlApplySchemeA.
1737  */
1738 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1739 {
1740     PARSEDURLW in_scheme;
1741     DWORD res1;
1742     HRESULT ret;
1743
1744     TRACE("(%s, %p, %p:out size %d, 0x%08x)\n", debugstr_w(pszIn),
1745             pszOut, pcchOut, pcchOut ? *pcchOut : 0, dwFlags);
1746
1747     if (!pszIn || !pszOut || !pcchOut) return E_INVALIDARG;
1748
1749     if (dwFlags & URL_APPLY_GUESSFILE) {
1750         FIXME("(%s %p %p(%d) 0x%08x): stub URL_APPLY_GUESSFILE not implemented\n",
1751               debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1752         strcpyW(pszOut, pszIn);
1753         *pcchOut = strlenW(pszOut);
1754         return S_FALSE;
1755     }
1756
1757     in_scheme.cbSize = sizeof(in_scheme);
1758     /* See if the base has a scheme */
1759     res1 = ParseURLW(pszIn, &in_scheme);
1760     if (res1) {
1761         /* no scheme in input, need to see if we need to guess */
1762         if (dwFlags & URL_APPLY_GUESSSCHEME) {
1763             if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != E_FAIL)
1764                 return ret;
1765         }
1766     }
1767     else {
1768         /* we have a scheme, see if valid (known scheme) */
1769         if (in_scheme.nScheme) {
1770             /* have valid scheme, so just copy and exit */
1771             if (strlenW(pszIn) + 1 > *pcchOut) {
1772                 *pcchOut = strlenW(pszIn) + 1;
1773                 return E_POINTER;
1774             }
1775             strcpyW(pszOut, pszIn);
1776             *pcchOut = strlenW(pszOut);
1777             TRACE("valid scheme, returning copy\n");
1778             return S_OK;
1779         }
1780     }
1781
1782     /* If we are here, then either invalid scheme,
1783      * or no scheme and can't/failed guess.
1784      */
1785     if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1786            ((res1 != 0)) ) &&
1787          (dwFlags & URL_APPLY_DEFAULT)) {
1788         /* find and apply default scheme */
1789         return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1790     }
1791
1792     return S_FALSE;
1793 }
1794
1795 /*************************************************************************
1796  *      UrlIsA          [SHLWAPI.@]
1797  *
1798  * Determine if a Url is of a certain class.
1799  *
1800  * PARAMS
1801  *  pszUrl [I] Url to check
1802  *  Urlis  [I] URLIS_ constant from "shlwapi.h"
1803  *
1804  * RETURNS
1805  *  TRUE if pszUrl belongs to the class type in Urlis.
1806  *  FALSE Otherwise.
1807  */
1808 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1809 {
1810     PARSEDURLA base;
1811     DWORD res1;
1812     LPCSTR last;
1813
1814     TRACE("(%s %d)\n", debugstr_a(pszUrl), Urlis);
1815
1816     if(!pszUrl)
1817         return FALSE;
1818
1819     switch (Urlis) {
1820
1821     case URLIS_OPAQUE:
1822         base.cbSize = sizeof(base);
1823         res1 = ParseURLA(pszUrl, &base);
1824         if (res1) return FALSE;  /* invalid scheme */
1825         switch (base.nScheme)
1826         {
1827         case URL_SCHEME_MAILTO:
1828         case URL_SCHEME_SHELL:
1829         case URL_SCHEME_JAVASCRIPT:
1830         case URL_SCHEME_VBSCRIPT:
1831         case URL_SCHEME_ABOUT:
1832             return TRUE;
1833         }
1834         return FALSE;
1835
1836     case URLIS_FILEURL:
1837         return !StrCmpNA("file:", pszUrl, 5);
1838
1839     case URLIS_DIRECTORY:
1840         last = pszUrl + strlen(pszUrl) - 1;
1841         return (last >= pszUrl && (*last == '/' || *last == '\\' ));
1842
1843     case URLIS_URL:
1844         return PathIsURLA(pszUrl);
1845
1846     case URLIS_NOHISTORY:
1847     case URLIS_APPLIABLE:
1848     case URLIS_HASQUERY:
1849     default:
1850         FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1851     }
1852     return FALSE;
1853 }
1854
1855 /*************************************************************************
1856  *      UrlIsW          [SHLWAPI.@]
1857  *
1858  * See UrlIsA.
1859  */
1860 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1861 {
1862     static const WCHAR stemp[] = { 'f','i','l','e',':',0 };
1863     PARSEDURLW base;
1864     DWORD res1;
1865     LPCWSTR last;
1866
1867     TRACE("(%s %d)\n", debugstr_w(pszUrl), Urlis);
1868
1869     if(!pszUrl)
1870         return FALSE;
1871
1872     switch (Urlis) {
1873
1874     case URLIS_OPAQUE:
1875         base.cbSize = sizeof(base);
1876         res1 = ParseURLW(pszUrl, &base);
1877         if (res1) return FALSE;  /* invalid scheme */
1878         switch (base.nScheme)
1879         {
1880         case URL_SCHEME_MAILTO:
1881         case URL_SCHEME_SHELL:
1882         case URL_SCHEME_JAVASCRIPT:
1883         case URL_SCHEME_VBSCRIPT:
1884         case URL_SCHEME_ABOUT:
1885             return TRUE;
1886         }
1887         return FALSE;
1888
1889     case URLIS_FILEURL:
1890         return !strncmpW(stemp, pszUrl, 5);
1891
1892     case URLIS_DIRECTORY:
1893         last = pszUrl + strlenW(pszUrl) - 1;
1894         return (last >= pszUrl && (*last == '/' || *last == '\\'));
1895
1896     case URLIS_URL:
1897         return PathIsURLW(pszUrl);
1898
1899     case URLIS_NOHISTORY:
1900     case URLIS_APPLIABLE:
1901     case URLIS_HASQUERY:
1902     default:
1903         FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1904     }
1905     return FALSE;
1906 }
1907
1908 /*************************************************************************
1909  *      UrlIsNoHistoryA         [SHLWAPI.@]
1910  *
1911  * Determine if a Url should not be stored in the users history list.
1912  *
1913  * PARAMS
1914  *  pszUrl [I] Url to check
1915  *
1916  * RETURNS
1917  *  TRUE, if pszUrl should be excluded from the history list,
1918  *  FALSE otherwise.
1919  */
1920 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1921 {
1922     return UrlIsA(pszUrl, URLIS_NOHISTORY);
1923 }
1924
1925 /*************************************************************************
1926  *      UrlIsNoHistoryW         [SHLWAPI.@]
1927  *
1928  * See UrlIsNoHistoryA.
1929  */
1930 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1931 {
1932     return UrlIsW(pszUrl, URLIS_NOHISTORY);
1933 }
1934
1935 /*************************************************************************
1936  *      UrlIsOpaqueA    [SHLWAPI.@]
1937  *
1938  * Determine if a Url is opaque.
1939  *
1940  * PARAMS
1941  *  pszUrl [I] Url to check
1942  *
1943  * RETURNS
1944  *  TRUE if pszUrl is opaque,
1945  *  FALSE Otherwise.
1946  *
1947  * NOTES
1948  *  An opaque Url is one that does not start with "<protocol>://".
1949  */
1950 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1951 {
1952     return UrlIsA(pszUrl, URLIS_OPAQUE);
1953 }
1954
1955 /*************************************************************************
1956  *      UrlIsOpaqueW    [SHLWAPI.@]
1957  *
1958  * See UrlIsOpaqueA.
1959  */
1960 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1961 {
1962     return UrlIsW(pszUrl, URLIS_OPAQUE);
1963 }
1964
1965 /*************************************************************************
1966  *  Scans for characters of type "type" and when not matching found,
1967  *  returns pointer to it and length in size.
1968  *
1969  * Characters tested based on RFC 1738
1970  */
1971 static LPCWSTR  URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1972 {
1973     static DWORD alwayszero = 0;
1974     BOOL cont = TRUE;
1975
1976     *size = 0;
1977
1978     switch(type){
1979
1980     case SCHEME:
1981         while (cont) {
1982             if ( (islowerW(*start) && isalphaW(*start)) ||
1983                  isdigitW(*start) ||
1984                  (*start == '+') ||
1985                  (*start == '-') ||
1986                  (*start == '.')) {
1987                 start++;
1988                 (*size)++;
1989             }
1990             else
1991                 cont = FALSE;
1992         }
1993
1994         if(*start != ':')
1995             *size = 0;
1996
1997         break;
1998
1999     case USERPASS:
2000         while (cont) {
2001             if ( isalphaW(*start) ||
2002                  isdigitW(*start) ||
2003                  /* user/password only characters */
2004                  (*start == ';') ||
2005                  (*start == '?') ||
2006                  (*start == '&') ||
2007                  (*start == '=') ||
2008                  /* *extra* characters */
2009                  (*start == '!') ||
2010                  (*start == '*') ||
2011                  (*start == '\'') ||
2012                  (*start == '(') ||
2013                  (*start == ')') ||
2014                  (*start == ',') ||
2015                  /* *safe* characters */
2016                  (*start == '$') ||
2017                  (*start == '_') ||
2018                  (*start == '+') ||
2019                  (*start == '-') ||
2020                  (*start == '.') ||
2021                  (*start == ' ')) {
2022                 start++;
2023                 (*size)++;
2024             } else if (*start == '%') {
2025                 if (isxdigitW(*(start+1)) &&
2026                     isxdigitW(*(start+2))) {
2027                     start += 3;
2028                     *size += 3;
2029                 } else
2030                     cont = FALSE;
2031             } else
2032                 cont = FALSE;
2033         }
2034         break;
2035
2036     case PORT:
2037         while (cont) {
2038             if (isdigitW(*start)) {
2039                 start++;
2040                 (*size)++;
2041             }
2042             else
2043                 cont = FALSE;
2044         }
2045         break;
2046
2047     case HOST:
2048         while (cont) {
2049             if (isalnumW(*start) ||
2050                 (*start == '-') ||
2051                 (*start == '.') ||
2052                 (*start == ' ') ||
2053                 (*start == '*') ) {
2054                 start++;
2055                 (*size)++;
2056             }
2057             else
2058                 cont = FALSE;
2059         }
2060         break;
2061     default:
2062         FIXME("unknown type %d\n", type);
2063         return (LPWSTR)&alwayszero;
2064     }
2065     /* TRACE("scanned %d characters next char %p<%c>\n",
2066      *size, start, *start); */
2067     return start;
2068 }
2069
2070 /*************************************************************************
2071  *  Attempt to parse URL into pieces.
2072  */
2073 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
2074 {
2075     LPCWSTR work;
2076
2077     memset(pl, 0, sizeof(WINE_PARSE_URL));
2078     pl->pScheme = pszUrl;
2079     work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
2080     if (!*work || (*work != ':')) goto ErrorExit;
2081     work++;
2082     if ((*work != '/') || (*(work+1) != '/')) goto SuccessExit;
2083     pl->pUserName = work + 2;
2084     work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
2085     if (*work == ':' ) {
2086         /* parse password */
2087         work++;
2088         pl->pPassword = work;
2089         work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
2090         if (*work != '@') {
2091             /* what we just parsed must be the hostname and port
2092              * so reset pointers and clear then let it parse */
2093             pl->szUserName = pl->szPassword = 0;
2094             work = pl->pUserName - 1;
2095             pl->pUserName = pl->pPassword = 0;
2096         }
2097     } else if (*work == '@') {
2098         /* no password */
2099         pl->szPassword = 0;
2100         pl->pPassword = 0;
2101     } else if (!*work || (*work == '/') || (*work == '.')) {
2102         /* what was parsed was hostname, so reset pointers and let it parse */
2103         pl->szUserName = pl->szPassword = 0;
2104         work = pl->pUserName - 1;
2105         pl->pUserName = pl->pPassword = 0;
2106     } else goto ErrorExit;
2107
2108     /* now start parsing hostname or hostnumber */
2109     work++;
2110     pl->pHostName = work;
2111     work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
2112     if (*work == ':') {
2113         /* parse port */
2114         work++;
2115         pl->pPort = work;
2116         work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
2117     }
2118     if (*work == '/') {
2119         /* see if query string */
2120         pl->pQuery = strchrW(work, '?');
2121         if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
2122     }
2123   SuccessExit:
2124     TRACE("parse successful: scheme=%p(%d), user=%p(%d), pass=%p(%d), host=%p(%d), port=%p(%d), query=%p(%d)\n",
2125           pl->pScheme, pl->szScheme,
2126           pl->pUserName, pl->szUserName,
2127           pl->pPassword, pl->szPassword,
2128           pl->pHostName, pl->szHostName,
2129           pl->pPort, pl->szPort,
2130           pl->pQuery, pl->szQuery);
2131     return S_OK;
2132   ErrorExit:
2133     FIXME("failed to parse %s\n", debugstr_w(pszUrl));
2134     return E_INVALIDARG;
2135 }
2136
2137 /*************************************************************************
2138  *      UrlGetPartA     [SHLWAPI.@]
2139  *
2140  * Retrieve part of a Url.
2141  *
2142  * PARAMS
2143  *  pszIn   [I]   Url to parse
2144  *  pszOut  [O]   Destination for part of pszIn requested
2145  *  pcchOut [I]   Size of pszOut
2146  *          [O]   length of pszOut string EXCLUDING '\0' if S_OK, otherwise
2147  *                needed size of pszOut INCLUDING '\0'.
2148  *  dwPart  [I]   URL_PART_ enum from "shlwapi.h"
2149  *  dwFlags [I]   URL_ flags from "shlwapi.h"
2150  *
2151  * RETURNS
2152  *  Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
2153  *  Failure: An HRESULT error code describing the error.
2154  */
2155 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
2156                            DWORD dwPart, DWORD dwFlags)
2157 {
2158     LPWSTR in, out;
2159     DWORD ret, len, len2;
2160
2161     if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2162         return E_INVALIDARG;
2163
2164     in = HeapAlloc(GetProcessHeap(), 0,
2165                               (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
2166     out = in + INTERNET_MAX_URL_LENGTH;
2167
2168     MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
2169
2170     len = INTERNET_MAX_URL_LENGTH;
2171     ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
2172
2173     if (FAILED(ret)) {
2174         HeapFree(GetProcessHeap(), 0, in);
2175         return ret;
2176     }
2177
2178     len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
2179     if (len2 > *pcchOut) {
2180         *pcchOut = len2+1;
2181         HeapFree(GetProcessHeap(), 0, in);
2182         return E_POINTER;
2183     }
2184     len2 = WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2185     *pcchOut = len2-1;
2186     HeapFree(GetProcessHeap(), 0, in);
2187     return ret;
2188 }
2189
2190 /*************************************************************************
2191  *      UrlGetPartW     [SHLWAPI.@]
2192  *
2193  * See UrlGetPartA.
2194  */
2195 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2196                            DWORD dwPart, DWORD dwFlags)
2197 {
2198     WINE_PARSE_URL pl;
2199     HRESULT ret;
2200     DWORD scheme, size, schsize;
2201     LPCWSTR addr, schaddr;
2202
2203     TRACE("(%s %p %p(%d) %08x %08x)\n",
2204           debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2205
2206     if(!pszIn || !pszOut || !pcchOut || *pcchOut <= 0)
2207         return E_INVALIDARG;
2208
2209     *pszOut = '\0';
2210
2211     addr = strchrW(pszIn, ':');
2212     if(!addr)
2213         scheme = URL_SCHEME_UNKNOWN;
2214     else
2215         scheme = get_scheme_code(pszIn, addr-pszIn);
2216
2217     ret = URL_ParseUrl(pszIn, &pl);
2218
2219         switch (dwPart) {
2220         case URL_PART_SCHEME:
2221             if (!pl.szScheme) {
2222                 *pcchOut = 0;
2223                 return S_FALSE;
2224             }
2225             addr = pl.pScheme;
2226             size = pl.szScheme;
2227             break;
2228
2229         case URL_PART_HOSTNAME:
2230             switch(scheme) {
2231             case URL_SCHEME_FTP:
2232             case URL_SCHEME_HTTP:
2233             case URL_SCHEME_GOPHER:
2234             case URL_SCHEME_TELNET:
2235             case URL_SCHEME_FILE:
2236             case URL_SCHEME_HTTPS:
2237                 break;
2238             default:
2239                 *pcchOut = 0;
2240                 return E_FAIL;
2241             }
2242
2243             if(scheme==URL_SCHEME_FILE && (!pl.szHostName ||
2244                         (pl.szHostName==1 && *(pl.pHostName+1)==':'))) {
2245                 *pcchOut = 0;
2246                 return S_FALSE;
2247             }
2248
2249             if (!pl.szHostName) {
2250                 *pcchOut = 0;
2251                 return S_FALSE;
2252             }
2253             addr = pl.pHostName;
2254             size = pl.szHostName;
2255             break;
2256
2257         case URL_PART_USERNAME:
2258             if (!pl.szUserName) {
2259                 *pcchOut = 0;
2260                 return S_FALSE;
2261             }
2262             addr = pl.pUserName;
2263             size = pl.szUserName;
2264             break;
2265
2266         case URL_PART_PASSWORD:
2267             if (!pl.szPassword) {
2268                 *pcchOut = 0;
2269                 return S_FALSE;
2270             }
2271             addr = pl.pPassword;
2272             size = pl.szPassword;
2273             break;
2274
2275         case URL_PART_PORT:
2276             if (!pl.szPort) {
2277                 *pcchOut = 0;
2278                 return S_FALSE;
2279             }
2280             addr = pl.pPort;
2281             size = pl.szPort;
2282             break;
2283
2284         case URL_PART_QUERY:
2285             if (!pl.szQuery) {
2286                 *pcchOut = 0;
2287                 return S_FALSE;
2288             }
2289             addr = pl.pQuery;
2290             size = pl.szQuery;
2291             break;
2292
2293         default:
2294             *pcchOut = 0;
2295             return E_INVALIDARG;
2296         }
2297
2298         if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2299             if(!pl.pScheme || !pl.szScheme) {
2300                 *pcchOut = 0;
2301                 return E_FAIL;
2302             }
2303             schaddr = pl.pScheme;
2304             schsize = pl.szScheme;
2305             if (*pcchOut < schsize + size + 2) {
2306                 *pcchOut = schsize + size + 2;
2307                 return E_POINTER;
2308             }
2309             memcpy(pszOut, schaddr, schsize*sizeof(WCHAR));
2310             pszOut[schsize] = ':';
2311             memcpy(pszOut+schsize+1, addr, size*sizeof(WCHAR));
2312             pszOut[schsize+1+size] = 0;
2313             *pcchOut = schsize + 1 + size;
2314         }
2315         else {
2316             if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2317             memcpy(pszOut, addr, size*sizeof(WCHAR));
2318             pszOut[size] = 0;
2319             *pcchOut = size;
2320         }
2321         TRACE("len=%d %s\n", *pcchOut, debugstr_w(pszOut));
2322
2323     return ret;
2324 }
2325
2326 /*************************************************************************
2327  * PathIsURLA   [SHLWAPI.@]
2328  *
2329  * Check if the given path is a Url.
2330  *
2331  * PARAMS
2332  *  lpszPath [I] Path to check.
2333  *
2334  * RETURNS
2335  *  TRUE  if lpszPath is a Url.
2336  *  FALSE if lpszPath is NULL or not a Url.
2337  */
2338 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2339 {
2340     PARSEDURLA base;
2341     HRESULT hres;
2342
2343     TRACE("%s\n", debugstr_a(lpstrPath));
2344
2345     if (!lpstrPath || !*lpstrPath) return FALSE;
2346
2347     /* get protocol        */
2348     base.cbSize = sizeof(base);
2349     hres = ParseURLA(lpstrPath, &base);
2350     return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2351 }
2352
2353 /*************************************************************************
2354  * PathIsURLW   [SHLWAPI.@]
2355  *
2356  * See PathIsURLA.
2357  */
2358 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2359 {
2360     PARSEDURLW base;
2361     HRESULT hres;
2362
2363     TRACE("%s\n", debugstr_w(lpstrPath));
2364
2365     if (!lpstrPath || !*lpstrPath) return FALSE;
2366
2367     /* get protocol        */
2368     base.cbSize = sizeof(base);
2369     hres = ParseURLW(lpstrPath, &base);
2370     return hres == S_OK && (base.nScheme != URL_SCHEME_INVALID);
2371 }
2372
2373 /*************************************************************************
2374  *      UrlCreateFromPathA      [SHLWAPI.@]
2375  * 
2376  * See UrlCreateFromPathW
2377  */
2378 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2379 {
2380     WCHAR bufW[INTERNET_MAX_URL_LENGTH];
2381     WCHAR *urlW = bufW;
2382     UNICODE_STRING pathW;
2383     HRESULT ret;
2384     DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA;
2385
2386     if(!RtlCreateUnicodeStringFromAsciiz(&pathW, pszPath))
2387         return E_INVALIDARG;
2388     if((ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved)) == E_POINTER) {
2389         urlW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR));
2390         ret = UrlCreateFromPathW(pathW.Buffer, urlW, &lenW, dwReserved);
2391     }
2392     if(ret == S_OK || ret == S_FALSE) {
2393         RtlUnicodeToMultiByteSize(&lenA, urlW, lenW * sizeof(WCHAR));
2394         if(*pcchUrl > lenA) {
2395             RtlUnicodeToMultiByteN(pszUrl, *pcchUrl - 1, &lenA, urlW, lenW * sizeof(WCHAR));
2396             pszUrl[lenA] = 0;
2397             *pcchUrl = lenA;
2398         } else {
2399             *pcchUrl = lenA + 1;
2400             ret = E_POINTER;
2401         }
2402     }
2403     if(urlW != bufW) HeapFree(GetProcessHeap(), 0, urlW);
2404     RtlFreeUnicodeString(&pathW);
2405     return ret;
2406 }
2407
2408 /*************************************************************************
2409  *      UrlCreateFromPathW      [SHLWAPI.@]
2410  *
2411  * Create a Url from a file path.
2412  *
2413  * PARAMS
2414  *  pszPath [I]    Path to convert
2415  *  pszUrl  [O]    Destination for the converted Url
2416  *  pcchUrl [I/O]  Length of pszUrl
2417  *  dwReserved [I] Reserved, must be 0
2418  *
2419  * RETURNS
2420  *  Success: S_OK pszUrl contains the converted path, S_FALSE if the path is already a Url
2421  *  Failure: An HRESULT error code.
2422  */
2423 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2424 {
2425     DWORD needed;
2426     HRESULT ret;
2427     WCHAR *pszNewUrl;
2428     WCHAR file_colonW[] = {'f','i','l','e',':',0};
2429     WCHAR three_slashesW[] = {'/','/','/',0};
2430     PARSEDURLW parsed_url;
2431
2432     TRACE("(%s, %p, %p, 0x%08x)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2433
2434     /* Validate arguments */
2435     if (dwReserved != 0)
2436         return E_INVALIDARG;
2437     if (!pszUrl || !pcchUrl)
2438         return E_INVALIDARG;
2439
2440
2441     parsed_url.cbSize = sizeof(parsed_url);
2442     if(ParseURLW(pszPath, &parsed_url) == S_OK) {
2443         if(parsed_url.nScheme != URL_SCHEME_INVALID && parsed_url.cchProtocol > 1) {
2444             needed = strlenW(pszPath);
2445             if (needed >= *pcchUrl) {
2446                 *pcchUrl = needed + 1;
2447                 return E_POINTER;
2448             } else {
2449                 *pcchUrl = needed;
2450                 strcpyW(pszUrl, pszPath);
2451                 return S_FALSE;
2452             }
2453         }
2454     }
2455
2456     pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszPath) + 9) * sizeof(WCHAR)); /* "file:///" + pszPath_len + 1 */
2457     strcpyW(pszNewUrl, file_colonW);
2458     if(isalphaW(pszPath[0]) && pszPath[1] == ':')
2459         strcatW(pszNewUrl, three_slashesW);
2460     strcatW(pszNewUrl, pszPath);
2461     ret = UrlEscapeW(pszNewUrl, pszUrl, pcchUrl, URL_ESCAPE_PERCENT);
2462
2463     HeapFree(GetProcessHeap(), 0, pszNewUrl);
2464     return ret;
2465 }
2466
2467 /*************************************************************************
2468  *      SHAutoComplete          [SHLWAPI.@]
2469  *
2470  * Enable auto-completion for an edit control.
2471  *
2472  * PARAMS
2473  *  hwndEdit [I] Handle of control to enable auto-completion for
2474  *  dwFlags  [I] SHACF_ flags from "shlwapi.h"
2475  *
2476  * RETURNS
2477  *  Success: S_OK. Auto-completion is enabled for the control.
2478  *  Failure: An HRESULT error code indicating the error.
2479  */
2480 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2481 {
2482   FIXME("stub\n");
2483   return S_FALSE;
2484 }
2485
2486 /*************************************************************************
2487  *  MLBuildResURLA      [SHLWAPI.405]
2488  *
2489  * Create a Url pointing to a resource in a module.
2490  *
2491  * PARAMS
2492  *  lpszLibName [I] Name of the module containing the resource
2493  *  hMod        [I] Callers module handle
2494  *  dwFlags     [I] Undocumented flags for loading the module
2495  *  lpszRes     [I] Resource name
2496  *  lpszDest    [O] Destination for resulting Url
2497  *  dwDestLen   [I] Length of lpszDest
2498  *
2499  * RETURNS
2500  *  Success: S_OK. lpszDest contains the resource Url.
2501  *  Failure: E_INVALIDARG, if any argument is invalid, or
2502  *           E_FAIL if dwDestLen is too small.
2503  */
2504 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2505                               LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2506 {
2507   WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2508   HRESULT hRet;
2509
2510   if (lpszLibName)
2511     MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2512
2513   if (lpszRes)
2514     MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2515
2516   if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2517     dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2518
2519   hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2520                         lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2521   if (SUCCEEDED(hRet) && lpszDest)
2522     WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2523
2524   return hRet;
2525 }
2526
2527 /*************************************************************************
2528  *  MLBuildResURLA      [SHLWAPI.406]
2529  *
2530  * See MLBuildResURLA.
2531  */
2532 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2533                               LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2534 {
2535   static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2536 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2537   HRESULT hRet = E_FAIL;
2538
2539   TRACE("(%s,%p,0x%08x,%s,%p,%d)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2540         debugstr_w(lpszRes), lpszDest, dwDestLen);
2541
2542   if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2543       !lpszDest || (dwFlags && dwFlags != 2))
2544     return E_INVALIDARG;
2545
2546   if (dwDestLen >= szResLen + 1)
2547   {
2548     dwDestLen -= (szResLen + 1);
2549     memcpy(lpszDest, szRes, sizeof(szRes));
2550
2551     hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2552
2553     if (hMod)
2554     {
2555       WCHAR szBuff[MAX_PATH];
2556       DWORD len;
2557
2558       len = GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR));
2559       if (len && len < sizeof(szBuff)/sizeof(WCHAR))
2560       {
2561         DWORD dwPathLen = strlenW(szBuff) + 1;
2562
2563         if (dwDestLen >= dwPathLen)
2564         {
2565           DWORD dwResLen;
2566
2567           dwDestLen -= dwPathLen;
2568           memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2569
2570           dwResLen = strlenW(lpszRes) + 1;
2571           if (dwDestLen >= dwResLen + 1)
2572           {
2573             lpszDest[szResLen + dwPathLen-1] = '/';
2574             memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2575             hRet = S_OK;
2576           }
2577         }
2578       }
2579       MLFreeLibrary(hMod);
2580     }
2581   }
2582   return hRet;
2583 }
2584
2585 /***********************************************************************
2586  *             UrlFixupW [SHLWAPI.462]
2587  *
2588  * Checks the scheme part of a URL and attempts to correct misspellings.
2589  *
2590  * PARAMS
2591  *  lpszUrl           [I] Pointer to the URL to be corrected
2592  *  lpszTranslatedUrl [O] Pointer to a buffer to store corrected URL
2593  *  dwMaxChars        [I] Maximum size of corrected URL
2594  *
2595  * RETURNS
2596  *  success: S_OK if URL corrected or already correct
2597  *  failure: S_FALSE if unable to correct / COM error code if other error
2598  *
2599  */
2600 HRESULT WINAPI UrlFixupW(LPCWSTR url, LPWSTR translatedUrl, DWORD maxChars)
2601 {
2602     DWORD srcLen;
2603
2604     FIXME("(%s,%p,%d) STUB\n", debugstr_w(url), translatedUrl, maxChars);
2605
2606     if (!url)
2607         return E_FAIL;
2608
2609     srcLen = lstrlenW(url) + 1;
2610
2611     /* For now just copy the URL directly */
2612     lstrcpynW(translatedUrl, url, (maxChars < srcLen) ? maxChars : srcLen);
2613
2614     return S_OK;
2615 }