msxml3: Improved checking of failed case.
[wine] / dlls / msxml3 / xmldoc.c
1 /*
2  * XML Document implementation
3  *
4  * Copyright 2007 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "ole2.h"
30 #include "msxml2.h"
31 #include "wininet.h"
32 #include "winreg.h"
33 #include "shlwapi.h"
34 #include "ocidl.h"
35
36 #include "wine/debug.h"
37
38 #include "msxml_private.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
41
42 #ifdef HAVE_LIBXML2
43
44 /* FIXME: IXMLDocument needs to implement
45  *   - IXMLError
46  *   - IPersistMoniker
47  */
48
49 typedef struct _xmldoc
50 {
51     const IXMLDocumentVtbl       *lpVtbl;
52     const IPersistStreamInitVtbl *lpvtblIPersistStreamInit;
53     LONG ref;
54     HRESULT error;
55
56     /* IXMLDocument */
57     IXMLElement *root;
58     xmlDocPtr xmldoc;
59
60     /* IPersistStream */
61     IStream *stream;
62 } xmldoc;
63
64 static inline xmldoc *impl_from_IXMLDocument(IXMLDocument *iface)
65 {
66     return (xmldoc *)((char*)iface - FIELD_OFFSET(xmldoc, lpVtbl));
67 }
68
69 static inline xmldoc *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
70 {
71     return (xmldoc *)((char*)iface - FIELD_OFFSET(xmldoc, lpvtblIPersistStreamInit));
72 }
73
74 static HRESULT WINAPI xmldoc_QueryInterface(IXMLDocument *iface, REFIID riid, void** ppvObject)
75 {
76     xmldoc *This = impl_from_IXMLDocument(iface);
77
78     TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
79
80     if (IsEqualGUID(riid, &IID_IUnknown) ||
81         IsEqualGUID(riid, &IID_IXMLDocument) ||
82         IsEqualGUID(riid, &IID_IXMLDOMDocument))
83     {
84         *ppvObject = iface;
85     }
86     else if (IsEqualGUID(&IID_IPersistStreamInit, riid) ||
87              IsEqualGUID(&IID_IPersistStream, riid))
88     {
89         *ppvObject = (IPersistStreamInit *)&(This->lpvtblIPersistStreamInit);
90     }
91     else
92     {
93         FIXME("interface %s not implemented\n", debugstr_guid(riid));
94         return E_NOINTERFACE;
95     }
96
97     IXMLDocument_AddRef(iface);
98
99     return S_OK;
100 }
101
102 static ULONG WINAPI xmldoc_AddRef(IXMLDocument *iface)
103 {
104     xmldoc *This = impl_from_IXMLDocument(iface);
105     TRACE("%p\n", This);
106     return InterlockedIncrement(&This->ref);
107 }
108
109 static ULONG WINAPI xmldoc_Release(IXMLDocument *iface)
110 {
111     xmldoc *This = impl_from_IXMLDocument(iface);
112     LONG ref;
113
114     TRACE("%p\n", This);
115
116     ref = InterlockedDecrement(&This->ref);
117     if (ref == 0)
118     {
119         xmlFreeDoc(This->xmldoc);
120         if (This->stream) IStream_Release(This->stream);
121         HeapFree(GetProcessHeap(), 0, This);
122     }
123
124     return ref;
125 }
126
127 static HRESULT WINAPI xmldoc_GetTypeInfoCount(IXMLDocument *iface, UINT* pctinfo)
128 {
129     xmldoc *This = impl_from_IXMLDocument(iface);
130
131     TRACE("(%p)->(%p)\n", This, pctinfo);
132
133     *pctinfo = 1;
134
135     return S_OK;
136 }
137
138 static HRESULT WINAPI xmldoc_GetTypeInfo(IXMLDocument *iface, UINT iTInfo,
139                                          LCID lcid, ITypeInfo** ppTInfo)
140 {
141     xmldoc *This = impl_from_IXMLDocument(iface);
142     HRESULT hr;
143
144     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
145
146     hr = get_typeinfo(IXMLDocument_tid, ppTInfo);
147
148     return hr;
149 }
150
151 static HRESULT WINAPI xmldoc_GetIDsOfNames(IXMLDocument *iface, REFIID riid,
152                                            LPOLESTR* rgszNames, UINT cNames,
153                                            LCID lcid, DISPID* rgDispId)
154 {
155     xmldoc *This = impl_from_IXMLDocument(iface);
156     ITypeInfo *typeinfo;
157     HRESULT hr;
158
159     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
160           lcid, rgDispId);
161
162     if(!rgszNames || cNames == 0 || !rgDispId)
163         return E_INVALIDARG;
164
165     hr = get_typeinfo(IXMLDocument_tid, &typeinfo);
166     if(SUCCEEDED(hr))
167     {
168         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
169         ITypeInfo_Release(typeinfo);
170     }
171
172     return hr;
173 }
174
175 static HRESULT WINAPI xmldoc_Invoke(IXMLDocument *iface, DISPID dispIdMember,
176                                     REFIID riid, LCID lcid, WORD wFlags,
177                                     DISPPARAMS* pDispParams, VARIANT* pVarResult,
178                                     EXCEPINFO* pExcepInfo, UINT* puArgErr)
179 {
180     xmldoc *This = impl_from_IXMLDocument(iface);
181     ITypeInfo *typeinfo;
182     HRESULT hr;
183
184     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
185           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
186
187     hr = get_typeinfo(IXMLDocument_tid, &typeinfo);
188     if(SUCCEEDED(hr))
189     {
190         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
191                 pVarResult, pExcepInfo, puArgErr);
192         ITypeInfo_Release(typeinfo);
193     }
194
195     return hr;
196 }
197
198 static HRESULT WINAPI xmldoc_get_root(IXMLDocument *iface, IXMLElement **p)
199 {
200     xmldoc *This = impl_from_IXMLDocument(iface);
201
202     TRACE("(%p, %p)\n", iface, p);
203
204     if (!p)
205         return E_INVALIDARG;
206
207     *p = This->root;
208     if (!*p)
209         return E_FAIL;
210
211     return S_OK;
212 }
213
214 static HRESULT WINAPI xmldoc_get_fileSize(IXMLDocument *iface, BSTR *p)
215 {
216     FIXME("(%p, %p): stub\n", iface, p);
217     return E_NOTIMPL;
218 }
219
220 static HRESULT WINAPI xmldoc_put_fileModifiedDate(IXMLDocument *iface, BSTR *p)
221 {
222     FIXME("(%p, %p): stub\n", iface, p);
223     return E_NOTIMPL;
224 }
225
226 static HRESULT WINAPI xmldoc_get_fileUpdatedDate(IXMLDocument *iface, BSTR *p)
227 {
228     FIXME("(%p, %p): stub\n", iface, p);
229     return E_NOTIMPL;
230 }
231
232 static HRESULT WINAPI xmldoc_get_URL(IXMLDocument *iface, BSTR *p)
233 {
234     FIXME("(%p, %p): stub\n", iface, p);
235     return E_NOTIMPL;
236 }
237
238 typedef struct {
239     const struct IBindStatusCallbackVtbl *lpVtbl;
240 } bsc;
241
242 static HRESULT WINAPI bsc_QueryInterface(
243     IBindStatusCallback *iface,
244     REFIID riid,
245     LPVOID *ppobj )
246 {
247     if (IsEqualGUID(riid, &IID_IUnknown) ||
248         IsEqualGUID(riid, &IID_IBindStatusCallback))
249     {
250         IBindStatusCallback_AddRef( iface );
251         *ppobj = iface;
252         return S_OK;
253     }
254
255     FIXME("interface %s not implemented\n", debugstr_guid(riid));
256     return E_NOINTERFACE;
257 }
258
259 static ULONG WINAPI bsc_AddRef(
260     IBindStatusCallback *iface )
261 {
262     return 2;
263 }
264
265 static ULONG WINAPI bsc_Release(
266     IBindStatusCallback *iface )
267 {
268     return 1;
269 }
270
271 static HRESULT WINAPI bsc_OnStartBinding(
272         IBindStatusCallback* iface,
273         DWORD dwReserved,
274         IBinding* pib)
275 {
276     return S_OK;
277 }
278
279 static HRESULT WINAPI bsc_GetPriority(
280         IBindStatusCallback* iface,
281         LONG* pnPriority)
282 {
283     return S_OK;
284 }
285
286 static HRESULT WINAPI bsc_OnLowResource(
287         IBindStatusCallback* iface,
288         DWORD reserved)
289 {
290     return S_OK;
291 }
292
293 static HRESULT WINAPI bsc_OnProgress(
294         IBindStatusCallback* iface,
295         ULONG ulProgress,
296         ULONG ulProgressMax,
297         ULONG ulStatusCode,
298         LPCWSTR szStatusText)
299 {
300     return S_OK;
301 }
302
303 static HRESULT WINAPI bsc_OnStopBinding(
304         IBindStatusCallback* iface,
305         HRESULT hresult,
306         LPCWSTR szError)
307 {
308     return S_OK;
309 }
310
311 static HRESULT WINAPI bsc_GetBindInfo(
312         IBindStatusCallback* iface,
313         DWORD* grfBINDF,
314         BINDINFO* pbindinfo)
315 {
316     *grfBINDF = BINDF_RESYNCHRONIZE;
317
318     return S_OK;
319 }
320
321 static HRESULT WINAPI bsc_OnDataAvailable(
322         IBindStatusCallback* iface,
323         DWORD grfBSCF,
324         DWORD dwSize,
325         FORMATETC* pformatetc,
326         STGMEDIUM* pstgmed)
327 {
328     return S_OK;
329 }
330
331 static HRESULT WINAPI bsc_OnObjectAvailable(
332         IBindStatusCallback* iface,
333         REFIID riid,
334         IUnknown* punk)
335 {
336     return S_OK;
337 }
338
339 static const struct IBindStatusCallbackVtbl bsc_vtbl =
340 {
341     bsc_QueryInterface,
342     bsc_AddRef,
343     bsc_Release,
344     bsc_OnStartBinding,
345     bsc_GetPriority,
346     bsc_OnLowResource,
347     bsc_OnProgress,
348     bsc_OnStopBinding,
349     bsc_GetBindInfo,
350     bsc_OnDataAvailable,
351     bsc_OnObjectAvailable
352 };
353
354 static bsc xmldoc_bsc = { &bsc_vtbl };
355
356 static HRESULT WINAPI xmldoc_put_URL(IXMLDocument *iface, BSTR p)
357 {
358     WCHAR url[INTERNET_MAX_URL_LENGTH];
359     IStream *stream;
360     IBindCtx *bctx;
361     IMoniker *moniker;
362     IPersistStreamInit *persist;
363     HRESULT hr;
364
365     TRACE("(%p, %s)\n", iface, debugstr_w(p));
366
367     if (!p)
368         return E_INVALIDARG;
369
370     if (!PathIsURLW(p))
371     {
372         WCHAR fullpath[MAX_PATH];
373         DWORD needed = sizeof(url) / sizeof(WCHAR);
374
375         if (!PathSearchAndQualifyW(p, fullpath, sizeof(fullpath) / sizeof(WCHAR)))
376         {
377             ERR("can't find path\n");
378             return E_FAIL;
379         }
380
381         if (FAILED(UrlCreateFromPathW(fullpath, url, &needed, 0)))
382         {
383             ERR("can't create url from path\n");
384             return E_FAIL;
385         }
386
387         p = url;
388     }
389
390     hr = CreateURLMoniker(NULL, p, &moniker);
391     if (FAILED(hr))
392         return hr;
393
394     CreateAsyncBindCtx(0, (IBindStatusCallback *)&xmldoc_bsc, 0, &bctx);
395
396     hr = IMoniker_BindToStorage(moniker, bctx, NULL, &IID_IStream, (LPVOID *)&stream);
397     IBindCtx_Release(bctx);
398     IMoniker_Release(moniker);
399     if (FAILED(hr))
400         return hr;
401
402     hr = IXMLDocument_QueryInterface(iface, &IID_IPersistStreamInit, (LPVOID *)&persist);
403     if (FAILED(hr))
404     {
405         IStream_Release(stream);
406         return hr;
407     }
408
409     hr = IPersistStreamInit_Load(persist, stream);
410     IPersistStreamInit_Release(persist);
411     IStream_Release(stream);
412
413     return hr;
414 }
415
416 static HRESULT WINAPI xmldoc_get_mimeType(IXMLDocument *iface, BSTR *p)
417 {
418     FIXME("(%p, %p): stub\n", iface, p);
419     return E_NOTIMPL;
420 }
421
422 static HRESULT WINAPI xmldoc_get_readyState(IXMLDocument *iface, long *p)
423 {
424     FIXME("(%p, %p): stub\n", iface, p);
425     return E_NOTIMPL;
426 }
427
428 static HRESULT WINAPI xmldoc_get_charset(IXMLDocument *iface, BSTR *p)
429 {
430     FIXME("(%p, %p): stub\n", iface, p);
431     return E_NOTIMPL;
432 }
433
434 static HRESULT WINAPI xmldoc_put_charset(IXMLDocument *iface, BSTR p)
435 {
436     FIXME("(%p, %p): stub\n", iface, p);
437     return E_NOTIMPL;
438 }
439
440 static HRESULT WINAPI xmldoc_get_version(IXMLDocument *iface, BSTR *p)
441 {
442     FIXME("(%p, %p): stub\n", iface, p);
443     return E_NOTIMPL;
444 }
445
446 static HRESULT WINAPI xmldoc_get_doctype(IXMLDocument *iface, BSTR *p)
447 {
448     FIXME("(%p, %p): stub\n", iface, p);
449     return E_NOTIMPL;
450 }
451
452 static HRESULT WINAPI xmldoc_get_dtdURl(IXMLDocument *iface, BSTR *p)
453 {
454     FIXME("(%p, %p): stub\n", iface, p);
455     return E_NOTIMPL;
456 }
457
458 static xmlElementType type_msxml_to_libxml(long type)
459 {
460     switch (type)
461     {
462         case XMLELEMTYPE_ELEMENT:
463             return XML_ELEMENT_NODE;
464         case XMLELEMTYPE_TEXT:
465             return XML_TEXT_NODE;
466         case XMLELEMTYPE_COMMENT:
467             return XML_COMMENT_NODE;
468         case XMLELEMTYPE_DOCUMENT:
469             return XML_DOCUMENT_NODE;
470         case XMLELEMTYPE_DTD:
471             return XML_DTD_NODE;
472         case XMLELEMTYPE_PI:
473             return XML_PI_NODE;
474         default:
475             break;
476     }
477
478     return -1; /* FIXME: what is OTHER in msxml? */
479 }
480
481 static HRESULT WINAPI xmldoc_createElement(IXMLDocument *iface, VARIANT vType,
482                                            VARIANT var1, IXMLElement **ppElem)
483 {
484     xmlNodePtr node;
485     static const xmlChar empty[] = "\0";
486
487     TRACE("(%p, %p)\n", iface, ppElem);
488
489     if (!ppElem)
490         return E_INVALIDARG;
491
492     *ppElem = NULL;
493
494     if (V_VT(&vType) != VT_I4)
495         return E_INVALIDARG;
496
497     if(type_msxml_to_libxml(V_I4(&vType)) == -1)
498         return E_NOTIMPL;
499
500     node = xmlNewNode(NULL, empty);
501     node->type = type_msxml_to_libxml(V_I4(&vType));
502
503     /* FIXME: create xmlNodePtr based on vType and var1 */
504     return XMLElement_create((IUnknown *)iface, node, (LPVOID *)ppElem);
505 }
506
507 static const struct IXMLDocumentVtbl xmldoc_vtbl =
508 {
509     xmldoc_QueryInterface,
510     xmldoc_AddRef,
511     xmldoc_Release,
512     xmldoc_GetTypeInfoCount,
513     xmldoc_GetTypeInfo,
514     xmldoc_GetIDsOfNames,
515     xmldoc_Invoke,
516     xmldoc_get_root,
517     xmldoc_get_fileSize,
518     xmldoc_put_fileModifiedDate,
519     xmldoc_get_fileUpdatedDate,
520     xmldoc_get_URL,
521     xmldoc_put_URL,
522     xmldoc_get_mimeType,
523     xmldoc_get_readyState,
524     xmldoc_get_charset,
525     xmldoc_put_charset,
526     xmldoc_get_version,
527     xmldoc_get_doctype,
528     xmldoc_get_dtdURl,
529     xmldoc_createElement
530 };
531
532 /************************************************************************
533  * xmldoc implementation of IPersistStreamInit.
534  */
535 static HRESULT WINAPI xmldoc_IPersistStreamInit_QueryInterface(
536     IPersistStreamInit *iface, REFIID riid, LPVOID *ppvObj)
537 {
538     xmldoc *this = impl_from_IPersistStreamInit(iface);
539     return IXMLDocument_QueryInterface((IXMLDocument *)this, riid, ppvObj);
540 }
541
542 static ULONG WINAPI xmldoc_IPersistStreamInit_AddRef(
543     IPersistStreamInit *iface)
544 {
545     xmldoc *this = impl_from_IPersistStreamInit(iface);
546     return IXMLDocument_AddRef((IXMLDocument *)this);
547 }
548
549 static ULONG WINAPI xmldoc_IPersistStreamInit_Release(
550     IPersistStreamInit *iface)
551 {
552     xmldoc *this = impl_from_IPersistStreamInit(iface);
553     return IXMLDocument_Release((IXMLDocument *)this);
554 }
555
556 static HRESULT WINAPI xmldoc_IPersistStreamInit_GetClassID(
557     IPersistStreamInit *iface, CLSID *classid)
558 {
559     FIXME("(%p,%p): stub!\n", iface, classid);
560     return E_NOTIMPL;
561 }
562
563 static HRESULT WINAPI xmldoc_IPersistStreamInit_IsDirty(
564     IPersistStreamInit *iface)
565 {
566     FIXME("(%p): stub!\n", iface);
567     return E_NOTIMPL;
568 }
569
570 xmlDocPtr parse_xml(char *ptr, int len)
571 {
572 #ifdef HAVE_XMLREADMEMORY
573     return xmlReadMemory(ptr, len, NULL, NULL,
574                          XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOBLANKS);
575 #else
576     return xmlParseMemory(ptr, len);
577 #endif
578 }
579
580 static HRESULT WINAPI xmldoc_IPersistStreamInit_Load(
581     IPersistStreamInit *iface, LPSTREAM pStm)
582 {
583     xmldoc *This = impl_from_IPersistStreamInit(iface);
584     xmlNodePtr xmlnode;
585     HRESULT hr;
586     HGLOBAL hglobal;
587     DWORD read, written, len;
588     BYTE buf[4096];
589     char *ptr;
590
591     TRACE("(%p, %p)\n", iface, pStm);
592
593     if (!pStm)
594         return E_INVALIDARG;
595
596     hr = CreateStreamOnHGlobal(NULL, TRUE, &This->stream);
597     if (FAILED(hr))
598         return hr;
599
600     do
601     {
602         IStream_Read(pStm, buf, sizeof(buf), &read);
603         hr = IStream_Write(This->stream, buf, read, &written);
604     } while(SUCCEEDED(hr) && written != 0 && read != 0);
605
606     if (FAILED(hr))
607     {
608         ERR("Failed to copy stream\n");
609         return hr;
610     }
611
612     hr = GetHGlobalFromStream(This->stream, &hglobal);
613     if (FAILED(hr))
614         return hr;
615
616     len = GlobalSize(hglobal);
617     ptr = GlobalLock(hglobal);
618     if (len != 0)
619         This->xmldoc = parse_xml(ptr, len);
620     GlobalUnlock(hglobal);
621
622     if (!This->xmldoc)
623     {
624         ERR("Failed to parse xml\n");
625         return E_FAIL;
626     }
627
628     xmlnode = xmlDocGetRootElement(This->xmldoc);
629     return XMLElement_create((IUnknown *)This, xmlnode, (LPVOID *)&This->root);
630 }
631
632 static HRESULT WINAPI xmldoc_IPersistStreamInit_Save(
633     IPersistStreamInit *iface, LPSTREAM pStm, BOOL fClearDirty)
634 {
635     FIXME("(%p, %p, %d): stub!\n", iface, pStm, fClearDirty);
636     return E_NOTIMPL;
637 }
638
639 static HRESULT WINAPI xmldoc_IPersistStreamInit_GetSizeMax(
640     IPersistStreamInit *iface, ULARGE_INTEGER *pcbSize)
641 {
642     FIXME("(%p, %p): stub!\n", iface, pcbSize);
643     return E_NOTIMPL;
644 }
645
646 static HRESULT WINAPI xmldoc_IPersistStreamInit_InitNew(
647     IPersistStreamInit *iface)
648 {
649     FIXME("(%p): stub!\n", iface);
650     return E_NOTIMPL;
651 }
652
653 static const IPersistStreamInitVtbl xmldoc_IPersistStreamInit_VTable =
654 {
655   xmldoc_IPersistStreamInit_QueryInterface,
656   xmldoc_IPersistStreamInit_AddRef,
657   xmldoc_IPersistStreamInit_Release,
658   xmldoc_IPersistStreamInit_GetClassID,
659   xmldoc_IPersistStreamInit_IsDirty,
660   xmldoc_IPersistStreamInit_Load,
661   xmldoc_IPersistStreamInit_Save,
662   xmldoc_IPersistStreamInit_GetSizeMax,
663   xmldoc_IPersistStreamInit_InitNew
664 };
665
666 HRESULT XMLDocument_create(IUnknown *pUnkOuter, LPVOID *ppObj)
667 {
668     xmldoc *doc;
669
670     TRACE("(%p,%p)\n", pUnkOuter, ppObj);
671
672     doc = HeapAlloc(GetProcessHeap(), 0, sizeof (*doc));
673     if(!doc)
674         return E_OUTOFMEMORY;
675
676     doc->lpVtbl = &xmldoc_vtbl;
677     doc->lpvtblIPersistStreamInit = &xmldoc_IPersistStreamInit_VTable;
678     doc->ref = 1;
679     doc->error = S_OK;
680     doc->root = NULL;
681     doc->xmldoc = NULL;
682     doc->stream = NULL;
683
684     *ppObj = &doc->lpVtbl;
685
686     TRACE("returning iface %p\n", *ppObj);
687     return S_OK;
688 }
689
690 #else
691
692 HRESULT XMLDocument_create(IUnknown *pUnkOuter, LPVOID *ppObj)
693 {
694     MESSAGE("This program tried to use an XMLDocument object, but\n"
695             "libxml2 support was not present at compile time.\n");
696     return E_NOTIMPL;
697 }
698
699 #endif