2 * Copyright 2005-2006 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "urlmon_main.h"
22 #include "wine/debug.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(urlmon
);
43 static struct list name_space_list
= LIST_INIT(name_space_list
);
44 static struct list mime_filter_list
= LIST_INIT(mime_filter_list
);
46 static CRITICAL_SECTION session_cs
;
47 static CRITICAL_SECTION_DEBUG session_cs_dbg
=
50 { &session_cs_dbg
.ProcessLocksList
, &session_cs_dbg
.ProcessLocksList
},
51 0, 0, { (DWORD_PTR
)(__FILE__
": session") }
53 static CRITICAL_SECTION session_cs
= { &session_cs_dbg
, -1, 0, 0, 0, 0 };
55 static name_space
*find_name_space(LPCWSTR protocol
)
59 LIST_FOR_EACH_ENTRY(iter
, &name_space_list
, name_space
, entry
) {
60 if(!wcsicmp(iter
->protocol
, protocol
))
67 static HRESULT
get_protocol_cf(LPCWSTR schema
, DWORD schema_len
, CLSID
*pclsid
, IClassFactory
**ret
)
71 DWORD res
, type
, size
;
76 static const WCHAR wszProtocolsKey
[] =
77 {'P','R','O','T','O','C','O','L','S','\\','H','a','n','d','l','e','r','\\'};
79 wszKey
= heap_alloc(sizeof(wszProtocolsKey
)+(schema_len
+1)*sizeof(WCHAR
));
80 memcpy(wszKey
, wszProtocolsKey
, sizeof(wszProtocolsKey
));
81 memcpy(wszKey
+ ARRAY_SIZE(wszProtocolsKey
), schema
, (schema_len
+1)*sizeof(WCHAR
));
83 res
= RegOpenKeyW(HKEY_CLASSES_ROOT
, wszKey
, &hkey
);
85 if(res
!= ERROR_SUCCESS
) {
86 TRACE("Could not open protocol handler key\n");
90 size
= sizeof(str_clsid
);
91 res
= RegQueryValueExW(hkey
, L
"CLSID", NULL
, &type
, (BYTE
*)str_clsid
, &size
);
93 if(res
!= ERROR_SUCCESS
|| type
!= REG_SZ
) {
94 WARN("Could not get protocol CLSID res=%d\n", res
);
98 hres
= CLSIDFromString(str_clsid
, &clsid
);
100 WARN("CLSIDFromString failed: %08x\n", hres
);
110 hres
= CoGetClassObject(&clsid
, CLSCTX_INPROC_SERVER
, NULL
, &IID_IClassFactory
, (void**)ret
);
111 return SUCCEEDED(hres
) ? S_OK
: MK_E_SYNTAX
;
114 HRESULT
register_namespace(IClassFactory
*cf
, REFIID clsid
, LPCWSTR protocol
, BOOL urlmon_protocol
)
116 name_space
*new_name_space
;
118 new_name_space
= heap_alloc(sizeof(name_space
));
121 IClassFactory_AddRef(cf
);
122 new_name_space
->cf
= cf
;
123 new_name_space
->clsid
= *clsid
;
124 new_name_space
->urlmon
= urlmon_protocol
;
125 new_name_space
->protocol
= heap_strdupW(protocol
);
127 EnterCriticalSection(&session_cs
);
129 list_add_head(&name_space_list
, &new_name_space
->entry
);
131 LeaveCriticalSection(&session_cs
);
136 static HRESULT
unregister_namespace(IClassFactory
*cf
, LPCWSTR protocol
)
140 EnterCriticalSection(&session_cs
);
142 LIST_FOR_EACH_ENTRY(iter
, &name_space_list
, name_space
, entry
) {
143 if(iter
->cf
== cf
&& !wcsicmp(iter
->protocol
, protocol
)) {
144 list_remove(&iter
->entry
);
146 LeaveCriticalSection(&session_cs
);
149 IClassFactory_Release(iter
->cf
);
150 heap_free(iter
->protocol
);
156 LeaveCriticalSection(&session_cs
);
160 BOOL
is_registered_protocol(LPCWSTR url
)
166 hres
= CoInternetParseUrl(url
, PARSE_SCHEMA
, 0, schema
, ARRAY_SIZE(schema
), &schema_len
, 0);
170 return get_protocol_cf(schema
, schema_len
, NULL
, NULL
) == S_OK
;
173 IInternetProtocolInfo
*get_protocol_info(LPCWSTR url
)
175 IInternetProtocolInfo
*ret
= NULL
;
182 hres
= CoInternetParseUrl(url
, PARSE_SCHEMA
, 0, schema
, ARRAY_SIZE(schema
), &schema_len
, 0);
183 if(FAILED(hres
) || !schema_len
)
186 EnterCriticalSection(&session_cs
);
188 ns
= find_name_space(schema
);
189 if(ns
&& !ns
->urlmon
) {
190 hres
= IClassFactory_QueryInterface(ns
->cf
, &IID_IInternetProtocolInfo
, (void**)&ret
);
192 hres
= IClassFactory_CreateInstance(ns
->cf
, NULL
, &IID_IInternetProtocolInfo
, (void**)&ret
);
195 LeaveCriticalSection(&session_cs
);
197 if(ns
&& SUCCEEDED(hres
))
200 hres
= get_protocol_cf(schema
, schema_len
, NULL
, &cf
);
204 hres
= IClassFactory_QueryInterface(cf
, &IID_IInternetProtocolInfo
, (void**)&ret
);
206 IClassFactory_CreateInstance(cf
, NULL
, &IID_IInternetProtocolInfo
, (void**)&ret
);
207 IClassFactory_Release(cf
);
212 HRESULT
get_protocol_handler(IUri
*uri
, CLSID
*clsid
, IClassFactory
**ret
)
220 /* FIXME: Avoid GetSchemeName call for known schemes */
221 hres
= IUri_GetSchemeName(uri
, &scheme
);
225 EnterCriticalSection(&session_cs
);
227 ns
= find_name_space(scheme
);
230 IClassFactory_AddRef(*ret
);
235 LeaveCriticalSection(&session_cs
);
237 hres
= *ret
? S_OK
: get_protocol_cf(scheme
, SysStringLen(scheme
), clsid
, ret
);
238 SysFreeString(scheme
);
242 IInternetProtocol
*get_mime_filter(LPCWSTR mime
)
244 IClassFactory
*cf
= NULL
;
245 IInternetProtocol
*ret
;
250 DWORD res
, type
, size
;
253 EnterCriticalSection(&session_cs
);
255 LIST_FOR_EACH_ENTRY(iter
, &mime_filter_list
, mime_filter
, entry
) {
256 if(!wcscmp(iter
->mime
, mime
)) {
262 LeaveCriticalSection(&session_cs
);
265 hres
= IClassFactory_CreateInstance(cf
, NULL
, &IID_IInternetProtocol
, (void**)&ret
);
267 WARN("CreateInstance failed: %08x\n", hres
);
274 res
= RegOpenKeyW(HKEY_CLASSES_ROOT
, L
"Protocols\\Filter", &hlist
);
275 if(res
!= ERROR_SUCCESS
) {
276 TRACE("Could not open MIME filters key\n");
280 res
= RegOpenKeyW(hlist
, mime
, &hfilter
);
282 if(res
!= ERROR_SUCCESS
)
285 size
= sizeof(clsidw
);
286 res
= RegQueryValueExW(hfilter
, L
"CLSID", NULL
, &type
, (BYTE
*)clsidw
, &size
);
287 CloseHandle(hfilter
);
288 if(res
!=ERROR_SUCCESS
|| type
!=REG_SZ
) {
289 WARN("Could not get filter CLSID for %s\n", debugstr_w(mime
));
293 hres
= CLSIDFromString(clsidw
, &clsid
);
295 WARN("CLSIDFromString failed for %s (%x)\n", debugstr_w(mime
), hres
);
299 hres
= CoCreateInstance(&clsid
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IInternetProtocol
, (void**)&ret
);
301 WARN("CoCreateInstance failed: %08x\n", hres
);
308 static HRESULT WINAPI
InternetSession_QueryInterface(IInternetSession
*iface
,
309 REFIID riid
, void **ppv
)
311 TRACE("(%s %p)\n", debugstr_guid(riid
), ppv
);
313 if(IsEqualGUID(&IID_IUnknown
, riid
) || IsEqualGUID(&IID_IInternetSession
, riid
)) {
315 IInternetSession_AddRef(iface
);
320 return E_NOINTERFACE
;
323 static ULONG WINAPI
InternetSession_AddRef(IInternetSession
*iface
)
330 static ULONG WINAPI
InternetSession_Release(IInternetSession
*iface
)
333 URLMON_UnlockModule();
337 static HRESULT WINAPI
InternetSession_RegisterNameSpace(IInternetSession
*iface
,
338 IClassFactory
*pCF
, REFCLSID rclsid
, LPCWSTR pwzProtocol
, ULONG cPatterns
,
339 const LPCWSTR
*ppwzPatterns
, DWORD dwReserved
)
341 TRACE("(%p %s %s %d %p %d)\n", pCF
, debugstr_guid(rclsid
), debugstr_w(pwzProtocol
),
342 cPatterns
, ppwzPatterns
, dwReserved
);
344 if(cPatterns
|| ppwzPatterns
)
345 FIXME("patterns not supported\n");
347 WARN("dwReserved = %d\n", dwReserved
);
349 if(!pCF
|| !pwzProtocol
)
352 return register_namespace(pCF
, rclsid
, pwzProtocol
, FALSE
);
355 static HRESULT WINAPI
InternetSession_UnregisterNameSpace(IInternetSession
*iface
,
356 IClassFactory
*pCF
, LPCWSTR pszProtocol
)
358 TRACE("(%p %s)\n", pCF
, debugstr_w(pszProtocol
));
360 if(!pCF
|| !pszProtocol
)
363 return unregister_namespace(pCF
, pszProtocol
);
366 static HRESULT WINAPI
InternetSession_RegisterMimeFilter(IInternetSession
*iface
,
367 IClassFactory
*pCF
, REFCLSID rclsid
, LPCWSTR pwzType
)
371 TRACE("(%p %s %s)\n", pCF
, debugstr_guid(rclsid
), debugstr_w(pwzType
));
373 filter
= heap_alloc(sizeof(mime_filter
));
375 IClassFactory_AddRef(pCF
);
377 filter
->clsid
= *rclsid
;
378 filter
->mime
= heap_strdupW(pwzType
);
380 EnterCriticalSection(&session_cs
);
382 list_add_head(&mime_filter_list
, &filter
->entry
);
384 LeaveCriticalSection(&session_cs
);
389 static HRESULT WINAPI
InternetSession_UnregisterMimeFilter(IInternetSession
*iface
,
390 IClassFactory
*pCF
, LPCWSTR pwzType
)
394 TRACE("(%p %s)\n", pCF
, debugstr_w(pwzType
));
396 EnterCriticalSection(&session_cs
);
398 LIST_FOR_EACH_ENTRY(iter
, &mime_filter_list
, mime_filter
, entry
) {
399 if(iter
->cf
== pCF
&& !wcscmp(iter
->mime
, pwzType
)) {
400 list_remove(&iter
->entry
);
402 LeaveCriticalSection(&session_cs
);
404 IClassFactory_Release(iter
->cf
);
405 heap_free(iter
->mime
);
411 LeaveCriticalSection(&session_cs
);
415 static HRESULT WINAPI
InternetSession_CreateBinding(IInternetSession
*iface
,
416 LPBC pBC
, LPCWSTR szUrl
, IUnknown
*pUnkOuter
, IUnknown
**ppUnk
,
417 IInternetProtocol
**ppOInetProt
, DWORD dwOption
)
419 BindProtocol
*protocol
;
422 TRACE("(%p %s %p %p %p %08x)\n", pBC
, debugstr_w(szUrl
), pUnkOuter
, ppUnk
,
423 ppOInetProt
, dwOption
);
425 if(pBC
|| pUnkOuter
|| ppUnk
|| dwOption
)
426 FIXME("Unsupported arguments\n");
428 hres
= create_binding_protocol(&protocol
);
432 *ppOInetProt
= (IInternetProtocol
*)&protocol
->IInternetProtocolEx_iface
;
436 static HRESULT WINAPI
InternetSession_SetSessionOption(IInternetSession
*iface
,
437 DWORD dwOption
, LPVOID pBuffer
, DWORD dwBufferLength
, DWORD dwReserved
)
439 FIXME("(%08x %p %d %d)\n", dwOption
, pBuffer
, dwBufferLength
, dwReserved
);
443 static const IInternetSessionVtbl InternetSessionVtbl
= {
444 InternetSession_QueryInterface
,
445 InternetSession_AddRef
,
446 InternetSession_Release
,
447 InternetSession_RegisterNameSpace
,
448 InternetSession_UnregisterNameSpace
,
449 InternetSession_RegisterMimeFilter
,
450 InternetSession_UnregisterMimeFilter
,
451 InternetSession_CreateBinding
,
452 InternetSession_SetSessionOption
455 static IInternetSession InternetSession
= { &InternetSessionVtbl
};
457 /***********************************************************************
458 * CoInternetGetSession (URLMON.@)
460 * Create a new internet session and return an IInternetSession interface
464 * dwSessionMode [I] Mode for the internet session
465 * ppIInternetSession [O] Destination for creates IInternetSession object
466 * dwReserved [I] Reserved, must be 0.
469 * Success: S_OK. ppIInternetSession contains the IInternetSession interface.
470 * Failure: E_INVALIDARG, if any argument is invalid, or
471 * E_OUTOFMEMORY if memory allocation fails.
473 HRESULT WINAPI
CoInternetGetSession(DWORD dwSessionMode
, IInternetSession
**ppIInternetSession
,
476 TRACE("(%d %p %d)\n", dwSessionMode
, ppIInternetSession
, dwReserved
);
479 ERR("dwSessionMode=%d\n", dwSessionMode
);
481 ERR("dwReserved=%d\n", dwReserved
);
483 IInternetSession_AddRef(&InternetSession
);
484 *ppIInternetSession
= &InternetSession
;
488 /**************************************************************************
489 * UrlMkGetSessionOption (URLMON.@)
491 static BOOL
get_url_encoding(HKEY root
, DWORD
*encoding
)
493 DWORD size
= sizeof(DWORD
), res
, type
;
496 res
= RegOpenKeyW(root
, L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey
);
497 if(res
!= ERROR_SUCCESS
)
500 res
= RegQueryValueExW(hkey
, L
"UrlEncoding", NULL
, &type
, (BYTE
*)encoding
, &size
);
503 return res
== ERROR_SUCCESS
;
506 static LPWSTR user_agent
;
508 static size_t obtain_user_agent(unsigned int version
, WCHAR
*ret
, size_t size
)
510 OSVERSIONINFOW info
= {sizeof(info
)};
511 const WCHAR
*os_type
, *is_nt
;
512 BOOL is_wow
, quirks
= FALSE
;
517 if(version
& UAS_EXACTLEGACY
) {
518 version
&= ~UAS_EXACTLEGACY
;
523 }else if(version
< 7) {
527 FIXME("Unsupported version %u\n", version
);
531 if(version
< 7 || (version
== 7 && !quirks
)) {
532 EnterCriticalSection(&session_cs
);
534 len
= wcslen(user_agent
) + 1;
535 memcpy(ret
, user_agent
, min(size
, len
) * sizeof(WCHAR
));
537 LeaveCriticalSection(&session_cs
);
541 swprintf(ret
, size
, L
"Mozilla/%s (", version
< 9 ? L
"4.0" : L
"5.0");
544 swprintf(ret
+ len
, size
- len
, L
"compatible; MSIE %u.0; ", version
);
545 len
+= wcslen(ret
+ len
);
548 GetVersionExW(&info
);
549 is_nt
= info
.dwPlatformId
== VER_PLATFORM_WIN32_NT
? L
"NT " : L
"";
551 if(sizeof(void*) == 8)
553 os_type
= L
"; Win64; x64";
555 os_type
= L
"; Win64";
557 else if(IsWow64Process(GetCurrentProcess(), &is_wow
) && is_wow
)
558 os_type
= L
"; WOW64";
562 swprintf(ret
+ len
, size
- len
, L
"Windows %s%d.%d%s", is_nt
, info
.dwMajorVersion
,
563 info
.dwMinorVersion
, os_type
);
567 wcscpy(ret
+ len
, L
"; Trident/7.0");
568 len
+= ARRAY_SIZE(L
"; Trident/7.0") - 1;
572 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
573 "Internet Settings\\5.0\\User Agent\\Post Platform", &key
);
574 if(res
== ERROR_SUCCESS
) {
575 DWORD value_len
, idx
;
577 for(idx
= 0;; idx
++) {
581 value_len
= size
- len
- 2;
582 res
= RegEnumValueW(key
, idx
, ret
+ len
, &value_len
, NULL
, NULL
, NULL
, NULL
);
583 if(res
!= ERROR_SUCCESS
)
593 wcscpy(ret
+ len
, version
>= 11 ? L
"; rv:11.0) like Gecko" : L
")");
594 len
+= wcslen(ret
+ len
) + 1;
596 TRACE("Using user agent %s\n", debugstr_w(ret
));
600 static void ensure_user_agent(void)
602 EnterCriticalSection(&session_cs
);
606 obtain_user_agent(0, buf
, ARRAY_SIZE(buf
));
607 user_agent
= heap_strdupW(buf
);
610 LeaveCriticalSection(&session_cs
);
613 LPWSTR
get_useragent(void)
619 EnterCriticalSection(&session_cs
);
620 ret
= heap_strdupW(user_agent
);
621 LeaveCriticalSection(&session_cs
);
626 HRESULT WINAPI
UrlMkGetSessionOption(DWORD dwOption
, LPVOID pBuffer
, DWORD dwBufferLength
,
627 DWORD
* pdwBufferLength
, DWORD dwReserved
)
629 TRACE("(%x, %p, %d, %p)\n", dwOption
, pBuffer
, dwBufferLength
, pdwBufferLength
);
632 WARN("dwReserved = %d\n", dwReserved
);
635 case URLMON_OPTION_USERAGENT
: {
636 HRESULT hres
= E_OUTOFMEMORY
;
642 EnterCriticalSection(&session_cs
);
646 size
= WideCharToMultiByte(CP_ACP
, 0, user_agent
, -1, NULL
, 0, NULL
, NULL
);
647 *pdwBufferLength
= size
;
648 if(size
<= dwBufferLength
) {
650 WideCharToMultiByte(CP_ACP
, 0, user_agent
, -1, pBuffer
, size
, NULL
, NULL
);
656 LeaveCriticalSection(&session_cs
);
658 /* Tests prove that we have to return E_OUTOFMEMORY on success. */
661 case URLMON_OPTION_URL_ENCODING
: {
664 if(!pBuffer
|| dwBufferLength
< sizeof(DWORD
) || !pdwBufferLength
)
667 if(!get_url_encoding(HKEY_CURRENT_USER
, &encoding
))
668 get_url_encoding(HKEY_LOCAL_MACHINE
, &encoding
);
670 *pdwBufferLength
= sizeof(DWORD
);
671 *(DWORD
*)pBuffer
= encoding
? URL_ENCODING_DISABLE_UTF8
: URL_ENCODING_ENABLE_UTF8
;
675 FIXME("unsupported option %x\n", dwOption
);
681 /**************************************************************************
682 * UrlMkSetSessionOption (URLMON.@)
684 HRESULT WINAPI
UrlMkSetSessionOption(DWORD dwOption
, LPVOID pBuffer
, DWORD dwBufferLength
,
687 TRACE("(%x %p %x)\n", dwOption
, pBuffer
, dwBufferLength
);
690 case URLMON_OPTION_USERAGENT
: {
691 LPWSTR new_user_agent
;
695 if(!pBuffer
|| !dwBufferLength
)
698 for(len
=0; len
<dwBufferLength
&& buf
[len
]; len
++);
700 TRACE("Setting user agent %s\n", debugstr_an(buf
, len
));
702 size
= MultiByteToWideChar(CP_ACP
, 0, buf
, len
, NULL
, 0);
703 new_user_agent
= heap_alloc((size
+1)*sizeof(WCHAR
));
705 return E_OUTOFMEMORY
;
706 MultiByteToWideChar(CP_ACP
, 0, buf
, len
, new_user_agent
, size
);
707 new_user_agent
[size
] = 0;
709 EnterCriticalSection(&session_cs
);
711 heap_free(user_agent
);
712 user_agent
= new_user_agent
;
713 update_user_agent(user_agent
);
715 LeaveCriticalSection(&session_cs
);
719 FIXME("Unknown option %x\n", dwOption
);
726 /**************************************************************************
727 * ObtainUserAgentString (URLMON.@)
729 HRESULT WINAPI
ObtainUserAgentString(DWORD option
, char *ret
, DWORD
*ret_size
)
735 TRACE("(%d %p %p)\n", option
, ret
, ret_size
);
737 if(!ret
|| !ret_size
)
740 len
= obtain_user_agent(option
, buf
, ARRAY_SIZE(buf
));
741 size
= WideCharToMultiByte(CP_ACP
, 0, buf
, len
, NULL
, 0, NULL
, NULL
);
742 if(size
<= *ret_size
)
743 WideCharToMultiByte(CP_ACP
, 0, buf
, len
, ret
, *ret_size
+1, NULL
, NULL
);
745 hres
= E_OUTOFMEMORY
;
751 void free_session(void)
753 name_space
*ns_iter
, *ns_last
;
754 mime_filter
*mf_iter
, *mf_last
;
756 LIST_FOR_EACH_ENTRY_SAFE(ns_iter
, ns_last
, &name_space_list
, name_space
, entry
) {
758 IClassFactory_Release(ns_iter
->cf
);
759 heap_free(ns_iter
->protocol
);
763 LIST_FOR_EACH_ENTRY_SAFE(mf_iter
, mf_last
, &mime_filter_list
, mime_filter
, entry
) {
764 IClassFactory_Release(mf_iter
->cf
);
765 heap_free(mf_iter
->mime
);
769 heap_free(user_agent
);