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