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