xref: /reactos/dll/win32/urlmon/sec_mgr.c (revision 8a978a17)
1 /*
2  * Internet Security and Zone Manager
3  *
4  * Copyright (c) 2004 Huw D M Davies
5  * Copyright 2004 Jacek Caban
6  * Copyright 2009 Detlef Riekenberg
7  * Copyright 2011 Thomas Mullaly for CodeWeavers
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include <stdio.h>
25 
26 #include "urlmon_main.h"
27 #include "winreg.h"
28 #include "wininet.h"
29 
30 #define NO_SHLWAPI_REG
31 #include "shlwapi.h"
32 
33 #include "wine/debug.h"
34 
35 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
36 
37 static const WCHAR currentlevelW[] = {'C','u','r','r','e','n','t','L','e','v','e','l',0};
38 static const WCHAR descriptionW[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
39 static const WCHAR displaynameW[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
40 static const WCHAR fileW[] = {'f','i','l','e',0};
41 static const WCHAR flagsW[] = {'F','l','a','g','s',0};
42 static const WCHAR iconW[] = {'I','c','o','n',0};
43 static const WCHAR minlevelW[] = {'M','i','n','L','e','v','e','l',0};
44 static const WCHAR recommendedlevelW[] = {'R','e','c','o','m','m','e','n','d','e','d',
45                                           'L','e','v','e','l',0};
46 static const WCHAR wszZonesKey[] = {'S','o','f','t','w','a','r','e','\\',
47                                     'M','i','c','r','o','s','o','f','t','\\',
48                                     'W','i','n','d','o','w','s','\\',
49                                     'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
50                                     'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
51                                     'Z','o','n','e','s','\\',0};
52 static const WCHAR zone_map_keyW[] = {'S','o','f','t','w','a','r','e','\\',
53                                       'M','i','c','r','o','s','o','f','t','\\',
54                                       'W','i','n','d','o','w','s','\\',
55                                       'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
56                                       'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
57                                       'Z','o','n','e','M','a','p',0};
58 static const WCHAR wszZoneMapDomainsKey[] = {'S','o','f','t','w','a','r','e','\\',
59                                              'M','i','c','r','o','s','o','f','t','\\',
60                                              'W','i','n','d','o','w','s','\\',
61                                              'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
62                                              'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
63                                              'Z','o','n','e','M','a','p','\\',
64                                              'D','o','m','a','i','n','s',0};
65 
66 static inline BOOL is_drive_path(const WCHAR *path)
67 {
68     return iswalpha(*path) && *(path+1) == ':';
69 }
70 
71 /* List of schemes types Windows seems to expect to be hierarchical. */
72 static inline BOOL is_hierarchical_scheme(URL_SCHEME type) {
73     return(type == URL_SCHEME_HTTP || type == URL_SCHEME_FTP ||
74            type == URL_SCHEME_GOPHER || type == URL_SCHEME_NNTP ||
75            type == URL_SCHEME_TELNET || type == URL_SCHEME_WAIS ||
76            type == URL_SCHEME_FILE || type == URL_SCHEME_HTTPS ||
77            type == URL_SCHEME_RES);
78 }
79 
80 /********************************************************************
81  * get_string_from_reg [internal]
82  *
83  * helper to get a string from the reg.
84  *
85  */
86 static void get_string_from_reg(HKEY hcu, HKEY hklm, LPCWSTR name, LPWSTR out, DWORD maxlen)
87 {
88     DWORD type = REG_SZ;
89     DWORD len = maxlen * sizeof(WCHAR);
90     DWORD res;
91 
92     res = RegQueryValueExW(hcu, name, NULL, &type, (LPBYTE) out, &len);
93 
94     if (res && hklm) {
95         len = maxlen * sizeof(WCHAR);
96         type = REG_SZ;
97         res = RegQueryValueExW(hklm, name, NULL, &type, (LPBYTE) out, &len);
98     }
99 
100     if (res) {
101         TRACE("%s failed: %d\n", debugstr_w(name), res);
102         *out = '\0';
103     }
104 }
105 
106 /********************************************************************
107  * get_dword_from_reg [internal]
108  *
109  * helper to get a dword from the reg.
110  *
111  */
112 static void get_dword_from_reg(HKEY hcu, HKEY hklm, LPCWSTR name, LPDWORD out)
113 {
114     DWORD type = REG_DWORD;
115     DWORD len = sizeof(DWORD);
116     DWORD res;
117 
118     res = RegQueryValueExW(hcu, name, NULL, &type, (LPBYTE) out, &len);
119 
120     if (res && hklm) {
121         len = sizeof(DWORD);
122         type = REG_DWORD;
123         res = RegQueryValueExW(hklm, name, NULL, &type, (LPBYTE) out, &len);
124     }
125 
126     if (res) {
127         TRACE("%s failed: %d\n", debugstr_w(name), res);
128         *out = 0;
129     }
130 }
131 
132 static HRESULT get_zone_from_reg(LPCWSTR schema, DWORD *zone)
133 {
134     DWORD res, size;
135     HKEY hkey;
136 
137     static const WCHAR wszZoneMapProtocolKey[] =
138         {'S','o','f','t','w','a','r','e','\\',
139          'M','i','c','r','o','s','o','f','t','\\',
140          'W','i','n','d','o','w','s','\\',
141          'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
142          'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
143          'Z','o','n','e','M','a','p','\\',
144          'P','r','o','t','o','c','o','l','D','e','f','a','u','l','t','s',0};
145 
146     res = RegOpenKeyW(HKEY_CURRENT_USER, wszZoneMapProtocolKey, &hkey);
147     if(res != ERROR_SUCCESS) {
148         ERR("Could not open key %s\n", debugstr_w(wszZoneMapProtocolKey));
149         return E_UNEXPECTED;
150     }
151 
152     size = sizeof(DWORD);
153     res = RegQueryValueExW(hkey, schema, NULL, NULL, (PBYTE)zone, &size);
154     RegCloseKey(hkey);
155     if(res == ERROR_SUCCESS)
156         return S_OK;
157 
158     res = RegOpenKeyW(HKEY_LOCAL_MACHINE, wszZoneMapProtocolKey, &hkey);
159     if(res != ERROR_SUCCESS) {
160         ERR("Could not open key %s\n", debugstr_w(wszZoneMapProtocolKey));
161         return E_UNEXPECTED;
162     }
163 
164     size = sizeof(DWORD);
165     res = RegQueryValueExW(hkey, schema, NULL, NULL, (PBYTE)zone, &size);
166     RegCloseKey(hkey);
167     if(res == ERROR_SUCCESS)
168         return S_OK;
169 
170     *zone = 3;
171     return S_OK;
172 }
173 
174 /********************************************************************
175  * matches_domain_pattern [internal]
176  *
177  * Checks if the given string matches the specified domain pattern.
178  *
179  * This function looks for explicit wildcard domain components iff
180  * they appear at the very beginning of the 'pattern' string
181  *
182  *  pattern = "*.google.com"
183  */
184 static BOOL matches_domain_pattern(LPCWSTR pattern, LPCWSTR str, BOOL implicit_wildcard, LPCWSTR *matched)
185 {
186     BOOL matches = FALSE;
187     DWORD pattern_len = lstrlenW(pattern);
188     DWORD str_len = lstrlenW(str);
189 
190     TRACE("(%d) Checking if %s matches %s\n", implicit_wildcard, debugstr_w(str), debugstr_w(pattern));
191 
192     *matched = NULL;
193     if(str_len >= pattern_len) {
194         /* Check if there's an explicit wildcard in the pattern. */
195         if(pattern[0] == '*' && pattern[1] == '.') {
196             /* Make sure that 'str' matches the wildcard pattern.
197              *
198              * Example:
199              *  pattern = "*.google.com"
200              *
201              * So in this case 'str' would have to end with ".google.com" in order
202              * to map to this pattern.
203              */
204             if(str_len >= pattern_len+1 && !wcsicmp(str+(str_len-pattern_len+1), pattern+1)) {
205                 /* Check if there's another '.' inside of the "unmatched" portion
206                  * of 'str'.
207                  *
208                  * Example:
209                  *  pattern = "*.google.com"
210                  *  str     = "test.testing.google.com"
211                  *
212                  * The currently matched portion is ".google.com" in 'str', we need
213                  * see if there's a '.' inside of the unmatched portion ("test.testing"), because
214                  * if there is and 'implicit_wildcard' isn't set, then this isn't
215                  * a match.
216                  */
217                 const WCHAR *ptr;
218                 for (ptr = str + str_len - pattern_len; ptr > str; ptr--) if (ptr[-1] == '.') break;
219                 if (ptr == str || implicit_wildcard) {
220                     matches = TRUE;
221                     *matched = ptr;
222                 }
223             }
224         } else if(implicit_wildcard && str_len > pattern_len) {
225             /* When the pattern has an implicit wildcard component, it means
226              * that anything goes in 'str' as long as it ends with the pattern
227              * and that the beginning of the match has a '.' before it.
228              *
229              * Example:
230              *  pattern = "google.com"
231              *  str     = "www.google.com"
232              *
233              * Implicitly matches the pattern, where as:
234              *
235              *  pattern = "google.com"
236              *  str     = "wwwgoogle.com"
237              *
238              * Doesn't match the pattern.
239              */
240             if(str[str_len-pattern_len-1] == '.' && !wcsicmp(str+(str_len-pattern_len), pattern)) {
241                 matches = TRUE;
242                 *matched = str+(str_len-pattern_len);
243             }
244         } else {
245             /* The pattern doesn't have an implicit wildcard, or an explicit wildcard,
246              * so 'str' has to be an exact match to the 'pattern'.
247              */
248             if(!wcsicmp(str, pattern)) {
249                 matches = TRUE;
250                 *matched = str;
251             }
252         }
253     }
254 
255     if(matches)
256         TRACE("Found a match: matched=%s\n", debugstr_w(*matched));
257     else
258         TRACE("No match found\n");
259 
260     return matches;
261 }
262 
263 static BOOL get_zone_for_scheme(HKEY key, LPCWSTR schema, DWORD *zone)
264 {
265     static const WCHAR wildcardW[] = {'*',0};
266 
267     DWORD res;
268     DWORD size = sizeof(DWORD);
269     DWORD type;
270 
271     /* See if the key contains a value for the scheme first. */
272     res = RegQueryValueExW(key, schema, NULL, &type, (BYTE*)zone, &size);
273     if(res == ERROR_SUCCESS) {
274         if(type == REG_DWORD)
275             return TRUE;
276         WARN("Unexpected value type %d for value %s, expected REG_DWORD\n", type, debugstr_w(schema));
277     }
278 
279     /* Try to get the zone for the wildcard scheme. */
280     size = sizeof(DWORD);
281     res = RegQueryValueExW(key, wildcardW, NULL, &type, (BYTE*)zone, &size);
282     if(res != ERROR_SUCCESS)
283         return FALSE;
284 
285     if(type != REG_DWORD) {
286         WARN("Unexpected value type %d for value %s, expected REG_DWORD\n", type, debugstr_w(wildcardW));
287         return FALSE;
288     }
289 
290     return TRUE;
291 }
292 
293 /********************************************************************
294  * search_domain_for_zone [internal]
295  *
296  * Searches the specified 'domain' registry key to see if 'host' maps into it, or any
297  * of its subdomain registry keys.
298  *
299  * Returns S_OK if a match is found, S_FALSE if no matches were found, or an error code.
300  */
301 static HRESULT search_domain_for_zone(HKEY domains, LPCWSTR domain, DWORD domain_len, LPCWSTR schema,
302                                       LPCWSTR host, DWORD host_len, DWORD *zone)
303 {
304     BOOL found = FALSE;
305     HKEY domain_key;
306     DWORD res;
307     LPCWSTR matched;
308 
309     if(host_len >= domain_len && matches_domain_pattern(domain, host, TRUE, &matched)) {
310         res = RegOpenKeyW(domains, domain, &domain_key);
311         if(res != ERROR_SUCCESS) {
312             ERR("Failed to open domain key %s: %d\n", debugstr_w(domain), res);
313             return E_UNEXPECTED;
314         }
315 
316         if(matched == host)
317             found = get_zone_for_scheme(domain_key, schema, zone);
318         else {
319             INT domain_offset;
320             DWORD subdomain_count, subdomain_len;
321             BOOL check_domain = TRUE;
322 
323             find_domain_name(domain, domain_len, &domain_offset);
324 
325             res = RegQueryInfoKeyW(domain_key, NULL, NULL, NULL, &subdomain_count, &subdomain_len,
326                                    NULL, NULL, NULL, NULL, NULL, NULL);
327             if(res != ERROR_SUCCESS) {
328                 ERR("Unable to query info for key %s: %d\n", debugstr_w(domain), res);
329                 RegCloseKey(domain_key);
330                 return E_UNEXPECTED;
331             }
332 
333             if(subdomain_count) {
334                 WCHAR *subdomain;
335                 WCHAR *component;
336                 DWORD i;
337 
338                 subdomain = heap_alloc((subdomain_len+1)*sizeof(WCHAR));
339                 if(!subdomain) {
340                     RegCloseKey(domain_key);
341                     return E_OUTOFMEMORY;
342                 }
343 
344                 component = heap_strndupW(host, matched-host-1);
345                 if(!component) {
346                     heap_free(subdomain);
347                     RegCloseKey(domain_key);
348                     return E_OUTOFMEMORY;
349                 }
350 
351                 for(i = 0; i < subdomain_count; ++i) {
352                     DWORD len = subdomain_len+1;
353                     const WCHAR *sub_matched;
354 
355                     res = RegEnumKeyExW(domain_key, i, subdomain, &len, NULL, NULL, NULL, NULL);
356                     if(res != ERROR_SUCCESS) {
357                         heap_free(component);
358                         heap_free(subdomain);
359                         RegCloseKey(domain_key);
360                         return E_UNEXPECTED;
361                     }
362 
363                     if(matches_domain_pattern(subdomain, component, FALSE, &sub_matched)) {
364                         HKEY subdomain_key;
365 
366                         res = RegOpenKeyW(domain_key, subdomain, &subdomain_key);
367                         if(res != ERROR_SUCCESS) {
368                             ERR("Unable to open subdomain key %s of %s: %d\n", debugstr_w(subdomain),
369                                 debugstr_w(domain), res);
370                             heap_free(component);
371                             heap_free(subdomain);
372                             RegCloseKey(domain_key);
373                             return E_UNEXPECTED;
374                         }
375 
376                         found = get_zone_for_scheme(subdomain_key, schema, zone);
377                         check_domain = FALSE;
378                         RegCloseKey(subdomain_key);
379                         break;
380                     }
381                 }
382                 heap_free(subdomain);
383                 heap_free(component);
384             }
385 
386             /* There's a chance that 'host' implicitly mapped into 'domain', in
387              * which case we check to see if 'domain' contains zone information.
388              *
389              * This can only happen if 'domain' is its own domain name.
390              *  Example:
391              *      "google.com" (domain name = "google.com")
392              *
393              *  So if:
394              *      host = "www.google.com"
395              *
396              *  Then host would map directly into the "google.com" domain key.
397              *
398              * If 'domain' has more than just its domain name, or it does not
399              * have a domain name, then we don't perform the check. The reason
400              * for this is that these domains don't allow implicit mappings.
401              *  Example:
402              *      domain = "org" (has no domain name)
403              *      host   = "www.org"
404              *
405              *  The mapping would only happen if the "org" key had an explicit subkey
406              *  called "www".
407              */
408             if(check_domain && !domain_offset && !wcschr(host, matched-host-1))
409                 found = get_zone_for_scheme(domain_key, schema, zone);
410         }
411         RegCloseKey(domain_key);
412     }
413 
414     return found ? S_OK : S_FALSE;
415 }
416 
417 static HRESULT search_for_domain_mapping(HKEY domains, LPCWSTR schema, LPCWSTR host, DWORD host_len, DWORD *zone)
418 {
419     WCHAR *domain;
420     DWORD domain_count, domain_len, i;
421     DWORD res;
422     HRESULT hres = S_FALSE;
423 
424     res = RegQueryInfoKeyW(domains, NULL, NULL, NULL, &domain_count, &domain_len,
425                            NULL, NULL, NULL, NULL, NULL, NULL);
426     if(res != ERROR_SUCCESS) {
427         WARN("Failed to retrieve information about key\n");
428         return E_UNEXPECTED;
429     }
430 
431     if(!domain_count)
432         return S_FALSE;
433 
434     domain = heap_alloc((domain_len+1)*sizeof(WCHAR));
435     if(!domain)
436         return E_OUTOFMEMORY;
437 
438     for(i = 0; i < domain_count; ++i) {
439         DWORD len = domain_len+1;
440 
441         res = RegEnumKeyExW(domains, i, domain, &len, NULL, NULL, NULL, NULL);
442         if(res != ERROR_SUCCESS) {
443             heap_free(domain);
444             return E_UNEXPECTED;
445         }
446 
447         hres = search_domain_for_zone(domains, domain, len, schema, host, host_len, zone);
448         if(FAILED(hres) || hres == S_OK)
449             break;
450     }
451 
452     heap_free(domain);
453     return hres;
454 }
455 
456 static HRESULT get_zone_from_domains(IUri *uri, DWORD *zone)
457 {
458     HRESULT hres;
459     BSTR host, scheme;
460     DWORD res;
461     HKEY domains;
462     DWORD scheme_type;
463 
464     hres = IUri_GetScheme(uri, &scheme_type);
465     if(FAILED(hres))
466         return hres;
467 
468     /* Windows doesn't play nice with unknown scheme types when it tries
469      * to check if a host name maps into any domains.
470      */
471     if(scheme_type == URL_SCHEME_UNKNOWN)
472         return S_FALSE;
473 
474     hres = IUri_GetHost(uri, &host);
475     if(FAILED(hres))
476         return hres;
477 
478     /* Known hierarchical scheme types must have a host. If they don't Windows
479      * assigns URLZONE_INVALID to the zone.
480      */
481     if((scheme_type != URL_SCHEME_UNKNOWN && scheme_type != URL_SCHEME_FILE)
482         && is_hierarchical_scheme(scheme_type) && !*host) {
483         *zone = URLZONE_INVALID;
484 
485         SysFreeString(host);
486 
487         /* The MapUrlToZone functions return S_OK when this condition occurs. */
488         return S_OK;
489     }
490 
491     hres = IUri_GetSchemeName(uri, &scheme);
492     if(FAILED(hres)) {
493         SysFreeString(host);
494         return hres;
495     }
496 
497     /* First try CURRENT_USER. */
498     res = RegOpenKeyW(HKEY_CURRENT_USER, wszZoneMapDomainsKey, &domains);
499     if(res == ERROR_SUCCESS) {
500         hres = search_for_domain_mapping(domains, scheme, host, SysStringLen(host), zone);
501         RegCloseKey(domains);
502     } else
503         WARN("Failed to open HKCU's %s key\n", debugstr_w(wszZoneMapDomainsKey));
504 
505     /* If that doesn't work try LOCAL_MACHINE. */
506     if(hres == S_FALSE) {
507         res = RegOpenKeyW(HKEY_LOCAL_MACHINE, wszZoneMapDomainsKey, &domains);
508         if(res == ERROR_SUCCESS) {
509             hres = search_for_domain_mapping(domains, scheme, host, SysStringLen(host), zone);
510             RegCloseKey(domains);
511         } else
512             WARN("Failed to open HKLM's %s key\n", debugstr_w(wszZoneMapDomainsKey));
513     }
514 
515     SysFreeString(host);
516     SysFreeString(scheme);
517     return hres;
518 }
519 
520 static HRESULT map_security_uri_to_zone(IUri *uri, DWORD *zone)
521 {
522     HRESULT hres;
523     BSTR scheme;
524 
525     *zone = URLZONE_INVALID;
526 
527     hres = IUri_GetSchemeName(uri, &scheme);
528     if(FAILED(hres))
529         return hres;
530 
531     if(!wcsicmp(scheme, fileW)) {
532         BSTR path;
533         WCHAR *ptr, *path_start, root[20];
534 
535         hres = IUri_GetPath(uri, &path);
536         if(FAILED(hres)) {
537             SysFreeString(scheme);
538             return hres;
539         }
540 
541         if(*path == '/' && is_drive_path(path+1))
542             path_start = path+1;
543         else
544             path_start = path;
545 
546         if((ptr = wcschr(path_start, ':')) && ptr-path_start+1 < ARRAY_SIZE(root)) {
547             UINT type;
548 
549             memcpy(root, path_start, (ptr-path_start+1)*sizeof(WCHAR));
550             root[ptr-path_start+1] = 0;
551 
552             type = GetDriveTypeW(root);
553 
554             switch(type) {
555             case DRIVE_UNKNOWN:
556             case DRIVE_NO_ROOT_DIR:
557                 break;
558             case DRIVE_REMOVABLE:
559             case DRIVE_FIXED:
560             case DRIVE_CDROM:
561             case DRIVE_RAMDISK:
562                 *zone = URLZONE_LOCAL_MACHINE;
563                 hres = S_OK;
564                 break;
565             case DRIVE_REMOTE:
566                 *zone = URLZONE_INTERNET;
567                 hres = S_OK;
568                 break;
569             default:
570                 FIXME("unsupported drive type %d\n", type);
571             }
572         }
573         SysFreeString(path);
574     }
575 
576     if(*zone == URLZONE_INVALID) {
577         hres = get_zone_from_domains(uri, zone);
578         if(hres == S_FALSE)
579             hres = get_zone_from_reg(scheme, zone);
580     }
581 
582     SysFreeString(scheme);
583     return hres;
584 }
585 
586 static HRESULT map_url_to_zone(LPCWSTR url, DWORD *zone, LPWSTR *ret_url)
587 {
588     IUri *secur_uri;
589     LPWSTR secur_url;
590     HRESULT hres;
591 
592     *zone = URLZONE_INVALID;
593 
594     hres = CoInternetGetSecurityUrl(url, &secur_url, PSU_SECURITY_URL_ONLY, 0);
595     if(hres != S_OK) {
596         DWORD size = lstrlenW(url)*sizeof(WCHAR);
597 
598         secur_url = CoTaskMemAlloc(size);
599         if(!secur_url)
600             return E_OUTOFMEMORY;
601 
602         memcpy(secur_url, url, size);
603     }
604 
605     hres = CreateUri(secur_url, Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME, 0, &secur_uri);
606     if(FAILED(hres)) {
607         CoTaskMemFree(secur_url);
608         return hres;
609     }
610 
611     hres = map_security_uri_to_zone(secur_uri, zone);
612     IUri_Release(secur_uri);
613 
614     if(FAILED(hres) || !ret_url)
615         CoTaskMemFree(secur_url);
616     else
617         *ret_url = secur_url;
618 
619     return hres;
620 }
621 
622 static HRESULT map_uri_to_zone(IUri *uri, DWORD *zone, IUri **ret_uri)
623 {
624     HRESULT hres;
625     IUri *secur_uri;
626 
627     hres = CoInternetGetSecurityUrlEx(uri, &secur_uri, PSU_SECURITY_URL_ONLY, 0);
628     if(FAILED(hres))
629         return hres;
630 
631     hres = map_security_uri_to_zone(secur_uri, zone);
632     if(FAILED(hres) || !ret_uri)
633         IUri_Release(secur_uri);
634     else
635         *ret_uri = secur_uri;
636 
637     return hres;
638 }
639 
640 static HRESULT open_zone_key(HKEY parent_key, DWORD zone, HKEY *hkey)
641 {
642     static const WCHAR wszFormat[] = {'%','s','%','u',0};
643 
644     WCHAR key_name[ARRAY_SIZE(wszZonesKey) + 12];
645     DWORD res;
646 
647     wsprintfW(key_name, wszFormat, wszZonesKey, zone);
648 
649     res = RegOpenKeyW(parent_key, key_name, hkey);
650 
651     if(res != ERROR_SUCCESS) {
652         WARN("RegOpenKey failed\n");
653         return E_INVALIDARG;
654     }
655 
656     return S_OK;
657 }
658 
659 static HRESULT get_action_policy(DWORD zone, DWORD action, BYTE *policy, DWORD size, URLZONEREG zone_reg)
660 {
661     HKEY parent_key;
662     HKEY hkey;
663     LONG res;
664     HRESULT hres;
665 
666     switch(action) {
667     case URLACTION_SCRIPT_OVERRIDE_SAFETY:
668     case URLACTION_ACTIVEX_OVERRIDE_SCRIPT_SAFETY:
669         *(DWORD*)policy = URLPOLICY_DISALLOW;
670         return S_OK;
671     }
672 
673     switch(zone_reg) {
674     case URLZONEREG_DEFAULT:
675     case URLZONEREG_HKCU:
676         parent_key = HKEY_CURRENT_USER;
677         break;
678     case URLZONEREG_HKLM:
679         parent_key = HKEY_LOCAL_MACHINE;
680         break;
681     default:
682         WARN("Unknown URLZONEREG: %d\n", zone_reg);
683         return E_FAIL;
684     };
685 
686     hres = open_zone_key(parent_key, zone, &hkey);
687     if(SUCCEEDED(hres)) {
688         WCHAR action_str[16];
689         DWORD len = size;
690 
691         static const WCHAR formatW[] = {'%','X',0};
692 
693         wsprintfW(action_str, formatW, action);
694 
695         res = RegQueryValueExW(hkey, action_str, NULL, NULL, policy, &len);
696         if(res == ERROR_MORE_DATA) {
697             hres = E_INVALIDARG;
698         }else if(res == ERROR_FILE_NOT_FOUND) {
699             hres = E_FAIL;
700         }else if(res != ERROR_SUCCESS) {
701             ERR("RegQueryValue failed: %d\n", res);
702             hres = E_UNEXPECTED;
703         }
704 
705         RegCloseKey(hkey);
706     }
707 
708     if(FAILED(hres) && zone_reg == URLZONEREG_DEFAULT)
709         return get_action_policy(zone, action, policy, size, URLZONEREG_HKLM);
710 
711     return hres;
712 }
713 
714 static HRESULT generate_security_id(IUri *uri, BYTE *secid, DWORD *secid_len, DWORD zone)
715 {
716     DWORD len;
717     HRESULT hres;
718     DWORD scheme_type;
719 
720     if(zone == URLZONE_INVALID)
721         return E_INVALIDARG;
722 
723     hres = IUri_GetScheme(uri, &scheme_type);
724     if(FAILED(hres))
725         return hres;
726 
727     /* Windows handles opaque URLs differently then hierarchical ones. */
728     if(!is_hierarchical_scheme(scheme_type) && scheme_type != URL_SCHEME_WILDCARD) {
729         BSTR display_uri;
730 
731         hres = IUri_GetDisplayUri(uri, &display_uri);
732         if(FAILED(hres))
733             return hres;
734 
735         len = WideCharToMultiByte(CP_ACP, 0, display_uri, -1, NULL, 0, NULL, NULL)-1;
736 
737         if(len+sizeof(DWORD) > *secid_len) {
738             SysFreeString(display_uri);
739             return E_NOT_SUFFICIENT_BUFFER;
740         }
741 
742         WideCharToMultiByte(CP_ACP, 0, display_uri, -1, (LPSTR)secid, len, NULL, NULL);
743         SysFreeString(display_uri);
744 
745         *(DWORD*)(secid+len) = zone;
746     } else {
747         BSTR host, scheme;
748         DWORD host_len, scheme_len;
749         BYTE *ptr;
750 
751         hres = IUri_GetHost(uri, &host);
752         if(FAILED(hres))
753             return hres;
754 
755         /* The host can't be empty for Wildcard URIs. */
756         if(scheme_type == URL_SCHEME_WILDCARD && !*host) {
757             SysFreeString(host);
758             return E_INVALIDARG;
759         }
760 
761         hres = IUri_GetSchemeName(uri, &scheme);
762         if(FAILED(hres)) {
763             SysFreeString(host);
764             return hres;
765         }
766 
767         host_len = WideCharToMultiByte(CP_ACP, 0, host, -1, NULL, 0, NULL, NULL)-1;
768         scheme_len = WideCharToMultiByte(CP_ACP, 0, scheme, -1, NULL, 0, NULL, NULL)-1;
769 
770         len = host_len+scheme_len+sizeof(BYTE);
771 
772         if(len+sizeof(DWORD) > *secid_len) {
773             SysFreeString(host);
774             SysFreeString(scheme);
775             return E_NOT_SUFFICIENT_BUFFER;
776         }
777 
778         WideCharToMultiByte(CP_ACP, 0, scheme, -1, (LPSTR)secid, len, NULL, NULL);
779         SysFreeString(scheme);
780 
781         ptr = secid+scheme_len;
782         *ptr++ = ':';
783 
784         WideCharToMultiByte(CP_ACP, 0, host, -1, (LPSTR)ptr, host_len, NULL, NULL);
785         SysFreeString(host);
786 
787         ptr += host_len;
788 
789         *(DWORD*)ptr = zone;
790     }
791 
792     *secid_len = len+sizeof(DWORD);
793 
794     return S_OK;
795 }
796 
797 static HRESULT get_security_id_for_url(LPCWSTR url, BYTE *secid, DWORD *secid_len)
798 {
799     HRESULT hres;
800     DWORD zone = URLZONE_INVALID;
801     LPWSTR secur_url = NULL;
802     IUri *uri;
803 
804     hres = map_url_to_zone(url, &zone, &secur_url);
805     if(FAILED(hres))
806         return hres == 0x80041001 ? E_INVALIDARG : hres;
807 
808     hres = CreateUri(secur_url, Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME, 0, &uri);
809     CoTaskMemFree(secur_url);
810     if(FAILED(hres))
811         return hres;
812 
813     hres = generate_security_id(uri, secid, secid_len, zone);
814     IUri_Release(uri);
815 
816     return hres;
817 }
818 
819 static HRESULT get_security_id_for_uri(IUri *uri, BYTE *secid, DWORD *secid_len)
820 {
821     HRESULT hres;
822     IUri *secur_uri;
823     DWORD zone = URLZONE_INVALID;
824 
825     hres = map_uri_to_zone(uri, &zone, &secur_uri);
826     if(FAILED(hres))
827         return hres;
828 
829     hres = generate_security_id(secur_uri, secid, secid_len, zone);
830     IUri_Release(secur_uri);
831 
832     return hres;
833 }
834 
835 /***********************************************************************
836  *           InternetSecurityManager implementation
837  *
838  */
839 typedef struct {
840     IInternetSecurityManagerEx2 IInternetSecurityManagerEx2_iface;
841 
842     LONG ref;
843 
844     IInternetSecurityMgrSite *mgrsite;
845     IInternetSecurityManager *custom_manager;
846 } SecManagerImpl;
847 
848 static inline SecManagerImpl *impl_from_IInternetSecurityManagerEx2(IInternetSecurityManagerEx2 *iface)
849 {
850     return CONTAINING_RECORD(iface, SecManagerImpl, IInternetSecurityManagerEx2_iface);
851 }
852 
853 static HRESULT WINAPI SecManagerImpl_QueryInterface(IInternetSecurityManagerEx2* iface,REFIID riid,void** ppvObject)
854 {
855     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
856 
857     TRACE("(%p)->(%s %p)\n",This,debugstr_guid(riid),ppvObject);
858 
859     if(!ppvObject)
860 	return E_INVALIDARG;
861 
862     if(IsEqualIID(&IID_IUnknown, riid) ||
863        IsEqualIID(&IID_IInternetSecurityManager, riid) ||
864        IsEqualIID(&IID_IInternetSecurityManagerEx, riid) ||
865        IsEqualIID(&IID_IInternetSecurityManagerEx2, riid)) {
866         *ppvObject = iface;
867     } else {
868         WARN("not supported interface %s\n", debugstr_guid(riid));
869         *ppvObject = NULL;
870         return E_NOINTERFACE;
871     }
872 
873     IInternetSecurityManagerEx2_AddRef(iface);
874     return S_OK;
875 }
876 
877 static ULONG WINAPI SecManagerImpl_AddRef(IInternetSecurityManagerEx2* iface)
878 {
879     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
880     ULONG refCount = InterlockedIncrement(&This->ref);
881 
882     TRACE("(%p) ref=%u\n", This, refCount);
883 
884     return refCount;
885 }
886 
887 static ULONG WINAPI SecManagerImpl_Release(IInternetSecurityManagerEx2* iface)
888 {
889     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
890     ULONG refCount = InterlockedDecrement(&This->ref);
891 
892     TRACE("(%p) ref=%u\n", This, refCount);
893 
894     /* destroy the object if there are no more references on it */
895     if (!refCount){
896         if(This->mgrsite)
897             IInternetSecurityMgrSite_Release(This->mgrsite);
898         if(This->custom_manager)
899             IInternetSecurityManager_Release(This->custom_manager);
900 
901         heap_free(This);
902 
903         URLMON_UnlockModule();
904     }
905 
906     return refCount;
907 }
908 
909 static HRESULT WINAPI SecManagerImpl_SetSecuritySite(IInternetSecurityManagerEx2 *iface,
910                                                      IInternetSecurityMgrSite *pSite)
911 {
912     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
913 
914     TRACE("(%p)->(%p)\n", This, pSite);
915 
916     if(This->mgrsite)
917         IInternetSecurityMgrSite_Release(This->mgrsite);
918 
919     if(This->custom_manager) {
920         IInternetSecurityManager_Release(This->custom_manager);
921         This->custom_manager = NULL;
922     }
923 
924     This->mgrsite = pSite;
925 
926     if(pSite) {
927         IServiceProvider *servprov;
928         HRESULT hres;
929 
930         IInternetSecurityMgrSite_AddRef(pSite);
931 
932         hres = IInternetSecurityMgrSite_QueryInterface(pSite, &IID_IServiceProvider,
933                 (void**)&servprov);
934         if(SUCCEEDED(hres)) {
935             IServiceProvider_QueryService(servprov, &SID_SInternetSecurityManager,
936                     &IID_IInternetSecurityManager, (void**)&This->custom_manager);
937             IServiceProvider_Release(servprov);
938         }
939     }
940 
941     return S_OK;
942 }
943 
944 static HRESULT WINAPI SecManagerImpl_GetSecuritySite(IInternetSecurityManagerEx2 *iface,
945                                                      IInternetSecurityMgrSite **ppSite)
946 {
947     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
948 
949     TRACE("(%p)->(%p)\n", This, ppSite);
950 
951     if(!ppSite)
952         return E_INVALIDARG;
953 
954     if(This->mgrsite)
955         IInternetSecurityMgrSite_AddRef(This->mgrsite);
956 
957     *ppSite = This->mgrsite;
958     return S_OK;
959 }
960 
961 static HRESULT WINAPI SecManagerImpl_MapUrlToZone(IInternetSecurityManagerEx2 *iface,
962                                                   LPCWSTR pwszUrl, DWORD *pdwZone,
963                                                   DWORD dwFlags)
964 {
965     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
966     HRESULT hres;
967 
968     TRACE("(%p)->(%s %p %08x)\n", iface, debugstr_w(pwszUrl), pdwZone, dwFlags);
969 
970     if(This->custom_manager) {
971         hres = IInternetSecurityManager_MapUrlToZone(This->custom_manager,
972                 pwszUrl, pdwZone, dwFlags);
973         if(hres != INET_E_DEFAULT_ACTION)
974             return hres;
975     }
976 
977     if(!pwszUrl) {
978         *pdwZone = URLZONE_INVALID;
979         return E_INVALIDARG;
980     }
981 
982     if(dwFlags)
983         FIXME("not supported flags: %08x\n", dwFlags);
984 
985     return map_url_to_zone(pwszUrl, pdwZone, NULL);
986 }
987 
988 static HRESULT WINAPI SecManagerImpl_GetSecurityId(IInternetSecurityManagerEx2 *iface,
989         LPCWSTR pwszUrl, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
990 {
991     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
992 
993     TRACE("(%p)->(%s %p %p %08lx)\n", iface, debugstr_w(pwszUrl), pbSecurityId,
994           pcbSecurityId, dwReserved);
995 
996     if(This->custom_manager) {
997         HRESULT hres;
998 
999         hres = IInternetSecurityManager_GetSecurityId(This->custom_manager,
1000                 pwszUrl, pbSecurityId, pcbSecurityId, dwReserved);
1001         if(hres != INET_E_DEFAULT_ACTION)
1002             return hres;
1003     }
1004 
1005     if(!pwszUrl || !pbSecurityId || !pcbSecurityId)
1006         return E_INVALIDARG;
1007 
1008     if(dwReserved)
1009         FIXME("dwReserved is not supported\n");
1010 
1011     return get_security_id_for_url(pwszUrl, pbSecurityId, pcbSecurityId);
1012 }
1013 
1014 
1015 static HRESULT WINAPI SecManagerImpl_ProcessUrlAction(IInternetSecurityManagerEx2 *iface,
1016                                                       LPCWSTR pwszUrl, DWORD dwAction,
1017                                                       BYTE *pPolicy, DWORD cbPolicy,
1018                                                       BYTE *pContext, DWORD cbContext,
1019                                                       DWORD dwFlags, DWORD dwReserved)
1020 {
1021     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1022     DWORD zone, policy;
1023     HRESULT hres;
1024 
1025     TRACE("(%p)->(%s %08x %p %08x %p %08x %08x %08x)\n", iface, debugstr_w(pwszUrl), dwAction,
1026           pPolicy, cbPolicy, pContext, cbContext, dwFlags, dwReserved);
1027 
1028     if(This->custom_manager) {
1029         hres = IInternetSecurityManager_ProcessUrlAction(This->custom_manager, pwszUrl, dwAction,
1030                 pPolicy, cbPolicy, pContext, cbContext, dwFlags, dwReserved);
1031         if(hres != INET_E_DEFAULT_ACTION)
1032             return hres;
1033     }
1034 
1035     if(dwFlags || dwReserved)
1036         FIXME("Unsupported arguments\n");
1037 
1038     if(!pwszUrl)
1039         return E_INVALIDARG;
1040 
1041     hres = map_url_to_zone(pwszUrl, &zone, NULL);
1042     if(FAILED(hres))
1043         return hres;
1044 
1045     hres = get_action_policy(zone, dwAction, (BYTE*)&policy, sizeof(policy), URLZONEREG_DEFAULT);
1046     if(FAILED(hres))
1047         return hres;
1048 
1049     TRACE("policy %x\n", policy);
1050     if(cbPolicy >= sizeof(DWORD))
1051         *(DWORD*)pPolicy = policy;
1052 
1053     switch(GetUrlPolicyPermissions(policy)) {
1054     case URLPOLICY_ALLOW:
1055     case URLPOLICY_CHANNEL_SOFTDIST_PRECACHE:
1056         return S_OK;
1057     case URLPOLICY_DISALLOW:
1058         return S_FALSE;
1059     case URLPOLICY_QUERY:
1060         FIXME("URLPOLICY_QUERY not implemented\n");
1061         return E_FAIL;
1062     default:
1063         FIXME("Not implemented policy %x\n", policy);
1064     }
1065 
1066     return E_FAIL;
1067 }
1068 
1069 
1070 static HRESULT WINAPI SecManagerImpl_QueryCustomPolicy(IInternetSecurityManagerEx2 *iface,
1071                                                        LPCWSTR pwszUrl, REFGUID guidKey,
1072                                                        BYTE **ppPolicy, DWORD *pcbPolicy,
1073                                                        BYTE *pContext, DWORD cbContext,
1074                                                        DWORD dwReserved)
1075 {
1076     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1077     HRESULT hres;
1078 
1079     TRACE("(%p)->(%s %s %p %p %p %08x %08x )\n", iface, debugstr_w(pwszUrl), debugstr_guid(guidKey),
1080           ppPolicy, pcbPolicy, pContext, cbContext, dwReserved);
1081 
1082     if(This->custom_manager) {
1083         hres = IInternetSecurityManager_QueryCustomPolicy(This->custom_manager, pwszUrl, guidKey,
1084                 ppPolicy, pcbPolicy, pContext, cbContext, dwReserved);
1085         if(hres != INET_E_DEFAULT_ACTION)
1086             return hres;
1087     }
1088 
1089     WARN("Unknown guidKey %s\n", debugstr_guid(guidKey));
1090     return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
1091 }
1092 
1093 static HRESULT WINAPI SecManagerImpl_SetZoneMapping(IInternetSecurityManagerEx2 *iface,
1094                                                     DWORD dwZone, LPCWSTR pwszPattern, DWORD dwFlags)
1095 {
1096     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1097     HRESULT hres;
1098 
1099     TRACE("(%p)->(%08x %s %08x)\n", iface, dwZone, debugstr_w(pwszPattern),dwFlags);
1100 
1101     if(This->custom_manager) {
1102         hres = IInternetSecurityManager_SetZoneMapping(This->custom_manager, dwZone,
1103                 pwszPattern, dwFlags);
1104         if(hres != INET_E_DEFAULT_ACTION)
1105             return hres;
1106     }
1107 
1108     FIXME("Default action is not implemented\n");
1109     return E_NOTIMPL;
1110 }
1111 
1112 static HRESULT WINAPI SecManagerImpl_GetZoneMappings(IInternetSecurityManagerEx2 *iface,
1113         DWORD dwZone, IEnumString **ppenumString, DWORD dwFlags)
1114 {
1115     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1116     HRESULT hres;
1117 
1118     TRACE("(%p)->(%08x %p %08x)\n", iface, dwZone, ppenumString,dwFlags);
1119 
1120     if(This->custom_manager) {
1121         hres = IInternetSecurityManager_GetZoneMappings(This->custom_manager, dwZone,
1122                 ppenumString, dwFlags);
1123         if(hres != INET_E_DEFAULT_ACTION)
1124             return hres;
1125     }
1126 
1127     FIXME("Default action is not implemented\n");
1128     return E_NOTIMPL;
1129 }
1130 
1131 static HRESULT WINAPI SecManagerImpl_ProcessUrlActionEx(IInternetSecurityManagerEx2 *iface,
1132         LPCWSTR pwszUrl, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext,
1133         DWORD dwFlags, DWORD dwReserved, DWORD *pdwOutFlags)
1134 {
1135     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1136     FIXME("(%p)->(%s %08x %p %d %p %d %08x %08x %p) stub\n", This, debugstr_w(pwszUrl), dwAction, pPolicy, cbPolicy,
1137           pContext, cbContext, dwFlags, dwReserved, pdwOutFlags);
1138     return E_NOTIMPL;
1139 }
1140 
1141 static HRESULT WINAPI SecManagerImpl_MapUrlToZoneEx2(IInternetSecurityManagerEx2 *iface,
1142         IUri *pUri, DWORD *pdwZone, DWORD dwFlags, LPWSTR *ppwszMappedUrl, DWORD *pdwOutFlags)
1143 {
1144     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1145 
1146     TRACE("(%p)->(%p %p %08x %p %p)\n", This, pUri, pdwZone, dwFlags, ppwszMappedUrl, pdwOutFlags);
1147 
1148     if(This->custom_manager) {
1149         HRESULT hres;
1150         IInternetSecurityManagerEx2 *sec_mgr2;
1151 
1152         hres = IInternetSecurityManager_QueryInterface(This->custom_manager, &IID_IInternetSecurityManagerEx2,
1153                 (void**)&sec_mgr2);
1154         if(SUCCEEDED(hres)) {
1155             hres = IInternetSecurityManagerEx2_MapUrlToZoneEx2(sec_mgr2, pUri, pdwZone, dwFlags, ppwszMappedUrl, pdwOutFlags);
1156             IInternetSecurityManagerEx2_Release(sec_mgr2);
1157         } else {
1158             BSTR url;
1159 
1160             hres = IUri_GetDisplayUri(pUri, &url);
1161             if(FAILED(hres))
1162                 return hres;
1163 
1164             hres = IInternetSecurityManager_MapUrlToZone(This->custom_manager, url, pdwZone, dwFlags);
1165             SysFreeString(url);
1166         }
1167 
1168         if(hres != INET_E_DEFAULT_ACTION)
1169             return hres;
1170     }
1171 
1172     if(!pdwZone)
1173         return E_INVALIDARG;
1174 
1175     if(!pUri) {
1176         *pdwZone = URLZONE_INVALID;
1177         return E_INVALIDARG;
1178     }
1179 
1180     if(dwFlags)
1181         FIXME("Unsupported flags: %08x\n", dwFlags);
1182 
1183     return map_uri_to_zone(pUri, pdwZone, NULL);
1184 }
1185 
1186 static HRESULT WINAPI SecManagerImpl_ProcessUrlActionEx2(IInternetSecurityManagerEx2 *iface,
1187         IUri *pUri, DWORD dwAction, BYTE *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext,
1188         DWORD dwFlags, DWORD_PTR dwReserved, DWORD *pdwOutFlags)
1189 {
1190     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1191     FIXME("(%p)->(%p %08x %p %d %p %d %08x %08x %p) stub\n", This, pUri, dwAction, pPolicy,
1192           cbPolicy, pContext, cbContext, dwFlags, (DWORD)dwReserved, pdwOutFlags);
1193     return E_NOTIMPL;
1194 }
1195 
1196 static HRESULT WINAPI SecManagerImpl_GetSecurityIdEx2(IInternetSecurityManagerEx2 *iface,
1197         IUri *pUri, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
1198 {
1199     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1200     TRACE("(%p)->(%p %p %p %08x) stub\n", This, pUri, pbSecurityId, pcbSecurityId, (DWORD)dwReserved);
1201 
1202     if(dwReserved)
1203         FIXME("dwReserved is not supported yet\n");
1204 
1205     if(!pUri || !pcbSecurityId || !pbSecurityId)
1206         return E_INVALIDARG;
1207 
1208     return get_security_id_for_uri(pUri, pbSecurityId, pcbSecurityId);
1209 }
1210 
1211 static HRESULT WINAPI SecManagerImpl_QueryCustomPolicyEx2(IInternetSecurityManagerEx2 *iface,
1212         IUri *pUri, REFGUID guidKey, BYTE **ppPolicy, DWORD *pcbPolicy, BYTE *pContext,
1213         DWORD cbContext, DWORD_PTR dwReserved)
1214 {
1215     SecManagerImpl *This = impl_from_IInternetSecurityManagerEx2(iface);
1216     FIXME("(%p)->(%p %s %p %p %p %d %08x) stub\n", This, pUri, debugstr_guid(guidKey), ppPolicy, pcbPolicy,
1217           pContext, cbContext, (DWORD)dwReserved);
1218     return E_NOTIMPL;
1219 }
1220 
1221 static const IInternetSecurityManagerEx2Vtbl VT_SecManagerImpl =
1222 {
1223     SecManagerImpl_QueryInterface,
1224     SecManagerImpl_AddRef,
1225     SecManagerImpl_Release,
1226     SecManagerImpl_SetSecuritySite,
1227     SecManagerImpl_GetSecuritySite,
1228     SecManagerImpl_MapUrlToZone,
1229     SecManagerImpl_GetSecurityId,
1230     SecManagerImpl_ProcessUrlAction,
1231     SecManagerImpl_QueryCustomPolicy,
1232     SecManagerImpl_SetZoneMapping,
1233     SecManagerImpl_GetZoneMappings,
1234     SecManagerImpl_ProcessUrlActionEx,
1235     SecManagerImpl_MapUrlToZoneEx2,
1236     SecManagerImpl_ProcessUrlActionEx2,
1237     SecManagerImpl_GetSecurityIdEx2,
1238     SecManagerImpl_QueryCustomPolicyEx2
1239 };
1240 
1241 HRESULT SecManagerImpl_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
1242 {
1243     SecManagerImpl *This;
1244 
1245     TRACE("(%p,%p)\n",pUnkOuter,ppobj);
1246     This = heap_alloc(sizeof(*This));
1247 
1248     /* Initialize the virtual function table. */
1249     This->IInternetSecurityManagerEx2_iface.lpVtbl = &VT_SecManagerImpl;
1250 
1251     This->ref = 1;
1252     This->mgrsite = NULL;
1253     This->custom_manager = NULL;
1254 
1255     *ppobj = This;
1256 
1257     URLMON_LockModule();
1258 
1259     return S_OK;
1260 }
1261 
1262 /***********************************************************************
1263  *           InternetZoneManager implementation
1264  *
1265  */
1266 typedef struct {
1267     IInternetZoneManagerEx2 IInternetZoneManagerEx2_iface;
1268     LONG ref;
1269     LPDWORD *zonemaps;
1270     DWORD zonemap_count;
1271 } ZoneMgrImpl;
1272 
1273 static inline ZoneMgrImpl *impl_from_IInternetZoneManagerEx2(IInternetZoneManagerEx2 *iface)
1274 {
1275     return CONTAINING_RECORD(iface, ZoneMgrImpl, IInternetZoneManagerEx2_iface);
1276 }
1277 
1278 
1279 /***********************************************************************
1280  * build_zonemap_from_reg [internal]
1281  *
1282  * Enumerate the Zones in the Registry and return the Zones in a DWORD-array
1283  * The number of the Zones is returned in data[0]
1284  */
1285 static LPDWORD build_zonemap_from_reg(void)
1286 {
1287     WCHAR name[32];
1288     HKEY hkey;
1289     LPDWORD data = NULL;
1290     DWORD allocated = 6; /* space for the zonecount and Zone "0" up to Zone "4" */
1291     DWORD used = 0;
1292     DWORD res;
1293     DWORD len;
1294 
1295 
1296     res = RegOpenKeyW(HKEY_CURRENT_USER, wszZonesKey, &hkey);
1297     if (res)
1298         return NULL;
1299 
1300     data = heap_alloc(allocated * sizeof(DWORD));
1301     if (!data)
1302         goto cleanup;
1303 
1304     while (!res) {
1305         name[0] = '\0';
1306         len = ARRAY_SIZE(name);
1307         res = RegEnumKeyExW(hkey, used, name, &len, NULL, NULL, NULL, NULL);
1308 
1309         if (!res) {
1310             used++;
1311             if (used == allocated) {
1312                 LPDWORD new_data;
1313 
1314                 allocated *= 2;
1315                 new_data = heap_realloc_zero(data, allocated * sizeof(DWORD));
1316                 if (!new_data)
1317                     goto cleanup;
1318 
1319                 data = new_data;
1320             }
1321             data[used] = wcstol(name, NULL, 10);
1322         }
1323     }
1324     if (used) {
1325         RegCloseKey(hkey);
1326         data[0] = used;
1327         return data;
1328     }
1329 
1330 cleanup:
1331     /* something failed */
1332     RegCloseKey(hkey);
1333     heap_free(data);
1334     return NULL;
1335 }
1336 
1337 /********************************************************************
1338  *      IInternetZoneManager_QueryInterface
1339  */
1340 static HRESULT WINAPI ZoneMgrImpl_QueryInterface(IInternetZoneManagerEx2* iface, REFIID riid, void** ppvObject)
1341 {
1342     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1343 
1344     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppvObject);
1345 
1346     if(!This || !ppvObject)
1347         return E_INVALIDARG;
1348 
1349     if(IsEqualIID(&IID_IUnknown, riid)) {
1350         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppvObject);
1351     }else if(IsEqualIID(&IID_IInternetZoneManager, riid)) {
1352         TRACE("(%p)->(IID_InternetZoneManager %p)\n", This, ppvObject);
1353     }else if(IsEqualIID(&IID_IInternetZoneManagerEx, riid)) {
1354         TRACE("(%p)->(IID_InternetZoneManagerEx %p)\n", This, ppvObject);
1355     }else if(IsEqualIID(&IID_IInternetZoneManagerEx2, riid)) {
1356         TRACE("(%p)->(IID_InternetZoneManagerEx2 %p)\n", This, ppvObject);
1357     }
1358     else
1359     {
1360         FIXME("Unknown interface: %s\n", debugstr_guid(riid));
1361         *ppvObject = NULL;
1362         return E_NOINTERFACE;
1363     }
1364 
1365     *ppvObject = iface;
1366     IInternetZoneManagerEx2_AddRef(iface);
1367     return S_OK;
1368 }
1369 
1370 /********************************************************************
1371  *      IInternetZoneManager_AddRef
1372  */
1373 static ULONG WINAPI ZoneMgrImpl_AddRef(IInternetZoneManagerEx2* iface)
1374 {
1375     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1376     ULONG refCount = InterlockedIncrement(&This->ref);
1377 
1378     TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
1379 
1380     return refCount;
1381 }
1382 
1383 /********************************************************************
1384  *      IInternetZoneManager_Release
1385  */
1386 static ULONG WINAPI ZoneMgrImpl_Release(IInternetZoneManagerEx2* iface)
1387 {
1388     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1389     ULONG refCount = InterlockedDecrement(&This->ref);
1390 
1391     TRACE("(%p)->(ref before=%u)\n",This, refCount + 1);
1392 
1393     if(!refCount) {
1394         while (This->zonemap_count) heap_free(This->zonemaps[--This->zonemap_count]);
1395         heap_free(This->zonemaps);
1396         heap_free(This);
1397         URLMON_UnlockModule();
1398     }
1399 
1400     return refCount;
1401 }
1402 
1403 /********************************************************************
1404  *      IInternetZoneManager_GetZoneAttributes
1405  */
1406 static HRESULT WINAPI ZoneMgrImpl_GetZoneAttributes(IInternetZoneManagerEx2* iface,
1407                                                     DWORD dwZone,
1408                                                     ZONEATTRIBUTES* pZoneAttributes)
1409 {
1410     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1411     HRESULT hr;
1412     HKEY hcu;
1413     HKEY hklm = NULL;
1414 
1415     TRACE("(%p)->(%d %p)\n", This, dwZone, pZoneAttributes);
1416 
1417     if (!pZoneAttributes)
1418         return E_INVALIDARG;
1419 
1420     hr = open_zone_key(HKEY_CURRENT_USER, dwZone, &hcu);
1421     if (FAILED(hr))
1422         return S_OK;  /* IE6 and older returned E_FAIL here */
1423 
1424     hr = open_zone_key(HKEY_LOCAL_MACHINE, dwZone, &hklm);
1425     if (FAILED(hr))
1426         TRACE("Zone %d not in HKLM\n", dwZone);
1427 
1428     get_string_from_reg(hcu, hklm, displaynameW, pZoneAttributes->szDisplayName, MAX_ZONE_PATH);
1429     get_string_from_reg(hcu, hklm, descriptionW, pZoneAttributes->szDescription, MAX_ZONE_DESCRIPTION);
1430     get_string_from_reg(hcu, hklm, iconW, pZoneAttributes->szIconPath, MAX_ZONE_PATH);
1431     get_dword_from_reg(hcu, hklm, minlevelW, &pZoneAttributes->dwTemplateMinLevel);
1432     get_dword_from_reg(hcu, hklm, currentlevelW, &pZoneAttributes->dwTemplateCurrentLevel);
1433     get_dword_from_reg(hcu, hklm, recommendedlevelW, &pZoneAttributes->dwTemplateRecommended);
1434     get_dword_from_reg(hcu, hklm, flagsW, &pZoneAttributes->dwFlags);
1435 
1436     RegCloseKey(hklm);
1437     RegCloseKey(hcu);
1438     return S_OK;
1439 }
1440 
1441 /********************************************************************
1442  *      IInternetZoneManager_SetZoneAttributes
1443  */
1444 static HRESULT WINAPI ZoneMgrImpl_SetZoneAttributes(IInternetZoneManagerEx2* iface,
1445                                                     DWORD dwZone,
1446                                                     ZONEATTRIBUTES* pZoneAttributes)
1447 {
1448     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1449     HRESULT hr;
1450     HKEY hcu;
1451 
1452     TRACE("(%p)->(%d %p)\n", This, dwZone, pZoneAttributes);
1453 
1454     if (!pZoneAttributes)
1455         return E_INVALIDARG;
1456 
1457     hr = open_zone_key(HKEY_CURRENT_USER, dwZone, &hcu);
1458     if (FAILED(hr))
1459         return S_OK;  /* IE6 returned E_FAIL here */
1460 
1461     /* cbSize is ignored */
1462     RegSetValueExW(hcu, displaynameW, 0, REG_SZ, (LPBYTE) pZoneAttributes->szDisplayName,
1463                     (lstrlenW(pZoneAttributes->szDisplayName)+1)* sizeof(WCHAR));
1464 
1465     RegSetValueExW(hcu, descriptionW, 0, REG_SZ, (LPBYTE) pZoneAttributes->szDescription,
1466                     (lstrlenW(pZoneAttributes->szDescription)+1)* sizeof(WCHAR));
1467 
1468     RegSetValueExW(hcu, iconW, 0, REG_SZ, (LPBYTE) pZoneAttributes->szIconPath,
1469                     (lstrlenW(pZoneAttributes->szIconPath)+1)* sizeof(WCHAR));
1470 
1471     RegSetValueExW(hcu, minlevelW, 0, REG_DWORD,
1472                     (const BYTE*) &pZoneAttributes->dwTemplateMinLevel, sizeof(DWORD));
1473 
1474     RegSetValueExW(hcu, currentlevelW, 0, REG_DWORD,
1475                     (const BYTE*) &pZoneAttributes->dwTemplateCurrentLevel, sizeof(DWORD));
1476 
1477     RegSetValueExW(hcu, recommendedlevelW, 0, REG_DWORD,
1478                     (const BYTE*) &pZoneAttributes->dwTemplateRecommended, sizeof(DWORD));
1479 
1480     RegSetValueExW(hcu, flagsW, 0, REG_DWORD, (const BYTE*) &pZoneAttributes->dwFlags, sizeof(DWORD));
1481     RegCloseKey(hcu);
1482     return S_OK;
1483 
1484 }
1485 
1486 /********************************************************************
1487  *      IInternetZoneManager_GetZoneCustomPolicy
1488  */
1489 static HRESULT WINAPI ZoneMgrImpl_GetZoneCustomPolicy(IInternetZoneManagerEx2* iface,
1490                                                       DWORD dwZone,
1491                                                       REFGUID guidKey,
1492                                                       BYTE** ppPolicy,
1493                                                       DWORD* pcbPolicy,
1494                                                       URLZONEREG ulrZoneReg)
1495 {
1496     FIXME("(%p)->(%08x %s %p %p %08x) stub\n", iface, dwZone, debugstr_guid(guidKey),
1497                                                     ppPolicy, pcbPolicy, ulrZoneReg);
1498     return E_NOTIMPL;
1499 }
1500 
1501 /********************************************************************
1502  *      IInternetZoneManager_SetZoneCustomPolicy
1503  */
1504 static HRESULT WINAPI ZoneMgrImpl_SetZoneCustomPolicy(IInternetZoneManagerEx2* iface,
1505                                                       DWORD dwZone,
1506                                                       REFGUID guidKey,
1507                                                       BYTE* ppPolicy,
1508                                                       DWORD cbPolicy,
1509                                                       URLZONEREG ulrZoneReg)
1510 {
1511     FIXME("(%p)->(%08x %s %p %08x %08x) stub\n", iface, dwZone, debugstr_guid(guidKey),
1512                                                     ppPolicy, cbPolicy, ulrZoneReg);
1513     return E_NOTIMPL;
1514 }
1515 
1516 /********************************************************************
1517  *      IInternetZoneManager_GetZoneActionPolicy
1518  */
1519 static HRESULT WINAPI ZoneMgrImpl_GetZoneActionPolicy(IInternetZoneManagerEx2* iface,
1520         DWORD dwZone, DWORD dwAction, BYTE* pPolicy, DWORD cbPolicy, URLZONEREG urlZoneReg)
1521 {
1522     TRACE("(%p)->(%d %08x %p %d %d)\n", iface, dwZone, dwAction, pPolicy,
1523             cbPolicy, urlZoneReg);
1524 
1525     if(!pPolicy)
1526         return E_INVALIDARG;
1527 
1528     return get_action_policy(dwZone, dwAction, pPolicy, cbPolicy, urlZoneReg);
1529 }
1530 
1531 /********************************************************************
1532  *      IInternetZoneManager_SetZoneActionPolicy
1533  */
1534 static HRESULT WINAPI ZoneMgrImpl_SetZoneActionPolicy(IInternetZoneManagerEx2* iface,
1535                                                       DWORD dwZone,
1536                                                       DWORD dwAction,
1537                                                       BYTE* pPolicy,
1538                                                       DWORD cbPolicy,
1539                                                       URLZONEREG urlZoneReg)
1540 {
1541     FIXME("(%p)->(%08x %08x %p %08x %08x) stub\n", iface, dwZone, dwAction, pPolicy,
1542                                                        cbPolicy, urlZoneReg);
1543     return E_NOTIMPL;
1544 }
1545 
1546 /********************************************************************
1547  *      IInternetZoneManager_PromptAction
1548  */
1549 static HRESULT WINAPI ZoneMgrImpl_PromptAction(IInternetZoneManagerEx2* iface,
1550                                                DWORD dwAction,
1551                                                HWND hwndParent,
1552                                                LPCWSTR pwszUrl,
1553                                                LPCWSTR pwszText,
1554                                                DWORD dwPromptFlags)
1555 {
1556     FIXME("%p %08x %p %s %s %08x\n", iface, dwAction, hwndParent,
1557           debugstr_w(pwszUrl), debugstr_w(pwszText), dwPromptFlags );
1558     return E_NOTIMPL;
1559 }
1560 
1561 /********************************************************************
1562  *      IInternetZoneManager_LogAction
1563  */
1564 static HRESULT WINAPI ZoneMgrImpl_LogAction(IInternetZoneManagerEx2* iface,
1565                                             DWORD dwAction,
1566                                             LPCWSTR pwszUrl,
1567                                             LPCWSTR pwszText,
1568                                             DWORD dwLogFlags)
1569 {
1570     FIXME("(%p)->(%08x %s %s %08x) stub\n", iface, dwAction, debugstr_w(pwszUrl),
1571                                               debugstr_w(pwszText), dwLogFlags);
1572     return E_NOTIMPL;
1573 }
1574 
1575 /********************************************************************
1576  *      IInternetZoneManager_CreateZoneEnumerator
1577  */
1578 static HRESULT WINAPI ZoneMgrImpl_CreateZoneEnumerator(IInternetZoneManagerEx2* iface,
1579                                                        DWORD* pdwEnum,
1580                                                        DWORD* pdwCount,
1581                                                        DWORD dwFlags)
1582 {
1583     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1584     LPDWORD * new_maps;
1585     LPDWORD data;
1586     DWORD i;
1587 
1588     TRACE("(%p)->(%p, %p, 0x%08x)\n", This, pdwEnum, pdwCount, dwFlags);
1589     if (!pdwEnum || !pdwCount || (dwFlags != 0))
1590         return E_INVALIDARG;
1591 
1592     data = build_zonemap_from_reg();
1593     TRACE("found %d zones\n", data ? data[0] : -1);
1594 
1595     if (!data)
1596         return E_FAIL;
1597 
1598     for (i = 0; i < This->zonemap_count; i++) {
1599         if (This->zonemaps && !This->zonemaps[i]) {
1600             This->zonemaps[i] = data;
1601             *pdwEnum = i;
1602             *pdwCount = data[0];
1603             return S_OK;
1604         }
1605     }
1606 
1607     if (This->zonemaps) {
1608         /* try to double the nr. of pointers in the array */
1609         new_maps = heap_realloc_zero(This->zonemaps, This->zonemap_count * 2 * sizeof(LPDWORD));
1610         if (new_maps)
1611             This->zonemap_count *= 2;
1612     }
1613     else
1614     {
1615         This->zonemap_count = 2;
1616         new_maps = heap_alloc_zero(This->zonemap_count * sizeof(LPDWORD));
1617     }
1618 
1619     if (!new_maps) {
1620         heap_free(data);
1621         return E_FAIL;
1622     }
1623     This->zonemaps = new_maps;
1624     This->zonemaps[i] = data;
1625     *pdwEnum = i;
1626     *pdwCount = data[0];
1627     return S_OK;
1628 }
1629 
1630 /********************************************************************
1631  *      IInternetZoneManager_GetZoneAt
1632  */
1633 static HRESULT WINAPI ZoneMgrImpl_GetZoneAt(IInternetZoneManagerEx2* iface,
1634                                             DWORD dwEnum,
1635                                             DWORD dwIndex,
1636                                             DWORD* pdwZone)
1637 {
1638     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1639     LPDWORD data;
1640 
1641     TRACE("(%p)->(0x%08x, %d, %p)\n", This, dwEnum, dwIndex, pdwZone);
1642 
1643     /* make sure, that dwEnum and dwIndex are in the valid range */
1644     if (dwEnum < This->zonemap_count) {
1645         if ((data = This->zonemaps[dwEnum])) {
1646             if (dwIndex < data[0]) {
1647                 *pdwZone = data[dwIndex + 1];
1648                 return S_OK;
1649             }
1650         }
1651     }
1652     return E_INVALIDARG;
1653 }
1654 
1655 /********************************************************************
1656  *      IInternetZoneManager_DestroyZoneEnumerator
1657  */
1658 static HRESULT WINAPI ZoneMgrImpl_DestroyZoneEnumerator(IInternetZoneManagerEx2* iface,
1659                                                         DWORD dwEnum)
1660 {
1661     ZoneMgrImpl* This = impl_from_IInternetZoneManagerEx2(iface);
1662     LPDWORD data;
1663 
1664     TRACE("(%p)->(0x%08x)\n", This, dwEnum);
1665     /* make sure, that dwEnum is valid */
1666     if (dwEnum < This->zonemap_count) {
1667         if ((data = This->zonemaps[dwEnum])) {
1668             This->zonemaps[dwEnum] = NULL;
1669             heap_free(data);
1670             return S_OK;
1671         }
1672     }
1673     return E_INVALIDARG;
1674 }
1675 
1676 /********************************************************************
1677  *      IInternetZoneManager_CopyTemplatePoliciesToZone
1678  */
1679 static HRESULT WINAPI ZoneMgrImpl_CopyTemplatePoliciesToZone(IInternetZoneManagerEx2* iface,
1680                                                              DWORD dwTemplate,
1681                                                              DWORD dwZone,
1682                                                              DWORD dwReserved)
1683 {
1684     FIXME("(%p)->(%08x %08x %08x) stub\n", iface, dwTemplate, dwZone, dwReserved);
1685     return E_NOTIMPL;
1686 }
1687 
1688 /********************************************************************
1689  *      IInternetZoneManagerEx_GetZoneActionPolicyEx
1690  */
1691 static HRESULT WINAPI ZoneMgrImpl_GetZoneActionPolicyEx(IInternetZoneManagerEx2* iface,
1692                                                         DWORD dwZone,
1693                                                         DWORD dwAction,
1694                                                         BYTE* pPolicy,
1695                                                         DWORD cbPolicy,
1696                                                         URLZONEREG urlZoneReg,
1697                                                         DWORD dwFlags)
1698 {
1699     TRACE("(%p)->(%d, 0x%x, %p, %d, %d, 0x%x)\n", iface, dwZone,
1700             dwAction, pPolicy, cbPolicy, urlZoneReg, dwFlags);
1701 
1702     if(!pPolicy)
1703         return E_INVALIDARG;
1704 
1705     if (dwFlags)
1706         FIXME("dwFlags 0x%x ignored\n", dwFlags);
1707 
1708     return get_action_policy(dwZone, dwAction, pPolicy, cbPolicy, urlZoneReg);
1709 }
1710 
1711 /********************************************************************
1712  *      IInternetZoneManagerEx_SetZoneActionPolicyEx
1713  */
1714 static HRESULT WINAPI ZoneMgrImpl_SetZoneActionPolicyEx(IInternetZoneManagerEx2* iface,
1715                                                         DWORD dwZone,
1716                                                         DWORD dwAction,
1717                                                         BYTE* pPolicy,
1718                                                         DWORD cbPolicy,
1719                                                         URLZONEREG urlZoneReg,
1720                                                         DWORD dwFlags)
1721 {
1722     FIXME("(%p)->(%d, 0x%x, %p, %d, %d, 0x%x) stub\n", iface, dwZone, dwAction, pPolicy,
1723                                                        cbPolicy, urlZoneReg, dwFlags);
1724     return E_NOTIMPL;
1725 }
1726 
1727 /********************************************************************
1728  *      IInternetZoneManagerEx2_GetZoneAttributesEx
1729  */
1730 static HRESULT WINAPI ZoneMgrImpl_GetZoneAttributesEx(IInternetZoneManagerEx2* iface,
1731                                                       DWORD dwZone,
1732                                                       ZONEATTRIBUTES* pZoneAttributes,
1733                                                       DWORD dwFlags)
1734 {
1735     TRACE("(%p)->(%d, %p, 0x%x)\n", iface, dwZone, pZoneAttributes, dwFlags);
1736 
1737     if (dwFlags)
1738         FIXME("dwFlags 0x%x ignored\n", dwFlags);
1739 
1740     return IInternetZoneManagerEx2_GetZoneAttributes(iface, dwZone, pZoneAttributes);
1741 }
1742 
1743 
1744 /********************************************************************
1745  *      IInternetZoneManagerEx2_GetZoneSecurityState
1746  */
1747 static HRESULT WINAPI ZoneMgrImpl_GetZoneSecurityState(IInternetZoneManagerEx2* iface,
1748                                                        DWORD dwZoneIndex,
1749                                                        BOOL fRespectPolicy,
1750                                                        LPDWORD pdwState,
1751                                                        BOOL *pfPolicyEncountered)
1752 {
1753     FIXME("(%p)->(%d, %d, %p, %p) stub\n", iface, dwZoneIndex, fRespectPolicy,
1754                                            pdwState, pfPolicyEncountered);
1755 
1756     *pdwState = SECURITY_IE_STATE_GREEN;
1757 
1758     if (pfPolicyEncountered)
1759         *pfPolicyEncountered = FALSE;
1760 
1761     return S_OK;
1762 }
1763 
1764 /********************************************************************
1765  *      IInternetZoneManagerEx2_GetIESecurityState
1766  */
1767 static HRESULT WINAPI ZoneMgrImpl_GetIESecurityState(IInternetZoneManagerEx2* iface,
1768                                                      BOOL fRespectPolicy,
1769                                                      LPDWORD pdwState,
1770                                                      BOOL *pfPolicyEncountered,
1771                                                      BOOL fNoCache)
1772 {
1773     FIXME("(%p)->(%d, %p, %p, %d) stub\n", iface, fRespectPolicy, pdwState,
1774                                            pfPolicyEncountered, fNoCache);
1775 
1776     *pdwState = SECURITY_IE_STATE_GREEN;
1777 
1778     if (pfPolicyEncountered)
1779         *pfPolicyEncountered = FALSE;
1780 
1781     return S_OK;
1782 }
1783 
1784 /********************************************************************
1785  *      IInternetZoneManagerEx2_FixInsecureSettings
1786  */
1787 static HRESULT WINAPI ZoneMgrImpl_FixInsecureSettings(IInternetZoneManagerEx2* iface)
1788 {
1789     FIXME("(%p) stub\n", iface);
1790     return S_OK;
1791 }
1792 
1793 /********************************************************************
1794  *      IInternetZoneManager_Construct
1795  */
1796 static const IInternetZoneManagerEx2Vtbl ZoneMgrImplVtbl = {
1797     ZoneMgrImpl_QueryInterface,
1798     ZoneMgrImpl_AddRef,
1799     ZoneMgrImpl_Release,
1800     /* IInternetZoneManager */
1801     ZoneMgrImpl_GetZoneAttributes,
1802     ZoneMgrImpl_SetZoneAttributes,
1803     ZoneMgrImpl_GetZoneCustomPolicy,
1804     ZoneMgrImpl_SetZoneCustomPolicy,
1805     ZoneMgrImpl_GetZoneActionPolicy,
1806     ZoneMgrImpl_SetZoneActionPolicy,
1807     ZoneMgrImpl_PromptAction,
1808     ZoneMgrImpl_LogAction,
1809     ZoneMgrImpl_CreateZoneEnumerator,
1810     ZoneMgrImpl_GetZoneAt,
1811     ZoneMgrImpl_DestroyZoneEnumerator,
1812     ZoneMgrImpl_CopyTemplatePoliciesToZone,
1813     /* IInternetZoneManagerEx */
1814     ZoneMgrImpl_GetZoneActionPolicyEx,
1815     ZoneMgrImpl_SetZoneActionPolicyEx,
1816     /* IInternetZoneManagerEx2 */
1817     ZoneMgrImpl_GetZoneAttributesEx,
1818     ZoneMgrImpl_GetZoneSecurityState,
1819     ZoneMgrImpl_GetIESecurityState,
1820     ZoneMgrImpl_FixInsecureSettings,
1821 };
1822 
1823 HRESULT ZoneMgrImpl_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
1824 {
1825     ZoneMgrImpl* ret = heap_alloc_zero(sizeof(ZoneMgrImpl));
1826 
1827     TRACE("(%p %p)\n", pUnkOuter, ppobj);
1828     ret->IInternetZoneManagerEx2_iface.lpVtbl = &ZoneMgrImplVtbl;
1829     ret->ref = 1;
1830     *ppobj = &ret->IInternetZoneManagerEx2_iface;
1831 
1832     URLMON_LockModule();
1833 
1834     return S_OK;
1835 }
1836 
1837 /***********************************************************************
1838  *           CoInternetCreateSecurityManager (URLMON.@)
1839  *
1840  */
1841 HRESULT WINAPI CoInternetCreateSecurityManager( IServiceProvider *pSP,
1842     IInternetSecurityManager **ppSM, DWORD dwReserved )
1843 {
1844     TRACE("%p %p %d\n", pSP, ppSM, dwReserved );
1845 
1846     if(pSP)
1847         FIXME("pSP not supported\n");
1848 
1849     return SecManagerImpl_Construct(NULL, (void**) ppSM);
1850 }
1851 
1852 /********************************************************************
1853  *      CoInternetCreateZoneManager (URLMON.@)
1854  */
1855 HRESULT WINAPI CoInternetCreateZoneManager(IServiceProvider* pSP, IInternetZoneManager** ppZM, DWORD dwReserved)
1856 {
1857     TRACE("(%p %p %x)\n", pSP, ppZM, dwReserved);
1858     return ZoneMgrImpl_Construct(NULL, (void**)ppZM);
1859 }
1860 
1861 static HRESULT parse_security_url(const WCHAR *url, PSUACTION action, WCHAR **result) {
1862     IInternetProtocolInfo *protocol_info;
1863     WCHAR *tmp, *new_url = NULL, *alloc_url = NULL;
1864     DWORD size, new_size;
1865     HRESULT hres = S_OK, parse_hres;
1866 
1867     while(1) {
1868         TRACE("parsing %s\n", debugstr_w(url));
1869 
1870         protocol_info = get_protocol_info(url);
1871         if(!protocol_info)
1872             break;
1873 
1874         size = lstrlenW(url)+1;
1875         new_url = CoTaskMemAlloc(size*sizeof(WCHAR));
1876         if(!new_url) {
1877             hres = E_OUTOFMEMORY;
1878             break;
1879         }
1880 
1881         new_size = 0;
1882         parse_hres = IInternetProtocolInfo_ParseUrl(protocol_info, url, PARSE_SECURITY_URL, 0, new_url, size, &new_size, 0);
1883         if(parse_hres == S_FALSE) {
1884             if(!new_size) {
1885                 hres = E_UNEXPECTED;
1886                 break;
1887             }
1888 
1889             tmp = CoTaskMemRealloc(new_url, new_size*sizeof(WCHAR));
1890             if(!tmp) {
1891                 hres = E_OUTOFMEMORY;
1892                 break;
1893             }
1894             new_url = tmp;
1895             parse_hres = IInternetProtocolInfo_ParseUrl(protocol_info, url, PARSE_SECURITY_URL, 0, new_url,
1896                     new_size, &new_size, 0);
1897             if(parse_hres == S_FALSE) {
1898                 hres = E_FAIL;
1899                 break;
1900             }
1901         }
1902 
1903         if(parse_hres != S_OK || !wcscmp(url, new_url))
1904             break;
1905 
1906         CoTaskMemFree(alloc_url);
1907         url = alloc_url = new_url;
1908         new_url = NULL;
1909     }
1910 
1911     CoTaskMemFree(new_url);
1912 
1913     if(hres != S_OK) {
1914         WARN("failed: %08x\n", hres);
1915         CoTaskMemFree(alloc_url);
1916         return hres;
1917     }
1918 
1919     if(action == PSU_DEFAULT && (protocol_info = get_protocol_info(url))) {
1920         size = lstrlenW(url)+1;
1921         new_url = CoTaskMemAlloc(size * sizeof(WCHAR));
1922         if(new_url) {
1923             new_size = 0;
1924             parse_hres = IInternetProtocolInfo_ParseUrl(protocol_info, url, PARSE_SECURITY_DOMAIN, 0,
1925                     new_url, size, &new_size, 0);
1926             if(parse_hres == S_FALSE) {
1927                 if(new_size) {
1928                     tmp = CoTaskMemRealloc(new_url, new_size*sizeof(WCHAR));
1929                     if(tmp) {
1930                         new_url = tmp;
1931                         parse_hres = IInternetProtocolInfo_ParseUrl(protocol_info, url, PARSE_SECURITY_DOMAIN, 0, new_url,
1932                                 new_size, &new_size, 0);
1933                         if(parse_hres == S_FALSE)
1934                             hres = E_FAIL;
1935                     }else {
1936                         hres = E_OUTOFMEMORY;
1937                     }
1938                 }else {
1939                     hres = E_UNEXPECTED;
1940                 }
1941             }
1942 
1943             if(hres == S_OK && parse_hres == S_OK) {
1944                 CoTaskMemFree(alloc_url);
1945                 url = alloc_url = new_url;
1946                 new_url = NULL;
1947             }
1948 
1949             CoTaskMemFree(new_url);
1950         }else {
1951             hres = E_OUTOFMEMORY;
1952         }
1953         IInternetProtocolInfo_Release(protocol_info);
1954     }
1955 
1956     if(FAILED(hres)) {
1957         WARN("failed %08x\n", hres);
1958         CoTaskMemFree(alloc_url);
1959         return hres;
1960     }
1961 
1962     if(!alloc_url) {
1963         size = lstrlenW(url)+1;
1964         alloc_url = CoTaskMemAlloc(size * sizeof(WCHAR));
1965         if(!alloc_url)
1966             return E_OUTOFMEMORY;
1967         memcpy(alloc_url, url, size * sizeof(WCHAR));
1968     }
1969 
1970     *result = alloc_url;
1971     return S_OK;
1972 }
1973 
1974 /********************************************************************
1975  *      CoInternetGetSecurityUrl (URLMON.@)
1976  */
1977 HRESULT WINAPI CoInternetGetSecurityUrl(LPCWSTR pwzUrl, LPWSTR *ppwzSecUrl, PSUACTION psuAction, DWORD dwReserved)
1978 {
1979     WCHAR *secure_url;
1980     HRESULT hres;
1981 
1982     TRACE("(%p,%p,%u,%u)\n", pwzUrl, ppwzSecUrl, psuAction, dwReserved);
1983 
1984     hres = parse_security_url(pwzUrl, psuAction, &secure_url);
1985     if(FAILED(hres))
1986         return hres;
1987 
1988     if(psuAction != PSU_SECURITY_URL_ONLY) {
1989         PARSEDURLW parsed_url = { sizeof(parsed_url) };
1990         DWORD size;
1991 
1992         /* FIXME: Use helpers from uri.c */
1993         if(SUCCEEDED(ParseURLW(secure_url, &parsed_url))) {
1994             WCHAR *new_url;
1995 
1996             switch(parsed_url.nScheme) {
1997             case URL_SCHEME_FTP:
1998             case URL_SCHEME_HTTP:
1999             case URL_SCHEME_HTTPS:
2000                 size = lstrlenW(secure_url)+1;
2001                 new_url = CoTaskMemAlloc(size * sizeof(WCHAR));
2002                 if(new_url)
2003                     hres = UrlGetPartW(secure_url, new_url, &size, URL_PART_HOSTNAME, URL_PARTFLAG_KEEPSCHEME);
2004                 else
2005                     hres = E_OUTOFMEMORY;
2006                 CoTaskMemFree(secure_url);
2007                 if(hres != S_OK) {
2008                     WARN("UrlGetPart failed: %08x\n", hres);
2009                     CoTaskMemFree(new_url);
2010                     return FAILED(hres) ? hres : E_FAIL;
2011                 }
2012                 secure_url = new_url;
2013             }
2014         }
2015     }
2016 
2017     *ppwzSecUrl = secure_url;
2018     return S_OK;
2019 }
2020 
2021 /********************************************************************
2022  *      CoInternetGetSecurityUrlEx (URLMON.@)
2023  */
2024 HRESULT WINAPI CoInternetGetSecurityUrlEx(IUri *pUri, IUri **ppSecUri, PSUACTION psuAction, DWORD_PTR dwReserved)
2025 {
2026     URL_SCHEME scheme_type;
2027     BSTR secure_uri;
2028     WCHAR *ret_url;
2029     HRESULT hres;
2030 
2031     TRACE("(%p,%p,%u,%u)\n", pUri, ppSecUri, psuAction, (DWORD)dwReserved);
2032 
2033     if(!pUri || !ppSecUri)
2034         return E_INVALIDARG;
2035 
2036     hres = IUri_GetDisplayUri(pUri, &secure_uri);
2037     if(FAILED(hres))
2038         return hres;
2039 
2040     hres = parse_security_url(secure_uri, psuAction, &ret_url);
2041     SysFreeString(secure_uri);
2042     if(FAILED(hres))
2043         return hres;
2044 
2045     /* File URIs have to hierarchical. */
2046     hres = IUri_GetScheme(pUri, (DWORD*)&scheme_type);
2047     if(SUCCEEDED(hres) && scheme_type == URL_SCHEME_FILE) {
2048         const WCHAR *tmp = ret_url;
2049 
2050         /* Check and see if a "//" is after the scheme name. */
2051         tmp += ARRAY_SIZE(fileW);
2052         if(*tmp != '/' || *(tmp+1) != '/')
2053             hres = E_INVALIDARG;
2054     }
2055 
2056     if(SUCCEEDED(hres))
2057         hres = CreateUri(ret_url, Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME, 0, ppSecUri);
2058     CoTaskMemFree(ret_url);
2059     return hres;
2060 }
2061 
2062 /********************************************************************
2063  *      CompareSecurityIds (URLMON.@)
2064  */
2065 HRESULT WINAPI CompareSecurityIds(BYTE *secid1, DWORD size1, BYTE *secid2, DWORD size2, DWORD reserved)
2066 {
2067     FIXME("(%p %d %p %d %x)\n", secid1, size1, secid2, size2, reserved);
2068     return E_NOTIMPL;
2069 }
2070 
2071 /********************************************************************
2072  *      IsInternetESCEnabledLocal (URLMON.108)
2073  *
2074  * Undocumented, returns TRUE if IE is running in Enhanced Security Configuration.
2075  */
2076 BOOL WINAPI IsInternetESCEnabledLocal(void)
2077 {
2078     static BOOL esc_initialized, esc_enabled;
2079 
2080     TRACE("()\n");
2081 
2082     if(!esc_initialized) {
2083         DWORD type, size, val;
2084         HKEY zone_map;
2085 
2086         static const WCHAR iehardenW[] = {'I','E','H','a','r','d','e','n',0};
2087 
2088         if(RegOpenKeyExW(HKEY_CURRENT_USER, zone_map_keyW, 0, KEY_QUERY_VALUE, &zone_map) == ERROR_SUCCESS) {
2089             size = sizeof(DWORD);
2090             if(RegQueryValueExW(zone_map, iehardenW, NULL, &type, (BYTE*)&val, &size) == ERROR_SUCCESS)
2091                 esc_enabled = type == REG_DWORD && val != 0;
2092             RegCloseKey(zone_map);
2093         }
2094         esc_initialized = TRUE;
2095     }
2096 
2097     return esc_enabled;
2098 }
2099