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
);
278 memset(identity
, 0, sizeof(*identity
));
282 /* <assembly><dependency><dependentAssembly> */
283 static BOOL
read_dependent_assembly(IXMLDOMElement
*root
, struct assembly_identity
*identity
)
285 IXMLDOMElement
*child
= NULL
;
286 WCHAR
*dependency_type
;
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
));
298 if (!(child
= select_xml_node(root
, L
".//assemblyIdentity")))
300 FIXME("Failed to find assemblyIdentity child node\n");
304 ret
= read_identity(child
, identity
);
307 if (child
) IXMLDOMElement_Release(child
);
308 free(dependency_type
);
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
));
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
);
332 free_dependency(entry
);
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
));
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
);
364 free_dependency(entry
);
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"))
387 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
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"))
406 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
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()))
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
);
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
));
449 if (!(entry
= alloc_registrykv()))
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(®istryop
->keyvalues
, &entry
->entry
);
461 free_registrykv(entry
);
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
;
477 if (wcscmp(tagname
, L
"registryKey"))
479 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
483 if (!(keyname
= get_xml_attribute(child
, L
"keyName")))
485 FIXME("RegistryKey tag doesn't specify keyName\n");
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
);
499 free_registryop(entry
);
506 static BOOL
iter_registry_keys(IXMLDOMElement
*root
, struct assembly_entry
*assembly
)
508 return call_xml_callbacks(root
, read_registry_keys
, 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"))
528 if (!wcscmp(tagname
, L
"configuration"))
530 if (!wcscmp(tagname
, L
"deployment"))
533 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
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");
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
);
566 IXMLDOMElement_Release(root
);
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
));
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
);
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
;
609 if (wcscmp(tagname
, L
"package"))
611 FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname
));
615 if (!(action
= get_xml_attribute(child
, L
"action")))
617 FIXME("Servicing tag doesn't specify action\n");
621 if (!wcscmp(action
, L
"install"))
622 ret
= iter_update_package(child
, update_list
);
624 FIXME("action %s not supported\n", debugstr_w(action
));
630 static BOOL
iter_servicing(IXMLDOMElement
*root
, struct list
*update_list
)
632 return call_xml_callbacks(root
, read_servicing
, update_list
);
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
));
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
;
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");
669 ret
= iter_unattend(root
, update_list
);
672 IXMLDOMElement_Release(root
);