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