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