mshtml: Correctly handle NULL nschannel in channelbsc_load_stream (Coverity).
[wine] / dlls / mshtml / navigate.c
1 /*
2  * Copyright 2006-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 "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "hlguids.h"
32 #include "shlguid.h"
33
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 #include "mshtml_private.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
40
41 #define CONTENT_LENGTH "Content-Length"
42 #define UTF16_STR "utf-16"
43
44 static WCHAR emptyW[] = {0};
45
46 typedef struct {
47     const nsIInputStreamVtbl *lpInputStreamVtbl;
48
49     LONG ref;
50
51     char buf[1024];
52     DWORD buf_size;
53 } nsProtocolStream;
54
55 #define NSINSTREAM(x) ((nsIInputStream*) &(x)->lpInputStreamVtbl)
56
57 typedef struct {
58     void (*destroy)(BSCallback*);
59     HRESULT (*init_bindinfo)(BSCallback*);
60     HRESULT (*start_binding)(BSCallback*);
61     HRESULT (*stop_binding)(BSCallback*,HRESULT);
62     HRESULT (*read_data)(BSCallback*,IStream*);
63     HRESULT (*on_progress)(BSCallback*,ULONG,LPCWSTR);
64     HRESULT (*on_response)(BSCallback*,DWORD);
65 } BSCallbackVtbl;
66
67 struct BSCallback {
68     const IBindStatusCallbackVtbl *lpBindStatusCallbackVtbl;
69     const IServiceProviderVtbl    *lpServiceProviderVtbl;
70     const IHttpNegotiate2Vtbl     *lpHttpNegotiate2Vtbl;
71     const IInternetBindInfoVtbl   *lpInternetBindInfoVtbl;
72
73     const BSCallbackVtbl          *vtbl;
74
75     LONG ref;
76
77     LPWSTR headers;
78     HGLOBAL post_data;
79     ULONG post_data_len;
80     ULONG readed;
81     DWORD bindf;
82     BOOL bindinfo_ready;
83
84     IMoniker *mon;
85     IBinding *binding;
86
87     HTMLDocumentNode *doc;
88
89     struct list entry;
90 };
91
92 #define NSINSTREAM_THIS(iface) DEFINE_THIS(nsProtocolStream, InputStream, iface)
93
94 static nsresult NSAPI nsInputStream_QueryInterface(nsIInputStream *iface, nsIIDRef riid,
95                                                    nsQIResult result)
96 {
97     nsProtocolStream *This = NSINSTREAM_THIS(iface);
98
99     *result = NULL;
100
101     if(IsEqualGUID(&IID_nsISupports, riid)) {
102         TRACE("(%p)->(IID_nsISupports %p)\n", This, result);
103         *result  = NSINSTREAM(This);
104     }else if(IsEqualGUID(&IID_nsIInputStream, riid)) {
105         TRACE("(%p)->(IID_nsIInputStream %p)\n", This, result);
106         *result  = NSINSTREAM(This);
107     }
108
109     if(*result) {
110         nsIInputStream_AddRef(NSINSTREAM(This));
111         return NS_OK;
112     }
113
114     WARN("unsupported interface %s\n", debugstr_guid(riid));
115     return NS_NOINTERFACE;
116 }
117
118 static nsrefcnt NSAPI nsInputStream_AddRef(nsIInputStream *iface)
119 {
120     nsProtocolStream *This = NSINSTREAM_THIS(iface);
121     LONG ref = InterlockedIncrement(&This->ref);
122
123     TRACE("(%p) ref=%d\n", This, ref);
124
125     return ref;
126 }
127
128
129 static nsrefcnt NSAPI nsInputStream_Release(nsIInputStream *iface)
130 {
131     nsProtocolStream *This = NSINSTREAM_THIS(iface);
132     LONG ref = InterlockedDecrement(&This->ref);
133
134     TRACE("(%p) ref=%d\n", This, ref);
135
136     if(!ref)
137         heap_free(This);
138
139     return ref;
140 }
141
142 static nsresult NSAPI nsInputStream_Close(nsIInputStream *iface)
143 {
144     nsProtocolStream *This = NSINSTREAM_THIS(iface);
145     FIXME("(%p)\n", This);
146     return NS_ERROR_NOT_IMPLEMENTED;
147 }
148
149 static nsresult NSAPI nsInputStream_Available(nsIInputStream *iface, PRUint32 *_retval)
150 {
151     nsProtocolStream *This = NSINSTREAM_THIS(iface);
152     FIXME("(%p)->(%p)\n", This, _retval);
153     return NS_ERROR_NOT_IMPLEMENTED;
154 }
155
156 static nsresult NSAPI nsInputStream_Read(nsIInputStream *iface, char *aBuf, PRUint32 aCount,
157                                          PRUint32 *_retval)
158 {
159     nsProtocolStream *This = NSINSTREAM_THIS(iface);
160     DWORD read = aCount;
161
162     TRACE("(%p)->(%p %d %p)\n", This, aBuf, aCount, _retval);
163
164     if(read > This->buf_size)
165         read = This->buf_size;
166
167     if(read) {
168         memcpy(aBuf, This->buf, read);
169         if(read < This->buf_size)
170             memmove(This->buf, This->buf+read, This->buf_size-read);
171         This->buf_size -= read;
172     }
173
174     *_retval = read;
175     return NS_OK;
176 }
177
178 static nsresult NSAPI nsInputStream_ReadSegments(nsIInputStream *iface,
179         nsresult (WINAPI *aWriter)(nsIInputStream*,void*,const char*,PRUint32,PRUint32,PRUint32*),
180         void *aClousure, PRUint32 aCount, PRUint32 *_retval)
181 {
182     nsProtocolStream *This = NSINSTREAM_THIS(iface);
183     PRUint32 written = 0;
184     nsresult nsres;
185
186     TRACE("(%p)->(%p %p %d %p)\n", This, aWriter, aClousure, aCount, _retval);
187
188     if(!This->buf_size)
189         return S_OK;
190
191     if(aCount > This->buf_size)
192         aCount = This->buf_size;
193
194     nsres = aWriter(NSINSTREAM(This), aClousure, This->buf, 0, aCount, &written);
195     if(NS_FAILED(nsres))
196         TRACE("aWritter failed: %08x\n", nsres);
197     else if(written != This->buf_size)
198         FIXME("written %d != buf_size %d\n", written, This->buf_size);
199
200     This->buf_size -= written; 
201
202     *_retval = written;
203     return nsres;
204 }
205
206 static nsresult NSAPI nsInputStream_IsNonBlocking(nsIInputStream *iface, PRBool *_retval)
207 {
208     nsProtocolStream *This = NSINSTREAM_THIS(iface);
209     FIXME("(%p)->(%p)\n", This, _retval);
210     return NS_ERROR_NOT_IMPLEMENTED;
211 }
212
213 #undef NSINSTREAM_THIS
214
215 static const nsIInputStreamVtbl nsInputStreamVtbl = {
216     nsInputStream_QueryInterface,
217     nsInputStream_AddRef,
218     nsInputStream_Release,
219     nsInputStream_Close,
220     nsInputStream_Available,
221     nsInputStream_Read,
222     nsInputStream_ReadSegments,
223     nsInputStream_IsNonBlocking
224 };
225
226 static nsProtocolStream *create_nsprotocol_stream(void)
227 {
228     nsProtocolStream *ret = heap_alloc(sizeof(nsProtocolStream));
229
230     ret->lpInputStreamVtbl = &nsInputStreamVtbl;
231     ret->ref = 1;
232     ret->buf_size = 0;
233
234     return ret;
235 }
236
237 #define STATUSCLB_THIS(iface) DEFINE_THIS(BSCallback, BindStatusCallback, iface)
238
239 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
240         REFIID riid, void **ppv)
241 {
242     BSCallback *This = STATUSCLB_THIS(iface);
243
244     *ppv = NULL;
245     if(IsEqualGUID(&IID_IUnknown, riid)) {
246         TRACE("(%p)->(IID_IUnknown, %p)\n", This, ppv);
247         *ppv = STATUSCLB(This);
248     }else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) {
249         TRACE("(%p)->(IID_IBindStatusCallback, %p)\n", This, ppv);
250         *ppv = STATUSCLB(This);
251     }else if(IsEqualGUID(&IID_IServiceProvider, riid)) {
252         TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv);
253         *ppv = SERVPROV(This);
254     }else if(IsEqualGUID(&IID_IHttpNegotiate, riid)) {
255         TRACE("(%p)->(IID_IHttpNegotiate %p)\n", This, ppv);
256         *ppv = HTTPNEG(This);
257     }else if(IsEqualGUID(&IID_IHttpNegotiate2, riid)) {
258         TRACE("(%p)->(IID_IHttpNegotiate2 %p)\n", This, ppv);
259         *ppv = HTTPNEG(This);
260     }else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) {
261         TRACE("(%p)->(IID_IInternetBindInfo %p)\n", This, ppv);
262         *ppv = BINDINFO(This);
263     }
264
265     if(*ppv) {
266         IBindStatusCallback_AddRef(STATUSCLB(This));
267         return S_OK;
268     }
269
270     TRACE("Unsupported riid = %s\n", debugstr_guid(riid));
271     return E_NOINTERFACE;
272 }
273
274 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
275 {
276     BSCallback *This = STATUSCLB_THIS(iface);
277     LONG ref = InterlockedIncrement(&This->ref);
278
279     TRACE("(%p) ref = %d\n", This, ref);
280
281     return ref;
282 }
283
284 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
285 {
286     BSCallback *This = STATUSCLB_THIS(iface);
287     LONG ref = InterlockedDecrement(&This->ref);
288
289     TRACE("(%p) ref = %d\n", This, ref);
290
291     if(!ref) {
292         if(This->post_data)
293             GlobalFree(This->post_data);
294         if(This->mon)
295             IMoniker_Release(This->mon);
296         if(This->binding)
297             IBinding_Release(This->binding);
298         list_remove(&This->entry);
299         heap_free(This->headers);
300
301         This->vtbl->destroy(This);
302     }
303
304     return ref;
305 }
306
307 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
308         DWORD dwReserved, IBinding *pbind)
309 {
310     BSCallback *This = STATUSCLB_THIS(iface);
311
312     TRACE("(%p)->(%d %p)\n", This, dwReserved, pbind);
313
314     IBinding_AddRef(pbind);
315     This->binding = pbind;
316
317     if(This->doc)
318         list_add_head(&This->doc->bindings, &This->entry);
319
320     return This->vtbl->start_binding(This);
321 }
322
323 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
324 {
325     BSCallback *This = STATUSCLB_THIS(iface);
326     FIXME("(%p)->(%p)\n", This, pnPriority);
327     return E_NOTIMPL;
328 }
329
330 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
331 {
332     BSCallback *This = STATUSCLB_THIS(iface);
333     FIXME("(%p)->(%d)\n", This, reserved);
334     return E_NOTIMPL;
335 }
336
337 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
338         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
339 {
340     BSCallback *This = STATUSCLB_THIS(iface);
341
342     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
343             debugstr_w(szStatusText));
344
345     return This->vtbl->on_progress(This, ulStatusCode, szStatusText);
346 }
347
348 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
349         HRESULT hresult, LPCWSTR szError)
350 {
351     BSCallback *This = STATUSCLB_THIS(iface);
352     HRESULT hres;
353
354     TRACE("(%p)->(%08x %s)\n", This, hresult, debugstr_w(szError));
355
356     /* NOTE: IE7 calls GetBindResult here */
357
358     hres = This->vtbl->stop_binding(This, hresult);
359
360     if(This->binding) {
361         IBinding_Release(This->binding);
362         This->binding = NULL;
363     }
364
365     list_remove(&This->entry);
366     This->doc = NULL;
367
368     return hres;
369 }
370
371 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
372         DWORD *grfBINDF, BINDINFO *pbindinfo)
373 {
374     BSCallback *This = STATUSCLB_THIS(iface);
375     DWORD size;
376
377     TRACE("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
378
379     if(!This->bindinfo_ready) {
380         HRESULT hres;
381
382         hres = This->vtbl->init_bindinfo(This);
383         if(FAILED(hres))
384             return hres;
385
386         This->bindinfo_ready = TRUE;
387     }
388
389     *grfBINDF = This->bindf;
390
391     size = pbindinfo->cbSize;
392     memset(pbindinfo, 0, size);
393     pbindinfo->cbSize = size;
394
395     pbindinfo->cbstgmedData = This->post_data_len;
396     pbindinfo->dwCodePage = CP_UTF8;
397     pbindinfo->dwOptions = 0x80000;
398
399     if(This->post_data) {
400         pbindinfo->dwBindVerb = BINDVERB_POST;
401
402         pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
403         pbindinfo->stgmedData.u.hGlobal = This->post_data;
404         pbindinfo->stgmedData.pUnkForRelease = (IUnknown*)STATUSCLB(This);
405         IBindStatusCallback_AddRef(STATUSCLB(This));
406     }
407
408     return S_OK;
409 }
410
411 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
412         DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
413 {
414     BSCallback *This = STATUSCLB_THIS(iface);
415
416     TRACE("(%p)->(%08x %d %p %p)\n", This, grfBSCF, dwSize, pformatetc, pstgmed);
417
418     return This->vtbl->read_data(This, pstgmed->u.pstm);
419 }
420
421 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
422         REFIID riid, IUnknown *punk)
423 {
424     BSCallback *This = STATUSCLB_THIS(iface);
425     FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), punk);
426     return E_NOTIMPL;
427 }
428
429 #undef STATUSCLB_THIS
430
431 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
432     BindStatusCallback_QueryInterface,
433     BindStatusCallback_AddRef,
434     BindStatusCallback_Release,
435     BindStatusCallback_OnStartBinding,
436     BindStatusCallback_GetPriority,
437     BindStatusCallback_OnLowResource,
438     BindStatusCallback_OnProgress,
439     BindStatusCallback_OnStopBinding,
440     BindStatusCallback_GetBindInfo,
441     BindStatusCallback_OnDataAvailable,
442     BindStatusCallback_OnObjectAvailable
443 };
444
445 #define HTTPNEG_THIS(iface) DEFINE_THIS(BSCallback, HttpNegotiate2, iface)
446
447 static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface,
448                                                    REFIID riid, void **ppv)
449 {
450     BSCallback *This = HTTPNEG_THIS(iface);
451     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
452 }
453
454 static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface)
455 {
456     BSCallback *This = HTTPNEG_THIS(iface);
457     return IBindStatusCallback_AddRef(STATUSCLB(This));
458 }
459
460 static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface)
461 {
462     BSCallback *This = HTTPNEG_THIS(iface);
463     return IBindStatusCallback_Release(STATUSCLB(This));
464 }
465
466 static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface,
467         LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
468 {
469     BSCallback *This = HTTPNEG_THIS(iface);
470     DWORD size;
471
472     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(szURL), debugstr_w(szHeaders),
473           dwReserved, pszAdditionalHeaders);
474
475     if(!This->headers) {
476         *pszAdditionalHeaders = NULL;
477         return S_OK;
478     }
479
480     size = (strlenW(This->headers)+1)*sizeof(WCHAR);
481     *pszAdditionalHeaders = CoTaskMemAlloc(size);
482     memcpy(*pszAdditionalHeaders, This->headers, size);
483
484     return S_OK;
485 }
486
487 static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode,
488         LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
489 {
490     BSCallback *This = HTTPNEG_THIS(iface);
491
492     TRACE("(%p)->(%d %s %s %p)\n", This, dwResponseCode, debugstr_w(szResponseHeaders),
493           debugstr_w(szRequestHeaders), pszAdditionalRequestHeaders);
494
495     return This->vtbl->on_response(This, dwResponseCode);
496 }
497
498 static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface,
499         BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
500 {
501     BSCallback *This = HTTPNEG_THIS(iface);
502     FIXME("(%p)->(%p %p %ld)\n", This, pbSecurityId, pcbSecurityId, dwReserved);
503     return E_NOTIMPL;
504 }
505
506 #undef HTTPNEG
507
508 static const IHttpNegotiate2Vtbl HttpNegotiate2Vtbl = {
509     HttpNegotiate_QueryInterface,
510     HttpNegotiate_AddRef,
511     HttpNegotiate_Release,
512     HttpNegotiate_BeginningTransaction,
513     HttpNegotiate_OnResponse,
514     HttpNegotiate_GetRootSecurityId
515 };
516
517 #define BINDINFO_THIS(iface) DEFINE_THIS(BSCallback, InternetBindInfo, iface)
518
519 static HRESULT WINAPI InternetBindInfo_QueryInterface(IInternetBindInfo *iface,
520                                                       REFIID riid, void **ppv)
521 {
522     BSCallback *This = BINDINFO_THIS(iface);
523     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
524 }
525
526 static ULONG WINAPI InternetBindInfo_AddRef(IInternetBindInfo *iface)
527 {
528     BSCallback *This = BINDINFO_THIS(iface);
529     return IBindStatusCallback_AddRef(STATUSCLB(This));
530 }
531
532 static ULONG WINAPI InternetBindInfo_Release(IInternetBindInfo *iface)
533 {
534     BSCallback *This = BINDINFO_THIS(iface);
535     return IBindStatusCallback_Release(STATUSCLB(This));
536 }
537
538 static HRESULT WINAPI InternetBindInfo_GetBindInfo(IInternetBindInfo *iface,
539                                                    DWORD *grfBINDF, BINDINFO *pbindinfo)
540 {
541     BSCallback *This = BINDINFO_THIS(iface);
542     FIXME("(%p)->(%p %p)\n", This, grfBINDF, pbindinfo);
543     return E_NOTIMPL;
544 }
545
546 static HRESULT WINAPI InternetBindInfo_GetBindString(IInternetBindInfo *iface,
547         ULONG ulStringType, LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched)
548 {
549     BSCallback *This = BINDINFO_THIS(iface);
550     FIXME("(%p)->(%u %p %u %p)\n", This, ulStringType, ppwzStr, cEl, pcElFetched);
551     return E_NOTIMPL;
552 }
553
554 #undef BINDINFO_THIS
555
556 static const IInternetBindInfoVtbl InternetBindInfoVtbl = {
557     InternetBindInfo_QueryInterface,
558     InternetBindInfo_AddRef,
559     InternetBindInfo_Release,
560     InternetBindInfo_GetBindInfo,
561     InternetBindInfo_GetBindString
562 };
563
564 #define SERVPROV_THIS(iface) DEFINE_THIS(BSCallback, ServiceProvider, iface)
565
566 static HRESULT WINAPI BSCServiceProvider_QueryInterface(IServiceProvider *iface,
567                                                         REFIID riid, void **ppv)
568 {
569     BSCallback *This = SERVPROV_THIS(iface);
570     return IBindStatusCallback_QueryInterface(STATUSCLB(This), riid, ppv);
571 }
572
573 static ULONG WINAPI BSCServiceProvider_AddRef(IServiceProvider *iface)
574 {
575     BSCallback *This = SERVPROV_THIS(iface);
576     return IBindStatusCallback_AddRef(STATUSCLB(This));
577 }
578
579 static ULONG WINAPI BSCServiceProvider_Release(IServiceProvider *iface)
580 {
581     BSCallback *This = SERVPROV_THIS(iface);
582     return IBindStatusCallback_Release(STATUSCLB(This));
583 }
584
585 static HRESULT WINAPI BSCServiceProvider_QueryService(IServiceProvider *iface,
586         REFGUID guidService, REFIID riid, void **ppv)
587 {
588     BSCallback *This = SERVPROV_THIS(iface);
589     TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv);
590     return E_NOINTERFACE;
591 }
592
593 #undef SERVPROV_THIS
594
595 static const IServiceProviderVtbl ServiceProviderVtbl = {
596     BSCServiceProvider_QueryInterface,
597     BSCServiceProvider_AddRef,
598     BSCServiceProvider_Release,
599     BSCServiceProvider_QueryService
600 };
601
602 static void init_bscallback(BSCallback *This, const BSCallbackVtbl *vtbl, IMoniker *mon, DWORD bindf)
603 {
604     This->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl;
605     This->lpServiceProviderVtbl    = &ServiceProviderVtbl;
606     This->lpHttpNegotiate2Vtbl     = &HttpNegotiate2Vtbl;
607     This->lpInternetBindInfoVtbl   = &InternetBindInfoVtbl;
608     This->vtbl = vtbl;
609     This->ref = 1;
610     This->bindf = bindf;
611
612     list_init(&This->entry);
613
614     if(mon)
615         IMoniker_AddRef(mon);
616     This->mon = mon;
617 }
618
619 /* Calls undocumented 84 cmd of CGID_ShellDocView */
620 static void call_docview_84(HTMLDocumentObj *doc)
621 {
622     IOleCommandTarget *olecmd;
623     VARIANT var;
624     HRESULT hres;
625
626     if(!doc->client)
627         return;
628
629     hres = IOleClientSite_QueryInterface(doc->client, &IID_IOleCommandTarget, (void**)&olecmd);
630     if(FAILED(hres))
631         return;
632
633     VariantInit(&var);
634     hres = IOleCommandTarget_Exec(olecmd, &CGID_ShellDocView, 84, 0, NULL, &var);
635     IOleCommandTarget_Release(olecmd);
636     if(SUCCEEDED(hres) && V_VT(&var) != VT_NULL)
637         FIXME("handle result\n");
638 }
639
640 static void parse_post_data(nsIInputStream *post_data_stream, LPWSTR *headers_ret,
641                             HGLOBAL *post_data_ret, ULONG *post_data_len_ret)
642 {
643     PRUint32 post_data_len = 0, available = 0;
644     HGLOBAL post_data = NULL;
645     LPWSTR headers = NULL;
646     DWORD headers_len = 0, len;
647     const char *ptr, *ptr2, *post_data_end;
648
649     nsIInputStream_Available(post_data_stream, &available);
650     post_data = GlobalAlloc(0, available+1);
651     nsIInputStream_Read(post_data_stream, post_data, available, &post_data_len);
652     
653     TRACE("post_data = %s\n", debugstr_an(post_data, post_data_len));
654
655     ptr = ptr2 = post_data;
656     post_data_end = (const char*)post_data+post_data_len;
657
658     while(ptr < post_data_end && (*ptr != '\r' || ptr[1] != '\n')) {
659         while(ptr < post_data_end && (*ptr != '\r' || ptr[1] != '\n'))
660             ptr++;
661
662         if(!*ptr) {
663             FIXME("*ptr = 0\n");
664             return;
665         }
666
667         ptr += 2;
668
669         if(ptr-ptr2 >= sizeof(CONTENT_LENGTH)
670            && CompareStringA(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
671                              CONTENT_LENGTH, sizeof(CONTENT_LENGTH)-1,
672                              ptr2, sizeof(CONTENT_LENGTH)-1) == CSTR_EQUAL) {
673             ptr2 = ptr;
674             continue;
675         }
676
677         len = MultiByteToWideChar(CP_ACP, 0, ptr2, ptr-ptr2, NULL, 0);
678
679         if(headers)
680             headers = heap_realloc(headers,(headers_len+len+1)*sizeof(WCHAR));
681         else
682             headers = heap_alloc((len+1)*sizeof(WCHAR));
683
684         len = MultiByteToWideChar(CP_ACP, 0, ptr2, ptr-ptr2, headers+headers_len, len);
685         headers_len += len;
686
687         ptr2 = ptr;
688     }
689
690     headers[headers_len] = 0;
691     *headers_ret = headers;
692
693     if(ptr >= post_data_end-2) {
694         GlobalFree(post_data);
695         return;
696     }
697
698     ptr += 2;
699
700     if(headers_len) {
701         post_data_len -= ptr-(const char*)post_data;
702         memmove(post_data, ptr, post_data_len);
703         post_data = GlobalReAlloc(post_data, post_data_len+1, 0);
704     }
705
706     *post_data_ret = post_data;
707     *post_data_len_ret = post_data_len;
708 }
709
710 HRESULT start_binding(HTMLWindow *window, HTMLDocumentNode *doc, BSCallback *bscallback, IBindCtx *bctx)
711 {
712     IStream *str = NULL;
713     HRESULT hres;
714
715     bscallback->doc = doc;
716
717     /* NOTE: IE7 calls IsSystemMoniker here*/
718
719     if(window)
720         call_docview_84(window->doc_obj);
721
722     if(bctx) {
723         RegisterBindStatusCallback(bctx, STATUSCLB(bscallback), NULL, 0);
724         IBindCtx_AddRef(bctx);
725     }else {
726         hres = CreateAsyncBindCtx(0, STATUSCLB(bscallback), NULL, &bctx);
727         if(FAILED(hres)) {
728             WARN("CreateAsyncBindCtx failed: %08x\n", hres);
729             bscallback->vtbl->stop_binding(bscallback, hres);
730             return hres;
731         }
732     }
733
734     hres = IMoniker_BindToStorage(bscallback->mon, bctx, NULL, &IID_IStream, (void**)&str);
735     IBindCtx_Release(bctx);
736     if(FAILED(hres)) {
737         WARN("BindToStorage failed: %08x\n", hres);
738         bscallback->vtbl->stop_binding(bscallback, hres);
739         return hres;
740     }
741
742     if(str)
743         IStream_Release(str);
744
745     IMoniker_Release(bscallback->mon);
746     bscallback->mon = NULL;
747
748     return S_OK;
749 }
750
751 typedef struct {
752     BSCallback bsc;
753
754     DWORD size;
755     BYTE *buf;
756     HRESULT hres;
757 } BufferBSC;
758
759 #define BUFFERBSC_THIS(bsc) ((BufferBSC*) bsc)
760
761 static void BufferBSC_destroy(BSCallback *bsc)
762 {
763     BufferBSC *This = BUFFERBSC_THIS(bsc);
764
765     heap_free(This->buf);
766     heap_free(This);
767 }
768
769 static HRESULT BufferBSC_init_bindinfo(BSCallback *bsc)
770 {
771     return S_OK;
772 }
773
774 static HRESULT BufferBSC_start_binding(BSCallback *bsc)
775 {
776     return S_OK;
777 }
778
779 static HRESULT BufferBSC_stop_binding(BSCallback *bsc, HRESULT result)
780 {
781     BufferBSC *This = BUFFERBSC_THIS(bsc);
782
783     This->hres = result;
784
785     if(FAILED(result)) {
786         heap_free(This->buf);
787         This->buf = NULL;
788         This->size = 0;
789     }
790
791     return S_OK;
792 }
793
794 static HRESULT BufferBSC_read_data(BSCallback *bsc, IStream *stream)
795 {
796     BufferBSC *This = BUFFERBSC_THIS(bsc);
797     DWORD readed;
798     HRESULT hres;
799
800     if(!This->buf) {
801         This->size = 128;
802         This->buf = heap_alloc(This->size);
803     }
804
805     do {
806         if(This->bsc.readed == This->size) {
807             This->size <<= 1;
808             This->buf = heap_realloc(This->buf, This->size);
809         }
810
811         readed = 0;
812         hres = IStream_Read(stream, This->buf+This->bsc.readed, This->size-This->bsc.readed, &readed);
813         This->bsc.readed += readed;
814     }while(hres == S_OK);
815
816     return S_OK;
817 }
818
819 static HRESULT BufferBSC_on_progress(BSCallback *bsc, ULONG status_code, LPCWSTR status_text)
820 {
821     return S_OK;
822 }
823
824 static HRESULT BufferBSC_on_response(BSCallback *bsc, DWORD response_code)
825 {
826     return S_OK;
827 }
828
829 #undef BUFFERBSC_THIS
830
831 static const BSCallbackVtbl BufferBSCVtbl = {
832     BufferBSC_destroy,
833     BufferBSC_init_bindinfo,
834     BufferBSC_start_binding,
835     BufferBSC_stop_binding,
836     BufferBSC_read_data,
837     BufferBSC_on_progress,
838     BufferBSC_on_response
839 };
840
841
842 static BufferBSC *create_bufferbsc(IMoniker *mon)
843 {
844     BufferBSC *ret = heap_alloc_zero(sizeof(*ret));
845
846     init_bscallback(&ret->bsc, &BufferBSCVtbl, mon, 0);
847     ret->hres = E_FAIL;
848
849     return ret;
850 }
851
852 HRESULT bind_mon_to_buffer(HTMLDocumentNode *doc, IMoniker *mon, void **buf, DWORD *size)
853 {
854     BufferBSC *bsc = create_bufferbsc(mon);
855     HRESULT hres;
856
857     *buf = NULL;
858
859     hres = start_binding(NULL, doc, &bsc->bsc, NULL);
860     if(SUCCEEDED(hres)) {
861         hres = bsc->hres;
862         if(SUCCEEDED(hres)) {
863             *buf = bsc->buf;
864             bsc->buf = NULL;
865             *size = bsc->bsc.readed;
866             bsc->size = 0;
867         }
868     }
869
870     IBindStatusCallback_Release(STATUSCLB(&bsc->bsc));
871
872     return hres;
873 }
874
875 struct nsChannelBSC {
876     BSCallback bsc;
877
878     HTMLWindow *window;
879
880     nsChannel *nschannel;
881     nsIStreamListener *nslistener;
882     nsISupports *nscontext;
883
884     nsProtocolStream *nsstream;
885 };
886
887 static void on_start_nsrequest(nsChannelBSC *This)
888 {
889     nsresult nsres;
890
891     /* FIXME: it's needed for http connections from BindToObject. */
892     if(!This->nschannel->response_status)
893         This->nschannel->response_status = 200;
894
895     nsres = nsIStreamListener_OnStartRequest(This->nslistener,
896             (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext);
897     if(NS_FAILED(nsres))
898         FIXME("OnStartRequest failed: %08x\n", nsres);
899 }
900
901 static void on_stop_nsrequest(nsChannelBSC *This, HRESULT result)
902 {
903     nsresult nsres;
904
905     if(!This->nslistener)
906         return;
907
908     if(!This->bsc.readed && SUCCEEDED(result)) {
909         TRACE("No data read! Calling OnStartRequest\n");
910         on_start_nsrequest(This);
911     }
912
913     nsres = nsIStreamListener_OnStopRequest(This->nslistener, (nsIRequest*)NSCHANNEL(This->nschannel),
914              This->nscontext, SUCCEEDED(result) ? NS_OK : NS_ERROR_FAILURE);
915     if(NS_FAILED(nsres))
916         WARN("OnStopRequest failed: %08x\n", nsres);
917 }
918
919 static HRESULT read_stream_data(nsChannelBSC *This, IStream *stream)
920 {
921     DWORD read;
922     nsresult nsres;
923     HRESULT hres;
924
925     if(!This->nslistener) {
926         BYTE buf[1024];
927
928         do {
929             read = 0;
930             hres = IStream_Read(stream, buf, sizeof(buf), &read);
931         }while(hres == S_OK && read);
932
933         return S_OK;
934     }
935
936     if(!This->nsstream)
937         This->nsstream = create_nsprotocol_stream();
938
939     do {
940         read = 0;
941         hres = IStream_Read(stream, This->nsstream->buf+This->nsstream->buf_size,
942                 sizeof(This->nsstream->buf)-This->nsstream->buf_size, &read);
943         if(!read)
944             break;
945
946         This->nsstream->buf_size += read;
947
948         if(!This->bsc.readed) {
949             if(This->nsstream->buf_size >= 2
950                && (BYTE)This->nsstream->buf[0] == 0xff
951                && (BYTE)This->nsstream->buf[1] == 0xfe)
952                 This->nschannel->charset = heap_strdupA(UTF16_STR);
953
954             on_start_nsrequest(This);
955
956             if(This->window)
957                 update_window_doc(This->window);
958         }
959
960         This->bsc.readed += This->nsstream->buf_size;
961
962         nsres = nsIStreamListener_OnDataAvailable(This->nslistener,
963                 (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext,
964                 NSINSTREAM(This->nsstream), This->bsc.readed-This->nsstream->buf_size,
965                 This->nsstream->buf_size);
966         if(NS_FAILED(nsres))
967             ERR("OnDataAvailable failed: %08x\n", nsres);
968
969         if(This->nsstream->buf_size == sizeof(This->nsstream->buf)) {
970             ERR("buffer is full\n");
971             break;
972         }
973     }while(hres == S_OK);
974
975     return S_OK;
976 }
977
978 static void add_nsrequest(nsChannelBSC *This)
979 {
980     nsresult nsres;
981
982     if(!This->nschannel || !This->nschannel->load_group)
983         return;
984
985     nsres = nsILoadGroup_AddRequest(This->nschannel->load_group,
986             (nsIRequest*)NSCHANNEL(This->nschannel), This->nscontext);
987
988     if(NS_FAILED(nsres))
989         ERR("AddRequest failed:%08x\n", nsres);
990 }
991
992 #define NSCHANNELBSC_THIS(bsc) ((nsChannelBSC*) bsc)
993
994 static void nsChannelBSC_destroy(BSCallback *bsc)
995 {
996     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
997
998     if(This->nschannel)
999         nsIChannel_Release(NSCHANNEL(This->nschannel));
1000     if(This->nslistener)
1001         nsIStreamListener_Release(This->nslistener);
1002     if(This->nscontext)
1003         nsISupports_Release(This->nscontext);
1004     if(This->nsstream)
1005         nsIInputStream_Release(NSINSTREAM(This->nsstream));
1006     heap_free(This);
1007 }
1008
1009 static HRESULT nsChannelBSC_start_binding(BSCallback *bsc)
1010 {
1011     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1012
1013     add_nsrequest(This);
1014
1015     return S_OK;
1016 }
1017
1018 static HRESULT nsChannelBSC_init_bindinfo(BSCallback *bsc)
1019 {
1020     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1021
1022     if(This->nschannel && This->nschannel->post_data_stream) {
1023         parse_post_data(This->nschannel->post_data_stream, &This->bsc.headers, &This->bsc.post_data, &This->bsc.post_data_len);
1024         TRACE("headers = %s post_data = %s\n", debugstr_w(This->bsc.headers),
1025               debugstr_an(This->bsc.post_data, This->bsc.post_data_len));
1026     }
1027
1028     return S_OK;
1029 }
1030
1031 static HRESULT nsChannelBSC_stop_binding(BSCallback *bsc, HRESULT result)
1032 {
1033     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1034
1035     on_stop_nsrequest(This, result);
1036
1037     if(This->nslistener) {
1038         if(This->nschannel->load_group) {
1039             nsresult nsres;
1040
1041             nsres = nsILoadGroup_RemoveRequest(This->nschannel->load_group,
1042                     (nsIRequest*)NSCHANNEL(This->nschannel), NULL, NS_OK);
1043             if(NS_FAILED(nsres))
1044                 ERR("RemoveRequest failed: %08x\n", nsres);
1045         }
1046     }
1047
1048     return S_OK;
1049 }
1050
1051 static HRESULT nsChannelBSC_read_data(BSCallback *bsc, IStream *stream)
1052 {
1053     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1054
1055     return read_stream_data(This, stream);
1056 }
1057
1058 static HRESULT nsChannelBSC_on_progress(BSCallback *bsc, ULONG status_code, LPCWSTR status_text)
1059 {
1060     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1061
1062     switch(status_code) {
1063     case BINDSTATUS_MIMETYPEAVAILABLE:
1064         if(!This->nschannel)
1065             return S_OK;
1066
1067         heap_free(This->nschannel->content_type);
1068         This->nschannel->content_type = heap_strdupWtoA(status_text);
1069     }
1070
1071     return S_OK;
1072 }
1073
1074 static HRESULT nsChannelBSC_on_response(BSCallback *bsc, DWORD response_code)
1075 {
1076     nsChannelBSC *This = NSCHANNELBSC_THIS(bsc);
1077
1078     This->nschannel->response_status = response_code;
1079     return S_OK;
1080 }
1081
1082 #undef NSCHANNELBSC_THIS
1083
1084 static const BSCallbackVtbl nsChannelBSCVtbl = {
1085     nsChannelBSC_destroy,
1086     nsChannelBSC_init_bindinfo,
1087     nsChannelBSC_start_binding,
1088     nsChannelBSC_stop_binding,
1089     nsChannelBSC_read_data,
1090     nsChannelBSC_on_progress,
1091     nsChannelBSC_on_response
1092 };
1093
1094 nsChannelBSC *create_channelbsc(IMoniker *mon)
1095 {
1096     nsChannelBSC *ret = heap_alloc_zero(sizeof(*ret));
1097
1098     init_bscallback(&ret->bsc, &nsChannelBSCVtbl, mon, BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA);
1099
1100     return ret;
1101 }
1102
1103 IMoniker *get_channelbsc_mon(nsChannelBSC *This)
1104 {
1105     if(This->bsc.mon)
1106         IMoniker_AddRef(This->bsc.mon);
1107     return This->bsc.mon;
1108 }
1109
1110 void set_window_bscallback(HTMLWindow *window, nsChannelBSC *callback)
1111 {
1112     if(window->bscallback) {
1113         if(window->bscallback->bsc.binding)
1114             IBinding_Abort(window->bscallback->bsc.binding);
1115         window->bscallback->bsc.doc = NULL;
1116         window->bscallback->window = NULL;
1117         IBindStatusCallback_Release(STATUSCLB(&window->bscallback->bsc));
1118     }
1119
1120     window->bscallback = callback;
1121
1122     if(callback) {
1123         callback->window = window;
1124         IBindStatusCallback_AddRef(STATUSCLB(&callback->bsc));
1125         callback->bsc.doc = window->doc;
1126     }
1127 }
1128
1129 void abort_document_bindings(HTMLDocumentNode *doc)
1130 {
1131     BSCallback *iter;
1132
1133     LIST_FOR_EACH_ENTRY(iter, &doc->bindings, BSCallback, entry) {
1134         if(iter->binding)
1135             IBinding_Abort(iter->binding);
1136         iter->doc = NULL;
1137         list_remove(&iter->entry);
1138     }
1139 }
1140
1141 HRESULT channelbsc_load_stream(nsChannelBSC *bscallback, IStream *stream)
1142 {
1143     HRESULT hres;
1144
1145     if(!bscallback->nschannel) {
1146         ERR("NULL nschannel\n");
1147         return E_FAIL;
1148     }
1149
1150     bscallback->nschannel->content_type = heap_strdupA("text/html");
1151     if(!bscallback->nschannel->content_type)
1152         return E_OUTOFMEMORY;
1153
1154     add_nsrequest(bscallback);
1155
1156     hres = read_stream_data(bscallback, stream);
1157     IBindStatusCallback_OnStopBinding(STATUSCLB(&bscallback->bsc), hres, ERROR_SUCCESS);
1158
1159     return hres;
1160 }
1161
1162 void channelbsc_set_channel(nsChannelBSC *This, nsChannel *channel, nsIStreamListener *listener, nsISupports *context)
1163 {
1164     nsIChannel_AddRef(NSCHANNEL(channel));
1165     This->nschannel = channel;
1166
1167     nsIStreamListener_AddRef(listener);
1168     This->nslistener = listener;
1169
1170     if(context) {
1171         nsISupports_AddRef(context);
1172         This->nscontext = context;
1173     }
1174 }
1175
1176 HRESULT hlink_frame_navigate(HTMLDocument *doc, LPCWSTR url,
1177         nsIInputStream *post_data_stream, DWORD hlnf)
1178 {
1179     IHlinkFrame *hlink_frame;
1180     IServiceProvider *sp;
1181     BSCallback *callback;
1182     IBindCtx *bindctx;
1183     IMoniker *mon;
1184     IHlink *hlink;
1185     HRESULT hres;
1186
1187     hres = IOleClientSite_QueryInterface(doc->doc_obj->client, &IID_IServiceProvider,
1188             (void**)&sp);
1189     if(FAILED(hres))
1190         return hres;
1191
1192     hres = IServiceProvider_QueryService(sp, &IID_IHlinkFrame, &IID_IHlinkFrame,
1193             (void**)&hlink_frame);
1194     IServiceProvider_Release(sp);
1195     if(FAILED(hres))
1196         return hres;
1197
1198     callback = &create_channelbsc(NULL)->bsc;
1199
1200     if(post_data_stream) {
1201         parse_post_data(post_data_stream, &callback->headers, &callback->post_data,
1202                         &callback->post_data_len);
1203         TRACE("headers = %s post_data = %s\n", debugstr_w(callback->headers),
1204               debugstr_an(callback->post_data, callback->post_data_len));
1205     }
1206
1207     hres = CreateAsyncBindCtx(0, STATUSCLB(callback), NULL, &bindctx);
1208     if(SUCCEEDED(hres))
1209         hres = CoCreateInstance(&CLSID_StdHlink, NULL, CLSCTX_INPROC_SERVER,
1210                 &IID_IHlink, (LPVOID*)&hlink);
1211
1212     if(SUCCEEDED(hres))
1213         hres = CreateURLMoniker(NULL, url, &mon);
1214
1215     if(SUCCEEDED(hres)) {
1216         IHlink_SetMonikerReference(hlink, HLINKSETF_TARGET, mon, NULL);
1217
1218         if(hlnf & HLNF_OPENINNEWWINDOW) {
1219             static const WCHAR wszBlank[] = {'_','b','l','a','n','k',0};
1220             IHlink_SetTargetFrameName(hlink, wszBlank); /* FIXME */
1221         }
1222
1223         hres = IHlinkFrame_Navigate(hlink_frame, hlnf, bindctx, STATUSCLB(callback), hlink);
1224
1225         IMoniker_Release(mon);
1226     }
1227
1228     IHlinkFrame_Release(hlink_frame);
1229     IBindCtx_Release(bindctx);
1230     IBindStatusCallback_Release(STATUSCLB(callback));
1231     return hres;
1232 }
1233
1234 HRESULT navigate_url(HTMLDocumentNode *doc, OLECHAR *url)
1235 {
1236     OLECHAR *translated_url = NULL;
1237     HRESULT hres;
1238
1239     if(!url)
1240         url = emptyW;
1241
1242     if(doc->basedoc.doc_obj->hostui) {
1243         hres = IDocHostUIHandler_TranslateUrl(doc->basedoc.doc_obj->hostui, 0, url,
1244                 &translated_url);
1245         if(hres == S_OK)
1246             url = translated_url;
1247     }
1248
1249     hres = hlink_frame_navigate(&doc->basedoc, url, NULL, 0);
1250     if(FAILED(hres))
1251         FIXME("hlink_frame_navigate failed: %08x\n", hres);
1252
1253     CoTaskMemFree(translated_url);
1254     return hres;
1255 }