version: Use GetModuleHandleEx to increment the module ref count.
[wine/multimedia.git] / dlls / urlmon / binding.c
blobae58503a715c81b6b45b9607be1e2a6a9cbec402
1 /*
2 * Copyright 2005-2007 Jacek Caban for CodeWeavers
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.
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.
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
19 #include <stdarg.h>
21 #define COBJMACROS
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
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"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
37 typedef struct Binding Binding;
39 struct _task_header_t;
41 typedef void (*task_proc_t)(Binding*, struct _task_header_t*);
43 typedef struct _task_header_t {
44 task_proc_t proc;
45 struct _task_header_t *next;
46 } task_header_t;
48 typedef struct {
49 const IStreamVtbl *lpStreamVtbl;
51 LONG ref;
53 IInternetProtocol *protocol;
55 BYTE buf[1024*8];
56 DWORD buf_size;
57 BOOL init_buf;
58 HRESULT hres;
59 } ProtocolStream;
61 typedef enum {
62 BEFORE_DOWNLOAD,
63 DOWNLOADING,
64 END_DOWNLOAD
65 } download_state_t;
67 struct Binding {
68 const IBindingVtbl *lpBindingVtbl;
69 const IInternetProtocolSinkVtbl *lpInternetProtocolSinkVtbl;
70 const IInternetBindInfoVtbl *lpInternetBindInfoVtbl;
71 const IServiceProviderVtbl *lpServiceProviderVtbl;
73 LONG ref;
75 IBindStatusCallback *callback;
76 IInternetProtocol *protocol;
77 IServiceProvider *service_provider;
78 ProtocolStream *stream;
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;
89 DWORD apartment_thread;
90 HWND notif_hwnd;
92 STGMEDIUM stgmed;
94 task_header_t *task_queue_head, *task_queue_tail;
95 CRITICAL_SECTION section;
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)
103 #define STREAM(x) ((IStream*) &(x)->lpStreamVtbl)
104 #define HTTPNEG2(x) ((IHttpNegotiate2*) &(x)->lpHttpNegotiate2Vtbl)
106 #define WM_MK_CONTINUE (WM_USER+101)
108 static void push_task(Binding *binding, task_header_t *task, task_proc_t proc)
110 task->proc = proc;
111 task->next = NULL;
113 EnterCriticalSection(&binding->section);
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;
122 LeaveCriticalSection(&binding->section);
125 static task_header_t *pop_task(Binding *binding)
127 task_header_t *ret;
129 EnterCriticalSection(&binding->section);
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;
138 LeaveCriticalSection(&binding->section);
140 return ret;
143 static void fill_stream_buffer(ProtocolStream *This)
145 DWORD read = 0;
147 if(sizeof(This->buf) == This->buf_size)
148 return;
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;
157 static LRESULT WINAPI notif_wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
159 if(msg == WM_MK_CONTINUE) {
160 Binding *binding = (Binding*)lParam;
161 task_header_t *task;
163 while((task = pop_task(binding))) {
164 binding->continue_call++;
165 task->proc(binding, task);
166 binding->continue_call--;
169 IBinding_Release(BINDING(binding));
170 return 0;
173 return DefWindowProcW(hwnd, msg, wParam, lParam);
176 static HWND get_notif_hwnd(void)
178 static ATOM wnd_class = 0;
179 HWND hwnd;
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};
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
194 wndclass.hInstance = URLMON_hInstance;
196 wnd_class = RegisterClassExW(&wndclass);
197 if (!wnd_class && GetLastError() == ERROR_CLASS_ALREADY_EXISTS)
198 wnd_class = 1;
201 hwnd = CreateWindowExW(0, wszURLMonikerNotificationWindow,
202 wszURLMonikerNotificationWindow, 0, 0, 0, 0, 0, HWND_MESSAGE,
203 NULL, URLMON_hInstance, NULL);
205 TRACE("hwnd = %p\n", hwnd);
207 return hwnd;
210 static void dump_BINDINFO(BINDINFO *bi)
212 static const char * const BINDINFOF_str[] = {
213 "#0",
214 "BINDINFOF_URLENCODESTGMEDDATA",
215 "BINDINFOF_URLENCODEDEXTRAINFO"
218 static const char * const BINDVERB_str[] = {
219 "BINDVERB_GET",
220 "BINDVERB_POST",
221 "BINDVERB_PUT",
222 "BINDVERB_CUSTOM"
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",
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
254 #define STREAM_THIS(iface) DEFINE_THIS(ProtocolStream, Stream, iface)
256 static HRESULT WINAPI ProtocolStream_QueryInterface(IStream *iface,
257 REFIID riid, void **ppv)
259 ProtocolStream *This = STREAM_THIS(iface);
261 *ppv = NULL;
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);
274 if(*ppv) {
275 IStream_AddRef(STREAM(This));
276 return S_OK;
279 WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
280 return E_NOINTERFACE;
283 static ULONG WINAPI ProtocolStream_AddRef(IStream *iface)
285 ProtocolStream *This = STREAM_THIS(iface);
286 LONG ref = InterlockedIncrement(&This->ref);
288 TRACE("(%p) ref=%d\n", This, ref);
290 return ref;
293 static ULONG WINAPI ProtocolStream_Release(IStream *iface)
295 ProtocolStream *This = STREAM_THIS(iface);
296 LONG ref = InterlockedDecrement(&This->ref);
298 TRACE("(%p) ref=%d\n", This, ref);
300 if(!ref) {
301 IInternetProtocol_Release(This->protocol);
302 HeapFree(GetProcessHeap(), 0, This);
304 URLMON_UnlockModule();
307 return ref;
310 static HRESULT WINAPI ProtocolStream_Read(IStream *iface, void *pv,
311 ULONG cb, ULONG *pcbRead)
313 ProtocolStream *This = STREAM_THIS(iface);
314 DWORD read = 0, pread = 0;
316 TRACE("(%p)->(%p %d %p)\n", This, pv, cb, pcbRead);
318 if(This->buf_size) {
319 read = cb;
321 if(read > This->buf_size)
322 read = This->buf_size;
324 memcpy(pv, This->buf, read);
326 if(read < This->buf_size)
327 memmove(This->buf, This->buf+read, This->buf_size-read);
328 This->buf_size -= read;
331 if(read == cb) {
332 if (pcbRead)
333 *pcbRead = read;
334 return S_OK;
337 This->hres = IInternetProtocol_Read(This->protocol, (PBYTE)pv+read, cb-read, &pread);
338 if (pcbRead)
339 *pcbRead = read + pread;
341 if(This->hres == E_PENDING)
342 return E_PENDING;
343 else if(FAILED(This->hres))
344 FIXME("Read failed: %08x\n", This->hres);
346 return read || pread ? S_OK : S_FALSE;
349 static HRESULT WINAPI ProtocolStream_Write(IStream *iface, const void *pv,
350 ULONG cb, ULONG *pcbWritten)
352 ProtocolStream *This = STREAM_THIS(iface);
354 TRACE("(%p)->(%p %d %p)\n", This, pv, cb, pcbWritten);
356 return STG_E_ACCESSDENIED;
359 static HRESULT WINAPI ProtocolStream_Seek(IStream *iface, LARGE_INTEGER dlibMove,
360 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
362 ProtocolStream *This = STREAM_THIS(iface);
363 FIXME("(%p)->(%d %08x %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
364 return E_NOTIMPL;
367 static HRESULT WINAPI ProtocolStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
369 ProtocolStream *This = STREAM_THIS(iface);
370 FIXME("(%p)->(%d)\n", This, libNewSize.u.LowPart);
371 return E_NOTIMPL;
374 static HRESULT WINAPI ProtocolStream_CopyTo(IStream *iface, IStream *pstm,
375 ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
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;
382 static HRESULT WINAPI ProtocolStream_Commit(IStream *iface, DWORD grfCommitFlags)
384 ProtocolStream *This = STREAM_THIS(iface);
386 TRACE("(%p)->(%08x)\n", This, grfCommitFlags);
388 return E_NOTIMPL;
391 static HRESULT WINAPI ProtocolStream_Revert(IStream *iface)
393 ProtocolStream *This = STREAM_THIS(iface);
395 TRACE("(%p)\n", This);
397 return E_NOTIMPL;
400 static HRESULT WINAPI ProtocolStream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
401 ULARGE_INTEGER cb, DWORD dwLockType)
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;
408 static HRESULT WINAPI ProtocolStream_UnlockRegion(IStream *iface,
409 ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
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;
416 static HRESULT WINAPI ProtocolStream_Stat(IStream *iface, STATSTG *pstatstg,
417 DWORD dwStatFlag)
419 ProtocolStream *This = STREAM_THIS(iface);
420 FIXME("(%p)->(%p %08x)\n", This, pstatstg, dwStatFlag);
421 return E_NOTIMPL;
424 static HRESULT WINAPI ProtocolStream_Clone(IStream *iface, IStream **ppstm)
426 ProtocolStream *This = STREAM_THIS(iface);
427 FIXME("(%p)->(%p)\n", This, ppstm);
428 return E_NOTIMPL;
431 #undef STREAM_THIS
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
450 #define BINDING_THIS(iface) DEFINE_THIS(Binding, Binding, iface)
452 static ProtocolStream *create_stream(IInternetProtocol *protocol)
454 ProtocolStream *ret = HeapAlloc(GetProcessHeap(), 0, sizeof(ProtocolStream));
456 ret->lpStreamVtbl = &ProtocolStreamVtbl;
457 ret->ref = 1;
458 ret->buf_size = 0;
459 ret->init_buf = FALSE;
460 ret->hres = S_OK;
462 IInternetProtocol_AddRef(protocol);
463 ret->protocol = protocol;
465 URLMON_LockModule();
467 return ret;
470 static HRESULT WINAPI Binding_QueryInterface(IBinding *iface, REFIID riid, void **ppv)
472 Binding *This = BINDING_THIS(iface);
474 *ppv = NULL;
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);
493 if(*ppv) {
494 IBinding_AddRef(BINDING(This));
495 return S_OK;
498 WARN("Unsupported interface %s\n", debugstr_guid(riid));
499 return E_NOINTERFACE;
502 static ULONG WINAPI Binding_AddRef(IBinding *iface)
504 Binding *This = BINDING_THIS(iface);
505 LONG ref = InterlockedIncrement(&This->ref);
507 TRACE("(%p) ref=%d\n", This, ref);
509 return ref;
512 static ULONG WINAPI Binding_Release(IBinding *iface)
514 Binding *This = BINDING_THIS(iface);
515 LONG ref = InterlockedDecrement(&This->ref);
517 TRACE("(%p) ref=%d\n", This, ref);
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));
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);
537 HeapFree(GetProcessHeap(), 0, This);
539 URLMON_UnlockModule();
542 return ref;
545 static HRESULT WINAPI Binding_Abort(IBinding *iface)
547 Binding *This = BINDING_THIS(iface);
548 FIXME("(%p)\n", This);
549 return E_NOTIMPL;
552 static HRESULT WINAPI Binding_Suspend(IBinding *iface)
554 Binding *This = BINDING_THIS(iface);
555 FIXME("(%p)\n", This);
556 return E_NOTIMPL;
559 static HRESULT WINAPI Binding_Resume(IBinding *iface)
561 Binding *This = BINDING_THIS(iface);
562 FIXME("(%p)\n", This);
563 return E_NOTIMPL;
566 static HRESULT WINAPI Binding_SetPriority(IBinding *iface, LONG nPriority)
568 Binding *This = BINDING_THIS(iface);
569 FIXME("(%p)->(%d)\n", This, nPriority);
570 return E_NOTIMPL;
573 static HRESULT WINAPI Binding_GetPriority(IBinding *iface, LONG *pnPriority)
575 Binding *This = BINDING_THIS(iface);
576 FIXME("(%p)->(%p)\n", This, pnPriority);
577 return E_NOTIMPL;
580 static HRESULT WINAPI Binding_GetBindResult(IBinding *iface, CLSID *pclsidProtocol,
581 DWORD *pdwResult, LPOLESTR *pszResult, DWORD *pdwReserved)
583 Binding *This = BINDING_THIS(iface);
584 FIXME("(%p)->(%p %p %p %p)\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved);
585 return E_NOTIMPL;
588 #undef BINDING_THIS
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
602 #define PROTSINK_THIS(iface) DEFINE_THIS(Binding, InternetProtocolSink, iface)
604 static HRESULT WINAPI InternetProtocolSink_QueryInterface(IInternetProtocolSink *iface,
605 REFIID riid, void **ppv)
607 Binding *This = PROTSINK_THIS(iface);
608 return IBinding_QueryInterface(BINDING(This), riid, ppv);
611 static ULONG WINAPI InternetProtocolSink_AddRef(IInternetProtocolSink *iface)
613 Binding *This = PROTSINK_THIS(iface);
614 return IBinding_AddRef(BINDING(This));
617 static ULONG WINAPI InternetProtocolSink_Release(IInternetProtocolSink *iface)
619 Binding *This = PROTSINK_THIS(iface);
620 return IBinding_Release(BINDING(This));
623 typedef struct {
624 task_header_t header;
625 PROTOCOLDATA *data;
626 } switch_task_t;
628 static void switch_proc(Binding *binding, task_header_t *t)
630 switch_task_t *task = (switch_task_t*)t;
632 IInternetProtocol_Continue(binding->protocol, task->data);
634 HeapFree(GetProcessHeap(), 0, task);
637 static HRESULT WINAPI InternetProtocolSink_Switch(IInternetProtocolSink *iface,
638 PROTOCOLDATA *pProtocolData)
640 Binding *This = PROTSINK_THIS(iface);
641 switch_task_t *task;
643 TRACE("(%p)->(%p)\n", This, pProtocolData);
645 task = HeapAlloc(GetProcessHeap(), 0, sizeof(switch_task_t));
646 task->data = pProtocolData;
648 push_task(This, &task->header, switch_proc);
650 IBinding_AddRef(BINDING(This));
651 PostMessageW(This->notif_hwnd, WM_MK_CONTINUE, 0, (LPARAM)This);
653 return S_OK;
656 typedef struct {
657 task_header_t header;
659 Binding *binding;
660 ULONG progress;
661 ULONG progress_max;
662 ULONG status_code;
663 LPWSTR status_text;
664 } on_progress_task_t;
666 static void on_progress_proc(Binding *binding, task_header_t *t)
668 on_progress_task_t *task = (on_progress_task_t*)t;
670 IBindStatusCallback_OnProgress(binding->callback, task->progress,
671 task->progress_max, task->status_code, task->status_text);
673 HeapFree(GetProcessHeap(), 0, task->status_text);
674 HeapFree(GetProcessHeap(), 0, task);
677 static void on_progress(Binding *This, ULONG progress, ULONG progress_max,
678 ULONG status_code, LPCWSTR status_text)
680 on_progress_task_t *task;
682 if(GetCurrentThreadId() == This->apartment_thread && !This->continue_call) {
683 IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
684 status_code, status_text);
685 return;
688 task = HeapAlloc(GetProcessHeap(), 0, sizeof(on_progress_task_t));
690 task->progress = progress;
691 task->progress_max = progress_max;
692 task->status_code = status_code;
694 if(status_text) {
695 DWORD size = (strlenW(status_text)+1)*sizeof(WCHAR);
697 task->status_text = HeapAlloc(GetProcessHeap(), 0, size);
698 memcpy(task->status_text, status_text, size);
699 }else {
700 task->status_text = NULL;
703 push_task(This, &task->header, on_progress_proc);
705 if(GetCurrentThreadId() != This->apartment_thread) {
706 IBinding_AddRef(BINDING(This));
707 PostMessageW(This->notif_hwnd, WM_MK_CONTINUE, 0, (LPARAM)This);
711 static HRESULT WINAPI InternetProtocolSink_ReportProgress(IInternetProtocolSink *iface,
712 ULONG ulStatusCode, LPCWSTR szStatusText)
714 Binding *This = PROTSINK_THIS(iface);
716 TRACE("(%p)->(%u %s)\n", This, ulStatusCode, debugstr_w(szStatusText));
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;
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;
751 return S_OK;
754 static void report_data(Binding *This, DWORD bscf, ULONG progress, ULONG progress_max)
756 FORMATETC formatetc = {0, NULL, 1, -1, TYMED_ISTREAM};
757 BOOL sent_begindownloaddata = FALSE;
759 TRACE("(%p)->(%d %u %u)\n", This, bscf, progress, progress_max);
761 if(This->download_state == END_DOWNLOAD)
762 return;
764 if(GetCurrentThreadId() != This->apartment_thread)
765 FIXME("called from worked hread\n");
767 if(This->report_mime) {
768 LPWSTR mime;
770 This->report_mime = FALSE;
772 fill_stream_buffer(This->stream);
774 FindMimeFromData(NULL, This->url, This->stream->buf,
775 min(This->stream->buf_size, 255), This->mime, 0, &mime, 0);
777 IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
778 BINDSTATUS_MIMETYPEAVAILABLE, mime);
781 if(This->download_state == BEFORE_DOWNLOAD) {
782 fill_stream_buffer(This->stream);
784 This->download_state = DOWNLOADING;
785 sent_begindownloaddata = TRUE;
786 IBindStatusCallback_OnProgress(This->callback, progress, progress_max,
787 BINDSTATUS_BEGINDOWNLOADDATA, This->url);
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);
799 if(!This->request_locked) {
800 HRESULT hres = IInternetProtocol_LockRequest(This->protocol, 0);
801 This->request_locked = SUCCEEDED(hres);
804 IBindStatusCallback_OnDataAvailable(This->callback, bscf, progress,
805 &formatetc, &This->stgmed);
807 if(This->download_state == END_DOWNLOAD) {
808 IBindStatusCallback_OnStopBinding(This->callback, S_OK, NULL);
812 typedef struct {
813 task_header_t header;
814 DWORD bscf;
815 ULONG progress;
816 ULONG progress_max;
817 } report_data_task_t;
819 static void report_data_proc(Binding *binding, task_header_t *t)
821 report_data_task_t *task = (report_data_task_t*)t;
823 report_data(binding, task->bscf, task->progress, task->progress_max);
825 HeapFree(GetProcessHeap(), 0, task);
828 static HRESULT WINAPI InternetProtocolSink_ReportData(IInternetProtocolSink *iface,
829 DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
831 Binding *This = PROTSINK_THIS(iface);
833 TRACE("(%p)->(%d %u %u)\n", This, grfBSCF, ulProgress, ulProgressMax);
835 if(GetCurrentThreadId() != This->apartment_thread)
836 FIXME("called from worked hread\n");
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;
844 push_task(This, &task->header, report_data_proc);
845 }else {
846 report_data(This, grfBSCF, ulProgress, ulProgressMax);
849 return S_OK;
852 static void report_result_proc(Binding *binding, task_header_t *t)
854 IInternetProtocol_Terminate(binding->protocol, 0);
856 if(binding->request_locked) {
857 IInternetProtocol_UnlockRequest(binding->protocol);
858 binding->request_locked = FALSE;
861 HeapFree(GetProcessHeap(), 0, t);
864 static HRESULT WINAPI InternetProtocolSink_ReportResult(IInternetProtocolSink *iface,
865 HRESULT hrResult, DWORD dwError, LPCWSTR szResult)
867 Binding *This = PROTSINK_THIS(iface);
869 TRACE("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult));
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);
878 return S_OK;
881 #undef PROTSINK_THIS
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
893 #define BINDINF_THIS(iface) DEFINE_THIS(Binding, InternetBindInfo, iface)
895 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
896 REFIID riid, void **ppv)
898 Binding *This = BINDINF_THIS(iface);
899 return IBinding_QueryInterface(BINDING(This), riid, ppv);
902 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
904 Binding *This = BINDINF_THIS(iface);
905 return IBinding_AddRef(BINDING(This));
908 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
910 Binding *This = BINDINF_THIS(iface);
911 return IBinding_Release(BINDING(This));
914 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
915 DWORD *grfBINDF, BINDINFO *pbindinfo)
917 Binding *This = BINDINF_THIS(iface);
919 TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
921 *grfBINDF = This->bindf;
923 memcpy(pbindinfo, &This->bindinfo, sizeof(BINDINFO));
925 if(pbindinfo->szExtraInfo || pbindinfo->szCustomVerb)
926 FIXME("copy strings\n");
928 if(pbindinfo->stgmedData.pUnkForRelease)
929 IUnknown_AddRef(pbindinfo->stgmedData.pUnkForRelease);
931 if(pbindinfo->pUnk)
932 IUnknown_AddRef(pbindinfo->pUnk);
934 return S_OK;
937 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
938 ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
940 Binding *This = BINDINF_THIS(iface);
942 TRACE("(%p)->(%d %p %d %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
944 switch(ulStringType) {
945 case BINDSTRING_ACCEPT_MIMES: {
946 static const WCHAR wszMimes[] = {'*','/','*',0};
948 if(!ppwzStr || !pcElFetched)
949 return E_INVALIDARG;
951 ppwzStr[0] = CoTaskMemAlloc(sizeof(wszMimes));
952 memcpy(ppwzStr[0], wszMimes, sizeof(wszMimes));
953 *pcElFetched = 1;
954 return S_OK;
956 case BINDSTRING_USER_AGENT: {
957 IInternetBindInfo *bindinfo = NULL;
958 HRESULT hres;
960 hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetBindInfo,
961 (void**)&bindinfo);
962 if(FAILED(hres))
963 return hres;
965 hres = IInternetBindInfo_GetBindString(bindinfo, ulStringType, ppwzStr,
966 cEl, pcElFetched);
967 IInternetBindInfo_Release(bindinfo);
969 return hres;
973 FIXME("not supported string type %d\n", ulStringType);
974 return E_NOTIMPL;
977 #undef BINDF_THIS
979 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
980 InternetBindInfo_QueryInterface,
981 InternetBindInfo_AddRef,
982 InternetBindInfo_Release,
983 InternetBindInfo_GetBindInfo,
984 InternetBindInfo_GetBindString
987 #define SERVPROV_THIS(iface) DEFINE_THIS(Binding, ServiceProvider, iface)
989 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface,
990 REFIID riid, void **ppv)
992 Binding *This = SERVPROV_THIS(iface);
993 return IBinding_QueryInterface(BINDING(This), riid, ppv);
996 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
998 Binding *This = SERVPROV_THIS(iface);
999 return IBinding_AddRef(BINDING(This));
1002 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
1004 Binding *This = SERVPROV_THIS(iface);
1005 return IBinding_Release(BINDING(This));
1008 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
1009 REFGUID guidService, REFIID riid, void **ppv)
1011 Binding *This = SERVPROV_THIS(iface);
1012 HRESULT hres;
1014 TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
1016 if(This->service_provider) {
1017 hres = IServiceProvider_QueryService(This->service_provider, guidService,
1018 riid, ppv);
1019 if(SUCCEEDED(hres))
1020 return hres;
1023 WARN("unknown service %s\n", debugstr_guid(guidService));
1024 return E_NOTIMPL;
1027 #undef SERVPROV_THIS
1029 static const IServiceProviderVtbl ServiceProviderVtbl = {
1030 ServiceProvider_QueryInterface,
1031 ServiceProvider_AddRef,
1032 ServiceProvider_Release,
1033 ServiceProvider_QueryService
1036 static HRESULT get_callback(IBindCtx *pbc, IBindStatusCallback **callback)
1038 IUnknown *unk;
1039 HRESULT hres;
1041 static WCHAR wszBSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
1043 hres = IBindCtx_GetObjectParam(pbc, wszBSCBHolder, &unk);
1044 if(SUCCEEDED(hres)) {
1045 hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)callback);
1046 IUnknown_Release(unk);
1049 return SUCCEEDED(hres) ? S_OK : MK_E_SYNTAX;
1052 static HRESULT get_protocol(Binding *This, LPCWSTR url)
1054 IClassFactory *cf = NULL;
1055 HRESULT hres;
1057 hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetProtocol,
1058 (void**)&This->protocol);
1059 if(SUCCEEDED(hres))
1060 return S_OK;
1062 if(This->service_provider) {
1063 hres = IServiceProvider_QueryService(This->service_provider, &IID_IInternetProtocol,
1064 &IID_IInternetProtocol, (void**)&This->protocol);
1065 if(SUCCEEDED(hres))
1066 return S_OK;
1069 hres = get_protocol_handler(url, NULL, &cf);
1070 if(FAILED(hres))
1071 return hres;
1073 hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&This->protocol);
1074 IClassFactory_Release(cf);
1076 return hres;
1079 static BOOL is_urlmon_protocol(LPCWSTR url)
1081 static const WCHAR wszCdl[] = {'c','d','l'};
1082 static const WCHAR wszFile[] = {'f','i','l','e'};
1083 static const WCHAR wszFtp[] = {'f','t','p'};
1084 static const WCHAR wszGopher[] = {'g','o','p','h','e','r'};
1085 static const WCHAR wszHttp[] = {'h','t','t','p'};
1086 static const WCHAR wszHttps[] = {'h','t','t','p','s'};
1087 static const WCHAR wszMk[] = {'m','k'};
1089 static const struct {
1090 LPCWSTR scheme;
1091 int len;
1092 } protocol_list[] = {
1093 {wszCdl, sizeof(wszCdl) /sizeof(WCHAR)},
1094 {wszFile, sizeof(wszFile) /sizeof(WCHAR)},
1095 {wszFtp, sizeof(wszFtp) /sizeof(WCHAR)},
1096 {wszGopher, sizeof(wszGopher)/sizeof(WCHAR)},
1097 {wszHttp, sizeof(wszHttp) /sizeof(WCHAR)},
1098 {wszHttps, sizeof(wszHttps) /sizeof(WCHAR)},
1099 {wszMk, sizeof(wszMk) /sizeof(WCHAR)}
1102 int i, len = strlenW(url);
1104 for(i=0; i < sizeof(protocol_list)/sizeof(protocol_list[0]); i++) {
1105 if(len >= protocol_list[i].len
1106 && !memcmp(url, protocol_list[i].scheme, protocol_list[i].len*sizeof(WCHAR)))
1107 return TRUE;
1110 return FALSE;
1113 static HRESULT Binding_Create(LPCWSTR url, IBindCtx *pbc, REFIID riid, Binding **binding)
1115 Binding *ret;
1116 int len;
1117 HRESULT hres;
1119 if(!IsEqualGUID(&IID_IStream, riid)) {
1120 FIXME("Unsupported riid %s\n", debugstr_guid(riid));
1121 return E_NOTIMPL;
1124 URLMON_LockModule();
1126 ret = HeapAlloc(GetProcessHeap(), 0, sizeof(Binding));
1128 ret->lpBindingVtbl = &BindingVtbl;
1129 ret->lpInternetProtocolSinkVtbl = &InternetProtocolSinkVtbl;
1130 ret->lpInternetBindInfoVtbl = &InternetBindInfoVtbl;
1131 ret->lpServiceProviderVtbl = &ServiceProviderVtbl;
1133 ret->ref = 1;
1135 ret->callback = NULL;
1136 ret->protocol = NULL;
1137 ret->service_provider = NULL;
1138 ret->stream = NULL;
1139 ret->mime = NULL;
1140 ret->url = NULL;
1141 ret->apartment_thread = GetCurrentThreadId();
1142 ret->notif_hwnd = get_notif_hwnd();
1143 ret->report_mime = TRUE;
1144 ret->continue_call = 0;
1145 ret->request_locked = FALSE;
1146 ret->download_state = BEFORE_DOWNLOAD;
1147 ret->task_queue_head = ret->task_queue_tail = NULL;
1149 memset(&ret->bindinfo, 0, sizeof(BINDINFO));
1150 ret->bindinfo.cbSize = sizeof(BINDINFO);
1151 ret->bindf = 0;
1153 InitializeCriticalSection(&ret->section);
1154 ret->section.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": Binding.section");
1156 hres = get_callback(pbc, &ret->callback);
1157 if(FAILED(hres)) {
1158 WARN("Could not get IBindStatusCallback\n");
1159 IBinding_Release(BINDING(ret));
1160 return hres;
1163 IBindStatusCallback_QueryInterface(ret->callback, &IID_IServiceProvider,
1164 (void**)&ret->service_provider);
1166 hres = get_protocol(ret, url);
1167 if(FAILED(hres)) {
1168 WARN("Could not get protocol handler\n");
1169 IBinding_Release(BINDING(ret));
1170 return hres;
1173 hres = IBindStatusCallback_GetBindInfo(ret->callback, &ret->bindf, &ret->bindinfo);
1174 if(FAILED(hres)) {
1175 WARN("GetBindInfo failed: %08x\n", hres);
1176 IBinding_Release(BINDING(ret));
1177 return hres;
1180 dump_BINDINFO(&ret->bindinfo);
1182 ret->bindf |= BINDF_FROMURLMON;
1184 if(!is_urlmon_protocol(url))
1185 ret->bindf |= BINDF_NEEDFILE;
1187 len = strlenW(url)+1;
1188 ret->url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
1189 memcpy(ret->url, url, len*sizeof(WCHAR));
1191 ret->stream = create_stream(ret->protocol);
1192 ret->stgmed.tymed = TYMED_ISTREAM;
1193 ret->stgmed.u.pstm = STREAM(ret->stream);
1194 ret->stgmed.pUnkForRelease = (IUnknown*)BINDING(ret); /* NOTE: Windows uses other IUnknown */
1196 *binding = ret;
1197 return S_OK;
1200 HRESULT start_binding(LPCWSTR url, IBindCtx *pbc, REFIID riid, void **ppv)
1202 Binding *binding = NULL;
1203 HRESULT hres;
1204 MSG msg;
1206 *ppv = NULL;
1208 hres = Binding_Create(url, pbc, riid, &binding);
1209 if(FAILED(hres))
1210 return hres;
1212 hres = IBindStatusCallback_OnStartBinding(binding->callback, 0, BINDING(binding));
1213 if(FAILED(hres)) {
1214 WARN("OnStartBinding failed: %08x\n", hres);
1215 IBindStatusCallback_OnStopBinding(binding->callback, 0x800c0008, NULL);
1216 IBinding_Release(BINDING(binding));
1217 return hres;
1220 hres = IInternetProtocol_Start(binding->protocol, url, PROTSINK(binding),
1221 BINDINF(binding), 0, 0);
1223 if(FAILED(hres)) {
1224 WARN("Start failed: %08x\n", hres);
1226 IInternetProtocol_Terminate(binding->protocol, 0);
1227 IBindStatusCallback_OnStopBinding(binding->callback, S_OK, NULL);
1228 IBinding_Release(BINDING(binding));
1230 return hres;
1233 while(!(binding->bindf & BINDF_ASYNCHRONOUS) &&
1234 binding->download_state != END_DOWNLOAD) {
1235 MsgWaitForMultipleObjects(0, NULL, FALSE, 5000, QS_POSTMESSAGE);
1236 while (PeekMessageW(&msg, binding->notif_hwnd, WM_USER, WM_USER+117, PM_REMOVE|PM_NOYIELD)) {
1237 TranslateMessage(&msg);
1238 DispatchMessageW(&msg);
1242 if(binding->stream->init_buf) {
1243 if(binding->request_locked)
1244 IInternetProtocol_UnlockRequest(binding->protocol);
1246 IStream_AddRef(STREAM(binding->stream));
1247 *ppv = binding->stream;
1249 hres = S_OK;
1250 }else {
1251 hres = MK_S_ASYNCHRONOUS;
1254 IBinding_Release(BINDING(binding));
1256 return hres;