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