wininet: Store hostname for secure connection in its SSL context.
[wine] / dlls / itss / protocol.c
1 /*
2  * Copyright 2006-2007 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 <stdarg.h>
20
21 #define COBJMACROS
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include "ole2.h"
28 #include "urlmon.h"
29 #include "shlwapi.h"
30 #include "itsstor.h"
31 #include "chm_lib.h"
32
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(itss);
37
38 typedef struct {
39     const IInternetProtocolVtbl     *lpInternetProtocolVtbl;
40     const IInternetProtocolInfoVtbl *lpInternetProtocolInfoVtbl;
41
42     LONG ref;
43
44     ULONG offset;
45     struct chmFile *chm_file;
46     struct chmUnitInfo chm_object;
47 } ITSProtocol;
48
49 #define PROTOCOL(x)  ((IInternetProtocol*)  &(x)->lpInternetProtocolVtbl)
50 #define PROTINFO(x)  (&(x)->lpInternetProtocolInfoVtbl)
51
52 static void release_chm(ITSProtocol *This)
53 {
54     if(This->chm_file) {
55         chm_close(This->chm_file);
56         This->chm_file = NULL;
57     }
58     This->offset = 0;
59 }
60
61 #define PROTOCOL_THIS(iface) DEFINE_THIS(ITSProtocol, InternetProtocol, iface)
62
63 static HRESULT WINAPI ITSProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
64 {
65     ITSProtocol *This = PROTOCOL_THIS(iface);
66
67     *ppv = NULL;
68     if(IsEqualGUID(&IID_IUnknown, riid)) {
69         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
70         *ppv = PROTOCOL(This);
71     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
72         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
73         *ppv = PROTOCOL(This);
74     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
75         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
76         *ppv = PROTOCOL(This);
77     }else if(IsEqualGUID(&IID_IInternetProtocolInfo, riid)) {
78         TRACE("(%p)->(IID_IInternetProtocolInfo %p)\n", This, ppv);
79         *ppv = PROTINFO(This);
80     }
81
82     if(*ppv) {
83         IInternetProtocol_AddRef(iface);
84         return S_OK;
85     }
86
87     WARN("not supported interface %s\n", debugstr_guid(riid));
88     return E_NOINTERFACE;
89 }
90
91 static ULONG WINAPI ITSProtocol_AddRef(IInternetProtocol *iface)
92 {
93     ITSProtocol *This = PROTOCOL_THIS(iface);
94     LONG ref = InterlockedIncrement(&This->ref);
95     TRACE("(%p) ref=%d\n", This, ref);
96     return ref;
97 }
98
99 static ULONG WINAPI ITSProtocol_Release(IInternetProtocol *iface)
100 {
101     ITSProtocol *This = PROTOCOL_THIS(iface);
102     LONG ref = InterlockedDecrement(&This->ref);
103
104     TRACE("(%p) ref=%d\n", This, ref);
105
106     if(!ref) {
107         release_chm(This);
108         HeapFree(GetProcessHeap(), 0, This);
109
110         ITSS_UnlockModule();
111     }
112
113     return ref;
114 }
115
116 static LPCWSTR skip_schema(LPCWSTR url)
117 {
118     static const WCHAR its_schema[] = {'i','t','s',':'};
119     static const WCHAR msits_schema[] = {'m','s','-','i','t','s',':'};
120     static const WCHAR mk_schema[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':'};
121
122     if(!strncmpiW(its_schema, url, sizeof(its_schema)/sizeof(WCHAR)))
123         return url+sizeof(its_schema)/sizeof(WCHAR);
124     if(!strncmpiW(msits_schema, url, sizeof(msits_schema)/sizeof(WCHAR)))
125         return url+sizeof(msits_schema)/sizeof(WCHAR);
126     if(!strncmpiW(mk_schema, url, sizeof(mk_schema)/sizeof(WCHAR)))
127         return url+sizeof(mk_schema)/sizeof(WCHAR);
128
129     return NULL;
130 }
131
132 static HRESULT report_result(IInternetProtocolSink *sink, HRESULT hres)
133 {
134     IInternetProtocolSink_ReportResult(sink, hres, 0, NULL);
135     return hres;
136 }
137
138 static HRESULT WINAPI ITSProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
139         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
140         DWORD grfPI, HANDLE_PTR dwReserved)
141 {
142     ITSProtocol *This = PROTOCOL_THIS(iface);
143     BINDINFO bindinfo;
144     DWORD bindf = 0, len;
145     LPWSTR file_name, mime, object_name, p;
146     LPCWSTR ptr;
147     struct chmFile *chm_file;
148     struct chmUnitInfo chm_object;
149     int res;
150     HRESULT hres;
151
152     static const WCHAR separator[] = {':',':',0};
153
154     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
155             pOIBindInfo, grfPI, dwReserved);
156
157     ptr = skip_schema(szUrl);
158     if(!ptr)
159         return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
160
161     memset(&bindinfo, 0, sizeof(bindinfo));
162     bindinfo.cbSize = sizeof(BINDINFO);
163     hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo);
164     if(FAILED(hres)) {
165         WARN("GetBindInfo failed: %08x\n", hres);
166         return hres;
167     }
168
169     ReleaseBindInfo(&bindinfo);
170
171     len = strlenW(ptr)+3;
172     file_name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
173     memcpy(file_name, ptr, len*sizeof(WCHAR));
174     hres = UrlUnescapeW(file_name, NULL, &len, URL_UNESCAPE_INPLACE);
175     if(FAILED(hres)) {
176         WARN("UrlUnescape failed: %08x\n", hres);
177         HeapFree(GetProcessHeap(), 0, file_name);
178         return hres;
179     }
180
181     p = strstrW(file_name, separator);
182     if(!p) {
183         WARN("invalid url\n");
184         HeapFree(GetProcessHeap(), 0, file_name);
185         return report_result(pOIProtSink, STG_E_FILENOTFOUND);
186     }
187
188     *p = 0;
189     chm_file = chm_openW(file_name);
190     if(!chm_file) {
191         WARN("Could not open chm file\n");
192         HeapFree(GetProcessHeap(), 0, file_name);
193         return report_result(pOIProtSink, STG_E_FILENOTFOUND);
194     }
195
196     object_name = p+2;
197     len = strlenW(object_name);
198
199     if(*object_name != '/' && *object_name != '\\') {
200         memmove(object_name+1, object_name, (len+1)*sizeof(WCHAR));
201         *object_name = '/';
202         len++;
203     }
204
205     if(object_name[len-1] == '/')
206         object_name[--len] = 0;
207
208     for(p=object_name; *p; p++) {
209         if(*p == '\\')
210             *p = '/';
211     }
212
213     TRACE("Resolving %s\n", debugstr_w(object_name));
214
215     memset(&chm_object, 0, sizeof(chm_object));
216     res = chm_resolve_object(chm_file, object_name, &chm_object);
217     if(res != CHM_RESOLVE_SUCCESS) {
218         WARN("Could not resolve chm object\n");
219         HeapFree(GetProcessHeap(), 0, file_name);
220         chm_close(chm_file);
221         return report_result(pOIProtSink, STG_E_FILENOTFOUND);
222     }
223
224     IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST,
225                                          strrchrW(object_name, '/')+1);
226
227     /* FIXME: Native doesn't use FindMimeFromData */
228     hres = FindMimeFromData(NULL, object_name, NULL, 0, NULL, 0, &mime, 0);
229     HeapFree(GetProcessHeap(), 0, file_name);
230     if(SUCCEEDED(hres)) {
231         IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, mime);
232         CoTaskMemFree(mime);
233     }
234
235     release_chm(This); /* Native leaks handle here */
236     This->chm_file = chm_file;
237     This->chm_object = chm_object;
238
239     hres = IInternetProtocolSink_ReportData(pOIProtSink,
240             BSCF_FIRSTDATANOTIFICATION|BSCF_DATAFULLYAVAILABLE,
241             chm_object.length, chm_object.length);
242     if(FAILED(hres)) {
243         WARN("ReportData failed: %08x\n", hres);
244         release_chm(This);
245         return report_result(pOIProtSink, hres);
246     }
247
248     hres = IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_BEGINDOWNLOADDATA, NULL);
249
250     return report_result(pOIProtSink, hres);
251 }
252
253 static HRESULT WINAPI ITSProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
254 {
255     ITSProtocol *This = PROTOCOL_THIS(iface);
256     FIXME("(%p)->(%p)\n", This, pProtocolData);
257     return E_NOTIMPL;
258 }
259
260 static HRESULT WINAPI ITSProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
261         DWORD dwOptions)
262 {
263     ITSProtocol *This = PROTOCOL_THIS(iface);
264     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
265     return E_NOTIMPL;
266 }
267
268 static HRESULT WINAPI ITSProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
269 {
270     ITSProtocol *This = PROTOCOL_THIS(iface);
271
272     TRACE("(%p)->(%08x)\n", This, dwOptions);
273
274     return S_OK;
275 }
276
277 static HRESULT WINAPI ITSProtocol_Suspend(IInternetProtocol *iface)
278 {
279     ITSProtocol *This = PROTOCOL_THIS(iface);
280     FIXME("(%p)\n", This);
281     return E_NOTIMPL;
282 }
283
284 static HRESULT WINAPI ITSProtocol_Resume(IInternetProtocol *iface)
285 {
286     ITSProtocol *This = PROTOCOL_THIS(iface);
287     FIXME("(%p)\n", This);
288     return E_NOTIMPL;
289 }
290
291 static HRESULT WINAPI ITSProtocol_Read(IInternetProtocol *iface, void *pv,
292         ULONG cb, ULONG *pcbRead)
293 {
294     ITSProtocol *This = PROTOCOL_THIS(iface);
295
296     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
297
298     if(!This->chm_file)
299         return INET_E_DATA_NOT_AVAILABLE;
300
301     *pcbRead = chm_retrieve_object(This->chm_file, &This->chm_object, pv, This->offset, cb);
302     This->offset += *pcbRead;
303
304     return *pcbRead ? S_OK : S_FALSE;
305 }
306
307 static HRESULT WINAPI ITSProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
308         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
309 {
310     ITSProtocol *This = PROTOCOL_THIS(iface);
311     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
312     return E_NOTIMPL;
313 }
314
315 static HRESULT WINAPI ITSProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
316 {
317     ITSProtocol *This = PROTOCOL_THIS(iface);
318
319     TRACE("(%p)->(%08x)\n", This, dwOptions);
320
321     return S_OK;
322 }
323
324 static HRESULT WINAPI ITSProtocol_UnlockRequest(IInternetProtocol *iface)
325 {
326     ITSProtocol *This = PROTOCOL_THIS(iface);
327
328     TRACE("(%p)\n", This);
329
330     return S_OK;
331 }
332
333 #undef PROTOCOL_THIS
334
335 static const IInternetProtocolVtbl ITSProtocolVtbl = {
336     ITSProtocol_QueryInterface,
337     ITSProtocol_AddRef,
338     ITSProtocol_Release,
339     ITSProtocol_Start,
340     ITSProtocol_Continue,
341     ITSProtocol_Abort,
342     ITSProtocol_Terminate,
343     ITSProtocol_Suspend,
344     ITSProtocol_Resume,
345     ITSProtocol_Read,
346     ITSProtocol_Seek,
347     ITSProtocol_LockRequest,
348     ITSProtocol_UnlockRequest
349 };
350
351 #define PROTINFO_THIS(iface) DEFINE_THIS(ITSProtocol, InternetProtocolInfo, iface)
352
353 static HRESULT WINAPI ITSProtocolInfo_QueryInterface(IInternetProtocolInfo *iface,
354                                               REFIID riid, void **ppv)
355 {
356     ITSProtocol *This = PROTINFO_THIS(iface);
357     return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
358 }
359
360 static ULONG WINAPI ITSProtocolInfo_AddRef(IInternetProtocolInfo *iface)
361 {
362     ITSProtocol *This = PROTINFO_THIS(iface);
363     return IInternetProtocol_AddRef(PROTOCOL(This));
364 }
365
366 static ULONG WINAPI ITSProtocolInfo_Release(IInternetProtocolInfo *iface)
367 {
368     ITSProtocol *This = PROTINFO_THIS(iface);
369     return IInternetProtocol_Release(PROTOCOL(This));
370 }
371
372 static HRESULT WINAPI ITSProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
373         PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult,
374         DWORD *pcchResult, DWORD dwReserved)
375 {
376     ITSProtocol *This = PROTINFO_THIS(iface);
377
378     TRACE("(%p)->(%s %x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), ParseAction,
379           dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved);
380
381     switch(ParseAction) {
382     case PARSE_CANONICALIZE:
383         FIXME("PARSE_CANONICALIZE\n");
384         return E_NOTIMPL;
385     case PARSE_SECURITY_URL:
386         FIXME("PARSE_SECURITY_URL\n");
387         return E_NOTIMPL;
388     default:
389         return INET_E_DEFAULT_ACTION;
390     }
391
392     return S_OK;
393 }
394
395 static HRESULT WINAPI ITSProtocolInfo_CombineUrl(IInternetProtocolInfo *iface,
396         LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult,
397         DWORD cchResult, DWORD* pcchResult, DWORD dwReserved)
398 {
399     ITSProtocol *This = PROTINFO_THIS(iface);
400     LPCWSTR base_end, ptr;
401     DWORD rel_len;
402
403     static const WCHAR separator[] = {':',':',0};
404
405     TRACE("(%p)->(%s %s %08x %p %d %p %d)\n", This, debugstr_w(pwzBaseUrl),
406             debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult,
407             pcchResult, dwReserved);
408
409     base_end = strstrW(pwzBaseUrl, separator);
410     if(!base_end)
411         return 0x80041001;
412     base_end += 2;
413
414     if(!skip_schema(pwzBaseUrl))
415         return INET_E_USE_DEFAULT_PROTOCOLHANDLER;
416
417     if(strchrW(pwzRelativeUrl, ':'))
418         return STG_E_INVALIDNAME;
419
420     if(pwzRelativeUrl[0] != '/') {
421         ptr = strrchrW(base_end, '/');
422         if(ptr)
423             base_end = ptr+1;
424         else
425             base_end += strlenW(base_end);
426     }
427
428     rel_len = strlenW(pwzRelativeUrl)+1;
429
430     *pcchResult = rel_len + (base_end-pwzBaseUrl);
431
432     if(*pcchResult > cchResult)
433         return E_OUTOFMEMORY;
434
435     memcpy(pwzResult, pwzBaseUrl, (base_end-pwzBaseUrl)*sizeof(WCHAR));
436     strcpyW(pwzResult + (base_end-pwzBaseUrl), pwzRelativeUrl);
437
438     return S_OK;
439 }
440
441 static HRESULT WINAPI ITSProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1,
442         LPCWSTR pwzUrl2, DWORD dwCompareFlags)
443 {
444     ITSProtocol *This = PROTINFO_THIS(iface);
445     FIXME("%p)->(%s %s %08x)\n", This, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags);
446     return E_NOTIMPL;
447 }
448
449 static HRESULT WINAPI ITSProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl,
450         QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf,
451         DWORD dwReserved)
452 {
453     ITSProtocol *This = PROTINFO_THIS(iface);
454     FIXME("(%p)->(%s %08x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), QueryOption,
455           dwQueryFlags, pBuffer, cbBuffer, pcbBuf, dwReserved);
456     return E_NOTIMPL;
457 }
458
459 #undef PROTINFO_THIS
460
461 static const IInternetProtocolInfoVtbl ITSProtocolInfoVtbl = {
462     ITSProtocolInfo_QueryInterface,
463     ITSProtocolInfo_AddRef,
464     ITSProtocolInfo_Release,
465     ITSProtocolInfo_ParseUrl,
466     ITSProtocolInfo_CombineUrl,
467     ITSProtocolInfo_CompareUrl,
468     ITSProtocolInfo_QueryInfo
469 };
470
471 HRESULT ITSProtocol_create(IUnknown *pUnkOuter, LPVOID *ppobj)
472 {
473     ITSProtocol *ret;
474
475     TRACE("(%p %p)\n", pUnkOuter, ppobj);
476
477     ITSS_LockModule();
478
479     ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ITSProtocol));
480
481     ret->lpInternetProtocolVtbl = &ITSProtocolVtbl;
482     ret->lpInternetProtocolInfoVtbl = &ITSProtocolInfoVtbl;
483     ret->ref = 1;
484
485     *ppobj = PROTOCOL(ret);
486
487     return S_OK;
488 }