Update the address of the Free Software Foundation.
[wine] / dlls / urlmon / binding.c
1 /*
2  * Copyright 2005 Jacek Caban
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "ole2.h"
29 #include "urlmon.h"
30 #include "urlmon_main.h"
31
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
36
37 typedef struct ProtocolStream ProtocolStream;
38
39 typedef struct {
40     const IBindingVtbl               *lpBindingVtbl;
41     const IInternetProtocolSinkVtbl  *lpInternetProtocolSinkVtbl;
42     const IInternetBindInfoVtbl      *lpInternetBindInfoVtbl;
43     const IServiceProviderVtbl       *lpServiceProviderVtbl;
44
45     LONG ref;
46
47     IBindStatusCallback *callback;
48     IInternetProtocol *protocol;
49     IServiceProvider *service_provider;
50     ProtocolStream *stream;
51
52     BINDINFO bindinfo;
53     DWORD bindf;
54     LPWSTR mime;
55     LPWSTR url;
56 } Binding;
57
58 struct ProtocolStream {
59     const IStreamVtbl *lpStreamVtbl;
60
61     LONG ref;
62
63     IInternetProtocol *protocol;
64
65     BYTE buf[1024*8];
66     DWORD buf_size;
67 };
68
69 #define BINDING(x)   ((IBinding*)               &(x)->lpBindingVtbl)
70 #define PROTSINK(x)  ((IInternetProtocolSink*)  &(x)->lpInternetProtocolSinkVtbl)
71 #define BINDINF(x)   ((IInternetBindInfo*)      &(x)->lpInternetBindInfoVtbl)
72 #define SERVPROV(x)  ((IServiceProvider*)       &(x)->lpServiceProviderVtbl)
73
74 #define STREAM(x) ((IStream*) &(x)->lpStreamVtbl)
75
76 static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface,
77                                                    REFIID riid, void **ppv)
78 {
79     *ppv = NULL;
80
81     if(IsEqualGUID(&IID_IUnknown, riid)) {
82         TRACE("(IID_IUnknown %p)\n", ppv);
83         *ppv = iface;
84     }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
85         TRACE("(IID_IHttpNegotiate %p)\n", ppv);
86         *ppv = iface;
87     }else if(IsEqualGUID(&IID_IHttpNegotiate2, riid)) {
88         TRACE("(IID_IHttpNegotiate2 %p)\n", ppv);
89         *ppv = iface;
90     }
91
92     if(*ppv) {
93         IHttpNegotiate2_AddRef(iface);
94         return S_OK;
95     }
96
97     WARN("Unsupported interface %s\n", debugstr_guid(riid));
98     return E_NOINTERFACE;
99 }
100
101 static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface)
102 {
103     URLMON_LockModule();
104     return 2;
105 }
106
107 static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface)
108 {
109     URLMON_UnlockModule();
110     return 1;
111 }
112
113 static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface,
114         LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
115 {
116     FIXME("(%s %s %ld %p)\n", debugstr_w(szURL), debugstr_w(szHeaders), dwReserved,
117           pszAdditionalHeaders);
118     return E_NOTIMPL;
119 }
120
121 static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode,
122         LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders,
123         LPWSTR *pszAdditionalRequestHeaders)
124 {
125     FIXME("(%ld %s %s %p)\n", dwResponseCode, debugstr_w(szResponseHeaders),
126           debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
127     return E_NOTIMPL;
128 }
129
130 static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface,
131         BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
132 {
133     FIXME("(%p %p %ld)\n", pbSecurityId, pcbSecurityId, dwReserved);
134     return E_NOTIMPL;
135 }
136
137 static const IHttpNegotiate2Vtbl HttpNegotiate2Vtbl = {
138     HttpNegotiate_QueryInterface,
139     HttpNegotiate_AddRef,
140     HttpNegotiate_Release,
141     HttpNegotiate_BeginningTransaction,
142     HttpNegotiate_OnResponse,
143     HttpNegotiate_GetRootSecurityId
144 };
145
146 static IHttpNegotiate2 HttpNegotiate = { &HttpNegotiate2Vtbl };
147
148 #define STREAM_THIS(iface) DEFINE_THIS(ProtocolStream, Stream, iface)
149
150 static HRESULT WINAPI ProtocolStream_QueryInterface(IStream *iface,
151                                                           REFIID riid, void **ppv)
152 {
153     ProtocolStream *This = STREAM_THIS(iface);
154
155     *ppv = NULL;
156
157     if(IsEqualGUID(&IID_IUnknown, riid)) {
158         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
159         *ppv = STREAM(This);
160     }else if(IsEqualGUID(&IID_ISequentialStream, riid)) {
161         TRACE("(%p)->(IID_ISequentialStream %p)\n", This, ppv);
162         *ppv = STREAM(This);
163     }else if(IsEqualGUID(&IID_IStream, riid)) {
164         TRACE("(%p)->(IID_IStream %p)\n", This, ppv);
165         *ppv = STREAM(This);
166     }
167
168     if(*ppv) {
169         IStream_AddRef(STREAM(This));
170         return S_OK;
171     }
172
173     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
174     return E_NOINTERFACE;
175 }
176
177 static ULONG WINAPI ProtocolStream_AddRef(IStream *iface)
178 {
179     ProtocolStream *This = STREAM_THIS(iface);
180     LONG ref = InterlockedIncrement(&This->ref);
181
182     TRACE("(%p) ref=%ld\n", This, ref);
183
184     return ref;
185 }
186
187 static ULONG WINAPI ProtocolStream_Release(IStream *iface)
188 {
189     ProtocolStream *This = STREAM_THIS(iface);
190     LONG ref = InterlockedDecrement(&This->ref);
191
192     TRACE("(%p) ref=%ld\n", This, ref);
193
194     if(!ref) {
195         IInternetProtocol_Release(This->protocol);
196         HeapFree(GetProcessHeap(), 0, This);
197
198         URLMON_UnlockModule();
199     }
200
201     return ref;
202 }
203
204 static HRESULT WINAPI ProtocolStream_Read(IStream *iface, void *pv,
205                                          ULONG cb, ULONG *pcbRead)
206 {
207     ProtocolStream *This = STREAM_THIS(iface);
208     DWORD read = 0, pread = 0;
209
210     TRACE("(%p)->(%p %ld %p)\n", This, pv, cb, pcbRead);
211
212     if(This->buf_size) {
213         read = cb;
214
215         if(read > This->buf_size)
216             read = This->buf_size;
217
218         memcpy(pv, This->buf, read);
219
220         if(read < This->buf_size)
221             memmove(This->buf, This->buf+read, This->buf_size-read);
222         This->buf_size -= read;
223     }
224
225     if(read == cb) {
226         *pcbRead = read;
227         return S_OK;
228     }
229
230     IInternetProtocol_Read(This->protocol, (PBYTE)pv+read, cb-read, &pread);
231     *pcbRead = read + pread;
232
233     return read || pread ? S_OK : S_FALSE;
234 }
235
236 static HRESULT WINAPI ProtocolStream_Write(IStream *iface, const void *pv,
237                                           ULONG cb, ULONG *pcbWritten)
238 {
239     ProtocolStream *This = STREAM_THIS(iface);
240
241     TRACE("(%p)->(%p %ld %p)\n", This, pv, cb, pcbWritten);
242
243     return STG_E_ACCESSDENIED;
244 }
245
246 static HRESULT WINAPI ProtocolStream_Seek(IStream *iface, LARGE_INTEGER dlibMove,
247                                          DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
248 {
249     ProtocolStream *This = STREAM_THIS(iface);
250     FIXME("(%p)->(%ld %08lx %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
251     return E_NOTIMPL;
252 }
253
254 static HRESULT WINAPI ProtocolStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
255 {
256     ProtocolStream *This = STREAM_THIS(iface);
257     FIXME("(%p)->(%ld)\n", This, libNewSize.u.LowPart);
258     return E_NOTIMPL;
259 }
260
261 static HRESULT WINAPI ProtocolStream_CopyTo(IStream *iface, IStream *pstm,
262         ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
263 {
264     ProtocolStream *This = STREAM_THIS(iface);
265     FIXME("(%p)->(%p %ld %p %p)\n", This, pstm, cb.u.LowPart, pcbRead, pcbWritten);
266     return E_NOTIMPL;
267 }
268
269 static HRESULT WINAPI ProtocolStream_Commit(IStream *iface, DWORD grfCommitFlags)
270 {
271     ProtocolStream *This = STREAM_THIS(iface);
272
273     TRACE("(%p)->(%08lx)\n", This, grfCommitFlags);
274
275     return E_NOTIMPL;
276 }
277
278 static HRESULT WINAPI ProtocolStream_Revert(IStream *iface)
279 {
280     ProtocolStream *This = STREAM_THIS(iface);
281
282     TRACE("(%p)\n", This);
283
284     return E_NOTIMPL;
285 }
286
287 static HRESULT WINAPI ProtocolStream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
288                                                ULARGE_INTEGER cb, DWORD dwLockType)
289 {
290     ProtocolStream *This = STREAM_THIS(iface);
291     FIXME("(%p)->(%ld %ld %ld)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
292     return E_NOTIMPL;
293 }
294
295 static HRESULT WINAPI ProtocolStream_UnlockRegion(IStream *iface,
296         ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
297 {
298     ProtocolStream *This = STREAM_THIS(iface);
299     FIXME("(%p)->(%ld %ld %ld)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType);
300     return E_NOTIMPL;
301 }
302
303 static HRESULT WINAPI ProtocolStream_Stat(IStream *iface, STATSTG *pstatstg,
304                                          DWORD dwStatFlag)
305 {
306     ProtocolStream *This = STREAM_THIS(iface);
307     FIXME("(%p)->(%p %08lx)\n", This, pstatstg, dwStatFlag);
308     return E_NOTIMPL;
309 }
310
311 static HRESULT WINAPI ProtocolStream_Clone(IStream *iface, IStream **ppstm)
312 {
313     ProtocolStream *This = STREAM_THIS(iface);
314     FIXME("(%p)->(%p)\n", This, ppstm);
315     return E_NOTIMPL;
316 }
317
318 #undef STREAM_THIS
319
320 static const IStreamVtbl ProtocolStreamVtbl = {
321     ProtocolStream_QueryInterface,
322     ProtocolStream_AddRef,
323     ProtocolStream_Release,
324     ProtocolStream_Read,
325     ProtocolStream_Write,
326     ProtocolStream_Seek,
327     ProtocolStream_SetSize,
328     ProtocolStream_CopyTo,
329     ProtocolStream_Commit,
330     ProtocolStream_Revert,
331     ProtocolStream_LockRegion,
332     ProtocolStream_UnlockRegion,
333     ProtocolStream_Stat,
334     ProtocolStream_Clone
335 };
336
337 #define BINDING_THIS(iface) DEFINE_THIS(Binding, Binding, iface)
338
339 static ProtocolStream *create_stream(IInternetProtocol *protocol)
340 {
341     ProtocolStream *ret = HeapAlloc(GetProcessHeap(), 0, sizeof(ProtocolStream));
342
343     ret->lpStreamVtbl = &ProtocolStreamVtbl;
344     ret->ref = 1;
345     ret->buf_size = 0;
346
347     IInternetProtocol_AddRef(protocol);
348     ret->protocol = protocol;
349
350     URLMON_LockModule();
351
352     return ret;
353 }
354
355 static void fill_stream_buffer(ProtocolStream *This)
356 {
357     DWORD read = 0;
358
359     IInternetProtocol_Read(This->protocol, This->buf+This->buf_size,
360                            sizeof(This->buf)-This->buf_size, &read);
361     This->buf_size += read;
362 }
363
364 static HRESULT WINAPI Binding_QueryInterface(IBinding *iface, REFIID riid, void **ppv)
365 {
366     Binding *This = BINDING_THIS(iface);
367
368     *ppv = NULL;
369
370     if(IsEqualGUID(&IID_IUnknown, riid)) {
371         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
372         *ppv = BINDING(This);
373     }else if(IsEqualGUID(&IID_IBinding, riid)) {
374         TRACE("(%p)->(IID_IBinding %p)\n", This, ppv);
375         *ppv = BINDING(This);
376     }else if(IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
377         TRACE("(%p)->(IID_IInternetProtocolSink %p)\n", This, ppv);
378         *ppv = PROTSINK(This);
379     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
380         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
381         *ppv = BINDINF(This);
382     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
383         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
384         *ppv = SERVPROV(This);
385     }
386
387     if(*ppv) {
388         IBinding_AddRef(BINDING(This));
389         return S_OK;
390     }
391
392     WARN("Unsupported interface %s\n", debugstr_guid(riid));
393     return E_NOINTERFACE;
394 }
395
396 static ULONG WINAPI Binding_AddRef(IBinding *iface)
397 {
398     Binding *This = BINDING_THIS(iface);
399     LONG ref = InterlockedIncrement(&This->ref);
400
401     TRACE("(%p) ref=%ld\n", This, ref);
402
403     return ref;
404 }
405
406 static ULONG WINAPI Binding_Release(IBinding *iface)
407 {
408     Binding *This = BINDING_THIS(iface);
409     LONG ref = InterlockedDecrement(&This->ref);
410
411     TRACE("(%p) ref=%ld\n", This, ref);
412
413     if(!ref) {
414         if(This->callback)
415             IBindStatusCallback_Release(This->callback);
416         if(This->protocol)
417             IInternetProtocol_Release(This->protocol);
418         if(This->service_provider)
419             IServiceProvider_Release(This->service_provider);
420         if(This->stream)
421             IStream_Release(STREAM(This->stream));
422
423         ReleaseBindInfo(&This->bindinfo);
424         HeapFree(GetProcessHeap(), 0, This->mime);
425         HeapFree(GetProcessHeap(), 0, This->url);
426
427         HeapFree(GetProcessHeap(), 0, This);
428
429         URLMON_UnlockModule();
430     }
431
432     return ref;
433 }
434
435 static HRESULT WINAPI Binding_Abort(IBinding *iface)
436 {
437     Binding *This = BINDING_THIS(iface);
438     FIXME("(%p)\n", This);
439     return E_NOTIMPL;
440 }
441
442 static HRESULT WINAPI Binding_Suspend(IBinding *iface)
443 {
444     Binding *This = BINDING_THIS(iface);
445     FIXME("(%p)\n", This);
446     return E_NOTIMPL;
447 }
448
449 static HRESULT WINAPI Binding_Resume(IBinding *iface)
450 {
451     Binding *This = BINDING_THIS(iface);
452     FIXME("(%p)\n", This);
453     return E_NOTIMPL;
454 }
455
456 static HRESULT WINAPI Binding_SetPriority(IBinding *iface, LONG nPriority)
457 {
458     Binding *This = BINDING_THIS(iface);
459     FIXME("(%p)->(%ld)\n", This, nPriority);
460     return E_NOTIMPL;
461 }
462
463 static HRESULT WINAPI Binding_GetPriority(IBinding *iface, LONG *pnPriority)
464 {
465     Binding *This = BINDING_THIS(iface);
466     FIXME("(%p)->(%p)\n", This, pnPriority);
467     return E_NOTIMPL;
468 }
469
470 static HRESULT WINAPI Binding_GetBindResult(IBinding *iface, CLSID *pclsidProtocol,
471         DWORD *pdwResult, LPOLESTR *pszResult, DWORD *pdwReserved)
472 {
473     Binding *This = BINDING_THIS(iface);
474     FIXME("(%p)->(%p %p %p %p)\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved);
475     return E_NOTIMPL;
476 }
477
478 #undef BINDING_THIS
479
480 static const IBindingVtbl BindingVtbl = {
481     Binding_QueryInterface,
482     Binding_AddRef,
483     Binding_Release,
484     Binding_Abort,
485     Binding_Suspend,
486     Binding_Resume,
487     Binding_SetPriority,
488     Binding_GetPriority,
489     Binding_GetBindResult
490 };
491
492 #define PROTSINK_THIS(iface) DEFINE_THIS(Binding, InternetProtocolSink, iface)
493
494 static HRESULT WINAPI InternetProtocolSink_QueryInterface(IInternetProtocolSink *iface,
495         REFIID riid, void **ppv)
496 {
497     Binding *This = PROTSINK_THIS(iface);
498     return IBinding_QueryInterface(BINDING(This), riid, ppv);
499 }
500
501 static ULONG WINAPI InternetProtocolSink_AddRef(IInternetProtocolSink *iface)
502 {
503     Binding *This = PROTSINK_THIS(iface);
504     return IBinding_AddRef(BINDING(This));
505 }
506
507 static ULONG WINAPI InternetProtocolSink_Release(IInternetProtocolSink *iface)
508 {
509     Binding *This = PROTSINK_THIS(iface);
510     return IBinding_Release(BINDING(This));
511 }
512
513 static HRESULT WINAPI InternetProtocolSink_Switch(IInternetProtocolSink *iface,
514         PROTOCOLDATA *pProtocolData)
515 {
516     Binding *This = PROTSINK_THIS(iface);
517     FIXME("(%p)->(%p)\n", This, pProtocolData);
518     return E_NOTIMPL;
519 }
520
521 static HRESULT WINAPI InternetProtocolSink_ReportProgress(IInternetProtocolSink *iface,
522         ULONG ulStatusCode, LPCWSTR szStatusText)
523 {
524     Binding *This = PROTSINK_THIS(iface);
525
526     TRACE("(%p)->(%lu %s)\n", This, ulStatusCode, debugstr_w(szStatusText));
527
528     switch(ulStatusCode) {
529     case BINDSTATUS_MIMETYPEAVAILABLE: {
530         int len = strlenW(szStatusText)+1;
531         This->mime = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
532         memcpy(This->mime, szStatusText, len*sizeof(WCHAR));
533         break;
534     }
535     case BINDSTATUS_SENDINGREQUEST:
536         IBindStatusCallback_OnProgress(This->callback, 0, 0, BINDSTATUS_SENDINGREQUEST,
537                                        szStatusText);
538         break;
539     case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE:
540         IBindStatusCallback_OnProgress(This->callback, 0, 0,
541                                        BINDSTATUS_MIMETYPEAVAILABLE, szStatusText);
542         break;
543     case BINDSTATUS_CACHEFILENAMEAVAILABLE:
544         break;
545     default:
546         FIXME("Unhandled status code %ld\n", ulStatusCode);
547         return E_NOTIMPL;
548     };
549
550     return S_OK;
551 }
552
553 static HRESULT WINAPI InternetProtocolSink_ReportData(IInternetProtocolSink *iface,
554         DWORD grfBSCF, ULONG ulProgress, ULONG ulProgressMax)
555 {
556     Binding *This = PROTSINK_THIS(iface);
557     STGMEDIUM stgmed;
558     FORMATETC formatetc;
559
560     TRACE("(%p)->(%ld %lu %lu)\n", This, grfBSCF, ulProgress, ulProgressMax);
561
562     if(grfBSCF & BSCF_FIRSTDATANOTIFICATION) {
563         if(This->mime)
564             IBindStatusCallback_OnProgress(This->callback, ulProgress, ulProgressMax,
565                                            BINDSTATUS_MIMETYPEAVAILABLE, This->mime);
566         IBindStatusCallback_OnProgress(This->callback, ulProgress, ulProgressMax,
567                                        BINDSTATUS_BEGINDOWNLOADDATA, This->url);
568     }
569
570     if(grfBSCF & BSCF_LASTDATANOTIFICATION)
571         IBindStatusCallback_OnProgress(This->callback, ulProgress, ulProgressMax,
572                                        BINDSTATUS_ENDDOWNLOADDATA, This->url);
573
574     if(grfBSCF & BSCF_FIRSTDATANOTIFICATION)
575         IInternetProtocol_LockRequest(This->protocol, 0);
576
577     fill_stream_buffer(This->stream);
578
579     stgmed.tymed = TYMED_ISTREAM;
580     stgmed.u.pstm = STREAM(This->stream);
581
582     formatetc.cfFormat = 0; /* FIXME */
583     formatetc.ptd = NULL;
584     formatetc.dwAspect = 1;
585     formatetc.lindex = -1;
586     formatetc.tymed = TYMED_ISTREAM;
587
588     IBindStatusCallback_OnDataAvailable(This->callback, grfBSCF, This->stream->buf_size,
589             &formatetc, &stgmed);
590
591     if(grfBSCF & BSCF_LASTDATANOTIFICATION)
592         IBindStatusCallback_OnStopBinding(This->callback, S_OK, NULL);
593
594     return S_OK;
595 }
596
597 static HRESULT WINAPI InternetProtocolSink_ReportResult(IInternetProtocolSink *iface,
598         HRESULT hrResult, DWORD dwError, LPCWSTR szResult)
599 {
600     Binding *This = PROTSINK_THIS(iface);
601     FIXME("(%p)->(%08lx %ld %s)\n", This, hrResult, dwError, debugstr_w(szResult));
602     return E_NOTIMPL;
603 }
604
605 #undef PROTSINK_THIS
606
607 static const IInternetProtocolSinkVtbl InternetProtocolSinkVtbl = {
608     InternetProtocolSink_QueryInterface,
609     InternetProtocolSink_AddRef,
610     InternetProtocolSink_Release,
611     InternetProtocolSink_Switch,
612     InternetProtocolSink_ReportProgress,
613     InternetProtocolSink_ReportData,
614     InternetProtocolSink_ReportResult
615 };
616
617 #define BINDINF_THIS(iface) DEFINE_THIS(Binding, InternetBindInfo, iface)
618
619 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
620         REFIID riid, void **ppv)
621 {
622     Binding *This = BINDINF_THIS(iface);
623     return IBinding_QueryInterface(BINDING(This), riid, ppv);
624 }
625
626 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
627 {
628     Binding *This = BINDINF_THIS(iface);
629     return IBinding_AddRef(BINDING(This));
630 }
631
632 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
633 {
634     Binding *This = BINDINF_THIS(iface);
635     return IBinding_Release(BINDING(This));
636 }
637
638 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
639         DWORD *grfBINDF, BINDINFO *pbindinfo)
640 {
641     Binding *This = BINDINF_THIS(iface);
642
643     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
644
645     *grfBINDF = This->bindf;
646
647     memcpy(pbindinfo, &This->bindinfo, sizeof(BINDINFO));
648
649     if(pbindinfo->szExtraInfo || pbindinfo->szCustomVerb)
650         FIXME("copy strings\n");
651
652     if(pbindinfo->pUnk)
653         IUnknown_AddRef(pbindinfo->pUnk);
654
655     return S_OK;
656 }
657
658 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
659         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
660 {
661     Binding *This = BINDINF_THIS(iface);
662
663     TRACE("(%p)->(%ld %p %ld %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
664
665     switch(ulStringType) {
666     case BINDSTRING_ACCEPT_MIMES: {
667         static const WCHAR wszMimes[] = {'*','/','*',0};
668
669         if(!ppwzStr || !pcElFetched)
670             return E_INVALIDARG;
671
672         ppwzStr[0] = CoTaskMemAlloc(sizeof(wszMimes));
673         memcpy(ppwzStr[0], wszMimes, sizeof(wszMimes));
674         *pcElFetched = 1;
675         return S_OK;
676     }
677     case BINDSTRING_USER_AGENT: {
678         IInternetBindInfo *bindinfo = NULL;
679         HRESULT hres;
680
681         hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetBindInfo,
682                                                   (void**)&bindinfo);
683         if(FAILED(hres))
684             return hres;
685
686         hres = IInternetBindInfo_GetBindString(bindinfo, ulStringType, ppwzStr,
687                                                cEl, pcElFetched);
688         IInternetBindInfo_Release(bindinfo);
689
690         return hres;
691     }
692     }
693
694     FIXME("not supported string type %ld\n", ulStringType);
695     return E_NOTIMPL;
696 }
697
698 #undef BINDF_THIS
699
700 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
701     InternetBindInfo_QueryInterface,
702     InternetBindInfo_AddRef,
703     InternetBindInfo_Release,
704     InternetBindInfo_GetBindInfo,
705     InternetBindInfo_GetBindString
706 };
707
708 #define SERVPROV_THIS(iface) DEFINE_THIS(Binding, ServiceProvider, iface)
709
710 static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface,
711         REFIID riid, void **ppv)
712 {
713     Binding *This = SERVPROV_THIS(iface);
714     return IBinding_QueryInterface(BINDING(This), riid, ppv);
715 }
716
717 static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface)
718 {
719     Binding *This = SERVPROV_THIS(iface);
720     return IBinding_AddRef(BINDING(This));
721 }
722
723 static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface)
724 {
725     Binding *This = SERVPROV_THIS(iface);
726     return IBinding_Release(BINDING(This));
727 }
728
729 static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface,
730         REFGUID guidService, REFIID riid, void **ppv)
731 {
732     Binding *This = SERVPROV_THIS(iface);
733     HRESULT hres;
734
735     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
736
737     if(This->service_provider) {
738         hres = IServiceProvider_QueryService(This->service_provider, guidService,
739                                              riid, ppv);
740         if(SUCCEEDED(hres))
741             return hres;
742     }
743
744     if(IsEqualGUID(&IID_IHttpNegotiate, guidService)
745        || IsEqualGUID(&IID_IHttpNegotiate2, guidService))
746         return IHttpNegotiate2_QueryInterface(&HttpNegotiate, riid, ppv);
747
748     WARN("unknown service %s\n", debugstr_guid(guidService));
749     return E_NOTIMPL;
750 }
751
752 #undef SERVPROV_THIS
753
754 static const IServiceProviderVtbl ServiceProviderVtbl = {
755     ServiceProvider_QueryInterface,
756     ServiceProvider_AddRef,
757     ServiceProvider_Release,
758     ServiceProvider_QueryService
759 };
760
761 static HRESULT get_callback(IBindCtx *pbc, IBindStatusCallback **callback)
762 {
763     HRESULT hres;
764
765     static WCHAR wszBSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
766
767     hres = IBindCtx_GetObjectParam(pbc, wszBSCBHolder, (IUnknown**)callback);
768     if(FAILED(hres))
769         return MK_E_SYNTAX;
770
771     return S_OK;
772 }
773
774 static HRESULT get_protocol(Binding *This, LPCWSTR url)
775 {
776     IUnknown *unk = NULL;
777     IClassFactory *cf = NULL;
778     HRESULT hres;
779
780     hres = IBindStatusCallback_QueryInterface(This->callback, &IID_IInternetProtocol,
781             (void**)&This->protocol);
782     if(SUCCEEDED(hres))
783         return S_OK;
784
785     if(This->service_provider) {
786         hres = IServiceProvider_QueryService(This->service_provider, &IID_IInternetProtocol,
787                 &IID_IInternetProtocol, (void**)&This->protocol);
788         if(SUCCEEDED(hres))
789             return S_OK;
790     }
791
792     hres = get_protocol_iface(url, &unk);
793     if(FAILED(hres))
794         return hres;
795
796     hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&cf);
797     IUnknown_Release(unk);
798     if(FAILED(hres))
799         return hres;
800
801     hres = IClassFactory_CreateInstance(cf, NULL, &IID_IInternetProtocol, (void**)&This->protocol);
802     IClassFactory_Release(cf);
803
804     return hres;
805 }
806
807 static HRESULT Binding_Create(LPCWSTR url, IBindCtx *pbc, REFIID riid, Binding **binding)
808 {
809     Binding *ret;
810     int len;
811     HRESULT hres;
812
813     static const WCHAR wszFile[] = {'f','i','l','e',':'};
814
815     if(!IsEqualGUID(&IID_IStream, riid)) {
816         FIXME("Unsupported riid %s\n", debugstr_guid(riid));
817         return E_NOTIMPL;
818     }
819
820     URLMON_LockModule();
821
822     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(Binding));
823
824     ret->lpBindingVtbl              = &BindingVtbl;
825     ret->lpInternetProtocolSinkVtbl = &InternetProtocolSinkVtbl;
826     ret->lpInternetBindInfoVtbl     = &InternetBindInfoVtbl;
827     ret->lpServiceProviderVtbl      = &ServiceProviderVtbl;
828
829     ret->ref = 1;
830
831     ret->callback = NULL;
832     ret->protocol = NULL;
833     ret->service_provider = NULL;
834     ret->stream = NULL;
835     ret->mime = NULL;
836     ret->url = NULL;
837
838     memset(&ret->bindinfo, 0, sizeof(BINDINFO));
839     ret->bindinfo.cbSize = sizeof(BINDINFO);
840     ret->bindf = 0;
841
842     hres = get_callback(pbc, &ret->callback);
843     if(FAILED(hres)) {
844         WARN("Could not get IBindStatusCallback\n");
845         IBinding_Release(BINDING(ret));
846         return hres;
847     }
848
849     IBindStatusCallback_QueryInterface(ret->callback, &IID_IServiceProvider,
850                                        (void**)&ret->service_provider);
851
852     hres = get_protocol(ret, url);
853     if(FAILED(hres)) {
854         WARN("Could not get protocol handler\n");
855         IBinding_Release(BINDING(ret));
856         return hres;
857     }
858
859     hres = IBindStatusCallback_GetBindInfo(ret->callback, &ret->bindf, &ret->bindinfo);
860     if(FAILED(hres)) {
861         WARN("GetBindInfo failed: %08lx\n", hres);
862         IBinding_Release(BINDING(ret));
863         return hres;
864     }
865
866     ret->bindf |= BINDF_FROMURLMON;
867
868     len = strlenW(url)+1;
869
870     if(len < sizeof(wszFile)/sizeof(WCHAR) || memcmp(wszFile, url, sizeof(wszFile)))
871         ret->bindf |= BINDF_NEEDFILE;
872
873     ret->url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
874     memcpy(ret->url, url, len*sizeof(WCHAR));
875
876     ret->stream = create_stream(ret->protocol);
877
878     *binding = ret;
879     return S_OK;
880 }
881
882 HRESULT start_binding(LPCWSTR url, IBindCtx *pbc, REFIID riid, void **ppv)
883 {
884     Binding *binding = NULL;
885     HRESULT hres;
886
887     *ppv = NULL;
888
889     hres = Binding_Create(url, pbc, riid, &binding);
890     if(FAILED(hres))
891         return hres;
892
893     hres = IBindStatusCallback_OnStartBinding(binding->callback, 0, BINDING(binding));
894     if(FAILED(hres)) {
895         WARN("OnStartBinding failed: %08lx\n", hres);
896         IBindStatusCallback_OnStopBinding(binding->callback, 0x800c0008, NULL);
897         IBinding_Release(BINDING(binding));
898         return hres;
899     }
900
901     hres = IInternetProtocol_Start(binding->protocol, url, PROTSINK(binding),
902              BINDINF(binding), 0, 0);
903     IInternetProtocol_Terminate(binding->protocol, 0);
904
905     if(SUCCEEDED(hres)) {
906         IInternetProtocol_UnlockRequest(binding->protocol);
907     }else {
908         WARN("Start failed: %08lx\n", hres);
909         IBindStatusCallback_OnStopBinding(binding->callback, S_OK, NULL);
910     }
911
912     IStream_AddRef(STREAM(binding->stream));
913     *ppv = binding->stream;
914
915     IBinding_Release(BINDING(binding));
916
917     return hres;
918 }