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