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