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