639 lines
15 KiB
C
639 lines
15 KiB
C
/*
|
|
* Web Services on Devices
|
|
*
|
|
* Copyright 2017 Owen Rudge for CodeWeavers
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wine/debug.h"
|
|
#include "wine/list.h"
|
|
#include "wsdapi.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
|
|
|
|
#define WSD_MAX_TEXT_LENGTH 8192
|
|
|
|
static LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value)
|
|
{
|
|
int valueLen;
|
|
LPWSTR dup;
|
|
|
|
valueLen = lstrlenW(value) + 1;
|
|
|
|
dup = WSDAllocateLinkedMemory(parentMemoryBlock, valueLen * sizeof(WCHAR));
|
|
|
|
if (dup) memcpy(dup, value, valueLen * sizeof(WCHAR));
|
|
return dup;
|
|
}
|
|
|
|
static WSDXML_NAMESPACE *duplicate_namespace(void *parentMemoryBlock, WSDXML_NAMESPACE *ns)
|
|
{
|
|
WSDXML_NAMESPACE *newNs;
|
|
|
|
newNs = WSDAllocateLinkedMemory(parentMemoryBlock, sizeof(WSDXML_NAMESPACE));
|
|
|
|
if (newNs == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
newNs->Encoding = ns->Encoding;
|
|
|
|
/* On Windows, both Names and NamesCount are set to null even if there are names present */
|
|
newNs->NamesCount = 0;
|
|
newNs->Names = NULL;
|
|
|
|
newNs->PreferredPrefix = duplicate_string(newNs, ns->PreferredPrefix);
|
|
newNs->Uri = duplicate_string(newNs, ns->Uri);
|
|
|
|
return newNs;
|
|
}
|
|
|
|
static WSDXML_NAME *duplicate_name(void *parentMemoryBlock, WSDXML_NAME *name)
|
|
{
|
|
WSDXML_NAME *dup;
|
|
|
|
dup = WSDAllocateLinkedMemory(parentMemoryBlock, sizeof(WSDXML_NAME));
|
|
|
|
if (dup == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
dup->Space = duplicate_namespace(dup, name->Space);
|
|
dup->LocalName = duplicate_string(dup, name->LocalName);
|
|
|
|
if (dup->LocalName == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(dup);
|
|
return NULL;
|
|
}
|
|
|
|
return dup;
|
|
}
|
|
|
|
HRESULT WINAPI WSDXMLAddChild(WSDXML_ELEMENT *pParent, WSDXML_ELEMENT *pChild)
|
|
{
|
|
WSDXML_NODE *currentNode;
|
|
|
|
TRACE("(%p, %p)\n", pParent, pChild);
|
|
|
|
if ((pParent == NULL) || (pChild == NULL) || (pChild->Node.Parent != NULL))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
/* See if the parent already has a child */
|
|
currentNode = pParent->FirstChild;
|
|
|
|
if (currentNode == NULL)
|
|
{
|
|
pParent->FirstChild = (WSDXML_NODE *)pChild;
|
|
}
|
|
else
|
|
{
|
|
/* Find the last sibling node and make this child the next sibling */
|
|
WSDXMLAddSibling((WSDXML_ELEMENT *)currentNode, pChild);
|
|
}
|
|
|
|
pChild->Node.Parent = pParent;
|
|
|
|
/* Link the memory allocations */
|
|
WSDAttachLinkedMemory(pParent, pChild);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI WSDXMLAddSibling(WSDXML_ELEMENT *pFirst, WSDXML_ELEMENT *pSecond)
|
|
{
|
|
WSDXML_NODE *currentNode;
|
|
|
|
TRACE("(%p, %p)\n", pFirst, pSecond);
|
|
|
|
if ((pFirst == NULL) || (pSecond == NULL))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
/* See if the first node already has a sibling */
|
|
currentNode = pFirst->Node.Next;
|
|
|
|
if (currentNode == NULL)
|
|
{
|
|
pFirst->Node.Next = (WSDXML_NODE *)pSecond;
|
|
}
|
|
else
|
|
{
|
|
/* Find the last sibling node and make the second element the next sibling */
|
|
while (1)
|
|
{
|
|
if (currentNode->Next == NULL)
|
|
{
|
|
currentNode->Next = (WSDXML_NODE *)pSecond;
|
|
break;
|
|
}
|
|
|
|
currentNode = currentNode->Next;
|
|
}
|
|
}
|
|
|
|
/* Reparent the second node under the first */
|
|
pSecond->Node.Parent = pFirst->Node.Parent;
|
|
|
|
/* Link the memory allocations */
|
|
WSDAttachLinkedMemory(pFirst->Node.Parent, pSecond);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI WSDXMLBuildAnyForSingleElement(WSDXML_NAME *pElementName, LPCWSTR pszText, WSDXML_ELEMENT **ppAny)
|
|
{
|
|
WSDXML_TEXT *child;
|
|
|
|
TRACE("(%p, %s, %p)\n", pElementName, debugstr_w(pszText), ppAny);
|
|
|
|
if ((pElementName == NULL) || ((pszText != NULL) && (lstrlenW(pszText) > WSD_MAX_TEXT_LENGTH)))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (ppAny == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*ppAny = WSDAllocateLinkedMemory(NULL, sizeof(WSDXML_ELEMENT));
|
|
|
|
if (*ppAny == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ZeroMemory(*ppAny, sizeof(WSDXML_ELEMENT));
|
|
|
|
(*ppAny)->Name = duplicate_name(*ppAny, pElementName);
|
|
|
|
if ((*ppAny)->Name == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(*ppAny);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (pszText != NULL)
|
|
{
|
|
child = WSDAllocateLinkedMemory(*ppAny, sizeof(WSDXML_TEXT));
|
|
|
|
if (child == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(*ppAny);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
child->Node.Parent = *ppAny;
|
|
child->Node.Next = NULL;
|
|
child->Node.Type = TextType;
|
|
child->Text = duplicate_string(child, pszText);
|
|
|
|
if (child->Text == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(*ppAny);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
(*ppAny)->FirstChild = (WSDXML_NODE *)child;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT WINAPI WSDXMLCleanupElement(WSDXML_ELEMENT *pAny)
|
|
{
|
|
TRACE("(%p)\n", pAny);
|
|
|
|
if (pAny == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
WSDFreeLinkedMemory(pAny);
|
|
return S_OK;
|
|
}
|
|
|
|
/* IWSDXMLContext implementation */
|
|
|
|
struct xmlNamespace
|
|
{
|
|
struct list entry;
|
|
WSDXML_NAMESPACE *namespace;
|
|
};
|
|
|
|
typedef struct IWSDXMLContextImpl
|
|
{
|
|
IWSDXMLContext IWSDXMLContext_iface;
|
|
LONG ref;
|
|
|
|
struct list *namespaces;
|
|
int nextUnknownPrefix;
|
|
} IWSDXMLContextImpl;
|
|
|
|
static WSDXML_NAMESPACE *find_namespace(struct list *namespaces, LPCWSTR uri)
|
|
{
|
|
struct xmlNamespace *ns;
|
|
|
|
LIST_FOR_EACH_ENTRY(ns, namespaces, struct xmlNamespace, entry)
|
|
{
|
|
if (lstrcmpW(ns->namespace->Uri, uri) == 0)
|
|
{
|
|
return ns->namespace;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static WSDXML_NAME *find_name(WSDXML_NAMESPACE *ns, LPCWSTR name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ns->NamesCount; i++)
|
|
{
|
|
if (lstrcmpW(ns->Names[i].LocalName, name) == 0)
|
|
{
|
|
return &ns->Names[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static WSDXML_NAME *add_name(WSDXML_NAMESPACE *ns, LPCWSTR name)
|
|
{
|
|
WSDXML_NAME *names;
|
|
WSDXML_NAME *newName;
|
|
int i;
|
|
|
|
names = WSDAllocateLinkedMemory(ns, sizeof(WSDXML_NAME) * (ns->NamesCount + 1));
|
|
|
|
if (names == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (ns->NamesCount > 0)
|
|
{
|
|
/* Copy the existing names array over to the new allocation */
|
|
memcpy(names, ns->Names, sizeof(WSDXML_NAME) * ns->NamesCount);
|
|
|
|
for (i = 0; i < ns->NamesCount; i++)
|
|
{
|
|
/* Attach the local name memory to the new names allocation */
|
|
WSDAttachLinkedMemory(names, names[i].LocalName);
|
|
}
|
|
|
|
WSDFreeLinkedMemory(ns->Names);
|
|
}
|
|
|
|
ns->Names = names;
|
|
|
|
newName = &names[ns->NamesCount];
|
|
|
|
newName->LocalName = duplicate_string(names, name);
|
|
newName->Space = ns;
|
|
|
|
if (newName->LocalName == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ns->NamesCount++;
|
|
return newName;
|
|
}
|
|
|
|
static BOOL is_prefix_unique(struct list *namespaces, LPCWSTR prefix)
|
|
{
|
|
struct xmlNamespace *ns;
|
|
|
|
LIST_FOR_EACH_ENTRY(ns, namespaces, struct xmlNamespace, entry)
|
|
{
|
|
if (lstrcmpW(ns->namespace->PreferredPrefix, prefix) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static LPWSTR generate_namespace_prefix(IWSDXMLContextImpl *impl, void *parentMemoryBlock, LPCWSTR uri)
|
|
{
|
|
WCHAR formatString[] = { 'u','n','%','d', 0 };
|
|
WCHAR suggestedPrefix[7];
|
|
|
|
/* Find a unique prefix */
|
|
while (impl->nextUnknownPrefix < 1000)
|
|
{
|
|
wsprintfW(suggestedPrefix, formatString, impl->nextUnknownPrefix++);
|
|
|
|
/* For the unlikely event where somebody has explicitly created a prefix called 'unX', check it is unique */
|
|
if (is_prefix_unique(impl->namespaces, suggestedPrefix))
|
|
{
|
|
return duplicate_string(parentMemoryBlock, suggestedPrefix);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static WSDXML_NAMESPACE *add_namespace(struct list *namespaces, LPCWSTR uri)
|
|
{
|
|
struct xmlNamespace *ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct xmlNamespace));
|
|
|
|
if (ns == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ns->namespace = WSDAllocateLinkedMemory(ns, sizeof(WSDXML_NAMESPACE));
|
|
|
|
if (ns->namespace == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(ns);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(ns->namespace, sizeof(WSDXML_NAMESPACE));
|
|
ns->namespace->Uri = duplicate_string(ns->namespace, uri);
|
|
|
|
if (ns->namespace->Uri == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(ns);
|
|
return NULL;
|
|
}
|
|
|
|
list_add_tail(namespaces, &ns->entry);
|
|
return ns->namespace;
|
|
}
|
|
|
|
static inline IWSDXMLContextImpl *impl_from_IWSDXMLContext(IWSDXMLContext *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IWSDXMLContextImpl, IWSDXMLContext_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI IWSDXMLContextImpl_QueryInterface(IWSDXMLContext *iface, REFIID riid, void **ppv)
|
|
{
|
|
IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
|
|
|
|
TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
|
|
|
|
if (!ppv)
|
|
{
|
|
WARN("Invalid parameter\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
*ppv = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) ||
|
|
IsEqualIID(riid, &IID_IWSDXMLContext))
|
|
{
|
|
*ppv = &This->IWSDXMLContext_iface;
|
|
}
|
|
else
|
|
{
|
|
WARN("Unknown IID %s\n", debugstr_guid(riid));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
IUnknown_AddRef((IUnknown*)*ppv);
|
|
return S_OK;
|
|
}
|
|
|
|
static ULONG WINAPI IWSDXMLContextImpl_AddRef(IWSDXMLContext *iface)
|
|
{
|
|
IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) ref=%d\n", This, ref);
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI IWSDXMLContextImpl_Release(IWSDXMLContext *iface)
|
|
{
|
|
IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p) ref=%d\n", This, ref);
|
|
|
|
if (ref == 0)
|
|
{
|
|
WSDFreeLinkedMemory(This);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI IWSDXMLContextImpl_AddNamespace(IWSDXMLContext *iface, LPCWSTR pszUri, LPCWSTR pszSuggestedPrefix, WSDXML_NAMESPACE **ppNamespace)
|
|
{
|
|
IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
|
|
WSDXML_NAMESPACE *ns;
|
|
LPCWSTR newPrefix = NULL;
|
|
BOOL setNewPrefix;
|
|
|
|
TRACE("(%p, %s, %s, %p)\n", This, debugstr_w(pszUri), debugstr_w(pszSuggestedPrefix), ppNamespace);
|
|
|
|
if ((pszUri == NULL) || (pszSuggestedPrefix == NULL) || (lstrlenW(pszUri) > WSD_MAX_TEXT_LENGTH) ||
|
|
(lstrlenW(pszSuggestedPrefix) > WSD_MAX_TEXT_LENGTH))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ns = find_namespace(This->namespaces, pszUri);
|
|
|
|
if (ns == NULL)
|
|
{
|
|
ns = add_namespace(This->namespaces, pszUri);
|
|
|
|
if (ns == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
setNewPrefix = (ns->PreferredPrefix == NULL);
|
|
|
|
if ((ns->PreferredPrefix == NULL) || (lstrcmpW(ns->PreferredPrefix, pszSuggestedPrefix) != 0))
|
|
{
|
|
newPrefix = pszSuggestedPrefix;
|
|
setNewPrefix = TRUE;
|
|
}
|
|
|
|
if (setNewPrefix)
|
|
{
|
|
WSDFreeLinkedMemory((void *)ns->PreferredPrefix);
|
|
|
|
if ((newPrefix != NULL) && (is_prefix_unique(This->namespaces, newPrefix)))
|
|
{
|
|
ns->PreferredPrefix = duplicate_string(ns, newPrefix);
|
|
}
|
|
else
|
|
{
|
|
ns->PreferredPrefix = generate_namespace_prefix(This, ns, pszUri);
|
|
if (ns->PreferredPrefix == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ppNamespace != NULL)
|
|
{
|
|
*ppNamespace = duplicate_namespace(NULL, ns);
|
|
|
|
if (*ppNamespace == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IWSDXMLContextImpl_AddNameToNamespace(IWSDXMLContext *iface, LPCWSTR pszUri, LPCWSTR pszName, WSDXML_NAME **ppName)
|
|
{
|
|
IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
|
|
WSDXML_NAMESPACE *ns;
|
|
WSDXML_NAME *name;
|
|
|
|
TRACE("(%p, %s, %s, %p)\n", This, debugstr_w(pszUri), debugstr_w(pszName), ppName);
|
|
|
|
if ((pszUri == NULL) || (pszName == NULL) || (lstrlenW(pszUri) > WSD_MAX_TEXT_LENGTH) || (lstrlenW(pszName) > WSD_MAX_TEXT_LENGTH))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ns = find_namespace(This->namespaces, pszUri);
|
|
|
|
if (ns == NULL)
|
|
{
|
|
/* The namespace doesn't exist, add it */
|
|
ns = add_namespace(This->namespaces, pszUri);
|
|
|
|
if (ns == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ns->PreferredPrefix = generate_namespace_prefix(This, ns, pszUri);
|
|
if (ns->PreferredPrefix == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
name = find_name(ns, pszName);
|
|
|
|
if (name == NULL)
|
|
{
|
|
name = add_name(ns, pszName);
|
|
|
|
if (name == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (ppName != NULL)
|
|
{
|
|
*ppName = duplicate_name(NULL, name);
|
|
|
|
if (*ppName == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IWSDXMLContextImpl_SetNamespaces(IWSDXMLContext *iface, const PCWSDXML_NAMESPACE *pNamespaces, WORD wNamespacesCount, BYTE bLayerNumber)
|
|
{
|
|
FIXME("(%p, %p, %d, %d)\n", iface, pNamespaces, wNamespacesCount, bLayerNumber);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI IWSDXMLContextImpl_SetTypes(IWSDXMLContext *iface, const PCWSDXML_TYPE *pTypes, DWORD dwTypesCount, BYTE bLayerNumber)
|
|
{
|
|
FIXME("(%p, %p, %d, %d)\n", iface, pTypes, dwTypesCount, bLayerNumber);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const IWSDXMLContextVtbl xmlcontext_vtbl =
|
|
{
|
|
IWSDXMLContextImpl_QueryInterface,
|
|
IWSDXMLContextImpl_AddRef,
|
|
IWSDXMLContextImpl_Release,
|
|
IWSDXMLContextImpl_AddNamespace,
|
|
IWSDXMLContextImpl_AddNameToNamespace,
|
|
IWSDXMLContextImpl_SetNamespaces,
|
|
IWSDXMLContextImpl_SetTypes
|
|
};
|
|
|
|
HRESULT WINAPI WSDXMLCreateContext(IWSDXMLContext **ppContext)
|
|
{
|
|
IWSDXMLContextImpl *obj;
|
|
|
|
TRACE("(%p)", ppContext);
|
|
|
|
if (ppContext == NULL)
|
|
{
|
|
WARN("Invalid parameter: ppContext == NULL\n");
|
|
return E_POINTER;
|
|
}
|
|
|
|
*ppContext = NULL;
|
|
|
|
obj = WSDAllocateLinkedMemory(NULL, sizeof(*obj));
|
|
|
|
if (!obj)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
obj->IWSDXMLContext_iface.lpVtbl = &xmlcontext_vtbl;
|
|
obj->ref = 1;
|
|
obj->namespaces = WSDAllocateLinkedMemory(obj, sizeof(struct list));
|
|
obj->nextUnknownPrefix = 0;
|
|
|
|
if (obj->namespaces == NULL)
|
|
{
|
|
WSDFreeLinkedMemory(obj);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
list_init(obj->namespaces);
|
|
|
|
*ppContext = &obj->IWSDXMLContext_iface;
|
|
TRACE("Returning iface %p\n", *ppContext);
|
|
|
|
return S_OK;
|
|
}
|