1use md5::{Digest, Md5};
16use roxmltree::Document;
17use std::collections::HashMap;
18
19fn get_logic_check(inp: &str, nonce: &str) -> String {
20 let mut out = String::new();
21 for c in nonce.chars() {
22 let idx = (c as u32) & 0xf;
23 if let Some(ch) = inp.chars().nth(idx as usize) {
24 out.push(ch);
25 } else {
26 out.push('.');
27 }
28 }
29 out
30}
31
32pub(crate) fn parse_version_xml(xml: &str) -> Option<String> {
33 let doc = Document::parse(xml).ok()?;
34 let latest = doc
35 .descendants()
36 .find(|n| n.has_tag_name("latest"))?
37 .text()?;
38 let mut parts: Vec<&str> = latest.split('/').collect();
39 if parts.len() == 3 {
40 parts.push(parts[0]);
41 }
42 if parts.len() >= 3 && parts[2].is_empty() {
43 parts[2] = parts[0];
44 }
45 Some(parts.join("/"))
46}
47
48pub(crate) fn binary_inform_req_xml(model: &str, region: &str, fw: &str, nonce: &str) -> String {
49 let logic_check = get_logic_check(fw, nonce);
50
51 format!(
52 r#"<FUSMsg>
53<FUSHdr><ProtoVer>1.0</ProtoVer><SessionID>0</SessionID><MsgID>1</MsgID></FUSHdr>
54<FUSBody>
55 <Put>
56 <CmdID>1</CmdID>
57 <ACCESS_MODE><Data>1</Data></ACCESS_MODE>
58 <BINARY_NATURE><Data>1</Data></BINARY_NATURE>
59 <REQUEST_TYPE><Data>2</Data></REQUEST_TYPE>
60 <LOGIC_CHECK><Data>{logic_check}</Data></LOGIC_CHECK>
61 <BINARY_SW_VERSION><Data>{fw}</Data></BINARY_SW_VERSION>
62 <BINARY_LOCAL_CODE><Data>{region}</Data></BINARY_LOCAL_CODE>
63 <BINARY_MODEL_NAME><Data>{model}</Data></BINARY_MODEL_NAME>
64 </Put>
65 <Get>
66 <CmdID>2</CmdID>
67 <BINARY_SW_VERSION></BINARY_SW_VERSION>
68 </Get>
69</FUSBody>
70</FUSMsg>"#
71 )
72}
73
74pub(crate) fn binary_init_req_xml(
75 filename: &str,
76 nonce: &str,
77 fw: &str,
78 model_type: &str,
79 region: &str,
80) -> String {
81 let start = filename.len().saturating_sub(25);
82 let end = filename.len().saturating_sub(9);
83 let checkinp = &filename[start..end];
84
85 let logic_check = get_logic_check(checkinp, nonce);
86
87 format!(
88 r#"<FUSMsg>
89<FUSHdr><ProtoVer>1.0</ProtoVer><SessionID>0</SessionID><MsgID>1</MsgID></FUSHdr>
90<FUSBody>
91 <Put>
92 <BINARY_NAME><Data>{filename}</Data></BINARY_NAME>
93 <BINARY_SW_VERSION><Data>{fw}</Data></BINARY_SW_VERSION>
94 <DEVICE_LOCAL_CODE><Data>{region}</Data></DEVICE_LOCAL_CODE>
95 <DEVICE_MODEL_TYPE><Data>{model_type}</Data></DEVICE_MODEL_TYPE>
96 <LOGIC_CHECK><Data>{logic_check}</Data></LOGIC_CHECK>
97 </Put>
98</FUSBody>
99</FUSMsg>"#
100 )
101}
102
103fn parse_xml_data(xml: &str) -> Option<HashMap<String, String>> {
104 let doc = Document::parse(xml).ok()?;
105
106 let status_str = doc
107 .root_element()
108 .children()
109 .find(|n| n.has_tag_name("FUSBody"))?
110 .children()
111 .find(|n| n.has_tag_name("Results"))?
112 .children()
113 .find(|n| n.has_tag_name("Status"))?
114 .text()?;
115
116 if status_str != "200" && status_str != "S00" {
117 return None;
118 }
119
120 let mut kv = HashMap::new();
121 doc.descendants()
122 .filter(|n| n.has_tag_name("Data"))
123 .for_each(|n| {
124 if let Some(v) = n.text() {
125 let parent = n.parent().unwrap();
126 kv.insert(parent.tag_name().name().to_string(), v.to_string());
127 }
128 });
129 Some(kv)
130}
131
132#[derive(Default, Clone)]
133pub struct BinaryInform {
134 pub version: String,
135 pub filename: String,
136 pub path: String,
137 pub size: u64,
138 pub key: Vec<u8>,
139 pub model_type: String,
140 pub region: String,
141}
142
143impl BinaryInform {
144 pub(crate) fn parse(xml: &str) -> Option<BinaryInform> {
145 let mut kv = parse_xml_data(xml)?;
146 let size: u64 = kv.get("BINARY_BYTE_SIZE")?.parse().ok()?;
147 let fw_ver = kv
148 .get("BINARY_SW_VERSION")
149 .cloned()
150 .or_else(|| kv.get("LATEST_FW_VERSION").cloned())?;
151 let logic_val = kv
152 .remove("LOGIC_VALUE_FACTORY")
153 .or_else(|| kv.remove("LOGIC_VALUE_HOME"))?;
154 let key = get_logic_check(&fw_ver, &logic_val);
155
156 Some(Self {
157 version: fw_ver,
158 filename: kv.remove("BINARY_NAME")?,
159 path: kv.remove("MODEL_PATH")?,
160 size,
161 key: Md5::digest(key.as_bytes()).to_vec(),
162 model_type: kv.remove("DEVICE_MODEL_TYPE")?,
163 region: kv.remove("BINARY_LOCAL_CODE")?,
164 })
165 }
166}