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