Skip to main content

provenant/parsers/
microsoft_update_manifest.rs

1//! Parser for Microsoft Update Manifest (.mum) files.
2//!
3//! Extracts Windows Update package metadata from .mum XML manifest files.
4//!
5//! # Supported Formats
6//! - `*.mum` - Microsoft Update Manifest XML files
7//!
8//! # Implementation Notes
9//! - Format: XML with assembly and package metadata
10//! - Spec: Windows Update manifests
11
12use crate::models::{DatasourceId, PackageType};
13use std::fs;
14use std::path::Path;
15
16use log::warn;
17use quick_xml::events::Event;
18use quick_xml::reader::Reader;
19
20use crate::models::PackageData;
21
22use super::PackageParser;
23
24const PACKAGE_TYPE: PackageType = PackageType::WindowsUpdate;
25
26pub struct MicrosoftUpdateManifestParser;
27
28impl PackageParser for MicrosoftUpdateManifestParser {
29    const PACKAGE_TYPE: PackageType = PACKAGE_TYPE;
30
31    fn is_match(path: &Path) -> bool {
32        path.extension().is_some_and(|ext| ext == "mum")
33    }
34
35    fn extract_packages(path: &Path) -> Vec<PackageData> {
36        let content = match fs::read_to_string(path) {
37            Ok(c) => c,
38            Err(e) => {
39                warn!("Failed to read .mum file {:?}: {}", path, e);
40                return vec![PackageData {
41                    package_type: Some(PACKAGE_TYPE),
42                    datasource_id: Some(DatasourceId::MicrosoftUpdateManifestMum),
43                    ..Default::default()
44                }];
45            }
46        };
47
48        vec![parse_mum_xml(&content)]
49    }
50}
51
52pub(crate) fn parse_mum_xml(content: &str) -> PackageData {
53    let mut reader = Reader::from_str(content);
54    reader.config_mut().trim_text(true);
55
56    let mut name = None;
57    let mut version = None;
58    let mut description = None;
59    let mut copyright = None;
60    let mut homepage_url = None;
61
62    let mut buf = Vec::new();
63
64    loop {
65        match reader.read_event_into(&mut buf) {
66            Ok(Event::Empty(e)) => {
67                if e.name().as_ref() == b"assemblyIdentity" {
68                    for attr in e.attributes().filter_map(|a| a.ok()) {
69                        match attr.key.as_ref() {
70                            b"name" => name = String::from_utf8(attr.value.to_vec()).ok(),
71                            b"version" => version = String::from_utf8(attr.value.to_vec()).ok(),
72                            _ => {}
73                        }
74                    }
75                }
76            }
77            Ok(Event::Start(e)) => {
78                if e.name().as_ref() == b"assembly" {
79                    for attr in e.attributes().filter_map(|a| a.ok()) {
80                        match attr.key.as_ref() {
81                            b"description" => {
82                                description = String::from_utf8(attr.value.to_vec()).ok()
83                            }
84                            b"copyright" => copyright = String::from_utf8(attr.value.to_vec()).ok(),
85                            b"supportInformation" => {
86                                homepage_url = String::from_utf8(attr.value.to_vec()).ok()
87                            }
88                            _ => {}
89                        }
90                    }
91                }
92            }
93            Ok(Event::Eof) => break,
94            Err(e) => {
95                warn!(
96                    "Error parsing XML at position {}: {}",
97                    reader.buffer_position(),
98                    e
99                );
100                break;
101            }
102            _ => {}
103        }
104        buf.clear();
105    }
106
107    PackageData {
108        package_type: Some(PACKAGE_TYPE),
109        name,
110        version,
111        description,
112        homepage_url,
113        copyright,
114        datasource_id: Some(DatasourceId::MicrosoftUpdateManifestMum),
115        ..Default::default()
116    }
117}
118
119crate::register_parser!(
120    "Microsoft Update Manifest .mum file",
121    &["*.mum"],
122    "windows-update",
123    "",
124    None,
125);