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