ntdll: Implement NtCreateToken().
[wine.git] / programs / wusa / manifest.c
blob986bda27cd85d528419b0f584cc4839b73131866
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 memset(identity, 0, sizeof(*identity));
279 return FALSE;
282 /* <assembly><dependency><dependentAssembly> */
283 static BOOL read_dependent_assembly(IXMLDOMElement *root, struct assembly_identity *identity)
285 IXMLDOMElement *child = NULL;
286 WCHAR *dependency_type;
287 BOOL ret = FALSE;
289 if (!(dependency_type = get_xml_attribute(root, L"dependencyType")))
291 WARN("Failed to get dependency type, assuming install\n");
293 if (dependency_type && wcscmp(dependency_type, L"install") && wcscmp(dependency_type, L"prerequisite"))
295 FIXME("Unimplemented dependency type %s\n", debugstr_w(dependency_type));
296 goto error;
298 if (!(child = select_xml_node(root, L".//assemblyIdentity")))
300 FIXME("Failed to find assemblyIdentity child node\n");
301 goto error;
304 ret = read_identity(child, identity);
306 error:
307 if (child) IXMLDOMElement_Release(child);
308 free(dependency_type);
309 return ret;
312 /* <assembly><dependency> */
313 static BOOL read_dependency(IXMLDOMElement *child, WCHAR *tagname, void *context)
315 struct assembly_entry *assembly = context;
316 struct dependency_entry *entry;
318 if (wcscmp(tagname, L"dependentAssembly"))
320 FIXME("Don't know how to handle dependency tag %s\n", debugstr_w(tagname));
321 return FALSE;
324 if ((entry = alloc_dependency()))
326 if (read_dependent_assembly(child, &entry->identity))
328 TRACE("Found dependency %s\n", debugstr_w(entry->identity.name));
329 list_add_tail(&assembly->dependencies, &entry->entry);
330 return TRUE;
332 free_dependency(entry);
335 return FALSE;
338 static BOOL iter_dependency(IXMLDOMElement *root, struct assembly_entry *assembly)
340 return call_xml_callbacks(root, read_dependency, assembly);
343 /* <assembly><package><update><component> */
344 /* <assembly><package><update><package> */
345 static BOOL read_components(IXMLDOMElement *child, WCHAR *tagname, void *context)
347 struct assembly_entry *assembly = context;
348 struct dependency_entry *entry;
350 if (wcscmp(tagname, L"assemblyIdentity"))
352 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
353 return TRUE;
356 if ((entry = alloc_dependency()))
358 if (read_identity(child, &entry->identity))
360 TRACE("Found identity %s\n", debugstr_w(entry->identity.name));
361 list_add_tail(&assembly->dependencies, &entry->entry);
362 return TRUE;
364 free_dependency(entry);
367 return FALSE;
370 static BOOL iter_components(IXMLDOMElement *root, struct assembly_entry *assembly)
372 return call_xml_callbacks(root, read_components, assembly);
375 /* <assembly><package><update> */
376 static BOOL read_update(IXMLDOMElement *child, WCHAR *tagname, void *context)
378 struct assembly_entry *assembly = context;
380 if (!wcscmp(tagname, L"component"))
381 return iter_components(child, assembly);
382 if (!wcscmp(tagname, L"package"))
383 return iter_components(child, assembly);
384 if (!wcscmp(tagname, L"applicable"))
385 return TRUE;
387 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
388 return FALSE;
391 static BOOL iter_update(IXMLDOMElement *root, struct assembly_entry *assembly)
393 return call_xml_callbacks(root, read_update, assembly);
396 /* <assembly><package> */
397 static BOOL read_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
399 struct assembly_entry *assembly = context;
401 if (!wcscmp(tagname, L"update"))
402 return iter_update(child, assembly);
403 if (!wcscmp(tagname, L"parent"))
404 return TRUE;
406 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
407 return TRUE;
410 static BOOL iter_package(IXMLDOMElement *root, struct assembly_entry *assembly)
412 return call_xml_callbacks(root, read_package, assembly);
415 /* <assembly><file> */
416 static BOOL read_file(IXMLDOMElement *root, struct assembly_entry *assembly)
418 struct fileop_entry *entry;
420 if (!(entry = alloc_fileop()))
421 return FALSE;
423 if (!(entry->source = get_xml_attribute(root, L"sourceName"))) goto error;
424 if (!(entry->target = get_xml_attribute(root, L"destinationPath"))) goto error;
426 TRACE("Found fileop %s -> %s\n", debugstr_w(entry->source), debugstr_w(entry->target));
427 list_add_tail(&assembly->fileops, &entry->entry);
428 return TRUE;
430 error:
431 free_fileop(entry);
432 return FALSE;
435 /* <assembly><registryKeys><registryKey> */
436 static BOOL read_registry_key(IXMLDOMElement *child, WCHAR *tagname, void *context)
438 struct registryop_entry *registryop = context;
439 struct registrykv_entry *entry;
441 if (!wcscmp(tagname, L"securityDescriptor")) return TRUE;
442 if (!wcscmp(tagname, L"systemProtection")) return TRUE;
443 if (wcscmp(tagname, L"registryValue"))
445 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
446 return TRUE;
449 if (!(entry = alloc_registrykv()))
450 return FALSE;
452 if (!(entry->value_type = get_xml_attribute(child, L"valueType"))) goto error;
453 entry->name = get_xml_attribute(child, L"name"); /* optional */
454 entry->value = get_xml_attribute(child, L"value"); /* optional */
456 TRACE("Found registry %s -> %s\n", debugstr_w(entry->name), debugstr_w(entry->value));
457 list_add_tail(&registryop->keyvalues, &entry->entry);
458 return TRUE;
460 error:
461 free_registrykv(entry);
462 return FALSE;
465 static BOOL iter_registry_key(IXMLDOMElement *root, struct registryop_entry *registryop)
467 return call_xml_callbacks(root, read_registry_key, registryop);
470 /* <assembly><registryKeys> */
471 static BOOL read_registry_keys(IXMLDOMElement *child, WCHAR *tagname, void *context)
473 struct assembly_entry *assembly = context;
474 struct registryop_entry *entry;
475 WCHAR *keyname;
477 if (wcscmp(tagname, L"registryKey"))
479 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
480 return TRUE;
483 if (!(keyname = get_xml_attribute(child, L"keyName")))
485 FIXME("RegistryKey tag doesn't specify keyName\n");
486 return FALSE;
489 if ((entry = alloc_registryop()))
491 list_init(&entry->keyvalues);
492 if (iter_registry_key(child, entry))
494 entry->key = keyname;
495 TRACE("Found registryop %s\n", debugstr_w(entry->key));
496 list_add_tail(&assembly->registryops, &entry->entry);
497 return TRUE;
499 free_registryop(entry);
502 free(keyname);
503 return FALSE;
506 static BOOL iter_registry_keys(IXMLDOMElement *root, struct assembly_entry *assembly)
508 return call_xml_callbacks(root, read_registry_keys, assembly);
511 /* <assembly> */
512 static BOOL read_assembly(IXMLDOMElement *child, WCHAR *tagname, void *context)
514 struct assembly_entry *assembly = context;
516 if (!wcscmp(tagname, L"assemblyIdentity") && !assembly->identity.name)
517 return read_identity(child, &assembly->identity);
518 if (!wcscmp(tagname, L"dependency"))
519 return iter_dependency(child, assembly);
520 if (!wcscmp(tagname, L"package"))
521 return iter_package(child, assembly);
522 if (!wcscmp(tagname, L"file"))
523 return read_file(child, assembly);
524 if (!wcscmp(tagname, L"registryKeys"))
525 return iter_registry_keys(child, assembly);
526 if (!wcscmp(tagname, L"trustInfo"))
527 return TRUE;
528 if (!wcscmp(tagname, L"configuration"))
529 return TRUE;
530 if (!wcscmp(tagname, L"deployment"))
531 return TRUE;
533 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
534 return TRUE;
537 static BOOL iter_assembly(IXMLDOMElement *root, struct assembly_entry *assembly)
539 return call_xml_callbacks(root, read_assembly, assembly);
542 struct assembly_entry *load_manifest(const WCHAR *filename)
544 struct assembly_entry *entry = NULL;
545 IXMLDOMElement *root = NULL;
547 TRACE("Loading manifest %s\n", debugstr_w(filename));
549 if (!(root = load_xml(filename))) return NULL;
550 if (!check_xml_tagname(root, L"assembly"))
552 FIXME("Didn't find assembly root node?\n");
553 goto done;
556 if ((entry = alloc_assembly()))
558 entry->filename = wcsdup(filename);
559 entry->displayname = get_xml_attribute(root, L"displayName");
560 if (iter_assembly(root, entry)) goto done;
561 free_assembly(entry);
562 entry = NULL;
565 done:
566 IXMLDOMElement_Release(root);
567 return entry;
570 /* <unattend><servicing><package> */
571 static BOOL read_update_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
573 struct dependency_entry *entry;
574 struct list *update_list = context;
576 if (!wcscmp(tagname, L"source")) return TRUE;
577 if (wcscmp(tagname, L"assemblyIdentity"))
579 TRACE("Ignoring unexpected tag %s\n", debugstr_w(tagname));
580 return TRUE;
583 if ((entry = alloc_dependency()))
585 if (read_identity(child, &entry->identity))
587 TRACE("Found update %s\n", debugstr_w(entry->identity.name));
588 list_add_tail(update_list, &entry->entry);
589 return TRUE;
591 free(entry);
594 return FALSE;
597 static BOOL iter_update_package(IXMLDOMElement *root, struct list *update_list)
599 return call_xml_callbacks(root, read_update_package, update_list);
602 /* <unattend><servicing> */
603 static BOOL read_servicing(IXMLDOMElement *child, WCHAR *tagname, void *context)
605 struct list *update_list = context;
606 WCHAR *action;
607 BOOL ret = TRUE;
609 if (wcscmp(tagname, L"package"))
611 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
612 return TRUE;
615 if (!(action = get_xml_attribute(child, L"action")))
617 FIXME("Servicing tag doesn't specify action\n");
618 return FALSE;
621 if (!wcscmp(action, L"install"))
622 ret = iter_update_package(child, update_list);
623 else
624 FIXME("action %s not supported\n", debugstr_w(action));
626 free(action);
627 return ret;
630 static BOOL iter_servicing(IXMLDOMElement *root, struct list *update_list)
632 return call_xml_callbacks(root, read_servicing, update_list);
635 /* <unattend> */
636 static BOOL read_unattend(IXMLDOMElement *child, WCHAR *tagname, void *context)
638 struct list *update_list = context;
640 if (wcscmp(tagname, L"servicing"))
642 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
643 return TRUE;
646 return iter_servicing(child, update_list);
650 static BOOL iter_unattend(IXMLDOMElement *root, struct list *update_list)
652 return call_xml_callbacks(root, read_unattend, update_list);
655 BOOL load_update(const WCHAR *filename, struct list *update_list)
657 IXMLDOMElement *root = NULL;
658 BOOL ret = FALSE;
660 TRACE("Reading update %s\n", debugstr_w(filename));
662 if (!(root = load_xml(filename))) return FALSE;
663 if (!check_xml_tagname(root, L"unattend"))
665 FIXME("Didn't find unattend root node?\n");
666 goto done;
669 ret = iter_unattend(root, update_list);
671 done:
672 IXMLDOMElement_Release(root);
673 return ret;