msxml3: Implement ::get_responseText() for UTF-8 and UTF-16 (little endian) response...
[wine] / dlls / msxml3 / httprequest.c
1 /*
2  *    IXMLHTTPRequest implementation
3  *
4  * Copyright 2008 Alistair Leslie-Hughes
5  * Copyright 2010 Nikolay Sivov for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23 #define NONAMELESSUNION
24
25 #include "config.h"
26
27 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "msxml6.h"
33
34 #include "msxml_private.h"
35
36 #include "wine/debug.h"
37 #include "wine/list.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
40
41 #ifdef HAVE_LIBXML2
42
43 #include <libxml/encoding.h>
44
45 static const WCHAR MethodGetW[] = {'G','E','T',0};
46 static const WCHAR MethodPutW[] = {'P','U','T',0};
47 static const WCHAR MethodPostW[] = {'P','O','S','T',0};
48
49 static const WCHAR colspaceW[] = {':',' ',0};
50 static const WCHAR crlfW[] = {'\r','\n',0};
51
52 typedef struct BindStatusCallback BindStatusCallback;
53
54 struct reqheader
55 {
56     struct list entry;
57     BSTR header;
58     BSTR value;
59 };
60
61 typedef struct
62 {
63     const struct IXMLHTTPRequestVtbl *lpVtbl;
64     LONG ref;
65
66     READYSTATE state;
67     IDispatch *sink;
68
69     /* request */
70     BINDVERB verb;
71     BSTR url;
72     BOOL async;
73     struct list reqheaders;
74     /* cached resulting custom request headers string length in WCHARs */
75     LONG reqheader_size;
76
77     /* credentials */
78     BSTR user;
79     BSTR password;
80
81     /* bind callback */
82     BindStatusCallback *bsc;
83     LONG status;
84 } httprequest;
85
86 static inline httprequest *impl_from_IXMLHTTPRequest( IXMLHTTPRequest *iface )
87 {
88     return (httprequest *)((char*)iface - FIELD_OFFSET(httprequest, lpVtbl));
89 }
90
91 static void httprequest_setreadystate(httprequest *This, READYSTATE state)
92 {
93     This->state = state;
94
95     if (This->sink)
96     {
97         DISPPARAMS params;
98
99         memset(&params, 0, sizeof(params));
100         IDispatch_Invoke(This->sink, 0, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, 0, 0, 0);
101     }
102 }
103
104 struct BindStatusCallback
105 {
106     const IBindStatusCallbackVtbl *lpBindStatusCallbackVtbl;
107     const IHttpNegotiateVtbl      *lpHttpNegotiateVtbl;
108     LONG ref;
109
110     IBinding *binding;
111     httprequest *request;
112
113     /* response data */
114     IStream *stream;
115 };
116
117 static inline BindStatusCallback *impl_from_IBindStatusCallback( IBindStatusCallback *iface )
118 {
119     return (BindStatusCallback *)((char*)iface - FIELD_OFFSET(BindStatusCallback, lpBindStatusCallbackVtbl));
120 }
121
122 static inline BindStatusCallback *impl_from_IHttpNegotiate( IHttpNegotiate *iface )
123 {
124     return (BindStatusCallback *)((char*)iface - FIELD_OFFSET(BindStatusCallback, lpHttpNegotiateVtbl));
125 }
126
127 void BindStatusCallback_Detach(BindStatusCallback *bsc)
128 {
129     if (bsc)
130     {
131         if (bsc->binding) IBinding_Abort(bsc->binding);
132         bsc->request = NULL;
133         IBindStatusCallback_Release((IBindStatusCallback*)bsc);
134     }
135 }
136
137 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
138         REFIID riid, void **ppv)
139 {
140     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
141
142     *ppv = NULL;
143
144     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
145
146     if (IsEqualGUID(&IID_IUnknown, riid) ||
147         IsEqualGUID(&IID_IBindStatusCallback, riid))
148     {
149         *ppv = &This->lpBindStatusCallbackVtbl;
150     }
151     else if (IsEqualGUID(&IID_IHttpNegotiate, riid))
152     {
153         *ppv = &This->lpHttpNegotiateVtbl;
154     }
155     else if (IsEqualGUID(&IID_IServiceProvider, riid) ||
156              IsEqualGUID(&IID_IBindStatusCallbackEx, riid) ||
157              IsEqualGUID(&IID_IInternetProtocol, riid) ||
158              IsEqualGUID(&IID_IHttpNegotiate2, riid))
159     {
160         return E_NOINTERFACE;
161     }
162
163     if (*ppv)
164     {
165         IBindStatusCallback_AddRef(iface);
166         return S_OK;
167     }
168
169     FIXME("Unsupported riid = %s\n", debugstr_guid(riid));
170
171     return E_NOINTERFACE;
172 }
173
174 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
175 {
176     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
177     LONG ref = InterlockedIncrement(&This->ref);
178
179     TRACE("(%p) ref = %d\n", This, ref);
180
181     return ref;
182 }
183
184 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
185 {
186     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
187     LONG ref = InterlockedDecrement(&This->ref);
188
189     TRACE("(%p) ref = %d\n", This, ref);
190
191     if (!ref)
192     {
193         if (This->binding) IBinding_Release(This->binding);
194         if (This->stream) IStream_Release(This->stream);
195         heap_free(This);
196     }
197
198     return ref;
199 }
200
201 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
202         DWORD reserved, IBinding *pbind)
203 {
204     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
205
206     TRACE("(%p)->(%d %p)\n", This, reserved, pbind);
207
208     if (!pbind) return E_INVALIDARG;
209
210     This->binding = pbind;
211     IBinding_AddRef(pbind);
212
213     httprequest_setreadystate(This->request, READYSTATE_LOADED);
214
215     return CreateStreamOnHGlobal(NULL, TRUE, &This->stream);
216 }
217
218 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pPriority)
219 {
220     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
221
222     TRACE("(%p)->(%p)\n", This, pPriority);
223
224     return E_NOTIMPL;
225 }
226
227 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
228 {
229     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
230
231     TRACE("(%p)->(%d)\n", This, reserved);
232
233     return E_NOTIMPL;
234 }
235
236 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
237         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
238 {
239     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
240
241     TRACE("%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
242             debugstr_w(szStatusText));
243
244     return S_OK;
245 }
246
247 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
248         HRESULT hr, LPCWSTR error)
249 {
250     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
251
252     TRACE("(%p)->(0x%08x %s)\n", This, hr, debugstr_w(error));
253
254     if (This->binding)
255     {
256         IBinding_Release(This->binding);
257         This->binding = NULL;
258     }
259
260     if (hr == S_OK)
261         httprequest_setreadystate(This->request, READYSTATE_COMPLETE);
262
263     return S_OK;
264 }
265
266 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
267         DWORD *bind_flags, BINDINFO *pbindinfo)
268 {
269     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
270
271     TRACE("(%p)->(%p %p)\n", This, bind_flags, pbindinfo);
272
273     *bind_flags = 0;
274     if (This->request->async) *bind_flags |= BINDF_ASYNCHRONOUS;
275
276     if (This->request->verb != BINDVERB_GET)
277     {
278         FIXME("only GET verb supported. Got %d\n", This->request->verb);
279         return E_FAIL;
280     }
281
282     pbindinfo->dwBindVerb = This->request->verb;
283
284     return S_OK;
285 }
286
287 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
288         DWORD flags, DWORD size, FORMATETC *format, STGMEDIUM *stgmed)
289 {
290     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
291     DWORD read, written;
292     BYTE buf[4096];
293     HRESULT hr;
294
295     TRACE("(%p)->(%08x %d %p %p)\n", This, flags, size, format, stgmed);
296
297     do
298     {
299         hr = IStream_Read(stgmed->u.pstm, buf, sizeof(buf), &read);
300         if (hr != S_OK) break;
301
302         hr = IStream_Write(This->stream, buf, read, &written);
303     } while((hr == S_OK) && written != 0 && read != 0);
304
305     httprequest_setreadystate(This->request, READYSTATE_INTERACTIVE);
306
307     return S_OK;
308 }
309
310 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
311         REFIID riid, IUnknown *punk)
312 {
313     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
314
315     FIXME("(%p)->(%s %p): stub\n", This, debugstr_guid(riid), punk);
316
317     return E_NOTIMPL;
318 }
319
320 #undef STATUSCLB_THIS
321
322 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
323     BindStatusCallback_QueryInterface,
324     BindStatusCallback_AddRef,
325     BindStatusCallback_Release,
326     BindStatusCallback_OnStartBinding,
327     BindStatusCallback_GetPriority,
328     BindStatusCallback_OnLowResource,
329     BindStatusCallback_OnProgress,
330     BindStatusCallback_OnStopBinding,
331     BindStatusCallback_GetBindInfo,
332     BindStatusCallback_OnDataAvailable,
333     BindStatusCallback_OnObjectAvailable
334 };
335
336 static HRESULT WINAPI BSCHttpNegotiate_QueryInterface(IHttpNegotiate *iface,
337         REFIID riid, void **ppv)
338 {
339     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
340     return IBindStatusCallback_QueryInterface((IBindStatusCallback*)This, riid, ppv);
341 }
342
343 static ULONG WINAPI BSCHttpNegotiate_AddRef(IHttpNegotiate *iface)
344 {
345     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
346     return IBindStatusCallback_AddRef((IBindStatusCallback*)This);
347 }
348
349 static ULONG WINAPI BSCHttpNegotiate_Release(IHttpNegotiate *iface)
350 {
351     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
352     return IBindStatusCallback_Release((IBindStatusCallback*)This);
353 }
354
355 static HRESULT WINAPI BSCHttpNegotiate_BeginningTransaction(IHttpNegotiate *iface,
356         LPCWSTR url, LPCWSTR headers, DWORD reserved, LPWSTR *add_headers)
357 {
358     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
359     const struct reqheader *entry;
360     WCHAR *buff, *ptr;
361
362     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(url), debugstr_w(headers), reserved, add_headers);
363
364     *add_headers = NULL;
365
366     if (list_empty(&This->request->reqheaders)) return S_OK;
367
368     buff = CoTaskMemAlloc(This->request->reqheader_size*sizeof(WCHAR));
369     if (!buff) return E_OUTOFMEMORY;
370
371     ptr = buff;
372     LIST_FOR_EACH_ENTRY(entry, &This->request->reqheaders, struct reqheader, entry)
373     {
374         lstrcpyW(ptr, entry->header);
375         ptr += SysStringLen(entry->header);
376
377         lstrcpyW(ptr, colspaceW);
378         ptr += sizeof(colspaceW)/sizeof(WCHAR)-1;
379
380         lstrcpyW(ptr, entry->value);
381         ptr += SysStringLen(entry->value);
382
383         lstrcpyW(ptr, crlfW);
384         ptr += sizeof(crlfW)/sizeof(WCHAR)-1;
385     }
386
387     *add_headers = buff;
388
389     return S_OK;
390 }
391
392 static HRESULT WINAPI BSCHttpNegotiate_OnResponse(IHttpNegotiate *iface, DWORD code,
393         LPCWSTR resp_headers, LPCWSTR req_headers, LPWSTR *add_reqheaders)
394 {
395     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
396
397     TRACE("(%p)->(%d %s %s %p)\n", This, code, debugstr_w(resp_headers),
398           debugstr_w(req_headers), add_reqheaders);
399
400     This->request->status = code;
401
402     return S_OK;
403 }
404
405 #undef HTTPNEG2_THIS
406
407 static const IHttpNegotiateVtbl BSCHttpNegotiateVtbl = {
408     BSCHttpNegotiate_QueryInterface,
409     BSCHttpNegotiate_AddRef,
410     BSCHttpNegotiate_Release,
411     BSCHttpNegotiate_BeginningTransaction,
412     BSCHttpNegotiate_OnResponse
413 };
414
415 static HRESULT BindStatusCallback_create(httprequest* This, BindStatusCallback **obj)
416 {
417     BindStatusCallback *bsc;
418     IBindCtx *pbc;
419     HRESULT hr;
420
421     hr = CreateBindCtx(0, &pbc);
422     if (hr != S_OK) return hr;
423
424     bsc = heap_alloc(sizeof(*bsc));
425     if (!bsc)
426     {
427         IBindCtx_Release(pbc);
428         return E_OUTOFMEMORY;
429     }
430
431     bsc->lpBindStatusCallbackVtbl = &BindStatusCallbackVtbl;
432     bsc->lpHttpNegotiateVtbl = &BSCHttpNegotiateVtbl;
433     bsc->ref = 1;
434     bsc->request = This;
435     bsc->binding = NULL;
436     bsc->stream = NULL;
437
438     hr = RegisterBindStatusCallback(pbc, (IBindStatusCallback*)bsc, NULL, 0);
439     if (hr == S_OK)
440     {
441         IMoniker *moniker;
442
443         hr = CreateURLMoniker(NULL, This->url, &moniker);
444         if (hr == S_OK)
445         {
446             IStream *stream;
447
448             hr = IMoniker_BindToStorage(moniker, pbc, NULL, &IID_IStream, (void**)&stream);
449             IMoniker_Release(moniker);
450             if (stream) IStream_Release(stream);
451         }
452         IBindCtx_Release(pbc);
453     }
454
455     if (FAILED(hr))
456     {
457         IBindStatusCallback_Release((IBindStatusCallback*)bsc);
458         bsc = NULL;
459     }
460
461     *obj = bsc;
462     return hr;
463 }
464
465 static HRESULT WINAPI httprequest_QueryInterface(IXMLHTTPRequest *iface, REFIID riid, void **ppvObject)
466 {
467     httprequest *This = impl_from_IXMLHTTPRequest( iface );
468     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
469
470     if ( IsEqualGUID( riid, &IID_IXMLHTTPRequest) ||
471          IsEqualGUID( riid, &IID_IDispatch) ||
472          IsEqualGUID( riid, &IID_IUnknown) )
473     {
474         *ppvObject = iface;
475     }
476     else
477     {
478         FIXME("Unsupported interface %s\n", debugstr_guid(riid));
479         return E_NOINTERFACE;
480     }
481
482     IXMLHTTPRequest_AddRef( iface );
483
484     return S_OK;
485 }
486
487 static ULONG WINAPI httprequest_AddRef(IXMLHTTPRequest *iface)
488 {
489     httprequest *This = impl_from_IXMLHTTPRequest( iface );
490     ULONG ref = InterlockedIncrement( &This->ref );
491     TRACE("(%p)->(%u)\n", This, ref );
492     return ref;
493 }
494
495 static ULONG WINAPI httprequest_Release(IXMLHTTPRequest *iface)
496 {
497     httprequest *This = impl_from_IXMLHTTPRequest( iface );
498     ULONG ref = InterlockedDecrement( &This->ref );
499
500     TRACE("(%p)->(%u)\n", This, ref );
501
502     if ( ref == 0 )
503     {
504         struct reqheader *header, *header2;
505
506         SysFreeString(This->url);
507         SysFreeString(This->user);
508         SysFreeString(This->password);
509
510         /* request headers */
511         LIST_FOR_EACH_ENTRY_SAFE(header, header2, &This->reqheaders, struct reqheader, entry)
512         {
513             list_remove(&header->entry);
514             SysFreeString(header->header);
515             SysFreeString(header->value);
516         }
517
518         /* detach callback object */
519         BindStatusCallback_Detach(This->bsc);
520
521         if (This->sink) IDispatch_Release(This->sink);
522
523         heap_free( This );
524     }
525
526     return ref;
527 }
528
529 static HRESULT WINAPI httprequest_GetTypeInfoCount(IXMLHTTPRequest *iface, UINT *pctinfo)
530 {
531     httprequest *This = impl_from_IXMLHTTPRequest( iface );
532
533     TRACE("(%p)->(%p)\n", This, pctinfo);
534
535     *pctinfo = 1;
536
537     return S_OK;
538 }
539
540 static HRESULT WINAPI httprequest_GetTypeInfo(IXMLHTTPRequest *iface, UINT iTInfo,
541         LCID lcid, ITypeInfo **ppTInfo)
542 {
543     httprequest *This = impl_from_IXMLHTTPRequest( iface );
544     HRESULT hr;
545
546     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
547
548     hr = get_typeinfo(IXMLHTTPRequest_tid, ppTInfo);
549
550     return hr;
551 }
552
553 static HRESULT WINAPI httprequest_GetIDsOfNames(IXMLHTTPRequest *iface, REFIID riid,
554         LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
555 {
556     httprequest *This = impl_from_IXMLHTTPRequest( iface );
557     ITypeInfo *typeinfo;
558     HRESULT hr;
559
560     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
561           lcid, rgDispId);
562
563     if(!rgszNames || cNames == 0 || !rgDispId)
564         return E_INVALIDARG;
565
566     hr = get_typeinfo(IXMLHTTPRequest_tid, &typeinfo);
567     if(SUCCEEDED(hr))
568     {
569         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
570         ITypeInfo_Release(typeinfo);
571     }
572
573     return hr;
574 }
575
576 static HRESULT WINAPI httprequest_Invoke(IXMLHTTPRequest *iface, DISPID dispIdMember, REFIID riid,
577         LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
578         EXCEPINFO *pExcepInfo, UINT *puArgErr)
579 {
580     httprequest *This = impl_from_IXMLHTTPRequest( iface );
581     ITypeInfo *typeinfo;
582     HRESULT hr;
583
584     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
585           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
586
587     hr = get_typeinfo(IXMLHTTPRequest_tid, &typeinfo);
588     if(SUCCEEDED(hr))
589     {
590         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
591                 pVarResult, pExcepInfo, puArgErr);
592         ITypeInfo_Release(typeinfo);
593     }
594
595     return hr;
596 }
597
598 static HRESULT WINAPI httprequest_open(IXMLHTTPRequest *iface, BSTR method, BSTR url,
599         VARIANT async, VARIANT user, VARIANT password)
600 {
601     httprequest *This = impl_from_IXMLHTTPRequest( iface );
602     HRESULT hr;
603     VARIANT str;
604
605     TRACE("(%p)->(%s %s)\n", This, debugstr_w(method), debugstr_w(url));
606
607     if (!method || !url) return E_INVALIDARG;
608
609     /* free previously set data */
610     SysFreeString(This->url);
611     SysFreeString(This->user);
612     SysFreeString(This->password);
613     This->url = This->user = This->password = NULL;
614
615     if (lstrcmpiW(method, MethodGetW) == 0)
616     {
617         This->verb = BINDVERB_GET;
618     }
619     else if (lstrcmpiW(method, MethodPutW) == 0)
620     {
621         This->verb = BINDVERB_PUT;
622     }
623     else if (lstrcmpiW(method, MethodPostW) == 0)
624     {
625         This->verb = BINDVERB_POST;
626     }
627     else
628     {
629         FIXME("unsupported request type %s\n", debugstr_w(method));
630         This->verb = -1;
631         return E_FAIL;
632     }
633
634     This->url = SysAllocString(url);
635
636     hr = VariantChangeType(&async, &async, 0, VT_BOOL);
637     This->async = hr == S_OK && V_BOOL(&async) == VARIANT_TRUE;
638
639     VariantInit(&str);
640     hr = VariantChangeType(&str, &user, 0, VT_BSTR);
641     if (hr == S_OK)
642         This->user = V_BSTR(&str);
643
644     hr = VariantChangeType(&str, &password, 0, VT_BSTR);
645     if (hr == S_OK)
646         This->password = V_BSTR(&str);
647
648     httprequest_setreadystate(This, READYSTATE_LOADING);
649
650     return S_OK;
651 }
652
653 static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR header, BSTR value)
654 {
655     httprequest *This = impl_from_IXMLHTTPRequest( iface );
656     struct reqheader *entry;
657
658     TRACE("(%p)->(%s %s)\n", This, debugstr_w(header), debugstr_w(value));
659
660     if (!header || !*header) return E_INVALIDARG;
661     if (This->state != READYSTATE_LOADING) return E_FAIL;
662     if (!value) return E_INVALIDARG;
663
664     /* replace existing header value if already added */
665     LIST_FOR_EACH_ENTRY(entry, &This->reqheaders, struct reqheader, entry)
666     {
667         if (lstrcmpW(entry->header, header) == 0)
668         {
669             LONG length = SysStringLen(entry->value);
670             HRESULT hr;
671
672             hr = SysReAllocString(&entry->value, value) ? S_OK : E_OUTOFMEMORY;
673
674             if (hr == S_OK)
675                 This->reqheader_size += (SysStringLen(entry->value) - length);
676
677             return hr;
678         }
679     }
680
681     entry = heap_alloc(sizeof(*entry));
682     if (!entry) return E_OUTOFMEMORY;
683
684     /* new header */
685     entry->header = SysAllocString(header);
686     entry->value  = SysAllocString(value);
687
688     /* header length including null terminator */
689     This->reqheader_size += SysStringLen(entry->header) + sizeof(colspaceW)/sizeof(WCHAR) +
690                             SysStringLen(entry->value)  + sizeof(crlfW)/sizeof(WCHAR) - 1;
691
692     list_add_head(&This->reqheaders, &entry->entry);
693
694     return S_OK;
695 }
696
697 static HRESULT WINAPI httprequest_getResponseHeader(IXMLHTTPRequest *iface, BSTR bstrHeader, BSTR *pbstrValue)
698 {
699     httprequest *This = impl_from_IXMLHTTPRequest( iface );
700
701     FIXME("stub (%p) %s %p\n", This, debugstr_w(bstrHeader), pbstrValue);
702
703     return E_NOTIMPL;
704 }
705
706 static HRESULT WINAPI httprequest_getAllResponseHeaders(IXMLHTTPRequest *iface, BSTR *pbstrHeaders)
707 {
708     httprequest *This = impl_from_IXMLHTTPRequest( iface );
709
710     FIXME("stub (%p) %p\n", This, pbstrHeaders);
711
712     return E_NOTIMPL;
713 }
714
715 static HRESULT WINAPI httprequest_send(IXMLHTTPRequest *iface, VARIANT varBody)
716 {
717     httprequest *This = impl_from_IXMLHTTPRequest( iface );
718     BindStatusCallback *bsc = NULL;
719     HRESULT hr;
720
721     TRACE("(%p)\n", This);
722
723     if (This->state != READYSTATE_LOADING) return E_FAIL;
724
725     hr = BindStatusCallback_create(This, &bsc);
726     if (FAILED(hr)) return hr;
727
728     BindStatusCallback_Detach(This->bsc);
729     This->bsc = bsc;
730
731     return hr;
732 }
733
734 static HRESULT WINAPI httprequest_abort(IXMLHTTPRequest *iface)
735 {
736     httprequest *This = impl_from_IXMLHTTPRequest( iface );
737
738     FIXME("stub (%p)\n", This);
739
740     return E_NOTIMPL;
741 }
742
743 static HRESULT WINAPI httprequest_get_status(IXMLHTTPRequest *iface, LONG *status)
744 {
745     httprequest *This = impl_from_IXMLHTTPRequest( iface );
746
747     TRACE("(%p)->(%p)\n", This, status);
748
749     if (!status) return E_INVALIDARG;
750     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
751
752     *status = This->status;
753
754     return S_OK;
755 }
756
757 static HRESULT WINAPI httprequest_get_statusText(IXMLHTTPRequest *iface, BSTR *pbstrStatus)
758 {
759     httprequest *This = impl_from_IXMLHTTPRequest( iface );
760
761     FIXME("stub %p %p\n", This, pbstrStatus);
762
763     return E_NOTIMPL;
764 }
765
766 static HRESULT WINAPI httprequest_get_responseXML(IXMLHTTPRequest *iface, IDispatch **ppBody)
767 {
768     httprequest *This = impl_from_IXMLHTTPRequest( iface );
769
770     FIXME("stub %p %p\n", This, ppBody);
771
772     return E_NOTIMPL;
773 }
774
775 static HRESULT WINAPI httprequest_get_responseText(IXMLHTTPRequest *iface, BSTR *body)
776 {
777     httprequest *This = impl_from_IXMLHTTPRequest( iface );
778     HGLOBAL hglobal;
779     HRESULT hr;
780
781     TRACE("(%p)->(%p)\n", This, body);
782
783     if (!body) return E_INVALIDARG;
784     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
785
786     hr = GetHGlobalFromStream(This->bsc->stream, &hglobal);
787     if (hr == S_OK)
788     {
789         xmlChar *ptr = GlobalLock(hglobal);
790         DWORD size = GlobalSize(hglobal);
791         xmlCharEncoding encoding = XML_CHAR_ENCODING_UTF8;
792
793         /* try to determine data encoding */
794         if (size >= 4)
795         {
796             encoding = xmlDetectCharEncoding(ptr, 4);
797             TRACE("detected encoding: %s\n", xmlGetCharEncodingName(encoding));
798             if ( encoding != XML_CHAR_ENCODING_UTF8 &&
799                  encoding != XML_CHAR_ENCODING_UTF16LE &&
800                  encoding != XML_CHAR_ENCODING_NONE )
801             {
802                 FIXME("unsupported encoding: %s\n", xmlGetCharEncodingName(encoding));
803                 GlobalUnlock(hglobal);
804                 return E_FAIL;
805             }
806         }
807
808         /* without BOM assume UTF-8 */
809         if (encoding == XML_CHAR_ENCODING_UTF8 ||
810             encoding == XML_CHAR_ENCODING_NONE )
811         {
812             *body = bstr_from_xmlChar(ptr);
813         }
814         else
815             *body = SysAllocStringByteLen((LPCSTR)ptr, size);
816
817         if (!*body) hr = E_OUTOFMEMORY;
818         GlobalUnlock(hglobal);
819     }
820
821     return hr;
822 }
823
824 static HRESULT WINAPI httprequest_get_responseBody(IXMLHTTPRequest *iface, VARIANT *pvarBody)
825 {
826     httprequest *This = impl_from_IXMLHTTPRequest( iface );
827
828     FIXME("stub %p %p\n", This, pvarBody);
829
830     return E_NOTIMPL;
831 }
832
833 static HRESULT WINAPI httprequest_get_responseStream(IXMLHTTPRequest *iface, VARIANT *pvarBody)
834 {
835     httprequest *This = impl_from_IXMLHTTPRequest( iface );
836
837     FIXME("stub %p %p\n", This, pvarBody);
838
839     return E_NOTIMPL;
840 }
841
842 static HRESULT WINAPI httprequest_get_readyState(IXMLHTTPRequest *iface, LONG *state)
843 {
844     httprequest *This = impl_from_IXMLHTTPRequest( iface );
845
846     TRACE("(%p)->(%p)\n", This, state);
847
848     if (!state) return E_INVALIDARG;
849
850     *state = This->state;
851     return S_OK;
852 }
853
854 static HRESULT WINAPI httprequest_put_onreadystatechange(IXMLHTTPRequest *iface, IDispatch *sink)
855 {
856     httprequest *This = impl_from_IXMLHTTPRequest( iface );
857
858     TRACE("(%p)->(%p)\n", This, sink);
859
860     if (This->sink) IDispatch_Release(This->sink);
861     if ((This->sink = sink)) IDispatch_AddRef(This->sink);
862
863     return S_OK;
864 }
865
866 static const struct IXMLHTTPRequestVtbl dimimpl_vtbl =
867 {
868     httprequest_QueryInterface,
869     httprequest_AddRef,
870     httprequest_Release,
871     httprequest_GetTypeInfoCount,
872     httprequest_GetTypeInfo,
873     httprequest_GetIDsOfNames,
874     httprequest_Invoke,
875     httprequest_open,
876     httprequest_setRequestHeader,
877     httprequest_getResponseHeader,
878     httprequest_getAllResponseHeaders,
879     httprequest_send,
880     httprequest_abort,
881     httprequest_get_status,
882     httprequest_get_statusText,
883     httprequest_get_responseXML,
884     httprequest_get_responseText,
885     httprequest_get_responseBody,
886     httprequest_get_responseStream,
887     httprequest_get_readyState,
888     httprequest_put_onreadystatechange
889 };
890
891 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
892 {
893     httprequest *req;
894     HRESULT hr = S_OK;
895
896     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
897
898     req = heap_alloc( sizeof (*req) );
899     if( !req )
900         return E_OUTOFMEMORY;
901
902     req->lpVtbl = &dimimpl_vtbl;
903     req->ref = 1;
904
905     req->async = FALSE;
906     req->verb = -1;
907     req->url = req->user = req->password = NULL;
908
909     req->state = READYSTATE_UNINITIALIZED;
910     req->sink = NULL;
911
912     req->bsc = NULL;
913     req->status = 0;
914     req->reqheader_size = 0;
915     list_init(&req->reqheaders);
916
917     *ppObj = &req->lpVtbl;
918
919     TRACE("returning iface %p\n", *ppObj);
920
921     return hr;
922 }
923
924 #else
925
926 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
927 {
928     MESSAGE("This program tried to use a XMLHTTPRequest object, but\n"
929             "libxml2 support was not present at compile time.\n");
930     return E_NOTIMPL;
931 }
932
933 #endif