user32/tests: Remove unneeded assignment (LLVM/Clang).
[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     IXMLHTTPRequest IXMLHTTPRequest_iface;
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 CONTAINING_RECORD(iface, httprequest, IXMLHTTPRequest_iface);
89 }
90
91 static void httprequest_setreadystate(httprequest *This, READYSTATE state)
92 {
93     READYSTATE last = This->state;
94
95     This->state = state;
96
97     if (This->sink && last != state)
98     {
99         DISPPARAMS params;
100
101         memset(&params, 0, sizeof(params));
102         IDispatch_Invoke(This->sink, 0, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &params, 0, 0, 0);
103     }
104 }
105
106 struct BindStatusCallback
107 {
108     IBindStatusCallback IBindStatusCallback_iface;
109     IHttpNegotiate      IHttpNegotiate_iface;
110     LONG ref;
111
112     IBinding *binding;
113     httprequest *request;
114
115     /* response data */
116     IStream *stream;
117
118     /* request body data */
119     HGLOBAL body;
120 };
121
122 static inline BindStatusCallback *impl_from_IBindStatusCallback( IBindStatusCallback *iface )
123 {
124     return CONTAINING_RECORD(iface, BindStatusCallback, IBindStatusCallback_iface);
125 }
126
127 static inline BindStatusCallback *impl_from_IHttpNegotiate( IHttpNegotiate *iface )
128 {
129     return CONTAINING_RECORD(iface, BindStatusCallback, IHttpNegotiate_iface);
130 }
131
132 void BindStatusCallback_Detach(BindStatusCallback *bsc)
133 {
134     if (bsc)
135     {
136         if (bsc->binding) IBinding_Abort(bsc->binding);
137         bsc->request = NULL;
138         IBindStatusCallback_Release(&bsc->IBindStatusCallback_iface);
139     }
140 }
141
142 static HRESULT WINAPI BindStatusCallback_QueryInterface(IBindStatusCallback *iface,
143         REFIID riid, void **ppv)
144 {
145     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
146
147     *ppv = NULL;
148
149     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
150
151     if (IsEqualGUID(&IID_IUnknown, riid) ||
152         IsEqualGUID(&IID_IBindStatusCallback, riid))
153     {
154         *ppv = &This->IBindStatusCallback_iface;
155     }
156     else if (IsEqualGUID(&IID_IHttpNegotiate, riid))
157     {
158         *ppv = &This->IHttpNegotiate_iface;
159     }
160     else if (IsEqualGUID(&IID_IServiceProvider, riid) ||
161              IsEqualGUID(&IID_IBindStatusCallbackEx, riid) ||
162              IsEqualGUID(&IID_IInternetProtocol, riid) ||
163              IsEqualGUID(&IID_IHttpNegotiate2, riid))
164     {
165         return E_NOINTERFACE;
166     }
167
168     if (*ppv)
169     {
170         IBindStatusCallback_AddRef(iface);
171         return S_OK;
172     }
173
174     FIXME("Unsupported riid = %s\n", debugstr_guid(riid));
175
176     return E_NOINTERFACE;
177 }
178
179 static ULONG WINAPI BindStatusCallback_AddRef(IBindStatusCallback *iface)
180 {
181     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
182     LONG ref = InterlockedIncrement(&This->ref);
183
184     TRACE("(%p) ref = %d\n", This, ref);
185
186     return ref;
187 }
188
189 static ULONG WINAPI BindStatusCallback_Release(IBindStatusCallback *iface)
190 {
191     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
192     LONG ref = InterlockedDecrement(&This->ref);
193
194     TRACE("(%p) ref = %d\n", This, ref);
195
196     if (!ref)
197     {
198         if (This->binding) IBinding_Release(This->binding);
199         if (This->stream) IStream_Release(This->stream);
200         if (This->body) GlobalFree(This->body);
201         heap_free(This);
202     }
203
204     return ref;
205 }
206
207 static HRESULT WINAPI BindStatusCallback_OnStartBinding(IBindStatusCallback *iface,
208         DWORD reserved, IBinding *pbind)
209 {
210     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
211
212     TRACE("(%p)->(%d %p)\n", This, reserved, pbind);
213
214     if (!pbind) return E_INVALIDARG;
215
216     This->binding = pbind;
217     IBinding_AddRef(pbind);
218
219     httprequest_setreadystate(This->request, READYSTATE_LOADED);
220
221     return CreateStreamOnHGlobal(NULL, TRUE, &This->stream);
222 }
223
224 static HRESULT WINAPI BindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pPriority)
225 {
226     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
227
228     TRACE("(%p)->(%p)\n", This, pPriority);
229
230     return E_NOTIMPL;
231 }
232
233 static HRESULT WINAPI BindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
234 {
235     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
236
237     TRACE("(%p)->(%d)\n", This, reserved);
238
239     return E_NOTIMPL;
240 }
241
242 static HRESULT WINAPI BindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
243         ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
244 {
245     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
246
247     TRACE("(%p)->(%u %u %u %s)\n", This, ulProgress, ulProgressMax, ulStatusCode,
248             debugstr_w(szStatusText));
249
250     return S_OK;
251 }
252
253 static HRESULT WINAPI BindStatusCallback_OnStopBinding(IBindStatusCallback *iface,
254         HRESULT hr, LPCWSTR error)
255 {
256     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
257
258     TRACE("(%p)->(0x%08x %s)\n", This, hr, debugstr_w(error));
259
260     if (This->binding)
261     {
262         IBinding_Release(This->binding);
263         This->binding = NULL;
264     }
265
266     if (hr == S_OK)
267         httprequest_setreadystate(This->request, READYSTATE_COMPLETE);
268
269     return S_OK;
270 }
271
272 static HRESULT WINAPI BindStatusCallback_GetBindInfo(IBindStatusCallback *iface,
273         DWORD *bind_flags, BINDINFO *pbindinfo)
274 {
275     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
276
277     TRACE("(%p)->(%p %p)\n", This, bind_flags, pbindinfo);
278
279     *bind_flags = 0;
280     if (This->request->async) *bind_flags |= BINDF_ASYNCHRONOUS;
281
282     if (This->request->verb != BINDVERB_GET && This->body)
283     {
284         pbindinfo->stgmedData.tymed = TYMED_HGLOBAL;
285         pbindinfo->stgmedData.u.hGlobal = This->body;
286         pbindinfo->cbstgmedData = GlobalSize(This->body);
287         /* callback owns passed body pointer */
288         IBindStatusCallback_QueryInterface(iface, &IID_IUnknown, (void**)&pbindinfo->stgmedData.pUnkForRelease);
289     }
290
291     pbindinfo->dwBindVerb = This->request->verb;
292
293     return S_OK;
294 }
295
296 static HRESULT WINAPI BindStatusCallback_OnDataAvailable(IBindStatusCallback *iface,
297         DWORD flags, DWORD size, FORMATETC *format, STGMEDIUM *stgmed)
298 {
299     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
300     DWORD read, written;
301     BYTE buf[4096];
302     HRESULT hr;
303
304     TRACE("(%p)->(%08x %d %p %p)\n", This, flags, size, format, stgmed);
305
306     do
307     {
308         hr = IStream_Read(stgmed->u.pstm, buf, sizeof(buf), &read);
309         if (hr != S_OK) break;
310
311         hr = IStream_Write(This->stream, buf, read, &written);
312     } while((hr == S_OK) && written != 0 && read != 0);
313
314     httprequest_setreadystate(This->request, READYSTATE_INTERACTIVE);
315
316     return S_OK;
317 }
318
319 static HRESULT WINAPI BindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface,
320         REFIID riid, IUnknown *punk)
321 {
322     BindStatusCallback *This = impl_from_IBindStatusCallback(iface);
323
324     FIXME("(%p)->(%s %p): stub\n", This, debugstr_guid(riid), punk);
325
326     return E_NOTIMPL;
327 }
328
329 static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = {
330     BindStatusCallback_QueryInterface,
331     BindStatusCallback_AddRef,
332     BindStatusCallback_Release,
333     BindStatusCallback_OnStartBinding,
334     BindStatusCallback_GetPriority,
335     BindStatusCallback_OnLowResource,
336     BindStatusCallback_OnProgress,
337     BindStatusCallback_OnStopBinding,
338     BindStatusCallback_GetBindInfo,
339     BindStatusCallback_OnDataAvailable,
340     BindStatusCallback_OnObjectAvailable
341 };
342
343 static HRESULT WINAPI BSCHttpNegotiate_QueryInterface(IHttpNegotiate *iface,
344         REFIID riid, void **ppv)
345 {
346     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
347     return IBindStatusCallback_QueryInterface(&This->IBindStatusCallback_iface, riid, ppv);
348 }
349
350 static ULONG WINAPI BSCHttpNegotiate_AddRef(IHttpNegotiate *iface)
351 {
352     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
353     return IBindStatusCallback_AddRef(&This->IBindStatusCallback_iface);
354 }
355
356 static ULONG WINAPI BSCHttpNegotiate_Release(IHttpNegotiate *iface)
357 {
358     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
359     return IBindStatusCallback_Release(&This->IBindStatusCallback_iface);
360 }
361
362 static HRESULT WINAPI BSCHttpNegotiate_BeginningTransaction(IHttpNegotiate *iface,
363         LPCWSTR url, LPCWSTR headers, DWORD reserved, LPWSTR *add_headers)
364 {
365     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
366     const struct reqheader *entry;
367     WCHAR *buff, *ptr;
368
369     TRACE("(%p)->(%s %s %d %p)\n", This, debugstr_w(url), debugstr_w(headers), reserved, add_headers);
370
371     *add_headers = NULL;
372
373     if (list_empty(&This->request->reqheaders)) return S_OK;
374
375     buff = CoTaskMemAlloc(This->request->reqheader_size*sizeof(WCHAR));
376     if (!buff) return E_OUTOFMEMORY;
377
378     ptr = buff;
379     LIST_FOR_EACH_ENTRY(entry, &This->request->reqheaders, struct reqheader, entry)
380     {
381         lstrcpyW(ptr, entry->header);
382         ptr += SysStringLen(entry->header);
383
384         lstrcpyW(ptr, colspaceW);
385         ptr += sizeof(colspaceW)/sizeof(WCHAR)-1;
386
387         lstrcpyW(ptr, entry->value);
388         ptr += SysStringLen(entry->value);
389
390         lstrcpyW(ptr, crlfW);
391         ptr += sizeof(crlfW)/sizeof(WCHAR)-1;
392     }
393
394     *add_headers = buff;
395
396     return S_OK;
397 }
398
399 static HRESULT WINAPI BSCHttpNegotiate_OnResponse(IHttpNegotiate *iface, DWORD code,
400         LPCWSTR resp_headers, LPCWSTR req_headers, LPWSTR *add_reqheaders)
401 {
402     BindStatusCallback *This = impl_from_IHttpNegotiate(iface);
403
404     TRACE("(%p)->(%d %s %s %p)\n", This, code, debugstr_w(resp_headers),
405           debugstr_w(req_headers), add_reqheaders);
406
407     This->request->status = code;
408
409     return S_OK;
410 }
411
412 static const IHttpNegotiateVtbl BSCHttpNegotiateVtbl = {
413     BSCHttpNegotiate_QueryInterface,
414     BSCHttpNegotiate_AddRef,
415     BSCHttpNegotiate_Release,
416     BSCHttpNegotiate_BeginningTransaction,
417     BSCHttpNegotiate_OnResponse
418 };
419
420 static HRESULT BindStatusCallback_create(httprequest* This, BindStatusCallback **obj, const VARIANT *body)
421 {
422     BindStatusCallback *bsc;
423     IBindCtx *pbc;
424     HRESULT hr;
425
426     hr = CreateBindCtx(0, &pbc);
427     if (hr != S_OK) return hr;
428
429     bsc = heap_alloc(sizeof(*bsc));
430     if (!bsc)
431     {
432         IBindCtx_Release(pbc);
433         return E_OUTOFMEMORY;
434     }
435
436     bsc->IBindStatusCallback_iface.lpVtbl = &BindStatusCallbackVtbl;
437     bsc->IHttpNegotiate_iface.lpVtbl = &BSCHttpNegotiateVtbl;
438     bsc->ref = 1;
439     bsc->request = This;
440     bsc->binding = NULL;
441     bsc->stream = NULL;
442     bsc->body = NULL;
443
444     TRACE("created callback %p\n", bsc);
445
446     if (This->verb != BINDVERB_GET)
447     {
448         if (V_VT(body) == VT_BSTR)
449         {
450             LONG size = SysStringLen(V_BSTR(body)) * sizeof(WCHAR);
451             void *ptr;
452
453             bsc->body = GlobalAlloc(GMEM_FIXED, size);
454             if (!bsc->body)
455             {
456                 heap_free(bsc);
457                 return E_OUTOFMEMORY;
458             }
459
460             ptr = GlobalLock(bsc->body);
461             memcpy(ptr, V_BSTR(body), size);
462             GlobalUnlock(bsc->body);
463         }
464         else
465             FIXME("unsupported body data type %d\n", V_VT(body));
466     }
467
468     hr = RegisterBindStatusCallback(pbc, &bsc->IBindStatusCallback_iface, NULL, 0);
469     if (hr == S_OK)
470     {
471         IMoniker *moniker;
472
473         hr = CreateURLMoniker(NULL, This->url, &moniker);
474         if (hr == S_OK)
475         {
476             IStream *stream;
477
478             hr = IMoniker_BindToStorage(moniker, pbc, NULL, &IID_IStream, (void**)&stream);
479             IMoniker_Release(moniker);
480             if (stream) IStream_Release(stream);
481         }
482         IBindCtx_Release(pbc);
483     }
484
485     if (FAILED(hr))
486     {
487         IBindStatusCallback_Release(&bsc->IBindStatusCallback_iface);
488         bsc = NULL;
489     }
490
491     *obj = bsc;
492     return hr;
493 }
494
495 static HRESULT WINAPI httprequest_QueryInterface(IXMLHTTPRequest *iface, REFIID riid, void **ppvObject)
496 {
497     httprequest *This = impl_from_IXMLHTTPRequest( iface );
498     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
499
500     if ( IsEqualGUID( riid, &IID_IXMLHTTPRequest) ||
501          IsEqualGUID( riid, &IID_IDispatch) ||
502          IsEqualGUID( riid, &IID_IUnknown) )
503     {
504         *ppvObject = iface;
505     }
506     else
507     {
508         FIXME("Unsupported interface %s\n", debugstr_guid(riid));
509         return E_NOINTERFACE;
510     }
511
512     IXMLHTTPRequest_AddRef( iface );
513
514     return S_OK;
515 }
516
517 static ULONG WINAPI httprequest_AddRef(IXMLHTTPRequest *iface)
518 {
519     httprequest *This = impl_from_IXMLHTTPRequest( iface );
520     ULONG ref = InterlockedIncrement( &This->ref );
521     TRACE("(%p)->(%u)\n", This, ref );
522     return ref;
523 }
524
525 static ULONG WINAPI httprequest_Release(IXMLHTTPRequest *iface)
526 {
527     httprequest *This = impl_from_IXMLHTTPRequest( iface );
528     ULONG ref = InterlockedDecrement( &This->ref );
529
530     TRACE("(%p)->(%u)\n", This, ref );
531
532     if ( ref == 0 )
533     {
534         struct reqheader *header, *header2;
535
536         SysFreeString(This->url);
537         SysFreeString(This->user);
538         SysFreeString(This->password);
539
540         /* request headers */
541         LIST_FOR_EACH_ENTRY_SAFE(header, header2, &This->reqheaders, struct reqheader, entry)
542         {
543             list_remove(&header->entry);
544             SysFreeString(header->header);
545             SysFreeString(header->value);
546             heap_free(header);
547         }
548
549         /* detach callback object */
550         BindStatusCallback_Detach(This->bsc);
551
552         if (This->sink) IDispatch_Release(This->sink);
553
554         heap_free( This );
555     }
556
557     return ref;
558 }
559
560 static HRESULT WINAPI httprequest_GetTypeInfoCount(IXMLHTTPRequest *iface, UINT *pctinfo)
561 {
562     httprequest *This = impl_from_IXMLHTTPRequest( iface );
563
564     TRACE("(%p)->(%p)\n", This, pctinfo);
565
566     *pctinfo = 1;
567
568     return S_OK;
569 }
570
571 static HRESULT WINAPI httprequest_GetTypeInfo(IXMLHTTPRequest *iface, UINT iTInfo,
572         LCID lcid, ITypeInfo **ppTInfo)
573 {
574     httprequest *This = impl_from_IXMLHTTPRequest( iface );
575     HRESULT hr;
576
577     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
578
579     hr = get_typeinfo(IXMLHTTPRequest_tid, ppTInfo);
580
581     return hr;
582 }
583
584 static HRESULT WINAPI httprequest_GetIDsOfNames(IXMLHTTPRequest *iface, REFIID riid,
585         LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
586 {
587     httprequest *This = impl_from_IXMLHTTPRequest( iface );
588     ITypeInfo *typeinfo;
589     HRESULT hr;
590
591     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
592           lcid, rgDispId);
593
594     if(!rgszNames || cNames == 0 || !rgDispId)
595         return E_INVALIDARG;
596
597     hr = get_typeinfo(IXMLHTTPRequest_tid, &typeinfo);
598     if(SUCCEEDED(hr))
599     {
600         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
601         ITypeInfo_Release(typeinfo);
602     }
603
604     return hr;
605 }
606
607 static HRESULT WINAPI httprequest_Invoke(IXMLHTTPRequest *iface, DISPID dispIdMember, REFIID riid,
608         LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
609         EXCEPINFO *pExcepInfo, UINT *puArgErr)
610 {
611     httprequest *This = impl_from_IXMLHTTPRequest( iface );
612     ITypeInfo *typeinfo;
613     HRESULT hr;
614
615     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
616           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
617
618     hr = get_typeinfo(IXMLHTTPRequest_tid, &typeinfo);
619     if(SUCCEEDED(hr))
620     {
621         hr = ITypeInfo_Invoke(typeinfo, &This->IXMLHTTPRequest_iface, dispIdMember, wFlags,
622                 pDispParams, pVarResult, pExcepInfo, puArgErr);
623         ITypeInfo_Release(typeinfo);
624     }
625
626     return hr;
627 }
628
629 static HRESULT WINAPI httprequest_open(IXMLHTTPRequest *iface, BSTR method, BSTR url,
630         VARIANT async, VARIANT user, VARIANT password)
631 {
632     httprequest *This = impl_from_IXMLHTTPRequest( iface );
633     HRESULT hr;
634     VARIANT str;
635
636     TRACE("(%p)->(%s %s)\n", This, debugstr_w(method), debugstr_w(url));
637
638     if (!method || !url) return E_INVALIDARG;
639
640     /* free previously set data */
641     SysFreeString(This->url);
642     SysFreeString(This->user);
643     SysFreeString(This->password);
644     This->url = This->user = This->password = NULL;
645
646     if (lstrcmpiW(method, MethodGetW) == 0)
647     {
648         This->verb = BINDVERB_GET;
649     }
650     else if (lstrcmpiW(method, MethodPutW) == 0)
651     {
652         This->verb = BINDVERB_PUT;
653     }
654     else if (lstrcmpiW(method, MethodPostW) == 0)
655     {
656         This->verb = BINDVERB_POST;
657     }
658     else
659     {
660         FIXME("unsupported request type %s\n", debugstr_w(method));
661         This->verb = -1;
662         return E_FAIL;
663     }
664
665     This->url = SysAllocString(url);
666
667     hr = VariantChangeType(&async, &async, 0, VT_BOOL);
668     This->async = hr == S_OK && V_BOOL(&async) == VARIANT_TRUE;
669
670     VariantInit(&str);
671     hr = VariantChangeType(&str, &user, 0, VT_BSTR);
672     if (hr == S_OK)
673         This->user = V_BSTR(&str);
674
675     hr = VariantChangeType(&str, &password, 0, VT_BSTR);
676     if (hr == S_OK)
677         This->password = V_BSTR(&str);
678
679     httprequest_setreadystate(This, READYSTATE_LOADING);
680
681     return S_OK;
682 }
683
684 static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR header, BSTR value)
685 {
686     httprequest *This = impl_from_IXMLHTTPRequest( iface );
687     struct reqheader *entry;
688
689     TRACE("(%p)->(%s %s)\n", This, debugstr_w(header), debugstr_w(value));
690
691     if (!header || !*header) return E_INVALIDARG;
692     if (This->state != READYSTATE_LOADING) return E_FAIL;
693     if (!value) return E_INVALIDARG;
694
695     /* replace existing header value if already added */
696     LIST_FOR_EACH_ENTRY(entry, &This->reqheaders, struct reqheader, entry)
697     {
698         if (lstrcmpW(entry->header, header) == 0)
699         {
700             LONG length = SysStringLen(entry->value);
701             HRESULT hr;
702
703             hr = SysReAllocString(&entry->value, value) ? S_OK : E_OUTOFMEMORY;
704
705             if (hr == S_OK)
706                 This->reqheader_size += (SysStringLen(entry->value) - length);
707
708             return hr;
709         }
710     }
711
712     entry = heap_alloc(sizeof(*entry));
713     if (!entry) return E_OUTOFMEMORY;
714
715     /* new header */
716     entry->header = SysAllocString(header);
717     entry->value  = SysAllocString(value);
718
719     /* header length including null terminator */
720     This->reqheader_size += SysStringLen(entry->header) + sizeof(colspaceW)/sizeof(WCHAR) +
721                             SysStringLen(entry->value)  + sizeof(crlfW)/sizeof(WCHAR) - 1;
722
723     list_add_head(&This->reqheaders, &entry->entry);
724
725     return S_OK;
726 }
727
728 static HRESULT WINAPI httprequest_getResponseHeader(IXMLHTTPRequest *iface, BSTR bstrHeader, BSTR *pbstrValue)
729 {
730     httprequest *This = impl_from_IXMLHTTPRequest( iface );
731
732     FIXME("stub (%p) %s %p\n", This, debugstr_w(bstrHeader), pbstrValue);
733
734     return E_NOTIMPL;
735 }
736
737 static HRESULT WINAPI httprequest_getAllResponseHeaders(IXMLHTTPRequest *iface, BSTR *pbstrHeaders)
738 {
739     httprequest *This = impl_from_IXMLHTTPRequest( iface );
740
741     FIXME("stub (%p) %p\n", This, pbstrHeaders);
742
743     return E_NOTIMPL;
744 }
745
746 static HRESULT WINAPI httprequest_send(IXMLHTTPRequest *iface, VARIANT body)
747 {
748     httprequest *This = impl_from_IXMLHTTPRequest( iface );
749     BindStatusCallback *bsc = NULL;
750     HRESULT hr;
751
752     TRACE("(%p)\n", This);
753
754     if (This->state != READYSTATE_LOADING) return E_FAIL;
755
756     hr = BindStatusCallback_create(This, &bsc, &body);
757     if (FAILED(hr)) return hr;
758
759     BindStatusCallback_Detach(This->bsc);
760     This->bsc = bsc;
761
762     return hr;
763 }
764
765 static HRESULT WINAPI httprequest_abort(IXMLHTTPRequest *iface)
766 {
767     httprequest *This = impl_from_IXMLHTTPRequest( iface );
768
769     TRACE("(%p)\n", This);
770
771     BindStatusCallback_Detach(This->bsc);
772     This->bsc = NULL;
773
774     httprequest_setreadystate(This, READYSTATE_UNINITIALIZED);
775
776     return S_OK;
777 }
778
779 static HRESULT WINAPI httprequest_get_status(IXMLHTTPRequest *iface, LONG *status)
780 {
781     httprequest *This = impl_from_IXMLHTTPRequest( iface );
782
783     TRACE("(%p)->(%p)\n", This, status);
784
785     if (!status) return E_INVALIDARG;
786     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
787
788     *status = This->status;
789
790     return S_OK;
791 }
792
793 static HRESULT WINAPI httprequest_get_statusText(IXMLHTTPRequest *iface, BSTR *pbstrStatus)
794 {
795     httprequest *This = impl_from_IXMLHTTPRequest( iface );
796
797     FIXME("stub %p %p\n", This, pbstrStatus);
798
799     return E_NOTIMPL;
800 }
801
802 static HRESULT WINAPI httprequest_get_responseXML(IXMLHTTPRequest *iface, IDispatch **body)
803 {
804     httprequest *This = impl_from_IXMLHTTPRequest( iface );
805     IXMLDOMDocument3 *doc;
806     HRESULT hr;
807     BSTR str;
808
809     TRACE("(%p)->(%p)\n", This, body);
810
811     if (!body) return E_INVALIDARG;
812     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
813
814     hr = DOMDocument_create(&CLSID_DOMDocument, NULL, (void**)&doc);
815     if (hr != S_OK) return hr;
816
817     hr = IXMLHTTPRequest_get_responseText(iface, &str);
818     if (hr == S_OK)
819     {
820         VARIANT_BOOL ok;
821
822         hr = IXMLDOMDocument3_loadXML(doc, str, &ok);
823         SysFreeString(str);
824     }
825
826     IXMLDOMDocument3_QueryInterface(doc, &IID_IDispatch, (void**)body);
827     IXMLDOMDocument3_Release(doc);
828
829     return hr;
830 }
831
832 static HRESULT WINAPI httprequest_get_responseText(IXMLHTTPRequest *iface, BSTR *body)
833 {
834     httprequest *This = impl_from_IXMLHTTPRequest( iface );
835     HGLOBAL hglobal;
836     HRESULT hr;
837
838     TRACE("(%p)->(%p)\n", This, body);
839
840     if (!body) return E_INVALIDARG;
841     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
842
843     hr = GetHGlobalFromStream(This->bsc->stream, &hglobal);
844     if (hr == S_OK)
845     {
846         xmlChar *ptr = GlobalLock(hglobal);
847         DWORD size = GlobalSize(hglobal);
848         xmlCharEncoding encoding = XML_CHAR_ENCODING_UTF8;
849
850         /* try to determine data encoding */
851         if (size >= 4)
852         {
853             encoding = xmlDetectCharEncoding(ptr, 4);
854             TRACE("detected encoding: %s\n", xmlGetCharEncodingName(encoding));
855             if ( encoding != XML_CHAR_ENCODING_UTF8 &&
856                  encoding != XML_CHAR_ENCODING_UTF16LE &&
857                  encoding != XML_CHAR_ENCODING_NONE )
858             {
859                 FIXME("unsupported encoding: %s\n", xmlGetCharEncodingName(encoding));
860                 GlobalUnlock(hglobal);
861                 return E_FAIL;
862             }
863         }
864
865         /* without BOM assume UTF-8 */
866         if (encoding == XML_CHAR_ENCODING_UTF8 ||
867             encoding == XML_CHAR_ENCODING_NONE )
868         {
869             DWORD length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)ptr, size, NULL, 0);
870
871             *body = SysAllocStringLen(NULL, length);
872             if (*body)
873                 MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)ptr, size, *body, length);
874         }
875         else
876             *body = SysAllocStringByteLen((LPCSTR)ptr, size);
877
878         if (!*body) hr = E_OUTOFMEMORY;
879         GlobalUnlock(hglobal);
880     }
881
882     return hr;
883 }
884
885 static HRESULT WINAPI httprequest_get_responseBody(IXMLHTTPRequest *iface, VARIANT *body)
886 {
887     httprequest *This = impl_from_IXMLHTTPRequest( iface );
888     HGLOBAL hglobal;
889     HRESULT hr;
890
891     TRACE("(%p)->(%p)\n", This, body);
892
893     if (!body) return E_INVALIDARG;
894     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
895
896     hr = GetHGlobalFromStream(This->bsc->stream, &hglobal);
897     if (hr == S_OK)
898     {
899         void *ptr = GlobalLock(hglobal);
900         DWORD size = GlobalSize(hglobal);
901
902         SAFEARRAYBOUND bound;
903         SAFEARRAY *array;
904
905         bound.lLbound = 0;
906         bound.cElements = size;
907         array = SafeArrayCreate(VT_UI1, 1, &bound);
908
909         if (array)
910         {
911             void *dest;
912
913             V_VT(body) = VT_ARRAY | VT_UI1;
914             V_ARRAY(body) = array;
915
916             hr = SafeArrayAccessData(array, &dest);
917             if (hr == S_OK)
918             {
919                 memcpy(dest, ptr, size);
920                 SafeArrayUnaccessData(array);
921             }
922             else
923             {
924                 VariantClear(body);
925             }
926         }
927         else
928             hr = E_FAIL;
929
930         GlobalUnlock(hglobal);
931     }
932
933     return hr;
934 }
935
936 static HRESULT WINAPI httprequest_get_responseStream(IXMLHTTPRequest *iface, VARIANT *pvarBody)
937 {
938     httprequest *This = impl_from_IXMLHTTPRequest( iface );
939
940     FIXME("stub %p %p\n", This, pvarBody);
941
942     return E_NOTIMPL;
943 }
944
945 static HRESULT WINAPI httprequest_get_readyState(IXMLHTTPRequest *iface, LONG *state)
946 {
947     httprequest *This = impl_from_IXMLHTTPRequest( iface );
948
949     TRACE("(%p)->(%p)\n", This, state);
950
951     if (!state) return E_INVALIDARG;
952
953     *state = This->state;
954     return S_OK;
955 }
956
957 static HRESULT WINAPI httprequest_put_onreadystatechange(IXMLHTTPRequest *iface, IDispatch *sink)
958 {
959     httprequest *This = impl_from_IXMLHTTPRequest( iface );
960
961     TRACE("(%p)->(%p)\n", This, sink);
962
963     if (This->sink) IDispatch_Release(This->sink);
964     if ((This->sink = sink)) IDispatch_AddRef(This->sink);
965
966     return S_OK;
967 }
968
969 static const struct IXMLHTTPRequestVtbl dimimpl_vtbl =
970 {
971     httprequest_QueryInterface,
972     httprequest_AddRef,
973     httprequest_Release,
974     httprequest_GetTypeInfoCount,
975     httprequest_GetTypeInfo,
976     httprequest_GetIDsOfNames,
977     httprequest_Invoke,
978     httprequest_open,
979     httprequest_setRequestHeader,
980     httprequest_getResponseHeader,
981     httprequest_getAllResponseHeaders,
982     httprequest_send,
983     httprequest_abort,
984     httprequest_get_status,
985     httprequest_get_statusText,
986     httprequest_get_responseXML,
987     httprequest_get_responseText,
988     httprequest_get_responseBody,
989     httprequest_get_responseStream,
990     httprequest_get_readyState,
991     httprequest_put_onreadystatechange
992 };
993
994 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
995 {
996     httprequest *req;
997     HRESULT hr = S_OK;
998
999     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
1000
1001     req = heap_alloc( sizeof (*req) );
1002     if( !req )
1003         return E_OUTOFMEMORY;
1004
1005     req->IXMLHTTPRequest_iface.lpVtbl = &dimimpl_vtbl;
1006     req->ref = 1;
1007
1008     req->async = FALSE;
1009     req->verb = -1;
1010     req->url = req->user = req->password = NULL;
1011
1012     req->state = READYSTATE_UNINITIALIZED;
1013     req->sink = NULL;
1014
1015     req->bsc = NULL;
1016     req->status = 0;
1017     req->reqheader_size = 0;
1018     list_init(&req->reqheaders);
1019
1020     *ppObj = &req->IXMLHTTPRequest_iface;
1021
1022     TRACE("returning iface %p\n", *ppObj);
1023
1024     return hr;
1025 }
1026
1027 #else
1028
1029 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
1030 {
1031     MESSAGE("This program tried to use a XMLHTTPRequest object, but\n"
1032             "libxml2 support was not present at compile time.\n");
1033     return E_NOTIMPL;
1034 }
1035
1036 #endif