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