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