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