source: trunk/src/shlwapi/url.c@ 7820

Last change on this file since 7820 was 7820, checked in by sandervl, 24 years ago

Wine resync

File size: 46.3 KB
Line 
1/*
2 * Url functions
3 *
4 * Copyright 2000 Huw D M Davies for CodeWeavers.
5 */
6#ifdef __WIN32OS2__
7#include <ctype.h>
8#include <stdarg.h>
9#include <winuser.h>
10#endif
11
12#include <string.h>
13#include <stdlib.h>
14#include "windef.h"
15#include "winnls.h"
16#include "winbase.h"
17#include "winerror.h"
18#include "wine/unicode.h"
19#include "wininet.h"
20#include "winreg.h"
21#define NO_SHLWAPI_STREAM
22#include "shlwapi.h"
23#include "debugtools.h"
24#include "ordinal.h"
25
26
27DEFAULT_DEBUG_CHANNEL(shell);
28
29typedef struct {
30 LPCWSTR pScheme; /* [out] start of scheme */
31 DWORD szScheme; /* [out] size of scheme (until colon) */
32 LPCWSTR pUserName; /* [out] start of Username */
33 DWORD szUserName; /* [out] size of Username (until ":" or "@") */
34 LPCWSTR pPassword; /* [out] start of Password */
35 DWORD szPassword; /* [out] size of Password (until "@") */
36 LPCWSTR pHostName; /* [out] start of Hostname */
37 DWORD szHostName; /* [out] size of Hostname (until ":" or "/") */
38 LPCWSTR pPort; /* [out] start of Port */
39 DWORD szPort; /* [out] size of Port (until "/" or eos) */
40 LPCWSTR pQuery; /* [out] start of Query */
41 DWORD szQuery; /* [out] size of Query (until eos) */
42} WINE_PARSE_URL;
43
44typedef enum {
45 SCHEME,
46 HOST,
47 PORT,
48 USERPASS,
49} WINE_URL_SCAN_TYPE;
50
51static const WCHAR fileW[] = {'f','i','l','e','\0'};
52
53static const unsigned char HashDataLookup[256] = {
54 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77, 0x8A, 0xAA, 0x7D, 0x76, 0x1B,
55 0xE9, 0x8C, 0x33, 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44, 0x1E, 0x07,
56 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41, 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94,
57 0xDF, 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C, 0x0C, 0xB5, 0x67, 0x46,
58 0x16, 0x3A, 0x4B, 0x4E, 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90, 0xB0,
59 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53, 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6,
60 0x29, 0xFE, 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58, 0x23, 0xCE, 0x5F,
61 0x74, 0xFC, 0xC0, 0x36, 0xDD, 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
62 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D, 0xA6, 0x50, 0x32, 0x22, 0xAF,
63 0xC3, 0x64, 0x63, 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD, 0x79, 0x40,
64 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A, 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9,
65 0xC2, 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B, 0x4A, 0x3B, 0x89, 0xE4,
66 0x6C, 0xBF, 0xE8, 0x8B, 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C, 0xFB,
67 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70, 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB,
68 0x0D, 0x20, 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B, 0xF9, 0xEC, 0x2D,
69 0xF4, 0x6F, 0xB6, 0x99, 0x88, 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
70 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72, 0xA2, 0x35, 0xA0, 0xD7, 0xCD,
71 0xB4, 0x2F, 0x6D, 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34, 0x3F, 0x17,
72 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8, 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB,
73 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 };
74
75static BOOL URL_NeedEscapeA(CHAR ch, DWORD dwFlags)
76{
77
78 if (isalnum(ch))
79 return FALSE;
80
81 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
82 if(ch == ' ')
83 return TRUE;
84 else
85 return FALSE;
86 }
87
88 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == '%'))
89 return TRUE;
90
91 if (ch <= 31 || ch >= 127)
92 return TRUE;
93
94 else {
95 switch (ch) {
96 case ' ':
97 case '<':
98 case '>':
99 case '\"':
100 case '{':
101 case '}':
102 case '|':
103 case '\\':
104 case '^':
105 case ']':
106 case '[':
107 case '`':
108 case '&':
109 return TRUE;
110
111 case '/':
112 case '?':
113 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
114 default:
115 return FALSE;
116 }
117 }
118}
119
120static BOOL URL_NeedEscapeW(WCHAR ch, DWORD dwFlags)
121{
122
123 if (isalnumW(ch))
124 return FALSE;
125
126 if(dwFlags & URL_ESCAPE_SPACES_ONLY) {
127 if(ch == L' ')
128 return TRUE;
129 else
130 return FALSE;
131 }
132
133 if ((dwFlags & URL_ESCAPE_PERCENT) && (ch == L'%'))
134 return TRUE;
135
136 if (ch <= 31 || ch >= 127)
137 return TRUE;
138
139 else {
140 switch (ch) {
141 case L' ':
142 case L'<':
143 case L'>':
144 case L'\"':
145 case L'{':
146 case L'}':
147 case L'|':
148 case L'\\':
149 case L'^':
150 case L']':
151 case L'[':
152 case L'`':
153 case L'&':
154 return TRUE;
155
156 case L'/':
157 case L'?':
158 if (dwFlags & URL_ESCAPE_SEGMENT_ONLY) return TRUE;
159 default:
160 return FALSE;
161 }
162 }
163}
164
165static BOOL URL_JustLocation(LPCWSTR str)
166{
167 while(*str && (*str == L'/')) str++;
168 if (*str) {
169 while (*str && ((*str == L'-') ||
170 (*str == L'.') ||
171 isalnumW(*str))) str++;
172 if (*str == L'/') return FALSE;
173 }
174 return TRUE;
175}
176
177
178/*************************************************************************
179 * UrlCanonicalizeA [SHLWAPI.@]
180 *
181 * Uses the W version to do job.
182 */
183HRESULT WINAPI UrlCanonicalizeA(LPCSTR pszUrl, LPSTR pszCanonicalized,
184 LPDWORD pcchCanonicalized, DWORD dwFlags)
185{
186 LPWSTR base, canonical;
187 DWORD ret, len, len2;
188
189 TRACE("(%s %p %p 0x%08lx) using W version\n",
190 debugstr_a(pszUrl), pszCanonicalized,
191 pcchCanonicalized, dwFlags);
192
193 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
194 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
195 canonical = base + INTERNET_MAX_URL_LENGTH;
196
197 MultiByteToWideChar(0, 0, pszUrl, -1, base, INTERNET_MAX_URL_LENGTH);
198 len = INTERNET_MAX_URL_LENGTH;
199
200 ret = UrlCanonicalizeW(base, canonical, &len, dwFlags);
201 if (ret != S_OK) {
202 HeapFree(GetProcessHeap(), 0, base);
203 return ret;
204 }
205
206 len2 = WideCharToMultiByte(0, 0, canonical, len, 0, 0, 0, 0);
207 if (len2 > *pcchCanonicalized) {
208 *pcchCanonicalized = len;
209 HeapFree(GetProcessHeap(), 0, base);
210 return E_POINTER;
211 }
212 WideCharToMultiByte(0, 0, canonical, len+1, pszCanonicalized,
213 *pcchCanonicalized, 0, 0);
214 *pcchCanonicalized = len2;
215 HeapFree(GetProcessHeap(), 0, base);
216 return S_OK;
217}
218
219/*************************************************************************
220 * UrlCanonicalizeW [SHLWAPI.@]
221 *
222 *
223 * MSDN is wrong (at 10/30/01 - go figure). This should support the
224 * following flags: GLA
225 * URL_DONT_ESCAPE_EXTRA_INFO 0x02000000
226 * URL_ESCAPE_SPACES_ONLY 0x04000000
227 * URL_ESCAPE_PERCENT 0x00001000
228 * URL_ESCAPE_UNSAFE 0x10000000
229 * URL_UNESCAPE 0x10000000
230 * URL_DONT_SIMPLIFY 0x08000000
231 * URL_ESCAPE_SEGMENT_ONLY 0x00002000
232 */
233HRESULT WINAPI UrlCanonicalizeW(LPCWSTR pszUrl, LPWSTR pszCanonicalized,
234 LPDWORD pcchCanonicalized, DWORD dwFlags)
235{
236 HRESULT hr = S_OK;
237 DWORD EscapeFlags;
238 LPWSTR lpszUrlCpy, wk1, wk2, mp, root;
239 INT nLen, nByteLen, state;
240
241 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszCanonicalized,
242 pcchCanonicalized, dwFlags);
243
244 nByteLen = (lstrlenW(pszUrl) + 1) * sizeof(WCHAR); /* length in bytes */
245 lpszUrlCpy = HeapAlloc(GetProcessHeap(), 0, nByteLen);
246
247 if (dwFlags & URL_DONT_SIMPLIFY)
248 memcpy(lpszUrlCpy, pszUrl, nByteLen);
249 else {
250
251 /*
252 * state =
253 * 0 initial 1,3
254 * 1 have 2[+] alnum 2,3
255 * 2 have scheme (found :) 4,6,3
256 * 3 failed (no location)
257 * 4 have // 5,3
258 * 5 have 1[+] alnum 6,3
259 * 6 have location (found /) save root location
260 */
261
262 wk1 = (LPWSTR)pszUrl;
263 wk2 = lpszUrlCpy;
264 state = 0;
265 while (*wk1) {
266 switch (state) {
267 case 0:
268 if (!isalnumW(*wk1)) {state = 3; break;}
269 *wk2++ = *wk1++;
270 if (!isalnumW(*wk1)) {state = 3; break;}
271 *wk2++ = *wk1++;
272 state = 1;
273 break;
274 case 1:
275 *wk2++ = *wk1;
276 if (*wk1++ == L':') state = 2;
277 break;
278 case 2:
279 if (*wk1 != L'/') {state = 3; break;}
280 *wk2++ = *wk1++;
281 if (*wk1 != L'/') {state = 6; break;}
282 *wk2++ = *wk1++;
283 state = 4;
284 break;
285 case 3:
286 strcpyW(wk2, wk1);
287 wk1 += strlenW(wk1);
288 wk2 += strlenW(wk2);
289 break;
290 case 4:
291 if (!isalnumW(*wk1) && (*wk1 != L'-')) {state = 3; break;}
292 while(isalnumW(*wk1) || (*wk1 == L'-')) *wk2++ = *wk1++;
293 state = 5;
294 break;
295 case 5:
296 if (*wk1 != L'/') {state = 3; break;}
297 *wk2++ = *wk1++;
298 state = 6;
299 break;
300 case 6:
301 /* Now at root location, cannot back up any more. */
302 /* "root" will point at the '/' */
303 root = wk2-1;
304 while (*wk1) {
305 TRACE("wk1=%c\n", (CHAR)*wk1);
306 mp = strchrW(wk1, L'/');
307 if (!mp) {
308 strcpyW(wk2, wk1);
309 wk1 += strlenW(wk1);
310 wk2 += strlenW(wk2);
311 continue;
312 }
313 nLen = mp - wk1 + 1;
314 strncpyW(wk2, wk1, nLen);
315 wk2 += nLen;
316 wk1 += nLen;
317 if (*wk1 == L'.') {
318 TRACE("found '/.'\n");
319 if (*(wk1+1) == L'/') {
320 /* case of /./ -> skip the ./ */
321 wk1 += 2;
322 }
323 else if (*(wk1+1) == L'.') {
324 /* found /.. look for next / */
325 TRACE("found '/..'\n");
326 if (*(wk1+2) == L'/') {
327 /* case /../ -> need to backup wk2 */
328 TRACE("found '/../'\n");
329 *(wk2-1) = L'\0'; /* set end of string */
330 mp = strrchrW(root, L'/');
331 if (mp && (mp >= root)) {
332 /* found valid backup point */
333 wk2 = mp + 1;
334 wk1 += 3;
335 }
336 else {
337 /* did not find point, restore '/' */
338 *(wk2-1) = L'/';
339 }
340 }
341 }
342 }
343 }
344 *wk2 = L'\0';
345 break;
346 default:
347 FIXME("how did we get here - state=%d\n", state);
348 return E_INVALIDARG;
349 }
350 }
351 *wk2 = L'\0';
352 TRACE("Simplified, orig <%s>, simple <%s>\n",
353 debugstr_w(pszUrl), debugstr_w(lpszUrlCpy));
354 }
355
356 if(dwFlags & URL_UNESCAPE)
357 UrlUnescapeW(lpszUrlCpy, NULL, NULL, URL_UNESCAPE_INPLACE);
358
359 if((EscapeFlags = dwFlags & (URL_ESCAPE_UNSAFE |
360 URL_ESCAPE_SPACES_ONLY |
361 URL_ESCAPE_PERCENT |
362 URL_DONT_ESCAPE_EXTRA_INFO |
363 URL_ESCAPE_SEGMENT_ONLY ))) {
364 EscapeFlags &= ~URL_ESCAPE_UNSAFE;
365 hr = UrlEscapeW(lpszUrlCpy, pszCanonicalized, pcchCanonicalized,
366 EscapeFlags);
367 } else { /* No escaping needed, just copy the string */
368 nLen = lstrlenW(lpszUrlCpy);
369 if(nLen < *pcchCanonicalized)
370 memcpy(pszCanonicalized, lpszUrlCpy, (nLen + 1)*sizeof(WCHAR));
371 else {
372 hr = E_POINTER;
373 nLen++;
374 }
375 *pcchCanonicalized = nLen;
376 }
377
378 HeapFree(GetProcessHeap(), 0, lpszUrlCpy);
379
380 if (hr == S_OK)
381 TRACE("result %s\n", debugstr_w(pszCanonicalized));
382
383 return hr;
384}
385
386/*************************************************************************
387 * UrlCombineA [SHLWAPI.@]
388 *
389 * Uses the W version to do job.
390 */
391HRESULT WINAPI UrlCombineA(LPCSTR pszBase, LPCSTR pszRelative,
392 LPSTR pszCombined, LPDWORD pcchCombined,
393 DWORD dwFlags)
394{
395 LPWSTR base, relative, combined;
396 DWORD ret, len, len2;
397
398 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx) using W version\n",
399 debugstr_a(pszBase),debugstr_a(pszRelative),
400 *pcchCombined,dwFlags);
401
402 base = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
403 (3*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
404 relative = base + INTERNET_MAX_URL_LENGTH;
405 combined = relative + INTERNET_MAX_URL_LENGTH;
406
407 MultiByteToWideChar(0, 0, pszBase, -1, base, INTERNET_MAX_URL_LENGTH);
408 MultiByteToWideChar(0, 0, pszRelative, -1, relative, INTERNET_MAX_URL_LENGTH);
409 len = INTERNET_MAX_URL_LENGTH;
410
411 ret = UrlCombineW(base, relative, combined, &len, dwFlags);
412 if (ret != S_OK) {
413 HeapFree(GetProcessHeap(), 0, base);
414 return ret;
415 }
416
417 len2 = WideCharToMultiByte(0, 0, combined, len, 0, 0, 0, 0);
418 if (len2 > *pcchCombined) {
419 *pcchCombined = len2;
420 HeapFree(GetProcessHeap(), 0, base);
421 return E_POINTER;
422 }
423 WideCharToMultiByte(0, 0, combined, len+1, pszCombined, *pcchCombined,
424 0, 0);
425 *pcchCombined = len2;
426 HeapFree(GetProcessHeap(), 0, base);
427 return S_OK;
428}
429
430/*************************************************************************
431 * UrlCombineW [SHLWAPI.@]
432 */
433HRESULT WINAPI UrlCombineW(LPCWSTR pszBase, LPCWSTR pszRelative,
434 LPWSTR pszCombined, LPDWORD pcchCombined,
435 DWORD dwFlags)
436{
437 UNKNOWN_SHLWAPI_2 base, relative;
438 DWORD myflags, sizeloc = 0;
439 DWORD len, res1, res2, process_case = 0;
440 LPWSTR work, preliminary, mbase, mrelative;
441 WCHAR myfilestr[] = {'f','i','l','e',':','/','/','/','\0'};
442 WCHAR single_slash[] = {'/','\0'};
443 HRESULT ret;
444
445 TRACE("(base %s, Relative %s, Combine size %ld, flags %08lx)\n",
446 debugstr_w(pszBase),debugstr_w(pszRelative),
447 *pcchCombined,dwFlags);
448
449 base.size = 24;
450 relative.size = 24;
451
452 /* Get space for duplicates of the input and the output */
453 preliminary = HeapAlloc(GetProcessHeap(), 0, (3*INTERNET_MAX_URL_LENGTH) *
454 sizeof(WCHAR));
455 mbase = preliminary + INTERNET_MAX_URL_LENGTH;
456 mrelative = mbase + INTERNET_MAX_URL_LENGTH;
457 *preliminary = L'\0';
458
459 /* Canonicalize the base input prior to looking for the scheme */
460 myflags = dwFlags & (URL_DONT_SIMPLIFY | URL_UNESCAPE);
461 len = INTERNET_MAX_URL_LENGTH;
462 ret = UrlCanonicalizeW(pszBase, mbase, &len, myflags);
463
464 /* Canonicalize the relative input prior to looking for the scheme */
465 len = INTERNET_MAX_URL_LENGTH;
466 ret = UrlCanonicalizeW(pszRelative, mrelative, &len, myflags);
467
468 /* See if the base has a scheme */
469 res1 = SHLWAPI_2(mbase, &base);
470 if (res1) {
471 /* if pszBase has no scheme, then return pszRelative */
472 TRACE("no scheme detected in Base\n");
473 process_case = 1;
474 }
475 else do {
476
477 /* get size of location field (if it exists) */
478 work = (LPWSTR)base.ap2;
479 sizeloc = 0;
480 if (*work++ == L'/') {
481 if (*work++ == L'/') {
482 /* At this point have start of location and
483 * it ends at next '/' or end of string.
484 */
485 while(*work && (*work != L'/')) work++;
486 sizeloc = work - base.ap2;
487 }
488 }
489
490 /* Change .sizep2 to not have the last leaf in it,
491 * Note: we need to start after the location (if it exists)
492 */
493 work = strrchrW((base.ap2+sizeloc), L'/');
494 if (work) {
495 len = work - base.ap2 + 1;
496 base.sizep2 = len;
497 }
498 /*
499 * At this point:
500 * .ap2 points to location (starting with '//')
501 * .sizep2 length of location (above) and rest less the last
502 * leaf (if any)
503 * sizeloc length of location (above) up to but not including
504 * the last '/'
505 */
506
507 res2 = SHLWAPI_2(mrelative, &relative);
508 if (res2) {
509 /* no scheme in pszRelative */
510 TRACE("no scheme detected in Relative\n");
511 relative.ap2 = mrelative; /* case 3,4,5 depends on this */
512 relative.sizep2 = strlenW(mrelative);
513 if (*pszRelative == L':') {
514 /* case that is either left alone or uses pszBase */
515 if (dwFlags & URL_PLUGGABLE_PROTOCOL) {
516 process_case = 5;
517 break;
518 }
519 process_case = 1;
520 break;
521 }
522 if (isalnum(*mrelative) && (*(mrelative + 1) == L':')) {
523 /* case that becomes "file:///" */
524 strcpyW(preliminary, myfilestr);
525 process_case = 1;
526 break;
527 }
528 if ((*mrelative == L'/') && (*(mrelative+1) == L'/')) {
529 /* pszRelative has location and rest */
530 process_case = 3;
531 break;
532 }
533 if (*mrelative == L'/') {
534 /* case where pszRelative is root to location */
535 process_case = 4;
536 break;
537 }
538 process_case = (*base.ap2 == L'/') ? 5 : 3;
539 break;
540 }
541
542 /* handle cases where pszRelative has scheme */
543 if ((base.sizep1 == relative.sizep1) &&
544 (strncmpW(base.ap1, relative.ap1, base.sizep1) == 0)) {
545
546 /* since the schemes are the same */
547 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
548 /* case where pszRelative replaces location and following */
549 process_case = 3;
550 break;
551 }
552 if (*relative.ap2 == L'/') {
553 /* case where pszRelative is root to location */
554 process_case = 4;
555 break;
556 }
557 /* case where scheme is followed by document path */
558 process_case = 5;
559 break;
560 }
561 if ((*relative.ap2 == L'/') && (*(relative.ap2+1) == L'/')) {
562 /* case where pszRelative replaces scheme, location,
563 * and following and handles PLUGGABLE
564 */
565 process_case = 2;
566 break;
567 }
568 process_case = 1;
569 break;
570 } while(FALSE); /* a litte trick to allow easy exit from nested if's */
571
572
573 ret = S_OK;
574 switch (process_case) {
575
576 case 1: /*
577 * Return pszRelative appended to what ever is in pszCombined,
578 * (which may the string "file:///"
579 */
580 len = strlenW(mrelative) + strlenW(preliminary);
581 if (len+1 > *pcchCombined) {
582 *pcchCombined = len;
583 ret = E_POINTER;
584 break;
585 }
586 strcatW(preliminary, mrelative);
587 break;
588
589 case 2: /*
590 * Same as case 1, but if URL_PLUGGABLE_PROTOCOL was specified
591 * and pszRelative starts with "//", then append a "/"
592 */
593 len = strlenW(mrelative) + 1;
594 if (len+1 > *pcchCombined) {
595 *pcchCombined = len;
596 ret = E_POINTER;
597 break;
598 }
599 strcpyW(preliminary, mrelative);
600 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
601 URL_JustLocation(relative.ap2))
602 strcatW(preliminary, single_slash);
603 break;
604
605 case 3: /*
606 * Return the pszBase scheme with pszRelative. Basicly
607 * keeps the scheme and replaces the domain and following.
608 */
609 len = base.sizep1 + 1 + relative.sizep2 + 1;
610 if (len+1 > *pcchCombined) {
611 *pcchCombined = len;
612 ret = E_POINTER;
613 break;
614 }
615 strncpyW(preliminary, base.ap1, base.sizep1 + 1);
616 work = preliminary + base.sizep1 + 1;
617 strcpyW(work, relative.ap2);
618 if (!(dwFlags & URL_PLUGGABLE_PROTOCOL) &&
619 URL_JustLocation(relative.ap2))
620 strcatW(work, single_slash);
621 break;
622
623 case 4: /*
624 * Return the pszBase scheme and location but everything
625 * after the location is pszRelative. (Replace document
626 * from root on.)
627 */
628 len = base.sizep1 + 1 + sizeloc + relative.sizep2 + 1;
629 if (len+1 > *pcchCombined) {
630 *pcchCombined = len;
631 ret = E_POINTER;
632 break;
633 }
634 strncpyW(preliminary, base.ap1, base.sizep1+1+sizeloc);
635 work = preliminary + base.sizep1 + 1 + sizeloc;
636 if (dwFlags & URL_PLUGGABLE_PROTOCOL)
637 *(work++) = L'/';
638 strcpyW(work, relative.ap2);
639 break;
640
641 case 5: /*
642 * Return the pszBase without its document (if any) and
643 * append pszRelative after its scheme.
644 */
645 len = base.sizep1 + 1 + base.sizep2 + relative.sizep2;
646 if (len+1 > *pcchCombined) {
647 *pcchCombined = len;
648 ret = E_POINTER;
649 break;
650 }
651 strncpyW(preliminary, base.ap1, base.sizep1+1+base.sizep2);
652 work = preliminary + base.sizep1+1+base.sizep2 - 1;
653 if (*work++ != L'/')
654 *(work++) = L'/';
655 strcpyW(work, relative.ap2);
656 break;
657
658 default:
659 FIXME("How did we get here????? process_case=%ld\n", process_case);
660 ret = E_INVALIDARG;
661 }
662
663 if (ret == S_OK) {
664 /*
665 * Now that the combining is done, process the escape options if
666 * necessary, otherwise just copy the string.
667 */
668 myflags = dwFlags & (URL_ESCAPE_PERCENT |
669 URL_ESCAPE_SPACES_ONLY |
670 URL_DONT_ESCAPE_EXTRA_INFO |
671 URL_ESCAPE_SEGMENT_ONLY);
672 if (myflags)
673 ret = UrlEscapeW(preliminary, pszCombined,
674 pcchCombined, myflags);
675 else {
676 len = (strlenW(preliminary) + 1) * sizeof(WCHAR);
677 memcpy(pszCombined, preliminary, len);
678 *pcchCombined = strlenW(preliminary);
679 }
680 TRACE("return-%ld len=%ld, %s\n",
681 process_case, *pcchCombined, debugstr_w(pszCombined));
682 }
683 HeapFree(GetProcessHeap(), 0, preliminary);
684 return ret;
685}
686
687/*************************************************************************
688 * UrlEscapeA [SHLWAPI.@]
689 *
690 * Converts unsafe characters into their escape sequences.
691 *
692 * The converted string is returned in pszEscaped if the buffer size
693 * (which should be supplied in pcchEscaped) is large enough, in this
694 * case the function returns S_OK and pcchEscaped contains the length
695 * of the escaped string. If the buffer is not large enough the
696 * function returns E_POINTER and pcchEscaped contains the required
697 * buffer size (including room for the '\0').
698 *
699 * By default the function stops converting at the first '?' or
700 * '#'. [MSDN says differently]. If URL_ESCAPE_SPACE_ONLY flag is set
701 * then only spaces are converted, but the conversion continues past a
702 * '?' or '#'.
703 *
704 * BUGS:
705 * Have now implemented the following flags:
706 * URL_ESCAPE_SPACES_ONLY
707 * URL_DONT_ESCAPE_EXTRA_INFO
708 * URL_ESCAPE_SEGMENT_ONLY
709 * URL_ESCAPE_PERCENT
710 * Initial testing seems to indicate that this is now working like
711 * native shlwapi version 5. Note that these functions did not work
712 * well (or at all) in shlwapi version 4.
713 *
714 */
715HRESULT WINAPI UrlEscapeA(
716 LPCSTR pszUrl,
717 LPSTR pszEscaped,
718 LPDWORD pcchEscaped,
719 DWORD dwFlags)
720{
721 LPCSTR src;
722 DWORD needed = 0, ret;
723 BOOL stop_escaping = FALSE;
724 char next[3], *dst = pszEscaped;
725 char hex[] = "0123456789ABCDEF";
726 INT len;
727
728 TRACE("(%s %p %p 0x%08lx)\n", debugstr_a(pszUrl), pszEscaped,
729 pcchEscaped, dwFlags);
730
731 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
732 URL_ESCAPE_SEGMENT_ONLY |
733 URL_DONT_ESCAPE_EXTRA_INFO |
734 URL_ESCAPE_PERCENT))
735 FIXME("Unimplemented flags: %08lx\n", dwFlags);
736
737 /* fix up flags */
738 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
739 /* if SPACES_ONLY specified, reset the other controls */
740 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
741 URL_ESCAPE_PERCENT |
742 URL_ESCAPE_SEGMENT_ONLY);
743
744 else
745 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
746 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
747
748 for(src = pszUrl; *src; src++) {
749 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
750 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
751 (*src == '#' || *src == '?'))
752 stop_escaping = TRUE;
753
754 if(URL_NeedEscapeA(*src, dwFlags) && stop_escaping == FALSE) {
755 /* TRACE("escaping %c\n", *src); */
756 next[0] = '%';
757 next[1] = hex[(*src >> 4) & 0xf];
758 next[2] = hex[*src & 0xf];
759 len = 3;
760 } else {
761 /* TRACE("passing %c\n", *src); */
762 next[0] = *src;
763 len = 1;
764 }
765
766 if(needed + len <= *pcchEscaped) {
767 memcpy(dst, next, len);
768 dst += len;
769 }
770 needed += len;
771 }
772
773 if(needed < *pcchEscaped) {
774 *dst = '\0';
775 ret = S_OK;
776 } else {
777 needed++; /* add one for the '\0' */
778 ret = E_POINTER;
779 }
780 *pcchEscaped = needed;
781 return ret;
782}
783
784/*************************************************************************
785 * UrlEscapeW [SHLWAPI.@]
786 *
787 * See UrlEscapeA for list of assumptions, bugs, and FIXMEs
788 */
789HRESULT WINAPI UrlEscapeW(
790 LPCWSTR pszUrl,
791 LPWSTR pszEscaped,
792 LPDWORD pcchEscaped,
793 DWORD dwFlags)
794{
795 LPCWSTR src;
796 DWORD needed = 0, ret;
797 BOOL stop_escaping = FALSE;
798 WCHAR next[5], *dst = pszEscaped;
799 CHAR hex[] = "0123456789ABCDEF";
800 INT len;
801
802 TRACE("(%s %p %p 0x%08lx)\n", debugstr_w(pszUrl), pszEscaped,
803 pcchEscaped, dwFlags);
804
805 if(dwFlags & ~(URL_ESCAPE_SPACES_ONLY |
806 URL_ESCAPE_SEGMENT_ONLY |
807 URL_DONT_ESCAPE_EXTRA_INFO |
808 URL_ESCAPE_PERCENT))
809 FIXME("Unimplemented flags: %08lx\n", dwFlags);
810
811 /* fix up flags */
812 if (dwFlags & URL_ESCAPE_SPACES_ONLY)
813 /* if SPACES_ONLY specified, reset the other controls */
814 dwFlags &= ~(URL_DONT_ESCAPE_EXTRA_INFO |
815 URL_ESCAPE_PERCENT |
816 URL_ESCAPE_SEGMENT_ONLY);
817
818 else
819 /* if SPACES_ONLY *not* specified the assume DONT_ESCAPE_EXTRA_INFO */
820 dwFlags |= URL_DONT_ESCAPE_EXTRA_INFO;
821
822 for(src = pszUrl; *src; src++) {
823 /*
824 * if(!(dwFlags & URL_ESCAPE_SPACES_ONLY) &&
825 * (*src == L'#' || *src == L'?'))
826 * stop_escaping = TRUE;
827 */
828 if(!(dwFlags & URL_ESCAPE_SEGMENT_ONLY) &&
829 (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO) &&
830 (*src == L'#' || *src == L'?'))
831 stop_escaping = TRUE;
832
833 if(URL_NeedEscapeW(*src, dwFlags) && stop_escaping == FALSE) {
834 /* TRACE("escaping %c\n", *src); */
835 next[0] = L'%';
836 /*
837 * I would have assumed that the W form would escape
838 * the character with 4 hex digits (or even 8),
839 * however, experiments show that native shlwapi escapes
840 * with only 2 hex digits.
841 * next[1] = hex[(*src >> 12) & 0xf];
842 * next[2] = hex[(*src >> 8) & 0xf];
843 * next[3] = hex[(*src >> 4) & 0xf];
844 * next[4] = hex[*src & 0xf];
845 * len = 5;
846 */
847 next[1] = hex[(*src >> 4) & 0xf];
848 next[2] = hex[*src & 0xf];
849 len = 3;
850 } else {
851 /* TRACE("passing %c\n", *src); */
852 next[0] = *src;
853 len = 1;
854 }
855
856 if(needed + len <= *pcchEscaped) {
857 memcpy(dst, next, len*sizeof(WCHAR));
858 dst += len;
859 }
860 needed += len;
861 }
862
863 if(needed < *pcchEscaped) {
864 *dst = L'\0';
865 ret = S_OK;
866 } else {
867 needed++; /* add one for the '\0' */
868 ret = E_POINTER;
869 }
870 *pcchEscaped = needed;
871 return ret;
872}
873
874
875/*************************************************************************
876 * UrlUnescapeA [SHLWAPI.@]
877 *
878 * Converts escape sequences back to ordinary characters.
879 *
880 * If URL_ESCAPE_INPLACE is set in dwFlags then pszUnescaped and
881 * pcchUnescaped are ignored and the converted string is returned in
882 * pszUrl, otherwise the string is returned in pszUnescaped.
883 * pcchUnescaped should contain the size of pszUnescaped on calling
884 * and will contain the length the the returned string on return if
885 * the buffer is big enough else it will contain the buffer size
886 * required (including room for the '\0'). The function returns S_OK
887 * on success or E_POINTER if the buffer is not large enough. If the
888 * URL_DONT_ESCAPE_EXTRA_INFO flag is set then the conversion stops at
889 * the first occurrence of either '?' or '#'.
890 *
891 */
892HRESULT WINAPI UrlUnescapeA(
893 LPCSTR pszUrl,
894 LPSTR pszUnescaped,
895 LPDWORD pcchUnescaped,
896 DWORD dwFlags)
897{
898 char *dst, next;
899 LPCSTR src;
900 HRESULT ret;
901 DWORD needed;
902 BOOL stop_unescaping = FALSE;
903
904 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_a(pszUrl), pszUnescaped,
905 pcchUnescaped, dwFlags);
906
907 if(dwFlags & URL_UNESCAPE_INPLACE)
908 dst = (char*)pszUrl;
909 else
910 dst = pszUnescaped;
911
912 for(src = pszUrl, needed = 0; *src; src++, needed++) {
913 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
914 (*src == '#' || *src == '?')) {
915 stop_unescaping = TRUE;
916 next = *src;
917 } else if(*src == '%' && isxdigit(*(src + 1)) && isxdigit(*(src + 2))
918 && stop_unescaping == FALSE) {
919 INT ih;
920 char buf[3];
921 memcpy(buf, src + 1, 2);
922 buf[2] = '\0';
923 ih = strtol(buf, NULL, 16);
924 next = (CHAR) ih;
925 src += 2; /* Advance to end of escape */
926 } else
927 next = *src;
928
929 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
930 *dst++ = next;
931 }
932
933 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
934 *dst = '\0';
935 ret = S_OK;
936 } else {
937 needed++; /* add one for the '\0' */
938 ret = E_POINTER;
939 }
940 if(!(dwFlags & URL_UNESCAPE_INPLACE))
941 *pcchUnescaped = needed;
942
943 if (ret == S_OK) {
944 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
945 debugstr_a(pszUrl) : debugstr_a(pszUnescaped));
946 }
947
948 return ret;
949}
950
951/*************************************************************************
952 * UrlUnescapeW [SHLWAPI.@]
953 *
954 * See UrlUnescapeA for list of assumptions, bugs, and FIXMEs
955 */
956HRESULT WINAPI UrlUnescapeW(
957 LPCWSTR pszUrl,
958 LPWSTR pszUnescaped,
959 LPDWORD pcchUnescaped,
960 DWORD dwFlags)
961{
962 WCHAR *dst, next;
963 LPCWSTR src;
964 HRESULT ret;
965 DWORD needed;
966 BOOL stop_unescaping = FALSE;
967
968 TRACE("(%s, %p, %p, 0x%08lx)\n", debugstr_w(pszUrl), pszUnescaped,
969 pcchUnescaped, dwFlags);
970
971 if(dwFlags & URL_UNESCAPE_INPLACE)
972 dst = (WCHAR*)pszUrl;
973 else
974 dst = pszUnescaped;
975
976 for(src = pszUrl, needed = 0; *src; src++, needed++) {
977 if(dwFlags & URL_DONT_UNESCAPE_EXTRA_INFO &&
978 (*src == L'#' || *src == L'?')) {
979 stop_unescaping = TRUE;
980 next = *src;
981 } else if(*src == L'%' && isxdigitW(*(src + 1)) && isxdigitW(*(src + 2))
982 && stop_unescaping == FALSE) {
983 INT ih;
984 WCHAR buf[3];
985 memcpy(buf, src + 1, 2*sizeof(WCHAR));
986 buf[2] = L'\0';
987 ih = StrToIntW(buf);
988 next = (WCHAR) ih;
989 src += 2; /* Advance to end of escape */
990 } else
991 next = *src;
992
993 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped)
994 *dst++ = next;
995 }
996
997 if(dwFlags & URL_UNESCAPE_INPLACE || needed < *pcchUnescaped) {
998 *dst = L'\0';
999 ret = S_OK;
1000 } else {
1001 needed++; /* add one for the '\0' */
1002 ret = E_POINTER;
1003 }
1004 if(!(dwFlags & URL_UNESCAPE_INPLACE))
1005 *pcchUnescaped = needed;
1006
1007 if (ret == S_OK) {
1008 TRACE("result %s\n", (dwFlags & URL_UNESCAPE_INPLACE) ?
1009 debugstr_w(pszUrl) : debugstr_w(pszUnescaped));
1010 }
1011
1012 return ret;
1013}
1014
1015/*************************************************************************
1016 * UrlGetLocationA [SHLWAPI.@]
1017 *
1018 * Bugs/Features:
1019 * MSDN (as of 2001-11-01) says that:
1020 * "The location is the segment of the URL starting with a ?
1021 * or # character."
1022 * Neither V4 nor V5 of shlwapi.dll implement the '?' and always return
1023 * a NULL.
1024 * MSDN further states that:
1025 * "If a file URL has a query string, ther returned string
1026 * the query string."
1027 * In all test cases if the scheme starts with "fi" then a NULL is
1028 * returned. V5 gives the following results:
1029 * NULL file://aa/b/cd#hohoh
1030 * #hohoh http://aa/b/cd#hohoh
1031 * NULL fi://aa/b/cd#hohoh
1032 * #hohoh ff://aa/b/cd#hohoh
1033 */
1034LPCSTR WINAPI UrlGetLocationA(
1035 LPCSTR pszUrl)
1036{
1037 UNKNOWN_SHLWAPI_1 base;
1038 DWORD res1;
1039
1040 base.size = 24;
1041 res1 = SHLWAPI_1(pszUrl, &base);
1042 if (res1) return NULL; /* invalid scheme */
1043
1044 /* if scheme is file: then never return pointer */
1045 if (strncmp(base.ap1, "file", min(4,base.sizep1)) == 0) return NULL;
1046
1047 /* Look for '#' and return its addr */
1048 return strchr(base.ap2, '#');
1049}
1050
1051/*************************************************************************
1052 * UrlGetLocationW [SHLWAPI.@]
1053 *
1054 * See UrlGetLocationA for list of assumptions, bugs, and FIXMEs
1055 */
1056LPCWSTR WINAPI UrlGetLocationW(
1057 LPCWSTR pszUrl)
1058{
1059 UNKNOWN_SHLWAPI_2 base;
1060 DWORD res1;
1061
1062 base.size = 24;
1063 res1 = SHLWAPI_2(pszUrl, &base);
1064 if (res1) return NULL; /* invalid scheme */
1065
1066 /* if scheme is file: then never return pointer */
1067 if (strncmpW(base.ap1, fileW, min(4,base.sizep1)) == 0) return NULL;
1068
1069 /* Look for '#' and return its addr */
1070 return strchrW(base.ap2, L'#');
1071}
1072
1073/*************************************************************************
1074 * UrlCompareA [SHLWAPI.@]
1075 */
1076INT WINAPI UrlCompareA(
1077 LPCSTR pszUrl1,
1078 LPCSTR pszUrl2,
1079 BOOL fIgnoreSlash)
1080{
1081 INT ret, len, len1, len2;
1082
1083 if (!fIgnoreSlash)
1084 return strcmp(pszUrl1, pszUrl2);
1085 len1 = strlen(pszUrl1);
1086 if (pszUrl1[len1-1] == L'/') len1--;
1087 len2 = strlen(pszUrl2);
1088 if (pszUrl2[len2-1] == L'/') len2--;
1089 if (len1 == len2)
1090 return strncmp(pszUrl1, pszUrl2, len1);
1091 len = min(len1, len2);
1092 ret = strncmp(pszUrl1, pszUrl2, len);
1093 if (ret) return ret;
1094 if (len1 > len2) return 1;
1095 return -1;
1096}
1097
1098/*************************************************************************
1099 * UrlCompareW [SHLWAPI.@]
1100 */
1101INT WINAPI UrlCompareW(
1102 LPCWSTR pszUrl1,
1103 LPCWSTR pszUrl2,
1104 BOOL fIgnoreSlash)
1105{
1106 INT ret, len, len1, len2;
1107
1108 if (!fIgnoreSlash)
1109 return strcmpW(pszUrl1, pszUrl2);
1110 len1 = strlenW(pszUrl1);
1111 if (pszUrl1[len1-1] == L'/') len1--;
1112 len2 = strlenW(pszUrl2);
1113 if (pszUrl2[len2-1] == L'/') len2--;
1114 if (len1 == len2)
1115 return strncmpW(pszUrl1, pszUrl2, len1);
1116 len = min(len1, len2);
1117 ret = strncmpW(pszUrl1, pszUrl2, len);
1118 if (ret) return ret;
1119 if (len1 > len2) return 1;
1120 return -1;
1121}
1122
1123/*************************************************************************
1124 * HashData [SHLWAPI.@]
1125 *
1126 * Hash an input block into a variable sized digest.
1127 */
1128BOOL WINAPI HashData(const unsigned char *lpSrc, INT nSrcLen,
1129 unsigned char *lpDest, INT nDestLen)
1130{
1131 INT srcCount = nSrcLen - 1, destCount = nDestLen - 1;
1132
1133 if (IsBadReadPtr(lpSrc, nSrcLen) ||
1134 IsBadWritePtr(lpDest, nDestLen))
1135 return FALSE;
1136
1137 while (destCount >= 0)
1138 {
1139 lpDest[destCount] = (destCount & 0xff);
1140 destCount--;
1141 }
1142
1143 while (srcCount >= 0)
1144 {
1145 destCount = nDestLen - 1;
1146 while (destCount >= 0)
1147 {
1148 lpDest[destCount] = HashDataLookup[lpSrc[srcCount] ^ lpDest[destCount]];
1149 destCount--;
1150 }
1151 srcCount--;
1152 }
1153 return TRUE;
1154}
1155
1156/*************************************************************************
1157 * UrlHashA [SHLWAPI.@]
1158 *
1159 * Hash an ASCII URL.
1160 */
1161HRESULT WINAPI UrlHashA(LPCSTR pszUrl, unsigned char *lpDest, INT nDestLen)
1162{
1163 if (IsBadStringPtrA(pszUrl, -1) || IsBadWritePtr(lpDest, nDestLen))
1164 return E_INVALIDARG;
1165
1166 HashData(pszUrl, strlen(pszUrl), lpDest, nDestLen);
1167 return NOERROR;
1168}
1169
1170/*************************************************************************
1171 * UrlApplySchemeA [SHLWAPI.@]
1172 */
1173HRESULT WINAPI UrlApplySchemeA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1174{
1175 LPWSTR in, out;
1176 DWORD ret, len, len2;
1177
1178 TRACE("(in %s, out size %ld, flags %08lx) using W version\n",
1179 debugstr_a(pszIn), *pcchOut, dwFlags);
1180
1181 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1182 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1183 out = in + INTERNET_MAX_URL_LENGTH;
1184
1185 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1186 len = INTERNET_MAX_URL_LENGTH;
1187
1188 ret = UrlApplySchemeW(in, out, &len, dwFlags);
1189 if ((ret != S_OK) && (ret != S_FALSE)) {
1190 HeapFree(GetProcessHeap(), 0, in);
1191 return ret;
1192 }
1193
1194 len2 = WideCharToMultiByte(0, 0, out, len+1, 0, 0, 0, 0);
1195 if (len2 > *pcchOut) {
1196 *pcchOut = len2;
1197 HeapFree(GetProcessHeap(), 0, in);
1198 return E_POINTER;
1199 }
1200 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1201 *pcchOut = len2;
1202 HeapFree(GetProcessHeap(), 0, in);
1203 return ret;
1204}
1205
1206HRESULT URL_GuessScheme(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1207{
1208 HKEY newkey;
1209 BOOL j;
1210 INT index, i;
1211 DWORD value_len, data_len, dwType;
1212 WCHAR reg_path[MAX_PATH];
1213 WCHAR value[MAX_PATH], data[MAX_PATH];
1214 WCHAR Wxx, Wyy;
1215
1216 MultiByteToWideChar(0, 0,
1217 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes",
1218 -1, reg_path, MAX_PATH);
1219 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1220 index = 0;
1221 while(value_len = data_len = MAX_PATH,
1222 RegEnumValueW(newkey, index, value, &value_len,
1223 0, &dwType, (LPVOID)data, &data_len) == 0) {
1224 TRACE("guess %d %s is %s\n",
1225 index, debugstr_w(value), debugstr_w(data));
1226
1227 j = FALSE;
1228 for(i=0; i<value_len; i++) {
1229 Wxx = pszIn[i];
1230 Wyy = value[i];
1231 /* remember that TRUE is not-equal */
1232 j = ChrCmpIW(Wxx, Wyy);
1233 if (j) break;
1234 }
1235 if ((i == value_len) && !j) {
1236 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1237 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1238 RegCloseKey(newkey);
1239 return E_POINTER;
1240 }
1241 strcpyW(pszOut, data);
1242 strcatW(pszOut, pszIn);
1243 *pcchOut = strlenW(pszOut);
1244 TRACE("matched and set to %s\n", debugstr_w(pszOut));
1245 RegCloseKey(newkey);
1246 return S_OK;
1247 }
1248 index++;
1249 }
1250 RegCloseKey(newkey);
1251 return -1;
1252}
1253
1254HRESULT URL_ApplyDefault(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut)
1255{
1256 HKEY newkey;
1257 DWORD data_len, dwType;
1258 WCHAR reg_path[MAX_PATH];
1259 WCHAR value[MAX_PATH], data[MAX_PATH];
1260
1261 /* get and prepend default */
1262 MultiByteToWideChar(0, 0,
1263 "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix",
1264 -1, reg_path, MAX_PATH);
1265 RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_path, 0, 1, &newkey);
1266 data_len = MAX_PATH;
1267 value[0] = L'@';
1268 value[1] = L'\0';
1269 RegQueryValueExW(newkey, value, 0, &dwType, (LPBYTE)data, &data_len);
1270 RegCloseKey(newkey);
1271 if (strlenW(data) + strlenW(pszIn) + 1 > *pcchOut) {
1272 *pcchOut = strlenW(data) + strlenW(pszIn) + 1;
1273 return E_POINTER;
1274 }
1275 strcpyW(pszOut, data);
1276 strcatW(pszOut, pszIn);
1277 *pcchOut = strlenW(pszOut);
1278 TRACE("used default %s\n", debugstr_w(pszOut));
1279 return S_OK;
1280}
1281
1282/*************************************************************************
1283 * UrlApplySchemeW [SHLWAPI.@]
1284 */
1285HRESULT WINAPI UrlApplySchemeW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags)
1286{
1287 UNKNOWN_SHLWAPI_2 in_scheme;
1288 DWORD res1;
1289 HRESULT ret;
1290
1291 TRACE("(in %s, out size %ld, flags %08lx)\n",
1292 debugstr_w(pszIn), *pcchOut, dwFlags);
1293
1294 if (dwFlags & URL_APPLY_GUESSFILE) {
1295 FIXME("(%s %p %p(%ld) 0x%08lx): stub URL_APPLY_GUESSFILE not implemented\n",
1296 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwFlags);
1297 strcpyW(pszOut, pszIn);
1298 *pcchOut = strlenW(pszOut);
1299 return S_FALSE;
1300 }
1301
1302 in_scheme.size = 24;
1303 /* See if the base has a scheme */
1304 res1 = SHLWAPI_2(pszIn, &in_scheme);
1305 if (res1) {
1306 /* no scheme in input, need to see if we need to guess */
1307 if (dwFlags & URL_APPLY_GUESSSCHEME) {
1308 if ((ret = URL_GuessScheme(pszIn, pszOut, pcchOut)) != -1)
1309 return ret;
1310 }
1311 }
1312 else {
1313 /* we have a scheme, see if valid (known scheme) */
1314 if (in_scheme.fcncde) {
1315 /* have valid scheme, so just copy and exit */
1316 if (strlenW(pszIn) + 1 > *pcchOut) {
1317 *pcchOut = strlenW(pszIn) + 1;
1318 return E_POINTER;
1319 }
1320 strcpyW(pszOut, pszIn);
1321 *pcchOut = strlenW(pszOut);
1322 TRACE("valid scheme, returing copy\n");
1323 return S_OK;
1324 }
1325 }
1326
1327 /* If we are here, then either invalid scheme,
1328 * or no scheme and can't/failed guess.
1329 */
1330 if ( ( ((res1 == 0) && (dwFlags & URL_APPLY_FORCEAPPLY)) ||
1331 ((res1 != 0)) ) &&
1332 (dwFlags & URL_APPLY_DEFAULT)) {
1333 /* find and apply default scheme */
1334 return URL_ApplyDefault(pszIn, pszOut, pcchOut);
1335 }
1336
1337 /* just copy and give proper return code */
1338 if (strlenW(pszIn) + 1 > *pcchOut) {
1339 *pcchOut = strlenW(pszIn) + 1;
1340 return E_POINTER;
1341 }
1342 strcpyW(pszOut, pszIn);
1343 *pcchOut = strlenW(pszOut);
1344 TRACE("returing copy, left alone\n");
1345 return S_FALSE;
1346}
1347
1348/*************************************************************************
1349 * UrlIsA [SHLWAPI.@]
1350 */
1351BOOL WINAPI UrlIsA(LPCSTR pszUrl, URLIS Urlis)
1352{
1353 UNKNOWN_SHLWAPI_1 base;
1354 DWORD res1;
1355
1356 switch (Urlis) {
1357
1358 case URLIS_OPAQUE:
1359 base.size = 24;
1360 res1 = SHLWAPI_1(pszUrl, &base);
1361 if (res1) return FALSE; /* invalid scheme */
1362 if ((*base.ap2 == '/') && (*(base.ap2+1) == '/'))
1363 /* has scheme followed by 2 '/' */
1364 return FALSE;
1365 return TRUE;
1366
1367 case URLIS_URL:
1368 case URLIS_NOHISTORY:
1369 case URLIS_FILEURL:
1370 case URLIS_APPLIABLE:
1371 case URLIS_DIRECTORY:
1372 case URLIS_HASQUERY:
1373 default:
1374 FIXME("(%s %d): stub\n", debugstr_a(pszUrl), Urlis);
1375 }
1376 return FALSE;
1377}
1378
1379/*************************************************************************
1380 * UrlIsW [SHLWAPI.@]
1381 */
1382BOOL WINAPI UrlIsW(LPCWSTR pszUrl, URLIS Urlis)
1383{
1384 UNKNOWN_SHLWAPI_2 base;
1385 DWORD res1;
1386
1387 switch (Urlis) {
1388
1389 case URLIS_OPAQUE:
1390 base.size = 24;
1391 res1 = SHLWAPI_2(pszUrl, &base);
1392 if (res1) return FALSE; /* invalid scheme */
1393 if ((*base.ap2 == L'/') && (*(base.ap2+1) == L'/'))
1394 /* has scheme followed by 2 '/' */
1395 return FALSE;
1396 return TRUE;
1397
1398 case URLIS_URL:
1399 case URLIS_NOHISTORY:
1400 case URLIS_FILEURL:
1401 case URLIS_APPLIABLE:
1402 case URLIS_DIRECTORY:
1403 case URLIS_HASQUERY:
1404 default:
1405 FIXME("(%s %d): stub\n", debugstr_w(pszUrl), Urlis);
1406 }
1407 return FALSE;
1408}
1409
1410/*************************************************************************
1411 * UrlIsNoHistoryA [SHLWAPI.@]
1412 */
1413BOOL WINAPI UrlIsNoHistoryA(LPCSTR pszUrl)
1414{
1415 return UrlIsA(pszUrl, URLIS_NOHISTORY);
1416}
1417
1418/*************************************************************************
1419 * UrlIsNoHistoryW [SHLWAPI.@]
1420 */
1421BOOL WINAPI UrlIsNoHistoryW(LPCWSTR pszUrl)
1422{
1423 return UrlIsW(pszUrl, URLIS_NOHISTORY);
1424}
1425
1426/*************************************************************************
1427 * UrlIsOpaqueA [SHLWAPI.@]
1428 */
1429BOOL WINAPI UrlIsOpaqueA(LPCSTR pszUrl)
1430{
1431 return UrlIsA(pszUrl, URLIS_OPAQUE);
1432}
1433
1434/*************************************************************************
1435 * UrlIsOpaqueW [SHLWAPI.@]
1436 */
1437BOOL WINAPI UrlIsOpaqueW(LPCWSTR pszUrl)
1438{
1439 return UrlIsW(pszUrl, URLIS_OPAQUE);
1440}
1441
1442/*************************************************************************
1443 * Scans for characters of type "type" and when not matching found,
1444 * returns pointer to it and length in size.
1445 *
1446 * Characters tested based on RFC 1738
1447 */
1448LPCWSTR URL_ScanID(LPCWSTR start, LPDWORD size, WINE_URL_SCAN_TYPE type)
1449{
1450 static DWORD alwayszero = 0;
1451 BOOL cont = TRUE;
1452
1453 *size = 0;
1454
1455 switch(type){
1456
1457 case SCHEME:
1458 while (cont) {
1459 if ( (islowerW(*start) && isalphaW(*start)) ||
1460 isdigitW(*start) ||
1461 (*start == L'+') ||
1462 (*start == L'-') ||
1463 (*start == L'.')) {
1464 start++;
1465 (*size)++;
1466 }
1467 else
1468 cont = FALSE;
1469 }
1470 break;
1471
1472 case USERPASS:
1473 while (cont) {
1474 if ( isalphaW(*start) ||
1475 isdigitW(*start) ||
1476 /* user/password only characters */
1477 (*start == L';') ||
1478 (*start == L'?') ||
1479 (*start == L'&') ||
1480 (*start == L'=') ||
1481 /* *extra* characters */
1482 (*start == L'!') ||
1483 (*start == L'*') ||
1484 (*start == L'\'') ||
1485 (*start == L'(') ||
1486 (*start == L')') ||
1487 (*start == L',') ||
1488 /* *safe* characters */
1489 (*start == L'$') ||
1490 (*start == L'_') ||
1491 (*start == L'+') ||
1492 (*start == L'-') ||
1493 (*start == L'.')) {
1494 start++;
1495 (*size)++;
1496 } else if (*start == L'%') {
1497 if (isxdigitW(*(start+1)) &&
1498 isxdigitW(*(start+2))) {
1499 start += 3;
1500 *size += 3;
1501 } else
1502 cont = FALSE;
1503 } else
1504 cont = FALSE;
1505 }
1506 break;
1507
1508 case PORT:
1509 while (cont) {
1510 if (isdigitW(*start)) {
1511 start++;
1512 (*size)++;
1513 }
1514 else
1515 cont = FALSE;
1516 }
1517 break;
1518
1519 case HOST:
1520 while (cont) {
1521 if (isalnumW(*start) ||
1522 (*start == L'-') ||
1523 (*start == L'.') ) {
1524 start++;
1525 (*size)++;
1526 }
1527 else
1528 cont = FALSE;
1529 }
1530 break;
1531 default:
1532 FIXME("unknown type %d\n", type);
1533 return (LPWSTR)&alwayszero;
1534 }
1535 /* TRACE("scanned %ld characters next char %p<%c>\n",
1536 *size, start, *start); */
1537 return start;
1538}
1539
1540/*************************************************************************
1541 * Attempt to parse URL into pieces.
1542 */
1543LONG URL_ParseUrl(LPCWSTR pszUrl, WINE_PARSE_URL *pl)
1544{
1545 LPCWSTR work;
1546
1547 memset(pl, 0, sizeof(WINE_PARSE_URL));
1548 pl->pScheme = pszUrl;
1549 work = URL_ScanID(pl->pScheme, &pl->szScheme, SCHEME);
1550#ifdef __WIN32OS2__
1551 if (!*work || (*work != L':')) goto __ERROR;
1552 work++;
1553 if ((*work != L'/') || (*(work+1) != L'/')) goto __ERROR;
1554#else
1555 if (!*work || (*work != L':')) goto ERROR1;
1556 work++;
1557 if ((*work != L'/') || (*(work+1) != L'/')) goto ERROR;
1558#endif
1559 pl->pUserName = work + 2;
1560 work = URL_ScanID(pl->pUserName, &pl->szUserName, USERPASS);
1561 if (*work == L':' ) {
1562 /* parse password */
1563 work++;
1564 pl->pPassword = work;
1565 work = URL_ScanID(pl->pPassword, &pl->szPassword, USERPASS);
1566 if (*work != L'@') {
1567 /* what we just parsed must be the hostname and port
1568 * so reset pointers and clear then let it parse */
1569 pl->szUserName = pl->szPassword = 0;
1570 work = pl->pUserName - 1;
1571 pl->pUserName = pl->pPassword = 0;
1572 }
1573 } else if (*work == L'@') {
1574 /* no password */
1575 pl->szPassword = 0;
1576 pl->pPassword = 0;
1577 } else if (!*work || (*work == L'/') || (*work == L'.')) {
1578 /* what was parsed was hostname, so reset pointers and let it parse */
1579 pl->szUserName = pl->szPassword = 0;
1580 work = pl->pUserName - 1;
1581 pl->pUserName = pl->pPassword = 0;
1582#ifdef __WIN32OS2__
1583 } else goto __ERROR;
1584#else
1585 } else goto ERROR1;
1586#endif
1587 /* now start parsing hostname or hostnumber */
1588 work++;
1589 pl->pHostName = work;
1590 work = URL_ScanID(pl->pHostName, &pl->szHostName, HOST);
1591 if (*work == L':') {
1592 /* parse port */
1593 work++;
1594 pl->pPort = work;
1595 work = URL_ScanID(pl->pPort, &pl->szPort, PORT);
1596 }
1597 if (*work == L'/') {
1598 /* see if query string */
1599 pl->pQuery = strchrW(work, L'?');
1600 if (pl->pQuery) pl->szQuery = strlenW(pl->pQuery);
1601 }
1602 TRACE("parse successful: scheme=%p(%ld), user=%p(%ld), pass=%p(%ld), host=%p(%ld), port=%p(%ld), query=%p(%ld)\n",
1603 pl->pScheme, pl->szScheme,
1604 pl->pUserName, pl->szUserName,
1605 pl->pPassword, pl->szPassword,
1606 pl->pHostName, pl->szHostName,
1607 pl->pPort, pl->szPort,
1608 pl->pQuery, pl->szQuery);
1609 return S_OK;
1610#ifdef __WIN32OS2__
1611 __ERROR:
1612#else
1613 ERROR:
1614#endif
1615 FIXME("failed to parse %s\n", debugstr_w(pszUrl));
1616 return E_INVALIDARG;
1617}
1618
1619/*************************************************************************
1620 * UrlGetPartA [SHLWAPI.@]
1621 */
1622HRESULT WINAPI UrlGetPartA(LPCSTR pszIn, LPSTR pszOut, LPDWORD pcchOut,
1623 DWORD dwPart, DWORD dwFlags)
1624{
1625 LPWSTR in, out;
1626 DWORD ret, len, len2;
1627
1628 in = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1629 (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
1630 out = in + INTERNET_MAX_URL_LENGTH;
1631
1632 MultiByteToWideChar(0, 0, pszIn, -1, in, INTERNET_MAX_URL_LENGTH);
1633
1634 len = INTERNET_MAX_URL_LENGTH;
1635 ret = UrlGetPartW(in, out, &len, dwPart, dwFlags);
1636
1637 if (ret != S_OK) {
1638 HeapFree(GetProcessHeap(), 0, in);
1639 return ret;
1640 }
1641
1642 len2 = WideCharToMultiByte(0, 0, out, len, 0, 0, 0, 0);
1643 if (len2 > *pcchOut) {
1644 *pcchOut = len2;
1645 HeapFree(GetProcessHeap(), 0, in);
1646 return E_POINTER;
1647 }
1648 WideCharToMultiByte(0, 0, out, len+1, pszOut, *pcchOut, 0, 0);
1649 *pcchOut = len2;
1650 HeapFree(GetProcessHeap(), 0, in);
1651 return S_OK;
1652}
1653
1654/*************************************************************************
1655 * UrlGetPartW [SHLWAPI.@]
1656 */
1657HRESULT WINAPI UrlGetPartW(LPCWSTR pszIn, LPWSTR pszOut, LPDWORD pcchOut,
1658 DWORD dwPart, DWORD dwFlags)
1659{
1660 WINE_PARSE_URL pl;
1661 HRESULT ret;
1662 DWORD size, schsize;
1663 LPCWSTR addr, schaddr;
1664 LPWSTR work;
1665
1666 TRACE("(%s %p %p(%ld) %08lx %08lx)\n",
1667 debugstr_w(pszIn), pszOut, pcchOut, *pcchOut, dwPart, dwFlags);
1668
1669 ret = URL_ParseUrl(pszIn, &pl);
1670 if (!ret) {
1671 schaddr = pl.pScheme;
1672 schsize = pl.szScheme;
1673
1674 switch (dwPart) {
1675 case URL_PART_SCHEME:
1676 if (!pl.szScheme) return E_INVALIDARG;
1677 addr = pl.pScheme;
1678 size = pl.szScheme;
1679 break;
1680
1681 case URL_PART_HOSTNAME:
1682 if (!pl.szHostName) return E_INVALIDARG;
1683 addr = pl.pHostName;
1684 size = pl.szHostName;
1685 break;
1686
1687 case URL_PART_USERNAME:
1688 if (!pl.szUserName) return E_INVALIDARG;
1689 addr = pl.pUserName;
1690 size = pl.szUserName;
1691 break;
1692
1693 case URL_PART_PASSWORD:
1694 if (!pl.szPassword) return E_INVALIDARG;
1695 addr = pl.pPassword;
1696 size = pl.szPassword;
1697 break;
1698
1699 case URL_PART_PORT:
1700 if (!pl.szPort) return E_INVALIDARG;
1701 addr = pl.pPort;
1702 size = pl.szPort;
1703 break;
1704
1705 case URL_PART_QUERY:
1706 if (!pl.szQuery) return E_INVALIDARG;
1707 addr = pl.pQuery;
1708 size = pl.szQuery;
1709 break;
1710
1711 default:
1712 return E_INVALIDARG;
1713 }
1714
1715 if (dwFlags == URL_PARTFLAG_KEEPSCHEME) {
1716 if (*pcchOut < size + schsize + 2) {
1717 *pcchOut = size + schsize + 2;
1718 return E_POINTER;
1719 }
1720 strncpyW(pszOut, schaddr, schsize);
1721 work = pszOut + schsize;
1722 *work = L':';
1723 strncpyW(work+1, addr, size);
1724 *pcchOut = size + schsize + 1;
1725 work += (size + 1);
1726 *work = L'\0';
1727 }
1728 else {
1729 if (*pcchOut < size + 1) {*pcchOut = size+1; return E_POINTER;}
1730 strncpyW(pszOut, addr, size);
1731 *pcchOut = size;
1732 work = pszOut + size;
1733 *work = L'\0';
1734 }
1735 TRACE("len=%ld %s\n", *pcchOut, debugstr_w(pszOut));
1736 }
1737 return ret;
1738}
Note: See TracBrowser for help on using the repository browser.