wined3d: Merge device_unload_resource() and reset_unload_resources().
[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 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;
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     hr = VariantChangeType(&async, &async, 0, VT_BOOL);
704     This->async = hr == S_OK && V_BOOL(&async) == VARIANT_TRUE;
705
706     VariantInit(&str);
707     hr = VariantChangeType(&str, &user, 0, VT_BSTR);
708     if (hr == S_OK)
709         This->user = V_BSTR(&str);
710
711     hr = VariantChangeType(&str, &password, 0, VT_BSTR);
712     if (hr == S_OK)
713         This->password = V_BSTR(&str);
714
715     httprequest_setreadystate(This, READYSTATE_LOADING);
716
717     return S_OK;
718 }
719
720 static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR header, BSTR value)
721 {
722     httprequest *This = impl_from_IXMLHTTPRequest( iface );
723     struct reqheader *entry;
724
725     TRACE("(%p)->(%s %s)\n", This, debugstr_w(header), debugstr_w(value));
726
727     if (!header || !*header) return E_INVALIDARG;
728     if (This->state != READYSTATE_LOADING) return E_FAIL;
729     if (!value) return E_INVALIDARG;
730
731     /* replace existing header value if already added */
732     LIST_FOR_EACH_ENTRY(entry, &This->reqheaders, struct reqheader, entry)
733     {
734         if (lstrcmpW(entry->header, header) == 0)
735         {
736             LONG length = SysStringLen(entry->value);
737             HRESULT hr;
738
739             hr = SysReAllocString(&entry->value, value) ? S_OK : E_OUTOFMEMORY;
740
741             if (hr == S_OK)
742                 This->reqheader_size += (SysStringLen(entry->value) - length);
743
744             return hr;
745         }
746     }
747
748     entry = heap_alloc(sizeof(*entry));
749     if (!entry) return E_OUTOFMEMORY;
750
751     /* new header */
752     entry->header = SysAllocString(header);
753     entry->value  = SysAllocString(value);
754
755     /* header length including null terminator */
756     This->reqheader_size += SysStringLen(entry->header) + sizeof(colspaceW)/sizeof(WCHAR) +
757                             SysStringLen(entry->value)  + sizeof(crlfW)/sizeof(WCHAR) - 1;
758
759     list_add_head(&This->reqheaders, &entry->entry);
760
761     return S_OK;
762 }
763
764 static HRESULT WINAPI httprequest_getResponseHeader(IXMLHTTPRequest *iface, BSTR bstrHeader, BSTR *pbstrValue)
765 {
766     httprequest *This = impl_from_IXMLHTTPRequest( iface );
767
768     FIXME("stub (%p) %s %p\n", This, debugstr_w(bstrHeader), pbstrValue);
769
770     return E_NOTIMPL;
771 }
772
773 static HRESULT WINAPI httprequest_getAllResponseHeaders(IXMLHTTPRequest *iface, BSTR *pbstrHeaders)
774 {
775     httprequest *This = impl_from_IXMLHTTPRequest( iface );
776
777     FIXME("stub (%p) %p\n", This, pbstrHeaders);
778
779     return E_NOTIMPL;
780 }
781
782 static HRESULT WINAPI httprequest_send(IXMLHTTPRequest *iface, VARIANT body)
783 {
784     httprequest *This = impl_from_IXMLHTTPRequest( iface );
785     BindStatusCallback *bsc = NULL;
786     HRESULT hr;
787
788     TRACE("(%p)->(%s)\n", This, debugstr_variant(&body));
789
790     if (This->state != READYSTATE_LOADING) return E_FAIL;
791
792     hr = BindStatusCallback_create(This, &bsc, &body);
793     if (FAILED(hr)) return hr;
794
795     BindStatusCallback_Detach(This->bsc);
796     This->bsc = bsc;
797
798     return hr;
799 }
800
801 static HRESULT WINAPI httprequest_abort(IXMLHTTPRequest *iface)
802 {
803     httprequest *This = impl_from_IXMLHTTPRequest( iface );
804
805     TRACE("(%p)\n", This);
806
807     BindStatusCallback_Detach(This->bsc);
808     This->bsc = NULL;
809
810     httprequest_setreadystate(This, READYSTATE_UNINITIALIZED);
811
812     return S_OK;
813 }
814
815 static HRESULT WINAPI httprequest_get_status(IXMLHTTPRequest *iface, LONG *status)
816 {
817     httprequest *This = impl_from_IXMLHTTPRequest( iface );
818
819     TRACE("(%p)->(%p)\n", This, status);
820
821     if (!status) return E_INVALIDARG;
822     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
823
824     *status = This->status;
825
826     return S_OK;
827 }
828
829 static HRESULT WINAPI httprequest_get_statusText(IXMLHTTPRequest *iface, BSTR *pbstrStatus)
830 {
831     httprequest *This = impl_from_IXMLHTTPRequest( iface );
832
833     FIXME("stub %p %p\n", This, pbstrStatus);
834
835     return E_NOTIMPL;
836 }
837
838 static HRESULT WINAPI httprequest_get_responseXML(IXMLHTTPRequest *iface, IDispatch **body)
839 {
840     httprequest *This = impl_from_IXMLHTTPRequest( iface );
841     IXMLDOMDocument3 *doc;
842     HRESULT hr;
843     BSTR str;
844
845     TRACE("(%p)->(%p)\n", This, body);
846
847     if (!body) return E_INVALIDARG;
848     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
849
850     hr = DOMDocument_create(&CLSID_DOMDocument, NULL, (void**)&doc);
851     if (hr != S_OK) return hr;
852
853     hr = IXMLHTTPRequest_get_responseText(iface, &str);
854     if (hr == S_OK)
855     {
856         VARIANT_BOOL ok;
857
858         hr = IXMLDOMDocument3_loadXML(doc, str, &ok);
859         SysFreeString(str);
860     }
861
862     IXMLDOMDocument3_QueryInterface(doc, &IID_IDispatch, (void**)body);
863     IXMLDOMDocument3_Release(doc);
864
865     return hr;
866 }
867
868 static HRESULT WINAPI httprequest_get_responseText(IXMLHTTPRequest *iface, BSTR *body)
869 {
870     httprequest *This = impl_from_IXMLHTTPRequest( iface );
871     HGLOBAL hglobal;
872     HRESULT hr;
873
874     TRACE("(%p)->(%p)\n", This, body);
875
876     if (!body) return E_INVALIDARG;
877     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
878
879     hr = GetHGlobalFromStream(This->bsc->stream, &hglobal);
880     if (hr == S_OK)
881     {
882         xmlChar *ptr = GlobalLock(hglobal);
883         DWORD size = GlobalSize(hglobal);
884         xmlCharEncoding encoding = XML_CHAR_ENCODING_UTF8;
885
886         /* try to determine data encoding */
887         if (size >= 4)
888         {
889             encoding = xmlDetectCharEncoding(ptr, 4);
890             TRACE("detected encoding: %s\n", xmlGetCharEncodingName(encoding));
891             if ( encoding != XML_CHAR_ENCODING_UTF8 &&
892                  encoding != XML_CHAR_ENCODING_UTF16LE &&
893                  encoding != XML_CHAR_ENCODING_NONE )
894             {
895                 FIXME("unsupported encoding: %s\n", xmlGetCharEncodingName(encoding));
896                 GlobalUnlock(hglobal);
897                 return E_FAIL;
898             }
899         }
900
901         /* without BOM assume UTF-8 */
902         if (encoding == XML_CHAR_ENCODING_UTF8 ||
903             encoding == XML_CHAR_ENCODING_NONE )
904         {
905             DWORD length = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)ptr, size, NULL, 0);
906
907             *body = SysAllocStringLen(NULL, length);
908             if (*body)
909                 MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)ptr, size, *body, length);
910         }
911         else
912             *body = SysAllocStringByteLen((LPCSTR)ptr, size);
913
914         if (!*body) hr = E_OUTOFMEMORY;
915         GlobalUnlock(hglobal);
916     }
917
918     return hr;
919 }
920
921 static HRESULT WINAPI httprequest_get_responseBody(IXMLHTTPRequest *iface, VARIANT *body)
922 {
923     httprequest *This = impl_from_IXMLHTTPRequest( iface );
924     HGLOBAL hglobal;
925     HRESULT hr;
926
927     TRACE("(%p)->(%p)\n", This, body);
928
929     if (!body) return E_INVALIDARG;
930     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
931
932     hr = GetHGlobalFromStream(This->bsc->stream, &hglobal);
933     if (hr == S_OK)
934     {
935         void *ptr = GlobalLock(hglobal);
936         DWORD size = GlobalSize(hglobal);
937
938         SAFEARRAYBOUND bound;
939         SAFEARRAY *array;
940
941         bound.lLbound = 0;
942         bound.cElements = size;
943         array = SafeArrayCreate(VT_UI1, 1, &bound);
944
945         if (array)
946         {
947             void *dest;
948
949             V_VT(body) = VT_ARRAY | VT_UI1;
950             V_ARRAY(body) = array;
951
952             hr = SafeArrayAccessData(array, &dest);
953             if (hr == S_OK)
954             {
955                 memcpy(dest, ptr, size);
956                 SafeArrayUnaccessData(array);
957             }
958             else
959             {
960                 VariantClear(body);
961             }
962         }
963         else
964             hr = E_FAIL;
965
966         GlobalUnlock(hglobal);
967     }
968
969     return hr;
970 }
971
972 static HRESULT WINAPI httprequest_get_responseStream(IXMLHTTPRequest *iface, VARIANT *pvarBody)
973 {
974     httprequest *This = impl_from_IXMLHTTPRequest( iface );
975
976     FIXME("stub %p %p\n", This, pvarBody);
977
978     return E_NOTIMPL;
979 }
980
981 static HRESULT WINAPI httprequest_get_readyState(IXMLHTTPRequest *iface, LONG *state)
982 {
983     httprequest *This = impl_from_IXMLHTTPRequest( iface );
984
985     TRACE("(%p)->(%p)\n", This, state);
986
987     if (!state) return E_INVALIDARG;
988
989     *state = This->state;
990     return S_OK;
991 }
992
993 static HRESULT WINAPI httprequest_put_onreadystatechange(IXMLHTTPRequest *iface, IDispatch *sink)
994 {
995     httprequest *This = impl_from_IXMLHTTPRequest( iface );
996
997     TRACE("(%p)->(%p)\n", This, sink);
998
999     if (This->sink) IDispatch_Release(This->sink);
1000     if ((This->sink = sink)) IDispatch_AddRef(This->sink);
1001
1002     return S_OK;
1003 }
1004
1005 static const struct IXMLHTTPRequestVtbl dimimpl_vtbl =
1006 {
1007     httprequest_QueryInterface,
1008     httprequest_AddRef,
1009     httprequest_Release,
1010     httprequest_GetTypeInfoCount,
1011     httprequest_GetTypeInfo,
1012     httprequest_GetIDsOfNames,
1013     httprequest_Invoke,
1014     httprequest_open,
1015     httprequest_setRequestHeader,
1016     httprequest_getResponseHeader,
1017     httprequest_getAllResponseHeaders,
1018     httprequest_send,
1019     httprequest_abort,
1020     httprequest_get_status,
1021     httprequest_get_statusText,
1022     httprequest_get_responseXML,
1023     httprequest_get_responseText,
1024     httprequest_get_responseBody,
1025     httprequest_get_responseStream,
1026     httprequest_get_readyState,
1027     httprequest_put_onreadystatechange
1028 };
1029
1030 /* IObjectWithSite */
1031 static HRESULT WINAPI
1032 httprequest_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
1033 {
1034     httprequest *This = impl_from_IObjectWithSite(iface);
1035     return IXMLHTTPRequest_QueryInterface( (IXMLHTTPRequest *)This, riid, ppvObject );
1036 }
1037
1038 static ULONG WINAPI httprequest_ObjectWithSite_AddRef( IObjectWithSite* iface )
1039 {
1040     httprequest *This = impl_from_IObjectWithSite(iface);
1041     return IXMLHTTPRequest_AddRef((IXMLHTTPRequest *)This);
1042 }
1043
1044 static ULONG WINAPI httprequest_ObjectWithSite_Release( IObjectWithSite* iface )
1045 {
1046     httprequest *This = impl_from_IObjectWithSite(iface);
1047     return IXMLHTTPRequest_Release((IXMLHTTPRequest *)This);
1048 }
1049
1050 static HRESULT WINAPI httprequest_ObjectWithSite_GetSite( IObjectWithSite *iface, REFIID iid, void **ppvSite )
1051 {
1052     httprequest *This = impl_from_IObjectWithSite(iface);
1053
1054     TRACE("(%p)->(%s %p)\n", This, debugstr_guid( iid ), ppvSite );
1055
1056     if ( !This->site )
1057         return E_FAIL;
1058
1059     return IUnknown_QueryInterface( This->site, iid, ppvSite );
1060 }
1061
1062 static HRESULT WINAPI httprequest_ObjectWithSite_SetSite( IObjectWithSite *iface, IUnknown *punk )
1063 {
1064     httprequest *This = impl_from_IObjectWithSite(iface);
1065
1066     TRACE("(%p)->(%p)\n", iface, punk);
1067
1068     if (punk)
1069         IUnknown_AddRef( punk );
1070
1071     if(This->site)
1072         IUnknown_Release( This->site );
1073
1074     This->site = punk;
1075
1076     return S_OK;
1077 }
1078
1079 static const IObjectWithSiteVtbl httprequestObjectSite =
1080 {
1081     httprequest_ObjectWithSite_QueryInterface,
1082     httprequest_ObjectWithSite_AddRef,
1083     httprequest_ObjectWithSite_Release,
1084     httprequest_ObjectWithSite_SetSite,
1085     httprequest_ObjectWithSite_GetSite
1086 };
1087
1088 /* IObjectSafety */
1089 static HRESULT WINAPI httprequest_Safety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
1090 {
1091     httprequest *This = impl_from_IObjectSafety(iface);
1092     return IXMLHTTPRequest_QueryInterface( (IXMLHTTPRequest *)This, riid, ppv );
1093 }
1094
1095 static ULONG WINAPI httprequest_Safety_AddRef(IObjectSafety *iface)
1096 {
1097     httprequest *This = impl_from_IObjectSafety(iface);
1098     return IXMLHTTPRequest_AddRef((IXMLHTTPRequest *)This);
1099 }
1100
1101 static ULONG WINAPI httprequest_Safety_Release(IObjectSafety *iface)
1102 {
1103     httprequest *This = impl_from_IObjectSafety(iface);
1104     return IXMLHTTPRequest_Release((IXMLHTTPRequest *)This);
1105 }
1106
1107 #define SAFETY_SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_CALLER|INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_SECURITY_MANAGER)
1108
1109 static HRESULT WINAPI httprequest_Safety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
1110         DWORD *supported, DWORD *enabled)
1111 {
1112     httprequest *This = impl_from_IObjectSafety(iface);
1113
1114     TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), supported, enabled);
1115
1116     if(!supported || !enabled) return E_POINTER;
1117
1118     *supported = SAFETY_SUPPORTED_OPTIONS;
1119     *enabled = This->safeopt;
1120
1121     return S_OK;
1122 }
1123
1124 static HRESULT WINAPI httprequest_Safety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
1125         DWORD mask, DWORD enabled)
1126 {
1127     httprequest *This = impl_from_IObjectSafety(iface);
1128     TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), mask, enabled);
1129
1130     if ((mask & ~SAFETY_SUPPORTED_OPTIONS) != 0)
1131         return E_FAIL;
1132
1133     This->safeopt = enabled & mask & SAFETY_SUPPORTED_OPTIONS;
1134     return S_OK;
1135 }
1136
1137 #undef SAFETY_SUPPORTED_OPTIONS
1138
1139 static const IObjectSafetyVtbl httprequestObjectSafety = {
1140     httprequest_Safety_QueryInterface,
1141     httprequest_Safety_AddRef,
1142     httprequest_Safety_Release,
1143     httprequest_Safety_GetInterfaceSafetyOptions,
1144     httprequest_Safety_SetInterfaceSafetyOptions
1145 };
1146
1147 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
1148 {
1149     httprequest *req;
1150     HRESULT hr = S_OK;
1151
1152     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
1153
1154     req = heap_alloc( sizeof (*req) );
1155     if( !req )
1156         return E_OUTOFMEMORY;
1157
1158     req->IXMLHTTPRequest_iface.lpVtbl = &dimimpl_vtbl;
1159     req->IObjectWithSite_iface.lpVtbl = &httprequestObjectSite;
1160     req->IObjectSafety_iface.lpVtbl = &httprequestObjectSafety;
1161     req->ref = 1;
1162
1163     req->async = FALSE;
1164     req->verb = -1;
1165     req->url = req->user = req->password = NULL;
1166
1167     req->state = READYSTATE_UNINITIALIZED;
1168     req->sink = NULL;
1169
1170     req->bsc = NULL;
1171     req->status = 0;
1172     req->reqheader_size = 0;
1173     list_init(&req->reqheaders);
1174     req->site = NULL;
1175     req->safeopt = 0;
1176
1177     *ppObj = &req->IXMLHTTPRequest_iface;
1178
1179     TRACE("returning iface %p\n", *ppObj);
1180
1181     return hr;
1182 }
1183
1184 #else
1185
1186 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
1187 {
1188     MESSAGE("This program tried to use a XMLHTTPRequest object, but\n"
1189             "libxml2 support was not present at compile time.\n");
1190     return E_NOTIMPL;
1191 }
1192
1193 #endif