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
= malloc(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=%ld\n", res
);
98 hres
= CLSIDFromString(str_clsid
, &clsid
);
100 WARN("CLSIDFromString failed: %08lx\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
= malloc(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
= wcsdup(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 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: %08lx\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 (%lx)\n", debugstr_w(mime
), hres
);
299 hres
= CoCreateInstance(&clsid
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IInternetProtocol
, (void**)&ret
);
301 WARN("CoCreateInstance failed: %08lx\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 %ld %p %ld)\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 = %ld\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
= malloc(sizeof(mime_filter
));
375 IClassFactory_AddRef(pCF
);
377 filter
->clsid
= *rclsid
;
378 filter
->mime
= wcsdup(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
);
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 %08lx)\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("(%08lx %p %ld %ld)\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("(%ld %p %ld)\n", dwSessionMode
, ppIInternetSession
, dwReserved
);
479 ERR("dwSessionMode=%ld\n", dwSessionMode
);
481 ERR("dwReserved=%ld\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
;
507 static BOOL user_agent_set
;
509 static size_t obtain_user_agent(unsigned int version
, WCHAR
*ret
, size_t size
)
511 BOOL is_wow
, quirks
= FALSE
, use_current
= FALSE
;
512 OSVERSIONINFOW info
= {sizeof(info
)};
513 const WCHAR
*os_type
, *is_nt
;
518 if(version
& UAS_EXACTLEGACY
) {
519 version
&= ~UAS_EXACTLEGACY
;
529 FIXME("Unsupported version %u\n", version
);
533 if(version
< 7 || use_current
) {
534 EnterCriticalSection(&session_cs
);
536 len
= wcslen(user_agent
) + 1;
537 memcpy(ret
, user_agent
, min(size
, len
) * sizeof(WCHAR
));
539 LeaveCriticalSection(&session_cs
);
546 swprintf(ret
, size
, L
"Mozilla/%s (", version
< 9 ? L
"4.0" : L
"5.0");
549 swprintf(ret
+ len
, size
- len
, L
"compatible; MSIE %u.0; ", version
);
550 len
+= wcslen(ret
+ len
);
553 GetVersionExW(&info
);
554 is_nt
= info
.dwPlatformId
== VER_PLATFORM_WIN32_NT
? L
"NT " : L
"";
556 if(sizeof(void*) == 8)
558 os_type
= L
"; Win64; x64";
560 os_type
= L
"; Win64";
562 else if(IsWow64Process(GetCurrentProcess(), &is_wow
) && is_wow
)
563 os_type
= L
"; WOW64";
567 swprintf(ret
+ len
, size
- len
, L
"Windows %s%d.%d%s", is_nt
, info
.dwMajorVersion
,
568 info
.dwMinorVersion
, os_type
);
572 wcscpy(ret
+ len
, L
"; Trident/7.0");
573 len
+= ARRAY_SIZE(L
"; Trident/7.0") - 1;
577 res
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
578 "Internet Settings\\5.0\\User Agent\\Post Platform", &key
);
579 if(res
== ERROR_SUCCESS
) {
580 DWORD value_len
, idx
;
582 for(idx
= 0;; idx
++) {
586 value_len
= size
- len
- 2;
587 res
= RegEnumValueW(key
, idx
, ret
+ len
, &value_len
, NULL
, NULL
, NULL
, NULL
);
588 if(res
!= ERROR_SUCCESS
)
598 wcscpy(ret
+ len
, version
>= 11 ? L
"; rv:11.0) like Gecko" : L
")");
599 len
+= wcslen(ret
+ len
) + 1;
601 TRACE("Using user agent %s\n", debugstr_w(ret
));
605 static void ensure_user_agent(void)
607 EnterCriticalSection(&session_cs
);
611 obtain_user_agent(0, buf
, ARRAY_SIZE(buf
));
612 user_agent
= wcsdup(buf
);
615 LeaveCriticalSection(&session_cs
);
618 LPWSTR
get_useragent(void)
624 EnterCriticalSection(&session_cs
);
625 ret
= wcsdup(user_agent
);
626 LeaveCriticalSection(&session_cs
);
631 HRESULT WINAPI
UrlMkGetSessionOption(DWORD dwOption
, LPVOID pBuffer
, DWORD dwBufferLength
,
632 DWORD
* pdwBufferLength
, DWORD dwReserved
)
634 TRACE("(%lx, %p, %ld, %p)\n", dwOption
, pBuffer
, dwBufferLength
, pdwBufferLength
);
637 WARN("dwReserved = %ld\n", dwReserved
);
640 case URLMON_OPTION_USERAGENT
: {
641 HRESULT hres
= E_OUTOFMEMORY
;
647 EnterCriticalSection(&session_cs
);
651 size
= WideCharToMultiByte(CP_ACP
, 0, user_agent
, -1, NULL
, 0, NULL
, NULL
);
652 *pdwBufferLength
= size
;
653 if(size
<= dwBufferLength
) {
655 WideCharToMultiByte(CP_ACP
, 0, user_agent
, -1, pBuffer
, size
, NULL
, NULL
);
661 LeaveCriticalSection(&session_cs
);
663 /* Tests prove that we have to return E_OUTOFMEMORY on success. */
666 case URLMON_OPTION_URL_ENCODING
: {
669 if(!pBuffer
|| dwBufferLength
< sizeof(DWORD
) || !pdwBufferLength
)
672 if(!get_url_encoding(HKEY_CURRENT_USER
, &encoding
))
673 get_url_encoding(HKEY_LOCAL_MACHINE
, &encoding
);
675 *pdwBufferLength
= sizeof(DWORD
);
676 *(DWORD
*)pBuffer
= encoding
? URL_ENCODING_DISABLE_UTF8
: URL_ENCODING_ENABLE_UTF8
;
680 FIXME("unsupported option %lx\n", dwOption
);
686 /**************************************************************************
687 * UrlMkSetSessionOption (URLMON.@)
689 HRESULT WINAPI
UrlMkSetSessionOption(DWORD dwOption
, LPVOID pBuffer
, DWORD dwBufferLength
,
692 TRACE("(%lx %p %lx)\n", dwOption
, pBuffer
, dwBufferLength
);
695 case URLMON_OPTION_USERAGENT
: {
696 LPWSTR new_user_agent
;
700 if(!pBuffer
|| !dwBufferLength
)
703 for(len
=0; len
<dwBufferLength
&& buf
[len
]; len
++);
705 TRACE("Setting user agent %s\n", debugstr_an(buf
, len
));
707 size
= MultiByteToWideChar(CP_ACP
, 0, buf
, len
, NULL
, 0);
708 new_user_agent
= malloc((size
+ 1) * sizeof(WCHAR
));
710 return E_OUTOFMEMORY
;
711 MultiByteToWideChar(CP_ACP
, 0, buf
, len
, new_user_agent
, size
);
712 new_user_agent
[size
] = 0;
714 EnterCriticalSection(&session_cs
);
717 user_agent
= new_user_agent
;
718 user_agent_set
= TRUE
;
719 update_user_agent(user_agent
);
721 LeaveCriticalSection(&session_cs
);
725 FIXME("Unknown option %lx\n", dwOption
);
732 /**************************************************************************
733 * ObtainUserAgentString (URLMON.@)
735 HRESULT WINAPI
ObtainUserAgentString(DWORD option
, char *ret
, DWORD
*ret_size
)
741 TRACE("(%ld %p %p)\n", option
, ret
, ret_size
);
743 if(!ret
|| !ret_size
)
746 len
= obtain_user_agent(option
, buf
, ARRAY_SIZE(buf
));
747 size
= WideCharToMultiByte(CP_ACP
, 0, buf
, len
, NULL
, 0, NULL
, NULL
);
748 if(size
<= *ret_size
)
749 WideCharToMultiByte(CP_ACP
, 0, buf
, len
, ret
, *ret_size
+1, NULL
, NULL
);
751 hres
= E_OUTOFMEMORY
;
757 /***********************************************************************
758 * MapBrowserEmulationModeToUserAgent (URLMON.445)
759 * Undocumented, added in IE8
761 HRESULT WINAPI
MapBrowserEmulationModeToUserAgent(const void *arg
, WCHAR
**ret
)
767 TRACE("%p %p: semi-stub\n", arg
, ret
);
770 /* Native ignores first arg if custom user agent has been set, doesn't crash even if NULL */
771 size
= (wcslen(user_agent
) + 1) * sizeof(WCHAR
);
776 /* First arg seems to be a pointer to a structure of unknown size, and crashes
777 if it's too small (or filled with arbitrary values from the stack). For our
778 purposes, we only check first field which seems to be the requested version. */
779 version
= *(DWORD
*)arg
;
782 if(version
< 7 || version
> 11)
785 size
= obtain_user_agent(version
, buf
, ARRAY_SIZE(buf
)) * sizeof(WCHAR
);
789 if(!(*ret
= CoTaskMemAlloc(size
)))
790 return E_OUTOFMEMORY
;
791 memcpy(*ret
, ua
, size
);
795 void free_session(void)
797 name_space
*ns_iter
, *ns_last
;
798 mime_filter
*mf_iter
, *mf_last
;
800 LIST_FOR_EACH_ENTRY_SAFE(ns_iter
, ns_last
, &name_space_list
, name_space
, entry
) {
802 IClassFactory_Release(ns_iter
->cf
);
803 free(ns_iter
->protocol
);
807 LIST_FOR_EACH_ENTRY_SAFE(mf_iter
, mf_last
, &mime_filter_list
, mime_filter
, entry
) {
808 IClassFactory_Release(mf_iter
->cf
);