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