comdlg32: Correct title of "Save As" dialog.
[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     if (This->state != READYSTATE_COMPLETE) return E_FAIL;
930
931     hr = GetHGlobalFromStream(This->bsc->stream, &hglobal);
932     if (hr == S_OK)
933     {
934         void *ptr = GlobalLock(hglobal);
935         DWORD size = GlobalSize(hglobal);
936
937         SAFEARRAYBOUND bound;
938         SAFEARRAY *array;
939
940         bound.lLbound = 0;
941         bound.cElements = size;
942         array = SafeArrayCreate(VT_UI1, 1, &bound);
943
944         if (array)
945         {
946             void *dest;
947
948             V_VT(body) = VT_ARRAY | VT_UI1;
949             V_ARRAY(body) = array;
950
951             hr = SafeArrayAccessData(array, &dest);
952             if (hr == S_OK)
953             {
954                 memcpy(dest, ptr, size);
955                 SafeArrayUnaccessData(array);
956             }
957             else
958             {
959                 VariantClear(body);
960             }
961         }
962         else
963             hr = E_FAIL;
964
965         GlobalUnlock(hglobal);
966     }
967
968     return hr;
969 }
970
971 static HRESULT WINAPI httprequest_get_responseStream(IXMLHTTPRequest *iface, VARIANT *pvarBody)
972 {
973     httprequest *This = impl_from_IXMLHTTPRequest( iface );
974
975     FIXME("stub %p %p\n", This, pvarBody);
976
977     return E_NOTIMPL;
978 }
979
980 static HRESULT WINAPI httprequest_get_readyState(IXMLHTTPRequest *iface, LONG *state)
981 {
982     httprequest *This = impl_from_IXMLHTTPRequest( iface );
983
984     TRACE("(%p)->(%p)\n", This, state);
985
986     if (!state) return E_INVALIDARG;
987
988     *state = This->state;
989     return S_OK;
990 }
991
992 static HRESULT WINAPI httprequest_put_onreadystatechange(IXMLHTTPRequest *iface, IDispatch *sink)
993 {
994     httprequest *This = impl_from_IXMLHTTPRequest( iface );
995
996     TRACE("(%p)->(%p)\n", This, sink);
997
998     if (This->sink) IDispatch_Release(This->sink);
999     if ((This->sink = sink)) IDispatch_AddRef(This->sink);
1000
1001     return S_OK;
1002 }
1003
1004 static const struct IXMLHTTPRequestVtbl dimimpl_vtbl =
1005 {
1006     httprequest_QueryInterface,
1007     httprequest_AddRef,
1008     httprequest_Release,
1009     httprequest_GetTypeInfoCount,
1010     httprequest_GetTypeInfo,
1011     httprequest_GetIDsOfNames,
1012     httprequest_Invoke,
1013     httprequest_open,
1014     httprequest_setRequestHeader,
1015     httprequest_getResponseHeader,
1016     httprequest_getAllResponseHeaders,
1017     httprequest_send,
1018     httprequest_abort,
1019     httprequest_get_status,
1020     httprequest_get_statusText,
1021     httprequest_get_responseXML,
1022     httprequest_get_responseText,
1023     httprequest_get_responseBody,
1024     httprequest_get_responseStream,
1025     httprequest_get_readyState,
1026     httprequest_put_onreadystatechange
1027 };
1028
1029 /* IObjectWithSite */
1030 static HRESULT WINAPI
1031 httprequest_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
1032 {
1033     httprequest *This = impl_from_IObjectWithSite(iface);
1034     return IXMLHTTPRequest_QueryInterface( (IXMLHTTPRequest *)This, riid, ppvObject );
1035 }
1036
1037 static ULONG WINAPI httprequest_ObjectWithSite_AddRef( IObjectWithSite* iface )
1038 {
1039     httprequest *This = impl_from_IObjectWithSite(iface);
1040     return IXMLHTTPRequest_AddRef((IXMLHTTPRequest *)This);
1041 }
1042
1043 static ULONG WINAPI httprequest_ObjectWithSite_Release( IObjectWithSite* iface )
1044 {
1045     httprequest *This = impl_from_IObjectWithSite(iface);
1046     return IXMLHTTPRequest_Release((IXMLHTTPRequest *)This);
1047 }
1048
1049 static HRESULT WINAPI httprequest_ObjectWithSite_GetSite( IObjectWithSite *iface, REFIID iid, void **ppvSite )
1050 {
1051     httprequest *This = impl_from_IObjectWithSite(iface);
1052
1053     TRACE("(%p)->(%s %p)\n", This, debugstr_guid( iid ), ppvSite );
1054
1055     if ( !This->site )
1056         return E_FAIL;
1057
1058     return IUnknown_QueryInterface( This->site, iid, ppvSite );
1059 }
1060
1061 static HRESULT WINAPI httprequest_ObjectWithSite_SetSite( IObjectWithSite *iface, IUnknown *punk )
1062 {
1063     httprequest *This = impl_from_IObjectWithSite(iface);
1064
1065     TRACE("(%p)->(%p)\n", iface, punk);
1066
1067     if (punk)
1068         IUnknown_AddRef( punk );
1069
1070     if(This->site)
1071         IUnknown_Release( This->site );
1072
1073     This->site = punk;
1074
1075     return S_OK;
1076 }
1077
1078 static const IObjectWithSiteVtbl httprequestObjectSite =
1079 {
1080     httprequest_ObjectWithSite_QueryInterface,
1081     httprequest_ObjectWithSite_AddRef,
1082     httprequest_ObjectWithSite_Release,
1083     httprequest_ObjectWithSite_SetSite,
1084     httprequest_ObjectWithSite_GetSite
1085 };
1086
1087 /* IObjectSafety */
1088 static HRESULT WINAPI httprequest_Safety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
1089 {
1090     httprequest *This = impl_from_IObjectSafety(iface);
1091     return IXMLHTTPRequest_QueryInterface( (IXMLHTTPRequest *)This, riid, ppv );
1092 }
1093
1094 static ULONG WINAPI httprequest_Safety_AddRef(IObjectSafety *iface)
1095 {
1096     httprequest *This = impl_from_IObjectSafety(iface);
1097     return IXMLHTTPRequest_AddRef((IXMLHTTPRequest *)This);
1098 }
1099
1100 static ULONG WINAPI httprequest_Safety_Release(IObjectSafety *iface)
1101 {
1102     httprequest *This = impl_from_IObjectSafety(iface);
1103     return IXMLHTTPRequest_Release((IXMLHTTPRequest *)This);
1104 }
1105
1106 #define SAFETY_SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_CALLER|INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_SECURITY_MANAGER)
1107
1108 static HRESULT WINAPI httprequest_Safety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
1109         DWORD *supported, DWORD *enabled)
1110 {
1111     httprequest *This = impl_from_IObjectSafety(iface);
1112
1113     TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), supported, enabled);
1114
1115     if(!supported || !enabled) return E_POINTER;
1116
1117     *supported = SAFETY_SUPPORTED_OPTIONS;
1118     *enabled = This->safeopt;
1119
1120     return S_OK;
1121 }
1122
1123 static HRESULT WINAPI httprequest_Safety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
1124         DWORD mask, DWORD enabled)
1125 {
1126     httprequest *This = impl_from_IObjectSafety(iface);
1127     TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), mask, enabled);
1128
1129     if ((mask & ~SAFETY_SUPPORTED_OPTIONS) != 0)
1130         return E_FAIL;
1131
1132     This->safeopt = (This->safeopt & ~mask) | (mask & enabled);
1133
1134     return S_OK;
1135 }
1136
1137 #undef SAFETY_SUPPORTED_OPTIONS
1138
1139 static const IObjectSafetyVtbl httprequestObjectSafety = {
1140     httprequest_Safety_QueryInterface,
1141     httprequest_Safety_AddRef,
1142     httprequest_Safety_Release,
1143     httprequest_Safety_GetInterfaceSafetyOptions,
1144     httprequest_Safety_SetInterfaceSafetyOptions
1145 };
1146
1147 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
1148 {
1149     httprequest *req;
1150     HRESULT hr = S_OK;
1151
1152     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
1153
1154     req = heap_alloc( sizeof (*req) );
1155     if( !req )
1156         return E_OUTOFMEMORY;
1157
1158     req->IXMLHTTPRequest_iface.lpVtbl = &dimimpl_vtbl;
1159     req->IObjectWithSite_iface.lpVtbl = &httprequestObjectSite;
1160     req->IObjectSafety_iface.lpVtbl = &httprequestObjectSafety;
1161     req->ref = 1;
1162
1163     req->async = FALSE;
1164     req->verb = -1;
1165     req->url = req->user = req->password = NULL;
1166
1167     req->state = READYSTATE_UNINITIALIZED;
1168     req->sink = NULL;
1169
1170     req->bsc = NULL;
1171     req->status = 0;
1172     req->reqheader_size = 0;
1173     list_init(&req->reqheaders);
1174     req->site = NULL;
1175     req->safeopt = 0;
1176
1177     *ppObj = &req->IXMLHTTPRequest_iface;
1178
1179     TRACE("returning iface %p\n", *ppObj);
1180
1181     return hr;
1182 }
1183
1184 #else
1185
1186 HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, void **ppObj)
1187 {
1188     MESSAGE("This program tried to use a XMLHTTPRequest object, but\n"
1189             "libxml2 support was not present at compile time.\n");
1190     return E_NOTIMPL;
1191 }
1192
1193 #endif