urlmon: Fix a reference count leak in RegisterBindStatusCallback.
[wine] / dlls / urlmon / bindctx.c
1 /*
2  * Copyright 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 #include <stdio.h>
21
22 #define COBJMACROS
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "objbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "urlmon.h"
32 #include "urlmon_main.h"
33
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
38
39 static WCHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
40
41 extern IID IID_IBindStatusCallbackHolder;
42
43 typedef struct {
44     const IBindStatusCallbackVtbl  *lpBindStatusCallbackVtbl;
45     const IServiceProviderVtbl     *lpServiceProviderVtbl;
46     const IHttpNegotiate2Vtbl      *lpHttpNegotiate2Vtbl;
47     const IAuthenticateVtbl        *lpAuthenticateVtbl;
48
49     LONG ref;
50
51     IBindStatusCallback *callback;
52     IServiceProvider *serv_prov;
53
54     IHttpNegotiate *http_negotiate;
55     BOOL init_http_negotiate;
56     IHttpNegotiate2 *http_negotiate2;
57     BOOL init_http_negotiate2;
58     IAuthenticate *authenticate;
59     BOOL init_authenticate;
60 } BindStatusCallback;
61
62 #define STATUSCLB(x)     ((IBindStatusCallback*)  &(x)->lpBindStatusCallbackVtbl)
63 #define SERVPROV(x)      ((IServiceProvider*)     &(x)->lpServiceProviderVtbl)
64 #define HTTPNEG2(x)      ((IHttpNegotiate2*)      &(x)->lpHttpNegotiate2Vtbl)
65 #define AUTHENTICATE(x)  ((IAuthenticate*)        &(x)->lpAuthenticateVtbl)
66
67 #define STATUSCLB_THIS(iface) DEFINE_THIS(BindStatusCallback, BindStatusCallback, iface)
68
69 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
70         REFIID riid, void **ppv)
71 {
72     BindStatusCallback *This = STATUSCLB_THIS(iface);
73
74     *ppv = NULL;
75
76     if(IsEqualGUID(&IID_IUnknown, riid)) {
77         TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv);
78         *ppv = STATUSCLB(This);
79     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
80         TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv);
81         *ppv = STATUSCLB(This);
82     }else if(IsEqualGUID(&IID_IBindStatusCallbackHolder, riid)) {
83         TRACE("(%p)->(IID_IBindStatusCallbackHolder, %p)\n", This, ppv);
84         *ppv = This;
85     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
86         TRACE("(%p)->(IID_IServiceProvider, %p)\n", This, ppv);
87         *ppv = SERVPROV(This);
88     }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
89         TRACE("(%p)->(IID_IHttpNegotiate, %p)\n", This, ppv);
90         *ppv = HTTPNEG2(This);
91     }else if(IsEqualGUID(&IID_IHttpNegotiate2, riid)) {
92         TRACE("(%p)->(IID_IHttpNegotiate2, %p)\n", This, ppv);
93         *ppv = HTTPNEG2(This);
94     }else if(IsEqualGUID(&IID_IAuthenticate, riid)) {
95         TRACE("(%p)->(IID_IAuthenticate, %p)\n", This, ppv);
96         *ppv = AUTHENTICATE(This);
97     }
98
99     if(*ppv) {
100         IBindStatusCallback_AddRef((IUnknown*)*ppv);
101         return S_OK;
102     }
103
104     TRACE("Unsupported riid = %s\n", debugstr_guid(riid));
105     return E_NOINTERFACE;
106 }
107
108 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
109 {
110     BindStatusCallback *This = STATUSCLB_THIS(iface);
111     LONG ref = InterlockedIncrement(&This->ref);
112
113     TRACE("(%p) ref = %d\n", This, ref);
114
115     return ref;
116 }
117
118 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
119 {
120     BindStatusCallback *This = STATUSCLB_THIS(iface);
121     LONG ref = InterlockedDecrement(&This->ref);
122
123     TRACE("(%p) ref = %d\n", This, ref);
124
125     if(!ref) {
126         if(This->serv_prov)
127             IServiceProvider_Release(This->serv_prov);
128         if(This->http_negotiate)
129             IHttpNegotiate_Release(This->http_negotiate);
130         if(This->http_negotiate2)
131             IHttpNegotiate2_Release(This->http_negotiate2);
132         if(This->authenticate)
133             IAuthenticate_Release(This->authenticate);
134         IBindStatusCallback_Release(This->callback);
135         HeapFree(GetProcessHeap(), 0, This);
136     }
137
138     return ref;
139 }
140
141 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
142         DWORD dwReserved, IBinding *pbind)
143 {
144     BindStatusCallback *This = STATUSCLB_THIS(iface);
145
146     TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind);
147
148     return IBindStatusCallback_OnStartBinding(This->callback, 0xff, pbind);
149 }
150
151 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
152 {
153     BindStatusCallback *This = STATUSCLB_THIS(iface);
154
155     TRACE("(%p)->(%p)\n", This, pnPriority);
156
157     return IBindStatusCallback_GetPriority(This->callback, pnPriority);
158 }
159
160 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
161 {
162     BindStatusCallback *This = STATUSCLB_THIS(iface);
163
164     TRACE("(%p)->(%d)\n", This, reserved);
165
166     return IBindStatusCallback_OnLowResource(This->callback, reserved);
167 }
168
169 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
170         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
171 {
172     BindStatusCallback *This = STATUSCLB_THIS(iface);
173
174     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
175             debugstr_w(szStatusText));
176
177     return IBindStatusCallback_OnProgress(This->callback, ulProgress,
178             ulProgressMax, ulStatusCode, szStatusText);
179 }
180
181 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
182         HRESULT hresult, LPCWSTR szError)
183 {
184     BindStatusCallback *This = STATUSCLB_THIS(iface);
185
186     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));
187
188     return IBindStatusCallback_OnStopBinding(This->callback, hresult, szError);
189 }
190
191 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
192         DWORD *grfBINDF, BINDINFO *pbindinfo)
193 {
194     BindStatusCallback *This = STATUSCLB_THIS(iface);
195
196     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
197
198     return IBindStatusCallback_GetBindInfo(This->callback, grfBINDF, pbindinfo);
199 }
200
201 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
202         DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
203 {
204     BindStatusCallback *This = STATUSCLB_THIS(iface);
205
206     TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed);
207
208     return IBindStatusCallback_OnDataAvailable(This->callback, grfBSCF, dwSize, pformatetc, pstgmed);
209 }
210
211 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
212         REFIID riid, IUnknown *punk)
213 {
214     BindStatusCallback *This = STATUSCLB_THIS(iface);
215
216     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk);
217
218     return IBindStatusCallback_OnObjectAvailable(This->callback, riid, punk);
219 }
220
221 #undef STATUSCLB_THIS
222
223 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
224     BindStatusCallback_QueryInterface,
225     BindStatusCallback_AddRef,
226     BindStatusCallback_Release,
227     BindStatusCallback_OnStartBinding,
228     BindStatusCallback_GetPriority,
229     BindStatusCallback_OnLowResource,
230     BindStatusCallback_OnProgress,
231     BindStatusCallback_OnStopBinding,
232     BindStatusCallback_GetBindInfo,
233     BindStatusCallback_OnDataAvailable,
234     BindStatusCallback_OnObjectAvailable
235 };
236
237 #define SERVPROV_THIS(iface) DEFINE_THIS(BindStatusCallback, ServiceProvider, iface)
238
239 static HRESULT WINAPI BSCServiceProvider_QueryInterface(IServiceProvider *iface,
240         REFIID riid, void **ppv)
241 {
242     BindStatusCallback *This = SERVPROV_THIS(iface);
243     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
244 }
245
246 static ULONG WINAPI BSCServiceProvider_AddRef(IServiceProvider *iface)
247 {
248     BindStatusCallback *This = SERVPROV_THIS(iface);
249     return IBindStatusCallback_AddRef(STATUSCLB(This));
250 }
251
252 static ULONG WINAPI BSCServiceProvider_Release(IServiceProvider *iface)
253 {
254     BindStatusCallback *This = SERVPROV_THIS(iface);
255     return IBindStatusCallback_Release(STATUSCLB(This));
256 }
257
258 static HRESULT WINAPI BSCServiceProvider_QueryService(IServiceProvider *iface,
259         REFGUID guidService, REFIID riid, void **ppv)
260 {
261     BindStatusCallback *This = SERVPROV_THIS(iface);
262     HRESULT hres;
263
264     if(IsEqualGUID(&IID_IHttpNegotiate, guidService)) {
265         TRACE("(%p)->(IID_IHttpNegotiate %s %p)\n", This, debugstr_guid(riid), ppv);
266
267         if(!This->init_http_negotiate) {
268             This->init_http_negotiate = TRUE;
269             hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IHttpNegotiate,
270                     (void**)&This->http_negotiate);
271             if(FAILED(hres) && This->serv_prov)
272                 IServiceProvider_QueryService(This->serv_prov, &IID_IHttpNegotiate,
273                         &IID_IHttpNegotiate, (void**)&This->http_negotiate);
274         }
275
276         return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
277     }
278
279     if(IsEqualGUID(&IID_IHttpNegotiate2, guidService)) {
280         TRACE("(%p)->(IID_IHttpNegotiate2 %s %p)\n", This, debugstr_guid(riid), ppv);
281
282         if(!This->init_http_negotiate2) {
283             This->init_http_negotiate2 = TRUE;
284             hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IHttpNegotiate2,
285                     (void**)&This->http_negotiate2);
286             if(FAILED(hres) && This->serv_prov)
287                 IServiceProvider_QueryService(This->serv_prov, &IID_IHttpNegotiate2,
288                         &IID_IHttpNegotiate2, (void**)&This->http_negotiate2);
289         }
290
291         return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
292     }
293
294     if(IsEqualGUID(&IID_IAuthenticate, guidService)) {
295         TRACE("(%p)->(IID_IAuthenticate %s %p)\n", This, debugstr_guid(riid), ppv);
296
297         if(!This->init_authenticate) {
298             This->init_authenticate = TRUE;
299             hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IAuthenticate,
300                     (void**)&This->authenticate);
301             if(FAILED(hres) && This->serv_prov)
302                 IServiceProvider_QueryService(This->serv_prov, &IID_IAuthenticate,
303                         &IID_IAuthenticate, (void**)&This->authenticate);
304         }
305
306         return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
307     }
308
309     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
310
311     hres = IBindStatusCallback_QueryInterface(This->callback, riid, ppv);
312     if(SUCCEEDED(hres))
313         return S_OK;
314
315     if(This->serv_prov) {
316         hres = IServiceProvider_QueryService(This->serv_prov, guidService, riid, ppv);
317         if(SUCCEEDED(hres))
318             return S_OK;
319     }
320
321     return E_NOINTERFACE;
322 }
323
324 #undef SERVPROV_THIS
325
326 static const IServiceProviderVtbl BSCServiceProviderVtbl = {
327     BSCServiceProvider_QueryInterface,
328     BSCServiceProvider_AddRef,
329     BSCServiceProvider_Release,
330     BSCServiceProvider_QueryService
331 };
332
333 #define HTTPNEG2_THIS(iface) DEFINE_THIS(BindStatusCallback, HttpNegotiate2, iface)
334
335 static HRESULT WINAPI BSCHttpNegotiate_QueryInterface(IHttpNegotiate2 *iface,
336         REFIID riid, void **ppv)
337 {
338     BindStatusCallback *This = HTTPNEG2_THIS(iface);
339     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
340 }
341
342 static ULONG WINAPI BSCHttpNegotiate_AddRef(IHttpNegotiate2 *iface)
343 {
344     BindStatusCallback *This = HTTPNEG2_THIS(iface);
345     return IBindStatusCallback_AddRef(STATUSCLB(This));
346 }
347
348 static ULONG WINAPI BSCHttpNegotiate_Release(IHttpNegotiate2 *iface)
349 {
350     BindStatusCallback *This = HTTPNEG2_THIS(iface);
351     return IBindStatusCallback_Release(STATUSCLB(This));
352 }
353
354 static HRESULT WINAPI BSCHttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface,
355         LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
356 {
357     BindStatusCallback *This = HTTPNEG2_THIS(iface);
358
359     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(szURL), debugstr_w(szHeaders), dwReserved,
360           pszAdditionalHeaders);
361
362     *pszAdditionalHeaders = NULL;
363
364     if(!This->http_negotiate)
365         return S_OK;
366
367     return IHttpNegotiate_BeginningTransaction(This->http_negotiate, szURL, szHeaders,
368                                                dwReserved, pszAdditionalHeaders);
369 }
370
371 static HRESULT WINAPI BSCHttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode,
372         LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders,
373         LPWSTR *pszAdditionalRequestHeaders)
374 {
375     BindStatusCallback *This = HTTPNEG2_THIS(iface);
376     LPWSTR additional_headers = NULL;
377     HRESULT hres = S_OK;
378
379     TRACE("(%p)->(%d %s %s %p)\n", This, dwResponseCode, debugstr_w(szResponseHeaders),
380           debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
381
382     if(This->http_negotiate)
383         hres = IHttpNegotiate_OnResponse(This->http_negotiate, dwResponseCode, szResponseHeaders,
384                                          szRequestHeaders, &additional_headers);
385
386     if(pszAdditionalRequestHeaders)
387         *pszAdditionalRequestHeaders = additional_headers;
388     else if(additional_headers)
389         CoTaskMemFree(additional_headers);
390
391     return hres;
392 }
393
394 static HRESULT WINAPI BSCHttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface,
395         BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
396 {
397     BindStatusCallback *This = HTTPNEG2_THIS(iface);
398
399     TRACE("(%p)->(%p %p %ld)\n", This, pbSecurityId, pcbSecurityId, dwReserved);
400
401     if(!This->http_negotiate2)
402         return E_FAIL;
403
404     return IHttpNegotiate2_GetRootSecurityId(This->http_negotiate2, pbSecurityId,
405                                              pcbSecurityId, dwReserved);
406 }
407
408 #undef HTTPNEG2_THIS
409
410 static const IHttpNegotiate2Vtbl BSCHttpNegotiateVtbl = {
411     BSCHttpNegotiate_QueryInterface,
412     BSCHttpNegotiate_AddRef,
413     BSCHttpNegotiate_Release,
414     BSCHttpNegotiate_BeginningTransaction,
415     BSCHttpNegotiate_OnResponse,
416     BSCHttpNegotiate_GetRootSecurityId
417 };
418
419 #define AUTHENTICATE_THIS(iface)  DEFINE_THIS(BindStatusCallback, Authenticate, iface)
420
421 static HRESULT WINAPI BSCAuthenticate_QueryInterface(IAuthenticate *iface, REFIID riid, void **ppv)
422 {
423     BindStatusCallback *This = AUTHENTICATE_THIS(iface);
424     return IBindStatusCallback_QueryInterface(AUTHENTICATE(This), riid, ppv);
425 }
426
427 static ULONG WINAPI BSCAuthenticate_AddRef(IAuthenticate *iface)
428 {
429     BindStatusCallback *This = AUTHENTICATE_THIS(iface);
430     return IBindStatusCallback_AddRef(STATUSCLB(This));
431 }
432
433 static ULONG WINAPI BSCAuthenticate_Release(IAuthenticate *iface)
434 {
435     BindStatusCallback *This = AUTHENTICATE_THIS(iface);
436     return IBindStatusCallback_Release(STATUSCLB(This));
437 }
438
439 static HRESULT WINAPI BSCAuthenticate_Authenticate(IAuthenticate *iface,
440         HWND *phwnd, LPWSTR *pszUsername, LPWSTR *pszPassword)
441 {
442     BindStatusCallback *This = AUTHENTICATE_THIS(iface);
443     FIXME("(%p)->(%p %p %p)\n", This, phwnd, pszUsername, pszPassword);
444     return E_NOTIMPL;
445 }
446
447 #undef AUTHENTICATE_THIS
448
449 static const IAuthenticateVtbl BSCAuthenticateVtbl = {
450     BSCAuthenticate_QueryInterface,
451     BSCAuthenticate_AddRef,
452     BSCAuthenticate_Release,
453     BSCAuthenticate_Authenticate
454 };
455
456 static IBindStatusCallback *create_bsc(IBindStatusCallback *bsc)
457 {
458     BindStatusCallback *ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BindStatusCallback));
459
460     ret->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl;
461     ret->lpServiceProviderVtbl    = &BSCServiceProviderVtbl;
462     ret->lpHttpNegotiate2Vtbl     = &BSCHttpNegotiateVtbl;
463     ret->lpAuthenticateVtbl       = &BSCAuthenticateVtbl;
464
465     ret->ref = 1;
466
467     IBindStatusCallback_AddRef(bsc);
468     ret->callback = bsc;
469
470     IBindStatusCallback_QueryInterface(bsc, &IID_IServiceProvider, (void**)&ret->serv_prov);
471
472     return STATUSCLB(ret);
473 }
474
475 /***********************************************************************
476  *           RegisterBindStatusCallback (urlmon.@)
477  *
478  * Register a bind status callback.
479  *
480  * PARAMS
481  *  pbc           [I] Binding context
482  *  pbsc          [I] Callback to register
483  *  ppbscPrevious [O] Destination for previous callback
484  *  dwReserved    [I] Reserved, must be 0.
485  *
486  * RETURNS
487  *    Success: S_OK.
488  *    Failure: E_INVALIDARG, if any argument is invalid, or
489  *             E_OUTOFMEMORY if memory allocation fails.
490  */
491 HRESULT WINAPI RegisterBindStatusCallback(IBindCtx *pbc, IBindStatusCallback *pbsc,
492         IBindStatusCallback **ppbscPrevious, DWORD dwReserved)
493 {
494     BindStatusCallback *holder;
495     IBindStatusCallback *bsc, *prev = NULL;
496     IUnknown *unk;
497     HRESULT hres;
498
499     TRACE("(%p %p %p %x)\n", pbc, pbsc, ppbscPrevious, dwReserved);
500
501     if (!pbc || !pbsc)
502         return E_INVALIDARG;
503
504     hres = IBindCtx_GetObjectParam(pbc, BSCBHolder, &unk);
505     if(SUCCEEDED(hres)) {
506         hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)&bsc);
507         if(SUCCEEDED(hres)) {
508             hres = IBindStatusCallback_QueryInterface(bsc, &IID_IBindStatusCallbackHolder, (void**)&holder);
509             if(SUCCEEDED(hres)) {
510                 prev = holder->callback;
511                 IBindStatusCallback_AddRef(prev);
512                 IBindStatusCallback_Release(bsc);
513                 IBindStatusCallback_Release(STATUSCLB(holder));
514             }else {
515                 prev = bsc;
516             }
517         }
518
519         IUnknown_Release(unk);
520         IBindCtx_RevokeObjectParam(pbc, BSCBHolder);
521     }
522
523     bsc = create_bsc(pbsc);
524     hres = IBindCtx_RegisterObjectParam(pbc, BSCBHolder, (IUnknown*)bsc);
525     IBindStatusCallback_Release(bsc);
526     if(FAILED(hres)) {
527         IBindStatusCallback_Release(prev);
528         return hres;
529     }
530
531     if(ppbscPrevious)
532         *ppbscPrevious = prev;
533     return S_OK;
534 }
535
536 /***********************************************************************
537  *           RevokeBindStatusCallback (URLMON.@)
538  *
539  * Unregister a bind status callback.
540  *
541  *  pbc           [I] Binding context
542  *  pbsc          [I] Callback to unregister
543  *
544  * RETURNS
545  *    Success: S_OK.
546  *    Failure: E_INVALIDARG, if any argument is invalid
547  */
548 HRESULT WINAPI RevokeBindStatusCallback(IBindCtx *pbc, IBindStatusCallback *pbsc)
549 {
550     BindStatusCallback *holder;
551     IBindStatusCallback *callback;
552     IUnknown *unk;
553     BOOL dorevoke = FALSE;
554     HRESULT hres;
555
556     TRACE("(%p %p)\n", pbc, pbsc);
557
558     if (!pbc || !pbsc)
559         return E_INVALIDARG;
560
561     hres = IBindCtx_GetObjectParam(pbc, BSCBHolder, &unk);
562     if(FAILED(hres))
563         return S_OK;
564
565     hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)&callback);
566     IUnknown_Release(unk);
567     if(FAILED(hres))
568         return S_OK;
569
570     hres = IBindStatusCallback_QueryInterface(callback, &IID_IBindStatusCallbackHolder, (void**)&holder);
571     if(SUCCEEDED(hres)) {
572         if(pbsc == holder->callback)
573             dorevoke = TRUE;
574         IBindStatusCallback_Release(STATUSCLB(holder));
575     }else if(pbsc == callback) {
576         dorevoke = TRUE;
577     }
578     IBindStatusCallback_Release(callback);
579
580     if(dorevoke)
581         IBindCtx_RevokeObjectParam(pbc, BSCBHolder);
582
583     return S_OK;
584 }
585
586 typedef struct {
587     const IBindCtxVtbl *lpBindCtxVtbl;
588
589     LONG ref;
590
591     IBindCtx *bindctx;
592 } AsyncBindCtx;
593
594 #define BINDCTX(x)  ((IBindCtx*)  &(x)->lpBindCtxVtbl)
595
596 #define BINDCTX_THIS(iface) DEFINE_THIS(AsyncBindCtx, BindCtx, iface)
597
598 static HRESULT WINAPI AsyncBindCtx_QueryInterface(IBindCtx *iface, REFIID riid, void **ppv)
599 {
600     AsyncBindCtx *This = BINDCTX_THIS(iface);
601
602     *ppv = NULL;
603
604     if(IsEqualGUID(riid, &IID_IUnknown)) {
605         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
606         *ppv = BINDCTX(This);
607     }else if(IsEqualGUID(riid, &IID_IBindCtx)) {
608         TRACE("(%p)->(IID_IBindCtx %p)\n", This, ppv);
609         *ppv = BINDCTX(This);
610     }else if(IsEqualGUID(riid, &IID_IAsyncBindCtx)) {
611         TRACE("(%p)->(IID_IAsyncBindCtx %p)\n", This, ppv);
612         *ppv = BINDCTX(This);
613     }
614
615     if(*ppv) {
616         IUnknown_AddRef((IUnknown*)*ppv);
617         return S_OK;
618     }
619
620     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
621     return E_NOINTERFACE;
622 }
623
624 static ULONG WINAPI AsyncBindCtx_AddRef(IBindCtx *iface)
625 {
626     AsyncBindCtx *This = BINDCTX_THIS(iface);
627     LONG ref = InterlockedIncrement(&This->ref);
628
629     TRACE("(%p) ref=%d\n", This, ref);
630
631     return ref;
632 }
633
634 static ULONG WINAPI AsyncBindCtx_Release(IBindCtx *iface)
635 {
636     AsyncBindCtx *This = BINDCTX_THIS(iface);
637     LONG ref = InterlockedDecrement(&This->ref);
638
639     TRACE("(%p) ref=%d\n", This, ref);
640
641     if(!ref) {
642         IBindCtx_Release(This->bindctx);
643         HeapFree(GetProcessHeap(), 0, This);
644     }
645
646     return ref;
647 }
648
649 static HRESULT WINAPI AsyncBindCtx_RegisterObjectBound(IBindCtx *iface, IUnknown *punk)
650 {
651     AsyncBindCtx *This = BINDCTX_THIS(iface);
652
653     TRACE("(%p)->(%p)\n", This, punk);
654
655     return IBindCtx_RegisterObjectBound(This->bindctx, punk);
656 }
657
658 static HRESULT WINAPI AsyncBindCtx_RevokeObjectBound(IBindCtx *iface, IUnknown *punk)
659 {
660     AsyncBindCtx *This = BINDCTX_THIS(iface);
661
662     TRACE("(%p %p)\n", This, punk);
663
664     return IBindCtx_RevokeObjectBound(This->bindctx, punk);
665 }
666
667 static HRESULT WINAPI AsyncBindCtx_ReleaseBoundObjects(IBindCtx *iface)
668 {
669     AsyncBindCtx *This = BINDCTX_THIS(iface);
670
671     TRACE("(%p)\n", This);
672
673     return IBindCtx_ReleaseBoundObjects(This->bindctx);
674 }
675
676 static HRESULT WINAPI AsyncBindCtx_SetBindOptions(IBindCtx *iface, BIND_OPTS *pbindopts)
677 {
678     AsyncBindCtx *This = BINDCTX_THIS(iface);
679
680     TRACE("(%p)->(%p)\n", This, pbindopts);
681
682     return IBindCtx_SetBindOptions(This->bindctx, pbindopts);
683 }
684
685 static HRESULT WINAPI AsyncBindCtx_GetBindOptions(IBindCtx *iface, BIND_OPTS *pbindopts)
686 {
687     AsyncBindCtx *This = BINDCTX_THIS(iface);
688
689     TRACE("(%p)->(%p)\n", This, pbindopts);
690
691     return IBindCtx_GetBindOptions(This->bindctx, pbindopts);
692 }
693
694 static HRESULT WINAPI AsyncBindCtx_GetRunningObjectTable(IBindCtx *iface, IRunningObjectTable **pprot)
695 {
696     AsyncBindCtx *This = BINDCTX_THIS(iface);
697
698     TRACE("(%p)->(%p)\n", This, pprot);
699
700     return IBindCtx_GetRunningObjectTable(This->bindctx, pprot);
701 }
702
703 static HRESULT WINAPI AsyncBindCtx_RegisterObjectParam(IBindCtx *iface, LPOLESTR pszkey, IUnknown *punk)
704 {
705     AsyncBindCtx *This = BINDCTX_THIS(iface);
706
707     TRACE("(%p)->(%s %p)\n", This, debugstr_w(pszkey), punk);
708
709     return IBindCtx_RegisterObjectParam(This->bindctx, pszkey, punk);
710 }
711
712 static HRESULT WINAPI AsyncBindCtx_GetObjectParam(IBindCtx* iface, LPOLESTR pszkey, IUnknown **punk)
713 {
714     AsyncBindCtx *This = BINDCTX_THIS(iface);
715
716     TRACE("(%p)->(%s %p)\n", This, debugstr_w(pszkey), punk);
717
718     return IBindCtx_GetObjectParam(This->bindctx, pszkey, punk);
719 }
720
721 static HRESULT WINAPI AsyncBindCtx_RevokeObjectParam(IBindCtx *iface, LPOLESTR ppenum)
722 {
723     AsyncBindCtx *This = BINDCTX_THIS(iface);
724
725     TRACE("(%p)->(%p)\n", This, ppenum);
726
727     return IBindCtx_RevokeObjectParam(This->bindctx, ppenum);
728 }
729
730 static HRESULT WINAPI AsyncBindCtx_EnumObjectParam(IBindCtx *iface, IEnumString **pszkey)
731 {
732     AsyncBindCtx *This = BINDCTX_THIS(iface);
733
734     TRACE("(%p)->(%p)\n", This, pszkey);
735
736     return IBindCtx_EnumObjectParam(This->bindctx, pszkey);
737 }
738
739 #undef BINDCTX_THIS
740
741 static const IBindCtxVtbl AsyncBindCtxVtbl =
742 {
743     AsyncBindCtx_QueryInterface,
744     AsyncBindCtx_AddRef,
745     AsyncBindCtx_Release,
746     AsyncBindCtx_RegisterObjectBound,
747     AsyncBindCtx_RevokeObjectBound,
748     AsyncBindCtx_ReleaseBoundObjects,
749     AsyncBindCtx_SetBindOptions,
750     AsyncBindCtx_GetBindOptions,
751     AsyncBindCtx_GetRunningObjectTable,
752     AsyncBindCtx_RegisterObjectParam,
753     AsyncBindCtx_GetObjectParam,
754     AsyncBindCtx_EnumObjectParam,
755     AsyncBindCtx_RevokeObjectParam
756 };
757
758 static HRESULT init_bindctx(IBindCtx *bindctx, DWORD options,
759        IBindStatusCallback *callback, IEnumFORMATETC *format)
760 {
761     BIND_OPTS bindopts;
762     HRESULT hres;
763
764     if(options)
765         FIXME("not supported options %08x\n", options);
766     if(format)
767         FIXME("format is not supported\n");
768
769     bindopts.cbStruct = sizeof(BIND_OPTS);
770     bindopts.grfFlags = BIND_MAYBOTHERUSER;
771     bindopts.grfMode = STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
772     bindopts.dwTickCountDeadline = 0;
773
774     hres = IBindCtx_SetBindOptions(bindctx, &bindopts);
775     if(FAILED(hres))
776        return hres;
777
778     if(callback) {
779         hres = RegisterBindStatusCallback(bindctx, callback, NULL, 0);
780         if(FAILED(hres))
781             return hres;
782     }
783
784     return S_OK;
785 }
786
787 /***********************************************************************
788  *           CreateAsyncBindCtx (urlmon.@)
789  */
790 HRESULT WINAPI CreateAsyncBindCtx(DWORD reserved, IBindStatusCallback *callback,
791         IEnumFORMATETC *format, IBindCtx **pbind)
792 {
793     IBindCtx *bindctx;
794     HRESULT hres;
795
796     TRACE("(%08x %p %p %p)\n", reserved, callback, format, pbind);
797
798     if(!pbind || !callback)
799         return E_INVALIDARG;
800
801     hres = CreateBindCtx(0, &bindctx);
802     if(FAILED(hres))
803         return hres;
804
805     hres = init_bindctx(bindctx, 0, callback, format);
806     if(FAILED(hres)) {
807         IBindCtx_Release(bindctx);
808         return hres;
809     }
810
811     *pbind = bindctx;
812     return S_OK;
813 }
814
815 /***********************************************************************
816  *           CreateAsyncBindCtxEx (urlmon.@)
817  *
818  * Create an asynchronous bind context.
819  */
820 HRESULT WINAPI CreateAsyncBindCtxEx(IBindCtx *ibind, DWORD options,
821         IBindStatusCallback *callback, IEnumFORMATETC *format, IBindCtx** pbind,
822         DWORD reserved)
823 {
824     AsyncBindCtx *ret;
825     IBindCtx *bindctx;
826     HRESULT hres;
827
828     TRACE("(%p %08x %p %p %p %d)\n", ibind, options, callback, format, pbind, reserved);
829
830     if(!pbind)
831         return E_INVALIDARG;
832
833     if(reserved)
834         WARN("reserved=%d\n", reserved);
835
836     hres = CreateBindCtx(0, &bindctx);
837     if(FAILED(hres))
838         return hres;
839
840     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(AsyncBindCtx));
841
842     ret->lpBindCtxVtbl = &AsyncBindCtxVtbl;
843     ret->ref = 1;
844     ret->bindctx = bindctx;
845
846     hres = init_bindctx(BINDCTX(ret), options, callback, format);
847     if(FAILED(hres)) {
848         IBindCtx_Release(BINDCTX(ret));
849         return hres;
850     }
851
852     *pbind = BINDCTX(ret);
853     return S_OK;
854 }