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