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