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