kernel32: Don't try to load an empty initialization file.
[wine] / dlls / 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 #include "winreg.h"
21
22 #include "wine/debug.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
25
26 typedef struct name_space {
27     LPWSTR protocol;
28     IClassFactory *cf;
29     CLSID clsid;
30     BOOL urlmon;
31
32     struct name_space *next;
33 } name_space;
34
35 typedef struct mime_filter {
36     IClassFactory *cf;
37     CLSID clsid;
38     LPWSTR mime;
39
40     struct mime_filter *next;
41 } mime_filter;
42
43 static name_space *name_space_list = NULL;
44 static mime_filter *mime_filter_list = NULL;
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 name_space *find_name_space(LPCWSTR protocol)
56 {
57     name_space *iter;
58
59     for(iter = name_space_list; iter; iter = iter->next) {
60         if(!strcmpW(iter->protocol, protocol))
61             return iter;
62     }
63
64     return NULL;
65 }
66
67 static HRESULT get_protocol_cf(LPCWSTR schema, DWORD schema_len, CLSID *pclsid, IClassFactory **ret)
68 {
69     WCHAR str_clsid[64];
70     HKEY hkey = NULL;
71     DWORD res, type, size;
72     CLSID clsid;
73     LPWSTR wszKey;
74     HRESULT hres;
75
76     static const WCHAR wszProtocolsKey[] =
77         {'P','R','O','T','O','C','O','L','S','\\','H','a','n','d','l','e','r','\\'};
78     static const WCHAR wszCLSID[] = {'C','L','S','I','D',0};
79
80     wszKey = heap_alloc(sizeof(wszProtocolsKey)+(schema_len+1)*sizeof(WCHAR));
81     memcpy(wszKey, wszProtocolsKey, sizeof(wszProtocolsKey));
82     memcpy(wszKey + sizeof(wszProtocolsKey)/sizeof(WCHAR), schema, (schema_len+1)*sizeof(WCHAR));
83
84     res = RegOpenKeyW(HKEY_CLASSES_ROOT, wszKey, &hkey);
85     heap_free(wszKey);
86     if(res != ERROR_SUCCESS) {
87         TRACE("Could not open protocol handler key\n");
88         return E_FAIL;
89     }
90     
91     size = sizeof(str_clsid);
92     res = RegQueryValueExW(hkey, wszCLSID, NULL, &type, (LPBYTE)str_clsid, &size);
93     RegCloseKey(hkey);
94     if(res != ERROR_SUCCESS || type != REG_SZ) {
95         WARN("Could not get protocol CLSID res=%d\n", res);
96         return E_FAIL;
97     }
98
99     hres = CLSIDFromString(str_clsid, &clsid);
100     if(FAILED(hres)) {
101         WARN("CLSIDFromString failed: %08x\n", hres);
102         return hres;
103     }
104
105     if(pclsid)
106         *pclsid = clsid;
107
108     if(!ret)
109         return S_OK;
110
111     return CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)ret);
112 }
113
114 static HRESULT register_namespace(IClassFactory *cf, REFIID clsid, LPCWSTR protocol, BOOL urlmon_protocol)
115 {
116     name_space *new_name_space;
117
118     new_name_space = heap_alloc(sizeof(name_space));
119
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);
126
127     EnterCriticalSection(&session_cs);
128
129     new_name_space->next = name_space_list;
130     name_space_list = new_name_space;
131
132     LeaveCriticalSection(&session_cs);
133
134     return S_OK;
135 }
136
137 static HRESULT unregister_namespace(IClassFactory *cf, LPCWSTR protocol)
138 {
139     name_space *iter, *last = NULL;
140
141     EnterCriticalSection(&session_cs);
142
143     for(iter = name_space_list; iter; iter = iter->next) {
144         if(iter->cf == cf && !strcmpW(iter->protocol, protocol))
145             break;
146         last = iter;
147     }
148
149     if(iter) {
150         if(last)
151             last->next = iter->next;
152         else
153             name_space_list = iter->next;
154     }
155
156     LeaveCriticalSection(&session_cs);
157
158     if(iter) {
159         if(!iter->urlmon)
160             IClassFactory_Release(iter->cf);
161         heap_free(iter->protocol);
162         heap_free(iter);
163     }
164
165     return S_OK;
166 }
167
168
169 void register_urlmon_namespace(IClassFactory *cf, REFIID clsid, LPCWSTR protocol, BOOL do_register)
170 {
171     if(do_register)
172         register_namespace(cf, clsid, protocol, TRUE);
173     else
174         unregister_namespace(cf, protocol);
175 }
176
177 BOOL is_registered_protocol(LPCWSTR url)
178 {
179     DWORD schema_len;
180     WCHAR schema[64];
181     HRESULT hres;
182
183     hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
184             &schema_len, 0);
185     if(FAILED(hres))
186         return FALSE;
187
188     return get_protocol_cf(schema, schema_len, NULL, NULL) == S_OK;
189 }
190
191 IInternetProtocolInfo *get_protocol_info(LPCWSTR url)
192 {
193     IInternetProtocolInfo *ret = NULL;
194     IClassFactory *cf;
195     name_space *ns;
196     WCHAR schema[64];
197     DWORD schema_len;
198     HRESULT hres;
199
200     hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
201             &schema_len, 0);
202     if(FAILED(hres) || !schema_len)
203         return NULL;
204
205     EnterCriticalSection(&session_cs);
206
207     ns = find_name_space(schema);
208     if(ns && !ns->urlmon) {
209         hres = IClassFactory_QueryInterface(ns->cf, &IID_IInternetProtocolInfo, (void**)&ret);
210         if(FAILED(hres))
211             hres = IClassFactory_CreateInstance(ns->cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
212     }
213
214     LeaveCriticalSection(&session_cs);
215
216     if(ns && SUCCEEDED(hres))
217         return ret;
218
219     hres = get_protocol_cf(schema, schema_len, NULL, &cf);
220     if(FAILED(hres))
221         return NULL;
222
223     hres = IClassFactory_QueryInterface(cf, &IID_IInternetProtocolInfo, (void**)&ret);
224     if(FAILED(hres))
225         IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
226     IClassFactory_Release(cf);
227
228     return ret;
229 }
230
231 HRESULT get_protocol_handler(LPCWSTR url, CLSID *clsid, IClassFactory **ret)
232 {
233     name_space *ns;
234     WCHAR schema[64];
235     DWORD schema_len;
236     HRESULT hres;
237
238     *ret = NULL;
239
240     hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
241             &schema_len, 0);
242     if(FAILED(hres) || !schema_len)
243         return schema_len ? hres : E_FAIL;
244
245     EnterCriticalSection(&session_cs);
246
247     ns = find_name_space(schema);
248     if(ns) {
249         *ret = ns->cf;
250         IClassFactory_AddRef(*ret);
251         if(clsid)
252             *clsid = ns->clsid;
253     }
254
255     LeaveCriticalSection(&session_cs);
256
257     if(*ret)
258         return S_OK;
259
260     return get_protocol_cf(schema, schema_len, clsid, ret);
261 }
262
263 static HRESULT WINAPI InternetSession_QueryInterface(IInternetSession *iface,
264         REFIID riid, void **ppv)
265 {
266     TRACE("(%s %p)\n", debugstr_guid(riid), ppv);
267
268     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetSession, riid)) {
269         *ppv = iface;
270         IInternetSession_AddRef(iface);
271         return S_OK;
272     }
273
274     *ppv = NULL;
275     return E_NOINTERFACE;
276 }
277
278 static ULONG WINAPI InternetSession_AddRef(IInternetSession *iface)
279 {
280     TRACE("()\n");
281     URLMON_LockModule();
282     return 2;
283 }
284
285 static ULONG WINAPI InternetSession_Release(IInternetSession *iface)
286 {
287     TRACE("()\n");
288     URLMON_UnlockModule();
289     return 1;
290 }
291
292 static HRESULT WINAPI InternetSession_RegisterNameSpace(IInternetSession *iface,
293         IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzProtocol, ULONG cPatterns,
294         const LPCWSTR *ppwzPatterns, DWORD dwReserved)
295 {
296     TRACE("(%p %s %s %d %p %d)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzProtocol),
297           cPatterns, ppwzPatterns, dwReserved);
298
299     if(cPatterns || ppwzPatterns)
300         FIXME("patterns not supported\n");
301     if(dwReserved)
302         WARN("dwReserved = %d\n", dwReserved);
303
304     if(!pCF || !pwzProtocol)
305         return E_INVALIDARG;
306
307     return register_namespace(pCF, rclsid, pwzProtocol, FALSE);
308 }
309
310 static HRESULT WINAPI InternetSession_UnregisterNameSpace(IInternetSession *iface,
311         IClassFactory *pCF, LPCWSTR pszProtocol)
312 {
313     TRACE("(%p %s)\n", pCF, debugstr_w(pszProtocol));
314
315     if(!pCF || !pszProtocol)
316         return E_INVALIDARG;
317
318     return unregister_namespace(pCF, pszProtocol);
319 }
320
321 static HRESULT WINAPI InternetSession_RegisterMimeFilter(IInternetSession *iface,
322         IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzType)
323 {
324     mime_filter *filter;
325
326     TRACE("(%p %s %s)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzType));
327
328     filter = heap_alloc(sizeof(mime_filter));
329
330     IClassFactory_AddRef(pCF);
331     filter->cf = pCF;
332     filter->clsid = *rclsid;
333     filter->mime = heap_strdupW(pwzType);
334
335     EnterCriticalSection(&session_cs);
336
337     filter->next = mime_filter_list;
338     mime_filter_list = filter;
339
340     LeaveCriticalSection(&session_cs);
341
342     return S_OK;
343 }
344
345 static HRESULT WINAPI InternetSession_UnregisterMimeFilter(IInternetSession *iface,
346         IClassFactory *pCF, LPCWSTR pwzType)
347 {
348     mime_filter *iter, *prev = NULL;
349
350     TRACE("(%p %s)\n", pCF, debugstr_w(pwzType));
351
352     EnterCriticalSection(&session_cs);
353
354     for(iter = mime_filter_list; iter; iter = iter->next) {
355         if(iter->cf == pCF && !strcmpW(iter->mime, pwzType))
356             break;
357         prev = iter;
358     }
359
360     if(iter) {
361         if(prev)
362             prev->next = iter->next;
363         else
364             mime_filter_list = iter->next;
365     }
366
367     LeaveCriticalSection(&session_cs);
368
369     if(iter) {
370         IClassFactory_Release(iter->cf);
371         heap_free(iter->mime);
372         heap_free(iter);
373     }
374
375     return S_OK;
376 }
377
378 static HRESULT WINAPI InternetSession_CreateBinding(IInternetSession *iface,
379         LPBC pBC, LPCWSTR szUrl, IUnknown *pUnkOuter, IUnknown **ppUnk,
380         IInternetProtocol **ppOInetProt, DWORD dwOption)
381 {
382     TRACE("(%p %s %p %p %p %08x)\n", pBC, debugstr_w(szUrl), pUnkOuter, ppUnk,
383             ppOInetProt, dwOption);
384
385     if(pBC || pUnkOuter || ppUnk || dwOption)
386         FIXME("Unsupported arguments\n");
387
388     return create_binding_protocol(szUrl, FALSE, ppOInetProt);
389 }
390
391 static HRESULT WINAPI InternetSession_SetSessionOption(IInternetSession *iface,
392         DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength, DWORD dwReserved)
393 {
394     FIXME("(%08x %p %d %d)\n", dwOption, pBuffer, dwBufferLength, dwReserved);
395     return E_NOTIMPL;
396 }
397
398 static const IInternetSessionVtbl InternetSessionVtbl = {
399     InternetSession_QueryInterface,
400     InternetSession_AddRef,
401     InternetSession_Release,
402     InternetSession_RegisterNameSpace,
403     InternetSession_UnregisterNameSpace,
404     InternetSession_RegisterMimeFilter,
405     InternetSession_UnregisterMimeFilter,
406     InternetSession_CreateBinding,
407     InternetSession_SetSessionOption
408 };
409
410 static IInternetSession InternetSession = { &InternetSessionVtbl };
411
412 /***********************************************************************
413  *           CoInternetGetSession (URLMON.@)
414  *
415  * Create a new internet session and return an IInternetSession interface
416  * representing it.
417  *
418  * PARAMS
419  *    dwSessionMode      [I] Mode for the internet session
420  *    ppIInternetSession [O] Destination for creates IInternetSession object
421  *    dwReserved         [I] Reserved, must be 0.
422  *
423  * RETURNS
424  *    Success: S_OK. ppIInternetSession contains the IInternetSession interface.
425  *    Failure: E_INVALIDARG, if any argument is invalid, or
426  *             E_OUTOFMEMORY if memory allocation fails.
427  */
428 HRESULT WINAPI CoInternetGetSession(DWORD dwSessionMode, IInternetSession **ppIInternetSession,
429         DWORD dwReserved)
430 {
431     TRACE("(%d %p %d)\n", dwSessionMode, ppIInternetSession, dwReserved);
432
433     if(dwSessionMode)
434         ERR("dwSessionMode=%d\n", dwSessionMode);
435     if(dwReserved)
436         ERR("dwReserved=%d\n", dwReserved);
437
438     IInternetSession_AddRef(&InternetSession);
439     *ppIInternetSession = &InternetSession;
440     return S_OK;
441 }
442
443 /**************************************************************************
444  *                 UrlMkGetSessionOption (URLMON.@)
445  */
446 static BOOL get_url_encoding(HKEY root, DWORD *encoding)
447 {
448     DWORD size = sizeof(DWORD), res, type;
449     HKEY hkey;
450
451     static const WCHAR wszKeyName[] = 
452         {'S','O','F','T','W','A','R','E',
453          '\\','M','i','c','r','o','s','o','f','t',
454          '\\','W','i','n','d','o','w','s',
455          '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
456          '\\','I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
457     static const WCHAR wszUrlEncoding[] = {'U','r','l','E','n','c','o','d','i','n','g',0};
458
459     res = RegOpenKeyW(root, wszKeyName, &hkey);
460     if(res != ERROR_SUCCESS)
461         return FALSE;
462
463     res = RegQueryValueExW(hkey, wszUrlEncoding, NULL, &type, (LPBYTE)encoding, &size);
464     RegCloseKey(hkey);
465
466     return res == ERROR_SUCCESS;
467 }
468
469 HRESULT WINAPI UrlMkGetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
470                                      DWORD* pdwBufferLength, DWORD dwReserved)
471 {
472     TRACE("(%x, %p, %d, %p)\n", dwOption, pBuffer, dwBufferLength, pdwBufferLength);
473
474     if(dwReserved)
475         WARN("dwReserved = %d\n", dwReserved);
476
477     switch(dwOption) {
478     case URLMON_OPTION_URL_ENCODING: {
479         DWORD encoding = 0;
480
481         if(!pBuffer || dwBufferLength < sizeof(DWORD) || !pdwBufferLength)
482             return E_INVALIDARG;
483
484         if(!get_url_encoding(HKEY_CURRENT_USER, &encoding))
485             get_url_encoding(HKEY_LOCAL_MACHINE, &encoding);
486
487         *pdwBufferLength = sizeof(DWORD);
488         *(DWORD*)pBuffer = encoding ? URL_ENCODING_DISABLE_UTF8 : URL_ENCODING_ENABLE_UTF8;
489         return S_OK;
490     }
491     default:
492         FIXME("unsupported option %x\n", dwOption);
493     }
494
495     return E_INVALIDARG;
496 }