wined3d: Add zero/near zero vertex rhw special case.
[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 "wine/debug.h"
21
22 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
23
24 typedef struct Binding Binding;
25
26 struct _task_header_t;
27
28 typedef void (*task_proc_t)(Binding*, struct _task_header_t*);
29
30 typedef struct _task_header_t {
31     task_proc_t proc;
32     struct _task_header_t *next;
33 } task_header_t;
34
35 typedef struct {
36     const IStreamVtbl *lpStreamVtbl;
37
38     LONG ref;
39
40     IInternetProtocol *protocol;
41
42     BYTE buf[1024*8];
43     DWORD buf_size;
44     BOOL init_buf;
45     HRESULT hres;
46 } ProtocolStream;
47
48 typedef enum {
49     BEFORE_DOWNLOAD,
50     DOWNLOADING,
51     END_DOWNLOAD
52 } download_state_t;
53
54 #define BINDING_LOCKED  0x0001
55
56 struct Binding {
57     const IBindingVtbl               *lpBindingVtbl;
58     const IInternetProtocolSinkVtbl  *lpInternetProtocolSinkVtbl;
59     const IInternetBindInfoVtbl      *lpInternetBindInfoVtbl;
60     const IServiceProviderVtbl       *lpServiceProviderVtbl;
61
62     LONG ref;
63
64     IBindStatusCallback *callback;
65     IInternetProtocol *protocol;
66     IServiceProvider *service_provider;
67     ProtocolStream *stream;
68
69     BINDINFO bindinfo;
70     DWORD bindf;
71     LPWSTR mime;
72     UINT clipboard_format;
73     LPWSTR url;
74     BOOL report_mime;
75     DWORD continue_call;
76     DWORD state;
77     download_state_t download_state;
78
79     DWORD apartment_thread;
80     HWND notif_hwnd;
81
82     STGMEDIUM stgmed;
83
84     task_header_t *task_queue_head, *task_queue_tail;
85     CRITICAL_SECTION section;
86 };
87
88 #define BINDING(x)   ((IBinding*)               &(x)->lpBindingVtbl)
89 #define PROTSINK(x)  ((IInternetProtocolSink*)  &(x)->lpInternetProtocolSinkVtbl)
90 #define BINDINF(x)   ((IInternetBindInfo*)      &(x)->lpInternetBindInfoVtbl)
91 #define SERVPROV(x)  ((IServiceProvider*)       &(x)->lpServiceProviderVtbl)
92
93 #define STREAM(x) ((IStream*) &(x)->lpStreamVtbl)
94 #define HTTPNEG2(x) ((IHttpNegotiate2*) &(x)->lpHttpNegotiate2Vtbl)
95
96 #define WM_MK_CONTINUE   (WM_USER+101)
97
98 static void push_task(Binding *binding, task_header_t *task, task_proc_t proc)
99 {
100     task->proc = proc;
101     task->next = NULL;
102
103     EnterCriticalSection(&binding->section);
104
105     if(binding->task_queue_tail) {
106         binding->task_queue_tail->next = task;
107         binding->task_queue_tail = task;
108     }else {
109         binding->task_queue_tail = binding->task_queue_head = task;
110     }
111
112     LeaveCriticalSection(&binding->section);
113 }
114
115 static task_header_t *pop_task(Binding *binding)
116 {
117     task_header_t *ret;
118
119     EnterCriticalSection(&binding->section);
120
121     ret = binding->task_queue_head;
122     if(ret) {
123         binding->task_queue_head = ret->next;
124         if(!binding->task_queue_head)
125             binding->task_queue_tail = NULL;
126     }
127
128     LeaveCriticalSection(&binding->section);
129
130     return ret;
131 }
132
133 static void fill_stream_buffer(ProtocolStream *This)
134 {
135     DWORD read = 0;
136
137     if(sizeof(This->buf) == This->buf_size)
138         return;
139
140     This->hres = IInternetProtocol_Read(This->protocol, This->buf+This->buf_size,
141             sizeof(This->buf)-This->buf_size, &read);
142     This->buf_size += read;
143     if(read > 0)
144         This->init_buf = TRUE;
145 }
146
147 static LRESULT WINAPI notif_wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
148 {
149     if(msg == WM_MK_CONTINUE) {
150         Binding *binding = (Binding*)lParam;
151         task_header_t *task;
152
153         while((task = pop_task(binding))) {
154             binding->continue_call++;
155             task->proc(binding, task);
156             binding->continue_call--;
157         }
158
159         IBinding_Release(BINDING(binding));
160         return 0;
161     }
162
163     return DefWindowProcW(hwnd, msg, wParam, lParam);
164 }
165
166 static HWND get_notif_hwnd(void)
167 {
168     static ATOM wnd_class = 0;
169     HWND hwnd;
170
171     static const WCHAR wszURLMonikerNotificationWindow[] =
172         {'U','R','L',' ','M','o','n','i','k','e','r',' ',
173          'N','o','t','i','f','i','c','a','t','i','o','n',' ','W','i','n','d','o','w',0};
174
175     if(!wnd_class) {
176         static WNDCLASSEXW wndclass = {
177             sizeof(wndclass), 0,
178             notif_wnd_proc, 0, 0,
179             NULL, NULL, NULL, NULL, NULL,
180             wszURLMonikerNotificationWindow,
181             NULL        
182         };
183
184         wndclass.hInstance = URLMON_hInstance;
185
186         wnd_class = RegisterClassExW(&wndclass);
187         if (!wnd_class && GetLastError() == ERROR_CLASS_ALREADY_EXISTS)
188             wnd_class = 1;
189     }
190
191     hwnd = CreateWindowExW(0, wszURLMonikerNotificationWindow,
192                            wszURLMonikerNotificationWindow, 0, 0, 0, 0, 0, HWND_MESSAGE,
193                            NULL, URLMON_hInstance, NULL);
194
195     TRACE("hwnd = %p\n", hwnd);
196
197     return hwnd;
198 }
199
200 static void dump_BINDINFO(BINDINFO *bi)
201 {
202     static const char * const BINDINFOF_str[] = {
203         "#0",
204         "BINDINFOF_URLENCODESTGMEDDATA",
205         "BINDINFOF_URLENCODEDEXTRAINFO"
206     };
207
208     static const char * const BINDVERB_str[] = {
209         "BINDVERB_GET",
210         "BINDVERB_POST",
211         "BINDVERB_PUT",
212         "BINDVERB_CUSTOM"
213     };
214
215     TRACE("\n"
216             "BINDINFO = {\n"
217             "    %d, %s,\n"
218             "    {%d, %p, %p},\n"
219             "    %s,\n"
220             "    %s,\n"
221             "    %s,\n"
222             "    %d, %08x, %d, %d\n"
223             "    {%d %p %x},\n"
224             "    %s\n"
225             "    %p, %d\n"
226             "}\n",
227
228             bi->cbSize, debugstr_w(bi->szExtraInfo),
229             bi->stgmedData.tymed, bi->stgmedData.u.hGlobal, bi->stgmedData.pUnkForRelease,
230             bi->grfBindInfoF > BINDINFOF_URLENCODEDEXTRAINFO
231                 ? "unknown" : BINDINFOF_str[bi->grfBindInfoF],
232             bi->dwBindVerb > BINDVERB_CUSTOM
233                 ? "unknown" : BINDVERB_str[bi->dwBindVerb],
234             debugstr_w(bi->szCustomVerb),
235             bi->cbstgmedData, bi->dwOptions, bi->dwOptionsFlags, bi->dwCodePage,
236             bi->securityAttributes.nLength,
237             bi->securityAttributes.lpSecurityDescriptor,
238             bi->securityAttributes.bInheritHandle,
239             debugstr_guid(&bi->iid),
240             bi->pUnk, bi->dwReserved
241             );
242 }
243
244 static void set_binding_mime(Binding *binding, LPCWSTR mime)
245 {
246     EnterCriticalSection(&binding->section);
247
248     if(binding->report_mime) {
249         heap_free(binding->mime);
250         binding->mime = heap_strdupW(mime);
251     }
252
253     LeaveCriticalSection(&binding->section);
254 }
255
256 static void handle_mime_available(Binding *binding, BOOL verify)
257 {
258     BOOL report_mime;
259
260     EnterCriticalSection(&binding->section);
261     report_mime = binding->report_mime;
262     binding->report_mime = FALSE;
263     LeaveCriticalSection(&binding->section);
264
265     if(!report_mime)
266         return;
267
268     if(verify) {
269         LPWSTR mime = NULL;
270
271         fill_stream_buffer(binding->stream);
272         FindMimeFromData(NULL, binding->url, binding->stream->buf,
273                          min(binding->stream->buf_size, 255), binding->mime, 0, &mime, 0);
274
275         heap_free(binding->mime);
276         binding->mime = heap_strdupW(mime);
277         CoTaskMemFree(mime);
278     }
279
280     IBindStatusCallback_OnProgress(binding->callback, 0, 0, BINDSTATUS_MIMETYPEAVAILABLE, binding->mime);
281
282     binding->clipboard_format = RegisterClipboardFormatW(binding->mime);
283 }
284
285 typedef struct {
286     task_header_t header;
287     BOOL verify;
288 } mime_available_task_t;
289
290 static void mime_available_proc(Binding *binding, task_header_t *t)
291 {
292     mime_available_task_t *task = (mime_available_task_t*)t;
293
294     handle_mime_available(binding, task->verify);
295
296     heap_free(task);
297 }
298
299 static void mime_available(Binding *This, LPCWSTR mime, BOOL verify)
300 {
301     if(mime)
302         set_binding_mime(This, mime);
303
304     if(GetCurrentThreadId() == This->apartment_thread) {
305         handle_mime_available(This, verify);
306     }else {
307         mime_available_task_t *task = heap_alloc(sizeof(task_header_t));
308         task->verify = verify;
309         push_task(This, &task->header, mime_available_proc);
310     }
311 }
312
313 #define STREAM_THIS(iface) DEFINE_THIS(ProtocolStream, Stream, iface)
314
315 static HRESULT WINAPI ProtocolStream_QueryInterface(IStream *iface,
316                                                           REFIID riid, void **ppv)
317 {
318     ProtocolStream *This = STREAM_THIS(iface);
319
320     *ppv = NULL;
321
322     if(IsEqualGUID(&IID_IUnknown, riid)) {
323         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
324         *ppv = STREAM(This);
325     }else if(IsEqualGUID(&IID_ISequentialStream, riid)) {
326         TRACE("(%p)->(IID_ISequentialStream %p)\n", This, ppv);
327         *ppv = STREAM(This);
328     }else if(IsEqualGUID(&IID_IStream, riid)) {
329         TRACE("(%p)->(IID_IStream %p)\n", This, ppv);
330         *ppv = STREAM(This);
331     }
332
333     if(*ppv) {
334         IStream_AddRef(STREAM(This));
335         return S_OK;
336     }
337
338     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
339     return E_NOINTERFACE;
340 }
341
342 static ULONG WINAPI ProtocolStream_AddRef(IStream *iface)
343 {
344     ProtocolStream *This = STREAM_THIS(iface);
345     LONG ref = InterlockedIncrement(&This->ref);
346
347     TRACE("(%p) ref=%d\n", This, ref);
348
349     return ref;
350 }
351
352 static ULONG WINAPI ProtocolStream_Release(IStream *iface)
353 {
354     ProtocolStream *This = STREAM_THIS(iface);
355     LONG ref = InterlockedDecrement(&This->ref);
356
357     TRACE("(%p) ref=%d\n", This, ref);
358
359     if(!ref) {
360         IInternetProtocol_Release(This->protocol);
361         heap_free(This);
362
363         URLMON_UnlockModule();
364     }
365
366     return ref;
367 }
368
369 static HRESULT WINAPI ProtocolStream_Read(IStream *iface, void *pv,
370                                          ULONG cb, ULONG *pcbRead)
371 {
372     ProtocolStream *This = STREAM_THIS(iface);
373     DWORD read = 0, pread = 0;
374
375     TRACE("(%p)->(%p %d %p)\n", This, pv, cb, pcbRead);
376
377     if(This->buf_size) {
378         read = cb;
379
380         if(read > This->buf_size)
381             read = This->buf_size;
382
383         memcpy(pv, This->buf, read);
384
385         if(read < This->buf_size)
386             memmove(This->buf, This->buf+read, This->buf_size-read);
387         This->buf_size -= read;
388     }
389
390     if(read == cb) {
391         if (pcbRead)
392             *pcbRead = read;
393         return S_OK;
394     }
395
396     This->hres = IInternetProtocol_Read(This->protocol, (PBYTE)pv+read, cb-read, &pread);
397     if (pcbRead)
398         *pcbRead = read + pread;
399
400     if(This->hres == E_PENDING)
401         return E_PENDING;
402     else if(FAILED(This->hres))
403         FIXME("Read failed: %08x\n", This->hres);
404
405     return read || pread ? S_OK : S_FALSE;
406 }
407
408 static HRESULT WINAPI ProtocolStream_Write(IStream *iface, const void *pv,
409                                           ULONG cb, ULONG *pcbWritten)
410 {
411     ProtocolStream *This = STREAM_THIS(iface);
412
413     TRACE("(%p)->(%p %d %p)\n", This, pv, cb, pcbWritten);
414
415     return STG_E_ACCESSDENIED;
416 }
417
418 static HRESULT WINAPI ProtocolStream_Seek(IStream *iface, LARGE_INTEGER dlibMove,
419                                          DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
420 {
421     ProtocolStream *This = STREAM_THIS(iface);
422     FIXME("(%p)->(%d %08x %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
423     return E_NOTIMPL;
424 }
425
426 static HRESULT WINAPI ProtocolStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
427 {
428     ProtocolStream *This = STREAM_THIS(iface);
429     FIXME("(%p)->(%d)\n", This, libNewSize.u.LowPart);
430     return E_NOTIMPL;
431 }
432
433 static HRESULT WINAPI ProtocolStream_CopyTo(IStream *iface, IStream *pstm,
434         ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
435 {
436     ProtocolStream *This = STREAM_THIS(iface);
437     FIXME("(%p)->(%p %d %p %p)\n", This, pstm, cb.u.LowPart, pcbRead, pcbWritten);
438     return E_NOTIMPL;
439 }
440
441 static HRESULT WINAPI ProtocolStream_Commit(IStream *iface, DWORD grfCommitFlags)
442 {
443     ProtocolStream *This = STREAM_THIS(iface);
444
445     TRACE("(%p)->(%08x)\n", This, grfCommitFlags);
446
447     return E_NOTIMPL;
448 }
449
450 static HRESULT WINAPI ProtocolStream_Revert(IStream *iface)
451 {
452     ProtocolStream *This = STREAM_THIS(iface);
453
454     TRACE("(%p)\n", This);
455
456     return E_NOTIMPL;
457 }
458
459 static HRESULT WINAPI ProtocolStream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
460                                                ULARGE_INTEGER cb, DWORD dwLockType)
461 {
462     ProtocolStream *This = STREAM_THIS(iface);
463     FIXME("(%p)->(%d %d %d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
464     return E_NOTIMPL;
465 }
466
467 static HRESULT WINAPI ProtocolStream_UnlockRegion(IStream *iface,
468         ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
469 {
470     ProtocolStream *This = STREAM_THIS(iface);
471     FIXME("(%p)->(%d %d %d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
472     return E_NOTIMPL;
473 }
474
475 static HRESULT WINAPI ProtocolStream_Stat(IStream *iface, STATSTG *pstatstg,
476                                          DWORD dwStatFlag)
477 {
478     ProtocolStream *This = STREAM_THIS(iface);
479     FIXME("(%p)->(%p %08x)\n", This, pstatstg, dwStatFlag);
480     return E_NOTIMPL;
481 }
482
483 static HRESULT WINAPI ProtocolStream_Clone(IStream *iface, IStream **ppstm)
484 {
485     ProtocolStream *This = STREAM_THIS(iface);
486     FIXME("(%p)->(%p)\n", This, ppstm);
487     return E_NOTIMPL;
488 }
489
490 #undef STREAM_THIS
491
492 static const IStreamVtbl ProtocolStreamVtbl = {
493     ProtocolStream_QueryInterface,
494     ProtocolStream_AddRef,
495     ProtocolStream_Release,
496     ProtocolStream_Read,
497     ProtocolStream_Write,
498     ProtocolStream_Seek,
499     ProtocolStream_SetSize,
500     ProtocolStream_CopyTo,
501     ProtocolStream_Commit,
502     ProtocolStream_Revert,
503     ProtocolStream_LockRegion,
504     ProtocolStream_UnlockRegion,
505     ProtocolStream_Stat,
506     ProtocolStream_Clone
507 };
508
509 #define BINDING_THIS(iface) DEFINE_THIS(Binding, Binding, iface)
510
511 static ProtocolStream *create_stream(IInternetProtocol *protocol)
512 {
513     ProtocolStream *ret = heap_alloc(sizeof(ProtocolStream));
514
515     ret->lpStreamVtbl = &ProtocolStreamVtbl;
516     ret->ref = 1;
517     ret->buf_size = 0;
518     ret->init_buf = FALSE;
519     ret->hres = S_OK;
520
521     IInternetProtocol_AddRef(protocol);
522     ret->protocol = protocol;
523
524     URLMON_LockModule();
525
526     return ret;
527 }
528
529 static HRESULT WINAPI Binding_QueryInterface(IBinding *iface, REFIID riid, void **ppv)
530 {
531     Binding *This = BINDING_THIS(iface);
532
533     *ppv = NULL;
534
535     if(IsEqualGUID(&IID_IUnknown, riid)) {
536         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
537         *ppv = BINDING(This);
538     }else if(IsEqualGUID(&IID_IBinding, riid)) {
539         TRACE("(%p)->(IID_IBinding %p)\n", This, ppv);
540         *ppv = BINDING(This);
541     }else if(IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
542         TRACE("(%p)->(IID_IInternetProtocolSink %p)\n", This, ppv);
543         *ppv = PROTSINK(This);
544     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
545         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
546         *ppv = BINDINF(This);
547     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
548         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
549         *ppv = SERVPROV(This);
550     }
551
552     if(*ppv) {
553         IBinding_AddRef(BINDING(This));
554         return S_OK;
555     }
556
557     WARN("Unsupported interface %s\n", debugstr_guid(riid));
558     return E_NOINTERFACE;
559 }
560
561 static ULONG WINAPI Binding_AddRef(IBinding *iface)
562 {
563     Binding *This = BINDING_THIS(iface);
564     LONG ref = InterlockedIncrement(&This->ref);
565
566     TRACE("(%p) ref=%d\n", This, ref);
567
568     return ref;
569 }
570
571 static ULONG WINAPI Binding_Release(IBinding *iface)
572 {
573     Binding *This = BINDING_THIS(iface);
574     LONG ref = InterlockedDecrement(&This->ref);
575
576     TRACE("(%p) ref=%d\n", This, ref);
577
578     if(!ref) {
579         if (This->notif_hwnd)
580             DestroyWindow( This->notif_hwnd );
581         if(This->callback)
582             IBindStatusCallback_Release(This->callback);
583         if(This->protocol)
584             IInternetProtocol_Release(This->protocol);
585         if(This->service_provider)
586             IServiceProvider_Release(This->service_provider);
587         if(This->stream)
588             IStream_Release(STREAM(This->stream));
589
590         ReleaseBindInfo(&This->bindinfo);
591         This->section.DebugInfo->Spare[0] = 0;
592         DeleteCriticalSection(&This->section);
593         heap_free(This->mime);
594         heap_free(This->url);
595
596         heap_free(This);
597
598         URLMON_UnlockModule();
599     }
600
601     return ref;
602 }
603
604 static HRESULT WINAPI Binding_Abort(IBinding *iface)
605 {
606     Binding *This = BINDING_THIS(iface);
607     FIXME("(%p)\n", This);
608     return E_NOTIMPL;
609 }
610
611 static HRESULT WINAPI Binding_Suspend(IBinding *iface)
612 {
613     Binding *This = BINDING_THIS(iface);
614     FIXME("(%p)\n", This);
615     return E_NOTIMPL;
616 }
617
618 static HRESULT WINAPI Binding_Resume(IBinding *iface)
619 {
620     Binding *This = BINDING_THIS(iface);
621     FIXME("(%p)\n", This);
622     return E_NOTIMPL;
623 }
624
625 static HRESULT WINAPI Binding_SetPriority(IBinding *iface, LONG nPriority)
626 {
627     Binding *This = BINDING_THIS(iface);
628     FIXME("(%p)->(%d)\n", This, nPriority);
629     return E_NOTIMPL;
630 }
631
632 static HRESULT WINAPI Binding_GetPriority(IBinding *iface, LONG *pnPriority)
633 {
634     Binding *This = BINDING_THIS(iface);
635     FIXME("(%p)->(%p)\n", This, pnPriority);
636     return E_NOTIMPL;
637 }
638
639 static HRESULT WINAPI Binding_GetBindResult(IBinding *iface, CLSID *pclsidProtocol,
640         DWORD *pdwResult, LPOLESTR *pszResult, DWORD *pdwReserved)
641 {
642     Binding *This = BINDING_THIS(iface);
643     FIXME("(%p)->(%p %p %p %p)\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved);
644     return E_NOTIMPL;
645 }
646
647 #undef BINDING_THIS
648
649 static const IBindingVtbl BindingVtbl = {
650     Binding_QueryInterface,
651     Binding_AddRef,
652     Binding_Release,
653     Binding_Abort,
654     Binding_Suspend,
655     Binding_Resume,
656     Binding_SetPriority,
657     Binding_GetPriority,
658     Binding_GetBindResult
659 };
660
661 #define PROTSINK_THIS(iface) DEFINE_THIS(Binding, InternetProtocolSink, iface)
662
663 static HRESULT WINAPI InternetProtocolSink_QueryInterface(IInternetProtocolSink *iface,
664         REFIID riid, void **ppv)
665 {
666     Binding *This = PROTSINK_THIS(iface);
667     return IBinding_QueryInterface(BINDING(This), riid, ppv);
668 }
669
670 static ULONG WINAPI InternetProtocolSink_AddRef(IInternetProtocolSink *iface)
671 {
672     Binding *This = PROTSINK_THIS(iface);
673     return IBinding_AddRef(BINDING(This));
674 }
675
676 static ULONG WINAPI InternetProtocolSink_Release(IInternetProtocolSink *iface)
677 {
678     Binding *This = PROTSINK_THIS(iface);
679     return IBinding_Release(BINDING(This));
680 }
681
682 typedef struct {
683     task_header_t header;
684     PROTOCOLDATA data;
685 } switch_task_t;
686
687 static void switch_proc(Binding *binding, task_header_t *t)
688 {
689     switch_task_t *task = (switch_task_t*)t;
690
691     IInternetProtocol_Continue(binding->protocol, &task->data);
692
693     heap_free(task);
694 }
695
696 static HRESULT WINAPI InternetProtocolSink_Switch(IInternetProtocolSink *iface,
697         PROTOCOLDATA *pProtocolData)
698 {
699     Binding *This = PROTSINK_THIS(iface);
700     switch_task_t *task;
701
702     TRACE("(%p)->(%p)\n", This, pProtocolData);
703
704     task = heap_alloc(sizeof(switch_task_t));
705     memcpy(&task->data, pProtocolData, sizeof(PROTOCOLDATA));
706
707     push_task(This, &task->header, switch_proc);
708
709     IBinding_AddRef(BINDING(This));
710     PostMessageW(This->notif_hwnd, WM_MK_CONTINUE, 0, (LPARAM)This);
711
712     return S_OK;
713 }
714
715 typedef struct {
716     task_header_t header;
717
718     Binding *binding;
719     ULONG progress;
720     ULONG progress_max;
721     ULONG status_code;
722     LPWSTR status_text;
723 } on_progress_task_t;
724
725 static void on_progress_proc(Binding *binding, task_header_t *t)
726 {
727     on_progress_task_t *task = (on_progress_task_t*)t;
728
729     IBindStatusCallback_OnProgress(binding->callback, task->progress,
730             task->progress_max, task->status_code, task->status_text);
731
732     heap_free(task->status_text);
733     heap_free(task);
734 }
735
736 static void on_progress(Binding *This, ULONG progress, ULONG progress_max,
737                         ULONG status_code, LPCWSTR status_text)
738 {
739     on_progress_task_t *task;
740
741     if(GetCurrentThreadId() == This->apartment_thread && !This->continue_call) {
742         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
743                                        status_code, status_text);
744         return;
745     }
746
747     task = heap_alloc(sizeof(on_progress_task_t));
748
749     task->progress = progress;
750     task->progress_max = progress_max;
751     task->status_code = status_code;
752
753     if(status_text) {
754         DWORD size = (strlenW(status_text)+1)*sizeof(WCHAR);
755
756         task->status_text = heap_alloc(size);
757         memcpy(task->status_text, status_text, size);
758     }else {
759         task->status_text = NULL;
760     }
761
762     push_task(This, &task->header, on_progress_proc);
763
764     if(GetCurrentThreadId() != This->apartment_thread) {
765         IBinding_AddRef(BINDING(This));
766         PostMessageW(This->notif_hwnd, WM_MK_CONTINUE, 0, (LPARAM)This);
767     }
768 }
769
770 static HRESULT WINAPI InternetProtocolSink_ReportProgress(IInternetProtocolSink *iface,
771         ULONG ulStatusCode, LPCWSTR szStatusText)
772 {
773     Binding *This = PROTSINK_THIS(iface);
774
775     TRACE("(%p)->(%u %s)\n", This, ulStatusCode, debugstr_w(szStatusText));
776
777     switch(ulStatusCode) {
778     case BINDSTATUS_FINDINGRESOURCE:
779         on_progress(This, 0, 0, BINDSTATUS_FINDINGRESOURCE, szStatusText);
780         break;
781     case BINDSTATUS_CONNECTING:
782         on_progress(This, 0, 0, BINDSTATUS_CONNECTING, szStatusText);
783         break;
784     case BINDSTATUS_BEGINDOWNLOADDATA:
785         fill_stream_buffer(This->stream);
786         break;
787     case BINDSTATUS_MIMETYPEAVAILABLE:
788         set_binding_mime(This, szStatusText);
789         break;
790     case BINDSTATUS_SENDINGREQUEST:
791         on_progress(This, 0, 0, BINDSTATUS_SENDINGREQUEST, szStatusText);
792         break;
793     case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE:
794         mime_available(This, szStatusText, FALSE);
795         break;
796     case BINDSTATUS_CACHEFILENAMEAVAILABLE:
797         break;
798     case BINDSTATUS_DIRECTBIND:
799         This->report_mime = FALSE;
800         break;
801     default:
802         FIXME("Unhandled status code %d\n", ulStatusCode);
803         return E_NOTIMPL;
804     };
805
806     return S_OK;
807 }
808
809 static void report_data(Binding *This, DWORD bscf, ULONG progress, ULONG progress_max)
810 {
811     FORMATETC formatetc = {0, NULL, 1, -1, TYMED_ISTREAM};
812     BOOL sent_begindownloaddata = FALSE;
813
814     TRACE("(%p)->(%d %u %u)\n", This, bscf, progress, progress_max);
815
816     if(This->download_state == END_DOWNLOAD)
817         return;
818
819     if(GetCurrentThreadId() != This->apartment_thread)
820         FIXME("called from worked hread\n");
821
822     if(This->report_mime)
823         mime_available(This, NULL, TRUE);
824
825     if(This->download_state == BEFORE_DOWNLOAD) {
826         fill_stream_buffer(This->stream);
827
828         This->download_state = DOWNLOADING;
829         sent_begindownloaddata = TRUE;
830         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
831                 BINDSTATUS_BEGINDOWNLOADDATA, This->url);
832     }
833
834     if(This->stream->hres == S_FALSE || (bscf & BSCF_LASTDATANOTIFICATION)) {
835         This->download_state = END_DOWNLOAD;
836         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
837                 BINDSTATUS_ENDDOWNLOADDATA, This->url);
838     }else if(!sent_begindownloaddata) {
839         IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
840                 BINDSTATUS_DOWNLOADINGDATA, This->url);
841     }
842
843     if(!(This->state & BINDING_LOCKED)) {
844         HRESULT hres = IInternetProtocol_LockRequest(This->protocol, 0);
845         if(SUCCEEDED(hres))
846             This->state |= BINDING_LOCKED;
847     }
848
849     formatetc.cfFormat = This->clipboard_format;
850     IBindStatusCallback_OnDataAvailable(This->callback, bscf, progress,
851             &formatetc, &This->stgmed);
852
853     if(This->download_state == END_DOWNLOAD) {
854         IBindStatusCallback_OnStopBinding(This->callback, S_OK, NULL);
855     }
856 }
857
858 typedef struct {
859     task_header_t header;
860     DWORD bscf;
861     ULONG progress;
862     ULONG progress_max;
863 } report_data_task_t;
864
865 static void report_data_proc(Binding *binding, task_header_t *t)
866 {
867     report_data_task_t *task = (report_data_task_t*)t;
868
869     report_data(binding, task->bscf, task->progress, task->progress_max);
870
871     heap_free(task);
872 }
873
874 static HRESULT WINAPI InternetProtocolSink_ReportData(IInternetProtocolSink *iface,
875         DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
876 {
877     Binding *This = PROTSINK_THIS(iface);
878
879     TRACE("(%p)->(%d %u %u)\n", This, grfBSCF, ulProgress, ulProgressMax);
880
881     if(GetCurrentThreadId() != This->apartment_thread)
882         FIXME("called from worked hread\n");
883
884     if(This->continue_call) {
885         report_data_task_t *task = heap_alloc(sizeof(report_data_task_t));
886         task->bscf = grfBSCF;
887         task->progress = ulProgress;
888         task->progress_max = ulProgressMax;
889
890         push_task(This, &task->header, report_data_proc);
891     }else {
892         report_data(This, grfBSCF, ulProgress, ulProgressMax);
893     }
894
895     return S_OK;
896 }
897
898 static void report_result_proc(Binding *binding, task_header_t *t)
899 {
900     IInternetProtocol_Terminate(binding->protocol, 0);
901
902     if(binding->state & BINDING_LOCKED) {
903         IInternetProtocol_UnlockRequest(binding->protocol);
904         binding->state &= ~BINDING_LOCKED;
905     }
906
907     heap_free(t);
908 }
909
910 static HRESULT WINAPI InternetProtocolSink_ReportResult(IInternetProtocolSink *iface,
911         HRESULT hrResult, DWORD dwError, LPCWSTR szResult)
912 {
913     Binding *This = PROTSINK_THIS(iface);
914
915     TRACE("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult));
916
917     if(GetCurrentThreadId() == This->apartment_thread && !This->continue_call) {
918         IInternetProtocol_Terminate(This->protocol, 0);
919     }else {
920         task_header_t *task = heap_alloc(sizeof(task_header_t));
921         push_task(This, task, report_result_proc);
922     }
923
924     return S_OK;
925 }
926
927 #undef PROTSINK_THIS
928
929 static const IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = {
930     InternetProtocolSink_QueryInterface,
931     InternetProtocolSink_AddRef,
932     InternetProtocolSink_Release,
933     InternetProtocolSink_Switch,
934     InternetProtocolSink_ReportProgress,
935     InternetProtocolSink_ReportData,
936     InternetProtocolSink_ReportResult
937 };
938
939 #define BINDINF_THIS(iface) DEFINE_THIS(Binding, InternetBindInfo, iface)
940
941 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
942         REFIID riid, void **ppv)
943 {
944     Binding *This = BINDINF_THIS(iface);
945     return IBinding_QueryInterface(BINDING(This), riid, ppv);
946 }
947
948 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
949 {
950     Binding *This = BINDINF_THIS(iface);
951     return IBinding_AddRef(BINDING(This));
952 }
953
954 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
955 {
956     Binding *This = BINDINF_THIS(iface);
957     return IBinding_Release(BINDING(This));
958 }
959
960 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
961         DWORD *grfBINDF, BINDINFO *pbindinfo)
962 {
963     Binding *This = BINDINF_THIS(iface);
964
965     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
966
967     *grfBINDF = This->bindf;
968
969     memcpy(pbindinfo, &This->bindinfo, sizeof(BINDINFO));
970
971     if(pbindinfo->szExtraInfo || pbindinfo->szCustomVerb)
972         FIXME("copy strings\n");
973
974     if(pbindinfo->stgmedData.pUnkForRelease)
975         IUnknown_AddRef(pbindinfo->stgmedData.pUnkForRelease);
976
977     if(pbindinfo->pUnk)
978         IUnknown_AddRef(pbindinfo->pUnk);
979
980     return S_OK;
981 }
982
983 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
984         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
985 {
986     Binding *This = BINDINF_THIS(iface);
987
988     TRACE("(%p)->(%d %p %d %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
989
990     switch(ulStringType) {
991     case BINDSTRING_ACCEPT_MIMES: {
992         static const WCHAR wszMimes[] = {'*','/','*',0};
993
994         if(!ppwzStr || !pcElFetched)
995             return E_INVALIDARG;
996
997         ppwzStr[0] = CoTaskMemAlloc(sizeof(wszMimes));
998         memcpy(ppwzStr[0], wszMimes, sizeof(wszMimes));
999         *pcElFetched = 1;
1000         return S_OK;
1001     }
1002     case BINDSTRING_USER_AGENT: {
1003         IInternetBindInfo *bindinfo = NULL;
1004         HRESULT hres;
1005
1006         hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetBindInfo,
1007                                                   (void**)&bindinfo);
1008         if(FAILED(hres))
1009             return hres;
1010
1011         hres = IInternetBindInfo_GetBindString(bindinfo, ulStringType, ppwzStr,
1012                                                cEl, pcElFetched);
1013         IInternetBindInfo_Release(bindinfo);
1014
1015         return hres;
1016     }
1017     }
1018
1019     FIXME("not supported string type %d\n", ulStringType);
1020     return E_NOTIMPL;
1021 }
1022
1023 #undef BINDF_THIS
1024
1025 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
1026     InternetBindInfo_QueryInterface,
1027     InternetBindInfo_AddRef,
1028     InternetBindInfo_Release,
1029     InternetBindInfo_GetBindInfo,
1030     InternetBindInfo_GetBindString
1031 };
1032
1033 #define SERVPROV_THIS(iface) DEFINE_THIS(Binding, ServiceProvider, iface)
1034
1035 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface,
1036         REFIID riid, void **ppv)
1037 {
1038     Binding *This = SERVPROV_THIS(iface);
1039     return IBinding_QueryInterface(BINDING(This), riid, ppv);
1040 }
1041
1042 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
1043 {
1044     Binding *This = SERVPROV_THIS(iface);
1045     return IBinding_AddRef(BINDING(This));
1046 }
1047
1048 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
1049 {
1050     Binding *This = SERVPROV_THIS(iface);
1051     return IBinding_Release(BINDING(This));
1052 }
1053
1054 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
1055         REFGUID guidService, REFIID riid, void **ppv)
1056 {
1057     Binding *This = SERVPROV_THIS(iface);
1058     HRESULT hres;
1059
1060     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
1061
1062     if(This->service_provider) {
1063         hres = IServiceProvider_QueryService(This->service_provider, guidService,
1064                                              riid, ppv);
1065         if(SUCCEEDED(hres))
1066             return hres;
1067     }
1068
1069     WARN("unknown service %s\n", debugstr_guid(guidService));
1070     return E_NOTIMPL;
1071 }
1072
1073 #undef SERVPROV_THIS
1074
1075 static const IServiceProviderVtbl ServiceProviderVtbl = {
1076     ServiceProvider_QueryInterface,
1077     ServiceProvider_AddRef,
1078     ServiceProvider_Release,
1079     ServiceProvider_QueryService
1080 };
1081
1082 static HRESULT get_callback(IBindCtx *pbc, IBindStatusCallback **callback)
1083 {
1084     IUnknown *unk;
1085     HRESULT hres;
1086
1087     static WCHAR wszBSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
1088
1089     hres = IBindCtx_GetObjectParam(pbc, wszBSCBHolder, &unk);
1090     if(SUCCEEDED(hres)) {
1091         hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)callback);
1092         IUnknown_Release(unk);
1093     }
1094
1095     return SUCCEEDED(hres) ? S_OK : MK_E_SYNTAX;
1096 }
1097
1098 static HRESULT get_protocol(Binding *This, LPCWSTR url)
1099 {
1100     IClassFactory *cf = NULL;
1101     HRESULT hres;
1102
1103     hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetProtocol,
1104             (void**)&This->protocol);
1105     if(SUCCEEDED(hres))
1106         return S_OK;
1107
1108     if(This->service_provider) {
1109         hres = IServiceProvider_QueryService(This->service_provider, &IID_IInternetProtocol,
1110                 &IID_IInternetProtocol, (void**)&This->protocol);
1111         if(SUCCEEDED(hres))
1112             return S_OK;
1113     }
1114
1115     hres = get_protocol_handler(url, NULL, &cf);
1116     if(FAILED(hres))
1117         return hres;
1118
1119     hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&This->protocol);
1120     IClassFactory_Release(cf);
1121
1122     return hres;
1123 }
1124
1125 static BOOL is_urlmon_protocol(LPCWSTR url)
1126 {
1127     static const WCHAR wszCdl[] = {'c','d','l'};
1128     static const WCHAR wszFile[] = {'f','i','l','e'};
1129     static const WCHAR wszFtp[]  = {'f','t','p'};
1130     static const WCHAR wszGopher[] = {'g','o','p','h','e','r'};
1131     static const WCHAR wszHttp[] = {'h','t','t','p'};
1132     static const WCHAR wszHttps[] = {'h','t','t','p','s'};
1133     static const WCHAR wszMk[]   = {'m','k'};
1134
1135     static const struct {
1136         LPCWSTR scheme;
1137         int len;
1138     } protocol_list[] = {
1139         {wszCdl,    sizeof(wszCdl)   /sizeof(WCHAR)},
1140         {wszFile,   sizeof(wszFile)  /sizeof(WCHAR)},
1141         {wszFtp,    sizeof(wszFtp)   /sizeof(WCHAR)},
1142         {wszGopher, sizeof(wszGopher)/sizeof(WCHAR)},
1143         {wszHttp,   sizeof(wszHttp)  /sizeof(WCHAR)},
1144         {wszHttps,  sizeof(wszHttps) /sizeof(WCHAR)},
1145         {wszMk,     sizeof(wszMk)    /sizeof(WCHAR)}
1146     };
1147
1148     int i, len = strlenW(url);
1149
1150     for(i=0; i < sizeof(protocol_list)/sizeof(protocol_list[0]); i++) {
1151         if(len >= protocol_list[i].len
1152            && !memcmp(url, protocol_list[i].scheme, protocol_list[i].len*sizeof(WCHAR)))
1153             return TRUE;
1154     }
1155
1156     return FALSE;
1157 }
1158
1159 static HRESULT Binding_Create(LPCWSTR url, IBindCtx *pbc, REFIID riid, Binding **binding)
1160 {
1161     Binding *ret;
1162     int len;
1163     HRESULT hres;
1164
1165     if(!IsEqualGUID(&IID_IStream, riid)) {
1166         FIXME("Unsupported riid %s\n", debugstr_guid(riid));
1167         return E_NOTIMPL;
1168     }
1169
1170     URLMON_LockModule();
1171
1172     ret = heap_alloc(sizeof(Binding));
1173
1174     ret->lpBindingVtbl              = &BindingVtbl;
1175     ret->lpInternetProtocolSinkVtbl = &InternetProtocolSinkVtbl;
1176     ret->lpInternetBindInfoVtbl     = &InternetBindInfoVtbl;
1177     ret->lpServiceProviderVtbl      = &ServiceProviderVtbl;
1178
1179     ret->ref = 1;
1180
1181     ret->callback = NULL;
1182     ret->protocol = NULL;
1183     ret->service_provider = NULL;
1184     ret->stream = NULL;
1185     ret->mime = NULL;
1186     ret->clipboard_format = 0;
1187     ret->url = NULL;
1188     ret->apartment_thread = GetCurrentThreadId();
1189     ret->notif_hwnd = get_notif_hwnd();
1190     ret->report_mime = TRUE;
1191     ret->continue_call = 0;
1192     ret->state = 0;
1193     ret->download_state = BEFORE_DOWNLOAD;
1194     ret->task_queue_head = ret->task_queue_tail = NULL;
1195
1196     memset(&ret->bindinfo, 0, sizeof(BINDINFO));
1197     ret->bindinfo.cbSize = sizeof(BINDINFO);
1198     ret->bindf = 0;
1199
1200     InitializeCriticalSection(&ret->section);
1201     ret->section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": Binding.section");
1202
1203     hres = get_callback(pbc, &ret->callback);
1204     if(FAILED(hres)) {
1205         WARN("Could not get IBindStatusCallback\n");
1206         IBinding_Release(BINDING(ret));
1207         return hres;
1208     }
1209
1210     IBindStatusCallback_QueryInterface(ret->callback, &IID_IServiceProvider,
1211                                        (void**)&ret->service_provider);
1212
1213     hres = get_protocol(ret, url);
1214     if(FAILED(hres)) {
1215         WARN("Could not get protocol handler\n");
1216         IBinding_Release(BINDING(ret));
1217         return hres;
1218     }
1219
1220     hres = IBindStatusCallback_GetBindInfo(ret->callback, &ret->bindf, &ret->bindinfo);
1221     if(FAILED(hres)) {
1222         WARN("GetBindInfo failed: %08x\n", hres);
1223         IBinding_Release(BINDING(ret));
1224         return hres;
1225     }
1226
1227     dump_BINDINFO(&ret->bindinfo);
1228
1229     ret->bindf |= BINDF_FROMURLMON;
1230
1231     if(!is_urlmon_protocol(url))
1232         ret->bindf |= BINDF_NEEDFILE;
1233
1234     len = strlenW(url)+1;
1235     ret->url = heap_alloc(len*sizeof(WCHAR));
1236     memcpy(ret->url, url, len*sizeof(WCHAR));
1237
1238     ret->stream = create_stream(ret->protocol);
1239     ret->stgmed.tymed = TYMED_ISTREAM;
1240     ret->stgmed.u.pstm = STREAM(ret->stream);
1241     ret->stgmed.pUnkForRelease = (IUnknown*)BINDING(ret); /* NOTE: Windows uses other IUnknown */
1242
1243     *binding = ret;
1244     return S_OK;
1245 }
1246
1247 HRESULT start_binding(LPCWSTR url, IBindCtx *pbc, REFIID riid, void **ppv)
1248 {
1249     Binding *binding = NULL;
1250     HRESULT hres;
1251     MSG msg;
1252
1253     *ppv = NULL;
1254
1255     hres = Binding_Create(url, pbc, riid, &binding);
1256     if(FAILED(hres))
1257         return hres;
1258
1259     hres = IBindStatusCallback_OnStartBinding(binding->callback, 0, BINDING(binding));
1260     if(FAILED(hres)) {
1261         WARN("OnStartBinding failed: %08x\n", hres);
1262         IBindStatusCallback_OnStopBinding(binding->callback, 0x800c0008, NULL);
1263         IBinding_Release(BINDING(binding));
1264         return hres;
1265     }
1266
1267     hres = IInternetProtocol_Start(binding->protocol, url, PROTSINK(binding),
1268              BINDINF(binding), 0, 0);
1269
1270     if(FAILED(hres)) {
1271         WARN("Start failed: %08x\n", hres);
1272
1273         IInternetProtocol_Terminate(binding->protocol, 0);
1274         IBindStatusCallback_OnStopBinding(binding->callback, S_OK, NULL);
1275         IBinding_Release(BINDING(binding));
1276
1277         return hres;
1278     }
1279
1280     while(!(binding->bindf & BINDF_ASYNCHRONOUS) &&
1281           binding->download_state != END_DOWNLOAD) {
1282         MsgWaitForMultipleObjects(0, NULL, FALSE, 5000, QS_POSTMESSAGE);
1283         while (PeekMessageW(&msg, binding->notif_hwnd, WM_USER, WM_USER+117, PM_REMOVE|PM_NOYIELD)) {
1284             TranslateMessage(&msg);
1285             DispatchMessageW(&msg);
1286         }
1287     }
1288
1289     if(binding->stream->init_buf) {
1290         if(binding->state & BINDING_LOCKED)
1291             IInternetProtocol_UnlockRequest(binding->protocol);
1292
1293         IStream_AddRef(STREAM(binding->stream));
1294         *ppv = binding->stream;
1295
1296         hres = S_OK;
1297     }else {
1298         hres = MK_S_ASYNCHRONOUS;
1299     }
1300
1301     IBinding_Release(BINDING(binding));
1302
1303     return hres;
1304 }