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