include: Add more sapi structs and enums.
[wine.git] / programs / wusa / manifest.c
blob620da4fc63b6c0c7a1ae2e2d26d0fe6de038da6c
1 /*
2 * Manifest parser for WUSA
4 * Copyright 2015 Michael Müller
5 * Copyright 2015 Sebastian Lackner
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <windows.h>
23 #define COBJMACROS
24 #include <initguid.h>
25 #include <msxml.h>
27 #include "wine/debug.h"
28 #include "wine/list.h"
29 #include "wusa.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(wusa);
33 static struct dependency_entry *alloc_dependency(void)
35 struct dependency_entry *entry = calloc(1, sizeof(*entry));
36 if (!entry) ERR("Failed to allocate memory for dependency\n");
37 return entry;
40 static struct fileop_entry *alloc_fileop(void)
42 struct fileop_entry *entry = calloc(1, sizeof(*entry));
43 if (!entry) ERR("Failed to allocate memory for fileop\n");
44 return entry;
47 static struct registrykv_entry *alloc_registrykv(void)
49 struct registrykv_entry *entry = calloc(1, sizeof(*entry));
50 if (!entry) ERR("Failed to allocate memory for registrykv\n");
51 return entry;
54 static struct registryop_entry *alloc_registryop(void)
56 struct registryop_entry *entry = calloc(1, sizeof(*entry));
57 if (!entry) ERR("Failed to allocate memory for registryop\n");
58 else
60 list_init(&entry->keyvalues);
62 return entry;
65 static struct assembly_entry *alloc_assembly(void)
67 struct assembly_entry *entry = calloc(1, sizeof(*entry));
68 if (!entry) ERR("Failed to allocate memory for assembly\n");
69 else
71 list_init(&entry->dependencies);
72 list_init(&entry->fileops);
73 list_init(&entry->registryops);
75 return entry;
78 static void clear_identity(struct assembly_identity *entry)
80 free(entry->name);
81 free(entry->version);
82 free(entry->architecture);
83 free(entry->language);
84 free(entry->pubkey_token);
87 void free_dependency(struct dependency_entry *entry)
89 clear_identity(&entry->identity);
90 free(entry);
93 static void free_fileop(struct fileop_entry *entry)
95 free(entry->source);
96 free(entry->target);
97 free(entry);
100 static void free_registrykv(struct registrykv_entry *entry)
102 free(entry->name);
103 free(entry->value_type);
104 free(entry->value);
105 free(entry);
108 static void free_registryop(struct registryop_entry *entry)
110 struct registrykv_entry *keyvalue, *keyvalue2;
112 free(entry->key);
114 LIST_FOR_EACH_ENTRY_SAFE(keyvalue, keyvalue2, &entry->keyvalues, struct registrykv_entry, entry)
116 list_remove(&keyvalue->entry);
117 free_registrykv(keyvalue);
120 free(entry);
123 void free_assembly(struct assembly_entry *entry)
125 struct dependency_entry *dependency, *dependency2;
126 struct fileop_entry *fileop, *fileop2;
127 struct registryop_entry *registryop, *registryop2;
129 free(entry->filename);
130 free(entry->displayname);
131 clear_identity(&entry->identity);
133 LIST_FOR_EACH_ENTRY_SAFE(dependency, dependency2, &entry->dependencies, struct dependency_entry, entry)
135 list_remove(&dependency->entry);
136 free_dependency(dependency);
138 LIST_FOR_EACH_ENTRY_SAFE(fileop, fileop2, &entry->fileops, struct fileop_entry, entry)
140 list_remove(&fileop->entry);
141 free_fileop(fileop);
143 LIST_FOR_EACH_ENTRY_SAFE(registryop, registryop2, &entry->registryops, struct registryop_entry, entry)
145 list_remove(&registryop->entry);
146 free_registryop(registryop);
149 free(entry);
152 static WCHAR *get_xml_attribute(IXMLDOMElement *root, const WCHAR *name)
154 WCHAR *ret = NULL;
155 VARIANT var;
156 BSTR bstr;
158 if ((bstr = SysAllocString(name)))
160 VariantInit(&var);
161 if (SUCCEEDED(IXMLDOMElement_getAttribute(root, bstr, &var)))
163 ret = (V_VT(&var) == VT_BSTR) ? wcsdup(V_BSTR(&var)) : NULL;
164 VariantClear(&var);
166 SysFreeString(bstr);
169 return ret;
172 static BOOL check_xml_tagname(IXMLDOMElement *root, const WCHAR *tagname)
174 BOOL ret = FALSE;
175 BSTR bstr;
177 if (SUCCEEDED(IXMLDOMElement_get_tagName(root, &bstr)))
179 ret = !wcscmp(bstr, tagname);
180 SysFreeString(bstr);
183 return ret;
186 static IXMLDOMElement *select_xml_node(IXMLDOMElement *root, const WCHAR *name)
188 IXMLDOMElement *ret = NULL;
189 IXMLDOMNode *node;
190 BSTR bstr;
192 if ((bstr = SysAllocString(name)))
194 if (SUCCEEDED(IXMLDOMElement_selectSingleNode(root, bstr, &node)))
196 if (FAILED(IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void **)&ret)))
197 ret = NULL;
198 IXMLDOMNode_Release(node);
200 SysFreeString(bstr);
203 return ret;
206 static BOOL call_xml_callbacks(IXMLDOMElement *root, BOOL (*func)(IXMLDOMElement *child, WCHAR *tagname, void *context), void *context)
208 IXMLDOMNodeList *children;
209 IXMLDOMElement *child;
210 IXMLDOMNode *node;
211 BSTR tagname;
212 BOOL ret = TRUE;
214 if (FAILED(IXMLDOMElement_get_childNodes(root, &children)))
215 return FALSE;
217 while (ret && IXMLDOMNodeList_nextNode(children, &node) == S_OK)
219 if (SUCCEEDED(IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void **)&child)))
221 if (SUCCEEDED(IXMLDOMElement_get_tagName(child, &tagname)))
223 ret = func(child, tagname, context);
224 SysFreeString(tagname);
226 IXMLDOMElement_Release(child);
228 IXMLDOMNode_Release(node);
231 IXMLDOMNodeList_Release(children);
232 return ret;
235 static IXMLDOMElement *load_xml(const WCHAR *filename)
237 IXMLDOMDocument *document = NULL;
238 IXMLDOMElement *root = NULL;
239 VARIANT_BOOL success;
240 VARIANT variant;
241 BSTR bstr;
243 TRACE("Loading XML from %s\n", debugstr_w(filename));
245 if (!(bstr = SysAllocString(filename)))
246 return FALSE;
248 if (SUCCEEDED(CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&document)))
250 VariantInit(&variant);
251 V_VT(&variant) = VT_BSTR;
252 V_BSTR(&variant) = bstr;
254 if (SUCCEEDED(IXMLDOMDocument_load(document, variant, &success)) && success)
256 if (FAILED(IXMLDOMDocument_get_documentElement(document, &root)))
257 root = NULL;
259 IXMLDOMDocument_Release(document);
262 SysFreeString(bstr);
263 return root;
266 static BOOL read_identity(IXMLDOMElement *root, struct assembly_identity *identity)
268 memset(identity, 0, sizeof(*identity));
269 if (!(identity->name = get_xml_attribute(root, L"name"))) goto error;
270 if (!(identity->version = get_xml_attribute(root, L"version"))) goto error;
271 if (!(identity->architecture = get_xml_attribute(root, L"processorArchitecture"))) goto error;
272 if (!(identity->language = get_xml_attribute(root, L"language"))) goto error;
273 if (!(identity->pubkey_token = get_xml_attribute(root, L"publicKeyToken"))) goto error;
274 return TRUE;
276 error:
277 clear_identity(identity);
278 return FALSE;
281 /* <assembly><dependency><dependentAssembly> */
282 static BOOL read_dependent_assembly(IXMLDOMElement *root, struct assembly_identity *identity)
284 IXMLDOMElement *child = NULL;
285 WCHAR *dependency_type;
286 BOOL ret = FALSE;
288 if (!(dependency_type = get_xml_attribute(root, L"dependencyType")))
290 WARN("Failed to get dependency type, assuming install\n");
292 if (dependency_type && wcscmp(dependency_type, L"install") && wcscmp(dependency_type, L"prerequisite"))
294 FIXME("Unimplemented dependency type %s\n", debugstr_w(dependency_type));
295 goto error;
297 if (!(child = select_xml_node(root, L".//assemblyIdentity")))
299 FIXME("Failed to find assemblyIdentity child node\n");
300 goto error;
303 ret = read_identity(child, identity);
305 error:
306 if (child) IXMLDOMElement_Release(child);
307 free(dependency_type);
308 return ret;
311 /* <assembly><dependency> */
312 static BOOL read_dependency(IXMLDOMElement *child, WCHAR *tagname, void *context)
314 struct assembly_entry *assembly = context;
315 struct dependency_entry *entry;
317 if (wcscmp(tagname, L"dependentAssembly"))
319 FIXME("Don't know how to handle dependency tag %s\n", debugstr_w(tagname));
320 return FALSE;
323 if ((entry = alloc_dependency()))
325 if (read_dependent_assembly(child, &entry->identity))
327 TRACE("Found dependency %s\n", debugstr_w(entry->identity.name));
328 list_add_tail(&assembly->dependencies, &entry->entry);
329 return TRUE;
331 free_dependency(entry);
334 return FALSE;
337 static BOOL iter_dependency(IXMLDOMElement *root, struct assembly_entry *assembly)
339 return call_xml_callbacks(root, read_dependency, assembly);
342 /* <assembly><package><update><component> */
343 /* <assembly><package><update><package> */
344 static BOOL read_components(IXMLDOMElement *child, WCHAR *tagname, void *context)
346 struct assembly_entry *assembly = context;
347 struct dependency_entry *entry;
349 if (wcscmp(tagname, L"assemblyIdentity"))
351 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
352 return TRUE;
355 if ((entry = alloc_dependency()))
357 if (read_identity(child, &entry->identity))
359 TRACE("Found identity %s\n", debugstr_w(entry->identity.name));
360 list_add_tail(&assembly->dependencies, &entry->entry);
361 return TRUE;
363 free_dependency(entry);
366 return FALSE;
369 static BOOL iter_components(IXMLDOMElement *root, struct assembly_entry *assembly)
371 return call_xml_callbacks(root, read_components, assembly);
374 /* <assembly><package><update> */
375 static BOOL read_update(IXMLDOMElement *child, WCHAR *tagname, void *context)
377 struct assembly_entry *assembly = context;
379 if (!wcscmp(tagname, L"component"))
380 return iter_components(child, assembly);
381 if (!wcscmp(tagname, L"package"))
382 return iter_components(child, assembly);
383 if (!wcscmp(tagname, L"applicable"))
384 return TRUE;
386 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
387 return FALSE;
390 static BOOL iter_update(IXMLDOMElement *root, struct assembly_entry *assembly)
392 return call_xml_callbacks(root, read_update, assembly);
395 /* <assembly><package> */
396 static BOOL read_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
398 struct assembly_entry *assembly = context;
400 if (!wcscmp(tagname, L"update"))
401 return iter_update(child, assembly);
402 if (!wcscmp(tagname, L"parent"))
403 return TRUE;
405 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
406 return TRUE;
409 static BOOL iter_package(IXMLDOMElement *root, struct assembly_entry *assembly)
411 return call_xml_callbacks(root, read_package, assembly);
414 /* <assembly><file> */
415 static BOOL read_file(IXMLDOMElement *root, struct assembly_entry *assembly)
417 struct fileop_entry *entry;
419 if (!(entry = alloc_fileop()))
420 return FALSE;
422 if (!(entry->source = get_xml_attribute(root, L"sourceName"))) goto error;
423 if (!(entry->target = get_xml_attribute(root, L"destinationPath"))) goto error;
425 TRACE("Found fileop %s -> %s\n", debugstr_w(entry->source), debugstr_w(entry->target));
426 list_add_tail(&assembly->fileops, &entry->entry);
427 return TRUE;
429 error:
430 free_fileop(entry);
431 return FALSE;
434 /* <assembly><registryKeys><registryKey> */
435 static BOOL read_registry_key(IXMLDOMElement *child, WCHAR *tagname, void *context)
437 struct registryop_entry *registryop = context;
438 struct registrykv_entry *entry;
440 if (!wcscmp(tagname, L"securityDescriptor")) return TRUE;
441 if (!wcscmp(tagname, L"systemProtection")) return TRUE;
442 if (wcscmp(tagname, L"registryValue"))
444 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
445 return TRUE;
448 if (!(entry = alloc_registrykv()))
449 return FALSE;
451 if (!(entry->value_type = get_xml_attribute(child, L"valueType"))) goto error;
452 entry->name = get_xml_attribute(child, L"name"); /* optional */
453 entry->value = get_xml_attribute(child, L"value"); /* optional */
455 TRACE("Found registry %s -> %s\n", debugstr_w(entry->name), debugstr_w(entry->value));
456 list_add_tail(&registryop->keyvalues, &entry->entry);
457 return TRUE;
459 error:
460 free_registrykv(entry);
461 return FALSE;
464 static BOOL iter_registry_key(IXMLDOMElement *root, struct registryop_entry *registryop)
466 return call_xml_callbacks(root, read_registry_key, registryop);
469 /* <assembly><registryKeys> */
470 static BOOL read_registry_keys(IXMLDOMElement *child, WCHAR *tagname, void *context)
472 struct assembly_entry *assembly = context;
473 struct registryop_entry *entry;
474 WCHAR *keyname;
476 if (wcscmp(tagname, L"registryKey"))
478 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
479 return TRUE;
482 if (!(keyname = get_xml_attribute(child, L"keyName")))
484 FIXME("RegistryKey tag doesn't specify keyName\n");
485 return FALSE;
488 if ((entry = alloc_registryop()))
490 list_init(&entry->keyvalues);
491 if (iter_registry_key(child, entry))
493 entry->key = keyname;
494 TRACE("Found registryop %s\n", debugstr_w(entry->key));
495 list_add_tail(&assembly->registryops, &entry->entry);
496 return TRUE;
498 free_registryop(entry);
501 free(keyname);
502 return FALSE;
505 static BOOL iter_registry_keys(IXMLDOMElement *root, struct assembly_entry *assembly)
507 return call_xml_callbacks(root, read_registry_keys, assembly);
510 /* <assembly> */
511 static BOOL read_assembly(IXMLDOMElement *child, WCHAR *tagname, void *context)
513 struct assembly_entry *assembly = context;
515 if (!wcscmp(tagname, L"assemblyIdentity") && !assembly->identity.name)
516 return read_identity(child, &assembly->identity);
517 if (!wcscmp(tagname, L"dependency"))
518 return iter_dependency(child, assembly);
519 if (!wcscmp(tagname, L"package"))
520 return iter_package(child, assembly);
521 if (!wcscmp(tagname, L"file"))
522 return read_file(child, assembly);
523 if (!wcscmp(tagname, L"registryKeys"))
524 return iter_registry_keys(child, assembly);
525 if (!wcscmp(tagname, L"trustInfo"))
526 return TRUE;
527 if (!wcscmp(tagname, L"configuration"))
528 return TRUE;
529 if (!wcscmp(tagname, L"deployment"))
530 return TRUE;
532 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
533 return TRUE;
536 static BOOL iter_assembly(IXMLDOMElement *root, struct assembly_entry *assembly)
538 return call_xml_callbacks(root, read_assembly, assembly);
541 struct assembly_entry *load_manifest(const WCHAR *filename)
543 struct assembly_entry *entry = NULL;
544 IXMLDOMElement *root = NULL;
546 TRACE("Loading manifest %s\n", debugstr_w(filename));
548 if (!(root = load_xml(filename))) return NULL;
549 if (!check_xml_tagname(root, L"assembly"))
551 FIXME("Didn't find assembly root node?\n");
552 goto done;
555 if ((entry = alloc_assembly()))
557 entry->filename = wcsdup(filename);
558 entry->displayname = get_xml_attribute(root, L"displayName");
559 if (iter_assembly(root, entry)) goto done;
560 free_assembly(entry);
561 entry = NULL;
564 done:
565 IXMLDOMElement_Release(root);
566 return entry;
569 /* <unattend><servicing><package> */
570 static BOOL read_update_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
572 struct dependency_entry *entry;
573 struct list *update_list = context;
575 if (!wcscmp(tagname, L"source")) return TRUE;
576 if (wcscmp(tagname, L"assemblyIdentity"))
578 TRACE("Ignoring unexpected tag %s\n", debugstr_w(tagname));
579 return TRUE;
582 if ((entry = alloc_dependency()))
584 if (read_identity(child, &entry->identity))
586 TRACE("Found update %s\n", debugstr_w(entry->identity.name));
587 list_add_tail(update_list, &entry->entry);
588 return TRUE;
590 free_dependency(entry);
593 return FALSE;
596 static BOOL iter_update_package(IXMLDOMElement *root, struct list *update_list)
598 return call_xml_callbacks(root, read_update_package, update_list);
601 /* <unattend><servicing> */
602 static BOOL read_servicing(IXMLDOMElement *child, WCHAR *tagname, void *context)
604 struct list *update_list = context;
605 WCHAR *action;
606 BOOL ret = TRUE;
608 if (wcscmp(tagname, L"package"))
610 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
611 return TRUE;
614 if (!(action = get_xml_attribute(child, L"action")))
616 FIXME("Servicing tag doesn't specify action\n");
617 return FALSE;
620 if (!wcscmp(action, L"install"))
621 ret = iter_update_package(child, update_list);
622 else
623 FIXME("action %s not supported\n", debugstr_w(action));
625 free(action);
626 return ret;
629 static BOOL iter_servicing(IXMLDOMElement *root, struct list *update_list)
631 return call_xml_callbacks(root, read_servicing, update_list);
634 /* <unattend> */
635 static BOOL read_unattend(IXMLDOMElement *child, WCHAR *tagname, void *context)
637 struct list *update_list = context;
639 if (wcscmp(tagname, L"servicing"))
641 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
642 return TRUE;
645 return iter_servicing(child, update_list);
649 static BOOL iter_unattend(IXMLDOMElement *root, struct list *update_list)
651 return call_xml_callbacks(root, read_unattend, update_list);
654 BOOL load_update(const WCHAR *filename, struct list *update_list)
656 IXMLDOMElement *root = NULL;
657 BOOL ret = FALSE;
659 TRACE("Reading update %s\n", debugstr_w(filename));
661 if (!(root = load_xml(filename))) return FALSE;
662 if (!check_xml_tagname(root, L"unattend"))
664 FIXME("Didn't find unattend root node?\n");
665 goto done;
668 ret = iter_unattend(root, update_list);
670 done:
671 IXMLDOMElement_Release(root);
672 return ret;