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