[PATCH 2/4] wusa: Parse assembly manifests.
Hans Leidekker
hans at codeweavers.com
Mon Dec 2 03:11:50 CST 2019
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
programs/wusa/Makefile.in | 5 +-
programs/wusa/main.c | 87 ++++++
programs/wusa/manifest.c | 567 ++++++++++++++++++++++++++++++++++++++
programs/wusa/wusa.h | 65 +++++
4 files changed, 722 insertions(+), 2 deletions(-)
create mode 100644 programs/wusa/manifest.c
diff --git a/programs/wusa/Makefile.in b/programs/wusa/Makefile.in
index 5e60bdf1b0..87bfe1dae0 100644
--- a/programs/wusa/Makefile.in
+++ b/programs/wusa/Makefile.in
@@ -1,7 +1,8 @@
MODULE = wusa.exe
-IMPORTS = cabinet shlwapi
+IMPORTS = cabinet shlwapi ole32 oleaut32
EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
C_SRCS = \
- main.c
+ main.c \
+ manifest.c
diff --git a/programs/wusa/main.c b/programs/wusa/main.c
index a719eefc5b..a1cdea4ee8 100644
--- a/programs/wusa/main.c
+++ b/programs/wusa/main.c
@@ -40,6 +40,7 @@ struct installer_state
BOOL norestart;
BOOL quiet;
struct list tempdirs;
+ struct list assemblies;
};
static void * CDECL cabinet_alloc(ULONG cb)
@@ -315,6 +316,7 @@ static BOOL delete_directory(const WCHAR *path)
static void installer_cleanup(struct installer_state *state)
{
struct installer_tempdir *tempdir, *tempdir2;
+ struct assembly_entry *assembly, *assembly2;
LIST_FOR_EACH_ENTRY_SAFE(tempdir, tempdir2, &state->tempdirs, struct installer_tempdir, entry)
{
@@ -323,14 +325,79 @@ static void installer_cleanup(struct installer_state *state)
heap_free(tempdir->path);
heap_free(tempdir);
}
+ LIST_FOR_EACH_ENTRY_SAFE(assembly, assembly2, &state->assemblies, struct assembly_entry, entry)
+ {
+ list_remove(&assembly->entry);
+ free_assembly(assembly);
+ }
+}
+
+static BOOL str_ends_with(const WCHAR *str, const WCHAR *suffix)
+{
+ DWORD str_len = lstrlenW(str), suffix_len = lstrlenW(suffix);
+ if (suffix_len > str_len) return FALSE;
+ return !wcsicmp(str + str_len - suffix_len, suffix);
+}
+
+static BOOL load_assemblies_from_cab(const WCHAR *filename, struct installer_state *state)
+{
+ struct assembly_entry *assembly;
+ const WCHAR *temp_path;
+ WIN32_FIND_DATAW data;
+ HANDLE search;
+ WCHAR *path;
+
+ TRACE("Processing cab file %s\n", debugstr_w(filename));
+
+ if (!(temp_path = create_temp_directory(state))) return FALSE;
+ if (!extract_cabinet(filename, temp_path))
+ {
+ ERR("Failed to extract %s\n", debugstr_w(filename));
+ return FALSE;
+ }
+
+ if (!(path = path_combine(temp_path, L"_manifest_.cix.xml"))) return FALSE;
+ if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES)
+ {
+ FIXME("Cabinet uses proprietary msdelta file compression which is not (yet) supported\n");
+ FIXME("Installation of msu file will most likely fail\n");
+ }
+ heap_free(path);
+
+ if (!(path = path_combine(temp_path, L"*"))) return FALSE;
+ search = FindFirstFileW(path, &data);
+ heap_free(path);
+
+ if (search != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
+ if (!str_ends_with(data.cFileName, L".manifest") &&
+ !str_ends_with(data.cFileName, L".mum")) continue;
+ if (!(path = path_combine(temp_path, data.cFileName))) continue;
+ if ((assembly = load_manifest(path)))
+ list_add_tail(&state->assemblies, &assembly->entry);
+ heap_free(path);
+ }
+ while (FindNextFileW(search, &data));
+ FindClose(search);
+ }
+
+ return TRUE;
}
static BOOL install_msu(const WCHAR *filename, struct installer_state *state)
{
const WCHAR *temp_path;
+ WIN32_FIND_DATAW data;
+ HANDLE search;
+ WCHAR *path;
BOOL ret = FALSE;
list_init(&state->tempdirs);
+ list_init(&state->assemblies);
+ CoInitialize(NULL);
TRACE("Processing msu file %s\n", debugstr_w(filename));
@@ -341,6 +408,26 @@ static BOOL install_msu(const WCHAR *filename, struct installer_state *state)
goto done;
}
+ /* load all manifests from contained cabinet archives */
+ if (!(path = path_combine(temp_path, L"*.cab"))) goto done;
+ search = FindFirstFileW(path, &data);
+ heap_free(path);
+
+ if (search != INVALID_HANDLE_VALUE)
+ {
+ do
+ {
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
+ if (!wcsicmp(data.cFileName, L"WSUSSCAN.cab")) continue;
+ if (!(path = path_combine(temp_path, data.cFileName))) continue;
+ if (!load_assemblies_from_cab(path, state))
+ ERR("Failed to load all manifests from %s, ignoring\n", debugstr_w(path));
+ heap_free(path);
+ }
+ while (FindNextFileW(search, &data));
+ FindClose(search);
+ }
+
ret = TRUE;
done:
diff --git a/programs/wusa/manifest.c b/programs/wusa/manifest.c
new file mode 100644
index 0000000000..44c263f6c0
--- /dev/null
+++ b/programs/wusa/manifest.c
@@ -0,0 +1,567 @@
+/*
+ * Manifest parser for WUSA
+ *
+ * Copyright 2015 Michael Müller
+ * Copyright 2015 Sebastian Lackner
+ *
+ * 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 <windows.h>
+#define COBJMACROS
+#include <initguid.h>
+#include <msxml.h>
+
+#include "wine/debug.h"
+#include "wine/list.h"
+#include "wusa.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wusa);
+
+static struct dependency_entry *alloc_dependency(void)
+{
+ struct dependency_entry *entry = heap_alloc_zero(sizeof(*entry));
+ if (!entry) ERR("Failed to allocate memory for dependency\n");
+ return entry;
+}
+
+static struct fileop_entry *alloc_fileop(void)
+{
+ struct fileop_entry *entry = heap_alloc_zero(sizeof(*entry));
+ if (!entry) ERR("Failed to allocate memory for fileop\n");
+ return entry;
+}
+
+static struct registrykv_entry *alloc_registrykv(void)
+{
+ struct registrykv_entry *entry = heap_alloc_zero(sizeof(*entry));
+ if (!entry) ERR("Failed to allocate memory for registrykv\n");
+ return entry;
+}
+
+static struct registryop_entry *alloc_registryop(void)
+{
+ struct registryop_entry *entry = heap_alloc_zero(sizeof(*entry));
+ if (!entry) ERR("Failed to allocate memory for registryop\n");
+ else
+ {
+ list_init(&entry->keyvalues);
+ }
+ return entry;
+}
+
+static struct assembly_entry *alloc_assembly(void)
+{
+ struct assembly_entry *entry = heap_alloc_zero(sizeof(*entry));
+ if (!entry) ERR("Failed to allocate memory for assembly\n");
+ else
+ {
+ list_init(&entry->dependencies);
+ list_init(&entry->fileops);
+ list_init(&entry->registryops);
+ }
+ return entry;
+}
+
+static void clear_identity(struct assembly_identity *entry)
+{
+ heap_free(entry->name);
+ heap_free(entry->version);
+ heap_free(entry->architecture);
+ heap_free(entry->language);
+ heap_free(entry->pubkey_token);
+}
+
+void free_dependency(struct dependency_entry *entry)
+{
+ clear_identity(&entry->identity);
+ heap_free(entry);
+}
+
+static void free_fileop(struct fileop_entry *entry)
+{
+ heap_free(entry->source);
+ heap_free(entry->target);
+ heap_free(entry);
+}
+
+static void free_registrykv(struct registrykv_entry *entry)
+{
+ heap_free(entry->name);
+ heap_free(entry->value_type);
+ heap_free(entry->value);
+ heap_free(entry);
+}
+
+static void free_registryop(struct registryop_entry *entry)
+{
+ struct registrykv_entry *keyvalue, *keyvalue2;
+
+ heap_free(entry->key);
+
+ LIST_FOR_EACH_ENTRY_SAFE(keyvalue, keyvalue2, &entry->keyvalues, struct registrykv_entry, entry)
+ {
+ list_remove(&keyvalue->entry);
+ free_registrykv(keyvalue);
+ }
+
+ heap_free(entry);
+}
+
+void free_assembly(struct assembly_entry *entry)
+{
+ struct dependency_entry *dependency, *dependency2;
+ struct fileop_entry *fileop, *fileop2;
+ struct registryop_entry *registryop, *registryop2;
+
+ heap_free(entry->filename);
+ heap_free(entry->displayname);
+ clear_identity(&entry->identity);
+
+ LIST_FOR_EACH_ENTRY_SAFE(dependency, dependency2, &entry->dependencies, struct dependency_entry, entry)
+ {
+ list_remove(&dependency->entry);
+ free_dependency(dependency);
+ }
+ LIST_FOR_EACH_ENTRY_SAFE(fileop, fileop2, &entry->fileops, struct fileop_entry, entry)
+ {
+ list_remove(&fileop->entry);
+ free_fileop(fileop);
+ }
+ LIST_FOR_EACH_ENTRY_SAFE(registryop, registryop2, &entry->registryops, struct registryop_entry, entry)
+ {
+ list_remove(®istryop->entry);
+ free_registryop(registryop);
+ }
+
+ heap_free(entry);
+}
+
+static WCHAR *get_xml_attribute(IXMLDOMElement *root, const WCHAR *name)
+{
+ WCHAR *ret = NULL;
+ VARIANT var;
+ BSTR bstr;
+
+ if ((bstr = SysAllocString(name)))
+ {
+ VariantInit(&var);
+ if (SUCCEEDED(IXMLDOMElement_getAttribute(root, bstr, &var)))
+ {
+ ret = (V_VT(&var) == VT_BSTR) ? strdupW(V_BSTR(&var)) : NULL;
+ VariantClear(&var);
+ }
+ SysFreeString(bstr);
+ }
+
+ return ret;
+}
+
+static BOOL check_xml_tagname(IXMLDOMElement *root, const WCHAR *tagname)
+{
+ BOOL ret = FALSE;
+ BSTR bstr;
+
+ if (SUCCEEDED(IXMLDOMElement_get_tagName(root, &bstr)))
+ {
+ ret = !wcscmp(bstr, tagname);
+ SysFreeString(bstr);
+ }
+
+ return ret;
+}
+
+static IXMLDOMElement *select_xml_node(IXMLDOMElement *root, const WCHAR *name)
+{
+ IXMLDOMElement *ret = NULL;
+ IXMLDOMNode *node;
+ BSTR bstr;
+
+ if ((bstr = SysAllocString(name)))
+ {
+ if (SUCCEEDED(IXMLDOMElement_selectSingleNode(root, bstr, &node)))
+ {
+ if (FAILED(IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void **)&ret)))
+ ret = NULL;
+ IXMLDOMNode_Release(node);
+ }
+ SysFreeString(bstr);
+ }
+
+ return ret;
+}
+
+static BOOL call_xml_callbacks(IXMLDOMElement *root, BOOL (*func)(IXMLDOMElement *child, WCHAR *tagname, void *context), void *context)
+{
+ IXMLDOMNodeList *children;
+ IXMLDOMElement *child;
+ IXMLDOMNode *node;
+ BSTR tagname;
+ BOOL ret = TRUE;
+
+ if (FAILED(IXMLDOMElement_get_childNodes(root, &children)))
+ return FALSE;
+
+ while (ret && IXMLDOMNodeList_nextNode(children, &node) == S_OK)
+ {
+ if (SUCCEEDED(IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void **)&child)))
+ {
+ if (SUCCEEDED(IXMLDOMElement_get_tagName(child, &tagname)))
+ {
+ ret = func(child, tagname, context);
+ SysFreeString(tagname);
+ }
+ IXMLDOMElement_Release(child);
+ }
+ IXMLDOMNode_Release(node);
+ }
+
+ IXMLDOMNodeList_Release(children);
+ return ret;
+}
+
+static IXMLDOMElement *load_xml(const WCHAR *filename)
+{
+ IXMLDOMDocument *document = NULL;
+ IXMLDOMElement *root = NULL;
+ VARIANT_BOOL success;
+ VARIANT variant;
+ BSTR bstr;
+
+ TRACE("Loading XML from %s\n", debugstr_w(filename));
+
+ if (!(bstr = SysAllocString(filename)))
+ return FALSE;
+
+ if (SUCCEEDED(CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&document)))
+ {
+ VariantInit(&variant);
+ V_VT(&variant) = VT_BSTR;
+ V_BSTR(&variant) = bstr;
+
+ if (SUCCEEDED(IXMLDOMDocument_load(document, variant, &success)) && success)
+ {
+ if (FAILED(IXMLDOMDocument_get_documentElement(document, &root)))
+ root = NULL;
+ }
+ IXMLDOMDocument_Release(document);
+ }
+
+ SysFreeString(bstr);
+ return root;
+}
+
+static BOOL read_identity(IXMLDOMElement *root, struct assembly_identity *identity)
+{
+ memset(identity, 0, sizeof(*identity));
+ if (!(identity->name = get_xml_attribute(root, L"name"))) goto error;
+ if (!(identity->version = get_xml_attribute(root, L"version"))) goto error;
+ if (!(identity->architecture = get_xml_attribute(root, L"processorArchitecture"))) goto error;
+ if (!(identity->language = get_xml_attribute(root, L"language"))) goto error;
+ if (!(identity->pubkey_token = get_xml_attribute(root, L"publicKeyToken"))) goto error;
+ return TRUE;
+
+error:
+ clear_identity(identity);
+ return FALSE;
+}
+
+/* <assembly><dependency><dependentAssembly> */
+static BOOL read_dependent_assembly(IXMLDOMElement *root, struct assembly_identity *identity)
+{
+ IXMLDOMElement *child = NULL;
+ WCHAR *dependency_type;
+ BOOL ret = FALSE;
+
+ if (!(dependency_type = get_xml_attribute(root, L"dependencyType")))
+ {
+ WARN("Failed to get dependency type, assuming install\n");
+ }
+ if (dependency_type && wcscmp(dependency_type, L"install") && wcscmp(dependency_type, L"prerequisite"))
+ {
+ FIXME("Unimplemented dependency type %s\n", debugstr_w(dependency_type));
+ goto error;
+ }
+ if (!(child = select_xml_node(root, L".//assemblyIdentity")))
+ {
+ FIXME("Failed to find assemblyIdentity child node\n");
+ goto error;
+ }
+
+ ret = read_identity(child, identity);
+
+error:
+ if (child) IXMLDOMElement_Release(child);
+ heap_free(dependency_type);
+ return ret;
+}
+
+/* <assembly><dependency> */
+static BOOL read_dependency(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct assembly_entry *assembly = context;
+ struct dependency_entry *entry;
+
+ if (wcscmp(tagname, L"dependentAssembly"))
+ {
+ FIXME("Don't know how to handle dependency tag %s\n", debugstr_w(tagname));
+ return FALSE;
+ }
+
+ if ((entry = alloc_dependency()))
+ {
+ if (read_dependent_assembly(child, &entry->identity))
+ {
+ TRACE("Found dependency %s\n", debugstr_w(entry->identity.name));
+ list_add_tail(&assembly->dependencies, &entry->entry);
+ return TRUE;
+ }
+ free_dependency(entry);
+ }
+
+ return FALSE;
+}
+
+static BOOL iter_dependency(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ return call_xml_callbacks(root, read_dependency, assembly);
+}
+
+/* <assembly><package><update><component> */
+/* <assembly><package><update><package> */
+static BOOL read_components(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct assembly_entry *assembly = context;
+ struct dependency_entry *entry;
+
+ if (wcscmp(tagname, L"assemblyIdentity"))
+ {
+ FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
+ return TRUE;
+ }
+
+ if ((entry = alloc_dependency()))
+ {
+ if (read_identity(child, &entry->identity))
+ {
+ TRACE("Found identity %s\n", debugstr_w(entry->identity.name));
+ list_add_tail(&assembly->dependencies, &entry->entry);
+ return TRUE;
+ }
+ free_dependency(entry);
+ }
+
+ return FALSE;
+}
+
+static BOOL iter_components(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ return call_xml_callbacks(root, read_components, assembly);
+}
+
+/* <assembly><package><update> */
+static BOOL read_update(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct assembly_entry *assembly = context;
+
+ if (!wcscmp(tagname, L"component"))
+ return iter_components(child, assembly);
+ if (!wcscmp(tagname, L"package"))
+ return iter_components(child, assembly);
+ if (!wcscmp(tagname, L"applicable"))
+ return TRUE;
+
+ FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
+ return FALSE;
+}
+
+static BOOL iter_update(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ return call_xml_callbacks(root, read_update, assembly);
+}
+
+/* <assembly><package> */
+static BOOL read_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct assembly_entry *assembly = context;
+
+ if (!wcscmp(tagname, L"update"))
+ return iter_update(child, assembly);
+ if (!wcscmp(tagname, L"parent"))
+ return TRUE;
+
+ FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
+ return TRUE;
+}
+
+static BOOL iter_package(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ return call_xml_callbacks(root, read_package, assembly);
+}
+
+/* <assembly><file> */
+static BOOL read_file(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ struct fileop_entry *entry;
+
+ if (!(entry = alloc_fileop()))
+ return FALSE;
+
+ if (!(entry->source = get_xml_attribute(root, L"sourceName"))) goto error;
+ if (!(entry->target = get_xml_attribute(root, L"destinationPath"))) goto error;
+
+ TRACE("Found fileop %s -> %s\n", debugstr_w(entry->source), debugstr_w(entry->target));
+ list_add_tail(&assembly->fileops, &entry->entry);
+ return TRUE;
+
+error:
+ free_fileop(entry);
+ return FALSE;
+}
+
+/* <assembly><registryKeys><registryKey> */
+static BOOL read_registry_key(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct registryop_entry *registryop = context;
+ struct registrykv_entry *entry;
+
+ if (!wcscmp(tagname, L"securityDescriptor")) return TRUE;
+ if (!wcscmp(tagname, L"systemProtection")) return TRUE;
+ if (wcscmp(tagname, L"registryValue"))
+ {
+ FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
+ return TRUE;
+ }
+
+ if (!(entry = alloc_registrykv()))
+ return FALSE;
+
+ if (!(entry->value_type = get_xml_attribute(child, L"valueType"))) goto error;
+ entry->name = get_xml_attribute(child, L"name"); /* optional */
+ entry->value = get_xml_attribute(child, L"value"); /* optional */
+
+ TRACE("Found registry %s -> %s\n", debugstr_w(entry->name), debugstr_w(entry->value));
+ list_add_tail(®istryop->keyvalues, &entry->entry);
+ return TRUE;
+
+error:
+ free_registrykv(entry);
+ return FALSE;
+}
+
+static BOOL iter_registry_key(IXMLDOMElement *root, struct registryop_entry *registryop)
+{
+ return call_xml_callbacks(root, read_registry_key, registryop);
+}
+
+/* <assembly><registryKeys> */
+static BOOL read_registry_keys(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct assembly_entry *assembly = context;
+ struct registryop_entry *entry;
+ WCHAR *keyname;
+
+ if (wcscmp(tagname, L"registryKey"))
+ {
+ FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
+ return TRUE;
+ }
+
+ if (!(keyname = get_xml_attribute(child, L"keyName")))
+ {
+ FIXME("RegistryKey tag doesn't specify keyName\n");
+ return FALSE;
+ }
+
+ if ((entry = alloc_registryop()))
+ {
+ list_init(&entry->keyvalues);
+ if (iter_registry_key(child, entry))
+ {
+ entry->key = keyname;
+ TRACE("Found registryop %s\n", debugstr_w(entry->key));
+ list_add_tail(&assembly->registryops, &entry->entry);
+ return TRUE;
+ }
+ free_registryop(entry);
+ }
+
+ heap_free(keyname);
+ return FALSE;
+}
+
+static BOOL iter_registry_keys(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ return call_xml_callbacks(root, read_registry_keys, assembly);
+}
+
+/* <assembly> */
+static BOOL read_assembly(IXMLDOMElement *child, WCHAR *tagname, void *context)
+{
+ struct assembly_entry *assembly = context;
+
+ if (!wcscmp(tagname, L"assemblyIdentity") && !assembly->identity.name)
+ return read_identity(child, &assembly->identity);
+ if (!wcscmp(tagname, L"dependency"))
+ return iter_dependency(child, assembly);
+ if (!wcscmp(tagname, L"package"))
+ return iter_package(child, assembly);
+ if (!wcscmp(tagname, L"file"))
+ return read_file(child, assembly);
+ if (!wcscmp(tagname, L"registryKeys"))
+ return iter_registry_keys(child, assembly);
+ if (!wcscmp(tagname, L"trustInfo"))
+ return TRUE;
+ if (!wcscmp(tagname, L"configuration"))
+ return TRUE;
+ if (!wcscmp(tagname, L"deployment"))
+ return TRUE;
+
+ FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
+ return TRUE;
+}
+
+static BOOL iter_assembly(IXMLDOMElement *root, struct assembly_entry *assembly)
+{
+ return call_xml_callbacks(root, read_assembly, assembly);
+}
+
+struct assembly_entry *load_manifest(const WCHAR *filename)
+{
+ struct assembly_entry *entry = NULL;
+ IXMLDOMElement *root = NULL;
+
+ TRACE("Loading manifest %s\n", debugstr_w(filename));
+
+ if (!(root = load_xml(filename))) return NULL;
+ if (!check_xml_tagname(root, L"assembly"))
+ {
+ FIXME("Didn't find assembly root node?\n");
+ goto done;
+ }
+
+ if ((entry = alloc_assembly()))
+ {
+ entry->filename = strdupW(filename);
+ entry->displayname = get_xml_attribute(root, L"displayName");
+ if (iter_assembly(root, entry)) goto done;
+ free_assembly(entry);
+ entry = NULL;
+ }
+
+done:
+ IXMLDOMElement_Release(root);
+ return entry;
+}
diff --git a/programs/wusa/wusa.h b/programs/wusa/wusa.h
index c6bf7eda11..2c81b639b9 100644
--- a/programs/wusa/wusa.h
+++ b/programs/wusa/wusa.h
@@ -17,12 +17,77 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+enum
+{
+ ASSEMBLY_STATUS_NONE,
+ ASSEMBLY_STATUS_IN_PROGRESS,
+ ASSEMBLY_STATUS_INSTALLED,
+};
+
+struct assembly_identity
+{
+ WCHAR *name;
+ WCHAR *version;
+ WCHAR *architecture;
+ WCHAR *language;
+ WCHAR *pubkey_token;
+};
+
+struct dependency_entry
+{
+ struct list entry;
+ struct assembly_identity identity;
+};
+
+struct fileop_entry
+{
+ struct list entry;
+ WCHAR *source;
+ WCHAR *target;
+};
+
+struct registrykv_entry
+{
+ struct list entry;
+ WCHAR *name;
+ WCHAR *value_type;
+ WCHAR *value;
+};
+
+struct registryop_entry
+{
+ struct list entry;
+ WCHAR *key;
+ struct list keyvalues;
+};
+
+struct assembly_entry
+{
+ struct list entry;
+ DWORD status;
+ WCHAR *filename;
+ WCHAR *displayname;
+ struct assembly_identity identity;
+ struct list dependencies;
+ struct list fileops;
+ struct list registryops;
+};
+
+void free_assembly(struct assembly_entry *entry) DECLSPEC_HIDDEN;
+struct assembly_entry *load_manifest(const WCHAR *filename) DECLSPEC_HIDDEN;
+
static void *heap_alloc(size_t len) __WINE_ALLOC_SIZE(1);
static inline void *heap_alloc(size_t len)
{
return HeapAlloc(GetProcessHeap(), 0, len);
}
+static void *heap_alloc_zero(size_t len) __WINE_ALLOC_SIZE(1);
+static inline void *heap_alloc_zero(size_t len)
+{
+ return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
+}
+
static inline BOOL heap_free(void *mem)
{
return HeapFree(GetProcessHeap(), 0, mem);
--
2.20.1
More information about the wine-devel
mailing list