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