rust_rcs_client/provisioning/
local_provisioning_doc.rs

1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15extern crate chrono;
16extern crate quick_xml;
17
18use std::{fs::File, io::Write};
19
20use chrono::{DateTime, Duration, FixedOffset, Utc};
21
22use quick_xml::{
23    events::{BytesEnd, BytesStart, Event},
24    Reader, Writer,
25};
26
27use rust_rcs_core::{ffi::log::platform_log, util::raw_string::StrEq};
28
29use super::{
30    characteristic::Characteristic,
31    wap_provisioning_doc::{
32        read_wap_provisioning_doc, write_wap_provisioning_doc, WapProvisioningDoc,
33    },
34};
35
36const LOG_TAG: &str = "provisioning-doc";
37
38pub struct LocalProvisioningDoc {
39    root: Vec<ProvisiongingInfo>,
40}
41
42impl LocalProvisioningDoc {
43    pub fn new(root: Vec<ProvisiongingInfo>) -> LocalProvisioningDoc {
44        LocalProvisioningDoc { root }
45    }
46
47    pub fn get_provisioning_info(
48        &self,
49        get_default: bool,
50        id: &str,
51    ) -> Option<(
52        i64,
53        DateTime<FixedOffset>,
54        Option<(&str, DateTime<FixedOffset>)>,
55        Option<&WapProvisioningDoc>,
56    )> {
57        for provisioning_info in &self.root {
58            if (get_default && provisioning_info.default == "yes") || provisioning_info.id == id {
59                return destructure_provisioning_info(&provisioning_info);
60            }
61        }
62
63        None
64    }
65
66    pub fn unlink(mut self, is_default: bool, id: &str) -> Self {
67        self.root.retain(|provisioning_info| -> bool {
68            if (is_default && provisioning_info.default == "yes") || provisioning_info.id == id {
69                return false;
70            }
71
72            true
73        });
74
75        self
76    }
77
78    pub fn unlink_token(mut self, is_default: bool, id: &str) -> Self {
79        for provisioning_info in self.root.iter_mut() {
80            if (is_default && provisioning_info.default == "yes") || provisioning_info.id == id {
81                if provisioning_info.default == "yes" {
82                    provisioning_info.TOKEN_token.take();
83                    provisioning_info.TOKEN_validity_through.take();
84                    continue;
85                }
86            }
87        }
88
89        self
90    }
91
92    pub fn update_application(
93        mut self,
94        is_default: bool,
95        id: &str,
96        app_id: &str,
97        application: &Characteristic,
98    ) -> Self {
99        platform_log(
100            LOG_TAG,
101            format!("update application {} for server {}", app_id, id),
102        );
103
104        for provisioning_info in &mut self.root {
105            if (is_default && provisioning_info.default == "yes") || provisioning_info.id == id {
106                if let Some(ref mut wap_provisioning_doc) = &mut provisioning_info.wap_doc {
107                    wap_provisioning_doc.update_application(app_id, application);
108                } else {
109                    platform_log(
110                        LOG_TAG,
111                        format!("inserting application {} for server {}", app_id, id),
112                    );
113
114                    let mut v = Vec::new();
115
116                    v.push(application.clone());
117
118                    let wap_provisioning_doc = WapProvisioningDoc::new(v);
119
120                    provisioning_info.wap_doc.replace(wap_provisioning_doc);
121                }
122
123                continue;
124            }
125        }
126
127        self
128    }
129
130    pub fn update_vers_token(
131        &mut self,
132        is_default: bool,
133        id: &str,
134        vers: i64,
135        vers_validity_through: String,
136        token: Option<(&str, Option<i64>)>,
137    ) {
138        let update_token = |provisioning_info: &mut ProvisiongingInfo,
139                            token: Option<(&str, Option<i64>)>| {
140            if let Some((token_string, token_validity)) = token {
141                provisioning_info
142                    .TOKEN_token
143                    .replace(String::from(token_string));
144
145                if let Some(token_validity) = token_validity {
146                    if token_validity > 0 {
147                        let token_validity_through =
148                            (Utc::now() + Duration::seconds(token_validity)).to_rfc3339();
149                        provisioning_info
150                            .TOKEN_validity_through
151                            .replace(token_validity_through);
152                    } else {
153                        let token_validity_through =
154                            (Utc::now() + Duration::days(365)).to_rfc3339();
155                        provisioning_info
156                            .TOKEN_validity_through
157                            .replace(token_validity_through);
158                    }
159                } else {
160                    let token_validity_through = (Utc::now() + Duration::days(365)).to_rfc3339();
161                    provisioning_info
162                        .TOKEN_validity_through
163                        .replace(token_validity_through);
164                }
165            } else {
166                provisioning_info.TOKEN_token.take();
167                provisioning_info.TOKEN_validity_through.take();
168            }
169        };
170
171        for provisioning_info in &mut self.root {
172            if (is_default && provisioning_info.default == "yes") || provisioning_info.id == id {
173                provisioning_info.VERS_version = format!("{}", vers);
174                provisioning_info.VERS_validity_through = format!("{}", vers_validity_through);
175                update_token(provisioning_info, token);
176                return;
177            }
178        }
179
180        let mut provisioning_info = ProvisiongingInfo {
181            default: if is_default {
182                String::from("yes")
183            } else {
184                String::from("no")
185            },
186            id: String::from(id),
187            VERS_version: format!("{}", vers),
188            VERS_validity_through: vers_validity_through,
189            TOKEN_token: None,
190            TOKEN_validity_through: None,
191            wap_doc: None,
192        };
193
194        update_token(&mut provisioning_info, token);
195
196        self.root.push(provisioning_info);
197    }
198
199    pub fn remove_non_application_characteristics_for_default_server(&mut self) {
200        for provisioning_info in &mut self.root {
201            if provisioning_info.default == "yes" {
202                if let Some(wap_provisioning_doc) = &mut provisioning_info.wap_doc {
203                    wap_provisioning_doc.remove_non_application_characteristics();
204                }
205                break;
206            }
207        }
208    }
209
210    pub fn update_characteristics_for_default_server(&mut self, characteristic: &Characteristic) {
211        for provisioning_info in &mut self.root {
212            if provisioning_info.default == "yes" {
213                if let Some(wap_provisioning_doc) = &mut provisioning_info.wap_doc {
214                    wap_provisioning_doc.add_non_application_characteristic(characteristic);
215                } else {
216                    let v = Vec::new();
217                    let mut wap_provisioning_doc = WapProvisioningDoc::new(v);
218                    wap_provisioning_doc.add_non_application_characteristic(characteristic);
219                    provisioning_info.wap_doc.replace(wap_provisioning_doc);
220                }
221                break;
222            }
223        }
224    }
225
226    pub fn provisioning_files(&self) -> ProvisiongingFiles {
227        ProvisiongingFiles {
228            root: self.root.iter(),
229        }
230    }
231}
232
233pub fn read_provisionging_info<R>(
234    xml_reader: &mut Reader<R>,
235    buf: &mut Vec<u8>,
236    e: &BytesStart,
237) -> Option<ProvisiongingInfo>
238where
239    R: std::io::BufRead,
240{
241    let mut level = 1;
242
243    let mut default: Option<String> = None;
244    let mut id: Option<String> = None;
245    let mut VERS_version: Option<String> = None;
246    let mut VERS_validity_through: Option<String> = None;
247    let mut TOKEN_token: Option<String> = None;
248    let mut TOKEN_validity_through: Option<String> = None;
249
250    let mut wap_provisioning_doc: Option<WapProvisioningDoc> = None;
251
252    for attribute in e.attributes() {
253        if let Ok(attribute) = attribute {
254            if attribute.key.as_ref() == b"default" {
255                if let Ok(attribute_value) = attribute.unescape_value() {
256                    default.replace(attribute_value.into_owned());
257                }
258            } else if attribute.key.as_ref() == b"id" {
259                if let Ok(attribute_value) = attribute.unescape_value() {
260                    id.replace(attribute_value.into_owned());
261                }
262            } else if attribute.key.as_ref() == b"VERS_version" {
263                if let Ok(attribute_value) = attribute.unescape_value() {
264                    VERS_version.replace(attribute_value.into_owned());
265                }
266            } else if attribute.key.as_ref() == b"VERS_validity_through" {
267                if let Ok(attribute_value) = attribute.unescape_value() {
268                    VERS_validity_through.replace(attribute_value.into_owned());
269                }
270            } else if attribute.key.as_ref() == b"TOKEN_token" {
271                if let Ok(attribute_value) = attribute.unescape_value() {
272                    TOKEN_token.replace(attribute_value.into_owned());
273                }
274            } else if attribute.key.as_ref() == b"TOKEN_validity_through" {
275                if let Ok(attribute_value) = attribute.unescape_value() {
276                    TOKEN_validity_through.replace(attribute_value.into_owned());
277                }
278            }
279        }
280    }
281
282    loop {
283        match xml_reader.read_event_into(buf) {
284            Ok(Event::Start(ref e)) => {
285                level += 1;
286                if e.name().as_ref().equals_bytes(b"wap-provisioningdoc", true) {
287                    let mut buf = Vec::new();
288                    wap_provisioning_doc
289                        .replace(read_wap_provisioning_doc(xml_reader, &mut buf, e));
290                    level -= 1;
291                }
292            }
293
294            Ok(Event::End(_)) => {
295                level -= 1;
296                if level == 0 {
297                    break;
298                }
299            }
300
301            Ok(Event::Eof) | Err(_) => {
302                return None;
303            }
304
305            _ => {}
306        }
307    }
308
309    buf.clear();
310
311    if let (Some(default), Some(id), Some(VERS_version), Some(VERS_validity_through)) =
312        (default, id, VERS_version, VERS_validity_through)
313    {
314        return Some(ProvisiongingInfo {
315            default,
316            id,
317            VERS_version,
318            VERS_validity_through,
319            TOKEN_token,
320            TOKEN_validity_through,
321            wap_doc: wap_provisioning_doc,
322        });
323    }
324
325    None
326}
327
328pub fn read_provisionging_doc<R>(
329    xml_reader: &mut Reader<R>,
330    buf: &mut Vec<u8>,
331    e: &BytesStart,
332) -> LocalProvisioningDoc
333where
334    R: std::io::BufRead,
335{
336    let mut level = 1;
337
338    let mut root = Vec::new();
339
340    loop {
341        match xml_reader.read_event_into(buf) {
342            Ok(Event::Start(ref e)) => {
343                level += 1;
344                if e.name().as_ref().equals_bytes(b"provisioning-info", true) {
345                    let mut buf = Vec::new();
346                    if let Some(provisioning_info) =
347                        read_provisionging_info(xml_reader, &mut buf, e)
348                    {
349                        root.push(provisioning_info);
350                    }
351                    level -= 1;
352                }
353            }
354
355            Ok(Event::End(_)) => {
356                level -= 1;
357                if level == 0 {
358                    break;
359                }
360            }
361
362            Ok(Event::Eof) | Err(_) => {
363                break;
364            }
365
366            _ => {}
367        }
368    }
369
370    buf.clear();
371
372    LocalProvisioningDoc { root }
373}
374
375pub fn load_provisionging_doc(path: &str) -> Option<LocalProvisioningDoc> {
376    if let Ok(mut xml_reader) = Reader::from_file(path) {
377        let mut buf = Vec::new();
378        loop {
379            match xml_reader.read_event_into(&mut buf) {
380                Ok(Event::Start(ref e)) => {
381                    if e.name().as_ref().equals_bytes(b"provisioning-doc", true) {
382                        let mut buf = Vec::new();
383                        return Some(read_provisionging_doc(&mut xml_reader, &mut buf, e));
384                    }
385                }
386
387                Ok(Event::Eof) | Err(_) => {
388                    break;
389                }
390
391                _ => {}
392            }
393        }
394    }
395
396    None
397}
398
399pub fn write_provisioning_info<W>(
400    xml_writer: &mut Writer<W>,
401    provisioning_info: &ProvisiongingInfo,
402) -> quick_xml::Result<()>
403where
404    W: Write,
405{
406    let mut elem = BytesStart::new("provisioning-info");
407
408    elem.push_attribute(("default", provisioning_info.default.as_str()));
409    elem.push_attribute(("id", provisioning_info.id.as_str()));
410    elem.push_attribute(("VERS_version", provisioning_info.VERS_version.as_str()));
411    elem.push_attribute((
412        "VERS_validity_through",
413        provisioning_info.VERS_validity_through.as_str(),
414    ));
415
416    if let (Some(TOKEN_token), Some(TOKEN_validity_through)) = (
417        &provisioning_info.TOKEN_token,
418        &provisioning_info.TOKEN_validity_through,
419    ) {
420        elem.push_attribute(("TOKEN_token", TOKEN_token.as_str()));
421        elem.push_attribute(("TOKEN_validity_through", TOKEN_validity_through.as_str()));
422    }
423
424    xml_writer.write_event(Event::Start(elem))?;
425
426    if let Some(wap_provisioning_doc) = &provisioning_info.wap_doc {
427        write_wap_provisioning_doc(xml_writer, wap_provisioning_doc)?;
428    }
429
430    xml_writer.write_event(Event::End(BytesEnd::new("provisioning-info")))?;
431
432    Ok(())
433}
434
435pub fn write_provisionging_doc<W>(
436    xml_writer: &mut Writer<W>,
437    provisioning_doc: &LocalProvisioningDoc,
438) -> quick_xml::Result<()>
439where
440    W: Write,
441{
442    let elem = BytesStart::new("provisioning-doc");
443
444    xml_writer.write_event(Event::Start(elem))?;
445
446    for provisioning_info in &provisioning_doc.root {
447        write_provisioning_info(xml_writer, provisioning_info)?;
448    }
449
450    xml_writer.write_event(Event::End(BytesEnd::new("provisioning-doc")))?;
451
452    Ok(())
453}
454
455pub fn store_provisionging_doc(
456    provisioning_doc: &LocalProvisioningDoc,
457    path: &str,
458) -> Result<(), ()> {
459    if let Ok(f) = File::create(path) {
460        let mut xml_writer = Writer::new(f);
461        if let Ok(_) = write_provisionging_doc(&mut xml_writer, provisioning_doc) {
462            return Ok(());
463        }
464    }
465
466    Err(())
467}
468
469pub struct ProvisiongingInfo {
470    pub default: String,
471    pub id: String,
472    pub VERS_version: String,
473    pub VERS_validity_through: String,
474    pub TOKEN_token: Option<String>,
475    pub TOKEN_validity_through: Option<String>,
476    pub wap_doc: Option<WapProvisioningDoc>,
477}
478
479fn destructure_provisioning_info(
480    provisioning_info: &ProvisiongingInfo,
481) -> Option<(
482    i64,
483    DateTime<FixedOffset>,
484    Option<(&str, DateTime<FixedOffset>)>,
485    Option<&WapProvisioningDoc>,
486)> {
487    platform_log(LOG_TAG, "de-structuring provisioning-info");
488
489    if let (Ok(vers_version), Ok(vers_validity_through)) = (
490        provisioning_info.VERS_version.parse::<i64>(),
491        DateTime::parse_from_rfc3339(&provisioning_info.VERS_validity_through),
492    ) {
493        if let (Some(token_string), Some(token_validity_through)) = (
494            &provisioning_info.TOKEN_token,
495            &provisioning_info.TOKEN_validity_through,
496        ) {
497            if let (Ok(token_validity_through),) =
498                (DateTime::parse_from_rfc3339(&token_validity_through),)
499            {
500                if let Some(wap_doc) = &provisioning_info.wap_doc {
501                    return Some((
502                        vers_version,
503                        vers_validity_through,
504                        Some((&token_string, token_validity_through)),
505                        Some(wap_doc),
506                    ));
507                } else {
508                    return Some((
509                        vers_version,
510                        vers_validity_through,
511                        Some((&token_string, token_validity_through)),
512                        None,
513                    ));
514                }
515            }
516        } else {
517            if let Some(wap_doc) = &provisioning_info.wap_doc {
518                return Some((vers_version, vers_validity_through, None, Some(wap_doc)));
519            } else {
520                return Some((vers_version, vers_validity_through, None, None));
521            }
522        }
523    }
524
525    None
526}
527
528pub struct ProvisiongingFiles<'a> {
529    root: std::slice::Iter<'a, ProvisiongingInfo>,
530}
531
532impl<'a> Iterator for ProvisiongingFiles<'a> {
533    type Item = &'a WapProvisioningDoc;
534    fn next(&mut self) -> Option<&'a WapProvisioningDoc> {
535        while let Some(provisioning_info) = self.root.next() {
536            if let Some(wap_doc) = &provisioning_info.wap_doc {
537                return Some(wap_doc);
538            }
539        }
540
541        None
542    }
543}