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