Sync with trunk r62754.
[reactos.git] / dll / win32 / urlmon / session.c
1 /*
2 * Copyright 2005-2006 Jacek Caban for CodeWeavers
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #include "urlmon_main.h"
20
21 typedef struct {
22 LPWSTR protocol;
23 IClassFactory *cf;
24 CLSID clsid;
25 BOOL urlmon;
26
27 struct list entry;
28 } name_space;
29
30 typedef struct {
31 IClassFactory *cf;
32 CLSID clsid;
33 LPWSTR mime;
34
35 struct list entry;
36 } mime_filter;
37
38 static struct list name_space_list = LIST_INIT(name_space_list);
39 static struct list mime_filter_list = LIST_INIT(mime_filter_list);
40
41 static CRITICAL_SECTION session_cs;
42 static CRITICAL_SECTION_DEBUG session_cs_dbg =
43 {
44 0, 0, &session_cs,
45 { &session_cs_dbg.ProcessLocksList, &session_cs_dbg.ProcessLocksList },
46 0, 0, { (DWORD_PTR)(__FILE__ ": session") }
47 };
48 static CRITICAL_SECTION session_cs = { &session_cs_dbg, -1, 0, 0, 0, 0 };
49
50 static const WCHAR internet_settings_keyW[] =
51 {'S','O','F','T','W','A','R','E',
52 '\\','M','i','c','r','o','s','o','f','t',
53 '\\','W','i','n','d','o','w','s',
54 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
55 '\\','I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
56
57 static name_space *find_name_space(LPCWSTR protocol)
58 {
59 name_space *iter;
60
61 LIST_FOR_EACH_ENTRY(iter, &name_space_list, name_space, entry) {
62 if(!strcmpiW(iter->protocol, protocol))
63 return iter;
64 }
65
66 return NULL;
67 }
68
69 static HRESULT get_protocol_cf(LPCWSTR schema, DWORD schema_len, CLSID *pclsid, IClassFactory **ret)
70 {
71 WCHAR str_clsid[64];
72 HKEY hkey = NULL;
73 DWORD res, type, size;
74 CLSID clsid;
75 LPWSTR wszKey;
76 HRESULT hres;
77
78 static const WCHAR wszProtocolsKey[] =
79 {'P','R','O','T','O','C','O','L','S','\\','H','a','n','d','l','e','r','\\'};
80 static const WCHAR wszCLSID[] = {'C','L','S','I','D',0};
81
82 wszKey = heap_alloc(sizeof(wszProtocolsKey)+(schema_len+1)*sizeof(WCHAR));
83 memcpy(wszKey, wszProtocolsKey, sizeof(wszProtocolsKey));
84 memcpy(wszKey + sizeof(wszProtocolsKey)/sizeof(WCHAR), schema, (schema_len+1)*sizeof(WCHAR));
85
86 res = RegOpenKeyW(HKEY_CLASSES_ROOT, wszKey, &hkey);
87 heap_free(wszKey);
88 if(res != ERROR_SUCCESS) {
89 TRACE("Could not open protocol handler key\n");
90 return MK_E_SYNTAX;
91 }
92
93 size = sizeof(str_clsid);
94 res = RegQueryValueExW(hkey, wszCLSID, NULL, &type, (LPBYTE)str_clsid, &size);
95 RegCloseKey(hkey);
96 if(res != ERROR_SUCCESS || type != REG_SZ) {
97 WARN("Could not get protocol CLSID res=%d\n", res);
98 return MK_E_SYNTAX;
99 }
100
101 hres = CLSIDFromString(str_clsid, &clsid);
102 if(FAILED(hres)) {
103 WARN("CLSIDFromString failed: %08x\n", hres);
104 return hres;
105 }
106
107 if(pclsid)
108 *pclsid = clsid;
109
110 if(!ret)
111 return S_OK;
112
113 hres = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)ret);
114 return SUCCEEDED(hres) ? S_OK : MK_E_SYNTAX;
115 }
116
117 HRESULT register_namespace(IClassFactory *cf, REFIID clsid, LPCWSTR protocol, BOOL urlmon_protocol)
118 {
119 name_space *new_name_space;
120
121 new_name_space = heap_alloc(sizeof(name_space));
122
123 if(!urlmon_protocol)
124 IClassFactory_AddRef(cf);
125 new_name_space->cf = cf;
126 new_name_space->clsid = *clsid;
127 new_name_space->urlmon = urlmon_protocol;
128 new_name_space->protocol = heap_strdupW(protocol);
129
130 EnterCriticalSection(&session_cs);
131
132 list_add_head(&name_space_list, &new_name_space->entry);
133
134 LeaveCriticalSection(&session_cs);
135
136 return S_OK;
137 }
138
139 static HRESULT unregister_namespace(IClassFactory *cf, LPCWSTR protocol)
140 {
141 name_space *iter;
142
143 EnterCriticalSection(&session_cs);
144
145 LIST_FOR_EACH_ENTRY(iter, &name_space_list, name_space, entry) {
146 if(iter->cf == cf && !strcmpiW(iter->protocol, protocol)) {
147 list_remove(&iter->entry);
148
149 LeaveCriticalSection(&session_cs);
150
151 if(!iter->urlmon)
152 IClassFactory_Release(iter->cf);
153 heap_free(iter->protocol);
154 heap_free(iter);
155 return S_OK;
156 }
157 }
158
159 LeaveCriticalSection(&session_cs);
160 return S_OK;
161 }
162
163 BOOL is_registered_protocol(LPCWSTR url)
164 {
165 DWORD schema_len;
166 WCHAR schema[64];
167 HRESULT hres;
168
169 hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
170 &schema_len, 0);
171 if(FAILED(hres))
172 return FALSE;
173
174 return get_protocol_cf(schema, schema_len, NULL, NULL) == S_OK;
175 }
176
177 IInternetProtocolInfo *get_protocol_info(LPCWSTR url)
178 {
179 IInternetProtocolInfo *ret = NULL;
180 IClassFactory *cf;
181 name_space *ns;
182 WCHAR schema[64];
183 DWORD schema_len;
184 HRESULT hres;
185
186 hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
187 &schema_len, 0);
188 if(FAILED(hres) || !schema_len)
189 return NULL;
190
191 EnterCriticalSection(&session_cs);
192
193 ns = find_name_space(schema);
194 if(ns && !ns->urlmon) {
195 hres = IClassFactory_QueryInterface(ns->cf, &IID_IInternetProtocolInfo, (void**)&ret);
196 if(FAILED(hres))
197 hres = IClassFactory_CreateInstance(ns->cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
198 }
199
200 LeaveCriticalSection(&session_cs);
201
202 if(ns && SUCCEEDED(hres))
203 return ret;
204
205 hres = get_protocol_cf(schema, schema_len, NULL, &cf);
206 if(FAILED(hres))
207 return NULL;
208
209 hres = IClassFactory_QueryInterface(cf, &IID_IInternetProtocolInfo, (void**)&ret);
210 if(FAILED(hres))
211 IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
212 IClassFactory_Release(cf);
213
214 return ret;
215 }
216
217 HRESULT get_protocol_handler(IUri *uri, CLSID *clsid, BOOL *urlmon_protocol, IClassFactory **ret)
218 {
219 name_space *ns;
220 BSTR scheme;
221 HRESULT hres;
222
223 *ret = NULL;
224
225 /* FIXME: Avoid GetSchemeName call for known schemes */
226 hres = IUri_GetSchemeName(uri, &scheme);
227 if(FAILED(hres))
228 return hres;
229
230 EnterCriticalSection(&session_cs);
231
232 ns = find_name_space(scheme);
233 if(ns) {
234 *ret = ns->cf;
235 IClassFactory_AddRef(*ret);
236 if(clsid)
237 *clsid = ns->clsid;
238 if(urlmon_protocol)
239 *urlmon_protocol = ns->urlmon;
240 }
241
242 LeaveCriticalSection(&session_cs);
243
244 if(*ret) {
245 hres = S_OK;
246 }else {
247 if(urlmon_protocol)
248 *urlmon_protocol = FALSE;
249 hres = get_protocol_cf(scheme, SysStringLen(scheme), clsid, ret);
250 }
251
252 SysFreeString(scheme);
253 return hres;
254 }
255
256 IInternetProtocol *get_mime_filter(LPCWSTR mime)
257 {
258 static const WCHAR filtersW[] = {'P','r','o','t','o','c','o','l','s',
259 '\\','F','i','l','t','e','r',0 };
260 static const WCHAR CLSIDW[] = {'C','L','S','I','D',0};
261
262 IClassFactory *cf = NULL;
263 IInternetProtocol *ret;
264 mime_filter *iter;
265 HKEY hlist, hfilter;
266 WCHAR clsidw[64];
267 CLSID clsid;
268 DWORD res, type, size;
269 HRESULT hres;
270
271 EnterCriticalSection(&session_cs);
272
273 LIST_FOR_EACH_ENTRY(iter, &mime_filter_list, mime_filter, entry) {
274 if(!strcmpW(iter->mime, mime)) {
275 cf = iter->cf;
276 break;
277 }
278 }
279
280 LeaveCriticalSection(&session_cs);
281
282 if(cf) {
283 hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&ret);
284 if(FAILED(hres)) {
285 WARN("CreateInstance failed: %08x\n", hres);
286 return NULL;
287 }
288
289 return ret;
290 }
291
292 res = RegOpenKeyW(HKEY_CLASSES_ROOT, filtersW, &hlist);
293 if(res != ERROR_SUCCESS) {
294 TRACE("Could not open MIME filters key\n");
295 return NULL;
296 }
297
298 res = RegOpenKeyW(hlist, mime, &hfilter);
299 CloseHandle(hlist);
300 if(res != ERROR_SUCCESS)
301 return NULL;
302
303 size = sizeof(clsidw);
304 res = RegQueryValueExW(hfilter, CLSIDW, NULL, &type, (LPBYTE)clsidw, &size);
305 CloseHandle(hfilter);
306 if(res!=ERROR_SUCCESS || type!=REG_SZ) {
307 WARN("Could not get filter CLSID for %s\n", debugstr_w(mime));
308 return NULL;
309 }
310
311 hres = CLSIDFromString(clsidw, &clsid);
312 if(FAILED(hres)) {
313 WARN("CLSIDFromString failed for %s (%x)\n", debugstr_w(mime), hres);
314 return NULL;
315 }
316
317 hres = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IInternetProtocol, (void**)&ret);
318 if(FAILED(hres)) {
319 WARN("CoCreateInstance failed: %08x\n", hres);
320 return NULL;
321 }
322
323 return ret;
324 }
325
326 static HRESULT WINAPI InternetSession_QueryInterface(IInternetSession *iface,
327 REFIID riid, void **ppv)
328 {
329 TRACE("(%s %p)\n", debugstr_guid(riid), ppv);
330
331 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetSession, riid)) {
332 *ppv = iface;
333 IInternetSession_AddRef(iface);
334 return S_OK;
335 }
336
337 *ppv = NULL;
338 return E_NOINTERFACE;
339 }
340
341 static ULONG WINAPI InternetSession_AddRef(IInternetSession *iface)
342 {
343 TRACE("()\n");
344 URLMON_LockModule();
345 return 2;
346 }
347
348 static ULONG WINAPI InternetSession_Release(IInternetSession *iface)
349 {
350 TRACE("()\n");
351 URLMON_UnlockModule();
352 return 1;
353 }
354
355 static HRESULT WINAPI InternetSession_RegisterNameSpace(IInternetSession *iface,
356 IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzProtocol, ULONG cPatterns,
357 const LPCWSTR *ppwzPatterns, DWORD dwReserved)
358 {
359 TRACE("(%p %s %s %d %p %d)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzProtocol),
360 cPatterns, ppwzPatterns, dwReserved);
361
362 if(cPatterns || ppwzPatterns)
363 FIXME("patterns not supported\n");
364 if(dwReserved)
365 WARN("dwReserved = %d\n", dwReserved);
366
367 if(!pCF || !pwzProtocol)
368 return E_INVALIDARG;
369
370 return register_namespace(pCF, rclsid, pwzProtocol, FALSE);
371 }
372
373 static HRESULT WINAPI InternetSession_UnregisterNameSpace(IInternetSession *iface,
374 IClassFactory *pCF, LPCWSTR pszProtocol)
375 {
376 TRACE("(%p %s)\n", pCF, debugstr_w(pszProtocol));
377
378 if(!pCF || !pszProtocol)
379 return E_INVALIDARG;
380
381 return unregister_namespace(pCF, pszProtocol);
382 }
383
384 static HRESULT WINAPI InternetSession_RegisterMimeFilter(IInternetSession *iface,
385 IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzType)
386 {
387 mime_filter *filter;
388
389 TRACE("(%p %s %s)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzType));
390
391 filter = heap_alloc(sizeof(mime_filter));
392
393 IClassFactory_AddRef(pCF);
394 filter->cf = pCF;
395 filter->clsid = *rclsid;
396 filter->mime = heap_strdupW(pwzType);
397
398 EnterCriticalSection(&session_cs);
399
400 list_add_head(&mime_filter_list, &filter->entry);
401
402 LeaveCriticalSection(&session_cs);
403
404 return S_OK;
405 }
406
407 static HRESULT WINAPI InternetSession_UnregisterMimeFilter(IInternetSession *iface,
408 IClassFactory *pCF, LPCWSTR pwzType)
409 {
410 mime_filter *iter;
411
412 TRACE("(%p %s)\n", pCF, debugstr_w(pwzType));
413
414 EnterCriticalSection(&session_cs);
415
416 LIST_FOR_EACH_ENTRY(iter, &mime_filter_list, mime_filter, entry) {
417 if(iter->cf == pCF && !strcmpW(iter->mime, pwzType)) {
418 list_remove(&iter->entry);
419
420 LeaveCriticalSection(&session_cs);
421
422 IClassFactory_Release(iter->cf);
423 heap_free(iter->mime);
424 heap_free(iter);
425 return S_OK;
426 }
427 }
428
429 LeaveCriticalSection(&session_cs);
430 return S_OK;
431 }
432
433 static HRESULT WINAPI InternetSession_CreateBinding(IInternetSession *iface,
434 LPBC pBC, LPCWSTR szUrl, IUnknown *pUnkOuter, IUnknown **ppUnk,
435 IInternetProtocol **ppOInetProt, DWORD dwOption)
436 {
437 BindProtocol *protocol;
438 HRESULT hres;
439
440 TRACE("(%p %s %p %p %p %08x)\n", pBC, debugstr_w(szUrl), pUnkOuter, ppUnk,
441 ppOInetProt, dwOption);
442
443 if(pBC || pUnkOuter || ppUnk || dwOption)
444 FIXME("Unsupported arguments\n");
445
446 hres = create_binding_protocol(FALSE, &protocol);
447 if(FAILED(hres))
448 return hres;
449
450 *ppOInetProt = (IInternetProtocol*)&protocol->IInternetProtocolEx_iface;
451 return S_OK;
452 }
453
454 static HRESULT WINAPI InternetSession_SetSessionOption(IInternetSession *iface,
455 DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength, DWORD dwReserved)
456 {
457 FIXME("(%08x %p %d %d)\n", dwOption, pBuffer, dwBufferLength, dwReserved);
458 return E_NOTIMPL;
459 }
460
461 static const IInternetSessionVtbl InternetSessionVtbl = {
462 InternetSession_QueryInterface,
463 InternetSession_AddRef,
464 InternetSession_Release,
465 InternetSession_RegisterNameSpace,
466 InternetSession_UnregisterNameSpace,
467 InternetSession_RegisterMimeFilter,
468 InternetSession_UnregisterMimeFilter,
469 InternetSession_CreateBinding,
470 InternetSession_SetSessionOption
471 };
472
473 static IInternetSession InternetSession = { &InternetSessionVtbl };
474
475 /***********************************************************************
476 * CoInternetGetSession (URLMON.@)
477 *
478 * Create a new internet session and return an IInternetSession interface
479 * representing it.
480 *
481 * PARAMS
482 * dwSessionMode [I] Mode for the internet session
483 * ppIInternetSession [O] Destination for creates IInternetSession object
484 * dwReserved [I] Reserved, must be 0.
485 *
486 * RETURNS
487 * Success: S_OK. ppIInternetSession contains the IInternetSession interface.
488 * Failure: E_INVALIDARG, if any argument is invalid, or
489 * E_OUTOFMEMORY if memory allocation fails.
490 */
491 HRESULT WINAPI CoInternetGetSession(DWORD dwSessionMode, IInternetSession **ppIInternetSession,
492 DWORD dwReserved)
493 {
494 TRACE("(%d %p %d)\n", dwSessionMode, ppIInternetSession, dwReserved);
495
496 if(dwSessionMode)
497 ERR("dwSessionMode=%d\n", dwSessionMode);
498 if(dwReserved)
499 ERR("dwReserved=%d\n", dwReserved);
500
501 IInternetSession_AddRef(&InternetSession);
502 *ppIInternetSession = &InternetSession;
503 return S_OK;
504 }
505
506 /**************************************************************************
507 * UrlMkGetSessionOption (URLMON.@)
508 */
509 static BOOL get_url_encoding(HKEY root, DWORD *encoding)
510 {
511 DWORD size = sizeof(DWORD), res, type;
512 HKEY hkey;
513
514 static const WCHAR wszUrlEncoding[] = {'U','r','l','E','n','c','o','d','i','n','g',0};
515
516 res = RegOpenKeyW(root, internet_settings_keyW, &hkey);
517 if(res != ERROR_SUCCESS)
518 return FALSE;
519
520 res = RegQueryValueExW(hkey, wszUrlEncoding, NULL, &type, (LPBYTE)encoding, &size);
521 RegCloseKey(hkey);
522
523 return res == ERROR_SUCCESS;
524 }
525
526 static LPWSTR user_agent;
527
528 static void ensure_useragent(void)
529 {
530 OSVERSIONINFOW info = {sizeof(info)};
531 const WCHAR *os_type, *is_nt;
532 WCHAR buf[512];
533 BOOL is_wow;
534
535 static const WCHAR formatW[] =
536 {'M','o','z','i','l','l','a','/','4','.','0',
537 ' ','(','c','o','m','p','a','t','i','b','l','e',';',
538 ' ','M','S','I','E',' ','8','.','0',';',
539 ' ','W','i','n','d','o','w','s',' ','%','s','%','d','.','%','d',';',
540 ' ','%','s',';',' ','T','r','i','d','e','n','t','/','5','.','0',')',0};
541 static const WCHAR ntW[] = {'N','T',' ',0};
542 static const WCHAR win32W[] = {'W','i','n','3','2',0};
543 static const WCHAR win64W[] = {'W','i','n','6','4',0};
544 static const WCHAR wow64W[] = {'W','O','W','6','4',0};
545 static const WCHAR emptyW[] = {0};
546
547 if(user_agent)
548 return;
549
550 GetVersionExW(&info);
551 is_nt = info.dwPlatformId == VER_PLATFORM_WIN32_NT ? ntW : emptyW;
552
553 if(sizeof(void*) == 8)
554 os_type = win64W;
555 else if(IsWow64Process(GetCurrentProcess(), &is_wow) && is_wow)
556 os_type = wow64W;
557 else
558 os_type = win32W;
559
560 sprintfW(buf, formatW, is_nt, info.dwMajorVersion, info.dwMinorVersion, os_type);
561 user_agent = heap_strdupW(buf);
562 }
563
564 LPWSTR get_useragent(void)
565 {
566 LPWSTR ret;
567
568 ensure_useragent();
569
570 EnterCriticalSection(&session_cs);
571 ret = heap_strdupW(user_agent);
572 LeaveCriticalSection(&session_cs);
573
574 return ret;
575 }
576
577 HRESULT WINAPI UrlMkGetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
578 DWORD* pdwBufferLength, DWORD dwReserved)
579 {
580 TRACE("(%x, %p, %d, %p)\n", dwOption, pBuffer, dwBufferLength, pdwBufferLength);
581
582 if(dwReserved)
583 WARN("dwReserved = %d\n", dwReserved);
584
585 switch(dwOption) {
586 case URLMON_OPTION_USERAGENT: {
587 HRESULT hres = E_OUTOFMEMORY;
588 DWORD size;
589
590 if(!pdwBufferLength)
591 return E_INVALIDARG;
592
593 EnterCriticalSection(&session_cs);
594
595 ensure_useragent();
596 if(user_agent) {
597 size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);
598 *pdwBufferLength = size;
599 if(size <= dwBufferLength) {
600 if(pBuffer)
601 WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pBuffer, size, NULL, NULL);
602 else
603 hres = E_INVALIDARG;
604 }
605 }
606
607 LeaveCriticalSection(&session_cs);
608
609 /* Tests prove that we have to return E_OUTOFMEMORY on success. */
610 return hres;
611 }
612 case URLMON_OPTION_URL_ENCODING: {
613 DWORD encoding = 0;
614
615 if(!pBuffer || dwBufferLength < sizeof(DWORD) || !pdwBufferLength)
616 return E_INVALIDARG;
617
618 if(!get_url_encoding(HKEY_CURRENT_USER, &encoding))
619 get_url_encoding(HKEY_LOCAL_MACHINE, &encoding);
620
621 *pdwBufferLength = sizeof(DWORD);
622 *(DWORD*)pBuffer = encoding ? URL_ENCODING_DISABLE_UTF8 : URL_ENCODING_ENABLE_UTF8;
623 return S_OK;
624 }
625 default:
626 FIXME("unsupported option %x\n", dwOption);
627 }
628
629 return E_INVALIDARG;
630 }
631
632 /**************************************************************************
633 * UrlMkSetSessionOption (URLMON.@)
634 */
635 HRESULT WINAPI UrlMkSetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
636 DWORD Reserved)
637 {
638 TRACE("(%x %p %x)\n", dwOption, pBuffer, dwBufferLength);
639
640 switch(dwOption) {
641 case URLMON_OPTION_USERAGENT: {
642 LPWSTR new_user_agent;
643 char *buf = pBuffer;
644 DWORD len, size;
645
646 if(!pBuffer || !dwBufferLength)
647 return E_INVALIDARG;
648
649 for(len=0; len<dwBufferLength && buf[len]; len++);
650
651 TRACE("Setting user agent %s\n", debugstr_an(buf, len));
652
653 size = MultiByteToWideChar(CP_ACP, 0, buf, len, NULL, 0);
654 new_user_agent = heap_alloc((size+1)*sizeof(WCHAR));
655 if(!new_user_agent)
656 return E_OUTOFMEMORY;
657 MultiByteToWideChar(CP_ACP, 0, buf, len, new_user_agent, size);
658 new_user_agent[size] = 0;
659
660 EnterCriticalSection(&session_cs);
661
662 heap_free(user_agent);
663 user_agent = new_user_agent;
664
665 LeaveCriticalSection(&session_cs);
666 break;
667 }
668 default:
669 FIXME("Unknown option %x\n", dwOption);
670 return E_INVALIDARG;
671 }
672
673 return S_OK;
674 }
675
676 /**************************************************************************
677 * ObtainUserAgentString (URLMON.@)
678 */
679 HRESULT WINAPI ObtainUserAgentString(DWORD dwOption, LPSTR pcszUAOut, DWORD *cbSize)
680 {
681 DWORD size;
682 HRESULT hres = E_FAIL;
683
684 TRACE("(%d %p %p)\n", dwOption, pcszUAOut, cbSize);
685
686 if(!pcszUAOut || !cbSize)
687 return E_INVALIDARG;
688
689 EnterCriticalSection(&session_cs);
690
691 ensure_useragent();
692 if(user_agent) {
693 size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);
694
695 if(size <= *cbSize) {
696 WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pcszUAOut, *cbSize, NULL, NULL);
697 hres = S_OK;
698 }else {
699 hres = E_OUTOFMEMORY;
700 }
701
702 *cbSize = size;
703 }
704
705 LeaveCriticalSection(&session_cs);
706 return hres;
707 }
708
709 void free_session(void)
710 {
711 name_space *ns_iter, *ns_last;
712 mime_filter *mf_iter, *mf_last;
713
714 LIST_FOR_EACH_ENTRY_SAFE(ns_iter, ns_last, &name_space_list, name_space, entry) {
715 if(!ns_iter->urlmon)
716 IClassFactory_Release(ns_iter->cf);
717 heap_free(ns_iter->protocol);
718 heap_free(ns_iter);
719 }
720
721 LIST_FOR_EACH_ENTRY_SAFE(mf_iter, mf_last, &mime_filter_list, mime_filter, entry) {
722 IClassFactory_Release(mf_iter->cf);
723 heap_free(mf_iter->mime);
724 heap_free(mf_iter);
725 }
726
727 heap_free(user_agent);
728 }