| /* |
| * 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; |
| } |
| |
| HRESULT WINAPI WSDXMLGetValueFromAny(const WCHAR *pszNamespace, const WCHAR *pszName, WSDXML_ELEMENT *pAny, LPCWSTR *ppszValue) |
| { |
| WSDXML_ELEMENT *element; |
| WSDXML_TEXT *text; |
| |
| if (pAny == NULL) |
| return E_INVALIDARG; |
| |
| if (ppszValue == NULL) |
| return E_POINTER; |
| |
| if ((pszNamespace == NULL) || (pszName == NULL) || (lstrlenW(pszNamespace) > WSD_MAX_TEXT_LENGTH) || (lstrlenW(pszName) > WSD_MAX_TEXT_LENGTH)) |
| return E_INVALIDARG; |
| |
| element = pAny; |
| |
| while (element != NULL) |
| { |
| if (element->Node.Type == ElementType) |
| { |
| if ((lstrcmpW(element->Name->LocalName, pszName) == 0) && (lstrcmpW(element->Name->Space->Uri, pszNamespace) == 0)) |
| { |
| if ((element->FirstChild == NULL) || (element->FirstChild->Type != TextType)) |
| { |
| return E_FAIL; |
| } |
| |
| text = (WSDXML_TEXT *) element->FirstChild; |
| *ppszValue = (LPCWSTR) text->Text; |
| |
| return S_OK; |
| } |
| } |
| |
| element = (WSDXML_ELEMENT *) element->Node.Next; |
| } |
| |
| return E_FAIL; |
| } |
| |
| /* 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); |
| ns->PreferredPrefix = NULL; |
| |
| 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)\n", 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; |
| } |