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