xref: /reactos/dll/win32/urlmon/binding.c (revision 84ccccab)
1 /*
2  * Copyright 2005-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 
21 static WCHAR cbinding_contextW[] = {'C','B','i','n','d','i','n','g',' ','C','o','n','t','e','x','t',0};
22 static WCHAR bscb_holderW[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
23 
24 typedef struct {
25     IUnknown IUnknown_iface;
26 
27     LONG ref;
28 
29     IInternetProtocolEx *protocol;
30 
31     HANDLE file;
32     HRESULT hres;
33 
34     LPWSTR cache_file;
35 } stgmed_buf_t;
36 
37 typedef struct _stgmed_obj_t stgmed_obj_t;
38 
39 typedef struct {
40     void (*release)(stgmed_obj_t*);
41     HRESULT (*fill_stgmed)(stgmed_obj_t*,STGMEDIUM*);
42     HRESULT (*get_result)(stgmed_obj_t*,DWORD,void**);
43 } stgmed_obj_vtbl;
44 
45 struct _stgmed_obj_t {
46     const stgmed_obj_vtbl *vtbl;
47 };
48 
49 typedef enum {
50     BEFORE_DOWNLOAD,
51     DOWNLOADING,
52     END_DOWNLOAD
53 } download_state_t;
54 
55 #define BINDING_LOCKED    0x0001
56 #define BINDING_STOPPED   0x0002
57 #define BINDING_OBJAVAIL  0x0004
58 #define BINDING_ABORTED   0x0008
59 
60 typedef struct {
61     IBinding              IBinding_iface;
62     IInternetProtocolSink IInternetProtocolSink_iface;
63     IInternetBindInfo     IInternetBindInfo_iface;
64     IWinInetHttpInfo      IWinInetHttpInfo_iface;
65     IServiceProvider      IServiceProvider_iface;
66 
67     LONG ref;
68 
69     IBindStatusCallback *callback;
70     IServiceProvider *service_provider;
71 
72     BindProtocol *protocol;
73 
74     stgmed_buf_t *stgmed_buf;
75     stgmed_obj_t *stgmed_obj;
76 
77     BINDINFO bindinfo;
78     DWORD bindf;
79     BOOL to_object;
80     LPWSTR mime;
81     UINT clipboard_format;
82     LPWSTR url;
83     LPWSTR redirect_url;
84     IID iid;
85     BOOL report_mime;
86     BOOL use_cache_file;
87     DWORD state;
88     HRESULT hres;
89     CLSID clsid;
90     download_state_t download_state;
91     IUnknown *obj;
92     IMoniker *mon;
93     IBindCtx *bctx;
94     HWND notif_hwnd;
95 
96     CRITICAL_SECTION section;
97 } Binding;
98 
99 static void read_protocol_data(stgmed_buf_t *stgmed_buf)
100 {
101     BYTE buf[8192];
102     DWORD read;
103     HRESULT hres;
104 
105     do hres = IInternetProtocolEx_Read(stgmed_buf->protocol, buf, sizeof(buf), &read);
106     while(hres == S_OK);
107 }
108 
109 static void dump_BINDINFO(BINDINFO *bi)
110 {
111     static const char * const BINDINFOF_str[] = {
112         "#0",
113         "BINDINFOF_URLENCODESTGMEDDATA",
114         "BINDINFOF_URLENCODEDEXTRAINFO"
115     };
116 
117     static const char * const BINDVERB_str[] = {
118         "BINDVERB_GET",
119         "BINDVERB_POST",
120         "BINDVERB_PUT",
121         "BINDVERB_CUSTOM"
122     };
123 
124     TRACE("\n"
125             "BINDINFO = {\n"
126             "    %d, %s,\n"
127             "    {%d, %p, %p},\n"
128             "    %s,\n"
129             "    %s,\n"
130             "    %s,\n"
131             "    %d, %08x, %d, %d\n"
132             "    {%d %p %x},\n"
133             "    %s\n"
134             "    %p, %d\n"
135             "}\n",
136 
137             bi->cbSize, debugstr_w(bi->szExtraInfo),
138             bi->stgmedData.tymed, bi->stgmedData.u.hGlobal, bi->stgmedData.pUnkForRelease,
139             bi->grfBindInfoF > BINDINFOF_URLENCODEDEXTRAINFO
140                 ? "unknown" : BINDINFOF_str[bi->grfBindInfoF],
141             bi->dwBindVerb > BINDVERB_CUSTOM
142                 ? "unknown" : BINDVERB_str[bi->dwBindVerb],
143             debugstr_w(bi->szCustomVerb),
144             bi->cbstgmedData, bi->dwOptions, bi->dwOptionsFlags, bi->dwCodePage,
145             bi->securityAttributes.nLength,
146             bi->securityAttributes.lpSecurityDescriptor,
147             bi->securityAttributes.bInheritHandle,
148             debugstr_guid(&bi->iid),
149             bi->pUnk, bi->dwReserved
150             );
151 }
152 
153 static void mime_available(Binding *This, LPCWSTR mime)
154 {
155     heap_free(This->mime);
156     This->mime = heap_strdupW(mime);
157 
158     if(!This->mime || !This->report_mime)
159         return;
160 
161     IBindStatusCallback_OnProgress(This->callback, 0, 0, BINDSTATUS_MIMETYPEAVAILABLE, This->mime);
162 
163     This->clipboard_format = RegisterClipboardFormatW(This->mime);
164 }
165 
166 static void stop_binding(Binding *binding, HRESULT hres, LPCWSTR str)
167 {
168     if(binding->state & BINDING_LOCKED) {
169         IInternetProtocolEx_UnlockRequest(&binding->protocol->IInternetProtocolEx_iface);
170         binding->state &= ~BINDING_LOCKED;
171     }
172 
173     if(!(binding->state & BINDING_STOPPED)) {
174         binding->state |= BINDING_STOPPED;
175 
176         binding->hres = hres;
177         IBindStatusCallback_OnStopBinding(binding->callback, hres, str);
178     }
179 }
180 
181 static LPWSTR get_mime_clsid(LPCWSTR mime, CLSID *clsid)
182 {
183     LPWSTR key_name, ret;
184     DWORD res, type, size;
185     HKEY hkey;
186     int len;
187     HRESULT hres;
188 
189     static const WCHAR mime_keyW[] =
190         {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\',
191          'C','o','n','t','e','n','t',' ','T','y','p','e','\\'};
192     static const WCHAR clsidW[] = {'C','L','S','I','D',0};
193 
194     len = strlenW(mime)+1;
195     key_name = heap_alloc(sizeof(mime_keyW) + len*sizeof(WCHAR));
196     memcpy(key_name, mime_keyW, sizeof(mime_keyW));
197     strcpyW(key_name + sizeof(mime_keyW)/sizeof(WCHAR), mime);
198 
199     res = RegOpenKeyW(HKEY_CLASSES_ROOT, key_name, &hkey);
200     heap_free(key_name);
201     if(res != ERROR_SUCCESS) {
202         WARN("Could not open MIME key: %x\n", res);
203         return NULL;
204     }
205 
206     size = 50*sizeof(WCHAR);
207     ret = heap_alloc(size);
208     res = RegQueryValueExW(hkey, clsidW, NULL, &type, (LPBYTE)ret, &size);
209     RegCloseKey(hkey);
210     if(res != ERROR_SUCCESS) {
211         WARN("Could not get CLSID: %08x\n", res);
212         heap_free(ret);
213         return NULL;
214     }
215 
216     hres = CLSIDFromString(ret, clsid);
217     if(FAILED(hres)) {
218         WARN("Could not parse CLSID: %08x\n", hres);
219         heap_free(ret);
220         return NULL;
221     }
222 
223     return ret;
224 }
225 
226 static void load_doc_mon(Binding *binding, IPersistMoniker *persist)
227 {
228     IBindCtx *bctx;
229     HRESULT hres;
230 
231     hres = CreateAsyncBindCtxEx(binding->bctx, 0, NULL, NULL, &bctx, 0);
232     if(FAILED(hres)) {
233         WARN("CreateAsyncBindCtxEx failed: %08x\n", hres);
234         return;
235     }
236 
237     IBindCtx_RevokeObjectParam(bctx, bscb_holderW);
238     IBindCtx_RegisterObjectParam(bctx, cbinding_contextW, (IUnknown*)&binding->IBinding_iface);
239 
240     hres = IPersistMoniker_Load(persist, binding->download_state == END_DOWNLOAD, binding->mon, bctx, 0x12);
241     IBindCtx_RevokeObjectParam(bctx, cbinding_contextW);
242     IBindCtx_Release(bctx);
243     if(FAILED(hres))
244         FIXME("Load failed: %08x\n", hres);
245 }
246 
247 static HRESULT create_mime_object(Binding *binding, const CLSID *clsid, LPCWSTR clsid_str)
248 {
249     IPersistMoniker *persist;
250     HRESULT hres;
251 
252     hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
253                             &binding->iid, (void**)&binding->obj);
254     if(FAILED(hres)) {
255         WARN("CoCreateInstance failed: %08x\n", hres);
256         return INET_E_CANNOT_INSTANTIATE_OBJECT;
257     }
258 
259     binding->state |= BINDING_OBJAVAIL;
260 
261     hres = IUnknown_QueryInterface(binding->obj, &IID_IPersistMoniker, (void**)&persist);
262     if(SUCCEEDED(hres)) {
263         IMonikerProp *prop;
264 
265         hres = IPersistMoniker_QueryInterface(persist, &IID_IMonikerProp, (void**)&prop);
266         if(SUCCEEDED(hres)) {
267             IMonikerProp_PutProperty(prop, MIMETYPEPROP, binding->mime);
268             IMonikerProp_PutProperty(prop, CLASSIDPROP, clsid_str);
269             IMonikerProp_Release(prop);
270         }
271 
272         load_doc_mon(binding, persist);
273 
274         IPersistMoniker_Release(persist);
275     }else {
276         FIXME("Could not get IPersistMoniker: %08x\n", hres);
277         /* FIXME: Try query IPersistFile */
278     }
279 
280     IBindStatusCallback_OnObjectAvailable(binding->callback, &binding->iid, binding->obj);
281 
282     return S_OK;
283 }
284 
285 static void create_object(Binding *binding)
286 {
287     LPWSTR clsid_str;
288     CLSID clsid;
289     HRESULT hres;
290 
291     if(!binding->mime) {
292         FIXME("MIME not available\n");
293         return;
294     }
295 
296     if((clsid_str = get_mime_clsid(binding->mime, &clsid)))
297         IBindStatusCallback_OnProgress(binding->callback, 0, 0, BINDSTATUS_CLASSIDAVAILABLE, clsid_str);
298 
299     IBindStatusCallback_OnProgress(binding->callback, 0, 0, BINDSTATUS_BEGINSYNCOPERATION, NULL);
300 
301     if(clsid_str) {
302         hres = create_mime_object(binding, &clsid, clsid_str);
303         heap_free(clsid_str);
304     }else {
305         FIXME("Could not find object for MIME %s\n", debugstr_w(binding->mime));
306         hres = REGDB_E_CLASSNOTREG;
307     }
308 
309     IBindStatusCallback_OnProgress(binding->callback, 0, 0, BINDSTATUS_ENDSYNCOPERATION, NULL);
310     binding->clsid = CLSID_NULL;
311 
312     stop_binding(binding, hres, NULL);
313     if(FAILED(hres))
314         IInternetProtocolEx_Terminate(&binding->protocol->IInternetProtocolEx_iface, 0);
315 }
316 
317 static void cache_file_available(Binding *This, const WCHAR *file_name)
318 {
319     heap_free(This->stgmed_buf->cache_file);
320     This->stgmed_buf->cache_file = heap_strdupW(file_name);
321 
322     if(This->use_cache_file) {
323         This->stgmed_buf->file = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
324                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
325         if(This->stgmed_buf->file == INVALID_HANDLE_VALUE)
326             WARN("CreateFile failed: %u\n", GetLastError());
327     }
328 }
329 
330 static inline stgmed_buf_t *impl_from_IUnknown(IUnknown *iface)
331 {
332     return CONTAINING_RECORD(iface, stgmed_buf_t, IUnknown_iface);
333 }
334 
335 static HRESULT WINAPI StgMedUnk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
336 {
337     stgmed_buf_t *This = impl_from_IUnknown(iface);
338 
339     *ppv = NULL;
340 
341     if(IsEqualGUID(riid, &IID_IUnknown)) {
342         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
343 
344         *ppv = &This->IUnknown_iface;
345         IUnknown_AddRef(&This->IUnknown_iface);
346         return S_OK;
347     }
348 
349     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
350     return E_NOINTERFACE;
351 }
352 
353 static ULONG WINAPI StgMedUnk_AddRef(IUnknown *iface)
354 {
355     stgmed_buf_t *This = impl_from_IUnknown(iface);
356     LONG ref = InterlockedIncrement(&This->ref);
357 
358     TRACE("(%p) ref=%d\n", This, ref);
359 
360     return ref;
361 }
362 
363 static ULONG WINAPI StgMedUnk_Release(IUnknown *iface)
364 {
365     stgmed_buf_t *This = impl_from_IUnknown(iface);
366     LONG ref = InterlockedDecrement(&This->ref);
367 
368     TRACE("(%p) ref=%d\n", This, ref);
369 
370     if(!ref) {
371         if(This->file != INVALID_HANDLE_VALUE)
372             CloseHandle(This->file);
373         IInternetProtocolEx_Release(This->protocol);
374         heap_free(This->cache_file);
375         heap_free(This);
376 
377         URLMON_UnlockModule();
378     }
379 
380     return ref;
381 }
382 
383 static const IUnknownVtbl StgMedUnkVtbl = {
384     StgMedUnk_QueryInterface,
385     StgMedUnk_AddRef,
386     StgMedUnk_Release
387 };
388 
389 static stgmed_buf_t *create_stgmed_buf(IInternetProtocolEx *protocol)
390 {
391     stgmed_buf_t *ret = heap_alloc(sizeof(*ret));
392 
393     ret->IUnknown_iface.lpVtbl = &StgMedUnkVtbl;
394     ret->ref = 1;
395     ret->file = INVALID_HANDLE_VALUE;
396     ret->hres = S_OK;
397     ret->cache_file = NULL;
398 
399     IInternetProtocolEx_AddRef(protocol);
400     ret->protocol = protocol;
401 
402     URLMON_LockModule();
403 
404     return ret;
405 }
406 
407 typedef struct {
408     stgmed_obj_t stgmed_obj;
409     IStream IStream_iface;
410 
411     LONG ref;
412 
413     stgmed_buf_t *buf;
414 } ProtocolStream;
415 
416 static inline ProtocolStream *impl_from_IStream(IStream *iface)
417 {
418     return CONTAINING_RECORD(iface, ProtocolStream, IStream_iface);
419 }
420 
421 static HRESULT WINAPI ProtocolStream_QueryInterface(IStream *iface,
422                                                           REFIID riid, void **ppv)
423 {
424     ProtocolStream *This = impl_from_IStream(iface);
425 
426     *ppv = NULL;
427 
428     if(IsEqualGUID(&IID_IUnknown, riid)) {
429         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
430         *ppv = &This->IStream_iface;
431     }else if(IsEqualGUID(&IID_ISequentialStream, riid)) {
432         TRACE("(%p)->(IID_ISequentialStream %p)\n", This, ppv);
433         *ppv = &This->IStream_iface;
434     }else if(IsEqualGUID(&IID_IStream, riid)) {
435         TRACE("(%p)->(IID_IStream %p)\n", This, ppv);
436         *ppv = &This->IStream_iface;
437     }
438 
439     if(*ppv) {
440         IStream_AddRef(&This->IStream_iface);
441         return S_OK;
442     }
443 
444     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
445     return E_NOINTERFACE;
446 }
447 
448 static ULONG WINAPI ProtocolStream_AddRef(IStream *iface)
449 {
450     ProtocolStream *This = impl_from_IStream(iface);
451     LONG ref = InterlockedIncrement(&This->ref);
452 
453     TRACE("(%p) ref=%d\n", This, ref);
454 
455     return ref;
456 }
457 
458 static ULONG WINAPI ProtocolStream_Release(IStream *iface)
459 {
460     ProtocolStream *This = impl_from_IStream(iface);
461     LONG ref = InterlockedDecrement(&This->ref);
462 
463     TRACE("(%p) ref=%d\n", This, ref);
464 
465     if(!ref) {
466         IUnknown_Release(&This->buf->IUnknown_iface);
467         heap_free(This);
468 
469         URLMON_UnlockModule();
470     }
471 
472     return ref;
473 }
474 
475 static HRESULT WINAPI ProtocolStream_Read(IStream *iface, void *pv,
476                                           ULONG cb, ULONG *pcbRead)
477 {
478     ProtocolStream *This = impl_from_IStream(iface);
479     DWORD read = 0;
480     HRESULT hres;
481 
482     TRACE("(%p)->(%p %d %p)\n", This, pv, cb, pcbRead);
483 
484     if(This->buf->file == INVALID_HANDLE_VALUE) {
485         hres = This->buf->hres = IInternetProtocolEx_Read(This->buf->protocol, (PBYTE)pv, cb, &read);
486     }else {
487         hres = ReadFile(This->buf->file, pv, cb, &read, NULL) ? S_OK : INET_E_DOWNLOAD_FAILURE;
488     }
489 
490     if (pcbRead)
491         *pcbRead = read;
492 
493     if(hres == E_PENDING)
494         return E_PENDING;
495     else if(FAILED(hres))
496         FIXME("Read failed: %08x\n", hres);
497 
498     return read ? S_OK : S_FALSE;
499 }
500 
501 static HRESULT WINAPI ProtocolStream_Write(IStream *iface, const void *pv,
502                                           ULONG cb, ULONG *pcbWritten)
503 {
504     ProtocolStream *This = impl_from_IStream(iface);
505 
506     TRACE("(%p)->(%p %d %p)\n", This, pv, cb, pcbWritten);
507 
508     return STG_E_ACCESSDENIED;
509 }
510 
511 static HRESULT WINAPI ProtocolStream_Seek(IStream *iface, LARGE_INTEGER dlibMove,
512                                          DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
513 {
514     ProtocolStream *This = impl_from_IStream(iface);
515     LARGE_INTEGER new_pos;
516     DWORD method;
517 
518     TRACE("(%p)->(%d %08x %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
519 
520     if(This->buf->file == INVALID_HANDLE_VALUE) {
521         /* We should probably call protocol handler's Seek. */
522         FIXME("no cache file, not supported\n");
523         return E_FAIL;
524     }
525 
526     switch(dwOrigin) {
527     case STREAM_SEEK_SET:
528         method = FILE_BEGIN;
529         break;
530     case STREAM_SEEK_CUR:
531         method = FILE_CURRENT;
532         break;
533     case STREAM_SEEK_END:
534         method = FILE_END;
535         break;
536     default:
537         WARN("Invalid origin %x\n", dwOrigin);
538         return E_FAIL;
539     }
540 
541     if(!SetFilePointerEx(This->buf->file, dlibMove, &new_pos, method)) {
542         FIXME("SetFilePointerEx failed: %u\n", GetLastError());
543         return E_FAIL;
544     }
545 
546     if(plibNewPosition)
547         plibNewPosition->QuadPart = new_pos.QuadPart;
548     return S_OK;
549 }
550 
551 static HRESULT WINAPI ProtocolStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
552 {
553     ProtocolStream *This = impl_from_IStream(iface);
554     FIXME("(%p)->(%d)\n", This, libNewSize.u.LowPart);
555     return E_NOTIMPL;
556 }
557 
558 static HRESULT WINAPI ProtocolStream_CopyTo(IStream *iface, IStream *pstm,
559         ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
560 {
561     ProtocolStream *This = impl_from_IStream(iface);
562     FIXME("(%p)->(%p %d %p %p)\n", This, pstm, cb.u.LowPart, pcbRead, pcbWritten);
563     return E_NOTIMPL;
564 }
565 
566 static HRESULT WINAPI ProtocolStream_Commit(IStream *iface, DWORD grfCommitFlags)
567 {
568     ProtocolStream *This = impl_from_IStream(iface);
569 
570     TRACE("(%p)->(%08x)\n", This, grfCommitFlags);
571 
572     return E_NOTIMPL;
573 }
574 
575 static HRESULT WINAPI ProtocolStream_Revert(IStream *iface)
576 {
577     ProtocolStream *This = impl_from_IStream(iface);
578 
579     TRACE("(%p)\n", This);
580 
581     return E_NOTIMPL;
582 }
583 
584 static HRESULT WINAPI ProtocolStream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
585                                                ULARGE_INTEGER cb, DWORD dwLockType)
586 {
587     ProtocolStream *This = impl_from_IStream(iface);
588     FIXME("(%p)->(%d %d %d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
589     return E_NOTIMPL;
590 }
591 
592 static HRESULT WINAPI ProtocolStream_UnlockRegion(IStream *iface,
593         ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
594 {
595     ProtocolStream *This = impl_from_IStream(iface);
596     FIXME("(%p)->(%d %d %d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
597     return E_NOTIMPL;
598 }
599 
600 static HRESULT WINAPI ProtocolStream_Stat(IStream *iface, STATSTG *pstatstg,
601                                          DWORD dwStatFlag)
602 {
603     ProtocolStream *This = impl_from_IStream(iface);
604     TRACE("(%p)->(%p %08x)\n", This, pstatstg, dwStatFlag);
605 
606     if(!pstatstg)
607         return E_FAIL;
608 
609     memset(pstatstg, 0, sizeof(STATSTG));
610 
611     if(!(dwStatFlag&STATFLAG_NONAME) && This->buf->cache_file) {
612         pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->buf->cache_file)+1)*sizeof(WCHAR));
613         if(!pstatstg->pwcsName)
614             return STG_E_INSUFFICIENTMEMORY;
615 
616         lstrcpyW(pstatstg->pwcsName, This->buf->cache_file);
617     }
618 
619     pstatstg->type = STGTY_STREAM;
620     if(This->buf->file != INVALID_HANDLE_VALUE) {
621         GetFileSizeEx(This->buf->file, (PLARGE_INTEGER)&pstatstg->cbSize);
622         GetFileTime(This->buf->file, &pstatstg->ctime, &pstatstg->atime, &pstatstg->mtime);
623         if(pstatstg->cbSize.QuadPart)
624             pstatstg->grfMode = GENERIC_READ;
625     }
626 
627     return S_OK;
628 }
629 
630 static HRESULT WINAPI ProtocolStream_Clone(IStream *iface, IStream **ppstm)
631 {
632     ProtocolStream *This = impl_from_IStream(iface);
633     FIXME("(%p)->(%p)\n", This, ppstm);
634     return E_NOTIMPL;
635 }
636 
637 static const IStreamVtbl ProtocolStreamVtbl = {
638     ProtocolStream_QueryInterface,
639     ProtocolStream_AddRef,
640     ProtocolStream_Release,
641     ProtocolStream_Read,
642     ProtocolStream_Write,
643     ProtocolStream_Seek,
644     ProtocolStream_SetSize,
645     ProtocolStream_CopyTo,
646     ProtocolStream_Commit,
647     ProtocolStream_Revert,
648     ProtocolStream_LockRegion,
649     ProtocolStream_UnlockRegion,
650     ProtocolStream_Stat,
651     ProtocolStream_Clone
652 };
653 
654 static void stgmed_stream_release(stgmed_obj_t *obj)
655 {
656     ProtocolStream *stream = (ProtocolStream*)obj;
657     IStream_Release(&stream->IStream_iface);
658 }
659 
660 static HRESULT stgmed_stream_fill_stgmed(stgmed_obj_t *obj, STGMEDIUM *stgmed)
661 {
662     ProtocolStream *stream = (ProtocolStream*)obj;
663 
664     stgmed->tymed = TYMED_ISTREAM;
665     stgmed->u.pstm = &stream->IStream_iface;
666     stgmed->pUnkForRelease = &stream->buf->IUnknown_iface;
667 
668     return S_OK;
669 }
670 
671 static HRESULT stgmed_stream_get_result(stgmed_obj_t *obj, DWORD bindf, void **result)
672 {
673     ProtocolStream *stream = (ProtocolStream*)obj;
674 
675     if(!(bindf & BINDF_ASYNCHRONOUS) && stream->buf->file == INVALID_HANDLE_VALUE
676        && stream->buf->hres != S_FALSE)
677         return INET_E_DATA_NOT_AVAILABLE;
678 
679     IStream_AddRef(&stream->IStream_iface);
680     *result = &stream->IStream_iface;
681     return S_OK;
682 }
683 
684 static const stgmed_obj_vtbl stgmed_stream_vtbl = {
685     stgmed_stream_release,
686     stgmed_stream_fill_stgmed,
687     stgmed_stream_get_result
688 };
689 
690 typedef struct {
691     stgmed_obj_t stgmed_obj;
692     stgmed_buf_t *buf;
693 } stgmed_file_obj_t;
694 
695 static stgmed_obj_t *create_stgmed_stream(stgmed_buf_t *buf)
696 {
697     ProtocolStream *ret = heap_alloc(sizeof(ProtocolStream));
698 
699     ret->stgmed_obj.vtbl = &stgmed_stream_vtbl;
700     ret->IStream_iface.lpVtbl = &ProtocolStreamVtbl;
701     ret->ref = 1;
702 
703     IUnknown_AddRef(&buf->IUnknown_iface);
704     ret->buf = buf;
705 
706     URLMON_LockModule();
707 
708     return &ret->stgmed_obj;
709 }
710 
711 static void stgmed_file_release(stgmed_obj_t *obj)
712 {
713     stgmed_file_obj_t *file_obj = (stgmed_file_obj_t*)obj;
714 
715     IUnknown_Release(&file_obj->buf->IUnknown_iface);
716     heap_free(file_obj);
717 }
718 
719 static HRESULT stgmed_file_fill_stgmed(stgmed_obj_t *obj, STGMEDIUM *stgmed)
720 {
721     stgmed_file_obj_t *file_obj = (stgmed_file_obj_t*)obj;
722 
723     if(!file_obj->buf->cache_file) {
724         WARN("cache_file not set\n");
725         return INET_E_DATA_NOT_AVAILABLE;
726     }
727 
728     read_protocol_data(file_obj->buf);
729 
730     stgmed->tymed = TYMED_FILE;
731     stgmed->u.lpszFileName = file_obj->buf->cache_file;
732     stgmed->pUnkForRelease = &file_obj->buf->IUnknown_iface;
733 
734     return S_OK;
735 }
736 
737 static HRESULT stgmed_file_get_result(stgmed_obj_t *obj, DWORD bindf, void **result)
738 {
739     return bindf & BINDF_ASYNCHRONOUS ? MK_S_ASYNCHRONOUS : S_OK;
740 }
741 
742 static const stgmed_obj_vtbl stgmed_file_vtbl = {
743     stgmed_file_release,
744     stgmed_file_fill_stgmed,
745     stgmed_file_get_result
746 };
747 
748 static stgmed_obj_t *create_stgmed_file(stgmed_buf_t *buf)
749 {
750     stgmed_file_obj_t *ret = heap_alloc(sizeof(*ret));
751 
752     ret->stgmed_obj.vtbl = &stgmed_file_vtbl;
753 
754     IUnknown_AddRef(&buf->IUnknown_iface);
755     ret->buf = buf;
756 
757     return &ret->stgmed_obj;
758 }
759 
760 static inline Binding *impl_from_IBinding(IBinding *iface)
761 {
762     return CONTAINING_RECORD(iface, Binding, IBinding_iface);
763 }
764 
765 static HRESULT WINAPI Binding_QueryInterface(IBinding *iface, REFIID riid, void **ppv)
766 {
767     Binding *This = impl_from_IBinding(iface);
768 
769     *ppv = NULL;
770 
771     if(IsEqualGUID(&IID_IUnknown, riid)) {
772         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
773         *ppv = &This->IBinding_iface;
774     }else if(IsEqualGUID(&IID_IBinding, riid)) {
775         TRACE("(%p)->(IID_IBinding %p)\n", This, ppv);
776         *ppv = &This->IBinding_iface;
777     }else if(IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
778         TRACE("(%p)->(IID_IInternetProtocolSink %p)\n", This, ppv);
779         *ppv = &This->IInternetProtocolSink_iface;
780     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
781         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
782         *ppv = &This->IInternetBindInfo_iface;
783     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
784         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
785         *ppv = &This->IServiceProvider_iface;
786     }else if(IsEqualGUID(&IID_IWinInetInfo, riid)) {
787         TRACE("(%p)->(IID_IWinInetInfo %p)\n", This, ppv);
788 
789         /* NOTE: This violidates COM rules, but tests prove that we should do it */
790         if(!This->protocol->wininet_info)
791            return E_NOINTERFACE;
792 
793         *ppv = &This->IWinInetHttpInfo_iface;
794     }else if(IsEqualGUID(&IID_IWinInetHttpInfo, riid)) {
795         TRACE("(%p)->(IID_IWinInetHttpInfo %p)\n", This, ppv);
796 
797         if(!This->protocol->wininet_http_info)
798             return E_NOINTERFACE;
799 
800         *ppv = &This->IWinInetHttpInfo_iface;
801     }
802 
803     if(*ppv) {
804         IBinding_AddRef(&This->IBinding_iface);
805         return S_OK;
806     }
807 
808     WARN("Unsupported interface %s\n", debugstr_guid(riid));
809     return E_NOINTERFACE;
810 }
811 
812 static ULONG WINAPI Binding_AddRef(IBinding *iface)
813 {
814     Binding *This = impl_from_IBinding(iface);
815     LONG ref = InterlockedIncrement(&This->ref);
816 
817     TRACE("(%p) ref=%d\n", This, ref);
818 
819     return ref;
820 }
821 
822 static ULONG WINAPI Binding_Release(IBinding *iface)
823 {
824     Binding *This = impl_from_IBinding(iface);
825     LONG ref = InterlockedDecrement(&This->ref);
826 
827     TRACE("(%p) ref=%d\n", This, ref);
828 
829     if(!ref) {
830         if(This->notif_hwnd)
831             release_notif_hwnd(This->notif_hwnd);
832         if(This->mon)
833             IMoniker_Release(This->mon);
834         if(This->callback)
835             IBindStatusCallback_Release(This->callback);
836         if(This->protocol)
837             IInternetProtocolEx_Release(&This->protocol->IInternetProtocolEx_iface);
838         if(This->service_provider)
839             IServiceProvider_Release(This->service_provider);
840         if(This->stgmed_buf)
841             IUnknown_Release(&This->stgmed_buf->IUnknown_iface);
842         if(This->stgmed_obj)
843             This->stgmed_obj->vtbl->release(This->stgmed_obj);
844         if(This->obj)
845             IUnknown_Release(This->obj);
846         if(This->bctx)
847             IBindCtx_Release(This->bctx);
848 
849         ReleaseBindInfo(&This->bindinfo);
850         This->section.DebugInfo->Spare[0] = 0;
851         DeleteCriticalSection(&This->section);
852         SysFreeString(This->url);
853         heap_free(This->mime);
854         heap_free(This->redirect_url);
855         heap_free(This);
856 
857         URLMON_UnlockModule();
858     }
859 
860     return ref;
861 }
862 
863 static HRESULT WINAPI Binding_Abort(IBinding *iface)
864 {
865     Binding *This = impl_from_IBinding(iface);
866     HRESULT hres;
867 
868     TRACE("(%p)\n", This);
869 
870     if(This->state & BINDING_ABORTED)
871         return E_FAIL;
872 
873     hres = IInternetProtocolEx_Abort(&This->protocol->IInternetProtocolEx_iface, E_ABORT,
874             ERROR_SUCCESS);
875     if(FAILED(hres))
876         return hres;
877 
878     This->state |= BINDING_ABORTED;
879     return S_OK;
880 }
881 
882 static HRESULT WINAPI Binding_Suspend(IBinding *iface)
883 {
884     Binding *This = impl_from_IBinding(iface);
885     FIXME("(%p)\n", This);
886     return E_NOTIMPL;
887 }
888 
889 static HRESULT WINAPI Binding_Resume(IBinding *iface)
890 {
891     Binding *This = impl_from_IBinding(iface);
892     FIXME("(%p)\n", This);
893     return E_NOTIMPL;
894 }
895 
896 static HRESULT WINAPI Binding_SetPriority(IBinding *iface, LONG nPriority)
897 {
898     Binding *This = impl_from_IBinding(iface);
899     FIXME("(%p)->(%d)\n", This, nPriority);
900     return E_NOTIMPL;
901 }
902 
903 static HRESULT WINAPI Binding_GetPriority(IBinding *iface, LONG *pnPriority)
904 {
905     Binding *This = impl_from_IBinding(iface);
906     FIXME("(%p)->(%p)\n", This, pnPriority);
907     return E_NOTIMPL;
908 }
909 
910 static HRESULT WINAPI Binding_GetBindResult(IBinding *iface, CLSID *pclsidProtocol,
911         DWORD *pdwResult, LPOLESTR *pszResult, DWORD *pdwReserved)
912 {
913     Binding *This = impl_from_IBinding(iface);
914 
915     TRACE("(%p)->(%p %p %p %p)\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved);
916 
917     if(!pdwResult || !pszResult || pdwReserved)
918         return E_INVALIDARG;
919 
920     if(!(This->state & BINDING_STOPPED)) {
921         *pclsidProtocol = CLSID_NULL;
922         *pdwResult = 0;
923         *pszResult = NULL;
924         return S_OK;
925     }
926 
927     *pclsidProtocol = This->hres==S_OK ? CLSID_NULL : This->clsid;
928     *pdwResult = This->hres;
929     *pszResult = NULL;
930     return S_OK;
931 }
932 
933 static const IBindingVtbl BindingVtbl = {
934     Binding_QueryInterface,
935     Binding_AddRef,
936     Binding_Release,
937     Binding_Abort,
938     Binding_Suspend,
939     Binding_Resume,
940     Binding_SetPriority,
941     Binding_GetPriority,
942     Binding_GetBindResult
943 };
944 
945 static Binding *get_bctx_binding(IBindCtx *bctx)
946 {
947     IBinding *binding;
948     IUnknown *unk;
949     HRESULT hres;
950 
951     hres = IBindCtx_GetObjectParam(bctx, cbinding_contextW, &unk);
952     if(FAILED(hres))
953         return NULL;
954 
955     hres = IUnknown_QueryInterface(unk, &IID_IBinding, (void**)&binding);
956     IUnknown_Release(unk);
957     if(FAILED(hres))
958         return NULL;
959 
960     if (binding->lpVtbl != &BindingVtbl)
961         return NULL;
962     return impl_from_IBinding(binding);
963 }
964 
965 static inline Binding *impl_from_IInternetProtocolSink(IInternetProtocolSink *iface)
966 {
967     return CONTAINING_RECORD(iface, Binding, IInternetProtocolSink_iface);
968 }
969 
970 static HRESULT WINAPI InternetProtocolSink_QueryInterface(IInternetProtocolSink *iface,
971         REFIID riid, void **ppv)
972 {
973     Binding *This = impl_from_IInternetProtocolSink(iface);
974     return IBinding_QueryInterface(&This->IBinding_iface, riid, ppv);
975 }
976 
977 static ULONG WINAPI InternetProtocolSink_AddRef(IInternetProtocolSink *iface)
978 {
979     Binding *This = impl_from_IInternetProtocolSink(iface);
980     return IBinding_AddRef(&This->IBinding_iface);
981 }
982 
983 static ULONG WINAPI InternetProtocolSink_Release(IInternetProtocolSink *iface)
984 {
985     Binding *This = impl_from_IInternetProtocolSink(iface);
986     return IBinding_Release(&This->IBinding_iface);
987 }
988 
989 static HRESULT WINAPI InternetProtocolSink_Switch(IInternetProtocolSink *iface,
990         PROTOCOLDATA *pProtocolData)
991 {
992     Binding *This = impl_from_IInternetProtocolSink(iface);
993 
994     WARN("(%p)->(%p)\n", This, pProtocolData);
995 
996     return E_FAIL;
997 }
998 
999 static void on_progress(Binding *This, ULONG progress, ULONG progress_max,
1000                         ULONG status_code, LPCWSTR status_text)
1001 {
1002     IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
1003             status_code, status_text);
1004 }
1005 
1006 static HRESULT WINAPI InternetProtocolSink_ReportProgress(IInternetProtocolSink *iface,
1007         ULONG ulStatusCode, LPCWSTR szStatusText)
1008 {
1009     Binding *This = impl_from_IInternetProtocolSink(iface);
1010 
1011     TRACE("(%p)->(%s %s)\n", This, debugstr_bindstatus(ulStatusCode), debugstr_w(szStatusText));
1012 
1013     switch(ulStatusCode) {
1014     case BINDSTATUS_FINDINGRESOURCE:
1015         on_progress(This, 0, 0, BINDSTATUS_FINDINGRESOURCE, szStatusText);
1016         break;
1017     case BINDSTATUS_CONNECTING:
1018         on_progress(This, 0, 0, BINDSTATUS_CONNECTING, szStatusText);
1019         break;
1020     case BINDSTATUS_REDIRECTING:
1021         heap_free(This->redirect_url);
1022         This->redirect_url = heap_strdupW(szStatusText);
1023         on_progress(This, 0, 0, BINDSTATUS_REDIRECTING, szStatusText);
1024         break;
1025     case BINDSTATUS_BEGINDOWNLOADDATA:
1026         break;
1027     case BINDSTATUS_SENDINGREQUEST:
1028         on_progress(This, 0, 0, BINDSTATUS_SENDINGREQUEST, szStatusText);
1029         break;
1030     case BINDSTATUS_PROTOCOLCLASSID:
1031         CLSIDFromString(szStatusText, &This->clsid);
1032         break;
1033     case BINDSTATUS_MIMETYPEAVAILABLE:
1034     case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE:
1035         mime_available(This, szStatusText);
1036         break;
1037     case BINDSTATUS_CACHEFILENAMEAVAILABLE:
1038         cache_file_available(This, szStatusText);
1039         break;
1040     case BINDSTATUS_DECODING:
1041         IBindStatusCallback_OnProgress(This->callback, 0, 0, BINDSTATUS_DECODING, szStatusText);
1042         break;
1043     case BINDSTATUS_LOADINGMIMEHANDLER:
1044         on_progress(This, 0, 0, BINDSTATUS_LOADINGMIMEHANDLER, szStatusText);
1045         break;
1046     case BINDSTATUS_DIRECTBIND: /* FIXME: Handle BINDSTATUS_DIRECTBIND in BindProtocol */
1047         This->report_mime = FALSE;
1048         break;
1049     case BINDSTATUS_ACCEPTRANGES:
1050         break;
1051     default:
1052         FIXME("Unhandled status code %d\n", ulStatusCode);
1053         return E_NOTIMPL;
1054     };
1055 
1056     return S_OK;
1057 }
1058 
1059 static void report_data(Binding *This, DWORD bscf, ULONG progress, ULONG progress_max)
1060 {
1061     FORMATETC formatetc = {0, NULL, 1, -1, TYMED_ISTREAM};
1062     BOOL sent_begindownloaddata = FALSE;
1063 
1064     TRACE("(%p)->(%d %u %u)\n", This, bscf, progress, progress_max);
1065 
1066     if(This->download_state == END_DOWNLOAD || (This->state & BINDING_ABORTED)) {
1067         read_protocol_data(This->stgmed_buf);
1068         return;
1069     }
1070 
1071     if(This->state & BINDING_STOPPED)
1072         return;
1073 
1074     if(This->stgmed_buf->file != INVALID_HANDLE_VALUE)
1075         read_protocol_data(This->stgmed_buf);
1076 
1077     if(This->download_state == BEFORE_DOWNLOAD) {
1078         This->download_state = DOWNLOADING;
1079         sent_begindownloaddata = TRUE;
1080         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
1081                 BINDSTATUS_BEGINDOWNLOADDATA, This->url);
1082 
1083         if(This->stgmed_buf->cache_file)
1084             IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
1085                     BINDSTATUS_CACHEFILENAMEAVAILABLE, This->stgmed_buf->cache_file);
1086     }
1087 
1088     if(This->stgmed_buf->hres == S_FALSE || (bscf & BSCF_LASTDATANOTIFICATION)) {
1089         This->download_state = END_DOWNLOAD;
1090         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
1091                 BINDSTATUS_ENDDOWNLOADDATA, This->url);
1092     }else if(!sent_begindownloaddata) {
1093         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
1094                 BINDSTATUS_DOWNLOADINGDATA, This->url);
1095     }
1096 
1097     if(This->state & (BINDING_STOPPED|BINDING_ABORTED))
1098         return;
1099 
1100     if(This->to_object) {
1101         if(!(This->state & BINDING_OBJAVAIL)) {
1102             IBinding_AddRef(&This->IBinding_iface);
1103             create_object(This);
1104             IBinding_Release(&This->IBinding_iface);
1105         }
1106     }else {
1107         STGMEDIUM stgmed;
1108         HRESULT hres;
1109 
1110         if(!(This->state & BINDING_LOCKED)) {
1111             HRESULT hres = IInternetProtocolEx_LockRequest(
1112                     &This->protocol->IInternetProtocolEx_iface, 0);
1113             if(SUCCEEDED(hres))
1114                 This->state |= BINDING_LOCKED;
1115         }
1116 
1117         hres = This->stgmed_obj->vtbl->fill_stgmed(This->stgmed_obj, &stgmed);
1118         if(FAILED(hres)) {
1119             stop_binding(This, hres, NULL);
1120             return;
1121         }
1122 
1123         formatetc.tymed = stgmed.tymed;
1124         formatetc.cfFormat = This->clipboard_format;
1125 
1126         hres = IBindStatusCallback_OnDataAvailable(This->callback, bscf, progress,
1127                 &formatetc, &stgmed);
1128         if(hres != S_OK) {
1129             if(This->download_state != END_DOWNLOAD) {
1130                 This->download_state = END_DOWNLOAD;
1131                 IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
1132                         BINDSTATUS_ENDDOWNLOADDATA, This->url);
1133             }
1134 
1135             WARN("OnDataAvailable returned %x\n", hres);
1136             stop_binding(This, hres, NULL);
1137             return;
1138         }
1139 
1140         if(This->download_state == END_DOWNLOAD)
1141             stop_binding(This, S_OK, NULL);
1142     }
1143 }
1144 
1145 static HRESULT WINAPI InternetProtocolSink_ReportData(IInternetProtocolSink *iface,
1146         DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
1147 {
1148     Binding *This = impl_from_IInternetProtocolSink(iface);
1149 
1150     TRACE("(%p)->(%d %u %u)\n", This, grfBSCF, ulProgress, ulProgressMax);
1151 
1152     report_data(This, grfBSCF, ulProgress, ulProgressMax);
1153     return S_OK;
1154 }
1155 
1156 static HRESULT WINAPI InternetProtocolSink_ReportResult(IInternetProtocolSink *iface,
1157         HRESULT hrResult, DWORD dwError, LPCWSTR szResult)
1158 {
1159     Binding *This = impl_from_IInternetProtocolSink(iface);
1160 
1161     TRACE("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult));
1162 
1163     stop_binding(This, hrResult, szResult);
1164 
1165     IInternetProtocolEx_Terminate(&This->protocol->IInternetProtocolEx_iface, 0);
1166     return S_OK;
1167 }
1168 
1169 static const IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = {
1170     InternetProtocolSink_QueryInterface,
1171     InternetProtocolSink_AddRef,
1172     InternetProtocolSink_Release,
1173     InternetProtocolSink_Switch,
1174     InternetProtocolSink_ReportProgress,
1175     InternetProtocolSink_ReportData,
1176     InternetProtocolSink_ReportResult
1177 };
1178 
1179 static inline Binding *impl_from_IInternetBindInfo(IInternetBindInfo *iface)
1180 {
1181     return CONTAINING_RECORD(iface, Binding, IInternetBindInfo_iface);
1182 }
1183 
1184 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
1185         REFIID riid, void **ppv)
1186 {
1187     Binding *This = impl_from_IInternetBindInfo(iface);
1188     return IBinding_QueryInterface(&This->IBinding_iface, riid, ppv);
1189 }
1190 
1191 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
1192 {
1193     Binding *This = impl_from_IInternetBindInfo(iface);
1194     return IBinding_AddRef(&This->IBinding_iface);
1195 }
1196 
1197 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
1198 {
1199     Binding *This = impl_from_IInternetBindInfo(iface);
1200     return IBinding_Release(&This->IBinding_iface);
1201 }
1202 
1203 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
1204         DWORD *grfBINDF, BINDINFO *pbindinfo)
1205 {
1206     Binding *This = impl_from_IInternetBindInfo(iface);
1207 
1208     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
1209 
1210     *grfBINDF = This->bindf;
1211     return CopyBindInfo(&This->bindinfo, pbindinfo);
1212 }
1213 
1214 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
1215         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
1216 {
1217     Binding *This = impl_from_IInternetBindInfo(iface);
1218 
1219     TRACE("(%p)->(%d %p %d %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
1220 
1221     switch(ulStringType) {
1222     case BINDSTRING_ACCEPT_MIMES: {
1223         static const WCHAR wszMimes[] = {'*','/','*',0};
1224 
1225         if(!ppwzStr || !pcElFetched)
1226             return E_INVALIDARG;
1227 
1228         ppwzStr[0] = CoTaskMemAlloc(sizeof(wszMimes));
1229         memcpy(ppwzStr[0], wszMimes, sizeof(wszMimes));
1230         *pcElFetched = 1;
1231         return S_OK;
1232     }
1233     case BINDSTRING_USER_AGENT: {
1234         IInternetBindInfo *bindinfo = NULL;
1235         HRESULT hres;
1236 
1237         hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetBindInfo,
1238                                                   (void**)&bindinfo);
1239         if(FAILED(hres))
1240             return hres;
1241 
1242         hres = IInternetBindInfo_GetBindString(bindinfo, ulStringType, ppwzStr,
1243                                                cEl, pcElFetched);
1244         IInternetBindInfo_Release(bindinfo);
1245 
1246         return hres;
1247     }
1248     case BINDSTRING_URL: {
1249         DWORD size = (SysStringLen(This->url)+1) * sizeof(WCHAR);
1250 
1251         if(!ppwzStr || !pcElFetched)
1252             return E_INVALIDARG;
1253 
1254         *ppwzStr = CoTaskMemAlloc(size);
1255         memcpy(*ppwzStr, This->url, size);
1256         *pcElFetched = 1;
1257         return S_OK;
1258     }
1259     }
1260 
1261     FIXME("not supported string type %d\n", ulStringType);
1262     return E_NOTIMPL;
1263 }
1264 
1265 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
1266     InternetBindInfo_QueryInterface,
1267     InternetBindInfo_AddRef,
1268     InternetBindInfo_Release,
1269     InternetBindInfo_GetBindInfo,
1270     InternetBindInfo_GetBindString
1271 };
1272 
1273 static inline Binding *impl_from_IWinInetHttpInfo(IWinInetHttpInfo *iface)
1274 {
1275     return CONTAINING_RECORD(iface, Binding, IWinInetHttpInfo_iface);
1276 }
1277 
1278 static HRESULT WINAPI WinInetHttpInfo_QueryInterface(IWinInetHttpInfo *iface, REFIID riid, void **ppv)
1279 {
1280     Binding *This = impl_from_IWinInetHttpInfo(iface);
1281     return IBinding_QueryInterface(&This->IBinding_iface, riid, ppv);
1282 }
1283 
1284 static ULONG WINAPI WinInetHttpInfo_AddRef(IWinInetHttpInfo *iface)
1285 {
1286     Binding *This = impl_from_IWinInetHttpInfo(iface);
1287     return IBinding_AddRef(&This->IBinding_iface);
1288 }
1289 
1290 static ULONG WINAPI WinInetHttpInfo_Release(IWinInetHttpInfo *iface)
1291 {
1292     Binding *This = impl_from_IWinInetHttpInfo(iface);
1293     return IBinding_Release(&This->IBinding_iface);
1294 }
1295 
1296 static HRESULT WINAPI WinInetHttpInfo_QueryOption(IWinInetHttpInfo *iface, DWORD dwOption,
1297         void *pBuffer, DWORD *pcbBuffer)
1298 {
1299     Binding *This = impl_from_IWinInetHttpInfo(iface);
1300     TRACE("(%p)->(%x %p %p)\n", This, dwOption, pBuffer, pcbBuffer);
1301 
1302     if(!This->protocol->wininet_info)
1303         return E_FAIL;
1304 
1305     return IWinInetInfo_QueryOption(This->protocol->wininet_info,
1306             dwOption, pBuffer, pcbBuffer);
1307 }
1308 
1309 static HRESULT WINAPI WinInetHttpInfo_QueryInfo(IWinInetHttpInfo *iface, DWORD dwOption,
1310         void *pBuffer, DWORD *pcbBuffer, DWORD *pdwFlags, DWORD *pdwReserved)
1311 {
1312     Binding *This = impl_from_IWinInetHttpInfo(iface);
1313     TRACE("(%p)->(%x %p %p %p %p)\n", This, dwOption, pBuffer, pcbBuffer, pdwFlags, pdwReserved);
1314 
1315     if(!This->protocol->wininet_http_info)
1316         return E_FAIL;
1317 
1318     return IWinInetHttpInfo_QueryInfo(This->protocol->wininet_http_info,
1319             dwOption, pBuffer, pcbBuffer, pdwFlags, pdwReserved);
1320 }
1321 
1322 static const IWinInetHttpInfoVtbl WinInetHttpInfoVtbl = {
1323     WinInetHttpInfo_QueryInterface,
1324     WinInetHttpInfo_AddRef,
1325     WinInetHttpInfo_Release,
1326     WinInetHttpInfo_QueryOption,
1327     WinInetHttpInfo_QueryInfo
1328 };
1329 
1330 static inline Binding *impl_from_IServiceProvider(IServiceProvider *iface)
1331 {
1332     return CONTAINING_RECORD(iface, Binding, IServiceProvider_iface);
1333 }
1334 
1335 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface,
1336         REFIID riid, void **ppv)
1337 {
1338     Binding *This = impl_from_IServiceProvider(iface);
1339     return IBinding_QueryInterface(&This->IBinding_iface, riid, ppv);
1340 }
1341 
1342 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
1343 {
1344     Binding *This = impl_from_IServiceProvider(iface);
1345     return IBinding_AddRef(&This->IBinding_iface);
1346 }
1347 
1348 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
1349 {
1350     Binding *This = impl_from_IServiceProvider(iface);
1351     return IBinding_Release(&This->IBinding_iface);
1352 }
1353 
1354 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
1355         REFGUID guidService, REFIID riid, void **ppv)
1356 {
1357     Binding *This = impl_from_IServiceProvider(iface);
1358     HRESULT hres;
1359 
1360     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
1361 
1362     if(This->service_provider) {
1363         hres = IServiceProvider_QueryService(This->service_provider, guidService,
1364                                              riid, ppv);
1365         if(SUCCEEDED(hres))
1366             return hres;
1367     }
1368 
1369     WARN("unknown service %s\n", debugstr_guid(guidService));
1370     return E_NOINTERFACE;
1371 }
1372 
1373 static const IServiceProviderVtbl ServiceProviderVtbl = {
1374     ServiceProvider_QueryInterface,
1375     ServiceProvider_AddRef,
1376     ServiceProvider_Release,
1377     ServiceProvider_QueryService
1378 };
1379 
1380 static HRESULT get_callback(IBindCtx *pbc, IBindStatusCallback **callback)
1381 {
1382     IUnknown *unk;
1383     HRESULT hres;
1384 
1385     hres = IBindCtx_GetObjectParam(pbc, bscb_holderW, &unk);
1386     if(FAILED(hres))
1387         return create_default_callback(callback);
1388 
1389     hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)callback);
1390     IUnknown_Release(unk);
1391     return hres;
1392 }
1393 
1394 static BOOL is_urlmon_protocol(IUri *uri)
1395 {
1396     DWORD scheme;
1397     HRESULT hres;
1398 
1399     hres = IUri_GetScheme(uri, &scheme);
1400     if(FAILED(hres))
1401         return FALSE;
1402 
1403     switch(scheme) {
1404     case URL_SCHEME_FILE:
1405     case URL_SCHEME_FTP:
1406     case URL_SCHEME_GOPHER:
1407     case URL_SCHEME_HTTP:
1408     case URL_SCHEME_HTTPS:
1409     case URL_SCHEME_MK:
1410         return TRUE;
1411     }
1412 
1413     return FALSE;
1414 }
1415 
1416 static HRESULT Binding_Create(IMoniker *mon, Binding *binding_ctx, IUri *uri, IBindCtx *pbc,
1417         BOOL to_obj, REFIID riid, Binding **binding)
1418 {
1419     Binding *ret;
1420     HRESULT hres;
1421 
1422     URLMON_LockModule();
1423 
1424     ret = heap_alloc_zero(sizeof(Binding));
1425 
1426     ret->IBinding_iface.lpVtbl = &BindingVtbl;
1427     ret->IInternetProtocolSink_iface.lpVtbl = &InternetProtocolSinkVtbl;
1428     ret->IInternetBindInfo_iface.lpVtbl = &InternetBindInfoVtbl;
1429     ret->IWinInetHttpInfo_iface.lpVtbl = &WinInetHttpInfoVtbl;
1430     ret->IServiceProvider_iface.lpVtbl = &ServiceProviderVtbl;
1431 
1432     ret->ref = 1;
1433 
1434     ret->to_object = to_obj;
1435     ret->iid = *riid;
1436     ret->notif_hwnd = get_notif_hwnd();
1437     ret->report_mime = !binding_ctx;
1438     ret->download_state = BEFORE_DOWNLOAD;
1439 
1440     if(to_obj) {
1441         IBindCtx_AddRef(pbc);
1442         ret->bctx = pbc;
1443     }
1444 
1445     if(mon) {
1446         IMoniker_AddRef(mon);
1447         ret->mon = mon;
1448     }
1449 
1450     ret->bindinfo.cbSize = sizeof(BINDINFO);
1451 
1452     InitializeCriticalSection(&ret->section);
1453     ret->section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": Binding.section");
1454 
1455     hres = get_callback(pbc, &ret->callback);
1456     if(FAILED(hres)) {
1457         WARN("Could not get IBindStatusCallback\n");
1458         IBinding_Release(&ret->IBinding_iface);
1459         return hres;
1460     }
1461 
1462     IBindStatusCallback_QueryInterface(ret->callback, &IID_IServiceProvider,
1463                                        (void**)&ret->service_provider);
1464 
1465     if(binding_ctx) {
1466         ret->protocol = binding_ctx->protocol;
1467         IInternetProtocolEx_AddRef(&ret->protocol->IInternetProtocolEx_iface);
1468     }else {
1469         hres = create_binding_protocol(TRUE, &ret->protocol);
1470         if(FAILED(hres)) {
1471             WARN("Could not get protocol handler\n");
1472             IBinding_Release(&ret->IBinding_iface);
1473             return hres;
1474         }
1475     }
1476 
1477     hres = IBindStatusCallback_GetBindInfo(ret->callback, &ret->bindf, &ret->bindinfo);
1478     if(FAILED(hres)) {
1479         WARN("GetBindInfo failed: %08x\n", hres);
1480         IBinding_Release(&ret->IBinding_iface);
1481         return hres;
1482     }
1483 
1484     TRACE("bindf %08x\n", ret->bindf);
1485     dump_BINDINFO(&ret->bindinfo);
1486 
1487     ret->bindf |= BINDF_FROMURLMON;
1488     if(to_obj)
1489         ret->bindinfo.dwOptions |= 0x100000;
1490 
1491     if(!(ret->bindf & BINDF_ASYNCHRONOUS) || !(ret->bindf & BINDF_PULLDATA)) {
1492         ret->bindf |= BINDF_NEEDFILE;
1493         ret->use_cache_file = TRUE;
1494     }else if(!is_urlmon_protocol(uri)) {
1495         ret->bindf |= BINDF_NEEDFILE;
1496     }
1497 
1498     hres = IUri_GetDisplayUri(uri, &ret->url);
1499     if(FAILED(hres)) {
1500         IBinding_Release(&ret->IBinding_iface);
1501         return hres;
1502     }
1503 
1504     if(binding_ctx) {
1505         ret->stgmed_buf = binding_ctx->stgmed_buf;
1506         IUnknown_AddRef(&ret->stgmed_buf->IUnknown_iface);
1507         ret->clipboard_format = binding_ctx->clipboard_format;
1508     }else {
1509         ret->stgmed_buf = create_stgmed_buf(&ret->protocol->IInternetProtocolEx_iface);
1510     }
1511 
1512     if(to_obj) {
1513         ret->stgmed_obj = NULL;
1514     }else if(IsEqualGUID(&IID_IStream, riid)) {
1515         ret->stgmed_obj = create_stgmed_stream(ret->stgmed_buf);
1516     }else if(IsEqualGUID(&IID_IUnknown, riid)) {
1517         ret->bindf |= BINDF_NEEDFILE;
1518         ret->stgmed_obj = create_stgmed_file(ret->stgmed_buf);
1519     }else {
1520         FIXME("Unsupported riid %s\n", debugstr_guid(riid));
1521         IBinding_Release(&ret->IBinding_iface);
1522         return E_NOTIMPL;
1523     }
1524 
1525     *binding = ret;
1526     return S_OK;
1527 }
1528 
1529 static HRESULT start_binding(IMoniker *mon, Binding *binding_ctx, IUri *uri, IBindCtx *pbc,
1530                              BOOL to_obj, REFIID riid, Binding **ret)
1531 {
1532     Binding *binding = NULL;
1533     HRESULT hres;
1534     MSG msg;
1535 
1536     hres = Binding_Create(mon, binding_ctx, uri, pbc, to_obj, riid, &binding);
1537     if(FAILED(hres))
1538         return hres;
1539 
1540     hres = IBindStatusCallback_OnStartBinding(binding->callback, 0, &binding->IBinding_iface);
1541     if(FAILED(hres)) {
1542         WARN("OnStartBinding failed: %08x\n", hres);
1543         if(hres != E_ABORT && hres != E_NOTIMPL)
1544             hres = INET_E_DOWNLOAD_FAILURE;
1545 
1546         stop_binding(binding, hres, NULL);
1547         IBinding_Release(&binding->IBinding_iface);
1548         return hres;
1549     }
1550 
1551     if(binding_ctx) {
1552         set_binding_sink(binding->protocol, &binding->IInternetProtocolSink_iface,
1553                 &binding->IInternetBindInfo_iface);
1554         if(binding_ctx->redirect_url)
1555             IBindStatusCallback_OnProgress(binding->callback, 0, 0, BINDSTATUS_REDIRECTING, binding_ctx->redirect_url);
1556         report_data(binding, BSCF_FIRSTDATANOTIFICATION | (binding_ctx->download_state == END_DOWNLOAD ? BSCF_LASTDATANOTIFICATION : 0),
1557                 0, 0);
1558     }else {
1559         hres = IInternetProtocolEx_StartEx(&binding->protocol->IInternetProtocolEx_iface, uri,
1560                 &binding->IInternetProtocolSink_iface, &binding->IInternetBindInfo_iface,
1561                 PI_APARTMENTTHREADED|PI_MIMEVERIFICATION, 0);
1562 
1563         TRACE("start ret %08x\n", hres);
1564 
1565         if(FAILED(hres) && hres != E_PENDING) {
1566             stop_binding(binding, hres, NULL);
1567             IBinding_Release(&binding->IBinding_iface);
1568 
1569             return hres;
1570         }
1571     }
1572 
1573     while(!(binding->bindf & BINDF_ASYNCHRONOUS) &&
1574           !(binding->state & BINDING_STOPPED)) {
1575         MsgWaitForMultipleObjects(0, NULL, FALSE, 5000, QS_POSTMESSAGE);
1576         while (PeekMessageW(&msg, binding->notif_hwnd, WM_USER, WM_USER+117, PM_REMOVE|PM_NOYIELD)) {
1577             TranslateMessage(&msg);
1578             DispatchMessageW(&msg);
1579         }
1580     }
1581 
1582     *ret = binding;
1583     return S_OK;
1584 }
1585 
1586 HRESULT bind_to_storage(IUri *uri, IBindCtx *pbc, REFIID riid, void **ppv)
1587 {
1588     Binding *binding = NULL, *binding_ctx;
1589     HRESULT hres;
1590 
1591     binding_ctx = get_bctx_binding(pbc);
1592 
1593     hres = start_binding(NULL, binding_ctx, uri, pbc, FALSE, riid, &binding);
1594     if(binding_ctx)
1595         IBinding_Release(&binding_ctx->IBinding_iface);
1596     if(FAILED(hres))
1597         return hres;
1598 
1599     if(binding->hres == S_OK && binding->download_state != BEFORE_DOWNLOAD /* FIXME */) {
1600         if((binding->state & BINDING_STOPPED) && (binding->state & BINDING_LOCKED))
1601             IInternetProtocolEx_UnlockRequest(&binding->protocol->IInternetProtocolEx_iface);
1602 
1603         hres = binding->stgmed_obj->vtbl->get_result(binding->stgmed_obj, binding->bindf, ppv);
1604     }else if(binding->bindf & BINDF_ASYNCHRONOUS) {
1605         hres = MK_S_ASYNCHRONOUS;
1606     }else {
1607         hres = FAILED(binding->hres) ? binding->hres : S_OK;
1608     }
1609 
1610     IBinding_Release(&binding->IBinding_iface);
1611 
1612     return hres;
1613 }
1614 
1615 HRESULT bind_to_object(IMoniker *mon, IUri *uri, IBindCtx *pbc, REFIID riid, void **ppv)
1616 {
1617     Binding *binding;
1618     HRESULT hres;
1619 
1620     *ppv = NULL;
1621 
1622     hres = start_binding(mon, NULL, uri, pbc, TRUE, riid, &binding);
1623     if(FAILED(hres))
1624         return hres;
1625 
1626     if(binding->hres != S_OK) {
1627         hres = SUCCEEDED(binding->hres) ? S_OK : binding->hres;
1628     }else if(binding->bindf & BINDF_ASYNCHRONOUS) {
1629         hres = MK_S_ASYNCHRONOUS;
1630     }else {
1631         *ppv = binding->obj;
1632         IUnknown_AddRef(binding->obj);
1633         hres = S_OK;
1634     }
1635 
1636     IBinding_Release(&binding->IBinding_iface);
1637 
1638     return hres;
1639 }
1640