blob: 1cf87855878271020264357e9200cc77cfc09c57 [file] [log] [blame]
/*
* XML Document implementation
*
* Copyright 2007 James Hawkins
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "config.h"
#include <stdarg.h>
#ifdef HAVE_LIBXML2
# include <libxml/parser.h>
# include <libxml/xmlerror.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
#include "msxml6.h"
#include "wininet.h"
#include "winreg.h"
#include "shlwapi.h"
#include "ocidl.h"
#include "wine/debug.h"
#include "msxml_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(msxml);
#ifdef HAVE_LIBXML2
/* FIXME: IXMLDocument needs to implement
* - IXMLError
* - IPersistMoniker
*/
typedef struct _xmldoc
{
IXMLDocument IXMLDocument_iface;
IPersistStreamInit IPersistStreamInit_iface;
LONG ref;
HRESULT error;
/* IXMLDocument */
xmlDocPtr xmldoc;
/* IPersistStream */
IStream *stream;
} xmldoc;
static inline xmldoc *impl_from_IXMLDocument(IXMLDocument *iface)
{
return CONTAINING_RECORD(iface, xmldoc, IXMLDocument_iface);
}
static inline xmldoc *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
{
return CONTAINING_RECORD(iface, xmldoc, IPersistStreamInit_iface);
}
static HRESULT WINAPI xmldoc_QueryInterface(IXMLDocument *iface, REFIID riid, void** ppvObject)
{
xmldoc *This = impl_from_IXMLDocument(iface);
TRACE("%p %s %p\n", This, debugstr_guid(riid), ppvObject);
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IDispatch) ||
IsEqualGUID(riid, &IID_IXMLDocument))
{
*ppvObject = iface;
}
else if (IsEqualGUID(&IID_IPersistStreamInit, riid) ||
IsEqualGUID(&IID_IPersistStream, riid))
{
*ppvObject = &This->IPersistStreamInit_iface;
}
else
{
FIXME("interface %s not implemented\n", debugstr_guid(riid));
*ppvObject = NULL;
return E_NOINTERFACE;
}
IXMLDocument_AddRef(iface);
return S_OK;
}
static ULONG WINAPI xmldoc_AddRef(IXMLDocument *iface)
{
xmldoc *This = impl_from_IXMLDocument(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p)->(%d)\n", This, ref);
return ref;
}
static ULONG WINAPI xmldoc_Release(IXMLDocument *iface)
{
xmldoc *This = impl_from_IXMLDocument(iface);
LONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p)->(%d)\n", This, ref);
if (ref == 0)
{
xmlFreeDoc(This->xmldoc);
if (This->stream) IStream_Release(This->stream);
heap_free(This);
}
return ref;
}
static HRESULT WINAPI xmldoc_GetTypeInfoCount(IXMLDocument *iface, UINT* pctinfo)
{
xmldoc *This = impl_from_IXMLDocument(iface);
TRACE("(%p)->(%p)\n", This, pctinfo);
*pctinfo = 1;
return S_OK;
}
static HRESULT WINAPI xmldoc_GetTypeInfo(IXMLDocument *iface, UINT iTInfo,
LCID lcid, ITypeInfo** ppTInfo)
{
xmldoc *This = impl_from_IXMLDocument(iface);
TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
return get_typeinfo(IXMLDocument_tid, ppTInfo);
}
static HRESULT WINAPI xmldoc_GetIDsOfNames(IXMLDocument *iface, REFIID riid,
LPOLESTR* rgszNames, UINT cNames,
LCID lcid, DISPID* rgDispId)
{
xmldoc *This = impl_from_IXMLDocument(iface);
ITypeInfo *typeinfo;
HRESULT hr;
TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
lcid, rgDispId);
if(!rgszNames || cNames == 0 || !rgDispId)
return E_INVALIDARG;
hr = get_typeinfo(IXMLDocument_tid, &typeinfo);
if(SUCCEEDED(hr))
{
hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
ITypeInfo_Release(typeinfo);
}
return hr;
}
static HRESULT WINAPI xmldoc_Invoke(IXMLDocument *iface, DISPID dispIdMember,
REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS* pDispParams, VARIANT* pVarResult,
EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
xmldoc *This = impl_from_IXMLDocument(iface);
ITypeInfo *typeinfo;
HRESULT hr;
TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
hr = get_typeinfo(IXMLDocument_tid, &typeinfo);
if(SUCCEEDED(hr))
{
hr = ITypeInfo_Invoke(typeinfo, &This->IXMLDocument_iface, dispIdMember, wFlags,
pDispParams, pVarResult, pExcepInfo, puArgErr);
ITypeInfo_Release(typeinfo);
}
return hr;
}
static HRESULT WINAPI xmldoc_get_root(IXMLDocument *iface, IXMLElement **p)
{
xmldoc *This = impl_from_IXMLDocument(iface);
xmlNodePtr root;
TRACE("(%p, %p)\n", iface, p);
if (!p)
return E_INVALIDARG;
*p = NULL;
if (!(root = xmlDocGetRootElement(This->xmldoc)))
return E_FAIL;
return XMLElement_create((IUnknown *)This, root, (LPVOID *)p, FALSE);
}
static HRESULT WINAPI xmldoc_get_fileSize(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_put_fileModifiedDate(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_get_fileUpdatedDate(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_get_URL(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
typedef struct {
IBindStatusCallback IBindStatusCallback_iface;
} bsc;
static HRESULT WINAPI bsc_QueryInterface(
IBindStatusCallback *iface,
REFIID riid,
LPVOID *ppobj )
{
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IBindStatusCallback))
{
IBindStatusCallback_AddRef( iface );
*ppobj = iface;
return S_OK;
}
TRACE("interface %s not implemented\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI bsc_AddRef(
IBindStatusCallback *iface )
{
return 2;
}
static ULONG WINAPI bsc_Release(
IBindStatusCallback *iface )
{
return 1;
}
static HRESULT WINAPI bsc_OnStartBinding(
IBindStatusCallback* iface,
DWORD dwReserved,
IBinding* pib)
{
return S_OK;
}
static HRESULT WINAPI bsc_GetPriority(
IBindStatusCallback* iface,
LONG* pnPriority)
{
return S_OK;
}
static HRESULT WINAPI bsc_OnLowResource(
IBindStatusCallback* iface,
DWORD reserved)
{
return S_OK;
}
static HRESULT WINAPI bsc_OnProgress(
IBindStatusCallback* iface,
ULONG ulProgress,
ULONG ulProgressMax,
ULONG ulStatusCode,
LPCWSTR szStatusText)
{
return S_OK;
}
static HRESULT WINAPI bsc_OnStopBinding(
IBindStatusCallback* iface,
HRESULT hresult,
LPCWSTR szError)
{
return S_OK;
}
static HRESULT WINAPI bsc_GetBindInfo(
IBindStatusCallback* iface,
DWORD* grfBINDF,
BINDINFO* pbindinfo)
{
*grfBINDF = BINDF_RESYNCHRONIZE;
return S_OK;
}
static HRESULT WINAPI bsc_OnDataAvailable(
IBindStatusCallback* iface,
DWORD grfBSCF,
DWORD dwSize,
FORMATETC* pformatetc,
STGMEDIUM* pstgmed)
{
return S_OK;
}
static HRESULT WINAPI bsc_OnObjectAvailable(
IBindStatusCallback* iface,
REFIID riid,
IUnknown* punk)
{
return S_OK;
}
static const struct IBindStatusCallbackVtbl bsc_vtbl =
{
bsc_QueryInterface,
bsc_AddRef,
bsc_Release,
bsc_OnStartBinding,
bsc_GetPriority,
bsc_OnLowResource,
bsc_OnProgress,
bsc_OnStopBinding,
bsc_GetBindInfo,
bsc_OnDataAvailable,
bsc_OnObjectAvailable
};
static bsc xmldoc_bsc = { { &bsc_vtbl } };
static HRESULT WINAPI xmldoc_put_URL(IXMLDocument *iface, BSTR p)
{
WCHAR url[INTERNET_MAX_URL_LENGTH];
IStream *stream;
IBindCtx *bctx;
IMoniker *moniker;
IPersistStreamInit *persist;
HRESULT hr;
TRACE("(%p, %s)\n", iface, debugstr_w(p));
if (!p)
return E_INVALIDARG;
if (!PathIsURLW(p))
{
WCHAR fullpath[MAX_PATH];
DWORD needed = sizeof(url) / sizeof(WCHAR);
if (!PathSearchAndQualifyW(p, fullpath, sizeof(fullpath) / sizeof(WCHAR)))
{
ERR("can't find path\n");
return E_FAIL;
}
if (FAILED(UrlCreateFromPathW(fullpath, url, &needed, 0)))
{
ERR("can't create url from path\n");
return E_FAIL;
}
p = url;
}
hr = CreateURLMoniker(NULL, p, &moniker);
if (FAILED(hr))
return hr;
CreateAsyncBindCtx(0, &xmldoc_bsc.IBindStatusCallback_iface, 0, &bctx);
hr = IMoniker_BindToStorage(moniker, bctx, NULL, &IID_IStream, (LPVOID *)&stream);
IBindCtx_Release(bctx);
IMoniker_Release(moniker);
if (FAILED(hr))
return hr;
hr = IXMLDocument_QueryInterface(iface, &IID_IPersistStreamInit, (LPVOID *)&persist);
if (FAILED(hr))
{
IStream_Release(stream);
return hr;
}
hr = IPersistStreamInit_Load(persist, stream);
IPersistStreamInit_Release(persist);
IStream_Release(stream);
return hr;
}
static HRESULT WINAPI xmldoc_get_mimeType(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_get_readyState(IXMLDocument *iface, LONG *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_get_charset(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_put_charset(IXMLDocument *iface, BSTR p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_get_version(IXMLDocument *iface, BSTR *p)
{
xmldoc *This = impl_from_IXMLDocument(iface);
TRACE("(%p, %p)\n", This, p);
if (!p) return E_INVALIDARG;
*p = bstr_from_xmlChar(This->xmldoc->version);
return S_OK;
}
static HRESULT WINAPI xmldoc_get_doctype(IXMLDocument *iface, BSTR *p)
{
xmldoc *This = impl_from_IXMLDocument(iface);
xmlDtd *dtd;
TRACE("(%p, %p)\n", This, p);
if (!p) return E_INVALIDARG;
dtd = xmlGetIntSubset(This->xmldoc);
if (!dtd) return S_FALSE;
*p = bstr_from_xmlChar(dtd->name);
CharUpperBuffW(*p, SysStringLen(*p));
return S_OK;
}
static HRESULT WINAPI xmldoc_get_dtdURl(IXMLDocument *iface, BSTR *p)
{
FIXME("(%p, %p): stub\n", iface, p);
return E_NOTIMPL;
}
static xmlElementType type_msxml_to_libxml(LONG type)
{
switch (type)
{
case XMLELEMTYPE_ELEMENT:
return XML_ELEMENT_NODE;
case XMLELEMTYPE_TEXT:
return XML_TEXT_NODE;
case XMLELEMTYPE_COMMENT:
return XML_COMMENT_NODE;
case XMLELEMTYPE_DOCUMENT:
return XML_DOCUMENT_NODE;
case XMLELEMTYPE_DTD:
return XML_DTD_NODE;
case XMLELEMTYPE_PI:
return XML_PI_NODE;
default:
break;
}
return -1; /* FIXME: what is OTHER in msxml? */
}
static HRESULT WINAPI xmldoc_createElement(IXMLDocument *iface, VARIANT vType,
VARIANT var1, IXMLElement **ppElem)
{
xmlNodePtr node;
static const xmlChar empty[] = "\0";
TRACE("(%p)->(%s %s %p)\n", iface, debugstr_variant(&vType),
debugstr_variant(&var1), ppElem);
if (!ppElem)
return E_INVALIDARG;
*ppElem = NULL;
if (V_VT(&vType) != VT_I4)
return E_INVALIDARG;
if(type_msxml_to_libxml(V_I4(&vType)) == -1)
return E_NOTIMPL;
node = xmlNewNode(NULL, empty);
node->type = type_msxml_to_libxml(V_I4(&vType));
/* FIXME: create xmlNodePtr based on vType and var1 */
return XMLElement_create((IUnknown *)iface, node, (LPVOID *)ppElem, TRUE);
}
static const struct IXMLDocumentVtbl xmldoc_vtbl =
{
xmldoc_QueryInterface,
xmldoc_AddRef,
xmldoc_Release,
xmldoc_GetTypeInfoCount,
xmldoc_GetTypeInfo,
xmldoc_GetIDsOfNames,
xmldoc_Invoke,
xmldoc_get_root,
xmldoc_get_fileSize,
xmldoc_put_fileModifiedDate,
xmldoc_get_fileUpdatedDate,
xmldoc_get_URL,
xmldoc_put_URL,
xmldoc_get_mimeType,
xmldoc_get_readyState,
xmldoc_get_charset,
xmldoc_put_charset,
xmldoc_get_version,
xmldoc_get_doctype,
xmldoc_get_dtdURl,
xmldoc_createElement
};
/************************************************************************
* xmldoc implementation of IPersistStreamInit.
*/
static HRESULT WINAPI xmldoc_IPersistStreamInit_QueryInterface(
IPersistStreamInit *iface, REFIID riid, LPVOID *ppvObj)
{
xmldoc *this = impl_from_IPersistStreamInit(iface);
return IXMLDocument_QueryInterface(&this->IXMLDocument_iface, riid, ppvObj);
}
static ULONG WINAPI xmldoc_IPersistStreamInit_AddRef(
IPersistStreamInit *iface)
{
xmldoc *this = impl_from_IPersistStreamInit(iface);
return IXMLDocument_AddRef(&this->IXMLDocument_iface);
}
static ULONG WINAPI xmldoc_IPersistStreamInit_Release(
IPersistStreamInit *iface)
{
xmldoc *this = impl_from_IPersistStreamInit(iface);
return IXMLDocument_Release(&this->IXMLDocument_iface);
}
static HRESULT WINAPI xmldoc_IPersistStreamInit_GetClassID(
IPersistStreamInit *iface, CLSID *classid)
{
xmldoc *this = impl_from_IPersistStreamInit(iface);
TRACE("(%p,%p)\n", this, classid);
if (!classid) return E_POINTER;
*classid = CLSID_XMLDocument;
return S_OK;
}
static HRESULT WINAPI xmldoc_IPersistStreamInit_IsDirty(
IPersistStreamInit *iface)
{
FIXME("(%p): stub!\n", iface);
return E_NOTIMPL;
}
static xmlDocPtr parse_xml(char *ptr, int len)
{
#ifdef HAVE_XMLREADMEMORY
return xmlReadMemory(ptr, len, NULL, NULL,
XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOBLANKS);
#else
return xmlParseMemory(ptr, len);
#endif
}
static HRESULT WINAPI xmldoc_IPersistStreamInit_Load(
IPersistStreamInit *iface, LPSTREAM pStm)
{
xmldoc *This = impl_from_IPersistStreamInit(iface);
HRESULT hr;
HGLOBAL hglobal;
DWORD read, written, len;
BYTE buf[4096];
char *ptr;
TRACE("(%p, %p)\n", iface, pStm);
if (!pStm)
return E_INVALIDARG;
/* release previously allocated stream */
if (This->stream) IStream_Release(This->stream);
hr = CreateStreamOnHGlobal(NULL, TRUE, &This->stream);
if (FAILED(hr))
return hr;
do
{
IStream_Read(pStm, buf, sizeof(buf), &read);
hr = IStream_Write(This->stream, buf, read, &written);
} while(SUCCEEDED(hr) && written != 0 && read != 0);
if (FAILED(hr))
{
ERR("Failed to copy stream\n");
return hr;
}
hr = GetHGlobalFromStream(This->stream, &hglobal);
if (FAILED(hr))
return hr;
len = GlobalSize(hglobal);
ptr = GlobalLock(hglobal);
if (len != 0)
{
xmlFreeDoc(This->xmldoc);
This->xmldoc = parse_xml(ptr, len);
}
GlobalUnlock(hglobal);
if (!This->xmldoc)
{
ERR("Failed to parse xml\n");
return E_FAIL;
}
return S_OK;
}
static HRESULT WINAPI xmldoc_IPersistStreamInit_Save(
IPersistStreamInit *iface, LPSTREAM pStm, BOOL fClearDirty)
{
FIXME("(%p, %p, %d): stub!\n", iface, pStm, fClearDirty);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_IPersistStreamInit_GetSizeMax(
IPersistStreamInit *iface, ULARGE_INTEGER *pcbSize)
{
xmldoc *This = impl_from_IPersistStreamInit(iface);
TRACE("(%p, %p)\n", This, pcbSize);
return E_NOTIMPL;
}
static HRESULT WINAPI xmldoc_IPersistStreamInit_InitNew(
IPersistStreamInit *iface)
{
xmldoc *This = impl_from_IPersistStreamInit(iface);
TRACE("(%p)\n", This);
return S_OK;
}
static const IPersistStreamInitVtbl xmldoc_IPersistStreamInit_VTable =
{
xmldoc_IPersistStreamInit_QueryInterface,
xmldoc_IPersistStreamInit_AddRef,
xmldoc_IPersistStreamInit_Release,
xmldoc_IPersistStreamInit_GetClassID,
xmldoc_IPersistStreamInit_IsDirty,
xmldoc_IPersistStreamInit_Load,
xmldoc_IPersistStreamInit_Save,
xmldoc_IPersistStreamInit_GetSizeMax,
xmldoc_IPersistStreamInit_InitNew
};
HRESULT XMLDocument_create(IUnknown *pUnkOuter, LPVOID *ppObj)
{
xmldoc *doc;
TRACE("(%p,%p)\n", pUnkOuter, ppObj);
doc = heap_alloc(sizeof (*doc));
if(!doc)
return E_OUTOFMEMORY;
doc->IXMLDocument_iface.lpVtbl = &xmldoc_vtbl;
doc->IPersistStreamInit_iface.lpVtbl = &xmldoc_IPersistStreamInit_VTable;
doc->ref = 1;
doc->error = S_OK;
doc->xmldoc = NULL;
doc->stream = NULL;
*ppObj = &doc->IXMLDocument_iface;
TRACE("returning iface %p\n", *ppObj);
return S_OK;
}
#else
HRESULT XMLDocument_create(IUnknown *pUnkOuter, LPVOID *ppObj)
{
MESSAGE("This program tried to use an XMLDocument object, but\n"
"libxml2 support was not present at compile time.\n");
return E_NOTIMPL;
}
#endif