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

Last change on this file since 21926 was 21926, checked in by dmik, 14 years ago

Make SHAutoComplete available in headers and in debug builds.

Also place it along other similar functions.

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