huawei_modem/gsm_encoding/
udh.rs

1//! Utilities for dealing with User Data Headers (used for concatenated SMS, among other things)
2//! inside messages.
3//!
4//! [This Wikipedia article](https://en.wikipedia.org/wiki/User_Data_Header) explains what this is
5//! for pretty well. Most uses of the UDH are vestigial; nowadays it's mostly useful for sending
6//! concatenated SMS.
7use std::convert::TryFrom;
8use crate::errors::*;
9
10/// Component of a User Data Header.
11#[derive(Debug, Clone)]
12pub struct UdhComponent {
13    /// Component identifier.
14    pub id: u8,
15    /// Component data.
16    pub data: Vec<u8>
17}
18/// A User Data Header itself.
19///
20/// You'll likely just want to call `get_concatenated_sms_data` on this to check whether the
21/// message is concatenated.
22#[derive(Debug, Clone)]
23pub struct UserDataHeader {
24    pub components: Vec<UdhComponent>
25}
26/// Data about a concatenated SMS.
27#[derive(Debug, Clone)]
28pub struct ConcatenatedSmsData {
29    /// Reference that identifies which message this is a part of - this is like an ID for the
30    /// whole message.
31    pub reference: u16,
32    /// How many parts to the message exist (e.g. 2).
33    pub parts: u8,
34    /// Which part this is (e.g. 1 of 2).
35    pub sequence: u8
36}
37impl UserDataHeader {
38    /// If there is concatenated SMS data in this header, return it.
39    pub fn get_concatenated_sms_data(&self) -> Option<ConcatenatedSmsData> {
40        for comp in self.components.iter() {
41            if comp.id == 0 && comp.data.len() == 3 {
42                return Some(ConcatenatedSmsData {
43                    reference: comp.data[0] as _,
44                    parts: comp.data[1],
45                    sequence: comp.data[2]
46                });
47            }
48            if comp.id == 8 && comp.data.len() == 4 {
49                let reference = ((comp.data[0] as u16) << 8) | (comp.data[1] as u16);
50                return Some(ConcatenatedSmsData {
51                    reference,
52                    parts: comp.data[2],
53                    sequence: comp.data[3]
54                });
55            }
56        }
57        None
58    }
59    /// Serialize this UDH to wire format.
60    pub fn as_bytes(&self) -> Vec<u8> {
61        let mut ret = vec![];
62        for comp in self.components.iter() {
63            ret.push(comp.id);
64            ret.push(comp.data.len() as u8);
65            ret.extend(comp.data.clone());
66        }
67        let len = ret.len() as u8;
68        ret.insert(0, len);
69        ret
70    }
71}
72impl<'a> TryFrom<&'a [u8]> for UserDataHeader {
73    type Error = HuaweiError;
74    /// Accepts a UDH *without* the UDH Length octet at the start.
75    fn try_from(b: &[u8]) -> HuaweiResult<Self> {
76        let mut offset = 0;
77        let mut ret = vec![];
78        loop {
79            if b.get(offset).is_none() {
80                break;
81            }
82            let id = b[offset];
83            offset += 1;
84            check_offset!(b, offset, "UDH component length");
85            let len = b[offset];
86            let end = offset + len as usize + 1;
87            offset += 1;
88            let o = end - 1;
89            check_offset!(b, o, "UDH component data");
90            let data = b[offset..end].to_owned();
91            offset = end;
92            ret.push(UdhComponent { id, data });
93        }
94        Ok(UserDataHeader {
95            components: ret
96        })
97    }
98}
99