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
27 #include "wine/debug.h"
28 #include "wine/list.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");
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");
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");
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");
60 list_init(&entry
->keyvalues
);
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");
71 list_init(&entry
->dependencies
);
72 list_init(&entry
->fileops
);
73 list_init(&entry
->registryops
);
78 static void clear_identity(struct assembly_identity
*entry
)
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
);
93 static void free_fileop(struct fileop_entry
*entry
)
100 static void free_registrykv(struct registrykv_entry
*entry
)
103 free(entry
->value_type
);
108 static void free_registryop(struct registryop_entry
*entry
)
110 struct registrykv_entry
*keyvalue
, *keyvalue2
;
114 LIST_FOR_EACH_ENTRY_SAFE(keyvalue
, keyvalue2
, &entry
->keyvalues
, struct registrykv_entry
, entry
)
116 list_remove(&keyvalue
->entry
);
117 free_registrykv(keyvalue
);
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
);
143 LIST_FOR_EACH_ENTRY_SAFE(registryop
, registryop2
, &entry
->registryops
, struct registryop_entry
, entry
)
145 list_remove(®istryop
->entry
);
146 free_registryop(registryop
);
152 static WCHAR
*get_xml_attribute(IXMLDOMElement
*root
, const WCHAR
*name
)
158 if ((bstr
= SysAllocString(name
)))
161 if (SUCCEEDED(IXMLDOMElement_getAttribute(root
, bstr
, &var
)))
163 ret
= (V_VT(&var
) == VT_BSTR
) ? wcsdup(V_BSTR(&var
)) : NULL
;
172 static BOOL
check_xml_tagname(IXMLDOMElement
*root
, const WCHAR
*tagname
)
177 if (SUCCEEDED(IXMLDOMElement_get_tagName(root
, &bstr
)))
179 ret
= !wcscmp(bstr
, tagname
);
186 static IXMLDOMElement
*select_xml_node(IXMLDOMElement
*root
, const WCHAR
*name
)
188 IXMLDOMElement
*ret
= NULL
;
192 if ((bstr
= SysAllocString(name
)))
194 if (SUCCEEDED(IXMLDOMElement_selectSingleNode(root
, bstr
, &node
)))
196 if (FAILED(IXMLDOMNode_QueryInterface(node
, &IID_IXMLDOMElement
, (void **)&ret
)))
198 IXMLDOMNode_Release(node
);
206 static BOOL
call_xml_callbacks(IXMLDOMElement
*root
, BOOL (*func
)(IXMLDOMElement
*child
, WCHAR
*tagname
, void *context
), void *context
)
208 IXMLDOMNodeList
*children
;
209 IXMLDOMElement
*child
;
214 if (FAILED(IXMLDOMElement_get_childNodes(root
, &children
)))
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
);
235 static IXMLDOMElement
*load_xml(const WCHAR
*filename
)
237 IXMLDOMDocument
*document
= NULL
;
238 IXMLDOMElement
*root
= NULL
;
239 VARIANT_BOOL success
;
243 TRACE("Loading XML from %s\n", debugstr_w(filename
));
245 if (!(bstr
= SysAllocString(filename
)))
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
)))
259 IXMLDOMDocument_Release(document
);
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
;
277 clear_identity(identity
);
281 /* <assembly><dependency><dependentAssembly> */
282 static BOOL
read_dependent_assembly(IXMLDOMElement
*root
, struct assembly_identity
*identity
)
284 IXMLDOMElement
*child
= NULL
;
285 WCHAR
*dependency_type
;
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
));
297 if (!(child
= select_xml_node(root
, L
".//assemblyIdentity")))
299 FIXME("Failed to find assemblyIdentity child node\n");
303 ret
= read_identity(child
, identity
);
306 if (child
) IXMLDOMElement_Release(child
);
307 free(dependency_type
);
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
));
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
);
331 free_dependency(entry
);
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
));
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
);
363 free_dependency(entry
);
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"))
386 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
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"))
405 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
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()))
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
);
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
));
448 if (!(entry
= alloc_registrykv()))
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(®istryop
->keyvalues
, &entry
->entry
);
460 free_registrykv(entry
);
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
;
476 if (wcscmp(tagname
, L
"registryKey"))
478 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
482 if (!(keyname
= get_xml_attribute(child
, L
"keyName")))
484 FIXME("RegistryKey tag doesn't specify keyName\n");
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
);
498 free_registryop(entry
);
505 static BOOL
iter_registry_keys(IXMLDOMElement
*root
, struct assembly_entry
*assembly
)
507 return call_xml_callbacks(root
, read_registry_keys
, 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"))
527 if (!wcscmp(tagname
, L
"configuration"))
529 if (!wcscmp(tagname
, L
"deployment"))
532 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
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");
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
);
565 IXMLDOMElement_Release(root
);
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
));
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
);
590 free_dependency(entry
);
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
;
608 if (wcscmp(tagname
, L
"package"))
610 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
614 if (!(action
= get_xml_attribute(child
, L
"action")))
616 FIXME("Servicing tag doesn't specify action\n");
620 if (!wcscmp(action
, L
"install"))
621 ret
= iter_update_package(child
, update_list
);
623 FIXME("action %s not supported\n", debugstr_w(action
));
629 static BOOL
iter_servicing(IXMLDOMElement
*root
, struct list
*update_list
)
631 return call_xml_callbacks(root
, read_servicing
, update_list
);
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
));
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
;
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");
668 ret
= iter_unattend(root
, update_list
);
671 IXMLDOMElement_Release(root
);