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