wrc: Store version and characteristics as simple integers.
[wine.git] / dlls / urlmon / session.c
blob052444dcec52176e77c17340f30ead9fab6c5f1d
1 /*
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"
20 #include "winreg.h"
22 #include "wine/debug.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
26 typedef struct {
27 LPWSTR protocol;
28 IClassFactory *cf;
29 CLSID clsid;
30 BOOL urlmon;
32 struct list entry;
33 } name_space;
35 typedef struct {
36 IClassFactory *cf;
37 CLSID clsid;
38 LPWSTR mime;
40 struct list entry;
41 } mime_filter;
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 =
49 0, 0, &session_cs,
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)
57 name_space *iter;
59 LIST_FOR_EACH_ENTRY(iter, &name_space_list, name_space, entry) {
60 if(!wcsicmp(iter->protocol, protocol))
61 return iter;
64 return NULL;
67 static HRESULT get_protocol_cf(LPCWSTR schema, DWORD schema_len, CLSID *pclsid, IClassFactory **ret)
69 WCHAR str_clsid[64];
70 HKEY hkey = NULL;
71 DWORD res, type, size;
72 CLSID clsid;
73 LPWSTR wszKey;
74 HRESULT hres;
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);
84 heap_free(wszKey);
85 if(res != ERROR_SUCCESS) {
86 TRACE("Could not open protocol handler key\n");
87 return MK_E_SYNTAX;
90 size = sizeof(str_clsid);
91 res = RegQueryValueExW(hkey, L"CLSID", NULL, &type, (BYTE*)str_clsid, &size);
92 RegCloseKey(hkey);
93 if(res != ERROR_SUCCESS || type != REG_SZ) {
94 WARN("Could not get protocol CLSID res=%ld\n", res);
95 return MK_E_SYNTAX;
98 hres = CLSIDFromString(str_clsid, &clsid);
99 if(FAILED(hres)) {
100 WARN("CLSIDFromString failed: %08lx\n", hres);
101 return hres;
104 if(pclsid)
105 *pclsid = clsid;
107 if(!ret)
108 return S_OK;
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));
120 if(!urlmon_protocol)
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);
133 return S_OK;
136 static HRESULT unregister_namespace(IClassFactory *cf, LPCWSTR protocol)
138 name_space *iter;
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);
148 if(!iter->urlmon)
149 IClassFactory_Release(iter->cf);
150 heap_free(iter->protocol);
151 heap_free(iter);
152 return S_OK;
156 LeaveCriticalSection(&session_cs);
157 return S_OK;
160 BOOL is_registered_protocol(LPCWSTR url)
162 DWORD schema_len;
163 WCHAR schema[64];
164 HRESULT hres;
166 hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, ARRAY_SIZE(schema), &schema_len, 0);
167 if(FAILED(hres))
168 return FALSE;
170 return get_protocol_cf(schema, schema_len, NULL, NULL) == S_OK;
173 IInternetProtocolInfo *get_protocol_info(LPCWSTR url)
175 IInternetProtocolInfo *ret = NULL;
176 IClassFactory *cf;
177 name_space *ns;
178 WCHAR schema[64];
179 DWORD schema_len;
180 HRESULT hres;
182 hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, ARRAY_SIZE(schema), &schema_len, 0);
183 if(FAILED(hres) || !schema_len)
184 return NULL;
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);
191 if(FAILED(hres))
192 hres = IClassFactory_CreateInstance(ns->cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
195 LeaveCriticalSection(&session_cs);
197 if(ns && SUCCEEDED(hres))
198 return ret;
200 hres = get_protocol_cf(schema, schema_len, NULL, &cf);
201 if(FAILED(hres))
202 return NULL;
204 hres = IClassFactory_QueryInterface(cf, &IID_IInternetProtocolInfo, (void**)&ret);
205 if(FAILED(hres))
206 IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
207 IClassFactory_Release(cf);
209 return ret;
212 HRESULT get_protocol_handler(IUri *uri, CLSID *clsid, IClassFactory **ret)
214 name_space *ns;
215 BSTR scheme;
216 HRESULT hres;
218 *ret = NULL;
220 /* FIXME: Avoid GetSchemeName call for known schemes */
221 hres = IUri_GetSchemeName(uri, &scheme);
222 if(FAILED(hres))
223 return hres;
225 EnterCriticalSection(&session_cs);
227 ns = find_name_space(scheme);
228 if(ns) {
229 *ret = ns->cf;
230 IClassFactory_AddRef(*ret);
231 if(clsid)
232 *clsid = ns->clsid;
235 LeaveCriticalSection(&session_cs);
237 hres = *ret ? S_OK : get_protocol_cf(scheme, SysStringLen(scheme), clsid, ret);
238 SysFreeString(scheme);
239 return hres;
242 IInternetProtocol *get_mime_filter(LPCWSTR mime)
244 IClassFactory *cf = NULL;
245 IInternetProtocol *ret;
246 mime_filter *iter;
247 HKEY hlist, hfilter;
248 WCHAR clsidw[64];
249 CLSID clsid;
250 DWORD res, type, size;
251 HRESULT hres;
253 EnterCriticalSection(&session_cs);
255 LIST_FOR_EACH_ENTRY(iter, &mime_filter_list, mime_filter, entry) {
256 if(!wcscmp(iter->mime, mime)) {
257 cf = iter->cf;
258 break;
262 LeaveCriticalSection(&session_cs);
264 if(cf) {
265 hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&ret);
266 if(FAILED(hres)) {
267 WARN("CreateInstance failed: %08lx\n", hres);
268 return NULL;
271 return ret;
274 res = RegOpenKeyW(HKEY_CLASSES_ROOT, L"Protocols\\Filter", &hlist);
275 if(res != ERROR_SUCCESS) {
276 TRACE("Could not open MIME filters key\n");
277 return NULL;
280 res = RegOpenKeyW(hlist, mime, &hfilter);
281 CloseHandle(hlist);
282 if(res != ERROR_SUCCESS)
283 return NULL;
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));
290 return NULL;
293 hres = CLSIDFromString(clsidw, &clsid);
294 if(FAILED(hres)) {
295 WARN("CLSIDFromString failed for %s (%lx)\n", debugstr_w(mime), hres);
296 return NULL;
299 hres = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void**)&ret);
300 if(FAILED(hres)) {
301 WARN("CoCreateInstance failed: %08lx\n", hres);
302 return NULL;
305 return ret;
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)) {
314 *ppv = iface;
315 IInternetSession_AddRef(iface);
316 return S_OK;
319 *ppv = NULL;
320 return E_NOINTERFACE;
323 static ULONG WINAPI InternetSession_AddRef(IInternetSession *iface)
325 TRACE("()\n");
326 URLMON_LockModule();
327 return 2;
330 static ULONG WINAPI InternetSession_Release(IInternetSession *iface)
332 TRACE("()\n");
333 URLMON_UnlockModule();
334 return 1;
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");
346 if(dwReserved)
347 WARN("dwReserved = %ld\n", dwReserved);
349 if(!pCF || !pwzProtocol)
350 return E_INVALIDARG;
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)
361 return E_INVALIDARG;
363 return unregister_namespace(pCF, pszProtocol);
366 static HRESULT WINAPI InternetSession_RegisterMimeFilter(IInternetSession *iface,
367 IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzType)
369 mime_filter *filter;
371 TRACE("(%p %s %s)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzType));
373 filter = heap_alloc(sizeof(mime_filter));
375 IClassFactory_AddRef(pCF);
376 filter->cf = 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);
386 return S_OK;
389 static HRESULT WINAPI InternetSession_UnregisterMimeFilter(IInternetSession *iface,
390 IClassFactory *pCF, LPCWSTR pwzType)
392 mime_filter *iter;
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);
406 heap_free(iter);
407 return S_OK;
411 LeaveCriticalSection(&session_cs);
412 return S_OK;
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;
420 HRESULT hres;
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);
429 if(FAILED(hres))
430 return hres;
432 *ppOInetProt = (IInternetProtocol*)&protocol->IInternetProtocolEx_iface;
433 return S_OK;
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);
440 return E_NOTIMPL;
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
461 * representing it.
463 * PARAMS
464 * dwSessionMode [I] Mode for the internet session
465 * ppIInternetSession [O] Destination for creates IInternetSession object
466 * dwReserved [I] Reserved, must be 0.
468 * RETURNS
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,
474 DWORD dwReserved)
476 TRACE("(%ld %p %ld)\n", dwSessionMode, ppIInternetSession, dwReserved);
478 if(dwSessionMode)
479 ERR("dwSessionMode=%ld\n", dwSessionMode);
480 if(dwReserved)
481 ERR("dwReserved=%ld\n", dwReserved);
483 IInternetSession_AddRef(&InternetSession);
484 *ppIInternetSession = &InternetSession;
485 return S_OK;
488 /**************************************************************************
489 * UrlMkGetSessionOption (URLMON.@)
491 static BOOL get_url_encoding(HKEY root, DWORD *encoding)
493 DWORD size = sizeof(DWORD), res, type;
494 HKEY hkey;
496 res = RegOpenKeyW(root, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &hkey);
497 if(res != ERROR_SUCCESS)
498 return FALSE;
500 res = RegQueryValueExW(hkey, L"UrlEncoding", NULL, &type, (BYTE*)encoding, &size);
501 RegCloseKey(hkey);
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;
513 DWORD res;
514 size_t len = 0;
515 HKEY key;
517 if(version & UAS_EXACTLEGACY) {
518 version &= ~UAS_EXACTLEGACY;
519 if(version == 7)
520 quirks = TRUE;
521 else
522 version = 7;
523 }else if(version < 7) {
524 version = 7;
526 if(version > 11) {
527 FIXME("Unsupported version %u\n", version);
528 version = 11;
531 if(version < 7 || (version == 7 && !quirks)) {
532 EnterCriticalSection(&session_cs);
533 if(user_agent) {
534 len = wcslen(user_agent) + 1;
535 memcpy(ret, user_agent, min(size, len) * sizeof(WCHAR));
537 LeaveCriticalSection(&session_cs);
538 if(len) return len;
541 swprintf(ret, size, L"Mozilla/%s (", version < 9 ? L"4.0" : L"5.0");
542 len = lstrlenW(ret);
543 if(version < 11) {
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)
552 #ifdef __x86_64__
553 os_type = L"; Win64; x64";
554 #else
555 os_type = L"; Win64";
556 #endif
557 else if(IsWow64Process(GetCurrentProcess(), &is_wow) && is_wow)
558 os_type = L"; WOW64";
559 else
560 os_type = L"";
562 swprintf(ret + len, size - len, L"Windows %s%d.%d%s", is_nt, info.dwMajorVersion,
563 info.dwMinorVersion, os_type);
564 len = lstrlenW(ret);
566 if(!quirks) {
567 wcscpy(ret + len, L"; Trident/7.0");
568 len += ARRAY_SIZE(L"; Trident/7.0") - 1;
571 if(version < 9) {
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++) {
578 ret[len++] = ';';
579 ret[len++] = ' ';
581 value_len = size - len - 2;
582 res = RegEnumValueW(key, idx, ret + len, &value_len, NULL, NULL, NULL, NULL);
583 if(res != ERROR_SUCCESS)
584 break;
586 len += value_len;
589 RegCloseKey(key);
590 if(idx) len -= 2;
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));
597 return len;
600 static void ensure_user_agent(void)
602 EnterCriticalSection(&session_cs);
604 if(!user_agent) {
605 WCHAR buf[1024];
606 obtain_user_agent(0, buf, ARRAY_SIZE(buf));
607 user_agent = heap_strdupW(buf);
610 LeaveCriticalSection(&session_cs);
613 LPWSTR get_useragent(void)
615 LPWSTR ret;
617 ensure_user_agent();
619 EnterCriticalSection(&session_cs);
620 ret = heap_strdupW(user_agent);
621 LeaveCriticalSection(&session_cs);
623 return ret;
626 HRESULT WINAPI UrlMkGetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
627 DWORD* pdwBufferLength, DWORD dwReserved)
629 TRACE("(%lx, %p, %ld, %p)\n", dwOption, pBuffer, dwBufferLength, pdwBufferLength);
631 if(dwReserved)
632 WARN("dwReserved = %ld\n", dwReserved);
634 switch(dwOption) {
635 case URLMON_OPTION_USERAGENT: {
636 HRESULT hres = E_OUTOFMEMORY;
637 DWORD size;
639 if(!pdwBufferLength)
640 return E_INVALIDARG;
642 EnterCriticalSection(&session_cs);
644 ensure_user_agent();
645 if(user_agent) {
646 size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);
647 *pdwBufferLength = size;
648 if(size <= dwBufferLength) {
649 if(pBuffer)
650 WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pBuffer, size, NULL, NULL);
651 else
652 hres = E_INVALIDARG;
656 LeaveCriticalSection(&session_cs);
658 /* Tests prove that we have to return E_OUTOFMEMORY on success. */
659 return hres;
661 case URLMON_OPTION_URL_ENCODING: {
662 DWORD encoding = 0;
664 if(!pBuffer || dwBufferLength < sizeof(DWORD) || !pdwBufferLength)
665 return E_INVALIDARG;
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;
672 return S_OK;
674 default:
675 FIXME("unsupported option %lx\n", dwOption);
678 return E_INVALIDARG;
681 /**************************************************************************
682 * UrlMkSetSessionOption (URLMON.@)
684 HRESULT WINAPI UrlMkSetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
685 DWORD Reserved)
687 TRACE("(%lx %p %lx)\n", dwOption, pBuffer, dwBufferLength);
689 switch(dwOption) {
690 case URLMON_OPTION_USERAGENT: {
691 LPWSTR new_user_agent;
692 char *buf = pBuffer;
693 DWORD len, size;
695 if(!pBuffer || !dwBufferLength)
696 return E_INVALIDARG;
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));
704 if(!new_user_agent)
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);
716 break;
718 default:
719 FIXME("Unknown option %lx\n", dwOption);
720 return E_INVALIDARG;
723 return S_OK;
726 /**************************************************************************
727 * ObtainUserAgentString (URLMON.@)
729 HRESULT WINAPI ObtainUserAgentString(DWORD option, char *ret, DWORD *ret_size)
731 DWORD size, len;
732 WCHAR buf[1024];
733 HRESULT hres = S_OK;
735 TRACE("(%ld %p %p)\n", option, ret, ret_size);
737 if(!ret || !ret_size)
738 return E_INVALIDARG;
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);
744 else
745 hres = E_OUTOFMEMORY;
747 *ret_size = size;
748 return hres;
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) {
757 if(!ns_iter->urlmon)
758 IClassFactory_Release(ns_iter->cf);
759 heap_free(ns_iter->protocol);
760 heap_free(ns_iter);
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);
766 heap_free(mf_iter);
769 heap_free(user_agent);