Add a couple of missing spec files.
[wine.git] / dlls / shlwapi / url.c
blobe078f1c30abf9e92315d300a95a57625ca1b4350
1 /*
2 * Url functions
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "config.h"
22 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wine/unicode.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #define NO_SHLWAPI_STREAM
34 #include "shlwapi.h"
35 #include "wine/debug.h"
37 HMODULE WINAPI MLLoadLibraryW(LPCWSTR,HMODULE,DWORD);
38 BOOL WINAPI MLFreeLibrary(HMODULE);
39 HRESULT WINAPI MLBuildResURLW(LPCWSTR,HMODULE,DWORD,LPCWSTR,LPWSTR,DWORD);
41 WINE_DEFAULT_DEBUG_CHANNEL(shell);
43 /* The following schemes were identified in the native version of
44 * SHLWAPI.DLL version 5.50
46 typedef enum {
47 URL_SCHEME_INVALID = -1,
48 URL_SCHEME_UNKNOWN = 0,
49 URL_SCHEME_FTP,
50 URL_SCHEME_HTTP,
51 URL_SCHEME_GOPHER,
52 URL_SCHEME_MAILTO,
53 URL_SCHEME_NEWS,
54 URL_SCHEME_NNTP,
55 URL_SCHEME_TELNET,
56 URL_SCHEME_WAIS,
57 URL_SCHEME_FILE,
58 URL_SCHEME_MK,
59 URL_SCHEME_HTTPS,
60 URL_SCHEME_SHELL,
61 URL_SCHEME_SNEWS,
62 URL_SCHEME_LOCAL,
63 URL_SCHEME_JAVASCRIPT,
64 URL_SCHEME_VBSCRIPT,
65 URL_SCHEME_ABOUT,
66 URL_SCHEME_RES,
67 URL_SCHEME_MAXVALUE
68 } URL_SCHEME;
70 typedef struct {
71 URL_SCHEME scheme_number;
72 LPCSTR scheme_name;
73 } SHL_2_inet_scheme;
75 static const SHL_2_inet_scheme shlwapi_schemes[] = {
76 {URL_SCHEME_FTP, "ftp"},
77 {URL_SCHEME_HTTP, "http"},
78 {URL_SCHEME_GOPHER, "gopher"},
79 {URL_SCHEME_MAILTO, "mailto"},
80 {URL_SCHEME_NEWS, "news"},
81 {URL_SCHEME_NNTP, "nntp"},
82 {URL_SCHEME_TELNET, "telnet"},
83 {URL_SCHEME_WAIS, "wais"},
84 {URL_SCHEME_FILE, "file"},
85 {URL_SCHEME_MK, "mk"},
86 {URL_SCHEME_HTTPS, "https"},
87 {URL_SCHEME_SHELL, "shell"},
88 {URL_SCHEME_SNEWS, "snews"},
89 {URL_SCHEME_LOCAL, "local"},
90 {URL_SCHEME_JAVASCRIPT, "javascript"},
91 {URL_SCHEME_VBSCRIPT, "vbscript"},
92 {URL_SCHEME_ABOUT, "about"},
93 {URL_SCHEME_RES, "res"},
94 {0, 0}
97 typedef struct {
98 LPCWSTR pScheme; /* [out] start of scheme */
99 DWORD szScheme; /* [out] size of scheme (until colon) */
100 LPCWSTR pUserName; /* [out] start of Username */
101 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
102 LPCWSTR pPassword; /* [out] start of Password */
103 DWORD szPassword; /* [out] size of Password (until "@") */
104 LPCWSTR pHostName; /* [out] start of Hostname */
105 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
106 LPCWSTR pPort; /* [out] start of Port */
107 DWORD szPort; /* [out] size of Port (until "/" or eos) */
108 LPCWSTR pQuery; /* [out] start of Query */
109 DWORD szQuery; /* [out] size of Query (until eos) */
110 } WINE_PARSE_URL;
112 typedef enum {
113 SCHEME,
114 HOST,
115 PORT,
116 USERPASS,
117 } WINE_URL_SCAN_TYPE;
119 typedef struct {
120 INT size; /* [in] (always 0x18) */
121 LPCSTR ap1; /* [out] start of scheme */
122 INT sizep1; /* [out] size of scheme (until colon) */
123 LPCSTR ap2; /* [out] pointer following first colon */
124 INT sizep2; /* [out] size of remainder */
125 INT fcncde; /* [out] function match of p1 (0 if unknown) */
126 } UNKNOWN_SHLWAPI_1;
128 typedef struct {
129 INT size; /* [in] (always 0x18) */
130 LPCWSTR ap1; /* [out] start of scheme */
131 INT sizep1; /* [out] size of scheme (until colon) */
132 LPCWSTR ap2; /* [out] pointer following first colon */
133 INT sizep2; /* [out] size of remainder */
134 INT fcncde; /* [out] function match of p1 (0 if unknown) */
135 } UNKNOWN_SHLWAPI_2;
137 static const CHAR hexDigits[] = "0123456789ABCDEF";
139 static const WCHAR fileW[] = {'f','i','l','e','\0'};
141 static const unsigned char HashDataLookup[256] = {
142 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
143 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
144 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
145 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
146 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
147 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
148 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
149 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
150 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
151 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
152 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
153 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
154 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
155 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
156 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
157 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
158 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
159 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
160 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
161 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
163 static inline BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
166 if (isalnum(ch))
167 return FALSE;
169 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
170 if(ch == ' ')
171 return TRUE;
172 else
173 return FALSE;
176 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
177 return TRUE;
179 if (ch <= 31 || ch >= 127)
180 return TRUE;
182 else {
183 switch (ch) {
184 case ' ':
185 case '<':
186 case '>':
187 case '\"':
188 case '{':
189 case '}':
190 case '|':
191 /* case '\\': */
192 case '^':
193 case ']':
194 case '[':
195 case '`':
196 case '&':
197 return TRUE;
199 case '/':
200 case '?':
201 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
202 default:
203 return FALSE;
208 static inline BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
211 if (isalnumW(ch))
212 return FALSE;
214 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
215 if(ch == L' ')
216 return TRUE;
217 else
218 return FALSE;
221 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
222 return TRUE;
224 if (ch <= 31 || ch >= 127)
225 return TRUE;
227 else {
228 switch (ch) {
229 case L' ':
230 case L'<':
231 case L'>':
232 case L'\"':
233 case L'{':
234 case L'}':
235 case L'|':
236 case L'\\':
237 case L'^':
238 case L']':
239 case L'[':
240 case L'`':
241 case L'&':
242 return TRUE;
244 case L'/':
245 case L'?':
246 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
247 default:
248 return FALSE;
253 static BOOL URL_JustLocation(LPCWSTR str)
255 while(*str && (*str == L'/')) str++;
256 if (*str) {
257 while (*str && ((*str == L'-') ||
258 (*str == L'.') ||
259 isalnumW(*str))) str++;
260 if (*str == L'/') return FALSE;
262 return TRUE;
266 /*************************************************************************
267 * @ [SHLWAPI.1]
269 * Parse a Url into its constituent parts.
271 * PARAMS
272 * x [I] Url to parse
273 * y [O] Undocumented structure holding the parsed information
275 * RETURNS
276 * Success: S_OK. y contains the parsed Url details.
277 * Failure: An HRESULT error code.
279 DWORD WINAPI ParseURLA(LPCSTR x, UNKNOWN_SHLWAPI_1 *y)
281 DWORD cnt;
282 const SHL_2_inet_scheme *inet_pro;
284 y->fcncde = URL_SCHEME_INVALID;
285 if (y->size != 0x18) return E_INVALIDARG;
286 /* FIXME: leading white space generates error of 0x80041001 which
287 * is undefined
289 if (*x <= ' ') return 0x80041001;
290 cnt = 0;
291 y->sizep1 = 0;
292 y->ap1 = x;
293 while (*x) {
294 if (*x == ':') {
295 y->sizep1 = cnt;
296 cnt = -1;
297 y->ap2 = x+1;
298 break;
300 x++;
301 cnt++;
304 /* check for no scheme in string start */
305 /* (apparently schemes *must* be larger than a single character) */
306 if ((*x == '\0') || (y->sizep1 <= 1)) {
307 y->ap1 = 0;
308 return 0x80041001;
311 /* found scheme, set length of remainder */
312 y->sizep2 = lstrlenA(y->ap2);
314 /* see if known scheme and return indicator number */
315 y->fcncde = URL_SCHEME_UNKNOWN;
316 inet_pro = shlwapi_schemes;
317 while (inet_pro->scheme_name) {
318 if (!strncasecmp(inet_pro->scheme_name, y->ap1,
319 min(y->sizep1, lstrlenA(inet_pro->scheme_name)))) {
320 y->fcncde = inet_pro->scheme_number;
321 break;
323 inet_pro++;
325 return S_OK;
328 /*************************************************************************
329 * @ [SHLWAPI.2]
331 * Unicode version of ParseURLA.
333 DWORD WINAPI ParseURLW(LPCWSTR x, UNKNOWN_SHLWAPI_2 *y)
335 DWORD cnt;
336 const SHL_2_inet_scheme *inet_pro;
337 LPSTR cmpstr;
338 INT len;
340 y->fcncde = URL_SCHEME_INVALID;
341 if (y->size != 0x18) return E_INVALIDARG;
342 /* FIXME: leading white space generates error of 0x80041001 which
343 * is undefined
345 if (*x <= L' ') return 0x80041001;
346 cnt = 0;
347 y->sizep1 = 0;
348 y->ap1 = x;
349 while (*x) {
350 if (*x == L':') {
351 y->sizep1 = cnt;
352 cnt = -1;
353 y->ap2 = x+1;
354 break;
356 x++;
357 cnt++;
360 /* check for no scheme in string start */
361 /* (apparently schemes *must* be larger than a single character) */
362 if ((*x == L'\0') || (y->sizep1 <= 1)) {
363 y->ap1 = 0;
364 return 0x80041001;
367 /* found scheme, set length of remainder */
368 y->sizep2 = lstrlenW(y->ap2);
370 /* see if known scheme and return indicator number */
371 len = WideCharToMultiByte(0, 0, y->ap1, y->sizep1, 0, 0, 0, 0);
372 cmpstr = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len+1);
373 WideCharToMultiByte(0, 0, y->ap1, y->sizep1, cmpstr, len+1, 0, 0);
374 y->fcncde = URL_SCHEME_UNKNOWN;
375 inet_pro = shlwapi_schemes;
376 while (inet_pro->scheme_name) {
377 if (!strncasecmp(inet_pro->scheme_name, cmpstr,
378 min(len, lstrlenA(inet_pro->scheme_name)))) {
379 y->fcncde = inet_pro->scheme_number;
380 break;
382 inet_pro++;
384 HeapFree(GetProcessHeap(), 0, cmpstr);
385 return S_OK;
388 /*************************************************************************
389 * UrlCanonicalizeA [SHLWAPI.@]
391 * Canonicalize a Url.
393 * PARAMS
394 * pszUrl [I] Url to cCanonicalize
395 * pszCanonicalized [O] Destination for converted Url.
396 * pcchCanonicalized [I/O] Length of pszUrl, destination for length of pszCanonicalized
397 * dwFlags [I] Flags controlling the conversion.
399 * RETURNS
400 * Success: S_OK. The pszCanonicalized contains the converted Url.
401 * Failure: E_POINTER, if *pcchCanonicalized is too small.
403 * MSDN incorrectly describes the flags for this function. They should be:
404 *| URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
405 *| URL_ESCAPE_SPACES_ONLY 0x04000000
406 *| URL_ESCAPE_PERCENT 0x00001000
407 *| URL_ESCAPE_UNSAFE 0x10000000
408 *| URL_UNESCAPE 0x10000000
409 *| URL_DONT_SIMPLIFY 0x08000000
410 *| URL_ESCAPE_SEGMENT_ONLY 0x00002000
412 HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
413 LPDWORD pcchCanonicalized, DWORD dwFlags)
415 LPWSTR base, canonical;
416 DWORD ret, len, len2;
418 TRACE("(%s %p %p 0x%08lx) using W version\n",
419 debugstr_a(pszUrl), pszCanonicalized,
420 pcchCanonicalized, dwFlags);
422 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
423 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
424 canonical = base + INTERNET_MAX_URL_LENGTH;
426 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
427 len = INTERNET_MAX_URL_LENGTH;
429 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
430 if (ret != S_OK) {
431 HeapFree(GetProcessHeap(), 0, base);
432 return ret;
435 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
436 if (len2 > *pcchCanonicalized) {
437 *pcchCanonicalized = len;
438 HeapFree(GetProcessHeap(), 0, base);
439 return E_POINTER;
441 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
442 *pcchCanonicalized, 0, 0);
443 *pcchCanonicalized = len2;
444 HeapFree(GetProcessHeap(), 0, base);
445 return S_OK;
448 /*************************************************************************
449 * UrlCanonicalizeW [SHLWAPI.@]
451 * See UrlCanonicalizeA.
453 HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
454 LPDWORD pcchCanonicalized, DWORD dwFlags)
456 HRESULT hr = S_OK;
457 DWORD EscapeFlags;
458 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
459 INT nLen, nByteLen, state;
461 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
462 pcchCanonicalized, dwFlags);
464 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
465 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
467 if (dwFlags & URL_DONT_SIMPLIFY)
468 memcpy(lpszUrlCpy, pszUrl, nByteLen);
469 else {
472 * state =
473 * 0 initial 1,3
474 * 1 have 2[+] alnum 2,3
475 * 2 have scheme (found :) 4,6,3
476 * 3 failed (no location)
477 * 4 have // 5,3
478 * 5 have 1[+] alnum 6,3
479 * 6 have location (found /) save root location
482 wk1 = (LPWSTR)pszUrl;
483 wk2 = lpszUrlCpy;
484 state = 0;
485 while (*wk1) {
486 switch (state) {
487 case 0:
488 if (!isalnumW(*wk1)) {state = 3; break;}
489 *wk2++ = *wk1++;
490 if (!isalnumW(*wk1)) {state = 3; break;}
491 *wk2++ = *wk1++;
492 state = 1;
493 break;
494 case 1:
495 *wk2++ = *wk1;
496 if (*wk1++ == L':') state = 2;
497 break;
498 case 2:
499 if (*wk1 != L'/') {state = 3; break;}
500 *wk2++ = *wk1++;
501 if (*wk1 != L'/') {state = 6; break;}
502 *wk2++ = *wk1++;
503 state = 4;
504 break;
505 case 3:
506 strcpyW(wk2, wk1);
507 wk1 += strlenW(wk1);
508 wk2 += strlenW(wk2);
509 break;
510 case 4:
511 if (!isalnumW(*wk1) && (*wk1 != L'-')) {state = 3; break;}
512 while(isalnumW(*wk1) || (*wk1 == L'-')) *wk2++ = *wk1++;
513 state = 5;
514 break;
515 case 5:
516 if (*wk1 != L'/') {state = 3; break;}
517 *wk2++ = *wk1++;
518 state = 6;
519 break;
520 case 6:
521 /* Now at root location, cannot back up any more. */
522 /* "root" will point at the '/' */
523 root = wk2-1;
524 while (*wk1) {
525 TRACE("wk1=%c\n", (CHAR)*wk1);
526 mp = strchrW(wk1, L'/');
527 if (!mp) {
528 strcpyW(wk2, wk1);
529 wk1 += strlenW(wk1);
530 wk2 += strlenW(wk2);
531 continue;
533 nLen = mp - wk1 + 1;
534 strncpyW(wk2, wk1, nLen);
535 wk2 += nLen;
536 wk1 += nLen;
537 if (*wk1 == L'.') {
538 TRACE("found '/.'\n");
539 if (*(wk1+1) == L'/') {
540 /* case of /./ -> skip the ./ */
541 wk1 += 2;
543 else if (*(wk1+1) == L'.') {
544 /* found /.. look for next / */
545 TRACE("found '/..'\n");
546 if (*(wk1+2) == L'/') {
547 /* case /../ -> need to backup wk2 */
548 TRACE("found '/../'\n");
549 *(wk2-1) = L'\0'; /* set end of string */
550 mp = strrchrW(root, L'/');
551 if (mp && (mp >= root)) {
552 /* found valid backup point */
553 wk2 = mp + 1;
554 wk1 += 3;
556 else {
557 /* did not find point, restore '/' */
558 *(wk2-1) = L'/';
564 *wk2 = L'\0';
565 break;
566 default:
567 FIXME("how did we get here - state=%d\n", state);
568 return E_INVALIDARG;
571 *wk2 = L'\0';
572 TRACE("Simplified, orig <%s>, simple <%s>\n",
573 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
576 if(dwFlags & URL_UNESCAPE)
577 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
579 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
580 URL_ESCAPE_SPACES_ONLY |
581 URL_ESCAPE_PERCENT |
582 URL_DONT_ESCAPE_EXTRA_INFO |
583 URL_ESCAPE_SEGMENT_ONLY ))) {
584 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
585 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
586 EscapeFlags);
587 } else { /* No escaping needed, just copy the string */
588 nLen = lstrlenW(lpszUrlCpy);
589 if(nLen < *pcchCanonicalized)
590 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
591 else {
592 hr = E_POINTER;
593 nLen++;
595 *pcchCanonicalized = nLen;
598 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
600 if (hr == S_OK)
601 TRACE("result %s\n", debugstr_w(pszCanonicalized));
603 return hr;
606 /*************************************************************************
607 * UrlCombineA [SHLWAPI.@]
609 * Combine two Urls.
611 * PARAMS
612 * pszBase [I] Base Url
613 * pszRelative [I] Url to combine with pszBase
614 * pszCombined [O] Destination for combined Url
615 * pcchCombined [O] Destination for length of pszCombined
616 * dwFlags [I] URL_ flags from "shlwapi.h"
618 * RETURNS
619 * Success: S_OK. pszCombined contains the combined Url, pcchCombined
620 * contains its length.
621 * Failure: An HRESULT error code indicating the error.
623 HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
624 LPSTR pszCombined, LPDWORD pcchCombined,
625 DWORD dwFlags)
627 LPWSTR base, relative, combined;
628 DWORD ret, len, len2;
630 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
631 debugstr_a(pszBase),debugstr_a(pszRelative),
632 *pcchCombined,dwFlags);
634 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
635 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
636 relative = base + INTERNET_MAX_URL_LENGTH;
637 combined = relative + INTERNET_MAX_URL_LENGTH;
639 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
640 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
641 len = INTERNET_MAX_URL_LENGTH;
643 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
644 if (ret != S_OK) {
645 HeapFree(GetProcessHeap(), 0, base);
646 return ret;
649 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
650 if (len2 > *pcchCombined) {
651 *pcchCombined = len2;
652 HeapFree(GetProcessHeap(), 0, base);
653 return E_POINTER;
655 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
656 0, 0);
657 *pcchCombined = len2;
658 HeapFree(GetProcessHeap(), 0, base);
659 return S_OK;
662 /*************************************************************************
663 * UrlCombineW [SHLWAPI.@]
665 * See UrlCombineA.
667 HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
668 LPWSTR pszCombined, LPDWORD pcchCombined,
669 DWORD dwFlags)
671 UNKNOWN_SHLWAPI_2 base, relative;
672 DWORD myflags, sizeloc = 0;
673 DWORD len, res1, res2, process_case = 0;
674 LPWSTR work, preliminary, mbase, mrelative;
675 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
676 WCHAR single_slash[] = {'/','\0'};
677 HRESULT ret;
679 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
680 debugstr_w(pszBase),debugstr_w(pszRelative),
681 *pcchCombined,dwFlags);
683 base.size = 24;
684 relative.size = 24;
686 /* Get space for duplicates of the input and the output */
687 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
688 sizeof(WCHAR));
689 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
690 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
691 *preliminary = L'\0';
693 /* Canonicalize the base input prior to looking for the scheme */
694 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
695 len = INTERNET_MAX_URL_LENGTH;
696 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
698 /* Canonicalize the relative input prior to looking for the scheme */
699 len = INTERNET_MAX_URL_LENGTH;
700 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
702 /* See if the base has a scheme */
703 res1 = ParseURLW(mbase, &base);
704 if (res1) {
705 /* if pszBase has no scheme, then return pszRelative */
706 TRACE("no scheme detected in Base\n");
707 process_case = 1;
709 else do {
711 /* get size of location field (if it exists) */
712 work = (LPWSTR)base.ap2;
713 sizeloc = 0;
714 if (*work++ == L'/') {
715 if (*work++ == L'/') {
716 /* At this point have start of location and
717 * it ends at next '/' or end of string.
719 while(*work && (*work != L'/')) work++;
720 sizeloc = (DWORD)(work - base.ap2);
724 /* Change .sizep2 to not have the last leaf in it,
725 * Note: we need to start after the location (if it exists)
727 work = strrchrW((base.ap2+sizeloc), L'/');
728 if (work) {
729 len = (DWORD)(work - base.ap2 + 1);
730 base.sizep2 = len;
733 * At this point:
734 * .ap2 points to location (starting with '//')
735 * .sizep2 length of location (above) and rest less the last
736 * leaf (if any)
737 * sizeloc length of location (above) up to but not including
738 * the last '/'
741 res2 = ParseURLW(mrelative, &relative);
742 if (res2) {
743 /* no scheme in pszRelative */
744 TRACE("no scheme detected in Relative\n");
745 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
746 relative.sizep2 = strlenW(mrelative);
747 if (*pszRelative == L':') {
748 /* case that is either left alone or uses pszBase */
749 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
750 process_case = 5;
751 break;
753 process_case = 1;
754 break;
756 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
757 /* case that becomes "file:///" */
758 strcpyW(preliminary, myfilestr);
759 process_case = 1;
760 break;
762 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
763 /* pszRelative has location and rest */
764 process_case = 3;
765 break;
767 if (*mrelative == L'/') {
768 /* case where pszRelative is root to location */
769 process_case = 4;
770 break;
772 process_case = (*base.ap2 == L'/') ? 5 : 3;
773 break;
776 /* handle cases where pszRelative has scheme */
777 if ((base.sizep1 == relative.sizep1) &&
778 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
780 /* since the schemes are the same */
781 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
782 /* case where pszRelative replaces location and following */
783 process_case = 3;
784 break;
786 if (*relative.ap2 == L'/') {
787 /* case where pszRelative is root to location */
788 process_case = 4;
789 break;
791 /* case where scheme is followed by document path */
792 process_case = 5;
793 break;
795 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
796 /* case where pszRelative replaces scheme, location,
797 * and following and handles PLUGGABLE
799 process_case = 2;
800 break;
802 process_case = 1;
803 break;
804 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
807 ret = S_OK;
808 switch (process_case) {
810 case 1: /*
811 * Return pszRelative appended to what ever is in pszCombined,
812 * (which may the string "file:///"
814 len = strlenW(mrelative) + strlenW(preliminary);
815 if (len+1 > *pcchCombined) {
816 *pcchCombined = len;
817 ret = E_POINTER;
818 break;
820 strcatW(preliminary, mrelative);
821 break;
823 case 2: /*
824 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
825 * and pszRelative starts with "//", then append a "/"
827 len = strlenW(mrelative) + 1;
828 if (len+1 > *pcchCombined) {
829 *pcchCombined = len;
830 ret = E_POINTER;
831 break;
833 strcpyW(preliminary, mrelative);
834 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
835 URL_JustLocation(relative.ap2))
836 strcatW(preliminary, single_slash);
837 break;
839 case 3: /*
840 * Return the pszBase scheme with pszRelative. Basicly
841 * keeps the scheme and replaces the domain and following.
843 len = base.sizep1 + 1 + relative.sizep2 + 1;
844 if (len+1 > *pcchCombined) {
845 *pcchCombined = len;
846 ret = E_POINTER;
847 break;
849 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
850 work = preliminary + base.sizep1 + 1;
851 strcpyW(work, relative.ap2);
852 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
853 URL_JustLocation(relative.ap2))
854 strcatW(work, single_slash);
855 break;
857 case 4: /*
858 * Return the pszBase scheme and location but everything
859 * after the location is pszRelative. (Replace document
860 * from root on.)
862 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
863 if (len+1 > *pcchCombined) {
864 *pcchCombined = len;
865 ret = E_POINTER;
866 break;
868 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
869 work = preliminary + base.sizep1 + 1 + sizeloc;
870 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
871 *(work++) = L'/';
872 strcpyW(work, relative.ap2);
873 break;
875 case 5: /*
876 * Return the pszBase without its document (if any) and
877 * append pszRelative after its scheme.
879 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
880 if (len+1 > *pcchCombined) {
881 *pcchCombined = len;
882 ret = E_POINTER;
883 break;
885 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
886 work = preliminary + base.sizep1+1+base.sizep2 - 1;
887 if (*work++ != L'/')
888 *(work++) = L'/';
889 strcpyW(work, relative.ap2);
890 break;
892 default:
893 FIXME("How did we get here????? process_case=%ld\n", process_case);
894 ret = E_INVALIDARG;
897 if (ret == S_OK) {
899 * Now that the combining is done, process the escape options if
900 * necessary, otherwise just copy the string.
902 myflags = dwFlags & (URL_ESCAPE_PERCENT |
903 URL_ESCAPE_SPACES_ONLY |
904 URL_DONT_ESCAPE_EXTRA_INFO |
905 URL_ESCAPE_SEGMENT_ONLY);
906 if (myflags)
907 ret = UrlEscapeW(preliminary, pszCombined,
908 pcchCombined, myflags);
909 else {
910 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
911 memcpy(pszCombined, preliminary, len);
912 *pcchCombined = strlenW(preliminary);
914 TRACE("return-%ld len=%ld, %s\n",
915 process_case, *pcchCombined, debugstr_w(pszCombined));
917 HeapFree(GetProcessHeap(), 0, preliminary);
918 return ret;
921 /*************************************************************************
922 * UrlEscapeA [SHLWAPI.@]
924 * Converts unsafe characters in a Url into escape sequences.
926 * PARAMS
927 * pszUrl [I] Url to modify
928 * pszEscaped [O] Destination for modified Url
929 * pcchEscaped [I/O] Length of pszUrl, destination for length of pszEscaped
930 * dwFlags [I] URL_ flags from "shlwapi.h"
932 * RETURNS
933 * Success: S_OK. pszEscaped contains the escaped Url, pcchEscaped
934 * contains its length.
935 * Failure: E_POINTER, if pszEscaped is not large enough. In this case
936 * pcchEscaped is set to the required length.
938 * Converts unsafe characters into their escape sequences.
940 * NOTES
941 * - By default this function stops converting at the first '?' or
942 * '#' character (MSDN does not document this).
943 * - If dwFlags contains URL_ESCAPE_SPACES_ONLY then only spaces are
944 * converted, but the conversion continues past a '?' or '#'.
945 * - Note that this function did not work well (or at all) in shlwapi version 4.
947 * BUGS
948 * Only the following flags are implemented:
949 *| URL_ESCAPE_SPACES_ONLY
950 *| URL_DONT_ESCAPE_EXTRA_INFO
951 *| URL_ESCAPE_SEGMENT_ONLY
952 *| URL_ESCAPE_PERCENT
954 HRESULT WINAPI UrlEscapeA(
955 LPCSTR pszUrl,
956 LPSTR pszEscaped,
957 LPDWORD pcchEscaped,
958 DWORD dwFlags)
960 LPCSTR src;
961 DWORD needed = 0, ret;
962 BOOL stop_escaping = FALSE;
963 char next[3], *dst = pszEscaped;
964 INT len;
966 TRACE("(%s %p %lx 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
967 *pcchEscaped, dwFlags);
969 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
970 URL_ESCAPE_SEGMENT_ONLY |
971 URL_DONT_ESCAPE_EXTRA_INFO |
972 URL_ESCAPE_PERCENT))
973 FIXME("Unimplemented flags: %08lx\n", dwFlags);
975 /* fix up flags */
976 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
977 /* if SPACES_ONLY specified, reset the other controls */
978 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
979 URL_ESCAPE_PERCENT |
980 URL_ESCAPE_SEGMENT_ONLY);
982 else
983 /* if SPACES_ONLY *not* specified then assume DONT_ESCAPE_EXTRA_INFO */
984 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
986 for(src = pszUrl; *src; src++) {
987 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
988 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
989 (*src == '#' || *src == '?'))
990 stop_escaping = TRUE;
992 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
993 /* TRACE("escaping %c\n", *src); */
994 next[0] = '%';
995 next[1] = hexDigits[(*src >> 4) & 0xf];
996 next[2] = hexDigits[*src & 0xf];
997 len = 3;
998 } else {
999 /* TRACE("passing %c\n", *src); */
1000 next[0] = *src;
1001 len = 1;
1004 if(needed + len <= *pcchEscaped) {
1005 memcpy(dst, next, len);
1006 dst += len;
1008 needed += len;
1011 if(needed < *pcchEscaped) {
1012 *dst = '\0';
1013 ret = S_OK;
1014 } else {
1015 needed++; /* add one for the '\0' */
1016 ret = E_POINTER;
1018 *pcchEscaped = needed;
1019 return ret;
1022 /*************************************************************************
1023 * UrlEscapeW [SHLWAPI.@]
1025 * See UrlEscapeA.
1027 HRESULT WINAPI UrlEscapeW(
1028 LPCWSTR pszUrl,
1029 LPWSTR pszEscaped,
1030 LPDWORD pcchEscaped,
1031 DWORD dwFlags)
1033 LPCWSTR src;
1034 DWORD needed = 0, ret;
1035 BOOL stop_escaping = FALSE;
1036 WCHAR next[5], *dst = pszEscaped;
1037 INT len;
1039 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
1040 pcchEscaped, dwFlags);
1042 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
1043 URL_ESCAPE_SEGMENT_ONLY |
1044 URL_DONT_ESCAPE_EXTRA_INFO |
1045 URL_ESCAPE_PERCENT))
1046 FIXME("Unimplemented flags: %08lx\n", dwFlags);
1048 /* fix up flags */
1049 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
1050 /* if SPACES_ONLY specified, reset the other controls */
1051 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
1052 URL_ESCAPE_PERCENT |
1053 URL_ESCAPE_SEGMENT_ONLY);
1055 else
1056 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
1057 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
1059 for(src = pszUrl; *src; src++) {
1061 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
1062 * (*src == L'#' || *src == L'?'))
1063 * stop_escaping = TRUE;
1065 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
1066 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
1067 (*src == L'#' || *src == L'?'))
1068 stop_escaping = TRUE;
1070 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
1071 /* TRACE("escaping %c\n", *src); */
1072 next[0] = L'%';
1074 * I would have assumed that the W form would escape
1075 * the character with 4 hex digits (or even 8),
1076 * however, experiments show that native shlwapi escapes
1077 * with only 2 hex digits.
1078 * next[1] = hexDigits[(*src >> 12) & 0xf];
1079 * next[2] = hexDigits[(*src >> 8) & 0xf];
1080 * next[3] = hexDigits[(*src >> 4) & 0xf];
1081 * next[4] = hexDigits[*src & 0xf];
1082 * len = 5;
1084 next[1] = hexDigits[(*src >> 4) & 0xf];
1085 next[2] = hexDigits[*src & 0xf];
1086 len = 3;
1087 } else {
1088 /* TRACE("passing %c\n", *src); */
1089 next[0] = *src;
1090 len = 1;
1093 if(needed + len <= *pcchEscaped) {
1094 memcpy(dst, next, len*sizeof(WCHAR));
1095 dst += len;
1097 needed += len;
1100 if(needed < *pcchEscaped) {
1101 *dst = L'\0';
1102 ret = S_OK;
1103 } else {
1104 needed++; /* add one for the '\0' */
1105 ret = E_POINTER;
1107 *pcchEscaped = needed;
1108 return ret;
1112 /*************************************************************************
1113 * UrlUnescapeA [SHLWAPI.@]
1115 * Converts Url escape sequences back to ordinary characters.
1117 * PARAMS
1118 * pszUrl [I/O] Url to convert
1119 * pszUnescaped [O] Destination for converted Url
1120 * pcchUnescaped [I/O] Size of output string
1121 * dwFlags [I] URL_ESCAPE_ Flags from "shlwapi.h"
1123 * RETURNS
1124 * Success: S_OK. The converted value is in pszUnescaped, or in pszUrl if
1125 * dwFlags includes URL_ESCAPE_INPLACE.
1126 * Failure: E_POINTER if the converted Url is bigger than pcchUnescaped. In
1127 * this case pcchUnescaped is set to the size required.
1128 * NOTES
1129 * If dwFlags includes URL_DONT_ESCAPE_EXTRA_INFO, the conversion stops at
1130 * the first occurrence of either a '?' or '#' character.
1132 HRESULT WINAPI UrlUnescapeA(
1133 LPCSTR pszUrl,
1134 LPSTR pszUnescaped,
1135 LPDWORD pcchUnescaped,
1136 DWORD dwFlags)
1138 char *dst, next;
1139 LPCSTR src;
1140 HRESULT ret;
1141 DWORD needed;
1142 BOOL stop_unescaping = FALSE;
1144 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
1145 pcchUnescaped, dwFlags);
1147 if(dwFlags & URL_UNESCAPE_INPLACE)
1148 dst = (char*)pszUrl;
1149 else
1150 dst = pszUnescaped;
1152 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1153 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1154 (*src == '#' || *src == '?')) {
1155 stop_unescaping = TRUE;
1156 next = *src;
1157 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
1158 && stop_unescaping == FALSE) {
1159 INT ih;
1160 char buf[3];
1161 memcpy(buf, src + 1, 2);
1162 buf[2] = '\0';
1163 ih = strtol(buf, NULL, 16);
1164 next = (CHAR) ih;
1165 src += 2; /* Advance to end of escape */
1166 } else
1167 next = *src;
1169 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1170 *dst++ = next;
1173 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1174 *dst = '\0';
1175 ret = S_OK;
1176 } else {
1177 needed++; /* add one for the '\0' */
1178 ret = E_POINTER;
1180 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1181 *pcchUnescaped = needed;
1183 if (ret == S_OK) {
1184 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1185 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
1188 return ret;
1191 /*************************************************************************
1192 * UrlUnescapeW [SHLWAPI.@]
1194 * See UrlUnescapeA.
1196 HRESULT WINAPI UrlUnescapeW(
1197 LPCWSTR pszUrl,
1198 LPWSTR pszUnescaped,
1199 LPDWORD pcchUnescaped,
1200 DWORD dwFlags)
1202 WCHAR *dst, next;
1203 LPCWSTR src;
1204 HRESULT ret;
1205 DWORD needed;
1206 BOOL stop_unescaping = FALSE;
1208 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
1209 pcchUnescaped, dwFlags);
1211 if(dwFlags & URL_UNESCAPE_INPLACE)
1212 dst = (WCHAR*)pszUrl;
1213 else
1214 dst = pszUnescaped;
1216 for(src = pszUrl, needed = 0; *src; src++, needed++) {
1217 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
1218 (*src == L'#' || *src == L'?')) {
1219 stop_unescaping = TRUE;
1220 next = *src;
1221 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
1222 && stop_unescaping == FALSE) {
1223 INT ih;
1224 WCHAR buf[3];
1225 memcpy(buf, src + 1, 2*sizeof(WCHAR));
1226 buf[2] = L'\0';
1227 ih = StrToIntW(buf);
1228 next = (WCHAR) ih;
1229 src += 2; /* Advance to end of escape */
1230 } else
1231 next = *src;
1233 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
1234 *dst++ = next;
1237 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
1238 *dst = L'\0';
1239 ret = S_OK;
1240 } else {
1241 needed++; /* add one for the '\0' */
1242 ret = E_POINTER;
1244 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1245 *pcchUnescaped = needed;
1247 if (ret == S_OK) {
1248 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1249 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1252 return ret;
1255 /*************************************************************************
1256 * UrlGetLocationA [SHLWAPI.@]
1258 * Get the location from a Url.
1260 * PARAMS
1261 * pszUrl [I] Url to get the location from
1263 * RETURNS
1264 * A pointer to the start of the location in pszUrl, or NULL if there is
1265 * no location.
1267 * NOTES
1268 * - MSDN erroneously states that "The location is the segment of the Url
1269 * starting with a '?' or '#' character". Neither V4 nor V5 of shlwapi.dll
1270 * stop at '?' and always return a NULL in this case.
1271 * - MSDN also erroneously states that "If a file URL has a query string,
1272 * the returned string is the query string". In all tested cases, if the
1273 * Url starts with "fi" then a NULL is returned. V5 gives the following results:
1274 *| Result Url
1275 *| ------ ---
1276 *| NULL file://aa/b/cd#hohoh
1277 *| #hohoh http://aa/b/cd#hohoh
1278 *| NULL fi://aa/b/cd#hohoh
1279 *| #hohoh ff://aa/b/cd#hohoh
1281 LPCSTR WINAPI UrlGetLocationA(
1282 LPCSTR pszUrl)
1284 UNKNOWN_SHLWAPI_1 base;
1285 DWORD res1;
1287 base.size = 24;
1288 res1 = ParseURLA(pszUrl, &base);
1289 if (res1) return NULL; /* invalid scheme */
1291 /* if scheme is file: then never return pointer */
1292 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1294 /* Look for '#' and return its addr */
1295 return strchr(base.ap2, '#');
1298 /*************************************************************************
1299 * UrlGetLocationW [SHLWAPI.@]
1301 * See UrlGetLocationA.
1303 LPCWSTR WINAPI UrlGetLocationW(
1304 LPCWSTR pszUrl)
1306 UNKNOWN_SHLWAPI_2 base;
1307 DWORD res1;
1309 base.size = 24;
1310 res1 = ParseURLW(pszUrl, &base);
1311 if (res1) return NULL; /* invalid scheme */
1313 /* if scheme is file: then never return pointer */
1314 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1316 /* Look for '#' and return its addr */
1317 return strchrW(base.ap2, L'#');
1320 /*************************************************************************
1321 * UrlCompareA [SHLWAPI.@]
1323 * Compare two Urls.
1325 * PARAMS
1326 * pszUrl1 [I] First Url to compare
1327 * pszUrl2 [I] Url to compare to pszUrl1
1328 * fIgnoreSlash [I] TRUE = compare only up to a final slash
1330 * RETURNS
1331 * less than zero, zero, or greater than zero indicating pszUrl2 is greater
1332 * than, equal to, or less than pszUrl1 respectively.
1334 INT WINAPI UrlCompareA(
1335 LPCSTR pszUrl1,
1336 LPCSTR pszUrl2,
1337 BOOL fIgnoreSlash)
1339 INT ret, len, len1, len2;
1341 if (!fIgnoreSlash)
1342 return strcmp(pszUrl1, pszUrl2);
1343 len1 = strlen(pszUrl1);
1344 if (pszUrl1[len1-1] == '/') len1--;
1345 len2 = strlen(pszUrl2);
1346 if (pszUrl2[len2-1] == '/') len2--;
1347 if (len1 == len2)
1348 return strncmp(pszUrl1, pszUrl2, len1);
1349 len = min(len1, len2);
1350 ret = strncmp(pszUrl1, pszUrl2, len);
1351 if (ret) return ret;
1352 if (len1 > len2) return 1;
1353 return -1;
1356 /*************************************************************************
1357 * UrlCompareW [SHLWAPI.@]
1359 * See UrlCompareA.
1361 INT WINAPI UrlCompareW(
1362 LPCWSTR pszUrl1,
1363 LPCWSTR pszUrl2,
1364 BOOL fIgnoreSlash)
1366 INT ret;
1367 size_t len, len1, len2;
1369 if (!fIgnoreSlash)
1370 return strcmpW(pszUrl1, pszUrl2);
1371 len1 = strlenW(pszUrl1);
1372 if (pszUrl1[len1-1] == '/') len1--;
1373 len2 = strlenW(pszUrl2);
1374 if (pszUrl2[len2-1] == '/') len2--;
1375 if (len1 == len2)
1376 return strncmpW(pszUrl1, pszUrl2, len1);
1377 len = min(len1, len2);
1378 ret = strncmpW(pszUrl1, pszUrl2, len);
1379 if (ret) return ret;
1380 if (len1 > len2) return 1;
1381 return -1;
1384 /*************************************************************************
1385 * HashData [SHLWAPI.@]
1387 * Hash an input block into a variable sized digest.
1389 * PARAMS
1390 * lpSrc [I] Input block
1391 * nSrcLen [I] Length of lpSrc
1392 * lpDest [I] Output for hash digest
1393 * nDestLen [I] Length of lpDest
1395 * RETURNS
1396 * Success: TRUE. lpDest is filled with the computed hash value.
1397 * Failure: FALSE, if any argument is invalid.
1399 BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen,
1400 unsigned char *lpDest, INT nDestLen)
1402 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1404 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1405 IsBadWritePtr(lpDest, nDestLen))
1406 return FALSE;
1408 while (destCount >= 0)
1410 lpDest[destCount] = (destCount & 0xff);
1411 destCount--;
1414 while (srcCount >= 0)
1416 destCount = nDestLen - 1;
1417 while (destCount >= 0)
1419 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1420 destCount--;
1422 srcCount--;
1424 return TRUE;
1427 /*************************************************************************
1428 * UrlHashA [SHLWAPI.@]
1430 * Produce a Hash from a Url.
1432 * PARAMS
1433 * pszUrl [I] Url to hash
1434 * lpDest [O] Destinationh for hash
1435 * nDestLen [I] Length of lpDest
1437 * RETURNS
1438 * Success: S_OK. lpDest is filled with the computed hash value.
1439 * Failure: E_INVALIDARG, if any argument is invalid.
1441 HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1443 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1444 return E_INVALIDARG;
1446 HashData((PBYTE)pszUrl, (int)strlen(pszUrl), lpDest, nDestLen);
1447 return S_OK;
1450 /*************************************************************************
1451 * UrlHashW [SHLWAPI.@]
1453 * See UrlHashA.
1455 HRESULT WINAPI UrlHashW(LPCWSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1457 char szUrl[MAX_PATH];
1459 TRACE("(%s,%p,%d)\n",debugstr_w(pszUrl), lpDest, nDestLen);
1461 if (IsBadStringPtrW(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1462 return E_INVALIDARG;
1464 /* Win32 hashes the data as an ASCII string, presumably so that both A+W
1465 * return the same digests for the same URL.
1467 WideCharToMultiByte(0, 0, pszUrl, -1, szUrl, MAX_PATH, 0, 0);
1468 HashData((PBYTE)szUrl, (int)strlen(szUrl), lpDest, nDestLen);
1469 return S_OK;
1472 /*************************************************************************
1473 * UrlApplySchemeA [SHLWAPI.@]
1475 * Apply a scheme to a Url.
1477 * PARAMS
1478 * pszIn [I] Url to apply scheme to
1479 * pszOut [O] Destination for modified Url
1480 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1481 * dwFlags [I] URL_ flags from "shlwapi.h"
1483 * RETURNS
1484 * Success: S_OK: pszOut contains the modified Url, pcchOut contains its length.
1485 * Failure: An HRESULT error code describing the error.
1487 HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1489 LPWSTR in, out;
1490 DWORD ret, len, len2;
1492 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1493 debugstr_a(pszIn), *pcchOut, dwFlags);
1495 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1496 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1497 out = in + INTERNET_MAX_URL_LENGTH;
1499 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1500 len = INTERNET_MAX_URL_LENGTH;
1502 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1503 if ((ret != S_OK) && (ret != S_FALSE)) {
1504 HeapFree(GetProcessHeap(), 0, in);
1505 return ret;
1508 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1509 if (len2 > *pcchOut) {
1510 *pcchOut = len2;
1511 HeapFree(GetProcessHeap(), 0, in);
1512 return E_POINTER;
1514 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1515 *pcchOut = len2;
1516 HeapFree(GetProcessHeap(), 0, in);
1517 return ret;
1520 static HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1522 HKEY newkey;
1523 BOOL j;
1524 INT index;
1525 DWORD value_len, data_len, dwType, i;
1526 WCHAR reg_path[MAX_PATH];
1527 WCHAR value[MAX_PATH], data[MAX_PATH];
1528 WCHAR Wxx, Wyy;
1530 MultiByteToWideChar(0, 0,
1531 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1532 -1, reg_path, MAX_PATH);
1533 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1534 index = 0;
1535 while(value_len = data_len = MAX_PATH,
1536 RegEnumValueW(newkey, index, value, &value_len,
1537 0, &dwType, (LPVOID)data, &data_len) == 0) {
1538 TRACE("guess %d %s is %s\n",
1539 index, debugstr_w(value), debugstr_w(data));
1541 j = FALSE;
1542 for(i=0; i<value_len; i++) {
1543 Wxx = pszIn[i];
1544 Wyy = value[i];
1545 /* remember that TRUE is not-equal */
1546 j = ChrCmpIW(Wxx, Wyy);
1547 if (j) break;
1549 if ((i == value_len) && !j) {
1550 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1551 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1552 RegCloseKey(newkey);
1553 return E_POINTER;
1555 strcpyW(pszOut, data);
1556 strcatW(pszOut, pszIn);
1557 *pcchOut = strlenW(pszOut);
1558 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1559 RegCloseKey(newkey);
1560 return S_OK;
1562 index++;
1564 RegCloseKey(newkey);
1565 return -1;
1568 static HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1570 HKEY newkey;
1571 DWORD data_len, dwType;
1572 WCHAR reg_path[MAX_PATH];
1573 WCHAR value[MAX_PATH], data[MAX_PATH];
1575 /* get and prepend default */
1576 MultiByteToWideChar(0, 0,
1577 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1578 -1, reg_path, MAX_PATH);
1579 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1580 data_len = MAX_PATH;
1581 value[0] = L'@';
1582 value[1] = L'\0';
1583 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1584 RegCloseKey(newkey);
1585 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1586 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1587 return E_POINTER;
1589 strcpyW(pszOut, data);
1590 strcatW(pszOut, pszIn);
1591 *pcchOut = strlenW(pszOut);
1592 TRACE("used default %s\n", debugstr_w(pszOut));
1593 return S_OK;
1596 /*************************************************************************
1597 * UrlApplySchemeW [SHLWAPI.@]
1599 * See UrlApplySchemeA.
1601 HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1603 UNKNOWN_SHLWAPI_2 in_scheme;
1604 DWORD res1;
1605 HRESULT ret;
1607 TRACE("(in %s, out size %ld, flags %08lx)\n",
1608 debugstr_w(pszIn), *pcchOut, dwFlags);
1610 if (dwFlags & URL_APPLY_GUESSFILE) {
1611 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1612 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1613 strcpyW(pszOut, pszIn);
1614 *pcchOut = strlenW(pszOut);
1615 return S_FALSE;
1618 in_scheme.size = 24;
1619 /* See if the base has a scheme */
1620 res1 = ParseURLW(pszIn, &in_scheme);
1621 if (res1) {
1622 /* no scheme in input, need to see if we need to guess */
1623 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1624 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1625 return ret;
1628 else {
1629 /* we have a scheme, see if valid (known scheme) */
1630 if (in_scheme.fcncde) {
1631 /* have valid scheme, so just copy and exit */
1632 if (strlenW(pszIn) + 1 > *pcchOut) {
1633 *pcchOut = strlenW(pszIn) + 1;
1634 return E_POINTER;
1636 strcpyW(pszOut, pszIn);
1637 *pcchOut = strlenW(pszOut);
1638 TRACE("valid scheme, returing copy\n");
1639 return S_OK;
1643 /* If we are here, then either invalid scheme,
1644 * or no scheme and can't/failed guess.
1646 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1647 ((res1 != 0)) ) &&
1648 (dwFlags & URL_APPLY_DEFAULT)) {
1649 /* find and apply default scheme */
1650 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1653 /* just copy and give proper return code */
1654 if (strlenW(pszIn) + 1 > *pcchOut) {
1655 *pcchOut = strlenW(pszIn) + 1;
1656 return E_POINTER;
1658 strcpyW(pszOut, pszIn);
1659 *pcchOut = strlenW(pszOut);
1660 TRACE("returning copy, left alone\n");
1661 return S_FALSE;
1664 /*************************************************************************
1665 * UrlIsA [SHLWAPI.@]
1667 * Determine if a Url is of a certain class.
1669 * PARAMS
1670 * pszUrl [I] Url to check
1671 * Urlis [I] URLIS_ constant from "shlwapi.h"
1673 * RETURNS
1674 * TRUE if pszUrl belongs to the class type in Urlis.
1675 * FALSE Otherwise.
1677 BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1679 UNKNOWN_SHLWAPI_1 base;
1680 DWORD res1;
1682 switch (Urlis) {
1684 case URLIS_OPAQUE:
1685 base.size = 24;
1686 res1 = ParseURLA(pszUrl, &base);
1687 if (res1) return FALSE; /* invalid scheme */
1688 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1689 /* has scheme followed by 2 '/' */
1690 return FALSE;
1691 return TRUE;
1693 case URLIS_URL:
1694 case URLIS_NOHISTORY:
1695 case URLIS_FILEURL:
1696 case URLIS_APPLIABLE:
1697 case URLIS_DIRECTORY:
1698 case URLIS_HASQUERY:
1699 default:
1700 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1702 return FALSE;
1705 /*************************************************************************
1706 * UrlIsW [SHLWAPI.@]
1708 * See UrlIsA.
1710 BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1712 UNKNOWN_SHLWAPI_2 base;
1713 DWORD res1;
1715 switch (Urlis) {
1717 case URLIS_OPAQUE:
1718 base.size = 24;
1719 res1 = ParseURLW(pszUrl, &base);
1720 if (res1) return FALSE; /* invalid scheme */
1721 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1722 /* has scheme followed by 2 '/' */
1723 return FALSE;
1724 return TRUE;
1726 case URLIS_URL:
1727 case URLIS_NOHISTORY:
1728 case URLIS_FILEURL:
1729 case URLIS_APPLIABLE:
1730 case URLIS_DIRECTORY:
1731 case URLIS_HASQUERY:
1732 default:
1733 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1735 return FALSE;
1738 /*************************************************************************
1739 * UrlIsNoHistoryA [SHLWAPI.@]
1741 * Determine if a Url should not be stored in the users history list.
1743 * PARAMS
1744 * pszUrl [I] Url to check
1746 * RETURNS
1747 * TRUE, if pszUrl should be excluded from the history list,
1748 * FALSE otherwise.
1750 BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1752 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1755 /*************************************************************************
1756 * UrlIsNoHistoryW [SHLWAPI.@]
1758 * See UrlIsNoHistoryA.
1760 BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1762 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1765 /*************************************************************************
1766 * UrlIsOpaqueA [SHLWAPI.@]
1768 * Determine if a Url is opaque.
1770 * PARAMS
1771 * pszUrl [I] Url to check
1773 * RETURNS
1774 * TRUE if pszUrl is opaque,
1775 * FALSE Otherwise.
1777 * NOTES
1778 * An opaque Url is one that does not start with "<protocol>://".
1780 BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1782 return UrlIsA(pszUrl, URLIS_OPAQUE);
1785 /*************************************************************************
1786 * UrlIsOpaqueW [SHLWAPI.@]
1788 * See UrlIsOpaqueA.
1790 BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1792 return UrlIsW(pszUrl, URLIS_OPAQUE);
1795 /*************************************************************************
1796 * Scans for characters of type "type" and when not matching found,
1797 * returns pointer to it and length in size.
1799 * Characters tested based on RFC 1738
1801 static LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1803 static DWORD alwayszero = 0;
1804 BOOL cont = TRUE;
1806 *size = 0;
1808 switch(type){
1810 case SCHEME:
1811 while (cont) {
1812 if ( (islowerW(*start) && isalphaW(*start)) ||
1813 isdigitW(*start) ||
1814 (*start == L'+') ||
1815 (*start == L'-') ||
1816 (*start == L'.')) {
1817 start++;
1818 (*size)++;
1820 else
1821 cont = FALSE;
1823 break;
1825 case USERPASS:
1826 while (cont) {
1827 if ( isalphaW(*start) ||
1828 isdigitW(*start) ||
1829 /* user/password only characters */
1830 (*start == L';') ||
1831 (*start == L'?') ||
1832 (*start == L'&') ||
1833 (*start == L'=') ||
1834 /* *extra* characters */
1835 (*start == L'!') ||
1836 (*start == L'*') ||
1837 (*start == L'\'') ||
1838 (*start == L'(') ||
1839 (*start == L')') ||
1840 (*start == L',') ||
1841 /* *safe* characters */
1842 (*start == L'$') ||
1843 (*start == L'_') ||
1844 (*start == L'+') ||
1845 (*start == L'-') ||
1846 (*start == L'.')) {
1847 start++;
1848 (*size)++;
1849 } else if (*start == L'%') {
1850 if (isxdigitW(*(start+1)) &&
1851 isxdigitW(*(start+2))) {
1852 start += 3;
1853 *size += 3;
1854 } else
1855 cont = FALSE;
1856 } else
1857 cont = FALSE;
1859 break;
1861 case PORT:
1862 while (cont) {
1863 if (isdigitW(*start)) {
1864 start++;
1865 (*size)++;
1867 else
1868 cont = FALSE;
1870 break;
1872 case HOST:
1873 while (cont) {
1874 if (isalnumW(*start) ||
1875 (*start == L'-') ||
1876 (*start == L'.') ) {
1877 start++;
1878 (*size)++;
1880 else
1881 cont = FALSE;
1883 break;
1884 default:
1885 FIXME("unknown type %d\n", type);
1886 return (LPWSTR)&alwayszero;
1888 /* TRACE("scanned %ld characters next char %p<%c>\n",
1889 *size, start, *start); */
1890 return start;
1893 /*************************************************************************
1894 * Attempt to parse URL into pieces.
1896 static LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1898 LPCWSTR work;
1900 memset(pl, 0, sizeof(WINE_PARSE_URL));
1901 pl->pScheme = pszUrl;
1902 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1903 if (!*work || (*work != L':')) goto ERROR;
1904 work++;
1905 if ((*work != L'/') || (*(work+1) != L'/')) goto ERROR;
1906 pl->pUserName = work + 2;
1907 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1908 if (*work == L':' ) {
1909 /* parse password */
1910 work++;
1911 pl->pPassword = work;
1912 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1913 if (*work != L'@') {
1914 /* what we just parsed must be the hostname and port
1915 * so reset pointers and clear then let it parse */
1916 pl->szUserName = pl->szPassword = 0;
1917 work = pl->pUserName - 1;
1918 pl->pUserName = pl->pPassword = 0;
1920 } else if (*work == L'@') {
1921 /* no password */
1922 pl->szPassword = 0;
1923 pl->pPassword = 0;
1924 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1925 /* what was parsed was hostname, so reset pointers and let it parse */
1926 pl->szUserName = pl->szPassword = 0;
1927 work = pl->pUserName - 1;
1928 pl->pUserName = pl->pPassword = 0;
1929 } else goto ERROR;
1931 /* now start parsing hostname or hostnumber */
1932 work++;
1933 pl->pHostName = work;
1934 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1935 if (*work == L':') {
1936 /* parse port */
1937 work++;
1938 pl->pPort = work;
1939 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1941 if (*work == L'/') {
1942 /* see if query string */
1943 pl->pQuery = strchrW(work, L'?');
1944 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1946 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1947 pl->pScheme, pl->szScheme,
1948 pl->pUserName, pl->szUserName,
1949 pl->pPassword, pl->szPassword,
1950 pl->pHostName, pl->szHostName,
1951 pl->pPort, pl->szPort,
1952 pl->pQuery, pl->szQuery);
1953 return S_OK;
1954 ERROR:
1955 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1956 return E_INVALIDARG;
1959 /*************************************************************************
1960 * UrlGetPartA [SHLWAPI.@]
1962 * Retrieve part of a Url.
1964 * PARAMS
1965 * pszIn [I] Url to parse
1966 * pszOut [O] Destination for part of pszIn requested
1967 * pcchOut [I/O] Length of pszOut/destination for length of pszOut
1968 * dwPart [I] URL_PART_ enum from "shlwapi.h"
1969 * dwFlags [I] URL_ flags from "shlwapi.h"
1971 * RETURNS
1972 * Success: S_OK. pszOut contains the part requested, pcchOut contains its length.
1973 * Failure: An HRESULT error code describing the error.
1975 HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1976 DWORD dwPart, DWORD dwFlags)
1978 LPWSTR in, out;
1979 DWORD ret, len, len2;
1981 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1982 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1983 out = in + INTERNET_MAX_URL_LENGTH;
1985 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1987 len = INTERNET_MAX_URL_LENGTH;
1988 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1990 if (ret != S_OK) {
1991 HeapFree(GetProcessHeap(), 0, in);
1992 return ret;
1995 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1996 if (len2 > *pcchOut) {
1997 *pcchOut = len2;
1998 HeapFree(GetProcessHeap(), 0, in);
1999 return E_POINTER;
2001 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
2002 *pcchOut = len2;
2003 HeapFree(GetProcessHeap(), 0, in);
2004 return S_OK;
2007 /*************************************************************************
2008 * UrlGetPartW [SHLWAPI.@]
2010 * See UrlGetPartA.
2012 HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
2013 DWORD dwPart, DWORD dwFlags)
2015 WINE_PARSE_URL pl;
2016 HRESULT ret;
2017 DWORD size, schsize;
2018 LPCWSTR addr, schaddr;
2019 LPWSTR work;
2021 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
2022 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
2024 ret = URL_ParseUrl(pszIn, &pl);
2025 if (!ret) {
2026 schaddr = pl.pScheme;
2027 schsize = pl.szScheme;
2029 switch (dwPart) {
2030 case URL_PART_SCHEME:
2031 if (!pl.szScheme) return E_INVALIDARG;
2032 addr = pl.pScheme;
2033 size = pl.szScheme;
2034 break;
2036 case URL_PART_HOSTNAME:
2037 if (!pl.szHostName) return E_INVALIDARG;
2038 addr = pl.pHostName;
2039 size = pl.szHostName;
2040 break;
2042 case URL_PART_USERNAME:
2043 if (!pl.szUserName) return E_INVALIDARG;
2044 addr = pl.pUserName;
2045 size = pl.szUserName;
2046 break;
2048 case URL_PART_PASSWORD:
2049 if (!pl.szPassword) return E_INVALIDARG;
2050 addr = pl.pPassword;
2051 size = pl.szPassword;
2052 break;
2054 case URL_PART_PORT:
2055 if (!pl.szPort) return E_INVALIDARG;
2056 addr = pl.pPort;
2057 size = pl.szPort;
2058 break;
2060 case URL_PART_QUERY:
2061 if (!pl.szQuery) return E_INVALIDARG;
2062 addr = pl.pQuery;
2063 size = pl.szQuery;
2064 break;
2066 default:
2067 return E_INVALIDARG;
2070 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
2071 if (*pcchOut < size + schsize + 2) {
2072 *pcchOut = size + schsize + 2;
2073 return E_POINTER;
2075 strncpyW(pszOut, schaddr, schsize);
2076 work = pszOut + schsize;
2077 *work = L':';
2078 strncpyW(work+1, addr, size);
2079 *pcchOut = size + schsize + 1;
2080 work += (size + 1);
2081 *work = L'\0';
2083 else {
2084 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
2085 strncpyW(pszOut, addr, size);
2086 *pcchOut = size;
2087 work = pszOut + size;
2088 *work = L'\0';
2090 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
2092 return ret;
2095 /*************************************************************************
2096 * PathIsURLA [SHLWAPI.@]
2098 * Check if the given path is a Url.
2100 * PARAMS
2101 * lpszPath [I] Path to check.
2103 * RETURNS
2104 * TRUE if lpszPath is a Url.
2105 * FALSE if lpszPath is NULL or not a Url.
2107 BOOL WINAPI PathIsURLA(LPCSTR lpstrPath)
2109 UNKNOWN_SHLWAPI_1 base;
2110 DWORD res1;
2112 if (!lpstrPath || !*lpstrPath) return FALSE;
2114 /* get protocol */
2115 base.size = sizeof(base);
2116 res1 = ParseURLA(lpstrPath, &base);
2117 return (base.fcncde > 0);
2120 /*************************************************************************
2121 * PathIsURLW [SHLWAPI.@]
2123 * See PathIsURLA.
2125 BOOL WINAPI PathIsURLW(LPCWSTR lpstrPath)
2127 UNKNOWN_SHLWAPI_2 base;
2128 DWORD res1;
2130 if (!lpstrPath || !*lpstrPath) return FALSE;
2132 /* get protocol */
2133 base.size = sizeof(base);
2134 res1 = ParseURLW(lpstrPath, &base);
2135 return (base.fcncde > 0);
2138 /*************************************************************************
2139 * UrlCreateFromPathA [SHLWAPI.@]
2141 * Create a Url from a file path.
2143 * PARAMS
2144 * pszPath [I] Path to convert
2145 * pszUrl [O] Destination for the converted Url
2146 * pcchUrl [I/O] Length of pszUrl
2147 * dwReserved [I] Reserved, must be 0
2149 * RETURNS
2150 * Success: S_OK. pszUrl contains the converted path.
2151 * Failure: An HRESULT error code.
2153 HRESULT WINAPI UrlCreateFromPathA(LPCSTR pszPath, LPSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2155 DWORD nCharBeforeColon = 0;
2156 DWORD nSlashes = 0;
2157 DWORD dwChRequired = 0;
2158 LPSTR pszNewUrl = NULL;
2159 LPCSTR pszConstPointer = NULL;
2160 LPSTR pszPointer = NULL;
2161 DWORD i;
2162 HRESULT ret;
2164 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszPath), pszUrl, pcchUrl, dwReserved);
2166 /* Validate arguments */
2167 if (dwReserved != 0)
2169 FIXME("dwReserved should be 0: 0x%08lx\n", dwReserved);
2170 return E_INVALIDARG;
2172 if (!pszUrl || !pcchUrl || !pszUrl)
2174 ERR("Invalid argument\n");
2175 return E_INVALIDARG;
2178 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2180 if (isalpha(*pszConstPointer) || isdigit(*pszConstPointer) ||
2181 *pszConstPointer == '.' || *pszConstPointer == '-')
2182 nCharBeforeColon++;
2183 else break;
2185 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2187 dwChRequired = lstrlenA(pszPath);
2188 if (dwChRequired > *pcchUrl)
2190 *pcchUrl = dwChRequired;
2191 return E_POINTER;
2193 else
2195 *pcchUrl = dwChRequired;
2196 StrCpyA(pszUrl, pszPath);
2197 return S_FALSE;
2200 /* then must need converting to file: format */
2202 /* Strip off leading slashes */
2203 while (*pszPath == '\\' || *pszPath == '/')
2205 pszPath++;
2206 nSlashes++;
2209 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2210 TRACE("pszUrl: %s\n", debugstr_a(pszPath));
2211 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, dwChRequired + 1);
2212 ret = UrlEscapeA(pszPath, pszNewUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2213 TRACE("ret: 0x%08lx, pszUrl: %s\n", ret, debugstr_a(pszNewUrl));
2214 TRACE("%ld\n", dwChRequired);
2215 if (ret != E_POINTER && FAILED(ret))
2216 return ret;
2217 dwChRequired += 5; /* "file:" */
2218 if ((lstrlenA(pszUrl) > 1) && isalpha(pszUrl[0]) && (pszUrl[1] == ':'))
2220 dwChRequired += 3; /* "///" */
2221 nSlashes = 3;
2223 else
2224 switch (nSlashes)
2226 case 0: /* no slashes */
2227 break;
2228 case 2: /* two slashes */
2229 case 4:
2230 case 5:
2231 case 6:
2232 dwChRequired += 2;
2233 nSlashes = 2;
2234 break;
2235 default: /* three slashes */
2236 dwChRequired += 3;
2237 nSlashes = 3;
2240 if (dwChRequired > *pcchUrl)
2241 return E_POINTER;
2242 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2243 StrCpyA(pszUrl, "file:");
2244 pszPointer = pszUrl + lstrlenA(pszUrl);
2245 for (i=0; i < nSlashes; i++)
2247 *pszPointer = '/';
2248 pszPointer++;
2250 StrCpyA(pszPointer, pszNewUrl);
2251 TRACE("<- %s\n", debugstr_a(pszUrl));
2252 return S_OK;
2255 /*************************************************************************
2256 * UrlCreateFromPathW [SHLWAPI.@]
2258 * See UrlCreateFromPathA.
2260 HRESULT WINAPI UrlCreateFromPathW(LPCWSTR pszPath, LPWSTR pszUrl, LPDWORD pcchUrl, DWORD dwReserved)
2262 DWORD nCharBeforeColon = 0;
2263 DWORD nSlashes = 0;
2264 DWORD dwChRequired = 0;
2265 LPWSTR pszNewUrl = NULL;
2266 LPCWSTR pszConstPointer = NULL;
2267 LPWSTR pszPointer = NULL;
2268 DWORD i;
2269 HRESULT ret;
2271 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszPath), pszUrl, pcchUrl, dwReserved);
2273 /* Validate arguments */
2274 if (dwReserved != 0)
2275 return E_INVALIDARG;
2276 if (!pszUrl || !pcchUrl || !pszUrl)
2277 return E_INVALIDARG;
2279 for (pszConstPointer = pszPath; *pszConstPointer; pszConstPointer++)
2281 if (isalphaW(*pszConstPointer) || isdigitW(*pszConstPointer) ||
2282 *pszConstPointer == '.' || *pszConstPointer == '-')
2283 nCharBeforeColon++;
2284 else break;
2286 if (*pszConstPointer == ':') /* then already in URL format, so copy */
2288 dwChRequired = lstrlenW(pszPath);
2289 *pcchUrl = dwChRequired;
2290 if (dwChRequired > *pcchUrl)
2291 return E_POINTER;
2292 else
2294 StrCpyW(pszUrl, pszPath);
2295 return S_FALSE;
2298 /* then must need converting to file: format */
2300 /* Strip off leading slashes */
2301 while (*pszPath == '\\' || *pszPath == '/')
2303 pszPath++;
2304 nSlashes++;
2307 dwChRequired = *pcchUrl; /* UrlEscape will fill this in with the correct amount */
2308 ret = UrlEscapeW(pszPath, pszUrl, &dwChRequired, URL_ESCAPE_PERCENT);
2309 if (ret != E_POINTER && FAILED(ret))
2310 return ret;
2311 dwChRequired += 5; /* "file:" */
2312 if ((lstrlenW(pszUrl) > 1) && isalphaW(pszUrl[0]) && (pszUrl[1] == ':'))
2314 dwChRequired += 3; /* "///" */
2315 nSlashes = 3;
2317 else
2318 switch (nSlashes)
2320 case 0: /* no slashes */
2321 break;
2322 case 2: /* two slashes */
2323 case 4:
2324 case 5:
2325 case 6:
2326 dwChRequired += 2;
2327 nSlashes = 2;
2328 break;
2329 default: /* three slashes */
2330 dwChRequired += 3;
2331 nSlashes = 3;
2334 *pcchUrl = dwChRequired; /* Return number of chars required (not including termination) */
2335 if (dwChRequired > *pcchUrl)
2336 return E_POINTER;
2337 pszNewUrl = HeapAlloc(GetProcessHeap(), 0, (dwChRequired + 1) * sizeof(WCHAR));
2338 StrCpyW(pszNewUrl, fileW);
2339 pszPointer = pszNewUrl + 4;
2340 *pszPointer = ':';
2341 pszPointer++;
2342 for (i=0; i < nSlashes; i++)
2344 *pszPointer = '/';
2345 pszPointer++;
2347 StrCpyW(pszPointer, pszPath);
2348 StrCpyW(pszUrl, pszNewUrl);
2349 return S_OK;
2352 /*************************************************************************
2353 * SHAutoComplete [SHLWAPI.@]
2355 * Enable auto-completion for an edit control.
2357 * PARAMS
2358 * hwndEdit [I] Handle of control to enable auto-completion for
2359 * dwFlags [I] SHACF_ flags from "shlwapi.h"
2361 * RETURNS
2362 * Success: S_OK. Auto-completion is enabled for the control.
2363 * Failure: An HRESULT error code indicating the error.
2365 HRESULT WINAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
2367 FIXME("SHAutoComplete stub\n");
2368 return S_FALSE;
2371 /*************************************************************************
2372 * MLBuildResURLA [SHLWAPI.405]
2374 * Create a Url pointing to a resource in a module.
2376 * PARAMS
2377 * lpszLibName [I] Name of the module containing the resource
2378 * hMod [I] Callers module handle
2379 * dwFlags [I] Undocumented flags for loading the module
2380 * lpszRes [I] Resource name
2381 * lpszDest [O] Destination for resulting Url
2382 * dwDestLen [I] Length of lpszDest
2384 * RETURNS
2385 * Success: S_OK. lpszDest constains the resource Url.
2386 * Failure: E_INVALIDARG, if any argument is invalid, or
2387 * E_FAIL if dwDestLen is too small.
2389 HRESULT WINAPI MLBuildResURLA(LPCSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2390 LPCSTR lpszRes, LPSTR lpszDest, DWORD dwDestLen)
2392 WCHAR szLibName[MAX_PATH], szRes[MAX_PATH], szDest[MAX_PATH];
2393 HRESULT hRet;
2395 if (lpszLibName)
2396 MultiByteToWideChar(CP_ACP, 0, lpszLibName, -1, szLibName, sizeof(szLibName)/sizeof(WCHAR));
2398 if (lpszRes)
2399 MultiByteToWideChar(CP_ACP, 0, lpszRes, -1, szRes, sizeof(szRes)/sizeof(WCHAR));
2401 if (dwDestLen > sizeof(szLibName)/sizeof(WCHAR))
2402 dwDestLen = sizeof(szLibName)/sizeof(WCHAR);
2404 hRet = MLBuildResURLW(lpszLibName ? szLibName : NULL, hMod, dwFlags,
2405 lpszRes ? szRes : NULL, lpszDest ? szDest : NULL, dwDestLen);
2406 if (SUCCEEDED(hRet) && lpszDest)
2407 WideCharToMultiByte(CP_ACP, 0, szDest, -1, lpszDest, dwDestLen, 0, 0);
2409 return hRet;
2412 /*************************************************************************
2413 * MLBuildResURLA [SHLWAPI.406]
2415 * See MLBuildResURLA.
2417 HRESULT WINAPI MLBuildResURLW(LPCWSTR lpszLibName, HMODULE hMod, DWORD dwFlags,
2418 LPCWSTR lpszRes, LPWSTR lpszDest, DWORD dwDestLen)
2420 static const WCHAR szRes[] = { 'r','e','s',':','/','/','\0' };
2421 #define szResLen ((sizeof(szRes) - sizeof(WCHAR))/sizeof(WCHAR))
2422 HRESULT hRet = E_FAIL;
2424 TRACE("(%s,%p,0x%08lx,%s,%p,%ld)\n", debugstr_w(lpszLibName), hMod, dwFlags,
2425 debugstr_w(lpszRes), lpszDest, dwDestLen);
2427 if (!lpszLibName || !hMod || hMod == INVALID_HANDLE_VALUE || !lpszRes ||
2428 !lpszDest || (dwFlags && dwFlags != 2))
2429 return E_INVALIDARG;
2431 if (dwDestLen >= szResLen + 1)
2433 dwDestLen -= (szResLen + 1);
2434 memcpy(lpszDest, szRes, sizeof(szRes));
2436 hMod = MLLoadLibraryW(lpszLibName, hMod, dwFlags);
2438 if (hMod)
2440 WCHAR szBuff[MAX_PATH];
2442 if (GetModuleFileNameW(hMod, szBuff, sizeof(szBuff)/sizeof(WCHAR)))
2444 DWORD dwPathLen = strlenW(szBuff) + 1;
2446 if (dwDestLen >= dwPathLen)
2448 DWORD dwResLen;
2450 dwDestLen -= dwPathLen;
2451 memcpy(lpszDest + szResLen, szBuff, dwPathLen * sizeof(WCHAR));
2453 dwResLen = strlenW(lpszRes) + 1;
2454 if (dwDestLen >= dwResLen + 1)
2456 lpszDest[szResLen + dwPathLen + dwResLen] = '/';
2457 memcpy(lpszDest + szResLen + dwPathLen, lpszRes, dwResLen * sizeof(WCHAR));
2458 hRet = S_OK;
2462 MLFreeLibrary(hMod);
2465 return hRet;