usp10/test: Add Tibetan shaping test.
[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 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     for(iter = name_space_list; iter; iter = iter->next) {
67         if(!strcmpW(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 + sizeof(wszProtocolsKey)/sizeof(WCHAR), 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 static 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     new_name_space->next = name_space_list;
138     name_space_list = new_name_space;
139
140     LeaveCriticalSection(&session_cs);
141
142     return S_OK;
143 }
144
145 static HRESULT unregister_namespace(IClassFactory *cf, LPCWSTR protocol)
146 {
147     name_space *iter, *last = NULL;
148
149     EnterCriticalSection(&session_cs);
150
151     for(iter = name_space_list; iter; iter = iter->next) {
152         if(iter->cf == cf && !strcmpW(iter->protocol, protocol))
153             break;
154         last = iter;
155     }
156
157     if(iter) {
158         if(last)
159             last->next = iter->next;
160         else
161             name_space_list = iter->next;
162     }
163
164     LeaveCriticalSection(&session_cs);
165
166     if(iter) {
167         if(!iter->urlmon)
168             IClassFactory_Release(iter->cf);
169         heap_free(iter->protocol);
170         heap_free(iter);
171     }
172
173     return S_OK;
174 }
175
176
177 void register_urlmon_namespace(IClassFactory *cf, REFIID clsid, LPCWSTR protocol, BOOL do_register)
178 {
179     if(do_register)
180         register_namespace(cf, clsid, protocol, TRUE);
181     else
182         unregister_namespace(cf, protocol);
183 }
184
185 BOOL is_registered_protocol(LPCWSTR url)
186 {
187     DWORD schema_len;
188     WCHAR schema[64];
189     HRESULT hres;
190
191     hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
192             &schema_len, 0);
193     if(FAILED(hres))
194         return FALSE;
195
196     return get_protocol_cf(schema, schema_len, NULL, NULL) == S_OK;
197 }
198
199 IInternetProtocolInfo *get_protocol_info(LPCWSTR url)
200 {
201     IInternetProtocolInfo *ret = NULL;
202     IClassFactory *cf;
203     name_space *ns;
204     WCHAR schema[64];
205     DWORD schema_len;
206     HRESULT hres;
207
208     hres = CoInternetParseUrl(url, PARSE_SCHEMA, 0, schema, sizeof(schema)/sizeof(schema[0]),
209             &schema_len, 0);
210     if(FAILED(hres) || !schema_len)
211         return NULL;
212
213     EnterCriticalSection(&session_cs);
214
215     ns = find_name_space(schema);
216     if(ns && !ns->urlmon) {
217         hres = IClassFactory_QueryInterface(ns->cf, &IID_IInternetProtocolInfo, (void**)&ret);
218         if(FAILED(hres))
219             hres = IClassFactory_CreateInstance(ns->cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
220     }
221
222     LeaveCriticalSection(&session_cs);
223
224     if(ns && SUCCEEDED(hres))
225         return ret;
226
227     hres = get_protocol_cf(schema, schema_len, NULL, &cf);
228     if(FAILED(hres))
229         return NULL;
230
231     hres = IClassFactory_QueryInterface(cf, &IID_IInternetProtocolInfo, (void**)&ret);
232     if(FAILED(hres))
233         IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocolInfo, (void**)&ret);
234     IClassFactory_Release(cf);
235
236     return ret;
237 }
238
239 HRESULT get_protocol_handler(IUri *uri, CLSID *clsid, BOOL *urlmon_protocol, IClassFactory **ret)
240 {
241     name_space *ns;
242     BSTR scheme;
243     HRESULT hres;
244
245     *ret = NULL;
246
247     /* FIXME: Avoid GetSchemeName call for known schemes */
248     hres = IUri_GetSchemeName(uri, &scheme);
249     if(FAILED(hres))
250         return hres;
251
252     EnterCriticalSection(&session_cs);
253
254     ns = find_name_space(scheme);
255     if(ns) {
256         *ret = ns->cf;
257         IClassFactory_AddRef(*ret);
258         if(clsid)
259             *clsid = ns->clsid;
260         if(urlmon_protocol)
261             *urlmon_protocol = ns->urlmon;
262     }
263
264     LeaveCriticalSection(&session_cs);
265
266     if(*ret) {
267         hres = S_OK;
268     }else {
269         if(urlmon_protocol)
270             *urlmon_protocol = FALSE;
271         hres = get_protocol_cf(scheme, SysStringLen(scheme), clsid, ret);
272     }
273
274     SysFreeString(scheme);
275     return hres;
276 }
277
278 IInternetProtocol *get_mime_filter(LPCWSTR mime)
279 {
280     IClassFactory *cf = NULL;
281     IInternetProtocol *ret;
282     mime_filter *iter;
283     HRESULT hres;
284
285     EnterCriticalSection(&session_cs);
286
287     for(iter = mime_filter_list; iter; iter = iter->next) {
288         if(!strcmpW(iter->mime, mime)) {
289             cf = iter->cf;
290             break;
291         }
292     }
293
294     LeaveCriticalSection(&session_cs);
295
296     if(!cf)
297         return NULL;
298
299     hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&ret);
300     if(FAILED(hres)) {
301         WARN("CreateInstance failed: %08x\n", hres);
302         return NULL;
303     }
304
305     return ret;
306 }
307
308 static HRESULT WINAPI InternetSession_QueryInterface(IInternetSession *iface,
309         REFIID riid, void **ppv)
310 {
311     TRACE("(%s %p)\n", debugstr_guid(riid), ppv);
312
313     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetSession, riid)) {
314         *ppv = iface;
315         IInternetSession_AddRef(iface);
316         return S_OK;
317     }
318
319     *ppv = NULL;
320     return E_NOINTERFACE;
321 }
322
323 static ULONG WINAPI InternetSession_AddRef(IInternetSession *iface)
324 {
325     TRACE("()\n");
326     URLMON_LockModule();
327     return 2;
328 }
329
330 static ULONG WINAPI InternetSession_Release(IInternetSession *iface)
331 {
332     TRACE("()\n");
333     URLMON_UnlockModule();
334     return 1;
335 }
336
337 static HRESULT WINAPI InternetSession_RegisterNameSpace(IInternetSession *iface,
338         IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzProtocol, ULONG cPatterns,
339         const LPCWSTR *ppwzPatterns, DWORD dwReserved)
340 {
341     TRACE("(%p %s %s %d %p %d)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzProtocol),
342           cPatterns, ppwzPatterns, dwReserved);
343
344     if(cPatterns || ppwzPatterns)
345         FIXME("patterns not supported\n");
346     if(dwReserved)
347         WARN("dwReserved = %d\n", dwReserved);
348
349     if(!pCF || !pwzProtocol)
350         return E_INVALIDARG;
351
352     return register_namespace(pCF, rclsid, pwzProtocol, FALSE);
353 }
354
355 static HRESULT WINAPI InternetSession_UnregisterNameSpace(IInternetSession *iface,
356         IClassFactory *pCF, LPCWSTR pszProtocol)
357 {
358     TRACE("(%p %s)\n", pCF, debugstr_w(pszProtocol));
359
360     if(!pCF || !pszProtocol)
361         return E_INVALIDARG;
362
363     return unregister_namespace(pCF, pszProtocol);
364 }
365
366 static HRESULT WINAPI InternetSession_RegisterMimeFilter(IInternetSession *iface,
367         IClassFactory *pCF, REFCLSID rclsid, LPCWSTR pwzType)
368 {
369     mime_filter *filter;
370
371     TRACE("(%p %s %s)\n", pCF, debugstr_guid(rclsid), debugstr_w(pwzType));
372
373     filter = heap_alloc(sizeof(mime_filter));
374
375     IClassFactory_AddRef(pCF);
376     filter->cf = pCF;
377     filter->clsid = *rclsid;
378     filter->mime = heap_strdupW(pwzType);
379
380     EnterCriticalSection(&session_cs);
381
382     filter->next = mime_filter_list;
383     mime_filter_list = filter;
384
385     LeaveCriticalSection(&session_cs);
386
387     return S_OK;
388 }
389
390 static HRESULT WINAPI InternetSession_UnregisterMimeFilter(IInternetSession *iface,
391         IClassFactory *pCF, LPCWSTR pwzType)
392 {
393     mime_filter *iter, *prev = NULL;
394
395     TRACE("(%p %s)\n", pCF, debugstr_w(pwzType));
396
397     EnterCriticalSection(&session_cs);
398
399     for(iter = mime_filter_list; iter; iter = iter->next) {
400         if(iter->cf == pCF && !strcmpW(iter->mime, pwzType))
401             break;
402         prev = iter;
403     }
404
405     if(iter) {
406         if(prev)
407             prev->next = iter->next;
408         else
409             mime_filter_list = iter->next;
410     }
411
412     LeaveCriticalSection(&session_cs);
413
414     if(iter) {
415         IClassFactory_Release(iter->cf);
416         heap_free(iter->mime);
417         heap_free(iter);
418     }
419
420     return S_OK;
421 }
422
423 static HRESULT WINAPI InternetSession_CreateBinding(IInternetSession *iface,
424         LPBC pBC, LPCWSTR szUrl, IUnknown *pUnkOuter, IUnknown **ppUnk,
425         IInternetProtocol **ppOInetProt, DWORD dwOption)
426 {
427     BindProtocol *protocol;
428     HRESULT hres;
429
430     TRACE("(%p %s %p %p %p %08x)\n", pBC, debugstr_w(szUrl), pUnkOuter, ppUnk,
431             ppOInetProt, dwOption);
432
433     if(pBC || pUnkOuter || ppUnk || dwOption)
434         FIXME("Unsupported arguments\n");
435
436     hres = create_binding_protocol(FALSE, &protocol);
437     if(FAILED(hres))
438         return hres;
439
440     *ppOInetProt = (IInternetProtocol*)&protocol->IInternetProtocolEx_iface;
441     return S_OK;
442 }
443
444 static HRESULT WINAPI InternetSession_SetSessionOption(IInternetSession *iface,
445         DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength, DWORD dwReserved)
446 {
447     FIXME("(%08x %p %d %d)\n", dwOption, pBuffer, dwBufferLength, dwReserved);
448     return E_NOTIMPL;
449 }
450
451 static const IInternetSessionVtbl InternetSessionVtbl = {
452     InternetSession_QueryInterface,
453     InternetSession_AddRef,
454     InternetSession_Release,
455     InternetSession_RegisterNameSpace,
456     InternetSession_UnregisterNameSpace,
457     InternetSession_RegisterMimeFilter,
458     InternetSession_UnregisterMimeFilter,
459     InternetSession_CreateBinding,
460     InternetSession_SetSessionOption
461 };
462
463 static IInternetSession InternetSession = { &InternetSessionVtbl };
464
465 /***********************************************************************
466  *           CoInternetGetSession (URLMON.@)
467  *
468  * Create a new internet session and return an IInternetSession interface
469  * representing it.
470  *
471  * PARAMS
472  *    dwSessionMode      [I] Mode for the internet session
473  *    ppIInternetSession [O] Destination for creates IInternetSession object
474  *    dwReserved         [I] Reserved, must be 0.
475  *
476  * RETURNS
477  *    Success: S_OK. ppIInternetSession contains the IInternetSession interface.
478  *    Failure: E_INVALIDARG, if any argument is invalid, or
479  *             E_OUTOFMEMORY if memory allocation fails.
480  */
481 HRESULT WINAPI CoInternetGetSession(DWORD dwSessionMode, IInternetSession **ppIInternetSession,
482         DWORD dwReserved)
483 {
484     TRACE("(%d %p %d)\n", dwSessionMode, ppIInternetSession, dwReserved);
485
486     if(dwSessionMode)
487         ERR("dwSessionMode=%d\n", dwSessionMode);
488     if(dwReserved)
489         ERR("dwReserved=%d\n", dwReserved);
490
491     IInternetSession_AddRef(&InternetSession);
492     *ppIInternetSession = &InternetSession;
493     return S_OK;
494 }
495
496 /**************************************************************************
497  *                 UrlMkGetSessionOption (URLMON.@)
498  */
499 static BOOL get_url_encoding(HKEY root, DWORD *encoding)
500 {
501     DWORD size = sizeof(DWORD), res, type;
502     HKEY hkey;
503
504     static const WCHAR wszUrlEncoding[] = {'U','r','l','E','n','c','o','d','i','n','g',0};
505
506     res = RegOpenKeyW(root, internet_settings_keyW, &hkey);
507     if(res != ERROR_SUCCESS)
508         return FALSE;
509
510     res = RegQueryValueExW(hkey, wszUrlEncoding, NULL, &type, (LPBYTE)encoding, &size);
511     RegCloseKey(hkey);
512
513     return res == ERROR_SUCCESS;
514 }
515
516 static LPWSTR user_agent;
517
518 static void ensure_useragent(void)
519 {
520     DWORD size = sizeof(DWORD), res, type;
521     HKEY hkey;
522
523     static const WCHAR user_agentW[] = {'U','s','e','r',' ','A','g','e','n','t',0};
524
525     if(user_agent)
526         return;
527
528     res = RegOpenKeyW(HKEY_CURRENT_USER, internet_settings_keyW, &hkey);
529     if(res != ERROR_SUCCESS)
530         return;
531
532     res = RegQueryValueExW(hkey, user_agentW, NULL, &type, NULL, &size);
533     if(res == ERROR_SUCCESS && type == REG_SZ) {
534         user_agent = heap_alloc(size);
535         res = RegQueryValueExW(hkey, user_agentW, NULL, &type, (LPBYTE)user_agent, &size);
536         if(res != ERROR_SUCCESS) {
537             heap_free(user_agent);
538             user_agent = NULL;
539         }
540     }else {
541         WARN("Could not find User Agent value: %u\n", res);
542     }
543
544     RegCloseKey(hkey);
545 }
546
547 LPWSTR get_useragent(void)
548 {
549     LPWSTR ret;
550
551     ensure_useragent();
552
553     EnterCriticalSection(&session_cs);
554     ret = heap_strdupW(user_agent);
555     LeaveCriticalSection(&session_cs);
556
557     return ret;
558 }
559
560 HRESULT WINAPI UrlMkGetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
561                                      DWORD* pdwBufferLength, DWORD dwReserved)
562 {
563     TRACE("(%x, %p, %d, %p)\n", dwOption, pBuffer, dwBufferLength, pdwBufferLength);
564
565     if(dwReserved)
566         WARN("dwReserved = %d\n", dwReserved);
567
568     switch(dwOption) {
569     case URLMON_OPTION_USERAGENT: {
570         HRESULT hres = E_OUTOFMEMORY;
571         DWORD size;
572
573         if(!pdwBufferLength)
574             return E_INVALIDARG;
575
576         EnterCriticalSection(&session_cs);
577
578         ensure_useragent();
579         if(user_agent) {
580             size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);
581             *pdwBufferLength = size;
582             if(size <= dwBufferLength) {
583                 if(pBuffer)
584                     WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pBuffer, size, NULL, NULL);
585                 else
586                     hres = E_INVALIDARG;
587             }
588         }
589
590         LeaveCriticalSection(&session_cs);
591
592         /* Tests prove that we have to return E_OUTOFMEMORY on success. */
593         return hres;
594     }
595     case URLMON_OPTION_URL_ENCODING: {
596         DWORD encoding = 0;
597
598         if(!pBuffer || dwBufferLength < sizeof(DWORD) || !pdwBufferLength)
599             return E_INVALIDARG;
600
601         if(!get_url_encoding(HKEY_CURRENT_USER, &encoding))
602             get_url_encoding(HKEY_LOCAL_MACHINE, &encoding);
603
604         *pdwBufferLength = sizeof(DWORD);
605         *(DWORD*)pBuffer = encoding ? URL_ENCODING_DISABLE_UTF8 : URL_ENCODING_ENABLE_UTF8;
606         return S_OK;
607     }
608     default:
609         FIXME("unsupported option %x\n", dwOption);
610     }
611
612     return E_INVALIDARG;
613 }
614
615 /**************************************************************************
616  *                 UrlMkSetSessionOption (URLMON.@)
617  */
618 HRESULT WINAPI UrlMkSetSessionOption(DWORD dwOption, LPVOID pBuffer, DWORD dwBufferLength,
619         DWORD Reserved)
620 {
621     TRACE("(%x %p %x)\n", dwOption, pBuffer, dwBufferLength);
622
623     switch(dwOption) {
624     case URLMON_OPTION_USERAGENT: {
625         LPWSTR new_user_agent;
626         char *buf = pBuffer;
627         DWORD len, size;
628
629         if(!pBuffer || !dwBufferLength)
630             return E_INVALIDARG;
631
632         for(len=0; len<dwBufferLength && buf[len]; len++);
633
634         TRACE("Setting user agent %s\n", debugstr_an(buf, len));
635
636         size = MultiByteToWideChar(CP_ACP, 0, buf, len, NULL, 0);
637         new_user_agent = heap_alloc((size+1)*sizeof(WCHAR));
638         if(!new_user_agent)
639             return E_OUTOFMEMORY;
640         MultiByteToWideChar(CP_ACP, 0, buf, len, new_user_agent, size);
641         new_user_agent[size] = 0;
642
643         EnterCriticalSection(&session_cs);
644
645         heap_free(user_agent);
646         user_agent = new_user_agent;
647
648         LeaveCriticalSection(&session_cs);
649         break;
650     }
651     default:
652         FIXME("Unknown option %x\n", dwOption);
653         return E_INVALIDARG;
654     }
655
656     return S_OK;
657 }
658
659 /**************************************************************************
660  *                 ObtainUserAgentString (URLMON.@)
661  */
662 HRESULT WINAPI ObtainUserAgentString(DWORD dwOption, LPSTR pcszUAOut, DWORD *cbSize)
663 {
664     DWORD size;
665     HRESULT hres = E_FAIL;
666
667     TRACE("(%d %p %p)\n", dwOption, pcszUAOut, cbSize);
668
669     if(!pcszUAOut || !cbSize)
670         return E_INVALIDARG;
671
672     EnterCriticalSection(&session_cs);
673
674     ensure_useragent();
675     if(user_agent) {
676         size = WideCharToMultiByte(CP_ACP, 0, user_agent, -1, NULL, 0, NULL, NULL);
677
678         if(size <= *cbSize) {
679             WideCharToMultiByte(CP_ACP, 0, user_agent, -1, pcszUAOut, *cbSize, NULL, NULL);
680             hres = S_OK;
681         }else {
682             hres = E_OUTOFMEMORY;
683         }
684
685         *cbSize = size;
686     }
687
688     LeaveCriticalSection(&session_cs);
689     return hres;
690 }
691
692 void free_session(void)
693 {
694     heap_free(user_agent);
695 }