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