1 /* 2 * Copyright 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 "urlmon_main.h" 20 #include "wine/debug.h" 21 22 #define NO_SHLWAPI_REG 23 #include "shlwapi.h" 24 25 WINE_DEFAULT_DEBUG_CHANNEL(urlmon); 26 27 typedef struct { 28 IUnknown IUnknown_inner; 29 IInternetProtocolEx IInternetProtocolEx_iface; 30 31 LONG ref; 32 IUnknown *outer; 33 34 IStream *stream; 35 } MkProtocol; 36 37 static inline MkProtocol *impl_from_IUnknown(IUnknown *iface) 38 { 39 return CONTAINING_RECORD(iface, MkProtocol, IUnknown_inner); 40 } 41 42 static inline MkProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface) 43 { 44 return CONTAINING_RECORD(iface, MkProtocol, IInternetProtocolEx_iface); 45 } 46 47 static HRESULT WINAPI MkProtocolUnk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) 48 { 49 MkProtocol *This = impl_from_IUnknown(iface); 50 51 if(IsEqualGUID(&IID_IUnknown, riid)) { 52 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 53 *ppv = &This->IUnknown_inner; 54 }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) { 55 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv); 56 *ppv = &This->IInternetProtocolEx_iface; 57 }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) { 58 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv); 59 *ppv = &This->IInternetProtocolEx_iface; 60 }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) { 61 TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv); 62 *ppv = &This->IInternetProtocolEx_iface; 63 }else { 64 *ppv = NULL; 65 WARN("not supported interface %s\n", debugstr_guid(riid)); 66 return E_NOINTERFACE; 67 } 68 69 IUnknown_AddRef((IUnknown*)*ppv); 70 return S_OK; 71 } 72 73 static ULONG WINAPI MkProtocolUnk_AddRef(IUnknown *iface) 74 { 75 MkProtocol *This = impl_from_IUnknown(iface); 76 LONG ref = InterlockedIncrement(&This->ref); 77 TRACE("(%p) ref=%d\n", This, ref); 78 return ref; 79 } 80 81 static ULONG WINAPI MkProtocolUnk_Release(IUnknown *iface) 82 { 83 MkProtocol *This = impl_from_IUnknown(iface); 84 LONG ref = InterlockedDecrement(&This->ref); 85 86 TRACE("(%p) ref=%d\n", This, ref); 87 88 if(!ref) { 89 if(This->stream) 90 IStream_Release(This->stream); 91 92 heap_free(This); 93 94 URLMON_UnlockModule(); 95 } 96 97 return ref; 98 } 99 100 static const IUnknownVtbl MkProtocolUnkVtbl = { 101 MkProtocolUnk_QueryInterface, 102 MkProtocolUnk_AddRef, 103 MkProtocolUnk_Release 104 }; 105 106 static HRESULT WINAPI MkProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv) 107 { 108 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 109 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); 110 return IUnknown_QueryInterface(This->outer, riid, ppv); 111 } 112 113 static ULONG WINAPI MkProtocol_AddRef(IInternetProtocolEx *iface) 114 { 115 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 116 TRACE("(%p)\n", This); 117 return IUnknown_AddRef(This->outer); 118 } 119 120 static ULONG WINAPI MkProtocol_Release(IInternetProtocolEx *iface) 121 { 122 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 123 TRACE("(%p)\n", This); 124 return IUnknown_Release(This->outer); 125 } 126 127 static HRESULT report_result(IInternetProtocolSink *sink, HRESULT hres, DWORD dwError) 128 { 129 IInternetProtocolSink_ReportResult(sink, hres, dwError, NULL); 130 return hres; 131 } 132 133 static HRESULT WINAPI MkProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl, 134 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, 135 DWORD grfPI, HANDLE_PTR dwReserved) 136 { 137 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 138 HRESULT hres; 139 IUri *uri; 140 141 TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink, 142 pOIBindInfo, grfPI, dwReserved); 143 144 hres = CreateUri(szUrl, 0, 0, &uri); 145 if(FAILED(hres)) 146 return hres; 147 148 hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink, 149 pOIBindInfo, grfPI, (HANDLE*)dwReserved); 150 151 IUri_Release(uri); 152 return hres; 153 } 154 155 static HRESULT WINAPI MkProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData) 156 { 157 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 158 FIXME("(%p)->(%p)\n", This, pProtocolData); 159 return E_NOTIMPL; 160 } 161 162 static HRESULT WINAPI MkProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason, 163 DWORD dwOptions) 164 { 165 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 166 FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions); 167 return E_NOTIMPL; 168 } 169 170 static HRESULT WINAPI MkProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions) 171 { 172 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 173 174 TRACE("(%p)->(%08x)\n", This, dwOptions); 175 176 return S_OK; 177 } 178 179 static HRESULT WINAPI MkProtocol_Suspend(IInternetProtocolEx *iface) 180 { 181 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 182 FIXME("(%p)\n", This); 183 return E_NOTIMPL; 184 } 185 186 static HRESULT WINAPI MkProtocol_Resume(IInternetProtocolEx *iface) 187 { 188 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 189 FIXME("(%p)\n", This); 190 return E_NOTIMPL; 191 } 192 193 static HRESULT WINAPI MkProtocol_Read(IInternetProtocolEx *iface, void *pv, 194 ULONG cb, ULONG *pcbRead) 195 { 196 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 197 198 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead); 199 200 if(!This->stream) 201 return E_FAIL; 202 203 return IStream_Read(This->stream, pv, cb, pcbRead); 204 } 205 206 static HRESULT WINAPI MkProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove, 207 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) 208 { 209 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 210 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition); 211 return E_NOTIMPL; 212 } 213 214 static HRESULT WINAPI MkProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions) 215 { 216 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 217 218 TRACE("(%p)->(%08x)\n", This, dwOptions); 219 220 return S_OK; 221 } 222 223 static HRESULT WINAPI MkProtocol_UnlockRequest(IInternetProtocolEx *iface) 224 { 225 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 226 227 TRACE("(%p)\n", This); 228 229 return S_OK; 230 } 231 232 static HRESULT WINAPI MkProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri, 233 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, 234 DWORD grfPI, HANDLE *dwReserved) 235 { 236 MkProtocol *This = impl_from_IInternetProtocolEx(iface); 237 LPWSTR mime, progid, display_name, colon_ptr; 238 DWORD bindf=0, eaten=0, scheme=0, len; 239 BSTR url, path = NULL; 240 IParseDisplayName *pdn; 241 BINDINFO bindinfo; 242 STATSTG statstg; 243 IMoniker *mon; 244 HRESULT hres; 245 CLSID clsid; 246 247 TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink, 248 pOIBindInfo, grfPI, dwReserved); 249 250 hres = IUri_GetScheme(pUri, &scheme); 251 if(FAILED(hres)) 252 return hres; 253 if(scheme != URL_SCHEME_MK) 254 return INET_E_INVALID_URL; 255 256 memset(&bindinfo, 0, sizeof(bindinfo)); 257 bindinfo.cbSize = sizeof(BINDINFO); 258 hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo); 259 if(FAILED(hres)) { 260 WARN("GetBindInfo failed: %08x\n", hres); 261 return hres; 262 } 263 264 ReleaseBindInfo(&bindinfo); 265 266 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, NULL); 267 268 hres = IUri_GetDisplayUri(pUri, &url); 269 if(FAILED(hres)) 270 return hres; 271 hres = FindMimeFromData(NULL, url, NULL, 0, NULL, 0, &mime, 0); 272 SysFreeString(url); 273 if(SUCCEEDED(hres)) { 274 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_MIMETYPEAVAILABLE, mime); 275 CoTaskMemFree(mime); 276 } 277 278 hres = IUri_GetPath(pUri, &path); 279 if(FAILED(hres)) 280 return hres; 281 len = SysStringLen(path)+1; 282 hres = UrlUnescapeW(path, NULL, &len, URL_UNESCAPE_INPLACE); 283 if (FAILED(hres)) { 284 SysFreeString(path); 285 return report_result(pOIProtSink, INET_E_RESOURCE_NOT_FOUND, ERROR_INVALID_PARAMETER); 286 } 287 288 progid = path+1; /* skip '@' symbol */ 289 colon_ptr = wcschr(path, ':'); 290 if(!colon_ptr) { 291 SysFreeString(path); 292 return report_result(pOIProtSink, INET_E_RESOURCE_NOT_FOUND, ERROR_INVALID_PARAMETER); 293 } 294 295 len = lstrlenW(path); 296 display_name = heap_alloc((len+1)*sizeof(WCHAR)); 297 memcpy(display_name, path, (len+1)*sizeof(WCHAR)); 298 299 progid[colon_ptr-progid] = 0; /* overwrite ':' with NULL terminator */ 300 hres = CLSIDFromProgID(progid, &clsid); 301 SysFreeString(path); 302 if(FAILED(hres)) 303 { 304 heap_free(display_name); 305 return report_result(pOIProtSink, INET_E_RESOURCE_NOT_FOUND, ERROR_INVALID_PARAMETER); 306 } 307 308 hres = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, 309 &IID_IParseDisplayName, (void**)&pdn); 310 if(FAILED(hres)) { 311 WARN("Could not create object %s\n", debugstr_guid(&clsid)); 312 heap_free(display_name); 313 return report_result(pOIProtSink, hres, ERROR_INVALID_PARAMETER); 314 } 315 316 hres = IParseDisplayName_ParseDisplayName(pdn, NULL /* FIXME */, display_name, &eaten, &mon); 317 heap_free(display_name); 318 IParseDisplayName_Release(pdn); 319 if(FAILED(hres)) { 320 WARN("ParseDisplayName failed: %08x\n", hres); 321 return report_result(pOIProtSink, hres, ERROR_INVALID_PARAMETER); 322 } 323 324 if(This->stream) { 325 IStream_Release(This->stream); 326 This->stream = NULL; 327 } 328 329 hres = IMoniker_BindToStorage(mon, NULL /* FIXME */, NULL, &IID_IStream, (void**)&This->stream); 330 IMoniker_Release(mon); 331 if(FAILED(hres)) { 332 WARN("BindToStorage failed: %08x\n", hres); 333 return report_result(pOIProtSink, hres, ERROR_INVALID_PARAMETER); 334 } 335 336 hres = IStream_Stat(This->stream, &statstg, STATFLAG_NONAME); 337 if(FAILED(hres)) { 338 WARN("Stat failed: %08x\n", hres); 339 return report_result(pOIProtSink, hres, ERROR_INVALID_PARAMETER); 340 } 341 342 IInternetProtocolSink_ReportData(pOIProtSink, 343 BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION, 344 statstg.cbSize.u.LowPart, statstg.cbSize.u.LowPart); 345 return report_result(pOIProtSink, S_OK, ERROR_SUCCESS); 346 } 347 348 static const IInternetProtocolExVtbl MkProtocolVtbl = { 349 MkProtocol_QueryInterface, 350 MkProtocol_AddRef, 351 MkProtocol_Release, 352 MkProtocol_Start, 353 MkProtocol_Continue, 354 MkProtocol_Abort, 355 MkProtocol_Terminate, 356 MkProtocol_Suspend, 357 MkProtocol_Resume, 358 MkProtocol_Read, 359 MkProtocol_Seek, 360 MkProtocol_LockRequest, 361 MkProtocol_UnlockRequest, 362 MkProtocol_StartEx 363 }; 364 365 HRESULT MkProtocol_Construct(IUnknown *outer, void **ppv) 366 { 367 MkProtocol *ret; 368 369 TRACE("(%p %p)\n", outer, ppv); 370 371 URLMON_LockModule(); 372 373 ret = heap_alloc(sizeof(MkProtocol)); 374 375 ret->IUnknown_inner.lpVtbl = &MkProtocolUnkVtbl; 376 ret->IInternetProtocolEx_iface.lpVtbl = &MkProtocolVtbl; 377 ret->ref = 1; 378 ret->outer = outer ? outer : &ret->IUnknown_inner; 379 ret->stream = NULL; 380 381 /* NOTE: 382 * Native returns NULL ppobj and S_OK in CreateInstance if called with IID_IUnknown riid and no outer. 383 */ 384 *ppv = &ret->IUnknown_inner; 385 return S_OK; 386 } 387