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 /* Adopted from urlmon */ 138 static void remove_dot_segments(WCHAR *path) { 139 const WCHAR *in = path; 140 WCHAR *out = path; 141 142 while(1) { 143 /* Move the first path segment in the input buffer to the end of 144 * the output buffer, and any subsequent characters up to, including 145 * the next "/" character (if any) or the end of the input buffer. 146 */ 147 while(*in != '/') { 148 if(!(*out++ = *in++)) 149 return; 150 } 151 152 *out++ = *in++; 153 154 while(*in) { 155 if(*in != '.') 156 break; 157 158 /* Handle ending "/." */ 159 if(!in[1]) { 160 ++in; 161 break; 162 } 163 164 /* Handle "/./" */ 165 if(in[1] == '/') { 166 in += 2; 167 continue; 168 } 169 170 /* If we don't have "/../" or ending "/.." */ 171 if(in[1] != '.' || (in[2] && in[2] != '/')) 172 break; 173 174 in += *in ? 3 : 2; 175 176 /* Find the slash preceding out pointer and move out pointer to it */ 177 if(out > path+1 && *--out == '/') 178 --out; 179 while(out > path && *(--out) != '/'); 180 if(*out == '/') 181 ++out; 182 } 183 } 184 } 185 186 static HRESULT report_result(IInternetProtocolSink *sink, HRESULT hres) 187 { 188 IInternetProtocolSink_ReportResult(sink, hres, 0, NULL); 189 return hres; 190 } 191 192 static HRESULT WINAPI ITSProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl, 193 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, 194 DWORD grfPI, HANDLE_PTR dwReserved) 195 { 196 ITSProtocol *This = impl_from_IInternetProtocol(iface); 197 BINDINFO bindinfo; 198 DWORD bindf = 0, len; 199 LPWSTR file_name, mime, object_name, p; 200 LPCWSTR ptr; 201 struct chmFile *chm_file; 202 struct chmUnitInfo chm_object; 203 int res; 204 HRESULT hres; 205 206 static const WCHAR separator[] = {':',':',0}; 207 208 TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink, 209 pOIBindInfo, grfPI, dwReserved); 210 211 ptr = skip_schema(szUrl); 212 if(!ptr) 213 return INET_E_USE_DEFAULT_PROTOCOLHANDLER; 214 215 memset(&bindinfo, 0, sizeof(bindinfo)); 216 bindinfo.cbSize = sizeof(BINDINFO); 217 hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo); 218 if(FAILED(hres)) { 219 WARN("GetBindInfo failed: %08x\n", hres); 220 return hres; 221 } 222 223 ReleaseBindInfo(&bindinfo); 224 225 len = strlenW(ptr)+3; 226 file_name = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); 227 memcpy(file_name, ptr, len*sizeof(WCHAR)); 228 hres = UrlUnescapeW(file_name, NULL, &len, URL_UNESCAPE_INPLACE); 229 if(FAILED(hres)) { 230 WARN("UrlUnescape failed: %08x\n", hres); 231 HeapFree(GetProcessHeap(), 0, file_name); 232 return hres; 233 } 234 235 p = strstrW(file_name, separator); 236 if(!p) { 237 WARN("invalid url\n"); 238 HeapFree(GetProcessHeap(), 0, file_name); 239 return report_result(pOIProtSink, STG_E_FILENOTFOUND); 240 } 241 242 *p = 0; 243 chm_file = chm_openW(file_name); 244 if(!chm_file) { 245 WARN("Could not open chm file\n"); 246 HeapFree(GetProcessHeap(), 0, file_name); 247 return report_result(pOIProtSink, STG_E_FILENOTFOUND); 248 } 249 250 object_name = p+2; 251 len = strlenW(object_name); 252 253 if(*object_name != '/' && *object_name != '\\') { 254 memmove(object_name+1, object_name, (len+1)*sizeof(WCHAR)); 255 *object_name = '/'; 256 len++; 257 } 258 259 if(object_name[len-1] == '/') 260 object_name[--len] = 0; 261 262 for(p=object_name; *p; p++) { 263 if(*p == '\\') 264 *p = '/'; 265 } 266 267 remove_dot_segments(object_name); 268 269 TRACE("Resolving %s\n", debugstr_w(object_name)); 270 271 memset(&chm_object, 0, sizeof(chm_object)); 272 res = chm_resolve_object(chm_file, object_name, &chm_object); 273 if(res != CHM_RESOLVE_SUCCESS) { 274 WARN("Could not resolve chm object\n"); 275 HeapFree(GetProcessHeap(), 0, file_name); 276 chm_close(chm_file); 277 return report_result(pOIProtSink, STG_E_FILENOTFOUND); 278 } 279 280 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, 281 strrchrW(object_name, '/')+1); 282 283 /* FIXME: Native doesn't use FindMimeFromData */ 284 hres = FindMimeFromData(NULL, object_name, NULL, 0, NULL, 0, &mime, 0); 285 HeapFree(GetProcessHeap(), 0, file_name); 286 if(SUCCEEDED(hres)) { 287 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, mime); 288 CoTaskMemFree(mime); 289 } 290 291 release_chm(This); /* Native leaks handle here */ 292 This->chm_file = chm_file; 293 This->chm_object = chm_object; 294 295 hres = IInternetProtocolSink_ReportData(pOIProtSink, 296 BSCF_FIRSTDATANOTIFICATION|BSCF_DATAFULLYAVAILABLE, 297 chm_object.length, chm_object.length); 298 if(FAILED(hres)) { 299 WARN("ReportData failed: %08x\n", hres); 300 release_chm(This); 301 return report_result(pOIProtSink, hres); 302 } 303 304 hres = IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_BEGINDOWNLOADDATA, NULL); 305 306 return report_result(pOIProtSink, hres); 307 } 308 309 static HRESULT WINAPI ITSProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData) 310 { 311 ITSProtocol *This = impl_from_IInternetProtocol(iface); 312 FIXME("(%p)->(%p)\n", This, pProtocolData); 313 return E_NOTIMPL; 314 } 315 316 static HRESULT WINAPI ITSProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason, 317 DWORD dwOptions) 318 { 319 ITSProtocol *This = impl_from_IInternetProtocol(iface); 320 FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions); 321 return E_NOTIMPL; 322 } 323 324 static HRESULT WINAPI ITSProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions) 325 { 326 ITSProtocol *This = impl_from_IInternetProtocol(iface); 327 328 TRACE("(%p)->(%08x)\n", This, dwOptions); 329 330 return S_OK; 331 } 332 333 static HRESULT WINAPI ITSProtocol_Suspend(IInternetProtocol *iface) 334 { 335 ITSProtocol *This = impl_from_IInternetProtocol(iface); 336 FIXME("(%p)\n", This); 337 return E_NOTIMPL; 338 } 339 340 static HRESULT WINAPI ITSProtocol_Resume(IInternetProtocol *iface) 341 { 342 ITSProtocol *This = impl_from_IInternetProtocol(iface); 343 FIXME("(%p)\n", This); 344 return E_NOTIMPL; 345 } 346 347 static HRESULT WINAPI ITSProtocol_Read(IInternetProtocol *iface, void *pv, 348 ULONG cb, ULONG *pcbRead) 349 { 350 ITSProtocol *This = impl_from_IInternetProtocol(iface); 351 352 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead); 353 354 if(!This->chm_file) 355 return INET_E_DATA_NOT_AVAILABLE; 356 357 *pcbRead = chm_retrieve_object(This->chm_file, &This->chm_object, pv, This->offset, cb); 358 This->offset += *pcbRead; 359 360 return *pcbRead ? S_OK : S_FALSE; 361 } 362 363 static HRESULT WINAPI ITSProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove, 364 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) 365 { 366 ITSProtocol *This = impl_from_IInternetProtocol(iface); 367 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition); 368 return E_NOTIMPL; 369 } 370 371 static HRESULT WINAPI ITSProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions) 372 { 373 ITSProtocol *This = impl_from_IInternetProtocol(iface); 374 375 TRACE("(%p)->(%08x)\n", This, dwOptions); 376 377 return S_OK; 378 } 379 380 static HRESULT WINAPI ITSProtocol_UnlockRequest(IInternetProtocol *iface) 381 { 382 ITSProtocol *This = impl_from_IInternetProtocol(iface); 383 384 TRACE("(%p)\n", This); 385 386 return S_OK; 387 } 388 389 static const IInternetProtocolVtbl ITSProtocolVtbl = { 390 ITSProtocol_QueryInterface, 391 ITSProtocol_AddRef, 392 ITSProtocol_Release, 393 ITSProtocol_Start, 394 ITSProtocol_Continue, 395 ITSProtocol_Abort, 396 ITSProtocol_Terminate, 397 ITSProtocol_Suspend, 398 ITSProtocol_Resume, 399 ITSProtocol_Read, 400 ITSProtocol_Seek, 401 ITSProtocol_LockRequest, 402 ITSProtocol_UnlockRequest 403 }; 404 405 static HRESULT WINAPI ITSProtocolInfo_QueryInterface(IInternetProtocolInfo *iface, 406 REFIID riid, void **ppv) 407 { 408 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 409 return IInternetProtocol_QueryInterface(&This->IInternetProtocol_iface, riid, ppv); 410 } 411 412 static ULONG WINAPI ITSProtocolInfo_AddRef(IInternetProtocolInfo *iface) 413 { 414 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 415 return IInternetProtocol_AddRef(&This->IInternetProtocol_iface); 416 } 417 418 static ULONG WINAPI ITSProtocolInfo_Release(IInternetProtocolInfo *iface) 419 { 420 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 421 return IInternetProtocol_Release(&This->IInternetProtocol_iface); 422 } 423 424 static HRESULT WINAPI ITSProtocolInfo_ParseUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl, 425 PARSEACTION ParseAction, DWORD dwParseFlags, LPWSTR pwzResult, DWORD cchResult, 426 DWORD *pcchResult, DWORD dwReserved) 427 { 428 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 429 430 TRACE("(%p)->(%s %x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), ParseAction, 431 dwParseFlags, pwzResult, cchResult, pcchResult, dwReserved); 432 433 switch(ParseAction) { 434 case PARSE_CANONICALIZE: 435 FIXME("PARSE_CANONICALIZE\n"); 436 return E_NOTIMPL; 437 case PARSE_SECURITY_URL: 438 FIXME("PARSE_SECURITY_URL\n"); 439 return E_NOTIMPL; 440 default: 441 return INET_E_DEFAULT_ACTION; 442 } 443 444 return S_OK; 445 } 446 447 static HRESULT WINAPI ITSProtocolInfo_CombineUrl(IInternetProtocolInfo *iface, 448 LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags, LPWSTR pwzResult, 449 DWORD cchResult, DWORD* pcchResult, DWORD dwReserved) 450 { 451 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 452 LPCWSTR base_end, ptr; 453 DWORD rel_len; 454 455 static const WCHAR separator[] = {':',':',0}; 456 457 TRACE("(%p)->(%s %s %08x %p %d %p %d)\n", This, debugstr_w(pwzBaseUrl), 458 debugstr_w(pwzRelativeUrl), dwCombineFlags, pwzResult, cchResult, 459 pcchResult, dwReserved); 460 461 base_end = strstrW(pwzBaseUrl, separator); 462 if(!base_end) 463 return 0x80041001; 464 base_end += 2; 465 466 if(!skip_schema(pwzBaseUrl)) 467 return INET_E_USE_DEFAULT_PROTOCOLHANDLER; 468 469 if(strchrW(pwzRelativeUrl, ':')) 470 return STG_E_INVALIDNAME; 471 472 if(pwzRelativeUrl[0] == '#') { 473 base_end += strlenW(base_end); 474 }else if(pwzRelativeUrl[0] != '/') { 475 ptr = strrchrW(base_end, '/'); 476 if(ptr) 477 base_end = ptr+1; 478 else 479 base_end += strlenW(base_end); 480 } 481 482 rel_len = strlenW(pwzRelativeUrl)+1; 483 484 *pcchResult = rel_len + (base_end-pwzBaseUrl); 485 486 if(*pcchResult > cchResult) 487 return E_OUTOFMEMORY; 488 489 memcpy(pwzResult, pwzBaseUrl, (base_end-pwzBaseUrl)*sizeof(WCHAR)); 490 strcpyW(pwzResult + (base_end-pwzBaseUrl), pwzRelativeUrl); 491 492 return S_OK; 493 } 494 495 static HRESULT WINAPI ITSProtocolInfo_CompareUrl(IInternetProtocolInfo *iface, LPCWSTR pwzUrl1, 496 LPCWSTR pwzUrl2, DWORD dwCompareFlags) 497 { 498 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 499 FIXME("%p)->(%s %s %08x)\n", This, debugstr_w(pwzUrl1), debugstr_w(pwzUrl2), dwCompareFlags); 500 return E_NOTIMPL; 501 } 502 503 static HRESULT WINAPI ITSProtocolInfo_QueryInfo(IInternetProtocolInfo *iface, LPCWSTR pwzUrl, 504 QUERYOPTION QueryOption, DWORD dwQueryFlags, LPVOID pBuffer, DWORD cbBuffer, DWORD* pcbBuf, 505 DWORD dwReserved) 506 { 507 ITSProtocol *This = impl_from_IInternetProtocolInfo(iface); 508 FIXME("(%p)->(%s %08x %08x %p %d %p %d)\n", This, debugstr_w(pwzUrl), QueryOption, 509 dwQueryFlags, pBuffer, cbBuffer, pcbBuf, dwReserved); 510 return E_NOTIMPL; 511 } 512 513 static const IInternetProtocolInfoVtbl ITSProtocolInfoVtbl = { 514 ITSProtocolInfo_QueryInterface, 515 ITSProtocolInfo_AddRef, 516 ITSProtocolInfo_Release, 517 ITSProtocolInfo_ParseUrl, 518 ITSProtocolInfo_CombineUrl, 519 ITSProtocolInfo_CompareUrl, 520 ITSProtocolInfo_QueryInfo 521 }; 522 523 HRESULT ITSProtocol_create(IUnknown *pUnkOuter, LPVOID *ppobj) 524 { 525 ITSProtocol *ret; 526 527 TRACE("(%p %p)\n", pUnkOuter, ppobj); 528 529 ITSS_LockModule(); 530 531 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ITSProtocol)); 532 533 ret->IInternetProtocol_iface.lpVtbl = &ITSProtocolVtbl; 534 ret->IInternetProtocolInfo_iface.lpVtbl = &ITSProtocolInfoVtbl; 535 ret->ref = 1; 536 537 *ppobj = &ret->IInternetProtocol_iface; 538 539 return S_OK; 540 } 541